Commit 8ddc6f6e1f715ff8aac9b30b12afdb28305fa35e

Authored by 刘淇
1 parent 6e9f0396

登录 修改样式

api/common.js
@@ -11,6 +11,16 @@ export const getRoadListByLatLng = (params) => { @@ -11,6 +11,16 @@ export const getRoadListByLatLng = (params) => {
11 return get('/app-api/bpm/garden/workorder/listRoadInfo', params); 11 return get('/app-api/bpm/garden/workorder/listRoadInfo', params);
12 }; 12 };
13 13
  14 +
  15 +/**
  16 + * 根据公司
  17 + * @param {Object} params {mobile, password, code}
  18 + * @returns {Promise}
  19 + */
  20 +export const getCompanyList = (params) => {
  21 + return get('/admin-api/system/dept/sub-list', params);
  22 +};
  23 +
14 // export const fileUpload = (params) => { 24 // export const fileUpload = (params) => {
15 // return post('/app-api/infra/file/upload', params); 25 // return post('/app-api/infra/file/upload', params);
16 // }; 26 // };
api/tree-archive/tree-archive.js 0 → 100644
  1 +
  2 +import { post, get } from '@/common/utils/request';
  3 +
  4 +
  5 +/**
  6 + * 班组集合
  7 + * @param {Object} params
  8 + * @returns {Promise}
  9 + */
  10 +export const deptListReq = (params) => {
  11 + return get('/app-api/garden/tree/dept/list', params);
  12 +};
  13 +
  14 +/**
  15 + * 道路树木列表
  16 + * @param {Object} params
  17 + * @returns {Promise}
  18 + */
  19 +export const treeRoadReq = (params) => {
  20 + return get('/app-api/garden/tree/road/list', params);
  21 +};
  22 +
  23 +
  24 +/**
  25 + * 新增树
  26 + * @param {Object} params
  27 + * @returns {Promise}
  28 + */
  29 +export const addTree = (params) => {
  30 + return get('/app-api/garden/tree/create', params);
  31 +};
  32 +
  33 +
  34 +
  35 +// // 树基本详情
  36 +// export const treeDetailReq = (params) => request.get('/business/tree/'+params )
  37 +// ==>
  38 +// /app-api/garden/tree/get
  39 +//
  40 +// // 修改树
  41 +// export const updateTree = (params) => request.put('/business/tree/',params )
  42 +// ==>
  43 +// /app-api/garden/tree/update
  44 +//
  45 +// // 树更变记录
  46 +// export const treeLogReq = (params) => request.get('/gardentree/logs/list/',params )
  47 +// ==>
  48 +// /app-api/garden/tree-change-log/page
pages-sub/data/tree-archive/addTree.vue 0 → 100644
  1 +<template>
  2 + <view class="container">
  3 + <up-form :model="formData" ref="formRef" label-width="140rpx" border-bottom>
  4 + <up-form-item label="名称" prop="treetype" required>
  5 + <up-input v-model="formData.treetype" placeholder="请输入名称" maxlength="30" border="none" />
  6 + </up-form-item>
  7 +
  8 + <up-row gutter="10">
  9 + <up-col span="6">
  10 + <up-form-item label="胸径" prop="dbh" required>
  11 + <up-input v-model="formData.dbh" placeholder="请输入" maxlength="10" border="none" input-align="left" />
  12 + <template #right>
  13 + <text style="padding-left: 12rpx;color:#ccc;font-size:12px">厘米</text>
  14 + </template>
  15 + </up-form-item>
  16 + </up-col>
  17 + <up-col span="6">
  18 + <up-form-item label="高度" prop="treeheight">
  19 + <up-input v-model="formData.treeheight" placeholder="请输入" maxlength="10" border="none" input-align="left" />
  20 + <template #right>
  21 + <text style="padding-left: 12rpx;color:#ccc;font-size:14px;">米</text>
  22 + </template>
  23 + </up-form-item>
  24 + </up-col>
  25 + </up-row>
  26 +
  27 + <up-form-item label="位置" prop="growlocation" required class="location-form-item" @click="openMap">
  28 + <up-input v-model="formData.growlocation" placeholder="请选择" disabled border="none" />
  29 + <template #right>
  30 + <up-icon name="map" size="22"></up-icon>
  31 + </template>
  32 + </up-form-item>
  33 +
  34 + <up-row gutter="10">
  35 + <up-col span="6">
  36 + <up-form-item label="经度" label-color="#ccc" prop="longitude" border-bottom="none">
  37 + <up-input v-model="formData.longitude" placeholder="" disabled border="none" />
  38 + </up-form-item>
  39 + </up-col>
  40 + <up-col span="6">
  41 + <up-form-item label="纬度" label-color="#ccc" prop="latitude" border-bottom="none">
  42 + <up-input v-model="formData.latitude" placeholder="" disabled border="none" />
  43 + </up-form-item>
  44 + </up-col>
  45 + </up-row>
  46 +
  47 + <up-form-item label="管护单位" prop="managedutyunit" required>
  48 + <up-input v-model="formData.managedutyunit" placeholder="请输入" maxlength="30" border="none" />
  49 + </up-form-item>
  50 +
  51 + <up-form-item label="权属分类" prop="oldtreeownershipText" required arrow @click="pickerShow = true">
  52 + <up-input v-model="formData.oldtreeownershipText" placeholder="请选择" disabled border="none" bg-color="transparent" />
  53 + </up-form-item>
  54 +
  55 + <!-- ✅✅✅ 核心修改:图片上传 完全对标参考代码写法 start -->
  56 + <up-form-item label="图片信息" prop="treeImgList" required>
  57 + <up-upload
  58 + :file-list="treeImgs.imgList.value || []"
  59 + @after-read="treeImgs.uploadImgs"
  60 + @delete="treeImgs.deleteImg"
  61 + multiple
  62 + :width="70"
  63 + :height="70"
  64 + :max-count="treeImgs.uploadConfig.maxCount"
  65 + :upload-text="treeImgs.uploadConfig.uploadText"
  66 + :size-type="treeImgs.uploadConfig.sizeType"
  67 + ></up-upload>
  68 + </up-form-item>
  69 + <!-- ✅✅✅ 图片上传 修改结束 -->
  70 +
  71 + <!-- 动画区域(初始隐藏)保留原逻辑 -->
  72 + <view
  73 + class="animated-area"
  74 + :style="{
  75 + height: isShow ? contentHeight + 'px' : '0',
  76 + opacity: isShow ? 1 : 0,
  77 + overflow: 'hidden'
  78 + }"
  79 + >
  80 + <up-row gutter="10">
  81 + <up-col span="6">
  82 + <up-form-item label="拉丁文" prop="latinname">
  83 + <up-input v-model="formData.latinname" placeholder="请输入" maxlength="30" border="none" />
  84 + </up-form-item>
  85 + </up-col>
  86 + <up-col span="6">
  87 + <up-form-item label="级别" arrow @click="levelshow = true">
  88 + <up-input v-model="formData.treeleveltext" placeholder="请选择" disabled border="none" bg-color="transparent" />
  89 + </up-form-item>
  90 + </up-col>
  91 + </up-row>
  92 +
  93 + <up-form-item label="生长环境" prop="growthenvironment">
  94 + <up-input v-model="formData.growthenvironment" placeholder="请输入" maxlength="50" border="none" />
  95 + </up-form-item>
  96 +
  97 + <up-row gutter="10">
  98 + <up-col span="6">
  99 + <up-form-item label="预估树龄" prop="estimationtreeage">
  100 + <up-input v-model="formData.estimationtreeage" placeholder="请输入" maxlength="10" border="none" input-align="left" />
  101 + <template #right>
  102 + <text style="padding-left:12rpx;color:#ccc;font-size:14px">年</text>
  103 + </template>
  104 + </up-form-item>
  105 + </up-col>
  106 + <up-col span="6">
  107 + <up-form-item label="干周" prop="weekday">
  108 + <up-input v-model="formData.weekday" placeholder="请输入" maxlength="10" border="none" input-align="left" />
  109 + <template #right>
  110 + <text style="padding-left:12rpx;color:#ccc;font-size:14px">厘米</text>
  111 + </template>
  112 + </up-form-item>
  113 + </up-col>
  114 + </up-row>
  115 +
  116 + <up-row gutter="10">
  117 + <up-col span="6">
  118 + <up-form-item label="东西冠幅" prop="canopyeastwest">
  119 + <up-input v-model="formData.canopyeastwest" placeholder="请输入" maxlength="10" border="none" input-align="left" />
  120 + <template #right>
  121 + <text style="padding-left:12rpx;color:#ccc;font-size:14px">米</text>
  122 + </template>
  123 + </up-form-item>
  124 + </up-col>
  125 + <up-col span="6">
  126 + <up-form-item label="南北冠幅" prop="canopysouthnorth">
  127 + <up-input v-model="formData.canopysouthnorth" placeholder="请输入" maxlength="10" border="none" input-align="left" />
  128 + <template #right>
  129 + <text style="padding-left:12rpx;color:#ccc;font-size:14px">米</text>
  130 + </template>
  131 + </up-form-item>
  132 + </up-col>
  133 + </up-row>
  134 + </view>
  135 +
  136 + <!-- 触发按钮 -->
  137 + <up-button
  138 + @click="toggleArea"
  139 + type="primary"
  140 + plain
  141 + size="large"
  142 + style="margin-top:20rpx"
  143 + >
  144 + {{ isShow ? '- 隐藏区域' : '+ 显示区域' }}
  145 + </up-button>
  146 + </up-form>
  147 +
  148 + <view style="height: 60px;width: 100%"></view>
  149 +
  150 + <!-- 底部提交按钮 -->
  151 + <view class="fixed-bottom-btn-wrap">
  152 + <up-button type="primary" @click="submit" :loading="loadingFlag" size="large" bold>提交</up-button>
  153 + </view>
  154 +
  155 + <up-picker
  156 + v-model="pickerShow"
  157 + :list="treeOwnershipData"
  158 + label-key="dictLabel"
  159 + value-key="dictValue"
  160 + @confirm="pickerChange"
  161 + ></up-picker>
  162 +
  163 + <up-picker
  164 + v-model="levelshow"
  165 + :list="treeLevelData"
  166 + label-key="dictLabel"
  167 + value-key="dictValue"
  168 + @confirm="pickerLevelChange"
  169 + ></up-picker>
  170 + </view>
  171 +</template>
  172 +
  173 +<script setup>
  174 +import { ref, reactive, nextTick } from 'vue'
  175 +import { onReady, onLoad, onShow } from '@dcloudio/uni-app';
  176 +import { addTree } from "@/api/tree-archive/tree-archive.js";
  177 +
  178 +import { useUploadImgs } from '@/common/utils/useUploadImgs'
  179 +import { useUserStore } from '@/pinia/user';
  180 +
  181 +// ========== 状态管理 ==========
  182 +const userStore = useUserStore();
  183 +
  184 +// ✅ 全局响应式数据
  185 +const formRef = ref(null)
  186 +const isShow = ref(false)
  187 +const contentHeight = ref(700)
  188 +const treeOwnershipData = ref([])
  189 +const treeLevelData = ref([])
  190 +const pickerShow = ref(false)
  191 +const levelshow = ref(false)
  192 +const loadingFlag = ref(false)
  193 +
  194 +// ✅✅✅ 核心修改:图片上传配置 完全对标参考代码写法
  195 +const treeImgs = useUploadImgs({
  196 + maxCount: 3,
  197 + uploadText: '选择图片',
  198 + sizeType: ['compressed'],
  199 + formRef: formRef,
  200 + fieldName: 'treeImgList'
  201 +})
  202 +// 图片数组兜底,防止非数组报错
  203 +if (!Array.isArray(treeImgs.rawImgList.value)) treeImgs.rawImgList.value = [];
  204 +
  205 +// ✅ 表单数据 - 替代vue2的data
  206 +const formData = reactive({
  207 + treetype: '',
  208 + treeheight: '',
  209 + dbh: '',
  210 + treelevel: '',
  211 + treeleveltext: '',
  212 + managedutyunit: '',
  213 + oldtreeownership: '',
  214 + oldtreeownershipText: '',
  215 + latinname: '',
  216 + estimationtreeage: '',
  217 + canopysouthnorth: '',
  218 + canopyeastwest: '',
  219 + weekday: '',
  220 + growlocation: '',
  221 + growthenvironment: '',
  222 + treeImgList: [],
  223 + address: '',
  224 + latitude: '',
  225 + longitude: '',
  226 + road: '',
  227 + maintainunit: ''
  228 +})
  229 +
  230 +// ✅✅✅ 核心修改:表单校验规则 - 图片校验改为参考代码同款
  231 +const rules = reactive({
  232 + treetype: [{ required: true, message: '请输入名称', trigger: 'blur' }],
  233 + treeheight: [{ max: 10, message: '树高不能超过10个字符', trigger: 'blur' }, { pattern: /^\d+(\.\d{1,2})?$/, message: '请输入正确的数字格式', trigger: 'blur' }],
  234 + dbh: [{ required: true, message: '请输入胸径', trigger: 'blur' }, { max: 10, message: '胸径不能超过10个字符', trigger: 'blur' }, { pattern: /^\d+(\.\d{1,2})?$/, message: '请输入正确的数字格式', trigger: 'blur' }],
  235 + estimationtreeage: [{ max: 10, message: '预估树龄不能超过10个字符', trigger: 'blur' }, { pattern: /^\d+(\.\d{1,2})?$/, message: '请输入正确的数字格式', trigger: 'blur' }],
  236 + weekday: [{ max: 10, message: '干周不能超过10个字符', trigger: 'blur' }, { pattern: /^\d+(\.\d{1,2})?$/, message: '请输入正确的数字格式', trigger: 'blur' }],
  237 + canopyeastwest: [{ max: 10, message: '东西冠幅不能超过10个字符', trigger: 'blur' }, { pattern: /^\d+(\.\d{1,2})?$/, message: '请输入正确的数字格式', trigger: 'blur' }],
  238 + canopysouthnorth: [{ max: 10, message: '南北冠幅不能超过10个字符', trigger: 'blur' }, { pattern: /^\d+(\.\d{1,2})?$/, message: '请输入正确的数字格式', trigger: 'blur' }],
  239 + growlocation: [{ required: true, message: '请地图选择位置', trigger: 'change' }],
  240 + managedutyunit: [{ required: true, message: '请输入管护单位', trigger: 'blur' }],
  241 + oldtreeownershipText: [{ required: true, message: '请选择权属分类', trigger: 'change' }],
  242 + treeImgList: [treeImgs.imgValidateRule] // ✅ 图片校验规则和参考代码一致
  243 +})
  244 +
  245 +// ✅ 生命周期
  246 +onLoad((options) => {
  247 + formData.road = options.roadId
  248 +})
  249 +
  250 +onShow(async () => {
  251 +
  252 + treeLevelData.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('tree_level'));
  253 + treeOwnershipData.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('tree_ownership'));
  254 +})
  255 +
  256 +// 修复表单ref挂载问题,和参考代码一致
  257 +onReady(() => {
  258 + nextTick(() => {
  259 + formRef.value?.setRules(rules);
  260 + });
  261 +});
  262 +
  263 +
  264 +
  265 +const toggleArea = () => {
  266 + isShow.value = !isShow.value
  267 +}
  268 +
  269 +
  270 +// 权属分类选择回调
  271 +const pickerChange = (val) => {
  272 + formData.oldtreeownership = val.value
  273 + formData.oldtreeownershipText = val.label
  274 + pickerShow.value = false
  275 +}
  276 +
  277 +// 树木级别选择回调
  278 +const pickerLevelChange = (val) => {
  279 + formData.treelevel = val.value
  280 + formData.treeleveltext = val.label
  281 + levelshow.value = false
  282 +}
  283 +
  284 +// 打开地图选址
  285 +const openMap = () => {
  286 + uni.chooseLocation({
  287 + success: (res) => {
  288 + formData.growlocation = res.address
  289 + formData.latitude = res.latitude
  290 + formData.longitude = res.longitude
  291 + },
  292 + fail: (err) => {
  293 + console.error('地图选择失败', err);
  294 + if (err.errMsg.includes('auth deny')) {
  295 + uni.showToast({ title: '请授权位置权限', icon: 'none' });
  296 + }
  297 + }
  298 + });
  299 +}
  300 +
  301 +// 表单提交核心方法
  302 +const submit = async () => {
  303 + if (loadingFlag.value) return
  304 + // 表单校验
  305 + const valid = await formRef.value.validate()
  306 + if (!valid) return
  307 +
  308 + // ✅✅✅ 核心修改:获取上传成功的图片地址,和参考代码一致
  309 + const uploadImgUrls = treeImgs.getSuccessImgUrls()
  310 +
  311 + // 组装提交参数
  312 + formData.maintainunit = uni.getStorageSync('userInfo')?.belongCompanyId || ''
  313 + formData.treeImgList = uploadImgUrls // 赋值图片数组
  314 + loadingFlag.value = true
  315 +
  316 + try {
  317 + const res = await addTree({ data: { ...formData } })
  318 + if (res.code == '200') {
  319 + uni.showToast({ title: "新增成功", icon: "success" });
  320 + uni.redirectTo({ url: '/subPackages/treePage/treeFiles' });
  321 + }
  322 + } catch (err) {
  323 + uni.showToast({ title: "新增失败,请重试", icon: "none" });
  324 + console.error(err)
  325 + } finally {
  326 + loadingFlag.value = false
  327 + }
  328 +}
  329 +</script>
  330 +
  331 +<style scoped lang="scss">
  332 +.container {
  333 + padding: 25rpx;
  334 + box-sizing: border-box;
  335 + background: #fff;
  336 +}
  337 +
  338 +.animated-area {
  339 + transition: all 0.3s ease-out;
  340 + margin-top: 10rpx;
  341 +}
  342 +.location-form-item {
  343 + padding-right: 10rpx;
  344 +}
  345 +</style>
0 \ No newline at end of file 346 \ No newline at end of file
pages-sub/data/tree-archive/editTree.vue 0 → 100644
  1 +<script lang="ts">
  2 +import {defineComponent} from 'vue'
  3 +
  4 +export default defineComponent({
  5 + name: "editTree"
  6 +})
  7 +</script>
  8 +
  9 +<template>
  10 +
  11 +</template>
  12 +
  13 +<style scoped lang="scss">
  14 +
  15 +</style>
0 \ No newline at end of file 16 \ No newline at end of file
pages-sub/data/tree-archive/index.vue
1 -<script setup lang="ts"> 1 +<template>
  2 + <view class="container">
  3 + <!-- 归属单位选择 -->
  4 + <up-form :model="formData" ref="formRef" label-width="140rpx" border-bottom style="padding: 0 15px">
  5 + <up-form-item
  6 + label="归属单位"
  7 + prop="companyId"
  8 + @click="showActionSheet = true"
  9 + class="form-item-custom"
  10 + >
  11 + <up-input
  12 + v-model="formData.companyId"
  13 + placeholder="请选择归属单位"
  14 + disabled
  15 + disabled-color="#ffffff"
  16 + border="none"
  17 + bg-color="transparent"
  18 + />
  19 + <template #right>
  20 + <up-icon name="arrow-right" size="16"></up-icon>
  21 + </template>
  22 + </up-form-item>
  23 + </up-form>
2 24
3 -</script> 25 + <!-- 弹窗式 Search 搜索组件 -->
  26 + <view class="search-wrap">
  27 + <up-search
  28 + v-model="searchValue"
  29 + placeholder="请输入道路名称搜索"
  30 + @search="handleSearch"
  31 + bg-color="#f5f5f5"
  32 + :clearabled="false"
  33 + :show-action="true"
  34 + actionText="搜索"
  35 + :animation="true"
  36 + @custom="handleSearch"
  37 + />
  38 + </view>
4 39
5 -<template> 40 + <!-- ✅ 核心:uview-plus up-row+up-col 实现左右布局【无u-scroll-list,无任何多余组件】 -->
  41 + <up-row type="flex" class="cate-layout" align="flex-start">
  42 + <!-- 左侧:班组列表 (uview栅格列 + 原生滚动) -->
  43 + <up-col span="3" class="cate-left">
  44 + <view
  45 + class="left-item"
  46 + :class="{ active: currentIndex === index }"
  47 + v-for="(item, index) in cateTabList"
  48 + :key="item.deptId"
  49 + @click="cateTabChange(index)"
  50 + >
  51 + {{ item.deptName }}
  52 + </view>
  53 +
  54 + </up-col>
6 55
  56 + <!-- 右侧:道路列表 (uview栅格列 + 原生滚动) -->
  57 + <up-col span="9" class="cate-right">
  58 + <scroll-view scroll-y class="right-scroll">
  59 + <!-- 暂无数据 -->
  60 + <empty-view v-if="!hasValidRoadId"></empty-view>
  61 + <!-- 道路数据列表 -->
  62 + <view v-else class="road-list-wrap">
  63 + <view
  64 + v-for="item in roads"
  65 + class="card-wrap cad-box-shadow"
  66 + :key="item.roadId"
  67 + @click="toNewPage(item.roadId, item.treeCount)"
  68 + >
  69 + <p class="fs-flex__between">
  70 + <span class="up-line-1">{{ item.roadName }}</span>
  71 + <span class="treeCount">{{ item.treeCount }}棵</span>
  72 + </p>
  73 + <p class="up-line-1 fs-my8">已录入行道树:{{ item.recordedCount }}棵</p>
  74 + <p class="up-line-1 fs-my8" style="color: #999;font-size: 12px">起点:{{ item.startRemark }}</p>
  75 + <p class="up-line-1" style="color: #999;font-size: 12px">终点:{{ item.endRemark }}</p>
  76 + </view>
  77 + </view>
  78 + </scroll-view>
  79 + </up-col>
  80 + </up-row>
  81 +
  82 + <!-- ActionSheet 底部弹出选择菜单 -->
  83 + <up-action-sheet
  84 + :show="showActionSheet"
  85 + :actions="belongCompanyData"
  86 + title="请选择归属单位"
  87 + @select="actionSheetSelect"
  88 + @close="showActionSheet = false"
  89 + ></up-action-sheet>
  90 +
  91 + </view>
7 </template> 92 </template>
8 93
9 -<style scoped> 94 +<script setup>
  95 +import { ref, reactive, watch } from 'vue'
  96 +import { onShow } from '@dcloudio/uni-app';
  97 +import { getCompanyList } from "@/api/common";
  98 +import { deptListReq } from '@/api/tree-archive/tree-archive'
  99 +
  100 +
  101 +const formRef = ref(null)
  102 +const showActionSheet = ref(false)
  103 +const currentIndex = ref(0)
  104 +const hasValidRoadId = ref(false)
  105 +const searchValue = ref('') // 搜索关键词 独立绑定
  106 +
  107 +// 表单数据
  108 +const formData = reactive({
  109 + companyId: '',
  110 +})
  111 +
  112 +// 接口数据变量
  113 +const companyId = ref('')
  114 +const belongCompanyData = ref([]) // 归属单位下拉数据
  115 +const depts = ref([]) // 归属班组列表
  116 +const roads = ref([]) // 当前选中班组的道路列表
  117 +const cateTabList = ref([]) // 左侧tab数据源
  118 +
  119 +// 生命周期
  120 +onShow( async() => {
  121 + const res = await getCompanyList()
  122 + belongCompanyData.value = res
  123 + if (belongCompanyData.value.length > 0) {
  124 + formData.companyId = belongCompanyData.value[0].name
  125 + companyId.value = belongCompanyData.value[0].id.toString()
  126 + deptListQuery()
  127 + }
  128 +})
  129 +
  130 +// 监听 CateTab 选中下标 数据联动
  131 +watch(currentIndex, () => {
  132 + if(depts.value.length > 0) {
  133 + roads.value = depts.value[currentIndex.value].roads
  134 + checkRoadDataValid()
  135 + }
  136 +})
  137 +
  138 +// 搜索事件 - 点击搜索按钮/回车触发
  139 +const handleSearch = () => {
  140 + deptListQuery()
  141 +}
  142 +
  143 +// 跳转道路档案记录页
  144 +const toNewPage = (roadId, treeCount) => {
  145 + uni.navigateTo({
  146 + url: `/pages-sub/data/tree-archive/treeRecord?roadId=${roadId}&count=${treeCount}`
  147 + })
  148 +}
  149 +
  150 +// 查询班组+道路数据 & 格式化CateTab的数据源
  151 +const deptListQuery = async () => {
  152 + const params = {
  153 + companyId: companyId.value,
  154 + roadName: searchValue.value
  155 + }
  156 + const res = await deptListReq(params)
  157 + if (res.length ==0) {
  158 + depts.value = []
  159 + roads.value = []
  160 + cateTabList.value = []
  161 + hasValidRoadId.value = false
  162 + } else {
  163 + depts.value = res[0].depts
  164 + cateTabList.value = depts.value
  165 + roads.value = depts.value[0].roads
  166 + checkRoadDataValid()
  167 + }
  168 +}
  169 +
  170 +// 左侧班组切换事件
  171 +const cateTabChange = (index) => {
  172 + currentIndex.value = index
  173 + roads.value = depts.value[index].roads
  174 + checkRoadDataValid()
  175 +}
  176 +
  177 +// ActionSheet 选择归属单位的确认事件
  178 +const actionSheetSelect = (val) => {
  179 + formData.companyId = val.name
  180 + companyId.value = val.id.toString()
  181 + showActionSheet.value = false // 关闭弹窗
  182 + deptListQuery() // 重新查询数据
  183 +}
  184 +
  185 +// 校验是否有有效道路数据
  186 +const checkRoadDataValid = () => {
  187 + hasValidRoadId.value = roads.value && roads.value.length > 0 && roads.value.some(item => {
  188 + return item.roadId !== null && item.roadId !== undefined && item.roadId
  189 + })
  190 +}
  191 +</script>
  192 +
  193 +<style scoped lang="scss">
  194 +// 页面容器 满高布局
  195 +.container {
  196 + height: 100%;
  197 + display: flex;
  198 + flex-direction: column;
  199 + background: #fff;
  200 + box-sizing: border-box;
  201 +}
  202 +
  203 +// 搜索框外层容器 间距不变
  204 +.search-wrap {
  205 + padding: 10rpx 15rpx;
  206 +}
  207 +
  208 +.cate-layout {
  209 + flex: 1;
  210 + width: 100%;
  211 + height: calc(100vh - 44px);
  212 +
  213 +}
  214 +
  215 +
  216 +.cate-left {
  217 + height: 100%;
  218 + padding: 0;
  219 + background: #f8f8f8;
  220 + box-sizing: border-box;
  221 + .left-scroll {
  222 + width: 100%;
  223 + height: 100%;
  224 + }
  225 + // 左侧列表项
  226 + .left-item {
  227 + padding: 24rpx 12rpx;
  228 + font-size: 28rpx;
  229 +
  230 + white-space: nowrap;
  231 + overflow: hidden;
  232 + text-overflow: ellipsis;
  233 + box-sizing: border-box;
  234 + }
  235 + // 选中项高亮 主题色和你一致 #5f5fe2
  236 + .left-item.active {
  237 + background: #ffffff;
  238 + color: #5f5fe2;
  239 + font-weight: 500;
  240 + border-left: 4rpx solid #5f5fe2;
  241 + }
  242 +}
  243 +
  244 +.cate-right {
  245 + height: 100%;
  246 + padding: 0 10rpx;
  247 + box-sizing: border-box;
  248 + background: #ffffff;
  249 + .right-scroll {
  250 + width: 100%;
  251 + height: 100%;
  252 + }
  253 +}
  254 +
  255 +// 道路列表容器
  256 +.road-list-wrap {
  257 + //padding-bottom: 30rpx;
  258 +}
  259 +
  260 +// 道路卡片样式 完全复用你的原版
  261 +.card-wrap {
  262 + box-shadow:0 0 3px rgba(0, 0, 0, 0.2);
  263 + padding: 20rpx;
  264 + border-radius: 14rpx;
  265 + background: #fff;
  266 + margin:10px 6px;
  267 +}
