xsh_1997 1 week geleden
bovenliggende
commit
61c661731c

+ 113 - 0
doc/产业数据模型及服务/三资具体问题上报/三资具体问题上报前端技术方案.md

@@ -0,0 +1,113 @@
1
+# 三资具体问题上报 — 前端技术方案
2
+
3
+> 依据:同目录功能需求、技术方案;普通 CRUD;**无**导入导出;字段 **小驼峰**。
4
+
5
+---
6
+
7
+## 1. 页面与路由
8
+
9
+| 项 | 说明 |
10
+| --- | --- |
11
+| **Vue 路径** | `ruoyi-ui/src/views/dataModel/threeAssetsProblemReport/index.vue` |
12
+| **组件名** | `ThreeAssetsProblemReport` |
13
+| **菜单配置** | 组件路径:`dataModel/threeAssetsProblemReport/index` |
14
+| **权限前缀** | `dataModel:threeAssetsProblemReport:list\|query\|add\|edit\|remove` |
15
+
16
+---
17
+
18
+## 2. API 模块
19
+
20
+| 项 | 说明 |
21
+| --- | --- |
22
+| **文件** | `ruoyi-ui/src/api/dataModel/threeAssetsProblemReport.js` |
23
+| **Base** | `/dataModel/threeAssetsProblemReport` |
24
+
25
+| 方法 | 路径 | 说明 |
26
+| --- | --- | --- |
27
+| `listThreeAssetsProblemReport` | GET `/list` | 分页;筛选上报时间区间、乡镇、村、问题类型 |
28
+| `getThreeAssetsProblemReport` | GET `/{id}` | 详情 |
29
+| `addThreeAssetsProblemReport` | POST `/` | 新增 |
30
+| `updateThreeAssetsProblemReport` | PUT `/` | 修改 |
31
+| `delThreeAssetsProblemReport` | DELETE `/{ids}` | 删除 |
32
+| `listThreeAssetsProblemReportTownOptions` | GET `/townOptions` | 乡镇下拉 |
33
+| `listThreeAssetsProblemReportVillageTree` | GET `/villageTree` | 县→乡镇→村树 |
34
+
35
+本期 **无** 导入 / 导出。
36
+
37
+---
38
+
39
+## 3. 页面结构
40
+
41
+1. **筛选**:上报时间(日期区间)、乡镇、村(可搜索)、问题类型。  
42
+2. **列表**:上报时间、乡镇、村、问题类型、问题等级、问题详细描述、操作。  
43
+3. **新增/编辑**:上报时间、所属村、问题类型、问题等级、问题详细描述、处理结果(选填);乡镇由后端按村反推,表单不选。  
44
+4. **详情**:含乡镇;处理结果一并展示。
45
+
46
+### 表单 / 查看分区
47
+
48
+| 分区 | 内容 |
49
+| --- | --- |
50
+| 基本信息 | 上报时间、村(查看含乡镇)、问题类型、问题等级、问题详细描述、处理结果 |
51
+
52
+### 字段映射
53
+
54
+| 界面 | 字段 | 说明 |
55
+| --- | --- | --- |
56
+| 上报时间 | `reportTime` | `yyyy-MM-dd HH:mm:ss`;新增默认当前时间 |
57
+| 所属村 | `villageDeptId` | 提交只传村;乡镇后端反推落库 |
58
+| 所属乡镇 | `townName` / `townDeptId` | 列表/详情展示;筛选可传 `townDeptId` |
59
+| 问题类型 | `problemType` | `fund` / `asset` / `resource` / `other` |
60
+| 问题等级 | `problemLevel` | `general` / `key` / `major` |
61
+| 问题详细描述 | `problemDetail` | 必填,≤2000 |
62
+| 处理结果 | `handleResult` | 选填,≤2000;不进列表列 |
63
+
64
+列表查询时间区间:`beginReportTime` / `endReportTime`(`yyyy-MM-dd`)。
65
+
66
+---
67
+
68
+## 4. 业务规则
69
+
70
+| 规则 | 实现 |
71
+| --- | --- |
72
+| CRUD | 列表 / 查看 / 新增 / 编辑 / 删除;无导入导出入口 |
73
+| 上报时间默认 | 新增打开表单时前端填当前时间;后端空则补 `now` |
74
+| 乡镇 | 提交只传 `villageDeptId`;乡镇由后端 `TownDeptSupport` 反推 |
75
+| 必填 | 上报时间、村、问题类型、问题等级、问题详细描述 |
76
+| 长度 | 描述 / 处理结果 ≤2000 |
77
+| 无唯一约束 | 同一村可多次上报 |
78
+| 数据权限 | 乡镇/村下拉与列表沿用后端填报部门数据范围 |
79
+
80
+---
81
+
82
+## 5. 国际化
83
+
84
+`dataModel.threeAssetsProblemReport`(`lang/zh|bo/dataModel.js`);页面 `dmNs = 'threeAssetsProblemReport'`,复用 `dataModelLocaleMixin`。查看表单使用 `dm-view-form`。
85
+
86
+枚举文案键:`problemType_fund|asset|resource|other`、`problemLevel_general|key|major`。
87
+
88
+---
89
+
90
+## 6. 联调说明
91
+
92
+1. 执行 `sql/biz_three_assets_problem_report.sql`(若尚未执行)。  
93
+2. 菜单组件 `dataModel/threeAssetsProblemReport/index`,权限见 §1 / 后端技术方案菜单示例。  
94
+3. 验证:新增默认上报时间、按村反推乡镇、类型/等级枚举、描述长度、时间区间筛选、无导入导出入口。
95
+
96
+---
97
+
98
+## 7. 文件清单
99
+
100
+| 类型 | 路径 |
101
+| --- | --- |
102
+| 页面 | `ruoyi-ui/src/views/dataModel/threeAssetsProblemReport/index.vue` |
103
+| API | `ruoyi-ui/src/api/dataModel/threeAssetsProblemReport.js` |
104
+| i18n | `lang/zh/dataModel.js`、`lang/bo/dataModel.js` → `threeAssetsProblemReport` |
105
+| 前端技术方案 | 本文档 |
106
+
107
+---
108
+
109
+## 8. 修订记录
110
+
111
+| 版本 | 说明 |
112
+| --- | --- |
113
+| 1.0 | 初版:普通 CRUD;上报时间默认;村反推乡镇;无导入导出 |

