device-list.uvue 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039
  1. <template>
  2. <uni-navbar-lite :showLeft="true" title="设备清单"></uni-navbar-lite>
  3. <view class="list-page">
  4. <!-- 任务信息栏 -->
  5. <view class="task-info-bar" v-if="taskInfo.planName != null">
  6. <view class="task-info-item">
  7. <text class="task-info-label">任务:</text>
  8. <text class="task-info-value">{{ taskInfo.planName }}</text>
  9. </view>
  10. <view class="task-info-item" v-if="taskInfo.displayTime != null">
  11. <text class="task-info-label">时间:</text>
  12. <text class="task-info-value">{{ taskInfo.displayTime }}</text>
  13. </view>
  14. </view>
  15. <!-- 搜索栏 -->
  16. <!--<view class="search-bar">
  17. <view class="search-box">
  18. <image class="search-icon" src="/static/images/workbench/list/1.png" mode="aspectFit"></image>
  19. <input class="search-input" type="text" placeholder="搜索设备" v-model="keyword" @confirm="handleSearch" @blur="handleSearch" />
  20. <text v-if="keyword.length > 0" class="clear-icon" @click="clearSearch">✕</text>
  21. </view>
  22. </view>-->
  23. <!-- 列表内容 -->
  24. <common-list
  25. :dataList="dataList"
  26. :loading="loading"
  27. :refreshing="refreshing"
  28. :hasMore="hasMore"
  29. @refresh="handleRefresh"
  30. @loadMore="loadMore"
  31. @itemClick="handleView"
  32. >
  33. <template #default="{ item, index }">
  34. <view class="list-item">
  35. <view class="item-container">
  36. <view class="item-header">
  37. <text class="item-title">{{ (item as DeviceCheckTaskDetail).machineryName }}</text>
  38. <view class="task-badges">
  39. <text class="status-tag" :class="getTaskStatusClass(item)">
  40. {{ getTaskStatusText((item as DeviceCheckTaskDetail).taskStatus) }}
  41. </text>
  42. </view>
  43. </view>
  44. <view class="info-row">
  45. <view class="info-label">
  46. <text class="text-gray">设备编号: {{ (item as DeviceCheckTaskDetail).machinerySpec }}</text>
  47. </view>
  48. </view>
  49. <view class="info-row">
  50. <view class="info-label">
  51. <text class="text-gray">位置: {{ (item as DeviceCheckTaskDetail).machineryLocation }}</text>
  52. </view>
  53. </view>
  54. <view class="btn-group">
  55. <!-- 显示扫描按钮的逻辑:如果设备未完成检查,且是第一个未检查的设备,则启用;否则禁用 -->
  56. <view
  57. v-if="(item as DeviceCheckTaskDetail).taskStatus == '0'"
  58. :class="['btn-primary', isFirstUncompletedTask(item) ? '' : 'btn-disabled']"
  59. @click.stop="isFirstUncompletedTask(item) ? handleItemClick(item,'scan') : null"
  60. >
  61. <!-- <text class="btn-text">扫描二维码</text> -->
  62. <text class="btn-text">开始检查</text>
  63. </view>
  64. <view
  65. v-else
  66. class="btn-primary"
  67. @click.stop="handleItemClick(item,'detail')"
  68. >
  69. <text class="btn-text">查看</text>
  70. </view>
  71. </view>
  72. </view>
  73. </view>
  74. </template>
  75. </common-list>
  76. </view>
  77. </template>
  78. <script setup lang="uts">
  79. import { ref, onBeforeUnmount, onMounted, reactive, onShow } from 'vue'
  80. import type { SysDictData } from '../../types/dict'
  81. import {checkPermi, getUserInfo} from '../../utils/storage'
  82. import { getTaskInfo, updateTaskInfo } from '../../api/task/list.uts'
  83. import { getTaskDetails } from '../../api/task/detail.uts'
  84. import { getDictDataByType } from '../../api/dict/index'
  85. // 任务基础信息类型(参照GenTask实体类)
  86. type TaskInfoForDevice = {
  87. taskId: Number | null
  88. planId: Number | null
  89. planType: string | null
  90. planName: string | null
  91. taskDate: string | null
  92. startTime: string | null
  93. endTime: string | null
  94. subjects: string | null
  95. machineryTypeName: string | null
  96. machinerySpec: string | null
  97. machineryLocation: string | null
  98. machineryName: string | null
  99. machineries: string | null
  100. locations: string | null
  101. operator: string | null
  102. operatorId: Number | null
  103. status: string | null
  104. taskDesc: string | null
  105. sourceType: string | null
  106. locationCount: Number
  107. machineryCount: Number
  108. createTime: string | null
  109. updateTime: string | null
  110. // 前端计算字段
  111. planTypeText: string | null
  112. statusText: string | null
  113. displayTime: string | null
  114. taskCount: string | null
  115. checkType: string | null
  116. }
  117. // 设备检查任务明细类型(参照DvCheckTaskDetail实体类)
  118. type DeviceCheckTaskDetail = {
  119. detailId: Number | null
  120. taskId: Number | null
  121. machineryRecordId: Number | null
  122. machineryId: Number | null
  123. machineryTypeName: string | null
  124. machinerySpec: string | null
  125. machineryLocation: string | null
  126. machineryName: string | null
  127. subjectId: Number | null
  128. subjectCode: string | null
  129. subjectName: string | null
  130. locationRecordId: Number | null
  131. locationId: Number | null
  132. locationName: string | null
  133. locationCode: string | null
  134. sourceType: string | null
  135. orderNum: Number | null
  136. status: string | null
  137. imagesUrl: string | null
  138. videosUrl: string | null
  139. resultValue: string | null
  140. resultDesc: string | null
  141. remark: string | null
  142. attr1: string | null
  143. attr2: string | null
  144. attr3: Number | null
  145. attr4: Number | null
  146. // 任务状态字段
  147. taskStatus: string | null // 'completed' 表示已完成,其他值表示未完成
  148. }
  149. // 设备清单数据
  150. const dataList = ref<DeviceCheckTaskDetail[]>([])
  151. // 任务信息
  152. const taskInfo = ref<TaskInfoForDevice>({
  153. taskId: null,
  154. planId: null,
  155. planType: null,
  156. planName: null,
  157. taskDate: null,
  158. startTime: null,
  159. endTime: null,
  160. subjects: null,
  161. machineryTypeName: null,
  162. machinerySpec: null,
  163. machineryLocation: null,
  164. machineryName: null,
  165. machineries: null,
  166. locations: null,
  167. operator: null,
  168. operatorId: null,
  169. status: null,
  170. taskDesc: null,
  171. sourceType: null,
  172. locationCount: 0,
  173. machineryCount: 0,
  174. createTime: null,
  175. updateTime: null,
  176. // 前端计算字段
  177. planTypeText: null,
  178. statusText: null,
  179. displayTime: null,
  180. taskCount: null,
  181. checkType: null
  182. })
  183. // 当前任务ID
  184. const currentTaskId = ref<string>('')
  185. const page = ref<number>(1)
  186. const pageSize: number = 10
  187. const hasMore = ref<boolean>(true)
  188. const loadingShow = ref<boolean>(false)
  189. const loading = ref<boolean>(false)
  190. const refreshing = ref<boolean>(false)
  191. const total = ref<number>(0)
  192. const taskStatusDictList = ref<SysDictData[]>([]) // 任务状态字典列表
  193. const dictLoaded = ref<boolean>(false) // 字典加载状态
  194. // 不再需要设备检查状态管理,使用taskStatus字段
  195. // 判断当前设备是否是第一个未完成的任务
  196. const isFirstUncompletedTask = (item: any | null): boolean => {
  197. if (item == null) return false
  198. const deviceItem = item as DeviceCheckTaskDetail
  199. // 如果任务已经完成,则返回false
  200. if (deviceItem.taskStatus == '2') {
  201. return false
  202. }
  203. // 查找第一个未完成的任务
  204. for (let i = 0; i < dataList.value.length; i++) {
  205. const device = dataList.value[i] as DeviceCheckTaskDetail
  206. if (device.taskStatus != '2') {
  207. // 返回当前设备是否是第一个未完成的任务
  208. return device.detailId == deviceItem.detailId
  209. }
  210. }
  211. // 如果所有任务都已完成,返回false
  212. return false
  213. }
  214. // 添加防重复刷新的标志
  215. const isRefreshing = ref<boolean>(false)
  216. // 添加刷新时间戳,用于防抖
  217. const lastRefreshTime = ref<number>(0)
  218. // 获取设备状态样式
  219. const getDeviceStatusClass = (item: any | null): string => {
  220. if (item == null) return ''
  221. const deviceItem = item as DeviceCheckTaskDetail
  222. const rawStatus = deviceItem.status
  223. if (rawStatus==null) return ''
  224. // 返回对应的状态类名
  225. return `status-${rawStatus}`
  226. }
  227. // 获取任务状态文本
  228. const getTaskStatusText = (taskStatus: string | null): string => {
  229. if (taskStatus == null || taskStatus.length == 0) return ''
  230. if (dictLoaded.value != true) {
  231. return taskStatus
  232. }
  233. const dictItem = taskStatusDictList.value.find((dict: SysDictData) => dict.dictValue == taskStatus)
  234. return dictItem != null && dictItem.dictLabel != null ? dictItem.dictLabel : taskStatus
  235. }
  236. // 获取任务状态字典列表
  237. const loadTaskStatusDictList = async (): Promise<void> => {
  238. try {
  239. const result = await getDictDataByType('dv_task_status')
  240. const resultObj = result as UTSJSONObject
  241. if (resultObj['code'] == 200) {
  242. const data = resultObj['data'] as any[]
  243. const dictData: SysDictData[] = []
  244. if (data.length > 0) {
  245. for (let i = 0; i < data.length; i++) {
  246. const item = data[i] as UTSJSONObject
  247. const dictItem: SysDictData = {
  248. dictValue: item['dictValue'] as string | null,
  249. dictLabel: item['dictLabel'] as string | null,
  250. dictCode: null,
  251. dictSort: null,
  252. dictType: null,
  253. cssClass: null,
  254. listClass: null,
  255. isDefault: null,
  256. status: null,
  257. default: null,
  258. createTime: null,
  259. remark: null
  260. }
  261. dictData.push(dictItem)
  262. }
  263. }
  264. taskStatusDictList.value = dictData
  265. dictLoaded.value = true
  266. }
  267. } catch (e: any) {
  268. console.error('获取任务状态字典失败:', e.message)
  269. dictLoaded.value = true
  270. }
  271. }
  272. // 获取任务状态样式
  273. const getTaskStatusClass = (item: any | null): string => {
  274. if (item == null) return ''
  275. const deviceItem = item as DeviceCheckTaskDetail
  276. const taskStatus = deviceItem.taskStatus
  277. if (taskStatus==null) return ''
  278. // 返回对应的任务状态类名
  279. return `taskStatus-${taskStatus}`
  280. }
  281. // 获取显示时间
  282. const getDisplayTime = (item: TaskInfoForDevice): string | null => {
  283. if (item.taskDate == null || item.taskDate.length == 0) return null
  284. if (item.startTime != null && item.startTime.length > 0 && item.endTime != null && item.endTime.length > 0) {
  285. return `${item.taskDate} ${item.startTime}-${item.endTime}`
  286. }
  287. return item.taskDate
  288. }
  289. // 根据dataList中的taskStatus更新taskInfo的status
  290. const updateTaskInfoStatus = async (): Promise<void> => {
  291. if (dataList.value.length == 0) return;
  292. let completedCount = 0;
  293. for (let i = 0; i < dataList.value.length; i++) {
  294. const item = dataList.value[i] as DeviceCheckTaskDetail;
  295. if (item.taskStatus == '2') {
  296. completedCount++;
  297. }
  298. }
  299. let newStatus = '0';
  300. // 全部完成
  301. if (completedCount == dataList.value.length) {
  302. newStatus = '2';
  303. }
  304. // 部分完成
  305. else if (completedCount > 0) {
  306. newStatus = '1';
  307. }
  308. // 全部未完成
  309. else {
  310. newStatus = '0';
  311. }
  312. // 只有状态发生变化时才调用接口更新
  313. if (taskInfo.value.status != newStatus) {
  314. taskInfo.value.status = newStatus;
  315. console.log(`任务状态更新: 总数${dataList.value.length}, 完成${completedCount}, 设置状态为${newStatus}`);
  316. // 调用后端接口更新任务状态
  317. try {
  318. const updateData: UTSJSONObject = {
  319. 'taskId': taskInfo.value.taskId,
  320. 'status': newStatus
  321. } as UTSJSONObject;
  322. await updateTaskInfo(updateData);
  323. console.log('任务状态已同步到后端');
  324. } catch (error) {
  325. console.error('更新任务状态失败:', error);
  326. // 如果更新失败,可以考虑回滚状态或给出提示
  327. }
  328. }
  329. }
  330. // 加载真实数据
  331. const loadData = async (isRefresh: boolean | null, disablePullDown: boolean | null): Promise<void> => {
  332. const shouldRefresh = isRefresh != null ? isRefresh : false
  333. const shouldDisablePullDown = disablePullDown != null ? disablePullDown : false
  334. if (currentTaskId.value == null || currentTaskId.value.length == 0) {
  335. return
  336. }
  337. loading.value = true
  338. if (shouldRefresh && !shouldDisablePullDown) {
  339. page.value = 1
  340. refreshing.value = true
  341. } else if (shouldRefresh && shouldDisablePullDown) {
  342. page.value = 1
  343. refreshing.value = false
  344. } else {
  345. refreshing.value = false
  346. }
  347. try {
  348. // 获取设备清单数据
  349. const result = await getTaskDetails(currentTaskId.value)
  350. // 提取响应数据
  351. const resultObj = result as UTSJSONObject
  352. const code = resultObj['code'] as number
  353. const responseData = resultObj['rows'] as any[]
  354. const responseTotal = resultObj['total'] as number
  355. if (code == 200) {
  356. // 将 any[] 转换为 DeviceCheckTaskDetail[]
  357. const newData: DeviceCheckTaskDetail[] = []
  358. for (let i = 0; i < responseData.length; i++) {
  359. const item = responseData[i] as UTSJSONObject
  360. const deviceItem: DeviceCheckTaskDetail = {
  361. detailId: item['detailId'] != null ? (item['detailId'] as Number) : null,
  362. taskId: item['taskId'] != null ? (item['taskId'] as Number) : null,
  363. machineryRecordId: item['machineryRecordId'] != null ? (item['machineryRecordId'] as Number) : null,
  364. machineryId: item['machineryId'] != null ? (item['machineryId'] as Number) : null,
  365. machineryTypeName: item['machineryTypeName'] as string | null,
  366. machinerySpec: item['machinerySpec'] as string | null,
  367. machineryLocation: item['machineryLocation'] as string | null,
  368. machineryName: item['machineryName'] as string | null,
  369. subjectId: item['subjectId'] != null ? (item['subjectId'] as Number) : null,
  370. subjectCode: item['subjectCode'] as string | null,
  371. subjectName: item['subjectName'] as string | null,
  372. locationRecordId: item['locationRecordId'] != null ? (item['locationRecordId'] as Number) : null,
  373. locationId: item['locationId'] != null ? (item['locationId'] as Number) : null,
  374. locationName: item['locationName'] as string | null,
  375. locationCode: item['locationCode'] as string | null,
  376. sourceType: item['sourceType'] as string | null,
  377. orderNum: item['orderNum'] != null ? (item['orderNum'] as Number) : null,
  378. status: item['status'] as string | null,
  379. imagesUrl: item['imagesUrl'] as string | null,
  380. videosUrl: item['videosUrl'] as string | null,
  381. resultValue: item['resultValue'] as string | null,
  382. resultDesc: item['resultDesc'] as string | null,
  383. remark: item['remark'] as string | null,
  384. attr1: item['attr1'] as string | null,
  385. attr2: item['attr2'] as string | null,
  386. attr3: item['attr3'] != null ? (item['attr3'] as Number) : null,
  387. attr4: item['attr4'] != null ? (item['attr4'] as Number) : null,
  388. // 任务状态字段
  389. //taskStatus: item['taskStatus'] != null ? (item['taskStatus'] as string) : '0',
  390. taskStatus: item['taskStatus'] as string | null,
  391. }
  392. newData.push(deviceItem)
  393. }
  394. if (shouldRefresh) {
  395. dataList.value = newData
  396. } else {
  397. dataList.value = [...dataList.value, ...newData]
  398. }
  399. total.value = responseTotal
  400. hasMore.value = dataList.value.length < responseTotal
  401. } else {
  402. const msg = resultObj['msg'] as string | null
  403. uni.showToast({
  404. title: msg ?? '加载失败',
  405. icon: 'none'
  406. })
  407. }
  408. } catch (e: any) {
  409. uni.showToast({
  410. title: e.message ?? '加载失败',
  411. icon: 'none'
  412. })
  413. } finally {
  414. loading.value = false
  415. if (shouldRefresh) {
  416. refreshing.value = false
  417. setTimeout(() => {
  418. isRefreshing.value = false
  419. }, 50)
  420. }
  421. }
  422. }
  423. // 加载任务基础信息
  424. const loadTaskInfo = async (): Promise<void> => {
  425. if (currentTaskId.value == null || currentTaskId.value.length == 0) {
  426. return
  427. }
  428. try {
  429. const result = await getTaskInfo(currentTaskId.value)
  430. // 提取响应数据
  431. const resultObj = result as UTSJSONObject
  432. const code = resultObj['code'] as number
  433. const taskData = resultObj['data'] as UTSJSONObject
  434. if (code == 200) {
  435. // 将返回的数据转换为TaskInfo类型
  436. const newTaskInfo: TaskInfoForDevice = {
  437. taskId: taskData['taskId'] != null ? (taskData['taskId'] as Number) : null,
  438. planId: taskData['planId'] != null ? (taskData['planId'] as Number) : null,
  439. planType: taskData['planType'] as string | null,
  440. planName: taskData['planName'] as string | null,
  441. taskDate: taskData['taskDate'] as string | null,
  442. startTime: taskData['startTime'] as string | null,
  443. endTime: taskData['endTime'] as string | null,
  444. subjects: taskData['subjects'] as string | null,
  445. machineryTypeName: taskData['machineryTypeName'] as string | null,
  446. machinerySpec: taskData['machinerySpec'] as string | null,
  447. machineryLocation: taskData['machineryLocation'] as string | null,
  448. machineryName: taskData['machineryName'] as string | null,
  449. machineries: taskData['machineries'] as string | null,
  450. locations: taskData['locations'] as string | null,
  451. operator: taskData['operator'] as string | null,
  452. operatorId: taskData['operatorId'] != null ? (taskData['operatorId'] as Number) : null,
  453. status: taskData['status'] as string | null,
  454. taskDesc: taskData['taskDesc'] as string | null,
  455. sourceType: taskData['sourceType'] as string | null,
  456. locationCount: taskData['locationCount'] != null ? (taskData['locationCount'] as Number) : 0,
  457. machineryCount: taskData['machineryCount'] != null ? (taskData['machineryCount'] as Number) : 0,
  458. createTime: taskData['createTime'] as string | null,
  459. updateTime: taskData['updateTime'] as string | null,
  460. // 前端计算字段初始化
  461. planTypeText: null,
  462. statusText: null,
  463. displayTime: null,
  464. taskCount: null,
  465. checkType: null
  466. }
  467. taskInfo.value = newTaskInfo
  468. // 计算前端显示字段
  469. taskInfo.value.displayTime = getDisplayTime(taskInfo.value)
  470. } else {
  471. const msg = resultObj['msg'] as string | null
  472. uni.showToast({
  473. title: msg ?? '获取任务信息失败',
  474. icon: 'none'
  475. })
  476. }
  477. } catch (e: any) {
  478. uni.showToast({
  479. title: e.message ?? '获取任务信息失败',
  480. icon: 'none'
  481. })
  482. }
  483. }
  484. // 下拉刷新
  485. const handleRefresh = async (): Promise<void> => {
  486. console.log("handleRefresh被触发")
  487. const now = Date.now();
  488. if (now - lastRefreshTime.value < 1000) {
  489. refreshing.value = false;
  490. return;
  491. }
  492. lastRefreshTime.value = now;
  493. if (loading.value || isRefreshing.value) {
  494. refreshing.value = false;
  495. return;
  496. }
  497. isRefreshing.value = true;
  498. try {
  499. await loadData(true, false);
  500. // 刷新完成后检查并更新任务状态
  501. await updateTaskInfoStatus();
  502. } catch (error) {
  503. console.error('刷新失败:', error);
  504. } finally {
  505. // 简单的状态重置
  506. refreshing.value = false;
  507. loading.value = false;
  508. isRefreshing.value = false;
  509. }
  510. setTimeout(() => {
  511. isRefreshing.value = false
  512. }, 100)
  513. }
  514. // 加载更多
  515. const loadMore = (): void => {
  516. if (!hasMore.value || loading.value) {
  517. return
  518. }
  519. page.value++
  520. loadData(false, false)
  521. }
  522. // 点击列表项
  523. const handleItemClick = (item: any | null, action: string): void => {
  524. if (item == null) return
  525. const deviceItem = item as DeviceCheckTaskDetail
  526. if(action == 'scan') {
  527. /* // 检查前一个任务是否已完成
  528. const currentIndex = dataList.value.findIndex(d => d.detailId == deviceItem.detailId);
  529. let canProcess = true;
  530. // 检查前面的所有任务是否都已完成
  531. for (let i = 0; i < currentIndex; i++) {
  532. if (dataList.value[i].taskStatus != 'completed') {
  533. canProcess = false;
  534. break;
  535. }
  536. }
  537. if (canProcess) { */
  538. // 跳转到任务处理页面
  539. uni.showModal({
  540. title: '开始检查',
  541. //content: `即将处理任务: ${deviceItem.machineryName} (${deviceItem.machinerySpec})`,
  542. confirmText: '确定开始',
  543. cancelText: '取消',
  544. success: (res) => {
  545. if(res.confirm) {
  546. // 跳转到设备检查页面进行任务处理
  547. /* const deviceName = deviceItem.machineryName != null ? deviceItem.machineryName : '';
  548. const deviceCode = deviceItem.machinerySpec != null ? deviceItem.machinerySpec : '';
  549. const deviceTypeText = deviceItem.machineryTypeName != null ? deviceItem.machineryTypeName : '';
  550. const location = deviceItem.locationName != null ? deviceItem.locationName : ''; */
  551. if(deviceItem.detailId != null){
  552. uni.navigateTo({
  553. url: `/pages/task/device-check?detailId=${deviceItem.detailId}`
  554. });
  555. }else{
  556. uni.showToast({
  557. title: '缺少参数',
  558. icon: 'none'
  559. })
  560. }
  561. }
  562. }
  563. });
  564. /* } else {
  565. uni.showToast({
  566. title: '请按顺序完成任务!前序任务完成后才能开始当前任务。',
  567. icon: 'none',
  568. duration: 2000
  569. });
  570. } */
  571. }
  572. if(action === 'detail') {
  573. uni.navigateTo({
  574. url: `/pages/task/device-check?detailId=${deviceItem.detailId}&opType=view`
  575. });
  576. }
  577. }
  578. // 点击列表项
  579. const handleView = (item: any | null): void => {
  580. /* if (item == null) return
  581. const deviceItem = item as deviceInfo
  582. uni.showToast({
  583. title: `设备详情: ${deviceItem.deviceName}`,
  584. icon: 'none'
  585. }); */
  586. }
  587. onLoad((options: any) => {
  588. const params = options as UTSJSONObject
  589. const taskId = params.get('taskId') as string | null
  590. // 获取传递的任务ID
  591. if (taskId != null) {
  592. currentTaskId.value = taskId
  593. // 并行加载字典、任务信息和设备清单
  594. Promise.all([
  595. loadTaskStatusDictList(),
  596. loadTaskInfo(),
  597. loadData(true, true)
  598. ]).catch(error => {
  599. console.error('加载数据失败:', error)
  600. }).finally(() => {
  601. // 确保在任何情况下都重置加载状态
  602. loading.value = false
  603. refreshing.value = false
  604. })
  605. } else {
  606. uni.showToast({
  607. title: '缺少任务ID参数',
  608. icon: 'none'
  609. })
  610. }
  611. })
  612. // 组件卸载前清理事件监听
  613. onBeforeUnmount(() => {
  614. refreshing.value = false
  615. loading.value = false
  616. isRefreshing.value = false
  617. })
  618. // 页面显示时强制重置加载状态
  619. onShow(() => {
  620. /* loading.value = false
  621. refreshing.value = false
  622. isRefreshing.value = false */
  623. // 检查是否有来自子页面的刷新标记
  624. const needRefresh = uni.getStorageSync('needRefresh')
  625. if (needRefresh == true) {
  626. // 清除标记
  627. uni.removeStorageSync('needRefresh')
  628. // 延迟一小段时间再执行刷新,避免状态冲突
  629. setTimeout(() => {
  630. loadData(true, false).then(() => {
  631. updateTaskInfoStatus()
  632. })
  633. }, 500)
  634. }
  635. // 延迟再重置一次确保生效
  636. /* setTimeout(() => {
  637. loading.value = false
  638. refreshing.value = false
  639. isRefreshing.value = false
  640. }, 500) */
  641. })
  642. // 页面卸载时设置返回标记
  643. onUnload(() => {
  644. // 设置标记通知index页面需要刷新
  645. uni.setStorageSync('needRefresh', true)
  646. })
  647. </script>
  648. <style lang="scss">
  649. .list-page {
  650. flex: 1;
  651. background-color: #e8f0f9;
  652. }
  653. .task-info-bar {
  654. padding: 20rpx 30rpx;
  655. background-color: #d7eafe;
  656. border-bottom: 1rpx solid #ccc;
  657. }
  658. .task-info-item {
  659. flex-direction: row;
  660. align-items: center;
  661. margin-bottom: 10rpx;
  662. }
  663. .task-info-label {
  664. font-size: 28rpx;
  665. color: #333;
  666. font-weight: bold;
  667. margin-right: 10rpx;
  668. }
  669. .task-info-value {
  670. font-size: 28rpx;
  671. color: #666;
  672. }
  673. .search-bar {
  674. padding: 20rpx 30rpx;
  675. background-color: #d7eafe;
  676. }
  677. .search-box {
  678. flex-direction: row;
  679. align-items: center;
  680. height: 72rpx;
  681. padding: 0 24rpx;
  682. background-color: #f5f5f5;
  683. border-radius: 36rpx;
  684. .search-icon {
  685. width: 32rpx;
  686. height: 32rpx;
  687. margin-right: 12rpx;
  688. }
  689. .search-input {
  690. flex: 1;
  691. font-size: 28rpx;
  692. color: #333333;
  693. }
  694. .clear-icon {
  695. margin-left: 12rpx;
  696. font-size: 28rpx;
  697. color: #999999;
  698. }
  699. }
  700. .list-item {
  701. margin: 12rpx 30rpx;
  702. background-color: #ffffff;
  703. border-radius: 16rpx;
  704. }
  705. .device-completed {
  706. background-color: #f0f9eb; /* 浅绿色背景表示已完成 */
  707. }
  708. .device-in-progress {
  709. background-color: #e6f4ff; /* 浅蓝色背景表示检查中 */
  710. }
  711. .device-pending {
  712. background-color: #ffffff; /* 白色背景表示待扫描 */
  713. }
  714. .btn-disabled {
  715. z-index: 999;
  716. border-radius: 10rpx;
  717. font-size: 24rpx;
  718. margin-left: 20rpx;
  719. background-color: #ccc;
  720. line-height: 45rpx;
  721. color: #999;
  722. .btn-text{
  723. color: #999;
  724. font-size: 24rpx;
  725. padding: 5px 15px;
  726. }
  727. }
  728. .item-container {
  729. padding: 30rpx;
  730. }
  731. .item-header {
  732. flex-direction: row;
  733. align-items: flex-start;
  734. margin-bottom: 16rpx;
  735. justify-content: space-between; /* 主轴两端对齐 */
  736. min-height: 55rpx;
  737. .item-title {
  738. font-size: 30rpx;
  739. color: #333333;
  740. font-weight: bold;
  741. flex-wrap: wrap;
  742. flex: 0 1 75%;
  743. min-width: 0;
  744. }
  745. .task-badges {
  746. display: flex;
  747. flex-direction: row;
  748. align-items: center;
  749. gap: 10rpx; /* 添加间距 */
  750. }
  751. }
  752. .info-row {
  753. flex-direction: row;
  754. justify-content: space-between;
  755. align-items: center;
  756. .info-label {
  757. font-size: 26rpx;
  758. color: #666;
  759. }
  760. .info-value {
  761. font-size: 26rpx;
  762. // color: #666;
  763. }
  764. }
  765. .btn-group {
  766. flex-direction: row;
  767. align-items: center;
  768. justify-content: flex-end;
  769. margin-top: 20rpx;
  770. }
  771. .btn-primary {
  772. z-index: 999;
  773. border-radius: 10rpx;
  774. font-size: 24rpx;
  775. // white-space: nowrap;
  776. margin-left: 20rpx;
  777. background-color: #165DFF;
  778. line-height: 45rpx;
  779. color: #ffffff;
  780. .btn-text{
  781. color: #ffffff;
  782. font-size: 24rpx;
  783. padding: 5px 15px;
  784. }
  785. }
  786. .btn-disabled {
  787. z-index: 999;
  788. border-radius: 10rpx;
  789. font-size: 24rpx;
  790. margin-left: 20rpx;
  791. background-color: #ccc;
  792. line-height: 45rpx;
  793. color: #999;
  794. .btn-text{
  795. color: #999;
  796. font-size: 24rpx;
  797. padding: 5px 15px;
  798. }
  799. }
  800. .status-tag {
  801. padding: 8rpx 20rpx;
  802. border-radius: 20rpx;
  803. font-size: 24rpx;
  804. white-space: nowrap;
  805. // margin-left: 50rpx;
  806. // justify-content: flex-end;
  807. border: 1rpx solid;
  808. text-align: center;
  809. }
  810. /* 设备类型样式 */
  811. .type-power {
  812. background-color: #ECF5FF;
  813. color: #409EFF;
  814. border-color: #d8ebff;
  815. }
  816. .type-cooling {
  817. background-color: #F4F4F5;
  818. color: #909399;
  819. border-color: #e9e9eb;
  820. }
  821. .type-water {
  822. background-color: #F0F9EB;
  823. color: #67C23A;
  824. border-color: #c2e7b0;
  825. }
  826. /* 设备状态样式 */
  827. .status-normal {
  828. background-color: #F0F9EB;
  829. color: #67C23A;
  830. border-color: #c2e7b0;
  831. }
  832. .status-warning {
  833. background-color: #FDF6EC;
  834. color: #E6A23C;
  835. border-color: #faecd8;
  836. }
  837. .status-danger {
  838. background-color: #FEF0F0;
  839. color: #F56C6C;
  840. border-color: #fde2e2;
  841. }
  842. /* 扫描状态样式 */
  843. .scan-completed {
  844. background-color: #F0F9EB;
  845. color: #67C23A;
  846. border-color: #c2e7b0;
  847. }
  848. .scan-pending {
  849. background-color: #FDF6EC;
  850. color: #E6A23C;
  851. border-color: #faecd8;
  852. }
  853. .scan-in-progress {
  854. background-color: #F4F4F5;
  855. color: #909399;
  856. border-color: #e9e9eb;
  857. }
  858. /* 任务类型样式 (保留兼容性) */
  859. .type-inspection {
  860. background-color: #ECF5FF;
  861. color: #409EFF;
  862. border-color: #d8ebff;
  863. }
  864. .type-patrol {
  865. background-color: #F4F4F5;
  866. color: #909399;
  867. border-color: #e9e9eb;
  868. }
  869. .type-maintenance {
  870. background-color: #F0F9EB;
  871. color: #67C23A;
  872. border-color: #c2e7b0;
  873. }
  874. /* 任务状态样式 (保留兼容性) */
  875. .status-pending {
  876. background-color: #FDF6EC;
  877. color: #E6A23C;
  878. border-color: #faecd8;
  879. }
  880. .status-completed {
  881. background-color: #F0F9EB;
  882. color: #67C23A;
  883. border-color: #c2e7b0;
  884. }
  885. .status-overdue {
  886. background-color: #FEF0F0;
  887. color: #F56C6C;
  888. border-color: #fde2e2;
  889. }
  890. /* 任务状态样式 */
  891. .taskStatus-0 {
  892. background-color: #FDF6EC;
  893. color: #E6A23C;
  894. border-color: #faecd8;
  895. }
  896. .taskStatus-1 {
  897. background-color: #EBF5FF;
  898. color: #409EFF;
  899. border-color: #d8ebff;
  900. }
  901. .taskStatus-2 {
  902. background-color: #F0F9EB;
  903. color: #67C23A;
  904. border-color: #c2e7b0;
  905. }
  906. /* 保持其他原有样式 */
  907. .text-gray{
  908. font-size: 26rpx;
  909. color: #666;
  910. }
  911. .scroll-view_H {
  912. width: 100%;
  913. flex-direction: row;
  914. }
  915. .scroll-view-item_H {
  916. justify-content: center;
  917. align-items: center;
  918. .status-txt{
  919. padding: 8px 12px;
  920. text-align: center;
  921. margin-right: 12rpx;
  922. border-radius: 36rpx;
  923. background-color: #fff;
  924. font-size: 28rpx;
  925. }
  926. .stauts-sel{
  927. background-color: #007AFF;
  928. color: #fff;
  929. }
  930. }
  931. </style>