10 268
  269 +// 公共样式 完全不变
  270 +.treeCount {
  271 + color: #5f5fe2;
  272 + min-width: 70px;
  273 + text-align: right;
  274 +}
  275 +.fs-flex__between {
  276 + display: flex;
  277 + justify-content: space-between;
  278 + align-items: center;
  279 +}
  280 +.fs-my8 { margin: 8rpx 0; }
11 </style> 281 </style>
12 \ No newline at end of file 282 \ No newline at end of file
pages-sub/data/tree-archive/treeRecord.vue 0 → 100644
  1 +<template>
  2 + <view class="container">
  3 + <!-- ✅ 替换tui-no-data 为 uview-plus的空数据组件 up-empty -->
  4 + <up-empty
  5 + v-if="rows.length === 0"
  6 + text="暂无数据"
  7 + ></up-empty>
  8 +
  9 + <view class="record-wrap" v-else>
  10 + <view class="record-list-wrap cad-box-shadow fs-bg__white" v-for="i in rows" :key="i.id" @click="toEditPage(i.id)">
  11 + <view style="display: flex">
  12 + <view class="record-list-left" :style="`background-image: url(${i.treephoto});`"></view>
  13 + <view class="record-list-right">
  14 + <view class="record-list-right-title">
  15 + <view class="fs-ellipsis treetypeName">{{ i.treetype }}</view>
  16 + <view style="text-align: right">{{ i.updatetime.substring(0, 10) }}</view>
  17 + </view>
  18 + <view class="fs-mt8 fs-align__center">
  19 + <img src="../../../static/imgs/tree/tree-high.png" style="width: 14px;height: 14px;margin-right: 6px;" alt=""> 高度:{{ i.treeheight }} 米
  20 + </view>
  21 + <view class="fs-mt8 fs-align__center">
  22 + <img src="../../../static/imgs/tree/treearound.png" style="width: 14px;height: 14px;margin-right: 6px;" alt="">胸径:{{ i.dbh }} 厘米
  23 + </view>
  24 + </view>
  25 + </view>
  26 + <view class="fs-mt8 fs-ellipsis treenumber-no">
  27 + 树木编号:{{ i.treenumber }}
  28 + </view>
  29 + </view>
  30 + </view>
  31 +
  32 + <view class="fixed-bottom-btn-wrap">
  33 + <up-button
  34 + type="primary"
  35 + class="addTree"
  36 + @click="toAddTreePage"
  37 + v-show="count > 0 && count > rows.length"
  38 + >
  39 + 新增树木录入
  40 + </up-button>
  41 + </view>
  42 +
  43 + </view>
  44 +</template>
  45 +
  46 +<script setup>
  47 +import { ref} from 'vue'
  48 +import { onLoad, onShow } from '@dcloudio/uni-app';
  49 +// 引入接口请求方法
  50 +import { treeRoadReq } from "@/api/tree-archive/tree-archive.js";
  51 +
  52 +
  53 +const rows = ref([])
  54 +const roadId = ref('')
  55 +const count = ref(0)
  56 +
  57 +
  58 +onLoad((options) => {
  59 + console.log(options)
  60 + roadId.value = options.roadId
  61 + count.value = options.count
  62 +})
  63 +
  64 +onShow(() => {
  65 + treeRoadQuery()
  66 +})
  67 +
  68 +// 前往修改页面
  69 +const toEditPage = (id) => {
  70 + uni.navigateTo({
  71 + url: `/pages-sub/data/tree-archive/editTree?id=${id}`
  72 + })
  73 +}
  74 +
  75 +// 前往新增页面
  76 +const toAddTreePage = () => {
  77 + uni.navigateTo({
  78 + url: `/pages-sub/data/tree-archive/addTree?roadId=${roadId.value}`
  79 + })
  80 +}
  81 +
  82 +// 树木记录列表查询接口
  83 +const treeRoadQuery = async () => {
  84 + const res = await treeRoadReq( {road: roadId.value})
  85 + console.log(res)
  86 + rows.value = res.rows
  87 +}
  88 +</script>
  89 +
  90 +<style scoped lang="scss">
  91 +// 保留原页面所有样式,一行未改,样式完全一致
  92 +.record-wrap {
  93 + padding-bottom: 60px;
  94 +}
  95 +
  96 +.record-list-wrap {
  97 + margin: 15px 10px 0;
  98 + padding: 10px;
  99 + border-radius: 6px;
  100 + font-size: 14px;
  101 +}
  102 +
  103 +.treetypeName {
  104 + flex: 1;
  105 + font-size: 16px;
  106 + font-weight: bold;
  107 +}
  108 +
  109 +.record-list-left {
  110 + height: 70px;
  111 + width: 70px;
  112 + background-size: 100% 100%;
  113 +}
  114 +
  115 +.record-list-right {
  116 + margin-left: 20px;
  117 + flex: 1;
  118 + overflow: hidden;
  119 +}
  120 +
  121 +.record-list-right-title {
  122 + display: flex;
  123 + justify-content: space-between;
  124 +}
  125 +
  126 +.treenumber-no {
  127 + padding: 3px 10px;
  128 + background: #bdefd0;
  129 + font-size: 12px;
  130 +}
  131 +
  132 +.addTree {
  133 + width: 100%;
  134 + position: fixed;
  135 + bottom: 0;
  136 +}
  137 +</style>
0 \ No newline at end of file 138 \ No newline at end of file
pages-sub/problem/ai-manage/index.vue
@@ -3,7 +3,6 @@ @@ -3,7 +3,6 @@
3 <!-- 顶部固定区域 --> 3 <!-- 顶部固定区域 -->
4 <up-sticky> 4 <up-sticky>
5 <view class="header-wrap"> 5 <view class="header-wrap">
6 - <!-- 第一行:u-tabs 待办/已办切换 :scrollable="false"-->  
7 <up-tabs 6 <up-tabs
8 v-model="activeTab" 7 v-model="activeTab"
9 :list="tabList" 8 :list="tabList"
@@ -12,10 +11,8 @@ @@ -12,10 +11,8 @@
12 font-size="30rpx" 11 font-size="30rpx"
13 @click="handleTabChange" 12 @click="handleTabChange"
14 /> 13 />
15 -  
16 <!-- 第二行:下拉框 + 搜索框 --> 14 <!-- 第二行:下拉框 + 搜索框 -->
17 <view class="search-header"> 15 <view class="search-header">
18 - <!-- 左侧下拉框 -->  
19 <view class="select-wrap common-text-color"> 16 <view class="select-wrap common-text-color">
20 <up-select 17 <up-select
21 v-model:current="selectedSortValue" 18 v-model:current="selectedSortValue"
@@ -26,8 +23,6 @@ @@ -26,8 +23,6 @@
26 :style="{ flex: 1 }" 23 :style="{ flex: 1 }"
27 /> 24 />
28 </view> 25 </view>
29 -  
30 - <!-- 右侧搜索框 -->  
31 <view class="search-input-wrap"> 26 <view class="search-input-wrap">
32 <up-search 27 <up-search
33 v-model="searchValue" 28 v-model="searchValue"
@@ -47,24 +42,23 @@ @@ -47,24 +42,23 @@
47 42
48 <!-- 列表容器 --> 43 <!-- 列表容器 -->
49 <z-paging 44 <z-paging
50 - ref="paging" 45 + ref="pagingRef"
51 v-model="orderList" 46 v-model="orderList"
52 @query="queryList" 47 @query="queryList"
53 :auto-show-system-loading="true" 48 :auto-show-system-loading="true"
54 -  
55 > 49 >
56 <template #empty> 50 <template #empty>
57 - <empty-view/> 51 + <empty-view />
58 </template> 52 </template>
59 53
60 <view class="common-card-list" style="padding-top: 200rpx;padding-bottom: 30rpx"> 54 <view class="common-card-list" style="padding-top: 200rpx;padding-bottom: 30rpx">
61 <!-- 待办工单卡片 --> 55 <!-- 待办工单卡片 -->
62 <up-card 56 <up-card
63 - v-if="activeTab == 0" 57 + v-if="activeTab === 0"
64 :border="false" 58 :border="false"
65 :foot-border-top="false" 59 :foot-border-top="false"
66 v-for="(item, index) in orderList" 60 v-for="(item, index) in orderList"
67 - :key="`todo_${item.orderNo}_${index}`" 61 + :key="`todo_${item.id}_${index}`"
68 :show-head="false" 62 :show-head="false"
69 class="order-card" 63 class="order-card"
70 > 64 >
@@ -86,7 +80,7 @@ @@ -86,7 +80,7 @@
86 <view class="u-body-item-title">情况描述:</view> 80 <view class="u-body-item-title">情况描述:</view>
87 <view class="u-line-1 u-body-value">{{ item.remark || '无' }}</view> 81 <view class="u-line-1 u-body-value">{{ item.remark || '无' }}</view>
88 </view> 82 </view>
89 - <view class="u-body-item u-flex common-item-center common-justify-between"> 83 + <view class="u-body-item u-flex common-item-center common-justify-between">
90 <view class="u-body-item-title">紧急程度:</view> 84 <view class="u-body-item-title">紧急程度:</view>
91 <view class="u-line-1 u-body-value"> 85 <view class="u-line-1 u-body-value">
92 {{ uni.$dict.getDictLabel('workorder_pressing_type', item.pressingType) }} 86 {{ uni.$dict.getDictLabel('workorder_pressing_type', item.pressingType) }}
@@ -101,20 +95,10 @@ @@ -101,20 +95,10 @@
101 <view class="u-line-1 u-body-value">{{ timeFormat(item.createTime, 'yyyy-mm-dd hh:MM:ss') }}</view> 95 <view class="u-line-1 u-body-value">{{ timeFormat(item.createTime, 'yyyy-mm-dd hh:MM:ss') }}</view>
102 </view> 96 </view>
103 <!-- 操作按钮行 --> 97 <!-- 操作按钮行 -->
104 -  
105 <view class="u-body-item u-flex common-justify-between common-item-center mt-20"> 98 <view class="u-body-item u-flex common-justify-between common-item-center mt-20">
106 - <up-button type="warning" size="mini" @click="handleReject(item)"  
107 - v-show="nextStepMap[item.taskKey].backShow">回退  
108 - </up-button>  
109 -  
110 - <up-button type="success" size="mini" @click="handleRenew(item)"  
111 - v-show="nextStepMap[item.taskKey].renewShow">重新提交  
112 - </up-button>  
113 -  
114 - <up-button type="primary" size="mini" @click="handleProcess(item)">{{  
115 - nextStepMap[item.taskKey].btnText  
116 - }}  
117 - </up-button> 99 + <up-button type="warning" size="mini" @click="handleReject(item)" v-show="nextStepMap[item.taskKey].backShow">回退</up-button>
  100 + <up-button type="success" size="mini" @click="handleRenew(item)" v-show="nextStepMap[item.taskKey].renewShow">重新提交</up-button>
  101 + <up-button type="primary" size="mini" @click="handleProcess(item)">{{ nextStepMap[item.taskKey].btnText }}</up-button>
118 <up-button type="info" size="mini" @click="handleDetail(item)">详情</up-button> 102 <up-button type="info" size="mini" @click="handleDetail(item)">详情</up-button>
119 </view> 103 </view>
120 </view> 104 </view>
@@ -123,11 +107,11 @@ @@ -123,11 +107,11 @@
123 107
124 <!-- 已办工单卡片和我发起的 --> 108 <!-- 已办工单卡片和我发起的 -->
125 <up-card 109 <up-card
126 - v-if="activeTab == 2||activeTab == 1" 110 + v-if="activeTab === 1 || activeTab === 2"
127 :border="false" 111 :border="false"
128 :foot-border-top="false" 112 :foot-border-top="false"
129 v-for="(item, index) in orderList" 113 v-for="(item, index) in orderList"
130 - :key="`done_${item.orderNo}_${index}`" 114 + :key="`done_${item.id}_${index}`"
131 :show-head="false" 115 :show-head="false"
132 class="order-card" 116 class="order-card"
133 > 117 >
@@ -149,12 +133,11 @@ @@ -149,12 +133,11 @@
149 <view class="u-body-item-title">情况描述:</view> 133 <view class="u-body-item-title">情况描述:</view>
150 <view class="u-line-1 u-body-value">{{ item.remark || '无' }}</view> 134 <view class="u-line-1 u-body-value">{{ item.remark || '无' }}</view>
151 </view> 135 </view>
152 -  
153 <view class="u-body-item u-flex common-justify-between common-item-center"> 136 <view class="u-body-item u-flex common-justify-between common-item-center">
154 <view class="u-body-item-title"> 137 <view class="u-body-item-title">
155 紧急程度:{{ uni.$dict.getDictLabel('workorder_pressing_type', item.pressingType) }} 138 紧急程度:{{ uni.$dict.getDictLabel('workorder_pressing_type', item.pressingType) }}
156 </view> 139 </view>
157 - <view class=" "> 140 + <view>
158 <up-button type="primary" size="mini" @click="handleDetail(item)">工单详情</up-button> 141 <up-button type="primary" size="mini" @click="handleDetail(item)">工单详情</up-button>
159 </view> 142 </view>
160 </view> 143 </view>
@@ -172,21 +155,12 @@ @@ -172,21 +155,12 @@
172 </view> 155 </view>
173 </z-paging> 156 </z-paging>
174 157
175 - <!-- 底部新增工单按钮(仅巡查员显示) -->  
176 - <!-- <view v-if="isInspector" class="fixed-bottom-btn-wrap">-->  
177 - <!-- <up-button type="primary" size="large" @click="handleAddOrder">-->  
178 - <!-- 新增工单-->  
179 - <!-- </up-button>-->  
180 - <!-- </view>-->  
181 - 158 + <!-- 底部新增工单按钮 -->
182 <view class="fixed-bottom-btn-wrap" v-if="isAi"> 159 <view class="fixed-bottom-btn-wrap" v-if="isAi">
183 - <up-button type="primary" size="large" @click="handleAddOrder">  
184 - 新增工单  
185 - </up-button> 160 + <up-button type="primary" size="large" @click="handleAddOrder">新增工单</up-button>
186 </view> 161 </view>
187 162
188 -  
189 - <!-- 回退原因弹窗:替换为up-modal(核心修改) --> 163 + <!-- 回退原因弹窗 -->
190 <up-modal 164 <up-modal
191 :show="rejectModalShow" 165 :show="rejectModalShow"
192 title="回退原因" 166 title="回退原因"
@@ -197,7 +171,6 @@ @@ -197,7 +171,6 @@
197 @confirm="confirmReject" 171 @confirm="confirmReject"
198 > 172 >
199 <view class="reject-modal-content"> 173 <view class="reject-modal-content">
200 - <!-- 回退原因 必填textarea -->  
201 <up-textarea 174 <up-textarea
202 v-model.trim="rejectReason" 175 v-model.trim="rejectReason"
203 placeholder="请输入回退原因(必填)" 176 placeholder="请输入回退原因(必填)"
@@ -206,11 +179,10 @@ @@ -206,11 +179,10 @@
206 maxlength="200" 179 maxlength="200"
207 class="reject-textarea" 180 class="reject-textarea"
208 /> 181 />
209 - <!-- 上传图片(选填)- 按照参考页面改造 -->  
210 <view class="upload-wrap mt-20"> 182 <view class="upload-wrap mt-20">
211 <view class="upload-title">上传图片(选填)</view> 183 <view class="upload-title">上传图片(选填)</view>
212 <up-upload 184 <up-upload
213 - :file-list="rejectImgs.rawImgList.value|| []" 185 + :file-list="rejectImgs.rawImgList.value || []"
214 @after-read="rejectImgs.uploadImgs" 186 @after-read="rejectImgs.uploadImgs"
215 @delete="rejectImgs.deleteImg" 187 @delete="rejectImgs.deleteImg"
216 multiple 188 multiple
@@ -224,7 +196,7 @@ @@ -224,7 +196,7 @@
224 </view> 196 </view>
225 </up-modal> 197 </up-modal>
226 198
227 - <!-- 验收弹窗 up-modal(含图片上传) --> 199 + <!-- 验收弹窗 -->
228 <up-modal 200 <up-modal
229 :show="acceptModalShow" 201 :show="acceptModalShow"
230 title="验收" 202 title="验收"
@@ -235,15 +207,12 @@ @@ -235,15 +207,12 @@
235 @confirm="handleAcceptModalConfirm" 207 @confirm="handleAcceptModalConfirm"
236 > 208 >
237 <view class="accept-modal-content"> 209 <view class="accept-modal-content">
238 - <!-- 第一行:单选框(通过/不通过,默认通过) -->  
239 <view class="radio-group-wrap"> 210 <view class="radio-group-wrap">
240 <up-radio-group v-model="acceptRadioValue"> 211 <up-radio-group v-model="acceptRadioValue">
241 <up-radio name="0" label="通过"></up-radio> 212 <up-radio name="0" label="通过"></up-radio>
242 <up-radio name="1" label="不通过"></up-radio> 213 <up-radio name="1" label="不通过"></up-radio>
243 </up-radio-group> 214 </up-radio-group>
244 </view> 215 </view>
245 -  
246 - <!-- 第二行:必填textarea,最多200字 -->  
247 <view class="textarea-wrap mt-30"> 216 <view class="textarea-wrap mt-30">
248 <up-textarea 217 <up-textarea
249 v-model.trim="acceptReason" 218 v-model.trim="acceptReason"
@@ -254,15 +223,12 @@ @@ -254,15 +223,12 @@
254 count 223 count
255 /> 224 />
256 </view> 225 </view>
257 -  
258 - <!-- 验收图片上传(选填,参考回退弹窗样式) -->  
259 <view class="upload-wrap mt-20"> 226 <view class="upload-wrap mt-20">
260 <view class="upload-title">上传验收图片(选填)</view> 227 <view class="upload-title">上传验收图片(选填)</view>
261 <up-upload 228 <up-upload
262 :file-list="acceptImgs.rawImgList.value || []" 229 :file-list="acceptImgs.rawImgList.value || []"
263 @after-read="acceptImgs.uploadImgs" 230 @after-read="acceptImgs.uploadImgs"
264 @delete="acceptImgs.deleteImg" 231 @delete="acceptImgs.deleteImg"
265 -  
266 multiple 232 multiple
267 width="70" 233 width="70"
268 height="70" 234 height="70"
@@ -284,263 +250,233 @@ import { @@ -284,263 +250,233 @@ import {
284 myBuzSimplePage, 250 myBuzSimplePage,
285 todoBuzSimplePage, 251 todoBuzSimplePage,
286 doneBuzSimplePage, 252 doneBuzSimplePage,
287 - qyWorkorderCreate,  
288 qyUniversalApproval, 253 qyUniversalApproval,
289 daquUniversalApproval, 254 daquUniversalApproval,
290 dcyUniversalApproval 255 dcyUniversalApproval
291 } from '@/api/regional-order-manage/regional-order-manage' 256 } from '@/api/regional-order-manage/regional-order-manage'
292 -// 从用户store获取角色信息  
293 import { useUserStore } from '@/pinia/user'; 257 import { useUserStore } from '@/pinia/user';
294 import { nextStepMap, buzStatusMap } from '@/common/utils/common' 258 import { nextStepMap, buzStatusMap } from '@/common/utils/common'
295 -// 引入图片上传组合式函数(与参考页面一致)  
296 import { useUploadImgs } from '@/common/utils/useUploadImgs' 259 import { useUploadImgs } from '@/common/utils/useUploadImgs'
297 -// ========== 状态管理 ========== 260 +
  261 +// ========== 全局实例 & 常量 ==========
298 const userStore = useUserStore(); 262 const userStore = useUserStore();
299 -// 标签页切换 263 +const USER_ROLES = userStore.userInfo?.roles || [];
  264 +
  265 +// ========== 基础状态 (按模块分组,语义化更强) ==========
  266 +// tab切换
300 const activeTab = ref(0); // 0-待办 1-我发起的 2-已办 267 const activeTab = ref(0); // 0-待办 1-我发起的 2-已办
301 -const tabList = ref([  
302 - {name: '待办'},  
303 - {name: '我发起的任务'},  
304 - {name: '已办'}  
305 -]);  
306 -// 排序下拉框 268 +const tabList = ref([{name: '待办'}, {name: '我发起的任务'}, {name: '已办'}]);
  269 +// 排序与搜索
307 const selectedSortValue = ref(1); 270 const selectedSortValue = ref(1);
308 const sortOptions = ref([ 271 const sortOptions = ref([
309 - {name: '位置', id: 1},  
310 - {name: '名称', id: 2},  
311 - {name: '描述', id: 3},  
312 - {name: '编号', id: 4}, 272 + {name: '位置', id: 1}, {name: '名称', id: 2}, {name: '描述', id: 3}, {name: '编号', id: 4},
313 ]); 273 ]);
314 -// 搜索  
315 const searchValue = ref(''); 274 const searchValue = ref('');
316 -// 分页  
317 -const paging = ref(null); 275 +// 分页相关
  276 +const pagingRef = ref(null);
318 const orderList = ref([]); 277 const orderList = ref([]);
319 -// 角色控制(巡查员显示新增按钮) 278 +
  279 +// ========== 角色权限计算属性 (修复核心bug: 原代码取反逻辑写反) ==========
320 const isAi = computed(() => { 280 const isAi = computed(() => {
321 - // patrol_global 全域巡查员  
322 - // regional_manager 大区经理  
323 - // AI_dispatcher AI工单派发人员  
324 - // 增加可选链,避免用户信息不存在报错  
325 - console.log('123')  
326 - console.log(!userStore.userInfo?.roles?.includes('AI_dispatcher'))  
327 - return !userStore.userInfo?.roles?.includes('AI_dispatcher') ;  
328 - // return true 281 + // AI工单派发人员 不显示新增按钮,其他角色显示
  282 + return !USER_ROLES.includes('AI_dispatcher');
329 }); 283 });
330 -// 回退弹窗相关  
331 -const rejectModalShow = ref(false); // 回退modal显示开关  
332 -const rejectReason = ref(''); // 回退原因  
333 -const currentRejectItem = ref(null); // 当前回退工单  
334 -// 回退图片上传配置(与参考页面风格一致) 284 +
  285 +// ========== 回退弹窗相关 ==========
  286 +const rejectModalShow = ref(false);
  287 +const rejectReason = ref('');
  288 +const currentRejectItem = ref(null);
