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,7 +5,7 @@ const env = { | ||
| 5 | // baseUrl: 'https://test.jichengshanshui.com.cn:28302', | 5 | // baseUrl: 'https://test.jichengshanshui.com.cn:28302', |
| 6 | // https://test.jichengshanshui.com.cn:28302 | 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 | uploadUrl: 'https://test.jichengshanshui.com.cn:28302', | 10 | uploadUrl: 'https://test.jichengshanshui.com.cn:28302', |
| 11 | // baseApi:'admin-api', | 11 | // baseApi:'admin-api', |
common/config/global.js
| @@ -25,6 +25,7 @@ export default { | @@ -25,6 +25,7 @@ export default { | ||
| 25 | moduleListKey:'jcss_module_list', | 25 | moduleListKey:'jcss_module_list', |
| 26 | dictDataKey:'jcss_dict_data', | 26 | dictDataKey:'jcss_dict_data', |
| 27 | refreshTokenKey: 'jcss_user_refresh_token', // 新增:刷新令牌缓存key | 27 | refreshTokenKey: 'jcss_user_refresh_token', // 新增:刷新令牌缓存key |
| 28 | + dictListKey: 'jcss_dict_list' // 新增这一行 | ||
| 28 | }, | 29 | }, |
| 29 | appName: 'JCSS管理系统', | 30 | appName: 'JCSS管理系统', |
| 30 | tokenExpireTime: 7 * 24 * 60 * 60 * 1000 | 31 | tokenExpireTime: 7 * 24 * 60 * 60 * 1000 |
pages-sub/daily/quick-order/add-order.vue
| @@ -2,204 +2,88 @@ | @@ -2,204 +2,88 @@ | ||
| 2 | <view class="page-container"> | 2 | <view class="page-container"> |
| 3 | <!-- 核心:将所有 up-form-item 包裹在同一个 up-form 内 --> | 3 | <!-- 核心:将所有 up-form-item 包裹在同一个 up-form 内 --> |
| 4 | <view class="work-order-form-content commonPageLRpadding"> | 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 | </up-radio-group> | 10 | </up-radio-group> |
| 30 | - | ||
| 31 | </up-form-item> | 11 | </up-form-item> |
| 32 | <!-- 1. 工单位置(地图选择) --> | 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 | </up-form-item> | 17 | </up-form-item> |
| 48 | - | ||
| 49 | <!-- 2. 道路名称(下拉框:位置选择后才可点击) --> | 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 | <template #right> | 23 | <template #right> |
| 65 | - <up-icon name="arrow-right" size="16" ></up-icon> | 24 | + <up-icon name="arrow-right" size="16"></up-icon> |
| 66 | </template> | 25 | </template> |
| 67 | </up-form-item> | 26 | </up-form-item> |
| 68 | - | ||
| 69 | <!-- 3. 问题类型(下拉框) --> | 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 | <template #right> | 32 | <template #right> |
| 85 | <up-icon name="arrow-right" size="16"></up-icon> | 33 | <up-icon name="arrow-right" size="16"></up-icon> |
| 86 | </template> | 34 | </template> |
| 87 | </up-form-item> | 35 | </up-form-item> |
| 88 | - | ||
| 89 | <!-- 4. 情况描述(文本域) --> | 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 | </up-form-item> | 40 | </up-form-item> |
| 104 | - | ||
| 105 | <!-- 问题照片 --> | 41 | <!-- 问题照片 --> |
| 106 | <up-form-item label="问题照片" prop="problemImgs" required> | 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 | </up-form-item> | 46 | </up-form-item> |
| 118 | - | ||
| 119 | <!-- 完成照片 --> | 47 | <!-- 完成照片 --> |
| 120 | <up-form-item label="完成照片" prop="completeImgs" required class="mt-20"> | 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 | </up-form-item> | 52 | </up-form-item> |
| 133 | - | ||
| 134 | <!-- 处理结果(不必填) --> | 53 | <!-- 处理结果(不必填) --> |
| 135 | <up-form-item label="处理结果" prop="handleResult" class="mt-20"> | 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 | </up-form-item> | 57 | </up-form-item> |
| 145 | - | ||
| 146 | </up-form> | 58 | </up-form> |
| 147 | </view> | 59 | </view> |
| 148 | <!-- 底部提交按钮(移除loading和disabled属性) --> | 60 | <!-- 底部提交按钮(移除loading和disabled属性) --> |
| 149 | <view class="fixed-bottom-btn-wrap"> | 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 | </view> | 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 | </view> | 70 | </view> |
| 176 | </template> | 71 | </template> |
| 177 | - | ||
| 178 | <script lang="ts"> | 72 | <script lang="ts"> |
| 179 | import { getRoadListByLatLng } from '@/api/common' | 73 | import { getRoadListByLatLng } from '@/api/common' |
| 180 | import { uploadImages } from '@/common/utils/upload'; | 74 | import { uploadImages } from '@/common/utils/upload'; |
| 181 | import { createQuick } from '@/api/quick-order/quick-order' | 75 | import { createQuick } from '@/api/quick-order/quick-order' |
| 182 | import { useUserStore } from '@/pinia/user'; | 76 | import { useUserStore } from '@/pinia/user'; |
| 183 | - | ||
| 184 | export default { | 77 | export default { |
| 78 | + computed: { | ||
| 79 | + busiLineOptions() { | ||
| 80 | + const userStore = useUserStore(); | ||
| 81 | + return userStore.dictList || [] | ||
| 82 | + } | ||
| 83 | + }, | ||
| 185 | data() { | 84 | data() { |
| 186 | return { | 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 | problemImgsList: [], | 88 | problemImgsList: [], |
| 205 | // 完成照片列表 | 89 | // 完成照片列表 |
| @@ -212,7 +96,7 @@ export default { | @@ -212,7 +96,7 @@ export default { | ||
| 212 | orderNameList: [], | 96 | orderNameList: [], |
| 213 | // 工单表单数据 | 97 | // 工单表单数据 |
| 214 | workOrderForm: { | 98 | workOrderForm: { |
| 215 | - busiLineCn: '', // 业务线中文名称 | 99 | + busiLine: '', // 业务线value yl / sz / wy |
| 216 | roadId: 0, // 道路ID | 100 | roadId: 0, // 道路ID |
| 217 | roadName: '', // 道路名称 | 101 | roadName: '', // 道路名称 |
| 218 | workLocation: '', // 工单位置 | 102 | workLocation: '', // 工单位置 |
| @@ -224,7 +108,7 @@ export default { | @@ -224,7 +108,7 @@ export default { | ||
| 224 | }, | 108 | }, |
| 225 | // 表单校验规则 | 109 | // 表单校验规则 |
| 226 | workOrderFormRules: { | 110 | workOrderFormRules: { |
| 227 | - busiLineCn: [ // 业务线必选校验 | 111 | + busiLine: [ |
| 228 | { type: 'string', required: true, message: '请选择业务线', trigger: ['change', 'blur'] } | 112 | { type: 'string', required: true, message: '请选择业务线', trigger: ['change', 'blur'] } |
| 229 | ], | 113 | ], |
| 230 | workLocation: [ | 114 | workLocation: [ |
| @@ -266,12 +150,11 @@ export default { | @@ -266,12 +150,11 @@ export default { | ||
| 266 | } | 150 | } |
| 267 | }, | 151 | }, |
| 268 | onLoad() { | 152 | onLoad() { |
| 269 | - // 初始化业务线选项 | ||
| 270 | - this.initBusiLineOptions(); | ||
| 271 | // 默认选中第一个业务线 | 153 | // 默认选中第一个业务线 |
| 272 | if (this.busiLineOptions && this.busiLineOptions.length > 0) { | 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 | onReady() { | 160 | onReady() { |
| @@ -284,25 +167,13 @@ export default { | @@ -284,25 +167,13 @@ export default { | ||
| 284 | console.log('问题类型列表:', this.orderNameList); | 167 | console.log('问题类型列表:', this.orderNameList); |
| 285 | }, | 168 | }, |
| 286 | methods: { | 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 | this.workOrderForm.roadName = ''; | 178 | this.workOrderForm.roadName = ''; |
| 308 | this.workOrderForm.roadId = 0; | 179 | this.workOrderForm.roadId = 0; |
| @@ -314,20 +185,13 @@ export default { | @@ -314,20 +185,13 @@ export default { | ||
| 314 | }, | 185 | }, |
| 315 | // 根据选中业务线请求道路列表 | 186 | // 根据选中业务线请求道路列表 |
| 316 | async getRoadListByBusiLine() { | 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 | return; | 189 | return; |
| 325 | } | 190 | } |
| 326 | - | ||
| 327 | try { | 191 | try { |
| 328 | uni.showLoading({ title: '获取道路名称中...' }); | 192 | uni.showLoading({ title: '获取道路名称中...' }); |
| 329 | const roadRes = await getRoadListByLatLng({ | 193 | const roadRes = await getRoadListByLatLng({ |
| 330 | - busiLine: busiLineEn, // 传递业务线英文标识 | 194 | + busiLine: this.workOrderForm.busiLine, // 直接传递value |
| 331 | latitude: this.workOrderForm.lat, | 195 | latitude: this.workOrderForm.lat, |
| 332 | longitude: this.workOrderForm.lon | 196 | longitude: this.workOrderForm.lon |
| 333 | }); | 197 | }); |
| @@ -380,7 +244,6 @@ export default { | @@ -380,7 +244,6 @@ export default { | ||
| 380 | console.log('上传图片事件:', event, '类型:', type); | 244 | console.log('上传图片事件:', event, '类型:', type); |
| 381 | const fileList = Array.isArray(event.file) ? event.file : [event.file]; | 245 | const fileList = Array.isArray(event.file) ? event.file : [event.file]; |
| 382 | const targetImgList = type === 'problemImgsList' ? this.problemImgsList : this.completeImgsList; | 246 | const targetImgList = type === 'problemImgsList' ? this.problemImgsList : this.completeImgsList; |
| 383 | - | ||
| 384 | const filePaths = fileList.map(item => item.url); | 247 | const filePaths = fileList.map(item => item.url); |
| 385 | const tempItems = fileList.map(item => ({ | 248 | const tempItems = fileList.map(item => ({ |
| 386 | ...item, | 249 | ...item, |
| @@ -389,14 +252,12 @@ export default { | @@ -389,14 +252,12 @@ export default { | ||
| 389 | })); | 252 | })); |
| 390 | const startIndex = targetImgList.length; | 253 | const startIndex = targetImgList.length; |
| 391 | targetImgList.push(...tempItems); | 254 | targetImgList.push(...tempItems); |
| 392 | - | ||
| 393 | try { | 255 | try { |
| 394 | const uploadResultUrls = await uploadImages({ | 256 | const uploadResultUrls = await uploadImages({ |
| 395 | filePaths: filePaths, | 257 | filePaths: filePaths, |
| 396 | ignoreError: true | 258 | ignoreError: true |
| 397 | }); | 259 | }); |
| 398 | console.log('上传成功的URL列表:', uploadResultUrls); | 260 | console.log('上传成功的URL列表:', uploadResultUrls); |
| 399 | - | ||
| 400 | uploadResultUrls.forEach((url, index) => { | 261 | uploadResultUrls.forEach((url, index) => { |
| 401 | if (targetImgList[startIndex + index]) { | 262 | if (targetImgList[startIndex + index]) { |
| 402 | targetImgList.splice(startIndex + index, 1, { | 263 | targetImgList.splice(startIndex + index, 1, { |
| @@ -407,7 +268,6 @@ export default { | @@ -407,7 +268,6 @@ export default { | ||
| 407 | }); | 268 | }); |
| 408 | } | 269 | } |
| 409 | }); | 270 | }); |
| 410 | - | ||
| 411 | if (uploadResultUrls.length < fileList.length) { | 271 | if (uploadResultUrls.length < fileList.length) { |
| 412 | const failCount = fileList.length - uploadResultUrls.length; | 272 | const failCount = fileList.length - uploadResultUrls.length; |
| 413 | for (let i = uploadResultUrls.length; i < fileList.length; i++) { | 273 | for (let i = uploadResultUrls.length; i < fileList.length; i++) { |
| @@ -423,7 +283,6 @@ export default { | @@ -423,7 +283,6 @@ export default { | ||
| 423 | } else { | 283 | } else { |
| 424 | uni.showToast({ title: `成功上传${fileList.length}张图片`, icon: 'success' }); | 284 | uni.showToast({ title: `成功上传${fileList.length}张图片`, icon: 'success' }); |
| 425 | } | 285 | } |
| 426 | - | ||
| 427 | // 上传完成后重新校验图片字段 | 286 | // 上传完成后重新校验图片字段 |
| 428 | if (type === 'problemImgsList') { | 287 | if (type === 'problemImgsList') { |
| 429 | this.$refs.workOrderFormRef.validateField('problemImgs'); | 288 | this.$refs.workOrderFormRef.validateField('problemImgs'); |
| @@ -431,7 +290,6 @@ export default { | @@ -431,7 +290,6 @@ export default { | ||
| 431 | this.$refs.workOrderFormRef.validateField('completeImgs'); | 290 | this.$refs.workOrderFormRef.validateField('completeImgs'); |
| 432 | } | 291 | } |
| 433 | } catch (err) { | 292 | } catch (err) { |
| 434 | - console.error('图片上传失败:', err); | ||
| 435 | for (let i = 0; i < fileList.length; i++) { | 293 | for (let i = 0; i < fileList.length; i++) { |
| 436 | if (targetImgList[startIndex + i]) { | 294 | if (targetImgList[startIndex + i]) { |
| 437 | targetImgList.splice(startIndex + i, 1, { | 295 | targetImgList.splice(startIndex + i, 1, { |
| @@ -442,7 +300,6 @@ export default { | @@ -442,7 +300,6 @@ export default { | ||
| 442 | } | 300 | } |
| 443 | } | 301 | } |
| 444 | uni.showToast({ title: '图片上传失败,请重试', icon: 'none' }); | 302 | uni.showToast({ title: '图片上传失败,请重试', icon: 'none' }); |
| 445 | - | ||
| 446 | // 上传失败后重新校验图片字段 | 303 | // 上传失败后重新校验图片字段 |
| 447 | if (type === 'problemImgsList') { | 304 | if (type === 'problemImgsList') { |
| 448 | this.$refs.workOrderFormRef.validateField('problemImgs'); | 305 | this.$refs.workOrderFormRef.validateField('problemImgs'); |
| @@ -461,14 +318,11 @@ export default { | @@ -461,14 +318,11 @@ export default { | ||
| 461 | that.workOrderForm.roadName = ''; | 318 | that.workOrderForm.roadName = ''; |
| 462 | that.workOrderForm.roadId = 0; | 319 | that.workOrderForm.roadId = 0; |
| 463 | that.roadNameList = []; | 320 | that.roadNameList = []; |
| 464 | - | ||
| 465 | that.workOrderForm.workLocation = res.name; | 321 | that.workOrderForm.workLocation = res.name; |
| 466 | that.workOrderForm.lat = res.latitude; | 322 | that.workOrderForm.lat = res.latitude; |
| 467 | that.workOrderForm.lon = res.longitude; | 323 | that.workOrderForm.lon = res.longitude; |
| 468 | - | ||
| 469 | that.$refs.workOrderFormRef.validateField('workLocation'); | 324 | that.$refs.workOrderFormRef.validateField('workLocation'); |
| 470 | that.$refs.workOrderFormRef.validateField('roadName'); | 325 | that.$refs.workOrderFormRef.validateField('roadName'); |
| 471 | - | ||
| 472 | // 调用封装的方法,自动携带业务线 | 326 | // 调用封装的方法,自动携带业务线 |
| 473 | await that.getRoadListByBusiLine(); | 327 | await that.getRoadListByBusiLine(); |
| 474 | }, | 328 | }, |
| @@ -516,14 +370,12 @@ export default { | @@ -516,14 +370,12 @@ export default { | ||
| 516 | try { | 370 | try { |
| 517 | // 先执行表单校验 | 371 | // 先执行表单校验 |
| 518 | await this.$refs.workOrderFormRef.validate(); | 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 | uni.showToast({ title: '业务线选择异常,请重新选择', icon: 'none' }); | 374 | uni.showToast({ title: '业务线选择异常,请重新选择', icon: 'none' }); |
| 524 | return; | 375 | return; |
| 525 | } | 376 | } |
| 526 | - | 377 | + // 根据value找到中文label |
| 378 | + const sourceName = this.busiLineOptions.find(item => item.value === this.workOrderForm.busiLine)?.label || ''; | ||
| 527 | const submitData = { | 379 | const submitData = { |
| 528 | roadId: this.workOrderForm.roadId, | 380 | roadId: this.workOrderForm.roadId, |
| 529 | roadName: this.workOrderForm.roadName, | 381 | roadName: this.workOrderForm.roadName, |
| @@ -538,24 +390,20 @@ export default { | @@ -538,24 +390,20 @@ export default { | ||
| 538 | pressingType: 2, | 390 | pressingType: 2, |
| 539 | orderName: this.workOrderForm.orderName, | 391 | orderName: this.workOrderForm.orderName, |
| 540 | sourceId: 1, | 392 | sourceId: 1, |
| 541 | - sourceName: this.workOrderForm.busiLineCn, // 业务线中文名称 | 393 | + sourceName: sourceName, // 业务线中文名称 |
| 542 | thirdWorkNo: '', | 394 | thirdWorkNo: '', |
| 543 | - busiLine: busiLineEn // 业务线英文标识(yl/sz/wy) | 395 | + busiLine: this.workOrderForm.busiLine // 业务线英文标识(yl/sz/wy) |
| 544 | }; | 396 | }; |
| 545 | - | ||
| 546 | // 显示加载中 | 397 | // 显示加载中 |
| 547 | uni.showLoading({ title: '提交中...' }); | 398 | uni.showLoading({ title: '提交中...' }); |
| 548 | - | ||
| 549 | // 调用提交接口 | 399 | // 调用提交接口 |
| 550 | const res = await createQuick(submitData); | 400 | const res = await createQuick(submitData); |
| 551 | - | ||
| 552 | uni.hideLoading(); | 401 | uni.hideLoading(); |
| 553 | uni.showToast({ | 402 | uni.showToast({ |
| 554 | title: '工单提交成功', | 403 | title: '工单提交成功', |
| 555 | icon: 'success', | 404 | icon: 'success', |
| 556 | duration: 1000 | 405 | duration: 1000 |
| 557 | }); | 406 | }); |
| 558 | - | ||
| 559 | // 延迟跳转 | 407 | // 延迟跳转 |
| 560 | setTimeout(() => { | 408 | setTimeout(() => { |
| 561 | uni.reLaunch({ | 409 | uni.reLaunch({ |
| @@ -565,7 +413,6 @@ export default { | @@ -565,7 +413,6 @@ export default { | ||
| 565 | } catch (error) { | 413 | } catch (error) { |
| 566 | // 隐藏加载框 | 414 | // 隐藏加载框 |
| 567 | uni.hideLoading(); | 415 | uni.hideLoading(); |
| 568 | - | ||
| 569 | // 区分是表单校验失败还是接口调用失败 | 416 | // 区分是表单校验失败还是接口调用失败 |
| 570 | if (!Array.isArray(error)) { | 417 | if (!Array.isArray(error)) { |
| 571 | // 接口调用失败 | 418 | // 接口调用失败 |
| @@ -581,16 +428,14 @@ export default { | @@ -581,16 +428,14 @@ export default { | ||
| 581 | } | 428 | } |
| 582 | } | 429 | } |
| 583 | </script> | 430 | </script> |
| 584 | - | ||
| 585 | <style lang="scss" scoped> | 431 | <style lang="scss" scoped> |
| 586 | // 全局页面样式 | 432 | // 全局页面样式 |
| 587 | .page-container { | 433 | .page-container { |
| 588 | min-height: 100vh; | 434 | min-height: 100vh; |
| 589 | } | 435 | } |
| 590 | - | ||
| 591 | // 工单表单内容容器 | 436 | // 工单表单内容容器 |
| 592 | .work-order-form-content { | 437 | .work-order-form-content { |
| 593 | background: #fff; | 438 | background: #fff; |
| 594 | margin-bottom: 100rpx; | 439 | margin-bottom: 100rpx; |
| 595 | } | 440 | } |
| 596 | -</style> | ||
| 597 | \ No newline at end of file | 441 | \ No newline at end of file |
| 442 | +</style> |
pages-sub/problem/regional-order-manage/add-order.vue
| 1 | <template> | 1 | <template> |
| 2 | <view class="page-container"> | 2 | <view class="page-container"> |
| 3 | <view class="work-order-form-content commonPageLRpadding"> | 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 | <!-- 1. 工单位置(地图选择)【公共项】 --> | 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 | </up-form-item> | 10 | </up-form-item> |
| 26 | - | ||
| 27 | <!-- 2. 情况描述【公共项】 --> | 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 | </up-form-item> | 15 | </up-form-item> |
| 43 | - | ||
| 44 | <!-- 3. 问题照片【公共项】 --> | 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 | </up-form-item> | 22 | </up-form-item> |
| 58 | - | ||
| 59 | <!-- 多组派单区域 --> | 23 | <!-- 多组派单区域 --> |
| 60 | <view class="dispatch-list-wrap"> | 24 | <view class="dispatch-list-wrap"> |
| 61 | <view class="dispatch-title">派单情况</view> | 25 | <view class="dispatch-title">派单情况</view> |
| 62 | - | ||
| 63 | <!-- 循环渲染每组派单 --> | 26 | <!-- 循环渲染每组派单 --> |
| 64 | <view class="dispatch-item" v-for="(item, idx) in dispatchList" :key="idx"> | 27 | <view class="dispatch-item" v-for="(item, idx) in dispatchList" :key="idx"> |
| 65 | <view class="item-head"> | 28 | <view class="item-head"> |
| 66 | <text>派单{{ idx + 1 }}</text> | 29 | <text>派单{{ idx + 1 }}</text> |
| 67 | <!-- 数量大于1才显示红色关闭X --> | 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 | </view> | 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 | <template #right> | 39 | <template #right> |
| 94 | <up-icon name="arrow-right" size="16"></up-icon> | 40 | <up-icon name="arrow-right" size="16"></up-icon> |
| 95 | </template> | 41 | </template> |
| 96 | </up-form-item> | 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 | <template #right> | 48 | <template #right> |
| 114 | <up-icon name="arrow-right" size="16"></up-icon> | 49 | <up-icon name="arrow-right" size="16"></up-icon> |
| 115 | </template> | 50 | </template> |
| 116 | </up-form-item> | 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 | <template #right> | 57 | <template #right> |
| 134 | <up-icon name="arrow-right" size="16"></up-icon> | 58 | <up-icon name="arrow-right" size="16"></up-icon> |
| 135 | </template> | 59 | </template> |
| 136 | </up-form-item> | 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 | <template #right> | 66 | <template #right> |
| 154 | <up-icon name="arrow-right" size="16"></up-icon> | 67 | <up-icon name="arrow-right" size="16"></up-icon> |
| 155 | </template> | 68 | </template> |
| 156 | </up-form-item> | 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 | <template #right> | 73 | <template #right> |
| 171 | <view v-if="item.expectedFinishDate" @click.stop> | 74 | <view v-if="item.expectedFinishDate" @click.stop> |
| 172 | <up-icon name="close" size="16" @click.stop="clearItemDate(idx)"></up-icon> | 75 | <up-icon name="close" size="16" @click.stop="clearItemDate(idx)"></up-icon> |
| @@ -176,81 +79,49 @@ | @@ -176,81 +79,49 @@ | ||
| 176 | </up-form-item> | 79 | </up-form-item> |
| 177 | </view> | 80 | </view> |
| 178 | </view> | 81 | </view> |
| 179 | - | ||
| 180 | <!-- 新增派单按钮 --> | 82 | <!-- 新增派单按钮 --> |
| 181 | <view class="add-dispatch-btn-wrap" v-if="!isRenew"> | 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 | </view> | 87 | </view> |
| 190 | </up-form> | 88 | </up-form> |
| 191 | </view> | 89 | </view> |
| 192 | - | ||
| 193 | <!-- 底部提交按钮 --> | 90 | <!-- 底部提交按钮 --> |
| 194 | <view class="fixed-bottom-btn-wrap"> | 91 | <view class="fixed-bottom-btn-wrap"> |
| 195 | <up-button type="primary" text="提交工单" @click="submitWorkOrder"></up-button> | 92 | <up-button type="primary" text="提交工单" @click="submitWorkOrder"></up-button> |
| 196 | </view> | 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 | </view> | 104 | </view> |
| 227 | </template> | 105 | </template> |
| 228 | - | ||
| 229 | <script setup> | 106 | <script setup> |
| 230 | -import { ref, reactive, nextTick } from 'vue' | 107 | +import { ref, reactive, nextTick, computed } from 'vue' |
| 231 | import { onReady, onShow, onLoad, onBackPress } from '@dcloudio/uni-app'; | 108 | import { onReady, onShow, onLoad, onBackPress } from '@dcloudio/uni-app'; |
| 232 | import { useUploadImgs } from '@/common/utils/useUploadImgs' | 109 | import { useUploadImgs } from '@/common/utils/useUploadImgs' |
| 233 | import { getRoadListByLatLng, getProblemListByType } from '@/api/common' | 110 | import { getRoadListByLatLng, getProblemListByType } from '@/api/common' |
| 234 | import { | 111 | import { |
| 235 | - daquUniversalApproval, qyUniversalApproval, qyWorkorderCreate,dycCreateBatchNew, | ||
| 236 | - dcyUniversalApproval, daquWorkorderCreate, dcyWorkorderCreate | 112 | +daquUniversalApproval, qyUniversalApproval, qyWorkorderCreate, dycCreateBatchNew, |
| 113 | +dcyUniversalApproval, daquWorkorderCreate, dcyWorkorderCreate | ||
| 237 | } from '@/api/regional-order-manage/regional-order-manage' | 114 | } from '@/api/regional-order-manage/regional-order-manage' |
| 238 | import { timeFormat } from '@/uni_modules/uview-plus' | 115 | import { timeFormat } from '@/uni_modules/uview-plus' |
| 239 | import { nextStepMap } from '@/common/utils/common' | 116 | import { nextStepMap } from '@/common/utils/common' |
| 240 | import { useUserStore } from '@/pinia/user'; | 117 | import { useUserStore } from '@/pinia/user'; |
| 241 | - | ||
| 242 | // ========== 常量配置区 ========== | 118 | // ========== 常量配置区 ========== |
| 243 | const CONST = { | 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 | const userStore = useUserStore(); | 126 | const userStore = useUserStore(); |
| 256 | const USER_ROLES = userStore.userInfo?.roles || []; | 127 | const USER_ROLES = userStore.userInfo?.roles || []; |
| @@ -258,556 +129,554 @@ const workOrderFormRef = ref(null); | @@ -258,556 +129,554 @@ const workOrderFormRef = ref(null); | ||
| 258 | const showActionSheet = ref(false); | 129 | const showActionSheet = ref(false); |
| 259 | const isRenew = ref(false); | 130 | const isRenew = ref(false); |
| 260 | const renewOrderData = ref(null); | 131 | const renewOrderData = ref(null); |
| 261 | - | ||
| 262 | // 当前操作的派单项下标 | 132 | // 当前操作的派单项下标 |
| 263 | const currentDispatchIndex = ref(0); | 133 | const currentDispatchIndex = ref(0); |
| 264 | - | ||
| 265 | // 三级级联选择器 | 134 | // 三级级联选择器 |
| 266 | const showCascader = ref(false); | 135 | const showCascader = ref(false); |
| 267 | const cascaderValue = ref([]); | 136 | const cascaderValue = ref([]); |
| 268 | const cascaderOptions = ref([]); | 137 | const cascaderOptions = ref([]); |
| 269 | - | ||
| 270 | // 下拉弹窗数据 | 138 | // 下拉弹窗数据 |
| 271 | const currentActionSheetData = reactive({ type: '', list: [], title: '', index: 0 }); | 139 | const currentActionSheetData = reactive({ type: '', list: [], title: '', index: 0 }); |
| 272 | const roadNameList = ref([]); | 140 | const roadNameList = ref([]); |
| 273 | const pressingTypeList = ref([]); | 141 | const pressingTypeList = ref([]); |
| 274 | const busiLineOptions = ref([]); | 142 | const busiLineOptions = ref([]); |
| 275 | const currentBusiLineCode = ref(''); | 143 | const currentBusiLineCode = ref(''); |
| 276 | - | ||
| 277 | // 每组派单独立时间选择器 | 144 | // 每组派单独立时间选择器 |
| 278 | const showItemDatePicker = ref(false); | 145 | const showItemDatePicker = ref(false); |
| 279 | const tempDateValue = ref(Date.now()); | 146 | const tempDateValue = ref(Date.now()); |
| 280 | const curSelectDateIndex = ref(-1); | 147 | const curSelectDateIndex = ref(-1); |
| 281 | - | ||
| 282 | // 图片上传 | 148 | // 图片上传 |
| 283 | const problemImgs = useUploadImgs({ | 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 | if (!Array.isArray(problemImgs.rawImgList.value)) problemImgs.rawImgList.value = []; | 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 | const baseForm = reactive({ | 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 | const createDispatchItem = (defaultBusiLine = null) => { | 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 | // 初始化默认 1 组派单 | 222 | // 初始化默认 1 组派单 |
| 313 | const dispatchList = ref([createDispatchItem()]) | 223 | const dispatchList = ref([createDispatchItem()]) |
| 314 | - | ||
| 315 | // ========== 表单校验规则 ========== | 224 | // ========== 表单校验规则 ========== |
| 316 | const workOrderFormRules = reactive({ | 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 | { type: 'string', required: true, message: '请输入情况描述', trigger: ['change', 'blur'] }, | 228 | { type: 'string', required: true, message: '请输入情况描述', trigger: ['change', 'blur'] }, |
| 320 | { type: 'string', min: 3, max: 200, message: '情况描述需3-200字', trigger: ['change', 'blur'] } | 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 | const toOrderListPage = () => uni.reLaunch({ url: CONST.PAGE_ORDER_LIST }); | 234 | const toOrderListPage = () => uni.reLaunch({ url: CONST.PAGE_ORDER_LIST }); |
| 329 | const formatDate = (dateVal, fmt = 'yyyy-mm-dd hh:MM:ss') => dateVal ? timeFormat(dateVal, fmt) : ''; | 235 | const formatDate = (dateVal, fmt = 'yyyy-mm-dd hh:MM:ss') => dateVal ? timeFormat(dateVal, fmt) : ''; |
| 330 | const dateToTimestamp = (dateStr) => dateStr ? new Date(dateStr).getTime() : ''; | 236 | const dateToTimestamp = (dateStr) => dateStr ? new Date(dateStr).getTime() : ''; |
| 331 | - | ||
| 332 | /** | 237 | /** |
| 333 | * 加载三级问题类型级联数据 | 238 | * 加载三级问题类型级联数据 |
| 334 | */ | 239 | */ |
| 335 | const loadProblemCascader = async (parentCode) => { | 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 | } catch (err) { | 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 | } finally { | 253 | } finally { |
| 349 | - uni.hideLoading(); | 254 | +uni.hideLoading(); |
| 350 | } | 255 | } |
| 351 | }; | 256 | }; |
| 352 | - | ||
| 353 | /** 打开级联选择器 */ | 257 | /** 打开级联选择器 */ |
| 354 | const openCascader = (idx) => { | 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 | const cascaderConfirm = (selectedArr) => { | 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 | const openItemDatePicker = (idx) => { | 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 | const closeItemDatePicker = () => { | 301 | const closeItemDatePicker = () => { |
| 402 | - showItemDatePicker.value = false; | ||
| 403 | - curSelectDateIndex.value = -1; | 302 | +showItemDatePicker.value = false; |
| 303 | +curSelectDateIndex.value = -1; | ||
| 404 | }; | 304 | }; |
| 405 | const itemDateConfirm = (e) => { | 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 | const clearItemDate = (idx) => { | 312 | const clearItemDate = (idx) => { |
| 413 | - dispatchList.value[idx].expectedFinishDate = ''; | 313 | +dispatchList.value[idx].expectedFinishDate = ''; |
| 414 | }; | 314 | }; |
| 415 | - | ||
| 416 | // ========== 新增 / 删除 派单项 ========== | 315 | // ========== 新增 / 删除 派单项 ========== |
| 417 | /** 新增派单 */ | 316 | /** 新增派单 */ |
| 418 | const addDispatchItem = () => { | 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 | const delDispatchItem = (idx) => { | 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 | onLoad(async (options) => { | 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 | onReady(() => { | 353 | onReady(() => { |
| 457 | - nextTick(() => { | ||
| 458 | - workOrderFormRef.value?.setRules(workOrderFormRules); | 354 | +nextTick(() => { |
| 355 | +workOrderFormRef.value?.setRules(workOrderFormRules); | ||
| 459 | }); | 356 | }); |
| 460 | }); | 357 | }); |
| 461 | - | ||
| 462 | onShow(() => { | 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 | onBackPress(() => { | 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 | const initBusiLineOptions = () => { | 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 | const handleBusiLineChange = (code, idx) => { | 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 | const getRoadListByBusiLine = async () => { | 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 | } catch (err) { | 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 | } finally { | 413 | } finally { |
| 519 | - uni.hideLoading(); | 414 | +uni.hideLoading(); |
| 520 | } | 415 | } |
| 521 | }; | 416 | }; |
| 522 | - | ||
| 523 | // ========== 通用下拉弹窗方法(支持下标) ========== | 417 | // ========== 通用下拉弹窗方法(支持下标) ========== |
| 524 | const handleActionSheetOpen = (type, idx) => { | 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 | const handleActionSheetClose = () => { | 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 | const handleActionSheetSelect = (e) => { | 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 | const hideKeyboard = () => uni.hideKeyboard(); | 459 | const hideKeyboard = () => uni.hideKeyboard(); |
| 568 | - | ||
| 569 | /** 选择地图位置 */ | 460 | /** 选择地图位置 */ |
| 570 | const chooseWorkLocation = () => { | 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 | const handleRenewOptions = async (tempKey) => { | 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 | } else { | 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 | } catch (error) { | 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 | } finally { | 498 | } finally { |
| 609 | - uni.removeStorageSync(tempKey); | 499 | +uni.removeStorageSync(tempKey); |
| 610 | } | 500 | } |
| 611 | }; | 501 | }; |
| 612 | - | ||
| 613 | - | ||
| 614 | /** 工单数据回显 */ | 502 | /** 工单数据回显 */ |
| 615 | const echoOrderData = async (orderItem) => { | 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 | const callApiByRole = async (params, isRenewFlag) => { | 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 | } else { | 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 | // ========== 提交工单(纯数组提交,无 dispatchList,校验 roadId 非0) ========== | 577 | // ========== 提交工单(纯数组提交,无 dispatchList,校验 roadId 非0) ========== |
| 699 | const submitWorkOrder = async () => { | 578 | const submitWorkOrder = async () => { |
| 700 | - try { | ||
| 701 | - // 校验公共表单 | ||
| 702 | - await workOrderFormRef.value.validate(); | ||
| 703 | - | ||
| 704 | - // 校验:同一业务线 + 同一问题类型 不允许重复 | 579 | +try { |
| 580 | +// 校验公共表单 | ||
| 581 | +await workOrderFormRef.value.validate(); | ||
| 582 | +// 校验:同一业务线 + 同一问题类型 不允许重复 | ||
| 705 | const list = dispatchList.value; | 583 | const list = dispatchList.value; |
| 706 | const keySet = new Set(); | 584 | const keySet = new Set(); |
| 707 | let hasRepeat = false; | 585 | let hasRepeat = false; |
| 708 | - | ||
| 709 | for (const item of list) { | 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 | if (hasRepeat) { | 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 | } else { | 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 | } catch (error) { | 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,4 +735,4 @@ if (hasRepeat) { | ||
| 866 | .add-dispatch-btn-wrap { | 735 | .add-dispatch-btn-wrap { |
| 867 | margin: 20rpx 0; | 736 | margin: 20rpx 0; |
| 868 | } | 737 | } |
| 869 | -</style> | ||
| 870 | \ No newline at end of file | 738 | \ No newline at end of file |
| 739 | +</style> |
pages-sub/problem/work-order-manage/add-order.vue
| @@ -3,20 +3,18 @@ | @@ -3,20 +3,18 @@ | ||
| 3 | <view class="work-order-form-content commonPageLRpadding"> | 3 | <view class="work-order-form-content commonPageLRpadding"> |
| 4 | <up-form label-position="left" :model="workOrderForm" ref="workOrderFormRef" labelWidth="200rpx"> | 4 | <up-form label-position="left" :model="workOrderForm" ref="workOrderFormRef" labelWidth="200rpx"> |
| 5 | <!-- 业务线单选框:移入form内,作为第一个表单项(核心调整) --> | 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 | </up-radio-group> | 10 | </up-radio-group> |
| 10 | - | ||
| 11 | </up-form-item> | 11 | </up-form-item> |
| 12 | - | ||
| 13 | <!-- 1. 工单位置(地图选择) --> | 12 | <!-- 1. 工单位置(地图选择) --> |
| 14 | <up-form-item label="工单位置" prop="workLocation" border-bottom required | 13 | <up-form-item label="工单位置" prop="workLocation" border-bottom required |
| 15 | @click="chooseWorkLocation(); hideKeyboard()"> | 14 | @click="chooseWorkLocation(); hideKeyboard()"> |
| 16 | <up-input v-model="workOrderForm.workLocation" border="none" readonly suffix-icon="map-fill" | 15 | <up-input v-model="workOrderForm.workLocation" border="none" readonly suffix-icon="map-fill" |
| 17 | placeholder="点击选择工单位置"></up-input> | 16 | placeholder="点击选择工单位置"></up-input> |
| 18 | </up-form-item> | 17 | </up-form-item> |
| 19 | - | ||
| 20 | <!-- 2. 道路名称(下拉框) --> | 18 | <!-- 2. 道路名称(下拉框) --> |
| 21 | <up-form-item label="道路名称" prop="roadName" border-bottom required | 19 | <up-form-item label="道路名称" prop="roadName" border-bottom required |
| 22 | @click="handleActionSheetOpen('roadName'); hideKeyboard()"> | 20 | @click="handleActionSheetOpen('roadName'); hideKeyboard()"> |
| @@ -26,7 +24,6 @@ | @@ -26,7 +24,6 @@ | ||
| 26 | <up-icon name="arrow-right" size="16"></up-icon> | 24 | <up-icon name="arrow-right" size="16"></up-icon> |
| 27 | </template> | 25 | </template> |
| 28 | </up-form-item> | 26 | </up-form-item> |
| 29 | - | ||
| 30 | <!-- 3. 问题类型(下拉框) --> | 27 | <!-- 3. 问题类型(下拉框) --> |
| 31 | <up-form-item label="问题类型" prop="orderName" border-bottom required | 28 | <up-form-item label="问题类型" prop="orderName" border-bottom required |
| 32 | @click="handleActionSheetOpen('orderName'); hideKeyboard()"> | 29 | @click="handleActionSheetOpen('orderName'); hideKeyboard()"> |
| @@ -36,7 +33,6 @@ | @@ -36,7 +33,6 @@ | ||
| 36 | <up-icon name="arrow-right" size="16"></up-icon> | 33 | <up-icon name="arrow-right" size="16"></up-icon> |
| 37 | </template> | 34 | </template> |
| 38 | </up-form-item> | 35 | </up-form-item> |
| 39 | - | ||
| 40 | <!-- 新增:紧急程度选择 --> | 36 | <!-- 新增:紧急程度选择 --> |
| 41 | <up-form-item label="紧急程度" prop="pressingTypeName" border-bottom required | 37 | <up-form-item label="紧急程度" prop="pressingTypeName" border-bottom required |
| 42 | @click="handleActionSheetOpen('pressingType'); hideKeyboard()"> | 38 | @click="handleActionSheetOpen('pressingType'); hideKeyboard()"> |
| @@ -46,13 +42,11 @@ | @@ -46,13 +42,11 @@ | ||
| 46 | <up-icon name="arrow-right" size="16"></up-icon> | 42 | <up-icon name="arrow-right" size="16"></up-icon> |
| 47 | </template> | 43 | </template> |
| 48 | </up-form-item> | 44 | </up-form-item> |
| 49 | - | ||
| 50 | <!-- 4. 情况描述(文本域) --> | 45 | <!-- 4. 情况描述(文本域) --> |
| 51 | <up-form-item label="情况描述" prop="problemDesc" required> | 46 | <up-form-item label="情况描述" prop="problemDesc" required> |
| 52 | <up-textarea placeholder="请输入情况描述(最多200字)" v-model.trim="workOrderForm.problemDesc" count maxlength="200" | 47 | <up-textarea placeholder="请输入情况描述(最多200字)" v-model.trim="workOrderForm.problemDesc" count maxlength="200" |
| 53 | rows="4" @blur="() => workOrderFormRef.validateField('problemDesc')"></up-textarea> | 48 | rows="4" @blur="() => workOrderFormRef.validateField('problemDesc')"></up-textarea> |
| 54 | </up-form-item> | 49 | </up-form-item> |
| 55 | - | ||
| 56 | <!-- 问题照片(核心修复:绑定纯数组) --> | 50 | <!-- 问题照片(核心修复:绑定纯数组) --> |
| 57 | <up-form-item label="问题照片" prop="problemImgs" border-bottom required> | 51 | <up-form-item label="问题照片" prop="problemImgs" border-bottom required> |
| 58 | <up-upload :file-list="problemImgs.imgList.value || []" @after-read="problemImgs.uploadImgs" | 52 | <up-upload :file-list="problemImgs.imgList.value || []" @after-read="problemImgs.uploadImgs" |
| @@ -60,7 +54,6 @@ | @@ -60,7 +54,6 @@ | ||
| 60 | :max-count="problemImgs.uploadConfig.maxCount" :upload-text="problemImgs.uploadConfig.uploadText" | 54 | :max-count="problemImgs.uploadConfig.maxCount" :upload-text="problemImgs.uploadConfig.uploadText" |
| 61 | :size-type="problemImgs.uploadConfig.sizeType"></up-upload> | 55 | :size-type="problemImgs.uploadConfig.sizeType"></up-upload> |
| 62 | </up-form-item> | 56 | </up-form-item> |
| 63 | - | ||
| 64 | <!-- 完成时间 --> | 57 | <!-- 完成时间 --> |
| 65 | <up-form-item label="希望完成时间" prop="expectedFinishDate" @click="show = true; hideKeyboard()"> | 58 | <up-form-item label="希望完成时间" prop="expectedFinishDate" @click="show = true; hideKeyboard()"> |
| 66 | <up-input v-model="workOrderForm.expectedFinishDate" border="none" readonly placeholder="点击选择时间"></up-input> | 59 | <up-input v-model="workOrderForm.expectedFinishDate" border="none" readonly placeholder="点击选择时间"></up-input> |
| @@ -74,25 +67,21 @@ | @@ -74,25 +67,21 @@ | ||
| 74 | </up-form-item> | 67 | </up-form-item> |
| 75 | </up-form> | 68 | </up-form> |
| 76 | </view> | 69 | </view> |
| 77 | - | ||
| 78 | <!-- 底部提交按钮 --> | 70 | <!-- 底部提交按钮 --> |
| 79 | <view class="fixed-bottom-btn-wrap"> | 71 | <view class="fixed-bottom-btn-wrap"> |
| 80 | <up-button type="primary" text="提交工单" @click="submitWorkOrder"></up-button> | 72 | <up-button type="primary" text="提交工单" @click="submitWorkOrder"></up-button> |
| 81 | </view> | 73 | </view> |
| 82 | - | ||
| 83 | <!-- 合并后的通用下拉弹窗 --> | 74 | <!-- 合并后的通用下拉弹窗 --> |
| 84 | <up-action-sheet :show="showActionSheet" :actions="currentActionSheetData.list" | 75 | <up-action-sheet :show="showActionSheet" :actions="currentActionSheetData.list" |
| 85 | :title="currentActionSheetData.title" @close="handleActionSheetClose" | 76 | :title="currentActionSheetData.title" @close="handleActionSheetClose" |
| 86 | @select="handleActionSheetSelect"></up-action-sheet> | 77 | @select="handleActionSheetSelect"></up-action-sheet> |
| 87 | - | ||
| 88 | <!-- 完成时间选择器 --> | 78 | <!-- 完成时间选择器 --> |
| 89 | <up-datetime-picker :show="show" v-model="expectedFinishDate" mode="datetime" :min-date="new Date()" | 79 | <up-datetime-picker :show="show" v-model="expectedFinishDate" mode="datetime" :min-date="new Date()" |
| 90 | @cancel="show = false" @confirm="expectedFinishDateConfirm"></up-datetime-picker> | 80 | @cancel="show = false" @confirm="expectedFinishDateConfirm"></up-datetime-picker> |
| 91 | </view> | 81 | </view> |
| 92 | </template> | 82 | </template> |
| 93 | - | ||
| 94 | <script setup> | 83 | <script setup> |
| 95 | -import { ref, reactive } from 'vue' | 84 | +import { ref, reactive, computed } from 'vue' |
| 96 | import { onReady, onShow, onLoad } from '@dcloudio/uni-app'; | 85 | import { onReady, onShow, onLoad } from '@dcloudio/uni-app'; |
| 97 | import { useUploadImgs } from '@/common/utils/useUploadImgs' | 86 | import { useUploadImgs } from '@/common/utils/useUploadImgs' |
| 98 | import { getRoadListByLatLng } from '@/api/common' | 87 | import { getRoadListByLatLng } from '@/api/common' |
| @@ -100,52 +89,16 @@ import { universalApproval, workorderCreate } from '@/api/work-order-manage/work | @@ -100,52 +89,16 @@ import { universalApproval, workorderCreate } from '@/api/work-order-manage/work | ||
| 100 | import { timeFormat } from '@/uni_modules/uview-plus' | 89 | import { timeFormat } from '@/uni_modules/uview-plus' |
| 101 | import { nextStepMap } from '@/common/utils/common' | 90 | import { nextStepMap } from '@/common/utils/common' |
| 102 | import { useUserStore } from '@/pinia/user'; | 91 | import { useUserStore } from '@/pinia/user'; |
| 103 | - | ||
| 104 | // ========== 状态管理 ========== | 92 | // ========== 状态管理 ========== |
| 105 | const userStore = useUserStore(); | 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 | // ========== 表单Ref ========== | 98 | // ========== 表单Ref ========== |
| 147 | const workOrderFormRef = ref(null) | 99 | const workOrderFormRef = ref(null) |
| 148 | - | 100 | +// 业务线单选框展示用(存中文label) |
| 101 | +const busiLineDisplay = ref('') | ||
| 149 | // ========== 公共上传逻辑复用 ========== | 102 | // ========== 公共上传逻辑复用 ========== |
| 150 | const problemImgs = useUploadImgs({ | 103 | const problemImgs = useUploadImgs({ |
| 151 | maxCount: 3, | 104 | maxCount: 3, |
| @@ -154,12 +107,10 @@ const problemImgs = useUploadImgs({ | @@ -154,12 +107,10 @@ const problemImgs = useUploadImgs({ | ||
| 154 | formRef: workOrderFormRef, | 107 | formRef: workOrderFormRef, |
| 155 | fieldName: 'problemImgs' | 108 | fieldName: 'problemImgs' |
| 156 | }) | 109 | }) |
| 157 | - | ||
| 158 | // 无需再手动初始化普通数组,直接使用 ref 响应式数据 | 110 | // 无需再手动初始化普通数组,直接使用 ref 响应式数据 |
| 159 | if (!Array.isArray(problemImgs.rawImgList.value)) { | 111 | if (!Array.isArray(problemImgs.rawImgList.value)) { |
| 160 | problemImgs.rawImgList.value = []; | 112 | problemImgs.rawImgList.value = []; |
| 161 | } | 113 | } |
| 162 | - | ||
| 163 | // ========== 页面状态 ========== | 114 | // ========== 页面状态 ========== |
| 164 | // 通用弹窗控制 | 115 | // 通用弹窗控制 |
| 165 | const showActionSheet = ref(false) | 116 | const showActionSheet = ref(false) |
| @@ -172,19 +123,16 @@ const currentActionSheetData = reactive({ | @@ -172,19 +123,16 @@ const currentActionSheetData = reactive({ | ||
| 172 | // 完成时间选择器控制 | 123 | // 完成时间选择器控制 |
| 173 | const show = ref(false) | 124 | const show = ref(false) |
| 174 | const expectedFinishDate = ref(Date.now()) | 125 | const expectedFinishDate = ref(Date.now()) |
| 175 | - | ||
| 176 | // ========== 重新提交相关状态(核心:本地存储读取) ========== | 126 | // ========== 重新提交相关状态(核心:本地存储读取) ========== |
| 177 | const isRenew = ref(false); // 是否为重新提交状态 | 127 | const isRenew = ref(false); // 是否为重新提交状态 |
| 178 | const renewOrderData = ref(null); // 重新提交的原有工单数据 | 128 | const renewOrderData = ref(null); // 重新提交的原有工单数据 |
| 179 | - | ||
| 180 | // ========== 下拉列表数据 ========== | 129 | // ========== 下拉列表数据 ========== |
| 181 | const roadNameList = ref([]) | 130 | const roadNameList = ref([]) |
| 182 | const orderNameList = ref([]) | 131 | const orderNameList = ref([]) |
| 183 | const pressingTypeList = ref([]) | 132 | const pressingTypeList = ref([]) |
| 184 | - | ||
| 185 | -// ========== 工单表单数据(核心调整:新增busiLineCn字段,绑定业务线) ========== | 133 | +// ========== 工单表单数据 ========== |
| 186 | const workOrderForm = reactive({ | 134 | const workOrderForm = reactive({ |
| 187 | - busiLineCn: '', // 新增:业务线中文名称(用于form绑定、校验、回显) | 135 | + busiLine: '', // 业务线value,yl / sz / wy |
| 188 | roadId: 0, // 原有字段 | 136 | roadId: 0, // 原有字段 |
| 189 | roadName: '', // 原有字段 | 137 | roadName: '', // 原有字段 |
| 190 | workLocation: '', // 原有字段 | 138 | workLocation: '', // 原有字段 |
| @@ -196,10 +144,9 @@ const workOrderForm = reactive({ | @@ -196,10 +144,9 @@ const workOrderForm = reactive({ | ||
| 196 | lon: 0, // 原有字段 | 144 | lon: 0, // 原有字段 |
| 197 | expectedFinishDate: '', // 原有字段 | 145 | expectedFinishDate: '', // 原有字段 |
| 198 | }) | 146 | }) |
| 199 | - | ||
| 200 | -// ========== 表单校验规则(核心调整:新增busiLineCn校验规则) ========== | 147 | +// ========== 表单校验规则 ========== |
| 201 | const workOrderFormRules = reactive({ | 148 | const workOrderFormRules = reactive({ |
| 202 | - busiLineCn: [ // 新增:业务线必选校验 | 149 | + busiLine: [ |
| 203 | { type: 'string', required: true, message: '请选择业务线', trigger: ['change', 'blur'] } | 150 | { type: 'string', required: true, message: '请选择业务线', trigger: ['change', 'blur'] } |
| 204 | ], | 151 | ], |
| 205 | workLocation: [ | 152 | workLocation: [ |
| @@ -220,30 +167,25 @@ const workOrderFormRules = reactive({ | @@ -220,30 +167,25 @@ const workOrderFormRules = reactive({ | ||
| 220 | ], | 167 | ], |
| 221 | problemImgs: [problemImgs.imgValidateRule] // 复用上传校验规则 | 168 | problemImgs: [problemImgs.imgValidateRule] // 复用上传校验规则 |
| 222 | }) | 169 | }) |
| 223 | - | ||
| 224 | // ========== 生命周期 ========== | 170 | // ========== 生命周期 ========== |
| 225 | -// 页面加载:读取本地存储的工单数据(适配workOrderForm.busiLineCn) | 171 | +// 页面加载 |
| 226 | onLoad((options) => { | 172 | onLoad((options) => { |
| 227 | - // 初始化业务线选项 | ||
| 228 | - formatBusiLineOptions(); | ||
| 229 | - // 默认选中第一个业务线(赋值给workOrderForm.busiLineCn,核心调整) | 173 | + // 默认选中第一个业务线 |
| 230 | if (busiLineOptions.value.length > 0) { | 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 | console.log(options) | 180 | console.log(options) |
| 236 | if (options.isRenew == 1 && options.tempKey) { | 181 | if (options.isRenew == 1 && options.tempKey) { |
| 237 | isRenew.value = true; | 182 | isRenew.value = true; |
| 238 | const tempKey = options.tempKey; | 183 | const tempKey = options.tempKey; |
| 239 | - | ||
| 240 | // 1. 从本地同步存储中读取完整工单数据 | 184 | // 1. 从本地同步存储中读取完整工单数据 |
| 241 | try { | 185 | try { |
| 242 | const orderData = uni.getStorageSync(tempKey); | 186 | const orderData = uni.getStorageSync(tempKey); |
| 243 | if (orderData && typeof orderData === 'object') { | 187 | if (orderData && typeof orderData === 'object') { |
| 244 | renewOrderData.value = orderData; | 188 | renewOrderData.value = orderData; |
| 245 | - | ||
| 246 | - console.log('123213') | ||
| 247 | console.log(orderData) | 189 | console.log(orderData) |
| 248 | // 2. 回显工单数据到表单 | 190 | // 2. 回显工单数据到表单 |
| 249 | echoOrderData(renewOrderData.value); | 191 | echoOrderData(renewOrderData.value); |
| @@ -264,7 +206,6 @@ onLoad((options) => { | @@ -264,7 +206,6 @@ onLoad((options) => { | ||
| 264 | } | 206 | } |
| 265 | } | 207 | } |
| 266 | }); | 208 | }); |
| 267 | - | ||
| 268 | onReady(() => { | 209 | onReady(() => { |
| 269 | // 设置表单校验规则 | 210 | // 设置表单校验规则 |
| 270 | if (workOrderFormRef.value) { | 211 | if (workOrderFormRef.value) { |
| @@ -272,27 +213,26 @@ onReady(() => { | @@ -272,27 +213,26 @@ onReady(() => { | ||
| 272 | } | 213 | } |
| 273 | console.log('工单表单规则初始化完成') | 214 | console.log('工单表单规则初始化完成') |
| 274 | }) | 215 | }) |
| 275 | - | ||
| 276 | onShow(() => { | 216 | onShow(() => { |
| 277 | console.log(uni.$dict.getDictLabel('ai_image_status', 20)) | 217 | console.log(uni.$dict.getDictLabel('ai_image_status', 20)) |
| 278 | console.log(uni.$dict.getDictSimpleList('work_name')) | 218 | console.log(uni.$dict.getDictSimpleList('work_name')) |
| 279 | - | ||
| 280 | // 初始化问题类型列表 | 219 | // 初始化问题类型列表 |
| 281 | orderNameList.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('work_name')) | 220 | orderNameList.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('work_name')) |
| 282 | console.log('问题类型列表:', orderNameList.value) | 221 | console.log('问题类型列表:', orderNameList.value) |
| 283 | - | ||
| 284 | // 初始化紧急程度列表 | 222 | // 初始化紧急程度列表 |
| 285 | pressingTypeList.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('workorder_pressing_type')) | 223 | pressingTypeList.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('workorder_pressing_type')) |
| 286 | console.log('紧急程度列表:', pressingTypeList.value) | 224 | console.log('紧急程度列表:', pressingTypeList.value) |
| 287 | }) | 225 | }) |
| 288 | - | ||
| 289 | -// ========== 核心方法:工单数据回显(适配workOrderForm.busiLineCn) ========== | 226 | +// ========== 核心方法:工单数据回显 ========== |
| 290 | const echoOrderData = (orderItem) => { | 227 | const echoOrderData = (orderItem) => { |
| 291 | - // 1. 回显业务线:将工单的英文busiLine转为中文名称,赋值给workOrderForm.busiLineCn(核心调整) | 228 | + // 业务线回显,后端给的是value,需要自动找到中文赋值给单选框 |
| 292 | if (orderItem.busiLine) { | 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 | // 2. 原有基础表单字段回显 | 236 | // 2. 原有基础表单字段回显 |
| 297 | workOrderForm.roadId = orderItem.roadId || 0; | 237 | workOrderForm.roadId = orderItem.roadId || 0; |
| 298 | workOrderForm.roadName = orderItem.roadName || ''; | 238 | workOrderForm.roadName = orderItem.roadName || ''; |
| @@ -304,7 +244,6 @@ const echoOrderData = (orderItem) => { | @@ -304,7 +244,6 @@ const echoOrderData = (orderItem) => { | ||
| 304 | workOrderForm.lat = orderItem.lat || 0; | 244 | workOrderForm.lat = orderItem.lat || 0; |
| 305 | workOrderForm.lon = orderItem.lon || 0; | 245 | workOrderForm.lon = orderItem.lon || 0; |
| 306 | workOrderForm.expectedFinishDate = orderItem.expectedFinishDate ? timeFormat(orderItem.expectedFinishDate, 'yyyy-mm-dd hh:MM:ss') : ''; | 246 | workOrderForm.expectedFinishDate = orderItem.expectedFinishDate ? timeFormat(orderItem.expectedFinishDate, 'yyyy-mm-dd hh:MM:ss') : ''; |
| 307 | - | ||
| 308 | // 3. 上传图片回显(兼容useUploadImgs格式) | 247 | // 3. 上传图片回显(兼容useUploadImgs格式) |
| 309 | if (orderItem.problemsImgs && Array.isArray(orderItem.problemsImgs) && orderItem.problemsImgs.length > 0) { | 248 | if (orderItem.problemsImgs && Array.isArray(orderItem.problemsImgs) && orderItem.problemsImgs.length > 0) { |
| 310 | const imgList = orderItem.problemsImgs.map((imgUrl, index) => ({ | 249 | const imgList = orderItem.problemsImgs.map((imgUrl, index) => ({ |
| @@ -315,18 +254,22 @@ const echoOrderData = (orderItem) => { | @@ -315,18 +254,22 @@ const echoOrderData = (orderItem) => { | ||
| 315 | problemImgs.imgList.value = imgList; | 254 | problemImgs.imgList.value = imgList; |
| 316 | problemImgs.rawImgList.value = imgList; | 255 | problemImgs.rawImgList.value = imgList; |
| 317 | } | 256 | } |
| 318 | - | ||
| 319 | - // 4. 自动获取道路列表(修改:传递当前选中的业务线英文标识) | 257 | + // 4. 自动获取道路列表 |
| 320 | if (orderItem.lat && orderItem.lon) { | 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 | // 1. 清空已渲染的道路名称和表单字段 | 273 | // 1. 清空已渲染的道路名称和表单字段 |
| 331 | workOrderForm.roadName = ''; | 274 | workOrderForm.roadName = ''; |
| 332 | workOrderForm.roadId = 0; | 275 | workOrderForm.roadId = 0; |
| @@ -337,26 +280,17 @@ const handleBusiLineChange = () => { | @@ -337,26 +280,17 @@ const handleBusiLineChange = () => { | ||
| 337 | getRoadListByBusiLine(); | 280 | getRoadListByBusiLine(); |
| 338 | } | 281 | } |
| 339 | }; | 282 | }; |
| 340 | - | ||
| 341 | /** | 283 | /** |
| 342 | - * 封装:根据当前选中业务线(中文)获取英文标识,再请求道路列表 | 284 | + * 封装:根据当前选中业务线,请求道路列表 |
| 343 | */ | 285 | */ |
| 344 | const getRoadListByBusiLine = async () => { | 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 | return; | 288 | return; |
| 353 | } | 289 | } |
| 354 | - | ||
| 355 | try { | 290 | try { |
| 356 | uni.showLoading({ title: '获取道路名称中...' }); | 291 | uni.showLoading({ title: '获取道路名称中...' }); |
| 357 | const roadRes = await getRoadListByLatLng({ | 292 | const roadRes = await getRoadListByLatLng({ |
| 358 | - busiLine: busiLineEn, // 传递英文标识(yl/sz/wy),接口正常使用 | ||
| 359 | - // companyCode: 'sls', // 传递英文标识(yl/sz/wy),接口正常使用 | 293 | + busiLine: workOrderForm.busiLine, |
| 360 | latitude: workOrderForm.lat, | 294 | latitude: workOrderForm.lat, |
| 361 | longitude: workOrderForm.lon | 295 | longitude: workOrderForm.lon |
| 362 | }); | 296 | }); |
| @@ -378,7 +312,6 @@ const getRoadListByBusiLine = async () => { | @@ -378,7 +312,6 @@ const getRoadListByBusiLine = async () => { | ||
| 378 | roadNameList.value = [{ name: '获取失败,请重新选择位置', code: '', id: 0 }]; | 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,7 +322,6 @@ const handleActionSheetOpen = (type) => { | ||
| 389 | uni.showToast({ title: '请先选择工单位置', icon: 'none' }) | 322 | uni.showToast({ title: '请先选择工单位置', icon: 'none' }) |
| 390 | return | 323 | return |
| 391 | } | 324 | } |
| 392 | - | ||
| 393 | // 配置当前弹窗参数 | 325 | // 配置当前弹窗参数 |
| 394 | const configMap = { | 326 | const configMap = { |
| 395 | roadName: { | 327 | roadName: { |
| @@ -405,13 +337,11 @@ const handleActionSheetOpen = (type) => { | @@ -405,13 +337,11 @@ const handleActionSheetOpen = (type) => { | ||
| 405 | list: pressingTypeList.value | 337 | list: pressingTypeList.value |
| 406 | } | 338 | } |
| 407 | } | 339 | } |
| 408 | - | ||
| 409 | currentActionSheetData.type = type | 340 | currentActionSheetData.type = type |
| 410 | currentActionSheetData.title = configMap[type].title | 341 | currentActionSheetData.title = configMap[type].title |
| 411 | currentActionSheetData.list = configMap[type].list | 342 | currentActionSheetData.list = configMap[type].list |
| 412 | showActionSheet.value = true | 343 | showActionSheet.value = true |
| 413 | } | 344 | } |
| 414 | - | ||
| 415 | /** | 345 | /** |
| 416 | * 关闭通用下拉弹窗 | 346 | * 关闭通用下拉弹窗 |
| 417 | */ | 347 | */ |
| @@ -422,7 +352,6 @@ const handleActionSheetClose = () => { | @@ -422,7 +352,6 @@ const handleActionSheetClose = () => { | ||
| 422 | currentActionSheetData.list = [] | 352 | currentActionSheetData.list = [] |
| 423 | currentActionSheetData.title = '' | 353 | currentActionSheetData.title = '' |
| 424 | } | 354 | } |
| 425 | - | ||
| 426 | /** | 355 | /** |
| 427 | * 通用下拉弹窗选择事件 | 356 | * 通用下拉弹窗选择事件 |
| 428 | */ | 357 | */ |
| @@ -449,7 +378,6 @@ const handleActionSheetSelect = (e) => { | @@ -449,7 +378,6 @@ const handleActionSheetSelect = (e) => { | ||
| 449 | // 关闭弹窗 | 378 | // 关闭弹窗 |
| 450 | showActionSheet.value = false | 379 | showActionSheet.value = false |
| 451 | } | 380 | } |
| 452 | - | ||
| 453 | /** | 381 | /** |
| 454 | * 返回上一页 | 382 | * 返回上一页 |
| 455 | */ | 383 | */ |
| @@ -462,14 +390,12 @@ const navigateBack = () => { | @@ -462,14 +390,12 @@ const navigateBack = () => { | ||
| 462 | } | 390 | } |
| 463 | }); | 391 | }); |
| 464 | } | 392 | } |
| 465 | - | ||
| 466 | -// 新增:清除希望完成时间的方法 | 393 | +/** |
| 394 | + * 清除希望完成时间的方法 | ||
| 395 | + */ | ||
| 467 | const clearExpectedFinishDate = () => { | 396 | const clearExpectedFinishDate = () => { |
| 468 | workOrderForm.expectedFinishDate = '' | 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,15 +405,11 @@ const chooseWorkLocation = () => { | ||
| 479 | workOrderForm.roadName = '' | 405 | workOrderForm.roadName = '' |
| 480 | workOrderForm.roadId = 0 | 406 | workOrderForm.roadId = 0 |
| 481 | roadNameList.value = [] | 407 | roadNameList.value = [] |
| 482 | - | ||
| 483 | workOrderForm.workLocation = res.name | 408 | workOrderForm.workLocation = res.name |
| 484 | workOrderForm.lat = res.latitude | 409 | workOrderForm.lat = res.latitude |
| 485 | workOrderForm.lon = res.longitude | 410 | workOrderForm.lon = res.longitude |
| 486 | - | ||
| 487 | workOrderFormRef.value?.validateField('workLocation') | 411 | workOrderFormRef.value?.validateField('workLocation') |
| 488 | workOrderFormRef.value?.validateField('roadName') | 412 | workOrderFormRef.value?.validateField('roadName') |
| 489 | - | ||
| 490 | - // 改用封装后的方法,自动携带选中的业务线英文标识 | ||
| 491 | await getRoadListByBusiLine(); | 413 | await getRoadListByBusiLine(); |
| 492 | }, | 414 | }, |
| 493 | fail: (err) => { | 415 | fail: (err) => { |
| @@ -496,7 +418,6 @@ const chooseWorkLocation = () => { | @@ -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,25 +426,20 @@ const expectedFinishDateConfirm = (e) => { | ||
| 505 | workOrderForm.expectedFinishDate = timeFormat(e.value, 'yyyy-mm-dd hh:MM:ss') | 426 | workOrderForm.expectedFinishDate = timeFormat(e.value, 'yyyy-mm-dd hh:MM:ss') |
| 506 | show.value = false | 427 | show.value = false |
| 507 | } | 428 | } |
| 508 | - | ||
| 509 | /** | 429 | /** |
| 510 | * 隐藏键盘 | 430 | * 隐藏键盘 |
| 511 | */ | 431 | */ |
| 512 | const hideKeyboard = () => { | 432 | const hideKeyboard = () => { |
| 513 | uni.hideKeyboard() | 433 | uni.hideKeyboard() |
| 514 | } | 434 | } |
| 515 | - | ||
| 516 | /** | 435 | /** |
| 517 | - * 提交工单(区分新增/重新提交,接口隔离:从workOrderForm.busiLineCn取值) | 436 | + * 提交工单 |
| 518 | */ | 437 | */ |
| 519 | const submitWorkOrder = async () => { | 438 | const submitWorkOrder = async () => { |
| 520 | try { | 439 | try { |
| 521 | - // 先执行表单校验(包含业务线校验) | 440 | + // 先执行表单校验 |
| 522 | await workOrderFormRef.value.validate() | 441 | await workOrderFormRef.value.validate() |
| 523 | - | ||
| 524 | - // 核心:从workOrderForm.busiLineCn获取中文名称,转为英文标识(核心调整) | ||
| 525 | - const busiLineEn = getBusiLineEnByCn(workOrderForm.busiLineCn); | ||
| 526 | - if (!busiLineEn) { | 442 | + if (!workOrderForm.busiLine) { |
| 527 | uni.showToast({ title: '业务线选择异常,请重新选择', icon: 'none' }); | 443 | uni.showToast({ title: '业务线选择异常,请重新选择', icon: 'none' }); |
| 528 | return; | 444 | return; |
| 529 | } | 445 | } |
| @@ -532,7 +448,7 @@ const submitWorkOrder = async () => { | @@ -532,7 +448,7 @@ const submitWorkOrder = async () => { | ||
| 532 | const commonSubmitData = { | 448 | const commonSubmitData = { |
| 533 | roadId: workOrderForm.roadId, | 449 | roadId: workOrderForm.roadId, |
| 534 | roadName: workOrderForm.roadName, | 450 | roadName: workOrderForm.roadName, |
| 535 | - problemsImgs: problemImgs.getSuccessImgUrls(), // 复用上传逻辑的URL获取方法 | 451 | + problemsImgs: problemImgs.getSuccessImgUrls(), |
| 536 | remark: workOrderForm.problemDesc.trim(), | 452 | remark: workOrderForm.problemDesc.trim(), |
| 537 | latLonType: 2, | 453 | latLonType: 2, |
| 538 | lat: workOrderForm.lat, | 454 | lat: workOrderForm.lat, |
| @@ -542,8 +458,8 @@ const submitWorkOrder = async () => { | @@ -542,8 +458,8 @@ const submitWorkOrder = async () => { | ||
| 542 | orderName: workOrderForm.orderName, | 458 | orderName: workOrderForm.orderName, |
| 543 | expectedFinishDate: workOrderForm.expectedFinishDate == "" ? "" : new Date(workOrderForm.expectedFinishDate).getTime(), | 459 | expectedFinishDate: workOrderForm.expectedFinishDate == "" ? "" : new Date(workOrderForm.expectedFinishDate).getTime(), |
| 544 | sourceId: 1, | 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 | console.log('工单数据') | 464 | console.log('工单数据') |
| 549 | console.log('完成时间:' + workOrderForm.expectedFinishDate) | 465 | console.log('完成时间:' + workOrderForm.expectedFinishDate) |
| @@ -551,34 +467,28 @@ const submitWorkOrder = async () => { | @@ -551,34 +467,28 @@ const submitWorkOrder = async () => { | ||
| 551 | // 显示加载中 | 467 | // 显示加载中 |
| 552 | uni.showLoading({ title: '提交中...' }) | 468 | uni.showLoading({ title: '提交中...' }) |
| 553 | let res | 469 | let res |
| 554 | - | ||
| 555 | - // 核心:根据状态区分接口调用 | ||
| 556 | if (isRenew.value) { | 470 | if (isRenew.value) { |
| 557 | // 重新提交:调用 universalApproval 接口 | 471 | // 重新提交:调用 universalApproval 接口 |
| 558 | const renewSubmitData = { | 472 | const renewSubmitData = { |
| 559 | - // 原有工单必要参数 | ||
| 560 | workerDataId: renewOrderData.value.id, | 473 | workerDataId: renewOrderData.value.id, |
| 561 | taskKey: renewOrderData.value.taskKey, | 474 | taskKey: renewOrderData.value.taskKey, |
| 562 | taskId: renewOrderData.value.taskId, | 475 | taskId: renewOrderData.value.taskId, |
| 563 | operateType: nextStepMap[renewOrderData.value.taskKey]?.operateTypeRenew || '', | 476 | operateType: nextStepMap[renewOrderData.value.taskKey]?.operateTypeRenew || '', |
| 564 | agree: 0, | 477 | agree: 0, |
| 565 | reason: workOrderForm.problemDesc.trim(), | 478 | reason: workOrderForm.problemDesc.trim(), |
| 566 | - // 新编辑的工单数据 | ||
| 567 | ...commonSubmitData | 479 | ...commonSubmitData |
| 568 | } | 480 | } |
| 569 | res = await universalApproval(renewSubmitData) | 481 | res = await universalApproval(renewSubmitData) |
| 570 | } else { | 482 | } else { |
| 571 | - // 新增工单:调用原有 workorderCreate 接口(不影响原有功能) | 483 | + // 新增工单:调用原有 workorderCreate 接口 |
| 572 | res = await workorderCreate(commonSubmitData) | 484 | res = await workorderCreate(commonSubmitData) |
| 573 | } | 485 | } |
| 574 | - | ||
| 575 | uni.hideLoading() | 486 | uni.hideLoading() |
| 576 | uni.showToast({ | 487 | uni.showToast({ |
| 577 | title: isRenew.value ? '重新提交成功' : '工单提交成功', | 488 | title: isRenew.value ? '重新提交成功' : '工单提交成功', |
| 578 | icon: 'success', | 489 | icon: 'success', |
| 579 | duration: 1000 | 490 | duration: 1000 |
| 580 | }) | 491 | }) |
| 581 | - | ||
| 582 | // 延迟跳转回列表页 | 492 | // 延迟跳转回列表页 |
| 583 | setTimeout(() => { | 493 | setTimeout(() => { |
| 584 | uni.reLaunch({ | 494 | uni.reLaunch({ |
| @@ -588,13 +498,12 @@ const submitWorkOrder = async () => { | @@ -588,13 +498,12 @@ const submitWorkOrder = async () => { | ||
| 588 | } catch (error) { | 498 | } catch (error) { |
| 589 | // 隐藏加载框 | 499 | // 隐藏加载框 |
| 590 | uni.hideLoading() | 500 | uni.hideLoading() |
| 591 | - | ||
| 592 | // 区分是表单校验失败还是接口调用失败 | 501 | // 区分是表单校验失败还是接口调用失败 |
| 593 | if (!Array.isArray(error)) { | 502 | if (!Array.isArray(error)) { |
| 594 | // 接口调用失败 | 503 | // 接口调用失败 |
| 595 | console.error(isRenew.value ? '工单重新提交失败:' : '工单提交失败:', error) | 504 | console.error(isRenew.value ? '工单重新提交失败:' : '工单提交失败:', error) |
| 596 | uni.showToast({ | 505 | uni.showToast({ |
| 597 | - title: isRenew.value ? error.msg : error.msg, | 506 | + title: error.msg || '提交失败', |
| 598 | icon: 'none', | 507 | icon: 'none', |
| 599 | duration: 2000 | 508 | duration: 2000 |
| 600 | }) | 509 | }) |
| @@ -602,7 +511,6 @@ const submitWorkOrder = async () => { | @@ -602,7 +511,6 @@ const submitWorkOrder = async () => { | ||
| 602 | } | 511 | } |
| 603 | } | 512 | } |
| 604 | </script> | 513 | </script> |
| 605 | - | ||
| 606 | <style lang="scss" scoped> | 514 | <style lang="scss" scoped> |
| 607 | // 全局页面样式 | 515 | // 全局页面样式 |
| 608 | .page-container { | 516 | .page-container { |
| @@ -610,9 +518,8 @@ const submitWorkOrder = async () => { | @@ -610,9 +518,8 @@ const submitWorkOrder = async () => { | ||
| 610 | padding-bottom: 100rpx; // 给底部按钮留空间 | 518 | padding-bottom: 100rpx; // 给底部按钮留空间 |
| 611 | } | 519 | } |
| 612 | 520 | ||
| 613 | - | ||
| 614 | // 工单表单内容容器 | 521 | // 工单表单内容容器 |
| 615 | .work-order-form-content { | 522 | .work-order-form-content { |
| 616 | background: #fff; | 523 | background: #fff; |
| 617 | } | 524 | } |
| 618 | -</style> | ||
| 619 | \ No newline at end of file | 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 | // 新增:导入 refreshToken 接口 | 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 | // 新增:定义刷新Token的定时器标识(避免重复创建定时器) | 14 | // 新增:定义刷新Token的定时器标识(避免重复创建定时器) |
| 8 | let refreshTokenTimer = null; | 15 | let refreshTokenTimer = null; |
| 9 | - | ||
| 10 | -export const useUserStore = defineStore('user', { | 16 | +export const useUserStore = defineStore("user", { |
| 11 | // 修复1:删除重复的 state 定义,只保留根层级的 state | 17 | // 修复1:删除重复的 state 定义,只保留根层级的 state |
| 12 | state: () => ({ | 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 | userInfo: cache.get(globalConfig.cache.userInfoKey) || {}, | 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 | expireTime: cache.get(globalConfig.cache.expireTimeKey) || 0, | 24 | expireTime: cache.get(globalConfig.cache.expireTimeKey) || 0, |
| 19 | dictData: cache.get(globalConfig.cache.dictDataKey) || {}, | 25 | dictData: cache.get(globalConfig.cache.dictDataKey) || {}, |
| 20 | - logoutLoading: false | 26 | + dictList: cache.get(globalConfig.cache.dictListKey) || [], // 用来存放【过滤后的】字典数组 |
| 27 | + logoutLoading: false, | ||
| 21 | }), | 28 | }), |
| 22 | - | ||
| 23 | getters: { | 29 | getters: { |
| 24 | isLogin: (state) => { | 30 | isLogin: (state) => { |
| 25 | if (!state.token) return false; | 31 | if (!state.token) return false; |
| @@ -35,104 +41,114 @@ export const useUserStore = defineStore('user', { | @@ -35,104 +41,114 @@ export const useUserStore = defineStore('user', { | ||
| 35 | const remainingTime = state.expireTime - Date.now(); | 41 | const remainingTime = state.expireTime - Date.now(); |
| 36 | // 剩余时间大于0秒 且 小于30秒(即将过期) | 42 | // 剩余时间大于0秒 且 小于30秒(即将过期) |
| 37 | return remainingTime > 0 && remainingTime < 30 * 1000; | 43 | return remainingTime > 0 && remainingTime < 30 * 1000; |
| 38 | - } | 44 | + }, |
| 39 | }, | 45 | }, |
| 40 | - | ||
| 41 | actions: { | 46 | actions: { |
| 42 | async login(params) { | 47 | async login(params) { |
| 43 | try { | 48 | try { |
| 44 | // smsLogin | 49 | // smsLogin |
| 45 | - let res | ||
| 46 | - if(params.type=='sms'){ | 50 | + let res; |
| 51 | + if (params.type == "sms") { | ||
| 47 | res = await smsLogin(params); | 52 | res = await smsLogin(params); |
| 48 | - }else{ | 53 | + } else { |
| 49 | res = await login(params); | 54 | res = await login(params); |
| 50 | } | 55 | } |
| 51 | - | ||
| 52 | // 新增:从登录接口返回值中获取 refreshToken(若接口返回字段名不一致,可调整,如 res.refresh_token) | 56 | // 新增:从登录接口返回值中获取 refreshToken(若接口返回字段名不一致,可调整,如 res.refresh_token) |
| 53 | const { accessToken, expiresTime, userId, refreshToken } = res; | 57 | const { accessToken, expiresTime, userId, refreshToken } = res; |
| 54 | - | ||
| 55 | if (!accessToken) { | 58 | if (!accessToken) { |
| 56 | - throw new Error('登录失败,未获取到令牌'); | 59 | + throw new Error("登录失败,未获取到令牌"); |
| 57 | } | 60 | } |
| 58 | - | ||
| 59 | // 更新 Pinia state | 61 | // 更新 Pinia state |
| 60 | this.token = accessToken; | 62 | this.token = accessToken; |
| 61 | - this.refreshToken = refreshToken || ''; // 新增:存储Refresh Token | 63 | + this.refreshToken = refreshToken || ""; // 新增:存储Refresh Token |
| 62 | this.expireTime = expiresTime; | 64 | this.expireTime = expiresTime; |
| 63 | this.userId = userId; | 65 | this.userId = userId; |
| 64 | this.userInfo = {}; | 66 | this.userInfo = {}; |
| 65 | - | ||
| 66 | // 等待 Pinia 持久化同步 | 67 | // 等待 Pinia 持久化同步 |
| 67 | - await new Promise(resolve => setTimeout(resolve, 50)); | ||
| 68 | - | 68 | + await new Promise((resolve) => setTimeout(resolve, 50)); |
| 69 | // 获取用户信息 | 69 | // 获取用户信息 |
| 70 | const userInfo = await this.getUserInfo(); | 70 | const userInfo = await this.getUserInfo(); |
| 71 | this.userInfo = userInfo; | 71 | this.userInfo = userInfo; |
| 72 | - | ||
| 73 | // 获取模块列表 | 72 | // 获取模块列表 |
| 74 | let moduleListInfo = null; | 73 | let moduleListInfo = null; |
| 75 | try { | 74 | try { |
| 76 | moduleListInfo = await this.getModuleList(); | 75 | moduleListInfo = await this.getModuleList(); |
| 77 | this.moduleListInfo = moduleListInfo; | 76 | this.moduleListInfo = moduleListInfo; |
| 78 | } catch (moduleErr) { | 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 | // 修复2:调用字典接口时,不直接抛错(避免阻断登录) | 84 | // 修复2:调用字典接口时,不直接抛错(避免阻断登录) |
| 84 | try { | 85 | try { |
| 85 | const dictRes = await this.getAndSaveDictData(); | 86 | const dictRes = await this.getAndSaveDictData(); |
| 86 | // 仅接口返回成功时才更新字典 | 87 | // 仅接口返回成功时才更新字典 |
| 87 | this.dictData = dictRes || {}; | 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 | } catch (dictErr) { | 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 | // 新增:登录成功后,启动 Token 自动刷新定时器 | 116 | // 新增:登录成功后,启动 Token 自动刷新定时器 |
| 95 | this.startRefreshTokenTimer(); | 117 | this.startRefreshTokenTimer(); |
| 96 | - | ||
| 97 | return { ...res, userInfo, moduleListInfo }; | 118 | return { ...res, userInfo, moduleListInfo }; |
| 98 | } catch (err) { | 119 | } catch (err) { |
| 99 | - console.error('登录流程失败:', err); | 120 | + console.error("登录流程失败:", err); |
| 100 | throw err; | 121 | throw err; |
| 101 | } | 122 | } |
| 102 | }, | 123 | }, |
| 103 | - | ||
| 104 | // 新增:调用刷新Token接口,返回刷新是否成功 | 124 | // 新增:调用刷新Token接口,返回刷新是否成功 |
| 105 | async refreshTokenApi() { | 125 | async refreshTokenApi() { |
| 106 | // 前置校验:无Refresh Token直接返回失败 | 126 | // 前置校验:无Refresh Token直接返回失败 |
| 107 | if (!this.refreshToken) { | 127 | if (!this.refreshToken) { |
| 108 | - console.warn('无刷新令牌,无法刷新Token'); | 128 | + console.warn("无刷新令牌,无法刷新Token"); |
| 109 | this.logout(); // 无刷新令牌,直接退出登录 | 129 | this.logout(); // 无刷新令牌,直接退出登录 |
| 110 | return false; | 130 | return false; |
| 111 | } | 131 | } |
| 112 | - | ||
| 113 | try { | 132 | try { |
| 114 | // 调用修正后的 refreshToken 接口,传入当前的 refreshToken | 133 | // 调用修正后的 refreshToken 接口,传入当前的 refreshToken |
| 115 | const res = await refreshToken(this.refreshToken); | 134 | const res = await refreshToken(this.refreshToken); |
| 116 | - | ||
| 117 | // 校验接口返回结果(根据你的实际接口返回格式调整) | 135 | // 校验接口返回结果(根据你的实际接口返回格式调整) |
| 118 | if (!res || !res.accessToken) { | 136 | if (!res || !res.accessToken) { |
| 119 | - throw new Error('刷新Token失败,未获取到新令牌'); | 137 | + throw new Error("刷新Token失败,未获取到新令牌"); |
| 120 | } | 138 | } |
| 121 | - | ||
| 122 | // 更新 Token 相关状态 | 139 | // 更新 Token 相关状态 |
| 123 | this.token = res.accessToken; // 新的访问令牌 | 140 | this.token = res.accessToken; // 新的访问令牌 |
| 124 | this.refreshToken = res.refreshToken || this.refreshToken; // 若接口返回新的Refresh Token则更新 | 141 | this.refreshToken = res.refreshToken || this.refreshToken; // 若接口返回新的Refresh Token则更新 |
| 125 | this.expireTime = res.expiresTime; // 新的过期时间 | 142 | this.expireTime = res.expiresTime; // 新的过期时间 |
| 126 | - console.log('Token 刷新成功'); | 143 | + console.log("Token 刷新成功"); |
| 127 | return true; | 144 | return true; |
| 128 | } catch (err) { | 145 | } catch (err) { |
| 129 | - console.error('刷新Token失败:', err); | 146 | + console.error("刷新Token失败:", err); |
| 130 | // 刷新失败,直接退出登录 | 147 | // 刷新失败,直接退出登录 |
| 131 | this.logout(); | 148 | this.logout(); |
| 132 | return false; | 149 | return false; |
| 133 | } | 150 | } |
| 134 | }, | 151 | }, |
| 135 | - | ||
| 136 | // 新增:启动 Token 自动刷新定时器 | 152 | // 新增:启动 Token 自动刷新定时器 |
| 137 | startRefreshTokenTimer() { | 153 | startRefreshTokenTimer() { |
| 138 | // 先清除已有定时器,避免重复创建 | 154 | // 先清除已有定时器,避免重复创建 |
| @@ -140,7 +156,6 @@ export const useUserStore = defineStore('user', { | @@ -140,7 +156,6 @@ export const useUserStore = defineStore('user', { | ||
| 140 | clearTimeout(refreshTokenTimer); | 156 | clearTimeout(refreshTokenTimer); |
| 141 | refreshTokenTimer = null; | 157 | refreshTokenTimer = null; |
| 142 | } | 158 | } |
| 143 | - | ||
| 144 | // 计算剩余时间(过期前30秒执行刷新) | 159 | // 计算剩余时间(过期前30秒执行刷新) |
| 145 | const remainingTime = this.expireTime - Date.now() - 30 * 1000; | 160 | const remainingTime = this.expireTime - Date.now() - 30 * 1000; |
| 146 | if (remainingTime <= 0) { | 161 | if (remainingTime <= 0) { |
| @@ -148,7 +163,6 @@ export const useUserStore = defineStore('user', { | @@ -148,7 +163,6 @@ export const useUserStore = defineStore('user', { | ||
| 148 | this.refreshTokenApi(); | 163 | this.refreshTokenApi(); |
| 149 | return; | 164 | return; |
| 150 | } | 165 | } |
| 151 | - | ||
| 152 | // 设置定时器,到期后执行刷新 | 166 | // 设置定时器,到期后执行刷新 |
| 153 | refreshTokenTimer = setTimeout(async () => { | 167 | refreshTokenTimer = setTimeout(async () => { |
| 154 | const refreshSuccess = await this.refreshTokenApi(); | 168 | const refreshSuccess = await this.refreshTokenApi(); |
| @@ -157,9 +171,10 @@ export const useUserStore = defineStore('user', { | @@ -157,9 +171,10 @@ export const useUserStore = defineStore('user', { | ||
| 157 | this.startRefreshTokenTimer(); | 171 | this.startRefreshTokenTimer(); |
| 158 | } | 172 | } |
| 159 | }, remainingTime); | 173 | }, remainingTime); |
| 160 | - console.log(`Token 自动刷新定时器已启动,将在 ${Math.ceil(remainingTime / 1000)} 秒后执行刷新`); | 174 | + console.log( |
| 175 | + `Token 自动刷新定时器已启动,将在 ${Math.ceil(remainingTime / 1000)} 秒后执行刷新`, | ||
| 176 | + ); | ||
| 161 | }, | 177 | }, |
| 162 | - | ||
| 163 | // 新增:停止 Token 自动刷新定时器(避免内存泄漏) | 178 | // 新增:停止 Token 自动刷新定时器(避免内存泄漏) |
| 164 | stopRefreshTokenTimer() { | 179 | stopRefreshTokenTimer() { |
| 165 | if (refreshTokenTimer) { | 180 | if (refreshTokenTimer) { |
| @@ -167,171 +182,169 @@ export const useUserStore = defineStore('user', { | @@ -167,171 +182,169 @@ export const useUserStore = defineStore('user', { | ||
| 167 | refreshTokenTimer = null; | 182 | refreshTokenTimer = null; |
| 168 | } | 183 | } |
| 169 | }, | 184 | }, |
| 170 | - | ||
| 171 | // 修复3:重构字典获取方法(加 Token 校验 + 强制携带 Token + 宽松错误处理) | 185 | // 修复3:重构字典获取方法(加 Token 校验 + 强制携带 Token + 宽松错误处理) |
| 172 | async getAndSaveDictData() { | 186 | async getAndSaveDictData() { |
| 173 | // 前置校验:无登录态直接返回,不请求接口 | 187 | // 前置校验:无登录态直接返回,不请求接口 |
| 174 | if (!this.isLogin) { | 188 | if (!this.isLogin) { |
| 175 | - console.warn('未登录,跳过字典获取'); | ||
| 176 | - return { code: -1, msg: '未登录' }; | 189 | + console.warn("未登录,跳过字典获取"); |
| 190 | + return { code: -1, msg: "未登录" }; | ||
| 177 | } | 191 | } |
| 178 | - | ||
| 179 | try { | 192 | try { |
| 180 | // 强制携带 Token(和 getModuleList 保持一致,避免拦截器同步延迟) | 193 | // 强制携带 Token(和 getModuleList 保持一致,避免拦截器同步延迟) |
| 181 | const res = await getSimpleDictDataList( | 194 | const res = await getSimpleDictDataList( |
| 182 | {}, // 接口入参(按需传,比如 dictType: ['level']) | 195 | {}, // 接口入参(按需传,比如 dictType: ['level']) |
| 183 | - { header: { 'Authorization': `Bearer ${this.token}` } } | 196 | + { header: { Authorization: `Bearer ${this.token}` } }, |
| 184 | ); | 197 | ); |
| 185 | - | ||
| 186 | // 校验接口返回码(核心:避免非 0 码数据存入) | 198 | // 校验接口返回码(核心:避免非 0 码数据存入) |
| 187 | if (res.code !== 0) { | 199 | if (res.code !== 0) { |
| 188 | - console.warn('字典接口返回失败:', res.msg); | 200 | + console.warn("字典接口返回失败:", res.msg); |
| 189 | return res; // 返回错误信息,但不抛错 | 201 | return res; // 返回错误信息,但不抛错 |
| 190 | } | 202 | } |
| 191 | return res; | 203 | return res; |
| 192 | } catch (err) { | 204 | } catch (err) { |
| 193 | // 修复4:宽松错误处理,只打印日志,不抛错(避免阻断登录) | 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 | async getModuleList() { | 213 | async getModuleList() { |
| 200 | try { | 214 | try { |
| 201 | if (!this.token) { | 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 | return res; | 224 | return res; |
| 209 | } catch (err) { | 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 | } else { | 229 | } else { |
| 214 | - throw new Error('获取用户菜单失败,请重新登录'); | 230 | + throw new Error("获取用户菜单失败,请重新登录"); |
| 215 | } | 231 | } |
| 216 | } | 232 | } |
| 217 | }, | 233 | }, |
| 218 | - | ||
| 219 | async getUserInfo() { | 234 | async getUserInfo() { |
| 220 | try { | 235 | try { |
| 221 | const res = await getUserInfo(); | 236 | const res = await getUserInfo(); |
| 222 | return res; | 237 | return res; |
| 223 | } catch (err) { | 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 | logout() { | 243 | logout() { |
| 230 | // 新增:退出登录时,停止刷新定时器 | 244 | // 新增:退出登录时,停止刷新定时器 |
| 231 | this.stopRefreshTokenTimer(); | 245 | this.stopRefreshTokenTimer(); |
| 232 | - | ||
| 233 | const pages = getCurrentPages(); | 246 | const pages = getCurrentPages(); |
| 234 | if (pages.length === 0) return; | 247 | if (pages.length === 0) return; |
| 235 | - | ||
| 236 | const currentPageRoute = pages[pages.length - 1].route; | 248 | const currentPageRoute = pages[pages.length - 1].route; |
| 237 | const loginPath = globalConfig.router.loginPath | 249 | const loginPath = globalConfig.router.loginPath |
| 238 | - .replace(/^\//, '') | ||
| 239 | - .split('?')[0]; | ||
| 240 | - | 250 | + .replace(/^\//, "") |
| 251 | + .split("?")[0]; | ||
| 241 | if (currentPageRoute === loginPath) { | 252 | if (currentPageRoute === loginPath) { |
| 242 | - this.token = ''; | ||
| 243 | - this.refreshToken = ''; // 新增:清空Refresh Token | 253 | + this.token = ""; |
| 254 | + this.refreshToken = ""; // 新增:清空Refresh Token | ||
| 244 | this.userInfo = {}; | 255 | this.userInfo = {}; |
| 245 | - this.userId = ''; | ||
| 246 | - this.moduleListInfo = ''; | 256 | + this.userId = ""; |
| 257 | + this.moduleListInfo = ""; | ||
| 247 | this.expireTime = 0; | 258 | this.expireTime = 0; |
| 248 | this.dictData = {}; | 259 | this.dictData = {}; |
| 260 | + this.dictList = []; | ||
| 249 | return; | 261 | return; |
| 250 | } | 262 | } |
| 251 | - | ||
| 252 | const logoutWithLock = async () => { | 263 | const logoutWithLock = async () => { |
| 253 | if (this.logoutLoading) return; | 264 | if (this.logoutLoading) return; |
| 254 | this.logoutLoading = true; | 265 | this.logoutLoading = true; |
| 255 | - | ||
| 256 | try { | 266 | try { |
| 257 | await logout(); | 267 | await logout(); |
| 258 | } catch (err) { | 268 | } catch (err) { |
| 259 | - console.error('退出登录接口调用失败:', err); | 269 | + console.error("退出登录接口调用失败:", err); |
| 260 | } finally { | 270 | } finally { |
| 261 | - this.token = ''; | ||
| 262 | - this.refreshToken = ''; // 新增:清空Refresh Token | 271 | + this.token = ""; |
| 272 | + this.refreshToken = ""; // 新增:清空Refresh Token | ||
| 263 | this.userInfo = {}; | 273 | this.userInfo = {}; |
| 264 | - this.userId = ''; | ||
| 265 | - this.moduleListInfo = ''; | 274 | + this.userId = ""; |
| 275 | + this.moduleListInfo = ""; | ||
| 266 | this.dictData = {}; | 276 | this.dictData = {}; |
| 277 | + this.dictList = []; | ||
| 267 | this.expireTime = 0; | 278 | this.expireTime = 0; |
| 268 | this.logoutLoading = false; | 279 | this.logoutLoading = false; |
| 269 | - | ||
| 270 | uni.redirectTo({ | 280 | uni.redirectTo({ |
| 271 | - url: globalConfig.router.loginPath | 281 | + url: globalConfig.router.loginPath, |
| 272 | }); | 282 | }); |
| 273 | } | 283 | } |
| 274 | }; | 284 | }; |
| 275 | - | ||
| 276 | logoutWithLock(); | 285 | logoutWithLock(); |
| 277 | }, | 286 | }, |
| 278 | - | ||
| 279 | checkLogin() { | 287 | checkLogin() { |
| 280 | if (!this.isLogin) { | 288 | if (!this.isLogin) { |
| 281 | const pages = getCurrentPages(); | 289 | const pages = getCurrentPages(); |
| 282 | if (pages.length === 0) return false; | 290 | if (pages.length === 0) return false; |
| 283 | - | ||
| 284 | const currentPageRoute = pages[pages.length - 1].route; | 291 | const currentPageRoute = pages[pages.length - 1].route; |
| 285 | const loginPath = globalConfig.router.loginPath | 292 | const loginPath = globalConfig.router.loginPath |
| 286 | - .replace(/^\//, '') | ||
| 287 | - .split('?')[0]; | ||
| 288 | - | 293 | + .replace(/^\//, "") |
| 294 | + .split("?")[0]; | ||
| 289 | if (currentPageRoute !== loginPath) { | 295 | if (currentPageRoute !== loginPath) { |
| 290 | uni.redirectTo({ | 296 | uni.redirectTo({ |
| 291 | - url: globalConfig.router.loginPath | 297 | + url: globalConfig.router.loginPath, |
| 292 | }); | 298 | }); |
| 293 | } | 299 | } |
| 294 | return false; | 300 | return false; |
| 295 | } | 301 | } |
| 296 | - | ||
| 297 | // 新增:已登录时,启动刷新定时器(防止页面刷新后定时器丢失) | 302 | // 新增:已登录时,启动刷新定时器(防止页面刷新后定时器丢失) |
| 298 | this.startRefreshTokenTimer(); | 303 | this.startRefreshTokenTimer(); |
| 299 | return true; | 304 | return true; |
| 300 | - } | 305 | + }, |
| 301 | }, | 306 | }, |
| 302 | - | ||
| 303 | persist: { | 307 | persist: { |
| 304 | enabled: true, | 308 | enabled: true, |
| 305 | - key: 'user_store', | 309 | + key: "user_store", |
| 306 | storage: { | 310 | storage: { |
| 307 | getItem: (key) => uni.getStorageSync(key), | 311 | getItem: (key) => uni.getStorageSync(key), |
| 308 | setItem: (key, value) => uni.setStorageSync(key, value), | 312 | setItem: (key, value) => uni.setStorageSync(key, value), |
| 309 | - removeItem: (key) => uni.removeStorageSync(key) | 313 | + removeItem: (key) => uni.removeStorageSync(key), |
| 310 | }, | 314 | }, |
| 311 | serializer: { | 315 | serializer: { |
| 312 | serialize: (state) => { | 316 | serialize: (state) => { |
| 313 | uni.setStorageSync(globalConfig.cache.tokenKey, state.token); | 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 | uni.setStorageSync(globalConfig.cache.userIdKey, state.userId); | 322 | uni.setStorageSync(globalConfig.cache.userIdKey, state.userId); |
| 316 | uni.setStorageSync(globalConfig.cache.expireTimeKey, state.expireTime); | 323 | uni.setStorageSync(globalConfig.cache.expireTimeKey, state.expireTime); |
| 317 | uni.setStorageSync(globalConfig.cache.userInfoKey, state.userInfo); | 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 | uni.setStorageSync(globalConfig.cache.dictDataKey, state.dictData); | 329 | uni.setStorageSync(globalConfig.cache.dictDataKey, state.dictData); |
| 330 | + uni.setStorageSync(globalConfig.cache.dictListKey, state.dictList); | ||
| 320 | return state; | 331 | return state; |
| 321 | }, | 332 | }, |
| 322 | deserialize: (value) => { | 333 | deserialize: (value) => { |
| 323 | return { | 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 | expireTime: uni.getStorageSync(globalConfig.cache.expireTimeKey) || 0, | 339 | expireTime: uni.getStorageSync(globalConfig.cache.expireTimeKey) || 0, |
| 328 | userInfo: uni.getStorageSync(globalConfig.cache.userInfoKey) || {}, | 340 | userInfo: uni.getStorageSync(globalConfig.cache.userInfoKey) || {}, |
| 329 | - moduleListInfo: uni.getStorageSync(globalConfig.cache.moduleListKey) || '', | 341 | + moduleListInfo: |
| 342 | + uni.getStorageSync(globalConfig.cache.moduleListKey) || "", | ||
| 330 | dictData: uni.getStorageSync(globalConfig.cache.dictDataKey) || {}, | 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 | \ No newline at end of file | 349 | \ No newline at end of file |
| 350 | + }, | ||
| 351 | +}); |