+ 60 - 0
ruoyi-ui/src/api/dataModel/threeAssetsProblemReport.js

@@ -0,0 +1,60 @@
1
+import request from "@/utils/request"
2
+
3
+/** 分页列表 */
4
+export function listThreeAssetsProblemReport(query) {
5
+  return request({
6
+    url: "/dataModel/threeAssetsProblemReport/list",
7
+    method: "get",
8
+    params: query
9
+  })
10
+}
11
+
12
+/** 详情 */
13
+export function getThreeAssetsProblemReport(id) {
14
+  return request({
15
+    url: "/dataModel/threeAssetsProblemReport/" + id,
16
+    method: "get"
17
+  })
18
+}
19
+
20
+/** 新增 */
21
+export function addThreeAssetsProblemReport(data) {
22
+  return request({
23
+    url: "/dataModel/threeAssetsProblemReport",
24
+    method: "post",
25
+    data
26
+  })
27
+}
28
+
29
+/** 修改 */
30
+export function updateThreeAssetsProblemReport(data) {
31
+  return request({
32
+    url: "/dataModel/threeAssetsProblemReport",
33
+    method: "put",
34
+    data
35
+  })
36
+}
37
+
38
+/** 删除 */
39
+export function delThreeAssetsProblemReport(ids) {
40
+  return request({
41
+    url: "/dataModel/threeAssetsProblemReport/" + ids,
42
+    method: "delete"
43
+  })
44
+}
45
+
46
+/** 乡镇下拉 */
47
+export function listThreeAssetsProblemReportTownOptions() {
48
+  return request({
49
+    url: "/dataModel/threeAssetsProblemReport/townOptions",
50
+    method: "get"
51
+  })
52
+}
53
+
54
+/** 县→乡镇→村树 */
55
+export function listThreeAssetsProblemReportVillageTree() {
56
+  return request({
57
+    url: "/dataModel/threeAssetsProblemReport/villageTree",
58
+    method: "get"
59
+  })
60
+}

+ 44 - 0
ruoyi-ui/src/lang/bo/dataModel.js

