소스 검색

Merge branch 'master' into temp

xujunjie 1 년 전
부모
커밋
922867fd83

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
public/Luckysheet/luckysheet.umd.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
public/Luckysheet/luckysheet.umd.js.map


+ 3 - 0
src/components/AuditForm/ItemAttribute.js

@@ -296,6 +296,9 @@ function DDDateField(props) {
       <Form.Item label="提示文字" name="placeholder">
         <Input />
       </Form.Item>
+      <Form.Item label="必填" valuePropName="checked" name="required">
+        <Switch />
+      </Form.Item>
       {btns}
     </Form>
   );

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

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

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

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

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

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

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

@@ -58,7 +58,7 @@ function Auth(props) {
     if (item.type == 'checked') {
       version = checkedVersionList.find(v => v.id == item.id);
       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 {
       //调用接口获取version信息
       dispatch({
@@ -67,7 +67,7 @@ function Auth(props) {
         callback: checkedVersionList => {
           let version = checkedVersionList.find(v => v.id == item.id) || {};
           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 - 11
src/pages/Detail/AuditDetailed.js

@@ -1,6 +1,6 @@
 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';
 
 const layout = {
@@ -14,7 +14,7 @@ const layout = {
 
 const AuditDetailed = props => {
   // const [form] = Form.useForm();
-  const { items, form } = props;
+  const {items, form} = props;
 
   const behavior = useMemo(() => {
     let data = {};
@@ -27,14 +27,14 @@ const AuditDetailed = props => {
           try {
             data = JSON.parse(o);
           } catch (error) {
-            data = { key: o, value: o };
+            data = {key: o, value: o};
           }
           return data;
         });
         item.behaviorLinkage.forEach(b => {
           const value = b.value;
           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};
           });
         });
       }
@@ -78,11 +78,12 @@ const AuditDetailed = props => {
     } = item.props;
     // 判断是否属于关联项
     if (behavior[id]) {
-      const { key, value } = behavior[id];
+      const {key, value} = behavior[id];
       let currentValue = form.getFieldValue(key);
       try {
         currentValue = JSON.parse(currentValue);
-      } catch (error) {}
+      } catch (error) {
+      }
       // 判断是否需要渲染
       if (currentValue instanceof Array) {
         if (currentValue?.indexOf(value) == -1) return null;
@@ -102,15 +103,16 @@ const AuditDetailed = props => {
       }
     }
 
-    const component = DDComponents({ item });
+    const component = DDComponents({item});
     if (!component) return null;
     return (
       <Form.Item label={formLabel}>
         {typeof component == 'string'
           ? component
           : form.getFieldDecorator(id, {
-              rules: [{ required }],
-            })(component)}
+            rules: [{required}],
+            initialValue: item.props.defaultValue
+          })(component)}
         {notUpper == 1 && <p>大写</p>}
       </Form.Item>
     );
@@ -118,7 +120,7 @@ const AuditDetailed = props => {
 
   return (
     <Form
-      style={{ height: '400px', overflowY: 'scroll', paddingRight: 20 }}
+      style={{height: '400px', overflowY: 'scroll', paddingRight: 20}}
       layout="vertical"
       autoComplete="off"
     >

+ 67 - 45
src/pages/Detail/CommitAuditModal.js

@@ -1,4 +1,4 @@
-import React, { useEffect, useState, useRef, useMemo, useCallback } from 'react';
+import React, {useEffect, useState, useRef, useMemo, useCallback} from 'react';
 import '@ant-design/compatible/assets/index.css';
 import {
   Modal,
@@ -19,11 +19,11 @@ import {
   Divider,
   Collapse,
 } from 'antd';
-import { PlusOutlined, UploadOutlined } from '@ant-design/icons';
-import { connect } from 'dva';
-import { isArray, result, set } from 'lodash';
-import { useForm } from 'rc-field-form';
-import { async } from '@antv/x6/lib/registry/marker/async';
+import {PlusOutlined, UploadOutlined} from '@ant-design/icons';
+import {connect} from 'dva';
+import {isArray, result, set} from 'lodash';
+import {useForm} from 'rc-field-form';
+import {async} from '@antv/x6/lib/registry/marker/async';
 import AuditDetailed from './AuditDetailed';
 import AuditFlow from './AuditFlow';
 import {
@@ -32,20 +32,23 @@ import {
   queryProcessFlows,
   querySaveBomForm,
 } from '@/services/boom';
-import { Form as Form3x } from '@ant-design/compatible';
-import { getCurrentUser } from '@/utils/authority';
+import {Form as Form3x} from '@ant-design/compatible';
+import {getCurrentUser} from '@/utils/authority';
 import DDCode from '@/components/DDComponents/DDCode';
-import { uploadFile, queryUserListByRoleID } from '@/services/boom';
+import {uploadFile, queryUserListByRoleID} from '@/services/boom';
 import ApprovalProcess from './ApprovalProcess';
-import { uuidv4 } from '@antv/xflow';
+import {uuidv4} from '@antv/xflow';
 import AliyunOSSUpload from '@/components/OssUpload/AliyunOssUploader';
 import AttachmentTable from '@/components/AttachmentTable';
+import {getToken} from '@/utils/utils';
+import LuckyExcel from 'luckyexcel';
+import DDComponents from "@/components/DDComponents";
 import uploadExcelByUrl from '@/utils/uploadExcelByUrl';
 
-const { TextArea } = Input;
-const { Option } = Select;
-const { TabPane } = Tabs;
-const { Step } = Steps;
+const {TextArea} = Input;
+const {Option} = Select;
+const {TabPane} = Tabs;
+const {Step} = Steps;
 
 // 提交
 function CommitAuditModal(props) {
@@ -81,7 +84,7 @@ function CommitAuditModal(props) {
 
   useEffect(() => {
     if (!visible) return;
-    const { edges, nodes } = flowDetail;
+    const {edges, nodes} = flowDetail;
     // initFormList().then(approvalProcess => {
     let Id = version.template_node_id;
     const currentId = flowDetail.nodes.find?.(item => item.Id == Id)?.node_id;
@@ -104,8 +107,9 @@ function CommitAuditModal(props) {
       // 设置延迟,等待组件渲染
       setTimeout(async () => {
         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);
     }
     setData(data);
@@ -136,7 +140,8 @@ function CommitAuditModal(props) {
     uploadList.current = files.map(file => OSSData.host + '/' + file.url);
     console.log(uploadList.current);
   };
-  const OnUploading = file => {};
+  const OnUploading = file => {
+  };
   const uploadProps = {
     OSSData: OSSData,
     // onDone: OnModelFileDone,
@@ -154,8 +159,9 @@ function CommitAuditModal(props) {
     if (res.data) {
       const formList = JSON.parse(res.data.json);
       setApprovalProcess(formList.approvalProcess || {});
-      return formList.approvalProcess;
-      // setFormComponentValues(defaultFormData);
+      const prevFormData = JSON.parse(formList.formList[0]);
+      setFormComponentValues(prevFormData);
+      return formList;
     }
   };
 
@@ -204,7 +210,7 @@ function CommitAuditModal(props) {
    * @returns
    */
   const getNextNodes = (currentId, type) => {
-    const { edges, nodes } = flowDetail;
+    const {edges, nodes} = flowDetail;
     if (!currentId) return [];
     //删除虚线通向的节点
     // let targetIds = edges
@@ -250,10 +256,18 @@ function CommitAuditModal(props) {
     setAuditId(node?.node_id);
   };
 
-  const onChange = value => {
+  const onChange = async (value, approvalProcess) => {
+    // 加载之前提交的form数据
+    const resFormData = await initFormList();
+    const resData = resFormData?.formList;
+    const prevFormData = resData && resData.length ? JSON.parse(resData[0]) : null;
     if (value) {
       changeAudit(value[value.length - 1]);
-      setAuditListFun(approvalProcess);
+      if (prevFormData !== null && prevFormData?.formComponentValues?.length && value[0] === prevFormData?.template_node_id) {
+        setAuditListFun(approvalProcess, prevFormData.formComponentValues);
+      } else {
+        setAuditListFun(approvalProcess);
+      }
     } else {
       changeAudit('');
       setAuditList([]);
@@ -310,7 +324,7 @@ function CommitAuditModal(props) {
   };
 
   //处理tabs页
-  const setAuditListFun = async (approvalProcess = {}) => {
+  const setAuditListFun = async (approvalProcess = {},prevFormData = []) => {
     var fieldsValue = await form.validateFields();
     let addAuditList = [];
     let result = Object.values(fieldsValue)
@@ -325,7 +339,7 @@ function CommitAuditModal(props) {
       })
       .filter(item => item);
     let flowIds = [...new Set(nodeList.map(item => item.flow_id))].join(',');
-    let data = await queryProcessFlows({ ids: flowIds });
+    let data = await queryProcessFlows({ids: flowIds});
     if (data && data?.length > 0) {
       let newlist = nodeList.map(node => {
         let curData = data.find(item => item.id == node.flow_id);
@@ -340,9 +354,17 @@ function CommitAuditModal(props) {
       addAuditList = [...addAuditList, ...newlist];
     }
     addAuditList.forEach((item, index) => {
+      // 回填历史数据
+      if (prevFormData.length) {
+        item.items.forEach(DDComponent => {
+          const prevValue = prevFormData.find(pItem => pItem.id === DDComponent.props.id)
+          DDComponent.props.defaultValue = prevValue.value
+        })
+      }
       let Components = Form3x.create({
         onValuesChange: (props, changedValues, allValues) => {
-          const { items } = props;
+          const {items} = props;
+          console.log(allValues)
           formComponentValues[item.nodeId] = items
             .map(item => {
               const itemProps = item.props;
@@ -364,16 +386,16 @@ function CommitAuditModal(props) {
             })
             .filter(item => item);
           if (getReComputeAudit(items, changedValues)) advanceSubmit();
-          setFormComponentValues({ ...formComponentValues });
+          setFormComponentValues({...formComponentValues});
         },
       })(AuditDetailed);
-      item.FormComponents = <Components items={item.items} />;
+      item.FormComponents = <Components items={item.items}/>;
     });
     setAuditList(addAuditList);
-    if (Object.keys(approvalProcess).length == 0) advanceSubmit();
+    advanceSubmit();
   };
 
-  const getFromData = async idList => {
+  const getFromData = idList => {
     const data = formComponentValues;
     const result = [];
     //获取流转节点的层级关系
@@ -493,7 +515,7 @@ function CommitAuditModal(props) {
     const flowPath = result.map(item => getFlowPath(item));
     setLoading(true);
     try {
-      const formList = await getFromData(result);
+      const formList = getFromData(result);
       let params = {
         desc: fieldsValue.desc,
         // 审核流程id
@@ -547,7 +569,7 @@ function CommitAuditModal(props) {
       await querySaveBomForm({
         project_id: version.project_id,
         node_id: version.template_node_id,
-        json: JSON.stringify({ approvalProcess }),
+        json: JSON.stringify({approvalProcess}),
       });
       params.audit_series = uuidv4();
       params.files = uploadList.current.join(',');
@@ -576,13 +598,13 @@ function CommitAuditModal(props) {
   const CascaderNode = index => {
     return (
       <Form.Item
-        labelCol={{ span: 7 }}
-        wrapperCol={{ span: 15 }}
+        labelCol={{span: 7}}
+        wrapperCol={{span: 15}}
         label={`审批节点${index + 1}`}
         name={`circle${index}`}
         key={`circle${index}`}
       >
-        <Cascader style={{ width: '100%' }} options={data} onChange={onChange} />
+        <Cascader style={{width: '100%'}} options={data} onChange={onChange}/>
       </Form.Item>
     );
   };
@@ -644,28 +666,28 @@ function CommitAuditModal(props) {
       <Form form={form}>
         {data.map((item, idx) => (data.length == 1 ? CascaderNode('') : CascaderNode(idx)))}
         <Form.Item
-          labelCol={{ span: 7 }}
-          wrapperCol={{ span: 15 }}
+          labelCol={{span: 7}}
+          wrapperCol={{span: 15}}
           label="业务节点"
           name="next_template_node_id"
           // rules={[{ required: true, message: '请选择业务节点' }]}
         >
-          <Select style={{ width: '100%' }}>
+          <Select style={{width: '100%'}}>
             {nextNodesList.map(item => (
               <Option key={item.value}>{item.label}</Option>
             ))}
           </Select>
         </Form.Item>
-        <Form.Item labelCol={{ span: 7 }} wrapperCol={{ span: 15 }} label="备注信息" name="desc">
-          <Input.TextArea />
+        <Form.Item labelCol={{span: 7}} wrapperCol={{span: 15}} label="备注信息" name="desc">
+          <Input.TextArea/>
         </Form.Item>
-        <Form.Item labelCol={{ span: 7 }} wrapperCol={{ span: 15 }} label="附件">
-          {OSSData.host && <AliyunOSSUpload {...uploadProps} directory={false} label="上传文件" />}
+        <Form.Item labelCol={{span: 7}} wrapperCol={{span: 15}} label="附件">
+          {OSSData.host && <AliyunOSSUpload {...uploadProps} directory={false} label="上传文件"/>}
         </Form.Item>
       </Form>
-      <Collapse style={{ marginTop: 20 }}>
+      <Collapse style={{marginTop: 20}}>
         <Collapse.Panel header="已上传附件" key="1">
-          <AttachmentTable version={version} canDelete={version.last_version == 0} />
+          <AttachmentTable version={version} canDelete={version.last_version == 0}/>
         </Collapse.Panel>
       </Collapse>
       <Tabs defaultActiveKey="1">
@@ -675,7 +697,7 @@ function CommitAuditModal(props) {
               <Col span={17}>{item.FormComponents}</Col>
               <Col offset={1} span={6}>
                 {!approvalProcess[item.nodeId] ? ( //!formComponentValues[item.nodeId] ||
-                  <Empty description="请先填写表单" />
+                  <Empty description="请先填写表单"/>
                 ) : (
                   <ApprovalProcess
                     id={item.nodeId}

+ 287 - 346
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 {
   Modal,
   Input,
@@ -18,24 +18,21 @@ import {
   Tooltip,
 } from 'antd';
 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 {
-  queryDelPurchaseExcel,
-  queryDingInstanceDetail,
-  queryRecordSheet,
-  queryTrySeal,
-  queryVserionByNode,
+  queryDelPurchaseExcel, queryDingInstanceDetail, queryRecordSheet, queryTrySeal, queryVserionByNode,
 } 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 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 PAGE_SIZE = 8;
 let controller = new AbortController();
@@ -44,10 +41,7 @@ let controller = new AbortController();
 function FlowModal(props) {
   let token = getToken();
   const SELECT_TYPE = {
-    NAME: '0',
-    TYPE: '1',
-    CREATOR: '2',
-    STATE: '3',
+    NAME: '0', TYPE: '1', CREATOR: '2', STATE: '3',
   };
   const {
     visible,
@@ -75,6 +69,7 @@ function FlowModal(props) {
   const [versionVisible, setVersionVisible] = useState(false);
   const [selectType, setSelectType] = useState(SELECT_TYPE.NAME);
   const [inputValue, setInputValue] = useState();
+  const [expandedRowKey, setExpandedRowKey] = useState([])
 
   const [sealLoading, setSealLoading] = useState(false);
   // const [currentTempNodeId, setCurrentTempNodeId] = useState();
@@ -83,12 +78,10 @@ function FlowModal(props) {
   const graphData = useMemo(() => {
     if (!flowDetail) return;
     let nodes = flowDetail.nodes?.map(item => ({
-      ...item,
-      isCheck: item.Id == version.template_node_id,
+      ...item, isCheck: item.Id == version.template_node_id,
     }));
     return {
-      nodes,
-      edges: flowDetail.edges,
+      nodes, edges: flowDetail.edges,
     };
   }, [flowDetail, version.template_node_id]);
 
@@ -115,17 +108,12 @@ function FlowModal(props) {
 
   const onDelVersion = data => {
     Modal.confirm({
-      title: '提示',
-      content: `是否确认删除清单?`,
-      okText: '确定',
-      cancelText: '取消',
-      onOk: async () => {
+      title: '提示', content: `是否确认删除清单?`, okText: '确定', cancelText: '取消', onOk: async () => {
         const res = await queryDelPurchaseExcel(data);
         if (res.code == 200) {
           message.success('删除成功');
           dispatch({
-            type: 'xflow/queryBoomFlowDetail',
-            payload: {
+            type: 'xflow/queryBoomFlowDetail', payload: {
               id: templateId,
             },
           });
@@ -142,18 +130,15 @@ function FlowModal(props) {
       }
       setNodeLoading(true);
       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;
       let data = [];
       if (!res.data.excel_version_tree) setData([]);
       res.data.excel_version_tree?.map(arr => {
         if (res.data.flow_id) {
-          data = [...data, { ...arr, flow_id: res.data.flow_id }];
+          data = [...data, {...arr, flow_id: res.data.flow_id}];
         } else {
           data = [...data, arr];
         }
@@ -164,11 +149,11 @@ function FlowModal(props) {
         data[id].key = `${id}-${item.name}`;
         item.isParent = true;
       });
-      console.log(data);
+      // console.log(data);
       currentTempNodeId.current = template_node_id;
       setData(data);
     } catch (error) {
-      console.log(error);
+      // console.log(error);
     }
     setNodeLoading(false);
     updateSteps([]);
@@ -192,14 +177,9 @@ function FlowModal(props) {
         if (item.audit_status == 2) status = 'error';
       });
       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 = {
-        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;
     });
@@ -209,162 +189,118 @@ function FlowModal(props) {
   const handleChangeClick = item => {
     let file = isOut ? 'list' : 'detail';
     let type = item.flow_id ? '/queryAuditRecord' : '/queryAuditExcel';
-    console.log(`${file}${type}`, item);
+    // console.log(`${file}${type}`, item);
     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);
       },
     });
   };
 
   const columns = useMemo(() => {
-    return [
-      {
-        title: '名称',
-        width: '20%',
-        render: item => (
-          <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>
-        ),
+    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>
+        </>)
       },
-      {
-        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>
-          );
-        },
-      },
-      {
-        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>
-          );
-        },
+        // 显示拒绝原因
+        // 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: '20%',
-        render: item =>
-          (item.flow_id || !item.isParent) &&
-          item.id != version.id && (
-            <Space>
-              <a
-                onClick={() => {
-                  console.log(item);
-                  onChangeVersion(item);
-                  onClose();
-                }}
-              >
-                加载
-              </a>
-              {item.audit_status == 0 &&
-              item.author == currentUser.ID && ( //自己创建的&&未提交的清单自己可以删除
-                  <a
-                    onClick={() => {
-                      onDelVersion({ excel_id: 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>),
+    },];
   }, [version]);
 
   const handleRetryClick = async id => {
     setNodeLoading(true);
-    const res = await queryTrySeal({ excel_id: id });
+    const res = await queryTrySeal({excel_id: id});
     setNodeLoading(false);
     if (res.data?.errcode != 0) {
       message.error(res.data?.errmsg);
@@ -385,16 +321,23 @@ function FlowModal(props) {
 
   const getDescription = node => {
     let str = `审批人:${node.AuditorUser?.CName || '-'}`;
-    return (
+    const date = new Date(node.audit_time)
+    const auditTime = date.toLocaleDateString('zh-CN', {
+      format: 'YYYY-MM-DD hh:mm:ss'
+    })
+    return (<div>
+      审批人:{node.AuditorUser?.CName || '-'}
       <div>
-        审批人:{node.AuditorUser?.CName || '-'}
-        <div>
-          <span style={{ color: '#1A73E8', textDecoration: 'undeline' }}>
+        <span style={{color: '#1A73E8', textDecoration: 'undeline'}}>
             审批意见:{node.desc || '-'}
-          </span>
-        </div>
+        </span>
       </div>
-    );
+      <div>
+        <span>
+          审批时间:{auditTime || '-'}
+        </span>
+      </div>
+    </div>);
   };
 
   const filterState = () => {
@@ -431,6 +374,19 @@ function FlowModal(props) {
     setShowData(resultData);
   }, [inputValue, data]);
 
+  const setRowClassName = (row) => {
+    const rowId = localStorage.excelId
+
+    if (row.id.toString() === rowId) {
+      return styles.selectedROW;
+    }
+    return ''
+  }
+
+  const handleExpandedRowChange = (expandedRows) => {
+    setExpandedRowKey(expandedRows)
+  }
+
   //列表筛选状态
   const STATE = {
     NOSUBMIT: 0, //未提交
@@ -440,159 +396,146 @@ function FlowModal(props) {
     SUBMIT: 4, //已提交
     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>
-                )}
-              </div>
-              {isOut && (
-                <Button type="primary" onClick={() => setVersionVisible(true)}>
-                  新建清单
-                </Button>
-              )}
+  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 style={{ width: '100%', marginTop: '10px' }}>
-              <Table
-                columns={columns}
-                dataSource={showData}
-                loading={nodeLoading}
-                bordered={false}
-                pagination={{ position: ['none', 'none'], pageSize: 999, onChange }}
-                scroll={{ y: '460px' }}
-                // 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.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}
+    />
+  </>);
 }
 
 const getColor = item => {
@@ -617,9 +560,7 @@ const getColor = item => {
   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);
 // export default FlowModal;

+ 47 - 31
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 LuckySheet from './LuckySheet';
 import AuditModal from './AuditModal';
@@ -18,8 +18,9 @@ import VersionModal from './VersionModal';
 import CommitAuditModal from './CommitAuditModal';
 import CommentContent from '@/components/CommentContent';
 import MergeModal from './MergeModal';
-import { GetTokenFromUrl, getToken } from '@/utils/utils';
+import {GetTokenFromUrl, getToken} from '@/utils/utils';
 import {
+  getAuditDetail,
   queryDelSheetRecord,
   queryDetail,
   queryDingInstanceExecute,
@@ -27,14 +28,16 @@ import {
 } from '@/services/boom';
 import HistoryDrawer from './HistoryDrawer';
 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 PreviewFile from '@/components/PreviewFile';
 import FormAndFilesNode from './FormAndFilesNode';
 import DropdownMenu from './DropdownMenu';
 import CurrentInfo from './CurrentInfo';
 import moment from 'moment';
+import {LocalStorage} from "@antv/x6";
+
 const LocalData = localStorage.luckysheet;
 
 function Detail(props) {
@@ -46,7 +49,8 @@ function Detail(props) {
     auditList,
     flowDetail,
     versionTree,
-    match: { params },
+    match: {params},
+    location: {query},
     instanceDetail,
     typeOptions,
     classifyList,
@@ -107,6 +111,11 @@ function Detail(props) {
 
   const projectId = parseInt(params.projectId);
   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(() => {
     let data = {
@@ -263,7 +272,7 @@ function Detail(props) {
     await queryDelSheetRecord(params);
   };
 
-  const onAudit = ({ audit_comment, audit_status }) => {
+  const onAudit = ({audit_comment, audit_status}) => {
     const flowNode = flow.currentNode;
     dispatch({
       type: 'detail/approve',
@@ -299,7 +308,7 @@ function Detail(props) {
         } else {
           dispatch({
             type: 'authList/queryAuthList',
-            payloda: { user_id: currentUser.ID },
+            payloda: {user_id: currentUser.ID},
           });
           localStorage.excelId = newVersion.id;
           setVersion({
@@ -311,7 +320,7 @@ function Detail(props) {
         //更新未审批列表
         dispatch({
           type: 'authList/queryAuthList',
-          payloda: { user_id: currentUser.ID },
+          payloda: {user_id: currentUser.ID},
         });
       },
     });
@@ -389,15 +398,23 @@ function Detail(props) {
     return !loading.effects['detail/queryComment'] && loading.models.detail;
   };
 
-  const changeVersion = id => {
+  const changeVersion = async id => {
     let version;
     if (typeof id == 'object') {
       version = id;
       localStorage.excelId = version.id;
       localStorage.excelItem = JSON.stringify(version);
     } 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;
     }
     setVersion(version);
@@ -430,7 +447,8 @@ function Detail(props) {
         userRef.current = newUser;
         setUser(newUser);
       }
-    } catch (error) {}
+    } catch (error) {
+    }
   };
   const handleSubmitCell = (value, callback) => {
     if (!value) return;
@@ -486,7 +504,7 @@ function Detail(props) {
 
   useEffect(() => {
     if (compareList.length == 2) {
-      const callback = ({ diff, add }) => {
+      const callback = ({diff, add}) => {
         setUpdateCount(updateCount => {
           return {
             diff: diff.length,
@@ -500,22 +518,20 @@ function Detail(props) {
   }, [compareList]);
 
   useEffect(() => {
-    if (versionList.length == 0) return;
+    // if (versionList.length == 0) return;
+    if (!currentUser.ID) return
     if (!version.id) {
-      const excelId = localStorage.excelItem
-        ? JSON.parse(localStorage.excelItem)
-        : localStorage.excelId;
-      changeVersion(excelId);
+      changeVersion(versionID);
     } else {
       changeVersion(version.id);
     }
-  }, [versionList]);
+  }, [versionList, currentUser]);
 
   return (
     <>
       <div className={styles.top}>
         <div>
-          <Button type="primary" style={{ marginRight: 20 }} onClick={() => setFlowVisible(true)}>
+          <Button type="primary" style={{marginRight: 20}} onClick={() => setFlowVisible(true)}>
             查看流程
           </Button>
 
@@ -525,10 +541,10 @@ function Detail(props) {
               新建清单
             </Button>
           )}
-          <CurrentInfo version={version} flowDetail={flowDetail} />
+          <CurrentInfo version={version} flowDetail={flowDetail}/>
         </div>
         <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 && (
             <Button type="primary" loading={loading.effects['detail/saveSheet']} onClick={onUpdate}>
               保存
@@ -536,16 +552,16 @@ function Detail(props) {
           )}
           <Button
             type="primary"
-            style={{ marginRight: 20 }}
+            style={{marginRight: 20}}
             onClick={() => setVersionTreeVisible(true)}
           >
             历史版本
           </Button>
-          <Avatar.Group style={{ marginRight: 20 }}>
+          <Avatar.Group style={{marginRight: 20}}>
             {user.map((item, id) => (
               <Avatar
                 key={`${id}-${item.name}`}
-                style={{ backgroundColor: '#008dff' }}
+                style={{backgroundColor: '#008dff'}}
                 size="large"
               >
                 {item.Name}
@@ -569,11 +585,11 @@ function Detail(props) {
         <input
           type="file"
           ref={fileRef}
-          style={{ display: 'none' }}
+          style={{display: 'none'}}
           onChange={e => exportExcl(e.target.files)}
         />
       </div>
-      <div style={{ display: 'flex' }}>
+      <div style={{display: 'flex'}}>
         <div
           className={styles.content}
           style={{
@@ -709,7 +725,7 @@ function Detail(props) {
   );
 }
 
-export default connect(({ detail, user, xflow, loading }) => ({
+export default connect(({detail, user, xflow, loading}) => ({
   flowDetail: xflow.flowDetail,
   auditList: detail.auditList,
   instanceDetail: detail.dingInstanceDetail,

+ 4 - 0
src/pages/Detail/Index.less

@@ -69,3 +69,7 @@
   // justify-content: center;
   // align-items: center;
 }
+
+.selectedROW{
+  background-color: rgba(186, 224, 255, 0.5);
+}

+ 21 - 12
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 { 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 { getCurrentUser } from '@/utils/authority';
+import {getCurrentUser} from '@/utils/authority';
+
+const {Step} = Steps;
 
-const { Step } = Steps;
 // 时间节点
 function TimeNode(props) {
   const {
@@ -23,21 +24,29 @@ function TimeNode(props) {
     stepDirection,
     currentUser,
   } = props;
-  const { current, list, active } = flow;
+  const {current, list, active} = flow;
 
   const getDescription = node => {
     let str = node?.AuditRoleInfo
       ? `审批人:${node?.AuditRoleInfo.Name || '-'}`
       : `审批人:${node?.AuditorUser.CName || '-'}`;
-
+    const date = new Date(node.audit_time)
+    const auditTime = date.toLocaleDateString('zh-CN', {
+      format: 'YYYY-MM-DD hh:mm:ss'
+    })
     return (
       <div>
         {str}
         <div>
-          <span style={{ color: '#1A73E8', textDecoration: 'undeline' }}>
+          <span style={{color: '#1A73E8', textDecoration: 'undeline'}}>
             审批意见:{node.desc || '-'}
           </span>
         </div>
+        <div>
+          <span>
+            审批时间:{auditTime || '-'}
+          </span>
+        </div>
       </div>
     );
 
@@ -53,11 +62,11 @@ function TimeNode(props) {
           status={active == 0 ? 'error' : 'process'}
         >
           {list.FlowNodes.map(item => (
-            <Step key={item.id} title={item.node} description={getDescription(item)} />
+            <Step key={item.id} title={item.node} description={getDescription(item)}/>
           ))}
         </Steps>
         {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>
@@ -77,7 +86,7 @@ function TimeNode(props) {
   return null;
 }
 
-export default connect(({ user, detail }) => ({
+export default connect(({user, detail}) => ({
   currentUser: user.currentUser,
   versionList: detail.versionList,
 }))(TimeNode);

+ 41 - 20
src/pages/List/List.js

@@ -1,6 +1,6 @@
-import React, { useState, useEffect, useRef } from 'react';
-import { Button, Divider, Form, Input, message, Modal, Select, Table } from 'antd';
-import { connect } from 'dva';
+import React, {useState, useEffect, useRef} from 'react';
+import {Button, Divider, Form, Input, message, Modal, Select, Table} from 'antd';
+import {connect} from 'dva';
 import router from 'umi/router';
 import FlowModal from '../Detail/FlowModal';
 import {
@@ -10,13 +10,14 @@ import {
   queryDelPurchaseExcel,
   queryRecordSheet,
 } from '@/services/boom';
-import { getToken } from '@/utils/utils';
+import {getToken} from '@/utils/utils';
 import ClassifyModal from './ClassifyModal';
 import VersionModal from './VersionModal';
+
 let token = getToken();
 
 function List(props) {
-  const { excel, loading, project, dispatch, typeOptions, userList, versionList } = props;
+  const {excel, loading, project, dispatch, typeOptions, userList, versionList} = props;
   const [flowVisible, setFlowVisible] = useState(false);
   const [version, setVersion] = useState({});
   const [versionVisible, setVersionVisible] = useState(false);
@@ -57,11 +58,12 @@ function List(props) {
             onClick={async () => {
               try {
                 setClassifyLoading(true);
-                const data = await queryBindClassify({ project_id: record.project_id }); //record.project_id
+                const data = await queryBindClassify({project_id: record.project_id}); //record.project_id
                 setClassifyLoading(false);
-                setData({ project_id: record.project_id, classify: data });
+                setData({project_id: record.project_id, classify: data});
                 setVisible(true);
-              } catch (error) {}
+              } catch (error) {
+              }
             }}
           >
             分类权限
@@ -72,7 +74,7 @@ function List(props) {
               localStorage.excelId = record.id;
               setLoading2(true);
               try {
-                const data = await queryBoomFlowDetail({ id: record.template_id });
+                const data = await queryBoomFlowDetail({id: record.template_id});
                 setFlowDetail(data);
                 setVersion(record);
                 setFlowVisible(true);
@@ -82,7 +84,8 @@ function List(props) {
                     project_id: record.project_id,
                   },
                 });
-              } catch (error) {}
+              } catch (error) {
+              }
               setLoading2(false);
               // router.push(`/home/detail/${record.project_id}/${record.template_id}`);
             }}
@@ -103,7 +106,7 @@ function List(props) {
     },
   ];
   const queryList = filter => {
-    filter = { ...filterRes.current, ...filter };
+    filter = {...filterRes.current, ...filter};
     dispatch({
       type: 'list/queryProjectRecord',
       payload: {
@@ -142,7 +145,7 @@ function List(props) {
     if (typeof item == 'object') {
       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 = () => {
@@ -152,7 +155,7 @@ function List(props) {
 
   const onCommit = async (values, callback) => {
     setCommitLoading(true);
-    let sheets = await queryRecordSheet({ gridKey: version.id, 'JWT-TOKEN': token });
+    let sheets = await queryRecordSheet({gridKey: version.id, 'JWT-TOKEN': token});
 
     let params = {
       ...values,
@@ -163,7 +166,7 @@ function List(props) {
       payload: params,
       callback: async newVersion => {
         // 更新flow流程图
-        const data = await queryBoomFlowDetail({ id: newVersion.template_id });
+        const data = await queryBoomFlowDetail({id: newVersion.template_id});
         console.log(data);
         setFlowDetail(data);
         setCommitLoading(false);
@@ -186,7 +189,7 @@ function List(props) {
         const res = await queryDelPurchaseExcel(data);
         if (res.code == 200) {
           message.success('删除成功');
-          const data = await queryBoomFlowDetail({ id: version.template_id });
+          const data = await queryBoomFlowDetail({id: version.template_id});
           console.log(data);
           setFlowDetail(data);
         }
@@ -199,15 +202,18 @@ function List(props) {
       <Form
         form={form}
         layout="inline"
-        style={{ marginBottom: 20 }}
-        onFinish={filter => queryList({ ...filter, currentPage: 1 })}
+        style={{marginBottom: 20}}
+        onFinish={filter => queryList({...filter, currentPage: 1})}
       >
         <Form.Item label="流程名称" name="name">
-          <Input />
+          <Input/>
+        </Form.Item>
+        <Form.Item label="项目编号" name="project_full_code">
+          <Input/>
         </Form.Item>
         <Form.Item label="所属项目" name="project_id">
           <Select
-            style={{ width: 200 }}
+            style={{width: 200}}
             showSearch
             allowClear
             options={project.list.map(item => ({
@@ -219,6 +225,21 @@ function List(props) {
             }
           ></Select>
         </Form.Item>
+        {/* 字段确定后展开 */}
+        {/* <Form.Item label="项目经理" name="project_id"> */}
+        {/*   <Select */}
+        {/*     style={{width: 200}} */}
+        {/*     showSearch */}
+        {/*     allowClear */}
+        {/*     options={project.list.map(item => ({ */}
+        {/*       label: item.project_name, */}
+        {/*       value: item.id, */}
+        {/*     }))} */}
+        {/*     filterOption={(input, option) => */}
+        {/*       (option?.label ?? '').toLowerCase().includes(input.toLowerCase()) */}
+        {/*     } */}
+        {/*   ></Select> */}
+        {/* </Form.Item> */}
         <Form.Item>
           <Button htmlType="submit" type="primary">
             查询
@@ -267,7 +288,7 @@ function List(props) {
   );
 }
 
-export default connect(({ list, loading, user }) => ({
+export default connect(({list, loading, user}) => ({
   excel: list.excel,
   versionList: list.versionList,
   project: list.project,

+ 70 - 40
src/services/boom.js

@@ -1,18 +1,19 @@
-import { message } from 'antd';
+import {message} from 'antd';
 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) {
   return request(`/purchase/record?${stringify(params)}`);
 }
+
 //删除excel中单个sheet页
 export async function queryDelSheetRecord(params) {
   const response = await request(`/purchase/bom/del-purchase-excel-sheet?${stringify(params)}`);
@@ -39,12 +40,14 @@ export async function commitSheet(params) {
     body: params,
   });
 }
+
 export async function approve(params) {
   return request(`/purchase/audit/status`, {
     method: 'POST',
     body: params,
   });
 }
+
 export async function queryAuthority(params) {
   const depId = localStorage.depId;
   return request(`/purchase/bom/user/excel/col?depId=${depId}`, {
@@ -52,27 +55,29 @@ export async function queryAuthority(params) {
     body: params,
   });
 }
+
 export async function addBomComment(params) {
   return request(`/purchase/comment`, {
     method: 'POST',
     body: params,
   });
 }
+
 export async function queryBomComment(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) {
   return request(`/purchase/next/node/submit`, {
@@ -80,6 +85,7 @@ export async function submitNextNode(params) {
     body: params,
   });
 }
+
 export async function advanceSubmitNextNode(params) {
   return request(`/api/v1/purchase/next/node/advance-submit`, {
     method: 'POST',
@@ -98,9 +104,11 @@ export async function queryDetail(params) {
   });
   return sheet;
 }
+
 export async function queryHistoryDetail(params) {
   return request(`/purchase/record/history/detail?${stringify(params)}`);
 }
+
 export async function queryHistoryList(params) {
   return request(`/purchase/record/history?${stringify(params)}`);
 }
@@ -108,22 +116,27 @@ export async function queryHistoryList(params) {
 export async function queryBoomFlowList(params) {
   return request(`/purchase/bom/flows?${stringify(params)}`);
 }
+
 //请求历史版本
 export async function queryVersionsTree(params) {
   return request(`/api/v1/purchase/record/version/tree?${stringify(params)}`);
 }
+
 //查询业务节点的审核记录
 export async function queryAuditExcel(params) {
   return request(`/api/v1/purchase/audit/excel?${stringify(params)}`);
 }
+
 //查询审批节点的审核记录
 export async function queryAuditRecord(params) {
   return request(`/api/v1/purchase/audit/record?${stringify(params)}`);
 }
+
 //查询表单数据接口
 export async function queryDingSchema(params) {
   return request(`/api/v1/purchase/bom/ding/schema?${stringify(params)}`);
 }
+
 export async function queryDingInstanceDetail(params) {
   let res = await request(`/api/v1/purchase/bom/ding/instance-detail`, {
     method: 'POST',
@@ -135,6 +148,7 @@ export async function queryDingInstanceDetail(params) {
   }
   return res;
 }
+
 export async function queryDingInstanceExecute(params) {
   let res = await request(`/api/v1/purchase/bom/ding/instance-execute`, {
     method: 'POST',
@@ -146,12 +160,14 @@ export async function queryDingInstanceExecute(params) {
   }
   return res;
 }
+
 export async function queryListParentByUser(params) {
   return request(`/api/v1/purchase/bom/ding/department/list-parent-by-user`, {
     method: 'POST',
     body: params,
   });
 }
+
 /**
  * 查看项目流程列表
  * project_id
@@ -159,20 +175,21 @@ export async function queryListParentByUser(params) {
 export async function queryProjectRecord(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) {
   return request(`/purchase/record/versions?${stringify(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 = {
     top: {
-      position: { name: 'top' },
+      position: {name: 'top'},
       attrs: {
         circle: {
           r: 4,
@@ -180,13 +197,13 @@ export async function queryBoomFlowDetail(params) {
           stroke: '#31d0c6',
           strokeWidth: 2,
           fill: '#fff',
-          style: { visibility: 'hidden' },
+          style: {visibility: 'hidden'},
         },
       },
       zIndex: 10,
     },
     right: {
-      position: { name: 'right' },
+      position: {name: 'right'},
       attrs: {
         circle: {
           r: 4,
@@ -194,13 +211,13 @@ export async function queryBoomFlowDetail(params) {
           stroke: '#31d0c6',
           strokeWidth: 2,
           fill: '#fff',
-          style: { visibility: 'hidden' },
+          style: {visibility: 'hidden'},
         },
       },
       zIndex: 10,
     },
     bottom: {
-      position: { name: 'bottom' },
+      position: {name: 'bottom'},
       attrs: {
         circle: {
           r: 4,
@@ -208,13 +225,13 @@ export async function queryBoomFlowDetail(params) {
           stroke: '#31d0c6',
           strokeWidth: 2,
           fill: '#fff',
-          style: { visibility: 'hidden' },
+          style: {visibility: 'hidden'},
         },
       },
       zIndex: 10,
     },
     left: {
-      position: { name: 'left' },
+      position: {name: 'left'},
       attrs: {
         circle: {
           r: 4,
@@ -222,7 +239,7 @@ export async function queryBoomFlowDetail(params) {
           stroke: '#31d0c6',
           strokeWidth: 2,
           fill: '#fff',
-          style: { visibility: 'hidden' },
+          style: {visibility: 'hidden'},
         },
       },
       zIndex: 10,
@@ -231,7 +248,7 @@ export async function queryBoomFlowDetail(params) {
   const attrs = {
     line: {
       stroke: '#A2B1C3',
-      targetMarker: { name: 'block', width: 12, height: 8 },
+      targetMarker: {name: 'block', width: 12, height: 8},
       strokeDasharray: '5 5',
       strokeWidth: 1,
     },
@@ -261,7 +278,7 @@ export async function queryBoomFlowDetail(params) {
         cell: item.target_cell,
         port: item.target_port,
       },
-      zIndex:0
+      zIndex: 0
     };
     try {
       edge.attrs = item.attr ? JSON.parse(item.attr) : attrs;
@@ -276,12 +293,14 @@ export async function queryBoomFlowDetail(params) {
     edges,
   };
 }
+
 export async function updateNode(data) {
   return request(`/purchase/bom/flow/${data.templateId}/${data.nodeId}`, {
     method: 'PUT',
     body: data.body,
   });
 }
+
 export async function addBoomFlow(data) {
   return request(`/purchase/bom/flow/info`, {
     method: 'POST',
@@ -299,23 +318,25 @@ export async function addAudit(data) {
     body: data,
   });
 }
+
 export async function addFlow(data) {
   return request(`/purchase/bom/flow/info`, {
     method: 'POST',
     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) {
   return request(`/purchase/flow/info/${data.flowId}`, {
@@ -334,6 +355,7 @@ export async function queryRecordSheet(data) {
     body: data,
   });
 }
+
 export async function queryDingTemplateList() {
   return request(`/purchase/bom/ding/template/list`);
 }
@@ -444,6 +466,7 @@ export async function queryDelPurchaseExcel(params) {
   });
   return res;
 }
+
 //提交流转存储表单审批人历史记录
 export async function querySaveBomForm(data) {
   return request(`/purchase/bom/save-bom-form`, {
@@ -451,6 +474,7 @@ export async function querySaveBomForm(data) {
     body: data,
   });
 }
+
 export async function queryGetBomForm(params) {
   let res = await request(`/purchase/bom/get-bom-form?${stringify(params)}`, {
     method: 'GET',
@@ -469,7 +493,13 @@ export async function queryTrySeal(params) {
 export async function ChartTempOSSData(params) {
   return request(`/purchase/bom/contract-file/${params.projectId}`);
 }
+
 // 设置最终版本
 export async function setLastVersion(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
+}

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.