方明岳 2 年前
父节点
当前提交
ccb00e5d4f

+ 0
- 5751
package-lock.json
文件差异内容过多而无法显示
查看文件


+ 1
- 0
package.json 查看文件

@@ -11,6 +11,7 @@
11 11
   "dependencies": {
12 12
     "@ant-design/icons": "^4.7.0",
13 13
     "@ant-design/pro-components": "^2.3.13",
14
+    "@wangeditor/editor-for-react": "^1.0.5",
14 15
     "@zjxpcyc/react-tiny-store": "^2.0.1",
15 16
     "antd": "^4.23.4",
16 17
     "axios": "^1.1.2",

+ 78
- 0
src/components/Wangeditor/index.jsx 查看文件

@@ -0,0 +1,78 @@
1
+import React, { useState, useEffect } from "react";
2
+import "@wangeditor/editor/dist/css/style.css";
3
+import { Editor, Toolbar } from "@wangeditor/editor-for-react";
4
+
5
+function MyEditor(props) {
6
+  const {
7
+    value,
8
+    onChange = (e) => {
9
+      setHtml(e);
10
+    },
11
+    toolbarConfig = {
12
+      excludeKeys: [
13
+        "insertImage",
14
+        "viewImageLink",
15
+        "editImage",
16
+        "uploadImage",
17
+        "uploadVideo",
18
+      ],
19
+    },
20
+    editorConfig = {
21
+      placeholder: "请输入内容...",
22
+    },
23
+    readonly = false,
24
+  } = props;
25
+  const [editor, setEditor] = useState(null); // 存储 editor 实例
26
+  const [html, setHtml] = useState("");
27
+  console.log(props, "MyEditor");
28
+  // 模拟 ajax 请求,异步设置 html
29
+  useEffect(() => {
30
+    console.log(editor     );
31
+
32
+    setHtml(value);
33
+  }, [value]);
34
+
35
+  // 及时销毁 editor
36
+  useEffect(() => {
37
+    return () => {
38
+      if (editor == null) return;
39
+      editor.destroy();
40
+      setEditor(null);
41
+    };
42
+  }, [editor]);
43
+
44
+  function insertText() {
45
+    if (editor == null) return;
46
+    editor.insertText(" hello ");
47
+  }
48
+
49
+  function printHtml() {
50
+    if (editor == null) return;
51
+    console.log(editor.getHtml());
52
+  }
53
+
54
+  return !readonly ? (
55
+    <>
56
+      <div style={{ border: "1px solid #ccc", zIndex: 100, marginTop: "15px" }}>
57
+        <Toolbar
58
+          editor={editor}
59
+          defaultConfig={toolbarConfig}
60
+          mode="default"
61
+          style={{ borderBottom: "1px solid #ccc" }}
62
+        />
63
+        <Editor
64
+          defaultConfig={editorConfig}
65
+          value={html}
66
+          onCreated={setEditor}
67
+          onChange={(editor) => onChange(editor.getHtml())}
68
+          mode="default"
69
+          style={{ height: "500px" }}
70
+        />
71
+      </div>
72
+    </>
73
+  ) : (
74
+    <div dangerouslySetInnerHTML={{ __html: value }}></div>
75
+  );
76
+}
77
+
78
+export default MyEditor;

+ 1
- 1
src/layouts/AuthLayout/components/Container.jsx 查看文件

@@ -1,5 +1,5 @@
1 1
 import React, { useEffect, useRef, useMemo, useState } from 'react';
2
-import { Layout, Spin } from 'antd';
2
+import { Layout } from 'antd';
3 3
 import { Outlet } from "react-router-dom";
4 4
 import PageTransition from './PageTransition';
5 5
 import Footer from './Footer';

+ 3
- 19
src/layouts/AuthLayout/components/Header/SplitMenu.jsx 查看文件

@@ -1,28 +1,12 @@
1 1
 import React from 'react';
2 2
 import { Menu } from 'antd';
3
-import { useNavigate } from 'react-router-dom';
4 3
 
5 4
 export default (props) => {
6
-  const { items, onChange, location } = props;
5
+  const { items, location } = props;
7 6
 
8
-  const navigate = useNavigate();
9
-  const [selectedKeys, setSelectedKeys] = React.useState([]);
10
-
11
-  const renderItems = React.useMemo(() => items.map(x => ({ ...x, children: undefined })), [items]);
12
-
13
-  const rootPath = React.useMemo(() => `/${location.pathname.split('/')[1]}`, [location.pathname]);
14
-
15
-  const onClick = (item) => {
16
-    onChange(item.key);
17
-    // navigate(item.key);
18
-  };
19
-
20
-  React.useEffect(() => {
21
-    setSelectedKeys([rootPath]);
22
-    onChange(rootPath);
23
-  }, [rootPath]);
7
+  const selectedKeys = React.useMemo(() => `/${location.pathname.split('/')[1]}`, [location.pathname]);
24 8
 
25 9
   return (
26
-    <Menu className='split-menu' mode="horizontal" items={renderItems} selectedKeys={selectedKeys} onClick={onClick} />
10
+    <Menu className='split-menu' mode="horizontal" items={items} selectedKeys={selectedKeys} />
27 11
   )
28 12
 }

+ 2
- 2
src/layouts/AuthLayout/components/Header/index.jsx 查看文件

@@ -10,7 +10,7 @@ import SplitMenu from './SplitMenu';
10 10
 const { Header } = Layout;
11 11
 
12 12
 export default (props) => {
13
-  const { menus, theme, location, onMenuChange } = props;
13
+  const { menus, theme, location } = props;
14 14
 
15 15
   const className = useMemo(() => classNames({
16 16
     'layout-header': true,
@@ -20,7 +20,7 @@ export default (props) => {
20 20
   return (
21 21
     <Header className={className}>
22 22
       <Logo />
23
-      <SplitMenu items={menus} location={location} onChange={onMenuChange} />
23
+      <SplitMenu items={menus} location={location} />
24 24
       <Space>
25 25
         <User />
26 26
         <Exit />

+ 0
- 29
src/layouts/AuthLayout/components/Menus.jsx 查看文件

@@ -1,39 +1,10 @@
1 1
 import React, { useState } from 'react';
2
-import { useNavigate, useLocation } from "react-router-dom";
3 2
 import { Menu } from 'antd';
4 3
 
5
-const linkTo = url => {
6
-  const a = document.createElement('a');
7
-  a.href = item.key;
8
-  a.target = '_blank';
9
-  a.click();
10
-}
11
-
12 4
 const menuStyle = { height: '100%' };
13 5
 
14 6
 export default (props) => {
15 7
   const { theme, items, location } = props;
16
-  // const [selectedKeys, setSelectedKeys] = useState([]);
17
-
18
-  const navigate = useNavigate();
19
-
20
-  const onClick = (item) => {
21
-    // console.log(item);
22
-
23
-    if (item.key.indexOf('http') === 0) {
24
-      // http 开头说明是外部链接
25
-      linkTo(item.key);
26
-    } else {
27
-      // setSelectedKeys([item.key]);
28
-      navigate(item.key);
29
-    }
30
-  }
31
-
32
-  // React.useEffect(() => {
33
-  //   if (location.pathname) {
34
-  //     setSelectedKeys([location.pathname]);
35
-  //   }
36
-  // }, [location.pathname]);
37 8
 
38 9
   return (
39 10
     <Menu style={menuStyle} theme={theme} items={items} selectedKeys={[location.pathname]} />

+ 3
- 5
src/layouts/AuthLayout/components/RequireLogin.jsx 查看文件

@@ -1,16 +1,14 @@
1 1
 import React, { useState, useEffect } from 'react';
2
-import { useLocation, Navigate } from "react-router-dom";
2
+import { Navigate } from "react-router-dom";
3 3
 import { useModel } from '@/store';
4
-import { queryCurrentUser } from '@/services/user';
5 4
 
6 5
 export default (props) => {
7
-  const { user, setUser } = useModel('user');
6
+  const { user, getCurrentUser } = useModel('user');
8 7
   const [userStatus, setUserStatus] = useState(user && user.id ? 1 : 0);
9 8
 
10 9
   useEffect(() => {
11 10
     if (!user || !user.id) {
12
-      queryCurrentUser().then((res) => {
13
-        setUser(res);
11
+      getCurrentUser().then(() => {
14 12
         setUserStatus(1);
15 13
       }).catch(() => {
16 14
         setUserStatus(-1);

+ 8
- 13
src/layouts/AuthLayout/index.jsx 查看文件

@@ -2,7 +2,7 @@ import React, { useEffect, useRef, useMemo, useState } from 'react';
2 2
 import { Layout, Spin } from 'antd';
3 3
 import { useLocation, Outlet } from "react-router-dom";
4 4
 import { useModel } from '@/store';
5
-import { getItems } from '@/routes/menus';
5
+import { getMenuItems } from '@/routes/menus';
6 6
 import useRoute from '@/utils/hooks/useRoute';
7 7
 import RequireLogin from './components/RequireLogin';
8 8
 import SiderBar from './components/SiderBar';
@@ -14,20 +14,15 @@ import './style.less';
14 14
 
15 15
 export default (props) => {
16 16
   const { theme } = useModel('system');
17
-  const { user } = useModel('user');
18
-
19
-  const allMenus = useMemo(() => getItems(), []);
20
-  const [siderMenus, setSiderMenus] = useState([]);
21
-
17
+  const { user, menus } = useModel('user');
22 18
   const location = useLocation();
23 19
   const currentRoute = useRoute();
24 20
 
25
-  const onSplitMenuChange = (key) => {
26
-    const target = allMenus.filter(x => x.key === key)[0];
27
-    if (target) {
28
-      setSiderMenus(target.children || []);
29
-    }
30
-  }
21
+  const splitMenus = useMemo(() => menus.map(x => ({ ...x, children: undefined })), [menus]);
22
+  const siderMenus = useMemo(() => {
23
+    const target = menus.filter(x => location.pathname.indexOf(x.key) === 0)[0];
24
+    return target ? target.children :  [];
25
+  }, [menus, location.pathname]);
31 26
 
32 27
   return (
33 28
     <Spin spinning={!user} size="large">
@@ -38,7 +33,7 @@ export default (props) => {
38 33
             ? <Outlet />
39 34
             : (
40 35
               <Layout style={{ minHeight: '100vh' }}>
41
-                <Header theme={theme} menus={allMenus} location={location} onMenuChange={onSplitMenuChange} />
36
+                <Header theme={theme} menus={splitMenus} location={location} />
42 37
                 <Layout>
43 38
                   <SiderBar theme={theme} menus={siderMenus} location={location} />
44 39
                   <Container location={location} />

+ 5
- 3
src/layouts/Container.jsx 查看文件

@@ -1,4 +1,5 @@
1 1
 import React from 'react';
2
+import { Typography } from 'antd';
2 3
 import { Outlet } from 'react-router-dom';
3 4
 import useRoute from '@/utils/hooks/useRoute';
4 5
 
@@ -6,14 +7,15 @@ const containerStyle = {
6 7
   margin: '24px 24px 0 24px',
7 8
   height: '100%',
8 9
 }
10
+const { Title } = Typography;
9 11
 
10 12
 export default (props) => {
11
-
12
-  const currentRoute = useRoute();
13
-  const style = currentRoute.meta && currentRoute.meta.noLayout ? { height: '100%' } : containerStyle;
13
+  const { meta = {} } = useRoute() || {};
14
+  const style = meta.noLayout ? { height: '100%' } : containerStyle;
14 15
 
15 16
   return (
16 17
     <div style={style}>
18
+      {/* { meta.title && !meta.noLayout && <Title level={4} style={{ paddingBottom: '12px' }}>{ meta.title }</Title> } */}
17 19
       <Outlet />
18 20
     </div>
19 21
   )

+ 123
- 0
src/pages/cms/emergencyPlan/edit/index.jsx 查看文件

@@ -0,0 +1,123 @@
1
+import { savePosts, getPostsDetail, updatePosts } from "@/services/posts";
2
+import { getStoreList } from "@/services/stock";
3
+import {
4
+  PageContainer,
5
+  ProForm,
6
+  ProFormSelect,
7
+  ProFormText,
8
+} from "@ant-design/pro-components";
9
+import { useNavigate, useSearchParams } from "react-router-dom";
10
+import { Card, Col, message, Row, Space } from "antd";
11
+import { useEffect, useRef, useState } from "react";
12
+import Wangeditor from "@/components/Wangeditor";
13
+
14
+export default (props) => {
15
+  const [searchParams] = useSearchParams();
16
+  const id = searchParams.get("id");
17
+  // const [foodDict, setFoodDict] = useState([]);
18
+  const navigate = useNavigate();
19
+
20
+  const formRef = useRef();
21
+  // useEffect(() => {
22
+  //   getStoreList({ isDish: 1, pageSize: 9999 }).then((res) => {
23
+  //     setFoodDict(
24
+  //       res?.records?.map((x) => ({
25
+  //         label: x.name,
26
+  //         value: x.id,
27
+  //       }))
28
+  //     );
29
+  //   });
30
+  // }, [id]);
31
+
32
+  useEffect(() => {
33
+    if (id) {
34
+      getPostsDetail(id).then((res) => {
35
+        formRef.current.setFieldsValue(res);
36
+      });
37
+    }
38
+  }, [id]);
39
+
40
+  const onFinish = async (values) => {
41
+    console.log(values, "===");
42
+
43
+    if (id) {
44
+      updatePosts(id, { ...values, status: Number(values.status) }).then(
45
+        (res) => {
46
+          navigate(-1);
47
+        }
48
+      );
49
+    } else {
50
+      savePosts({ ...values, status: Number(values.status) }).then((res) => {
51
+        navigate(-1);
52
+      });
53
+    }
54
+
55
+    return false;
56
+  };
57
+
58
+  return (
59
+    <PageContainer>
60
+      <Card>
61
+        <ProForm
62
+          //  readonly
63
+          formRef={formRef}
64
+          layout={"horizontal"}
65
+          labelCol={{ span: 8 }}
66
+          wrapperCol={{ span: 12 }}
67
+          onFinish={onFinish}
68
+          initialValues={{ status: 0 }}
69
+          submitter={{
70
+            searchConfig: {
71
+              resetText: "返回",
72
+            },
73
+            onReset: () => navigate(-1),
74
+            render: (props, doms) => {
75
+              return (
76
+                <Row>
77
+                  <Col span={8} offset={8}>
78
+                    <Space>{doms}</Space>
79
+                  </Col>
80
+                </Row>
81
+              );
82
+            },
83
+          }}
84
+        >
85
+          <ProFormText
86
+            name="title"
87
+            label="内容名称"
88
+            placeholder="请输入内容名称"
89
+            rules={[{ required: true, message: "请输入内容名称" }]}
90
+            width={460}
91
+          />
92
+
93
+          <ProForm.Item
94
+            name="detail"
95
+            label="发布详情"
96
+            placeholder="请输入发布详情"
97
+            rules={[{ required: true, message: "请输入发布详情" }]}
98
+          >
99
+            <Wangeditor></Wangeditor>
100
+          </ProForm.Item>
101
+
102
+          <ProFormSelect
103
+            name="status"
104
+            label="状态"
105
+            placeholder="请选择"
106
+            rules={[{ required: true, message: "请选择状态" }]}
107
+            width={460}
108
+            options={[
109
+              {
110
+                value: 0,
111
+                label: "未发布",
112
+              },
113
+              {
114
+                value: 1,
115
+                label: "已发布",
116
+              },
117
+            ]}
118
+          />
119
+        </ProForm>
120
+      </Card>
121
+    </PageContainer>
122
+  );
123
+};

+ 113
- 0
src/pages/cms/emergencyPlan/list/index.jsx 查看文件

@@ -0,0 +1,113 @@
1
+import { getPostsList } from "@/services/posts";
2
+import { queryTable } from "@/utils/request";
3
+import { PageContainer, ProTable } from "@ant-design/pro-components";
4
+import { useNavigate } from "react-router-dom";
5
+import { Button, message, Popconfirm } from "antd";
6
+import { useRef, useState, useEffect } from "react";
7
+import { floatMultiply, floatDivide } from "@/utils";
8
+
9
+// type plan 采购计划  bill 采购账单  inStore  采购入库
10
+const StockList = (props) => {
11
+  const { type } = props;
12
+  const [showDetail, setShowDetail] = useState(false);
13
+  const [activeKey, setActiveKey] = useState("");
14
+  const actionRef = useRef();
15
+  const navigate = useNavigate();
16
+  // console.log(props, "props");
17
+
18
+  useEffect(() => {
19
+    actionRef.current.reload();
20
+  }, [type]);
21
+  const handleDelete = (id) => {
22
+    if (id) {
23
+      deletePurchase(id).then((res) => {
24
+        actionRef.current.reload();
25
+      });
26
+    }
27
+  };
28
+
29
+  const columns = [
30
+    {
31
+      title: "内容名称",
32
+      dataIndex: "title",
33
+    },
34
+
35
+    {
36
+      title: "发布内容",
37
+      dataIndex: "detail",
38
+      search: false,
39
+      width:'50%'
40
+    },
41
+    {
42
+      title: "状态",
43
+      dataIndex: "status",
44
+      valueType: 'select',
45
+      valueEnum:{
46
+        0: { text: '未发布', status: 'Error' },
47
+        1: { text: '已发布', status: 'Processing' }, 
48
+      },
49
+    
50
+    //   const valueEnum = {
51
+    //     all: { text: '全部', status: 'Default' },
52
+    //     running: { text: '运行中', status: 'Processing' },
53
+    //     online: { text: '已上线', status: 'Success' },
54
+    //     error: { text: '异常', status: 'Error' },
55
+    //   };
56
+    },
57
+
58
+    {
59
+      title: "操作",
60
+      valueType: "option",
61
+      width: 200,
62
+      render: (_, record) => [
63
+        <Button
64
+          key={2}
65
+          style={{ padding: 0 }}
66
+          type="link"
67
+          onClick={() => {
68
+            navigate(`/cms/emergency-plan/edit?id=${record.id}`);
69
+          }}
70
+        >
71
+          修改
72
+        </Button>,
73
+        <Popconfirm
74
+          key={3}
75
+          title="您是否确认删除 ?"
76
+          onConfirm={() => handleDelete(record.id)}
77
+          okText="确定"
78
+          cancelText="取消"
79
+        >
80
+          {/* manualPush */}
81
+          <Button style={{ padding: 0 }} type="link">
82
+            删除
83
+          </Button>
84
+        </Popconfirm>,
85
+      ],
86
+    },
87
+  ];
88
+
89
+  return (
90
+    <PageContainer>
91
+      <ProTable
92
+        actionRef={actionRef}
93
+        rowKey="id"
94
+        toolBarRender={() => [
95
+          <Button
96
+            key="2"
97
+            type="primary"
98
+            onClick={() => {
99
+              navigate("/cms/emergency-plan/edit");
100
+            }}
101
+          >
102
+            新增
103
+          </Button>,
104
+        ]}
105
+        // search={false}
106
+        request={queryTable(getPostsList)}
107
+        columns={columns}
108
+      />
109
+    </PageContainer>
110
+  );
111
+};
112
+
113
+export default StockList;

+ 213
- 0
src/pages/purchase/bill/edit/index.jsx 查看文件

@@ -0,0 +1,213 @@
1
+import {
2
+  savePurchase,
3
+  updatePurchase,
4
+  getPurchaseDetail,
5
+} from "@/services/purchase";
6
+import { getStoreList } from "@/services/stock";
7
+import {
8
+  PageContainer,
9
+  ProForm,
10
+  ProFormSelect,
11
+  ProFormText,
12
+  ProFormDigit,
13
+} from "@ant-design/pro-components";
14
+import { useNavigate, useSearchParams } from "react-router-dom";
15
+import { Card, Col, message, Row, Space, Form, Button } from "antd";
16
+import { useEffect, useRef, useState } from "react";
17
+import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons";
18
+import { floatMultiply, floatDivide } from "@/utils";
19
+
20
+export default (props) => {
21
+  const [searchParams] = useSearchParams();
22
+  const id = searchParams.get("id");
23
+  const [storeList, setStoreList] = useState([]);
24
+  const [isCompleted, setIsCompleted] = useState(false);
25
+  const navigate = useNavigate();
26
+  
27
+  const formRef = useRef();
28
+  useEffect(() => {
29
+    getStoreList({ pageSize: 9999 }).then((res) => {
30
+      setStoreList(
31
+        res?.records?.map((x) => ({
32
+          label: x.name,
33
+          value: x.id,
34
+        }))
35
+      );
36
+    });
37
+  }, []);
38
+
39
+  useEffect(() => {
40
+    if (id) {
41
+      getPurchaseDetail(id).then((res) => {
42
+        setIsCompleted(res.isCompleted)
43
+        formRef.current.setFieldsValue({
44
+          ...res,
45
+          itemsList: res.itemsList?.map((x) => ({
46
+            ...x,
47
+            planUnitPrice: floatDivide(x.planUnitPrice, 100),
48
+            actUnitPrice: floatDivide(x.actUnitPrice, 100),
49
+          })),
50
+        });
51
+      });
52
+    }
53
+  }, [id]);
54
+
55
+  const onFinish = async (values) => {
56
+    console.log(values, "===");
57
+
58
+    let data = {
59
+      id: id ? Number(id) : null,
60
+      ...values,
61
+      itemsList: values.itemsList?.map((x) => ({
62
+        ...x,
63
+        planUnitPrice: floatMultiply(x.planUnitPrice, 100),
64
+        actUnitPrice: floatMultiply(x.actUnitPrice, 100),
65
+      })),
66
+    };
67
+
68
+    savePurchase(data).then((res) => {
69
+      // message.success("添加成功");
70
+      navigate(-1);
71
+    });
72
+
73
+    return false;
74
+  };
75
+
76
+  const onBillFinish = ({ form }) => {
77
+    form.validateFields().then((res) => {
78
+      console.log(res, "===--");
79
+      onFinish({ ...res, isCompleted: true });
80
+    });
81
+  };
82
+
83
+  return (
84
+    <PageContainer>
85
+      <Card>
86
+        <ProForm
87
+          formRef={formRef}
88
+          layout={"horizontal"}
89
+          labelCol={{ span: 2 }}
90
+          wrapperCol={{ span: 8 }}
91
+          onFinish={onFinish}
92
+          submitter={{
93
+            searchConfig: {
94
+              resetText: "返回",
95
+            },
96
+            onReset: () => navigate(-1),
97
+            render: (props, doms) => {
98
+              console.log(props, doms, "renderprops");
99
+              return (
100
+                <Row>
101
+                  <Col span={8} offset={2}>
102
+                    <Space>
103
+                      {doms?.map((x, index) => {
104
+                        if (index === 1 && isCompleted) {
105
+                          return null;
106
+                        }
107
+                        return x;
108
+                      })}
109
+                      {id&&!isCompleted && (
110
+                        <Button
111
+                          type="primary"
112
+                          onClick={() => onBillFinish(props)}
113
+                        >
114
+                          采购完成
115
+                        </Button>
116
+                      )}
117
+                    </Space>
118
+                  </Col>
119
+                </Row>
120
+              );
121
+            },
122
+          }}
123
+        >
124
+          <ProFormText
125
+            name="title"
126
+            label="采购计划"
127
+            placeholder="请输入采购计划"
128
+            disabled
129
+            width={480}
130
+          />
131
+          <ProFormText
132
+            name="planDate"
133
+            label="计划时间"
134
+            placeholder="请输入计划时间"
135
+            disabled
136
+            width={480}
137
+          />
138
+          <Form.Item
139
+            label="采购清单"
140
+            labelCol={{ span: 2 }}
141
+            wrapperCol={{ span: 22 }}
142
+          >
143
+            <Form.List name="itemsList">
144
+              {(fields, { add, remove }) => (
145
+                <>
146
+                  {fields.map(({ key, name, ...restField }) => (
147
+                    <Space
148
+                      key={key}
149
+                      style={{ display: "flex" }}
150
+                      align="baseline"
151
+                    >
152
+                      <ProFormSelect
153
+                        {...restField}
154
+                        name={[name, "storeId"]}
155
+                        label="库存名称"
156
+                        placeholder="请选择库存名称"
157
+                        disabled
158
+                        options={storeList}
159
+                      />
160
+
161
+                      <ProFormDigit
162
+                        {...restField}
163
+                        name={[name, "planAmount"]}
164
+                        label="计划数量"
165
+                        placeholder="请输入计划数量"
166
+                        disabled
167
+                        width={100}
168
+                        fieldProps={{
169
+                          precision: 10,
170
+                          formatter: (value) => value,
171
+                        }}
172
+                      />
173
+                      <ProFormDigit
174
+                        {...restField}
175
+                        name={[name, "planUnitPrice"]}
176
+                        label="预估单价"
177
+                        placeholder="请输入预估单价"
178
+                        disabled
179
+                        fieldProps={{ precision: 2, prefix: "¥" }}
180
+                        width={100}
181
+                      />
182
+                      <ProFormDigit
183
+                        {...restField}
184
+                        name={[name, "actAmount"]}
185
+                        label="采购数量"
186
+                        placeholder="请输入采购数量"
187
+                        rules={[{ required: true, message: "请输入采购数量" }]}
188
+                        width={100}
189
+                        fieldProps={{
190
+                          precision: 10,
191
+                          formatter: (value) => value,
192
+                        }}
193
+                      />
194
+                      <ProFormDigit
195
+                        {...restField}
196
+                        name={[name, "actUnitPrice"]}
197
+                        label="采购单价"
198
+                        placeholder="请输入采购单价"
199
+                        rules={[{ required: true, message: "请输入采购单价" }]}
200
+                        fieldProps={{ precision: 2, prefix: "¥" }}
201
+                        width={100}
202
+                      />
203
+                    </Space>
204
+                  ))}
205
+                </>
206
+              )}
207
+            </Form.List>
208
+          </Form.Item>
209
+        </ProForm>
210
+      </Card>
211
+    </PageContainer>
212
+  );
213
+};

+ 197
- 0
src/pages/purchase/inStore/edit/index.jsx 查看文件

@@ -0,0 +1,197 @@
1
+import {
2
+  savePurchaseInStore,
3
+  getPurchaseDetail,
4
+} from "@/services/purchase";
5
+import { getStoreList } from "@/services/stock";
6
+import {
7
+  PageContainer,
8
+  ProForm,
9
+  ProFormSelect,
10
+  ProFormText,
11
+  ProFormDigit,
12
+} from "@ant-design/pro-components";
13
+import { useNavigate, useSearchParams } from "react-router-dom";
14
+import { Card, Col, message, Row, Space, Form, Button } from "antd";
15
+import { useEffect, useRef, useState } from "react";
16
+import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons";
17
+import { floatMultiply, floatDivide } from "@/utils";
18
+
19
+export default (props) => {
20
+  const [searchParams] = useSearchParams();
21
+  const id = searchParams.get("id");
22
+  const [storeList, setStoreList] = useState([]);
23
+  const [isInStore, setIsInStore] = useState(false);
24
+  const navigate = useNavigate();
25
+  
26
+  const formRef = useRef();
27
+  useEffect(() => {
28
+    getStoreList({ pageSize: 9999 }).then((res) => {
29
+      setStoreList(
30
+        res?.records?.map((x) => ({
31
+          label: x.name,
32
+          value: x.id,
33
+        }))
34
+      );
35
+    });
36
+  }, []);
37
+
38
+  useEffect(() => {
39
+    if (id) {
40
+      getPurchaseDetail(id).then((res) => {
41
+        setIsInStore(res.isInStore)
42
+        formRef.current.setFieldsValue({
43
+          ...res,
44
+          itemsList: res.itemsList?.map((x) => ({
45
+            ...x,
46
+            planUnitPrice: x.planUnitPrice
47
+              ? floatDivide(x.planUnitPrice, 100)
48
+              : null,
49
+            actUnitPrice: x.actUnitPrice
50
+              ? floatDivide(x.actUnitPrice, 100)
51
+              : null,
52
+          })),
53
+        });
54
+      });
55
+    }
56
+  }, [id]);
57
+
58
+  const onFinish = async (values) => {
59
+    console.log(values, "===");
60
+
61
+
62
+
63
+    savePurchaseInStore(Number(id)).then((res) => {
64
+      // message.success("添加成功");
65
+      navigate(-1);
66
+    });
67
+
68
+    return false;
69
+  };
70
+
71
+
72
+
73
+  return (
74
+    <PageContainer>
75
+      <Card>
76
+        <ProForm
77
+        
78
+          formRef={formRef}
79
+          layout={"horizontal"}
80
+          labelCol={{ span: 2 }}
81
+          wrapperCol={{ span: 8 }}
82
+          onFinish={onFinish}
83
+          submitter={{
84
+            searchConfig: {
85
+              resetText: "返回",
86
+              submitText:'入库'
87
+            },
88
+            onReset: () => navigate(-1),
89
+            render: (props, doms) => {
90
+              console.log(props, doms, "renderprops");
91
+              return (
92
+                <Row>
93
+                  <Col span={8} offset={2}>
94
+                    <Space>
95
+                      {doms?.map((x, index) => {
96
+                        if (index === 1 && isInStore) {
97
+                          return null;
98
+                        }
99
+                        return x;
100
+                      })}
101
+                    </Space>
102
+                  </Col>
103
+                </Row>
104
+              );
105
+            },
106
+          }}
107
+        >
108
+          <ProFormText
109
+            name="title"
110
+            label="采购计划"
111
+            placeholder="请输入采购计划"
112
+            disabled
113
+            width={480}
114
+          />
115
+          <ProFormText
116
+            name="planDate"
117
+            label="计划时间"
118
+            placeholder="请输入计划时间"
119
+            disabled
120
+            width={480}
121
+          />
122
+          <Form.Item
123
+            label="采购清单"
124
+            labelCol={{ span: 2 }}
125
+            wrapperCol={{ span: 22 }}
126
+          >
127
+            <Form.List name="itemsList">
128
+              {(fields, { add, remove }) => (
129
+                <>
130
+                  {fields.map(({ key, name, ...restField }) => (
131
+                    <Space
132
+                      key={key}
133
+                      style={{ display: "flex" }}
134
+                      align="baseline"
135
+                    >
136
+                      <ProFormSelect
137
+                        {...restField}
138
+                        name={[name, "storeId"]}
139
+                        label="库存名称"
140
+                        placeholder="请选择库存名称"
141
+                        disabled
142
+                        options={storeList}
143
+                      />
144
+
145
+                      <ProFormDigit
146
+                        {...restField}
147
+                        name={[name, "planAmount"]}
148
+                        label="计划数量"
149
+                        placeholder="请输入计划数量"
150
+                        disabled
151
+                        width={100}
152
+                        fieldProps={{
153
+                          precision: 10,
154
+                          formatter: (value) => value,
155
+                        }}
156
+                      />
157
+                      <ProFormDigit
158
+                        {...restField}
159
+                        name={[name, "planUnitPrice"]}
160
+                        label="预估单价"
161
+                        placeholder="请输入预估单价"
162
+                        disabled
163
+                        fieldProps={{ precision: 2, prefix: "¥" }}
164
+                        width={100}
165
+                      />
166
+                      <ProFormDigit
167
+                        {...restField}
168
+                        name={[name, "actAmount"]}
169
+                        label="采购数量"
170
+                        placeholder="请输入采购数量"
171
+                        disabled
172
+                        width={100}
173
+                        fieldProps={{
174
+                          precision: 10,
175
+                          formatter: (value) => value,
176
+                        }}
177
+                      />
178
+                      <ProFormDigit
179
+                        {...restField}
180
+                        name={[name, "actUnitPrice"]}
181
+                        label="采购单价"
182
+                        placeholder="请输入采购单价"
183
+                        disabled
184
+                        fieldProps={{ precision: 2, prefix: "¥" }}
185
+                        width={100}
186
+                      />
187
+                    </Space>
188
+                  ))}
189
+                </>
190
+              )}
191
+            </Form.List>
192
+          </Form.Item>
193
+        </ProForm>
194
+      </Card>
195
+    </PageContainer>
196
+  );
197
+};

+ 190
- 0
src/pages/purchase/plan/edit/index.jsx 查看文件

@@ -0,0 +1,190 @@
1
+import {
2
+  savePurchase,
3
+  updatePurchase,
4
+  getPurchaseDetail,
5
+} from "@/services/purchase";
6
+import { getStoreList } from "@/services/stock";
7
+import {
8
+  PageContainer,
9
+  ProForm,
10
+  ProFormSelect,
11
+  ProFormText,
12
+  ProFormDigit,
13
+} from "@ant-design/pro-components";
14
+import { useNavigate, useSearchParams } from "react-router-dom";
15
+import { Card, Col, message, Row, Space, Form, Button } from "antd";
16
+import { useEffect, useRef, useState } from "react";
17
+import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons";
18
+import { floatMultiply, floatDivide } from "@/utils";
19
+
20
+export default (props) => {
21
+  const [searchParams] = useSearchParams();
22
+  const id = searchParams.get("id");
23
+  const [storeList, setStoreList] = useState([]);
24
+  const [isCompleted, setIsCompleted] = useState(false);
25
+
26
+  const navigate = useNavigate();
27
+
28
+  const formRef = useRef();
29
+  useEffect(() => {
30
+    getStoreList({ pageSize: 9999 }).then((res) => {
31
+      setStoreList(
32
+        res?.records?.map((x) => ({
33
+          label: x.name,
34
+          value: x.id,
35
+        }))
36
+      );
37
+    });
38
+  }, []);
39
+
40
+  useEffect(() => {
41
+    if (id) {
42
+      getPurchaseDetail(id).then((res) => {
43
+        setIsCompleted(res.isCompleted);
44
+        formRef.current.setFieldsValue({
45
+          ...res,
46
+          itemsList: res.itemsList?.map((x) => ({
47
+            ...x,
48
+            planUnitPrice: x.planUnitPrice
49
+              ? floatDivide(x.planUnitPrice, 100)
50
+              : null,
51
+          })),
52
+        });
53
+      });
54
+    }
55
+  }, [id]);
56
+
57
+  const onFinish = async (values) => {
58
+    let data = {
59
+      id: id ? Number(id) : null,
60
+      ...values,
61
+      itemsList: values.itemsList?.map((x) => ({
62
+        ...x,
63
+        planUnitPrice: floatMultiply(x.planUnitPrice, 100),
64
+      })),
65
+    };
66
+
67
+    savePurchase(data).then((res) => {
68
+      // message.success("添加成功");
69
+      navigate(-1);
70
+    });
71
+
72
+    return false;
73
+  };
74
+
75
+  return (
76
+    <PageContainer>
77
+      <Card>
78
+        <ProForm
79
+          formRef={formRef}
80
+          layout={"horizontal"}
81
+          labelCol={{ span: 2 }}
82
+          wrapperCol={{ span: 8 }}
83
+          onFinish={onFinish}
84
+          initialValues={{ type: "1", state: "1" }}
85
+          submitter={{
86
+            searchConfig: {
87
+              resetText: "返回",
88
+            },
89
+            onReset: () => navigate(-1),
90
+            render: (props, doms) => {
91
+              console.log(props, doms, "propsprops");
92
+              console.log(props?.form?.getFieldsValue(), "propsprops");
93
+              return (
94
+                <Row>
95
+                  <Col span={8} offset={2}>
96
+                    <Space>
97
+                      {doms?.map((x, index) => {
98
+                        if (index === 1 && isCompleted) {
99
+                          return null;
100
+                        }
101
+                        return x;
102
+                      })}
103
+                    </Space>
104
+                  </Col>
105
+                </Row>
106
+              );
107
+            },
108
+          }}
109
+        >
110
+          <ProFormText
111
+            name="title"
112
+            label="采购计划"
113
+            placeholder="请输入采购计划"
114
+            rules={[{ required: true, message: "请输入采购计划" }]}
115
+            width={480}
116
+          />
117
+          <ProFormText
118
+            name="planDate"
119
+            label="计划时间"
120
+            placeholder="请输入计划时间"
121
+            rules={[{ required: true, message: "请输入计划时间" }]}
122
+            width={480}
123
+          />
124
+          <Form.Item
125
+            label="采购清单"
126
+            labelCol={{ span: 2 }}
127
+            wrapperCol={{ span: 22 }}
128
+          >
129
+            <Form.List name="itemsList">
130
+              {(fields, { add, remove }) => (
131
+                <>
132
+                  {fields.map(({ key, name, ...restField }) => (
133
+                    <Space
134
+                      key={key}
135
+                      style={{ display: "flex" }}
136
+                      align="baseline"
137
+                    >
138
+                      <ProFormSelect
139
+                        {...restField}
140
+                        name={[name, "storeId"]}
141
+                        label="库存名称"
142
+                        placeholder="请选择库存名称"
143
+                        rules={[{ required: true, message: "请输入选择名称" }]}
144
+                        options={storeList}
145
+                      />
146
+
147
+                      <ProFormDigit
148
+                        {...restField}
149
+                        name={[name, "planAmount"]}
150
+                        label="计划数量"
151
+                        placeholder="请输入计划数量"
152
+                        rules={[{ required: true, message: "请输入计划数量" }]}
153
+                        // width={150}
154
+                        fieldProps={{
155
+                          precision: 10,
156
+                          formatter: (value) => value,
157
+                        }}
158
+                      />
159
+                      <ProFormDigit
160
+                        {...restField}
161
+                        name={[name, "planUnitPrice"]}
162
+                        label="预估单价"
163
+                        placeholder="请输入预估单价"
164
+                        rules={[{ required: true, message: "请输入预估单价" }]}
165
+                        fieldProps={{ precision: 2, prefix: "¥" }}
166
+                        // width={150}
167
+                      />
168
+                      <MinusCircleOutlined onClick={() => remove(name)} />
169
+                    </Space>
170
+                  ))}
171
+                  <Form.Item>
172
+                    <Button
173
+                      type="dashed"
174
+                      onClick={() => add()}
175
+                      // block
176
+                      // style={{width:100}}
177
+                      icon={<PlusOutlined />}
178
+                    >
179
+                      添加
180
+                    </Button>
181
+                  </Form.Item>
182
+                </>
183
+              )}
184
+            </Form.List>
185
+          </Form.Item>
186
+        </ProForm>
187
+      </Card>
188
+    </PageContainer>
189
+  );
190
+};

+ 160
- 0
src/pages/purchase/plan/list/index.jsx 查看文件

@@ -0,0 +1,160 @@
1
+import {
2
+  getPurchaseList,
3
+  getPurchaseDetail,
4
+  savePurchase,
5
+  updatePurchase,
6
+  deletePurchase,
7
+} from "@/services/purchase";
8
+import { queryTable } from "@/utils/request";
9
+import { PageContainer, ProTable } from "@ant-design/pro-components";
10
+import { useNavigate } from "react-router-dom";
11
+import { Button, message, Popconfirm } from "antd";
12
+import { useRef, useState, useEffect } from "react";
13
+import { floatMultiply, floatDivide } from "@/utils";
14
+
15
+// type plan 采购计划  bill 采购账单  inStore  采购入库
16
+const StockList = (props) => {
17
+  const { type } = props;
18
+  const [showDetail, setShowDetail] = useState(false);
19
+  const [activeKey, setActiveKey] = useState("");
20
+  const actionRef = useRef();
21
+  const navigate = useNavigate();
22
+  // console.log(props, "props");
23
+
24
+  useEffect(() => {
25
+    actionRef.current.reload();
26
+  }, [type]);
27
+  const handleDelete = (id) => {
28
+    if (id) {
29
+      deletePurchase(id).then((res) => {
30
+        actionRef.current.reload();
31
+      });
32
+    }
33
+  };
34
+
35
+  const columns = [
36
+    {
37
+      title: "采购计划",
38
+      dataIndex: "title",
39
+    },
40
+
41
+    {
42
+      title: "计划时间",
43
+      dataIndex: "planDate",
44
+    },
45
+    {
46
+      title: "采购项目",
47
+      dataIndex: "items",
48
+      search: false,
49
+    },
50
+
51
+    {
52
+      title: "是否入库",
53
+      dataIndex: "isInStore",
54
+      valueEnum: {
55
+        false: { text: "否", status: "Error" },
56
+        true: { text: "是", status: "Success" },
57
+      },
58
+      search: false,
59
+    },
60
+
61
+    {
62
+      title: "是否采购完成",
63
+      dataIndex: "isCompleted",
64
+      valueEnum: {
65
+        false: { text: "否", status: "Error" },
66
+        true: { text: "是", status: "Success" },
67
+      },
68
+      search: false,
69
+    },
70
+
71
+    {
72
+      title: "操作",
73
+      valueType: "option",
74
+      width: 200,
75
+      render: (_, record) => [
76
+        type === "plan" ? (
77
+          <Button
78
+            key={2}
79
+            style={{ padding: 0 }}
80
+            type="link"
81
+            onClick={() => {
82
+              navigate(`/purchase/plan/edit?id=${record.id}`);
83
+            }}
84
+          >
85
+            修改
86
+          </Button>
87
+        ) : null,
88
+
89
+        type === "bill" ? (
90
+          <Button
91
+            key={4}
92
+            style={{ padding: 0 }}
93
+            type="link"
94
+            onClick={() => {
95
+              navigate(`/purchase/bill/edit?id=${record.id}`);
96
+            }}
97
+          >
98
+            采购
99
+          </Button>
100
+        ) : null,
101
+
102
+        type === "inStore" ? (
103
+          <Button
104
+            key={5}
105
+            style={{ padding: 0 }}
106
+            type="link"
107
+            onClick={() => {
108
+              navigate(`/purchase/inStore/edit?id=${record.id}`);
109
+            }}
110
+          >
111
+            入库
112
+          </Button>
113
+        ) : null,
114
+
115
+        type === "plan" ? (
116
+          <Popconfirm
117
+            key={3}
118
+            title="您是否确认删除 ?"
119
+            onConfirm={() => handleDelete(record.id)}
120
+            okText="确定"
121
+            cancelText="取消"
122
+          >
123
+            {/* manualPush */}
124
+            <Button style={{ padding: 0 }} type="link">
125
+              删除
126
+            </Button>
127
+          </Popconfirm>
128
+        ) : null,
129
+      ],
130
+    },
131
+  ];
132
+
133
+  return (
134
+    <PageContainer>
135
+      <ProTable
136
+        actionRef={actionRef}
137
+        rowKey="id"
138
+        params={type === "inStore"? {isCompleted:true}:{}}
139
+        toolBarRender={() => [
140
+          type === "plan" ? (
141
+            <Button
142
+              key="2"
143
+              type="primary"
144
+              onClick={() => {
145
+                navigate("/purchase/plan/edit");
146
+              }}
147
+            >
148
+              新增
149
+            </Button>
150
+          ) : null,
151
+        ]}
152
+        // search={false}
153
+        request={queryTable(getPurchaseList)}
154
+        columns={columns}
155
+      />
156
+    </PageContainer>
157
+  );
158
+};
159
+
160
+export default StockList;

+ 2
- 2
src/pages/roles/list.jsx 查看文件

@@ -93,8 +93,8 @@ export default (props) => {
93 93
 
94 94
   React.useEffect(() => {
95 95
     startLoading();
96
-    getRoleList({ pageSize: 100 }).then((res) => {
97
-      setList(res.records || []);
96
+    getRoleList().then((res = []) => {
97
+      setList(res);
98 98
       cancelLoading();
99 99
     }).catch(() => {
100 100
       cancelLoading();

+ 2
- 2
src/pages/stock/list/index.jsx 查看文件

@@ -93,7 +93,7 @@ const StockList = (props) => {
93 93
           style={{ padding: 0 }}
94 94
           type="link"
95 95
           onClick={() => {
96
-            navigate(`/stock/edit?id=${record.id}`);
96
+            navigate(`/stock/add?id=${record.id}`);
97 97
           }}
98 98
         >
99 99
           修改
@@ -126,7 +126,7 @@ const StockList = (props) => {
126 126
             key="2"
127 127
             type="primary"
128 128
             onClick={() => {
129
-              navigate('/stock/edit');
129
+              navigate('/stock/add');
130 130
             }}
131 131
           >
132 132
             新增

+ 1
- 1
src/pages/stock/outAndIn/index.jsx 查看文件

@@ -18,7 +18,7 @@ const OutAndIn = (props) => {
18 18
         amount: tabKey === 'out' ? -values.amount : values.amount,
19 19
         optType: tabKey,
20 20
       }).then((res) => {
21
-        message.success('操作成功');
21
+        
22 22
 
23 23
         onSuccess();
24 24
       });

+ 147
- 0
src/pages/user/Edit.jsx 查看文件

@@ -0,0 +1,147 @@
1
+import React from 'react';
2
+import { Button, Card, Form, Input, Select } from 'antd';
3
+import useBool from '@/utils/hooks/useBool';
4
+import { getRoleList } from '@/services/role';
5
+import { saveUser, getUser } from '@/services/user';
6
+import { useSearchParams, useNavigate } from 'react-router-dom';
7
+import md5 from 'md5';
8
+
9
+const formItemLayout = {
10
+  labelCol: {
11
+    xs: { span: 24 },
12
+    sm: { span: 8 },
13
+  },
14
+  wrapperCol: {
15
+    xs: { span: 24 },
16
+    sm: { span: 16 },
17
+  },
18
+};
19
+const tailFormItemLayout = {
20
+  wrapperCol: {
21
+    xs: {
22
+      span: 24,
23
+      offset: 0,
24
+    },
25
+    sm: {
26
+      span: 16,
27
+      offset: 8,
28
+    },
29
+  },
30
+};
31
+
32
+const { Option } = Select;
33
+
34
+export default (props) => {
35
+  const [loading, startLoading, cancelLoading] = useBool();
36
+  const [submiting, startSubmit, cancelSubmit] = useBool();
37
+  const [roleList, setRoleList] = React.useState([]);
38
+  const [searchParams] = useSearchParams();
39
+  const [form] = Form.useForm();
40
+  const userRef = React.useRef();
41
+  const navigate = useNavigate();
42
+
43
+  const id = searchParams.get('id');
44
+
45
+  const onFinish = (values) => {
46
+    const password = values.password ? md5(values.password) : undefined;
47
+    const loginName = values.phone;
48
+    const rolesList = (values.rolesList || []).map(x => ({ id: x }));
49
+
50
+    startSubmit();
51
+    saveUser({ ...values, loginName, password, rolesList, id }).then(res => {
52
+      cancelSubmit();
53
+      navigate(-1);
54
+    }).catch(() => {
55
+      cancelSubmit();
56
+    });
57
+  }
58
+
59
+  React.useEffect(() => {
60
+    getRoleList().then(setRoleList);
61
+  }, []);
62
+  
63
+  React.useEffect(() => {
64
+    if (id) {
65
+      startLoading();
66
+      getUser(id).then(user => {
67
+        userRef.current = user;
68
+        form.setFieldsValue({
69
+          ...user,
70
+          rolesList: (user.rolesList || []).map(x => `${x.id}`),
71
+        });
72
+        cancelLoading();
73
+      }).catch(() => {
74
+        cancelLoading();
75
+      });
76
+    } else {
77
+      form.setFieldsValue({ password: '123456' });
78
+    }
79
+  }, [id]);
80
+
81
+  return (
82
+    <Card loading={loading}>
83
+      <Form onFinish={onFinish} form={form} {...formItemLayout} scrollToFirstError>
84
+        <Form.Item
85
+          name="name"
86
+          label="姓名"
87
+          rules={[
88
+            {
89
+              required: true,
90
+              message: '请填写姓名',
91
+            },
92
+          ]}
93
+        >
94
+          <Input />
95
+        </Form.Item>
96
+        <Form.Item
97
+          name="dept"
98
+          label="部门"
99
+        >
100
+          <Input />
101
+        </Form.Item>
102
+        <Form.Item
103
+          name="phone"
104
+          label="手机号"
105
+        >
106
+          <Input />
107
+        </Form.Item>
108
+        <Form.Item label="账号">
109
+          系统账号与手机号相同
110
+        </Form.Item>
111
+        <Form.Item
112
+          name="password"
113
+          label="密码"
114
+          extra={id ? '填写密码意味着重置该账户密码' : '默认密码 123456'}
115
+          hasFeedback
116
+        >
117
+          <Input.Password allowClear />
118
+        </Form.Item>
119
+        <Form.Item
120
+          name="rolesList"
121
+          label="角色"
122
+        >
123
+          <Select
124
+            mode="multiple"
125
+            allowClear
126
+            style={{ width: '100%' }}
127
+            placeholder="请选择角色"
128
+          >
129
+            {
130
+              roleList.map(role => (<Option key={role.id}>{role.name}</Option>))
131
+            }
132
+          </Select>
133
+        </Form.Item>
134
+        <Form.Item {...tailFormItemLayout}>
135
+          <Button loading={submiting} type="primary" htmlType="submit">
136
+            保存
137
+          </Button>
138
+          <Button style={{ marginLeft: '2em' }} onClick={() => navigate(-1)}>
139
+            返回
140
+          </Button>
141
+        </Form.Item>
142
+      </Form>
143
+    </Card>
144
+  )
145
+}
146
+
147
+

+ 0
- 79
src/pages/user/Role.jsx 查看文件

@@ -1,79 +0,0 @@
1
-import React from 'react';
2
-import { Button, Row, Col, Card, List, Popconfirm, Tree, Space, Modal } from 'antd';
3
-import List from './list';
4
-
5
-export default (props) => {
6
-
7
-  const treeData = [
8
-    {
9
-      title: 'parent 1',
10
-      key: '0-0',
11
-      children: [
12
-        {
13
-          title: 'parent 1-0',
14
-          key: '0-0-0',
15
-          disabled: true,
16
-          children: [
17
-            {
18
-              title: 'leaf',
19
-              key: '0-0-0-0',
20
-              disableCheckbox: true,
21
-            },
22
-            {
23
-              title: 'leaf',
24
-              key: '0-0-0-1',
25
-            },
26
-          ],
27
-        },
28
-        {
29
-          title: 'parent 1-1',
30
-          key: '0-0-1',
31
-          children: [
32
-            {
33
-              title: (
34
-                <span
35
-                  style={{
36
-                    color: '#1890ff',
37
-                  }}
38
-                >
39
-                  sss
40
-                </span>
41
-              ),
42
-              key: '0-0-1-0',
43
-            },
44
-          ],
45
-        },
46
-      ],
47
-    },
48
-  ];
49
-
50
-  const onSelect = (selectedKeys, info) => {
51
-    console.log('selected', selectedKeys, info);
52
-  };
53
-  const onCheck = (checkedKeys, info) => {
54
-    console.log('onCheck', checkedKeys, info);
55
-  };
56
-
57
-  return (
58
-    <div>
59
-      <Row gutter={24}>
60
-        <Col span={8}>
61
-        </Col>
62
-        <Col span={16}>
63
-          <Card title='授权菜单' extra={<Button type='primary' ghost>保存</Button>}>
64
-            <Tree
65
-              checkable
66
-              defaultExpandedKeys={['0-0-0', '0-0-1']}
67
-              defaultSelectedKeys={['0-0-0', '0-0-1']}
68
-              defaultCheckedKeys={['0-0-0', '0-0-1']}
69
-              onSelect={onSelect}
70
-              onCheck={onCheck}
71
-              treeData={treeData}
72
-            />
73
-          </Card>
74
-        </Col>
75
-      </Row>
76
-
77
-    </div>
78
-  )
79
-}

+ 112
- 0
src/pages/user/index.jsx 查看文件

@@ -0,0 +1,112 @@
1
+import React from 'react';
2
+import { useNavigate } from 'react-router-dom';
3
+import { queryTable } from '@/utils/request';
4
+import { ProTable } from '@ant-design/pro-components';
5
+import { Button, message, Popconfirm } from 'antd';
6
+import { getUserList, updateUserStatus } from '@/services/user';
7
+
8
+const queryUserList = queryTable(getUserList);
9
+
10
+export default (props) => {
11
+  const actionRef = React.useRef();
12
+  const navigate = useNavigate();
13
+
14
+  const updateStatus = (user) => {
15
+    const status = user.status === 1 ? 0 : 1;
16
+    const hide = message.loading('请稍候...', 0);
17
+    updateUserStatus(user.id, status).then(res => {
18
+      hide();
19
+      actionRef.current.reload();
20
+    }).catch(() => {
21
+      hide();
22
+    })
23
+  }
24
+
25
+  const columns = [
26
+    {
27
+      title: 'id',
28
+      dataIndex: 'id',
29
+      search: false,
30
+    },
31
+    {
32
+      title: '姓名',
33
+      dataIndex: 'name',
34
+    },
35
+    {
36
+      title: '部门',
37
+      dataIndex: 'dept',
38
+    },
39
+    {
40
+      title: '手机号',
41
+      dataIndex: 'phone',
42
+    },
43
+    {
44
+      title: '账号',
45
+      dataIndex: 'loginName',
46
+      search: false,
47
+    },
48
+    {
49
+      title: '状态',
50
+      dataIndex: 'status',
51
+      search: false,
52
+      valueEnum: {
53
+        1: {
54
+          text: '正常',
55
+          status: 'Processing',
56
+        },
57
+        0: {
58
+          text: '禁用',
59
+          status: 'Error',
60
+        },
61
+      },
62
+    },
63
+    {
64
+      title: '操作',
65
+      valueType: 'option',
66
+      width: 200,
67
+      render: (_, record) => [
68
+        <Button
69
+          key={1}
70
+          style={{ padding: 0 }}
71
+          type="link"
72
+          onClick={() => {
73
+            updateStatus(record);
74
+          }}
75
+        >
76
+          {record.status === 1 ? '禁用' : '启用'}
77
+        </Button>,
78
+        <Button
79
+          key={2}
80
+          style={{ padding: 0 }}
81
+          type="link"
82
+          onClick={() => {
83
+            console.log(record, ']]');
84
+            navigate(`/system/user/edit?id=${record.id}`);
85
+          }}
86
+        >
87
+          编辑
88
+        </Button>,
89
+      ],
90
+    },
91
+  ]
92
+
93
+  return (
94
+    <ProTable
95
+      actionRef={actionRef}
96
+      rowKey="id"
97
+      toolBarRender={() => [
98
+        <Button
99
+          key="1"
100
+          type="primary"
101
+          onClick={() => {
102
+            navigate('/system/user/edit');
103
+          }}
104
+        >
105
+          新增
106
+        </Button>,
107
+      ]}
108
+      request={queryUserList}
109
+      columns={columns}
110
+    />
111
+  )
112
+}

+ 13
- 4
src/routes/Router.jsx 查看文件

@@ -1,9 +1,18 @@
1
+import React from "react";
1 2
 import { createHashRouter, RouterProvider } from "react-router-dom";
2
-import routes from './routes'
3
-
4
-const router = createHashRouter(routes);
5
-export const hashRouter = true;
3
+import { useModel } from "@/store";
4
+import { defaultRoutes } from './routes';
6 5
 
7 6
 export default (props) => {
7
+  const { routes } = useModel('user');
8
+
9
+  const router = React.useMemo(() => {
10
+    if (!routes || routes.length < 1) {
11
+      return createHashRouter(defaultRoutes);
12
+    } else {
13
+      return createHashRouter(routes);
14
+    }
15
+  }, [routes]);
16
+
8 17
   return <RouterProvider router={router} />
9 18
 }

+ 12
- 41
src/routes/menus.jsx 查看文件

@@ -1,8 +1,4 @@
1 1
 import { Link } from 'react-router-dom';
2
-import routes from './routes';
3
-import { hashRouter } from './Router';
4
-
5
-let routeArr = [];
6 2
 
7 3
 // 菜单是否显示
8 4
 // 没有 meta 或者 meta.title 为空, 或者 meta.hideInMenu = true 的 都不显示
@@ -20,55 +16,30 @@ const getPath = (parent, current = '') => {
20 16
   return `${parent}/${current}`.replace(/\/\//g, '/');
21 17
 }
22 18
 
23
-const getMenuItems = (dts = [], fullPath = '/') => {
24
-  return dts.map(item => {
25
-    const path = getPath(fullPath, item.path);
19
+export const getMenuItems = (routes = [], fullPath = '/') => {
20
+  return routes.map(route => {
21
+    const path = getPath(fullPath, route.path);
26 22
 
27 23
     //
28
-    if (!isShow(item)) return false;
24
+    if (!isShow(route)) return false;
29 25
     
30
-    const children = hasChildren(item.children) ? getMenuItems(item.children, path) : false;
26
+    const children = hasChildren(route.children) ? getMenuItems(route.children, path) : false;
27
+
28
+    const { target, title, icon } = route.meta || {}
31 29
 
32 30
     // 坑爹 react-router v6 不支持 hash 路由的 target 跳转
33
-    const label = hashRouter && item.meta && item.meta.target === '_blank' ?
34
-      <a href={`${window.location.pathname}#${path}`} target={item.meta.target}>{item.meta.title}</a>
35
-      : <Link to={path} target={item.meta.target}>{item.meta.title}</Link>;
31
+    const label = target === '_blank' ?
32
+      <a href={`${window.location.pathname}#${path}`} target={target}>{title}</a>
33
+      : <Link to={path} target={target}>{title}</Link>;
36 34
 
37 35
     return Object.assign(
38 36
       {
39 37
         key: path,
40 38
         label,
41
-        title: item.meta.title,
42
-        icon: item.meta.icon,
39
+        title,
40
+        icon,
43 41
       },
44 42
       children && { children },
45 43
     )
46 44
   }).filter(Boolean);
47 45
 }
48
-
49
-const flatten = (routes, parent) => {
50
-  (routes || []).forEach((route) => {
51
-    const path = getPath(parent, route.path);
52
-    routeArr.push({
53
-      ...route,
54
-      path
55
-    });
56
-
57
-    if (route.children) {
58
-      flatten(route.children, path);
59
-    }
60
-  });
61
-}
62
-
63
-const getItems = () => getMenuItems(routes[0]?.children);
64
-const getRouteArr = () => {
65
-  if (routeArr.length < 1) {
66
-    flatten(routes, '/');
67
-  }
68
-  return routeArr;
69
-}
70
-// 
71
-export {
72
-  getItems,
73
-  getRouteArr,
74
-};

+ 14
- 0
src/routes/permissions.js 查看文件

@@ -0,0 +1,14 @@
1
+
2
+export const getAuthedRoutes = (routes, permissions) => {
3
+  if (!routes || routes.length < 1) return [];
4
+
5
+  return routes.map(route => {
6
+    if (route.meta && route.meta.permission && permissions.indexOf(route.meta.permission) < 0) return false;
7
+
8
+    if (route.children) {
9
+      route.children = getAuthedRoutes(route.children, permissions);
10
+    }
11
+
12
+    return route;
13
+  }).filter(Boolean);
14
+}

+ 327
- 214
src/routes/routes.jsx 查看文件

@@ -32,7 +32,14 @@ import RotationChartEdit from '@/pages/rotationChart/edit';
32 32
 import RotationChartIntroduction from '@/pages/rotationChart/introduction';
33 33
 import Roles from '@/pages/roles/index';
34 34
 import RegulationList from '@/regulation';
35
-
35
+import UserList from '@/pages/user';
36
+import UserEdit from '@/pages/user/Edit';
37
+import PurchasePlanList from "@/pages/purchase/plan/list";
38
+import PurchasePlanEdit from "@/pages/purchase/plan/edit";
39
+import PurchaseBillEdit from "@/pages/purchase/bill/edit";
40
+import PurchaseInStoreEdit from "@/pages/purchase/inStore/edit";
41
+import EmergencyPlanList from "@/pages/cms/emergencyPlan/list";
42
+import EmergencyPlanEdit from "@/pages/cms/emergencyPlan/edit";
36 43
 /**
37 44
  * meta 用来扩展自定义数据数据
38 45
  * {
@@ -43,242 +50,319 @@ import RegulationList from '@/regulation';
43 50
  * }
44 51
  */
45 52
 
46
-export default [
53
+export const authRoutes = [
47 54
   {
48
-    path: '/',
49
-    element: <AuthLayout />,
55
+    path: 'task',
56
+    element: <Container />,
57
+    meta: {
58
+      title: '军供任务',
59
+      icon: <AppstoreOutlined />,
60
+    },
50 61
     children: [
51 62
       {
52 63
         index: true,
53
-        element: <Home />,
64
+        element: <Navigate to='guaranteeTask' replace />,
54 65
       },
55 66
       {
56
-        path: 'home',
57
-        element: <Home />,
67
+        path: 'guaranteeTask',
68
+        element: <GuaranteeTaskList />,
58 69
         meta: {
59
-          title: '首页',
60
-          icon: <DesktopOutlined />,
70
+          title: '军供通报',
71
+        },
72
+      },
73
+      {
74
+        path: 'guaranteeTask/edit',
75
+        element: <GuaranteeTaskEdit />,
76
+        meta: {
77
+          title: '任务配置',
78
+        },
79
+      },
80
+      {
81
+        path: 'guaranteeTask/print',
82
+        element: <GuaranteeTaskPrint />,
83
+        meta: {
84
+          hideInMenu: true,
85
+          noLayout: true,
86
+          target: '_blank',
87
+          title: '任务执行',
88
+        },
89
+      }
90
+    ]
91
+  },
92
+  {
93
+    path: 'material',
94
+    element: <Container />,
95
+    meta: {
96
+      title: '物资管理',
97
+      icon: <AppstoreOutlined />,
98
+    },
99
+    children: [
100
+      {
101
+        index: true,
102
+        element: <Navigate to='dish/list' replace />,
103
+      },
104
+      {
105
+        path: 'dish/list',
106
+        element: <DishList />,
107
+        meta: {
108
+          title: '菜肴管理',
109
+        },
110
+      },
111
+      {
112
+        path: 'dish/edit',
113
+        element: <DishEdit />,
114
+        meta: {
115
+          hideInMenu: true,
116
+          title: '菜肴维护',
117
+        },
118
+      },
119
+      {
120
+        path: 'package/list',
121
+        element: <PackageList />,
122
+        meta: {
123
+          title: '套餐管理',
124
+        },
125
+      },
126
+    ]
127
+  },
128
+  {
129
+    path: 'stock',
130
+    element: <Container />,
131
+    meta: {
132
+      title: '库存管理',
133
+      icon: <AppstoreOutlined />,
134
+    },
135
+    children: [
136
+      {
137
+        index: true,
138
+        element: <Navigate to='list' replace />,
139
+      },
140
+      {
141
+        path: 'list',
142
+        element: <StockList />,
143
+        meta: {
144
+          title: '库存列表',
145
+        },
146
+      },
147
+      {
148
+        path: 'add',
149
+        element: <StockEdit />,
150
+        meta: {
151
+          title: '库存维护',
152
+        },
153
+      },
154
+    ],
155
+  },
156
+  {
157
+    path: 'cms',
158
+    element: <Container />,
159
+    meta: {
160
+      title: '公告文件',
161
+    },
162
+    children: [
163
+      {
164
+        index: true,
165
+        element: <Navigate to='rotationChart/list' replace />,
166
+      },
167
+      {
168
+        path: 'station',
169
+        element: null,
170
+        meta: {
171
+          title: '本站信息',
172
+        },
173
+      },
174
+      {
175
+        path: 'rotationChart/list',
176
+        element: <RotationChartList />,
177
+        meta: {
178
+          title: '公告内容',
179
+        },
180
+      },
181
+      {
182
+        path: 'rotationChart/edit',
183
+        element: <RotationChartEdit />,
184
+        meta: {
185
+          title: '公告维护',
186
+        },
187
+      },
188
+      {
189
+        path: 'rotationChart/introduction',
190
+        element: <RotationChartIntroduction />,
191
+        meta: {
192
+          title: '本站信息简介',
193
+        },
194
+      },
195
+      {
196
+        path: 'regulation',
197
+        element: null,
198
+        meta: {
199
+          title: '规章制度',
200
+        },
201
+      },
202
+      {
203
+        path: "emergency-plan",
204
+        element: <EmergencyPlanList />,
205
+        meta: {
206
+          title: "应急预案",
207
+        },
208
+      },
209
+      {
210
+        path: "emergency-plan/edit",
211
+        element: <EmergencyPlanEdit />,
212
+        meta: {
213
+          title: "应急预案维护",
214
+          hideInMenu: true,
61 215
         },
62 216
       },
63 217
       {
64
-        path: 'task',
65
-        element: <Container />,
218
+        path: 'files',
219
+        element: null,
66 220
         meta: {
67
-          title: '军供任务',
68
-          icon: <AppstoreOutlined />,
221
+          title: '文件管理',
69 222
         },
70
-        children: [
71
-          {
72
-            index: true,
73
-            element: <Navigate to='guaranteeTask' replace />,
74
-          },
75
-          {
76
-            path: 'guaranteeTask',
77
-            element: <GuaranteeTaskList />,
78
-            meta: {
79
-              title: '军供通报',
80
-            },
81
-          },
82
-          {
83
-            path: 'guaranteeTask/edit',
84
-            element: <GuaranteeTaskEdit />,
85
-            meta: {
86
-              title: '任务配置',
87
-            },
88
-          },
89
-          {
90
-            path: 'guaranteeTask/print',
91
-            element: <GuaranteeTaskPrint />,
92
-            meta: {
93
-              hideInMenu: true,
94
-              noLayout: true,
95
-              target: '_blank',
96
-              title: '任务执行',
97
-            },
98
-          }
99
-        ]
100
-      },
101
-      {
102
-        path: 'material',
103
-        element: <Container />,
223
+      },
224
+    ],
225
+  },
226
+  {
227
+    path: 'static',
228
+    element: <Container />,
229
+    meta: {
230
+      title: '数据分析',
231
+    },
232
+  },
233
+  {
234
+    path: 'system',
235
+    element: <Container />,
236
+    meta: {
237
+      title: '系统管理',
238
+    },
239
+    children: [
240
+      {
241
+        index: true,
242
+        element: <Navigate to='stockClassification/list' replace />,
243
+      },
244
+      {
245
+        path: 'stockClassification/list',
246
+        element: <StockClassificationList />,
104 247
         meta: {
105
-          title: '物资管理',
106
-          icon: <AppstoreOutlined />,
248
+          title: '库存分类管理',
107 249
         },
108
-        children: [
109
-          {
110
-            index: true,
111
-            element: <Navigate to='dish/list' replace />,
112
-          },
113
-          {
114
-            path: 'dish/list',
115
-            element: <DishList />,
116
-            meta: {
117
-              title: '菜肴管理',
118
-            },
119
-          },
120
-          {
121
-            path: 'dish/edit',
122
-            element: <DishEdit />,
123
-            meta: {
124
-              hideInMenu: true,
125
-              title: '菜肴维护',
126
-            },
127
-          },
128
-          {
129
-            path: 'package/list',
130
-            element: <PackageList />,
131
-            meta: {
132
-              title: '套餐管理',
133
-            },
134
-          },
135
-        ]
136
-      },
137
-      {
138
-        path: 'stock',
139
-        element: <Container />,
250
+      },
251
+      {
252
+        path: 'stockClassification/edit',
253
+        element: <StockClassificationEdit />,
140 254
         meta: {
141
-          title: '库存管理',
142
-          icon: <AppstoreOutlined />,
255
+          title: '库存分类维护',
256
+          hideInMenu: true,
143 257
         },
144
-        children: [
145
-          {
146
-            index: true,
147
-            element: <Navigate to='list' replace />,
148
-          },
149
-          {
150
-            path: 'list',
151
-            element: <StockList />,
152
-            meta: {
153
-              title: '库存列表',
154
-            },
155
-          },
156
-          {
157
-            path: 'add',
158
-            element: <StockEdit />,
159
-            meta: {
160
-              title: '库存维护',
161
-            },
162
-          },
163
-        ],
164
-      },
165
-      {
166
-        path: 'cms',
167
-        element: <Container />,
258
+      },
259
+      {
260
+        path: 'log',
261
+        element: <StockLog />,
168 262
         meta: {
169
-          title: '公告文件',
263
+          title: '库存操作日志',
170 264
         },
171
-        children: [
172
-          {
173
-            index: true,
174
-            element: <Navigate to='rotationChart/list' replace />,
175
-          },
176
-          {
177
-            path: 'station',
178
-            element: null,
179
-            meta: {
180
-              title: '本站信息',
181
-            },
182
-          },
183
-          {
184
-            path: 'rotationChart/list',
185
-            element: <RotationChartList />,
186
-            meta: {
187
-              title: '公告内容',
188
-            },
189
-          },
190
-          {
191
-            path: 'rotationChart/edit',
192
-            element: <RotationChartEdit />,
193
-            meta: {
194
-              title: '公告维护',
195
-            },
196
-          },
197
-          {
198
-            path: 'rotationChart/introduction',
199
-            element: <RotationChartIntroduction />,
200
-            meta: {
201
-              title: '本站信息简介',
202
-            },
203
-          },
204
-          {
205
-            path: 'regulation',
206
-            element: <RegulationList />,
207
-            meta: {
208
-              title: '规章制度',
209
-            },
210
-          },
211
-          {
212
-            path: 'emergency-plan',
213
-            element: null,
214
-            meta: {
215
-              title: '应急预案',
216
-            },
217
-          },
218
-          {
219
-            path: 'files',
220
-            element: null,
221
-            meta: {
222
-              title: '文件管理',
223
-            },
224
-          },
225
-        ],
226
-      },
227
-      {
228
-        path: 'static',
229
-        element: <Container />,
265
+      },
266
+      {
267
+        path: 'roles',
268
+        element: <Roles />,
230 269
         meta: {
231
-          title: '数据分析',
270
+          title: '系统角色管理',
232 271
         },
233 272
       },
234 273
       {
235
-        path: 'system',
236
-        element: <Container />,
274
+        path: 'user',
275
+        element: <UserList />,
237 276
         meta: {
238
-          title: '系统管理',
277
+          title: '系统用户管理',
278
+        },
279
+      },
280
+      {
281
+        path: 'user/edit',
282
+        element: <UserEdit />,
283
+        meta: {
284
+          hideInMenu: true,
285
+          title: '系统用户编辑',
286
+        },
287
+      }
288
+    ],
289
+  },
290
+  {
291
+    path: "purchase",
292
+    element: <Container />,
293
+    meta: {
294
+      title: "采购管理",
295
+    },
296
+    children: [
297
+      {
298
+        index: true,
299
+        element: <Navigate to="plan/list" replace />,
300
+      },
301
+      {
302
+        path: "plan/list",
303
+        element: <PurchasePlanList type="plan" />,
304
+        meta: {
305
+          title: "采购计划",
306
+        },
307
+      },
308
+      {
309
+        path: "plan/edit",
310
+        element: <PurchasePlanEdit />,
311
+        meta: {
312
+          title: "采购计划维护",
313
+          hideInMenu: true,
314
+        },
315
+      },
316
+      {
317
+        path: "bill/list",
318
+        element: <PurchasePlanList type="bill" />,
319
+        meta: {
320
+          title: "采购账单",
321
+        },
322
+      },
323
+      {
324
+        path: "bill/edit",
325
+        element: <PurchaseBillEdit />,
326
+        meta: {
327
+          title: "采购账单维护",
328
+          hideInMenu: true,
329
+        },
330
+      },
331
+      {
332
+        path: "inStore/list",
333
+        element: <PurchasePlanList type="inStore" />,
334
+        meta: {
335
+          title: "采购入库",
336
+        },
337
+      },
338
+      {
339
+        path: "inStore/edit",
340
+        element: <PurchaseInStoreEdit  />,
341
+        meta: {
342
+          title: "采购入库维护",
343
+          hideInMenu: true,
344
+        },
345
+      },
346
+    ],
347
+  },
348
+]
349
+
350
+export const defaultRoutes = [
351
+  {
352
+    path: '/',
353
+    element: <AuthLayout />,
354
+    children: [
355
+      {
356
+        index: true,
357
+        element: <Home />,
358
+      },
359
+      {
360
+        path: 'home',
361
+        element: <Home />,
362
+        meta: {
363
+          title: '首页',
364
+          icon: <DesktopOutlined />,
239 365
         },
240
-        children: [
241
-          {
242
-            index: true,
243
-            element: <Navigate to='stockClassification/list' replace />,
244
-          },
245
-          {
246
-            path: 'stockClassification/list',
247
-            element: <StockClassificationList />,
248
-            meta: {
249
-              title: '库存分类管理',
250
-            },
251
-          },
252
-          {
253
-            path: 'stockClassification/edit',
254
-            element: <StockClassificationEdit />,
255
-            meta: {
256
-              title: '库存分类维护',
257
-              hideInMenu: true,
258
-            },
259
-          },
260
-          {
261
-            path: 'log',
262
-            element: <StockLog />,
263
-            meta: {
264
-              title: '库存操作日志',
265
-            },
266
-          },
267
-          {
268
-            path: 'roles',
269
-            element: <Roles />,
270
-            meta: {
271
-              title: '系统角色管理',
272
-            },
273
-          },
274
-          {
275
-            path: 'user',
276
-            element: null,
277
-            meta: {
278
-              title: '系统用户管理',
279
-            },
280
-          }
281
-        ],
282 366
       },
283 367
       {
284 368
         path: '*',
@@ -294,4 +378,33 @@ export default [
294 378
     path: '*',
295 379
     element: <Page404 />
296 380
   }
297
-]
381
+];
382
+
383
+export function mergeAuthRoutes (r1, r2) {
384
+  const r = r1.slice();
385
+  const children = r1[0].children.slice();
386
+  r[0].children = children.concat(r2);
387
+  return r;
388
+}
389
+
390
+// 全部路由
391
+export const routes = mergeAuthRoutes(defaultRoutes, authRoutes);
392
+
393
+function getPath(parent = '/', current = '') {
394
+  if (current.indexOf('/') === 0 || current.indexOf('http') === 0) return current;
395
+  return `${parent}/${current}`.replace(/\/\//g, '/');
396
+}
397
+
398
+// 路由数组, 一维数组
399
+export const routeArr = (() => {
400
+  const flatten = (routes, parentPath = '/') => {
401
+    return routes.reduce((acc, route) => {
402
+      const path = getPath(parentPath, route.path);
403
+      const children = route.children ? flatten(route.children, path) : [];
404
+      
405
+      return acc.concat([{ ...route, path }].concat(children));
406
+    }, []);
407
+  }
408
+
409
+  return flatten(routes);
410
+})();

+ 10
- 0
src/services/posts.js 查看文件

@@ -0,0 +1,10 @@
1
+import { restful } from "@/utils/request";
2
+
3
+/**
4
+ * 构造 Service
5
+ * @returns
6
+ */
7
+const [getPostsList, getPostsDetail, savePosts, updatePosts, deletePosts] =
8
+  restful("/posts");
9
+
10
+export { getPostsList, getPostsDetail, savePosts, updatePosts, deletePosts };

+ 43
- 0
src/services/purchase.js 查看文件

@@ -0,0 +1,43 @@
1
+import request, { restful } from "@/utils/request";
2
+
3
+/**
4
+ * 构造 Service
5
+ * @returns
6
+ */
7
+const [
8
+  getPurchaseList,
9
+  getPurchaseDetail,
10
+  savePurchase,
11
+  updatePurchase,
12
+  deletePurchase,
13
+] = restful("/purchase");
14
+
15
+/**
16
+ * 新增
17
+ *  @param {*} id
18
+ * @param {*} data
19
+ * @returns
20
+ */
21
+  const savePurchaseInStore = (id) => request(`/purchase/${id}/in-store`, { method: 'put'});
22
+
23
+//  const [
24
+//   getResourceList,
25
+//   getResourceDetail,
26
+//   saveResource,
27
+//   updateResource,
28
+//   deleteResource,
29
+//  ] = restful('/resources');
30
+
31
+export {
32
+  getPurchaseList,
33
+  getPurchaseDetail,
34
+  savePurchase,
35
+  updatePurchase,
36
+  deletePurchase,
37
+  savePurchaseInStore,
38
+  //   getResourceList,
39
+  //   getResourceDetail,
40
+  //   saveResource,
41
+  //   updateResource,
42
+  //   deleteResource,
43
+};

+ 2
- 2
src/services/stock.js 查看文件

@@ -6,7 +6,7 @@ import request from '@/utils/request';
6 6
  * @param {*} params
7 7
  * @returns
8 8
  */
9
-export const getStoreList = (params) => request('/store', { params });
9
+export const getStoreList = (params) => request('/store', { params,successTip:false });
10 10
 
11 11
 /**
12 12
  * 新增
@@ -51,7 +51,7 @@ export const storeExport = (params) => request('/store/export',  { download: tru
51 51
  * @param {*} params
52 52
  * @returns
53 53
  */
54
-export const getStoreTypeList = (params) => request('/storeType', { params });
54
+export const getStoreTypeList = (params) => request('/storeType', { params,successTip:false });
55 55
 
56 56
 /**
57 57
  * 新增

+ 26
- 2
src/services/user.js 查看文件

@@ -1,4 +1,4 @@
1
-import request from '@/utils/request';
1
+import request, { restful } from '@/utils/request';
2 2
 
3 3
 /**
4 4
  * 获取当前人员
@@ -8,7 +8,7 @@ import request from '@/utils/request';
8 8
  * @param {*} 
9 9
  * @returns
10 10
  */
11
- export const queryCurrentUser = () => request('/users/current', { silent: true });
11
+ export const queryCurrentUser = () => request('/users/current', { silent: true,successTip:false });
12 12
 
13 13
  /**
14 14
   * 登录
@@ -17,3 +17,27 @@ import request from '@/utils/request';
17 17
   */
18 18
   export const login = (data) => request('/login', { data, method: 'post' });
19 19
 
20
+
21
+  const [
22
+    getUserList,
23
+    getUser,
24
+    saveUser,
25
+    updateUser,
26
+    deleteUser,
27
+   ] = restful('/users');
28
+
29
+  export {
30
+    getUserList,
31
+    getUser,
32
+    saveUser,
33
+    updateUser,
34
+    deleteUser,
35
+  }
36
+
37
+  /**
38
+   * 更新用户状态
39
+   * @param {*} id 
40
+   * @param {*} status 
41
+   * @returns 
42
+   */
43
+  export const updateUserStatus = (id, status) => request.put(`/users/${id}/status?status=${status}`);

+ 26
- 2
src/store/models/user.js 查看文件

@@ -1,12 +1,36 @@
1
-import { useState } from "react";
1
+import { useState, useRef } from "react";
2
+import { queryCurrentUser } from "@/services/user";
3
+import { defaultRoutes, authRoutes, mergeAuthRoutes } from '@/routes/routes';
4
+import { getMenuItems } from "@/routes/menus";
5
+import { getAuthedRoutes } from "@/routes/permissions";
2 6
 
3 7
 export default function useUser() {
4
-
5 8
   const [user, setUser] = useState();
9
+  const menusRef = useRef();
10
+  const routesRef = useRef();
11
+
12
+  const getCurrentUser = () => new Promise((resolve, reject) => {
13
+    queryCurrentUser().then(res => {
14
+      const permissions = (res.resourcesList || []).map(x => x.code);
15
+
16
+      // authRoutes 是所有待验证授权的路由
17
+      // authedRoutes 是已经被授权的路由
18
+      const authedRoutes = getAuthedRoutes(authRoutes, permissions);
19
+
20
+      menusRef.current = getMenuItems(authedRoutes);
21
+      routesRef.current = mergeAuthRoutes(defaultRoutes, authedRoutes);
22
+
23
+      setUser(res);
24
+      resolve(res);
25
+    })
26
+  });
6 27
 
7 28
   return {
8 29
     user,
9 30
     setUser,
31
+    getCurrentUser,
32
+    menus: menusRef.current || [],
33
+    routes: routesRef.current || [],
10 34
   }
11 35
 
12 36
 }

+ 1
- 4
src/utils/hooks/useRoute.jsx 查看文件

@@ -1,12 +1,9 @@
1 1
 import { useLocation } from "react-router-dom";
2
-import { getRouteArr } from '@/routes/menus';
2
+import { routeArr } from '@/routes/routes';
3 3
 
4 4
 // 获取当前的 route 信息
5 5
 export default function useRoute() {
6 6
   const location = useLocation();
7
-  const routeArr = getRouteArr();
8
-
9 7
   const currentRoute = routeArr.filter(x => x.path === location.pathname)[0];
10
-
11 8
   return currentRoute;
12 9
 }

+ 75
- 0
src/utils/index.jsx 查看文件

@@ -0,0 +1,75 @@
1
+let toNonExponential = (num) => {
2
+  var m = num.toExponential().match(/\d(?:\.(\d*))?e([+-]\d+)/);
3
+  return num.toFixed(Math.max(0, (m[1] || "").length - m[2]));
4
+};
5
+
6
+/**
7
+ * 乘法 - js运算精度丢失问题
8
+ * @param arg1  数1
9
+ * @param arg2  数2
10
+ * 0.0023 * 100 ==> 0.22999999999999998
11
+ * {{ 0.0023 | multiply(100) }} ==> 0.23
12
+ */
13
+export const floatMultiply = (arg1, arg2) => {
14
+  console.log("==222==");
15
+  arg1 = Number(arg1);
16
+  arg2 = Number(arg2);
17
+  if ((!arg1 && arg1 !== 0) || (!arg2 && arg2 !== 0)) {
18
+    return null;
19
+  }
20
+  arg1 = toNonExponential(arg1);
21
+  arg2 = toNonExponential(arg2);
22
+  let n1, n2;
23
+  let r1, r2; // 小数位数
24
+  try {
25
+    r1 = arg1.toString().split(".")[1].length;
26
+  } catch (e) {
27
+    r1 = 0;
28
+  }
29
+  try {
30
+    r2 = arg2.toString().split(".")[1].length;
31
+  } catch (e) {
32
+    r2 = 0;
33
+  }
34
+  n1 = Number(arg1.toString().replace(".", ""));
35
+  n2 = Number(arg2.toString().replace(".", ""));
36
+  return (n1 * n2) / Math.pow(10, r1 + r2);
37
+};
38
+
39
+/**
40
+ * 除法 - js运算精度丢失问题
41
+ * @param arg1  数1
42
+ * @param arg2  数2
43
+ * 0.0023 / 0.00001 ==> 229.99999999999997
44
+ * {{ 0.0023 | divide(0.00001) }} ==> 230
45
+ */
46
+ export const floatDivide = (arg1, arg2) => {
47
+    arg1 = Number(arg1);
48
+    arg2 = Number(arg2);
49
+    if (!arg2) {
50
+        return null;
51
+    }
52
+    if (!arg1 && arg1!==0) {
53
+        return null;
54
+    }else if(arg1===0) {
55
+        return 0;
56
+    }
57
+    arg1 = toNonExponential(arg1);
58
+    arg2 = toNonExponential(arg2);
59
+    let n1, n2;
60
+    let r1, r2; // 小数位数
61
+    try {
62
+        r1 = arg1.toString().split(".")[1].length;
63
+    } catch (e) {
64
+        r1 = 0;
65
+    }
66
+    try {
67
+        r2 = arg2.toString().split(".")[1].length;
68
+    } catch (e) {
69
+        r2 = 0;
70
+    }
71
+    n1 = Number(arg1.toString().replace(".", ""));
72
+    n2 = Number(arg2.toString().replace(".", ""));
73
+    return floatMultiply((n1 / n2), Math.pow(10, r2 - r1));
74
+    // return (n1 / n2) * Math.pow(10, r2 - r1);   // 直接乘法还是会出现精度问题
75
+}

+ 2
- 2
src/utils/request.js 查看文件

@@ -3,7 +3,7 @@ import { message } from 'antd';
3 3
 
4 4
 const instance = axios.create({
5 5
   baseURL: import.meta.env.VITE_SERVER_BASE,
6
-  timeout: 1000,
6
+  timeout: 10000,
7 7
 });
8 8
 
9 9
 
@@ -118,7 +118,7 @@ export function queryDict(apiRequest) {
118 118
 
119 119
 export function restful(url) {
120 120
   const list = params => instance.get(url, { params, successTip: false });
121
-  const get = id => instance.get(`${url}/${id}`);
121
+  const get = id => instance.get(`${url}/${id}`,{ successTip: false });
122 122
   const add = data => instance.post(url, data);
123 123
   const update = (id, data) => instance.put(`${url}/${id}`, data);
124 124
   const del = id => instance.delete(`${url}/${id}`);

+ 1
- 1
vite.config.js 查看文件

@@ -9,7 +9,7 @@ export default defineConfig({
9 9
     proxy: {
10 10
       '/api/': {
11 11
         // 要代理的地址
12
-        target: 'http://127.0.0.1:8087',
12
+        target: 'http://192.168.89.147:8087',
13 13
         // 配置了这个可以从 http 代理到 https
14 14
         // 依赖 origin 的功能可能需要这个,比如 cookie
15 15
         changeOrigin: true,