Переглянути джерело

feat(审批流-表单设计): 新增表格控件

ZhaoJun 1 рік тому
батько
коміт
5b709368be

+ 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>
   );
 }

+ 10 - 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 = {
@@ -62,8 +64,6 @@ function ItemAttribute(props) {
     return FormContent;
   };
 
-  if (!item) return null;
-
   return (
     <div
       style={{
@@ -118,6 +118,7 @@ function InnerContactField(props) {
     </Form>
   );
 }
+
 function DepartmentField(props) {
   const { item, btns, onFinish } = props;
   const [form] = Form.useForm();
@@ -151,6 +152,7 @@ function DepartmentField(props) {
     </Form>
   );
 }
+
 function ProjectField(props) {
   const { item, btns, onFinish } = props;
   const [form] = Form.useForm();
@@ -176,6 +178,7 @@ function ProjectField(props) {
     </Form>
   );
 }
+
 function ManufacturerField(props) {
   const { item, btns, onFinish } = props;
   const [form] = Form.useForm();
@@ -201,6 +204,7 @@ function ManufacturerField(props) {
     </Form>
   );
 }
+
 function TextField(props) {
   const { item, btns, onFinish } = props;
   const [form] = Form.useForm();
@@ -230,6 +234,7 @@ function TextField(props) {
     </Form>
   );
 }
+
 function DDAttachment(props) {
   const { item, btns, onFinish } = props;
   const [form] = Form.useForm();
@@ -301,6 +306,7 @@ function DDDateField(props) {
     </Form>
   );
 }
+
 function TextareaField(props) {
   const { item, btns, onFinish } = props;
   const [form] = Form.useForm();
@@ -326,6 +332,7 @@ function TextareaField(props) {
     </Form>
   );
 }
+
 function DDSelectField(props) {
   const { item, btns, onFinish, onRelClick } = props;
   const [form] = Form.useForm();
@@ -368,6 +375,7 @@ function DDSelectField(props) {
     </Form>
   );
 }
+
 function DDMultiSelectField(props) {
   const { item, btns, onFinish } = props;
   const [form] = Form.useForm();

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

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

+ 102 - 12
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,28 @@ 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]);
+    let oldValue = formList.find((item) => item.props.id === select);
+    const newFormList = [];
+    for (const item of formList) {
+      if (item.props.id === select) {
+        item.props = { ...oldValue, newItem };
+      }
+      newFormList.push(item);
+    }
+    handleChangeList(newFormList);
+    // let oldValue = formList[select].props;
+    // formList[select].props = { ...oldValue, ...newItem };
+    // handleChangeList([...formList]);
   };
 
   const handleChangeList = (list) => {
@@ -39,6 +81,49 @@ 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 = () => {
+    console.log();
+    let formItem = formList.find((item) => item.props.id === selectList[0]);
+
+    if (formItem?.isTable) {
+      // 如果是表格的话,还要寻找内部的被点击的col
+
+      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 +144,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)}

+ 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,