Commit 03b006dc743efe38a64d91581f69b2739e050de3

Authored by 刘淇
1 parent c5fdc60a

树修改

api/tree-archive/tree-archive.js
1 1  
2   -import { post, get } from '@/common/utils/request';
  2 +import { post, get, put } from '@/common/utils/request';
3 3  
4 4  
5 5 /**
... ...
pages-sub/data/tree-archive/editTree.vue
1 1 <template>
2 2 <view class="container">
3   - <!-- ✅ 顶部固定吸顶Tabs区域 - 核心新增 -->
  3 + <!-- 顶部固定吸顶Tabs区域 -->
4 4 <up-sticky bg-color="#ffffff">
5 5 <view class="header-wrap">
6 6 <up-tabs
... ... @@ -15,33 +15,170 @@
15 15 </view>
16 16 </up-sticky>
17 17  
18   - <!-- ✅ Tab0 基本信息页面 (树木的详情编辑页,保留你原编辑页核心结构,这里给你做了标准基础版,可直接加表单) -->
  18 + <!-- Tab0 基本信息页面 - v-if改v-show 核心修复 -->
19 19 <view v-show="activeTab === 0" class="base-info-wrap">
20   - <up-card :border="false" :show-head="false" class="base-card">
21   - <view class="card-body-inner">
22   - <view class="record-list-left" :style="`background-image: url(${treeInfo.treephoto});`"></view>
23   - <view class="record-list-right">
24   - <view class="record-list-right-title">
25   - <view class="u-line-1 treetypeName">{{ treeInfo.treetype }}</view>
26   - </view>
27   - <view class="fs-mt8 fs-align__center">
28   - <img src="../../../static/imgs/tree/tree-high.png" style="width: 14px;height: 14px;margin-right: 6px;" alt=""> 高度:{{ treeInfo.treeheight }} 米
29   - </view>
30   - <view class="fs-mt8 fs-align__center">
31   - <img src="../../../static/imgs/tree/treearound.png" style="width: 14px;height: 14px;margin-right: 6px;" alt="">胸径:{{ treeInfo.dbh }} 厘米
32   - </view>
33   - <view class="fs-mt8 fs-align__center">
34   - <text>树木编号:{{ treeInfo.treenumber }}</text>
35   - </view>
  20 + <up-form :model="formData" ref="formRef" label-width="140rpx" border-bottom>
  21 + <up-form-item label="名称" prop="treetype" required>
  22 + <up-input v-model.trim="formData.treetype" placeholder="请输入名称" maxlength="30" border="none"/>
  23 + </up-form-item>
  24 +
  25 + <view class="form-row-wrap">
  26 + <up-row gutter="10">
  27 + <up-col span="6">
  28 + <up-form-item label="胸径" prop="dbh" required>
  29 + <up-input v-model.trim="formData.dbh" placeholder="请输入" maxlength="10" border="none"
  30 + input-align="left"/>
  31 + <template #right>
  32 + <text style="padding-left: 12rpx;color:#ccc;font-size:14px">厘米</text>
  33 + </template>
  34 + </up-form-item>
  35 + </up-col>
  36 + <up-col span="6">
  37 + <up-form-item label="高度" prop="treeheight">
  38 + <up-input v-model.trim="formData.treeheight" placeholder="请输入" maxlength="10" border="none"
  39 + input-align="left"/>
  40 + <template #right>
  41 + <text style="padding-left: 12rpx;color:#ccc;font-size:14px;">米</text>
  42 + </template>
  43 + </up-form-item>
  44 + </up-col>
  45 + </up-row>
  46 + </view>
  47 +
  48 + <up-form-item label="位置" prop="growlocation" required class="location-form-item" @click="openMap">
  49 + <up-input v-model="formData.growlocation" placeholder="请选择" readonly border="none"/>
  50 + <template #right>
  51 + <up-icon name="map" size="22"></up-icon>
  52 + </template>
  53 + </up-form-item>
  54 +
  55 + <up-row gutter="10">
  56 + <up-col span="6">
  57 + <up-form-item label="经度" :borderBottom="false">
  58 + <up-input v-model="formData.longitude" placeholder="" readonly border="none"/>
  59 + </up-form-item>
  60 + </up-col>
  61 + <up-col span="6">
  62 + <up-form-item label="纬度" :borderBottom="false">
  63 + <up-input v-model="formData.latitude" placeholder="" readonly border="none"/>
  64 + </up-form-item>
  65 + </up-col>
  66 + </up-row>
  67 +
  68 + <up-form-item label="管护单位" prop="managedutyunit" required>
  69 + <up-input v-model.trim="formData.managedutyunit" placeholder="请输入" maxlength="30" border="none"/>
  70 + </up-form-item>
  71 +
  72 + <up-form-item label="权属分类" prop="oldtreeownershipText" required arrow
  73 + @click="handleActionSheetOpen('ownership')">
  74 + <up-input v-model.trim="formData.oldtreeownershipText" placeholder="请选择" readonly border="none"
  75 + bg-color="transparent"/>
  76 + </up-form-item>
  77 +
  78 + <!-- ✅ 核心修复:补全name属性 + 所有配置齐全 -->
  79 + <up-form-item label="图片信息" prop="treeImgList" required>
  80 + <up-upload
  81 + name="treeImgList"
  82 + :file-list="treeImgList.imgList.value"
  83 + @after-read="treeImgList.uploadImgs"
  84 + @delete="treeImgList.deleteImg"
  85 + multiple
  86 + :width="70"
  87 + :height="70"
  88 + :max-count="treeImgList.uploadConfig.maxCount"
  89 + :upload-text="treeImgList.uploadConfig.uploadText"
  90 + :size-type="treeImgList.uploadConfig.sizeType"
  91 + ></up-upload>
  92 + </up-form-item>
  93 +
  94 + <view
  95 + class="animated-area"
  96 + :style="{
  97 + height: isShow ? contentHeight + 'px' : '0',
  98 + opacity: isShow ? 1 : 0,
  99 + overflow: 'hidden'
  100 + }"
  101 + >
  102 + <up-row gutter="10">
  103 + <up-col span="6">
  104 + <up-form-item label="拉丁文" prop="latinname">
  105 + <up-input v-model.trim="formData.latinname" placeholder="请输入" maxlength="30" border="none"/>
  106 + </up-form-item>
  107 + </up-col>
  108 + <up-col span="6">
  109 + <up-form-item label="级别" arrow @click="handleActionSheetOpen('level')">
  110 + <up-input v-model.trim="formData.treeleveltext" placeholder="请选择" readonly border="none"
  111 + bg-color="transparent"/>
  112 + </up-form-item>
  113 + </up-col>
  114 + </up-row>
  115 +
  116 + <up-form-item label="生长环境" prop="growthenvironment">
  117 + <up-input v-model.trim="formData.growthenvironment" placeholder="请输入" maxlength="50" border="none"/>
  118 + </up-form-item>
  119 +
  120 + <view class="form-row-wrap">
  121 + <up-row gutter="10">
  122 + <up-col span="6">
  123 + <up-form-item label="预估树龄" prop="estimationtreeage">
  124 + <up-input v-model.trim="formData.estimationtreeage" placeholder="请输入" maxlength="10" border="none"
  125 + input-align="left"/>
  126 + <template #right>
  127 + <text style="padding-left:12rpx;color:#ccc;font-size:14px">年</text>
  128 + </template>
  129 + </up-form-item>
  130 + </up-col>
  131 + <up-col span="6">
  132 + <up-form-item label="干周" prop="weekday">
  133 + <up-input v-model.trim="formData.weekday" placeholder="请输入" maxlength="10" border="none"
  134 + input-align="left"/>
  135 + <template #right>
  136 + <text style="padding-left:12rpx;color:#ccc;font-size:14px">厘米</text>
  137 + </template>
  138 + </up-form-item>
  139 + </up-col>
  140 + </up-row>
  141 + </view>
  142 +
  143 + <view class="form-row-wrap">
  144 + <up-row gutter="10">
  145 + <up-col span="6">
  146 + <up-form-item label="东西冠幅" prop="canopyeastwest">
  147 + <up-input v-model.trim="formData.canopyeastwest" placeholder="请输入" maxlength="10" border="none"
  148 + input-align="left"/>
  149 + <template #right>
  150 + <text style="padding-left:12rpx;color:#ccc;font-size:14px">米</text>
  151 + </template>
  152 + </up-form-item>
  153 + </up-col>
  154 + <up-col span="6">
  155 + <up-form-item label="南北冠幅" prop="canopysouthnorth">
  156 + <up-input v-model.trim="formData.canopysouthnorth" placeholder="请输入" maxlength="10" border="none"
  157 + input-align="left"/>
  158 + <template #right>
  159 + <text style="padding-left:12rpx;color:#ccc;font-size:14px">米</text>
  160 + </template>
  161 + </up-form-item>
  162 + </up-col>
  163 + </up-row>
36 164 </view>
37 165 </view>
38   - <!-- 这里可以继续加你的 编辑表单/其他基本信息 -->
39   - </up-card>
  166 +
  167 + <up-button
  168 + @click="toggleArea"
  169 + type="primary"
  170 + plain
  171 + size="large"
  172 + style="margin-top:20rpx"
  173 + >
  174 + {{ isShow ? '- 隐藏区域' : '+ 显示区域' }}
  175 + </up-button>
  176 + </up-form>
40 177 </view>
41 178  
42   - <!-- ✅ Tab1 变更日志页面 - 和列表页布局完全一致 + 无新增按钮 + 点击卡片跳转详情 -->
  179 + <!-- Tab1 变更日志页面 - 不变,保留原布局 -->
43 180 <view v-show="activeTab === 1" class="log-wrap">
44   - <up-empty v-if="logRows.length === 0" text="暂无变更日志"></up-empty>
  181 + <up-empty v-if="logRows.length === 0" marginTop="100" text="暂无变更日志"></up-empty>
45 182 <view class="record-wrap" v-else>
46 183 <up-card
47 184 v-for="item in logRows"
... ... @@ -54,7 +191,6 @@
54 191 >
55 192 <template #body>
56 193 <view class="card-body-inner">
57   - <!-- ✅ 保留你要求的背景图写法 -->
58 194 <view class="record-list-left" :style="`background-image: url(${item.treephotoone});`"></view>
59 195 <view class="record-list-right">
60 196 <view class="record-list-right-title">
... ... @@ -62,10 +198,12 @@
62 198 <view style="text-align: right">{{ timeFormat(item.updatetime) }}</view>
63 199 </view>
64 200 <view class=" fs-align__center" style="margin: 5px 0">
65   - <img src="../../../static/imgs/tree/tree-high.png" style="width: 14px;height: 14px;margin-right: 6px;" alt=""> 高度:{{ item.treeheight }} 米
  201 + <img src="../../../static/imgs/tree/tree-high.png" style="width:14px;height:14px;margin-right:6px;"
  202 + alt=""> 高度:{{ item.treeheight }} 米
66 203 </view>
67 204 <view class=" fs-align__center">
68   - <img src="../../../static/imgs/tree/treearound.png" style="width: 14px;height: 14px;margin-right: 6px;" alt="">胸径:{{ item.dbh }} 厘米
  205 + <img src="../../../static/imgs/tree/treearound.png" style="width:14px;height:14px;margin-right:6px;"
  206 + alt="">胸径:{{ item.dbh }} 厘米
69 207 </view>
70 208 </view>
71 209 </view>
... ... @@ -77,130 +215,376 @@
77 215 </view>
78 216 </view>
79 217  
80   - <!-- ✅ 新增按钮 只在【基本信息Tab】显示,变更日志Tab自动隐藏,无需手动处理 -->
81   - <view class="fixed-bottom-btn-wrap">
82   - <up-button
83   - type="primary"
84   - @click="toAddTreePage"
85   - v-show="count > 0 && count > rows.length && activeTab === 0"
86   - >
87   - 新增树木录入
88   - </up-button>
  218 + <!-- 修改:底部按钮 新增改修改,只在基本信息Tab显示 -->
  219 + <view class="fixed-bottom-btn-wrap" v-show="activeTab === 0">
  220 + <up-button type="primary" @click="submit" :loading="loadingFlag" size="large" >修改树木信息</up-button>
89 221 </view>
90 222  
  223 + <!-- 字典选择弹窗 -->
  224 + <up-action-sheet
  225 + :show="showActionSheet"
  226 + :actions="currentActionSheetData.list"
  227 + :title="currentActionSheetData.title"
  228 + @close="handleActionSheetClose"
  229 + @select="handleActionSheetSelect"
  230 + ></up-action-sheet>
  231 +
91 232 </view>
92 233 </template>
93 234  
94 235 <script setup>
95   -import { ref, reactive } from 'vue'
  236 +import { ref, reactive, nextTick } from 'vue'
96 237 import { onReady, onLoad, onShow, onUnload } from '@dcloudio/uni-app';
  238 +// 接口修改:删除addTree,新增 treeDetailReq + updateTree
  239 +import { treeRoadReq, treeLogReq, treeDetailReq, updateTree } from "@/api/tree-archive/tree-archive.js";
  240 +import { timeFormat } from '@/uni_modules/uview-plus';
  241 +import { useUploadImgs } from '@/common/utils/useUploadImgs'
  242 +import { useUserStore } from '@/pinia/user';
  243 +
  244 +// ========== 常量配置区 - 完全对标参考页写法 集中管理 ==========
  245 +const CONST = {
  246 + // 上传配置 统一抽离,和参考页一致
  247 + UPLOAD_CONFIG: { maxCount: 3, uploadText: '选择图片', sizeType: ['compressed'] }
  248 +}
  249 +
  250 +// ========== 页面关闭统一跳转 ==========
97 251 onUnload(() => {
98   - // 关闭所有页面,直接打开【行道树档案】主页面 【微信小程序完美兼容,无任何报错】
99   - uni.reLaunch({
100   - url: '/pages-sub/data/tree-archive/index'
101   - })
  252 + uni.reLaunch({url: '/pages-sub/data/tree-archive/index'})
102 253 })
103   -import { treeRoadReq,treeLogReq } from "@/api/tree-archive/tree-archive.js";
104   -import { timeFormat } from '@/uni_modules/uview-plus';
105 254  
106 255 // ========== 基础变量 ==========
107 256 const rows = ref([])
108 257 const roadId = ref('')
109 258 const count = ref(0)
110   -const treeId = ref('')
  259 +const treeId = ref('') // 树木主键ID 必传修改接口
  260 +const loadingFlag = ref(false)
  261 +const isInit = ref(false) // ✅ 新增:防止重复加载回显数据的核心标识
111 262  
112 263 // ========== Tab切换核心变量 ==========
113 264 const activeTab = ref(0); // 0=基本信息 1=变更日志
114   -const tabList = ref([
115   - { name: '基本信息' },
116   - { name: '变更日志' }
117   -]);
  265 +const tabList = ref([{name: '基本信息'}, {name: '变更日志'}]);
118 266  
119 267 // ========== 数据变量 ==========
120   -const treeInfo = reactive({ // 基本信息-单棵树详情
121   - treephoto: '',
  268 +const logRows = ref([]) // 变更日志列表数据
  269 +const userStore = useUserStore();
  270 +const formRef = ref(null)
  271 +const isShow = ref(false)
  272 +const contentHeight = ref(200)
  273 +const treeOwnershipData = ref([])
  274 +const treeLevelData = ref([])
  275 +const showActionSheet = ref(false);
  276 +const currentActionSheetData = reactive({type: '', list: [], title: ''});
  277 +
  278 +// ========== 核心修复:图片上传配置 - 1:1对标工单页面 无任何冗余 ==========
  279 +const treeImgList = useUploadImgs({
  280 + ...CONST.UPLOAD_CONFIG,
  281 + formRef: formRef,
  282 + fieldName: 'treeImgList'
  283 +})
  284 +// ✅ 初始化图片数组为纯净空数组,杜绝残留数据
  285 +treeImgList.imgList.value = []
  286 +treeImgList.rawImgList.value = []
  287 +
  288 +// ========== 表单数据 ==========
  289 +const formData = reactive({
  290 + id: '', // 新增主键ID,提交修改必传
122 291 treetype: '',
123 292 treeheight: '',
124 293 dbh: '',
125   - treenumber: '',
126   - updatetime: ''
  294 + treelevel: '',
  295 + treeleveltext: '',
  296 + managedutyunit: '',
  297 + oldtreeownership: '',
  298 + oldtreeownershipText: '',
  299 + latinname: '',
  300 + estimationtreeage: '',
  301 + canopysouthnorth: '',
  302 + canopyeastwest: '',
  303 + weekday: '',
  304 + growlocation: '',
  305 + growthenvironment: '',
  306 + treeImgList: [],
  307 + address: '',
  308 + latitude: '',
  309 + longitude: '',
  310 + road: '',
  311 + maintainunit: '',
  312 + treephotoone: '',
  313 + treephototwo: '',
  314 + treephotothree: '',
  315 + treephotofour: '',
  316 + treephotofive: ''
  317 +})
  318 +
  319 +// ========== 表单校验规则【完整保留,无修改】 ==========
  320 +const isPureZero = (val) => {
  321 + if (!val) return false;
  322 + const pureZeroReg = /^0+$/;
  323 + return pureZeroReg.test(val.trim());
  324 +};
  325 +const trimFormData = (obj) => {
  326 + for (const key in obj) {
  327 + if (Object.prototype.hasOwnProperty.call(obj, key) && typeof obj[key] === 'string') {
  328 + obj[key] = obj[key].trim();
  329 + }
  330 + }
  331 +};
  332 +const emojiReg = /[\uD83C\uD83D\uD83E][\uDC00-\uDFFF]+|[\u2702\u2705\u2708-\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763-\u2767\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299\ufe0f\u200d]+/g;
  333 +const filterEmoji = (val) => typeof val === 'string' ? val.replace(emojiReg, '') : val;
  334 +const hasEmoji = (val) => val && emojiReg.test(val);
  335 +const rules = reactive({
  336 + treetype: [
  337 + {
  338 + validator: (rule, val, callback) => hasEmoji(val) ? callback(new Error('禁止输入表情符号')) : callback(),
  339 + trigger: ['blur', 'change', 'input'],
  340 + priority: 10
  341 + },
  342 + {required: true, message: '请输入名称', trigger: ['blur', 'change', 'input']},
  343 + {max: 30, message: '名称最多输入30个字符', trigger: ['blur', 'change', 'input']}
  344 + ],
  345 + treeheight: [
  346 + {max: 10, message: '树高不能超过10个字符', trigger: ['blur', 'change', 'input']},
  347 + {pattern: /^\d+(\.\d{1,2})?$/, message: '格式不正确,支持数字或两位小数', trigger: ['blur', 'change', 'input']},
  348 + {
  349 + validator: (rule, val, callback) => isPureZero(val) ? callback(new Error('不能输入纯0无效值')) : callback(),
  350 + trigger: ['blur', 'change', 'input']
  351 + }
  352 + ],
  353 + dbh: [
  354 + {
  355 + validator: (rule, val, callback) => hasEmoji(val) ? callback(new Error('禁止输入表情符号')) : callback(),
  356 + trigger: ['blur', 'change', 'input'],
  357 + priority: 10
  358 + },
  359 + {required: true, message: '请输入胸径', trigger: ['blur', 'change', 'input']},
  360 + {max: 10, message: '胸径不能超过10个字符', trigger: ['blur', 'change', 'input']},
  361 + {pattern: /^\d+(\.\d{1,2})?$/, message: '格式不正确,支持数字或两位小数', trigger: ['blur', 'change', 'input']},
  362 + {
  363 + validator: (rule, val, callback) => isPureZero(val) ? callback(new Error('不能输入纯0无效值')) : callback(),
  364 + trigger: ['blur', 'change', 'input']
  365 + }
  366 + ],
  367 + estimationtreeage: [
  368 + {max: 10, message: '预估树龄不能超过10个字符', trigger: ['blur', 'change', 'input']},
  369 + {pattern: /^\d+(\.\d{1,2})?$/, message: '格式不正确,支持数字或两位小数', trigger: ['blur', 'change', 'input']},
  370 + {
  371 + validator: (rule, val, callback) => isPureZero(val) ? callback(new Error('不能输入纯0无效值')) : callback(),
  372 + trigger: ['blur', 'change', 'input']
  373 + }
  374 + ],
  375 + weekday: [
  376 + {max: 10, message: '干周不能超过10个字符', trigger: ['blur', 'change', 'input']},
  377 + {pattern: /^\d+(\.\d{1,2})?$/, message: '格式不正确,支持数字或两位小数', trigger: ['blur', 'change', 'input']},
  378 + {
  379 + validator: (rule, val, callback) => isPureZero(val) ? callback(new Error('不能输入纯0无效值')) : callback(),
  380 + trigger: ['blur', 'change', 'input']
  381 + }
  382 + ],
  383 + canopyeastwest: [
  384 + {max: 10, message: '东西冠幅不能超过10个字符', trigger: ['blur', 'change', 'input']},
  385 + {pattern: /^\d+(\.\d{1,2})?$/, message: '格式不正确,支持数字或两位小数', trigger: ['blur', 'change', 'input']},
  386 + {
  387 + validator: (rule, val, callback) => isPureZero(val) ? callback(new Error('不能输入纯0无效值')) : callback(),
  388 + trigger: ['blur', 'change', 'input']
  389 + }
  390 + ],
  391 + canopysouthnorth: [
  392 + {max: 10, message: '南北冠幅不能超过10个字符', trigger: ['blur', 'change', 'input']},
  393 + {pattern: /^\d+(\.\d{1,2})?$/, message: '格式不正确,支持数字或两位小数', trigger: ['blur', 'change', 'input']},
  394 + {
  395 + validator: (rule, val, callback) => isPureZero(val) ? callback(new Error('不能输入纯0无效值')) : callback(),
  396 + trigger: ['blur', 'change', 'input']
  397 + }
  398 + ],
  399 + growlocation: [{required: true, message: '请选择地图位置', trigger: ['blur', 'change', 'manual']}],
  400 + managedutyunit: [
  401 + {
  402 + validator: (rule, val, callback) => hasEmoji(val) ? callback(new Error('禁止输入表情符号')) : callback(),
  403 + trigger: ['blur', 'change', 'input'],
  404 + priority: 10
  405 + },
  406 + {required: true, message: '请输入管护单位', trigger: ['blur', 'change', 'input']},
  407 + {max: 30, message: '管护单位最多输入30个字符', trigger: ['blur', 'change', 'input']}
  408 + ],
  409 + oldtreeownershipText: [{required: true, message: '请选择权属分类', trigger: ['blur', 'change']}],
  410 + latinname: [
  411 + {
  412 + validator: (rule, val, callback) => hasEmoji(val) ? callback(new Error('禁止输入表情符号')) : callback(),
  413 + trigger: ['blur', 'change', 'input'],
  414 + priority: 10
  415 + },
  416 + {max: 30, message: '拉丁文最多输入30个字符', trigger: ['blur', 'change', 'input']}
  417 + ],
  418 + growthenvironment: [
  419 + {
  420 + validator: (rule, val, callback) => hasEmoji(val) ? callback(new Error('禁止输入表情符号')) : callback(),
  421 + trigger: ['blur', 'change', 'input'],
  422 + priority: 10
  423 + },
  424 + {max: 50, message: '生长环境最多输入50个字符', trigger: ['blur', 'change', 'input']}
  425 + ],
  426 + treeImgList: [treeImgList.imgValidateRule]
127 427 })
128   -const logRows = ref([]) // 变更日志列表数据
129 428  
130 429 // ========== 生命周期 ==========
131 430 onLoad((options) => {
132 431 console.log('编辑页入参', options)
133 432 roadId.value = options.roadId || ''
134 433 count.value = options.count || 0
135   - treeId.value = options.id || '' // 树木主键ID
136   -})
137   -
138   -onShow(() => {
139   - treeRoadQuery()
140   - if(activeTab.value === 0){
141   - // getTreeDetail() // 进入页面默认加载基本信息
  434 + treeId.value = options.id || '' // 获取树木主键ID
  435 + formData.id = treeId.value // 赋值给表单,提交必传
  436 + formData.road = roadId.value // 赋值道路ID
  437 + isInit.value = false // 初始化标识为false
  438 +});
  439 +
  440 +onShow(async () => {
  441 + treeLevelData.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('tree_level'))
  442 + treeOwnershipData.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('tree_ownership'))
  443 + // ✅ 核心修复:只执行一次回显加载,彻底杜绝重复加载
  444 + if (activeTab.value === 0 && treeId.value && !isInit.value) {
  445 + await getTreeDetail()
  446 + isInit.value = true // 加载完成后标记为true,永不重复执行
142 447 }
143 448 })
144 449  
  450 +onReady(() => {
  451 + nextTick(() => formRef.value?.setRules(rules));
  452 +});
145 453  
146 454 // ========== Tab切换事件 ==========
147   -const handleTabChange = (item) => {
  455 +const handleTabChange = async (item) => {
148 456 activeTab.value = item.index
149   - // 切换到哪个tab,加载对应的数据
150   - if(activeTab.value === 0){
151   - getTreeDetail() // 加载基本信息
152   - }else if(activeTab.value === 1){
153   - getTreeLogList() // 加载变更日志列表
  457 + if (activeTab.value === 0) {
  458 + getTreeLogList()
  459 + } else if (activeTab.value === 1) {
  460 + getTreeLogList()
154 461 }
155 462 }
156 463  
157 464 // ========== 接口请求 ==========
158   -// 树木列表查询
159 465 const treeRoadQuery = async () => {
160   - const res = await treeRoadReq( {road: roadId.value})
  466 + const res = await treeRoadReq({road: roadId.value})
161 467 rows.value = res.list
162 468 }
163 469  
164   -// // 获取单棵树的基本信息
165   -// const getTreeDetail = async () => {
166   -// const res = await treeDetailReq({ id: treeId.value })
167   -// Object.assign(treeInfo, res)
168   -// }
  470 +// ========== ✅ 终极修复:图片回显逻辑 - 彻底解决重复渲染4张图的问题 ==========
  471 +const getTreeDetail = async () => {
  472 + const res = await treeDetailReq({id: treeId.value})
  473 + Object.assign(formData, res)
  474 + // 字典文本回显
  475 + formData.oldtreeownershipText = uni.$dict.getDictLabel('tree_ownership', res.oldtreeownership)
  476 + formData.treeleveltext = uni.$dict.getDictLabel('tree_level', formData.treelevel)
  477 +
  478 + // ✅ 第一步:强制清空为纯净空数组,杜绝任何残留
  479 + treeImgList.imgList.value = []
  480 + treeImgList.rawImgList.value = []
  481 + formData.treephotoone = ''
  482 + formData.treephototwo = ''
  483 + formData.treephotoone = ''
  484 + formData.treephotothree = ''
  485 + formData.treephotofour = ''
  486 + formData.treephotofive = ''
  487 +
  488 + // ✅ 第二步:接口返回几张就渲染几张,绝对不会重复
  489 + if (Array.isArray(res.treeImgList) && res.treeImgList.length > 0) {
  490 + const imgList = res.treeImgList.map((url, idx) => ({
  491 + url,
  492 + name: `renew_img_${idx}`,
  493 + status: 'success'
  494 + }));
  495 + // 直接赋值,不再追加,彻底解决重复问题
  496 + treeImgList.imgList.value = imgList
  497 + treeImgList.rawImgList.value = imgList
  498 + }
  499 +}
169 500  
170 501 // 获取树木的变更日志列表
171 502 const getTreeLogList = async () => {
172   - const res = await treeLogReq({ treeid: treeId.value })
  503 + const res = await treeLogReq({treeid: treeId.value})
173 504 logRows.value = res.list
174 505 }
175 506  
176   -// ========== 页面跳转 ==========
177   -// 前往修改页面(原有)
178   -const toEditPage = (id) => {
179   - uni.navigateTo({
180   - url: `/pages-sub/data/tree-archive/editTree?id=${id}&roadId=${roadId.value}`
181   - })
  507 +// ========== 表单相关方法 ==========
  508 +const toggleArea = () => isShow.value = !isShow.value
  509 +
  510 +const handleActionSheetOpen = (type) => {
  511 + const configMap = {
  512 + ownership: {title: '请选择权属分类', list: treeOwnershipData.value},
  513 + level: {title: '请选择树木级别', list: treeLevelData.value}
  514 + };
  515 + Object.assign(currentActionSheetData, configMap[type], {type});
  516 + showActionSheet.value = true;
  517 +};
  518 +
  519 +const handleActionSheetClose = () => {
  520 + showActionSheet.value = false;
  521 + Object.assign(currentActionSheetData, {type: '', list: [], title: ''});
  522 +};
  523 +
  524 +const handleActionSheetSelect = (e) => {
  525 + const {type} = currentActionSheetData;
  526 + if (type === 'ownership') {
  527 + formData.oldtreeownership = e.value
  528 + formData.oldtreeownershipText = e.name
  529 + formRef.value?.validateField('oldtreeownershipText');
  530 + } else if (type === 'level') {
  531 + formData.treelevel = e.value
  532 + formData.treeleveltext = e.name
  533 + }
  534 + handleActionSheetClose();
  535 +};
  536 +
  537 +// 地图选址
  538 +const openMap = () => {
  539 + uni.chooseLocation({
  540 + success: async (res) => {
  541 + formData.growlocation = res.address
  542 + formData.latitude = res.latitude
  543 + formData.longitude = res.longitude
  544 + await nextTick()
  545 + formRef.value?.validateField('growlocation');
  546 + },
  547 + fail: (err) => {
  548 + console.error('地图选择失败', err);
  549 + err.errMsg.includes('auth deny') && uni.showToast({title: '请授权位置权限', icon: 'none'});
  550 + }
  551 + });
182 552 }
183 553  
184   -// 前往新增页面(原有)
185   -const toAddTreePage = () => {
186   - uni.navigateTo({
187   - url: `/pages-sub/data/tree-archive/addTree?roadId=${roadId.value}`
188   - })
  554 +// ========== 核心修改:表单提交 ==========
  555 +const submit = async () => {
  556 + if (loadingFlag.value) return
  557 + // 过滤表情+去空格
  558 + for (const key in formData) {
  559 + if (typeof formData[key] === 'string') {
  560 + formData[key] = filterEmoji(formData[key]).trim();
  561 + }
  562 + }
  563 + // 表单校验
  564 + const valid = await formRef.value.validate()
  565 + if (!valid) return
  566 + // 对标参考页:图片取值 统一调用封装方法
  567 + const uploadImgUrls = treeImgList.getSuccessImgUrls()
  568 + formData.maintainunit = userStore.userInfo.user.companyId
  569 + formData.treeImgList = uploadImgUrls
  570 + loadingFlag.value = true
  571 + try {
  572 + await updateTree(formData)
  573 + uni.showToast({title: "修改成功", icon: "success"});
  574 + uni.redirectTo({url: '/pages-sub/data/tree-archive/index'});
  575 + } catch (err) {
  576 + uni.showToast({title: "修改失败,请重试", icon: "none"});
  577 + console.error(err)
  578 + } finally {
  579 + loadingFlag.value = false
  580 + }
189 581 }
190 582  
191   -// ✅ 新增:点击变更日志卡片 前往日志详情页面
  583 +// ========== 页面跳转 ==========
192 584 const toLogDetailPage = (i) => {
193 585 uni.navigateTo({
194   - // url: `/pages-sub/data/tree-archive/logDetail?id=${logId}&treeId=${treeId.value}`
195 586 url: `/pages-sub/data/tree-archive/logDetail`,
196   - events: {
197   - sendBackData: (data) => {
198   - console.log('B页面回传的数据:', data);
199   - }
200   - },
201   - // 2. 向B页面传递数据
202 587 success: (res) => {
203   - // 通过 eventChannel 发送数据,key
204 588 res.eventChannel.emit('logData', i);
205 589 }
206 590 })
... ... @@ -210,29 +594,23 @@ const toLogDetailPage = (i) =&gt; {
210 594 <style scoped lang="scss">
211 595 .container {
212 596 min-height: 100vh;
  597 + padding-bottom: 100rpx;
213 598 }
214 599  
215   -// ✅ 顶部tabs吸顶样式
216 600 .header-wrap {
217 601 background-color: #fff;
218 602 padding: 10rpx 0;
219   - box-shadow: 0 2rpx 4rpx rgba(0,0,0,0.03);
  603 + box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.03);
220 604 }
221 605  
222   -// 基本信息样式
223 606 .base-info-wrap {
224   - padding: 20rpx 10rpx;
225   -}
226   -.base-card {
227   - background: #fff;
228   - border-radius: 6px;
229   - padding: 10px;
  607 + padding: 20rpx 15rpx;
230 608 }
231 609  
232   -// 变更日志样式 和列表页完全一致
233 610 .log-wrap {
234 611 min-height: calc(100vh - 120rpx);
235 612 }
  613 +
236 614 .record-wrap {
237 615 padding-bottom: 20px;
238 616 }
... ... @@ -269,30 +647,48 @@ const toLogDetailPage = (i) =&gt; {
269 647 margin-top: 8px;
270 648 }
271 649  
272   -// up-card样式适配
273   -.tree-card {
274   - //margin: 15px 10px 0;
275   - //padding: 10px;
276   - //border-radius: 6px;
277   - //font-size: 14px;
278   - //box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
279   - //background-color: #ffffff;
280   -}
281   -.card-body-inner{
  650 +.card-body-inner {
282 651 display: flex;
283 652 }
284 653  
  654 +.fs-align__center {
  655 + display: flex;
  656 + align-items: center;
  657 +}
285 658  
286   -.addTree {
287   - width: 100%;
288   - position: fixed;
289   - bottom: 0;
  659 +// 表单样式
  660 +.animated-area {
  661 + transition: all 0.3s ease-out;
  662 + margin-top: 10rpx;
290 663 }
291 664  
  665 +.location-form-item {
  666 + padding-right: 10rpx;
  667 +}
292 668  
293   -.fs-align__center {
  669 +.form-row-wrap {
  670 + width: 100%;
294 671 display: flex;
295   - align-items: center;
  672 + flex-direction: column;
  673 +
  674 + :deep(.u-form-item) {
  675 + position: relative;
  676 + margin-bottom: 0 !important;
  677 + }
  678 +
  679 + :deep(.u-form-item__body__right__message ) {
  680 + position: absolute;
  681 + left: 0;
  682 + bottom: -20rpx;
  683 + line-height: 20rpx;
  684 + font-size: 22rpx;
  685 + color: #f56c6c;
  686 + width: 100%;
  687 + box-sizing: border-box;
  688 + }
296 689 }
297 690  
  691 +.form-row-wrap + .u-form-item {
  692 + margin-top: 25rpx !important;
  693 +}
298 694 </style>
299 695 \ No newline at end of file
... ...
pages-sub/data/tree-archive/index.vue
... ... @@ -57,7 +57,8 @@
57 57 <up-col span="9" class="cate-right">
58 58 <scroll-view scroll-y class="right-scroll">
59 59 <!-- 暂无数据 -->
60   - <empty-view v-if="!hasValidRoadId"></empty-view>
  60 +<!-- <empty-view v-if="!hasValidRoadId" style="margin-top: 100px"></empty-view>-->
  61 + <up-empty v-if="!hasValidRoadId" marginTop="100" mode="list"></up-empty>
61 62 <!-- 道路数据列表 -->
62 63 <view v-else class="road-list-wrap">
63 64 <view
... ... @@ -67,10 +68,10 @@
67 68 @click="toNewPage(item.roadId, item.treeCount)"
68 69 >
69 70 <p class="fs-flex__between">
70   - <span class="up-line-1">{{ item.roadName }}</span>
71   - <span class="treeCount">{{ item.treeCount }}棵</span>
  71 + <span class="up-line-1" style="color: #333;font-size: 14px">{{ item.roadName }}</span>
  72 + <span class="treeCount" style="font-size: 14px">{{ item.treeCount }}棵</span>
72 73 </p>
73   - <p class="up-line-1 fs-my8">已录入行道树:{{ item.recordedCount }}棵</p>
  74 + <p class="up-line-1 fs-my8" style="color: #333;font-size: 14px">已录入行道树:{{ item.recordedCount }}棵</p>
74 75 <p class="up-line-1 fs-my8" style="color: #999;font-size: 12px">起点:{{ item.startRemark }}</p>
75 76 <p class="up-line-1" style="color: #999;font-size: 12px">终点:{{ item.endRemark }}</p>
76 77 </view>
... ...
pages-sub/data/tree-archive/logDetail.vue
... ... @@ -60,6 +60,7 @@
60 60 :height="70"
61 61 :maxCount="formatImgList(formData.treeImgList).length"
62 62 disabled
  63 + :deletable="false"
63 64 :preview-full-image="true"
64 65 ></up-upload>
65 66 </up-form-item>
... ...