@@ -1153,5 +1153,49 @@ export default {
1153 1153
     ruleVillageRequired: "གྲོང་ཚོ་འདེམས་རོགས།",
1154 1154
     ruleMoneyNonNegative: "0་ཡན་གྱི་གྲངས་དགོས།",
1155 1155
     villageTreeEmpty: "གྲོང་ཚོའི་སྒྲིག་འཛུགས་མ་ལོན,ལས་ཁུངས་ནང་「ཤང་གྲོང→གྲོང་ཚོ」ཡོད་མེད་ཞིབ་རོགས།"
1156
+  },
1157
+  threeAssetsProblemReport: {
1158
+    queryReportTime: "སྙན་ཞུ་དུས།",
1159
+    queryDateRangePh: "འགོ་ཚེས།",
1160
+    queryDateRangeEndPh: "མཇུག་ཚེས།",
1161
+    queryTown: "ཤང་གྲོང།",
1162
+    queryVillage: "གྲོང་ཚོ།",
1163
+    queryProblemType: "གནད་དོན་རིགས།",
1164
+    phVillageSearch: "འདེམས་པའམ་འཚོལ་འཇུག་རོགས།",
1165
+    phProblemDetail: "གནད་དོན་ཞིབ་བརྗོད་འབྲི་རོགས,ཡིག་ཆ་2000ལས་མི་ཆོག",
1166
+    phHandleResult: "འདེམས་འབྲི,ཡིག་ཆ་2000ལས་མི་ཆོག",
1167
+    colReportTime: "སྙན་ཞུ་དུས།",
1168
+    colTownName: "ཤང་གྲོང།",
1169
+    colVillageName: "གྲོང་ཚོ།",
1170
+    colProblemType: "གནད་དོན་རིགས།",
1171
+    colProblemLevel: "གནད་དོན་རིམ་པ།",
1172
+    colProblemDetail: "གནད་དོན་ཞིབ་བརྗོད།",
1173
+    formReportTime: "སྙན་ཞུ་དུས།",
1174
+    formTown: "ཤང་གྲོང།",
1175
+    formVillage: "གྲོང་ཚོ།",
1176
+    formProblemType: "གནད་དོན་རིགས།",
1177
+    formProblemLevel: "གནད་དོན་རིམ་པ།",
1178
+    formProblemDetail: "གནད་དོན་ཞིབ་བརྗོད།",
1179
+    formHandleResult: "ཐག་གཅོད་མཇུག་འབྲས།",
1180
+    sectionBasic: "གཞི་རྩའི་གནས་ཚུལ།",
1181
+    problemType_fund: "མ་དངུལ་གནད་དོན།",
1182
+    problemType_asset: "རྒྱུ་ནོར་གནད་དོན།",
1183
+    problemType_resource: "ཐོན་ཁུངས་གནད་དོན།",
1184
+    problemType_other: "གཞན།",
1185
+    problemLevel_general: "སྤྱིར་བཏང།",
1186
+    problemLevel_key: "གལ་ཆེ།",
1187
+    problemLevel_major: "ཧ་ཅང་གལ་ཆེ།",
1188
+    dialogAdd: "[གསར་སྣོན]རྒྱུ་ནོར་གསུམ་གནད་དོན་སྙན་ཞུ།",
1189
+    dialogEdit: "[རྩོམ་སྒྲིག]རྒྱུ་ནོར་གསུམ་གནད་དོན་སྙན་ཞུ།",
1190
+    confirmDelete: "ཐོ་གཞུང་འདི་སུབ་རྒྱུ་གཏན་ཁེལ་ལམ?",
1191
+    emptyList: "སྙན་ཞུ་གཞི་གྲངས་མེད།",
1192
+    ruleReportTimeRequired: "སྙན་ཞུ་དུས་འདེམས་རོགས།",
1193
+    ruleVillageRequired: "གྲོང་ཚོ་འདེམས་རོགས།",
1194
+    ruleProblemTypeRequired: "གནད་དོན་རིགས་འདེམས་རོགས།",
1195
+    ruleProblemLevelRequired: "གནད་དོན་རིམ་པ་འདེམས་རོགས།",
1196
+    ruleProblemDetailRequired: "གནད་དོན་ཞིབ་བརྗོད་འབྲི་རོགས།",
1197
+    ruleProblemDetailLen: "གནད་དོན་ཞིབ་བརྗོད་ཡིག་ཆ་2000ལས་མི་ཆོག",
1198
+    ruleHandleResultLen: "ཐག་གཅོད་མཇུག་འབྲས་ཡིག་ཆ་2000ལས་མི་ཆོག",
1199
+    villageTreeEmpty: "གྲོང་ཚོའི་སྒྲིག་འཛུགས་མ་ལོན,ལས་ཁུངས་ནང་「ཤང་གྲོང→གྲོང་ཚོ」ཡོད་མེད་ཞིབ་རོགས།"
1156 1200
   }
1157 1201
 }

+ 44 - 0
ruoyi-ui/src/lang/zh/dataModel.js

