张涛 1 年之前
父節點
當前提交
c8010ece84

+ 1
- 1
.env 查看文件

@@ -1,2 +1,2 @@
1
-# VITE_SERVER_BASE=/api/platform
1
+VITE_SERVER_BASE=/api/platform
2 2
 # VITE_LOGIN_URL=http://localhost:3009/admin/

+ 4
- 1
package.json 查看文件

@@ -18,13 +18,16 @@
18 18
     "axios": "^1.2.0",
19 19
     "classnames": "^2.3.2",
20 20
     "echarts": "^5.4.0",
21
+    "i18next": "^23.10.0",
21 22
     "md5": "^2.3.0",
22 23
     "moment": "^2.29.4",
23 24
     "react": "18.2.0",
24 25
     "react-dom": "18.2.0",
25 26
     "react-helmet": "^6.1.0",
27
+    "react-i18next": "^14.0.5",
26 28
     "react-router-dom": "^6.4.2",
27
-    "react-transition-group": "^4.4.5"
29
+    "react-transition-group": "^4.4.5",
30
+    "swiper": "^11.0.7"
28 31
   },
29 32
   "devDependencies": {
30 33
     "@types/react": "18.0.27",

+ 89
- 65
src/components/Page/List.jsx 查看文件

@@ -1,66 +1,93 @@
1
-import React from 'react';
2
-import { Button, Popconfirm, message } from 'antd';
3
-import { ProTable } from '@ant-design/pro-components';
4
-import { queryTable } from '@/utils/request';
5
-import Page from './index';
1
+import React from "react";
2
+import { Button, Popconfirm, message } from "antd";
3
+import { ProTable } from "@ant-design/pro-components";
4
+import { queryTable } from "@/utils/request";
5
+import Page from "./index";
6
+import { useTranslation } from "react-i18next";
7
+import useRoute from "@/routes/hooks/useRoute";
6 8
 
7 9
 export default React.forwardRef((props, ref) => {
8 10
   /**
9 11
    * request 与 ProTable 定义不同,这个只是普通的接口即可
10 12
    */
11
-  const { request, rowKey, columns, onAdd, onDelete, onEdit, toolBarRender, columnOptionRender, ...leftProps } = props;
13
+  const {
14
+    request,
15
+    rowKey,
16
+    columns,
17
+    onAdd,
18
+    onDelete,
19
+    onEdit,
20
+    toolBarRender,
21
+    columnOptionRender,
22
+    ...leftProps
23
+  } = props;
12 24
   const actionRef = React.useRef();
13 25
   const hideRef = React.useRef();
14 26
 
15 27
   const api = React.useMemo(() => queryTable(request), [request]);
16 28
 
29
+  const { t } = useTranslation();
30
+
17 31
   // 统一实现操作列
18
-  const cols = React.useMemo(() => [
19
-    ...columns,
20
-    {
21
-      title: '操作',
22
-      valueType: 'option',
23
-      key: 'option',
24
-      ellipsis: true,
25
-      fixed: 'right',
26
-      render: (_, record) => [
27
-        (
28
-          onEdit &&
29
-          <Button style={{ padding: 0 }} type="link" key={1} onClick={() => onEdit(record)}>
30
-            编辑
31
-          </Button>
32
-        ),
33
-        (
34
-          onDelete &&
35
-          <Popconfirm
36
-            key={3}
37
-            title="您是否确认删除 ?"
38
-            onConfirm={() => onDelete(record)}
39
-            okText="确定"
40
-            cancelText="取消"
41
-          >
42
-            <Button style={{ padding: 0 }} type="link">
43
-              删除
44
-            </Button>
45
-          </Popconfirm>
46
-        ),
47
-        ...(columnOptionRender ? columnOptionRender(_, record) : []),
48
-      ].filter(Boolean),
49
-    },
50
-  ], [columns, onEdit, onDelete]);
32
+  const cols = React.useMemo(
33
+    () => [
34
+      ...columns,
35
+      {
36
+        title: t("operate"),
37
+        valueType: "option",
38
+        key: "option",
39
+        ellipsis: true,
40
+        fixed: "right",
41
+        render: (_, record) =>
42
+          [
43
+            onEdit && (
44
+              <Button
45
+                style={{ padding: 0 }}
46
+                type="link"
47
+                key={1}
48
+                onClick={() => onEdit(record)}
49
+              >
50
+                编辑
51
+              </Button>
52
+            ),
53
+            onDelete && (
54
+              <Popconfirm
55
+                key={3}
56
+                title="您是否确认删除 ?"
57
+                onConfirm={() => onDelete(record)}
58
+                okText="确定"
59
+                cancelText="取消"
60
+              >
61
+                <Button style={{ padding: 0 }} type="link">
62
+                  删除
63
+                </Button>
64
+              </Popconfirm>
65
+            ),
66
+            ...(columnOptionRender ? columnOptionRender(_, record) : []),
67
+          ].filter(Boolean),
68
+      },
69
+    ],
70
+    [columns, onEdit, onDelete]
71
+  );
51 72
 
52
-  React.useImperativeHandle(ref, () => {
53
-    const showLoading = msg => (hideRef.current = message.loading(msg || '操作中, 请稍候...'));
54
-    const hideLoading = () => hideRef.current && hideRef.current();
55
-    const reload = () => actionRef.current?.reload && actionRef.current.reload();
73
+  React.useImperativeHandle(
74
+    ref,
75
+    () => {
76
+      const showLoading = (msg) =>
77
+        (hideRef.current = message.loading(msg || "操作中, 请稍候..."));
78
+      const hideLoading = () => hideRef.current && hideRef.current();
79
+      const reload = () =>
80
+        actionRef.current?.reload && actionRef.current.reload();
56 81
 
57
-    return {
58
-      showLoading,
59
-      hideLoading,
60
-      reload,
61
-      actionRef: actionRef,
62
-    }
63
-  }, []);
82
+      return {
83
+        showLoading,
84
+        hideLoading,
85
+        reload,
86
+        actionRef: actionRef,
87
+      };
88
+    },
89
+    []
90
+  );
64 91
 
65 92
   return (
66 93
     <Page>
@@ -70,21 +97,18 @@ export default React.forwardRef((props, ref) => {
70 97
         request={api}
71 98
         cardBordered
72 99
         actionRef={actionRef}
73
-        toolBarRender={() => [
74
-          (
75
-            onAdd &&
76
-            <Button
77
-              key="1"
78
-              type="primary"
79
-              onClick={onAdd}
80
-            >
81
-              新增
82
-            </Button>
83
-          ),
84
-          ...(toolBarRender ? toolBarRender() : []),
85
-        ].filter(Boolean)}
100
+        toolBarRender={() =>
101
+          [
102
+            onAdd && (
103
+              <Button key="1" type="primary" onClick={onAdd}>
104
+                新增
105
+              </Button>
106
+            ),
107
+            ...(toolBarRender ? toolBarRender() : []),
108
+          ].filter(Boolean)
109
+        }
86 110
         {...leftProps}
87 111
       />
88 112
     </Page>
89
-  )
90
-});
113
+  );
114
+});

