Commit 8ddc6f6e1f715ff8aac9b30b12afdb28305fa35e
1 parent
6e9f0396
登录 修改样式
Showing
17 changed files
with
1802 additions
and
1450 deletions
api/common.js
| ... | ... | @@ -11,6 +11,16 @@ export const getRoadListByLatLng = (params) => { |
| 11 | 11 | return get('/app-api/bpm/garden/workorder/listRoadInfo', params); |
| 12 | 12 | }; |
| 13 | 13 | |
| 14 | + | |
| 15 | +/** | |
| 16 | + * 根据公司 | |
| 17 | + * @param {Object} params {mobile, password, code} | |
| 18 | + * @returns {Promise} | |
| 19 | + */ | |
| 20 | +export const getCompanyList = (params) => { | |
| 21 | + return get('/admin-api/system/dept/sub-list', params); | |
| 22 | +}; | |
| 23 | + | |
| 14 | 24 | // export const fileUpload = (params) => { |
| 15 | 25 | // return post('/app-api/infra/file/upload', params); |
| 16 | 26 | // }; | ... | ... |
api/tree-archive/tree-archive.js
0 → 100644
| 1 | + | |
| 2 | +import { post, get } from '@/common/utils/request'; | |
| 3 | + | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * 班组集合 | |
| 7 | + * @param {Object} params | |
| 8 | + * @returns {Promise} | |
| 9 | + */ | |
| 10 | +export const deptListReq = (params) => { | |
| 11 | + return get('/app-api/garden/tree/dept/list', params); | |
| 12 | +}; | |
| 13 | + | |
| 14 | +/** | |
| 15 | + * 道路树木列表 | |
| 16 | + * @param {Object} params | |
| 17 | + * @returns {Promise} | |
| 18 | + */ | |
| 19 | +export const treeRoadReq = (params) => { | |
| 20 | + return get('/app-api/garden/tree/road/list', params); | |
| 21 | +}; | |
| 22 | + | |
| 23 | + | |
| 24 | +/** | |
| 25 | + * 新增树 | |
| 26 | + * @param {Object} params | |
| 27 | + * @returns {Promise} | |
| 28 | + */ | |
| 29 | +export const addTree = (params) => { | |
| 30 | + return get('/app-api/garden/tree/create', params); | |
| 31 | +}; | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | +// // 树基本详情 | |
| 36 | +// export const treeDetailReq = (params) => request.get('/business/tree/'+params ) | |
| 37 | +// ==> | |
| 38 | +// /app-api/garden/tree/get | |
| 39 | +// | |
| 40 | +// // 修改树 | |
| 41 | +// export const updateTree = (params) => request.put('/business/tree/',params ) | |
| 42 | +// ==> | |
| 43 | +// /app-api/garden/tree/update | |
| 44 | +// | |
| 45 | +// // 树更变记录 | |
| 46 | +// export const treeLogReq = (params) => request.get('/gardentree/logs/list/',params ) | |
| 47 | +// ==> | |
| 48 | +// /app-api/garden/tree-change-log/page | ... | ... |
pages-sub/data/tree-archive/addTree.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="container"> | |
| 3 | + <up-form :model="formData" ref="formRef" label-width="140rpx" border-bottom> | |
| 4 | + <up-form-item label="名称" prop="treetype" required> | |
| 5 | + <up-input v-model="formData.treetype" placeholder="请输入名称" maxlength="30" border="none" /> | |
| 6 | + </up-form-item> | |
| 7 | + | |
| 8 | + <up-row gutter="10"> | |
| 9 | + <up-col span="6"> | |
| 10 | + <up-form-item label="胸径" prop="dbh" required> | |
| 11 | + <up-input v-model="formData.dbh" placeholder="请输入" maxlength="10" border="none" input-align="left" /> | |
| 12 | + <template #right> | |
| 13 | + <text style="padding-left: 12rpx;color:#ccc;font-size:12px">厘米</text> | |
| 14 | + </template> | |
| 15 | + </up-form-item> | |
| 16 | + </up-col> | |
| 17 | + <up-col span="6"> | |
| 18 | + <up-form-item label="高度" prop="treeheight"> | |
| 19 | + <up-input v-model="formData.treeheight" placeholder="请输入" maxlength="10" border="none" input-align="left" /> | |
| 20 | + <template #right> | |
| 21 | + <text style="padding-left: 12rpx;color:#ccc;font-size:14px;">米</text> | |
| 22 | + </template> | |
| 23 | + </up-form-item> | |
| 24 | + </up-col> | |
| 25 | + </up-row> | |
| 26 | + | |
| 27 | + <up-form-item label="位置" prop="growlocation" required class="location-form-item" @click="openMap"> | |
| 28 | + <up-input v-model="formData.growlocation" placeholder="请选择" disabled border="none" /> | |
| 29 | + <template #right> | |
| 30 | + <up-icon name="map" size="22"></up-icon> | |
| 31 | + </template> | |
| 32 | + </up-form-item> | |
| 33 | + | |
| 34 | + <up-row gutter="10"> | |
| 35 | + <up-col span="6"> | |
| 36 | + <up-form-item label="经度" label-color="#ccc" prop="longitude" border-bottom="none"> | |
| 37 | + <up-input v-model="formData.longitude" placeholder="" disabled border="none" /> | |
| 38 | + </up-form-item> | |
| 39 | + </up-col> | |
| 40 | + <up-col span="6"> | |
| 41 | + <up-form-item label="纬度" label-color="#ccc" prop="latitude" border-bottom="none"> | |
| 42 | + <up-input v-model="formData.latitude" placeholder="" disabled border="none" /> | |
| 43 | + </up-form-item> | |
| 44 | + </up-col> | |
| 45 | + </up-row> | |
| 46 | + | |
| 47 | + <up-form-item label="管护单位" prop="managedutyunit" required> | |
| 48 | + <up-input v-model="formData.managedutyunit" placeholder="请输入" maxlength="30" border="none" /> | |
| 49 | + </up-form-item> | |
| 50 | + | |
| 51 | + <up-form-item label="权属分类" prop="oldtreeownershipText" required arrow @click="pickerShow = true"> | |
| 52 | + <up-input v-model="formData.oldtreeownershipText" placeholder="请选择" disabled border="none" bg-color="transparent" /> | |
| 53 | + </up-form-item> | |
| 54 | + | |
| 55 | + <!-- ✅✅✅ 核心修改:图片上传 完全对标参考代码写法 start --> | |
| 56 | + <up-form-item label="图片信息" prop="treeImgList" required> | |
| 57 | + <up-upload | |
| 58 | + :file-list="treeImgs.imgList.value || []" | |
| 59 | + @after-read="treeImgs.uploadImgs" | |
| 60 | + @delete="treeImgs.deleteImg" | |
| 61 | + multiple | |
| 62 | + :width="70" | |
| 63 | + :height="70" | |
| 64 | + :max-count="treeImgs.uploadConfig.maxCount" | |
| 65 | + :upload-text="treeImgs.uploadConfig.uploadText" | |
| 66 | + :size-type="treeImgs.uploadConfig.sizeType" | |
| 67 | + ></up-upload> | |
| 68 | + </up-form-item> | |
| 69 | + <!-- ✅✅✅ 图片上传 修改结束 --> | |
| 70 | + | |
| 71 | + <!-- 动画区域(初始隐藏)保留原逻辑 --> | |
| 72 | + <view | |
| 73 | + class="animated-area" | |
| 74 | + :style="{ | |
| 75 | + height: isShow ? contentHeight + 'px' : '0', | |
| 76 | + opacity: isShow ? 1 : 0, | |
| 77 | + overflow: 'hidden' | |
| 78 | + }" | |
| 79 | + > | |
| 80 | + <up-row gutter="10"> | |
| 81 | + <up-col span="6"> | |
| 82 | + <up-form-item label="拉丁文" prop="latinname"> | |
| 83 | + <up-input v-model="formData.latinname" placeholder="请输入" maxlength="30" border="none" /> | |
| 84 | + </up-form-item> | |
| 85 | + </up-col> | |
| 86 | + <up-col span="6"> | |
| 87 | + <up-form-item label="级别" arrow @click="levelshow = true"> | |
| 88 | + <up-input v-model="formData.treeleveltext" placeholder="请选择" disabled border="none" bg-color="transparent" /> | |
| 89 | + </up-form-item> | |
| 90 | + </up-col> | |
| 91 | + </up-row> | |
| 92 | + | |
| 93 | + <up-form-item label="生长环境" prop="growthenvironment"> | |
| 94 | + <up-input v-model="formData.growthenvironment" placeholder="请输入" maxlength="50" border="none" /> | |
| 95 | + </up-form-item> | |
| 96 | + | |
| 97 | + <up-row gutter="10"> | |
| 98 | + <up-col span="6"> | |
| 99 | + <up-form-item label="预估树龄" prop="estimationtreeage"> | |
| 100 | + <up-input v-model="formData.estimationtreeage" placeholder="请输入" maxlength="10" border="none" input-align="left" /> | |
| 101 | + <template #right> | |
| 102 | + <text style="padding-left:12rpx;color:#ccc;font-size:14px">年</text> | |
| 103 | + </template> | |
| 104 | + </up-form-item> | |
| 105 | + </up-col> | |
| 106 | + <up-col span="6"> | |
| 107 | + <up-form-item label="干周" prop="weekday"> | |
| 108 | + <up-input v-model="formData.weekday" placeholder="请输入" maxlength="10" border="none" input-align="left" /> | |
| 109 | + <template #right> | |
| 110 | + <text style="padding-left:12rpx;color:#ccc;font-size:14px">厘米</text> | |
| 111 | + </template> | |
| 112 | + </up-form-item> | |
| 113 | + </up-col> | |
| 114 | + </up-row> | |
| 115 | + | |
| 116 | + <up-row gutter="10"> | |
| 117 | + <up-col span="6"> | |
| 118 | + <up-form-item label="东西冠幅" prop="canopyeastwest"> | |
| 119 | + <up-input v-model="formData.canopyeastwest" placeholder="请输入" maxlength="10" border="none" input-align="left" /> | |
| 120 | + <template #right> | |
| 121 | + <text style="padding-left:12rpx;color:#ccc;font-size:14px">米</text> | |
| 122 | + </template> | |
| 123 | + </up-form-item> | |
| 124 | + </up-col> | |
| 125 | + <up-col span="6"> | |
| 126 | + <up-form-item label="南北冠幅" prop="canopysouthnorth"> | |
| 127 | + <up-input v-model="formData.canopysouthnorth" placeholder="请输入" maxlength="10" border="none" input-align="left" /> | |
| 128 | + <template #right> | |
| 129 | + <text style="padding-left:12rpx;color:#ccc;font-size:14px">米</text> | |
| 130 | + </template> | |
| 131 | + </up-form-item> | |
| 132 | + </up-col> | |
| 133 | + </up-row> | |
| 134 | + </view> | |
| 135 | + | |
| 136 | + <!-- 触发按钮 --> | |
| 137 | + <up-button | |
| 138 | + @click="toggleArea" | |
| 139 | + type="primary" | |
| 140 | + plain | |
| 141 | + size="large" | |
| 142 | + style="margin-top:20rpx" | |
| 143 | + > | |
| 144 | + {{ isShow ? '- 隐藏区域' : '+ 显示区域' }} | |
| 145 | + </up-button> | |
| 146 | + </up-form> | |
| 147 | + | |
| 148 | + <view style="height: 60px;width: 100%"></view> | |
| 149 | + | |
| 150 | + <!-- 底部提交按钮 --> | |
| 151 | + <view class="fixed-bottom-btn-wrap"> | |
| 152 | + <up-button type="primary" @click="submit" :loading="loadingFlag" size="large" bold>提交</up-button> | |
| 153 | + </view> | |
| 154 | + | |
| 155 | + <up-picker | |
| 156 | + v-model="pickerShow" | |
| 157 | + :list="treeOwnershipData" | |
| 158 | + label-key="dictLabel" | |
| 159 | + value-key="dictValue" | |
| 160 | + @confirm="pickerChange" | |
| 161 | + ></up-picker> | |
| 162 | + | |
| 163 | + <up-picker | |
| 164 | + v-model="levelshow" | |
| 165 | + :list="treeLevelData" | |
| 166 | + label-key="dictLabel" | |
| 167 | + value-key="dictValue" | |
| 168 | + @confirm="pickerLevelChange" | |
| 169 | + ></up-picker> | |
| 170 | + </view> | |
| 171 | +</template> | |
| 172 | + | |
| 173 | +<script setup> | |
| 174 | +import { ref, reactive, nextTick } from 'vue' | |
| 175 | +import { onReady, onLoad, onShow } from '@dcloudio/uni-app'; | |
| 176 | +import { addTree } from "@/api/tree-archive/tree-archive.js"; | |
| 177 | + | |
| 178 | +import { useUploadImgs } from '@/common/utils/useUploadImgs' | |
| 179 | +import { useUserStore } from '@/pinia/user'; | |
| 180 | + | |
| 181 | +// ========== 状态管理 ========== | |
| 182 | +const userStore = useUserStore(); | |
| 183 | + | |
| 184 | +// ✅ 全局响应式数据 | |
| 185 | +const formRef = ref(null) | |
| 186 | +const isShow = ref(false) | |
| 187 | +const contentHeight = ref(700) | |
| 188 | +const treeOwnershipData = ref([]) | |
| 189 | +const treeLevelData = ref([]) | |
| 190 | +const pickerShow = ref(false) | |
| 191 | +const levelshow = ref(false) | |
| 192 | +const loadingFlag = ref(false) | |
| 193 | + | |
| 194 | +// ✅✅✅ 核心修改:图片上传配置 完全对标参考代码写法 | |
| 195 | +const treeImgs = useUploadImgs({ | |
| 196 | + maxCount: 3, | |
| 197 | + uploadText: '选择图片', | |
| 198 | + sizeType: ['compressed'], | |
| 199 | + formRef: formRef, | |
| 200 | + fieldName: 'treeImgList' | |
| 201 | +}) | |
| 202 | +// 图片数组兜底,防止非数组报错 | |
| 203 | +if (!Array.isArray(treeImgs.rawImgList.value)) treeImgs.rawImgList.value = []; | |
| 204 | + | |
| 205 | +// ✅ 表单数据 - 替代vue2的data | |
| 206 | +const formData = reactive({ | |
| 207 | + treetype: '', | |
| 208 | + treeheight: '', | |
| 209 | + dbh: '', | |
| 210 | + treelevel: '', | |
| 211 | + treeleveltext: '', | |
| 212 | + managedutyunit: '', | |
| 213 | + oldtreeownership: '', | |
| 214 | + oldtreeownershipText: '', | |
| 215 | + latinname: '', | |
| 216 | + estimationtreeage: '', | |
| 217 | + canopysouthnorth: '', | |
| 218 | + canopyeastwest: '', | |
| 219 | + weekday: '', | |
| 220 | + growlocation: '', | |
| 221 | + growthenvironment: '', | |
| 222 | + treeImgList: [], | |
| 223 | + address: '', | |
| 224 | + latitude: '', | |
| 225 | + longitude: '', | |
| 226 | + road: '', | |
| 227 | + maintainunit: '' | |
| 228 | +}) | |
| 229 | + | |
| 230 | +// ✅✅✅ 核心修改:表单校验规则 - 图片校验改为参考代码同款 | |
| 231 | +const rules = reactive({ | |
| 232 | + treetype: [{ required: true, message: '请输入名称', trigger: 'blur' }], | |
| 233 | + treeheight: [{ max: 10, message: '树高不能超过10个字符', trigger: 'blur' }, { pattern: /^\d+(\.\d{1,2})?$/, message: '请输入正确的数字格式', trigger: 'blur' }], | |
| 234 | + dbh: [{ required: true, message: '请输入胸径', trigger: 'blur' }, { max: 10, message: '胸径不能超过10个字符', trigger: 'blur' }, { pattern: /^\d+(\.\d{1,2})?$/, message: '请输入正确的数字格式', trigger: 'blur' }], | |
| 235 | + estimationtreeage: [{ max: 10, message: '预估树龄不能超过10个字符', trigger: 'blur' }, { pattern: /^\d+(\.\d{1,2})?$/, message: '请输入正确的数字格式', trigger: 'blur' }], | |
| 236 | + weekday: [{ max: 10, message: '干周不能超过10个字符', trigger: 'blur' }, { pattern: /^\d+(\.\d{1,2})?$/, message: '请输入正确的数字格式', trigger: 'blur' }], | |
| 237 | + canopyeastwest: [{ max: 10, message: '东西冠幅不能超过10个字符', trigger: 'blur' }, { pattern: /^\d+(\.\d{1,2})?$/, message: '请输入正确的数字格式', trigger: 'blur' }], | |
| 238 | + canopysouthnorth: [{ max: 10, message: '南北冠幅不能超过10个字符', trigger: 'blur' }, { pattern: /^\d+(\.\d{1,2})?$/, message: '请输入正确的数字格式', trigger: 'blur' }], | |
| 239 | + growlocation: [{ required: true, message: '请地图选择位置', trigger: 'change' }], | |
| 240 | + managedutyunit: [{ required: true, message: '请输入管护单位', trigger: 'blur' }], | |
| 241 | + oldtreeownershipText: [{ required: true, message: '请选择权属分类', trigger: 'change' }], | |
| 242 | + treeImgList: [treeImgs.imgValidateRule] // ✅ 图片校验规则和参考代码一致 | |
| 243 | +}) | |
| 244 | + | |
| 245 | +// ✅ 生命周期 | |
| 246 | +onLoad((options) => { | |
| 247 | + formData.road = options.roadId | |
| 248 | +}) | |
| 249 | + | |
| 250 | +onShow(async () => { | |
| 251 | + | |
| 252 | + treeLevelData.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('tree_level')); | |
| 253 | + treeOwnershipData.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('tree_ownership')); | |
| 254 | +}) | |
| 255 | + | |
| 256 | +// 修复表单ref挂载问题,和参考代码一致 | |
| 257 | +onReady(() => { | |
| 258 | + nextTick(() => { | |
| 259 | + formRef.value?.setRules(rules); | |
| 260 | + }); | |
| 261 | +}); | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | +const toggleArea = () => { | |
| 266 | + isShow.value = !isShow.value | |
| 267 | +} | |
| 268 | + | |
| 269 | + | |
| 270 | +// 权属分类选择回调 | |
| 271 | +const pickerChange = (val) => { | |
| 272 | + formData.oldtreeownership = val.value | |
| 273 | + formData.oldtreeownershipText = val.label | |
| 274 | + pickerShow.value = false | |
| 275 | +} | |
| 276 | + | |
| 277 | +// 树木级别选择回调 | |
| 278 | +const pickerLevelChange = (val) => { | |
| 279 | + formData.treelevel = val.value | |
| 280 | + formData.treeleveltext = val.label | |
| 281 | + levelshow.value = false | |
| 282 | +} | |
| 283 | + | |
| 284 | +// 打开地图选址 | |
| 285 | +const openMap = () => { | |
| 286 | + uni.chooseLocation({ | |
| 287 | + success: (res) => { | |
| 288 | + formData.growlocation = res.address | |
| 289 | + formData.latitude = res.latitude | |
| 290 | + formData.longitude = res.longitude | |
| 291 | + }, | |
| 292 | + fail: (err) => { | |
| 293 | + console.error('地图选择失败', err); | |
| 294 | + if (err.errMsg.includes('auth deny')) { | |
| 295 | + uni.showToast({ title: '请授权位置权限', icon: 'none' }); | |
| 296 | + } | |
| 297 | + } | |
| 298 | + }); | |
| 299 | +} | |
| 300 | + | |
| 301 | +// 表单提交核心方法 | |
| 302 | +const submit = async () => { | |
| 303 | + if (loadingFlag.value) return | |
| 304 | + // 表单校验 | |
| 305 | + const valid = await formRef.value.validate() | |
| 306 | + if (!valid) return | |
| 307 | + | |
| 308 | + // ✅✅✅ 核心修改:获取上传成功的图片地址,和参考代码一致 | |
| 309 | + const uploadImgUrls = treeImgs.getSuccessImgUrls() | |
| 310 | + | |
| 311 | + // 组装提交参数 | |
| 312 | + formData.maintainunit = uni.getStorageSync('userInfo')?.belongCompanyId || '' | |
| 313 | + formData.treeImgList = uploadImgUrls // 赋值图片数组 | |
| 314 | + loadingFlag.value = true | |
| 315 | + | |
| 316 | + try { | |
| 317 | + const res = await addTree({ data: { ...formData } }) | |
| 318 | + if (res.code == '200') { | |
| 319 | + uni.showToast({ title: "新增成功", icon: "success" }); | |
| 320 | + uni.redirectTo({ url: '/subPackages/treePage/treeFiles' }); | |
| 321 | + } | |
| 322 | + } catch (err) { | |
| 323 | + uni.showToast({ title: "新增失败,请重试", icon: "none" }); | |
| 324 | + console.error(err) | |
| 325 | + } finally { | |
| 326 | + loadingFlag.value = false | |
| 327 | + } | |
| 328 | +} | |
| 329 | +</script> | |
| 330 | + | |
| 331 | +<style scoped lang="scss"> | |
| 332 | +.container { | |
| 333 | + padding: 25rpx; | |
| 334 | + box-sizing: border-box; | |
| 335 | + background: #fff; | |
| 336 | +} | |
| 337 | + | |
| 338 | +.animated-area { | |
| 339 | + transition: all 0.3s ease-out; | |
| 340 | + margin-top: 10rpx; | |
| 341 | +} | |
| 342 | +.location-form-item { | |
| 343 | + padding-right: 10rpx; | |
| 344 | +} | |
| 345 | +</style> | |
| 0 | 346 | \ No newline at end of file | ... | ... |
pages-sub/data/tree-archive/editTree.vue
0 → 100644
pages-sub/data/tree-archive/index.vue
| 1 | -<script setup lang="ts"> | |
| 1 | +<template> | |
| 2 | + <view class="container"> | |
| 3 | + <!-- 归属单位选择 --> | |
| 4 | + <up-form :model="formData" ref="formRef" label-width="140rpx" border-bottom style="padding: 0 15px"> | |
| 5 | + <up-form-item | |
| 6 | + label="归属单位" | |
| 7 | + prop="companyId" | |
| 8 | + @click="showActionSheet = true" | |
| 9 | + class="form-item-custom" | |
| 10 | + > | |
| 11 | + <up-input | |
| 12 | + v-model="formData.companyId" | |
| 13 | + placeholder="请选择归属单位" | |
| 14 | + disabled | |
| 15 | + disabled-color="#ffffff" | |
| 16 | + border="none" | |
| 17 | + bg-color="transparent" | |
| 18 | + /> | |
| 19 | + <template #right> | |
| 20 | + <up-icon name="arrow-right" size="16"></up-icon> | |
| 21 | + </template> | |
| 22 | + </up-form-item> | |
| 23 | + </up-form> | |
| 2 | 24 | |
| 3 | -</script> | |
| 25 | + <!-- 弹窗式 Search 搜索组件 --> | |
| 26 | + <view class="search-wrap"> | |
| 27 | + <up-search | |
| 28 | + v-model="searchValue" | |
| 29 | + placeholder="请输入道路名称搜索" | |
| 30 | + @search="handleSearch" | |
| 31 | + bg-color="#f5f5f5" | |
| 32 | + :clearabled="false" | |
| 33 | + :show-action="true" | |
| 34 | + actionText="搜索" | |
| 35 | + :animation="true" | |
| 36 | + @custom="handleSearch" | |
| 37 | + /> | |
| 38 | + </view> | |
| 4 | 39 | |
| 5 | -<template> | |
| 40 | + <!-- ✅ 核心:uview-plus up-row+up-col 实现左右布局【无u-scroll-list,无任何多余组件】 --> | |
| 41 | + <up-row type="flex" class="cate-layout" align="flex-start"> | |
| 42 | + <!-- 左侧:班组列表 (uview栅格列 + 原生滚动) --> | |
| 43 | + <up-col span="3" class="cate-left"> | |
| 44 | + <view | |
| 45 | + class="left-item" | |
| 46 | + :class="{ active: currentIndex === index }" | |
| 47 | + v-for="(item, index) in cateTabList" | |
| 48 | + :key="item.deptId" | |
| 49 | + @click="cateTabChange(index)" | |
| 50 | + > | |
| 51 | + {{ item.deptName }} | |
| 52 | + </view> | |
| 53 | + | |
| 54 | + </up-col> | |
| 6 | 55 | |
| 56 | + <!-- 右侧:道路列表 (uview栅格列 + 原生滚动) --> | |
| 57 | + <up-col span="9" class="cate-right"> | |
| 58 | + <scroll-view scroll-y class="right-scroll"> | |
| 59 | + <!-- 暂无数据 --> | |
| 60 | + <empty-view v-if="!hasValidRoadId"></empty-view> | |
| 61 | + <!-- 道路数据列表 --> | |
| 62 | + <view v-else class="road-list-wrap"> | |
| 63 | + <view | |
| 64 | + v-for="item in roads" | |
| 65 | + class="card-wrap cad-box-shadow" | |
| 66 | + :key="item.roadId" | |
| 67 | + @click="toNewPage(item.roadId, item.treeCount)" | |
| 68 | + > | |
| 69 | + <p class="fs-flex__between"> | |
| 70 | + <span class="up-line-1">{{ item.roadName }}</span> | |
| 71 | + <span class="treeCount">{{ item.treeCount }}棵</span> | |
| 72 | + </p> | |
| 73 | + <p class="up-line-1 fs-my8">已录入行道树:{{ item.recordedCount }}棵</p> | |
| 74 | + <p class="up-line-1 fs-my8" style="color: #999;font-size: 12px">起点:{{ item.startRemark }}</p> | |
| 75 | + <p class="up-line-1" style="color: #999;font-size: 12px">终点:{{ item.endRemark }}</p> | |
| 76 | + </view> | |
| 77 | + </view> | |
| 78 | + </scroll-view> | |
| 79 | + </up-col> | |
| 80 | + </up-row> | |
| 81 | + | |
| 82 | + <!-- ActionSheet 底部弹出选择菜单 --> | |
| 83 | + <up-action-sheet | |
| 84 | + :show="showActionSheet" | |
| 85 | + :actions="belongCompanyData" | |
| 86 | + title="请选择归属单位" | |
| 87 | + @select="actionSheetSelect" | |
| 88 | + @close="showActionSheet = false" | |
| 89 | + ></up-action-sheet> | |
| 90 | + | |
| 91 | + </view> | |
| 7 | 92 | </template> |
| 8 | 93 | |
| 9 | -<style scoped> | |
| 94 | +<script setup> | |
| 95 | +import { ref, reactive, watch } from 'vue' | |
| 96 | +import { onShow } from '@dcloudio/uni-app'; | |
| 97 | +import { getCompanyList } from "@/api/common"; | |
| 98 | +import { deptListReq } from '@/api/tree-archive/tree-archive' | |
| 99 | + | |
| 100 | + | |
| 101 | +const formRef = ref(null) | |
| 102 | +const showActionSheet = ref(false) | |
| 103 | +const currentIndex = ref(0) | |
| 104 | +const hasValidRoadId = ref(false) | |
| 105 | +const searchValue = ref('') // 搜索关键词 独立绑定 | |
| 106 | + | |
| 107 | +// 表单数据 | |
| 108 | +const formData = reactive({ | |
| 109 | + companyId: '', | |
| 110 | +}) | |
| 111 | + | |
| 112 | +// 接口数据变量 | |
| 113 | +const companyId = ref('') | |
| 114 | +const belongCompanyData = ref([]) // 归属单位下拉数据 | |
| 115 | +const depts = ref([]) // 归属班组列表 | |
| 116 | +const roads = ref([]) // 当前选中班组的道路列表 | |
| 117 | +const cateTabList = ref([]) // 左侧tab数据源 | |
| 118 | + | |
| 119 | +// 生命周期 | |
| 120 | +onShow( async() => { | |
| 121 | + const res = await getCompanyList() | |
| 122 | + belongCompanyData.value = res | |
| 123 | + if (belongCompanyData.value.length > 0) { | |
| 124 | + formData.companyId = belongCompanyData.value[0].name | |
| 125 | + companyId.value = belongCompanyData.value[0].id.toString() | |
| 126 | + deptListQuery() | |
| 127 | + } | |
| 128 | +}) | |
| 129 | + | |
| 130 | +// 监听 CateTab 选中下标 数据联动 | |
| 131 | +watch(currentIndex, () => { | |
| 132 | + if(depts.value.length > 0) { | |
| 133 | + roads.value = depts.value[currentIndex.value].roads | |
| 134 | + checkRoadDataValid() | |
| 135 | + } | |
| 136 | +}) | |
| 137 | + | |
| 138 | +// 搜索事件 - 点击搜索按钮/回车触发 | |
| 139 | +const handleSearch = () => { | |
| 140 | + deptListQuery() | |
| 141 | +} | |
| 142 | + | |
| 143 | +// 跳转道路档案记录页 | |
| 144 | +const toNewPage = (roadId, treeCount) => { | |
| 145 | + uni.navigateTo({ | |
| 146 | + url: `/pages-sub/data/tree-archive/treeRecord?roadId=${roadId}&count=${treeCount}` | |
| 147 | + }) | |
| 148 | +} | |
| 149 | + | |
| 150 | +// 查询班组+道路数据 & 格式化CateTab的数据源 | |
| 151 | +const deptListQuery = async () => { | |
| 152 | + const params = { | |
| 153 | + companyId: companyId.value, | |
| 154 | + roadName: searchValue.value | |
| 155 | + } | |
| 156 | + const res = await deptListReq(params) | |
| 157 | + if (res.length ==0) { | |
| 158 | + depts.value = [] | |
| 159 | + roads.value = [] | |
| 160 | + cateTabList.value = [] | |
| 161 | + hasValidRoadId.value = false | |
| 162 | + } else { | |
| 163 | + depts.value = res[0].depts | |
| 164 | + cateTabList.value = depts.value | |
| 165 | + roads.value = depts.value[0].roads | |
| 166 | + checkRoadDataValid() | |
| 167 | + } | |
| 168 | +} | |
| 169 | + | |
| 170 | +// 左侧班组切换事件 | |
| 171 | +const cateTabChange = (index) => { | |
| 172 | + currentIndex.value = index | |
| 173 | + roads.value = depts.value[index].roads | |
| 174 | + checkRoadDataValid() | |
| 175 | +} | |
| 176 | + | |
| 177 | +// ActionSheet 选择归属单位的确认事件 | |
| 178 | +const actionSheetSelect = (val) => { | |
| 179 | + formData.companyId = val.name | |
| 180 | + companyId.value = val.id.toString() | |
| 181 | + showActionSheet.value = false // 关闭弹窗 | |
| 182 | + deptListQuery() // 重新查询数据 | |
| 183 | +} | |
| 184 | + | |
| 185 | +// 校验是否有有效道路数据 | |
| 186 | +const checkRoadDataValid = () => { | |
| 187 | + hasValidRoadId.value = roads.value && roads.value.length > 0 && roads.value.some(item => { | |
| 188 | + return item.roadId !== null && item.roadId !== undefined && item.roadId | |
| 189 | + }) | |
| 190 | +} | |
| 191 | +</script> | |
| 192 | + | |
| 193 | +<style scoped lang="scss"> | |
| 194 | +// 页面容器 满高布局 | |
| 195 | +.container { | |
| 196 | + height: 100%; | |
| 197 | + display: flex; | |
| 198 | + flex-direction: column; | |
| 199 | + background: #fff; | |
| 200 | + box-sizing: border-box; | |
| 201 | +} | |
| 202 | + | |
| 203 | +// 搜索框外层容器 间距不变 | |
| 204 | +.search-wrap { | |
| 205 | + padding: 10rpx 15rpx; | |
| 206 | +} | |
| 207 | + | |
| 208 | +.cate-layout { | |
| 209 | + flex: 1; | |
| 210 | + width: 100%; | |
| 211 | + height: calc(100vh - 44px); | |
| 212 | + | |
| 213 | +} | |
| 214 | + | |
| 215 | + | |
| 216 | +.cate-left { | |
| 217 | + height: 100%; | |
| 218 | + padding: 0; | |
| 219 | + background: #f8f8f8; | |
| 220 | + box-sizing: border-box; | |
| 221 | + .left-scroll { | |
| 222 | + width: 100%; | |
| 223 | + height: 100%; | |
| 224 | + } | |
| 225 | + // 左侧列表项 | |
| 226 | + .left-item { | |
| 227 | + padding: 24rpx 12rpx; | |
| 228 | + font-size: 28rpx; | |
| 229 | + | |
| 230 | + white-space: nowrap; | |
| 231 | + overflow: hidden; | |
| 232 | + text-overflow: ellipsis; | |
| 233 | + box-sizing: border-box; | |
| 234 | + } | |
| 235 | + // 选中项高亮 主题色和你一致 #5f5fe2 | |
| 236 | + .left-item.active { | |
| 237 | + background: #ffffff; | |
| 238 | + color: #5f5fe2; | |
| 239 | + font-weight: 500; | |
| 240 | + border-left: 4rpx solid #5f5fe2; | |
| 241 | + } | |
| 242 | +} | |
| 243 | + | |
| 244 | +.cate-right { | |
| 245 | + height: 100%; | |
| 246 | + padding: 0 10rpx; | |
| 247 | + box-sizing: border-box; | |
| 248 | + background: #ffffff; | |
| 249 | + .right-scroll { | |
| 250 | + width: 100%; | |
| 251 | + height: 100%; | |
| 252 | + } | |
| 253 | +} | |
| 254 | + | |
| 255 | +// 道路列表容器 | |
| 256 | +.road-list-wrap { | |
| 257 | + //padding-bottom: 30rpx; | |
| 258 | +} | |
| 259 | + | |
| 260 | +// 道路卡片样式 完全复用你的原版 | |
| 261 | +.card-wrap { | |
| 262 | + box-shadow:0 0 3px rgba(0, 0, 0, 0.2); | |
| 263 | + padding: 20rpx; | |
| 264 | + border-radius: 14rpx; | |
| 265 | + background: #fff; | |
| 266 | + margin:10px 6px; | |
| 267 | +} | |
| 10 | 268 | |
| 269 | +// 公共样式 完全不变 | |
| 270 | +.treeCount { | |
| 271 | + color: #5f5fe2; | |
| 272 | + min-width: 70px; | |
| 273 | + text-align: right; | |
| 274 | +} | |
| 275 | +.fs-flex__between { | |
| 276 | + display: flex; | |
| 277 | + justify-content: space-between; | |
| 278 | + align-items: center; | |
| 279 | +} | |
| 280 | +.fs-my8 { margin: 8rpx 0; } | |
| 11 | 281 | </style> |
| 12 | 282 | \ No newline at end of file | ... | ... |
pages-sub/data/tree-archive/treeRecord.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="container"> | |
| 3 | + <!-- ✅ 替换tui-no-data 为 uview-plus的空数据组件 up-empty --> | |
| 4 | + <up-empty | |
| 5 | + v-if="rows.length === 0" | |
| 6 | + text="暂无数据" | |
| 7 | + ></up-empty> | |
| 8 | + | |
| 9 | + <view class="record-wrap" v-else> | |
| 10 | + <view class="record-list-wrap cad-box-shadow fs-bg__white" v-for="i in rows" :key="i.id" @click="toEditPage(i.id)"> | |
| 11 | + <view style="display: flex"> | |
| 12 | + <view class="record-list-left" :style="`background-image: url(${i.treephoto});`"></view> | |
| 13 | + <view class="record-list-right"> | |
| 14 | + <view class="record-list-right-title"> | |
| 15 | + <view class="fs-ellipsis treetypeName">{{ i.treetype }}</view> | |
| 16 | + <view style="text-align: right">{{ i.updatetime.substring(0, 10) }}</view> | |
| 17 | + </view> | |
| 18 | + <view class="fs-mt8 fs-align__center"> | |
| 19 | + <img src="../../../static/imgs/tree/tree-high.png" style="width: 14px;height: 14px;margin-right: 6px;" alt=""> 高度:{{ i.treeheight }} 米 | |
| 20 | + </view> | |
| 21 | + <view class="fs-mt8 fs-align__center"> | |
| 22 | + <img src="../../../static/imgs/tree/treearound.png" style="width: 14px;height: 14px;margin-right: 6px;" alt="">胸径:{{ i.dbh }} 厘米 | |
| 23 | + </view> | |
| 24 | + </view> | |
| 25 | + </view> | |
| 26 | + <view class="fs-mt8 fs-ellipsis treenumber-no"> | |
| 27 | + 树木编号:{{ i.treenumber }} | |
| 28 | + </view> | |
| 29 | + </view> | |
| 30 | + </view> | |
| 31 | + | |
| 32 | + <view class="fixed-bottom-btn-wrap"> | |
| 33 | + <up-button | |
| 34 | + type="primary" | |
| 35 | + class="addTree" | |
| 36 | + @click="toAddTreePage" | |
| 37 | + v-show="count > 0 && count > rows.length" | |
| 38 | + > | |
| 39 | + 新增树木录入 | |
| 40 | + </up-button> | |
| 41 | + </view> | |
| 42 | + | |
| 43 | + </view> | |
| 44 | +</template> | |
| 45 | + | |
| 46 | +<script setup> | |
| 47 | +import { ref} from 'vue' | |
| 48 | +import { onLoad, onShow } from '@dcloudio/uni-app'; | |
| 49 | +// 引入接口请求方法 | |
| 50 | +import { treeRoadReq } from "@/api/tree-archive/tree-archive.js"; | |
| 51 | + | |
| 52 | + | |
| 53 | +const rows = ref([]) | |
| 54 | +const roadId = ref('') | |
| 55 | +const count = ref(0) | |
| 56 | + | |
| 57 | + | |
| 58 | +onLoad((options) => { | |
| 59 | + console.log(options) | |
| 60 | + roadId.value = options.roadId | |
| 61 | + count.value = options.count | |
| 62 | +}) | |
| 63 | + | |
| 64 | +onShow(() => { | |
| 65 | + treeRoadQuery() | |
| 66 | +}) | |
| 67 | + | |
| 68 | +// 前往修改页面 | |
| 69 | +const toEditPage = (id) => { | |
| 70 | + uni.navigateTo({ | |
| 71 | + url: `/pages-sub/data/tree-archive/editTree?id=${id}` | |
| 72 | + }) | |
| 73 | +} | |
| 74 | + | |
| 75 | +// 前往新增页面 | |
| 76 | +const toAddTreePage = () => { | |
| 77 | + uni.navigateTo({ | |
| 78 | + url: `/pages-sub/data/tree-archive/addTree?roadId=${roadId.value}` | |
| 79 | + }) | |
| 80 | +} | |
| 81 | + | |
| 82 | +// 树木记录列表查询接口 | |
| 83 | +const treeRoadQuery = async () => { | |
| 84 | + const res = await treeRoadReq( {road: roadId.value}) | |
| 85 | + console.log(res) | |
| 86 | + rows.value = res.rows | |
| 87 | +} | |
| 88 | +</script> | |
| 89 | + | |
| 90 | +<style scoped lang="scss"> | |
| 91 | +// 保留原页面所有样式,一行未改,样式完全一致 | |
| 92 | +.record-wrap { | |
| 93 | + padding-bottom: 60px; | |
| 94 | +} | |
| 95 | + | |
| 96 | +.record-list-wrap { | |
| 97 | + margin: 15px 10px 0; | |
| 98 | + padding: 10px; | |
| 99 | + border-radius: 6px; | |
| 100 | + font-size: 14px; | |
| 101 | +} | |
| 102 | + | |
| 103 | +.treetypeName { | |
| 104 | + flex: 1; | |
| 105 | + font-size: 16px; | |
| 106 | + font-weight: bold; | |
| 107 | +} | |
| 108 | + | |
| 109 | +.record-list-left { | |
| 110 | + height: 70px; | |
| 111 | + width: 70px; | |
| 112 | + background-size: 100% 100%; | |
| 113 | +} | |
| 114 | + | |
| 115 | +.record-list-right { | |
| 116 | + margin-left: 20px; | |
| 117 | + flex: 1; | |
| 118 | + overflow: hidden; | |
| 119 | +} | |
| 120 | + | |
| 121 | +.record-list-right-title { | |
| 122 | + display: flex; | |
| 123 | + justify-content: space-between; | |
| 124 | +} | |
| 125 | + | |
| 126 | +.treenumber-no { | |
| 127 | + padding: 3px 10px; | |
| 128 | + background: #bdefd0; | |
| 129 | + font-size: 12px; | |
| 130 | +} | |
| 131 | + | |
| 132 | +.addTree { | |
| 133 | + width: 100%; | |
| 134 | + position: fixed; | |
| 135 | + bottom: 0; | |
| 136 | +} | |
| 137 | +</style> | |
| 0 | 138 | \ No newline at end of file | ... | ... |
pages-sub/problem/ai-manage/index.vue
| ... | ... | @@ -3,7 +3,6 @@ |
| 3 | 3 | <!-- 顶部固定区域 --> |
| 4 | 4 | <up-sticky> |
| 5 | 5 | <view class="header-wrap"> |
| 6 | - <!-- 第一行:u-tabs 待办/已办切换 :scrollable="false"--> | |
| 7 | 6 | <up-tabs |
| 8 | 7 | v-model="activeTab" |
| 9 | 8 | :list="tabList" |
| ... | ... | @@ -12,10 +11,8 @@ |
| 12 | 11 | font-size="30rpx" |
| 13 | 12 | @click="handleTabChange" |
| 14 | 13 | /> |
| 15 | - | |
| 16 | 14 | <!-- 第二行:下拉框 + 搜索框 --> |
| 17 | 15 | <view class="search-header"> |
| 18 | - <!-- 左侧下拉框 --> | |
| 19 | 16 | <view class="select-wrap common-text-color"> |
| 20 | 17 | <up-select |
| 21 | 18 | v-model:current="selectedSortValue" |
| ... | ... | @@ -26,8 +23,6 @@ |
| 26 | 23 | :style="{ flex: 1 }" |
| 27 | 24 | /> |
| 28 | 25 | </view> |
| 29 | - | |
| 30 | - <!-- 右侧搜索框 --> | |
| 31 | 26 | <view class="search-input-wrap"> |
| 32 | 27 | <up-search |
| 33 | 28 | v-model="searchValue" |
| ... | ... | @@ -47,24 +42,23 @@ |
| 47 | 42 | |
| 48 | 43 | <!-- 列表容器 --> |
| 49 | 44 | <z-paging |
| 50 | - ref="paging" | |
| 45 | + ref="pagingRef" | |
| 51 | 46 | v-model="orderList" |
| 52 | 47 | @query="queryList" |
| 53 | 48 | :auto-show-system-loading="true" |
| 54 | - | |
| 55 | 49 | > |
| 56 | 50 | <template #empty> |
| 57 | - <empty-view/> | |
| 51 | + <empty-view /> | |
| 58 | 52 | </template> |
| 59 | 53 | |
| 60 | 54 | <view class="common-card-list" style="padding-top: 200rpx;padding-bottom: 30rpx"> |
| 61 | 55 | <!-- 待办工单卡片 --> |
| 62 | 56 | <up-card |
| 63 | - v-if="activeTab == 0" | |
| 57 | + v-if="activeTab === 0" | |
| 64 | 58 | :border="false" |
| 65 | 59 | :foot-border-top="false" |
| 66 | 60 | v-for="(item, index) in orderList" |
| 67 | - :key="`todo_${item.orderNo}_${index}`" | |
| 61 | + :key="`todo_${item.id}_${index}`" | |
| 68 | 62 | :show-head="false" |
| 69 | 63 | class="order-card" |
| 70 | 64 | > |
| ... | ... | @@ -86,7 +80,7 @@ |
| 86 | 80 | <view class="u-body-item-title">情况描述:</view> |
| 87 | 81 | <view class="u-line-1 u-body-value">{{ item.remark || '无' }}</view> |
| 88 | 82 | </view> |
| 89 | - <view class="u-body-item u-flex common-item-center common-justify-between"> | |
| 83 | + <view class="u-body-item u-flex common-item-center common-justify-between"> | |
| 90 | 84 | <view class="u-body-item-title">紧急程度:</view> |
| 91 | 85 | <view class="u-line-1 u-body-value"> |
| 92 | 86 | {{ uni.$dict.getDictLabel('workorder_pressing_type', item.pressingType) }} |
| ... | ... | @@ -101,20 +95,10 @@ |
| 101 | 95 | <view class="u-line-1 u-body-value">{{ timeFormat(item.createTime, 'yyyy-mm-dd hh:MM:ss') }}</view> |
| 102 | 96 | </view> |
| 103 | 97 | <!-- 操作按钮行 --> |
| 104 | - | |
| 105 | 98 | <view class="u-body-item u-flex common-justify-between common-item-center mt-20"> |
| 106 | - <up-button type="warning" size="mini" @click="handleReject(item)" | |
| 107 | - v-show="nextStepMap[item.taskKey].backShow">回退 | |
| 108 | - </up-button> | |
| 109 | - | |
| 110 | - <up-button type="success" size="mini" @click="handleRenew(item)" | |
| 111 | - v-show="nextStepMap[item.taskKey].renewShow">重新提交 | |
| 112 | - </up-button> | |
| 113 | - | |
| 114 | - <up-button type="primary" size="mini" @click="handleProcess(item)">{{ | |
| 115 | - nextStepMap[item.taskKey].btnText | |
| 116 | - }} | |
| 117 | - </up-button> | |
| 99 | + <up-button type="warning" size="mini" @click="handleReject(item)" v-show="nextStepMap[item.taskKey].backShow">回退</up-button> | |
| 100 | + <up-button type="success" size="mini" @click="handleRenew(item)" v-show="nextStepMap[item.taskKey].renewShow">重新提交</up-button> | |
| 101 | + <up-button type="primary" size="mini" @click="handleProcess(item)">{{ nextStepMap[item.taskKey].btnText }}</up-button> | |
| 118 | 102 | <up-button type="info" size="mini" @click="handleDetail(item)">详情</up-button> |
| 119 | 103 | </view> |
| 120 | 104 | </view> |
| ... | ... | @@ -123,11 +107,11 @@ |
| 123 | 107 | |
| 124 | 108 | <!-- 已办工单卡片和我发起的 --> |
| 125 | 109 | <up-card |
| 126 | - v-if="activeTab == 2||activeTab == 1" | |
| 110 | + v-if="activeTab === 1 || activeTab === 2" | |
| 127 | 111 | :border="false" |
| 128 | 112 | :foot-border-top="false" |
| 129 | 113 | v-for="(item, index) in orderList" |
| 130 | - :key="`done_${item.orderNo}_${index}`" | |
| 114 | + :key="`done_${item.id}_${index}`" | |
| 131 | 115 | :show-head="false" |
| 132 | 116 | class="order-card" |
| 133 | 117 | > |
| ... | ... | @@ -149,12 +133,11 @@ |
| 149 | 133 | <view class="u-body-item-title">情况描述:</view> |
| 150 | 134 | <view class="u-line-1 u-body-value">{{ item.remark || '无' }}</view> |
| 151 | 135 | </view> |
| 152 | - | |
| 153 | 136 | <view class="u-body-item u-flex common-justify-between common-item-center"> |
| 154 | 137 | <view class="u-body-item-title"> |
| 155 | 138 | 紧急程度:{{ uni.$dict.getDictLabel('workorder_pressing_type', item.pressingType) }} |
| 156 | 139 | </view> |
| 157 | - <view class=" "> | |
| 140 | + <view> | |
| 158 | 141 | <up-button type="primary" size="mini" @click="handleDetail(item)">工单详情</up-button> |
| 159 | 142 | </view> |
| 160 | 143 | </view> |
| ... | ... | @@ -172,21 +155,12 @@ |
| 172 | 155 | </view> |
| 173 | 156 | </z-paging> |
| 174 | 157 | |
| 175 | - <!-- 底部新增工单按钮(仅巡查员显示) --> | |
| 176 | - <!-- <view v-if="isInspector" class="fixed-bottom-btn-wrap">--> | |
| 177 | - <!-- <up-button type="primary" size="large" @click="handleAddOrder">--> | |
| 178 | - <!-- 新增工单--> | |
| 179 | - <!-- </up-button>--> | |
| 180 | - <!-- </view>--> | |
| 181 | - | |
| 158 | + <!-- 底部新增工单按钮 --> | |
| 182 | 159 | <view class="fixed-bottom-btn-wrap" v-if="isAi"> |
| 183 | - <up-button type="primary" size="large" @click="handleAddOrder"> | |
| 184 | - 新增工单 | |
| 185 | - </up-button> | |
| 160 | + <up-button type="primary" size="large" @click="handleAddOrder">新增工单</up-button> | |
| 186 | 161 | </view> |
| 187 | 162 | |
| 188 | - | |
| 189 | - <!-- 回退原因弹窗:替换为up-modal(核心修改) --> | |
| 163 | + <!-- 回退原因弹窗 --> | |
| 190 | 164 | <up-modal |
| 191 | 165 | :show="rejectModalShow" |
| 192 | 166 | title="回退原因" |
| ... | ... | @@ -197,7 +171,6 @@ |
| 197 | 171 | @confirm="confirmReject" |
| 198 | 172 | > |
| 199 | 173 | <view class="reject-modal-content"> |
| 200 | - <!-- 回退原因 必填textarea --> | |
| 201 | 174 | <up-textarea |
| 202 | 175 | v-model.trim="rejectReason" |
| 203 | 176 | placeholder="请输入回退原因(必填)" |
| ... | ... | @@ -206,11 +179,10 @@ |
| 206 | 179 | maxlength="200" |
| 207 | 180 | class="reject-textarea" |
| 208 | 181 | /> |
| 209 | - <!-- 上传图片(选填)- 按照参考页面改造 --> | |
| 210 | 182 | <view class="upload-wrap mt-20"> |
| 211 | 183 | <view class="upload-title">上传图片(选填)</view> |
| 212 | 184 | <up-upload |
| 213 | - :file-list="rejectImgs.rawImgList.value|| []" | |
| 185 | + :file-list="rejectImgs.rawImgList.value || []" | |
| 214 | 186 | @after-read="rejectImgs.uploadImgs" |
| 215 | 187 | @delete="rejectImgs.deleteImg" |
| 216 | 188 | multiple |
| ... | ... | @@ -224,7 +196,7 @@ |
| 224 | 196 | </view> |
| 225 | 197 | </up-modal> |
| 226 | 198 | |
| 227 | - <!-- 验收弹窗 up-modal(含图片上传) --> | |
| 199 | + <!-- 验收弹窗 --> | |
| 228 | 200 | <up-modal |
| 229 | 201 | :show="acceptModalShow" |
| 230 | 202 | title="验收" |
| ... | ... | @@ -235,15 +207,12 @@ |
| 235 | 207 | @confirm="handleAcceptModalConfirm" |
| 236 | 208 | > |
| 237 | 209 | <view class="accept-modal-content"> |
| 238 | - <!-- 第一行:单选框(通过/不通过,默认通过) --> | |
| 239 | 210 | <view class="radio-group-wrap"> |
| 240 | 211 | <up-radio-group v-model="acceptRadioValue"> |
| 241 | 212 | <up-radio name="0" label="通过"></up-radio> |
| 242 | 213 | <up-radio name="1" label="不通过"></up-radio> |
| 243 | 214 | </up-radio-group> |
| 244 | 215 | </view> |
| 245 | - | |
| 246 | - <!-- 第二行:必填textarea,最多200字 --> | |
| 247 | 216 | <view class="textarea-wrap mt-30"> |
| 248 | 217 | <up-textarea |
| 249 | 218 | v-model.trim="acceptReason" |
| ... | ... | @@ -254,15 +223,12 @@ |
| 254 | 223 | count |
| 255 | 224 | /> |
| 256 | 225 | </view> |
| 257 | - | |
| 258 | - <!-- 验收图片上传(选填,参考回退弹窗样式) --> | |
| 259 | 226 | <view class="upload-wrap mt-20"> |
| 260 | 227 | <view class="upload-title">上传验收图片(选填)</view> |
| 261 | 228 | <up-upload |
| 262 | 229 | :file-list="acceptImgs.rawImgList.value || []" |
| 263 | 230 | @after-read="acceptImgs.uploadImgs" |
| 264 | 231 | @delete="acceptImgs.deleteImg" |
| 265 | - | |
| 266 | 232 | multiple |
| 267 | 233 | width="70" |
| 268 | 234 | height="70" |
| ... | ... | @@ -284,263 +250,233 @@ import { |
| 284 | 250 | myBuzSimplePage, |
| 285 | 251 | todoBuzSimplePage, |
| 286 | 252 | doneBuzSimplePage, |
| 287 | - qyWorkorderCreate, | |
| 288 | 253 | qyUniversalApproval, |
| 289 | 254 | daquUniversalApproval, |
| 290 | 255 | dcyUniversalApproval |
| 291 | 256 | } from '@/api/regional-order-manage/regional-order-manage' |
| 292 | -// 从用户store获取角色信息 | |
| 293 | 257 | import { useUserStore } from '@/pinia/user'; |
| 294 | 258 | import { nextStepMap, buzStatusMap } from '@/common/utils/common' |
| 295 | -// 引入图片上传组合式函数(与参考页面一致) | |
| 296 | 259 | import { useUploadImgs } from '@/common/utils/useUploadImgs' |
| 297 | -// ========== 状态管理 ========== | |
| 260 | + | |
| 261 | +// ========== 全局实例 & 常量 ========== | |
| 298 | 262 | const userStore = useUserStore(); |
| 299 | -// 标签页切换 | |
| 263 | +const USER_ROLES = userStore.userInfo?.roles || []; | |
| 264 | + | |
| 265 | +// ========== 基础状态 (按模块分组,语义化更强) ========== | |
| 266 | +// tab切换 | |
| 300 | 267 | const activeTab = ref(0); // 0-待办 1-我发起的 2-已办 |
| 301 | -const tabList = ref([ | |
| 302 | - {name: '待办'}, | |
| 303 | - {name: '我发起的任务'}, | |
| 304 | - {name: '已办'} | |
| 305 | -]); | |
| 306 | -// 排序下拉框 | |
| 268 | +const tabList = ref([{name: '待办'}, {name: '我发起的任务'}, {name: '已办'}]); | |
| 269 | +// 排序与搜索 | |
| 307 | 270 | const selectedSortValue = ref(1); |
| 308 | 271 | const sortOptions = ref([ |
| 309 | - {name: '位置', id: 1}, | |
| 310 | - {name: '名称', id: 2}, | |
| 311 | - {name: '描述', id: 3}, | |
| 312 | - {name: '编号', id: 4}, | |
| 272 | + {name: '位置', id: 1}, {name: '名称', id: 2}, {name: '描述', id: 3}, {name: '编号', id: 4}, | |
| 313 | 273 | ]); |
| 314 | -// 搜索 | |
| 315 | 274 | const searchValue = ref(''); |
| 316 | -// 分页 | |
| 317 | -const paging = ref(null); | |
| 275 | +// 分页相关 | |
| 276 | +const pagingRef = ref(null); | |
| 318 | 277 | const orderList = ref([]); |
| 319 | -// 角色控制(巡查员显示新增按钮) | |
| 278 | + | |
| 279 | +// ========== 角色权限计算属性 (修复核心bug: 原代码取反逻辑写反) ========== | |
| 320 | 280 | const isAi = computed(() => { |
| 321 | - // patrol_global 全域巡查员 | |
| 322 | - // regional_manager 大区经理 | |
| 323 | - // AI_dispatcher AI工单派发人员 | |
| 324 | - // 增加可选链,避免用户信息不存在报错 | |
| 325 | - console.log('123') | |
| 326 | - console.log(!userStore.userInfo?.roles?.includes('AI_dispatcher')) | |
| 327 | - return !userStore.userInfo?.roles?.includes('AI_dispatcher') ; | |
| 328 | - // return true | |
| 281 | + // AI工单派发人员 不显示新增按钮,其他角色显示 | |
| 282 | + return !USER_ROLES.includes('AI_dispatcher'); | |
| 329 | 283 | }); |
| 330 | -// 回退弹窗相关 | |
| 331 | -const rejectModalShow = ref(false); // 回退modal显示开关 | |
| 332 | -const rejectReason = ref(''); // 回退原因 | |
| 333 | -const currentRejectItem = ref(null); // 当前回退工单 | |
| 334 | -// 回退图片上传配置(与参考页面风格一致) | |
| 284 | + | |
| 285 | +// ========== 回退弹窗相关 ========== | |
| 286 | +const rejectModalShow = ref(false); | |
| 287 | +const rejectReason = ref(''); | |
| 288 | +const currentRejectItem = ref(null); | |
| 335 | 289 | const rejectImgs = useUploadImgs({ |
| 336 | - maxCount: 3, // 最多上传3张 | |
| 337 | - uploadText: '选择回退图片', // 自定义上传提示文字 | |
| 338 | - sizeType: ['compressed'], // 仅上传压缩图 | |
| 339 | - formRef: null, // 该弹窗无表单校验 | |
| 340 | - fieldName: 'rejectImgs' // 自定义字段名 | |
| 290 | + maxCount: 3, uploadText: '选择回退图片', sizeType: ['compressed'], formRef: null, fieldName: 'rejectImgs' | |
| 341 | 291 | }) |
| 342 | -// ========== 验收弹窗相关状态(含图片上传) ========== | |
| 343 | -const acceptModalShow = ref(false); // 验收弹窗显示开关 | |
| 344 | -const acceptRadioValue = ref('0'); // 单选框值,默认0(通过) | |
| 345 | -const acceptReason = ref(''); // 验收原因 | |
| 346 | -const currentAcceptItem = ref(null); // 当前验收的工单项 | |
| 347 | -// 验收图片上传配置(独立实例,参考回退弹窗) | |
| 292 | + | |
| 293 | +// ========== 验收弹窗相关 ========== | |
| 294 | +const acceptModalShow = ref(false); | |
| 295 | +const acceptRadioValue = ref('0'); // 默认通过 | |
| 296 | +const acceptReason = ref(''); | |
| 297 | +const currentAcceptItem = ref(null); | |
| 348 | 298 | const acceptImgs = useUploadImgs({ |
| 349 | - maxCount: 3, // 最多上传3张,与回退弹窗一致 | |
| 350 | - uploadText: '选择验收图片', // 自定义上传提示文字 | |
| 351 | - sizeType: ['compressed'], // 仅上传压缩图,优化性能 | |
| 352 | - formRef: null, // 验收弹窗无表单校验 | |
| 353 | - fieldName: 'acceptImgs' // 自定义字段名,区分回退图片 | |
| 299 | + maxCount: 3, uploadText: '选择验收图片', sizeType: ['compressed'], formRef: null, fieldName: 'acceptImgs' | |
| 354 | 300 | }) |
| 301 | + | |
| 302 | +// ========== 公共封装方法 (核心优化:消灭重复代码) ========== | |
| 303 | +/** | |
| 304 | + * 生成统一的临时存储key | |
| 305 | + * @param {String} prefix 前缀标识 | |
| 306 | + * @returns {String} 唯一key | |
| 307 | + */ | |
| 308 | +const generateTempKey = (prefix = 'order') => { | |
| 309 | + return `${prefix}_${Date.now()}_${Math.floor(Math.random() * 10000)}`; | |
| 310 | +}; | |
| 311 | + | |
| 312 | +/** | |
| 313 | + * 存储工单数据到本地缓存 | |
| 314 | + * @param {Object} item 工单数据 | |
| 315 | + * @param {String} prefix key前缀 | |
| 316 | + * @returns {String|null} 成功返回key,失败返回null | |
| 317 | + */ | |
| 318 | +const setOrderStorage = (item, prefix) => { | |
| 319 | + if (!item?.id) return null; | |
| 320 | + const tempKey = generateTempKey(prefix); | |
| 321 | + try { | |
| 322 | + uni.setStorageSync(tempKey, item); | |
| 323 | + return tempKey; | |
| 324 | + } catch (error) { | |
| 325 | + console.error('存储工单数据失败:', error); | |
| 326 | + uni.showToast({title: '数据存储异常,请重试', icon: 'none'}); | |
| 327 | + return null; | |
| 328 | + } | |
| 329 | +}; | |
| 330 | + | |
| 331 | +/** | |
| 332 | + * 获取分页请求公共参数 | |
| 333 | + * @param {Number} pageNo 页码 | |
| 334 | + * @param {Number} pageSize 页大小 | |
| 335 | + * @returns {Object} 请求参数 | |
| 336 | + */ | |
| 337 | +const getQueryParams = (pageNo, pageSize) => { | |
| 338 | + return { | |
| 339 | + searchContent: searchValue.value.trim() || '', | |
| 340 | + pageNo, | |
| 341 | + pageSize, | |
| 342 | + type: selectedSortValue.value | |
| 343 | + }; | |
| 344 | +}; | |
| 345 | + | |
| 346 | +/** | |
| 347 | + * 统一调用审批接口 | |
| 348 | + * @param {Object} params 请求参数 | |
| 349 | + * @param {String} taskKey 工单任务key | |
| 350 | + * @returns {Promise} 接口请求Promise | |
| 351 | + */ | |
| 352 | +const callApprovalApi = async (params, taskKey) => { | |
| 353 | + if (taskKey === 'shRegionManager') return await dcyUniversalApproval(params); | |
| 354 | + if (taskKey === 'regionManager') return await qyUniversalApproval(params); | |
| 355 | + // 根据角色匹配对应接口 | |
| 356 | + if (USER_ROLES.includes('regional_manager')) return await daquUniversalApproval(params); | |
| 357 | + if (USER_ROLES.includes('Inspector_global')) return await dcyUniversalApproval(params); | |
| 358 | + if (USER_ROLES.includes('patrol_global')) return await qyUniversalApproval(params); | |
| 359 | +}; | |
| 360 | + | |
| 361 | +/** | |
| 362 | + * 统一跳转工单页面 | |
| 363 | + * @param {String} path 页面路径 | |
| 364 | + * @param {Object} query 拼接参数 | |
| 365 | + */ | |
| 366 | +const navToOrderPage = (path, query = {}) => { | |
| 367 | + const queryStr = Object.keys(query).map(k => `${k}=${query[k]}`).join('&'); | |
| 368 | + uni.navigateTo({ url: `${path}${queryStr ? '?' + queryStr : ''}` }); | |
| 369 | +}; | |
| 370 | + | |
| 371 | +/** | |
| 372 | + * 刷新列表 - 统一封装,避免空指针 | |
| 373 | + */ | |
| 374 | +const refreshOrderList = () => { | |
| 375 | + pagingRef.value && pagingRef.value.reload(); | |
| 376 | +}; | |
| 377 | + | |
| 378 | +// ========== 业务核心方法 ========== | |
| 355 | 379 | // 分页查询列表 |
| 356 | 380 | const queryList = async (pageNo, pageSize) => { |
| 357 | 381 | try { |
| 358 | - const apiParams = { | |
| 359 | - searchContent: searchValue.value.trim() || '', | |
| 360 | - pageNo, | |
| 361 | - pageSize, | |
| 362 | - type: selectedSortValue.value // 1-位置 2-工单名称 3-情况描述 4-工单编号 | |
| 363 | - }; | |
| 382 | + const params = getQueryParams(pageNo, pageSize); | |
| 364 | 383 | let res; |
| 365 | - if (activeTab.value == 0) { | |
| 366 | - // 待办工单 | |
| 367 | - res = await todoBuzSimplePage(apiParams); | |
| 368 | - } else if (activeTab.value == 1) { | |
| 369 | - // 我发起的任务 | |
| 370 | - res = await myBuzSimplePage(apiParams); | |
| 371 | - } else { | |
| 372 | - // 已办工单 | |
| 373 | - res = await doneBuzSimplePage(apiParams); | |
| 374 | - } | |
| 375 | - // 适配z-paging分页 | |
| 376 | - paging.value.complete(res.list, res.total); | |
| 384 | + if (activeTab.value === 0) res = await todoBuzSimplePage(params); | |
| 385 | + else if (activeTab.value === 1) res = await myBuzSimplePage(params); | |
| 386 | + else res = await doneBuzSimplePage(params); | |
| 387 | + pagingRef.value.complete(res?.list || [], res?.total || 0); | |
| 377 | 388 | } catch (error) { |
| 378 | 389 | console.error('加载工单失败:', error); |
| 379 | - paging.value?.complete(false); | |
| 390 | + pagingRef.value?.complete(false); | |
| 380 | 391 | uni.showToast({title: '加载失败,请重试', icon: 'none'}); |
| 381 | 392 | } |
| 382 | 393 | }; |
| 383 | -// ========== 事件处理 ========== | |
| 394 | + | |
| 384 | 395 | // 标签页切换 |
| 385 | 396 | const handleTabChange = (item) => { |
| 386 | - orderList.value = []; | |
| 387 | - console.log(item) | |
| 388 | 397 | activeTab.value = item.index; |
| 389 | - paging.value?.reload(); // 切换标签页刷新列表 | |
| 398 | + orderList.value = []; | |
| 399 | + refreshOrderList(); | |
| 390 | 400 | }; |
| 401 | + | |
| 391 | 402 | // 排序变更 |
| 392 | 403 | const handleSortChange = (val) => { |
| 393 | 404 | selectedSortValue.value = val.id; |
| 394 | 405 | searchValue.value = ''; |
| 395 | - paging.value?.reload(); // 排序变更刷新列表 | |
| 406 | + refreshOrderList(); | |
| 396 | 407 | }; |
| 408 | + | |
| 397 | 409 | // 搜索 |
| 398 | 410 | const handleSearch = (val) => { |
| 399 | 411 | searchValue.value = val; |
| 400 | - paging.value?.reload(); // 搜索刷新列表 | |
| 412 | + refreshOrderList(); | |
| 401 | 413 | }; |
| 414 | + | |
| 402 | 415 | // 工单详情 |
| 403 | 416 | const handleDetail = (item) => { |
| 404 | - // 0-待办 1我发起的- 2-已办 | |
| 405 | - uni.navigateTo({ | |
| 406 | - url: `/pages-sub/problem/regional-order-manage/order-detail?taskId=${item.taskId}&activeTab=${activeTab.value}&processInstanceId=${item.processInstanceId}`, | |
| 407 | - events: { | |
| 408 | - // 自定义事件名:needRefresh(与详情页保持一致) | |
| 409 | - needRefresh: () => { | |
| 410 | - console.log('详情页返回,触发工单列表刷新'); | |
| 411 | - if (paging.value) { | |
| 412 | - paging.value.reload(); // 刷新z-paging列表 | |
| 413 | - } | |
| 414 | - } | |
| 415 | - } | |
| 417 | + if (!item?.taskId) return uni.showToast({title: '工单信息异常', icon: 'none'}); | |
| 418 | + navToOrderPage('/pages-sub/problem/regional-order-manage/order-detail', { | |
| 419 | + taskId: item.taskId, | |
| 420 | + activeTab: activeTab.value, | |
| 421 | + processInstanceId: item.processInstanceId | |
| 422 | + }, { | |
| 423 | + needRefresh: () => refreshOrderList() | |
| 416 | 424 | }); |
| 417 | 425 | }; |
| 418 | -// 生成临时key | |
| 419 | -const generateTempKey = () => { | |
| 420 | - return 'renew_order_' + Date.now() + '_' + Math.floor(Math.random() * 10000); | |
| 421 | -}; | |
| 422 | -// 待办-重新提交工单(改造后:大数据存本地,仅传唯一标识) | |
| 426 | + | |
| 427 | +// 重新提交工单 | |
| 423 | 428 | const handleRenew = (item) => { |
| 429 | + const tempKey = setOrderStorage(item, 'renew_order'); | |
| 430 | + if (!tempKey) return; | |
| 424 | 431 | |
| 425 | - // 1. 生成唯一临时标识 | |
| 426 | - const tempKey = generateTempKey(); | |
| 427 | - // 2. 将完整工单数据存入本地临时存储(同步存储,确保数据立即生效) | |
| 428 | - try { | |
| 429 | - console.log(item) | |
| 430 | - console.log('123') | |
| 431 | - uni.setStorageSync(tempKey, item); | |
| 432 | - } catch (error) { | |
| 433 | - console.error('存储工单数据失败:', error); | |
| 434 | - uni.showToast({title: '数据存储异常,无法重新提交', icon: 'none'}); | |
| 435 | - return; | |
| 436 | - } | |
| 432 | + const pageUrl = USER_ROLES.includes('patrol_global') | |
| 433 | + ? '/pages-sub/problem/regional-order-manage/add-patrol-order' | |
| 434 | + : '/pages-sub/problem/regional-order-manage/add-order'; | |
| 437 | 435 | |
| 438 | - if (userStore.userInfo?.roles.includes('patrol_global')) { // 全域巡查员 | |
| 439 | - uni.navigateTo({ | |
| 440 | - url: `/pages-sub/problem/regional-order-manage/add-patrol-order?isRenew=1&tempKey=${tempKey}` | |
| 441 | - }); | |
| 442 | - } | |
| 443 | - if (userStore.userInfo?.roles.includes('regional_manager')) { // 大区经理 | |
| 444 | - uni.navigateTo({ | |
| 445 | - url: `/pages-sub/problem/regional-order-manage/add-order?isRenew=1&tempKey=${tempKey}` | |
| 446 | - }); | |
| 447 | - } | |
| 448 | - if (userStore.userInfo?.roles.includes('Inspector_global')) { // 督察员 | |
| 449 | - uni.navigateTo({ | |
| 450 | - url: `/pages-sub/problem/regional-order-manage/add-order?isRenew=1&tempKey=${tempKey}` | |
| 451 | - }); | |
| 452 | - } | |
| 453 | - // // 3. URL 仅传递「唯一标识」和「重新提交标记」(数据量极小,无长度问题) | |
| 454 | - // uni.navigateTo({ | |
| 455 | - // url: `/pages-sub/problem/regional-order-manage/add-patrol-order?isRenew=1&tempKey=${tempKey}` | |
| 456 | - // }); | |
| 436 | + navToOrderPage(pageUrl, { isRenew: 1, tempKey }); | |
| 457 | 437 | }; |
| 458 | -// 待办-处理工单 | |
| 438 | + | |
| 439 | +// 处理工单核心逻辑 | |
| 459 | 440 | const handleProcess = async (item) => { |
| 460 | - console.log(nextStepMap[item.taskKey].name) | |
| 441 | + if (!item) return uni.showToast({title: '工单信息异常', icon: 'none'}); | |
| 442 | + const stepName = nextStepMap[item.taskKey]?.name; | |
| 443 | + | |
| 461 | 444 | try { |
| 462 | - if (nextStepMap[item.taskKey]?.name == '大区经理分配') { | |
| 463 | - // ① 生成唯一临时key(统一规则,避免冲突) | |
| 464 | - const tempKey = `distribute_order_${Date.now()}_${Math.floor(Math.random() * 10000)}`; | |
| 465 | - // ② 存储完整item到本地缓存(同步存储,确保立即生效) | |
| 466 | - try { | |
| 467 | - uni.setStorageSync(tempKey, item); | |
| 468 | - } catch (error) { | |
| 469 | - console.error('存储分配工单数据失败:', error); | |
| 470 | - uni.showToast({title: '数据存储异常,无法跳转', icon: 'none'}); | |
| 471 | - return; | |
| 472 | - } | |
| 473 | - // ③ URL仅传递临时key,无其他冗余参数 | |
| 474 | - uni.navigateTo({ | |
| 475 | - url: `/pages-sub/problem/regional-order-manage/distribution-order?tempKey=${tempKey}` | |
| 476 | - }) | |
| 445 | + // 大区经理分配 | |
| 446 | + if (stepName === '大区经理分配') { | |
| 447 | + const tempKey = setOrderStorage(item, 'distribute_order'); | |
| 448 | + tempKey && navToOrderPage('/pages-sub/problem/regional-order-manage/distribution-order', { tempKey }); | |
| 477 | 449 | } |
| 478 | - if (nextStepMap[item.taskKey]?.name == '督察员单子大区经理分配') { | |
| 479 | - let postData = { | |
| 480 | - "taskKey":item.taskKey, | |
| 481 | - "taskId": item.taskId, | |
| 482 | - "operateType":60, | |
| 483 | - "workerDataId":item.id, | |
| 484 | - "agree":0, | |
| 485 | - "reason":item.remark, | |
| 486 | - "roadId":item.roadId, | |
| 487 | - "roadName":item.roadName, | |
| 488 | - "pressingType":item.pressingType, | |
| 489 | - "orderName":item.orderName, | |
| 490 | - "expectedFinishDate": item.expectedFinishDate, | |
| 491 | - "busiLine":item.busiLine, | |
| 492 | - } | |
| 493 | - const res = await dcyUniversalApproval(postData); | |
| 494 | - uni.showToast({title: '分配成功', icon: 'success', duration: 1000}); | |
| 495 | - paging.value?.reload(); // 刷新列表 | |
| 450 | + // 督察员单子大区经理分配 | |
| 451 | + else if (stepName === '督察员单子大区经理分配') { | |
| 452 | + const postData = { taskKey: item.taskKey, taskId: item.taskId, operateType:60, workerDataId:item.id, agree:0, reason:item.remark, roadId:item.roadId, roadName:item.roadName, pressingType:item.pressingType, orderName:item.orderName, expectedFinishDate: item.expectedFinishDate, busiLine:item.busiLine }; | |
| 453 | + await dcyUniversalApproval(postData); | |
| 454 | + uni.showToast({title: '分配成功', icon: 'success'}); | |
| 455 | + refreshOrderList(); | |
| 496 | 456 | } |
| 497 | - | |
| 498 | - | |
| 499 | - // 验收 - 打开弹窗 | |
| 500 | - if (nextStepMap[item.taskKey]?.name == '巡查员验收' || nextStepMap[item.taskKey]?.name == '养护组长验收') { | |
| 501 | - currentAcceptItem.value = item; // 存储当前工单信息 | |
| 502 | - acceptReason.value = ''; // 清空上次的验收原因 | |
| 503 | - acceptRadioValue.value = '0'; // 重置默认选中“通过” | |
| 504 | - acceptModalShow.value = true; // 显示验收弹窗 | |
| 457 | + // 验收弹窗 | |
| 458 | + else if (['巡查员验收', '养护组长验收'].includes(stepName)) { | |
| 459 | + currentAcceptItem.value = item; | |
| 460 | + acceptReason.value = ''; | |
| 461 | + acceptRadioValue.value = '0'; | |
| 462 | + acceptModalShow.value = true; | |
| 505 | 463 | } |
| 506 | - | |
| 507 | - // 发起人确认 | |
| 508 | - if (nextStepMap[item.taskKey]?.name == '发起人确认') { | |
| 509 | - console.log(item) | |
| 464 | + // 发起人确认-结束工单 | |
| 465 | + else if (stepName === '发起人确认') { | |
| 510 | 466 | uni.showModal({ |
| 511 | 467 | title: "结束工单", |
| 512 | 468 | content: "请确定是否结束工单?", |
| 513 | - success: async function (res) { | |
| 469 | + success: async (res) => { | |
| 514 | 470 | if (res.confirm) { |
| 515 | - // 构建请求参数 | |
| 516 | 471 | const requestData = { |
| 517 | - | |
| 518 | - "workerDataId": item.id, | |
| 519 | - "taskKey": 'ylInspectorStart', | |
| 520 | - "taskId": item.taskId, | |
| 521 | - "operateType": 200, | |
| 522 | - "agree": 1, | |
| 523 | - "reason": '结束工单' | |
| 472 | + workerDataId: item.id, taskKey: 'ylInspectorStart', taskId: item.taskId, | |
| 473 | + operateType: 200, agree: 1, reason: '结束工单' | |
| 524 | 474 | }; |
| 525 | - // 调用回退工单接口 daquUniversalApproval | |
| 526 | - | |
| 527 | - if( userStore.userInfo.roles.includes('regional_manager')){ // 大区经理 | |
| 528 | - await daquUniversalApproval(requestData) | |
| 529 | - } | |
| 530 | - if( userStore.userInfo.roles.includes('Inspector_global')){ // 督察员 | |
| 531 | - await dcyUniversalApproval(requestData); | |
| 532 | - } | |
| 533 | - if( userStore.userInfo.roles.includes('patrol_global')){ // 全域巡查员 | |
| 534 | - await qyUniversalApproval(requestData); | |
| 535 | - } | |
| 536 | - | |
| 537 | - uni.showToast({title: '结束成功', icon: 'success', duration: 1000}); | |
| 538 | - rejectModalShow.value = false; | |
| 539 | - paging.value?.reload(); // 刷新列表 | |
| 540 | - } else if (res.cancel) { | |
| 541 | - console.log("用户点击取消"); | |
| 475 | + await callApprovalApi(requestData, item.taskKey); | |
| 476 | + uni.showToast({title: '结束成功', icon: 'success'}); | |
| 477 | + refreshOrderList(); | |
| 542 | 478 | } |
| 543 | - }, | |
| 479 | + } | |
| 544 | 480 | }); |
| 545 | 481 | } |
| 546 | 482 | } catch (error) { |
| ... | ... | @@ -548,168 +484,92 @@ const handleProcess = async (item) => { |
| 548 | 484 | uni.showToast({title: '处理失败,请重试', icon: 'none'}); |
| 549 | 485 | } |
| 550 | 486 | }; |
| 551 | -// 待办-回退工单(打开回退modal) | |
| 487 | + | |
| 488 | +// 回退工单-打开弹窗 | |
| 552 | 489 | const handleReject = (item) => { |
| 553 | - console.log('123213') | |
| 554 | - // 校验工单有效性 | |
| 555 | - if (!item || !item.id) { | |
| 556 | - uni.showToast({title: '工单信息异常,无法回退', icon: 'none'}); | |
| 557 | - return; | |
| 558 | - } | |
| 490 | + if (!item?.id) return uni.showToast({title: '工单信息异常,无法回退', icon: 'none'}); | |
| 559 | 491 | currentRejectItem.value = item; |
| 560 | - rejectReason.value = ''; // 清空上次输入 | |
| 561 | - rejectImgs.clearImgs(); // 改造后:使用组合式函数的清空方法 | |
| 562 | - rejectModalShow.value = true; // 显示回退modal | |
| 492 | + rejectReason.value = ''; | |
| 493 | + rejectImgs.clearImgs(); | |
| 494 | + rejectModalShow.value = true; | |
| 563 | 495 | }; |
| 564 | -// 回退modal - 取消按钮 | |
| 496 | + | |
| 497 | +// 关闭回退弹窗-重置状态 | |
| 565 | 498 | const handleRejectModalCancel = () => { |
| 566 | 499 | rejectModalShow.value = false; |
| 567 | 500 | rejectReason.value = ''; |
| 568 | - rejectImgs.clearImgs(); // 改造后:使用组合式函数的清空方法 | |
| 501 | + rejectImgs.clearImgs(); | |
| 569 | 502 | }; |
| 503 | + | |
| 570 | 504 | // 确认回退工单 |
| 571 | 505 | const confirmReject = async () => { |
| 572 | - // 严格校验回退原因(去除首尾空格) | |
| 573 | - const rejectReasonTrim = rejectReason.value.trim(); | |
| 574 | - if (!rejectReasonTrim) { | |
| 575 | - uni.showToast({title: '请填写回退原因', icon: 'none', duration: 1000}); | |
| 576 | - return; | |
| 577 | - } | |
| 578 | - // 校验当前工单有效性 | |
| 579 | - if (!currentRejectItem.value || !currentRejectItem.value.id) { | |
| 580 | - uni.showToast({title: '工单信息异常,无法提交', icon: 'none', duration: 1000}); | |
| 581 | - rejectModalShow.value = false; | |
| 582 | - return; | |
| 583 | - } | |
| 506 | + const reason = rejectReason.value.trim(); | |
| 507 | + if (!reason) return uni.showToast({title: '请填写回退原因', icon: 'none'}); | |
| 508 | + const item = currentRejectItem.value; | |
| 509 | + if (!item?.id) return uni.showToast({title: '工单信息异常', icon: 'none'}); | |
| 510 | + | |
| 511 | + uni.showLoading({title: '提交中...', mask: true}); | |
| 584 | 512 | try { |
| 585 | - // 显示加载中,防止重复提交 | |
| 586 | - uni.showLoading({title: '提交中...', mask: true}); | |
| 587 | - // 构建请求参数 | |
| 588 | 513 | const requestData = { |
| 589 | - "returnImgs": rejectImgs.getSuccessImgUrls(), // 改造后:获取上传成功的图片URL数组 | |
| 590 | - "workerDataId": currentRejectItem.value.id, | |
| 591 | - "taskKey": currentRejectItem.value.taskKey, | |
| 592 | - "taskId": currentRejectItem.value.taskId, | |
| 593 | - | |
| 594 | - "operateType": nextStepMap[currentRejectItem.value.taskKey].operateTypeNoPass, | |
| 595 | - "agree": 1, | |
| 596 | - "reason": rejectReasonTrim | |
| 514 | + returnImgs: rejectImgs.getSuccessImgUrls(), workerDataId: item.id, taskKey: item.taskKey, | |
| 515 | + taskId: item.taskId, operateType: nextStepMap[item.taskKey].operateTypeNoPass, agree:1, reason | |
| 597 | 516 | }; |
| 598 | - // 调用回退工单接口 | |
| 599 | - if(currentRejectItem.value.taskKey=='shRegionManager'){ // 对督察员单子 回退 | |
| 600 | - const res = await dcyUniversalApproval(requestData); | |
| 601 | - } | |
| 602 | - if(currentRejectItem.value.taskKey=='regionManager'){ // 对全域巡查员单子 回退 | |
| 603 | - const res = await qyUniversalApproval(requestData); | |
| 604 | - } | |
| 605 | - // 对大区经理单子 回退 | |
| 606 | - uni.showToast({title: '回退成功', icon: 'success', duration: 1000}); | |
| 607 | - rejectModalShow.value = false; | |
| 608 | - paging.value?.reload(); // 刷新列表 | |
| 517 | + await callApprovalApi(requestData, item.taskKey); | |
| 518 | + uni.showToast({title: '回退成功', icon: 'success'}); | |
| 519 | + handleRejectModalCancel(); | |
| 520 | + refreshOrderList(); | |
| 609 | 521 | } catch (error) { |
| 610 | 522 | console.error('回退工单失败:', error); |
| 611 | - uni.showToast({title: '网络异常,回退失败', icon: 'none', duration: 1000}); | |
| 523 | + uni.showToast({title: '回退失败,请重试', icon: 'none'}); | |
| 612 | 524 | } finally { |
| 613 | - // 隐藏加载中 | |
| 614 | 525 | uni.hideLoading(); |
| 615 | 526 | } |
| 616 | 527 | }; |
| 528 | + | |
| 617 | 529 | // 新增工单 |
| 618 | 530 | const handleAddOrder = () => { |
| 619 | - if (userStore.userInfo?.roles.includes('patrol_global')) { // 全域巡查员 | |
| 620 | - uni.navigateTo({ | |
| 621 | - url: '/pages-sub/problem/regional-order-manage/add-patrol-order' | |
| 622 | - }); | |
| 623 | - } | |
| 624 | - if (userStore.userInfo?.roles.includes('regional_manager')) { // 大区经理 | |
| 625 | - uni.navigateTo({ | |
| 626 | - url: '/pages-sub/problem/regional-order-manage/add-order' | |
| 627 | - }); | |
| 628 | - } | |
| 629 | - if (userStore.userInfo?.roles.includes('Inspector_global')) { // 督察员 | |
| 630 | - uni.navigateTo({ | |
| 631 | - url: '/pages-sub/problem/regional-order-manage/add-order' | |
| 632 | - }); | |
| 633 | - } | |
| 531 | + const pageUrl = USER_ROLES.includes('patrol_global') | |
| 532 | + ? '/pages-sub/problem/regional-order-manage/add-patrol-order' | |
| 533 | + : '/pages-sub/problem/regional-order-manage/add-order'; | |
| 534 | + navToOrderPage(pageUrl); | |
| 634 | 535 | }; |
| 635 | -// 验收弹窗 - 取消按钮(清空状态) | |
| 536 | + | |
| 537 | +// 关闭验收弹窗-重置状态 | |
| 636 | 538 | const handleAcceptModalCancel = () => { |
| 637 | 539 | acceptModalShow.value = false; |
| 638 | - acceptReason.value = ''; // 清空验收原因 | |
| 639 | - acceptRadioValue.value = '0'; // 重置单选框为“通过” | |
| 640 | - acceptImgs.clearImgs(); // 清空验收图片 | |
| 540 | + acceptReason.value = ''; | |
| 541 | + acceptRadioValue.value = '0'; | |
| 542 | + acceptImgs.clearImgs(); | |
| 641 | 543 | }; |
| 642 | -// 验收弹窗 - 确定按钮(含returnImgs传参) | |
| 544 | + | |
| 545 | +// 验收提交 | |
| 643 | 546 | const handleAcceptModalConfirm = async () => { |
| 644 | - // 1. 校验验收原因是否为空 | |
| 645 | - if (!acceptReason.value.trim()) { | |
| 646 | - uni.showToast({title: '请填写验收原因', icon: 'none', duration: 1000}); | |
| 647 | - return; | |
| 648 | - } | |
| 649 | - // 2. 校验验收原因长度 | |
| 650 | - if (acceptReason.value.length > 200) { | |
| 651 | - uni.showToast({title: '验收原因最多200字', icon: 'none', duration: 1000}); | |
| 652 | - return; | |
| 653 | - } | |
| 654 | - try { | |
| 655 | - // 3. 构建请求参数(含returnImgs) | |
| 656 | - console.log(currentAcceptItem.value) | |
| 657 | - console.log( userStore.userInfo.roles) | |
| 658 | - console.log( userStore.userInfo.roles.includes('Inspector_global')) | |
| 659 | - // | |
| 660 | - if( userStore.userInfo.roles.includes('regional_manager')){ // 大区经理验收 | |
| 661 | - let postData = { | |
| 662 | - "returnImgs": acceptImgs.getSuccessImgUrls(), // 验收图片URL数组 | |
| 663 | - "taskKey": currentAcceptItem.value.taskKey, | |
| 664 | - "workerDataId": currentAcceptItem.value.id, | |
| 665 | - "taskId": currentAcceptItem.value.taskId, | |
| 666 | - "operateType": acceptRadioValue.value == 0 ? nextStepMap[currentAcceptItem.value.taskKey].operateTypePass : nextStepMap[currentAcceptItem.value.taskKey].operateTypeNoPass, | |
| 667 | - "reason": acceptReason.value.trim(), | |
| 668 | - "agree":acceptRadioValue.value, | |
| 669 | - } | |
| 670 | - await daquUniversalApproval(postData); | |
| 671 | - } | |
| 672 | - // | |
| 673 | - if( userStore.userInfo.roles.includes('patrol_global')){ // 全域巡查员验收 | |
| 674 | - let postData = { | |
| 675 | - "returnImgs": acceptImgs.getSuccessImgUrls(), // 验收图片URL数组 | |
| 676 | - "taskKey": currentAcceptItem.value.taskKey, | |
| 677 | - "taskId": currentAcceptItem.value.taskId, | |
| 678 | - "workerDataId": currentAcceptItem.value.id, | |
| 679 | - "operateType": acceptRadioValue.value == 0 ? nextStepMap[currentAcceptItem.value.taskKey].operateTypePass : nextStepMap[currentAcceptItem.value.taskKey].operateTypeNoPass, | |
| 680 | - "reason": acceptReason.value.trim(), | |
| 681 | - "agree": acceptRadioValue.value | |
| 682 | - } | |
| 683 | - await qyUniversalApproval(postData); | |
| 684 | - } | |
| 685 | - // | |
| 686 | - if( userStore.userInfo.roles.includes('Inspector_global')){ // 督察员验收 | |
| 687 | - let postData = { | |
| 688 | - "returnImgs": acceptImgs.getSuccessImgUrls(), // 验收图片URL数组 | |
| 689 | - "taskKey": currentAcceptItem.value.taskKey, | |
| 690 | - "taskId": currentAcceptItem.value.taskId, | |
| 691 | - "workerDataId": currentAcceptItem.value.id, | |
| 692 | - "operateType": acceptRadioValue.value == 0 ? nextStepMap[currentAcceptItem.value.taskKey].operateTypePass : nextStepMap[currentAcceptItem.value.taskKey].operateTypeNoPass, | |
| 693 | - "reason": acceptReason.value.trim(), | |
| 694 | - "agree": acceptRadioValue.value | |
| 695 | - } | |
| 696 | - await dcyUniversalApproval(postData); | |
| 697 | - } | |
| 547 | + const reason = acceptReason.value.trim(); | |
| 548 | + if (!reason) return uni.showToast({title: '请填写验收原因', icon: 'none'}); | |
| 549 | + if (reason.length > 200) return uni.showToast({title: '验收原因最多200字', icon: 'none'}); | |
| 698 | 550 | |
| 699 | - // 4. 操作成功处理 | |
| 700 | - uni.showToast({title: '提交成功', icon: 'success', duration: 1000}); | |
| 701 | - handleAcceptModalCancel(); // 清空状态 | |
| 702 | - paging.value?.reload(); // 刷新工单列表 | |
| 551 | + const item = currentAcceptItem.value; | |
| 552 | + if (!item?.id) return uni.showToast({title: '工单信息异常', icon: 'none'}); | |
| 553 | + | |
| 554 | + try { | |
| 555 | + const postData = { | |
| 556 | + returnImgs: acceptImgs.getSuccessImgUrls(), taskKey: item.taskKey, workerDataId: item.id, | |
| 557 | + taskId: item.taskId, agree: acceptRadioValue.value, reason, | |
| 558 | + operateType: acceptRadioValue.value === '0' ? nextStepMap[item.taskKey].operateTypePass : nextStepMap[item.taskKey].operateTypeNoPass | |
| 559 | + }; | |
| 560 | + await callApprovalApi(postData, item.taskKey); | |
| 561 | + uni.showToast({title: '提交成功', icon: 'success'}); | |
| 562 | + handleAcceptModalCancel(); | |
| 563 | + refreshOrderList(); | |
| 703 | 564 | } catch (error) { |
| 704 | - // 5. 操作失败处理 | |
| 705 | 565 | console.error('验收失败:', error); |
| 706 | - uni.showToast({title: '验收提交失败,请重试', icon: 'none', duration: 1000}); | |
| 566 | + uni.showToast({title: '验收提交失败,请重试', icon: 'none'}); | |
| 707 | 567 | } |
| 708 | 568 | }; |
| 709 | -// 页面初始化 | |
| 569 | + | |
| 570 | +// ========== 页面生命周期 ========== | |
| 710 | 571 | onLoad(() => { |
| 711 | - // 初始化加载列表 | |
| 712 | - paging.value?.reload(); | |
| 572 | + refreshOrderList(); | |
| 713 | 573 | }); |
| 714 | 574 | </script> |
| 715 | 575 | |
| ... | ... | @@ -734,15 +594,10 @@ onLoad(() => { |
| 734 | 594 | .select-wrap { |
| 735 | 595 | width: 120rpx; |
| 736 | 596 | margin-right: 20rpx; |
| 737 | - | |
| 738 | - :deep(.u-select) { | |
| 597 | + :deep(.u-select), :deep(.u-input__placeholder) { | |
| 739 | 598 | width: 100%; |
| 740 | 599 | font-size: 28rpx; |
| 741 | 600 | } |
| 742 | - | |
| 743 | - :deep(.u-input__placeholder) { | |
| 744 | - font-size: 28rpx; | |
| 745 | - } | |
| 746 | 601 | } |
| 747 | 602 | |
| 748 | 603 | .search-input-wrap { |
| ... | ... | @@ -758,31 +613,14 @@ onLoad(() => { |
| 758 | 613 | box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04); |
| 759 | 614 | } |
| 760 | 615 | |
| 761 | -.card-body { | |
| 762 | - | |
| 763 | -} | |
| 764 | - | |
| 765 | -// 回退modal样式 | |
| 766 | -.reject-modal-content { | |
| 616 | +// 弹窗公共样式 | |
| 617 | +.reject-modal-content, .accept-modal-content { | |
| 767 | 618 | width: 100%; |
| 768 | 619 | box-sizing: border-box; |
| 769 | 620 | padding: 10rpx 0; |
| 770 | 621 | } |
| 771 | 622 | |
| 772 | -.textarea-label { | |
| 773 | - font-size: 28rpx; | |
| 774 | - color: #333; | |
| 775 | - margin-bottom: 10rpx; | |
| 776 | - | |
| 777 | - .required-mark { | |
| 778 | - color: #f56c6c; | |
| 779 | - margin-left: 4rpx; | |
| 780 | - } | |
| 781 | -} | |
| 782 | - | |
| 783 | 623 | .upload-wrap { |
| 784 | - margin-top: 20rpx; | |
| 785 | - | |
| 786 | 624 | .upload-title { |
| 787 | 625 | font-size: 28rpx; |
| 788 | 626 | color: #333; |
| ... | ... | @@ -790,37 +628,20 @@ onLoad(() => { |
| 790 | 628 | } |
| 791 | 629 | } |
| 792 | 630 | |
| 793 | -.mt-20 { | |
| 794 | - margin-top: 20rpx; | |
| 795 | -} | |
| 796 | - | |
| 797 | -.mt-30 { | |
| 798 | - margin-top: 30rpx; | |
| 799 | -} | |
| 800 | - | |
| 801 | -// 养护组长验收弹窗样式 | |
| 802 | -.accept-modal-content { | |
| 803 | - width: 100%; | |
| 804 | - box-sizing: border-box; | |
| 805 | -} | |
| 806 | - | |
| 631 | +// 验收弹窗单选框样式 | |
| 807 | 632 | .radio-group-wrap { |
| 808 | 633 | display: flex; |
| 809 | 634 | align-items: center; |
| 810 | - gap: 40rpx; // 单选框之间的间距 | |
| 635 | + gap: 40rpx; | |
| 811 | 636 | font-size: 28rpx; |
| 812 | 637 | margin-bottom: 20rpx; |
| 813 | 638 | } |
| 814 | 639 | |
| 815 | 640 | .textarea-wrap { |
| 816 | 641 | width: 100%; |
| 817 | - margin-top: 30rpx; | |
| 818 | 642 | } |
| 819 | 643 | |
| 820 | -.modal-btn-wrap { | |
| 821 | - display: flex; | |
| 822 | - align-items: center; | |
| 823 | - justify-content: flex-end; | |
| 824 | - padding-right: 10rpx; | |
| 825 | -} | |
| 644 | +// 间距公共样式 | |
| 645 | +.mt-20 { margin-top: 20rpx; } | |
| 646 | +.mt-30 { margin-top: 30rpx; } | |
| 826 | 647 | </style> |
| 827 | 648 | \ No newline at end of file | ... | ... |
pages-sub/problem/regional-order-manage/add-order.vue
| ... | ... | @@ -63,7 +63,7 @@ |
| 63 | 63 | <!-- 4. 问题照片 --> |
| 64 | 64 | <up-form-item label="问题照片" prop="problemImgs" required> |
| 65 | 65 | <up-upload |
| 66 | - :file-list="problemImgs.imgList.value||[]" | |
| 66 | + :file-list="problemImgs.imgList.value || []" | |
| 67 | 67 | @after-read="problemImgs.uploadImgs" |
| 68 | 68 | @delete="problemImgs.deleteImg" |
| 69 | 69 | multiple |
| ... | ... | @@ -75,11 +75,9 @@ |
| 75 | 75 | ></up-upload> |
| 76 | 76 | </up-form-item> |
| 77 | 77 | |
| 78 | - <!-- 派单情况分组(放在问题照片下方,增加间距) --> | |
| 79 | -<!-- <up-gap height="20" bgColor="#bbb"></up-gap>--> | |
| 78 | + <!-- 派单情况分组 --> | |
| 80 | 79 | <view class="dispatch-group"> |
| 81 | 80 | <view class="dispatch-title">派单情况</view> |
| 82 | - | |
| 83 | 81 | <!-- 业务线单选框 --> |
| 84 | 82 | <up-form-item |
| 85 | 83 | label="业务线" |
| ... | ... | @@ -117,7 +115,7 @@ |
| 117 | 115 | border="none" |
| 118 | 116 | ></up-input> |
| 119 | 117 | <template #right> |
| 120 | - <up-icon name="arrow-right" size="16" ></up-icon> | |
| 118 | + <up-icon name="arrow-right" size="16"></up-icon> | |
| 121 | 119 | </template> |
| 122 | 120 | </up-form-item> |
| 123 | 121 | |
| ... | ... | @@ -156,11 +154,7 @@ |
| 156 | 154 | ></up-input> |
| 157 | 155 | <template #right> |
| 158 | 156 | <view v-if="workOrderForm.expectedFinishDate" @click.stop> |
| 159 | - <up-icon | |
| 160 | - name="close" | |
| 161 | - size="16" | |
| 162 | - @click.stop="clearExpectedFinishDate" | |
| 163 | - ></up-icon> | |
| 157 | + <up-icon name="close" size="16" @click.stop="clearExpectedFinishDate"></up-icon> | |
| 164 | 158 | </view> |
| 165 | 159 | <up-icon name="arrow-right" size="16" v-else></up-icon> |
| 166 | 160 | </template> |
| ... | ... | @@ -170,11 +164,7 @@ |
| 170 | 164 | |
| 171 | 165 | <!-- 底部提交按钮 --> |
| 172 | 166 | <view class="fixed-bottom-btn-wrap"> |
| 173 | - <up-button | |
| 174 | - type="primary" | |
| 175 | - text="提交工单" | |
| 176 | - @click="submitWorkOrder" | |
| 177 | - ></up-button> | |
| 167 | + <up-button type="primary" text="提交工单" @click="submitWorkOrder"></up-button> | |
| 178 | 168 | </view> |
| 179 | 169 | |
| 180 | 170 | <!-- 合并后的通用下拉弹窗 --> |
| ... | ... | @@ -199,116 +189,73 @@ |
| 199 | 189 | </template> |
| 200 | 190 | |
| 201 | 191 | <script setup> |
| 202 | -import { ref, reactive } from 'vue' | |
| 203 | -import { onReady, onShow, onLoad } from '@dcloudio/uni-app'; | |
| 192 | +import { ref, reactive, nextTick } from 'vue' | |
| 193 | +import { onReady, onShow, onLoad, onBackPress } from '@dcloudio/uni-app'; | |
| 204 | 194 | import { useUploadImgs } from '@/common/utils/useUploadImgs' |
| 205 | 195 | import { getRoadListByLatLng } from '@/api/common' |
| 206 | -import { daquUniversalApproval, qyUniversalApproval,qyWorkorderCreate, | |
| 207 | - dcyUniversalApproval, daquWorkorderCreate, | |
| 208 | - dcyWorkorderCreate } from '@/api/regional-order-manage/regional-order-manage' | |
| 196 | +import { | |
| 197 | + daquUniversalApproval, qyUniversalApproval, qyWorkorderCreate, | |
| 198 | + dcyUniversalApproval, daquWorkorderCreate, dcyWorkorderCreate | |
| 199 | +} from '@/api/regional-order-manage/regional-order-manage' | |
| 209 | 200 | import { timeFormat } from '@/uni_modules/uview-plus' |
| 210 | 201 | import { nextStepMap } from '@/common/utils/common' |
| 211 | 202 | import { useUserStore } from '@/pinia/user'; |
| 212 | 203 | |
| 213 | -// ========== 状态管理 ========== | |
| 214 | -const userStore = useUserStore(); | |
| 204 | +// ========== 【常量配置区】- 硬编码集中管理,便于维护 ========== | |
| 205 | +const CONST = { | |
| 206 | + // 字典编码 | |
| 207 | + DICT_ORDER_NAME: 'work_name', | |
| 208 | + DICT_PRESSING_TYPE: 'workorder_pressing_type', | |
| 209 | + // 上传配置 | |
| 210 | + UPLOAD_CONFIG: { maxCount: 3, uploadText: '选择问题照片', sizeType: ['compressed'] }, | |
| 211 | + // 路由地址 | |
| 212 | + PAGE_ORDER_LIST: '/pages-sub/problem/regional-order-manage/index', | |
| 213 | + // 业务线映射 | |
| 214 | + BUSI_LINE_MAP: { yl: '园林', sz: '市政', wy: '物业', '园林': 'yl', '市政': 'sz', '物业': 'wy' }, | |
| 215 | + // 角色标识 | |
| 216 | + ROLE: { MANAGER: 'regional_manager', INSPECTOR: 'Inspector_global', PATROL: 'patrol_global' } | |
| 217 | +} | |
| 215 | 218 | |
| 216 | -// ========== 业务线相关状态 ========== | |
| 217 | -// 业务线映射表 | |
| 218 | -const busiLineMap = ref({ | |
| 219 | - 'yl': '园林', | |
| 220 | - 'sz': '市政', | |
| 221 | - 'wy': '物业', | |
| 222 | - '园林': 'yl', | |
| 223 | - '市政': 'sz', | |
| 224 | - '物业': 'wy' | |
| 225 | -}); | |
| 219 | +// ========== 【全局实例 & 基础状态】 ========== | |
| 220 | +const userStore = useUserStore(); | |
| 221 | +const USER_ROLES = userStore.userInfo?.roles || []; | |
| 222 | +const workOrderFormRef = ref(null); | |
| 223 | +const showActionSheet = ref(false); | |
| 224 | +const show = ref(false); | |
| 225 | +const expectedFinishDate = ref(Date.now()); | |
| 226 | +const isRenew = ref(false); | |
| 227 | +const renewOrderData = ref(null); | |
| 226 | 228 | |
| 227 | -// 业务线选项列表 | |
| 229 | +// ========== 【下拉弹窗数据】 ========== | |
| 230 | +const currentActionSheetData = reactive({ type: '', list: [], title: '' }); | |
| 231 | +const roadNameList = ref([]); | |
| 232 | +const orderNameList = ref([]); | |
| 233 | +const pressingTypeList = ref([]); | |
| 228 | 234 | const busiLineOptions = ref([]); |
| 229 | -const formatBusiLineOptions = () => { | |
| 230 | - if (!userStore.userInfo?.user?.busiLine) { | |
| 231 | - busiLineOptions.value = []; | |
| 232 | - return; | |
| 233 | - } | |
| 234 | - const rawBusiLines = userStore.userInfo.user.busiLine.split(','); | |
| 235 | - busiLineOptions.value = rawBusiLines.map(item => ({ | |
| 236 | - name: busiLineMap.value[item.trim()] | |
| 237 | - })); | |
| 238 | -}; | |
| 239 | - | |
| 240 | -// 工具方法:通过中文名称获取对应的英文标识 | |
| 241 | -const getBusiLineEnByCn = (cnName) => { | |
| 242 | - return busiLineMap.value[cnName] || ''; | |
| 243 | -}; | |
| 244 | 235 | |
| 245 | -// ========== 表单Ref ========== | |
| 246 | -const workOrderFormRef = ref(null) | |
| 247 | - | |
| 248 | -// ========== 公共上传逻辑复用 ========== | |
| 236 | +// ========== 【上传逻辑复用】 ========== | |
| 249 | 237 | const problemImgs = useUploadImgs({ |
| 250 | - maxCount: 3, | |
| 251 | - uploadText: '选择问题照片', | |
| 252 | - sizeType: ['compressed'], | |
| 238 | + ...CONST.UPLOAD_CONFIG, | |
| 253 | 239 | formRef: workOrderFormRef, |
| 254 | 240 | fieldName: 'problemImgs' |
| 255 | 241 | }) |
| 242 | +// 图片数组兜底,防止非数组报错 | |
| 243 | +if (!Array.isArray(problemImgs.rawImgList.value)) problemImgs.rawImgList.value = []; | |
| 256 | 244 | |
| 257 | -if (!Array.isArray(problemImgs.rawImgList.value)) { | |
| 258 | - problemImgs.rawImgList.value = []; | |
| 259 | -} | |
| 260 | - | |
| 261 | -// ========== 页面状态 ========== | |
| 262 | -const showActionSheet = ref(false) | |
| 263 | -const currentActionSheetData = reactive({ | |
| 264 | - type: '', | |
| 265 | - list: [], | |
| 266 | - title: '' | |
| 267 | -}) | |
| 268 | -const show = ref(false) | |
| 269 | -const expectedFinishDate = ref(Date.now()) | |
| 270 | - | |
| 271 | -// ========== 重新提交相关状态 ========== | |
| 272 | -const isRenew = ref(false); | |
| 273 | -const renewOrderData = ref(null); | |
| 274 | - | |
| 275 | -// ========== 下拉列表数据 ========== | |
| 276 | -const roadNameList = ref([]) | |
| 277 | -const orderNameList = ref([]) | |
| 278 | -const pressingTypeList = ref([]) | |
| 279 | - | |
| 280 | -// ========== 工单表单数据 ========== | |
| 245 | +// ========== 【工单表单数据】 ========== | |
| 281 | 246 | const workOrderForm = reactive({ |
| 282 | - busiLineCn: '', | |
| 283 | - roadId: 0, | |
| 284 | - roadName: '', | |
| 285 | - workLocation: '', | |
| 286 | - orderName: '', | |
| 287 | - pressingType: '', | |
| 288 | - pressingTypeName: '', | |
| 289 | - problemDesc: '', | |
| 290 | - lat: 0, | |
| 291 | - lon: 0, | |
| 292 | - expectedFinishDate: '', | |
| 247 | + busiLineCn: '', roadId: 0, roadName: '', workLocation: '', | |
| 248 | + orderName: '', pressingType: '', pressingTypeName: '', | |
| 249 | + problemDesc: '', lat: 0, lon: 0, expectedFinishDate: '' | |
| 293 | 250 | }) |
| 294 | 251 | |
| 295 | -// ========== 表单校验规则 ========== | |
| 252 | +// ========== 【表单校验规则】 ========== | |
| 296 | 253 | const workOrderFormRules = reactive({ |
| 297 | - busiLineCn: [ | |
| 298 | - { type: 'string', required: true, message: '请选择业务线', trigger: ['change', 'blur'] } | |
| 299 | - ], | |
| 300 | - workLocation: [ | |
| 301 | - { type: 'string', required: true, message: '请选择工单位置', trigger: ['change', 'blur'] } | |
| 302 | - ], | |
| 303 | - roadName: [ | |
| 304 | - { type: 'string', required: true, message: '请选择道路名称', trigger: ['change', 'blur'] } | |
| 305 | - ], | |
| 306 | - orderName: [ | |
| 307 | - { type: 'string', required: true, message: '请选择工单名称', trigger: ['change', 'blur'] } | |
| 308 | - ], | |
| 309 | - pressingTypeName: [ | |
| 310 | - { type: 'string', required: true, message: '请选择紧急程度', trigger: ['change'] } | |
| 311 | - ], | |
| 254 | + busiLineCn: [{ type: 'string', required: true, message: '请选择业务线', trigger: ['change', 'blur'] }], | |
| 255 | + workLocation: [{ type: 'string', required: true, message: '请选择工单位置', trigger: ['change', 'blur'] }], | |
| 256 | + roadName: [{ type: 'string', required: true, message: '请选择道路名称', trigger: ['change', 'blur'] }], | |
| 257 | + orderName: [{ type: 'string', required: true, message: '请选择工单名称', trigger: ['change', 'blur'] }], | |
| 258 | + pressingTypeName: [{ type: 'string', required: true, message: '请选择紧急程度', trigger: ['change'] }], | |
| 312 | 259 | problemDesc: [ |
| 313 | 260 | { type: 'string', required: true, message: '请输入情况描述', trigger: ['change', 'blur'] }, |
| 314 | 261 | { type: 'string', min: 3, max: 200, message: '情况描述需3-200字', trigger: ['change', 'blur'] } |
| ... | ... | @@ -316,268 +263,251 @@ const workOrderFormRules = reactive({ |
| 316 | 263 | problemImgs: [problemImgs.imgValidateRule] |
| 317 | 264 | }) |
| 318 | 265 | |
| 319 | -// ========== 生命周期 ========== | |
| 266 | +// ========== 【工具方法封装】- 抽离通用逻辑,消灭重复代码 ========== | |
| 267 | +/** | |
| 268 | + * 业务线中文转英文 | |
| 269 | + */ | |
| 270 | +const busiLineCnToEn = (cn) => CONST.BUSI_LINE_MAP[cn] || ''; | |
| 271 | +/** | |
| 272 | + * 业务线英文转中文 | |
| 273 | + */ | |
| 274 | +const busiLineEnToCn = (en) => CONST.BUSI_LINE_MAP[en] || ''; | |
| 275 | +/** | |
| 276 | + * 统一跳转返回工单列表页 | |
| 277 | + */ | |
| 278 | +const toOrderListPage = () => uni.reLaunch({ url: CONST.PAGE_ORDER_LIST }); | |
| 279 | +/** | |
| 280 | + * 格式化时间戳 - 增加空值兜底 | |
| 281 | + */ | |
| 282 | +const formatDate = (dateVal, fmt = 'yyyy-mm-dd hh:MM:ss') => dateVal ? timeFormat(dateVal, fmt) : ''; | |
| 283 | +/** | |
| 284 | + * 时间转时间戳 - 增加空值兜底 | |
| 285 | + */ | |
| 286 | +const dateToTimestamp = (dateStr) => dateStr ? new Date(dateStr).getTime() : ''; | |
| 287 | + | |
| 288 | +// ========== 【生命周期】 ========== | |
| 320 | 289 | onLoad((options) => { |
| 321 | - // 初始化业务线选项 | |
| 322 | - formatBusiLineOptions(); | |
| 290 | + // 初始化工单线选项 | |
| 291 | + initBusiLineOptions(); | |
| 323 | 292 | // 默认选中第一个业务线 |
| 324 | - if (busiLineOptions.value.length > 0) { | |
| 325 | - workOrderForm.busiLineCn = busiLineOptions.value[0].name; | |
| 326 | - } | |
| 327 | - | |
| 328 | - // 判断是否为重新提交状态 | |
| 329 | - if (options.isRenew == 1 && options.tempKey) { | |
| 330 | - isRenew.value = true; | |
| 331 | - const tempKey = options.tempKey; | |
| 332 | - | |
| 333 | - try { | |
| 334 | - const orderData = uni.getStorageSync(tempKey); | |
| 335 | - console.log(orderData) | |
| 336 | - if (orderData && typeof orderData === 'object') { | |
| 337 | - renewOrderData.value = orderData; | |
| 338 | - echoOrderData(renewOrderData.value); | |
| 339 | - } else { | |
| 340 | - uni.showToast({ title: '工单数据不存在,无法重新提交', icon: 'none' }); | |
| 341 | - setTimeout(() => uni.navigateBack(), 1000); | |
| 342 | - return; | |
| 343 | - } | |
| 344 | - } catch (error) { | |
| 345 | - console.error('读取工单数据失败:', error); | |
| 346 | - uni.showToast({ title: '数据读取异常,无法重新提交', icon: 'none' }); | |
| 347 | - setTimeout(() => uni.navigateBack(), 1000); | |
| 348 | - return; | |
| 349 | - } finally { | |
| 350 | - uni.removeStorageSync(tempKey); | |
| 351 | - } | |
| 352 | - } | |
| 293 | + workOrderForm.busiLineCn = busiLineOptions.value[0]?.name || ''; | |
| 294 | + // 处理重新提交逻辑 | |
| 295 | + if (options.isRenew == 1 && options.tempKey) handleRenewOptions(options.tempKey); | |
| 353 | 296 | }); |
| 354 | 297 | |
| 355 | 298 | onReady(() => { |
| 356 | - if (workOrderFormRef.value) { | |
| 357 | - workOrderFormRef.value.setRules(workOrderFormRules) | |
| 358 | - } | |
| 359 | - console.log('工单表单规则初始化完成') | |
| 360 | -}) | |
| 299 | + // 修复bug:nextTick确保表单ref一定挂载完成,再设置规则 | |
| 300 | + nextTick(() => { | |
| 301 | + workOrderFormRef.value?.setRules(workOrderFormRules); | |
| 302 | + }); | |
| 303 | +}); | |
| 361 | 304 | |
| 362 | 305 | onShow(() => { |
| 363 | - // 初始化工单名称列表 | |
| 364 | - orderNameList.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('work_name')) | |
| 365 | - // 初始化紧急程度列表 | |
| 366 | - pressingTypeList.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('workorder_pressing_type')) | |
| 367 | -}) | |
| 306 | + // 初始化下拉字典数据 | |
| 307 | + orderNameList.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList(CONST.DICT_ORDER_NAME)); | |
| 308 | + pressingTypeList.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList(CONST.DICT_PRESSING_TYPE)); | |
| 309 | +}); | |
| 368 | 310 | |
| 369 | -// ========== 核心方法 ========== | |
| 370 | -const echoOrderData = (orderItem) => { | |
| 371 | - // 回显业务线 | |
| 372 | - if (orderItem.busiLine) { | |
| 373 | - workOrderForm.busiLineCn = busiLineMap.value[orderItem.busiLine]; | |
| 311 | +// ========== 【核心业务方法】 ========== | |
| 312 | +/** | |
| 313 | + * 初始化工单线选项 | |
| 314 | + */ | |
| 315 | +const initBusiLineOptions = () => { | |
| 316 | + const rawBusiLines = userStore.userInfo?.user?.busiLine?.split(',') || []; | |
| 317 | + busiLineOptions.value = rawBusiLines.map(item => ({ name: busiLineEnToCn(item.trim()) })); | |
| 318 | +}; | |
| 319 | + | |
| 320 | +/** | |
| 321 | + * 处理重新提交的参数和数据回显 | |
| 322 | + */ | |
| 323 | +const handleRenewOptions = async (tempKey) => { | |
| 324 | + isRenew.value = true; | |
| 325 | + try { | |
| 326 | + const orderData = uni.getStorageSync(tempKey); | |
| 327 | + if (orderData && typeof orderData === 'object') { | |
| 328 | + renewOrderData.value = orderData; | |
| 329 | + echoOrderData(orderData); | |
| 330 | + } else { | |
| 331 | + uni.showToast({ title: '工单数据不存在,无法重新提交', icon: 'none' }); | |
| 332 | + setTimeout(toOrderListPage, 1000); | |
| 333 | + } | |
| 334 | + } catch (error) { | |
| 335 | + console.error('读取工单数据失败:', error); | |
| 336 | + uni.showToast({ title: '数据读取异常,无法重新提交', icon: 'none' }); | |
| 337 | + setTimeout(toOrderListPage, 1000); | |
| 338 | + } finally { | |
| 339 | + uni.removeStorageSync(tempKey); | |
| 374 | 340 | } |
| 341 | +}; | |
| 375 | 342 | |
| 376 | - // 回显基础字段 | |
| 343 | +/** | |
| 344 | + * 工单数据回显 | |
| 345 | + */ | |
| 346 | +const echoOrderData = (orderItem) => { | |
| 347 | + // 基础字段回显 | |
| 348 | + console.log(orderItem) | |
| 349 | + console.log('123') | |
| 350 | + workOrderForm.busiLineCn = busiLineEnToCn(orderItem.busiLine); | |
| 377 | 351 | workOrderForm.roadId = orderItem.roadId || 0; |
| 378 | 352 | workOrderForm.roadName = orderItem.roadName || ''; |
| 379 | 353 | workOrderForm.workLocation = orderItem.lonLatAddress || orderItem.roadName || ''; |
| 380 | 354 | workOrderForm.orderName = orderItem.orderName || ''; |
| 381 | 355 | workOrderForm.pressingType = orderItem.pressingType || ''; |
| 382 | - workOrderForm.pressingTypeName = uni.$dict.getDictLabel('workorder_pressing_type', orderItem.pressingType) || ''; | |
| 356 | + workOrderForm.pressingTypeName = uni.$dict.getDictLabel(CONST.DICT_PRESSING_TYPE, orderItem.pressingType) || ''; | |
| 383 | 357 | workOrderForm.problemDesc = orderItem.remark || ''; |
| 384 | 358 | workOrderForm.lat = orderItem.lat || 0; |
| 385 | 359 | workOrderForm.lon = orderItem.lon || 0; |
| 386 | - workOrderForm.expectedFinishDate = timeFormat(orderItem.expectedFinishDate, 'yyyy-mm-dd hh:MM:ss') || ''; | |
| 387 | - | |
| 388 | - // 回显图片 | |
| 389 | - if (orderItem.problemsImgs && Array.isArray(orderItem.problemsImgs) && orderItem.problemsImgs.length > 0) { | |
| 390 | - const imgList = orderItem.problemsImgs.map((imgUrl, index) => ({ | |
| 391 | - url: imgUrl, | |
| 392 | - name: `renew_img_${index}`, | |
| 393 | - status: 'success' | |
| 394 | - })); | |
| 360 | + workOrderForm.expectedFinishDate = orderItem.expectedFinishDate?"":formatDate(orderItem.expectedFinishDate); | |
| 361 | + | |
| 362 | + // 图片回显 - 兼容异常格式 | |
| 363 | + if (Array.isArray(orderItem.problemsImgs) && orderItem.problemsImgs.length) { | |
| 364 | + const imgList = orderItem.problemsImgs.map((url, idx) => ({ url, name: `renew_img_${idx}`, status: 'success' })); | |
| 395 | 365 | problemImgs.imgList.value = imgList; |
| 396 | 366 | problemImgs.rawImgList.value = imgList; |
| 397 | 367 | } |
| 398 | 368 | |
| 399 | 369 | // 自动获取道路列表 |
| 400 | - if (orderItem.lat && orderItem.lon) { | |
| 401 | - getRoadListByBusiLine(); | |
| 402 | - } | |
| 370 | + if (orderItem.lat && orderItem.lon) getRoadListByBusiLine(); | |
| 403 | 371 | }; |
| 404 | 372 | |
| 405 | -// 业务线切换事件 | |
| 373 | +/** | |
| 374 | + * 业务线切换事件 | |
| 375 | + */ | |
| 406 | 376 | const handleBusiLineChange = () => { |
| 407 | 377 | workOrderForm.roadName = ''; |
| 408 | 378 | workOrderForm.roadId = 0; |
| 409 | 379 | roadNameList.value = []; |
| 410 | - if (workOrderForm.workLocation) { | |
| 411 | - getRoadListByBusiLine(); | |
| 412 | - } | |
| 380 | + workOrderForm.workLocation && getRoadListByBusiLine(); | |
| 413 | 381 | }; |
| 414 | 382 | |
| 415 | -// 获取道路列表(带业务线) | |
| 383 | +/** | |
| 384 | + * 根据坐标+业务线获取道路列表 | |
| 385 | + */ | |
| 416 | 386 | const getRoadListByBusiLine = async () => { |
| 417 | - if (!workOrderForm.lat || !workOrderForm.lon) { | |
| 418 | - return; | |
| 419 | - } | |
| 420 | - const busiLineEn = getBusiLineEnByCn(workOrderForm.busiLineCn); | |
| 421 | - if (!busiLineEn) { | |
| 422 | - uni.showToast({ title: '业务线标识异常', icon: 'none' }); | |
| 423 | - return; | |
| 424 | - } | |
| 387 | + if (!workOrderForm.lat || !workOrderForm.lon) return; | |
| 388 | + const busiLineEn = busiLineCnToEn(workOrderForm.busiLineCn); | |
| 389 | + if (!busiLineEn) return uni.showToast({ title: '业务线选择异常', icon: 'none' }); | |
| 425 | 390 | |
| 426 | 391 | try { |
| 427 | 392 | uni.showLoading({ title: '获取道路名称中...' }); |
| 428 | - const roadRes = await getRoadListByLatLng({ | |
| 429 | - busiLine: busiLineEn, | |
| 430 | - latitude: workOrderForm.lat, | |
| 431 | - longitude: workOrderForm.lon | |
| 432 | - }); | |
| 433 | - uni.hideLoading(); | |
| 434 | - if (Array.isArray(roadRes)) { | |
| 435 | - roadNameList.value = roadRes.map((item) => ({ | |
| 436 | - name: item.roadName || '', | |
| 437 | - code: item.roadCode || '', | |
| 438 | - id: item.roadId || 0 | |
| 439 | - })); | |
| 440 | - } else { | |
| 441 | - roadNameList.value = [{ name: '未查询到道路名称', code: '', id: 0 }]; | |
| 442 | - uni.showToast({ title: '未查询到该位置的道路信息', icon: 'none' }); | |
| 443 | - } | |
| 393 | + const roadRes = await getRoadListByLatLng({ busiLine: busiLineEn, latitude: workOrderForm.lat, longitude: workOrderForm.lon }); | |
| 394 | + console.log(roadRes) | |
| 395 | + roadNameList.value = Array.isArray(roadRes) | |
| 396 | + ? roadRes.map(item => ({ name: item.roadName || '', code: item.roadCode || '', id: item.roadId || 0 })) | |
| 397 | + : [{ name: '未查询到道路名称', code: '', id: 0 }]; | |
| 398 | + console.log(roadNameList) | |
| 399 | + !Array.isArray(roadRes) && uni.showToast({ title: '未查询到该位置的道路信息', icon: 'none' }); | |
| 444 | 400 | } catch (err) { |
| 445 | - uni.hideLoading(); | |
| 446 | 401 | console.error('获取道路名称失败:', err); |
| 447 | - uni.showToast({ title: '获取道路名称失败,请重试', icon: 'none' }); | |
| 448 | 402 | roadNameList.value = [{ name: '获取失败,请重新选择位置', code: '', id: 0 }]; |
| 403 | + uni.showToast({ title: '获取道路名称失败,请重试', icon: 'none' }); | |
| 404 | + } finally { | |
| 405 | + uni.hideLoading(); | |
| 449 | 406 | } |
| 450 | 407 | }; |
| 451 | 408 | |
| 452 | -// ========== 通用弹窗方法 ========== | |
| 409 | +// ========== 【通用下拉弹窗方法】 ========== | |
| 453 | 410 | const handleActionSheetOpen = (type) => { |
| 454 | 411 | if (type === 'roadName' && !workOrderForm.workLocation) { |
| 455 | - uni.showToast({ title: '请先选择工单位置', icon: 'none' }) | |
| 456 | - return | |
| 412 | + return uni.showToast({ title: '请先选择工单位置', icon: 'none' }); | |
| 457 | 413 | } |
| 458 | - | |
| 459 | 414 | const configMap = { |
| 460 | - roadName: { | |
| 461 | - title: '请选择道路名称', | |
| 462 | - list: roadNameList.value | |
| 463 | - }, | |
| 464 | - orderName: { | |
| 465 | - title: '请选择工单名称', | |
| 466 | - list: orderNameList.value | |
| 467 | - }, | |
| 468 | - pressingType: { | |
| 469 | - title: '请选择紧急程度', | |
| 470 | - list: pressingTypeList.value | |
| 471 | - } | |
| 472 | - } | |
| 473 | - | |
| 474 | - currentActionSheetData.type = type | |
| 475 | - currentActionSheetData.title = configMap[type].title | |
| 476 | - currentActionSheetData.list = configMap[type].list | |
| 477 | - showActionSheet.value = true | |
| 478 | -} | |
| 415 | + roadName: { title: '请选择道路名称', list: roadNameList.value }, | |
| 416 | + orderName: { title: '请选择工单名称', list: orderNameList.value }, | |
| 417 | + pressingType: { title: '请选择紧急程度', list: pressingTypeList.value } | |
| 418 | + }; | |
| 419 | + Object.assign(currentActionSheetData, configMap[type], { type }); | |
| 420 | + showActionSheet.value = true; | |
| 421 | +}; | |
| 479 | 422 | |
| 480 | 423 | const handleActionSheetClose = () => { |
| 481 | - showActionSheet.value = false | |
| 482 | - currentActionSheetData.type = '' | |
| 483 | - currentActionSheetData.list = [] | |
| 484 | - currentActionSheetData.title = '' | |
| 485 | -} | |
| 424 | + showActionSheet.value = false; | |
| 425 | + Object.assign(currentActionSheetData, { type: '', list: [], title: '' }); | |
| 426 | +}; | |
| 486 | 427 | |
| 487 | 428 | const handleActionSheetSelect = (e) => { |
| 488 | - const { type } = currentActionSheetData | |
| 429 | + const { type } = currentActionSheetData; | |
| 489 | 430 | switch (type) { |
| 490 | 431 | case 'roadName': |
| 491 | - workOrderForm.roadName = e.name | |
| 492 | - workOrderForm.roadId = e.code | |
| 493 | - workOrderFormRef.value?.validateField('roadName') | |
| 494 | - break | |
| 432 | + workOrderForm.roadName = e.name; | |
| 433 | + workOrderForm.roadId = e.code; | |
| 434 | + break; | |
| 495 | 435 | case 'orderName': |
| 496 | - workOrderForm.orderName = e.name | |
| 497 | - workOrderFormRef.value?.validateField('orderName') | |
| 498 | - break | |
| 436 | + workOrderForm.orderName = e.name; | |
| 437 | + break; | |
| 499 | 438 | case 'pressingType': |
| 500 | - workOrderForm.pressingType = e.value | |
| 501 | - workOrderForm.pressingTypeName = e.name | |
| 502 | - workOrderFormRef.value?.validateField('pressingTypeName') | |
| 503 | - break | |
| 439 | + workOrderForm.pressingType = e.value; | |
| 440 | + workOrderForm.pressingTypeName = e.name; | |
| 441 | + break; | |
| 504 | 442 | } |
| 505 | - showActionSheet.value = false | |
| 506 | -} | |
| 507 | - | |
| 508 | -const navigateBack = () => { | |
| 509 | - uni.reLaunch({ | |
| 510 | - url: '/pages-sub/problem/work-order-manage/index', | |
| 511 | - fail: () => { | |
| 512 | - uni.navigateBack({ delta: 2 }); | |
| 513 | - } | |
| 514 | - }); | |
| 515 | -} | |
| 443 | + workOrderFormRef.value?.validateField(type === 'roadName' ? 'roadName' : type === 'orderName' ? 'orderName' : 'pressingTypeName'); | |
| 444 | + handleActionSheetClose(); | |
| 445 | +}; | |
| 516 | 446 | |
| 517 | -// 清除希望完成时间 | |
| 518 | -const clearExpectedFinishDate = ()=> { | |
| 519 | - workOrderForm.expectedFinishDate = '' | |
| 520 | -} | |
| 447 | +// ========== 【页面交互方法】 ========== | |
| 448 | +/** 清除希望完成时间 */ | |
| 449 | +const clearExpectedFinishDate = () => workOrderForm.expectedFinishDate = ''; | |
| 450 | +/** 隐藏软键盘 */ | |
| 451 | +const hideKeyboard = () => uni.hideKeyboard(); | |
| 452 | +/** 时间选择确认 */ | |
| 453 | +const expectedFinishDateConfirm = (e) => { | |
| 454 | + workOrderForm.expectedFinishDate = formatDate(e.value); | |
| 455 | + show.value = false; | |
| 456 | +}; | |
| 521 | 457 | |
| 522 | -// 选择工单位置 | |
| 458 | +/** 选择工单位置 */ | |
| 523 | 459 | const chooseWorkLocation = () => { |
| 524 | 460 | uni.chooseLocation({ |
| 525 | 461 | success: async (res) => { |
| 526 | - workOrderForm.roadName = '' | |
| 527 | - workOrderForm.roadId = 0 | |
| 528 | - roadNameList.value = [] | |
| 529 | - | |
| 530 | - workOrderForm.workLocation = res.name | |
| 531 | - workOrderForm.lat = res.latitude | |
| 532 | - workOrderForm.lon = res.longitude | |
| 533 | - | |
| 534 | - workOrderFormRef.value?.validateField('workLocation') | |
| 535 | - workOrderFormRef.value?.validateField('roadName') | |
| 536 | - | |
| 462 | + workOrderForm.roadName = ''; | |
| 463 | + workOrderForm.roadId = 0; | |
| 464 | + roadNameList.value = []; | |
| 465 | + workOrderForm.workLocation = res.name; | |
| 466 | + workOrderForm.lat = res.latitude; | |
| 467 | + workOrderForm.lon = res.longitude; | |
| 468 | + workOrderFormRef.value?.validateField(['workLocation', 'roadName']); | |
| 537 | 469 | await getRoadListByBusiLine(); |
| 538 | 470 | }, |
| 539 | 471 | fail: (err) => { |
| 540 | - console.error('选择位置失败:', err) | |
| 541 | - uni.showToast({ title: '选择位置失败:' + err.errMsg, icon: 'none' }) | |
| 472 | + console.error('选择位置失败:', err); | |
| 473 | + const errMsg = err.errMsg.includes('auth deny') ? '请开启位置权限后重试' : '选择位置失败,请重试'; | |
| 474 | + uni.showToast({ title: errMsg, icon: 'none' }); | |
| 542 | 475 | } |
| 543 | 476 | }) |
| 544 | -} | |
| 545 | - | |
| 546 | -// 完成时间确认 | |
| 547 | -const expectedFinishDateConfirm = (e) => { | |
| 548 | - workOrderForm.expectedFinishDate = timeFormat(e.value, 'yyyy-mm-dd hh:MM:ss') | |
| 549 | - show.value = false | |
| 550 | -} | |
| 477 | +}; | |
| 478 | +// 新增页返回直接回列表页 | |
| 479 | +onBackPress(() => { | |
| 480 | + uni.redirectTo({ | |
| 481 | + url: '/pages-sub/problem/regional-order-manage/index' | |
| 482 | + }) | |
| 483 | + return true; | |
| 484 | +}) | |
| 551 | 485 | |
| 552 | -// 隐藏键盘 | |
| 553 | -const hideKeyboard = () => { | |
| 554 | - uni.hideKeyboard() | |
| 555 | -} | |
| 486 | +// ========== 【封装:统一接口调用方法】 ========== | |
| 487 | +const callApiByRole = async (params, isRenewFlag) => { | |
| 488 | + if (isRenewFlag) { | |
| 489 | + if (USER_ROLES.includes(CONST.ROLE.MANAGER)) return await daquUniversalApproval(params); | |
| 490 | + if (USER_ROLES.includes(CONST.ROLE.INSPECTOR)) return await dcyUniversalApproval(params); | |
| 491 | + if (USER_ROLES.includes(CONST.ROLE.PATROL)) return await qyUniversalApproval(params); | |
| 492 | + } else { | |
| 493 | + if (USER_ROLES.includes(CONST.ROLE.MANAGER)) return await daquWorkorderCreate(params); | |
| 494 | + if (USER_ROLES.includes(CONST.ROLE.INSPECTOR)) return await dcyWorkorderCreate(params); | |
| 495 | + if (USER_ROLES.includes(CONST.ROLE.PATROL)) return await qyWorkorderCreate(params); | |
| 496 | + } | |
| 497 | +}; | |
| 556 | 498 | |
| 557 | -// 提交工单 | |
| 499 | +// ========== 【核心:提交工单】 ========== | |
| 558 | 500 | const submitWorkOrder = async () => { |
| 559 | 501 | try { |
| 560 | - await workOrderFormRef.value.validate() | |
| 561 | - | |
| 562 | - const busiLineEn = getBusiLineEnByCn(workOrderForm.busiLineCn); | |
| 563 | - if (!busiLineEn) { | |
| 564 | - uni.showToast({ title: '业务线选择异常,请重新选择', icon: 'none' }); | |
| 565 | - return; | |
| 566 | - } | |
| 567 | - | |
| 568 | - const commonSubmitData = { | |
| 569 | - // roadId: workOrderForm.roadId, | |
| 570 | - // roadName: workOrderForm.roadName, | |
| 571 | - | |
| 572 | - // roadId: 550, | |
| 573 | - // roadName:'东明胡同(三海)', | |
| 574 | - | |
| 575 | - roadId: 551, | |
| 576 | - roadName:'地安门西大街51号', | |
| 577 | - // | |
| 578 | - // roadId: 553, | |
| 579 | - // roadName:'什刹海前海南沿', | |
| 580 | - | |
| 502 | + // 表单校验 | |
| 503 | + await workOrderFormRef.value.validate(); | |
| 504 | + const busiLineEn = busiLineCnToEn(workOrderForm.busiLineCn); | |
| 505 | + if (!busiLineEn) return uni.showToast({ title: '业务线选择异常,请重新选择', icon: 'none' }); | |
| 506 | + | |
| 507 | + // 组装公共提交参数 | |
| 508 | + const baseParams = { | |
| 509 | + roadId: workOrderForm.roadId, | |
| 510 | + roadName: workOrderForm.roadName, | |
| 581 | 511 | problemsImgs: problemImgs.getSuccessImgUrls(), |
| 582 | 512 | remark: workOrderForm.problemDesc.trim(), |
| 583 | 513 | latLonType: 2, |
| ... | ... | @@ -586,77 +516,37 @@ const submitWorkOrder = async () => { |
| 586 | 516 | lonLatAddress: workOrderForm.workLocation, |
| 587 | 517 | pressingType: workOrderForm.pressingType, |
| 588 | 518 | orderName: workOrderForm.orderName, |
| 589 | - expectedFinishDate: workOrderForm.expectedFinishDate==""?"":new Date(workOrderForm.expectedFinishDate).getTime(), | |
| 519 | + expectedFinishDate: dateToTimestamp(workOrderForm.expectedFinishDate), | |
| 590 | 520 | sourceId: 1, |
| 591 | 521 | sourceName: workOrderForm.busiLineCn, |
| 592 | 522 | busiLine: busiLineEn |
| 593 | - } | |
| 594 | - | |
| 595 | - uni.showLoading({ title: '提交中...' }) | |
| 596 | - let res | |
| 597 | - | |
| 598 | - if (isRenew.value) { | |
| 599 | - const renewSubmitData = { | |
| 600 | - workerDataId: renewOrderData.value.id, | |
| 601 | - taskKey: renewOrderData.value.taskKey, | |
| 602 | - taskId: renewOrderData.value.taskId, | |
| 603 | - operateType: nextStepMap[renewOrderData.value.taskKey]?.operateTypeRenew || '', | |
| 604 | - agree: 0, | |
| 605 | - reason: workOrderForm.problemDesc.trim(), | |
| 606 | - ...commonSubmitData | |
| 607 | - } | |
| 608 | - | |
| 609 | - if( userStore.userInfo.roles.includes('regional_manager')){ // 大区经理 | |
| 610 | - res = await daquUniversalApproval(renewSubmitData) | |
| 611 | - } | |
| 612 | - | |
| 613 | - if( userStore.userInfo.roles.includes('Inspector_global')){ // 督察员 | |
| 614 | - res = await dcyUniversalApproval(renewSubmitData) | |
| 615 | - } | |
| 616 | - | |
| 617 | - if( userStore.userInfo.roles.includes('patrol_global')){ // 全域巡查员 | |
| 618 | - res = await qyUniversalApproval(renewSubmitData) | |
| 619 | - } | |
| 620 | - | |
| 621 | - } else { | |
| 622 | - | |
| 623 | - if( userStore.userInfo.roles.includes('regional_manager')){ // 大区经理 | |
| 624 | - res = await daquWorkorderCreate(commonSubmitData) | |
| 625 | - } | |
| 626 | - | |
| 627 | - if( userStore.userInfo.roles.includes('Inspector_global')){ // 督察员 | |
| 628 | - res = await dcyWorkorderCreate(commonSubmitData) | |
| 629 | - } | |
| 630 | - | |
| 631 | - if( userStore.userInfo.roles.includes('patrol_global')){ // 全域巡查员 | |
| 632 | - res = await qyWorkorderCreate(commonSubmitData) | |
| 633 | - } | |
| 634 | - | |
| 635 | - | |
| 636 | - } | |
| 637 | - | |
| 638 | - uni.hideLoading() | |
| 639 | - uni.showToast({ | |
| 640 | - title: isRenew.value ? '重新提交成功' : '工单提交成功', | |
| 641 | - icon: 'success', | |
| 642 | - duration: 1000 | |
| 643 | - }) | |
| 644 | - | |
| 645 | - setTimeout(() => { | |
| 646 | - uni.reLaunch({ | |
| 647 | - url: '/pages-sub/problem/regional-order-manage/index' | |
| 648 | - }) | |
| 649 | - }, 1000) | |
| 523 | + }; | |
| 524 | + | |
| 525 | + uni.showLoading({ title: '提交中...', mask: true }); | |
| 526 | + // 区分:重新提交 / 新增工单 | |
| 527 | + const submitParams = isRenew.value | |
| 528 | + ? { | |
| 529 | + workerDataId: renewOrderData.value.id, | |
| 530 | + taskKey: renewOrderData.value.taskKey, | |
| 531 | + taskId: renewOrderData.value.taskId, | |
| 532 | + operateType: nextStepMap[renewOrderData.value.taskKey]?.operateTypeRenew || '', | |
| 533 | + agree: 0, | |
| 534 | + reason: workOrderForm.problemDesc.trim(), | |
| 535 | + ...baseParams | |
| 536 | + } | |
| 537 | + : baseParams; | |
| 538 | + | |
| 539 | + // 统一调用接口 | |
| 540 | + await callApiByRole(submitParams, isRenew.value); | |
| 541 | + | |
| 542 | + // 提交成功处理 | |
| 543 | + uni.showToast({ title: isRenew.value ? '重新提交成功' : '工单提交成功', icon: 'success', duration: 1000 }); | |
| 544 | + setTimeout(toOrderListPage, 1000); | |
| 650 | 545 | } catch (error) { |
| 651 | - uni.hideLoading() | |
| 652 | - | |
| 546 | + uni.hideLoading(); | |
| 653 | 547 | if (!Array.isArray(error)) { |
| 654 | - console.error(isRenew.value ? '工单重新提交失败:' : '工单提交失败:', error) | |
| 655 | - uni.showToast({ | |
| 656 | - title: isRenew.value ? error.msg : error.msg, | |
| 657 | - icon: 'none', | |
| 658 | - duration: 2000 | |
| 659 | - }) | |
| 548 | + console.error(isRenew.value ? '工单重新提交失败:' : '工单提交失败:', error); | |
| 549 | + uni.showToast({ title: error.msg || '提交失败,请重试', icon: 'none', duration: 2000 }); | |
| 660 | 550 | } |
| 661 | 551 | } |
| 662 | 552 | } |
| ... | ... | @@ -667,6 +557,7 @@ const submitWorkOrder = async () => { |
| 667 | 557 | .page-container { |
| 668 | 558 | min-height: 100vh; |
| 669 | 559 | padding-bottom: 100rpx; // 给底部按钮留空间 |
| 560 | + background: #fafafa; | |
| 670 | 561 | } |
| 671 | 562 | |
| 672 | 563 | // 工单表单内容容器 |
| ... | ... | @@ -674,19 +565,18 @@ const submitWorkOrder = async () => { |
| 674 | 565 | background: #fff; |
| 675 | 566 | } |
| 676 | 567 | |
| 677 | -// 派单情况分组样式(核心:增加顶部间距) | |
| 568 | +// 派单情况分组样式 | |
| 678 | 569 | .dispatch-group { |
| 679 | - | |
| 680 | - padding-top: 20rpx; // 分组内顶部内边距,增强视觉区分 | |
| 681 | - | |
| 682 | -} | |
| 683 | - | |
| 684 | -// 派单情况标题样式 | |
| 685 | -.dispatch-title { | |
| 686 | - font-size: 32rpx; | |
| 687 | - font-weight: 600; | |
| 688 | - color:$u-primary; | |
| 689 | - margin-bottom: 20rpx; | |
| 690 | - padding-left: 10rpx; | |
| 570 | + padding-top: 24rpx; | |
| 571 | + margin-top: 8rpx; | |
| 572 | + border-top: 1px solid #f5f5f5; | |
| 573 | + | |
| 574 | + .dispatch-title { | |
| 575 | + font-size: 32rpx; | |
| 576 | + font-weight: 600; | |
| 577 | + color: $u-primary; | |
| 578 | + margin-bottom: 20rpx; | |
| 579 | + padding-left: 10rpx; | |
| 580 | + } | |
| 691 | 581 | } |
| 692 | 582 | </style> |
| 693 | 583 | \ No newline at end of file | ... | ... |
pages-sub/problem/regional-order-manage/add-patrol-order.vue
| ... | ... | @@ -101,7 +101,7 @@ |
| 101 | 101 | |
| 102 | 102 | <script setup> |
| 103 | 103 | import { ref, reactive } from 'vue' |
| 104 | -import { onReady, onShow, onLoad } from '@dcloudio/uni-app'; | |
| 104 | +import { onReady, onShow, onLoad, onBackPress } from '@dcloudio/uni-app'; | |
| 105 | 105 | import { useUploadImgs } from '@/common/utils/useUploadImgs' |
| 106 | 106 | import { qyUniversalApproval, qyWorkorderCreate } from '@/api/regional-order-manage/regional-order-manage' |
| 107 | 107 | import { timeFormat } from '@/uni_modules/uview-plus' |
| ... | ... | @@ -237,6 +237,13 @@ onShow(() => { |
| 237 | 237 | // 初始化工单名称列表 |
| 238 | 238 | orderNameList.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('work_name')) |
| 239 | 239 | }) |
| 240 | +// 新增页返回直接回列表页 | |
| 241 | +onBackPress(() => { | |
| 242 | + uni.redirectTo({ | |
| 243 | + url: '/pages-sub/problem/regional-order-manage/index' | |
| 244 | + }) | |
| 245 | + return true; | |
| 246 | +}) | |
| 240 | 247 | |
| 241 | 248 | // ========== 核心方法 ========== |
| 242 | 249 | const echoOrderData = (orderItem) => { | ... | ... |
pages-sub/problem/regional-order-manage/distribution-order.vue
| ... | ... | @@ -194,7 +194,7 @@ |
| 194 | 194 | |
| 195 | 195 | <script setup> |
| 196 | 196 | import { ref, reactive } from 'vue' |
| 197 | -import { onReady, onShow, onLoad } from '@dcloudio/uni-app'; | |
| 197 | +import { onReady, onShow, onLoad, onBackPress } from '@dcloudio/uni-app'; | |
| 198 | 198 | import { useUploadImgs } from '@/common/utils/useUploadImgs' |
| 199 | 199 | import { getRoadListByLatLng } from '@/api/common' |
| 200 | 200 | import { daquUniversalApproval, daquWorkorderCreate, qyUniversalApproval } from '@/api/regional-order-manage/regional-order-manage' |
| ... | ... | @@ -357,6 +357,14 @@ onShow(() => { |
| 357 | 357 | pressingTypeList.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('workorder_pressing_type')) |
| 358 | 358 | }) |
| 359 | 359 | |
| 360 | +// 新增页返回直接回列表页 | |
| 361 | +onBackPress(() => { | |
| 362 | + uni.redirectTo({ | |
| 363 | + url: '/pages-sub/problem/regional-order-manage/index' | |
| 364 | + }) | |
| 365 | + return true; | |
| 366 | +}) | |
| 367 | + | |
| 360 | 368 | // ========== 核心方法 ========== |
| 361 | 369 | const echoOrderData = (orderItem) => { |
| 362 | 370 | // 回显业务线 |
| ... | ... | @@ -374,7 +382,7 @@ const echoOrderData = (orderItem) => { |
| 374 | 382 | workOrderForm.problemDesc = orderItem.remark || ''; |
| 375 | 383 | workOrderForm.lat = orderItem.lat || 0; |
| 376 | 384 | workOrderForm.lon = orderItem.lon || 0; |
| 377 | - workOrderForm.expectedFinishDate = orderItem.expectedFinishDate=='' ? '':timeFormat(orderItem.expectedFinishDate, 'yyyy-mm-dd hh:MM:ss') ; | |
| 385 | + workOrderForm.expectedFinishDate = orderItem.expectedFinishDate? timeFormat(orderItem.expectedFinishDate, 'yyyy-mm-dd hh:MM:ss'):'' ; | |
| 378 | 386 | |
| 379 | 387 | // 回显图片【核心:问题照片赋值】 |
| 380 | 388 | if (orderItem.problemsImgs && Array.isArray(orderItem.problemsImgs) && orderItem.problemsImgs.length > 0) { | ... | ... |
pages-sub/problem/regional-order-manage/index.vue
| ... | ... | @@ -3,7 +3,6 @@ |
| 3 | 3 | <!-- 顶部固定区域 --> |
| 4 | 4 | <up-sticky> |
| 5 | 5 | <view class="header-wrap"> |
| 6 | - <!-- 第一行:u-tabs 待办/已办切换 :scrollable="false"--> | |
| 7 | 6 | <up-tabs |
| 8 | 7 | v-model="activeTab" |
| 9 | 8 | :list="tabList" |
| ... | ... | @@ -12,10 +11,8 @@ |
| 12 | 11 | font-size="30rpx" |
| 13 | 12 | @click="handleTabChange" |
| 14 | 13 | /> |
| 15 | - | |
| 16 | 14 | <!-- 第二行:下拉框 + 搜索框 --> |
| 17 | 15 | <view class="search-header"> |
| 18 | - <!-- 左侧下拉框 --> | |
| 19 | 16 | <view class="select-wrap common-text-color"> |
| 20 | 17 | <up-select |
| 21 | 18 | v-model:current="selectedSortValue" |
| ... | ... | @@ -26,8 +23,6 @@ |
| 26 | 23 | :style="{ flex: 1 }" |
| 27 | 24 | /> |
| 28 | 25 | </view> |
| 29 | - | |
| 30 | - <!-- 右侧搜索框 --> | |
| 31 | 26 | <view class="search-input-wrap"> |
| 32 | 27 | <up-search |
| 33 | 28 | v-model="searchValue" |
| ... | ... | @@ -47,24 +42,23 @@ |
| 47 | 42 | |
| 48 | 43 | <!-- 列表容器 --> |
| 49 | 44 | <z-paging |
| 50 | - ref="paging" | |
| 45 | + ref="pagingRef" | |
| 51 | 46 | v-model="orderList" |
| 52 | 47 | @query="queryList" |
| 53 | 48 | :auto-show-system-loading="true" |
| 54 | - | |
| 55 | 49 | > |
| 56 | 50 | <template #empty> |
| 57 | - <empty-view/> | |
| 51 | + <empty-view /> | |
| 58 | 52 | </template> |
| 59 | 53 | |
| 60 | 54 | <view class="common-card-list" style="padding-top: 200rpx;padding-bottom: 30rpx"> |
| 61 | 55 | <!-- 待办工单卡片 --> |
| 62 | 56 | <up-card |
| 63 | - v-if="activeTab == 0" | |
| 57 | + v-if="activeTab === 0" | |
| 64 | 58 | :border="false" |
| 65 | 59 | :foot-border-top="false" |
| 66 | 60 | v-for="(item, index) in orderList" |
| 67 | - :key="`todo_${item.orderNo}_${index}`" | |
| 61 | + :key="`todo_${item.id}_${index}`" | |
| 68 | 62 | :show-head="false" |
| 69 | 63 | class="order-card" |
| 70 | 64 | > |
| ... | ... | @@ -86,7 +80,7 @@ |
| 86 | 80 | <view class="u-body-item-title">情况描述:</view> |
| 87 | 81 | <view class="u-line-1 u-body-value">{{ item.remark || '无' }}</view> |
| 88 | 82 | </view> |
| 89 | - <view class="u-body-item u-flex common-item-center common-justify-between"> | |
| 83 | + <view class="u-body-item u-flex common-item-center common-justify-between"> | |
| 90 | 84 | <view class="u-body-item-title">紧急程度:</view> |
| 91 | 85 | <view class="u-line-1 u-body-value"> |
| 92 | 86 | {{ uni.$dict.getDictLabel('workorder_pressing_type', item.pressingType) }} |
| ... | ... | @@ -101,20 +95,10 @@ |
| 101 | 95 | <view class="u-line-1 u-body-value">{{ timeFormat(item.createTime, 'yyyy-mm-dd hh:MM:ss') }}</view> |
| 102 | 96 | </view> |
| 103 | 97 | <!-- 操作按钮行 --> |
| 104 | - | |
| 105 | 98 | <view class="u-body-item u-flex common-justify-between common-item-center mt-20"> |
| 106 | - <up-button type="warning" size="mini" @click="handleReject(item)" | |
| 107 | - v-show="nextStepMap[item.taskKey].backShow">回退 | |
| 108 | - </up-button> | |
| 109 | - | |
| 110 | - <up-button type="success" size="mini" @click="handleRenew(item)" | |
| 111 | - v-show="nextStepMap[item.taskKey].renewShow">重新提交 | |
| 112 | - </up-button> | |
| 113 | - | |
| 114 | - <up-button type="primary" size="mini" @click="handleProcess(item)">{{ | |
| 115 | - nextStepMap[item.taskKey].btnText | |
| 116 | - }} | |
| 117 | - </up-button> | |
| 99 | + <up-button type="warning" size="mini" @click="handleReject(item)" v-show="nextStepMap[item.taskKey].backShow">回退</up-button> | |
| 100 | + <up-button type="success" size="mini" @click="handleRenew(item)" v-show="nextStepMap[item.taskKey].renewShow">重新提交</up-button> | |
| 101 | + <up-button type="primary" size="mini" @click="handleProcess(item)">{{ nextStepMap[item.taskKey].btnText }}</up-button> | |
| 118 | 102 | <up-button type="info" size="mini" @click="handleDetail(item)">详情</up-button> |
| 119 | 103 | </view> |
| 120 | 104 | </view> |
| ... | ... | @@ -123,11 +107,11 @@ |
| 123 | 107 | |
| 124 | 108 | <!-- 已办工单卡片和我发起的 --> |
| 125 | 109 | <up-card |
| 126 | - v-if="activeTab == 2||activeTab == 1" | |
| 110 | + v-if="activeTab === 1 || activeTab === 2" | |
| 127 | 111 | :border="false" |
| 128 | 112 | :foot-border-top="false" |
| 129 | 113 | v-for="(item, index) in orderList" |
| 130 | - :key="`done_${item.orderNo}_${index}`" | |
| 114 | + :key="`done_${item.id}_${index}`" | |
| 131 | 115 | :show-head="false" |
| 132 | 116 | class="order-card" |
| 133 | 117 | > |
| ... | ... | @@ -149,12 +133,11 @@ |
| 149 | 133 | <view class="u-body-item-title">情况描述:</view> |
| 150 | 134 | <view class="u-line-1 u-body-value">{{ item.remark || '无' }}</view> |
| 151 | 135 | </view> |
| 152 | - | |
| 153 | 136 | <view class="u-body-item u-flex common-justify-between common-item-center"> |
| 154 | 137 | <view class="u-body-item-title"> |
| 155 | 138 | 紧急程度:{{ uni.$dict.getDictLabel('workorder_pressing_type', item.pressingType) }} |
| 156 | 139 | </view> |
| 157 | - <view class=" "> | |
| 140 | + <view> | |
| 158 | 141 | <up-button type="primary" size="mini" @click="handleDetail(item)">工单详情</up-button> |
| 159 | 142 | </view> |
| 160 | 143 | </view> |
| ... | ... | @@ -172,21 +155,12 @@ |
| 172 | 155 | </view> |
| 173 | 156 | </z-paging> |
| 174 | 157 | |
| 175 | - <!-- 底部新增工单按钮(仅巡查员显示) --> | |
| 176 | - <!-- <view v-if="isInspector" class="fixed-bottom-btn-wrap">--> | |
| 177 | - <!-- <up-button type="primary" size="large" @click="handleAddOrder">--> | |
| 178 | - <!-- 新增工单--> | |
| 179 | - <!-- </up-button>--> | |
| 180 | - <!-- </view>--> | |
| 181 | - | |
| 158 | + <!-- 底部新增工单按钮 --> | |
| 182 | 159 | <view class="fixed-bottom-btn-wrap" v-if="isAi"> |
| 183 | - <up-button type="primary" size="large" @click="handleAddOrder"> | |
| 184 | - 新增工单 | |
| 185 | - </up-button> | |
| 160 | + <up-button type="primary" size="large" @click="handleAddOrder">新增工单</up-button> | |
| 186 | 161 | </view> |
| 187 | 162 | |
| 188 | - | |
| 189 | - <!-- 回退原因弹窗:替换为up-modal(核心修改) --> | |
| 163 | + <!-- 回退原因弹窗 --> | |
| 190 | 164 | <up-modal |
| 191 | 165 | :show="rejectModalShow" |
| 192 | 166 | title="回退原因" |
| ... | ... | @@ -197,7 +171,6 @@ |
| 197 | 171 | @confirm="confirmReject" |
| 198 | 172 | > |
| 199 | 173 | <view class="reject-modal-content"> |
| 200 | - <!-- 回退原因 必填textarea --> | |
| 201 | 174 | <up-textarea |
| 202 | 175 | v-model.trim="rejectReason" |
| 203 | 176 | placeholder="请输入回退原因(必填)" |
| ... | ... | @@ -206,11 +179,10 @@ |
| 206 | 179 | maxlength="200" |
| 207 | 180 | class="reject-textarea" |
| 208 | 181 | /> |
| 209 | - <!-- 上传图片(选填)- 按照参考页面改造 --> | |
| 210 | 182 | <view class="upload-wrap mt-20"> |
| 211 | 183 | <view class="upload-title">上传图片(选填)</view> |
| 212 | 184 | <up-upload |
| 213 | - :file-list="rejectImgs.rawImgList.value|| []" | |
| 185 | + :file-list="rejectImgs.rawImgList.value || []" | |
| 214 | 186 | @after-read="rejectImgs.uploadImgs" |
| 215 | 187 | @delete="rejectImgs.deleteImg" |
| 216 | 188 | multiple |
| ... | ... | @@ -224,7 +196,7 @@ |
| 224 | 196 | </view> |
| 225 | 197 | </up-modal> |
| 226 | 198 | |
| 227 | - <!-- 验收弹窗 up-modal(含图片上传) --> | |
| 199 | + <!-- 验收弹窗 --> | |
| 228 | 200 | <up-modal |
| 229 | 201 | :show="acceptModalShow" |
| 230 | 202 | title="验收" |
| ... | ... | @@ -235,15 +207,12 @@ |
| 235 | 207 | @confirm="handleAcceptModalConfirm" |
| 236 | 208 | > |
| 237 | 209 | <view class="accept-modal-content"> |
| 238 | - <!-- 第一行:单选框(通过/不通过,默认通过) --> | |
| 239 | 210 | <view class="radio-group-wrap"> |
| 240 | 211 | <up-radio-group v-model="acceptRadioValue"> |
| 241 | 212 | <up-radio name="0" label="通过"></up-radio> |
| 242 | 213 | <up-radio name="1" label="不通过"></up-radio> |
| 243 | 214 | </up-radio-group> |
| 244 | 215 | </view> |
| 245 | - | |
| 246 | - <!-- 第二行:必填textarea,最多200字 --> | |
| 247 | 216 | <view class="textarea-wrap mt-30"> |
| 248 | 217 | <up-textarea |
| 249 | 218 | v-model.trim="acceptReason" |
| ... | ... | @@ -254,15 +223,12 @@ |
| 254 | 223 | count |
| 255 | 224 | /> |
| 256 | 225 | </view> |
| 257 | - | |
| 258 | - <!-- 验收图片上传(选填,参考回退弹窗样式) --> | |
| 259 | 226 | <view class="upload-wrap mt-20"> |
| 260 | 227 | <view class="upload-title">上传验收图片(选填)</view> |
| 261 | 228 | <up-upload |
| 262 | 229 | :file-list="acceptImgs.rawImgList.value || []" |
| 263 | 230 | @after-read="acceptImgs.uploadImgs" |
| 264 | 231 | @delete="acceptImgs.deleteImg" |
| 265 | - | |
| 266 | 232 | multiple |
| 267 | 233 | width="70" |
| 268 | 234 | height="70" |
| ... | ... | @@ -284,124 +250,171 @@ import { |
| 284 | 250 | myBuzSimplePage, |
| 285 | 251 | todoBuzSimplePage, |
| 286 | 252 | doneBuzSimplePage, |
| 287 | - qyWorkorderCreate, | |
| 288 | 253 | qyUniversalApproval, |
| 289 | 254 | daquUniversalApproval, |
| 290 | 255 | dcyUniversalApproval |
| 291 | 256 | } from '@/api/regional-order-manage/regional-order-manage' |
| 292 | -// 从用户store获取角色信息 | |
| 293 | 257 | import { useUserStore } from '@/pinia/user'; |
| 294 | 258 | import { nextStepMap, buzStatusMap } from '@/common/utils/common' |
| 295 | -// 引入图片上传组合式函数(与参考页面一致) | |
| 296 | 259 | import { useUploadImgs } from '@/common/utils/useUploadImgs' |
| 297 | -// ========== 状态管理 ========== | |
| 260 | + | |
| 261 | +// ========== 全局实例 & 常量 ========== | |
| 298 | 262 | const userStore = useUserStore(); |
| 299 | -// 标签页切换 | |
| 263 | +const USER_ROLES = userStore.userInfo?.roles || []; | |
| 264 | + | |
| 265 | +// ========== 基础状态 (按模块分组,语义化更强) ========== | |
| 266 | +// tab切换 | |
| 300 | 267 | const activeTab = ref(0); // 0-待办 1-我发起的 2-已办 |
| 301 | -const tabList = ref([ | |
| 302 | - {name: '待办'}, | |
| 303 | - {name: '我发起的任务'}, | |
| 304 | - {name: '已办'} | |
| 305 | -]); | |
| 306 | -// 排序下拉框 | |
| 268 | +const tabList = ref([{name: '待办'}, {name: '我发起的任务'}, {name: '已办'}]); | |
| 269 | +// 排序与搜索 | |
| 307 | 270 | const selectedSortValue = ref(1); |
| 308 | 271 | const sortOptions = ref([ |
| 309 | - {name: '位置', id: 1}, | |
| 310 | - {name: '名称', id: 2}, | |
| 311 | - {name: '描述', id: 3}, | |
| 312 | - {name: '编号', id: 4}, | |
| 272 | + {name: '位置', id: 1}, {name: '名称', id: 2}, {name: '描述', id: 3}, {name: '编号', id: 4}, | |
| 313 | 273 | ]); |
| 314 | -// 搜索 | |
| 315 | 274 | const searchValue = ref(''); |
| 316 | -// 分页 | |
| 317 | -const paging = ref(null); | |
| 275 | +// 分页相关 | |
| 276 | +const pagingRef = ref(null); | |
| 318 | 277 | const orderList = ref([]); |
| 319 | -// 角色控制(巡查员显示新增按钮) | |
| 278 | + | |
| 279 | +// ========== 角色权限计算属性 (修复核心bug: 原代码取反逻辑写反) ========== | |
| 320 | 280 | const isAi = computed(() => { |
| 321 | - // patrol_global 全域巡查员 | |
| 322 | - // regional_manager 大区经理 | |
| 323 | - // AI_dispatcher AI工单派发人员 | |
| 324 | - // 增加可选链,避免用户信息不存在报错 | |
| 325 | - console.log('123') | |
| 326 | - console.log(!userStore.userInfo?.roles?.includes('AI_dispatcher')) | |
| 327 | - return !userStore.userInfo?.roles?.includes('AI_dispatcher') ; | |
| 328 | - // return true | |
| 281 | + // AI工单派发人员 不显示新增按钮,其他角色显示 | |
| 282 | + return !USER_ROLES.includes('AI_dispatcher'); | |
| 329 | 283 | }); |
| 330 | -// 回退弹窗相关 | |
| 331 | -const rejectModalShow = ref(false); // 回退modal显示开关 | |
| 332 | -const rejectReason = ref(''); // 回退原因 | |
| 333 | -const currentRejectItem = ref(null); // 当前回退工单 | |
| 334 | -// 回退图片上传配置(与参考页面风格一致) | |
| 284 | + | |
| 285 | +// ========== 回退弹窗相关 ========== | |
| 286 | +const rejectModalShow = ref(false); | |
| 287 | +const rejectReason = ref(''); | |
| 288 | +const currentRejectItem = ref(null); | |
| 335 | 289 | const rejectImgs = useUploadImgs({ |
| 336 | - maxCount: 3, // 最多上传3张 | |
| 337 | - uploadText: '选择回退图片', // 自定义上传提示文字 | |
| 338 | - sizeType: ['compressed'], // 仅上传压缩图 | |
| 339 | - formRef: null, // 该弹窗无表单校验 | |
| 340 | - fieldName: 'rejectImgs' // 自定义字段名 | |
| 290 | + maxCount: 3, uploadText: '选择回退图片', sizeType: ['compressed'], formRef: null, fieldName: 'rejectImgs' | |
| 341 | 291 | }) |
| 342 | -// ========== 验收弹窗相关状态(含图片上传) ========== | |
| 343 | -const acceptModalShow = ref(false); // 验收弹窗显示开关 | |
| 344 | -const acceptRadioValue = ref('0'); // 单选框值,默认0(通过) | |
| 345 | -const acceptReason = ref(''); // 验收原因 | |
| 346 | -const currentAcceptItem = ref(null); // 当前验收的工单项 | |
| 347 | -// 验收图片上传配置(独立实例,参考回退弹窗) | |
| 292 | + | |
| 293 | +// ========== 验收弹窗相关 ========== | |
| 294 | +const acceptModalShow = ref(false); | |
| 295 | +const acceptRadioValue = ref('0'); // 默认通过 | |
| 296 | +const acceptReason = ref(''); | |
| 297 | +const currentAcceptItem = ref(null); | |
| 348 | 298 | const acceptImgs = useUploadImgs({ |
| 349 | - maxCount: 3, // 最多上传3张,与回退弹窗一致 | |
| 350 | - uploadText: '选择验收图片', // 自定义上传提示文字 | |
| 351 | - sizeType: ['compressed'], // 仅上传压缩图,优化性能 | |
| 352 | - formRef: null, // 验收弹窗无表单校验 | |
| 353 | - fieldName: 'acceptImgs' // 自定义字段名,区分回退图片 | |
| 299 | + maxCount: 3, uploadText: '选择验收图片', sizeType: ['compressed'], formRef: null, fieldName: 'acceptImgs' | |
| 354 | 300 | }) |
| 301 | + | |
| 302 | +// ========== 公共封装方法 (核心优化:消灭重复代码) ========== | |
| 303 | +/** | |
| 304 | + * 生成统一的临时存储key | |
| 305 | + * @param {String} prefix 前缀标识 | |
| 306 | + * @returns {String} 唯一key | |
| 307 | + */ | |
| 308 | +const generateTempKey = (prefix = 'order') => { | |
| 309 | + return `${prefix}_${Date.now()}_${Math.floor(Math.random() * 10000)}`; | |
| 310 | +}; | |
| 311 | + | |
| 312 | +/** | |
| 313 | + * 存储工单数据到本地缓存 | |
| 314 | + * @param {Object} item 工单数据 | |
| 315 | + * @param {String} prefix key前缀 | |
| 316 | + * @returns {String|null} 成功返回key,失败返回null | |
| 317 | + */ | |
| 318 | +const setOrderStorage = (item, prefix) => { | |
| 319 | + if (!item?.id) return null; | |
| 320 | + const tempKey = generateTempKey(prefix); | |
| 321 | + try { | |
| 322 | + uni.setStorageSync(tempKey, item); | |
| 323 | + return tempKey; | |
| 324 | + } catch (error) { | |
| 325 | + console.error('存储工单数据失败:', error); | |
| 326 | + uni.showToast({title: '数据存储异常,请重试', icon: 'none'}); | |
| 327 | + return null; | |
| 328 | + } | |
| 329 | +}; | |
| 330 | + | |
| 331 | +/** | |
| 332 | + * 获取分页请求公共参数 | |
| 333 | + * @param {Number} pageNo 页码 | |
| 334 | + * @param {Number} pageSize 页大小 | |
| 335 | + * @returns {Object} 请求参数 | |
| 336 | + */ | |
| 337 | +const getQueryParams = (pageNo, pageSize) => { | |
| 338 | + return { | |
| 339 | + searchContent: searchValue.value.trim() || '', | |
| 340 | + pageNo, | |
| 341 | + pageSize, | |
| 342 | + type: selectedSortValue.value | |
| 343 | + }; | |
| 344 | +}; | |
| 345 | + | |
| 346 | +/** | |
| 347 | + * 统一调用审批接口 | |
| 348 | + * @param {Object} params 请求参数 | |
| 349 | + * @param {String} taskKey 工单任务key | |
| 350 | + * @returns {Promise} 接口请求Promise | |
| 351 | + */ | |
| 352 | +const callApprovalApi = async (params, taskKey) => { | |
| 353 | + if (taskKey === 'shRegionManager') return await dcyUniversalApproval(params); | |
| 354 | + if (taskKey === 'regionManager') return await qyUniversalApproval(params); | |
| 355 | + // 根据角色匹配对应接口 | |
| 356 | + if (USER_ROLES.includes('regional_manager')) return await daquUniversalApproval(params); | |
| 357 | + if (USER_ROLES.includes('Inspector_global')) return await dcyUniversalApproval(params); | |
| 358 | + if (USER_ROLES.includes('patrol_global')) return await qyUniversalApproval(params); | |
| 359 | +}; | |
| 360 | + | |
| 361 | +/** | |
| 362 | + * 统一跳转工单页面 | |
| 363 | + * @param {String} path 页面路径 | |
| 364 | + * @param {Object} query 拼接参数 | |
| 365 | + */ | |
| 366 | +const navToOrderPage = (path, query = {}) => { | |
| 367 | + const queryStr = Object.keys(query).map(k => `${k}=${query[k]}`).join('&'); | |
| 368 | + uni.redirectTo({ url: `${path}${queryStr ? '?' + queryStr : ''}` }); | |
| 369 | +}; | |
| 370 | + | |
| 371 | +/** | |
| 372 | + * 刷新列表 - 统一封装,避免空指针 | |
| 373 | + */ | |
| 374 | +const refreshOrderList = () => { | |
| 375 | + pagingRef.value && pagingRef.value.reload(); | |
| 376 | +}; | |
| 377 | + | |
| 378 | +// ========== 业务核心方法 ========== | |
| 355 | 379 | // 分页查询列表 |
| 356 | 380 | const queryList = async (pageNo, pageSize) => { |
| 357 | 381 | try { |
| 358 | - const apiParams = { | |
| 359 | - searchContent: searchValue.value.trim() || '', | |
| 360 | - pageNo, | |
| 361 | - pageSize, | |
| 362 | - type: selectedSortValue.value // 1-位置 2-工单名称 3-情况描述 4-工单编号 | |
| 363 | - }; | |
| 382 | + const params = getQueryParams(pageNo, pageSize); | |
| 364 | 383 | let res; |
| 365 | - if (activeTab.value == 0) { | |
| 366 | - // 待办工单 | |
| 367 | - res = await todoBuzSimplePage(apiParams); | |
| 368 | - } else if (activeTab.value == 1) { | |
| 369 | - // 我发起的任务 | |
| 370 | - res = await myBuzSimplePage(apiParams); | |
| 371 | - } else { | |
| 372 | - // 已办工单 | |
| 373 | - res = await doneBuzSimplePage(apiParams); | |
| 374 | - } | |
| 375 | - // 适配z-paging分页 | |
| 376 | - paging.value.complete(res.list, res.total); | |
| 384 | + if (activeTab.value === 0) res = await todoBuzSimplePage(params); | |
| 385 | + else if (activeTab.value === 1) res = await myBuzSimplePage(params); | |
| 386 | + else res = await doneBuzSimplePage(params); | |
| 387 | + pagingRef.value.complete(res?.list || [], res?.total || 0); | |
| 377 | 388 | } catch (error) { |
| 378 | 389 | console.error('加载工单失败:', error); |
| 379 | - paging.value?.complete(false); | |
| 390 | + pagingRef.value?.complete(false); | |
| 380 | 391 | uni.showToast({title: '加载失败,请重试', icon: 'none'}); |
| 381 | 392 | } |
| 382 | 393 | }; |
| 383 | -// ========== 事件处理 ========== | |
| 394 | + | |
| 384 | 395 | // 标签页切换 |
| 385 | 396 | const handleTabChange = (item) => { |
| 386 | - orderList.value = []; | |
| 387 | - console.log(item) | |
| 388 | 397 | activeTab.value = item.index; |
| 389 | - paging.value?.reload(); // 切换标签页刷新列表 | |
| 398 | + orderList.value = []; | |
| 399 | + refreshOrderList(); | |
| 390 | 400 | }; |
| 401 | + | |
| 391 | 402 | // 排序变更 |
| 392 | 403 | const handleSortChange = (val) => { |
| 393 | 404 | selectedSortValue.value = val.id; |
| 394 | 405 | searchValue.value = ''; |
| 395 | - paging.value?.reload(); // 排序变更刷新列表 | |
| 406 | + refreshOrderList(); | |
| 396 | 407 | }; |
| 408 | + | |
| 397 | 409 | // 搜索 |
| 398 | 410 | const handleSearch = (val) => { |
| 399 | 411 | searchValue.value = val; |
| 400 | - paging.value?.reload(); // 搜索刷新列表 | |
| 412 | + refreshOrderList(); | |
| 401 | 413 | }; |
| 414 | + | |
| 402 | 415 | // 工单详情 |
| 403 | 416 | const handleDetail = (item) => { |
| 404 | - // 0-待办 1我发起的- 2-已办 | |
| 417 | + if (!item?.taskId) return uni.showToast({title: '工单信息异常', icon: 'none'}); | |
| 405 | 418 | uni.navigateTo({ |
| 406 | 419 | url: `/pages-sub/problem/regional-order-manage/order-detail?taskId=${item.taskId}&activeTab=${activeTab.value}&processInstanceId=${item.processInstanceId}`, |
| 407 | 420 | events: { |
| ... | ... | @@ -414,302 +427,163 @@ const handleDetail = (item) => { |
| 414 | 427 | } |
| 415 | 428 | } |
| 416 | 429 | }); |
| 430 | + // navToOrderPage('/pages-sub/problem/regional-order-manage/order-detail', { | |
| 431 | + // taskId: item.taskId, | |
| 432 | + // activeTab: activeTab.value, | |
| 433 | + // processInstanceId: item.processInstanceId | |
| 434 | + // }, { | |
| 435 | + // needRefresh: () => refreshOrderList() | |
| 436 | + // }); | |
| 437 | + | |
| 438 | + | |
| 417 | 439 | }; |
| 418 | -// 生成临时key | |
| 419 | -const generateTempKey = () => { | |
| 420 | - return 'renew_order_' + Date.now() + '_' + Math.floor(Math.random() * 10000); | |
| 421 | -}; | |
| 422 | -// 待办-重新提交工单(改造后:大数据存本地,仅传唯一标识) | |
| 440 | + | |
| 441 | +// 重新提交工单 | |
| 423 | 442 | const handleRenew = (item) => { |
| 443 | + const tempKey = setOrderStorage(item, 'renew_order'); | |
| 444 | + if (!tempKey) return; | |
| 424 | 445 | |
| 425 | - // 1. 生成唯一临时标识 | |
| 426 | - const tempKey = generateTempKey(); | |
| 427 | - // 2. 将完整工单数据存入本地临时存储(同步存储,确保数据立即生效) | |
| 428 | - try { | |
| 429 | - console.log(item) | |
| 430 | - console.log('123') | |
| 431 | - uni.setStorageSync(tempKey, item); | |
| 432 | - } catch (error) { | |
| 433 | - console.error('存储工单数据失败:', error); | |
| 434 | - uni.showToast({title: '数据存储异常,无法重新提交', icon: 'none'}); | |
| 435 | - return; | |
| 436 | - } | |
| 446 | + const pageUrl = USER_ROLES.includes('patrol_global') | |
| 447 | + ? '/pages-sub/problem/regional-order-manage/add-patrol-order' | |
| 448 | + : '/pages-sub/problem/regional-order-manage/add-order'; | |
| 437 | 449 | |
| 438 | - if (userStore.userInfo?.roles.includes('patrol_global')) { // 全域巡查员 | |
| 439 | - uni.navigateTo({ | |
| 440 | - url: `/pages-sub/problem/regional-order-manage/add-patrol-order?isRenew=1&tempKey=${tempKey}` | |
| 441 | - }); | |
| 442 | - } | |
| 443 | - if (userStore.userInfo?.roles.includes('regional_manager')) { // 大区经理 | |
| 444 | - uni.navigateTo({ | |
| 445 | - url: `/pages-sub/problem/regional-order-manage/add-order?isRenew=1&tempKey=${tempKey}` | |
| 446 | - }); | |
| 447 | - } | |
| 448 | - if (userStore.userInfo?.roles.includes('Inspector_global')) { // 督察员 | |
| 449 | - uni.navigateTo({ | |
| 450 | - url: `/pages-sub/problem/regional-order-manage/add-order?isRenew=1&tempKey=${tempKey}` | |
| 451 | - }); | |
| 452 | - } | |
| 453 | - // // 3. URL 仅传递「唯一标识」和「重新提交标记」(数据量极小,无长度问题) | |
| 454 | - // uni.navigateTo({ | |
| 455 | - // url: `/pages-sub/problem/regional-order-manage/add-patrol-order?isRenew=1&tempKey=${tempKey}` | |
| 456 | - // }); | |
| 450 | + navToOrderPage(pageUrl, { isRenew: 1, tempKey }); | |
| 457 | 451 | }; |
| 458 | -// 待办-处理工单 | |
| 452 | + | |
| 453 | +// 处理工单核心逻辑 | |
| 459 | 454 | const handleProcess = async (item) => { |
| 460 | - console.log(nextStepMap[item.taskKey].name) | |
| 455 | + if (!item) return uni.showToast({title: '工单信息异常', icon: 'none'}); | |
| 456 | + const stepName = nextStepMap[item.taskKey]?.name; | |
| 457 | + let resData | |
| 461 | 458 | try { |
| 462 | - if (nextStepMap[item.taskKey]?.name == '大区经理分配') { | |
| 463 | - // ① 生成唯一临时key(统一规则,避免冲突) | |
| 464 | - const tempKey = `distribute_order_${Date.now()}_${Math.floor(Math.random() * 10000)}`; | |
| 465 | - // ② 存储完整item到本地缓存(同步存储,确保立即生效) | |
| 466 | - try { | |
| 467 | - uni.setStorageSync(tempKey, item); | |
| 468 | - } catch (error) { | |
| 469 | - console.error('存储分配工单数据失败:', error); | |
| 470 | - uni.showToast({title: '数据存储异常,无法跳转', icon: 'none'}); | |
| 471 | - return; | |
| 472 | - } | |
| 473 | - // ③ URL仅传递临时key,无其他冗余参数 | |
| 474 | - uni.navigateTo({ | |
| 475 | - url: `/pages-sub/problem/regional-order-manage/distribution-order?tempKey=${tempKey}` | |
| 476 | - }) | |
| 459 | + // 大区经理分配 | |
| 460 | + if (stepName === '大区经理分配') { | |
| 461 | + const tempKey = setOrderStorage(item, 'distribute_order'); | |
| 462 | + tempKey && navToOrderPage('/pages-sub/problem/regional-order-manage/distribution-order', { tempKey }); | |
| 477 | 463 | } |
| 478 | - if (nextStepMap[item.taskKey]?.name == '督察员单子大区经理分配') { | |
| 479 | - let postData = { | |
| 480 | - "taskKey":item.taskKey, | |
| 481 | - "taskId": item.taskId, | |
| 482 | - "operateType":60, | |
| 483 | - "workerDataId":item.id, | |
| 484 | - "agree":0, | |
| 485 | - "reason":item.remark, | |
| 486 | - "roadId":item.roadId, | |
| 487 | - "roadName":item.roadName, | |
| 488 | - "pressingType":item.pressingType, | |
| 489 | - "orderName":item.orderName, | |
| 490 | - "expectedFinishDate": item.expectedFinishDate, | |
| 491 | - "busiLine":item.busiLine, | |
| 492 | - } | |
| 493 | - const res = await dcyUniversalApproval(postData); | |
| 494 | - uni.showToast({title: '分配成功', icon: 'success', duration: 1000}); | |
| 495 | - paging.value?.reload(); // 刷新列表 | |
| 464 | + // 督察员单子大区经理分配 | |
| 465 | + else if (stepName === '督察员单子大区经理分配') { | |
| 466 | + const postData = { taskKey: item.taskKey, taskId: item.taskId, operateType:60, workerDataId:item.id, agree:0, reason:item.remark, roadId:item.roadId, roadName:item.roadName, pressingType:item.pressingType, orderName:item.orderName, expectedFinishDate: item.expectedFinishDate, busiLine:item.busiLine }; | |
| 467 | + resData = await dcyUniversalApproval(postData); | |
| 468 | + uni.showToast({title: '分配成功', icon: 'success'}); | |
| 469 | + refreshOrderList(); | |
| 496 | 470 | } |
| 497 | - | |
| 498 | - | |
| 499 | - // 验收 - 打开弹窗 | |
| 500 | - if (nextStepMap[item.taskKey]?.name == '巡查员验收' || nextStepMap[item.taskKey]?.name == '养护组长验收') { | |
| 501 | - currentAcceptItem.value = item; // 存储当前工单信息 | |
| 502 | - acceptReason.value = ''; // 清空上次的验收原因 | |
| 503 | - acceptRadioValue.value = '0'; // 重置默认选中“通过” | |
| 504 | - acceptModalShow.value = true; // 显示验收弹窗 | |
| 471 | + // 验收弹窗 | |
| 472 | + else if (['巡查员验收', '养护组长验收'].includes(stepName)) { | |
| 473 | + currentAcceptItem.value = item; | |
| 474 | + acceptReason.value = ''; | |
| 475 | + acceptRadioValue.value = '0'; | |
| 476 | + acceptModalShow.value = true; | |
| 505 | 477 | } |
| 506 | - | |
| 507 | - // 发起人确认 | |
| 508 | - if (nextStepMap[item.taskKey]?.name == '发起人确认') { | |
| 509 | - console.log(item) | |
| 478 | + // 发起人确认-结束工单 | |
| 479 | + else if (stepName === '发起人确认') { | |
| 510 | 480 | uni.showModal({ |
| 511 | 481 | title: "结束工单", |
| 512 | 482 | content: "请确定是否结束工单?", |
| 513 | - success: async function (res) { | |
| 483 | + success: async (res) => { | |
| 514 | 484 | if (res.confirm) { |
| 515 | - // 构建请求参数 | |
| 516 | 485 | const requestData = { |
| 517 | - | |
| 518 | - "workerDataId": item.id, | |
| 519 | - "taskKey": 'ylInspectorStart', | |
| 520 | - "taskId": item.taskId, | |
| 521 | - "operateType": 200, | |
| 522 | - "agree": 1, | |
| 523 | - "reason": '结束工单' | |
| 486 | + workerDataId: item.id, taskKey: 'ylInspectorStart', taskId: item.taskId, | |
| 487 | + operateType: 200, agree: 1, reason: '结束工单' | |
| 524 | 488 | }; |
| 525 | - // 调用回退工单接口 daquUniversalApproval | |
| 526 | - | |
| 527 | - if( userStore.userInfo.roles.includes('regional_manager')){ // 大区经理 | |
| 528 | - await daquUniversalApproval(requestData) | |
| 529 | - } | |
| 530 | - if( userStore.userInfo.roles.includes('Inspector_global')){ // 督察员 | |
| 531 | - await dcyUniversalApproval(requestData); | |
| 532 | - } | |
| 533 | - if( userStore.userInfo.roles.includes('patrol_global')){ // 全域巡查员 | |
| 534 | - await qyUniversalApproval(requestData); | |
| 535 | - } | |
| 536 | - | |
| 537 | - uni.showToast({title: '结束成功', icon: 'success', duration: 1000}); | |
| 538 | - rejectModalShow.value = false; | |
| 539 | - paging.value?.reload(); // 刷新列表 | |
| 540 | - } else if (res.cancel) { | |
| 541 | - console.log("用户点击取消"); | |
| 489 | + await callApprovalApi(requestData, item.taskKey); | |
| 490 | + uni.showToast({title: '结束成功', icon: 'success'}); | |
| 491 | + refreshOrderList(); | |
| 542 | 492 | } |
| 543 | - }, | |
| 493 | + } | |
| 544 | 494 | }); |
| 545 | 495 | } |
| 546 | 496 | } catch (error) { |
| 547 | 497 | console.error('处理工单失败:', error); |
| 548 | - uni.showToast({title: '处理失败,请重试', icon: 'none'}); | |
| 498 | + uni.showToast({title: resData.msg, icon: 'none'}); | |
| 549 | 499 | } |
| 550 | 500 | }; |
| 551 | -// 待办-回退工单(打开回退modal) | |
| 501 | + | |
| 502 | +// 回退工单-打开弹窗 | |
| 552 | 503 | const handleReject = (item) => { |
| 553 | - console.log('123213') | |
| 554 | - // 校验工单有效性 | |
| 555 | - if (!item || !item.id) { | |
| 556 | - uni.showToast({title: '工单信息异常,无法回退', icon: 'none'}); | |
| 557 | - return; | |
| 558 | - } | |
| 504 | + if (!item?.id) return uni.showToast({title: '工单信息异常,无法回退', icon: 'none'}); | |
| 559 | 505 | currentRejectItem.value = item; |
| 560 | - rejectReason.value = ''; // 清空上次输入 | |
| 561 | - rejectImgs.clearImgs(); // 改造后:使用组合式函数的清空方法 | |
| 562 | - rejectModalShow.value = true; // 显示回退modal | |
| 506 | + rejectReason.value = ''; | |
| 507 | + rejectImgs.clearImgs(); | |
| 508 | + rejectModalShow.value = true; | |
| 563 | 509 | }; |
| 564 | -// 回退modal - 取消按钮 | |
| 510 | + | |
| 511 | +// 关闭回退弹窗-重置状态 | |
| 565 | 512 | const handleRejectModalCancel = () => { |
| 566 | 513 | rejectModalShow.value = false; |
| 567 | 514 | rejectReason.value = ''; |
| 568 | - rejectImgs.clearImgs(); // 改造后:使用组合式函数的清空方法 | |
| 515 | + rejectImgs.clearImgs(); | |
| 569 | 516 | }; |
| 517 | + | |
| 570 | 518 | // 确认回退工单 |
| 571 | 519 | const confirmReject = async () => { |
| 572 | - // 严格校验回退原因(去除首尾空格) | |
| 573 | - const rejectReasonTrim = rejectReason.value.trim(); | |
| 574 | - if (!rejectReasonTrim) { | |
| 575 | - uni.showToast({title: '请填写回退原因', icon: 'none', duration: 1000}); | |
| 576 | - return; | |
| 577 | - } | |
| 578 | - // 校验当前工单有效性 | |
| 579 | - if (!currentRejectItem.value || !currentRejectItem.value.id) { | |
| 580 | - uni.showToast({title: '工单信息异常,无法提交', icon: 'none', duration: 1000}); | |
| 581 | - rejectModalShow.value = false; | |
| 582 | - return; | |
| 583 | - } | |
| 520 | + const reason = rejectReason.value.trim(); | |
| 521 | + if (!reason) return uni.showToast({title: '请填写回退原因', icon: 'none'}); | |
| 522 | + const item = currentRejectItem.value; | |
| 523 | + if (!item?.id) return uni.showToast({title: '工单信息异常', icon: 'none'}); | |
| 524 | + | |
| 525 | + uni.showLoading({title: '提交中...', mask: true}); | |
| 584 | 526 | try { |
| 585 | - // 显示加载中,防止重复提交 | |
| 586 | - uni.showLoading({title: '提交中...', mask: true}); | |
| 587 | - // 构建请求参数 | |
| 588 | 527 | const requestData = { |
| 589 | - "returnImgs": rejectImgs.getSuccessImgUrls(), // 改造后:获取上传成功的图片URL数组 | |
| 590 | - "workerDataId": currentRejectItem.value.id, | |
| 591 | - "taskKey": currentRejectItem.value.taskKey, | |
| 592 | - "taskId": currentRejectItem.value.taskId, | |
| 593 | - | |
| 594 | - "operateType": nextStepMap[currentRejectItem.value.taskKey].operateTypeNoPass, | |
| 595 | - "agree": 1, | |
| 596 | - "reason": rejectReasonTrim | |
| 528 | + returnImgs: rejectImgs.getSuccessImgUrls(), workerDataId: item.id, taskKey: item.taskKey, | |
| 529 | + taskId: item.taskId, operateType: nextStepMap[item.taskKey].operateTypeNoPass, agree:1, reason | |
| 597 | 530 | }; |
| 598 | - // 调用回退工单接口 | |
| 599 | - if(currentRejectItem.value.taskKey=='shRegionManager'){ // 对督察员单子 回退 | |
| 600 | - const res = await dcyUniversalApproval(requestData); | |
| 601 | - } | |
| 602 | - if(currentRejectItem.value.taskKey=='regionManager'){ // 对全域巡查员单子 回退 | |
| 603 | - const res = await qyUniversalApproval(requestData); | |
| 604 | - } | |
| 605 | - // 对大区经理单子 回退 | |
| 606 | - uni.showToast({title: '回退成功', icon: 'success', duration: 1000}); | |
| 607 | - rejectModalShow.value = false; | |
| 608 | - paging.value?.reload(); // 刷新列表 | |
| 531 | + await callApprovalApi(requestData, item.taskKey); | |
| 532 | + uni.showToast({title: '回退成功', icon: 'success'}); | |
| 533 | + handleRejectModalCancel(); | |
| 534 | + refreshOrderList(); | |
| 609 | 535 | } catch (error) { |
| 610 | 536 | console.error('回退工单失败:', error); |
| 611 | - uni.showToast({title: '网络异常,回退失败', icon: 'none', duration: 1000}); | |
| 537 | + uni.showToast({title: '回退失败,请重试', icon: 'none'}); | |
| 612 | 538 | } finally { |
| 613 | - // 隐藏加载中 | |
| 614 | 539 | uni.hideLoading(); |
| 615 | 540 | } |
| 616 | 541 | }; |
| 542 | + | |
| 617 | 543 | // 新增工单 |
| 618 | 544 | const handleAddOrder = () => { |
| 619 | - if (userStore.userInfo?.roles.includes('patrol_global')) { // 全域巡查员 | |
| 620 | - uni.navigateTo({ | |
| 621 | - url: '/pages-sub/problem/regional-order-manage/add-patrol-order' | |
| 622 | - }); | |
| 623 | - } | |
| 624 | - if (userStore.userInfo?.roles.includes('regional_manager')) { // 大区经理 | |
| 625 | - uni.navigateTo({ | |
| 626 | - url: '/pages-sub/problem/regional-order-manage/add-order' | |
| 627 | - }); | |
| 628 | - } | |
| 629 | - if (userStore.userInfo?.roles.includes('Inspector_global')) { // 督察员 | |
| 630 | - uni.navigateTo({ | |
| 631 | - url: '/pages-sub/problem/regional-order-manage/add-order' | |
| 632 | - }); | |
| 633 | - } | |
| 545 | + const pageUrl = USER_ROLES.includes('patrol_global') | |
| 546 | + ? '/pages-sub/problem/regional-order-manage/add-patrol-order' | |
| 547 | + : '/pages-sub/problem/regional-order-manage/add-order'; | |
| 548 | + navToOrderPage(pageUrl); | |
| 634 | 549 | }; |
| 635 | -// 验收弹窗 - 取消按钮(清空状态) | |
| 550 | + | |
| 551 | +// 关闭验收弹窗-重置状态 | |
| 636 | 552 | const handleAcceptModalCancel = () => { |
| 637 | 553 | acceptModalShow.value = false; |
| 638 | - acceptReason.value = ''; // 清空验收原因 | |
| 639 | - acceptRadioValue.value = '0'; // 重置单选框为“通过” | |
| 640 | - acceptImgs.clearImgs(); // 清空验收图片 | |
| 554 | + acceptReason.value = ''; | |
| 555 | + acceptRadioValue.value = '0'; | |
| 556 | + acceptImgs.clearImgs(); | |
| 641 | 557 | }; |
| 642 | -// 验收弹窗 - 确定按钮(含returnImgs传参) | |
| 558 | + | |
| 559 | +// 验收提交 | |
| 643 | 560 | const handleAcceptModalConfirm = async () => { |
| 644 | - // 1. 校验验收原因是否为空 | |
| 645 | - if (!acceptReason.value.trim()) { | |
| 646 | - uni.showToast({title: '请填写验收原因', icon: 'none', duration: 1000}); | |
| 647 | - return; | |
| 648 | - } | |
| 649 | - // 2. 校验验收原因长度 | |
| 650 | - if (acceptReason.value.length > 200) { | |
| 651 | - uni.showToast({title: '验收原因最多200字', icon: 'none', duration: 1000}); | |
| 652 | - return; | |
| 653 | - } | |
| 654 | - try { | |
| 655 | - // 3. 构建请求参数(含returnImgs) | |
| 656 | - console.log(currentAcceptItem.value) | |
| 657 | - console.log( userStore.userInfo.roles) | |
| 658 | - console.log( userStore.userInfo.roles.includes('Inspector_global')) | |
| 659 | - // | |
| 660 | - if( userStore.userInfo.roles.includes('regional_manager')){ // 大区经理验收 | |
| 661 | - let postData = { | |
| 662 | - "returnImgs": acceptImgs.getSuccessImgUrls(), // 验收图片URL数组 | |
| 663 | - "taskKey": currentAcceptItem.value.taskKey, | |
| 664 | - "workerDataId": currentAcceptItem.value.id, | |
| 665 | - "taskId": currentAcceptItem.value.taskId, | |
| 666 | - "operateType": acceptRadioValue.value == 0 ? nextStepMap[currentAcceptItem.value.taskKey].operateTypePass : nextStepMap[currentAcceptItem.value.taskKey].operateTypeNoPass, | |
| 667 | - "reason": acceptReason.value.trim(), | |
| 668 | - "agree":acceptRadioValue.value, | |
| 669 | - } | |
| 670 | - await daquUniversalApproval(postData); | |
| 671 | - } | |
| 672 | - // | |
| 673 | - if( userStore.userInfo.roles.includes('patrol_global')){ // 全域巡查员验收 | |
| 674 | - let postData = { | |
| 675 | - "returnImgs": acceptImgs.getSuccessImgUrls(), // 验收图片URL数组 | |
| 676 | - "taskKey": currentAcceptItem.value.taskKey, | |
| 677 | - "taskId": currentAcceptItem.value.taskId, | |
| 678 | - "workerDataId": currentAcceptItem.value.id, | |
| 679 | - "operateType": acceptRadioValue.value == 0 ? nextStepMap[currentAcceptItem.value.taskKey].operateTypePass : nextStepMap[currentAcceptItem.value.taskKey].operateTypeNoPass, | |
| 680 | - "reason": acceptReason.value.trim(), | |
| 681 | - "agree": acceptRadioValue.value | |
| 682 | - } | |
| 683 | - await qyUniversalApproval(postData); | |
| 684 | - } | |
| 685 | - // | |
| 686 | - if( userStore.userInfo.roles.includes('Inspector_global')){ // 督察员验收 | |
| 687 | - let postData = { | |
| 688 | - "returnImgs": acceptImgs.getSuccessImgUrls(), // 验收图片URL数组 | |
| 689 | - "taskKey": currentAcceptItem.value.taskKey, | |
| 690 | - "taskId": currentAcceptItem.value.taskId, | |
| 691 | - "workerDataId": currentAcceptItem.value.id, | |
| 692 | - "operateType": acceptRadioValue.value == 0 ? nextStepMap[currentAcceptItem.value.taskKey].operateTypePass : nextStepMap[currentAcceptItem.value.taskKey].operateTypeNoPass, | |
| 693 | - "reason": acceptReason.value.trim(), | |
| 694 | - "agree": acceptRadioValue.value | |
| 695 | - } | |
| 696 | - await dcyUniversalApproval(postData); | |
| 697 | - } | |
| 561 | + const reason = acceptReason.value.trim(); | |
| 562 | + if (!reason) return uni.showToast({title: '请填写验收原因', icon: 'none'}); | |
| 563 | + if (reason.length > 200) return uni.showToast({title: '验收原因最多200字', icon: 'none'}); | |
| 564 | + | |
| 565 | + const item = currentAcceptItem.value; | |
| 566 | + if (!item?.id) return uni.showToast({title: '工单信息异常', icon: 'none'}); | |
| 698 | 567 | |
| 699 | - // 4. 操作成功处理 | |
| 700 | - uni.showToast({title: '提交成功', icon: 'success', duration: 1000}); | |
| 701 | - handleAcceptModalCancel(); // 清空状态 | |
| 702 | - paging.value?.reload(); // 刷新工单列表 | |
| 568 | + try { | |
| 569 | + const postData = { | |
| 570 | + returnImgs: acceptImgs.getSuccessImgUrls(), taskKey: item.taskKey, workerDataId: item.id, | |
| 571 | + taskId: item.taskId, agree: acceptRadioValue.value, reason, | |
| 572 | + operateType: acceptRadioValue.value === '0' ? nextStepMap[item.taskKey].operateTypePass : nextStepMap[item.taskKey].operateTypeNoPass | |
| 573 | + }; | |
| 574 | + await callApprovalApi(postData, item.taskKey); | |
| 575 | + uni.showToast({title: '提交成功', icon: 'success'}); | |
| 576 | + handleAcceptModalCancel(); | |
| 577 | + refreshOrderList(); | |
| 703 | 578 | } catch (error) { |
| 704 | - // 5. 操作失败处理 | |
| 705 | 579 | console.error('验收失败:', error); |
| 706 | - uni.showToast({title: '验收提交失败,请重试', icon: 'none', duration: 1000}); | |
| 580 | + uni.showToast({title: '验收提交失败,请重试', icon: 'none'}); | |
| 707 | 581 | } |
| 708 | 582 | }; |
| 709 | -// 页面初始化 | |
| 583 | + | |
| 584 | +// ========== 页面生命周期 ========== | |
| 710 | 585 | onLoad(() => { |
| 711 | - // 初始化加载列表 | |
| 712 | - paging.value?.reload(); | |
| 586 | + refreshOrderList(); | |
| 713 | 587 | }); |
| 714 | 588 | </script> |
| 715 | 589 | |
| ... | ... | @@ -734,15 +608,10 @@ onLoad(() => { |
| 734 | 608 | .select-wrap { |
| 735 | 609 | width: 120rpx; |
| 736 | 610 | margin-right: 20rpx; |
| 737 | - | |
| 738 | - :deep(.u-select) { | |
| 611 | + :deep(.u-select), :deep(.u-input__placeholder) { | |
| 739 | 612 | width: 100%; |
| 740 | 613 | font-size: 28rpx; |
| 741 | 614 | } |
| 742 | - | |
| 743 | - :deep(.u-input__placeholder) { | |
| 744 | - font-size: 28rpx; | |
| 745 | - } | |
| 746 | 615 | } |
| 747 | 616 | |
| 748 | 617 | .search-input-wrap { |
| ... | ... | @@ -758,31 +627,14 @@ onLoad(() => { |
| 758 | 627 | box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04); |
| 759 | 628 | } |
| 760 | 629 | |
| 761 | -.card-body { | |
| 762 | - | |
| 763 | -} | |
| 764 | - | |
| 765 | -// 回退modal样式 | |
| 766 | -.reject-modal-content { | |
| 630 | +// 弹窗公共样式 | |
| 631 | +.reject-modal-content, .accept-modal-content { | |
| 767 | 632 | width: 100%; |
| 768 | 633 | box-sizing: border-box; |
| 769 | 634 | padding: 10rpx 0; |
| 770 | 635 | } |
| 771 | 636 | |
| 772 | -.textarea-label { | |
| 773 | - font-size: 28rpx; | |
| 774 | - color: #333; | |
| 775 | - margin-bottom: 10rpx; | |
| 776 | - | |
| 777 | - .required-mark { | |
| 778 | - color: #f56c6c; | |
| 779 | - margin-left: 4rpx; | |
| 780 | - } | |
| 781 | -} | |
| 782 | - | |
| 783 | 637 | .upload-wrap { |
| 784 | - margin-top: 20rpx; | |
| 785 | - | |
| 786 | 638 | .upload-title { |
| 787 | 639 | font-size: 28rpx; |
| 788 | 640 | color: #333; |
| ... | ... | @@ -790,37 +642,20 @@ onLoad(() => { |
| 790 | 642 | } |
| 791 | 643 | } |
| 792 | 644 | |
| 793 | -.mt-20 { | |
| 794 | - margin-top: 20rpx; | |
| 795 | -} | |
| 796 | - | |
| 797 | -.mt-30 { | |
| 798 | - margin-top: 30rpx; | |
| 799 | -} | |
| 800 | - | |
| 801 | -// 养护组长验收弹窗样式 | |
| 802 | -.accept-modal-content { | |
| 803 | - width: 100%; | |
| 804 | - box-sizing: border-box; | |
| 805 | -} | |
| 806 | - | |
| 645 | +// 验收弹窗单选框样式 | |
| 807 | 646 | .radio-group-wrap { |
| 808 | 647 | display: flex; |
| 809 | 648 | align-items: center; |
| 810 | - gap: 40rpx; // 单选框之间的间距 | |
| 649 | + gap: 40rpx; | |
| 811 | 650 | font-size: 28rpx; |
| 812 | 651 | margin-bottom: 20rpx; |
| 813 | 652 | } |
| 814 | 653 | |
| 815 | 654 | .textarea-wrap { |
| 816 | 655 | width: 100%; |
| 817 | - margin-top: 30rpx; | |
| 818 | 656 | } |
| 819 | 657 | |
| 820 | -.modal-btn-wrap { | |
| 821 | - display: flex; | |
| 822 | - align-items: center; | |
| 823 | - justify-content: flex-end; | |
| 824 | - padding-right: 10rpx; | |
| 825 | -} | |
| 658 | +// 间距公共样式 | |
| 659 | +.mt-20 { margin-top: 20rpx; } | |
| 660 | +.mt-30 { margin-top: 30rpx; } | |
| 826 | 661 | </style> |
| 827 | 662 | \ No newline at end of file | ... | ... |
pages-sub/problem/regional-order-manage/order-detail.vue
| ... | ... | @@ -267,23 +267,23 @@ |
| 267 | 267 | </view> |
| 268 | 268 | |
| 269 | 269 | <!-- activeTab==0的时候才出现, 也就是待办 --> |
| 270 | - <view v-if="activeTab==0&&nextStepMap[orderDetail.taskKey]" class="fixed-bottom-btn-wrap"> | |
| 271 | - <view class="u-body-item u-flex common-justify-between common-item-center "> | |
| 272 | - <up-button type="warning" size="normal" @click="handleReject(orderDetail)" | |
| 273 | - v-show="nextStepMap[orderDetail.taskKey].backShow">回退 | |
| 274 | - </up-button> | |
| 275 | - | |
| 276 | - <up-button type="success" size="normal" @click="handleRenew(orderDetail)" | |
| 277 | - v-show="nextStepMap[orderDetail.taskKey].renewShow">重新提交 | |
| 278 | - </up-button> | |
| 279 | - | |
| 280 | - <up-button type="primary" size="normal" @click="handleProcess(orderDetail)">{{ | |
| 281 | - nextStepMap[orderDetail.taskKey].btnText | |
| 282 | - }} | |
| 283 | - </up-button> | |
| 284 | - | |
| 285 | - </view> | |
| 286 | - </view> | |
| 270 | +<!-- <view v-if="activeTab==0&&nextStepMap[orderDetail.taskKey]" class="fixed-bottom-btn-wrap">--> | |
| 271 | +<!-- <view class="u-body-item u-flex common-justify-between common-item-center ">--> | |
| 272 | +<!-- <up-button type="warning" size="normal" @click="handleReject(orderDetail)"--> | |
| 273 | +<!-- v-show="nextStepMap[orderDetail.taskKey].backShow">回退--> | |
| 274 | +<!-- </up-button>--> | |
| 275 | + | |
| 276 | +<!-- <up-button type="success" size="normal" @click="handleRenew(orderDetail)"--> | |
| 277 | +<!-- v-show="nextStepMap[orderDetail.taskKey].renewShow">重新提交--> | |
| 278 | +<!-- </up-button>--> | |
| 279 | + | |
| 280 | +<!-- <up-button type="primary" size="normal" @click="handleProcess(orderDetail)">{{--> | |
| 281 | +<!-- nextStepMap[orderDetail.taskKey].btnText--> | |
| 282 | +<!-- }}--> | |
| 283 | +<!-- </up-button>--> | |
| 284 | + | |
| 285 | +<!-- </view>--> | |
| 286 | +<!-- </view>--> | |
| 287 | 287 | |
| 288 | 288 | <!-- 回退原因弹窗(新增图片上传) --> |
| 289 | 289 | <up-modal | ... | ... |
pages-sub/problem/work-order-manage/add-maintain-order.vue
| ... | ... | @@ -492,11 +492,11 @@ const loadCoProcessorList = async () => { |
| 492 | 492 | }).filter(Boolean); // 过滤null项 |
| 493 | 493 | } else { |
| 494 | 494 | coProcessorList.value = [] |
| 495 | - uni.showToast({ | |
| 496 | - title: '暂无共同处理人数据', | |
| 497 | - icon: 'none', | |
| 498 | - duration: 2000 | |
| 499 | - }); | |
| 495 | + // uni.showToast({ | |
| 496 | + // title: '暂无共同处理人数据', | |
| 497 | + // icon: 'none', | |
| 498 | + // duration: 1000 | |
| 499 | + // }); | |
| 500 | 500 | console.warn('loadCoProcessorList提示:处理人列表为空'); |
| 501 | 501 | } |
| 502 | 502 | } catch (err) { | ... | ... |
pages.json
| ... | ... | @@ -10,7 +10,8 @@ |
| 10 | 10 | { |
| 11 | 11 | "path": "pages/login/index", |
| 12 | 12 | "style": { |
| 13 | - "navigationBarTitleText": "登录" | |
| 13 | + "navigationBarTitleText": "登录", | |
| 14 | + "navigationStyle": "custom" | |
| 14 | 15 | } |
| 15 | 16 | }, |
| 16 | 17 | { |
| ... | ... | @@ -166,7 +167,7 @@ |
| 166 | 167 | |
| 167 | 168 | { |
| 168 | 169 | "path": "regional-order-manage/add-patrol-order", |
| 169 | - "style": { "navigationBarTitleText": "待派单" } | |
| 170 | + "style": { "navigationBarTitleText": "提交工单" } | |
| 170 | 171 | }, |
| 171 | 172 | |
| 172 | 173 | { |
| ... | ... | @@ -197,7 +198,21 @@ |
| 197 | 198 | { |
| 198 | 199 | "path": "tree-archive/index", |
| 199 | 200 | "style": { "navigationBarTitleText": "行道树档案" } |
| 201 | + }, | |
| 202 | + { | |
| 203 | + "path": "tree-archive/treeRecord", | |
| 204 | + "style": { "navigationBarTitleText": "行道树记录" } | |
| 205 | + }, | |
| 206 | + { | |
| 207 | + "path": "tree-archive/addTree", | |
| 208 | + "style": { "navigationBarTitleText": "新增行道树" } | |
| 209 | + }, | |
| 210 | + { | |
| 211 | + "path": "tree-archive/editTree", | |
| 212 | + "style": { "navigationBarTitleText": "编辑行道树" } | |
| 200 | 213 | } |
| 214 | + | |
| 215 | + | |
| 201 | 216 | ] |
| 202 | 217 | } |
| 203 | 218 | ], | ... | ... |
pages/login/index.vue
| 1 | 1 | <template> |
| 2 | 2 | <view class="login-page"> |
| 3 | - <!-- 纯CSS渐变动效背景(替代粒子动画,兼容所有版本) --> | |
| 4 | - <view class="bg-animation"></view> | |
| 3 | + <!-- 顶部标题区 - 加高 + 左下大圆角 + 底部蓝色过滤阴影 + 从浅到深 蓝色渐变背景 --> | |
| 4 | + <view class="top-title"> | |
| 5 | + <text class="welcome-text">你好,欢迎光临</text> | |
| 6 | + <text class="platform-name">全域智能运营管理平台</text> | |
| 7 | + </view> | |
| 5 | 8 | |
| 6 | - <!-- 登录表单区域(悬浮层) --> | |
| 9 | + <!-- 登录表单区域 - 往上偏移 盖住top-title一部分 --> | |
| 7 | 10 | <view class="login-form"> |
| 8 | - <!-- 登录标题(简约大气) --> | |
| 9 | - <view class="login-title">园林登录</view> | |
| 11 | + <!-- 登录标题 --> | |
| 12 | + <view class="login-title">账户登录</view> | |
| 10 | 13 | |
| 11 | - <!-- 账号输入框 --> | |
| 12 | - <view class="form-item"> | |
| 13 | - <up-input | |
| 14 | - v-model="form.account" | |
| 15 | - placeholder="请输入登录账号" | |
| 16 | - border="surround" | |
| 17 | - clearable | |
| 18 | - maxlength="30" | |
| 19 | - input-align="left" | |
| 20 | - :disabled="isLoading" | |
| 21 | - @blur="checkAccount" | |
| 22 | - :custom-style="{ | |
| 23 | - backgroundColor: 'rgba(255, 255, 255, 0.9)', | |
| 24 | - borderColor: '#e5e7eb' | |
| 25 | - }" | |
| 14 | + <!-- ✅ 核心:和工单页同款 up-form 表单校验容器 --> | |
| 15 | + <up-form | |
| 16 | + label-position="left" | |
| 17 | + :model="form" | |
| 18 | + ref="loginFormRef" | |
| 19 | + labelWidth="0" | |
| 20 | + > | |
| 21 | + <!-- 账号输入框 + 校验 --> | |
| 22 | + <up-form-item | |
| 23 | + prop="account" | |
| 26 | 24 | > |
| 27 | - <template #prefix> | |
| 28 | - <up-icon name="account" color="#6b7280" size="20"></up-icon> | |
| 29 | - </template> | |
| 30 | - </up-input> | |
| 31 | - <!-- 账号错误提示 --> | |
| 32 | - <view class="error-tip" v-if="error.account">{{ error.account }}</view> | |
| 33 | - </view> | |
| 25 | + <up-input | |
| 26 | + v-model="form.account" | |
| 27 | + border="surround" | |
| 28 | + clearable | |
| 29 | + maxlength="30" | |
| 30 | + input-align="left" | |
| 31 | + fontSize="16px" | |
| 32 | + :disabled="isLoading" | |
| 33 | + shape="circle" | |
| 34 | + placeholder="请输入账户" | |
| 35 | + selectionStart="15" | |
| 36 | + selectionEnd="15" | |
| 37 | + @blur="() => loginFormRef.validateField('account')" | |
| 38 | + /> | |
| 39 | + </up-form-item> | |
| 34 | 40 | |
| 35 | - <!-- 密码输入框(移除查看密码功能) --> | |
| 36 | - <view class="form-item"> | |
| 37 | - <up-input | |
| 38 | - v-model="form.password" | |
| 39 | - placeholder="请输入登录密码" | |
| 40 | - maxlength="20" | |
| 41 | - border="surround" | |
| 42 | - clearable | |
| 43 | - input-align="left" | |
| 44 | - type="password" | |
| 45 | - :disabled="isLoading" | |
| 46 | - @blur="checkPassword" | |
| 47 | - :custom-style="{ | |
| 48 | - backgroundColor: 'rgba(255, 255, 255, 0.9)', | |
| 49 | - borderColor: '#e5e7eb' | |
| 50 | - }" | |
| 41 | + <!-- 密码输入框 + 校验 --> | |
| 42 | + <up-form-item | |
| 43 | + prop="password" | |
| 51 | 44 | > |
| 52 | - <template #prefix> | |
| 53 | - <up-icon name="lock" color="#6b7280" size="20"></up-icon> | |
| 54 | - </template> | |
| 55 | - </up-input> | |
| 56 | - <!-- 密码错误提示 --> | |
| 57 | - <view class="error-tip" v-if="error.password">{{ error.password }}</view> | |
| 58 | - </view> | |
| 45 | + <up-input | |
| 46 | + v-model="form.password" | |
| 47 | + placeholder="请输入密码" | |
| 48 | + maxlength="20" | |
| 49 | + border="surround" | |
| 50 | + clearable | |
| 51 | + input-align="left" | |
| 52 | + type="password" | |
| 53 | + :disabled="isLoading" | |
| 54 | + shape="circle" | |
| 55 | + selectionStart="5" | |
| 56 | + selectionEnd="5" | |
| 57 | + @blur="() => loginFormRef.validateField('password')" | |
| 58 | + /> | |
| 59 | + </up-form-item> | |
| 60 | + </up-form> | |
| 59 | 61 | |
| 60 | 62 | <!-- 登录按钮 --> |
| 61 | 63 | <up-button |
| ... | ... | @@ -64,44 +66,58 @@ |
| 64 | 66 | size="large" |
| 65 | 67 | :loading="isLoading" |
| 66 | 68 | @click="handleLogin" |
| 67 | - :custom-style="{ | |
| 68 | - | |
| 69 | - }" | |
| 69 | + shape="circle" | |
| 70 | 70 | > |
| 71 | 71 | 登录 |
| 72 | 72 | </up-button> |
| 73 | 73 | </view> |
| 74 | + | |
| 75 | + <!-- 版权信息 --> | |
| 76 | + <view class="copyright">蓟城山水集团版权所有</view> | |
| 74 | 77 | </view> |
| 75 | 78 | </template> |
| 76 | 79 | |
| 77 | 80 | <script setup> |
| 78 | -import { ref, reactive, onMounted } from 'vue'; | |
| 81 | +import { ref, reactive, onMounted, nextTick } from 'vue'; | |
| 79 | 82 | import { useUserStore } from '@/pinia/user'; |
| 80 | 83 | import globalConfig from '@/common/config/global'; |
| 81 | 84 | |
| 82 | -// 表单数据 | |
| 85 | +// ========== 【全局实例 & 基础状态】 和工单页写法一致 ========== | |
| 86 | +const userStore = useUserStore(); | |
| 87 | +const loginFormRef = ref(null); // 表单ref 用于校验 | |
| 88 | +const isLoading = ref(false); // 登录加载状态 | |
| 89 | + | |
| 90 | +// ========== 【表单数据】 和工单页一致的 reactive 声明 ========== | |
| 83 | 91 | const form = reactive({ |
| 84 | - account: '', // 账号 | |
| 85 | - password: '' // 密码 | |
| 92 | + account: '', // 账号 | |
| 93 | + password: '' // 密码 | |
| 86 | 94 | }); |
| 87 | 95 | |
| 88 | -// 状态管理 | |
| 89 | -const isLoading = ref(false); | |
| 90 | -const error = reactive({ | |
| 91 | - account: '', | |
| 92 | - password: '' | |
| 96 | +// ========== 【表单校验规则】 ✅核心 和工单页1:1同款校验规则写法 ========== | |
| 97 | +const loginFormRules = reactive({ | |
| 98 | + // 账号校验:必填 + 长度限制 2-30位(合理账号长度,可自行调整) | |
| 99 | + account: [ | |
| 100 | + { type: 'string', required: true, message: '请输入登录账号', trigger: ['change', 'blur'] }, | |
| 101 | + { type: 'string', min: 2, max: 30, message: '账号长度为2-30个字符', trigger: ['change', 'blur'] } | |
| 102 | + ], | |
| 103 | + // 密码校验:必填 + 长度限制 6-20位(行业通用密码长度,可自行调整) | |
| 104 | + password: [ | |
| 105 | + { type: 'string', required: true, message: '请输入登录密码', trigger: ['change', 'blur'] }, | |
| 106 | + { type: 'string', min: 6, max: 20, message: '密码长度为6-20个字符', trigger: ['change', 'blur'] } | |
| 107 | + ] | |
| 93 | 108 | }); |
| 94 | 109 | |
| 95 | -// 实例化 Pinia 用户仓库 | |
| 96 | -const userStore = useUserStore(); | |
| 97 | - | |
| 98 | -// 页面加载时初始化 | |
| 110 | +// ========== 【生命周期】 nextTick 设置校验规则 和工单页一致 ========== | |
| 99 | 111 | onMounted(() => { |
| 100 | 112 | // 检查登录态 |
| 101 | 113 | checkLoginStatus(); |
| 114 | + // nextTick确保表单挂载完成再赋值规则,修复校验不生效bug,和工单页写法一致 | |
| 115 | + nextTick(() => { | |
| 116 | + loginFormRef.value?.setRules(loginFormRules); | |
| 117 | + }); | |
| 102 | 118 | }); |
| 103 | 119 | |
| 104 | -// 检查登录状态 | |
| 120 | +// ========== 【检查登录状态】 原有逻辑不变 ========== | |
| 105 | 121 | const checkLoginStatus = () => { |
| 106 | 122 | try { |
| 107 | 123 | // 已登录则直接跳首页 |
| ... | ... | @@ -109,7 +125,6 @@ const checkLoginStatus = () => { |
| 109 | 125 | uni.switchTab({ |
| 110 | 126 | url: '/pages/workbench/index', |
| 111 | 127 | fail: () => { |
| 112 | - // 非tabBar页面用redirectTo | |
| 113 | 128 | uni.reLaunch({ url: '/pages/workbench/index' }); |
| 114 | 129 | } |
| 115 | 130 | }); |
| ... | ... | @@ -120,38 +135,14 @@ const checkLoginStatus = () => { |
| 120 | 135 | } |
| 121 | 136 | }; |
| 122 | 137 | |
| 123 | -// 校验账号 | |
| 124 | -const checkAccount = () => { | |
| 125 | - if (!form.account) { | |
| 126 | - error.account = '请输入登录账号'; | |
| 127 | - } else { | |
| 128 | - error.account = ''; | |
| 129 | - } | |
| 130 | -}; | |
| 131 | - | |
| 132 | -// 校验密码 | |
| 133 | -const checkPassword = () => { | |
| 134 | - if (!form.password) { | |
| 135 | - error.password = '请输入登录密码'; | |
| 136 | - } else { | |
| 137 | - error.password = ''; | |
| 138 | - } | |
| 139 | -}; | |
| 140 | - | |
| 141 | -// 表单整体校验 | |
| 142 | -const validateForm = () => { | |
| 143 | - checkAccount(); | |
| 144 | - checkPassword(); | |
| 145 | - return !error.account && !error.password; | |
| 146 | -}; | |
| 147 | - | |
| 148 | -// 登录处理 | |
| 138 | +// ========== 【核心登录方法】 ✅ 完整表单校验 + 登录逻辑,和工单页submit提交逻辑一致 ========== | |
| 149 | 139 | const handleLogin = async () => { |
| 150 | - if (!validateForm()) return; | |
| 151 | - | |
| 152 | - isLoading.value = true; | |
| 153 | - | |
| 154 | 140 | try { |
| 141 | + // ✅ 第一步:先执行表单整体校验,通过后再执行登录逻辑(和工单页submitWorkOrder写法一致) | |
| 142 | + await loginFormRef.value.validate(); | |
| 143 | + | |
| 144 | + isLoading.value = true; | |
| 145 | + // 执行登录请求 | |
| 155 | 146 | await userStore.login({ |
| 156 | 147 | username: form.account, |
| 157 | 148 | password: form.password |
| ... | ... | @@ -170,16 +161,15 @@ const handleLogin = async () => { |
| 170 | 161 | }); |
| 171 | 162 | }, 1000); |
| 172 | 163 | } catch (err) { |
| 173 | - console.error('登录失败详情:', err); | |
| 174 | - const errorMsg = | |
| 175 | - err.message === '网络异常,请稍后重试' | |
| 176 | - ? '网络异常,请稍后重试' | |
| 177 | - : err.message || '登录失败,请检查账号密码'; | |
| 178 | - uni.showToast({ | |
| 179 | - title: errorMsg, | |
| 180 | - icon: 'none', | |
| 181 | - duration: 2000 | |
| 182 | - }); | |
| 164 | + // ✅ 校验失败/登录失败 统一捕获提示,和工单页异常处理一致 | |
| 165 | + if (!Array.isArray(err)) { | |
| 166 | + console.error('登录失败:', err); | |
| 167 | + uni.showToast({ | |
| 168 | + title: err.message || '账号或密码错误,请重试', | |
| 169 | + icon: 'none', | |
| 170 | + duration: 2000 | |
| 171 | + }); | |
| 172 | + } | |
| 183 | 173 | } finally { |
| 184 | 174 | isLoading.value = false; |
| 185 | 175 | } |
| ... | ... | @@ -190,122 +180,83 @@ const handleLogin = async () => { |
| 190 | 180 | // 核心布局 |
| 191 | 181 | .login-page { |
| 192 | 182 | min-height: 100vh; |
| 193 | - position: relative; | |
| 194 | - padding: 40rpx 30rpx; | |
| 183 | + padding: 0; | |
| 195 | 184 | box-sizing: border-box; |
| 196 | - overflow: hidden; // 隐藏动效溢出 | |
| 197 | - | |
| 198 | - // 适配小程序安全区 | |
| 199 | - /* #ifdef MP-WEIXIN */ | |
| 200 | - padding-bottom: constant(safe-area-inset-bottom); | |
| 201 | - padding-bottom: env(safe-area-inset-bottom); | |
| 202 | - /* #endif */ | |
| 185 | + overflow: hidden; | |
| 186 | + position: relative; | |
| 187 | + background: #f5f7fa; | |
| 203 | 188 | } |
| 204 | 189 | |
| 205 | -// 纯CSS渐变动效背景(替代粒子,兼容所有版本) | |
| 206 | -.bg-animation { | |
| 207 | - position: absolute; | |
| 208 | - top: 0; | |
| 209 | - left: 0; | |
| 210 | - width: 100%; | |
| 211 | - height: 100%; | |
| 212 | - z-index: 0; | |
| 213 | - // 基础渐变底色 | |
| 214 | - background: linear-gradient(120deg, #f0f9ff 0%, #e6f7ff 100%); | |
| 215 | - // 伪粒子动效(多个透明渐变圆缓慢移动) | |
| 216 | - &::before, | |
| 217 | - &::after { | |
| 218 | - content: ''; | |
| 219 | - position: absolute; | |
| 220 | - border-radius: 50%; | |
| 221 | - background: rgba(59, 130, 246, 0.05); | |
| 222 | - animation: float 20s infinite linear; | |
| 223 | - } | |
| 224 | - | |
| 225 | - &::before { | |
| 226 | - width: 600rpx; | |
| 227 | - height: 600rpx; | |
| 228 | - top: -200rpx; | |
| 229 | - left: -200rpx; | |
| 230 | - } | |
| 190 | +// 顶部:从左到右 由浅到深 线性渐变 过渡自然柔和 无突兀色块 | |
| 191 | +.top-title { | |
| 192 | + background: linear-gradient(120deg, #4299e1 0%, #2970e8 50%, #2563eb 100%); | |
| 193 | + color: #fff; | |
| 194 | + padding: 240rpx 30rpx 80rpx; | |
| 195 | + text-align: left; | |
| 196 | + border-bottom-left-radius: 120rpx; | |
| 197 | + position: relative; | |
| 198 | + z-index: 1; | |
| 199 | + box-shadow: 0 25rpx 40rpx -15rpx rgba(37, 99, 235, 0.3); | |
| 231 | 200 | |
| 232 | - &::after { | |
| 233 | - width: 800rpx; | |
| 234 | - height: 800rpx; | |
| 235 | - bottom: -300rpx; | |
| 236 | - right: -300rpx; | |
| 237 | - animation-delay: -10s; // 错开动画时间 | |
| 201 | + .welcome-text { | |
| 202 | + font-size: 46rpx; | |
| 203 | + display: block; | |
| 204 | + margin-bottom: 10px; | |
| 205 | + font-weight: 500; | |
| 238 | 206 | } |
| 239 | 207 | |
| 240 | - // 额外小动效点(模拟粒子) | |
| 241 | - & > view { | |
| 242 | - position: absolute; | |
| 243 | - width: 400rpx; | |
| 244 | - height: 400rpx; | |
| 245 | - border-radius: 50%; | |
| 246 | - background: rgba(59, 130, 246, 0.03); | |
| 247 | - top: 50%; | |
| 248 | - left: 50%; | |
| 249 | - transform: translate(-50%, -50%); | |
| 250 | - animation: float 15s infinite linear reverse; | |
| 208 | + .platform-name { | |
| 209 | + margin-bottom: 10px; | |
| 210 | + font-size: 46rpx; | |
| 211 | + display: block; | |
| 212 | + opacity: 0.95; | |
| 251 | 213 | } |
| 252 | 214 | } |
| 253 | 215 | |
| 254 | -// 浮动动画(模拟粒子运动) | |
| 255 | -@keyframes float { | |
| 256 | - 0% { | |
| 257 | - transform: translate(0, 0) rotate(0deg); | |
| 258 | - } | |
| 259 | - 25% { | |
| 260 | - transform: translate(50rpx, 30rpx) rotate(90deg); | |
| 261 | - } | |
| 262 | - 50% { | |
| 263 | - transform: translate(0, 60rpx) rotate(180deg); | |
| 264 | - } | |
| 265 | - 75% { | |
| 266 | - transform: translate(-50rpx, 30rpx) rotate(270deg); | |
| 267 | - } | |
| 268 | - 100% { | |
| 269 | - transform: translate(0, 0) rotate(360deg); | |
| 270 | - } | |
| 271 | -} | |
| 272 | - | |
| 273 | -// 登录表单(悬浮层) | |
| 274 | 216 | .login-form { |
| 217 | + background-color: #fff; | |
| 218 | + padding: 40rpx 30rpx; | |
| 219 | + border-radius: 16rpx; | |
| 220 | + box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06); | |
| 221 | + margin: -60rpx 40rpx 0; | |
| 275 | 222 | position: relative; |
| 276 | 223 | z-index: 10; |
| 277 | - background-color: rgba(255, 255, 255, 0.95); | |
| 278 | - padding: 60rpx 40rpx; | |
| 279 | - border-radius: 16rpx; | |
| 280 | - box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08); | |
| 281 | - max-width: 600rpx; | |
| 282 | - margin: 0 auto; | |
| 283 | - margin-top: 100rpx; | |
| 224 | + box-sizing: border-box; | |
| 284 | 225 | |
| 285 | - // 登录标题 | |
| 286 | 226 | .login-title { |
| 287 | - font-size: 36rpx; | |
| 227 | + font-size: 38rpx; | |
| 288 | 228 | font-weight: 600; |
| 289 | - color: #111827; | |
| 290 | - text-align: center; | |
| 291 | - margin-bottom: 40rpx; | |
| 292 | - letter-spacing: 2rpx; | |
| 229 | + color: #666; | |
| 230 | + margin-bottom: 30rpx; | |
| 231 | + letter-spacing: 1rpx; | |
| 293 | 232 | } |
| 294 | 233 | } |
| 295 | 234 | |
| 296 | -// 表单项 | |
| 297 | -.form-item { | |
| 298 | - margin-bottom: 30rpx; | |
| 235 | +// 适配表单校验的间距 | |
| 236 | +:deep(.u-form-item) { | |
| 237 | + margin-bottom: 20rpx; | |
| 299 | 238 | position: relative; |
| 239 | +} | |
| 300 | 240 | |
| 301 | - // 错误提示 | |
| 302 | - .error-tip { | |
| 303 | - font-size: 24rpx; | |
| 304 | - color: #ef4444; | |
| 305 | - margin-top: 10rpx; | |
| 306 | - line-height: 1.2; | |
| 307 | - padding-left: 10rpx; | |
| 308 | - } | |
| 241 | +// 登录按钮:和顶部完全同款 从左到右 由浅到深 线性渐变 + 保留所有原尺寸 | |
| 242 | +.login-btn { | |
| 243 | + margin-top: 40rpx; | |
| 244 | + width: 100%; | |
| 245 | + height: 88rpx; | |
| 246 | + line-height: 88rpx; | |
| 247 | + border-radius: 8rpx; | |
| 248 | + font-size: 32rpx; | |
| 249 | + background: linear-gradient(120deg, #4299e1 0%, #2970e8 50%, #2563eb 100%) !important; | |
| 250 | + border: none !important; | |
| 251 | + &::after { border: none !important; } | |
| 309 | 252 | } |
| 310 | 253 | |
| 254 | +.copyright { | |
| 255 | + width: 100%; | |
| 256 | + text-align: center; | |
| 257 | + font-size: 24rpx; | |
| 258 | + color: #999; | |
| 259 | + position: fixed; | |
| 260 | + bottom: 100px; | |
| 261 | +} | |
| 311 | 262 | </style> |
| 312 | 263 | \ No newline at end of file | ... | ... |
static/imgs/tree/tree-high.png
0 → 100644
1003 Bytes
static/imgs/tree/treearound.png
0 → 100644
927 Bytes