Browse Source

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

Renxy 1 year ago
parent
commit
6a74a7a6c3

+ 32 - 25
src/components/AuditForm/ComponentLibrary.js

@@ -3,16 +3,16 @@ 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 handleOk = () => {
-    if (!currnetItem) {
+    if (!currentItem) {
       message.error('请选择控件');
       return;
     }
     setCurrentItem(null);
-    onOk?.(currnetItem);
+    onOk?.(currentItem);
   };
   const handleCancel = () => {
     setCurrentItem(null);
@@ -21,27 +21,34 @@ function ComponentLibrary(props) {
   return (
     <Modal open={visible} onCancel={handleCancel} onOk={handleOk}>
       <Row gutter={12} style={{ paddingTop: 20 }}>
-        {COMPONENT_LIST.map((item) => (
-          <Col span={8} key={item.componentName}>
-            <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>
-        ))}
+        {COMPONENT_LIST.map((item) => {
+          if (addToTable && item.props.label === '表格') {
+            return null;
+          }
+          return (
+            <Col span={8} key={item.componentName}>
+              <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>
   );

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

@@ -1,13 +1,15 @@
-import { Form } from 'antd';
+import { Button, Form } from 'antd';
 import React, { useState, useMemo } from 'react';
 import {
   ArrowUpOutlined,
   ArrowDownOutlined,
   DeleteOutlined,
+  PlusOutlined,
 } from '@ant-design/icons';
+import position from '../Flow/node/fields/position';
 
 function FormContent(props) {
-  const { list, onChange, onSelect } = props;
+  const { list, onChange, onSelect, onTableColumnChange } = props;
   const [currentItem, setCurrentItem] = useState(null);
 
   const linkedList = useMemo(() => {
@@ -41,22 +43,24 @@ 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: 500, height: 636, overflow: 'auto' }}>
       {list.map((item, index) => {
         const btns = (
           <>
-            {index != 0 && (
+            {index !== 0 && (
               <ArrowUpOutlined
                 style={{ marginLeft: 5, cursor: 'pointer' }}
                 onClick={() => handleUp(index)}
               />
             )}
-            {index != list.length - 1 && (
+            {index !== list.length - 1 && (
               <ArrowDownOutlined
                 style={{ marginLeft: 5, cursor: 'pointer' }}
                 onClick={() => handleDown(index)}
@@ -68,14 +72,16 @@ function FormContent(props) {
             />
           </>
         );
+
         return (
           <FormItem
             key={item.props?.id}
             islinked={
-              linkedList?.findIndex((id) => id == item.props?.id) !== -1
+              linkedList?.findIndex((id) => id === item.props?.id) !== -1
             } //不等于-1表示是被单选框关联的组件需要置灰
-            active={index == currentItem}
-            onClick={() => handleSelect(index)}
+            active={item.props?.id === currentItem}
+            onClick={handleFormItemClick}
+            onTableColumnChange={onTableColumnChange}
             item={item}
             btns={btns}
           />
@@ -86,8 +92,46 @@ function FormContent(props) {
 }
 
 function FormItem(props) {
-  const { item, btns, active, onClick, islinked = false } = props;
+  const {
+    item,
+    btns,
+    active,
+    onClick,
+    onTableColumnChange,
+    islinked = false,
+  } = 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) => {
+    let 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;
+    }
+    onTableColumnChange(item.props.id, newCol);
+  };
+
   return (
     <div
       style={{
@@ -95,13 +139,24 @@ function FormItem(props) {
         marginBottom: 20,
         padding: '4px 12px',
         border: islinked ? '1px solid #bebcbc' : '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 {
+          // 触发列中的click事件
+          onClick();
+        }
       }}
-      onClick={onClick}
     >
       <div
         style={{
-          fontSzie: 24,
+          fontSize: 16,
           color: '#000',
           fontWeight: 'bold',
           position: 'relative',
@@ -120,7 +175,61 @@ function FormItem(props) {
           {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>
   );
 }

+ 32 - 2
src/components/AuditForm/ItemAttribute.js

@@ -1,9 +1,11 @@
 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, onRelClick } = props;
 
+  if (!item) return null;
   const renderForm = () => {
     let FormContent;
     const formProps = {
@@ -57,6 +59,8 @@ function ItemAttribute(props) {
       case 'DDDateField':
         FormContent = <DDDateField {...formProps} />;
         break;
+      case 'DIYTable':
+        FormContent = <TableName {...formProps} />;
       case 'CodeField':
         FormContent = <CodeField {...formProps} />;
         break;
@@ -65,8 +69,6 @@ function ItemAttribute(props) {
     return FormContent;
   };
 
-  if (!item) return null;
-
   return (
     <div
       style={{
@@ -121,6 +123,7 @@ function InnerContactField(props) {
     </Form>
   );
 }
+
 function DepartmentField(props) {
   const { item, btns, onFinish } = props;
   const [form] = Form.useForm();
@@ -154,6 +157,7 @@ function DepartmentField(props) {
     </Form>
   );
 }
+
 function ProjectField(props) {
   const { item, btns, onFinish } = props;
   const [form] = Form.useForm();
@@ -204,6 +208,7 @@ function CodeField(props) {
     </Form>
   );
 }
+
 function ManufacturerField(props) {
   const { item, btns, onFinish } = props;
   const [form] = Form.useForm();
@@ -229,6 +234,7 @@ function ManufacturerField(props) {
     </Form>
   );
 }
+
 function TextField(props) {
   const { item, btns, onFinish } = props;
   const [form] = Form.useForm();
@@ -258,6 +264,7 @@ function TextField(props) {
     </Form>
   );
 }
+
 function DDAttachment(props) {
   const { item, btns, onFinish } = props;
   const [form] = Form.useForm();
@@ -329,6 +336,7 @@ function DDDateField(props) {
     </Form>
   );
 }
+
 function TextareaField(props) {
   const { item, btns, onFinish } = props;
   const [form] = Form.useForm();
@@ -354,6 +362,7 @@ function TextareaField(props) {
     </Form>
   );
 }
+
 function DDSelectField(props) {
   const { item, btns, onFinish, onRelClick } = props;
   const [form] = Form.useForm();
@@ -396,6 +405,7 @@ function DDSelectField(props) {
     </Form>
   );
 }
+
 function DDMultiSelectField(props) {
   const { item, btns, onFinish } = props;
   const [form] = Form.useForm();
@@ -559,3 +569,23 @@ function NumberField(props) {
     </Form>
   );
 }
+
+function TableName(props) {
+  const { item, btns, onFinish } = props;
+  const [form] = Form.useForm();
+  return (
+    <Form
+      form={form}
+      labelCol={{ span: 4 }}
+      wrapperCol={{ span: 20 }}
+      autoComplete="off"
+      initialValues={item.props}
+      onFinish={onFinish}
+    >
+      <Form.Item label="表格名称" name="label">
+        <Input />
+      </Form.Item>
+      {btns}
+    </Form>
+  );
+}

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

@@ -12,6 +12,7 @@ import {
   ProjectOutlined,
   SolutionOutlined,
   NumberOutlined,
+  TableOutlined,
 } from '@ant-design/icons';
 
 export const COMPONENT_LIST = [
@@ -141,4 +142,12 @@ export const COMPONENT_LIST = [
       required: false,
     },
   },
+  {
+    componentName: 'DIYTable',
+    icon: <TableOutlined />,
+    props: {
+      label: '表格',
+      required: false,
+    },
+  },
 ];

+ 116 - 13
src/components/AuditForm/index.js

@@ -9,13 +9,41 @@ import RelModal from './RelModal';
 function AuditForm(props) {
   const { value, onChange } = props;
   const [formList, setFormList] = useState([]);
-  const [select, setSelect] = useState(-1);
+  const [select, setSelect] = useState('');
+  const [selectList, setSelectList] = useState([]);
   const [visible, setVisible] = useState(false);
+
   const [relVisible, setRelVisible] = useState(false); //关联选项弹窗
+  const [addToTable, setAddToTable] = useState(false);
+  const [currentTableID, setCurrentTableID] = useState('');
 
   const handleAddItem = (item) => {
-    const formItem = generateItem(item);
-    handleChangeList([...formList, formItem]);
+    if (addToTable) {
+      // 新增列处理
+      const column = generateItem(item);
+      // 找到对应的表格
+      const tableItem = formList.find(
+        (item) => item.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);
   };
 
@@ -24,14 +52,40 @@ function AuditForm(props) {
       ...item,
       props: { ...item.props, id: `${item.componentName}_${uuidv4()}` },
     };
+    // 如果是表格的话
+    if (item.props.label === '表格') {
+      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 onChangeAttribute = (newItemProps) => {
+    let oldFormItem = findFormItem();
+    let 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 handleChangeList = (list) => {
@@ -39,6 +93,50 @@ function AuditForm(props) {
     onChange?.(list);
   };
 
+  // 表格列变化时(新增,调整顺序)
+  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]);
+  };
+
+  const findFormItem = () => {
+    let 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;
+    }
+  };
+
   useEffect(() => {
     if (value instanceof Array) {
       setFormList([...value]);
@@ -59,24 +157,29 @@ function AuditForm(props) {
         }}
       >
         <FormContent
-          onSelect={setSelect}
+          onSelect={handleFormContentSelect}
           onChange={handleChangeList}
+          onTableColumnChange={handleTableColumnChange}
           list={formList}
         ></FormContent>
         <ItemAttribute
-          key={select}
-          item={formList[select]}
+          key={selectList[selectList.length - 1]}
+          item={findFormItem()}
           onRelClick={() => setRelVisible(true)}
           onChange={onChangeAttribute}
         ></ItemAttribute>
       </div>
       <ComponentLibrary
+        addToTable={addToTable}
         onOk={handleAddItem}
-        visible={visible}
-        onCancel={() => setVisible(false)}
+        visible={visible || addToTable}
+        onCancel={() => {
+          setVisible(false);
+          setAddToTable(false);
+        }}
       />
       <RelModal
-        item={formList[select]}
+        item={formList.find((item) => item.props.id === select)}
         formList={formList}
         visible={relVisible}
         onCancel={() => setRelVisible(false)}

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

@@ -2,10 +2,10 @@ import React from 'react';
 import { DatePicker } from 'antd';
 
 function DDDateField(props) {
-  const { format = "", disabled, onChange, placeholder } = props;
+  const { format = '', disabled, onChange, placeholder } = props;
 
   const handleChange = (date) => {
-    onChange?.(date.format('YYYY-MM-DD HH:mm:ss'));
+    onChange?.(date.format('YYYY-MM-DD HH:mm:ss'), props.id, props.label);
   };
   return (
     <DatePicker

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

@@ -10,11 +10,11 @@ function DDSelectField(props) {
     <Select
       style={{ width: '100%' }}
       disabled={disabled}
-      onChange={value => {
-        onChange(String(value));
+      onChange={(value) => {
+        onChange(String(value), props.id, props.label);
       }}
     >
-      {options?.map(cur => {
+      {options?.map((cur) => {
         return (
           <Option key={cur} value={cur}>
             {cur}

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

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

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

@@ -0,0 +1,210 @@
+import React, { useEffect, useState } from 'react';
+// @ts-ignore
+import { Button, Input, Table } from 'antd';
+import type { ColumnsType } from 'antd/lib/table';
+import DDSelectField from '@/components/DDComponents/DDSelectField';
+import './index.css';
+import DDDateField from '@/components/DDComponents/DDDateField';
+import NumberField from '@/components/DDComponents/NumberField';
+import TextNote from '@/components/DDComponents/TextNote';
+import { PlusOutlined } from '@ant-design/icons';
+
+interface IProps {
+  table?: any; // 整个表格
+  columns?: Array<any>; // 当前列配置
+  onChange?: (e?: any, id?: string, label?: string) => void; // 表格修改后的值
+  displayOnly?: boolean; // 是否仅用于展示
+}
+
+type 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, setTableColumnDef] = useState<ColumnsType<any>>([
+    {
+      title: '序号',
+      dataIndex: 'index',
+      className: 'hidden',
+    },
+  ]);
+
+  // 表单填写时的表格生成
+  const handleGenerateTable = () => {
+    if (columns !== undefined && columns.length) {
+      for (let index = 0; index < columns.length; index++) {
+        let column = columns[index];
+        let columnID = column.props.id;
+        let columnLabel = column.props.label;
+        let colDef: any = {
+          dataIndex: 'col' + (index + 1),
+          title: columnLabel || column.name,
+          className: 'p-8',
+        };
+        switch (column.componentName) {
+          case 'DDSelectField':
+            colDef.render = (_: any, __: any, rowIndex: number) => {
+              let id =
+                rowIndex + ',' + index + ';' + columnID + '>' + table.props.id;
+              return (
+                <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) => {
+              let 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) => {
+              let 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) => {
+              let 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) => {
+              let id =
+                rowIndex + ',' + index + ';' + columnID + '>' + table.props.id;
+              return (
+                <TextNote
+                  id={id}
+                  style={{ padding: '0', margin: '0' }}
+                  value={column.props.placeholder}
+                />
+              );
+            };
+            break;
+        }
+        tableColumnDef.push(colDef);
+      }
+    }
+  };
+
+  // 当仅用作展示时
+  const handleDisplayOnly = () => {
+    const rows = columns;
+    const newTableData = [];
+    const newColumnDef: ColumnsType<any> = [];
+    if (rows && columns.length) {
+      for (let index = 0; index < rows.length; index++) {
+        // 把每一行的数据提出来到一个对象里
+        if (rows) {
+          const row = rows[index];
+
+          if (index === 0) {
+            // 列配置
+            row.forEach((col: any) => {
+              newColumnDef.push({ title: col.name, dataIndex: col.type });
+            });
+          }
+          let rowData: any = {};
+          row.forEach((col: any) => {
+            rowData[col.type] = col.value[0];
+            rowData.key = col.id + index;
+          });
+          newTableData.push(rowData);
+        }
+      }
+      setTableColumnDef(newColumnDef);
+      setTableData(newTableData);
+    }
+  };
+
+  const handleRowChange = () => {
+    setTableData([...tableData, { index: tableData.length }]);
+  };
+
+  useEffect(() => {
+    if (displayOnly) {
+      handleDisplayOnly();
+    } else {
+      handleGenerateTable();
+    }
+  }, []);
+
+  return (
+    <>
+      {table.name ? table.name : table.props.label}
+      <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;

+ 4 - 3
src/components/DDComponents/NumberField/index.js

@@ -2,15 +2,16 @@ import React from 'react';
 import { InputNumber } from 'antd';
 
 function NumberField(props) {
-  const { onChange, disabled, unit } = props;
+  const { onChange, disabled, unit, size, width } = props;
 
   return (
     <InputNumber
-      style={{ width: '100%' }}
+      size={size}
+      style={{ width: '100%', padding: size === 'small' ? '4px' : '0' }}
       disabled={disabled}
       formatter={(value) => `${value}${unit || ''}`}
       onChange={(e) => {
-        onChange?.(e ? String(e) : 0);
+        onChange?.(e ? String(e) : 0, props.id, props.label);
       }}
     />
   );

+ 6 - 0
src/components/DDComponents/index.js

@@ -14,6 +14,7 @@ import DDDateRangeField from './DDDateRangeField';
 import DDAttachment from './DDAttachment';
 import TextNote from './TextNote';
 import CodeField from './CodeFiled';
+import DIYTable from './DIYTable';
 
 export default function DDComponents(props) {
   const { depId = '', item, onChange } = props;
@@ -112,6 +113,11 @@ export default function DDComponents(props) {
     case 'ManufacturerField':
       component = <ManufacturerField onChange={onChange} />;
       break;
+    case 'DIYTable':
+      component = (
+        <DIYTable table={item} columns={item.columns} onChange={onChange} />
+      );
+      break;
     case 'RelateField': //关联审批单
       component = '关联审批单控件未渲染!';
       break;

+ 6 - 7
src/pages/Flow/Audit.js

@@ -17,10 +17,8 @@ function Audit(props) {
   } = props;
   const [tabActiveKey, setTabActiveKey] = useState('1');
   const ref = useRef();
-  const {
-    initialState,
-  } = useModel('@@initialState');
-  const user = initialState?.user || {}
+  const { initialState } = useModel('@@initialState');
+  const user = initialState?.user || {};
   const permission = user?.Permission || {};
 
   const curItem = useMemo(() => {
@@ -121,10 +119,10 @@ function Audit(props) {
         },
       ]}
     >
-      {tabActiveKey == 1 && (
-        <AuditForm value={formData} onChange={(values) => onChange(values)} />
+      {tabActiveKey === '1' && (
+        <AuditForm value={formData} onChange={onChange} />
       )}
-      {tabActiveKey == 2 && (
+      {tabActiveKey === '2' && (
         <Flow
           meta={{ type: 'edit', editMode, flowId: curItem.id }}
           flowDetail={flowDetail}
@@ -134,6 +132,7 @@ function Audit(props) {
     </PageContent>
   );
 }
+
 export default connect(({ flow, loading, user, xflow }) => ({
   roleList: flow.roleList,
   loading: loading.effects,

+ 2 - 4
src/pages/Flow/OaAuditDetail.js

@@ -13,10 +13,8 @@ import { Type } from '../Profile';
 import { queryContractDetail } from '../../services/contract';
 
 function OaAuditDetail() {
-  const {
-    initialState,
-  } = useModel('@@initialState');
-  const user = initialState?.user || {}
+  const { initialState } = useModel('@@initialState');
+  const user = initialState?.user || {};
   const [auditVisible, setAuditVisible] = useState(false);
   const location = useLocation();
   const {

+ 172 - 10
src/pages/Flow/OaDetail.js

@@ -12,11 +12,15 @@ import {
   queryLeader,
 } from '@/services/boom';
 import { useParams, useRequest, useNavigate } from 'umi';
+import table from '../Table';
 
 const OaDetail = () => {
   const [form] = Form.useForm();
+
   const [approvalProcess, setApprovalProcess] = useState([]);
   const [auditCheck, setAuditCheck] = useState([]);
+  const [tableData, setTableData] = useState([]);
+
   const { oaId } = useParams();
   const formValueRef = useRef({
     form: '',
@@ -87,31 +91,188 @@ const OaDetail = () => {
     run(params);
   };
 
-  const submit = () => {
+  const handleTableValChange = (value, id, label) => {
+    let ids = id.split(';');
+    let [rowIndex, colIndex] = ids[0].split(',').map((item) => Number(item));
+    let [columnID, tableID] = ids[1].split('>');
+    let [columnLabel, tableLabel] = label.split('>');
+
+    const cell = {
+      name: columnLabel,
+      id: columnID,
+      type: columnID.split('_')[0],
+      value: [value],
+    };
+    // 组装可能用到的数据
+    const cols = [];
+    cols[colIndex] = cell;
+    const rows = [];
+    rows[rowIndex] = cols;
+    // 如果已经有数据
+    let oldTableData = [];
+    // 这里不知道为什么不能直接读取state(tableData)
+    setTableData((prevState) => {
+      oldTableData = prevState;
+      return prevState;
+    });
+    console.clear();
+    console.log(oldTableData);
+    if (oldTableData && oldTableData.length > 0) {
+      let table = oldTableData.find((item) => item.id === tableID);
+      // 如果某个表格数据存在
+      if (table) {
+        const oldRows = table.value;
+        // 如果某个表格的行数据存在
+        if (oldRows) {
+          let odlCols = oldRows[rowIndex];
+          // 如果某个表格的某个行数据存在
+          if (odlCols) {
+            // 记录可编辑控件
+            table.value[rowIndex][colIndex] = cell;
+            // 不可编辑控件
+            table.value[rowIndex] = addUnEditableColumn(
+              table.value[rowIndex],
+              tableID,
+            );
+            const newTableData = oldTableData.map((item) => {
+              if (item.id === table.id) {
+                return table;
+              }
+              return item;
+            });
+
+            setTableData(newTableData);
+          } else {
+            // 如果某个表格的某个行数据不存在
+            // 写入可编辑控件
+            table.value[rowIndex] = cols;
+            // 写入不可编辑控件的值
+            table.value[rowIndex] = addUnEditableColumn(
+              table.value[rowIndex],
+              tableID,
+            );
+            const newTableData = oldTableData.map((item) => {
+              if (item.id === table.id) {
+                return table;
+              }
+              return item;
+            });
+            setTableData([]);
+            setTableData(newTableData);
+          }
+        } else {
+          // 如果某个表格的行数据不存在
+          // 写入可编辑控件
+          table.value = rows;
+          // 写入不可编辑控件
+          table.value[rowIndex] = addUnEditableColumn(
+            table.value[rowIndex],
+            tableID,
+          );
+          const newTableData = oldTableData.map((item) => {
+            if (item.id === table.id) {
+              return table;
+            }
+            return item;
+          });
+          setTableData(newTableData);
+        }
+      } else {
+        // 如果某个table的数据不存在
+        rows[rowIndex] = addUnEditableColumn(rows[rowIndex], tableID);
+        const newTableData = [
+          {
+            name: tableLabel,
+            id: tableID,
+            type: tableID.split('_')[0],
+            value: rows,
+          },
+        ];
+        setTableData([...oldTableData, ...newTableData]);
+      }
+    } else {
+      // 如果没有数据
+      // 添加不可编辑控件
+      rows[rowIndex] = addUnEditableColumn(rows[rowIndex], tableID);
+      const newTableData = [
+        {
+          name: tableLabel,
+          id: tableID,
+          type: tableID.split('_')[0],
+          value: rows,
+        },
+      ];
+      setTableData(newTableData);
+    }
+  };
+
+  const addUnEditableColumn = (rows, tableID) => {
+    const { columns: originColumns } = data.formData.find(
+      (item) => item.props.id === tableID,
+    );
+    // 检查是否有文字说明控件,有的话需要添加在第几列的index后写入tableData
+    const allTextNote = originColumns.filter((item, index) => {
+      if (item.componentName === 'TextNote') {
+        item.index = index;
+        return true;
+      }
+      return false;
+    });
+    // 对这个表格的每个第 (allTextNote.item 的 index )列写入
+    let textNoteCellList = [];
+    if (allTextNote && allTextNote.length) {
+      textNoteCellList = allTextNote.map((item) => {
+        return {
+          name: item.props.label,
+          id: item.props.id,
+          type: item.componentName,
+          value: [item.props.placeholder],
+          colIndex: item.index,
+        };
+      });
+    }
+    if (textNoteCellList.length) {
+      for (const note of textNoteCellList) {
+        rows[note.colIndex] = note;
+      }
+    }
+    return rows;
+  };
+
+  const submit = async () => {
     form.validateFields().then((values) => {
       const { form: formCur } = formValueRef.current;
-      let audit_list = [],
-        cc_list = [];
+      let audit_list = [];
+      let cc_list = [];
       approvalProcess?.forEach((item, index) => {
-        let arr = item[0].is_cc == 1 ? cc_list : audit_list;
+        let arr = item[0].is_cc === 1 ? cc_list : audit_list;
 
-        if (item[0].type == 'role') arr.push(auditCheck[index]);
-        else if (item[0].type == 'leader')
+        if (item[0].type === 'role') arr.push(auditCheck[index]);
+        else if (item[0].type === 'leader')
           arr.push(
             ...leaderData.slice(0, item[0].value).map((leader) => leader.ID),
           );
         else arr.push(item.map((cur) => cur.value));
       });
-      let files = [],
-        formData = [];
+      let files = [];
+      let formData = [];
       formCur.forEach((item) => {
-        if (item.type == 'DDAttachment') {
+        if (item.type === 'DDAttachment') {
           files = files.concat(item.value);
         } else {
           formData.push(item);
         }
       });
-      console.log(audit_list, cc_list);
+
+      // 根据表格在原来的form中的顺序插入
+      const tableIndex = tableData.map((table) => {
+        return data.formData.findIndex((item) => item.props.id === table.id);
+      });
+
+      // 按保存的顺序把表格再插入进去
+      tableIndex.forEach((item, index) => {
+        formData.splice(item, 0, tableData[index]);
+      });
       createRun({
         flow_id: Number(oaId),
         form: JSON.stringify(formData),
@@ -151,6 +312,7 @@ const OaDetail = () => {
             form={form}
             items={data?.formData}
             onValuesChange={advanceSubmit}
+            onTableValChange={handleTableValChange}
           />
         </Col>
         <Col span={12}>

+ 23 - 11
src/pages/Flow/components/AuditDetailed.js

@@ -4,7 +4,13 @@ import { Button, Form } from 'antd';
 
 const AuditDetailed = (props) => {
   // const [form] = Form.useForm();
-  const { allValues = [], items, form, onValuesChange } = props;
+  const {
+    allValues = [],
+    items,
+    form,
+    onValuesChange,
+    onTableValChange,
+  } = props;
 
   const depId = useMemo(() => {
     const id = items.find((item) => item.componentName == 'DepartmentField')
@@ -69,18 +75,24 @@ const AuditDetailed = (props) => {
     // const component = DDComponents({ item });
     // if (!component) return null;
     return (
-      <Form.Item
-        key={id}
-        name={id}
-        label={formLabel}
-        rules={[{ required: required }]}
-      >
-        {item.componentName == 'CodeField' ? (
-          <DDComponents item={item} depId={depId} />
+      <>
+        {item?.isTable === undefined ? (
+          <Form.Item
+            key={id}
+            name={id}
+            label={formLabel}
+            rules={[{ required: required }]}
+          >
+            {item.componentName === 'CodeField' ? (
+              <DDComponents item={item} depId={depId} />
+            ) : (
+              <DDComponents item={item} />
+            )}
+          </Form.Item>
         ) : (
-          <DDComponents item={item} />
+          <DDComponents item={item} onChange={onTableValChange} />
         )}
-      </Form.Item>
+      </>
     );
   };
 

+ 25 - 11
src/pages/Flow/components/FormAndFilesNode.js

@@ -1,16 +1,17 @@
 import { Card, Col, Row, Empty } from 'antd';
-import { Form } from 'antd';
+import { Form, Table } from 'antd';
 import { useMemo, useState } from 'react';
 import { useModel } from 'umi';
 import AttachmentTable from '@/components/AttachmentTable';
 import InnerContactField from '@/components/DDComponents/InnerContactField';
 import DepartmentField from '@/components/DDComponents/DepartmentField';
 import ProjectField from '@/components/DDComponents/ProjectField';
+import DIYTable from '../../../components/DDComponents/DIYTable';
 
 const FormAndFilesNode = (props) => {
   const { formData, fileList } = props;
 
-  const renderFormItem = (type, value) => {
+  const renderFormItem = (type, value, item) => {
     switch (type) {
       case 'InnerContactField':
         return <InnerContactField value={value} disabled={true} />;
@@ -40,20 +41,33 @@ const FormAndFilesNode = (props) => {
     if (!data) return <Empty description="没有表单信息" />;
     try {
       const formData = JSON.parse(data);
-      if (formData.length == 0) return <Empty description="没有表单信息" />;
+      if (formData.length === 0) return <Empty description="没有表单信息" />;
       return (
         <>
           {formData.map((item, idx) => {
             const value = item.value.join(',');
             return (
-              <Form.Item
-                key={`FormAndFilesNode_${idx}`}
-                labelCol={{ span: 4 }}
-                wrapperCol={{ span: 14 }}
-                label={item.name}
-              >
-                {renderFormItem(item.type, value)}
-              </Form.Item>
+              <>
+                {item.type !== 'DIYTable' ? (
+                  <Form.Item
+                    key={`FormAndFilesNode_${idx}`}
+                    labelCol={{ span: 4 }}
+                    wrapperCol={{ span: 14 }}
+                    label={item.name}
+                    labelAlign="left"
+                  >
+                    {renderFormItem(item.type, value, item)}
+                  </Form.Item>
+                ) : (
+                  <div style={{ marginBottom: '24px' }}>
+                    <DIYTable
+                      table={item}
+                      columns={item.value}
+                      displayOnly={true}
+                    ></DIYTable>
+                  </div>
+                )}
+              </>
             );
           })}
         </>