@@ -1153,5 +1153,49 @@ export default {
1153 1153
     ruleVillageRequired: "请选择所属村",
1154 1154
     ruleMoneyNonNegative: "须为大于等于 0 的数字",
1155 1155
     villageTreeEmpty: "未加载到村级组织数据,请确认系统部门已维护「乡镇→村」"
1156
+  },
1157
+  threeAssetsProblemReport: {
1158
+    queryReportTime: "上报时间",
1159
+    queryDateRangePh: "开始日期",
1160
+    queryDateRangeEndPh: "结束日期",
1161
+    queryTown: "所属乡镇",
1162
+    queryVillage: "所属村",
1163
+    queryProblemType: "问题类型",
1164
+    phVillageSearch: "请选择或输入搜索",
1165
+    phProblemDetail: "请输入问题详细描述,不超过 2000 字",
1166
+    phHandleResult: "选填,不超过 2000 字",
1167
+    colReportTime: "上报时间",
1168
+    colTownName: "所属乡镇",
1169
+    colVillageName: "所属村",
1170
+    colProblemType: "问题类型",
1171
+    colProblemLevel: "问题等级",
1172
+    colProblemDetail: "问题详细描述",
1173
+    formReportTime: "上报时间",
1174
+    formTown: "所属乡镇",
1175
+    formVillage: "所属村",
1176
+    formProblemType: "问题类型",
1177
+    formProblemLevel: "问题等级",
1178
+    formProblemDetail: "问题详细描述",
1179
+    formHandleResult: "处理结果",
1180
+    sectionBasic: "基本信息",
1181
+    problemType_fund: "资金问题",
1182
+    problemType_asset: "资产问题",
1183
+    problemType_resource: "资源问题",
1184
+    problemType_other: "其他",
1185
+    problemLevel_general: "一般",
1186
+    problemLevel_key: "重点",
1187
+    problemLevel_major: "重大",
1188
+    dialogAdd: "[新增]三资具体问题上报",
1189
+    dialogEdit: "[编辑]三资具体问题上报",
1190
+    confirmDelete: "是否确认删除这条数据?",
1191
+    emptyList: "暂无上报数据",
1192
+    ruleReportTimeRequired: "请选择上报时间",
1193
+    ruleVillageRequired: "请选择所属村",
1194
+    ruleProblemTypeRequired: "请选择问题类型",
1195
+    ruleProblemLevelRequired: "请选择问题等级",
1196
+    ruleProblemDetailRequired: "请填写问题详细描述",
1197
+    ruleProblemDetailLen: "问题详细描述不能超过 2000 字",
1198
+    ruleHandleResultLen: "处理结果不能超过 2000 字",
1199
+    villageTreeEmpty: "未加载到村级组织数据,请确认系统部门已维护「乡镇→村」"
1156 1200
   }
1157 1201
 }

+ 516 - 0
ruoyi-ui/src/views/dataModel/threeAssetsProblemReport/index.vue

