todo_list.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738
  1. <template>
  2. <view class="todo-page">
  3. <!-- 待办列表 -->
  4. <scroll-view class="todo-container" :scroll-y="needScroll" refresher-enabled :refresher-triggered="refreshing"
  5. @refresherrefresh="onRefresh" @refresherrestore="onRefreshRestore" @click="closeAllSwipeStates">
  6. <view class="todo-content">
  7. <!-- 草稿箱 - 固定顶部 -->
  8. <view v-if="draftList.length > 0" class="draft-container">
  9. <!-- 文件夹标签页效果 -->
  10. <view class="folder-tab">
  11. <!-- 底层:100%宽度的橙色线 -->
  12. <view class="tab-base-line orange"></view>
  13. <!-- 上层:左侧梯形白色区域 -->
  14. <view class="tab-trapezoid">
  15. <!-- 梯形内的橙色线 -->
  16. <view class="tab-inner-line orange" v-if="!draftExpanded"></view>
  17. </view>
  18. <!-- 右侧:二分之一高度的橙色线 -->
  19. <view class="tab-right-line orange"></view>
  20. </view>
  21. <!-- 草稿箱内容 -->
  22. <view class="draft-section">
  23. <view class="draft-header">
  24. <view class="draft-title">
  25. <Icon name="icon-caogaoxiang" size="38" color="#575d6d" />
  26. <text class="title-text">草稿箱</text>
  27. </view>
  28. <text class="count-badge" @click="toggleDraft">{{ draftList.length }}</text>
  29. </view>
  30. <!-- 草稿内容 -->
  31. <view v-if="draftExpanded" class="draft-content">
  32. <view v-for="(item, index) in draftList" :key="index" class="draft-item-wrapper"
  33. :class="{ 'swiped': swipeStates[`draft-${index}`] }"
  34. @touchstart="hasActions(item) ? handleTouchStart($event, `draft-${index}`) : null"
  35. @touchmove="hasActions(item) ? handleTouchMove($event, `draft-${index}`) : null"
  36. @touchend="hasActions(item) ? handleTouchEnd($event, `draft-${index}`) : null">
  37. <view class="draft-item" :class="{ 'has-actions': hasActions(item) }" @click="handleTodoClick(item)">
  38. <view class="draft-icon">
  39. <view class="dot"></view>
  40. </view>
  41. <view class="draft-title">{{ item.name }}</view>
  42. <view class="draft-time-wrapper">
  43. <view class="draft-time">
  44. <text>{{ formatTime(item.time) }}</text>
  45. <text>{{ formatDate(item.time) }}</text>
  46. </view>
  47. </view>
  48. </view>
  49. <!-- 左滑显示的操作按钮 -->
  50. <view class="swipe-actions">
  51. <view class="action-btn edit-btn" @click.stop="handleEditDraft(item)">
  52. 编辑
  53. </view>
  54. <view class="action-btn delete-btn" @click.stop="handleDeleteDraft(item)">
  55. 删除
  56. </view>
  57. </view>
  58. </view>
  59. </view>
  60. </view> <!-- 草稿箱内容结束 -->
  61. </view> <!-- 草稿箱容器结束 -->
  62. <!-- 审核分组列表 -->
  63. <view v-for="(group, groupIndex) in approvalGroups" :key="groupIndex" class="approval-group">
  64. <!-- 文件夹类型(多级) -->
  65. <view v-if="group.type === 'folder'" class="folder-container">
  66. <!-- 文件夹标签页效果 -->
  67. <view class="folder-tab">
  68. <!-- 底层:100%宽度的灰色线 -->
  69. <view class="tab-base-line gray"></view>
  70. <!-- 上层:左侧梯形白色区域 -->
  71. <view class="tab-trapezoid">
  72. <!-- 梯形内的灰色线 -->
  73. <view class="tab-inner-line gray" v-if="!group.expanded"></view>
  74. </view>
  75. <!-- 右侧:二分之一高度的灰色线 -->
  76. <view class="tab-right-line gray"></view>
  77. </view>
  78. <!-- 文件夹内容 -->
  79. <view class="folder-item">
  80. <view class="folder-header" @click="handleTodoClick(group)">
  81. <view class="folder-title">
  82. <Icon name="icon-gongwen" size="38" color="#575d6d" />
  83. <text class="title-text">{{ group.name }}</text>
  84. </view>
  85. <text class="count-badge" @click.stop="toggleGroup(groupIndex)">{{ group.count }}</text>
  86. </view>
  87. <!-- 展开的子项 -->
  88. <view v-if="group.expanded" class="folder-content">
  89. <view v-for="(item, itemIndex) in group.items" :key="itemIndex" class="sub-item-wrapper"
  90. :class="{ 'swiped': swipeStates[`folder-${groupIndex}-${itemIndex}`] }"
  91. @touchstart="hasActions(item) ? handleTouchStart($event, `folder-${groupIndex}-${itemIndex}`) : null"
  92. @touchmove="hasActions(item) ? handleTouchMove($event, `folder-${groupIndex}-${itemIndex}`) : null"
  93. @touchend="hasActions(item) ? handleTouchEnd($event, `folder-${groupIndex}-${itemIndex}`) : null">
  94. <view class="sub-item" :class="{ 'has-actions': hasActions(item) }" @click="handleTodoClick(item)">
  95. <view class="sub-icon">
  96. <view class="dot"></view>
  97. </view>
  98. <view class="sub-title">{{ item.name }}</view>
  99. <view class="sub-time-wrapper">
  100. <view class="sub-time">
  101. <text>{{ formatTime(item.time) }}</text>
  102. <text>{{ formatDate(item.time) }}</text>
  103. </view>
  104. </view>
  105. </view>
  106. <!-- 左滑显示的操作按钮 -->
  107. <view class="swipe-actions">
  108. <view v-for="(action, actionIndex) in item.actions" :key="actionIndex" class="action-btn"
  109. @click.stop="handleActionClick(action, item)">
  110. {{ action.name }}
  111. </view>
  112. </view>
  113. </view>
  114. </view>
  115. </view> <!-- 文件夹内容结束 -->
  116. </view> <!-- 文件夹容器结束 -->
  117. <!-- 文件类型(单级) -->
  118. <view v-else class="file-item">
  119. <view class="file-item-wrapper" :class="{ 'swiped': swipeStates[`file-${groupIndex}`] }"
  120. @touchstart="hasActions(group) ? handleTouchStart($event, `file-${groupIndex}`) : null"
  121. @touchmove="hasActions(group) ? handleTouchMove($event, `file-${groupIndex}`) : null"
  122. @touchend="hasActions(group) ? handleTouchEnd($event, `file-${groupIndex}`) : null">
  123. <view class="file-content" :class="{ 'has-actions': hasActions(group) }" @click="handleTodoClick(group)">
  124. <view class="file-icon">
  125. <Icon name="icon-daiban" size="38" color="#575d6d" />
  126. </view>
  127. <view class="file-title">{{ group.name }}</view>
  128. <view class="file-time-wrapper">
  129. <view class="file-time">
  130. <text>{{ formatTime(group.time) }}</text>
  131. <text>{{ formatDate(group.time) }}</text>
  132. </view>
  133. </view>
  134. </view>
  135. <!-- 左滑显示的操作按钮 -->
  136. <view class="swipe-actions">
  137. <view v-for="(action, actionIndex) in group.actions" :key="actionIndex" class="action-btn"
  138. @click.stop="handleActionClick(action, group)">
  139. {{ action.name }}
  140. </view>
  141. </view>
  142. </view>
  143. </view>
  144. </view>
  145. <!-- 空状态 -->
  146. <view v-if="draftList.length === 0 && approvalGroups.length === 0" class="empty-state">
  147. <Icon name="icon-wujilu" size="120" color="#ccc" />
  148. <text class="empty-text">暂无待办事项</text>
  149. </view>
  150. </view>
  151. </scroll-view>
  152. </view>
  153. </template>
  154. <script setup>
  155. import { ref, onMounted, computed } from 'vue'
  156. import { onShow } from '@dcloudio/uni-app'
  157. import { todoApi } from '@/api/todo'
  158. import Icon from '@/components/icon/index.vue'
  159. import { goTo } from '@/utils/navigation'
  160. import { formatDate as utilFormatDate } from '@/utils/date'
  161. // 待办数据
  162. const draftList = ref([]) // 草稿箱数据
  163. const approvalGroups = ref([]) // 审核分组数据
  164. const loading = ref(false)
  165. // 展开状态
  166. const draftExpanded = ref(true) // 草稿箱展开状态
  167. // 左滑状态管理
  168. const swipeStates = ref({}) // 记录每个项目的滑动状态
  169. const touchStartX = ref(0) // 触摸开始位置
  170. const touchStartY = ref(0) // 触摸开始Y位置
  171. // 刷新状态
  172. const refreshing = ref(false)
  173. // 计算是否需要滚动
  174. const needScroll = computed(() => {
  175. // 如果数据很少,就不允许滚动
  176. const totalItems = draftList.value.length + approvalGroups.value.length
  177. return totalItems > 5 // 超过5个项目才允许滚动
  178. })
  179. /**
  180. * 切换草稿箱展开/收起
  181. */
  182. const toggleDraft = () => {
  183. draftExpanded.value = !draftExpanded.value
  184. // 如果收起,关闭所有草稿项的左滑状态
  185. if (!draftExpanded.value) {
  186. draftList.value.forEach((_, index) => {
  187. swipeStates.value[`draft-${index}`] = false
  188. })
  189. }
  190. }
  191. /**
  192. * 切换分组展开/收起
  193. */
  194. const toggleGroup = (groupIndex) => {
  195. const group = approvalGroups.value[groupIndex]
  196. group.expanded = !group.expanded
  197. // 如果收起,关闭该分组内所有子项的左滑状态
  198. if (!group.expanded && group.items) {
  199. group.items.forEach((_, itemIndex) => {
  200. swipeStates.value[`folder-${groupIndex}-${itemIndex}`] = false
  201. })
  202. }
  203. // 同时关闭该分组本身的左滑状态(如果是文件类型)
  204. swipeStates.value[`file-${groupIndex}`] = false
  205. }
  206. /**
  207. * 格式化时间 - 使用工具函数
  208. */
  209. const formatTime = (timeStr) => {
  210. if (!timeStr) return ''
  211. try {
  212. // 使用工具函数解析时间,返回 HH:mm 格式
  213. return utilFormatDate(timeStr, 'HH:mm')
  214. } catch (error) {
  215. console.error('时间格式化错误:', error, timeStr)
  216. return ''
  217. }
  218. }
  219. /**
  220. * 格式化日期 - 使用工具函数
  221. */
  222. const formatDate = (timeStr) => {
  223. if (!timeStr) return ''
  224. try {
  225. // 使用工具函数解析日期,返回 MM/dd 格式
  226. return utilFormatDate(timeStr, 'MM/DD')
  227. } catch (error) {
  228. console.error('日期格式化错误:', error, timeStr)
  229. return ''
  230. }
  231. }
  232. /**
  233. * 处理待办项点击
  234. */
  235. const handleTodoClick = (item) => {
  236. //console.log('点击待办项:', item)
  237. // 根据项目类型选择合适的服务
  238. let targetService = null
  239. if (item.type === 'folder' && item.cservice) {
  240. // 批量处理项目使用 cservice
  241. targetService = item.cservice
  242. } else if (item.service) {
  243. // 单个审核项目使用 service
  244. targetService = item.service
  245. }
  246. if (targetService) {
  247. // 根据service配置跳转
  248. const parts = targetService.dest.split('_')
  249. const dir = parts[0]
  250. const mobilePage = `/pages/${dir}/${targetService.dest}`
  251. goTo(mobilePage, targetService.param || {})
  252. } else {
  253. console.warn('没有找到可用的服务配置:', item)
  254. }
  255. }
  256. /**
  257. * 处理操作按钮点击
  258. */
  259. const handleActionClick = (action, item) => {
  260. //console.log('点击操作按钮:', action, item)
  261. if (action.service) {
  262. const parts = action.service.dest.split('_')
  263. const dir = parts[0]
  264. const mobilePage = `/pages/${dir}/${action.service.dest}`
  265. goTo(mobilePage, action.service.param || {})
  266. }
  267. }
  268. /**
  269. * 编辑草稿
  270. */
  271. const handleEditDraft = (item) => {
  272. //console.log('编辑草稿:', item)
  273. handleTodoClick(item)
  274. }
  275. /**
  276. * 删除草稿
  277. */
  278. const handleDeleteDraft = (item) => {
  279. //console.log('删除草稿:', item)
  280. uni.showModal({
  281. title: '确认删除',
  282. content: `确定要删除草稿"${item.name}"吗?`,
  283. success: (res) => {
  284. if (res.confirm) {
  285. // TODO: 调用删除API
  286. const index = draftList.value.findIndex(draft => draft.id === item.id)
  287. if (index > -1) {
  288. draftList.value.splice(index, 1)
  289. uni.showToast({
  290. title: '删除成功',
  291. icon: 'success'
  292. })
  293. }
  294. }
  295. }
  296. })
  297. }
  298. /**
  299. * 处理触摸开始
  300. */
  301. const handleTouchStart = (event, itemId) => {
  302. touchStartX.value = event.touches[0].clientX
  303. touchStartY.value = event.touches[0].clientY
  304. }
  305. /**
  306. * 处理触摸移动
  307. */
  308. const handleTouchMove = (event, itemId) => {
  309. const deltaX = event.touches[0].clientX - touchStartX.value
  310. const deltaY = event.touches[0].clientY - touchStartY.value
  311. // 判断是否为水平滑动
  312. if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > 30) {
  313. event.preventDefault()
  314. if (deltaX < -50) {
  315. // 左滑,先关闭其他所有项目,再显示当前项目
  316. closeAllSwipeStates()
  317. swipeStates.value[itemId] = true
  318. } else if (deltaX > 50) {
  319. // 右滑,隐藏操作按钮
  320. swipeStates.value[itemId] = false
  321. }
  322. }
  323. }
  324. /**
  325. * 关闭所有左滑状态
  326. */
  327. const closeAllSwipeStates = () => {
  328. Object.keys(swipeStates.value).forEach(key => {
  329. swipeStates.value[key] = false
  330. })
  331. }
  332. /**
  333. * 下拉刷新
  334. */
  335. const onRefresh = async () => {
  336. refreshing.value = true
  337. try {
  338. // 关闭所有左滑状态
  339. closeAllSwipeStates()
  340. // 关闭所有展开状态
  341. draftExpanded.value = false
  342. approvalGroups.value.forEach(group => {
  343. group.expanded = false
  344. })
  345. // 重新加载数据
  346. await loadTodoData()
  347. uni.showToast({
  348. title: '刷新成功',
  349. icon: 'success'
  350. })
  351. } catch (error) {
  352. console.error('刷新失败:', error)
  353. uni.showToast({
  354. title: '刷新失败',
  355. icon: 'error'
  356. })
  357. } finally {
  358. refreshing.value = false
  359. }
  360. }
  361. /**
  362. * 刷新恢复
  363. */
  364. const onRefreshRestore = () => {
  365. refreshing.value = false
  366. }
  367. /**
  368. * 判断项目是否有操作
  369. */
  370. const hasActions = (item) => {
  371. return item.actions && item.actions.length > 0
  372. }
  373. /**
  374. * 处理触摸结束
  375. */
  376. const handleTouchEnd = (event, itemId) => {
  377. // 可以在这里添加一些收尾逻辑
  378. }
  379. /**
  380. * 加载待办数据
  381. */
  382. const loadTodoData = async () => {
  383. try {
  384. loading.value = true
  385. // //console.log('加载待办数据')
  386. const res = await todoApi.getTaskHomep()
  387. //console.log('待办数据:', res.data)
  388. // 使用模拟数据(已注释,现在使用真实数据)
  389. // const mockData = getMockData()
  390. // //console.log('模拟数据:', mockData)
  391. // 分别处理草稿和审核数据
  392. // draftList.value = processDraftData(mockData.cgList)
  393. // approvalGroups.value = processApprovalData(mockData.shList)
  394. approvalGroups.value = processApprovalData(res.data.shList)
  395. // //console.log('处理后的草稿数据:', draftList.value)
  396. // //console.log('处理后的审核数据:', approvalGroups.value)
  397. } catch (error) {
  398. console.error('加载待办数据失败:', error)
  399. uni.showToast({
  400. title: '加载失败',
  401. icon: 'none'
  402. })
  403. // 设置空数据
  404. draftList.value = []
  405. approvalGroups.value = []
  406. } finally {
  407. loading.value = false
  408. }
  409. }
  410. /**
  411. * 处理草稿数据
  412. */
  413. const processDraftData = (cgList) => {
  414. return cgList.filter(item => !item.branch).map((item, index) => ({
  415. id: item.id,
  416. name: item.mc,
  417. time: item.sqsj,
  418. service: item.updateService,
  419. // 测试:只有部分草稿有操作(偶数索引的有操作)
  420. actions: index % 2 === 0 ? [
  421. { name: '编辑', type: 'edit' },
  422. { name: '删除', type: 'delete' }
  423. ] : []
  424. }))
  425. }
  426. /**
  427. * 处理审核数据 - 按照PC端的树形结构逻辑
  428. */
  429. const processApprovalData = (shList) => {
  430. //console.log('处理审核数据:', shList)
  431. if (!shList || !Array.isArray(shList)) {
  432. console.warn('shList 不是数组:', shList)
  433. return []
  434. }
  435. const groups = []
  436. const parentMap = new Map() // 存储批量处理项目作为父节点
  437. // 第一遍:收集所有批量处理项目作为父节点
  438. shList.forEach((innerList, groupIndex) => {
  439. if (innerList && Array.isArray(innerList) && innerList.length > 0) {
  440. innerList.forEach(item => {
  441. if (item.mark === 'pl') {
  442. const parentId = item.plid || item.index || `pl_${groupIndex}`
  443. parentMap.set(parentId, {
  444. id: parentId,
  445. name: item.name,
  446. type: 'folder',
  447. hasChildren: true,
  448. expanded: false,
  449. count: 0, // 稍后计算
  450. time: item.time, // 批量处理使用 time 字段
  451. cservice: item.cservice, // 批量操作服务
  452. items: [],
  453. // 批量处理项目通常没有单独的操作按钮
  454. actions: []
  455. })
  456. }
  457. })
  458. }
  459. })
  460. // 第二遍:处理所有项目,根据 plid 归类
  461. shList.forEach((innerList) => {
  462. if (innerList && Array.isArray(innerList) && innerList.length > 0) {
  463. innerList.forEach((item) => {
  464. if (item.mark === 'sh') {
  465. // 检查是否有对应的父节点
  466. const parentId = item.plid
  467. const parentNode = parentMap.get(parentId)
  468. if (parentNode) {
  469. // 作为子项目添加到对应的父节点
  470. parentNode.items.push({
  471. id: item.objectId || item.taskid,
  472. name: item.name,
  473. time: item.jzsj || item.fssj, // 优先使用截止时间,没有则用发送时间
  474. isOverdue: !!item.jzsj, // 有截止时间说明可能逾期
  475. service: item.service,
  476. // 根据是否有 iservice 决定是否显示操作按钮
  477. actions: item.iservice ? [
  478. { name: '查看', service: item.iservice }
  479. ] : []
  480. })
  481. parentNode.count = parentNode.items.length
  482. } else {
  483. // 独立的文件项目(没有对应的父节点)
  484. groups.push({
  485. id: item.objectId || item.taskid,
  486. name: item.name,
  487. type: 'file',
  488. hasChildren: false,
  489. expanded: true,
  490. count: 1,
  491. time: item.jzsj || item.fssj, // 优先使用截止时间,没有则用发送时间
  492. isOverdue: !!item.jzsj,
  493. service: item.service,
  494. // 根据是否有 iservice 决定是否显示操作按钮
  495. actions: item.iservice ? [
  496. { name: '查看', service: item.iservice }
  497. ] : []
  498. })
  499. }
  500. }
  501. })
  502. }
  503. })
  504. // 第三遍:将所有父节点添加到结果中
  505. parentMap.forEach(parent => {
  506. if (parent.items.length > 0) {
  507. groups.push(parent)
  508. }
  509. })
  510. //console.log('处理后的分组数据:', groups)
  511. //console.log('父节点映射:', parentMap)
  512. return groups
  513. }
  514. /**
  515. * 获取模拟数据
  516. */
  517. const getMockData = () => {
  518. return {
  519. // 草稿箱数据
  520. cgList: [
  521. {
  522. id: 'draft_1',
  523. mc: '车辆《粤A88888》的用车申请',
  524. sqsj: '2024-01-15 14:30:00',
  525. updateService: { dest: 'clyy_inp', param: {} }
  526. },
  527. {
  528. id: 'draft_2',
  529. mc: '于[部门]/单位名称]调整[某项工作]程的报批函',
  530. sqsj: '2024-01-14 09:15:00',
  531. updateService: { dest: 'qj_inp', param: {} }
  532. }
  533. ],
  534. // 审核列表数据
  535. shList: [
  536. // 单级审核项目
  537. [
  538. {
  539. objectId: 'sh_1',
  540. name: '车辆《粤A88888》的用车申请',
  541. beanName: '车辆申请',
  542. fssj: '2024-01-15 14:42:00',
  543. mark: 'sh',
  544. service: { dest: 'clyy_sh', param: {} },
  545. iservice: { name: 'clyy_view', title: '查看', dest: 'clyy_view', param: {} }
  546. }
  547. ],
  548. [
  549. {
  550. objectId: 'sh_2',
  551. name: '于[部门]/单位名称]调整[某项工作]程的报批函',
  552. beanName: '工作调整',
  553. fssj: '2024-01-15 14:42:00',
  554. mark: 'sh',
  555. service: { dest: 'gztz_sh', param: {} },
  556. iservice: { name: 'gztz_view', title: '查看', dest: 'gztz_view', param: {} }
  557. }
  558. ],
  559. // 多级审核项目(公文审批)
  560. [
  561. {
  562. objectId: 'pl_1',
  563. name: '申核《规章制度名称》(试行稿) 》并批准印发的请示',
  564. beanName: '公文审批',
  565. fssj: '2024-01-15 14:42:00',
  566. mark: 'pl',
  567. service: { dest: 'gw_sh', param: {} },
  568. iservice: { name: 'gw_view', title: '查看', dest: 'gw_view', param: {} }
  569. },
  570. {
  571. objectId: 'pl_2',
  572. name: '关于[设备采购/工程建设]项目预算及实施计划的申核报批',
  573. beanName: '公文审批',
  574. fssj: '2024-01-15 14:42:00',
  575. mark: 'pl',
  576. service: { dest: 'gw_sh2', param: {} },
  577. iservice: { name: 'gw_view2', title: '查看', dest: 'gw_view2', param: {} }
  578. },
  579. {
  580. objectId: 'pl_3',
  581. name: '关于[设备采购/工程建设]项目预算及实施计划的申核报批',
  582. beanName: '公文审批',
  583. fssj: '2024-01-15 14:42:00',
  584. mark: 'pl',
  585. service: { dest: 'gw_sh3', param: {} },
  586. iservice: { name: 'gw_view3', title: '查看', dest: 'gw_view3', param: {} }
  587. }
  588. ]
  589. ]
  590. }
  591. }
  592. // 生命周期处理函数
  593. const handleOnShow = () => {
  594. console.log('待办页面显示')
  595. // 页面显示时加载数据
  596. loadTodoData()
  597. }
  598. const handleOnHide = () => {
  599. console.log('待办页面隐藏')
  600. }
  601. const handleOnLoad = () => {
  602. console.log('待办页面加载')
  603. loadTodoData()
  604. }
  605. const handleOnUnload = () => {
  606. console.log('待办页面卸载')
  607. }
  608. // 页面加载时获取数据
  609. onMounted(() => {
  610. handleOnLoad()
  611. uni.loadFontFace({
  612. family: 'SSXinYiTi',
  613. source: 'url("https://m.hfdcschool.com/skin/mp_easy/fonts/056-ShangShouXinYiTi-2.woff")',
  614. success() {
  615. console.log('字体加载成功');
  616. },
  617. fail() {
  618. console.log('字体加载失败');
  619. },
  620. });
  621. })
  622. // 页面显示时更新数据
  623. onShow(() => {
  624. handleOnShow()
  625. uni.loadFontFace({
  626. family: 'SSXinYiTi',
  627. source: 'url("https://m.hfdcschool.com/skin/mp_easy/fonts/056-ShangShouXinYiTi-2.woff")',
  628. success() {
  629. console.log('字体加载成功');
  630. },
  631. fail() {
  632. console.log('字体加载失败');
  633. },
  634. });
  635. })
  636. // 下拉刷新
  637. const onPullDownRefresh = () => {
  638. loadTodoData().finally(() => {
  639. uni.stopPullDownRefresh()
  640. })
  641. }
  642. // 暴露生命周期方法供主容器调用
  643. defineExpose({
  644. onShow: handleOnShow,
  645. onHide: handleOnHide,
  646. onLoad: handleOnLoad,
  647. onUnload: handleOnUnload,
  648. onPullDownRefresh
  649. })
  650. </script>
  651. <style scoped lang="scss">
  652. @import './todo_list.scss';
  653. </style>