From e4eb5e316ee41cf41d5c90afb5824e542768bec6 Mon Sep 17 00:00:00 2001 From: zlwu <704541015@qq.com> Date: Tue, 26 Aug 2025 14:41:10 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=A4=9A=E4=BD=99?= =?UTF-8?q?=E7=9A=84md=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/开发文档/API/物业报修管理API.md | 811 --------------------------- 1 file changed, 811 deletions(-) delete mode 100644 docs/开发文档/API/物业报修管理API.md diff --git a/docs/开发文档/API/物业报修管理API.md b/docs/开发文档/API/物业报修管理API.md deleted file mode 100644 index b7141d5c..00000000 --- a/docs/开发文档/API/物业报修管理API.md +++ /dev/null @@ -1,811 +0,0 @@ -# 物业报修管理 API - -## 📋 文档说明 - -本文档记录物业报修管理相关 API 接口,包括维修分类管理、报修工单管理、工单进度跟踪等功能。 - -**依赖文档**: 请先阅读 [API 文档基础规范.md](./API文档基础规范.md) 了解通用规范。 - ---- - -## 🔧 维修分类管理 - -### 查询维修分类列表 - -**接口地址**: `GET /hq/pm/repairCategory/list` - -**接口描述**: 分页查询维修分类列表,支持权限过滤 - -**权限要求**: `pm:category:list` - -**请求参数**: - -| 参数名 | 类型 | 必填 | 说明 | -| ------------ | ------ | ---- | --------------------- | -| pageNum | int | 否 | 页码,默认 1 | -| pageSize | int | 否 | 页大小,默认 10 | -| categoryName | string | 否 | 分类名称 | -| categoryCode | string | 否 | 分类编码 | -| parentId | long | 否 | 父级分类 ID | -| skillLevel | string | 否 | 技能等级要求 | -| status | string | 否 | 状态(0-停用,1-正常) | - -**响应示例**: - -```json -{ - "total": 6, - "rows": [ - { - "id": 1, - "categoryName": "电路维修", - "categoryCode": "ELECTRIC", - "categoryDescription": "电路、照明、开关、插座等电气设备维修", - "parentId": 0, - "ancestorIds": "0", - "levelDepth": 1, - "childrenCount": 0, - "requiredSkills": "电工技能、安全操作", - "skillLevel": "2", - "estimatedDuration": 120, - "standardCost": 200.0, - "iconUrl": null, - "colorCode": "#FF6B35", - "sortOrder": 1, - "status": "1", - "usageCount": 0, - "lastUsedTime": null, - "remark": null, - "createTime": "2025-01-23 14:30:00" - } - ], - "code": 200, - "msg": "查询成功" -} -``` - -### 获取维修分类树形结构 - -**接口地址**: `GET /hq/pm/repairCategory/tree` - -**接口描述**: 获取维修分类的树形结构数据 - -**权限要求**: `pm:category:tree` - -**请求参数**: 无 - -**响应示例**: - -```json -{ - "code": 200, - "msg": "操作成功", - "data": [ - { - "id": 1, - "categoryName": "电路维修", - "categoryCode": "ELECTRIC", - "categoryDescription": "电路、照明、开关、插座等电气设备维修", - "parentId": 0, - "children": [] - } - ] -} -``` - -### 获取维修分类下拉选项 - -**接口地址**: `GET /hq/pm/repairCategory/options` - -**接口描述**: 获取维修分类的下拉选项数据,用于表单选择器 - -**权限要求**: `pm:category:list` - -**请求参数**: 无 - -**响应示例**: - -```json -{ - "code": 200, - "msg": "操作成功", - "data": [ - { - "value": 1, - "label": "电路维修" - }, - { - "value": 2, - "label": "水管维修" - }, - { - "value": 3, - "label": "门窗维修" - } - ] -} -``` - -**数据格式说明**: - -- **value**: 分类 ID,用于表单提交 -- **label**: 分类名称,用于显示 -- **扁平化处理**: 将树形结构扁平化,便于下拉选择 - -**使用场景**: - -- 报修工单创建时选择维修类型 -- 表单编辑时的分类选择 -- 搜索条件中的分类筛选 - -### 获取维修分类详情 - -**接口地址**: `GET /hq/pm/repairCategory/{id}` - -**接口描述**: 根据 ID 获取维修分类详细信息 - -**权限要求**: `pm:category:query` - -**路径参数**: - -| 参数名 | 类型 | 必填 | 说明 | -| ------ | ---- | ---- | ------- | -| id | long | 是 | 分类 ID | - -### 新增维修分类 - -**接口地址**: `POST /hq/pm/repairCategory` - -**接口描述**: 创建新的维修分类 - -**权限要求**: `pm:category:add` - -**请求参数**: - -```json -{ - "categoryName": "测试维修类型", - "categoryCode": "TEST_REPAIR", - "categoryDescription": "这是一个测试的维修类型", - "parentId": 0, - "skillLevel": "1", - "estimatedDuration": 60, - "standardCost": 100.0, - "colorCode": "#1890FF", - "sortOrder": 99, - "status": "1", - "remark": "测试数据" -} -``` - ---- - -## 🛠️ 报修工单管理 - -### 查询报修工单列表 - -**接口地址**: `GET /hq/pm/repairOrder/list` - -**接口描述**: 分页查询报修工单列表,支持多种条件筛选,根据用户角色进行数据权限过滤 - -**权限要求**: `pm:repairOrder:list` - -**数据权限说明**: - -- **超级管理员**: 可查看所有报修工单 -- **物业维修管理员** (role_key: property_maintenance_admin): 可查看所有报修工单 -- **维修人员** (role_key: maintenance_worker): 只能查看自己创建的工单和分配给自己的工单 -- **普通用户**: 只能查看自己创建的工单 - -**请求参数**: - -| 参数名 | 类型 | 必填 | 说明 | -| --------------- | ------ | ---- | --------------- | -| pageNum | int | 否 | 页码,默认 1 | -| pageSize | int | 否 | 页大小,默认 10 | -| orderNo | string | 否 | 工单编号 | -| title | string | 否 | 报修标题 | -| repairLocation | string | 否 | 报修地点 | -| categoryId | long | 否 | 维修类型 ID | -| priorityLevel | int | 否 | 紧急程度 | -| reporterName | string | 否 | 报修人姓名 | -| reporterPhone | string | 否 | 报修人电话 | -| orderStatus | string | 否 | 工单状态 | -| assigneeId | long | 否 | 分派维修员 ID | -| assigneeName | string | 否 | 分派维修员姓名 | -| createTimeRange | array | 否 | 创建时间范围 | -| finishTimeRange | array | 否 | 完成时间范围 | - -**响应示例**: - -```json -{ - "total": 1, - "rows": [ - { - "id": "1939510596285673473", - "tenantId": "000000", - "orderNo": "RO202506300002", - "title": "详细测试工单 - 会议室投影仪故障", - "description": "会议室投影仪无法正常启动,显示器黑屏,影响会议正常进行。设备型号:EPSON EB-U05,购买时间:2023年6月。", - "repairLocation": "行政楼三楼305会议室", - "categoryId": 1, - "categoryName": "电路维修", - "priorityLevel": 2, - "expectedFinishTime": "2025-07-02 02:25:34", - "reporterId": 1, - "reporterName": "测试用户", - "reporterPhone": "13900139001", - "reporterDept": "信息技术部", - "orderStatus": "1", - "assignTime": null, - "assigneeId": null, - "assigneeName": null, - "assignRemark": null, - "startTime": null, - "finishTime": null, - "totalDuration": null, - "repairResult": null, - "usedMaterials": null, - "followSuggestions": null, - "evaluationScore": null, - "evaluationTime": null, - "attachmentCount": 0, - "progressCount": 1, - "cancelReason": null, - "remark": "设备在保修期内,请联系厂家技术支持", - "createTime": "2025-06-30 02:25:34", - "updateTime": "2025-06-30 02:25:34" - } - ], - "code": 200, - "msg": "查询成功" -} -``` - -### 生成工单编号 - -**接口地址**: `GET /hq/pm/repairOrder/generateOrderNo` - -**接口描述**: 自动生成报修工单编号 - -**权限要求**: 无需特殊权限 - -**请求参数**: 无 - -**响应示例**: - -```json -{ - "code": 200, - "msg": "RO202506300002", - "data": null -} -``` - -**说明**: 工单编号格式为`RO + 年月日 + 序号`,如`RO202506300002` - -### 获取报修统计数据 - -**接口地址**: `GET /hq/pm/repairOrder/statistics` - -**接口描述**: 获取报修工单的统计数据,用于仪表板展示 - -**权限要求**: `pm:repairOrder:statistics` - -**访问权限说明**: - -- **超级管理员**: 可以访问统计数据 -- **物业维修管理员** (role_key: property_maintenance_admin): 可以访问统计数据 -- **维修人员** (role_key: maintenance_worker): **无权限访问** -- **普通用户**: **无权限访问** - -**请求参数**: 无 - -**响应示例**: - -```json -{ - "code": 200, - "msg": "操作成功", - "data": { - "totalOrders": 156, - "pendingAssign": 23, - "inProgress": 45, - "completed": 88, - "cancelled": 0, - "avgProcessTime": 2.5, - "avgEvaluationScore": 4.3, - "monthlyNewOrders": 67, - "monthlyCompletedOrders": 52, - "statusDistribution": [ - { - "status": "待分派", - "count": 23, - "percentage": 14.7 - }, - { - "status": "进行中", - "count": 45, - "percentage": 28.8 - }, - { - "status": "已完成", - "count": 88, - "percentage": 56.4 - } - ], - "categoryDistribution": [ - { - "categoryName": "电路维修", - "count": 45, - "percentage": 28.8 - }, - { - "categoryName": "水管维修", - "count": 38, - "percentage": 24.4 - } - ], - "priorityDistribution": [ - { - "priorityLevel": 1, - "count": 12, - "percentage": 7.7 - }, - { - "priorityLevel": 2, - "count": 34, - "percentage": 21.8 - } - ] - } -} -``` - -**统计字段说明**: - -| 字段名 | 类型 | 说明 | -| ---------------------- | ----- | ---------------------- | -| totalOrders | int | 总工单数量 | -| pendingAssign | int | 待分派工单数 | -| inProgress | int | 进行中工单数 | -| completed | int | 已完成工单数 | -| cancelled | int | 已取消工单数 | -| avgProcessTime | float | 平均处理时长(小时) | -| avgEvaluationScore | float | 平均评价得分(1-5 分) | -| monthlyNewOrders | int | 本月新增工单数 | -| monthlyCompletedOrders | int | 本月完成工单数 | -| statusDistribution | array | 各状态分布统计 | -| categoryDistribution | array | 各类型分布统计 | -| priorityDistribution | array | 紧急程度分布统计 | - -### 分派工单 - -**接口地址**: `PUT /hq/pm/repairOrder/{orderId}/assign` - -**接口描述**: 分派工单给维修员 - -**权限要求**: `pm:repairOrder:assign` - -**路径参数**: - -| 参数名 | 类型 | 必填 | 说明 | 示例值 | -| ------- | ---- | ---- | ------- | ------ | -| orderId | Long | 是 | 工单 ID | 1 | - -**请求参数**: - -| 参数名 | 类型 | 必填 | 说明 | 示例值 | -| ------------ | ------ | ---- | --------- | ---------- | -| assigneeId | Long | 是 | 维修员 ID | 2 | -| assignRemark | String | 否 | 分派备注 | "紧急处理" | - -**响应示例**: - -```json -{ - "code": 200, - "msg": "分派成功", - "data": null -} -``` - -**错误码**: - -| 错误码 | 说明 | 解决方案 | -| ------ | ------------------ | ------------- | -| 400001 | 工单不存在 | 检查工单 ID | -| 400002 | 维修员不存在 | 检查维修员 ID | -| 400003 | 工单状态不允许分派 | 检查工单状态 | - -### 获取工单进度列表 - -**接口地址**: `GET /hq/pm/repairOrder/{orderId}/progress` - -**接口描述**: 获取指定工单的进度记录列表 - -**权限要求**: `pm:repairOrder:progress` - -**路径参数**: - -| 参数名 | 类型 | 必填 | 说明 | 示例值 | -| ------- | ---- | ---- | ------- | ------ | -| orderId | Long | 是 | 工单 ID | 1 | - -**响应示例**: - -```json -{ - "code": 200, - "msg": "查询成功", - "data": [ - { - "id": 1, - "orderId": 1, - "progressType": "5", - "progressStatus": "正在维修", - "progressDetail": "已到达现场,开始检查设备", - "operatorId": 2, - "operatorName": "张维修", - "operatorRole": "worker", - "locationInfo": "教学楼A座301", - "estimatedDuration": 60, - "operationTime": "2025-06-30 14:30:00", - "nextStep": "更换零件", - "issuesEncountered": "发现零件老化", - "materialsNeeded": "电路板x1", - "remark": "需要采购新零件" - } - ] -} -``` - -### 更新维修进度 - -**接口地址**: `POST /hq/pm/repairOrder/progress` - -**接口描述**: 添加工单维修进度记录 - -**权限要求**: `pm:repairOrder:updateProgress` - -**请求参数**: - -| 参数名 | 类型 | 必填 | 说明 | 示例值 | -| ----------------- | ------- | ---- | ---------------------------------------------------------------------------------------------------------------- | -------------------------- | -| orderId | Long | 是 | 工单 ID | 1 | -| progressType | String | 是 | 进度类型:1-工单分派,2-接收确认,3-到达现场,4-开始维修,5-进度更新,6-完成维修,7-用户评价,8-工单归档,9-其他 | "5" | -| progressStatus | String | 是 | 进度状态描述 | "正在维修" | -| progressDetail | String | 是 | 进度详细描述 | "已到达现场,开始检查设备" | -| locationInfo | String | 否 | 操作位置信息 | "教学楼 A 座 301" | -| estimatedDuration | Integer | 否 | 预计耗时(分钟) | 60 | -| nextStep | String | 否 | 下一步计划 | "更换零件" | -| issuesEncountered | String | 否 | 遇到的问题 | "发现零件老化" | -| materialsNeeded | String | 否 | 需要的材料 | "电路板 x1" | -| remark | String | 否 | 备注信息 | "需要采购新零件" | - -**响应示例**: - -```json -{ - "code": 200, - "msg": "进度更新成功", - "data": null -} -``` - -### 完成工单 - -**接口地址**: `PUT /hq/pm/repairOrder/{orderId}/finish` - -**接口描述**: 完成工单维修,填写维修结果 - -**权限要求**: `pm:repairOrder:complete` - -**路径参数**: - -| 参数名 | 类型 | 必填 | 说明 | 示例值 | -| ------- | ---- | ---- | ------- | ------ | -| orderId | Long | 是 | 工单 ID | 1 | - -**请求参数**: - -| 参数名 | 类型 | 必填 | 说明 | 示例值 | -| ----------------- | ------ | ---- | ------------ | -------------------------------- | -| repairResult | String | 是 | 维修结果描述 | "已更换故障电路板,设备恢复正常" | -| usedMaterials | String | 否 | 使用材料清单 | "电路板 x1, 螺丝 x4" | -| followSuggestions | String | 否 | 后续建议 | "建议定期检查电路板状态" | - -**响应示例**: - -```json -{ - "code": 200, - "msg": "工单完成成功", - "data": null -} -``` - ---- - -## ⭐ 服务评价管理 - -### 创建服务评价 - -**接口地址**: `POST /hq/pm/repairOrder/evaluation` - -**接口描述**: 创建工单服务评价 - -**权限要求**: `pm:repairOrder:evaluate` - -**请求参数**: - -| 参数名 | 类型 | 必填 | 说明 | 示例值 | -| ---------------------- | ------- | ---- | -------------------------------------------------------------- | ---------------------------- | -| orderId | Long | 是 | 工单 ID | 1 | -| evaluatedWorkerId | Long | 是 | 被评价维修员 ID | 2 | -| overallScore | Number | 是 | 综合评价得分(1.0-5.0) | 4.5 | -| qualityScore | Number | 是 | 维修质量得分(1.0-5.0) | 4.8 | -| attitudeScore | Number | 是 | 服务态度得分(1.0-5.0) | 4.5 | -| responseSpeedScore | Number | 是 | 响应速度得分(1.0-5.0) | 4.2 | -| communicationScore | Number | 是 | 沟通效果得分(1.0-5.0) | 4.6 | -| evaluationContent | String | 是 | 文字评价内容 | "维修师傅技术过硬,态度很好" | -| satisfactionLevel | String | 是 | 满意度等级:1-非常满意,2-满意,3-一般,4-不满意,5-非常不满意 | "1" | -| isAnonymous | Integer | 否 | 是否匿名评价:0-否,1-是 | 0 | -| isPublic | Integer | 否 | 是否公开显示:0-否,1-是 | 1 | -| positiveTags | String | 否 | 正面标签 | "技术专业,态度友好" | -| negativeTags | String | 否 | 负面标签 | "" | -| improvementSuggestions | String | 否 | 改进建议 | "希望能提前通知到达时间" | -| remark | String | 否 | 备注信息 | "" | - -**响应示例**: - -```json -{ - "code": 200, - "msg": "评价提交成功", - "data": null -} -``` - -### 获取工单评价 - -**接口地址**: `GET /hq/pm/repairOrder/{orderId}/evaluation` - -**接口描述**: 获取指定工单的服务评价信息 - -**权限要求**: `pm:repairOrder:query` - -**路径参数**: - -| 参数名 | 类型 | 必填 | 说明 | 示例值 | -| ------- | ---- | ---- | ------- | ------ | -| orderId | Long | 是 | 工单 ID | 1 | - -**响应示例**: - -```json -{ - "code": 200, - "msg": "查询成功", - "data": { - "id": 1, - "orderId": 1, - "evaluatorId": 3, - "evaluatorName": "张老师", - "evaluatedWorkerId": 2, - "evaluatedWorkerName": "张维修", - "overallScore": 4.5, - "qualityScore": 4.8, - "attitudeScore": 4.5, - "responseSpeedScore": 4.2, - "communicationScore": 4.6, - "evaluationContent": "维修师傅技术过硬,态度很好", - "satisfactionLevel": "1", - "isAnonymous": 0, - "isPublic": 1, - "positiveTags": "技术专业,态度友好", - "improvementSuggestions": "希望能提前通知到达时间", - "evaluationTime": "2025-06-30 16:30:00" - } -} -``` - ---- - -## 📊 数据字典 - -### 工单状态 - -| 值 | 说明 | 备注 | -| --- | ------ | ------------ | -| 1 | 待分派 | 已创建待分派 | -| 2 | 进行中 | 已分派进行中 | -| 3 | 已完成 | 维修完成 | -| 4 | 已取消 | 工单取消 | - -### 紧急程度 - -| 值 | 说明 | 备注 | -| --- | ---- | ------------ | -| 1 | 低 | 一般维修 | -| 2 | 中 | 较重要维修 | -| 3 | 高 | 紧急维修 | -| 4 | 紧急 | 非常紧急维修 | - -### 进度类型 - -| 值 | 说明 | 备注 | -| --- | -------- | -------------- | -| 1 | 工单分派 | 分派给维修员 | -| 2 | 接收确认 | 维修员确认接收 | -| 3 | 到达现场 | 到达维修现场 | -| 4 | 开始维修 | 开始进行维修 | -| 5 | 进度更新 | 维修进度更新 | -| 6 | 完成维修 | 维修工作完成 | -| 7 | 用户评价 | 用户评价完成 | -| 8 | 工单归档 | 工单归档完成 | -| 9 | 其他 | 其他操作 | - -### 满意度等级 - -| 值 | 说明 | 备注 | -| --- | ---------- | ---- | -| 1 | 非常满意 | 5 分 | -| 2 | 满意 | 4 分 | -| 3 | 一般 | 3 分 | -| 4 | 不满意 | 2 分 | -| 5 | 非常不满意 | 1 分 | - ---- - -## 📞 联系方式 - -如有物业报修相关问题,请联系开发团队或查看项目仓库的 Issues。 - ---- - -**📝 维护**: 物业报修管理 API 文档 -**🔄 更新**: 2025-07-01 -**�� 文件**: 物业报修管理 API.md - -## 📊 统计分析接口 - -**详细信息参考**: `docs/业务模块/物业报修管理/统计分析功能.md` - ---- - -## 🔧 培训申请待审批接口 (新增 2025-01-03) - -### 查询待审批培训申请列表 - -**接口地址**: `GET /rs/training/application/pending/list` - -**权限要求**: `rs:trainingapproval:process` - -**接口说明**: 专门为审批人员提供的待审批培训申请列表接口,解决了rs-Approver角色权限问题 - -#### 请求参数 - -| 参数名 | 类型 | 必填 | 说明 | -|--------|------|------|------| -| pageNum | number | 否 | 页码,默认1 | -| pageSize | number | 否 | 每页数量,默认10 | -| courseName | string | 否 | 课程名称(模糊查询) | -| location | string | 否 | 培训地点(模糊查询) | -| status | string | 否 | 申请状态 | -| excludeStatus | string | 否 | 排除状态(默认排除已通过状态"1") | - -#### 响应参数 - -```json -{ - "code": 200, - "msg": "查询成功", - "rows": [ - { - "applicationId": 1234567890123456789, - "courseId": 1, - "courseName": "Spring Boot开发培训", - "userId": 123, - "userName": "张三", - "department": "技术部", - "position": "开发工程师", - "reason": "提升技术能力", - "status": "0", - "applyTime": "2025-01-03 10:00:00", - "location": "培训中心A座", - "duration": 3, - "startTime": "2025-01-10 09:00:00", - "endTime": "2025-01-12 17:00:00" - } - ], - "total": 15 -} -``` - -#### 状态码说明 - -| 状态 | 含义 | 类型 | -|------|------|------| -| 0 | 待审核 | warning | -| 1 | 已通过 | success | -| 2 | 已拒绝 | danger | -| 3 | 已取消 | info | -| 4 | 已完成 | success | - -#### 权限控制 - -- **接口权限**: `rs:trainingapproval:process` -- **角色支持**: rs-Approver, rs-admin -- **数据过滤**: - - rs-Approver角色:可查看所有待审批申请 - - rs-admin角色:可查看所有申请 - - 自动排除已通过状态的申请 - -#### 使用示例 - -**前端调用**: -```typescript -import { listPendingTrainingApplication } from '@/api/rs/training' - -// 查询待审批列表 -const getPendingList = async () => { - const params = { - pageNum: 1, - pageSize: 10, - excludeStatus: '1' // 排除已通过状态 - } - const response = await listPendingTrainingApplication(params) - return response -} -``` - -**Curl示例**: -```bash -curl -X GET "http://localhost:8080/rs/training/application/pending/list?pageNum=1&pageSize=10" \ - -H "Authorization: Bearer your-token" \ - -H "clientid: e5cd7e4891bf95d1d19206ce24a7b32e" \ - -H "referer: http://localhost" -``` - -#### 与原接口对比 - -| 项目 | 原接口 | 新接口 | -|------|--------|--------| -| 接口地址 | `/rs/training/application/list` | `/rs/training/application/pending/list` | -| 权限要求 | `rs:trainingapplication:list` | `rs:trainingapproval:process` | -| 角色支持 | rs-admin | rs-Approver, rs-admin | -| 设计目的 | 申请管理 | 审批处理 | -| 权限独立性 | ❌ 权限混合 | ✅ 权限分离 | - -#### 技术实现 - -**后端Controller**: -```java -@SaCheckPermission("rs:trainingapproval:process") -@GetMapping("/pending/list") -public TableDataInfo pendingList( - RsTrainingApplication application, - PageQuery pageQuery) { - // 设置租户ID和排除状态 - application.setTenantId(LoginHelper.getTenantId()); - application.setExcludeStatus("1"); // 排除已通过状态 - - return trainingApplicationService.selectPageRsTrainingApplicationList( - application, pageQuery); -} -``` - -**权限过滤逻辑**: -- Service层根据rs-Approver角色自动放开数据权限 -- Mapper层支持excludeStatus参数过滤 -- 前端调用专门的审批接口,权限控制更精确 - ---- - -**接口文档最后更新**: 2025-01-03 -**维护责任人**: 系统架构师 -**相关文档**: -- `docs/开发文档/培训申请权限一致性修复报告.md` -- `.cursor/cursorrules-backend.md` - 后端开发规范 From 46775101e964d5ee953dca7fde06fbb6cd1d6389 Mon Sep 17 00:00:00 2001 From: zlwu <704541015@qq.com> Date: Tue, 26 Aug 2025 15:48:36 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mobile/src/pages/xg/xsjcxx/xsjcxx/index.vue | 593 +++++++++++++++----- 1 file changed, 453 insertions(+), 140 deletions(-) diff --git a/mobile/src/pages/xg/xsjcxx/xsjcxx/index.vue b/mobile/src/pages/xg/xsjcxx/xsjcxx/index.vue index a646bd0b..49b1e42a 100644 --- a/mobile/src/pages/xg/xsjcxx/xsjcxx/index.vue +++ b/mobile/src/pages/xg/xsjcxx/xsjcxx/index.vue @@ -10,6 +10,12 @@ + + + + 部分辅助数据正在后台加载中... + + @@ -95,7 +101,7 @@ 籍贯 - {{ getRegionFullPath(form.nativePlace) || '-' }} + {{ nativePlaceDisplayName || '-' }} 政治面貌 @@ -172,7 +178,7 @@ 生源地区 - {{ getRegionFullPath(form.sourceRegion) || '-' }} + {{ sourceRegionDisplayName || '-' }} @@ -782,38 +788,32 @@ export default { // 显示名称(异步加载) roomDisplayName: '', bedDisplayName: '', + nativePlaceDisplayName: '', // 籍贯显示名称 + sourceRegionDisplayName: '', // 生源地区显示名称 + + // 加载状态 + regionLoadingPromise: null, + backgroundDataLoaded: false, // 后台数据是否加载完成 }; }, async created() { console.log('=== 学生信息页面初始化开始 ==='); - try { - await this.loadDictData(); - console.log('✓ 字典数据加载完成'); - } catch (error) { - console.error('字典数据加载出错(不影响后续流程):', error); - } - - try { - await this.loadRegionAndDormData(); - console.log('✓ 区域和宿舍数据加载完成'); - } catch (error) { - console.error('区域和宿舍数据加载出错:', error); - } - - try { - await this.loadDormRoomAndBedData(); - console.log('✓ 房间和床位数据加载完成'); - } catch (error) { - console.error('房间和床位数据加载出错:', error); - } + // 第一阶段:立即加载核心个人信息,让页面内容快速显示 try { + this.loading = true; await this.getPersonalInfoData(); - console.log('✓ 个人信息加载完成'); + console.log('✓ 核心个人信息加载完成,页面可以显示'); } catch (error) { - console.error('个人信息加载出错:', error); + console.error('个人信息加载失败:', error); + // 即使个人信息加载失败,也要设置loading为false,避免页面一直loading + this.loading = false; } + + // 第二阶段:并行加载辅助数据(不阻塞页面显示) + this.loadSupportingDataInBackground(); + console.log('=== 学生信息页面初始化完成 ==='); }, @@ -1054,6 +1054,256 @@ export default { return getDictTagType(this.studentStatusOptions, this.form.studentStatus, fallbackMap); }, + // 后台并行加载辅助数据 + async loadSupportingDataInBackground() { + console.log('开始后台加载辅助数据...'); + + // 第一阶段:并行加载所有非核心数据,互不阻塞 + const loadPromises = [ + this.loadDictDataSafely(), + this.loadDormDataSafely(), + this.loadDormRoomAndBedDataSafely(), + ]; + + // 使用Promise.allSettled确保即使某些请求失败也不影响其他 + const results = await Promise.allSettled(loadPromises); + + results.forEach((result, index) => { + const taskNames = ['字典数据', '宿舍数据', '房间床位数据']; + if (result.status === 'fulfilled') { + console.log(`✓ ${taskNames[index]}后台加载完成`); + } else { + console.warn(`✗ ${taskNames[index]}后台加载失败:`, result.reason); + } + }); + + // 第二阶段:更新宿舍相关显示名称(不依赖区域数据) + try { + await this.updateDisplayNamesAfterBackgroundLoad(); + console.log('✓ 后台数据加载后显示名称更新完成'); + } catch (error) { + console.warn('后台数据加载后显示名称更新失败:', error); + } + + // 第三阶段:尝试加载区域数据并更新区域显示名称(可选,不阻塞整体完成) + this.loadRegionDataAndUpdateDisplay(); + + console.log('=== 后台辅助数据加载完成 ==='); + this.backgroundDataLoaded = true; + }, + + // 安全加载字典数据(出错不影响页面) + async loadDictDataSafely() { + try { + await this.loadDictData(); + } catch (error) { + console.warn('字典数据加载失败,使用默认值:', error); + // 确保字典数组不为undefined + this.ensureDictArraysExist(); + } + }, + + // 安全加载宿舍数据(出错不影响页面) + async loadDormDataSafely() { + try { + await this.loadDormBuildingData(); + } catch (error) { + console.warn('宿舍数据加载失败,使用默认值:', error); + this.dormBuildingOptions = []; + } + }, + + // 安全加载房间床位数据(出错不影响页面) + async loadDormRoomAndBedDataSafely() { + try { + await this.loadDormRoomAndBedData(); + } catch (error) { + console.warn('房间床位数据加载失败,使用默认值:', error); + this.dormRoomOptions = []; + this.dormBedOptions = []; + } + }, + + // 设置临时显示名称,避免页面空白 + setTemporaryDisplayNames() { + // 设置临时的区域显示名称 + if (this.form.nativePlace) { + this.nativePlaceDisplayName = `区域${this.form.nativePlace}`; + } + if (this.form.sourceRegion) { + this.sourceRegionDisplayName = `区域${this.form.sourceRegion}`; + } + + // 房间和床位显示名称暂时留空,将在后台数据加载完成后更新 + console.log('临时显示名称已设置'); + }, + + // 异步加载区域数据并更新显示名称(不阻塞主流程) + async loadRegionDataAndUpdateDisplay() { + try { + console.log('开始异步加载区域数据...'); + await this.loadRegionDataLazily(); + + // 区域数据加载完成后,更新区域显示名称 + if (this.form.nativePlace) { + const nativePlaceName = this.buildRegionPath(this.form.nativePlace); + if (nativePlaceName !== `区域${this.form.nativePlace}`) { + this.nativePlaceDisplayName = nativePlaceName; + console.log('✓ 异步更新籍贯名称:', nativePlaceName); + } + } + + if (this.form.sourceRegion) { + const sourceRegionName = this.buildRegionPath(this.form.sourceRegion); + if (sourceRegionName !== `区域${this.form.sourceRegion}`) { + this.sourceRegionDisplayName = sourceRegionName; + console.log('✓ 异步更新生源地区名称:', sourceRegionName); + } + } + + console.log('✓ 区域数据加载和显示名称更新完成'); + } catch (error) { + console.warn('区域数据加载失败,保持临时显示名称:', error); + } + }, + + // 后台数据加载完成后更新显示名称(仅处理宿舍相关) + async updateDisplayNamesAfterBackgroundLoad() { + const updatePromises = []; + + // 更新房间和床位名称(需要宿舍数据) + if (this.form.dormitoryRoom) { + updatePromises.push( + this.getRoomName(this.form.dormitoryRoom) + .then((name) => { + this.roomDisplayName = name; + console.log('✓ 后台更新房间名称:', name); + }) + .catch((error) => { + console.warn('后台更新房间名称失败:', error); + }), + ); + } + + if (this.form.extField1) { + updatePromises.push( + this.getBedName(this.form.extField1) + .then((name) => { + this.bedDisplayName = name; + console.log('✓ 后台更新床位名称:', name); + }) + .catch((error) => { + console.warn('后台更新床位名称失败:', error); + }), + ); + } + + // 等待所有宿舍相关更新完成 + await Promise.allSettled(updatePromises); + console.log('宿舍相关显示名称更新完成'); + }, + + // 确保字典数组存在,避免页面报错 + ensureDictArraysExist() { + const dictArrays = [ + 'genderOptions', + 'nationalityOptions', + 'politicalStatusOptions', + 'educationLevelOptions', + 'householdRegisterTypeOptions', + 'maritalStatusOptions', + 'yesNoOptions', + 'familyEconomicOptions', + 'bloodTypeOptions', + 'disabilityTypeOptions', + 'academicLevelOptions', + 'trainingModeOptions', + 'admissionCategoryOptions', + 'studentStatusOptions', + ]; + + dictArrays.forEach((arrayName) => { + if (!this[arrayName] || !Array.isArray(this[arrayName])) { + this[arrayName] = []; + } + }); + }, + + // 单独加载宿舍楼栋数据 + async loadDormBuildingData() { + console.log('开始加载宿舍楼栋数据...'); + try { + const dormRes = await request({ + url: '/xgss/dormBuildingTree/listAll', + method: 'GET', + }); + + if (dormRes && dormRes.data) { + this.dormBuildingOptions = this.processDormTreeData(dormRes.data); + console.log('宿舍楼栋数据处理完成,条目数:', this.dormBuildingOptions.length); + } else { + console.warn('宿舍楼栋数据加载失败或为空'); + this.dormBuildingOptions = []; + } + } catch (error) { + console.error('加载宿舍楼栋数据失败:', error); + this.dormBuildingOptions = []; + throw error; + } + }, + + // 懒加载行政区划数据(仅在需要时加载) + async loadRegionDataLazily() { + // 如果已经加载过,直接返回 + if (this.regionOptions && this.regionOptions.length > 0) { + return this.regionOptions; + } + + // 如果正在加载中,等待加载完成 + if (this.regionLoadingPromise) { + return await this.regionLoadingPromise; + } + + // 开始加载 + this.regionLoadingPromise = this.doLoadRegionData(); + + try { + await this.regionLoadingPromise; + return this.regionOptions; + } finally { + this.regionLoadingPromise = null; + } + }, + + // 实际执行行政区划数据加载 + async doLoadRegionData() { + console.log('懒加载行政区划数据...'); + try { + const regionRes = await request({ + url: '/system/SysRegion/tree', + method: 'GET', + }); + + if (regionRes && regionRes.data) { + this.regionOptions = regionRes.data; + console.log('行政区划数据懒加载完成,条目数:', regionRes.data.length); + } else { + console.warn('行政区划数据懒加载失败或为空'); + this.regionOptions = []; + } + } catch (error) { + console.error('懒加载行政区划数据失败:', error); + this.regionOptions = []; + // 显示用户友好的错误提示 + uni.showToast({ + title: '区域数据加载失败', + icon: 'none', + duration: 2000, + }); + throw error; + } + }, + // 加载字典数据 async loadDictData() { console.log('开始加载字典数据...'); @@ -1163,105 +1413,6 @@ export default { } }, - // 加载行政区划和宿舍楼栋数据 - async loadRegionAndDormData() { - console.log('开始加载区域和宿舍数据...'); - try { - console.log('发起并行请求: 行政区划 & 宿舍楼栋'); - const [regionRes, dormRes] = await Promise.all([ - request({ - url: '/system/SysRegion/tree', - method: 'GET', - }) - .then((res) => { - console.log('✓ 行政区划请求完成'); - return res; - }) - .catch((err) => { - console.error('✗ 行政区划请求失败:', err); - throw err; - }), - request({ - url: '/xgss/dormBuildingTree/listAll', - method: 'GET', - }) - .then((res) => { - console.log('✓ 宿舍楼栋请求完成'); - return res; - }) - .catch((err) => { - console.error('✗ 宿舍楼栋请求失败:', err); - throw err; - }), - ]); - - // 处理行政区划数据 - if (regionRes && regionRes.data) { - this.regionOptions = regionRes.data; - console.log('行政区划数据处理完成,条目数:', regionRes.data.length); - } else { - console.warn('行政区划数据加载失败或为空'); - } - - // 处理宿舍楼栋数据,添加性别标识 - if (dormRes && dormRes.data) { - console.log('dormRes', dormRes); - this.dormBuildingOptions = this.processDormTreeData(dormRes.data); - console.log('宿舍楼栋数据处理完成,条目数:', this.dormBuildingOptions.length); - } else { - console.warn('宿舍楼栋数据加载失败或为空'); - this.dormBuildingOptions = []; - } - } catch (error) { - console.error('加载区域和宿舍数据失败:', error); - uni.showToast({ - title: '加载基础数据失败,部分功能可能受影响', - icon: 'none', - duration: 3000, - }); - } - }, - - // 处理宿舍楼栋树形数据,为楼栋添加性别标识 - processDormTreeData(nodes) { - console.log('processDormTreeData - nodes', nodes); - if (!nodes || !Array.isArray(nodes)) { - console.warn('宿舍楼栋数据格式不正确'); - return []; - } - - return nodes.map((node) => { - const processedNode = { ...node }; - - // 如果是楼栋类型,添加性别标识 - if (node.type === 'building' && node.buildingType) { - console.log('node.buildingType', node.buildingType); - const genderSuffix = - node.buildingType === '1' - ? '(男)' - : node.buildingType === '2' - ? '(女)' - : ''; - - // 确保 label 和 name 存在后再添加后缀 - if (node.label) { - processedNode.label = node.label + genderSuffix; - } - if (node.name) { - processedNode.name = node.name + genderSuffix; - } - } - - // 递归处理子节点 - if (node.children && node.children.length > 0) { - processedNode.children = this.processDormTreeData(node.children); - } - console.log('processedNode', processedNode); - - return processedNode; - }); - }, - // 加载房间和床位数据(改为按需加载) async loadDormRoomAndBedData() { console.log('房间和床位数据改为按需加载模式,不再预加载所有数据'); @@ -1270,29 +1421,70 @@ export default { this.dormBedOptions = []; }, - // 异步加载房间和床位显示名称 - async loadRoomAndBedDisplayNames() { + // 异步加载各种显示名称(房间、床位、区域) + async loadDisplayNames() { try { - // 加载房间名称 + // 并行加载房间和床位名称 + const loadPromises = []; + if (this.form.dormitoryRoom) { - this.roomDisplayName = await this.getRoomName(this.form.dormitoryRoom); - console.log('房间名称加载完成:', this.roomDisplayName); + loadPromises.push( + this.getRoomName(this.form.dormitoryRoom).then((name) => { + this.roomDisplayName = name; + console.log('房间名称加载完成:', name); + }), + ); } - // 加载床位名称 if (this.form.extField1) { - this.bedDisplayName = await this.getBedName(this.form.extField1); - console.log('床位名称加载完成:', this.bedDisplayName); + loadPromises.push( + this.getBedName(this.form.extField1).then((name) => { + this.bedDisplayName = name; + console.log('床位名称加载完成:', name); + }), + ); } + + // 并行加载区域显示名称(不阻塞房间床位显示) + if (this.form.nativePlace) { + loadPromises.push( + this.getRegionFullPathAsync(this.form.nativePlace) + .then((name) => { + this.nativePlaceDisplayName = name; + console.log('籍贯显示名称加载完成:', name); + }) + .catch((error) => { + console.warn('籍贯显示名称加载失败:', error); + this.nativePlaceDisplayName = `区域${this.form.nativePlace}`; + }), + ); + } + + if (this.form.sourceRegion) { + loadPromises.push( + this.getRegionFullPathAsync(this.form.sourceRegion) + .then((name) => { + this.sourceRegionDisplayName = name; + console.log('生源地区显示名称加载完成:', name); + }) + .catch((error) => { + console.warn('生源地区显示名称加载失败:', error); + this.sourceRegionDisplayName = `区域${this.form.sourceRegion}`; + }), + ); + } + + // 等待所有显示名称加载完成(使用 allSettled 确保部分失败不影响其他) + await Promise.allSettled(loadPromises); + console.log('所有显示名称加载完成'); } catch (error) { - console.error('加载房间和床位显示名称失败:', error); + console.error('加载显示名称失败:', error); } }, // 获取学生个人信息 async getPersonalInfoData() { try { - this.loading = true; const response = await request({ url: '/studentMsg/studentMsg/personalInfo', method: 'GET', @@ -1331,8 +1523,10 @@ export default { extField1Type: typeof this.form.extField1, }); - // 异步加载房间和床位信息 - await this.loadRoomAndBedDisplayNames(); + // 立即设置临时显示名称,避免空白 + this.setTemporaryDisplayNames(); + + console.log('✓ 个人信息加载完成,页面可以显示'); } else { uni.showToast({ title: response.msg || '获取个人信息失败', @@ -1437,9 +1631,63 @@ export default { } }, - // 根据行政区划代码获取完整路径(省/市/区) - getRegionFullPath(code) { - if (!code || !this.regionOptions) return ''; + // 处理宿舍楼栋树形数据,为楼栋添加性别标识 + processDormTreeData(nodes) { + console.log('processDormTreeData - nodes', nodes); + if (!nodes || !Array.isArray(nodes)) { + console.warn('宿舍楼栋数据格式不正确'); + return []; + } + + return nodes.map((node) => { + const processedNode = { ...node }; + + // 如果是楼栋类型,添加性别标识 + if (node.type === 'building' && node.buildingType) { + console.log('node.buildingType', node.buildingType); + const genderSuffix = + node.buildingType === '1' + ? '(男)' + : node.buildingType === '2' + ? '(女)' + : ''; + + // 确保 label 和 name 存在后再添加后缀 + if (node.label) { + processedNode.label = node.label + genderSuffix; + } + if (node.name) { + processedNode.name = node.name + genderSuffix; + } + } + + // 递归处理子节点 + if (node.children && node.children.length > 0) { + processedNode.children = this.processDormTreeData(node.children); + } + console.log('processedNode', processedNode); + + return processedNode; + }); + }, + + // 异步获取区域完整路径(用于后台加载) + async getRegionFullPathAsync(code) { + if (!code) return ''; + + // 懒加载区域数据 + await this.loadRegionDataLazily(); + + // 获取完整路径 + return this.buildRegionPath(code); + }, + + // 构建区域路径(纯计算,不触发加载) + buildRegionPath(code) { + if (!code || !this.regionOptions || this.regionOptions.length === 0) { + return `区域${code}`; + } + const path = []; const findPath = (nodes, targetCode) => { for (const node of nodes) { @@ -1456,8 +1704,58 @@ export default { } return false; }; - findPath(this.regionOptions, code); - return path.join(' / '); + + if (findPath(this.regionOptions, code)) { + return path.join(' / '); + } + + return `区域${code}`; + }, + + // 根据行政区划代码获取完整路径(省/市/区)- 同步版本,用于模板(已弃用) + getRegionFullPath(code) { + if (!code) return ''; + + // 如果行政区划数据未加载,触发懒加载并返回临时显示 + if (!this.regionOptions || this.regionOptions.length === 0) { + // 异步加载区域数据,不阻塞当前显示 + this.loadRegionDataLazily() + .then(() => { + // 加载完成后触发页面更新 + this.$forceUpdate(); + }) + .catch(() => { + // 加载失败时显示代码 + console.warn('行政区划数据懒加载失败,显示原始代码'); + }); + + // 返回代码作为临时显示 + return `区域${code}`; + } + + const path = []; + const findPath = (nodes, targetCode) => { + for (const node of nodes) { + path.push(node.name); + if (node.code === targetCode) { + return true; + } + if (node.children && node.children.length > 0) { + if (findPath(node.children, targetCode)) { + return true; + } + } + path.pop(); + } + return false; + }; + + if (findPath(this.regionOptions, code)) { + return path.join(' / '); + } + + // 如果找不到,返回代码 + return `区域${code}`; }, // 根据楼栋ID获取楼栋名称(含性别标识) @@ -1726,6 +2024,21 @@ export default { min-height: 100vh; } +.background-loading-tip { + display: flex; + align-items: center; + justify-content: center; + padding: 16rpx 30rpx; + background-color: #f0f9ff; + border-bottom: 1px solid #e1ecf4; + gap: 8rpx; +} + +.tip-text { + font-size: 24rpx; + color: #606266; +} + .content-wrapper { padding: 20rpx; padding-bottom: 40rpx; From 0479c40907c124fc67e1179c066bfe81d3d063ac Mon Sep 17 00:00:00 2001 From: zlwu <704541015@qq.com> Date: Tue, 26 Aug 2025 16:04:50 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/views/hq/bgtgl/hallInfo/index.vue | 10 ++++++- web/src/views/index.vue | 33 +++++++++++++++++------ 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/web/src/views/hq/bgtgl/hallInfo/index.vue b/web/src/views/hq/bgtgl/hallInfo/index.vue index 10dbf320..51060dd5 100644 --- a/web/src/views/hq/bgtgl/hallInfo/index.vue +++ b/web/src/views/hq/bgtgl/hallInfo/index.vue @@ -58,7 +58,7 @@