application-list.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. /**
  2. * 「我的申请」/「申请记录」列表页共用逻辑
  3. *
  4. * 两个页面只有数据源不同:
  5. * - 我的申请(pages/hr/apply/list):GET /hr/ess/applications
  6. * - 申请记录(pages/hr/apply/record,管理端):GET /hr/ess/admin/applications
  7. * 跳转逻辑完全一致——都按 businessType 跳到对应提交页的只读详情;
  8. * 区别仅在于管理端恒为只读(调用 openEssApplication 时带 admin: true)。
  9. */
  10. import {
  11. APPROVAL_STATUS_FILTERS,
  12. adaptEssApplication,
  13. listAdminApplications,
  14. listMyApplications,
  15. openEssApplication,
  16. } from "@/api/hr/essApplication";
  17. export const applicationListMixin = {
  18. data() {
  19. return {
  20. // 子页面覆盖:true 使用管理端接口
  21. adminMode: false,
  22. loading: false,
  23. loadingMore: false,
  24. tabs: APPROVAL_STATUS_FILTERS,
  25. tabIndex: 0,
  26. keyword: "",
  27. typeFilter: "",
  28. // 类型筛选:与其它选择器同款的底部滚轮(页面里挂 u-picker)
  29. typePickerShow: false,
  30. typePickerItems: [],
  31. list: [],
  32. pageNum: 1,
  33. size: 20,
  34. total: 0,
  35. finished: false,
  36. };
  37. },
  38. computed: {
  39. statusFilter() {
  40. return (this.tabs[this.tabIndex] || {}).value || "";
  41. },
  42. /** 类型筛选项来自已加载数据,避免额外请求字典 */
  43. typeOptions() {
  44. const map = new Map();
  45. this.list.forEach((item) => {
  46. if (!item.businessType) return;
  47. if (!map.has(item.businessType)) {
  48. map.set(item.businessType, item.businessTypeName || item.businessType);
  49. }
  50. });
  51. return Array.from(map.entries()).map(([value, label]) => ({ value, label }));
  52. },
  53. /** u-picker 的单列选项 */
  54. typePickerColumns() {
  55. return this.typePickerItems.map((item) => item.label);
  56. },
  57. typeFilterLabel() {
  58. if (!this.typeFilter) return "全部类型";
  59. const hit = this.typeOptions.find((item) => item.value === this.typeFilter);
  60. return hit ? hit.label : "全部类型";
  61. },
  62. hasMore() {
  63. return !this.finished && this.list.length < this.total;
  64. },
  65. emptyText() {
  66. if (this.loading) return "加载中…";
  67. return this.adminMode ? "暂无申请记录" : "暂无相关申请";
  68. },
  69. },
  70. onShow() {
  71. // 从详情页返回时刷新,保证新状态立即可见
  72. if (!this.loading) this.loadList(true);
  73. },
  74. onReachBottom() {
  75. if (this.hasMore) this.loadList(false);
  76. },
  77. onPullDownRefresh() {
  78. this.loadList(true).then(() => uni.stopPullDownRefresh());
  79. },
  80. methods: {
  81. async loadList(reset = true) {
  82. if (reset) {
  83. if (this.loading) return;
  84. this.loading = true;
  85. this.pageNum = 1;
  86. this.finished = false;
  87. } else {
  88. if (this.loadingMore || !this.hasMore) return;
  89. this.loadingMore = true;
  90. this.pageNum += 1;
  91. }
  92. const params = {
  93. pageNum: this.pageNum,
  94. size: this.size,
  95. approvalStatus: this.statusFilter || undefined,
  96. businessType: this.typeFilter || undefined,
  97. keyword: this.keyword ? String(this.keyword).trim() : undefined,
  98. };
  99. try {
  100. const fetcher = this.adminMode ? listAdminApplications : listMyApplications;
  101. const page = await fetcher(params, false);
  102. const rows = (page.list || []).map(adaptEssApplication);
  103. this.list = reset ? rows : this.list.concat(rows);
  104. this.total = page.total || this.list.length;
  105. // 本页未满则认为是最后一页
  106. this.finished = rows.length < this.size;
  107. } catch (error) {
  108. if (reset) this.list = [];
  109. this.finished = true;
  110. uni.showToast({
  111. title: (error && error.message) || "申请列表加载失败",
  112. icon: "none",
  113. });
  114. } finally {
  115. this.loading = false;
  116. this.loadingMore = false;
  117. }
  118. },
  119. onTabChange(index) {
  120. if (this.tabIndex === index) return;
  121. this.tabIndex = index;
  122. this.loadList(true);
  123. },
  124. onKeywordConfirm() {
  125. // 想先收起键盘再查。注意别写成 (a && a.b)():当 a 不存在时该表达式是 undefined,
  126. // 后面再 () 就 TypeError,会把 loadList 一起中断 —— 表现为「点搜索没反应/报错,列表不变」。
  127. const input = this.$refs && this.$refs.keywordInput;
  128. if (input && typeof input.blur === "function") input.blur();
  129. this.loadList(true);
  130. },
  131. /** 类型筛选(u-picker 底部滚轮,替代 actionSheet) */
  132. chooseType() {
  133. this.typePickerItems = [{ value: "", label: "全部类型" }].concat(this.typeOptions);
  134. this.typePickerShow = true;
  135. },
  136. onTypeConfirm(e) {
  137. const index = (e && e.indexs && e.indexs[0]) || 0;
  138. this.typePickerShow = false;
  139. const picked = this.typePickerItems[index];
  140. if (!picked) return;
  141. this.typeFilter = picked.value;
  142. this.loadList(true);
  143. },
  144. /** 打开「申请详情」:按 businessType 跳到对应页面的只读详情(管理端恒只读) */
  145. openItem(row) {
  146. openEssApplication(row, { admin: this.adminMode });
  147. },
  148. /**
  149. * 打开「审批流程」:公共流程详情页(与首页待办点进去的是同一个页面)。
  150. * 没有流程实例时给提示,不跳空白页。
  151. */
  152. openFlow(row) {
  153. const processInstanceId = row && row.processInstanceId;
  154. if (!processInstanceId) {
  155. uni.showToast({ title: "该申请暂无审批流程", icon: "none" });
  156. return;
  157. }
  158. uni.navigateTo({
  159. url: `/pages/home/wt/components/detail?processInstanceId=${processInstanceId}`,
  160. });
  161. },
  162. },
  163. };