diff --git a/apps/web-antd/src/router/routes/modules/ops.ts b/apps/web-antd/src/router/routes/modules/ops.ts index a6739690e..40f5bb36d 100644 --- a/apps/web-antd/src/router/routes/modules/ops.ts +++ b/apps/web-antd/src/router/routes/modules/ops.ts @@ -63,6 +63,26 @@ const routes: RouteRecordRaw[] = [ component: () => import('#/views/ops/cleaning/work-order/dashboard/index.vue'), }, + // 工单中心详情(通用,支持所有工单类型) + { + path: 'work-order/detail/:id', + name: 'WorkOrderDetail', + meta: { + title: '工单详情', + activePath: '/ops/work-order', + }, + component: () => import('#/views/ops/work-order/detail/index.vue'), + }, + // 工单中心统计看板 + { + path: 'work-order/dashboard', + name: 'WorkOrderDashboard', + meta: { + title: '统计看板', + activePath: '/ops/work-order', + }, + component: () => import('#/views/ops/work-order/dashboard/index.vue'), + }, ], }, ]; diff --git a/apps/web-antd/src/views/ops/work-order/dashboard/index.vue b/apps/web-antd/src/views/ops/work-order/dashboard/index.vue new file mode 100644 index 000000000..5273c205b --- /dev/null +++ b/apps/web-antd/src/views/ops/work-order/dashboard/index.vue @@ -0,0 +1,1402 @@ + + + + + + + + + + + + + + + 待处理工单 + + + + + + + + + + + + + + 进行中工单 + + + + + + + + + + + + + + 今日完成 + + + + + + + + + + + + + + 累计完成 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web-antd/src/views/ops/work-order/data.ts b/apps/web-antd/src/views/ops/work-order/data.ts new file mode 100644 index 000000000..bf226979d --- /dev/null +++ b/apps/web-antd/src/views/ops/work-order/data.ts @@ -0,0 +1,362 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { OpsOrderCenterApi } from '#/api/ops/order-center'; + +/** 状态颜色映射 */ +export const STATUS_COLOR_MAP: Record = { + PENDING: '#8c8c8c', // 灰色 - 待分配 + QUEUED: '#faad14', // 黄色 - 排队中 + DISPATCHED: '#1677ff', // 蓝色 - 已推送 + CONFIRMED: '#13c2c2', // 青色 - 已确认 + ARRIVED: '#52c41a', // 绿色 - 已到岗 + PAUSED: '#fa8c16', // 橙色 - 已暂停 + RESUMED: '#52c41a', // 绿色 - 已恢复 + COMPLETED: '#389e0d', // 深绿 - 已完成 + CANCELLED: '#ff4d4f', // 红色 - 已取消 +}; + +/** 状态文本映射 */ +export const STATUS_TEXT_MAP: Record = { + PENDING: '待分配', + QUEUED: '排队中', + DISPATCHED: '已推送', + CONFIRMED: '已确认', + ARRIVED: '作业中', + PAUSED: '已暂停', + RESUMED: '已恢复', + COMPLETED: '已完成', + CANCELLED: '已取消', +}; + +/** 状态图标映射 */ +export const STATUS_ICON_MAP: Record = { + PENDING: 'solar:inbox-line-bold-duotone', // 待分配 + QUEUED: 'solar:clock-circle-bold-duotone', // 排队中 + DISPATCHED: 'solar:transfer-horizontal-bold-duotone', // 已推送 + CONFIRMED: 'solar:check-circle-bold-duotone', // 已确认 + ARRIVED: 'solar:play-circle-bold-duotone', // 作业中 + PAUSED: 'solar:pause-circle-bold-duotone', // 已暂停 + RESUMED: 'solar:play-circle-bold-duotone', // 已恢复 + COMPLETED: 'solar:check-read-bold-duotone', // 已完成 + CANCELLED: 'solar:close-circle-bold-duotone', // 已取消 +}; + +/** Tab 快捷筛选配置 */ +export const STATUS_TAB_OPTIONS = [ + { key: 'ALL', label: '全部', statuses: undefined }, + { key: 'PENDING', label: '待处理', statuses: ['PENDING'] }, + { + key: 'IN_PROGRESS', + label: '进行中', + statuses: ['DISPATCHED', 'CONFIRMED', 'ARRIVED', 'QUEUED'], + }, + { key: 'PAUSED', label: '已暂停', statuses: ['PAUSED'] }, + { key: 'COMPLETED', label: '已完成', statuses: ['COMPLETED'] }, + { key: 'CANCELLED', label: '已取消', statuses: ['CANCELLED'] }, +]; + +/** 优先级样式映射 */ +export const PRIORITY_STYLE_MAP: Record< + number, + { + animation: boolean; + bgColor: string; + color: string; + icon: string; + label: string; + } +> = { + 0: { + label: 'P0', + color: '#F44336', + bgColor: '#FFEBEE', + icon: 'lucide:zap', + animation: true, + }, + 1: { + label: 'P1', + color: '#FF9800', + bgColor: '#FFF3E0', + icon: 'lucide:alert-triangle', + animation: false, + }, + 2: { + label: 'P2', + color: '#9E9E9E', + bgColor: '#FAFAFA', + icon: 'lucide:info', // Added icon + animation: false, + }, +}; + +/** 工单类型选项 */ +export const ORDER_TYPE_OPTIONS = [ + { label: '保洁', value: OpsOrderCenterApi.OrderType.CLEAN }, + { label: '维修', value: OpsOrderCenterApi.OrderType.REPAIR }, + { label: '安保', value: OpsOrderCenterApi.OrderType.SECURITY }, +]; + +/** 工单类型文本映射 */ +export const ORDER_TYPE_TEXT_MAP: Record = { + CLEAN: '保洁', + REPAIR: '维修', + SECURITY: '安保', +}; + +/** 工单类型颜色映射 */ +export const ORDER_TYPE_COLOR_MAP: Record = { + CLEAN: '#52c41a', // 绿色 + REPAIR: '#fa8c16', // 橙色 + SECURITY: '#1890ff', // 蓝色 +}; + +/** 工单状态选项 */ +export const ORDER_STATUS_OPTIONS = [ + { label: '待分配', value: OpsOrderCenterApi.OrderStatus.PENDING }, + { label: '排队中', value: OpsOrderCenterApi.OrderStatus.QUEUED }, + { label: '已推送', value: OpsOrderCenterApi.OrderStatus.DISPATCHED }, + { label: '已确认', value: OpsOrderCenterApi.OrderStatus.CONFIRMED }, + { label: '已到岗', value: OpsOrderCenterApi.OrderStatus.ARRIVED }, + { label: '已暂停', value: OpsOrderCenterApi.OrderStatus.PAUSED }, + { label: '已完成', value: OpsOrderCenterApi.OrderStatus.COMPLETED }, + { label: '已取消', value: OpsOrderCenterApi.OrderStatus.CANCELLED }, +]; + +/** 优先级选项 */ +export const PRIORITY_OPTIONS = [ + { label: 'P0 (紧急)', value: OpsOrderCenterApi.Priority.P0 }, + { label: 'P1 (重要)', value: OpsOrderCenterApi.Priority.P1 }, + { label: 'P2 (普通)', value: OpsOrderCenterApi.Priority.P2 }, +]; + +/** 触发来源选项 */ +export const TRIGGER_SOURCE_OPTIONS = [ + { label: '蓝牙信标', value: OpsOrderCenterApi.TriggerSource.IOT_BEACON }, + { label: '客流阈值', value: OpsOrderCenterApi.TriggerSource.PEOPLE_FLOW }, + { label: '手动创建', value: OpsOrderCenterApi.TriggerSource.MANUAL }, + { label: '视频告警', value: OpsOrderCenterApi.TriggerSource.VIDEO_ALARM }, + { label: '门禁告警', value: OpsOrderCenterApi.TriggerSource.ACCESS_ALARM }, + { label: '巡更告警', value: OpsOrderCenterApi.TriggerSource.PATROL_ALARM }, + { label: '紧急按钮', value: OpsOrderCenterApi.TriggerSource.PANIC_BUTTON }, +]; + +/** 触发来源文本映射 */ +export const TRIGGER_SOURCE_TEXT_MAP: Record = { + IOT_BEACON: '蓝牙信标', + IOT_TRAFFIC: '客流阈值', + TRAFFIC: '客流阈值', + PEOPLE_FLOW: '客流阈值', + MANUAL: '手动创建', + VIDEO_ALARM: '视频告警', + ACCESS_ALARM: '门禁告警', + PATROL_ALARM: '巡更告警', + PANIC_BUTTON: '紧急按钮', +}; + +/** 列表表格列定义 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'seq', width: 50, title: '序号' }, + { + field: 'orderCode', + title: '工单编号', + minWidth: 180, + showOverflow: true, + }, + { + field: 'title', + title: '工单标题', + minWidth: 180, + showOverflow: true, + }, + { + field: 'orderType', + title: '类型', + width: 80, + align: 'center', + slots: { default: 'orderType' }, + }, + { + field: 'status', + title: '状态', + width: 90, + align: 'center', + slots: { default: 'status' }, + }, + { + field: 'priority', + title: '优先级', + width: 80, + align: 'center', + slots: { default: 'priority' }, + }, + { + field: 'location', + title: '位置', + minWidth: 150, + showOverflow: true, + }, + { + field: 'assigneeName', + title: '执行人', + width: 90, + align: 'center', + slots: { default: 'assignee' }, + }, + { + field: 'createTime', + title: '创建时间', + width: 160, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 200, + fixed: 'right', + align: 'center', + slots: { default: 'actions' }, + }, + ]; +} + +/** 搜索表单Schema */ +export function useSearchFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'orderType', + label: '工单类型', + component: 'Select', + componentProps: { + options: ORDER_TYPE_OPTIONS, + placeholder: '请选择工单类型', + allowClear: true, + }, + }, + { + fieldName: 'status', + label: '工单状态', + component: 'Select', + componentProps: { + options: ORDER_STATUS_OPTIONS, + placeholder: '请选择工单状态', + mode: 'multiple', + allowClear: true, + maxTagCount: 2, + }, + }, + { + fieldName: 'priority', + label: '优先级', + component: 'Select', + componentProps: { + options: PRIORITY_OPTIONS, + placeholder: '请选择优先级', + allowClear: true, + }, + }, + { + fieldName: 'orderCode', + label: '工单编号', + component: 'Input', + componentProps: { + placeholder: '请输入工单编号', + allowClear: true, + }, + }, + { + fieldName: 'title', + label: '工单标题', + component: 'Input', + componentProps: { + placeholder: '请输入标题关键词', + allowClear: true, + }, + }, + ]; +} + +/** 派单表单Schema */ +export function useAssignFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'orderId', + label: '工单ID', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'assigneeId', + label: '执行人', + component: 'Select', + componentProps: { + placeholder: '请选择执行人', + // TODO: 接入保洁员列表API + options: [], + }, + rules: 'required', + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注(选填)', + rows: 3, + }, + }, + ]; +} + +/** 升级优先级表单Schema */ +export function useUpgradePriorityFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'orderId', + label: '工单ID', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'reason', + label: '升级原因', + component: 'Textarea', + componentProps: { + placeholder: '请输入升级为P0紧急工单的原因', + rows: 4, + }, + rules: 'required', + }, + ]; +} + +/** 状态干预表单Schema */ +export function useStatusInterventionFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'orderId', + label: '工单ID', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'reason', + label: '操作原因', + component: 'Textarea', + componentProps: { + placeholder: '请输入操作原因', + rows: 4, + }, + rules: 'required', + }, + ]; +} diff --git a/apps/web-antd/src/views/ops/work-order/detail/index.vue b/apps/web-antd/src/views/ops/work-order/detail/index.vue new file mode 100644 index 000000000..030fa49e6 --- /dev/null +++ b/apps/web-antd/src/views/ops/work-order/detail/index.vue @@ -0,0 +1,1955 @@ + + + + + + + + + + + + + + + + 返回 + + + + + {{ order.title }} + + + {{ STATUS_TEXT_MAP[order.status] }} + + + + {{ getPriorityStyle(order.priority)?.label }} + + + + + + {{ order.orderCode }} + + + + {{ order.location || '-' }} + + + + + + + + + + + + + + + + + + 工单进度 + + + + + + + + 创建于 {{ formatRelativeTime(order.createTime) }} + + + + + {{ businessLogs.length }} 条日志 + + + + + + + 日志 + + + + + + + + + + + + + + + + + + + + {{ step.title }} + + {{ + timeline.find((t) => t.status === step.key)?.time + ? formatRelativeTime( + timeline.find((t) => t.status === step.key)?.time || '', + ) + : step.key === 'PENDING' + ? formatRelativeTime(order.createTime) + : step.key === 'COMPLETED' && order.endTime + ? formatRelativeTime(order.endTime) + : '' + }} + + + + 进行中 + + + + + + + + + 工单已取消 + + + + + + + + + + + + + + + {{ log.title }} + + {{ + log.content + }} + + {{ + formatRelativeTime(log.time) + }} + + + + + + + + + + + + + + + + + + + + + 基础信息 + + + + + + + {{ ORDER_TYPE_TEXT_MAP[order.orderType] }} + + + + + + {{ order.location || '-' }} + + + + + + {{ + TRIGGER_SOURCE_TEXT_MAP[ + order.triggerSource || order.sourceType || '' + ] || '-' + }} + + + + + + {{ formatDateTime(order.createTime) }} + + + + + + {{ formatDateTime(order.startTime) }} + + + + + + {{ formatDateTime(order.endTime) }} + + + + + + {{ formatDateTime(order.updateTime) }} + + + + {{ order.triggerDeviceKey }} + + + + + + + + + + + + + 执行人 + + + + + + {{ order.assigneeName?.charAt(0) || '?' }} + + + + {{ order.assigneeName || '未知' }} + + 工号: {{ order.assigneeId }} + + + {{ getBadgeStatusText(badgeStatus.status) }} + + + + + + + + 工牌实时状态 + + + + + + + + + 位置状态 + + + {{ badgeStatus.isInArea ? '在区域内' : '已离开' }} + + + {{ badgeStatus.areaName }} + + + + + + + + + + 电池电量 + + + + + + {{ badgeStatus.batteryLevel }}% + + + 未知 + + + + + + + + 信号强度 + + + + {{ badgeStatus.rssi }} dBm + + + 未知 + + + + + + + + 最后心跳 + + {{ formatRelativeTime(badgeStatus.lastHeartbeatTime) }} + + + + + + + + + + + 暂未分配执行人 + + + 立即分配 + + + + + + + + + + 快捷操作 + + + + + + + + 分配执行人 + + + + 取消工单 + + + + + + + + + + 取消工单 + + + + + + 升级为 P0 紧急 + + + + + + + + + + diff --git a/apps/web-antd/src/views/ops/work-order/index.vue b/apps/web-antd/src/views/ops/work-order/index.vue new file mode 100644 index 000000000..b35d73f12 --- /dev/null +++ b/apps/web-antd/src/views/ops/work-order/index.vue @@ -0,0 +1,863 @@ + + + + + + + + + + + + + + + + + + + + + + + + + {{ tab.label }} + + {{ tabCounts[tab.key] }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + message.info('手动创建工单功能开发中')" + > + + 创建工单 + + + + + + + + + 关键词 + + + + 工单编号 + + + + 工单类型 + + + + 优先级 + + + + + + + 重置 + + + + 搜索 + + + + + + + + + + + + + + + + {{ ORDER_TYPE_TEXT_MAP[row.orderType] }} + + + + + + + {{ STATUS_TEXT_MAP[row.status] }} + + + + + + P0 + + P1 + + P2 + + + + + {{ row.assigneeName }} + 待分配 + + + + + + 详情 + + + 派单 + + + 提醒 + + + 升级 + + + 取消 + + + + + + + + + diff --git a/apps/web-antd/src/views/ops/work-order/modules/assign-form.vue b/apps/web-antd/src/views/ops/work-order/modules/assign-form.vue new file mode 100644 index 000000000..08415d341 --- /dev/null +++ b/apps/web-antd/src/views/ops/work-order/modules/assign-form.vue @@ -0,0 +1,227 @@ + + + + + + + + + 工单编号: + {{ orderCode }} + + + + + + + 选择执行工牌 + + + + + + + {{ badge.deviceKey?.charAt(0) || '?' }} + + + + + {{ + badge.deviceKey + }} + + {{ getStatusText(badge.status) }} + + + + {{ badge.currentAreaName || '未知区域' }} + + {{ badge.batteryLevel }}% + + + + + + + 暂无可用工牌 + + + + + + + + + + diff --git a/apps/web-antd/src/views/ops/work-order/modules/card-view.vue b/apps/web-antd/src/views/ops/work-order/modules/card-view.vue new file mode 100644 index 000000000..190b5f44e --- /dev/null +++ b/apps/web-antd/src/views/ops/work-order/modules/card-view.vue @@ -0,0 +1,865 @@ + + + + + + + + + + + + + + {{ + STATUS_TEXT_MAP[item.status] + }} + + + + {{ getPriorityInfo(item.priority).label }} + + + + + + + + {{ item.title }} + + + + + + + + + {{ item.orderCode }} + + + + + + + + {{ item.location || '未指定位置' }} + + + + + + + + {{ item.assigneeName }} + + 待分配 + + + + + + + {{ getSourceTypeInfo(item.sourceType).label }} + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web-antd/src/views/ops/work-order/modules/stats-bar.vue b/apps/web-antd/src/views/ops/work-order/modules/stats-bar.vue new file mode 100644 index 000000000..d93399478 --- /dev/null +++ b/apps/web-antd/src/views/ops/work-order/modules/stats-bar.vue @@ -0,0 +1,486 @@ + + + + + + + + + + + + + + + + 待处理 + {{ statsData.pendingCount }} + + + + + + + + + + + + + + 进行中 + {{ statsData.inProgressCount }} + + + + + + + + + + + + + + 今日完成 + + {{ statsData.completedTodayCount }} + + + + + + + + + + + + + + + 在线工牌 + + {{ statsData.onlineBadgeCount }} + + + + + + + + + + + diff --git a/apps/web-antd/src/views/ops/work-order/modules/upgrade-priority-form.vue b/apps/web-antd/src/views/ops/work-order/modules/upgrade-priority-form.vue new file mode 100644 index 000000000..5f2813780 --- /dev/null +++ b/apps/web-antd/src/views/ops/work-order/modules/upgrade-priority-form.vue @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + 工单编号 + {{ orderCode }} + + + 当前优先级 + + + {{ getPriorityText(currentPriority) }} + + + P0 (紧急) + + + + + + + + + + +
{{ order.triggerDeviceKey }}