Commit 4c54ad5d917083f7a2024f12b9f96f80337e5a95

Authored by 刘淇
1 parent 2f1e3176

转工单 选择是 传紧急程度和希望完成时间

api/regional-order-manage/regional-order-manage.js 0 → 100644
common/utils/common.js
@@ -55,4 +55,111 @@ export const buzStatusMap = { @@ -55,4 +55,111 @@ export const buzStatusMap = {
55 "230" : '养护组长验收不通过', 55 "230" : '养护组长验收不通过',
56 "140" : '巡查员验收通过', 56 "140" : '巡查员验收通过',
57 "240" : '巡查员验收不通过', 57 "240" : '巡查员验收不通过',
58 -}  
59 \ No newline at end of file 58 \ No newline at end of file
  59 +}
  60 +
  61 +
  62 +/**
  63 + * 计算两个时间的时间差,返回格式化字符串(天/小时/分钟/秒,按需显示,无无效单位)
  64 + * @param {string | Date | number} startTime - 开始时间(时间字符串/Date对象/10位/13位时间戳)
  65 + * @param {string | Date | number} endTime - 结束时间(时间字符串/Date对象/10位/13位时间戳)
  66 + * @returns {string} - 格式化时间差字符串(如:3天5小时2分钟30秒、3小时2分钟30秒、2分钟30秒、30秒)
  67 + */
  68 +export const calculateFormatTimeDiff = (startTime, endTime) => {
  69 + // 辅助函数:将任意合法时间格式转换为13位毫秒级时间戳
  70 + const to13BitTimestamp = (time) => {
  71 + let timestamp = 0;
  72 +
  73 + // 情况1:数字类型(时间戳)
  74 + if (typeof time === 'number') {
  75 + const timeStr = time.toString();
  76 + if (timeStr.length === 10) {
  77 + timestamp = time * 1000; // 10位秒级转13位毫秒级
  78 + } else if (timeStr.length === 13) {
  79 + timestamp = time; // 13位毫秒级直接使用
  80 + } else {
  81 + console.error('非法时间戳:仅支持10位/13位数字', time);
  82 + return 0;
  83 + }
  84 + return timestamp;
  85 + }
  86 +
  87 + // 情况2:Date对象
  88 + if (time instanceof Date) {
  89 + timestamp = time.getTime();
  90 + if (isNaN(timestamp)) {
  91 + console.error('无效的Date对象', time);
  92 + return 0;
  93 + }
  94 + return timestamp;
  95 + }
  96 +
  97 + // 情况3:字符串类型
  98 + if (typeof time === 'string') {
  99 + const timeStr = time.trim().replace(/\//g, '-'); // 统一分隔符,兼容YYYY/MM/DD
  100 + const date = new Date(timeStr);
  101 + timestamp = date.getTime();
  102 + if (isNaN(timestamp)) {
  103 + console.error('无效的时间字符串', time);
  104 + return 0;
  105 + }
  106 + return timestamp;
  107 + }
  108 +
  109 + // 非法类型
  110 + console.error('不支持的时间类型:仅支持string/Date/number', time);
  111 + return 0;
  112 + };
  113 +
  114 + // 1. 统一转换为13位时间戳
  115 + const startTimestamp = to13BitTimestamp(startTime);
  116 + const endTimestamp = to13BitTimestamp(endTime);
  117 +
  118 + // 2. 校验时间戳有效性
  119 + if (startTimestamp === 0 || endTimestamp === 0) {
  120 + return '0秒';
  121 + }
  122 +
  123 + // 3. 计算总毫秒差(取绝对值,确保时间差为正数,不影响单位计算)
  124 + const totalMsDiff = Math.abs(endTimestamp - startTimestamp);
  125 +
  126 + // 4. 定义时间单位换算(毫秒)
  127 + const oneDayMs = 24 * 60 * 60 * 1000;
  128 + const oneHourMs = 60 * 60 * 1000;
  129 + const oneMinuteMs = 60 * 1000;
  130 + const oneSecondMs = 1000;
  131 +
  132 + // 5. 计算各单位的差值(向下取整,获取整数单位)
  133 + const days = Math.floor(totalMsDiff / oneDayMs);
  134 + const remainingMsAfterDay = totalMsDiff % oneDayMs; // 扣除天数后剩余的毫秒数
  135 +
  136 + const hours = Math.floor(remainingMsAfterDay / oneHourMs);
  137 + const remainingMsAfterHour = remainingMsAfterDay % oneHourMs; // 扣除小时后剩余的毫秒数
  138 +
  139 + const minutes = Math.floor(remainingMsAfterHour / oneMinuteMs);
  140 + const remainingMsAfterMinute = remainingMsAfterHour % oneMinuteMs; // 扣除分钟后剩余的毫秒数
  141 +
  142 + const seconds = Math.floor(remainingMsAfterMinute / oneSecondMs);
  143 +
  144 + // 6. 组装格式化字符串(按需添加单位,无无效单位)
  145 + const timeParts = [];
  146 + if (days > 0) {
  147 + timeParts.push(`${days}天`);
  148 + }
  149 + if (hours > 0 || (days > 0 && hours === 0)) { // 有天数时,小时即使为0也可保留(可选:删除 || 后的条件则不显示0小时)
  150 + timeParts.push(`${hours}小时`);
  151 + }
  152 + if (minutes > 0 || (timeParts.length > 0 && minutes === 0)) { // 有天/小时时,分钟即使为0也可保留(可选:删除 || 后的条件则不显示0分钟)
  153 + timeParts.push(`${minutes}分钟`);
  154 + }
  155 + // 秒数始终保留(即使为0,保证最后有一个有效单位)
  156 + timeParts.push(`${seconds}秒`);
  157 +
  158 + // 7. 过滤掉可能存在的「0单位」(可选优化:避免出现“0小时”等无效字段)
  159 + const validTimeParts = timeParts.filter(part => {
  160 + const num = parseInt(part);
  161 + return num > 0 || (timeParts.length === 1 && num === 0); // 仅当只有秒数时,允许0秒
  162 + });
  163 +
  164 + // 8. 拼接并返回结果
  165 + return validTimeParts.join('');
  166 +};
60 \ No newline at end of file 167 \ No newline at end of file
common/utils/useUploadImgs.js
@@ -17,6 +17,8 @@ export function useUploadImgs(config) { @@ -17,6 +17,8 @@ export function useUploadImgs(config) {
17 17
18 // 核心修复:初始化为纯数组,且格式适配u-upload 18 // 核心修复:初始化为纯数组,且格式适配u-upload
19 const imgList = ref([]) 19 const imgList = ref([])
  20 + // 确保 rawImgList 响应式对象的初始值也是空数组
  21 + // const rawImgList = ref([]);
20 22
21 /** 23 /**
22 * 新增:清空所有图片 24 * 新增:清空所有图片
pages-sub/daily/patrol-manage/add-patrol-record.vue
1 <template> 1 <template>
2 <view class="u-page"> 2 <view class="u-page">
3 - <!-- 核心:将所有 up-form-item 包裹在同一个 up-form 内 --> 3 + <!-- 原有模板内容不变,仅调整表单校验相关配置 -->
4 <view class="inspect-form-content commonPageLRpadding"> 4 <view class="inspect-form-content commonPageLRpadding">
5 <up-form 5 <up-form
6 label-position="left" 6 label-position="left"
7 :model="inspectForm" 7 :model="inspectForm"
8 ref="inspectFormRef" 8 ref="inspectFormRef"
9 - labelWidth="140rpx" 9 + labelWidth="190rpx"
10 > 10 >
11 - <!-- 1. 巡查描述(文本域) --> 11 + <!-- 1. 巡查描述 -->
12 <up-form-item 12 <up-form-item
13 label="巡查描述" 13 label="巡查描述"
14 prop="content" 14 prop="content"
@@ -41,11 +41,12 @@ @@ -41,11 +41,12 @@
41 upload-text="选择图片" 41 upload-text="选择图片"
42 del-color="#ff4d4f" 42 del-color="#ff4d4f"
43 class="upload-wrap" 43 class="upload-wrap"
  44 + width="70"
  45 + height="70"
44 ></up-upload> 46 ></up-upload>
45 - <!-- <view class="tips">(最少1张,最多3张)</view>-->  
46 </up-form-item> 47 </up-form-item>
47 48
48 - <!-- 3. 转为工单(单选框) --> 49 + <!-- 3. 转为工单 -->
49 <up-form-item 50 <up-form-item
50 label="转为工单" 51 label="转为工单"
51 prop="isWorkOrder" 52 prop="isWorkOrder"
@@ -57,6 +58,7 @@ @@ -57,6 +58,7 @@
57 active-color="#1989fa" 58 active-color="#1989fa"
58 direction="row" 59 direction="row"
59 class="radio-group" 60 class="radio-group"
  61 + @change="handleWorkOrderChange"
60 > 62 >
61 <up-radio 63 <up-radio
62 :custom-style="{marginRight: '40rpx'}" 64 :custom-style="{marginRight: '40rpx'}"
@@ -70,6 +72,51 @@ @@ -70,6 +72,51 @@
70 </up-radio-group> 72 </up-radio-group>
71 </up-form-item> 73 </up-form-item>
72 74
  75 + <!-- 紧急程度(仍为必填) -->
  76 +<!-- v-if="inspectForm.isWorkOrder === '2'"-->
  77 + <up-form-item
  78 + v-if="inspectForm.isWorkOrder === '2'"
  79 + label="紧急程度"
  80 + prop="pressingTypeName"
  81 + border-bottom
  82 + required
  83 + class="form-item"
  84 + @click="handleActionSheetOpen('pressingType')"
  85 + >
  86 + <up-input
  87 + v-model="inspectForm.pressingTypeName"
  88 + disabled
  89 + disabled-color="#ffffff"
  90 + placeholder="请选择紧急程度"
  91 + border="none"
  92 +
  93 + ></up-input>
  94 + <template #right>
  95 + <up-icon name="arrow-right" size="16"></up-icon>
  96 + </template>
  97 + </up-form-item>
  98 +
  99 + <!-- 希望完成时间(移除 required 属性,改为非必填) -->
  100 +<!-- v-if="inspectForm.isWorkOrder === '2'"-->
  101 + <up-form-item
  102 + v-if="inspectForm.isWorkOrder === '2'"
  103 + label="希望完成时间"
  104 + prop="expectedFinishDate"
  105 + class="form-item"
  106 + @click="show = true; uni.hideKeyboard()"
  107 + >
  108 + <up-input
  109 + v-model="inspectForm.expectedFinishDate"
  110 + border="none"
  111 + readonly
  112 + placeholder="点击选择时间"
  113 +
  114 + ></up-input>
  115 + <template #right>
  116 + <up-icon name="arrow-right" size="16"></up-icon>
  117 + </template>
  118 + </up-form-item>
  119 +
73 </up-form> 120 </up-form>
74 </view> 121 </view>
75 122
@@ -82,38 +129,59 @@ @@ -82,38 +129,59 @@
82 :style="{ width: '100%', height: '88rpx', fontSize: '32rpx', borderRadius: 0 }" 129 :style="{ width: '100%', height: '88rpx', fontSize: '32rpx', borderRadius: 0 }"
83 ></up-button> 130 ></up-button>
84 </view> 131 </view>
  132 +
  133 + <!-- 紧急程度下拉弹窗 -->
  134 + <up-action-sheet
  135 + :show="showActionSheet"
  136 + :actions="pressingTypeList"
  137 + title="请选择紧急程度"
  138 + @close="handleActionSheetClose"
  139 + @select="handlePressingTypeSelect"
  140 + ></up-action-sheet>
  141 +
  142 + <!-- 完成时间选择器 -->
  143 + <up-datetime-picker
  144 + :show="show"
  145 + v-model="datetimeValue"
  146 + mode="datetime"
  147 + :min-date="new Date()"
  148 + @cancel="show = false"
  149 + @confirm="expectedFinishDateConfirm"
  150 + ></up-datetime-picker>
85 </view> 151 </view>
86 </template> 152 </template>
87 153
88 <script setup lang="ts"> 154 <script setup lang="ts">
89 import { ref } from 'vue' 155 import { ref } from 'vue'
90 import type { UniFormRef } from '@/uni_modules/uview-plus/types' 156 import type { UniFormRef } from '@/uni_modules/uview-plus/types'
91 -// 定义ref供选项式API使用  
92 const inspectFormRef = ref<UniFormRef>(null) 157 const inspectFormRef = ref<UniFormRef>(null)
93 </script> 158 </script>
94 159
95 <script lang="ts"> 160 <script lang="ts">
96 import { uploadImages } from '@/common/utils/upload'; 161 import { uploadImages } from '@/common/utils/upload';
97 import { inspectionCreate } from "@/api/patrol-manage/patrol-plan"; 162 import { inspectionCreate } from "@/api/patrol-manage/patrol-plan";
98 - 163 +import { timeFormat } from '@/uni_modules/uview-plus'
99 164
100 export default { 165 export default {
101 data() { 166 data() {
102 return { 167 return {
103 - // 图片列表  
104 imagesList: [], 168 imagesList: [],
105 - // 单选列表  
106 radioList: [ 169 radioList: [
107 { label: '否', value: '1' }, 170 { label: '否', value: '1' },
108 { label: '是', value: '2' } 171 { label: '是', value: '2' }
109 ], 172 ],
110 - // 巡查表单数据 173 + pressingTypeList: [],
111 inspectForm: { 174 inspectForm: {
112 - content: '', // 巡查描述  
113 - isWorkOrder: '1' // 是否转为工单 1:否(默认) 2:是 175 + content: '',
  176 + isWorkOrder: '1',
  177 + pressingTypeName: '',
  178 + pressingType: '',
  179 + expectedFinishDate: '' // 非必填,可空
114 }, 180 },
115 - paramsOptins:{},//接受参数  
116 - // 表单校验规则 181 + paramsOptins: {},
  182 + showActionSheet: false,
  183 + show: false,
  184 + datetimeValue: Date.now(),
117 inspectFormRules: { 185 inspectFormRules: {
118 images: [ 186 images: [
119 { 187 {
@@ -121,7 +189,6 @@ export default { @@ -121,7 +189,6 @@ export default {
121 message: '请上传图片', 189 message: '请上传图片',
122 trigger: 'change', 190 trigger: 'change',
123 validator: (rule, value, callback) => { 191 validator: (rule, value, callback) => {
124 - // 自定义校验规则:检查是否有成功上传的图片  
125 const hasSuccessImg = this.imagesList.some(item => item.status === 'success') 192 const hasSuccessImg = this.imagesList.some(item => item.status === 'success')
126 const imgCount = this.imagesList.filter(item => item.status === 'success').length 193 const imgCount = this.imagesList.filter(item => item.status === 'success').length
127 194
@@ -135,42 +202,104 @@ export default { @@ -135,42 +202,104 @@ export default {
135 } 202 }
136 } 203 }
137 ], 204 ],
138 -  
139 content: [ 205 content: [
140 { type: 'string', required: true, message: '请输入工单描述', trigger: ['change'] } 206 { type: 'string', required: true, message: '请输入工单描述', trigger: ['change'] }
141 ], 207 ],
142 isWorkOrder: [ 208 isWorkOrder: [
143 { type: 'string', required: true, message: '请选择是否转为工单', trigger: ['change'] } 209 { type: 'string', required: true, message: '请选择是否转为工单', trigger: ['change'] }
  210 + ],
  211 + pressingTypeName: [
  212 + {
  213 + required: true,
  214 + message: '请选择紧急程度',
  215 + trigger: 'change',
  216 + validator: (rule, value, callback) => {
  217 + if (this.inspectForm.isWorkOrder === '2' && !value) {
  218 + callback(new Error('请选择紧急程度'))
  219 + } else {
  220 + callback()
  221 + }
  222 + }
  223 + }
  224 + ],
  225 + // 关键修改:希望完成时间 移除必填校验,或改为非必填
  226 + expectedFinishDate: [
  227 + {
  228 + // 移除 required: true,改为非必填
  229 + message: '请选择有效的希望完成时间', // 仅当有值时,校验格式(可选)
  230 + trigger: 'change',
  231 + validator: (rule, value, callback) => {
  232 + // 只有当字段有值时,才校验时间格式是否有效
  233 + if (value && !/^\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}$/.test(value)) {
  234 + callback(new Error('请选择有效的希望完成时间'))
  235 + } else {
  236 + callback() // 无值时直接通过校验
  237 + }
  238 + }
  239 + }
144 ] 240 ]
145 } 241 }
146 } 242 }
147 }, 243 },
148 onLoad(option){ 244 onLoad(option){
149 - console.log(option)  
150 this.paramsOptins = option 245 this.paramsOptins = option
  246 + this.getPressingTypeDict()
151 }, 247 },
152 onReady() { 248 onReady() {
153 - // 兼容微信小程序,通过setRules设置校验规则  
154 this.$refs.inspectFormRef.setRules(this.inspectFormRules) 249 this.$refs.inspectFormRef.setRules(this.inspectFormRules)
155 - console.log('巡查表单规则初始化完成')  
156 }, 250 },
157 methods: { 251 methods: {
158 - /**  
159 - * 删除图片  
160 - */ 252 + getPressingTypeDict() {
  253 + const dictList = uni.$dict.getDictSimpleList('workorder_pressing_type')
  254 + this.pressingTypeList = uni.$dict.transformLabelValueToNameValue(dictList)
  255 + console.log(this.pressingTypeList)
  256 + },
  257 +
  258 + handleWorkOrderChange() {
  259 + if (this.inspectForm.isWorkOrder === '1') {
  260 + this.inspectForm.pressingTypeName = ''
  261 + this.inspectForm.pressingType = ''
  262 + this.inspectForm.expectedFinishDate = '' // 清空非必填字段
  263 + this.$refs.inspectFormRef.validateField(['pressingTypeName', 'expectedFinishDate'])
  264 + }
  265 + },
  266 +
  267 +
  268 + handleActionSheetOpen(type) {
  269 + uni.hideKeyboard()
  270 + console.log('showActionSheet')
  271 + if (type === 'pressingType') {
  272 + console.log('showActionSheet')
  273 + this.showActionSheet = true
  274 + }
  275 + uni.hideKeyboard()
  276 + },
  277 +
  278 + handleActionSheetClose() {
  279 + this.showActionSheet = false
  280 + },
  281 +
  282 + handlePressingTypeSelect(e) {
  283 + console.log(e)
  284 + this.inspectForm.pressingTypeName = e.name
  285 + this.inspectForm.pressingType = e.value
  286 + this.showActionSheet = false
  287 + this.$refs.inspectFormRef.validateField('pressingTypeName')
  288 + },
  289 +
  290 + expectedFinishDateConfirm(e) {
  291 + this.inspectForm.expectedFinishDate = timeFormat(e.value, 'yyyy-mm-dd hh:MM:ss')
  292 + this.show = false
  293 + this.$refs.inspectFormRef.validateField('expectedFinishDate')
  294 + },
  295 +
161 deleteImg(event) { 296 deleteImg(event) {
162 - console.log('删除图片事件:', event)  
163 this.imagesList.splice(event.index, 1) 297 this.imagesList.splice(event.index, 1)
164 - // 删除图片后重新校验图片字段  
165 this.$refs.inspectFormRef.validateField('images') 298 this.$refs.inspectFormRef.validateField('images')
166 uni.showToast({ title: '图片删除成功', icon: 'success' }) 299 uni.showToast({ title: '图片删除成功', icon: 'success' })
167 }, 300 },
168 301
169 - /**  
170 - * 上传图片  
171 - */  
172 async uploadImgs(event) { 302 async uploadImgs(event) {
173 - console.log('上传图片事件:', event)  
174 const fileList = Array.isArray(event.file) ? event.file : [event.file] 303 const fileList = Array.isArray(event.file) ? event.file : [event.file]
175 const targetImgList = this.imagesList 304 const targetImgList = this.imagesList
176 305
@@ -188,7 +317,6 @@ export default { @@ -188,7 +317,6 @@ export default {
188 filePaths: filePaths, 317 filePaths: filePaths,
189 ignoreError: true 318 ignoreError: true
190 }) 319 })
191 - console.log('上传成功的URL列表:', uploadResultUrls)  
192 320
193 uploadResultUrls.forEach((url, index) => { 321 uploadResultUrls.forEach((url, index) => {
194 if (targetImgList[startIndex + index]) { 322 if (targetImgList[startIndex + index]) {
@@ -203,21 +331,11 @@ export default { @@ -203,21 +331,11 @@ export default {
203 331
204 if (uploadResultUrls.length < fileList.length) { 332 if (uploadResultUrls.length < fileList.length) {
205 const failCount = fileList.length - uploadResultUrls.length 333 const failCount = fileList.length - uploadResultUrls.length
206 - for (let i = uploadResultUrls.length; i < fileList.length; i++) {  
207 - if (targetImgList[startIndex + i]) {  
208 - targetImgList.splice(startIndex + i, 1, {  
209 - ...fileList[i],  
210 - status: 'failed',  
211 - message: '上传失败'  
212 - })  
213 - }  
214 - }  
215 uni.showToast({ title: `成功上传${uploadResultUrls.length}张,失败${failCount}张`, icon: 'none' }) 334 uni.showToast({ title: `成功上传${uploadResultUrls.length}张,失败${failCount}张`, icon: 'none' })
216 } else { 335 } else {
217 uni.showToast({ title: `成功上传${fileList.length}张图片`, icon: 'success' }) 336 uni.showToast({ title: `成功上传${fileList.length}张图片`, icon: 'success' })
218 } 337 }
219 338
220 - // 上传完成后重新校验图片字段  
221 this.$refs.inspectFormRef.validateField('images') 339 this.$refs.inspectFormRef.validateField('images')
222 } catch (err) { 340 } catch (err) {
223 console.error('图片上传失败:', err) 341 console.error('图片上传失败:', err)
@@ -231,55 +349,48 @@ export default { @@ -231,55 +349,48 @@ export default {
231 } 349 }
232 } 350 }
233 uni.showToast({ title: '图片上传失败,请重试', icon: 'none' }) 351 uni.showToast({ title: '图片上传失败,请重试', icon: 'none' })
234 -  
235 - // 上传失败后重新校验图片字段  
236 this.$refs.inspectFormRef.validateField('images') 352 this.$refs.inspectFormRef.validateField('images')
237 } 353 }
238 }, 354 },
239 355
240 - /**  
241 - * 处理图片超出数量限制  
242 - */  
243 handleExceed() { 356 handleExceed() {
244 uni.showToast({ title: '最多只能上传3张图片', icon: 'none' }) 357 uni.showToast({ title: '最多只能上传3张图片', icon: 'none' })
245 }, 358 },
246 359
247 - /**  
248 - * 提取图片URL数组  
249 - */  
250 getImgUrlList(imgList) { 360 getImgUrlList(imgList) {
251 return imgList.filter(item => item.status === 'success').map(item => item.url) 361 return imgList.filter(item => item.status === 'success').map(item => item.url)
252 }, 362 },
253 363
254 - /**  
255 - * 提交巡查表单  
256 - */  
257 async submitInspect() { 364 async submitInspect() {
258 console.log('当前选择是否转为工单:', this.inspectForm.isWorkOrder) 365 console.log('当前选择是否转为工单:', this.inspectForm.isWorkOrder)
259 try { 366 try {
260 - // 先执行表单校验  
261 await this.$refs.inspectFormRef.validate() 367 await this.$refs.inspectFormRef.validate()
262 console.log(this.imagesList) 368 console.log(this.imagesList)
263 - // 构造提交数据  
264 - const submitData = {  
265 - // content: this.inspectForm.content,  
266 - // images: this.getImgUrlList(this.imagesList),  
267 - // isWorkOrder: this.inspectForm.isWorkOrder // 1=否,2=是  
268 -  
269 369
  370 + // 构造基础提交数据
  371 + const baseSubmitData = {
270 "batchNo": this.paramsOptins.batchNo, 372 "batchNo": this.paramsOptins.batchNo,
271 - "planNo":this.paramsOptins.planNo, 373 + "planNo": this.paramsOptins.planNo,
272 "imgHost": "1", 374 "imgHost": "1",
273 "imgList": this.getImgUrlList(this.imagesList), 375 "imgList": this.getImgUrlList(this.imagesList),
274 "inspectionState": this.inspectForm.isWorkOrder, 376 "inspectionState": this.inspectForm.isWorkOrder,
275 - "transState": this.inspectForm.isWorkOrder==1?'1':'2', 377 + "transState": this.inspectForm.isWorkOrder === '1' ? '1' : '2',
276 "transWorkNo": "default'", 378 "transWorkNo": "default'",
277 "remark": this.inspectForm.content.trim() 379 "remark": this.inspectForm.content.trim()
278 } 380 }
279 381
280 - // 显示加载中  
281 - uni.showLoading({ title: '提交中...' }) 382 + // 构造最终提交数据
  383 + let submitData = { ...baseSubmitData }
  384 + if (this.inspectForm.isWorkOrder === '2') {
  385 + // 紧急程度仍为必填,必传
  386 + submitData.pressingType = this.inspectForm.pressingType
  387 + // 关键修改:仅当希望完成时间有值时,才添加该参数
  388 + if (this.inspectForm.expectedFinishDate) {
  389 + submitData.expectedFinishDate = new Date(this.inspectForm.expectedFinishDate).getTime()
  390 + }
  391 + }
282 392
  393 + uni.showLoading({ title: '提交中...' })
283 await inspectionCreate(submitData) 394 await inspectionCreate(submitData)
284 395
285 uni.hideLoading() 396 uni.hideLoading()
@@ -289,7 +400,6 @@ export default { @@ -289,7 +400,6 @@ export default {
289 duration: 1000 400 duration: 1000
290 }) 401 })
291 402
292 - // // 延迟跳转(等待提示框显示完成)  
293 setTimeout(() => { 403 setTimeout(() => {
294 uni.reLaunch({ 404 uni.reLaunch({
295 url: '/pages-sub/daily/patrol-manage/index' 405 url: '/pages-sub/daily/patrol-manage/index'
@@ -297,14 +407,8 @@ export default { @@ -297,14 +407,8 @@ export default {
297 }, 1000) 407 }, 1000)
298 408
299 } catch (error) { 409 } catch (error) {
300 - // 隐藏加载框  
301 uni.hideLoading() 410 uni.hideLoading()
302 -  
303 - // 区分是表单校验失败还是接口调用失败  
304 - if (Array.isArray(error)) {  
305 - // 表单校验失败 - 静默处理(uView会自动提示)  
306 - } else {  
307 - // 接口调用失败 411 + if (!Array.isArray(error)) {
308 console.error('巡查表单提交失败:', error) 412 console.error('巡查表单提交失败:', error)
309 uni.showToast({ 413 uni.showToast({
310 title: '提交失败,请重试', 414 title: '提交失败,请重试',
@@ -319,14 +423,18 @@ export default { @@ -319,14 +423,18 @@ export default {
319 </script> 423 </script>
320 424
321 <style lang="scss" scoped> 425 <style lang="scss" scoped>
322 -// 全局页面样式  
323 .u-page { 426 .u-page {
  427 + min-height: 100vh;
324 } 428 }
325 429
326 -// 巡查表单内容容器  
327 .inspect-form-content { 430 .inspect-form-content {
328 background: #fff; 431 background: #fff;
329 padding: 20rpx; 432 padding: 20rpx;
330 } 433 }
331 434
  435 +.form-item {
  436 + margin-bottom: 20rpx;
  437 +}
  438 +
  439 +
332 </style> 440 </style>
333 \ No newline at end of file 441 \ No newline at end of file
pages-sub/daily/quick-order/add-order.vue
@@ -89,6 +89,8 @@ @@ -89,6 +89,8 @@
89 @after-read="(event) => uploadImgs(event, 'problemImgsList')" 89 @after-read="(event) => uploadImgs(event, 'problemImgsList')"
90 @delete="(event) => deleteImg(event, 'problemImgsList')" 90 @delete="(event) => deleteImg(event, 'problemImgsList')"
91 multiple 91 multiple
  92 + width="70"
  93 + height="70"
92 :max-count="3" 94 :max-count="3"
93 upload-text="选择问题照片" 95 upload-text="选择问题照片"
94 ></up-upload> 96 ></up-upload>
@@ -101,6 +103,8 @@ @@ -101,6 +103,8 @@
101 @after-read="(event) => uploadImgs(event, 'completeImgsList')" 103 @after-read="(event) => uploadImgs(event, 'completeImgsList')"
102 @delete="(event) => deleteImg(event, 'completeImgsList')" 104 @delete="(event) => deleteImg(event, 'completeImgsList')"
103 multiple 105 multiple
  106 + width="70"
  107 + height="70"
104 :max-count="3" 108 :max-count="3"
105 :sizeType="['compressed']" 109 :sizeType="['compressed']"
106 upload-text="选择完成照片" 110 upload-text="选择完成照片"
pages-sub/daily/quick-order/order-detail.vue
@@ -65,11 +65,11 @@ @@ -65,11 +65,11 @@
65 65
66 <!-- 修复1:正确判断problemImgsList,补充空数组默认值 --> 66 <!-- 修复1:正确判断problemImgsList,补充空数组默认值 -->
67 <up-album 67 <up-album
68 - v-if="!!orderDetail.problemImgsList?.length"  
69 - :urls="orderDetail.problemImgsList || []" 68 + v-if="!!orderDetail.problemsImgs?.length"
  69 + :urls="orderDetail.problemsImgs || []"
70 singleSize="70" 70 singleSize="70"
71 :preview-full-image="true" 71 :preview-full-image="true"
72 - 72 + multipleSize="70"
73 ></up-album> 73 ></up-album>
74 <text v-else class="empty-text">暂无问题照片</text> 74 <text v-else class="empty-text">暂无问题照片</text>
75 </view> 75 </view>
@@ -81,9 +81,10 @@ @@ -81,9 +81,10 @@
81 <template #value> 81 <template #value>
82 <view class="cell-content-wrap"> 82 <view class="cell-content-wrap">
83 <up-album 83 <up-album
84 - v-if="!!orderDetail.completeImgsList?.length"  
85 - :urls="orderDetail.completeImgsList || []" 84 + v-if="!!orderDetail.endImgs?.length"
  85 + :urls="orderDetail.endImgs || []"
86 singleSize="70" 86 singleSize="70"
  87 + multipleSize="70"
87 :preview-full-image="true" 88 :preview-full-image="true"
88 89
89 ></up-album> 90 ></up-album>
@@ -131,8 +132,8 @@ const getOrderDetail = async (id: string) =&gt; { @@ -131,8 +132,8 @@ const getOrderDetail = async (id: string) =&gt; {
131 // 优化:确保图片数组为数组类型,避免非数组导致渲染错误 132 // 优化:确保图片数组为数组类型,避免非数组导致渲染错误
132 orderDetail.value = { 133 orderDetail.value = {
133 ...res, 134 ...res,
134 - problemImgsList: Array.isArray(res.problemImgsList) ? res.problemImgsList : [],  
135 - completeImgsList: Array.isArray(res.completeImgsList) ? res.completeImgsList : [] 135 + problemsImgs: Array.isArray(res.problemsImgs) ? res.problemsImgs : [],
  136 + endImgs: Array.isArray(res.endImgs) ? res.endImgs : []
136 }; 137 };
137 } catch (error) { 138 } catch (error) {
138 console.error('获取工单详情失败:', error); 139 console.error('获取工单详情失败:', error);
pages-sub/problem/regional-order-manage/add-order.vue 0 → 100644
  1 +<script lang="ts">
  2 +import {defineComponent} from 'vue'
  3 +
  4 +export default defineComponent({
  5 + name: "add-order"
  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/problem/regional-order-manage/index.vue 0 → 100644
  1 +<script lang="ts">
  2 +import {defineComponent} from 'vue'
  3 +
  4 +export default defineComponent({
  5 + name: "index"
  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/problem/regional-order-manage/order-detail.vue 0 → 100644
  1 +<script lang="ts">
  2 +import {defineComponent} from 'vue'
  3 +
  4 +export default defineComponent({
  5 + name: "order-detail"
  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/problem/work-order-manage/add-order.vue
@@ -103,7 +103,7 @@ @@ -103,7 +103,7 @@
103 <!-- 问题照片(核心修复:绑定纯数组) --> 103 <!-- 问题照片(核心修复:绑定纯数组) -->
104 <up-form-item label="问题照片" prop="problemImgs" required> 104 <up-form-item label="问题照片" prop="problemImgs" required>
105 <up-upload 105 <up-upload
106 - :file-list="problemImgs.imgList" 106 + :file-list="problemImgs.imgList.value||[]"
107 @after-read="problemImgs.uploadImgs" 107 @after-read="problemImgs.uploadImgs"
108 @delete="problemImgs.deleteImg" 108 @delete="problemImgs.deleteImg"
109 multiple 109 multiple
@@ -185,10 +185,11 @@ const problemImgs = useUploadImgs({ @@ -185,10 +185,11 @@ const problemImgs = useUploadImgs({
185 fieldName: 'problemImgs' 185 fieldName: 'problemImgs'
186 }) 186 })
187 187
188 -// 核心修复:监听响应式数组变化,同步更新纯数组(解决u-upload不刷新问题)  
189 -watch(() => problemImgs.rawImgList.value, (newVal) => {  
190 - problemImgs.imgList = newVal  
191 -}, { deep: true }) 188 +// 无需再手动初始化普通数组,直接使用 ref 响应式数据
  189 +if (!Array.isArray(problemImgs.rawImgList.value)) {
  190 + problemImgs.rawImgList.value = [];
  191 +}
  192 +
192 193
193 // ========== 页面状态 ========== 194 // ========== 页面状态 ==========
194 // 通用弹窗控制 195 // 通用弹窗控制
@@ -318,7 +319,7 @@ const echoOrderData = (orderItem) =&gt; { @@ -318,7 +319,7 @@ const echoOrderData = (orderItem) =&gt; {
318 workOrderForm.problemDesc = orderItem.remark || ''; 319 workOrderForm.problemDesc = orderItem.remark || '';
319 workOrderForm.lat = orderItem.lat || 0; 320 workOrderForm.lat = orderItem.lat || 0;
320 workOrderForm.lon = orderItem.lon || 0; 321 workOrderForm.lon = orderItem.lon || 0;
321 - workOrderForm.expectedFinishDate = orderItem.expectedFinishDate || timeFormat(new Date(), 'yyyy-mm-dd hh:MM:ss'); 322 + workOrderForm.expectedFinishDate = timeFormat(orderItem.expectedFinishDate, 'yyyy-mm-dd hh:MM:ss') || timeFormat(new Date(), 'yyyy-mm-dd hh:MM:ss');
322 323
323 // 2. 上传图片回显(兼容useUploadImgs格式) 324 // 2. 上传图片回显(兼容useUploadImgs格式)
324 if (orderItem.problemsImgs && Array.isArray(orderItem.problemsImgs) && orderItem.problemsImgs.length > 0) { 325 if (orderItem.problemsImgs && Array.isArray(orderItem.problemsImgs) && orderItem.problemsImgs.length > 0) {
@@ -327,7 +328,7 @@ const echoOrderData = (orderItem) =&gt; { @@ -327,7 +328,7 @@ const echoOrderData = (orderItem) =&gt; {
327 name: `renew_img_${index}`, 328 name: `renew_img_${index}`,
328 status: 'success' // 标记为已上传状态 329 status: 'success' // 标记为已上传状态
329 })); 330 }));
330 - problemImgs.imgList = imgList; 331 + problemImgs.imgList.value = imgList;
331 problemImgs.rawImgList.value = imgList; 332 problemImgs.rawImgList.value = imgList;
332 } 333 }
333 334
@@ -513,7 +514,8 @@ const submitWorkOrder = async () =&gt; { @@ -513,7 +514,8 @@ const submitWorkOrder = async () =&gt; {
513 lonLatAddress: workOrderForm.workLocation, 514 lonLatAddress: workOrderForm.workLocation,
514 pressingType: workOrderForm.pressingType, 515 pressingType: workOrderForm.pressingType,
515 orderName: workOrderForm.orderName, 516 orderName: workOrderForm.orderName,
516 - expectedFinishDate: workOrderForm.expectedFinishDate, 517 + // expectedFinishDate: workOrderForm.expectedFinishDate,
  518 + expectedFinishDate: new Date(workOrderForm.expectedFinishDate).getTime(),
517 sourceId: 1, 519 sourceId: 1,
518 sourceName: '园林', 520 sourceName: '园林',
519 busiLine: 'yl' 521 busiLine: 'yl'
pages-sub/problem/work-order-manage/order-detail.vue
@@ -80,7 +80,7 @@ @@ -80,7 +80,7 @@
80 ></up-cell> 80 ></up-cell>
81 81
82 <!-- 处理结果 --> 82 <!-- 处理结果 -->
83 - <up-cell> 83 + <up-cell v-if="orderDetail.handleResult">
84 <template #title> 84 <template #title>
85 <view style="min-width: 200rpx">处理结果</view> 85 <view style="min-width: 200rpx">处理结果</view>
86 </template> 86 </template>
@@ -109,15 +109,17 @@ @@ -109,15 +109,17 @@
109 109
110 <!-- 希望完成时间 --> 110 <!-- 希望完成时间 -->
111 <up-cell 111 <up-cell
  112 + v-if="orderDetail.expectedFinishDate"
112 title="希望完成时间" 113 title="希望完成时间"
113 - :value="orderDetail.expectedFinishDate === 0 ? '未设置' : timeFormat(orderDetail.expectedFinishDate, 'yyyy-mm-dd hh:MM:ss')" 114 + :value="timeFormat(orderDetail.expectedFinishDate, 'yyyy-mm-dd hh:MM:ss')"
114 align="middle" 115 align="middle"
115 :border="false" 116 :border="false"
116 ></up-cell> 117 ></up-cell>
117 118
118 <up-cell 119 <up-cell
  120 + v-if="orderDetail.finishDate"
119 title="工单完结时间" 121 title="工单完结时间"
120 - :value="orderDetail.finishDate === 0 ? '暂无' : timeFormat(orderDetail.finishDate, 'yyyy-mm-dd hh:MM:ss')" 122 + :value="timeFormat(orderDetail.finishDate, 'yyyy-mm-dd hh:MM:ss')"
121 align="middle" 123 align="middle"
122 :border="false" 124 :border="false"
123 ></up-cell> 125 ></up-cell>
@@ -198,6 +200,11 @@ @@ -198,6 +200,11 @@
198 <text v-if="item.endTime"> 至 {{ timeFormat(item.endTime, 'yyyy-mm-dd hh:MM:ss') }}</text> 200 <text v-if="item.endTime"> 至 {{ timeFormat(item.endTime, 'yyyy-mm-dd hh:MM:ss') }}</text>
199 <text v-else class="processing-tag">(处理中)</text> 201 <text v-else class="processing-tag">(处理中)</text>
200 </view> 202 </view>
  203 +
  204 + <view class="reason-line up-line-1" v-if="index!==0&&item.endTime">
  205 + 总耗时:{{ calculateFormatTimeDiff(item.startTime , item.endTime) }}
  206 + </view>
  207 +
201 <!-- 原因行 --> 208 <!-- 原因行 -->
202 <view class="reason-line up-line-2" v-if="item.tasks && item.tasks[0]?.reason"> 209 <view class="reason-line up-line-2" v-if="item.tasks && item.tasks[0]?.reason">
203 描述:{{ getLimitReason(item.tasks && item.tasks[0]?.reason) }} 210 描述:{{ getLimitReason(item.tasks && item.tasks[0]?.reason) }}
@@ -321,7 +328,7 @@ import { @@ -321,7 +328,7 @@ import {
321 getApprovalDetail, 328 getApprovalDetail,
322 universalApproval 329 universalApproval
323 } from '@/api/work-order-manage/work-order-manage'; 330 } from '@/api/work-order-manage/work-order-manage';
324 -import { nextStepMap, buzStatusMap } from '@/common/utils/common' 331 +import { nextStepMap, buzStatusMap, calculateFormatTimeDiff } from '@/common/utils/common'
325 // 引入图片上传组合式函数 332 // 引入图片上传组合式函数
326 import { useUploadImgs } from '@/common/utils/useUploadImgs' 333 import { useUploadImgs } from '@/common/utils/useUploadImgs'
327 334
pages.json
@@ -134,8 +134,6 @@ @@ -134,8 +134,6 @@
134 "style": { "navigationBarTitleText": "分配工单" } 134 "style": { "navigationBarTitleText": "分配工单" }
135 }, 135 },
136 136
137 -  
138 -  
139 { 137 {
140 "path": "work-order-manage/order-detail", 138 "path": "work-order-manage/order-detail",
141 "style": { "navigationBarTitleText": "工单详情" } 139 "style": { "navigationBarTitleText": "工单详情" }
@@ -145,11 +143,24 @@ @@ -145,11 +143,24 @@
145 "style": { "navigationBarTitleText": "养护任务" } 143 "style": { "navigationBarTitleText": "养护任务" }
146 }, 144 },
147 145
148 -  
149 -  
150 { 146 {
151 "path": "problem-allot/index", 147 "path": "problem-allot/index",
152 "style": { "navigationBarTitleText": "问题分配" } 148 "style": { "navigationBarTitleText": "问题分配" }
  149 + },
  150 +
  151 + {
  152 + "path": "regional-order-manage/index",
  153 + "style": { "navigationBarTitleText": "工单管理" }
  154 + },
  155 +
  156 + {
  157 + "path": "regional-order-manage/add-order",
  158 + "style": { "navigationBarTitleText": "待派单" }
  159 + },
  160 +
  161 + {
  162 + "path": "regional-order-manage/order-detail",
  163 + "style": { "navigationBarTitleText": "工单详情" }
153 } 164 }
154 ] 165 ]
155 }, 166 },