Jelajahi Sumber

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

xujunjie 1 tahun lalu
induk
melakukan
f69b4f2dd3

+ 1 - 1
config/config.js

@@ -143,7 +143,7 @@ export default {
       // target: 'http://192.168.20.53:8888/',
       // target: 'http://120.55.44.4:8896/',
       // target: 'http://47.96.12.136:8888/',
-      target: 'http://47.96.12.136:8896/',
+      target: 'http://47.96.12.136:8895/',
       // target: 'http://oraysmart.com:8889/',
       // target: 'http://oraysmart.com:8888/api',
       // changeOrigin: true,

+ 38 - 24
src/components/AuditForm/ComponentLibrary.js

@@ -3,16 +3,28 @@ import React, { useState } from 'react';
 import { COMPONENT_LIST } from './constant';
 
 function ComponentLibrary(props) {
-  const { visible, onCancel, onOk } = props;
-  const [currnetItem, setCurrentItem] = useState(null);
+  const { visible, onCancel, onOk, addToTable } = props;
+  const [currentItem, setCurrentItem] = useState(null);
+
+  const DIYTableComponents = [
+    'DDSelectField',
+    'DDDateField',
+    'NumberField',
+    'TextField',
+    'TextNote',
+  ];
+
+  const componenetList = COMPONENT_LIST.filter(item =>
+    addToTable ? DIYTableComponents.includes(item.componentName) : true
+  );
 
   const handleOk = () => {
-    if (!currnetItem) {
+    if (!currentItem) {
       message.error('请选择控件');
       return;
     }
     setCurrentItem(null);
-    onOk?.(currnetItem);
+    onOk?.(currentItem);
   };
   const handleCancel = () => {
     setCurrentItem(null);
@@ -21,26 +33,28 @@ function ComponentLibrary(props) {
   return (
     <Modal visible={visible} onCancel={handleCancel} onOk={handleOk}>
       <Row gutter={12} style={{ paddingTop: 20 }}>
-        {COMPONENT_LIST.map(item => (
-          <Col span={8}>
-            <div
-              onClick={() => setCurrentItem(item)}
-              style={{
-                display: 'flex',
-                justifyContent: 'flex-start',
-                alignItems: 'center',
-                border: item == currnetItem ? '1px solid #1890FF' : '1px solid #aaa',
-                width: '100%',
-                padding: '4px 12px',
-                cursor: 'pointer',
-                margin: '10px 0',
-              }}
-            >
-              {item.icon}
-              <span style={{ marginLeft: 8 }}>{item.props.label}</span>
-            </div>
-          </Col>
-        ))}
+        {componenetList.map(item => {
+          return (
+            <Col span={8}>
+              <div
+                onClick={() => setCurrentItem(item)}
+                style={{
+                  display: 'flex',
+                  justifyContent: 'flex-start',
+                  alignItems: 'center',
+                  border: item === currentItem ? '1px solid #1890FF' : '1px solid #aaa',
+                  width: '100%',
+                  padding: '4px 12px',
+                  cursor: 'pointer',
+                  margin: '10px 0',
+                }}
+              >
+                {item.icon}
+                <span style={{ marginLeft: 8 }}>{item.props.label}</span>
+              </div>
+            </Col>
+          );
+        })}
       </Row>
     </Modal>
   );

+ 125 - 15
src/components/AuditForm/FormContent.js

@@ -1,9 +1,14 @@
-import { Form } from 'antd';
+import { Button, Form } from 'antd';
 import React, { useState } from 'react';
-import { ArrowUpOutlined, ArrowDownOutlined, DeleteOutlined } from '@ant-design/icons';
+import {
+  ArrowUpOutlined,
+  ArrowDownOutlined,
+  DeleteOutlined,
+  PlusOutlined,
+} from '@ant-design/icons';
 
 function FormContent(props) {
-  const { list, onChange, onSelect } = props;
+  const { list, onChange, onSelect, onTableColumnChange } = props;
   const [currentItem, setCurrentItem] = useState(null);
   const handleDelete = index => {
     let _list = [...list];
@@ -25,9 +30,9 @@ function FormContent(props) {
     _list[index] = temp;
     onChange(_list);
   };
-  const handleSelect = index => {
-    setCurrentItem(index);
-    onSelect(index);
+  const handleFormItemClick = id => {
+    setCurrentItem(id[0]);
+    onSelect(id);
   };
   return (
     <div style={{ width: 300 }}>
@@ -54,9 +59,10 @@ function FormContent(props) {
         );
         return (
           <FormItem
-            key={item.id}
-            active={index == currentItem}
-            onClick={() => handleSelect(index)}
+            key={item.props?.id}
+            active={item.props?.id === currentItem}
+            onClick={handleFormItemClick}
+            onTableColumnChange={onTableColumnChange}
             item={item}
             btns={btns}
           />
@@ -67,24 +73,128 @@ function FormContent(props) {
 }
 
 function FormItem(props) {
-  const { item, btns, active, onClick } = props;
+  const { item, btns, active, onClick, onTableColumnChange } = props;
   const { label, placeholder, required } = item.props;
+
+  // 子控件激活id
+  const [selectColumnID, setSelectColumnID] = useState('');
+
+  // 新增列时通过id定位
+  const addTableColumn = event => {
+    // 记录当前表格uuid
+    onTableColumnChange(item.props.id);
+  };
+
+  // 修改表格内部的控件顺序
+  const changeIndex = (index, operate) => {
+    const newCol = [...item.columns];
+    const prev = newCol[index - 1];
+    const next = newCol[index + 1];
+    switch (operate) {
+      case 'up':
+        newCol[index - 1] = newCol[index];
+        newCol[index] = prev;
+        break;
+      case 'down':
+        newCol[index + 1] = newCol[index];
+        newCol[index] = next;
+        break;
+      case 'delete':
+        newCol.splice(index, 1);
+        break;
+      default:
+        break;
+    }
+    onTableColumnChange(item.props.id, newCol);
+  };
   return (
     <div
       style={{
         marginBottom: 20,
         padding: '4px 12px',
         border: '1px solid #666',
-        borderLeft: active ? '10px solid #1890FF' : '1px solid #666',
+        borderLeft: active && !item.isTable ? '10px solid #1890FF' : '1px solid #666',
+      }}
+      onClick={e => {
+        e.stopPropagation();
+        if (item.isColumn === undefined) {
+          onClick([item.props.id]);
+        } else {
+          onClick();
+        }
       }}
-      onClick={onClick}
     >
-      <div style={{ fontSzie: 24, color: '#000', fontWeight: 'bold', position: 'relative' }}>
+      <div
+        style={{
+          fontSize: 16,
+          color: '#000',
+          fontWeight: 'bold',
+          position: 'relative',
+        }}
+      >
         {required && <i style={{ color: 'red' }}>*</i>}
         {label}
-        <div style={{ position: 'absolute', right: 0, top: 0, padding: '5px 10px' }}>{btns}</div>
+        <div
+          style={{
+            position: 'absolute',
+            right: 0,
+            top: 0,
+            padding: '5px 10px',
+          }}
+        >
+          {btns}
+        </div>
       </div>
-      <div style={{ color: '#999', fontSize: 16 }}>{placeholder}</div>
+      {item.isTable ? (
+        <div style={{ padding: '10px 0 5px 0' }}>
+          {item.columns.map((column, index) => {
+            // column的按钮和外部的控件按钮不一样
+            const colBtns = (
+              <>
+                {index !== 0 && (
+                  <ArrowUpOutlined
+                    style={{ marginLeft: 5, cursor: 'pointer' }}
+                    onClick={() => {
+                      changeIndex(index, 'up');
+                    }}
+                  />
+                )}
+                {index !== item.columns.length - 1 && (
+                  <ArrowDownOutlined
+                    style={{ marginLeft: 5, cursor: 'pointer' }}
+                    onClick={() => {
+                      changeIndex(index, 'down');
+                    }}
+                  />
+                )}
+                <DeleteOutlined
+                  style={{ marginLeft: 5, cursor: 'pointer' }}
+                  onClick={() => {
+                    changeIndex(index, 'delete');
+                  }}
+                />
+              </>
+            );
+            return (
+              <FormItem
+                key={column.props?.id}
+                item={column}
+                active={active && column.props?.id === selectColumnID}
+                onClick={() => {
+                  setSelectColumnID(column.props.id);
+                  onClick([item.props.id, column.props.id]);
+                }}
+                btns={colBtns}
+              />
+            );
+          })}
+          <Button type="dashed" block onClick={addTableColumn} icon={<PlusOutlined />}>
+            点击添加列
+          </Button>
+        </div>
+      ) : (
+        <div style={{ color: '#999', fontSize: 16 }}>{placeholder}</div>
+      )}
     </div>
   );
 }

+ 145 - 0
src/components/AuditForm/FormulaModal.js

@@ -0,0 +1,145 @@
+import React, { useEffect, useMemo, useState } from 'react';
+import { Divider, Input, Modal, message, Button } from 'antd';
+
+const { TextArea } = Input;
+
+export const FormulaType = {
+  Filed: 'filed',
+  Symbol: 'symbol',
+  Number: 'number',
+};
+
+const FormulaModal = props => {
+  const { item, numFiledList = [], visible, onCancel, onChange } = props;
+  const symbolList = ['+', '-', '*', '/', '(', ')'];
+  const numberList = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '.'];
+
+  const [formula, setFormula] = useState([]);
+
+  useEffect(() => {
+    if (!visible) setFormula([]);
+    if (item?.props) setFormula(item.props.formula || []);
+  }, [visible, item]);
+
+  const value = useMemo(() => {
+    const strList = formula.map(fItem =>
+      fItem.type === FormulaType.Filed ? `【${fItem.label}】` : fItem.label
+    );
+    return `计算公式=${strList.join('')}`;
+  }, [formula]);
+  console.log('-----------------', formula);
+
+  const isSameBeforeItem = type => {
+    return formula.length > 0 && formula[formula.length - 1].type == type;
+  };
+
+  const handlerChange = (type, item) => {
+    let obj = {};
+    console.log(formula[formula.length - 1]);
+    switch (type) {
+      case FormulaType.Filed:
+        if (isSameBeforeItem(FormulaType.Filed)) {
+          message.error('不能选择连续两个组件');
+        }
+        obj = {
+          type: FormulaType.Filed,
+          id: item.props.id,
+          label: item.props.label,
+        };
+        setFormula([...formula, obj]);
+        break;
+      case FormulaType.Symbol:
+        if (isSameBeforeItem(FormulaType.Symbol)) {
+          message.error('不能选择连续两个符号');
+        }
+        obj = { type: FormulaType.Symbol, label: item };
+        setFormula([...formula, obj]);
+        break;
+      case FormulaType.Number:
+        // 如果前一个选择的也是数字则合并数字 否则直接添加
+        if (isSameBeforeItem(FormulaType.Number)) {
+          const len = formula.length - 1;
+          const label = formula[len].label + item;
+          obj = { type: FormulaType.Number, label };
+          const result = [...formula];
+          result[len] = obj;
+          setFormula(result);
+        } else {
+          obj = { type: FormulaType.Number, label: item };
+          setFormula([...formula, obj]);
+        }
+        break;
+      default:
+        break;
+    }
+  };
+
+  return (
+    <Modal
+      title="编辑计算公式"
+      open={visible}
+      onCancel={onCancel}
+      onOk={() => {
+        onChange?.({ ...item.props, formula, formulaLabel: value });
+      }}
+      width={1000}
+      pagination={false}
+    >
+      <Divider />
+
+      <Button type="primary" style={{ marginBottom: '20px' }} onClick={() => setFormula([])}>
+        清空
+      </Button>
+      <TextArea value={value} />
+      <div style={{ margin: '20px 20px 20px 0' }}>
+        计算对象:
+        {numFiledList.map((nItem, idx) => (
+          <span
+            style={{
+              padding: '6px 10px',
+              border: '1px solid #e5e5e5',
+              borderRadius: '2px',
+              marginRight: '10px',
+            }}
+            onClick={() => handlerChange(FormulaType.Filed, nItem)}
+          >
+            {nItem.props.label}
+          </span>
+        ))}
+      </div>
+      <div style={{ margin: '0 20px 20px 0' }}>
+        计算符号:
+        {symbolList.map((sItem, idx) => (
+          <span
+            style={{
+              padding: '6px 10px',
+              border: '1px solid #e5e5e5',
+              borderRadius: '2px',
+              marginRight: '10px',
+            }}
+            onClick={() => handlerChange(FormulaType.Symbol, sItem)}
+          >
+            {sItem}
+          </span>
+        ))}
+      </div>
+      <div style={{ margin: '0 20px 20px 0' }}>
+        数字键盘:
+        {numberList.map((nItem, idx) => (
+          <span
+            style={{
+              padding: '6px 10px',
+              border: '1px solid #e5e5e5',
+              borderRadius: '2px',
+              marginRight: '10px',
+            }}
+            onClick={() => handlerChange(FormulaType.Number, nItem)}
+          >
+            {nItem}
+          </span>
+        ))}
+      </div>
+    </Modal>
+  );
+};
+export default FormulaModal;

+ 174 - 5
src/components/AuditForm/ItemAttribute.js

@@ -2,8 +2,8 @@ import { Form, Button, Switch, Input, Radio, Space, Row } from 'antd';
 import React, { useMemo, useState, useEffect } from 'react';
 import { DeleteOutlined } from '@ant-design/icons';
 function ItemAttribute(props) {
-  const { item, onChange } = props;
-
+  const { item, onChange, onRelClick, onFormulaClick } = props;
+  if (!item) return null;
   const renderForm = () => {
     let FormContent;
     const formProps = {
@@ -42,16 +42,34 @@ function ItemAttribute(props) {
       case 'NumberField':
         FormContent = <NumberField {...formProps} />;
         break;
+      case 'TextNote':
+        FormContent = <TextNote {...formProps} />;
+        break;
       case 'DDDateField':
         FormContent = <DDDateField {...formProps} />;
         break;
+      case 'DIYTable':
+        FormContent = <DIYTableField {...formProps} />;
+        break;
+      case 'FormulaField':
+        FormContent = <FormulaField {...formProps} onFormulaClick={onFormulaClick} />;
+        break;
+      case 'ProjectField':
+        FormContent = <ProjectField {...formProps} onFormulaClick={onFormulaClick} />;
+        break;
+      case 'ManufacturerField':
+        FormContent = <ManufacturerField {...formProps} />;
+        break;
+      case 'CodeField':
+        FormContent = <CodeField {...formProps} />;
+        break;
+      default:
+        FormContent = null;
+        break;
     }
-
     return FormContent;
   };
 
-  if (!item) return null;
-
   return (
     <div
       style={{
@@ -412,3 +430,154 @@ function NumberField(props) {
     </Form>
   );
 }
+
+function TextNote(props) {
+  const { item, btns, onFinish } = props;
+  const [form] = Form.useForm();
+
+  return (
+    <Form
+      form={form}
+      labelCol={{ span: 6 }}
+      wrapperCol={{ span: 18 }}
+      onFinish={onFinish}
+      autoComplete="off"
+      initialValues={item.props}
+    >
+      <Form.Item label="说明文字" name="placeholder">
+        <Input />
+      </Form.Item>
+      {btns}
+    </Form>
+  );
+}
+
+function ProjectField(props) {
+  const { item, btns, onFinish } = props;
+  const [form] = Form.useForm();
+  return (
+    <Form
+      form={form}
+      labelCol={{ span: 6 }}
+      wrapperCol={{ span: 20 }}
+      autoComplete="off"
+      initialValues={item.props}
+      onFinish={onFinish}
+    >
+      <Form.Item label="标题" name="label">
+        <Input />
+      </Form.Item>
+      <Form.Item label="提示文字" name="placeholder">
+        <Input />
+      </Form.Item>
+      <Form.Item label="必填" name="required" valuePropName="checked">
+        <Switch />
+      </Form.Item>
+      {btns}
+    </Form>
+  );
+}
+
+function DIYTableField(props) {
+  const { item, btns, onFinish } = props;
+  const [form] = Form.useForm();
+  return (
+    <Form
+      form={form}
+      labelCol={{ span: 6 }}
+      wrapperCol={{ span: 18 }}
+      autoComplete="off"
+      initialValues={item.props}
+      onFinish={onFinish}
+    >
+      <Form.Item label="表格名称" name="label">
+        <Input />
+      </Form.Item>
+      {btns}
+    </Form>
+  );
+}
+
+function FormulaField(props) {
+  const { item, btns, onFinish, onFormulaClick } = props;
+  const [form] = Form.useForm();
+  const value = useMemo(() => {
+    return item.props.formulaLabel;
+  }, [item.props]);
+  return (
+    <Form
+      form={form}
+      labelCol={{ span: 6 }}
+      wrapperCol={{ span: 18 }}
+      autoComplete="off"
+      initialValues={item.props}
+      onFinish={values => onFinish({ ...item.props, ...values })}
+    >
+      <Form.Item label="标题" name="label">
+        <Input />
+      </Form.Item>
+      <Form.Item label="提示文字" name="placeholder">
+        <Input />
+      </Form.Item>
+      <Form.Item label="计算公式">
+        <Input value={value} onClick={onFormulaClick} />
+      </Form.Item>
+      <Form.Item label="必填" name="required" valuePropName="checked">
+        <Switch />
+      </Form.Item>
+      {btns}
+    </Form>
+  );
+}
+
+function ManufacturerField(props) {
+  const { item, btns, onFinish } = props;
+  const [form] = Form.useForm();
+  return (
+    <Form
+      form={form}
+      labelCol={{ span: 6 }}
+      wrapperCol={{ span: 18 }}
+      autoComplete="off"
+      initialValues={item.props}
+      onFinish={onFinish}
+    >
+      <Form.Item label="标题" name="label">
+        <Input />
+      </Form.Item>
+      <Form.Item label="提示文字" name="placeholder">
+        <Input />
+      </Form.Item>
+      <Form.Item label="必填" name="required" valuePropName="checked">
+        <Switch />
+      </Form.Item>
+      {btns}
+    </Form>
+  );
+}
+
+function CodeField(props) {
+  const { item, btns, onFinish } = props;
+  const [form] = Form.useForm();
+  return (
+    <Form
+      form={form}
+      labelCol={{ span: 6 }}
+      wrapperCol={{ span: 18 }}
+      autoComplete="off"
+      initialValues={item.props}
+      onFinish={onFinish}
+    >
+      <Form.Item label="标题" name="label">
+        <Input />
+      </Form.Item>
+      <Form.Item label="提示文字" name="placeholder">
+        <Input />
+      </Form.Item>
+      <Form.Item label="必填" name="required" valuePropName="checked">
+        <Switch />
+      </Form.Item>
+      {btns}
+    </Form>
+  );
+}

+ 63 - 0
src/components/AuditForm/constant.js

@@ -1,3 +1,4 @@
+import React from 'react';
 import {
   UserOutlined,
   TeamOutlined,
@@ -7,6 +8,12 @@ import {
   BlockOutlined,
   FieldNumberOutlined,
   InsertRowAboveOutlined,
+  TableOutlined,
+  LaptopOutlined,
+  ProjectOutlined,
+  SolutionOutlined,
+  NumberOutlined,
+  FontColorsOutlined,
 } from '@ant-design/icons';
 
 export const COMPONENT_LIST = [
@@ -30,6 +37,36 @@ export const COMPONENT_LIST = [
       choice: '0',
     },
   },
+  {
+    componentName: 'ProjectField',
+    icon: <ProjectOutlined />,
+    props: {
+      label: '选择项目',
+      placeholder: '请选择项目',
+      required: false,
+      choice: '0',
+    },
+  },
+  {
+    componentName: 'ManufacturerField',
+    icon: <SolutionOutlined />,
+    props: {
+      label: '选择供应商',
+      placeholder: '请选择供应商',
+      required: false,
+      // choice: '0',
+    },
+  },
+  {
+    componentName: 'TextNote',
+    icon: <FontColorsOutlined />,
+    props: {
+      label: '文本说明控件',
+      bizAlias: '',
+      placeholder: '请输入文本',
+      required: false,
+    },
+  },
   {
     componentName: 'TextField',
     icon: <ItalicOutlined />,
@@ -88,4 +125,30 @@ export const COMPONENT_LIST = [
       unit: '',
     },
   },
+  {
+    componentName: 'CodeField',
+    icon: <NumberOutlined />,
+    props: {
+      label: '合同编号',
+      placeholder: '自动生成',
+      required: false,
+    },
+  },
+  {
+    componentName: 'DIYTable',
+    icon: <TableOutlined />,
+    props: {
+      label: 'DIY表格',
+      required: false,
+    },
+  },
+  {
+    componentName: 'FormulaField',
+    icon: <LaptopOutlined />,
+    props: {
+      label: '计算公式',
+      placeholder: '自动计算数值',
+      required: false,
+    },
+  },
 ];

+ 140 - 20
src/components/AuditForm/index.js

@@ -1,42 +1,135 @@
 import React, { useState, useEffect } from 'react';
+import { uuidv4 } from '@antv/xflow';
+import { Button } from 'antd';
 import FormContent from './FormContent';
 import ComponentLibrary from './ComponentLibrary';
 import ItemAttribute from './ItemAttribute';
-import { uuidv4 } from '@antv/xflow';
-import { Button } from 'antd';
+import FormulaModal from './FormulaModal';
 
 function AuditForm(props) {
   const { value, onChange } = props;
   const [formList, setFormList] = useState([]);
   const [select, setSelect] = useState(-1);
+  const [selectList, setSelectList] = useState([]);
   const [visible, setVisible] = useState(false);
-
-  const handleAddItem = item => {
-    const formItem = generateItem(item);
-    handleChangeList([...formList, formItem]);
-    setVisible(false);
-  };
+  const [forVisible, setForVisible] = useState(false); // 计算公式弹窗
+  const [addToTable, setAddToTable] = useState(false);
+  const [currentTableID, setCurrentTableID] = useState('');
 
   const generateItem = item => {
-    let newItem = {
+    const newItem = {
       ...item,
       props: { ...item.props, id: `${item.componentName}_${uuidv4()}` },
     };
+    // 如果是表格的话
+    if (item.componentName === 'DIYTable') {
+      newItem.columns = [];
+      newItem.isTable = true;
+    }
     delete newItem.icon;
     return newItem;
   };
 
-  const onChangeAttribute = newItem => {
-    let oldValue = formList[select].props;
-    formList[select].props = { ...oldValue, ...newItem };
-    handleChangeList([...formList]);
-  };
-
   const handleChangeList = list => {
     setFormList(list);
     onChange?.(list);
   };
 
+  const handleAddItem = item => {
+    if (addToTable) {
+      const column = generateItem(item);
+      // 找到对应的表格
+      const tableItem = formList.find(tItem => tItem.props.id === currentTableID);
+      column.isColumn = true;
+      // 把新增的列加入到表格中
+      tableItem.columns.push(column);
+      // 把新增列的表格放回
+      const newFormList = [];
+      for (const item of formList) {
+        if (item.props.id !== currentTableID) {
+          newFormList.push(item);
+        } else {
+          newFormList.push(tableItem);
+        }
+      }
+      handleChangeList(newFormList);
+      setCurrentTableID('');
+    } else {
+      const formItem = generateItem(item);
+      handleChangeList([...formList, formItem]);
+    }
+    setAddToTable(false);
+    setVisible(false);
+  };
+
+  const findFormItem = () => {
+    const formItem = formList.find(item => item.props.id === selectList[0]);
+
+    if (formItem?.isTable) {
+      // 如果是表格的话,还要寻找内部的被点击的col
+      if (selectList.length === 1) {
+        return formItem || null;
+      }
+      return (
+        formItem.columns.find(item => item.props.id === selectList[selectList.length - 1]) || null
+      );
+    } else {
+      return formList.find(item => item.props.id === select) || null;
+    }
+  };
+
+  const onChangeAttribute = newItemProps => {
+    const oldFormItem = findFormItem();
+    const newFormList = [];
+    if (oldFormItem.isColumn) {
+      // 找到表格和col然后改掉属性
+      for (const item of formList) {
+        if (item.isTable) {
+          for (const column of item.columns) {
+            if (column.props.id === selectList[selectList.length - 1]) {
+              column.props = { ...column.props, ...newItemProps };
+            }
+          }
+        }
+        newFormList.push(item);
+      }
+    } else {
+      for (const item of formList) {
+        if (item.props.id === select) {
+          item.props = { ...item.props, ...newItemProps };
+        }
+        newFormList.push(item);
+      }
+    }
+    handleChangeList(newFormList);
+  };
+
+  // 表格列变化时(新增,调整顺序)
+  const handleTableColumnChange = (id, newCole = []) => {
+    if (newCole.length) {
+      // 调整col顺序
+      const tableItem = formList.find(item => item.props.id === id);
+      tableItem.columns = newCole;
+      const newFormList = [];
+      for (const item of formList) {
+        if (item.props.id !== currentTableID) {
+          newFormList.push(item);
+        } else {
+          newFormList.push(tableItem);
+        }
+      }
+      handleChangeList(newFormList);
+    } else {
+      setCurrentTableID(id);
+      setAddToTable(true);
+    }
+  };
+
+  const handleFormContentSelect = ids => {
+    setSelectList(ids);
+    setSelect(ids[0]);
+  };
+
   useEffect(() => {
     if (value instanceof Array) {
       setFormList([...value]);
@@ -56,14 +149,41 @@ function AuditForm(props) {
           marginTop: 20,
         }}
       >
-        <FormContent onSelect={setSelect} onChange={handleChangeList} list={formList}></FormContent>
-        <ItemAttribute item={formList[select]} onChange={onChangeAttribute}></ItemAttribute>
+        <FormContent
+          onSelect={handleFormContentSelect}
+          onChange={handleChangeList}
+          onTableColumnChange={handleTableColumnChange}
+          list={formList}
+        />
+        <ItemAttribute
+          formList={formList}
+          key={selectList[selectList.length - 1]}
+          item={findFormItem()}
+          onFormulaClick={() => {
+            setForVisible(true);
+          }}
+          onChange={onChangeAttribute}
+        />
       </div>
       <ComponentLibrary
+        addToTable={addToTable}
         onOk={handleAddItem}
-        visible={visible}
-        onCancel={() => setVisible(false)}
-      ></ComponentLibrary>
+        visible={visible || addToTable}
+        onCancel={() => {
+          setVisible(false);
+          setAddToTable(false);
+        }}
+      />
+      <FormulaModal
+        item={formList.find(item => item.props.id === select)}
+        numFiledList={formList.filter(item => item.componentName === 'NumberField')}
+        visible={forVisible}
+        onCancel={() => setForVisible(false)}
+        onChange={formula => {
+          setForVisible(false);
+          onChangeAttribute(formula);
+        }}
+      />
     </div>
   );
 }

+ 49 - 0
src/components/DDComponents/CodeFiled/index.js

@@ -0,0 +1,49 @@
+import React, { useEffect, useState } from 'react';
+import { connect } from 'dva';
+import { Input } from 'antd';
+import { queryContractCode } from '@/services/contract';
+
+function CodeField(props) {
+  const { depId, onChange, depUserTree } = props;
+  const [value, setValue] = useState('');
+
+  const getContractCode = async params => {
+    const res = await queryContractCode(params).catch(err => console.log(err));
+    if (res.code === 200 && res.data && res.data.code) {
+      setValue(res.data.code);
+      onChange(res.data.code);
+    }
+  };
+
+  const getDepItemById = id => {
+    const fun = list => {
+      for (let i = 0; i < list.length; i++) {
+        const item = list[i];
+        if (Number(item.ID) === Number(id)) {
+          return item;
+        }
+        if (item.children?.length > 0) {
+          const res = fun(item.children);
+          if (res) return res;
+        }
+      }
+    };
+    return fun(depUserTree);
+  };
+
+  useEffect(() => {
+    if (!depId || !depUserTree.length) return;
+    const dep_code = getDepItemById(depId)?.Code;
+    const company = depUserTree.find(item => item.Flag === 1);
+    const params = {
+      company_id: company?.ID,
+      company_code: company?.Code,
+      dep_code,
+    };
+    getContractCode(params);
+  }, [depId, depUserTree]);
+
+  return <Input value={value} placeholder="选择部门后自动生成" disabled />;
+}
+
+export default connect(({ user }) => ({ depUserTree: user.depUserTree }))(CodeField);

+ 1 - 0
src/components/DDComponents/DDAttachment/index.js

@@ -1,6 +1,7 @@
 import React, { useState, useEffect } from 'react';
 import { Upload, Button, message } from 'antd';
 import { PlusOutlined } from '@ant-design/icons';
+import AliyunOSSUpload from '@/components/OssUpload/AliyunOssUploader';
 
 const CORP_ID = 'ding0cdce2d5dbf986d9';
 const AGENT_ID = '1788653353';

+ 1 - 1
src/components/DDComponents/DDDateField/index.js

@@ -5,7 +5,7 @@ function DDDateField(props) {
   const { format = '', disabled, onChange } = props;
 
   const handleChange = date => {
-    onChange?.(date.format('YYYY-MM-DD'));
+    onChange?.(date.format('YYYY-MM-DD HH:mm:ss'), props.id, props.label);
   };
   return (
     <DatePicker

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

@@ -12,7 +12,7 @@ function DDSelectField(props) {
       disabled={disabled}
       defaultValue={defaultValue}
       onChange={value => {
-        onChange(String(value));
+        onChange(String(value), props.id, props.label);
       }}
     >
       {options?.map(cur => {

+ 7 - 0
src/components/DDComponents/DIYTable/index.css

@@ -0,0 +1,7 @@
+.hidden {
+  display: none;
+}
+
+.p-8 {
+  padding: 10px !important;
+}

+ 223 - 0
src/components/DDComponents/DIYTable/index.tsx

@@ -0,0 +1,223 @@
+import { PlusOutlined } from '@ant-design/icons';
+import { Button, Input, Table } from 'antd';
+import { ColumnsType } from 'antd/lib/table';
+import React, { useState, useEffect } from 'react';
+import DDDateField from '@/components/DDComponents/DDDateField';
+import DDSelectField from '@/components/DDComponents/DDSelectField';
+import NumberField from '@/components/DDComponents/NumberField';
+import TextNote from '@/components/DDComponents/TextNote';
+import './index.css';
+
+interface IProps {
+  table?: any; // 整个表格
+  columns?: any[]; // 当前列配置
+  onChange?: (e?: any, id?: string, label?: string) => void; // 表格修改后的值
+  displayOnly?: boolean; // 是否仅用于展示
+}
+
+interface TableDataType {
+  index: number;
+  id?: string;
+  col1?: any;
+  col2?: any;
+  col3?: any;
+  col4?: any;
+  col5?: any;
+}
+
+function DIYTable(props: IProps) {
+  const { table, columns, displayOnly, onChange } = props;
+
+  // table数据
+  const [tableData, setTableData] = useState<TableDataType[]>([{ index: 1 }]);
+
+  // table列配置
+  const tableColumnDef = [
+    {
+      title: '序号',
+      dataIndex: 'index',
+      className: 'hidden',
+    },
+  ];
+
+  // 表单填写时的表格生成
+  const handleGenerateTable = () => {
+    if (columns !== undefined && columns.length) {
+      for (let index = 0; index < columns.length; index++) {
+        const column = columns[index];
+        const columnID = column.props.id;
+        const columnLabel = column.props.label;
+        const colDef: any = {
+          dataIndex: `col${index + 1}`,
+          title: columnLabel || column.name,
+          className: 'p-8',
+        };
+        switch (column.componentName) {
+          case 'DDSelectField':
+            colDef.render = (_: any, __: any, rowIndex: number) => {
+              const id = `${rowIndex},${index};${columnID}>${table.props.id}`;
+              return (
+                // eslint-disable-next-line react/jsx-filename-extension
+                <DDSelectField
+                  id={id}
+                  label={`${columnLabel}>${table.props.label}`}
+                  style={{ padding: '0', margin: '0' }}
+                  options={column.props.options}
+                  disabled={column.props.disabled}
+                  onChange={onChange}
+                />
+              );
+            };
+            break;
+          case 'DDDateField':
+            colDef.render = (_: any, __: any, rowIndex: number) => {
+              const id = `${rowIndex},${index};${columnID}>${table.props.id}`;
+              return (
+                <DDDateField
+                  id={id}
+                  label={`${columnLabel}>${table.props.label}`}
+                  key={`${index + rowIndex}`}
+                  style={{ padding: '0', margin: '0' }}
+                  placeholder={column.props.placeholder}
+                  format={column.props.format}
+                  disabled={column.props.disabled}
+                  onChange={onChange}
+                />
+              );
+            };
+            break;
+          case 'NumberField':
+            colDef.render = (_: any, __: any, rowIndex: number) => {
+              const id = `${rowIndex},${index};${columnID}>${table.props.id}`;
+              return (
+                <NumberField
+                  id={id}
+                  label={`${columnLabel}>${table.props.label}`}
+                  size="small"
+                  width="50%"
+                  style={{ padding: '4px 11px' }}
+                  disabled={column.props.disabled}
+                  unit={column.props.unit}
+                  onChange={onChange}
+                />
+              );
+            };
+            break;
+          case 'TextField':
+            colDef.render = (_: any, __: any, rowIndex: number) => {
+              const id = `${rowIndex},${index};${columnID}>${table.props.id}`;
+              return (
+                <Input
+                  disabled={column.props.disabled}
+                  placeholder={column.props.placeholder}
+                  onChange={e =>
+                    onChange?.(e.target.value, id, `${columnLabel}>${table.props.label}`)
+                  }
+                />
+              );
+            };
+            break;
+          case 'TextNote':
+            colDef.title = '说明';
+            colDef.render = (_: any, __: any, rowIndex: number) => {
+              const id = `${rowIndex},${index};${columnID}>${table.props.id}`;
+              return (
+                <TextNote
+                  id={id}
+                  style={{ padding: '0', margin: '0' }}
+                  value={column.props.placeholder}
+                />
+              );
+            };
+            break;
+          default:
+            break;
+        }
+        tableColumnDef.push(colDef);
+      }
+    }
+  };
+
+  // 当仅用作展示时
+  const displayColumnDef = () => {
+    const rows = columns;
+    if (rows && rows.length) {
+      for (let index = 0; index < rows.length; index++) {
+        // 把每一行的数据提出来到一个对象里
+        if (rows) {
+          const row = rows[index];
+          if (index === 0) {
+            // 列配置
+            row.forEach((col: any) => {
+              tableColumnDef.push({ title: col.name, dataIndex: col.type, className: '' });
+            });
+          }
+        }
+      }
+    }
+  };
+
+  const handleRowChange = () => {
+    setTableData([...tableData, { index: tableData.length + 1 }]);
+  };
+
+  if (!displayOnly) {
+    handleGenerateTable();
+  } else {
+    displayColumnDef();
+  }
+
+  useEffect(() => {
+    if (columns.length === 0) {
+      return;
+    }
+    const rows = columns;
+    const newTableData = [];
+    if (rows && rows.length) {
+      if (displayOnly) {
+        for (let index = 0; index < rows.length; index++) {
+          // 把每一行的数据提出来到一个对象里
+          const row = rows[index];
+          const rowData: any = {};
+          if (displayOnly) {
+            row.forEach((col: any) => {
+              rowData.index = index + 1;
+              // eslint-disable-next-line prefer-destructuring
+              rowData[col.type] = col.value[0];
+              rowData.key = col.id + index;
+            });
+            newTableData.push(rowData);
+          }
+        }
+      } else {
+        newTableData.push({
+          index: 1,
+        });
+      }
+      setTableData(newTableData);
+    }
+  }, [columns]);
+
+  return (
+    <>
+      {table.name && !displayOnly ? table.name : null}
+      <Table
+        style={displayOnly ? { margin: '10px 24px 10px 0' } : {}}
+        columns={tableColumnDef}
+        dataSource={tableData}
+        pagination={false}
+      />
+      <Button
+        type="dashed"
+        icon={<PlusOutlined />}
+        block
+        onClick={handleRowChange}
+        style={displayOnly ? { display: 'none' } : {}}
+      >
+        新增行
+      </Button>
+    </>
+  );
+}
+
+export default DIYTable;

+ 21 - 20
src/components/DDComponents/DepartmentField/index.js

@@ -1,31 +1,32 @@
 import { TreeSelect } from 'antd';
 import React, { useState, useEffect } from 'react';
-import { queryDDdepList } from '@/services/boom';
 import { connect } from 'dva';
+// import { queryDDdepList } from '@/services/boom';
+// import { queryDDdepList } from '@/services/boom';
 
 function DepartmentField(props) {
   const { value = [], onChange, depUserTree } = props;
-  const [treeData, setTreeData] = useState([]);
+  // const [treeData, setTreeData] = useState([]);
 
-  const genTreeNode = dep => {
-    return {
-      id: dep.dept_id,
-      pId: dep.parent_id,
-      value: dep.dept_id,
-      title: dep.name,
-      isLeaf: false,
-    };
-  };
+  // const genTreeNode = dep => {
+  //   return {
+  //     id: dep.dept_id,
+  //     pId: dep.parent_id,
+  //     value: dep.dept_id,
+  //     title: dep.name,
+  //     isLeaf: false,
+  //   };
+  // };
 
-  const onLoadData = async ({ id }) => {
-    let depList = await queryDDdepList({ dept_id: id });
+  // const onLoadData = async ({ id }) => {
+  //   let depList = await queryDDdepList({ dept_id: id });
 
-    console.log(depList);
-    if (depList.length > 0) {
-      let nodes = depList.map(genTreeNode);
-      setTreeData([...treeData, ...nodes]);
-    }
-  };
+  //   console.log(depList);
+  //   if (depList.length > 0) {
+  //     let nodes = depList.map(genTreeNode);
+  //     setTreeData([...treeData, ...nodes]);
+  //   }
+  // };
 
   const onChangeValue = newValue => {
     onChange(newValue);
@@ -34,7 +35,7 @@ function DepartmentField(props) {
   return (
     <TreeSelect
       showSearch
-      multiple
+      // // multiple
       allowClear
       defaultValue={value}
       dropdownStyle={{

+ 14 - 0
src/components/DDComponents/FormulaField/index.js

@@ -0,0 +1,14 @@
+import React, { useEffect, useState } from 'react';
+import { Input } from 'antd';
+
+function FormulaField(props) {
+  const { evalStr, onChange } = props;
+  useEffect(() => {
+    const value = eval(evalStr);
+    onChange?.(value);
+  }, [evalStr]);
+
+  return <Input value={eval(evalStr)} />;
+}
+
+export default FormulaField;

+ 54 - 0
src/components/DDComponents/ManufacturerField/index.js

@@ -0,0 +1,54 @@
+import React, { useState, useEffect } from 'react';
+import { Select } from 'antd';
+import { querySupplierList } from '@/services/contract';
+
+function ManufacturerField(props) {
+  const { value, disabled = false, onChange } = props;
+  const user = JSON.parse(localStorage.getItem('currentUser'));
+
+  const [option, setOption] = useState([]);
+  const [loading, setLoading] = useState(false);
+
+  const getSupplier = async () => {
+    setLoading(true);
+    const res = await querySupplierList({
+      project_id: 1,
+      is_super: user.IsSuper,
+      created_by: user.CName,
+      page_size: 9999,
+    }).catch(err => {
+      console.log(err);
+      setLoading(false);
+    });
+
+    if (res?.code === 200 && res?.data?.list?.length) {
+      const supplier = res?.data?.list.map(item => {
+        return { label: item.name, value: item.id };
+      });
+      setOption(supplier);
+    }
+    setLoading(false);
+  };
+
+  // 只要第一次加载即可
+  useEffect(() => {
+    getSupplier();
+  }, []);
+
+  return (
+    <Select
+      showSearch
+      loading={loading}
+      style={{ width: '100%' }}
+      disabled={disabled}
+      defaultValue={value ? Number(value) : undefined}
+      onChange={val => {
+        onChange(String(val));
+      }}
+      filterOption={(input, opt) => (opt?.label ?? '').toLowerCase().includes(input.toLowerCase())}
+      options={option}
+    />
+  );
+}
+
+export default ManufacturerField;

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

@@ -11,7 +11,7 @@ function NumberField(props) {
       disabled={disabled}
       formatter={value => `${value}${unit || ''}`}
       onChange={e => {
-        onChange?.(String(e));
+        onChange?.(e ? String(e) : 0, props.id, props.label);
       }}
     />
   );

+ 50 - 0
src/components/DDComponents/ProjectField/index.js

@@ -0,0 +1,50 @@
+import React, { useEffect, useState } from 'react';
+import { Select } from 'antd';
+import { queryApproval } from '@/services/approval';
+
+function DDProjectField(props) {
+  const { value, disabled = false, onChange } = props;
+  const [projectList, setProjectList] = useState([]);
+  const [loading, setLoading] = useState(false);
+
+  const getProjectList = async () => {
+    setLoading(true);
+    const res = await queryApproval({ pageSize: 9999 }).catch(err => {
+      console.log(err);
+      setLoading(false);
+    });
+    if (res.code === 200) {
+      console.log(res.data.list);
+      setProjectList(res.data.list);
+      setLoading(false);
+    }
+  };
+
+  useEffect(() => {
+    getProjectList();
+  }, []);
+  return (
+    <Select
+      showSearch
+      loading={loading}
+      style={{ width: '100%' }}
+      disabled={disabled}
+      defaultValue={value ? Number(value) : undefined}
+      onChange={val => {
+        console.log(val);
+        onChange(String(val));
+      }}
+      filterOption={(input, option) =>
+        (option?.label ?? '').toLowerCase().includes(input.toLowerCase())
+      }
+      options={projectList.map(item => {
+        return {
+          label: `${item.project_name}(${item.project_full_code})`,
+          value: item.id,
+        };
+      })}
+    />
+  );
+}
+
+export default DDProjectField;

+ 9 - 0
src/components/DDComponents/TextNote/index.js

@@ -0,0 +1,9 @@
+import React from 'react';
+
+function TextNote(props) {
+  const { value } = props;
+
+  return <div>{value}</div>;
+}
+
+export default TextNote;

+ 81 - 42
src/components/DDComponents/index.js

@@ -1,21 +1,28 @@
-import {Input, InputNumber, Select, DatePicker, Rate} from 'antd';
+import React from 'react';
+import { Input, InputNumber, Select, DatePicker, Rate } from 'antd';
 import TableField from './TableField';
 import PhoneField from './PhoneField';
 import InnerContactField from './InnerContactField';
 import DepartmentField from './DepartmentField';
+import CodeField from './CodeFiled';
 import DDMultiSelectField from './DDMultiSelectField';
 import NumberField from './NumberField';
 import DDPhotoField from './DDPhotoField';
 import DDSelectField from './DDSelectField';
 import DDDateField from './DDDateField';
 import DDDateRangeField from './DDDateRangeField';
-import DDAttachment from './DDAttachment';
+// import DDAttachment from './DDAttachment';
+import FormulaField from './FormulaField';
+import DIYTable from './DIYTable/index.tsx';
+import TextNode from './TextNote';
+import DDProjectField from './ProjectField';
+import ManufacturerField from './ManufacturerField';
 
-const {Option} = Select;
-const {RangePicker} = DatePicker;
+const { Option } = Select;
+const { RangePicker } = DatePicker;
 
 export default function DDComponents(props) {
-  const {item, onChange} = props;
+  const { depId = '', evalStr = '', item, onChange } = props;
   const {
     id,
     label,
@@ -47,7 +54,7 @@ export default function DDComponents(props) {
   } = item.props;
   let component = null;
   switch (item.componentName) {
-    case 'TextField': //单行输入
+    case 'TextField': // 单行输入
       component = (
         <Input
           defaultValue={defaultValue}
@@ -57,7 +64,7 @@ export default function DDComponents(props) {
         />
       );
       break;
-    case 'TextareaField': //多行输入
+    case 'TextareaField': // 多行输入
       component = (
         <Input.TextArea
           defaultValue={defaultValue}
@@ -67,60 +74,92 @@ export default function DDComponents(props) {
         />
       );
       break;
-    case 'NumberField': //数字输入
-      component = <NumberField defaultValue={defaultValue} disabled={disabled} unit={unit} onChange={onChange}/>;
+    case 'NumberField': // 数字输入
+      component = (
+        <NumberField
+          defaultValue={defaultValue}
+          disabled={disabled}
+          unit={unit}
+          onChange={onChange}
+        />
+      );
       break;
-    case 'DDSelectField': //单选框
-      component =
-        <DDSelectField defaultValue={defaultValue} options={options} onChange={onChange} disabled={disabled}/>;
+    case 'DDSelectField': // 单选框
+      component = (
+        <DDSelectField
+          defaultValue={defaultValue}
+          options={options}
+          onChange={onChange}
+          disabled={disabled}
+        />
+      );
       break;
-    case 'DDMultiSelectField': //多选框
-      component = <DDMultiSelectField disabled={disabled} options={options} onChange={onChange}/>;
+    case 'DDMultiSelectField': // 多选框
+      component = <DDMultiSelectField disabled={disabled} options={options} onChange={onChange} />;
       break;
-    case 'DDDateField': //日期控件
-      component = <DDDateField format={format} disabled={disabled} onChange={onChange}/>;
+    case 'DDDateField': // 日期控件
+      component = <DDDateField format={format} disabled={disabled} onChange={onChange} />;
       break;
-    case 'DDDateRangeField': //时间区间控件
-      component = <DDDateRangeField format={format} disabled={disabled} onChange={onChange}/>;
+    case 'DDDateRangeField': // 时间区间控件
+      component = <DDDateRangeField format={format} disabled={disabled} onChange={onChange} />;
       break;
-    case 'TextNote': //文本说明控件
-      console.info('文本说明控件!');
-      console.log(item);
+    case 'TextNote': // 文本说明控件
+      component = <TextNode value={placeholder} />;
       break;
-    case 'PhoneField': //电话控件
-      component = <PhoneField onChange={onChange}/>;
+    case 'PhoneField': // 电话控件
+      component = <PhoneField onChange={onChange} />;
       break;
-    case 'DDPhotoField': //图片控件
-      component = <DDPhotoField/>;
+    case 'DDPhotoField': // 图片控件
+      component = <DDPhotoField />;
       break;
-    case 'MoneyField': //金额控件
-      component = <Input defaultValue={defaultValue} placeholder={placeholder} onChange={onChange}/>;
+    case 'MoneyField': // 金额控件
+      component = (
+        <Input defaultValue={defaultValue} placeholder={placeholder} onChange={onChange} />
+      );
       break;
-    case 'TableField': //明细控件
-      component = <TableField item={item}/>;
+    case 'TableField': // 明细控件
+      component = <TableField item={item} />;
       break;
-    case 'DDAttachment': //附件
+    case 'DDAttachment': // 附件
       // component = <DDAttachment />
       // component = '附件控件未渲染!'
       console.info('附件控件未渲染!');
       break;
-    case 'InnerContactField': //联系人控件
-      component = <InnerContactField onChange={onChange}></InnerContactField>;
+    case 'InnerContactField': // 联系人控件
+      component = <InnerContactField onChange={onChange} />;
+      break;
+    case 'DepartmentField': // 部门控件
+      component = <DepartmentField onChange={onChange} />;
+      break;
+    case 'CodeField': // 合同编号控件
+      component = <CodeField depId={depId} onChange={onChange} />;
+      break;
+    case 'ProjectField': // 项目控件
+      component = <DDProjectField value={defaultValue} disabled={disabled} onChange={onChange} />;
       break;
-    case 'DepartmentField': //部门控件
-      component = <DepartmentField onChange={onChange}/>;
+    case 'DIYTable': // 可自定义表格控件
+      component = <DIYTable table={item} columns={item.columns} onChange={onChange} />;
       break;
-    case 'RelateField': //关联审批单
-      component = '关联审批单控件未渲染!'
+    case 'FormulaField':
+      component = <FormulaField evalStr={evalStr} onChange={onChange} />;
       break;
-    case 'AddressField': //省市区控件
-      component = '省市区控件未渲染!'
+    case 'ManufacturerField':
+      component = <ManufacturerField onChange={onChange} />;
       break;
-    case 'StarRatingField': //评分控件
-      component = '评分控件未渲染!'
+    case 'RelateField': // 关联审批单
+      component = '关联审批单控件未渲染!';
       break;
-    case 'FormRelateField': //关联控件
-      component = '关联控件未渲染!'
+    case 'AddressField': // 省市区控件
+      component = '省市区控件未渲染!';
+      break;
+    case 'StarRatingField': // 评分控件
+      component = '评分控件未渲染!';
+      break;
+    case 'FormRelateField': // 关联控件
+      component = '关联控件未渲染!';
+      break;
+
+    default:
       break;
   }
 

+ 73 - 90
src/pages/Detail/AuditDetailed.js

@@ -1,46 +1,32 @@
 import DDComponents from '@/components/DDComponents';
-import React, {useMemo, useState} from 'react';
-import {Form} from '@ant-design/compatible';
-import '@ant-design/compatible/assets/index.css';
-
-const layout = {
-  labelCol: {
-    span: 8,
-  },
-  wrapperCol: {
-    span: 16,
-  },
-};
+import React, { useMemo, useState } from 'react';
+import { Button, Form } from 'antd';
+import { FormulaType } from '@/components/AuditForm/FormulaModal';
 
 const AuditDetailed = props => {
-  // const [form] = Form.useForm();
-  const {items, form} = props;
+  const { allValues = [], items, form, onValuesChange, onTableValChange } = props;
+  const depId = useMemo(() => {
+    const id = items.find(item => item.componentName === 'DepartmentField')?.props.id;
+    const value = allValues.find(item => item.id === id)?.value;
+    if (value) return value[0];
+  }, [allValues, items]);
 
-  const behavior = useMemo(() => {
-    let data = {};
+  const data = useMemo(() => {
+    let linkedData = {};
     items.forEach(d => {
       const item = d.props;
-      if (item.behaviorLinkage) {
-        const key = item.id;
-        const options = item.options.map(o => {
-          let data;
-          try {
-            data = JSON.parse(o);
-          } catch (error) {
-            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};
-          });
-        });
+      if (item.linked) {
+        linkedData = { ...linkedData, [item.id]: item.linked };
       }
     });
-
-    return data;
+    const linkedList =
+      items
+        ?.map(item => {
+          const linked = item.props.linked;
+          return linked ? Object.values(linked).flat() : [];
+        })
+        .flat() || [];
+    return { linkedData, linkedList };
   }, [items]);
 
   const onFinish = values => {
@@ -48,51 +34,25 @@ const AuditDetailed = props => {
   };
 
   const GetComponent = item => {
-    const {
-      id,
-      label,
-      bizAlias,
-      required,
-      placeholder,
-      options,
-      align,
-      statField,
-      hideLabel,
-      objOptions,
-      format,
-      pushToAttendance,
-      labelEditableFreeze,
-      requiredEditableFreeze,
-      unit,
-      extract,
-      link,
-      payEnable,
-      bizType,
-      childFieldVisible,
-      notPrint,
-      verticalPrint,
-      hiddenInApprovalDetail,
-      disabled,
-      notUpper,
-      children, // 子控件
-    } = item.props;
-    // 判断是否属于关联项
-    if (behavior[id]) {
-      const {key, value} = behavior[id];
-      let currentValue = form.getFieldValue(key);
-      try {
-        currentValue = JSON.parse(currentValue);
-      } catch (error) {
-      }
-      // 判断是否需要渲染
-      if (currentValue instanceof Array) {
-        if (currentValue?.indexOf(value) == -1) return null;
-      } else {
-        if (currentValue != value) return null;
-      }
+    const { id, label, bizAlias, required, notUpper } = item.props;
+    // 判断是否关联项
+    if (data.linkedList.findIndex(curId => curId == id) !== -1) {
+      let control = null; // 当前空间是否显示的条件 当id为control.id的组件选择的选项值为control.value 时显示
+      Object.keys(data.linkedData).forEach(ctlIs => {
+        const linked = data.linkedData[ctlIs];
+        Object.keys(linked).forEach(value => {
+          const ids = linked[value];
+          if (ids.findIndex(curId => curId == id) !== -1) {
+            control = { id: ctlIs, value };
+          }
+        });
+      });
+      let currentValue = form.getFieldValue(control?.id);
+      if (currentValue != control?.value) return null;
     }
+
     let formLabel;
-    if (bizAlias) {
+    if (bizAlias !== undefined) {
       formLabel = bizAlias;
     } else {
       try {
@@ -103,26 +63,49 @@ const AuditDetailed = props => {
       }
     }
 
-    const component = DDComponents({item});
-    if (!component) return null;
+    const renderComponents = () => {
+      let content = '';
+      if (item.componentName === 'CodeField') {
+        content = <DDComponents item={item} depId={depId} />;
+      } else if (item.componentName === 'FormulaField') {
+        const strList = item.props?.formula?.map(formu => {
+          if (formu.type === FormulaType.Filed) {
+            const numItem = allValues?.find(item => item.id == formu.id);
+            return numItem?.value[0] || 0;
+          }
+          return formu.label;
+        });
+        const evalStr = strList?.join('');
+        content = <DDComponents item={item} evalStr={evalStr} />;
+      } else {
+        content = <DDComponents item={item} />;
+      }
+      return content;
+    };
+
+    // const component = DDComponents({ item });
+    // if (!component) return null;
     return (
-      <Form.Item label={formLabel}>
-        {typeof component == 'string'
-          ? component
-          : form.getFieldDecorator(id, {
-            rules: [{required}],
-            initialValue: item.props.defaultValue
-          })(component)}
-        {notUpper == 1 && <p>大写</p>}
-      </Form.Item>
+      <>
+        {item?.isTable === undefined ? (
+          <Form.Item key={id} name={id} label={formLabel} rules={[{ required }]}>
+            {renderComponents()}
+          </Form.Item>
+        ) : (
+          <DDComponents item={item} onChange={onTableValChange} />
+        )}
+      </>
     );
   };
 
   return (
     <Form
-      style={{height: '400px', overflowY: 'scroll', paddingRight: 20}}
+      form={form}
+      style={{ minHeight: '25vh', overflowY: 'auto', paddingRight: 20 }}
       layout="vertical"
       autoComplete="off"
+      onValuesChange={onValuesChange}
+      onFinish={onFinish}
     >
       {items.map(item => GetComponent(item))}
     </Form>

File diff ditekan karena terlalu besar
+ 520 - 332
src/pages/Detail/CommitAuditModal.js


+ 22 - 6
src/pages/Detail/FormAndFilesNode.js

@@ -1,10 +1,11 @@
 import { Card, Col, Input, Row, Modal, Empty, Collapse } from 'antd';
 import { Form } from '@ant-design/compatible';
 import { useForm } from 'antd/lib/form/Form';
-import { useMemo, useState } from 'react';
-import CommentContent from '@/components/CommentContent';
+import React, { useMemo, useState } from 'react';
 import { connect } from 'dva';
+import CommentContent from '@/components/CommentContent';
 import AttachmentTable from '@/components/AttachmentTable';
+import DIYTable from '@/components/DDComponents/DIYTable';
 
 const { confirm } = Modal;
 const { Panel } = Collapse;
@@ -28,15 +29,16 @@ const FormAndFilesNode = props => {
         </Row>
       </Card>
     );
-  } else if (excelFileList?.length > 0) {
+  }
+
+  if (excelFileList?.length > 0) {
     return (
       <Card title="附件信息">
         <AttachmentTable version={version} canDelete={version.last_version == 0} />
       </Card>
     );
-  } else {
-    return null;
   }
+  return null;
 };
 
 const renderFrom = data => {
@@ -48,10 +50,23 @@ const renderFrom = data => {
     return (
       <>
         {formData.map((item, idx) => {
+          if (item.type === 'DIYTable') {
+            return (
+              <Form.Item
+                key={`FormAndFilesNode_${item.id}`}
+                labelCol={{ span: 4 }}
+                wrapperCol={{ span: 14 }}
+                label={item.name}
+              >
+                <DIYTable key={item.id} table={item} columns={item.value} displayOnly />
+              </Form.Item>
+            );
+          }
+
           const value = item.value.join(',');
           return (
             <Form.Item
-              key={`FormAndFilesNode_${idx}`}
+              key={`FormAndFilesNode_${item.id}`}
               labelCol={{ span: 4 }}
               wrapperCol={{ span: 14 }}
               label={item.name}
@@ -63,6 +78,7 @@ const renderFrom = data => {
                   backgroundColor: '#ececef',
                   border: '1px solid #bcb9b9',
                   borderRadius: '4px',
+                  minHeight: '40px',
                 }}
               >
                 {value}

+ 3 - 2
src/pages/Detail/Index.js

@@ -135,6 +135,7 @@ function Detail(props) {
     }
     return data;
   }, [auditList, version]);
+
   const active_audit = flow.active_audit;
   const isAuditor = useMemo(() => {
     return active_audit == 1 && flow.currentNode?.auditor == currentUser.ID;
@@ -395,7 +396,7 @@ function Detail(props) {
 
   const changeVersion = async id => {
     let version;
-    if (typeof id == 'object') {
+    if (typeof id === 'object') {
       version = id;
       localStorage.excelId = version.id;
       localStorage.excelItem = JSON.stringify(version);
@@ -634,7 +635,7 @@ function Detail(props) {
           )}
 
           <FormAndFilesNode
-            formData={version?.formStr}
+            formData={version?.formStr || version?.ding_schema}
             excelFileList={excelFileList}
             version={version}
           />

+ 24 - 0
src/services/contract.js

@@ -0,0 +1,24 @@
+import request from '@/utils/request';
+import { stringify } from 'qs';
+
+// 计算合同编号
+export const queryContractCode = async data => {
+  return await request(`/api/contract/v1/code?${stringify(data)}`);
+};
+
+// http://localhost:8000/api/supplier/v1/supplier/list
+// http://localhost:8001/api/supplier/v1/supplier/list
+// created_by
+// end_time
+// is_super
+// page
+// page_size
+// project_id
+// start_time
+
+export const querySupplierList = async data => {
+  return await request('/api/supplier/v1/supplier/list', {
+    method: 'POST',
+    body: data,
+  });
+};

+ 5 - 1
src/utils/request.js

@@ -81,7 +81,11 @@ export default function request(url, option, jwt) {
   // console.log(API_HOST);
   const time = url.indexOf('?') > -1 ? `&time=${number}` : `?time=${number}`;
   if (url.indexOf('http://') == -1 && url.indexOf('https://') == -1) {
-    if (url.indexOf('/api/v') === -1) {
+    if (
+      url.indexOf('/api/v') === -1 &&
+      url.indexOf('/api/supplier') === -1 &&
+      url.indexOf('/api/contract') === -1
+    ) {
       url = `/api/v1${url}${time}`;
     }
     // API_HOST在config/config.js的define中配置

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini