index.uvue 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947
  1. <template>
  2. <uni-navbar-lite :showLeft=false title="我的任务"></uni-navbar-lite>
  3. <view class="list-page">
  4. <!-- 搜索栏 -->
  5. <view class="search-bar">
  6. <view class="search-box">
  7. <image class="search-icon" src="/static/images/workbench/list/1.png" mode="aspectFit"></image>
  8. <input class="search-input" type="text" placeholder="搜索任务" v-model="keyword" @confirm="handleSearch" @blur="handleSearch" />
  9. <text v-if="keyword.length > 0" class="clear-icon" @click="clearSearch">✕</text>
  10. </view>
  11. </view>
  12. <view class="status-bar">
  13. <scroll-view class="scroll-view_H" direction="horizontal" :show-scrollbar="false">
  14. <view class="scroll-view-item_H uni-bg-red">
  15. <text class="status-txt" :class="{'stauts-sel': currentStatus == ''}" @click="switchStatus('')">全部</text>
  16. </view>
  17. <view class="scroll-view-item_H" v-if="!dealLoad">
  18. <text class="status-txt" :class="{'stauts-sel': currentStatus == '0'}" @click="switchStatus('0')">待执行</text>
  19. </view>
  20. <!-- <view class="scroll-view-item_H" v-if="!dealLoad">
  21. <text class="status-txt" :class="{'stauts-sel': currentStatus == '1'}" @click="switchStatus('1')">执行中</text>
  22. </view> -->
  23. <view class="scroll-view-item_H" v-if="!dealLoad">
  24. <text class="status-txt" :class="{'stauts-sel': currentStatus == '2'}" @click="switchStatus('2')">已完成</text>
  25. </view>
  26. <!-- <view class="scroll-view-item_H" v-if="!dealLoad">
  27. <text class="status-txt" :class="{'stauts-sel': currentStatus == '3'}" @click="switchStatus('3')">已超时</text>
  28. </view>
  29. <view class="scroll-view-item_H" v-if="!dealLoad">
  30. <text class="status-txt" :class="{'stauts-sel': currentStatus == '4'}" @click="switchStatus('4')">已逾期</text>
  31. </view> -->
  32. </scroll-view>
  33. </view>
  34. <!-- 列表内容 -->
  35. <common-list
  36. :dataList="dataList"
  37. :loading="loading"
  38. :refreshing="refreshing"
  39. :hasMore="hasMore"
  40. @refresh="handleRefresh"
  41. @loadMore="loadMore"
  42. @itemClick="handleView"
  43. >
  44. <template #default="{ item, index }">
  45. <view class="list-item">
  46. <view class="item-container">
  47. <view class="item-header">
  48. <text class="item-title">{{ (item as taskInfo).planName }}</text>
  49. <view class="task-badges">
  50. <text class="status-tag" :class="getTaskType(item)">{{ (item as taskInfo).planTypeText }}</text>
  51. <text class="status-tag" :class="getStatusClass((item as taskInfo).status)">{{ (item as taskInfo).statusText }}</text>
  52. </view>
  53. </view>
  54. <view class="info-row">
  55. <view class="info-label">
  56. <text class="text-gray">{{ (item as taskInfo).displayTime }}</text>
  57. </view>
  58. </view>
  59. <view class="info-row">
  60. <view class="info-label">
  61. <text class="text-gray">{{ (item as taskInfo).taskCount }}</text>
  62. </view>
  63. </view>
  64. <view class="btn-group">
  65. <view
  66. v-if="(item as taskInfo).status != '2' && (item as taskInfo).status != '4'"
  67. class="btn-primary info-value"
  68. @click.stop="handleItemClick(item,'start')"
  69. >
  70. <text class="btn-text">开始执行</text>
  71. </view>
  72. <view
  73. v-if="(item as taskInfo).status == '2' || (item as taskInfo).status == '4'"
  74. class="btn-primary info-value"
  75. @click.stop="handleItemClick(item,'detail')"
  76. >
  77. <text class="btn-text">查看</text>
  78. </view>
  79. </view>
  80. </view>
  81. </view>
  82. </template>
  83. </common-list>
  84. <!-- <custom-tabbar :current="1" :hide0="tabbar[0]" :hide1="tabbar[1]" :hide2="tabbar[2]" :hide3="tabbar[3]"/> -->
  85. </view>
  86. </template>
  87. <script setup lang="uts">
  88. import { ref, onBeforeUnmount, onMounted, reactive } from 'vue'
  89. import { onLoad, onShow,onHide } from '@dcloudio/uni-app';
  90. import type { SysDictData, DictDataResponse } from '../../types/dict'
  91. import {checkPermi, getUserInfo} from '../../utils/storage'
  92. import { getDictDataByType } from '../../api/dict/index'
  93. import { registerServer} from '../../api/index/index'
  94. import { getMyTaskList } from '../../api/task/list'
  95. type taskInfo = {
  96. taskId: Number
  97. planId: Number
  98. planType: string | null
  99. planName: string | null
  100. taskDate: string | null
  101. startTime: string | null
  102. endTime: string | null
  103. subjects: string | null
  104. machineryTypeName: string | null
  105. machineryCode: string | null
  106. machineryName: string | null
  107. machineries: string | null
  108. locations: string | null
  109. operator: string | null
  110. operatorId: Number
  111. status: string | null
  112. taskDesc: string | null
  113. sourceType: string | null
  114. locationCount: Number
  115. machineryCount: Number
  116. createTime: string | null
  117. updateTime: string | null
  118. // 前端计算字段
  119. planTypeText: string | null
  120. statusText: string | null
  121. displayTime: string | null
  122. taskCount: string | null
  123. checkType: string | null
  124. }
  125. const userId = ref<string>("")
  126. const roles = ref<string>('')
  127. // 列表数据
  128. const dataList = ref<taskInfo[]>([])
  129. let keyword = ref<string>("")
  130. const page = ref<number>(1)
  131. const pageSize: number = 10
  132. const hasMore = ref<boolean>(true)
  133. const loading = ref<boolean>(false)
  134. const refreshing = ref<boolean>(false)
  135. const total = ref<number>(0)
  136. let currentStatus = ref<string>('')
  137. let currentPlanType = ref<string>('')
  138. const statusDictList = ref<SysDictData[]>([]) // 工单状态字典列表
  139. const planTypeDictList = ref<SysDictData[]>([]) // 计划类型字典列表
  140. const tabbar = reactive([1,1,1,1])
  141. // 添加防重复请求的标志位(参考score/pending.uvue的实现)
  142. const isSearching = ref<boolean>(false)
  143. // 添加防重复刷新的标志
  144. const isRefreshing = ref<boolean>(false)
  145. // 添加刷新时间戳,用于防抖
  146. const lastRefreshTime = ref<number>(0)
  147. // 添加字典加载状态
  148. const dictLoaded = ref<boolean>(false)
  149. // 待处理工单加载
  150. const dealLoad = ref<boolean>(false)
  151. // 定时刷新定时器
  152. let refreshTimer: number | null = 0
  153. // 定时刷新间隔(毫秒)
  154. const REFRESH_INTERVAL = 10000 // 10秒
  155. // 获取任务类型
  156. const getTaskType = (item: any | null): string => {
  157. if (item == null) return ''
  158. const taskItem = item as taskInfo
  159. const rawType = taskItem.planType
  160. if (rawType == null || rawType.length == 0) return ''
  161. // 返回对应的状态类名
  162. return `type-${rawType}`
  163. }
  164. const getStatusClass = (rawStatus: string | null): string => {
  165. if (rawStatus == null) return ''
  166. // 返回对应的状态类名
  167. return `status-${rawStatus}`
  168. }
  169. const getStatusText = (rawStatus: string | null): string => {
  170. if (rawStatus == null || rawStatus.length == 0) return ''
  171. if (dictLoaded.value != true) {
  172. return rawStatus
  173. }
  174. const dictItem = statusDictList.value.find((dict: SysDictData) => dict.dictValue == rawStatus)
  175. return dictItem != null && dictItem.dictLabel != null ? dictItem.dictLabel : rawStatus
  176. }
  177. // 获取计划类型文本
  178. const getPlanTypeText = (planType: string | null): string => {
  179. if (planType == null || planType.length == 0) return ''
  180. if (dictLoaded.value != true) {
  181. return planType
  182. }
  183. const dictItem = planTypeDictList.value.find((dict: SysDictData) => dict.dictValue == planType)
  184. return dictItem != null && dictItem.dictLabel != null ? dictItem.dictLabel : planType
  185. }
  186. // 获取显示时间
  187. const getDisplayTime = (item: taskInfo): string | null => {
  188. if (item.taskDate == null || item.taskDate.length == 0) return null
  189. if (item.startTime != null && item.startTime.length > 0 && item.endTime != null && item.endTime.length > 0) {
  190. return `${item.taskDate} ${item.startTime}-${item.endTime}`
  191. }
  192. return item.taskDate
  193. }
  194. // 获取任务数量显示
  195. const getTaskCount = (item: taskInfo): string | null => {
  196. if (item.planType != null && item.planType == 'PATROL') {
  197. return `地点数量: ${item.locationCount != null ? item.locationCount : 0}个`
  198. } else {
  199. return `设备数量: ${item.machineryCount != null ? item.machineryCount : 0}台`
  200. }
  201. }
  202. // 获取检查类型
  203. const getCheckType = (item: taskInfo): string | null => {
  204. if (item.planType != null && item.planType == 'PATROL') {
  205. return 'area'
  206. } else {
  207. return 'device'
  208. }
  209. }
  210. // 获取任务状态字典列表
  211. const loadStatusDictList = async (): Promise<void> => {
  212. try {
  213. const result = await getDictDataByType('dv_task_status')
  214. const resultObj = result as UTSJSONObject
  215. if (resultObj['code'] == 200) {
  216. const data = resultObj['data'] as any[]
  217. const dictData: SysDictData[] = []
  218. if (data.length > 0) {
  219. for (let i = 0; i < data.length; i++) {
  220. const item = data[i] as UTSJSONObject
  221. const dictItem: SysDictData = {
  222. dictValue: item['dictValue'] as string | null,
  223. dictLabel: item['dictLabel'] as string | null,
  224. dictCode: null,
  225. dictSort: null,
  226. dictType: null,
  227. cssClass: null,
  228. listClass: null,
  229. isDefault: null,
  230. status: null,
  231. default: null,
  232. createTime: null,
  233. remark: null
  234. }
  235. dictData.push(dictItem)
  236. }
  237. }
  238. statusDictList.value = dictData
  239. dictLoaded.value = true
  240. }
  241. } catch (e: any) {
  242. console.error('获取任务状态字典失败:', e.message)
  243. dictLoaded.value = true
  244. }
  245. }
  246. // 获取计划类型字典列表
  247. const loadPlanTypeDictList = async (): Promise<void> => {
  248. try {
  249. const result = await getDictDataByType('dv_plan_type')
  250. const resultObj = result as UTSJSONObject
  251. if (resultObj['code'] == 200) {
  252. const data = resultObj['data'] as any[]
  253. const dictData: SysDictData[] = []
  254. if (data.length > 0) {
  255. for (let i = 0; i < data.length; i++) {
  256. const item = data[i] as UTSJSONObject
  257. const dictItem: SysDictData = {
  258. dictValue: item['dictValue'] as string | null,
  259. dictLabel: item['dictLabel'] as string | null,
  260. dictCode: null,
  261. dictSort: null,
  262. dictType: null,
  263. cssClass: null,
  264. listClass: null,
  265. isDefault: null,
  266. status: null,
  267. default: null,
  268. createTime: null,
  269. remark: null
  270. }
  271. dictData.push(dictItem)
  272. }
  273. }
  274. planTypeDictList.value = dictData
  275. }
  276. } catch (e: any) {
  277. console.error('获取计划类型字典失败:', e.message)
  278. }
  279. }
  280. // 加载列表数据
  281. const loadData = async (isRefresh: boolean | null, disablePullDown: boolean | null): Promise<void> => {
  282. // 防止重复请求的核心机制
  283. if (loading.value) {
  284. if (isRefresh != true) {
  285. refreshing.value = false;
  286. }
  287. return
  288. }
  289. const shouldRefresh = isRefresh != null ? isRefresh : false
  290. const shouldDisablePullDown = disablePullDown != null ? disablePullDown : false
  291. loading.value = true
  292. if (shouldRefresh && !shouldDisablePullDown) {
  293. page.value = 1
  294. refreshing.value = true
  295. } else if (shouldRefresh && shouldDisablePullDown) {
  296. page.value = 1
  297. refreshing.value = false
  298. } else {
  299. refreshing.value = false;
  300. }
  301. try {
  302. // 构建查询参数对象
  303. const queryParams: UTSJSONObject = {
  304. pageNum: page.value,
  305. pageSize: pageSize
  306. }
  307. // 添加当前用户ID过滤
  308. if (userId.value.length > 0) {
  309. queryParams['operatorId'] = userId.value
  310. }
  311. // 添加搜索关键字
  312. if (keyword.value.length > 0) {
  313. queryParams['planName'] = keyword.value
  314. }
  315. // 添加状态筛选
  316. /* if (currentStatus.value != 'all') {
  317. queryParams['status'] = currentStatus.value
  318. } */
  319. queryParams['status'] = currentStatus.value
  320. // 添加计划类型筛选
  321. if (currentPlanType.value.length > 0) {
  322. queryParams['planType'] = currentPlanType.value
  323. }
  324. const result = await getMyTaskList(queryParams)
  325. // 提取响应数据
  326. const resultObj = result as UTSJSONObject
  327. const code = resultObj['code'] as number
  328. const responseData = resultObj['rows'] as any[]
  329. const responseTotal = resultObj['total'] as number
  330. if (code == 200) {
  331. // 将 any[] 转换为 taskInfo[]
  332. const newData: taskInfo[] = []
  333. for (let i = 0; i < responseData.length; i++) {
  334. const item = responseData[i] as UTSJSONObject
  335. const taskItem: taskInfo = {
  336. taskId: item['taskId'] as Number,
  337. planId: item['planId'] as Number,
  338. planType: item['planType'] as string | null,
  339. planName: item['planName'] as string | null,
  340. taskDate: item['taskDate'] as string | null,
  341. startTime: item['startTime'] as string | null,
  342. endTime: item['endTime'] as string | null,
  343. subjects: item['subjects'] as string | null,
  344. machineryTypeName: item['machineryTypeName'] as string | null,
  345. machineryCode: item['machineryCode'] as string | null,
  346. machineryName: item['machineryName'] as string | null,
  347. machineries: item['machineries'] as string | null,
  348. locations: item['locations'] as string | null,
  349. operator: item['operator'] as string | null,
  350. operatorId: item['operatorId'] as Number,
  351. status: item['status'] as string | null,
  352. taskDesc: item['taskDesc'] as string | null,
  353. sourceType: item['sourceType'] as string | null,
  354. locationCount: item['locationCount'] != null ? (item['locationCount'] as Number) : 0,
  355. machineryCount: item['machineryCount'] != null ? (item['machineryCount'] as Number) : 0,
  356. createTime: item['createTime'] as string | null,
  357. updateTime: item['updateTime'] as string | null,
  358. // 前端计算字段初始化
  359. planTypeText: null,
  360. statusText: null,
  361. displayTime: null,
  362. taskCount: null,
  363. checkType: null
  364. }
  365. // 计算前端显示字段
  366. taskItem.planTypeText = getPlanTypeText(taskItem.planType)
  367. taskItem.statusText = getStatusText(taskItem.status)
  368. taskItem.displayTime = getDisplayTime(taskItem)
  369. taskItem.taskCount = getTaskCount(taskItem)
  370. taskItem.checkType = getCheckType(taskItem)
  371. newData.push(taskItem)
  372. }
  373. if (shouldRefresh) {
  374. dataList.value = newData
  375. } else {
  376. dataList.value = [...dataList.value, ...newData]
  377. }
  378. total.value = responseTotal
  379. hasMore.value = dataList.value.length < responseTotal
  380. } else {
  381. const msg = resultObj['msg'] as string | null
  382. uni.showToast({
  383. title: msg ?? '加载失败',
  384. icon: 'none'
  385. })
  386. }
  387. } catch (e: any) {
  388. uni.showToast({
  389. title: e.message ?? '加载失败',
  390. icon: 'none'
  391. })
  392. } finally {
  393. loading.value = false
  394. if (shouldRefresh) {
  395. refreshing.value = false;
  396. setTimeout(() => {
  397. isRefreshing.value = false;
  398. }, 50);
  399. }
  400. }
  401. }
  402. // 切换状态
  403. const switchStatus = (status: string): void => {
  404. // 添加防重复调用检查(参考score/pending.uvue的实现)
  405. if (loading.value) {
  406. return;
  407. }
  408. // 添加防重复请求检查
  409. if (isSearching.value) {
  410. return
  411. }
  412. isSearching.value = true
  413. currentStatus.value = status
  414. page.value = 1
  415. loadData(true, true) // 第二个参数为true表示禁用下拉刷新动画
  416. // 延迟重置标志位,确保请求发送后才允许下一次搜索
  417. setTimeout(() => {
  418. isSearching.value = false
  419. }, 100)
  420. }
  421. // 下拉刷新
  422. const handleRefresh = async (): Promise<void> => {
  423. console.log("handleRefresh被触发")
  424. // 防抖处理,避免频繁触发(参考score/pending.uvue的实现)
  425. const now = Date.now();
  426. if (now - lastRefreshTime.value < 1000) {
  427. refreshing.value = false;
  428. return;
  429. }
  430. lastRefreshTime.value = now;
  431. // 添加防重复调用检查
  432. if (loading.value || isRefreshing.value) {
  433. // 如果已经在加载或正在刷新,直接重置刷新状态
  434. refreshing.value = false;
  435. return;
  436. }
  437. // 设置刷新标志
  438. isRefreshing.value = true;
  439. try {
  440. await loadData(true, false); // 使用默认的下拉刷新行为
  441. } catch (error) {
  442. console.error('刷新失败:', error);
  443. refreshing.value = false;
  444. isRefreshing.value = false;
  445. }
  446. // 确保在一定时间后重置刷新标志,防止意外情况
  447. setTimeout(() => {
  448. isRefreshing.value = false
  449. }, 100) // 延迟重置,确保状态完全更新
  450. }
  451. // 加载更多
  452. const loadMore = (): void => {
  453. if (!hasMore.value || loading.value) {
  454. return
  455. }
  456. page.value++
  457. loadData(false, false)
  458. }
  459. // 搜索
  460. const handleSearch = (): void => {
  461. // 添加防重复调用检查(参考score/pending.uvue的实现)
  462. if (loading.value) {
  463. return;
  464. }
  465. // 添加防重复请求检查
  466. if (isSearching.value) {
  467. return
  468. }
  469. isSearching.value = true
  470. page.value = 1
  471. loadData(true, true) // 状态切换时禁用下拉刷新动画
  472. // 延迟重置标志位,确保请求发送后才允许下一次搜索
  473. setTimeout(() => {
  474. isSearching.value = false
  475. }, 100)
  476. }
  477. const handleSearchOnBlur = (): void => {
  478. handleSearch()
  479. }
  480. // 点击列表项
  481. const handleItemClick = (item: any | null, action: string): void => {
  482. if (item == null) return
  483. const taskItem = item as taskInfo
  484. if(action == 'start') {
  485. // 开始执行任务
  486. if(taskItem.checkType == 'device') {
  487. // 如果是设备检查类型,则跳转到设备清单页面
  488. uni.navigateTo({
  489. url: `/pages/task/device-list?taskId=${taskItem.taskId}&opType='execute'`
  490. });
  491. } else if(taskItem.checkType == 'area') {
  492. // 如果是地点巡查类型,则跳转到地点清单页面
  493. uni.navigateTo({
  494. url: `/pages/task/area-list?taskId=${taskItem.taskId}&opType='execute'`
  495. });
  496. }
  497. } else if(action == 'detail') {
  498. // 查看详情
  499. if(taskItem.checkType == 'device') {
  500. // 如果是设备检查类型,则跳转到设备清单页面
  501. uni.navigateTo({
  502. url: `/pages/task/device-list?taskId=${taskItem.taskId}&opType='view'`
  503. });
  504. } else if(taskItem.checkType == 'area') {
  505. // 如果是地点巡查类型,则跳转到地点清单页面
  506. uni.navigateTo({
  507. url: `/pages/task/area-list?taskId=${taskItem.taskId}&opType='view'`
  508. });
  509. }
  510. }
  511. }
  512. // 点击列表项
  513. const handleView = (item: any | null): void => {
  514. // handleItemClick(item, 'detail')
  515. // if (item == null) return
  516. // const taskItem = item as taskInfo
  517. // if(taskItem.checkType == 'device') {
  518. // // 如果是设备检查类型,则跳转到设备清单页面
  519. // const taskName = taskItem.taskName != null ? taskItem.taskName : '';
  520. // uni.navigateTo({
  521. // url: `/pages/task/device-list?taskId=${taskItem.id}&taskName=${encodeURIComponent(taskName)}`
  522. // });
  523. // } else if(taskItem.checkType == 'area') {
  524. // // 如果是地点巡查类型,则跳转到地点清单页面
  525. // const taskName = taskItem.taskName != null ? taskItem.taskName : '';
  526. // uni.navigateTo({
  527. // url: `/pages/task/area-list?taskId=${taskItem.id}&taskName=${encodeURIComponent(taskName)}`
  528. // });
  529. // } else {
  530. // // 其他类型任务的处理
  531. // uni.showToast({
  532. // title: `查看详情: ${taskItem.taskName}`,
  533. // icon: 'none'
  534. // });
  535. // }
  536. }
  537. // 清空搜索
  538. const clearSearch = (): void => {
  539. // 添加防重复调用检查(参考score/pending.uvue的实现)
  540. if (loading.value) {
  541. return;
  542. }
  543. // 添加防重复请求检查
  544. if (isSearching.value) {
  545. return
  546. }
  547. isSearching.value = true
  548. keyword.value = ""
  549. page.value = 1
  550. loadData(true, true) // 状态切换时禁用下拉刷新动画
  551. // 延迟重置标志位,确保请求发送后才允许下一次搜索
  552. setTimeout(() => {
  553. isSearching.value = false
  554. }, 100)
  555. }
  556. const registerPush =() =>{
  557. console.log("获取CID")
  558. uni.getPushClientId({
  559. success: (res) => {
  560. console.log(res.cid);
  561. registerServer(res.cid).then((result:any) => {
  562. console.log("注册成功");
  563. });
  564. },
  565. fail(err) {
  566. console.log(err)
  567. }
  568. })
  569. }
  570. // 停止定时刷新
  571. const stopAutoRefresh = (): void => {
  572. if (refreshTimer != null) {
  573. clearInterval(refreshTimer as number)
  574. refreshTimer = null
  575. }
  576. }
  577. // 启动定时刷新
  578. const startAutoRefresh = (): void => {
  579. // 清除已有的定时器
  580. stopAutoRefresh()
  581. // 创建新的定时器
  582. refreshTimer = setInterval(() => {
  583. console.log('定时刷新列表数据')
  584. // 只有在不在加载状态时才刷新
  585. if (!loading.value) {
  586. loadData(true, true)
  587. }
  588. }, REFRESH_INTERVAL)
  589. }
  590. // 初始化
  591. onLoad((options: any) => {
  592. const userInfo = getUserInfo()
  593. if (userInfo != null) {
  594. const userIdStr = userInfo['userId'].toString()
  595. userId.value = userIdStr
  596. roles.value = userInfo['roleNames'].toString()
  597. registerPush();
  598. }
  599. const params = options as UTSJSONObject
  600. const isDeal = params['isDeal'] as string | null
  601. if(isDeal != null) {
  602. dealLoad.value = true;
  603. }
  604. loadStatusDictList()
  605. loadPlanTypeDictList()
  606. currentStatus.value = '0'
  607. loadData(true, true) // 首次加载时禁用下拉刷新动画
  608. // 启动定时刷新
  609. startAutoRefresh()
  610. // 监听首页切换状态事件
  611. uni.$on('switchOrderStatus', (status: string) => {
  612. switchStatus(status)
  613. })
  614. })
  615. // 组件卸载前清理事件监听
  616. onBeforeUnmount(() => {
  617. refreshing.value = false
  618. loading.value = false
  619. isRefreshing.value = false
  620. // 停止定时刷新
  621. stopAutoRefresh()
  622. // 清理事件监听
  623. uni.$off('switchOrderStatus')
  624. })
  625. // 页面显示时检查是否需要刷新数据
  626. onShow(() => {
  627. // 检查是否有来自子页面的刷新标记
  628. const needRefresh = uni.getStorageSync('needRefresh')
  629. if (needRefresh == true) {
  630. // 清除标记
  631. uni.removeStorageSync('needRefresh')
  632. // 延迟一小段时间再执行刷新,避免状态冲突
  633. setTimeout(() => {
  634. loadData(true, false); // 使用默认的下拉刷新行为
  635. }, 500)
  636. }
  637. // 页面显示时重新启动定时刷新
  638. startAutoRefresh()
  639. })
  640. // 页面隐藏时停止定时刷新
  641. onHide(() => {
  642. stopAutoRefresh()
  643. })
  644. </script>
  645. <style lang="scss">
  646. .list-page {
  647. flex: 1;
  648. background-color: #e8f0f9;
  649. }
  650. .search-bar {
  651. padding: 20rpx 30rpx;
  652. background-color: #d7eafe;
  653. }
  654. .search-box {
  655. flex-direction: row;
  656. align-items: center;
  657. height: 72rpx;
  658. padding: 0 24rpx;
  659. background-color: #f5f5f5;
  660. border-radius: 36rpx;
  661. .search-icon {
  662. width: 32rpx;
  663. height: 32rpx;
  664. margin-right: 12rpx;
  665. }
  666. .search-input {
  667. flex: 1;
  668. font-size: 28rpx;
  669. color: #333333;
  670. }
  671. .clear-icon {
  672. margin-left: 12rpx;
  673. font-size: 28rpx;
  674. color: #999999;
  675. }
  676. }
  677. .status-bar{
  678. padding-bottom: 10px;
  679. padding-left:30rpx;
  680. background-color: #d7eafe;
  681. }
  682. .status-box {
  683. flex-direction: row;
  684. align-items: center;
  685. height: 72rpx;
  686. flex: 1;
  687. .status-txt{
  688. padding: 8px 12px;
  689. text-align: center;
  690. margin-right: 12rpx;
  691. border-radius: 36rpx;
  692. background-color: #fff;
  693. font-size: 28rpx;
  694. // cursor: pointer;
  695. }
  696. .stauts-sel{
  697. background-color: #007AFF;
  698. color: #fff;
  699. }
  700. }
  701. .list-item {
  702. margin: 12rpx 30rpx;
  703. background-color: #ffffff;
  704. border-radius: 16rpx;
  705. }
  706. .item-container {
  707. padding: 30rpx;
  708. }
  709. .item-header {
  710. flex-direction: row;
  711. align-items: flex-start;
  712. margin-bottom: 16rpx;
  713. justify-content: space-between; /* 主轴两端对齐 */
  714. min-height: 55rpx;
  715. .item-title {
  716. font-size: 30rpx;
  717. color: #333333;
  718. font-weight: bold;
  719. flex-wrap: wrap;
  720. flex: 0 1 75%;
  721. min-width: 0;
  722. }
  723. .task-badges {
  724. display: flex;
  725. flex-direction: row;
  726. align-items: center;
  727. gap: 10rpx; /* 添加间距 */
  728. }
  729. }
  730. .info-row {
  731. flex-direction: row;
  732. justify-content: space-between;
  733. align-items: center;
  734. .info-label {
  735. font-size: 26rpx;
  736. color: #666;
  737. }
  738. .info-value {
  739. font-size: 26rpx;
  740. // color: #666;
  741. }
  742. }
  743. .btn-group {
  744. flex-direction: row;
  745. align-items: center;
  746. justify-content: flex-end;
  747. margin-top: 20rpx;
  748. }
  749. .btn-primary {
  750. z-index: 999;
  751. border-radius: 10rpx;
  752. font-size: 24rpx;
  753. // white-space: nowrap;
  754. margin-left: 20rpx;
  755. background-color: #165DFF;
  756. line-height: 45rpx;
  757. color: #ffffff;
  758. .btn-text{
  759. color: #ffffff;
  760. font-size: 24rpx;
  761. padding: 5px 15px;
  762. }
  763. }
  764. .status-tag {
  765. padding: 8rpx 20rpx;
  766. border-radius: 20rpx;
  767. font-size: 24rpx;
  768. white-space: nowrap;
  769. // margin-left: 50rpx;
  770. // justify-content: flex-end;
  771. border: 1rpx solid;
  772. text-align: center;
  773. }
  774. /* 计划类型样式 */
  775. .type-CHECK {
  776. background-color: #ECF5FF;
  777. color: #409EFF;
  778. border-color: #d8ebff;
  779. }
  780. .type-PATROL {
  781. background-color: #F4F4F5;
  782. color: #909399;
  783. border-color: #e9e9eb;
  784. }
  785. .type-MAINTEN {
  786. background-color: #F0F9EB;
  787. color: #67C23A;
  788. border-color: #c2e7b0;
  789. }
  790. /* 任务状态样式 */
  791. .status-0 {
  792. background-color: #FDF6EC;
  793. color: #E6A23C;
  794. border-color: #faecd8;
  795. }
  796. .status-1 {
  797. background-color: #EBF5FF;
  798. color: #409EFF;
  799. border-color: #d8ebff;
  800. }
  801. .status-2 {
  802. background-color: #F0F9EB;
  803. color: #67C23A;
  804. border-color: #c2e7b0;
  805. }
  806. .status-3 {
  807. background-color: #FEF0F0;
  808. color: #F56C6C;
  809. border-color: #fbc4c4;
  810. }
  811. .status-4 {
  812. background-color: #FDF6EC;
  813. color: #E6A23C;
  814. border-color: #faecd8;
  815. }
  816. /* 保持其他原有样式 */
  817. .text-gray{
  818. font-size: 26rpx;
  819. color: #666;
  820. }
  821. .scroll-view_H {
  822. width: 100%;
  823. flex-direction: row;
  824. }
  825. .scroll-view-item_H {
  826. justify-content: center;
  827. align-items: center;
  828. .status-txt{
  829. padding: 8px 12px;
  830. text-align: center;
  831. margin-right: 12rpx;
  832. border-radius: 36rpx;
  833. background-color: #fff;
  834. font-size: 28rpx;
  835. }
  836. .stauts-sel{
  837. background-color: #007AFF;
  838. color: #fff;
  839. }
  840. }
  841. </style>