xjj hace 2 años
padre
commit
ed98a4fc5a

+ 18 - 2
.umirc.ts

@@ -14,7 +14,7 @@ export default defineConfig({
   proxy: {
     '/api': {
       // target: 'http://47.96.12.136:8788/',
-      target: 'http://47.96.12.136:8888/',
+      target: 'http://47.96.12.136:8896/',
       // target: 'http://120.55.44.4:8900/',
       changeOrigin: true,
     },
@@ -55,7 +55,23 @@ export default defineConfig({
     {
       name: 'OA审批',
       path: '/oa',
-      component: './Flow/Oa',
+      hideChildrenInMenu: true,
+      routes: [
+        {
+          path: '/oa',
+          redirect: '/oa/list',
+        },
+        {
+          name: 'OA审批',
+          path: '/oa/list',
+          component: './Flow/Oa',
+        },
+        {
+          name: 'OA详情',
+          path: '/oa/detail/:oaId',
+          component: './Flow/OaDetail',
+        },
+      ],
     },
     {
       name: '权限演示',

+ 23 - 1
config/router.config.ts

@@ -1,4 +1,4 @@
-export default [
+const appMenu = [
   {
     path: '/',
     routes: [
@@ -9,3 +9,25 @@ export default [
     ],
   },
 ];
+const adminMenu = [
+  {
+    path: '/',
+    routes: [
+      { path: '/', redirect: '/login' },
+      {
+        component: '404',
+      },
+    ],
+  },
+];
+function generateMenu(key: string, menu: any) {
+  return menu.map((item: any) => {
+    item.access = key;
+    if(item.access)
+    return item;
+  });
+}
+export default {
+  appMenu: generateMenu('appMenu', appMenu),
+  adminMenu: generateMenu('adminMenu', adminMenu),
+};

+ 1 - 0
package.json

@@ -16,6 +16,7 @@
     "@antv/xflow": "^1.0.55",
     "@umijs/max": "^4.0.64",
     "antd": "^5.0.0",
+    "dayjs": "^1.11.7",
     "moment": "^2.29.4",
     "qs": "^6.11.1",
     "umi-request": "^1.4.0"

+ 2 - 0
src/access.ts

@@ -6,5 +6,7 @@ export default (initialState: API.UserInfo) => {
   );
   return {
     canSeeAdmin,
+    appMenu: true,
+    adminMenu: false,
   };
 };

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

@@ -0,0 +1,36 @@
+import React, { useState, useEffect } from 'react';
+import { Upload, Button, message } from 'antd';
+import { PlusOutlined } from '@ant-design/icons';
+
+const CORP_ID = 'ding0cdce2d5dbf986d9';
+const AGENT_ID = '1788653353';
+
+function DDAttachment(props) {
+  const { disabled, onChange, spaceId = '213865445' } = props;
+  const [value, setValue] = useState([]);
+
+  const upload = () => {
+    // dd.biz.util.uploadAttachment({
+    //   // image: { multiple: true, compress: false, max: 9, spaceId: '12345' },
+    //   space: { corpId: CORP_ID, spaceId: spaceId, isCopy: 1, max: 9 },
+    //   file: { spaceId: spaceId, max: 1 },
+    //   types: ['file', 'space'], //PC端支持["photo","file","space"]
+    //   onSuccess: function(result) {
+    //     //onSuccess将在文件上传成功之后调用
+    //     onChange?.(result.data);
+    //   },
+    //   onFail: function(err) {
+    //     console.log(err);
+    //     message.error('附件上传失败');
+    //   },
+    // });
+  };
+
+  return (
+    <div onClick={upload}>
+      <Button icon={<PlusOutlined />}>添加附件</Button>
+    </div>
+  );
+}
+
+export default DDAttachment;

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

@@ -0,0 +1,49 @@
+import QRCode from 'qrcode.react';
+import React, { useEffect, useState } from 'react';
+import { Spin } from 'antd';
+import { connect } from 'dva';
+
+let timer;
+function DDCode(props) {
+  const {
+    value,
+    onChange,
+    currentUser: { DingUserId },
+  } = props;
+  const [loading, setLoading] = useState(false);
+  // const [time, setTime] = useState(0);
+
+  // const queryCode = async () => {
+  //   setLoading(true);
+  //   // TODO 查询接口获得code
+
+  //   setLoading(false);
+  //   clearInterval(timer);
+  //   onChange({
+  //     code: '',
+  //   });
+  //   let second = 60 * 5;
+  //   if (second <= 0) return;
+  //   setTime(second);
+
+  //   timer = setInterval(() => {
+  //     setTime(time => {
+  //       if (time > 0) {
+  //         return time--;
+  //       }
+  //       return time;
+  //     });
+  //   }, second * 1000);
+  // };
+  if (!DingUserId) return <div>未知用户,请联系管理员。</div>;
+  return (
+    <div>
+      <Spin spinning={loading}>
+        <QRCode size={128} value={`http://47.96.12.136:8896/#/bom/dd-login/${DingUserId}`} />
+        请使用钉钉扫码授权
+      </Spin>
+    </div>
+  );
+}
+
+export default connect(({ user }) => ({ currentUser: user.currentUser }))(DDCode);

+ 13 - 0
src/components/DDComponents/DDDateField/index.js

@@ -0,0 +1,13 @@
+import React from 'react';
+import { DatePicker } from 'antd';
+
+function DDDateField(props) {
+  const { format, disabled, onChange } = props;
+
+  const handleChange = date => {
+    onChange?.(date.format('YYYY-MM-DD'));
+  };
+  return <DatePicker disabled={disabled} format={format.replace("yyyy","YYYY").replace("dd","DD")} onChange={handleChange} />;
+}
+
+export default DDDateField;

+ 22 - 0
src/components/DDComponents/DDDateRangeField/index.js

@@ -0,0 +1,22 @@
+import React from 'react';
+import { DatePicker } from 'antd';
+
+const { RangePicker } = DatePicker;
+
+function DDDateRangeField(props) {
+  const { format, disabled, onChange } = props;
+
+  const handleChange = date => {
+    let value = [date[0].format('YYYY-MM-DD'), date[1].format('YYYY-MM-DD')];
+    onChange?.(JSON.stringify(value));
+  };
+  return (
+    <RangePicker
+      disabled={disabled}
+      format={format.replace('yyyy', 'YYYY').replace('dd', 'DD')}
+      onChange={handleChange}
+    />
+  );
+}
+
+export default DDDateRangeField;

+ 30 - 0
src/components/DDComponents/DDMultiSelectField/index.js

@@ -0,0 +1,30 @@
+import React from 'react';
+import { Select } from 'antd';
+
+const { Option } = Select;
+
+function DDMultiSelectField(props) {
+  const { options, disabled, onChange } = props;
+
+  return (
+    <Select
+      mode="multiple"
+      allowClear
+      style={{ width: '100%' }}
+      disabled={disabled}
+      onChange={value => {
+        onChange(value);
+      }}
+    >
+      {options?.map(cur => {
+        return (
+          <Option key={cur} value={cur}>
+            {cur}
+          </Option>
+        );
+      })}
+    </Select>
+  );
+}
+
+export default DDMultiSelectField;

+ 107 - 0
src/components/DDComponents/DDPhotoField/AliyunOssUploader.js

@@ -0,0 +1,107 @@
+import React from 'react'
+import { Upload, message, Button} from 'antd';
+import { PlusOutlined } from '@ant-design/icons';
+
+class AliyunOSSUpload extends React.Component {
+  state = {
+    OSSData: {},
+  };
+
+  componentDidMount() {
+    this.init();
+  }
+
+  init = () => {
+    try {
+      const { OSSData } = this.props;
+
+      this.setState({
+        OSSData,
+      });
+    } catch (error) {
+      message.error(error);
+    }
+  };
+
+  onChange = ({ file, fileList }) => {
+    const { onChange, onDone , onUploading} = this.props;
+    console.log('Aliyun OSS:', file, fileList);
+    if (onChange) {
+      onChange([...fileList]);
+    }
+
+    if (onDone) {
+      if (file.status === 'done')
+        onDone(file)
+    }
+    if(onUploading && file.status === 'uploading') {
+      onUploading({ file, fileList })
+    }
+    this.setState({ fileList: [...fileList] });
+  };
+
+  onRemove = file => {
+    const { value, onChange } = this.props;
+
+    const files = value.filter(v => v.url !== file.url);
+
+    if (onChange) {
+      onChange(files);
+    }
+  };
+
+  transformFile = file => {
+    const { OSSData } = this.state;
+    file.url = OSSData.dir + file.name;
+    return file;
+  };
+
+  getExtraData = file => {
+    const { OSSData } = this.state;
+
+    return {
+      key: file.url,
+      OSSAccessKeyId: OSSData.accessid,
+      policy: OSSData.policy,
+      Signature: OSSData.signature,
+    };
+  };
+
+  beforeUpload = async () => {
+    const { OSSData } = this.state;
+    const expire = OSSData.expire * 1000;
+
+    if (expire < Date.now()) {
+      await this.init();
+    }
+    return true;
+  };
+
+  render() {
+    const { value, directory, label, noStyle, showUploadList, accept } = this.props;
+    const props = {
+      name: 'file',
+      fileList: this.state.fileList,
+      action: this.state.OSSData.host,
+      onChange: this.onChange,
+      onRemove: this.onRemove,
+      transformFile: this.transformFile,
+      data: this.getExtraData,
+      beforeUpload: this.beforeUpload,
+      accept: accept,
+      showUploadList: showUploadList !== false,
+      headers: { "Access-Control-Allow-Origin": "*" }
+    };
+    return (
+      <Upload {...props} directory={directory}>
+        {noStyle ? label : (<Button type="primary">
+          <PlusOutlined /> {label}
+        </Button>)}
+
+      </Upload>
+    );
+  }
+}
+
+
+export default AliyunOSSUpload;

+ 46 - 0
src/components/DDComponents/DDPhotoField/index.js

@@ -0,0 +1,46 @@
+import React, { useState, useEffect } from 'react';
+import { Upload, Button, Image } from 'antd';
+import AliyunOssUploader from '@/components/OssUpload/AliyunOssUploader';
+import { queryOSSData } from '@/services/boom';
+
+function DDPhotoField(props) {
+  const { disabled, onChange } = props;
+  const [value, setValue] = useState([]);
+  const [ossData, setOssData] = useState();
+
+  const OnModelFileDone = file => {
+    var path = ossData.host + '/' + file.url;
+    let newValue = [...value, path];
+    setValue(newValue);
+    onChange(JSON.stringify(newValue));
+  };
+  const OnUploading = file => {};
+
+  useEffect(() => {
+    queryOSSData().then(res => {
+      setOssData(res.data);
+    });
+  }, []);
+
+  return ossData ? (
+    <div>
+      <div>
+        {value.map(img => (
+          <Image height={200} src={img} />
+        ))}
+      </div>
+      <AliyunOssUploader
+        OSSData={ossData}
+        onDone={OnModelFileDone}
+        onUploading={OnUploading}
+        noStyle={false}
+        showUploadList={false}
+        directory={false}
+        accept={'.png, .jpg, .jpeg'}
+        label="上传图片"
+      ></AliyunOssUploader>
+    </div>
+  ) : null;
+}
+
+export default DDPhotoField;

+ 28 - 0
src/components/DDComponents/DDSelectField/index.js

@@ -0,0 +1,28 @@
+import React from 'react';
+import { Select } from 'antd';
+
+const { Option } = Select;
+
+function DDSelectField(props) {
+  const { options, disabled, onChange } = props;
+
+  return (
+    <Select
+      style={{ width: '100%' }}
+      disabled={disabled}
+      onChange={value => {
+        onChange(String(value));
+      }}
+    >
+      {options?.map(cur => {
+        return (
+          <Option key={cur} value={cur}>
+            {cur}
+          </Option>
+        );
+      })}
+    </Select>
+  );
+}
+
+export default DDSelectField;

+ 29 - 0
src/components/DDComponents/DepartmentField/index.js

@@ -0,0 +1,29 @@
+import { TreeSelect } from 'antd';
+import { connect } from 'dva';
+
+function DepartmentField(props) {
+  const { value, onChange, depUserTree } = props;
+  const onChangeValue = newValue => {
+    let dep = depUserTree.find(dep => dep.id == newValue);
+    onChange(String(dep?.ID));
+  };
+
+  return (
+
+    <TreeSelect
+      showSearch
+      multiple
+      allowClear
+      dropdownStyle={{
+        maxHeight: 400,
+        overflow: 'auto',
+      }}
+      style={{ width: '100%' }}
+      placeholder="请选择部门"
+      treeData={depUserTree}
+      onChange={onChangeValue}
+    />
+  );
+}
+
+export default connect(({ user }) => ({ depUserTree: user.depUserTree }))(DepartmentField);

+ 29 - 0
src/components/DDComponents/InnerContactField/index.js

@@ -0,0 +1,29 @@
+import React from 'react';
+import { message, Select } from 'antd';
+import { connect } from 'dva';
+
+const { Option } = Select;
+
+function InnerContactField(props) {
+  const { value, userList, onChange } = props;
+  return (
+    <Select
+      showSearch
+      onChange={value => {
+        onChange(String(value));
+        // onChange(JSON.stringify([value]));
+      }}
+      filterOption={(input, option) => option.children.toLowerCase().includes(input.toLowerCase())}
+    >
+      {(userList || []).map(item => (
+        <Option key={item.ID} value={item.ID}>
+          {item.CName}
+        </Option>
+      ))}
+    </Select>
+  );
+}
+
+export default connect(({ user }) => ({
+  userList: user.list,
+}))(InnerContactField);

+ 19 - 0
src/components/DDComponents/NumberField/index.js

@@ -0,0 +1,19 @@
+import React from 'react';
+import { InputNumber } from 'antd';
+
+function NumberField(props) {
+  const { onChange, disabled, unit } = props;
+
+  return (
+    <InputNumber
+      style={{ width: '100%' }}
+      disabled={disabled}
+      formatter={value => `${value}${unit || ''}`}
+      onChange={e => {
+        onChange?.(String(e));
+      }}
+    />
+  );
+}
+
+export default NumberField;

+ 19 - 0
src/components/DDComponents/PhoneField/index.js

@@ -0,0 +1,19 @@
+import React from 'react';
+import { Input, Row, Col, Select } from 'antd';
+import { connect } from 'dva';
+
+const { Option } = Select;
+
+function PhoneField(props) {
+  const { onChange } = props;
+  return (
+    <Input.Group size="large">
+      <Select defaultValue="+86">
+        <Option value="+86">+86</Option>
+      </Select>
+      <Input style={{ width: '80%' }} {...props} onChange={e => onChange(e.target.value)} />
+    </Input.Group>
+  );
+}
+
+export default PhoneField;

+ 90 - 0
src/components/DDComponents/TableField/index.js

@@ -0,0 +1,90 @@
+import React, { useMemo, useState, useEffect } from 'react';
+import { Button } from 'antd';
+import DDComponents from '../index';
+import style from './index.less';
+
+function TableField(props) {
+  const { item, onChange } = props;
+  const [value, setValue] = useState([]);
+
+  const getEmptyValue = () => {
+    let data = item.children.map(children => ({
+      name: children.props.id,
+      value: '',
+    }));
+    data.id = Math.random();
+    return data;
+  };
+  const getChildren = (item, data) => {
+    let cur = data.find(d => d.name == item.props.id);
+    const childrenChange = value => {
+      cur.value = value;
+      onChange(JSON.stringify(value));
+    };
+    return DDComponents({ item, onChange: childrenChange, value: cur.value });
+  };
+
+  // const localValue = useMemo(() => {
+  //   let v = [];
+  //   try {
+  //     v = JSON.parse(value);
+  //   } catch (error) {}
+  //   if (v.length == 0) {
+  //     let data = getEmptyValue();
+  //     v.push(data);
+  //   }
+  //   return v;
+  // }, [value]);
+
+  const addRow = () => {
+    let newValue = [...value, getEmptyValue()];
+    setValue(newValue);
+    onChange(JSON.stringify(newValue));
+  };
+
+  const removeRow = index => {
+    let newValue = [...value];
+    newValue.splice(index, 1);
+
+    setValue(newValue);
+    onChange(JSON.stringify(newValue));
+  };
+  useEffect(() => {
+    setValue([getEmptyValue()]);
+  }, []);
+
+  return (
+    <div>
+      <table className={style.table}>
+        <thead>
+          <tr>
+            <th></th>
+            {item.children.map(item => (
+              <th>{item.props.label}</th>
+            ))}
+            <th>操作</th>
+          </tr>
+        </thead>
+        <tbody>
+          {value.map((curItem, index) => (
+            <tr key={curItem.id}>
+              <td>{index + 1}</td>
+              {item.children.map(item => (
+                <td>{getChildren(item, curItem)}</td>
+              ))}
+              <td>
+                {value.length > 1 && <a onClick={() => removeRow(index)}>删除</a>}
+                {/* <a>复制</a> */}
+              </td>
+            </tr>
+          ))}
+        </tbody>
+      </table>
+      <Button style={{ marginTop: 20 }} onClick={addRow}>
+        {item.props.actionName}
+      </Button>
+    </div>
+  );
+}
+
+export default TableField;

+ 16 - 0
src/components/DDComponents/TableField/index.less

@@ -0,0 +1,16 @@
+.table {
+  width: 100%;
+  td {
+    padding: 10px;
+    border-bottom: 1px solid rgba(0, 0, 0, 0.06);
+  }
+  th {
+    padding: 10px;
+    color: #000000d9;
+    font-weight: 500;
+    text-align: left;
+    background: #fafafa;
+    border-bottom: 1px solid rgba(0, 0, 0, 0.06);
+    transition: background 0.3s ease;
+  }
+}

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

@@ -0,0 +1,103 @@
+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 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';
+
+const { Option } = Select;
+const { RangePicker } = DatePicker;
+
+export default function DDComponents(props) {
+  const { item, onChange } = props;
+  const {
+    placeholder,
+    options,
+    format,
+    unit,
+    disabled,
+  } = item.props;
+  let component = null;
+  switch (item.componentName) {
+    case 'TextField': //单行输入
+      component = (
+        <Input
+          disabled={disabled}
+          placeholder={placeholder}
+          onChange={e => onChange?.(e.target.value)}
+        />
+      );
+      break;
+    case 'TextareaField': //多行输入
+      component = (
+        <Input.TextArea
+          disabled={disabled}
+          placeholder={placeholder}
+          onChange={e => onChange?.(e.target.value)}
+        />
+      );
+      break;
+    case 'NumberField': //数字输入
+      component = <NumberField disabled={disabled} unit={unit} onChange={onChange} />;
+      break;
+    case 'DDSelectField': //单选框
+      component = <DDSelectField options={options} onChange={onChange} disabled={disabled} />;
+      break;
+    case 'DDMultiSelectField': //多选框
+      component = <DDMultiSelectField disabled={disabled} options={options} onChange={onChange} />;
+      break;
+    case 'DDDateField': //日期控件
+      component = <DDDateField format={format} disabled={disabled} onChange={onChange} />;
+      break;
+    case 'DDDateRangeField': //时间区间控件
+      component = <DDDateRangeField format={format} disabled={disabled} onChange={onChange} />;
+      break;
+    case 'TextNote': //文本说明控件
+      console.info('文本说明控件!');
+      console.log(item);
+      break;
+    case 'PhoneField': //电话控件
+      component = <PhoneField onChange={onChange} />;
+      break;
+    case 'DDPhotoField': //图片控件
+      component = <DDPhotoField />;
+      break;
+    case 'MoneyField': //金额控件
+      component = <Input placeholder={placeholder} onChange={onChange} />;
+      break;
+    case 'TableField': //明细控件
+      component = <TableField item={item} />;
+      break;
+    case 'DDAttachment': //附件
+      // component = <DDAttachment />
+      // component = '附件控件未渲染!'
+      console.info('附件控件未渲染!');
+      break;
+    case 'InnerContactField': //联系人控件
+      component = <InnerContactField onChange={onChange}></InnerContactField>;
+      break;
+    case 'DepartmentField': //部门控件
+      component = <DepartmentField onChange={onChange} />;
+      break;
+    case 'RelateField': //关联审批单
+      component = '关联审批单控件未渲染!'
+      break;
+    case 'AddressField': //省市区控件
+      component = '省市区控件未渲染!'
+      break;
+    case 'StarRatingField': //评分控件
+      component = '评分控件未渲染!'
+      break;
+    case 'FormRelateField': //关联控件
+      component = '关联控件未渲染!'
+      break;
+  }
+
+  return component;
+}

+ 10 - 8
src/pages/Flow/AuditModal.js

@@ -1,9 +1,9 @@
-import React, { useEffect } from "react";
-import { Modal, Input, Form, Select } from "antd";
+import React, { useEffect } from 'react';
+import { Modal, Input, Form, Select } from 'antd';
 
 // 审批意见
 function AuditModal(props) {
-  const { visible, onCancel, onOk, loading } = props;
+  const { visible, onCancel, onOk, loading, classify } = props;
   const [form] = Form.useForm();
   const formLayout = { labelCol: { span: 4 }, wrapperCol: { span: 14 } };
 
@@ -31,11 +31,13 @@ function AuditModal(props) {
         <Form.Item label="详情" name="desc">
           <Input.TextArea />
         </Form.Item>
-        <Form.Item label="分类" name="desc">
-          <Select options={[
-            {value: 1,label: "BOM"},
-            {value: 2,label: "OA"},
-          ]}></Select>
+        <Form.Item label="分类" name="classify_id">
+          <Select
+            options={classify.map((item) => ({
+              value: item.id,
+              label: item.name,
+            }))}
+          ></Select>
         </Form.Item>
       </Form>
     </Modal>

+ 77 - 140
src/pages/Flow/Oa.js

@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useMemo } from 'react';
 import { Collapse, Layout, List } from 'antd';
 import {
   UserOutlined,
@@ -7,105 +7,12 @@ import {
   ExceptionOutlined,
 } from '@ant-design/icons';
 import { PageContainer } from '@ant-design/pro-components';
-import { Link } from '@umijs/max';
+import { Link, useRequest } from '@umijs/max';
+import { queryAuditList, queryClassify } from '@/services/boom';
 
 const { Sider, Content } = Layout;
 const { Panel } = Collapse;
 
-const categories = [
-  {
-    name: '请假审批',
-    items: [
-      {
-        name: '病假申请',
-        icon: <UserOutlined />,
-      },
-      {
-        name: '事假申请',
-        icon: <FileOutlined />,
-      },
-      {
-        name: '探亲假申请',
-        icon: <UserOutlined />,
-      },
-      {
-        name: '婚假申请',
-        icon: <UserOutlined />,
-      },
-      {
-        name: '产假申请',
-        icon: <UserOutlined />,
-      },
-      {
-        name: '陪产假申请',
-        icon: <UserOutlined />,
-      },
-      {
-        name: '丧假申请',
-        icon: <UserOutlined />,
-      },
-    ],
-  },
-  {
-    name: '加班审批',
-    items: [
-      {
-        name: '普通加班申请',
-        icon: <EditOutlined />,
-      },
-      {
-        name: '周末加班申请',
-        icon: <EditOutlined />,
-      },
-      {
-        name: '节假日加班申请',
-        icon: <EditOutlined />,
-      },
-    ],
-  },
-  {
-    name: '报销审批',
-    items: [
-      {
-        name: '差旅费报销',
-        icon: <FileOutlined />,
-      },
-      {
-        name: '通讯费报销',
-        icon: <FileOutlined />,
-      },
-      {
-        name: '餐费报销',
-        icon: <FileOutlined />,
-      },
-      {
-        name: '办公用品采购报销办公用品采购报销办公用品采购报销办公用品采购报销',
-        icon: <FileOutlined />,
-      },
-      {
-        name: '项目费用报销',
-        icon: <FileOutlined />,
-      },
-    ],
-  },
-  {
-    name: '异常审批',
-    items: [
-      {
-        name: '物品遗失申请',
-        icon: <ExceptionOutlined />,
-      },
-      {
-        name: '客诉申请',
-        icon: <ExceptionOutlined />,
-      },
-      {
-        name: '工作计划申请',
-        icon: <ExceptionOutlined />,
-      },
-    ],
-  },
-];
 const getBackgroundColors = (index) => {
   const backgroundColors = [
     '#1ABC9C', // 深绿色
@@ -121,52 +28,82 @@ const getBackgroundColors = (index) => {
   return backgroundColors[index % backgroundColors.length];
 };
 const Approval = () => {
+  const { data: classify, loading } = useRequest(queryClassify, {
+    defaultParams: [{ c_type: 1 }],
+  });
+  const { data: auditList } = useRequest(queryAuditList, {
+    defaultParams: [{ flow_type: 1 }],
+  });
+
+  const categories = useMemo(() => {
+    if (!auditList || !classify) return [];
+    let categories = {};
+    auditList.forEach((item) => {
+      const { classify_id } = item;
+      if (!categories[classify_id]) {
+        categories[classify_id] = {
+          id: classify_id,
+          name: classify.find((item) => item.id == classify_id)?.name,
+          items: [],
+        };
+      }
+      categories[classify_id].items.push(item);
+    });
+    return Object.values(categories);
+  }, [classify, auditList]);
+
   return (
-    <PageContainer>
-      <Collapse defaultActiveKey={categories.map((category) => category.name)}>
-        {categories.map((category, index) => (
-          <Panel style={{userSelect: 'none'}} header={category.name} key={category.name}>
-            <List
-              grid={{ gutter: 16, column: 3 }}
-              dataSource={category.items}
-              renderItem={(item) => (
-                <List.Item key={item.name}>
-                  <Link
-                    to={`/oa/${item.name}`}
-                    style={{ display: 'flex', alignItems: 'center' }}
-                  >
-                    <div
-                      style={{
-                        width: 48,
-                        height: 48,
-                        borderRadius: 8,
-                        textAlign: 'center',
-                        lineHeight: '48px',
-                        color: '#fff',
-                        fontSize: 24,
-                        backgroundColor: getBackgroundColors(index),
-                      }}
-                    >
-                      {item.icon}
-                    </div>
-                    <span
-                      style={{
-                        marginLeft: 12,
-                        flex: 1,
-                        fontSize: 18,
-                        maxWidth: '100%',
-                        paddingRight: 30,
-                      }}
+    <PageContainer loading={loading}>
+      {categories.length > 0 && (
+        <Collapse defaultActiveKey={categories.map((category) => category.id)}>
+          {categories.map((category, index) => (
+            <Panel
+              style={{ userSelect: 'none' }}
+              header={category.name}
+              key={category.id}
+            >
+              <List
+                grid={{ gutter: 16, column: 3 }}
+                dataSource={category.items}
+                renderItem={(item) => (
+                  <List.Item key={item.id}>
+                    <Link
+                      to={`/oa/detail/${item.id}`}
+                      style={{ display: 'flex', alignItems: 'center' }}
                     >
-                      {item.name}
-                    </span>
-                  </Link>
-                </List.Item>
-              )}
-            ></List>
-          </Panel>
-        ))}
-      </Collapse>
+                      <div
+                        style={{
+                          width: 48,
+                          height: 48,
+                          borderRadius: 8,
+                          textAlign: 'center',
+                          lineHeight: '48px',
+                          color: '#fff',
+                          fontSize: 24,
+                          backgroundColor: getBackgroundColors(index),
+                        }}
+                      >
+                        <UserOutlined />
+                      </div>
+                      <span
+                        style={{
+                          marginLeft: 12,
+                          flex: 1,
+                          fontSize: 18,
+                          maxWidth: '100%',
+                          paddingRight: 30,
+                        }}
+                      >
+                        {item.name}
+                      </span>
+                    </Link>
+                  </List.Item>
+                )}
+              ></List>
+            </Panel>
+          ))}
+        </Collapse>
+      )}
     </PageContainer>
   );
 };

+ 6 - 4
src/pages/Flow/OaDetail.js

@@ -1,7 +1,8 @@
-import React from 'react';
+import React, { useState } from 'react';
 import { PageContainer } from '@ant-design/pro-components';
 import { Col, Empty, Row } from 'antd';
 import ApprovalProcess from './components/ApprovalProcess';
+import AuditDetailed from './components/AuditDetailed';
 import { queryGetBomForm } from '@/services/boom';
 
 const OaDetail = () => {
@@ -19,9 +20,9 @@ const OaDetail = () => {
     }
   };
   //填写表单实时计算审批流程
-  const advanceSubmit = async () => {
+  const advanceSubmit = async (fieldsValue) => {
     console.log('重重新计算审批流程');
-    var fieldsValue = await form.validateFields();
+    // var fieldsValue = await form.validateFields();
 
     let result = Object.values(fieldsValue)
       .map((item) => {
@@ -56,11 +57,12 @@ const OaDetail = () => {
       },
     });
   };
+
   return (
     <PageContainer>
       <Row>
         <Col span={17}>
-          <Components items={items} onChange={advanceSubmit} />
+          <AuditDetailed items={items} onFieldsChange={advanceSubmit} />
         </Col>
         <Col offset={1} span={6}>
           {approvalProcess.length == 0 ? ( //!formComponentValues[item.nodeId] ||

+ 94 - 0
src/pages/Flow/components/AuditDetailed.js

@@ -0,0 +1,94 @@
+import DDComponents from '@/components/DDComponents';
+import React, { useMemo, useState } from 'react';
+import { Form } from 'antd';
+
+const AuditDetailed = (props) => {
+  const [form] = Form.useForm();
+  const { items, onFieldsChange } = props;
+
+  const behavior = useMemo(() => {
+    let data = {};
+    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,
+            };
+          });
+        });
+      }
+    });
+
+    return data;
+  }, [items]);
+
+  const onFinish = (values) => {
+    console.log(values);
+  };
+
+  const GetComponent = (item) => {
+    const { id, label, bizAlias, required, notUpper } = 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;
+      }
+    }
+    let formLabel;
+    if (bizAlias) {
+      formLabel = bizAlias;
+    } else {
+      try {
+        // 判断label是否为JSON数组
+        formLabel = JSON.parse(label).join(',');
+      } catch (error) {
+        formLabel = label;
+      }
+    }
+
+    // const component = DDComponents({ item });
+    // if (!component) return null;
+    return (
+      <Form.Item label={formLabel} rules={[{ required }]}>
+        <DDComponents item={item} />
+        {notUpper == 1 && <p>大写</p>}
+      </Form.Item>
+    );
+  };
+
+  return (
+    <Form
+      form={form}
+      style={{ height: '400px', overflowY: 'scroll', paddingRight: 20 }}
+      layout="vertical"
+      autoComplete="off"
+      onFieldsChange={onFieldsChange}
+    >
+      {items.map((item) => GetComponent(item))}
+    </Form>
+  );
+};
+
+export default AuditDetailed;

+ 23 - 13
src/pages/Flow/index.js

@@ -1,22 +1,25 @@
 import React, { useState, useEffect } from 'react';
-import { Form, Button, Table } from 'antd';
+import { Button, Table } from 'antd';
 import { connect, useNavigate } from 'umi';
 import AuditModal from './AuditModal';
 import { PageContainer, ProCard } from '@ant-design/pro-components';
 
 function Audit(props) {
-  const { list = [], dispatch, loading } = props;
+  const { list = [], classify = [], dispatch, loading } = props;
   let navigate = useNavigate();
-  const [form] = Form.useForm();
   const [visible, setVisible] = useState({
     audit: false,
     auditNode: false,
   });
-  const [tabActive, setTabActive] = useState('1');
   const columns = [
     {
       title: '审批流名称',
-      dataIndex: ['list', 'name'],
+      dataIndex: 'name',
+    },
+    {
+      title: '分类',
+      dataIndex: 'classify_id',
+      render: (id) => classify?.find((item) => item.id == id)?.name || '-',
     },
     {
       title: '操作',
@@ -36,7 +39,10 @@ function Audit(props) {
   const handleAuditOk = (values) => {
     dispatch({
       type: 'flow/addAudit',
-      payload: values,
+      payload: {
+        ...values,
+        flow_type: 1,
+      },
       callback: () => {
         changeVisible('audit', false);
       },
@@ -63,6 +69,15 @@ function Audit(props) {
   useEffect(() => {
     dispatch({
       type: 'flow/queryAuditList',
+      payload: {
+        flow_type: 1,
+      },
+    });
+    dispatch({
+      type: 'flow/queryClassify',
+      payload: {
+        c_type: 1,
+      },
     });
   }, []);
 
@@ -78,12 +93,6 @@ function Audit(props) {
           新建流程
         </Button>,
       ]}
-      tabActiveKey={tabActive}
-      tabList={[
-        { key: 1, tab: 'OA' },
-        { key: 2, tab: 'BOM' },
-      ]}
-      onTabChange={setTabActive}
     >
       <ProCard>
         <Table
@@ -99,12 +108,13 @@ function Audit(props) {
         visible={visible.audit}
         onOk={handleAuditOk}
         onCancel={() => changeVisible('audit', false)}
+        classify={classify}
       />
     </PageContainer>
   );
 }
 export default connect(({ flow, loading }) => ({
-  userList: [],
   list: flow.auditList,
+  classify: flow.classify,
   loading: loading.effects,
 }))(Audit);

+ 15 - 4
src/pages/Flow/models/flow.js

@@ -3,6 +3,7 @@ import {
   addAudit,
   queryProcessFlows,
   saveAuditFlowInfo,
+  queryClassify
   // addAuditNode,
   // addFlow,
   // queryBoomFlowDetail,
@@ -145,6 +146,7 @@ export default {
     templateList: [],
     depUserTree: [],
     simpleFlowDteail: '',
+    classify: []
   },
 
   effects: {
@@ -154,10 +156,19 @@ export default {
         yield put({
           type: 'save',
           payload: {
-            auditList: response.data.map((item) => ({
-              ...item,
-              id: item.list.id,
-            })),
+            auditList: response.data,
+          },
+        });
+      } catch (error) {}
+    },
+    *queryClassify({ payload }, { call, put }) {
+      try {
+        const response = yield call(queryClassify, payload);
+
+        yield put({
+          type: 'save',
+          payload: {
+            classify: response.data,
           },
         });
       } catch (error) {}

+ 20 - 12
src/services/boom.js

@@ -2,7 +2,13 @@ import { request } from 'umi';
 import { stringify } from 'qs';
 
 export async function queryAuditList(params) {
-  return request(`/api/v1/purchase/flow/info?${stringify(params)}`);
+  let res = await request(`/api/v1/purchase/flow/info?${stringify(params)}`);
+  res.data = res.data.map((item) => ({
+    ...item,
+    list: null,
+    ...item.list,
+  }));
+  return res;
 }
 
 export async function addAudit(data) {
@@ -44,6 +50,19 @@ export async function queryGetBomForm(params) {
   );
   return res;
 }
+//获取分类列表
+export async function queryClassify(data) {
+  let res = await request(`/api/v1/purchase/bom/get-classify`, {
+    method: 'GET',
+    params: data,
+  });
+  return res;
+}
+
+export async function queryOSSData() {
+  return request(`/config/chart-template-img?destDir=public/bom`);
+}
+
 // /**
 //   project_id
 //   version_id	大版本id
@@ -354,9 +373,6 @@ export async function queryGetBomForm(params) {
 //   });
 // }
 
-// export async function queryOSSData() {
-//   return request(`/config/chart-template-img?destDir=public/bom`);
-// }
 
 // export async function queryRecordSheet(data) {
 //   return request(`/api/v1/purchase/record/sheet?${stringify(data)}`, {
@@ -425,14 +441,6 @@ export async function queryGetBomForm(params) {
 //   return res.data;
 // }
 
-// //获取分类列表
-// export async function queryClassify() {
-//   let res = await request(`/api/v1/purchase/bom/get-classify`, {
-//     method: 'GET',
-//   });
-//   return res.data;
-// }
-
 // export async function queryBindClassify(params) {
 //   let res = await request(`/api/v1/purchase/bom/get-bind-classify?${stringify(params)}`, {
 //     method: 'GET',