|
@@ -1,858 +0,0 @@
|
|
|
-<!--工单处置管理-->
|
|
|
|
|
-<template>
|
|
|
|
|
- <div class="min-h-screen bg-[#f0f2f6] p-5">
|
|
|
|
|
- <div class="mb-5 flex flex-wrap items-center justify-between gap-4 rounded-[20px] bg-white px-6 py-4">
|
|
|
|
|
- <div class="flex items-center gap-[14px] text-[20px] font-600">
|
|
|
|
|
- <el-icon><Tickets /></el-icon>
|
|
|
|
|
- <span>工单任务管理中心</span>
|
|
|
|
|
- <el-tag type="primary" effect="dark">待办工单 {{ pendingCount }}</el-tag>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="flex flex-wrap gap-3">
|
|
|
|
|
- <el-input
|
|
|
|
|
- v-model="searchKeyword"
|
|
|
|
|
- placeholder="搜索工单编号/案件编号"
|
|
|
|
|
- clearable
|
|
|
|
|
- :prefix-icon="Search"
|
|
|
|
|
- class="w-[220px]"
|
|
|
|
|
- @keyup.enter="loadOrders"
|
|
|
|
|
- />
|
|
|
|
|
- <el-button type="primary" @click="loadOrders">刷新</el-button>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <div class="mb-5 flex flex-wrap gap-6">
|
|
|
|
|
- <div class="min-w-[120px] cursor-pointer rounded-2xl bg-white px-4 py-3 text-center" :class="{ 'bg-[#ecf5ff]': statusFilter === '' }" @click="filterStatus('')">
|
|
|
|
|
- <div class="text-[26px] font-bold">{{ totalOrders }}</div>
|
|
|
|
|
- <div>全部工单</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="min-w-[120px] cursor-pointer rounded-2xl bg-white px-4 py-3 text-center" :class="{ 'bg-[#ecf5ff]': statusFilter === 'pending' }" @click="filterStatus('pending')">
|
|
|
|
|
- <div class="text-[26px] font-bold text-[#e6a23c]">{{ pendingCount }}</div>
|
|
|
|
|
- <div>待处理</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="min-w-[120px] cursor-pointer rounded-2xl bg-white px-4 py-3 text-center" :class="{ 'bg-[#ecf5ff]': statusFilter === 'processing' }" @click="filterStatus('processing')">
|
|
|
|
|
- <div class="text-[26px] font-bold">{{ processingCount }}</div>
|
|
|
|
|
- <div>处理中</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="min-w-[120px] cursor-pointer rounded-2xl bg-white px-4 py-3 text-center" :class="{ 'bg-[#ecf5ff]': statusFilter === 'verify' }" @click="filterStatus('verify')">
|
|
|
|
|
- <div class="text-[26px] font-bold">{{ verifyCount }}</div>
|
|
|
|
|
- <div>待验收</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="min-w-[120px] cursor-pointer rounded-2xl bg-white px-4 py-3 text-center" :class="{ 'bg-[#ecf5ff]': statusFilter === 'completed' }" @click="filterStatus('completed')">
|
|
|
|
|
- <div class="text-[26px] font-bold">{{ completedCount }}</div>
|
|
|
|
|
- <div>已完成</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <div class="flex gap-5">
|
|
|
|
|
- <div class="w-[380px] flex flex-col overflow-hidden rounded-[20px] bg-white" v-loading="loading">
|
|
|
|
|
- <div class="flex justify-between border-b border-[#e9ecef] px-4 py-4 font-600">
|
|
|
|
|
- <span>工单列表</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="flex-1 overflow-y-auto p-3">
|
|
|
|
|
- <div
|
|
|
|
|
- v-for="order in paginatedOrders"
|
|
|
|
|
- :key="order.orderId"
|
|
|
|
|
- class="mb-2 cursor-pointer rounded-[14px] border border-transparent bg-[#fafbfc] p-3"
|
|
|
|
|
- :class="{ 'border-[#409eff] bg-[#ecf5ff]': selectedOrder?.orderId === order.orderId }"
|
|
|
|
|
- @click="selectOrder(order)"
|
|
|
|
|
- >
|
|
|
|
|
- <div class="mb-1.5 flex justify-between">
|
|
|
|
|
- <span class="font-600">{{ order.orderNo }}</span>
|
|
|
|
|
- <el-tag :type="order.orderStatusTag" size="small">{{ order.orderStatusText }}</el-tag>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="text-[12px] text-[#666]">关联案件: {{ order.caseNo || '—' }}</div>
|
|
|
|
|
- <div class="my-1 text-[12px] text-[#666]">设备编号: {{ order.deviceCode || '—' }}</div>
|
|
|
|
|
- <div class="text-[12px] text-[#666]">创建: {{ order.createTimeText }}</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <el-empty v-if="!paginatedOrders.length" description="暂无工单数据" :image-size="80" />
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="border-t border-[#eee] p-3 text-center">
|
|
|
|
|
- <el-pagination small layout="prev, pager, next" :total="filteredOrders.length" :page-size="pageSize" v-model:current-page="currentPage" />
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <div class="flex-1 overflow-y-auto rounded-[20px] bg-white p-5" v-if="selectedOrder" v-loading="detailLoading">
|
|
|
|
|
- <el-tabs v-model="activeTab">
|
|
|
|
|
- <el-tab-pane label="工单信息" name="info">
|
|
|
|
|
- <div class="mb-3 mt-4 flex items-center gap-2 text-[16px] font-600">
|
|
|
|
|
- <span><el-icon><Document /></el-icon> 工单基本信息</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- <el-descriptions :column="2" border>
|
|
|
|
|
- <el-descriptions-item label="工单编号">{{ selectedOrder.orderNo }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="工单状态">
|
|
|
|
|
- <el-tag :type="selectedOrder.orderStatusTag">{{ selectedOrder.orderStatusText }}</el-tag>
|
|
|
|
|
- </el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="关联案件编号">{{ selectedOrder.caseNo || '—' }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="设备编号">{{ selectedOrder.deviceCode || '—' }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="优先级">
|
|
|
|
|
- <el-tag :type="selectedOrder.orderLevelTag">{{ selectedOrder.orderLevelText }}</el-tag>
|
|
|
|
|
- </el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="问题描述" :span="2">{{ selectedOrder.orderDesc || '—' }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="设备类别">{{ selectedOrder.equipmentTypeName || '—' }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="派单时间">{{ selectedOrder.dispatchTimeText }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="创建时间">{{ selectedOrder.createTimeText }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="计划完成时间">{{ selectedOrder.planFinishTimeText }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="延期原因">{{ selectedOrder.delayedReason || '—' }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="延期截止时间">{{ selectedOrder.delayedDeadlineText }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="验收时间">{{ selectedOrder.verifyTimeText }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="实际完成时间">{{ selectedOrder.realFinishTimeText }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="运维状态">{{ selectedOrder.currentStatusText }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="在线状态">{{ selectedOrder.onlineStatusText }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="报警状态">{{ selectedOrder.alarmStatusText }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="处置结果" :span="2">{{ selectedOrder.repairResult || '—' }}</el-descriptions-item>
|
|
|
|
|
- </el-descriptions>
|
|
|
|
|
-
|
|
|
|
|
- <RepairImagePanel :before-images="selectedOrder.maintenanceBeforeImg" :after-images="selectedOrder.maintenanceAfterImg" />
|
|
|
|
|
-
|
|
|
|
|
- <div class="mt-5 border-t border-[#e9ecef] pt-3">
|
|
|
|
|
- <div class="mb-3 mt-4 flex items-center gap-2 text-[16px] font-600">
|
|
|
|
|
- <span><el-icon><Timer /></el-icon> 工单操作</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="mb-3 flex items-center gap-3" v-if="canDispatch(selectedOrder)">
|
|
|
|
|
- <el-input v-model="receiveUserDisplay" readonly placeholder="请选择接单人" class="w-[260px]" />
|
|
|
|
|
- <el-button @click="openDispatchDialog">选择人员</el-button>
|
|
|
|
|
- <el-button type="primary" @click="dispatchOrder">下派任务</el-button>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="flex flex-wrap gap-3">
|
|
|
|
|
- <el-button v-if="canAcceptOrder(selectedOrder)" type="warning" @click="acceptOrder">接单</el-button>
|
|
|
|
|
- <el-button v-if="canStartRepair(selectedOrder)" type="primary" @click="startRepair">开始维修</el-button>
|
|
|
|
|
- <el-button v-if="canSubmitVerify(selectedOrder)" type="warning" @click="submitVerify">提交验收</el-button>
|
|
|
|
|
- <el-button v-if="canApprove(selectedOrder)" type="success" @click="approveOrder(true)">验收通过</el-button>
|
|
|
|
|
- <el-button v-if="canApprove(selectedOrder)" type="danger" @click="approveOrder(false)">驳回工单</el-button>
|
|
|
|
|
- <el-button v-if="canApproveDelay(selectedOrder)" type="success" @click="approveDelay">延期审核通过</el-button>
|
|
|
|
|
- <el-button v-if="canRejectDelay(selectedOrder)" type="danger" @click="rejectDelay">驳回延期</el-button>
|
|
|
|
|
- <el-button v-if="canDelay(selectedOrder)" type="info" plain @click="openDelayDialog">申请延期</el-button>
|
|
|
|
|
- </div>
|
|
|
|
|
- <el-empty v-if="!hasOperationButtons(selectedOrder)" description="当前状态暂无可操作项" :image-size="80" />
|
|
|
|
|
- </div>
|
|
|
|
|
- </el-tab-pane>
|
|
|
|
|
-
|
|
|
|
|
- <el-tab-pane label="处理历史" name="history">
|
|
|
|
|
- <el-timeline v-if="selectedOrder.workOrderLogList?.length">
|
|
|
|
|
- <el-timeline-item
|
|
|
|
|
- v-for="log in selectedOrder.workOrderLogList"
|
|
|
|
|
- :key="log.logId"
|
|
|
|
|
- :timestamp="formatDateTime(log.operTime)"
|
|
|
|
|
- :type="getLogType(log.operType)"
|
|
|
|
|
- >
|
|
|
|
|
- <div>
|
|
|
|
|
- <span>{{ log.operDesc }}</span>
|
|
|
|
|
- <span v-if="log.operUserName" class="ml-3 text-[12px] text-[#909399]">操作人:{{ log.operUserName }}</span>
|
|
|
|
|
- <!-- 显示处理图片(如果有的话) -->
|
|
|
|
|
- <div v-if="log.maintenanceImages && log.maintenanceImages.length" class="mt-2 flex gap-2">
|
|
|
|
|
- <div
|
|
|
|
|
- v-for="(img, idx) in log.maintenanceImages"
|
|
|
|
|
- :key="idx"
|
|
|
|
|
- class="cursor-pointer rounded border p-1 hover:shadow-md"
|
|
|
|
|
- @click="() => previewImage(img.url)"
|
|
|
|
|
- >
|
|
|
|
|
- <img :src="img.url" alt="处理图片" class="h-[60px] w-[80px] object-cover" />
|
|
|
|
|
- <div class="text-center text-xs text-[#909399]">
|
|
|
|
|
- {{ img.type === 'before' ? '⚠️ 处理前' : img.type === 'after' ? '✅ 处理后' : '🖼️ 图片' }}
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </el-timeline-item>
|
|
|
|
|
- </el-timeline>
|
|
|
|
|
- <el-empty v-else description="暂无工单日志" :image-size="80" />
|
|
|
|
|
- </el-tab-pane>
|
|
|
|
|
- </el-tabs>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <div v-else class="flex flex-1 items-center justify-center rounded-[20px] bg-white">
|
|
|
|
|
- <el-empty description="请从左侧选择工单进行处理" :image-size="140" />
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <el-dialog v-model="beforeImageDialogVisible" title="开始维修" width="600px">
|
|
|
|
|
- <ImageUpload v-model="maintenanceBeforeImages" :limit="9" :isShowTip="true" :upload-url="imageUploadUrl" />
|
|
|
|
|
- <template #footer>
|
|
|
|
|
- <el-button @click="beforeImageDialogVisible = false">取消</el-button>
|
|
|
|
|
- <el-button type="primary" @click="submitStartRepair">确定</el-button>
|
|
|
|
|
- </template>
|
|
|
|
|
- </el-dialog>
|
|
|
|
|
-
|
|
|
|
|
- <el-dialog v-model="afterImageDialogVisible" title="提交验收" width="600px">
|
|
|
|
|
- <ImageUpload v-model="maintenanceAfterImages" :limit="9" :isShowTip="true" :upload-url="imageUploadUrl" />
|
|
|
|
|
- <template #footer>
|
|
|
|
|
- <el-button @click="afterImageDialogVisible = false">取消</el-button>
|
|
|
|
|
- <el-button type="primary" @click="submitVerify">提交</el-button>
|
|
|
|
|
- </template>
|
|
|
|
|
- </el-dialog>
|
|
|
|
|
-
|
|
|
|
|
- <el-dialog v-model="delayDialogVisible" title="申请延期" width="450px">
|
|
|
|
|
- <el-form label-width="88px">
|
|
|
|
|
- <el-form-item label="截止时间">
|
|
|
|
|
- <el-date-picker
|
|
|
|
|
- v-model="delayDeadline"
|
|
|
|
|
- type="datetime"
|
|
|
|
|
- value-format="YYYY-MM-DD HH:mm:ss"
|
|
|
|
|
- placeholder="选择截止时间"
|
|
|
|
|
- class="w-full"
|
|
|
|
|
- />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="申请理由">
|
|
|
|
|
- <el-input type="textarea" v-model="delayReason" placeholder="请详细说明延期理由" rows="3" />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- </el-form>
|
|
|
|
|
- <template #footer>
|
|
|
|
|
- <el-button @click="delayDialogVisible = false">取消</el-button>
|
|
|
|
|
- <el-button type="primary" @click="submitDelay">提交申请</el-button>
|
|
|
|
|
- </template>
|
|
|
|
|
- </el-dialog>
|
|
|
|
|
-
|
|
|
|
|
- <el-dialog v-model="dispatchDialogVisible" title="下派任务" width="860px">
|
|
|
|
|
- <div class="flex gap-4">
|
|
|
|
|
- <div class="w-[260px] overflow-auto rounded-lg border border-[#ebeef5] p-3">
|
|
|
|
|
- <div class="mb-3 font-600">部门树</div>
|
|
|
|
|
- <el-input
|
|
|
|
|
- v-model="deptKeyword"
|
|
|
|
|
- placeholder="筛选部门"
|
|
|
|
|
- clearable
|
|
|
|
|
- size="small"
|
|
|
|
|
- class="mb-3"
|
|
|
|
|
- />
|
|
|
|
|
- <el-tree
|
|
|
|
|
- ref="deptTreeRef"
|
|
|
|
|
- :data="deptOptions"
|
|
|
|
|
- node-key="id"
|
|
|
|
|
- :props="{ label: 'label', children: 'children' }"
|
|
|
|
|
- default-expand-all
|
|
|
|
|
- highlight-current
|
|
|
|
|
- :filter-node-method="filterDeptNode"
|
|
|
|
|
- @node-click="handleDeptClick"
|
|
|
|
|
- />
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="flex-1 rounded-lg border border-[#ebeef5] p-3">
|
|
|
|
|
- <div class="mb-3 font-600">用户列表</div>
|
|
|
|
|
- <el-table
|
|
|
|
|
- :data="dispatchUsers"
|
|
|
|
|
- height="360"
|
|
|
|
|
- border
|
|
|
|
|
- highlight-current-row
|
|
|
|
|
- v-loading="dispatchUserLoading"
|
|
|
|
|
- @current-change="handleUserCurrentChange"
|
|
|
|
|
- @row-click="handleUserCurrentChange"
|
|
|
|
|
- >
|
|
|
|
|
- <el-table-column prop="userName" label="用户名" min-width="120" />
|
|
|
|
|
- <el-table-column prop="nickName" label="昵称" min-width="120" />
|
|
|
|
|
- <el-table-column prop="dept.deptName" label="部门" min-width="140" show-overflow-tooltip />
|
|
|
|
|
- </el-table>
|
|
|
|
|
- <div class="mt-3 text-[#606266]">
|
|
|
|
|
- 已选人员:{{ dispatchSelectedUser ? `${dispatchSelectedUser.userName}${dispatchSelectedUser.nickName ? ` / ${dispatchSelectedUser.nickName}` : ''}` : '未选择' }}
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <template #footer>
|
|
|
|
|
- <el-button @click="dispatchDialogVisible = false">取消</el-button>
|
|
|
|
|
- <el-button type="primary" @click="confirmDispatchUser">确认选择</el-button>
|
|
|
|
|
- </template>
|
|
|
|
|
- </el-dialog>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 图片预览弹窗 -->
|
|
|
|
|
- <el-drawer v-model="imagePreviewVisible" title="图片预览" size="600px" append-to-body class="detail-drawer" direction="rtl">
|
|
|
|
|
- <img :src="currentImagePreviewUrl" alt="工单图片" class="max-w-full" style="border-radius: 4px;" />
|
|
|
|
|
- </el-drawer>
|
|
|
|
|
- </div>
|
|
|
|
|
-</template>
|
|
|
|
|
-
|
|
|
|
|
-<script setup>
|
|
|
|
|
-import{computed, nextTick, onMounted, ref, watch} from 'vue'
|
|
|
|
|
-import {Document, Search, Tickets, Timer} from '@element-plus/icons-vue'
|
|
|
|
|
-import {ElMessage} from 'element-plus'
|
|
|
|
|
-import ImageUpload from '@/components/ImageUpload/index.vue'
|
|
|
|
|
-import RepairImagePanel from '@/components/RepairImagePanel/index.vue'
|
|
|
|
|
-import {getManholeWorkOrderDetail, getManholeWorkOrderPage, operManholeWorkOrder} from '@/api/pipeNetwork/basic'
|
|
|
|
|
-import {deptTreeSelect, listUser} from '@/api/system/user'
|
|
|
|
|
-
|
|
|
|
|
-const DEFAULT_OPER_USER_ID = 1
|
|
|
|
|
-
|
|
|
|
|
-const orders = ref([])
|
|
|
|
|
-const loading = ref(false)
|
|
|
|
|
-const detailLoading = ref(false)
|
|
|
|
|
-const searchKeyword = ref('')
|
|
|
|
|
-const statusFilter = ref('')
|
|
|
|
|
-const currentPage = ref(1)
|
|
|
|
|
-const pageSize = 5
|
|
|
|
|
-const selectedOrder = ref(null)
|
|
|
|
|
-const activeTab = ref('info')
|
|
|
|
|
-const receiveUserId = ref(null)
|
|
|
|
|
-const receiveUserDisplay = ref('')
|
|
|
|
|
-const delayDialogVisible = ref(false)
|
|
|
|
|
-const delayDeadline = ref('')
|
|
|
|
|
-const delayReason = ref('')
|
|
|
|
|
-const beforeImageDialogVisible = ref(false)
|
|
|
|
|
-const afterImageDialogVisible = ref(false)
|
|
|
|
|
-const maintenanceBeforeImages = ref('')
|
|
|
|
|
-const maintenanceAfterImages = ref('')
|
|
|
|
|
-const imageUploadUrl = import.meta.env.VITE_APP_BASE_API + '/minio/upload'
|
|
|
|
|
-const dispatchDialogVisible = ref(false)
|
|
|
|
|
-const dispatchUserLoading = ref(false)
|
|
|
|
|
-const deptOptions = ref([])
|
|
|
|
|
-const deptKeyword = ref('')
|
|
|
|
|
-const selectedDeptId = ref(undefined)
|
|
|
|
|
-const dispatchUsers = ref([])
|
|
|
|
|
-const dispatchSelectedUser = ref(null)
|
|
|
|
|
-const deptTreeRef = ref(null)
|
|
|
|
|
-
|
|
|
|
|
-// 图片预览相关
|
|
|
|
|
-const imagePreviewVisible = ref(false)
|
|
|
|
|
-const currentImagePreviewUrl = ref('')
|
|
|
|
|
-const baseUrl = import.meta.env.VITE_APP_BASE_API
|
|
|
|
|
-
|
|
|
|
|
-function formatDateTime(value) {
|
|
|
|
|
- if (!value) return '—'
|
|
|
|
|
- return String(value).replace('T', ' ')
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const maintenanceBeforeImageList = computed(() => splitImageList(selectedOrder.value?.maintenanceBeforeImg))
|
|
|
|
|
-const maintenanceAfterImageList = computed(() => splitImageList(selectedOrder.value?.maintenanceAfterImg))
|
|
|
|
|
-
|
|
|
|
|
-function previewImage(url) {
|
|
|
|
|
- currentImagePreviewUrl.value = url
|
|
|
|
|
- imagePreviewVisible.value = true
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function getOrderLevelMeta(level) {
|
|
|
|
|
- const map = {
|
|
|
|
|
- 0: { text: '紧急', tag: 'danger' },
|
|
|
|
|
- 1: { text: '一般', tag: 'warning' },
|
|
|
|
|
- 2: { text: '低', tag: 'info' }
|
|
|
|
|
- }
|
|
|
|
|
- return map[Number(level)] || { text: '—', tag: 'info' }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-function getOrderStatusMeta(status) {
|
|
|
|
|
- const map = {
|
|
|
|
|
- 0: { key: 'pending', text: '待派单', tag: 'danger' },
|
|
|
|
|
- 1: { key: 'pending', text: '待接单', tag: 'danger' },
|
|
|
|
|
- 2: { key: 'processing', text: '已接单', tag: 'warning' },
|
|
|
|
|
- 3: { key: 'processing', text: '处理中', tag: 'warning' },
|
|
|
|
|
- 4: { key: 'verify', text: '待验收', tag: 'primary' },
|
|
|
|
|
- 5: { key: 'completed', text: '已结案', tag: 'success' },
|
|
|
|
|
- 6: { key: 'processing', text: '延期中', tag: 'warning' },
|
|
|
|
|
- 7: { key: 'completed', text: '已取消', tag: 'info' }
|
|
|
|
|
- }
|
|
|
|
|
- return map[Number(status)] || { key: '', text: '—', tag: 'info' }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function getCurrentStatusText(status) {
|
|
|
|
|
- const map = {
|
|
|
|
|
- 1: '在用',
|
|
|
|
|
- 2: '闲置',
|
|
|
|
|
- 3: '维修',
|
|
|
|
|
- 4: '报废',
|
|
|
|
|
- 5: '待入库'
|
|
|
|
|
- }
|
|
|
|
|
- return map[Number(status)] || '—'
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function getOnlineStatusText(status) {
|
|
|
|
|
- const map = {
|
|
|
|
|
- 0: '离线',
|
|
|
|
|
- 1: '在线'
|
|
|
|
|
- }
|
|
|
|
|
- return map[Number(status)] || '—'
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function getAlarmStatusText(status) {
|
|
|
|
|
- const map = {
|
|
|
|
|
- 0: '正常',
|
|
|
|
|
- 1: '报警'
|
|
|
|
|
- }
|
|
|
|
|
- return map[Number(status)] || '—'
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function normalizeOrder(item) {
|
|
|
|
|
- const levelMeta = getOrderLevelMeta(item.orderLevel)
|
|
|
|
|
- const statusMeta = getOrderStatusMeta(item.orderStatus)
|
|
|
|
|
- return {
|
|
|
|
|
- ...item,
|
|
|
|
|
- orderLevelText: levelMeta.text,
|
|
|
|
|
- orderLevelTag: levelMeta.tag,
|
|
|
|
|
- orderStatusKey: statusMeta.key,
|
|
|
|
|
- orderStatusText: statusMeta.text,
|
|
|
|
|
- orderStatusTag: statusMeta.tag,
|
|
|
|
|
- createTimeText: formatDateTime(item.createTime),
|
|
|
|
|
- dispatchTimeText: formatDateTime(item.dispatchTime),
|
|
|
|
|
- planFinishTimeText: formatDateTime(item.planFinishTime),
|
|
|
|
|
- delayedDeadlineText: formatDateTime(item.delayedDeadline),
|
|
|
|
|
- realFinishTimeText: formatDateTime(item.realFinishTime),
|
|
|
|
|
- verifyTimeText: formatDateTime(item.verifyTime),
|
|
|
|
|
- updateTimeText: formatDateTime(item.updateTime),
|
|
|
|
|
- currentStatusText: getCurrentStatusText(item.equipmentStatus?.currentStatus),
|
|
|
|
|
- onlineStatusText: getOnlineStatusText(item.equipmentStatus?.onlineStatus),
|
|
|
|
|
- alarmStatusText: getAlarmStatusText(item.equipmentStatus?.alarmStatus),
|
|
|
|
|
- statusUpdateTimeText: formatDateTime(item.equipmentStatus?.statusUpdateTime)
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const filteredOrders = computed(() => {
|
|
|
|
|
- if (!statusFilter.value) return orders.value
|
|
|
|
|
- return orders.value.filter(item => item.orderStatusKey === statusFilter.value)
|
|
|
|
|
-})
|
|
|
|
|
-
|
|
|
|
|
-const paginatedOrders = computed(() => {
|
|
|
|
|
- const start = (currentPage.value - 1) * pageSize
|
|
|
|
|
- return filteredOrders.value.slice(start, start + pageSize)
|
|
|
|
|
-})
|
|
|
|
|
-
|
|
|
|
|
-const totalOrders = computed(() => orders.value.length)
|
|
|
|
|
-const pendingCount = computed(() => orders.value.filter(item => item.orderStatusKey === 'pending').length)
|
|
|
|
|
-const processingCount = computed(() => orders.value.filter(item => item.orderStatusKey === 'processing').length)
|
|
|
|
|
-const verifyCount = computed(() => orders.value.filter(item => item.orderStatusKey === 'verify').length)
|
|
|
|
|
-const completedCount = computed(() => orders.value.filter(item => item.orderStatusKey === 'completed').length)
|
|
|
|
|
-
|
|
|
|
|
-async function loadOrders() {
|
|
|
|
|
- loading.value = true
|
|
|
|
|
- try {
|
|
|
|
|
- const keyword = searchKeyword.value.trim()
|
|
|
|
|
- const res = await getManholeWorkOrderPage({
|
|
|
|
|
- pageNum: 1,
|
|
|
|
|
- pageSize: 200,
|
|
|
|
|
- orderNo: keyword || undefined,
|
|
|
|
|
- caseNo: keyword || undefined
|
|
|
|
|
- })
|
|
|
|
|
- const records = Array.isArray(res?.data?.records) ? res.data.records : []
|
|
|
|
|
- orders.value = records.map(normalizeOrder)
|
|
|
|
|
- currentPage.value = 1
|
|
|
|
|
- const first = filteredOrders.value[0]
|
|
|
|
|
- if (!first) {
|
|
|
|
|
- selectedOrder.value = null
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- if (selectedOrder.value) {
|
|
|
|
|
- const matched = orders.value.find(item => item.orderId === selectedOrder.value.orderId)
|
|
|
|
|
- if (matched) {
|
|
|
|
|
- await selectOrder(matched)
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- await selectOrder(first)
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- orders.value = []
|
|
|
|
|
- selectedOrder.value = null
|
|
|
|
|
- ElMessage.error('工单列表加载失败')
|
|
|
|
|
- } finally {
|
|
|
|
|
- loading.value = false
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-async function selectOrder(order) {
|
|
|
|
|
- detailLoading.value = true
|
|
|
|
|
- receiveUserId.value = order.receiveUser || null
|
|
|
|
|
- receiveUserDisplay.value = order.receiveUser ? '已选择接单人' : ''
|
|
|
|
|
- try {
|
|
|
|
|
- const res = await getManholeWorkOrderDetail(order.orderId)
|
|
|
|
|
- selectedOrder.value = normalizeOrder(res?.data || order)
|
|
|
|
|
- receiveUserId.value = selectedOrder.value.receiveUser || null
|
|
|
|
|
- receiveUserDisplay.value = selectedOrder.value.receiveUser ? '已选择接单人' : ''
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- selectedOrder.value = order
|
|
|
|
|
- ElMessage.error('工单详情加载失败')
|
|
|
|
|
- } finally {
|
|
|
|
|
- detailLoading.value = false
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function filterStatus(status) {
|
|
|
|
|
- statusFilter.value = status
|
|
|
|
|
- currentPage.value = 1
|
|
|
|
|
- const first = filteredOrders.value[0]
|
|
|
|
|
- if (first) {
|
|
|
|
|
- selectOrder(first)
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function canDispatch(order) {
|
|
|
|
|
- return Number(order.orderStatus) === 0
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function canAcceptOrder(order) {
|
|
|
|
|
- return [1, 6].includes(Number(order.orderStatus))
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function canStartRepair(order) {
|
|
|
|
|
- return Number(order.orderStatus) === 2
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function canSubmitVerify(order) {
|
|
|
|
|
- return [3, 6].includes(Number(order.orderStatus))
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function canApprove(order) {
|
|
|
|
|
- return Number(order.orderStatus) === 4
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function canApproveDelay(order) {
|
|
|
|
|
- return Number(order.delayApplyStatus) === 1
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function canRejectDelay(order) {
|
|
|
|
|
- return Number(order.delayApplyStatus) === 1
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function canDelay(order) {
|
|
|
|
|
- return Number(order.orderStatus) === 3 && Number(order.delayApplyStatus ?? 0) === 0
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function hasOperationButtons(order) {
|
|
|
|
|
- if (!order) return false
|
|
|
|
|
- return (
|
|
|
|
|
- canDispatch(order) ||
|
|
|
|
|
- canAcceptOrder(order) ||
|
|
|
|
|
- canStartRepair(order) ||
|
|
|
|
|
- canSubmitVerify(order) ||
|
|
|
|
|
- canApprove(order) ||
|
|
|
|
|
- canApproveDelay(order) ||
|
|
|
|
|
- canRejectDelay(order) ||
|
|
|
|
|
- canDelay(order)
|
|
|
|
|
- )
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-async function operateCurrent(operType, operDesc, extra = {}) {
|
|
|
|
|
- if (!selectedOrder.value?.orderId) return
|
|
|
|
|
- await operManholeWorkOrder({
|
|
|
|
|
- orderId: selectedOrder.value.orderId,
|
|
|
|
|
- operType,
|
|
|
|
|
- operDesc,
|
|
|
|
|
- operUserId: DEFAULT_OPER_USER_ID,
|
|
|
|
|
- ...extra
|
|
|
|
|
- })
|
|
|
|
|
- await loadOrders()
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-async function dispatchOrder() {
|
|
|
|
|
- if (!receiveUserId.value) {
|
|
|
|
|
- ElMessage.warning('请先选择接单人')
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- try {
|
|
|
|
|
- const receiveUserName = dispatchSelectedUser.value?.userName || '指定人员'
|
|
|
|
|
- await operateCurrent(1, `下派工单至${receiveUserName}`, { receiveUser: Number(receiveUserId.value) })
|
|
|
|
|
- ElMessage.success('工单下派成功')
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- ElMessage.error(error?.message || '工单下派失败')
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function filterDeptNode(value, data) {
|
|
|
|
|
- if (!value) return true
|
|
|
|
|
- return data.label?.includes(value)
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-watch(deptKeyword, value => {
|
|
|
|
|
- nextTick(() => {
|
|
|
|
|
- deptTreeRef.value?.filter(value)
|
|
|
|
|
- })
|
|
|
|
|
-})
|
|
|
|
|
-
|
|
|
|
|
-async function loadDeptTree() {
|
|
|
|
|
- const res = await deptTreeSelect()
|
|
|
|
|
- deptOptions.value = Array.isArray(res?.data) ? res.data : []
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-async function loadDispatchUsers(deptId) {
|
|
|
|
|
- dispatchUserLoading.value = true
|
|
|
|
|
- try {
|
|
|
|
|
- const res = await listUser({
|
|
|
|
|
- pageNum: 1,
|
|
|
|
|
- pageSize: 100,
|
|
|
|
|
- deptId,
|
|
|
|
|
- status: '0'
|
|
|
|
|
- })
|
|
|
|
|
- dispatchUsers.value = Array.isArray(res?.rows) ? res.rows : []
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- dispatchUsers.value = []
|
|
|
|
|
- ElMessage.error('用户列表加载失败')
|
|
|
|
|
- } finally {
|
|
|
|
|
- dispatchUserLoading.value = false
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-async function openDispatchDialog() {
|
|
|
|
|
- dispatchDialogVisible.value = true
|
|
|
|
|
- dispatchSelectedUser.value = null
|
|
|
|
|
- dispatchUsers.value = []
|
|
|
|
|
- deptKeyword.value = ''
|
|
|
|
|
- if (!deptOptions.value.length) {
|
|
|
|
|
- await loadDeptTree()
|
|
|
|
|
- }
|
|
|
|
|
- const firstDept = findFirstDept(deptOptions.value)
|
|
|
|
|
- if (firstDept?.id) {
|
|
|
|
|
- selectedDeptId.value = firstDept.id
|
|
|
|
|
- await nextTick()
|
|
|
|
|
- deptTreeRef.value?.setCurrentKey(firstDept.id)
|
|
|
|
|
- await loadDispatchUsers(firstDept.id)
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function findFirstDept(nodes = []) {
|
|
|
|
|
- for (const node of nodes) {
|
|
|
|
|
- if (node?.id) return node
|
|
|
|
|
- if (node?.children?.length) {
|
|
|
|
|
- const child = findFirstDept(node.children)
|
|
|
|
|
- if (child) return child
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- return null
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function handleDeptClick(data) {
|
|
|
|
|
- selectedDeptId.value = data.id
|
|
|
|
|
- loadDispatchUsers(data.id)
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function handleUserCurrentChange(row) {
|
|
|
|
|
- dispatchSelectedUser.value = row || null
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function confirmDispatchUser() {
|
|
|
|
|
- if (!dispatchSelectedUser.value?.userId) {
|
|
|
|
|
- ElMessage.warning('请选择用户')
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- receiveUserId.value = dispatchSelectedUser.value.userId
|
|
|
|
|
- receiveUserDisplay.value = `${dispatchSelectedUser.value.userName}${dispatchSelectedUser.value.nickName ? ` / ${dispatchSelectedUser.value.nickName}` : ''}`
|
|
|
|
|
- dispatchDialogVisible.value = false
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-async function acceptOrder() {
|
|
|
|
|
- try {
|
|
|
|
|
- await operateCurrent(2, '接单')
|
|
|
|
|
- ElMessage.success('已接单')
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- ElMessage.error(error?.message || '接单失败')
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-async function startRepair() {
|
|
|
|
|
- maintenanceBeforeImages.value = ''
|
|
|
|
|
- beforeImageDialogVisible.value = true
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function normalizeImageUrl(value) {
|
|
|
|
|
- const url = String(value || '').trim()
|
|
|
|
|
- if (!url || /^(https?:|blob:|data:)/.test(url)) return url
|
|
|
|
|
- if (baseUrl && (url === baseUrl || url.startsWith(`${baseUrl}/`))) return url
|
|
|
|
|
- return url.startsWith('/') ? baseUrl + url : `${baseUrl}/${url}`
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function splitImageList(value) {
|
|
|
|
|
- return value
|
|
|
|
|
- ? String(value).split(',').map(normalizeImageUrl).filter(Boolean)
|
|
|
|
|
- : []
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-async function submitStartRepair() {
|
|
|
|
|
- try {
|
|
|
|
|
- await operateCurrent(3, '开始维修', { maintenanceBeforeImg: maintenanceBeforeImages.value })
|
|
|
|
|
- beforeImageDialogVisible.value = false
|
|
|
|
|
- ElMessage.success('已开始维修')
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- ElMessage.error(error?.message || '开始维修失败')
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-async function submitVerify() {
|
|
|
|
|
- if (!afterImageDialogVisible.value) {
|
|
|
|
|
- maintenanceAfterImages.value = ''
|
|
|
|
|
- afterImageDialogVisible.value = true
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- try {
|
|
|
|
|
- await operateCurrent(4, '提交验收', { maintenanceAfterImg: maintenanceAfterImages.value })
|
|
|
|
|
- afterImageDialogVisible.value = false
|
|
|
|
|
- ElMessage.success('已提交验收')
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- ElMessage.error(error?.message || '提交验收失败')
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-async function approveOrder(isApproved) {
|
|
|
|
|
- try {
|
|
|
|
|
- await operateCurrent(isApproved ? 5 : 6, isApproved ? '验收通过' : '验收驳回')
|
|
|
|
|
- ElMessage.success(isApproved ? '验收已通过' : '工单已驳回')
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- ElMessage.error(error?.message || '工单操作失败')
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-async function approveDelay() {
|
|
|
|
|
- try {
|
|
|
|
|
- await operateCurrent(9, '延期审核通过')
|
|
|
|
|
- ElMessage.success('延期审核已通过')
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- ElMessage.error(error?.message || '延期审核通过失败')
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-async function rejectDelay() {
|
|
|
|
|
- try {
|
|
|
|
|
- await operateCurrent(10, '延期审核驳回')
|
|
|
|
|
- ElMessage.success('延期申请已驳回')
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- ElMessage.error(error?.message || '驳回延期失败')
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function openDelayDialog() {
|
|
|
|
|
- delayDeadline.value = ''
|
|
|
|
|
- delayReason.value = ''
|
|
|
|
|
- delayDialogVisible.value = true
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-async function submitDelay() {
|
|
|
|
|
- if (!delayDeadline.value) {
|
|
|
|
|
- ElMessage.warning('请选择截止时间')
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- if (!delayReason.value.trim()) {
|
|
|
|
|
- ElMessage.warning('请填写延期理由')
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- try {
|
|
|
|
|
- await operateCurrent(8, '申请延期', {
|
|
|
|
|
- delayedDeadline: delayDeadline.value,
|
|
|
|
|
- delayedReason: delayReason.value.trim()
|
|
|
|
|
- })
|
|
|
|
|
- delayDialogVisible.value = false
|
|
|
|
|
- ElMessage.success('延期申请已提交')
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- ElMessage.error(error?.message || '延期申请失败')
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function getLogType(operType) {
|
|
|
|
|
- const map = {
|
|
|
|
|
- 2: 'primary',
|
|
|
|
|
- 3: 'warning',
|
|
|
|
|
- 4: 'primary',
|
|
|
|
|
- 5: 'warning',
|
|
|
|
|
- 6: 'success',
|
|
|
|
|
- 7: 'danger',
|
|
|
|
|
- 8: 'warning',
|
|
|
|
|
- 9: 'warning'
|
|
|
|
|
- }
|
|
|
|
|
- return map[Number(operType)] || 'info'
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-onMounted(() => {
|
|
|
|
|
- loadOrders()
|
|
|
|
|
-})
|
|
|
|
|
-</script>
|
|
|
|
|
-
|
|
|
|
|
-<style scoped lang="scss">
|
|
|
|
|
-.repair-image-panel {
|
|
|
|
|
- margin-top: 20px;
|
|
|
|
|
- padding-top: 16px;
|
|
|
|
|
- border-top: 1px solid #e9ecef;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.repair-image-panel__header,
|
|
|
|
|
-.repair-image-column__header {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: space-between;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.repair-image-panel__header {
|
|
|
|
|
- margin-bottom: 12px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.repair-image-panel__title {
|
|
|
|
|
- display: inline-flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 8px;
|
|
|
|
|
- color: #303133;
|
|
|
|
|
- font-size: 16px;
|
|
|
|
|
- font-weight: 600;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.repair-image-panel__total {
|
|
|
|
|
- color: #909399;
|
|
|
|
|
- font-size: 13px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.repair-image-panel__body {
|
|
|
|
|
- display: grid;
|
|
|
|
|
- grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
|
|
- gap: 16px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.repair-image-column {
|
|
|
|
|
- min-width: 0;
|
|
|
|
|
- padding: 14px;
|
|
|
|
|
- border: 1px solid #ebeef5;
|
|
|
|
|
- border-radius: 8px;
|
|
|
|
|
- background: #fafbfc;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.repair-image-column__header {
|
|
|
|
|
- margin-bottom: 12px;
|
|
|
|
|
- color: #606266;
|
|
|
|
|
- font-size: 14px;
|
|
|
|
|
- font-weight: 600;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.repair-image-list {
|
|
|
|
|
- display: grid;
|
|
|
|
|
- grid-template-columns: repeat(auto-fill, minmax(116px, 1fr));
|
|
|
|
|
- gap: 10px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.repair-image-item {
|
|
|
|
|
- position: relative;
|
|
|
|
|
- aspect-ratio: 4 / 3;
|
|
|
|
|
- overflow: hidden;
|
|
|
|
|
- border: 1px solid #dcdfe6;
|
|
|
|
|
- border-radius: 6px;
|
|
|
|
|
- background: #f2f6fc;
|
|
|
|
|
- cursor: pointer;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.repair-image-item:hover img {
|
|
|
|
|
- transform: scale(1.04);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.repair-image-item img {
|
|
|
|
|
- display: block;
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- height: 100%;
|
|
|
|
|
- object-fit: cover;
|
|
|
|
|
- transition: transform 0.2s ease;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.repair-image-item__index {
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- right: 5px;
|
|
|
|
|
- bottom: 5px;
|
|
|
|
|
- min-width: 20px;
|
|
|
|
|
- padding: 2px 5px;
|
|
|
|
|
- border-radius: 10px;
|
|
|
|
|
- background: rgb(0 0 0 / 55%);
|
|
|
|
|
- color: #fff;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- line-height: 16px;
|
|
|
|
|
- text-align: center;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.repair-image-empty {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- min-height: 120px;
|
|
|
|
|
- border: 1px dashed #dcdfe6;
|
|
|
|
|
- border-radius: 6px;
|
|
|
|
|
- color: #a8abb2;
|
|
|
|
|
- font-size: 13px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-@media (max-width: 900px) {
|
|
|
|
|
- .repair-image-panel__body {
|
|
|
|
|
- grid-template-columns: 1fr;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-</style>
|
|
|