Commit 25904bec21db0fc8d40aab3ec9216d652868b88b

Authored by 刘淇
1 parent 0c577b68

养护重新改

pages-sub/daily/maintain-manage/index.vue
... ... @@ -4,7 +4,7 @@
4 4 <up-sticky>
5 5 <view class="sticky-wrap">
6 6 <!-- 第一行标签:养护类型 -->
7   - <up-tabs
  7 + <!-- <up-tabs
8 8 v-model="activeType"
9 9 :list="typeTabs"
10 10 line-width="40rpx"
... ... @@ -13,10 +13,10 @@
13 13 :scrollable="true"
14 14 class="first-tabs"
15 15 @click="handleTypeChange"
16   - ></up-tabs>
  16 + ></up-tabs> -->
17 17  
18 18 <!-- 第二行标签:状态筛选 -->
19   - <up-tabs
  19 + <!-- <up-tabs
20 20 v-model="activeStatus"
21 21 :list="statusTabs"
22 22 line-width="40rpx"
... ... @@ -25,7 +25,7 @@
25 25 :scrollable="true"
26 26 class="second-tabs"
27 27 @click="handleStatusChange"
28   - ></up-tabs>
  28 + ></up-tabs> -->
29 29  
30 30 <!-- 动态搜索框(道路名称/计划名称) -->
31 31 <up-search
... ... @@ -40,7 +40,7 @@
40 40 @custom="handleSearch"
41 41 :clearabled="false"
42 42 maxlength="50"
43   - style="margin: 20rpx 20rpx 0"
  43 + style="margin: 0rpx 20rpx 0"
44 44 ></up-search>
45 45 </view>
46 46 </up-sticky>
... ... @@ -58,7 +58,7 @@
58 58 </template>
59 59  
60 60 <!-- 道路类型卡片 -->
61   - <view v-if="activeType === '道路'" class="common-card-list" style="padding-top: 144px;">
  61 + <view v-if="activeType === '道路'" class="common-card-list" style="padding-top: 34px;">
62 62  
63 63 <up-card
64 64 :border="false"
... ... @@ -81,8 +81,8 @@
81 81 <!-- </up-button>-->
82 82 <!-- </view>-->
83 83  
84   - <view class="common-custom-btn " style="--btn-top: 68px;" @click="goToDetail(item)">
85   - 计划明细
  84 + <view class="common-custom-btn" style="--btn-top: 28px;" @click="goToDetail(item)">
  85 + 养护任务
86 86 </view>
87 87 <!-- <view class="u-body-item u-flex">-->
88 88 <!-- <view class="u-body-item-title">批次号:</view>-->
... ... @@ -93,11 +93,11 @@
93 93 <view class="u-line-1 u-body-value">{{ item.roadName || '-' }}</view>
94 94 </view>
95 95  
96   - <view class="u-body-item u-flex">
  96 + <!-- <view class="u-body-item u-flex">
97 97 <view class="u-body-item-title">所属街道:</view>
98 98 <view class="u-line-1 u-body-value">{{ item.streetName }}
99 99 </view>
100   - </view>
  100 + </view> -->
