Commit 25904bec21db0fc8d40aab3ec9216d652868b88b

Authored by 刘淇
1 parent 0c577b68

养护重新改

pages-sub/daily/maintain-manage/index.vue
@@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
4 <up-sticky> 4 <up-sticky>
5 <view class="sticky-wrap"> 5 <view class="sticky-wrap">
6 <!-- 第一行标签:养护类型 --> 6 <!-- 第一行标签:养护类型 -->
7 - <up-tabs 7 + <!-- <up-tabs
8 v-model="activeType" 8 v-model="activeType"
9 :list="typeTabs" 9 :list="typeTabs"
10 line-width="40rpx" 10 line-width="40rpx"
@@ -13,10 +13,10 @@ @@ -13,10 +13,10 @@
13 :scrollable="true" 13 :scrollable="true"
14 class="first-tabs" 14 class="first-tabs"
15 @click="handleTypeChange" 15 @click="handleTypeChange"
16 - ></up-tabs> 16 + ></up-tabs> -->
17 17
18 <!-- 第二行标签:状态筛选 --> 18 <!-- 第二行标签:状态筛选 -->
19 - <up-tabs 19 + <!-- <up-tabs
20 v-model="activeStatus" 20 v-model="activeStatus"
21 :list="statusTabs" 21 :list="statusTabs"
22 line-width="40rpx" 22 line-width="40rpx"
@@ -25,7 +25,7 @@ @@ -25,7 +25,7 @@
25 :scrollable="true" 25 :scrollable="true"
26 class="second-tabs" 26 class="second-tabs"
27 @click="handleStatusChange" 27 @click="handleStatusChange"
28 - ></up-tabs> 28 + ></up-tabs> -->
29 29
30 <!-- 动态搜索框(道路名称/计划名称) --> 30 <!-- 动态搜索框(道路名称/计划名称) -->
31 <up-search 31 <up-search
@@ -40,7 +40,7 @@ @@ -40,7 +40,7 @@
40 @custom="handleSearch" 40 @custom="handleSearch"
41 :clearabled="false" 41 :clearabled="false"
42 maxlength="50" 42 maxlength="50"
43 - style="margin: 20rpx 20rpx 0" 43 + style="margin: 0rpx 20rpx 0"
44 ></up-search> 44 ></up-search>
45 </view> 45 </view>
46 </up-sticky> 46 </up-sticky>
@@ -58,7 +58,7 @@ @@ -58,7 +58,7 @@
58 </template> 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 <up-card 63 <up-card
64 :border="false" 64 :border="false"
@@ -81,8 +81,8 @@ @@ -81,8 +81,8 @@
81 <!-- </up-button>--> 81 <!-- </up-button>-->
82 <!-- </view>--> 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 </view> 86 </view>
87 <!-- <view class="u-body-item u-flex">--> 87 <!-- <view class="u-body-item u-flex">-->
88 <!-- <view class="u-body-item-title">批次号:</view>--> 88 <!-- <view class="u-body-item-title">批次号:</view>-->
@@ -93,11 +93,11 @@ @@ -93,11 +93,11 @@
93 <view class="u-line-1 u-body-value">{{ item.roadName || '-' }}</view> 93 <view class="u-line-1 u-body-value">{{ item.roadName || '-' }}</view>
94 </view> 94 </view>
95 95
96 - <view class="u-body-item u-flex"> 96 + <!-- <view class="u-body-item u-flex">
97 <view class="u-body-item-title">所属街道:</view> 97 <view class="u-body-item-title">所属街道:</view>
98 <view class="u-line-1 u-body-value">{{ item.streetName }} 98 <view class="u-line-1 u-body-value">{{ item.streetName }}
99 </view> 99 </view>
100 - </view> 100 + </view> -->
101 101
102 <view class="u-body-item u-flex common-item-center common-justify-between"> 102 <view class="u-body-item u-flex common-item-center common-justify-between">
103 <view class="u-body-item-title">养护级别: {{uni.$dict.getDictLabel('conserve_level', item.levelId) || '-'}}</view> 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,8 +308,12 @@ const fetchData = async (pageNo, pageSize) =&gt; {
308 308
309 // 跳转到计划明细页面 309 // 跳转到计划明细页面
310 const goToDetail = (item) => { 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 uni.navigateTo({ 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,7 +347,6 @@ onShow(()=&gt;{
343 <style scoped lang="scss"> 347 <style scoped lang="scss">
344 // 吸顶区域样式 348 // 吸顶区域样式
345 .sticky-wrap { 349 .sticky-wrap {
346 - margin-top: -10rpx;  
347 background-color: #fff; 350 background-color: #fff;
348 padding-bottom: 10rpx; 351 padding-bottom: 10rpx;
349 border-bottom: 1px solid #eee; 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 <template> 1 <template>
2 <view class="u-page"> 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 @delete="(event) => deleteImg(event)" @on-exceed="handleExceed" multiple :max-count="3" upload-text="选择图片" 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 @change="handleWorkOrderChange"> 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 :label="item.label" :name="item.value"> 21 :label="item.label" :name="item.value">
22 {{ item.label }} 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 @click="show = true; uni.hideKeyboard()"> 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 :style="{ width: '100%', height: '88rpx', fontSize: '32rpx', borderRadius: 0 }"></up-button> 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 @select="handlePressingTypeSelect"></up-action-sheet> 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 :auto-close="false" @confirm="cascaderConfirm"></up-cascader> 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 @cancel="show = false" @confirm="expectedFinishDateConfirm"></up-datetime-picker> 79 @cancel="show = false" @confirm="expectedFinishDateConfirm"></up-datetime-picker>
80 -</view> 80 + </view>
81 </template> 81 </template>
82 <script setup lang="ts"> 82 <script setup lang="ts">
83 import { ref, getCurrentInstance } from 'vue' 83 import { ref, getCurrentInstance } from 'vue'
@@ -98,68 +98,68 @@ const cascaderOptions = ref([]); @@ -98,68 +98,68 @@ const cascaderOptions = ref([]);
98 98
99 // 打开级联弹窗 99 // 打开级联弹窗
100 const openCascader = () => { 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 const cascaderConfirm = (selectedArr) => { 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 const loadProblemCascader = async (parentCode) => { 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 } catch (err) { 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 const getBusiLineCnByEn = (busiLineEn) => { 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 const getBusiLineEnByCn = (cnName) => { 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,105 +177,105 @@ import { timeFormat } from &#39;@/uni_modules/uview-plus&#39;
177 import { useUserStore } from '@/pinia/user'; 177 import { useUserStore } from '@/pinia/user';
178 import { getDictLabel } from '@/common/utils/dict'; 178 import { getDictLabel } from '@/common/utils/dict';
179 export default { 179 export default {
180 -data() {  
181 -return {  
182 -imagesList: [],  
183 -radioList: [ 180 + data() {
  181 + return {
  182 + imagesList: [],
  183 + radioList: [
184 { label: '否', value: '1' }, 184 { label: '否', value: '1' },
185 { label: '是', value: '2' } 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 } else if (imgCount > 3) { 214 } else if (imgCount > 3) {
215 -callback(new Error('最多只能上传3张图片')) 215 + callback(new Error('最多只能上传3张图片'))
216 } else { 216 } else {
217 -callback() 217 + callback()
218 } 218 }
219 } 219 }
220 } 220 }
221 ], 221 ],
222 -content: [ 222 + content: [
223 { type: 'string', required: true, message: '请输入巡查描述', trigger: ['change'] } 223 { type: 'string', required: true, message: '请输入巡查描述', trigger: ['change'] }
224 ], 224 ],
225 -isWorkOrder: [ 225 + isWorkOrder: [
226 { type: 'string', required: true, message: '请选择是否转为工单', trigger: ['change'] } 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 } else { 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 } else { 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 } else { 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 } else { 277 } else {
278 -callback() 278 + callback()
279 } 279 }
280 } 280 }
281 } 281 }
@@ -283,183 +283,183 @@ callback() @@ -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 } else { 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 } else { 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 } catch (err) { 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 }, 1000) 454 }, 1000)
455 } catch (error) { 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,13 +469,15 @@ duration: 2000
469 </script> 469 </script>
470 <style lang="scss" scoped> 470 <style lang="scss" scoped>
471 .u-page { 471 .u-page {
472 -min-height: 100vh; 472 + min-height: 100vh;
473 } 473 }
  474 +
474 .inspect-form-content { 475 .inspect-form-content {
475 -background: #fff;  
476 -padding: 20rpx; 476 + background: #fff;
  477 + padding: 20rpx;
477 } 478 }
  479 +
478 .form-item { 480 .form-item {
479 -margin-bottom: 20rpx; 481 + margin-bottom: 20rpx;
480 } 482 }
481 </style> 483 </style>
pages-sub/daily/patrol-manage/index.vue
@@ -162,7 +162,7 @@ const queryList = async (pageNo, pageSize) =&gt; { @@ -162,7 +162,7 @@ const queryList = async (pageNo, pageSize) =&gt; {
162 // 跳转明细 162 // 跳转明细
163 const gotoDetail = (item) => { 163 const gotoDetail = (item) => {
164 uni.navigateTo({ 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 // 修复点5:优化页面生命周期逻辑 168 // 修复点5:优化页面生命周期逻辑
pages.json
@@ -107,6 +107,23 @@ @@ -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 "path": "maintain-manage/add-record", 127 "path": "maintain-manage/add-record",
111 "style": { "navigationBarTitleText": "新增养护记录" } 128 "style": { "navigationBarTitleText": "新增养护记录" }
112 }, 129 },
pages/index/index.vue
@@ -132,7 +132,7 @@ import { timeFormat } from &#39;@/uni_modules/uview-plus&#39;; @@ -132,7 +132,7 @@ import { timeFormat } from &#39;@/uni_modules/uview-plus&#39;;
132 // import uniCharts from '@/components/uni-charts/uni-charts.vue'; 132 // import uniCharts from '@/components/uni-charts/uni-charts.vue';
133 import { getUnreadCount } from "@/api/user"; 133 import { getUnreadCount } from "@/api/user";
134 import { 134 import {
135 - getTaskCompletionSummary, 135 + // getTaskCompletionSummary,
136 taskDetailsWithDistance, 136 taskDetailsWithDistance,
137 taskDetailsWithSmartSort, 137 taskDetailsWithSmartSort,
138 getCurrentOrg, 138 getCurrentOrg,
@@ -319,6 +319,20 @@ const baseList = ref&lt;Array&lt;{ num: string; title: string; name: string }&gt;&gt;([]); @@ -319,6 +319,20 @@ const baseList = ref&lt;Array&lt;{ num: string; title: string; name: string }&gt;&gt;([]);
319 const orgOptions = ref<any[]>([]); 319 const orgOptions = ref<any[]>([]);
320 const value1 = ref<number | string>(''); 320 const value1 = ref<number | string>('');
321 const showActionSheet = ref(false); 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 watch(orgOptions, async (list) => { 336 watch(orgOptions, async (list) => {
323 if (list.length && !value1.value) { 337 if (list.length && !value1.value) {
324 value1.value = list[0].value; 338 value1.value = list[0].value;
@@ -382,6 +396,8 @@ const handleTabClick = (tabIndex: number) =&gt; { @@ -382,6 +396,8 @@ const handleTabClick = (tabIndex: number) =&gt; {
382 currentTabValue.value = tabIndex; 396 currentTabValue.value = tabIndex;
383 showChartArea.value = true; 397 showChartArea.value = true;
384 }; 398 };
  399 +
  400 +
385 // ========== 公共函数:请求 orgTaskIndicator 指标接口 ========== 401 // ========== 公共函数:请求 orgTaskIndicator 指标接口 ==========
386 const fetchOrgIndicator = async (orgId: number, beginDate: string, endDate: string) => { 402 const fetchOrgIndicator = async (orgId: number, beginDate: string, endDate: string) => {
387 try { 403 try {
@@ -410,13 +426,13 @@ const initRecent7Days = () =&gt; { @@ -410,13 +426,13 @@ const initRecent7Days = () =&gt; {
410 const end = new Date(now); 426 const end = new Date(now);
411 end.setDate(end.getDate()); 427 end.setDate(end.getDate());
412 const start = new Date(end); 428 const start = new Date(end);
413 - start.setDate(start.getDate() - 6); 429 + start.setDate(start.getDate() );
414 dateRange.value = [ 430 dateRange.value = [
415 timeFormat(start, 'yyyy-mm-dd'), 431 timeFormat(start, 'yyyy-mm-dd'),
416 timeFormat(end, 'yyyy-mm-dd') 432 timeFormat(end, 'yyyy-mm-dd')
417 ]; 433 ];
418 console.log(dateRange.value) 434 console.log(dateRange.value)
419 - initChartData(dateRange.value[0], dateRange.value[1]); 435 + // initChartData(dateRange.value[0], dateRange.value[1]);
420 }; 436 };
421 const getUnread = async () => { 437 const getUnread = async () => {
422 try { 438 try {
@@ -439,7 +455,7 @@ const getOrg = async () =&gt; { @@ -439,7 +455,7 @@ const getOrg = async () =&gt; {
439 const initChartData = async (beginTime: string, endTime: string) => { 455 const initChartData = async (beginTime: string, endTime: string) => {
440 try { 456 try {
441 const postData = { beginTime, endTime }; 457 const postData = { beginTime, endTime };
442 - const res = await getTaskCompletionSummary(postData); 458 + // const res = await getTaskCompletionSummary(postData);
443 klineCategories.value = res.map((item: any) => item.currentDate); 459 klineCategories.value = res.map((item: any) => item.currentDate);
444 klineChartData.value[0].data = res.map((item: any) => item.completedNum); 460 klineChartData.value[0].data = res.map((item: any) => item.completedNum);
445 klineChartData.value[1].data = res.map((item: any) => item.countPendingNum); 461 klineChartData.value[1].data = res.map((item: any) => item.countPendingNum);
@@ -840,11 +856,11 @@ $border-color: #e5e5e5; @@ -840,11 +856,11 @@ $border-color: #e5e5e5;
840 display: flex; 856 display: flex;
841 } 857 }
842 .task-item__name { 858 .task-item__name {
843 - width: 80rpx; 859 + width: 88rpx;
844 } 860 }
845 .task-name-text { 861 .task-name-text {
846 flex: 1; 862 flex: 1;
847 - font-size: 24rpx; 863 + font-size: 28rpx;
848 color: $text-color; 864 color: $text-color;
849 display: -webkit-box; 865 display: -webkit-box;
850 -webkit-line-clamp: 2; 866 -webkit-line-clamp: 2;
@@ -853,7 +869,7 @@ $border-color: #e5e5e5; @@ -853,7 +869,7 @@ $border-color: #e5e5e5;
853 text-overflow: ellipsis; 869 text-overflow: ellipsis;
854 } 870 }
855 &__name { 871 &__name {
856 - font-size: 24rpx; 872 + font-size: 28rpx;
857 color: $text-color; 873 color: $text-color;
858 display: inline-block; 874 display: inline-block;
859 } 875 }
@@ -861,7 +877,7 @@ $border-color: #e5e5e5; @@ -861,7 +877,7 @@ $border-color: #e5e5e5;
861 display: flex; 877 display: flex;
862 align-items: flex-start; 878 align-items: flex-start;
863 gap: 6rpx; 879 gap: 6rpx;
864 - font-size: 24rpx; 880 + font-size: 26rpx;
865 color: $text-color-light; 881 color: $text-color-light;
866 margin-top: $spacing-sm; 882 margin-top: $spacing-sm;
867 align-items: center; 883 align-items: center;