NodeRender.tsx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. 'use client';
  2. import React, { useEffect, useMemo, useState } from 'react'
  3. import Agent from './NodeType/Agent';
  4. import Form from './NodeType/Form';
  5. import Unsupport from './NodeType/Unsupport';
  6. import * as R from 'ramda'
  7. import { curNodeAtom, curStepAtom, arrowStateAtom, cardInstantAtomFamily, cardInstantAtomsAtom, instantDataAtom, stepsNodesAtom, viewedStepAtom, asideInstantAtomsAtom, asideInstantAtomFamily } from '../store';
  8. import { useAtom, useAtomValue, useSetAtom } from 'jotai';
  9. import { exportFlowToDocx } from '../export';
  10. import PreviewModal from './PreviewModal';
  11. import { useReducerAtom } from 'jotai/utils';
  12. import ClearAfterFlowConfirmModal from './ClearAfterFlowConfirmModal';
  13. const NodeRender = () => {
  14. const [curStep, setCurStep] = useAtom(curStepAtom)
  15. const [viewedStep, dispatchViewedStep] = useReducerAtom(viewedStepAtom, (prev: number, step: number) => {
  16. return R.max(prev, step)
  17. })
  18. const forceSetViewedStep = useSetAtom(viewedStepAtom)
  19. useEffect(() => {
  20. dispatchViewedStep(curStep)
  21. }, [curStep])
  22. const [confirmClearAfterFlowModalOpen, setConfirmClearAfterFlowModalOpen] = useState(false)
  23. const node = useAtomValue(curNodeAtom)
  24. const arrowState = useAtomValue(arrowStateAtom)
  25. const Comp = useMemo(() => {
  26. return R.cond([
  27. [R.propEq('form_card', 'type'), R.always(Form)],
  28. [R.propEq('UserTask', 'type'), R.always(Agent)],
  29. [R.T, R.always(Unsupport)],
  30. ])(node)
  31. }, [node, node?.id])
  32. const nodeName = useMemo(() => {
  33. return R.cond([
  34. [R.propEq('form_card', 'type'), R.always('表单填写')],
  35. [R.propEq('UserTask', 'type'), R.pathOr('Unknown Agent', ['properties', 'item', 'assistantName'])],
  36. [R.T, R.always('Unknown')],
  37. ])(node)
  38. }, [node, node?.id])
  39. // 动态注册当前节点的Atom,并将其放进一个atom集合的atom,方便后续格式化所有节点实例
  40. const cardInstantAtom = cardInstantAtomFamily(node)
  41. const [cardInstantAtoms, dispatchCardInstantAtoms] = useAtom(cardInstantAtomsAtom)
  42. useEffect(() => {
  43. if (node?.id) {
  44. dispatchCardInstantAtoms({ payload: { [node.id]: cardInstantAtom }, type: 'update' })
  45. }
  46. }, [cardInstantAtom])
  47. const stepsNodes = useAtomValue(stepsNodesAtom);
  48. const [asideInstantAtoms, dispatchAsideInstantAtoms] = useAtom(asideInstantAtomsAtom)
  49. const onNextStep = (confirmed?: 'clear' | 'not_clear') => {
  50. if (!confirmed) {
  51. if (curStep < viewedStep) {
  52. setConfirmClearAfterFlowModalOpen(true)
  53. } else {
  54. setCurStep(prev => prev + 1)
  55. }
  56. return
  57. }
  58. switch (confirmed) {
  59. case 'clear':
  60. // Clear node instant data after current node
  61. const nodesToClear = stepsNodes.slice(curStep + 1);
  62. const updatedCardInstantAtoms = R.pickBy(
  63. (_, key) => !nodesToClear.some(node => node.id === key),
  64. cardInstantAtoms
  65. );
  66. dispatchCardInstantAtoms({ payload: updatedCardInstantAtoms, type: 'replace' });
  67. const updatedAsideInstantAtoms = R.pickBy(
  68. (_, key) => !nodesToClear.some(node => node.id === key),
  69. asideInstantAtoms
  70. )
  71. dispatchAsideInstantAtoms({ payload: updatedAsideInstantAtoms, type: 'replace' })
  72. nodesToClear.forEach((node) => {
  73. console.log('removed: ', node.id)
  74. cardInstantAtomFamily.remove(node)
  75. asideInstantAtomFamily.remove(node)
  76. })
  77. forceSetViewedStep(curStep);
  78. break;
  79. case 'not_clear':
  80. break;
  81. default:
  82. break;
  83. }
  84. setCurStep(prev => prev + 1)
  85. setConfirmClearAfterFlowModalOpen(false)
  86. }
  87. const onPrevStep = () => {
  88. setCurStep(prev => prev - 1)
  89. }
  90. const [isPreviewModalOpen, setPreviewModalOpen] = useState(false)
  91. const onPreview = () => {
  92. setPreviewModalOpen(true)
  93. }
  94. return (
  95. <div className="card card-compact shadow-xl flex-1 overflow-hidden">
  96. <div className="card-body overflow-hidden">
  97. <div className="card-title rounded-box bg-slate-100 p-2">
  98. <h2 className="flex-1">
  99. {curStep + 1}: {nodeName}
  100. </h2>
  101. {arrowState.prev &&
  102. <button className='btn btn-sm' onClick={() => onPrevStep()}>上一步</button>
  103. }
  104. {arrowState.next
  105. ? <button className='btn btn-sm' onClick={() => onNextStep()}>下一步</button>
  106. : <button className='btn btn-sm btn-neutral' onClick={onPreview}>预览</button>
  107. }
  108. </div>
  109. <div className="flex-1 flex flex-col items-stretch overflow-auto">
  110. <Comp key={node?.id} node={node} cardInstantAtom={cardInstantAtom}></Comp>
  111. </div>
  112. </div>
  113. <ClearAfterFlowConfirmModal
  114. open={confirmClearAfterFlowModalOpen}
  115. onCancel={() => setConfirmClearAfterFlowModalOpen(false)}
  116. onConfirm={onNextStep}
  117. />
  118. <PreviewModal open={isPreviewModalOpen} onClose={() => setPreviewModalOpen(false)} />
  119. </div>
  120. )
  121. }
  122. export default React.memo(React.forwardRef(NodeRender))