Commit 3da5bb94666325174474add84f0a3351cd4e501a

Authored by 王彪总
2 parents e93ebf16 9120066c

Merge remote-tracking branch 'origin/dev2.0' into dev2.0

# Conflicts:
#	App.vue
#	pages-sub/daily/maintain-manage/index.vue
#	pages-sub/daily/maintain-manage/now-list-page.vue
#	pages-sub/daily/maintain-manage/type-list-page.vue
#	pages-sub/problem/ai-manage/index.vue
#	pages-sub/problem/regional-order-manage/index.vue
#	pages-sub/problem/regional-order-manage/list-detail.vue
#	pages-sub/problem/regional-order-manage/order-detail.vue
#	pages-sub/problem/work-order-manage/index.vue
#	pages-sub/problem/work-order-manage/order-detail.vue
pages-sub/daily/maintain-manage/index.vue
... ... @@ -80,6 +80,20 @@
80 80  
81 81 <template #body>
82 82 <view class="card-body">
  83 +<!-- <view class="">-->
  84 +<!-- <up-button-->
  85 +<!-- type="primary"-->
  86 +<!-- size="mini"-->
  87 +<!-- @click="goToDetail(item)"-->
  88 +<!-- class="submit-record-btn"-->
  89 +<!-- >-->
  90 +<!-- 计划明细-->
  91 +<!-- </up-button>-->
  92 +<!-- </view>-->
  93 +
  94 + <view class="common-custom-btn" style="--btn-top: 48px;" @click="goToDetail(item)">
  95 + 养护任务
  96 + </view>
83 97 <!-- <view class="u-body-item u-flex">-->
84 98 <!-- <view class="u-body-item-title">批次号:</view>-->
85 99 <!-- <view class="u-line-1 u-body-value">{{ item.batchNo || '-' }}</view>-->
... ...
pages-sub/daily/maintain-manage/now-list-page.vue
1 1 <template>
2   - <view class="page-wrap">
3   - <!-- 顶部提示栏 -->
4   - <view class="tip-bar" v-if="isView==1" >
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[0].workRecords" :key="index">
10   - <view class="item-title">【第{{ item.sortNum || (index + 1) }}次上报】{{ planName }}</view>
11   - <view class="item-row item-row-bottom">
12   - <view class="item-time">提交时间:{{ timeFormat(item.createTime,'yyyy-mm-dd hh:MM:ss')}}</view>
13   - <view class="look-btn" @click="handleView(index,item,reportList[0])">查看</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" v-if="isView==1">
23   - <up-button type="primary" size="large" block @click="handleAddReport">上报本次记录</up-button>
  2 + <view class="page-wrap">
  3 + <!-- 顶部提示栏 -->
  4 + <view class="tip-bar" v-if="isView==1" >
  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[0].workRecords" :key="index">
  10 + <view class="item-title">【第{{ item.sortNum || (index + 1) }}次上报】{{ planName }}</view>
  11 + <view class="item-row item-row-bottom">
  12 + <view class="item-time">提交时间:{{ timeFormat(item.createTime,'yyyy-mm-dd hh:MM:ss')}}</view>
  13 + <view class="look-btn" @click="handleView(index,item,reportList[0])">查看</view>
24 14 </view>
  15 + </view>
25 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" v-if="isView==1">
  23 + <up-button type="primary" size="large" block @click="handleAddReport">上报本次记录</up-button>
  24 + </view>
  25 + </view>
