Pārlūkot izejas kodu

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

xujunjie 2 gadi atpakaļ
vecāks
revīzija
237c7d483c

+ 1 - 0
config/config.js

@@ -142,6 +142,7 @@ export default {
     '/api': {
       // target: 'http://192.168.20.152:8888/',
       target: 'http://120.55.44.4:8896/',
+      // target: 'http://47.96.12.136:8888/',
       // target: 'http://47.96.12.136:8896/',
       // target: 'http://oraysmart.com:8889/',
       // target: 'http://oraysmart.com:8888/api',

+ 3 - 1
package.json

@@ -68,6 +68,7 @@
     "lodash-decorators": "^6.0.1",
     "luckyexcel": "^1.0.1",
     "memoize-one": "^5.0.0",
+    "module": "^1.2.5",
     "moment": "^2.24.0",
     "nanoid": "1.0.0",
     "numeral": "^2.0.6",
@@ -97,7 +98,8 @@
     "react-router-dom": "^4.3.1",
     "react-sizeme": "^2.6.7",
     "react-sortable-hoc": "^2.0.0",
-    "react-zmage": "^0.8.5"
+    "react-zmage": "^0.8.5",
+    "swiper": "^8.4.5"
   },
   "devDependencies": {
     "@types/react": "^16.8.1",

+ 58 - 65
src/components/Flow/node/rect/mapServe.tsx

@@ -124,7 +124,7 @@ const Component = (props: any) => {
         });
       });
     } catch (error) {
-      message.error("excel文件导入失败!请联系管理员。")
+      message.error('excel文件导入失败!请联系管理员。');
       return false;
     }
   };
