Browse Source

Merge branch 'zhangtao' of gitadmin/tuoheng_lc_web into develop

tags/v1.1.0^2
zhangtao 1 year ago
parent
commit
2d9c7e8fc3
33 changed files with 2400 additions and 22 deletions
  1. +1
    -1
      .vscode/extensions.json
  2. +65
    -0
      src/api/order/index.js
  3. +21
    -2
      src/components/DataTable/tools/Image.vue
  4. +13
    -6
      src/components/DataTable/tools/Switch.vue
  5. +19
    -0
      src/utils/dictionary.js
  6. +119
    -0
      src/views/order-manage/all-order/components/AssignModal.vue
  7. +66
    -0
      src/views/order-manage/all-order/components/MapDrawer.vue
  8. +163
    -0
      src/views/order-manage/all-order/components/PreviewModal.vue
  9. +149
    -0
      src/views/order-manage/all-order/components/QuestionDrawer.vue
  10. +65
    -0
      src/views/order-manage/all-order/index.vue
  11. +20
    -0
      src/views/order-manage/all-order/tools/form.js
  12. +26
    -0
      src/views/order-manage/all-order/tools/quesSearch.js
  13. +154
    -0
      src/views/order-manage/all-order/tools/quesTable.js
  14. +32
    -0
      src/views/order-manage/all-order/tools/search.js
  15. +139
    -0
      src/views/order-manage/all-order/tools/table.js
  16. +66
    -0
      src/views/order-manage/deal-order/components/MapDrawer.vue
  17. +163
    -0
      src/views/order-manage/deal-order/components/PreviewModal.vue
  18. +152
    -0
      src/views/order-manage/deal-order/components/QuestionDrawer.vue
  19. +169
    -0
      src/views/order-manage/deal-order/components/QuestionModal.vue
  20. +217
    -0
      src/views/order-manage/deal-order/components/ReportDrawer.vue
  21. +63
    -0
      src/views/order-manage/deal-order/index.vue
  22. +21
    -0
      src/views/order-manage/deal-order/tools/quesForm.js
  23. +26
    -0
      src/views/order-manage/deal-order/tools/quesSearch.js
  24. +190
    -0
      src/views/order-manage/deal-order/tools/quesTable.js
  25. +32
    -0
      src/views/order-manage/deal-order/tools/search.js
  26. +128
    -0
      src/views/order-manage/deal-order/tools/table.js
  27. +31
    -3
      src/views/question-manage/question-list/index.vue
  28. +56
    -5
      src/views/question-manage/question-list/tools/table.js
  29. +6
    -0
      src/views/report-manage/all-report/components/ReportDrawer.vue
  30. +2
    -1
      src/views/report-manage/all-report/index.vue
  31. +14
    -2
      src/views/report-manage/all-report/tools/table.js
  32. +1
    -1
      src/views/system-manage/menu-manage/tools/form.js
  33. +11
    -1
      src/views/task-manage/question/tools/table.js

+ 1
- 1
.vscode/extensions.json View File

@@ -1,3 +1,3 @@
{
"recommendations": ["johnsoncodehk.volar"]
"recommendations": ["vue.volar"]
}

+ 65
- 0
src/api/order/index.js View File

@@ -0,0 +1,65 @@
import { defAxios as request } from '@/utils/http'

/**
* @description: 获取工单列表
* @param {*} params
* @return {*}
*/
export function generateOrder(ids) {
return request({
url: `/workOrder/generate/${ids}`,
method: 'POST'
})
}

/**
* @description: 获取工单列表
* @param {*} params
* @return {*}
*/
export function getOrderList(params) {
return request({
url: '/workOrder/page',
method: 'GET',
params
})
}

/**
* @description: 分配工单
* @param {*} data
* @return {*}
*/
export function assignOrder(data) {
return request({
url: '/workOrder/assign',
method: 'POST',
data
})
}

/**
* @description: 获取工单下的问题
* @param {*} data
* @return {*}
*/
export function getOrderQuesList(params) {
return request({
url: '/workOrder/question/page',
method: 'GET',
params
})
}

/**
* @description: 处理问题
* @param {*} data
* @return {*}
*/
export function handleOrder(data) {
return request({
url: '/workOrder/handle',
method: 'POST',
data
})
}

+ 21
- 2
src/components/DataTable/tools/Image.vue View File

@@ -1,9 +1,11 @@
<template>
<n-image v-bind="getProps" />
<!-- <n-image v-if="getProps.src" v-bind="getProps" /> -->
<n-image v-for="(item,index) in getProps" v-show="index < limit" :key="index" v-bind="item" />
</template>

