<script setup lang="tsx">
|
import noDataImg from '@/assets/element-img/no-data.png'
|
import type { DataTableColumns } from 'naive-ui'
|
import { useBoolean } from '@/hooks'
|
import { NButton, NSpace } from 'naive-ui'
|
import { createTaskAccident, deleteTaskOrder, exportTaskOrderCenter, getTaskAccidentTypeList, getTaskOrderCenterList, getTaskOrderDetail } from '@/service/api/attendance'
|
import { downloadFile } from '@/utils'
|
import SearchPanel from '@/components/custom/SearchPanel.vue'
|
import OperationLogModal from '@/components/custom/OperationLogModal.vue'
|
import TableModal from './components/TableModal.vue'
|
import AddFormModal from './components/AddFormModal.vue'
|
import AccidentDetailModal from './components/detailModal.vue'
|
import RegularDetailModal from './components/RegularDetailModal.vue'
|
import dayjs from 'dayjs'
|
|
const { bool: loading, setTrue: startLoading, setFalse: endLoading } = useBoolean(false)
|
const dialog = useDialog()
|
|
const selectArrowIconUrl = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/e120f81c-9f15-4e1a-9938-22252898edf9.png'
|
const isTaskCategorySelectOpen = ref(false)
|
const isTaskGeneralSelectOpen = ref(false)
|
const isTaskErrorIdCascaderOpen = ref(false)
|
const isTaskUrgencyLevelSelectOpen = ref(false)
|
const isStatusSelectOpen = ref(false)
|
|
const initialModel = {
|
plateNumber: '',
|
taskCategory: null,
|
taskErrorId: null,
|
taskGeneralId: null,
|
taskUrgencyLevel: null,
|
time: null,
|
status: null,
|
taskDescription: '',
|
handleUserName: '',
|
}
|
const model = ref({ ...initialModel })
|
|
const modalRef = ref()
|
const routineDetailModalRef = ref()
|
const accidentDetailModalRef = ref()
|
|
const showFormModal = ref(false)
|
const formMode = ref<'add' | 'edit'>('add')
|
const showOperationLogModal = ref(false)
|
|
const pageParams = ref({
|
pageNum: 1,
|
pageSize: 10,
|
})
|
|
function handleResetSearch() {
|
model.value = { ...initialModel }
|
handleSearch()
|
}
|
|
function handleSearch() {
|
pageParams.value.pageNum = 1
|
getList()
|
}
|
|
const options = [
|
{ label: '常规', value: 1 },
|
{ label: '事故', value: 2 },
|
]
|
const taskGeneralOptions = [
|
{ label: '车辆检查', value: 1 },
|
{ label: '车辆充电', value: 2 },
|
{ label: '车辆整备', value: 3 },
|
{ label: '整备洗车', value: 4 },
|
{ label: '车辆维修', value: 5 },
|
{ label: '车辆保养', value: 6 },
|
{ label: '车辆保险到期', value: 7 },
|
{ label: '车辆年检到期', value: 8 },
|
{ label: '车辆运营资质到期', value: 9 },
|
]
|
const taskUrgencyLevelOptions = [
|
{ label: '一级', value: 0, type: 'error' },
|
{ label: '二级', value: 1, type: 'warning' },
|
{ label: '三级', value: 2, type: 'primary' },
|
// { label: '四级', value: 3, type: 'warning' },
|
]
|
const statusOptions = [
|
{ label: '待到达', value: 0, type: 'default' },
|
{ label: '待处理', value: 1, type: 'warning' },
|
{ label: '已完成', value: 2, type: 'primary' },
|
{ label: '已取消', value: 3, type: 'default' },
|
{ label: '改派中', value: 4, type: 'default' },
|
]
|
const taskErrorIdOptions = ref<any[]>([])
|
|
const filterItems = computed(() => [
|
{
|
label: '车辆',
|
path: 'plateNumber',
|
render: () => <n-input v-model:value={model.value.plateNumber} />,
|
},
|
{
|
label: '任务类别',
|
path: 'taskCategory',
|
render: () => (
|
<n-select
|
v-model:value={model.value.taskCategory}
|
options={options}
|
placeholder="请选择"
|
clearable
|
onUpdate:show={(value: boolean) => isTaskCategorySelectOpen.value = value}
|
>
|
{{
|
arrow: () => (
|
<img
|
src={selectArrowIconUrl}
|
class={`arrow-icon ${isTaskCategorySelectOpen.value ? 'arrow-icon--open' : ''}`}
|
style={{ width: '16px', height: '16px' }}
|
alt="arrow"
|
/>
|
),
|
empty: () => (
|
<div class="flex-center">
|
<img
|
src={noDataImg}
|
style={{ width: '50%' }}
|
/>
|
</div>
|
),
|
}}
|
</n-select>
|
),
|
},
|
{
|
label: '常规任务类型',
|
path: 'taskGeneralId',
|
render: () => (
|
<n-select
|
v-model:value={model.value.taskGeneralId}
|
options={taskGeneralOptions}
|
placeholder="请选择"
|
clearable
|
onUpdate:show={(value: boolean) => isTaskGeneralSelectOpen.value = value}
|
>
|
{{
|
arrow: () => (
|
<img
|
src={selectArrowIconUrl}
|
class={`arrow-icon ${isTaskGeneralSelectOpen.value ? 'arrow-icon--open' : ''}`}
|
style={{ width: '16px', height: '16px' }}
|
alt="arrow"
|
/>
|
),
|
empty: () => (
|
<div class="flex-center">
|
<img
|
src={noDataImg}
|
style={{ width: '50%' }}
|
/>
|
</div>
|
),
|
}}
|
</n-select>
|
),
|
},
|
{
|
label: '事故任务类型',
|
path: 'taskErrorId',
|
render: () => (
|
<n-cascader
|
v-model:value={model.value.taskErrorId}
|
options={taskErrorIdOptions.value}
|
value-field="id"
|
label-field="errorName"
|
children-field="child"
|
check-strategy="child"
|
clearable
|
onUpdate:show={(value: boolean) => isTaskErrorIdCascaderOpen.value = value}
|
>
|
{{
|
arrow: () => (
|
<img
|
src={selectArrowIconUrl}
|
class={`arrow-icon ${isTaskErrorIdCascaderOpen.value ? 'arrow-icon--open' : ''}`}
|
style={{ width: '16px', height: '16px' }}
|
alt="arrow"
|
/>
|
),
|
}}
|
</n-cascader>
|
),
|
},
|
{
|
label: '级别',
|
path: 'taskUrgencyLevel',
|
render: () => (
|
<n-select
|
v-model:value={model.value.taskUrgencyLevel}
|
options={taskUrgencyLevelOptions}
|
placeholder="请选择"
|
clearable
|
onUpdate:show={(value: boolean) => isTaskUrgencyLevelSelectOpen.value = value}
|
>
|
{{
|
arrow: () => (
|
<img
|
src={selectArrowIconUrl}
|
class={`arrow-icon ${isTaskUrgencyLevelSelectOpen.value ? 'arrow-icon--open' : ''}`}
|
style={{ width: '16px', height: '16px' }}
|
alt="arrow"
|
/>
|
),
|
empty: () => (
|
<div class="flex-center">
|
<img
|
src={noDataImg}
|
style={{ width: '50%' }}
|
/>
|
</div>
|
),
|
}}
|
</n-select>
|
),
|
},
|
{
|
label: '任务状态',
|
path: 'status',
|
render: () => (
|
<n-select
|
v-model:value={model.value.status}
|
options={statusOptions}
|
placeholder="请选择"
|
clearable
|
onUpdate:show={(value: boolean) => isStatusSelectOpen.value = value}
|
>
|
{{
|
arrow: () => (
|
<img
|
src={selectArrowIconUrl}
|
class={`arrow-icon ${isStatusSelectOpen.value ? 'arrow-icon--open' : ''}`}
|
style={{ width: '16px', height: '16px' }}
|
alt="arrow"
|
/>
|
),
|
empty: () => (
|
<div class="flex-center">
|
<img
|
src={noDataImg}
|
style={{ width: '50%' }}
|
/>
|
</div>
|
),
|
}}
|
</n-select>
|
),
|
},
|
{
|
label: '上报时间',
|
path: 'time',
|
render: () => (
|
<n-date-picker
|
format="yyyy/MM/dd"
|
v-model:value={model.value.time}
|
type="daterange"
|
clearable
|
actions={[]}
|
close-on-select={true}
|
start-placeholder="开始时间"
|
end-placeholder="结束时间"
|
separator="-"
|
/>
|
),
|
},
|
{
|
label: '任务描述',
|
path: 'taskDescription',
|
render: () => <n-input v-model:value={model.value.taskDescription} clearable />,
|
},
|
{
|
label: '处理人',
|
path: 'handleUserName',
|
render: () => <n-input v-model:value={model.value.handleUserName} clearable />,
|
},
|
])
|
|
const total = ref(0)
|
const listData = ref<any[]>([])
|
const tableScrollX = 1266
|
|
async function getList() {
|
startLoading()
|
const params = {
|
...pageParams.value,
|
status: model.value.status,
|
plateNumber: model.value.plateNumber?.trim() || undefined,
|
taskCategory: model.value.taskCategory,
|
taskGeneralId: model.value.taskGeneralId,
|
taskErrorId: model.value.taskErrorId,
|
taskUrgencyLevel: model.value.taskUrgencyLevel,
|
taskDescription: model.value.taskDescription?.trim() || undefined,
|
handleUserName: model.value.handleUserName?.trim() || undefined,
|
startTime: model.value.time ? dayjs(model.value.time?.[0]).format('YYYY-MM-DD HH:mm:ss') : null,
|
endTime: model.value.time ? dayjs(model.value.time?.[1]).format('YYYY-MM-DD HH:mm:ss') : null,
|
}
|
getTaskOrderCenterList(params).then((res) => {
|
listData.value = res.data.records || []
|
listData.value.forEach((item: any, index: number) => {
|
item.serialNumber = (pageParams.value.pageNum - 1) * pageParams.value.pageSize + index + 1
|
})
|
total.value = res.data.total || res.data.records?.length
|
}).finally(() => {
|
endLoading()
|
})
|
}
|
|
function getAccidentTypeList() {
|
getTaskAccidentTypeList().then((res) => {
|
if (res.code === 200) {
|
taskErrorIdOptions.value = res.data || []
|
}
|
})
|
}
|
|
onMounted(() => {
|
getList()
|
getAccidentTypeList()
|
})
|
|
function changePage(page: number, size: number) {
|
pageParams.value.pageNum = page
|
pageParams.value.pageSize = size
|
getList()
|
}
|
|
function handleExport() {
|
const params = {
|
...pageParams.value,
|
status: model.value.status,
|
plateNumber: model.value.plateNumber?.trim() || undefined,
|
taskCategory: model.value.taskCategory,
|
taskGeneralId: model.value.taskGeneralId,
|
taskErrorId: model.value.taskErrorId,
|
taskUrgencyLevel: model.value.taskUrgencyLevel,
|
taskDescription: model.value.taskDescription?.trim() || undefined,
|
handleUserName: model.value.handleUserName?.trim() || undefined,
|
startTime: model.value.time ? dayjs(model.value.time?.[0]).format('YYYY-MM-DD HH:mm:ss') : null,
|
endTime: model.value.time ? dayjs(model.value.time?.[1]).format('YYYY-MM-DD HH:mm:ss') : null,
|
}
|
exportTaskOrderCenter(params).then((res) => {
|
if (res instanceof Blob) {
|
downloadFile(res, `工单中心列表_${new Date().getTime()}.xlsx`)
|
window.$message.success('导出成功')
|
}
|
})
|
}
|
|
function handleAdd() {
|
showFormModal.value = true
|
formMode.value = 'add'
|
}
|
|
function handleOpenOperationLog() {
|
showOperationLogModal.value = true
|
}
|
|
/**
|
* 级联选中时自动补充父级 id
|
* @param tree 原始树结构
|
* @param selectedIds 已选中的 id 列表
|
*/
|
function addParentIds(
|
tree: any[],
|
selectedIds: string[],
|
): string[] {
|
const result = new Set(selectedIds)
|
|
function traverse(nodes: any[], parent?: any) {
|
nodes.forEach((node) => {
|
// 如果当前节点被选中
|
if (result.has(node.id) && parent) {
|
result.add(parent.id)
|
}
|
|
if (node.child && node.child.length) {
|
traverse(node.child, node)
|
}
|
})
|
}
|
|
traverse(tree)
|
|
return Array.from(result)
|
}
|
|
function handleConfirmForm(data: any) {
|
const taskImage = data.taskImage.map((item: any) => {
|
return {
|
id: item.id,
|
name: item.name,
|
url: item.url,
|
}
|
})
|
const params = {
|
...data,
|
taskTypeId: addParentIds(taskErrorIdOptions.value, [data.taskTypeId]).join(','),
|
taskImage: JSON.stringify(taskImage),
|
}
|
createTaskAccident(params).then((res) => {
|
if (res.code === 200) {
|
window.$message.success('新增成功')
|
}
|
}).finally(() => {
|
getList()
|
})
|
}
|
|
async function handleDetail(row: any) {
|
const res = await getTaskOrderDetail(row.id)
|
if (res.code === 200) {
|
if (row.taskCategory === 2) {
|
accidentDetailModalRef.value.openModal(res.data, row.id)
|
}
|
else {
|
routineDetailModalRef.value.openModal(res.data, row.id)
|
}
|
}
|
}
|
|
const columns = computed<DataTableColumns<any>>(() => [
|
{
|
title: '序号',
|
key: 'serialNumber',
|
width: 80,
|
align: 'left',
|
resizable: true,
|
fixed: 'left',
|
},
|
{
|
title: '车辆',
|
key: 'plateNumber',
|
width: 116,
|
align: 'left',
|
resizable: true,
|
fixed: 'left',
|
},
|
{
|
title: '任务类别',
|
key: 'taskCategory',
|
width: 80,
|
align: 'left',
|
resizable: true,
|
render: row => options.find(item => item.value === row.taskCategory)?.label || '-',
|
},
|
{
|
title: '紧急程度',
|
key: 'taskUrgencyLevel',
|
width: 100,
|
align: 'left',
|
resizable: true,
|
render(row) {
|
const option = taskUrgencyLevelOptions.find(item => item.value == row.taskUrgencyLevel)
|
return (
|
<n-tag type={option?.type || 'default'} bordered={false}>
|
{option?.label || '_'}
|
</n-tag>
|
)
|
},
|
},
|
{
|
title: '类型',
|
key: 'taskTypeName',
|
width: 200,
|
align: 'left',
|
resizable: true,
|
ellipsis: {
|
tooltip: true,
|
},
|
},
|
{
|
title: '任务描述',
|
key: 'taskDescription',
|
width: 180,
|
align: 'left',
|
resizable: true,
|
ellipsis: {
|
tooltip: true,
|
},
|
},
|
{
|
title: '上报时间',
|
key: 'createTime',
|
width: 160,
|
align: 'left',
|
resizable: true,
|
render: row => <span>{row.createTime ? dayjs(row.createTime).format('YYYY/MM/DD HH:mm') : '-'}</span>,
|
},
|
{
|
title: '处理状态',
|
key: 'status',
|
width: 100,
|
align: 'left',
|
resizable: true,
|
render(row) {
|
const option = statusOptions.find(item => item.value === row.status)
|
return (
|
<n-tag type={option?.type || 'default'} bordered={false}>
|
{option?.label || ''}
|
</n-tag>
|
)
|
},
|
},
|
{
|
title: '处理人',
|
key: 'handleUserName',
|
width: 110,
|
align: 'left',
|
resizable: true,
|
ellipsis: {
|
tooltip: true,
|
},
|
},
|
{
|
title: '操作',
|
key: 'actions',
|
width: 140,
|
align: 'left',
|
fixed: 'right',
|
render: (row) => {
|
return (
|
<NSpace>
|
<NButton
|
text
|
v-permission="7054"
|
type="primary"
|
onClick={() => handleDetail(row)}
|
>
|
详情
|
</NButton>
|
{(row.status === 0 || row.status === 1) && (
|
<NButton
|
text
|
v-permission="7053"
|
type="primary"
|
onClick={() => modalRef.value.openModal(row.id)}
|
>
|
分配
|
</NButton>
|
)}
|
|
<NButton
|
text
|
v-permission="7055"
|
type="primary"
|
onClick={() => {
|
dialog.create({
|
title: '是否确认删除数据?',
|
content: `确认删除该任务?此操作不可恢复。`,
|
positiveText: '确认',
|
negativeText: '取消',
|
maskClosable: false,
|
autoFocus: false,
|
onPositiveClick: () => {
|
deleteTaskOrder(row.id).then((res) => {
|
if (res.code === 200) {
|
window.$message.success('删除成功')
|
getList()
|
}
|
})
|
},
|
})
|
}}
|
>
|
删除
|
</NButton>
|
</NSpace>
|
)
|
},
|
},
|
])
|
</script>
|
|
<template>
|
<div class="roboTaxi">
|
<div class="roboTaxi-form-card mb-12px">
|
<n-form
|
:model="model"
|
label-placement="left"
|
inline
|
:show-feedback="false"
|
>
|
<SearchPanel>
|
<n-gi v-for="item in filterItems" :key="item.path" class="n-grid-item">
|
<n-form-item :label="item.label" :path="item.path">
|
<component :is="item.render" />
|
</n-form-item>
|
</n-gi>
|
<template #action>
|
<NButton @click="handleResetSearch">
|
重置
|
</NButton>
|
<NButton type="primary" @click="handleSearch">
|
查询
|
</NButton>
|
</template>
|
</SearchPanel>
|
</n-form>
|
</div>
|
|
<n-card class="flex-1 robotaxi-table-card">
|
<template #header>
|
<n-flex justify="space-between">
|
|
<NButton v-permission="7051" @click="handleExport">
|
导出
|
</NButton>
|
<n-flex justify="flex-end">
|
<NButton v-permission="7056" @click="handleOpenOperationLog">
|
操作日志
|
</NButton>
|
<NButton
|
v-permission="7052"
|
type="primary"
|
secondary
|
@click="handleAdd"
|
>
|
新增
|
</NButton>
|
|
</n-flex>
|
</n-flex>
|
</template>
|
<div class="flex-1 flex flex-col h-full">
|
<n-data-table
|
class="flex-1"
|
flex-height
|
:columns="columns"
|
:data="listData"
|
:loading="loading"
|
:bordered="false"
|
:header-height="18"
|
:row-height="18"
|
:scroll-x="tableScrollX"
|
>
|
<template #empty>
|
<div class="flex-center">
|
<img
|
:src="noDataImg"
|
style="width: 123px; height: 125px"
|
@error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }"
|
>
|
</div>
|
</template>
|
</n-data-table>
|
<Pagination
|
class="robotaxi-pagination mt-12px"
|
:count="total"
|
:page="pageParams.pageNum"
|
:page-size="pageParams.pageSize"
|
@change="changePage"
|
/>
|
</div>
|
</n-card>
|
<TableModal ref="modalRef" @refresh="getList" />
|
<RegularDetailModal ref="routineDetailModalRef" />
|
<AccidentDetailModal ref="accidentDetailModalRef" />
|
<OperationLogModal v-model:show="showOperationLogModal" />
|
<AddFormModal
|
v-model:show="showFormModal"
|
:mode="formMode"
|
:task-error-id-options="taskErrorIdOptions"
|
:task-urgency-level-options="taskUrgencyLevelOptions"
|
@confirm="handleConfirmForm"
|
/>
|
</div>
|
</template>
|