Commit 1d3624b054a22e7c4b0ca549d9c5d880e1ee4d5e

Authored by 刘淇
1 parent ecbfbe06

首页。养护任务

api/index/index.js
@@ -30,5 +30,23 @@ export const iWorkOrderSummary = (params) => { @@ -30,5 +30,23 @@ export const iWorkOrderSummary = (params) => {
30 return post('/app-api/homePage/summary/iWorkOrderSummary', params); 30 return post('/app-api/homePage/summary/iWorkOrderSummary', params);
31 }; 31 };
32 32
  33 +/**
  34 + * 距离待办已办
  35 + * @param {Object} params {mobile, password, code}
  36 + * @returns {Promise}
  37 + */
  38 +export const taskDetailsWithDistance = (params) => {
  39 + return post('/app-api/homePage/summary/taskDetailsWithDistance', params);
  40 +};
  41 +
  42 +/**
  43 + * 效率待办已办
  44 + * @param {Object} params {mobile, password, code}
  45 + * @returns {Promise}
  46 + */
  47 +export const taskDetailsWithSmartSort = (params) => {
  48 + return post('/app-api/homePage/summary/taskDetailsWithSmartSort', params);
  49 +};
  50 +
33 51
34 52
common/config/env.js
@@ -4,7 +4,8 @@ const env = { @@ -4,7 +4,8 @@ const env = {
4 development: { 4 development: {
5 baseUrl: 'https://test.jichengshanshui.com.cn:28302', 5 baseUrl: 'https://test.jichengshanshui.com.cn:28302',
6 // https://test.jichengshanshui.com.cn:28302 6 // https://test.jichengshanshui.com.cn:28302
7 - // http://h95ce3aa.natappfree.cc 7 +
  8 + // baseUrl: 'http://192.168.1.186:48081',
8 9
9 uploadUrl: 'https://test.jichengshanshui.com.cn:28302', 10 uploadUrl: 'https://test.jichengshanshui.com.cn:28302',
10 // baseApi:'admin-api', 11 // baseApi:'admin-api',
pages-sub/data/tree-archive/addTree.vue
@@ -5,11 +5,22 @@ @@ -5,11 +5,22 @@
5 <up-input v-model.trim="formData.treetype" placeholder="请输入名称" maxlength="30" border="none"/> 5 <up-input v-model.trim="formData.treetype" placeholder="请输入名称" maxlength="30" border="none"/>
6 </up-form-item> 6 </up-form-item>
7 7
  8 + <!-- 树种类型 底部弹窗选择 -->
  9 + <!-- <up-form-item label="树种类型" prop="treeSpeciesType" required arrow @click="openTreeSpeciesType">
  10 + <up-input
  11 + v-model="formData.treeSpeciesTypeName"
  12 + placeholder="请选择树种类型"
  13 + readonly
  14 + border="none"
  15 + bg-color="transparent"
  16 + />
  17 + </up-form-item> -->
  18 +
8 <view class="form-row-wrap"> 19 <view class="form-row-wrap">
9 <up-row gutter="10"> 20 <up-row gutter="10">
10 <up-col span="6"> 21 <up-col span="6">
11 <up-form-item label="胸径" prop="dbh" required> 22 <up-form-item label="胸径" prop="dbh" required>
12 - <up-input v-model.trim="formData.dbh" placeholder="请输入" maxlength="10" border="none" input-align="left"/> 23 + <up-input v-model.trim="formData.dbh" placeholder="请输入" maxlength="10" border="none" input-align="left"/>
13 <template #right> 24 <template #right>
14 <text style="padding-left: 12rpx;color:#ccc;font-size:14px">厘米</text> 25 <text style="padding-left: 12rpx;color:#ccc;font-size:14px">厘米</text>
15 </template> 26 </template>
@@ -17,7 +28,7 @@ @@ -17,7 +28,7 @@
17 </up-col> 28 </up-col>
18 <up-col span="6"> 29 <up-col span="6">
19 <up-form-item label="高度" prop="treeheight"> 30 <up-form-item label="高度" prop="treeheight">
20 - <up-input v-model.trim="formData.treeheight" placeholder="请输入" maxlength="10" border="none" 31 + <up-input v-model.trim="formData.treeheight" placeholder="请输入" maxlength="10" border="none"
21 input-align="left"/> 32 input-align="left"/>
22 <template #right> 33 <template #right>
23 <text style="padding-left: 12rpx;color:#ccc;font-size:14px;">米</text> 34 <text style="padding-left: 12rpx;color:#ccc;font-size:14px;">米</text>
@@ -65,7 +76,7 @@ @@ -65,7 +76,7 @@
65 :width="70" 76 :width="70"
66 :height="70" 77 :height="70"
67 :max-count="treeImgs.uploadConfig.maxCount" 78 :max-count="treeImgs.uploadConfig.maxCount"
68 - :upload-text="treeImgs.uploadConfig.uploadText" 79 + :upload-text="treeImgs.uploadText"
69 :size-type="treeImgs.uploadConfig.sizeType" 80 :size-type="treeImgs.uploadConfig.sizeType"
70 ></up-upload> 81 ></up-upload>
71 </up-form-item> 82 </up-form-item>
@@ -182,11 +193,13 @@ @@ -182,11 +193,13 @@
182 import { timeFormat } from '@/uni_modules/uview-plus'; 193 import { timeFormat } from '@/uni_modules/uview-plus';
183 import { ref, reactive, nextTick } from 'vue' 194 import { ref, reactive, nextTick } from 'vue'
184 import { onReady, onLoad, onShow, onUnload } from '@dcloudio/uni-app'; 195 import { onReady, onLoad, onShow, onUnload } from '@dcloudio/uni-app';
  196 +
185 onUnload(() => { 197 onUnload(() => {
186 uni.reLaunch({ 198 uni.reLaunch({
187 url: '/pages-sub/data/tree-archive/index' 199 url: '/pages-sub/data/tree-archive/index'
188 }) 200 })
189 }) 201 })
  202 +
190 import { addTree } from "@/api/tree-archive/tree-archive.js"; 203 import { addTree } from "@/api/tree-archive/tree-archive.js";
191 import { useUploadImgs } from '@/common/utils/useUploadImgs' 204 import { useUploadImgs } from '@/common/utils/useUploadImgs'
192 import { useUserStore } from '@/pinia/user'; 205 import { useUserStore } from '@/pinia/user';
@@ -224,10 +237,10 @@ const isShow = ref(false) @@ -224,10 +237,10 @@ const isShow = ref(false)
224 const contentHeight = ref(200) 237 const contentHeight = ref(200)
225 const treeOwnershipData = ref([]) 238 const treeOwnershipData = ref([])
226 const treeLevelData = ref([]) 239 const treeLevelData = ref([])
  240 +const treeSpeciesTypeList = ref([])
227 const loadingFlag = ref(false) 241 const loadingFlag = ref(false)
228 const showActionSheet = ref(false); 242 const showActionSheet = ref(false);
229 const currentActionSheetData = reactive({ type: '', list: [], title: '' }); 243 const currentActionSheetData = reactive({ type: '', list: [], title: '' });
230 -// 年月弹窗开关  
231 const showDatePicker = ref(false); 244 const showDatePicker = ref(false);
232 const plantingDate = ref(Date.now()) 245 const plantingDate = ref(Date.now())
233 246
@@ -241,9 +254,11 @@ const treeImgs = useUploadImgs({ @@ -241,9 +254,11 @@ const treeImgs = useUploadImgs({
241 }) 254 })
242 if (!Array.isArray(treeImgs.rawImgList.value)) treeImgs.rawImgList.value = []; 255 if (!Array.isArray(treeImgs.rawImgList.value)) treeImgs.rawImgList.value = [];
243 256
244 -// 表单数据:estimationtreeage 改为 plantingDate 257 +// 表单数据:treeSpeciesType 存编码,treeSpeciesTypeName 页面展示文字
245 const formData = reactive({ 258 const formData = reactive({
246 treetype: '', 259 treetype: '',
  260 + // treeSpeciesType: '',
  261 + treeSpeciesTypeName: '',
247 treeheight: '', 262 treeheight: '',
248 dbh: '', 263 dbh: '',
249 treelevel: '', 264 treelevel: '',
@@ -252,7 +267,7 @@ const formData = reactive({ @@ -252,7 +267,7 @@ const formData = reactive({
252 oldtreeownership: '', 267 oldtreeownership: '',
253 oldtreeownershipText: '', 268 oldtreeownershipText: '',
254 latinname: '', 269 latinname: '',
255 - plantingDate: '', //栽种年月 yyyy-MM 270 + plantingDate: '',
256 canopysouthnorth: '', 271 canopysouthnorth: '',
257 canopyeastwest: '', 272 canopyeastwest: '',
258 weekday: '', 273 weekday: '',
@@ -266,84 +281,107 @@ const formData = reactive({ @@ -266,84 +281,107 @@ const formData = reactive({
266 maintainunit: '' 281 maintainunit: ''
267 }) 282 })
268 283
269 -// 表单校验规则(移除estimationtreeage,plantingDate非必填) 284 +// 表单校验规则
270 const rules = reactive({ 285 const rules = reactive({
271 treetype: [ 286 treetype: [
272 - {validator: (rule, val, callback) => { 287 + {
  288 + validator: (rule, val, callback) => {
273 if (val && hasEmoji(val)) return callback(new Error('禁止输入表情符号')); 289 if (val && hasEmoji(val)) return callback(new Error('禁止输入表情符号'));
274 callback(); 290 callback();
275 - }, trigger: ['blur','change','input'], priority: 10},  
276 - {required: true, message: '请输入名称', trigger: ['blur','change','input']},  
277 - {max:30, message:'名称最多输入30个字符', trigger: ['blur','change','input']} 291 + }, trigger: ['blur', 'change', 'input'], priority: 10
  292 + },
  293 + { required: true, message: '请输入名称', trigger: ['blur', 'change', 'input'] },
  294 + { max: 30, message: '名称最多输入30个字符', trigger: ['blur', 'change', 'input'] }
278 ], 295 ],
  296 + // treeSpeciesType: [
  297 + // { required: true, message: '请选择树种类型', trigger: ['change'] }
  298 + // ],
279 treeheight: [ 299 treeheight: [
280 - {max: 10, message: '树高不能超过10个字符', trigger: ['blur','change','input']},  
281 - {pattern: /^\d+(\.\d{1,2})?$/, message: '格式不正确,支持数字或两位小数', trigger: ['blur','change','input']},  
282 - {validator: (rule, val, callback) => { 300 + { max: 10, message: '树高不能超过10个字符', trigger: ['blur', 'change', 'input'] },
  301 + { pattern: /^\d+(\.\d{1,2})?$/, message: '格式不正确,支持数字或两位小数', trigger: ['blur', 'change', 'input'] },
  302 + {
  303 + validator: (rule, val, callback) => {
283 if (isPureZero(val)) return callback(new Error('不能输入纯0无效值')); 304 if (isPureZero(val)) return callback(new Error('不能输入纯0无效值'));
284 callback(); 305 callback();
285 - }, trigger: ['blur','change','input']} 306 + }, trigger: ['blur', 'change', 'input']
  307 + }
286 ], 308 ],
287 dbh: [ 309 dbh: [
288 - {validator: (rule, val, callback) => { 310 + {
  311 + validator: (rule, val, callback) => {
289 if (val && hasEmoji(val)) return callback(new Error('禁止输入表情符号')); 312 if (val && hasEmoji(val)) return callback(new Error('禁止输入表情符号'));
290 callback(); 313 callback();
291 - }, trigger: ['blur','change','input'], priority:10},  
292 - {required: true, message: '请输入胸径',trigger: ['blur','change','input']},  
293 - {max: 10, message: '胸径不能超过10个字符', trigger: ['blur','change','input']},  
294 - {pattern: /^\d+(\.\d{1,2})?$/, message: '格式不正确,支持数字或两位小数', trigger: ['blur','change','input']},  
295 - {validator: (rule, val, callback) => { 314 + }, trigger: ['blur', 'change', 'input'], priority: 10
  315 + },
  316 + { required: true, message: '请输入胸径', trigger: ['blur', 'change', 'input'] },
  317 + { max: 10, message: '胸径不能超过10个字符', trigger: ['blur', 'change', 'input'] },
  318 + { pattern: /^\d+(\.\d{1,2})?$/, message: '格式不正确,支持数字或两位小数', trigger: ['blur', 'change', 'input'] },
  319 + {
  320 + validator: (rule, val, callback) => {
296 if (isPureZero(val)) return callback(new Error('不能输入纯0无效值')); 321 if (isPureZero(val)) return callback(new Error('不能输入纯0无效值'));
297 callback(); 322 callback();
298 - }, trigger: ['blur','change','input']} 323 + }, trigger: ['blur', 'change', 'input']
  324 + }
299 ], 325 ],
300 weekday: [ 326 weekday: [
301 - {max: 10, message: '干周不能超过10个字符', trigger: ['blur','change','input']},  
302 - {pattern: /^\d+(\.\d{1,2})?$/, message: '格式不正确,支持数字或两位小数', trigger: ['blur','change','input']},  
303 - {validator: (rule, val, callback) => { 327 + { max: 10, message: '干周不能超过10个字符', trigger: ['blur', 'change', 'input'] },
  328 + { pattern: /^\d+(\.\d{1,2})?$/, message: '格式不正确,支持数字或两位小数', trigger: ['blur', 'change', 'input'] },
  329 + {
  330 + validator: (rule, val, callback) => {
304 if (isPureZero(val)) return callback(new Error('不能输入纯0无效值')); 331 if (isPureZero(val)) return callback(new Error('不能输入纯0无效值'));
305 callback(); 332 callback();
306 - }, trigger: ['blur','change','input']} 333 + }, trigger: ['blur', 'change', 'input']
  334 + }
307 ], 335 ],
308 canopyeastwest: [ 336 canopyeastwest: [
309 - {max: 10, message: '东西冠幅不能超过10个字符',trigger: ['blur','change','input']},  
310 - {pattern: /^\d+(\.\d{1,2})?$/, message: '格式不正确,支持数字或两位小数', trigger: ['blur','change','input']},  
311 - {validator: (rule, val, callback) => { 337 + { max: 10, message: '东西冠幅不能超过10个字符', trigger: ['blur', 'change', 'input'] },
  338 + { pattern: /^\d+(\.\d{1,2})?$/, message: '格式不正确,支持数字或两位小数', trigger: ['blur', 'change', 'input'] },
  339 + {
  340 + validator: (rule, val, callback) => {
312 if (isPureZero(val)) return callback(new Error('不能输入纯0无效值')); 341 if (isPureZero(val)) return callback(new Error('不能输入纯0无效值'));
313 callback(); 342 callback();
314 - }, trigger: ['blur','change','input']} 343 + }, trigger: ['blur', 'change', 'input']
  344 + }
315 ], 345 ],
316 canopysouthnorth: [ 346 canopysouthnorth: [
317 - {max: 10, message: '南北冠幅不能超过10个字符', trigger: ['blur','change','input']},  
318 - {pattern: /^\d+(\.\d{1,2})?$/, message: '格式不正确,支持数字或两位小数', trigger: ['blur','change','input']},  
319 - {validator: (rule, val, callback) => { 347 + { max: 10, message: '南北冠幅不能超过10个字符', trigger: ['blur', 'change', 'input'] },
  348 + { pattern: /^\d+(\.\d{1,2})?$/, message: '格式不正确,支持数字或两位小数', trigger: ['blur', 'change', 'input'] },
  349 + {
  350 + validator: (rule, val, callback) => {
320 if (isPureZero(val)) return callback(new Error('不能输入纯0无效值')); 351 if (isPureZero(val)) return callback(new Error('不能输入纯0无效值'));
321 callback(); 352 callback();
322 - }, trigger: ['blur','change','input']} 353 + }, trigger: ['blur', 'change', 'input']
  354 + }
323 ], 355 ],
324 - growlocation: [{required: true, message: '请选择地图位置', trigger: ['blur','change','manual']}], 356 + growlocation: [{ required: true, message: '请选择地图位置', trigger: ['blur', 'change', 'manual'] }],
325 managedutyunit: [ 357 managedutyunit: [
326 - {validator: (rule, val, callback) => { 358 + {
  359 + validator: (rule, val, callback) => {
327 if (val && hasEmoji(val)) return callback(new Error('禁止输入表情符号')); 360 if (val && hasEmoji(val)) return callback(new Error('禁止输入表情符号'));
328 callback(); 361 callback();
329 - }, trigger: ['blur','change','input'], priority:10},  
330 - {required: true, message: '请输入管护单位', trigger: ['blur','change','input']},  
331 - {max:30, message:'管护单位最多输入30个字符', trigger: ['blur','change','input']} 362 + }, trigger: ['blur', 'change', 'input'], priority: 10
  363 + },
  364 + { required: true, message: '请输入管护单位', trigger: ['blur', 'change', 'input'] },
  365 + { max: 30, message: '管护单位最多输入30个字符', trigger: ['blur', 'change', 'input'] }
332 ], 366 ],
333 - oldtreeownershipText: [{required: true, message: '请选择权属分类', trigger: ['blur','change']}], 367 + oldtreeownershipText: [{ required: true, message: '请选择权属分类', trigger: ['blur', 'change'] }],
334 latinname: [ 368 latinname: [
335 - {validator: (rule, val, callback) => { 369 + {
  370 + validator: (rule, val, callback) => {
336 if (val && hasEmoji(val)) return callback(new Error('禁止输入表情符号')); 371 if (val && hasEmoji(val)) return callback(new Error('禁止输入表情符号'));
337 callback(); 372 callback();
338 - }, trigger: ['blur','change','input'], priority:10},  
339 - {max:30, message:'拉丁文最多输入30个字符', trigger: ['blur','change','input']} 373 + }, trigger: ['blur', 'change', 'input'], priority: 10
  374 + },
  375 + { max: 30, message: '拉丁文最多输入30个字符', trigger: ['blur', 'change', 'input'] }
340 ], 376 ],
341 growthenvironment: [ 377 growthenvironment: [
342 - {validator: (rule, val, callback) => { 378 + {
  379 + validator: (rule, val, callback) => {
343 if (val && hasEmoji(val)) return callback(new Error('禁止输入表情符号')); 380 if (val && hasEmoji(val)) return callback(new Error('禁止输入表情符号'));
344 callback(); 381 callback();
345 - }, trigger: ['blur','change','input'], priority:10},  
346 - {max:50, message:'生长环境最多输入50个字符', trigger: ['blur','change','input']} 382 + }, trigger: ['blur', 'change', 'input'], priority: 10
  383 + },
  384 + { max: 50, message: '生长环境最多输入50个字符', trigger: ['blur', 'change', 'input'] }
347 ], 385 ],
348 treeImgList: [treeImgs.imgValidateRule] 386 treeImgList: [treeImgs.imgValidateRule]
349 }) 387 })
@@ -352,10 +390,13 @@ const rules = reactive({ @@ -352,10 +390,13 @@ const rules = reactive({
352 onLoad((options) => { 390 onLoad((options) => {
353 formData.road = options.roadId 391 formData.road = options.roadId
354 }) 392 })
  393 +
355 onShow(async () => { 394 onShow(async () => {
356 treeLevelData.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('tree_level')) 395 treeLevelData.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('tree_level'))
357 - treeOwnershipData.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('tree_ownership')) 396 + treeOwnershipData.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('tree_ownership'))
  397 + treeSpeciesTypeList.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('tree_species_type'))
358 }) 398 })
  399 +
359 onReady(() => { 400 onReady(() => {
360 nextTick(() => { 401 nextTick(() => {
361 formRef.value?.setRules(rules); 402 formRef.value?.setRules(rules);
@@ -363,22 +404,22 @@ onReady(() =&gt; { @@ -363,22 +404,22 @@ onReady(() =&gt; {
363 }); 404 });
364 405
365 // 打开栽种年月选择器 406 // 打开栽种年月选择器
366 -const openPlantDatePicker = ()=>{ 407 +const openPlantDatePicker = () => {
367 showDatePicker.value = true 408 showDatePicker.value = true
368 } 409 }
369 -// 选中年月回调,自动格式 yyyy-MM  
370 -const onSelectPlantDate = (date)=>{  
371 -console.log(plantingDate.value)  
372 - console.log(timeFormat(plantingDate.value, 'yyyy-mm'))  
373 - // date格式:yyyy-MM  
374 - formData.plantingDate = timeFormat(plantingDate.value, 'yyyy-mm') 410 +
  411 +// 年月选择回调
  412 +const onSelectPlantDate = () => {
  413 + formData.plantingDate = timeFormat(plantingDate.value, 'yyyy-mm')
375 showDatePicker.value = false 414 showDatePicker.value = false
376 } 415 }
377 416
  417 +// 展开/收起更多区域
378 const toggleArea = () => { 418 const toggleArea = () => {
379 isShow.value = !isShow.value 419 isShow.value = !isShow.value
380 } 420 }
381 421
  422 +// 打开权属/级别选择弹窗
382 const handleActionSheetOpen = (type) => { 423 const handleActionSheetOpen = (type) => {
383 const configMap = { 424 const configMap = {
384 ownership: { title: '请选择权属分类', list: treeOwnershipData.value }, 425 ownership: { title: '请选择权属分类', list: treeOwnershipData.value },
@@ -388,11 +429,23 @@ const handleActionSheetOpen = (type) =&gt; { @@ -388,11 +429,23 @@ const handleActionSheetOpen = (type) =&gt; {
388 showActionSheet.value = true; 429 showActionSheet.value = true;
389 }; 430 };
390 431
  432 +// 打开树种类型选择弹窗
  433 +const openTreeSpeciesType = () => {
  434 + Object.assign(currentActionSheetData, {
  435 + title: '请选择树种类型',
  436 + list: treeSpeciesTypeList.value,
  437 + type: 'treeSpecies'
  438 + })
  439 + showActionSheet.value = true
  440 +}
  441 +
  442 +// 关闭弹窗
391 const handleActionSheetClose = () => { 443 const handleActionSheetClose = () => {
392 showActionSheet.value = false; 444 showActionSheet.value = false;
393 Object.assign(currentActionSheetData, { type: '', list: [], title: '' }); 445 Object.assign(currentActionSheetData, { type: '', list: [], title: '' });
394 }; 446 };
395 447
  448 +// 弹窗选中回调
396 const handleActionSheetSelect = (e) => { 449 const handleActionSheetSelect = (e) => {
397 const { type } = currentActionSheetData; 450 const { type } = currentActionSheetData;
398 if (type === 'ownership') { 451 if (type === 'ownership') {
@@ -402,6 +455,10 @@ const handleActionSheetSelect = (e) =&gt; { @@ -402,6 +455,10 @@ const handleActionSheetSelect = (e) =&gt; {
402 } else if (type === 'level') { 455 } else if (type === 'level') {
403 formData.treelevel = e.value 456 formData.treelevel = e.value
404 formData.treeleveltext = e.name 457 formData.treeleveltext = e.name
  458 + } else if (type === 'treeSpecies') {
  459 + formData.treeSpeciesType = e.value
  460 + formData.treeSpeciesTypeName = e.name
  461 + formRef.value?.validateField('treeSpeciesType');
405 } 462 }
406 handleActionSheetClose(); 463 handleActionSheetClose();
407 }; 464 };
@@ -419,13 +476,13 @@ const openMap = () =&gt; { @@ -419,13 +476,13 @@ const openMap = () =&gt; {
419 fail: (err) => { 476 fail: (err) => {
420 console.error('地图选择失败', err); 477 console.error('地图选择失败', err);
421 if (err.errMsg.includes('auth deny')) { 478 if (err.errMsg.includes('auth deny')) {
422 - uni.showToast({title: '请授权位置权限', icon: 'none'}); 479 + uni.showToast({ title: '请授权位置权限', icon: 'none' });
423 } 480 }
424 } 481 }
425 }); 482 });
426 } 483 }
427 484
428 -// 提交 485 +// 提交表单
429 const submit = async () => { 486 const submit = async () => {
430 if (loadingFlag.value) return 487 if (loadingFlag.value) return
431 488
@@ -443,11 +500,11 @@ const submit = async () =&gt; { @@ -443,11 +500,11 @@ const submit = async () =&gt; {
443 formData.treeImgList = uploadImgUrls 500 formData.treeImgList = uploadImgUrls
444 loadingFlag.value = true 501 loadingFlag.value = true
445 try { 502 try {
446 - const res = await addTree(formData)  
447 - uni.showToast({title: "新增成功", icon: "success"});  
448 - uni.redirectTo({url: '/pages-sub/data/tree-archive/index'}); 503 + await addTree(formData)
  504 + uni.showToast({ title: "新增成功", icon: "success" });
  505 + uni.redirectTo({ url: '/pages-sub/data/tree-archive/index' });
449 } catch (err) { 506 } catch (err) {
450 - uni.showToast({title: "新增失败,请重试", icon: "none"}); 507 + uni.showToast({ title: "新增失败,请重试", icon: "none" });
451 console.error(err) 508 console.error(err)
452 } finally { 509 } finally {
453 loadingFlag.value = false 510 loadingFlag.value = false
@@ -479,7 +536,7 @@ const submit = async () =&gt; { @@ -479,7 +536,7 @@ const submit = async () =&gt; {
479 position: relative; 536 position: relative;
480 margin-bottom: 0 !important; 537 margin-bottom: 0 !important;
481 } 538 }
482 - :deep(.u-form-item__body__right__message ) { 539 + :deep(.u-form-item__body__right__message) {
483 position: absolute; 540 position: absolute;
484 left: 0; 541 left: 0;
485 bottom: -20rpx; 542 bottom: -20rpx;
@@ -490,6 +547,7 @@ const submit = async () =&gt; { @@ -490,6 +547,7 @@ const submit = async () =&gt; {
490 box-sizing: border-box; 547 box-sizing: border-box;
491 } 548 }
492 } 549 }
  550 +
493 .form-row-wrap + .u-form-item { 551 .form-row-wrap + .u-form-item {
494 margin-top: 25rpx !important; 552 margin-top: 25rpx !important;
495 } 553 }
pages-sub/data/tree-archive/editTree.vue
@@ -22,6 +22,17 @@ @@ -22,6 +22,17 @@
22 <up-input v-model.trim="formData.treetype" placeholder="请输入名称" maxlength="30" border="none"/> 22 <up-input v-model.trim="formData.treetype" placeholder="请输入名称" maxlength="30" border="none"/>
23 </up-form-item> 23 </up-form-item>
24 24
  25 + <!-- 新增:树种类型 底部弹窗选择 -->
  26 + <!-- <up-form-item label="树种类型" prop="treeSpeciesType" required arrow @click="openTreeSpeciesType">
  27 + <up-input
  28 + v-model="formData.treeSpeciesTypeName"
  29 + placeholder="请选择树种类型"
  30 + readonly
  31 + border="none"
  32 + bg-color="transparent"
  33 + />
  34 + </up-form-item> -->
  35 +
25 <view class="form-row-wrap"> 36 <view class="form-row-wrap">
26 <up-row gutter="10"> 37 <up-row gutter="10">
27 <up-col span="6"> 38 <up-col span="6">
@@ -277,8 +288,6 @@ import { onReady, onLoad, onShow, onUnload } from &#39;@dcloudio/uni-app&#39;; @@ -277,8 +288,6 @@ import { onReady, onLoad, onShow, onUnload } from &#39;@dcloudio/uni-app&#39;;
277 288
278 import { treeRoadReq, treeLogReq, treeDetailReq, updateTree, inspectionLogReq } 289 import { treeRoadReq, treeLogReq, treeDetailReq, updateTree, inspectionLogReq }
279 from "@/api/tree-archive/tree-archive.js"; 290 from "@/api/tree-archive/tree-archive.js";
280 -// 引入巡检列表接口  
281 -// import { getInspectListReq } from "@/api/tree-archive/tree-archive.js";  
282 291
283 import { timeFormat } from '@/uni_modules/uview-plus'; 292 import { timeFormat } from '@/uni_modules/uview-plus';
284 import { useUploadImgs } from '@/common/utils/useUploadImgs' 293 import { useUploadImgs } from '@/common/utils/useUploadImgs'
@@ -289,11 +298,6 @@ const CONST = { @@ -289,11 +298,6 @@ const CONST = {
289 UPLOAD_CONFIG: { maxCount: 3, uploadText: '选择图片', sizeType: ['compressed'] } 298 UPLOAD_CONFIG: { maxCount: 3, uploadText: '选择图片', sizeType: ['compressed'] }
290 } 299 }
291 300
292 -// ========== 页面关闭统一跳转 ==========  
293 -onUnload(() => {  
294 - uni.reLaunch({url: '/pages-sub/data/tree-archive/index'})  
295 -})  
296 -  
297 // ========== 基础变量 ========== 301 // ========== 基础变量 ==========
298 const rows = ref([]) 302 const rows = ref([])
299 const roadId = ref('') 303 const roadId = ref('')
@@ -321,6 +325,7 @@ const isShow = ref(false) @@ -321,6 +325,7 @@ const isShow = ref(false)
321 const contentHeight = ref(200) 325 const contentHeight = ref(200)
322 const treeOwnershipData = ref([]) 326 const treeOwnershipData = ref([])
323 const treeLevelData = ref([]) 327 const treeLevelData = ref([])
  328 +const treeSpeciesTypeList = ref([]) // 树种类型字典列表
324 const showActionSheet = ref(false); 329 const showActionSheet = ref(false);
325 const currentActionSheetData = reactive({type: '', list: [], title: ''}); 330 const currentActionSheetData = reactive({type: '', list: [], title: ''});
326 331
@@ -333,10 +338,12 @@ const treeImgList = useUploadImgs({ @@ -333,10 +338,12 @@ const treeImgList = useUploadImgs({
333 treeImgList.imgList.value = [] 338 treeImgList.imgList.value = []
334 treeImgList.rawImgList.value = [] 339 treeImgList.rawImgList.value = []
335 340
336 -// ========== 表单数据 移除estimationtreeage,替换为plantingDate ========== 341 +// ========== 表单数据 ==========
337 const formData = reactive({ 342 const formData = reactive({
338 id: '', 343 id: '',
339 treetype: '', 344 treetype: '',
  345 + // treeSpeciesType: '', // 树种类型编码(提交接口)
  346 + treeSpeciesTypeName: '', // 树种类型名称(页面展示)
340 treeheight: '', 347 treeheight: '',
341 dbh: '', 348 dbh: '',
342 treelevel: '', 349 treelevel: '',
@@ -390,6 +397,10 @@ const rules = reactive({ @@ -390,6 +397,10 @@ const rules = reactive({
390 {required: true, message: '请输入名称', trigger: ['blur', 'change', 'input']}, 397 {required: true, message: '请输入名称', trigger: ['blur', 'change', 'input']},
391 {max: 30, message: '名称最多输入30个字符', trigger: ['blur', 'change', 'input']} 398 {max: 30, message: '名称最多输入30个字符', trigger: ['blur', 'change', 'input']}
392 ], 399 ],
  400 + // 树种类型 必填校验
  401 + // treeSpeciesType: [
  402 + // { required: true, message: '请选择树种类型', trigger: ['change'] }
  403 + // ],
393 treeheight: [ 404 treeheight: [
394 {max: 10, message: '树高不能超过10个字符', trigger: ['blur', 'change', 'input']}, 405 {max: 10, message: '树高不能超过10个字符', trigger: ['blur', 'change', 'input']},
395 {pattern: /^\d+(\.\d{1,2})?$/, message: '格式不正确,支持数字或两位小数', trigger: ['blur', 'change', 'input']}, 406 {pattern: /^\d+(\.\d{1,2})?$/, message: '格式不正确,支持数字或两位小数', trigger: ['blur', 'change', 'input']},
@@ -478,8 +489,11 @@ onLoad((options) =&gt; { @@ -478,8 +489,11 @@ onLoad((options) =&gt; {
478 }); 489 });
479 490
480 onShow(async () => { 491 onShow(async () => {
  492 + // 加载全部字典
481 treeLevelData.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('tree_level')) 493 treeLevelData.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('tree_level'))
482 treeOwnershipData.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('tree_ownership')) 494 treeOwnershipData.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('tree_ownership'))
  495 + treeSpeciesTypeList.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('tree_species_type'))
  496 +
483 if (activeTab.value === 0 && treeId.value && !isInit.value) { 497 if (activeTab.value === 0 && treeId.value && !isInit.value) {
484 await getTreeDetail() 498 await getTreeDetail()
485 isInit.value = true 499 isInit.value = true
@@ -511,12 +525,14 @@ const treeRoadQuery = async () =&gt; { @@ -511,12 +525,14 @@ const treeRoadQuery = async () =&gt; {
511 rows.value = res.list 525 rows.value = res.list
512 } 526 }
513 527
514 -// 获取树木详情 528 +// 获取树木详情(回显树种类型名称+编码)
515 const getTreeDetail = async () => { 529 const getTreeDetail = async () => {
516 const res = await treeDetailReq({id: treeId.value}) 530 const res = await treeDetailReq({id: treeId.value})
517 Object.assign(formData, res) 531 Object.assign(formData, res)
  532 + // 字典回显
518 formData.oldtreeownershipText = uni.$dict.getDictLabel('tree_ownership', res.oldtreeownership) 533 formData.oldtreeownershipText = uni.$dict.getDictLabel('tree_ownership', res.oldtreeownership)
519 formData.treeleveltext = uni.$dict.getDictLabel('tree_level', formData.treelevel) 534 formData.treeleveltext = uni.$dict.getDictLabel('tree_level', formData.treelevel)
  535 + // formData.treeSpeciesTypeName = uni.$dict.getDictLabel('tree_species_type', formData.treeSpeciesType)
520 536
521 treeImgList.imgList.value = [] 537 treeImgList.imgList.value = []
522 treeImgList.rawImgList.value = [] 538 treeImgList.rawImgList.value = []
@@ -545,7 +561,6 @@ const getTreeLogList = async () =&gt; { @@ -545,7 +561,6 @@ const getTreeLogList = async () =&gt; {
545 561
546 // 获取巡检记录列表 562 // 获取巡检记录列表
547 const getInspectList = async () => { 563 const getInspectList = async () => {
548 - // 正式环境启用下面接口  
549 const res = await inspectionLogReq({treeId: treeId.value,pageNo:1,pageSize:100}) 564 const res = await inspectionLogReq({treeId: treeId.value,pageNo:1,pageSize:100})
550 console.log(res) 565 console.log(res)
551 inspectRows.value = res.list || [] 566 inspectRows.value = res.list || []
@@ -556,10 +571,6 @@ const openPlantDatePicker = () =&gt; { @@ -556,10 +571,6 @@ const openPlantDatePicker = () =&gt; {
556 showDatePicker.value = true 571 showDatePicker.value = true
557 } 572 }
558 const onSelectPlantDate = (date) => { 573 const onSelectPlantDate = (date) => {
559 - // date自动 yyyy-MM  
560 - console.log(plantingDate.value)  
561 - console.log(timeFormat(plantingDate.value, 'yyyy-mm'))  
562 - // date格式:yyyy-MM  
563 formData.plantingDate = timeFormat(plantingDate.value, 'yyyy-mm') 574 formData.plantingDate = timeFormat(plantingDate.value, 'yyyy-mm')
564 showDatePicker.value = false 575 showDatePicker.value = false
565 } 576 }
@@ -567,6 +578,7 @@ const onSelectPlantDate = (date) =&gt; { @@ -567,6 +578,7 @@ const onSelectPlantDate = (date) =&gt; {
567 // ========== 表单相关方法 ========== 578 // ========== 表单相关方法 ==========
568 const toggleArea = () => isShow.value = !isShow.value 579 const toggleArea = () => isShow.value = !isShow.value
569 580
  581 +// 打开权属/级别弹窗
570 const handleActionSheetOpen = (type) => { 582 const handleActionSheetOpen = (type) => {
571 const configMap = { 583 const configMap = {
572 ownership: {title: '请选择权属分类', list: treeOwnershipData.value}, 584 ownership: {title: '请选择权属分类', list: treeOwnershipData.value},
@@ -576,11 +588,22 @@ const handleActionSheetOpen = (type) =&gt; { @@ -576,11 +588,22 @@ const handleActionSheetOpen = (type) =&gt; {
576 showActionSheet.value = true; 588 showActionSheet.value = true;
577 }; 589 };
578 590
  591 +// 打开树种类型弹窗
  592 +const openTreeSpeciesType = () => {
  593 + Object.assign(currentActionSheetData, {
  594 + title: '请选择树种类型',
  595 + list: treeSpeciesTypeList.value,
  596 + type: 'treeSpecies'
  597 + })
  598 + showActionSheet.value = true
  599 +}
  600 +
579 const handleActionSheetClose = () => { 601 const handleActionSheetClose = () => {
580 showActionSheet.value = false; 602 showActionSheet.value = false;
581 Object.assign(currentActionSheetData, {type: '', list: [], title: ''}); 603 Object.assign(currentActionSheetData, {type: '', list: [], title: ''});
582 }; 604 };
583 605
  606 +// 弹窗选中回调(区分三种类型)
584 const handleActionSheetSelect = (e) => { 607 const handleActionSheetSelect = (e) => {
585 const {type} = currentActionSheetData; 608 const {type} = currentActionSheetData;
586 if (type === 'ownership') { 609 if (type === 'ownership') {
@@ -590,6 +613,11 @@ const handleActionSheetSelect = (e) =&gt; { @@ -590,6 +613,11 @@ const handleActionSheetSelect = (e) =&gt; {
590 } else if (type === 'level') { 613 } else if (type === 'level') {
591 formData.treelevel = e.value 614 formData.treelevel = e.value
592 formData.treeleveltext = e.name 615 formData.treeleveltext = e.name
  616 + } else if (type === 'treeSpecies') {
  617 + // 树种类型:编码存接口字段,名称页面展示
  618 + formData.treeSpeciesType = e.value
  619 + formData.treeSpeciesTypeName = e.name
  620 + formRef.value?.validateField('treeSpeciesType');
593 } 621 }
594 handleActionSheetClose(); 622 handleActionSheetClose();
595 }; 623 };
@@ -658,11 +686,9 @@ const toInspectDetailPage = (item) =&gt; { @@ -658,11 +686,9 @@ const toInspectDetailPage = (item) =&gt; {
658 // 跳转到新增巡检页面 686 // 跳转到新增巡检页面
659 const toAddInspect = () => { 687 const toAddInspect = () => {
660 const firstItem = inspectRows.value.length > 0 ? inspectRows.value[0] : null 688 const firstItem = inspectRows.value.length > 0 ? inspectRows.value[0] : null
661 - // 存入本地缓存  
662 if (firstItem) { 689 if (firstItem) {
663 uni.setStorageSync('temp_inspect_fill_data', JSON.stringify(firstItem)) 690 uni.setStorageSync('temp_inspect_fill_data', JSON.stringify(firstItem))
664 } else { 691 } else {
665 - // 没有数据先清空残留缓存  
666 uni.removeStorageSync('temp_inspect_fill_data') 692 uni.removeStorageSync('temp_inspect_fill_data')
667 } 693 }
668 uni.navigateTo({ 694 uni.navigateTo({
pages-sub/data/tree-archive/logDetail.vue
@@ -185,9 +185,18 @@ const formatImgList = (imgList) =&gt; { @@ -185,9 +185,18 @@ const formatImgList = (imgList) =&gt; {
185 185
186 // ========== 生命周期 ========== 186 // ========== 生命周期 ==========
187 onUnload(() => { 187 onUnload(() => {
188 - uni.reLaunch({  
189 - url: '/pages-sub/data/tree-archive/index'  
190 - }) 188 + const pages = getCurrentPages()
  189 + // 页面栈长度 >=2 说明有上两页,回退2级
  190 + if (pages.length >= 2) {
  191 + uni.navigateBack({
  192 + delta: 2
  193 + })
  194 + } else {
  195 + // 兜底:页面栈不足时,跳列表页
  196 + uni.reLaunch({
  197 + url: '/pages-sub/data/tree-archive/index'
  198 + })
  199 + }
191 }) 200 })
192 201
193 onLoad((options) => { 202 onLoad((options) => {
pages-sub/data/tree-archive/treeRecord.vue
@@ -69,12 +69,12 @@ @@ -69,12 +69,12 @@
69 <script setup> 69 <script setup>
70 import { ref } from 'vue' 70 import { ref } from 'vue'
71 import { onLoad, onShow, onUnload } from '@dcloudio/uni-app'; 71 import { onLoad, onShow, onUnload } from '@dcloudio/uni-app';
72 -onUnload(() => {  
73 - // 关闭所有页面,直接打开【行道树档案】主页面 【微信小程序完美兼容,无任何报错】  
74 - uni.reLaunch({  
75 - url: '/pages-sub/data/tree-archive/index'  
76 - })  
77 -}) 72 +// onUnload(() => {
  73 +// // 关闭所有页面,直接打开【行道树档案】主页面 【微信小程序完美兼容,无任何报错】
  74 +// uni.reLaunch({
  75 +// url: '/pages-sub/data/tree-archive/index'
  76 +// })
  77 +// })
78 import { treeRoadReq } from "@/api/tree-archive/tree-archive.js"; 78 import { treeRoadReq } from "@/api/tree-archive/tree-archive.js";
79 import { timeFormat } from '@/uni_modules/uview-plus'; 79 import { timeFormat } from '@/uni_modules/uview-plus';
80 const rows = ref([]) 80 const rows = ref([])
pages.json
@@ -18,7 +18,8 @@ @@ -18,7 +18,8 @@
18 "path": "pages/index/index", 18 "path": "pages/index/index",
19 "style": { 19 "style": {
20 "navigationBarTitleText": "首页", 20 "navigationBarTitleText": "首页",
21 - "navigationStyle": "custom" 21 + "navigationStyle": "custom",
  22 + "enablePullDownRefresh": true
22 } 23 }
23 }, 24 },
24 25
pages/index/index.vue
@@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
9 </view> 9 </view>
10 </view> 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 - <up-icon name="chat" color="#fff" size="24"/> 12 + <up-icon name="chat" color="#fff" size="24" />
13 <view class="msg-badge"> 13 <view class="msg-badge">
14 <up-badge type="error" showZero="true" max="999" :value="msgCount"></up-badge> 14 <up-badge type="error" showZero="true" max="999" :value="msgCount"></up-badge>
15 </view> 15 </view>
@@ -17,161 +17,192 @@ @@ -17,161 +17,192 @@
17 </view> 17 </view>
18 18
19 <view class="content-wrap"> 19 <view class="content-wrap">
20 - <!-- 任务完成情况标题 -->  
21 - <view class="module-title">任务完成情况</view>  
22 -  
23 - <!-- 任务完成情况卡片 -->  
24 - <view class="task-chart-card">  
25 - <view class="card-header">  
26 - <view class="unit-tip">单位: 个</view>  
27 - <view class="date-picker-wrap">  
28 - <neo-datetime-pro  
29 - v-model="dateRange"  
30 - type="daterange"  
31 - :clearable="false"  
32 - placeholder="请选择日期范围"  
33 - @confirm="handleDateConfirm"  
34 - :max-date="maxDate"  
35 -  
36 - @popup-show="handlePopupShow"  
37 - @popup-hide="handlePopupHide"  
38 - />  
39 -<!-- :minDate="minDate"--> 20 + <!-- 图表区域 增加平滑动画容器 -->
  21 + <view class="chart-wrap" v-show="showChartArea">
  22 + <!-- 任务完成情况标题 -->
  23 + <view class="module-title">任务完成情况</view>
  24 +
  25 + <!-- 任务完成情况卡片 -->
  26 + <view class="task-chart-card">
  27 + <view class="card-header">
  28 + <view class="unit-tip">单位: 个</view>
  29 + <view class="date-picker-wrap">
  30 + <neo-datetime-pro v-model="dateRange" type="daterange" :clearable="false" placeholder="请选择日期范围"
  31 + @confirm="handleDateConfirm" :max-date="maxDate" @popup-show="handlePopupShow"
  32 + @popup-hide="handlePopupHide" />
  33 + </view>
40 </view> 34 </view>
41 - </view>  
42 35
43 - <!-- 双折线K线图 -->  
44 - <view class="chart-container">  
45 - <uni-charts  
46 - v-if="!isDatePickerOpen"  
47 - type="line"  
48 - :data="klineChartData"  
49 - :categories="klineCategories"  
50 - :option="klineOption"  
51 - :width="chartWidth"  
52 - :height="chartHeight"  
53 - /> 36 + <!-- 双折线K线图 -->
  37 + <view class="chart-container">
  38 + <uni-charts v-if="!isDatePickerOpen" type="line" :data="klineChartData" :categories="klineCategories"
  39 + :option="klineOption" :width="chartWidth" :height="chartHeight" />
  40 + </view>
54 </view> 41 </view>
55 </view> 42 </view>
56 43
57 - <!-- 使用up-tabs组件实现Tab切换 -->  
58 - <up-tabs  
59 - :list="tabList"  
60 - lineWidth="60"  
61 - @change="handleTabChange"  
62 - class="task-tab-container"  
63 - :scrollable="false"  
64 - :activeStyle="{  
65 - color: '#000000',  
66 - fontSize: '14'  
67 - }"  
68 - :inactiveStyle="{  
69 - color: '#787070',  
70 - fontSize: '14'  
71 - }"  
72 - ></up-tabs>  
73 -  
74 - <!-- z-paging分页列表 -->  
75 - <z-paging  
76 - ref="paging"  
77 - v-model="currentTaskList"  
78 - @query="queryList"  
79 - :auto-show-system-loading="true"  
80 - :page-size="10"  
81 - use-page-scroll  
82 - >  
83 -  
84 - <template #empty>  
85 - <up-empty/>  
86 - </template>  
87 - <view class="task-list-container">  
88 - <view  
89 - class="task-item"  
90 - v-for="(item,index) in currentTaskList"  
91 - :key="`${item.taskName}_${index}`"  
92 - hover-class="task-item--hover"  
93 - @click="handleTaskClick(item)"  
94 - > 44 + <view class="con-wrap">
  45 + <view class="con-title">养护任务 </view>
  46 +
  47 + <!-- 手写Tab切换:胶囊分段样式 -->
  48 + <view class="custom-tab-wrap">
  49 + <view class="tab-capsule" :class="{ active: currentTabValue === 0 }" @click="handleTabClick(0)">
  50 + 待办事项({{ todoTotal }})
  51 + </view>
  52 + <view class="tab-capsule" :class="{ active: currentTabValue === 1 }" @click="handleTabClick(1)">
  53 + 已办事项({{ doneTotal }})
  54 + </view>
  55 + </view>
  56 +
  57 +
  58 + <!-- 动态组信息文案 -->
  59 + <view class="tip-top" style="padding: 8px 0 0 70px;">
  60 + <text>{{ tipText }}</text>
  61 + </view>
  62 +
  63 + <!-- 顶部进度提示栏 -->
  64 + <view v-if="showTipHeader" class="tip-header">
  65 + <up-icon name="checkmark-circle" color="#00b42a" size="20"></up-icon>
  66 + <text style="color: #1677ff;">今日组内:您已经完成{{ finishNum }}项,小组均值{{ avgNum }}项</text>
  67 + </view>
  68 +
  69 +
  70 + <scroll-view
  71 + class="task-list-container"
  72 + scroll-y
  73 + @scroll="onListScroll"
  74 + @scrolltoupper="handleTouchTop"
  75 + enhanced
  76 + >
  77 + <view v-if="taskList.length === 0" class="empty-box">
  78 + <up-empty />
  79 + </view>
  80 + <view class="task-item" v-for="(item, index) in taskList" :key="`${item.taskName}_${index}`"
  81 + hover-class="task-item--hover" @click="handleTaskClick(item)">
95 <view class="task-item__content"> 82 <view class="task-item__content">
96 - <view class="task-item__name u-line-1">  
97 - {{ item.taskName || '无' }}  
98 - </view>  
99 - <view class="task-item__footer u-flex common-item-center common-justify-between"  
100 - style="font-size: 13px; margin-top: 5px;">  
101 - <view class="urgency-tag">  
102 - 紧急程度: {{ uni.$dict.getDictLabel('workorder_pressing_type', item.pressingType) || '--' }} 83 + <view class="name-fixed-wrap">
  84 + <view class="task-item__name ">
  85 + 事项{{ index + 1 }}
103 </view> 86 </view>
104 - <view style="font-size: 13px;color: #333">{{  
105 - timeFormat(item.busiDateTime, 'yyyy-mm-dd hh:MM:ss')  
106 - }} 87 + <view class="task-name-text">
  88 + {{ item.taskName || '无' }}
107 </view> 89 </view>
108 </view> 90 </view>
  91 +
  92 + <view v-if="currentTabValue === 0" class="task-item__footer" style="padding-left: 60px;">
  93 + <image class="loc-icon" src="https://img.jichengshanshui.com.cn:28207/appimg/定位icon@3x.png" mode="aspectFit"></image>
  94 + <template v-if="isWalkTextMode">
  95 + <text>距离当前{{ item.distance }}m ·约{{ item.estimatedWalkingMinutes }}分钟步行</text>
  96 + </template>
  97 + <template v-else>
  98 + <text>{{ item.distance }}m ·</text>
  99 + <up-icon name="star-fill" color="#ffd21d" size="20"></up-icon>
  100 + <text>历史完成率{{ item.efficiencyPercent }}%</text>
  101 + </template>
  102 + </view>
109 </view> 103 </view>
110 </view> 104 </view>
111 - </view>  
112 - </z-paging> 105 + </scroll-view>
  106 + </view>
113 </view> 107 </view>
114 </view> 108 </view>
115 </template> 109 </template>
116 110
117 <script setup lang="ts"> 111 <script setup lang="ts">
118 -import {ref, watch, computed, reactive} from 'vue';  
119 -import {onShow} from '@dcloudio/uni-app'  
120 -import {useUserStore} from '@/pinia/user';  
121 -import {timeFormat} from '@/uni_modules/uview-plus'; 112 +import { ref, computed, nextTick } from 'vue';
  113 +import { onShow,onUnload } from '@dcloudio/uni-app'
  114 +import { useUserStore } from '@/pinia/user';
  115 +import { timeFormat } from '@/uni_modules/uview-plus';
122 import uniCharts from '@/components/uni-charts/uni-charts.vue'; 116 import uniCharts from '@/components/uni-charts/uni-charts.vue';
123 -import {getUnreadCount} from "@/api/user";  
124 -import {getTaskCompletionSummary, getTaskDetails} from "@/api/index/index"; 117 +import { getUnreadCount } from "@/api/user";
  118 +import {
  119 + getTaskCompletionSummary,
  120 + taskDetailsWithDistance,
  121 + taskDetailsWithSmartSort
  122 +} from "@/api/index/index";
125 123
  124 +const userStore = useUserStore();
  125 +const deptId = computed(() => String(userStore.userInfo?.user?.deptId ?? ''));
  126 +
  127 +// ====================== 只需要在这里统一维护分组配置 ======================
  128 +type GroupItem = {
  129 + deptId: string;
  130 + label: string;
  131 + showTipHeader: boolean; // 是否展示顶部进度条
  132 + useWalkText: boolean; // 是否展示步行距离文案
  133 +};
  134 +const groupConfig: GroupItem[] = [
  135 + { deptId: '364', label: '[组2 A1B2 纯效率·有提醒]', showTipHeader: true, useWalkText: true },
  136 + { deptId: '337', label: '[组3 A2B1 个性化·无提醒]', showTipHeader: false, useWalkText: false },
  137 + { deptId: '338', label: '[组4 A2B2 个性化·有提醒]', showTipHeader: true, useWalkText: false },
  138 +];
  139 +// 默认组1配置
  140 +const defaultGroup: GroupItem = { deptId: '', label: '[组1 A1B1 纯效率·无提醒]', showTipHeader: false, useWalkText: true };
  141 +
  142 +// 获取当前部门配置对象
  143 +const currentGroup = computed(() => {
  144 + const target = groupConfig.find(item => item.deptId === deptId.value);
  145 + return target ?? defaultGroup;
  146 +});
  147 +
  148 +// 下面全部自动读取配置,无需写if/switch
  149 +const tipText = computed(() => currentGroup.value.label);
  150 +const showTipHeader = computed(() => currentGroup.value.showTipHeader);
  151 +const isWalkTextMode = computed(() => currentGroup.value.useWalkText);
  152 +const useDistanceApi = computed(() => currentGroup.value.useWalkText);
  153 +// =========================================================================
126 154
127 -// ========== 2. 响应式数据 ========== 155 +const finishNum = ref(0);
  156 +const avgNum = ref(0);
128 const msgCount = ref(0); 157 const msgCount = ref(0);
129 -const currentTab = ref('todo'); // todo:待办 done:已办  
130 -const dateRange = ref([]); 158 +const currentTabValue = ref(0);
  159 +
  160 +const dateRange = ref<string[]>([]);
131 const yesterday = new Date(); 161 const yesterday = new Date();
132 -yesterday.setDate(yesterday.getDate() - 1); // 日期减1天 = 昨天 162 +yesterday.setDate(yesterday.getDate() - 1);
133 const maxDate = ref(timeFormat(yesterday, 'yyyy-mm-dd')); 163 const maxDate = ref(timeFormat(yesterday, 'yyyy-mm-dd'));
134 -const minDate = '2026-01-06' 164 +const minDate = '2026-01-06';
135 165
136 -// 新增响应式变量:标记日期选择器弹窗是否打开  
137 const isDatePickerOpen = ref(false); 166 const isDatePickerOpen = ref(false);
138 -  
139 -// 弹窗打开时触发  
140 const handlePopupShow = () => { 167 const handlePopupShow = () => {
141 isDatePickerOpen.value = true; 168 isDatePickerOpen.value = true;
142 }; 169 };
143 -  
144 -// 弹窗关闭时触发  
145 const handlePopupHide = () => { 170 const handlePopupHide = () => {
146 isDatePickerOpen.value = false; 171 isDatePickerOpen.value = false;
147 }; 172 };
148 -// Tab列表配置  
149 -const tabList = ref([]);  
150 -const todoTotal = ref(0); // 待办事项总数  
151 -const doneTotal = ref(0); // 已办事项总数  
152 173
153 -// 分页相关  
154 -const paging = ref(null);  
155 -const currentTaskList = ref([]); // 当前显示的任务列表(分页数据) 174 +const todoTotal = ref(0);
  175 +const doneTotal = ref(0);
  176 +const todoFullList = ref<any[]>([]);
  177 +const doneFullList = ref<any[]>([]);
  178 +const taskList = computed(() => {
  179 + return currentTabValue.value === 0 ? todoFullList.value : doneFullList.value;
  180 +});
  181 +
  182 +const chooseOptions = computed(() => [
  183 + { title: `待办事项(${todoTotal.value})`, id: 'todo' },
  184 + { title: `已办事项(${doneTotal.value})`, id: 'done' }
  185 +]);
156 186
157 -// 折线图数据配置 187 +const currentLon = ref<string>('');
  188 +const currentLat = ref<string>('');
158 const chartWidth = ref(0); 189 const chartWidth = ref(0);
159 const chartHeight = ref(300); 190 const chartHeight = ref(300);
160 -const klineCategories = ref([]); // X轴日期 191 +
  192 +const klineCategories = ref<string[]>([]);
161 const klineChartData = ref([ 193 const klineChartData = ref([
162 { 194 {
163 name: '已完成任务数', 195 name: '已完成任务数',
164 - data: [], 196 + data: [] as number[],
165 color: '#25AF69' 197 color: '#25AF69'
166 }, 198 },
167 { 199 {
168 name: '总任务数', 200 name: '总任务数',
169 - data: [], 201 + data: [] as number[],
170 color: '#B34C17' 202 color: '#B34C17'
171 } 203 }
172 ]); 204 ]);
173 205
174 -// 图表配置  
175 const klineOption = ref({ 206 const klineOption = ref({
176 grid: { 207 grid: {
177 top: '25%', 208 top: '25%',
@@ -180,16 +211,16 @@ const klineOption = ref({ @@ -180,16 +211,16 @@ const klineOption = ref({
180 bottom: '16%' 211 bottom: '16%'
181 }, 212 },
182 xAxis: { 213 xAxis: {
183 - axisLabel: {fontSize: 12, color: '#666'} 214 + axisLabel: { fontSize: 12, color: '#666' }
184 }, 215 },
185 yAxis: { 216 yAxis: {
186 min: 0, 217 min: 0,
187 - splitLine: {lineStyle: {color: '#f5f5f7'}},  
188 - axisLabel: {fontSize: 12, color: '#666'} 218 + splitLine: { lineStyle: { color: '#f5f5f7' } },
  219 + axisLabel: { fontSize: 12, color: '#666' }
189 }, 220 },
190 tooltip: { 221 tooltip: {
191 trigger: 'axis', 222 trigger: 'axis',
192 - formatter: (params) => { 223 + formatter: (params: any[]) => {
193 return `${params[0].name}<br/> 224 return `${params[0].name}<br/>
194 ${params[0].seriesName}: ${params[0].data}<br/> 225 ${params[0].seriesName}: ${params[0].data}<br/>
195 ${params[1].seriesName}: ${params[1].data}`; 226 ${params[1].seriesName}: ${params[1].data}`;
@@ -198,34 +229,144 @@ const klineOption = ref({ @@ -198,34 +229,144 @@ const klineOption = ref({
198 legend: { 229 legend: {
199 show: true, 230 show: true,
200 top: '5%', 231 top: '5%',
201 - textStyle: {fontSize: 12} 232 + textStyle: { fontSize: 12 }
202 } 233 }
203 }); 234 });
204 235
205 -// ========== 3. 计算属性 ==========  
206 -const userStore = useUserStore();  
207 const userName = computed(() => userStore.userInfo?.user?.nickname || '用户'); 236 const userName = computed(() => userStore.userInfo?.user?.nickname || '用户');
208 237
209 -// ========== 4. 工具函数 ==========  
210 -const rpx2px = (rpx) => { 238 +const rpx2px = (rpx: number) => {
211 const systemInfo = uni.getSystemInfoSync(); 239 const systemInfo = uni.getSystemInfoSync();
212 - return Math.floor(rpx * (systemInfo.screenWidth / 750)); 240 + return Math.floor(systemInfo.screenWidth / 750 * rpx);
  241 +};
  242 +
  243 +// ========== 滚动逻辑 终极优化 ==========
  244 +const showChartArea = ref(true);
  245 +// 隐藏阈值:超过80rpx收起
  246 +const scrollHideThreshold = rpx2px(80);
  247 +// 安全显示区间:≤80rpx一律显示,覆盖所有回弹残留(70/80/90)
  248 +const safeShowThreshold = rpx2px(130);
  249 +let scrollTimer: number | null = null;
  250 +
  251 +// 下拉触顶强制展示
  252 +const handleTouchTop = () => {
  253 + showChartArea.value = true;
213 }; 254 };
214 255
  256 +const onListScroll = (e: any) => {
  257 + const scrollTop = e.detail.scrollTop;
  258 + console.log('scrollTop', scrollTop)
  259 + if (scrollTimer) clearTimeout(scrollTimer);
  260 +
  261 + // 实时:只要在安全区间立刻显示
  262 + if (scrollTop <= safeShowThreshold) {
  263 + showChartArea.value = true;
  264 + } else if (scrollTop > scrollHideThreshold) {
  265 + showChartArea.value = false;
  266 + }
  267 +
  268 + // 滚动停止兜底二次校验
  269 + scrollTimer = setTimeout(() => {
  270 + if (scrollTop <= safeShowThreshold) {
  271 + showChartArea.value = true;
  272 + }
  273 + }, 100);
  274 +};
  275 +
  276 +// 切换tab重置图表显示
  277 +const handleTabClick = (tabIndex: number) => {
  278 + if (currentTabValue.value === tabIndex) return;
  279 + currentTabValue.value = tabIndex;
  280 + // 切换tab强制显示图表
  281 + showChartArea.value = true;
  282 +};
  283 +
  284 +// 页面每次显示强制重置图表为显示状态
  285 +onShow(async () => {
  286 + showChartArea.value = true;
  287 + chartWidth.value = rpx2px(750 - 60);
  288 + chartHeight.value = rpx2px(300);
  289 + initRecent7Days();
  290 + getUnread();
  291 + await loadAllTaskByDept();
  292 +});
  293 +
  294 +// 页面销毁清除定时器
  295 +onUnload(() => {
  296 + if (scrollTimer) clearTimeout(scrollTimer);
  297 +});
  298 +// ======================================
  299 +
  300 +const getCurrentLocation = (): Promise<{ lon: string; lat: string }> => {
  301 + return new Promise((resolve, reject) => {
  302 + uni.getSetting({
  303 + success: (settingRes) => {
  304 + if (!settingRes.authSetting['scope.userLocation']) {
  305 + uni.authorize({
  306 + scope: 'scope.userLocation',
  307 + success: () => {
  308 + uni.getLocation({
  309 + type: 'gcj02',
  310 + success: (loc) => {
  311 + resolve({
  312 + lon: loc.longitude.toFixed(6),
  313 + lat: loc.latitude.toFixed(6)
  314 + })
  315 + },
  316 + fail: (err) => {
  317 + uni.showToast({ title: '获取位置失败,请打开手机定位', icon: 'none' })
  318 + reject(err)
  319 + }
  320 + })
  321 + },
  322 + fail: () => {
  323 + uni.showModal({
  324 + title: '需要定位权限',
  325 + content: '请允许获取位置才能正常使用',
  326 + confirmText: '去设置',
  327 + success: (modal) => {
  328 + if (modal.confirm) {
  329 + uni.openSetting()
  330 + }
  331 + }
  332 + })
  333 + reject('用户拒绝定位权限')
  334 + }
  335 + })
  336 + } else {
  337 + uni.getLocation({
  338 + type: 'gcj02',
  339 + success: (loc) => {
  340 + resolve({
  341 + lon: loc.longitude.toFixed(6),
  342 + lat: loc.latitude.toFixed(6)
  343 + })
  344 + },
  345 + fail: (err) => {
  346 + uni.showToast({ title: '获取位置失败,请打开手机定位', icon: 'none' })
  347 + reject(err)
  348 + }
  349 + })
  350 + }
  351 + },
  352 + fail: (err) => {
  353 + reject('获取权限失败:' + err.errMsg)
  354 + }
  355 + })
  356 + })
  357 +}
215 358
216 const initRecent7Days = async () => { 359 const initRecent7Days = async () => {
217 const yesterday = new Date(); 360 const yesterday = new Date();
218 - yesterday.setDate(yesterday.getDate() - 1); // 结束时间 = 昨天 361 + yesterday.setDate(yesterday.getDate() - 1);
219 const sevenDaysAgo = new Date(yesterday); 362 const sevenDaysAgo = new Date(yesterday);
220 - sevenDaysAgo.setDate(yesterday.getDate() - 6); // 开始时间 = 昨天往前推6天(共7天) 363 + sevenDaysAgo.setDate(yesterday.getDate() - 6);
221 364
222 - // 格式化日期为 YYYY-MM-DD 格式  
223 dateRange.value = [ 365 dateRange.value = [
224 timeFormat(sevenDaysAgo, 'yyyy-mm-dd'), 366 timeFormat(sevenDaysAgo, 'yyyy-mm-dd'),
225 timeFormat(yesterday, 'yyyy-mm-dd') 367 timeFormat(yesterday, 'yyyy-mm-dd')
226 ]; 368 ];
227 -  
228 - initChartData(timeFormat(sevenDaysAgo, 'yyyy-mm-dd'), timeFormat(yesterday, 'yyyy-mm-dd')); 369 + initChartData(dateRange.value[0], dateRange.value[1]);
229 }; 370 };
230 371
231 const getUnread = async () => { 372 const getUnread = async () => {
@@ -238,145 +379,115 @@ const getUnread = async () =&gt; { @@ -238,145 +379,115 @@ const getUnread = async () =&gt; {
238 } 379 }
239 }; 380 };
240 381
241 -/**  
242 - * 初始化图表数据  
243 - * @param {string} beginTime 开始时间  
244 - * @param {string} endTime 结束时间  
245 - */  
246 -const initChartData = async (beginTime, endTime) => { 382 +const initChartData = async (beginTime: string, endTime: string) => {
247 try { 383 try {
248 - let postData = {  
249 - beginTime: beginTime,  
250 - endTime: endTime  
251 - }; 384 + const postData = { beginTime, endTime };
252 const res = await getTaskCompletionSummary(postData); 385 const res = await getTaskCompletionSummary(postData);
253 - console.log('图表数据:', res);  
254 - klineCategories.value = res.map(item => item.currentDate);  
255 - klineChartData.value[0].data = res.map(item => item.completedNum);  
256 - klineChartData.value[1].data = res.map(item => item.countpendingNum); 386 + klineCategories.value = res.map((item: any) => item.currentDate);
  387 + klineChartData.value[0].data = res.map((item: any) => item.completedNum);
  388 + klineChartData.value[1].data = res.map((item: any) => item.countpendingNum);
257 } catch (error) { 389 } catch (error) {
258 console.error('获取图表数据失败:', error); 390 console.error('获取图表数据失败:', error);
259 } 391 }
260 }; 392 };
261 393
262 -/**  
263 - * 获取所有Tab的总数(初始化时调用)  
264 - */  
265 -const getAllTabTotal = async () => { 394 +const loadDistanceApiTask = async (lon: string, lat: string) => {
266 try { 395 try {
267 - // 同时请求待办和已办的总数(只请求第1页,每页1条,仅获取total) 396 + const paramsTodo = { queryType: 1, currentLon: lon, currentLat: lat };
  397 + const paramsDone = { queryType: 2, currentLon: lon, currentLat: lat };
268 const [todoRes, doneRes] = await Promise.all([ 398 const [todoRes, doneRes] = await Promise.all([
269 - getTaskDetails({queryType: 1, pageNo: 1, pageSize: 1}),  
270 - getTaskDetails({queryType: 2, pageNo: 1, pageSize: 1}) 399 + taskDetailsWithDistance(paramsTodo),
  400 + taskDetailsWithDistance(paramsDone)
271 ]); 401 ]);
272 -  
273 - // 更新总数  
274 - todoTotal.value = todoRes?.total || 0;  
275 - doneTotal.value = doneRes?.total || 0;  
276 - console.log('已办:')  
277 - console.log(doneRes)  
278 - tabList.value = [  
279 - {name: `待办事项(${todoTotal.value})`},  
280 - {name: `已办事项(${doneTotal.value})`},  
281 - ]  
282 - console.log('初始化总数:', {todoTotal: todoTotal.value, doneTotal: doneTotal.value});  
283 - } catch (error) {  
284 - console.error('获取Tab总数失败:', error);  
285 - // 保底:如果已办请求失败,至少保证待办数据正常 402 + todoFullList.value = todoRes?.sortedTasks || [];
  403 + doneFullList.value = doneRes?.sortedTasks || [];
  404 + todoTotal.value = Number(todoRes?.totalPendingTasks) || 0;
  405 + doneTotal.value = Number(doneRes?.totalPendingTasks) || 0;
  406 + finishNum.value = todoRes?.teamProgress?.userCompletedCount ?? 0;
  407 + avgNum.value = doneRes?.teamProgress?.teamAverage ?? 0;
  408 + await nextTick();
  409 + } catch (err) {
  410 + uni.showToast({ title: '加载任务失败', icon: 'none' });
  411 + console.error('加载distance接口任务报错', err);
  412 + todoFullList.value = [];
  413 + doneFullList.value = [];
  414 + todoTotal.value = 0;
  415 + doneTotal.value = 0;
286 } 416 }
287 }; 417 };
288 418
289 -// ========== 5. 业务逻辑 ==========  
290 -/**  
291 - * up-tabs切换事件处理  
292 - * @param {object} item 选中的Tab项  
293 - */  
294 -const handleTabChange = (item) => {  
295 - // 解析Tab类型(todo/done)  
296 - console.log(item)  
297 - const newTab = item.name.includes('待办') ? 'todo' : 'done';  
298 - if (currentTab.value === newTab) return;  
299 -  
300 - currentTab.value = newTab;  
301 - // 切换标签后刷新分页数据  
302 - paging.value?.reload();  
303 -};  
304 -  
305 -/**  
306 - * 分页查询列表数据  
307 - * @param {number} pageNo 页码  
308 - * @param {number} pageSize 每页条数  
309 - */  
310 -const queryList = async (pageNo, pageSize) => { 419 +const loadSmartSortTask = async (lon: string, lat: string) => {
311 try { 420 try {
312 - // 1:待完成 2:已完成  
313 - const queryType = currentTab.value === 'todo' ? 1 : 2;  
314 - const postData = {  
315 - queryType,  
316 - pageNo,  
317 - pageSize,  
318 - };  
319 -  
320 - const res = await getTaskDetails(postData);  
321 - console.log(`${currentTab.value}列表数据:`, res);  
322 -  
323 - // 更新当前Tab的总数  
324 - if (currentTab.value === 'todo') {  
325 - todoTotal.value = res?.total || 0;  
326 - } else {  
327 - doneTotal.value = res?.total || 0;  
328 - }  
329 -  
330 - // 适配z-paging分页(注意接口返回结构是res.data.list)  
331 - paging.value.complete(res?.list || [], res?.total || 0);  
332 - } catch (error) {  
333 - console.error(`加载${currentTab.value}列表失败:`, error);  
334 - paging.value?.complete(false);  
335 - uni.showToast({title: '加载失败,请重试', icon: 'none'}); 421 + const [todoRes, doneRes] = await Promise.all([
  422 + taskDetailsWithSmartSort({
  423 + queryType: 1,
  424 + currentLon: lon,
  425 + currentLat: lat
  426 + }),
  427 + taskDetailsWithSmartSort({
  428 + queryType: 2,
  429 + currentLon: lon,
  430 + currentLat: lat
  431 + })
  432 + ]);
  433 + todoFullList.value = todoRes?.sortedTasks || [];
  434 + doneFullList.value = doneRes?.sortedTasks || [];
  435 + todoTotal.value = Number(todoRes?.totalPendingTasks) || 0;
  436 + doneTotal.value = Number(doneRes?.totalPendingTasks) || 0;
  437 + finishNum.value = todoRes?.teamProgress?.userCompletedCount ?? 0;
  438 + avgNum.value = doneRes?.teamProgress?.teamAverage ?? 0;
  439 + await nextTick();
  440 + } catch (err) {
  441 + uni.showToast({ title: '加载任务失败', icon: 'none' });
  442 + console.error('加载smartSort接口任务报错', err);
  443 + todoFullList.value = [];
  444 + doneFullList.value = [];
  445 + todoTotal.value = 0;
  446 + doneTotal.value = 0;
336 } 447 }
337 }; 448 };
338 449
339 -// 日期选择确认事件  
340 -const handleDateConfirm = (e) => {  
341 - console.log('日期选择确认:', e); 450 +const loadAllTaskByDept = async () => {
  451 + const loc = await getCurrentLocation();
  452 + currentLon.value = loc.lon;
  453 + currentLat.value = loc.lat;
  454 + if (useDistanceApi.value) {
  455 + await loadDistanceApiTask(loc.lon, loc.lat);
  456 + } else {
  457 + await loadSmartSortTask(loc.lon, loc.lat);
  458 + }
  459 +};
342 460
343 - // 更新选中的日期范围 461 +const handleDateConfirm = (e: string[]) => {
344 dateRange.value = e; 462 dateRange.value = e;
345 -  
346 - // 加载选择日期后的图表数据  
347 initChartData(e[0], e[1]); 463 initChartData(e[0], e[1]);
348 }; 464 };
349 465
350 const handleMsgClick = () => { 466 const handleMsgClick = () => {
351 - uni.navigateTo({url: '/pages-sub/msg/index'});  
352 -};  
353 -  
354 -const handleTaskClick = (item) => {  
355 - // uni.navigateTo({url: `/pages-sub/task/detail?id=${item.id || ''}`}); 467 + uni.navigateTo({ url: '/pages-sub/msg/index' });
356 }; 468 };
357 469
358 -// ========== 6. 生命周期 ==========  
359 -onShow(async () => {  
360 - // 初始化图表尺寸  
361 - chartWidth.value = rpx2px(750 - 60);  
362 - chartHeight.value = rpx2px(300);  
363 -  
364 - // 初始化默认日期范围(最近7天)  
365 - initRecent7Days();  
366 -  
367 - // 获取未读消息数  
368 - getUnread();  
369 - await getAllTabTotal();  
370 -  
371 - // 初始化分页数据(待办列表)  
372 - if (paging.value) {  
373 - paging.value.reload(); 470 +const handleTaskClick = (item: any) => {
  471 + if (item.taskType == "maintain") {
  472 + if (currentTabValue.value === 0) {
  473 + uni.navigateTo({ url: `/pages-sub/daily/maintain-manage/add-record?planNo=${item.planNo || ''}` });
  474 + }
  475 + if (currentTabValue.value === 1) {
  476 + uni.navigateTo({ url: `/pages-sub/daily/maintain-manage/finish-plan-detail?planNo=${item.planNo || ''}` });
  477 + }
374 } 478 }
375 -}); 479 + if (item.taskType == "inspection") {
  480 + if (currentTabValue.value === 0) {
  481 + uni.navigateTo({ url: `/pages-sub/daily/patrol-manage/add-patrol-record?planNo=${item.planNo}&batchNo=${item.batchNo}` });
  482 + }
  483 + if (currentTabValue.value === 1) {
  484 + uni.navigateTo({ url: `/pages-sub/daily/patrol-manage/finish-plan-detail?planNo=${item.planNo || ''}` });
  485 + }
  486 + }
  487 +};
376 </script> 488 </script>
377 489
378 <style scoped lang="scss"> 490 <style scoped lang="scss">
379 -/* 样式变量 */  
380 $primary-color: #1677ff; 491 $primary-color: #1677ff;
381 $danger-color: #E53935; 492 $danger-color: #E53935;
382 $text-color: #333; 493 $text-color: #333;
@@ -389,14 +500,18 @@ $card-radius: 5px; @@ -389,14 +500,18 @@ $card-radius: 5px;
389 $spacing-sm: 5px; 500 $spacing-sm: 5px;
390 $spacing-md: 10px; 501 $spacing-md: 10px;
391 $spacing-lg: 15px; 502 $spacing-lg: 15px;
392 -$border-color: #e5e5e5; // 新增边框颜色变量 503 +$border-color: #e5e5e5;
393 504
394 .page-container { 505 .page-container {
395 - min-height: 100vh; 506 + height: 100vh;
396 background-color: $bg-color; 507 background-color: $bg-color;
  508 + overflow: hidden;
  509 + display: flex;
  510 + flex-direction: column;
397 } 511 }
398 512
399 .user-info-bar { 513 .user-info-bar {
  514 + flex-shrink: 0;
400 background: url("https://img.jichengshanshui.com.cn:28207/appimg/bg.jpg") no-repeat; 515 background: url("https://img.jichengshanshui.com.cn:28207/appimg/bg.jpg") no-repeat;
401 background-size: 100% 100%; 516 background-size: 100% 100%;
402 padding: 80px $spacing-lg 135px; 517 padding: 80px $spacing-lg 135px;
@@ -404,28 +519,22 @@ $border-color: #e5e5e5; // 新增边框颜色变量 @@ -404,28 +519,22 @@ $border-color: #e5e5e5; // 新增边框颜色变量
404 justify-content: space-between; 519 justify-content: space-between;
405 align-items: center; 520 align-items: center;
406 color: #fff; 521 color: #fff;
407 - //position: relative;  
408 - //z-index: 1;  
409 522
410 .username { 523 .username {
411 font-size: 16px; 524 font-size: 16px;
412 margin-bottom: 4px; 525 margin-bottom: 4px;
413 } 526 }
414 -  
415 .login-desc { 527 .login-desc {
416 font-size: 16px; 528 font-size: 16px;
417 } 529 }
418 -  
419 .msg-icon { 530 .msg-icon {
420 position: relative; 531 position: relative;
421 padding: 5px; 532 padding: 5px;
422 top: 10px; 533 top: 10px;
423 border-radius: 50%; 534 border-radius: 50%;
424 -  
425 &--hover { 535 &--hover {
426 background-color: rgba(255, 255, 255, 0.2); 536 background-color: rgba(255, 255, 255, 0.2);
427 } 537 }
428 -  
429 .msg-badge { 538 .msg-badge {
430 position: absolute; 539 position: absolute;
431 top: -2px; 540 top: -2px;
@@ -435,13 +544,25 @@ $border-color: #e5e5e5; // 新增边框颜色变量 @@ -435,13 +544,25 @@ $border-color: #e5e5e5; // 新增边框颜色变量
435 } 544 }
436 545
437 .content-wrap { 546 .content-wrap {
  547 + flex: 1;
438 margin-top: -122px; 548 margin-top: -122px;
439 border-radius: $border-radius $border-radius 0 0; 549 border-radius: $border-radius $border-radius 0 0;
440 background-color: $bg-color; 550 background-color: $bg-color;
441 - //position: relative;  
442 - //z-index: 999;  
443 overflow: hidden; 551 overflow: hidden;
444 - padding-bottom: $spacing-lg; 552 + display: flex;
  553 + flex-direction: column;
  554 +}
  555 +
  556 +.chart-wrap {
  557 + flex-shrink: 0;
  558 + transition: all 0.35s ease-out;
  559 + opacity: 1;
  560 + overflow: hidden;
  561 +}
  562 +.chart-wrap[hidden] {
  563 + opacity: 0;
  564 + height: 0;
  565 + margin: 0;
445 } 566 }
446 567
447 .module-title { 568 .module-title {
@@ -449,6 +570,7 @@ $border-color: #e5e5e5; // 新增边框颜色变量 @@ -449,6 +570,7 @@ $border-color: #e5e5e5; // 新增边框颜色变量
449 font-size: 14px; 570 font-size: 14px;
450 font-weight: 600; 571 font-weight: 600;
451 color: $text-color-light; 572 color: $text-color-light;
  573 + flex-shrink: 0;
452 } 574 }
453 575
454 .task-chart-card { 576 .task-chart-card {
@@ -456,7 +578,7 @@ $border-color: #e5e5e5; // 新增边框颜色变量 @@ -456,7 +578,7 @@ $border-color: #e5e5e5; // 新增边框颜色变量
456 border-radius: $card-radius; 578 border-radius: $card-radius;
457 margin: 0 $spacing-lg $spacing-md; 579 margin: 0 $spacing-lg $spacing-md;
458 padding: $spacing-md; 580 padding: $spacing-md;
459 - 581 + flex-shrink: 0;
460 .card-header { 582 .card-header {
461 display: flex; 583 display: flex;
462 justify-content: space-between; 584 justify-content: space-between;
@@ -464,7 +586,6 @@ $border-color: #e5e5e5; // 新增边框颜色变量 @@ -464,7 +586,6 @@ $border-color: #e5e5e5; // 新增边框颜色变量
464 font-size: 12px; 586 font-size: 12px;
465 color: $text-color-placeholder; 587 color: $text-color-placeholder;
466 margin-bottom: $spacing-md; 588 margin-bottom: $spacing-md;
467 -  
468 .date-picker-wrap { 589 .date-picker-wrap {
469 padding: 2px 4px; 590 padding: 2px 4px;
470 border-radius: 3px; 591 border-radius: 3px;
@@ -476,7 +597,6 @@ $border-color: #e5e5e5; // 新增边框颜色变量 @@ -476,7 +597,6 @@ $border-color: #e5e5e5; // 新增边框颜色变量
476 } 597 }
477 } 598 }
478 } 599 }
479 -  
480 .chart-container { 600 .chart-container {
481 width: 100%; 601 width: 100%;
482 height: 150px; 602 height: 150px;
@@ -485,53 +605,124 @@ $border-color: #e5e5e5; // 新增边框颜色变量 @@ -485,53 +605,124 @@ $border-color: #e5e5e5; // 新增边框颜色变量
485 justify-content: center; 605 justify-content: center;
486 } 606 }
487 } 607 }
  608 +.con-wrap {
  609 + border-radius: 8px;
  610 + background-color: #fff;
  611 + margin: 0 15px;
  612 + flex: 1;
  613 + min-height: 0;
  614 + display: flex;
  615 + flex-direction: column;
  616 + overflow: hidden;
  617 +}
488 618
489 -.task-tab-container {  
490 - position: relative;  
491 - z-index: 4;  
492 - margin: 0 $spacing-lg $spacing-sm; 619 +.con-title {
  620 + padding: 10px 15px;
  621 + border-bottom: 1px solid #f0e9e9;
  622 + flex-shrink: 0;
  623 +}
  624 +
  625 +.custom-tab-wrap {
  626 + display: flex;
  627 + align-items: center;
  628 + gap: 20rpx;
  629 + padding: 20rpx 0 20rpx 20rpx;
  630 + background: #fff;
  631 + flex-shrink: 0;
  632 +}
  633 +
  634 +.tab-capsule {
  635 + padding: 10rpx 20rpx;
  636 + display: flex;
  637 + align-items: center;
  638 + justify-content: center;
  639 + font-size: 28rpx;
  640 + color: #999;
  641 + background: #f5f5f5;
  642 + border-radius: 0;
  643 + transition: all 0.2s ease;
  644 + font-weight: 500;
  645 +}
  646 +.tab-capsule.active {
  647 + color: #d4af37;
  648 + background: #fff8dc;
  649 +}
  650 +
  651 +.tip-header {
  652 + display: flex;
  653 + align-items: center;
  654 + gap: 8rpx;
  655 + padding: 12rpx 15rpx;
  656 + background: #e6f7ff;
  657 + font-size: 14px;
  658 + color: #000;
  659 + flex-shrink: 0;
  660 +}
  661 +
  662 +.tip-top {
  663 + padding: 8px 0 0 70px;
  664 + flex-shrink: 0;
493 } 665 }
494 666
495 .task-list-container { 667 .task-list-container {
496 - margin: 0 $spacing-lg;  
497 - background-color: $card-bg;  
498 - border-radius: $card-radius;  
499 - overflow: hidden; // 防止边框溢出圆角 668 + width: 100%;
  669 + height: 100%;
  670 + padding-bottom: 300rpx;
  671 + box-sizing: border-box;
  672 +}
  673 +
  674 +.empty-box {
  675 + padding: 60rpx 0;
500 } 676 }
501 677
502 .task-item { 678 .task-item {
503 padding: $spacing-md; 679 padding: $spacing-md;
504 transition: background-color 0.2s; 680 transition: background-color 0.2s;
505 border-bottom: 1px solid $border-color; 681 border-bottom: 1px solid $border-color;
506 -  
507 &:last-child { 682 &:last-child {
508 border-bottom: none; 683 border-bottom: none;
509 } 684 }
510 -  
511 &--hover { 685 &--hover {
512 background-color: $bg-color; 686 background-color: $bg-color;
513 } 687 }
514 -  
515 &__content { 688 &__content {
516 width: 100%; 689 width: 100%;
517 } 690 }
518 - 691 + .name-fixed-wrap {
  692 + display: flex;
  693 + }
  694 + .task-item__name {
  695 + width: 60px;
  696 + }
  697 + .task-name-text {
  698 + flex: 1;
  699 + font-size: 14px;
  700 + color: $text-color;
  701 + overflow: hidden;
  702 + white-space: nowrap;
  703 + text-overflow: ellipsis;
  704 + }
519 &__name { 705 &__name {
520 - font-size: 13px; 706 + font-size: 15px;
521 color: $text-color; 707 color: $text-color;
522 - margin-bottom: $spacing-sm; 708 + display: inline-block;
523 } 709 }
524 -  
525 &__footer { 710 &__footer {
526 display: flex; 711 display: flex;
  712 + align-items: flex-start;
  713 + gap: 6rpx;
  714 + font-size: 13px;
  715 + color: $text-color-light;
  716 + margin-top: $spacing-sm;
527 align-items: center; 717 align-items: center;
528 - justify-content: space-between;  
529 } 718 }
530 } 719 }
  720 +.loc-icon {
  721 + width: 20px;
  722 + height: 20px;
  723 +}
531 724
532 -// 紧急程度标签样式  
533 .urgency-tag { 725 .urgency-tag {
534 color: #7D7D7D; 726 color: #7D7D7D;
535 -  
536 } 727 }
537 </style> 728 </style>
538 \ No newline at end of file 729 \ No newline at end of file