<script>
import { defineComponent, computed, toRaw } from 'vue'
import { isArray } from '@/utils/is.js'
export default defineComponent({
name: 'TableImage',
props: {
@@ -11,11 +13,28 @@ export default defineComponent({
type: Object,
default: null,
required: true
},
limit: {
type: Number,
default: 1
}
},
setup(props, { emit }) {
const getProps = computed(() => {
return (toRaw(props.images))
const images = { ...toRaw(props.images) }
let list = []
if (isArray(images.src)) {
list = images.src
}
if (images.src) {
list = images.src.split(',')
}
return list.map((item) => {
return {
...toRaw(props.images),
src: item
}
})
})

return {

+ 13
- 6
src/components/DataTable/tools/Switch.vue View File

@@ -1,9 +1,9 @@
<template>
<n-switch v-bind="getSwitchProps" v-model:value="switchVlue" @update:value="changeValue" />
<n-switch v-bind="getSwitchProps" v-model:value="getSwitchValue" @update:value="changeValue" />
</template>

<script>
import { defineComponent, ref, unref, computed } from 'vue'
import { defineComponent, unref, computed } from 'vue'
export default defineComponent({
name: 'TableSwitch',
props: {
@@ -18,10 +18,16 @@ export default defineComponent({
},
emits: ['change'],
setup(props, { emit }) {
const switchVlue = ref()
const { data, rowKey } = unref(props)
switchVlue.value = rowKey ? data[rowKey] : data
// const switchValue = ref()
// const { data, rowKey } = unref(props)
// switchValue.value = rowKey ? data[rowKey] : data
const getSwitchValue = computed(() => {
const { data, rowKey } = unref(props)
const value = rowKey ? data[rowKey] : data
return value
})
const getSwitchProps = computed(() => {
console.log(111)
return {
...unref(props)
}
@@ -35,7 +41,8 @@ export default defineComponent({
emit('change', params)
}
return {
switchVlue,
// switchValue,
getSwitchValue,
getSwitchProps,
changeValue
}

+ 19
- 0
src/utils/dictionary.js View File

@@ -29,6 +29,25 @@ export const QUESTION_STATUS = [
{ label: '待确认', value: 3 }
]

export const ORDER_STATUS = [
{ label: '待生成', value: 0 },
{ label: '已生成', value: 1 },
{ label: '待分配', value: 5 },
{ label: '处理中', value: 10 },
{ label: '已完成', value: 15 }
]

export const ORDER_STATUS2 = [
{ label: '待分配', value: 5 },
{ label: '已分配', value: 10 },
{ label: '已完成', value: 15 }
]

export const QUES_STATUS = [
{ label: '待处理', value: 0 },
{ label: '已处理', value: 1 }
]

export const USER_STATUS = [
{ label: '正常', value: 1 },
{ label: '禁用', value: 2 }

+ 119
- 0
src/views/order-manage/all-order/components/AssignModal.vue View File

@@ -0,0 +1,119 @@
<template>
<Modal
:options="getModalOptions"
:on-positive-click="handleConfirm"
:on-negative-click="handleClose"
:on-close="handleClose"
>
<template #Context>
<n-form
ref="formRef"
:model="assignForm"
:rules="assignRules"
:label-width="80"
label-placement="left"
require-mark-placement="left"
:disabled="disabled"
>
<template v-for="(item,index) in getFormOptions" :key="index">
<n-form-item :label="item.label" :path="item.key">
<n-select v-if="item.type === 'select'" v-model:value="assignForm[item.key]" v-bind="item.props" />
</n-form-item>
</template>
</n-form>
</template>
</Modal>
</template>

<script>
import { form, getUserOptions } from '../tools/form.js'
import { defineComponent, ref, reactive, computed, toRefs } from 'vue'
import Modal from '@/components/Modal/index.vue'
import { assignOrder } from '@/api/order/index.js'
export default defineComponent({
name: 'UserModal',
components: { Modal },
props: {
visible: {
type: Boolean,
default: false
},
type: {
type: String,
default: 'create'
},
data: {
type: Object,
default: () => null
}
},
emits: {
'update:visible': null,
'reload': null
},
setup(props, { emit }) {
getUserOptions()
const { assignForm, assignRules } = form
const formRef = ref()
const data = reactive({
assignForm: {
...assignForm
},
assignRules: {
...assignRules
},
disabled: props.type === 'preview'
})
const getModalOptions = computed(() => {
return {
show: props.visible,
title: '分配用户',
negativeText: '取消',
positiveText: '确认'
}
})

const getFormOptions = computed(() => {
return {
...form.formItem
}
})

function handleConfirm() {
formRef.value?.validate((errors) => {
if (!errors) {
const params = {
id: props.data.id,
...data.assignForm
// handleUserList: data.assignForm.handleUserList.join(',')
}
assignOrder(params)
.then(res => {
if (res.code === 0) {
emit('reload')
handleClose()
}
})
} else {
$message.error('请完善必填信息')
}
})
}

/* 关闭弹窗 */
const handleClose = () => {
emit('update:visible', false)
}
return {
...toRefs(data),
formRef,
getModalOptions,
getFormOptions,
handleConfirm,
handleClose
}
}
})
</script>
<style scoped lang='scss'>
</style>

+ 66
- 0
src/views/order-manage/all-order/components/MapDrawer.vue View File

@@ -0,0 +1,66 @@
<template>
<n-drawer v-bind="getDrawerOptions" @update:show="handleDrawerColse">
<n-drawer-content closable title="图片位置">
<PositionMsg :data="getPostionOptions" :show-legend="false" />
</n-drawer-content>
</n-drawer>
</template>

<script>
import PositionMsg from '@/components/PositionMsg/index.vue'
import { defineComponent, computed, reactive, toRefs } from 'vue'

export default defineComponent({
name: 'LiveDrawer',
components: { PositionMsg },
props: {
/* 可见 */
visible: {
type: Boolean,
default: false
},
/* 选中的数据 */
data: {
type: Object,
default: () => {}
}
},
emits: {
'update:visible': null
},
setup(props, { emit }) {
const data = reactive({
})

/* 获取抽屉的信息 */
const getDrawerOptions = computed(() => {
return {
show: props.visible,
width: '100%',
placement: 'right'
}
})

const getPostionOptions = computed(() => {
return [props.data]
})

function handleDrawerColse() {
emit('update:visible', false)
}

return {
...toRefs(data),
getDrawerOptions,
getPostionOptions,
handleDrawerColse
}
}
})
</script>

<style scoped lang='scss'>
.n-button+.n-button{
margin-left: 30px;
}
</style>

+ 163
- 0
src/views/order-manage/all-order/components/PreviewModal.vue View File

@@ -0,0 +1,163 @@
<template>
<Modal
:options="getModalOptions"
:on-close="handleClose"
>
<template #Context>
<div class="modal-form">
<label>问题类型</label>
{{ getQuestionType }}
</div>
<div class="carousel__flex">
<n-icon size="26" color="#8A8A8A">
<LeftOutlined @click="handleCarousel('prev')" />
</n-icon>
<div class="carousel__container">
<label>问题图片</label>
<n-carousel ref="carouselRef" :default-index="selectIndex" :show-dots="false">
<img
v-for="item in getCarouselInfo"
:key="item.id"
class="carousel-img"
:src="item.fileMarkerUrl"
>
</n-carousel>
</div>
<n-icon size="26" color="#8A8A8A">
<RightOutlined @click="handleCarousel('next')" />
</n-icon>
</div>
</template>
</Modal>
</template>

<script>
import { QUESTION_TYPE } from '@/utils/dictionary.js'
import { LeftOutlined, RightOutlined } from '@vicons/antd'
import Modal from '@/components/Modal/index.vue'
import { defineComponent, computed, ref, reactive, toRefs } from 'vue'
export default defineComponent({
name: 'UserModal',
components: { Modal, LeftOutlined, RightOutlined },
props: {
visible: {
type: Boolean,
default: false
},
data: {
type: Array,
default: () => null
},
selectRow: {
type: Object,
default: () => {}
}
},
emits: {
'update:visible': null
},
setup(props, { emit }) {
const formRef = ref()
const data = reactive({
selectRow: props.selectRow,
selectIndex: props.data.findIndex((item) => { return item.id === props.selectRow.id }),
selectType: props.selectRow.type
})
/* 获取弹窗的属性 */
const getModalOptions = computed(() => {
return {
show: props.visible,
title: '图片详情',
width: 700
}
})

const getCarouselInfo = computed(() => {
return props.data
})

const getQuestionType = computed(() => {
const row = QUESTION_TYPE.value.find((item) => { return item.value === data.selectRow.type })
return row?.label || '-'
})

const carouselRef = ref(null)

function handleCarousel(type) {
const currentIndex = carouselRef.value.getCurrentIndex()
switch (type) {
case 'prev':
if (currentIndex !== 0) {
carouselRef.value.prev()
data.selectRow = props.data[currentIndex - 1]
data.selectType = props.data[currentIndex - 1].type
}
break
case 'next':
if (currentIndex !== props.data.length - 1) {
carouselRef.value.next()
data.selectRow = props.data[currentIndex + 1]
data.selectType = props.data[currentIndex + 1].type
}
break
}
}

/* 关闭弹窗 */
const handleClose = () => {
emit('update:visible', false)
}

return {
...toRefs(data),
formRef,
getModalOptions,
getQuestionType,
getCarouselInfo,
carouselRef,
handleCarousel,
handleClose
}
}

})
</script>
<style scoped lang='scss'>
.carousel__flex{
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20px;
.carousel__container{
width: calc(100% - 100px);
display: flex;
margin-right: 40px;
label{
width: 70px;
flex-shrink: 0;
}
}
.n-icon{
cursor: pointer;
}
.carousel-img{
width: 100%;
height: 280px;
object-fit: cover
}
}

.modal-form{
width: calc(100% - 60px);
display: flex;
align-items: center;
margin: 20px auto 20px;
label{
width: 70px;
flex-shrink: 0;
}
.n-select{
width: 300px;
}
}
</style>

+ 149
- 0
src/views/order-manage/all-order/components/QuestionDrawer.vue View File

@@ -0,0 +1,149 @@
<template>
<n-drawer v-bind="getDrawerOptions" @update:show="handleDrawerColse">
<n-drawer-content closable title="问题列表">
<HeadSearch :info="search" @search="handleSearch" @reset="handleSearch" />
<DataTable
ref="tableRef"
:columns="columns"
:row-key="(row) => row.id"
:request="loadDataTable"
size="large"
@fetch-success="getTableData"
/>

<MapDrawer v-model:visible="mapDrawer" :data="rowData" />

<PreviewModal v-if="previewModal" v-model:visible="previewModal" :data="pageData" :select-row="rowData" />

</n-drawer-content>
</n-drawer>

</template>

<script>
import table from '../tools/quesTable.js'
import { search } from '../tools/quesSearch.js'
import HeadSearch from '@/components/Search/index.vue'
import DataTable from '@/components/DataTable/index.vue'
import { defineComponent, reactive, unref, toRefs, computed, watch } from 'vue'
import PreviewModal from './PreviewModal.vue'
import MapDrawer from './MapDrawer.vue'
import { getOrderQuesList } from '@/api/order/index.js'

export default defineComponent({
name: 'LiveDrawer',
components: { HeadSearch, DataTable, MapDrawer, PreviewModal },
props: {
/* 可见 */
visible: {
type: Boolean,
default: false
},
/* 选中的数据 */
data: {
type: Object,
default: () => {}
}
},
emits: {
'update:visible': null
},
setup(props, { emit }) {
const data = reactive({
search,
...toRefs(table),
pageData: []
})

/* 获取抽屉的信息 */
const getDrawerOptions = computed(() => {
return {
show: props.visible,
width: '100%',
placement: 'right'
}
})
/* 关闭抽屉 */
function handleDrawerColse() {
emit('update:visible', false)
}
watch(() => [props.visible, props.data],
([val1, val2]) => {
// if (val1 && val2.id) {

// }
})
const loadDataTable = async(res) => {
const _params = {
...unref(data.searchParams),
workOrderId: props.data.id,
...res
}
if (_params.questionType === 'all') _params.questionType = null
if (_params.status === 'all') _params.status = null
return await getOrderQuesList(_params)
}

function getTableData(list) {
data.pageData = list.items
}

return {
...toRefs(data),
getDrawerOptions,
handleDrawerColse,
loadDataTable,
getTableData
}
}
})
</script>

<style scoped lang='scss'>
.report__container{
width: 900px;
margin: 0 auto 40px auto;
.report__item{
margin-bottom: 40px;
.report__item--title{
font-size: 18px;
color: #333333;
line-height: 44px;
}
.n-grid{
border-top: 1px solid rgba(216, 216, 216, 1);
border-left: 1px solid rgba(216, 216, 216, 1);
>div{
position: relative;
text-align: center;
font-size: 14px;
color: #333333;
line-height: 20px;
padding: 6px 12px;
word-wrap: break-word;
word-break: normal;
border-right: 1px solid rgba(216, 216, 216, 1);
border-bottom: 1px solid rgba(216, 216, 216, 1);
&:nth-child(2n-1){
span{
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
}
background: rgba(228, 231, 237, 1);
}
}
}
}
.report__operate{
text-align: center;
}
}
::v-deep(.n-image){
width: 100%;
img{
width: 100%;
}
}
</style>

+ 65
- 0
src/views/order-manage/all-order/index.vue View File

@@ -0,0 +1,65 @@
<template>
<div>
<n-card>
<HeadSearch :info="search" @search="handleSearch" @reset="handleSearch" />
<DataTable
ref="tableRef"
:columns="columns"
:row-key="(row) => row.id"
:request="loadDataTable"
size="large"
/>
</n-card>
</div>

<AssignModal v-if="modalShow" v-model:visible="modalShow" :data="rowData" @reload="handleSearch" />
<QuestionDrawer v-model:visible="detailDrawer" :data="rowData" />

</template>

<script>
import table from './tools/table.js'
import { search } from './tools/search.js'
import HeadSearch from '@/components/Search/index.vue'
import DataTable from '@/components/DataTable/index.vue'
import AssignModal from './components/AssignModal.vue'
import QuestionDrawer from './components/QuestionDrawer.vue'
import { reactive, unref, toRefs, onUnmounted } from 'vue'
import { getOrderList } from '@/api/order/index.js'

export default {
name: 'TaskAll',
components: { HeadSearch, DataTable, AssignModal, QuestionDrawer },
setup() {
const data = reactive({
search,
...toRefs(table)
})

/**
* @description: 加载表格数据
* @param {*} res
* @return {*}
*/
const loadDataTable = async(res) => {
const _params = {
...unref(data.searchParams),
...res
}
return await getOrderList(_params)
}

onUnmounted(() => {
data.searchParams = null
})

return {
...toRefs(data),
loadDataTable
}
}
}

</script>
<style scoped lang='scss'>
</style>

+ 20
- 0
src/views/order-manage/all-order/tools/form.js View File

@@ -0,0 +1,20 @@
import { ref, reactive } from 'vue'
import { getUserList } from '@/api/system/user/index'
const userOptions = ref()
export const form = reactive({
assignForm: {
handleUserList: []
},
assignRules: {
handleUserList: [{ required: true, type: 'array', message: '请选择分配用户', trigger: 'blur' }]
},
formItem: [
{ type: 'select', key: 'handleUserList', label: '人员姓名', props: { multiple: true, options: userOptions, placeholder: '请选择用户', labelField: 'realname', valueField: 'id' }}
]
})

export const getUserOptions = async function() {
const res = await getUserList({ page: 1, limit: 100 })
userOptions.value = res.data.records
}


+ 26
- 0
src/views/order-manage/all-order/tools/quesSearch.js View File

@@ -0,0 +1,26 @@
import { reactive } from 'vue'
import { QUESTION_TYPE_ALL, QUES_STATUS } from '@/utils/dictionary.js'

export const search = reactive([
{
label: '问题类型',
key: 'questionType',
type: 'select',
props: {
placeholder: '请选择问题类型',
options: QUESTION_TYPE_ALL
}
},
{
label: '问题状态',
key: 'status',
type: 'select',
props: {
placeholder: '请选择问题状态',
options: [
{ label: '全部', value: 'all' },
...QUES_STATUS
]
}
}
])

+ 154
- 0
src/views/order-manage/all-order/tools/quesTable.js View File

@@ -0,0 +1,154 @@
import { QUESTION_TYPE, QUES_STATUS } from '@/utils/dictionary.js'
import TableImage from '@/components/DataTable/tools/Image.vue'
import TableTags from '@/components/DataTable/tools/Tags.vue'
import TableAction from '@/components/DataTable/tools/Action.vue'
import { h, ref, reactive } from 'vue'

/* 注册table */
const tableRef = ref()
const searchParams = ref()

function handleSearch(params) {
searchParams.value = { ...params }
if (params?.time?.length) {
searchParams.value = {
...params,
startTime: params.time[0],
endTime: params.time[1]
}
}
delete searchParams.value.time
tableRef.value.reFetch({ searchParams })
}

/* 位置 */
function handlePositionDrawer(row) {
data.rowData = row
data.mapDrawer = true
}

function handleImgPreview(row) {
data.rowData = row
data.previewModal = true
}

const data = reactive({
tableRef,
searchParams,
rowData: {},
mapDrawer: false,
previewModal: false,
handleSearch,

columns: [
{
title: '序号',
key: 'key',
render: (_, index) => {
return `${index + 1}`
},
align: 'center'
},
{
title: '问题类型',
key: 'questionName',
align: 'center'
},
{
title: '问题图片',
key: 'fileMarkerUrl',
align: 'center',
render(row) {
return h(TableImage, {
images: {
width: 36,
height: 36,
src: row.fileMarkerUrl,
previewDisabled: true,
onClick: handleImgPreview.bind(null, row)
}
})
}
},
{
title: '经纬度',
key: 'name',
align: 'center',
render(row) {
return h(TableTags, {
data: [{ name: row.lng }, { name: row.lat }]
})
}
},
{
title: '位置',
key: 'position',
align: 'center',
render(row) {
return h(TableAction, {
actions: [
{
label: '图片位置',
type: 'button',
props: {
type: 'primary',
text: true,
onClick: handlePositionDrawer.bind(null, row)
},
auth: 'basic_list'
}
],
align: 'center'
})
}
},
{
title: '问题状态',
key: 'status',
align: 'center',
render(row) {
return h(TableTags, {
data: row.status,
filters: QUES_STATUS
})
}
},
{
title: '处理人员',
key: 'handlerUserName',
align: 'center',
width: 100
},
{
title: '处理时间',
key: 'handlerTime',
align: 'center',
width: 200
},
{
title: '处理后图片',
key: 'handlerImage',
align: 'center',
render(row) {
return h(TableImage, {
images: {
width: 36,
height: 36,
src: row.handlerImage,
previewDisabled: true
// onClick: handleImgPreview.bind(null, row)
}
})
}
},
{
title: '备注',
key: 'handlerResult',
align: 'center',
width: 200
}

]
})

export default data

+ 32
- 0
src/views/order-manage/all-order/tools/search.js View File

@@ -0,0 +1,32 @@
import { reactive } from 'vue'
import { ORDER_STATUS2 } from '@/utils/dictionary.js'

export const search = reactive([
{
label: '工单编号',
key: 'reportNo',
props: {
placeholder: '请输入工单编号'
}
},
{
label: '工单分配状态',
key: 'status',
type: 'select',
props: {
placeholder: '请选择工单分配状态',
options: ORDER_STATUS2
}
},
{
label: '工单生成时间',
key: 'time',
type: 'date',
value: null,
props: {
type: 'daterange',
valueFormat: 'yyyy-MM-dd',
format: 'yyyy-MM-dd'
}
}
])

+ 139
- 0
src/views/order-manage/all-order/tools/table.js View File

@@ -0,0 +1,139 @@
import { ORDER_STATUS } from '@/utils/dictionary.js'
import TableTags from '@/components/DataTable/tools/Tags.vue'
import TableAction from '@/components/DataTable/tools/Action.vue'
import { h, ref, reactive } from 'vue'

/* 注册table */
const tableRef = ref()
const searchParams = ref()

function handleSearch(params) {
console.log(params)
searchParams.value = { ...params }
if (params?.time?.length) {
searchParams.value = {
...params,
createStartTime: params.time[0],
createEndTime: params.time[1]
}
}
delete searchParams.value.time
tableRef.value.reFetch({ searchParams })
}

/**
* @description: 获取数据及操作
* @param {*} row 单行数据
* @param {*} type 操作类型 create:创建,preview:预览,edit:编辑
* @return {*}
*/
function handleAssign(row) {
data.rowData = row
data.modalShow = true
}
function handleDetail(row) {
data.rowData = row
data.detailDrawer = true
}

const data = reactive({
tableRef,
searchParams,
rowData: {},
modalShow: false,
detailDrawer: false,
handleSearch,

columns: [
{
title: '序号',
key: 'key',
render: (_, index) => {
return `${index + 1}`
},
align: 'center'
},
{
title: '工单编号',
key: 'code',
align: 'center',
width: 250
},
{
title: '问题总数',
key: 'questionTotal',
align: 'center'
},
{
title: '未处理问题数',
key: 'unhandledTotal',
align: 'center'
},
{
title: '工单分配状态',
key: 'status',
align: 'center',
render(row) {
return h(TableTags, {
data: row.status,
filters: ORDER_STATUS
})
}
},
{
title: '工单生成时间',
key: 'createTime',
align: 'center',
width: 200
},
{
title: '工单完成时间',
key: 'finishTime',
align: 'center',
width: 200
},
{
title: '处理人员',
key: 'assignUserName',
align: 'center',
width: 200
},
{
title: '操作',
align: 'center',
width: 150,
fixed: 'right',
render(row) {
return h(TableAction, {
actions: [
{
label: '分配',
type: 'button',
props: {
type: 'primary',
text: true,
onClick: handleAssign.bind(null, row)
},
auth: 'basic_list',
hidden: row.status !== 5
},
{
label: '详情',
type: 'button',
props: {
type: 'primary',
text: true,
onClick: handleDetail.bind(null, row)
},
auth: 'basic_list'
}
],
align: 'center'
})
}
}

]
})

export default data

+ 66
- 0
src/views/order-manage/deal-order/components/MapDrawer.vue View File

@@ -0,0 +1,66 @@
<template>
<n-drawer v-bind="getDrawerOptions" @update:show="handleDrawerColse">
<n-drawer-content closable title="图片位置">
<PositionMsg :data="getPostionOptions" :show-legend="false" />
</n-drawer-content>
</n-drawer>
</template>

<script>
import PositionMsg from '@/components/PositionMsg/index.vue'
import { defineComponent, computed, reactive, toRefs } from 'vue'

export default defineComponent({
name: 'LiveDrawer',
components: { PositionMsg },
props: {
/* 可见 */
visible: {
type: Boolean,
default: false
},
/* 选中的数据 */
data: {
type: Object,
default: () => {}
}
},
emits: {
'update:visible': null
},
setup(props, { emit }) {
const data = reactive({
})

/* 获取抽屉的信息 */
const getDrawerOptions = computed(() => {
return {
show: props.visible,
width: '100%',
placement: 'right'
}
})

const getPostionOptions = computed(() => {
return [props.data]
})

function handleDrawerColse() {
emit('update:visible', false)
}

return {
...toRefs(data),
getDrawerOptions,
getPostionOptions,
handleDrawerColse
}
}
})
</script>

<style scoped lang='scss'>
.n-button+.n-button{
margin-left: 30px;
}
</style>

+ 163
- 0
src/views/order-manage/deal-order/components/PreviewModal.vue View File

@@ -0,0 +1,163 @@
<template>
<Modal
:options="getModalOptions"
:on-close="handleClose"
>
<template #Context>
<div class="modal-form">
<label>问题类型</label>
{{ getQuestionType }}
</div>
<div class="carousel__flex">
<n-icon size="26" color="#8A8A8A">
<LeftOutlined @click="handleCarousel('prev')" />
</n-icon>
<div class="carousel__container">
<label>问题图片</label>
<n-carousel ref="carouselRef" :default-index="selectIndex" :show-dots="false">
<img
v-for="item in getCarouselInfo"
:key="item.id"
class="carousel-img"
:src="item.fileMarkerUrl"
>
</n-carousel>
</div>
<n-icon size="26" color="#8A8A8A">
<RightOutlined @click="handleCarousel('next')" />
</n-icon>
</div>
</template>
</Modal>
</template>

<script>
import { QUESTION_TYPE } from '@/utils/dictionary.js'
import { LeftOutlined, RightOutlined } from '@vicons/antd'
import Modal from '@/components/Modal/index.vue'
import { defineComponent, computed, ref, reactive, toRefs } from 'vue'
export default defineComponent({
name: 'UserModal',
components: { Modal, LeftOutlined, RightOutlined },
props: {
visible: {
type: Boolean,
default: false
},
data: {
type: Array,
default: () => null
},
selectRow: {
type: Object,
default: () => {}
}
},
emits: {
'update:visible': null
},
setup(props, { emit }) {
const formRef = ref()
const data = reactive({
selectRow: props.selectRow,
selectIndex: props.data.findIndex((item) => { return item.id === props.selectRow.id }),
selectType: props.selectRow.type
})
/* 获取弹窗的属性 */
const getModalOptions = computed(() => {
return {
show: props.visible,
title: '图片详情',
width: 700
}
})

const getCarouselInfo = computed(() => {
return props.data
})

const getQuestionType = computed(() => {
const row = QUESTION_TYPE.value.find((item) => { return item.value === data.selectRow.type })
return row?.label || '-'
})

const carouselRef = ref(null)

function handleCarousel(type) {
const currentIndex = carouselRef.value.getCurrentIndex()
switch (type) {
case 'prev':
if (currentIndex !== 0) {
carouselRef.value.prev()
data.selectRow = props.data[currentIndex - 1]
data.selectType = props.data[currentIndex - 1].type
}
break
case 'next':
if (currentIndex !== props.data.length - 1) {
carouselRef.value.next()
data.selectRow = props.data[currentIndex + 1]
data.selectType = props.data[currentIndex + 1].type
}
break
}
}

/* 关闭弹窗 */
const handleClose = () => {
emit('update:visible', false)
}

return {
...toRefs(data),
formRef,
getModalOptions,
getQuestionType,
getCarouselInfo,
carouselRef,
handleCarousel,
handleClose
}
}

})
</script>
<style scoped lang='scss'>
.carousel__flex{
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20px;
.carousel__container{
width: calc(100% - 100px);
display: flex;
margin-right: 40px;
label{
width: 70px;
flex-shrink: 0;
}
}
.n-icon{
cursor: pointer;
}
.carousel-img{
width: 100%;
height: 280px;
object-fit: cover
}
}

.modal-form{
width: calc(100% - 60px);
display: flex;
align-items: center;
margin: 20px auto 20px;
label{
width: 70px;
flex-shrink: 0;
}
.n-select{
width: 300px;
}
}
</style>

+ 152
- 0
src/views/order-manage/deal-order/components/QuestionDrawer.vue View File

@@ -0,0 +1,152 @@
<template>
<n-drawer v-bind="getDrawerOptions" @update:show="handleDrawerColse">
<n-drawer-content closable title="问题列表">
<HeadSearch :info="search" @search="handleSearch" @reset="handleSearch" />
<DataTable
ref="tableRef"
:columns="columns"
:row-key="(row) => row.id"
:request="loadDataTable"
size="large"
@fetch-success="getTableData"
/>

<MapDrawer v-model:visible="mapDrawer" :data="rowData" />

<PreviewModal v-if="previewModal" v-model:visible="previewModal" :data="pageData" :select-row="rowData" />

<QuestionModal v-if="modalShow" v-model:visible="modalShow" :type="modalType" :order-id="orderId" :data="rowData" @reload="handleSearch" />
</n-drawer-content>
</n-drawer>

</template>

<script>
import table from '../tools/quesTable.js'
import { search } from '../tools/quesSearch.js'
import HeadSearch from '@/components/Search/index.vue'
import DataTable from '@/components/DataTable/index.vue'
import { defineComponent, reactive, unref, toRefs, computed, watch } from 'vue'
import MapDrawer from './MapDrawer.vue'
import QuestionModal from './QuestionModal.vue'
import PreviewModal from './PreviewModal.vue'
import { getOrderQuesList } from '@/api/order/index.js'

export default defineComponent({
name: 'LiveDrawer',
components: { HeadSearch, DataTable, MapDrawer, QuestionModal, PreviewModal },
props: {
/* 可见 */
visible: {
type: Boolean,
default: false
},
/* 选中的数据 */
data: {
type: Object,
default: () => {}
}
},
emits: {
'update:visible': null
},
setup(props, { emit }) {
const data = reactive({
search,
...toRefs(table),
orderId: null,
pageData: []
})

/* 获取抽屉的信息 */
const getDrawerOptions = computed(() => {
return {
show: props.visible,
width: '100%',
placement: 'right'
}
})
/* 关闭抽屉 */
function handleDrawerColse() {
emit('update:visible', false)
}
watch(() => [props.visible, props.data],
([val1, val2]) => {
if (val1 && val2.id) {
data.orderId = val2.id
}
})
const loadDataTable = async(res) => {
const _params = {
...unref(data.searchParams),
workOrderId: props.data.id,
...res
}
if (_params.questionType === 'all') _params.questionType = null
if (_params.status === 'all') _params.status = null
return await getOrderQuesList(_params)
}

function getTableData(list) {
data.pageData = list.items
}

return {
...toRefs(data),
getDrawerOptions,
handleDrawerColse,
loadDataTable,
getTableData
}
}
})
</script>

<style scoped lang='scss'>
.report__container{
width: 900px;
margin: 0 auto 40px auto;
.report__item{
margin-bottom: 40px;
.report__item--title{
font-size: 18px;
color: #333333;
line-height: 44px;
}
.n-grid{
border-top: 1px solid rgba(216, 216, 216, 1);
border-left: 1px solid rgba(216, 216, 216, 1);
>div{
position: relative;
text-align: center;
font-size: 14px;
color: #333333;
line-height: 20px;
padding: 6px 12px;
word-wrap: break-word;
word-break: normal;
border-right: 1px solid rgba(216, 216, 216, 1);
border-bottom: 1px solid rgba(216, 216, 216, 1);
&:nth-child(2n-1){
span{
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
}
background: rgba(228, 231, 237, 1);
}
}
}
}
.report__operate{
text-align: center;
}
}
::v-deep(.n-image){
width: 100%;
img{
width: 100%;
}
}
</style>

+ 169
- 0
src/views/order-manage/deal-order/components/QuestionModal.vue View File

@@ -0,0 +1,169 @@
<template>
<Modal
:options="getModalOptions"
:on-positive-click="handleConfirm"
:on-negative-click="handleClose"
:on-close="handleClose"
>
<template #Context>
<n-form
ref="formRef"
:model="questionForm"
:rules="questionRules"
:label-width="120"
label-placement="left"
require-mark-placement="left"
:disabled="disabled"
>
<template v-for="(item,index) in getFormOptions" :key="index">
<n-form-item :label="item.label" :path="item.key">
<n-input v-if="item.type === 'input'" v-model:value="questionForm[item.key]" v-bind="item.props" />
<n-select v-if="item.type === 'select'" v-model:value="questionForm[item.key]" v-bind="item.props" />
<n-image v-if="item.type === 'image'" v-model:src="questionForm[item.key]" v-bind="item.props" />
<UploadOss v-if="item.type === 'oss'" :ref="el=>{ossRefs[item.refIndex] = el}" :limit="5" :default-list="questionForm[item.file]" @upload-status="handleUploadStatus" />
</n-form-item>
</template>
</n-form>
</template>
</Modal>
</template>

<script>
import { form } from '../tools/quesForm.js'
import { defineComponent, ref, reactive, computed, toRefs } from 'vue'
import Modal from '@/components/Modal/index.vue'
import UploadOss from '@/components/UploadOss/index.vue'
import { handleOrder } from '@/api/order/index.js'
export default defineComponent({
name: 'UserModal',
components: { Modal, UploadOss },
props: {
visible: {
type: Boolean,
default: false
},
type: {
type: String,
default: 'preview'
},
orderId: {
type: Number,
default: null
},
data: {
type: Object,
default: () => null
}
},
emits: {
'update:visible': null,
'reload': null
},
setup(props, { emit }) {
const MODAL_TYPE = {
'preview': '问题详情',
'update': '处理问题'
}
const { questionForm, questionRules } = form
const formRef = ref()
const data = reactive({
questionForm: {
...questionForm,
imageStatus: '',
...props.data,
position: `${props.data.lng},${props.data.lat}`
},
questionRules: {
...questionRules
},
disabled: props.type === 'preview'
})
const getModalOptions = computed(() => {
return {
show: props.visible,
title: MODAL_TYPE[props.type],
negativeText: '取消',
positiveText: '确认'
}
})

const getFormOptions = computed(() => {
if (props.type === 'preview' && props.data.status === 0) {
const list = form.formItem.filter((item) => !item.mode)
return {
...list
}
} else {
return {
...form.formItem
}
}
})

const ossRefs = ref([])
function handleUploadStatus(status) {
switch (status) {
case 'no-file':
data.questionForm.imageStatus = ''
break
default:
data.questionForm.imageStatus = status
}
}

function handleConfirm() {
if (props.type === 'preview') {
handleClose()
return
}
formRef.value?.validate((errors) => {
if (!errors) {
const uploads = ossRefs.value.map((item, index) => {
return item.startUpload()
})
Promise.all(uploads)
.then(response => {
const isError = response.map((item) => {
return item.includes('error')
})
if (!isError.includes(true)) {
const imageStr = response.join()
const params = {
workOrderId: props.orderId,
...data.questionForm,
handlerImage: imageStr
}
handleOrder(params)
.then(res => {
if (res.code === 0) {
emit('reload')
handleClose()
}
})
}
})
} else {
$message.error('请完善必填信息')
}
})
}

/* 关闭弹窗 */
const handleClose = () => {
emit('update:visible', false)
}
return {
...toRefs(data),
formRef,
getModalOptions,
getFormOptions,
ossRefs,
handleUploadStatus,
handleConfirm,
handleClose
}
}
})
</script>
<style scoped lang='scss'>
</style>

+ 217
- 0
src/views/order-manage/deal-order/components/ReportDrawer.vue View File

@@ -0,0 +1,217 @@
<template>
<n-drawer v-bind="getDrawerOptions" @update:show="handleDrawerColse">
<n-drawer-content closable title="报告详情">
<div class="report__container">
<div class="report__item">
<p class="report__item--title">林场信息</p>
<div>
<n-grid :cols="2">
<n-gi><span>责任单位</span></n-gi>
<n-gi><span>{{ reportDetail.company }}</span></n-gi>
<n-gi><span>林场名称</span></n-gi>
<n-gi><span>{{ reportDetail.lcName }}</span></n-gi>
<n-gi><span>巡查里程</span></n-gi>
<n-gi><span>{{ reportDetail?.mission?.mileage / 1000 }}公里</span></n-gi>
</n-grid>
</div>
</div>
<div class="report__item">
<p class="report__item--title">巡检信息</p>
<div>
<n-grid :cols="2">
<n-gi><span>气象信息</span></n-gi>
<n-gi><span>{{ reportDetail.airWeather || '-' }}</span></n-gi>
<n-gi><span>巡检方式</span></n-gi>
<n-gi><span>{{ taskType[reportDetail.mission?.inspectionType] }}</span></n-gi>
<n-gi><span>巡检设备</span></n-gi>
<n-gi><span>{{ reportDetail?.mission?.droneName }}</span></n-gi>
<n-gi><span>巡检开始时间</span></n-gi>
<n-gi><span>{{ reportDetail?.mission?.executionStartTime }}</span></n-gi>
<n-gi><span>巡检结束时间</span></n-gi>
<n-gi><span>{{ reportDetail?.mission?.executionEndTime }}</span></n-gi>
<n-gi><span>问题数量</span></n-gi>
<n-gi><span>{{ reportDetail.questionCount }}</span></n-gi>
</n-grid>
</div>
</div>
<div class="report__item">
<p class="report__item--title">巡检结果</p>
<div>
<n-grid :cols="2">
<n-gi><span>巡检内容</span></n-gi>
<n-gi><span>巡检检查结果</span></n-gi>
<template v-for="(item,index) in problemsList" :key="index">
<n-gi><span>{{ item.label }}</span></n-gi>
<n-gi><span>{{ item.value }}</span></n-gi>
</template>
</n-grid>
</div>
</div>
<div v-if="reportDetail.questionCount>0" class="report__item">
<p class="report__item--title">问题清单</p>
<!-- <n-image-group> -->
<div v-for="(item,index) in reportDetail.questionReportList" :key="index">
<p>问题{{ index + 1 }}</p>
<n-grid :cols="2">
<n-gi><span>坐标</span></n-gi>
<n-gi><span>{{ item.lng }},{{ item.lat }}</span></n-gi>
<n-gi><span>问题描述</span></n-gi>
<n-gi><span>{{ item.questionDesc ? item.questionDesc : '-' }}</span></n-gi>
<n-gi><span>问题图片</span></n-gi>
<n-gi><n-image :src="item.fileMarkerUrl" /></n-gi>
</n-grid>
</div>
<!-- </n-image-group> -->
</div>
<div class="report__operate">
<n-button type="primary" @click="handleDownload">下载</n-button>
</div>
</div>
</n-drawer-content>
</n-drawer>
</template>

<script>
import { QUESTION_TYPE } from '@/utils/dictionary.js'
import { getReportDetail, reportDownload } from '@/api/report/index.js'
import { defineComponent, reactive, toRefs, computed, watch } from 'vue'

export default defineComponent({
name: 'LiveDrawer',
props: {
/* 可见 */
visible: {
type: Boolean,
default: false
},
/* 选中的数据 */
data: {
type: Object,
default: () => {}
}
},
emits: {
'update:visible': null
},
setup(props, { emit }) {
const data = reactive({
reportDetail: {},
problemsList: [],
taskType: {
1: '日常巡检',
2: '应急巡检'
}
})

/* 获取抽屉的信息 */
const getDrawerOptions = computed(() => {
return {
show: props.visible,
width: '100%',
placement: 'right'
}
})
/* 关闭抽屉 */
function handleDrawerColse() {
emit('update:visible', false)
}
watch(() => props.data,
(val) => {
if (val.id) {
getReportDetail(val.id)
.then(res => {
if (res.code === 0) {
data.reportDetail = res.data
data.problemsList = []
QUESTION_TYPE.value.forEach((item) => {
const obj = {
label: item.label,
value: 0
}
res.data.questionTypeInfo.forEach((list) => {
if (item.code === list.type) {
obj.value = list.quantity
}
})
data.problemsList.push(obj)
})
// data.reportDetail.questionReportList.forEach((item, index) => {
// if (index === 0)item.fileMarkerUrl = 'https://image.t-aaron.com/XJRW20220720165837/2022-07-20-17-07-34_frame-6563-6720_type-%E6%B0%B4%E7%94%9F%E6%A4%8D%E8%A2%AB_o3MORSWHXz5pQ8F9_s-live-XJRW20220720165837-a0ec218ddd884ffcadd4f3c8fc27d825_AI.jpg'
// if (index === 1)item.fileMarkerUrl = 'https://image.t-aaron.com/XJRW20220720165837/2022-07-20-17-04-30_frame-1802-1920_type-%E6%B0%B4%E7%94%9F%E6%A4%8D%E8%A2%AB_b0N6GXoM178nUxhC_s-live-XJRW20220720165837-a0ec218ddd884ffcadd4f3c8fc27d825_AI.jpg'
// })
}
})
}
})

function handleDownload() {
reportDownload(props.data.id)
.then((res) => {
const download = document.createElement('iframe')
download.src = res.data
download.style.display = 'none'
document.body.appendChild(download)
setTimeout(() => {
document.body.removeChild(download)
}, 300)
})
}

return {
...toRefs(data),
getDrawerOptions,
handleDrawerColse,
handleDownload
}
}
})
</script>

<style scoped lang='scss'>
.report__container{
width: 900px;
margin: 0 auto 40px auto;
.report__item{
margin-bottom: 40px;
.report__item--title{
font-size: 18px;
color: #333333;
line-height: 44px;
}
.n-grid{
border-top: 1px solid rgba(216, 216, 216, 1);
border-left: 1px solid rgba(216, 216, 216, 1);
>div{
position: relative;
text-align: center;
font-size: 14px;
color: #333333;
line-height: 20px;
padding: 6px 12px;
word-wrap: break-word;
word-break: normal;
border-right: 1px solid rgba(216, 216, 216, 1);
border-bottom: 1px solid rgba(216, 216, 216, 1);
&:nth-child(2n-1){
span{
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
}
background: rgba(228, 231, 237, 1);
}
}
}
}
.report__operate{
text-align: center;
}
}
::v-deep(.n-image){
width: 100%;
img{
width: 100%;
}
}
</style>

+ 63
- 0
src/views/order-manage/deal-order/index.vue View File

@@ -0,0 +1,63 @@
<template>
<div>
<n-card>
<HeadSearch :info="search" @search="handleSearch" @reset="handleSearch" />
<DataTable
ref="tableRef"
:columns="columns"
:row-key="(row) => row.id"
:request="loadDataTable"
size="large"
/>
</n-card>
</div>

<QuestionDrawer v-model:visible="detailDrawer" :data="rowData" />

</template>

<script>
import table from './tools/table.js'
import { search } from './tools/search.js'
import HeadSearch from '@/components/Search/index.vue'
import DataTable from '@/components/DataTable/index.vue'
import QuestionDrawer from './components/QuestionDrawer.vue'
import { reactive, unref, toRefs, onUnmounted } from 'vue'
import { getOrderList } from '@/api/order/index.js'

export default {
name: 'TaskAll',
components: { HeadSearch, DataTable, QuestionDrawer },
setup() {
const data = reactive({
search,
...toRefs(table)
})

/**
* @description: 加载表格数据
* @param {*} res
* @return {*}
*/
const loadDataTable = async(res) => {
const _params = {
...unref(data.searchParams),
...res
}
return await getOrderList(_params)
}

onUnmounted(() => {
data.searchParams = null
})

return {
...toRefs(data),
loadDataTable
}
}
}

</script>
<style scoped lang='scss'>
</style>

+ 21
- 0
src/views/order-manage/deal-order/tools/quesForm.js View File

@@ -0,0 +1,21 @@
import { ref, reactive } from 'vue'
import { QUESTION_TYPE_ALL, QUES_STATUS } from '@/utils/dictionary.js'

export const form = reactive({
questionForm: {
handlerResult: ''
},
questionRules: {
imageStatus: { required: true, message: '请选择图片', trigger: ['blur', 'change'] }
},
formItem: [
{ type: 'select', key: 'type', label: '问题类型', props: { options: QUESTION_TYPE_ALL, disabled: true }},
{ type: 'input', key: 'position', label: '问题经纬度', props: { disabled: true }},
{ type: 'input', key: 'createTime', label: '问题发现时间', props: { disabled: true }},
{ type: 'image', key: 'fileMarkerUrl', label: '问题图片', props: { width: 100 }},
{ type: 'select', key: 'status', label: '问题状态', props: { options: QUES_STATUS, disabled: true }},
{ type: 'oss', refIndex: 0, key: 'imageStatus', file: 'handlerImage', label: '封面', mode: true },
{ type: 'input', key: 'handlerResult', label: '描述', props: { type: 'textarea', maxlength: 255 }, mode: true }
]
})


+ 26
- 0
src/views/order-manage/deal-order/tools/quesSearch.js View File

@@ -0,0 +1,26 @@
import { reactive } from 'vue'
import { QUESTION_TYPE_ALL, QUES_STATUS } from '@/utils/dictionary.js'

export const search = reactive([
{
label: '问题类型',
key: 'questionType',
type: 'select',
props: {
placeholder: '请选择问题类型',
options: QUESTION_TYPE_ALL
}
},
{
label: '问题状态',
key: 'status',
type: 'select',
props: {
placeholder: '请选择问题状态',
options: [
{ label: '全部', value: 'all' },
...QUES_STATUS
]
}
}
])

+ 190
- 0
src/views/order-manage/deal-order/tools/quesTable.js View File

@@ -0,0 +1,190 @@
import { QUESTION_TYPE, QUES_STATUS } from '@/utils/dictionary.js'
import TableImage from '@/components/DataTable/tools/Image.vue'
import TableTags from '@/components/DataTable/tools/Tags.vue'
import TableAction from '@/components/DataTable/tools/Action.vue'
import { h, ref, reactive } from 'vue'

/* 注册table */
const tableRef = ref()
const searchParams = ref()

function handleSearch(params) {
searchParams.value = { ...params }
if (params?.time?.length) {
searchParams.value = {
...params,
startTime: params.time[0],
endTime: params.time[1]
}
}
delete searchParams.value.time
tableRef.value.reFetch({ searchParams })
}

function handleDetail(row, type) {
data.rowData = row
data.modalType = type
data.modalShow = true
}

/* 位置 */
function handlePositionDrawer(row) {
data.rowData = row
data.mapDrawer = true
}

function handleImgPreview(row) {
data.rowData = row
data.previewModal = true
}

const data = reactive({
tableRef,
searchParams,
rowData: {},
mapDrawer: false,
previewModal: false,
modalShow: false,
modalType: 'preview',
handleSearch,

columns: [
{
title: '序号',
key: 'key',
render: (_, index) => {
return `${index + 1}`
},
align: 'center'
},
{
title: '问题类型',
key: 'questionName',
align: 'center'
},
{
title: '问题图片',
key: 'fileMarkerUrl',
align: 'center',
render(row) {
return h(TableImage, {
images: {
width: 36,
height: 36,
src: row.fileMarkerUrl,
previewDisabled: true,
onClick: handleImgPreview.bind(null, row)
}
})
}
},
{
title: '经纬度',
key: 'name',
align: 'center',
render(row) {
return h(TableTags, {
data: [{ name: row.lng }, { name: row.lat }]
})
}
},
{
title: '位置',
key: 'position',
align: 'center',
render(row) {
return h(TableAction, {
actions: [
{
label: '图片位置',
type: 'button',
props: {
type: 'primary',
text: true,
onClick: handlePositionDrawer.bind(null, row)
},
auth: 'basic_list'
}
],
align: 'center'
})
}
},
{
title: '问题状态',
key: 'status',
align: 'center',
render(row) {
return h(TableTags, {
data: row.status,
filters: QUES_STATUS
})
}
},
{
title: '处理人员',
key: 'handlerUserName',
align: 'center',
width: 100
},
{
title: '处理时间',
key: 'handlerTime',
align: 'center',
width: 200
},
{
title: '处理后图片',
key: 'handlerImage',
align: 'center',
render(row) {
return h(TableImage, {
images: {
width: 36,
height: 36,
src: row.handlerImage,
previewDisabled: true,
onClick: handleImgPreview.bind(null, row)
}
})
}
},
{
title: '操作',
align: 'center',
width: 150,
fixed: 'right',
render(row) {
return h(TableAction, {
actions: [
{
label: '处理',
type: 'button',
props: {
type: 'primary',
text: true,
onClick: handleDetail.bind(null, row, 'update')
},
auth: 'basic_list',
hidden: row.status === 1
},
{
label: '详情',
type: 'button',
props: {
type: 'primary',
text: true,
onClick: handleDetail.bind(null, row, 'preview')
},
auth: 'basic_list'
}
],
align: 'center'
})
}
}

]
})

export default data

+ 32
- 0
src/views/order-manage/deal-order/tools/search.js View File

@@ -0,0 +1,32 @@
import { reactive } from 'vue'
import { ORDER_STATUS2 } from '@/utils/dictionary.js'

export const search = reactive([
{
label: '工单编号',
key: 'reportNo',
props: {
placeholder: '请输入工单编号'
}
},
{
label: '工单分配状态',
key: 'status',
type: 'select',
props: {
placeholder: '请选择工单分配状态',
options: ORDER_STATUS2
}
},
{
label: '工单生成时间',
key: 'time',
type: 'date',
value: null,
props: {
type: 'daterange',
valueFormat: 'yyyy-MM-dd',
format: 'yyyy-MM-dd'
}
}
])

+ 128
- 0
src/views/order-manage/deal-order/tools/table.js View File

@@ -0,0 +1,128 @@
import { ORDER_STATUS } from '@/utils/dictionary.js'
import TableTags from '@/components/DataTable/tools/Tags.vue'
import TableAction from '@/components/DataTable/tools/Action.vue'
import { h, ref, reactive } from 'vue'

/* 注册table */
const tableRef = ref()
const searchParams = ref()

function handleSearch(params) {
console.log(params)
searchParams.value = { ...params }
if (params?.time?.length) {
searchParams.value = {
...params,
createStartTime: params.time[0],
createEndTime: params.time[1]
}
}
delete searchParams.value.time
tableRef.value.reFetch({ searchParams })
}

/**
* @description: 获取数据及操作
* @param {*} row 单行数据
* @param {*} type 操作类型 create:创建,preview:预览,edit:编辑
* @return {*}
*/
function handleAssign(row) {
data.rowData = row
data.modalShow = true
}
function handleDetail(row) {
data.rowData = row
data.detailDrawer = true
}

const data = reactive({
tableRef,
searchParams,
rowData: {},
modalShow: false,
detailDrawer: false,
handleSearch,

columns: [
{
title: '序号',
key: 'key',
render: (_, index) => {
return `${index + 1}`
},
align: 'center'
},
{
title: '工单编号',
key: 'code',
align: 'center',
width: 250
},
{
title: '问题总数',
key: 'questionTotal',
align: 'center'
},
{
title: '未处理问题数',
key: 'unhandledTotal',
align: 'center'
},
{
title: '工单状态',
key: 'status',
align: 'center',
render(row) {
return h(TableTags, {
data: row.status,
filters: ORDER_STATUS
})
}
},
{
title: '工单生成时间',
key: 'createTime',
align: 'center',
width: 200
},
{
title: '工单完成时间',
key: 'finishTime',
align: 'center',
width: 200
},
{
title: '处理人员',
key: 'assignUserName',
align: 'center',
width: 200
},
{
title: '操作',
align: 'center',
width: 150,
fixed: 'right',
render(row) {
return h(TableAction, {
actions: [
{
label: '工单问题',
type: 'button',
props: {
type: 'primary',
text: true,
onClick: handleDetail.bind(null, row)
},
auth: 'basic_list'
}
],
align: 'center'
})
}
}

]
})

export default data

+ 31
- 3
src/views/question-manage/question-list/index.vue View File

@@ -8,7 +8,12 @@
:request="loadDataTable"
:row-key="(row) => row.id"
@fetch-success="getTableData"
/>
@update:checked-row-keys="handleCheck"
>
<template #tableTitle>
<n-button type="primary" @click="handleOrder"> 生成工单 </n-button>
</template>
</DataTable>
</n-card>
</div>

@@ -25,8 +30,9 @@ import HeadSearch from '@/components/Search/index.vue'
import DataTable from '@/components/DataTable/index.vue'
import MapDrawer from './components/MapDrawer.vue'
import PreviewModal from './components/PreviewModal.vue'
import { reactive, unref, toRefs, onUnmounted } from 'vue'
import { reactive, ref, unref, toRefs, onUnmounted } from 'vue'
import { getQuestionList } from '@/api/task/index.js'
import { generateOrder } from '@/api/order/index.js'

export default {
name: 'QuestionList',
@@ -65,11 +71,33 @@ export default {
data.pageData = list.items
}

// 选择表格数据
const selectedIds = ref([])
function handleCheck(rowKeys) {
selectedIds.value = rowKeys
}

const handleOrder = async() => {
if (selectedIds.value.length) {
const ids = selectedIds.value.join(',')
const res = await generateOrder(ids)
if (res.code === 0) {
selectedIds.value = []
data.handleSearch()
}
} else {
$message.warning('请至少选中一条数据')
}
}

return {
...toRefs(data),
loadDataTable,
getTableData,
handleModal
handleModal,
handleOrder,
handleCheck,
selectedIds
}
}
}

+ 56
- 5
src/views/question-manage/question-list/tools/table.js View File

@@ -1,4 +1,4 @@
import { QUESTION_TYPE } from '@/utils/dictionary.js'
import { QUESTION_TYPE, ORDER_STATUS, QUES_STATUS } from '@/utils/dictionary.js'
import TableImage from '@/components/DataTable/tools/Image.vue'
import TableTags from '@/components/DataTable/tools/Tags.vue'
import TableAction from '@/components/DataTable/tools/Action.vue'
@@ -21,6 +21,11 @@ function handleSearch(params) {
tableRef.value.reFetch({ searchParams })
}

function handlePreview(row) {
data.orderModal = true
data.rowData = row
}

/* 位置 */
function handlePositionDrawer(row) {
data.rowData = row
@@ -36,11 +41,18 @@ const data = reactive({
tableRef,
searchParams,
rowData: {},
orderModal: false,
mapDrawer: false,
previewModal: false,
handleSearch,

columns: [
{
type: 'selection',
disabled(row) {
return row.wordOrderStatus === 1
}
},
{
title: '序号',
key: 'key',
@@ -114,11 +126,50 @@ const data = reactive({
}
},
{
title: '备注',
key: 'note',
align: 'center'
title: '工单生成状态',
key: 'wordOrderStatus',
align: 'center',
render(row) {
return h(TableTags, {
data: row.wordOrderStatus,
filters: ORDER_STATUS
})
}
},
{
title: '处理状态',
key: 'handleStatus',
align: 'center',
render(row) {
return h(TableTags, {
data: row.handleStatus,
filters: QUES_STATUS
})
}
},
{
title: '操作',
align: 'center',
width: 150,
fixed: 'right',
render(row) {
return h(TableAction, {
actions: [
{
label: '查看',
type: 'button',
props: {
type: 'primary',
text: true,
onClick: handlePreview.bind(null, row)
},
auth: 'basic_list'
}
],
align: 'center'
})
}
}

]
})


+ 6
- 0
src/views/report-manage/all-report/components/ReportDrawer.vue View File

@@ -59,6 +59,8 @@
<n-gi><span>{{ item.questionDesc ? item.questionDesc : '-' }}</span></n-gi>
<n-gi><span>问题图片</span></n-gi>
<n-gi><n-image :src="item.fileMarkerUrl" /></n-gi>
<n-gi v-if="type === 'result' && item.questionHandleList"><span>处理结果</span></n-gi>
<n-gi v-if="type === 'result' && item.questionHandleList"><n-image v-for="(cItem,cIndex) in item.questionHandleList" :key="cIndex" :src="cItem.handlerImage" /></n-gi>
</n-grid>
</div>
<!-- </n-image-group> -->
@@ -88,6 +90,10 @@ export default defineComponent({
data: {
type: Object,
default: () => {}
},
type: {
type: String,
default: 'inspection'
}
},
emits: {

+ 2
- 1
src/views/report-manage/all-report/index.vue View File

@@ -12,7 +12,7 @@
</n-card>
</div>

<ReportDrawer v-model:visible="detailDrawer" :data="rowData" />
<ReportDrawer v-model:visible="detailDrawer" :type="detailType" :data="rowData" />

</template>

@@ -50,6 +50,7 @@ export default {

onUnmounted(() => {
data.searchParams = null
data.detailDrawer = false
})

return {

+ 14
- 2
src/views/report-manage/all-report/tools/table.js View File

@@ -18,9 +18,10 @@ function handleSearch(params) {
* @param {*} type 操作类型 create:创建,preview:预览,edit:编辑
* @return {*}
*/
function getRowData(row) {
function getRowData(row, type) {
data.rowData = row
data.detailDrawer = true
data.detailType = type
}

const data = reactive({
@@ -28,6 +29,7 @@ const data = reactive({
searchParams,
rowData: {},
detailDrawer: false,
detailType: 'inspection',
handleSearch,

columns: [
@@ -89,7 +91,17 @@ const data = reactive({
props: {
type: 'primary',
text: true,
onClick: getRowData.bind(null, row)
onClick: getRowData.bind(null, row, 'inspection')
},
auth: 'basic_list'
},
{
label: '处理报告',
type: 'button',
props: {
type: 'primary',
text: true,
onClick: getRowData.bind(null, row, 'result')
},
auth: 'basic_list'
}

+ 1
- 1
src/views/system-manage/menu-manage/tools/form.js View File

@@ -33,7 +33,7 @@ export const form = reactive({
{ type: 'input', key: 'permission', label: '权限标识', props: { maxlength: '20', placeholder: '请输入部门名称', disabled: menuType0, clearable: true }},
{ type: 'input', key: 'path', label: '路由地址', props: { maxlength: '20', placeholder: '请输入部门名称', disabled: menuType1, clearable: true }},
{ type: 'number', key: 'sort', label: '排序号', props: { min: 0, placeholder: '请输入排序号', clearable: true }},
{ type: 'input', key: 'component', label: '组件路径', props: { maxlength: '20', placeholder: '请输入部门名称', disabled: menuType1, clearable: true }},
{ type: 'input', key: 'component', label: '组件路径', props: { maxlength: '200', placeholder: '请输入部门名称', disabled: menuType1, clearable: true }},
{ type: 'radio', key: 'hide', label: '是否可见', options: MENU_VISIBLE },
{ type: 'radio', key: 'status', label: '菜单状态', options: MENU_STATUS }
]

+ 11
- 1
src/views/task-manage/question/tools/table.js View File

@@ -154,7 +154,17 @@ const data = reactive({
})
}
},

{
title: '工单生成状态',
key: 'status',
align: 'center',
render(row) {
return h(TableTags, {
data: row.status,
filters: QUESTION_STATUS
})
}
},
{
title: '操作',
align: 'center',

Loading…
Cancel
Save