Commit c7df828a939478c246d15dd8583e078840820353

Authored by 刘淇
1 parent 8d0834be

快速工单 样式优化

... ... @@ -123,4 +123,9 @@ page {
123 123 padding-left: 15px;
124 124 padding-right: 15px;
125 125 }
  126 +
  127 +.common-text-color{
  128 + font-size: 14px;
  129 + color: #606266
  130 +}
126 131 </style>
127 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 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 6 * @param config 上传配置
25 7 * @returns 上传相关方法和状态
26 8 */
27   -export function useUploadImgs(config: UploadImgConfig) {
  9 +export function useUploadImgs(config) {
28 10 // 默认配置
29 11 const defaultConfig = {
30 12 maxCount: 3,
31 13 uploadText: '选择图片',
32   - sizeType: ['compressed'] as UniApp.UploadFileOption['sizeType'],
  14 + sizeType: ['compressed'],
33 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 31 defaultConfig.formRef.value?.validateField(defaultConfig.fieldName)
46 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 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 54 uni.showToast({ title: `最多只能上传${defaultConfig.maxCount}张图片`, icon: 'none' })
59 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 67 const startIndex = targetImgList.length
69 68 targetImgList.push(...tempItems)
... ... @@ -78,29 +77,32 @@ export function useUploadImgs(config: UploadImgConfig) {
78 77 uploadResultUrls.forEach((url, index) => {
79 78 if (targetImgList[startIndex + index]) {
80 79 targetImgList.splice(startIndex + index, 1, {
81   - ...fileList[index],
82   - status: 'success' as const,
  80 + url: url,
  81 + status: 'success',
83 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 93 if (targetImgList[startIndex + i]) {
94 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 103 uni.showToast({ title: `成功上传${uploadResultUrls.length}张,失败${failCount}张`, icon: 'none' })
102 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 110 } catch (err) {
109 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 114 if (targetImgList[startIndex + i]) {
113 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 141 required: true,
138 142 message: `请上传${defaultConfig.uploadText.replace('选择', '')}`,
139 143 trigger: 'change',
140   - validator: (rule: any, value: any, callback: (error?: Error) => void) => {
  144 + validator: (rule, value, callback) => {
141 145 const hasSuccessImg = imgList.value.some(item => item.status === 'success')
142 146 hasSuccessImg ? callback() : callback(new Error(`请上传至少1张${defaultConfig.uploadText.replace('选择', '')}`))
143 147 }
144 148 }
145 149  
146 150 return {
147   - imgList,
  151 + imgList: imgList.value, // 核心修复:返回纯数组(解除响应式代理)
  152 + rawImgList: imgList, // 保留响应式引用(内部使用)
148 153 uploadImgs,
149 154 deleteImg,
150 155 getSuccessImgUrls,
... ...
pages-sub/daily/maintain-manage/finish-plan-detail.vue
... ... @@ -71,7 +71,7 @@
71 71 <view style="min-width: 200rpx">巡查描述</view>
72 72 </template>
73 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 75 </template>
76 76 </up-cell>
77 77  
... ...
pages-sub/daily/patrol-manage/finish-plan-detail.vue
... ... @@ -72,7 +72,7 @@
72 72 <view style="min-width: 200rpx">巡查描述</view>
73 73 </template>
74 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 76 </template>
77 77 </up-cell>
78 78  
... ...
pages-sub/daily/quick-order/add-order.vue
1 1 <template>
2   - <view class="u-page">
  2 + <view class="page-container">
3 3 <!-- 核心:将所有 up-form-item 包裹在同一个 up-form 内 -->
4 4 <view class="work-order-form-content commonPageLRpadding">
5 5 <up-form
... ... @@ -35,14 +35,14 @@
35 35 >
36 36 <up-input
37 37 v-model="workOrderForm.roadName"
38   - disabled
  38 + readonly
39 39 disabled-color="#ffffff"
40 40 placeholder="请先选择工单位置"
41 41 border="none"
42   - :placeholder-style="workOrderForm.workLocation ? '' : 'color:#999;'"
  42 +
43 43 ></up-input>
44 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 46 </template>
47 47 </up-form-item>
48 48  
... ... @@ -74,7 +74,7 @@
74 74 >
75 75 <up-textarea
76 76 placeholder="请输入情况描述(最多200字)"
77   - v-model="workOrderForm.problemDesc"
  77 + v-model.trim="workOrderForm.problemDesc"
78 78 count
79 79 maxlength="200"
80 80 rows="4"
... ... @@ -111,7 +111,7 @@
111 111 <up-form-item label="处理结果" prop="handleResult" class="mt-20">
112 112 <up-textarea
113 113 placeholder="请输入处理结果描述(最多200字,选填)"
114   - v-model="workOrderForm.handleResult"
  114 + v-model.trim="workOrderForm.handleResult"
115 115 count
116 116 maxlength="200"
117 117 rows="4"
... ... @@ -206,7 +206,7 @@ export default {
206 206 ],
207 207 problemDesc: [
208 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 211 problemImgs: [
212 212 {
... ... @@ -447,8 +447,8 @@ export default {
447 447 roadName: this.workOrderForm.roadName,
448 448 imgs: this.getImgUrlList(this.problemImgsList),
449 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 452 latLonType: 2,
453 453 lat: this.workOrderForm.lat,
454 454 lon: this.workOrderForm.lon,
... ... @@ -509,13 +509,14 @@ export default {
509 509  
510 510 <style lang="scss" scoped>
511 511 // 全局页面样式
512   -.u-page {
  512 +.page-container {
513 513 min-height: 100vh;
514 514 }
515 515  
516 516 // 工单表单内容容器
517 517 .work-order-form-content {
518 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 33 <view style="min-width: 200rpx">工单位置</view>
34 34 </template>
35 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 37 </template>
38 38  
39 39 </up-cell>
... ... @@ -54,7 +54,7 @@
54 54 <view style="min-width: 200rpx">情况描述</view>
55 55 </template>
56 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 58 </template>
59 59 </up-cell>
60 60  
... ... @@ -101,7 +101,7 @@
101 101 <view style="min-width: 200rpx">处理结果</view>
102 102 </template>
103 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 105 </template>
106 106 </up-cell>
107 107 </up-cell-group>
... ...
pages-sub/problem/work-order-manage/add-order.vue
1 1 <template>
2   - <view class="u-page">
3   - <!-- 核心:将所有 up-form-item 包裹在同一个 up-form 内 -->
  2 + <view class="page-container">
4 3 <view class="work-order-form-content commonPageLRpadding">
5 4 <up-form
6 5 label-position="left"
... ... @@ -25,7 +24,7 @@
25 24 ></up-input>
26 25 </up-form-item>
27 26  
28   - <!-- 2. 道路名称(下拉框:位置选择后才可点击) -->
  27 + <!-- 2. 道路名称(下拉框) -->
29 28 <up-form-item
30 29 label="道路名称"
31 30 prop="roadName"
... ... @@ -98,25 +97,26 @@
98 97 count
99 98 maxlength="200"
100 99 rows="4"
101   - @blur="() => $refs.workOrderFormRef.validateField('problemDesc')"
  100 + @blur="() => workOrderFormRef.validateField('problemDesc')"
102 101 ></up-textarea>
103 102 </up-form-item>
104 103  
105   - <!-- 问题照片(移除完成照片相关代码) -->
  104 + <!-- 问题照片(核心修复:绑定纯数组) -->
106 105 <up-form-item label="问题照片" prop="problemImgs" required>
107 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 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 114 ></up-upload>
115 115 </up-form-item>
116 116  
117 117 <!-- 完成时间 -->
118 118 <up-form-item
119   - label="完成时间"
  119 + label="希望完成时间"
120 120 prop="finishTime"
121 121 @click="show=true;hideKeyboard()"
122 122 >
... ... @@ -163,402 +163,307 @@
163 163 </view>
164 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 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 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 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 471  
567 472 <style lang="scss" scoped>
568 473 // 全局页面样式
569   -.u-page {
  474 +.page-container {
570 475 min-height: 100vh;
  476 + padding-bottom: 100rpx; // 给底部按钮留空间
571 477 }
572 478  
573 479 // 工单表单内容容器
... ... @@ -575,5 +481,4 @@ export default {
575 481 background: #fff;
576 482 }
577 483  
578   -
579 484 </style>
580 485 \ No newline at end of file
... ...