+ 18
- 13
src/components/Page/index.jsx 查看文件

@@ -1,23 +1,28 @@
1
-import React from 'react';
2
-import { Typography } from 'antd';
3
-import useRoute from '@/routes/hooks/useRoute';
1
+import React from "react";
2
+import { Typography } from "antd";
3
+import useRoute from "@/routes/hooks/useRoute";
4
+import { useTranslation } from "react-i18next";
4 5
 
5 6
 const pageStyle = {
6
-  margin: '0 24px 24px 24px',
7
+  margin: "0 24px 24px 24px",
7 8
   // margin: '24px',
8
-  minHeight: 'calc(100% - 24px)',
9
-}
9
+  minHeight: "calc(100% - 24px)",
10
+};
10 11
 const { Title } = Typography;
11 12
 
12 13
 export default (props) => {
13
-  const { meta = {} } = useRoute() || {};
14
-  const style = meta.noLayout ? { height: '100%' } : pageStyle;
15
-  const title = props.title || meta.title;
16
-
14
+  const router = useRoute() || {};
15
+  const style = router.meta.noLayout ? { height: "100%" } : pageStyle;
16
+  const title = props.title || router.meta.title;
17
+  const { t } = useTranslation();
17 18
   return (
18 19
     <div style={style}>
19
-      { title && !meta.noLayout && <Title level={3} style={{ paddingBottom: '12px', fontWeight: 400 }}>{ title }</Title> }
20
+      {title && !router.meta.noLayout && (
21
+        <Title level={3} style={{ paddingBottom: "12px", fontWeight: 400 }}>
22
+          {t(`${router.path}.title`)}
23
+        </Title>
24
+      )}
20 25
       {props.children}
21 26
     </div>
22
-  )
23
-}
27
+  );
28
+};

+ 15
- 8
src/layouts/AuthLayout/components/Footer.jsx 查看文件

@@ -1,15 +1,22 @@
1
-import React from 'react';
2
-import { Layout } from 'antd';
3
-import { useModel } from '@/store';
1
+import React from "react";
2
+import { Layout } from "antd";
3
+import { useModel } from "@/store";
4
+import { useTranslation } from "react-i18next";
4 5
 
5 6
 const { Footer } = Layout;
6 7
 
7 8
 const year = new Date().getFullYear();
8 9
 export default React.forwardRef((props, ref) => {
9
-  const { app } = useModel('system');
10
-  const copyRight = `${app.company || '南京云致'} @ ${year}`;
10
+  // const { app } = useModel("system");
11
+  const { t } = useTranslation();
12
+  const copyRight = `${t("companyName") || "南京云致"} @ ${year}`;
11 13
 
12 14
   return (
13
-    <Footer ref={ref} style={{ textAlign: 'center', color: 'rgba(0,0,0, 0.3)' }}>{copyRight}</Footer>
14
-  )
15
-})
15
+    <Footer
16
+      ref={ref}
17
+      style={{ textAlign: "center", color: "rgba(0,0,0, 0.3)" }}
18
+    >
19
+      {copyRight}
20
+    </Footer>
21
+  );
22
+});

+ 38
- 0
src/layouts/AuthLayout/components/Header/Language.jsx 查看文件

@@ -0,0 +1,38 @@
1
+import { Select } from "antd";
2
+import React, { useMemo } from "react";
3
+// import { useModel } from "@/store";
4
+import i18n from '@/locale/config'
5
+
6
+let defaultLang = navigator.language;
7
+if (defaultLang.indexOf('zh') == 0) {
8
+  if (defaultLang == 'zh-CN' || defaultLang == 'zh') {
9
+    defaultLang = 'zh';
10
+  } else {
11
+    defaultLang = 'zht';
12
+  }
13
+} else {
14
+  defaultLang = 'en';
15
+}
16
+
17
+export default (props) => {
18
+  const { style } = props;
19
+
20
+  const styles = useMemo(() => {
21
+    return { ...style, width: 120 };
22
+  }, []);
23
+
24
+
25
+  console.log(styles)
26
+
27
+  const language = [
28
+    { label: "中文简体", value: "zh" },
29
+    { label: "中文繁体", value: "zht" },
30
+    { label: "英文", value: "en" },
31
+  ];
32
+
33
+  const onChange = (v) => {
34
+    i18n.changeLanguage(v);
35
+  }
36
+
37
+  return <Select defaultValue={defaultLang} style={styles} options={language} onChange={onChange} />;
38
+};

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

@@ -1,28 +1,34 @@
1
-import React, { useMemo } from 'react';
2
-import { Layout, Space } from 'antd';
3
-import classNames from 'classnames';
4
-import Title from './Title';
5
-import User from './User';
6
-import Exit from './Exit';
7
-import Logo from '../Logo';
1
+import React, { useMemo } from "react";
2
+import { Layout, Space } from "antd";
3
+import classNames from "classnames";
4
+import Title from "./Title";
5
+import User from "./User";
6
+import Exit from "./Exit";
7
+import Logo from "../Logo";
8
+import Language from "./Language";
8 9
 
9 10
 const { Header } = Layout;
10 11
 
11 12
 export default (props) => {
12 13
   const { user } = props;
13 14
 
14
-  const className = useMemo(() => classNames({
15
-    'layout-header': true,
16
-    'light': props.theme === 'light',
17
-  }), [props.theme]);
15
+  const className = useMemo(
16
+    () =>
17
+      classNames({
18
+        "layout-header": true,
19
+        light: props.theme === "light",
20
+      }),
21
+    [props.theme]
22
+  );
18 23
 
19 24
   return (
20 25
     <Header className={className}>
21 26
       <Logo />
22 27
       <Space>
28
+        <Language />
23 29
         <User user={user} />
24 30
         <Exit />
25 31
       </Space>
26 32
     </Header>
27
-  )
28
-}
33
+  );
34
+};

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

@@ -2,15 +2,17 @@ import React from 'react';
2 2
 import { Typography } from 'antd';
3 3
 import { NavLink } from "react-router-dom";
4 4
 import { useModel } from '@/store';
5
+import { useTranslation } from 'react-i18next';
5 6
 
