Commit 13203a04c9c4ee0ac0b95a6b92b11eeb4d95ebdc

Authored by 刘淇
1 parent 2d57b60f

首页 统计

pages-sub/daily/maintain-manage/add-record.vue
... ... @@ -4,14 +4,21 @@
4 4 <up-form label-position="left" :model="inspectForm" ref="inspectFormRef" label-width="140rpx">
5 5 <!-- ========== 动态表单区域 ========== -->
6 6 <template v-for="item in dynamicFields" :key="item.fieldCode">
7   - <up-form-item :label="item.fieldName" :prop="item.fieldCode" :required="item.required === 1" class="form-item"
8   - @click="openPickerIfNeeded(item)">
  7 + <up-form-item :label="item.fieldName" :prop="item.fieldCode" class="form-item" @click="openPickerIfNeeded(item)">
  8 +
9 9 <!-- type 1 数值:u-number-box,最多2位小数,min=0,max取item.length -->
10 10 <view v-if="item.fieldType === 1" class="number-box-wrap">
11   - <u-number-box v-model="inspectForm[item.fieldCode]" min="1" :step="1"
12   - :max="item.length ? 10 ** Number(item.length) - 1 : null"
13   - :placeholder="`请输入${item.fieldName}${item.unit} `" @change="valChange(item)" class="large-number-box">
14   - </u-number-box>
  11 + <u-number-box
  12 + v-model="inspectForm[item.fieldCode]"
  13 + min="1"
  14 + :step="1"
  15 + :max="item.length ? 10 ** Number(item.length) - 1 : null"
  16 + :placeholder="`请输入${item.fieldName}${item.unit} `"
  17 + @change="valChange(item)"
  18 + @blur="valChange(item)"
  19 + class="large-number-box">
  20 +</u-number-box>
  21 +
15 22 <text class="unit-text" v-if="item.unit">{{ item.unit }}</text>
16 23 </view>
17 24 <!-- type 2 文本 -->
... ... @@ -132,10 +139,14 @@
132 139 </up-form>
133 140 </view>
134 141 <!-- 底部提交按钮 -->
135   - <view class="fixed-bottom-btn-wrap">
136   - <up-button type="primary" text="提交" @click="submitInspect"
137   - style="width: 100%; height: 88rpx; font-size:32rpx; border-radius: 0;"></up-button>
138   - </view>
  142 + <view v-if="dynamicFields.length === 0" class="empty-field-tip">
  143 + <text>请去平台配置业务类型字段</text>
  144 +</view>
  145 +<!-- ========== 底部提交按钮:只有存在动态字段才显示 ========== -->
  146 +<view class="fixed-bottom-btn-wrap" v-if="dynamicFields.length > 0">
  147 +<up-button type="primary" text="提交" @click="submitInspect"
  148 +style="width: 100%; height: 88rpx; font-size:32rpx; border-radius: 0;"></up-button>
  149 +</view>
