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,6 +14,10 @@ | ||
| 14 | <!-- 1. 计划名称:独立标题卡(设计稿顶部白卡) --> | 14 | <!-- 1. 计划名称:独立标题卡(设计稿顶部白卡) --> |
| 15 | <view class="record-head"> | 15 | <view class="record-head"> |
| 16 | <view class="record-title">{{i.planName || '--'}}</view> | 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 | </view> | 21 | </view> |
| 18 | <!-- 工单详情内容 --> | 22 | <!-- 工单详情内容 --> |
| 19 | <view class="form-card"> | 23 | <view class="form-card"> |
| @@ -95,56 +99,66 @@ | @@ -95,56 +99,66 @@ | ||
| 95 | <view class="u-body-item-title">提交时间:</view> | 99 | <view class="u-body-item-title">提交时间:</view> |
| 96 | <view class="u-line-1 u-body-value">{{ i.createTime==null?'--':timeFormat(i.createTime,'yyyy-mm-dd hh:MM:ss') }}</view> | 100 | <view class="u-line-1 u-body-value">{{ i.createTime==null?'--':timeFormat(i.createTime,'yyyy-mm-dd hh:MM:ss') }}</view> |
| 97 | </view> | 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 | </view> | 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 | </view> | 163 | </view> |
| 150 | </template> | 164 | </template> |
| @@ -195,18 +209,34 @@ const previewImage = (currentUrl, allUrls) => { | @@ -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 | console.log('getOrderDetail') | 226 | console.log('getOrderDetail') |
| 203 | try { | 227 | try { |
| 204 | loading.value = true; | 228 | loading.value = true; |
| 205 | let queryData: any = { | 229 | let queryData: any = { |
| 206 | - planNo: planNo, | ||
| 207 | pageSize: commitId ? 1 : 100, | 230 | pageSize: commitId ? 1 : 100, |
| 208 | pageNo: 1, | 231 | pageNo: 1, |
| 209 | }; | 232 | }; |
| 233 | + // 计划编码 + 批次号同时带上:只查当前这一期次(planNo)的提交记录,批次号用于限定范围 | ||
| 234 | + if (planNo) { | ||
| 235 | + queryData.planNo = planNo; | ||
| 236 | + } | ||
| 237 | + if (batchNo) { | ||
| 238 | + queryData.batchNo = batchNo; | ||
| 239 | + } | ||
| 210 | if (commitId) { | 240 | if (commitId) { |
| 211 | queryData.commitId = commitId; | 241 | queryData.commitId = commitId; |
| 212 | } | 242 | } |
| @@ -214,12 +244,25 @@ const getOrderDetail = async (planNo: string, commitId?: string) => { | @@ -214,12 +244,25 @@ const getOrderDetail = async (planNo: string, commitId?: string) => { | ||
| 214 | console.log('接口返回:', res); | 244 | console.log('接口返回:', res); |
| 215 | const list = res.data?.list || res.list || []; | 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 | orderDetail.value = list.map(item => { | 252 | orderDetail.value = list.map(item => { |
| 218 | - // ✅直接使用接口返回的 item.fields,不再读取 workRecords | 253 | + // 一次养护可能有多次子上报(workRecords),每次上报有自己的字段值,逐条展示; |
| 254 | + // 历史数据(双写上线前)没有 workRecords,退回用 item.fields 当唯一一条记录。 | ||
| 219 | const useValues = Array.isArray(item.fields) ? item.fields : [] | 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 | return { | 259 | return { |
| 221 | ...item, | 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 | } catch (error) { | 268 | } catch (error) { |
| @@ -532,9 +575,9 @@ const openGeo = (geo) => { | @@ -532,9 +575,9 @@ const openGeo = (geo) => { | ||
| 532 | // const workRecordIndex = ref(0) | 575 | // const workRecordIndex = ref(0) |
| 533 | // 页面加载 | 576 | // 页面加载 |
| 534 | onLoad((options) => { | 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 | } else { | 581 | } else { |
| 539 | loading.value = false; | 582 | loading.value = false; |
| 540 | uni.showToast({title: '缺少工单ID', icon: 'none'}); | 583 | uni.showToast({title: '缺少工单ID', icon: 'none'}); |
| @@ -582,7 +625,9 @@ onLoad((options) => { | @@ -582,7 +625,9 @@ onLoad((options) => { | ||
| 582 | // 计划名称标题卡:白底 / 8px 圆角 / 卡片阴影 / 32rpx 500(设计稿顶部卡) | 625 | // 计划名称标题卡:白底 / 8px 圆角 / 卡片阴影 / 32rpx 500(设计稿顶部卡) |
| 583 | .record-head { | 626 | .record-head { |
| 584 | display: flex; | 627 | display: flex; |
| 585 | - align-items: center; | 628 | + flex-direction: column; |
| 629 | + align-items: flex-start; | ||
| 630 | + gap: 8rpx; | ||
| 586 | margin: 0 24rpx 24rpx; | 631 | margin: 0 24rpx 24rpx; |
| 587 | padding: 24rpx; | 632 | padding: 24rpx; |
| 588 | background: #ffffff; | 633 | background: #ffffff; |
| @@ -590,6 +635,13 @@ onLoad((options) => { | @@ -590,6 +635,13 @@ onLoad((options) => { | ||
| 590 | box-shadow: 0 4rpx 16rpx rgba(17, 24, 39, 0.06); | 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 | .record-title { | 645 | .record-title { |
| 594 | flex: 1; | 646 | flex: 1; |
| 595 | min-width: 0; | 647 | min-width: 0; |
| @@ -625,6 +677,21 @@ onLoad((options) => { | @@ -625,6 +677,21 @@ onLoad((options) => { | ||
| 625 | line-height: 1.4; | 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 | .cell-content-wrap { | 696 | .cell-content-wrap { |
| 630 | //padding: 10rpx 0; | 697 | //padding: 10rpx 0; |
pages-sub/daily/maintain-manage/index.vue
| @@ -91,9 +91,6 @@ | @@ -91,9 +91,6 @@ | ||
| 91 | <!-- </up-button>--> | 91 | <!-- </up-button>--> |
| 92 | <!-- </view>--> | 92 | <!-- </view>--> |
| 93 | 93 | ||
| 94 | - <view class="common-custom-btn" style="--btn-top: 48px;" @click="goToDetail(item)"> | ||
| 95 | - 养护任务 | ||
| 96 | - </view> | ||
| 97 | <!-- <view class="u-body-item u-flex">--> | 94 | <!-- <view class="u-body-item u-flex">--> |
| 98 | <!-- <view class="u-body-item-title">批次号:</view>--> | 95 | <!-- <view class="u-body-item-title">批次号:</view>--> |
| 99 | <!-- <view class="u-line-1 u-body-value">{{ item.batchNo || '-' }}</view>--> | 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 +14,11 @@ | ||
| 14 | <view class="task-card" v-for="(item, index) in currentList" :key="`${item.planNo}_${index}`"> | 14 | <view class="task-card" v-for="(item, index) in currentList" :key="`${item.planNo}_${index}`"> |
| 15 | <!-- 计划名称 --> | 15 | <!-- 计划名称 --> |
| 16 | <view class="card-head"> | 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 | </view> | 22 | </view> |
| 26 | <!-- 有效期 --> | 23 | <!-- 有效期 --> |
| 27 | <view class="info-row"> | 24 | <view class="info-row"> |
| @@ -31,6 +28,16 @@ | @@ -31,6 +28,16 @@ | ||
| 31 | timeFormat(item.endTime, 'yyyy-mm-dd') }} | 28 | timeFormat(item.endTime, 'yyyy-mm-dd') }} |
| 32 | </view> | 29 | </view> |
| 33 | </view> | 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 | <view class="action-row"> | 42 | <view class="action-row"> |
| 36 | <!-- 待完成且一次都没上报过才允许置为失效;已有养护记录(进行中/已完成过)不再显示 --> | 43 | <!-- 待完成且一次都没上报过才允许置为失效;已有养护记录(进行中/已完成过)不再显示 --> |
| @@ -326,8 +333,11 @@ const handleGoWork = (item) => { | @@ -326,8 +333,11 @@ const handleGoWork = (item) => { | ||
| 326 | } | 333 | } |
| 327 | const handleGoWorkLook = (item) => { | 334 | const handleGoWorkLook = (item) => { |
| 328 | const isView = activeStatus.value | 335 | const isView = activeStatus.value |
| 336 | + // 已完成:一次一条期次,传批次号让详情页把该批次所有期次的上报记录一起列出来; | ||
| 337 | + // 已失效:只看向被置为失效的那一次,仍按计划编码查单条。 | ||
| 338 | + const batchParam = activeStatus.value === '2' && item.batchNo ? `&batchNo=${item.batchNo}` : '' | ||
| 329 | uni.navigateTo({ | 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 | const handleEndTask = async (item) => { | 343 | const handleEndTask = async (item) => { |
| @@ -408,22 +418,31 @@ onUnload(() => { | @@ -408,22 +418,31 @@ onUnload(() => { | ||
| 408 | display: flex; | 418 | display: flex; |
| 409 | align-items: center; | 419 | align-items: center; |
| 410 | gap: 16rpx; | 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 | flex: 1; | 425 | flex: 1; |
| 415 | min-width: 0; | 426 | min-width: 0; |
| 427 | +} | ||
| 428 | +.plan-name { | ||
| 416 | font-size: 32rpx; | 429 | font-size: 32rpx; |
| 417 | color: #1F2937; | 430 | color: #1F2937; |
| 418 | font-weight: 500; | 431 | font-weight: 500; |
| 419 | line-height: 1.4; | 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 | .info-row { | 440 | .info-row { |
| 422 | display: flex; | 441 | display: flex; |
| 423 | align-items: center; | 442 | align-items: center; |
| 424 | justify-content: space-between; | 443 | justify-content: space-between; |
| 425 | gap: 16rpx; | 444 | gap: 16rpx; |
| 426 | - margin-bottom: 6rpx; | 445 | + margin-bottom: 8rpx; |
| 427 | } | 446 | } |
| 428 | .info-label { | 447 | .info-label { |
| 429 | font-size: 26rpx; | 448 | font-size: 26rpx; |
pages-sub/daily/maintain-manage/type-list.vue
| 1 | <template> | 1 | <template> |
| 2 | <view class="type-grid"> | 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 | @click="toNextPage(item)"> | 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 | </view> | 13 | </view> |
| 14 | </view> | 14 | </view> |
| 15 | </template> | 15 | </template> |
| @@ -20,15 +20,32 @@ | @@ -20,15 +20,32 @@ | ||
| 20 | </template> | 20 | </template> |
| 21 | <script setup lang="ts"> | 21 | <script setup lang="ts"> |
| 22 | import { ref } from 'vue' | 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 | planTypeId: string | number | 27 | planTypeId: string | number |
| 27 | - planTypeName: string | ||
| 28 | batchNos: string[] | 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 | onShow(() => { | 49 | onShow(() => { |
| 33 | // 从storage拿上一页传过来完整对象 | 50 | // 从storage拿上一页传过来完整对象 |
| 34 | const item = uni.getStorageSync('temp_maintain_item') | 51 | const item = uni.getStorageSync('temp_maintain_item') |
| @@ -37,15 +54,43 @@ onShow(() => { | @@ -37,15 +54,43 @@ onShow(() => { | ||
| 37 | title: '数据异常,请返回重试', | 54 | title: '数据异常,请返回重试', |
| 38 | icon: 'none' | 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 | const toNextPage = (item: any) => { | 92 | const toNextPage = (item: any) => { |
| 48 | - uni.setStorageSync('temp_maintain_rawList', item.batchNos) | 93 | + uni.setStorageSync('temp_maintain_rawList', item.batchNos || []) |
| 49 | uni.navigateTo({ | 94 | uni.navigateTo({ |
| 50 | url: `/pages-sub/daily/maintain-manage/type-list-page?planTypeId=${item.planTypeId}` | 95 | url: `/pages-sub/daily/maintain-manage/type-list-page?planTypeId=${item.planTypeId}` |
| 51 | }) | 96 | }) |
| @@ -57,15 +102,15 @@ onUnload(()=>{ | @@ -57,15 +102,15 @@ onUnload(()=>{ | ||
| 57 | }) | 102 | }) |
| 58 | </script> | 103 | </script> |
| 59 | <style scoped lang="scss"> | 104 | <style scoped lang="scss"> |
| 60 | -/* 两列卡片栅格:页面 24rpx 内边距,卡片间距 16rpx(设计稿 .type-grid) */ | 105 | +/* 卡片列表:页面 24rpx 内边距,卡片间距 16rpx */ |
| 61 | .type-grid { | 106 | .type-grid { |
| 62 | - display: grid; | ||
| 63 | - grid-template-columns: 1fr 1fr; | 107 | + display: flex; |
| 108 | + flex-direction: column; | ||
| 64 | gap: 16rpx; | 109 | gap: 16rpx; |
| 65 | padding: 24rpx; | 110 | padding: 24rpx; |
| 66 | } | 111 | } |
| 67 | 112 | ||
| 68 | -/* 卡片:12px 内边距 / 8px 圆角 / 8rpx 内部行距(设计稿 .type-card) */ | 113 | +/* 卡片:一张卡对应一个养护类型(多批次合并),撑满一行 */ |
| 69 | .type-card { | 114 | .type-card { |
| 70 | display: flex; | 115 | display: flex; |
| 71 | flex-direction: column; | 116 | flex-direction: column; |
| @@ -82,69 +127,44 @@ onUnload(()=>{ | @@ -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 | display: flex; | 132 | display: flex; |
| 114 | - align-items: center; | 133 | + align-items: flex-start; |
| 115 | justify-content: space-between; | 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 | color: #9CA3AF; | 141 | color: #9CA3AF; |
| 124 | line-height: 1.5; | 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 | color: #4B5563; | 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 | .type-empty { | 164 | .type-empty { |
| 144 | - grid-column: 1 / -1; | ||
| 145 | - padding-top: 60rpx; | 165 | + padding: 120rpx 0; |
| 166 | + text-align: center; | ||
| 146 | font-size: 26rpx; | 167 | font-size: 26rpx; |
| 147 | color: #9CA3AF; | 168 | color: #9CA3AF; |
| 148 | - text-align: center; | ||
| 149 | } | 169 | } |
| 150 | </style> | 170 | </style> |