|
|
|
@ -18,15 +18,16 @@
|
|
|
|
<div class="main-area">
|
|
|
|
<div class="main-area">
|
|
|
|
<el-card>
|
|
|
|
<el-card>
|
|
|
|
<div style="margin-bottom: 16px;">
|
|
|
|
<div style="margin-bottom: 16px;">
|
|
|
|
<el-form :inline="true" :model="filters" size="small">
|
|
|
|
<el-form :inline="true" :model="filters" size="small" @submit.prevent="handleQuery">
|
|
|
|
<el-form-item label="关键词">
|
|
|
|
<el-form-item label="标题">
|
|
|
|
<el-input v-model="filters.keyword" size="small" placeholder="请输入关键词" clearable />
|
|
|
|
<el-input v-model="filters.keyword" style="width: 200px;" placeholder="请输入标题" clearable @keyup.enter="handleQuery" />
|
|
|
|
</el-form-item>
|
|
|
|
</el-form-item>
|
|
|
|
<el-form-item label="时间区间">
|
|
|
|
<el-form-item label="故障时间">
|
|
|
|
<el-date-picker
|
|
|
|
<el-date-picker
|
|
|
|
v-model="filters.startTime"
|
|
|
|
v-model="filters.startTime"
|
|
|
|
type="datetime"
|
|
|
|
type="datetime"
|
|
|
|
format="YYYY-MM-DD HH:mm:ss"
|
|
|
|
format="YYYY-MM-DD HH:mm:ss"
|
|
|
|
|
|
|
|
value-format="YYYY-MM-DD HH:mm:ss"
|
|
|
|
placeholder="开始时间"
|
|
|
|
placeholder="开始时间"
|
|
|
|
style="width: 160px"
|
|
|
|
style="width: 160px"
|
|
|
|
/>
|
|
|
|
/>
|
|
|
|
@ -35,6 +36,7 @@
|
|
|
|
v-model="filters.endTime"
|
|
|
|
v-model="filters.endTime"
|
|
|
|
type="datetime"
|
|
|
|
type="datetime"
|
|
|
|
format="YYYY-MM-DD HH:mm:ss"
|
|
|
|
format="YYYY-MM-DD HH:mm:ss"
|
|
|
|
|
|
|
|
value-format="YYYY-MM-DD HH:mm:ss"
|
|
|
|
placeholder="结束时间"
|
|
|
|
placeholder="结束时间"
|
|
|
|
style="width: 160px"
|
|
|
|
style="width: 160px"
|
|
|
|
/>
|
|
|
|
/>
|
|
|
|
@ -58,12 +60,13 @@
|
|
|
|
</el-form>
|
|
|
|
</el-form>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<el-tabs v-model="currentTab" @tab-change="handleTabChange" style="margin-bottom: 16px;">
|
|
|
|
<el-tabs v-model="currentTab" @tab-change="handleTabChange" style="margin-bottom: 16px;">
|
|
|
|
<el-tab-pane label="全部工单" name="all" />
|
|
|
|
|
|
|
|
<el-tab-pane label="我的工单" name="mine" />
|
|
|
|
<el-tab-pane label="我的工单" name="mine" />
|
|
|
|
|
|
|
|
<el-tab-pane label="全部工单" name="all" />
|
|
|
|
</el-tabs>
|
|
|
|
</el-tabs>
|
|
|
|
<el-table :data="ticketList" border style="width: 100%">
|
|
|
|
<el-table :data="ticketList" border style="width: 100%">
|
|
|
|
<el-table-column prop="title" label="工单标题" min-width="120" />
|
|
|
|
<el-table-column prop="title" label="工单标题" min-width="120" />
|
|
|
|
<el-table-column prop="description" label="工单描述" min-width="180" />
|
|
|
|
<!-- <el-table-column prop="description" label="工单描述" min-width="180" /> -->
|
|
|
|
<el-table-column prop="source" label="来源" min-width="80">
|
|
|
|
<el-table-column prop="source" label="来源" min-width="80">
|
|
|
|
<template #default="{ row }">
|
|
|
|
<template #default="{ row }">
|
|
|
|
{{ getSourceLabel(row.source) }}
|
|
|
|
{{ getSourceLabel(row.source) }}
|
|
|
|
@ -76,16 +79,31 @@
|
|
|
|
</el-tag>
|
|
|
|
</el-tag>
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
</el-table-column>
|
|
|
|
</el-table-column>
|
|
|
|
|
|
|
|
<el-table-column label="故障设备" min-width="120">
|
|
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
|
|
{{ deviceOptions.find(item => item.commUid === row.comm_uid)?.ilcName || row.comm_uid || '-' }}
|
|
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
|
|
<el-table-column prop="fault_time" label="故障时间" min-width="180" />
|
|
|
|
|
|
|
|
<el-table-column label="处理人" min-width="100">
|
|
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
|
|
{{ handlerOptions.find(user => user.username === row.handler)?.nickname || row.handler }}
|
|
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
</el-table-column>
|
|
|
|
<el-table-column prop="contact" label="联系方式" min-width="120" />
|
|
|
|
<el-table-column prop="contact" label="联系方式" min-width="120" />
|
|
|
|
<el-table-column prop="expected_completion_time" label="预计完成时间" min-width="140" />
|
|
|
|
<el-table-column prop="expected_completion_time" label="预计完成时间" min-width="180" />
|
|
|
|
<el-table-column prop="status" label="状态" min-width="100">
|
|
|
|
<el-table-column prop="status" label="状态" min-width="100" fixed="right">
|
|
|
|
<template #default="{ row }">
|
|
|
|
<template #default="{ row }">
|
|
|
|
<el-tag :type="row.status === 'completed' ? 'success' : row.status === 'in_progress' ? 'warning' : row.status === 'closed' ? 'info' : 'default'">
|
|
|
|
<el-tag
|
|
|
|
|
|
|
|
:type="getStatusType(row.status)"
|
|
|
|
|
|
|
|
effect="dark"
|
|
|
|
|
|
|
|
disable-transitions
|
|
|
|
|
|
|
|
>
|
|
|
|
{{ getStatusLabel(row.status) }}
|
|
|
|
{{ getStatusLabel(row.status) }}
|
|
|
|
</el-tag>
|
|
|
|
</el-tag>
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
</el-table-column>
|
|
|
|
</el-table-column>
|
|
|
|
<el-table-column label="操作" min-width="180">
|
|
|
|
<el-table-column label="操作" min-width="180" fixed="right">
|
|
|
|
<template #default="{ row }">
|
|
|
|
<template #default="{ row }">
|
|
|
|
<el-button-group>
|
|
|
|
<el-button-group>
|
|
|
|
<el-button size="small" @click="handleView(row)">查看</el-button>
|
|
|
|
<el-button size="small" @click="handleView(row)">查看</el-button>
|
|
|
|
@ -96,8 +114,8 @@
|
|
|
|
</el-button>
|
|
|
|
</el-button>
|
|
|
|
<template #dropdown>
|
|
|
|
<template #dropdown>
|
|
|
|
<el-dropdown-menu>
|
|
|
|
<el-dropdown-menu>
|
|
|
|
<el-dropdown-item @click="handleUpdateStatus(row)" v-if="canUpdateStatus(row.status)">流转</el-dropdown-item>
|
|
|
|
<el-dropdown-item @click="handleProcess(row)" :disabled="!canProcess(row.status, row)">处理</el-dropdown-item>
|
|
|
|
<el-dropdown-item @click="handleDispatch(row)">派工</el-dropdown-item>
|
|
|
|
<el-dropdown-item @click="handleDispatch(row)" :disabled="row.status !== 'new'">派工</el-dropdown-item>
|
|
|
|
<el-dropdown-item divided @click="handleDelete(row)" v-if="canDelete(row.status)">
|
|
|
|
<el-dropdown-item divided @click="handleDelete(row)" v-if="canDelete(row.status)">
|
|
|
|
<span style="color: #f56c6c;">删除</span>
|
|
|
|
<span style="color: #f56c6c;">删除</span>
|
|
|
|
</el-dropdown-item>
|
|
|
|
</el-dropdown-item>
|
|
|
|
@ -134,6 +152,16 @@
|
|
|
|
<el-option v-for="item in sources" :key="item.value" :label="item.label" :value="item.value" />
|
|
|
|
<el-option v-for="item in sources" :key="item.value" :label="item.label" :value="item.value" />
|
|
|
|
</el-select>
|
|
|
|
</el-select>
|
|
|
|
</el-form-item>
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
<el-form-item label="故障设备" required>
|
|
|
|
|
|
|
|
<el-select v-model="createForm.comm_uid" filterable placeholder="请选择故障设备">
|
|
|
|
|
|
|
|
<el-option
|
|
|
|
|
|
|
|
v-for="item in deviceOptions"
|
|
|
|
|
|
|
|
:key="item.commUid"
|
|
|
|
|
|
|
|
:label="item.ilcName"
|
|
|
|
|
|
|
|
:value="item.commUid"
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
|
|
</el-form-item>
|
|
|
|
<el-form-item label="优先级" required>
|
|
|
|
<el-form-item label="优先级" required>
|
|
|
|
<el-select v-model="createForm.priority" placeholder="请选择">
|
|
|
|
<el-select v-model="createForm.priority" placeholder="请选择">
|
|
|
|
<el-option v-for="item in priorities" :key="item.value" :label="item.label" :value="item.value" />
|
|
|
|
<el-option v-for="item in priorities" :key="item.value" :label="item.label" :value="item.value" />
|
|
|
|
@ -142,12 +170,9 @@
|
|
|
|
<el-form-item label="联系方式" required>
|
|
|
|
<el-form-item label="联系方式" required>
|
|
|
|
<el-input v-model="createForm.contact" />
|
|
|
|
<el-input v-model="createForm.contact" />
|
|
|
|
</el-form-item>
|
|
|
|
</el-form-item>
|
|
|
|
<el-form-item label="预计完成时间">
|
|
|
|
|
|
|
|
<el-date-picker v-model="createForm.expected_completion_time" format="YYYY-MM-DD HH:mm:ss" type="datetime" style="width: 100%" />
|
|
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
</el-form>
|
|
|
|
</el-form>
|
|
|
|
<template #footer>
|
|
|
|
<template #footer>
|
|
|
|
<el-button @click="showCreateDialog = false">取消</el-button>
|
|
|
|
<el-button @click="() => { showCreateDialog = false; Object.assign(createForm, initialCreateForm()) }">取消</el-button>
|
|
|
|
<el-button type="primary" @click="handleCreate">确定</el-button>
|
|
|
|
<el-button type="primary" @click="handleCreate">确定</el-button>
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
</el-dialog>
|
|
|
|
</el-dialog>
|
|
|
|
@ -207,42 +232,64 @@
|
|
|
|
<el-skeleton v-if="viewLoading" rows="6" animated />
|
|
|
|
<el-skeleton v-if="viewLoading" rows="6" animated />
|
|
|
|
<div v-else>
|
|
|
|
<div v-else>
|
|
|
|
<div v-if="currentTicket" class="ticket-detail">
|
|
|
|
<div v-if="currentTicket" class="ticket-detail">
|
|
|
|
<div class="detail-item">
|
|
|
|
<div class="detail-item"><span class="label">工单ID:</span><span class="value">{{ currentTicket.id }}</span></div>
|
|
|
|
<span class="label">工单标题:</span>
|
|
|
|
<div class="detail-item"><span class="label">工单标题:</span><span class="value">{{ currentTicket.title }}</span></div>
|
|
|
|
<span class="value">{{ currentTicket.title }}</span>
|
|
|
|
<div class="detail-item"><span class="label">工单描述:</span><span class="value">{{ currentTicket.description }}</span></div>
|
|
|
|
</div>
|
|
|
|
<div class="detail-item"><span class="label">工单来源:</span><span class="value">{{ getSourceLabel(currentTicket.source) }}</span></div>
|
|
|
|
<div class="detail-item">
|
|
|
|
<div class="detail-item"><span class="label">故障设备:</span><span class="value">
|
|
|
|
<span class="label">工单描述:</span>
|
|
|
|
{{
|
|
|
|
<span class="value">{{ currentTicket.description }}</span>
|
|
|
|
(() => {
|
|
|
|
</div>
|
|
|
|
if (!currentTicket) return '-';
|
|
|
|
<div class="detail-item">
|
|
|
|
// 兼容 comm_uid/commUid 字段
|
|
|
|
<span class="label">工单来源:</span>
|
|
|
|
const commUid = (currentTicket as any).comm_uid || (currentTicket as any).commUid;
|
|
|
|
<span class="value">{{ getSourceLabel(currentTicket.source) }}</span>
|
|
|
|
if (!commUid) return '-';
|
|
|
|
</div>
|
|
|
|
return deviceOptions.find(item => item.commUid === commUid)?.ilcName || commUid || '-';
|
|
|
|
<div class="detail-item">
|
|
|
|
})()
|
|
|
|
<span class="label">优先级:</span>
|
|
|
|
}}
|
|
|
|
<span class="value">{{ getPriorityLabel(currentTicket.priority) }}</span>
|
|
|
|
</span></div>
|
|
|
|
</div>
|
|
|
|
<div class="detail-item"><span class="label">优先级:</span><span class="value">{{ getPriorityLabel(currentTicket.priority) }}</span></div>
|
|
|
|
<div class="detail-item">
|
|
|
|
<div class="detail-item"><span class="label">工单状态:</span><span class="value">{{ getStatusLabel(currentTicket.status) }}</span></div>
|
|
|
|
<span class="label">联系方式:</span>
|
|
|
|
|
|
|
|
<span class="value">{{ currentTicket.contact }}</span>
|
|
|
|
<div class="detail-item"><span class="label">创建人姓名:</span><span class="value">{{ currentTicket.created_name }}</span></div>
|
|
|
|
</div>
|
|
|
|
<div class="detail-item"><span class="label">联系方式:</span><span class="value">
|
|
|
|
<div class="detail-item">
|
|
|
|
{{
|
|
|
|
<span class="label">预计完成时间:</span>
|
|
|
|
(() => {
|
|
|
|
<span class="value">{{ currentTicket.expected_completion_time }}</span>
|
|
|
|
if (!currentTicket) return '-';
|
|
|
|
</div>
|
|
|
|
return (currentTicket as any).contact || (currentTicket as any).contact_info || '-';
|
|
|
|
<div class="detail-item">
|
|
|
|
})()
|
|
|
|
<span class="label">工单状态:</span>
|
|
|
|
}}
|
|
|
|
<span class="value">{{ getStatusLabel(currentTicket.status) }}</span>
|
|
|
|
</span></div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="detail-item">
|
|
|
|
<div class="detail-item"><span class="label">故障发生时间:</span><span class="value">{{ currentTicket.fault_time }}</span></div>
|
|
|
|
<span class="label">创建时间:</span>
|
|
|
|
<div class="detail-item"><span class="label">派单时间:</span><span class="value">{{ currentTicket.dispatch_time }}</span></div>
|
|
|
|
<span class="value">{{ currentTicket.created_at }}</span>
|
|
|
|
<div class="detail-item"><span class="label">预计完成时间:</span><span class="value">{{ currentTicket.expected_completion_time }}</span></div>
|
|
|
|
</div>
|
|
|
|
<div class="detail-item"><span class="label">处理人:</span><span class="value">{{ currentTicket.handler }}</span></div>
|
|
|
|
<div class="detail-item">
|
|
|
|
<div class="detail-item"><span class="label">故障描述:</span><span class="value">
|
|
|
|
<span class="label">更新时间:</span>
|
|
|
|
{{
|
|
|
|
<span class="value">{{ currentTicket.updated_at }}</span>
|
|
|
|
(() => {
|
|
|
|
|
|
|
|
if (!currentTicket) return '-';
|
|
|
|
|
|
|
|
const desc = (currentTicket as any).fault_description || (currentTicket as any).fault_description || '-';
|
|
|
|
|
|
|
|
return desc || '-';
|
|
|
|
|
|
|
|
})()
|
|
|
|
|
|
|
|
}}
|
|
|
|
|
|
|
|
</span></div>
|
|
|
|
|
|
|
|
<div class="detail-item"><span class="label">处理完成时间:</span><span class="value">{{ getStatusLabel(currentTicket.status) === '已完成' ? currentTicket.updated_at : '-' }}</span></div>
|
|
|
|
|
|
|
|
<div class="detail-item"><span class="label">图片:</span>
|
|
|
|
|
|
|
|
<span class="value">
|
|
|
|
|
|
|
|
<el-image
|
|
|
|
|
|
|
|
v-for="(img, idx) in currentTicket.images"
|
|
|
|
|
|
|
|
:key="idx"
|
|
|
|
|
|
|
|
:src="img"
|
|
|
|
|
|
|
|
style="width: 60px; margin-right: 8px;"
|
|
|
|
|
|
|
|
fit="cover"
|
|
|
|
|
|
|
|
:preview-src-list="currentTicket.images"
|
|
|
|
|
|
|
|
:initial-index="idx"
|
|
|
|
|
|
|
|
v-if="currentTicket.images && currentTicket.images.length"
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
<span v-if="!currentTicket.images || !currentTicket.images.length">-</span>
|
|
|
|
|
|
|
|
</span>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</el-dialog>
|
|
|
|
</el-dialog>
|
|
|
|
@ -251,7 +298,22 @@
|
|
|
|
<el-dialog v-model="showDispatchDialog" title="派工" width="400px">
|
|
|
|
<el-dialog v-model="showDispatchDialog" title="派工" width="400px">
|
|
|
|
<el-form :model="dispatchForm" label-width="80px">
|
|
|
|
<el-form :model="dispatchForm" label-width="80px">
|
|
|
|
<el-form-item label="派工人">
|
|
|
|
<el-form-item label="派工人">
|
|
|
|
<el-input v-model="dispatchForm.handler" />
|
|
|
|
<el-select v-model="dispatchForm.handler" filterable placeholder="请选择处理人">
|
|
|
|
|
|
|
|
<el-option
|
|
|
|
|
|
|
|
v-for="user in handlerOptions"
|
|
|
|
|
|
|
|
:key="user.username"
|
|
|
|
|
|
|
|
:label="user.nickname"
|
|
|
|
|
|
|
|
:value="user.username"
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
<el-form-item label="优先级">
|
|
|
|
|
|
|
|
<el-select v-model="dispatchForm.priority" placeholder="请选择优先级">
|
|
|
|
|
|
|
|
<el-option v-for="item in priorities" :key="item.value" :label="item.label" :value="item.value" />
|
|
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
<el-form-item label="预计完成时间" required>
|
|
|
|
|
|
|
|
<el-date-picker v-model="dispatchForm.expected_completion_time" format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" type="datetime" style="width: 100%" />
|
|
|
|
</el-form-item>
|
|
|
|
</el-form-item>
|
|
|
|
<el-form-item label="备注">
|
|
|
|
<el-form-item label="备注">
|
|
|
|
<el-input v-model="dispatchForm.remark" type="textarea" />
|
|
|
|
<el-input v-model="dispatchForm.remark" type="textarea" />
|
|
|
|
@ -273,6 +335,34 @@
|
|
|
|
>
|
|
|
|
>
|
|
|
|
<fault-management-view />
|
|
|
|
<fault-management-view />
|
|
|
|
</el-dialog>
|
|
|
|
</el-dialog>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 处理弹窗 -->
|
|
|
|
|
|
|
|
<el-dialog v-model="showProcessDialog" title="工单处理" width="500px">
|
|
|
|
|
|
|
|
<el-form :model="processForm" label-width="100px">
|
|
|
|
|
|
|
|
<el-form-item label="故障描述" required>
|
|
|
|
|
|
|
|
<el-input v-model="processForm.fault_description" type="textarea" placeholder="请输入故障描述" />
|
|
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
<el-form-item label="上传图片">
|
|
|
|
|
|
|
|
<el-upload
|
|
|
|
|
|
|
|
action="/filestransfer/upload"
|
|
|
|
|
|
|
|
list-type="picture-card"
|
|
|
|
|
|
|
|
:file-list="processForm.images"
|
|
|
|
|
|
|
|
:on-remove="handleRemoveImage"
|
|
|
|
|
|
|
|
:on-success="handleUploadSuccess"
|
|
|
|
|
|
|
|
:before-upload="beforeImageUpload"
|
|
|
|
|
|
|
|
accept="image/*"
|
|
|
|
|
|
|
|
:limit="5"
|
|
|
|
|
|
|
|
multiple
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
<i class="el-icon-plus"></i>
|
|
|
|
|
|
|
|
</el-upload>
|
|
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
|
|
<template #footer>
|
|
|
|
|
|
|
|
<el-button @click="showProcessDialog = false">取消</el-button>
|
|
|
|
|
|
|
|
<el-button type="primary" @click="submitProcess">确定</el-button>
|
|
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
</el-dialog>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
@ -286,6 +376,8 @@ import type { TicketItem, TicketDetail } from '../types/ticket'
|
|
|
|
import FaultManagementView from './FaultManagementView.vue'
|
|
|
|
import FaultManagementView from './FaultManagementView.vue'
|
|
|
|
import { fetchTicketList } from '../api/ticket'
|
|
|
|
import { fetchTicketList } from '../api/ticket'
|
|
|
|
import { ArrowDown } from '@element-plus/icons-vue'
|
|
|
|
import { ArrowDown } from '@element-plus/icons-vue'
|
|
|
|
|
|
|
|
import dayjs from 'dayjs'
|
|
|
|
|
|
|
|
import rpc from '../utils/rpc'
|
|
|
|
|
|
|
|
|
|
|
|
const ticketStore = useTicketStore()
|
|
|
|
const ticketStore = useTicketStore()
|
|
|
|
const userStore = useUserStore()
|
|
|
|
const userStore = useUserStore()
|
|
|
|
@ -298,21 +390,22 @@ const ticketList = ref<TicketItem[]>([])
|
|
|
|
|
|
|
|
|
|
|
|
// 标签页配置
|
|
|
|
// 标签页配置
|
|
|
|
const tabs = [
|
|
|
|
const tabs = [
|
|
|
|
|
|
|
|
{ label: '我的工单', value: 'mine' },
|
|
|
|
{ label: '全部工单', value: 'all' },
|
|
|
|
{ label: '全部工单', value: 'all' },
|
|
|
|
{ label: '我的工单', value: 'my' }
|
|
|
|
|
|
|
|
]
|
|
|
|
]
|
|
|
|
const currentTab = ref('all')
|
|
|
|
const currentTab = ref('mine')
|
|
|
|
|
|
|
|
|
|
|
|
// 状态配置
|
|
|
|
// 状态配置
|
|
|
|
const statuses = [
|
|
|
|
const statuses = [
|
|
|
|
{ label: '新建', value: 'new' },
|
|
|
|
{ label: '新建', value: 'new' },
|
|
|
|
{ label: '已审核', value: 'reviewed' },
|
|
|
|
// { label: '已审核', value: 'reviewed' },
|
|
|
|
{ label: '已派发', value: 'dispatched' },
|
|
|
|
{ label: '已派发', value: 'dispatched' },
|
|
|
|
{ label: '已分配', value: 'assigned' },
|
|
|
|
// { label: '已分配', value: 'assigned' },
|
|
|
|
{ label: '处理中', value: 'in_progress' },
|
|
|
|
// { label: '处理中', value: 'in_progress' },
|
|
|
|
{ label: '已完成', value: 'completed' },
|
|
|
|
{ label: '已完成', value: 'completed' },
|
|
|
|
{ label: '已关闭', value: 'closed' },
|
|
|
|
// { label: '已关闭', value: 'closed' },
|
|
|
|
{ label: '已驳回', value: 'rejected' }
|
|
|
|
// { label: '已驳回', value: 'rejected' }
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
// 优先级配置
|
|
|
|
// 优先级配置
|
|
|
|
@ -324,9 +417,9 @@ const priorities = [
|
|
|
|
|
|
|
|
|
|
|
|
// 来源配置
|
|
|
|
// 来源配置
|
|
|
|
const sources = [
|
|
|
|
const sources = [
|
|
|
|
{ label: '市民举报', value: 'CITIZEN' },
|
|
|
|
{ label: '市民举报', value: '市民举报' },
|
|
|
|
{ label: '巡检发现', value: 'INSPECTION' },
|
|
|
|
{ label: '巡检发现', value: '巡检发现' },
|
|
|
|
{ label: '监控系统', value: 'MONITOR' }
|
|
|
|
{ label: '监控系统', value: '监控系统' }
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
interface Filters {
|
|
|
|
interface Filters {
|
|
|
|
@ -358,19 +451,22 @@ const showHistory = ref(false)
|
|
|
|
const showFaultManagement = ref(false)
|
|
|
|
const showFaultManagement = ref(false)
|
|
|
|
const viewLoading = ref(false)
|
|
|
|
const viewLoading = ref(false)
|
|
|
|
const showDispatchDialog = ref(false)
|
|
|
|
const showDispatchDialog = ref(false)
|
|
|
|
|
|
|
|
const showProcessDialog = ref(false)
|
|
|
|
|
|
|
|
|
|
|
|
// 当前选中的工单
|
|
|
|
// 当前选中的工单
|
|
|
|
const currentTicket = ref<TicketDetail | null>(null)
|
|
|
|
const currentTicket = ref<TicketDetail | null>(null)
|
|
|
|
|
|
|
|
|
|
|
|
// 创建工单表单
|
|
|
|
// 创建工单表单
|
|
|
|
const createForm = reactive({
|
|
|
|
const initialCreateForm = () => ({
|
|
|
|
title: '',
|
|
|
|
title: '',
|
|
|
|
description: '',
|
|
|
|
description: '',
|
|
|
|
source: '',
|
|
|
|
source: '',
|
|
|
|
priority: '',
|
|
|
|
priority: '',
|
|
|
|
contact: '',
|
|
|
|
contact: '',
|
|
|
|
expected_completion_time: ''
|
|
|
|
expected_completion_time: '',
|
|
|
|
|
|
|
|
comm_uid: ''
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
const createForm = reactive(initialCreateForm())
|
|
|
|
|
|
|
|
|
|
|
|
// 状态更新表单
|
|
|
|
// 状态更新表单
|
|
|
|
const statusForm = ref({
|
|
|
|
const statusForm = ref({
|
|
|
|
@ -382,7 +478,16 @@ const statusForm = ref({
|
|
|
|
const dispatchForm = reactive({
|
|
|
|
const dispatchForm = reactive({
|
|
|
|
handler: '',
|
|
|
|
handler: '',
|
|
|
|
remark: '',
|
|
|
|
remark: '',
|
|
|
|
ticketId: null as number | null
|
|
|
|
ticketId: null as number | null,
|
|
|
|
|
|
|
|
priority: '',
|
|
|
|
|
|
|
|
expected_completion_time: ''
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 处理表单
|
|
|
|
|
|
|
|
const processForm = reactive({
|
|
|
|
|
|
|
|
id: null as number | null,
|
|
|
|
|
|
|
|
fault_description: '',
|
|
|
|
|
|
|
|
images: [] as any[]
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 获取工单列表
|
|
|
|
// 获取工单列表
|
|
|
|
@ -395,18 +500,18 @@ const fetchList = async () => {
|
|
|
|
and: {},
|
|
|
|
and: {},
|
|
|
|
orderby: [{ created_at: 'desc' }]
|
|
|
|
orderby: [{ created_at: 'desc' }]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (filters.keyword) params.like.title = [filters.keyword]
|
|
|
|
if (filters.keyword) params.like.title = ['%' + filters.keyword + '%']
|
|
|
|
if (filters.status) params.and.status = [filters.status]
|
|
|
|
if (filters.status) params.and.status = [filters.status]
|
|
|
|
if (filters.priority) params.and.priority = [filters.priority]
|
|
|
|
if (filters.priority) params.and.priority = [filters.priority]
|
|
|
|
if (filters.startTime && filters.endTime) {
|
|
|
|
if (filters.startTime && filters.endTime) {
|
|
|
|
params.where = [
|
|
|
|
params.where = [
|
|
|
|
{ field: 'created_at', operator: '>=', value: filters.startTime },
|
|
|
|
{ field: 'fault_time', operator: '>=', value: dayjs(filters.startTime).format('YYYY-MM-DD HH:mm:ss') },
|
|
|
|
{ field: 'created_at', operator: '<=', value: filters.endTime }
|
|
|
|
{ field: 'fault_time', operator: '<=', value: dayjs(filters.endTime).format('YYYY-MM-DD HH:mm:ss') }
|
|
|
|
]
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 只查自己的工单
|
|
|
|
// 只查自己的工单
|
|
|
|
if (currentTab.value === 'mine') {
|
|
|
|
if (currentTab.value === 'mine') {
|
|
|
|
params.and.handler = [userStore.nickname]
|
|
|
|
params.and.handler = [userStore.username]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const res: any = await fetchTicketList(params)
|
|
|
|
const res: any = await fetchTicketList(params)
|
|
|
|
ticketList.value = res?.rows || []
|
|
|
|
ticketList.value = res?.rows || []
|
|
|
|
@ -462,13 +567,14 @@ const handleCreate = async () => {
|
|
|
|
...createForm,
|
|
|
|
...createForm,
|
|
|
|
status: 'new',
|
|
|
|
status: 'new',
|
|
|
|
creator_id: userStore.token || 1, // 用token或实际用户ID
|
|
|
|
creator_id: userStore.token || 1, // 用token或实际用户ID
|
|
|
|
fault_time: new Date().toISOString(),
|
|
|
|
fault_time: dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
|
|
|
expected_completion_time: createForm.expected_completion_time
|
|
|
|
expected_completion_time: createForm.expected_completion_time
|
|
|
|
? new Date(createForm.expected_completion_time).toISOString()
|
|
|
|
? dayjs(createForm.expected_completion_time).format('YYYY-MM-DD HH:mm:ss')
|
|
|
|
: undefined
|
|
|
|
: undefined
|
|
|
|
})
|
|
|
|
})
|
|
|
|
ElMessage.success('创建成功')
|
|
|
|
ElMessage.success('创建成功')
|
|
|
|
showCreateDialog.value = false
|
|
|
|
showCreateDialog.value = false
|
|
|
|
|
|
|
|
Object.assign(createForm, initialCreateForm())
|
|
|
|
fetchList()
|
|
|
|
fetchList()
|
|
|
|
} catch (e: any) {
|
|
|
|
} catch (e: any) {
|
|
|
|
console.error(e)
|
|
|
|
console.error(e)
|
|
|
|
@ -584,20 +690,132 @@ const handleDispatch = (row: any) => {
|
|
|
|
dispatchForm.handler = ''
|
|
|
|
dispatchForm.handler = ''
|
|
|
|
dispatchForm.remark = ''
|
|
|
|
dispatchForm.remark = ''
|
|
|
|
dispatchForm.ticketId = row.id
|
|
|
|
dispatchForm.ticketId = row.id
|
|
|
|
|
|
|
|
dispatchForm.priority = row.priority
|
|
|
|
|
|
|
|
dispatchForm.expected_completion_time = row.expected_completion_time
|
|
|
|
showDispatchDialog.value = true
|
|
|
|
showDispatchDialog.value = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const submitDispatch = () => {
|
|
|
|
const submitDispatch = async () => {
|
|
|
|
|
|
|
|
if (!dispatchForm.handler) {
|
|
|
|
|
|
|
|
ElMessage.error('请填写派工人')
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!dispatchForm.expected_completion_time) {
|
|
|
|
|
|
|
|
ElMessage.error('请填写预计完成时间')
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
await ticketStore.processTicket({
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
id: dispatchForm.ticketId,
|
|
|
|
|
|
|
|
operator_id: userStore.token,
|
|
|
|
|
|
|
|
handler: dispatchForm.handler,
|
|
|
|
|
|
|
|
expected_completion_time: dayjs(dispatchForm.expected_completion_time).format('YYYY-MM-DD HH:mm:ss'),
|
|
|
|
|
|
|
|
// 优先级
|
|
|
|
|
|
|
|
priority: dispatchForm.priority,
|
|
|
|
|
|
|
|
remark: dispatchForm.remark
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: 调用派工API
|
|
|
|
// TODO: 调用派工API
|
|
|
|
ElMessage.success(`工单${dispatchForm.ticketId}已派工给:${dispatchForm.handler}`)
|
|
|
|
ElMessage.success(`工单${dispatchForm.ticketId}已派工给:${dispatchForm.handler}`)
|
|
|
|
showDispatchDialog.value = false
|
|
|
|
showDispatchDialog.value = false
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fetchList()
|
|
|
|
// 可在此刷新列表
|
|
|
|
// 可在此刷新列表
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 处理工单
|
|
|
|
|
|
|
|
const handleProcess = (row: any) => {
|
|
|
|
|
|
|
|
processForm.id = row.id
|
|
|
|
|
|
|
|
processForm.fault_description = ''
|
|
|
|
|
|
|
|
processForm.images = []
|
|
|
|
|
|
|
|
showProcessDialog.value = true
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const handleRemoveImage = (file: any, fileList: any[]) => {
|
|
|
|
|
|
|
|
processForm.images = fileList
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const handleUploadSuccess = (response: any, file: any, fileList: any[]) => {
|
|
|
|
|
|
|
|
// 假设后端返回的图片url在 response.url
|
|
|
|
|
|
|
|
file.url = response.url || response?.result?.url || ''
|
|
|
|
|
|
|
|
processForm.images = fileList
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const beforeImageUpload = (file: File) => {
|
|
|
|
|
|
|
|
const isImage = file.type.startsWith('image/')
|
|
|
|
|
|
|
|
if (!isImage) {
|
|
|
|
|
|
|
|
ElMessage.error('只能上传图片文件!')
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return isImage
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const submitProcess = async () => {
|
|
|
|
|
|
|
|
if (!processForm.fault_description) {
|
|
|
|
|
|
|
|
ElMessage.error('请输入故障描述')
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// 模拟图片上传,实际应上传到服务器后拿到url
|
|
|
|
|
|
|
|
const imageUrls = processForm.images.map(f => f.url || f.response?.url).filter(Boolean)
|
|
|
|
|
|
|
|
await ticketStore.processTicket({
|
|
|
|
|
|
|
|
id: processForm.id,
|
|
|
|
|
|
|
|
operator_id: userStore.token,
|
|
|
|
|
|
|
|
fault_description: processForm.fault_description,
|
|
|
|
|
|
|
|
images: imageUrls
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
ElMessage.success('处理成功')
|
|
|
|
|
|
|
|
showProcessDialog.value = false
|
|
|
|
|
|
|
|
fetchList()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 判断是否可以处理
|
|
|
|
|
|
|
|
const canProcess = (status: string, row?: any) => {
|
|
|
|
|
|
|
|
// 处理人必须是自己
|
|
|
|
|
|
|
|
const currentUser = userStore.username
|
|
|
|
|
|
|
|
const isMine = row && row.handler && row.handler === currentUser
|
|
|
|
|
|
|
|
return ['in_progress', 'dispatched', 'assigned'].includes(status) && isMine
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const handlerOptions = ref<{username: string, nickname: string}[]>([])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const fetchHandlerOptions = async () => {
|
|
|
|
|
|
|
|
const res = await rpc.post('/account/list', { limit: -1 }) as any
|
|
|
|
|
|
|
|
handlerOptions.value = (res?.rows || []).map((item: any) => ({
|
|
|
|
|
|
|
|
username: item.username,
|
|
|
|
|
|
|
|
nickname: item.nickname || item.username
|
|
|
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const deviceOptions = ref<{ commUid: string, ilcName: string }[]>([])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const fetchDeviceOptions = async () => {
|
|
|
|
|
|
|
|
const res = await rpc.post('/lightm/ilc/list', { limit: -1 }) as any
|
|
|
|
|
|
|
|
deviceOptions.value = (res?.rows || []).map((item: any) => ({
|
|
|
|
|
|
|
|
commUid: item.commUid,
|
|
|
|
|
|
|
|
ilcName: item.ilcName || item.commUid
|
|
|
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化
|
|
|
|
// 初始化
|
|
|
|
onMounted(() => {
|
|
|
|
onMounted(() => {
|
|
|
|
fetchList()
|
|
|
|
fetchList()
|
|
|
|
|
|
|
|
fetchHandlerOptions()
|
|
|
|
|
|
|
|
fetchDeviceOptions()
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const getStatusType = (status: string) => {
|
|
|
|
|
|
|
|
switch (status) {
|
|
|
|
|
|
|
|
case 'new': return 'info';
|
|
|
|
|
|
|
|
case 'dispatched': return 'primary';
|
|
|
|
|
|
|
|
case 'assigned': return 'warning';
|
|
|
|
|
|
|
|
case 'in_progress': return 'warning';
|
|
|
|
|
|
|
|
case 'completed': return 'success';
|
|
|
|
|
|
|
|
case 'closed': return 'default';
|
|
|
|
|
|
|
|
case 'rejected': return 'danger';
|
|
|
|
|
|
|
|
default: return 'info';
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
<style scoped>
|
|
|
|
@ -1233,25 +1451,6 @@ input:checked + .slider:before {
|
|
|
|
z-index: 999;
|
|
|
|
z-index: 999;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 自定义滚动条 */
|
|
|
|
|
|
|
|
::-webkit-scrollbar {
|
|
|
|
|
|
|
|
width: 6px;
|
|
|
|
|
|
|
|
height: 6px;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
::-webkit-scrollbar-thumb {
|
|
|
|
|
|
|
|
background: rgba(255, 255, 255, 0.1);
|
|
|
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
::-webkit-scrollbar-thumb:hover {
|
|
|
|
|
|
|
|
background: rgba(255, 255, 255, 0.2);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
::-webkit-scrollbar-track {
|
|
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.fault-management-dialog {
|
|
|
|
.fault-management-dialog {
|
|
|
|
:deep(.el-dialog) {
|
|
|
|
:deep(.el-dialog) {
|
|
|
|
margin: 0 !important;
|
|
|
|
margin: 0 !important;
|
|
|
|
|