tool.ts 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. import {
  2. alcoholAnalysisReport,
  3. back,
  4. edge,
  5. end,
  6. healthAnalysis,
  7. healthAnalysisReport,
  8. healthAnalysisScheme,
  9. ID_Analysis_Health,
  10. ID_Analysis_Pulse,
  11. ID_Analysis_TongueAndFace,
  12. ID_Back,
  13. ID_End,
  14. ID_Register,
  15. ID_Report_Alcohol,
  16. ID_Report_Health,
  17. ID_Report_Pulse,
  18. ID_Report_TongueAndFace,
  19. ID_Scheme_Health,
  20. ID_Start,
  21. pulseAnalysis,
  22. pulseAnalysisReport,
  23. register,
  24. start,
  25. tongueAndFaceAnalysis,
  26. tongueAndFaceAnalysisReport,
  27. } from './nodes/config';
  28. import type { LogicFlowGraphData } from '@/libs/logic-flow';
  29. const nodeRef = {
  30. [ID_Start]: 'screen',
  31. [ID_End]: 'screen',
  32. [ID_Register]: /* 建档页 */ 'patient_file',
  33. [ID_Analysis_Pulse]: /* 脉诊页 */ 'pulse_upload',
  34. [ID_Analysis_TongueAndFace]: /*拍照页*/ 'tongueface_upload',
  35. [ID_Analysis_Health]: /* 问诊页 */ 'tongueface_analysis',
  36. [ID_Report_Pulse]: /* 脉诊结果页 */ 'pulse_upload_result',
  37. [ID_Report_TongueAndFace]: /* 舌面诊分析报告页 */'tongueface_analysis_result',
  38. [ID_Report_Health]: /* 健康报告页 */ 'health_analysis',
  39. [ID_Scheme_Health]: /* 调理方案页 */ 'health_analysis_scheme',
  40. [ID_Report_Alcohol]: /* 酒精结果页 */ 'alcohol_upload_result',
  41. } as const;
  42. export type NodeId = keyof typeof nodeRef;
  43. export type Gather = { level: number; sourceNodeId: NodeId; targetNodeId: NodeId | typeof ID_Back; edgeId: string }[];
  44. const refNode = ((ref) => Object.keys(ref).reduce((record, key) => ((record[ref[<NodeId>key]] = <NodeId>key), record), {} as Record<string, NodeId>))(nodeRef);
  45. export function toFlowRequestData(gather: Gather, data: Record<string, any>) {
  46. const value: string[] = [];
  47. for (let i = 0; i < gather.length; i++) {
  48. const { targetNodeId } = gather[i];
  49. if (targetNodeId === ID_Back && i !== gather.length - 1) {
  50. value.push(value.pop()!.replace(/\?*$/, '?'));
  51. } else if (targetNodeId !== ID_Back && targetNodeId !== ID_End) value.push(nodeRef[targetNodeId]);
  52. }
  53. const getReportRequestData = (key: string) => data[key]?.elements ?? [];
  54. return {
  55. tabletProcessModules: value,
  56. tabletFileFields: data[ID_Register] ?? [],
  57. tabletRequiredPageOperationElements: [
  58. ...getReportRequestData(ID_Report_TongueAndFace),
  59. ...getReportRequestData(ID_Report_Health),
  60. ...getReportRequestData(ID_Scheme_Health),
  61. ] as string[],
  62. ...(data[ID_Start] as {
  63. partner: string;
  64. technicalSupporter: string;
  65. tabletDrainageImage: string;
  66. }),
  67. timestamp: Date.now(),
  68. } as const;
  69. }
  70. export type FlowRequestData = Partial<ReturnType<typeof toFlowRequestData>>;
  71. type ReportPrefixKey = 'tongueface_upload_report_page' | 'health_analysis_report_page' | 'health_analysis_scheme_page';
  72. export const Preset_Image_1 = `preset:1;el:btn;`;
  73. export const Preset_Image_2 = `preset:2;el:scan;`;
  74. export function fromFlowRequestData(data?: FlowRequestData) {
  75. const getReportRequestData = (key: ReportPrefixKey) => ({
  76. key,
  77. elements: Array.isArray(data?.tabletRequiredPageOperationElements) ? data.tabletRequiredPageOperationElements.filter((item) => item.startsWith(key)) : [],
  78. });
  79. const group = [
  80. [register({ requestData: Array.isArray(data?.tabletFileFields) ? data.tabletFileFields : [] })],
  81. [pulseAnalysis(), pulseAnalysisReport()],
  82. [tongueAndFaceAnalysis(), tongueAndFaceAnalysisReport({ requestData: getReportRequestData('tongueface_upload_report_page') })],
  83. [healthAnalysis()],
  84. [alcoholAnalysisReport()],
  85. [
  86. healthAnalysisReport({ requestData: getReportRequestData('health_analysis_report_page') }),
  87. healthAnalysisScheme({ requestData: getReportRequestData('health_analysis_scheme_page') }),
  88. ],
  89. [back()],
  90. ];
  91. const nodes = new Set<string>([ID_Start, ID_End]);
  92. const edges: LogicFlowGraphData['edges'] = [];
  93. const flow = Array.isArray(data?.tabletProcessModules) ? [...data.tabletProcessModules] : [];
  94. if (flow.length && flow[0] === nodeRef[ID_Start]) flow.shift();
  95. if (flow.length && flow[flow.length - 1] === nodeRef[ID_End]) flow.pop();
  96. flow.unshift(ID_Start);
  97. flow.push(ID_End);
  98. for (let i = 1; i < flow.length; i++) {
  99. const [source] = flow[i - 1].split(/[?:]/).filter(Boolean);
  100. const [target, title, countDown] = flow[i].split(/[?:]/).filter(Boolean);
  101. const optional = flow[i].includes('?');
  102. const sourceNodeId = refNode[source] ?? source;
  103. const targetNodeId = refNode[target] ?? target;
  104. nodes.add(sourceNodeId).add(targetNodeId);
  105. edges.push(edge(sourceNodeId, targetNodeId));
  106. if (optional) edges.push(edge(sourceNodeId, ID_End, { title, countDown }));
  107. }
  108. return {
  109. graph: {
  110. nodes: Array.from(nodes, (id) => {
  111. if (id === ID_Start)
  112. return start({
  113. requestData: {
  114. partner: data?.partner,
  115. technicalSupporter: data?.technicalSupporter,
  116. tabletDrainageImage: data?.tabletDrainageImage || Preset_Image_1,
  117. },
  118. });
  119. if (id === ID_End) return end();
  120. for (const items of group) for (const node of items) if (node.id == id) return node;
  121. return void 0;
  122. }).filter(Boolean),
  123. edges,
  124. } as LogicFlowGraphData,
  125. group,
  126. };
  127. }
  128. export const Field_Card = 'cardno';
  129. export const Field_Phone = 'phone';
  130. export function analysisRegisterFields(fields?: string[]) {
  131. const config = [
  132. [Field_Card, '身份证号', true],
  133. [Field_Phone, '手机号码', true],
  134. ['name', '姓名'],
  135. ['age', '年龄'],
  136. ['sex', '性别'],
  137. ['height', '身高'],
  138. ['weight', '体重'],
  139. ['womenSpecialPeriod', '女性特殊期'],
  140. ['isEasyAllergy', '容易过敏'],
  141. ['foodAllergy', '食物过敏'],
  142. ['hobbyFlavor', '喜好口味'],
  143. ['drinkState', '饮酒情况'],
  144. ['smokeState', '吸烟情况'],
  145. ['address', '现住址'],
  146. ['detailAddress', '详细地址'],
  147. ['job', '职业'],
  148. ] as const;
  149. if (!Array.isArray(fields)) fields = [];
  150. type Option = { id: string; name: string; required?: boolean };
  151. const options: Option[] = config.map(([id, name, required = false]) => ({ id, name, required }));
  152. const selected: Option[] = [];
  153. for (const item of fields) {
  154. const [id, required] = item.split(':');
  155. if (id === 'code') continue;
  156. const index = options.findIndex((item) => item.id === id);
  157. if (index > -1) selected.push({ ...options.splice(index, 1)[0], required: required === 'required' || required === 'true' });
  158. }
  159. return { config, options, selected };
  160. }
  161. export const Key_miniProgramCode = 'appletbutton';
  162. export const Key_payLock = 'appletscan';
  163. export const Key_jumpable = 'notjump';
  164. export const Key_printable = 'notprint';
  165. export function analysisRequestData(data?: FlowRequestData): Record<
  166. NodeId,
  167. {
  168. has: boolean;
  169. optional: boolean;
  170. format: string;
  171. fields?: { id: string; name: string; required?: boolean }[];
  172. payLock?: boolean;
  173. miniProgramCode?: boolean;
  174. jumpable?: boolean;
  175. printable?: boolean;
  176. }
  177. > {
  178. const get = (id: NodeId) => {
  179. const key = data?.tabletProcessModules?.find((key) => key.startsWith(nodeRef[id])) ?? '';
  180. const has = !!key;
  181. const optional = key.includes('?');
  182. return { has, optional, format: has ? `有${optional ? '(可选)' : ''}` : '无' };
  183. };
  184. const report = (id: NodeId, prefix: ReportPrefixKey) => {
  185. const values = get(id);
  186. if (values.has) {
  187. const payLock = data?.tabletRequiredPageOperationElements?.includes(`${prefix}_${Key_payLock}`);
  188. const mini = data?.tabletRequiredPageOperationElements?.includes(`${prefix}_${Key_miniProgramCode}`);
  189. values.format += `(${mini && payLock ? '扫码查看' : '完整展示'})`;
  190. Object.assign(values, { payLock: mini && payLock, miniProgramCode: mini });
  191. if (id === ID_Report_Health) {
  192. const jumpable = !data?.tabletRequiredPageOperationElements?.includes(`${prefix}_${Key_jumpable}`);
  193. const printable = !data?.tabletRequiredPageOperationElements?.includes(`${prefix}_${Key_printable}`);
  194. Object.assign(values, { jumpable, printable });
  195. }
  196. }
  197. return values;
  198. };
  199. return {
  200. [ID_Register]: {
  201. ...get(ID_Register),
  202. fields: analysisRegisterFields(data?.tabletFileFields).selected,
  203. },
  204. [ID_Analysis_Pulse]: get(ID_Analysis_Pulse),
  205. [ID_Analysis_TongueAndFace]: get(ID_Analysis_TongueAndFace),
  206. [ID_Analysis_Health]: get(ID_Analysis_Health),
  207. [ID_Report_Pulse]: get(ID_Report_Pulse),
  208. [ID_Report_TongueAndFace]: report(ID_Report_TongueAndFace, 'tongueface_upload_report_page'),
  209. [ID_Report_Alcohol]: get(ID_Report_Alcohol),
  210. [ID_Report_Health]: report(ID_Report_Health, 'health_analysis_report_page'),
  211. [ID_Scheme_Health]: report(ID_Scheme_Health, 'health_analysis_scheme_page'),
  212. } as any;
  213. }