Bläddra i källkod

Merge branch 'saved_form' of http://120.55.44.4:10080/xujunjie/BomWeb into saved_form

# Conflicts:
#	src/pages/Detail/AuditDetailed.js
#	src/pages/Detail/CommitAuditModal.js
Renxy 1 år sedan
förälder
incheckning
e6b71a9cf8

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
public/Luckysheet/luckysheet.umd.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
public/Luckysheet/luckysheet.umd.js.map


+ 2 - 1
src/components/DDComponents/DDSelectField/index.js

@@ -4,12 +4,13 @@ import { Select } from 'antd';
 const { Option } = Select;
 const { Option } = Select;
 
 
 function DDSelectField(props) {
 function DDSelectField(props) {
-  const { options, disabled, onChange } = props;
+  const { options, disabled, onChange, defaultValue } = props;
 
 
   return (
   return (
     <Select
     <Select
       style={{ width: '100%' }}
       style={{ width: '100%' }}
       disabled={disabled}
       disabled={disabled}
+      defaultValue={defaultValue}
       onChange={value => {
       onChange={value => {
         onChange(String(value));
         onChange(String(value));
       }}
       }}

+ 3 - 26
src/components/DDComponents/DepartmentField/index.js

@@ -4,9 +4,7 @@ import { queryDDdepList } from '@/services/boom';
 import { connect } from 'dva';
 import { connect } from 'dva';
 
 
 function DepartmentField(props) {
 function DepartmentField(props) {
-  const { value, onChange, depUserTree } = props;
-
-  // const [value, setValue] = useState();
+  const { value = [], onChange, depUserTree } = props;
   const [treeData, setTreeData] = useState([]);
   const [treeData, setTreeData] = useState([]);
 
 
   const genTreeNode = dep => {
   const genTreeNode = dep => {
@@ -30,36 +28,15 @@ function DepartmentField(props) {
   };
   };
 
 
   const onChangeValue = newValue => {
   const onChangeValue = newValue => {
-    console.log(newValue);
-    let dep = depUserTree.find(dep => dep.id == newValue);
-    onChange(String(dep?.ID));
-    // onChange({ value: dep?.title, id: dep?.ID });
+    onChange(newValue);
   };
   };
 
 
-  // useEffect(() => {
-  //   onLoadData({});
-  // }, []);
-
   return (
   return (
-    // <TreeSelect
-    //   treeDataSimpleMode
-    //   style={{
-    //     width: '100%',
-    //   }}
-    //   // value={value}
-    //   dropdownStyle={{
-    //     maxHeight: 400,
-    //     overflow: 'auto',
-    //   }}
-    //   placeholder="请选择部门"
-    //   onChange={onChangeValue}
-    //   loadData={onLoadData}
-    //   treeData={treeData}
-    // />
     <TreeSelect
     <TreeSelect
       showSearch
       showSearch
       multiple
       multiple
       allowClear
       allowClear
+      defaultValue={value}
       dropdownStyle={{
       dropdownStyle={{
         maxHeight: 400,
         maxHeight: 400,
         overflow: 'auto',
         overflow: 'auto',

+ 2 - 1
src/components/DDComponents/NumberField/index.js

@@ -2,11 +2,12 @@ import React from 'react';
 import { InputNumber } from 'antd';
 import { InputNumber } from 'antd';
 
 
 function NumberField(props) {
 function NumberField(props) {
-  const { onChange, disabled, unit } = props;
+  const { onChange, disabled, unit, defaultValue } = props;
 
 
   return (
   return (
     <InputNumber
     <InputNumber
       style={{ width: '100%' }}
       style={{ width: '100%' }}
+      defaultValue={defaultValue}
       disabled={disabled}
       disabled={disabled}
       formatter={value => `${value}${unit || ''}`}
       formatter={value => `${value}${unit || ''}`}
       onChange={e => {
       onChange={e => {

+ 18 - 14
src/components/DDComponents/index.js

@@ -1,4 +1,4 @@
-import { Input, InputNumber, Select, DatePicker, Rate } from 'antd';
+import {Input, InputNumber, Select, DatePicker, Rate} from 'antd';
 import TableField from './TableField';
 import TableField from './TableField';
 import PhoneField from './PhoneField';
 import PhoneField from './PhoneField';
 import InnerContactField from './InnerContactField';
 import InnerContactField from './InnerContactField';
@@ -11,11 +11,11 @@ import DDDateField from './DDDateField';
 import DDDateRangeField from './DDDateRangeField';
 import DDDateRangeField from './DDDateRangeField';
 import DDAttachment from './DDAttachment';
 import DDAttachment from './DDAttachment';
 
 
-const { Option } = Select;
-const { RangePicker } = DatePicker;
+const {Option} = Select;
+const {RangePicker} = DatePicker;
 
 
 export default function DDComponents(props) {
 export default function DDComponents(props) {
-  const { item, onChange } = props;
+  const {item, onChange} = props;
   const {
   const {
     id,
     id,
     label,
     label,
@@ -40,6 +40,7 @@ export default function DDComponents(props) {
     notPrint,
     notPrint,
     verticalPrint,
     verticalPrint,
     hiddenInApprovalDetail,
     hiddenInApprovalDetail,
+    defaultValue,
     disabled,
     disabled,
     notUpper,
     notUpper,
     children, // 子控件
     children, // 子控件
@@ -49,6 +50,7 @@ export default function DDComponents(props) {
     case 'TextField': //单行输入
     case 'TextField': //单行输入
       component = (
       component = (
         <Input
         <Input
+          defaultValue={defaultValue}
           disabled={disabled}
           disabled={disabled}
           placeholder={placeholder}
           placeholder={placeholder}
           onChange={e => onChange?.(e.target.value)}
           onChange={e => onChange?.(e.target.value)}
@@ -58,6 +60,7 @@ export default function DDComponents(props) {
     case 'TextareaField': //多行输入
     case 'TextareaField': //多行输入
       component = (
       component = (
         <Input.TextArea
         <Input.TextArea
+          defaultValue={defaultValue}
           disabled={disabled}
           disabled={disabled}
           placeholder={placeholder}
           placeholder={placeholder}
           onChange={e => onChange?.(e.target.value)}
           onChange={e => onChange?.(e.target.value)}
@@ -65,35 +68,36 @@ export default function DDComponents(props) {
       );
       );
       break;
       break;
     case 'NumberField': //数字输入
     case 'NumberField': //数字输入
-      component = <NumberField disabled={disabled} unit={unit} onChange={onChange} />;
+      component = <NumberField defaultValue={defaultValue} disabled={disabled} unit={unit} onChange={onChange}/>;
       break;
       break;
     case 'DDSelectField': //单选框
     case 'DDSelectField': //单选框
-      component = <DDSelectField options={options} onChange={onChange} disabled={disabled} />;
+      component =
+        <DDSelectField defaultValue={defaultValue} options={options} onChange={onChange} disabled={disabled}/>;
       break;
       break;
     case 'DDMultiSelectField': //多选框
     case 'DDMultiSelectField': //多选框
-      component = <DDMultiSelectField disabled={disabled} options={options} onChange={onChange} />;
+      component = <DDMultiSelectField disabled={disabled} options={options} onChange={onChange}/>;
       break;
       break;
     case 'DDDateField': //日期控件
     case 'DDDateField': //日期控件
-      component = <DDDateField format={format} disabled={disabled} onChange={onChange} />;
+      component = <DDDateField format={format} disabled={disabled} onChange={onChange}/>;
       break;
       break;
     case 'DDDateRangeField': //时间区间控件
     case 'DDDateRangeField': //时间区间控件
-      component = <DDDateRangeField format={format} disabled={disabled} onChange={onChange} />;
+      component = <DDDateRangeField format={format} disabled={disabled} onChange={onChange}/>;
       break;
       break;
     case 'TextNote': //文本说明控件
     case 'TextNote': //文本说明控件
       console.info('文本说明控件!');
       console.info('文本说明控件!');
       console.log(item);
       console.log(item);
       break;
       break;
     case 'PhoneField': //电话控件
     case 'PhoneField': //电话控件
-      component = <PhoneField onChange={onChange} />;
+      component = <PhoneField onChange={onChange}/>;
       break;
       break;
     case 'DDPhotoField': //图片控件
     case 'DDPhotoField': //图片控件
-      component = <DDPhotoField />;
+      component = <DDPhotoField/>;
       break;
       break;
     case 'MoneyField': //金额控件
     case 'MoneyField': //金额控件
-      component = <Input placeholder={placeholder} onChange={onChange} />;
+      component = <Input defaultValue={defaultValue} placeholder={placeholder} onChange={onChange}/>;
       break;
       break;
     case 'TableField': //明细控件
     case 'TableField': //明细控件
-      component = <TableField item={item} />;
+      component = <TableField item={item}/>;
       break;
       break;
     case 'DDAttachment': //附件
     case 'DDAttachment': //附件
       // component = <DDAttachment />
       // component = <DDAttachment />
@@ -104,7 +108,7 @@ export default function DDComponents(props) {
       component = <InnerContactField onChange={onChange}></InnerContactField>;
       component = <InnerContactField onChange={onChange}></InnerContactField>;
       break;
       break;
     case 'DepartmentField': //部门控件
     case 'DepartmentField': //部门控件
-      component = <DepartmentField onChange={onChange} />;
+      component = <DepartmentField onChange={onChange}/>;
       break;
       break;
     case 'RelateField': //关联审批单
     case 'RelateField': //关联审批单
       component = '关联审批单控件未渲染!'
       component = '关联审批单控件未渲染!'

+ 9 - 3
src/components/FileViewer/index.js

@@ -1,8 +1,9 @@
 import FileViewer from 'react-file-viewer';
 import FileViewer from 'react-file-viewer';
 import { Modal, Spin } from 'antd';
 import { Modal, Spin } from 'antd';
-import { memo, useMemo } from 'react';
+import { memo, useEffect, useMemo, useState } from 'react';
 
 
 const FileViewerModal = ({ data, visible, onCancel, downloadFile }) => {
 const FileViewerModal = ({ data, visible, onCancel, downloadFile }) => {
+  const [active, setActive] = useState(false);
   const type = useMemo(() => {
   const type = useMemo(() => {
     if (!data || !data.url) return '';
     if (!data || !data.url) return '';
     let url = data.url || '';
     let url = data.url || '';
@@ -11,6 +12,12 @@ const FileViewerModal = ({ data, visible, onCancel, downloadFile }) => {
     return arr[arr.length - 1];
     return arr[arr.length - 1];
   }, [data?.url]);
   }, [data?.url]);
 
 
+  useEffect(() => {
+    setTimeout(() => {
+      setActive(visible);
+    }, 300);
+  }, [visible]);
+
   const renderContent = () => {
   const renderContent = () => {
     if (type == 'xlsx') {
     if (type == 'xlsx') {
       return (
       return (
@@ -48,9 +55,8 @@ const FileViewerModal = ({ data, visible, onCancel, downloadFile }) => {
       width={1000}
       width={1000}
       footer={null}
       footer={null}
       onCancel={onCancel}
       onCancel={onCancel}
-      bodyStyle={{ height: '680px', overflowY: 'hidden' }}
     >
     >
-      {renderContent()}
+      <div style={{ height: '680px', overflowY: 'hidden' }}>{active && renderContent()}</div>
     </Modal>
     </Modal>
   );
   );
 };
 };

+ 2 - 2
src/pages/Auth/Auth.js

@@ -58,7 +58,7 @@ function Auth(props) {
     if (item.type == 'checked') {
     if (item.type == 'checked') {
       version = checkedVersionList.find(v => v.id == item.id);
       version = checkedVersionList.find(v => v.id == item.id);
       localStorage.excelItem = JSON.stringify(version);
       localStorage.excelItem = JSON.stringify(version);
-      router.push(`/home/detail/${item.project_id}/${item.template_id}`);
+      router.push(`/home/detail/${item.project_id}/${item.template_id}?version_id=${version.version_id}`);
     } else {
     } else {
       //调用接口获取version信息
       //调用接口获取version信息
       dispatch({
       dispatch({
@@ -67,7 +67,7 @@ function Auth(props) {
         callback: checkedVersionList => {
         callback: checkedVersionList => {
           let version = checkedVersionList.find(v => v.id == item.id) || {};
           let version = checkedVersionList.find(v => v.id == item.id) || {};
           localStorage.excelItem = JSON.stringify(version);
           localStorage.excelItem = JSON.stringify(version);
-          router.push(`/home/detail/${item.project_id}/${item.template_id}`);
+          router.push(`/home/detail/${item.project_id}/${item.template_id}?version_id=${version.version_id}`);
         },
         },
       });
       });
     }
     }

+ 13 - 12
src/pages/Detail/AuditDetailed.js

@@ -1,6 +1,6 @@
 import DDComponents from '@/components/DDComponents';
 import DDComponents from '@/components/DDComponents';
-import React, { useMemo, useState } from 'react';
-import { Form } from '@ant-design/compatible';
+import React, {useMemo, useState} from 'react';
+import {Form} from '@ant-design/compatible';
 import '@ant-design/compatible/assets/index.css';
 import '@ant-design/compatible/assets/index.css';
 
 
 const layout = {
 const layout = {
@@ -14,8 +14,7 @@ const layout = {
 
 
 const AuditDetailed = props => {
 const AuditDetailed = props => {
   // const [form] = Form.useForm();
   // const [form] = Form.useForm();
-  const { items, form } = props;
-  console.log(items)
+  const {items, form} = props;
 
 
   const behavior = useMemo(() => {
   const behavior = useMemo(() => {
     let data = {};
     let data = {};
@@ -28,14 +27,14 @@ const AuditDetailed = props => {
           try {
           try {
             data = JSON.parse(o);
             data = JSON.parse(o);
           } catch (error) {
           } catch (error) {
-            data = { key: o, value: o };
+            data = {key: o, value: o};
           }
           }
           return data;
           return data;
         });
         });
         item.behaviorLinkage.forEach(b => {
         item.behaviorLinkage.forEach(b => {
           const value = b.value;
           const value = b.value;
           b.targets.forEach(t => {
           b.targets.forEach(t => {
-            data[t.fieldId] = { key, value: options.find(o => o.key == value)?.value };
+            data[t.fieldId] = {key, value: options.find(o => o.key == value)?.value};
           });
           });
         });
         });
       }
       }
@@ -79,11 +78,12 @@ const AuditDetailed = props => {
     } = item.props;
     } = item.props;
     // 判断是否属于关联项
     // 判断是否属于关联项
     if (behavior[id]) {
     if (behavior[id]) {
-      const { key, value } = behavior[id];
+      const {key, value} = behavior[id];
       let currentValue = form.getFieldValue(key);
       let currentValue = form.getFieldValue(key);
       try {
       try {
         currentValue = JSON.parse(currentValue);
         currentValue = JSON.parse(currentValue);
-      } catch (error) {}
+      } catch (error) {
+      }
       // 判断是否需要渲染
       // 判断是否需要渲染
       if (currentValue instanceof Array) {
       if (currentValue instanceof Array) {
         if (currentValue?.indexOf(value) == -1) return null;
         if (currentValue?.indexOf(value) == -1) return null;
@@ -103,15 +103,16 @@ const AuditDetailed = props => {
       }
       }
     }
     }
 
 
-    const component = DDComponents({ item });
+    const component = DDComponents({item});
     if (!component) return null;
     if (!component) return null;
     return (
     return (
       <Form.Item label={formLabel}>
       <Form.Item label={formLabel}>
         {typeof component == 'string'
         {typeof component == 'string'
           ? component
           ? component
           : form.getFieldDecorator(id, {
           : form.getFieldDecorator(id, {
-              rules: [{ required }],
-            })(component)}
+            rules: [{required}],
+            initialValue: item.props.defaultValue
+          })(component)}
         {notUpper == 1 && <p>大写</p>}
         {notUpper == 1 && <p>大写</p>}
       </Form.Item>
       </Form.Item>
     );
     );
@@ -119,7 +120,7 @@ const AuditDetailed = props => {
 
 
   return (
   return (
     <Form
     <Form
-      style={{ height: '400px', overflowY: 'scroll', paddingRight: 20 }}
+      style={{height: '400px', overflowY: 'scroll', paddingRight: 20}}
       layout="vertical"
       layout="vertical"
       autoComplete="off"
       autoComplete="off"
     >
     >

+ 34 - 21
src/pages/Detail/CommitAuditModal.js

@@ -106,8 +106,9 @@ function CommitAuditModal(props) {
       // 设置延迟,等待组件渲染
       // 设置延迟,等待组件渲染
       setTimeout(async () => {
       setTimeout(async () => {
         form.setFieldsValue(defaultValues);
         form.setFieldsValue(defaultValues);
-        const {approvalProcess} = await initFormList();
-        Object.values(defaultValues).forEach(value => onChange(value, approvalProcess || {}));
+        const initForm = await initFormList()
+        const approvalProcess = initForm?.approvalProcess ? initForm.approvalProcess : {};
+        Object.values(defaultValues).forEach(value => onChange(value, approvalProcess));
       }, 200);
       }, 200);
     }
     }
     setData(data);
     setData(data);
@@ -157,8 +158,10 @@ function CommitAuditModal(props) {
     if (res.data) {
     if (res.data) {
       const formList = JSON.parse(res.data.json);
       const formList = JSON.parse(res.data.json);
       setApprovalProcess(formList.approvalProcess || {});
       setApprovalProcess(formList.approvalProcess || {});
+      const temp = formList.formList;
+      const prevFormData = (temp && temp.length) ? temp.map(tempItem => JSON.parse(tempItem)) : [];
+      setFormComponentValues(prevFormData);
       return formList;
       return formList;
-      // setFormComponentValues(defaultFormData);
     }
     }
   };
   };
 
 
@@ -254,12 +257,15 @@ function CommitAuditModal(props) {
   };
   };
 
 
   const onChange = async (value, approvalProcess) => {
   const onChange = async (value, approvalProcess) => {
-    const {formList: resData} = await initFormList();
-    const prevFormData = resData && resData.length ? JSON.parse(resData[0]) : [];
+    // 加载之前提交的form数据
+    const resFormData = await initFormList();
+    const resData = resFormData?.formList;
+
+    const prevFormData = resData && resData.length ? resData.map(resItem => JSON.parse(resItem)) : null;
     if (value) {
     if (value) {
       changeAudit(value[value.length - 1]);
       changeAudit(value[value.length - 1]);
-      if (value[0] === prevFormData.template_node_id) {
-        setAuditListFun(approvalProcess, prevFormData.formComponentValues);
+      if (prevFormData !== null) {
+        setAuditListFun(approvalProcess, prevFormData);
       } else {
       } else {
         setAuditListFun(approvalProcess);
         setAuditListFun(approvalProcess);
       }
       }
@@ -268,7 +274,6 @@ function CommitAuditModal(props) {
       setAuditList([]);
       setAuditList([]);
       setApprovalProcess({});
       setApprovalProcess({});
     }
     }
-
     form.setFieldValue('next_template_node_id', '');
     form.setFieldValue('next_template_node_id', '');
   };
   };
 
 
@@ -320,7 +325,7 @@ function CommitAuditModal(props) {
   };
   };
 
 
   //处理tabs页
   //处理tabs页
-  const setAuditListFun = async (approvalProcess = {}, prevFromData = []) => {
+  const setAuditListFun = async (approvalProcess = {}, prevFormData = []) => {
     var fieldsValue = await form.validateFields();
     var fieldsValue = await form.validateFields();
     let addAuditList = [];
     let addAuditList = [];
     let result = Object.values(fieldsValue)
     let result = Object.values(fieldsValue)
@@ -342,7 +347,7 @@ function CommitAuditModal(props) {
         let newItem = {
         let newItem = {
           name: curData?.name,
           name: curData?.name,
           nodeId: node.Id,
           nodeId: node.Id,
-          items: JSON.parse(curData.form_json || '[]'),
+          items: JSON.parse(curData?.form_json || '[]'),
         };
         };
 
 
         return newItem;
         return newItem;
@@ -350,15 +355,20 @@ function CommitAuditModal(props) {
       addAuditList = [...addAuditList, ...newlist];
       addAuditList = [...addAuditList, ...newlist];
     }
     }
     addAuditList.forEach((item, index) => {
     addAuditList.forEach((item, index) => {
-      console.log('XXX', item, prevFromData);
-      item.items.forEach(DDComponent => {
-        console.log('XXX',DDComponent)
-        const prevValue = prevFromData.find(item => item.id===DDComponent.props.id)
-        console.log('XXX',prevValue)
-      })
+      // 回填历史数据
+      if (prevFormData.length) {
+        const currentForm = prevFormData.find(pItem=>pItem.template_node_id===item.nodeId);
+        item.items.forEach((DDComponent) => {
+          console.log(currentForm, DDComponent)
+          const prevValue = currentForm?.formComponentValues?.find(cItem => cItem.id === DDComponent.props.id)
+          // console.log(prevValue)
+          DDComponent.props.defaultValue = prevValue?.value || prevValue?.defaultValue
+        })
+      }
       let Components = Form3x.create({
       let Components = Form3x.create({
         onValuesChange: (props, changedValues, allValues) => {
         onValuesChange: (props, changedValues, allValues) => {
           const {items} = props;
           const {items} = props;
+          console.log(allValues)
           formComponentValues[item.nodeId] = items
           formComponentValues[item.nodeId] = items
             .map(item => {
             .map(item => {
               const itemProps = item.props;
               const itemProps = item.props;
@@ -386,10 +396,10 @@ function CommitAuditModal(props) {
       item.FormComponents = <Components items={item.items}/>;
       item.FormComponents = <Components items={item.items}/>;
     });
     });
     setAuditList(addAuditList);
     setAuditList(addAuditList);
-    if (Object.keys(approvalProcess).length == 0) advanceSubmit();
+    advanceSubmit();
   };
   };
 
 
-  const getFromData = async idList => {
+  const getFromData = idList => {
     const data = formComponentValues;
     const data = formComponentValues;
     const result = [];
     const result = [];
     //获取流转节点的层级关系
     //获取流转节点的层级关系
@@ -408,7 +418,8 @@ function CommitAuditModal(props) {
     firstList.forEach(id => {
     firstList.forEach(id => {
       let approvalNode = flowDetail.nodes.find?.(item => item.Id == id);
       let approvalNode = flowDetail.nodes.find?.(item => item.Id == id);
       if (!approvalNode) return;
       if (!approvalNode) return;
-      let values = data[approvalNode.Id] || [];
+      const prevValues = data.length ? data.find((item)=>item.template_node_id === approvalNode.Id).formComponentValues : []
+      let values = data[approvalNode.Id] || prevValues ||  [];
       let audit_list = [],
       let audit_list = [],
         cc_list = [];
         cc_list = [];
       approvalProcess[approvalNode.Id]?.forEach(item => {
       approvalProcess[approvalNode.Id]?.forEach(item => {
@@ -416,6 +427,7 @@ function CommitAuditModal(props) {
         if (item[0].type == 'role') return arr.push(item[0].nowValue);
         if (item[0].type == 'role') return arr.push(item[0].nowValue);
         return arr.push(item[0].value);
         return arr.push(item[0].value);
       });
       });
+      console.log(values)
       const formItem = {
       const formItem = {
         flow_id: approvalNode.flow_id,
         flow_id: approvalNode.flow_id,
         template_node_id: approvalNode.Id,
         template_node_id: approvalNode.Id,
@@ -509,7 +521,7 @@ function CommitAuditModal(props) {
     const flowPath = result.map(item => getFlowPath(item));
     const flowPath = result.map(item => getFlowPath(item));
     setLoading(true);
     setLoading(true);
     try {
     try {
-      const formList = await getFromData(result);
+      const formList = getFromData(result);
       let params = {
       let params = {
         desc: fieldsValue.desc,
         desc: fieldsValue.desc,
         // 审核流程id
         // 审核流程id
@@ -557,10 +569,11 @@ function CommitAuditModal(props) {
         message.error('当前存在审批节点未绑定审批流程!请联系管理员。');
         message.error('当前存在审批节点未绑定审批流程!请联系管理员。');
         return;
         return;
       }
       }
+      debugger
       await querySaveBomForm({
       await querySaveBomForm({
         project_id: version.project_id,
         project_id: version.project_id,
         node_id: version.template_node_id,
         node_id: version.template_node_id,
-        json: JSON.stringify({approvalProcess, fromList}),
+        json: JSON.stringify({approvalProcess, formList}),
       });
       });
       params.audit_series = uuidv4();
       params.audit_series = uuidv4();
       params.files = uploadList.current.join(',');
       params.files = uploadList.current.join(',');

+ 385 - 268
src/pages/Detail/FlowModal.js

@@ -1,4 +1,4 @@
-import React, {useEffect, useState, useRef, useMemo, memo} from 'react';
+import React, { useEffect, useState, useRef, useMemo, memo } from 'react';
 import {
 import {
   Modal,
   Modal,
   Input,
   Input,
@@ -18,21 +18,25 @@ import {
   Tooltip,
   Tooltip,
 } from 'antd';
 } from 'antd';
 import Flow from '@/components/Flow/index';
 import Flow from '@/components/Flow/index';
-import {connect} from 'dva';
-import {GetTokenFromUrl, getToken} from '@/utils/utils';
-import {MODELS, useXFlowApp, useModelAsync} from '@antv/xflow';
-import {CheckOutlined} from '@ant-design/icons';
+import { connect } from 'dva';
+import { GetTokenFromUrl, getToken } from '@/utils/utils';
+import { MODELS, useXFlowApp, useModelAsync } from '@antv/xflow';
+import { CheckOutlined } from '@ant-design/icons';
 import {
 import {
-  queryDelPurchaseExcel, queryDingInstanceDetail, queryRecordSheet, queryTrySeal, queryVserionByNode,
+  queryDelPurchaseExcel,
+  queryDingInstanceDetail,
+  queryRecordSheet,
+  queryTrySeal,
+  queryVserionByNode,
 } from '@/services/boom';
 } from '@/services/boom';
-import {async} from '@antv/x6/lib/registry/marker/async';
+import { async } from '@antv/x6/lib/registry/marker/async';
 import VersionModal from './VersionModal';
 import VersionModal from './VersionModal';
-import styles from './Index.less'
+import styles from './Index.less';
 
 
-const {Option} = Select;
-const {Step} = Steps;
+const { Option } = Select;
+const { Step } = Steps;
 
 
-const {TextArea} = Input;
+const { TextArea } = Input;
 const localData = JSON.parse(localStorage.ggDetaiData || '{}');
 const localData = JSON.parse(localStorage.ggDetaiData || '{}');
 const PAGE_SIZE = 8;
 const PAGE_SIZE = 8;
 let controller = new AbortController();
 let controller = new AbortController();
@@ -41,7 +45,10 @@ let controller = new AbortController();
 function FlowModal(props) {
 function FlowModal(props) {
   let token = getToken();
   let token = getToken();
   const SELECT_TYPE = {
   const SELECT_TYPE = {
-    NAME: '0', TYPE: '1', CREATOR: '2', STATE: '3',
+    NAME: '0',
+    TYPE: '1',
+    CREATOR: '2',
+    STATE: '3',
   };
   };
   const {
   const {
     visible,
     visible,
@@ -69,7 +76,7 @@ function FlowModal(props) {
   const [versionVisible, setVersionVisible] = useState(false);
   const [versionVisible, setVersionVisible] = useState(false);
   const [selectType, setSelectType] = useState(SELECT_TYPE.NAME);
   const [selectType, setSelectType] = useState(SELECT_TYPE.NAME);
   const [inputValue, setInputValue] = useState();
   const [inputValue, setInputValue] = useState();
-  const [expandedRowKey, setExpandedRowKey] = useState([])
+  const [expandedRowKey, setExpandedRowKey] = useState([]);
 
 
   const [sealLoading, setSealLoading] = useState(false);
   const [sealLoading, setSealLoading] = useState(false);
   // const [currentTempNodeId, setCurrentTempNodeId] = useState();
   // const [currentTempNodeId, setCurrentTempNodeId] = useState();
@@ -78,10 +85,12 @@ function FlowModal(props) {
   const graphData = useMemo(() => {
   const graphData = useMemo(() => {
     if (!flowDetail) return;
     if (!flowDetail) return;
     let nodes = flowDetail.nodes?.map(item => ({
     let nodes = flowDetail.nodes?.map(item => ({
-      ...item, isCheck: item.Id == version.template_node_id,
+      ...item,
+      isCheck: item.Id == version.template_node_id,
     }));
     }));
     return {
     return {
-      nodes, edges: flowDetail.edges,
+      nodes,
+      edges: flowDetail.edges,
     };
     };
   }, [flowDetail, version.template_node_id]);
   }, [flowDetail, version.template_node_id]);
 
 
@@ -108,12 +117,17 @@ function FlowModal(props) {
 
 
   const onDelVersion = data => {
   const onDelVersion = data => {
     Modal.confirm({
     Modal.confirm({
-      title: '提示', content: `是否确认删除清单?`, okText: '确定', cancelText: '取消', onOk: async () => {
+      title: '提示',
+      content: `是否确认删除清单?`,
+      okText: '确定',
+      cancelText: '取消',
+      onOk: async () => {
         const res = await queryDelPurchaseExcel(data);
         const res = await queryDelPurchaseExcel(data);
         if (res.code == 200) {
         if (res.code == 200) {
           message.success('删除成功');
           message.success('删除成功');
           dispatch({
           dispatch({
-            type: 'xflow/queryBoomFlowDetail', payload: {
+            type: 'xflow/queryBoomFlowDetail',
+            payload: {
               id: templateId,
               id: templateId,
             },
             },
           });
           });
@@ -130,15 +144,18 @@ function FlowModal(props) {
       }
       }
       setNodeLoading(true);
       setNodeLoading(true);
       controller = new AbortController();
       controller = new AbortController();
-      const res = await queryVserionByNode({
-        template_node_id: template_node_id,
-      }, controller.signal);
+      const res = await queryVserionByNode(
+        {
+          template_node_id: template_node_id,
+        },
+        controller.signal
+      );
       controller = null;
       controller = null;
       let data = [];
       let data = [];
       if (!res.data.excel_version_tree) setData([]);
       if (!res.data.excel_version_tree) setData([]);
       res.data.excel_version_tree?.map(arr => {
       res.data.excel_version_tree?.map(arr => {
         if (res.data.flow_id) {
         if (res.data.flow_id) {
-          data = [...data, {...arr, flow_id: res.data.flow_id}];
+          data = [...data, { ...arr, flow_id: res.data.flow_id }];
         } else {
         } else {
           data = [...data, arr];
           data = [...data, arr];
         }
         }
@@ -177,9 +194,14 @@ function FlowModal(props) {
         if (item.audit_status == 2) status = 'error';
         if (item.audit_status == 2) status = 'error';
       });
       });
       let curNode = flowDetail.nodes.find(item => item.Id == itemDataList[0].template_node_id);
       let curNode = flowDetail.nodes.find(item => item.Id == itemDataList[0].template_node_id);
-      const seqList = itemDataList[0].FlowInfo.FlowNodes.filter(item => item.template_node_id == template_node_id).sort((a, b) => a.seq - b.seq);
+      const seqList = itemDataList[0].FlowInfo.FlowNodes.filter(
+        item => item.template_node_id == template_node_id
+      ).sort((a, b) => a.seq - b.seq);
       let obj = {
       let obj = {
-        status, current: curid, list: seqList, name: curNode?.label || itemDataList[0].FlowInfo.name,
+        status,
+        current: curid,
+        list: seqList,
+        name: curNode?.label || itemDataList[0].FlowInfo.name,
       };
       };
       return obj;
       return obj;
     });
     });
@@ -191,116 +213,163 @@ function FlowModal(props) {
     let type = item.flow_id ? '/queryAuditRecord' : '/queryAuditExcel';
     let type = item.flow_id ? '/queryAuditRecord' : '/queryAuditExcel';
     // console.log(`${file}${type}`, item);
     // console.log(`${file}${type}`, item);
     dispatch({
     dispatch({
-      type: `${file}${type}`, payload: {
-        excel_id: item.id, pageSize: 100,
-      }, callback: res => {
+      type: `${file}${type}`,
+      payload: {
+        excel_id: item.id,
+        pageSize: 100,
+      },
+      callback: res => {
         updateSteps(res, item.template_node_id);
         updateSteps(res, item.template_node_id);
       },
       },
     });
     });
   };
   };
 
 
   const columns = useMemo(() => {
   const columns = useMemo(() => {
-    return [{
-      title: '名称', width: '20%', render: item => {
-        return (<>
-              <span style={{color: item.audit_status != 0 ? '#9b9b9b' : ''}}>
-                {(item.id == version.id && !item.isParent) && (<CheckOutlined style={{marginRight: 10}}/>)}
-                {item.version_no && !item.children?.length ? `${item.version_name}.${item.version_no}` : `${item.version_name}`}
+    return [
+      {
+        title: '名称',
+        width: '20%',
+        render: item => {
+          return (
+            <>
+              <span style={{ color: item.audit_status != 0 ? '#9b9b9b' : '' }}>
+                {item.id == version.id && !item.isParent && (
+                  <CheckOutlined style={{ marginRight: 10 }} />
+                )}
+                {item.version_no && !item.children?.length
+                  ? `${item.version_name}.${item.version_no}`
+                  : `${item.version_name}`}
               </span>
               </span>
-        </>)
+            </>
+          );
+        },
       },
       },
-    }, {
-      title: '创建人', width: '15%', render: item => {
-        return (item.isParent && (<span>{userList.find(cur => cur.ID == item.author)?.CName || '-'}</span>));
+      {
+        title: '创建人',
+        width: '15%',
+        render: item => {
+          return (
+            item.isParent && (
+              <span>{userList.find(cur => cur.ID == item.author)?.CName || '-'}</span>
+            )
+          );
+        },
       },
       },
-    }, {
-      title: '分类', width: '15%', render: item => {
-        return (item.isParent && (<span>{typeOptions.find(cur => cur.id == item.classify_id)?.name}</span>));
+      {
+        title: '分类',
+        width: '15%',
+        render: item => {
+          return (
+            item.isParent && (
+              <span>{typeOptions.find(cur => cur.id == item.classify_id)?.name}</span>
+            )
+          );
+        },
       },
       },
-    }, {
-      title: '状态', width: '15%', render: item => {
-        if (!item.flow_id && item.isParent) return;
-        let style = {color: getColor(item)};
-        let txt = '';
-        let dom = '';
-        switch (item.audit_status) {
-          case 0:
-            txt = '未提交';
-            break;
-          case 1:
-            txt = '待审批';
-            break;
-          case 2:
-            txt = '已拒绝';
-            break;
-          case 3:
-            txt = '已通过';
-            break;
-          case 4:
-            txt = '已提交';
-            break;
-        }
-        if (item.status == 1) txt = '已失效';
+      {
+        title: '状态',
+        width: '15%',
+        render: item => {
+          if (!item.flow_id && item.isParent) return;
+          let style = { color: getColor(item) };
+          let txt = '';
+          let dom = '';
+          switch (item.audit_status) {
+            case 0:
+              txt = '未提交';
+              break;
+            case 1:
+              txt = '待审批';
+              break;
+            case 2:
+              txt = '已拒绝';
+              break;
+            case 3:
+              txt = '已通过';
+              break;
+            case 4:
+              txt = '已提交';
+              break;
+          }
+          if (item.status == 1) txt = '已失效';
 
 
-        // 显示拒绝原因
-        // if (item.audit_comment) {
-        //   dom = (
-        //     <Popover content={item.audit_comment} title="原因">
-        //       {txt}
-        //     </Popover>
-        //   );
-        // } else {
-        dom = txt;
-        // }
-        return item.audit_status != 0 ? (<Button onClick={() => handleChangeClick(item)}>{dom}</Button>) : (
-          <span style={style}>{dom}</span>);
+          // 显示拒绝原因
+          // if (item.audit_comment) {
+          //   dom = (
+          //     <Popover content={item.audit_comment} title="原因">
+          //       {txt}
+          //     </Popover>
+          //   );
+          // } else {
+          dom = txt;
+          // }
+          return item.audit_status != 0 ? (
+            <Button onClick={() => handleChangeClick(item)}>{dom}</Button>
+          ) : (
+            <span style={style}>{dom}</span>
+          );
+        },
       },
       },
-    }, {
-      title: '印章申请', width: '15%', render: item => {
-        if (!item.flow_id && item.isParent) return;
-        let txt = '';
-        //申请印章成功 1  默认0 失败2
-        switch (item.is_seal_succeed) {
-          case 0:
-            txt = '-';
-            break;
-          case 1:
-            txt = '成功';
-            break;
-          case 2:
-            txt = '失败';
-            break;
-        }
-        return (<Space>
-          <span>{txt}</span>
-          {item.is_seal_succeed == 2 && <a onClick={() => handleRetryClick(item.id)}>重试</a>}
-        </Space>);
+      {
+        title: '印章申请',
+        width: '15%',
+        render: item => {
+          if (!item.flow_id && item.isParent) return;
+          let txt = '';
+          //申请印章成功 1  默认0 失败2
+          switch (item.is_seal_succeed) {
+            case 0:
+              txt = '-';
+              break;
+            case 1:
+              txt = '成功';
+              break;
+            case 2:
+              txt = '失败';
+              break;
+          }
+          return (
+            <Space>
+              <span>{txt}</span>
+              {item.is_seal_succeed == 2 && <a onClick={() => handleRetryClick(item.id)}>重试</a>}
+            </Space>
+          );
+        },
+      },
+      {
+        title: '操作',
+        width: '20%',
+        render: item =>
+          (item.flow_id || !item.isParent) &&
+          item.id != version.id && (
+            <Space>
+              <a
+                onClick={() => {
+                  onChangeVersion(item);
+                  onClose();
+                }}
+              >
+                加载
+              </a>
+              {item.audit_status == 0 &&
+              item.author == currentUser.ID && ( //自己创建的&&未提交的清单自己可以删除
+                  <a
+                    onClick={() => {
+                      onDelVersion({ excel_id: item.id });
+                    }}
+                  >
+                    删除
+                  </a>
+                )}
+            </Space>
+          ),
       },
       },
-    }, {
-      title: '操作', width: '20%', render: item => (item.flow_id || !item.isParent) && item.id != version.id && (<Space>
-        <a
-          onClick={() => {
-            onChangeVersion(item);
-            onClose();
-          }}
-        >
-          加载
-        </a>
-        {item.audit_status == 0 && item.author == currentUser.ID && ( //自己创建的&&未提交的清单自己可以删除
-          <a
-            onClick={() => {
-              onDelVersion({excel_id: item.id});
-            }}
-          >
-            删除
-          </a>)}
-      </Space>),
-    },];
+    ];
   }, [version]);
   }, [version]);
 
 
   const handleRetryClick = async id => {
   const handleRetryClick = async id => {
     setNodeLoading(true);
     setNodeLoading(true);
-    const res = await queryTrySeal({excel_id: id});
+    const res = await queryTrySeal({ excel_id: id });
     setNodeLoading(false);
     setNodeLoading(false);
     if (res.data?.errcode != 0) {
     if (res.data?.errcode != 0) {
       message.error(res.data?.errmsg);
       message.error(res.data?.errmsg);
@@ -319,16 +388,43 @@ function FlowModal(props) {
     setInputValue(null);
     setInputValue(null);
   };
   };
 
 
-  const getDescription = node => {
+  function calculateHoursDifference(date1, date2) {
+    const timestamp1 = date1.getTime(); // 获取第一个Date对象的时间戳(以毫秒为单位)
+    const timestamp2 = date2.getTime(); // 获取第二个Date对象的时间戳(以毫秒为单位)
+
+    const timeDifferenceInMillis = Math.abs(timestamp2 - timestamp1); // 计算时间戳之间的差值(毫秒数)
+    const hoursDifference = timeDifferenceInMillis / (1000 * 60 * 60); // 将差值转换为小时数
+
+    return hoursDifference.toFixed(2);
+  }
+
+  const getDescription = (node, prevNode) => {
     let str = `审批人:${node.AuditorUser?.CName || '-'}`;
     let str = `审批人:${node.AuditorUser?.CName || '-'}`;
-    return (<div>
-      审批人:{node.AuditorUser?.CName || '-'}
+    const date = new Date(node.audit_time);
+    const auditTime =
+      node.audit_time === '0001-01-01T00:00:00Z'
+        ? '-'
+        : date.toLocaleDateString('zh-CN', {
+            format: 'YYYY-MM-DD hh:mm:ss',
+          });
+    const residenceTime =
+      auditTime === '-' ? '-' : calculateHoursDifference(date, new Date(prevNode.audit_time));
+    return (
       <div>
       <div>
-          <span style={{color: '#1A73E8', textDecoration: 'undeline'}}>
+        审批人:{node.AuditorUser?.CName || '-'}
+        <div>
+          <span style={{ color: '#1A73E8', textDecoration: 'unerline' }}>
             审批意见:{node.desc || '-'}
             审批意见:{node.desc || '-'}
           </span>
           </span>
+        </div>
+        <div>
+          <span>审批时间:{auditTime || '-'}</span>
+        </div>
+        <div>
+          <span>滞留时间:{`${residenceTime}小时`}</span>
+        </div>
       </div>
       </div>
-    </div>);
+    );
   };
   };
 
 
   const filterState = () => {
   const filterState = () => {
@@ -365,18 +461,18 @@ function FlowModal(props) {
     setShowData(resultData);
     setShowData(resultData);
   }, [inputValue, data]);
   }, [inputValue, data]);
 
 
-  const setRowClassName = (row) => {
-    const rowId = localStorage.excelId
+  const setRowClassName = row => {
+    const rowId = localStorage.excelId;
 
 
     if (row.id.toString() === rowId) {
     if (row.id.toString() === rowId) {
       return styles.selectedROW;
       return styles.selectedROW;
     }
     }
-    return ''
-  }
+    return '';
+  };
 
 
-  const handleExpandedRowChange = (expandedRows) => {
-    setExpandedRowKey(expandedRows)
-  }
+  const handleExpandedRowChange = expandedRows => {
+    setExpandedRowKey(expandedRows);
+  };
 
 
   //列表筛选状态
   //列表筛选状态
   const STATE = {
   const STATE = {
@@ -387,146 +483,165 @@ function FlowModal(props) {
     SUBMIT: 4, //已提交
     SUBMIT: 4, //已提交
     FAILURE: 5, //已失效
     FAILURE: 5, //已失效
   };
   };
-  return (<>
-    <Modal
-      // confirmLoading={loading}
-      destroyOnClose
-      title="流程图"
-      visible={visible}
-      onCancel={() => {
-        setSelectType(SELECT_TYPE.NAME);
-        setInputValue('');
-        onClose();
-      }}
-      footer={false}
-      width="98%"
-      // bodyStyle={{ maxHeight: '660px', overflow: 'auto' }}
-    >
-      <Row gutter={8}>
-        <Col span={14}>
-          <Flow meta={{type: 'view'}} flowDetail={graphData} onSelectNode={handleSelectNode}/>
-        </Col>
-        <Col span={10}>
-          <div style={{fontSize: '16px', marginBottom: '10px'}}>清单列表</div>
-          <div style={{display: 'flex', justifyContent: 'space-between'}}>
-            <div style={{width: '60%'}}>
-              <Select
-                value={selectType}
-                style={{width: '30%'}}
-                onChange={value => {
-                  setSelectType(value);
-                  setInputValue('');
-                }}
-              >
-                <Option value={SELECT_TYPE.NAME}>名称:</Option>
-                <Option value={SELECT_TYPE.TYPE}>分类:</Option>
-                <Option value={SELECT_TYPE.CREATOR}>创建人:</Option>
-                <Option value={SELECT_TYPE.STATE}>状态:</Option>
-              </Select>
-              {(selectType == SELECT_TYPE.NAME || selectType == SELECT_TYPE.CREATOR) && (<Input
-                style={{width: '70%'}}
-                placeholder="请输入"
-                value={inputValue}
-                onChange={e => setInputValue(e.target.value)}
-              />)}
-              {selectType == SELECT_TYPE.TYPE && (<Select
-                showSearch
-                allowClear
-                style={{width: '70%'}}
-                placeholder="请选择分类"
-                options={typeOptions}
-                onChange={id => setInputValue(id)}
-                filterOption={(input, option) => (option?.label ?? '').toLowerCase().includes(input.toLowerCase())}
-              />)}
-              {selectType == SELECT_TYPE.STATE && (<Select
-                showSearch
-                allowClear
-                style={{width: '70%'}}
-                placeholder="请选择状态"
-                // options={typeOptions}
-                onChange={id => setInputValue(id)}
-              >
-                <Option value={STATE.NOSUBMIT}>未提交</Option>
-                <Option value={STATE.NOAPPROVE}>待审批</Option>
-                <Option value={STATE.REJECT}>已拒绝</Option>
-                <Option value={STATE.PASS}>已通过</Option>
-                <Option value={STATE.SUBMIT}>已提交</Option>
-                <Option value={STATE.FAILURE}>已失效</Option>
-              </Select>)}
+  return (
+    <>
+      <Modal
+        // confirmLoading={loading}
+        destroyOnClose
+        title="流程图"
+        visible={visible}
+        onCancel={() => {
+          setSelectType(SELECT_TYPE.NAME);
+          setInputValue('');
+          onClose();
+        }}
+        footer={false}
+        width="98%"
+        // bodyStyle={{ maxHeight: '660px', overflow: 'auto' }}
+      >
+        <Row gutter={8}>
+          <Col span={14}>
+            <Flow meta={{ type: 'view' }} flowDetail={graphData} onSelectNode={handleSelectNode} />
+          </Col>
+          <Col span={10}>
+            <div style={{ fontSize: '16px', marginBottom: '10px' }}>清单列表</div>
+            <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+              <div style={{ width: '60%' }}>
+                <Select
+                  value={selectType}
+                  style={{ width: '30%' }}
+                  onChange={value => {
+                    setSelectType(value);
+                    setInputValue('');
+                  }}
+                >
+                  <Option value={SELECT_TYPE.NAME}>名称:</Option>
+                  <Option value={SELECT_TYPE.TYPE}>分类:</Option>
+                  <Option value={SELECT_TYPE.CREATOR}>创建人:</Option>
+                  <Option value={SELECT_TYPE.STATE}>状态:</Option>
+                </Select>
+                {(selectType == SELECT_TYPE.NAME || selectType == SELECT_TYPE.CREATOR) && (
+                  <Input
+                    style={{ width: '70%' }}
+                    placeholder="请输入"
+                    value={inputValue}
+                    onChange={e => setInputValue(e.target.value)}
+                  />
+                )}
+                {selectType == SELECT_TYPE.TYPE && (
+                  <Select
+                    showSearch
+                    allowClear
+                    style={{ width: '70%' }}
+                    placeholder="请选择分类"
+                    options={typeOptions}
+                    onChange={id => setInputValue(id)}
+                    filterOption={(input, option) =>
+                      (option?.label ?? '').toLowerCase().includes(input.toLowerCase())
+                    }
+                  />
+                )}
+                {selectType == SELECT_TYPE.STATE && (
+                  <Select
+                    showSearch
+                    allowClear
+                    style={{ width: '70%' }}
+                    placeholder="请选择状态"
+                    // options={typeOptions}
+                    onChange={id => setInputValue(id)}
+                  >
+                    <Option value={STATE.NOSUBMIT}>未提交</Option>
+                    <Option value={STATE.NOAPPROVE}>待审批</Option>
+                    <Option value={STATE.REJECT}>已拒绝</Option>
+                    <Option value={STATE.PASS}>已通过</Option>
+                    <Option value={STATE.SUBMIT}>已提交</Option>
+                    <Option value={STATE.FAILURE}>已失效</Option>
+                  </Select>
+                )}
+              </div>
+              {isOut && (
+                <Button type="primary" onClick={() => setVersionVisible(true)}>
+                  新建清单
+                </Button>
+              )}
             </div>
             </div>
-            {isOut && (<Button type="primary" onClick={() => setVersionVisible(true)}>
-              新建清单
-            </Button>)}
-          </div>
 
 
-          <div style={{width: '100%', marginTop: '10px'}}>
-            <Table
-              columns={columns}
-              dataSource={showData}
-              loading={nodeLoading}
-              bordered={false}
-              rowKey='id'
-              pagination={{position: ['none', 'none'], pageSize: 999, onChange}}
-              scroll={{y: '460px'}}
-              rowClassName={setRowClassName}
-              onExpandedRowsChange={handleExpandedRowChange}
-              expandedRowKeys={expandedRowKey}
-              defaultExpandedRowKeys={expandedRowKey}
-              // childrenColumnName="none"
-              // expandable={{
-              //   expandedRowRender: record => (
-              //     <Table
-              //       columns={columns}
-              //       dataSource={record.children}
-              //       pagination={{ position: ['none', 'none'] }}
-              //     />
-              //   ),
-              //   rowExpandable: record => record.children?.length > 0,
-              // }}
-            />
-          </div>
-          {/* <Spin spinning={loading.global}> */}
-          <div
-            style={{
-              display: 'flex', justifyContent: 'space-around', maxHeight: '300px', overflow: 'auto',
-            }}
-          >
-            {stepsData.map((item, idx) => (
-              <div key={`${item.name}_${idx}`} style={{marginTop: '20px', display: 'inline'}}>
-                <div style={{marginBottom: '4px'}}>{item.name}</div>
-                <Steps
-                  direction="vertical"
-                  size="small"
-                  current={item.current}
-                  status={item.status}
-                >
-                  {item.list.map(node => (<Step
-                    key={`${node.id}_${node.node}`}
-                    title={node.node}
-                    description={getDescription(node)}
-                  />))}
-                </Steps>
-              </div>))}
-          </div>
-          {/* </Spin> */}
-        </Col>
-      </Row>
-    </Modal>
-    <VersionModal
-      typeOptions={typeOptions}
-      visible={versionVisible}
-      version={version}
-      versionList={versionList}
-      flowDetail={flowDetail}
-      onClose={() => setVersionVisible(false)}
-      onOk={values => {
-        onCommit?.(values, () => {
-          setVersionVisible(false);
-        });
-      }}
-      loading={commitLoading}
-    />
-  </>);
+            <div style={{ width: '100%', marginTop: '10px' }}>
+              <Table
+                columns={columns}
+                dataSource={showData}
+                loading={nodeLoading}
+                bordered={false}
+                rowKey="id"
+                pagination={{ position: ['none', 'none'], pageSize: 999, onChange }}
+                scroll={{ y: '460px' }}
+                rowClassName={setRowClassName}
+                onExpandedRowsChange={handleExpandedRowChange}
+                expandedRowKeys={expandedRowKey}
+                defaultExpandedRowKeys={expandedRowKey}
+                // childrenColumnName="none"
+                // expandable={{
+                //   expandedRowRender: record => (
+                //     <Table
+                //       columns={columns}
+                //       dataSource={record.children}
+                //       pagination={{ position: ['none', 'none'] }}
+                //     />
+                //   ),
+                //   rowExpandable: record => record.children?.length > 0,
+                // }}
+              />
+            </div>
+            {/* <Spin spinning={loading.global}> */}
+            <div
+              style={{
+                display: 'flex',
+                justifyContent: 'space-around',
+                maxHeight: '300px',
+                overflow: 'auto',
+              }}
+            >
+              {stepsData.length > 0 &&
+                stepsData.map((item, idx) => (
+                  <div key={`${item.name}_${idx}`} style={{ marginTop: '20px', display: 'inline' }}>
+                    <div style={{ marginBottom: '4px' }}>{item.name}</div>
+                    <Steps
+                      direction="vertical"
+                      size="small"
+                      current={item.current}
+                      status={item.status}
+                    >
+                      {item.list.reduce((prev, node) => (
+                        <Step
+                          key={`${node.id}_${node.node}`}
+                          title={node.node}
+                          description={getDescription(node, prev)}
+                        />
+                      ))}
+                    </Steps>
+                  </div>
+                ))}
+            </div>
+            {/* </Spin> */}
+          </Col>
+        </Row>
+      </Modal>
+      <VersionModal
+        typeOptions={typeOptions}
+        visible={versionVisible}
+        version={version}
+        versionList={versionList}
+        flowDetail={flowDetail}
+        onClose={() => setVersionVisible(false)}
+        onOk={values => {
+          onCommit?.(values, () => {
+            setVersionVisible(false);
+          });
+        }}
+        loading={commitLoading}
+      />
+    </>
+  );
 }
 }
 
 
 const getColor = item => {
 const getColor = item => {
@@ -551,7 +666,9 @@ const getColor = item => {
   return color;
   return color;
 };
 };
 
 
-export default connect(({loading, user}) => ({
-  loading, currentUser: user.currentUser, userList: user.list,
+export default connect(({ loading, user }) => ({
+  loading,
+  currentUser: user.currentUser,
+  userList: user.list,
 }))(FlowModal);
 }))(FlowModal);
 // export default FlowModal;
 // export default FlowModal;

+ 50 - 33
src/pages/Detail/Index.js

@@ -1,7 +1,7 @@
-import React, { useEffect, useState, useRef, useMemo } from 'react';
-import { UnorderedListOutlined, PlusOutlined, InfoOutlined } from '@ant-design/icons';
-import { Button, Modal, message, Alert, Avatar } from 'antd';
-import { connect } from 'dva';
+import React, {useEffect, useState, useRef, useMemo} from 'react';
+import {UnorderedListOutlined, PlusOutlined, InfoOutlined} from '@ant-design/icons';
+import {Button, Modal, message, Alert, Avatar} from 'antd';
+import {connect} from 'dva';
 import styles from './Index.less';
 import styles from './Index.less';
 import LuckySheet from './LuckySheet';
 import LuckySheet from './LuckySheet';
 import AuditModal from './AuditModal';
 import AuditModal from './AuditModal';
@@ -18,8 +18,9 @@ import VersionModal from './VersionModal';
 import CommitAuditModal from './CommitAuditModal';
 import CommitAuditModal from './CommitAuditModal';
 import CommentContent from '@/components/CommentContent';
 import CommentContent from '@/components/CommentContent';
 import MergeModal from './MergeModal';
 import MergeModal from './MergeModal';
-import { GetTokenFromUrl, getToken } from '@/utils/utils';
+import {GetTokenFromUrl, getToken} from '@/utils/utils';
 import {
 import {
+  getAuditDetail,
   queryDelSheetRecord,
   queryDelSheetRecord,
   queryDetail,
   queryDetail,
   queryDingInstanceExecute,
   queryDingInstanceExecute,
@@ -27,14 +28,16 @@ import {
 } from '@/services/boom';
 } from '@/services/boom';
 import HistoryDrawer from './HistoryDrawer';
 import HistoryDrawer from './HistoryDrawer';
 import AuditFlow from './AuditFlow';
 import AuditFlow from './AuditFlow';
-import { getCurrentUser } from '@/utils/authority';
-import { async } from '@antv/x6/es/registry/marker/async';
+import {getCurrentUser} from '@/utils/authority';
+import {async} from '@antv/x6/es/registry/marker/async';
 import FileViewerModal from '@/components/FileViewer';
 import FileViewerModal from '@/components/FileViewer';
 import PreviewFile from '@/components/PreviewFile';
 import PreviewFile from '@/components/PreviewFile';
 import FormAndFilesNode from './FormAndFilesNode';
 import FormAndFilesNode from './FormAndFilesNode';
 import DropdownMenu from './DropdownMenu';
 import DropdownMenu from './DropdownMenu';
 import CurrentInfo from './CurrentInfo';
 import CurrentInfo from './CurrentInfo';
 import moment from 'moment';
 import moment from 'moment';
+import {LocalStorage} from "@antv/x6";
+
 const LocalData = localStorage.luckysheet;
 const LocalData = localStorage.luckysheet;
 
 
 function Detail(props) {
 function Detail(props) {
@@ -46,7 +49,8 @@ function Detail(props) {
     auditList,
     auditList,
     flowDetail,
     flowDetail,
     versionTree,
     versionTree,
-    match: { params },
+    match: {params},
+    location: {query},
     instanceDetail,
     instanceDetail,
     typeOptions,
     typeOptions,
     classifyList,
     classifyList,
@@ -87,7 +91,8 @@ function Detail(props) {
   const cellPosition = useRef({});
   const cellPosition = useRef({});
 
 
   useEffect(() => {
   useEffect(() => {
-    //不请求excelFileList 时清空excelFileList,否则会出现清单切换后如果attachment_id不存在,附件信息没有更新
+    // if (!version.id) return
+    // 不请求excelFileList 时清空excelFileList,否则会出现清单切换后如果attachment_id不存在,附件信息没有更新
     if (!version.attachment_id) {
     if (!version.attachment_id) {
       dispatch({
       dispatch({
         type: 'detail/save',
         type: 'detail/save',
@@ -107,6 +112,11 @@ function Detail(props) {
 
 
   const projectId = parseInt(params.projectId);
   const projectId = parseInt(params.projectId);
   const templateId = parseInt(params.templateId);
   const templateId = parseInt(params.templateId);
+  const versionID = parseInt(query.version_id);
+  const TOKEN = query['JWT-TOKEN']
+  if (!localStorage.getItem('JWT-TOKEN')) {
+    localStorage['JWT-TOKEN'] = TOKEN;
+  }
 
 
   const flow = useMemo(() => {
   const flow = useMemo(() => {
     let data = {
     let data = {
@@ -263,7 +273,7 @@ function Detail(props) {
     await queryDelSheetRecord(params);
     await queryDelSheetRecord(params);
   };
   };
 
 
-  const onAudit = ({ audit_comment, audit_status }) => {
+  const onAudit = ({audit_comment, audit_status}) => {
     const flowNode = flow.currentNode;
     const flowNode = flow.currentNode;
     dispatch({
     dispatch({
       type: 'detail/approve',
       type: 'detail/approve',
@@ -299,7 +309,7 @@ function Detail(props) {
         } else {
         } else {
           dispatch({
           dispatch({
             type: 'authList/queryAuthList',
             type: 'authList/queryAuthList',
-            payloda: { user_id: currentUser.ID },
+            payloda: {user_id: currentUser.ID},
           });
           });
           localStorage.excelId = newVersion.id;
           localStorage.excelId = newVersion.id;
           setVersion({
           setVersion({
@@ -311,7 +321,7 @@ function Detail(props) {
         //更新未审批列表
         //更新未审批列表
         dispatch({
         dispatch({
           type: 'authList/queryAuthList',
           type: 'authList/queryAuthList',
-          payloda: { user_id: currentUser.ID },
+          payloda: {user_id: currentUser.ID},
         });
         });
       },
       },
     });
     });
@@ -389,15 +399,23 @@ function Detail(props) {
     return !loading.effects['detail/queryComment'] && loading.models.detail;
     return !loading.effects['detail/queryComment'] && loading.models.detail;
   };
   };
 
 
-  const changeVersion = id => {
+  const changeVersion = async id => {
     let version;
     let version;
     if (typeof id == 'object') {
     if (typeof id == 'object') {
       version = id;
       version = id;
       localStorage.excelId = version.id;
       localStorage.excelId = version.id;
       localStorage.excelItem = JSON.stringify(version);
       localStorage.excelItem = JSON.stringify(version);
     } else {
     } else {
-      version = versionList.find(item => item.id == id);
-      if (!version) return;
+      version = await getAuditDetail({userId: currentUser.ID, versionID})
+      if (!version) {
+        const excelId = localStorage.excelItem
+          ? JSON.parse(localStorage.excelItem)
+          : localStorage.excelId;
+        if (typeof excelId === 'object') {
+          changeVersion(excelId)
+          return;
+        }
+      }
       localStorage.excelId = id;
       localStorage.excelId = id;
     }
     }
     setVersion(version);
     setVersion(version);
@@ -430,7 +448,8 @@ function Detail(props) {
         userRef.current = newUser;
         userRef.current = newUser;
         setUser(newUser);
         setUser(newUser);
       }
       }
-    } catch (error) {}
+    } catch (error) {
+    }
   };
   };
   const handleSubmitCell = (value, callback) => {
   const handleSubmitCell = (value, callback) => {
     if (!value) return;
     if (!value) return;
@@ -486,7 +505,7 @@ function Detail(props) {
 
 
   useEffect(() => {
   useEffect(() => {
     if (compareList.length == 2) {
     if (compareList.length == 2) {
-      const callback = ({ diff, add }) => {
+      const callback = ({diff, add}) => {
         setUpdateCount(updateCount => {
         setUpdateCount(updateCount => {
           return {
           return {
             diff: diff.length,
             diff: diff.length,
@@ -500,22 +519,20 @@ function Detail(props) {
   }, [compareList]);
   }, [compareList]);
 
 
   useEffect(() => {
   useEffect(() => {
-    if (versionList.length == 0) return;
+    // if (versionList.length == 0) return;
+    if (!currentUser.ID) return
     if (!version.id) {
     if (!version.id) {
-      const excelId = localStorage.excelItem
-        ? JSON.parse(localStorage.excelItem)
-        : localStorage.excelId;
-      changeVersion(excelId);
+      changeVersion(versionID);
     } else {
     } else {
       changeVersion(version.id);
       changeVersion(version.id);
     }
     }
-  }, [versionList]);
+  }, [versionList, currentUser]);
 
 
   return (
   return (
     <>
     <>
       <div className={styles.top}>
       <div className={styles.top}>
         <div>
         <div>
-          <Button type="primary" style={{ marginRight: 20 }} onClick={() => setFlowVisible(true)}>
+          <Button type="primary" style={{marginRight: 20}} onClick={() => setFlowVisible(true)}>
             查看流程
             查看流程
           </Button>
           </Button>
 
 
@@ -525,10 +542,10 @@ function Detail(props) {
               新建清单
               新建清单
             </Button>
             </Button>
           )}
           )}
-          <CurrentInfo version={version} flowDetail={flowDetail} />
+          <CurrentInfo version={version} flowDetail={flowDetail}/>
         </div>
         </div>
         <div className={styles.btns}>
         <div className={styles.btns}>
-          {saveTime && <span style={{ color: '#333', fontSize: 14 }}>上次保存时间 {saveTime}</span>}
+          {saveTime && <span style={{color: '#333', fontSize: 14}}>上次保存时间 {saveTime}</span>}
           {version.audit_status === 0 && (
           {version.audit_status === 0 && (
             <Button type="primary" loading={loading.effects['detail/saveSheet']} onClick={onUpdate}>
             <Button type="primary" loading={loading.effects['detail/saveSheet']} onClick={onUpdate}>
               保存
               保存
@@ -536,16 +553,16 @@ function Detail(props) {
           )}
           )}
           <Button
           <Button
             type="primary"
             type="primary"
-            style={{ marginRight: 20 }}
+            style={{marginRight: 20}}
             onClick={() => setVersionTreeVisible(true)}
             onClick={() => setVersionTreeVisible(true)}
           >
           >
             历史版本
             历史版本
           </Button>
           </Button>
-          <Avatar.Group style={{ marginRight: 20 }}>
+          <Avatar.Group style={{marginRight: 20}}>
             {user.map((item, id) => (
             {user.map((item, id) => (
               <Avatar
               <Avatar
                 key={`${id}-${item.name}`}
                 key={`${id}-${item.name}`}
-                style={{ backgroundColor: '#008dff' }}
+                style={{backgroundColor: '#008dff'}}
                 size="large"
                 size="large"
               >
               >
                 {item.Name}
                 {item.Name}
@@ -569,11 +586,11 @@ function Detail(props) {
         <input
         <input
           type="file"
           type="file"
           ref={fileRef}
           ref={fileRef}
-          style={{ display: 'none' }}
+          style={{display: 'none'}}
           onChange={e => exportExcl(e.target.files)}
           onChange={e => exportExcl(e.target.files)}
         />
         />
       </div>
       </div>
-      <div style={{ display: 'flex' }}>
+      <div style={{display: 'flex'}}>
         <div
         <div
           className={styles.content}
           className={styles.content}
           style={{
           style={{
@@ -628,7 +645,7 @@ function Detail(props) {
             display: 'inline',
             display: 'inline',
             marginLeft: '20px',
             marginLeft: '20px',
           }}
           }}
-        ></TimeNode>
+        />
       </div>
       </div>
 
 
       <CommentContent
       <CommentContent
@@ -709,7 +726,7 @@ function Detail(props) {
   );
   );
 }
 }
 
 
-export default connect(({ detail, user, xflow, loading }) => ({
+export default connect(({detail, user, xflow, loading}) => ({
   flowDetail: xflow.flowDetail,
   flowDetail: xflow.flowDetail,
   auditList: detail.auditList,
   auditList: detail.auditList,
   instanceDetail: detail.dingInstanceDetail,
   instanceDetail: detail.dingInstanceDetail,

+ 42 - 15
src/pages/Detail/TimeNode.js

@@ -1,12 +1,13 @@
-import React, { useEffect, useState, useRef } from 'react';
-import { Form } from '@ant-design/compatible';
+import React, {useEffect, useState, useRef} from 'react';
+import {Form} from '@ant-design/compatible';
 import '@ant-design/compatible/assets/index.css';
 import '@ant-design/compatible/assets/index.css';
-import { connect } from 'dva';
-import { Steps, Button, Modal, Tooltip } from 'antd';
+import {connect} from 'dva';
+import {Steps, Button, Modal, Tooltip} from 'antd';
 import styles from './Index.less';
 import styles from './Index.less';
-import { getCurrentUser } from '@/utils/authority';
+import {getCurrentUser} from '@/utils/authority';
+
+const {Step} = Steps;
 
 
-const { Step } = Steps;
 // 时间节点
 // 时间节点
 function TimeNode(props) {
 function TimeNode(props) {
   const {
   const {
@@ -23,21 +24,47 @@ function TimeNode(props) {
     stepDirection,
     stepDirection,
     currentUser,
     currentUser,
   } = props;
   } = props;
-  const { current, list, active } = flow;
+  const {current, list, active} = flow;
+  console.log(list.FlowNodes)
+
+
+  function calculateHoursDifference(date1, date2) {
+    const timestamp1 = date1.getTime(); // 获取第一个Date对象的时间戳(以毫秒为单位)
+    const timestamp2 = date2.getTime(); // 获取第二个Date对象的时间戳(以毫秒为单位)
+
+    const timeDifferenceInMillis = Math.abs(timestamp2 - timestamp1); // 计算时间戳之间的差值(毫秒数)
+    const hoursDifference = timeDifferenceInMillis / (1000 * 60 * 60); // 将差值转换为小时数
+
+    return hoursDifference.toFixed(2);
+  }
 
 
-  const getDescription = node => {
+  const getDescription = (node, prevNode) => {
     let str = node?.AuditRoleInfo
     let str = node?.AuditRoleInfo
       ? `审批人:${node?.AuditRoleInfo.Name || '-'}`
       ? `审批人:${node?.AuditRoleInfo.Name || '-'}`
       : `审批人:${node?.AuditorUser.CName || '-'}`;
       : `审批人:${node?.AuditorUser.CName || '-'}`;
-
+    const date = new Date(node.audit_time)
+    const auditTime = node.audit_time === '0001-01-01T00:00:00Z' ? '-' : date.toLocaleDateString('zh-CN', {
+      format: 'YYYY-MM-DD hh:mm:ss'
+    })
+    // const residenceTime = auditTime === '-' ? '-' : calculateHoursDifference(date, new Date(prevNode.audit_time))
     return (
     return (
       <div>
       <div>
         {str}
         {str}
         <div>
         <div>
-          <span style={{ color: '#1A73E8', textDecoration: 'undeline' }}>
+          <span style={{color: '#1A73E8', textDecoration: 'undeline'}}>
             审批意见:{node.desc || '-'}
             审批意见:{node.desc || '-'}
           </span>
           </span>
         </div>
         </div>
+        <div>
+          <span>
+            审批时间:{auditTime}
+          </span>
+        </div>
+        {/* <div> */}
+        {/*   <span> */}
+        {/*     滞留时间:{`${residenceTime}小时`} */}
+        {/*   </span> */}
+        {/* </div> */}
       </div>
       </div>
     );
     );
 
 
@@ -52,12 +79,12 @@ function TimeNode(props) {
           current={current}
           current={current}
           status={active == 0 ? 'error' : 'process'}
           status={active == 0 ? 'error' : 'process'}
         >
         >
-          {list.FlowNodes.map(item => (
-            <Step key={item.id} title={item.node} description={getDescription(item)} />
-          ))}
+          {list.FlowNodes.map(( item) => {
+            return <Step key={item.id} title={item.node} description={getDescription(item)} />
+          })}
         </Steps>
         </Steps>
         {isAuditor && active != 0 && (
         {isAuditor && active != 0 && (
-          <div className={styles.btns} style={{ margin: '40px 0' }}>
+          <div className={styles.btns} style={{margin: '40px 0'}}>
             <Button type="primary" onClick={() => setAuditVisible(1)}>
             <Button type="primary" onClick={() => setAuditVisible(1)}>
               审批通过
               审批通过
             </Button>
             </Button>
@@ -77,7 +104,7 @@ function TimeNode(props) {
   return null;
   return null;
 }
 }
 
 
-export default connect(({ user, detail }) => ({
+export default connect(({user, detail}) => ({
   currentUser: user.currentUser,
   currentUser: user.currentUser,
   versionList: detail.versionList,
   versionList: detail.versionList,
 }))(TimeNode);
 }))(TimeNode);

+ 15 - 14
src/pages/Flow/FlowDetail.json

@@ -68,7 +68,7 @@
       "parentKey": "1",
       "parentKey": "1",
       "x": -578,
       "x": -578,
       "y": 6,
       "y": 6,
-      "flow_id": 1,
+      "flow_id": 9,
       "node_type_psr": 1,
       "node_type_psr": 1,
       "count": 0,
       "count": 0,
       "role_list": ""
       "role_list": ""
@@ -165,7 +165,7 @@
       "x": -78,
       "x": -78,
       "y": -254,
       "y": -254,
       "flow_id": 0,
       "flow_id": 0,
-      "node_type_psr": 4,
+      "node_type_psr": 3,
       "count": 0,
       "count": 0,
       "role_list": ""
       "role_list": ""
     },
     },
@@ -213,7 +213,7 @@
       "x": 616,
       "x": 616,
       "y": -556,
       "y": -556,
       "flow_id": 0,
       "flow_id": 0,
-      "node_type_psr": 0,
+      "node_type_psr": 2,
       "count": 0,
       "count": 0,
       "role_list": ""
       "role_list": ""
     },
     },
@@ -239,6 +239,7 @@
       "flow_id": 0,
       "flow_id": 0,
       "node_type_psr": 0,
       "node_type_psr": 0,
       "count": 0,
       "count": 0,
+      "is_seal": 1,
       "role_list": ""
       "role_list": ""
     },
     },
     {
     {
@@ -260,7 +261,7 @@
       "parentKey": "1",
       "parentKey": "1",
       "x": 325,
       "x": 325,
       "y": -170,
       "y": -170,
-      "flow_id": 1,
+      "flow_id": 5,
       "node_type_psr": 1,
       "node_type_psr": 1,
       "count": 0,
       "count": 0,
       "role_list": ""
       "role_list": ""
@@ -284,7 +285,7 @@
       "parentKey": "1",
       "parentKey": "1",
       "x": 111,
       "x": 111,
       "y": 6,
       "y": 6,
-      "flow_id": 1,
+      "flow_id": 8,
       "node_type_psr": 1,
       "node_type_psr": 1,
       "count": 0,
       "count": 0,
       "role_list": ""
       "role_list": ""
@@ -308,7 +309,7 @@
       "parentKey": "1",
       "parentKey": "1",
       "x": -63,
       "x": -63,
       "y": -435,
       "y": -435,
-      "flow_id": 10,
+      "flow_id": 51,
       "node_type_psr": 1,
       "node_type_psr": 1,
       "count": 0,
       "count": 0,
       "role_list": ""
       "role_list": ""
@@ -357,7 +358,7 @@
       "x": -537,
       "x": -537,
       "y": -240,
       "y": -240,
       "zIndex": 10,
       "zIndex": 10,
-      "flow_id": 0,
+      "flow_id": 3,
       "node_type_psr": 1,
       "node_type_psr": 1,
       "count": 0
       "count": 0
     },
     },
@@ -381,7 +382,7 @@
       "x": -537,
       "x": -537,
       "y": -117,
       "y": -117,
       "zIndex": 10,
       "zIndex": 10,
-      "flow_id": 0,
+      "flow_id": 4,
       "node_type_psr": 1,
       "node_type_psr": 1,
       "count": 0
       "count": 0
     },
     },
@@ -404,7 +405,7 @@
       "parentKey": "1",
       "parentKey": "1",
       "x": 168,
       "x": 168,
       "y": -117,
       "y": -117,
-      "flow_id": 1,
+      "flow_id": 4,
       "node_type_psr": 1,
       "node_type_psr": 1,
       "count": 0,
       "count": 0,
       "role_list": ""
       "role_list": ""
@@ -428,7 +429,7 @@
       "parentKey": "1",
       "parentKey": "1",
       "x": 168,
       "x": 168,
       "y": -240,
       "y": -240,
-      "flow_id": 1,
+      "flow_id": 3,
       "node_type_psr": 1,
       "node_type_psr": 1,
       "count": 0,
       "count": 0,
       "role_list": ""
       "role_list": ""
@@ -453,7 +454,7 @@
       "x": -264,
       "x": -264,
       "y": -170,
       "y": -170,
       "zIndex": 10,
       "zIndex": 10,
-      "flow_id": 0,
+      "flow_id": 5,
       "node_type_psr": 1,
       "node_type_psr": 1,
       "count": 0
       "count": 0
     },
     },
@@ -477,7 +478,7 @@
       "x": 471,
       "x": 471,
       "y": -170,
       "y": -170,
       "zIndex": 10,
       "zIndex": 10,
-      "flow_id": 0,
+      "flow_id": 7,
       "node_type_psr": 1,
       "node_type_psr": 1,
       "count": 0
       "count": 0
     },
     },
@@ -501,7 +502,7 @@
       "x": 616,
       "x": 616,
       "y": -277,
       "y": -277,
       "flow_id": 0,
       "flow_id": 0,
-      "node_type_psr": 3, 
+      "node_type_psr": 4,
       "count": 0,
       "count": 0,
       "role_list": ""
       "role_list": ""
     },
     },
@@ -525,7 +526,7 @@
       "x": 631,
       "x": 631,
       "y": -435,
       "y": -435,
       "zIndex": 10,
       "zIndex": 10,
-      "flow_id": 0,
+      "flow_id": 48,
       "node_type_psr": 2,
       "node_type_psr": 2,
       "count": 0
       "count": 0
     },
     },

+ 14 - 10
src/pages/List/List.js

@@ -44,9 +44,12 @@ function List(props) {
     {
     {
       title: '所属项目',
       title: '所属项目',
       dataIndex: 'project_id',
       dataIndex: 'project_id',
-      render: project_id => {
-        let p = project.list.find(p => p.id == project_id);
-        if (p) return `${p.project_name}(${p.project_full_code})`;
+      render: projectID => {
+        const p = project.list.find(pItem => pItem.id === projectID);
+        if (p) {
+          return `${p.project_name}(${p.project_full_code})`;
+        }
+        return ''
       },
       },
     },
     },
     {
     {
@@ -105,6 +108,7 @@ function List(props) {
       ),
       ),
     },
     },
   ];
   ];
+
   const queryList = filter => {
   const queryList = filter => {
     filter = {...filterRes.current, ...filter};
     filter = {...filterRes.current, ...filter};
     dispatch({
     dispatch({
@@ -142,16 +146,16 @@ function List(props) {
   }, []);
   }, []);
 
 
   const changeVersion = item => {
   const changeVersion = item => {
-    if (typeof item == 'object') {
+    if (typeof item === 'object') {
       localStorage.excelItem = JSON.stringify(item);
       localStorage.excelItem = JSON.stringify(item);
     }
     }
-    router.push(`/home/detail/${item.project_id}/${item.template_id}`);
+    router.push(`/home/detail/${item.project_id}/${item.template_id}?version_id=${item.version_id}`);
   };
   };
 
 
-  const getLoading = () => {
-    let effects = loadingVersion.effects;
-    return !loadingVersion.effects['detail/queryComment'] && loadingVersion.models.detail;
-  };
+  // const getLoading = () => {
+  //   let effects = loadingVersion.effects;
+  //   return !loadingVersion.effects['detail/queryComment'] && loadingVersion.models.detail;
+  // };
 
 
   const onCommit = async (values, callback) => {
   const onCommit = async (values, callback) => {
     setCommitLoading(true);
     setCommitLoading(true);
@@ -167,7 +171,7 @@ function List(props) {
       callback: async newVersion => {
       callback: async newVersion => {
         // 更新flow流程图
         // 更新flow流程图
         const data = await queryBoomFlowDetail({id: newVersion.template_id});
         const data = await queryBoomFlowDetail({id: newVersion.template_id});
-        console.log(data);
+        // console.log(data);
         setFlowDetail(data);
         setFlowDetail(data);
         setCommitLoading(false);
         setCommitLoading(false);
         callback && callback();
         callback && callback();

+ 70 - 40
src/services/boom.js

@@ -1,18 +1,19 @@
-import { message } from 'antd';
+import {message} from 'antd';
 import request from '@/utils/request';
 import request from '@/utils/request';
-import { async } from '@antv/x6/lib/registry/marker/async';
-import { stringify } from 'qs';
+import {async} from '@antv/x6/lib/registry/marker/async';
+import {stringify} from 'qs';
 
 
 /**
 /**
-  project_id
-  version_id	大版本id
-  template_id	
-  template_node_id	查询某流程和某节点下最新版本的数据记录
-  node_id	查询某审批流程和某审批节点下最新版本的数据记录
+ project_id
+ version_id	大版本id
+ template_id
+ template_node_id	查询某流程和某节点下最新版本的数据记录
+ node_id	查询某审批流程和某审批节点下最新版本的数据记录
  */
  */
 export async function queryRecord(params) {
 export async function queryRecord(params) {
   return request(`/purchase/record?${stringify(params)}`);
   return request(`/purchase/record?${stringify(params)}`);
 }
 }
+
 //删除excel中单个sheet页
 //删除excel中单个sheet页
 export async function queryDelSheetRecord(params) {
 export async function queryDelSheetRecord(params) {
   const response = await request(`/purchase/bom/del-purchase-excel-sheet?${stringify(params)}`);
   const response = await request(`/purchase/bom/del-purchase-excel-sheet?${stringify(params)}`);
@@ -39,12 +40,14 @@ export async function commitSheet(params) {
     body: params,
     body: params,
   });
   });
 }
 }
+
 export async function approve(params) {
 export async function approve(params) {
   return request(`/purchase/audit/status`, {
   return request(`/purchase/audit/status`, {
     method: 'POST',
     method: 'POST',
     body: params,
     body: params,
   });
   });
 }
 }
+
 export async function queryAuthority(params) {
 export async function queryAuthority(params) {
   const depId = localStorage.depId;
   const depId = localStorage.depId;
   return request(`/purchase/bom/user/excel/col?depId=${depId}`, {
   return request(`/purchase/bom/user/excel/col?depId=${depId}`, {
@@ -52,27 +55,29 @@ export async function queryAuthority(params) {
     body: params,
     body: params,
   });
   });
 }
 }
+
 export async function addBomComment(params) {
 export async function addBomComment(params) {
   return request(`/purchase/comment`, {
   return request(`/purchase/comment`, {
     method: 'POST',
     method: 'POST',
     body: params,
     body: params,
   });
   });
 }
 }
+
 export async function queryBomComment(params) {
 export async function queryBomComment(params) {
   return request(`/purchase/comment?${stringify(params)}`);
   return request(`/purchase/comment?${stringify(params)}`);
 }
 }
 
 
 /**
 /**
  * 提交流转
  * 提交流转
-  "id":3, 当前流转文档id,必填
-  "project_id":46, 所属项目id
-  "template_id":1, 所属模板id ,必填
-  "template_node_id":34,所属节点id,必填
-  "next_template_id":1,跳转的下级业务模板id,必填
-  "next_template_node_id":2,跳转的下级业务节点id,必填
-  "flow_id":1, 跳转的下级审核流程id , 如果不为空,则说明流转的是审核节点,下级业务节点为审核通过后进入的业务节点
-  "node_id":1,跳转的下级审核节点id
-  "desc":"流转描述"
+ "id":3, 当前流转文档id,必填
+ "project_id":46, 所属项目id
+ "template_id":1, 所属模板id ,必填
+ "template_node_id":34,所属节点id,必填
+ "next_template_id":1,跳转的下级业务模板id,必填
+ "next_template_node_id":2,跳转的下级业务节点id,必填
+ "flow_id":1, 跳转的下级审核流程id , 如果不为空,则说明流转的是审核节点,下级业务节点为审核通过后进入的业务节点
+ "node_id":1,跳转的下级审核节点id
+ "desc":"流转描述"
  */
  */
 export async function submitNextNode(params) {
 export async function submitNextNode(params) {
   return request(`/purchase/next/node/submit`, {
   return request(`/purchase/next/node/submit`, {
@@ -80,6 +85,7 @@ export async function submitNextNode(params) {
     body: params,
     body: params,
   });
   });
 }
 }
+
 export async function advanceSubmitNextNode(params) {
 export async function advanceSubmitNextNode(params) {
   return request(`/api/v1/purchase/next/node/advance-submit`, {
   return request(`/api/v1/purchase/next/node/advance-submit`, {
     method: 'POST',
     method: 'POST',
@@ -98,9 +104,11 @@ export async function queryDetail(params) {
   });
   });
   return sheet;
   return sheet;
 }
 }
+
 export async function queryHistoryDetail(params) {
 export async function queryHistoryDetail(params) {
   return request(`/purchase/record/history/detail?${stringify(params)}`);
   return request(`/purchase/record/history/detail?${stringify(params)}`);
 }
 }
+
 export async function queryHistoryList(params) {
 export async function queryHistoryList(params) {
   return request(`/purchase/record/history?${stringify(params)}`);
   return request(`/purchase/record/history?${stringify(params)}`);
 }
 }
@@ -108,22 +116,27 @@ export async function queryHistoryList(params) {
 export async function queryBoomFlowList(params) {
 export async function queryBoomFlowList(params) {
   return request(`/purchase/bom/flows?${stringify(params)}`);
   return request(`/purchase/bom/flows?${stringify(params)}`);
 }
 }
+
 //请求历史版本
 //请求历史版本
 export async function queryVersionsTree(params) {
 export async function queryVersionsTree(params) {
   return request(`/api/v1/purchase/record/version/tree?${stringify(params)}`);
   return request(`/api/v1/purchase/record/version/tree?${stringify(params)}`);
 }
 }
+
 //查询业务节点的审核记录
 //查询业务节点的审核记录
 export async function queryAuditExcel(params) {
 export async function queryAuditExcel(params) {
   return request(`/api/v1/purchase/audit/excel?${stringify(params)}`);
   return request(`/api/v1/purchase/audit/excel?${stringify(params)}`);
 }
 }
+
 //查询审批节点的审核记录
 //查询审批节点的审核记录
 export async function queryAuditRecord(params) {
 export async function queryAuditRecord(params) {
   return request(`/api/v1/purchase/audit/record?${stringify(params)}`);
   return request(`/api/v1/purchase/audit/record?${stringify(params)}`);
 }
 }
+
 //查询表单数据接口
 //查询表单数据接口
 export async function queryDingSchema(params) {
 export async function queryDingSchema(params) {
   return request(`/api/v1/purchase/bom/ding/schema?${stringify(params)}`);
   return request(`/api/v1/purchase/bom/ding/schema?${stringify(params)}`);
 }
 }
+
 export async function queryDingInstanceDetail(params) {
 export async function queryDingInstanceDetail(params) {
   let res = await request(`/api/v1/purchase/bom/ding/instance-detail`, {
   let res = await request(`/api/v1/purchase/bom/ding/instance-detail`, {
     method: 'POST',
     method: 'POST',
@@ -135,6 +148,7 @@ export async function queryDingInstanceDetail(params) {
   }
   }
   return res;
   return res;
 }
 }
+
 export async function queryDingInstanceExecute(params) {
 export async function queryDingInstanceExecute(params) {
   let res = await request(`/api/v1/purchase/bom/ding/instance-execute`, {
   let res = await request(`/api/v1/purchase/bom/ding/instance-execute`, {
     method: 'POST',
     method: 'POST',
@@ -146,12 +160,14 @@ export async function queryDingInstanceExecute(params) {
   }
   }
   return res;
   return res;
 }
 }
+
 export async function queryListParentByUser(params) {
 export async function queryListParentByUser(params) {
   return request(`/api/v1/purchase/bom/ding/department/list-parent-by-user`, {
   return request(`/api/v1/purchase/bom/ding/department/list-parent-by-user`, {
     method: 'POST',
     method: 'POST',
     body: params,
     body: params,
   });
   });
 }
 }
+
 /**
 /**
  * 查看项目流程列表
  * 查看项目流程列表
  * project_id
  * project_id
@@ -159,20 +175,21 @@ export async function queryListParentByUser(params) {
 export async function queryProjectRecord(params) {
 export async function queryProjectRecord(params) {
   return request(`/purchase/bom/project/record?${stringify(params)}`);
   return request(`/purchase/bom/project/record?${stringify(params)}`);
 }
 }
+
 /** 查看版本列表
 /** 查看版本列表
- *  project_id		
-    template_id		流程id
-    template_node_id	流程节点id
+ *  project_id
+ template_id		流程id
+ template_node_id	流程节点id
  */
  */
 export async function queryVersionsList(params) {
 export async function queryVersionsList(params) {
   return request(`/purchase/record/versions?${stringify(params)}`);
   return request(`/purchase/record/versions?${stringify(params)}`);
 }
 }
 
 
 export async function queryBoomFlowDetail(params) {
 export async function queryBoomFlowDetail(params) {
-  let { data } = await request(`/purchase/bom/flow/info?${stringify(params)}`);
+  let {data} = await request(`/purchase/bom/flow/info?${stringify(params)}`);
   const groups = {
   const groups = {
     top: {
     top: {
-      position: { name: 'top' },
+      position: {name: 'top'},
       attrs: {
       attrs: {
         circle: {
         circle: {
           r: 4,
           r: 4,
@@ -180,13 +197,13 @@ export async function queryBoomFlowDetail(params) {
           stroke: '#31d0c6',
           stroke: '#31d0c6',
           strokeWidth: 2,
           strokeWidth: 2,
           fill: '#fff',
           fill: '#fff',
-          style: { visibility: 'hidden' },
+          style: {visibility: 'hidden'},
         },
         },
       },
       },
       zIndex: 10,
       zIndex: 10,
     },
     },
     right: {
     right: {
-      position: { name: 'right' },
+      position: {name: 'right'},
       attrs: {
       attrs: {
         circle: {
         circle: {
           r: 4,
           r: 4,
@@ -194,13 +211,13 @@ export async function queryBoomFlowDetail(params) {
           stroke: '#31d0c6',
           stroke: '#31d0c6',
           strokeWidth: 2,
           strokeWidth: 2,
           fill: '#fff',
           fill: '#fff',
-          style: { visibility: 'hidden' },
+          style: {visibility: 'hidden'},
         },
         },
       },
       },
       zIndex: 10,
       zIndex: 10,
     },
     },
     bottom: {
     bottom: {
-      position: { name: 'bottom' },
+      position: {name: 'bottom'},
       attrs: {
       attrs: {
         circle: {
         circle: {
           r: 4,
           r: 4,
@@ -208,13 +225,13 @@ export async function queryBoomFlowDetail(params) {
           stroke: '#31d0c6',
           stroke: '#31d0c6',
           strokeWidth: 2,
           strokeWidth: 2,
           fill: '#fff',
           fill: '#fff',
-          style: { visibility: 'hidden' },
+          style: {visibility: 'hidden'},
         },
         },
       },
       },
       zIndex: 10,
       zIndex: 10,
     },
     },
     left: {
     left: {
-      position: { name: 'left' },
+      position: {name: 'left'},
       attrs: {
       attrs: {
         circle: {
         circle: {
           r: 4,
           r: 4,
@@ -222,7 +239,7 @@ export async function queryBoomFlowDetail(params) {
           stroke: '#31d0c6',
           stroke: '#31d0c6',
           strokeWidth: 2,
           strokeWidth: 2,
           fill: '#fff',
           fill: '#fff',
-          style: { visibility: 'hidden' },
+          style: {visibility: 'hidden'},
         },
         },
       },
       },
       zIndex: 10,
       zIndex: 10,
@@ -231,7 +248,7 @@ export async function queryBoomFlowDetail(params) {
   const attrs = {
   const attrs = {
     line: {
     line: {
       stroke: '#A2B1C3',
       stroke: '#A2B1C3',
-      targetMarker: { name: 'block', width: 12, height: 8 },
+      targetMarker: {name: 'block', width: 12, height: 8},
       strokeDasharray: '5 5',
       strokeDasharray: '5 5',
       strokeWidth: 1,
       strokeWidth: 1,
     },
     },
@@ -261,7 +278,7 @@ export async function queryBoomFlowDetail(params) {
         cell: item.target_cell,
         cell: item.target_cell,
         port: item.target_port,
         port: item.target_port,
       },
       },
-      zIndex:0
+      zIndex: 0
     };
     };
     try {
     try {
       edge.attrs = item.attr ? JSON.parse(item.attr) : attrs;
       edge.attrs = item.attr ? JSON.parse(item.attr) : attrs;
@@ -276,12 +293,14 @@ export async function queryBoomFlowDetail(params) {
     edges,
     edges,
   };
   };
 }
 }
+
 export async function updateNode(data) {
 export async function updateNode(data) {
   return request(`/purchase/bom/flow/${data.templateId}/${data.nodeId}`, {
   return request(`/purchase/bom/flow/${data.templateId}/${data.nodeId}`, {
     method: 'PUT',
     method: 'PUT',
     body: data.body,
     body: data.body,
   });
   });
 }
 }
+
 export async function addBoomFlow(data) {
 export async function addBoomFlow(data) {
   return request(`/purchase/bom/flow/info`, {
   return request(`/purchase/bom/flow/info`, {
     method: 'POST',
     method: 'POST',
@@ -299,23 +318,25 @@ export async function addAudit(data) {
     body: data,
     body: data,
   });
   });
 }
 }
+
 export async function addFlow(data) {
 export async function addFlow(data) {
   return request(`/purchase/bom/flow/info`, {
   return request(`/purchase/bom/flow/info`, {
     method: 'POST',
     method: 'POST',
     body: data,
     body: data,
   });
   });
 }
 }
+
 /**
 /**
  *  [
  *  [
-      {
-        "flow_id": 23,
-        "node": "主管",
-        "desc": "desc",
-        "auditor": 2,
-        "seq": 1,
-        "seq_relate": 0
-      }
-    ]
+ {
+ "flow_id": 23,
+ "node": "主管",
+ "desc": "desc",
+ "auditor": 2,
+ "seq": 1,
+ "seq_relate": 0
+ }
+ ]
  */
  */
 export async function addAuditNode(data) {
 export async function addAuditNode(data) {
   return request(`/purchase/flow/info/${data.flowId}`, {
   return request(`/purchase/flow/info/${data.flowId}`, {
@@ -334,6 +355,7 @@ export async function queryRecordSheet(data) {
     body: data,
     body: data,
   });
   });
 }
 }
+
 export async function queryDingTemplateList() {
 export async function queryDingTemplateList() {
   return request(`/purchase/bom/ding/template/list`);
   return request(`/purchase/bom/ding/template/list`);
 }
 }
@@ -444,6 +466,7 @@ export async function queryDelPurchaseExcel(params) {
   });
   });
   return res;
   return res;
 }
 }
+
 //提交流转存储表单审批人历史记录
 //提交流转存储表单审批人历史记录
 export async function querySaveBomForm(data) {
 export async function querySaveBomForm(data) {
   return request(`/purchase/bom/save-bom-form`, {
   return request(`/purchase/bom/save-bom-form`, {
@@ -451,6 +474,7 @@ export async function querySaveBomForm(data) {
     body: data,
     body: data,
   });
   });
 }
 }
+
 export async function queryGetBomForm(params) {
 export async function queryGetBomForm(params) {
   let res = await request(`/purchase/bom/get-bom-form?${stringify(params)}`, {
   let res = await request(`/purchase/bom/get-bom-form?${stringify(params)}`, {
     method: 'GET',
     method: 'GET',
@@ -469,7 +493,13 @@ export async function queryTrySeal(params) {
 export async function ChartTempOSSData(params) {
 export async function ChartTempOSSData(params) {
   return request(`/purchase/bom/contract-file/${params.projectId}`);
   return request(`/purchase/bom/contract-file/${params.projectId}`);
 }
 }
+
 // 设置最终版本
 // 设置最终版本
 export async function setLastVersion(excelId) {
 export async function setLastVersion(excelId) {
   return request(`/purchase/bom/set-last-version/${excelId}`);
   return request(`/purchase/bom/set-last-version/${excelId}`);
 }
 }
+
+export async function getAuditDetail(params) {
+  const res = await request(`/purchase/bom/get-audit-detail/${params.userId}?version_id=${params.versionID}`)
+  return res.data
+}

+ 2 - 1
src/utils/request.js

@@ -2,7 +2,7 @@ import fetch from 'dva/fetch';
 import { message, notification } from 'antd';
 import { message, notification } from 'antd';
 import router from 'umi/router';
 import router from 'umi/router';
 import hash from 'hash.js';
 import hash from 'hash.js';
-import { isAntdPro, getToken, GetTokenFromUrl } from './utils';
+import { isAntdPro, getToken, GetTokenFromUrl, storeToken } from './utils';
 
 
 // var apiUrl = "http://oraysmart.com:8888"
 // var apiUrl = "http://oraysmart.com:8888"
 // var apiUrl = "http://120.55.44.4:8900"
 // var apiUrl = "http://120.55.44.4:8900"
@@ -90,6 +90,7 @@ export default function request(url, option, jwt) {
   let token = getToken();
   let token = getToken();
   if (!token) {
   if (!token) {
     token = GetTokenFromUrl();
     token = GetTokenFromUrl();
+    storeToken(token);
   }
   }
 
 
   const options = {
   const options = {

Vissa filer visades inte eftersom för många filer har ändrats