From dff366a823622f0a126e82c2696047640f371548 Mon Sep 17 00:00:00 2001 From: wangkai <10903508@qq.com> Date: Sun, 25 Oct 2026 02:33:54 +0800 Subject: [PATCH] =?UTF-8?q?feat(survey):=20=E5=8B=98=E5=AF=9F=E4=B8=9A?= =?UTF-8?q?=E5=8A=A1=E9=87=8D=E6=9E=84=E4=B8=BA=E8=AE=BE=E5=A4=87=E6=A0=91?= =?UTF-8?q?=E6=98=8E=E7=BB=86=E6=A8=A1=E5=BC=8F=E5=B9=B6=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=8B=98=E5=AF=9F=E5=AE=8C=E6=88=90=E6=B5=81=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 设备树明细模式:customer_equipment 新增 parent_id 自引用与树形查询(getTree/tree 接口),勘察明细按设备节点挂接(survey_detail 新增 equip_id,saveDetails 全量替换),新增迁移 20261024_survey_equipment_tree.sql 并同步 db_init;客户拜访页发起勘察改为两步向导路由(CustomerVisitSurveyCreate),form.vue 重构为两步向导 - 勘察明细页(detail.vue):三栏布局(设备树/拓扑图/属性与勘察登记);设备拓扑图修复显示不完整(tree roam 拖拽缩放+全展开+弹窗增高);新增/编辑设备表单「所在位置」改为「所属建筑」下拉并增加「所在楼层」;选择建筑后按建筑地上/地下层数动态生成楼层下拉(负N层在前,切换建筑清空楼层) - 设备管理台账页:设备新增弹窗同步「所属建筑/所在楼层」动态下拉 - 台账页:进度跟踪主按钮改为跳转勘察明细页(DispatchSurveyDetail)而非编辑页;「审核测绘成果」按钮与复核弹窗标题改名「技术复核」 - 勘察完成流转:新增 PUT /api/survey-orders/:id/complete(现场勘察中→待技术复核,可见性校验+审计日志),勘察明细页新增「勘察完成」按钮(先保存明细再流转状态,成功后跳回台账页) - 验证:server tsc 与 client vue-tsc 编译通过;浏览器端到端验证通过(技术复核按钮/弹窗标题、勘察完成按钮仅勘察中显示、进度跟踪跳转数字id) --- client/src/api/customerEquipment.ts | 77 + client/src/api/device.ts | 36 +- client/src/api/surveyOrder.ts | 23 +- client/src/router/index.ts | 36 +- client/src/views/customer/visit/index.vue | 4 +- client/src/views/device/equipment/index.vue | 278 +++- client/src/views/dispatch/survey/detail.vue | 1266 +++++++++++++++++ client/src/views/dispatch/survey/form.vue | 394 ++--- client/src/views/dispatch/survey/index.vue | 170 ++- database/db_init.sql | 22 +- .../20261024_survey_equipment_tree.sql | 22 + .../customer_equipment.controller.ts | 4 + .../src/controller/survey_order.controller.ts | 8 +- server/src/dto/survey_order.dto.ts | 19 +- .../src/entity/customer_equipment.entity.ts | 8 +- server/src/entity/survey_detail.entity.ts | 14 +- .../src/service/customer_equipment.service.ts | 79 +- server/src/service/survey_order.service.ts | 60 +- 系统设计/表结构新增及变更.md | 17 + 19 files changed, 2095 insertions(+), 442 deletions(-) create mode 100644 client/src/api/customerEquipment.ts create mode 100644 client/src/views/dispatch/survey/detail.vue create mode 100644 database/migrations/20261024_survey_equipment_tree.sql diff --git a/client/src/api/customerEquipment.ts b/client/src/api/customerEquipment.ts new file mode 100644 index 0000000..b9d46a3 --- /dev/null +++ b/client/src/api/customerEquipment.ts @@ -0,0 +1,77 @@ +import request from '@/utils/request'; + +// 客户设备类型:配电间/配电井/配电箱/配电柜 +export const EQUIP_TYPE_OPTIONS = [ + { label: '配电间', value: '配电间' }, + { label: '配电井', value: '配电井' }, + { label: '配电箱', value: '配电箱' }, + { label: '配电柜', value: '配电柜' }, +]; + +export interface CustomerEquipmentNode { + id: number; + equipCode: string; + customerId: number; + equipName: string; + equipType?: string; + parentId?: number | null; + buildingId?: number | null; + floorNo?: string | null; + location?: string; + description?: string; + remark?: string; + photoUrl?: string; + createdAt?: string; + // 树形:子节点(tree 接口返回) + children?: CustomerEquipmentNode[]; +} + +export interface CustomerEquipmentCreateParams { + customerId: number; + equipName: string; + equipType: string; + parentId?: number | null; + buildingId?: number | null; + floorNo?: string | null; + location?: string; + description?: string; + remark?: string; + photoUrl?: string; +} + +export type CustomerEquipmentUpdateParams = Partial; + +export interface CustomerEquipmentQuery { + page?: number; + pageSize?: number; + keyword?: string; + customerId?: number; + equipType?: string; +} + +export interface CustomerEquipmentListResult { + list: CustomerEquipmentNode[]; + total: number; + page: number; + pageSize: number; +} + +export const customerEquipmentApi = { + // 客户设备树(按 parentId 嵌套,根节点 parentId 为空) + getTree: (customerId: number) => + request.get('/customer-equipments/tree', { params: { customerId } }), + + getList: (params: CustomerEquipmentQuery) => + request.get('/customer-equipments', { params }), + + getDetail: (id: number) => + request.get(`/customer-equipments/${id}`), + + create: (data: CustomerEquipmentCreateParams) => + request.post('/customer-equipments', data), + + update: (id: number, data: CustomerEquipmentUpdateParams) => + request.put(`/customer-equipments/${id}`, data), + + delete: (id: number) => request.delete(`/customer-equipments/${id}`) +}; diff --git a/client/src/api/device.ts b/client/src/api/device.ts index d8a1552..ef5b9f2 100644 --- a/client/src/api/device.ts +++ b/client/src/api/device.ts @@ -4,16 +4,16 @@ import request from '@/utils/request'; export interface CustomerEquipment { id: number; - equipmentNo: string; - equipmentName: string; + equipCode: string; + equipName: string; equipType?: string; customerId?: number; - customerName?: string; - building?: string; - floor?: string; + parentId?: number | null; + buildingId?: number | null; + buildingName?: string; + floorNo?: string; location?: string; - photo?: string; - status?: number; + photoUrl?: string; remark?: string; createdAt: string; updatedAt: string; @@ -24,7 +24,7 @@ export interface CustomerEquipmentQuery { pageSize?: number; keyword?: string; customerId?: number; - building?: string; + buildingId?: number; equipType?: string; } @@ -36,26 +36,26 @@ export interface CustomerEquipmentListResult { } export interface CustomerEquipmentCreateParams { - equipmentName: string; + equipName: string; equipType?: string; customerId?: number; - building?: string; - floor?: string; + parentId?: number | null; + buildingId?: number | null; + floorNo?: string; location?: string; - photo?: string; - status?: number; + photoUrl?: string; remark?: string; } export interface CustomerEquipmentUpdateParams { - equipmentName?: string; + equipName?: string; equipType?: string; customerId?: number; - building?: string; - floor?: string; + parentId?: number | null; + buildingId?: number | null; + floorNo?: string; location?: string; - photo?: string; - status?: number; + photoUrl?: string; remark?: string; } diff --git a/client/src/api/surveyOrder.ts b/client/src/api/surveyOrder.ts index b633d7d..53356c0 100644 --- a/client/src/api/surveyOrder.ts +++ b/client/src/api/surveyOrder.ts @@ -57,10 +57,12 @@ export interface SurveyOrderWorker { completedCount?: number; } -// 勘察明细:一条 = 建筑 + 楼层 + 房间(可空) + 现场照片 +// 勘察明细:一条 = 设备节点(优先)+ 现场照片;兼容历史 建筑/楼层/房间 export interface SurveyOrderDetail { id?: number; surveyOrderId?: number; + // 客户设备 ID(→ customer_equipment,勘察明细挂接到设备节点) + equipId?: number | null; buildingId?: number | null; // 建筑名称(冗余存储,客户拓扑变更后仍可读) buildingName?: string | null; @@ -75,6 +77,18 @@ export interface SurveyOrderDetail { remark?: string | null; } +// 客户设备树节点(工单详情附带,勘察明细按设备节点拓扑展示) +// 与 @/api/customerEquipment 的 CustomerEquipmentNode 对齐(equip_code/customer_id/equip_name 必填,equip_type 可空) +export interface SurveyEquipNode { + id: number; + equipCode: string; + customerId: number; + equipName: string; + equipType?: string; + parentId?: number | null; + children?: SurveyEquipNode[]; +} + export interface SurveyOrderInfo { id: number; surveyNo: string; @@ -124,8 +138,10 @@ export interface SurveyOrderInfo { updatedAt?: string; created_at?: string; updated_at?: string; - // 详情附带:勘察明细(建筑/楼层/房间 + 现场照片) + // 详情附带:勘察明细(设备节点/建筑楼层房间 + 现场照片) details?: SurveyOrderDetail[]; + // 详情附带:客户设备树(勘察明细按设备节点拓扑展示) + equipmentTree?: SurveyEquipNode[]; } export interface SurveyOrderQuery { @@ -260,6 +276,9 @@ export const surveyOrderApi = { review: (id: number, data: SurveyOrderReviewParams) => request.put(`/survey-orders/${id}/review`, data), + // 勘察完成:现场勘察中 → 待技术复核 + complete: (id: number) => request.put(`/survey-orders/${id}/complete`), + // 催办 urge: (id: number) => request.post(`/survey-orders/${id}/urge`), diff --git a/client/src/router/index.ts b/client/src/router/index.ts index a4a631a..f9712d4 100644 --- a/client/src/router/index.ts +++ b/client/src/router/index.ts @@ -79,6 +79,18 @@ const routes: RouteRecordRaw[] = [ parentPath: "/customer/visit" } }, + { + path: "customer/visit/survey/create", + name: "CustomerVisitSurveyCreate", + component: () => import("@/views/dispatch/survey/form.vue"), + meta: { + requiresAuth: true, + title: "发起勘察派单", + permission: "customer:visit", + parentTitle: "客户拜访", + parentPath: "/customer/visit" + } + }, { path: "customer/topology", name: "CustomerTopology", @@ -133,18 +145,6 @@ const routes: RouteRecordRaw[] = [ component: () => import("@/views/dispatch/survey/index.vue"), meta: { requiresAuth: true, title: "项目勘察", permission: "project:survey" } }, - { - path: "dispatch/survey/create", - name: "DispatchSurveyCreate", - component: () => import("@/views/dispatch/survey/form.vue"), - meta: { - requiresAuth: true, - title: "新建勘察派单", - permission: "project:survey", - parentTitle: "现场勘察派单", - parentPath: "/dispatch/survey" - } - }, { path: "dispatch/survey/edit/:id", name: "DispatchSurveyEdit", @@ -157,6 +157,18 @@ const routes: RouteRecordRaw[] = [ parentPath: "/dispatch/survey" } }, + { + path: "dispatch/survey/:id/details", + name: "DispatchSurveyDetail", + component: () => import("@/views/dispatch/survey/detail.vue"), + meta: { + requiresAuth: true, + title: "勘察明细", + permission: "project:survey", + parentTitle: "现场勘察派单", + parentPath: "/dispatch/survey" + } + }, { path: "dispatch/install", name: "DispatchInstall", diff --git a/client/src/views/customer/visit/index.vue b/client/src/views/customer/visit/index.vue index 96a4fe4..77bded1 100644 --- a/client/src/views/customer/visit/index.vue +++ b/client/src/views/customer/visit/index.vue @@ -795,10 +795,10 @@ function handleFollow(record: CustomerVisitInfo) { router.push({ name: 'CustomerVisitEdit', params: { id: record.id }, query: { action: 'follow' } }); } -// 一键发起勘察派单:跳转勘察派单页并预填客户(不创建、不回写状态) +// 一键发起勘察派单:跳转两步向导并预填客户(不创建、不回写状态) function handleSurvey(record: CustomerVisitInfo) { if (!record.customerId) return; - router.push({ path: '/dispatch/survey', query: { customerId: String(record.customerId) } }); + router.push({ name: 'CustomerVisitSurveyCreate', query: { customerId: String(record.customerId) } }); } // 查看关联工程 diff --git a/client/src/views/device/equipment/index.vue b/client/src/views/device/equipment/index.vue index d9972f5..fbe1f62 100644 --- a/client/src/views/device/equipment/index.vue +++ b/client/src/views/device/equipment/index.vue @@ -38,11 +38,11 @@
-
@@ -103,12 +103,12 @@ @change="handleTableChange" >