shaogen1995 vor 1 Jahr
Ursprung
Commit
b89bb8802d

+ 59 - 0
package-lock.json

@@ -20,6 +20,7 @@
         "antd-mobile": "^5.30.0",
         "antd-mobile": "^5.30.0",
         "axios": "^1.1.3",
         "axios": "^1.1.3",
         "dayjs": "^1.11.7",
         "dayjs": "^1.11.7",
+        "echarts": "^5.4.3",
         "js-base64": "^3.7.3",
         "js-base64": "^3.7.3",
         "js-export-excel": "^1.1.4",
         "js-export-excel": "^1.1.4",
         "react": "^18.2.0",
         "react": "^18.2.0",
@@ -6746,6 +6747,20 @@
       "resolved": "https://registry.npmmirror.com/duplexer/-/duplexer-0.1.2.tgz",
       "resolved": "https://registry.npmmirror.com/duplexer/-/duplexer-0.1.2.tgz",
       "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg=="
       "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg=="
     },
     },
+    "node_modules/echarts": {
+      "version": "5.4.3",
+      "resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.4.3.tgz",
+      "integrity": "sha512-mYKxLxhzy6zyTi/FaEbJMOZU1ULGEQHaeIeuMR5L+JnJTpz+YR03mnnpBhbR4+UYJAgiXgpyTVLffPAjOTLkZA==",
+      "dependencies": {
+        "tslib": "2.3.0",
+        "zrender": "5.4.4"
+      }
+    },
+    "node_modules/echarts/node_modules/tslib": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz",
+      "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
+    },
     "node_modules/ee-first": {
     "node_modules/ee-first": {
       "version": "1.1.1",
       "version": "1.1.1",
       "resolved": "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz",
       "resolved": "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz",
@@ -17381,6 +17396,19 @@
       "engines": {
       "engines": {
         "node": ">=10"
         "node": ">=10"
       }
       }
+    },
+    "node_modules/zrender": {
+      "version": "5.4.4",
+      "resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.4.4.tgz",
+      "integrity": "sha512-0VxCNJ7AGOMCWeHVyTrGzUgrK4asT4ml9PEkeGirAkKNYXYzoPJCLvmyfdoOXcjTHPs10OZVMfD1Rwg16AZyYw==",
+      "dependencies": {
+        "tslib": "2.3.0"
+      }
+    },
+    "node_modules/zrender/node_modules/tslib": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz",
+      "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
     }
     }
   },
   },
   "dependencies": {
   "dependencies": {
@@ -22464,6 +22492,22 @@
       "resolved": "https://registry.npmmirror.com/duplexer/-/duplexer-0.1.2.tgz",
       "resolved": "https://registry.npmmirror.com/duplexer/-/duplexer-0.1.2.tgz",
       "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg=="
       "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg=="
     },
     },
+    "echarts": {
+      "version": "5.4.3",
+      "resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.4.3.tgz",
+      "integrity": "sha512-mYKxLxhzy6zyTi/FaEbJMOZU1ULGEQHaeIeuMR5L+JnJTpz+YR03mnnpBhbR4+UYJAgiXgpyTVLffPAjOTLkZA==",
+      "requires": {
+        "tslib": "2.3.0",
+        "zrender": "5.4.4"
+      },
+      "dependencies": {
+        "tslib": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz",
+          "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
+        }
+      }
+    },
     "ee-first": {
     "ee-first": {
       "version": "1.1.1",
       "version": "1.1.1",
       "resolved": "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz",
       "resolved": "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz",
@@ -30582,6 +30626,21 @@
       "version": "0.1.0",
       "version": "0.1.0",
       "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz",
       "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz",
       "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="
       "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="
+    },
+    "zrender": {
+      "version": "5.4.4",
+      "resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.4.4.tgz",
+      "integrity": "sha512-0VxCNJ7AGOMCWeHVyTrGzUgrK4asT4ml9PEkeGirAkKNYXYzoPJCLvmyfdoOXcjTHPs10OZVMfD1Rwg16AZyYw==",
+      "requires": {
+        "tslib": "2.3.0"
+      },
+      "dependencies": {
+        "tslib": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz",
+          "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
+        }
+      }
     }
     }
   }
   }
 }
 }

+ 1 - 0
package.json

@@ -15,6 +15,7 @@
     "antd-mobile": "^5.30.0",
     "antd-mobile": "^5.30.0",
     "axios": "^1.1.3",
     "axios": "^1.1.3",
     "dayjs": "^1.11.7",
     "dayjs": "^1.11.7",
+    "echarts": "^5.4.3",
     "js-base64": "^3.7.3",
     "js-base64": "^3.7.3",
     "js-export-excel": "^1.1.4",
     "js-export-excel": "^1.1.4",
     "react": "^18.2.0",
     "react": "^18.2.0",

BIN
public/favicon.ico


+ 2 - 1
public/index.html

@@ -10,6 +10,7 @@
       content="Web site created using create-react-app"
       content="Web site created using create-react-app"
     />
     />
     <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
     <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
+    <link rel="shortcut icon" href="./favicon.ico" type="image/x-icon">
     <!--
     <!--
       manifest.json provides metadata used when your web app is installed on a
       manifest.json provides metadata used when your web app is installed on a
       user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
       user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
@@ -24,7 +25,7 @@
       work correctly both with client-side routing and a non-root public URL.
       work correctly both with client-side routing and a non-root public URL.
       Learn how to configure a non-root public URL by running `npm run build`.
       Learn how to configure a non-root public URL by running `npm run build`.
     -->
     -->
-    <title>中国铁塔集团-管理后台</title>
+    <title>秋收起义馆藏-管理后台</title>
   </head>
   </head>
   <body>
   <body>
     <noscript>You need to enable JavaScript to run this app.</noscript>
     <noscript>You need to enable JavaScript to run this app.</noscript>

+ 84 - 0
src/components/Z_upFileOne/index.module.scss

