Commit 03b006dc743efe38a64d91581f69b2739e050de3
1 parent
c5fdc60a
树修改
Showing
4 changed files
with
521 additions
and
123 deletions
api/tree-archive/tree-archive.js
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) => { |
| 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) => { |
| 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> | ... | ... |