Commit 3cbb87547081deaf9f903201077ab1a9e5a32fc4
1 parent
b055b398
动态字典
Showing
6 changed files
with
671 additions
and
1036 deletions
common/config/env.js
| ... | ... | @@ -5,7 +5,7 @@ const env = { |
| 5 | 5 | // baseUrl: 'https://test.jichengshanshui.com.cn:28302', |
| 6 | 6 | // https://test.jichengshanshui.com.cn:28302 |
| 7 | 7 | |
| 8 | - baseUrl: 'http://192.168.1.111:48081', | |
| 8 | + baseUrl: 'https://test.jichengshanshui.com.cn:28302', | |
| 9 | 9 | |
| 10 | 10 | uploadUrl: 'https://test.jichengshanshui.com.cn:28302', |
| 11 | 11 | // baseApi:'admin-api', | ... | ... |
common/config/global.js
| ... | ... | @@ -25,6 +25,7 @@ export default { |
| 25 | 25 | moduleListKey:'jcss_module_list', |
| 26 | 26 | dictDataKey:'jcss_dict_data', |
| 27 | 27 | refreshTokenKey: 'jcss_user_refresh_token', // 新增:刷新令牌缓存key |
| 28 | + dictListKey: 'jcss_dict_list' // 新增这一行 | |
| 28 | 29 | }, |
| 29 | 30 | appName: 'JCSS管理系统', |
| 30 | 31 | tokenExpireTime: 7 * 24 * 60 * 60 * 1000 | ... | ... |
pages-sub/daily/quick-order/add-order.vue
| ... | ... | @@ -2,204 +2,88 @@ |
| 2 | 2 | <view class="page-container"> |
| 3 | 3 | <!-- 核心:将所有 up-form-item 包裹在同一个 up-form 内 --> |
| 4 | 4 | <view class="work-order-form-content commonPageLRpadding"> |
| 5 | - <up-form | |
| 6 | - label-position="left" | |
| 7 | - :model="workOrderForm" | |
| 8 | - ref="workOrderFormRef" | |
| 9 | - labelWidth="160rpx" | |
| 10 | - > | |
| 11 | - | |
| 12 | - <up-form-item | |
| 13 | - label="业务线" | |
| 14 | - prop="busiLineCn" | |
| 15 | - border-bottom | |
| 16 | - required | |
| 17 | - > | |
| 18 | - <up-radio-group | |
| 19 | - v-model="workOrderForm.busiLineCn" | |
| 20 | - placement="row" | |
| 21 | - @change="handleBusiLineChange" | |
| 22 | - > | |
| 23 | - <up-radio | |
| 24 | - v-for="item in busiLineOptions" | |
| 25 | - :key="item.name" | |
| 26 | - :label="item.name" | |
| 27 | - :name="item.name" | |
| 28 | - ></up-radio> | |
| 5 | + <up-form label-position="left" :model="workOrderForm" ref="workOrderFormRef" labelWidth="160rpx"> | |
| 6 | + <up-form-item label="业务线" prop="busiLine" border-bottom required> | |
| 7 | + <up-radio-group v-model="busiLineDisplay" placement="row" @change="handleBusiLineChange"> | |
| 8 | + <up-radio v-for="item in busiLineOptions" :key="item.value" :label="item.label" :name="item.label" | |
| 9 | + > </up-radio> | |
| 29 | 10 | </up-radio-group> |
| 30 | - | |
| 31 | 11 | </up-form-item> |
| 32 | 12 | <!-- 1. 工单位置(地图选择) --> |
| 33 | - <up-form-item | |
| 34 | - label="工单位置" | |
| 35 | - prop="workLocation" | |
| 36 | - border-bottom | |
| 37 | - required | |
| 38 | - @click="chooseWorkLocation(); hideKeyboard()" | |
| 39 | - > | |
| 40 | - <up-input | |
| 41 | - v-model="workOrderForm.workLocation" | |
| 42 | - border="none" | |
| 43 | - readonly | |
| 44 | - suffix-icon="map-fill" | |
| 45 | - placeholder="点击选择工单位置" | |
| 46 | - ></up-input> | |
| 13 | + <up-form-item label="工单位置" prop="workLocation" border-bottom required | |
| 14 | + @click="chooseWorkLocation(); hideKeyboard()"> | |
| 15 | + <up-input v-model="workOrderForm.workLocation" border="none" readonly suffix-icon="map-fill" | |
| 16 | + placeholder="点击选择工单位置"></up-input> | |
| 47 | 17 | </up-form-item> |
| 48 | - | |
| 49 | 18 | <!-- 2. 道路名称(下拉框:位置选择后才可点击) --> |
| 50 | - <up-form-item | |
| 51 | - label="道路名称" | |
| 52 | - prop="roadName" | |
| 53 | - border-bottom | |
| 54 | - required | |
| 55 | - @click="workOrderForm.workLocation ? (showRoadName = true, hideKeyboard()) : uni.showToast({title: '请先选择工单位置', icon: 'none'})" | |
| 56 | - > | |
| 57 | - <up-input | |
| 58 | - v-model="workOrderForm.roadName" | |
| 59 | - readonly | |
| 60 | - disabled-color="#ffffff" | |
| 61 | - placeholder="请先选择工单位置" | |
| 62 | - border="none" | |
| 63 | - ></up-input> | |
| 19 | + <up-form-item label="道路名称" prop="roadName" border-bottom required | |
| 20 | + @click="workOrderForm.workLocation ? (showRoadName = true, hideKeyboard()) : uni.showToast({ title: '请先选择工单位置', icon: 'none' })"> | |
| 21 | + <up-input v-model="workOrderForm.roadName" readonly disabled-color="#ffffff" placeholder="请先选择工单位置" | |
| 22 | + border="none"></up-input> | |
| 64 | 23 | <template #right> |
| 65 | - <up-icon name="arrow-right" size="16" ></up-icon> | |
| 24 | + <up-icon name="arrow-right" size="16"></up-icon> | |
| 66 | 25 | </template> |
| 67 | 26 | </up-form-item> |
| 68 | - | |
| 69 | 27 | <!-- 3. 问题类型(下拉框) --> |
| 70 | - <up-form-item | |
| 71 | - label="问题类型" | |
| 72 | - prop="orderName" | |
| 73 | - border-bottom | |
| 74 | - required | |
| 75 | - @click="showOrderName = true; hideKeyboard()" | |
| 76 | - > | |
| 77 | - <up-input | |
| 78 | - v-model="workOrderForm.orderName" | |
| 79 | - disabled | |
| 80 | - disabled-color="#ffffff" | |
| 81 | - placeholder="请选择问题类型" | |
| 82 | - border="none" | |
| 83 | - ></up-input> | |
| 28 | + <up-form-item label="问题类型" prop="orderName" border-bottom required | |
| 29 | + @click="showOrderName = true; hideKeyboard()"> | |
| 30 | + <up-input v-model="workOrderForm.orderName" disabled disabled-color="#ffffff" placeholder="请选择问题类型" | |
| 31 | + border="none"></up-input> | |
| 84 | 32 | <template #right> |
| 85 | 33 | <up-icon name="arrow-right" size="16"></up-icon> |
| 86 | 34 | </template> |
| 87 | 35 | </up-form-item> |
| 88 | - | |
| 89 | 36 | <!-- 4. 情况描述(文本域) --> |
| 90 | - <up-form-item | |
| 91 | - label="情况描述" | |
| 92 | - prop="problemDesc" | |
| 93 | - required | |
| 94 | - > | |
| 95 | - <up-textarea | |
| 96 | - placeholder="请输入情况描述(最多200字)" | |
| 97 | - v-model.trim="workOrderForm.problemDesc" | |
| 98 | - count | |
| 99 | - maxlength="200" | |
| 100 | - rows="4" | |
| 101 | - @blur="() => $refs.workOrderFormRef.validateField('problemDesc')" | |
| 102 | - ></up-textarea> | |
| 37 | + <up-form-item label="情况描述" prop="problemDesc" required> | |
| 38 | + <up-textarea placeholder="请输入情况描述(最多200字)" v-model.trim="workOrderForm.problemDesc" count maxlength="200" | |
| 39 | + rows="4" @blur="() => $refs.workOrderFormRef.validateField('problemDesc')"></up-textarea> | |
| 103 | 40 | </up-form-item> |
| 104 | - | |
| 105 | 41 | <!-- 问题照片 --> |
| 106 | 42 | <up-form-item label="问题照片" prop="problemImgs" required> |
| 107 | - <up-upload | |
| 108 | - :file-list="problemImgsList" | |
| 109 | - @after-read="(event) => uploadImgs(event, 'problemImgsList')" | |
| 110 | - @delete="(event) => deleteImg(event, 'problemImgsList')" | |
| 111 | - multiple | |
| 112 | - width="70" | |
| 113 | - height="70" | |
| 114 | - :max-count="3" | |
| 115 | - upload-text="选择问题照片" | |
| 116 | - ></up-upload> | |
| 43 | + <up-upload :file-list="problemImgsList" @after-read="(event) => uploadImgs(event, 'problemImgsList')" | |
| 44 | + @delete="(event) => deleteImg(event, 'problemImgsList')" multiple width="70" height="70" :max-count="3" | |
| 45 | + upload-text="选择问题照片"></up-upload> | |
| 117 | 46 | </up-form-item> |
| 118 | - | |
| 119 | 47 | <!-- 完成照片 --> |
| 120 | 48 | <up-form-item label="完成照片" prop="completeImgs" required class="mt-20"> |
| 121 | - <up-upload | |
| 122 | - :file-list="completeImgsList" | |
| 123 | - @after-read="(event) => uploadImgs(event, 'completeImgsList')" | |
| 124 | - @delete="(event) => deleteImg(event, 'completeImgsList')" | |
| 125 | - multiple | |
| 126 | - width="70" | |
| 127 | - height="70" | |
| 128 | - :max-count="3" | |
| 129 | - :sizeType="['compressed']" | |
| 130 | - upload-text="选择完成照片" | |
| 131 | - ></up-upload> | |
| 49 | + <up-upload :file-list="completeImgsList" @after-read="(event) => uploadImgs(event, 'completeImgsList')" | |
| 50 | + @delete="(event) => deleteImg(event, 'completeImgsList')" multiple width="70" height="70" :max-count="3" | |
| 51 | + :sizeType="['compressed']" upload-text="选择完成照片"></up-upload> | |
| 132 | 52 | </up-form-item> |
| 133 | - | |
| 134 | 53 | <!-- 处理结果(不必填) --> |
| 135 | 54 | <up-form-item label="处理结果" prop="handleResult" class="mt-20"> |
| 136 | - <up-textarea | |
| 137 | - placeholder="请输入处理结果描述(最多200字,选填)" | |
| 138 | - v-model.trim="workOrderForm.handleResult" | |
| 139 | - count | |
| 140 | - maxlength="200" | |
| 141 | - rows="4" | |
| 142 | - @blur="() => $refs.workOrderFormRef.validateField('handleResult')" | |
| 143 | - ></up-textarea> | |
| 55 | + <up-textarea placeholder="请输入处理结果描述(最多200字,选填)" v-model.trim="workOrderForm.handleResult" count | |
| 56 | + maxlength="200" rows="4" @blur="() => $refs.workOrderFormRef.validateField('handleResult')"></up-textarea> | |
| 144 | 57 | </up-form-item> |
| 145 | - | |
| 146 | 58 | </up-form> |
| 147 | 59 | </view> |
| 148 | 60 | <!-- 底部提交按钮(移除loading和disabled属性) --> |
| 149 | 61 | <view class="fixed-bottom-btn-wrap"> |
| 150 | - <up-button | |
| 151 | - type="primary" | |
| 152 | - size="large" | |
| 153 | - text="提交工单" | |
| 154 | - @click="submitWorkOrder" | |
| 155 | - ></up-button> | |
| 62 | + <up-button type="primary" size="large" text="提交工单" @click="submitWorkOrder"></up-button> | |
| 156 | 63 | </view> |
| 157 | - | |
| 158 | 64 | <!-- 道路名称下拉弹窗 --> |
| 159 | - <up-action-sheet | |
| 160 | - :show="showRoadName" | |
| 161 | - :actions="roadNameList" | |
| 162 | - title="请选择道路名称" | |
| 163 | - @close="showRoadName = false" | |
| 164 | - @select="handleRoadNameSelect" | |
| 165 | - ></up-action-sheet> | |
| 166 | - | |
| 65 | + <up-action-sheet :show="showRoadName" :actions="roadNameList" title="请选择道路名称" @close="showRoadName = false" | |
| 66 | + @select="handleRoadNameSelect"></up-action-sheet> | |
| 167 | 67 | <!-- 问题类型下拉弹窗 --> |
| 168 | - <up-action-sheet | |
| 169 | - :show="showOrderName" | |
| 170 | - :actions="orderNameList" | |
| 171 | - title="请选择问题类型" | |
| 172 | - @close="showOrderName = false" | |
| 173 | - @select="handleOrderNameSelect" | |
| 174 | - ></up-action-sheet> | |
| 68 | + <up-action-sheet :show="showOrderName" :actions="orderNameList" title="请选择问题类型" @close="showOrderName = false" | |
| 69 | + @select="handleOrderNameSelect"></up-action-sheet> | |
| 175 | 70 | </view> |
| 176 | 71 | </template> |
| 177 | - | |
| 178 | 72 | <script lang="ts"> |
| 179 | 73 | import { getRoadListByLatLng } from '@/api/common' |
| 180 | 74 | import { uploadImages } from '@/common/utils/upload'; |
| 181 | 75 | import { createQuick } from '@/api/quick-order/quick-order' |
| 182 | 76 | import { useUserStore } from '@/pinia/user'; |
| 183 | - | |
| 184 | 77 | export default { |
| 78 | + computed: { | |
| 79 | + busiLineOptions() { | |
| 80 | + const userStore = useUserStore(); | |
| 81 | + return userStore.dictList || [] | |
| 82 | + } | |
| 83 | + }, | |
| 185 | 84 | data() { |
| 186 | 85 | return { |
| 187 | - // 业务线相关 | |
| 188 | - busiLineMap: { | |
| 189 | - 'yl': '园林', | |
| 190 | - 'sz': '市政', | |
| 191 | - 'wy': '物业', | |
| 192 | - 'zx': '秩序管理', | |
| 193 | - 'hj': '环境卫生', | |
| 194 | - 'yw': '常规运维', | |
| 195 | - '园林': 'yl', | |
| 196 | - '市政': 'sz', | |
| 197 | - '物业': 'wy', | |
| 198 | - '秩序管理': 'zx', | |
| 199 | - '环境卫生': 'hj', | |
| 200 | - '常规运维' : 'yw' | |
| 201 | - }, | |
| 202 | - busiLineOptions: [], // 业务线选项列表 | |
| 86 | + busiLineDisplay: '', // 单选框展示用,存中文label | |
| 203 | 87 | // 问题照片列表 |
| 204 | 88 | problemImgsList: [], |
| 205 | 89 | // 完成照片列表 |
| ... | ... | @@ -212,7 +96,7 @@ export default { |
| 212 | 96 | orderNameList: [], |
| 213 | 97 | // 工单表单数据 |
| 214 | 98 | workOrderForm: { |
| 215 | - busiLineCn: '', // 业务线中文名称 | |
| 99 | + busiLine: '', // 业务线value yl / sz / wy | |
| 216 | 100 | roadId: 0, // 道路ID |
| 217 | 101 | roadName: '', // 道路名称 |
| 218 | 102 | workLocation: '', // 工单位置 |
| ... | ... | @@ -224,7 +108,7 @@ export default { |
| 224 | 108 | }, |
| 225 | 109 | // 表单校验规则 |
| 226 | 110 | workOrderFormRules: { |
| 227 | - busiLineCn: [ // 业务线必选校验 | |
| 111 | + busiLine: [ | |
| 228 | 112 | { type: 'string', required: true, message: '请选择业务线', trigger: ['change', 'blur'] } |
| 229 | 113 | ], |
| 230 | 114 | workLocation: [ |
| ... | ... | @@ -266,12 +150,11 @@ export default { |
| 266 | 150 | } |
| 267 | 151 | }, |
| 268 | 152 | onLoad() { |
| 269 | - // 初始化业务线选项 | |
| 270 | - this.initBusiLineOptions(); | |
| 271 | 153 | // 默认选中第一个业务线 |
| 272 | 154 | if (this.busiLineOptions && this.busiLineOptions.length > 0) { |
| 273 | - this.workOrderForm.busiLineCn = this.busiLineOptions[0].name; | |
| 274 | - console.log('默认选中业务线:', this.workOrderForm.busiLineCn); | |
| 155 | + this.workOrderForm.busiLine = this.busiLineOptions[0].value; | |
| 156 | + this.busiLineDisplay = this.busiLineOptions[0].label; | |
| 157 | + console.log('默认选中业务线value:', this.workOrderForm.busiLine); | |
| 275 | 158 | } |
| 276 | 159 | }, |
| 277 | 160 | onReady() { |
| ... | ... | @@ -284,25 +167,13 @@ export default { |
| 284 | 167 | console.log('问题类型列表:', this.orderNameList); |
| 285 | 168 | }, |
| 286 | 169 | methods: { |
| 287 | - // 初始化业务线选项 | |
| 288 | - initBusiLineOptions() { | |
| 289 | - // 获取pinia中的用户信息 | |
| 290 | - const userStore = useUserStore(); | |
| 291 | - if (userStore.userInfo?.user?.busiLine) { | |
| 292 | - const rawBusiLines = userStore.userInfo.user.busiLine.split(','); | |
| 293 | - // 生成中文名称列表 | |
| 294 | - this.busiLineOptions = rawBusiLines.map(item => ({ | |
| 295 | - name: this.busiLineMap[item.trim()] // name=中文名称(园林/市政/物业) | |
| 296 | - })); | |
| 297 | - } | |
| 298 | - console.log('业务线选项:', this.busiLineOptions); | |
| 299 | - }, | |
| 300 | - // 工具方法:通过中文名称获取对应的英文标识 | |
| 301 | - getBusiLineEnByCn(cnName) { | |
| 302 | - return this.busiLineMap[cnName] || ''; | |
| 303 | - }, | |
| 304 | 170 | // 业务线切换事件 |
| 305 | - handleBusiLineChange() { | |
| 171 | + handleBusiLineChange(selectedLabel) { | |
| 172 | + // 根据选中的中文label反向找到value,存入表单 | |
| 173 | + const selectedItem = this.busiLineOptions.find(item => item.label === selectedLabel) | |
| 174 | + if (selectedItem) { | |
| 175 | + this.workOrderForm.busiLine = selectedItem.value; | |
| 176 | + } | |
| 306 | 177 | // 清空道路信息 |
| 307 | 178 | this.workOrderForm.roadName = ''; |
| 308 | 179 | this.workOrderForm.roadId = 0; |
| ... | ... | @@ -314,20 +185,13 @@ export default { |
| 314 | 185 | }, |
| 315 | 186 | // 根据选中业务线请求道路列表 |
| 316 | 187 | async getRoadListByBusiLine() { |
| 317 | - if (!this.workOrderForm.lat || !this.workOrderForm.lon) { | |
| 318 | - return; | |
| 319 | - } | |
| 320 | - // 中文转英文标识 | |
| 321 | - const busiLineEn = this.getBusiLineEnByCn(this.workOrderForm.busiLineCn); | |
| 322 | - if (!busiLineEn) { | |
| 323 | - uni.showToast({ title: '业务线标识异常', icon: 'none' }); | |
| 188 | + if (!this.workOrderForm.lat || !this.workOrderForm.lon || !this.workOrderForm.busiLine) { | |
| 324 | 189 | return; |
| 325 | 190 | } |
| 326 | - | |
| 327 | 191 | try { |
| 328 | 192 | uni.showLoading({ title: '获取道路名称中...' }); |
| 329 | 193 | const roadRes = await getRoadListByLatLng({ |
| 330 | - busiLine: busiLineEn, // 传递业务线英文标识 | |
| 194 | + busiLine: this.workOrderForm.busiLine, // 直接传递value | |
| 331 | 195 | latitude: this.workOrderForm.lat, |
| 332 | 196 | longitude: this.workOrderForm.lon |
| 333 | 197 | }); |
| ... | ... | @@ -380,7 +244,6 @@ export default { |
| 380 | 244 | console.log('上传图片事件:', event, '类型:', type); |
| 381 | 245 | const fileList = Array.isArray(event.file) ? event.file : [event.file]; |
| 382 | 246 | const targetImgList = type === 'problemImgsList' ? this.problemImgsList : this.completeImgsList; |
| 383 | - | |
| 384 | 247 | const filePaths = fileList.map(item => item.url); |
| 385 | 248 | const tempItems = fileList.map(item => ({ |
| 386 | 249 | ...item, |
| ... | ... | @@ -389,14 +252,12 @@ export default { |
| 389 | 252 | })); |
| 390 | 253 | const startIndex = targetImgList.length; |
| 391 | 254 | targetImgList.push(...tempItems); |
| 392 | - | |
| 393 | 255 | try { |
| 394 | 256 | const uploadResultUrls = await uploadImages({ |
| 395 | 257 | filePaths: filePaths, |
| 396 | 258 | ignoreError: true |
| 397 | 259 | }); |
| 398 | 260 | console.log('上传成功的URL列表:', uploadResultUrls); |
| 399 | - | |
| 400 | 261 | uploadResultUrls.forEach((url, index) => { |
| 401 | 262 | if (targetImgList[startIndex + index]) { |
| 402 | 263 | targetImgList.splice(startIndex + index, 1, { |
| ... | ... | @@ -407,7 +268,6 @@ export default { |
| 407 | 268 | }); |
| 408 | 269 | } |
| 409 | 270 | }); |
| 410 | - | |
| 411 | 271 | if (uploadResultUrls.length < fileList.length) { |
| 412 | 272 | const failCount = fileList.length - uploadResultUrls.length; |
| 413 | 273 | for (let i = uploadResultUrls.length; i < fileList.length; i++) { |
| ... | ... | @@ -423,7 +283,6 @@ export default { |
| 423 | 283 | } else { |
| 424 | 284 | uni.showToast({ title: `成功上传${fileList.length}张图片`, icon: 'success' }); |
| 425 | 285 | } |
| 426 | - | |
| 427 | 286 | // 上传完成后重新校验图片字段 |
| 428 | 287 | if (type === 'problemImgsList') { |
| 429 | 288 | this.$refs.workOrderFormRef.validateField('problemImgs'); |
| ... | ... | @@ -431,7 +290,6 @@ export default { |
| 431 | 290 | this.$refs.workOrderFormRef.validateField('completeImgs'); |
| 432 | 291 | } |
| 433 | 292 | } catch (err) { |
| 434 | - console.error('图片上传失败:', err); | |
| 435 | 293 | for (let i = 0; i < fileList.length; i++) { |
| 436 | 294 | if (targetImgList[startIndex + i]) { |
| 437 | 295 | targetImgList.splice(startIndex + i, 1, { |
| ... | ... | @@ -442,7 +300,6 @@ export default { |
| 442 | 300 | } |
| 443 | 301 | } |
| 444 | 302 | uni.showToast({ title: '图片上传失败,请重试', icon: 'none' }); |
| 445 | - | |
| 446 | 303 | // 上传失败后重新校验图片字段 |
| 447 | 304 | if (type === 'problemImgsList') { |
| 448 | 305 | this.$refs.workOrderFormRef.validateField('problemImgs'); |
| ... | ... | @@ -461,14 +318,11 @@ export default { |
| 461 | 318 | that.workOrderForm.roadName = ''; |
| 462 | 319 | that.workOrderForm.roadId = 0; |
| 463 | 320 | that.roadNameList = []; |
| 464 | - | |
| 465 | 321 | that.workOrderForm.workLocation = res.name; |
| 466 | 322 | that.workOrderForm.lat = res.latitude; |
| 467 | 323 | that.workOrderForm.lon = res.longitude; |
| 468 | - | |
| 469 | 324 | that.$refs.workOrderFormRef.validateField('workLocation'); |
| 470 | 325 | that.$refs.workOrderFormRef.validateField('roadName'); |
| 471 | - | |
| 472 | 326 | // 调用封装的方法,自动携带业务线 |
| 473 | 327 | await that.getRoadListByBusiLine(); |
| 474 | 328 | }, |
| ... | ... | @@ -516,14 +370,12 @@ export default { |
| 516 | 370 | try { |
| 517 | 371 | // 先执行表单校验 |
| 518 | 372 | await this.$refs.workOrderFormRef.validate(); |
| 519 | - | |
| 520 | - // 中文转英文标识 | |
| 521 | - const busiLineEn = this.getBusiLineEnByCn(this.workOrderForm.busiLineCn); | |
| 522 | - if (!busiLineEn) { | |
| 373 | + if (!this.workOrderForm.busiLine) { | |
| 523 | 374 | uni.showToast({ title: '业务线选择异常,请重新选择', icon: 'none' }); |
| 524 | 375 | return; |
| 525 | 376 | } |
| 526 | - | |
| 377 | + // 根据value找到中文label | |
| 378 | + const sourceName = this.busiLineOptions.find(item => item.value === this.workOrderForm.busiLine)?.label || ''; | |
| 527 | 379 | const submitData = { |
| 528 | 380 | roadId: this.workOrderForm.roadId, |
| 529 | 381 | roadName: this.workOrderForm.roadName, |
| ... | ... | @@ -538,24 +390,20 @@ export default { |
| 538 | 390 | pressingType: 2, |
| 539 | 391 | orderName: this.workOrderForm.orderName, |
| 540 | 392 | sourceId: 1, |
| 541 | - sourceName: this.workOrderForm.busiLineCn, // 业务线中文名称 | |
| 393 | + sourceName: sourceName, // 业务线中文名称 | |
| 542 | 394 | thirdWorkNo: '', |
| 543 | - busiLine: busiLineEn // 业务线英文标识(yl/sz/wy) | |
| 395 | + busiLine: this.workOrderForm.busiLine // 业务线英文标识(yl/sz/wy) | |
| 544 | 396 | }; |
| 545 | - | |
| 546 | 397 | // 显示加载中 |
| 547 | 398 | uni.showLoading({ title: '提交中...' }); |
| 548 | - | |
| 549 | 399 | // 调用提交接口 |
| 550 | 400 | const res = await createQuick(submitData); |
| 551 | - | |
| 552 | 401 | uni.hideLoading(); |
| 553 | 402 | uni.showToast({ |
| 554 | 403 | title: '工单提交成功', |
| 555 | 404 | icon: 'success', |
| 556 | 405 | duration: 1000 |
| 557 | 406 | }); |
| 558 | - | |
| 559 | 407 | // 延迟跳转 |
| 560 | 408 | setTimeout(() => { |
| 561 | 409 | uni.reLaunch({ |
| ... | ... | @@ -565,7 +413,6 @@ export default { |
| 565 | 413 | } catch (error) { |
| 566 | 414 | // 隐藏加载框 |
| 567 | 415 | uni.hideLoading(); |
| 568 | - | |
| 569 | 416 | // 区分是表单校验失败还是接口调用失败 |
| 570 | 417 | if (!Array.isArray(error)) { |
| 571 | 418 | // 接口调用失败 |
| ... | ... | @@ -581,16 +428,14 @@ export default { |
| 581 | 428 | } |
| 582 | 429 | } |
| 583 | 430 | </script> |
| 584 | - | |
| 585 | 431 | <style lang="scss" scoped> |
| 586 | 432 | // 全局页面样式 |
| 587 | 433 | .page-container { |
| 588 | 434 | min-height: 100vh; |
| 589 | 435 | } |
| 590 | - | |
| 591 | 436 | // 工单表单内容容器 |
| 592 | 437 | .work-order-form-content { |
| 593 | 438 | background: #fff; |
| 594 | 439 | margin-bottom: 100rpx; |
| 595 | 440 | } |
| 596 | -</style> | |
| 597 | 441 | \ No newline at end of file |
| 442 | +</style> | ... | ... |
pages-sub/problem/regional-order-manage/add-order.vue
| 1 | 1 | <template> |
| 2 | 2 | <view class="page-container"> |
| 3 | 3 | <view class="work-order-form-content commonPageLRpadding"> |
| 4 | - <up-form | |
| 5 | - label-position="left" | |
| 6 | - :model="baseForm" | |
| 7 | - ref="workOrderFormRef" | |
| 8 | - labelWidth="200rpx" | |
| 9 | - > | |
| 4 | + <up-form label-position="left" :model="baseForm" ref="workOrderFormRef" labelWidth="200rpx"> | |
| 10 | 5 | <!-- 1. 工单位置(地图选择)【公共项】 --> |
| 11 | - <up-form-item | |
| 12 | - label="工单位置" | |
| 13 | - prop="workLocation" | |
| 14 | - border-bottom | |
| 15 | - required | |
| 16 | - @click="chooseWorkLocation(); hideKeyboard()" | |
| 17 | - > | |
| 18 | - <up-input | |
| 19 | - v-model="baseForm.workLocation" | |
| 20 | - border="none" | |
| 21 | - readonly | |
| 22 | - suffix-icon="map-fill" | |
| 23 | - placeholder="点击选择工单位置" | |
| 24 | - ></up-input> | |
| 6 | + <up-form-item label="工单位置" prop="workLocation" border-bottom required | |
| 7 | + @click="chooseWorkLocation(); hideKeyboard()"> | |
| 8 | + <up-input v-model="baseForm.workLocation" border="none" readonly suffix-icon="map-fill" | |
| 9 | + placeholder="点击选择工单位置"></up-input> | |
| 25 | 10 | </up-form-item> |
| 26 | - | |
| 27 | 11 | <!-- 2. 情况描述【公共项】 --> |
| 28 | - <up-form-item | |
| 29 | - label="情况描述" | |
| 30 | - prop="problemDesc" | |
| 31 | - required | |
| 32 | - border-bottom | |
| 33 | - > | |
| 34 | - <up-textarea | |
| 35 | - placeholder="请输入情况描述(最多200字)" | |
| 36 | - v-model.trim="baseForm.problemDesc" | |
| 37 | - count | |
| 38 | - maxlength="200" | |
| 39 | - rows="4" | |
| 40 | - @blur="() => workOrderFormRef.validateField('problemDesc')" | |
| 41 | - ></up-textarea> | |
| 12 | + <up-form-item label="情况描述" prop="problemDesc" required border-bottom> | |
| 13 | + <up-textarea placeholder="请输入情况描述(最多200字)" v-model.trim="baseForm.problemDesc" count maxlength="200" rows="4" | |
| 14 | + @blur="() => workOrderFormRef.validateField('problemDesc')"></up-textarea> | |
| 42 | 15 | </up-form-item> |
| 43 | - | |
| 44 | 16 | <!-- 3. 问题照片【公共项】 --> |
| 45 | - <up-form-item label="问题照片" prop="problemImgs" required > | |
| 46 | - <up-upload | |
| 47 | - :file-list="problemImgs.imgList.value || []" | |
| 48 | - @after-read="problemImgs.uploadImgs" | |
| 49 | - @delete="problemImgs.deleteImg" | |
| 50 | - multiple | |
| 51 | - :width="70" | |
| 52 | - :height="70" | |
| 53 | - :max-count="problemImgs.uploadConfig.maxCount" | |
| 54 | - :upload-text="problemImgs.uploadText" | |
| 55 | - :size-type="problemImgs.uploadConfig.sizeType" | |
| 56 | - ></up-upload> | |
| 17 | + <up-form-item label="问题照片" prop="problemImgs" required> | |
| 18 | + <up-upload :file-list="problemImgs.imgList.value || []" @after-read="problemImgs.uploadImgs" | |
| 19 | + @delete="problemImgs.deleteImg" multiple :width="70" :height="70" | |
| 20 | + :max-count="problemImgs.uploadConfig.maxCount" :upload-text="problemImgs.uploadText" | |
| 21 | + :size-type="problemImgs.uploadConfig.sizeType"></up-upload> | |
| 57 | 22 | </up-form-item> |
| 58 | - | |
| 59 | 23 | <!-- 多组派单区域 --> |
| 60 | 24 | <view class="dispatch-list-wrap"> |
| 61 | 25 | <view class="dispatch-title">派单情况</view> |
| 62 | - | |
| 63 | 26 | <!-- 循环渲染每组派单 --> |
| 64 | 27 | <view class="dispatch-item" v-for="(item, idx) in dispatchList" :key="idx"> |
| 65 | 28 | <view class="item-head"> |
| 66 | 29 | <text>派单{{ idx + 1 }}</text> |
| 67 | 30 | <!-- 数量大于1才显示红色关闭X --> |
| 68 | - <up-icon | |
| 69 | - v-if="dispatchList.length > 1" | |
| 70 | - name="close" | |
| 71 | - color="#f53f3f" | |
| 72 | - size="26" | |
| 73 | - class="close-icon" | |
| 74 | - @click="delDispatchItem(idx)" | |
| 75 | - ></up-icon> | |
| 31 | + <up-icon v-if="dispatchList.length > 1" name="close" color="#f53f3f" size="26" class="close-icon" | |
| 32 | + @click="delDispatchItem(idx)"></up-icon> | |
| 76 | 33 | </view> |
| 77 | - | |
| 78 | 34 | <!-- 业务线 --> |
| 79 | - <up-form-item | |
| 80 | - label="业务线" | |
| 81 | - prop="busiLineCn" | |
| 82 | - border-bottom | |
| 83 | - required | |
| 84 | - @click="handleActionSheetOpen('busiLine', idx); hideKeyboard()" | |
| 85 | - > | |
| 86 | - <up-input | |
| 87 | - v-model="item.busiLineCn" | |
| 88 | - disabled | |
| 89 | - disabled-color="#ffffff" | |
| 90 | - placeholder="请选择业务线" | |
| 91 | - border="none" | |
| 92 | - ></up-input> | |
| 35 | + <up-form-item label="业务线" prop="busiLineCn" border-bottom required | |
| 36 | + @click="handleActionSheetOpen('busiLine', idx); hideKeyboard()"> | |
| 37 | + <up-input v-model="item.busiLineCn" disabled disabled-color="#ffffff" placeholder="请选择业务线" | |
| 38 | + border="none"></up-input> | |
| 93 | 39 | <template #right> |
| 94 | 40 | <up-icon name="arrow-right" size="16"></up-icon> |
| 95 | 41 | </template> |
| 96 | 42 | </up-form-item> |
| 97 | - | |
| 98 | 43 | <!-- 问题类型 --> |
| 99 | - <up-form-item | |
| 100 | - label="问题类型" | |
| 101 | - prop="orderName" | |
| 102 | - border-bottom | |
| 103 | - required | |
| 104 | - @click="openCascader(idx); hideKeyboard()" | |
| 105 | - > | |
| 106 | - <up-input | |
| 107 | - v-model="item.orderName" | |
| 108 | - disabled | |
| 109 | - disabled-color="#ffffff" | |
| 110 | - placeholder="请选择问题类型" | |
| 111 | - border="none" | |
| 112 | - ></up-input> | |
| 44 | + <up-form-item label="问题类型" prop="orderName" border-bottom required | |
| 45 | + @click="openCascader(idx); hideKeyboard()"> | |
| 46 | + <up-input v-model="item.orderName" disabled disabled-color="#ffffff" placeholder="请选择问题类型" | |
| 47 | + border="none"></up-input> | |
| 113 | 48 | <template #right> |
| 114 | 49 | <up-icon name="arrow-right" size="16"></up-icon> |
| 115 | 50 | </template> |
| 116 | 51 | </up-form-item> |
| 117 | - | |
| 118 | 52 | <!-- 道路名称 --> |
| 119 | - <up-form-item | |
| 120 | - label="道路名称" | |
| 121 | - prop="roadName" | |
| 122 | - border-bottom | |
| 123 | - required | |
| 124 | - @click="handleActionSheetOpen('roadName', idx); hideKeyboard()" | |
| 125 | - > | |
| 126 | - <up-input | |
| 127 | - v-model="item.roadName" | |
| 128 | - readonly | |
| 129 | - disabled-color="#ffffff" | |
| 130 | - placeholder="请先选择工单位置" | |
| 131 | - border="none" | |
| 132 | - ></up-input> | |
| 53 | + <up-form-item label="道路名称" prop="roadName" border-bottom required | |
| 54 | + @click="handleActionSheetOpen('roadName', idx); hideKeyboard()"> | |
| 55 | + <up-input v-model="item.roadName" readonly disabled-color="#ffffff" placeholder="请先选择工单位置" | |
| 56 | + border="none"></up-input> | |
| 133 | 57 | <template #right> |
| 134 | 58 | <up-icon name="arrow-right" size="16"></up-icon> |
| 135 | 59 | </template> |
| 136 | 60 | </up-form-item> |
| 137 | - | |
| 138 | 61 | <!-- 紧急程度 --> |
| 139 | - <up-form-item | |
| 140 | - label="紧急程度" | |
| 141 | - prop="pressingTypeName" | |
| 142 | - border-bottom | |
| 143 | - required | |
| 144 | - @click="handleActionSheetOpen('pressingType', idx); hideKeyboard()" | |
| 145 | - > | |
| 146 | - <up-input | |
| 147 | - v-model="item.pressingTypeName" | |
| 148 | - disabled | |
| 149 | - disabled-color="#ffffff" | |
| 150 | - placeholder="请选择紧急程度" | |
| 151 | - border="none" | |
| 152 | - ></up-input> | |
| 62 | + <up-form-item label="紧急程度" prop="pressingTypeName" border-bottom required | |
| 63 | + @click="handleActionSheetOpen('pressingType', idx); hideKeyboard()"> | |
| 64 | + <up-input v-model="item.pressingTypeName" disabled disabled-color="#ffffff" placeholder="请选择紧急程度" | |
| 65 | + border="none"></up-input> | |
| 153 | 66 | <template #right> |
| 154 | 67 | <up-icon name="arrow-right" size="16"></up-icon> |
| 155 | 68 | </template> |
| 156 | 69 | </up-form-item> |
| 157 | - | |
| 158 | 70 | <!-- 每组派单独立:希望完成时间 --> |
| 159 | - <up-form-item | |
| 160 | - label="希望完成时间" | |
| 161 | - | |
| 162 | - @click="openItemDatePicker(idx); hideKeyboard()" | |
| 163 | - > | |
| 164 | - <up-input | |
| 165 | - v-model="item.expectedFinishDate" | |
| 166 | - border="none" | |
| 167 | - readonly | |
| 168 | - placeholder="点击选择时间" | |
| 169 | - ></up-input> | |
| 71 | + <up-form-item label="希望完成时间" @click="openItemDatePicker(idx); hideKeyboard()"> | |
| 72 | + <up-input v-model="item.expectedFinishDate" border="none" readonly placeholder="点击选择时间"></up-input> | |
| 170 | 73 | <template #right> |
| 171 | 74 | <view v-if="item.expectedFinishDate" @click.stop> |
| 172 | 75 | <up-icon name="close" size="16" @click.stop="clearItemDate(idx)"></up-icon> |
| ... | ... | @@ -176,81 +79,49 @@ |
| 176 | 79 | </up-form-item> |
| 177 | 80 | </view> |
| 178 | 81 | </view> |
| 179 | - | |
| 180 | 82 | <!-- 新增派单按钮 --> |
| 181 | 83 | <view class="add-dispatch-btn-wrap" v-if="!isRenew"> |
| 182 | - <up-button | |
| 183 | - type="primary" | |
| 184 | - plain | |
| 185 | - text="新增派单" | |
| 186 | - :disabled="dispatchList.length >= 3" | |
| 187 | - @click="addDispatchItem" | |
| 188 | - ></up-button> | |
| 84 | + <up-button type="primary" plain text="新增派单" | |
| 85 | + :disabled="dispatchList.length >= 3 || getAvailableBusiLineList().length === 0" | |
| 86 | + @click="addDispatchItem"></up-button> | |
| 189 | 87 | </view> |
| 190 | 88 | </up-form> |
| 191 | 89 | </view> |
| 192 | - | |
| 193 | 90 | <!-- 底部提交按钮 --> |
| 194 | 91 | <view class="fixed-bottom-btn-wrap"> |
| 195 | 92 | <up-button type="primary" text="提交工单" @click="submitWorkOrder"></up-button> |
| 196 | 93 | </view> |
| 197 | - | |
| 198 | 94 | <!-- 通用下拉弹窗:业务线 / 道路 / 紧急程度 共用 --> |
| 199 | - <up-action-sheet | |
| 200 | - :show="showActionSheet" | |
| 201 | - :actions="currentActionSheetData.list" | |
| 202 | - :title="currentActionSheetData.title" | |
| 203 | - @close="handleActionSheetClose" | |
| 204 | - @select="handleActionSheetSelect" | |
| 205 | - ></up-action-sheet> | |
| 206 | - | |
| 95 | + <up-action-sheet :show="showActionSheet" :actions="currentActionSheetData.list" | |
| 96 | + :title="currentActionSheetData.title" @close="handleActionSheetClose" | |
| 97 | + @select="handleActionSheetSelect"></up-action-sheet> | |
| 207 | 98 | <!-- 问题类型 级联选择器 --> |
| 208 | - <up-cascader | |
| 209 | - v-model="cascaderValue" | |
| 210 | - :data="cascaderOptions" | |
| 211 | - v-model:show="showCascader" | |
| 212 | - title="问题类型" | |
| 213 | - :auto-close="false" | |
| 214 | - @confirm="cascaderConfirm" | |
| 215 | - ></up-cascader> | |
| 216 | - | |
| 99 | + <up-cascader v-model="cascaderValue" :data="cascaderOptions" v-model:show="showCascader" title="问题类型" | |
| 100 | + :auto-close="false" @confirm="cascaderConfirm"></up-cascader> | |
| 217 | 101 | <!-- 单独时间选择器(给每组派单用) --> |
| 218 | - <up-datetime-picker | |
| 219 | - :show="showItemDatePicker" | |
| 220 | - v-model="tempDateValue" | |
| 221 | - mode="datetime" | |
| 222 | - :min-date="Date.now()" | |
| 223 | - @cancel="closeItemDatePicker" | |
| 224 | - @confirm="itemDateConfirm" | |
| 225 | - ></up-datetime-picker> | |
| 102 | + <up-datetime-picker :show="showItemDatePicker" v-model="tempDateValue" mode="datetime" :min-date="Date.now()" | |
| 103 | + @cancel="closeItemDatePicker" @confirm="itemDateConfirm"></up-datetime-picker> | |
| 226 | 104 | </view> |
| 227 | 105 | </template> |
| 228 | - | |
| 229 | 106 | <script setup> |
| 230 | -import { ref, reactive, nextTick } from 'vue' | |
| 107 | +import { ref, reactive, nextTick, computed } from 'vue' | |
| 231 | 108 | import { onReady, onShow, onLoad, onBackPress } from '@dcloudio/uni-app'; |
| 232 | 109 | import { useUploadImgs } from '@/common/utils/useUploadImgs' |
| 233 | 110 | import { getRoadListByLatLng, getProblemListByType } from '@/api/common' |
| 234 | 111 | import { |
| 235 | - daquUniversalApproval, qyUniversalApproval, qyWorkorderCreate,dycCreateBatchNew, | |
| 236 | - dcyUniversalApproval, daquWorkorderCreate, dcyWorkorderCreate | |
| 112 | +daquUniversalApproval, qyUniversalApproval, qyWorkorderCreate, dycCreateBatchNew, | |
| 113 | +dcyUniversalApproval, daquWorkorderCreate, dcyWorkorderCreate | |
| 237 | 114 | } from '@/api/regional-order-manage/regional-order-manage' |
| 238 | 115 | import { timeFormat } from '@/uni_modules/uview-plus' |
| 239 | 116 | import { nextStepMap } from '@/common/utils/common' |
| 240 | 117 | import { useUserStore } from '@/pinia/user'; |
| 241 | - | |
| 242 | 118 | // ========== 常量配置区 ========== |
| 243 | 119 | const CONST = { |
| 244 | - DICT_PRESSING_TYPE: 'workorder_pressing_type', | |
| 245 | - UPLOAD_CONFIG: { maxCount: 3, uploadText: '选择问题照片', sizeType: ['compressed'] }, | |
| 246 | - PAGE_ORDER_LIST: '/pages-sub/problem/regional-order-manage/index', | |
| 247 | - BUSI_LINE_MAP: { | |
| 248 | - yl: '园林', sz: '市政', wy: '物业', '园林': 'yl', '市政': 'sz', '物业': 'wy', | |
| 249 | - 'zx': '秩序管理', 'hj': '环境卫生', '秩序管理': 'zx', '环境卫生': 'hj' | |
| 250 | - }, | |
| 251 | - ROLE: { MANAGER: 'regional_manager', INSPECTOR: 'Inspector_global', PATROL: 'patrol_global' , LEADER: 'Inspector_global_leader'} | |
| 120 | +DICT_PRESSING_TYPE: 'workorder_pressing_type', | |
| 121 | +UPLOAD_CONFIG: { maxCount: 3, uploadText: '选择问题照片', sizeType: ['compressed'] }, | |
| 122 | +PAGE_ORDER_LIST: '/pages-sub/problem/regional-order-manage/index', | |
| 123 | +ROLE: { MANAGER: 'regional_manager', INSPECTOR: 'Inspector_global', PATROL: 'patrol_global', LEADER: 'Inspector_global_leader' } | |
| 252 | 124 | } |
| 253 | - | |
| 254 | 125 | // ========== 全局实例 & 基础状态 ========== |
| 255 | 126 | const userStore = useUserStore(); |
| 256 | 127 | const USER_ROLES = userStore.userInfo?.roles || []; |
| ... | ... | @@ -258,556 +129,554 @@ const workOrderFormRef = ref(null); |
| 258 | 129 | const showActionSheet = ref(false); |
| 259 | 130 | const isRenew = ref(false); |
| 260 | 131 | const renewOrderData = ref(null); |
| 261 | - | |
| 262 | 132 | // 当前操作的派单项下标 |
| 263 | 133 | const currentDispatchIndex = ref(0); |
| 264 | - | |
| 265 | 134 | // 三级级联选择器 |
| 266 | 135 | const showCascader = ref(false); |
| 267 | 136 | const cascaderValue = ref([]); |
| 268 | 137 | const cascaderOptions = ref([]); |
| 269 | - | |
| 270 | 138 | // 下拉弹窗数据 |
| 271 | 139 | const currentActionSheetData = reactive({ type: '', list: [], title: '', index: 0 }); |
| 272 | 140 | const roadNameList = ref([]); |
| 273 | 141 | const pressingTypeList = ref([]); |
| 274 | 142 | const busiLineOptions = ref([]); |
| 275 | 143 | const currentBusiLineCode = ref(''); |
| 276 | - | |
| 277 | 144 | // 每组派单独立时间选择器 |
| 278 | 145 | const showItemDatePicker = ref(false); |
| 279 | 146 | const tempDateValue = ref(Date.now()); |
| 280 | 147 | const curSelectDateIndex = ref(-1); |
| 281 | - | |
| 282 | 148 | // 图片上传 |
| 283 | 149 | const problemImgs = useUploadImgs({ |
| 284 | - ...CONST.UPLOAD_CONFIG, | |
| 285 | - formRef: workOrderFormRef, | |
| 286 | - fieldName: 'problemImgs' | |
| 150 | +...CONST.UPLOAD_CONFIG, | |
| 151 | +formRef: workOrderFormRef, | |
| 152 | +fieldName: 'problemImgs' | |
| 287 | 153 | }) |
| 288 | 154 | if (!Array.isArray(problemImgs.rawImgList.value)) problemImgs.rawImgList.value = []; |
| 289 | 155 | |
| 156 | +// ===================== 【动态构建业务线双向映射,从 userStore.dictList】===================== | |
| 157 | +let busiLineEnToCnMap = {}; // code -> 中文 | |
| 158 | +let busiLineCnToEnMap = {}; // 中文 -> code | |
| 159 | +/** | |
| 160 | + * 从dictList构建业务线双向map | |
| 161 | + * @param {Array} dictList userStore.dictList | |
| 162 | + */ | |
| 163 | +const buildBusiLineMap = (dictList) => { | |
| 164 | + busiLineEnToCnMap = {}; | |
| 165 | + busiLineCnToEnMap = {}; | |
| 166 | + if (!Array.isArray(dictList)) return; | |
| 167 | + dictList.forEach(item => { | |
| 168 | + const code = item.value?.trim(); | |
| 169 | + const name = item.label?.trim(); | |
| 170 | + if (code && name) { | |
| 171 | + busiLineEnToCnMap[code] = name; | |
| 172 | + busiLineCnToEnMap[name] = code; | |
| 173 | + } | |
| 174 | + }) | |
| 175 | +} | |
| 176 | +// 工具方法(替换原来硬编码BUSI_LINE_MAP) | |
| 177 | +const busiLineCnToEn = (cn) => busiLineCnToEnMap[cn] || ''; | |
| 178 | +const busiLineEnToCn = (en) => busiLineEnToCnMap[en] || ''; | |
| 179 | +// ========================================================================== | |
| 180 | + | |
| 181 | +// ===================== 【新增方法:获取已占用业务线code】===================== | |
| 182 | +const getUsedBusiCodeList = () => { | |
| 183 | +return dispatchList.value | |
| 184 | + .filter(item => !!item.busiLineCode) | |
| 185 | + .map(item => item.busiLineCode) | |
| 186 | +} | |
| 187 | +/** | |
| 188 | + * 获取【当前可选业务线】:排除已经被其他派单项占用的业务线 | |
| 189 | + * @param {number} currentIdx 当前正在操作的派单项下标 | |
| 190 | + */ | |
| 191 | +const getAvailableBusiLineList = (currentIdx = -1) => { | |
| 192 | +const usedCodes = getUsedBusiCodeList() | |
| 193 | +return busiLineOptions.value.filter(opt => { | |
| 194 | +// 如果是当前这条自己已经选中的业务线,保留;其他派单项占用的过滤掉 | |
| 195 | +const isSelfSelected = dispatchList.value[currentIdx]?.busiLineCode === opt.value | |
| 196 | +const isUsedByOther = usedCodes.includes(opt.value) && !isSelfSelected | |
| 197 | +return !isUsedByOther | |
| 198 | + }) | |
| 199 | +} | |
| 200 | +// ========================================================================== | |
| 290 | 201 | // ========== 公共表单(所有派单共用) ========== |
| 291 | 202 | const baseForm = reactive({ |
| 292 | - workLocation: '', | |
| 293 | - problemDesc: '', | |
| 294 | - lat: 0, | |
| 295 | - lon: 0 | |
| 203 | +workLocation: '', | |
| 204 | +problemDesc: '', | |
| 205 | +lat: 0, | |
| 206 | +lon: 0 | |
| 296 | 207 | }) |
| 297 | - | |
| 298 | 208 | // ========== 派单列表(多组派单核心数组) ========== |
| 299 | 209 | const createDispatchItem = (defaultBusiLine = null) => { |
| 300 | - return { | |
| 301 | - busiLineCn: defaultBusiLine?.name || '', | |
| 302 | - busiLineCode: defaultBusiLine?.value || '', | |
| 303 | - roadId: 0, | |
| 304 | - roadName: '', | |
| 305 | - orderName: '', | |
| 306 | - orderCode: '', | |
| 307 | - pressingType: '', | |
| 308 | - pressingTypeName: '', | |
| 309 | - expectedFinishDate: '' // 每组独立完成时间 | |
| 210 | +return { | |
| 211 | +busiLineCn: defaultBusiLine?.name || '', | |
| 212 | +busiLineCode: defaultBusiLine?.value || '', | |
| 213 | +roadId: 0, | |
| 214 | +roadName: '', | |
| 215 | +orderName: '', | |
| 216 | +orderCode: '', | |
| 217 | +pressingType: '', | |
| 218 | +pressingTypeName: '', | |
| 219 | +expectedFinishDate: '' // 每组独立完成时间 | |
| 310 | 220 | } |
| 311 | 221 | } |
| 312 | 222 | // 初始化默认 1 组派单 |
| 313 | 223 | const dispatchList = ref([createDispatchItem()]) |
| 314 | - | |
| 315 | 224 | // ========== 表单校验规则 ========== |
| 316 | 225 | const workOrderFormRules = reactive({ |
| 317 | - workLocation: [{ type: 'string', required: true, message: '请选择工单位置', trigger: ['change', 'blur'] }], | |
| 318 | - problemDesc: [ | |
| 226 | +workLocation: [{ type: 'string', required: true, message: '请选择工单位置', trigger: ['change', 'blur'] }], | |
| 227 | +problemDesc: [ | |
| 319 | 228 | { type: 'string', required: true, message: '请输入情况描述', trigger: ['change', 'blur'] }, |
| 320 | 229 | { type: 'string', min: 3, max: 200, message: '情况描述需3-200字', trigger: ['change', 'blur'] } |
| 321 | 230 | ], |
| 322 | - problemImgs: [problemImgs.imgValidateRule] | |
| 231 | +problemImgs: [problemImgs.imgValidateRule] | |
| 323 | 232 | }) |
| 324 | - | |
| 325 | 233 | // ========== 工具方法 ========== |
| 326 | -const busiLineCnToEn = (cn) => CONST.BUSI_LINE_MAP[cn] || ''; | |
| 327 | -const busiLineEnToCn = (en) => CONST.BUSI_LINE_MAP[en] || ''; | |
| 328 | 234 | const toOrderListPage = () => uni.reLaunch({ url: CONST.PAGE_ORDER_LIST }); |
| 329 | 235 | const formatDate = (dateVal, fmt = 'yyyy-mm-dd hh:MM:ss') => dateVal ? timeFormat(dateVal, fmt) : ''; |
| 330 | 236 | const dateToTimestamp = (dateStr) => dateStr ? new Date(dateStr).getTime() : ''; |
| 331 | - | |
| 332 | 237 | /** |
| 333 | 238 | * 加载三级问题类型级联数据 |
| 334 | 239 | */ |
| 335 | 240 | const loadProblemCascader = async (parentCode) => { |
| 336 | - if (!parentCode) { | |
| 337 | - cascaderOptions.value = []; | |
| 338 | - return; | |
| 241 | +if (!parentCode) { | |
| 242 | +cascaderOptions.value = []; | |
| 243 | +return; | |
| 339 | 244 | } |
| 340 | - try { | |
| 341 | - uni.showLoading({ title: '加载类型中...' }); | |
| 342 | - const res = await getProblemListByType({ parentCode }); | |
| 343 | - cascaderOptions.value = res|| []; | |
| 245 | +try { | |
| 246 | +uni.showLoading({ title: '加载类型中...' }); | |
| 247 | +const res = await getProblemListByType({ parentCode }); | |
| 248 | +cascaderOptions.value = res || []; | |
| 344 | 249 | } catch (err) { |
| 345 | - console.error('加载问题类型失败', err); | |
| 346 | - uni.showToast({ title: '问题类型加载失败', icon: 'none' }); | |
| 347 | - cascaderOptions.value = []; | |
| 250 | +console.error('加载问题类型失败', err); | |
| 251 | +uni.showToast({ title: '问题类型加载失败', icon: 'none' }); | |
| 252 | +cascaderOptions.value = []; | |
| 348 | 253 | } finally { |
| 349 | - uni.hideLoading(); | |
| 254 | +uni.hideLoading(); | |
| 350 | 255 | } |
| 351 | 256 | }; |
| 352 | - | |
| 353 | 257 | /** 打开级联选择器 */ |
| 354 | 258 | const openCascader = (idx) => { |
| 355 | - currentDispatchIndex.value = idx; | |
| 356 | - const item = dispatchList.value[idx]; | |
| 357 | - if (!item.busiLineCode) { | |
| 358 | - uni.showToast({ title: '请先选择业务线', icon: 'none' }); | |
| 359 | - return; | |
| 259 | +currentDispatchIndex.value = idx; | |
| 260 | +const item = dispatchList.value[idx]; | |
| 261 | +if (!item.busiLineCode) { | |
| 262 | +uni.showToast({ title: '请先选择业务线', icon: 'none' }); | |
| 263 | +return; | |
| 360 | 264 | } |
| 361 | - if (!cascaderOptions.value || cascaderOptions.value.length === 0) { | |
| 362 | - uni.showToast({ title: '正在加载问题类型,请稍等', icon: 'none' }); | |
| 363 | - loadProblemCascader(item.busiLineCode); | |
| 364 | - return; | |
| 265 | +if (!cascaderOptions.value || cascaderOptions.value.length === 0) { | |
| 266 | +uni.showToast({ title: '正在加载问题类型,请稍等', icon: 'none' }); | |
| 267 | +loadProblemCascader(item.busiLineCode); | |
| 268 | +return; | |
| 365 | 269 | } |
| 366 | - cascaderValue.value = []; | |
| 367 | - showCascader.value = true; | |
| 270 | +cascaderValue.value = []; | |
| 271 | +showCascader.value = true; | |
| 368 | 272 | }; |
| 369 | - | |
| 370 | 273 | /** 级联选择确认回调 */ |
| 371 | 274 | const cascaderConfirm = (selectedArr) => { |
| 372 | - const idx = currentDispatchIndex.value; | |
| 373 | - const item = dispatchList.value[idx]; | |
| 374 | - console.log('最终选中值数组:', selectedArr); | |
| 375 | - | |
| 376 | - const getLabelByValueList = (list, valList) => { | |
| 377 | - const labelArr = []; | |
| 378 | - let currentList = list; | |
| 379 | - for (const val of valList) { | |
| 380 | - const target = currentList.find(item => item.value === val); | |
| 381 | - if (!target) break; | |
| 382 | - labelArr.push(target.label); | |
| 383 | - currentList = target.children || []; | |
| 275 | +const idx = currentDispatchIndex.value; | |
| 276 | +const item = dispatchList.value[idx]; | |
| 277 | +console.log('最终选中值数组:', selectedArr); | |
| 278 | +const getLabelByValueList = (list, valList) => { | |
| 279 | +const labelArr = []; | |
| 280 | +let currentList = list; | |
| 281 | +for (const val of valList) { | |
| 282 | +const target = currentList.find(item => item.value === val); | |
| 283 | +if (!target) break; | |
| 284 | +labelArr.push(target.label); | |
| 285 | +currentList = target.children || []; | |
| 384 | 286 | } |
| 385 | - return labelArr.join('/'); | |
| 287 | +return labelArr.join('/'); | |
| 386 | 288 | }; |
| 387 | - | |
| 388 | - const fullLabel = getLabelByValueList(cascaderOptions.value, selectedArr); | |
| 389 | - item.orderName = fullLabel; | |
| 390 | - item.orderCode = selectedArr.at(-1); | |
| 391 | - cascaderValue.value = selectedArr; | |
| 392 | - workOrderFormRef.value?.validateField('orderName'); | |
| 289 | +const fullLabel = getLabelByValueList(cascaderOptions.value, selectedArr); | |
| 290 | +item.orderName = fullLabel; | |
| 291 | +item.orderCode = selectedArr.at(-1); | |
| 292 | +cascaderValue.value = selectedArr; | |
| 293 | +workOrderFormRef.value?.validateField('orderName'); | |
| 393 | 294 | }; |
| 394 | - | |
| 395 | 295 | // ========== 每组派单 时间选择相关 ========== |
| 396 | 296 | const openItemDatePicker = (idx) => { |
| 397 | - curSelectDateIndex.value = idx; | |
| 398 | - tempDateValue.value = Date.now(); | |
| 399 | - showItemDatePicker.value = true; | |
| 297 | +curSelectDateIndex.value = idx; | |
| 298 | +tempDateValue.value = Date.now(); | |
| 299 | +showItemDatePicker.value = true; | |
| 400 | 300 | }; |
| 401 | 301 | const closeItemDatePicker = () => { |
| 402 | - showItemDatePicker.value = false; | |
| 403 | - curSelectDateIndex.value = -1; | |
| 302 | +showItemDatePicker.value = false; | |
| 303 | +curSelectDateIndex.value = -1; | |
| 404 | 304 | }; |
| 405 | 305 | const itemDateConfirm = (e) => { |
| 406 | - const idx = curSelectDateIndex.value; | |
| 407 | - if (idx > -1 && dispatchList.value[idx]) { | |
| 408 | - dispatchList.value[idx].expectedFinishDate = formatDate(e.value); | |
| 306 | +const idx = curSelectDateIndex.value; | |
| 307 | +if (idx > -1 && dispatchList.value[idx]) { | |
| 308 | +dispatchList.value[idx].expectedFinishDate = formatDate(e.value); | |
| 409 | 309 | } |
| 410 | - closeItemDatePicker(); | |
| 310 | +closeItemDatePicker(); | |
| 411 | 311 | }; |
| 412 | 312 | const clearItemDate = (idx) => { |
| 413 | - dispatchList.value[idx].expectedFinishDate = ''; | |
| 313 | +dispatchList.value[idx].expectedFinishDate = ''; | |
| 414 | 314 | }; |
| 415 | - | |
| 416 | 315 | // ========== 新增 / 删除 派单项 ========== |
| 417 | 316 | /** 新增派单 */ |
| 418 | 317 | const addDispatchItem = () => { |
| 419 | - if (dispatchList.value.length >= 3) return; | |
| 420 | - // 新项:业务线默认第一个,其余置空 | |
| 421 | - const firstBusi = busiLineOptions.value[0] || null; | |
| 422 | - dispatchList.value.push(createDispatchItem(firstBusi)); | |
| 423 | - if(firstBusi) { | |
| 424 | - currentBusiLineCode.value = firstBusi.value; | |
| 425 | - loadProblemCascader(firstBusi.value); | |
| 318 | +if (dispatchList.value.length >= 3) return; | |
| 319 | +// 【修改】新增取剩下可用业务线第一条 | |
| 320 | +const availList = getAvailableBusiLineList() | |
| 321 | +const firstBusi = availList[0] || null; | |
| 322 | +dispatchList.value.push(createDispatchItem(firstBusi)); | |
| 323 | +if (firstBusi) { | |
| 324 | +currentBusiLineCode.value = firstBusi.value; | |
| 325 | +loadProblemCascader(firstBusi.value); | |
| 426 | 326 | } |
| 427 | 327 | }; |
| 428 | - | |
| 429 | 328 | /** 删除派单项 */ |
| 430 | 329 | const delDispatchItem = (idx) => { |
| 431 | - if (dispatchList.value.length <= 1) return; | |
| 432 | - dispatchList.value.splice(idx, 1); | |
| 330 | +if (dispatchList.value.length <= 1) return; | |
| 331 | +dispatchList.value.splice(idx, 1); | |
| 433 | 332 | }; |
| 434 | - | |
| 435 | - | |
| 436 | 333 | // 生命周期 |
| 437 | 334 | onLoad(async (options) => { |
| 438 | - initBusiLineOptions(); | |
| 439 | - // 初始化第一组派单默认业务线 | |
| 440 | - if (busiLineOptions.value.length) { | |
| 441 | - const firstLine = busiLineOptions.value[0]; | |
| 442 | - dispatchList.value[0].busiLineCn = firstLine.name; | |
| 443 | - dispatchList.value[0].busiLineCode = firstLine.value; | |
| 444 | - currentBusiLineCode.value = firstLine.value; | |
| 445 | - // 非重新提交才默认加载级联 | |
| 446 | - if (!(options.isRenew == 1 && options.tempKey)) { | |
| 447 | - loadProblemCascader(firstLine.value); | |
| 335 | +initBusiLineOptions(); | |
| 336 | +// 初始化第一组派单默认业务线 | |
| 337 | +if (busiLineOptions.value.length) { | |
| 338 | +const availList = getAvailableBusiLineList(0) | |
| 339 | +const firstLine = availList[0]; | |
| 340 | +dispatchList.value[0].busiLineCn = firstLine.name; | |
| 341 | +dispatchList.value[0].busiLineCode = firstLine.value; | |
| 342 | +currentBusiLineCode.value = firstLine.value; | |
| 343 | +// 非重新提交才默认加载级联 | |
| 344 | +if (!(options.isRenew == 1 && options.tempKey)) { | |
| 345 | +loadProblemCascader(firstLine.value); | |
| 448 | 346 | } |
| 449 | 347 | } |
| 450 | - // 重新提交场景:等待回显完成 | |
| 451 | - if (options.isRenew == 1 && options.tempKey) { | |
| 452 | - await handleRenewOptions(options.tempKey); | |
| 348 | +// 重新提交场景:等待回显完成 | |
| 349 | +if (options.isRenew == 1 && options.tempKey) { | |
| 350 | +await handleRenewOptions(options.tempKey); | |
| 453 | 351 | } |
| 454 | 352 | }); |
| 455 | - | |
| 456 | 353 | onReady(() => { |
| 457 | - nextTick(() => { | |
| 458 | - workOrderFormRef.value?.setRules(workOrderFormRules); | |
| 354 | +nextTick(() => { | |
| 355 | +workOrderFormRef.value?.setRules(workOrderFormRules); | |
| 459 | 356 | }); |
| 460 | 357 | }); |
| 461 | - | |
| 462 | 358 | onShow(() => { |
| 463 | - pressingTypeList.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList(CONST.DICT_PRESSING_TYPE)); | |
| 359 | +pressingTypeList.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList(CONST.DICT_PRESSING_TYPE)); | |
| 464 | 360 | }); |
| 465 | - | |
| 466 | 361 | // 物理返回拦截 |
| 467 | 362 | onBackPress(() => { |
| 468 | - uni.redirectTo({ url: CONST.PAGE_ORDER_LIST }); | |
| 469 | - return true; | |
| 363 | +uni.redirectTo({ url: CONST.PAGE_ORDER_LIST }); | |
| 364 | +return true; | |
| 470 | 365 | }); |
| 471 | - | |
| 472 | 366 | // ========== 业务线相关 ========== |
| 473 | 367 | const initBusiLineOptions = () => { |
| 474 | - const rawBusiLines = userStore.userInfo?.user?.busiLine?.split(',') || []; | |
| 475 | - busiLineOptions.value = rawBusiLines.map(item => ({ | |
| 476 | - name: busiLineEnToCn(item.trim()), | |
| 477 | - value: item.trim() | |
| 368 | +// 从userStore.dictList构建双向映射 | |
| 369 | +buildBusiLineMap(userStore.dictList); | |
| 370 | +// 用户拥有的业务线code数组 | |
| 371 | +const rawBusiLines = userStore.userInfo?.user?.busiLine?.split(',') || []; | |
| 372 | +// 过滤dict,只保留用户有权限的业务线,组装 {name, value} | |
| 373 | +busiLineOptions.value = userStore.dictList | |
| 374 | + .filter(dictItem => rawBusiLines.includes(dictItem.value)) | |
| 375 | + .map(dictItem => ({ | |
| 376 | + name: dictItem.label, | |
| 377 | + value: dictItem.value | |
| 478 | 378 | })); |
| 479 | 379 | }; |
| 480 | - | |
| 481 | 380 | /** 业务线切换联动清空数据并重新加载级联 */ |
| 482 | 381 | const handleBusiLineChange = (code, idx) => { |
| 483 | - const item = dispatchList.value[idx]; | |
| 484 | - item.roadName = ''; | |
| 485 | - item.roadId = 0; | |
| 486 | - item.orderName = ''; | |
| 487 | - item.orderCode = ''; | |
| 488 | - cascaderValue.value = []; | |
| 489 | - roadNameList.value = []; | |
| 490 | - | |
| 491 | - currentBusiLineCode.value = code; | |
| 492 | - loadProblemCascader(code); | |
| 493 | - | |
| 494 | - if(baseForm.workLocation) getRoadListByBusiLine(); | |
| 382 | +const item = dispatchList.value[idx]; | |
| 383 | +item.roadName = ''; | |
| 384 | +item.roadId = 0; | |
| 385 | +item.orderName = ''; | |
| 386 | +item.orderCode = ''; | |
| 387 | +cascaderValue.value = []; | |
| 388 | +roadNameList.value = []; | |
| 389 | +currentBusiLineCode.value = code; | |
| 390 | +loadProblemCascader(code); | |
| 391 | +if (baseForm.workLocation) getRoadListByBusiLine(); | |
| 495 | 392 | }; |
| 496 | - | |
| 497 | 393 | /** 根据经纬度+业务线获取道路列表 */ |
| 498 | 394 | const getRoadListByBusiLine = async () => { |
| 499 | - if (!baseForm.lat || !baseForm.lon) return; | |
| 500 | - const busiLineEn = busiLineCnToEn(dispatchList.value[0].busiLineCn); | |
| 501 | - if (!busiLineEn) return uni.showToast({ title: '业务线选择异常', icon: 'none' }); | |
| 502 | - | |
| 503 | - try { | |
| 504 | - uni.showLoading({ title: '获取道路名称中...' }); | |
| 505 | - const roadRes = await getRoadListByLatLng({ | |
| 506 | - busiLine: busiLineEn, | |
| 507 | - latitude: baseForm.lat, | |
| 508 | - longitude: baseForm.lon | |
| 395 | +if (!baseForm.lat || !baseForm.lon) return; | |
| 396 | +const busiLineEn = busiLineCnToEn(dispatchList.value[0].busiLineCn); | |
| 397 | +if (!busiLineEn) return uni.showToast({ title: '业务线选择异常', icon: 'none' }); | |
| 398 | +try { | |
| 399 | +uni.showLoading({ title: '获取道路名称中...' }); | |
| 400 | +const roadRes = await getRoadListByLatLng({ | |
| 401 | +busiLine: busiLineEn, | |
| 402 | +latitude: baseForm.lat, | |
| 403 | +longitude: baseForm.lon | |
| 509 | 404 | }); |
| 510 | - roadNameList.value = Array.isArray(roadRes) | |
| 511 | - ? roadRes.map(item => ({ name: item.roadName || '', code: item.roadCode || '', id: item.roadId || 0 })) | |
| 512 | - : [{ name: '未查询到道路名称', code: '', id: 0 }]; | |
| 513 | - !Array.isArray(roadRes) && uni.showToast({ title: '未查询到该位置的道路信息', icon: 'none' }); | |
| 405 | +roadNameList.value = Array.isArray(roadRes) | |
| 406 | +? roadRes.map(item => ({ name: item.roadName || '', code: item.roadCode || '', id: item.roadId || 0 })) | |
| 407 | +: [{ name: '未查询到道路名称', code: '', id: 0 }]; | |
| 408 | +!Array.isArray(roadRes) && uni.showToast({ title: '未查询到该位置的道路信息', icon: 'none' }); | |
| 514 | 409 | } catch (err) { |
| 515 | - console.error('获取道路名称失败:', err); | |
| 516 | - roadNameList.value = [{ name: '获取失败,请重新选择位置', code: '', id: 0 }]; | |
| 517 | - uni.showToast({ title: '获取道路名称失败,请重试', icon: 'none' }); | |
| 410 | +console.error('获取道路名称失败:', err); | |
| 411 | +roadNameList.value = [{ name: '获取失败,请重新选择位置', code: '', id: 0 }]; | |
| 412 | +uni.showToast({ title: '获取道路名称失败,请重试', icon: 'none' }); | |
| 518 | 413 | } finally { |
| 519 | - uni.hideLoading(); | |
| 414 | +uni.hideLoading(); | |
| 520 | 415 | } |
| 521 | 416 | }; |
| 522 | - | |
| 523 | 417 | // ========== 通用下拉弹窗方法(支持下标) ========== |
| 524 | 418 | const handleActionSheetOpen = (type, idx) => { |
| 525 | - currentDispatchIndex.value = idx; | |
| 526 | - if (type === 'roadName' && !baseForm.workLocation) { | |
| 527 | - return uni.showToast({ title: '请先选择工单位置', icon: 'none' }); | |
| 419 | +currentDispatchIndex.value = idx; | |
| 420 | +if (type === 'roadName' && !baseForm.workLocation) { | |
| 421 | +return uni.showToast({ title: '请先选择工单位置', icon: 'none' }); | |
| 528 | 422 | } |
| 529 | - const configMap = { | |
| 530 | - busiLine: { title: '请选择业务线', list: busiLineOptions.value }, | |
| 531 | - roadName: { title: '请选择道路名称', list: roadNameList.value }, | |
| 532 | - pressingType: { title: '请选择紧急程度', list: pressingTypeList.value } | |
| 423 | +const configMap = { | |
| 424 | +// 【重点改动:业务线弹窗,传入当前下标,过滤掉已占用业务线】 | |
| 425 | +busiLine: { title: '请选择业务线', list: getAvailableBusiLineList(idx) }, | |
| 426 | +roadName: { title: '请选择道路名称', list: roadNameList.value }, | |
| 427 | +pressingType: { title: '请选择紧急程度', list: pressingTypeList.value } | |
| 533 | 428 | }; |
| 534 | - Object.assign(currentActionSheetData, configMap[type], { type, index: idx }); | |
| 535 | - showActionSheet.value = true; | |
| 429 | +Object.assign(currentActionSheetData, configMap[type], { type, index: idx }); | |
| 430 | +showActionSheet.value = true; | |
| 536 | 431 | }; |
| 537 | - | |
| 538 | 432 | const handleActionSheetClose = () => { |
| 539 | - showActionSheet.value = false; | |
| 540 | - Object.assign(currentActionSheetData, { type: '', list: [], title: '', index: 0 }); | |
| 433 | +showActionSheet.value = false; | |
| 434 | +Object.assign(currentActionSheetData, { type: '', list: [], title: '', index: 0 }); | |
| 541 | 435 | }; |
| 542 | - | |
| 543 | 436 | const handleActionSheetSelect = (e) => { |
| 544 | - const { type, index } = currentActionSheetData; | |
| 545 | - const item = dispatchList.value[index]; | |
| 546 | - switch (type) { | |
| 547 | - case 'busiLine': | |
| 548 | - item.busiLineCn = e.name; | |
| 549 | - item.busiLineCode = e.value; | |
| 550 | - handleBusiLineChange(e.value, index); | |
| 551 | - break; | |
| 552 | - case 'roadName': | |
| 553 | - item.roadName = e.name; | |
| 554 | - item.roadId = e.code; | |
| 555 | - console.log('选中道路对象e:', e) | |
| 556 | - console.log('赋值后 roadId:', item.roadName, '类型:', typeof item.roadId) | |
| 557 | - break; | |
| 558 | - case 'pressingType': | |
| 559 | - item.pressingType = e.value; | |
| 560 | - item.pressingTypeName = e.name; | |
| 561 | - break; | |
| 437 | +const { type, index } = currentActionSheetData; | |
| 438 | +const item = dispatchList.value[index]; | |
| 439 | +switch (type) { | |
| 440 | +case 'busiLine': | |
| 441 | +item.busiLineCn = e.name; | |
| 442 | +item.busiLineCode = e.value; | |
| 443 | +handleBusiLineChange(e.value, index); | |
| 444 | +break; | |
| 445 | +case 'roadName': | |
| 446 | +item.roadName = e.name; | |
| 447 | +item.roadId = e.code; | |
| 448 | +console.log('选中道路对象e:', e) | |
| 449 | +console.log('赋值后 roadId:', item.roadName, '类型:', typeof item.roadId) | |
| 450 | +break; | |
| 451 | +case 'pressingType': | |
| 452 | +item.pressingType = e.value; | |
| 453 | +item.pressingTypeName = e.name; | |
| 454 | +break; | |
| 562 | 455 | } |
| 563 | - handleActionSheetClose(); | |
| 456 | +handleActionSheetClose(); | |
| 564 | 457 | }; |
| 565 | - | |
| 566 | 458 | // ========== 页面交互方法 ========== |
| 567 | 459 | const hideKeyboard = () => uni.hideKeyboard(); |
| 568 | - | |
| 569 | 460 | /** 选择地图位置 */ |
| 570 | 461 | const chooseWorkLocation = () => { |
| 571 | - uni.chooseLocation({ | |
| 572 | - success: async (res) => { | |
| 573 | - baseForm.workLocation = res.name; | |
| 574 | - baseForm.lat = res.latitude; | |
| 575 | - baseForm.lon = res.longitude; | |
| 576 | - // 清空所有组道路 | |
| 577 | - dispatchList.value.forEach(item => { | |
| 578 | - item.roadName = ''; | |
| 579 | - item.roadId = 0; | |
| 462 | +uni.chooseLocation({ | |
| 463 | +success: async (res) => { | |
| 464 | +baseForm.workLocation = res.name; | |
| 465 | +baseForm.lat = res.latitude; | |
| 466 | +baseForm.lon = res.longitude; | |
| 467 | +// 清空所有组道路 | |
| 468 | +dispatchList.value.forEach(item => { | |
| 469 | +item.roadName = ''; | |
| 470 | +item.roadId = 0; | |
| 580 | 471 | }) |
| 581 | - workOrderFormRef.value?.validateField(['workLocation']); | |
| 582 | - await getRoadListByBusiLine(); | |
| 472 | +workOrderFormRef.value?.validateField(['workLocation']); | |
| 473 | +await getRoadListByBusiLine(); | |
| 583 | 474 | }, |
| 584 | - fail: (err) => { | |
| 585 | - console.error('选择位置失败:', err); | |
| 586 | - const errMsg = err.errMsg.includes('auth deny') ? '请开启位置权限后重试' : '选择位置失败,请重试'; | |
| 587 | - uni.showToast({ title: errMsg, icon: 'none' }); | |
| 475 | +fail: (err) => { | |
| 476 | +console.error('选择位置失败:', err); | |
| 477 | +const errMsg = err.errMsg.includes('auth deny') ? '请开启位置权限后重试' : '选择位置失败,请重试'; | |
| 478 | +uni.showToast({ title: errMsg, icon: 'none' }); | |
| 588 | 479 | } |
| 589 | 480 | }) |
| 590 | 481 | }; |
| 591 | - | |
| 592 | 482 | /** 重新提交数据回显 */ |
| 593 | 483 | const handleRenewOptions = async (tempKey) => { |
| 594 | - isRenew.value = true; | |
| 595 | - try { | |
| 596 | - const orderData = uni.getStorageSync(tempKey); | |
| 597 | - if (orderData && typeof orderData === 'object') { | |
| 598 | - renewOrderData.value = orderData; | |
| 599 | - await echoOrderData(orderData); // 等待回显执行完毕 | |
| 484 | +isRenew.value = true; | |
| 485 | +try { | |
| 486 | +const orderData = uni.getStorageSync(tempKey); | |
| 487 | +if (orderData && typeof orderData === 'object') { | |
| 488 | +renewOrderData.value = orderData; | |
| 489 | +await echoOrderData(orderData); // 等待回显执行完毕 | |
| 600 | 490 | } else { |
| 601 | - uni.showToast({ title: '工单数据不存在,无法重新提交', icon: 'none' }); | |
| 602 | - setTimeout(toOrderListPage, 1000); | |
| 491 | +uni.showToast({ title: '工单数据不存在,无法重新提交', icon: 'none' }); | |
| 492 | +setTimeout(toOrderListPage, 1000); | |
| 603 | 493 | } |
| 604 | 494 | } catch (error) { |
| 605 | - console.error('读取工单数据失败:', error); | |
| 606 | - uni.showToast({ title: '数据读取异常,无法重新提交', icon: 'none' }); | |
| 607 | - setTimeout(toOrderListPage, 1000); | |
| 495 | +console.error('读取工单数据失败:', error); | |
| 496 | +uni.showToast({ title: '数据读取异常,无法重新提交', icon: 'none' }); | |
| 497 | +setTimeout(toOrderListPage, 1000); | |
| 608 | 498 | } finally { |
| 609 | - uni.removeStorageSync(tempKey); | |
| 499 | +uni.removeStorageSync(tempKey); | |
| 610 | 500 | } |
| 611 | 501 | }; |
| 612 | - | |
| 613 | - | |
| 614 | 502 | /** 工单数据回显 */ |
| 615 | 503 | const echoOrderData = async (orderItem) => { |
| 616 | - const busiCode = orderItem.busiLine || ''; | |
| 617 | - // 回显公共字段 | |
| 618 | - baseForm.workLocation = orderItem.lonLatAddress || orderItem.roadName || ''; | |
| 619 | - baseForm.problemDesc = orderItem.remark || ''; | |
| 620 | - baseForm.lat = orderItem.lat || 0; | |
| 621 | - baseForm.lon = orderItem.lon || 0; | |
| 622 | - console.log(orderItem) | |
| 623 | - console.log(orderItem.orderCode) | |
| 624 | - | |
| 625 | - // 回显第一组派单 | |
| 626 | - if(dispatchList.value[0]){ | |
| 627 | - const firstItem = dispatchList.value[0] | |
| 628 | - firstItem.busiLineCn = busiLineEnToCn(busiCode); | |
| 629 | - firstItem.busiLineCode = busiCode; | |
| 630 | - firstItem.roadId = orderItem.roadId || 0; | |
| 631 | - | |
| 632 | - firstItem.roadName = orderItem.roadName || ''; | |
| 633 | - firstItem.orderName = orderItem.orderName || ''; | |
| 634 | - firstItem.orderCode = orderItem.orderCode || ''; // 回显编码 | |
| 635 | - firstItem.pressingType = orderItem.pressingType || ''; | |
| 636 | - firstItem.pressingTypeName = uni.$dict.getDictLabel(CONST.DICT_PRESSING_TYPE, orderItem.pressingType) || ''; | |
| 637 | - firstItem.expectedFinishDate = orderItem.expectedFinishDate ? formatDate(orderItem.expectedFinishDate) : ''; | |
| 504 | +const busiCode = orderItem.busiLine || ''; | |
| 505 | +// 回显公共字段 | |
| 506 | +baseForm.workLocation = orderItem.lonLatAddress || orderItem.roadName || ''; | |
| 507 | +baseForm.problemDesc = orderItem.remark || ''; | |
| 508 | +baseForm.lat = orderItem.lat || 0; | |
| 509 | +baseForm.lon = orderItem.lon || 0; | |
| 510 | +console.log(orderItem) | |
| 511 | +console.log(orderItem.orderCode) | |
| 512 | +// 回显第一组派单 | |
| 513 | +if (dispatchList.value[0]) { | |
| 514 | +const firstItem = dispatchList.value[0] | |
| 515 | +firstItem.busiLineCn = busiLineEnToCn(busiCode); | |
| 516 | +firstItem.busiLineCode = busiCode; | |
| 517 | +firstItem.roadId = orderItem.roadId || 0; | |
| 518 | +firstItem.roadName = orderItem.roadName || ''; | |
| 519 | +firstItem.orderName = orderItem.orderName || ''; | |
| 520 | +firstItem.orderCode = orderItem.orderCode || ''; // 回显编码 | |
| 521 | +firstItem.pressingType = orderItem.pressingType || ''; | |
| 522 | +firstItem.pressingTypeName = uni.$dict.getDictLabel(CONST.DICT_PRESSING_TYPE, orderItem.pressingType) || ''; | |
| 523 | +firstItem.expectedFinishDate = orderItem.expectedFinishDate ? formatDate(orderItem.expectedFinishDate) : ''; | |
| 638 | 524 | } |
| 639 | - | |
| 640 | - // 图片回显 | |
| 641 | - if (Array.isArray(orderItem.problemsImgs) && orderItem.problemsImgs.length) { | |
| 642 | - const imgList = orderItem.problemsImgs.map((url, idx) => ({ url, name: `renew_img_${idx}`, status: 'success' })); | |
| 643 | - problemImgs.imgList.value = imgList; | |
| 644 | - problemImgs.rawImgList.value = imgList; | |
| 525 | +// 图片回显 | |
| 526 | +if (Array.isArray(orderItem.problemsImgs) && orderItem.problemsImgs.length) { | |
| 527 | +const imgList = orderItem.problemsImgs.map((url, idx) => ({ url, name: `renew_img_${idx}`, status: 'success' })); | |
| 528 | +problemImgs.imgList.value = imgList; | |
| 529 | +problemImgs.rawImgList.value = imgList; | |
| 645 | 530 | } |
| 646 | - | |
| 647 | - // ========== 关键修复:业务线加载完级联数据后,回填 cascaderValue ========== | |
| 648 | - if (busiCode) { | |
| 649 | - // 先加载级联选项 | |
| 650 | - await loadProblemCascader(busiCode); | |
| 651 | - // 根据回显的 orderCode 反向匹配级联路径,赋值给 cascaderValue | |
| 652 | - const targetCode = orderItem.orderCode | |
| 653 | - if (targetCode && cascaderOptions.value.length) { | |
| 654 | - // 递归查找级联选中路径 | |
| 655 | - const findCascaderPath = (list, targetVal, path = []) => { | |
| 656 | - for (const node of list) { | |
| 657 | - const newPath = [...path, node.value] | |
| 658 | - if (node.value === targetVal) { | |
| 659 | - return newPath | |
| 531 | +// ========== 关键修复:业务线加载完级联数据后,回填 cascaderValue ========== | |
| 532 | +if (busiCode) { | |
| 533 | +// 先加载级联选项 | |
| 534 | +await loadProblemCascader(busiCode); | |
| 535 | +// 根据回显的 orderCode 反向匹配级联路径,赋值给 cascaderValue | |
| 536 | +const targetCode = orderItem.orderCode | |
| 537 | +if (targetCode && cascaderOptions.value.length) { | |
| 538 | +// 递归查找级联选中路径 | |
| 539 | +const findCascaderPath = (list, targetVal, path = []) => { | |
| 540 | +for (const node of list) { | |
| 541 | +const newPath = [...path, node.value] | |
| 542 | +if (node.value === targetVal) { | |
| 543 | +return newPath | |
| 660 | 544 | } |
| 661 | - if (node.children && node.children.length) { | |
| 662 | - const res = findCascaderPath(node.children, targetVal, newPath) | |
| 663 | - if (res) return res | |
| 545 | +if (node.children && node.children.length) { | |
| 546 | +const res = findCascaderPath(node.children, targetVal, newPath) | |
| 547 | +if (res) return res | |
| 664 | 548 | } |
| 665 | 549 | } |
| 666 | - return null | |
| 550 | +return null | |
| 667 | 551 | } |
| 668 | - const selectPath = findCascaderPath(cascaderOptions.value, targetCode) | |
| 669 | - if (selectPath) { | |
| 670 | - cascaderValue.value = selectPath | |
| 552 | +const selectPath = findCascaderPath(cascaderOptions.value, targetCode) | |
| 553 | +if (selectPath) { | |
| 554 | +cascaderValue.value = selectPath | |
| 671 | 555 | } |
| 672 | 556 | } |
| 673 | 557 | } |
| 674 | - | |
| 675 | - if (orderItem.lat && orderItem.lon) getRoadListByBusiLine(); | |
| 558 | +if (orderItem.lat && orderItem.lon) getRoadListByBusiLine(); | |
| 676 | 559 | }; |
| 677 | - | |
| 678 | 560 | // ========== 接口分发(按角色) ========== |
| 679 | 561 | const callApiByRole = async (params, isRenewFlag) => { |
| 680 | - if (isRenewFlag) { | |
| 681 | - if (USER_ROLES.includes(CONST.ROLE.MANAGER)) return await daquUniversalApproval(params); | |
| 682 | - if (USER_ROLES.includes(CONST.ROLE.INSPECTOR)) return await dcyUniversalApproval(params); | |
| 683 | - if (USER_ROLES.includes(CONST.ROLE.LEADER)) return await dcyUniversalApproval(params); | |
| 684 | - if (USER_ROLES.includes(CONST.ROLE.PATROL)) return await qyUniversalApproval(params); | |
| 562 | +if (isRenewFlag) { | |
| 563 | +if (USER_ROLES.includes(CONST.ROLE.MANAGER)) return await daquUniversalApproval(params); | |
| 564 | +if (USER_ROLES.includes(CONST.ROLE.INSPECTOR)) return await dcyUniversalApproval(params); | |
| 565 | +if (USER_ROLES.includes(CONST.ROLE.LEADER)) return await dcyUniversalApproval(params); | |
| 566 | +if (USER_ROLES.includes(CONST.ROLE.PATROL)) return await qyUniversalApproval(params); | |
| 685 | 567 | } else { |
| 686 | - console.log('dasda') | |
| 687 | - console.log(CONST.ROLE.INSPECTOR) | |
| 688 | - if (USER_ROLES.includes(CONST.ROLE.MANAGER)) return await daquWorkorderCreate(params); | |
| 689 | - // if (USER_ROLES.includes(CONST.ROLE.INSPECTOR)) return await dcyWorkorderCreate(params); | |
| 690 | - if (USER_ROLES.includes(CONST.ROLE.INSPECTOR)) return await dycCreateBatchNew(params); | |
| 691 | - if (USER_ROLES.includes(CONST.ROLE.LEADER)) return await dycCreateBatchNew(params); | |
| 692 | - if (USER_ROLES.includes(CONST.ROLE.PATROL)) return await qyWorkorderCreate(params); | |
| 693 | - | |
| 694 | - | |
| 568 | +console.log('dasda') | |
| 569 | +console.log(CONST.ROLE.INSPECTOR) | |
| 570 | +if (USER_ROLES.includes(CONST.ROLE.MANAGER)) return await daquWorkorderCreate(params); | |
| 571 | +// if (USER_ROLES.includes(CONST.ROLE.INSPECTOR)) return await dcyWorkorderCreate(params); | |
| 572 | +if (USER_ROLES.includes(CONST.ROLE.INSPECTOR)) return await dycCreateBatchNew(params); | |
| 573 | +if (USER_ROLES.includes(CONST.ROLE.LEADER)) return await dycCreateBatchNew(params); | |
| 574 | +if (USER_ROLES.includes(CONST.ROLE.PATROL)) return await qyWorkorderCreate(params); | |
| 695 | 575 | } |
| 696 | 576 | }; |
| 697 | - | |
| 698 | 577 | // ========== 提交工单(纯数组提交,无 dispatchList,校验 roadId 非0) ========== |
| 699 | 578 | const submitWorkOrder = async () => { |
| 700 | - try { | |
| 701 | - // 校验公共表单 | |
| 702 | - await workOrderFormRef.value.validate(); | |
| 703 | - | |
| 704 | - // 校验:同一业务线 + 同一问题类型 不允许重复 | |
| 579 | +try { | |
| 580 | +// 校验公共表单 | |
| 581 | +await workOrderFormRef.value.validate(); | |
| 582 | +// 校验:同一业务线 + 同一问题类型 不允许重复 | |
| 705 | 583 | const list = dispatchList.value; |
| 706 | 584 | const keySet = new Set(); |
| 707 | 585 | let hasRepeat = false; |
| 708 | - | |
| 709 | 586 | for (const item of list) { |
| 710 | - const busi = (item.busiLineCn || '').trim(); | |
| 711 | - const type = (item.orderName || '').trim(); | |
| 712 | - // 拼接唯一标识 | |
| 713 | - const uniqueKey = `${busi}_${type}`; | |
| 714 | - if (keySet.has(uniqueKey)) { | |
| 715 | - hasRepeat = true; | |
| 716 | - break; | |
| 717 | - } | |
| 718 | - keySet.add(uniqueKey); | |
| 719 | -} | |
| 720 | - | |
| 587 | +const busi = (item.busiLineCn || '').trim(); | |
| 588 | +const type = (item.orderName || '').trim(); | |
| 589 | +// 拼接唯一标识 | |
| 590 | +const uniqueKey = `${busi}_${type}`; | |
| 591 | +if (keySet.has(uniqueKey)) { | |
| 592 | +hasRepeat = true; | |
| 593 | +break; | |
| 594 | + } | |
| 595 | +keySet.add(uniqueKey); | |
| 596 | + } | |
| 721 | 597 | if (hasRepeat) { |
| 722 | - uni.showToast({ | |
| 723 | - title: '存在同业务同问题类型,请修改后再提交', | |
| 724 | - icon: 'none' | |
| 725 | - }); | |
| 726 | - return; | |
| 727 | -} | |
| 728 | - // ============================================== | |
| 729 | - | |
| 730 | - // 组装纯数组,不再包裹 dispatchList | |
| 731 | - const submitArr = dispatchList.value.map(item => { | |
| 732 | - return { | |
| 733 | - roadId: item.roadId, | |
| 734 | - roadName: item.roadName, | |
| 735 | - orderName: item.orderName, | |
| 736 | - orderCode: item.orderCode, | |
| 737 | - pressingType: item.pressingType, | |
| 738 | - busiLine: busiLineCnToEn(item.busiLineCn), | |
| 739 | - busiType: busiLineCnToEn(item.busiLineCn), | |
| 740 | - expectedFinishDate: dateToTimestamp(item.expectedFinishDate), | |
| 741 | - problemsImgs: problemImgs.getSuccessImgUrls(), | |
| 742 | - remark: baseForm.problemDesc.trim(), | |
| 743 | - latLonType: 2, | |
| 744 | - lat: baseForm.lat, | |
| 745 | - lon: baseForm.lon, | |
| 746 | - lonLatAddress: baseForm.workLocation, | |
| 747 | - sourceId: 1, | |
| 748 | - sourceName: item.busiLineCn | |
| 598 | +uni.showToast({ | |
| 599 | +title: '存在同业务同问题类型,请修改后再提交', | |
| 600 | +icon: 'none' | |
| 601 | + }); | |
| 602 | +return; | |
| 603 | + } | |
| 604 | +// ============================================== | |
| 605 | +// 组装纯数组,不再包裹 dispatchList | |
| 606 | +const submitArr = dispatchList.value.map(item => { | |
| 607 | +return { | |
| 608 | +roadId: item.roadId, | |
| 609 | +roadName: item.roadName, | |
| 610 | +orderName: item.orderName, | |
| 611 | +orderCode: item.orderCode, | |
| 612 | +pressingType: item.pressingType, | |
| 613 | +busiLine: busiLineCnToEn(item.busiLineCn), | |
| 614 | +busiType: busiLineCnToEn(item.busiLineCn), | |
| 615 | +expectedFinishDate: dateToTimestamp(item.expectedFinishDate), | |
| 616 | +problemsImgs: problemImgs.getSuccessImgUrls(), | |
| 617 | +remark: baseForm.problemDesc.trim(), | |
| 618 | +latLonType: 2, | |
| 619 | +lat: baseForm.lat, | |
| 620 | +lon: baseForm.lon, | |
| 621 | +lonLatAddress: baseForm.workLocation, | |
| 622 | +sourceId: 1, | |
| 623 | +sourceName: item.busiLineCn | |
| 749 | 624 | } |
| 750 | 625 | }); |
| 751 | - | |
| 752 | - // 逐项校验:保证 roadId 不为 0,以及其他必填项 | |
| 753 | - for (let i = 0; i < submitArr.length; i++) { | |
| 754 | - const row = submitArr[i]; | |
| 755 | - const num = i + 1; | |
| 756 | - if (!row.roadId || row.roadId === 0) { | |
| 757 | - uni.showToast({ title: `第${num}条派单请选择道路名称`, icon: 'none' }); | |
| 758 | - return; | |
| 626 | +// 逐项校验:保证 roadId 不为 0,以及其他必填项 | |
| 627 | +for (let i = 0; i < submitArr.length; i++) { | |
| 628 | +const row = submitArr[i]; | |
| 629 | +const num = i + 1; | |
| 630 | +if (!row.roadId || row.roadId === 0) { | |
| 631 | +uni.showToast({ title: `第${num}条派单请选择道路名称`, icon: 'none' }); | |
| 632 | +return; | |
| 759 | 633 | } |
| 760 | - if (!row.busiLine) { | |
| 761 | - uni.showToast({ title: `第${num}条派单请选择业务线`, icon: 'none' }); | |
| 762 | - return; | |
| 634 | +if (!row.busiLine) { | |
| 635 | +uni.showToast({ title: `第${num}条派单请选择业务线`, icon: 'none' }); | |
| 636 | +return; | |
| 763 | 637 | } |
| 764 | - if (!row.orderCode) { | |
| 765 | - uni.showToast({ title: `第${num}条派单请选择问题类型`, icon: 'none' }); | |
| 766 | - return; | |
| 638 | +if (!row.orderCode) { | |
| 639 | +uni.showToast({ title: `第${num}条派单请选择问题类型`, icon: 'none' }); | |
| 640 | +return; | |
| 767 | 641 | } |
| 768 | - if (!row.pressingType) { | |
| 769 | - uni.showToast({ title: `第${num}条派单请选择紧急程度`, icon: 'none' }); | |
| 770 | - return; | |
| 642 | +if (!row.pressingType) { | |
| 643 | +uni.showToast({ title: `第${num}条派单请选择紧急程度`, icon: 'none' }); | |
| 644 | +return; | |
| 771 | 645 | } |
| 772 | 646 | } |
| 773 | - | |
| 774 | - uni.showLoading({ title: '提交中...', mask: true }); | |
| 775 | - | |
| 776 | - let submitParams; | |
| 777 | - if (isRenew.value) { | |
| 778 | - // 重新提交:外层对象 + list 承载数组 | |
| 779 | - submitParams = { | |
| 780 | - workerDataId: renewOrderData.value.id, | |
| 781 | - taskKey: renewOrderData.value.taskKey, | |
| 782 | - taskId: renewOrderData.value.taskId, | |
| 783 | - operateType: nextStepMap[renewOrderData.value.taskKey]?.operateTypeRenew || '', | |
| 784 | - agree: 0, | |
| 785 | - reason: baseForm.problemDesc.trim(), | |
| 786 | - ...submitArr[0] | |
| 647 | +uni.showLoading({ title: '提交中...', mask: true }); | |
| 648 | +let submitParams; | |
| 649 | +if (isRenew.value) { | |
| 650 | +// 重新提交:外层对象 + list 承载数组 | |
| 651 | +submitParams = { | |
| 652 | +workerDataId: renewOrderData.value.id, | |
| 653 | +taskKey: renewOrderData.value.taskKey, | |
| 654 | +taskId: renewOrderData.value.taskId, | |
| 655 | +operateType: nextStepMap[renewOrderData.value.taskKey]?.operateTypeRenew || '', | |
| 656 | +agree: 0, | |
| 657 | +reason: baseForm.problemDesc.trim(), | |
| 658 | +...submitArr[0] | |
| 787 | 659 | }; |
| 788 | 660 | } else { |
| 789 | - // 新建工单:根节点直接为纯数组 | |
| 790 | - submitParams = submitArr; | |
| 661 | +// 新建工单:根节点直接为纯数组 | |
| 662 | +submitParams = submitArr; | |
| 791 | 663 | } |
| 792 | - | |
| 793 | - console.log(submitParams) | |
| 794 | - | |
| 795 | - await callApiByRole(submitParams, isRenew.value); | |
| 796 | - | |
| 797 | - uni.showToast({ | |
| 798 | - title: isRenew.value ? '重新提交成功' : '工单提交成功', | |
| 799 | - icon: 'success', | |
| 800 | - duration: 1000 | |
| 664 | +console.log(submitParams) | |
| 665 | +await callApiByRole(submitParams, isRenew.value); | |
| 666 | +uni.showToast({ | |
| 667 | +title: isRenew.value ? '重新提交成功' : '工单提交成功', | |
| 668 | +icon: 'success', | |
| 669 | +duration: 1000 | |
| 801 | 670 | }); |
| 802 | - setTimeout(toOrderListPage, 1000); | |
| 671 | +setTimeout(toOrderListPage, 1000); | |
| 803 | 672 | } catch (error) { |
| 804 | - uni.hideLoading(); | |
| 805 | - if (!Array.isArray(error)) { | |
| 806 | - console.error(isRenew.value ? '工单重新提交失败:' : '工单提交失败:', error); | |
| 807 | - uni.showToast({ | |
| 808 | - title: error.msg || '提交失败,请重试', | |
| 809 | - icon: 'none', | |
| 810 | - duration: 2000 | |
| 673 | +uni.hideLoading(); | |
| 674 | +if (!Array.isArray(error)) { | |
| 675 | +console.error(isRenew.value ? '工单重新提交失败:' : '工单提交失败:', error); | |
| 676 | +uni.showToast({ | |
| 677 | +title: error.msg || '提交失败,请重试', | |
| 678 | +icon: 'none', | |
| 679 | +duration: 2000 | |
| 811 | 680 | }); |
| 812 | 681 | } |
| 813 | 682 | } |
| ... | ... | @@ -866,4 +735,4 @@ if (hasRepeat) { |
| 866 | 735 | .add-dispatch-btn-wrap { |
| 867 | 736 | margin: 20rpx 0; |
| 868 | 737 | } |
| 869 | -</style> | |
| 870 | 738 | \ No newline at end of file |
| 739 | +</style> | ... | ... |
pages-sub/problem/work-order-manage/add-order.vue
| ... | ... | @@ -3,20 +3,18 @@ |
| 3 | 3 | <view class="work-order-form-content commonPageLRpadding"> |
| 4 | 4 | <up-form label-position="left" :model="workOrderForm" ref="workOrderFormRef" labelWidth="200rpx"> |
| 5 | 5 | <!-- 业务线单选框:移入form内,作为第一个表单项(核心调整) --> |
| 6 | - <up-form-item label="业务线" prop="busiLineCn" border-bottom required> | |
| 7 | - <up-radio-group v-model="workOrderForm.busiLineCn" placement="row" @change="handleBusiLineChange"> | |
| 8 | - <up-radio v-for="item in busiLineOptions" :key="item.name" :label="item.name" :name="item.name"></up-radio> | |
| 6 | + <up-form-item label="业务线" prop="busiLine" border-bottom required> | |
| 7 | + <up-radio-group v-model="busiLineDisplay" placement="row" @change="handleBusiLineChange"> | |
| 8 | + <up-radio v-for="item in busiLineOptions" :key="item.value" :label="item.label" | |
| 9 | + :name="item.label"></up-radio> | |
| 9 | 10 | </up-radio-group> |
| 10 | - | |
| 11 | 11 | </up-form-item> |
| 12 | - | |
| 13 | 12 | <!-- 1. 工单位置(地图选择) --> |
| 14 | 13 | <up-form-item label="工单位置" prop="workLocation" border-bottom required |
| 15 | 14 | @click="chooseWorkLocation(); hideKeyboard()"> |
| 16 | 15 | <up-input v-model="workOrderForm.workLocation" border="none" readonly suffix-icon="map-fill" |
| 17 | 16 | placeholder="点击选择工单位置"></up-input> |
| 18 | 17 | </up-form-item> |
| 19 | - | |
| 20 | 18 | <!-- 2. 道路名称(下拉框) --> |
| 21 | 19 | <up-form-item label="道路名称" prop="roadName" border-bottom required |
| 22 | 20 | @click="handleActionSheetOpen('roadName'); hideKeyboard()"> |
| ... | ... | @@ -26,7 +24,6 @@ |
| 26 | 24 | <up-icon name="arrow-right" size="16"></up-icon> |
| 27 | 25 | </template> |
| 28 | 26 | </up-form-item> |
| 29 | - | |
| 30 | 27 | <!-- 3. 问题类型(下拉框) --> |
| 31 | 28 | <up-form-item label="问题类型" prop="orderName" border-bottom required |
| 32 | 29 | @click="handleActionSheetOpen('orderName'); hideKeyboard()"> |
| ... | ... | @@ -36,7 +33,6 @@ |
| 36 | 33 | <up-icon name="arrow-right" size="16"></up-icon> |
| 37 | 34 | </template> |
| 38 | 35 | </up-form-item> |
| 39 | - | |
| 40 | 36 | <!-- 新增:紧急程度选择 --> |
| 41 | 37 | <up-form-item label="紧急程度" prop="pressingTypeName" border-bottom required |
| 42 | 38 | @click="handleActionSheetOpen('pressingType'); hideKeyboard()"> |
| ... | ... | @@ -46,13 +42,11 @@ |
| 46 | 42 | <up-icon name="arrow-right" size="16"></up-icon> |
| 47 | 43 | </template> |
| 48 | 44 | </up-form-item> |
| 49 | - | |
| 50 | 45 | <!-- 4. 情况描述(文本域) --> |
| 51 | 46 | <up-form-item label="情况描述" prop="problemDesc" required> |
| 52 | 47 | <up-textarea placeholder="请输入情况描述(最多200字)" v-model.trim="workOrderForm.problemDesc" count maxlength="200" |
| 53 | 48 | rows="4" @blur="() => workOrderFormRef.validateField('problemDesc')"></up-textarea> |
| 54 | 49 | </up-form-item> |
| 55 | - | |
| 56 | 50 | <!-- 问题照片(核心修复:绑定纯数组) --> |
| 57 | 51 | <up-form-item label="问题照片" prop="problemImgs" border-bottom required> |
| 58 | 52 | <up-upload :file-list="problemImgs.imgList.value || []" @after-read="problemImgs.uploadImgs" |
| ... | ... | @@ -60,7 +54,6 @@ |
| 60 | 54 | :max-count="problemImgs.uploadConfig.maxCount" :upload-text="problemImgs.uploadConfig.uploadText" |
| 61 | 55 | :size-type="problemImgs.uploadConfig.sizeType"></up-upload> |
| 62 | 56 | </up-form-item> |
| 63 | - | |
| 64 | 57 | <!-- 完成时间 --> |
| 65 | 58 | <up-form-item label="希望完成时间" prop="expectedFinishDate" @click="show = true; hideKeyboard()"> |
| 66 | 59 | <up-input v-model="workOrderForm.expectedFinishDate" border="none" readonly placeholder="点击选择时间"></up-input> |
| ... | ... | @@ -74,25 +67,21 @@ |
| 74 | 67 | </up-form-item> |
| 75 | 68 | </up-form> |
| 76 | 69 | </view> |
| 77 | - | |
| 78 | 70 | <!-- 底部提交按钮 --> |
| 79 | 71 | <view class="fixed-bottom-btn-wrap"> |
| 80 | 72 | <up-button type="primary" text="提交工单" @click="submitWorkOrder"></up-button> |
| 81 | 73 | </view> |
| 82 | - | |
| 83 | 74 | <!-- 合并后的通用下拉弹窗 --> |
| 84 | 75 | <up-action-sheet :show="showActionSheet" :actions="currentActionSheetData.list" |
| 85 | 76 | :title="currentActionSheetData.title" @close="handleActionSheetClose" |
| 86 | 77 | @select="handleActionSheetSelect"></up-action-sheet> |
| 87 | - | |
| 88 | 78 | <!-- 完成时间选择器 --> |
| 89 | 79 | <up-datetime-picker :show="show" v-model="expectedFinishDate" mode="datetime" :min-date="new Date()" |
| 90 | 80 | @cancel="show = false" @confirm="expectedFinishDateConfirm"></up-datetime-picker> |
| 91 | 81 | </view> |
| 92 | 82 | </template> |
| 93 | - | |
| 94 | 83 | <script setup> |
| 95 | -import { ref, reactive } from 'vue' | |
| 84 | +import { ref, reactive, computed } from 'vue' | |
| 96 | 85 | import { onReady, onShow, onLoad } from '@dcloudio/uni-app'; |
| 97 | 86 | import { useUploadImgs } from '@/common/utils/useUploadImgs' |
| 98 | 87 | import { getRoadListByLatLng } from '@/api/common' |
| ... | ... | @@ -100,52 +89,16 @@ import { universalApproval, workorderCreate } from '@/api/work-order-manage/work |
| 100 | 89 | import { timeFormat } from '@/uni_modules/uview-plus' |
| 101 | 90 | import { nextStepMap } from '@/common/utils/common' |
| 102 | 91 | import { useUserStore } from '@/pinia/user'; |
| 103 | - | |
| 104 | 92 | // ========== 状态管理 ========== |
| 105 | 93 | const userStore = useUserStore(); |
| 106 | - | |
| 107 | -// ========== 业务线相关状态(核心改造:已整合到workOrderForm中,删除冗余selectedBusiLine) ========== | |
| 108 | -// 业务线映射表(键:英文标识,值:中文名称;新增反向映射:中文->英文) | |
| 109 | -const busiLineMap = ref({ | |
| 110 | - 'yl': '园林', | |
| 111 | - 'sz': '市政', | |
| 112 | - 'wy': '物业', | |
| 113 | - 'zx': '秩序管理', | |
| 114 | - 'hj': '环境卫生', | |
| 115 | - 'yw': '常规运维', | |
| 116 | - '园林': 'yl', | |
| 117 | - '市政': 'sz', | |
| 118 | - '物业': 'wy', | |
| 119 | - '秩序管理': 'zx', | |
| 120 | - '环境卫生': 'hj', | |
| 121 | - '常规运维': 'yw' | |
| 122 | -}); | |
| 123 | - | |
| 124 | -// 业务线选项列表(仅保留name字段=中文名称,适配单选框配置) | |
| 125 | -const busiLineOptions = ref([]); | |
| 126 | -// 格式化业务线选项(兼容1个/2个/3个的情况,生成中文名称列表) | |
| 127 | -const formatBusiLineOptions = () => { | |
| 128 | - if (!userStore.userInfo?.user?.busiLine) { | |
| 129 | - busiLineOptions.value = []; | |
| 130 | - return; | |
| 131 | - } | |
| 132 | - const rawBusiLines = userStore.userInfo.user.busiLine.split(','); // 拆分字符串为数组 ["yl", "sz", "wy"] | |
| 133 | - console.log(rawBusiLines) | |
| 134 | - // 核心改造:仅生成name字段(值为中文名称),适配单选框:label="item.name" | |
| 135 | - busiLineOptions.value = rawBusiLines.map(item => ({ | |
| 136 | - name: busiLineMap.value[item.trim()] // name=中文名称(园林/市政/物业) | |
| 137 | - })); | |
| 138 | - console.log(busiLineOptions.value) | |
| 139 | -}; | |
| 140 | - | |
| 141 | -// ========== 工具方法:通过中文名称获取对应的英文标识 ========== | |
| 142 | -const getBusiLineEnByCn = (cnName) => { | |
| 143 | - return busiLineMap.value[cnName] || ''; | |
| 144 | -}; | |
| 145 | - | |
| 94 | +// 业务线选项,直接从pinia dictList拿 | |
| 95 | +const busiLineOptions = computed(() => { | |
| 96 | + return userStore.dictList || [] | |
| 97 | +}) | |
| 146 | 98 | // ========== 表单Ref ========== |
| 147 | 99 | const workOrderFormRef = ref(null) |
| 148 | - | |
| 100 | +// 业务线单选框展示用(存中文label) | |
| 101 | +const busiLineDisplay = ref('') | |
| 149 | 102 | // ========== 公共上传逻辑复用 ========== |
| 150 | 103 | const problemImgs = useUploadImgs({ |
| 151 | 104 | maxCount: 3, |
| ... | ... | @@ -154,12 +107,10 @@ const problemImgs = useUploadImgs({ |
| 154 | 107 | formRef: workOrderFormRef, |
| 155 | 108 | fieldName: 'problemImgs' |
| 156 | 109 | }) |
| 157 | - | |
| 158 | 110 | // 无需再手动初始化普通数组,直接使用 ref 响应式数据 |
| 159 | 111 | if (!Array.isArray(problemImgs.rawImgList.value)) { |
| 160 | 112 | problemImgs.rawImgList.value = []; |
| 161 | 113 | } |
| 162 | - | |
| 163 | 114 | // ========== 页面状态 ========== |
| 164 | 115 | // 通用弹窗控制 |
| 165 | 116 | const showActionSheet = ref(false) |
| ... | ... | @@ -172,19 +123,16 @@ const currentActionSheetData = reactive({ |
| 172 | 123 | // 完成时间选择器控制 |
| 173 | 124 | const show = ref(false) |
| 174 | 125 | const expectedFinishDate = ref(Date.now()) |
| 175 | - | |
| 176 | 126 | // ========== 重新提交相关状态(核心:本地存储读取) ========== |
| 177 | 127 | const isRenew = ref(false); // 是否为重新提交状态 |
| 178 | 128 | const renewOrderData = ref(null); // 重新提交的原有工单数据 |
| 179 | - | |
| 180 | 129 | // ========== 下拉列表数据 ========== |
| 181 | 130 | const roadNameList = ref([]) |
| 182 | 131 | const orderNameList = ref([]) |
| 183 | 132 | const pressingTypeList = ref([]) |
| 184 | - | |
| 185 | -// ========== 工单表单数据(核心调整:新增busiLineCn字段,绑定业务线) ========== | |
| 133 | +// ========== 工单表单数据 ========== | |
| 186 | 134 | const workOrderForm = reactive({ |
| 187 | - busiLineCn: '', // 新增:业务线中文名称(用于form绑定、校验、回显) | |
| 135 | + busiLine: '', // 业务线value,yl / sz / wy | |
| 188 | 136 | roadId: 0, // 原有字段 |
| 189 | 137 | roadName: '', // 原有字段 |
| 190 | 138 | workLocation: '', // 原有字段 |
| ... | ... | @@ -196,10 +144,9 @@ const workOrderForm = reactive({ |
| 196 | 144 | lon: 0, // 原有字段 |
| 197 | 145 | expectedFinishDate: '', // 原有字段 |
| 198 | 146 | }) |
| 199 | - | |
| 200 | -// ========== 表单校验规则(核心调整:新增busiLineCn校验规则) ========== | |
| 147 | +// ========== 表单校验规则 ========== | |
| 201 | 148 | const workOrderFormRules = reactive({ |
| 202 | - busiLineCn: [ // 新增:业务线必选校验 | |
| 149 | + busiLine: [ | |
| 203 | 150 | { type: 'string', required: true, message: '请选择业务线', trigger: ['change', 'blur'] } |
| 204 | 151 | ], |
| 205 | 152 | workLocation: [ |
| ... | ... | @@ -220,30 +167,25 @@ const workOrderFormRules = reactive({ |
| 220 | 167 | ], |
| 221 | 168 | problemImgs: [problemImgs.imgValidateRule] // 复用上传校验规则 |
| 222 | 169 | }) |
| 223 | - | |
| 224 | 170 | // ========== 生命周期 ========== |
| 225 | -// 页面加载:读取本地存储的工单数据(适配workOrderForm.busiLineCn) | |
| 171 | +// 页面加载 | |
| 226 | 172 | onLoad((options) => { |
| 227 | - // 初始化业务线选项 | |
| 228 | - formatBusiLineOptions(); | |
| 229 | - // 默认选中第一个业务线(赋值给workOrderForm.busiLineCn,核心调整) | |
| 173 | + // 默认选中第一个业务线 | |
| 230 | 174 | if (busiLineOptions.value.length > 0) { |
| 231 | - workOrderForm.busiLineCn = busiLineOptions.value[0].name; | |
| 175 | + // ✅ 业务线:表单存value,单选展示中文label | |
| 176 | + workOrderForm.busiLine = busiLineOptions.value[0].value | |
| 177 | + busiLineDisplay.value = busiLineOptions.value[0].label | |
| 232 | 178 | } |
| 233 | - | |
| 234 | 179 | // 判断是否为重新提交状态 |
| 235 | 180 | console.log(options) |
| 236 | 181 | if (options.isRenew == 1 && options.tempKey) { |
| 237 | 182 | isRenew.value = true; |
| 238 | 183 | const tempKey = options.tempKey; |
| 239 | - | |
| 240 | 184 | // 1. 从本地同步存储中读取完整工单数据 |
| 241 | 185 | try { |
| 242 | 186 | const orderData = uni.getStorageSync(tempKey); |
| 243 | 187 | if (orderData && typeof orderData === 'object') { |
| 244 | 188 | renewOrderData.value = orderData; |
| 245 | - | |
| 246 | - console.log('123213') | |
| 247 | 189 | console.log(orderData) |
| 248 | 190 | // 2. 回显工单数据到表单 |
| 249 | 191 | echoOrderData(renewOrderData.value); |
| ... | ... | @@ -264,7 +206,6 @@ onLoad((options) => { |
| 264 | 206 | } |
| 265 | 207 | } |
| 266 | 208 | }); |
| 267 | - | |
| 268 | 209 | onReady(() => { |
| 269 | 210 | // 设置表单校验规则 |
| 270 | 211 | if (workOrderFormRef.value) { |
| ... | ... | @@ -272,27 +213,26 @@ onReady(() => { |
| 272 | 213 | } |
| 273 | 214 | console.log('工单表单规则初始化完成') |
| 274 | 215 | }) |
| 275 | - | |
| 276 | 216 | onShow(() => { |
| 277 | 217 | console.log(uni.$dict.getDictLabel('ai_image_status', 20)) |
| 278 | 218 | console.log(uni.$dict.getDictSimpleList('work_name')) |
| 279 | - | |
| 280 | 219 | // 初始化问题类型列表 |
| 281 | 220 | orderNameList.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('work_name')) |
| 282 | 221 | console.log('问题类型列表:', orderNameList.value) |
| 283 | - | |
| 284 | 222 | // 初始化紧急程度列表 |
| 285 | 223 | pressingTypeList.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('workorder_pressing_type')) |
| 286 | 224 | console.log('紧急程度列表:', pressingTypeList.value) |
| 287 | 225 | }) |
| 288 | - | |
| 289 | -// ========== 核心方法:工单数据回显(适配workOrderForm.busiLineCn) ========== | |
| 226 | +// ========== 核心方法:工单数据回显 ========== | |
| 290 | 227 | const echoOrderData = (orderItem) => { |
| 291 | - // 1. 回显业务线:将工单的英文busiLine转为中文名称,赋值给workOrderForm.busiLineCn(核心调整) | |
| 228 | + // 业务线回显,后端给的是value,需要自动找到中文赋值给单选框 | |
| 292 | 229 | if (orderItem.busiLine) { |
| 293 | - workOrderForm.busiLineCn = busiLineMap.value[orderItem.busiLine]; // 英文->中文(如wy->物业) | |
| 230 | + workOrderForm.busiLine = orderItem.busiLine; | |
| 231 | + const findItem = busiLineOptions.value.find(item => item.value === orderItem.busiLine) | |
| 232 | + if (findItem) { | |
| 233 | + busiLineDisplay.value = findItem.label | |
| 234 | + } | |
| 294 | 235 | } |
| 295 | - | |
| 296 | 236 | // 2. 原有基础表单字段回显 |
| 297 | 237 | workOrderForm.roadId = orderItem.roadId || 0; |
| 298 | 238 | workOrderForm.roadName = orderItem.roadName || ''; |
| ... | ... | @@ -304,7 +244,6 @@ const echoOrderData = (orderItem) => { |
| 304 | 244 | workOrderForm.lat = orderItem.lat || 0; |
| 305 | 245 | workOrderForm.lon = orderItem.lon || 0; |
| 306 | 246 | workOrderForm.expectedFinishDate = orderItem.expectedFinishDate ? timeFormat(orderItem.expectedFinishDate, 'yyyy-mm-dd hh:MM:ss') : ''; |
| 307 | - | |
| 308 | 247 | // 3. 上传图片回显(兼容useUploadImgs格式) |
| 309 | 248 | if (orderItem.problemsImgs && Array.isArray(orderItem.problemsImgs) && orderItem.problemsImgs.length > 0) { |
| 310 | 249 | const imgList = orderItem.problemsImgs.map((imgUrl, index) => ({ |
| ... | ... | @@ -315,18 +254,22 @@ const echoOrderData = (orderItem) => { |
| 315 | 254 | problemImgs.imgList.value = imgList; |
| 316 | 255 | problemImgs.rawImgList.value = imgList; |
| 317 | 256 | } |
| 318 | - | |
| 319 | - // 4. 自动获取道路列表(修改:传递当前选中的业务线英文标识) | |
| 257 | + // 4. 自动获取道路列表 | |
| 320 | 258 | if (orderItem.lat && orderItem.lon) { |
| 321 | - getRoadListByBusiLine(); // 改用封装后的方法,自动携带选中的业务线 | |
| 259 | + getRoadListByBusiLine(); | |
| 322 | 260 | } |
| 323 | 261 | }; |
| 324 | - | |
| 325 | -// ========== 业务线相关方法(适配workOrderForm.busiLineCn) ========== | |
| 262 | +// ========== 业务线相关方法 ========== | |
| 326 | 263 | /** |
| 327 | 264 | * 业务线切换事件:清空道路信息 + 重新请求道路列表 |
| 265 | + * selectedLabel 是选中的中文label | |
| 328 | 266 | */ |
| 329 | -const handleBusiLineChange = () => { | |
| 267 | +const handleBusiLineChange = (selectedLabel) => { | |
| 268 | + // 根据中文label反向找到对应的value存入表单 | |
| 269 | + const selectedItem = busiLineOptions.value.find(item => item.label === selectedLabel) | |
| 270 | + if (selectedItem) { | |
| 271 | + workOrderForm.busiLine = selectedItem.value | |
| 272 | + } | |
| 330 | 273 | // 1. 清空已渲染的道路名称和表单字段 |
| 331 | 274 | workOrderForm.roadName = ''; |
| 332 | 275 | workOrderForm.roadId = 0; |
| ... | ... | @@ -337,26 +280,17 @@ const handleBusiLineChange = () => { |
| 337 | 280 | getRoadListByBusiLine(); |
| 338 | 281 | } |
| 339 | 282 | }; |
| 340 | - | |
| 341 | 283 | /** |
| 342 | - * 封装:根据当前选中业务线(中文)获取英文标识,再请求道路列表 | |
| 284 | + * 封装:根据当前选中业务线,请求道路列表 | |
| 343 | 285 | */ |
| 344 | 286 | const getRoadListByBusiLine = async () => { |
| 345 | - if (!workOrderForm.lat || !workOrderForm.lon) { | |
| 346 | - return; | |
| 347 | - } | |
| 348 | - // 核心:从workOrderForm.busiLineCn获取中文名称,转为英文标识(核心调整) | |
| 349 | - const busiLineEn = getBusiLineEnByCn(workOrderForm.busiLineCn); | |
| 350 | - if (!busiLineEn) { | |
| 351 | - uni.showToast({ title: '业务线标识异常', icon: 'none' }); | |
| 287 | + if (!workOrderForm.lat || !workOrderForm.lon || !workOrderForm.busiLine) { | |
| 352 | 288 | return; |
| 353 | 289 | } |
| 354 | - | |
| 355 | 290 | try { |
| 356 | 291 | uni.showLoading({ title: '获取道路名称中...' }); |
| 357 | 292 | const roadRes = await getRoadListByLatLng({ |
| 358 | - busiLine: busiLineEn, // 传递英文标识(yl/sz/wy),接口正常使用 | |
| 359 | - // companyCode: 'sls', // 传递英文标识(yl/sz/wy),接口正常使用 | |
| 293 | + busiLine: workOrderForm.busiLine, | |
| 360 | 294 | latitude: workOrderForm.lat, |
| 361 | 295 | longitude: workOrderForm.lon |
| 362 | 296 | }); |
| ... | ... | @@ -378,7 +312,6 @@ const getRoadListByBusiLine = async () => { |
| 378 | 312 | roadNameList.value = [{ name: '获取失败,请重新选择位置', code: '', id: 0 }]; |
| 379 | 313 | } |
| 380 | 314 | }; |
| 381 | - | |
| 382 | 315 | // ========== 通用弹窗方法 ========== |
| 383 | 316 | /** |
| 384 | 317 | * 打开通用下拉弹窗 |
| ... | ... | @@ -389,7 +322,6 @@ const handleActionSheetOpen = (type) => { |
| 389 | 322 | uni.showToast({ title: '请先选择工单位置', icon: 'none' }) |
| 390 | 323 | return |
| 391 | 324 | } |
| 392 | - | |
| 393 | 325 | // 配置当前弹窗参数 |
| 394 | 326 | const configMap = { |
| 395 | 327 | roadName: { |
| ... | ... | @@ -405,13 +337,11 @@ const handleActionSheetOpen = (type) => { |
| 405 | 337 | list: pressingTypeList.value |
| 406 | 338 | } |
| 407 | 339 | } |
| 408 | - | |
| 409 | 340 | currentActionSheetData.type = type |
| 410 | 341 | currentActionSheetData.title = configMap[type].title |
| 411 | 342 | currentActionSheetData.list = configMap[type].list |
| 412 | 343 | showActionSheet.value = true |
| 413 | 344 | } |
| 414 | - | |
| 415 | 345 | /** |
| 416 | 346 | * 关闭通用下拉弹窗 |
| 417 | 347 | */ |
| ... | ... | @@ -422,7 +352,6 @@ const handleActionSheetClose = () => { |
| 422 | 352 | currentActionSheetData.list = [] |
| 423 | 353 | currentActionSheetData.title = '' |
| 424 | 354 | } |
| 425 | - | |
| 426 | 355 | /** |
| 427 | 356 | * 通用下拉弹窗选择事件 |
| 428 | 357 | */ |
| ... | ... | @@ -449,7 +378,6 @@ const handleActionSheetSelect = (e) => { |
| 449 | 378 | // 关闭弹窗 |
| 450 | 379 | showActionSheet.value = false |
| 451 | 380 | } |
| 452 | - | |
| 453 | 381 | /** |
| 454 | 382 | * 返回上一页 |
| 455 | 383 | */ |
| ... | ... | @@ -462,14 +390,12 @@ const navigateBack = () => { |
| 462 | 390 | } |
| 463 | 391 | }); |
| 464 | 392 | } |
| 465 | - | |
| 466 | -// 新增:清除希望完成时间的方法 | |
| 393 | +/** | |
| 394 | + * 清除希望完成时间的方法 | |
| 395 | + */ | |
| 467 | 396 | const clearExpectedFinishDate = () => { |
| 468 | 397 | workOrderForm.expectedFinishDate = '' |
| 469 | - // 清除后触发一次字段校验,确保校验状态更新 | |
| 470 | - // this.$refs.inspectFormRef.validateField('expectedFinishDate') | |
| 471 | 398 | } |
| 472 | - | |
| 473 | 399 | /** |
| 474 | 400 | * 选择工单位置 |
| 475 | 401 | */ |
| ... | ... | @@ -479,15 +405,11 @@ const chooseWorkLocation = () => { |
| 479 | 405 | workOrderForm.roadName = '' |
| 480 | 406 | workOrderForm.roadId = 0 |
| 481 | 407 | roadNameList.value = [] |
| 482 | - | |
| 483 | 408 | workOrderForm.workLocation = res.name |
| 484 | 409 | workOrderForm.lat = res.latitude |
| 485 | 410 | workOrderForm.lon = res.longitude |
| 486 | - | |
| 487 | 411 | workOrderFormRef.value?.validateField('workLocation') |
| 488 | 412 | workOrderFormRef.value?.validateField('roadName') |
| 489 | - | |
| 490 | - // 改用封装后的方法,自动携带选中的业务线英文标识 | |
| 491 | 413 | await getRoadListByBusiLine(); |
| 492 | 414 | }, |
| 493 | 415 | fail: (err) => { |
| ... | ... | @@ -496,7 +418,6 @@ const chooseWorkLocation = () => { |
| 496 | 418 | } |
| 497 | 419 | }) |
| 498 | 420 | } |
| 499 | - | |
| 500 | 421 | /** |
| 501 | 422 | * 完成时间确认 |
| 502 | 423 | */ |
| ... | ... | @@ -505,25 +426,20 @@ const expectedFinishDateConfirm = (e) => { |
| 505 | 426 | workOrderForm.expectedFinishDate = timeFormat(e.value, 'yyyy-mm-dd hh:MM:ss') |
| 506 | 427 | show.value = false |
| 507 | 428 | } |
| 508 | - | |
| 509 | 429 | /** |
| 510 | 430 | * 隐藏键盘 |
| 511 | 431 | */ |
| 512 | 432 | const hideKeyboard = () => { |
| 513 | 433 | uni.hideKeyboard() |
| 514 | 434 | } |
| 515 | - | |
| 516 | 435 | /** |
| 517 | - * 提交工单(区分新增/重新提交,接口隔离:从workOrderForm.busiLineCn取值) | |
| 436 | + * 提交工单 | |
| 518 | 437 | */ |
| 519 | 438 | const submitWorkOrder = async () => { |
| 520 | 439 | try { |
| 521 | - // 先执行表单校验(包含业务线校验) | |
| 440 | + // 先执行表单校验 | |
| 522 | 441 | await workOrderFormRef.value.validate() |
| 523 | - | |
| 524 | - // 核心:从workOrderForm.busiLineCn获取中文名称,转为英文标识(核心调整) | |
| 525 | - const busiLineEn = getBusiLineEnByCn(workOrderForm.busiLineCn); | |
| 526 | - if (!busiLineEn) { | |
| 442 | + if (!workOrderForm.busiLine) { | |
| 527 | 443 | uni.showToast({ title: '业务线选择异常,请重新选择', icon: 'none' }); |
| 528 | 444 | return; |
| 529 | 445 | } |
| ... | ... | @@ -532,7 +448,7 @@ const submitWorkOrder = async () => { |
| 532 | 448 | const commonSubmitData = { |
| 533 | 449 | roadId: workOrderForm.roadId, |
| 534 | 450 | roadName: workOrderForm.roadName, |
| 535 | - problemsImgs: problemImgs.getSuccessImgUrls(), // 复用上传逻辑的URL获取方法 | |
| 451 | + problemsImgs: problemImgs.getSuccessImgUrls(), | |
| 536 | 452 | remark: workOrderForm.problemDesc.trim(), |
| 537 | 453 | latLonType: 2, |
| 538 | 454 | lat: workOrderForm.lat, |
| ... | ... | @@ -542,8 +458,8 @@ const submitWorkOrder = async () => { |
| 542 | 458 | orderName: workOrderForm.orderName, |
| 543 | 459 | expectedFinishDate: workOrderForm.expectedFinishDate == "" ? "" : new Date(workOrderForm.expectedFinishDate).getTime(), |
| 544 | 460 | sourceId: 1, |
| 545 | - sourceName: workOrderForm.busiLineCn, // 从form字段获取业务线中文名称 | |
| 546 | - busiLine: busiLineEn // 传递英文标识(yl/sz/wy),接口正常使用 | |
| 461 | + sourceName: busiLineOptions.value.find(item => item.value === workOrderForm.busiLine)?.label || '', | |
| 462 | + busiLine: workOrderForm.busiLine | |
| 547 | 463 | } |
| 548 | 464 | console.log('工单数据') |
| 549 | 465 | console.log('完成时间:' + workOrderForm.expectedFinishDate) |
| ... | ... | @@ -551,34 +467,28 @@ const submitWorkOrder = async () => { |
| 551 | 467 | // 显示加载中 |
| 552 | 468 | uni.showLoading({ title: '提交中...' }) |
| 553 | 469 | let res |
| 554 | - | |
| 555 | - // 核心:根据状态区分接口调用 | |
| 556 | 470 | if (isRenew.value) { |
| 557 | 471 | // 重新提交:调用 universalApproval 接口 |
| 558 | 472 | const renewSubmitData = { |
| 559 | - // 原有工单必要参数 | |
| 560 | 473 | workerDataId: renewOrderData.value.id, |
| 561 | 474 | taskKey: renewOrderData.value.taskKey, |
| 562 | 475 | taskId: renewOrderData.value.taskId, |
| 563 | 476 | operateType: nextStepMap[renewOrderData.value.taskKey]?.operateTypeRenew || '', |
| 564 | 477 | agree: 0, |
| 565 | 478 | reason: workOrderForm.problemDesc.trim(), |
| 566 | - // 新编辑的工单数据 | |
| 567 | 479 | ...commonSubmitData |
| 568 | 480 | } |
| 569 | 481 | res = await universalApproval(renewSubmitData) |
| 570 | 482 | } else { |
| 571 | - // 新增工单:调用原有 workorderCreate 接口(不影响原有功能) | |
| 483 | + // 新增工单:调用原有 workorderCreate 接口 | |
| 572 | 484 | res = await workorderCreate(commonSubmitData) |
| 573 | 485 | } |
| 574 | - | |
| 575 | 486 | uni.hideLoading() |
| 576 | 487 | uni.showToast({ |
| 577 | 488 | title: isRenew.value ? '重新提交成功' : '工单提交成功', |
| 578 | 489 | icon: 'success', |
| 579 | 490 | duration: 1000 |
| 580 | 491 | }) |
| 581 | - | |
| 582 | 492 | // 延迟跳转回列表页 |
| 583 | 493 | setTimeout(() => { |
| 584 | 494 | uni.reLaunch({ |
| ... | ... | @@ -588,13 +498,12 @@ const submitWorkOrder = async () => { |
| 588 | 498 | } catch (error) { |
| 589 | 499 | // 隐藏加载框 |
| 590 | 500 | uni.hideLoading() |
| 591 | - | |
| 592 | 501 | // 区分是表单校验失败还是接口调用失败 |
| 593 | 502 | if (!Array.isArray(error)) { |
| 594 | 503 | // 接口调用失败 |
| 595 | 504 | console.error(isRenew.value ? '工单重新提交失败:' : '工单提交失败:', error) |
| 596 | 505 | uni.showToast({ |
| 597 | - title: isRenew.value ? error.msg : error.msg, | |
| 506 | + title: error.msg || '提交失败', | |
| 598 | 507 | icon: 'none', |
| 599 | 508 | duration: 2000 |
| 600 | 509 | }) |
| ... | ... | @@ -602,7 +511,6 @@ const submitWorkOrder = async () => { |
| 602 | 511 | } |
| 603 | 512 | } |
| 604 | 513 | </script> |
| 605 | - | |
| 606 | 514 | <style lang="scss" scoped> |
| 607 | 515 | // 全局页面样式 |
| 608 | 516 | .page-container { |
| ... | ... | @@ -610,9 +518,8 @@ const submitWorkOrder = async () => { |
| 610 | 518 | padding-bottom: 100rpx; // 给底部按钮留空间 |
| 611 | 519 | } |
| 612 | 520 | |
| 613 | - | |
| 614 | 521 | // 工单表单内容容器 |
| 615 | 522 | .work-order-form-content { |
| 616 | 523 | background: #fff; |
| 617 | 524 | } |
| 618 | -</style> | |
| 619 | 525 | \ No newline at end of file |
| 526 | +</style> | ... | ... |
pinia/user.js
| 1 | -import { defineStore } from 'pinia'; | |
| 2 | -import cache from '@/common/utils/cache'; | |
| 3 | -import globalConfig from '@/common/config/global'; | |
| 1 | +import { defineStore } from "pinia"; | |
| 2 | +import cache from "@/common/utils/cache"; | |
| 3 | +import globalConfig from "@/common/config/global"; | |
| 4 | 4 | // 新增:导入 refreshToken 接口 |
| 5 | -import { login,smsLogin, getUserInfo, logout, moduleList, getSimpleDictDataList, refreshToken } from '@/api/user'; | |
| 6 | - | |
| 5 | +import { | |
| 6 | + login, | |
| 7 | + smsLogin, | |
| 8 | + getUserInfo, | |
| 9 | + logout, | |
| 10 | + moduleList, | |
| 11 | + getSimpleDictDataList, | |
| 12 | + refreshToken, | |
| 13 | +} from "@/api/user"; | |
| 7 | 14 | // 新增:定义刷新Token的定时器标识(避免重复创建定时器) |
| 8 | 15 | let refreshTokenTimer = null; |
| 9 | - | |
| 10 | -export const useUserStore = defineStore('user', { | |
| 16 | +export const useUserStore = defineStore("user", { | |
| 11 | 17 | // 修复1:删除重复的 state 定义,只保留根层级的 state |
| 12 | 18 | state: () => ({ |
| 13 | - token: cache.get(globalConfig.cache.tokenKey) || '', // 初始值从缓存取(兼容持久化) | |
| 14 | - refreshToken: cache.get(globalConfig.cache.refreshTokenKey) || '', // 新增:Refresh Token 状态 | |
| 19 | + token: cache.get(globalConfig.cache.tokenKey) || "", // 初始值从缓存取(兼容持久化) | |
| 20 | + refreshToken: cache.get(globalConfig.cache.refreshTokenKey) || "", // 新增:Refresh Token 状态 | |
| 15 | 21 | userInfo: cache.get(globalConfig.cache.userInfoKey) || {}, |
| 16 | - userId: cache.get(globalConfig.cache.userIdKey) || '', | |
| 17 | - moduleListInfo: cache.get(globalConfig.cache.moduleListKey) || '', | |
| 22 | + userId: cache.get(globalConfig.cache.userIdKey) || "", | |
| 23 | + moduleListInfo: cache.get(globalConfig.cache.moduleListKey) || "", | |
| 18 | 24 | expireTime: cache.get(globalConfig.cache.expireTimeKey) || 0, |
| 19 | 25 | dictData: cache.get(globalConfig.cache.dictDataKey) || {}, |
| 20 | - logoutLoading: false | |
| 26 | + dictList: cache.get(globalConfig.cache.dictListKey) || [], // 用来存放【过滤后的】字典数组 | |
| 27 | + logoutLoading: false, | |
| 21 | 28 | }), |
| 22 | - | |
| 23 | 29 | getters: { |
| 24 | 30 | isLogin: (state) => { |
| 25 | 31 | if (!state.token) return false; |
| ... | ... | @@ -35,104 +41,114 @@ export const useUserStore = defineStore('user', { |
| 35 | 41 | const remainingTime = state.expireTime - Date.now(); |
| 36 | 42 | // 剩余时间大于0秒 且 小于30秒(即将过期) |
| 37 | 43 | return remainingTime > 0 && remainingTime < 30 * 1000; |
| 38 | - } | |
| 44 | + }, | |
| 39 | 45 | }, |
| 40 | - | |
| 41 | 46 | actions: { |
| 42 | 47 | async login(params) { |
| 43 | 48 | try { |
| 44 | 49 | // smsLogin |
| 45 | - let res | |
| 46 | - if(params.type=='sms'){ | |
| 50 | + let res; | |
| 51 | + if (params.type == "sms") { | |
| 47 | 52 | res = await smsLogin(params); |
| 48 | - }else{ | |
| 53 | + } else { | |
| 49 | 54 | res = await login(params); |
| 50 | 55 | } |
| 51 | - | |
| 52 | 56 | // 新增:从登录接口返回值中获取 refreshToken(若接口返回字段名不一致,可调整,如 res.refresh_token) |
| 53 | 57 | const { accessToken, expiresTime, userId, refreshToken } = res; |
| 54 | - | |
| 55 | 58 | if (!accessToken) { |
| 56 | - throw new Error('登录失败,未获取到令牌'); | |
| 59 | + throw new Error("登录失败,未获取到令牌"); | |
| 57 | 60 | } |
| 58 | - | |
| 59 | 61 | // 更新 Pinia state |
| 60 | 62 | this.token = accessToken; |
| 61 | - this.refreshToken = refreshToken || ''; // 新增:存储Refresh Token | |
| 63 | + this.refreshToken = refreshToken || ""; // 新增:存储Refresh Token | |
| 62 | 64 | this.expireTime = expiresTime; |
| 63 | 65 | this.userId = userId; |
| 64 | 66 | this.userInfo = {}; |
| 65 | - | |
| 66 | 67 | // 等待 Pinia 持久化同步 |
| 67 | - await new Promise(resolve => setTimeout(resolve, 50)); | |
| 68 | - | |
| 68 | + await new Promise((resolve) => setTimeout(resolve, 50)); | |
| 69 | 69 | // 获取用户信息 |
| 70 | 70 | const userInfo = await this.getUserInfo(); |
| 71 | 71 | this.userInfo = userInfo; |
| 72 | - | |
| 73 | 72 | // 获取模块列表 |
| 74 | 73 | let moduleListInfo = null; |
| 75 | 74 | try { |
| 76 | 75 | moduleListInfo = await this.getModuleList(); |
| 77 | 76 | this.moduleListInfo = moduleListInfo; |
| 78 | 77 | } catch (moduleErr) { |
| 79 | - console.warn('获取模块列表失败(不影响登录):', moduleErr); | |
| 80 | - uni.showToast({ title: '获取模块列表失败,可正常登录', icon: 'none' }); | |
| 78 | + console.warn("获取模块列表失败(不影响登录):", moduleErr); | |
| 79 | + uni.showToast({ | |
| 80 | + title: "获取模块列表失败,可正常登录", | |
| 81 | + icon: "none", | |
| 82 | + }); | |
| 81 | 83 | } |
| 82 | - | |
| 83 | 84 | // 修复2:调用字典接口时,不直接抛错(避免阻断登录) |
| 84 | 85 | try { |
| 85 | 86 | const dictRes = await this.getAndSaveDictData(); |
| 86 | 87 | // 仅接口返回成功时才更新字典 |
| 87 | 88 | this.dictData = dictRes || {}; |
| 88 | - console.log('字典数据获取成功:', this.dictData); | |
| 89 | + console.log("字典数据获取成功:", this.dictData); | |
| 90 | + // 获取原始完整字典 | |
| 91 | + const originDictList = uni.$dict.getDictSimpleList("business_line"); | |
| 92 | + console.log("原始完整字典:", originDictList); | |
| 93 | + | |
| 94 | + // ========== 核心:根据 userInfo.busiLine 过滤,结果存入 dictList ========== | |
| 95 | + | |
| 96 | + // this.userInfo?.busiLine | |
| 97 | + | |
| 98 | + console.log("用户信息", this.userInfo.user); | |
| 99 | + if (this.userInfo.user.busiLine && originDictList?.length) { | |
| 100 | + const busiValueArr = this.userInfo.user.busiLine | |
| 101 | + .split(",") | |
| 102 | + .map((item) => item.trim()) | |
| 103 | + .filter(Boolean); | |
| 104 | + this.dictList = originDictList.filter((item) => { | |
| 105 | + return busiValueArr.includes(item.value); | |
| 106 | + }); | |
| 107 | + } else { | |
| 108 | + this.dictList = []; | |
| 109 | + } | |
| 110 | + console.log("过滤完成,dictList = ", this.dictList); | |
| 89 | 111 | } catch (dictErr) { |
| 90 | - console.warn('获取字典失败(不影响登录):', dictErr); | |
| 91 | - uni.showToast({ title: '获取字典失败,可正常使用', icon: 'none' }); | |
| 112 | + console.warn("获取字典失败(不影响登录):", dictErr); | |
| 113 | + uni.showToast({ title: "获取字典失败,可正常使用", icon: "none" }); | |
| 114 | + this.dictList = []; // 异常情况置空 | |
| 92 | 115 | } |
| 93 | - | |
| 94 | 116 | // 新增:登录成功后,启动 Token 自动刷新定时器 |
| 95 | 117 | this.startRefreshTokenTimer(); |
| 96 | - | |
| 97 | 118 | return { ...res, userInfo, moduleListInfo }; |
| 98 | 119 | } catch (err) { |
| 99 | - console.error('登录流程失败:', err); | |
| 120 | + console.error("登录流程失败:", err); | |
| 100 | 121 | throw err; |
| 101 | 122 | } |
| 102 | 123 | }, |
| 103 | - | |
| 104 | 124 | // 新增:调用刷新Token接口,返回刷新是否成功 |
| 105 | 125 | async refreshTokenApi() { |
| 106 | 126 | // 前置校验:无Refresh Token直接返回失败 |
| 107 | 127 | if (!this.refreshToken) { |
| 108 | - console.warn('无刷新令牌,无法刷新Token'); | |
| 128 | + console.warn("无刷新令牌,无法刷新Token"); | |
| 109 | 129 | this.logout(); // 无刷新令牌,直接退出登录 |
| 110 | 130 | return false; |
| 111 | 131 | } |
| 112 | - | |
| 113 | 132 | try { |
| 114 | 133 | // 调用修正后的 refreshToken 接口,传入当前的 refreshToken |
| 115 | 134 | const res = await refreshToken(this.refreshToken); |
| 116 | - | |
| 117 | 135 | // 校验接口返回结果(根据你的实际接口返回格式调整) |
| 118 | 136 | if (!res || !res.accessToken) { |
| 119 | - throw new Error('刷新Token失败,未获取到新令牌'); | |
| 137 | + throw new Error("刷新Token失败,未获取到新令牌"); | |
| 120 | 138 | } |
| 121 | - | |
| 122 | 139 | // 更新 Token 相关状态 |
| 123 | 140 | this.token = res.accessToken; // 新的访问令牌 |
| 124 | 141 | this.refreshToken = res.refreshToken || this.refreshToken; // 若接口返回新的Refresh Token则更新 |
| 125 | 142 | this.expireTime = res.expiresTime; // 新的过期时间 |
| 126 | - console.log('Token 刷新成功'); | |
| 143 | + console.log("Token 刷新成功"); | |
| 127 | 144 | return true; |
| 128 | 145 | } catch (err) { |
| 129 | - console.error('刷新Token失败:', err); | |
| 146 | + console.error("刷新Token失败:", err); | |
| 130 | 147 | // 刷新失败,直接退出登录 |
| 131 | 148 | this.logout(); |
| 132 | 149 | return false; |
| 133 | 150 | } |
| 134 | 151 | }, |
| 135 | - | |
| 136 | 152 | // 新增:启动 Token 自动刷新定时器 |
| 137 | 153 | startRefreshTokenTimer() { |
| 138 | 154 | // 先清除已有定时器,避免重复创建 |
| ... | ... | @@ -140,7 +156,6 @@ export const useUserStore = defineStore('user', { |
| 140 | 156 | clearTimeout(refreshTokenTimer); |
| 141 | 157 | refreshTokenTimer = null; |
| 142 | 158 | } |
| 143 | - | |
| 144 | 159 | // 计算剩余时间(过期前30秒执行刷新) |
| 145 | 160 | const remainingTime = this.expireTime - Date.now() - 30 * 1000; |
| 146 | 161 | if (remainingTime <= 0) { |
| ... | ... | @@ -148,7 +163,6 @@ export const useUserStore = defineStore('user', { |
| 148 | 163 | this.refreshTokenApi(); |
| 149 | 164 | return; |
| 150 | 165 | } |
| 151 | - | |
| 152 | 166 | // 设置定时器,到期后执行刷新 |
| 153 | 167 | refreshTokenTimer = setTimeout(async () => { |
| 154 | 168 | const refreshSuccess = await this.refreshTokenApi(); |
| ... | ... | @@ -157,9 +171,10 @@ export const useUserStore = defineStore('user', { |
| 157 | 171 | this.startRefreshTokenTimer(); |
| 158 | 172 | } |
| 159 | 173 | }, remainingTime); |
| 160 | - console.log(`Token 自动刷新定时器已启动,将在 ${Math.ceil(remainingTime / 1000)} 秒后执行刷新`); | |
| 174 | + console.log( | |
| 175 | + `Token 自动刷新定时器已启动,将在 ${Math.ceil(remainingTime / 1000)} 秒后执行刷新`, | |
| 176 | + ); | |
| 161 | 177 | }, |
| 162 | - | |
| 163 | 178 | // 新增:停止 Token 自动刷新定时器(避免内存泄漏) |
| 164 | 179 | stopRefreshTokenTimer() { |
| 165 | 180 | if (refreshTokenTimer) { |
| ... | ... | @@ -167,171 +182,169 @@ export const useUserStore = defineStore('user', { |
| 167 | 182 | refreshTokenTimer = null; |
| 168 | 183 | } |
| 169 | 184 | }, |
| 170 | - | |
| 171 | 185 | // 修复3:重构字典获取方法(加 Token 校验 + 强制携带 Token + 宽松错误处理) |
| 172 | 186 | async getAndSaveDictData() { |
| 173 | 187 | // 前置校验:无登录态直接返回,不请求接口 |
| 174 | 188 | if (!this.isLogin) { |
| 175 | - console.warn('未登录,跳过字典获取'); | |
| 176 | - return { code: -1, msg: '未登录' }; | |
| 189 | + console.warn("未登录,跳过字典获取"); | |
| 190 | + return { code: -1, msg: "未登录" }; | |
| 177 | 191 | } |
| 178 | - | |
| 179 | 192 | try { |
| 180 | 193 | // 强制携带 Token(和 getModuleList 保持一致,避免拦截器同步延迟) |
| 181 | 194 | const res = await getSimpleDictDataList( |
| 182 | 195 | {}, // 接口入参(按需传,比如 dictType: ['level']) |
| 183 | - { header: { 'Authorization': `Bearer ${this.token}` } } | |
| 196 | + { header: { Authorization: `Bearer ${this.token}` } }, | |
| 184 | 197 | ); |
| 185 | - | |
| 186 | 198 | // 校验接口返回码(核心:避免非 0 码数据存入) |
| 187 | 199 | if (res.code !== 0) { |
| 188 | - console.warn('字典接口返回失败:', res.msg); | |
| 200 | + console.warn("字典接口返回失败:", res.msg); | |
| 189 | 201 | return res; // 返回错误信息,但不抛错 |
| 190 | 202 | } |
| 191 | 203 | return res; |
| 192 | 204 | } catch (err) { |
| 193 | 205 | // 修复4:宽松错误处理,只打印日志,不抛错(避免阻断登录) |
| 194 | - console.error('字典接口请求异常:', err); | |
| 195 | - return { code: -2, msg: '接口请求异常:' + (err.message || '网络错误') }; | |
| 206 | + console.error("字典接口请求异常:", err); | |
| 207 | + return { | |
| 208 | + code: -2, | |
| 209 | + msg: "接口请求异常:" + (err.message || "网络错误"), | |
| 210 | + }; | |
| 196 | 211 | } |
| 197 | 212 | }, |
| 198 | - | |
| 199 | 213 | async getModuleList() { |
| 200 | 214 | try { |
| 201 | 215 | if (!this.token) { |
| 202 | - throw new Error('未获取到登录令牌,无法获取模块列表'); | |
| 216 | + throw new Error("未获取到登录令牌,无法获取模块列表"); | |
| 203 | 217 | } |
| 204 | - | |
| 205 | - const res = await moduleList({}, { | |
| 206 | - header: { 'Authorization': `Bearer ${this.token}` } | |
| 207 | - }); | |
| 218 | + const res = await moduleList( | |
| 219 | + {}, | |
| 220 | + { | |
| 221 | + header: { Authorization: `Bearer ${this.token}` }, | |
| 222 | + }, | |
| 223 | + ); | |
| 208 | 224 | return res; |
| 209 | 225 | } catch (err) { |
| 210 | - console.error('获取用户菜单失败:', err); | |
| 211 | - if (err?.data?.code === 401 || err?.message.includes('401')) { | |
| 212 | - throw new Error('登录态已过期,请重新登录'); | |
| 226 | + console.error("获取用户菜单失败:", err); | |
| 227 | + if (err?.data?.code === 401 || err?.message.includes("401")) { | |
| 228 | + throw new Error("登录态已过期,请重新登录"); | |
| 213 | 229 | } else { |
| 214 | - throw new Error('获取用户菜单失败,请重新登录'); | |
| 230 | + throw new Error("获取用户菜单失败,请重新登录"); | |
| 215 | 231 | } |
| 216 | 232 | } |
| 217 | 233 | }, |
| 218 | - | |
| 219 | 234 | async getUserInfo() { |
| 220 | 235 | try { |
| 221 | 236 | const res = await getUserInfo(); |
| 222 | 237 | return res; |
| 223 | 238 | } catch (err) { |
| 224 | - console.error('获取用户信息失败:', err); | |
| 225 | - throw new Error('获取用户信息失败,请重新登录'); | |
| 239 | + console.error("获取用户信息失败:", err); | |
| 240 | + throw new Error("获取用户信息失败,请重新登录"); | |
| 226 | 241 | } |
| 227 | 242 | }, |
| 228 | - | |
| 229 | 243 | logout() { |
| 230 | 244 | // 新增:退出登录时,停止刷新定时器 |
| 231 | 245 | this.stopRefreshTokenTimer(); |
| 232 | - | |
| 233 | 246 | const pages = getCurrentPages(); |
| 234 | 247 | if (pages.length === 0) return; |
| 235 | - | |
| 236 | 248 | const currentPageRoute = pages[pages.length - 1].route; |
| 237 | 249 | const loginPath = globalConfig.router.loginPath |
| 238 | - .replace(/^\//, '') | |
| 239 | - .split('?')[0]; | |
| 240 | - | |
| 250 | + .replace(/^\//, "") | |
| 251 | + .split("?")[0]; | |
| 241 | 252 | if (currentPageRoute === loginPath) { |
| 242 | - this.token = ''; | |
| 243 | - this.refreshToken = ''; // 新增:清空Refresh Token | |
| 253 | + this.token = ""; | |
| 254 | + this.refreshToken = ""; // 新增:清空Refresh Token | |
| 244 | 255 | this.userInfo = {}; |
| 245 | - this.userId = ''; | |
| 246 | - this.moduleListInfo = ''; | |
| 256 | + this.userId = ""; | |
| 257 | + this.moduleListInfo = ""; | |
| 247 | 258 | this.expireTime = 0; |
| 248 | 259 | this.dictData = {}; |
| 260 | + this.dictList = []; | |
| 249 | 261 | return; |
| 250 | 262 | } |
| 251 | - | |
| 252 | 263 | const logoutWithLock = async () => { |
| 253 | 264 | if (this.logoutLoading) return; |
| 254 | 265 | this.logoutLoading = true; |
| 255 | - | |
| 256 | 266 | try { |
| 257 | 267 | await logout(); |
| 258 | 268 | } catch (err) { |
| 259 | - console.error('退出登录接口调用失败:', err); | |
| 269 | + console.error("退出登录接口调用失败:", err); | |
| 260 | 270 | } finally { |
| 261 | - this.token = ''; | |
| 262 | - this.refreshToken = ''; // 新增:清空Refresh Token | |
| 271 | + this.token = ""; | |
| 272 | + this.refreshToken = ""; // 新增:清空Refresh Token | |
| 263 | 273 | this.userInfo = {}; |
| 264 | - this.userId = ''; | |
| 265 | - this.moduleListInfo = ''; | |
| 274 | + this.userId = ""; | |
| 275 | + this.moduleListInfo = ""; | |
| 266 | 276 | this.dictData = {}; |
| 277 | + this.dictList = []; | |
| 267 | 278 | this.expireTime = 0; |
| 268 | 279 | this.logoutLoading = false; |
| 269 | - | |
| 270 | 280 | uni.redirectTo({ |
| 271 | - url: globalConfig.router.loginPath | |
| 281 | + url: globalConfig.router.loginPath, | |
| 272 | 282 | }); |
| 273 | 283 | } |
| 274 | 284 | }; |
| 275 | - | |
| 276 | 285 | logoutWithLock(); |
| 277 | 286 | }, |
| 278 | - | |
| 279 | 287 | checkLogin() { |
| 280 | 288 | if (!this.isLogin) { |
| 281 | 289 | const pages = getCurrentPages(); |
| 282 | 290 | if (pages.length === 0) return false; |
| 283 | - | |
| 284 | 291 | const currentPageRoute = pages[pages.length - 1].route; |
| 285 | 292 | const loginPath = globalConfig.router.loginPath |
| 286 | - .replace(/^\//, '') | |
| 287 | - .split('?')[0]; | |
| 288 | - | |
| 293 | + .replace(/^\//, "") | |
| 294 | + .split("?")[0]; | |
| 289 | 295 | if (currentPageRoute !== loginPath) { |
| 290 | 296 | uni.redirectTo({ |
| 291 | - url: globalConfig.router.loginPath | |
| 297 | + url: globalConfig.router.loginPath, | |
| 292 | 298 | }); |
| 293 | 299 | } |
| 294 | 300 | return false; |
| 295 | 301 | } |
| 296 | - | |
| 297 | 302 | // 新增:已登录时,启动刷新定时器(防止页面刷新后定时器丢失) |
| 298 | 303 | this.startRefreshTokenTimer(); |
| 299 | 304 | return true; |
| 300 | - } | |
| 305 | + }, | |
| 301 | 306 | }, |
| 302 | - | |
| 303 | 307 | persist: { |
| 304 | 308 | enabled: true, |
| 305 | - key: 'user_store', | |
| 309 | + key: "user_store", | |
| 306 | 310 | storage: { |
| 307 | 311 | getItem: (key) => uni.getStorageSync(key), |
| 308 | 312 | setItem: (key, value) => uni.setStorageSync(key, value), |
| 309 | - removeItem: (key) => uni.removeStorageSync(key) | |
| 313 | + removeItem: (key) => uni.removeStorageSync(key), | |
| 310 | 314 | }, |
| 311 | 315 | serializer: { |
| 312 | 316 | serialize: (state) => { |
| 313 | 317 | uni.setStorageSync(globalConfig.cache.tokenKey, state.token); |
| 314 | - uni.setStorageSync(globalConfig.cache.refreshTokenKey, state.refreshToken); // 新增:持久化Refresh Token | |
| 318 | + uni.setStorageSync( | |
| 319 | + globalConfig.cache.refreshTokenKey, | |
| 320 | + state.refreshToken, | |
| 321 | + ); | |
| 315 | 322 | uni.setStorageSync(globalConfig.cache.userIdKey, state.userId); |
| 316 | 323 | uni.setStorageSync(globalConfig.cache.expireTimeKey, state.expireTime); |
| 317 | 324 | uni.setStorageSync(globalConfig.cache.userInfoKey, state.userInfo); |
| 318 | - uni.setStorageSync(globalConfig.cache.moduleListKey, state.moduleListInfo); | |
| 325 | + uni.setStorageSync( | |
| 326 | + globalConfig.cache.moduleListKey, | |
| 327 | + state.moduleListInfo, | |
| 328 | + ); | |
| 319 | 329 | uni.setStorageSync(globalConfig.cache.dictDataKey, state.dictData); |
| 330 | + uni.setStorageSync(globalConfig.cache.dictListKey, state.dictList); | |
| 320 | 331 | return state; |
| 321 | 332 | }, |
| 322 | 333 | deserialize: (value) => { |
| 323 | 334 | return { |
| 324 | - token: uni.getStorageSync(globalConfig.cache.tokenKey) || '', | |
| 325 | - refreshToken: uni.getStorageSync(globalConfig.cache.refreshTokenKey) || '', // 新增:读取Refresh Token | |
| 326 | - userId: uni.getStorageSync(globalConfig.cache.userIdKey) || '', | |
| 335 | + token: uni.getStorageSync(globalConfig.cache.tokenKey) || "", | |
| 336 | + refreshToken: | |
| 337 | + uni.getStorageSync(globalConfig.cache.refreshTokenKey) || "", | |
| 338 | + userId: uni.getStorageSync(globalConfig.cache.userIdKey) || "", | |
| 327 | 339 | expireTime: uni.getStorageSync(globalConfig.cache.expireTimeKey) || 0, |
| 328 | 340 | userInfo: uni.getStorageSync(globalConfig.cache.userInfoKey) || {}, |
| 329 | - moduleListInfo: uni.getStorageSync(globalConfig.cache.moduleListKey) || '', | |
| 341 | + moduleListInfo: | |
| 342 | + uni.getStorageSync(globalConfig.cache.moduleListKey) || "", | |
| 330 | 343 | dictData: uni.getStorageSync(globalConfig.cache.dictDataKey) || {}, |
| 331 | - logoutLoading: false | |
| 344 | + dictList: uni.getStorageSync(globalConfig.cache.dictListKey) || [], | |
| 345 | + logoutLoading: false, | |
| 332 | 346 | }; |
| 333 | - } | |
| 347 | + }, | |
| 334 | 348 | }, |
| 335 | - // paths: [] | |
| 336 | - } | |
| 337 | -}); | |
| 338 | 349 | \ No newline at end of file |
| 350 | + }, | |
| 351 | +}); | ... | ... |