@@ -0,0 +1,84 @@
+.Z_upFileOne {
+  width: 100%;
+  height: 100%;
+  position: relative;
+
+  :global {
+
+    .file_upIcon {
+      color: #a6a6a6;
+      border-radius: 3px;
+      cursor: pointer;
+      font-size: 30px;
+      width: 100px;
+      height: 100px;
+      border: 1px dashed #797979;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+
+
+    }
+
+    .file_img {
+      width: 100px;
+      height: 126px;
+      position: relative;
+
+      .file_closeBox {
+        position: absolute;
+        right: -10px;
+        top: -10px;
+        z-index: 99;
+        background-color: rgba(0, 0, 0, 0.8);
+        width: 20px;
+        height: 20px;
+        border-radius: 50%;
+        font-size: 16px;
+        color: #fff;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+      }
+
+
+
+      .file_lookBox {
+        width: 100%;
+        background-color: rgba(0, 0, 0, .6);
+        color: #fff;
+        display: flex;
+        justify-content: space-around;
+
+        &>a {
+          color: #fff;
+        }
+
+        font-size: 16px;
+      }
+    }
+
+    .fileBoxRow_r_tit {
+      height: 46px;
+      margin-top: 5px;
+      font-size: 14px;
+      color: rgb(126, 124, 124);
+
+
+    }
+
+    .noUpThumb {
+      position: relative;
+      overflow: hidden;
+      opacity: 0;
+      transition: top .2s;
+      color: #ff4d4f;
+      top: -10px;
+    }
+
+    .noUpThumbAc {
+      top: 0;
+      opacity: 1;
+    }
+  }
+}

+ 166 - 0
src/components/Z_upFileOne/index.tsx

@@ -0,0 +1,166 @@
+import React, { useCallback, useRef } from "react";
+import styles from "./index.module.scss";
+import ImageLazy from "@/components/ImageLazy";
+import {
+  PlusOutlined,
+  EyeOutlined,
+  CloseOutlined,
+  DownloadOutlined,
+} from "@ant-design/icons";
+import store from "@/store";
+import { baseURL } from "@/utils/http";
+import classNames from "classnames";
+import { Popconfirm } from "antd";
+import { MessageFu } from "@/utils/message";
+import { fileDomInitialFu } from "@/utils/domShow";
+import { API_upFile } from "@/store/action/layout";
+
+type Props = {
+  cover: string; //封面图
+  setCover: (val: string) => void; //设置封面图
+  isLook: boolean; //是不是查看
+  coverCheck: boolean; //有没有点击过确定
+  size: number; //上传图片大小(M)
+  dirCode: string; //文件的code码
+  myUrl: string;
+  format?: string[]; //上传图片格式
+  formatTxt?: string; //上传图片提示
+  sizeTxt?: string; //后面的建议尺寸信息
+  fromData?: any;
+  checkTxt?: string;
+};
+
+function Z_upFileOne({
+  cover,
+  setCover,
+  isLook,
+  coverCheck,
+  size,
+  dirCode,
+  myUrl,
+  format = ["image/jpeg", "image/png"],
+  formatTxt = "png、jpg和jpeg",
+  checkTxt = "请上传封面图!",
+  sizeTxt,
+  fromData,
+}: Props) {
+  const myInput = useRef<HTMLInputElement>(null);
+
+  // 上传封面图
+  const handeUpPhoto = useCallback(
+    async (e: React.ChangeEvent<HTMLInputElement>) => {
+      if (e.target.files) {
+        // 拿到files信息
+        const filesInfo = e.target.files[0];
+        // 校验格式
+        const type = format;
+        if (!type.includes(filesInfo.type)) {
+          e.target.value = "";
+          return MessageFu.warning(`只支持${formatTxt}格式!`);
+        }
+        // 校验大小
+        if (filesInfo.size > size * 1024 * 1024) {
+          e.target.value = "";
+          return MessageFu.warning(`最大支持${size}M!`);
+        }
+        // 创建FormData对象
+        const fd = new FormData();
+        // 把files添加进FormData对象(‘photo’为后端需要的字段)
+        fd.append("type", "thumb");
+        fd.append("dirCode", dirCode);
+        fd.append("file", filesInfo);
+
+        if (fromData) {
+          for (const k in fromData) {
+            if (fromData[k]) fd.append(k, fromData[k]);
+          }
+        }
+
+        e.target.value = "";
+
+        try {
+          const res = await API_upFile(fd, myUrl);
+          if (res.code === 0) {
+            MessageFu.success("上传成功!");
+            setCover(res.data.filePath);
+          }
+          fileDomInitialFu();
+        } catch (error) {
+          fileDomInitialFu();
+        }
+      }
+    },
+    [dirCode, format, formatTxt, fromData, myUrl, setCover, size]
+  );
+
+  return (
+    <div className={styles.Z_upFileOne}>
+      <input
+        id="upInput"
+        type="file"
+        accept=".png,.jpg,.jpeg"
+        ref={myInput}
+        onChange={(e) => handeUpPhoto(e)}
+      />
+
+      <div
+        hidden={cover !== ""}
+        className="file_upIcon"
+        onClick={() => myInput.current?.click()}
+      >
+        <PlusOutlined rev={undefined} />
+      </div>
+
+      <div className="file_img" hidden={cover === ""}>
+        {cover ? (
+          <ImageLazy width={100} height={100} src={cover} noLook />
+        ) : null}
+
+        {/* 删除 */}
+        <div className="file_closeBox" hidden={isLook}>
+          <Popconfirm
+            title="删除后无法恢复,是否删除?"
+            okText="删除"
+            cancelText="取消"
+            onConfirm={() => setCover("")}
+          >
+            <CloseOutlined rev={undefined} />
+          </Popconfirm>
+        </div>
+
+        {/* 预览 下载 */}
+        <div className="file_lookBox">
+          <EyeOutlined
+            onClick={() =>
+              store.dispatch({
+                type: "layout/lookBigImg",
+                payload: { url: baseURL + cover, show: true },
+              })
+            }
+            rev={undefined}
+          />
+          <a href={baseURL + cover} download target="_blank" rel="noreferrer">
+            <DownloadOutlined rev={undefined} />
+          </a>
+        </div>
+      </div>
+      <div className="fileBoxRow_r_tit" hidden={isLook}>
+      格式要求:支持{formatTxt}的图片格式;最大支持{size}M,最多1张。
+        {sizeTxt ? `建议尺寸${sizeTxt}` : null}
+        <br />
+        <div
+          className={classNames(
+            "noUpThumb",
+            !cover && coverCheck ? "noUpThumbAc" : ""
+          )}
+        >
+          {checkTxt}
+        </div>
+      </div>
+    </div>
+  );
+}
+
+const MemoZ_upFileOne = React.memo(Z_upFileOne);
+
+export default MemoZ_upFileOne;

+ 40 - 0
src/components/Z_upFileOtherList/index.module.scss

@@ -0,0 +1,40 @@
+.Z_upFileOtherList {
+  :global {
+    .ZOtit {
+      margin: 8px 0 10px;
+      font-size: 14px;
+      color: rgb(126, 124, 124);
+    }
+
+    .ZOlistBox {
+      max-height: 330px;
+      overflow-y: auto;
+
+      .ZOlistRow {
+        display: flex;
+        justify-content: space-between;
+        height: 30px;
+        align-items: center;
+
+        .ZOlistRowll {
+          width: 290px;
+          overflow: hidden;
+          text-overflow: ellipsis;
+          white-space: nowrap;
+        }
+
+        .ZOlistRowrr {
+          &>span,a {
+            cursor: pointer;
+            display: inline-block;
+            margin-left: 10px;
+            color: black;
+            &:hover{
+              color: var(--themeColor);
+            }
+          }
+        }
+      }
+    }
+  }
+}

+ 155 - 0
src/components/Z_upFileOtherList/index.tsx

@@ -0,0 +1,155 @@
+import React, { useCallback, useRef, useState } from "react";
+import styles from "./index.module.scss";
+import { Button, Popconfirm } from "antd";
+import { forwardRef, useImperativeHandle } from "react";
+import { MessageFu } from "@/utils/message";
+import { API_upFile } from "@/store/action/layout";
+import { fileDomInitialFu } from "@/utils/domShow";
+import {
+  EyeOutlined,
+  CloseOutlined,
+  DownloadOutlined,
+} from "@ant-design/icons";
+import filesLookFu from "@/utils/filesLook";
+import { baseURL } from "@/utils/http";
+
+export type FileListType = {
+  id: number;
+  name: string;
+  filePath: string;
+};
+
+type Props = {
+  max: number; //最多上传多少个文件
+  isLook: boolean; //是不是查看
+  size: number; //上传文件大小(M)
+  dirCode: string; //文件的code码
+  myUrl: string;
+  fromData?: any;
+  ref: any; //当前自己的ref,给父组件调用
+};
+
+function Z_upFileOtherList(
+  { max, isLook, size, dirCode, myUrl, fromData }: Props,
+  ref: any
+) {
+  // 附件数组
+  const [fileList, setFileList] = useState<FileListType[]>([]);
+
+  const myInput = useRef<HTMLInputElement>(null);
+
+  // 上传 附件 函数
+  const handeUpPhoto = useCallback(
+    async (e: React.ChangeEvent<HTMLInputElement>) => {
+      if (e.target.files) {
+        // 拿到files信息
+        const filesInfo = e.target.files[0];
+
+        // 校验大小
+        if (filesInfo.size > size * 1024 * 1024) {
+          e.target.value = "";
+          return MessageFu.warning(`最大支持${size}M!`);
+        }
+        // 创建FormData对象
+        const fd = new FormData();
+        // 把files添加进FormData对象(‘photo’为后端需要的字段)
+        fd.append("type", "doc");
+        fd.append("dirCode", dirCode);
+        fd.append("file", filesInfo);
+
+        if (fromData) {
+          for (const k in fromData) {
+            if (fromData[k]) fd.append(k, fromData[k]);
+          }
+        }
+
+        e.target.value = "";
+
+        try {
+          const res = await API_upFile(fd, myUrl);
+          if (res.code === 0) {
+            MessageFu.success("上传成功!");
+            setFileList([...fileList, res.data]);
+          }
+          fileDomInitialFu();
+        } catch (error) {
+          fileDomInitialFu();
+        }
+      }
+    },
+    [dirCode, fileList, fromData, myUrl, size]
+  );
+
+  // 给父组件返回 fileIds
+  const fileIdsRefFu = useCallback(() => {
+    return fileList.map((v) => v.id).join(",");
+  }, [fileList]);
+
+  // 可以让父组件调用子组件的方法
+  useImperativeHandle(ref, () => ({
+    fileIdsRefFu,
+  }));
+
+  return (
+    <div className={styles.Z_upFileOtherList}>
+      <input
+        id="upInput"
+        type="file"
+        ref={myInput}
+        onChange={(e) => handeUpPhoto(e)}
+      />
+
+      {fileList.length < max && !isLook ? (
+        <Button onClick={() => myInput.current?.click()}>上传</Button>
+      ) : null}
+
+      <div className="ZOlistBox mySorrl">
+        {fileList.map((v) => (
+          <div key={v.id} className="ZOlistRow">
+            <div className="ZOlistRowll" title={v.name}>
+              {v.name}
+            </div>
+            <div className="ZOlistRowrr">
+              {filesLookFu(v.name) ? (
+                <span>
+                  <EyeOutlined
+                    rev={undefined}
+                    onClick={() => filesLookFu(v.name, v.filePath)}
+                  />
+                </span>
+              ) : null}
+
+              <a
+                href={baseURL + v.filePath}
+                download={v.name}
+                target="_blank"
+                rel="noreferrer"
+              >
+                <DownloadOutlined rev={undefined} />
+              </a>
+
+              {isLook ? null : (
+                <Popconfirm
+                  title="删除后无法恢复,是否删除?"
+                  okText="删除"
+                  cancelText="取消"
+                  onConfirm={() =>
+                    setFileList(fileList.filter((c) => c.id !== v.id))
+                  }
+                >
+                  <span>
+                    <CloseOutlined rev={undefined} />
+                  </span>
+                </Popconfirm>
+              )}
+            </div>
+          </div>
+        ))}
+      </div>
+
+      <div className="ZOtit">格式要求:最大支持500M,最多{max}个。</div>
+    </div>
+  );
+}
+
+export default forwardRef(Z_upFileOtherList);

+ 119 - 1
src/pages/A1Stat/index.module.scss

@@ -1,5 +1,123 @@
 .A1Stat{
 .A1Stat{
   :global{
   :global{
-    
+    .A1tit{
+      font-size: 20px;
+      font-weight: 700;
+      position: absolute;
+      top: 15px;
+      left: 15px;
+      z-index: 10;
+      &>span{
+        font-weight: 400;
+        color: #999;
+        font-size: 12px;
+      }
+    }
+    .A1box1{
+      width: 100%;
+      height: calc(48% - 20px);
+      background-color: #fff;
+      border-radius: 10px;
+      margin-bottom: 20px;
+      padding: 20px 15px 0;
+      position: relative;
+      #echarts1{
+        width: 100%;
+        height: 100%;
+        // background-color: red;
+      }
+    }
+    .A1box2{
+      height: 51%;
+      display: flex;
+      &>div{
+        background-color: #fff;
+        border-radius: 10px;
+        position: relative;
+      }
+      .A1box2_1{
+        width: calc(45% - 20px);
+        padding: 40px 20px 0px;
+        margin-right: 20px;
+        #echarts2{
+          width: 100%;
+          height: 100%;
+        }
+      }
+      .A1box2_2{
+        width: 55%;
+        padding: 90px 20px 20px;
+        .A1box2_2Txt{
+          width: 100%;
+          height: 100%;
+          display: flex;
+          .A1t2_1{
+            width: 32%;
+            margin-right: 20px;
+            height: 100%;
+            &>div{
+              box-shadow: 0px 0px 3px 3px #ccc;
+              border-radius: 6px;
+              height: 40%;
+              display: flex;
+              justify-content: space-around;
+              align-items: center;
+              font-size: 24px;
+              &>span{
+               color: var(--themeColor);
+              }
+              &:nth-of-type(1){
+                margin-bottom: 20px;
+              }
+            }
+          }
+          .A1t2_2{
+            width: calc(68% - 20px);
+            .A1t2_2_1{
+              width: 100%;
+              height: 40%;
+              margin-bottom: 20px;
+              display: flex;
+              &>div{
+                border-radius: 6px;
+                box-shadow: 0px 0px 3px 3px #ccc;
+                margin-right: 20px;
+                display: flex;
+                justify-content: space-around;
+                align-items: center;
+                font-size: 20px;
+                width: calc(50% - 10px);
+                &>span{
+                 color: var(--themeColor);
+                }
+                &:last-child{
+                  margin-right: 0;
+                }
+              }
+            }
+            .A1t2_2_2{
+              height: 40%;
+              display: flex;
+              &>div{
+                border-radius: 6px;
+                box-shadow: 0px 0px 3px 3px #ccc;
+                margin-right: 20px;
+                display: flex;
+                justify-content: space-around;
+                align-items: center;
+                font-size: 20px;
+                width: calc(24%);
+                &>span{
+                 color: var(--themeColor);
+                }
+                &:last-child{
+                  margin-right: 0;
+                }
+              }
+            }
+          }
+        }
+      }
+    }
   }
   }
 }
 }