26 26 </template>
27 27 <script setup lang="ts">
28 28 import { ref } from 'vue'
... ... @@ -43,184 +43,184 @@ const currentCommitId = ref(&#39;&#39;)
43 43  
44 44 // onLoad接收url传过来的planNo
45 45 onLoad((options) => {
46   - if (options.planNo) {
47   - planNo.value = options.planNo
48   - planTypeId.value = options.planTypeId
49   - businessType.value = options.businessType
50   - isView.value = options.isView
51   - getReportList()
52   - } else {
53   - uni.showToast({ title: 'planNo参数缺失', icon: 'none' })
54   - }
  46 + if (options.planNo) {
  47 + planNo.value = options.planNo
  48 + planTypeId.value = options.planTypeId
  49 + businessType.value = options.businessType
  50 + isView.value = options.isView
  51 + getReportList()
  52 + } else {
  53 + uni.showToast({ title: 'planNo参数缺失', icon: 'none' })
  54 + }
55 55 })
56 56 // 请求上报记录接口
57 57 const getReportList = async () => {
58   - console.log('3123')
59   - uni.showLoading({ title: '加载中...', mask: true })
60   - try {
61   - // 传入planNo调取接口
62   - let data = {
63   - planNo: planNo.value,
64   - pageNo: 1,
65   - pageSize: 10
66   - }
67   - const res = await maintainPlanList(data)
68   - // 接口返回list,无数据则reportList为空,页面自动显示empty
69   - dataList.value = res?.list[0].workRecords
70   - planName.value = res?.list[0].planName
71   - reportList.value = res?.list || []
  58 + console.log('3123')
  59 + uni.showLoading({ title: '加载中...', mask: true })
  60 + try {
  61 + // 传入planNo调取接口
  62 + let data = {
  63 + planNo: planNo.value,
  64 + pageNo: 1,
  65 + pageSize: 10
  66 + }
  67 + const res = await maintainPlanList(data)
  68 + // 接口返回list,无数据则reportList为空,页面自动显示empty
  69 + dataList.value = res?.list[0].workRecords
  70 + planName.value = res?.list[0].planName
  71 + reportList.value = res?.list || []
72 72  
73   -
74 73  
75   - // ============ 新增逻辑:寻找commitState ===1 的commitId ============
76   - currentCommitId.value = ''
77   - if(reportList.value.length > 0){
78   - // 找到第一条 commitState ===1 的数据
79   - const activeItem = reportList.value.find(item => item.commitState === 1)
80   - if(activeItem){
81   - currentCommitId.value = activeItem.commitId || ''
82   - }
83   - // 没有找到 activeItem,currentCommitId保持 ''
84   - }
85   - // reportList为空直接 currentCommitId=''
86 74  
87   - } catch (err) {
88   - console.error('获取上报记录失败', err)
89   - // uni.showToast({ title: '加载失败', icon: 'none' })
90   - } finally {
91   - uni.hideLoading()
  75 + // ============ 新增逻辑:寻找commitState ===1 的commitId ============
  76 + currentCommitId.value = ''
  77 + if(reportList.value.length > 0){
  78 + // 找到第一条 commitState ===1 的数据
  79 + const activeItem = reportList.value.find(item => item.commitState === 1)
  80 + if(activeItem){
  81 + currentCommitId.value = activeItem.commitId || ''
  82 + }
  83 + // 没有找到 activeItem,currentCommitId保持 ''
92 84 }
  85 + // reportList为空直接 currentCommitId=''
  86 +
  87 + } catch (err) {
  88 + console.error('获取上报记录失败', err)
  89 + // uni.showToast({ title: '加载失败', icon: 'none' })
  90 + } finally {
  91 + uni.hideLoading()
  92 + }
93 93 }
94 94 // 查看上报详情
95 95 const handleView = (index,item,i) => {
96   - console.log('查看详情', item)
97   - console.log('查看详情', index)
98   - uni.navigateTo({
99   - // 后端 page 接口支持 commitId 精确过滤,必须带上,否则会把该计划全部上报都展示出来
100   - url: `/pages-sub/daily/maintain-manage/finish-plan-detail?planNo=${i.planNo}&commitId=${i.commitId}&index=${index}`
101   - })
  96 + console.log('查看详情', item)
  97 + console.log('查看详情', index)
  98 + uni.navigateTo({
  99 + // 后端 page 接口支持 commitId 精确过滤,必须带上,否则会把该计划全部上报都展示出来
  100 + url: `/pages-sub/daily/maintain-manage/finish-plan-detail?planNo=${i.planNo}&commitId=${i.commitId}&index=${index}`
  101 + })
102 102 }
103 103 // 新增上报记录,把planNo + currentCommitId带到新增页面
104 104 const handleAddReport = () => {
105   - console.log('上报本次记录', currentCommitId.value)
106   - uni.navigateTo({
107   - url: `/pages-sub/daily/maintain-manage/add-record?planNo=${planNo.value}&planTypeId=${planTypeId.value}&businessType=${businessType.value}&commitId=${currentCommitId.value}`
108   - })
  105 + console.log('上报本次记录', currentCommitId.value)
  106 + uni.navigateTo({
  107 + url: `/pages-sub/daily/maintain-manage/add-record?planNo=${planNo.value}&planTypeId=${planTypeId.value}&businessType=${businessType.value}&commitId=${currentCommitId.value}`
  108 + })
109 109 }
110 110 </script>
111 111 <style scoped lang="scss">
112 112 .page-wrap {
113   - min-height: 100vh;
114   - // 底部固定栏(按钮 88rpx + 上下各 24rpx)+ 安全区
115   - padding-bottom: calc(180rpx + constant(safe-area-inset-bottom));
116   - padding-bottom: calc(180rpx + env(safe-area-inset-bottom));
117   - background: #F4F6FA;
  113 + min-height: 100vh;
  114 + // 底部固定栏(按钮 88rpx + 上下各 24rpx)+ 安全区
  115 + padding-bottom: calc(180rpx + constant(safe-area-inset-bottom));
  116 + padding-bottom: calc(180rpx + env(safe-area-inset-bottom));
  117 + background: #F4F6FA;
118 118 }
119 119 // 顶部提示条:浅蓝底 + 蓝字 + 圆形感叹号(设计稿 .banner)
120 120 .tip-bar {
121   - display: flex;
122   - align-items: flex-start;
123   - gap: 12rpx;
124   - margin: 24rpx 24rpx 0;
125   - padding: 16rpx 24rpx;
126   - background-color: #EBF3FF;
127   - border-radius: 16rpx;
128   - color: #2563EB;
129   - font-size: 24rpx;
130   - line-height: 1.6;
  121 + display: flex;
  122 + align-items: flex-start;
  123 + gap: 12rpx;
  124 + margin: 24rpx 24rpx 0;
  125 + padding: 16rpx 24rpx;
  126 + background-color: #EBF3FF;
  127 + border-radius: 16rpx;
  128 + color: #2563EB;
  129 + font-size: 24rpx;
  130 + line-height: 1.6;
131 131  
132   - &::before {
133   - content: "!";
134   - box-sizing: border-box;
135   - flex: 0 0 auto;
136   - width: 30rpx;
137   - height: 30rpx;
138   - margin-top: 4rpx;
139   - line-height: 26rpx;
140   - text-align: center;
141   - font-size: 22rpx;
142   - font-weight: 600;
143   - color: #2563EB;
144   - border: 2rpx solid #2563EB;
145   - border-radius: 50%;
146   - }
  132 + &::before {
  133 + content: "!";
  134 + box-sizing: border-box;
  135 + flex: 0 0 auto;
  136 + width: 30rpx;
  137 + height: 30rpx;
  138 + margin-top: 4rpx;
  139 + line-height: 26rpx;
  140 + text-align: center;
  141 + font-size: 22rpx;
  142 + font-weight: 600;
  143 + color: #2563EB;
  144 + border: 2rpx solid #2563EB;
  145 + border-radius: 50%;
  146 + }
147 147 }
148 148 // 记录卡片列表:卡片之间 24rpx(设计稿 .body)
149 149 .list-wrap {
150   - display: flex;
151   - flex-direction: column;
152   - gap: 24rpx;
153   - padding: 24rpx;
  150 + display: flex;
  151 + flex-direction: column;
  152 + gap: 24rpx;
  153 + padding: 24rpx;
154 154 }
155 155 // 卡片:白底 / 8px 圆角 / 卡片阴影(设计稿 .card)
156 156 .report-item {
157   - padding: 24rpx;
158   - background-color: #ffffff;
159   - border-radius: 16rpx;
160   - box-shadow: 0 4rpx 16rpx rgba(17, 24, 39, 0.06);
  157 + padding: 24rpx;
  158 + background-color: #ffffff;
  159 + border-radius: 16rpx;
  160 + box-shadow: 0 4rpx 16rpx rgba(17, 24, 39, 0.06);
161 161  
162   - .item-title {
163   - font-size: 32rpx;
164   - color: #1F2937;
165   - font-weight: 500;
166   - line-height: 1.4;
167   - margin-bottom: 16rpx;
168   - }
169   - .item-row {
170   - font-size: 26rpx;
171   - color: #4B5563;
172   - margin-bottom: 12rpx;
173   - }
174   - .item-row-bottom {
175   - display: flex;
176   - justify-content: space-between;
177   - align-items: center;
178   - margin-bottom: 0;
179   - }
180   - // 查看:次要操作,白底描边(设计稿 .btn 次级按钮)
181   - .look-btn {
182   - box-sizing: border-box;
183   - display: flex;
184   - align-items: center;
185   - justify-content: center;
186   - min-width: 120rpx;
187   - height: 56rpx;
188   - padding: 0 24rpx;
189   - font-size: 24rpx;
190   - color: #4B5563;
191   - background-color: #ffffff;
192   - border: 1rpx solid #E8EBF0;
193   - border-radius: 8rpx;
194   - flex-shrink: 0;
195   - }
  162 + .item-title {
  163 + font-size: 32rpx;
  164 + color: #1F2937;
  165 + font-weight: 500;
  166 + line-height: 1.4;
  167 + margin-bottom: 16rpx;
  168 + }
  169 + .item-row {
  170 + font-size: 26rpx;
  171 + color: #4B5563;
  172 + margin-bottom: 12rpx;
  173 + }
  174 + .item-row-bottom {
  175 + display: flex;
  176 + justify-content: space-between;
  177 + align-items: center;
  178 + margin-bottom: 0;
  179 + }
  180 + // 查看:次要操作,白底描边(设计稿 .btn 次级按钮)
  181 + .look-btn {
  182 + box-sizing: border-box;
  183 + display: flex;
  184 + align-items: center;
  185 + justify-content: center;
  186 + min-width: 120rpx;
  187 + height: 56rpx;
  188 + padding: 0 24rpx;
  189 + font-size: 24rpx;
  190 + color: #4B5563;
  191 + background-color: #ffffff;
  192 + border: 1rpx solid #E8EBF0;
  193 + border-radius: 8rpx;
  194 + flex-shrink: 0;
  195 + }
196 196 }
197 197 .empty-wrap {
198   - padding-top: 120rpx;
  198 + padding-top: 120rpx;
199 199 }
200 200 // 底部固定操作栏:白底 + 上分隔线 + 蓝色渐变主按钮(设计稿 .bar / .btn.pri)
201 201 .fixed-bottom-btn-wrap {
202   - position: fixed;
203   - left: 0;
204   - right: 0;
205   - bottom: 0;
206   - z-index: 10;
207   - padding: 24rpx 24rpx calc(24rpx + constant(safe-area-inset-bottom));
208   - padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
209   - background-color: #ffffff;
210   - border-top: 1rpx solid #E8EBF0;
  202 + position: fixed;
  203 + left: 0;
  204 + right: 0;
  205 + bottom: 0;
  206 + z-index: 10;
  207 + padding: 24rpx 24rpx calc(24rpx + constant(safe-area-inset-bottom));
  208 + padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
  209 + background-color: #ffffff;
  210 + border-top: 1rpx solid #E8EBF0;
211 211  
212   - :deep(.u-button) {
213   - height: 88rpx !important;
214   - border-radius: 16rpx !important;
215   - border: none !important;
216   - /* 与顶部导航栏同色 */
217   - background-color: #2563EB !important;
218   - background-image: none !important;
219   - }
  212 + :deep(.u-button) {
  213 + height: 88rpx !important;
  214 + border-radius: 16rpx !important;
  215 + border: none !important;
  216 + /* 与顶部导航栏同色 */
  217 + background-color: #2563EB !important;
  218 + background-image: none !important;
  219 + }
220 220  
221   - :deep(.u-button__text) {
222   - font-size: 30rpx !important;
223   - font-weight: 500 !important;
224   - }
  221 + :deep(.u-button__text) {
  222 + font-size: 30rpx !important;
  223 + font-weight: 500 !important;
  224 + }
225 225 }
226 226 </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 v-model="activeStatus" :list="statusTabs" line-width="44rpx" active-color="#2563EB"
6   - inactive-color="#4B5563" :scrollable="false" @change="handleStatusChange"></up-tabs>
  2 + <view class="page-container">
  3 + <up-sticky>
  4 + <view class="sticky-wrap">
  5 + <up-tabs v-model="activeStatus" :list="statusTabs" line-width="44rpx" active-color="#2563EB"
  6 + inactive-color="#4B5563" :scrollable="false" @change="handleStatusChange"></up-tabs>
  7 + </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="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>
  25 + </view>
  26 + <!-- 有效期 -->
  27 + <view class="info-row">
  28 + <!-- timeFormat(start, 'yyyy-mm-dd'), -->
  29 + <view class="info-label">有效期</view>
  30 + <view class="info-value u-line-1">{{ timeFormat(item.beginTime, 'yyyy-mm-dd') }}—{{
  31 + timeFormat(item.endTime, 'yyyy-mm-dd') }}
7 32 </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="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>
25   - </view>
26   - <!-- 有效期 -->
27   - <view class="info-row">
28   - <!-- timeFormat(start, 'yyyy-mm-dd'), -->
29   - <view class="info-label">有效期</view>
30   - <view class="info-value u-line-1">{{ timeFormat(item.beginTime, 'yyyy-mm-dd') }}—{{
31   - timeFormat(item.endTime, 'yyyy-mm-dd') }}
32   - </view>
33   - </view>
34   - <!-- 操作区:统一收底,与信息区用分隔线隔开 -->
35   - <view class="action-row">
36   - <!-- 待完成且一次都没上报过才允许置为失效;已有养护记录(进行中/已完成过)不再显示 -->
37   - <view v-if="activeStatus === '1' && !hasReport(item)" class="mini-btn btn-brand-soft"
38   - @click.stop="handleSetInvalid(item)">
39   - 置为失效
40   - </view>
41   - <view class="action-right">
42   - <!-- ✅ 修改:去掉item.commitId判断,待完成tab就一直展示结束任务 -->
43   - <view v-if="activeStatus === '1'" class="mini-btn btn-brand-outline"
44   - @click.stop="handleEndTask(item)">结束任务</view>
45   - <!-- 待完成:去作业;已完成:去查看 -->
46   - <view v-if="activeStatus === '1'" class="mini-btn btn-brand-solid" @click.stop="handleGoWork(item)">去作业</view>
47   - <view v-else-if="activeStatus === '2'" class="mini-btn mini-btn--info" @click.stop="handleGoWorkLook(item)">去查看</view>
48   - <view v-else-if="activeStatus === '3'" class="mini-btn mini-btn--info" @click.stop="handleGoWorkLook(item)">去查看</view>
49   - </view>
50   - </view>
51   - </view>
  33 + </view>
  34 + <!-- 操作区:统一收底,与信息区用分隔线隔开 -->
  35 + <view class="action-row">
  36 + <!-- 待完成且一次都没上报过才允许置为失效;已有养护记录(进行中/已完成过)不再显示 -->
  37 + <view v-if="activeStatus === '1' && !hasReport(item)" class="mini-btn btn-brand-soft"
  38 + @click.stop="handleSetInvalid(item)">
  39 + 置为失效
52 40 </view>
53   - </z-paging>
54   - <!-- ========== 置为失效 弹窗(必须填写失效原因) ========== -->
55   - <up-modal :show="invalidModalShow" title="置为失效" :closeOnClickOverlay="false" :showConfirmButton="true"
56   - :showCancelButton="true" @cancel="handleInvalidModalCancel" @confirm="confirmSetInvalid">
57   - <view class="invalid-modal-content">
58   - <view class="invalid-modal-label">
59   - <text class="invalid-modal-required">*</text>
60   - <text>失效原因</text>
61   - </view>
62   - <up-textarea v-model="invalidReason" placeholder="请输入失效原因" :maxlength="200" count
63   - height="100" border="surround"></up-textarea>
64   - <view class="invalid-modal-label">
65   - <text>失效凭证(选填,最多3张)</text>
66   - </view>
67   - <up-upload :file-list="invalidImages" @after-read="uploadInvalidImgs"
68   - @delete="deleteInvalidImg" @on-exceed="handleInvalidExceed" multiple :max-count="3"
69   - upload-text="选择图片" del-color="#ff4d4f" class="upload-wrap"></up-upload>
  41 + <view class="action-right">
  42 + <!-- ✅ 修改:去掉item.commitId判断,待完成tab就一直展示结束任务 -->
  43 + <view v-if="activeStatus === '1'" class="mini-btn btn-brand-outline"
  44 + @click.stop="handleEndTask(item)">结束任务</view>
  45 + <!-- 待完成:去作业;已完成:去查看 -->
  46 + <view v-if="activeStatus === '1'" class="mini-btn btn-brand-solid" @click.stop="handleGoWork(item)">去作业</view>
  47 + <view v-else-if="activeStatus === '2'" class="mini-btn mini-btn--info" @click.stop="handleGoWorkLook(item)">去查看</view>
  48 + <view v-else-if="activeStatus === '3'" class="mini-btn mini-btn--info" @click.stop="handleGoWorkLook(item)">去查看</view>
70 49 </view>
71   - </up-modal>
72   - <!-- ========== 结束任务二次确认弹窗 ========== -->
73   - <up-modal :show="endTaskModalShow" title="提示" :closeOnClickOverlay="false" :showConfirmButton="true"
74   - :showCancelButton="true" @cancel="handleEndTaskModalCancel" @confirm="confirmEndTask">
75   - <view class="end-task-modal-content">
76   - <text>确定要结束吗?</text>
77   - </view>
78   - </up-modal>
79   - </view>
  50 + </view>
  51 + </view>
  52 + </view>
  53 + </z-paging>
  54 + <!-- ========== 置为失效 弹窗(必须填写失效原因) ========== -->
  55 + <up-modal :show="invalidModalShow" title="置为失效" :closeOnClickOverlay="false" :showConfirmButton="true"
  56 + :showCancelButton="true" @cancel="handleInvalidModalCancel" @confirm="confirmSetInvalid">
  57 + <view class="invalid-modal-content">
  58 + <view class="invalid-modal-label">
  59 + <text class="invalid-modal-required">*</text>
  60 + <text>失效原因</text>
  61 + </view>
  62 + <up-textarea v-model="invalidReason" placeholder="请输入失效原因" :maxlength="200" count
  63 + height="100" border="surround"></up-textarea>
  64 + <view class="invalid-modal-label">
  65 + <text>失效凭证(选填,最多3张)</text>
  66 + </view>
  67 + <up-upload :file-list="invalidImages" @after-read="uploadInvalidImgs"
  68 + @delete="deleteInvalidImg" @on-exceed="handleInvalidExceed" multiple :max-count="3"
  69 + upload-text="选择图片" del-color="#ff4d4f" class="upload-wrap"></up-upload>
  70 + </view>
  71 + </up-modal>
  72 + <!-- ========== 结束任务二次确认弹窗 ========== -->
  73 + <up-modal :show="endTaskModalShow" title="提示" :closeOnClickOverlay="false" :showConfirmButton="true"
  74 + :showCancelButton="true" @cancel="handleEndTaskModalCancel" @confirm="confirmEndTask">
  75 + <view class="end-task-modal-content">
  76 + <text>确定要结束吗?</text>
  77 + </view>
  78 + </up-modal>
  79 + </view>
80 80 </template>
81 81 <script setup lang="ts">
82 82 import { ref } from 'vue'
... ... @@ -96,9 +96,9 @@ const getTodayStr = () =&gt; {
96 96 // 有养护记录的任务不允许置为失效
97 97 const hasReport = (item: any) => !!item?.commitId || Number(item?.planFinishNum || 0) > 0
98 98 const statusTabs = ref([
99   - { name: '待完成', id: '1' },
100   - { name: '已失效', id: '3' },
101   - { name: '已完成', id: '2' }
  99 + { name: '待完成', id: '1' },
  100 + { name: '已失效', id: '3' },
  101 + { name: '已完成', id: '2' }
102 102 ])
103 103 const activeStatus = ref('1')
104 104 const pagingRef = ref(null)
... ... @@ -119,339 +119,339 @@ const endTaskModalShow = ref(false)
119 119 const currentEndTaskItem = ref<any>(null)
120 120 // 加载数据,根据当前activeStatus动态调用接口
121 121 const loadData = async () => {
122   - console.log('123123')
123   - if (!originRawList || originRawList.length === 0) {
124   - // 没有批次时要显式结束分页,否则 z-paging 收不到 complete,页面一直停在加载中
125   - allTotalData.value = []
126   - pagingRef.value?.complete([], 0)
127   - return
  122 + console.log('123123')
  123 + if (!originRawList || originRawList.length === 0) {
  124 + // 没有批次时要显式结束分页,否则 z-paging 收不到 complete,页面一直停在加载中
  125 + allTotalData.value = []
  126 + pagingRef.value?.complete([], 0)
  127 + return
  128 + }
  129 + uni.showLoading({ title: '加载数据...', mask: true })
  130 + try {
  131 + const tempResult: any[] = []
  132 + // 获取需要请求的finishState数组
  133 + let stateArr: string[] = []
  134 + if (activeStatus.value === '1') {
  135 + // 待完成,只查1
  136 + stateArr = ['1']
  137 + } else if (activeStatus.value === '3') {
  138 + // 已失效,同时查3、4
  139 + stateArr = ['3', '4']
  140 + } else if (activeStatus.value === '2') {
  141 + // 已完成,只查2
  142 + stateArr = ['2']
128 143 }
129   - uni.showLoading({ title: '加载数据...', mask: true })
130   - try {
131   - const tempResult: any[] = []
132   - // 获取需要请求的finishState数组
133   - let stateArr: string[] = []
  144 + // 遍历state数组
  145 + for (const finishState of stateArr) {
  146 + // originRawList 为batchNo字符串数组,循环每一个batchNo去请求
  147 + for (const batchNoItem of originRawList) {
  148 + const apiParams: any = {
  149 + pageNo: 1,
  150 + pageSize: 10,
  151 + planNo: '',
  152 + planAttr: '',
  153 + finishState: finishState,
  154 + batchNo: batchNoItem
  155 + }
  156 + // 待完成状态追加当天日期 beginTime endTime
134 157 if (activeStatus.value === '1') {
135   - // 待完成,只查1
136   - stateArr = ['1']
137   - } else if (activeStatus.value === '3') {
138   - // 已失效,同时查3、4
139   - stateArr = ['3', '4']
140   - } else if (activeStatus.value === '2') {
141   - // 已完成,只查2
142   - stateArr = ['2']
  158 + const today = getTodayStr()
  159 + apiParams.beginTime = today
  160 + apiParams.endTime = today
143 161 }
144   - // 遍历state数组
145   - for (const finishState of stateArr) {
146   - // originRawList 为batchNo字符串数组,循环每一个batchNo去请求
147   - for (const batchNoItem of originRawList) {
148   - const apiParams: any = {
149   - pageNo: 1,
150   - pageSize: 10,
151   - planNo: '',
152   - planAttr: '',
153   - finishState: finishState,
154   - batchNo: batchNoItem
155   - }
156   - // 待完成状态追加当天日期 beginTime endTime
157   - if (activeStatus.value === '1') {
158   - const today = getTodayStr()
159   - apiParams.beginTime = today
160   - apiParams.endTime = today
161   - }
162   - const res = await maintainPlanNum(apiParams)
163   - if (Array.isArray(res.list)) {
164   - tempResult.push(...res.list)
165   - }
166   - }
  162 + const res = await maintainPlanNum(apiParams)
  163 + if (Array.isArray(res.list)) {
  164 + tempResult.push(...res.list)
167 165 }
168   - allTotalData.value = tempResult
169   - } catch (err) {
170   - console.error('批量请求接口异常', err)
171   - uni.showToast({ title: err.msg || '数据加载失败', icon: 'none', duration:2000 })
172   - } finally {
173   - uni.hideLoading()
174   - pagingRef.value?.reload()
  166 + }
175 167 }
  168 + allTotalData.value = tempResult
  169 + } catch (err) {
  170 + console.error('批量请求接口异常', err)
  171 + uni.showToast({ title: err.msg || '数据加载失败', icon: 'none', duration:2000 })
  172 + } finally {
  173 + uni.hideLoading()
  174 + pagingRef.value?.reload()
  175 + }
176 176 }
177 177 const planTypeId = ref('')
178 178 onLoad((options) => {
179   - planTypeId.value = options.planTypeId
  179 + planTypeId.value = options.planTypeId
180 180 });
181 181 // onShow 只保留一处,读取storage,originRawList接收batchNo字符串数组
182 182 onShow(async () => {
183   - originRawList = uni.getStorageSync('temp_maintain_rawList') || []
184   - await loadData()
  183 + originRawList = uni.getStorageSync('temp_maintain_rawList') || []
  184 + await loadData()
185 185 })
186 186 // z-paging分页查询,直接使用allTotalData
187 187 const fetchData = async (pageNo, pageSize) => {
188   - await new Promise(resolve => setTimeout(resolve, 300))
189   - const filterList = allTotalData.value
190   - const start = (pageNo - 1) * pageSize
191   - const pageData = filterList.slice(start, start + pageSize)
192   - pagingRef.value?.complete(pageData, filterList.length)
  188 + await new Promise(resolve => setTimeout(resolve, 300))
  189 + const filterList = allTotalData.value
  190 + const start = (pageNo - 1) * pageSize
  191 + const pageData = filterList.slice(start, start + pageSize)
  192 + pagingRef.value?.complete(pageData, filterList.length)
193 193 }
194 194 const handleStatusChange = async (tabItem) => {
195   - activeStatus.value = tabItem.id
196   - // 切换tab,重新拉接口
197   - await loadData()
  195 + activeStatus.value = tabItem.id
  196 + // 切换tab,重新拉接口
  197 + await loadData()
198 198 }
199 199 // 打开失效弹窗
200 200 const handleSetInvalid = (item) => {
201   - currentInvalidItem.value = item
202   - invalidReason.value = ''
203   - invalidImages.value = []
204   - invalidModalShow.value = true
  201 + currentInvalidItem.value = item
  202 + invalidReason.value = ''
  203 + invalidImages.value = []
  204 + invalidModalShow.value = true
205 205 }
206 206 // 关闭弹窗,重置数据
207 207 const handleInvalidModalCancel = () => {
208   - invalidModalShow.value = false
209   - currentInvalidItem.value = null
210   - invalidReason.value = ''
211   - invalidImages.value = []
  208 + invalidModalShow.value = false
  209 + currentInvalidItem.value = null
  210 + invalidReason.value = ''
  211 + invalidImages.value = []
212 212 }
213 213 // 失效凭证上传(最多 3 张,与 add-record 的图片上传保持一致)
214 214 const uploadInvalidImgs = async (event: any) => {
215   - const fileList = Array.isArray(event.file) ? event.file : [event.file]
216   - const validFiles = fileList.filter((item: any) => item && item.url)
217   - if (!validFiles.length) return
218   - const startIndex = invalidImages.value.length
219   - invalidImages.value.push(...validFiles.map((item: any) => ({ ...item, status: 'uploading', message: '上传中' })))
220   - try {
221   - const urls = await uploadImages({ filePaths: validFiles.map((item: any) => item.url), ignoreError: true })
222   - urls.forEach((url: string, index: number) => {
223   - if (invalidImages.value[startIndex + index]) {
224   - invalidImages.value.splice(startIndex + index, 1, {
225   - ...validFiles[index], status: 'success', message: '', url
226   - })
227   - }
  215 + const fileList = Array.isArray(event.file) ? event.file : [event.file]
  216 + const validFiles = fileList.filter((item: any) => item && item.url)
  217 + if (!validFiles.length) return
  218 + const startIndex = invalidImages.value.length
  219 + invalidImages.value.push(...validFiles.map((item: any) => ({ ...item, status: 'uploading', message: '上传中' })))
  220 + try {
  221 + const urls = await uploadImages({ filePaths: validFiles.map((item: any) => item.url), ignoreError: true })
  222 + urls.forEach((url: string, index: number) => {
  223 + if (invalidImages.value[startIndex + index]) {
  224 + invalidImages.value.splice(startIndex + index, 1, {
  225 + ...validFiles[index], status: 'success', message: '', url
228 226 })
229   - for (let i = urls.length; i < validFiles.length; i++) {
230   - if (invalidImages.value[startIndex + i]) {
231   - invalidImages.value.splice(startIndex + i, 1, {
232   - ...validFiles[i], status: 'failed', message: '上传失败'
233   - })
234   - }
235   - }
236   - if (urls.length < validFiles.length) {
237   - uni.showToast({ title: `成功上传${urls.length}张,失败${validFiles.length - urls.length}张`, icon: 'none' })
238   - }
239   - } catch (err) {
240   - console.error('失效凭证上传失败:', err)
241   - for (let i = 0; i < validFiles.length; i++) {
242   - if (invalidImages.value[startIndex + i]) {
243   - invalidImages.value.splice(startIndex + i, 1, {
244   - ...validFiles[i], status: 'failed', message: '上传失败'
245   - })
246   - }
247   - }
248   - uni.showToast({ title: '图片上传失败,请重试', icon: 'none' })
  227 + }
  228 + })
  229 + for (let i = urls.length; i < validFiles.length; i++) {
  230 + if (invalidImages.value[startIndex + i]) {
  231 + invalidImages.value.splice(startIndex + i, 1, {
  232 + ...validFiles[i], status: 'failed', message: '上传失败'
  233 + })
  234 + }
249 235 }
  236 + if (urls.length < validFiles.length) {
  237 + uni.showToast({ title: `成功上传${urls.length}张,失败${validFiles.length - urls.length}张`, icon: 'none' })
  238 + }
  239 + } catch (err) {
  240 + console.error('失效凭证上传失败:', err)
  241 + for (let i = 0; i < validFiles.length; i++) {
  242 + if (invalidImages.value[startIndex + i]) {
  243 + invalidImages.value.splice(startIndex + i, 1, {
  244 + ...validFiles[i], status: 'failed', message: '上传失败'
  245 + })
  246 + }
  247 + }
  248 + uni.showToast({ title: '图片上传失败,请重试', icon: 'none' })
  249 + }
250 250 }
251 251 const deleteInvalidImg = (event: any) => {
252   - invalidImages.value.splice(event.index, 1)
  252 + invalidImages.value.splice(event.index, 1)
253 253 }
254 254 const handleInvalidExceed = () => {
255   - uni.showToast({ title: '最多只能上传3张图片', icon: 'none' })
  255 + uni.showToast({ title: '最多只能上传3张图片', icon: 'none' })
256 256 }
257 257 // 确认提交置为失效
258 258 const confirmSetInvalid = async () => {
259   - const item = currentInvalidItem.value
260   - console.log(currentInvalidItem.value)
261   - if (!item) {
262   - uni.showToast({ title: '数据异常', icon: 'none', duration:2000 })
263   - return
264   - }
265   - // 失效原因必填:为空时不关闭弹窗、不发起任何请求
266   - const reason = (invalidReason.value || '').trim()
267   - if (!reason) {
268   - uni.showToast({ title: '请填写失效原因', icon: 'none', duration:2000 })
269   - return
270   - }
271   - // 图片还在上传时不能提交,否则会把空 url 或本地路径传给后端
272   - if (invalidImages.value.some((file: any) => file.status === 'uploading')) {
273   - uni.showToast({ title: '图片上传中,请稍候', icon: 'none', duration:2000 })
  259 + const item = currentInvalidItem.value
  260 + console.log(currentInvalidItem.value)
  261 + if (!item) {
  262 + uni.showToast({ title: '数据异常', icon: 'none', duration:2000 })
  263 + return
  264 + }
  265 + // 失效原因必填:为空时不关闭弹窗、不发起任何请求
  266 + const reason = (invalidReason.value || '').trim()
  267 + if (!reason) {
  268 + uni.showToast({ title: '请填写失效原因', icon: 'none', duration:2000 })
  269 + return
  270 + }
  271 + // 图片还在上传时不能提交,否则会把空 url 或本地路径传给后端
  272 + if (invalidImages.value.some((file: any) => file.status === 'uploading')) {
  273 + uni.showToast({ title: '图片上传中,请稍候', icon: 'none', duration:2000 })
  274 + return
  275 + }
  276 + const invalidFiles = invalidImages.value
  277 + .filter((file: any) => file.status === 'success' && file.url)
  278 + .map((file: any) => file.url)
  279 + uni.showLoading({ title: '提交中...', mask: true })
  280 + try {
  281 + let targetCommitId = item.commitId
  282 + // ✅ 如果没有 commitId,先执行 maintainCreate 创建草稿
  283 + if (!targetCommitId) {
  284 + const submitData = {
  285 + planNo: item.planNo,
  286 + imgHost: '1',
  287 + beginImg: [],
  288 + commonUserList: [],
  289 + remark: ''
  290 + }
  291 + const createRes: any = await maintainCreate(submitData)
  292 + // 兼容后端两种返回:裸id / {id:xxx}
  293 + targetCommitId = createRes && typeof createRes === 'object' ? createRes.id : createRes
  294 + if (!targetCommitId) {
  295 + uni.hideLoading()
  296 + uni.showToast({ title: '创建养护草稿失败,无法置为失效', icon: 'none', duration:2000 })
274 297 return
  298 + }
275 299 }
276   - const invalidFiles = invalidImages.value
277   - .filter((file: any) => file.status === 'success' && file.url)
278   - .map((file: any) => file.url)
279   - uni.showLoading({ title: '提交中...', mask: true })
280   - try {
281   - let targetCommitId = item.commitId
282   - // ✅ 如果没有 commitId,先执行 maintainCreate 创建草稿
283   - if (!targetCommitId) {
284   - const submitData = {
285   - planNo: item.planNo,
286   - imgHost: '1',
287   - beginImg: [],
288   - commonUserList: [],
289   - remark: ''
290   - }
291   - const createRes: any = await maintainCreate(submitData)
292   - // 兼容后端两种返回:裸id / {id:xxx}
293   - targetCommitId = createRes && typeof createRes === 'object' ? createRes.id : createRes
294   - if (!targetCommitId) {
295   - uni.hideLoading()
296   - uni.showToast({ title: '创建养护草稿失败,无法置为失效', icon: 'none', duration:2000 })
297   - return
298   - }
299   - }
300   - const params = {
301   - commitId: targetCommitId,
302   - // 使用弹窗中填写的原因(必填),不再写死默认文案
303   - invalidReason: reason,
304   - // 后端 invalidFiles 是 List<String>,无图时传空数组
305   - invalidFiles
306   - }
307   - console.log('置为失效提交参数:', params)
308   - await invalidatePlan(params)
309   - uni.showToast({ title: '操作成功', icon: 'success', duration:2000 })
310   - handleInvalidModalCancel()
311   - // 重新加载数据刷新列表
312   - await loadData()
313   - pagingRef.value?.reload()
314   - } catch (err) {
315   - console.error(err)
316   - uni.showToast({ title: err?.msg || '提交失败,请重试', icon: 'none', duration:2000 })
317   - } finally {
318   - uni.hideLoading()
  300 + const params = {
  301 + commitId: targetCommitId,
  302 + // 使用弹窗中填写的原因(必填),不再写死默认文案
  303 + invalidReason: reason,
  304 + // 后端 invalidFiles 是 List<String>,无图时传空数组
  305 + invalidFiles
319 306 }
  307 + console.log('置为失效提交参数:', params)
  308 + await invalidatePlan(params)
  309 + uni.showToast({ title: '操作成功', icon: 'success', duration:2000 })
  310 + handleInvalidModalCancel()
  311 + // 重新加载数据刷新列表
  312 + await loadData()
  313 + pagingRef.value?.reload()
  314 + } catch (err) {
  315 + console.error(err)
  316 + uni.showToast({ title: err?.msg || '提交失败,请重试', icon: 'none', duration:2000 })
  317 + } finally {
  318 + uni.hideLoading()
  319 + }
320 320 }
321 321 const handleGoWork = (item) => {
322 322 const isView = activeStatus.value
323   - uni.navigateTo({
324   - url: `/pages-sub/daily/maintain-manage/now-list-page?planNo=${item.planNo}&planTypeId=${planTypeId.value}&businessType=${item.busiLine}&isView=${isView}`
325   - })
  323 + uni.navigateTo({
  324 + url: `/pages-sub/daily/maintain-manage/now-list-page?planNo=${item.planNo}&planTypeId=${planTypeId.value}&businessType=${item.busiLine}&isView=${isView}`
  325 + })
326 326 }
327 327 const handleGoWorkLook = (item) => {
328 328 const isView = activeStatus.value
329   - uni.navigateTo({
330   - url: `/pages-sub/daily/maintain-manage/finish-plan-detail?planNo=${item.planNo}&planTypeId=${planTypeId.value}&businessType=${item.busiLine}&isView=${isView}`
331   - })
  329 + uni.navigateTo({
  330 + url: `/pages-sub/daily/maintain-manage/finish-plan-detail?planNo=${item.planNo}&planTypeId=${planTypeId.value}&businessType=${item.busiLine}&isView=${isView}`
  331 + })
332 332 }
333 333 const handleEndTask = async (item) => {
334   - currentEndTaskItem.value = item
335   - endTaskModalShow.value = true
  334 + currentEndTaskItem.value = item
  335 + endTaskModalShow.value = true
336 336 }
337 337 // 关闭结束任务弹窗
338 338 const handleEndTaskModalCancel = () => {
339   - endTaskModalShow.value = false
340   - currentEndTaskItem.value = null
  339 + endTaskModalShow.value = false
  340 + currentEndTaskItem.value = null
341 341 }
342 342 // ✅改造confirmEndTask:没有commitId先maintainCreate,和置为失效逻辑保持一致
343 343 const confirmEndTask = async () => {
344   - const item = currentEndTaskItem.value
345   - if (!item) {
346   - uni.showToast({ title: '数据异常', icon: 'none', duration:2000 })
347   - return
348   - }
349   - endTaskModalShow.value = false
350   - uni.showLoading({ title: '提交中...', mask: true })
351   - try {
352   - const targetCommitId = item.commitId
353   - // 没有进行中的上报说明本次还没作业:后端结束任务要求至少一条正常作业记录,
354   - // 先建草稿再结束必定失败,还会留下一条永远「进行中」的脏草稿,这里直接拦下。
355   - if (!targetCommitId) {
356   - uni.hideLoading()
357   - uni.showToast({ title: '请先添加作业记录后再结束任务', icon: 'none', duration: 2000 })
358   - return
359   - }
360   - await finishPlan({ commitId: targetCommitId })
361   - uni.hideLoading()
362   - uni.showToast({ title: '结束任务成功', icon: 'success', duration:2000 })
363   - await loadData()
364   - pagingRef.value?.reload()
365   - } catch (err) {
366   - console.error('结束任务失败', err)
367   - uni.hideLoading()
368   - uni.showToast({ title: err?.msg || '结束任务失败,请重试', icon: 'none', duration:2000 })
369   - } finally {
370   - currentEndTaskItem.value = null
  344 + const item = currentEndTaskItem.value
  345 + if (!item) {
  346 + uni.showToast({ title: '数据异常', icon: 'none', duration:2000 })
  347 + return
  348 + }
  349 + endTaskModalShow.value = false
  350 + uni.showLoading({ title: '提交中...', mask: true })
  351 + try {
  352 + const targetCommitId = item.commitId
  353 + // 没有进行中的上报说明本次还没作业:后端结束任务要求至少一条正常作业记录,
  354 + // 先建草稿再结束必定失败,还会留下一条永远「进行中」的脏草稿,这里直接拦下。
  355 + if (!targetCommitId) {
  356 + uni.hideLoading()
  357 + uni.showToast({ title: '请先添加作业记录后再结束任务', icon: 'none', duration: 2000 })
  358 + return
371 359 }
  360 + await finishPlan({ commitId: targetCommitId })
  361 + uni.hideLoading()
  362 + uni.showToast({ title: '结束任务成功', icon: 'success', duration:2000 })
  363 + await loadData()
  364 + pagingRef.value?.reload()
  365 + } catch (err) {
  366 + console.error('结束任务失败', err)
  367 + uni.hideLoading()
  368 + uni.showToast({ title: err?.msg || '结束任务失败,请重试', icon: 'none', duration:2000 })
  369 + } finally {
  370 + currentEndTaskItem.value = null
  371 + }
372 372 }
373 373 // 页面卸载清空,防止内存残留
374 374 onUnload(() => {
375   - allTotalData.value = []
376   - originRawList = []
  375 + allTotalData.value = []
  376 + originRawList = []
377 377 })
378 378 </script>
379 379 <style scoped lang="scss">
380 380 .page-container {
381   - height: 100vh;
382   - background-color: #F4F6FA;
  381 + height: 100vh;
  382 + background-color: #F4F6FA;
383 383 }
384 384 .sticky-wrap {
385   - background: #fff;
386   - border-bottom: 1rpx solid #E8EBF0;
  385 + background: #fff;
  386 + border-bottom: 1rpx solid #E8EBF0;
387 387 }
388 388 /* 卡片列表:页面左右 24rpx、卡片之间 24rpx(设计稿 .body)
389 389 * 顶部留白 = 吸顶 Tabs 高度 + 12px 间距,z-paging 内容从容器顶部起算,
390 390 * 不留这段会被吸顶 Tabs 盖住卡片标题(与原页面 padding-top:100rpx 等效) */
391 391 .card-list {
392   - padding: 112rpx 24rpx 24rpx;
393   - display: flex;
394   - flex-direction: column;
395   - gap: 24rpx;
  392 + padding: 112rpx 24rpx 24rpx;
  393 + display: flex;
  394 + flex-direction: column;
  395 + gap: 24rpx;
396 396 }
397 397 /* 卡片:12px 内边距 / 8px 圆角 / 卡片阴影(设计稿 .tcard) */
398 398 .task-card {
399   - background: #fff;
400   - border-radius: 16rpx;
401   - padding: 24rpx;
402   - box-shadow: 0 4rpx 16rpx rgba(17, 24, 39, 0.06);
403   - display: flex;
404   - flex-direction: column;
  399 + background: #fff;
  400 + border-radius: 16rpx;
  401 + padding: 24rpx;
  402 + box-shadow: 0 4rpx 16rpx rgba(17, 24, 39, 0.06);
  403 + display: flex;
  404 + flex-direction: column;
405 405 }
406 406 /* 标题行:标题占满剩余宽度,状态标签靠右(设计稿 .tcard-head) */
407 407 .card-head {
408   - display: flex;
409   - align-items: center;
410   - gap: 16rpx;
411   - margin-bottom: 16rpx;
  408 + display: flex;
  409 + align-items: center;
  410 + gap: 16rpx;
  411 + margin-bottom: 16rpx;
412 412 }
413 413 .plan-name {
414   - flex: 1;
415   - min-width: 0;
416   - font-size: 32rpx;
417   - color: #1F2937;
418   - font-weight: 500;
419   - line-height: 1.4;
  414 + flex: 1;
  415 + min-width: 0;
  416 + font-size: 32rpx;
  417 + color: #1F2937;
  418 + font-weight: 500;
  419 + line-height: 1.4;
420 420 }
421 421 .info-row {
422   - display: flex;
423   - align-items: center;
424   - justify-content: space-between;
425   - gap: 16rpx;
426   - margin-bottom: 6rpx;
  422 + display: flex;
  423 + align-items: center;
  424 + justify-content: space-between;
  425 + gap: 16rpx;
  426 + margin-bottom: 6rpx;
427 427 }
428 428 .info-label {
429   - font-size: 26rpx;
430   - color: #9CA3AF;
431   - flex-shrink: 0;
  429 + font-size: 26rpx;
  430 + color: #9CA3AF;
  431 + flex-shrink: 0;
432 432 }
433 433 .info-value {
434   - flex: 1;
435   - min-width: 0;
436   - font-size: 26rpx;
437   - color: #4B5563;
438   - text-align: right;
  434 + flex: 1;
  435 + min-width: 0;
  436 + font-size: 26rpx;
  437 + color: #4B5563;
  438 + text-align: right;
439 439 }
440 440 /* 操作区统一收底:上 16rpx 留白 + 分隔线(设计稿 .tcard-foot) */
441 441 .action-row {
442   - display: flex;
443   - align-items: center;
444   - justify-content: space-between;
445   - gap: 16rpx;
446   - margin-top: 16rpx;
447   - padding-top: 16rpx;
448   - border-top: 1rpx solid #E8EBF0;
  442 + display: flex;
  443 + align-items: center;
  444 + justify-content: space-between;
  445 + gap: 16rpx;
  446 + margin-top: 16rpx;
  447 + padding-top: 16rpx;
  448 + border-top: 1rpx solid #E8EBF0;
449 449 }
450 450 .action-right {
451   - display: flex;
452   - align-items: center;
453   - gap: 16rpx;
454   - margin-left: auto;
  451 + display: flex;
  452 + align-items: center;
  453 + gap: 16rpx;
  454 + margin-left: auto;
455 455 }
456 456 // 置为失效(红字)/ 去作业(蓝)/ 去查看(灰)/ 结束任务(白底描边)
457 457 // 尺寸与配色统一由全局 .btn-text-danger、.mini-btn 系列控制
... ... @@ -459,42 +459,42 @@ onUnload(() =&gt; {
459 459 * 浅底(置为失效) / 描边(结束任务) / 实心(去作业)
460 460 * 需 !important 覆盖全局 .mini-btn 的白字与 .btn-text-danger 等既有配色 */
461 461 .btn-brand-soft {
462   - color: #3b9dff !important;
463   - background-color: #EAF4FF !important;
  462 + color: #3b9dff !important;
  463 + background-color: #EAF4FF !important;
464 464 }
465 465  
466 466 .btn-brand-outline {
467   - color: #3b9dff !important;
468   - background-color: #ffffff !important;
469   - border: 1rpx solid #3b9dff;
  467 + color: #3b9dff !important;
  468 + background-color: #ffffff !important;
  469 + border: 1rpx solid #3b9dff;
470 470 }
471 471  
472 472 .btn-brand-solid {
473   - color: #ffffff !important;
474   - background-color: #3b9dff !important;
  473 + color: #ffffff !important;
  474 + background-color: #3b9dff !important;
475 475 }
476 476 .invalid-modal-content {
477   - width: 100%;
478   - box-sizing: border-box;
479   - padding: 10rpx 0;
  477 + width: 100%;
  478 + box-sizing: border-box;
  479 + padding: 10rpx 0;
480 480 }
481 481  
482 482 /* 失效原因(必填):标签 + 输入域 */
483 483 .invalid-modal-label {
484   - display: flex;
485   - align-items: center;
486   - gap: 6rpx;
487   - margin-bottom: 16rpx;
488   - font-size: 28rpx;
489   - color: #1F2937;
  484 + display: flex;
  485 + align-items: center;
  486 + gap: 6rpx;
  487 + margin-bottom: 16rpx;
  488 + font-size: 28rpx;
  489 + color: #1F2937;
490 490 }
491 491  
492 492 .invalid-modal-required {
493   - color: #DC2626;
  493 + color: #DC2626;
494 494 }
495 495  
496 496 /* 失效凭证上传:与上方原因输入框保持同样的间距 */
497 497 .upload-wrap {
498   - margin-top: 16rpx;
  498 + margin-top: 16rpx;
499 499 }
500 500 </style>
... ...
pages-sub/problem/ai-manage/index.vue
... ... @@ -151,9 +151,9 @@
151 151 <view class="u-body-item-title">
152 152 紧急程度:{{ uni.$dict.getDictLabel('workorder_pressing_type', item.pressingType) }}
153 153 </view>
154   -<!-- <view>-->
155   -<!-- <up-button type="primary" size="mini" @click="handleDetail(item)">工单详情</up-button>-->
156   -<!-- </view>-->
  154 + <!-- <view>-->
  155 + <!-- <up-button type="primary" size="mini" @click="handleDetail(item)">工单详情</up-button>-->
  156 + <!-- </view>-->
157 157 </view>
158 158 <view class="u-body-item u-flex">
159 159 <view class="u-body-item-title">工单状态:</view>
... ...
pages-sub/problem/regional-order-manage/index.vue
... ... @@ -62,9 +62,9 @@
62 62 >
63 63 <template #body>
64 64 <view class="card-body">
65   -<!-- <view v-if="activeTab !== 0" class="ml-10">-->
66   -<!-- <up-button type="primary" size="mini" @click="handleDetail(item)">工单详情</up-button>-->
67   -<!-- </view>-->
  65 + <!-- <view v-if="activeTab !== 0" class="ml-10">-->
  66 + <!-- <up-button type="primary" size="mini" @click="handleDetail(item)">工单详情</up-button>-->
  67 + <!-- </view>-->
68 68  
69 69 <view class="common-custom-btn " v-if="activeTab !== 0" style="--btn-top: 130px;" @click="handleDetail(item)">
70 70 工单详情
... ... @@ -89,13 +89,13 @@
89 89  
90 90 <view class="u-body-item u-flex common-justify-between common-item-center">
91 91 <view class="u-body-item-title">紧急程度:{{ uni.$dict.getDictLabel('workorder_pressing_type', item.pressingType) || '-' }}</view>
92   -<!-- <view class="u-line-1 u-body-value flex-1 text-right">-->
93   -<!-- {{ uni.$dict.getDictLabel('workorder_pressing_type', item.pressingType) || '-' }}-->
94   -<!-- </view>-->
  92 + <!-- <view class="u-line-1 u-body-value flex-1 text-right">-->
  93 + <!-- {{ uni.$dict.getDictLabel('workorder_pressing_type', item.pressingType) || '-' }}-->
  94 + <!-- </view>-->
95 95 <!-- 只有 我发起的(1)、已办(2) 显示该行的详情按钮 -->
96   -<!-- <view v-if="activeTab !== 0" class="ml-10">-->
97   -<!-- <up-button type="primary" size="mini" @click="handleDetail(item)">工单详情</up-button>-->
98   -<!-- </view>-->
  96 + <!-- <view v-if="activeTab !== 0" class="ml-10">-->
  97 + <!-- <up-button type="primary" size="mini" @click="handleDetail(item)">工单详情</up-button>-->
  98 + <!-- </view>-->
99 99 </view>
100 100 <view class="u-body-item u-flex">
101 101 <view class="u-body-item-title">工单状态:</view>
... ...
pages-sub/problem/regional-order-manage/list-detail.vue
... ... @@ -2,24 +2,24 @@
2 2 <view class="page-container">
3 3 <!-- 页面加载 -->
4 4 <up-loading-page
5   - v-if="loading"
6   - :loading="true"
7   - title="加载中..."
8   - color="#2563EB"
  5 + v-if="loading"
  6 + :loading="true"
  7 + title="加载中..."
  8 + color="#2563EB"
9 9 ></up-loading-page>
10 10  
11 11 <view v-else class="main-content" :class="'pb-rows-' + actionBarRows">
12 12 <!-- 顶部标签栏 -->
13 13 <up-sticky :bgColor="'#fff'">
14 14 <up-tabs
15   - v-model="activeTopTab"
16   - :list="topTabList"
17   - :scrollable="false"
18   - sticky
19   - active-color="#2563EB"
20   - inactive-color="#606266"
21   - class="top-tabs"
22   - @click="activeTopTabClick"
  15 + v-model="activeTopTab"
  16 + :list="topTabList"
  17 + :scrollable="false"
  18 + sticky
  19 + active-color="#2563EB"
  20 + inactive-color="#606266"
  21 + class="top-tabs"
  22 + @click="activeTopTabClick"
23 23 ></up-tabs>
24 24 </up-sticky>
25 25  
... ... @@ -68,17 +68,17 @@
68 68 </up-cell>
69 69  
70 70 <up-cell
71   - title="紧急程度"
72   - :value="uni.$dict.getDictLabel('workorder_pressing_type', orderDetail.pressingType) || '--'"
73   - align="middle"
  71 + title="紧急程度"
  72 + :value="uni.$dict.getDictLabel('workorder_pressing_type', orderDetail.pressingType) || '--'"
  73 + align="middle"
74 74 ></up-cell>
75 75  
76 76 <up-cell title="提交人" :value="orderDetail.userName || '--'" align="middle"></up-cell>
77 77  
78 78 <up-cell
79   - title="提交时间"
80   - :value="timeFormat(orderDetail.createTime, 'yyyy-mm-dd hh:MM:ss') || '--'"
81   - align="middle"
  79 + title="提交时间"
  80 + :value="timeFormat(orderDetail.createTime, 'yyyy-mm-dd hh:MM:ss') || '--'"
  81 + align="middle"
82 82 ></up-cell>
83 83  
84 84 <up-cell v-if="orderDetail.handleResult">
... ... @@ -94,11 +94,11 @@
94 94 <template #value>
95 95 <view class="cell-content-wrap">
96 96 <up-album
97   - v-if="!!orderDetail.problemsImgs?.length"
98   - :urls="orderDetail.problemsImgs.slice(0, 3)"
99   - singleSize="70"
100   - multipleSize="70"
101   - :preview-full-image="true"
  97 + v-if="!!orderDetail.problemsImgs?.length"
  98 + :urls="orderDetail.problemsImgs.slice(0, 3)"
  99 + singleSize="70"
  100 + multipleSize="70"
  101 + :preview-full-image="true"
102 102 ></up-album>
103 103 <text v-else class="empty-text">暂无问题照片</text>
104 104 </view>
... ... @@ -106,19 +106,19 @@
106 106 </up-cell>
107 107  
108 108 <up-cell
109   - v-if="orderDetail.expectedFinishDate"
110   - title="希望完成时间"
111   - :value="timeFormat(orderDetail.expectedFinishDate, 'yyyy-mm-dd hh:MM:ss')"
112   - align="middle"
113   - :border="false"
  109 + v-if="orderDetail.expectedFinishDate"
  110 + title="希望完成时间"
  111 + :value="timeFormat(orderDetail.expectedFinishDate, 'yyyy-mm-dd hh:MM:ss')"
  112 + align="middle"
  113 + :border="false"
114 114 ></up-cell>
115 115  
116 116 <up-cell
117   - v-if="orderDetail.finishDate"
118   - title="工单完结时间"
119   - :value="timeFormat(orderDetail.finishDate, 'yyyy-mm-dd hh:MM:ss')"
120   - align="middle"
121   - :border="false"
  117 + v-if="orderDetail.finishDate"
  118 + title="工单完结时间"
  119 + :value="timeFormat(orderDetail.finishDate, 'yyyy-mm-dd hh:MM:ss')"
  120 + align="middle"
  121 + :border="false"
122 122 ></up-cell>
123 123 </up-cell-group>
124 124  
... ... @@ -137,22 +137,22 @@
137 137 </up-cell>
138 138 </up-cell-group>
139 139 <up-tabs
140   - @change="imgTabChange"
141   - v-model="activeImgTab"
142   - :list="imgTabList"
143   - :scrollable="false"
144   - active-color="#2563EB"
145   - inactive-color="#606266"
146   - class="img-tabs"
  140 + @change="imgTabChange"
  141 + v-model="activeImgTab"
  142 + :list="imgTabList"
  143 + :scrollable="false"
  144 + active-color="#2563EB"
  145 + inactive-color="#606266"
  146 + class="img-tabs"
147 147 ></up-tabs>
148 148 <view class="img-tab-content">
149 149 <up-album
150   - v-if="currentImgList.length"
151   - :urls="currentImgList.slice(0, 3)"
152   - :singleSize="70"
153   - :multipleSize="70"
154   - :preview-full-image="true"
155   - class="img-album custom-album"
  150 + v-if="currentImgList.length"
  151 + :urls="currentImgList.slice(0, 3)"
  152 + :singleSize="70"
  153 + :multipleSize="70"
  154 + :preview-full-image="true"
  155 + class="img-album custom-album"
156 156 ></up-album>
157 157 <text v-else class="empty-img-text">养护员未上传图片</text>
158 158 </view>
... ... @@ -162,18 +162,18 @@
162 162 <!-- 流程节点 Tab -->
163 163 <view v-show="activeTopTab === 1" class="process-content">
164 164 <up-steps
165   - v-if="processData.activityNodes && processData.activityNodes.length"
166   - :list="processData.activityNodes"
167   - :current="getCurrentStepIndex()"
168   - direction="column"
169   - active-color="#2563EB"
170   - inactive-color="#909399"
171   - class="vertical-steps"
  165 + v-if="processData.activityNodes && processData.activityNodes.length"
  166 + :list="processData.activityNodes"
  167 + :current="getCurrentStepIndex()"
  168 + direction="column"
  169 + active-color="#2563EB"
  170 + inactive-color="#909399"
  171 + class="vertical-steps"
172 172 >
173 173 <template>
174 174 <up-steps-item
175   - v-for="(item, index) in processData.activityNodes"
176   - :key="`${item.id}_${index}`"
  175 + v-for="(item, index) in processData.activityNodes"
  176 + :key="`${item.id}_${index}`"
177 177 >
178 178 <template #content>
179 179 <view class="step-content-wrap">
... ... @@ -199,12 +199,12 @@
199 199 </view>
200 200 <view class="step-album-wrap">
201 201 <up-album
202   - v-if="item.tasks && item.tasks[0]?.attattmentUrls && item.tasks[0].attattmentUrls.length"
203   - :urls="item.tasks[0].attattmentUrls.slice(0, 3)"
204   - :singleSize="70"
205   - :multipleSize="70"
206   - :preview-full-image="true"
207   - class="step-album"
  202 + v-if="item.tasks && item.tasks[0]?.attattmentUrls && item.tasks[0].attattmentUrls.length"
  203 + :urls="item.tasks[0].attattmentUrls.slice(0, 3)"
  204 + :singleSize="70"
  205 + :multipleSize="70"
  206 + :preview-full-image="true"
  207 + class="step-album"
208 208 ></up-album>
209 209 </view>
210 210 </view>
... ... @@ -213,10 +213,10 @@
213 213 </template>
214 214 </up-steps>
215 215 <up-empty
216   - v-else
217   - mode="data"
218   - title="暂无流程节点数据"
219   - class="empty-process"
  216 + v-else
  217 + mode="data"
  218 + title="暂无流程节点数据"
  219 + class="empty-process"
220 220 ></up-empty>
221 221 </view>
222 222 </view>
... ... @@ -251,35 +251,35 @@
251 251  
252 252 <!-- 回退弹窗 -->
253 253 <up-modal
254   - :show="rejectModalShow"
255   - title="回退原因"
256   - :closeOnClickOverlay="false"
257   - :showConfirmButton="true"
258   - :showCancelButton="true"
259   - @cancel="handleRejectModalCancel"
260   - @confirm="confirmReject"
  254 + :show="rejectModalShow"
  255 + title="回退原因"
  256 + :closeOnClickOverlay="false"
  257 + :showConfirmButton="true"
  258 + :showCancelButton="true"
  259 + @cancel="handleRejectModalCancel"
  260 + @confirm="confirmReject"
261 261 >
262 262 <view class="reject-modal-content">
263 263 <up-textarea
264   - v-model.trim="rejectReason"
265   - placeholder="请输入回退原因(必填)"
266   - rows="6"
267   - :count="200"
268   - maxlength="200"
269   - class="reject-textarea"
  264 + v-model.trim="rejectReason"
  265 + placeholder="请输入回退原因(必填)"
  266 + rows="6"
  267 + :count="200"
  268 + maxlength="200"
  269 + class="reject-textarea"
270 270 />
271 271 <view class="upload-wrap mt-20">
272 272 <view class="upload-title">上传回退图片(选填)</view>
273 273 <up-upload
274   - :file-list="rejectImgs.rawImgList.value || []"
275   - @after-read="rejectImgs.uploadImgs"
276   - @delete="rejectImgs.deleteImg"
277   - multiple
278   - width="70"
279   - height="70"
280   - :max-count="rejectImgs.uploadConfig.maxCount"
281   - :upload-text="rejectImgs.uploadConfig.uploadText"
282   - :size-type="rejectImgs.uploadConfig.sizeType"
  274 + :file-list="rejectImgs.rawImgList.value || []"
  275 + @after-read="rejectImgs.uploadImgs"
  276 + @delete="rejectImgs.deleteImg"
  277 + multiple
  278 + width="70"
  279 + height="70"
  280 + :max-count="rejectImgs.uploadConfig.maxCount"
  281 + :upload-text="rejectImgs.uploadConfig.uploadText"
  282 + :size-type="rejectImgs.uploadConfig.sizeType"
283 283 />
284 284 </view>
285 285 </view>
... ... @@ -287,13 +287,13 @@
287 287  
288 288 <!-- 验收弹窗 -->
289 289 <up-modal
290   - :show="acceptModalShow"
291   - title="验收"
292   - :closeOnClickOverlay="false"
293   - :showConfirmButton="true"
294   - :showCancelButton="true"
295   - @cancel="handleAcceptModalCancel"
296   - @confirm="handleAcceptModalConfirm"
  290 + :show="acceptModalShow"
  291 + title="验收"
  292 + :closeOnClickOverlay="false"
  293 + :showConfirmButton="true"
  294 + :showCancelButton="true"
  295 + @cancel="handleAcceptModalCancel"
  296 + @confirm="handleAcceptModalConfirm"
297 297 >
298 298 <view class="accept-modal-content">
299 299 <view class="radio-group-wrap">
... ... @@ -304,26 +304,26 @@
304 304 </view>
305 305 <view class="textarea-wrap mt-30">
306 306 <up-textarea
307   - v-model.trim="acceptReason"
308   - placeholder="请输入验收原因(必填,最多200字)"
309   - :required="true"
310   - maxlength="200"
311   - rows="5"
312   - count
  307 + v-model.trim="acceptReason"
  308 + placeholder="请输入验收原因(必填,最多200字)"
  309 + :required="true"
  310 + maxlength="200"
  311 + rows="5"
  312 + count
313 313 />
314 314 </view>
315 315 <view class="upload-wrap mt-20">
316 316 <view class="upload-title">上传验收图片(选填)</view>
317 317 <up-upload
318   - :file-list="acceptImgs.rawImgList.value || []"
319   - @after-read="acceptImgs.uploadImgs"
320   - @delete="acceptImgs.deleteImg"
321   - multiple
322   - width="70"
323   - height="70"
324   - :max-count="acceptImgs.uploadConfig.maxCount"
325   - :upload-text="acceptImgs.uploadConfig.uploadText"
326   - :size-type="acceptImgs.uploadConfig.sizeType"
  318 + :file-list="acceptImgs.rawImgList.value || []"
  319 + @after-read="acceptImgs.uploadImgs"
  320 + @delete="acceptImgs.deleteImg"
  321 + multiple
  322 + width="70"
  323 + height="70"
  324 + :max-count="acceptImgs.uploadConfig.maxCount"
  325 + :upload-text="acceptImgs.uploadConfig.uploadText"
  326 + :size-type="acceptImgs.uploadConfig.sizeType"
327 327 />
328 328 </view>
329 329 </view>
... ... @@ -382,8 +382,8 @@ const callApprovalApi = async (params: any, taskKey: string) =&gt; {
382 382  
383 383 const navToOrderPage = (path: string, query = {}) => {
384 384 const queryStr = Object.keys(query)
385   - .map(k => `${k}=${encodeURIComponent(query[k])}`)
386   - .join('&')
  385 + .map(k => `${k}=${encodeURIComponent(query[k])}`)
  386 + .join('&')
387 387 const url = queryStr ? `${path}?${queryStr}` : path
388 388 uni.navigateTo({ url })
389 389 }
... ... @@ -631,8 +631,8 @@ const activeTopTabClick = async (item: any) =&gt; {
631 631 if (res && res.activityNodes && res.activityNodes.length) {
632 632 // 过滤未开始(status = -1)的预测节点:退回后 BPM 会返回后续待处理节点,工单时间线只展示已发生/进行中的节点
633 633 const formatNodes = res.activityNodes
634   - .filter(node => node.status !== -1)
635   - .map(node => ({ ...node, title: node.name }))
  634 + .filter(node => node.status !== -1)
  635 + .map(node => ({ ...node, title: node.name }))
636 636 processData.value = { ...res, activityNodes: formatNodes }
637 637 } else {
638 638 processData.value = res
... ... @@ -671,8 +671,8 @@ const handleRenew = (item: any) =&gt; {
671 671 const tempKey = setOrderStorage(item, 'renew_order')
672 672 if (!tempKey) return
673 673 const url = USER_ROLES.includes('patrol_global')
674   - ? '/pages-sub/problem/regional-order-manage/add-patrol-order'
675   - : '/pages-sub/problem/regional-order-manage/add-order'
  674 + ? '/pages-sub/problem/regional-order-manage/add-patrol-order'
  675 + : '/pages-sub/problem/regional-order-manage/add-order'
676 676 navToOrderPage(url, { isRenew: 1, tempKey })
677 677 }
678 678  
... ... @@ -874,8 +874,8 @@ const handleAcceptModalConfirm = async () =&gt; {
874 874 workerDataId: item.id,
875 875 taskId: item.taskId,
876 876 operateType: acceptRadioValue.value === '0'
877   - ? nextStepMap[item.taskKey].operateTypePass
878   - : nextStepMap[item.taskKey].operateTypeNoPass,
  877 + ? nextStepMap[item.taskKey].operateTypePass
  878 + : nextStepMap[item.taskKey].operateTypeNoPass,
879 879 reason
880 880 }
881 881 }
... ... @@ -886,8 +886,8 @@ const handleAcceptModalConfirm = async () =&gt; {
886 886 taskId: item.taskId,
887 887 workerDataId: item.id,
888 888 operateType: acceptRadioValue.value === '0'
889   - ? nextStepMap[item.taskKey].operateTypePass
890   - : nextStepMap[item.taskKey].operateTypeNoPass,
  889 + ? nextStepMap[item.taskKey].operateTypePass
  890 + : nextStepMap[item.taskKey].operateTypeNoPass,
891 891 reason,
892 892 agree: acceptRadioValue.value
893 893 }
... ...
pages-sub/problem/work-order-manage/order-detail.vue
... ... @@ -95,7 +95,7 @@
95 95 </template>
96 96 </up-cell>
97 97  
98   - <up-cell title="重点巡检区域" :value="orderDetail.keyInspection==0?'否':'是'" align="middle"></up-cell>
  98 + <up-cell title="重点巡检区域" :value="orderDetail.keyInspection==0?'否':'是'" align="middle"></up-cell>
99 99  
100 100 <!-- 希望完成时间 -->
101 101 <up-cell
... ...