6 7
 export default (props) => {
7 8
   const { app } = useModel('system');
9
+  const { t } = useTranslation();
8 10
 
9 11
   return (
10 12
     // <NavLink className='logo'  to="/">
11 13
     <div className="logo">
12 14
       <img src="./logo.png" alt="" />
13
-      <h3>{app.shorName}</h3>
15
+      <h3>{t("fullName")}</h3>
14 16
     </div>
15 17
     // </NavLink>
16 18
   )

+ 26
- 9
src/layouts/AuthLayout/components/SiderBar.jsx 查看文件

@@ -1,21 +1,38 @@
1
-import React, { useMemo } from 'react';
2
-import { Layout, Spin } from 'antd';
3
-import { getPropertyValue } from '@/utils/css';
4
-import Menus from './Menus';
1
+import React, { useEffect, useMemo, useState } from "react";
2
+import { Layout, Spin } from "antd";
3
+import { getPropertyValue } from "@/utils/css";
4
+import Menus from "./Menus";
5
+import { useTranslation } from "react-i18next";
5 6
 
6 7
 const { Sider } = Layout;
7 8
 
8
-
9 9
 export default (props) => {
10 10
   const { theme, location, menus } = props;
11
+  // const [menysTranslation, setMenysTranslation] = useState([]);
12
+  const { t } = useTranslation();
11 13
 
12 14
   const width = useMemo(() => {
13
-    return /\d+/.exec(getPropertyValue('--siderbar-width'))[0] - 0;
15
+    return /\d+/.exec(getPropertyValue("--siderbar-width"))[0] - 0;
14 16
   }, []);
15 17
 
18
+  const menysTranslation = (menus || []).map((x) => {
19
+    return {
20
+      ...x,
21
+      label: {
22
+        ...x.label,
23
+        props: {
24
+          ...x.label.props,
25
+          children: t(`${x.path}.title`),
26
+        },
27
+      },
28
+    };
29
+  });
30
+  console.log(menus);
31
+  console.log(menysTranslation);
32
+  // setMenysTranslation(data);
16 33
   return (
17
-    <Sider className='layout-sidebar' theme={theme} collapsible width={width}>
18
-      <Menus theme={theme} items={menus} location={location} />
34
+    <Sider className="layout-sidebar" theme={theme} collapsible width={width}>
35
+      <Menus theme={theme} items={menysTranslation} location={"/company"} />
19 36
     </Sider>
20 37
   );
21
-}
38
+};

+ 39
- 0
src/locale/config.js 查看文件

@@ -0,0 +1,39 @@
1
+import i18n from 'i18next';
2
+import { initReactI18next } from 'react-i18next';
3
+import translation_en from './en.json';
4
+import translation_zh from './zh.json';
5
+import translation_zht from './zht.json';
6
+
7
+const resources = {
8
+    en: {
9
+        translation: translation_en,
10
+    },
11
+    zh: {
12
+        translation: translation_zh,
13
+    },
14
+    zht: {
15
+        translation: translation_zht,
16
+    },
17
+};
18
+
19
+let defaultLang = navigator.language;
20
+if (defaultLang.indexOf('zh') == 0) {
21
+    if (defaultLang == 'zh-CN' || defaultLang == 'zh') {
22
+        defaultLang = 'zh';
23
+    } else {
24
+        defaultLang = 'zht';
25
+    }
26
+} else {
27
+    defaultLang = 'en';
28
+}
29
+
30
+i18n.use(initReactI18next).init({
31
+    resources,
32
+    // 默认语言  zh/en/zht  中文/英文/繁体
33
+    lng: defaultLang,
34
+    interpolation: {
35
+        escapeValue: false,
36
+    },
37
+});
38
+
39
+export default i18n;

+ 26
- 0
src/locale/en.json 查看文件

@@ -0,0 +1,26 @@
1
+{
2
+  "language": "Language",
3
+  "switch": "Switch",
4
+  "fullName": "platform management system",
5
+  "companyName": "yun zhi",
6
+  "operate": "operate",
7
+  "details": "details",
8
+  "fxuser": {
9
+    "title": "User Management"
10
+  },
11
+  "fxuser/edit": {
12
+    "title": "User maintenance"
13
+  },
14
+
15
+  "company": {
16
+    "title": "Lists of companies",
17
+    "registrationLocation":"Enterprise registration location",
18
+    "companyNameCn": "Chinese name of registration location",
19
+    "companyNameEn": "Enterprise English name",
20
+    "isListedCompany": "Is it a listed company",
21
+    "companyType": "Company type"
22
+  },
23
+  "company/edit": {
24
+    "title": "Company Details"
25
+  }
26
+}

+ 26
- 0
src/locale/zh.json 查看文件

@@ -0,0 +1,26 @@
1
+{
2
+  "language": "语言",
3
+  "switch": "选择",
4
+  "fullName": "平台管理系统",
5
+  "companyName": "云致",
6
+  "operate": "操作",
7
+  "details": "详情",
8
+  "fxuser": {
9
+    "title": "用户管理"
10
+  },
11
+  "fxuser/edit": {
12
+    "title": "用户维护"
13
+  },
14
+
15
+  "company": {
16
+    "title": "公司列表",
17
+    "registrationLocation": "企业注册地",
18
+    "companyNameCn": "企业中文名",
19
+    "companyNameEn": "企业英文名",
20
+    "isListedCompany": "是否为上市企业",
21
+    "companyType": "企业类型"
22
+  },
23
+  "company/edit": {
24
+    "title": "公司详情"
25
+  }
26
+}

+ 26
- 0
src/locale/zht.json 查看文件

@@ -0,0 +1,26 @@
1
+{
2
+  "language": "語言",
3
+  "switch": "選擇",
4
+  "fullName": "平台管理系統",
5
+  "companyName": "雲致",
6
+  "operate": "操作",
7
+  "details": "詳情",
8
+  "fxuser": {
9
+    "title": "用戶管理"
10
+  },
11
+  "fxuser/edit": {
12
+    "title": "用戶維護"
13
+  },
14
+
15
+  "company": {
16
+    "title": "公司列表",
17
+    "registrationLocation": "企業註冊地",
18
+    "companyNameCn": "註冊地中文名",
19
+    "companyNameEn": "企業英文名",
20
+    "isListedCompany": "是否為上市企業",
21
+    "otherAnnex": "公司類型"
22
+  },
23
+  "company/edit": {
24
+    "title": "公司詳情"
25
+  }
26
+}

+ 308
- 0
src/pages/company/Edit.jsx 查看文件

@@ -0,0 +1,308 @@
1
+import Page from "@/components/Page";
2
+import {
3
+  PrivateIndividualsShareCapitalUnlimitedCompany,
4
+  PrivateSharesCorporation,
5
+  PublicShareCapitalUnlimitedCompany,
6
+  PublicSharesCorporation,
7
+} from "@/utils/company";
8
+import { ProCard, ProDescriptions } from "@ant-design/pro-components";
9
+import {
10
+  Badge,
11
+  Button,
12
+  Card,
13
+  Col,
14
+  Descriptions,
15
+  Image,
16
+  Row,
17
+  Space,
18
+} from "antd";
19
+import React, { useEffect, useState } from "react";
20
+import { useSearchParams } from "react-router-dom";
21
+import UploadFile from "./component/UploadFile";
22
+import PreviewModal from "./component/PreviewModal";
23
+import {
24
+  getTaCompanyRealName,
25
+  getTaCompanyRealNameId,
26
+} from "@/services/companyreal";
27
+import { DownloadOutlined } from "@ant-design/icons";
28
+export default (props) => {
29
+  const [searchParams] = useSearchParams();
30
+  const id = searchParams.get("id");
31
+  const [companyList, setCompanyList] = useState({});
32
+  const requestApi = async () => {
33
+    const res = await getTaCompanyRealNameId(id);
34
+    const data = {
35
+      success: true,
36
+      data: {
37
+        ...res,
38
+        ...(res.taCompanyInfo || {}),
39
+      },
40
+    };
41
+    console.log(data);
42
+    return data;
43
+  };
44
+
45
+  const classification = (value) => {
46
+    const str = value;
47
+    const fileExtension = str.substring(str.lastIndexOf(".") + 1);
48
+
49
+    if (fileExtension == "rar" || fileExtension == "zip") {
50
+      return (
51
+        <div>
52
+          <div>{value}</div>
53
+          <Button
54
+            href={value}
55
+            type="primary"
56
+            shape="round"
57
+            icon={<DownloadOutlined />}
58
+            size="middle"
59
+          />
60
+        </div>
61
+      );
62
+    } else if (
63
+      fileExtension == "jpg" ||
64
+      fileExtension == "jpeg" ||
65
+      fileExtension == "png"
66
+    ) {
67
+      return <img src={value} alt="" height="100" width="100" />;
68
+    } else if (fileExtension == "xlsx" || fileExtension == "xls") {
69
+      <div>
70
+        <div>{value}</div>
71
+        <Button
72
+          href={value}
73
+          type="primary"
74
+          shape="round"
75
+          icon={<DownloadOutlined />}
76
+          size="middle"
77
+        />
78
+      </div>;
79
+    } else if (fileExtension == "pdf") {
80
+      <iframe src={value} height="100%" width="100%"></iframe>;
81
+    }
82
+  };
83
+
84
+  const columns = [
85
+    {
86
+      title: "企业注册地",
87
+      dataIndex: "registrationLocation",
88
+    },
89
+    {
90
+      title: "企业中文名",
91
+      dataIndex: "companyNameCn",
92
+    },
93
+    {
94
+      title: "企业英文名",
95
+      dataIndex: "companyNameEn",
96
+    },
97
+    {
98
+      title: "是否为上市企业",
99
+      dataIndex: "isListedCompany",
100
+      valueType: "select",
101
+      valueEnum: {
102
+        0: { text: "否" },
103
+        1: { text: "是" },
104
+      },
105
+    },
106
+
107
+    {
108
+      title: "企业类型",
109
+      dataIndex: "companyType",
110
+      valueType: "select",
111
+      // render: (_, r) => {
112
+      //   return r.taCompanyInfo?.companyType;
113
+      // },
114
+      valueEnum: {
115
+        1: {
116
+          text: PrivateSharesCorporation,
117
+        },
118
+        2: {
119
+          text: PublicSharesCorporation,
120
+        },
121
+        3: {
122
+          text: PublicShareCapitalUnlimitedCompany,
123
+        },
124
+        4: {
125
+          text: PrivateIndividualsShareCapitalUnlimitedCompany,
126
+        },
127
+      },
128
+    },
129
+    {
130
+      title: "商业登记号",
131
+      dataIndex: "businessRegistrationNum",
132
+    },
133
+    {
134
+      title: "公司注册证书",
135
+      dataIndex: "certificateIncorporation",
136
+      render: (_, r) => {
137
+        return classification(r?.certificateIncorporation);
138
+      },
139
+    },
140
+    {
141
+      title: "公司商业登记证",
142
+      dataIndex: "companyBusinessRegistration",
143
+      render: (_, r) => {
144
+        return classification(r?.companyBusinessRegistration);
145
+      },
146
+    },
147
+    {
148
+      title: "nnc1/最新nar1",
149
+      // valueType: "image",
150
+      dataIndex: "nnc1Nar1",
151
+      render: (_, r) => {
152
+        return classification(r?.nnc1Nar1);
153
+      },
154
+    },
155
+    {
156
+      title: "其他附件",
157
+      dataIndex: "otherAnnex",
158
+      render: (_, r) => {
159
+        return classification(r?.otherAnnex);
160
+      },
161
+    },
162
+    {
163
+      title: "注册-国家",
164
+      dataIndex: "registerCountry",
165
+    },
166
+    {
167
+      title: "注册-省/地区",
168
+      dataIndex: "registerProvinceRegion",
169
+      // render: (_, r) => {
170
+      //   return r.taCompanyInfo?.registerProvinceRegion;
171
+      // },
172
+    },
173
+    {
174
+      title: "注册-市",
175
+      dataIndex: "registerCity",
176
+      // render: (_, r) => {
177
+      //   return r.taCompanyInfo?.registerCity;
178
+      // },
179
+    },
180
+    {
181
+      title: "注册-具体地址",
182
+      dataIndex: "registerSpecificAddress",
183
+      // render: (_, r) => {
184
+      //   return r.taCompanyInfo?.registerSpecificAddress;
185
+      // },
186
+    },
187
+    {
188
+      title: "注册-邮政编码",
189
+      dataIndex: "registerPostalCode",
190
+      // render: (_, r) => {
191
+      //   return r.taCompanyInfo?.registerPostalCode;
192
+      // },
193
+    },
194
+    {
195
+      title: "实际-国家",
196
+      dataIndex: "realityCountry",
197
+      // render: (_, r) => {
198
+      //   return r.taCompanyInfo?.realityCountry;
199
+      // },
200
+    },
201
+    {
202
+      title: "实际-省/地区",
203
+      dataIndex: "realityProvinceRegion",
204
+      // render: (_, r) => {
205
+      //   return r.taCompanyInfo?.realityProvinceRegion;
206
+      // },
207
+    },
208
+    {
209
+      title: "实际-市",
210
+      dataIndex: "realityCity",
211
+      // render: (_, r) => {
212
+      //   return r.taCompanyInfo?.realityCity;
213
+      // },
214
+    },
215
+    {
216
+      title: "实际-具体地址",
217
+      dataIndex: "realitySpecificAddress",
218
+      // render: (_, r) => {
219
+      //   return r.taCompanyInfo?.realitySpecificAddress;
220
+      // },
221
+    },
222
+    {
223
+      title: "实际-邮政编码",
224
+      dataIndex: "realityPostalCode",
225
+      // render: (_, r) => {
226
+      //   return r.taCompanyInfo?.realityPostalCode;
227
+      // },
228
+    },
229
+    {
230
+      title: "雇员人数",
231
+      dataIndex: "numEmployees",
232
+      valueType: "select",
233
+      // render: (_, r) => {
234
+      //   return r.taCompanyInfo?.numEmployees;
235
+      // },
236
+      valueEnum: {
237
+        1: { text: "员工人数超过200人" },
238
+        2: { text: "员工人数50-200人" },
239
+        3: { text: "员工人数少于50人" },
240
+      },
241
+    },
242
+    {
243
+      title: "经营场所类型",
244
+      dataIndex: "businessPremisesType",
245
+      valueType: "select",
246
+      // render: (_, r) => {
247
+      //   return r.taCompanyInfo?.businessPremisesType;
248
+      // },
249
+      valueEnum: {
250
+        1: { text: "自有办公场所" },
251
+        2: { text: "较大租用办公场所 (大于500平方)" },
252
+        3: { text: "较小租用办公场所 (小于500平方)" },
253
+        4: { text: "无固定办公场所" },
254
+      },
255
+    },
256
+    {
257
+      title: "受益人",
258
+      dataIndex: "taCompanyBeneficiaryInfoList",
259
+      render: (_, r) => {
260
+        const data = (r.taCompanyBeneficiaryInfoList || []).map(
261
+          (x) => x.beneficiaryName
262
+        );
263
+        // console.log(data);
264
+        return data.join(" ,");
265
+      },
266
+    },
267
+    {
268
+      title: "董事",
269
+      dataIndex: "taCompanyLegalInfoList",
270
+      render: (_, r) => {
271
+        const data = (r.taCompanyLegalInfoList || []).map(
272
+          (x) => x.directorNameCn
273
+        );
274
+        return data.join(" ,");
275
+      },
276
+    },
277
+  ];
278
+  const [isModalOpen, setIsModalOpen] = React.useState(false);
279
+  const [content, setContent] = React.useState();
280
+
281
+  const onOpen = () => {
282
+    setIsModalOpen(true);
283
+  };
284
+  const onClose = () => {
285
+    setIsModalOpen(false);
286
+  };
287
+
288
+  return (
289
+    <Page>
290
+      <Row>
291
+        <ProCard gutter={20}>
292
+          <ProDescriptions
293
+            layout="vertical"
294
+            bordered
295
+            title="公司基本信息"
296
+            request={requestApi}
297
+            columns={columns}
298
+          ></ProDescriptions>
299
+        </ProCard>
300
+      </Row>
301
+      {/* <PreviewModal
302
+        isModalOpen={isModalOpen}
303
+        onClose={onClose}
304
+        versionInfo={content}
305
+      /> */}
306
+    </Page>
307
+  );
308
+};

+ 40
- 0
src/pages/company/component/PreviewModal.jsx 查看文件

@@ -0,0 +1,40 @@
1
+import React, { useState } from 'react';
2
+import { Modal, Image } from 'antd';
3
+import { Swiper, SwiperSlide } from 'swiper/react';
4
+// Import Swiper styles
5
+import 'swiper/css';
6
+import 'swiper/css/effect-fade';
7
+import 'swiper/css/navigation';
8
+import 'swiper/css/pagination';
9
+// import 'style.module.less';
10
+
11
+// import required modules
12
+import { Pagination } from 'swiper/modules';
13
+
14
+export default (props) => {
15
+  const { versionInfo, isModalOpen, onClose, width = 1200, ...leftProps } = props;
16
+
17
+  return (
18
+    <Modal keyboard footer={null} width={width} open={isModalOpen} onCancel={onClose} {...leftProps}>
19
+      <Swiper
20
+        spaceBetween={30}
21
+        effect={'fade'}
22
+        pagination={{
23
+          clickable: true,
24
+        }}
25
+        modules={[Pagination]}
26
+        className="mySwiper"
27
+      >
28
+        {
29
+          (versionInfo || []).map((x, index) => {
30
+            return (
31
+              <SwiperSlide>
32
+                <img src={x} key={index} style={{ width: '100%' }} alt="fail" />
33
+              </SwiperSlide>
34
+            )
35
+          })
36
+        }
37
+      </Swiper>
38
+    </Modal>
39
+  )
40
+}

+ 72
- 0
src/pages/company/component/UploadFile.jsx 查看文件

@@ -0,0 +1,72 @@
1
+import React from "react";
2
+import { UploadOutlined } from "@ant-design/icons";
3
+import { Button } from "antd";
4
+import Upload from "@/components/Upload/Upload";
5
+
6
+const getFileInfo = (x) => ({
7
+  uid: x,
8
+  name: x?.split("/")?.at(-1)?.replace(/^\d+-/, ""),
9
+  status: "done",
10
+  thumbUrl: x,
11
+  url: x,
12
+});
13
+
14
+export default (props) => {
15
+  const { value, onChange, onPreview, versionInfo, accept = ".ppt,.pptx,.pdf" } = props;
16
+
17
+  const [loading, setLoading] = React.useState(false);
18
+
19
+  const [fileList, setFileList] = React.useState([]);
20
+
21
+
22
+  const handleChange = (info) => {
23
+    const newFileList = [...info.fileList];
24
+
25
+    if (info.file.status === "uploading") {
26
+      setLoading(true);
27
+    } else if (info.file.status === "error") {
28
+      setLoading(false);
29
+    } else if (info.file.status === "done") {
30
+      setLoading(false);
31
+      const { url, fileType } = info.file.response;
32
+      if (onChange) {
33
+        onChange(url, fileType);
34
+      }
35
+    }
36
+
37
+    setFileList(newFileList);
38
+  };
39
+
40
+  const handlePreview = (e) => {
41
+    e.preventDefault();
42
+    e.stopPropagation();
43
+    onPreview();
44
+  }
45
+
46
+  React.useEffect(() => {
47
+    if (!value) {
48
+      setFileList([]);
49
+    } else {
50
+      setFileList([getFileInfo(value)]);
51
+    }
52
+  }, [value]);
53
+
54
+  // console.log('versionInfo', versionInfo)
55
+
56
+  return (
57
+    <>
58
+      <Upload
59
+        accept={accept}
60
+        fileList={fileList}
61
+        onChange={handleChange}
62
+      >
63
+        <Button loading={loading} icon={<UploadOutlined />}>
64
+          点击上传文件
65
+        </Button>
66
+        {
67
+          versionInfo ? <Button type="link" onClick={handlePreview} >预览</Button> : null
68
+        }
69
+      </Upload>
70
+    </>
71
+  );
72
+};

+ 141
- 0
src/pages/company/index.jsx 查看文件

@@ -0,0 +1,141 @@
1
+import List from "@/components/Page/List";
2
+import { deleteTaCompanyInfo, getTaCompanyInfo } from "@/services/company";
3
+import { getTaCompanyRealName } from "@/services/companyreal";
4
+import {
5
+  PrivateIndividualsShareCapitalUnlimitedCompany,
6
+  PrivateSharesCorporation,
7
+  PublicShareCapitalUnlimitedCompany,
8
+  PublicSharesCorporation,
9
+} from "@/utils/company";
10
+import { Button } from "antd";
11
+import React, { useRef } from "react";
12
+import { useTranslation } from "react-i18next";
13
+import { useNavigate } from "react-router-dom";
14
+export default (props) => {
15
+  const actionRef = useRef();
16
+
17
+  const navigate = useNavigate();
18
+  const { t } = useTranslation();
19
+  const columns = [
20
+    {
21
+      title: t("company.registrationLocation"),
22
+      dataIndex: "registrationLocation",
23
+    },
24
+    {
25
+      title: t("company.companyNameCn"),
26
+      dataIndex: "companyNameCn",
27
+    },
28
+    {
29
+      title: t("company.companyNameEn"),
30
+      dataIndex: "companyNameEn",
31
+    },
32
+    {
33
+      title: t("company.isListedCompany"),
34
+      valueType: "select",
35
+      dataIndex: "isListedCompany",
36
+      valueEnum: {
37
+        0: {
38
+          text: "否",
39
+        },
40
+        1: {
41
+          text: "是",
42
+        },
43
+      },
44
+    },
45
+    // {
46
+    //   title: t("company.companyType"),
47
+    //   valueType: "select",
48
+    //   dataIndex: "companyType",
49
+    //   render: (_, r) => {
50
+    //     return r.companyType == 1
51
+    //       ? PrivateSharesCorporation
52
+    //       : r.companyType == 2
53
+    //       ? PublicSharesCorporation
54
+    //       : r.companyType == 3
55
+    //       ? PublicShareCapitalUnlimitedCompany
56
+    //       : r.companyType == 4
57
+    //       ? PrivateIndividualsShareCapitalUnlimitedCompany
58
+    //       : "-";
59
+    //   },
60
+    //   valueEnum: {
61
+    //     1: {
62
+    //       text: PrivateSharesCorporation,
63
+    //     },
64
+    //     2: {
65
+    //       text: PublicSharesCorporation,
66
+    //     },
67
+    //     3: {
68
+    //       text: PublicShareCapitalUnlimitedCompany,
69
+    //     },
70
+    //     4: {
71
+    //       text: PrivateIndividualsShareCapitalUnlimitedCompany,
72
+    //     },
73
+    //   },
74
+    // },
75
+    // {
76
+    //   title: "公司注册证书",
77
+    //   valueType: "image",
78
+    //   dataIndex: "certificateIncorporation",
79
+    // },
80
+    // {
81
+    //   title: "公司商业登记证",
82
+    //   valueType: "image",
83
+    //   dataIndex: "companyBusinessRegistration",
84
+    // },
85
+    // {
86
+    //   title: "nnc1/最新nar1",
87
+    //   valueType: "image",
88
+    //   dataIndex: "nnc1Nar1",
89
+    // },
90
+    // {
91
+    //   title: "其他附件",
92
+    //   valueType: "image",
93
+    //   dataIndex: "otherAnnex",
94
+    // },
95
+    // {
96
+    //   title: "商业登记号",
97
+    //   dataIndex: "businessRegistrationNum",
98
+    // },
99
+    // {
100
+    //   title: "注册国家",
101
+    //   dataIndex: "registerCountry",
102
+    // },
103
+    // {
104
+    //   title: "注册省/地区",
105
+    //   dataIndex: "registerProvinceRegion",
106
+    // }, {
107
+    //     title: "注册市",
108
+    //     dataIndex: "registerCity",
109
+    //   },
110
+  ];
111
+  return (
112
+    <List
113
+      rowKey="realCompanyId"
114
+      actionRef={actionRef}
115
+      request={getTaCompanyRealName}
116
+      columns={columns}
117
+      columnOptionRender={(_, r) => [
118
+        <Button
119
+          style={{ padding: 0 }}
120
+          type="link"
121
+          key={1}
122
+          onClick={() => navigate(`/company/edit?id=${r.realCompanyId}`)}
123
+        >
124
+          {t("details")}
125
+        </Button>,
126
+      ]}
127
+
128
+      // toolBarRender={() => [
129
+      //   <Button
130
+      //     key="1"
131
+      //     type="primary"
132
+      //     onClick={() => {
133
+      //       navigate("edit");
134
+      //     }}
135
+      //   >
136
+      //     新增
137
+      //   </Button>,
138
+      // ]}
139
+    />
140
+  );
141
+};

+ 10
- 10
src/pages/tenant/index.jsx 查看文件

@@ -51,16 +51,16 @@ export default (props) => {
51 51
       title: "手机号",
52 52
       dataIndex: "phone",
53 53
     },