+ 198 - 5
src/pages/A1Stat/index.tsx

@@ -1,12 +1,205 @@
-import React from "react";
+import React, { useCallback, useEffect, useRef } from "react";
 import styles from "./index.module.scss";
 import styles from "./index.module.scss";
- function A1Stat() {
-  
+
+import * as echarts from "echarts/core";
+import {
+  TitleComponent,
+  ToolboxComponent,
+  TooltipComponent,
+  GridComponent,
+  LegendComponent,
+} from "echarts/components";
+import { LineChart, PieChart } from "echarts/charts";
+import { UniversalTransition, LabelLayout } from "echarts/features";
+import { CanvasRenderer } from "echarts/renderers";
+
+echarts.use([
+  TitleComponent,
+  ToolboxComponent,
+  TooltipComponent,
+  GridComponent,
+  LegendComponent,
+  LineChart,
+  CanvasRenderer,
+  UniversalTransition,
+  PieChart,
+  LabelLayout,
+]);
+
+function A1Stat() {
+  const echartRef1 = useRef<HTMLDivElement>(null);
+  const echartRef2 = useRef<HTMLDivElement>(null);
+  // 获取藏品统计数据
+  const getInfo1 = useCallback(async () => {
+    // const data =[]
+    const myChart = echarts.init(echartRef1.current);
+    const option = {
+      // color: ["#910000", "#E2CAA3"],
+      tooltip: {
+        trigger: "axis",
+      },
+      legend: {
+        data: ["已登记藏品", "已入库藏品"],
+        // top:'-10'
+      },
+      grid: {
+        left: "-24", //距左边边框的距离
+        right: "10", //距右边边框的距离
+        bottom: "10", //距下面边框的距离
+        top: "30", //距上面边框的距离
+        containLabel: true,
+      },
+      xAxis: {
+        type: "category",
+        splitLine: {
+          show: false, //隐藏背景刻度
+        },
+        boundaryGap: false,
+        data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
+        axisLine: {
+          show: false, //隐藏X轴
+        },
+        axisTick: {
+          show: false, //隐藏刻度线
+        },
+        axisLabel: {
+          show: false, //隐藏X轴文字
+        },
+      },
+      yAxis: {
+        type: "value",
+        splitLine: {
+          show: false, //隐藏背景刻度
+        },
+      },
+      series: [
+        {
+          name: "已登记藏品",
+          type: "line",
+          stack: "Total",
+          data: [120, 132, 101, 134, 90, 230, 210],
+        },
+        {
+          name: "已入库藏品",
+          type: "line",
+          stack: "Total",
+          data: [220, 182, 191, 234, 290, 330, 310],
+        },
+      ],
+    };
+    option && myChart.setOption(option);
+  }, []);
+
+  // 获取藏品级别数据
+  const getInfo2 = useCallback(async () => {
+    const myChart = echarts.init(echartRef2.current);
+    const option = {
+      tooltip: {
+        trigger: "item",
+      },
+      legend: {
+        top: "5%",
+        left: "center",
+      },
+      series: [
+        {
+          name: "Access From",
+          type: "pie",
+          radius: ["40%", "70%"],
+          avoidLabelOverlap: false,
+          itemStyle: {
+            borderRadius: 10,
+            borderColor: "#fff",
+            borderWidth: 2,
+          },
+          label: {
+            show: false,
+            position: "center",
+          },
+          emphasis: {
+            label: {
+              show: true,
+              fontSize: 40,
+              fontWeight: "bold",
+            },
+          },
+          labelLine: {
+            show: false,
+          },
+          data: [
+            { value: 1048, name: "Search Engine" },
+            { value: 735, name: "Direct" },
+            { value: 580, name: "Email" },
+            { value: 484, name: "Union Ads" },
+            { value: 300, name: "Video Ads" },
+          ],
+        },
+      ],
+    };
+    option && myChart.setOption(option);
+  }, []);
+
+  useEffect(() => {
+    getInfo1();
+    getInfo2();
+  }, [getInfo1, getInfo2]);
+
   return (
   return (
     <div className={styles.A1Stat}>
     <div className={styles.A1Stat}>
-       <div className="pageTitle">藏品统计</div>
+      <div className="pageTitle">藏品统计</div>
+      {/* 藏品数量折线图 */}
+      <div className="A1box1">
+        <div className="A1tit">藏品数量</div>
+        <div id="echarts1" ref={echartRef1}></div>
+      </div>
+      {/* 藏品级别和统计 */}
+      <div className="A1box2">
+        <div className="A1box2_1">
+          <div className="A1tit">
+            藏品级别 <span>以[已登记]的藏品数量为准</span>
+          </div>
+          <div id="echarts2" ref={echartRef2}></div>
+        </div>
+        <div className="A1box2_2">
+          <div className="A1tit">藏品统计</div>
+          <div className="A1box2_2Txt">
+            <div className="A1t2_1">
+              <div>
+                已登记<span>21</span>
+              </div>
+              <div>
+                已入库<span>22</span>
+              </div>
+            </div>
+            <div className="A1t2_2">
+              <div className="A1t2_2_1">
+                <div>
+                  登记中<span>2111</span>
+                </div>
+                <div>
+                  编辑中<span>22</span>
+                </div>
+              </div>
+              <div className="A1t2_2_2">
+                <div>
+                  未入库<span>21</span>
+                </div>
+                <div>
+                  入库中<span>999</span>
+                </div>
+                <div>
+                  出库中<span>21</span>
+                </div>
+                <div>
+                  移库中<span>22</span>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
     </div>
     </div>
-  )
+  );
 }
 }
 
 
 const MemoA1Stat = React.memo(A1Stat);
 const MemoA1Stat = React.memo(A1Stat);

