bpm.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. import { get, postJ, putJ } from "@/utils/request";
  2. import Vue from "vue";
  3. // BPM 任务结果枚举(后端 bpm_task.result:1 通过 / 2 不通过 / 3 取消 / 4 退回)
  4. const TASK_RESULT_LABEL = {
  5. 1: "已同意",
  6. 2: "已驳回",
  7. 3: "已取消",
  8. 4: "已退回",
  9. };
  10. // 兼容后端返回 { list } / { records } / 直接数组 三种分页结构
  11. function asList(data) {
  12. if (Array.isArray(data)) return data;
  13. if (data && Array.isArray(data.list)) return data.list;
  14. if (data && Array.isArray(data.records)) return data.records;
  15. return [];
  16. }
  17. // 去掉空串 / null / undefined,避免后端把空值当成有效查询条件
  18. function compact(object = {}) {
  19. return Object.keys(object).reduce((result, key) => {
  20. const value = object[key];
  21. if (value !== "" && value !== null && value !== undefined) result[key] = value;
  22. return result;
  23. }, {});
  24. }
  25. function normalizePage(res) {
  26. const data = res.data || {};
  27. const list = asList(data);
  28. const total = Number(
  29. data.count !== undefined ? data.count : data.total !== undefined ? data.total : list.length
  30. );
  31. return { list, total };
  32. }
  33. /**
  34. * 待办任务分页
  35. * POST /bpm/task/todo-page
  36. */
  37. export async function getTodoTaskPage(params = {}, loding = true) {
  38. const res = await postJ(
  39. Vue.prototype.apiUrl + "/bpm/task/todo-page",
  40. compact({
  41. pageNo: params.pageNo || 1,
  42. pageSize: params.pageSize || 20,
  43. name: params.name,
  44. processType: params.processType,
  45. }),
  46. loding
  47. );
  48. if (res.code == 0) {
  49. return normalizePage(res);
  50. }
  51. return Promise.reject(new Error(res.message));
  52. }
  53. /**
  54. * 已办任务分页
  55. * POST /bpm/task/done-page
  56. */
  57. export async function getDoneTaskPage(params = {}, loding = true) {
  58. const res = await postJ(
  59. Vue.prototype.apiUrl + "/bpm/task/done-page",
  60. compact({
  61. pageNo: params.pageNo || 1,
  62. pageSize: params.pageSize || 20,
  63. name: params.name,
  64. result: params.result,
  65. keyword: params.keyword,
  66. startUserName: params.startUserName,
  67. }),
  68. loding
  69. );
  70. if (res.code == 0) {
  71. return normalizePage(res);
  72. }
  73. return Promise.reject(new Error(res.message));
  74. }
  75. /**
  76. * 抄送我的流程分页
  77. * GET /bpm/process-instance/cc/my-page
  78. */
  79. export async function getCcProcessPage(params = {}, loding = true) {
  80. const res = await get(
  81. Vue.prototype.apiUrl + "/bpm/process-instance/cc/my-page",
  82. compact({
  83. pageNo: params.pageNo || 1,
  84. pageSize: params.pageSize || 20,
  85. processInstanceName: params.processInstanceName,
  86. }),
  87. loding
  88. );
  89. if (res.code == 0) {
  90. return normalizePage(res);
  91. }
  92. return Promise.reject(new Error(res.message));
  93. }
  94. /**
  95. * 流程实例详情
  96. * GET /bpm/process-instance/get?id=
  97. */
  98. export async function getProcessInstance(id, loding = true) {
  99. if (!id) return null;
  100. const res = await get(
  101. Vue.prototype.apiUrl + `/bpm/process-instance/get?id=${encodeURIComponent(String(id))}`,
  102. {},
  103. loding
  104. );
  105. if (res.code == 0) {
  106. return res.data || null;
  107. }
  108. return Promise.reject(new Error(res.message));
  109. }
  110. /**
  111. * 流程实例下的全部任务(审批记录)
  112. * GET /bpm/task/list-by-process-instance-id
  113. */
  114. export async function getTaskListByProcessInstanceId(processInstanceId, loding = true) {
  115. if (!processInstanceId) return [];
  116. const res = await get(
  117. Vue.prototype.apiUrl +
  118. `/bpm/task/list-by-process-instance-id?processInstanceId=${encodeURIComponent(String(processInstanceId))}`,
  119. {},
  120. loding
  121. );
  122. if (res.code == 0) {
  123. return asList(res.data);
  124. }
  125. return Promise.reject(new Error(res.message));
  126. }
  127. /**
  128. * 审批通过
  129. * PUT /bpm/task/approve
  130. */
  131. export async function approveTask({ id, reason = "" } = {}) {
  132. if (!id) throw new Error("缺少任务主键");
  133. const res = await putJ(
  134. Vue.prototype.apiUrl + "/bpm/task/approve",
  135. compact({ id, reason }),
  136. true
  137. );
  138. if (res.code == 0) {
  139. return res.data;
  140. }
  141. return Promise.reject(new Error(res.message));
  142. }
  143. /**
  144. * 审批驳回(原因必填)
  145. * PUT /bpm/task/reject
  146. */
  147. export async function rejectTask({ id, reason } = {}) {
  148. if (!id) throw new Error("缺少任务主键");
  149. const res = await putJ(
  150. Vue.prototype.apiUrl + "/bpm/task/reject",
  151. compact({ id, reason }),
  152. true
  153. );
  154. if (res.code == 0) {
  155. return res.data;
  156. }
  157. return Promise.reject(new Error(res.message));
  158. }
  159. /**
  160. * 归一化 BPM 任务,兼容不同字段命名
  161. */
  162. export function adaptBpmTask(vo = {}) {
  163. const instance = vo.processInstance || {};
  164. const resultCode = Number(vo.result);
  165. return {
  166. taskId: vo.id,
  167. processInstanceId: instance.id || vo.processInstanceId || "",
  168. processName: instance.name || "",
  169. startUserId: instance.startUserId,
  170. businessId: vo.businessId || "",
  171. businessCode: vo.businessCode || "",
  172. businessName: vo.businessName || "",
  173. businessType: vo.businessType || "",
  174. applicant: instance.startUserNickname || "",
  175. department: "",
  176. position: "",
  177. name: vo.name || "",
  178. viewRouter: vo.miniViewRouter || vo.pcViewRouter || "",
  179. handleRouter: vo.miniHandleRouter || vo.pcHandleRouter || "",
  180. reason: vo.reason || "",
  181. createTime: vo.createTime || "",
  182. handledAt: vo.endTime || vo.updateTime || "",
  183. resultLabel: TASK_RESULT_LABEL[resultCode] || "",
  184. raw: vo,
  185. };
  186. }
  187. /**
  188. * 我的流程实例分页(我发起的申请)
  189. * POST /bpm/process-instance/my-page
  190. * 入参:pageNo/pageSize + keyword/status/result/createTimeBegin/createTimeEnd(均为可选)
  191. */
  192. export async function getMyProcessInstancePage(params = {}, loding = true) {
  193. const res = await postJ(
  194. Vue.prototype.apiUrl + "/bpm/process-instance/my-page",
  195. compact({
  196. pageNo: params.pageNo || 1,
  197. pageSize: params.pageSize || 50,
  198. keyword: params.keyword,
  199. status: params.status,
  200. result: params.result,
  201. createTimeBegin: params.createTimeBegin,
  202. createTimeEnd: params.createTimeEnd,
  203. }),
  204. loding
  205. );
  206. if (res.code == 0) {
  207. return normalizePage(res);
  208. }
  209. return Promise.reject(new Error(res.message || "查询我的申请失败"));
  210. }
  211. // 流程实例结果枚举(BpmProcessInstanceResultEnum)
  212. const INSTANCE_RESULT_LABEL = {
  213. 1: "审批中", // 处理中
  214. 2: "已通过",
  215. 3: "已驳回", // 不通过
  216. 4: "已取消",
  217. 5: "已驳回", // 驳回(退回发起人)
  218. 6: "审批中", // 委派
  219. 7: "审批中", // 待后加签
  220. 8: "审批中", // 待前加签
  221. 9: "审批中", // 待前置任务完成
  222. 10: "已通过", // 任务被其他人审批完成
  223. };
  224. // 已知业务类型码 → 中文名(后端 businessType 为英文码;未识别的码原样透出)
  225. const BUSINESS_TYPE_LABELS = {
  226. ATTENDANCE_LEAVE: "请假申请",
  227. ATTENDANCE_OVERTIME: "加班申请",
  228. ATTENDANCE_TRIP: "出差申请",
  229. ATTENDANCE_OUTING: "外勤申请",
  230. ATTENDANCE_CARD_REPAIR: "补卡申请",
  231. RESIGNATION: "离职申请",
  232. PROFILE_CHANGE: "资料变更",
  233. };
  234. /**
  235. * 归一化「我发起的流程实例」为列表条目。
  236. * status:1 进行中 / 2 已完成 / 3 已取消(BpmProcessInstanceStatusEnum)
  237. */
  238. export function adaptMyProcess(vo = {}) {
  239. const statusCode = Number(vo.status);
  240. const resultCode = Number(vo.result);
  241. let statusLabel;
  242. if (statusCode === 1) statusLabel = "审批中";
  243. else if (statusCode === 3) statusLabel = "已取消";
  244. else statusLabel = INSTANCE_RESULT_LABEL[resultCode] || "已完成";
  245. const title = vo.businessName || vo.name || "";
  246. const code = vo.businessCode || vo.processInstanceNo || "";
  247. const type =
  248. BUSINESS_TYPE_LABELS[vo.businessType] ||
  249. vo.formName ||
  250. vo.categoryName ||
  251. vo.name ||
  252. "流程申请";
  253. return {
  254. id: vo.id,
  255. processInstanceId: vo.id,
  256. processNo: vo.processInstanceNo || "",
  257. type,
  258. title,
  259. summary: [title, code].filter(Boolean).join(" · ") || "—",
  260. businessId: vo.businessId || "",
  261. businessCode: code,
  262. createdAt: formatInstanceTime(vo.createTime),
  263. endTime: formatInstanceTime(vo.endTime),
  264. statusLabel,
  265. viewRouter: vo.pcViewRouter || "",
  266. raw: vo,
  267. };
  268. }
  269. function formatInstanceTime(value) {
  270. if (!value) return "";
  271. // 兼容 "2026-10-12 09:30:00" / ISO / 时间戳
  272. if (typeof value === "number") {
  273. const d = new Date(value);
  274. return isNaN(d.getTime()) ? "" : formatDateTimeLocal(d);
  275. }
  276. return String(value).replace("T", " ").slice(0, 16);
  277. }
  278. function formatDateTimeLocal(d) {
  279. const pad = (n) => String(n).padStart(2, "0");
  280. return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
  281. }
  282. /**
  283. * 抄送流程 VO → 列表条目(抄送只读,不参与审批操作)
  284. */
  285. export function adaptCcProcess(vo = {}) {
  286. return {
  287. id: vo.id,
  288. processInstanceId: vo.processInstanceId || "",
  289. processName: vo.processInstanceName || "",
  290. applicant: vo.startUserNickname || "",
  291. creatorName: vo.creatorNickname || "",
  292. reason: vo.reason || "",
  293. taskName: vo.taskName || "",
  294. createTime: vo.createTime || "",
  295. raw: vo,
  296. };
  297. }