139 150 <!-- uview-plus 日期时间选择器(全局复用) -->
140 151 <up-datetime-picker :show="datePickerShow" v-model="datePickerValue" mode="datetime" close-on-click-overlay
141 152 @confirm="onDateConfirm" @cancel="onDateClose" @close="onDateClose"></up-datetime-picker>
... ... @@ -646,31 +657,51 @@ export default {
646 657 * 7/8/9/11 的值是数组或对象,async-validator 的 required 按默认 string 类型判定,
647 658 * 会放过「空数组」「null」⇒ 必须用 validator 显式判空,否则未选也能提交
648 659 */
649   - buildRequiredRule(field: any) {
650   - const type = field.fieldType
651   - if (type === 6 || type === 7 || type === 8) {
652   - const action = type === 6 ? '请选择' : '请上传'
653   - return {
654   - required: true,
655   - trigger: 'change',
656   - validator: (rule, value, callback) => {
657   - Array.isArray(value) && value.length > 0 ? callback() : callback(new Error(`${action}${field.fieldName}`))
658   - }
659   - }
  660 +buildRequiredRule(field: any) {
  661 + const type = field.fieldType
  662 + if (type === 6 || type === 7 || type === 8) {
  663 + const action = type === 6 ? '请选择' : '请上传'
  664 + return {
  665 + required: true,
  666 + trigger: ['change','blur'],
  667 + validator: (rule, value, callback) => {
  668 + Array.isArray(value) && value.length > 0 ? callback() : callback(new Error(`${action}${field.fieldName}`))
660 669 }
661   - if (type === 9 || type === 11) {
662   - return {
663   - required: true,
664   - trigger: 'change',
665   - validator: (rule, value, callback) => {
666   - value ? callback() : callback(new Error(type === 9 ? `请选择${field.fieldName}` : `请选择${field.fieldName}的位置`))
667   - }
  670 + }
  671 + }
  672 + if (type === 9 || type === 11) {
  673 + return {
  674 + required: true,
  675 + trigger: ['change','blur'],
  676 + validator: (rule, value, callback) => {
  677 + value ? callback() : callback(new Error(type === 9 ? `请选择${field.fieldName}` : `请选择${field.fieldName}的位置`))
  678 + }
  679 + }
  680 + }
  681 + // ========= 新增:数值类型单独校验 =========
  682 + if (type === 1) {
  683 + return {
  684 + required: true,
  685 + trigger: ['change','blur'],
  686 + validator: (rule, value, callback) => {
  687 + // 空字符串、null、undefined 才是空;数字 1,7,100 全部合法
  688 + if (value === '' || value === null || value === undefined) {
  689 + callback(new Error(`请填写${field.fieldName}`))
  690 + } else {
  691 + callback()
668 692 }
669 693 }
670   - // 1数值/2文本/4日期/5布尔/10链接 为字符串;3枚举/6单选为选择项
671   - const action = type === 3 || type === 5 ? '请选择' : '请填写'
672   - return { required: true, message: `${action}${field.fieldName}`, trigger: 'change' }
673   - },
  694 + }
  695 + }
  696 + // 2文本 /4日期 /5布尔 /10链接
  697 + const action = type === 3 || type === 5 ? '请选择' : '请填写'
  698 + return {
  699 + required: true,
  700 + message: `${action}${field.fieldName}`,
  701 + trigger: ['change','blur']
  702 + }
  703 +},
  704 +
674 705 /** 幂等流水号(后端按 userId+clientSeq 去重,命中则返回已存在的 recordId) */
675 706 genClientSeq() {
676 707 return `${Date.now()}-${Math.random().toString(16).slice(2, 10)}`
... ... @@ -910,6 +941,12 @@ this.$refs.inspectFormRef.validateField(code)
910 941 * 重试安全:commitId 一旦取到就缓存住,重试只补写 ②(② 自身按 clientSeq 幂等),避免重复创建养护记录
911 942 */
912 943 async submitInspect() {
  944 + // 调试打印,打开小程序控制台查看
  945 + console.log('=====调试=====')
  946 + console.log('dynamicFields', this.dynamicFields)
  947 + this.dynamicFields.forEach(item=>{
  948 + console.log(item.fieldCode, this.inspectForm[item.fieldCode], typeof this.inspectForm[item.fieldCode])
  949 + })
913 950 try {
914 951 await this.$refs.inspectFormRef.validate()
915 952 const dynamicValues = this.buildDynamicFields()
... ... @@ -1068,4 +1105,13 @@ this.$refs.inspectFormRef.validateField(code)
1068 1105 }
1069 1106 }
1070 1107 }
  1108 +
  1109 +.empty-field-tip {
  1110 + padding: 80rpx 30rpx;
  1111 + text-align: center;
  1112 + font-size: 30rpx;
  1113 + color: #999;
  1114 +}
  1115 +
  1116 +
1071 1117 </style>
... ...
pages-sub/daily/maintain-manage/finish-plan-detail.vue
... ... @@ -95,7 +95,7 @@
95 95 title="提交人"
96 96 :value="i.userName || '--'"
97 97 align="middle"
98   - :border="false"
  98 +
99 99 ></up-cell>
100 100 </up-cell-group>
101 101  
... ... @@ -105,9 +105,12 @@
105 105 :border="false"
106 106 inset
107 107 title=""
108   - style="margin-top: 16rpx;"
  108 +
109 109 >
110   - <up-cell v-for="(f, fi) in i.fieldRows" :key="fi" :title="f.name" align="middle">
  110 + <up-cell v-for="(f, fi) in i.fieldRows" :key="fi" align="middle">
  111 + <template #title>
  112 + <view class="field-cell-title-multi">{{f.name}}</view>
  113 + </template>
111 114 <template #value>
112 115 <!-- 7 图片:复用本页照片的相册组件(预览/尺寸一致) -->
113 116 <up-album
... ... @@ -616,4 +619,8 @@ onLoad((options) =&gt; {
616 619 font-size: 26rpx;
617 620 word-break: break-all;
618 621 }
  622 +:deep(.field-cell-title-multi) {
  623 + min-width: 160rpx; // 固定左侧标题最大宽度,文字会在这个宽度内自动换行
  624 +}
  625 +
619 626 </style>
620 627 \ No newline at end of file
... ...
pages-sub/problem/regional-order-manage/list-detail.vue
... ... @@ -96,8 +96,8 @@
96 96 <up-album
97 97 v-if="!!orderDetail.problemsImgs?.length"
98 98 :urls="orderDetail.problemsImgs.slice(0, 3)"
99   - :singleSize="70"
100   - :multipleSize="70"
  99 + singleSize="70"
  100 + multipleSize="70"
101 101 :preview-full-image="true"
102 102 ></up-album>
103 103 <text v-else class="empty-text">暂无问题照片</text>
... ...
pages/index/index.vue
... ... @@ -8,27 +8,25 @@
8 8 <view class="login-desc">蓟城山水全域智能运营管理平台</view>
9 9 </view>
10 10 </view>
11   - <view class="msg-icon" @click="handleMsgClick" hover-class="msg-icon--hover">
  11 + <!-- <view class="msg-icon" @click="handleMsgClick" hover-class="msg-icon--hover">
12 12 <up-icon name="chat" color="#fff" size="24" />
13 13 <view class="msg-badge">
14   - <up-badge type="error" showZero="true" max="999" :value="msgCount"></up-badge>
  14 + <up-badge type="error" show-zero max="999" :value="msgCount"></up-badge>
15 15 </view>
16   - </view>
  16 + </view> -->
17 17 </view>
18   -
19 18 <view class="content-wrap">
20 19 <!-- 图表区域 增加平滑动画容器 -->
21 20 <view class="chart-wrap" v-show="showChartArea">
22 21 <!-- 任务完成情况标题 -->
23 22 <view class="module-title">任务完成情况</view>
24   -
25 23 <!-- 任务完成情况卡片 -->
26 24 <view class="task-chart-card">
27 25 <view class="card-header">
28 26 <view class="unit-tip">数字指标</view>
29 27 <!-- ActionSheet 点击唤起 -->
30 28 <view class="sort-trigger" @click="openActionSheet">
31   - {{ sortLabel }}
  29 + <view class="sort-text">{{ sortLabel }}</view>
32 30 <up-icon name="arrow-down" size="14" style="margin-left:4rpx;"></up-icon>
33 31 </view>
34 32 <!-- ActionSheet组件 -->
... ... @@ -40,14 +38,11 @@
40 38 @popup-hide="handlePopupHide" />
41 39 </view>
42 40 </view>
43   -
44   -
45 41 <!-- 双折线K线图 -->
46 42 <!-- <view class="chart-container">
47 43 <uni-charts v-if="!isDatePickerOpen" type="line" :data="klineChartData" :categories="klineCategories"
48 44 :option="klineOption" :width="chartWidth" :height="chartHeight" />
49 45 </view> -->
50   -
51 46 <up-grid :border="false" col="4">
52 47 <up-grid-item v-for="(baseListItem, baseListIndex) in baseList" :key="baseListIndex">
53 48 <view class="grid-item-icon-box">
... ... @@ -57,14 +52,30 @@
57 52 </view>
58 53 </up-grid-item>
59 54 </up-grid>
60   -
61 55 </view>
62 56 </view>
63   -
64 57 <view class="con-wrap">
65   - <view class="con-title">养护任务 </view>
66   -
67   - <!-- 手写Tab切换:胶囊分段样式 -->
  58 + <!-- ========= 一级大Tab【养护任务 / 巡检任务 / 工单】权限控制 ========= -->
  59 + <view class="main-tab-wrap">
  60 + <view
  61 + v-if="showMainTabYh"
  62 + class="main-tab-item"
  63 + :class="{active: mainTab === 'maintain'}"
  64 + @click="handleMainTabClick('maintain')"
  65 + >养护任务</view>
  66 + <view
  67 + v-if="showMainTabXj"
  68 + class="main-tab-item"
  69 + :class="{active: mainTab === 'inspect'}"
  70 + @click="handleMainTabClick('inspect')"
  71 + >巡检任务</view>
  72 + <view
  73 + class="main-tab-item"
  74 + :class="{active: mainTab === 'workorder'}"
  75 + @click="handleMainTabClick('workorder')"
  76 + >工单</view>
  77 + </view>
  78 + <!-- 手写Tab切换:胶囊分段样式(待办/已办)【原有样式完全保留不动】 -->
68 79 <view class="custom-tab-wrap">
69 80 <view class="tab-capsule" :class="{ active: currentTabValue === 0 }" @click="handleTabClick(0)">
70 81 待办事项({{ todoTotal }})
... ... @@ -73,38 +84,27 @@
73 84 已办事项({{ doneTotal }})
74 85 </view>
75 86 </view>
76   -
77   -
78   - <!-- 动态组信息文案 -->
79   - <!-- <view class="tip-top" style="padding: 8px 0 0 70px;">
80   - <text>{{ tipText }}</text>
81   - </view> -->
82   -
83 87 <!-- 顶部进度提示栏 -->
84 88 <view v-if="showTipHeader" class="tip-header">
85 89 <up-icon name="checkmark-circle" color="#00b42a" size="20"></up-icon>
86 90 <text style="color: #1677ff;">今日组内:您已经完成{{ finishNum }}项,小组均值{{ avgNum }}项</text>
87 91 </view>
88   -
89   -
90   - <scroll-view class="task-list-container" scroll-y @scroll="onListScroll" @scrolltoupper="handleTouchTop"
91   - enhanced>
  92 + <scroll-view class="task-list-container" scroll-y @scroll="onListScroll" @scrolltoupper="handleTouchTop" enhanced>
92 93 <view v-if="taskList.length === 0" class="empty-box">
93   - <up-empty />
  94 + <up-empty></up-empty>
94 95 </view>
95 96 <view class="task-item" v-for="(item, index) in taskList" :key="`${item.taskName}_${index}`"
96 97 hover-class="task-item--hover" @click="handleTaskClick(item)">
97 98 <view class="task-item__content">
98 99 <view class="name-fixed-wrap">
99   - <view class="task-item__name ">
  100 + <view class="task-item__name">
100 101 事项{{ index + 1 }}
101 102 </view>
102 103 <view class="task-name-text">
103 104 {{ item.taskName || '无' }}
104 105 </view>
105 106 </view>
106   -
107   - <view v-if="currentTabValue === 0" class="task-item__footer" style="padding-left: 60px;">
  107 + <view v-if="currentTabValue === 0" class="task-item__footer" style="padding-left: 60rpx;">
108 108 <image class="loc-icon" src="https://img.jichengshanshui.com.cn:28207/appimg/定位icon@3x.png"
109 109 mode="aspectFit"></image>
110 110 <template v-if="isWalkTextMode">
... ... @@ -129,7 +129,7 @@ import { ref, computed, nextTick, watch } from &#39;vue&#39;;
129 129 import { onShow, onUnload } from '@dcloudio/uni-app';
130 130 import { useUserStore } from '@/pinia/user';
131 131 import { timeFormat } from '@/uni_modules/uview-plus';
132   -import uniCharts from '@/components/uni-charts/uni-charts.vue';
  132 +// import uniCharts from '@/components/uni-charts/uni-charts.vue';
133 133 import { getUnreadCount } from "@/api/user";
134 134 import {
135 135 getTaskCompletionSummary,
... ... @@ -189,6 +189,59 @@ const isWalkTextMode = computed(() =&gt; currentGroup.value.useWalkText);
189 189 const useDistanceApi = computed(() => currentGroup.value.useWalkText);
190 190 // =========================================================================
191 191  
  192 +// ========== 一级Tab:养护/巡检/工单 =========
  193 +// 默认期望打开 maintain,watch权限自动修正
  194 +const mainTab = ref<'maintain' | 'inspect' | 'workorder'>('maintain');
  195 +
  196 +// ✅ 修复:用户角色数组
  197 +const userRoles = computed<string[]>(() => userStore.userInfo?.roles || []);
  198 +console.log('当前用户角色数组:', userRoles.value)
  199 +
  200 +// 权限控制角色列表:包含这些角色,则隐藏对应tab
  201 +const hideYhRoleList = ['patrol_leader', 'patrol_global', 'yl_inspector', 'hjws_inspector', 'zxgl_inspector'];
  202 +const hideXjRoleList = ['zxgl_worker', 'team_leader_hjws', 'hjws_worker', 'team_leader_wy', 'wy_worker', 'yl_worker', 'team_leader_yl'];
  203 +
  204 +// ✅ 修复:用户角色数组 和 黑名单数组 存在交集 → 隐藏养护tab
  205 +const showMainTabYh = computed(() => {
  206 + // 判断:用户角色里是否存在任意一个在hideYhRoleList中的角色
  207 + const hasHideRole = userRoles.value.some(role => hideYhRoleList.includes(role));
  208 + return !hasHideRole;
  209 +});
  210 +// ✅ 修复:巡检tab权限
  211 +const showMainTabXj = computed(() => {
  212 + const hasHideRole = userRoles.value.some(role => hideXjRoleList.includes(role));
  213 + return !hasHideRole;
  214 +});
  215 +
  216 +// 获取可见tab数组,用于自动选中
  217 +const availableTabList = computed(() => {
  218 + const list: Array<'maintain' | 'inspect' | 'workorder'> = [];
  219 + if (showMainTabYh.value) list.push('maintain');
  220 + if (showMainTabXj.value) list.push('inspect');
  221 + list.push('workorder');
  222 + return list;
  223 +});
  224 +
  225 +// 监听权限变化:如果当前选中tab不可见,自动切到第一个可用tab
  226 +watch([showMainTabYh, showMainTabXj], () => {
  227 + const available = availableTabList.value;
  228 + console.log('可用tab列表', available);
  229 + if (!available.includes(mainTab.value)) {
  230 + mainTab.value = available[0];
  231 + console.log(`当前角色无【养护任务】权限,自动切换至:${mainTab.value}`)
  232 + }
  233 +}, { immediate: true });
  234 +
  235 +// 一级Tab切换事件
  236 +const handleMainTabClick = async (tabKey: 'maintain' | 'inspect' | 'workorder') => {
  237 + if (mainTab.value === tabKey) return;
  238 + mainTab.value = tabKey;
  239 + currentTabValue.value = 0; // 切大tab默认回到待办
  240 + showChartArea.value = true;
  241 + // 重新拉取对应类型任务
  242 + await loadAllTaskByDept();
  243 +};
  244 +
192 245 const finishNum = ref(0);
193 246 const avgNum = ref(0);
194 247 const msgCount = ref(0);
... ... @@ -205,7 +258,6 @@ const handlePopupShow = () =&gt; {
205 258 const handlePopupHide = () => {
206 259 isDatePickerOpen.value = false;
207 260 };
208   -
209 261 const todoTotal = ref(0);
210 262 const doneTotal = ref(0);
211 263 const todoFullList = ref<any[]>([]);
... ... @@ -217,7 +269,6 @@ const chooseOptions = computed(() =&gt; [
217 269 { title: `待办事项(${todoTotal.value})`, id: 'todo' },
218 270 { title: `已办事项(${doneTotal.value})`, id: 'done' },
219 271 ]);
220   -
221 272 const currentLon = ref<string>('');
222 273 const currentLat = ref<string>('');
223 274 const chartWidth = ref(0);
... ... @@ -264,12 +315,10 @@ ${params[1].seriesName}: ${params[1].value}`;
264 315 textStyle: { fontSize: 12 }
265 316 }
266 317 });
267   -
268   -const baseList = ref<Array<{ num: string; title: string }>>([]);
  318 +const baseList = ref<Array<{ num: string; title: string; name: string }>>([]);
269 319 const orgOptions = ref<any[]>([]);
270 320 const value1 = ref<number | string>('');
271 321 const showActionSheet = ref(false);
272   -
273 322 watch(orgOptions, async (list) => {
274 323 if (list.length && !value1.value) {
275 324 value1.value = list[0].value;
... ... @@ -279,7 +328,6 @@ watch(orgOptions, async (list) =&gt; {
279 328 }
280 329 }
281 330 }, { immediate: true });
282   -
283 331 const actionSheetOptions = computed(() => {
284 332 return orgOptions.value.map(opt => ({
285 333 name: opt.label,
... ... @@ -302,13 +350,11 @@ const handleSheetSelect = async (item: { name: string; value: number }) =&gt; {
302 350 await fetchOrgIndicator(item.value, dateRange.value[0], dateRange.value[1]);
303 351 }
304 352 };
305   -
306   -const userName = computed(() => userStore.userInfo?.nickname || '用户');
  353 +const userName = computed(() => userStore.userInfo.user.nickname || '用户');
307 354 const rpx2px = (rpx: number) => {
308 355 const systemInfo = uni.getSystemInfoSync();
309 356 return Math.floor(systemInfo.screenWidth / 750 * rpx);
310 357 };
311   -
312 358 // ========== 滚动逻辑 ==========
313 359 const showChartArea = ref(true);
314 360 const scrollHideThreshold = rpx2px(80);
... ... @@ -336,7 +382,6 @@ const handleTabClick = (tabIndex: number) =&gt; {
336 382 currentTabValue.value = tabIndex;
337 383 showChartArea.value = true;
338 384 };
339   -
340 385 // ========== 公共函数:请求 orgTaskIndicator 指标接口 ==========
341 386 const fetchOrgIndicator = async (orgId: number, beginDate: string, endDate: string) => {
342 387 try {
... ... @@ -355,19 +400,16 @@ const fetchOrgIndicator = async (orgId: number, beginDate: string, endDate: stri
355 400 { num: String(res.pendingCnt ?? 0), title: '待办任务数', name: 'clock' },
356 401 { num: String(res.overdueCnt ?? 0), title: '超时未完成', name: 'close-circle' }
357 402 ];
358   -
359 403 } catch (err) {
360 404 console.error('orgTaskIndicator 请求失败:', err);
361 405 }
362 406 };
363   -
364 407 // ========== 初始化日期范围(近7天) ==========
365 408 const initRecent7Days = () => {
366 409 const now = new Date();
367 410 const end = new Date(now);
368   - end.setDate(end.getDate() - 1);
  411 + end.setDate(end.getDate());
369 412 const start = new Date(end);
370   - console.log(start)
371 413 start.setDate(start.getDate() - 6);
372 414 dateRange.value = [
373 415 timeFormat(start, 'yyyy-mm-dd'),
... ... @@ -376,7 +418,6 @@ const initRecent7Days = () =&gt; {
376 418 console.log(dateRange.value)
377 419 initChartData(dateRange.value[0], dateRange.value[1]);
378 420 };
379   -
380 421 const getUnread = async () => {
381 422 try {
382 423 const res = await getUnreadCount();
... ... @@ -385,7 +426,6 @@ const getUnread = async () =&gt; {
385 426 console.error('获取未读消息失败:', error);
386 427 }
387 428 };
388   -
389 429 // getOrg:只拉组织下拉选项,不处理指标
390 430 const getOrg = async () => {
391 431 try {
... ... @@ -396,7 +436,6 @@ const getOrg = async () =&gt; {
396 436 console.error('获取组织下拉失败:', error);
397 437 }
398 438 };
399   -
400 439 const initChartData = async (beginTime: string, endTime: string) => {
401 440 try {
402 441 const postData = { beginTime, endTime };
... ... @@ -408,11 +447,10 @@ const initChartData = async (beginTime: string, endTime: string) =&gt; {
408 447 console.error('图表数据请求失败:', error);
409 448 }
410 449 };
411   -
412   -const loadDistanceApiTask = async (lon: string, lat: string) => {
  450 +const loadDistanceApiTask = async (lon: string, lat: string, taskType: string) => {
413 451 try {
414   - const paramsTodo = { queryType: 1, currentLon: lon, currentLat: lat };
415   - const paramsDone = { queryType: 2, currentLon: lon, currentLat: lat };
  452 + const paramsTodo = { queryType: 1, currentLon: lon, currentLat: lat, taskType };
  453 + const paramsDone = { queryType: 2, currentLon: lon, currentLat: lat, taskType };
416 454 const [todoRes, doneRes] = await Promise.all([
417 455 taskDetailsWithDistance(paramsTodo),
418 456 taskDetailsWithDistance(paramsDone)
... ... @@ -433,11 +471,11 @@ const loadDistanceApiTask = async (lon: string, lat: string) =&gt; {
433 471 doneTotal.value = 0;
434 472 }
435 473 };
436   -const loadSmartSortTask = async (lon: string, lat: string) => {
  474 +const loadSmartSortTask = async (lon: string, lat: string, taskType: string) => {
437 475 try {
438 476 const [todoRes, doneRes] = await Promise.all([
439   - taskDetailsWithSmartSort({ queryType: 1, currentLon: lon, currentLat: lat }),
440   - taskDetailsWithSmartSort({ queryType: 2, currentLon: lon, currentLat: lat })
  477 + taskDetailsWithSmartSort({ queryType: 1, currentLon: lon, currentLat: lat, taskType }),
  478 + taskDetailsWithSmartSort({ queryType: 2, currentLon: lon, currentLat: lat, taskType })
441 479 ]);
442 480 todoFullList.value = todoRes?.sortedTasks || [];
443 481 doneFullList.value = doneRes?.sortedTasks || [];
... ... @@ -459,22 +497,27 @@ const loadAllTaskByDept = async () =&gt; {
459 497 const loc = await getCurrentLocation();
460 498 currentLon.value = loc.lon;
461 499 currentLat.value = loc.lat;
462   - if (useDistanceApi.value) {
463   - await loadDistanceApiTask(loc.lon, loc.lat);
464   - } else {
465   - await loadSmartSortTask(loc.lon, loc.lat);
466   - }
467   -};
468   -
469   -// 日期确认回调:更新图表 + 重新请求指标orgTaskIndicator
470   -const handleDateConfirm = async (e: string[]) => {
471   - dateRange.value = e;
472   - await initChartData(e[0], e[1]);
473   - if (value1.value) {
474   - await fetchOrgIndicator(Number(value1.value), e[0], e[1]);
  500 + // 根据一级Tab选择不同接口
  501 + if (mainTab.value === 'maintain') {
  502 + if (useDistanceApi.value) {
  503 + await loadDistanceApiTask(loc.lon, loc.lat, 'maintain');
  504 + } else {
  505 + await loadSmartSortTask(loc.lon, loc.lat, 'maintain');
  506 + }
  507 + } else if (mainTab.value === 'inspect') {
  508 + if (useDistanceApi.value) {
  509 + await loadDistanceApiTask(loc.lon, loc.lat, 'inspect');
  510 + } else {
  511 + await loadSmartSortTask(loc.lon, loc.lat, 'inspect');
  512 + }
  513 + } else if (mainTab.value === 'workorder') {
  514 + // ========= 工单tab,后续对接工单接口,先置空列表占位 =========
  515 + todoFullList.value = [];
  516 + doneFullList.value = [];
  517 + todoTotal.value = 0;
  518 + doneTotal.value = 0;
475 519 }
476 520 };
477   -
478 521 const getCurrentLocation = (): Promise<{ lon: string; lat: string }> => {
479 522 return new Promise((resolve, reject) => {
480 523 uni.getSetting({
... ... @@ -533,7 +576,6 @@ const getCurrentLocation = (): Promise&lt;{ lon: string; lat: string }&gt; =&gt; {
533 576 });
534 577 });
535 578 };
536   -
537 579 const handleMsgClick = () => {
538 580 uni.navigateTo({ url: '/pages-sub/msg/msg' });
539 581 };
... ... @@ -561,39 +603,34 @@ const handleTaskClick = (item: any) =&gt; {
561 603 }
562 604 }
563 605 };
564   -
565 606 onShow(async () => {
566 607 showChartArea.value = true;
567 608 chartWidth.value = rpx2px(750 - 60);
568 609 chartHeight.value = rpx2px(300);
569 610 initRecent7Days();
570   - getUnread();
  611 + // getUnread();
571 612 await getOrg(); // 拉取组织下拉列表
572 613 await loadAllTaskByDept();
573 614 });
574   -
575 615 onUnload(() => {
576 616 if (scrollTimer) clearTimeout(scrollTimer);
577 617 });
578 618 </script>
579 619  
580   -
581   -
582 620 <style scoped lang="scss">
583 621 $primary-color: #1677ff;
584 622 $danger-color: #E53935;
585 623 $text-color: #333;
586 624 $text-color-light: #666;
587   -$text-color-placeholder: #999;
  625 +$text-placeholder: #999;
588 626 $bg-color: #f5f5f7;
589 627 $card-bg: #fff;
590   -$border-radius: 16px;
591   -$card-radius: 5px;
592   -$spacing-sm: 5px;
593   -$spacing-md: 10px;
594   -$spacing-lg: 15px;
  628 +$border-radius: 16rpx;
  629 +$card-radius: 5rpx;
  630 +$spacing-sm: 5rpx;
  631 +$spacing-md: 10rpx;
  632 +$spacing-lg: 15rpx;
595 633 $border-color: #e5e5e5;
596   -
597 634 .page-container {
598 635 height: 100vh;
599 636 background-color: $bg-color;
... ... @@ -601,128 +638,134 @@ $border-color: #e5e5e5;
601 638 display: flex;
602 639 flex-direction: column;
603 640 }
604   -
605 641 .user-info-bar {
606 642 flex-shrink: 0;
607 643 background: url("https://img.jichengshanshui.com.cn:28207/appimg/bg.jpg") no-repeat;
608 644 background-size: 100% 100%;
609   - padding: 80px $spacing-lg 135px;
  645 + padding: 140rpx $spacing-lg 165rpx;
610 646 display: flex;
611 647 justify-content: space-between;
612 648 align-items: center;
613 649 color: #fff;
614   -
615 650 .username {
616   - font-size: 16px;
617   - margin-bottom: 4px;
  651 + font-size: 32rpx;
  652 + margin-bottom: 4rpx;
618 653 }
619   -
620 654 .login-desc {
621   - font-size: 16px;
  655 + font-size: 28rpx;
622 656 }
623   -
624 657 .msg-icon {
625 658 position: relative;
626   - padding: 5px;
627   - top: 10px;
  659 + padding: 5rpx;
  660 + top: 10rpx;
628 661 border-radius: 50%;
629   -
630 662 &--hover {
631 663 background-color: rgba(255, 255, 255, 0.2);
632 664 }
633   -
634 665 .msg-badge {
635 666 position: absolute;
636   - top: -2px;
637   - right: -3px;
  667 + top: -2rpx;
  668 + right: -3rpx;
638 669 }
639 670 }
640 671 }
641   -
642 672 .content-wrap {
643 673 flex: 1;
644   - margin-top: -122px;
  674 + margin-top: -122rpx;
645 675 border-radius: $border-radius $border-radius 0 0;
646 676 background-color: $bg-color;
647 677 overflow: hidden;
648 678 display: flex;
649 679 flex-direction: column;
650 680 }
651   -
652 681 .chart-wrap {
653 682 flex-shrink: 0;
654 683 transition: all 0.35s ease-out;
655 684 opacity: 1;
656 685 overflow: hidden;
  686 + &[hidden] {
  687 + opacity: 0;
  688 + height: 0;
  689 + margin: 0;
  690 + }
657 691 }
658   -
659   -.chart-wrap[hidden] {
660   - opacity: 0;
661   - height: 0;
662   - margin: 0;
663   -}
664   -
665 692 .module-title {
666 693 padding: $spacing-lg;
667   - font-size: 14px;
  694 + font-size: 28rpx;
668 695 font-weight: 600;
669 696 color: $text-color-light;
670 697 flex-shrink: 0;
671 698 }
672   -
673 699 .task-chart-card {
674 700 background-color: $card-bg;
675 701 border-radius: $card-radius;
676 702 margin: 0 $spacing-lg $spacing-md;
677 703 padding: $spacing-md;
678 704 flex-shrink: 0;
679   -
680 705 .card-header {
681 706 display: flex;
682 707 justify-content: space-between;
683 708 align-items: center;
684   - font-size: 12px;
685   - color: $text-color-placeholder;
  709 + font-size: 24rpx;
  710 + color: $text-placeholder;
686 711 margin-bottom: $spacing-md;
687   -
688 712 .date-picker-wrap {
689   - border-radius: 3px;
  713 + border-radius: 3rpx;
690 714 transition: background-color 0.2s;
691 715 position: relative;
692 716 z-index: 999;
693   -
694 717 &:active {
695 718 background-color: $bg-color;
696 719 }
697 720 }
698 721 }
699   -
700 722 .chart-container {
701 723 width: 100%;
702   - height: 150px;
  724 + height: 150rpx;
703 725 display: flex;
704 726 align-items: center;
705 727 justify-content: center;
706 728 }
707 729 }
708   -
709 730 .con-wrap {
710   - border-radius: 8px;
  731 + border-radius: 8rpx;
711 732 background-color: #fff;
712   - margin: 0 15px;
  733 + margin: 0 15rpx;
713 734 flex: 1;
714 735 min-height: 0;
715 736 display: flex;
716 737 flex-direction: column;
717 738 overflow: hidden;
718 739 }
719   -
720   -.con-title {
721   - padding: 10px 15px;
722   - border-bottom: 1px solid #f0e9e9;
723   - flex-shrink: 0;
  740 +// ========= 【仅一级Tab:养护/巡检/工单】下划线样式,其余全部保留原有css =========
  741 +.main-tab-wrap {
  742 + display: flex;
  743 + align-items: center;
  744 + background: #fff;
  745 + padding: 0 30rpx;
724 746 }
725   -
  747 +.main-tab-item {
  748 + padding: 24rpx 16rpx;
  749 + font-size: 30rpx;
  750 + color: #666;
  751 + position: relative;
  752 + transition: color 0.24s;
  753 + &.active {
  754 + color: #1677ff;
  755 + font-weight: 500;
  756 + &::after {
  757 + content: '';
  758 + position: absolute;
  759 + left: 0;
  760 + bottom: 0;
  761 + width: 100%;
  762 + height: 4rpx;
  763 + background-color: #1677ff;
  764 + border-radius: 2rpx;
  765 + }
  766 + }
  767 +}
  768 +// 待办/已办胶囊tab【原样保留你原来的样式,完全没改动!!】
726 769 .custom-tab-wrap {
727 770 display: flex;
728 771 align-items: center;
... ... @@ -731,88 +774,76 @@ $border-color: #e5e5e5;
731 774 background: #fff;
732 775 flex-shrink: 0;
733 776 }
734   -
735 777 .tab-capsule {
736 778 padding: 10rpx 20rpx;
737   - display: flex;
738   - align-items: center;
739   - justify-content: center;
740 779 font-size: 28rpx;
741 780 color: #999;
742 781 background: #f5f5f5;
743 782 border-radius: 0;
744 783 transition: all 0.2s ease;
745 784 font-weight: 500;
  785 + &.active {
  786 + color: #d4af37;
  787 + background: #fff8dc;
  788 + }
746 789 }
747   -
748   -.tab-capsule.active {
749   - color: #d4af37;
750   - background: #fff8dc;
751   -}
752   -
753 790 .tip-header {
754 791 display: flex;
755 792 align-items: center;
756 793 gap: 8rpx;
757 794 padding: 12rpx 15rpx;
758 795 background: #e6f7ff;
759   - font-size: 14px;
  796 + font-size: 28rpx;
760 797 color: #000;
761 798 flex-shrink: 0;
762 799 }
763   -
764 800 .tip-top {
765   - padding: 8px 0 0 70px;
  801 + padding: 8rpx 0 0 70rpx;
766 802 flex-shrink: 0;
767 803 }
768   -
769 804 .task-list-container {
770 805 width: 100%;
771 806 height: 100%;
772 807 padding-bottom: 300rpx;
773 808 box-sizing: border-box;
774 809 }
775   -
776 810 .empty-box {
777 811 padding: 60rpx 0;
778 812 }
779   -
780 813 .sort-trigger {
781 814 display: flex;
782 815 align-items: center;
783   - font-size: 12px;
784   - color: $text-color-placeholder;
  816 + font-size: 24rpx;
  817 + color: $text-placeholder;
  818 + .sort-text {
  819 + white-space: nowrap;
  820 + overflow: hidden;
  821 + text-overflow: ellipsis;
  822 + max-width: 220rpx;
  823 + }
785 824 }
786   -
787   -
788 825 .task-item {
789 826 padding: $spacing-md;
790 827 transition: background-color 0.2s;
791   - border-bottom: 1px solid $border-color;
792   -
  828 + border-bottom: 1rpx solid $border-color;
793 829 &:last-child {
794 830 border-bottom: none;
795 831 }
796   -
797 832 &--hover {
798 833 background-color: $bg-color;
799 834 }
800   -
801 835 &__content {
802 836 width: 100%;
803 837 }
804   -
805 838 .name-fixed-wrap {
806 839 display: flex;
807 840 }
808   -
809 841 .task-item__name {
810   - width: 60px;
  842 + width: 80rpx;
811 843 }
812   -
813 844 .task-name-text {
814 845 flex: 1;
815   - font-size: 14px;
  846 + font-size: 24rpx;
816 847 color: $text-color;
817 848 display: -webkit-box;
818 849 -webkit-line-clamp: 2;
... ... @@ -820,49 +851,43 @@ $border-color: #e5e5e5;
820 851 overflow: hidden;
821 852 text-overflow: ellipsis;
822 853 }
823   -
824 854 &__name {
825   - font-size: 15px;
  855 + font-size: 24rpx;
826 856 color: $text-color;
827 857 display: inline-block;
828 858 }
829   -
830 859 &__footer {
831 860 display: flex;
832 861 align-items: flex-start;
833 862 gap: 6rpx;
834   - font-size: 13px;
  863 + font-size: 24rpx;
835 864 color: $text-color-light;
836 865 margin-top: $spacing-sm;
837 866 align-items: center;
838 867 }
839 868 }
840   -
841 869 .loc-icon {
842   - width: 20px;
843   - height: 20px;
  870 + width: 28rpx;
  871 + height: 28rpx;
844 872 }
845   -
846 873 .urgency-tag {
847 874 color: #7D7D7D;
848 875 }
849   -
850 876 .grid-item-icon-box {
851   - display:flex;
852   - flex-direction:column;
853   - align-items:center;
854   - padding:16rpx;
  877 + display: flex;
  878 + flex-direction: column;
  879 + align-items: center;
  880 + padding: 16rpx;
855 881 }
856 882 .grid-num-icon {
857   - font-size:40rpx;
858   - font-weight:600;
859   - color:#333;
860   - margin-top:8rpx;
  883 + font-size: 40rpx;
  884 + font-weight: 600;
  885 + color: #333;
  886 + margin-top: 8rpx;
861 887 }
862 888 .grid-label-icon {
863   - font-size:22rpx;
864   - color:#666;
865   - margin-top:4rpx;
  889 + font-size: 22rpx;
  890 + color: #666;
  891 + margin-top: 4rpx;
866 892 }
867   -
868   -</style>
869 893 \ No newline at end of file
  894 +</style>
... ...