+ 284 - 0
src/pages/A2Goods/A2Register/A2AddModal.tsx

@@ -0,0 +1,284 @@
+import React, { useCallback, useEffect, useRef, useState } from "react";
+import styles from "./index.module.scss";
+import {
+  Button,
+  Form,
+  FormInstance,
+  Input,
+  InputNumber,
+  Modal,
+  Popconfirm,
+  Select,
+} from "antd";
+import { A2addInfoType, A2inTableType, options1 } from "../data";
+import TextArea from "antd/es/input/TextArea";
+import ZupFileOne from "@/components/Z_upFileOne";
+import ZupFileOtherList from "@/components/Z_upFileOtherList";
+import { A2_APIaddSon } from "@/store/action/A2Goods";
+import { MessageFu } from "@/utils/message";
+
+type Props = {
+  addInfo: A2addInfoType;
+  upTableFu: (item: A2inTableType) => void;
+  closeFu: () => void;
+};
+
+function A2AddModal({ addInfo, upTableFu, closeFu }: Props) {
+  // 获取详情的方法
+  const getInfoFu = useCallback((id: number) => {}, []);
+
+  useEffect(() => {
+    if (addInfo.txt === "新增") {
+      setDirCode(Date.now() + "");
+    } else getInfoFu(addInfo.id);
+  }, [addInfo.id, addInfo.txt, getInfoFu]);
+
+  // 表单的ref
+  const FormBoxRef = useRef<FormInstance>(null);
+  // FormBoxRef.current?.setFieldsValue(res.data);
+
+  // 目录码
+  const [dirCode, setDirCode] = useState("");
+
+  // 点击了提交的校验
+  const [isOk, setIsOk] = useState(false);
+
+  // 封面图
+  const [thumb, setThumb] = useState("");
+
+  // 附件的ref
+  const fileRef = useRef<any>(null);
+
+  // 没有通过校验
+  const onFinishFailed = useCallback(() => {
+    setIsOk(true);
+  }, []);
+
+  // 通过校验点击确定
+  const onFinish = useCallback(
+    async (value: any) => {
+      setIsOk(true);
+
+      const fileIds = fileRef.current.fileIdsRefFu();
+
+      if (!thumb) return;
+
+      const size = [
+        value.sizeChang || " ",
+        value.sizeKuang || " ",
+        value.sizeGao || " ",
+      ];
+
+      const obj = {
+        ...value,
+        id: addInfo.txt === "新增" ? null : addInfo.id,
+        size: size.join(","),
+        thumb,
+        fileIds,
+      };
+
+      const res = await A2_APIaddSon(obj);
+      if (res.code === 0) {
+        MessageFu.success(`${addInfo.txt}成功!`);
+        upTableFu(res.data);
+        closeFu();
+      }
+    },
+    [addInfo.id, addInfo.txt, closeFu, thumb, upTableFu]
+  );
+
+  return (
+    <Modal
+      wrapClassName={styles.A2AddModal}
+      open={true}
+      title={addInfo.txt}
+      footer={
+        [] // 设置footer为空,去掉 取消 确定默认按钮
+      }
+    >
+      <div className="A2RAmain">
+        <Form
+          scrollToFirstError={true}
+          ref={FormBoxRef}
+          // labelCol={{ span: 2 }}
+          name="basic"
+          onFinish={onFinish}
+          onFinishFailed={onFinishFailed}
+          autoComplete="off"
+        >
+          <Form.Item
+            label="名称"
+            name="name"
+            rules={[{ required: true, message: "请输入名称!" }]}
+            getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
+          >
+            <Input maxLength={20} showCount placeholder="请输入内容" />
+          </Form.Item>
+          <Form.Item
+            label="编号"
+            name="num"
+            rules={[{ required: true, message: "请输入名称!" }]}
+            getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
+          >
+            <Input maxLength={20} showCount placeholder="请输入内容" />
+          </Form.Item>
+
+          <div className="A2RArow">
+            <Form.Item
+              label="时代"
+              name="dictAge"
+              getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
+            >
+              <Input maxLength={10} showCount placeholder="请输入内容" />
+            </Form.Item>
+            <Form.Item
+              label="质地"
+              name="dictTexture"
+              getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
+            >
+              <Input maxLength={10} showCount placeholder="请输入内容" />
+            </Form.Item>
+          </div>
+
+          <div className="A2RArow A2RArow2">
+            <Form.Item
+              label="长"
+              name="sizeChang"
+              getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
+            >
+              <Input maxLength={8} showCount placeholder="请输入长度" />
+            </Form.Item>
+            <Form.Item
+              label="宽"
+              name="sizeKuang"
+              getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
+            >
+              <Input maxLength={8} showCount placeholder="请输入宽度" />
+            </Form.Item>
+            <Form.Item
+              label="高"
+              name="sizeGao"
+              getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
+            >
+              <Input maxLength={8} showCount placeholder="请输入高度" />
+            </Form.Item>
+          </div>
+
+          <div className="A2RArow">
+            <Form.Item
+              label="重量"
+              name="quality"
+              getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
+            >
+              <Input maxLength={8} showCount placeholder="请输入内容" />
+            </Form.Item>
+            <Form.Item
+              className="A2RArowLabel"
+              label="实际数量"
+              name="pcs"
+            >
+              <InputNumber style={{width:256}} min={1} max={8} placeholder="请输入数字" />
+              {/* <Input maxLength={8} showCount /> */}
+            </Form.Item>
+          </div>
+
+          <div className="A2RArow">
+            <Form.Item
+              label="来源"
+              name="source"
+              getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
+            >
+              <Input maxLength={10} showCount placeholder="请输入内容" />
+            </Form.Item>
+            <Form.Item
+              className="A2RArowLabel"
+              label="完残程度"
+              name="complete"
+              getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
+            >
+              <Input maxLength={10} showCount placeholder="请输入内容" />
+            </Form.Item>
+          </div>
+
+          <div className="A2RArow">
+            <Form.Item
+              label="级别"
+              name="dictLevel"
+              rules={[{ required: true, message: "请选择级别!" }]}
+            >
+              <Select
+                placeholder="请选择"
+                options={options1.filter((v) => v.label !== "全部")}
+              />
+            </Form.Item>
+            <Form.Item
+              className="A2RArowLabel"
+              label="定级时间"
+              name="levelTime"
+              getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
+            >
+              <Input maxLength={10} showCount placeholder="请输入内容" />
+            </Form.Item>
+          </div>
+
+          <Form.Item label="简介" name="description">
+            <TextArea placeholder="请输入内容" showCount maxLength={200} />
+          </Form.Item>
+
+          <br />
+          <Form.Item className="A2RAbtn">
+            <Button type="primary" htmlType="submit">
+              提交
+            </Button>
+            &emsp;
+            <Popconfirm
+              title="放弃编辑后,信息将不会保存!"
+              okText="放弃"
+              cancelText="取消"
+              onConfirm={closeFu}
+              okButtonProps={{ loading: false }}
+            >
+              <Button>取消</Button>
+            </Popconfirm>
+          </Form.Item>
+        </Form>
+        <div className="A2RAmainRight">
+          {/* 封面图和附件 */}
+          <div className="A2RAFormRow">
+            <div className="A2RAFormRowll">
+              <span>* </span>封面:
+            </div>
+            <div className="A2RAFormRowrr">
+              <ZupFileOne
+                cover={thumb}
+                setCover={(val) => setThumb(val)}
+                isLook={addInfo.txt === "查看"}
+                coverCheck={isOk}
+                size={2}
+                dirCode={dirCode}
+                myUrl="cms/goods/file/upload"
+              />
+            </div>
+          </div>
+          <div className="A2RAFormRow">
+            <div className="A2RAFormRowll">图片或附件:</div>
+            <div className="A2RAFormRowrr">
+              <ZupFileOtherList
+                max={30}
+                isLook={addInfo.txt === "查看"}
+                size={500}
+                dirCode={dirCode}
+                myUrl="cms/goods/file/upload"
+                ref={fileRef}
+              />
+            </div>
+          </div>
+        </div>
+      </div>
+    </Modal>
+  );
+}
+
+const MemoA2AddModal = React.memo(A2AddModal);
+
+export default MemoA2AddModal;

