Commit 7da61cdb67f64bdcbe21d4f6b77bedf856c2b6ed
1 parent
25904bec
养护计划
Showing
8 changed files
with
932 additions
and
560 deletions
api/maintain-manage/maintain-manage.js
| 1 | 1 | |
| 2 | -import { post, get } from '@/common/utils/request'; | |
| 2 | +import { post, get, put } from '@/common/utils/request'; | |
| 3 | 3 | |
| 4 | 4 | /** |
| 5 | 5 | * 道路汇总 |
| ... | ... | @@ -34,6 +34,15 @@ export const maintainCreate = (data) => { |
| 34 | 34 | }; |
| 35 | 35 | |
| 36 | 36 | /** |
| 37 | + * 新增养护计划并完成 | |
| 38 | + * @returns {Promise} | |
| 39 | + */ | |
| 40 | +export const maintainCreateFinish = (data) => { | |
| 41 | + return post('/app-api/app/garden/maintain-plan-commit/create-and-finish',data); | |
| 42 | +}; | |
| 43 | + | |
| 44 | + | |
| 45 | +/** | |
| 37 | 46 | * 未完成养护计划 |
| 38 | 47 | * @returns {Promise} |
| 39 | 48 | */ |
| ... | ... | @@ -51,3 +60,59 @@ export const detailList = (params) => { |
| 51 | 60 | }; |
| 52 | 61 | |
| 53 | 62 | |
| 63 | +/** | |
| 64 | + * 查询单条上报记录 | |
| 65 | + * @returns {Promise} | |
| 66 | + */ | |
| 67 | +export const getDetailList = (params) => { | |
| 68 | + return get('/admin-api/garden/maintain-plan-commit/get',params); | |
| 69 | +}; | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | +/** | |
| 75 | + * 养护道路 | |
| 76 | + * @returns {Promise} | |
| 77 | + */ | |
| 78 | +export const maintainPlan= (params) => { | |
| 79 | + return get('/admin-api/garden/maintain-plan/page',params); | |
| 80 | +}; | |
| 81 | +/** | |
| 82 | + * 养护类型次数 | |
| 83 | + * @returns {Promise} | |
| 84 | + */ | |
| 85 | +export const maintainPlanNum= (params) => { | |
| 86 | + return get('/admin-api/garden/maintain-plan-detail/page',params); | |
| 87 | +}; | |
| 88 | + | |
| 89 | + | |
| 90 | +/** | |
| 91 | + * 查询上报记录列表 | |
| 92 | + * @returns {Promise} | |
| 93 | + */ | |
| 94 | +export const maintainPlanList= (params) => { | |
| 95 | + return get('/app-api/app/garden/maintain-plan-commit/page',params); | |
| 96 | +}; | |
| 97 | + | |
| 98 | + | |
| 99 | +/** | |
| 100 | + * 直接结束 | |
| 101 | + * @returns {Promise} | |
| 102 | + */ | |
| 103 | +export const finishPlan= (params) => { | |
| 104 | + return post('/app-api/app/garden/maintain-plan-commit/finish',params); | |
| 105 | +}; | |
| 106 | + | |
| 107 | +/** | |
| 108 | + * 设为失效 | |
| 109 | + * @returns {Promise} | |
| 110 | + */ | |
| 111 | +export const invalidatePlan= (params) => { | |
| 112 | + return put('/admin-api/garden/maintain-plan-detail/invalidate',params); | |
| 113 | +}; | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | ... | ... |
pages-sub/daily/maintain-manage/add-record.vue
| ... | ... | @@ -4,44 +4,40 @@ |
| 4 | 4 | <up-form label-position="left" :model="inspectForm" ref="inspectFormRef" label-width="140rpx"> |
| 5 | 5 | <!-- ========== 动态表单区域 ========== --> |
| 6 | 6 | <template v-for="item in dynamicFields" :key="item.fieldCode"> |
| 7 | - <up-form-item :label="item.fieldName" :prop="item.fieldCode" class="form-item" @click="openPickerIfNeeded(item)"> | |
| 7 | + <up-form-item :label="item.fieldName" :prop="item.fieldCode" class="form-item" | |
| 8 | + @click="openPickerIfNeeded(item)"> | |
| 8 | 9 | |
| 9 | 10 | <!-- type 1 数值:u-number-box,最多2位小数,min=0,max取item.length --> |
| 10 | 11 | <view v-if="item.fieldType === 1" class="number-box-wrap"> |
| 11 | - <u-number-box | |
| 12 | - v-model="inspectForm[item.fieldCode]" | |
| 13 | - min="1" | |
| 14 | - :step="1" | |
| 15 | - :max="item.length ? 10 ** Number(item.length) - 1 : null" | |
| 16 | - :placeholder="`请输入${item.fieldName}${item.unit} `" | |
| 17 | - @change="valChange(item)" | |
| 18 | - @blur="valChange(item)" | |
| 19 | - class="large-number-box"> | |
| 20 | -</u-number-box> | |
| 12 | + <u-number-box v-model="inspectForm[item.fieldCode]" min="1" :step="1" | |
| 13 | + :max="item.length ? 10 ** Number(item.length) - 1 : null" | |
| 14 | + :placeholder="`请输入${item.fieldName}${item.unit} `" @change="valChange(item)" @blur="valChange(item)" | |
| 15 | + class="large-number-box"> | |
| 16 | + </u-number-box> | |
| 21 | 17 | |
| 22 | 18 | <text class="unit-text" v-if="item.unit">{{ item.unit }}</text> |
| 23 | 19 | </view> |
| 24 | 20 | <!-- type 2 文本 --> |
| 25 | 21 | <up-input v-else-if="item.fieldType === 2" v-model="inspectForm[item.fieldCode]" |
| 26 | - :maxlength="item.length || 50" :placeholder="`请输入${item.fieldName}`"></up-input> | |
| 22 | + :maxlength="item.length || 50" :placeholder="`请输入${item.fieldName}`"></up-input> | |
| 27 | 23 | <!-- 分层选项(3枚举/6多选,且选项带 path):统一只读入口,点开分组弹层选择。 |
| 28 | 24 | 必须放在下面的 3/6 分支之前——v-else-if 短路,放后面就永远进不来 --> |
| 29 | 25 | <up-input v-else-if="isGroupedField(item)" v-model="labelMap[item.fieldCode]" readonly |
| 30 | - disabled-color="#ffffff" :placeholder="`请选择${item.fieldName}`" border="none"> | |
| 26 | + disabled-color="#ffffff" :placeholder="`请选择${item.fieldName}`" border="none"> | |
| 31 | 27 | <template #right> |
| 32 | 28 | <up-icon name="arrow-right" size="16"></up-icon> |
| 33 | 29 | </template> |
| 34 | 30 | </up-input> |
| 35 | 31 | <!-- type3 枚举:多选 multiple=1 使用复选框组(name 必须是 value,提交按逗号拼接 value,与值格式契约一致) --> |
| 36 | 32 | <u-checkbox-group v-else-if="item.fieldType === 3 && item.multiple === 1" |
| 37 | - v-model="inspectForm[item.fieldCode]"> | |
| 33 | + v-model="inspectForm[item.fieldCode]"> | |
| 38 | 34 | <u-checkbox v-for="opt in item.selectOptions" :key="opt.value" :label="opt.label" :name="opt.value"> |
| 39 | 35 | |
| 40 | 36 | </u-checkbox> |
| 41 | 37 | </u-checkbox-group> |
| 42 | 38 | <!-- type3 枚举:单选 用up-action-sheet 底部选择 --> |
| 43 | 39 | <up-input v-else-if="item.fieldType === 3" v-model="labelMap[item.fieldCode]" readonly |
| 44 | - disabled-color="#ffffff" :placeholder="`请选择${item.fieldName}`" border="none"> | |
| 40 | + disabled-color="#ffffff" :placeholder="`请选择${item.fieldName}`" border="none"> | |
| 45 | 41 | <template #right> |
| 46 | 42 | <up-icon name="arrow-right" size="16"></up-icon> |
| 47 | 43 | </template> |
| ... | ... | @@ -59,7 +55,7 @@ |
| 59 | 55 | <view v-else-if="item.fieldType === 6" class="multi-wrap"> |
| 60 | 56 | <u-checkbox-group v-model="inspectForm[item.fieldCode]" @change="onMultiChange(item)"> |
| 61 | 57 | <u-checkbox v-for="opt in item.selectOptions" :key="opt.value" :label="opt.label" :name="opt.value" |
| 62 | - :disabled="isOptionLocked(item, opt)"> | |
| 58 | + :disabled="isOptionLocked(item, opt)"> | |
| 63 | 59 | </u-checkbox> |
| 64 | 60 | </u-checkbox-group> |
| 65 | 61 | <!-- 有限上限时提示已选数。上限靠「禁用未选中项」实现:u-checkbox 的勾选态在组件内部,回写 v-model 撤不掉 --> |
| ... | ... | @@ -73,22 +69,12 @@ |
| 73 | 69 | </view> |
| 74 | 70 | </view> |
| 75 | 71 | <!-- type7 图片:多张上传,提交 JSON 数组(URL) --> |
| 76 | - <!-- type7 图片:多张上传,提交 JSON 数组(URL) --> | |
| 77 | -<!-- type7 图片:多张上传,提交 JSON 数组(URL)【对齐工单上传组件】 --> | |
| 78 | -<up-upload | |
| 79 | - v-else-if="item.fieldType === 7" | |
| 80 | - :file-list="fileListMap[item.fieldCode] || []" | |
| 81 | - @after-read="(event) => uploadFieldFiles(item, event)" | |
| 82 | - @delete="(event) => deleteFieldFile(item, event)" | |
| 83 | - multiple | |
| 84 | - width="70" | |
| 85 | - height="70" | |
| 86 | - :sizeType="['compressed']" | |
| 87 | - :max-count="pickCountOf(item, 5)" | |
| 88 | - upload-text="选择图片" | |
| 89 | - del-color="#ff4d4f" | |
| 90 | - class="upload-wrap" | |
| 91 | -></up-upload> | |
| 72 | + <!-- type7 图片:多张上传,提交 JSON 数组(URL) --> | |
| 73 | + <!-- type7 图片:多张上传,提交 JSON 数组(URL)【对齐工单上传组件】 --> | |
| 74 | + <up-upload v-else-if="item.fieldType === 7" :file-list="fileListMap[item.fieldCode] || []" | |
| 75 | + @after-read="(event) => uploadFieldFiles(item, event)" @delete="(event) => deleteFieldFile(item, event)" | |
| 76 | + multiple width="70" height="70" :sizeType="['compressed']" :max-count="pickCountOf(item, 5)" | |
| 77 | + upload-text="选择图片" del-color="#ff4d4f" class="upload-wrap"></up-upload> | |
| 92 | 78 | |
| 93 | 79 | |
| 94 | 80 | <!-- type8 附件:微信只能从聊天记录选文件(无通用文件选择器),提交 JSON 数组(URL) --> |
| ... | ... | @@ -98,21 +84,21 @@ |
| 98 | 84 | <text class="file-del" @click.stop="deleteFieldFile(item, { index: idx })">删除</text> |
| 99 | 85 | </view> |
| 100 | 86 | <up-button size="small" type="primary" plain :text="fileButtonText(item)" |
| 101 | - @click.stop="chooseFieldFiles(item)"></up-button> | |
| 87 | + @click.stop="chooseFieldFiles(item)"></up-button> | |
| 102 | 88 | </view> |
| 103 | 89 | <!-- type9 关联记录:底部选择,提交 JSON 数组 [{id,name}] --> |
| 104 | 90 | <up-input v-else-if="item.fieldType === 9" v-model="labelMap[item.fieldCode]" readonly |
| 105 | - disabled-color="#ffffff" :placeholder="`请选择${item.fieldName}`" border="none"> | |
| 91 | + disabled-color="#ffffff" :placeholder="`请选择${item.fieldName}`" border="none"> | |
| 106 | 92 | <template #right> |
| 107 | 93 | <up-icon name="arrow-right" size="16"></up-icon> |
| 108 | 94 | </template> |
| 109 | 95 | </up-input> |
| 110 | 96 | <!-- type10 链接:URL 输入,提交裸 URL --> |
| 111 | 97 | <up-input v-else-if="item.fieldType === 10" v-model.trim="inspectForm[item.fieldCode]" |
| 112 | - :placeholder="`请输入${item.fieldName}(http/https 开头)`"></up-input> | |
| 98 | + :placeholder="`请输入${item.fieldName}(http/https 开头)`"></up-input> | |
| 113 | 99 | <!-- type11 地理位置:uni.chooseLocation 选点,提交 JSON 对象 {lng,lat,address} --> |
| 114 | 100 | <up-input v-else-if="item.fieldType === 11" v-model="labelMap[item.fieldCode]" readonly |
| 115 | - disabled-color="#ffffff" :placeholder="`点击选择${item.fieldName}`" border="none"> | |
| 101 | + disabled-color="#ffffff" :placeholder="`点击选择${item.fieldName}`" border="none"> | |
| 116 | 102 | <template #right> |
| 117 | 103 | <up-icon name="arrow-right" size="16"></up-icon> |
| 118 | 104 | </template> |
| ... | ... | @@ -138,38 +124,40 @@ |
| 138 | 124 | </up-form-item> --> |
| 139 | 125 | </up-form> |
| 140 | 126 | </view> |
| 141 | - <!-- 底部提交按钮 --> | |
| 142 | - <view v-if="dynamicFields.length === 0" class="empty-field-tip"> | |
| 143 | - <text>请去平台配置业务类型字段</text> | |
| 144 | -</view> | |
| 145 | -<!-- ========== 底部提交按钮:只有存在动态字段才显示 ========== --> | |
| 146 | -<view class="fixed-bottom-btn-wrap" v-if="dynamicFields.length > 0"> | |
| 147 | -<up-button type="primary" text="提交" @click="submitInspect" | |
| 148 | -style="width: 100%; height: 88rpx; font-size:32rpx; border-radius: 0;"></up-button> | |
| 149 | -</view> | |
| 127 | + <!-- 底部双按钮 --> | |
| 128 | + <view v-if="dynamicFields.length === 0" class="empty-field-tip"> | |
| 129 | + <text>请去平台配置业务类型字段</text> | |
| 130 | + </view> | |
| 131 | + <view class="fixed-bottom-btn-wrap" v-if="dynamicFields.length > 0"> | |
| 132 | + <view class="btn-item btn-left" @click="submitInspect"> | |
| 133 | + <text>提交</text> | |
| 134 | + </view> | |
| 135 | + <view class="btn-item btn-right" @click="submitAndFinish"> | |
| 136 | + <text>提交并结束本次任务</text> | |
| 137 | + </view> | |
| 138 | + </view> | |
| 150 | 139 | <!-- uview-plus 日期时间选择器(全局复用) --> |
| 151 | 140 | <up-datetime-picker :show="datePickerShow" v-model="datePickerValue" mode="datetime" close-on-click-overlay |
| 152 | - @confirm="onDateConfirm" @cancel="onDateClose" @close="onDateClose"></up-datetime-picker> | |
| 141 | + @confirm="onDateConfirm" @cancel="onDateClose" @close="onDateClose"></up-datetime-picker> | |
| 153 | 142 | <!-- ActionSheet 底部选择弹窗 --> |
| 154 | 143 | <up-action-sheet :show="actionSheet.show" :actions="actionSheet.options" @select="onActionSheetSelect" |
| 155 | - @close="actionSheet.show = false" cancel-text="取消"></up-action-sheet> | |
| 144 | + @close="actionSheet.show = false" cancel-text="取消"></up-action-sheet> | |
| 156 | 145 | <!-- 分层选项选择器(选项带 path 的动态字段) --> |
| 157 | 146 | <grouped-picker :show="groupPicker.show" :title="groupPicker.title" :options="groupPicker.options" |
| 158 | - :multiple="groupPicker.multiple" :max-count="groupPicker.maxCount" | |
| 159 | - :need-quantity="groupPicker.needQuantity" :value="groupPicker.value" | |
| 160 | - :container-width="groupPicker.containerWidth" | |
| 161 | - :quantities="groupPicker.quantities" @update:show="groupPicker.show = $event" | |
| 162 | - @confirm="onGroupedConfirm"></grouped-picker> | |
| 147 | + :multiple="groupPicker.multiple" :max-count="groupPicker.maxCount" :need-quantity="groupPicker.needQuantity" | |
| 148 | + :value="groupPicker.value" :container-width="groupPicker.containerWidth" :quantities="groupPicker.quantities" | |
| 149 | + @update:show="groupPicker.show = $event" @confirm="onGroupedConfirm"></grouped-picker> | |
| 163 | 150 | </view> |
| 164 | 151 | </template> |
| 165 | 152 | |
| 166 | 153 | <script lang="ts"> |
| 167 | 154 | import { uploadImages } from '@/common/utils/upload' |
| 168 | -import { maintainCreate } from "@/api/maintain-manage/maintain-manage" | |
| 155 | +import { maintainCreate, maintainCreateFinish } from "@/api/maintain-manage/maintain-manage" | |
| 169 | 156 | import { getByType, getOptionsByCode, getMaterialByCode, workRecordSubmit } from "@/api/common" |
| 170 | 157 | export default { |
| 171 | 158 | data() { |
| 172 | 159 | return { |
| 160 | + submitting: false, | |
| 173 | 161 | imagesList: [], |
| 174 | 162 | initProgress: 0, |
| 175 | 163 | inspectForm: { |
| ... | ... | @@ -234,7 +222,7 @@ export default { |
| 234 | 222 | // 数量单位不再传进弹层(原型列表行右侧只有步进器,没有单位),页面行内仍显示 item.unit |
| 235 | 223 | unit: '', |
| 236 | 224 | // 新增:由父页面点击时赋值 |
| 237 | - containerWidth: 0 | |
| 225 | + containerWidth: 0 | |
| 238 | 226 | } |
| 239 | 227 | } |
| 240 | 228 | }, |
| ... | ... | @@ -328,15 +316,15 @@ export default { |
| 328 | 316 | this.groupPicker.needQuantity = item.fieldType === 6 && this.needQty(item) |
| 329 | 317 | this.groupPicker.value = multiple ? (this.inspectForm[code] || []).slice() : (this.inspectForm[code] || '') |
| 330 | 318 | this.groupPicker.quantities = Object.assign({}, this.qtyMap[code] || {}) |
| 331 | - // ========== 新增:点击打开弹窗瞬间,获取父表单容器宽度 ========== | |
| 332 | - const query = uni.createSelectorQuery().in(this) | |
| 333 | - query.select('.inspect-form-content').boundingClientRect((rect) => { | |
| 334 | - if (rect && rect.width) { | |
| 335 | - // rect.width 单位 px,直接传给子组件 | |
| 336 | - this.groupPicker.containerWidth = rect.width | |
| 337 | - } | |
| 338 | - this.groupPicker.show = true | |
| 339 | - }).exec() | |
| 319 | + // ========== 新增:点击打开弹窗瞬间,获取父表单容器宽度 ========== | |
| 320 | + const query = uni.createSelectorQuery().in(this) | |
| 321 | + query.select('.inspect-form-content').boundingClientRect((rect) => { | |
| 322 | + if (rect && rect.width) { | |
| 323 | + // rect.width 单位 px,直接传给子组件 | |
| 324 | + this.groupPicker.containerWidth = rect.width | |
| 325 | + } | |
| 326 | + this.groupPicker.show = true | |
| 327 | + }).exec() | |
| 340 | 328 | }, |
| 341 | 329 | /** |
| 342 | 330 | * 分层弹层确定回调。写入位置与原有两条路径完全一致(inspectForm/labelMap/qtyMap), |
| ... | ... | @@ -363,8 +351,8 @@ export default { |
| 363 | 351 | // 物料带 currentNum 时在展示名后附库存余量(只影响展示名,提交的 materialName 仍取干净的 opt.label) |
| 364 | 352 | this.actionSheet.options = (item.selectOptions || []).map(opt => { |
| 365 | 353 | const stock = opt.currentNum === null || opt.currentNum === undefined || opt.currentNum === '' |
| 366 | - ? NaN | |
| 367 | - : Number(opt.currentNum) | |
| 354 | + ? NaN | |
| 355 | + : Number(opt.currentNum) | |
| 368 | 356 | return { |
| 369 | 357 | name: isNaN(stock) ? opt.label : `${opt.label}(库存${stock}${opt.unit || ''})`, |
| 370 | 358 | value: opt.value |
| ... | ... | @@ -487,9 +475,9 @@ export default { |
| 487 | 475 | // 3物料为标量(领用数量),默认 0 = 本次不领用、不扣库存 |
| 488 | 476 | if (field.fieldType === 6 && this.needQty(field)) { |
| 489 | 477 | const qty = {} |
| 490 | - ;(field.selectOptions || []).forEach(opt => { | |
| 491 | - qty[opt.value] = '' | |
| 492 | - }) | |
| 478 | + ; (field.selectOptions || []).forEach(opt => { | |
| 479 | + qty[opt.value] = '' | |
| 480 | + }) | |
| 493 | 481 | this.qtyMap[field.fieldCode] = qty |
| 494 | 482 | } |
| 495 | 483 | if (field.fieldType === 3 && field.optionSource === 3) { |
| ... | ... | @@ -657,50 +645,50 @@ export default { |
| 657 | 645 | * 7/8/9/11 的值是数组或对象,async-validator 的 required 按默认 string 类型判定, |
| 658 | 646 | * 会放过「空数组」「null」⇒ 必须用 validator 显式判空,否则未选也能提交 |
| 659 | 647 | */ |
| 660 | -buildRequiredRule(field: any) { | |
| 661 | - const type = field.fieldType | |
| 662 | - if (type === 6 || type === 7 || type === 8) { | |
| 663 | - const action = type === 6 ? '请选择' : '请上传' | |
| 664 | - return { | |
| 665 | - required: true, | |
| 666 | - trigger: ['change','blur'], | |
| 667 | - validator: (rule, value, callback) => { | |
| 668 | - Array.isArray(value) && value.length > 0 ? callback() : callback(new Error(`${action}${field.fieldName}`)) | |
| 648 | + buildRequiredRule(field: any) { | |
| 649 | + const type = field.fieldType | |
| 650 | + if (type === 6 || type === 7 || type === 8) { | |
| 651 | + const action = type === 6 ? '请选择' : '请上传' | |
| 652 | + return { | |
| 653 | + required: true, | |
| 654 | + trigger: ['change', 'blur'], | |
| 655 | + validator: (rule, value, callback) => { | |
| 656 | + Array.isArray(value) && value.length > 0 ? callback() : callback(new Error(`${action}${field.fieldName}`)) | |
| 657 | + } | |
| 658 | + } | |
| 669 | 659 | } |
| 670 | - } | |
| 671 | - } | |
| 672 | - if (type === 9 || type === 11) { | |
| 673 | - return { | |
| 674 | - required: true, | |
| 675 | - trigger: ['change','blur'], | |
| 676 | - validator: (rule, value, callback) => { | |
| 677 | - value ? callback() : callback(new Error(type === 9 ? `请选择${field.fieldName}` : `请选择${field.fieldName}的位置`)) | |
| 660 | + if (type === 9 || type === 11) { | |
| 661 | + return { | |
| 662 | + required: true, | |
| 663 | + trigger: ['change', 'blur'], | |
| 664 | + validator: (rule, value, callback) => { | |
| 665 | + value ? callback() : callback(new Error(type === 9 ? `请选择${field.fieldName}` : `请选择${field.fieldName}的位置`)) | |
| 666 | + } | |
| 667 | + } | |
| 678 | 668 | } |
| 679 | - } | |
| 680 | - } | |
| 681 | - // ========= 新增:数值类型单独校验 ========= | |
| 682 | - if (type === 1) { | |
| 683 | - return { | |
| 684 | - required: true, | |
| 685 | - trigger: ['change','blur'], | |
| 686 | - validator: (rule, value, callback) => { | |
| 687 | - // 空字符串、null、undefined 才是空;数字 1,7,100 全部合法 | |
| 688 | - if (value === '' || value === null || value === undefined) { | |
| 689 | - callback(new Error(`请填写${field.fieldName}`)) | |
| 690 | - } else { | |
| 691 | - callback() | |
| 669 | + // ========= 新增:数值类型单独校验 ========= | |
| 670 | + if (type === 1) { | |
| 671 | + return { | |
| 672 | + required: true, | |
| 673 | + trigger: ['change', 'blur'], | |
| 674 | + validator: (rule, value, callback) => { | |
| 675 | + // 空字符串、null、undefined 才是空;数字 1,7,100 全部合法 | |
| 676 | + if (value === '' || value === null || value === undefined) { | |
| 677 | + callback(new Error(`请填写${field.fieldName}`)) | |
| 678 | + } else { | |
| 679 | + callback() | |
| 680 | + } | |
| 681 | + } | |
| 692 | 682 | } |
| 693 | 683 | } |
| 694 | - } | |
| 695 | - } | |
| 696 | - // 2文本 /4日期 /5布尔 /10链接 | |
| 697 | - const action = type === 3 || type === 5 ? '请选择' : '请填写' | |
| 698 | - return { | |
| 699 | - required: true, | |
| 700 | - message: `${action}${field.fieldName}`, | |
| 701 | - trigger: ['change','blur'] | |
| 702 | - } | |
| 703 | -}, | |
| 684 | + // 2文本 /4日期 /5布尔 /10链接 | |
| 685 | + const action = type === 3 || type === 5 ? '请选择' : '请填写' | |
| 686 | + return { | |
| 687 | + required: true, | |
| 688 | + message: `${action}${field.fieldName}`, | |
| 689 | + trigger: ['change', 'blur'] | |
| 690 | + } | |
| 691 | + }, | |
| 704 | 692 | |
| 705 | 693 | /** 幂等流水号(后端按 userId+clientSeq 去重,命中则返回已存在的 recordId) */ |
| 706 | 694 | genClientSeq() { |
| ... | ... | @@ -727,26 +715,26 @@ buildRequiredRule(field: any) { |
| 727 | 715 | /** |
| 728 | 716 | * 7图片 上传(u-upload 的 after-read 回调)【对齐工单上传逻辑】 |
| 729 | 717 | */ |
| 730 | -uploadFieldFiles(item: any, event: any) { | |
| 731 | - console.log('动态图片上传', item.fieldName, event) | |
| 732 | - const rawFile = event && event.file | |
| 733 | - const fileList = Array.isArray(rawFile) ? rawFile : [rawFile] | |
| 734 | - const targetImgList = this.fileListMap[item.fieldCode] || (this.fileListMap[item.fieldCode] = []) | |
| 735 | - const filePaths = fileList.map(file => file.url) | |
| 736 | - // const tempItems = fileList.map(item => ({ | |
| 737 | - // ...item, | |
| 738 | - // status: 'uploading', | |
| 739 | - // message: '上传中' | |
| 740 | - // })) | |
| 741 | - // const startIndex = targetImgList.length | |
| 742 | - // targetImgList.push(...tempItems) | |
| 718 | + uploadFieldFiles(item: any, event: any) { | |
| 719 | + console.log('动态图片上传', item.fieldName, event) | |
| 720 | + const rawFile = event && event.file | |
| 721 | + const fileList = Array.isArray(rawFile) ? rawFile : [rawFile] | |
| 722 | + const targetImgList = this.fileListMap[item.fieldCode] || (this.fileListMap[item.fieldCode] = []) | |
| 723 | + const filePaths = fileList.map(file => file.url) | |
| 724 | + // const tempItems = fileList.map(item => ({ | |
| 725 | + // ...item, | |
| 726 | + // status: 'uploading', | |
| 727 | + // message: '上传中' | |
| 728 | + // })) | |
| 729 | + // const startIndex = targetImgList.length | |
| 730 | + // targetImgList.push(...tempItems) | |
| 743 | 731 | |
| 744 | - this.uploadToField(item, fileList.filter(file => file && file.url).map(file => ({ | |
| 745 | - url: file.url, | |
| 746 | - name: file.name || '', | |
| 747 | - size: file.size | |
| 748 | - }))) | |
| 749 | -}, | |
| 732 | + this.uploadToField(item, fileList.filter(file => file && file.url).map(file => ({ | |
| 733 | + url: file.url, | |
| 734 | + name: file.name || '', | |
| 735 | + size: file.size | |
| 736 | + }))) | |
| 737 | + }, | |
| 750 | 738 | |
| 751 | 739 | /** |
| 752 | 740 | * 8附件 选择并上传。 |
| ... | ... | @@ -783,65 +771,65 @@ uploadFieldFiles(item: any, event: any) { |
| 783 | 771 | * 把选中的本地文件逐个上传并把 http 地址回填到 fileListMap / inspectForm。 |
| 784 | 772 | * 7图片与8附件共用(uploadImages 是通用 uni.uploadFile,不限图片类型) |
| 785 | 773 | */ |
| 786 | - async uploadToField(item: any, files: any[]) { | |
| 787 | -const code = item.fieldCode | |
| 788 | -const maxCount = this.maxCountOf(item, 5) | |
| 789 | -const maxSizeMb = this.maxSizeMbOf(item, 5) | |
| 790 | -const validFiles = (files || []).filter(file => file && file.url && !this.isOversize(file, maxSizeMb)) | |
| 791 | -if (!validFiles.length) { | |
| 792 | -return | |
| 793 | -} | |
| 794 | -const targetList = this.fileListMap[code] || (this.fileListMap[code] = []) | |
| 795 | -if (targetList.length + validFiles.length > maxCount) { | |
| 796 | -uni.showToast({ title: `最多只能上传${maxCount}个`, icon: 'none' }) | |
| 797 | -return | |
| 798 | -} | |
| 799 | -// 保存起始下标 | |
| 800 | -const startIndex = targetList.length | |
| 801 | -// 只在这里push一次 | |
| 802 | -targetList.push(...validFiles.map(file => ({ | |
| 803 | -url: file.url, status: 'uploading', message: '上传中', name: file.name || '' | |
| 804 | -}))) | |
| 805 | -try { | |
| 806 | -const uploadResultUrls = await uploadImages({ | |
| 807 | -filePaths: validFiles.map(file => file.url), | |
| 808 | -ignoreError: true | |
| 774 | + async uploadToField(item: any, files: any[]) { | |
| 775 | + const code = item.fieldCode | |
| 776 | + const maxCount = this.maxCountOf(item, 5) | |
| 777 | + const maxSizeMb = this.maxSizeMbOf(item, 5) | |
| 778 | + const validFiles = (files || []).filter(file => file && file.url && !this.isOversize(file, maxSizeMb)) | |
| 779 | + if (!validFiles.length) { | |
| 780 | + return | |
| 781 | + } | |
| 782 | + const targetList = this.fileListMap[code] || (this.fileListMap[code] = []) | |
| 783 | + if (targetList.length + validFiles.length > maxCount) { | |
| 784 | + uni.showToast({ title: `最多只能上传${maxCount}个`, icon: 'none' }) | |
| 785 | + return | |
| 786 | + } | |
| 787 | + // 保存起始下标 | |
| 788 | + const startIndex = targetList.length | |
| 789 | + // 只在这里push一次 | |
| 790 | + targetList.push(...validFiles.map(file => ({ | |
| 791 | + url: file.url, status: 'uploading', message: '上传中', name: file.name || '' | |
| 792 | + }))) | |
| 793 | + try { | |
| 794 | + const uploadResultUrls = await uploadImages({ | |
| 795 | + filePaths: validFiles.map(file => file.url), | |
| 796 | + ignoreError: true | |
| 809 | 797 | }) |
| 810 | -uploadResultUrls.forEach((url, index) => { | |
| 811 | -// 用startIndex + index 替换对应条目 | |
| 812 | -if (targetList[startIndex + index]) { | |
| 813 | -targetList.splice(startIndex + index, 1, { | |
| 814 | -url, status: 'success', message: '', name: validFiles[index].name || '' | |
| 798 | + uploadResultUrls.forEach((url, index) => { | |
| 799 | + // 用startIndex + index 替换对应条目 | |
| 800 | + if (targetList[startIndex + index]) { | |
| 801 | + targetList.splice(startIndex + index, 1, { | |
| 802 | + url, status: 'success', message: '', name: validFiles[index].name || '' | |
| 815 | 803 | }) |
| 816 | 804 | } |
| 817 | 805 | }) |
| 818 | -for (let i = uploadResultUrls.length; i < validFiles.length; i++) { | |
| 819 | -if (targetList[startIndex + i]) { | |
| 820 | -targetList.splice(startIndex + i, 1, { | |
| 821 | -url: validFiles[i].url, status: 'failed', message: '上传失败', name: validFiles[i].name || '' | |
| 806 | + for (let i = uploadResultUrls.length; i < validFiles.length; i++) { | |
| 807 | + if (targetList[startIndex + i]) { | |
| 808 | + targetList.splice(startIndex + i, 1, { | |
| 809 | + url: validFiles[i].url, status: 'failed', message: '上传失败', name: validFiles[i].name || '' | |
| 822 | 810 | }) |
| 823 | 811 | } |
| 824 | 812 | } |
| 825 | -if (uploadResultUrls.length < validFiles.length) { | |
| 826 | -uni.showToast({ | |
| 827 | -title: `成功${uploadResultUrls.length}个,失败${validFiles.length - uploadResultUrls.length}个`, | |
| 828 | -icon: 'none' | |
| 813 | + if (uploadResultUrls.length < validFiles.length) { | |
| 814 | + uni.showToast({ | |
| 815 | + title: `成功${uploadResultUrls.length}个,失败${validFiles.length - uploadResultUrls.length}个`, | |
| 816 | + icon: 'none' | |
| 829 | 817 | }) |
| 830 | 818 | } |
| 831 | 819 | } catch (err) { |
| 832 | -console.error(`【${code}】文件上传失败:`, err) | |
| 833 | -for (let i = 0; i < validFiles.length; i++) { | |
| 834 | -if (targetList[startIndex + i]) { | |
| 835 | -targetList.splice(startIndex + i, 1, { | |
| 836 | -url: validFiles[i].url, status: 'failed', message: '上传失败', name: validFiles[i].name || '' | |
| 820 | + console.error(`【${code}】文件上传失败:`, err) | |
| 821 | + for (let i = 0; i < validFiles.length; i++) { | |
| 822 | + if (targetList[startIndex + i]) { | |
| 823 | + targetList.splice(startIndex + i, 1, { | |
| 824 | + url: validFiles[i].url, status: 'failed', message: '上传失败', name: validFiles[i].name || '' | |
| 837 | 825 | }) |
| 838 | 826 | } |
| 839 | 827 | } |
| 840 | -uni.showToast({ title: '上传失败,请重试', icon: 'none' }) | |
| 828 | + uni.showToast({ title: '上传失败,请重试', icon: 'none' }) | |
| 841 | 829 | } |
| 842 | -this.syncFileValue(item) | |
| 843 | -this.$refs.inspectFormRef.validateField(code) | |
| 844 | -}, | |
| 830 | + this.syncFileValue(item) | |
| 831 | + this.$refs.inspectFormRef.validateField(code) | |
| 832 | + }, | |
| 845 | 833 | |
| 846 | 834 | /** |
| 847 | 835 | * 删除 7图片/8附件 的某一个已选文件 |
| ... | ... | @@ -893,12 +881,12 @@ this.$refs.inspectFormRef.validateField(code) |
| 893 | 881 | fields.push(row) |
| 894 | 882 | } else if (type === 3) { |
| 895 | 883 | // 3枚举:单选取裸 value;历史「枚举+是否多选」存逗号分隔 |
| 896 | - fields.push({ | |
| 897 | - fieldCode: code, | |
| 898 | - valueText: item.multiple === 1 | |
| 899 | - ? (Array.isArray(value) ? value.join(',') : String(value || '')) | |
| 900 | - : String(value) | |
| 901 | -}) | |
| 884 | + fields.push({ | |
| 885 | + fieldCode: code, | |
| 886 | + valueText: item.multiple === 1 | |
| 887 | + ? (Array.isArray(value) ? value.join(',') : String(value || '')) | |
| 888 | + : String(value) | |
| 889 | + }) | |
| 902 | 890 | } else if (type === 6) { |
| 903 | 891 | const selected = (item.selectOptions || []).filter(opt => (value || []).indexOf(opt.value) > -1) |
| 904 | 892 | if (this.needQty(item)) { |
| ... | ... | @@ -934,6 +922,70 @@ this.$refs.inspectFormRef.validateField(code) |
| 934 | 922 | } |
| 935 | 923 | return fields |
| 936 | 924 | }, |
| 925 | + | |
| 926 | + async submitAndFinish() { | |
| 927 | + | |
| 928 | + try { | |
| 929 | + await this.$refs.inspectFormRef.validate() | |
| 930 | + const dynamicValues = this.buildDynamicFields() | |
| 931 | + if (!this.clientSeq) { | |
| 932 | + this.clientSeq = this.genClientSeq() | |
| 933 | + } | |
| 934 | + uni.showLoading({ title: '提交中...' }) | |
| 935 | + console.log('dsahdjashj') | |
| 936 | + // this.submitting = true | |
| 937 | + | |
| 938 | + // if (!this.commitId) { | |
| 939 | + // const submitData = { | |
| 940 | + // planNo: this.paramsOptins.planNo, | |
| 941 | + // imgHost: this.getImgUrlList(this.imagesList), | |
| 942 | + // commonUserList: [], | |
| 943 | + // remark: this.inspectForm.remark.trim(), | |
| 944 | + // } | |
| 945 | + // const commitRes: any = await maintainCreate(submitData) | |
| 946 | + // this.commitId = commitRes && typeof commitRes === 'object' ? commitRes.id : commitRes | |
| 947 | + // } | |
| 948 | + | |
| 949 | + // if (dynamicValues.length > 0 && this.commitId) { | |
| 950 | + // await workRecordSubmit({ | |
| 951 | + // businessType: this.paramsOptins.businessType, | |
| 952 | + // bizType: 3, | |
| 953 | + // bizId: this.commitId, | |
| 954 | + // workTypeId: Number(this.paramsOptins.planTypeId), | |
| 955 | + // clientSeq: this.clientSeq, | |
| 956 | + // remark: this.inspectForm.remark.trim(), | |
| 957 | + // fields: dynamicValues | |
| 958 | + // }) | |
| 959 | + // } | |
| 960 | + | |
| 961 | + // 新增:调用【提交并结束】接口 | |
| 962 | + await maintainCreateFinish({ | |
| 963 | + submitData | |
| 964 | + }) | |
| 965 | + | |
| 966 | + uni.hideLoading() | |
| 967 | + uni.showToast({ title: '提交并结束任务成功', icon: 'success', duration: 1000 }) | |
| 968 | + setTimeout(() => { | |
| 969 | + uni.reLaunch({ url: '/pages-sub/daily/maintain-manage/index' }) | |
| 970 | + }, 1000) | |
| 971 | + } catch (error) { | |
| 972 | + uni.hideLoading() | |
| 973 | + // if (Array.isArray(error)) return | |
| 974 | + // console.error('提交并结束失败', error) | |
| 975 | + // const backendMsg = error && error.msg | |
| 976 | + // if (this.commitId) { | |
| 977 | + // uni.showToast({ | |
| 978 | + // title: backendMsg ? `养护记录已提交:${backendMsg}` : '养护记录已提交,结束任务接口失败', | |
| 979 | + // icon: 'none', duration: 2500 | |
| 980 | + // }) | |
| 981 | + // } else if (!backendMsg) { | |
| 982 | + // uni.showToast({ title: '提交失败,请重试', icon: 'none', duration: 2000 }) | |
| 983 | + // } | |
| 984 | + } finally { | |
| 985 | + // this.submitting = false | |
| 986 | + } | |
| 987 | + }, | |
| 988 | + | |
| 937 | 989 | /** |
| 938 | 990 | * 提交养护记录 —— 双写两条链路: |
| 939 | 991 | * ① maintain-plan-commit/create:养护计划本身(描述 + 图片 + 人员),返回 commit.id |
| ... | ... | @@ -942,11 +994,12 @@ this.$refs.inspectFormRef.validateField(code) |
| 942 | 994 | */ |
| 943 | 995 | async submitInspect() { |
| 944 | 996 | // 调试打印,打开小程序控制台查看 |
| 945 | - console.log('=====调试=====') | |
| 946 | - console.log('dynamicFields', this.dynamicFields) | |
| 947 | - this.dynamicFields.forEach(item=>{ | |
| 948 | - console.log(item.fieldCode, this.inspectForm[item.fieldCode], typeof this.inspectForm[item.fieldCode]) | |
| 949 | - }) | |
| 997 | + console.log('=====调试=====') | |
| 998 | + console.log('dynamicFields', this.dynamicFields) | |
| 999 | + if (this.submitting) return | |
| 1000 | + this.dynamicFields.forEach(item => { | |
| 1001 | + console.log(item.fieldCode, this.inspectForm[item.fieldCode], typeof this.inspectForm[item.fieldCode]) | |
| 1002 | + }) | |
| 950 | 1003 | try { |
| 951 | 1004 | await this.$refs.inspectFormRef.validate() |
| 952 | 1005 | const dynamicValues = this.buildDynamicFields() |
| ... | ... | @@ -954,6 +1007,7 @@ this.$refs.inspectFormRef.validateField(code) |
| 954 | 1007 | this.clientSeq = this.genClientSeq() |
| 955 | 1008 | } |
| 956 | 1009 | uni.showLoading({ title: '提交中...' }) |
| 1010 | + this.submitting = true | |
| 957 | 1011 | // ① 养护计划本体:仅首次提交时写入 |
| 958 | 1012 | if (!this.commitId) { |
| 959 | 1013 | const submitData = { |
| ... | ... | @@ -1017,11 +1071,13 @@ this.$refs.inspectFormRef.validateField(code) |
| 1017 | 1071 | .page-container { |
| 1018 | 1072 | padding-bottom: 120rpx; |
| 1019 | 1073 | } |
| 1074 | + | |
| 1020 | 1075 | .inspect-form-content { |
| 1021 | 1076 | background: #fff; |
| 1022 | 1077 | padding: 20rpx; |
| 1023 | 1078 | margin-bottom: 20rpx; |
| 1024 | 1079 | } |
| 1080 | + | |
| 1025 | 1081 | .date-input-wrap { |
| 1026 | 1082 | width: 100%; |
| 1027 | 1083 | } |
| ... | ... | @@ -1033,35 +1089,42 @@ this.$refs.inspectFormRef.validateField(code) |
| 1033 | 1089 | height: 72rpx; |
| 1034 | 1090 | font-size: 32rpx; |
| 1035 | 1091 | } |
| 1092 | + | |
| 1036 | 1093 | :deep(.u-number-box__btn) { |
| 1037 | 1094 | width: 64rpx; |
| 1038 | 1095 | height: 72rpx; |
| 1039 | 1096 | } |
| 1040 | 1097 | } |
| 1098 | + | |
| 1041 | 1099 | .number-box-wrap { |
| 1042 | 1100 | display: flex; |
| 1043 | 1101 | align-items: center; |
| 1044 | 1102 | gap: 16rpx; |
| 1103 | + | |
| 1045 | 1104 | .unit-text { |
| 1046 | 1105 | font-size: 28rpx; |
| 1047 | 1106 | color: #333; |
| 1048 | 1107 | } |
| 1049 | 1108 | } |
| 1109 | + | |
| 1050 | 1110 | // 6多选:复选框组 + needQuantity 时每个已选项一行数量输入 |
| 1051 | 1111 | .multi-wrap { |
| 1052 | 1112 | width: 100%; |
| 1113 | + | |
| 1053 | 1114 | .multi-hint { |
| 1054 | 1115 | display: block; |
| 1055 | 1116 | margin-top: 8rpx; |
| 1056 | 1117 | font-size: 24rpx; |
| 1057 | 1118 | color: #909399; |
| 1058 | 1119 | } |
| 1120 | + | |
| 1059 | 1121 | .qty-list { |
| 1060 | 1122 | margin-top: 16rpx; |
| 1061 | 1123 | padding-left: 20rpx; |
| 1062 | 1124 | border-left: 4rpx solid #f0f0f0; |
| 1063 | 1125 | } |
| 1064 | 1126 | } |
| 1127 | + | |
| 1065 | 1128 | // 数量行(6多选的每个已选项 / 3物料的领用数量共用) |
| 1066 | 1129 | .qty-row { |
| 1067 | 1130 | display: flex; |
| ... | ... | @@ -1069,6 +1132,7 @@ this.$refs.inspectFormRef.validateField(code) |
| 1069 | 1132 | justify-content: space-between; |
| 1070 | 1133 | gap: 16rpx; |
| 1071 | 1134 | padding: 8rpx 0; |
| 1135 | + | |
| 1072 | 1136 | .qty-label { |
| 1073 | 1137 | flex: 1; |
| 1074 | 1138 | min-width: 0; |
| ... | ... | @@ -1076,20 +1140,24 @@ this.$refs.inspectFormRef.validateField(code) |
| 1076 | 1140 | color: #333; |
| 1077 | 1141 | } |
| 1078 | 1142 | } |
| 1143 | + | |
| 1079 | 1144 | .qty-row-material { |
| 1080 | 1145 | margin-top: 12rpx; |
| 1081 | 1146 | padding-top: 12rpx; |
| 1082 | 1147 | border-top: 1rpx solid #f0f0f0; |
| 1083 | 1148 | } |
| 1149 | + | |
| 1084 | 1150 | // 8附件:自绘文件列表 + 选择按钮(微信无通用文件选择器,走 chooseMessageFile) |
| 1085 | 1151 | .file-wrap { |
| 1086 | 1152 | width: 100%; |
| 1153 | + | |
| 1087 | 1154 | .file-row { |
| 1088 | 1155 | display: flex; |
| 1089 | 1156 | align-items: center; |
| 1090 | 1157 | justify-content: space-between; |
| 1091 | 1158 | gap: 16rpx; |
| 1092 | 1159 | padding: 8rpx 0; |
| 1160 | + | |
| 1093 | 1161 | .file-name { |
| 1094 | 1162 | flex: 1; |
| 1095 | 1163 | min-width: 0; |
| ... | ... | @@ -1099,6 +1167,7 @@ this.$refs.inspectFormRef.validateField(code) |
| 1099 | 1167 | white-space: nowrap; |
| 1100 | 1168 | text-overflow: ellipsis; |
| 1101 | 1169 | } |
| 1170 | + | |
| 1102 | 1171 | .file-del { |
| 1103 | 1172 | font-size: 26rpx; |
| 1104 | 1173 | color: #ff4d4f; |
| ... | ... | @@ -1113,5 +1182,38 @@ this.$refs.inspectFormRef.validateField(code) |
| 1113 | 1182 | color: #999; |
| 1114 | 1183 | } |
| 1115 | 1184 | |
| 1185 | +.fixed-bottom-btn-wrap { | |
| 1186 | + background-color: #ffffff; | |
| 1187 | + display: flex; | |
| 1188 | + .btn-item { | |
| 1189 | + flex: 1; | |
| 1190 | + height: 88rpx; | |
| 1191 | + display: flex; | |
| 1192 | + align-items: center; | |
| 1193 | + justify-content: center; | |
| 1194 | + font-size: 32rpx; | |
| 1195 | + color: #fff; | |
| 1196 | + } | |
| 1197 | + | |
| 1198 | + .btn-left { | |
| 1199 | + background: #1989fa; | |
| 1200 | + } | |
| 1201 | + | |
| 1202 | + .btn-right { | |
| 1203 | + background: #8b572a; | |
| 1204 | + /* 褐色 */ | |
| 1205 | + } | |
| 1206 | +} | |
| 1207 | + | |
| 1208 | +// 页面底部预留空白,防止表单内容被固定按钮遮挡 | |
| 1209 | +.page-container { | |
| 1210 | + padding-bottom: calc(120rpx + env(safe-area-inset-bottom)); | |
| 1211 | +} | |
| 1116 | 1212 | |
| 1213 | +.empty-field-tip { | |
| 1214 | + text-align: center; | |
| 1215 | + padding: 80rpx 20rpx; | |
| 1216 | + font-size: 30rpx; | |
| 1217 | + color: #999; | |
| 1218 | +} | |
| 1117 | 1219 | </style> | ... | ... |
pages-sub/daily/maintain-manage/finish-plan-detail.vue
| ... | ... | @@ -154,7 +154,7 @@ |
| 154 | 154 | |
| 155 | 155 | <script setup lang="ts"> |
| 156 | 156 | import {ref} from 'vue'; |
| 157 | -import {detailList} from "@/api/maintain-manage/maintain-manage"; | |
| 157 | +import {detailList,getDetailList} from "@/api/maintain-manage/maintain-manage"; | |
| 158 | 158 | import {onLoad} from '@dcloudio/uni-app'; |
| 159 | 159 | import {timeFormat} from '@/uni_modules/uview-plus'; |
| 160 | 160 | |
| ... | ... | @@ -199,15 +199,15 @@ const previewImage = (currentUrl, allUrls) => { |
| 199 | 199 | /** |
| 200 | 200 | * 获取工单详情 |
| 201 | 201 | */ |
| 202 | -const getOrderDetail = async (planNo: string) => { | |
| 202 | +const getOrderDetail = async (id: string) => { | |
| 203 | 203 | try { |
| 204 | 204 | loading.value = true; |
| 205 | 205 | let queryData = { |
| 206 | - planNo: planNo, | |
| 207 | - pageSize: 100, | |
| 208 | - pageNo: 1, | |
| 206 | + id: id, | |
| 207 | + // pageSize: 100, | |
| 208 | + // pageNo: 1, | |
| 209 | 209 | }; |
| 210 | - const res = await detailList(queryData); | |
| 210 | + const res = await getDetailList(queryData); | |
| 211 | 211 | console.log('接口返回:', res); |
| 212 | 212 | |
| 213 | 213 | // 修复2:正确解析接口数据(res.data.list 而非 res.list) |
| ... | ... | @@ -527,9 +527,9 @@ const openGeo = (geo) => { |
| 527 | 527 | |
| 528 | 528 | // 页面加载 |
| 529 | 529 | onLoad((options) => { |
| 530 | - const {planNo} = options; | |
| 531 | - if (planNo) { | |
| 532 | - getOrderDetail(planNo); | |
| 530 | + const {id} = options; | |
| 531 | + if (id) { | |
| 532 | + getOrderDetail(id); | |
| 533 | 533 | } else { |
| 534 | 534 | loading.value = false; |
| 535 | 535 | uni.showToast({title: '缺少工单ID', icon: 'none'}); | ... | ... |
pages-sub/daily/maintain-manage/index.vue
| ... | ... | @@ -185,7 +185,7 @@ |
| 185 | 185 | <script setup> |
| 186 | 186 | import { ref, computed } from 'vue' |
| 187 | 187 | import { onLoad, onShow } from '@dcloudio/uni-app' |
| 188 | -import { roadPage, planTypePage } from "@/api/maintain-manage/maintain-manage"; | |
| 188 | +import { maintainPlan } from "@/api/maintain-manage/maintain-manage"; | |
| 189 | 189 | import { timeFormat } from '@/uni_modules/uview-plus'; |
| 190 | 190 | |
| 191 | 191 | // 初始化响应式数据 |
| ... | ... | @@ -274,26 +274,14 @@ const handleSearch = () => { |
| 274 | 274 | const fetchData = async (pageNo, pageSize) => { |
| 275 | 275 | try { |
| 276 | 276 | let res = null |
| 277 | - // 道路类型请求 | |
| 278 | - if (activeType.value === '道路') { | |
| 279 | - res = await roadPage({ | |
| 277 | + const currentType = typeTabs.value.find(item => item.name === activeType.value) | |
| 278 | + res = await maintainPlan({ | |
| 279 | + // planTypeId: currentType?.planTypeId || '', | |
| 280 | 280 | roadName: searchValue.value.trim(), |
| 281 | 281 | pageNo, |
| 282 | 282 | pageSize, |
| 283 | - finishState: activeStatus.value | |
| 283 | + // finishState: activeStatus.value | |
| 284 | 284 | }) |
| 285 | - } else { | |
| 286 | - // 其他养护类型请求 | |
| 287 | - // 找到当前类型对应的planTypeId | |
| 288 | - const currentType = typeTabs.value.find(item => item.name === activeType.value) | |
| 289 | - res = await planTypePage({ | |
| 290 | - planTypeId: currentType?.planTypeId || '', | |
| 291 | - planName: searchValue.value.trim(), | |
| 292 | - pageNo, | |
| 293 | - pageSize, | |
| 294 | - finishState: activeStatus.value | |
| 295 | - }) | |
| 296 | - } | |
| 297 | 285 | |
| 298 | 286 | // 告诉z-paging数据加载完成 |
| 299 | 287 | // res.list为当前页数据,res.total为总条数(如果接口返回) |
| ... | ... | @@ -313,8 +301,9 @@ const goToDetail = (item) => { |
| 313 | 301 | // }) |
| 314 | 302 | |
| 315 | 303 | uni.navigateTo({ |
| 316 | - url: `/pages-sub/daily/maintain-manage/type-list?roadId=${item.roadId}&finish_state=${item.finishState || ''} &businessType=${item.busiLine}` | |
| 317 | - }) | |
| 304 | + url: `/pages-sub/daily/maintain-manage/type-list?roadName=${encodeURIComponent(item.roadName)}&levelId=${item.levelId}` | |
| 305 | +}) | |
| 306 | + | |
| 318 | 307 | } |
| 319 | 308 | |
| 320 | 309 | // 提交记录 | ... | ... |
pages-sub/daily/maintain-manage/now-list-page.vue
| 1 | 1 | <template> |
| 2 | - <view class="page-wrap"> | |
| 3 | - <!-- 顶部提示栏 --> | |
| 4 | - <view class="tip-bar"> | |
| 5 | - 一个任务可提交多次上报记录,每一次作业需如实上报。 | |
| 6 | - </view> | |
| 7 | - | |
| 8 | - <!-- 上报记录列表 --> | |
| 9 | - <view class="list-wrap" v-if="reportList.length > 0"> | |
| 10 | - <view class="report-item" v-for="(item, index) in reportList" :key="index"> | |
| 11 | - <view class="item-title">【第{{ item.no }}次上报】{{ item.taskName }}</view> | |
| 12 | - | |
| 13 | - <view class="item-row item-row-bottom"> | |
| 14 | - <view class="item-time">提交时间:{{ item.submitTime }}</view> | |
| 15 | - <view class="look-btn" @click="handleView(item)">查看</view> | |
| 2 | + <view class="page-wrap"> | |
| 3 | + <!-- 顶部提示栏 --> | |
| 4 | + <view class="tip-bar"> | |
| 5 | + 一个任务可提交多次上报记录,每一次作业需如实上报。 | |
| 6 | + </view> | |
| 7 | + <!-- 上报记录列表 --> | |
| 8 | + <view class="list-wrap" v-if="reportList.length > 0"> | |
| 9 | + <view class="report-item" v-for="(item, index) in reportList" :key="index"> | |
| 10 | + <view class="item-title">【第{{ index+1 }}次上报】{{ item.planName }}{{ item.planName }}</view> | |
| 11 | + <view class="item-row item-row-bottom"> | |
| 12 | + <view class="item-time">提交时间:{{ item.submitTime }}</view> | |
| 13 | + <view class="look-btn" @click="handleView(item)">查看</view> | |
| 14 | + </view> | |
| 15 | + </view> | |
| 16 | + </view> | |
| 17 | + <!-- 暂无数据 --> | |
| 18 | + <view class="empty-wrap" v-else> | |
| 19 | + <empty-view text="暂无数据"></empty-view> | |
| 20 | + </view> | |
| 21 | + <!-- 底部固定按钮 --> | |
| 22 | + <view class="fixed-bottom-btn-wrap"> | |
| 23 | + <up-button type="primary" size="large" block @click="handleAddReport">上报本次记录</up-button> | |
| 16 | 24 | </view> |
| 17 | - </view> | |
| 18 | - </view> | |
| 19 | - | |
| 20 | - <!-- 暂无数据 --> | |
| 21 | - <view class="empty-wrap" v-else> | |
| 22 | - <empty-view text="暂无数据"></empty-view> | |
| 23 | - </view> | |
| 24 | - | |
| 25 | - <!-- 底部固定按钮 --> | |
| 26 | - <view class="fixed-bottom-btn-wrap"> | |
| 27 | - <up-button type="primary" size="large" block @click="handleAddReport">上报本次记录</up-button> | |
| 28 | 25 | </view> |
| 29 | - </view> | |
| 30 | 26 | </template> |
| 31 | - | |
| 32 | 27 | <script setup lang="ts"> |
| 33 | 28 | import { ref } from 'vue' |
| 29 | +import { onShow, onLoad } from '@dcloudio/uni-app' | |
| 30 | +import { maintainPlanList } from "@/api/maintain-manage/maintain-manage"; | |
| 34 | 31 | |
| 35 | 32 | // 上报记录数据 |
| 36 | -const reportList = ref([ | |
| 37 | - { | |
| 38 | - no:3, | |
| 39 | - taskName:'德胜门东大街月季补植', | |
| 40 | - varityName:'果汁阳台月季', | |
| 41 | - submitTime:'2026-06-22 14:34:33' | |
| 42 | - }, | |
| 43 | - { | |
| 44 | - no:2, | |
| 45 | - taskName:'德胜门东大街月季补植', | |
| 46 | - varityName:'蓝色风暴月季', | |
| 47 | - submitTime:'2026-06-21 10:34:33' | |
| 48 | - }, | |
| 49 | - { | |
| 50 | - no:1, | |
| 51 | - taskName:'德胜门东大街月季补植', | |
| 52 | - varityName:'金丝雀月季', | |
| 53 | - submitTime:'2026-06-20 10:34:33' | |
| 54 | - } | |
| 55 | -]) | |
| 33 | +const reportList = ref<any[]>([]) | |
| 34 | +let planNo = ref('') | |
| 35 | +let planTypeId = ref('') | |
| 36 | +let businessType = ref('') | |
| 37 | +// onLoad接收url传过来的planNo | |
| 38 | +onLoad((options) => { | |
| 39 | + if (options.planNo) { | |
| 40 | + planNo.value = options.planNo | |
| 41 | + planTypeId.value = options.planTypeId | |
| 42 | + businessType.value = options.businessType | |
| 43 | + // planTypeId=${item.planTypeId}&businessType=${item.busiLine} | |
| 44 | + getReportList() | |
| 45 | + } else { | |
| 46 | + uni.showToast({ title: 'planNo参数缺失', icon: 'none' }) | |
| 47 | + } | |
| 48 | +}) | |
| 49 | + | |
| 50 | +// 请求上报记录接口 | |
| 51 | +const getReportList = async () => { | |
| 52 | + console.log('3123') | |
| 53 | + uni.showLoading({ title: '加载中...', mask: true }) | |
| 54 | + try { | |
| 55 | + // 传入planNo调取接口 | |
| 56 | + let data = { | |
| 57 | + planNo: planNo.value, | |
| 58 | + pageNo: 1, | |
| 59 | + pageSize: 10 | |
| 60 | + } | |
| 61 | + const res = await maintainPlanList(data) | |
| 62 | + // 接口返回list,无数据则reportList为空,页面自动显示empty | |
| 63 | + reportList.value = res?.list || [] | |
| 64 | + } catch (err) { | |
| 65 | + console.error('获取上报记录失败', err) | |
| 66 | + uni.showToast({ title: '加载失败', icon: 'none' }) | |
| 67 | + } finally { | |
| 68 | + uni.hideLoading() | |
| 69 | + } | |
| 70 | +} | |
| 56 | 71 | |
| 57 | 72 | // 查看上报详情 |
| 58 | 73 | const handleView = (item) => { |
| 59 | - console.log('查看详情', item) | |
| 60 | - uni.navigateTo({ | |
| 61 | - url: `/pages-sub/daily/maintain-manage/finish-plan-detail` | |
| 62 | - }) | |
| 74 | + console.log('查看详情', item) | |
| 75 | + uni.navigateTo({ | |
| 76 | + url: `/pages-sub/daily/maintain-manage/finish-plan-detail?id=${item.commitId}` | |
| 77 | + }) | |
| 63 | 78 | } |
| 64 | - | |
| 65 | -// 新增上报记录 | |
| 79 | +// 新增上报记录,把planNo带到新增页面 | |
| 66 | 80 | const handleAddReport = () => { |
| 67 | - console.log('上报本次记录') | |
| 68 | - uni.navigateTo({ | |
| 69 | - url: `/pages-sub/daily/maintain-manage/add-record` | |
| 70 | - }) | |
| 81 | + console.log('上报本次记录') | |
| 82 | + uni.navigateTo({ | |
| 83 | + url: `/pages-sub/daily/maintain-manage/add-record?planNo=${planNo.value}&planTypeId=${planTypeId.value}&businessType=${businessType.value}` | |
| 84 | + }) | |
| 71 | 85 | } |
| 72 | 86 | </script> |
| 73 | - | |
| 74 | 87 | <style scoped lang="scss"> |
| 75 | 88 | .page-wrap { |
| 76 | - min-height: 100vh; | |
| 77 | - padding-bottom: 120rpx; // 给底部按钮预留空间 | |
| 78 | - background: #ffffff; | |
| 89 | + min-height: 100vh; | |
| 90 | + padding-bottom: 120rpx; // 给底部按钮预留空间 | |
| 91 | + background: #ffffff; | |
| 79 | 92 | } |
| 93 | + | |
| 80 | 94 | // 顶部黄色提示条 |
| 81 | 95 | .tip-bar { |
| 82 | - background-color: #f9e0a2; | |
| 83 | - color: #6b4b00; | |
| 84 | - font-size: 28rpx; | |
| 85 | - padding:24rpx 20rpx; | |
| 86 | - line-height:1.5; | |
| 96 | + background-color: #f9e0a2; | |
| 97 | + color: #6b4b00; | |
| 98 | + font-size: 28rpx; | |
| 99 | + padding: 24rpx 20rpx; | |
| 100 | + line-height: 1.5; | |
| 87 | 101 | } |
| 88 | 102 | |
| 89 | 103 | .list-wrap { |
| 90 | - padding: 0; | |
| 104 | + padding: 0; | |
| 91 | 105 | } |
| 106 | + | |
| 92 | 107 | .report-item { |
| 93 | - padding:30rpx 24rpx; | |
| 94 | - border-bottom:1rpx solid #eee; | |
| 95 | - .item-title { | |
| 96 | - font-size:32rpx; | |
| 97 | - color:#222; | |
| 98 | - font-weight:500; | |
| 99 | - margin-bottom:20rpx; | |
| 100 | - } | |
| 101 | - .item-row { | |
| 102 | - font-size:28rpx; | |
| 103 | - color:#555; | |
| 104 | - margin-bottom:12rpx; | |
| 105 | - } | |
| 106 | - .item-row-bottom { | |
| 107 | - display:flex; | |
| 108 | - justify-content:space-between; | |
| 109 | - align-items:center; | |
| 110 | - margin-bottom:0; | |
| 111 | - } | |
| 112 | - .look-btn { | |
| 113 | - font-size:28rpx; | |
| 114 | - color:#1989fa; | |
| 115 | - } | |
| 108 | + padding: 30rpx 24rpx; | |
| 109 | + border-bottom: 1rpx solid #eee; | |
| 110 | + | |
| 111 | + .item-title { | |
| 112 | + font-size: 32rpx; | |
| 113 | + color: #222; | |
| 114 | + font-weight: 500; | |
| 115 | + margin-bottom: 20rpx; | |
| 116 | + } | |
| 117 | + | |
| 118 | + .item-row { | |
| 119 | + font-size: 28rpx; | |
| 120 | + color: #555; | |
| 121 | + margin-bottom: 12rpx; | |
| 122 | + } | |
| 123 | + | |
| 124 | + .item-row-bottom { | |
| 125 | + display: flex; | |
| 126 | + justify-content: space-between; | |
| 127 | + align-items: center; | |
| 128 | + margin-bottom: 0; | |
| 129 | + } | |
| 130 | + | |
| 131 | + .look-btn { | |
| 132 | + font-size: 28rpx; | |
| 133 | + color: #1989fa; | |
| 134 | + } | |
| 116 | 135 | } |
| 136 | + | |
| 117 | 137 | .empty-wrap { |
| 118 | - padding-top: 120rpx; | |
| 138 | + padding-top: 120rpx; | |
| 119 | 139 | } |
| 120 | 140 | |
| 141 | +.fixed-bottom-btn-wrap { | |
| 142 | + // position: fixed; | |
| 143 | + // bottom: 0; | |
| 144 | + // left:0; | |
| 145 | + // width:100%; | |
| 146 | + // padding:20rpx; | |
| 147 | + // background:#fff; | |
| 148 | +} | |
| 121 | 149 | </style> | ... | ... |
pages-sub/daily/maintain-manage/type-list-page.vue
| 1 | 1 | <template> |
| 2 | - <view class="page-container"> | |
| 3 | - <up-sticky> | |
| 4 | - <view class="sticky-wrap"> | |
| 5 | - <up-tabs | |
| 6 | - v-model="activeStatus" | |
| 7 | - :list="statusTabs" | |
| 8 | - line-width="40rpx" | |
| 9 | - active-color="#1989fa" | |
| 10 | - inactive-color="#666" | |
| 11 | - :scrollable="false" | |
| 12 | - @change="handleStatusChange" | |
| 13 | - ></up-tabs> | |
| 14 | - </view> | |
| 15 | - </up-sticky> | |
| 16 | - <z-paging | |
| 17 | - ref="pagingRef" | |
| 18 | - v-model="currentList" | |
| 19 | - @query="fetchData" | |
| 20 | - :auto-show-system-loading="true" | |
| 21 | - > | |
| 22 | - <template #empty> | |
| 23 | - <empty-view/> | |
| 24 | - </template> | |
| 25 | - <view class="card-list"> | |
| 26 | - <view | |
| 27 | - class="task-card" | |
| 28 | - v-for="(item,index) in currentList" | |
| 29 | - :key="`${item.planNo}_${index}`" | |
| 30 | - > | |
| 31 | - <!-- 第一行:任务名称 + 置为失效 --> | |
| 32 | - <view class="row row-1"> | |
| 33 | - <view class="plan-name u-line-1">{{ item.planName || '无计划名称' }}</view> | |
| 34 | - <view | |
| 35 | - v-if="isMaintainLeader" | |
| 36 | - class="btn-set-invalid" | |
| 37 | - @click.stop="handleSetInvalid(item)" | |
| 38 | - > | |
| 39 | - 置为失效 | |
| 2 | + <view class="page-container"> | |
| 3 | + <up-sticky> | |
| 4 | + <view class="sticky-wrap"> | |
| 5 | + <up-tabs v-model="activeStatus" :list="statusTabs" line-width="40rpx" active-color="#1989fa" | |
| 6 | + inactive-color="#666" :scrollable="false" @change="handleStatusChange"></up-tabs> | |
| 40 | 7 | </view> |
| 41 | - </view> | |
| 42 | - <!-- 第二行:任务次数 + 去作业按钮 --> | |
| 43 | - <view class="row row-2"> | |
| 44 | - <view class="info-text">任务次数:第{{ item.taskTimes || 1 }}次</view> | |
| 45 | - <view class="mini-btn" @click.stop="handleGoWork(item)">去作业</view> | |
| 46 | - </view> | |
| 47 | - <!-- 第三行:有效期 + 结束任务按钮 --> | |
| 48 | - <view class="row row-3"> | |
| 49 | - <view class="info-text u-line-1">有效期:{{ item.beginTime || '-' }}—{{ item.endTime || '-' }}</view> | |
| 50 | - <view class="mini-btn" @click.stop="handleEndTask(item)">结束任务</view> | |
| 51 | - </view> | |
| 52 | - </view> | |
| 53 | - </view> | |
| 54 | - </z-paging> | |
| 55 | - | |
| 56 | - <!-- ========== 置为失效弹窗(参考回退弹窗结构) ========== --> | |
| 57 | - <up-modal | |
| 58 | - :show="invalidModalShow" | |
| 59 | - title="失效原因" | |
| 60 | - :closeOnClickOverlay="false" | |
| 61 | - :showConfirmButton="true" | |
| 62 | - :showCancelButton="true" | |
| 63 | - @cancel="handleInvalidModalCancel" | |
| 64 | - @confirm="confirmSetInvalid" | |
| 65 | - > | |
| 66 | - <view class="invalid-modal-content"> | |
| 67 | - <up-textarea | |
| 68 | - v-model.trim="invalidReason" | |
| 69 | - placeholder="请输入失效原因(必填)" | |
| 70 | - rows="6" | |
| 71 | - :count="200" | |
| 72 | - maxlength="200" | |
| 73 | - class="invalid-textarea" | |
| 74 | - /> | |
| 75 | - <view class="upload-wrap mt-20"> | |
| 76 | - <view class="upload-title">上传图片(选填)</view> | |
| 77 | - <up-upload | |
| 78 | - :file-list="invalidImgs.rawImgList.value || []" | |
| 79 | - @after-read="invalidImgs.uploadImgs" | |
| 80 | - @delete="invalidImgs.deleteImg" | |
| 81 | - multiple | |
| 82 | - width="70" | |
| 83 | - height="70" | |
| 84 | - :max-count="invalidImgs.uploadConfig.maxCount" | |
| 85 | - :upload-text="invalidImgs.uploadConfig.uploadText" | |
| 86 | - :size-type="invalidImgs.uploadConfig.sizeType" | |
| 87 | - /> | |
| 88 | - </view> | |
| 89 | - </view> | |
| 90 | - </up-modal> | |
| 91 | - </view> | |
| 8 | + </up-sticky> | |
| 9 | + <z-paging ref="pagingRef" v-model="currentList" @query="fetchData" :auto-show-system-loading="true"> | |
| 10 | + <template #empty> | |
| 11 | + <empty-view /> | |
| 12 | + </template> | |
| 13 | + <view class="card-list"> | |
| 14 | + <view class="task-card" v-for="(item, index) in currentList" :key="`${item.planNo}_${index}`"> | |
| 15 | + <!-- 第一行:任务名称 + 置为失效 --> | |
| 16 | + <view class="row row-1"> | |
| 17 | + <view class="plan-name u-line-1">{{ item.planName || '无计划名称' }}</view> | |
| 18 | + <!-- 权限:包含team_leader_yl / team_leader_wy / team_leader_hjws 并且当前是待完成tab,才显示置为失效 --> | |
| 19 | + <view v-if="hasInvalidPerm && activeStatus === '1'" class="btn-set-invalid" | |
| 20 | + @click.stop="handleSetInvalid(item)"> | |
| 21 | + 置为失效 | |
| 22 | + </view> | |
| 23 | + </view> | |
| 24 | + <!-- 第二行:任务次数 + 去作业按钮 --> | |
| 25 | + <view class="row row-2"> | |
| 26 | + <view class="info-text">任务次数:第{{ item.plannum || 1 }}次</view> | |
| 27 | + <!-- 仅待完成才显示去作业 --> | |
| 28 | + <view v-if="activeStatus === '1'" class="mini-btn" @click.stop="handleGoWork(item)">去作业</view> | |
| 29 | + </view> | |
| 30 | + <!-- 第三行:有效期 + 结束任务按钮 --> | |
| 31 | + <view class="row row-3"> | |
| 32 | + <!-- timeFormat(start, 'yyyy-mm-dd'), --> | |
| 33 | + <view class="info-text u-line-1">有效期:{{ timeFormat(item.beginTime, 'yyyy-mm-dd') }}—{{ timeFormat(item.endTime, 'yyyy-mm-dd') }} | |
| 34 | + </view> | |
| 35 | + <!-- 仅待完成才显示结束任务 --> | |
| 36 | + <view v-if="activeStatus === '1'" class="mini-btn" @click.stop="handleEndTask(item)">结束任务</view> | |
| 37 | + </view> | |
| 38 | + </view> | |
| 39 | + </view> | |
| 40 | + </z-paging> | |
| 41 | + <!-- ========== 置为失效弹窗(参考回退弹窗结构) ========== --> | |
| 42 | + <up-modal :show="invalidModalShow" title="失效原因" :closeOnClickOverlay="false" :showConfirmButton="true" | |
| 43 | + :showCancelButton="true" @cancel="handleInvalidModalCancel" @confirm="confirmSetInvalid"> | |
| 44 | + <view class="invalid-modal-content"> | |
| 45 | + <up-textarea v-model.trim="invalidReason" placeholder="请输入失效原因(必填)" rows="6" :count="200" | |
| 46 | + maxlength="200" class="invalid-textarea" /> | |
| 47 | + <view class="upload-wrap mt-20"> | |
| 48 | + <view class="upload-title">上传图片(选填)</view> | |
| 49 | + <up-upload :file-list="invalidImgs.rawImgList.value || []" @after-read="invalidImgs.uploadImgs" | |
| 50 | + @delete="invalidImgs.deleteImg" multiple width="70" height="70" | |
| 51 | + :max-count="invalidImgs.uploadConfig.maxCount" | |
| 52 | + :upload-text="invalidImgs.uploadConfig.uploadText" | |
| 53 | + :size-type="invalidImgs.uploadConfig.sizeType" /> | |
| 54 | + </view> | |
| 55 | + </view> | |
| 56 | + </up-modal> | |
| 57 | + </view> | |
| 92 | 58 | </template> |
| 93 | - | |
| 94 | 59 | <script setup lang="ts"> |
| 95 | 60 | import { ref, computed } from 'vue' |
| 96 | -import { onShow } from '@dcloudio/uni-app' | |
| 61 | +import { onShow, onUnload,onLoad } from '@dcloudio/uni-app' | |
| 97 | 62 | import { useUserStore } from '@/pinia/user'; |
| 98 | -// 引入图片上传hook(和你回退弹窗同一个工具) | |
| 63 | +import { maintainPlanNum, finishPlan, invalidatePlan } from "@/api/maintain-manage/maintain-manage"; | |
| 64 | +import { timeFormat } from '@/uni_modules/uview-plus'; | |
| 99 | 65 | import { useUploadImgs } from '@/common/utils/useUploadImgs' |
| 100 | - | |
| 101 | 66 | const userStore = useUserStore(); |
| 102 | 67 | const userRoles = computed<string[]>(() => userStore.userInfo?.roles || []); |
| 103 | -const isMaintainLeader = computed(() => userRoles.value.includes('yl_worker')) | |
| 68 | + | |
| 69 | +// 判断是否拥有置为失效权限:任意一个组长角色就可以 | |
| 70 | +const hasInvalidPerm = computed(() => { | |
| 71 | + const roles = userRoles.value | |
| 72 | + const leaderRoles = ['team_leader_yl', 'team_leader_wy', 'team_leader_hjws'] | |
| 73 | + return leaderRoles.some(key => roles.includes(key)) | |
| 74 | +}) | |
| 104 | 75 | |
| 105 | 76 | const statusTabs = ref([ |
| 106 | - { name: '待完成', id: '1' }, | |
| 107 | - { name: '已失效', id: '3' }, | |
| 108 | - { name: '已完成', id: '2' } | |
| 77 | + { name: '待完成', id: '1' }, | |
| 78 | + { name: '已失效', id: '3' }, | |
| 79 | + { name: '已完成', id: '2' } | |
| 109 | 80 | ]) |
| 110 | 81 | const activeStatus = ref('1') |
| 111 | 82 | const pagingRef = ref(null) |
| 112 | 83 | const currentList = ref([]) |
| 113 | - | |
| 84 | +// 保存上一页传过来的原始数组 | |
| 85 | +let originRawList: any[] = [] | |
| 86 | +// 合并之后全部接口返回的总数据 | |
| 87 | +const allTotalData = ref<any[]>([]) | |
| 114 | 88 | // ===================== 失效弹窗状态 ===================== |
| 115 | 89 | const invalidModalShow = ref(false) |
| 116 | 90 | const invalidReason = ref('') |
| 117 | 91 | const currentInvalidItem = ref(null) |
| 118 | 92 | // 图片上传实例,和回退保持一致,最多3张 |
| 119 | 93 | const invalidImgs = useUploadImgs({ |
| 120 | - maxCount: 3, | |
| 121 | - uploadText: '选择失效图片', | |
| 122 | - sizeType: ['compressed'], | |
| 123 | - formRef: null, | |
| 124 | - fieldName: 'invalidImgs' | |
| 94 | + maxCount: 3, | |
| 95 | + uploadText: '选择失效图片', | |
| 96 | + sizeType: ['compressed'], | |
| 97 | + formRef: null, | |
| 98 | + fieldName: 'invalidImgs' | |
| 125 | 99 | }) |
| 126 | - | |
| 127 | -const mockAllData = [ | |
| 128 | - { planNo: 'MH20260616001', planName: '德胜门东大街大街月季补植', taskTimes: 1, beginTime: '2026/06/16', endTime: '2026/12/31', finishState: '1' }, | |
| 129 | - { planNo: 'MH20260616002', planName: '西长安街行道树修剪养护作业任务', taskTimes: 2, beginTime: '2026/05/01', endTime: '2026/11/30', finishState: '1' }, | |
| 130 | - { planNo: 'MH20260616003', planName: '北三环绿地浇水、防寒养护专项任务超长名称测试文字溢出', taskTimes: 1, beginTime: '2026/04/10', endTime: '2026/12/20', finishState: '1' }, | |
| 131 | - { planNo: 'MH20260616004', planName: '奥森园区绿地保洁日常养护', taskTimes: 3, beginTime: '2026/03/01', endTime: '2026/10/15', finishState: '3' }, | |
| 132 | - { planNo: 'MH20260616005', planName: '护城河沿岸病虫害防治专项作业', taskTimes: 2, beginTime: '2026/02/15', endTime: '2026/09/30', finishState: '2' }, | |
| 133 | - { planNo: 'MH20260616006', planName: '朝阳公园施肥、除草春季养护任务', taskTimes: 1, beginTime: '2026/04/05', endTime: '2026/11/10', finishState: '1' }, | |
| 134 | - { planNo: 'MH20260616007', planName: '中关村大街灌木修剪与枯枝清理', taskTimes: 4, beginTime: '2026/01/20', endTime: '2026/12/25', finishState: '1' }, | |
| 135 | - { planNo: 'MH20260616008', planName: '永定河滨河绿地冬季防寒浇水作业', taskTimes: 1, beginTime: '2026/07/01', endTime: '2026/12/31', finishState: '3' }, | |
| 136 | - { planNo: 'MH20260616009', planName: '颐和园外围绿带病虫害防治', taskTimes: 2, beginTime: '2026/05/20', endTime: '2026/10/20', finishState: '2' }, | |
| 137 | - { planNo: 'MH20260616010', planName: '上地街道行道树补植更换任务', taskTimes: 1, beginTime: '2026/06/01', endTime: '2026/12/20', finishState: '1' } | |
| 138 | -] | |
| 139 | - | |
| 100 | +// 加载数据,根据当前activeStatus动态调用接口 | |
| 101 | +const loadData = async () => { | |
| 102 | + // originRawList = uni.getStorageSync('temp_maintain_rawList') | |
| 103 | + console.log('123123') | |
| 104 | + if (!originRawList || originRawList.length === 0) { | |
| 105 | + return | |
| 106 | + } | |
| 107 | + uni.showLoading({ title: '加载数据...', mask: true }) | |
| 108 | + try { | |
| 109 | + const tempResult: any[] = [] | |
| 110 | + // 获取需要请求的finishState数组 | |
| 111 | + let stateArr: string[] = [] | |
| 112 | + if (activeStatus.value === '1') { | |
| 113 | + // 待完成,只查1 | |
| 114 | + stateArr = ['1'] | |
| 115 | + } else if (activeStatus.value === '3') { | |
| 116 | + // 已失效,同时查3、4 | |
| 117 | + stateArr = ['3', '4'] | |
| 118 | + } else if (activeStatus.value === '2') { | |
| 119 | + // 已完成,只查2 | |
| 120 | + stateArr = ['2'] | |
| 121 | + } | |
| 122 | + // 遍历state数组,每个状态,循环rawList里面所有batchNo请求 | |
| 123 | + for (const finishState of stateArr) { | |
| 124 | + for (const item of originRawList) { | |
| 125 | + const res = await maintainPlanNum({ | |
| 126 | + pageNo: 1, | |
| 127 | + pageSize: 10, | |
| 128 | + planNo: item.planNo || '', | |
| 129 | + planAttr: '', | |
| 130 | + finishState: finishState, | |
| 131 | + batchNo: item.batchNo | |
| 132 | + }) | |
| 133 | + if (Array.isArray(res.list)) { | |
| 134 | + tempResult.push(...res.list) | |
| 135 | + } | |
| 136 | + } | |
| 137 | + } | |
| 138 | + allTotalData.value = tempResult | |
| 139 | + } catch (err) { | |
| 140 | + console.error('批量请求接口异常', err) | |
| 141 | + uni.showToast({ title: '数据加载失败', icon: 'none' }) | |
| 142 | + } finally { | |
| 143 | + uni.hideLoading() | |
| 144 | + pagingRef.value?.reload() | |
| 145 | + } | |
| 146 | +} | |
| 147 | +const planTypeId = ref('') | |
| 148 | +onLoad((options) => { | |
| 149 | + planTypeId.value = options.planTypeId | |
| 150 | + | |
| 151 | +}); | |
| 152 | +// onShow 只保留一处,读取storage,只第一次读取storage | |
| 153 | +onShow(async () => { | |
| 154 | + originRawList = uni.getStorageSync('temp_maintain_rawList') || [] | |
| 155 | + // ✅ 读取完立刻删除,防止脏数据 | |
| 156 | + // uni.removeStorageSync('temp_maintain_rawList') | |
| 157 | + await loadData() | |
| 158 | +}) | |
| 159 | +// z-paging分页查询,直接使用allTotalData | |
| 140 | 160 | const fetchData = async (pageNo, pageSize) => { |
| 141 | - await new Promise(resolve => setTimeout(resolve, 400)) | |
| 142 | - const filterList = mockAllData.filter(item => item.finishState === activeStatus.value) | |
| 143 | - const start = (pageNo - 1) * pageSize | |
| 144 | - const pageData = filterList.slice(start, start + pageSize) | |
| 145 | - pagingRef.value?.complete(pageData, filterList.length) | |
| 161 | + await new Promise(resolve => setTimeout(resolve, 300)) | |
| 162 | + const filterList = allTotalData.value | |
| 163 | + const start = (pageNo - 1) * pageSize | |
| 164 | + const pageData = filterList.slice(start, start + pageSize) | |
| 165 | + pagingRef.value?.complete(pageData, filterList.length) | |
| 146 | 166 | } |
| 147 | - | |
| 148 | -const handleStatusChange = (item) => { | |
| 149 | - activeStatus.value = item.id | |
| 150 | - pagingRef.value?.reload() | |
| 167 | +const handleStatusChange = async (tabItem) => { | |
| 168 | + activeStatus.value = tabItem.id | |
| 169 | + // 切换tab,重新拉接口 | |
| 170 | + await loadData() | |
| 151 | 171 | } |
| 152 | - | |
| 153 | 172 | // 打开失效弹窗 |
| 154 | 173 | const handleSetInvalid = (item) => { |
| 155 | - currentInvalidItem.value = item | |
| 156 | - invalidReason.value = '' | |
| 157 | - invalidImgs.clearImgs() | |
| 158 | - invalidModalShow.value = true | |
| 174 | + currentInvalidItem.value = item | |
| 175 | + invalidReason.value = '' | |
| 176 | + invalidImgs.clearImgs() | |
| 177 | + invalidModalShow.value = true | |
| 159 | 178 | } |
| 160 | - | |
| 161 | 179 | // 关闭弹窗,重置数据 |
| 162 | 180 | const handleInvalidModalCancel = () => { |
| 163 | - invalidModalShow.value = false | |
| 164 | - invalidReason.value = '' | |
| 165 | - invalidImgs.clearImgs() | |
| 181 | + invalidModalShow.value = false | |
| 182 | + invalidReason.value = '' | |
| 183 | + invalidImgs.clearImgs() | |
| 166 | 184 | } |
| 167 | 185 | |
| 168 | 186 | // 确认提交置为失效 |
| ... | ... | @@ -173,23 +191,27 @@ const confirmSetInvalid = async () => { |
| 173 | 191 | return |
| 174 | 192 | } |
| 175 | 193 | const item = currentInvalidItem.value |
| 176 | - if (!item) return | |
| 194 | + if (!item || !item.id) return | |
| 177 | 195 | |
| 178 | 196 | uni.showLoading({ title: '提交中...', mask: true }) |
| 179 | 197 | try { |
| 180 | - // 这里后续替换真实接口,获取上传图片url: invalidImgs.getSuccessImgUrls() | |
| 181 | - const imgList = invalidImgs.getSuccessImgUrls() | |
| 182 | - console.log('置为失效提交参数:', { | |
| 183 | - planNo: item.planNo, | |
| 184 | - reason, | |
| 185 | - imgList | |
| 186 | - }) | |
| 198 | + // 获取上传成功图片url数组,拼接逗号 | |
| 199 | + const imgUrlArr = invalidImgs.getSuccessImgUrls() | |
| 200 | + const invalidFiles = imgUrlArr.join(',') | |
| 187 | 201 | |
| 188 | - // ============ 此处替换后端接口调用 ============ | |
| 189 | - // await api.setTaskInvalid({ ... }) | |
| 202 | + const params = { | |
| 203 | + id: item.id, | |
| 204 | + invalidReason: reason, | |
| 205 | + invalidFiles: invalidFiles | |
| 206 | + } | |
| 207 | + console.log('置为失效提交参数:', params) | |
| 208 | + // 调用接口 invalidatePlan | |
| 209 | + await invalidatePlan(params) | |
| 190 | 210 | |
| 191 | 211 | uni.showToast({ title: '操作成功', icon: 'success' }) |
| 192 | 212 | handleInvalidModalCancel() |
| 213 | + // 重新加载数据刷新列表 | |
| 214 | + await loadData() | |
| 193 | 215 | pagingRef.value?.reload() |
| 194 | 216 | } catch (err) { |
| 195 | 217 | console.error(err) |
| ... | ... | @@ -200,84 +222,117 @@ const confirmSetInvalid = async () => { |
| 200 | 222 | } |
| 201 | 223 | |
| 202 | 224 | const handleGoWork = (item) => { |
| 203 | - | |
| 204 | - uni.navigateTo({ | |
| 205 | - url: `/pages-sub/daily/maintain-manage/now-list-page?roadId=${item.roadId}&finish_state=${item.finishState || ''} &businessType=${item.busiLine}` | |
| 206 | - }) | |
| 225 | + uni.navigateTo({ | |
| 226 | + // 重点:把planNo传过去 | |
| 227 | + url: `/pages-sub/daily/maintain-manage/now-list-page?planNo=${item.planNo}&planTypeId=${planTypeId.value}&businessType=${item.busiLine}` | |
| 228 | + }) | |
| 229 | +} | |
| 230 | +const handleEndTask = async (item) => { | |
| 231 | + console.log(item) | |
| 232 | + uni.showLoading({ title: '提交中...', mask: true }) | |
| 233 | + try { | |
| 234 | + // 接口 finishPlan,传 planNo | |
| 235 | + await finishPlan({ | |
| 236 | + planNo: item.planNo | |
| 237 | + }) | |
| 238 | + uni.showToast({ title: '结束任务成功', icon: 'success' }) | |
| 239 | + // 操作成功,重新加载数据,刷新列表 | |
| 240 | + await loadData() | |
| 241 | + pagingRef.value?.reload() | |
| 242 | + } catch (err) { | |
| 243 | + console.error('结束任务失败', err) | |
| 244 | + uni.showToast({ title: '结束任务失败,请重试', icon: 'none' }) | |
| 245 | + } finally { | |
| 246 | + uni.hideLoading() | |
| 247 | + } | |
| 207 | 248 | } |
| 208 | -const handleEndTask = (item) => console.log('结束任务', item) | |
| 209 | 249 | |
| 210 | -onShow(() => pagingRef.value?.reload()) | |
| 250 | +// 页面卸载清空,防止内存残留 | |
| 251 | +onUnload(() => { | |
| 252 | + allTotalData.value = [] | |
| 253 | + originRawList = [] | |
| 254 | +}) | |
| 211 | 255 | </script> |
| 212 | - | |
| 213 | 256 | <style scoped lang="scss"> |
| 214 | 257 | .page-container { |
| 215 | - height: 100vh; | |
| 216 | - background-color: #f5f5f5; | |
| 258 | + height: 100vh; | |
| 259 | + background-color: #f5f5f5; | |
| 217 | 260 | } |
| 261 | + | |
| 218 | 262 | .sticky-wrap { |
| 219 | - background: #fff; | |
| 220 | - border-bottom: 1rpx solid #eee; | |
| 263 | + background: #fff; | |
| 264 | + border-bottom: 1rpx solid #eee; | |
| 221 | 265 | } |
| 266 | + | |
| 222 | 267 | .card-list { |
| 223 | - padding:100rpx 20rpx 20rpx; | |
| 224 | - display: flex; | |
| 225 | - flex-direction: column; | |
| 226 | - gap: 20rpx; | |
| 268 | + padding: 100rpx 20rpx 20rpx; | |
| 269 | + display: flex; | |
| 270 | + flex-direction: column; | |
| 271 | + gap: 20rpx; | |
| 227 | 272 | } |
| 273 | + | |
| 228 | 274 | .task-card { |
| 229 | - background: #fff; | |
| 230 | - border-radius: 12rpx; | |
| 231 | - padding: 24rpx; | |
| 232 | - display: flex; | |
| 233 | - flex-direction: column; | |
| 234 | - gap: 20rpx; | |
| 275 | + background: #fff; | |
| 276 | + border-radius: 12rpx; | |
| 277 | + padding: 24rpx; | |
| 278 | + display: flex; | |
| 279 | + flex-direction: column; | |
| 280 | + gap: 20rpx; | |
| 235 | 281 | } |
| 282 | + | |
| 236 | 283 | .row { |
| 237 | - display: flex; | |
| 238 | - align-items: center; | |
| 239 | - justify-content: space-between; | |
| 240 | - gap: 16rpx; | |
| 284 | + display: flex; | |
| 285 | + align-items: center; | |
| 286 | + justify-content: space-between; | |
| 287 | + gap: 16rpx; | |
| 241 | 288 | } |
| 289 | + | |
| 242 | 290 | .plan-name { |
| 243 | - font-size: 30rpx; | |
| 244 | - color: #333; | |
| 245 | - font-weight: 500; | |
| 246 | - flex: 1; | |
| 291 | + font-size: 30rpx; | |
| 292 | + color: #333; | |
| 293 | + font-weight: 500; | |
| 294 | + flex: 1; | |
| 247 | 295 | } |
| 296 | + | |
| 248 | 297 | .btn-set-invalid { |
| 249 | - font-size: 26rpx; | |
| 250 | - color: #1677ff; | |
| 251 | - flex-shrink: 0; | |
| 252 | - padding-left: 16rpx; | |
| 298 | + font-size: 26rpx; | |
| 299 | + color: #1677ff; | |
| 300 | + flex-shrink: 0; | |
| 301 | + padding-left: 16rpx; | |
| 253 | 302 | } |
| 303 | + | |
| 254 | 304 | .info-text { |
| 255 | - font-size: 26rpx; | |
| 256 | - color: #666; | |
| 257 | - flex: 1; | |
| 305 | + font-size: 26rpx; | |
| 306 | + color: #666; | |
| 307 | + flex: 1; | |
| 258 | 308 | } |
| 309 | + | |
| 259 | 310 | .mini-btn { |
| 260 | - font-size: 24rpx; | |
| 261 | - color: #333; | |
| 262 | - background: #f5f5f5; | |
| 263 | - border: 1rpx solid #ddd; | |
| 264 | - border-radius: 6rpx; | |
| 265 | - padding: 8rpx 20rpx; | |
| 266 | - flex-shrink: 0; | |
| 311 | + font-size: 24rpx; | |
| 312 | + color: #333; | |
| 313 | + background: #f5f5f5; | |
| 314 | + border: 1rpx solid #ddd; | |
| 315 | + border-radius: 6rpx; | |
| 316 | + padding: 8rpx 20rpx; | |
| 317 | + flex-shrink: 0; | |
| 267 | 318 | } |
| 268 | 319 | |
| 269 | 320 | // 弹窗样式,和回退弹窗保持一致 |
| 270 | 321 | .invalid-modal-content { |
| 271 | - width: 100%; | |
| 272 | - box-sizing: border-box; | |
| 273 | - padding: 10rpx 0; | |
| 322 | + width: 100%; | |
| 323 | + box-sizing: border-box; | |
| 324 | + padding: 10rpx 0; | |
| 274 | 325 | } |
| 326 | + | |
| 275 | 327 | .upload-wrap { |
| 276 | - .upload-title { | |
| 277 | - font-size: 28rpx; | |
| 278 | - color: #333; | |
| 279 | - margin-bottom: 10rpx; | |
| 280 | - } | |
| 328 | + .upload-title { | |
| 329 | + font-size: 28rpx; | |
| 330 | + color: #333; | |
| 331 | + margin-bottom: 10rpx; | |
| 332 | + } | |
| 333 | +} | |
| 334 | + | |
| 335 | +.mt-20 { | |
| 336 | + margin-top: 20rpx; | |
| 281 | 337 | } |
| 282 | -.mt-20 { margin-top: 20rpx; } | |
| 283 | 338 | </style> | ... | ... |
pages-sub/daily/maintain-manage/type-list.vue
| 1 | 1 | <template> |
| 2 | - <view class="tag-wrap"> | |
| 3 | - <!-- 动态渲染标签列表 --> | |
| 4 | - <view class="tag-item" v-for="(item, idx) in tagList" :key="idx" hover-class="tag-item--hover" @click="toNextPage"> | |
| 5 | - <view class="tag-text"> | |
| 6 | - <!-- 文字区域:超长省略号,数字括号单独放后面,永远可见 --> | |
| 7 | - <text class="tag-name">{{ item.label }}</text> | |
| 8 | - <text class="tag-count">({{ item.count }})</text> | |
| 9 | - </view> | |
| 10 | - | |
| 2 | + <view class="tag-wrap"> | |
| 3 | + <!-- 动态渲染标签列表 --> | |
| 4 | + <template v-if="tagList.length > 0"> | |
| 5 | + <view class="tag-item" v-for="(item, idx) in tagList" :key="idx" hover-class="tag-item--hover" | |
| 6 | + @click="toNextPage(item)"> | |
| 7 | + <view class="tag-text"> | |
| 8 | + <!-- 文字区域:超长省略号,数字括号单独放后面,永远可见 --> | |
| 9 | + <text class="tag-name">{{ item.label }}</text> | |
| 10 | + <text class="tag-count">({{ item.count }})</text> | |
| 11 | + </view> | |
| 12 | + </view> | |
| 13 | + </template> | |
| 14 | + <template v-else> | |
| 15 | + <view style="padding-top: 30px;text-align: center;width: 100%;">暂无养护类型,请去平台配置</view> | |
| 16 | + </template> | |
| 11 | 17 | </view> |
| 12 | - </view> | |
| 13 | 18 | </template> |
| 14 | - | |
| 15 | 19 | <script setup lang="ts"> |
| 16 | 20 | import { ref } from 'vue' |
| 17 | - | |
| 18 | -// 数据源,完全动态,你可以接口返回覆盖 | |
| 19 | -const tagList = ref([ | |
| 20 | - { label: '补植', count: 5 }, | |
| 21 | - { label: '修剪', count: 2 }, | |
| 22 | - { label: '浇水、防寒', count: 3 }, | |
| 23 | - { label: '绿地保洁', count: 3 }, | |
| 24 | - { label: '病虫害防治', count: 3 }, | |
| 25 | - { label: '施肥、除草', count: 4 }, | |
| 26 | - // 测试超长文本 | |
| 27 | - { label: '非常长的养护任务名称测试文字溢出效果', count: 9 }, | |
| 28 | -]) | |
| 29 | - | |
| 30 | -const toNextPage = (item)=> { | |
| 21 | +import { maintainPlan, maintainPlanNum } from "@/api/maintain-manage/maintain-manage"; | |
| 22 | +import { getDictLabel } from '@/common/utils/dict'; | |
| 23 | +import { useUserStore } from '@/pinia/user'; | |
| 24 | +// 新增维护仓库 | |
| 25 | +// import { useMaintainStore } from '@/pinia/maintain'; | |
| 26 | +import { onLoad, onShow } from '@dcloudio/uni-app'; | |
| 27 | +const userStore = useUserStore(); | |
| 28 | +// const maintainStore = useMaintainStore(); | |
| 29 | +// 标签列表,合并之后的数据结构 | |
| 30 | +// rawList:保存该分类下全部原始数据,用于点击跳转 | |
| 31 | +const tagList = ref<Array<{ | |
| 32 | + label: string, | |
| 33 | + count: number, | |
| 34 | + rawList: any[], | |
| 35 | + planTypeId: string | number | |
| 36 | +}>>([]) | |
| 37 | +const roadName = ref<string | null>(null) | |
| 38 | +const levelId = ref<string | null>(null) | |
| 39 | +onLoad((options) => { | |
| 40 | + roadName.value = decodeURIComponent(options.roadName) | |
| 41 | + levelId.value = decodeURIComponent(options.levelId); | |
| 42 | +}); | |
| 43 | +onShow(() => { | |
| 44 | + if (!roadName.value) { | |
| 45 | + uni.showToast({ | |
| 46 | + title: '道路不存在', | |
| 47 | + icon: 'none' | |
| 48 | + }); | |
| 49 | + return; | |
| 50 | + } | |
| 51 | + fetchData(); | |
| 52 | +}); | |
| 53 | +// 第一步:获取plan列表 | |
| 54 | +const fetchData = async () => { | |
| 55 | + try { | |
| 56 | + const res = await maintainPlan({ | |
| 57 | + roadName: roadName.value, | |
| 58 | + levelId: levelId.value | |
| 59 | + }) | |
| 60 | + console.log('maintainPlan返回', res) | |
| 61 | + if (!Array.isArray(res.list) || res.list.length === 0) { | |
| 62 | + tagList.value = [] | |
| 63 | + uni.showToast({ | |
| 64 | + title: '暂无养护类型', | |
| 65 | + icon: 'none' | |
| 66 | + }); | |
| 67 | + return | |
| 68 | + } | |
| 69 | + await requestBatchList(res.list) | |
| 70 | + } catch (error) { | |
| 71 | + console.error('请求失败:', error) | |
| 72 | + uni.showToast({ title: '网络异常,请重试', icon: 'none' }) | |
| 73 | + } | |
| 74 | +} | |
| 75 | +// 串行遍历所有batchNo,逐个调用第二个接口 | |
| 76 | +const requestBatchList = async (list: any[]) => { | |
| 77 | + const resultTags: any[] = [] | |
| 78 | + for (const item of list) { | |
| 79 | + try { | |
| 80 | + const numRes = await maintainPlanNum({ | |
| 81 | + pageNo: 1, | |
| 82 | + pageSize: 10, | |
| 83 | + planNo: '', | |
| 84 | + planAttr: item.planAttr ?? '', | |
| 85 | + finishState: 1, | |
| 86 | + batchNo: item.batchNo | |
| 87 | + }) | |
| 88 | + const total = numRes?.total || 0 | |
| 89 | + // 字典转换,把planTypeId转为养护类型名称 | |
| 90 | + const typeLabel = getDictLabel('maintain_type', item.planTypeId) | |
| 91 | + resultTags.push({ | |
| 92 | + label: typeLabel, | |
| 93 | + planTypeId: item.planTypeId, | |
| 94 | + count: total, | |
| 95 | + // 保留原始整条数据 | |
| 96 | + originItem: item | |
| 97 | + }) | |
| 98 | + } catch (err) { | |
| 99 | + console.error('batch请求异常', item.batchNo, err) | |
| 100 | + const typeLabel = getDictLabel('maintain_type', item.planTypeId) | |
| 101 | + resultTags.push({ | |
| 102 | + label: typeLabel, | |
| 103 | + planTypeId: item.planTypeId, | |
| 104 | + count: 0, | |
| 105 | + originItem: item | |
| 106 | + }) | |
| 107 | + } | |
| 108 | + } | |
| 109 | + console.log(resultTags) | |
| 110 | + // ========== 分组合并相同养护类型,count累加,保存原始数组 ========== | |
| 111 | + const groupMap = new Map<string, { | |
| 112 | + count: number, | |
| 113 | + rawList: any[], | |
| 114 | + planTypeId: string | number | |
| 115 | + }>() | |
| 116 | + for (const item of resultTags) { | |
| 117 | + console.log(item); | |
| 118 | + const name = item.label | |
| 119 | + if (!name) continue // 过滤字典为空的脏数据 | |
| 120 | + if (groupMap.has(name)) { | |
| 121 | + const groupData = groupMap.get(name)! | |
| 122 | + groupData.count += item.count | |
| 123 | + groupData.rawList.push(item.originItem) | |
| 124 | + } else { | |
| 125 | + groupMap.set(name, { | |
| 126 | + count: item.count, | |
| 127 | + rawList: [item.originItem], | |
| 128 | + planTypeId: item.planTypeId | |
| 129 | + }) | |
| 130 | + } | |
| 131 | + } | |
| 132 | + // map转为数组给页面渲染 | |
| 133 | + const groupList: any[] = [] | |
| 134 | + groupMap.forEach((group, label) => { | |
| 135 | + groupList.push({ | |
| 136 | + label, | |
| 137 | + count: group.count, | |
| 138 | + rawList: group.rawList, | |
| 139 | + planTypeId: group.planTypeId | |
| 140 | + }) | |
| 141 | + }) | |
| 142 | + tagList.value = groupList | |
| 143 | + console.log(tagList.value) | |
| 144 | +} | |
| 145 | +// 标签点击跳转,rawList存入storage,并且把planTypeId拼到url传给下一页 | |
| 146 | +const toNextPage = (item: any) => { | |
| 147 | + // maintainStore.setRawList(item.rawList) | |
| 148 | + uni.setStorageSync('temp_maintain_rawList', item.rawList) | |
| 31 | 149 | uni.navigateTo({ |
| 32 | - url: `/pages-sub/daily/maintain-manage/type-list-page?roadId=${item.roadId}&finish_state=${item.finishState || ''} &businessType=${item.busiLine}` | |
| 33 | - }) | |
| 150 | + url: `/pages-sub/daily/maintain-manage/type-list-page?planTypeId=${item.planTypeId}` | |
| 151 | + }) | |
| 34 | 152 | } |
| 35 | 153 | </script> |
| 36 | - | |
| 37 | 154 | <style scoped lang="scss"> |
| 38 | 155 | .tag-wrap { |
| 39 | - display: flex; | |
| 40 | - flex-wrap: wrap; | |
| 41 | - gap: 24rpx; | |
| 42 | - padding: 30rpx; | |
| 156 | + display: flex; | |
| 157 | + flex-wrap: wrap; | |
| 158 | + gap: 24rpx; | |
| 159 | + padding: 30rpx; | |
| 43 | 160 | } |
| 44 | 161 | |
| 45 | 162 | .tag-item { |
| 46 | - display: inline-flex; | |
| 47 | - align-items: center; | |
| 48 | - background: #e8f3ff; | |
| 49 | - border: 1rpx solid #94c5ff; | |
| 50 | - border-radius: 8rpx; | |
| 51 | - padding: 16rpx 24rpx; | |
| 52 | - max-width: 320rpx; // 限制标签最大宽度,超出自动省略 | |
| 53 | - transition: all 0.2s; | |
| 54 | - &--hover { | |
| 55 | - background: #d8eaff; | |
| 56 | - } | |
| 163 | + display: inline-flex; | |
| 164 | + align-items: center; | |
| 165 | + background: #e8f3ff; | |
| 166 | + border: 1rpx solid #94c5ff; | |
| 167 | + border-radius: 8rpx; | |
| 168 | + padding: 16rpx 24rpx; | |
| 169 | + max-width: 320rpx; // 限制标签最大宽度,超出自动省略 | |
| 170 | + transition: all 0.2s; | |
| 171 | + | |
| 172 | + &--hover { | |
| 173 | + background: #d8eaff; | |
| 174 | + } | |
| 57 | 175 | } |
| 58 | 176 | |
| 59 | 177 | .tag-text { |
| 60 | - display: flex; | |
| 61 | - align-items: center; | |
| 62 | - flex: 1; | |
| 63 | - overflow: hidden; | |
| 178 | + display: flex; | |
| 179 | + align-items: center; | |
| 180 | + flex: 1; | |
| 181 | + overflow: hidden; | |
| 64 | 182 | } |
| 65 | 183 | |
| 66 | 184 | .tag-name { |
| 67 | - // 核心:文字超长省略,只对名称生效 | |
| 68 | - white-space: nowrap; | |
| 69 | - overflow: hidden; | |
| 70 | - text-overflow: ellipsis; | |
| 71 | - color: #1677ff; | |
| 72 | - font-size: 28rpx; | |
| 185 | + // 核心:文字超长省略,只对名称生效 | |
| 186 | + white-space: nowrap; | |
| 187 | + overflow: hidden; | |
| 188 | + text-overflow: ellipsis; | |
| 189 | + color: #1677ff; | |
| 190 | + font-size: 28rpx; | |
| 73 | 191 | } |
| 74 | 192 | |
| 75 | 193 | .tag-count { |
| 76 | - color: #1677ff; | |
| 77 | - font-size: 28rpx; | |
| 78 | - flex-shrink: 0; // 重点!禁止压缩,保证(数字)永远显示 | |
| 79 | - margin-left: 4rpx; | |
| 194 | + color: #1677ff; | |
| 195 | + font-size: 28rpx; | |
| 196 | + flex-shrink: 0; // 重点!禁止压缩,保证(数字)永远显示 | |
| 197 | + margin-left: 4rpx; | |
| 80 | 198 | } |
| 81 | 199 | |
| 82 | 200 | .tag-icon { |
| 83 | - flex-shrink: 0; | |
| 84 | - margin-left: 12rpx; | |
| 201 | + flex-shrink: 0; | |
| 202 | + margin-left: 12rpx; | |
| 85 | 203 | } |
| 86 | 204 | </style> |
| 87 | - | ... | ... |
pinia/maintain.js
0 → 100644
| 1 | +import { defineStore } from 'pinia' | |
| 2 | +export const useMaintainStore = defineStore('maintain', { | |
| 3 | + state() { | |
| 4 | + return { | |
| 5 | + planRawList: [] | |
| 6 | + } | |
| 7 | + }, | |
| 8 | + actions: { | |
| 9 | + setRawList(list) { | |
| 10 | + this.planRawList = list | |
| 11 | + }, | |
| 12 | + clearRawList() { | |
| 13 | + this.planRawList = [] | |
| 14 | + } | |
| 15 | + } | |
| 16 | +}) | ... | ... |