@@ -0,0 +1,516 @@
1
+<template>
2
+  <div class="app-container">
3
+    <el-card shadow="never">
4
+      <el-form ref="queryForm" :model="queryParams" size="small" :inline="true">
5
+        <el-form-item>
6
+          <template slot="label">{{ dmT("queryReportTime") }}</template>
7
+          <el-date-picker
8
+            v-model="queryDateRange"
9
+            type="daterange"
10
+            value-format="yyyy-MM-dd"
11
+            range-separator="-"
12
+            :start-placeholder="dmT('queryDateRangePh')"
13
+            :end-placeholder="dmT('queryDateRangeEndPh')"
14
+            clearable
15
+            style="width: 240px"
16
+          />
17
+        </el-form-item>
18
+        <el-form-item prop="townDeptId">
19
+          <template slot="label">{{ dmT("queryTown") }}</template>
20
+          <el-select v-model="queryParams.townDeptId" :placeholder="dmCommon('pleaseSelect')" clearable filterable style="width: 180px">
21
+            <el-option v-for="item in townOptions" :key="item.deptId" :label="item.deptName" :value="item.deptId" />
22
+          </el-select>
23
+        </el-form-item>
24
+        <el-form-item prop="villageDeptId">
25
+          <template slot="label">{{ dmT("queryVillage") }}</template>
26
+          <el-select
27
+            v-model="queryParams.villageDeptId"
28
+            :placeholder="dmT('phVillageSearch')"
29
+            clearable
30
+            filterable
31
+            style="width: 220px"
32
+          >
33
+            <el-option
34
+              v-for="item in villageOptions"
35
+              :key="'q-v-' + item.deptId"
36
+              :label="item.label"
37
+              :value="item.deptId"
38
+            />
39
+          </el-select>
40
+        </el-form-item>
41
+        <el-form-item prop="problemType">
42
+          <template slot="label">{{ dmT("queryProblemType") }}</template>
43
+          <el-select v-model="queryParams.problemType" :placeholder="dmCommon('pleaseSelect')" clearable style="width: 140px">
44
+            <el-option v-for="item in problemTypeOptions" :key="'q-t-' + item.value" :label="item.label" :value="item.value" />
45
+          </el-select>
46
+        </el-form-item>
47
+        <el-form-item>
48
+          <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">{{ dmCommon("search") }}</el-button>
49
+          <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">{{ dmCommon("reset") }}</el-button>
50
+          <el-button
51
+            v-hasPermi="['dataModel:threeAssetsProblemReport:add']"
52
+            type="primary"
53
+            plain
54
+            icon="el-icon-plus"
55
+            size="mini"
56
+            @click="handleAdd"
57
+          >{{ dmCommon("add") }}</el-button>
58
+        </el-form-item>
59
+      </el-form>
60
+    </el-card>
61
+
62
+    <br />
63
+
64
+    <el-card shadow="never">
65
+      <el-table v-loading="loading" :data="tableList" border>
66
+        <template slot="empty">
67
+          <span>{{ dmT("emptyList") }}</span>
68
+        </template>
69
+        <el-table-column :label="dmT('colReportTime')" align="center" min-width="160">
70
+          <template slot-scope="scope">{{ formatReportTime(scope.row.reportTime) }}</template>
71
+        </el-table-column>
72
+        <el-table-column :label="dmT('colTownName')" prop="townName" align="center" min-width="110" :show-overflow-tooltip="true" />
73
+        <el-table-column :label="dmT('colVillageName')" prop="villageName" align="center" min-width="110" :show-overflow-tooltip="true" />
74
+        <el-table-column :label="dmT('colProblemType')" align="center" min-width="110">
75
+          <template slot-scope="scope">{{ problemTypeLabel(scope.row.problemType) }}</template>
76
+        </el-table-column>
77
+        <el-table-column :label="dmT('colProblemLevel')" align="center" width="100">
78
+          <template slot-scope="scope">{{ problemLevelLabel(scope.row.problemLevel) }}</template>
79
+        </el-table-column>
80
+        <el-table-column :label="dmT('colProblemDetail')" prop="problemDetail" align="center" min-width="180" :show-overflow-tooltip="true" />
81
+        <el-table-column :label="dmCommon('colOp')" align="center" class-name="small-padding fixed-width" width="180" fixed="right">
82
+          <template slot-scope="scope">
83
+            <el-button v-hasPermi="['dataModel:threeAssetsProblemReport:query']" type="text" size="mini" @click="handleView(scope.row)">{{ dmCommon("view") }}</el-button>
84
+            <el-button v-hasPermi="['dataModel:threeAssetsProblemReport:edit']" type="text" size="mini" @click="handleEdit(scope.row)">{{ dmCommon("edit") }}</el-button>
85
+            <el-button v-hasPermi="['dataModel:threeAssetsProblemReport:remove']" type="text" size="mini" @click="handleDelete(scope.row)">{{ dmCommon("delete") }}</el-button>
86
+          </template>
87
+        </el-table-column>
88
+      </el-table>
89
+      <pagination
90
+        v-show="total > 0"
91
+        :total="total"
92
+        :page.sync="queryParams.pageNum"
93
+        :limit.sync="queryParams.pageSize"
94
+        @pagination="getList"
95
+      />
96
+    </el-card>
97
+
98
+    <el-dialog :title="dialogTitle" :visible.sync="open" width="720px" append-to-body @close="cancel">
99
+      <el-form :key="formKey" ref="form" :model="form" class="tapr-form" label-width="140px" size="small">
100
+        <div class="tapr-section-title">{{ dmT("sectionBasic") }}</div>
101
+        <el-form-item prop="reportTime" :rules="formRules.reportTime">
102
+          <template slot="label">{{ dmT("formReportTime") }}</template>
103
+          <el-date-picker
104
+            v-model="form.reportTime"
105
+            type="datetime"
106
+            value-format="yyyy-MM-dd HH:mm:ss"
107
+            :placeholder="dmCommon('pleaseSelect')"
108
+            style="width: 100%"
109
+          />
110
+        </el-form-item>
111
+        <el-form-item prop="villageDeptId" :rules="formRules.villageDeptId">
112
+          <template slot="label">{{ dmT("formVillage") }}</template>
113
+          <el-select
114
+            v-model="form.villageDeptId"
115
+            :placeholder="dmT('phVillageSearch')"
116
+            filterable
117
+            clearable
118
+            style="width: 100%"
119
+          >
120
+            <el-option
121
+              v-for="item in villageOptions"
122
+              :key="'f-v-' + item.deptId"
123
+              :label="item.label"
124
+              :value="item.deptId"
125
+            />
126
+          </el-select>
127
+        </el-form-item>
128
+        <el-form-item prop="problemType" :rules="formRules.problemType">
129
+          <template slot="label">{{ dmT("formProblemType") }}</template>
130
+          <el-select v-model="form.problemType" :placeholder="dmCommon('pleaseSelect')" style="width: 100%">
131
+            <el-option v-for="item in problemTypeOptions" :key="'f-t-' + item.value" :label="item.label" :value="item.value" />
132
+          </el-select>
133
+        </el-form-item>
134
+        <el-form-item prop="problemLevel" :rules="formRules.problemLevel">
135
+          <template slot="label">{{ dmT("formProblemLevel") }}</template>
136
+          <el-radio-group v-model="form.problemLevel">
137
+            <el-radio v-for="item in problemLevelOptions" :key="'f-l-' + item.value" :label="item.value">{{ item.label }}</el-radio>
138
+          </el-radio-group>
139
+        </el-form-item>
140
+        <el-form-item prop="problemDetail" :rules="formRules.problemDetail">
141
+          <template slot="label">{{ dmT("formProblemDetail") }}</template>
142
+          <el-input
143
+            v-model="form.problemDetail"
144
+            type="textarea"
145
+            :rows="4"
146
+            :placeholder="dmT('phProblemDetail')"
147
+            maxlength="2000"
148
+            show-word-limit
149
+          />
150
+        </el-form-item>
151
+        <el-form-item prop="handleResult" :rules="formRules.handleResult">
152
+          <template slot="label">{{ dmT("formHandleResult") }}</template>
153
+          <el-input
154
+            v-model="form.handleResult"
155
+            type="textarea"
156
+            :rows="3"
157
+            :placeholder="dmT('phHandleResult')"
158
+            maxlength="2000"
159
+            show-word-limit
160
+          />
161
+        </el-form-item>
162
+      </el-form>
163
+      <div slot="footer" class="dialog-footer">
164
+        <el-button type="primary" @click="submitForm">{{ dmCommon("ok") }}</el-button>
165
+        <el-button @click="cancel">{{ dmCommon("cancel") }}</el-button>
166
+      </div>
167
+    </el-dialog>
168
+
169
+    <el-dialog :title="dmCommon('viewTitle')" :visible.sync="viewOpen" width="720px" append-to-body>
170
+      <el-form class="dm-view-form tapr-form" label-width="140px" size="small">
171
+        <div class="tapr-section-title">{{ dmT("sectionBasic") }}</div>
172
+        <el-form-item :label="dmT('formReportTime')">
173
+          <span>{{ formatReportTime(viewRow.reportTime) }}</span>
174
+        </el-form-item>
175
+        <el-form-item :label="dmT('formTown')">
176
+          <span>{{ viewRow.townName || dash }}</span>
177
+        </el-form-item>
178
+        <el-form-item :label="dmT('formVillage')">
179
+          <span>{{ viewRow.villageName || dash }}</span>
180
+        </el-form-item>
181
+        <el-form-item :label="dmT('formProblemType')">
182
+          <span>{{ problemTypeLabel(viewRow.problemType) }}</span>
183
+        </el-form-item>
184
+        <el-form-item :label="dmT('formProblemLevel')">
185
+          <span>{{ problemLevelLabel(viewRow.problemLevel) }}</span>
186
+        </el-form-item>
187
+        <el-form-item :label="dmT('formProblemDetail')">
188
+          <span class="tapr-view-text">{{ viewRow.problemDetail || dash }}</span>
189
+        </el-form-item>
190
+        <el-form-item :label="dmT('formHandleResult')">
191
+          <span class="tapr-view-text">{{ viewRow.handleResult || dash }}</span>
192
+        </el-form-item>
193
+      </el-form>
194
+      <div slot="footer" class="dialog-footer">
195
+        <el-button v-hasPermi="['dataModel:threeAssetsProblemReport:edit']" type="primary" @click="handleEditFromView">{{ dmCommon("edit") }}</el-button>
196
+        <el-button @click="viewOpen = false">{{ dmCommon("close") }}</el-button>
197
+      </div>
198
+    </el-dialog>
199
+  </div>
200
+</template>
201
+
202
+<script>
203
+import dataModelLocaleMixin from "@/mixins/dataModelLocaleMixin"
204
+import {
205
+  listThreeAssetsProblemReport,
206
+  getThreeAssetsProblemReport,
207
+  addThreeAssetsProblemReport,
208
+  updateThreeAssetsProblemReport,
209
+  delThreeAssetsProblemReport,
210
+  listThreeAssetsProblemReportTownOptions,
211
+  listThreeAssetsProblemReportVillageTree
212
+} from "@/api/dataModel/threeAssetsProblemReport"
213
+
214
+const PROBLEM_TYPE_VALUES = ["fund", "asset", "resource", "other"]
215
+const PROBLEM_LEVEL_VALUES = ["general", "key", "major"]
216
+
217
+function createEmptyForm() {
218
+  return {
219
+    id: undefined,
220
+    reportTime: undefined,
221
+    villageDeptId: undefined,
222
+    problemType: undefined,
223
+    problemLevel: undefined,
224
+    problemDetail: undefined,
225
+    handleResult: undefined
226
+  }
227
+}
228
+
229
+function normalizeForm(data) {
230
+  return Object.assign(createEmptyForm(), data || {})
231
+}
232
+
233
+function flattenVillageOptions(tree) {
234
+  const list = []
235
+  ;(tree || []).forEach((county) => {
236
+    ;(county.children || []).forEach((town) => {
237
+      ;(town.children || []).forEach((village) => {
238
+        if (village == null || village.id == null) {
239
+          return
240
+        }
241
+        const townName = town.label || ""
242
+        const villageName = village.label || ""
243
+        list.push({
244
+          deptId: village.id,
245
+          label: townName ? `${townName} / ${villageName}` : villageName
246
+        })
247
+      })
248
+    })
249
+  })
250
+  return list
251
+}
252
+
253
+export default {
254
+  name: "ThreeAssetsProblemReport",
255
+  mixins: [dataModelLocaleMixin],
256
+  data() {
257
+    return {
258
+      dmNs: "threeAssetsProblemReport",
259
+      loading: false,
260
+      open: false,
261
+      viewOpen: false,
262
+      dialogEdit: false,
263
+      formKey: 0,
264
+      total: 0,
265
+      tableList: [],
266
+      townOptions: [],
267
+      villageOptions: [],
268
+      queryDateRange: null,
269
+      viewRow: {},
270
+      queryParams: {
271
+        pageNum: 1,
272
+        pageSize: 20,
273
+        townDeptId: undefined,
274
+        villageDeptId: undefined,
275
+        problemType: undefined,
276
+        beginReportTime: undefined,
277
+        endReportTime: undefined
278
+      },
279
+      form: createEmptyForm()
280
+    }
281
+  },
282
+  computed: {
283
+    dash() {
284
+      return this.$t("dataModel.status.dash")
285
+    },
286
+    dialogTitle() {
287
+      return this.dialogEdit ? this.dmT("dialogEdit") : this.dmT("dialogAdd")
288
+    },
289
+    problemTypeOptions() {
290
+      return PROBLEM_TYPE_VALUES.map((value) => ({
291
+        value,
292
+        label: this.dmT("problemType_" + value)
293
+      }))
294
+    },
295
+    problemLevelOptions() {
296
+      return PROBLEM_LEVEL_VALUES.map((value) => ({
297
+        value,
298
+        label: this.dmT("problemLevel_" + value)
299
+      }))
300
+    },
301
+    formRules() {
302
+      return {
303
+        reportTime: [{ required: true, message: this.dmT("ruleReportTimeRequired"), trigger: "change" }],
304
+        villageDeptId: [{ required: true, message: this.dmT("ruleVillageRequired"), trigger: "change" }],
305
+        problemType: [{ required: true, message: this.dmT("ruleProblemTypeRequired"), trigger: "change" }],
306
+        problemLevel: [{ required: true, message: this.dmT("ruleProblemLevelRequired"), trigger: "change" }],
307
+        problemDetail: [
308
+          { required: true, message: this.dmT("ruleProblemDetailRequired"), trigger: "blur" },
309
+          { max: 2000, message: this.dmT("ruleProblemDetailLen"), trigger: "blur" }
310
+        ],
311
+        handleResult: [{ max: 2000, message: this.dmT("ruleHandleResultLen"), trigger: "blur" }]
312
+      }
313
+    }
314
+  },
315
+  created() {
316
+    this.loadOptions()
317
+    this.getList()
318
+  },
319
+  methods: {
320
+    loadOptions() {
321
+      listThreeAssetsProblemReportTownOptions().then((res) => {
322
+        this.townOptions = Array.isArray(res.data) ? res.data : []
323
+      })
324
+      this.loadVillageTree()
325
+    },
326
+    loadVillageTree() {
327
+      return listThreeAssetsProblemReportVillageTree()
328
+        .then((res) => {
329
+          const raw = res && res.data !== undefined ? res.data : res
330
+          this.villageOptions = flattenVillageOptions(Array.isArray(raw) ? raw : [])
331
+          if (!this.villageOptions.length) {
332
+            this.$modal.msgWarning(this.dmT("villageTreeEmpty"))
333
+          }
334
+        })
335
+        .catch(() => {
336
+          this.villageOptions = []
337
+        })
338
+    },
339
+    formatReportTime(val) {
340
+      if (!val) {
341
+        return this.dash
342
+      }
343
+      return this.parseTime(val) || this.dash
344
+    },
345
+    problemTypeLabel(val) {
346
+      if (!val) {
347
+        return this.dash
348
+      }
349
+      const item = this.problemTypeOptions.find((o) => o.value === val)
350
+      return item ? item.label : val
351
+    },
352
+    problemLevelLabel(val) {
353
+      if (!val) {
354
+        return this.dash
355
+      }
356
+      const item = this.problemLevelOptions.find((o) => o.value === val)
357
+      return item ? item.label : val
358
+    },
359
+    nowReportTime() {
360
+      return this.parseTime(new Date(), "{y}-{m}-{d} {h}:{i}:{s}")
361
+    },
362
+    buildListQuery() {
363
+      const q = {
364
+        pageNum: this.queryParams.pageNum,
365
+        pageSize: this.queryParams.pageSize
366
+      }
367
+      if (this.queryDateRange && this.queryDateRange.length === 2) {
368
+        q.beginReportTime = this.queryDateRange[0]
369
+        q.endReportTime = this.queryDateRange[1]
370
+      }
371
+      if (this.queryParams.townDeptId != null) {
372
+        q.townDeptId = this.queryParams.townDeptId
373
+      }
374
+      if (this.queryParams.villageDeptId != null) {
375
+        q.villageDeptId = this.queryParams.villageDeptId
376
+      }
377
+      if (this.queryParams.problemType) {
378
+        q.problemType = this.queryParams.problemType
379
+      }
380
+      return q
381
+    },
382
+    getList() {
383
+      this.loading = true
384
+      listThreeAssetsProblemReport(this.buildListQuery())
385
+        .then((res) => {
386
+          this.tableList = res.rows || []
387
+          this.total = res.total || 0
388
+        })
389
+        .finally(() => {
390
+          this.loading = false
391
+        })
392
+    },
393
+    handleQuery() {
394
+      this.queryParams.pageNum = 1
395
+      this.getList()
396
+    },
397
+    resetQuery() {
398
+      this.queryDateRange = null
399
+      this.queryParams = {
400
+        pageNum: 1,
401
+        pageSize: 20,
402
+        townDeptId: undefined,
403
+        villageDeptId: undefined,
404
+        problemType: undefined,
405
+        beginReportTime: undefined,
406
+        endReportTime: undefined
407
+      }
408
+      this.getList()
409
+    },
410
+    handleAdd() {
411
+      this.dialogEdit = false
412
+      this.formKey += 1
413
+      this.form = createEmptyForm()
414
+      this.form.reportTime = this.nowReportTime()
415
+      this.open = true
416
+      this.$nextTick(() => {
417
+        if (this.$refs.form) {
418
+          this.$refs.form.clearValidate()
419
+        }
420
+      })
421
+    },
422
+    handleEdit(row) {
423
+      getThreeAssetsProblemReport(row.id).then((res) => {
424
+        this.dialogEdit = true
425
+        this.formKey += 1
426
+        this.form = normalizeForm(res.data)
427
+        if (this.form.reportTime) {
428
+          this.form.reportTime = this.parseTime(this.form.reportTime, "{y}-{m}-{d} {h}:{i}:{s}")
429
+        }
430
+        this.open = true
431
+        this.viewOpen = false
432
+        this.$nextTick(() => {
433
+          if (this.$refs.form) {
434
+            this.$refs.form.clearValidate()
435
+          }
436
+        })
437
+      })
438
+    },
439
+    handleView(row) {
440
+      getThreeAssetsProblemReport(row.id).then((res) => {
441
+        this.viewRow = res.data || {}
442
+        this.viewOpen = true
443
+      })
444
+    },
445
+    handleEditFromView() {
446
+      if (!this.viewRow || this.viewRow.id == null) {
447
+        return
448
+      }
449
+      this.handleEdit(this.viewRow)
450
+    },
451
+    handleDelete(row) {
452
+      this.$modal
453
+        .confirm(this.dmT("confirmDelete"))
454
+        .then(() => delThreeAssetsProblemReport(row.id))
455
+        .then(() => {
456
+          this.$modal.msgSuccess(this.dmCommon("msgDelOk"))
457
+          this.getList()
458
+        })
459
+        .catch(() => {})
460
+    },
461
+    buildSubmitPayload() {
462
+      const detail = this.form.problemDetail != null ? String(this.form.problemDetail).trim() : ""
463
+      const handle = this.form.handleResult != null ? String(this.form.handleResult).trim() : ""
464
+      const payload = {
465
+        villageDeptId: this.form.villageDeptId,
466
+        reportTime: this.form.reportTime,
467
+        problemType: this.form.problemType,
468
+        problemLevel: this.form.problemLevel,
469
+        problemDetail: detail,
470
+        handleResult: handle !== "" ? handle : undefined
471
+      }
472
+      if (this.dialogEdit) {
473
+        payload.id = this.form.id
474
+      }
475
+      return payload
476
+    },
477
+    submitForm() {
478
+      this.$refs.form.validate((valid) => {
479
+        if (!valid) {
480
+          return
481
+        }
482
+        const payload = this.buildSubmitPayload()
483
+        if (this.dialogEdit) {
484
+          updateThreeAssetsProblemReport(payload).then(() => {
485
+            this.$modal.msgSuccess(this.dmCommon("msgModOk"))
486
+            this.open = false
487
+            this.getList()
488
+          })
489
+        } else {
490
+          addThreeAssetsProblemReport(payload).then(() => {
491
+            this.$modal.msgSuccess(this.dmCommon("msgAddOk"))
492
+            this.open = false
493
+            this.getList()
494
+          })
495
+        }
496
+      })
497
+    },
498
+    cancel() {
499
+      this.open = false
500
+      this.form = createEmptyForm()
501
+    }
502
+  }
503
+}
504
+</script>
505
+
506
+<style scoped>
507
+.tapr-section-title {
508
+  margin: 4px 0 12px;
509
+  font-weight: 600;
510
+  color: #303133;
511
+}
512
+.tapr-view-text {
513
+  white-space: pre-wrap;
514
+  word-break: break-all;
515
+}
516
+</style>