+ 141 - 0
src/pages/A2Goods/A2Register/index.module.scss

@@ -0,0 +1,141 @@
+.A2Register {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 10;
+  background-color: #ecedf1;
+
+  :global {
+    .A2Rtop {
+      display: flex;
+      margin-bottom: 15px;
+      background-color: #fff;
+      border-radius: 10px;
+      padding: 24px;
+
+      .A2Rtop1 {
+        width: 80px;
+      }
+
+      .A2Rtop2 {
+        width: calc(100% - 80px);
+      }
+    }
+
+    .A2Rtable {
+      height: calc(100% - 160px);
+      background-color: #fff;
+      border-radius: 10px;
+      padding: 24px;
+
+      .A2Rtable1 {
+        display: flex;
+        justify-content: space-between;
+        margin-bottom: 10px;
+      }
+
+      .A2Rtable2 {
+        height: calc(100% - 80px);
+
+        .ant-table-body {
+          height: 482px;
+          overflow-y: auto !important;
+          overflow-y: overlay !important;
+          .ant-table-cell{
+            padding: 4px 6px !important;
+          }
+        }
+      }
+
+      .A2Rtable3 {
+        margin-top: 10px;
+        text-align: center;
+      }
+    }
+  }
+}
+
+// 新增和编辑弹窗的样式
+.A2AddModal {
+  :global {
+    .ant-modal-close {
+      display: none;
+    }
+
+    .ant-modal {
+      width: 1200px !important;
+      max-width: 1200px !important;
+      top: 50px;
+
+      .A2RAmain {
+        display: flex;
+        position: relative;
+        padding-bottom: 50px;
+        border-top: 1px solid #ccc;
+        padding-top: 15px;
+
+
+        .ant-form {
+          width: 700px;
+
+          .ant-form-item-label{
+            width: 52px;
+          }
+
+          .A2RArow {
+            display: flex;
+            justify-content: space-between;
+
+            .ant-form-item {
+              width: 50%;
+
+    
+            }
+            .A2RArowLabel{
+              .ant-form-item-label {
+                width: 80px;
+              }
+            }
+
+          }
+          .A2RArow2{
+            .ant-form-item{
+              width: 33.33%;
+            }
+          }
+
+          .A2RAbtn {
+            position: absolute;
+            bottom: 0px;
+            left: 50%;
+            transform: translateX(-50%);
+            margin-bottom: 0;
+          }
+        }
+
+        .A2RAmainRight {
+          width: 480px;
+          margin-left: 20px;
+          .A2RAFormRow{
+            margin-bottom: 24px;
+            display: flex;
+            .A2RAFormRowll{
+              width: 85px;
+              text-align: right;
+              &>span{
+                color: #ff4d4f;
+              }
+            }
+            .A2RAFormRowrr{
+              width: calc(100% - 85px);
+            }
+          }
+        }
+      }
+
+
+    }
+  }
+}