101 101  
102 102 <view class="u-body-item u-flex common-item-center common-justify-between">
103 103 <view class="u-body-item-title">养护级别: {{uni.$dict.getDictLabel('conserve_level', item.levelId) || '-'}}</view>
... ... @@ -308,8 +308,12 @@ const fetchData = async (pageNo, pageSize) =&gt; {
308 308  
309 309 // 跳转到计划明细页面
310 310 const goToDetail = (item) => {
  311 + // uni.navigateTo({
  312 + // url: `/pages-sub/daily/maintain-manage/road-detail-list?roadId=${item.roadId}&finish_state=${item.finishState || ''} &businessType=${item.busiLine}`
  313 + // })
  314 +
311 315 uni.navigateTo({
312   - url: `/pages-sub/daily/maintain-manage/road-detail-list?roadId=${item.roadId}&finish_state=${item.finishState || ''} &businessType=${item.busiLine}`
  316 + url: `/pages-sub/daily/maintain-manage/type-list?roadId=${item.roadId}&finish_state=${item.finishState || ''} &businessType=${item.busiLine}`
313 317 })
314 318 }
315 319  
... ... @@ -343,7 +347,6 @@ onShow(()=&gt;{
343 347 <style scoped lang="scss">
344 348 // 吸顶区域样式
345 349 .sticky-wrap {
346   - margin-top: -10rpx;
347 350 background-color: #fff;
348 351 padding-bottom: 10rpx;
349 352 border-bottom: 1px solid #eee;
... ...
pages-sub/daily/maintain-manage/now-list-page.vue 0 → 100644
  1 +<template>
  2 + <view class="page-wrap">
  3 + <!-- 顶部提示栏 -->
  4 + <view class="tip-bar">
  5 + 一个任务可提交多次上报记录,每一次作业需如实上报。
  6 + </view>
  7 +
  8 + <!-- 上报记录列表 -->
  9 + <view class="list-wrap" v-if="reportList.length > 0">
  10 + <view class="report-item" v-for="(item, index) in reportList" :key="index">
  11 + <view class="item-title">【第{{ item.no }}次上报】{{ item.taskName }}</view>
  12 +
  13 + <view class="item-row item-row-bottom">
  14 + <view class="item-time">提交时间:{{ item.submitTime }}</view>
  15 + <view class="look-btn" @click="handleView(item)">查看</view>
  16 + </view>
  17 + </view>
  18 + </view>
  19 +
  20 + <!-- 暂无数据 -->
  21 + <view class="empty-wrap" v-else>
  22 + <empty-view text="暂无数据"></empty-view>
  23 + </view>
  24 +
  25 + <!-- 底部固定按钮 -->
  26 + <view class="fixed-bottom-btn-wrap">
  27 + <up-button type="primary" size="large" block @click="handleAddReport">上报本次记录</up-button>
  28 + </view>
  29 + </view>
  30 +</template>
  31 +
  32 +<script setup lang="ts">
  33 +import { ref } from 'vue'
  34 +
  35 +// 上报记录数据
  36 +const reportList = ref([
  37 + {
  38 + no:3,
  39 + taskName:'德胜门东大街月季补植',
  40 + varityName:'果汁阳台月季',
  41 + submitTime:'2026-06-22 14:34:33'
  42 + },
  43 + {
  44 + no:2,
  45 + taskName:'德胜门东大街月季补植',
  46 + varityName:'蓝色风暴月季',
  47 + submitTime:'2026-06-21 10:34:33'
  48 + },
  49 + {
  50 + no:1,
  51 + taskName:'德胜门东大街月季补植',
  52 + varityName:'金丝雀月季',
  53 + submitTime:'2026-06-20 10:34:33'
  54 + }
  55 +])
  56 +
  57 +// 查看上报详情
  58 +const handleView = (item) => {
  59 + console.log('查看详情', item)
  60 + uni.navigateTo({
  61 + url: `/pages-sub/daily/maintain-manage/finish-plan-detail`
  62 + })
  63 +}
  64 +
  65 +// 新增上报记录
  66 +const handleAddReport = () => {
  67 + console.log('上报本次记录')
  68 + uni.navigateTo({
  69 + url: `/pages-sub/daily/maintain-manage/add-record`
  70 + })
  71 +}
  72 +</script>
  73 +
  74 +<style scoped lang="scss">
  75 +.page-wrap {
  76 + min-height: 100vh;
  77 + padding-bottom: 120rpx; // 给底部按钮预留空间
  78 + background: #ffffff;
  79 +}
  80 +// 顶部黄色提示条
  81 +.tip-bar {
  82 + background-color: #f9e0a2;
  83 + color: #6b4b00;
  84 + font-size: 28rpx;
  85 + padding:24rpx 20rpx;
  86 + line-height:1.5;
  87 +}
  88 +
  89 +.list-wrap {
  90 + padding: 0;
  91 +}
  92 +.report-item {
  93 + padding:30rpx 24rpx;
  94 + border-bottom:1rpx solid #eee;
  95 + .item-title {
  96 + font-size:32rpx;
  97 + color:#222;
  98 + font-weight:500;
  99 + margin-bottom:20rpx;
  100 + }
  101 + .item-row {
  102 + font-size:28rpx;
  103 + color:#555;
  104 + margin-bottom:12rpx;
  105 + }
  106 + .item-row-bottom {
  107 + display:flex;
  108 + justify-content:space-between;
  109 + align-items:center;
  110 + margin-bottom:0;
  111 + }
  112 + .look-btn {
  113 + font-size:28rpx;
  114 + color:#1989fa;
  115 + }
  116 +}
  117 +.empty-wrap {
  118 + padding-top: 120rpx;
  119 +}
  120 +
  121 +</style>
... ...
pages-sub/daily/maintain-manage/type-list-page.vue 0 → 100644
  1 +<template>
  2 + <view class="page-container">
  3 + <up-sticky>
  4 + <view class="sticky-wrap">
  5 + <up-tabs
  6 + v-model="activeStatus"
  7 + :list="statusTabs"
  8 + line-width="40rpx"
  9 + active-color="#1989fa"
  10 + inactive-color="#666"
  11 + :scrollable="false"
  12 + @change="handleStatusChange"
  13 + ></up-tabs>
  14 + </view>
  15 + </up-sticky>
  16 + <z-paging
  17 + ref="pagingRef"
  18 + v-model="currentList"
  19 + @query="fetchData"
  20 + :auto-show-system-loading="true"
  21 + >
  22 + <template #empty>
  23 + <empty-view/>
  24 + </template>
  25 + <view class="card-list">
  26 + <view
  27 + class="task-card"
  28 + v-for="(item,index) in currentList"
  29 + :key="`${item.planNo}_${index}`"
  30 + >
  31 + <!-- 第一行:任务名称 + 置为失效 -->
  32 + <view class="row row-1">
  33 + <view class="plan-name u-line-1">{{ item.planName || '无计划名称' }}</view>
  34 + <view
  35 + v-if="isMaintainLeader"
  36 + class="btn-set-invalid"
  37 + @click.stop="handleSetInvalid(item)"
  38 + >
  39 + 置为失效
  40 + </view>
  41 + </view>
  42 + <!-- 第二行:任务次数 + 去作业按钮 -->
  43 + <view class="row row-2">
  44 + <view class="info-text">任务次数:第{{ item.taskTimes || 1 }}次</view>
  45 + <view class="mini-btn" @click.stop="handleGoWork(item)">去作业</view>
  46 + </view>
  47 + <!-- 第三行:有效期 + 结束任务按钮 -->
  48 + <view class="row row-3">
  49 + <view class="info-text u-line-1">有效期:{{ item.beginTime || '-' }}—{{ item.endTime || '-' }}</view>
  50 + <view class="mini-btn" @click.stop="handleEndTask(item)">结束任务</view>
  51 + </view>
  52 + </view>
  53 + </view>
  54 + </z-paging>
  55 +
  56 + <!-- ========== 置为失效弹窗(参考回退弹窗结构) ========== -->
  57 + <up-modal
  58 + :show="invalidModalShow"
  59 + title="失效原因"
  60 + :closeOnClickOverlay="false"
  61 + :showConfirmButton="true"
  62 + :showCancelButton="true"
  63 + @cancel="handleInvalidModalCancel"
  64 + @confirm="confirmSetInvalid"
  65 + >
  66 + <view class="invalid-modal-content">
  67 + <up-textarea
  68 + v-model.trim="invalidReason"
  69 + placeholder="请输入失效原因(必填)"
  70 + rows="6"
  71 + :count="200"
  72 + maxlength="200"
  73 + class="invalid-textarea"
  74 + />
  75 + <view class="upload-wrap mt-20">
  76 + <view class="upload-title">上传图片(选填)</view>
  77 + <up-upload
  78 + :file-list="invalidImgs.rawImgList.value || []"
  79 + @after-read="invalidImgs.uploadImgs"
  80 + @delete="invalidImgs.deleteImg"
  81 + multiple
  82 + width="70"
  83 + height="70"
  84 + :max-count="invalidImgs.uploadConfig.maxCount"
  85 + :upload-text="invalidImgs.uploadConfig.uploadText"
  86 + :size-type="invalidImgs.uploadConfig.sizeType"
  87 + />
  88 + </view>
  89 + </view>
  90 + </up-modal>
  91 + </view>
  92 +</template>
  93 +
  94 +<script setup lang="ts">
  95 +import { ref, computed } from 'vue'
  96 +import { onShow } from '@dcloudio/uni-app'
  97 +import { useUserStore } from '@/pinia/user';
  98 +// 引入图片上传hook(和你回退弹窗同一个工具)
  99 +import { useUploadImgs } from '@/common/utils/useUploadImgs'
  100 +
  101 +const userStore = useUserStore();
  102 +const userRoles = computed<string[]>(() => userStore.userInfo?.roles || []);
  103 +const isMaintainLeader = computed(() => userRoles.value.includes('yl_worker'))
  104 +
  105 +const statusTabs = ref([
  106 + { name: '待完成', id: '1' },
  107 + { name: '已失效', id: '3' },
  108 + { name: '已完成', id: '2' }
  109 +])
  110 +const activeStatus = ref('1')
  111 +const pagingRef = ref(null)
  112 +const currentList = ref([])
  113 +
  114 +// ===================== 失效弹窗状态 =====================
  115 +const invalidModalShow = ref(false)
  116 +const invalidReason = ref('')
  117 +const currentInvalidItem = ref(null)
  118 +// 图片上传实例,和回退保持一致,最多3张
  119 +const invalidImgs = useUploadImgs({
  120 + maxCount: 3,
  121 + uploadText: '选择失效图片',
  122 + sizeType: ['compressed'],
  123 + formRef: null,
  124 + fieldName: 'invalidImgs'
  125 +})
  126 +
  127 +const mockAllData = [
  128 + { planNo: 'MH20260616001', planName: '德胜门东大街大街月季补植', taskTimes: 1, beginTime: '2026/06/16', endTime: '2026/12/31', finishState: '1' },
  129 + { planNo: 'MH20260616002', planName: '西长安街行道树修剪养护作业任务', taskTimes: 2, beginTime: '2026/05/01', endTime: '2026/11/30', finishState: '1' },
  130 + { planNo: 'MH20260616003', planName: '北三环绿地浇水、防寒养护专项任务超长名称测试文字溢出', taskTimes: 1, beginTime: '2026/04/10', endTime: '2026/12/20', finishState: '1' },
  131 + { planNo: 'MH20260616004', planName: '奥森园区绿地保洁日常养护', taskTimes: 3, beginTime: '2026/03/01', endTime: '2026/10/15', finishState: '3' },
  132 + { planNo: 'MH20260616005', planName: '护城河沿岸病虫害防治专项作业', taskTimes: 2, beginTime: '2026/02/15', endTime: '2026/09/30', finishState: '2' },
  133 + { planNo: 'MH20260616006', planName: '朝阳公园施肥、除草春季养护任务', taskTimes: 1, beginTime: '2026/04/05', endTime: '2026/11/10', finishState: '1' },
  134 + { planNo: 'MH20260616007', planName: '中关村大街灌木修剪与枯枝清理', taskTimes: 4, beginTime: '2026/01/20', endTime: '2026/12/25', finishState: '1' },
  135 + { planNo: 'MH20260616008', planName: '永定河滨河绿地冬季防寒浇水作业', taskTimes: 1, beginTime: '2026/07/01', endTime: '2026/12/31', finishState: '3' },
  136 + { planNo: 'MH20260616009', planName: '颐和园外围绿带病虫害防治', taskTimes: 2, beginTime: '2026/05/20', endTime: '2026/10/20', finishState: '2' },
  137 + { planNo: 'MH20260616010', planName: '上地街道行道树补植更换任务', taskTimes: 1, beginTime: '2026/06/01', endTime: '2026/12/20', finishState: '1' }
  138 +]
  139 +
  140 +const fetchData = async (pageNo, pageSize) => {
  141 + await new Promise(resolve => setTimeout(resolve, 400))
  142 + const filterList = mockAllData.filter(item => item.finishState === activeStatus.value)
  143 + const start = (pageNo - 1) * pageSize
  144 + const pageData = filterList.slice(start, start + pageSize)
  145 + pagingRef.value?.complete(pageData, filterList.length)
  146 +}
  147 +
  148 +const handleStatusChange = (item) => {
  149 + activeStatus.value = item.id
  150 + pagingRef.value?.reload()
  151 +}
  152 +
  153 +// 打开失效弹窗
  154 +const handleSetInvalid = (item) => {
  155 + currentInvalidItem.value = item
  156 + invalidReason.value = ''
  157 + invalidImgs.clearImgs()
  158 + invalidModalShow.value = true
  159 +}
  160 +
  161 +// 关闭弹窗,重置数据
  162 +const handleInvalidModalCancel = () => {
  163 + invalidModalShow.value = false
  164 + invalidReason.value = ''
  165 + invalidImgs.clearImgs()
  166 +}
  167 +
  168 +// 确认提交置为失效
  169 +const confirmSetInvalid = async () => {
  170 + const reason = invalidReason.value.trim()
  171 + if (!reason) {
  172 + uni.showToast({ title: '请填写失效原因', icon: 'none' })
  173 + return
  174 + }
  175 + const item = currentInvalidItem.value
  176 + if (!item) return
  177 +
  178 + uni.showLoading({ title: '提交中...', mask: true })
  179 + try {
  180 + // 这里后续替换真实接口,获取上传图片url: invalidImgs.getSuccessImgUrls()
  181 + const imgList = invalidImgs.getSuccessImgUrls()
  182 + console.log('置为失效提交参数:', {
  183 + planNo: item.planNo,
  184 + reason,
  185 + imgList
  186 + })
  187 +
  188 + // ============ 此处替换后端接口调用 ============
  189 + // await api.setTaskInvalid({ ... })
  190 +
  191 + uni.showToast({ title: '操作成功', icon: 'success' })
  192 + handleInvalidModalCancel()
  193 + pagingRef.value?.reload()
  194 + } catch (err) {
  195 + console.error(err)
  196 + uni.showToast({ title: '提交失败,请重试', icon: 'none' })
  197 + } finally {
  198 + uni.hideLoading()
  199 + }
  200 +}
  201 +
  202 +const handleGoWork = (item) => {
  203 +
  204 + uni.navigateTo({
  205 + url: `/pages-sub/daily/maintain-manage/now-list-page?roadId=${item.roadId}&finish_state=${item.finishState || ''} &businessType=${item.busiLine}`
  206 + })
  207 +}
  208 +const handleEndTask = (item) => console.log('结束任务', item)
  209 +
  210 +onShow(() => pagingRef.value?.reload())
  211 +</script>
  212 +
  213 +<style scoped lang="scss">
  214 +.page-container {
  215 + height: 100vh;
  216 + background-color: #f5f5f5;
  217 +}
  218 +.sticky-wrap {
  219 + background: #fff;
  220 + border-bottom: 1rpx solid #eee;
  221 +}
  222 +.card-list {
  223 + padding:100rpx 20rpx 20rpx;
  224 + display: flex;
  225 + flex-direction: column;
  226 + gap: 20rpx;
  227 +}
  228 +.task-card {
  229 + background: #fff;
  230 + border-radius: 12rpx;
  231 + padding: 24rpx;
  232 + display: flex;
  233 + flex-direction: column;
  234 + gap: 20rpx;
  235 +}
  236 +.row {
  237 + display: flex;
  238 + align-items: center;
  239 + justify-content: space-between;
  240 + gap: 16rpx;
  241 +}
  242 +.plan-name {
  243 + font-size: 30rpx;
  244 + color: #333;
  245 + font-weight: 500;
  246 + flex: 1;
  247 +}
  248 +.btn-set-invalid {
  249 + font-size: 26rpx;
  250 + color: #1677ff;
  251 + flex-shrink: 0;
  252 + padding-left: 16rpx;
  253 +}
  254 +.info-text {
  255 + font-size: 26rpx;
  256 + color: #666;
  257 + flex: 1;
  258 +}
  259 +.mini-btn {
  260 + font-size: 24rpx;
  261 + color: #333;
  262 + background: #f5f5f5;
  263 + border: 1rpx solid #ddd;
  264 + border-radius: 6rpx;
  265 + padding: 8rpx 20rpx;
  266 + flex-shrink: 0;
  267 +}
  268 +
  269 +// 弹窗样式,和回退弹窗保持一致
  270 +.invalid-modal-content {
  271 + width: 100%;
  272 + box-sizing: border-box;
  273 + padding: 10rpx 0;
  274 +}
  275 +.upload-wrap {
  276 + .upload-title {
  277 + font-size: 28rpx;
  278 + color: #333;
  279 + margin-bottom: 10rpx;
  280 + }
  281 +}
  282 +.mt-20 { margin-top: 20rpx; }
  283 +</style>
... ...
pages-sub/daily/maintain-manage/type-list.vue 0 → 100644
  1 +<template>
  2 + <view class="tag-wrap">
  3 + <!-- 动态渲染标签列表 -->
  4 + <view class="tag-item" v-for="(item, idx) in tagList" :key="idx" hover-class="tag-item--hover" @click="toNextPage">
  5 + <view class="tag-text">
  6 + <!-- 文字区域:超长省略号,数字括号单独放后面,永远可见 -->
  7 + <text class="tag-name">{{ item.label }}</text>
  8 + <text class="tag-count">({{ item.count }})</text>
  9 + </view>
  10 +
  11 + </view>
  12 + </view>
  13 +</template>
  14 +
  15 +<script setup lang="ts">
  16 +import { ref } from 'vue'
  17 +
  18 +// 数据源,完全动态,你可以接口返回覆盖
  19 +const tagList = ref([
  20 + { label: '补植', count: 5 },
  21 + { label: '修剪', count: 2 },
  22 + { label: '浇水、防寒', count: 3 },
  23 + { label: '绿地保洁', count: 3 },
  24 + { label: '病虫害防治', count: 3 },
  25 + { label: '施肥、除草', count: 4 },
  26 + // 测试超长文本
  27 + { label: '非常长的养护任务名称测试文字溢出效果', count: 9 },
  28 +])
  29 +
  30 +const toNextPage = (item)=> {
  31 + uni.navigateTo({
  32 + url: `/pages-sub/daily/maintain-manage/type-list-page?roadId=${item.roadId}&finish_state=${item.finishState || ''} &businessType=${item.busiLine}`
  33 + })
  34 +}
  35 +</script>
  36 +
  37 +<style scoped lang="scss">
  38 +.tag-wrap {
  39 + display: flex;
  40 + flex-wrap: wrap;
  41 + gap: 24rpx;
  42 + padding: 30rpx;
  43 +}
  44 +
  45 +.tag-item {
  46 + display: inline-flex;
  47 + align-items: center;
  48 + background: #e8f3ff;
  49 + border: 1rpx solid #94c5ff;
  50 + border-radius: 8rpx;
  51 + padding: 16rpx 24rpx;
  52 + max-width: 320rpx; // 限制标签最大宽度,超出自动省略
  53 + transition: all 0.2s;
  54 + &--hover {
  55 + background: #d8eaff;
  56 + }
  57 +}
  58 +
  59 +.tag-text {
  60 + display: flex;
  61 + align-items: center;
  62 + flex: 1;
  63 + overflow: hidden;
  64 +}
  65 +
  66 +.tag-name {
  67 + // 核心:文字超长省略,只对名称生效
  68 + white-space: nowrap;
  69 + overflow: hidden;
  70 + text-overflow: ellipsis;
  71 + color: #1677ff;
  72 + font-size: 28rpx;
  73 +}
  74 +
  75 +.tag-count {
  76 + color: #1677ff;
  77 + font-size: 28rpx;
  78 + flex-shrink: 0; // 重点!禁止压缩,保证(数字)永远显示
  79 + margin-left: 4rpx;
  80 +}
  81 +
  82 +.tag-icon {
  83 + flex-shrink: 0;
  84 + margin-left: 12rpx;
  85 +}
  86 +</style>
  87 +
... ...
pages-sub/daily/patrol-manage/add-patrol-record.vue
1 1 <template>
2 2 <view class="u-page">
3   -<!-- 原有模板内容不变,仅调整表单校验相关配置 -->
4   -<view class="inspect-form-content commonPageLRpadding">
5   -<up-form label-position="left" :model="inspectForm" ref="inspectFormRef" labelWidth="200rpx">
6   -<!-- 1. 巡查描述 -->
7   -<up-form-item label="巡查描述" prop="content" class="form-item" required>
8   -<up-textarea v-model.trim="inspectForm.content" placeholder="请输入巡查描述" maxlength="200" count></up-textarea>
9   -</up-form-item>
10   -<!-- 2. 上传图片 -->
11   -<up-form-item label="上传图片" prop="images" required border-bottom class="form-item">
12   -<up-upload :file-list="imagesList" @after-read="(event) => uploadImgs(event)"
  3 + <!-- 原有模板内容不变,仅调整表单校验相关配置 -->
  4 + <view class="inspect-form-content commonPageLRpadding">
  5 + <up-form label-position="left" :model="inspectForm" ref="inspectFormRef" labelWidth="200rpx">
  6 + <!-- 1. 巡查描述 -->
  7 + <up-form-item label="巡查描述" prop="content" class="form-item" required>
  8 + <up-textarea v-model.trim="inspectForm.content" placeholder="请输入巡查描述" maxlength="200" count></up-textarea>
  9 + </up-form-item>
  10 + <!-- 2. 上传图片 -->
  11 + <up-form-item label="上传图片" prop="images" required border-bottom class="form-item">
  12 + <up-upload :file-list="imagesList" @after-read="(event) => uploadImgs(event)"
13 13 @delete="(event) => deleteImg(event)" @on-exceed="handleExceed" multiple :max-count="3" upload-text="选择图片"
14   -del-color="#ff4d4f" class="upload-wrap" width="70" height="70"></up-upload>
15   -</up-form-item>
16   -<!-- 3. 转为工单 -->
17   -<up-form-item label="转为工单" prop="isWorkOrder" required class="form-item">
18   -<up-radio-group v-model="inspectForm.isWorkOrder" active-color="#1989fa" direction="row" class="radio-group"
  14 + del-color="#ff4d4f" class="upload-wrap" width="70" height="70"></up-upload>
  15 + </up-form-item>
  16 + <!-- 3. 转为工单 -->
  17 + <up-form-item label="转为工单" prop="isWorkOrder" required class="form-item">
  18 + <up-radio-group v-model="inspectForm.isWorkOrder" active-color="#1989fa" direction="row" class="radio-group"
19 19 @change="handleWorkOrderChange">
20   -<up-radio :custom-style="{ marginRight: '40rpx' }" v-for="(item, index) in radioList" :key="index"
  20 + <up-radio :custom-style="{ marginRight: '40rpx' }" v-for="(item, index) in radioList" :key="index"
21 21 :label="item.label" :name="item.value">
22 22 {{ item.label }}
23   -</up-radio>
24   -</up-radio-group>
25   -</up-form-item>
26   -<!-- ========== 【新增】业务线(单选框,对齐参考页面) ========== -->
27   -<up-form-item v-if="inspectForm.isWorkOrder === '2'" label="业务线" prop="busiLineCn" border-bottom required
28   -class="form-item">
29   -<up-radio-group v-model="inspectForm.busiLineCn" placement="row" @change="handleBusiLineChange">
30   -<up-radio v-for="item in busiLineOptions" :key="item.name" :label="item.name" :name="item.name"></up-radio>
31   -</up-radio-group>
32   -</up-form-item>
33   -<!-- ========== 【新增】问题类型(级联选择器,对齐参考页面) ========== -->
34   -<up-form-item v-if="inspectForm.isWorkOrder === '2'" label="问题类型" prop="orderName" border-bottom required
35   -class="form-item" @click="openCascader(); uni.hideKeyboard()">
36   -<up-input v-model="inspectForm.orderName" disabled disabled-color="#ffffff" placeholder="请选择问题类型"
37   -border="none"></up-input>
38   -<template #right>
39   -<up-icon name="arrow-right" size="16"></up-icon>
40   -</template>
41   -</up-form-item>
42   -<!-- 紧急程度(仍为必填) -->
43   -<up-form-item v-if="inspectForm.isWorkOrder === '2'" label="紧急程度" prop="pressingTypeName" border-bottom required
44   -class="form-item" @click="handleActionSheetOpen('pressingType')">
45   -<up-input v-model="inspectForm.pressingTypeName" disabled disabled-color="#ffffff" placeholder="请选择紧急程度"
46   -border="none"></up-input>
47   -<template #right>
48   -<up-icon name="arrow-right" size="16"></up-icon>
49   -</template>
50   -</up-form-item>
51   -<!-- 希望完成时间(移除 required 属性,改为非必填) -->
52   -<up-form-item v-if="inspectForm.isWorkOrder === '2'" label="希望完成时间" prop="expectedFinishDate" class="form-item"
  23 + </up-radio>
  24 + </up-radio-group>
  25 + </up-form-item>
  26 + <!-- ========== 【新增】业务线(单选框,对齐参考页面) ========== -->
  27 + <up-form-item v-if="inspectForm.isWorkOrder === '2'" label="业务线" prop="busiLineCn" border-bottom required
  28 + class="form-item">
  29 + <up-radio-group v-model="inspectForm.busiLineCn" placement="row" @change="handleBusiLineChange">
  30 + <up-radio v-for="item in busiLineOptions" :key="item.name" :label="item.name" :name="item.name"></up-radio>
  31 + </up-radio-group>
  32 + </up-form-item>
  33 + <!-- ========== 【新增】问题类型(级联选择器,对齐参考页面) ========== -->
  34 + <up-form-item v-if="inspectForm.isWorkOrder === '2'" label="问题类型" prop="orderName" border-bottom required
  35 + class="form-item" @click="openCascader(); uni.hideKeyboard()">
  36 + <up-input v-model="inspectForm.orderName" disabled disabled-color="#ffffff" placeholder="请选择问题类型"
  37 + border="none"></up-input>
  38 + <template #right>
  39 + <up-icon name="arrow-right" size="16"></up-icon>
  40 + </template>
  41 + </up-form-item>
  42 + <!-- 紧急程度(仍为必填) -->
  43 + <up-form-item v-if="inspectForm.isWorkOrder === '2'" label="紧急程度" prop="pressingTypeName" border-bottom required
  44 + class="form-item" @click="handleActionSheetOpen('pressingType')">
  45 + <up-input v-model="inspectForm.pressingTypeName" disabled disabled-color="#ffffff" placeholder="请选择紧急程度"
  46 + border="none"></up-input>
  47 + <template #right>
  48 + <up-icon name="arrow-right" size="16"></up-icon>
  49 + </template>
  50 + </up-form-item>
  51 + <!-- 希望完成时间(移除 required 属性,改为非必填) -->
  52 + <up-form-item v-if="inspectForm.isWorkOrder === '2'" label="希望完成时间" prop="expectedFinishDate" class="form-item"
53 53 @click="show = true; uni.hideKeyboard()">
54   -<up-input v-model="inspectForm.expectedFinishDate" border="none" readonly placeholder="点击选择时间">
55   -</up-input>
56   -<template #right>
57   -<!-- 核心修复:用view包裹图标,添加双重@click.stop -->
58   -<view v-if="inspectForm.expectedFinishDate" @click.stop>
59   -<up-icon name="close" size="16" @click.stop="clearExpectedFinishDate"></up-icon>
60   -</view>
61   -<up-icon name="arrow-right" size="16" v-else></up-icon>
62   -</template>
63   -</up-form-item>
64   -</up-form>
65   -</view>
66   -<!-- 底部提交按钮 -->
67   -<view class="fixed-bottom-btn-wrap">
68   -<up-button type="primary" text="提交" @click="submitInspect"
  54 + <up-input v-model="inspectForm.expectedFinishDate" border="none" readonly placeholder="点击选择时间">
  55 + </up-input>
  56 + <template #right>
  57 + <!-- 核心修复:用view包裹图标,添加双重@click.stop -->
  58 + <view v-if="inspectForm.expectedFinishDate" @click.stop>
  59 + <up-icon name="close" size="16" @click.stop="clearExpectedFinishDate"></up-icon>
  60 + </view>
  61 + <up-icon name="arrow-right" size="16" v-else></up-icon>
  62 + </template>
  63 + </up-form-item>
  64 + </up-form>
  65 + </view>
  66 + <!-- 底部提交按钮 -->
  67 + <view class="fixed-bottom-btn-wrap">
  68 + <up-button type="primary" text="提交" @click="submitInspect"
69 69 :style="{ width: '100%', height: '88rpx', fontSize: '32rpx', borderRadius: 0 }"></up-button>
70   -</view>
71   -<!-- 紧急程度下拉弹窗 -->
72   -<up-action-sheet :show="showActionSheet" :actions="pressingTypeList" title="请选择紧急程度" @close="handleActionSheetClose"
  70 + </view>
  71 + <!-- 紧急程度下拉弹窗 -->
  72 + <up-action-sheet :show="showActionSheet" :actions="pressingTypeList" title="请选择紧急程度" @close="handleActionSheetClose"
73 73 @select="handlePressingTypeSelect"></up-action-sheet>
74   -<!-- ========== 新增级联选择器组件 ========== -->
75   -<up-cascader v-model:show="showCascader" v-model="cascaderValue" :data="cascaderOptions" title="选择问题类型"
  74 + <!-- ========== 新增级联选择器组件 ========== -->
  75 + <up-cascader v-model:show="showCascader" v-model="cascaderValue" :data="cascaderOptions" title="选择问题类型"
76 76 :auto-close="false" @confirm="cascaderConfirm"></up-cascader>
77   -<!-- 完成时间选择器 -->
78   -<up-datetime-picker :show="show" v-model="datetimeValue" mode="datetime" :min-date="new Date()"
  77 + <!-- 完成时间选择器 -->
  78 + <up-datetime-picker :show="show" v-model="datetimeValue" mode="datetime" :min-date="new Date()"
79 79 @cancel="show = false" @confirm="expectedFinishDateConfirm"></up-datetime-picker>
80   -</view>
  80 + </view>
81 81 </template>
82 82 <script setup lang="ts">
83 83 import { ref, getCurrentInstance } from 'vue'
... ... @@ -98,68 +98,68 @@ const cascaderOptions = ref([]);
98 98  
99 99 // 打开级联弹窗
100 100 const openCascader = () => {
101   -const busiLineEn = getBusiLineEnByCn(proxy.$data.inspectForm.busiLineCn)
102   -if (!busiLineEn) {
103   -uni.showToast({ title: '请先选择业务线', icon: 'none' })
104   -return
  101 + const busiLineEn = getBusiLineEnByCn(proxy.$data.inspectForm.busiLineCn)
  102 + if (!busiLineEn) {
  103 + uni.showToast({ title: '请先选择业务线', icon: 'none' })
  104 + return
105 105 }
106   -if (!cascaderOptions.value || cascaderOptions.value.length === 0) {
107   -uni.showToast({ title: '正在加载问题类型,请稍等', icon: 'none' });
108   -loadProblemCascader(busiLineEn);
109   -return;
  106 + if (!cascaderOptions.value || cascaderOptions.value.length === 0) {
  107 + uni.showToast({ title: '正在加载问题类型,请稍等', icon: 'none' });
  108 + loadProblemCascader(busiLineEn);
  109 + return;
110 110 }
111   -cascaderValue.value = [];
112   -showCascader.value = true;
  111 + cascaderValue.value = [];
  112 + showCascader.value = true;
113 113 };
114 114  
115 115 // 级联确认回调
116 116 const cascaderConfirm = (selectedArr) => {
117   -const getLabelByValueList = (list, valList) => {
118   -const labelArr = [];
119   -let currentList = list;
120   -for (const val of valList) {
121   -const target = currentList.find(item => item.value === val);
122   -if (!target) break;
123   -labelArr.push(target.label);
124   -currentList = target.children || [];
  117 + const getLabelByValueList = (list, valList) => {
  118 + const labelArr = [];
  119 + let currentList = list;
  120 + for (const val of valList) {
  121 + const target = currentList.find(item => item.value === val);
  122 + if (!target) break;
  123 + labelArr.push(target.label);
  124 + currentList = target.children || [];
125 125 }
126   -return labelArr.join('/');
  126 + return labelArr.join('/');
127 127 };
128   -const fullLabel = getLabelByValueList(cascaderOptions.value, selectedArr);
129   -proxy.$data.inspectForm.orderName = fullLabel;
130   -proxy.$data.inspectForm.orderCode = selectedArr.at(-1);
131   -cascaderValue.value = selectedArr;
132   -inspectFormRef.value?.validateField('orderName');
  128 + const fullLabel = getLabelByValueList(cascaderOptions.value, selectedArr);
  129 + proxy.$data.inspectForm.orderName = fullLabel;
  130 + proxy.$data.inspectForm.orderCode = selectedArr.at(-1);
  131 + cascaderValue.value = selectedArr;
  132 + inspectFormRef.value?.validateField('orderName');
133 133 };
134 134  
135 135 // 加载级联树形数据
136 136 const loadProblemCascader = async (parentCode) => {
137   -if (!parentCode) {
138   -cascaderOptions.value = []
139   -return
  137 + if (!parentCode) {
  138 + cascaderOptions.value = []
  139 + return
140 140 }
141   -try {
142   -uni.showLoading({ title: '加载类型中...' })
143   -const res = await getProblemListByType({ parentCode })
144   -uni.hideLoading()
145   -cascaderOptions.value = res || []
  141 + try {
  142 + uni.showLoading({ title: '加载类型中...' })
  143 + const res = await getProblemListByType({ parentCode })
  144 + uni.hideLoading()
  145 + cascaderOptions.value = res || []
146 146 } catch (err) {
147   -uni.hideLoading()
148   -console.error('获取问题类型失败', err)
149   -uni.showToast({ title: '获取问题类型失败', icon: 'none' })
150   -cascaderOptions.value = []
  147 + uni.hideLoading()
  148 + console.error('获取问题类型失败', err)
  149 + uni.showToast({ title: '获取问题类型失败', icon: 'none' })
  150 + cascaderOptions.value = []
151 151 }
152 152 }
153 153  
154 154 const getBusiLineCnByEn = (busiLineEn) => {
155   -if (!busiLineEn) return '';
156   -return getDictLabel(BUSI_LINE_DICT_TYPE, busiLineEn) || busiLineEn;
  155 + if (!busiLineEn) return '';
  156 + return getDictLabel(BUSI_LINE_DICT_TYPE, busiLineEn) || busiLineEn;
157 157 };
158 158 const getBusiLineEnByCn = (cnName) => {
159   -if (!cnName) return '';
160   -const code = getDictValue(BUSI_LINE_DICT_TYPE, cnName);
161   -if (code) return code;
162   -return CJK_RE.test(cnName) ? '' : cnName;
  159 + if (!cnName) return '';
  160 + const code = getDictValue(BUSI_LINE_DICT_TYPE, cnName);
  161 + if (code) return code;
  162 + return CJK_RE.test(cnName) ? '' : cnName;
163 163 };
164 164  
165 165 // 暴露给选项式访问
... ... @@ -177,105 +177,105 @@ import { timeFormat } from &#39;@/uni_modules/uview-plus&#39;
177 177 import { useUserStore } from '@/pinia/user';
178 178 import { getDictLabel } from '@/common/utils/dict';
179 179 export default {
180   -data() {
181   -return {
182   -imagesList: [],
183   -radioList: [
  180 + data() {
  181 + return {
  182 + imagesList: [],
  183 + radioList: [
184 184 { label: '否', value: '1' },
185 185 { label: '是', value: '2' }
186 186 ],
187   -pressingTypeList: [],
188   -busiLineOptions: [], // 业务线移到data,不再放在setup
189   -inspectForm: {
190   -content: '',
191   -isWorkOrder: '1',
192   -pressingTypeName: '',
193   -pressingType: '',
194   -busiLineCn: '',
195   -orderName: '',
196   -orderCode: '',
197   -expectedFinishDate: '' // 非必填,可空
  187 + pressingTypeList: [],
  188 + busiLineOptions: [], // 业务线移到data,不再放在setup
  189 + inspectForm: {
  190 + content: '',
  191 + isWorkOrder: '1',
  192 + pressingTypeName: '',
  193 + pressingType: '',
  194 + busiLineCn: '',
  195 + orderName: '',
  196 + orderCode: '',
  197 + expectedFinishDate: '' // 非必填,可空
198 198 },
199   -paramsOptins: {},
200   -showActionSheet: false,
201   -show: false,
202   -datetimeValue: Date.now(),
203   -inspectFormRules: {
204   -images: [
  199 + paramsOptins: {},
  200 + showActionSheet: false,
  201 + show: false,
  202 + datetimeValue: Date.now(),
  203 + inspectFormRules: {
  204 + images: [
205 205 {
206   -required: true,
207   -message: '请上传图片',
208   -trigger: 'change',
209   -validator: (rule, value, callback) => {
210   -const hasSuccessImg = this.imagesList.some(item => item.status === 'success')
211   -const imgCount = this.imagesList.filter(item => item.status === 'success').length
212   -if (!hasSuccessImg || imgCount < 1) {
213   -callback(new Error('最少需要上传1张图片'))
  206 + required: true,
  207 + message: '请上传图片',
  208 + trigger: 'change',
  209 + validator: (rule, value, callback) => {
  210 + const hasSuccessImg = this.imagesList.some(item => item.status === 'success')
  211 + const imgCount = this.imagesList.filter(item => item.status === 'success').length
  212 + if (!hasSuccessImg || imgCount < 1) {
  213 + callback(new Error('最少需要上传1张图片'))
214 214 } else if (imgCount > 3) {
215   -callback(new Error('最多只能上传3张图片'))
  215 + callback(new Error('最多只能上传3张图片'))
216 216 } else {
217   -callback()
  217 + callback()
218 218 }
219 219 }
220 220 }
221 221 ],
222   -content: [
  222 + content: [
223 223 { type: 'string', required: true, message: '请输入巡查描述', trigger: ['change'] }
224 224 ],
225   -isWorkOrder: [
  225 + isWorkOrder: [
226 226 { type: 'string', required: true, message: '请选择是否转为工单', trigger: ['change'] }
227 227 ],
228   -pressingTypeName: [
  228 + pressingTypeName: [
229 229 {
230   -required: true,
231   -message: '请选择紧急程度',
232   -trigger: 'change',
233   -validator: (rule, value, callback) => {
234   -if (this.inspectForm.isWorkOrder === '2' && !value) {
235   -callback(new Error('请选择紧急程度'))
  230 + required: true,
  231 + message: '请选择紧急程度',
  232 + trigger: 'change',
  233 + validator: (rule, value, callback) => {
  234 + if (this.inspectForm.isWorkOrder === '2' && !value) {
  235 + callback(new Error('请选择紧急程度'))
236 236 } else {
237   -callback()
  237 + callback()
238 238 }
239 239 }
240 240 }
241 241 ],
242   -busiLineCn: [
  242 + busiLineCn: [
243 243 {
244   -required: true,
245   -message: '请选择业务线',
246   -trigger: 'change',
247   -validator: (rule, value, callback) => {
248   -if (this.inspectForm.isWorkOrder === '2' && !value) {
249   -callback(new Error('请选择业务线'))
  244 + required: true,
  245 + message: '请选择业务线',
  246 + trigger: 'change',
  247 + validator: (rule, value, callback) => {
  248 + if (this.inspectForm.isWorkOrder === '2' && !value) {
  249 + callback(new Error('请选择业务线'))
250 250 } else {
251   -callback()
  251 + callback()
252 252 }
253 253 }
254 254 }
255 255 ],
256   -orderName: [
  256 + orderName: [
257 257 {
258   -required: true,
259   -message: '请选择问题类型',
260   -trigger: 'change',
261   -validator: (rule, value, callback) => {
262   -if (this.inspectForm.isWorkOrder === '2' && !value) {
263   -callback(new Error('请选择问题类型'))
  258 + required: true,
  259 + message: '请选择问题类型',
  260 + trigger: 'change',
  261 + validator: (rule, value, callback) => {
  262 + if (this.inspectForm.isWorkOrder === '2' && !value) {
  263 + callback(new Error('请选择问题类型'))
264 264 } else {
265   -callback()
  265 + callback()
266 266 }
267 267 }
268 268 }
269 269 ],
270   -expectedFinishDate: [
  270 + expectedFinishDate: [
271 271 {
272   -message: '请选择有效的希望完成时间',
273   -trigger: 'change',
274   -validator: (rule, value, callback) => {
275   -if (value && !/^\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}$/.test(value)) {
276   -callback(new Error('请选择有效的希望完成时间'))
  272 + message: '请选择有效的希望完成时间',
  273 + trigger: 'change',
  274 + validator: (rule, value, callback) => {
  275 + if (value && !/^\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}$/.test(value)) {
  276 + callback(new Error('请选择有效的希望完成时间'))
277 277 } else {
278   -callback()
  278 + callback()
279 279 }
280 280 }
281 281 }
... ... @@ -283,183 +283,183 @@ callback()
283 283 }
284 284 }
285 285 },
286   -onLoad(option) {
287   -this.paramsOptins = option
288   -// 初始化业务线选项
289   -const userStore = useUserStore();
290   -if (userStore.userInfo?.user?.busiLine) {
291   -const rawBusiLines = userStore.userInfo.user.busiLine.split(',');
292   -this.busiLineOptions = rawBusiLines.map(item => ({
293   -name: getDictLabel('business_line', item.trim()) || item.trim()
294   -}));
295   -}
  286 + onLoad(option) {
  287 + this.paramsOptins = option
  288 + // 初始化业务线选项
  289 + const userStore = useUserStore();
  290 + if (userStore.userInfo?.user?.busiLine) {
  291 + const rawBusiLines = userStore.userInfo.user.busiLine.split(',');
  292 + this.busiLineOptions = rawBusiLines.map(item => ({
  293 + name: getDictLabel('business_line', item.trim()) || item.trim()
  294 + }));
  295 + }
296 296 },
297   -onReady() {
298   -this.$refs.inspectFormRef.setRules(this.inspectFormRules)
  297 + onReady() {
  298 + this.$refs.inspectFormRef.setRules(this.inspectFormRules)
299 299 },
300   -onShow() {
301   -this.pressingTypeList = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('workorder_pressing_type'))
  300 + onShow() {
  301 + this.pressingTypeList = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('workorder_pressing_type'))
302 302 },
303   -methods: {
304   -handleWorkOrderChange() {
305   -if (this.inspectForm.isWorkOrder === '1') {
306   -this.inspectForm.pressingTypeName = ''
307   -this.inspectForm.pressingType = ''
308   -this.inspectForm.busiLineCn = ''
309   -this.inspectForm.orderName = ''
310   -this.inspectForm.orderCode = ''
311   -this.$setupCtx.cascaderOptions.value = []
312   -this.$setupCtx.cascaderValue.value = []
313   -this.inspectForm.expectedFinishDate = ''
314   -this.$refs.inspectFormRef.validateField(['pressingTypeName', 'busiLineCn', 'orderName', 'expectedFinishDate'])
  303 + methods: {
  304 + handleWorkOrderChange() {
  305 + if (this.inspectForm.isWorkOrder === '1') {
  306 + this.inspectForm.pressingTypeName = ''
  307 + this.inspectForm.pressingType = ''
  308 + this.inspectForm.busiLineCn = ''
  309 + this.inspectForm.orderName = ''
  310 + this.inspectForm.orderCode = ''
  311 + this.$setupCtx.cascaderOptions.value = []
  312 + this.$setupCtx.cascaderValue.value = []
  313 + this.inspectForm.expectedFinishDate = ''
  314 + this.$refs.inspectFormRef.validateField(['pressingTypeName', 'busiLineCn', 'orderName', 'expectedFinishDate'])
315 315 } else {
316   -// 修复点:直接读取data中的 busiLineOptions,不再访问setup
317   -const busiOpts = this.busiLineOptions
318   -if (busiOpts.length > 0) {
319   -this.inspectForm.busiLineCn = busiOpts[0].name
320   -this.handleBusiLineChange()
  316 + // 修复点:直接读取data中的 busiLineOptions,不再访问setup
  317 + const busiOpts = this.busiLineOptions
  318 + if (busiOpts.length > 0) {
  319 + this.inspectForm.busiLineCn = busiOpts[0].name
  320 + this.handleBusiLineChange()
321 321 }
322 322 }
323 323 },
324   -handleBusiLineChange() {
325   -this.inspectForm.orderName = ''
326   -this.inspectForm.orderCode = ''
327   -this.$setupCtx.cascaderValue.value = []
328   -this.$setupCtx.cascaderOptions.value = []
329   -const busiLineEn = this.$setupCtx.getBusiLineEnByCn(this.inspectForm.busiLineCn)
330   -this.$setupCtx.loadProblemCascader(busiLineEn)
331   -this.$refs.inspectFormRef.validateField('busiLineCn')
  324 + handleBusiLineChange() {
  325 + this.inspectForm.orderName = ''
  326 + this.inspectForm.orderCode = ''
  327 + this.$setupCtx.cascaderValue.value = []
  328 + this.$setupCtx.cascaderOptions.value = []
  329 + const busiLineEn = this.$setupCtx.getBusiLineEnByCn(this.inspectForm.busiLineCn)
  330 + this.$setupCtx.loadProblemCascader(busiLineEn)
  331 + this.$refs.inspectFormRef.validateField('busiLineCn')
332 332 },
333   -handleActionSheetOpen(type) {
334   -uni.hideKeyboard()
335   -if (type === 'pressingType') {
336   -this.showActionSheet = true
  333 + handleActionSheetOpen(type) {
  334 + uni.hideKeyboard()
  335 + if (type === 'pressingType') {
  336 + this.showActionSheet = true
337 337 }
338 338 },
339   -handleActionSheetClose() {
340   -this.showActionSheet = false
  339 + handleActionSheetClose() {
  340 + this.showActionSheet = false
341 341 },
342   -handlePressingTypeSelect(e) {
343   -this.inspectForm.pressingTypeName = e.name
344   -this.inspectForm.pressingType = e.value
345   -this.showActionSheet = false
346   -this.$refs.inspectFormRef.validateField('pressingTypeName')
  342 + handlePressingTypeSelect(e) {
  343 + this.inspectForm.pressingTypeName = e.name
  344 + this.inspectForm.pressingType = e.value
  345 + this.showActionSheet = false
  346 + this.$refs.inspectFormRef.validateField('pressingTypeName')
347 347 },
348   -expectedFinishDateConfirm(e) {
349   -this.inspectForm.expectedFinishDate = timeFormat(e.value, 'yyyy-mm-dd hh:MM:ss')
350   -this.show = false
351   -this.$refs.inspectFormRef.validateField('expectedFinishDate')
  348 + expectedFinishDateConfirm(e) {
  349 + this.inspectForm.expectedFinishDate = timeFormat(e.value, 'yyyy-mm-dd hh:MM:ss')
  350 + this.show = false
  351 + this.$refs.inspectFormRef.validateField('expectedFinishDate')
352 352 },
353   -clearExpectedFinishDate() {
354   -this.inspectForm.expectedFinishDate = ''
355   -this.$refs.inspectFormRef.validateField('expectedFinishDate')
  353 + clearExpectedFinishDate() {
  354 + this.inspectForm.expectedFinishDate = ''
  355 + this.$refs.inspectFormRef.validateField('expectedFinishDate')
356 356 },
357   -deleteImg(event) {
358   -this.imagesList.splice(event.index, 1)
359   -this.$refs.inspectFormRef.validateField('images')
360   -uni.showToast({ title: '图片删除成功', icon: 'success' })
  357 + deleteImg(event) {
  358 + this.imagesList.splice(event.index, 1)
  359 + this.$refs.inspectFormRef.validateField('images')
  360 + uni.showToast({ title: '图片删除成功', icon: 'success' })
361 361 },
362   -async uploadImgs(event) {
363   -const fileList = Array.isArray(event.file) ? event.file : [event.file]
364   -const targetImgList = this.imagesList
365   -const filePaths = fileList.map(item => item.url)
366   -const tempItems = fileList.map(item => ({
367   -...item,
368   -status: 'uploading',
369   -message: '上传中'
  362 + async uploadImgs(event) {
  363 + const fileList = Array.isArray(event.file) ? event.file : [event.file]
  364 + const targetImgList = this.imagesList
  365 + const filePaths = fileList.map(item => item.url)
  366 + const tempItems = fileList.map(item => ({
  367 + ...item,
  368 + status: 'uploading',
  369 + message: '上传中'
370 370 }))
371   -const startIndex = targetImgList.length
372   -targetImgList.push(...tempItems)
373   -try {
374   -const uploadResultUrls = await uploadImages({
375   -filePaths: filePaths,
376   -ignoreError: true
  371 + const startIndex = targetImgList.length
  372 + targetImgList.push(...tempItems)
  373 + try {
  374 + const uploadResultUrls = await uploadImages({
  375 + filePaths: filePaths,
  376 + ignoreError: true
377 377 })
378   -uploadResultUrls.forEach((url, index) => {
379   -if (targetImgList[startIndex + index]) {
380   -targetImgList.splice(startIndex + index, 1, {
381   -...fileList[index],
382   -status: 'success',
383   -message: '',
384   -url: url
  378 + uploadResultUrls.forEach((url, index) => {
  379 + if (targetImgList[startIndex + index]) {
  380 + targetImgList.splice(startIndex + index, 1, {
  381 + ...fileList[index],
  382 + status: 'success',
  383 + message: '',
  384 + url: url
385 385 })
386 386 }
387 387 })
388   -if (uploadResultUrls.length < fileList.length) {
389   -const failCount = fileList.length - uploadResultUrls.length
390   -uni.showToast({ title: `成功上传${uploadResultUrls.length}张,失败${failCount}张`, icon: 'none' })
  388 + if (uploadResultUrls.length < fileList.length) {
  389 + const failCount = fileList.length - uploadResultUrls.length
  390 + uni.showToast({ title: `成功上传${uploadResultUrls.length}张,失败${failCount}张`, icon: 'none' })
391 391 } else {
392   -uni.showToast({ title: `成功上传${fileList.length}张图片`, icon: 'success' })
  392 + uni.showToast({ title: `成功上传${fileList.length}张图片`, icon: 'success' })
393 393 }
394   -this.$refs.inspectFormRef.validateField('images')
  394 + this.$refs.inspectFormRef.validateField('images')
395 395 } catch (err) {
396   -console.error('图片上传失败:', err)
397   -for (let i = 0; i < fileList.length; i++) {
398   -if (targetImgList[startIndex + i]) {
399   -targetImgList.splice(startIndex + i, 1, {
400   -...fileList[i],
401   -status: 'failed',
402   -message: '上传失败'
  396 + console.error('图片上传失败:', err)
  397 + for (let i = 0; i < fileList.length; i++) {
  398 + if (targetImgList[startIndex + i]) {
  399 + targetImgList.splice(startIndex + i, 1, {
  400 + ...fileList[i],
  401 + status: 'failed',
  402 + message: '上传失败'
403 403 })
404 404 }
405 405 }
406   -uni.showToast({ title: '图片上传失败,请重试', icon: 'none' })
407   -this.$refs.inspectFormRef.validateField('images')
  406 + uni.showToast({ title: '图片上传失败,请重试', icon: 'none' })
  407 + this.$refs.inspectFormRef.validateField('images')
408 408 }
409 409 },
410   -handleExceed() {
411   -uni.showToast({ title: '最多只能上传3张图片', icon: 'none' })
  410 + handleExceed() {
  411 + uni.showToast({ title: '最多只能上传3张图片', icon: 'none' })
412 412 },
413   -getImgUrlList(imgList) {
414   -return imgList.filter(item => item.status === 'success').map(item => item.url)
  413 + getImgUrlList(imgList) {
  414 + return imgList.filter(item => item.status === 'success').map(item => item.url)
415 415 },
416   -async submitInspect() {
417   -console.log('当前选择是否转为工单:', this.inspectForm.isWorkOrder)
418   -try {
419   -await this.$refs.inspectFormRef.validate()
420   -console.log(this.imagesList)
421   -const baseSubmitData = {
422   -"batchNo": this.paramsOptins.batchNo,
423   -"planNo": this.paramsOptins.planNo,
424   -"imgHost": "1",
425   -"imgList": this.getImgUrlList(this.imagesList),
426   -"inspectionState": this.inspectForm.isWorkOrder,
427   -"transState": this.inspectForm.isWorkOrder === '1' ? '1' : '2',
428   -"transWorkNo": "default'",
429   -"remark": this.inspectForm.content.trim()
  416 + async submitInspect() {
  417 + console.log('当前选择是否转为工单:', this.inspectForm.isWorkOrder)
  418 + try {
  419 + await this.$refs.inspectFormRef.validate()
  420 + console.log(this.imagesList)
  421 + const baseSubmitData = {
  422 + "batchNo": this.paramsOptins.batchNo,
  423 + "planNo": this.paramsOptins.planNo,
  424 + "imgHost": "1",
  425 + "imgList": this.getImgUrlList(this.imagesList),
  426 + "inspectionState": this.inspectForm.isWorkOrder,
  427 + "transState": this.inspectForm.isWorkOrder === '1' ? '1' : '2',
  428 + "transWorkNo": "default'",
  429 + "remark": this.inspectForm.content.trim()
430 430 }
431   -let submitData = { ...baseSubmitData }
432   -if (this.inspectForm.isWorkOrder === '2') {
433   -submitData.pressingType = this.inspectForm.pressingType
434   -const busiLineEn = this.$setupCtx.getBusiLineEnByCn(this.inspectForm.busiLineCn)
435   -submitData.busiLine = busiLineEn
436   -submitData.orderName = this.inspectForm.orderName
437   -submitData.orderCode = this.inspectForm.orderCode
438   -if (this.inspectForm.expectedFinishDate) {
439   -submitData.expectedFinishDate = new Date(this.inspectForm.expectedFinishDate).getTime()
  431 + let submitData = { ...baseSubmitData }
  432 + if (this.inspectForm.isWorkOrder === '2') {
  433 + submitData.pressingType = this.inspectForm.pressingType
  434 + const busiLineEn = this.$setupCtx.getBusiLineEnByCn(this.inspectForm.busiLineCn)
  435 + submitData.busiLine = busiLineEn
  436 + submitData.orderName = this.inspectForm.orderName
  437 + submitData.orderCode = this.inspectForm.orderCode
  438 + if (this.inspectForm.expectedFinishDate) {
  439 + submitData.expectedFinishDate = new Date(this.inspectForm.expectedFinishDate).getTime()
440 440 }
441 441 }
442   -uni.showLoading({ title: '提交中...' })
443   -await inspectionCreate(submitData)
444   -uni.hideLoading()
445   -uni.showToast({
446   -title: '提交成功',
447   -icon: 'success',
448   -duration: 1000
  442 + uni.showLoading({ title: '提交中...' })
  443 + await inspectionCreate(submitData)
  444 + uni.hideLoading()
  445 + uni.showToast({
  446 + title: '提交成功',
  447 + icon: 'success',
  448 + duration: 1000
449 449 })
450   -setTimeout(() => {
451   -uni.reLaunch({
452   -url: '/pages-sub/daily/patrol-manage/index'
  450 + setTimeout(() => {
  451 + uni.reLaunch({
  452 + url: '/pages-sub/daily/patrol-manage/index'
453 453 })
454 454 }, 1000)
455 455 } catch (error) {
456   -uni.hideLoading()
457   -if (!Array.isArray(error)) {
458   -console.error('巡查表单提交失败:', error)
459   -uni.showToast({
460   -title: '提交失败,请重试',
461   -icon: 'none',
462   -duration: 2000
  456 + uni.hideLoading()
  457 + if (!Array.isArray(error)) {
  458 + console.error('巡查表单提交失败:', error)
  459 + uni.showToast({
  460 + title: error.msg,
  461 + icon: 'none',
  462 + duration: 2000
463 463 })
464 464 }
465 465 }
... ... @@ -469,13 +469,15 @@ duration: 2000
469 469 </script>
470 470 <style lang="scss" scoped>
471 471 .u-page {
472   -min-height: 100vh;
  472 + min-height: 100vh;
473 473 }
  474 +
474 475 .inspect-form-content {
475   -background: #fff;
476   -padding: 20rpx;
  476 + background: #fff;
  477 + padding: 20rpx;
477 478 }
  479 +
478 480 .form-item {
479   -margin-bottom: 20rpx;
  481 + margin-bottom: 20rpx;
480 482 }
481 483 </style>
... ...
pages-sub/daily/patrol-manage/index.vue
... ... @@ -162,7 +162,7 @@ const queryList = async (pageNo, pageSize) =&gt; {
162 162 // 跳转明细
163 163 const gotoDetail = (item) => {
164 164 uni.navigateTo({
165   - url: `/pages-sub/daily/patrol-manage/pending-plan-detail?batchNo=${item.batchNo}&status=${activeTab.value}`
  165 + url: `/pages-sub/daily/patrol-manage/pending-plan-detail?batchNo=${item.batchNo}&status=${activeTab.value}&busiLine=${item.busiLine}`
166 166 });
167 167 };
168 168 // 修复点5:优化页面生命周期逻辑
... ...
pages.json
... ... @@ -107,6 +107,23 @@
107 107 },
108 108  
109 109 {
  110 + "path": "maintain-manage/type-list",
  111 + "style": { "navigationBarTitleText": "养护类型" }
  112 + },
  113 +
  114 + {
  115 + "path": "maintain-manage/type-list-page",
  116 + "style": { "navigationBarTitleText": "养护" }
  117 + },
  118 +
  119 + {
  120 + "path": "maintain-manage/now-list-page",
  121 + "style": { "navigationBarTitleText": "本次养护记录" }
  122 + },
  123 +
  124 +
  125 +
  126 + {
110 127 "path": "maintain-manage/add-record",
111 128 "style": { "navigationBarTitleText": "新增养护记录" }
112 129 },
... ...
pages/index/index.vue
... ... @@ -132,7 +132,7 @@ import { timeFormat } from &#39;@/uni_modules/uview-plus&#39;;
132 132 // import uniCharts from '@/components/uni-charts/uni-charts.vue';
133 133 import { getUnreadCount } from "@/api/user";
134 134 import {
135   - getTaskCompletionSummary,
  135 + // getTaskCompletionSummary,
136 136 taskDetailsWithDistance,
137 137 taskDetailsWithSmartSort,
138 138 getCurrentOrg,
... ... @@ -319,6 +319,20 @@ const baseList = ref&lt;Array&lt;{ num: string; title: string; name: string }&gt;&gt;([]);
319 319 const orgOptions = ref<any[]>([]);
320 320 const value1 = ref<number | string>('');
321 321 const showActionSheet = ref(false);
  322 +// 日期选择确认回调
  323 +const handleDateConfirm = async (range: string[]) => {
  324 + console.log('选择的日期范围:', range);
  325 + dateRange.value = range;
  326 + const [startDate, endDate] = range;
  327 + // 1. 更新图表数据
  328 + // await initChartData(startDate, endDate);
  329 +
  330 + // 2. 刷新组织指标卡片(数字指标4格卡片)
  331 + if(value1.value){
  332 + await fetchOrgIndicator(Number(value1.value), startDate, endDate);
  333 + }
  334 +};
  335 +
322 336 watch(orgOptions, async (list) => {
323 337 if (list.length && !value1.value) {
324 338 value1.value = list[0].value;
... ... @@ -382,6 +396,8 @@ const handleTabClick = (tabIndex: number) =&gt; {
382 396 currentTabValue.value = tabIndex;
383 397 showChartArea.value = true;
384 398 };
  399 +
  400 +
385 401 // ========== 公共函数:请求 orgTaskIndicator 指标接口 ==========
386 402 const fetchOrgIndicator = async (orgId: number, beginDate: string, endDate: string) => {
387 403 try {
... ... @@ -410,13 +426,13 @@ const initRecent7Days = () =&gt; {
410 426 const end = new Date(now);
411 427 end.setDate(end.getDate());
412 428 const start = new Date(end);
413   - start.setDate(start.getDate() - 6);
  429 + start.setDate(start.getDate() );
414 430 dateRange.value = [
415 431 timeFormat(start, 'yyyy-mm-dd'),
416 432 timeFormat(end, 'yyyy-mm-dd')
417 433 ];
418 434 console.log(dateRange.value)
419   - initChartData(dateRange.value[0], dateRange.value[1]);
  435 + // initChartData(dateRange.value[0], dateRange.value[1]);
420 436 };
421 437 const getUnread = async () => {
422 438 try {
... ... @@ -439,7 +455,7 @@ const getOrg = async () =&gt; {
439 455 const initChartData = async (beginTime: string, endTime: string) => {
440 456 try {
441 457 const postData = { beginTime, endTime };
442   - const res = await getTaskCompletionSummary(postData);
  458 + // const res = await getTaskCompletionSummary(postData);
443 459 klineCategories.value = res.map((item: any) => item.currentDate);
444 460 klineChartData.value[0].data = res.map((item: any) => item.completedNum);
445 461 klineChartData.value[1].data = res.map((item: any) => item.countPendingNum);
... ... @@ -840,11 +856,11 @@ $border-color: #e5e5e5;
840 856 display: flex;
841 857 }
842 858 .task-item__name {
843   - width: 80rpx;
  859 + width: 88rpx;
844 860 }
845 861 .task-name-text {
846 862 flex: 1;
847   - font-size: 24rpx;
  863 + font-size: 28rpx;
848 864 color: $text-color;
849 865 display: -webkit-box;
850 866 -webkit-line-clamp: 2;
... ... @@ -853,7 +869,7 @@ $border-color: #e5e5e5;
853 869 text-overflow: ellipsis;
854 870 }
855 871 &__name {
856   - font-size: 24rpx;
  872 + font-size: 28rpx;
857 873 color: $text-color;
858 874 display: inline-block;
859 875 }
... ... @@ -861,7 +877,7 @@ $border-color: #e5e5e5;
861 877 display: flex;
862 878 align-items: flex-start;
863 879 gap: 6rpx;
864   - font-size: 24rpx;
  880 + font-size: 26rpx;
865 881 color: $text-color-light;
866 882 margin-top: $spacing-sm;
867 883 align-items: center;
... ...