335 const rejectImgs = useUploadImgs({ 289 const rejectImgs = useUploadImgs({
336 - maxCount: 3, // 最多上传3张  
337 - uploadText: '选择回退图片', // 自定义上传提示文字  
338 - sizeType: ['compressed'], // 仅上传压缩图  
339 - formRef: null, // 该弹窗无表单校验  
340 - fieldName: 'rejectImgs' // 自定义字段名 290 + maxCount: 3, uploadText: '选择回退图片', sizeType: ['compressed'], formRef: null, fieldName: 'rejectImgs'
341 }) 291 })
342 -// ========== 验收弹窗相关状态(含图片上传) ==========  
343 -const acceptModalShow = ref(false); // 验收弹窗显示开关  
344 -const acceptRadioValue = ref('0'); // 单选框值,默认0(通过)  
345 -const acceptReason = ref(''); // 验收原因  
346 -const currentAcceptItem = ref(null); // 当前验收的工单项  
347 -// 验收图片上传配置(独立实例,参考回退弹窗) 292 +
  293 +// ========== 验收弹窗相关 ==========
  294 +const acceptModalShow = ref(false);
  295 +const acceptRadioValue = ref('0'); // 默认通过
  296 +const acceptReason = ref('');
  297 +const currentAcceptItem = ref(null);
348 const acceptImgs = useUploadImgs({ 298 const acceptImgs = useUploadImgs({
349 - maxCount: 3, // 最多上传3张,与回退弹窗一致  
350 - uploadText: '选择验收图片', // 自定义上传提示文字  
351 - sizeType: ['compressed'], // 仅上传压缩图,优化性能  
352 - formRef: null, // 验收弹窗无表单校验  
353 - fieldName: 'acceptImgs' // 自定义字段名,区分回退图片 299 + maxCount: 3, uploadText: '选择验收图片', sizeType: ['compressed'], formRef: null, fieldName: 'acceptImgs'
354 }) 300 })
  301 +
  302 +// ========== 公共封装方法 (核心优化:消灭重复代码) ==========
  303 +/**
  304 + * 生成统一的临时存储key
  305 + * @param {String} prefix 前缀标识
  306 + * @returns {String} 唯一key
  307 + */
  308 +const generateTempKey = (prefix = 'order') => {
  309 + return `${prefix}_${Date.now()}_${Math.floor(Math.random() * 10000)}`;
  310 +};
  311 +
  312 +/**
  313 + * 存储工单数据到本地缓存
  314 + * @param {Object} item 工单数据
  315 + * @param {String} prefix key前缀
  316 + * @returns {String|null} 成功返回key,失败返回null
  317 + */
  318 +const setOrderStorage = (item, prefix) => {
  319 + if (!item?.id) return null;
  320 + const tempKey = generateTempKey(prefix);
  321 + try {
  322 + uni.setStorageSync(tempKey, item);
  323 + return tempKey;
  324 + } catch (error) {
  325 + console.error('存储工单数据失败:', error);
  326 + uni.showToast({title: '数据存储异常,请重试', icon: 'none'});
  327 + return null;
  328 + }
  329 +};
  330 +
  331 +/**
  332 + * 获取分页请求公共参数
  333 + * @param {Number} pageNo 页码
  334 + * @param {Number} pageSize 页大小
  335 + * @returns {Object} 请求参数
  336 + */
  337 +const getQueryParams = (pageNo, pageSize) => {
  338 + return {
  339 + searchContent: searchValue.value.trim() || '',
  340 + pageNo,
  341 + pageSize,
  342 + type: selectedSortValue.value
  343 + };
  344 +};
  345 +
  346 +/**
  347 + * 统一调用审批接口
  348 + * @param {Object} params 请求参数
  349 + * @param {String} taskKey 工单任务key
  350 + * @returns {Promise} 接口请求Promise
  351 + */
  352 +const callApprovalApi = async (params, taskKey) => {
  353 + if (taskKey === 'shRegionManager') return await dcyUniversalApproval(params);
  354 + if (taskKey === 'regionManager') return await qyUniversalApproval(params);
  355 + // 根据角色匹配对应接口
  356 + if (USER_ROLES.includes('regional_manager')) return await daquUniversalApproval(params);
  357 + if (USER_ROLES.includes('Inspector_global')) return await dcyUniversalApproval(params);
  358 + if (USER_ROLES.includes('patrol_global')) return await qyUniversalApproval(params);
  359 +};
  360 +
  361 +/**
  362 + * 统一跳转工单页面
  363 + * @param {String} path 页面路径
  364 + * @param {Object} query 拼接参数
  365 + */
  366 +const navToOrderPage = (path, query = {}) => {
  367 + const queryStr = Object.keys(query).map(k => `${k}=${query[k]}`).join('&');
  368 + uni.navigateTo({ url: `${path}${queryStr ? '?' + queryStr : ''}` });
  369 +};
  370 +
  371 +/**
  372 + * 刷新列表 - 统一封装,避免空指针
  373 + */
  374 +const refreshOrderList = () => {
  375 + pagingRef.value && pagingRef.value.reload();
  376 +};
  377 +
  378 +// ========== 业务核心方法 ==========
355 // 分页查询列表 379 // 分页查询列表
356 const queryList = async (pageNo, pageSize) => { 380 const queryList = async (pageNo, pageSize) => {
357 try { 381 try {
358 - const apiParams = {  
359 - searchContent: searchValue.value.trim() || '',  
360 - pageNo,  
361 - pageSize,  
362 - type: selectedSortValue.value // 1-位置 2-工单名称 3-情况描述 4-工单编号  
363 - }; 382 + const params = getQueryParams(pageNo, pageSize);
364 let res; 383 let res;
365 - if (activeTab.value == 0) {  
366 - // 待办工单  
367 - res = await todoBuzSimplePage(apiParams);  
368 - } else if (activeTab.value == 1) {  
369 - // 我发起的任务  
370 - res = await myBuzSimplePage(apiParams);  
371 - } else {  
372 - // 已办工单  
373 - res = await doneBuzSimplePage(apiParams);  
374 - }  
375 - // 适配z-paging分页  
376 - paging.value.complete(res.list, res.total); 384 + if (activeTab.value === 0) res = await todoBuzSimplePage(params);
  385 + else if (activeTab.value === 1) res = await myBuzSimplePage(params);
  386 + else res = await doneBuzSimplePage(params);
  387 + pagingRef.value.complete(res?.list || [], res?.total || 0);
377 } catch (error) { 388 } catch (error) {
378 console.error('加载工单失败:', error); 389 console.error('加载工单失败:', error);
379 - paging.value?.complete(false); 390 + pagingRef.value?.complete(false);
380 uni.showToast({title: '加载失败,请重试', icon: 'none'}); 391 uni.showToast({title: '加载失败,请重试', icon: 'none'});
381 } 392 }
382 }; 393 };
383 -// ========== 事件处理 ========== 394 +
384 // 标签页切换 395 // 标签页切换
385 const handleTabChange = (item) => { 396 const handleTabChange = (item) => {
386 - orderList.value = [];  
387 - console.log(item)  
388 activeTab.value = item.index; 397 activeTab.value = item.index;
389 - paging.value?.reload(); // 切换标签页刷新列表 398 + orderList.value = [];
  399 + refreshOrderList();
390 }; 400 };
  401 +
391 // 排序变更 402 // 排序变更
392 const handleSortChange = (val) => { 403 const handleSortChange = (val) => {
393 selectedSortValue.value = val.id; 404 selectedSortValue.value = val.id;
394 searchValue.value = ''; 405 searchValue.value = '';
395 - paging.value?.reload(); // 排序变更刷新列表 406 + refreshOrderList();
396 }; 407 };
  408 +
397 // 搜索 409 // 搜索
398 const handleSearch = (val) => { 410 const handleSearch = (val) => {
399 searchValue.value = val; 411 searchValue.value = val;
400 - paging.value?.reload(); // 搜索刷新列表 412 + refreshOrderList();
401 }; 413 };
  414 +
402 // 工单详情 415 // 工单详情
403 const handleDetail = (item) => { 416 const handleDetail = (item) => {
404 - // 0-待办 1我发起的- 2-已办  
405 - uni.navigateTo({  
406 - url: `/pages-sub/problem/regional-order-manage/order-detail?taskId=${item.taskId}&activeTab=${activeTab.value}&processInstanceId=${item.processInstanceId}`,  
407 - events: {  
408 - // 自定义事件名:needRefresh(与详情页保持一致)  
409 - needRefresh: () => {  
410 - console.log('详情页返回,触发工单列表刷新');  
411 - if (paging.value) {  
412 - paging.value.reload(); // 刷新z-paging列表  
413 - }  
414 - }  
415 - } 417 + if (!item?.taskId) return uni.showToast({title: '工单信息异常', icon: 'none'});
  418 + navToOrderPage('/pages-sub/problem/regional-order-manage/order-detail', {
  419 + taskId: item.taskId,
  420 + activeTab: activeTab.value,
  421 + processInstanceId: item.processInstanceId
  422 + }, {
  423 + needRefresh: () => refreshOrderList()
416 }); 424 });
417 }; 425 };
418 -// 生成临时key  
419 -const generateTempKey = () => {  
420 - return 'renew_order_' + Date.now() + '_' + Math.floor(Math.random() * 10000);  
421 -};  
422 -// 待办-重新提交工单(改造后:大数据存本地,仅传唯一标识) 426 +
  427 +// 重新提交工单
423 const handleRenew = (item) => { 428 const handleRenew = (item) => {
  429 + const tempKey = setOrderStorage(item, 'renew_order');
  430 + if (!tempKey) return;
424 431
425 - // 1. 生成唯一临时标识  
426 - const tempKey = generateTempKey();  
427 - // 2. 将完整工单数据存入本地临时存储(同步存储,确保数据立即生效)  
428 - try {  
429 - console.log(item)  
430 - console.log('123')  
431 - uni.setStorageSync(tempKey, item);  
432 - } catch (error) {  
433 - console.error('存储工单数据失败:', error);  
434 - uni.showToast({title: '数据存储异常,无法重新提交', icon: 'none'});  
435 - return;  
436 - } 432 + const pageUrl = USER_ROLES.includes('patrol_global')
  433 + ? '/pages-sub/problem/regional-order-manage/add-patrol-order'
  434 + : '/pages-sub/problem/regional-order-manage/add-order';
437 435
438 - if (userStore.userInfo?.roles.includes('patrol_global')) { // 全域巡查员  
439 - uni.navigateTo({  
440 - url: `/pages-sub/problem/regional-order-manage/add-patrol-order?isRenew=1&tempKey=${tempKey}`  
441 - });  
442 - }  
443 - if (userStore.userInfo?.roles.includes('regional_manager')) { // 大区经理  
444 - uni.navigateTo({  
445 - url: `/pages-sub/problem/regional-order-manage/add-order?isRenew=1&tempKey=${tempKey}`  
446 - });  
447 - }  
448 - if (userStore.userInfo?.roles.includes('Inspector_global')) { // 督察员  
449 - uni.navigateTo({  
450 - url: `/pages-sub/problem/regional-order-manage/add-order?isRenew=1&tempKey=${tempKey}`  
451 - });  
452 - }  
453 - // // 3. URL 仅传递「唯一标识」和「重新提交标记」(数据量极小,无长度问题)  
454 - // uni.navigateTo({  
455 - // url: `/pages-sub/problem/regional-order-manage/add-patrol-order?isRenew=1&tempKey=${tempKey}`  
456 - // }); 436 + navToOrderPage(pageUrl, { isRenew: 1, tempKey });
457 }; 437 };
458 -// 待办-处理工单 438 +
  439 +// 处理工单核心逻辑