+ 200 - 0
src/pages/A2Goods/A2Register/index.tsx

@@ -0,0 +1,200 @@
+import React, {
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
+import styles from "./index.module.scss";
+import TextArea from "antd/es/input/TextArea";
+import { Button, Popconfirm, Table } from "antd";
+import {
+  A2addInfoType,
+  A2inTableType,
+  statusTxtObj,
+  storageStatusTxtObj,
+} from "../data";
+import A2AddModal from "./A2AddModal";
+import { MessageFu } from "@/utils/message";
+import ImageLazy from "@/components/ImageLazy";
+import { A2_APIaddWai, A2_APIinDels } from "@/store/action/A2Goods";
+import history from "@/utils/history";
+
+type Props = {
+  closeFu: () => void;
+  outInfo: A2addInfoType;
+};
+
+function A2Register({ closeFu, outInfo }: Props) {
+  // 记录删除了的所有id集合
+  const delIdArr = useRef<number[]>([]);
+
+  // 获取详情
+  const getInfoFu = useCallback((id: number) => {}, []);
+
+  useEffect(() => {
+    if (outInfo.txt !== "新增") getInfoFu(outInfo.id);
+  }, [getInfoFu, outInfo.id, outInfo.txt]);
+
+  // 表格信息
+  const [tableList, setTableList] = useState<A2inTableType[]>([]);
+
+  // 申请说明字段
+  const [topValue, setTopValue] = useState("");
+
+  // 点击提交或者存草稿
+  const btnOkFu = useCallback(
+    async (status: 0 | 1) => {
+      const obj = {
+        id: outInfo.txt === "新增" ? null : outInfo.id,
+        description: topValue,
+        goodsIds: tableList.map((v) => v.id).join(","),
+        status,
+        type: "DJ",
+      };
+
+      const res1 = await A2_APIaddWai(obj);
+
+      // 删除id集合
+      if(delIdArr.current.length>0){
+         await A2_APIinDels(delIdArr.current.join(","));
+      }
+
+      if (res1.code === 0) {
+        MessageFu.success(`${outInfo.txt}成功!`);
+        history.push("/submit");
+      }
+    },
+    [outInfo.id, outInfo.txt, tableList, topValue]
+  );
+
+  const columns = useMemo(() => {
+    return [
+      {
+        title: "缩略图",
+        render: (item: A2inTableType) => (
+          <div className="tableImgAuto">
+            <ImageLazy width={60} height={60} src={item.thumb} />
+          </div>
+        ),
+      },
+      {
+        title: "编号",
+        dataIndex: "num",
+      },
+      {
+        title: "名称",
+        dataIndex: "name",
+      },
+      {
+        title: "级别",
+        dataIndex: "dictLevel",
+      },
+      {
+        title: "藏品状态",
+        render: (item: A2inTableType) =>
+          Reflect.get(statusTxtObj, item.status) || "-",
+      },
+      {
+        title: "库存状态",
+        render: (item: A2inTableType) =>
+          Reflect.get(storageStatusTxtObj, item.storageStatus) || "-",
+      },
+      {
+        title: "备注",
+        render: (item: A2inTableType) => (
+          <>
+            <Button size="small" type="text">
+              编辑
+            </Button>
+            <Popconfirm
+              title="删除后无法恢复,是否删除?"
+              okText="删除"
+              cancelText="取消"
+              onConfirm={() => {
+                delIdArr.current.push(item.id);
+                setTableList(tableList.filter((v) => v.id !== item.id));
+                MessageFu.success("删除成功!");
+              }}
+              okButtonProps={{ loading: false }}
+            >
+              <Button size="small" type="text" danger>
+                删除
+              </Button>
+            </Popconfirm>
+          </>
+        ),
+      },
+    ];
+  }, [tableList]);
+
+  // 新增和编辑
+  const [addInfo, setAddInfo] = useState<A2addInfoType>({ id: 0, txt: "" });
+
+  return (
+    <div className={styles.A2Register}>
+      <div className="A2Rtop">
+        <div className="A2Rtop1">申请说明:</div>
+        <div className="A2Rtop2">
+          <TextArea
+            value={topValue}
+            onChange={(e) => setTopValue(e.target.value)}
+            placeholder="请输入内容"
+            showCount
+            maxLength={200}
+          />
+        </div>
+      </div>
+      <div className="A2Rtable">
+        <div className="A2Rtable1">
+          <div>藏品清单:</div>
+          <Button
+            type="primary"
+            onClick={() => setAddInfo({ id: -1, txt: "新增" })}
+          >
+            新增
+          </Button>
+        </div>
+        <div className="A2Rtable2">
+          <Table
+            size="small"
+            scroll={{ y: 482 }}
+            dataSource={tableList}
+            columns={columns}
+            rowKey="id"
+            pagination={false}
+          />
+        </div>
+        <div className="A2Rtable3">
+          <Button type="primary" onClick={() => btnOkFu(1)}>
+            提交
+          </Button>
+          &emsp;
+          <Button onClick={() => btnOkFu(0)}>存草稿</Button>&emsp;
+          <Popconfirm
+            title="放弃编辑后,信息将不会保存!"
+            okText="放弃"
+            cancelText="取消"
+            onConfirm={closeFu}
+            okButtonProps={{ loading: false }}
+          >
+            <Button>取消</Button>
+          </Popconfirm>
+        </div>
+      </div>
+
+      {/* 点击新增和编辑出来的弹窗 */}
+      {addInfo.id ? (
+        <A2AddModal
+          addInfo={addInfo}
+          closeFu={() => setAddInfo({ id: 0, txt: "" })}
+          upTableFu={(item) => setTableList([...tableList, item])}
+        />
+      ) : null}
+    </div>
+  );
+}
+
+const MemoA2Register = React.memo(A2Register);
+
+export default MemoA2Register;

+ 124 - 0
src/pages/A2Goods/data.ts

@@ -0,0 +1,124 @@
+export type A2selectType = {
+  searchKey: string;
+  dictLevel: "" | "一级" | "二级" | "三级";
+  status: "" | 0 | 1 | 2 | 3 | 4 | 5;
+  storageStatus: "" | 0 | 1 | 2 | 3 | 4;
+  pageSize: number;
+  pageNum: number;
+};
+
+// 内部弹窗信息的 参数 类型
+export type A2addInfoType={
+  id:number
+  txt:'新增'|'编辑'|'查看'|''
+}
+
+export const options1 = [
+  {
+    value: "",
+    label: "全部",
+  },
+  {
+    value: "一级",
+    label: "一级",
+  },
+  {
+    value: "二级",
+    label: "二级",
+  },
+  {
+    value: "三级",
+    label: "三级",
+  },
+];
+
+export const options2 = [
+  {
+    value: "",
+    label: "全部",
+  },
+  {
+    value: 2,
+    label: "已登记",
+  },
+  {
+    value: 3,
+    label: "编辑中",
+  },
+  {
+    value: 4,
+    label: "注销中",
+  },
+];
+
+export const statusTxtObj = {
+  0: "草稿中",
+  1: "登记中",
+  2: "已登记",
+  3: "编辑中",
+  4: "注销中",
+  5: "已注销",
+};
+
+export const options3 = [
+  {
+    value: "",
+    label: "全部",
+  },
+  {
+    value: 0,
+    label: "未入库",
+  },
+  {
+    value: 1,
+    label: "入库中",
+  },
+  {
+    value: 2,
+    label: "已入库",
+  },
+  {
+    value: 3,
+    label: "出库中",
+  },
+  {
+    value: 4,
+    label: "移库中",
+  },
+];
+
+export const storageStatusTxtObj = {
+  0: "未入库",
+  1: "入库中",
+  2: "已入库",
+  3: "出库中",
+  4: "移库中",
+};
+
+
+export type A2inTableType ={
+	complete: string;
+	createTime: string;
+	creatorId: number;
+	creatorName: string;
+	description: string;
+	dictAge: string;
+	dictLevel: string;
+	dictTexture: string;
+	dirCode: string;
+	display: string;
+	fileIds: string;
+	id: number;
+	levelTime: string;
+	name: string;
+	num: string;
+	pcs: number;
+	quality: string;
+	size: string;
+	source: string;
+	status: string;
+	storageIds: string;
+	storageStatus: string;
+	thumb: string;
+	updateTime: string;
+}

+ 31 - 2
src/pages/A2Goods/index.module.scss

@@ -1,5 +1,34 @@
 .A2Goods{
 .A2Goods{
-  :global{
-    
+  position: relative;
+  :global {
+    .A2top {
+      display: flex;
+      justify-content: space-between;
+      border-radius: 10px;
+      background-color: #fff;
+      padding: 15px 24px;
+
+      &>div {
+        display: flex;
+
+        .A2topRow {
+          margin-right: 20px;
+        }
+      }
+    }
+
+    .tableMain {
+      border-radius: 10px;
+      margin-top: 15px;
+      height: calc(100% - 75px);
+      background-color: #fff;
+
+      .ant-table-body {
+        height: 625px;
+        overflow-y: auto !important;
+        overflow-y: overlay !important;
+
+      }
+    }
   }
   }
 }
 }

+ 161 - 5
src/pages/A2Goods/index.tsx

@@ -1,12 +1,168 @@
-import React from "react";
+import React, { useCallback, useEffect, useRef, useState } from "react";
 import styles from "./index.module.scss";
 import styles from "./index.module.scss";
- function A2Goods() {
-  
+import { useDispatch } from "react-redux";
+import {
+  A2addInfoType,
+  A2selectType,
+  options1,
+  options2,
+  options3,
+} from "./data";
+import { Button, Input, Select } from "antd";
+import { A2_APIgetList } from "@/store/action/A2Goods";
+import A2Register from "./A2Register";
+function A2Goods() {
+  const dispatch = useDispatch();
+
+  // 筛选和分页
+  const [tableSelect, setTableSelect] = useState<A2selectType>({
+    searchKey: "",
+    dictLevel: "",
+    status: "",
+    storageStatus: "",
+    pageSize: 10,
+    pageNum: 1,
+  });
+
+  // 输入框的改变
+  const txtTimeRef = useRef(-1);
+  const txtChangeFu = useCallback(
+    (txt: string, key: "searchKey") => {
+      clearTimeout(txtTimeRef.current);
+      txtTimeRef.current = window.setTimeout(() => {
+        setTableSelect({ ...tableSelect, [key]: txt, pageNum: 1 });
+      }, 500);
+    },
+    [tableSelect]
+  );
+
+  // 发送接口的函数
+  const getListFu = useCallback(() => {
+    dispatch(A2_APIgetList(tableSelect));
+  }, [dispatch, tableSelect]);
+
+  useEffect(() => {
+    getListFu();
+  }, [getListFu]);
+
+  // 点击重置
+  const [inputKey, setInputKey] = useState(1);
+  const resetSelectFu = useCallback(() => {
+    // 把2个输入框和时间选择器清空
+    setInputKey(Date.now());
+    setTableSelect({
+      searchKey: "",
+      dictLevel: "",
+      status: "",
+      storageStatus: "",
+      pageSize: 10,
+      pageNum: 1,
+    });
+  }, []);
+
+  // 点击 申请登记
+  const [outInfo, setOutInfo] = useState<A2addInfoType>({ id: 0, txt: "" });
+
   return (
   return (
     <div className={styles.A2Goods}>
     <div className={styles.A2Goods}>
-     <div className="pageTitle">藏品清单</div>
+      <div className="pageTitle">
+        藏品清单{outInfo.txt === "新增" ? "/申请登记" : null}
+      </div>
+      {/* 顶部筛选 */}
+      <div className="A2top">
+        {/* 左侧输入框 */}
+        <div className="A2top1">
+          <div className="A2topRow">
+            <span>搜索项:</span>
+            <Input
+              key={inputKey}
+              maxLength={10}
+              style={{ width: 248 }}
+              placeholder="请输入藏品编号/名称,最多10字"
+              allowClear
+              onChange={(e) => txtChangeFu(e.target.value, "searchKey")}
+            />
+          </div>
+
+          <div className="A2topRow">
+            <span>级别:</span>
+            <Select
+              style={{ width: 150 }}
+              value={tableSelect.dictLevel}
+              onChange={(e) =>
+                setTableSelect({ ...tableSelect, dictLevel: e, pageNum: 1 })
+              }
+              options={options1}
+            />
+          </div>
+
+          <div className="A2topRow">
+            <span>藏品状态:</span>
+            <Select
+              style={{ width: 150 }}
+              value={tableSelect.status}
+              onChange={(e) =>
+                setTableSelect({ ...tableSelect, status: e, pageNum: 1 })
+              }
+              options={options2}
+            />
+          </div>
+
+          <div className="A2topRow">
+            <span>库存状态:</span>
+            <Select
+              style={{ width: 150 }}
+              value={tableSelect.storageStatus}
+              onChange={(e) =>
+                setTableSelect({ ...tableSelect, storageStatus: e, pageNum: 1 })
+              }
+              options={options3}
+            />
+          </div>
+        </div>
+        {/* 右侧按钮 */}
+        <div className="A2top2">
+          <Button onClick={resetSelectFu}>重置</Button>&emsp;
+          <Button
+            type="primary"
+            onClick={() => setOutInfo({ id: -1, txt: "新增" })}
+          >
+            申请登记
+          </Button>
+          &emsp;
+          <Button type="primary">申请注销</Button>
+        </div>
+      </div>
+      {/* 表格主体 */}
+      <div className="tableMain">
+        {/* <Table
+          scroll={{ y: 625 }}
+          dataSource={A2TableList.list}
+          columns={columns}
+          rowKey="id"
+          pagination={{
+            showQuickJumper: true,
+            position: ["bottomCenter"],
+            showSizeChanger: true,
+            current: tableSelect.pageNum,
+            pageSize: tableSelect.pageSize,
+            total: A2TableList.total,
+            onChange: paginationChange(),
+          }}
+        /> */}
+      </div>
+
+      {/* 申请编辑出来的页面 */}
+      {outInfo.id ? (
+        <A2Register
+          closeFu={() => setOutInfo({ id: 0, txt: "" })}
+          outInfo={outInfo}
+          // addTableFu={resetSelectFu}
+          // editTableFu={getListFu}
+        />
+      ) : null}
     </div>
     </div>
-  )
+  );
 }
 }
 
 
 const MemoA2Goods = React.memo(A2Goods);
 const MemoA2Goods = React.memo(A2Goods);

+ 39 - 0
src/store/action/A2Goods.ts

@@ -0,0 +1,39 @@
+import http from "@/utils/http";
+import { AppDispatch } from "..";
+
+/**
+ * 获取日志表格列表
+ */
+export const A2_APIgetList = (data: any) => {
+  return async (dispatch: AppDispatch) => {
+    const res = await http.post("cms/goods/pageList", data);
+    if (res.code === 0) {
+      const obj = {
+        list: res.data.records,
+        total: res.data.total,
+      };
+      dispatch({ type: "A2/getList", payload: obj });
+    }
+  };
+};
+
+/**
+ * 编辑/新增单个藏品(内)
+ */
+export const A2_APIaddSon = (data: any) => {
+  return http.post("cms/goods/save", data);
+};
+
+/**
+ * 删除里面的列表的藏品
+ */
+export const A2_APIinDels = (ids: string) => {
+  return http.get(`cms/goods/removes/${ids}`);
+};
+
+/**
+ * 登记藏品(外)
+ */
+export const A2_APIaddWai = (data: any) => {
+  return http.post("cms/order/save/registerOrCancel", data);
+};

+ 26 - 0
src/store/reducer/A2Goods.ts

@@ -0,0 +1,26 @@
+import { A2tableType } from "@/types";
+
+// 初始化状态
+const initState = {
+  // 列表数据
+  tableInfo: {
+    list: [] as A2tableType[],
+    total: 0,
+  },
+};
+
+// 定义 action 类型
+type Props = {
+  type: "A2/getList";
+  payload: { list: A2tableType[]; total: number };
+};
+
+export default function Reducer(state = initState, action: Props) {
+  switch (action.type) {
+    // 获取列表数据
+    case "A2/getList":
+      return { ...state, tableInfo: action.payload };
+    default:
+      return state;
+  }
+}

+ 2 - 0
src/store/reducer/index.ts

@@ -3,11 +3,13 @@ import { combineReducers } from "redux";
 
 
 // 导入 登录 模块的 reducer
 // 导入 登录 模块的 reducer
 import A0Layout from "./layout";
 import A0Layout from "./layout";
+import A2Goods from "./A2Goods";
 import C2Log from "./C2Log";
 import C2Log from "./C2Log";
 
 
 // 合并 reducer
 // 合并 reducer
 const rootReducer = combineReducers({
 const rootReducer = combineReducers({
   A0Layout,
   A0Layout,
+  A2Goods,
   C2Log,
   C2Log,
 });
 });
 
 

+ 1 - 0
src/types/api/A2Goods.d.ts

@@ -0,0 +1 @@
+export type A2tableType =any

+ 1 - 0
src/types/index.d.ts

@@ -1,2 +1,3 @@
 export * from './api/layot'
 export * from './api/layot'
+export * from './api/A2Goods'
 export * from './api/C2Log'
 export * from './api/C2Log'

+ 60 - 0
src/utils/filesLook.ts

@@ -0,0 +1,60 @@
+import store from "@/store";
+import { baseURL } from "./http";
+
+const filesLookFu = (name: string, url?: string) => {
+  let flag = false;
+
+  const nameRes = name ? name : "";
+
+  if (nameRes.toLowerCase().endsWith(".pdf")) {
+    if (url) window.open(baseURL + url);
+    flag = true;
+  }
+
+  const arr1 = [".png", ".jpg", ".jpeg", ".gif"];
+  arr1.forEach((v) => {
+    if (nameRes.toLowerCase().endsWith(v)) {
+      if (url) {
+        store.dispatch({
+          type: "layout/lookBigImg",
+          payload: {
+            url: baseURL + url,
+            show: true,
+          },
+        });
+      }
+
+      flag = true;
+    }
+  });
+
+  let type: "" | "video" | "audio" = "";
+
+  const arr2 = [".mp3", ".wav"];
+
+  arr2.forEach((v) => {
+    if (nameRes.toLowerCase().endsWith(v)) {
+      type = "audio";
+      flag = true;
+    }
+  });
+
+  if (nameRes.toLowerCase().endsWith(".mp4")) {
+    type = "video";
+    flag = true;
+  }
+
+  if (type && url) {
+    store.dispatch({
+      type: "layout/lookDom",
+      payload: {
+        src: url,
+        type,
+      },
+    });
+  }
+
+  return flag;
+};
+
+export default filesLookFu;