| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022 |
- <!-- 业务工单管理 -->
- <template>
- <div class="business-work-order">
- <!-- 顶部搜索区 -->
- <el-card class="search-card" shadow="never">
- <div class="search-form">
- <el-form :model="queryParams" ref="queryFormRef" :inline>
- <el-form-item label="类型">
- <el-select v-model="queryParams.orderType" placeholder="全部类型" clearable style="width: 150px">
- <el-option label="故障维修" :value="1" />
- <el-option label="日常巡检" :value="2" />
- <el-option label="设备保养" :value="3" />
- </el-select>
- </el-form-item>
- <el-form-item label="优先级">
- <el-select v-model="queryParams.orderLevel" placeholder="全部优先级" clearable style="width: 120px">
- <el-option label="紧急" :value="1" />
- <el-option label="一般" :value="2" />
- <el-option label="低" :value="3" />
- </el-select>
- </el-form-item>
- <el-form-item label="状态">
- <el-select v-model="queryParams.orderStatus" placeholder="全部状态" clearable style="width: 120px">
- <el-option label="待派单" :value="1" />
- <el-option label="待接单" :value="2" />
- <el-option label="已接单" :value="3" />
- <el-option label="处理中" :value="4" />
- <el-option label="已提交验收" :value="5" />
- <el-option label="已结案" :value="6" />
- <el-option label="已驳回" :value="7" />
- </el-select>
- </el-form-item>
- <el-form-item label="关键词">
- <el-input
- v-model="queryParams.keyword"
- placeholder="工单编号/设备编码"
- clearable
- style="width: 200px"
- />
- </el-form-item>
- <el-form-item>
- <el-button type="primary" @click="handleSearch">查询</el-button>
- <el-button @click="handleReset">重置</el-button>
- </el-form-item>
- </el-form>
- </div>
- </el-card>
- <!-- 主容器 -->
- <el-row :gutter="16" style="height: calc(100vh - 200px)">
- <!-- 左侧:工单列表 -->
- <el-col :span="8" style="height: 100%">
- <el-card class="box-card" shadow="always">
- <template #header>
- <div class="card-header">
- <span>工单列表</span>
- </div>
- </template>
-
- <!-- 工单列表 -->
- <el-scrollbar style="flex: 1">
- <div class="order-list">
- <div
- v-for="order in tableData"
- :key="order.orderId"
- class="order-item"
- :class="{ 'active': selectedOrderId === order.orderId }"
- @click="handleSelectOrder(order)"
- >
- <div class="order-header">
- <span class="order-no">{{ order.orderNo }}</span>
- <el-tag
- v-if="order.orderStatus === 1"
- type="warning"
- size="small"
- effect="light"
- class="status-badge"
- >待派单</el-tag>
- <el-tag
- v-else-if="order.orderStatus === 2"
- type="info"
- size="small"
- effect="light"
- class="status-badge"
- >待接单</el-tag>
- <el-tag
- v-else-if="order.orderStatus === 3"
- type="success"
- size="small"
- effect="light"
- class="status-badge"
- >已接单</el-tag>
- <el-tag
- v-else-if="order.orderStatus === 4"
- type="danger"
- size="small"
- effect="light"
- class="status-badge"
- >处理中</el-tag>
- <el-tag
- v-else-if="order.orderStatus === 5"
- type="success"
- size="small"
- effect="light"
- class="status-badge"
- >已提交验收</el-tag>
- <el-tag
- v-else-if="order.orderStatus === 6"
- type="info"
- size="small"
- effect="light"
- class="status-badge"
- >已结案</el-tag>
- <el-tag
- v-else-if="order.orderStatus === 7"
- type="danger"
- size="small"
- effect="light"
- class="status-badge"
- >已驳回</el-tag>
- </div>
- <div class="order-body">
- <div class="order-tags">
- <el-tag :type="getLevelTag(order.orderLevel)" size="small" effect="plain">{{ getLevelLabel(order.orderLevel) }}</el-tag>
- <el-tag :type="getTypeTag(order.orderType)" size="small" effect="plain">{{ getTypeLabel(order.orderType) }}</el-tag>
- </div>
- <div class="order-desc" v-if="order.orderDesc">{{ order.orderDesc }}</div>
- <div class="device-code">设备编号:{{ order.deviceCode || '-' }}</div>
- <div class="device-code" v-if="order.hazardNo">关联隐患:{{ order.hazardNo }}</div>
- <div class="create-time">创建:{{ formatDate(order.createTime) }}</div>
- </div>
- </div>
- <el-empty v-if="tableData.length === 0" description="暂无数据" :image-size="100"></el-empty>
- </div>
- </el-scrollbar>
-
- <!-- 分页 -->
- <div class="pagination-wrapper">
- <el-pagination
- v-model:current-page="pageNum"
- v-model:page-size="pageSize"
- :total="total"
- layout="total, prev, pager, next"
- :page-sizes="[10, 20, 50, 100]"
- @size-change="handleSizeChange"
- @current-change="handlePageChange"
- class="pagination"
- />
- </div>
- </el-card>
- </el-col>
-
- <!-- 右侧:工单详情 -->
- <el-col :span="16" style="height: 100%">
- <el-card v-loading="detailLoading" class="box-card" shadow="always">
- <template #header>
- <div class="card-header">
- <span v-if="selectedOrder && selectedOrder.orderId">工单详情 - {{ selectedOrder.orderNo }}</span>
- <span v-else>请选择工单查看详情</span>
- </div>
- </template>
-
- <div v-if="selectedOrder && selectedOrder.orderId">
- <!-- 标签页 -->
- <el-tabs v-model="activeTab" class="detail-tabs">
- <!-- 工单信息 -->
- <el-tab-pane label="工单信息" name="info">
- <el-descriptions :column="2" border size="small">
- <el-descriptions-item label="工单编号">{{ selectedOrder.orderNo }}</el-descriptions-item>
- <el-descriptions-item label="工单状态">
- <el-tag
- v-if="selectedOrder.orderStatus === 1"
- type="warning"
- size="small"
- >待派单</el-tag>
- <el-tag
- v-else-if="selectedOrder.orderStatus === 2"
- type="info"
- size="small"
- >待接单</el-tag>
- <el-tag
- v-else-if="selectedOrder.orderStatus === 3"
- type="success"
- size="small"
- >已接单</el-tag>
- <el-tag
- v-else-if="selectedOrder.orderStatus === 4"
- type="danger"
- size="small"
- >处理中</el-tag>
- <el-tag
- v-else-if="selectedOrder.orderStatus === 5"
- type="success"
- size="small"
- >已提交验收</el-tag>
- <el-tag
- v-else-if="selectedOrder.orderStatus === 6"
- type="info"
- size="small"
- >已结案</el-tag>
- <el-tag
- v-else-if="selectedOrder.orderStatus === 7"
- type="danger"
- size="small"
- >已驳回</el-tag>
- </el-descriptions-item>
- <el-descriptions-item label="关联案件编号">{{ selectedOrder.caseNo || '-' }}</el-descriptions-item>
- <el-descriptions-item label="关联隐患编号">{{ selectedOrder.hazardNo || '-' }}</el-descriptions-item>
- <el-descriptions-item label="设备编号">{{ selectedOrder.deviceCode || '-' }}</el-descriptions-item>
- <el-descriptions-item label="优先级">
- <el-tag
- v-if="selectedOrder.orderLevel === 1"
- type="danger"
- size="small"
- >紧急</el-tag>
- <el-tag
- v-else-if="selectedOrder.orderLevel === 2"
- type="warning"
- size="small"
- >一般</el-tag>
- <el-tag
- v-else
- type="info"
- size="small"
- >低</el-tag>
- </el-descriptions-item>
- <el-descriptions-item label="工单类型">{{ getOrderTypeText(selectedOrder.orderType) }}</el-descriptions-item>
- <el-descriptions-item label="问题描述" :span="2">{{ selectedOrder.orderDesc }}</el-descriptions-item>
- <el-descriptions-item label="创建时间">{{ formatDate(selectedOrder.createTime) }}</el-descriptions-item>
- <el-descriptions-item label="更新时间">{{ formatDate(selectedOrder.updateTime) }}</el-descriptions-item>
- <el-descriptions-item label="运维状态">在用</el-descriptions-item>
- <el-descriptions-item label="在线状态">在线</el-descriptions-item>
- <el-descriptions-item label="报警状态">正常</el-descriptions-item>
- <el-descriptions-item label="处置结果">{{ selectedOrder.repairResult || '-' }}</el-descriptions-item>
- </el-descriptions>
-
- <RepairImagePanel :before-images="selectedOrder.maintenanceBeforeImg" :after-images="selectedOrder.maintenanceAfterImg" />
-
- <!-- 工单操作 -->
- <div class="action-section">
- <h4 class="section-title">工单操作</h4>
- <div class="action-buttons">
- <el-button
- v-if="canDispatch()"
- type="primary"
- icon="Download"
- @click="handleDispatch"
- >下派任务</el-button>
- <el-button
- v-if="canVerify()"
- type="warning"
- icon="Check"
- @click="handleVerify"
- >核查</el-button>
- <el-button
- v-if="canProcess()"
- type="success"
- icon="Edit"
- @click="handleProcess"
- >处理</el-button>
- <el-button
- v-if="canComplete()"
- type="danger"
- icon="Checked"
- @click="handleComplete"
- >提交验收</el-button>
- <el-button
- v-if="canClose()"
- type="success"
- icon="CircleCheck"
- @click="handleClose"
- >验收通过</el-button>
- <el-button
- v-if="canReject()"
- type="info"
- icon="Close"
- @click="handleReject"
- >驳回</el-button>
- </div>
- </div>
- </el-tab-pane>
-
- <!-- 处理历史 -->
- <el-tab-pane label="处理历史" name="history">
- <div class="timeline-scroll">
- <el-timeline v-if="currentOrderLogs && currentOrderLogs.length > 0">
- <el-timeline-item
- v-for="log in currentOrderLogs"
- :key="log.logId"
- :timestamp="formatDate(log.operTime)"
- placement="top"
- :type="getLogType(log.operType)"
- :size="'large'"
- >
- <el-card shadow="hover">
- <div class="timeline-content">
- <div class="timestamp">{{ formatDate(log.operTime) }}</div>
- <div class="operator-info">
- <span class="operator-name">{{ log.operUserName }}</span>
- <span class="oper-type">· {{ getOperTypeLabel(log.operType) }}</span>
- </div>
- <div class="oper-desc">{{ log.operDesc }}</div>
- </div>
- </el-card>
- </el-timeline-item>
- </el-timeline>
- <el-empty v-else description="暂无处理记录" :image-size="100"></el-empty>
- </div>
- </el-tab-pane>
- </el-tabs>
- </div>
-
- <el-empty v-else description="请从左侧选择工单" :image-size="150"></el-empty>
- </el-card>
- </el-col>
- </el-row>
-
- <!-- 派单弹窗 -->
- <el-dialog v-model="dispatchDialogVisible" title="下派任务 - 选择接单人" width="750px">
- <div style="display: flex; gap: 16px; min-height: 350px;">
- <!-- 左侧:部门树 -->
- <div style="width: 280px; border-right: 1px solid #e4e7ed; padding-right: 12px;">
- <el-input
- v-model="deptFilterText"
- placeholder="筛选部门"
- clearable
- size="small"
- style="margin-bottom: 8px"
- />
- <el-tree
- ref="deptTreeRef"
- :data="deptTreeData"
- :props="{ label: 'deptName', children: 'children' }"
- :filter-node-method="filterDeptNode"
- node-key="deptId"
- highlight-current
- @node-click="handleDeptClick"
- style="font-size: 14px"
- />
- </div>
- <!-- 右侧:用户列表 -->
- <div style="flex: 1; min-width: 0;">
- <el-table
- :data="userList"
- border
- size="small"
- max-height="300"
- ref="userTableRef"
- row-key="userId"
- @selection-change="handleSelectionChange"
- @row-click="handleRowClick"
- >
- <el-table-column type="selection" width="40" align="center" :reserve-selection="true" />
- <el-table-column prop="userName" label="用户名" width="100" />
- <el-table-column prop="nickName" label="昵称" width="100" />
- <el-table-column prop="dept.deptName" label="部门" show-overflow-tooltip />
- </el-table>
- <div v-if="userList.length === 0" style="text-align: center; color: #909399; padding: 40px 0;">请先在左侧选择部门</div>
- </div>
- </div>
- <el-form :model="dispatchForm" label-width="80px" style="margin-top: 16px">
- <el-form-item label="备注"><el-input v-model="dispatchForm.remarks" placeholder="请输入派单备注" /></el-form-item>
- </el-form>
- <template #footer>
- <span style="float: left; line-height: 32px; color: #606266;">
- 已选人员:<span v-if="selectedUserNames.length > 0" style="color: #409eff; font-weight: bold;">{{ selectedUserNames.join(', ') }}</span>
- <span v-else style="color: #909399">未选择</span>
- (<span v-if="dispatchForm.handlerIds && dispatchForm.handlerIds.length > 0">{{ dispatchForm.handlerIds.length }}</span><span v-else>0</span>)
- </span>
- <el-button @click="dispatchDialogVisible = false">取消</el-button>
- <el-button type="primary" @click="submitDispatch">确认</el-button>
- </template>
- </el-dialog>
-
- <!-- 核查/处理弹窗 -->
- <el-dialog v-model="actionDialogVisible" :title="actionTitle" width="600px">
- <ImageUpload v-if="actionTitle === '处理工单'" v-model="processBeforeImages" :limit="9" :isShowTip="true" :upload-url="imageUploadUrl" />
- <el-input v-model="actionFormData.solution" type="textarea" :rows="6" placeholder="请输入说明文字" />
- <template #footer>
- <el-button @click="actionDialogVisible = false">取消</el-button>
- <el-button type="primary" @click="submitAction">确定</el-button>
- </template>
- </el-dialog>
-
- <!-- 完成/验收弹窗 -->
- <el-dialog v-model="completeDialogVisible" title="提交验收" width="600px">
- <ImageUpload v-model="maintenanceAfterImages" :limit="9" :isShowTip="true" :upload-url="imageUploadUrl" />
- <el-form label-width="80px">
- <el-form-item label="验收意见">
- <el-input v-model="completeForm.result" type="textarea" :rows="4" />
- </el-form-item>
- </el-form>
- <template #footer>
- <el-button @click="completeDialogVisible = false">取消</el-button>
- <el-button type="primary" @click="submitComplete">提交</el-button>
- </template>
- </el-dialog>
-
- <!-- 验收通过/结案弹窗 -->
- <el-dialog v-model="closeDialogVisible" title="验收通过" width="500px">
- <el-form label-width="80px">
- <el-form-item label="验收结果">
- <el-input v-model="closeForm.result" type="textarea" :rows="4" placeholder="请输入验收结果/整改反馈" />
- </el-form-item>
- </el-form>
- <template #footer>
- <el-button @click="closeDialogVisible = false">取消</el-button>
- <el-button type="primary" @click="submitClose">确认验收</el-button>
- </template>
- </el-dialog>
- <!-- 驳回工单弹窗 -->
- <el-dialog v-model="rejectDialogVisible" title="驳回工单" width="500px">
- <el-form label-width="80px">
- <el-form-item label="驳回原因" required>
- <el-input v-model="rejectReason" type="textarea" :rows="4" placeholder="请输入驳回原因(驳回后工单状态为已驳回)" maxlength="300" show-word-limit />
- </el-form-item>
- </el-form>
- <template #footer>
- <el-button @click="rejectDialogVisible = false">取消</el-button>
- <el-button type="danger" :loading="rejecting" @click="submitReject">确认驳回</el-button>
- </template>
- </el-dialog>
- </div>
- </template>
- <script setup name="BusinessWorkOrder">
- import { ElMessage } from 'element-plus'
- import ImageUpload from '@/components/ImageUpload'
- import RepairImagePanel from '@/components/RepairImagePanel'
- import { listWorkOrder, getWorkOrderDetail, dispatchWorkOrder, verifyWorkOrder, processWorkOrder, completeWorkOrder, closeWorkOrder, rejectWorkOrder } from '@/api/work/order'
- import { listDept } from '@/api/system/dept'
- import { listUser } from '@/api/system/user'
- import { handleTree } from '@/utils/ruoyi'
- const listLoading = ref(false)
- const tableData = ref([])
- const pageNum = ref(1)
- const pageSize = ref(10)
- const total = ref(0)
- const queryFormRef = ref(null)
- const queryParams = reactive({
- orderType: null,
- orderLevel: null,
- keyword: '',
- // 燃气工单:只查询燃气类型设备工单
- equipmentType: 'gas'
- })
- // 选中的工单
- const selectedOrderId = ref(null)
- const selectedOrder = ref({})
- const activeTab = ref('info')
- const currentOrderLogs = ref([])
- // 派单相关
- const dispatchDialogVisible = ref(false)
- const dispatchForm = reactive({ handlerIds: [], remarks: '' })
- const deptTreeRef = ref(null)
- const userTableRef = ref(null)
- const deptTreeData = ref([])
- const deptFilterText = ref('')
- const userList = ref([])
- const selectedUserList = ref([]) // 存储选中的用户行数据
- // 部门筛选
- watch(deptFilterText, (val) => { deptTreeRef.value && deptTreeRef.value.filter(val) })
- function filterDeptNode(value, data) {
- if (!value) return true
- return data.deptName.includes(value)
- }
- // 加载部门树
- async function loadDeptTree() {
- if (deptTreeData.value.length > 0) return
- try {
- const res = await listDept()
- deptTreeData.value = handleTree(res.data || [], 'deptId')
- } catch (e) { console.error('加载部门失败', e) }
- }
- // 点击部门加载用户
- async function handleDeptClick(node) {
- try {
- // 清除当前选择
- userTableRef.value && userTableRef.value.clearSelection()
- selectedUserList.value = []
- dispatchForm.handlerIds = []
-
- const res = await listUser({ deptId: node.deptId, pageNum: 1, pageSize: 100 })
- userList.value = res.rows || []
- } catch (e) { console.error('加载用户失败', e) }
- }
- // 选择变化时触发
- function handleSelectionChange(selection) {
- selectedUserList.value = selection
- dispatchForm.handlerIds = selection.map(item => item.userId)
- }
- // 点击行切换选中状态
- function handleRowClick(row) {
- userTableRef.value && userTableRef.value.toggleRowSelection(row)
- }
- // 获取已选人员昵称(用于显示)
- const selectedUserNames = computed(() => {
- return selectedUserList.value.map(item => item.nickName || item.userName)
- })
- /** 查询工单列表 */
- async function handleSearch() {
- listLoading.value = true
- try {
- const res = await listWorkOrder(queryParams, pageNum.value, pageSize.value)
- if (res.code === 200) {
- tableData.value = res.data.records || []
- total.value = res.data.total || 0
- }
- } catch (e) {
- ElMessage.error('查询失败')
- } finally {
- listLoading.value = false
- }
- }
- /** 重置搜索 */
- function handleReset() {
- queryFormRef.value && queryFormRef.value.resetFields()
- queryParams.orderType = null
- queryParams.orderLevel = null
- queryParams.keyword = ''
- pageNum.value = 1
- selectedOrderId.value = null
- selectedOrder.value = {}
- handleSearch()
- }
- /** 选择工单 */
- async function handleSelectOrder(order) {
- selectedOrderId.value = order.orderId
- selectedOrder.value = { ...order } // 克隆对象确保更新
- activeTab.value = 'info'
- currentOrderLogs.value = []
-
- // 加载工单详情和处理历史
- await loadOrderDetail(order.orderId)
- }
- /** 分页变化 */
- function handleSizeChange() {
- pageNum.value = 1
- selectedOrderId.value = null
- selectedOrder.value = {}
- handleSearch()
- }
- function handlePageChange() {
- selectedOrderId.value = null
- selectedOrder.value = {}
- handleSearch()
- }
- /** 派单、核查、处理、完成、驳回的判断函数(示例) */
- function canDispatch() { return selectedOrder.value.orderStatus === 1 }
- function canVerify() { return [2, 3].includes(selectedOrder.value.orderStatus) }
- function canProcess() { return [3, 4].includes(selectedOrder.value.orderStatus) }
- function canComplete() { return selectedOrder.value.orderStatus === 4 }
- function canClose() { return selectedOrder.value.orderStatus === 5 }
- function canReject() { return [1, 2].includes(selectedOrder.value.orderStatus) }
- function handleDispatch() {
- if (!canDispatch()) {
- ElMessage.warning('仅可对待派单工单进行派单操作')
- return
- }
- Object.assign(dispatchForm, { handlerIds: [], remarks: '' })
- selectedUserList.value = []
- userList.value = []
- loadDeptTree()
- dispatchDialogVisible.value = true
- }
- async function submitDispatch() {
- if (!dispatchForm.handlerIds || dispatchForm.handlerIds.length === 0) {
- ElMessage.warning('请选择至少一个接单人')
- return
- }
- try {
- await dispatchWorkOrder({
- orderId: selectedOrder.value.orderId,
- handlerIds: dispatchForm.handlerIds, // 传递数组
- remarks: dispatchForm.remarks
- })
- ElMessage.success('派单成功!已向 ' + dispatchForm.handlerIds.length + ' 人下发任务')
- dispatchDialogVisible.value = false
- handleSearch()
- } catch (e) {
- ElMessage.error('派单失败')
- }
- }
- /** 核查、处理相关 */
- const actionDialogVisible = ref(false)
- const actionTitle = ref('')
- const actionFormData = reactive({ solution: '' })
- const processBeforeImages = ref('')
- const imageUploadUrl = import.meta.env.VITE_APP_BASE_API + '/minio/upload'
- function handleVerify() {
- if (![2, 3].includes(selectedOrder.value.orderStatus)) {
- ElMessage.warning('当前工单不允许核查')
- return
- }
- actionTitle.value = '核查工单'
- actionFormData.solution = ''
- processBeforeImages.value = ''
- actionDialogVisible.value = true
- }
- function handleProcess() {
- if (![3, 4].includes(selectedOrder.value.orderStatus)) {
- ElMessage.warning('当前工单不允许处理')
- return
- }
- actionTitle.value = '处理工单'
- actionFormData.solution = ''
- processBeforeImages.value = ''
- actionDialogVisible.value = true
- }
- async function submitAction() {
- if (!actionFormData.solution.trim()) {
- ElMessage.warning('请填写说明')
- return
- }
- try {
- const data = {
- orderId: selectedOrder.value.orderId,
- handleResult: 'solved',
- solution: actionFormData.solution
- }
- if (actionTitle.value === '处理工单' && processBeforeImages.value) {
- data.maintenanceBeforeImg = processBeforeImages.value
- }
- if (actionTitle.value === '核查工单') {
- await verifyWorkOrder(data)
- } else {
- await processWorkOrder(data)
- }
- ElMessage.success(actionTitle.value + '成功')
- actionDialogVisible.value = false
- handleSearch()
- } catch (e) {
- ElMessage.error('操作失败')
- }
- }
- /** 完成/验收相关 */
- const completeDialogVisible = ref(false)
- const completeForm = reactive({ result: '' })
- const maintenanceAfterImages = ref('')
- function handleComplete() {
- if (![4, 5].includes(selectedOrder.value.orderStatus)) {
- ElMessage.warning('当前工单不允许提交验收')
- return
- }
- completeForm.result = ''
- maintenanceAfterImages.value = ''
- completeDialogVisible.value = true
- }
- async function submitComplete() {
- if (!completeForm.result.trim()) {
- ElMessage.warning('请填写验收意见')
- return
- }
- try {
- await completeWorkOrder({
- orderId: selectedOrder.value.orderId,
- result: completeForm.result,
- maintenanceAfterImg: maintenanceAfterImages.value
- })
- ElMessage.success('提交验收成功')
- completeDialogVisible.value = false
- handleSearch()
- } catch (e) {
- ElMessage.error('提交失败')
- }
- }
- /** 驳回相关 */
- const rejectDialogVisible = ref(false)
- const rejectReason = ref('')
- const rejecting = ref(false)
- function handleReject() {
- if (![1, 2].includes(selectedOrder.value.orderStatus)) {
- ElMessage.warning('当前工单不允许驳回')
- return
- }
- rejectReason.value = ''
- rejectDialogVisible.value = true
- }
- async function submitReject() {
- if (!rejectReason.value.trim()) {
- ElMessage.warning('请填写驳回原因')
- return
- }
- rejecting.value = true
- try {
- await rejectWorkOrder({
- orderId: selectedOrder.value.orderId,
- reason: rejectReason.value.trim()
- })
- ElMessage.success('驳回成功,工单状态已更新为已驳回')
- rejectDialogVisible.value = false
- handleSearch()
- } catch (e) {
- ElMessage.error('驳回失败:' + (e.msg || e.message || '请重试'))
- } finally {
- rejecting.value = false
- }
- }
- /** 验收通过/结案相关 */
- const closeDialogVisible = ref(false)
- const closeForm = reactive({ result: '' })
- function handleClose() {
- if (selectedOrder.value.orderStatus !== 5) {
- ElMessage.warning('当前工单不允许验收通过')
- return
- }
- closeForm.result = ''
- closeDialogVisible.value = true
- }
- async function submitClose() {
- if (!closeForm.result.trim()) {
- ElMessage.warning('请填写验收结果')
- return
- }
- try {
- await closeWorkOrder({
- orderId: selectedOrder.value.orderId,
- result: closeForm.result
- })
- ElMessage.success('验收通过,关联隐患已同步为已整改')
- closeDialogVisible.value = false
- handleSearch()
- } catch (e) {
- ElMessage.error('验收失败')
- }
- }
- /** 辅助函数 */
- function getOrderTypeText(type) {
- const map = { 1: '故障维修', 2: '日常巡检', 3: '设备保养' }
- return map[type] || '-'
- }
- function getOperTypeLabel(type) {
- const map = {
- 1: '创建工单', 2: '派单', 3: '接单', 4: '开始维修',
- 5: '提交验收', 6: '验收通过', 7: '驳回工单', 8: '申请延期'
- }
- return map[type] || '-'
- }
- function getLogType(type) {
- const map = {
- 1: 'primary', 2: 'success', 3: 'warning', 4: 'danger',
- 5: 'success', 6: 'success', 7: 'danger', 8: 'warning'
- }
- return map[type] || ''
- }
- function formatDate(dateStr) {
- if (!dateStr) return '-'
- return new Date(dateStr).toLocaleString('zh-CN')
- }
- /** 优先级标签 */
- function getLevelLabel(level) {
- const map = { 1: '紧急', 2: '一般', 3: '低' }
- return map[level] || '未知'
- }
- function getLevelTag(level) {
- const map = { 1: 'danger', 2: 'warning', 3: 'info' }
- return map[level] || 'info'
- }
- /** 工单类型标签 */
- function getTypeLabel(type) {
- const map = { 1: '故障维修', 2: '日常巡检', 3: '设备保养' }
- return map[type] || '未知'
- }
- function getTypeTag(type) {
- const map = { 1: 'danger', 2: 'primary', 3: 'success' }
- return map[type] || 'info'
- }
- /** 加载工单详情和处理历史 */
- async function loadOrderDetail(orderId) {
- try {
- const res = await getWorkOrderDetail(orderId)
- if (res.code === 200) {
- selectedOrder.value = { ...selectedOrder.value, ...res.data.order }
- currentOrderLogs.value = res.data.logs || []
- }
- } catch (e) {
- ElMessage.error('加载工单详情失败')
- }
- }
- onMounted(() => {
- handleSearch()
- })
- </script>
- <style scoped lang="scss">
- .business-work-order {
- padding: 0 16px 16px;
-
- .search-card {
- margin-bottom: 16px;
-
- :deep(.el-card__body) {
- padding: 16px;
- }
- }
-
- .search-form {
- .el-form {
- display: flex;
- flex-wrap: wrap;
- align-items: flex-start;
- gap: 12px;
-
- .el-form-item {
- margin-bottom: 0;
- min-width: 150px;
-
- &:last-child {
- margin-left: auto;
- }
- }
- }
- }
-
- .order-list {
- overflow-y: auto;
- }
-
- .order-item {
- padding: 16px;
- margin-bottom: 8px;
- border-radius: 8px;
- cursor: pointer;
- transition: all 0.2s;
- border: 1px solid #e4e7ed;
-
- &:hover {
- background-color: #f5f7fa;
- }
-
- &.active {
- background-color: #ecf5ff;
- border-color: #409eff;
- }
-
- .order-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 8px;
-
- .order-no {
- font-weight: bold;
- font-size: 14px;
- }
-
- .status-badge {
- min-width: 60px;
- }
- }
-
- .order-body {
- font-size: 12px;
- color: #606266;
-
- .order-tags {
- display: flex;
- gap: 6px;
- margin-bottom: 6px;
- }
-
- .order-desc {
- margin-top: 4px;
- color: #303133;
- font-size: 13px;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
-
- .device-code, .create-time {
- margin-top: 4px;
- }
- }
- }
-
- .pagination-wrapper {
- padding: 16px 0;
- border-top: 1px solid #e4e7ed;
- margin-top: 8px;
-
- .pagination {
- display: flex;
- justify-content: flex-end;
- }
- }
-
- .box-card {
- height: 100%;
- display: flex;
- flex-direction: column;
-
- :deep(.el-card__body) {
- flex: 1;
- display: flex;
- flex-direction: column;
- overflow: hidden;
- }
- }
-
- .detail-tabs {
- margin-top: 16px;
- }
-
- .timeline-scroll {
- max-height: calc(100vh - 340px);
- overflow-y: auto;
- padding-right: 8px;
- }
-
- .image-section {
- margin-top: 24px;
- padding-top: 24px;
- border-top: 1px solid #e4e7ed;
- .image-group {
- margin-bottom: 16px;
- }
- .image-group-title {
- margin-bottom: 8px;
- color: #606266;
- font-weight: 600;
- }
-
- .image-list {
- display: flex;
- flex-wrap: wrap;
- gap: 12px;
- }
- }
-
- .action-section {
- margin-top: 24px;
- padding-top: 24px;
- border-top: 1px solid #e4e7ed;
-
- .section-title {
- font-size: 16px;
- font-weight: bold;
- margin-bottom: 16px;
- }
-
- .action-buttons {
- display: flex;
- gap: 12px;
- flex-wrap: wrap;
-
- .el-button {
- min-width: 100px;
- }
- }
- }
-
- .timeline-content {
- .timestamp {
- font-size: 12px;
- color: #909399;
- margin-bottom: 6px;
- }
-
- .operator-info {
- font-size: 14px;
- margin-bottom: 8px;
-
- .operator-name {
- font-weight: bold;
- color: #303133;
- }
-
- .oper-type {
- color: #909399;
- }
- }
-
- .oper-desc {
- font-size: 14px;
- color: #606266;
- }
- }
- }
- </style>
|