@@ -146,6 +146,7 @@ const Component = (props: any) => {
     var idx = name?.lastIndexOf('/');
     let str = name.substring(idx + 1, name.length);
     setFileName(str);
+    onNodeConfigChange('version_name', str);
   };
 
   useEffect(() => {
@@ -192,7 +193,6 @@ const Component = (props: any) => {
           onChange={value => {
             onNodeConfigChange('muti_version', value);
           }}
-          disabled={meta?.editMode == 2}
           options={[
             { label: '是', value: 1 },
             { label: '否', value: 0 },
@@ -204,7 +204,6 @@ const Component = (props: any) => {
           onChange={value => {
             onNodeConfigChange('is_start_node', value);
           }}
-          disabled={meta?.editMode == 2}
           options={[
             { label: '是', value: 1 },
             { label: '否', value: 0 },
@@ -215,19 +214,16 @@ const Component = (props: any) => {
             <InputFiled
               label="模板名称"
               value={nodeConfig.version_name}
-              disabled={meta?.editMode == 2}
               onChange={value => {
                 onNodeConfigChange('version_name', value);
               }}
             />
 
-            {meta?.editMode == 1 && (
-              <UploadFiled
-                label="模板"
-                onChange={url => onNodeConfigChange('bom_template', url)}
-                beforeUpload={beforeUpload}
-              />
-            )}
+            <UploadFiled
+              label="模板"
+              onChange={url => onNodeConfigChange('bom_template', url)}
+              beforeUpload={beforeUpload}
+            />
 
             {/* <div onClick={handleFileNameClick}>{fileName}</div> */}
             <a href={nodeConfig.bom_template}>{fileName}</a>
@@ -252,68 +248,65 @@ const Component = (props: any) => {
         </div>
       </div>
       {meta?.editMode != 2 && (
-        <>
-          <div className={`${PREFIX}-panel-group`}>
-            <h5>样式</h5>
-            <Position
-              x={nodeConfig.x}
-              y={nodeConfig.y}
-              onChange={(key, value) => {
-                onNodeConfigChange(key, value);
-              }}
-            />
-            <Size
-              width={nodeConfig.width}
-              height={nodeConfig.height}
-              onChange={(key, value) => {
-                onNodeConfigChange(key, value);
-              }}
-            />
-            <ColorPicker
-              label="填充"
-              value={nodeConfig.fill}
-              onChange={(value: string) => {
-                onNodeConfigChange('fill', value);
+        <div className={`${PREFIX}-panel-group`}>
+          <h5>样式</h5>
+          <Position
+            x={nodeConfig.x}
+            y={nodeConfig.y}
+            onChange={(key, value) => {
+              onNodeConfigChange(key, value);
+            }}
+          />
+          <Size
+            width={nodeConfig.width}
+            height={nodeConfig.height}
+            onChange={(key, value) => {
+              onNodeConfigChange(key, value);
+            }}
+          />
+          <ColorPicker
+            label="填充"
+            value={nodeConfig.fill}
+            onChange={(value: string) => {
+              onNodeConfigChange('fill', value);
+            }}
+          />
+          <ColorPicker
+            label="边框"
+            value={nodeConfig.stroke}
+            onChange={(value: string) => {
+              onNodeConfigChange('stroke', value);
+            }}
+          />
+          <InputNumberFiled
+            label="消息数量"
+            value={nodeConfig.count}
+            onChange={value => {
+              onNodeConfigChange('count', value);
+            }}
+          />
+          <div style={{ display: 'flex' }}>
+            <InputNumberFiled
+              label="字号"
+              value={nodeConfig.fontSize}
+              width={68}
+              onChange={value => {
+                onNodeConfigChange('fontSize', value);
               }}
+              style={{ marginRight: 10 }}
             />
             <ColorPicker
-              label="边框"
-              value={nodeConfig.stroke}
+              value={nodeConfig.fontFill}
               onChange={(value: string) => {
-                onNodeConfigChange('stroke', value);
+                onNodeConfigChange('fontFill', value);
               }}
             />
-            <InputNumberFiled
-              label="消息数量"
-              value={nodeConfig.count}
-              onChange={value => {
-                onNodeConfigChange('count', value);
-              }}
-            />
-            <div style={{ display: 'flex' }}>
-              <InputNumberFiled
-                label="字号"
-                value={nodeConfig.fontSize}
-                width={68}
-                onChange={value => {
-                  onNodeConfigChange('fontSize', value);
-                }}
-                style={{ marginRight: 10 }}
-              />
-              <ColorPicker
-                value={nodeConfig.fontFill}
-                onChange={(value: string) => {
-                  onNodeConfigChange('fontFill', value);
-                }}
-              />
-            </div>
           </div>
-
-          <Button type="primary" onClick={onSave}>
-            保存
-          </Button>
-        </>
+        </div>
       )}
+      <Button type="primary" onClick={onSave}>
+        保存
+      </Button>
     </div>
   );
 };

+ 71 - 18
src/pages/Detail/FilesModal.js

@@ -1,28 +1,73 @@
-import React, { useEffect, useState, useRef } from 'react';
+import React, { useEffect, useState, useRef, useMemo } from 'react';
 import { UploadOutlined } from '@ant-design/icons';
 import { Form } from '@ant-design/compatible';
 import '@ant-design/compatible/assets/index.css';
-import { Button, Modal, message, Table, Spin, Tabs, Upload } from 'antd';
+import { Button, Modal, message, Table, Spin, Tabs, Upload, Select, Space } from 'antd';
 const { TabPane } = Tabs;
 const { confirm } = Modal;
 import moment from 'moment/moment';
 import PreviewFile from '@/components/PreviewFile';
+import { getToken, GetTokenFromUrl } from '@/utils/utils';
 // 历史清单
 function FilesModal(props) {
   const {
+    projectId,
     visible,
     onClose,
     onUpload,
     data,
-    uploadProps,
+    // uploadProps,
+    queryFiles,
     DeleteFile,
     downloadFile,
     loading,
+    typeOptions,
   } = props;
-  const handleSelect = item => {
-    // onSelect(item);
-    // onClose();
-  };
+  const [value, setValue] = useState();
+  const [showData, setShowData] = useState();
+
+  useEffect(() => {
+    if (!value) {
+      setShowData(data);
+      return;
+    }
+    const newData = data.filter(item => item.classify_id == value);
+    setShowData(newData);
+  }, [data, value]);
+
+  const uploadProps = useMemo(() => {
+    const token = getToken() || GetTokenFromUrl();
+    const uploadProps = {
+      name: 'file',
+      showUploadList: false,
+      action: `/api/v1/purchase/attachment/${projectId}`,
+      headers: {
+        'JWT-TOKEN': token,
+      },
+      data: {
+        classify_id: value,
+      },
+      beforeUpload(file) {
+        if (!value) {
+          message.error('请先选择附件分类');
+          return false;
+        }
+      },
+      onChange(info) {
+        if (info.file.status !== 'uploading') {
+          console.log(info.file, info.fileList);
+        }
+        if (info.file.status === 'done') {
+          message.success(`${info.file.name} 文件上传成功`);
+          queryFiles();
+        } else if (info.file.status === 'error') {
+          message.error(`${info.file.name} 文件上传失败`);
+        }
+      },
+    };
+    return uploadProps;
+  }, [value, projectId]);
+
   const columns = [
     {
       title: '预览',
@@ -43,6 +88,11 @@ function FilesModal(props) {
       dataIndex: 'CreatorUser',
       render: record => record.CName || '',
     },
+    {
+      title: '分类',
+      dataIndex: 'classify_id',
+      render: id => typeOptions.find(item => item.id == id)?.name,
+    },
     {
       title: '操作',
       render: record => (
@@ -52,7 +102,7 @@ function FilesModal(props) {
               downloadFile(record);
             }}
           >
-            查看
+            下载
           </a>
           <a
             onClick={() => {
@@ -74,21 +124,24 @@ function FilesModal(props) {
       ),
     },
   ];
-
-  const onClick = item => {
-    // onClose();
-    // onSelect(item);
-  };
   return (
-    <Modal title="附件列表" width="60%" onCancel={onClose} visible={visible} footer={false}>
-      <div>
+    <Modal title="附件列表" width="70%" onCancel={onClose} visible={visible} footer={false}>
+      <Space style={{ display: 'flex', marginBottom: '20px' }}>
+        <div>附件类型:</div>
+        <Select
+          style={{ width: 400 }}
+          value={value}
+          allowClear
+          options={typeOptions}
+          onChange={value => setValue(value)}
+        />
         <Upload {...uploadProps}>
-          <Button type="primary" style={{ marginBottom: 20 }} loading={loading}>
+          <Button type="primary" loading={loading}>
             <UploadOutlined /> 上传文件
           </Button>
         </Upload>
-      </div>
-      <Table rowKey="id" columns={columns} dataSource={data} loading={loading} />
+      </Space>
+      <Table rowKey="id" columns={columns} dataSource={showData} loading={loading} />
     </Modal>
   );
 }

+ 247 - 32
src/pages/Detail/FlowModal.js

@@ -1,15 +1,36 @@
 import React, { useEffect, useState, useRef, useMemo, memo } from 'react';
-import { Modal, Input, Select, List, Row, Col, Table, message, Steps, Space, Button, Popover } from 'antd';
+import {
+  Modal,
+  Input,
+  Select,
+  List,
+  Row,
+  Col,
+  Table,
+  message,
+  Steps,
+  Space,
+  Button,
+  Popover,
+  Cascader,
+  AutoComplete,
+} from 'antd';
 import Flow from '@/components/Flow/index';
 import { connect } from 'dva';
 import { GetTokenFromUrl, getToken } from '@/utils/utils';
 import { MODELS, useXFlowApp, useModelAsync } from '@antv/xflow';
 import { CheckOutlined } from '@ant-design/icons';
-import { queryDingInstanceDetail, queryRecordSheet, queryVserionByNode } from '@/services/boom';
+import {
+  queryDelPurchaseExcel,
+  queryDingInstanceDetail,
+  queryRecordSheet,
+  queryVserionByNode,
+} from '@/services/boom';
 import { async } from '@antv/x6/lib/registry/marker/async';
 import VersionModal from './VersionModal';
 import AuditFlow from './AuditFlow';
 
+const { Option } = Select;
 const { Step } = Steps;
 
 const { TextArea } = Input;
@@ -18,25 +39,37 @@ const PAGE_SIZE = 8;
 
 // 提交
 function FlowModal(props) {
+  let token = getToken();
+  const SELECT_TYPE = {
+    NAME: '0',
+    TYPE: '1',
+    CREATOR: '2',
+  };
   const {
     visible,
     version,
     onClose,
     onChangeVersion,
+    onDelVersion,
     form,
     loading,
     flowDetail,
     dispatch,
     isOut,
     onCommit,
+    commitLoading,
     currentUser,
+    typeOptions,
+    userList,
   } = props;
   const [data, setData] = useState([]);
+  const [showData, setShowData] = useState([]);
   const [nodeLoading, setNodeLoading] = useState(false);
   const [pageSize, setPageSize] = useState(PAGE_SIZE);
   const [stepsData, setStepsData] = useState([]);
   const [versionVisible, setVersionVisible] = useState(false);
-  let token = getToken();
+  const [selectType, setSelectType] = useState(SELECT_TYPE.NAME);
+  const [inputValue, setInputValue] = useState('');
 
   const graphData = useMemo(() => {
     if (!flowDetail) return;
@@ -127,7 +160,6 @@ function FlowModal(props) {
       // });
       return obj;
     });
-    console.log(dataList);
     setStepsData(dataList);
   };
 
@@ -191,7 +223,7 @@ function FlowModal(props) {
     return [
       {
         title: '名称',
-        // width: '33%',
+        width: '25%',
         render: item => (
           <span style={{ color: item.audit_status != 0 ? '#9b9b9b' : '' }}>
             {item.id == version.id && !item.isParent && (
@@ -203,9 +235,31 @@ function FlowModal(props) {
           </span>
         ),
       },
+      {
+        title: '创建人',
+        width: '15%',
+        render: item => {
+          return (
+            item.isParent && (
+              <span>{userList.find(cur => cur.ID == item.author)?.CName || '-'}</span>
+            )
+          );
+        },
+      },
+      {
+        title: '分类',
+        width: '15%',
+        render: item => {
+          return (
+            item.isParent && (
+              <span>{typeOptions.find(cur => cur.id == item.classify_id)?.name}</span>
+            )
+          );
+        },
+      },
       {
         title: '状态',
-        width: '30%',
+        width: '15%',
         render: item => {
           if (!item.flow_id && item.isParent) return;
           let style = { color: getColor(item) };
@@ -232,12 +286,16 @@ function FlowModal(props) {
 
           // 显示拒绝原因
           if (item.audit_comment) {
-            dom = <Popover content={item.audit_comment} title="原因">{txt}</Popover>
+            dom = (
+              <Popover content={item.audit_comment} title="原因">
+                {txt}
+              </Popover>
+            );
           } else {
-            dom = txt
+            dom = txt;
           }
           return item.audit_status != 0 ? (
-            <Button onClick={() => handleChangeClick(item)}>{dom}</Button>
+            <Button onClick={() => onDelVersion(item)}>{dom}</Button>
           ) : (
             <span style={style}>{dom}</span>
           );
@@ -249,24 +307,150 @@ function FlowModal(props) {
         render: item =>
           (item.flow_id || !item.isParent) &&
           item.id != version.id && (
-            <a
-              onClick={() => {
-                console.log(item);
-                onChangeVersion(item);
-                onClose();
-              }}
-            >
-              加载
-            </a>
+            <Space>
+              <a
+                onClick={() => {
+                  console.log(item);
+                  onChangeVersion(item);
+                  onClose();
+                }}
+              >
+                加载
+              </a>
+              {item.audit_status == 0 &&
+              item.author == currentUser.ID && ( //自己创建的&&未提交的清单自己可以删除
+                  <a
+                    onClick={() => {
+                      onDelVersion({ excel_id: item.id });
+                    }}
+                  >
+                    删除
+                  </a>
+                )}
+            </Space>
+          ),
+      },
+    ];
+  }, [version]);
+
+  const childColumns = useMemo(() => {
+    return [
+      {
+        title: '名称',
+        width: '50%',
+        render: item => (
+          <span style={{ color: item.audit_status != 0 ? '#9b9b9b' : '' }}>
+            {item.id == version.id && !item.isParent && (
+              <CheckOutlined style={{ marginRight: 10 }} />
+            )}
+            {item.version_no && !item.children?.length
+              ? `${item.version_name}.${item.version_no}`
+              : item.version_name}
+          </span>
+        ),
+      },
+      {
+        title: '状态',
+        render: item => {
+          if (!item.flow_id && item.isParent) return;
+          let style = { color: getColor(item) };
+          let txt = '';
+          let dom = '';
+          switch (item.audit_status) {
+            case 0:
+              txt = '未提交';
+              break;
+            case 1:
+              txt = '待审批';
+              break;
+            case 2:
+              txt = '已拒绝';
+              break;
+            case 3:
+              txt = '已通过';
+              break;
+            case 4:
+              txt = '已提交';
+              break;
+          }
+          if (item.status == 1) txt = '已失效';
+
+          // 显示拒绝原因
+          if (item.audit_comment) {
+            dom = (
+              <Popover content={item.audit_comment} title="原因">
+                {txt}
+              </Popover>
+            );
+          } else {
+            dom = txt;
+          }
+          return item.audit_status != 0 ? (
+            <Button onClick={() => onDelVersion(item)}>{dom}</Button>
+          ) : (
+            <span style={style}>{dom}</span>
+          );
+        },
+      },
+      {
+        title: '操作',
+        width: '30%',
+        render: item =>
+          (item.flow_id || !item.isParent) &&
+          item.id != version.id && (
+            <Space>
+              <a
+                onClick={() => {
+                  console.log(item);
+                  onChangeVersion(item);
+                  onClose();
+                }}
+              >
+                加载
+              </a>
+              {item.audit_status == 0 &&
+              item.author == currentUser.ID && ( //自己创建的&&未提交的清单自己可以删除
+                  <a
+                    onClick={() => {
+                      onDelVersion({ excel_id: item.id });
+                    }}
+                  >
+                    删除
+                  </a>
+                )}
+            </Space>
           ),
       },
     ];
   }, [version]);
 
-  const onChange = () => {
+  const onChange = value => {
     updateSteps([]);
   };
 
+  useEffect(() => {
+    if (!inputValue) {
+      setShowData(data);
+      return;
+    }
+    let resultData = [...data];
+    switch (selectType) {
+      case SELECT_TYPE.NAME:
+        resultData = data.filter(item => item.version_name.includes(inputValue));
+        break;
+      case SELECT_TYPE.TYPE:
+        const classify = typeOptions.find(item => item.name.includes(inputValue));
+        if (classify) {
+          resultData = data.filter(item => item.classify_id == classify.id);
+        }
+        break;
+      case SELECT_TYPE.CREATOR:
+        resultData = data.filter(item => item.AuthorInfo?.CName.includes(inputValue));
+        break;
+    }
+    setShowData(resultData);
+  }, [inputValue, data]);
+
   return (
     <>
       <Modal
@@ -277,34 +461,61 @@ function FlowModal(props) {
         onCancel={onClose}
         footer={false}
         width="98%"
+        bodyStyle={{ maxHeight: '660px' }}
       >
         <Row gutter={8}>
-          <Col span={16}>
+          <Col span={14}>
             <Flow meta={{ type: 'view' }} flowDetail={graphData} onSelectNode={handleSelectNode} />
           </Col>
-          <Col span={8}>
-            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
-              <div style={{ fontSize: '16px' }}>清单列表</div>
+          <Col span={10}>
+            <div style={{ fontSize: '16px', marginBottom: '10px' }}>清单列表</div>
+            <div style={{ display: 'flex' }}>
+              <Select
+                defaultValue={selectType}
+                style={{ width: '20%' }}
+                onChange={value => {
+                  setSelectType(value);
+                  setInputValue('');
+                }}
+              >
+                <Option value={SELECT_TYPE.NAME}>名称:</Option>
+                <Option value={SELECT_TYPE.TYPE}>分类:</Option>
+                <Option value={SELECT_TYPE.CREATOR}>创建人:</Option>
+              </Select>
+              <Input
+                placeholder="请输入"
+                value={inputValue}
+                onChange={e => setInputValue(e.target.value)}
+              />
               {isOut && (
                 <Button
                   type="primary"
-                  style={{ float: 'right', marginBottom: '10px' }}
+                  style={{ marginLeft: '20%' }}
                   onClick={() => setVersionVisible(true)}
                 >
                   新建清单
                 </Button>
               )}
             </div>
-
-            <div style={{ width: '100%' }}>
+            <div style={{ width: '100%', marginTop: '10px' }}>
               <Table
-                style={{ maxHeight: '90%' }}
                 columns={columns}
-                dataSource={data}
+                dataSource={showData} //data
                 loading={nodeLoading}
                 bordered={false}
-                pagination={{ pageSize: 8, onChange }}
-                scroll={{ y: 65 * pageSize }}
+                pagination={{ position: ['none', 'none'], pageSize: 999, onChange }}
+                scroll={{ y: '460px' }}
+                // childrenColumnName="none"
+                // expandable={{
+                //   expandedRowRender: record => (
+                //     <Table
+                //       columns={columns}
+                //       dataSource={record.children}
+                //       pagination={{ position: ['none', 'none'] }}
+                //     />
+                //   ),
+                //   rowExpandable: record => record.children?.length > 0,
+                // }}
               />
             </div>
             {stepsData.map((item, idx) => (
@@ -325,12 +536,15 @@ function FlowModal(props) {
         </Row>
       </Modal>
       <VersionModal
+        typeOptions={typeOptions}
         visible={versionVisible}
         onClose={() => setVersionVisible(false)}
         onOk={values => {
-          onCommit?.(values);
-          setVersionVisible(false);
+          onCommit?.(values, null, () => {
+            setVersionVisible(false);
+          });
         }}
+        loading={commitLoading}
       />
     </>
   );
@@ -361,5 +575,6 @@ const getColor = item => {
 export default connect(({ loading, user }) => ({
   loading,
   currentUser: user.currentUser,
+  userList: user.list,
 }))(FlowModal);
 // export default FlowModal;

+ 74 - 9
src/pages/Detail/Index.js

@@ -20,7 +20,12 @@ import CommitAuditModal from './CommitAuditModal';
 import CommentContent from '@/components/CommentContent';
 import MergeModal from './MergeModal';
 import { GetTokenFromUrl, getToken } from '@/utils/utils';
-import { queryDetail, queryDingInstanceExecute } from '@/services/boom';
+import {
+  queryDelPurchaseExcel,
+  queryDelSheetRecord,
+  queryDetail,
+  queryDingInstanceExecute,
+} from '@/services/boom';
 import HistoryDrawer from './HistoryDrawer';
 import AuditFlow from './AuditFlow';
 import { getCurrentUser } from '@/utils/authority';
@@ -45,6 +50,8 @@ function Detail(props) {
     versionTree,
     match: { params },
     instanceDetail,
+    typeOptions,
+    classifyList,
   } = props;
   const [versionTreeVisible, setVersionTreeVisible] = useState(false);
   const [commentVisible, setCommentVisible] = useState(false);
@@ -81,7 +88,6 @@ function Detail(props) {
 
   const projectId = parseInt(params.projectId);
   const templateId = parseInt(params.templateId);
-
   const auditDetail = useMemo(() => {
     let data = {
       processCode: '',
@@ -321,6 +327,14 @@ function Detail(props) {
     });
   };
 
+  const onDelSheet = async id => {
+    const params = {
+      excel_id: version.id,
+      sheet_id: id,
+    };
+    await queryDelSheetRecord(params);
+  };
+
   const onAudit = ({ audit_comment }) => {
     const flowNode = flow.currentNode;
     dispatch({
@@ -678,19 +692,25 @@ function Detail(props) {
     // version.audit_status:4 为副本。不可操作
     if (version.audit_status != 4) {
       //判断权限配置,如果配置了,就指定权限的人可提交,没配置就全部人都可提交
+      //判断分类,这个清单所属分类的操作人可以提交
       const getIsSubmit = () => {
+        let bool = true;
         const nodeId = version.template_node_id;
         if (!flowDetail?.nodes || !nodeId) return;
         const node = flowDetail.nodes.find(item => item.Id == nodeId);
         if (!node || node.name == 'custom-circle') return;
         if (node?.role_list) {
-          return node.role_list
+          bool = node.role_list
             .split(',')
             .some(id => currentUser.roleList?.find(role => role.ID == id));
         }
-        return true;
+        const uidsStr = classifyList.find(item => item.classify_id == version.classify_id)?.uid;
+        if (uidsStr && uidsStr.split(',')?.findIndex(item => item == currentUser.ID) < 0) {
+          bool = false;
+        }
+        return bool;
       };
-      // console.log('是否有权限提交流转   ', getIsSubmit());
+      console.log('是否有权限提交流转   ', getIsSubmit());
       if (getIsSubmit() && version.audit_status != 3)
         menuList.push(<Menu.Item key="commitAudit">提交流转</Menu.Item>);
 
@@ -791,7 +811,7 @@ function Detail(props) {
       type: 'detail/queryFiles',
       payload: {
         // excel_id: id || excelId,
-        excel_id: version.id,
+        excel_id: projectId,
       },
     });
   };
@@ -805,6 +825,9 @@ function Detail(props) {
       headers: {
         'JWT-TOKEN': token,
       },
+      data: {
+        type: 'file',
+      },
       onChange(info) {
         if (info.file.status !== 'uploading') {
           console.log(info.file, info.fileList);
@@ -950,6 +973,28 @@ function Detail(props) {
       callback,
     });
   };
+
+  const onDelVersion = data => {
+    Modal.confirm({
+      title: '提示',
+      content: `是否确认删除清单?`,
+      okText: '确定',
+      cancelText: '取消',
+      onOk: async () => {
+        const res = await queryDelPurchaseExcel(data);
+        if (res.code == 200) {
+          message.success('删除成功');
+          dispatch({
+            type: 'xflow/queryBoomFlowDetail',
+            payload: {
+              id: templateId,
+            },
+          });
+        }
+      },
+    });
+  };
+
   useEffect(() => {
     dispatch({
       type: 'detail/queryProjectRecord',
@@ -966,12 +1011,23 @@ function Detail(props) {
     dispatch({
       type: 'user/getRoleList',
     });
+    // dispatch({
+    //   type: 'user/fetch',
+    // });
     dispatch({
-      type: 'user/fetch',
+      type: 'user/fetchDepV2',
     });
     dispatch({
-      type: 'user/fetchDepV2',
+      type: 'detail/queryClassify',
     });
+
+    dispatch({
+      type: 'detail/queryBindClassify',
+      payload: {
+        project_id: projectId,
+      },
+    });
+
     // dispatch({
     //   type: 'detail/queryListParentByUser',
     //   payload: {
@@ -1077,6 +1133,7 @@ function Detail(props) {
               templateId={templateId}
               getUser={getUser}
               onUpdate={onUpdate}
+              onDelSheet={onDelSheet}
             />
           )}
         </div>
@@ -1121,11 +1178,13 @@ function Detail(props) {
         onOk={downloadExcel}
       />
       <FlowModal
+        typeOptions={typeOptions}
         flowDetail={flowDetail}
         visible={flowVisible}
         onClose={() => setFlowVisible(false)}
         version={version}
         onChangeVersion={version => changeVersion(version)}
+        onDelVersion={onDelVersion}
       />
       <AuditModal
         loading={getLoading()}
@@ -1134,15 +1193,19 @@ function Detail(props) {
         onOk={onAudit}
       />
       <FilesModal
+        projectId={projectId}
+        typeOptions={typeOptions}
         loading={getFilesLoading()}
         visible={fileVisible}
         onClose={() => setFileVisible(false)}
-        uploadProps={getUploadProps()}
+        queryFiles={queryFiles}
+        // uploadProps={getUploadProps()}
         DeleteFile={deleteFile}
         downloadFile={downloadFile}
         data={fileList}
       />
       <VersionModal
+        typeOptions={typeOptions}
         loading={getLoading()}
         visible={versionVisible}
         onClose={() => setVersionVisible(false)}
@@ -1171,5 +1234,7 @@ export default connect(({ detail, user, xflow, loading }) => ({
   roleList: detail.roleList,
   versionList: detail.versionList,
   versionTree: detail.versionTree,
+  typeOptions: detail.typeOptions,
+  classifyList: detail.classifyList,
   loading,
 }))(Detail);

+ 24 - 3
src/pages/Detail/LuckySheet.js

@@ -13,6 +13,8 @@ class LuckySheet extends React.Component {
     super(props);
     this.sheetRef = React.createRef();
     this.luckysheet = null;
+    this.updateTimer = null;
+    this.currentSheetIndex = null;
     this.updateCell = {
       add: [],
       diff: [],
@@ -62,8 +64,9 @@ class LuckySheet extends React.Component {
     return uuid.join('');
   }
   renderSheet(currentData) {
-    const { onClickCell, version, getUser, onUpdate, templateId } = this.props;
+    const { onClickCell, version, getUser, onUpdate, templateId, onDelSheet } = this.props;
     const data = currentData || this.props.data;
+    const _this = this;
     if (!this.luckysheet) {
       clearTimeout(this.renderTimer);
       this.renderTimer = setTimeout(() => {
@@ -90,8 +93,26 @@ class LuckySheet extends React.Component {
           }
         },
         updated(operate) {
-          console.log(operate);
-          onUpdate && onUpdate();
+          if (operate.type == 'datachange') {
+            if(_this.currentSheetIndex != operate.sheetIndex) {
+              _this.currentSheetIndex = operate.sheetIndex
+              return;
+            }
+            clearTimeout(_this.updateTimer);
+            _this.updateTimer = setTimeout(() => {
+              onUpdate.bind(_this)
+              onUpdate();
+            }, 1000);
+          }
+        },
+        sheetActivate: sheet => {
+          console.log(sheet);
+          setTimeout(() => {
+            this.luckysheet.setCellFormat(0, 0, 'bg', '#fff');
+          }, 100);
+        },
+        sheetDeleteAfter: sheet => {
+          onDelSheet && onDelSheet(sheet?.sheet.id);
         },
         sheetActivate: sheet => {
           console.log(sheet);

+ 10 - 3
src/pages/Detail/VersionModal.js

@@ -1,18 +1,18 @@
 import React, { useEffect } from 'react';
-import { Modal, Input, Form } from 'antd';
+import { Modal, Input, Form, Select } from 'antd';
 
 // 新建流程
 function VersionModal(props) {
-  const { visible, onClose, onOk, userList = [], data = {}, loading } = props;
+  const { visible, onClose, onOk, userList = [], data = {}, loading ,typeOptions} = props;
   const [form] = Form.useForm();
   const formLayout = { labelCol: { span: 4 }, wrapperCol: { span: 14 } };
 
   const handleOk = async () => {
     let fieldsValue = await form.validateFields();
+    if (!fieldsValue.classify_id) fieldsValue.classify_id = 1;
     fieldsValue.new_version = 0;
     onOk(fieldsValue);
   };
-
   useEffect(() => {
     if (visible) form.resetFields();
   }, [visible]);
@@ -29,6 +29,13 @@ function VersionModal(props) {
         <Form.Item label="名称" name="version_name">
           <Input />
         </Form.Item>
+        <Form.Item label="分类" name="classify_id">
+          <Select
+            defaultValue={typeOptions[0]?.value || 1}
+            options={typeOptions}
+          >
+          </Select>
+        </Form.Item>
         <Form.Item label="详情" name="description">
           <Input.TextArea />
         </Form.Item>

+ 30 - 0
src/pages/Detail/models/detail.js

@@ -35,6 +35,8 @@ import {
   queryDingSchema,
   queryDingInstanceDetail,
   queryListParentByUser,
+  queryClassify,
+  queryBindClassify,
 } from '@/services/boom';
 import { queryRole } from '@/services/SysAdmin';
 import { setCurrentUser } from '@/utils/authority';
@@ -78,6 +80,8 @@ export default {
     versionTree: [],
     auditExcel: [],
     dingInstanceD: [],
+    typeOptions: [],
+    classifyList: [],
   },
 
   effects: {
@@ -561,6 +565,32 @@ export default {
         });
       }
     },
+    *queryClassify({ payload, callback }, { call, put }) {
+      const data = yield call(queryClassify, payload);
+      if (data) {
+        yield put({
+          type: 'save',
+          payload: {
+            typeOptions: data?.map(item => {
+              return { ...item, label: item.name, value: item.id };
+            }),
+          },
+        });
+      }
+    },
+    *queryBindClassify({ payload, callback }, { call, put }) {
+      const data = yield call(queryBindClassify, payload);
+      if (data) {
+        yield put({
+          type: 'save',
+          payload: {
+            classifyList: data?.map(item => {
+              return { ...item, label: item.name, value: item.id };
+            }),
+          },
+        });
+      }
+    },
   },
 
   reducers: {

+ 14 - 10
src/pages/Flow/Audit.js

@@ -22,10 +22,10 @@ function Audit(props) {
     formData,
     flowDetail,
     simpleFlowDteail,
-    currentUser
+    currentUser,
   } = props;
   const ref = useRef();
-  const permission = currentUser.Permission
+  const permission = currentUser.Permission;
 
   const curItem = useMemo(() => {
     let item = localStorage.getItem('currentAudit');
@@ -34,16 +34,16 @@ function Audit(props) {
 
   const editMode = useMemo(() => {
     // 判断是否有权限
-    if(permission['func-01-point-bom-flow']) {
-      return 1
+    if (permission['func-01-point-bom-flow']) {
+      return 1;
     }
     // 判断是否为创建者
-    if(flowDetail.Creator == currentUser.ID) {
-      return 1
+    if (currentUser.IsSuper) {
+      return 1;
     }
 
-    return 2
-  },[permission,flowDetail])
+    return 2;
+  }, [permission, flowDetail]);
 
   useEffect(() => {
     dispatch({
@@ -61,7 +61,7 @@ function Audit(props) {
       type: 'user/fetchDepV2',
     });
   }, []);
-  
+
   const onChange = values => {
     dispatch({
       type: 'xflow/save',
@@ -103,7 +103,11 @@ function Audit(props) {
           <AuditForm value={formData} onChange={values => onChange(values)} />
         </TabPane>
         <TabPane tab="流程控制" key="2">
-          <Flow meta={{ type: 'edit', editMode, flowId: curItem.id }} flowDetail={flowDetail} ref={ref} />
+          <Flow
+            meta={{ type: 'edit', editMode, flowId: curItem.id }}
+            flowDetail={flowDetail}
+            ref={ref}
+          />
         </TabPane>
       </Tabs>
       <Button

+ 24 - 13
src/pages/Flow/Flow.js

@@ -2,7 +2,7 @@ import Flow, { FLOW_TYPE } from '@/components/Flow';
 import { connect } from 'dva';
 import React, { useEffect } from 'react';
 import { UnityAction } from '@/utils/utils';
-import { Button } from 'antd';
+import { Button, Spin } from 'antd';
 import router from 'umi/router';
 
 @connect(({ xflow, user }) => ({
@@ -11,6 +11,11 @@ import router from 'umi/router';
   permission: user.currentUser.Permission,
 }))
 class FlowPage extends React.PureComponent {
+  constructor(props) {
+    super(props);
+    this.state = { spinning: false };
+  }
+
   onUpdate(node) {
     const { dispatch, flowDetail } = this.props;
     let params = {
@@ -31,6 +36,9 @@ class FlowPage extends React.PureComponent {
         nodeId: node.Id,
         body: params,
       },
+      callback: () => {
+        // this.setState({ spinning: false });
+      },
     });
   }
 
@@ -59,6 +67,7 @@ class FlowPage extends React.PureComponent {
 
     UnityAction.on('NODE_SAVE', nodeConfig => {
       this.onUpdate(nodeConfig);
+      // this.setState({ spinning: true });
     });
   }
   componentWillUnmount() {
@@ -67,7 +76,7 @@ class FlowPage extends React.PureComponent {
   getEditMode() {
     const { flowDetail, permission } = this.props;
 
-    return 2
+    return 2;
   }
   render() {
     const { flowDetail, permission, currentUser } = this.props;
@@ -76,24 +85,26 @@ class FlowPage extends React.PureComponent {
     if (
       // 判断是否有权限
       permission['func-01-point-bom-flow'] ||
-      // 判断是否为创建者
-      flowDetail.Creator == currentUser.ID) {
-      editMode = 1
+      // 判断是否为管理员
+      currentUser.IsSuper
+    ) {
+      editMode = 1;
     }
 
     return (
-      <div>
+      <Spin spinning={this.state.spinning}>
         {/* <Form></Form> */}
         <Button style={{ marginBottom: 20 }} onClick={() => router.go(-1)}>
           返回
         </Button>
-        {currentUser.ID && <Flow
-          meta={{ type: 'edit', editMode, flowId: 1 }}
-          flowDetail={flowDetail}
-        // onUpdate={node => this.onUpdate(node)}
-        />}
-
-      </div>
+        {currentUser.ID && (
+          <Flow
+            meta={{ type: 'edit', editMode, flowId: 1 }}
+            flowDetail={flowDetail}
+            // onUpdate={node => this.onUpdate(node)}
+          />
+        )}
+      </Spin>
     );
   }
 }

+ 12 - 5
src/pages/Flow/List.js

@@ -8,7 +8,7 @@ import Link from 'umi/link';
 const { Option } = Select;
 
 function List(props) {
-  const { userList, list, dispatch, projectList, permission } = props;
+  const { userList, list, dispatch, projectList, permission, currentUser } = props;
   const [visible, setVisible] = useState(false);
   const columns = [
     {
@@ -19,9 +19,9 @@ function List(props) {
       title: '所属项目',
       dataIndex: 'ProjectId',
       render: projectId => {
-        let project = projectList.find(item => item.id == projectId)
-        if(project) return `${project.project_name}(${project.project_full_code})`
-      }
+        let project = projectList.find(item => item.id == projectId);
+        if (project) return `${project.project_name}(${project.project_full_code})`;
+      },
     },
     {
       title: '操作',
@@ -40,6 +40,12 @@ function List(props) {
       payload: values,
       callback: () => {
         setVisible(false);
+        dispatch({
+          type: 'flow/queryDefaultBindClassify',
+          payload: {
+            project_id: values.project_id,
+          },
+        });
       },
     });
   };
@@ -66,7 +72,7 @@ function List(props) {
         <Button type="primary" style={{ marginRight: 20 }} onClick={() => setVisible(true)}>
           新增工作流
         </Button>
-        {permission['func-01-point-bom-flow'] && (
+        {(permission['func-01-point-bom-flow'] || currentUser.IsSuper) && (
           <Link to="/home/audit-list">
             <Button type="primary">审批流管理</Button>
           </Link>
@@ -87,6 +93,7 @@ function List(props) {
 export default connect(({ user, flow, loading }) => ({
   userList: user.list,
   permission: user.currentUser.Permission,
+  currentUser: user.currentUser,
   list: flow.flowList,
   projectList: flow.projectList,
   loading: loading.models.purchaseList2,

+ 9 - 1
src/pages/Flow/models/flow.js

@@ -10,6 +10,7 @@ import {
   saveAuditFlowInfo,
   queryDepV2,
   queryProcessFlows,
+  queryDefaultBindClassify,
 } from '@/services/boom';
 import {
   queryApproval,
@@ -195,10 +196,11 @@ export default {
         payload: { flowList: res.data },
       });
     },
-    *updateNode({ payload }, { call, put }) {
+    *updateNode({ payload, callback }, { call, put }) {
       const data = yield call(updateNode, payload);
       console.log(data);
       message.success('修改成功');
+      callback && callback();
     },
     *queryAuditList({ payload }, { call, put }) {
       const response = yield call(queryAuditList, payload);
@@ -271,6 +273,12 @@ export default {
         });
       }
     },
+    *queryDefaultBindClassify({ payload }, { call, put }) {
+      const data = yield call(queryDefaultBindClassify, payload);
+      if (data) {
+       
+      }
+    },
   },
 
   reducers: {

+ 73 - 0
src/pages/List/ClassifyModal.js

@@ -0,0 +1,73 @@
+import { Button, List, Modal, Select, Table } from 'antd';
+import TreeSelect from 'rc-tree-select';
+import React, { useEffect, useMemo } from 'react';
+
+const ClassifyModal = ({ data, visible, userList, onClose, handleChange }) => {
+  const { classify = [], project_id } = data;
+  const list = useMemo(() => {
+    return userList.map(item => {
+      return { label: item.CName, value: item.ID };
+    });
+  }, [userList]);
+
+  const onChange = (e, item) => {
+    console.log(e, item);
+    const idx = classify.findIndex(cur => cur.classify_id == item.classify_id);
+    if (idx > -1) {
+      const curClassifyItem = { ...classify[idx], uid: e.join(',') };
+      classify[idx] = curClassifyItem;
+      handleChange({ project_id, classify });
+    }
+  };
+
+  const columns = [
+    {
+      title: '分类名称',
+      width: '50%',
+      render: item => item.name,
+    },
+    {
+      title: '选择操作人',
+      width: '50%',
+      render: item => {
+        return (
+          <Select
+            mode="multiple"
+            allowClear
+            style={{ width: '100%' }}
+            placeholder="选择操作人"
+            defaultValue={item.uid ? item.uid.split(',').map(item => Number(item)) : []}
+            onChange={e => onChange(e, item)}
+            options={list}
+          />
+        );
+      },
+    },
+  ];
+
+  return (
+    <Modal
+      width="60%"
+      // confirmLoading={loading}
+      maskClosable={false}
+      destroyOnClose
+      title="编辑分类"
+      visible={visible}
+      onCancel={onClose}
+      onOk={onClose}
+      footer={[
+        <Button key="submit" type="primary" onClick={onClose}>
+          确定
+        </Button>,
+      ]}
+    >
+      <Table
+        columns={columns}
+        dataSource={classify}
+        pagination={{ position: ['none', 'none'], pageSize: 999 }}
+        scroll={{ y: 500 }}
+      />
+    </Modal>
+  );
+};
+export default ClassifyModal;

+ 77 - 11
src/pages/List/List.js

@@ -1,18 +1,29 @@
 import React, { useState, useEffect } from 'react';
-import { Table} from 'antd';
+import { message, Modal, Table } from 'antd';
 import { connect } from 'dva';
 import router from 'umi/router';
 import FlowModal from '../Detail/FlowModal';
-import { queryBoomFlowDetail, queryRecordSheet } from '@/services/boom';
+import {
+  queryAddBindClassify,
+  queryBindClassify,
+  queryBoomFlowDetail,
+  queryDelPurchaseExcel,
+  queryRecordSheet,
+} from '@/services/boom';
 import { getToken } from '@/utils/utils';
+import ClassifyModal from './ClassifyModal';
 
 function List(props) {
-  const { excel, loading, project, dispatch, versionList } = props;
+  const { excel, loading, project, dispatch, versionList, typeOptions, userList } = props;
   const [flowVisible, setFlowVisible] = useState(false);
   const [version, setVersion] = useState({});
   const [versionVisible, setVersionVisible] = useState(false);
   const [flowDetail, setFlowDetail] = useState();
   const [loading2, setLoading2] = useState(false);
+  const [commitLoading, setCommitLoading] = useState(false);
+  const [visible, setVisible] = useState(false);
+
+  const [data, setData] = useState([]);
 
   let token = getToken();
 
@@ -35,14 +46,28 @@ function List(props) {
       width: '35%',
       render: item => {
         if (!item.is_parent) return '';
-        let p = project.list.find(p => p.id == item.project_id)
-        if(p) return `${p.project_name}(${p.project_full_code})`
-      }
+        let p = project.list.find(p => p.id == item.project_id);
+        if (p) return `${p.project_name}(${p.project_full_code})`;
+      },
     },
     {
       title: '操作',
       render: record => {
-        if (record.is_parent) return null;
+        if (record.is_parent) {
+          return (
+            <a
+              onClick={async () => {
+                try {
+                  const data = await queryBindClassify({ project_id: record.project_id }); //record.project_id
+                  setData({ project_id: record.project_id, classify: data });
+                  setVisible(true);
+                } catch (error) {}
+              }}
+            >
+              编辑
+            </a>
+          );
+        }
         return (
           <a
             onClick={async () => {
@@ -65,7 +90,6 @@ function List(props) {
       },
     },
   ];
-  console.log(project)
   const queryList = page => {
     console.log(page);
     dispatch({
@@ -90,10 +114,15 @@ function List(props) {
     dispatch({
       type: 'list/queryVersionsList',
     });
-
+    dispatch({
+      type: 'user/fetch',
+    });
     dispatch({
       type: 'user/queryDepV2',
     });
+    dispatch({
+      type: 'list/queryClassify',
+    });
   }, []);
 
   const changeVersion = item => {
@@ -108,7 +137,7 @@ function List(props) {
     return !loadingVersion.effects['detail/queryComment'] && loadingVersion.models.detail;
   };
 
-  const onCommit = async (values, id) => {
+  const onCommit = async (values, id, callback) => {
     let currentNode = flowDetail.nodes.find?.(item => item.Id == version.template_node_id);
     let sheets = await queryRecordSheet({ gridKey: version.id, 'JWT-TOKEN': token });
     // if (!currentNode.muti_version) {
@@ -136,6 +165,7 @@ function List(props) {
       data: sheets,
       base_id: version.id,
     };
+    setCommitLoading(true);
     dispatch({
       type: 'list/commitSheet',
       payload: params,
@@ -144,6 +174,30 @@ function List(props) {
         const data = await queryBoomFlowDetail({ id: newVersion.template_id });
         console.log(data);
         setFlowDetail(data);
+        setCommitLoading(false);
+        callback && callback();
+      },
+    });
+  };
+
+  const handleChange = async data => {
+    const res = await queryAddBindClassify(data);
+  };
+
+  const onDelVersion = async data => {
+    Modal.confirm({
+      title: '提示',
+      content: `是否确认删除清单?`,
+      okText: '确定',
+      cancelText: '取消',
+      onOk: async () => {
+        const res = await queryDelPurchaseExcel(data);
+        if (res.code == 200) {
+          message.success('删除成功');
+          const data = await queryBoomFlowDetail({ id: version.template_id });
+          console.log(data);
+          setFlowDetail(data);
+        }
       },
     });
   };
@@ -159,6 +213,7 @@ function List(props) {
         onChange={queryList}
       />
       <FlowModal
+        typeOptions={typeOptions}
         isOut={true}
         flowDetail={flowDetail}
         visible={flowVisible}
@@ -166,15 +221,26 @@ function List(props) {
         version={version}
         onCommit={onCommit}
         onChangeVersion={version => changeVersion(version)}
+        commitLoading={commitLoading}
+        onDelVersion={onDelVersion}
+      />
+      <ClassifyModal
+        visible={visible}
+        data={data}
+        userList={userList}
+        onClose={() => setVisible(false)}
+        handleChange={handleChange}
       />
     </div>
   );
 }
 
-export default connect(({ list, loading, detail }) => ({
+export default connect(({ list, loading, detail, user }) => ({
   excel: list.excel,
   project: list.project,
   loading: loading.models.list,
   loadingVersion: loading,
   versionList: list.versionList,
+  typeOptions: list.typeOptions,
+  userList: user.list,
 }))(List);

+ 24 - 1
src/pages/List/models/list.js

@@ -1,4 +1,4 @@
-import { queryProjectRecord } from '@/services/boom';
+import { queryBindClassify, queryClassify, queryProjectRecord } from '@/services/boom';
 import { queryProject } from '@/services/PurchaseList';
 import {
   queryApproval,
@@ -18,6 +18,7 @@ export default {
       pagination: false,
     },
     versionList: [],
+    typeOptions:[ ],
   },
 
   effects: {
@@ -96,6 +97,28 @@ export default {
         callback && callback(response.data?.all);
       }
     },
+    *queryClassify ({ payload, callback }, { call, put }) {
+      const data = yield call(queryClassify, payload);
+      if (data) {
+         yield put({
+          type: 'save',
+          payload: {
+            typeOptions: data?.map(item=>{return {...item, label:item.name, value:item.id}}),
+          },
+        });
+      }
+    },
+    *queryBindClassify({ payload, callback }, { call, put }) {
+      const data = yield call(queryBindClassify, payload);
+      if (data) {
+         yield put({
+          type: 'save',
+          payload: {
+            typeOptions: data?.map(item=>{return {...item, label:item.name, value:item.id}}),
+          },
+        });
+      }
+    },
   },
 
   reducers: {

+ 46 - 0
src/services/boom.js

@@ -13,6 +13,14 @@ import { stringify } from 'qs';
 export async function queryRecord(params) {
   return request(`/purchase/record?${stringify(params)}`);
 }
+//删除excel中单个sheet页
+export async function queryDelSheetRecord(params) {
+  const response = await request(`/purchase/bom/del-purchase-excel-sheet?${stringify(params)}`);
+  if (response.code == 200) {
+    message.success('删除成功');
+  }
+}
+
 // 查询全部工作流
 export async function queryFlowList(params) {
   return request(`/purchase/bom/flows?${stringify(params)}`);
@@ -395,3 +403,41 @@ export async function queryUserListByRoleID(params) {
   });
   return res.data;
 }
+
+//新增工作流时调用接口 给项目绑定默认分类列表
+//purchase/bom/default-bind-classify?project_id=1
+export async function queryDefaultBindClassify(params) {
+  let res = await request(`/purchase/bom/default-bind-classify?${stringify(params)}`, {
+    method: 'GET',
+  });
+  return res.data;
+}
+
+//获取分类列表
+export async function queryClassify() {
+  let res = await request(`/purchase/bom/get-classify`, {
+    method: 'GET',
+  });
+  return res.data;
+}
+
+export async function queryBindClassify(params) {
+  let res = await request(`/purchase/bom/get-bind-classify?${stringify(params)}`, {
+    method: 'GET',
+  });
+  return res.data;
+}
+
+export async function queryAddBindClassify(data) {
+  return request(`/purchase/bom/add-bind-classify`, {
+    method: 'POST',
+    body: data,
+  });
+}
+
+export async function queryDelPurchaseExcel(params) {
+  let res = await request(`/purchase/bom/del-purchase-excel?${stringify(params)}`, {
+    method: 'GET',
+  });
+  return res;
+}