459 const handleProcess = async (item) => { 440 const handleProcess = async (item) => {
460 - console.log(nextStepMap[item.taskKey].name) 441 + if (!item) return uni.showToast({title: '工单信息异常', icon: 'none'});
  442 + const stepName = nextStepMap[item.taskKey]?.name;
  443 +
461 try { 444 try {
462 - if (nextStepMap[item.taskKey]?.name == '大区经理分配') {  
463 - // ① 生成唯一临时key(统一规则,避免冲突)  
464 - const tempKey = `distribute_order_${Date.now()}_${Math.floor(Math.random() * 10000)}`;  
465 - // ② 存储完整item到本地缓存(同步存储,确保立即生效)  
466 - try {  
467 - uni.setStorageSync(tempKey, item);  
468 - } catch (error) {  
469 - console.error('存储分配工单数据失败:', error);  
470 - uni.showToast({title: '数据存储异常,无法跳转', icon: 'none'});  
471 - return;  
472 - }  
473 - // ③ URL仅传递临时key,无其他冗余参数  
474 - uni.navigateTo({  
475 - url: `/pages-sub/problem/regional-order-manage/distribution-order?tempKey=${tempKey}`  
476 - }) 445 + // 大区经理分配
  446 + if (stepName === '大区经理分配') {
  447 + const tempKey = setOrderStorage(item, 'distribute_order');
  448 + tempKey && navToOrderPage('/pages-sub/problem/regional-order-manage/distribution-order', { tempKey });
477 } 449 }
478 - if (nextStepMap[item.taskKey]?.name == '督察员单子大区经理分配') {  
479 - let postData = {  
480 - "taskKey":item.taskKey,  
481 - "taskId": item.taskId,  
482 - "operateType":60,  
483 - "workerDataId":item.id,  
484 - "agree":0,  
485 - "reason":item.remark,  
486 - "roadId":item.roadId,  
487 - "roadName":item.roadName,  
488 - "pressingType":item.pressingType,  
489 - "orderName":item.orderName,  
490 - "expectedFinishDate": item.expectedFinishDate,  
491 - "busiLine":item.busiLine,  
492 - }  
493 - const res = await dcyUniversalApproval(postData);  
494 - uni.showToast({title: '分配成功', icon: 'success', duration: 1000});  
495 - paging.value?.reload(); // 刷新列表 450 + // 督察员单子大区经理分配
  451 + else if (stepName === '督察员单子大区经理分配') {
  452 + const postData = { taskKey: item.taskKey, taskId: item.taskId, operateType:60, workerDataId:item.id, agree:0, reason:item.remark, roadId:item.roadId, roadName:item.roadName, pressingType:item.pressingType, orderName:item.orderName, expectedFinishDate: item.expectedFinishDate, busiLine:item.busiLine };
  453 + await dcyUniversalApproval(postData);
  454 + uni.showToast({title: '分配成功', icon: 'success'});
  455 + refreshOrderList();
496 } 456 }
497 -  
498 -  
499 - // 验收 - 打开弹窗  
500 - if (nextStepMap[item.taskKey]?.name == '巡查员验收' || nextStepMap[item.taskKey]?.name == '养护组长验收') {  
501 - currentAcceptItem.value = item; // 存储当前工单信息  
502 - acceptReason.value = ''; // 清空上次的验收原因  
503 - acceptRadioValue.value = '0'; // 重置默认选中“通过”  
504 - acceptModalShow.value = true; // 显示验收弹窗 457 + // 验收弹窗
  458 + else if (['巡查员验收', '养护组长验收'].includes(stepName)) {
  459 + currentAcceptItem.value = item;
  460 + acceptReason.value = '';
  461 + acceptRadioValue.value = '0';
  462 + acceptModalShow.value = true;
505 } 463 }
506 -  
507 - // 发起人确认  
508 - if (nextStepMap[item.taskKey]?.name == '发起人确认') {  
509 - console.log(item) 464 + // 发起人确认-结束工单
  465 + else if (stepName === '发起人确认') {
510 uni.showModal({ 466 uni.showModal({
511 title: "结束工单", 467 title: "结束工单",
512 content: "请确定是否结束工单?", 468 content: "请确定是否结束工单?",
513 - success: async function (res) { 469 + success: async (res) => {
514 if (res.confirm) { 470 if (res.confirm) {
515 - // 构建请求参数  
516 const requestData = { 471 const requestData = {
517 -  
518 - "workerDataId": item.id,  
519 - "taskKey": 'ylInspectorStart',  
520 - "taskId": item.taskId,  
521 - "operateType": 200,  
522 - "agree": 1,  
523 - "reason": '结束工单' 472 + workerDataId: item.id, taskKey: 'ylInspectorStart', taskId: item.taskId,
  473 + operateType: 200, agree: 1, reason: '结束工单'
524 }; 474 };
525 - // 调用回退工单接口 daquUniversalApproval  
526 -  
527 - if( userStore.userInfo.roles.includes('regional_manager')){ // 大区经理  
528 - await daquUniversalApproval(requestData)  
529 - }  
530 - if( userStore.userInfo.roles.includes('Inspector_global')){ // 督察员  
531 - await dcyUniversalApproval(requestData);  
532 - }  
533 - if( userStore.userInfo.roles.includes('patrol_global')){ // 全域巡查员  
534 - await qyUniversalApproval(requestData);  
535 - }  
536 -  
537 - uni.showToast({title: '结束成功', icon: 'success', duration: 1000});  
538 - rejectModalShow.value = false;  
539 - paging.value?.reload(); // 刷新列表  
540 - } else if (res.cancel) {  
541 - console.log("用户点击取消"); 475 + await callApprovalApi(requestData, item.taskKey);
  476 + uni.showToast({title: '结束成功', icon: 'success'});
  477 + refreshOrderList();
542 } 478 }
543 - }, 479 + }
544 }); 480 });
545 } 481 }
546 } catch (error) { 482 } catch (error) {
@@ -548,168 +484,92 @@ const handleProcess = async (item) =&gt; { @@ -548,168 +484,92 @@ const handleProcess = async (item) =&gt; {
548 uni.showToast({title: '处理失败,请重试', icon: 'none'}); 484 uni.showToast({title: '处理失败,请重试', icon: 'none'});
549 } 485 }
550 }; 486 };
551 -// 待办-回退工单(打开回退modal) 487 +
  488 +// 回退工单-打开弹窗
552 const handleReject = (item) => { 489 const handleReject = (item) => {
553 - console.log('123213')  
554 - // 校验工单有效性  
555 - if (!item || !item.id) {  
556 - uni.showToast({title: '工单信息异常,无法回退', icon: 'none'});  
557 - return;  
558 - } 490 + if (!item?.id) return uni.showToast({title: '工单信息异常,无法回退', icon: 'none'});
559 currentRejectItem.value = item; 491 currentRejectItem.value = item;
560 - rejectReason.value = ''; // 清空上次输入  
561 - rejectImgs.clearImgs(); // 改造后:使用组合式函数的清空方法  
562 - rejectModalShow.value = true; // 显示回退modal 492 + rejectReason.value = '';
  493 + rejectImgs.clearImgs();
  494 + rejectModalShow.value = true;
563 }; 495 };
564 -// 回退modal - 取消按钮 496 +
  497 +// 关闭回退弹窗-重置状态
565 const handleRejectModalCancel = () => { 498 const handleRejectModalCancel = () => {
566 rejectModalShow.value = false; 499 rejectModalShow.value = false;
567 rejectReason.value = ''; 500 rejectReason.value = '';
568 - rejectImgs.clearImgs(); // 改造后:使用组合式函数的清空方法 501 + rejectImgs.clearImgs();
569 }; 502 };
  503 +
570 // 确认回退工单 504 // 确认回退工单
571 const confirmReject = async () => { 505 const confirmReject = async () => {
572 - // 严格校验回退原因(去除首尾空格)  
573 - const rejectReasonTrim = rejectReason.value.trim();  
574 - if (!rejectReasonTrim) {  
575 - uni.showToast({title: '请填写回退原因', icon: 'none', duration: 1000});  
576 - return;  
577 - }  
578 - // 校验当前工单有效性  
579 - if (!currentRejectItem.value || !currentRejectItem.value.id) {  
580 - uni.showToast({title: '工单信息异常,无法提交', icon: 'none', duration: 1000});  
581 - rejectModalShow.value = false;  
582 - return;  
583 - } 506 + const reason = rejectReason.value.trim();
  507 + if (!reason) return uni.showToast({title: '请填写回退原因', icon: 'none'});
  508 + const item = currentRejectItem.value;
  509 + if (!item?.id) return uni.showToast({title: '工单信息异常', icon: 'none'});
  510 +
  511 + uni.showLoading({title: '提交中...', mask: true});
584 try { 512 try {
585 - // 显示加载中,防止重复提交  
586 - uni.showLoading({title: '提交中...', mask: true});  
587 - // 构建请求参数  
588 const requestData = { 513 const requestData = {
589 - "returnImgs": rejectImgs.getSuccessImgUrls(), // 改造后:获取上传成功的图片URL数组  
590 - "workerDataId": currentRejectItem.value.id,  
591 - "taskKey": currentRejectItem.value.taskKey,  
592 - "taskId": currentRejectItem.value.taskId,  
593 -  
594 - "operateType": nextStepMap[currentRejectItem.value.taskKey].operateTypeNoPass,  
595 - "agree": 1,  
596 - "reason": rejectReasonTrim 514 + returnImgs: rejectImgs.getSuccessImgUrls(), workerDataId: item.id, taskKey: item.taskKey,
  515 + taskId: item.taskId, operateType: nextStepMap[item.taskKey].operateTypeNoPass, agree:1, reason
597 }; 516 };
598 - // 调用回退工单接口  
599 - if(currentRejectItem.value.taskKey=='shRegionManager'){ // 对督察员单子 回退  
600 - const res = await dcyUniversalApproval(requestData);  
601 - }  
602 - if(currentRejectItem.value.taskKey=='regionManager'){ // 对全域巡查员单子 回退  
603 - const res = await qyUniversalApproval(requestData);  
604 - }  
605 - // 对大区经理单子 回退  
606 - uni.showToast({title: '回退成功', icon: 'success', duration: 1000});  
607 - rejectModalShow.value = false;  
608 - paging.value?.reload(); // 刷新列表 517 + await callApprovalApi(requestData, item.taskKey);
  518 + uni.showToast({title: '回退成功', icon: 'success'});
  519 + handleRejectModalCancel();
  520 + refreshOrderList();
609 } catch (error) { 521 } catch (error) {
610 console.error('回退工单失败:', error); 522 console.error('回退工单失败:', error);
611 - uni.showToast({title: '网络异常,回退失败', icon: 'none', duration: 1000}); 523 + uni.showToast({title: '回退失败,请重试', icon: 'none'});
612 } finally { 524 } finally {
613 - // 隐藏加载中  
614 uni.hideLoading(); 525 uni.hideLoading();
615 } 526 }
616 }; 527 };
  528 +
617 // 新增工单 529 // 新增工单
618 const handleAddOrder = () => { 530 const handleAddOrder = () => {
619 - if (userStore.userInfo?.roles.includes('patrol_global')) { // 全域巡查员  
620 - uni.navigateTo({  
621 - url: '/pages-sub/problem/regional-order-manage/add-patrol-order'  
622 - });  
623 - }  
624 - if (userStore.userInfo?.roles.includes('regional_manager')) { // 大区经理  
625 - uni.navigateTo({  
626 - url: '/pages-sub/problem/regional-order-manage/add-order'  
627 - });  
628 - }  
629 - if (userStore.userInfo?.roles.includes('Inspector_global')) { // 督察员  
630 - uni.navigateTo({  
631 - url: '/pages-sub/problem/regional-order-manage/add-order'  
632 - });  
633 - } 531 + const pageUrl = USER_ROLES.includes('patrol_global')
  532 + ? '/pages-sub/problem/regional-order-manage/add-patrol-order'
  533 + : '/pages-sub/problem/regional-order-manage/add-order';
  534 + navToOrderPage(pageUrl);
634 }; 535 };
635 -// 验收弹窗 - 取消按钮(清空状态) 536 +
  537 +// 关闭验收弹窗-重置状态
636 const handleAcceptModalCancel = () => { 538 const handleAcceptModalCancel = () => {
637 acceptModalShow.value = false; 539 acceptModalShow.value = false;
638 - acceptReason.value = ''; // 清空验收原因  
639 - acceptRadioValue.value = '0'; // 重置单选框为“通过”  
640 - acceptImgs.clearImgs(); // 清空验收图片 540 + acceptReason.value = '';
  541 + acceptRadioValue.value = '0';
  542 + acceptImgs.clearImgs();
641 }; 543 };
642 -// 验收弹窗 - 确定按钮(含returnImgs传参) 544 +
  545 +// 验收提交
643 const handleAcceptModalConfirm = async () => { 546 const handleAcceptModalConfirm = async () => {
644 - // 1. 校验验收原因是否为空  
645 - if (!acceptReason.value.trim()) {  
646 - uni.showToast({title: '请填写验收原因', icon: 'none', duration: 1000});  
647 - return;  
648 - }  
649 - // 2. 校验验收原因长度  
650 - if (acceptReason.value.length > 200) {  
651 - uni.showToast({title: '验收原因最多200字', icon: 'none', duration: 1000});  
652 - return;  
653 - }  
654 - try {  
655 - // 3. 构建请求参数(含returnImgs)  
656 - console.log(currentAcceptItem.value)  
657 - console.log( userStore.userInfo.roles)  
658 - console.log( userStore.userInfo.roles.includes('Inspector_global'))  
659 - //  
660 - if( userStore.userInfo.roles.includes('regional_manager')){ // 大区经理验收  
661 - let postData = {  
662 - "returnImgs": acceptImgs.getSuccessImgUrls(), // 验收图片URL数组  
663 - "taskKey": currentAcceptItem.value.taskKey,  
664 - "workerDataId": currentAcceptItem.value.id,  
665 - "taskId": currentAcceptItem.value.taskId,  
666 - "operateType": acceptRadioValue.value == 0 ? nextStepMap[currentAcceptItem.value.taskKey].operateTypePass : nextStepMap[currentAcceptItem.value.taskKey].operateTypeNoPass,  
667 - "reason": acceptReason.value.trim(),  
668 - "agree":acceptRadioValue.value,  
669 - }  
670 - await daquUniversalApproval(postData);  
671 - }  
672 - //  
673 - if( userStore.userInfo.roles.includes('patrol_global')){ // 全域巡查员验收  
674 - let postData = {  
675 - "returnImgs": acceptImgs.getSuccessImgUrls(), // 验收图片URL数组  
676 - "taskKey": currentAcceptItem.value.taskKey,  
677 - "taskId": currentAcceptItem.value.taskId,  
678 - "workerDataId": currentAcceptItem.value.id,  
679 - "operateType": acceptRadioValue.value == 0 ? nextStepMap[currentAcceptItem.value.taskKey].operateTypePass : nextStepMap[currentAcceptItem.value.taskKey].operateTypeNoPass,  
680 - "reason": acceptReason.value.trim(),  
681 - "agree": acceptRadioValue.value  
682 - }  
683 - await qyUniversalApproval(postData);  
684 - }  
685 - //  
686 - if( userStore.userInfo.roles.includes('Inspector_global')){ // 督察员验收  
687 - let postData = {  
688 - "returnImgs": acceptImgs.getSuccessImgUrls(), // 验收图片URL数组  
689 - "taskKey": currentAcceptItem.value.taskKey,  
690 - "taskId": currentAcceptItem.value.taskId,  
691 - "workerDataId": currentAcceptItem.value.id,  
692 - "operateType": acceptRadioValue.value == 0 ? nextStepMap[currentAcceptItem.value.taskKey].operateTypePass : nextStepMap[currentAcceptItem.value.taskKey].operateTypeNoPass,  
693 - "reason": acceptReason.value.trim(),  
694 - "agree": acceptRadioValue.value  
695 - }  
696 - await dcyUniversalApproval(postData);  
697 - } 547 + const reason = acceptReason.value.trim();
  548 + if (!reason) return uni.showToast({title: '请填写验收原因', icon: 'none'});
  549 + if (reason.length > 200) return uni.showToast({title: '验收原因最多200字', icon: 'none'});
698 550
699 - // 4. 操作成功处理  
700 - uni.showToast({title: '提交成功', icon: 'success', duration: 1000});  
701 - handleAcceptModalCancel(); // 清空状态  
702 - paging.value?.reload(); // 刷新工单列表 551 + const item = currentAcceptItem.value;
  552 + if (!item?.id) return uni.showToast({title: '工单信息异常', icon: 'none'});
  553 +
  554 + try {
  555 + const postData = {
  556 + returnImgs: acceptImgs.getSuccessImgUrls(), taskKey: item.taskKey, workerDataId: item.id,
  557 + taskId: item.taskId, agree: acceptRadioValue.value, reason,
  558 + operateType: acceptRadioValue.value === '0' ? nextStepMap[item.taskKey].operateTypePass : nextStepMap[item.taskKey].operateTypeNoPass
  559 + };
  560 + await callApprovalApi(postData, item.taskKey);
  561 + uni.showToast({title: '提交成功', icon: 'success'});
  562 + handleAcceptModalCancel();
  563 + refreshOrderList();
703 } catch (error) { 564 } catch (error) {
704 - // 5. 操作失败处理  
705 console.error('验收失败:', error); 565 console.error('验收失败:', error);
706 - uni.showToast({title: '验收提交失败,请重试', icon: 'none', duration: 1000}); 566 + uni.showToast({title: '验收提交失败,请重试', icon: 'none'});
707 } 567 }
708 }; 568 };
709 -// 页面初始化 569 +
  570 +// ========== 页面生命周期 ==========
710 onLoad(() => { 571 onLoad(() => {
711 - // 初始化加载列表  
712 - paging.value?.reload(); 572 + refreshOrderList();
713 }); 573 });
714 </script> 574 </script>
715 575
@@ -734,15 +594,10 @@ onLoad(() =&gt; { @@ -734,15 +594,10 @@ onLoad(() =&gt; {
734 .select-wrap { 594 .select-wrap {
735 width: 120rpx; 595 width: 120rpx;
736 margin-right: 20rpx; 596 margin-right: 20rpx;
737 -  
738 - :deep(.u-select) { 597 + :deep(.u-select), :deep(.u-input__placeholder) {
739 width: 100%; 598 width: 100%;
740 font-size: 28rpx; 599 font-size: 28rpx;
741 } 600 }
742 -  
743 - :deep(.u-input__placeholder) {  
744 - font-size: 28rpx;  
745 - }  
746 } 601 }
747 602
748 .search-input-wrap { 603 .search-input-wrap {
@@ -758,31 +613,14 @@ onLoad(() =&gt; { @@ -758,31 +613,14 @@ onLoad(() =&gt; {
758 box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04); 613 box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
759 } 614 }
760 615
761 -.card-body {  
762 -  
763 -}  
764 -  
765 -// 回退modal样式  
766 -.reject-modal-content { 616 +// 弹窗公共样式
  617 +.reject-modal-content, .accept-modal-content {
767 width: 100%; 618 width: 100%;
768 box-sizing: border-box; 619 box-sizing: border-box;
769 padding: 10rpx 0; 620 padding: 10rpx 0;
770 } 621 }
771 622
772 -.textarea-label {  
773 - font-size: 28rpx;  
774 - color: #333;  
775 - margin-bottom: 10rpx;  
776 -  
777 - .required-mark {  
778 - color: #f56c6c;  
779 - margin-left: 4rpx;  
780 - }  
781 -}  
782 -  
783 .upload-wrap { 623 .upload-wrap {
784 - margin-top: 20rpx;  
785 -  
786 .upload-title { 624 .upload-title {
787 font-size: 28rpx; 625 font-size: 28rpx;
788 color: #333; 626 color: #333;
@@ -790,37 +628,20 @@ onLoad(() =&gt; { @@ -790,37 +628,20 @@ onLoad(() =&gt; {
790 } 628 }
791 } 629 }
792 630
793 -.mt-20 {  
794 - margin-top: 20rpx;  
795 -}  
796 -  
797 -.mt-30 {  
798 - margin-top: 30rpx;  
799 -}  
800 -  
801 -// 养护组长验收弹窗样式  
802 -.accept-modal-content {  
803 - width: 100%;  
804 - box-sizing: border-box;  
805 -}  
806 - 631 +// 验收弹窗单选框样式
807 .radio-group-wrap { 632 .radio-group-wrap {
808 display: flex; 633 display: flex;
809 align-items: center; 634 align-items: center;
810 - gap: 40rpx; // 单选框之间的间距 635 + gap: 40rpx;
811 font-size: 28rpx; 636 font-size: 28rpx;
812 margin-bottom: 20rpx; 637 margin-bottom: 20rpx;
813 } 638 }
814 639
815 .textarea-wrap { 640 .textarea-wrap {
816 width: 100%; 641 width: 100%;
817 - margin-top: 30rpx;  
818 } 642 }
819 643
820 -.modal-btn-wrap {  
821 - display: flex;  
822 - align-items: center;  
823 - justify-content: flex-end;  
824 - padding-right: 10rpx;  
825 -} 644 +// 间距公共样式
  645 +.mt-20 { margin-top: 20rpx; }
  646 +.mt-30 { margin-top: 30rpx; }
826 </style> 647 </style>
827 \ No newline at end of file 648 \ No newline at end of file
pages-sub/problem/regional-order-manage/add-order.vue
@@ -63,7 +63,7 @@ @@ -63,7 +63,7 @@
63 <!-- 4. 问题照片 --> 63 <!-- 4. 问题照片 -->
64 <up-form-item label="问题照片" prop="problemImgs" required> 64 <up-form-item label="问题照片" prop="problemImgs" required>
65 <up-upload 65 <up-upload
66 - :file-list="problemImgs.imgList.value||[]" 66 + :file-list="problemImgs.imgList.value || []"
67 @after-read="problemImgs.uploadImgs" 67 @after-read="problemImgs.uploadImgs"
68 @delete="problemImgs.deleteImg" 68 @delete="problemImgs.deleteImg"
69 multiple 69 multiple
@@ -75,11 +75,9 @@ @@ -75,11 +75,9 @@
75 ></up-upload> 75 ></up-upload>
76 </up-form-item> 76 </up-form-item>
77 77
78 - <!-- 派单情况分组(放在问题照片下方,增加间距) -->  
79 -<!-- <up-gap height="20" bgColor="#bbb"></up-gap>--> 78 + <!-- 派单情况分组 -->
80 <view class="dispatch-group"> 79 <view class="dispatch-group">
81 <view class="dispatch-title">派单情况</view> 80 <view class="dispatch-title">派单情况</view>
82 -  
83 <!-- 业务线单选框 --> 81 <!-- 业务线单选框 -->
84 <up-form-item 82 <up-form-item
85 label="业务线" 83 label="业务线"
@@ -117,7 +115,7 @@ @@ -117,7 +115,7 @@
117 border="none" 115 border="none"
118 ></up-input> 116 ></up-input>
119 <template #right> 117 <template #right>
120 - <up-icon name="arrow-right" size="16" ></up-icon> 118 + <up-icon name="arrow-right" size="16"></up-icon>
121 </template> 119 </template>
122 </up-form-item> 120 </up-form-item>
123 121
@@ -156,11 +154,7 @@ @@ -156,11 +154,7 @@
156 ></up-input> 154 ></up-input>
157 <template #right> 155 <template #right>
158 <view v-if="workOrderForm.expectedFinishDate" @click.stop> 156 <view v-if="workOrderForm.expectedFinishDate" @click.stop>
159 - <up-icon  
160 - name="close"  
161 - size="16"  
162 - @click.stop="clearExpectedFinishDate"  
163 - ></up-icon> 157 + <up-icon name="close" size="16" @click.stop="clearExpectedFinishDate"></up-icon>
164 </view> 158 </view>
165 <up-icon name="arrow-right" size="16" v-else></up-icon> 159 <up-icon name="arrow-right" size="16" v-else></up-icon>
166 </template> 160 </template>
@@ -170,11 +164,7 @@ @@ -170,11 +164,7 @@
170 164
171 <!-- 底部提交按钮 --> 165 <!-- 底部提交按钮 -->
172 <view class="fixed-bottom-btn-wrap"> 166 <view class="fixed-bottom-btn-wrap">
173 - <up-button  
174 - type="primary"  
175 - text="提交工单"  
176 - @click="submitWorkOrder"  
177 - ></up-button> 167 + <up-button type="primary" text="提交工单" @click="submitWorkOrder"></up-button>
178 </view> 168 </view>
179 169
180 <!-- 合并后的通用下拉弹窗 --> 170 <!-- 合并后的通用下拉弹窗 -->
@@ -199,116 +189,73 @@ @@ -199,116 +189,73 @@
199 </template> 189 </template>
200 190
201 <script setup> 191 <script setup>
202 -import { ref, reactive } from 'vue'  
203 -import { onReady, onShow, onLoad } from '@dcloudio/uni-app'; 192 +import { ref, reactive, nextTick } from 'vue'
  193 +import { onReady, onShow, onLoad, onBackPress } from '@dcloudio/uni-app';
204 import { useUploadImgs } from '@/common/utils/useUploadImgs' 194 import { useUploadImgs } from '@/common/utils/useUploadImgs'
205 import { getRoadListByLatLng } from '@/api/common' 195 import { getRoadListByLatLng } from '@/api/common'
206 -import { daquUniversalApproval, qyUniversalApproval,qyWorkorderCreate,  
207 - dcyUniversalApproval, daquWorkorderCreate,  
208 - dcyWorkorderCreate } from '@/api/regional-order-manage/regional-order-manage' 196 +import {
  197 + daquUniversalApproval, qyUniversalApproval, qyWorkorderCreate,
  198 + dcyUniversalApproval, daquWorkorderCreate, dcyWorkorderCreate
  199 +} from '@/api/regional-order-manage/regional-order-manage'
209 import { timeFormat } from '@/uni_modules/uview-plus' 200 import { timeFormat } from '@/uni_modules/uview-plus'
210 import { nextStepMap } from '@/common/utils/common' 201 import { nextStepMap } from '@/common/utils/common'
211 import { useUserStore } from '@/pinia/user'; 202 import { useUserStore } from '@/pinia/user';
212 203
213 -// ========== 状态管理 ==========  
214 -const userStore = useUserStore(); 204 +// ========== 【常量配置区】- 硬编码集中管理,便于维护 ==========
  205 +const CONST = {
  206 + // 字典编码
  207 + DICT_ORDER_NAME: 'work_name',
  208 + DICT_PRESSING_TYPE: 'workorder_pressing_type',
  209 + // 上传配置
  210 + UPLOAD_CONFIG: { maxCount: 3, uploadText: '选择问题照片', sizeType: ['compressed'] },
  211 + // 路由地址
  212 + PAGE_ORDER_LIST: '/pages-sub/problem/regional-order-manage/index',
  213 + // 业务线映射
  214 + BUSI_LINE_MAP: { yl: '园林', sz: '市政', wy: '物业', '园林': 'yl', '市政': 'sz', '物业': 'wy' },
  215 + // 角色标识
  216 + ROLE: { MANAGER: 'regional_manager', INSPECTOR: 'Inspector_global', PATROL: 'patrol_global' }
  217 +}
215 218
216 -// ========== 业务线相关状态 ==========  
217 -// 业务线映射表  
218 -const busiLineMap = ref({  
219 - 'yl': '园林',  
220 - 'sz': '市政',  
221 - 'wy': '物业',  
222 - '园林': 'yl',  
223 - '市政': 'sz',  
224 - '物业': 'wy'  
225 -}); 219 +// ========== 【全局实例 & 基础状态】 ==========
  220 +const userStore = useUserStore();
  221 +const USER_ROLES = userStore.userInfo?.roles || [];
  222 +const workOrderFormRef = ref(null);
  223 +const showActionSheet = ref(false);
  224 +const show = ref(false);
  225 +const expectedFinishDate = ref(Date.now());
  226 +const isRenew = ref(false);
  227 +const renewOrderData = ref(null);
226 228
227 -// 业务线选项列表 229 +// ========== 【下拉弹窗数据】 ==========
  230 +const currentActionSheetData = reactive({ type: '', list: [], title: '' });
  231 +const roadNameList = ref([]);
  232 +const orderNameList = ref([]);
  233 +const pressingTypeList = ref([]);
228 const busiLineOptions = ref([]); 234 const busiLineOptions = ref([]);
229 -const formatBusiLineOptions = () => {  
230 - if (!userStore.userInfo?.user?.busiLine) {  
231 - busiLineOptions.value = [];  
232 - return;  
233 - }  
234 - const rawBusiLines = userStore.userInfo.user.busiLine.split(',');  
235 - busiLineOptions.value = rawBusiLines.map(item => ({  
236 - name: busiLineMap.value[item.trim()]  
237 - }));  
238 -};  
239 -  
240 -// 工具方法:通过中文名称获取对应的英文标识  
241 -const getBusiLineEnByCn = (cnName) => {  
242 - return busiLineMap.value[cnName] || '';  
243 -};  
244 235
245 -// ========== 表单Ref ==========  
246 -const workOrderFormRef = ref(null)  
247 -  
248 -// ========== 公共上传逻辑复用 ========== 236 +// ========== 【上传逻辑复用】 ==========
249 const problemImgs = useUploadImgs({ 237 const problemImgs = useUploadImgs({
250 - maxCount: 3,  
251 - uploadText: '选择问题照片',  
252 - sizeType: ['compressed'], 238 + ...CONST.UPLOAD_CONFIG,
253 formRef: workOrderFormRef, 239 formRef: workOrderFormRef,
254 fieldName: 'problemImgs' 240 fieldName: 'problemImgs'
255 }) 241 })
  242 +// 图片数组兜底,防止非数组报错
  243 +if (!Array.isArray(problemImgs.rawImgList.value)) problemImgs.rawImgList.value = [];
256 244
257 -if (!Array.isArray(problemImgs.rawImgList.value)) {  
258 - problemImgs.rawImgList.value = [];  
259 -}  
260 -  
261 -// ========== 页面状态 ==========  
262 -const showActionSheet = ref(false)  
263 -const currentActionSheetData = reactive({  
264 - type: '',  
265 - list: [],  
266 - title: ''  
267 -})  
268 -const show = ref(false)  
269 -const expectedFinishDate = ref(Date.now())  
270 -  
271 -// ========== 重新提交相关状态 ==========  
272 -const isRenew = ref(false);  
273 -const renewOrderData = ref(null);  
274 -  
275 -// ========== 下拉列表数据 ==========  
276 -const roadNameList = ref([])  
277 -const orderNameList = ref([])  
278 -const pressingTypeList = ref([])  
279 -  
280 -// ========== 工单表单数据 ========== 245 +// ========== 【工单表单数据】 ==========
281 const workOrderForm = reactive({ 246 const workOrderForm = reactive({
282 - busiLineCn: '',  
283 - roadId: 0,  
284 - roadName: '',  
285 - workLocation: '',  
286 - orderName: '',  
287 - pressingType: '',  
288 - pressingTypeName: '',  
289 - problemDesc: '',  
290 - lat: 0,  
291 - lon: 0,  
292 - expectedFinishDate: '', 247 + busiLineCn: '', roadId: 0, roadName: '', workLocation: '',
  248 + orderName: '', pressingType: '', pressingTypeName: '',
  249 + problemDesc: '', lat: 0, lon: 0, expectedFinishDate: ''
293 }) 250 })
294 251
295 -// ========== 表单校验规则 ========== 252 +// ========== 【表单校验规则】 ==========
296 const workOrderFormRules = reactive({ 253 const workOrderFormRules = reactive({
297 - busiLineCn: [  
298 - { type: 'string', required: true, message: '请选择业务线', trigger: ['change', 'blur'] }  
299 - ],  
300 - workLocation: [  
301 - { type: 'string', required: true, message: '请选择工单位置', trigger: ['change', 'blur'] }  
302 - ],  
303 - roadName: [  
304 - { type: 'string', required: true, message: '请选择道路名称', trigger: ['change', 'blur'] }  
305 - ],  
306 - orderName: [  
307 - { type: 'string', required: true, message: '请选择工单名称', trigger: ['change', 'blur'] }  
308 - ],  
309 - pressingTypeName: [  
310 - { type: 'string', required: true, message: '请选择紧急程度', trigger: ['change'] }  
311 - ], 254 + busiLineCn: [{ type: 'string', required: true, message: '请选择业务线', trigger: ['change', 'blur'] }],
  255 + workLocation: [{ type: 'string', required: true, message: '请选择工单位置', trigger: ['change', 'blur'] }],
  256 + roadName: [{ type: 'string', required: true, message: '请选择道路名称', trigger: ['change', 'blur'] }],
  257 + orderName: [{ type: 'string', required: true, message: '请选择工单名称', trigger: ['change', 'blur'] }],
  258 + pressingTypeName: [{ type: 'string', required: true, message: '请选择紧急程度', trigger: ['change'] }],
312 problemDesc: [ 259 problemDesc: [
313 { type: 'string', required: true, message: '请输入情况描述', trigger: ['change', 'blur'] }, 260 { type: 'string', required: true, message: '请输入情况描述', trigger: ['change', 'blur'] },
314 { type: 'string', min: 3, max: 200, message: '情况描述需3-200字', trigger: ['change', 'blur'] } 261 { type: 'string', min: 3, max: 200, message: '情况描述需3-200字', trigger: ['change', 'blur'] }
@@ -316,268 +263,251 @@ const workOrderFormRules = reactive({ @@ -316,268 +263,251 @@ const workOrderFormRules = reactive({
316 problemImgs: [problemImgs.imgValidateRule] 263 problemImgs: [problemImgs.imgValidateRule]
317 }) 264 })
318 265
319 -// ========== 生命周期 ========== 266 +// ========== 【工具方法封装】- 抽离通用逻辑,消灭重复代码 ==========
  267 +/**
  268 + * 业务线中文转英文
  269 + */
  270 +const busiLineCnToEn = (cn) => CONST.BUSI_LINE_MAP[cn] || '';
  271 +/**
  272 + * 业务线英文转中文
  273 + */
  274 +const busiLineEnToCn = (en) => CONST.BUSI_LINE_MAP[en] || '';
  275 +/**
  276 + * 统一跳转返回工单列表页
  277 + */
  278 +const toOrderListPage = () => uni.reLaunch({ url: CONST.PAGE_ORDER_LIST });
  279 +/**
  280 + * 格式化时间戳 - 增加空值兜底
  281 + */
  282 +const formatDate = (dateVal, fmt = 'yyyy-mm-dd hh:MM:ss') => dateVal ? timeFormat(dateVal, fmt) : '';
  283 +/**
  284 + * 时间转时间戳 - 增加空值兜底
  285 + */
  286 +const dateToTimestamp = (dateStr) => dateStr ? new Date(dateStr).getTime() : '';
  287 +
  288 +// ========== 【生命周期】 ==========
320 onLoad((options) => { 289 onLoad((options) => {
321 - // 初始化业务线选项  
322 - formatBusiLineOptions(); 290 + // 初始化工单线选项
  291 + initBusiLineOptions();
323 // 默认选中第一个业务线 292 // 默认选中第一个业务线
324 - if (busiLineOptions.value.length > 0) {  
325 - workOrderForm.busiLineCn = busiLineOptions.value[0].name;  
326 - }  
327 -  
328 - // 判断是否为重新提交状态  
329 - if (options.isRenew == 1 && options.tempKey) {  
330 - isRenew.value = true;  
331 - const tempKey = options.tempKey;  
332 -  
333 - try {  
334 - const orderData = uni.getStorageSync(tempKey);  
335 - console.log(orderData)  
336 - if (orderData && typeof orderData === 'object') {  
337 - renewOrderData.value = orderData;  
338 - echoOrderData(renewOrderData.value);  
339 - } else {  
340 - uni.showToast({ title: '工单数据不存在,无法重新提交', icon: 'none' });  
341 - setTimeout(() => uni.navigateBack(), 1000);  
342 - return;  
343 - }  
344 - } catch (error) {  
345 - console.error('读取工单数据失败:', error);  
346 - uni.showToast({ title: '数据读取异常,无法重新提交', icon: 'none' });  
347 - setTimeout(() => uni.navigateBack(), 1000);  
348 - return;  
349 - } finally {  
350 - uni.removeStorageSync(tempKey);  
351 - }  
352 - } 293 + workOrderForm.busiLineCn = busiLineOptions.value[0]?.name || '';
  294 + // 处理重新提交逻辑
  295 + if (options.isRenew == 1 && options.tempKey) handleRenewOptions(options.tempKey);
353 }); 296 });
354 297
355 onReady(() => { 298 onReady(() => {
356 - if (workOrderFormRef.value) {  
357 - workOrderFormRef.value.setRules(workOrderFormRules)  
358 - }  
359 - console.log('工单表单规则初始化完成')  
360 -}) 299 + // 修复bug:nextTick确保表单ref一定挂载完成,再设置规则
  300 + nextTick(() => {
  301 + workOrderFormRef.value?.setRules(workOrderFormRules);
  302 + });
  303 +});
361 304
362 onShow(() => { 305 onShow(() => {
363 - // 初始化工单名称列表  
364 - orderNameList.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('work_name'))  
365 - // 初始化紧急程度列表  
366 - pressingTypeList.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('workorder_pressing_type'))  
367 -}) 306 + // 初始化下拉字典数据
  307 + orderNameList.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList(CONST.DICT_ORDER_NAME));
  308 + pressingTypeList.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList(CONST.DICT_PRESSING_TYPE));
  309 +});
368 310
369 -// ========== 核心方法 ==========  
370 -const echoOrderData = (orderItem) => {  
371 - // 回显业务线  
372 - if (orderItem.busiLine) {  
373 - workOrderForm.busiLineCn = busiLineMap.value[orderItem.busiLine]; 311 +// ========== 【核心业务方法】 ==========
  312 +/**
  313 + * 初始化工单线选项
  314 + */
  315 +const initBusiLineOptions = () => {
  316 + const rawBusiLines = userStore.userInfo?.user?.busiLine?.split(',') || [];
  317 + busiLineOptions.value = rawBusiLines.map(item => ({ name: busiLineEnToCn(item.trim()) }));
  318 +};
  319 +
  320 +/**
  321 + * 处理重新提交的参数和数据回显
  322 + */
  323 +const handleRenewOptions = async (tempKey) => {
  324 + isRenew.value = true;
  325 + try {
  326 + const orderData = uni.getStorageSync(tempKey);
  327 + if (orderData && typeof orderData === 'object') {
  328 + renewOrderData.value = orderData;
  329 + echoOrderData(orderData);
  330 + } else {
  331 + uni.showToast({ title: '工单数据不存在,无法重新提交', icon: 'none' });
  332 + setTimeout(toOrderListPage, 1000);
  333 + }
  334 + } catch (error) {
  335 + console.error('读取工单数据失败:', error);
  336 + uni.showToast({ title: '数据读取异常,无法重新提交', icon: 'none' });
  337 + setTimeout(toOrderListPage, 1000);
  338 + } finally {
  339 + uni.removeStorageSync(tempKey);
374 } 340 }
  341 +};
375 342
376 - // 回显基础字段 343 +/**
  344 + * 工单数据回显
  345 + */
  346 +const echoOrderData = (orderItem) => {
  347 + // 基础字段回显
  348 + console.log(orderItem)
  349 + console.log('123')
  350 + workOrderForm.busiLineCn = busiLineEnToCn(orderItem.busiLine);
377 workOrderForm.roadId = orderItem.roadId || 0; 351 workOrderForm.roadId = orderItem.roadId || 0;
378 workOrderForm.roadName = orderItem.roadName || ''; 352 workOrderForm.roadName = orderItem.roadName || '';
379 workOrderForm.workLocation = orderItem.lonLatAddress || orderItem.roadName || ''; 353 workOrderForm.workLocation = orderItem.lonLatAddress || orderItem.roadName || '';
380 workOrderForm.orderName = orderItem.orderName || ''; 354 workOrderForm.orderName = orderItem.orderName || '';
381 workOrderForm.pressingType = orderItem.pressingType || ''; 355 workOrderForm.pressingType = orderItem.pressingType || '';
382 - workOrderForm.pressingTypeName = uni.$dict.getDictLabel('workorder_pressing_type', orderItem.pressingType) || ''; 356 + workOrderForm.pressingTypeName = uni.$dict.getDictLabel(CONST.DICT_PRESSING_TYPE, orderItem.pressingType) || '';
383 workOrderForm.problemDesc = orderItem.remark || ''; 357 workOrderForm.problemDesc = orderItem.remark || '';
384 workOrderForm.lat = orderItem.lat || 0; 358 workOrderForm.lat = orderItem.lat || 0;
385 workOrderForm.lon = orderItem.lon || 0; 359 workOrderForm.lon = orderItem.lon || 0;
386 - workOrderForm.expectedFinishDate = timeFormat(orderItem.expectedFinishDate, 'yyyy-mm-dd hh:MM:ss') || '';  
387 -  
388 - // 回显图片  
389 - if (orderItem.problemsImgs && Array.isArray(orderItem.problemsImgs) && orderItem.problemsImgs.length > 0) {  
390 - const imgList = orderItem.problemsImgs.map((imgUrl, index) => ({  
391 - url: imgUrl,  
392 - name: `renew_img_${index}`,  
393 - status: 'success'  
394 - })); 360 + workOrderForm.expectedFinishDate = orderItem.expectedFinishDate?"":formatDate(orderItem.expectedFinishDate);
  361 +
  362 + // 图片回显 - 兼容异常格式
  363 + if (Array.isArray(orderItem.problemsImgs) && orderItem.problemsImgs.length) {
  364 + const imgList = orderItem.problemsImgs.map((url, idx) => ({ url, name: `renew_img_${idx}`, status: 'success' }));
395 problemImgs.imgList.value = imgList; 365 problemImgs.imgList.value = imgList;
396 problemImgs.rawImgList.value = imgList; 366 problemImgs.rawImgList.value = imgList;
397 } 367 }
398 368
399 // 自动获取道路列表 369 // 自动获取道路列表
400 - if (orderItem.lat && orderItem.lon) {  
401 - getRoadListByBusiLine();  
402 - } 370 + if (orderItem.lat && orderItem.lon) getRoadListByBusiLine();
403 }; 371 };
404 372
405 -// 业务线切换事件 373 +/**
  374 + * 业务线切换事件
  375 + */
406 const handleBusiLineChange = () => { 376 const handleBusiLineChange = () => {
407 workOrderForm.roadName = ''; 377 workOrderForm.roadName = '';
408 workOrderForm.roadId = 0; 378 workOrderForm.roadId = 0;
409 roadNameList.value = []; 379 roadNameList.value = [];
410 - if (workOrderForm.workLocation) {  
411 - getRoadListByBusiLine();  
412 - } 380 + workOrderForm.workLocation && getRoadListByBusiLine();
413 }; 381 };
414 382
415 -// 获取道路列表(带业务线) 383 +/**
  384 + * 根据坐标+业务线获取道路列表
  385 + */
416 const getRoadListByBusiLine = async () => { 386 const getRoadListByBusiLine = async () => {
417 - if (!workOrderForm.lat || !workOrderForm.lon) {  
418 - return;  
419 - }  
420 - const busiLineEn = getBusiLineEnByCn(workOrderForm.busiLineCn);  
421 - if (!busiLineEn) {  
422 - uni.showToast({ title: '业务线标识异常', icon: 'none' });  
423 - return;  
424 - } 387 + if (!workOrderForm.lat || !workOrderForm.lon) return;
  388 + const busiLineEn = busiLineCnToEn(workOrderForm.busiLineCn);
  389 + if (!busiLineEn) return uni.showToast({ title: '业务线选择异常', icon: 'none' });
425 390
426 try { 391 try {
427 uni.showLoading({ title: '获取道路名称中...' }); 392 uni.showLoading({ title: '获取道路名称中...' });
428 - const roadRes = await getRoadListByLatLng({  
429 - busiLine: busiLineEn,  
430 - latitude: workOrderForm.lat,  
431 - longitude: workOrderForm.lon  
432 - });  
433 - uni.hideLoading();  
434 - if (Array.isArray(roadRes)) {  
435 - roadNameList.value = roadRes.map((item) => ({  
436 - name: item.roadName || '',  
437 - code: item.roadCode || '',  
438 - id: item.roadId || 0  
439 - }));  
440 - } else {  
441 - roadNameList.value = [{ name: '未查询到道路名称', code: '', id: 0 }];  
442 - uni.showToast({ title: '未查询到该位置的道路信息', icon: 'none' });  
443 - } 393 + const roadRes = await getRoadListByLatLng({ busiLine: busiLineEn, latitude: workOrderForm.lat, longitude: workOrderForm.lon });
  394 + console.log(roadRes)
  395 + roadNameList.value = Array.isArray(roadRes)
  396 + ? roadRes.map(item => ({ name: item.roadName || '', code: item.roadCode || '', id: item.roadId || 0 }))
  397 + : [{ name: '未查询到道路名称', code: '', id: 0 }];
  398 + console.log(roadNameList)
  399 + !Array.isArray(roadRes) && uni.showToast({ title: '未查询到该位置的道路信息', icon: 'none' });
444 } catch (err) { 400 } catch (err) {
445 - uni.hideLoading();  
446 console.error('获取道路名称失败:', err); 401 console.error('获取道路名称失败:', err);
447 - uni.showToast({ title: '获取道路名称失败,请重试', icon: 'none' });  
448 roadNameList.value = [{ name: '获取失败,请重新选择位置', code: '', id: 0 }]; 402 roadNameList.value = [{ name: '获取失败,请重新选择位置', code: '', id: 0 }];
  403 + uni.showToast({ title: '获取道路名称失败,请重试', icon: 'none' });
  404 + } finally {
  405 + uni.hideLoading();
449 } 406 }
450 }; 407 };
451 408
452 -// ========== 通用弹窗方法 ========== 409 +// ========== 【通用下拉弹窗方法】 ==========
453 const handleActionSheetOpen = (type) => { 410 const handleActionSheetOpen = (type) => {
454 if (type === 'roadName' && !workOrderForm.workLocation) { 411 if (type === 'roadName' && !workOrderForm.workLocation) {
455 - uni.showToast({ title: '请先选择工单位置', icon: 'none' })  
456 - return 412 + return uni.showToast({ title: '请先选择工单位置', icon: 'none' });
457 } 413 }
458 -  
459 const configMap = { 414 const configMap = {
460 - roadName: {  
461 - title: '请选择道路名称',  
462 - list: roadNameList.value  
463 - },  
464 - orderName: {  
465 - title: '请选择工单名称',  
466 - list: orderNameList.value  
467 - },  
468 - pressingType: {  
469 - title: '请选择紧急程度',  
470 - list: pressingTypeList.value  
471 - }  
472 - }  
473 -  
474 - currentActionSheetData.type = type  
475 - currentActionSheetData.title = configMap[type].title  
476 - currentActionSheetData.list = configMap[type].list  
477 - showActionSheet.value = true  
478 -} 415 + roadName: { title: '请选择道路名称', list: roadNameList.value },
  416 + orderName: { title: '请选择工单名称', list: orderNameList.value },
  417 + pressingType: { title: '请选择紧急程度', list: pressingTypeList.value }
  418 + };
  419 + Object.assign(currentActionSheetData, configMap[type], { type });
  420 + showActionSheet.value = true;
  421 +};
479 422
480 const handleActionSheetClose = () => { 423 const handleActionSheetClose = () => {
481 - showActionSheet.value = false  
482 - currentActionSheetData.type = ''  
483 - currentActionSheetData.list = []  
484 - currentActionSheetData.title = ''  
485 -} 424 + showActionSheet.value = false;
  425 + Object.assign(currentActionSheetData, { type: '', list: [], title: '' });
  426 +};
486 427
487 const handleActionSheetSelect = (e) => { 428 const handleActionSheetSelect = (e) => {
488 - const { type } = currentActionSheetData 429 + const { type } = currentActionSheetData;
489 switch (type) { 430 switch (type) {
490 case 'roadName': 431 case 'roadName':
491 - workOrderForm.roadName = e.name  
492 - workOrderForm.roadId = e.code  
493 - workOrderFormRef.value?.validateField('roadName')  
494 - break 432 + workOrderForm.roadName = e.name;
  433 + workOrderForm.roadId = e.code;
  434 + break;
495 case 'orderName': 435 case 'orderName':
496 - workOrderForm.orderName = e.name  
497 - workOrderFormRef.value?.validateField('orderName')  
498 - break 436 + workOrderForm.orderName = e.name;
  437 + break;
499 case 'pressingType': 438 case 'pressingType':
500 - workOrderForm.pressingType = e.value  
501 - workOrderForm.pressingTypeName = e.name  
502 - workOrderFormRef.value?.validateField('pressingTypeName')  
503 - break 439 + workOrderForm.pressingType = e.value;
  440 + workOrderForm.pressingTypeName = e.name;
  441 + break;
504 } 442 }
505 - showActionSheet.value = false  
506 -}  
507 -  
508 -const navigateBack = () => {  
509 - uni.reLaunch({  
510 - url: '/pages-sub/problem/work-order-manage/index',  
511 - fail: () => {  
512 - uni.navigateBack({ delta: 2 });  
513 - }  
514 - });  
515 -} 443 + workOrderFormRef.value?.validateField(type === 'roadName' ? 'roadName' : type === 'orderName' ? 'orderName' : 'pressingTypeName');
  444 + handleActionSheetClose();
  445 +};
516 446
517 -// 清除希望完成时间  
518 -const clearExpectedFinishDate = ()=> {  
519 - workOrderForm.expectedFinishDate = ''  
520 -} 447 +// ========== 【页面交互方法】 ==========
  448 +/** 清除希望完成时间 */
  449 +const clearExpectedFinishDate = () => workOrderForm.expectedFinishDate = '';
  450 +/** 隐藏软键盘 */
  451 +const hideKeyboard = () => uni.hideKeyboard();
  452 +/** 时间选择确认 */
  453 +const expectedFinishDateConfirm = (e) => {
  454 + workOrderForm.expectedFinishDate = formatDate(e.value);
  455 + show.value = false;
  456 +};
521 457
522 -// 选择工单位置 458 +/** 选择工单位置 */
523 const chooseWorkLocation = () => { 459 const chooseWorkLocation = () => {
524 uni.chooseLocation({ 460 uni.chooseLocation({
525 success: async (res) => { 461 success: async (res) => {
526 - workOrderForm.roadName = ''  
527 - workOrderForm.roadId = 0  
528 - roadNameList.value = []  
529 -  
530 - workOrderForm.workLocation = res.name  
531 - workOrderForm.lat = res.latitude  
532 - workOrderForm.lon = res.longitude  
533 -  
534 - workOrderFormRef.value?.validateField('workLocation')  
535 - workOrderFormRef.value?.validateField('roadName')  
536 - 462 + workOrderForm.roadName = '';
  463 + workOrderForm.roadId = 0;
  464 + roadNameList.value = [];
  465 + workOrderForm.workLocation = res.name;
  466 + workOrderForm.lat = res.latitude;
  467 + workOrderForm.lon = res.longitude;
  468 + workOrderFormRef.value?.validateField(['workLocation', 'roadName']);
537 await getRoadListByBusiLine(); 469 await getRoadListByBusiLine();
538 }, 470 },
539 fail: (err) => { 471 fail: (err) => {
540 - console.error('选择位置失败:', err)  
541 - uni.showToast({ title: '选择位置失败:' + err.errMsg, icon: 'none' }) 472 + console.error('选择位置失败:', err);
  473 + const errMsg = err.errMsg.includes('auth deny') ? '请开启位置权限后重试' : '选择位置失败,请重试';
  474 + uni.showToast({ title: errMsg, icon: 'none' });
542 } 475 }
543 }) 476 })
544 -}  
545 -  
546 -// 完成时间确认  
547 -const expectedFinishDateConfirm = (e) => {  
548 - workOrderForm.expectedFinishDate = timeFormat(e.value, 'yyyy-mm-dd hh:MM:ss')  
549 - show.value = false  
550 -} 477 +};
  478 +// 新增页返回直接回列表页
  479 +onBackPress(() => {
  480 + uni.redirectTo({
  481 + url: '/pages-sub/problem/regional-order-manage/index'
  482 + })
  483 + return true;
  484 +})
551 485
552 -// 隐藏键盘  
553 -const hideKeyboard = () => {  
554 - uni.hideKeyboard()  
555 -} 486 +// ========== 【封装:统一接口调用方法】 ==========
  487 +const callApiByRole = async (params, isRenewFlag) => {
  488 + if (isRenewFlag) {
  489 + if (USER_ROLES.includes(CONST.ROLE.MANAGER)) return await daquUniversalApproval(params);
  490 + if (USER_ROLES.includes(CONST.ROLE.INSPECTOR)) return await dcyUniversalApproval(params);
  491 + if (USER_ROLES.includes(CONST.ROLE.PATROL)) return await qyUniversalApproval(params);
  492 + } else {
  493 + if (USER_ROLES.includes(CONST.ROLE.MANAGER)) return await daquWorkorderCreate(params);
  494 + if (USER_ROLES.includes(CONST.ROLE.INSPECTOR)) return await dcyWorkorderCreate(params);
  495 + if (USER_ROLES.includes(CONST.ROLE.PATROL)) return await qyWorkorderCreate(params);
  496 + }
  497 +};
556 498
557 -// 提交工单 499 +// ========== 【核心:提交工单】 ==========
558 const submitWorkOrder = async () => { 500 const submitWorkOrder = async () => {
559 try { 501 try {
560 - await workOrderFormRef.value.validate()  
561 -  
562 - const busiLineEn = getBusiLineEnByCn(workOrderForm.busiLineCn);  
563 - if (!busiLineEn) {  
564 - uni.showToast({ title: '业务线选择异常,请重新选择', icon: 'none' });  
565 - return;  
566 - }  
567 -  
568 - const commonSubmitData = {  
569 - // roadId: workOrderForm.roadId,  
570 - // roadName: workOrderForm.roadName,  
571 -  
572 - // roadId: 550,  
573 - // roadName:'东明胡同(三海)',  
574 -  
575 - roadId: 551,  
576 - roadName:'地安门西大街51号',  
577 - //  
578 - // roadId: 553,  
579 - // roadName:'什刹海前海南沿',  
580 - 502 + // 表单校验
  503 + await workOrderFormRef.value.validate();
  504 + const busiLineEn = busiLineCnToEn(workOrderForm.busiLineCn);
  505 + if (!busiLineEn) return uni.showToast({ title: '业务线选择异常,请重新选择', icon: 'none' });
  506 +
  507 + // 组装公共提交参数
  508 + const baseParams = {
  509 + roadId: workOrderForm.roadId,
  510 + roadName: workOrderForm.roadName,
581 problemsImgs: problemImgs.getSuccessImgUrls(), 511 problemsImgs: problemImgs.getSuccessImgUrls(),
582 remark: workOrderForm.problemDesc.trim(), 512 remark: workOrderForm.problemDesc.trim(),
583 latLonType: 2, 513 latLonType: 2,
@@ -586,77 +516,37 @@ const submitWorkOrder = async () =&gt; { @@ -586,77 +516,37 @@ const submitWorkOrder = async () =&gt; {
586 lonLatAddress: workOrderForm.workLocation, 516 lonLatAddress: workOrderForm.workLocation,
587 pressingType: workOrderForm.pressingType, 517 pressingType: workOrderForm.pressingType,
588 orderName: workOrderForm.orderName, 518 orderName: workOrderForm.orderName,
589 - expectedFinishDate: workOrderForm.expectedFinishDate==""?"":new Date(workOrderForm.expectedFinishDate).getTime(), 519 + expectedFinishDate: dateToTimestamp(workOrderForm.expectedFinishDate),
590 sourceId: 1, 520 sourceId: 1,
591 sourceName: workOrderForm.busiLineCn, 521 sourceName: workOrderForm.busiLineCn,
592 busiLine: busiLineEn 522 busiLine: busiLineEn
593 - }  
594 -  
595 - uni.showLoading({ title: '提交中...' })  
596 - let res  
597 -  
598 - if (isRenew.value) {  
599 - const renewSubmitData = {  
600 - workerDataId: renewOrderData.value.id,  
601 - taskKey: renewOrderData.value.taskKey,  
602 - taskId: renewOrderData.value.taskId,  
603 - operateType: nextStepMap[renewOrderData.value.taskKey]?.operateTypeRenew || '',  
604 - agree: 0,  
605 - reason: workOrderForm.problemDesc.trim(),  
606 - ...commonSubmitData  
607 - }  
608 -  
609 - if( userStore.userInfo.roles.includes('regional_manager')){ // 大区经理  
610 - res = await daquUniversalApproval(renewSubmitData)  
611 - }  
612 -  
613 - if( userStore.userInfo.roles.includes('Inspector_global')){ // 督察员  
614 - res = await dcyUniversalApproval(renewSubmitData)  
615 - }  
616 -  
617 - if( userStore.userInfo.roles.includes('patrol_global')){ // 全域巡查员  
618 - res = await qyUniversalApproval(renewSubmitData)  
619 - }  
620 -  
621 - } else {  
622 -  
623 - if( userStore.userInfo.roles.includes('regional_manager')){ // 大区经理  
624 - res = await daquWorkorderCreate(commonSubmitData)  
625 - }  
626 -  
627 - if( userStore.userInfo.roles.includes('Inspector_global')){ // 督察员  
628 - res = await dcyWorkorderCreate(commonSubmitData)  
629 - }  
630 -  
631 - if( userStore.userInfo.roles.includes('patrol_global')){ // 全域巡查员  
632 - res = await qyWorkorderCreate(commonSubmitData)  
633 - }  
634 -  
635 -  
636 - }  
637 -  
638 - uni.hideLoading()  
639 - uni.showToast({  
640 - title: isRenew.value ? '重新提交成功' : '工单提交成功',  
641 - icon: 'success',  
642 - duration: 1000  
643 - })  
644 -  
645 - setTimeout(() => {  
646 - uni.reLaunch({  
647 - url: '/pages-sub/problem/regional-order-manage/index'  
648 - })  
649 - }, 1000) 523 + };
  524 +
  525 + uni.showLoading({ title: '提交中...', mask: true });
  526 + // 区分:重新提交 / 新增工单
  527 + const submitParams = isRenew.value
  528 + ? {
  529 + workerDataId: renewOrderData.value.id,
  530 + taskKey: renewOrderData.value.taskKey,
  531 + taskId: renewOrderData.value.taskId,
  532 + operateType: nextStepMap[renewOrderData.value.taskKey]?.operateTypeRenew || '',
  533 + agree: 0,
  534 + reason: workOrderForm.problemDesc.trim(),
  535 + ...baseParams
  536 + }
  537 + : baseParams;
  538 +
  539 + // 统一调用接口
  540 + await callApiByRole(submitParams, isRenew.value);
  541 +
  542 + // 提交成功处理
  543 + uni.showToast({ title: isRenew.value ? '重新提交成功' : '工单提交成功', icon: 'success', duration: 1000 });
  544 + setTimeout(toOrderListPage, 1000);
650 } catch (error) { 545 } catch (error) {
651 - uni.hideLoading()  
652 - 546 + uni.hideLoading();
653 if (!Array.isArray(error)) { 547 if (!Array.isArray(error)) {
654 - console.error(isRenew.value ? '工单重新提交失败:' : '工单提交失败:', error)  
655 - uni.showToast({  
656 - title: isRenew.value ? error.msg : error.msg,  
657 - icon: 'none',  
658 - duration: 2000  
659 - }) 548 + console.error(isRenew.value ? '工单重新提交失败:' : '工单提交失败:', error);
  549 + uni.showToast({ title: error.msg || '提交失败,请重试', icon: 'none', duration: 2000 });
660 } 550 }
661 } 551 }
662 } 552 }
@@ -667,6 +557,7 @@ const submitWorkOrder = async () =&gt; { @@ -667,6 +557,7 @@ const submitWorkOrder = async () =&gt; {
667 .page-container { 557 .page-container {
668 min-height: 100vh; 558 min-height: 100vh;
669 padding-bottom: 100rpx; // 给底部按钮留空间 559 padding-bottom: 100rpx; // 给底部按钮留空间
  560 + background: #fafafa;
670 } 561 }
671 562
672 // 工单表单内容容器 563 // 工单表单内容容器
@@ -674,19 +565,18 @@ const submitWorkOrder = async () =&gt; { @@ -674,19 +565,18 @@ const submitWorkOrder = async () =&gt; {
674 background: #fff; 565 background: #fff;
675 } 566 }
676 567
677 -// 派单情况分组样式(核心:增加顶部间距) 568 +// 派单情况分组样式
678 .dispatch-group { 569 .dispatch-group {
679 -  
680 - padding-top: 20rpx; // 分组内顶部内边距,增强视觉区分  
681 -  
682 -}  
683 -  
684 -// 派单情况标题样式  
685 -.dispatch-title {  
686 - font-size: 32rpx;  
687 - font-weight: 600;  
688 - color:$u-primary;  
689 - margin-bottom: 20rpx;  
690 - padding-left: 10rpx; 570 + padding-top: 24rpx;
  571 + margin-top: 8rpx;
  572 + border-top: 1px solid #f5f5f5;
  573 +
  574 + .dispatch-title {
  575 + font-size: 32rpx;
  576 + font-weight: 600;
  577 + color: $u-primary;
  578 + margin-bottom: 20rpx;
  579 + padding-left: 10rpx;
  580 + }
691 } 581 }
692 </style> 582 </style>
693 \ No newline at end of file 583 \ No newline at end of file
pages-sub/problem/regional-order-manage/add-patrol-order.vue
@@ -101,7 +101,7 @@ @@ -101,7 +101,7 @@
101 101
102 <script setup> 102 <script setup>
103 import { ref, reactive } from 'vue' 103 import { ref, reactive } from 'vue'
104 -import { onReady, onShow, onLoad } from '@dcloudio/uni-app'; 104 +import { onReady, onShow, onLoad, onBackPress } from '@dcloudio/uni-app';
105 import { useUploadImgs } from '@/common/utils/useUploadImgs' 105 import { useUploadImgs } from '@/common/utils/useUploadImgs'
106 import { qyUniversalApproval, qyWorkorderCreate } from '@/api/regional-order-manage/regional-order-manage' 106 import { qyUniversalApproval, qyWorkorderCreate } from '@/api/regional-order-manage/regional-order-manage'
107 import { timeFormat } from '@/uni_modules/uview-plus' 107 import { timeFormat } from '@/uni_modules/uview-plus'
@@ -237,6 +237,13 @@ onShow(() =&gt; { @@ -237,6 +237,13 @@ onShow(() =&gt; {
237 // 初始化工单名称列表 237 // 初始化工单名称列表
238 orderNameList.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('work_name')) 238 orderNameList.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('work_name'))
239 }) 239 })
  240 +// 新增页返回直接回列表页
  241 +onBackPress(() => {
  242 + uni.redirectTo({
  243 + url: '/pages-sub/problem/regional-order-manage/index'
  244 + })
  245 + return true;
  246 +})
240 247
241 // ========== 核心方法 ========== 248 // ========== 核心方法 ==========
242 const echoOrderData = (orderItem) => { 249 const echoOrderData = (orderItem) => {
pages-sub/problem/regional-order-manage/distribution-order.vue
@@ -194,7 +194,7 @@ @@ -194,7 +194,7 @@
194 194
195 <script setup> 195 <script setup>
196 import { ref, reactive } from 'vue' 196 import { ref, reactive } from 'vue'
197 -import { onReady, onShow, onLoad } from '@dcloudio/uni-app'; 197 +import { onReady, onShow, onLoad, onBackPress } from '@dcloudio/uni-app';
198 import { useUploadImgs } from '@/common/utils/useUploadImgs' 198 import { useUploadImgs } from '@/common/utils/useUploadImgs'
199 import { getRoadListByLatLng } from '@/api/common' 199 import { getRoadListByLatLng } from '@/api/common'
200 import { daquUniversalApproval, daquWorkorderCreate, qyUniversalApproval } from '@/api/regional-order-manage/regional-order-manage' 200 import { daquUniversalApproval, daquWorkorderCreate, qyUniversalApproval } from '@/api/regional-order-manage/regional-order-manage'
@@ -357,6 +357,14 @@ onShow(() =&gt; { @@ -357,6 +357,14 @@ onShow(() =&gt; {
357 pressingTypeList.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('workorder_pressing_type')) 357 pressingTypeList.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('workorder_pressing_type'))
358 }) 358 })
359 359
  360 +// 新增页返回直接回列表页
  361 +onBackPress(() => {
  362 + uni.redirectTo({
  363 + url: '/pages-sub/problem/regional-order-manage/index'
  364 + })
  365 + return true;
  366 +})
  367 +
360 // ========== 核心方法 ========== 368 // ========== 核心方法 ==========
361 const echoOrderData = (orderItem) => { 369 const echoOrderData = (orderItem) => {
362 // 回显业务线 370 // 回显业务线
@@ -374,7 +382,7 @@ const echoOrderData = (orderItem) =&gt; { @@ -374,7 +382,7 @@ const echoOrderData = (orderItem) =&gt; {
374 workOrderForm.problemDesc = orderItem.remark || ''; 382 workOrderForm.problemDesc = orderItem.remark || '';
375 workOrderForm.lat = orderItem.lat || 0; 383 workOrderForm.lat = orderItem.lat || 0;
376 workOrderForm.lon = orderItem.lon || 0; 384 workOrderForm.lon = orderItem.lon || 0;
377 - workOrderForm.expectedFinishDate = orderItem.expectedFinishDate=='' ? '':timeFormat(orderItem.expectedFinishDate, 'yyyy-mm-dd hh:MM:ss') ; 385 + workOrderForm.expectedFinishDate = orderItem.expectedFinishDate? timeFormat(orderItem.expectedFinishDate, 'yyyy-mm-dd hh:MM:ss'):'' ;
378 386
379 // 回显图片【核心:问题照片赋值】 387 // 回显图片【核心:问题照片赋值】
380 if (orderItem.problemsImgs && Array.isArray(orderItem.problemsImgs) && orderItem.problemsImgs.length > 0) { 388 if (orderItem.problemsImgs && Array.isArray(orderItem.problemsImgs) && orderItem.problemsImgs.length > 0) {
pages-sub/problem/regional-order-manage/index.vue
@@ -3,7 +3,6 @@ @@ -3,7 +3,6 @@
3 <!-- 顶部固定区域 --> 3 <!-- 顶部固定区域 -->
4 <up-sticky> 4 <up-sticky>
5 <view class="header-wrap"> 5 <view class="header-wrap">
6 - <!-- 第一行:u-tabs 待办/已办切换 :scrollable="false"-->  
7 <up-tabs 6 <up-tabs
8 v-model="activeTab" 7 v-model="activeTab"
9 :list="tabList" 8 :list="tabList"
@@ -12,10 +11,8 @@ @@ -12,10 +11,8 @@
12 font-size="30rpx" 11 font-size="30rpx"
13 @click="handleTabChange" 12 @click="handleTabChange"
14 /> 13 />
15 -  
16 <!-- 第二行:下拉框 + 搜索框 --> 14 <!-- 第二行:下拉框 + 搜索框 -->
17 <view class="search-header"> 15 <view class="search-header">
18 - <!-- 左侧下拉框 -->  
19 <view class="select-wrap common-text-color"> 16 <view class="select-wrap common-text-color">
20 <up-select 17 <up-select
21 v-model:current="selectedSortValue" 18 v-model:current="selectedSortValue"
@@ -26,8 +23,6 @@ @@ -26,8 +23,6 @@
26 :style="{ flex: 1 }" 23 :style="{ flex: 1 }"
27 /> 24 />
28 </view> 25 </view>
29 -  
30 - <!-- 右侧搜索框 -->  
31 <view class="search-input-wrap"> 26 <view class="search-input-wrap">
32 <up-search 27 <up-search
33 v-model="searchValue" 28 v-model="searchValue"
@@ -47,24 +42,23 @@ @@ -47,24 +42,23 @@
47 42
48 <!-- 列表容器 --> 43 <!-- 列表容器 -->
49 <z-paging 44 <z-paging
50 - ref="paging" 45 + ref="pagingRef"
51 v-model="orderList" 46 v-model="orderList"
52 @query="queryList" 47 @query="queryList"
53 :auto-show-system-loading="true" 48 :auto-show-system-loading="true"
54 -  
55 > 49 >
56 <template #empty> 50 <template #empty>
57 - <empty-view/> 51 + <empty-view />
58 </template> 52 </template>
59 53
60 <view class="common-card-list" style="padding-top: 200rpx;padding-bottom: 30rpx"> 54 <view class="common-card-list" style="padding-top: 200rpx;padding-bottom: 30rpx">
61 <!-- 待办工单卡片 --> 55 <!-- 待办工单卡片 -->
62 <up-card 56 <up-card
63 - v-if="activeTab == 0" 57 + v-if="activeTab === 0"
64 :border="false" 58 :border="false"
65 :foot-border-top="false" 59 :foot-border-top="false"
66 v-for="(item, index) in orderList" 60 v-for="(item, index) in orderList"
67 - :key="`todo_${item.orderNo}_${index}`" 61 + :key="`todo_${item.id}_${index}`"
68 :show-head="false" 62 :show-head="false"
69 class="order-card" 63 class="order-card"
70 > 64 >
@@ -86,7 +80,7 @@ @@ -86,7 +80,7 @@
86 <view class="u-body-item-title">情况描述:</view> 80 <view class="u-body-item-title">情况描述:</view>
87 <view class="u-line-1 u-body-value">{{ item.remark || '无' }}</view> 81 <view class="u-line-1 u-body-value">{{ item.remark || '无' }}</view>
88 </view> 82 </view>
89 - <view class="u-body-item u-flex common-item-center common-justify-between"> 83 + <view class="u-body-item u-flex common-item-center common-justify-between">
90 <view class="u-body-item-title">紧急程度:</view> 84 <view class="u-body-item-title">紧急程度:</view>
91 <view class="u-line-1 u-body-value"> 85 <view class="u-line-1 u-body-value">
92 {{ uni.$dict.getDictLabel('workorder_pressing_type', item.pressingType) }} 86 {{ uni.$dict.getDictLabel('workorder_pressing_type', item.pressingType) }}
@@ -101,20 +95,10 @@ @@ -101,20 +95,10 @@
101 <view class="u-line-1 u-body-value">{{ timeFormat(item.createTime, 'yyyy-mm-dd hh:MM:ss') }}</view> 95 <view class="u-line-1 u-body-value">{{ timeFormat(item.createTime, 'yyyy-mm-dd hh:MM:ss') }}</view>
102 </view> 96 </view>
103 <!-- 操作按钮行 --> 97 <!-- 操作按钮行 -->
104 -  
105 <view class="u-body-item u-flex common-justify-between common-item-center mt-20"> 98 <view class="u-body-item u-flex common-justify-between common-item-center mt-20">
106 - <up-button type="warning" size="mini" @click="handleReject(item)"  
107 - v-show="nextStepMap[item.taskKey].backShow">回退  
108 - </up-button>  
109 -  
110 - <up-button type="success" size="mini" @click="handleRenew(item)"  
111 - v-show="nextStepMap[item.taskKey].renewShow">重新提交  
112 - </up-button>  
113 -  
114 - <up-button type="primary" size="mini" @click="handleProcess(item)">{{  
115 - nextStepMap[item.taskKey].btnText  
116 - }}  
117 - </up-button> 99 + <up-button type="warning" size="mini" @click="handleReject(item)" v-show="nextStepMap[item.taskKey].backShow">回退</up-button>
  100 + <up-button type="success" size="mini" @click="handleRenew(item)" v-show="nextStepMap[item.taskKey].renewShow">重新提交</up-button>
  101 + <up-button type="primary" size="mini" @click="handleProcess(item)">{{ nextStepMap[item.taskKey].btnText }}</up-button>
118 <up-button type="info" size="mini" @click="handleDetail(item)">详情</up-button> 102 <up-button type="info" size="mini" @click="handleDetail(item)">详情</up-button>
119 </view> 103 </view>
120 </view> 104 </view>
@@ -123,11 +107,11 @@ @@ -123,11 +107,11 @@
123 107
124 <!-- 已办工单卡片和我发起的 --> 108 <!-- 已办工单卡片和我发起的 -->
125 <up-card 109 <up-card
126 - v-if="activeTab == 2||activeTab == 1" 110 + v-if="activeTab === 1 || activeTab === 2"
127 :border="false" 111 :border="false"
128 :foot-border-top="false" 112 :foot-border-top="false"
129 v-for="(item, index) in orderList" 113 v-for="(item, index) in orderList"
130 - :key="`done_${item.orderNo}_${index}`" 114 + :key="`done_${item.id}_${index}`"
131 :show-head="false" 115 :show-head="false"
132 class="order-card" 116 class="order-card"
133 > 117 >
@@ -149,12 +133,11 @@ @@ -149,12 +133,11 @@
149 <view class="u-body-item-title">情况描述:</view> 133 <view class="u-body-item-title">情况描述:</view>
150 <view class="u-line-1 u-body-value">{{ item.remark || '无' }}</view> 134 <view class="u-line-1 u-body-value">{{ item.remark || '无' }}</view>
151 </view> 135 </view>
152 -  
153 <view class="u-body-item u-flex common-justify-between common-item-center"> 136 <view class="u-body-item u-flex common-justify-between common-item-center">
154 <view class="u-body-item-title"> 137 <view class="u-body-item-title">
155 紧急程度:{{ uni.$dict.getDictLabel('workorder_pressing_type', item.pressingType) }} 138 紧急程度:{{ uni.$dict.getDictLabel('workorder_pressing_type', item.pressingType) }}
156 </view> 139 </view>
157 - <view class=" "> 140 + <view>
158 <up-button type="primary" size="mini" @click="handleDetail(item)">工单详情</up-button> 141 <up-button type="primary" size="mini" @click="handleDetail(item)">工单详情</up-button>
159 </view> 142 </view>
160 </view> 143 </view>
@@ -172,21 +155,12 @@ @@ -172,21 +155,12 @@
172 </view> 155 </view>
173 </z-paging> 156 </z-paging>
174 157
175 - <!-- 底部新增工单按钮(仅巡查员显示) -->  
176 - <!-- <view v-if="isInspector" class="fixed-bottom-btn-wrap">-->  
177 - <!-- <up-button type="primary" size="large" @click="handleAddOrder">-->  
178 - <!-- 新增工单-->  
179 - <!-- </up-button>-->  
180 - <!-- </view>-->  
181 - 158 + <!-- 底部新增工单按钮 -->
182 <view class="fixed-bottom-btn-wrap" v-if="isAi"> 159 <view class="fixed-bottom-btn-wrap" v-if="isAi">
183 - <up-button type="primary" size="large" @click="handleAddOrder">  
184 - 新增工单  
185 - </up-button> 160 + <up-button type="primary" size="large" @click="handleAddOrder">新增工单</up-button>
186 </view> 161 </view>
187 162
188 -  
189 - <!-- 回退原因弹窗:替换为up-modal(核心修改) --> 163 + <!-- 回退原因弹窗 -->
190 <up-modal 164 <up-modal
191 :show="rejectModalShow" 165 :show="rejectModalShow"
192 title="回退原因" 166 title="回退原因"
@@ -197,7 +171,6 @@ @@ -197,7 +171,6 @@
197 @confirm="confirmReject" 171 @confirm="confirmReject"
198 > 172 >
199 <view class="reject-modal-content"> 173 <view class="reject-modal-content">
200 - <!-- 回退原因 必填textarea -->  
201 <up-textarea 174 <up-textarea
202 v-model.trim="rejectReason" 175 v-model.trim="rejectReason"
203 placeholder="请输入回退原因(必填)" 176 placeholder="请输入回退原因(必填)"
@@ -206,11 +179,10 @@ @@ -206,11 +179,10 @@
206 maxlength="200" 179 maxlength="200"
207 class="reject-textarea" 180 class="reject-textarea"
208 /> 181 />
209 - <!-- 上传图片(选填)- 按照参考页面改造 -->  
210 <view class="upload-wrap mt-20"> 182 <view class="upload-wrap mt-20">
211 <view class="upload-title">上传图片(选填)</view> 183 <view class="upload-title">上传图片(选填)</view>
212 <up-upload 184 <up-upload
213 - :file-list="rejectImgs.rawImgList.value|| []" 185 + :file-list="rejectImgs.rawImgList.value || []"
214 @after-read="rejectImgs.uploadImgs" 186 @after-read="rejectImgs.uploadImgs"
215 @delete="rejectImgs.deleteImg" 187 @delete="rejectImgs.deleteImg"
216 multiple 188 multiple
@@ -224,7 +196,7 @@ @@ -224,7 +196,7 @@
224 </view> 196 </view>
225 </up-modal> 197 </up-modal>
226 198
227 - <!-- 验收弹窗 up-modal(含图片上传) --> 199 + <!-- 验收弹窗 -->
228 <up-modal 200 <up-modal
229 :show="acceptModalShow" 201 :show="acceptModalShow"
230 title="验收" 202 title="验收"
@@ -235,15 +207,12 @@ @@ -235,15 +207,12 @@
235 @confirm="handleAcceptModalConfirm" 207 @confirm="handleAcceptModalConfirm"
236 > 208 >
237 <view class="accept-modal-content"> 209 <view class="accept-modal-content">
238 - <!-- 第一行:单选框(通过/不通过,默认通过) -->  
239 <view class="radio-group-wrap"> 210 <view class="radio-group-wrap">
240 <up-radio-group v-model="acceptRadioValue"> 211 <up-radio-group v-model="acceptRadioValue">
241 <up-radio name="0" label="通过"></up-radio> 212 <up-radio name="0" label="通过"></up-radio>
242 <up-radio name="1" label="不通过"></up-radio> 213 <up-radio name="1" label="不通过"></up-radio>
243 </up-radio-group> 214 </up-radio-group>
244 </view> 215 </view>
245 -  
246 - <!-- 第二行:必填textarea,最多200字 -->  
247 <view class="textarea-wrap mt-30"> 216 <view class="textarea-wrap mt-30">
248 <up-textarea 217 <up-textarea
249 v-model.trim="acceptReason" 218 v-model.trim="acceptReason"
@@ -254,15 +223,12 @@ @@ -254,15 +223,12 @@
254 count 223 count
255 /> 224 />
256 </view> 225 </view>
257 -  
258 - <!-- 验收图片上传(选填,参考回退弹窗样式) -->  
259 <view class="upload-wrap mt-20"> 226 <view class="upload-wrap mt-20">
260 <view class="upload-title">上传验收图片(选填)</view> 227 <view class="upload-title">上传验收图片(选填)</view>
261 <up-upload 228 <up-upload
262 :file-list="acceptImgs.rawImgList.value || []" 229 :file-list="acceptImgs.rawImgList.value || []"
263 @after-read="acceptImgs.uploadImgs" 230 @after-read="acceptImgs.uploadImgs"
264 @delete="acceptImgs.deleteImg" 231 @delete="acceptImgs.deleteImg"
265 -  
266 multiple 232 multiple
267 width="70" 233 width="70"
268 height="70" 234 height="70"
@@ -284,124 +250,171 @@ import { @@ -284,124 +250,171 @@ import {
284 myBuzSimplePage, 250 myBuzSimplePage,
285 todoBuzSimplePage, 251 todoBuzSimplePage,
286 doneBuzSimplePage, 252 doneBuzSimplePage,
287 - qyWorkorderCreate,  
288 qyUniversalApproval, 253 qyUniversalApproval,
289 daquUniversalApproval, 254 daquUniversalApproval,
290 dcyUniversalApproval 255 dcyUniversalApproval
291 } from '@/api/regional-order-manage/regional-order-manage' 256 } from '@/api/regional-order-manage/regional-order-manage'
292 -// 从用户store获取角色信息  
293 import { useUserStore } from '@/pinia/user'; 257 import { useUserStore } from '@/pinia/user';
294 import { nextStepMap, buzStatusMap } from '@/common/utils/common' 258 import { nextStepMap, buzStatusMap } from '@/common/utils/common'
295 -// 引入图片上传组合式函数(与参考页面一致)  
296 import { useUploadImgs } from '@/common/utils/useUploadImgs' 259 import { useUploadImgs } from '@/common/utils/useUploadImgs'
297 -// ========== 状态管理 ========== 260 +
  261 +// ========== 全局实例 & 常量 ==========
298 const userStore = useUserStore(); 262 const userStore = useUserStore();
299 -// 标签页切换 263 +const USER_ROLES = userStore.userInfo?.roles || [];
  264 +
  265 +// ========== 基础状态 (按模块分组,语义化更强) ==========
  266 +// tab切换
300 const activeTab = ref(0); // 0-待办 1-我发起的 2-已办 267 const activeTab = ref(0); // 0-待办 1-我发起的 2-已办
301 -const tabList = ref([  
302 - {name: '待办'},  
303 - {name: '我发起的任务'},  
304 - {name: '已办'}  
305 -]);  
306 -// 排序下拉框 268 +const tabList = ref([{name: '待办'}, {name: '我发起的任务'}, {name: '已办'}]);
  269 +// 排序与搜索
307 const selectedSortValue = ref(1); 270 const selectedSortValue = ref(1);
308 const sortOptions = ref([ 271 const sortOptions = ref([
309 - {name: '位置', id: 1},  
310 - {name: '名称', id: 2},  
311 - {name: '描述', id: 3},  
312 - {name: '编号', id: 4}, 272 + {name: '位置', id: 1}, {name: '名称', id: 2}, {name: '描述', id: 3}, {name: '编号', id: 4},
313 ]); 273 ]);
314 -// 搜索  
315 const searchValue = ref(''); 274 const searchValue = ref('');
316 -// 分页  
317 -const paging = ref(null); 275 +// 分页相关
  276 +const pagingRef = ref(null);
318 const orderList = ref([]); 277 const orderList = ref([]);
319 -// 角色控制(巡查员显示新增按钮) 278 +
  279 +// ========== 角色权限计算属性 (修复核心bug: 原代码取反逻辑写反) ==========
320 const isAi = computed(() => { 280 const isAi = computed(() => {
321 - // patrol_global 全域巡查员  
322 - // regional_manager 大区经理  
323 - // AI_dispatcher AI工单派发人员  
324 - // 增加可选链,避免用户信息不存在报错  
325 - console.log('123')  
326 - console.log(!userStore.userInfo?.roles?.includes('AI_dispatcher'))  
327 - return !userStore.userInfo?.roles?.includes('AI_dispatcher') ;  
328 - // return true 281 + // AI工单派发人员 不显示新增按钮,其他角色显示
  282 + return !USER_ROLES.includes('AI_dispatcher');
329 }); 283 });
330 -// 回退弹窗相关  
331 -const rejectModalShow = ref(false); // 回退modal显示开关  
332 -const rejectReason = ref(''); // 回退原因  
333 -const currentRejectItem = ref(null); // 当前回退工单  
334 -// 回退图片上传配置(与参考页面风格一致) 284 +
  285 +// ========== 回退弹窗相关 ==========
  286 +const rejectModalShow = ref(false);
  287 +const rejectReason = ref('');
  288 +const currentRejectItem = ref(null);
335 const rejectImgs = useUploadImgs({ 289 const rejectImgs = useUploadImgs({
336 - maxCount: 3, // 最多上传3张  
337 - uploadText: '选择回退图片', // 自定义上传提示文字  
338 - sizeType: ['compressed'], // 仅上传压缩图  
339 - formRef: null, // 该弹窗无表单校验  
340 - fieldName: 'rejectImgs' // 自定义字段名 290 + maxCount: 3, uploadText: '选择回退图片', sizeType: ['compressed'], formRef: null, fieldName: 'rejectImgs'
341 }) 291 })
342 -// ========== 验收弹窗相关状态(含图片上传) ==========  
343 -const acceptModalShow = ref(false); // 验收弹窗显示开关  
344 -const acceptRadioValue = ref('0'); // 单选框值,默认0(通过)  
345 -const acceptReason = ref(''); // 验收原因  
346 -const currentAcceptItem = ref(null); // 当前验收的工单项  
347 -// 验收图片上传配置(独立实例,参考回退弹窗) 292 +
  293 +// ========== 验收弹窗相关 ==========
  294 +const acceptModalShow = ref(false);
  295 +const acceptRadioValue = ref('0'); // 默认通过
  296 +const acceptReason = ref('');
  297 +const currentAcceptItem = ref(null);
348 const acceptImgs = useUploadImgs({ 298 const acceptImgs = useUploadImgs({
349 - maxCount: 3, // 最多上传3张,与回退弹窗一致  
350 - uploadText: '选择验收图片', // 自定义上传提示文字  
351 - sizeType: ['compressed'], // 仅上传压缩图,优化性能  
352 - formRef: null, // 验收弹窗无表单校验  
353 - fieldName: 'acceptImgs' // 自定义字段名,区分回退图片 299 + maxCount: 3, uploadText: '选择验收图片', sizeType: ['compressed'], formRef: null, fieldName: 'acceptImgs'
354 }) 300 })
  301 +
  302 +// ========== 公共封装方法 (核心优化:消灭重复代码) ==========
  303 +/**
  304 + * 生成统一的临时存储key
  305 + * @param {String} prefix 前缀标识
  306 + * @returns {String} 唯一key
  307 + */
  308 +const generateTempKey = (prefix = 'order') => {
  309 + return `${prefix}_${Date.now()}_${Math.floor(Math.random() * 10000)}`;
  310 +};
  311 +
  312 +/**
  313 + * 存储工单数据到本地缓存
  314 + * @param {Object} item 工单数据
  315 + * @param {String} prefix key前缀
  316 + * @returns {String|null} 成功返回key,失败返回null
  317 + */
  318 +const setOrderStorage = (item, prefix) => {
  319 + if (!item?.id) return null;
  320 + const tempKey = generateTempKey(prefix);
  321 + try {
  322 + uni.setStorageSync(tempKey, item);
  323 + return tempKey;
  324 + } catch (error) {
  325 + console.error('存储工单数据失败:', error);
  326 + uni.showToast({title: '数据存储异常,请重试', icon: 'none'});
  327 + return null;
  328 + }
  329 +};
  330 +
  331 +/**
  332 + * 获取分页请求公共参数
  333 + * @param {Number} pageNo 页码
  334 + * @param {Number} pageSize 页大小
  335 + * @returns {Object} 请求参数
  336 + */
  337 +const getQueryParams = (pageNo, pageSize) => {
  338 + return {
  339 + searchContent: searchValue.value.trim() || '',
  340 + pageNo,
  341 + pageSize,
  342 + type: selectedSortValue.value
  343 + };
  344 +};
  345 +
  346 +/**
  347 + * 统一调用审批接口
  348 + * @param {Object} params 请求参数
  349 + * @param {String} taskKey 工单任务key
  350 + * @returns {Promise} 接口请求Promise
  351 + */
  352 +const callApprovalApi = async (params, taskKey) => {
  353 + if (taskKey === 'shRegionManager') return await dcyUniversalApproval(params);
  354 + if (taskKey === 'regionManager') return await qyUniversalApproval(params);
  355 + // 根据角色匹配对应接口
  356 + if (USER_ROLES.includes('regional_manager')) return await daquUniversalApproval(params);
  357 + if (USER_ROLES.includes('Inspector_global')) return await dcyUniversalApproval(params);
  358 + if (USER_ROLES.includes('patrol_global')) return await qyUniversalApproval(params);
  359 +};
  360 +
  361 +/**
  362 + * 统一跳转工单页面
  363 + * @param {String} path 页面路径
  364 + * @param {Object} query 拼接参数
  365 + */
  366 +const navToOrderPage = (path, query = {}) => {
  367 + const queryStr = Object.keys(query).map(k => `${k}=${query[k]}`).join('&');
  368 + uni.redirectTo({ url: `${path}${queryStr ? '?' + queryStr : ''}` });
  369 +};
  370 +
  371 +/**
  372 + * 刷新列表 - 统一封装,避免空指针
  373 + */
  374 +const refreshOrderList = () => {
  375 + pagingRef.value && pagingRef.value.reload();
  376 +};
  377 +
  378 +// ========== 业务核心方法 ==========
355 // 分页查询列表 379 // 分页查询列表
356 const queryList = async (pageNo, pageSize) => { 380 const queryList = async (pageNo, pageSize) => {
357 try { 381 try {
358 - const apiParams = {  
359 - searchContent: searchValue.value.trim() || '',  
360 - pageNo,  
361 - pageSize,  
362 - type: selectedSortValue.value // 1-位置 2-工单名称 3-情况描述 4-工单编号  
363 - }; 382 + const params = getQueryParams(pageNo, pageSize);
364 let res; 383 let res;
365 - if (activeTab.value == 0) {  
366 - // 待办工单  
367 - res = await todoBuzSimplePage(apiParams);  
368 - } else if (activeTab.value == 1) {  
369 - // 我发起的任务  
370 - res = await myBuzSimplePage(apiParams);  
371 - } else {  
372 - // 已办工单  
373 - res = await doneBuzSimplePage(apiParams);  
374 - }  
375 - // 适配z-paging分页  
376 - paging.value.complete(res.list, res.total); 384 + if (activeTab.value === 0) res = await todoBuzSimplePage(params);
  385 + else if (activeTab.value === 1) res = await myBuzSimplePage(params);
  386 + else res = await doneBuzSimplePage(params);
  387 + pagingRef.value.complete(res?.list || [], res?.total || 0);
377 } catch (error) { 388 } catch (error) {
378 console.error('加载工单失败:', error); 389 console.error('加载工单失败:', error);
379 - paging.value?.complete(false); 390 + pagingRef.value?.complete(false);
380 uni.showToast({title: '加载失败,请重试', icon: 'none'}); 391 uni.showToast({title: '加载失败,请重试', icon: 'none'});
381 } 392 }
382 }; 393 };
383 -// ========== 事件处理 ========== 394 +
384 // 标签页切换 395 // 标签页切换
385 const handleTabChange = (item) => { 396 const handleTabChange = (item) => {
386 - orderList.value = [];  
387 - console.log(item)  
388 activeTab.value = item.index; 397 activeTab.value = item.index;
389 - paging.value?.reload(); // 切换标签页刷新列表 398 + orderList.value = [];
  399 + refreshOrderList();
390 }; 400 };
  401 +
391 // 排序变更 402 // 排序变更
392 const handleSortChange = (val) => { 403 const handleSortChange = (val) => {
393 selectedSortValue.value = val.id; 404 selectedSortValue.value = val.id;
394 searchValue.value = ''; 405 searchValue.value = '';
395 - paging.value?.reload(); // 排序变更刷新列表 406 + refreshOrderList();
396 }; 407 };
  408 +
397 // 搜索 409 // 搜索
398 const handleSearch = (val) => { 410 const handleSearch = (val) => {
399 searchValue.value = val; 411 searchValue.value = val;
400 - paging.value?.reload(); // 搜索刷新列表 412 + refreshOrderList();
401 }; 413 };
  414 +
402 // 工单详情 415 // 工单详情
403 const handleDetail = (item) => { 416 const handleDetail = (item) => {
404 - // 0-待办 1我发起的- 2-已办 417 + if (!item?.taskId) return uni.showToast({title: '工单信息异常', icon: 'none'});
405 uni.navigateTo({ 418 uni.navigateTo({
406 url: `/pages-sub/problem/regional-order-manage/order-detail?taskId=${item.taskId}&activeTab=${activeTab.value}&processInstanceId=${item.processInstanceId}`, 419 url: `/pages-sub/problem/regional-order-manage/order-detail?taskId=${item.taskId}&activeTab=${activeTab.value}&processInstanceId=${item.processInstanceId}`,
407 events: { 420 events: {
@@ -414,302 +427,163 @@ const handleDetail = (item) =&gt; { @@ -414,302 +427,163 @@ const handleDetail = (item) =&gt; {
414 } 427 }
415 } 428 }
416 }); 429 });
  430 + // navToOrderPage('/pages-sub/problem/regional-order-manage/order-detail', {
  431 + // taskId: item.taskId,
  432 + // activeTab: activeTab.value,
  433 + // processInstanceId: item.processInstanceId
  434 + // }, {
  435 + // needRefresh: () => refreshOrderList()
  436 + // });
  437 +
  438 +
417 }; 439 };
418 -// 生成临时key  
419 -const generateTempKey = () => {  
420 - return 'renew_order_' + Date.now() + '_' + Math.floor(Math.random() * 10000);  
421 -};  
422 -// 待办-重新提交工单(改造后:大数据存本地,仅传唯一标识) 440 +
  441 +// 重新提交工单
423 const handleRenew = (item) => { 442 const handleRenew = (item) => {
  443 + const tempKey = setOrderStorage(item, 'renew_order');
  444 + if (!tempKey) return;
424 445
425 - // 1. 生成唯一临时标识  
426 - const tempKey = generateTempKey();  
427 - // 2. 将完整工单数据存入本地临时存储(同步存储,确保数据立即生效)  
428 - try {  
429 - console.log(item)  
430 - console.log('123')  
431 - uni.setStorageSync(tempKey, item);  
432 - } catch (error) {  
433 - console.error('存储工单数据失败:', error);  
434 - uni.showToast({title: '数据存储异常,无法重新提交', icon: 'none'});  
435 - return;  
436 - } 446 + const pageUrl = USER_ROLES.includes('patrol_global')
  447 + ? '/pages-sub/problem/regional-order-manage/add-patrol-order'
  448 + : '/pages-sub/problem/regional-order-manage/add-order';
437 449
438 - if (userStore.userInfo?.roles.includes('patrol_global')) { // 全域巡查员  
439 - uni.navigateTo({  
440 - url: `/pages-sub/problem/regional-order-manage/add-patrol-order?isRenew=1&tempKey=${tempKey}`  
441 - });  
442 - }  
443 - if (userStore.userInfo?.roles.includes('regional_manager')) { // 大区经理  
444 - uni.navigateTo({  
445 - url: `/pages-sub/problem/regional-order-manage/add-order?isRenew=1&tempKey=${tempKey}`  
446 - });  
447 - }  
448 - if (userStore.userInfo?.roles.includes('Inspector_global')) { // 督察员  
449 - uni.navigateTo({  
450 - url: `/pages-sub/problem/regional-order-manage/add-order?isRenew=1&tempKey=${tempKey}`  
451 - });  
452 - }  
453 - // // 3. URL 仅传递「唯一标识」和「重新提交标记」(数据量极小,无长度问题)  
454 - // uni.navigateTo({  
455 - // url: `/pages-sub/problem/regional-order-manage/add-patrol-order?isRenew=1&tempKey=${tempKey}`  
456 - // }); 450 + navToOrderPage(pageUrl, { isRenew: 1, tempKey });
457 }; 451 };
458 -// 待办-处理工单 452 +
  453 +// 处理工单核心逻辑
459 const handleProcess = async (item) => { 454 const handleProcess = async (item) => {
460 - console.log(nextStepMap[item.taskKey].name) 455 + if (!item) return uni.showToast({title: '工单信息异常', icon: 'none'});
  456 + const stepName = nextStepMap[item.taskKey]?.name;
  457 + let resData
461 try { 458 try {
462 - if (nextStepMap[item.taskKey]?.name == '大区经理分配') {  
463 - // ① 生成唯一临时key(统一规则,避免冲突)  
464 - const tempKey = `distribute_order_${Date.now()}_${Math.floor(Math.random() * 10000)}`;  
465 - // ② 存储完整item到本地缓存(同步存储,确保立即生效)  
466 - try {  
467 - uni.setStorageSync(tempKey, item);  
468 - } catch (error) {  
469 - console.error('存储分配工单数据失败:', error);  
470 - uni.showToast({title: '数据存储异常,无法跳转', icon: 'none'});  
471 - return;  
472 - }  
473 - // ③ URL仅传递临时key,无其他冗余参数  
474 - uni.navigateTo({  
475 - url: `/pages-sub/problem/regional-order-manage/distribution-order?tempKey=${tempKey}`  
476 - }) 459 + // 大区经理分配
  460 + if (stepName === '大区经理分配') {
  461 + const tempKey = setOrderStorage(item, 'distribute_order');
  462 + tempKey && navToOrderPage('/pages-sub/problem/regional-order-manage/distribution-order', { tempKey });
477 } 463 }
478 - if (nextStepMap[item.taskKey]?.name == '督察员单子大区经理分配') {  
479 - let postData = {  
480 - "taskKey":item.taskKey,  
481 - "taskId": item.taskId,  
482 - "operateType":60,  
483 - "workerDataId":item.id,  
484 - "agree":0,  
485 - "reason":item.remark,  
486 - "roadId":item.roadId,  
487 - "roadName":item.roadName,  
488 - "pressingType":item.pressingType,  
489 - "orderName":item.orderName,  
490 - "expectedFinishDate": item.expectedFinishDate,  
491 - "busiLine":item.busiLine,  
492 - }  
493 - const res = await dcyUniversalApproval(postData);  
494 - uni.showToast({title: '分配成功', icon: 'success', duration: 1000});  
495 - paging.value?.reload(); // 刷新列表 464 + // 督察员单子大区经理分配
  465 + else if (stepName === '督察员单子大区经理分配') {
  466 + const postData = { taskKey: item.taskKey, taskId: item.taskId, operateType:60, workerDataId:item.id, agree:0, reason:item.remark, roadId:item.roadId, roadName:item.roadName, pressingType:item.pressingType, orderName:item.orderName, expectedFinishDate: item.expectedFinishDate, busiLine:item.busiLine };
  467 + resData = await dcyUniversalApproval(postData);
  468 + uni.showToast({title: '分配成功', icon: 'success'});
  469 + refreshOrderList();
496 } 470 }
497 -  
498 -  
499 - // 验收 - 打开弹窗  
500 - if (nextStepMap[item.taskKey]?.name == '巡查员验收' || nextStepMap[item.taskKey]?.name == '养护组长验收') {  
501 - currentAcceptItem.value = item; // 存储当前工单信息  
502 - acceptReason.value = ''; // 清空上次的验收原因  
503 - acceptRadioValue.value = '0'; // 重置默认选中“通过”  
504 - acceptModalShow.value = true; // 显示验收弹窗 471 + // 验收弹窗
  472 + else if (['巡查员验收', '养护组长验收'].includes(stepName)) {
  473 + currentAcceptItem.value = item;
  474 + acceptReason.value = '';
  475 + acceptRadioValue.value = '0';
  476 + acceptModalShow.value = true;
505 } 477 }
506 -  
507 - // 发起人确认  
508 - if (nextStepMap[item.taskKey]?.name == '发起人确认') {  
509 - console.log(item) 478 + // 发起人确认-结束工单
  479 + else if (stepName === '发起人确认') {
510 uni.showModal({ 480 uni.showModal({
511 title: "结束工单", 481 title: "结束工单",
512 content: "请确定是否结束工单?", 482 content: "请确定是否结束工单?",
513 - success: async function (res) { 483 + success: async (res) => {
514 if (res.confirm) { 484 if (res.confirm) {
515 - // 构建请求参数  
516 const requestData = { 485 const requestData = {
517 -  
518 - "workerDataId": item.id,  
519 - "taskKey": 'ylInspectorStart',  
520 - "taskId": item.taskId,  
521 - "operateType": 200,  
522 - "agree": 1,  
523 - "reason": '结束工单' 486 + workerDataId: item.id, taskKey: 'ylInspectorStart', taskId: item.taskId,
  487 + operateType: 200, agree: 1, reason: '结束工单'
524 }; 488 };
525 - // 调用回退工单接口 daquUniversalApproval  
526 -  
527 - if( userStore.userInfo.roles.includes('regional_manager')){ // 大区经理  
528 - await daquUniversalApproval(requestData)  
529 - }  
530 - if( userStore.userInfo.roles.includes('Inspector_global')){ // 督察员  
531 - await dcyUniversalApproval(requestData);  
532 - }  
533 - if( userStore.userInfo.roles.includes('patrol_global')){ // 全域巡查员  
534 - await qyUniversalApproval(requestData);  
535 - }  
536 -  
537 - uni.showToast({title: '结束成功', icon: 'success', duration: 1000});  
538 - rejectModalShow.value = false;  
539 - paging.value?.reload(); // 刷新列表  
540 - } else if (res.cancel) {  
541 - console.log("用户点击取消"); 489 + await callApprovalApi(requestData, item.taskKey);
  490 + uni.showToast({title: '结束成功', icon: 'success'});
  491 + refreshOrderList();
542 } 492 }
543 - }, 493 + }
544 }); 494 });
545 } 495 }
546 } catch (error) { 496 } catch (error) {
547 console.error('处理工单失败:', error); 497 console.error('处理工单失败:', error);
548 - uni.showToast({title: '处理失败,请重试', icon: 'none'}); 498 + uni.showToast({title: resData.msg, icon: 'none'});
549 } 499 }
550 }; 500 };
551 -// 待办-回退工单(打开回退modal) 501 +
  502 +// 回退工单-打开弹窗
552 const handleReject = (item) => { 503 const handleReject = (item) => {
553 - console.log('123213')  
554 - // 校验工单有效性  
555 - if (!item || !item.id) {  
556 - uni.showToast({title: '工单信息异常,无法回退', icon: 'none'});  
557 - return;  
558 - } 504 + if (!item?.id) return uni.showToast({title: '工单信息异常,无法回退', icon: 'none'});
559 currentRejectItem.value = item; 505 currentRejectItem.value = item;
560 - rejectReason.value = ''; // 清空上次输入  
561 - rejectImgs.clearImgs(); // 改造后:使用组合式函数的清空方法  
562 - rejectModalShow.value = true; // 显示回退modal 506 + rejectReason.value = '';
  507 + rejectImgs.clearImgs();
  508 + rejectModalShow.value = true;
563 }; 509 };
564 -// 回退modal - 取消按钮 510 +
  511 +// 关闭回退弹窗-重置状态
565 const handleRejectModalCancel = () => { 512 const handleRejectModalCancel = () => {
566 rejectModalShow.value = false; 513 rejectModalShow.value = false;
567 rejectReason.value = ''; 514 rejectReason.value = '';
568 - rejectImgs.clearImgs(); // 改造后:使用组合式函数的清空方法 515 + rejectImgs.clearImgs();
569 }; 516 };
  517 +
570 // 确认回退工单 518 // 确认回退工单
571 const confirmReject = async () => { 519 const confirmReject = async () => {
572 - // 严格校验回退原因(去除首尾空格)  
573 - const rejectReasonTrim = rejectReason.value.trim();  
574 - if (!rejectReasonTrim) {  
575 - uni.showToast({title: '请填写回退原因', icon: 'none', duration: 1000});  
576 - return;  
577 - }  
578 - // 校验当前工单有效性  
579 - if (!currentRejectItem.value || !currentRejectItem.value.id) {  
580 - uni.showToast({title: '工单信息异常,无法提交', icon: 'none', duration: 1000});  
581 - rejectModalShow.value = false;  
582 - return;  
583 - } 520 + const reason = rejectReason.value.trim();
  521 + if (!reason) return uni.showToast({title: '请填写回退原因', icon: 'none'});
  522 + const item = currentRejectItem.value;
  523 + if (!item?.id) return uni.showToast({title: '工单信息异常', icon: 'none'});
  524 +
  525 + uni.showLoading({title: '提交中...', mask: true});
584 try { 526 try {
585 - // 显示加载中,防止重复提交  
586 - uni.showLoading({title: '提交中...', mask: true});  
587 - // 构建请求参数  
588 const requestData = { 527 const requestData = {
589 - "returnImgs": rejectImgs.getSuccessImgUrls(), // 改造后:获取上传成功的图片URL数组  
590 - "workerDataId": currentRejectItem.value.id,  
591 - "taskKey": currentRejectItem.value.taskKey,  
592 - "taskId": currentRejectItem.value.taskId,  
593 -  
594 - "operateType": nextStepMap[currentRejectItem.value.taskKey].operateTypeNoPass,  
595 - "agree": 1,  
596 - "reason": rejectReasonTrim 528 + returnImgs: rejectImgs.getSuccessImgUrls(), workerDataId: item.id, taskKey: item.taskKey,
  529 + taskId: item.taskId, operateType: nextStepMap[item.taskKey].operateTypeNoPass, agree:1, reason
597 }; 530 };
598 - // 调用回退工单接口  
599 - if(currentRejectItem.value.taskKey=='shRegionManager'){ // 对督察员单子 回退  
600 - const res = await dcyUniversalApproval(requestData);  
601 - }  
602 - if(currentRejectItem.value.taskKey=='regionManager'){ // 对全域巡查员单子 回退  
603 - const res = await qyUniversalApproval(requestData);  
604 - }  
605 - // 对大区经理单子 回退  
606 - uni.showToast({title: '回退成功', icon: 'success', duration: 1000});  
607 - rejectModalShow.value = false;  
608 - paging.value?.reload(); // 刷新列表 531 + await callApprovalApi(requestData, item.taskKey);
  532 + uni.showToast({title: '回退成功', icon: 'success'});
  533 + handleRejectModalCancel();
  534 + refreshOrderList();
609 } catch (error) { 535 } catch (error) {
610 console.error('回退工单失败:', error); 536 console.error('回退工单失败:', error);
611 - uni.showToast({title: '网络异常,回退失败', icon: 'none', duration: 1000}); 537 + uni.showToast({title: '回退失败,请重试', icon: 'none'});
612 } finally { 538 } finally {
613 - // 隐藏加载中  
614 uni.hideLoading(); 539 uni.hideLoading();
615 } 540 }
616 }; 541 };
  542 +
617 // 新增工单 543 // 新增工单
618 const handleAddOrder = () => { 544 const handleAddOrder = () => {
619 - if (userStore.userInfo?.roles.includes('patrol_global')) { // 全域巡查员  
620 - uni.navigateTo({  
621 - url: '/pages-sub/problem/regional-order-manage/add-patrol-order'  
622 - });  
623 - }  
624 - if (userStore.userInfo?.roles.includes('regional_manager')) { // 大区经理  
625 - uni.navigateTo({  
626 - url: '/pages-sub/problem/regional-order-manage/add-order'  
627 - });  
628 - }  
629 - if (userStore.userInfo?.roles.includes('Inspector_global')) { // 督察员  
630 - uni.navigateTo({  
631 - url: '/pages-sub/problem/regional-order-manage/add-order'  
632 - });  
633 - } 545 + const pageUrl = USER_ROLES.includes('patrol_global')
  546 + ? '/pages-sub/problem/regional-order-manage/add-patrol-order'
  547 + : '/pages-sub/problem/regional-order-manage/add-order';
  548 + navToOrderPage(pageUrl);
634 }; 549 };
635 -// 验收弹窗 - 取消按钮(清空状态) 550 +
  551 +// 关闭验收弹窗-重置状态
636 const handleAcceptModalCancel = () => { 552 const handleAcceptModalCancel = () => {
637 acceptModalShow.value = false; 553 acceptModalShow.value = false;
638 - acceptReason.value = ''; // 清空验收原因  
639 - acceptRadioValue.value = '0'; // 重置单选框为“通过”  
640 - acceptImgs.clearImgs(); // 清空验收图片 554 + acceptReason.value = '';
  555 + acceptRadioValue.value = '0';
  556 + acceptImgs.clearImgs();
641 }; 557 };
642 -// 验收弹窗 - 确定按钮(含returnImgs传参) 558 +
  559 +// 验收提交
643 const handleAcceptModalConfirm = async () => { 560 const handleAcceptModalConfirm = async () => {
644 - // 1. 校验验收原因是否为空  
645 - if (!acceptReason.value.trim()) {  
646 - uni.showToast({title: '请填写验收原因', icon: 'none', duration: 1000});  
647 - return;  
648 - }  
649 - // 2. 校验验收原因长度  
650 - if (acceptReason.value.length > 200) {  
651 - uni.showToast({title: '验收原因最多200字', icon: 'none', duration: 1000});  
652 - return;  
653 - }  
654 - try {  
655 - // 3. 构建请求参数(含returnImgs)  
656 - console.log(currentAcceptItem.value)  
657 - console.log( userStore.userInfo.roles)  
658 - console.log( userStore.userInfo.roles.includes('Inspector_global'))  
659 - //  
660 - if( userStore.userInfo.roles.includes('regional_manager')){ // 大区经理验收  
661 - let postData = {  
662 - "returnImgs": acceptImgs.getSuccessImgUrls(), // 验收图片URL数组  
663 - "taskKey": currentAcceptItem.value.taskKey,  
664 - "workerDataId": currentAcceptItem.value.id,  
665 - "taskId": currentAcceptItem.value.taskId,  
666 - "operateType": acceptRadioValue.value == 0 ? nextStepMap[currentAcceptItem.value.taskKey].operateTypePass : nextStepMap[currentAcceptItem.value.taskKey].operateTypeNoPass,  
667 - "reason": acceptReason.value.trim(),  
668 - "agree":acceptRadioValue.value,  
669 - }  
670 - await daquUniversalApproval(postData);  
671 - }  
672 - //  
673 - if( userStore.userInfo.roles.includes('patrol_global')){ // 全域巡查员验收  
674 - let postData = {  
675 - "returnImgs": acceptImgs.getSuccessImgUrls(), // 验收图片URL数组  
676 - "taskKey": currentAcceptItem.value.taskKey,  
677 - "taskId": currentAcceptItem.value.taskId,  
678 - "workerDataId": currentAcceptItem.value.id,  
679 - "operateType": acceptRadioValue.value == 0 ? nextStepMap[currentAcceptItem.value.taskKey].operateTypePass : nextStepMap[currentAcceptItem.value.taskKey].operateTypeNoPass,  
680 - "reason": acceptReason.value.trim(),  
681 - "agree": acceptRadioValue.value  
682 - }  
683 - await qyUniversalApproval(postData);  
684 - }  
685 - //  
686 - if( userStore.userInfo.roles.includes('Inspector_global')){ // 督察员验收  
687 - let postData = {  
688 - "returnImgs": acceptImgs.getSuccessImgUrls(), // 验收图片URL数组  
689 - "taskKey": currentAcceptItem.value.taskKey,  
690 - "taskId": currentAcceptItem.value.taskId,  
691 - "workerDataId": currentAcceptItem.value.id,  
692 - "operateType": acceptRadioValue.value == 0 ? nextStepMap[currentAcceptItem.value.taskKey].operateTypePass : nextStepMap[currentAcceptItem.value.taskKey].operateTypeNoPass,  
693 - "reason": acceptReason.value.trim(),  
694 - "agree": acceptRadioValue.value  
695 - }  
696 - await dcyUniversalApproval(postData);  
697 - } 561 + const reason = acceptReason.value.trim();
  562 + if (!reason) return uni.showToast({title: '请填写验收原因', icon: 'none'});
  563 + if (reason.length > 200) return uni.showToast({title: '验收原因最多200字', icon: 'none'});
  564 +
  565 + const item = currentAcceptItem.value;
  566 + if (!item?.id) return uni.showToast({title: '工单信息异常', icon: 'none'});
698 567
699 - // 4. 操作成功处理  
700 - uni.showToast({title: '提交成功', icon: 'success', duration: 1000});  
701 - handleAcceptModalCancel(); // 清空状态  
702 - paging.value?.reload(); // 刷新工单列表 568 + try {
  569 + const postData = {
  570 + returnImgs: acceptImgs.getSuccessImgUrls(), taskKey: item.taskKey, workerDataId: item.id,
  571 + taskId: item.taskId, agree: acceptRadioValue.value, reason,
  572 + operateType: acceptRadioValue.value === '0' ? nextStepMap[item.taskKey].operateTypePass : nextStepMap[item.taskKey].operateTypeNoPass
  573 + };
  574 + await callApprovalApi(postData, item.taskKey);
  575 + uni.showToast({title: '提交成功', icon: 'success'});
  576 + handleAcceptModalCancel();
  577 + refreshOrderList();
703 } catch (error) { 578 } catch (error) {
704 - // 5. 操作失败处理  
705 console.error('验收失败:', error); 579 console.error('验收失败:', error);
706 - uni.showToast({title: '验收提交失败,请重试', icon: 'none', duration: 1000}); 580 + uni.showToast({title: '验收提交失败,请重试', icon: 'none'});
707 } 581 }
708 }; 582 };
709 -// 页面初始化 583 +
  584 +// ========== 页面生命周期 ==========
710 onLoad(() => { 585 onLoad(() => {
711 - // 初始化加载列表  
712 - paging.value?.reload(); 586 + refreshOrderList();
713 }); 587 });
714 </script> 588 </script>
715 589
@@ -734,15 +608,10 @@ onLoad(() =&gt; { @@ -734,15 +608,10 @@ onLoad(() =&gt; {
734 .select-wrap { 608 .select-wrap {
735 width: 120rpx; 609 width: 120rpx;
736 margin-right: 20rpx; 610 margin-right: 20rpx;
737 -  
738 - :deep(.u-select) { 611 + :deep(.u-select), :deep(.u-input__placeholder) {
739 width: 100%; 612 width: 100%;
740 font-size: 28rpx; 613 font-size: 28rpx;
741 } 614 }
742 -  
743 - :deep(.u-input__placeholder) {  
744 - font-size: 28rpx;  
745 - }  
746 } 615 }
747 616
748 .search-input-wrap { 617 .search-input-wrap {
@@ -758,31 +627,14 @@ onLoad(() =&gt; { @@ -758,31 +627,14 @@ onLoad(() =&gt; {
758 box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04); 627 box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
759 } 628 }
760 629
761 -.card-body {  
762 -  
763 -}  
764 -  
765 -// 回退modal样式  
766 -.reject-modal-content { 630 +// 弹窗公共样式
  631 +.reject-modal-content, .accept-modal-content {
767 width: 100%; 632 width: 100%;
768 box-sizing: border-box; 633 box-sizing: border-box;
769 padding: 10rpx 0; 634 padding: 10rpx 0;
770 } 635 }
771 636
772 -.textarea-label {  
773 - font-size: 28rpx;  
774 - color: #333;  
775 - margin-bottom: 10rpx;  
776 -  
777 - .required-mark {  
778 - color: #f56c6c;  
779 - margin-left: 4rpx;  
780 - }  
781 -}  
782 -  
783 .upload-wrap { 637 .upload-wrap {
784 - margin-top: 20rpx;  
785 -  
786 .upload-title { 638 .upload-title {
787 font-size: 28rpx; 639 font-size: 28rpx;
788 color: #333; 640 color: #333;
@@ -790,37 +642,20 @@ onLoad(() =&gt; { @@ -790,37 +642,20 @@ onLoad(() =&gt; {
790 } 642 }
791 } 643 }
792 644
793 -.mt-20 {  
794 - margin-top: 20rpx;  
795 -}  
796 -  
797 -.mt-30 {  
798 - margin-top: 30rpx;  
799 -}  
800 -  
801 -// 养护组长验收弹窗样式  
802 -.accept-modal-content {  
803 - width: 100%;  
804 - box-sizing: border-box;  
805 -}  
806 - 645 +// 验收弹窗单选框样式
807 .radio-group-wrap { 646 .radio-group-wrap {
808 display: flex; 647 display: flex;
809 align-items: center; 648 align-items: center;
810 - gap: 40rpx; // 单选框之间的间距 649 + gap: 40rpx;
811 font-size: 28rpx; 650 font-size: 28rpx;
812 margin-bottom: 20rpx; 651 margin-bottom: 20rpx;
813 } 652 }
814 653
815 .textarea-wrap { 654 .textarea-wrap {
816 width: 100%; 655 width: 100%;
817 - margin-top: 30rpx;  
818 } 656 }
819 657
820 -.modal-btn-wrap {  
821 - display: flex;  
822 - align-items: center;  
823 - justify-content: flex-end;  
824 - padding-right: 10rpx;  
825 -} 658 +// 间距公共样式
  659 +.mt-20 { margin-top: 20rpx; }
  660 +.mt-30 { margin-top: 30rpx; }
826 </style> 661 </style>
827 \ No newline at end of file 662 \ No newline at end of file
pages-sub/problem/regional-order-manage/order-detail.vue
@@ -267,23 +267,23 @@ @@ -267,23 +267,23 @@
267 </view> 267 </view>
268 268
269 <!-- activeTab==0的时候才出现, 也就是待办 --> 269 <!-- activeTab==0的时候才出现, 也就是待办 -->
270 - <view v-if="activeTab==0&&nextStepMap[orderDetail.taskKey]" class="fixed-bottom-btn-wrap">  
271 - <view class="u-body-item u-flex common-justify-between common-item-center ">  
272 - <up-button type="warning" size="normal" @click="handleReject(orderDetail)"  
273 - v-show="nextStepMap[orderDetail.taskKey].backShow">回退  
274 - </up-button>  
275 -  
276 - <up-button type="success" size="normal" @click="handleRenew(orderDetail)"  
277 - v-show="nextStepMap[orderDetail.taskKey].renewShow">重新提交  
278 - </up-button>  
279 -  
280 - <up-button type="primary" size="normal" @click="handleProcess(orderDetail)">{{  
281 - nextStepMap[orderDetail.taskKey].btnText  
282 - }}  
283 - </up-button>  
284 -  
285 - </view>  
286 - </view> 270 +<!-- <view v-if="activeTab==0&&nextStepMap[orderDetail.taskKey]" class="fixed-bottom-btn-wrap">-->
  271 +<!-- <view class="u-body-item u-flex common-justify-between common-item-center ">-->
  272 +<!-- <up-button type="warning" size="normal" @click="handleReject(orderDetail)"-->
  273 +<!-- v-show="nextStepMap[orderDetail.taskKey].backShow">回退-->
  274 +<!-- </up-button>-->
  275 +
  276 +<!-- <up-button type="success" size="normal" @click="handleRenew(orderDetail)"-->
  277 +<!-- v-show="nextStepMap[orderDetail.taskKey].renewShow">重新提交-->
  278 +<!-- </up-button>-->
  279 +
  280 +<!-- <up-button type="primary" size="normal" @click="handleProcess(orderDetail)">{{-->
  281 +<!-- nextStepMap[orderDetail.taskKey].btnText-->
  282 +<!-- }}-->
  283 +<!-- </up-button>-->
  284 +
  285 +<!-- </view>-->
  286 +<!-- </view>-->
287 287
288 <!-- 回退原因弹窗(新增图片上传) --> 288 <!-- 回退原因弹窗(新增图片上传) -->
289 <up-modal 289 <up-modal
pages-sub/problem/work-order-manage/add-maintain-order.vue
@@ -492,11 +492,11 @@ const loadCoProcessorList = async () =&gt; { @@ -492,11 +492,11 @@ const loadCoProcessorList = async () =&gt; {
492 }).filter(Boolean); // 过滤null项 492 }).filter(Boolean); // 过滤null项
493 } else { 493 } else {
494 coProcessorList.value = [] 494 coProcessorList.value = []
495 - uni.showToast({  
496 - title: '暂无共同处理人数据',  
497 - icon: 'none',  
498 - duration: 2000  
499 - }); 495 + // uni.showToast({
  496 + // title: '暂无共同处理人数据',
  497 + // icon: 'none',
  498 + // duration: 1000
  499 + // });
500 console.warn('loadCoProcessorList提示:处理人列表为空'); 500 console.warn('loadCoProcessorList提示:处理人列表为空');
501 } 501 }
502 } catch (err) { 502 } catch (err) {
pages.json
@@ -10,7 +10,8 @@ @@ -10,7 +10,8 @@
10 { 10 {
11 "path": "pages/login/index", 11 "path": "pages/login/index",
12 "style": { 12 "style": {
13 - "navigationBarTitleText": "登录" 13 + "navigationBarTitleText": "登录",
  14 + "navigationStyle": "custom"
14 } 15 }
15 }, 16 },
16 { 17 {
@@ -166,7 +167,7 @@ @@ -166,7 +167,7 @@
166 167
167 { 168 {
168 "path": "regional-order-manage/add-patrol-order", 169 "path": "regional-order-manage/add-patrol-order",
169 - "style": { "navigationBarTitleText": "待派单" } 170 + "style": { "navigationBarTitleText": "提交工单" }
170 }, 171 },
171 172
172 { 173 {
@@ -197,7 +198,21 @@ @@ -197,7 +198,21 @@
197 { 198 {
198 "path": "tree-archive/index", 199 "path": "tree-archive/index",
199 "style": { "navigationBarTitleText": "行道树档案" } 200 "style": { "navigationBarTitleText": "行道树档案" }
  201 + },
  202 + {
  203 + "path": "tree-archive/treeRecord",
  204 + "style": { "navigationBarTitleText": "行道树记录" }
  205 + },
  206 + {
  207 + "path": "tree-archive/addTree",
  208 + "style": { "navigationBarTitleText": "新增行道树" }
  209 + },
  210 + {
  211 + "path": "tree-archive/editTree",
  212 + "style": { "navigationBarTitleText": "编辑行道树" }
200 } 213 }
  214 +
  215 +
201 ] 216 ]
202 } 217 }
203 ], 218 ],
pages/login/index.vue
1 <template> 1 <template>
2 <view class="login-page"> 2 <view class="login-page">
3 - <!-- 纯CSS渐变动效背景(替代粒子动画,兼容所有版本) -->  
4 - <view class="bg-animation"></view> 3 + <!-- 顶部标题区 - 加高 + 左下大圆角 + 底部蓝色过滤阴影 + 从浅到深 蓝色渐变背景 -->
  4 + <view class="top-title">
  5 + <text class="welcome-text">你好,欢迎光临</text>
  6 + <text class="platform-name">全域智能运营管理平台</text>
  7 + </view>
5 8
6 - <!-- 登录表单区域(悬浮层) --> 9 + <!-- 登录表单区域 - 往上偏移 盖住top-title一部分 -->
7 <view class="login-form"> 10 <view class="login-form">
8 - <!-- 登录标题(简约大气) -->  
9 - <view class="login-title">园林登录</view> 11 + <!-- 登录标题 -->
  12 + <view class="login-title">账户登录</view>
10 13
11 - <!-- 账号输入框 -->  
12 - <view class="form-item">  
13 - <up-input  
14 - v-model="form.account"  
15 - placeholder="请输入登录账号"  
16 - border="surround"  
17 - clearable  
18 - maxlength="30"  
19 - input-align="left"  
20 - :disabled="isLoading"  
21 - @blur="checkAccount"  
22 - :custom-style="{  
23 - backgroundColor: 'rgba(255, 255, 255, 0.9)',  
24 - borderColor: '#e5e7eb'  
25 - }" 14 + <!-- ✅ 核心:和工单页同款 up-form 表单校验容器 -->
  15 + <up-form
  16 + label-position="left"
  17 + :model="form"
  18 + ref="loginFormRef"
  19 + labelWidth="0"
  20 + >
  21 + <!-- 账号输入框 + 校验 -->
  22 + <up-form-item
  23 + prop="account"
26 > 24 >
27 - <template #prefix>  
28 - <up-icon name="account" color="#6b7280" size="20"></up-icon>  
29 - </template>  
30 - </up-input>  
31 - <!-- 账号错误提示 -->  
32 - <view class="error-tip" v-if="error.account">{{ error.account }}</view>  
33 - </view> 25 + <up-input
  26 + v-model="form.account"
  27 + border="surround"
  28 + clearable
  29 + maxlength="30"
  30 + input-align="left"
  31 + fontSize="16px"
  32 + :disabled="isLoading"
  33 + shape="circle"
  34 + placeholder="请输入账户"
  35 + selectionStart="15"
  36 + selectionEnd="15"
  37 + @blur="() => loginFormRef.validateField('account')"
  38 + />
  39 + </up-form-item>
34 40
35 - <!-- 密码输入框(移除查看密码功能) -->  
36 - <view class="form-item">  
37 - <up-input  
38 - v-model="form.password"  
39 - placeholder="请输入登录密码"  
40 - maxlength="20"  
41 - border="surround"  
42 - clearable  
43 - input-align="left"  
44 - type="password"  
45 - :disabled="isLoading"  
46 - @blur="checkPassword"  
47 - :custom-style="{  
48 - backgroundColor: 'rgba(255, 255, 255, 0.9)',  
49 - borderColor: '#e5e7eb'  
50 - }" 41 + <!-- 密码输入框 + 校验 -->
  42 + <up-form-item
  43 + prop="password"
51 > 44 >
52 - <template #prefix>  
53 - <up-icon name="lock" color="#6b7280" size="20"></up-icon>  
54 - </template>  
55 - </up-input>  
56 - <!-- 密码错误提示 -->  
57 - <view class="error-tip" v-if="error.password">{{ error.password }}</view>  
58 - </view> 45 + <up-input
  46 + v-model="form.password"
  47 + placeholder="请输入密码"
  48 + maxlength="20"
  49 + border="surround"
  50 + clearable
  51 + input-align="left"
  52 + type="password"
  53 + :disabled="isLoading"
  54 + shape="circle"
  55 + selectionStart="5"
  56 + selectionEnd="5"
  57 + @blur="() => loginFormRef.validateField('password')"
  58 + />
  59 + </up-form-item>
  60 + </up-form>
59 61
60 <!-- 登录按钮 --> 62 <!-- 登录按钮 -->
61 <up-button 63 <up-button
@@ -64,44 +66,58 @@ @@ -64,44 +66,58 @@
64 size="large" 66 size="large"
65 :loading="isLoading" 67 :loading="isLoading"
66 @click="handleLogin" 68 @click="handleLogin"
67 - :custom-style="{  
68 -  
69 - }" 69 + shape="circle"
70 > 70 >
71 登录 71 登录
72 </up-button> 72 </up-button>
73 </view> 73 </view>
  74 +
  75 + <!-- 版权信息 -->
  76 + <view class="copyright">蓟城山水集团版权所有</view>
74 </view> 77 </view>
75 </template> 78 </template>
76 79
77 <script setup> 80 <script setup>
78 -import { ref, reactive, onMounted } from 'vue'; 81 +import { ref, reactive, onMounted, nextTick } from 'vue';
79 import { useUserStore } from '@/pinia/user'; 82 import { useUserStore } from '@/pinia/user';
80 import globalConfig from '@/common/config/global'; 83 import globalConfig from '@/common/config/global';
81 84
82 -// 表单数据 85 +// ========== 【全局实例 & 基础状态】 和工单页写法一致 ==========
  86 +const userStore = useUserStore();
  87 +const loginFormRef = ref(null); // 表单ref 用于校验
  88 +const isLoading = ref(false); // 登录加载状态
  89 +
  90 +// ========== 【表单数据】 和工单页一致的 reactive 声明 ==========
83 const form = reactive({ 91 const form = reactive({
84 - account: '', // 账号  
85 - password: '' // 密码 92 + account: '', // 账号
  93 + password: '' // 密码
86 }); 94 });
87 95
88 -// 状态管理  
89 -const isLoading = ref(false);  
90 -const error = reactive({  
91 - account: '',  
92 - password: '' 96 +// ========== 【表单校验规则】 ✅核心 和工单页1:1同款校验规则写法 ==========
  97 +const loginFormRules = reactive({
  98 + // 账号校验:必填 + 长度限制 2-30位(合理账号长度,可自行调整)
  99 + account: [
  100 + { type: 'string', required: true, message: '请输入登录账号', trigger: ['change', 'blur'] },
  101 + { type: 'string', min: 2, max: 30, message: '账号长度为2-30个字符', trigger: ['change', 'blur'] }
  102 + ],
  103 + // 密码校验:必填 + 长度限制 6-20位(行业通用密码长度,可自行调整)
  104 + password: [
  105 + { type: 'string', required: true, message: '请输入登录密码', trigger: ['change', 'blur'] },
  106 + { type: 'string', min: 6, max: 20, message: '密码长度为6-20个字符', trigger: ['change', 'blur'] }
  107 + ]
93 }); 108 });
94 109
95 -// 实例化 Pinia 用户仓库  
96 -const userStore = useUserStore();  
97 -  
98 -// 页面加载时初始化 110 +// ========== 【生命周期】 nextTick 设置校验规则 和工单页一致 ==========
99 onMounted(() => { 111 onMounted(() => {
100 // 检查登录态 112 // 检查登录态
101 checkLoginStatus(); 113 checkLoginStatus();
  114 + // nextTick确保表单挂载完成再赋值规则,修复校验不生效bug,和工单页写法一致
  115 + nextTick(() => {
  116 + loginFormRef.value?.setRules(loginFormRules);
  117 + });
102 }); 118 });
103 119
104 -// 检查登录状态 120 +// ========== 【检查登录状态】 原有逻辑不变 ==========
105 const checkLoginStatus = () => { 121 const checkLoginStatus = () => {
106 try { 122 try {
107 // 已登录则直接跳首页 123 // 已登录则直接跳首页
@@ -109,7 +125,6 @@ const checkLoginStatus = () =&gt; { @@ -109,7 +125,6 @@ const checkLoginStatus = () =&gt; {
109 uni.switchTab({ 125 uni.switchTab({
110 url: '/pages/workbench/index', 126 url: '/pages/workbench/index',
111 fail: () => { 127 fail: () => {
112 - // 非tabBar页面用redirectTo  
113 uni.reLaunch({ url: '/pages/workbench/index' }); 128 uni.reLaunch({ url: '/pages/workbench/index' });
114 } 129 }
115 }); 130 });
@@ -120,38 +135,14 @@ const checkLoginStatus = () =&gt; { @@ -120,38 +135,14 @@ const checkLoginStatus = () =&gt; {
120 } 135 }
121 }; 136 };
122 137
123 -// 校验账号  
124 -const checkAccount = () => {  
125 - if (!form.account) {  
126 - error.account = '请输入登录账号';  
127 - } else {  
128 - error.account = '';  
129 - }  
130 -};  
131 -  
132 -// 校验密码  
133 -const checkPassword = () => {  
134 - if (!form.password) {  
135 - error.password = '请输入登录密码';  
136 - } else {  
137 - error.password = '';  
138 - }  
139 -};  
140 -  
141 -// 表单整体校验  
142 -const validateForm = () => {  
143 - checkAccount();  
144 - checkPassword();  
145 - return !error.account && !error.password;  
146 -};  
147 -  
148 -// 登录处理 138 +// ========== 【核心登录方法】 ✅ 完整表单校验 + 登录逻辑,和工单页submit提交逻辑一致 ==========
149 const handleLogin = async () => { 139 const handleLogin = async () => {
150 - if (!validateForm()) return;  
151 -  
152 - isLoading.value = true;  
153 -  
154 try { 140 try {
  141 + // ✅ 第一步:先执行表单整体校验,通过后再执行登录逻辑(和工单页submitWorkOrder写法一致)
  142 + await loginFormRef.value.validate();
  143 +
  144 + isLoading.value = true;
  145 + // 执行登录请求
155 await userStore.login({ 146 await userStore.login({
156 username: form.account, 147 username: form.account,
157 password: form.password 148 password: form.password
@@ -170,16 +161,15 @@ const handleLogin = async () =&gt; { @@ -170,16 +161,15 @@ const handleLogin = async () =&gt; {
170 }); 161 });
171 }, 1000); 162 }, 1000);
172 } catch (err) { 163 } catch (err) {
173 - console.error('登录失败详情:', err);  
174 - const errorMsg =  
175 - err.message === '网络异常,请稍后重试'  
176 - ? '网络异常,请稍后重试'  
177 - : err.message || '登录失败,请检查账号密码';  
178 - uni.showToast({  
179 - title: errorMsg,  
180 - icon: 'none',  
181 - duration: 2000  
182 - }); 164 + // ✅ 校验失败/登录失败 统一捕获提示,和工单页异常处理一致
  165 + if (!Array.isArray(err)) {
  166 + console.error('登录失败:', err);
  167 + uni.showToast({
  168 + title: err.message || '账号或密码错误,请重试',
  169 + icon: 'none',
  170 + duration: 2000
  171 + });
  172 + }
183 } finally { 173 } finally {
184 isLoading.value = false; 174 isLoading.value = false;
185 } 175 }
@@ -190,122 +180,83 @@ const handleLogin = async () =&gt; { @@ -190,122 +180,83 @@ const handleLogin = async () =&gt; {
190 // 核心布局 180 // 核心布局
191 .login-page { 181 .login-page {
192 min-height: 100vh; 182 min-height: 100vh;
193 - position: relative;  
194 - padding: 40rpx 30rpx; 183 + padding: 0;
195 box-sizing: border-box; 184 box-sizing: border-box;
196 - overflow: hidden; // 隐藏动效溢出  
197 -  
198 - // 适配小程序安全区  
199 - /* #ifdef MP-WEIXIN */  
200 - padding-bottom: constant(safe-area-inset-bottom);  
201 - padding-bottom: env(safe-area-inset-bottom);  
202 - /* #endif */ 185 + overflow: hidden;
  186 + position: relative;
  187 + background: #f5f7fa;
203 } 188 }
204 189
205 -// 纯CSS渐变动效背景(替代粒子,兼容所有版本)  
206 -.bg-animation {  
207 - position: absolute;  
208 - top: 0;  
209 - left: 0;  
210 - width: 100%;  
211 - height: 100%;  
212 - z-index: 0;  
213 - // 基础渐变底色  
214 - background: linear-gradient(120deg, #f0f9ff 0%, #e6f7ff 100%);  
215 - // 伪粒子动效(多个透明渐变圆缓慢移动)  
216 - &::before,  
217 - &::after {  
218 - content: '';  
219 - position: absolute;  
220 - border-radius: 50%;  
221 - background: rgba(59, 130, 246, 0.05);  
222 - animation: float 20s infinite linear;  
223 - }  
224 -  
225 - &::before {  
226 - width: 600rpx;  
227 - height: 600rpx;  
228 - top: -200rpx;  
229 - left: -200rpx;  
230 - } 190 +// 顶部:从左到右 由浅到深 线性渐变 过渡自然柔和 无突兀色块
  191 +.top-title {
  192 + background: linear-gradient(120deg, #4299e1 0%, #2970e8 50%, #2563eb 100%);
  193 + color: #fff;
  194 + padding: 240rpx 30rpx 80rpx;
  195 + text-align: left;
  196 + border-bottom-left-radius: 120rpx;
  197 + position: relative;
  198 + z-index: 1;
  199 + box-shadow: 0 25rpx 40rpx -15rpx rgba(37, 99, 235, 0.3);
231 200
232 - &::after {  
233 - width: 800rpx;  
234 - height: 800rpx;  
235 - bottom: -300rpx;  
236 - right: -300rpx;  
237 - animation-delay: -10s; // 错开动画时间 201 + .welcome-text {
  202 + font-size: 46rpx;
  203 + display: block;
  204 + margin-bottom: 10px;
  205 + font-weight: 500;
238 } 206 }
239 207
240 - // 额外小动效点(模拟粒子)  
241 - & > view {  
242 - position: absolute;  
243 - width: 400rpx;  
244 - height: 400rpx;  
245 - border-radius: 50%;  
246 - background: rgba(59, 130, 246, 0.03);  
247 - top: 50%;  
248 - left: 50%;  
249 - transform: translate(-50%, -50%);  
250 - animation: float 15s infinite linear reverse; 208 + .platform-name {
  209 + margin-bottom: 10px;
  210 + font-size: 46rpx;
  211 + display: block;
  212 + opacity: 0.95;
251 } 213 }
252 } 214 }
253 215
254 -// 浮动动画(模拟粒子运动)  
255 -@keyframes float {  
256 - 0% {  
257 - transform: translate(0, 0) rotate(0deg);  
258 - }  
259 - 25% {  
260 - transform: translate(50rpx, 30rpx) rotate(90deg);  
261 - }  
262 - 50% {  
263 - transform: translate(0, 60rpx) rotate(180deg);  
264 - }  
265 - 75% {  
266 - transform: translate(-50rpx, 30rpx) rotate(270deg);  
267 - }  
268 - 100% {  
269 - transform: translate(0, 0) rotate(360deg);  
270 - }  
271 -}  
272 -  
273 -// 登录表单(悬浮层)  
274 .login-form { 216 .login-form {
  217 + background-color: #fff;
  218 + padding: 40rpx 30rpx;
  219 + border-radius: 16rpx;
  220 + box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06);
  221 + margin: -60rpx 40rpx 0;
275 position: relative; 222 position: relative;
276 z-index: 10; 223 z-index: 10;
277 - background-color: rgba(255, 255, 255, 0.95);  
278 - padding: 60rpx 40rpx;  
279 - border-radius: 16rpx;  
280 - box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);  
281 - max-width: 600rpx;  
282 - margin: 0 auto;  
283 - margin-top: 100rpx; 224 + box-sizing: border-box;
284 225
285 - // 登录标题  
286 .login-title { 226 .login-title {
287 - font-size: 36rpx; 227 + font-size: 38rpx;
288 font-weight: 600; 228 font-weight: 600;
289 - color: #111827;  
290 - text-align: center;  
291 - margin-bottom: 40rpx;  
292 - letter-spacing: 2rpx; 229 + color: #666;
  230 + margin-bottom: 30rpx;
  231 + letter-spacing: 1rpx;
293 } 232 }
294 } 233 }
295 234
296 -// 表单项  
297 -.form-item {  
298 - margin-bottom: 30rpx; 235 +// 适配表单校验的间距
  236 +:deep(.u-form-item) {
  237 + margin-bottom: 20rpx;
299 position: relative; 238 position: relative;
  239 +}
300 240
301 - // 错误提示  
302 - .error-tip {  
303 - font-size: 24rpx;  
304 - color: #ef4444;  
305 - margin-top: 10rpx;  
306 - line-height: 1.2;  
307 - padding-left: 10rpx;  
308 - } 241 +// 登录按钮:和顶部完全同款 从左到右 由浅到深 线性渐变 + 保留所有原尺寸
  242 +.login-btn {
  243 + margin-top: 40rpx;
  244 + width: 100%;
  245 + height: 88rpx;
  246 + line-height: 88rpx;
  247 + border-radius: 8rpx;
  248 + font-size: 32rpx;
  249 + background: linear-gradient(120deg, #4299e1 0%, #2970e8 50%, #2563eb 100%) !important;
  250 + border: none !important;
  251 + &::after { border: none !important; }
309 } 252 }
310 253
  254 +.copyright {
  255 + width: 100%;
  256 + text-align: center;
  257 + font-size: 24rpx;
  258 + color: #999;
  259 + position: fixed;
  260 + bottom: 100px;
  261 +}
311 </style> 262 </style>
312 \ No newline at end of file 263 \ No newline at end of file
static/imgs/tree/tree-high.png 0 → 100644

1003 Bytes

static/imgs/tree/treearound.png 0 → 100644

927 Bytes