Commit 7b93c3f6794baad3393c7f20acc8ae6450ef3177
1 parent
3da5bb94
业务线 小程序不用写死,从后端接口获取
Showing
4 changed files
with
246 additions
and
143 deletions
pages-sub/daily/maintain-manage/finish-plan-detail.vue
| ... | ... | @@ -14,6 +14,10 @@ |
| 14 | 14 | <!-- 1. 计划名称:独立标题卡(设计稿顶部白卡) --> |
| 15 | 15 | <view class="record-head"> |
| 16 | 16 | <view class="record-title">{{i.planName || '--'}}</view> |
| 17 | + <!-- 按批次查看时一期次一条,用「第X次 + 上报状态」区分多次提交记录 --> | |
| 18 | + <view class="record-sub" v-if="i.sortNum"> | |
| 19 | + 第 {{ i.sortNum }} 次 · {{ commitStateText(i.commitState) }} | |
| 20 | + </view> | |
| 17 | 21 | </view> |
| 18 | 22 | <!-- 工单详情内容 --> |
| 19 | 23 | <view class="form-card"> |
| ... | ... | @@ -95,56 +99,66 @@ |
| 95 | 99 | <view class="u-body-item-title">提交时间:</view> |
| 96 | 100 | <view class="u-line-1 u-body-value">{{ i.createTime==null?'--':timeFormat(i.createTime,'yyyy-mm-dd hh:MM:ss') }}</view> |
| 97 | 101 | </view> |
| 102 | + | |
| 103 | + <!-- 本次养护被置为失效时,把原因一并展示 --> | |
| 104 | + <view class="u-body-item u-flex" v-if="i.commitState === 3"> | |
| 105 | + <view class="u-body-item-title">失效原因:</view> | |
| 106 | + <view class="u-line-1 u-body-value">{{ i.invalidReason || '--' }}</view> | |
| 107 | + </view> | |
| 98 | 108 | </view> |
| 99 | 109 | |
| 100 | - <!-- 动态配置的养护字段填报值(garden_work_record/garden_work_field_value),未填报过则整段不显示 --> | |
| 101 | - <!-- 动态配置的养护字段填报值(garden_work_record/garden_work_field_value),未填报过则整段不显示 --> | |
| 102 | -<view | |
| 103 | - class="form-card" | |
| 104 | - v-if="i.fieldRows && i.fieldRows.length" | |
| 105 | -> | |
| 106 | - <!-- 区块标题:蓝色竖条 + 标题 --> | |
| 107 | - <view class="sec-head"> | |
| 108 | - <view class="sec-bar"></view> | |
| 109 | - <view class="sec-title">养护填报内容</view> | |
| 110 | - </view> | |
| 111 | - <view class="u-body-item u-flex field-row" v-for="(f, fi) in i.fieldRows" :key="fi"> | |
| 112 | - <view class="u-body-item-title field-cell-title-multi">{{ f.name }}</view> | |
| 113 | - <view class="field-value"> | |
| 114 | - <!-- 7 图片:复用本页照片的相册组件(预览/尺寸一致) --> | |
| 115 | - <up-album | |
| 116 | - v-if="f.kind === 'image'" | |
| 117 | - :urls="f.images" | |
| 118 | - singleSize="60" | |
| 119 | - multipleSize="60" | |
| 120 | - :preview-full-image="true" | |
| 121 | - ></up-album> | |
| 122 | - <!-- 6 多选 / 3 历史多选 / 9 关联记录:标签平铺(自绘,避免引入未使用的组件) --> | |
| 123 | - <view v-else-if="f.kind === 'multi'" class="field-tags"> | |
| 124 | - <view v-for="(t, ti) in f.tags" :key="ti" class="field-tag">{{ t.text }}</view> | |
| 125 | - </view> | |
| 126 | - <!-- 8 附件:点击下载后用系统应用打开 --> | |
| 127 | - <view v-else-if="f.kind === 'file'" class="field-tags"> | |
| 128 | - <text | |
| 129 | - v-for="(file, ui) in f.files" | |
| 130 | - :key="ui" | |
| 131 | - class="field-file" | |
| 132 | - @click="openFile(file)" | |
| 133 | - >{{ file.name }}</text> | |
| 134 | - </view> | |
| 135 | - <!-- 10 链接:小程序不能直接打开外部 URL,点击复制 --> | |
| 136 | - <text v-else-if="f.kind === 'link'" class="field-link" @click="copyLink(f.text)">{{ f.text }}</text> | |
| 137 | - <!-- 11 地理位置:点击调微信原生地图(无需地图 AK) --> | |
| 138 | - <text v-else-if="f.kind === 'geo' && f.geo" class="field-link" @click="openGeo(f.geo)">{{ f.text }}</text> | |
| 139 | - <!-- 其余(1数值/2文本/3单选枚举/4日期/5布尔等)文本直出 --> | |
| 140 | - <text v-else class="common-text-color field-text">{{ f.text }}</text> | |
| 141 | - </view> | |
| 142 | - </view> | |
| 143 | -</view> | |
| 144 | -<!-- 当没有填报字段,提示暂无提交记录 --> | |
| 145 | -<view v-else class="no-record-tip"> | |
| 146 | - 直接结束了本次任务 | |
| 147 | -</view> | |
| 110 | + <!-- 动态配置的养护字段填报值:每条子上报(garden_work_record)单独一张卡,不把多次提交的数据混在一起 --> | |
| 111 | + <template v-if="i.recordRows && i.recordRows.length"> | |
| 112 | + <view class="form-card" v-for="(r, ri) in i.recordRows" :key="ri"> | |
| 113 | + <!-- 区块标题:蓝色竖条 + 标题;多条时右侧标注第几条与上报时间 --> | |
| 114 | + <view class="sec-head"> | |
| 115 | + <view class="sec-bar"></view> | |
| 116 | + <view class="sec-title">养护填报内容</view> | |
| 117 | + <!-- 按批次查看时每个期次都标「第几条 + 上报时间」,单条记录也标,避免和上一期次的卡片混读 --> | |
| 118 | + <text class="sec-count" v-if="i.recordRows.length > 1 || i.sortNum"> | |
| 119 | + 第 {{ ri + 1 }} 条 · {{ r.workTime == null ? '--' : timeFormat(r.workTime, 'yyyy-mm-dd hh:MM:ss') }} | |
| 120 | + </text> | |
| 121 | + </view> | |
| 122 | + <view class="u-body-item u-flex field-row" v-for="(f, fi) in r.fieldRows" :key="fi"> | |
| 123 | + <view class="u-body-item-title field-cell-title-multi">{{ f.name }}</view> | |
| 124 | + <view class="field-value"> | |
| 125 | + <!-- 7 图片:复用本页照片的相册组件(预览/尺寸一致) --> | |
| 126 | + <up-album | |
| 127 | + v-if="f.kind === 'image'" | |
| 128 | + :urls="f.images" | |
| 129 | + singleSize="60" | |
| 130 | + multipleSize="60" | |
| 131 | + :preview-full-image="true" | |
| 132 | + ></up-album> | |
| 133 | + <!-- 6 多选 / 3 历史多选 / 9 关联记录:标签平铺(自绘,避免引入未使用的组件) --> | |
| 134 | + <view v-else-if="f.kind === 'multi'" class="field-tags"> | |
| 135 | + <view v-for="(t, ti) in f.tags" :key="ti" class="field-tag">{{ t.text }}</view> | |
| 136 | + </view> | |
| 137 | + <!-- 8 附件:点击下载后用系统应用打开 --> | |
| 138 | + <view v-else-if="f.kind === 'file'" class="field-tags"> | |
| 139 | + <text | |
| 140 | + v-for="(file, ui) in f.files" | |
| 141 | + :key="ui" | |
| 142 | + class="field-file" | |
| 143 | + @click="openFile(file)" | |
| 144 | + >{{ file.name }}</text> | |
| 145 | + </view> | |
| 146 | + <!-- 10 链接:小程序不能直接打开外部 URL,点击复制 --> | |
| 147 | + <text v-else-if="f.kind === 'link'" class="field-link" @click="copyLink(f.text)">{{ f.text }}</text> | |
| 148 | + <!-- 11 地理位置:点击调微信原生地图(无需地图 AK) --> | |
| 149 | + <text v-else-if="f.kind === 'geo' && f.geo" class="field-link" @click="openGeo(f.geo)">{{ f.text }}</text> | |
| 150 | + <!-- 其余(1数值/2文本/3单选枚举/4日期/5布尔等)文本直出 --> | |
| 151 | + <text v-else class="common-text-color field-text">{{ f.text }}</text> | |
| 152 | + </view> | |
| 153 | + </view> | |
| 154 | + <!-- 该条上报没填字段时给个占位,避免出现一张空白卡 --> | |
| 155 | + <view class="no-field-tip" v-if="!r.fieldRows.length">本条未填写养护内容</view> | |
| 156 | + </view> | |
| 157 | + </template> | |
| 158 | + <!-- 当没有填报字段,提示暂无提交记录 --> | |
| 159 | + <view v-else class="no-record-tip"> | |
| 160 | + {{ i.commitState === 3 ? '本次养护已置为失效,未填写养护内容' : '直接结束了本次任务' }} | |
| 161 | + </view> | |
| 148 | 162 | |
| 149 | 163 | </view> |
| 150 | 164 | </template> |
| ... | ... | @@ -195,18 +209,34 @@ const previewImage = (currentUrl, allUrls) => { |
| 195 | 209 | }); |
| 196 | 210 | }; |
| 197 | 211 | |
| 212 | +/** 上报状态文案:1进行中/2已完成/3已失效 */ | |
| 213 | +const commitStateText = (state) => { | |
| 214 | + if (state === 1) return '进行中' | |
| 215 | + if (state === 2) return '已完成' | |
| 216 | + if (state === 3) return '已失效' | |
| 217 | + return '--' | |
| 218 | +}; | |
| 219 | + | |
| 198 | 220 | /** |
| 199 | 221 | * 获取工单详情 |
| 222 | + * 传 batchNo 时返回该批次下所有期次的上报记录(一次一条期次,多次提交要全部列出); | |
| 223 | + * 只传 planNo 时仍按计划编码查单条,兼容原入口。 | |
| 200 | 224 | */ |
| 201 | -const getOrderDetail = async (planNo: string, commitId?: string) => { | |
| 225 | +const getOrderDetail = async (planNo: string, commitId?: string, batchNo?: string) => { | |
| 202 | 226 | console.log('getOrderDetail') |
| 203 | 227 | try { |
| 204 | 228 | loading.value = true; |
| 205 | 229 | let queryData: any = { |
| 206 | - planNo: planNo, | |
| 207 | 230 | pageSize: commitId ? 1 : 100, |
| 208 | 231 | pageNo: 1, |
| 209 | 232 | }; |
| 233 | + // 计划编码 + 批次号同时带上:只查当前这一期次(planNo)的提交记录,批次号用于限定范围 | |
| 234 | + if (planNo) { | |
| 235 | + queryData.planNo = planNo; | |
| 236 | + } | |
| 237 | + if (batchNo) { | |
| 238 | + queryData.batchNo = batchNo; | |
| 239 | + } | |
| 210 | 240 | if (commitId) { |
| 211 | 241 | queryData.commitId = commitId; |
| 212 | 242 | } |
| ... | ... | @@ -214,12 +244,25 @@ const getOrderDetail = async (planNo: string, commitId?: string) => { |
| 214 | 244 | console.log('接口返回:', res); |
| 215 | 245 | const list = res.data?.list || res.list || []; |
| 216 | 246 | |
| 247 | + // 按批次查看时按「第几次」正序,读起来和 App 列表一致 | |
| 248 | + if (batchNo) { | |
| 249 | + list.sort((a, b) => Number(a.sortNum || 0) - Number(b.sortNum || 0)); | |
| 250 | + } | |
| 251 | + | |
| 217 | 252 | orderDetail.value = list.map(item => { |
| 218 | - // ✅直接使用接口返回的 item.fields,不再读取 workRecords | |
| 253 | + // 一次养护可能有多次子上报(workRecords),每次上报有自己的字段值,逐条展示; | |
| 254 | + // 历史数据(双写上线前)没有 workRecords,退回用 item.fields 当唯一一条记录。 | |
| 219 | 255 | const useValues = Array.isArray(item.fields) ? item.fields : [] |
| 256 | + const workRecords = Array.isArray(item.workRecords) && item.workRecords.length | |
| 257 | + ? item.workRecords | |
| 258 | + : [{ workTime: item.createTime, values: useValues }] | |
| 220 | 259 | return { |
| 221 | 260 | ...item, |
| 222 | - fieldRows: useValues.map(buildFieldRow) | |
| 261 | + // 每条子上报一行自己的字段渲染结果,模板里一条记录一张卡 | |
| 262 | + recordRows: workRecords.map(record => ({ | |
| 263 | + workTime: record?.workTime ?? item.createTime, | |
| 264 | + fieldRows: (Array.isArray(record?.values) ? record.values : []).map(buildFieldRow) | |
| 265 | + })) | |
| 223 | 266 | } |
| 224 | 267 | }) |
| 225 | 268 | } catch (error) { |
| ... | ... | @@ -532,9 +575,9 @@ const openGeo = (geo) => { |
| 532 | 575 | // const workRecordIndex = ref(0) |
| 533 | 576 | // 页面加载 |
| 534 | 577 | onLoad((options) => { |
| 535 | - const {planNo, commitId } = options; | |
| 536 | - if (planNo) { | |
| 537 | - getOrderDetail(planNo, commitId); | |
| 578 | + const {planNo, commitId, batchNo } = options; | |
| 579 | + if (planNo || batchNo) { | |
| 580 | + getOrderDetail(planNo, commitId, batchNo); | |
| 538 | 581 | } else { |
| 539 | 582 | loading.value = false; |
| 540 | 583 | uni.showToast({title: '缺少工单ID', icon: 'none'}); |
| ... | ... | @@ -582,7 +625,9 @@ onLoad((options) => { |
| 582 | 625 | // 计划名称标题卡:白底 / 8px 圆角 / 卡片阴影 / 32rpx 500(设计稿顶部卡) |
| 583 | 626 | .record-head { |
| 584 | 627 | display: flex; |
| 585 | - align-items: center; | |
| 628 | + flex-direction: column; | |
| 629 | + align-items: flex-start; | |
| 630 | + gap: 8rpx; | |
| 586 | 631 | margin: 0 24rpx 24rpx; |
| 587 | 632 | padding: 24rpx; |
| 588 | 633 | background: #ffffff; |
| ... | ... | @@ -590,6 +635,13 @@ onLoad((options) => { |
| 590 | 635 | box-shadow: 0 4rpx 16rpx rgba(17, 24, 39, 0.06); |
| 591 | 636 | } |
| 592 | 637 | |
| 638 | +// 标题卡副标题:第X次 + 上报状态 | |
| 639 | +.record-sub { | |
| 640 | + font-size: 26rpx; | |
| 641 | + color: #6B7280; | |
| 642 | + line-height: 1.4; | |
| 643 | +} | |
| 644 | + | |
| 593 | 645 | .record-title { |
| 594 | 646 | flex: 1; |
| 595 | 647 | min-width: 0; |
| ... | ... | @@ -625,6 +677,21 @@ onLoad((options) => { |
| 625 | 677 | line-height: 1.4; |
| 626 | 678 | } |
| 627 | 679 | |
| 680 | +/* 区块标题右侧:多条上报时标注第几条 + 上报时间 */ | |
| 681 | +.sec-count { | |
| 682 | + margin-left: auto; | |
| 683 | + font-size: 24rpx; | |
| 684 | + color: #9CA3AF; | |
| 685 | + white-space: nowrap; | |
| 686 | +} | |
| 687 | + | |
| 688 | +/* 某条子上报没填字段时的占位文案 */ | |
| 689 | +.no-field-tip { | |
| 690 | + font-size: 26rpx; | |
| 691 | + color: #9CA3AF; | |
| 692 | + padding: 8rpx 0; | |
| 693 | +} | |
| 694 | + | |
| 628 | 695 | // 图片容器样式 |
| 629 | 696 | .cell-content-wrap { |
| 630 | 697 | //padding: 10rpx 0; | ... | ... |
pages-sub/daily/maintain-manage/index.vue
| ... | ... | @@ -91,9 +91,6 @@ |
| 91 | 91 | <!-- </up-button>--> |
| 92 | 92 | <!-- </view>--> |
| 93 | 93 | |
| 94 | - <view class="common-custom-btn" style="--btn-top: 48px;" @click="goToDetail(item)"> | |
| 95 | - 养护任务 | |
| 96 | - </view> | |
| 97 | 94 | <!-- <view class="u-body-item u-flex">--> |
| 98 | 95 | <!-- <view class="u-body-item-title">批次号:</view>--> |
| 99 | 96 | <!-- <view class="u-line-1 u-body-value">{{ item.batchNo || '-' }}</view>--> | ... | ... |
pages-sub/daily/maintain-manage/type-list-page.vue
| ... | ... | @@ -14,14 +14,11 @@ |
| 14 | 14 | <view class="task-card" v-for="(item, index) in currentList" :key="`${item.planNo}_${index}`"> |
| 15 | 15 | <!-- 计划名称 --> |
| 16 | 16 | <view class="card-head"> |
| 17 | - <view class="plan-name u-line-1">{{ item.planName || '无计划名称' }}</view> | |
| 18 | - </view> | |
| 19 | - <!-- 任务次数 --> | |
| 20 | - <view class="info-row"> | |
| 21 | - <view class="info-label">任务次数</view> | |
| 22 | - <view class="info-value" v-if="activeStatus === '2'">完成 {{ item.planFinishNum }} 次</view> | |
| 23 | - <!-- 一次一条期次(planNum=1)时「第几次」取期次序号,历史多条次数的期次仍按完成次数递增 --> | |
| 24 | - <view class="info-value" v-else>第 {{ item.planNum === 1 ? (item.sortNum || 1) : (item.planFinishNum + 1) }} 次</view> | |
| 17 | + <view class="plan-name-wrap"> | |
| 18 | + <view class="plan-name u-line-1">{{ item.planName || '无计划名称' }}</view> | |
| 19 | + <!-- 同一批次下多个期次的计划名称相同,必须显示计划编码才能区分 --> | |
| 20 | + <view class="plan-no u-line-1" v-if="item.planNo">计划编码:{{ item.planNo }}</view> | |
| 21 | + </view> | |
| 25 | 22 | </view> |
| 26 | 23 | <!-- 有效期 --> |
| 27 | 24 | <view class="info-row"> |
| ... | ... | @@ -31,6 +28,16 @@ |
| 31 | 28 | timeFormat(item.endTime, 'yyyy-mm-dd') }} |
| 32 | 29 | </view> |
| 33 | 30 | </view> |
| 31 | + <!-- 任务次数 --> | |
| 32 | + <view class="info-row"> | |
| 33 | + <view class="info-label">任务次数</view> | |
| 34 | + <!-- 已完成:一次一条期次时每个期次只完成 1 次,必须带上期次序号才能区分多次提交记录 --> | |
| 35 | + <view class="info-value" v-if="activeStatus === '2'"> | |
| 36 | + 有效期内第 {{ item.sortNum || 1 }} 个任务,共提交 {{ item.submitCount || 0 }} 次 | |
| 37 | + </view> | |
| 38 | + <!-- 一次一条期次(planNum=1)时「第几次」取期次序号,历史多条次数的期次仍按完成次数递增 --> | |
| 39 | + <view class="info-value" v-else>第 {{ item.planNum === 1 ? (item.sortNum || 1) : (item.planFinishNum + 1) }} 次</view> | |
| 40 | + </view> | |
| 34 | 41 | <!-- 操作区:统一收底,与信息区用分隔线隔开 --> |
| 35 | 42 | <view class="action-row"> |
| 36 | 43 | <!-- 待完成且一次都没上报过才允许置为失效;已有养护记录(进行中/已完成过)不再显示 --> |
| ... | ... | @@ -326,8 +333,11 @@ const handleGoWork = (item) => { |
| 326 | 333 | } |
| 327 | 334 | const handleGoWorkLook = (item) => { |
| 328 | 335 | const isView = activeStatus.value |
| 336 | + // 已完成:一次一条期次,传批次号让详情页把该批次所有期次的上报记录一起列出来; | |
| 337 | + // 已失效:只看向被置为失效的那一次,仍按计划编码查单条。 | |
| 338 | + const batchParam = activeStatus.value === '2' && item.batchNo ? `&batchNo=${item.batchNo}` : '' | |
| 329 | 339 | uni.navigateTo({ |
| 330 | - url: `/pages-sub/daily/maintain-manage/finish-plan-detail?planNo=${item.planNo}&planTypeId=${planTypeId.value}&businessType=${item.busiLine}&isView=${isView}` | |
| 340 | + url: `/pages-sub/daily/maintain-manage/finish-plan-detail?planNo=${item.planNo}&planTypeId=${planTypeId.value}&businessType=${item.busiLine}&isView=${isView}${batchParam}` | |
| 331 | 341 | }) |
| 332 | 342 | } |
| 333 | 343 | const handleEndTask = async (item) => { |
| ... | ... | @@ -408,22 +418,31 @@ onUnload(() => { |
| 408 | 418 | display: flex; |
| 409 | 419 | align-items: center; |
| 410 | 420 | gap: 16rpx; |
| 411 | - margin-bottom: 16rpx; | |
| 421 | + /* 与 .plan-no / .info-row 的间距保持一致,卡内文字行上下距离统一 */ | |
| 422 | + margin-bottom: 8rpx; | |
| 412 | 423 | } |
| 413 | -.plan-name { | |
| 424 | +.plan-name-wrap { | |
| 414 | 425 | flex: 1; |
| 415 | 426 | min-width: 0; |
| 427 | +} | |
| 428 | +.plan-name { | |
| 416 | 429 | font-size: 32rpx; |
| 417 | 430 | color: #1F2937; |
| 418 | 431 | font-weight: 500; |
| 419 | 432 | line-height: 1.4; |
| 420 | 433 | } |
| 434 | +.plan-no { | |
| 435 | + margin-top: 8rpx; | |
| 436 | + font-size: 24rpx; | |
| 437 | + color: #9CA3AF; | |
| 438 | + line-height: 1.4; | |
| 439 | +} | |
| 421 | 440 | .info-row { |
| 422 | 441 | display: flex; |
| 423 | 442 | align-items: center; |
| 424 | 443 | justify-content: space-between; |
| 425 | 444 | gap: 16rpx; |
| 426 | - margin-bottom: 6rpx; | |
| 445 | + margin-bottom: 8rpx; | |
| 427 | 446 | } |
| 428 | 447 | .info-label { |
| 429 | 448 | font-size: 26rpx; | ... | ... |
pages-sub/daily/maintain-manage/type-list.vue
| 1 | 1 | <template> |
| 2 | 2 | <view class="type-grid"> |
| 3 | - <!-- 动态渲染养护类型卡片 --> | |
| 4 | - <template v-if="tagList.length > 0"> | |
| 5 | - <view class="type-card" v-for="(item, idx) in tagList" :key="idx" hover-class="type-card--hover" | |
| 3 | + <!-- 按养护类型渲染计划卡片:同一类型下多个批次合并成一张卡,批次号/计划名称等每项一行,多批次时逐行列出 --> | |
| 4 | + <template v-if="cardList.length > 0"> | |
| 5 | + <view class="type-card" v-for="item in cardList" :key="item.key" hover-class="type-card--hover" | |
| 6 | 6 | @click="toNextPage(item)"> |
| 7 | - <!-- 类型图标:取类型名称首字,底色淡蓝 --> | |
| 8 | - <view class="type-icon">{{ (item.planTypeName || '养护').charAt(0) }}</view> | |
| 9 | - <view class="type-name">{{ item.planTypeName }}</view> | |
| 10 | - <view class="type-foot"> | |
| 11 | - <text class="type-foot-label">待完成任务</text> | |
| 12 | - <text class="type-count">{{ item.todayPendingCount }}</text> | |
| 7 | + <view class="info-row" v-for="row in item.rows" :key="row.label"> | |
| 8 | + <text class="info-label">{{ row.label }}:</text> | |
| 9 | + <view class="info-value"> | |
| 10 | + <view class="info-line" v-for="(value, idx) in row.values" :key="idx" | |
| 11 | + :class="{ 'info-line--count': row.label === '待完成任务' }">{{ value }}</view> | |
| 12 | + </view> | |
| 13 | 13 | </view> |
| 14 | 14 | </view> |
| 15 | 15 | </template> |
| ... | ... | @@ -20,15 +20,32 @@ |
| 20 | 20 | </template> |
| 21 | 21 | <script setup lang="ts"> |
| 22 | 22 | import { ref } from 'vue' |
| 23 | -import { onLoad, onShow, onUnload } from '@dcloudio/uni-app'; | |
| 23 | +import { onShow, onUnload } from '@dcloudio/uni-app'; | |
| 24 | 24 | |
| 25 | -const tagList = ref<Array<{ | |
| 25 | +const cardList = ref<Array<{ | |
| 26 | + key: string | |
| 26 | 27 | planTypeId: string | number |
| 27 | - planTypeName: string | |
| 28 | 28 | batchNos: string[] |
| 29 | - todayPendingCount: number | |
| 29 | + rows: Array<{ label: string; values: string[] }> | |
| 30 | 30 | }>>([]) |
| 31 | 31 | |
| 32 | +/** 日期字段可能是 'yyyy-MM-dd' 字符串(LocalDate)或时间戳,统一转成 yyyy-MM-dd */ | |
| 33 | +const formatDate = (value: any) => { | |
| 34 | + if (value === null || value === undefined || value === '') return '--' | |
| 35 | + if (Array.isArray(value)) { | |
| 36 | + return `${value[0]}-${String(value[1]).padStart(2, '0')}-${String(value[2]).padStart(2, '0')}` | |
| 37 | + } | |
| 38 | + if (typeof value === 'number') { | |
| 39 | + const d = new Date(value) | |
| 40 | + return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}` | |
| 41 | + } | |
| 42 | + // 'yyyy-MM-dd HH:mm:ss' 之类的字符串只取日期部分 | |
| 43 | + return String(value).slice(0, 10) | |
| 44 | +} | |
| 45 | + | |
| 46 | +/** 去重并保持原顺序,合并卡片时避免同一个值重复占一行 */ | |
| 47 | +const uniq = (list: any[]) => Array.from(new Set(list.filter(v => v !== null && v !== undefined && v !== ''))) | |
| 48 | + | |
| 32 | 49 | onShow(() => { |
| 33 | 50 | // 从storage拿上一页传过来完整对象 |
| 34 | 51 | const item = uni.getStorageSync('temp_maintain_item') |
| ... | ... | @@ -37,15 +54,43 @@ onShow(() => { |
| 37 | 54 | title: '数据异常,请返回重试', |
| 38 | 55 | icon: 'none' |
| 39 | 56 | }); |
| 40 | - return; | |
| 57 | + return | |
| 41 | 58 | } |
| 42 | - // 直接取planTypes | |
| 43 | - tagList.value = item.planTypes || [] | |
| 59 | + const roadName = item.roadName || '--' | |
| 60 | + // 接口已按养护类型分组,同一类型下的多个批次在这里合并成一张卡 | |
| 61 | + cardList.value = (item.planTypes || []).map((type: any) => { | |
| 62 | + const batches = Array.isArray(type.batches) && type.batches.length | |
| 63 | + ? type.batches | |
| 64 | + : (type.batchNos || []).map((batchNo: string) => ({ | |
| 65 | + batchNo, | |
| 66 | + planName: '', | |
| 67 | + beginTime: null, | |
| 68 | + endTime: null, | |
| 69 | + todayPendingCount: type.todayPendingCount | |
| 70 | + })) | |
| 71 | + const batchNos = batches.map((batch: any) => batch.batchNo).filter(Boolean) | |
| 72 | + const pendingCount = batches.reduce((sum: number, batch: any) => sum + Number(batch.todayPendingCount || 0), 0) | |
| 73 | + return { | |
| 74 | + key: String(type.planTypeId), | |
| 75 | + planTypeId: type.planTypeId, | |
| 76 | + batchNos, | |
| 77 | + rows: [ | |
| 78 | + { label: '批次号', values: batchNos.length ? batchNos : ['--'] }, | |
| 79 | + { label: '计划名称', values: uniq(batches.map((batch: any) => batch.planName)).length | |
| 80 | + ? uniq(batches.map((batch: any) => batch.planName)) : ['--'] }, | |
| 81 | + { label: '道路名称', values: [roadName] }, | |
| 82 | + { label: '计划类型', values: [type.planTypeName || '--'] }, | |
| 83 | + { label: '执行时间', values: uniq(batches.map((batch: any) => | |
| 84 | + `${formatDate(batch.beginTime)} - ${formatDate(batch.endTime)}`)) }, | |
| 85 | + { label: '待完成任务', values: [`${pendingCount}次`] } | |
| 86 | + ] | |
| 87 | + } | |
| 88 | + }) | |
| 44 | 89 | }); |
| 45 | 90 | |
| 46 | -// 标签点击跳转,batchNos存入storage,并且把planTypeId拼到url传给下一页 | |
| 91 | +// 点击卡片跳到期次列表,把该类型下所有批次号一起带过去 | |
| 47 | 92 | const toNextPage = (item: any) => { |
| 48 | - uni.setStorageSync('temp_maintain_rawList', item.batchNos) | |
| 93 | + uni.setStorageSync('temp_maintain_rawList', item.batchNos || []) | |
| 49 | 94 | uni.navigateTo({ |
| 50 | 95 | url: `/pages-sub/daily/maintain-manage/type-list-page?planTypeId=${item.planTypeId}` |
| 51 | 96 | }) |
| ... | ... | @@ -57,15 +102,15 @@ onUnload(()=>{ |
| 57 | 102 | }) |
| 58 | 103 | </script> |
| 59 | 104 | <style scoped lang="scss"> |
| 60 | -/* 两列卡片栅格:页面 24rpx 内边距,卡片间距 16rpx(设计稿 .type-grid) */ | |
| 105 | +/* 卡片列表:页面 24rpx 内边距,卡片间距 16rpx */ | |
| 61 | 106 | .type-grid { |
| 62 | - display: grid; | |
| 63 | - grid-template-columns: 1fr 1fr; | |
| 107 | + display: flex; | |
| 108 | + flex-direction: column; | |
| 64 | 109 | gap: 16rpx; |
| 65 | 110 | padding: 24rpx; |
| 66 | 111 | } |
| 67 | 112 | |
| 68 | -/* 卡片:12px 内边距 / 8px 圆角 / 8rpx 内部行距(设计稿 .type-card) */ | |
| 113 | +/* 卡片:一张卡对应一个养护类型(多批次合并),撑满一行 */ | |
| 69 | 114 | .type-card { |
| 70 | 115 | display: flex; |
| 71 | 116 | flex-direction: column; |
| ... | ... | @@ -82,69 +127,44 @@ onUnload(()=>{ |
| 82 | 127 | } |
| 83 | 128 | } |
| 84 | 129 | |
| 85 | -/* 类型图标位:68rpx 方块 + 淡蓝底(设计稿 .type-ico) */ | |
| 86 | -.type-icon { | |
| 87 | - display: flex; | |
| 88 | - align-items: center; | |
| 89 | - justify-content: center; | |
| 90 | - width: 68rpx; | |
| 91 | - height: 68rpx; | |
| 92 | - background: #EBF3FF; | |
| 93 | - border-radius: 8rpx; | |
| 94 | - font-size: 30rpx; | |
| 95 | - font-weight: 600; | |
| 96 | - color: #2563EB; | |
| 97 | - line-height: 1; | |
| 98 | -} | |
| 99 | - | |
| 100 | -/* 类型名称:14px / 500(设计稿 .type-card 标题) */ | |
| 101 | -.type-name { | |
| 102 | - font-size: 28rpx; | |
| 103 | - font-weight: 500; | |
| 104 | - color: #1F2937; | |
| 105 | - line-height: 1.4; | |
| 106 | - white-space: nowrap; | |
| 107 | - overflow: hidden; | |
| 108 | - text-overflow: ellipsis; | |
| 109 | -} | |
| 110 | - | |
| 111 | -/* 底部统计行:左侧说明 11px 浅灰,右侧数量标签(设计稿 .type-meta) */ | |
| 112 | -.type-foot { | |
| 130 | +/* 信息行:左侧字段名浅灰,右侧值右对齐;多个批次时值逐行显示 */ | |
| 131 | +.info-row { | |
| 113 | 132 | display: flex; |
| 114 | - align-items: center; | |
| 133 | + align-items: flex-start; | |
| 115 | 134 | justify-content: space-between; |
| 116 | - margin-top: auto; | |
| 117 | - font-size: 22rpx; | |
| 118 | - color: #9CA3AF; | |
| 135 | + gap: 16rpx; | |
| 119 | 136 | } |
| 120 | 137 | |
| 121 | -.type-foot-label { | |
| 122 | - font-size: 22rpx; | |
| 138 | +.info-label { | |
| 139 | + flex-shrink: 0; | |
| 140 | + font-size: 26rpx; | |
| 123 | 141 | color: #9CA3AF; |
| 124 | 142 | line-height: 1.5; |
| 125 | 143 | } |
| 126 | 144 | |
| 127 | -/* 数量标签:灰底胶囊(设计稿 .tag.gray) */ | |
| 128 | -.type-count { | |
| 129 | - display: inline-flex; | |
| 130 | - align-items: center; | |
| 131 | - flex-shrink: 0; | |
| 132 | - height: 40rpx; | |
| 133 | - padding: 0 16rpx; | |
| 134 | - background: #F4F6FA; | |
| 135 | - border-radius: 8rpx; | |
| 136 | - font-size: 22rpx; | |
| 137 | - font-weight: 500; | |
| 145 | +.info-value { | |
| 146 | + flex: 1; | |
| 147 | + min-width: 0; | |
| 148 | + text-align: right; | |
| 149 | +} | |
| 150 | + | |
| 151 | +.info-line { | |
| 152 | + font-size: 26rpx; | |
| 138 | 153 | color: #4B5563; |
| 139 | - line-height: 1; | |
| 154 | + line-height: 1.5; | |
| 155 | + word-break: break-all; | |
| 156 | + | |
| 157 | + /* 待完成次数加重,便于一眼看到 */ | |
| 158 | + &--count { | |
| 159 | + color: #2563EB; | |
| 160 | + font-weight: 500; | |
| 161 | + } | |
| 140 | 162 | } |
| 141 | 163 | |
| 142 | -/* 空状态 */ | |
| 143 | 164 | .type-empty { |
| 144 | - grid-column: 1 / -1; | |
| 145 | - padding-top: 60rpx; | |
| 165 | + padding: 120rpx 0; | |
| 166 | + text-align: center; | |
| 146 | 167 | font-size: 26rpx; |
| 147 | 168 | color: #9CA3AF; |
| 148 | - text-align: center; | |
| 149 | 169 | } |
| 150 | 170 | </style> | ... | ... |