54
-    {
55
-      title: "登录地址",
56
-      dataIndex: "tenantId",
57
-      render: (_, record) => {
58
-        const url = `${import.meta.env.VITE_LOGIN_URL}#/${record.tenantId}/login`
59
-        console.log(url)
60
-        return <Text copyable={{ text: url }} ellipsis={true}>{url}</Text>;
61
-      },
62
-      search: false,
63
-    },
54
+    // {
55
+    //   title: "登录地址",
56
+    //   dataIndex: "tenantId",
57
+    //   render: (_, record) => {
58
+    //     const url = `${import.meta.env.VITE_LOGIN_URL}#/${record.tenantId}/login`
59
+    //     console.log(url)
60
+    //     return <Text copyable={{ text: url }} ellipsis={true}>{url}</Text>;
61
+    //   },
62
+    //   search: false,
63
+    // },
64 64
     {
65 65
       title: "状态",
66 66
       dataIndex: "status",

+ 47
- 35
src/routes/menus.jsx 查看文件

@@ -1,44 +1,56 @@
1
-import { Link } from 'react-router-dom';
2
-import { getPath } from './utils';
1
+import { Link } from "react-router-dom";
2
+import { getPath } from "./utils";
3
+import { useTranslation } from "react-i18next";
3 4
 
4 5
 // 菜单是否显示
5 6
 // 没有 meta 或者 meta.title 为空, 或者 meta.hideInMenu = true 的 都不显示
6
-const isShow = item => item.meta && item.meta.title && !item.meta.hideInMenu;
7
+const isShow = (item) => item.meta && item.meta.title && !item.meta.hideInMenu;
7 8
 
8 9
 const hasChildren = (list) => {
9 10
   if (!list || list.length < 1) return false;
10 11
 
11 12
   // 如果子元素全部都是不显示的, 说明子菜单不需要显示
12
-  return list.filter(it => !isShow(it)).length !== list.length;
13
-}
14
-
15
-export const getMenuItems = (routes = [], fullPath = '/') => {
16
-  return routes.map(route => {
17
-    const path = getPath(fullPath, route.path);
18
-
19
-    //
20
-    if (!isShow(route)) return false;
21
-    
22
-    const children = hasChildren(route.children) ? getMenuItems(route.children, path) : false;
23
-
24
-    const { target, title, icon } = route.meta || {}
25
-
26
-    // 坑爹 react-router v6 不支持 hash 路由的 target 跳转
27
-    const label = target === '_blank' ?
28
-      <a href={`${window.location.pathname}#${path}`} target={target}>{title}</a>
29
-      : (
30
-        path.indexOf('http') === 0  ? <a href={path} target="_blank">{title}</a>
31
-        : <Link to={path} target={target}>{title}</Link>
13
+  return list.filter((it) => !isShow(it)).length !== list.length;
14
+};
15
+
16
+export const getMenuItems = (routes = [], fullPath = "/") => {
17
+  return routes
18
+    .map((route) => {
19
+      const path = getPath(fullPath, route.path);
20
+
21
+      //
22
+      if (!isShow(route)) return false;
23
+
24
+      const children = hasChildren(route.children)
25
+        ? getMenuItems(route.children, path)
26
+        : false;
27
+
28
+      const { target, title, icon } = route.meta || {};
29
+      // 坑爹 react-router v6 不支持 hash 路由的 target 跳转
30
+      const label =
31
+        target === "_blank" ? (
32
+          <a href={`${window.location.pathname}#${path}`} target={target}>
33
+            {title}
34
+          </a>
35
+        ) : path.indexOf("http") === 0 ? (
36
+          <a href={path} target="_blank">
37
+            {title}
38
+          </a>
39
+        ) : (
40
+          <Link to={path} target={target}>
41
+            {title}
42
+          </Link>
43
+        );
44
+      return Object.assign(
45
+        {
46
+          key: path,
47
+          path:route.path,
48
+          label,
49
+          title,
50
+          icon,
51
+        },
52
+        children && { children }
32 53
       );
33
-
34
-    return Object.assign(
35
-      {
36
-        key: path,
37
-        label,
38
-        title,
39
-        icon,
40
-      },
41
-      children && { children },
42
-    )
43
-  }).filter(Boolean);
44
-}
54
+    })
55
+    .filter(Boolean);
56
+};

+ 25
- 6
src/routes/routes.jsx 查看文件

@@ -11,6 +11,9 @@ import FxUser from "@/pages/fxuser";
11 11
 import FxUserEdit from "@/pages/fxuser/Edit";
12 12
 import Tenant from "@/pages/tenant";
13 13
 import TenantEdit from "@/pages/tenant/Edit";
14
+import Company from "@/pages/company";
15
+import CompanyEdit from "@/pages/company/Edit";
16
+
14 17
 /**
15 18
  * meta 用来扩展自定义数据数据
16 19
  * {
@@ -45,19 +48,35 @@ export const authRoutes = [
45 48
       hideInMenu: true,
46 49
     },
47 50
   },
51
+  // {
52
+  //   path: "tenant",
53
+  //   element: <Tenant />,
54
+  //   meta: {
55
+  //     title: "租户管理",
56
+  //     icon: <UserOutlined />,
57
+  //   },
58
+  // },
59
+  // {
60
+  //   path: "tenant/edit",
61
+  //   element: <TenantEdit />,
62
+  //   meta: {
63
+  //     title: "租户维护",
64
+  //     hideInMenu: true,
65
+  //   },
66
+  // },
48 67
   {
49
-    path: "tenant",
50
-    element: <Tenant />,
68
+    path: "company",
69
+    element: <Company />,
51 70
     meta: {
52
-      title: "租户管理",
71
+      title: "公司列表",
53 72
       icon: <UserOutlined />,
54 73
     },
55 74
   },
56 75
   {
57
-    path: "tenant/edit",
58
-    element: <TenantEdit />,
76
+    path: "company/edit",
77
+    element: <CompanyEdit />,
59 78
     meta: {
60
-      title: "租户维护",
79
+      title: "公司详情",
61 80
       hideInMenu: true,
62 81
     },
63 82
   },

+ 26
- 0
src/services/company.js 查看文件

@@ -0,0 +1,26 @@
1
+import request from "@/utils/request";
2
+
3
+/*
4
+ * 分页查询
5
+ */
6
+export const getTaCompanyInfo = (params) => request(`/taCompanyInfo`, { params });
7
+
8
+/*
9
+ * 新增数据
10
+ */
11
+export const postTaCompanyInfo = (data) => request('/taCompanyInfo', { data, method: 'post' });
12
+
13
+/*
14
+ * 更新数据
15
+ */
16
+export const putTaCompanyInfo = (id, data) => request(`/taCompanyInfo/${id}`, { data, method: 'put' });
17
+
18
+/*
19
+ * 通过主键删除数据
20
+ */
21
+export const deleteTaCompanyInfo = (id) => request(`/taCompanyInfo/${id}`, { method: 'delete' });
22
+
23
+/*
24
+ * 通过ID查询单条数据
25
+ */
26
+export const getTaCompanyInfoId = (id) => request(`/taCompanyInfo/${id}`);

+ 26
- 0
src/services/companyreal.js 查看文件

@@ -0,0 +1,26 @@
1
+import request from "@/utils/request";
2
+
3
+/*
4
+ * 分页查询
5
+ */
6
+export const getTaCompanyRealName = (params) => request(`/taCompanyRealName`, { params });
7
+
8
+/*
9
+ * 新增数据
10
+ */
11
+export const postTaCompanyRealName = (data) => request('/taCompanyRealName', { data, method: 'post' });
12
+
13
+/*
14
+ * 更新数据
15
+ */
16
+export const putTaCompanyRealName = (id, data) => request(`/taCompanyRealName/${id}`, { data, method: 'put' });
17
+
18
+/*
19
+ * 通过主键删除数据
20
+ */
21
+export const deleteTaCompanyRealName = (id) => request(`/taCompanyRealName/${id}`, { method: 'delete' });
22
+
23
+/*
24
+ * 通过ID查询单条数据
25
+ */
26
+export const getTaCompanyRealNameId = (id) => request(`/taCompanyRealName/${id}`);

+ 5
- 0
src/utils/company.js 查看文件

@@ -0,0 +1,5 @@
1
+
2
+export const PrivateSharesCorporation = "私人股份有限公司";
3
+export const PublicSharesCorporation = "公众股份有限公司";
4
+export const PublicShareCapitalUnlimitedCompany = "有股本的公众无限公司";
5
+export const PrivateIndividualsShareCapitalUnlimitedCompany = "有股本的私人无限公司";

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

@@ -11,7 +11,7 @@ export default defineConfig({
11 11
     host: "0.0.0.0",
12 12
     proxy: {
13 13
       '/api/platform': {
14
-        target: 'http://192.168.89.25:9110',
14
+        target: 'http://192.168.89.13:9110',
15 15
         changeOrigin: true,
16 16
       },
17 17
     },

+ 44
- 0
yarn.lock 查看文件

@@ -455,6 +455,13 @@
455 455
   dependencies:
456 456
     regenerator-runtime "^0.13.11"
457 457
 
458
+"@babel/runtime@^7.23.2", "@babel/runtime@^7.23.9":
459
+  version "7.24.0"
460
+  resolved "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.24.0.tgz#584c450063ffda59697021430cb47101b085951e"
461
+  integrity sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==
462
+  dependencies:
463
+    regenerator-runtime "^0.14.0"
464
+
458 465
 "@babel/template@^7.20.7", "@babel/template@^7.21.9":
459 466
   version "7.21.9"
460 467
   resolved "https://registry.npmmirror.com/@babel/template/-/template-7.21.9.tgz"
@@ -1312,6 +1319,13 @@ has@^1.0.3:
1312 1319
   dependencies:
1313 1320
     function-bind "^1.1.1"
1314 1321
 
1322
+html-parse-stringify@^3.0.1:
1323
+  version "3.0.1"
1324
+  resolved "https://registry.npmmirror.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz#dfc1017347ce9f77c8141a507f233040c59c55d2"
1325
+  integrity sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==
1326
+  dependencies:
1327
+    void-elements "3.1.0"
1328
+
1315 1329
 html-void-elements@^2.0.0:
1316 1330
   version "2.0.1"
1317 1331
   resolved "https://registry.npmmirror.com/html-void-elements/-/html-void-elements-2.0.1.tgz"
@@ -1324,6 +1338,13 @@ i18next@^20.4.0:
1324 1338
   dependencies:
1325 1339
     "@babel/runtime" "^7.12.0"
1326 1340
 
1341
+i18next@^23.10.0:
1342
+  version "23.10.0"
1343
+  resolved "https://registry.npmmirror.com/i18next/-/i18next-23.10.0.tgz#fb328794ae692e6fdde0564259e421f4203c4a2c"
1344
+  integrity sha512-/TgHOqsa7/9abUKJjdPeydoyDc0oTi/7u9F8lMSj6ufg4cbC1Oj3f/Jja7zj7WRIhEQKB7Q4eN6y68I9RDxxGQ==
1345
+  dependencies:
1346
+    "@babel/runtime" "^7.23.2"
1347
+
1327 1348
 iconv-lite@^0.6.3:
1328 1349
   version "0.6.3"
1329 1350
   resolved "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz"
@@ -2083,6 +2104,14 @@ react-helmet@^6.1.0:
2083 2104
     react-fast-compare "^3.1.1"
2084 2105
     react-side-effect "^2.1.0"
2085 2106
 
2107
+react-i18next@^14.0.5:
2108
+  version "14.0.5"
2109
+  resolved "https://registry.npmmirror.com/react-i18next/-/react-i18next-14.0.5.tgz#5df7b88a3ac8afbef8089ed0d0c27e12b9a1acac"
2110
+  integrity sha512-5+bQSeEtgJrMBABBL5lO7jPdSNAbeAZ+MlFWDw//7FnVacuVu3l9EeWFzBQvZsKy+cihkbThWOAThEdH8YjGEw==
2111
+  dependencies:
2112
+    "@babel/runtime" "^7.23.9"
2113
+    html-parse-stringify "^3.0.1"
2114
+
2086 2115
 react-is@^16.12.0, react-is@^16.13.1:
2087 2116
   version "16.13.1"
2088 2117
   resolved "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz"
@@ -2147,6 +2176,11 @@ regenerator-runtime@^0.13.11:
2147 2176
   resolved "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz"
2148 2177
   integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
2149 2178
 
2179
+regenerator-runtime@^0.14.0:
2180
+  version "0.14.1"
2181
+  resolved "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f"
2182
+  integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==
2183
+
2150 2184
 resize-observer-polyfill@^1.5.1:
2151 2185
   version "1.5.1"
2152 2186
   resolved "https://registry.npmmirror.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz"
@@ -2277,6 +2311,11 @@ supports-preserve-symlinks-flag@^1.0.0:
2277 2311
   resolved "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz"
2278 2312
   integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
2279 2313
 
2314
+swiper@^11.0.7:
2315
+  version "11.0.7"
2316
+  resolved "https://registry.npmmirror.com/swiper/-/swiper-11.0.7.tgz#fe51bb64546c2c21a1ec6914e7764af953443ded"
2317
+  integrity sha512-cDfglW1B6uSmB6eB6pNmzDTNLmZtu5bWWa1vak0RU7fOI9qHjMzl7gVBvYSl34b0RU2N11HxxETJqQ5LeqI1cA==
2318
+
2280 2319
 swr@^2.0.0:
2281 2320
   version "2.1.5"
2282 2321
   resolved "https://registry.npmmirror.com/swr/-/swr-2.1.5.tgz"
@@ -2367,6 +2406,11 @@ vite@^3.0.0:
2367 2406
   optionalDependencies:
2368 2407
     fsevents "~2.3.2"
2369 2408
 
2409
+void-elements@3.1.0:
2410
+  version "3.1.0"
2411
+  resolved "https://registry.npmmirror.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09"
2412
+  integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==
2413
+
2370 2414
 warning@^4.0.3:
2371 2415
   version "4.0.3"
2372 2416
   resolved "https://registry.npmmirror.com/warning/-/warning-4.0.3.tgz"