index.uvue 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804
  1. <template>
  2. <view class="detail-page">
  3. <scroll-view class="detail-content" :scroll-y="true">
  4. <!-- 工单信息 -->
  5. <view class="info-section">
  6. <view class="section-title">
  7. <text class="section-title-text">工单信息</text>
  8. </view>
  9. <view class="info-card">
  10. <view class="info-item">
  11. <text class="info-label">工单编码</text>
  12. <text class="info-value">{{ detailData.workOrderProjectNo ?? '' }}</text>
  13. </view>
  14. <view class="info-item">
  15. <text class="info-label">工单类型</text>
  16. <text class="info-value">{{ detailData.orderType == 1 ? '维修工单' : '维保工单' }}</text>
  17. </view>
  18. <view class="info-item">
  19. <text class="info-label">风机编号</text>
  20. <text class="info-value">{{ detailData.pcsDeviceName ?? '' }}</text>
  21. </view>
  22. <view class="info-item">
  23. <text class="info-label">场站</text>
  24. <text class="info-value">{{ detailData.pcsStationName ?? '' }}</text>
  25. </view>
  26. <view class="info-item">
  27. <text class="info-label">机型</text>
  28. <text class="info-value">{{ detailData.brand ?? '' }} {{ detailData.model ?? '' }}</text>
  29. </view>
  30. <view class="info-item" v-if="returnType">
  31. <text class="info-label">结单退回原因</text>
  32. <text class="info-value">{{ getReturnTypeName(returnType)}}</text>
  33. </view>
  34. <view class="info-item" v-if="returnReason">
  35. <text class="info-label">结单退回说明</text>
  36. <text class="info-value">{{ returnReason}}</text>
  37. </view>
  38. <view class="info-item" v-if="acceptReturnType">
  39. <text class="info-label">接单退回原因</text>
  40. <text class="info-value">{{ getAcceptReturnTypeName(acceptReturnType)}}</text>
  41. </view>
  42. <view class="info-item" v-if="acceptReturnReason">
  43. <text class="info-label">接单退回说明</text>
  44. <text class="info-value">{{ acceptReturnReason}}</text>
  45. </view>
  46. <view class="info-item" v-if="detailData.pauseTime != null && detailData.orderType == 1">
  47. <text class="info-label">发生时间</text>
  48. <text class="info-value">{{ detailData.pauseTime ?? '' }}</text>
  49. </view>
  50. <view class="info-item" v-if="detailData.pauseTime != null && detailData.orderType == 2">
  51. <text class="info-label">停机时间</text>
  52. <text class="info-value">{{ detailData.pauseTime ?? '' }}</text>
  53. </view>
  54. <view class="info-item" v-if="detailData.restartTime != null">
  55. <text class="info-label">恢复运行时间</text>
  56. <text class="info-value">{{ detailData.restartTime ?? '' }}</text>
  57. </view>
  58. <view class="info-item">
  59. <text class="info-label">{{getLabel(detailData)}}</text>
  60. <text class="info-value">{{ getDisplayTime(detailData) }}</text>
  61. </view>
  62. <view class="info-item" v-if="detailData.orderType == 2">
  63. <text class="info-label">维保内容</text>
  64. <text class="info-value">{{ detailData.content ?? '' }}</text>
  65. </view>
  66. <view class="info-item" v-if="detailData.orderType == 1">
  67. <text class="info-label">故障代码</text>
  68. <text class="info-value">{{ detailData.faultCode ?? '' }}</text>
  69. </view>
  70. <view class="info-item" v-if="detailData.orderType == 1">
  71. <text class="info-label">故障条文</text>
  72. <text class="info-value">{{ detailData.faultBarcode ?? '' }}</text>
  73. </view>
  74. <view class="info-item" v-if="detailData.orderType == 1">
  75. <text class="info-label">故障描述</text>
  76. <text class="info-value">{{ detailData.faultDesc ?? '' }}</text>
  77. </view>
  78. </view>
  79. </view>
  80. <!-- 附件信息 -->
  81. <view class="info-section" v-if="attachmentList.length > 0">
  82. <view class="section-title">
  83. <text class="section-title-text">附件</text>
  84. </view>
  85. <view class="info-card">
  86. <view class="image-container">
  87. <image
  88. v-for="(attachment, index) in attachmentList"
  89. :key="index"
  90. :src="attachment"
  91. :alt="'附件图片' + (index + 1)"
  92. class="attachment-image"
  93. @click="previewImage(attachment)"
  94. />
  95. </view>
  96. </view>
  97. </view>
  98. <!-- 工单流转 -->
  99. <view class="info-section">
  100. <view class="section-title">
  101. <text class="section-title-text">工单流转</text>
  102. <text @click="toggleFlowList" v-if="detailData.workOrderFlowList != null && detailData.workOrderFlowList.length > 1" class="toggle-btn">{{ isFlowListExpanded ? '收起' : '展开' }}</text>
  103. </view>
  104. <view class="info-card" v-if="detailData.workOrderFlowList != null && detailData.workOrderFlowList.length > 0">
  105. <view class="flow-item" v-for="(flow, index) in displayedFlowList" :key="index">
  106. <view class="flow-header">
  107. <text class="flow-operator">{{ flow.operatorName ?? '未知操作人' }}</text>
  108. <text class="flow-time">{{ flow.actionTime ?? '' }}</text>
  109. </view>
  110. <view class="flow-content">
  111. <text class="flow-action">{{ getActionTypeName(flow.actionType) }}</text>
  112. <!-- <text class="flow-remark" v-if="flow.actionRemark">{{ flow.actionRemark }}</text> -->
  113. </view>
  114. </view>
  115. </view>
  116. <view class="info-card" v-else>
  117. <view class="no-data">暂无流转记录</view>
  118. </view>
  119. </view>
  120. </scroll-view>
  121. <!-- 加载中状态 -->
  122. <view v-if="loading" class="loading-mask">
  123. <text class="loading-text">加载中...</text>
  124. </view>
  125. </view>
  126. </template>
  127. <script setup lang="uts">
  128. import { ref, computed } from 'vue'
  129. //import type { acceptOrderInfo } from '../../../types/order'
  130. import type { WorkOrderFlow,WorkOrderFlowList } from '../../../types/flow'
  131. import { getOrderInfoById, getRepairOrderInfoById } from '../../../api/order/detail'
  132. import type { SysDictData } from '../../../types/dict'
  133. import { getDictDataByType } from '../../../api/dict/index'
  134. import { getBaseUrl } from '../../../utils/request'
  135. type OrderInfoForDetail = {
  136. orderType: Number
  137. id: Number
  138. teamLeaderId: Number | 0
  139. teamLeaderName: string | null
  140. acceptUserId: Number | 0
  141. acceptUserName: string | null
  142. acceptTime: string | null
  143. assignTime: string | null
  144. assignUserName: string | null
  145. status: Number
  146. workOrderProjectNo: string | null
  147. workOrderStatus: string | null
  148. gxtCenterId: Number | 0
  149. gxtCenter: string | null
  150. pcsStationId: Number | 0
  151. pcsStationName: string | null
  152. pcsDeviceId: Number | 0
  153. pcsDeviceName: string | null
  154. brand: string | null
  155. model: string | null
  156. createTime: string | null
  157. workOrderFlowList?: WorkOrderFlowList | null
  158. suspendReason: string | null
  159. rejectionReason: string | null
  160. updateTime: string | null // 新增字段
  161. workEndTime: string | null // 新增字段
  162. faultCode: string | null
  163. faultBarcode: string | null
  164. faultDesc: string | null
  165. pauseTime: string | null,
  166. restartTime: string | null,
  167. attachmentUrls: string | null, // 附件URLs(逗号分隔的字符串格式)
  168. content: string | null,
  169. inspectionType: string | null
  170. }
  171. // 详情数据
  172. const detailData = ref<OrderInfoForDetail>({
  173. orderType: 0,
  174. id: 0,
  175. teamLeaderId: 0,
  176. acceptUserId: 0,
  177. teamLeaderName: null,
  178. acceptUserName: null,
  179. acceptTime: null,
  180. assignTime: null,
  181. assignUserName: null,
  182. status: 0,
  183. workOrderProjectNo: null,
  184. workOrderStatus: null,
  185. gxtCenterId: 0,
  186. gxtCenter: null,
  187. pcsStationId: 0,
  188. pcsStationName: null,
  189. pcsDeviceId: 0,
  190. pcsDeviceName: null,
  191. brand: null,
  192. model: null,
  193. createTime: null,
  194. workOrderFlowList: null,
  195. suspendReason: null,
  196. rejectionReason: null,
  197. updateTime: null, // 新增字段
  198. workEndTime: null, // 新增字段
  199. faultCode: null,
  200. faultBarcode: null,
  201. faultDesc: null,
  202. pauseTime: null,
  203. restartTime: null,
  204. attachmentUrls: null,
  205. content: null,
  206. inspectionType: null
  207. })
  208. const statusDictList = ref<SysDictData[]>([]) // 工单状态字典列表
  209. // 添加字典加载状态
  210. const dictLoaded = ref<boolean>(false)
  211. const returnType = ref<string>("")
  212. const returnReason = ref<string>("")
  213. const acceptReturnType = ref<string>("")
  214. const acceptReturnReason = ref<string>("")
  215. const returnTypeDictList = ref<SysDictData[]>([]) // 退回原因字典列表
  216. const acceptReturnTypeDictList = ref<SysDictData[]>([]) // 退回原因字典列表
  217. const formatDate = (dateString: string): string => {
  218. if (dateString == '' || dateString == null) return ''
  219. const date = new Date(dateString)
  220. const year = date.getFullYear()
  221. const month = (date.getMonth() + 1).toString().padStart(2, '0')
  222. const day = date.getDate().toString().padStart(2, '0')
  223. const hours = date.getHours().toString().padStart(2, '0')
  224. const minutes = date.getMinutes().toString().padStart(2, '0')
  225. return `${year}-${month}-${day} ${hours}:${minutes}`
  226. }
  227. // 根据状态显示不同的时间label
  228. const getLabel = (item : OrderInfoForDetail | null): string|null => {
  229. if (item == null) return null
  230. // 如果是"待接单"状态,显示派单时间
  231. if (item.workOrderStatus == 'assigned') {
  232. return '下发时间'
  233. } else if(item.workOrderStatus == 'to_finish') {
  234. if(item.workEndTime != null) {
  235. return '结束时间'
  236. }
  237. return '接单时间'
  238. } else if(item.workOrderStatus == 'to_approve') {
  239. return '申请挂起时间'
  240. } else if(item.workOrderStatus == 'suspended') {
  241. return '审批通过时间'
  242. } else if(item.workOrderStatus == 'return' || item.workOrderStatus == 'accept_return') {
  243. return '退回时间'
  244. } else if(item.workOrderStatus == 'completed') {
  245. return '结单时间'
  246. } else if(item.workOrderStatus == "archived") {
  247. return '归档时间'
  248. }
  249. // 默认显示创建时间
  250. return '创建时间'
  251. }
  252. // 根据状态显示不同的时间
  253. const getDisplayTime = (item : OrderInfoForDetail | null): string|null => {
  254. if (item == null) return null
  255. let showTime = ref<string|null>('')
  256. // 如果是"待接单"状态,显示派单时间
  257. if (item.workOrderStatus == 'assigned') {
  258. showTime.value = item.assignTime
  259. } else if(item.workOrderStatus == 'to_finish') {
  260. showTime.value = item.acceptTime
  261. } else if(item.workOrderStatus == 'to_approve') {
  262. showTime.value = item.updateTime
  263. } else if(item.workOrderStatus == 'suspended') {
  264. showTime.value = item.updateTime
  265. } else if(item.workOrderStatus == 'return' || item.workOrderStatus == 'accept_return') {
  266. showTime.value = item.updateTime
  267. } else if(item.workOrderStatus == 'completed') {
  268. showTime.value = item.updateTime
  269. } else if(item.workOrderStatus == "archived") {
  270. showTime.value = item.updateTime
  271. } else {
  272. if (item.createTime != null && item.createTime.length >= 3) {
  273. showTime.value = item.createTime.slice(0, -3);
  274. } else {
  275. showTime.value = item.createTime != null ? item.createTime : '';
  276. }
  277. }
  278. return showTime.value
  279. // return formatDate(showTime.value ?? '')
  280. }
  281. // 获取工单状态字典列表
  282. const loadStatusDictList = async (): Promise<void> => {
  283. try {
  284. const result = await getDictDataByType('gxt_repair_order_flow_action_type')
  285. const resultObj = result as UTSJSONObject
  286. if (resultObj['code'] == 200) {
  287. const data = resultObj['data'] as any[]
  288. const dictData: SysDictData[] = []
  289. if (data.length > 0) {
  290. for (let i = 0; i < data.length; i++) {
  291. const item = data[i] as UTSJSONObject
  292. // 只提取需要的字段
  293. const dictItem: SysDictData = {
  294. dictValue: item['dictValue'] as string | null,
  295. dictLabel: item['dictLabel'] as string | null,
  296. dictCode: null,
  297. dictSort: null,
  298. dictType: null,
  299. cssClass: null,
  300. listClass: null,
  301. isDefault: null,
  302. status: null,
  303. default: null,
  304. createTime: null,
  305. remark: null
  306. }
  307. dictData.push(dictItem)
  308. }
  309. }
  310. statusDictList.value = dictData
  311. dictLoaded.value = true
  312. }
  313. } catch (e: any) {
  314. console.error('获取工单状态字典失败:', e.message)
  315. dictLoaded.value = true
  316. }
  317. }
  318. const loading = ref<boolean>(false)
  319. // 控制工单流转列表是否展开
  320. const isFlowListExpanded = ref<boolean>(false)
  321. // 计算显示的工单流转列表
  322. const displayedFlowList = computed(() => {
  323. if (detailData.value.workOrderFlowList == null) return []
  324. // 如果已经展开,则显示全部
  325. if (isFlowListExpanded.value) {
  326. return detailData.value.workOrderFlowList
  327. }
  328. // 默认只显示最后一条
  329. const length = detailData.value.workOrderFlowList.length
  330. return length > 0 ? [detailData.value.workOrderFlowList[length - 1]] : []
  331. })
  332. // 切换工单流转列表的展开/收起状态
  333. const toggleFlowList = () => {
  334. isFlowListExpanded.value = !isFlowListExpanded.value
  335. }
  336. // 计算附件列表
  337. const attachmentList = computed<string[]>(() => {
  338. if (detailData.value.attachmentUrls != null && detailData.value.attachmentUrls != '') {
  339. // 按逗号分割附件URL字符串,并加上基础URL
  340. const arr = detailData.value.attachmentUrls.split(',')
  341. .map((url: string) => {
  342. const trimmedUrl = url.trim();
  343. // 如果是相对路径,加上基础URL
  344. if (trimmedUrl.startsWith('/')) {
  345. return getBaseUrl() + trimmedUrl;
  346. }
  347. return trimmedUrl;
  348. })
  349. .filter((url: string) => url.length > 0);
  350. return arr;
  351. }
  352. return []
  353. })
  354. // 预览图片
  355. const previewImage = (url: string) => {
  356. if (url == '') return
  357. // 检查是否为图片文件
  358. const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp']
  359. const isImage = imageExtensions.some(ext => url.toLowerCase().endsWith(ext))
  360. if (isImage) {
  361. // 如果是图片,使用uni.previewImage预览
  362. uni.previewImage({
  363. urls: [url],
  364. current: 0
  365. })
  366. } else {
  367. // 对于非图片文件,显示提示
  368. uni.showToast({
  369. title: '不支持预览此文件类型',
  370. icon: 'none',
  371. duration: 2000
  372. })
  373. }
  374. }
  375. // 获取操作类型名称
  376. const getActionTypeName = (item: string | null): string | null => {
  377. if (item == null) return ''
  378. // const orderInfoItem = item as orderInfo
  379. const rawStatus = item
  380. // 如果字典尚未加载,返回原始值
  381. if (dictLoaded.value == false) {
  382. return rawStatus
  383. }
  384. // 查找字典中对应的标签
  385. const dictItem = statusDictList.value.find(dict => dict.dictValue == rawStatus)
  386. return dictItem!=null ? dictItem.dictLabel : rawStatus
  387. }
  388. const getReturnTypeName = (item: string | null): string | null => {
  389. if (item == null) return ''
  390. // const orderInfoItem = item as orderInfo
  391. const rawStatus = item
  392. // 如果字典尚未加载,返回原始值
  393. if (dictLoaded.value == false) {
  394. return rawStatus
  395. }
  396. // 查找字典中对应的标签
  397. const dictItem = returnTypeDictList.value.find(dict => dict.dictValue == rawStatus)
  398. return dictItem!=null ? dictItem.dictLabel : rawStatus
  399. }
  400. const getAcceptReturnTypeName = (item: string | null): string | null => {
  401. if (item == null) return ''
  402. // const orderInfoItem = item as orderInfo
  403. const rawStatus = item
  404. // 如果字典尚未加载,返回原始值
  405. if (dictLoaded.value == false) {
  406. return rawStatus
  407. }
  408. // 查找字典中对应的标签
  409. const dictItem = acceptReturnTypeDictList.value.find(dict => dict.dictValue == rawStatus)
  410. return dictItem!=null ? dictItem.dictLabel : rawStatus
  411. }
  412. const loadReturnDictList = async (returnTypeDict: string): Promise<void> => {
  413. try {
  414. const result = await getDictDataByType(returnTypeDict)
  415. const resultObj = result as UTSJSONObject
  416. if (resultObj['code'] == 200) {
  417. const data = resultObj['data'] as any[]
  418. const dictData: SysDictData[] = []
  419. if (data.length > 0) {
  420. for (let i = 0; i < data.length; i++) {
  421. const item = data[i] as UTSJSONObject
  422. // 只提取需要的字段
  423. const dictItem: SysDictData = {
  424. dictValue: item['dictValue'] as string | null,
  425. dictLabel: item['dictLabel'] as string | null,
  426. dictCode: null,
  427. dictSort: null,
  428. dictType: null,
  429. cssClass: null,
  430. listClass: null,
  431. isDefault: null,
  432. status: null,
  433. default: null,
  434. createTime: null,
  435. remark: null
  436. }
  437. dictData.push(dictItem)
  438. }
  439. }
  440. if(returnTypeDict == 'gxt_return_type') {
  441. returnTypeDictList.value = dictData
  442. } else {
  443. acceptReturnTypeDictList.value = dictData
  444. }
  445. }
  446. } catch (e: any) {
  447. console.error('获取工单状态字典失败:', e.message)
  448. }
  449. }
  450. // 加载详情数据
  451. const loadDetail = async (id: string, orderType?: number): Promise<void> => {
  452. try {
  453. loading.value = true
  454. let result: any;
  455. // 根据orderType决定调用哪个API
  456. if (orderType == 1) {
  457. // 维修工单
  458. result = await getRepairOrderInfoById(id)
  459. } else {
  460. // 维保工单
  461. result = await getOrderInfoById(id)
  462. }
  463. // 提取响应数据
  464. const resultObj = result as UTSJSONObject
  465. const code = resultObj['code'] as number
  466. const data = resultObj['data'] as UTSJSONObject | null
  467. if (code == 200 && data != null) {
  468. // 处理工单流转列表
  469. let workOrderFlowList: WorkOrderFlow[] | null = null
  470. let flowList: UTSJSONObject[] = []
  471. if (orderType == 1) {
  472. // 维修工单
  473. flowList = data['repairOrderFlowList'] as UTSJSONObject[]
  474. } else {
  475. // 维保工单
  476. flowList = data['workOrderFlowList'] as UTSJSONObject[]
  477. }
  478. if (flowList.length > 0) {
  479. workOrderFlowList = []
  480. for (let i = 0; i < flowList.length; i++) {
  481. const flowItem = flowList[i]
  482. const flow: WorkOrderFlow = {
  483. id: flowItem['id'] as Number,
  484. orderId: flowItem['orderId'] as Number,
  485. orderCode: flowItem['orderCode'] as string,
  486. actionType: flowItem['actionType'] as string,
  487. fromStatus: flowItem['fromStatus'] as string | null,
  488. toStatus: flowItem['toStatus'] as string,
  489. operatorId: flowItem['operatorId'] as Number | null,
  490. operatorName: flowItem['operatorName'] as string | null,
  491. actionTime: flowItem['actionTime'] as string,
  492. actionRemark: flowItem['actionRemark'] as string | null,
  493. createBy: flowItem['createBy'] as string | null,
  494. createTime: flowItem['createTime'] as string | null
  495. }
  496. workOrderFlowList.push(flow)
  497. }
  498. }
  499. // 转换数据
  500. const orderDtail: OrderInfoForDetail = {
  501. orderType: data['orderType'] as Number,
  502. id: data['id'] as Number,
  503. teamLeaderId: data['teamLeaderId'] != null ? (data['teamLeaderId'] as Number) : 0,
  504. acceptUserId: data['acceptUserId'] != null ? (data['acceptUserId'] as Number) : 0,
  505. teamLeaderName: data['teamLeaderName'] as string | null,
  506. acceptUserName: data['acceptUserName'] as string | null,
  507. acceptTime: data['acceptTime'] as string | null,
  508. assignTime: data['assignTime'] as string | null,
  509. assignUserName: data['assignUserName'] as string | null,
  510. status: (data['status']==null)?0:data['status'] as Number,
  511. workOrderProjectNo: data['workOrderProjectNo'] as string | null,
  512. workOrderStatus: data['workOrderStatus'] as string | null,
  513. gxtCenterId: data['gxtCenterId'] as Number | 0,
  514. gxtCenter: data['gxtCenter'] as string | null,
  515. pcsStationId: data['pcsStationId'] as Number | 0,
  516. pcsStationName: data['pcsStationName'] as string | null,
  517. pcsDeviceId: data['pcsDeviceId'] as Number | 0,
  518. pcsDeviceName: data['pcsDeviceName'] as string | null,
  519. brand: data['brand'] as string | null,
  520. model: data['model'] as string | null,
  521. createTime: data['createTime'] as string | null,
  522. workOrderFlowList: workOrderFlowList,
  523. suspendReason: data['suspendReason'] as string | null,
  524. rejectionReason: data['rejectionReason'] as string | null,
  525. updateTime: data['updateTime'] as string | null, // 新增字段
  526. workEndTime: data['workEndTime'] as string | null, // 新增字段
  527. faultCode: data['faultCode'] as string | null,
  528. faultBarcode: data['faultBarcode'] as string | null,
  529. faultDesc: data['faultDesc'] as string | null,
  530. pauseTime: data['pauseTime'] as string | null,
  531. restartTime: data['restartTime'] as string | null,
  532. attachmentUrls: data['attachmentUrls'] as string | null,
  533. content: data['content'] as string | null,
  534. inspectionType: data['inspectionType'] as string | null
  535. }
  536. detailData.value = orderDtail
  537. await loadReturnDictList('gxt_return_type')
  538. await loadReturnDictList('gxt_accept_return_type')
  539. returnType.value = (data['returnType'] as string | null) ?? ''
  540. returnReason.value = (data['returnReason'] as string | null) ?? ''
  541. acceptReturnType.value = (data['acceptReturnType'] as string | null) ?? ''
  542. acceptReturnReason.value = (data['acceptReturnReason'] as string | null) ?? ''
  543. } else {
  544. const msg = resultObj['msg'] as string | null
  545. uni.showToast({
  546. title: msg ?? '加载失败',
  547. icon: 'none'
  548. })
  549. }
  550. } catch (e: any) {
  551. uni.showToast({
  552. title: e.message ?? '加载失败',
  553. icon: 'none'
  554. })
  555. } finally {
  556. loading.value = false
  557. }
  558. }
  559. // 页面加载
  560. onLoad((options: any) => {
  561. const params = options as UTSJSONObject
  562. const id = params['id'] as string | null
  563. const orderTypeParam = params['orderType'] as string | null
  564. if (id != null && orderTypeParam != null) {
  565. // 先尝试从参数中获取orderType
  566. const orderTypeNumber = parseInt(orderTypeParam)
  567. loadDetail(id, orderTypeNumber)
  568. }
  569. })
  570. // 初始化
  571. onMounted(() => {
  572. loadStatusDictList()
  573. })
  574. </script>
  575. <style lang="scss">
  576. .detail-page {
  577. flex: 1;
  578. background-color: #e8f0f9;
  579. }
  580. .detail-content {
  581. flex: 1;
  582. padding: 20rpx 0;
  583. }
  584. .info-section {
  585. margin: 0 30rpx 24rpx;
  586. .section-title {
  587. position: relative;
  588. padding-left: 20rpx;
  589. margin-bottom: 20rpx;
  590. flex-direction: row;
  591. justify-content: space-between;
  592. align-items: center;
  593. &::before {
  594. // content: '';
  595. position: absolute;
  596. left: 0;
  597. top: 50%;
  598. transform: translateY(-50%);
  599. width: 8rpx;
  600. height: 32rpx;
  601. background-color: #007aff;
  602. border-radius: 4rpx;
  603. }
  604. &-text {
  605. font-size: 32rpx;
  606. font-weight: bold;
  607. color: #333333;
  608. }
  609. .toggle-btn {
  610. padding-right: 20rpx;
  611. font-size: 28rpx;
  612. color: #165dff;
  613. }
  614. }
  615. .info-card {
  616. background-color: #ffffff;
  617. border-radius: 16rpx;
  618. padding: 30rpx;
  619. .info-item {
  620. flex-direction: row;
  621. padding: 20rpx 0;
  622. border-bottom: 1rpx solid #f0f0f0;
  623. &:last-child {
  624. border-bottom: none;
  625. }
  626. &.full-width {
  627. flex-direction: column;
  628. .info-label {
  629. margin-bottom: 12rpx;
  630. }
  631. .info-value {
  632. line-height: 44rpx;
  633. }
  634. }
  635. .info-label {
  636. width: 240rpx;
  637. font-size: 28rpx;
  638. color: #666666;
  639. white-space: nowrap;
  640. }
  641. .info-value {
  642. flex: 1;
  643. font-size: 28rpx;
  644. color: #333333;
  645. text-align: right;
  646. &.highlight {
  647. color: #007aff;
  648. font-weight: bold;
  649. }
  650. }
  651. }
  652. .flow-item {
  653. padding: 20rpx 0;
  654. border-bottom: 1rpx solid #f0f0f0;
  655. &:last-child {
  656. border-bottom: none;
  657. }
  658. .flow-header {
  659. flex-direction: row;
  660. justify-content: space-between;
  661. margin-bottom: 10rpx;
  662. .flow-operator {
  663. font-size: 28rpx;
  664. color: #333333;
  665. font-weight: bold;
  666. }
  667. .flow-time {
  668. font-size: 24rpx;
  669. color: #999999;
  670. }
  671. }
  672. .flow-content {
  673. flex-direction: column;
  674. .flow-action {
  675. font-size: 26rpx;
  676. color: #666666;
  677. margin-bottom: 8rpx;
  678. }
  679. .flow-remark {
  680. font-size: 24rpx;
  681. color: #999999;
  682. background-color: #f5f5f5;
  683. padding: 10rpx;
  684. border-radius: 8rpx;
  685. }
  686. }
  687. }
  688. .no-data {
  689. text-align: center;
  690. padding: 40rpx 0;
  691. font-size: 28rpx;
  692. color: #999999;
  693. }
  694. }
  695. }
  696. .loading-mask {
  697. position: absolute;
  698. top: 0;
  699. left: 0;
  700. right: 0;
  701. bottom: 0;
  702. justify-content: center;
  703. align-items: center;
  704. background-color: rgba(0, 0, 0, 0.3);
  705. .loading-text {
  706. padding: 30rpx 60rpx;
  707. background-color: rgba(0, 0, 0, 0.7);
  708. color: #ffffff;
  709. font-size: 28rpx;
  710. border-radius: 12rpx;
  711. }
  712. }
  713. .image-container {
  714. display: flex;
  715. flex-direction: row;
  716. flex-wrap: wrap;
  717. justify-content: flex-start; // 或 space-around
  718. // justify-content: space-around; /* 自动分配间距,避免挤压 */
  719. align-items: center;
  720. padding: 0 10rpx;
  721. }
  722. .attachment-image {
  723. width: 300rpx; /* 一行显示两个图片,减去间距的一半 */
  724. height: 300rpx;
  725. padding: 10rpx
  726. }
  727. </style>