Commit c7df828a939478c246d15dd8583e078840820353

Authored by 刘淇
1 parent 8d0834be

快速工单 样式优化

@@ -123,4 +123,9 @@ page { @@ -123,4 +123,9 @@ page {
123 padding-left: 15px; 123 padding-left: 15px;
124 padding-right: 15px; 124 padding-right: 15px;
125 } 125 }
  126 +
  127 +.common-text-color{
  128 + font-size: 14px;
  129 + color: #606266
  130 +}
126 </style> 131 </style>
127 \ No newline at end of file 132 \ No newline at end of file
common/utils/useUploadImgs.ts renamed to common/utils/useUploadImgs.js
1 -import { ref, type Ref } from 'vue' 1 +import { ref } from 'vue'
2 import { uploadImages } from '@/common/utils/upload' 2 import { uploadImages } from '@/common/utils/upload'
3 -import type { UniFormRef } from '@/uni_modules/uview-plus/types'  
4 -  
5 -// 定义上传配置类型  
6 -export interface UploadImgConfig {  
7 - maxCount?: number // 最大上传数量,默认3  
8 - uploadText?: string // 上传按钮文案  
9 - sizeType?: UniApp.UploadFileOption['sizeType'] // 图片压缩类型  
10 - formRef: Ref<UniFormRef | null> // 表单ref,用于校验  
11 - fieldName: string // 表单校验字段名(如problemImgs)  
12 -}  
13 -  
14 -// 定义图片项类型  
15 -export interface UploadImgItem {  
16 - url: string  
17 - status: 'uploading' | 'success' | 'failed'  
18 - message: string  
19 - [key: string]: any // 兼容其他字段  
20 -}  
21 3
22 /** 4 /**
23 - * 图片上传组合式函数(支持多实例复用 5 + * 图片上传组合式函数(兼容非TS环境,纯数组格式
24 * @param config 上传配置 6 * @param config 上传配置
25 * @returns 上传相关方法和状态 7 * @returns 上传相关方法和状态
26 */ 8 */
27 -export function useUploadImgs(config: UploadImgConfig) { 9 +export function useUploadImgs(config) {
28 // 默认配置 10 // 默认配置
29 const defaultConfig = { 11 const defaultConfig = {
30 maxCount: 3, 12 maxCount: 3,
31 uploadText: '选择图片', 13 uploadText: '选择图片',
32 - sizeType: ['compressed'] as UniApp.UploadFileOption['sizeType'], 14 + sizeType: ['compressed'],
33 ...config 15 ...config
34 } 16 }
35 17
36 - // 图片列表  
37 - const imgList = ref<UploadImgItem[]>([]) 18 + // 核心修复:初始化为纯数组,且格式适配u-upload
  19 + const imgList = ref([])
38 20
39 /** 21 /**
40 * 删除图片 22 * 删除图片
41 */ 23 */
42 - const deleteImg = (event: { index: number }) => {  
43 - imgList.value.splice(event.index, 1) 24 + const deleteImg = (event) => {
  25 + // 确保index是有效数字
  26 + const index = Number(event.index)
  27 + if (isNaN(index) || index < 0 || index >= imgList.value.length) return
  28 +
  29 + imgList.value.splice(index, 1)
44 // 删除后重新校验 30 // 删除后重新校验
45 defaultConfig.formRef.value?.validateField(defaultConfig.fieldName) 31 defaultConfig.formRef.value?.validateField(defaultConfig.fieldName)
46 uni.showToast({ title: '图片删除成功', icon: 'success' }) 32 uni.showToast({ title: '图片删除成功', icon: 'success' })
47 } 33 }
48 34
49 /** 35 /**
50 - * 上传图片 36 + * 上传图片(适配u-upload的file格式)
51 */ 37 */
52 - const uploadImgs = async (event: { file: UniApp.ChooseImageSuccessCallbackResult | UniApp.ChooseImageSuccessCallbackResult[] }) => {  
53 - const fileList = Array.isArray(event.file) ? event.file : [event.file] 38 + const uploadImgs = async (event) => {
  39 + // 核心修复:标准化file数据格式
  40 + const rawFile = event.file || {}
  41 + const fileList = Array.isArray(rawFile) ? rawFile : [rawFile]
  42 +
  43 + // 过滤无效文件
  44 + const validFiles = fileList.filter(file => file && file.url)
  45 + if (validFiles.length === 0) {
  46 + uni.showToast({ title: '请选择有效图片', icon: 'none' })
  47 + return
  48 + }
  49 +
54 const targetImgList = imgList.value 50 const targetImgList = imgList.value
55 51
56 - // 过滤超出最大数量的图片  
57 - if (targetImgList.length + fileList.length > defaultConfig.maxCount) { 52 + // 校验最大数量
  53 + if (targetImgList.length + validFiles.length > defaultConfig.maxCount) {
58 uni.showToast({ title: `最多只能上传${defaultConfig.maxCount}张图片`, icon: 'none' }) 54 uni.showToast({ title: `最多只能上传${defaultConfig.maxCount}张图片`, icon: 'none' })
59 return 55 return
60 } 56 }
61 57
62 - const filePaths = fileList.map(item => item.url)  
63 - const tempItems = fileList.map(item => ({  
64 - ...item,  
65 - status: 'uploading' as const,  
66 - message: '上传中' 58 + // 核心修复:转换为u-upload兼容的格式
  59 + const filePaths = validFiles.map(item => item.url)
  60 + const tempItems = validFiles.map(item => ({
  61 + url: item.url,
  62 + status: 'uploading',
  63 + message: '上传中',
  64 + name: item.name || '',
  65 + size: item.size || 0
67 })) 66 }))
68 const startIndex = targetImgList.length 67 const startIndex = targetImgList.length
69 targetImgList.push(...tempItems) 68 targetImgList.push(...tempItems)
@@ -78,29 +77,32 @@ export function useUploadImgs(config: UploadImgConfig) { @@ -78,29 +77,32 @@ export function useUploadImgs(config: UploadImgConfig) {
78 uploadResultUrls.forEach((url, index) => { 77 uploadResultUrls.forEach((url, index) => {
79 if (targetImgList[startIndex + index]) { 78 if (targetImgList[startIndex + index]) {
80 targetImgList.splice(startIndex + index, 1, { 79 targetImgList.splice(startIndex + index, 1, {
81 - ...fileList[index],  
82 - status: 'success' as const, 80 + url: url,
  81 + status: 'success',
83 message: '', 82 message: '',
84 - url: url 83 + name: validFiles[index].name || '',
  84 + size: validFiles[index].size || 0
85 }) 85 })
86 } 86 }
87 }) 87 })
88 88
89 // 处理上传失败的图片 89 // 处理上传失败的图片
90 - if (uploadResultUrls.length < fileList.length) {  
91 - const failCount = fileList.length - uploadResultUrls.length  
92 - for (let i = uploadResultUrls.length; i < fileList.length; i++) { 90 + if (uploadResultUrls.length < validFiles.length) {
  91 + const failCount = validFiles.length - uploadResultUrls.length
  92 + for (let i = uploadResultUrls.length; i < validFiles.length; i++) {
93 if (targetImgList[startIndex + i]) { 93 if (targetImgList[startIndex + i]) {
94 targetImgList.splice(startIndex + i, 1, { 94 targetImgList.splice(startIndex + i, 1, {
95 - ...fileList[i],  
96 - status: 'failed' as const,  
97 - message: '上传失败' 95 + url: validFiles[i].url,
  96 + status: 'failed',
  97 + message: '上传失败',
  98 + name: validFiles[i].name || '',
  99 + size: validFiles[i].size || 0
98 }) 100 })
99 } 101 }
100 } 102 }
101 uni.showToast({ title: `成功上传${uploadResultUrls.length}张,失败${failCount}张`, icon: 'none' }) 103 uni.showToast({ title: `成功上传${uploadResultUrls.length}张,失败${failCount}张`, icon: 'none' })
102 } else { 104 } else {
103 - uni.showToast({ title: `成功上传${fileList.length}张图片`, icon: 'success' }) 105 + uni.showToast({ title: `成功上传${validFiles.length}张图片`, icon: 'success' })
104 } 106 }
105 107
106 // 上传完成后校验 108 // 上传完成后校验
@@ -108,12 +110,14 @@ export function useUploadImgs(config: UploadImgConfig) { @@ -108,12 +110,14 @@ export function useUploadImgs(config: UploadImgConfig) {
108 } catch (err) { 110 } catch (err) {
109 console.error(`【${defaultConfig.fieldName}】图片上传失败:`, err) 111 console.error(`【${defaultConfig.fieldName}】图片上传失败:`, err)
110 // 标记所有上传失败 112 // 标记所有上传失败
111 - for (let i = 0; i < fileList.length; i++) { 113 + for (let i = 0; i < validFiles.length; i++) {
112 if (targetImgList[startIndex + i]) { 114 if (targetImgList[startIndex + i]) {
113 targetImgList.splice(startIndex + i, 1, { 115 targetImgList.splice(startIndex + i, 1, {
114 - ...fileList[i],  
115 - status: 'failed' as const,  
116 - message: '上传失败' 116 + url: validFiles[i].url,
  117 + status: 'failed',
  118 + message: '上传失败',
  119 + name: validFiles[i].name || '',
  120 + size: validFiles[i].size || 0
117 }) 121 })
118 } 122 }
119 } 123 }
@@ -137,14 +141,15 @@ export function useUploadImgs(config: UploadImgConfig) { @@ -137,14 +141,15 @@ export function useUploadImgs(config: UploadImgConfig) {
137 required: true, 141 required: true,
138 message: `请上传${defaultConfig.uploadText.replace('选择', '')}`, 142 message: `请上传${defaultConfig.uploadText.replace('选择', '')}`,
139 trigger: 'change', 143 trigger: 'change',
140 - validator: (rule: any, value: any, callback: (error?: Error) => void) => { 144 + validator: (rule, value, callback) => {
141 const hasSuccessImg = imgList.value.some(item => item.status === 'success') 145 const hasSuccessImg = imgList.value.some(item => item.status === 'success')
142 hasSuccessImg ? callback() : callback(new Error(`请上传至少1张${defaultConfig.uploadText.replace('选择', '')}`)) 146 hasSuccessImg ? callback() : callback(new Error(`请上传至少1张${defaultConfig.uploadText.replace('选择', '')}`))
143 } 147 }
144 } 148 }
145 149
146 return { 150 return {
147 - imgList, 151 + imgList: imgList.value, // 核心修复:返回纯数组(解除响应式代理)
  152 + rawImgList: imgList, // 保留响应式引用(内部使用)
148 uploadImgs, 153 uploadImgs,
149 deleteImg, 154 deleteImg,
150 getSuccessImgUrls, 155 getSuccessImgUrls,
pages-sub/daily/maintain-manage/finish-plan-detail.vue
@@ -71,7 +71,7 @@ @@ -71,7 +71,7 @@
71 <view style="min-width: 200rpx">巡查描述</view> 71 <view style="min-width: 200rpx">巡查描述</view>
72 </template> 72 </template>
73 <template #value> 73 <template #value>
74 - <view class="up-line-1" style="color: #606266">{{i.remark || '--'}}</view> 74 + <view class="up-line-1 common-text-color" >{{i.remark || '--'}}</view>
75 </template> 75 </template>
76 </up-cell> 76 </up-cell>
77 77
pages-sub/daily/patrol-manage/finish-plan-detail.vue
@@ -72,7 +72,7 @@ @@ -72,7 +72,7 @@
72 <view style="min-width: 200rpx">巡查描述</view> 72 <view style="min-width: 200rpx">巡查描述</view>
73 </template> 73 </template>
74 <template #value> 74 <template #value>
75 - <view class="up-line-1" style="color: #606266">{{i.remark || '--'}}</view> 75 + <view class="up-line-1 common-text-color">{{i.remark || '--'}}</view>
76 </template> 76 </template>
77 </up-cell> 77 </up-cell>
78 78
pages-sub/daily/quick-order/add-order.vue
1 <template> 1 <template>
2 - <view class="u-page"> 2 + <view class="page-container">
3 <!-- 核心:将所有 up-form-item 包裹在同一个 up-form 内 --> 3 <!-- 核心:将所有 up-form-item 包裹在同一个 up-form 内 -->
4 <view class="work-order-form-content commonPageLRpadding"> 4 <view class="work-order-form-content commonPageLRpadding">
5 <up-form 5 <up-form
@@ -35,14 +35,14 @@ @@ -35,14 +35,14 @@
35 > 35 >
36 <up-input 36 <up-input
37 v-model="workOrderForm.roadName" 37 v-model="workOrderForm.roadName"
38 - disabled 38 + readonly
39 disabled-color="#ffffff" 39 disabled-color="#ffffff"
40 placeholder="请先选择工单位置" 40 placeholder="请先选择工单位置"
41 border="none" 41 border="none"
42 - :placeholder-style="workOrderForm.workLocation ? '' : 'color:#999;'" 42 +
43 ></up-input> 43 ></up-input>
44 <template #right> 44 <template #right>
45 - <up-icon name="arrow-right" size="16" :color="workOrderForm.workLocation ? '#333' : '#999'"></up-icon> 45 + <up-icon name="arrow-right" size="16" ></up-icon>
46 </template> 46 </template>
47 </up-form-item> 47 </up-form-item>
48 48
@@ -74,7 +74,7 @@ @@ -74,7 +74,7 @@
74 > 74 >
75 <up-textarea 75 <up-textarea
76 placeholder="请输入情况描述(最多200字)" 76 placeholder="请输入情况描述(最多200字)"
77 - v-model="workOrderForm.problemDesc" 77 + v-model.trim="workOrderForm.problemDesc"
78 count 78 count
79 maxlength="200" 79 maxlength="200"
80 rows="4" 80 rows="4"
@@ -111,7 +111,7 @@ @@ -111,7 +111,7 @@
111 <up-form-item label="处理结果" prop="handleResult" class="mt-20"> 111 <up-form-item label="处理结果" prop="handleResult" class="mt-20">
112 <up-textarea 112 <up-textarea
113 placeholder="请输入处理结果描述(最多200字,选填)" 113 placeholder="请输入处理结果描述(最多200字,选填)"
114 - v-model="workOrderForm.handleResult" 114 + v-model.trim="workOrderForm.handleResult"
115 count 115 count
116 maxlength="200" 116 maxlength="200"
117 rows="4" 117 rows="4"
@@ -206,7 +206,7 @@ export default { @@ -206,7 +206,7 @@ export default {
206 ], 206 ],
207 problemDesc: [ 207 problemDesc: [
208 {type: 'string', required: true, message: '请输入情况描述', trigger: ['change', 'blur']}, 208 {type: 'string', required: true, message: '请输入情况描述', trigger: ['change', 'blur']},
209 - {type: 'string', min: 3, max: 200, message: '情况描述需3-150字', trigger: ['change', 'blur']} 209 + {type: 'string', min: 3, max: 200, message: '情况描述需3-200字', trigger: ['change', 'blur']}
210 ], 210 ],
211 problemImgs: [ 211 problemImgs: [
212 { 212 {
@@ -447,8 +447,8 @@ export default { @@ -447,8 +447,8 @@ export default {
447 roadName: this.workOrderForm.roadName, 447 roadName: this.workOrderForm.roadName,
448 imgs: this.getImgUrlList(this.problemImgsList), 448 imgs: this.getImgUrlList(this.problemImgsList),
449 longRangeImgList: this.getImgUrlList(this.completeImgsList), 449 longRangeImgList: this.getImgUrlList(this.completeImgsList),
450 - remark: this.workOrderForm.problemDesc,  
451 - handleResult: this.workOrderForm.handleResult, 450 + remark: this.workOrderForm.problemDesc.trim(),
  451 + handleResult: this.workOrderForm.handleResult.trim(),
452 latLonType: 2, 452 latLonType: 2,
453 lat: this.workOrderForm.lat, 453 lat: this.workOrderForm.lat,
454 lon: this.workOrderForm.lon, 454 lon: this.workOrderForm.lon,
@@ -509,13 +509,14 @@ export default { @@ -509,13 +509,14 @@ export default {
509 509
510 <style lang="scss" scoped> 510 <style lang="scss" scoped>
511 // 全局页面样式 511 // 全局页面样式
512 -.u-page { 512 +.page-container {
513 min-height: 100vh; 513 min-height: 100vh;
514 } 514 }
515 515
516 // 工单表单内容容器 516 // 工单表单内容容器
517 .work-order-form-content { 517 .work-order-form-content {
518 background: #fff; 518 background: #fff;
  519 + margin-bottom: 100rpx;
519 } 520 }
520 521
521 522
pages-sub/daily/quick-order/order-detail.vue
@@ -33,7 +33,7 @@ @@ -33,7 +33,7 @@
33 <view style="min-width: 200rpx">工单位置</view> 33 <view style="min-width: 200rpx">工单位置</view>
34 </template> 34 </template>
35 <template #value> 35 <template #value>
36 - <view class="up-line-1">{{orderDetail.roadName || '--'}}</view> 36 + <view class="up-line-1 common-text-color" >{{orderDetail.roadName || '--'}}</view>
37 </template> 37 </template>
38 38
39 </up-cell> 39 </up-cell>
@@ -54,7 +54,7 @@ @@ -54,7 +54,7 @@
54 <view style="min-width: 200rpx">情况描述</view> 54 <view style="min-width: 200rpx">情况描述</view>
55 </template> 55 </template>
56 <template #value> 56 <template #value>
57 - <view class="up-line-1">{{orderDetail.remark || '--'}}</view> 57 + <view class="up-line-1 common-text-color" >{{orderDetail.remark || '--'}}</view>
58 </template> 58 </template>
59 </up-cell> 59 </up-cell>
60 60
@@ -101,7 +101,7 @@ @@ -101,7 +101,7 @@
101 <view style="min-width: 200rpx">处理结果</view> 101 <view style="min-width: 200rpx">处理结果</view>
102 </template> 102 </template>
103 <template #value> 103 <template #value>
104 - <view class="up-line-1">{{orderDetail.handleResult || '--'}}</view> 104 + <view class="up-line-1 common-text-color" >{{orderDetail.handleResult || '--'}}</view>
105 </template> 105 </template>
106 </up-cell> 106 </up-cell>
107 </up-cell-group> 107 </up-cell-group>
pages-sub/problem/work-order-manage/add-order.vue
1 <template> 1 <template>
2 - <view class="u-page">  
3 - <!-- 核心:将所有 up-form-item 包裹在同一个 up-form 内 --> 2 + <view class="page-container">
4 <view class="work-order-form-content commonPageLRpadding"> 3 <view class="work-order-form-content commonPageLRpadding">
5 <up-form 4 <up-form
6 label-position="left" 5 label-position="left"
@@ -25,7 +24,7 @@ @@ -25,7 +24,7 @@
25 ></up-input> 24 ></up-input>
26 </up-form-item> 25 </up-form-item>
27 26
28 - <!-- 2. 道路名称(下拉框:位置选择后才可点击) --> 27 + <!-- 2. 道路名称(下拉框) -->
29 <up-form-item 28 <up-form-item
30 label="道路名称" 29 label="道路名称"
31 prop="roadName" 30 prop="roadName"
@@ -98,25 +97,26 @@ @@ -98,25 +97,26 @@
98 count 97 count
99 maxlength="200" 98 maxlength="200"
100 rows="4" 99 rows="4"
101 - @blur="() => $refs.workOrderFormRef.validateField('problemDesc')" 100 + @blur="() => workOrderFormRef.validateField('problemDesc')"
102 ></up-textarea> 101 ></up-textarea>
103 </up-form-item> 102 </up-form-item>
104 103
105 - <!-- 问题照片(移除完成照片相关代码) --> 104 + <!-- 问题照片(核心修复:绑定纯数组) -->
106 <up-form-item label="问题照片" prop="problemImgs" required> 105 <up-form-item label="问题照片" prop="problemImgs" required>
107 <up-upload 106 <up-upload
108 - :file-list="problemImgsList"  
109 - @after-read="(event) => uploadImgs(event, 'problemImgsList')"  
110 - @delete="(event) => deleteImg(event, 'problemImgsList')" 107 + :file-list="problemImgs.imgList"
  108 + @after-read="problemImgs.uploadImgs"
  109 + @delete="problemImgs.deleteImg"
111 multiple 110 multiple
112 - :max-count="3"  
113 - upload-text="选择问题照片" 111 + :max-count="problemImgs.uploadConfig.maxCount"
  112 + :upload-text="problemImgs.uploadConfig.uploadText"
  113 + :size-type="problemImgs.uploadConfig.sizeType"
114 ></up-upload> 114 ></up-upload>
115 </up-form-item> 115 </up-form-item>
116 116
117 <!-- 完成时间 --> 117 <!-- 完成时间 -->
118 <up-form-item 118 <up-form-item
119 - label="完成时间" 119 + label="希望完成时间"
120 prop="finishTime" 120 prop="finishTime"
121 @click="show=true;hideKeyboard()" 121 @click="show=true;hideKeyboard()"
122 > 122 >
@@ -163,402 +163,307 @@ @@ -163,402 +163,307 @@
163 </view> 163 </view>
164 </template> 164 </template>
165 165
166 -<script setup lang="ts">  
167 -import {ref} from 'vue'  
168 -import type {UniFormRef} from '@/uni_modules/uview-plus/types'  
169 -// 定义ref供选项式API使用  
170 -const workOrderFormRef = ref<UniFormRef>(null)  
171 -</script> 166 +<script setup>
  167 +import { ref, reactive, watch } from 'vue'
  168 +import { onReady, onShow } from '@dcloudio/uni-app';
  169 +import { useUploadImgs } from '@/common/utils/useUploadImgs' // 引入改造后的上传逻辑
  170 +import { getRoadListByLatLng } from '@/api/common'
  171 +import { createQuick } from '@/api/quick-order/quick-order'
  172 +import { timeFormat } from '@/uni_modules/uview-plus'
  173 +
  174 +// ========== 表单Ref ==========
  175 +const workOrderFormRef = ref(null)
  176 +
  177 +// ========== 公共上传逻辑复用 ==========
  178 +const problemImgs = useUploadImgs({
  179 + maxCount: 3,
  180 + uploadText: '选择问题照片',
  181 + sizeType: ['compressed'],
  182 + formRef: workOrderFormRef,
  183 + fieldName: 'problemImgs'
  184 +})
  185 +
  186 +// 核心修复:监听响应式数组变化,同步更新纯数组(解决u-upload不刷新问题)
  187 +watch(() => problemImgs.rawImgList.value, (newVal) => {
  188 + problemImgs.imgList = newVal
  189 +}, { deep: true })
  190 +
  191 +// ========== 页面状态 ==========
  192 +// 通用弹窗控制
  193 +const showActionSheet = ref(false)
  194 +// 当前弹窗配置
  195 +const currentActionSheetData = reactive({
  196 + type: '',
  197 + list: [],
  198 + title: ''
  199 +})
  200 +// 完成时间选择器控制
  201 +const show = ref(false)
  202 +const finishTime = ref(Date.now())
  203 +
  204 +// ========== 下拉列表数据 ==========
  205 +const roadNameList = ref([])
  206 +const orderNameList = ref([])
  207 +const pressingTypeList = ref([])
  208 +
  209 +// ========== 工单表单数据 ==========
  210 +const workOrderForm = reactive({
  211 + roadId: 0, // 道路ID
  212 + roadName: '', // 道路名称
  213 + workLocation: '', // 工单位置
  214 + orderName: '', // 工单名称
  215 + pressingType: 0, // 紧急程度值(提交接口用)
  216 + pressingTypeName: '', // 紧急程度名称(显示用)
  217 + problemDesc: '', // 情况描述
  218 + lat: 0, // 纬度
  219 + lon: 0, // 经度
  220 + finishTime: '', // 完成时间
  221 +})
  222 +
  223 +// ========== 表单校验规则 ==========
  224 +const workOrderFormRules = reactive({
  225 + workLocation: [
  226 + { type: 'string', required: true, message: '请选择工单位置', trigger: ['change', 'blur'] }
  227 + ],
  228 + roadName: [
  229 + { type: 'string', required: true, message: '请选择道路名称', trigger: ['change', 'blur'] }
  230 + ],
  231 + orderName: [
  232 + { type: 'string', required: true, message: '请选择工单名称', trigger: ['change', 'blur'] }
  233 + ],
  234 + pressingType: [
  235 + { type: 'number', required: true, message: '请选择紧急程度', trigger: ['change'] }
  236 + ],
  237 + problemDesc: [
  238 + { type: 'string', required: true, message: '请输入情况描述', trigger: ['change', 'blur'] },
  239 + { type: 'string', min: 3, max: 200, message: '情况描述需3-200字', trigger: ['change', 'blur'] }
  240 + ],
  241 + problemImgs: [problemImgs.imgValidateRule] // 复用上传校验规则
  242 +})
  243 +
  244 +// ========== 生命周期 ==========
  245 +onReady(() => {
  246 + // 设置表单校验规则
  247 + if (workOrderFormRef.value) {
  248 + workOrderFormRef.value.setRules(workOrderFormRules)
  249 + }
  250 + console.log('工单表单规则初始化完成')
  251 +})
  252 +
  253 +onShow(() => {
  254 + console.log(uni.$dict.getDictLabel('ai_image_status', 20))
  255 + console.log(uni.$dict.getDictSimpleList('work_name'))
  256 +
  257 + // 初始化工单名称列表
  258 + orderNameList.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('work_name'))
  259 + console.log('工单名称列表:', orderNameList.value)
  260 +
  261 + // 初始化紧急程度列表
  262 + pressingTypeList.value = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('workorder_pressing_type'))
  263 + console.log('紧急程度列表:', pressingTypeList.value)
  264 +})
  265 +
  266 +// ========== 方法定义 ==========
  267 +/**
  268 + * 打开通用下拉弹窗
  269 + */
  270 +const handleActionSheetOpen = (type) => {
  271 + // 道路名称需先校验工单位置是否选择
  272 + if (type === 'roadName' && !workOrderForm.workLocation) {
  273 + uni.showToast({ title: '请先选择工单位置', icon: 'none' })
  274 + return
  275 + }
172 276
173 -<script lang="ts">  
174 -import {getRoadListByLatLng} from '@/api/common'  
175 -import {uploadImages} from '@/common/utils/upload';  
176 -import {createQuick} from '@/api/quick-order/quick-order'  
177 -import { timeFormat } from '@/uni_modules/uview-plus' // 引入时间格式化工具  
178 -  
179 -export default {  
180 - data() {  
181 - return {  
182 - // 问题照片列表  
183 - problemImgsList: [],  
184 - // 通用弹窗控制  
185 - showActionSheet: false,  
186 - // 当前弹窗配置(类型 + 数据 + 标题)  
187 - currentActionSheetData: {  
188 - type: '', // roadName / orderName / pressingType  
189 - list: [], // 对应类型的选项列表  
190 - title: '' // 弹窗标题  
191 - },  
192 - // 完成时间选择器控制  
193 - show: false,  
194 - finishTime: Date.now(),  
195 - // 下拉列表数据  
196 - roadNameList: [],  
197 - orderNameList: [],  
198 - pressingTypeList: [],  
199 - // 工单表单数据  
200 - workOrderForm: {  
201 - roadId: 0, // 道路ID  
202 - roadName: '', // 道路名称  
203 - workLocation: '', // 工单位置  
204 - orderName: '', // 工单名称  
205 - pressingType: 0, // 紧急程度值(提交接口用)  
206 - pressingTypeName: '', // 紧急程度名称(显示用)  
207 - problemDesc: '', // 情况描述  
208 - lat: 0, // 纬度  
209 - lon: 0, // 经度  
210 - finishTime: '', // 完成时间  
211 - },  
212 -  
213 - // 表单校验规则  
214 - workOrderFormRules: {  
215 - workLocation: [  
216 - {type: 'string', required: true, message: '请选择工单位置', trigger: ['change', 'blur']}  
217 - ],  
218 - roadName: [  
219 - {type: 'string', required: true, message: '请选择道路名称', trigger: ['change', 'blur']}  
220 - ],  
221 - orderName: [  
222 - {type: 'string', required: true, message: '请选择工单名称', trigger: ['change', 'blur']}  
223 - ],  
224 - pressingType: [  
225 - {type: 'number', required: true, message: '请选择紧急程度', trigger: ['change']}  
226 - ],  
227 - problemDesc: [  
228 - {type: 'string', required: true, message: '请输入情况描述', trigger: ['change', 'blur']},  
229 - {type: 'string', min: 3, max: 200, message: '情况描述需3-200字', trigger: ['change', 'blur']}  
230 - ],  
231 - problemImgs: [  
232 - {  
233 - required: true,  
234 - message: '请上传问题照片',  
235 - trigger: 'change',  
236 - validator: (rule, value, callback) => {  
237 - const hasSuccessImg = this.problemImgsList.some(item => item.status === 'success')  
238 - hasSuccessImg ? callback() : callback(new Error('请上传至少1张问题照片'))  
239 - }  
240 - }  
241 - ]  
242 - } 277 + // 配置当前弹窗参数
  278 + const configMap = {
  279 + roadName: {
  280 + title: '请选择道路名称',
  281 + list: roadNameList.value
  282 + },
  283 + orderName: {
  284 + title: '请选择工单名称',
  285 + list: orderNameList.value
  286 + },
  287 + pressingType: {
  288 + title: '请选择紧急程度',
  289 + list: pressingTypeList.value
243 } 290 }
244 - },  
245 -  
246 - onReady() {  
247 - // 兼容微信小程序,通过setRules设置校验规则  
248 - this.$refs.workOrderFormRef.setRules(this.workOrderFormRules)  
249 - console.log('工单表单规则初始化完成')  
250 - },  
251 -  
252 - onShow(){  
253 - console.log(uni.$dict.getDictLabel('ai_image_status', 20))  
254 - console.log(uni.$dict.getDictSimpleList('work_name'))  
255 -  
256 - // 初始化工单名称列表  
257 - this.orderNameList = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('work_name'))  
258 - console.log('工单名称列表:', this.orderNameList)  
259 -  
260 - // 初始化紧急程度列表  
261 - this.pressingTypeList = uni.$dict.transformLabelValueToNameValue(uni.$dict.getDictSimpleList('workorder_pressing_type'))  
262 - console.log('紧急程度列表:', this.pressingTypeList)  
263 - },  
264 -  
265 - methods: {  
266 - /**  
267 - * 打开通用下拉弹窗  
268 - * @param type 弹窗类型:roadName / orderName / pressingType  
269 - */  
270 - handleActionSheetOpen(type) {  
271 - // 道路名称需先校验工单位置是否选择  
272 - if (type === 'roadName' && !this.workOrderForm.workLocation) {  
273 - uni.showToast({title: '请先选择工单位置', icon: 'none'})  
274 - return  
275 - } 291 + }
276 292
277 - // 配置当前弹窗参数  
278 - const configMap = {  
279 - roadName: {  
280 - title: '请选择道路名称',  
281 - list: this.roadNameList  
282 - },  
283 - orderName: {  
284 - title: '请选择工单名称',  
285 - list: this.orderNameList  
286 - },  
287 - pressingType: {  
288 - title: '请选择紧急程度',  
289 - list: this.pressingTypeList  
290 - }  
291 - } 293 + currentActionSheetData.type = type
  294 + currentActionSheetData.title = configMap[type].title
  295 + currentActionSheetData.list = configMap[type].list
  296 + showActionSheet.value = true
  297 +}
292 298
293 - this.currentActionSheetData = {  
294 - type,  
295 - title: configMap[type].title,  
296 - list: configMap[type].list  
297 - }  
298 - this.showActionSheet = true  
299 - }, 299 +/**
  300 + * 关闭通用下拉弹窗
  301 + */
  302 +const handleActionSheetClose = () => {
  303 + showActionSheet.value = false
  304 + // 重置当前弹窗配置
  305 + currentActionSheetData.type = ''
  306 + currentActionSheetData.list = []
  307 + currentActionSheetData.title = ''
  308 +}
300 309
301 - /**  
302 - * 关闭通用下拉弹窗  
303 - */  
304 - handleActionSheetClose() {  
305 - this.showActionSheet = false  
306 - // 重置当前弹窗配置(可选,防止数据残留)  
307 - this.currentActionSheetData = { type: '', list: [], title: '' }  
308 - }, 310 +/**
  311 + * 通用下拉弹窗选择事件
  312 + */
  313 +const handleActionSheetSelect = (e) => {
  314 + const { type } = currentActionSheetData
  315 + // 根据类型处理不同的选择逻辑
  316 + switch (type) {
  317 + case 'roadName':
  318 + workOrderForm.roadName = e.name
  319 + workOrderForm.roadId = e.code
  320 + workOrderFormRef.value?.validateField('roadName')
  321 + break
  322 + case 'orderName':
  323 + workOrderForm.orderName = e.name
  324 + workOrderFormRef.value?.validateField('orderName')
  325 + break
  326 + case 'pressingType':
  327 + workOrderForm.pressingType = Number(e.value)
  328 + workOrderForm.pressingTypeName = e.name
  329 + workOrderFormRef.value?.validateField('pressingType')
  330 + break
  331 + }
  332 + // 关闭弹窗
  333 + showActionSheet.value = false
  334 +}
309 335
310 - /**  
311 - * 通用下拉弹窗选择事件  
312 - * @param e 选择的项  
313 - */  
314 - handleActionSheetSelect(e) {  
315 - const { type } = this.currentActionSheetData  
316 - // 根据类型处理不同的选择逻辑  
317 - switch (type) {  
318 - case 'roadName':  
319 - this.workOrderForm.roadName = e.name  
320 - this.workOrderForm.roadId = e.code  
321 - this.$refs.workOrderFormRef.validateField('roadName')  
322 - break  
323 - case 'orderName':  
324 - this.workOrderForm.orderName = e.name  
325 - this.$refs.workOrderFormRef.validateField('orderName')  
326 - break  
327 - case 'pressingType':  
328 - this.workOrderForm.pressingType = Number(e.value)  
329 - this.workOrderForm.pressingTypeName = e.name  
330 - this.$refs.workOrderFormRef.validateField('pressingType')  
331 - break  
332 - }  
333 - // 关闭弹窗  
334 - this.showActionSheet = false  
335 - }, 336 +/**
  337 + * 返回上一页
  338 + */
  339 +const navigateBack = () => {
  340 + uni.navigateBack()
  341 +}
336 342
337 - /**  
338 - * 返回上一页  
339 - */  
340 - navigateBack() {  
341 - uni.navigateBack()  
342 - }, 343 +/**
  344 + * 选择工单位置
  345 + */
  346 +const chooseWorkLocation = () => {
  347 + uni.chooseLocation({
  348 + success: async (res) => {
  349 + workOrderForm.roadName = ''
  350 + workOrderForm.roadId = 0
  351 + roadNameList.value = []
343 352
344 - /**  
345 - * 删除图片  
346 - */  
347 - deleteImg(event, type) {  
348 - console.log('删除图片事件:', event, '类型:', type)  
349 - if (type === 'problemImgsList') {  
350 - this.problemImgsList.splice(event.index, 1)  
351 - this.$refs.workOrderFormRef.validateField('problemImgs')  
352 - }  
353 - uni.showToast({title: '图片删除成功', icon: 'success'})  
354 - }, 353 + workOrderForm.workLocation = res.name
  354 + workOrderForm.lat = res.latitude
  355 + workOrderForm.lon = res.longitude
355 356
356 - /**  
357 - * 上传图片  
358 - */  
359 - async uploadImgs(event, type) {  
360 - console.log('上传图片事件:', event, '类型:', type)  
361 - if (type !== 'problemImgsList') return  
362 -  
363 - const fileList = Array.isArray(event.file) ? event.file : [event.file]  
364 - const targetImgList = this.problemImgsList  
365 -  
366 - const filePaths = fileList.map(item => item.url)  
367 - const tempItems = fileList.map(item => ({  
368 - ...item,  
369 - status: 'uploading',  
370 - message: '上传中'  
371 - }))  
372 - const startIndex = targetImgList.length  
373 - targetImgList.push(...tempItems) 357 + workOrderFormRef.value?.validateField('workLocation')
  358 + workOrderFormRef.value?.validateField('roadName')
374 359
375 try { 360 try {
376 - const uploadResultUrls = await uploadImages({  
377 - filePaths: filePaths,  
378 - ignoreError: true  
379 - })  
380 - console.log('上传成功的URL列表:', uploadResultUrls)  
381 -  
382 - uploadResultUrls.forEach((url, index) => {  
383 - if (targetImgList[startIndex + index]) {  
384 - targetImgList.splice(startIndex + index, 1, {  
385 - ...fileList[index],  
386 - status: 'success',  
387 - message: '',  
388 - url: url  
389 - })  
390 - } 361 + uni.showLoading({ title: '获取道路名称中...' })
  362 + const roadRes = await getRoadListByLatLng({
  363 + companyCode: 'sls',
  364 + latitude: res.latitude,
  365 + longitude: res.longitude
391 }) 366 })
  367 + uni.hideLoading()
392 368
393 - if (uploadResultUrls.length < fileList.length) {  
394 - const failCount = fileList.length - uploadResultUrls.length  
395 - for (let i = uploadResultUrls.length; i < fileList.length; i++) {  
396 - if (targetImgList[startIndex + i]) {  
397 - targetImgList.splice(startIndex + i, 1, {  
398 - ...fileList[i],  
399 - status: 'failed',  
400 - message: '上传失败'  
401 - })  
402 - }  
403 - }  
404 - uni.showToast({title: `成功上传${uploadResultUrls.length}张,失败${failCount}张`, icon: 'none'}) 369 + if (Array.isArray(roadRes)) {
  370 + roadNameList.value = roadRes.map((item) => ({
  371 + name: item.roadName || '',
  372 + code: item.roadCode || '',
  373 + id: item.roadId || 0
  374 + }))
405 } else { 375 } else {
406 - uni.showToast({title: `成功上传${fileList.length}张图片`, icon: 'success'}) 376 + roadNameList.value = [{ name: '未查询到道路名称', code: '', id: 0 }]
  377 + uni.showToast({ title: '未查询到该位置的道路信息', icon: 'none' })
407 } 378 }
408 -  
409 - this.$refs.workOrderFormRef.validateField('problemImgs')  
410 } catch (err) { 379 } catch (err) {
411 - console.error('图片上传失败:', err)  
412 - for (let i = 0; i < fileList.length; i++) {  
413 - if (targetImgList[startIndex + i]) {  
414 - targetImgList.splice(startIndex + i, 1, {  
415 - ...fileList[i],  
416 - status: 'failed',  
417 - message: '上传失败'  
418 - })  
419 - }  
420 - }  
421 - uni.showToast({title: '图片上传失败,请重试', icon: 'none'})  
422 - this.$refs.workOrderFormRef.validateField('problemImgs') 380 + uni.hideLoading()
  381 + console.error('获取道路名称失败:', err)
  382 + uni.showToast({ title: '获取道路名称失败,请重试', icon: 'none' })
  383 + roadNameList.value = [{ name: '获取失败,请重新选择位置', code: '', id: 0 }]
423 } 384 }
424 }, 385 },
  386 + fail: (err) => {
  387 + console.error('选择位置失败:', err)
  388 + uni.showToast({ title: '选择位置失败:' + err.errMsg, icon: 'none' })
  389 + }
  390 + })
  391 +}
425 392
426 - /**  
427 - * 选择工单位置  
428 - */  
429 - chooseWorkLocation() {  
430 - let that = this  
431 - uni.chooseLocation({  
432 - success: async (res) => {  
433 - that.workOrderForm.roadName = ''  
434 - that.workOrderForm.roadId = 0  
435 - that.roadNameList = []  
436 -  
437 - that.workOrderForm.workLocation = res.name  
438 - that.workOrderForm.lat = res.latitude  
439 - that.workOrderForm.lon = res.longitude  
440 -  
441 - that.$refs.workOrderFormRef.validateField('workLocation')  
442 - that.$refs.workOrderFormRef.validateField('roadName')  
443 -  
444 - try {  
445 - uni.showLoading({title: '获取道路名称中...'})  
446 - const roadRes = await getRoadListByLatLng({  
447 - companyCode: 'sls',  
448 - latitude: res.latitude,  
449 - longitude: res.longitude  
450 - })  
451 - uni.hideLoading()  
452 -  
453 - if (Array.isArray(roadRes)) {  
454 - that.roadNameList = roadRes.map((item) => ({  
455 - name: item.roadName || '',  
456 - code: item.roadCode || '',  
457 - id: item.roadId || 0  
458 - }))  
459 - } else {  
460 - that.roadNameList = [{name: '未查询到道路名称', code: '', id: 0}]  
461 - uni.showToast({title: '未查询到该位置的道路信息', icon: 'none'})  
462 - }  
463 - } catch (err) {  
464 - uni.hideLoading()  
465 - console.error('获取道路名称失败:', err)  
466 - uni.showToast({title: '获取道路名称失败,请重试', icon: 'none'})  
467 - that.roadNameList = [{name: '获取失败,请重新选择位置', code: '', id: 0}]  
468 - }  
469 - },  
470 - fail: (err) => {  
471 - console.error('选择位置失败:', err)  
472 - uni.showToast({title: '选择位置失败:' + err.errMsg, icon: 'none'})  
473 - }  
474 - })  
475 - },  
476 -  
477 - /**  
478 - * 完成时间确认  
479 - */  
480 - finishTimeConfirm(e) {  
481 - console.log('选择的完成时间:', e)  
482 - this.workOrderForm.finishTime = timeFormat(e.value, 'yyyy-mm-dd hh:MM:ss')  
483 - this.show = false  
484 - },  
485 -  
486 - /**  
487 - * 隐藏键盘  
488 - */  
489 - hideKeyboard() {  
490 - uni.hideKeyboard()  
491 - },  
492 -  
493 - /**  
494 - * 提取图片URL数组  
495 - */  
496 - getImgUrlList(imgList) {  
497 - return imgList.filter(item => item.status === 'success').map(item => item.url)  
498 - }, 393 +/**
  394 + * 完成时间确认
  395 + */
  396 +const finishTimeConfirm = (e) => {
  397 + console.log('选择的完成时间:', e)
  398 + workOrderForm.finishTime = timeFormat(e.value, 'yyyy-mm-dd hh:MM:ss')
  399 + show.value = false
  400 +}
499 401
500 - /**  
501 - * 提交工单  
502 - */  
503 - async submitWorkOrder() {  
504 - try {  
505 - // 先执行表单校验  
506 - await this.$refs.workOrderFormRef.validate()  
507 -  
508 - const submitData = {  
509 - roadId: this.workOrderForm.roadId,  
510 - roadName: this.workOrderForm.roadName,  
511 - imgs: this.getImgUrlList(this.problemImgsList),  
512 - remark: this.workOrderForm.problemDesc,  
513 - latLonType: 2,  
514 - lat: this.workOrderForm.lat,  
515 - lon: this.workOrderForm.lon,  
516 - lonLatAddress: this.workOrderForm.workLocation,  
517 - pressingType: this.workOrderForm.pressingType,  
518 - orderName: this.workOrderForm.orderName,  
519 - finishTime: this.workOrderForm.finishTime,  
520 - sourceId: 1,  
521 - sourceName: '园林',  
522 - thirdWorkNo: '',  
523 - busiLine: 'yl'  
524 - } 402 +/**
  403 + * 隐藏键盘
  404 + */
  405 +const hideKeyboard = () => {
  406 + uni.hideKeyboard()
  407 +}
525 408
526 - // 显示加载中  
527 - uni.showLoading({title: '提交中...'}) 409 +/**
  410 + * 提交工单
  411 + */
  412 +const submitWorkOrder = async () => {
  413 + try {
  414 + // 先执行表单校验
  415 + await workOrderFormRef.value.validate()
  416 +
  417 + const submitData = {
  418 + roadId: workOrderForm.roadId,
  419 + roadName: workOrderForm.roadName,
  420 + imgs: problemImgs.getSuccessImgUrls(), // 复用上传逻辑的URL获取方法
  421 + remark: workOrderForm.problemDesc,
  422 + latLonType: 2,
  423 + lat: workOrderForm.lat,
  424 + lon: workOrderForm.lon,
  425 + lonLatAddress: workOrderForm.workLocation,
  426 + pressingType: workOrderForm.pressingType,
  427 + orderName: workOrderForm.orderName,
  428 + finishTime: workOrderForm.finishTime,
  429 + sourceId: 1,
  430 + sourceName: '园林',
  431 + thirdWorkNo: '',
  432 + busiLine: 'yl'
  433 + }
528 434
529 - // 调用提交接口  
530 - const res = await createQuick(submitData) 435 + // 显示加载中
  436 + uni.showLoading({ title: '提交中...' })
531 437
532 - uni.hideLoading()  
533 - uni.showToast({  
534 - title: '工单提交成功',  
535 - icon: 'success',  
536 - duration: 1000  
537 - }) 438 + // 调用提交接口
  439 + const res = await createQuick(submitData)
538 440
539 - // 延迟跳转  
540 - setTimeout(() => {  
541 - uni.redirectTo({  
542 - url: '/pages-sub/daily/quick-order/index'  
543 - })  
544 - }, 1000)  
545 - } catch (error) {  
546 - // 隐藏加载框  
547 - uni.hideLoading() 441 + uni.hideLoading()
  442 + uni.showToast({
  443 + title: '工单提交成功',
  444 + icon: 'success',
  445 + duration: 1000
  446 + })
548 447
549 - // 区分是表单校验失败还是接口调用失败  
550 - if (Array.isArray(error)) {  
551 - // 表单校验失败(无需额外提示,uView会自动提示)  
552 - } else {  
553 - // 接口调用失败  
554 - console.error('工单提交失败:', error)  
555 - uni.showToast({  
556 - title: '提交失败,请重试',  
557 - icon: 'none',  
558 - duration: 2000  
559 - })  
560 - }  
561 - } 448 + // 延迟跳转
  449 + setTimeout(() => {
  450 + uni.redirectTo({
  451 + url: '/pages-sub/daily/quick-order/index'
  452 + })
  453 + }, 1000)
  454 + } catch (error) {
  455 + // 隐藏加载框
  456 + uni.hideLoading()
  457 +
  458 + // 区分是表单校验失败还是接口调用失败
  459 + if (!Array.isArray(error)) {
  460 + // 接口调用失败
  461 + console.error('工单提交失败:', error)
  462 + uni.showToast({
  463 + title: '提交失败,请重试',
  464 + icon: 'none',
  465 + duration: 2000
  466 + })
562 } 467 }
563 } 468 }
564 } 469 }
@@ -566,8 +471,9 @@ export default { @@ -566,8 +471,9 @@ export default {
566 471
567 <style lang="scss" scoped> 472 <style lang="scss" scoped>
568 // 全局页面样式 473 // 全局页面样式
569 -.u-page { 474 +.page-container {
570 min-height: 100vh; 475 min-height: 100vh;
  476 + padding-bottom: 100rpx; // 给底部按钮留空间
571 } 477 }
572 478
573 // 工单表单内容容器 479 // 工单表单内容容器
@@ -575,5 +481,4 @@ export default { @@ -575,5 +481,4 @@ export default {
575 background: #fff; 481 background: #fff;
576 } 482 }
577 483
578 -  
579 </style> 484 </style>
580 \ No newline at end of file 485 \ No newline at end of file