shaogen1995 1 рік тому
коміт
d3e4438d22
100 змінених файлів з 41211 додано та 0 видалено
  1. 23 0
      .gitignore
  2. 46 0
      README.md
  3. 10 0
      config-overrides.js
  4. 30467 0
      package-lock.json
  5. 66 0
      package.json
  6. 8 0
      path.tsconfig.json
  7. 1 0
      public/baseUrl.js
  8. BIN
      public/favicon.ico
  9. 44 0
      public/index.html
  10. 62 0
      src/App.tsx
  11. BIN
      src/assets/img/login/IMGerror.png
  12. BIN
      src/assets/img/login/LOGO.png
  13. BIN
      src/assets/img/login/Statistics.png
  14. BIN
      src/assets/img/login/bg.jpg
  15. BIN
      src/assets/img/login/flower.png
  16. BIN
      src/assets/img/login/homeBg.jpg
  17. BIN
      src/assets/img/login/icon_1.png
  18. BIN
      src/assets/img/login/icon_2.png
  19. BIN
      src/assets/img/login/icon_3.png
  20. BIN
      src/assets/img/login/icon_4.png
  21. BIN
      src/assets/img/login/icon_5.png
  22. BIN
      src/assets/img/login/loading.gif
  23. BIN
      src/assets/img/login/remind.png
  24. BIN
      src/assets/img/login/user_1.png
  25. 120 0
      src/assets/styles/base.css
  26. 159 0
      src/assets/styles/base.less
  27. 26 0
      src/components/AsyncSpinLoding/index.module.scss
  28. 29 0
      src/components/AsyncSpinLoding/index.tsx
  29. 28 0
      src/components/AuthButton/index.tsx
  30. 32 0
      src/components/AuthRoute/index.tsx
  31. 17 0
      src/components/BreadTit/index.module.scss
  32. 17 0
      src/components/BreadTit/index.tsx
  33. 51 0
      src/components/ImageLazy/index.module.scss
  34. 63 0
      src/components/ImageLazy/index.tsx
  35. 32 0
      src/components/LeftBar/index.module.scss
  36. 37 0
      src/components/LeftBar/index.tsx
  37. 297 0
      src/components/LookObjTable/LookModal.tsx
  38. 79 0
      src/components/LookObjTable/index.css
  39. 120 0
      src/components/LookObjTable/index.less
  40. 95 0
      src/components/LookObjTable/index.tsx
  41. 29 0
      src/components/Message/index.tsx
  42. 32 0
      src/components/NotFound/index.tsx
  43. 157 0
      src/components/ObjectAdd/index.css
  44. 211 0
      src/components/ObjectAdd/index.less
  45. 9 0
      src/components/ObjectAdd/index.module.scss
  46. 926 0
      src/components/ObjectAdd/index.tsx
  47. 10 0
      src/components/SpinLoding/index.module.scss
  48. 13 0
      src/components/SpinLoding/index.tsx
  49. 36 0
      src/components/UpAsyncLoding/index.module.scss
  50. 15 0
      src/components/UpAsyncLoding/index.tsx
  51. 41 0
      src/index.tsx
  52. 208 0
      src/pages/Home/index.module.scss
  53. 291 0
      src/pages/Home/index.tsx
  54. 221 0
      src/pages/Layout/index.module.scss
  55. 272 0
      src/pages/Layout/index.tsx
  56. 91 0
      src/pages/Login/index.module.scss
  57. 82 0
      src/pages/Login/index.tsx
  58. 5 0
      src/pages/Object/index.module.scss
  59. 249 0
      src/pages/Object/index.tsx
  60. 87 0
      src/pages/ObjectSon/Object1/AddObject1/index.module.scss
  61. 341 0
      src/pages/ObjectSon/Object1/AddObject1/index.tsx
  62. 81 0
      src/pages/ObjectSon/Object1/AuditObject1/index.module.scss
  63. 164 0
      src/pages/ObjectSon/Object1/AuditObject1/index.tsx
  64. 56 0
      src/pages/ObjectSon/Object1/LookObject1/index.module.scss
  65. 128 0
      src/pages/ObjectSon/Object1/LookObject1/index.tsx
  66. 5 0
      src/pages/ObjectSon/Object1/index.module.scss
  67. 339 0
      src/pages/ObjectSon/Object1/index.tsx
  68. 26 0
      src/pages/ObjectSon/Object2/LookObject2/index.css
  69. 28 0
      src/pages/ObjectSon/Object2/LookObject2/index.less
  70. 127 0
      src/pages/ObjectSon/Object2/LookObject2/index.module.scss
  71. 534 0
      src/pages/ObjectSon/Object2/LookObject2/index.tsx
  72. 127 0
      src/pages/ObjectSon/Object2/LookObject2/table.tsx
  73. 91 0
      src/pages/ObjectSon/Object2/index.module.scss
  74. 420 0
      src/pages/ObjectSon/Object2/index.tsx
  75. 15 0
      src/pages/ObjectSon/Object3/AddObject3/GoodsAll.css
  76. 29 0
      src/pages/ObjectSon/Object3/AddObject3/GoodsAll.less
  77. 166 0
      src/pages/ObjectSon/Object3/AddObject3/GoodsAll.tsx
  78. 87 0
      src/pages/ObjectSon/Object3/AddObject3/index.module.scss
  79. 352 0
      src/pages/ObjectSon/Object3/AddObject3/index.tsx
  80. 86 0
      src/pages/ObjectSon/Object3/AuditObject3/index.module.scss
  81. 234 0
      src/pages/ObjectSon/Object3/AuditObject3/index.tsx
  82. 56 0
      src/pages/ObjectSon/Object3/LookObject3/index.module.scss
  83. 202 0
      src/pages/ObjectSon/Object3/LookObject3/index.tsx
  84. 5 0
      src/pages/ObjectSon/Object3/index.module.scss
  85. 316 0
      src/pages/ObjectSon/Object3/index.tsx
  86. 165 0
      src/pages/ObjectSon/Object4/AddObject4/GoodsAll.tsx
  87. 87 0
      src/pages/ObjectSon/Object4/AddObject4/index.module.scss
  88. 365 0
      src/pages/ObjectSon/Object4/AddObject4/index.tsx
  89. 86 0
      src/pages/ObjectSon/Object4/AuditObject4/index.module.scss
  90. 238 0
      src/pages/ObjectSon/Object4/AuditObject4/index.tsx
  91. 70 0
      src/pages/ObjectSon/Object4/LookObject4/index.module.scss
  92. 264 0
      src/pages/ObjectSon/Object4/LookObject4/index.tsx
  93. 5 0
      src/pages/ObjectSon/Object4/index.module.scss
  94. 316 0
      src/pages/ObjectSon/Object4/index.tsx
  95. 96 0
      src/pages/ObjectSon/Object5/AuditObject5/index.module.scss
  96. 270 0
      src/pages/ObjectSon/Object5/AuditObject5/index.tsx
  97. 96 0
      src/pages/ObjectSon/Object5/LookObject5/index.module.scss
  98. 219 0
      src/pages/ObjectSon/Object5/LookObject5/index.tsx
  99. 5 0
      src/pages/ObjectSon/Object5/index.module.scss
  100. 0 0
      src/pages/ObjectSon/Object5/index.tsx

+ 23 - 0
.gitignore

@@ -0,0 +1,23 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# production
+/build
+
+# misc
+.DS_Store
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*

+ 46 - 0
README.md

@@ -0,0 +1,46 @@
+# Getting Started with Create React App
+
+This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
+
+## Available Scripts
+
+In the project directory, you can run:
+
+### `npm start`
+
+Runs the app in the development mode.\
+Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
+
+The page will reload if you make edits.\
+You will also see any lint errors in the console.
+
+### `npm test`
+
+Launches the test runner in the interactive watch mode.\
+See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
+
+### `npm run build`
+
+Builds the app for production to the `build` folder.\
+It correctly bundles React in production mode and optimizes the build for the best performance.
+
+The build is minified and the filenames include the hashes.\
+Your app is ready to be deployed!
+
+See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
+
+### `npm run eject`
+
+**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
+
+If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
+
+Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
+
+You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
+
+## Learn More
+
+You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
+
+To learn React, check out the [React documentation](https://reactjs.org/).

+ 10 - 0
config-overrides.js

@@ -0,0 +1,10 @@
+const path = require('path')
+const { override, addWebpackAlias } = require('customize-cra')
+
+// 添加 @ 别名
+const webpackAlias = addWebpackAlias({
+  '@': path.resolve(__dirname, 'src'),
+})
+
+// 导出要进行覆盖的 webpack 配置
+module.exports = override(webpackAlias)

Різницю між файлами не показано, бо вона завелика
+ 30467 - 0
package-lock.json


+ 66 - 0
package.json

@@ -0,0 +1,66 @@
+{
+  "name": "demo",
+  "version": "0.1.0",
+  "private": true,
+  "dependencies": {
+    "@ant-design/cssinjs": "^1.5.6",
+    "@testing-library/jest-dom": "^5.16.5",
+    "@testing-library/react": "^13.4.0",
+    "@testing-library/user-event": "^13.5.0",
+    "@types/jest": "^27.5.2",
+    "@types/node": "^16.18.3",
+    "@types/react": "^18.0.24",
+    "@types/react-dom": "^18.0.8",
+    "antd": "^5.0.4",
+    "antd-mobile": "^5.30.0",
+    "axios": "^1.1.3",
+    "dayjs": "^1.11.7",
+    "echarts": "^5.4.0",
+    "js-base64": "^3.7.3",
+    "js-export-excel": "^1.1.4",
+    "lodash": "^4.17.21",
+    "react": "^18.2.0",
+    "react-dom": "^18.2.0",
+    "react-redux": "^8.0.4",
+    "react-router-dom": "5.3",
+    "react-scripts": "5.0.1",
+    "redux": "^4.2.0",
+    "redux-devtools-extension": "^2.13.9",
+    "redux-thunk": "^2.4.1",
+    "sass": "^1.55.0",
+    "typescript": "^4.8.4",
+    "web-vitals": "^2.1.4"
+  },
+  "scripts": {
+    "dev": "react-app-rewired start",
+    "build": "react-app-rewired build",
+    "test": "react-app-rewired test",
+    "eject": "react-scripts eject"
+  },
+  "eslintConfig": {
+    "extends": [
+      "react-app",
+      "react-app/jest"
+    ]
+  },
+  "browserslist": {
+    "production": [
+      ">0.2%",
+      "not dead",
+      "not op_mini all"
+    ],
+    "development": [
+      "last 1 chrome version",
+      "last 1 firefox version",
+      "last 1 safari version"
+    ]
+  },
+  "devDependencies": {
+    "@types/history": "^5.0.0",
+    "@types/react-router-dom": "^5.3.3",
+    "customize-cra": "^1.0.0",
+    "default-passive-events": "^2.0.0",
+    "react-app-rewired": "^2.2.1"
+  },
+  "homepage": "."
+}

+ 8 - 0
path.tsconfig.json

@@ -0,0 +1,8 @@
+{
+    "compilerOptions": {
+      "baseUrl": "./",
+      "paths": {
+        "@/*": ["src/*"]
+      }
+    }
+  }

+ 1 - 0
public/baseUrl.js

@@ -0,0 +1 @@
+const baseUrlHttp='https://sit-hdyzbwg.4dage.com'

BIN
public/favicon.ico


+ 44 - 0
public/index.html

@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html lang="zh">
+  <head>
+    <meta charset="utf-8" />
+    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <meta name="theme-color" content="#000000" />
+    <script src="./baseUrl.js"></script>
+    <meta
+      name="description"
+      content="Web site created using create-react-app"
+    />
+    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
+    <!--
+      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/
+    -->
+
+    <!--
+      Notice the use of %PUBLIC_URL% in the tags above.
+      It will be replaced with the URL of the `public` folder during the build.
+      Only files inside the `public` folder can be referenced from the HTML.
+
+      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
+      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`.
+    -->
+    <title>华东野战军博物馆馆藏系统</title>
+  </head>
+  <body>
+    <noscript>You need to enable JavaScript to run this app.</noscript>
+    <div id="root"></div>
+    <!--
+      This HTML file is a template.
+      If you open it directly in the browser, you will see an empty page.
+
+      You can add webfonts, meta tags, or analytics to this file.
+      The build step will place the bundled scripts into the <body> tag.
+
+      To begin the development, run `npm start` or `yarn start`.
+      To create a production bundle, use `npm run build` or `yarn build`.
+    -->
+  </body>
+</html>

+ 62 - 0
src/App.tsx

@@ -0,0 +1,62 @@
+import "@/assets/styles/base.css";
+// 关于路由
+import React from "react";
+import { Router, Route, Switch } from "react-router-dom";
+import history from "./utils/history";
+import AuthRoute from "./components/AuthRoute";
+import SpinLoding from "./components/SpinLoding";
+import AsyncSpinLoding from "./components/AsyncSpinLoding";
+import { Image } from "antd";
+import { useDispatch, useSelector } from "react-redux";
+import { RootState } from "./store";
+import UpAsyncLoding from "./components/UpAsyncLoding";
+import MessageCom from "./components/Message";
+const Layout = React.lazy(() => import("./pages/Layout"));
+const Login = React.lazy(() => import("./pages/Login"));
+
+export default function App() {
+  const dispatch = useDispatch();
+
+  // 从仓库中获取查看图片的信息
+  const lookBigImg = useSelector(
+    (state: RootState) => state.loginStore.lookBigImg
+  );
+
+  return (
+    <>
+      {/* 关于路由 */}
+      <Router history={history}>
+        <React.Suspense fallback={<SpinLoding />}>
+          <Switch>
+            <Route path="/login" component={Login} />
+            <AuthRoute path="/" component={Layout} />
+          </Switch>
+        </React.Suspense>
+      </Router>
+
+      {/* 发送请求的加载组件 */}
+      <AsyncSpinLoding />
+
+      {/* 所有图片点击预览查看大图 */}
+      <Image
+        preview={{
+          visible: lookBigImg.show,
+          src: lookBigImg.url,
+          onVisibleChange: (value) => {
+            // 清除仓库信息
+            dispatch({
+              type: "login/lookBigImg",
+              payload: { url: "", show: false },
+            });
+          },
+        }}
+      />
+
+      {/* 上传附件的进度条元素 */}
+      <UpAsyncLoding />
+
+      {/* antd 轻提示 ---兼容360浏览器 */}
+      <MessageCom />
+    </>
+  );
+}

BIN
src/assets/img/login/IMGerror.png


BIN
src/assets/img/login/LOGO.png


BIN
src/assets/img/login/Statistics.png


BIN
src/assets/img/login/bg.jpg


BIN
src/assets/img/login/flower.png


BIN
src/assets/img/login/homeBg.jpg


BIN
src/assets/img/login/icon_1.png


BIN
src/assets/img/login/icon_2.png


BIN
src/assets/img/login/icon_3.png


BIN
src/assets/img/login/icon_4.png


BIN
src/assets/img/login/icon_5.png


BIN
src/assets/img/login/loading.gif


BIN
src/assets/img/login/remind.png


BIN
src/assets/img/login/user_1.png


+ 120 - 0
src/assets/styles/base.css

@@ -0,0 +1,120 @@
+* {
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
+}
+html {
+  height: 100%;
+  font-size: 14px;
+  user-select: none;
+}
+body {
+  font: 1em/1.4 'Microsoft Yahei', 'PingFang SC', 'Avenir', 'Segoe UI', 'Hiragino Sans GB', 'STHeiti', 'Microsoft Sans Serif', 'WenQuanYi Micro Hei', sans-serif;
+  height: 100%;
+  color: #333;
+}
+a {
+  text-decoration: none;
+  color: #333;
+  outline: none;
+}
+i {
+  font-style: normal;
+}
+img {
+  max-width: 100%;
+  max-height: 100%;
+  vertical-align: middle;
+  object-fit: cover;
+}
+ul {
+  list-style: none;
+}
+body {
+  overflow-y: overlay;
+}
+/* 文本域取消下拉 */
+textarea {
+  resize: none !important;
+}
+/* 主题色 */
+:root {
+  --themeColor: #9F1927;
+}
+a {
+  color: var(--themeColor);
+}
+[hidden] {
+  display: none !important;
+}
+/* 兼容360浏览器的下拉框 */
+.ant-select-selector {
+  position: relative;
+  background-color: #ffffff;
+  border: 1px solid #d9d9d9;
+  transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
+}
+#root {
+  width: 100vw;
+  height: 100vh;
+  min-width: 1600px;
+  min-height: 900px;
+  overflow-y: auto;
+  /* 按钮的危险颜色 */
+  /* antd分页器样式 */
+  /* antd图片预览组件 */
+}
+#root .ant-btn-text.ant-btn-dangerous {
+  color: var(--themeColor);
+}
+#root .ant-pagination .ant-pagination-item {
+  border-radius: 50%;
+  border: 1px solid #999;
+  background-color: transparent;
+}
+#root .ant-pagination .ant-pagination-item-active {
+  background-color: var(--themeColor);
+}
+#root .ant-pagination .ant-pagination-item-active a {
+  color: #fff;
+}
+#root .ant-pagination .ant-pagination-item:hover {
+  background-color: var(--themeColor);
+}
+#root .ant-pagination .ant-pagination-item:hover a {
+  color: #fff;
+}
+#root .ant-pagination-prev {
+  border-radius: 50%;
+  border: 1px solid #999;
+}
+#root .ant-pagination-prev:hover {
+  background-color: var(--themeColor);
+}
+#root .ant-pagination-prev:hover button {
+  color: #fff;
+}
+#root .ant-pagination-next {
+  border-radius: 50%;
+  border: 1px solid #999;
+}
+#root .ant-pagination-next:hover {
+  background-color: var(--themeColor);
+}
+#root .ant-pagination-next:hover button {
+  color: #fff;
+}
+#root .ant-pagination-disabled {
+  border: 1px solid #ccc;
+}
+#root .ant-pagination-disabled:hover {
+  background-color: transparent;
+}
+#root .ant-image {
+  display: none !important;
+}
+/* 找不到页面 */
+.noFindPage {
+  opacity: 0;
+  transition: opacity 0.5s;
+}

+ 159 - 0
src/assets/styles/base.less

@@ -0,0 +1,159 @@
+* {
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
+}
+
+html {
+  height: 100%;
+  font-size: 14px;
+  user-select: none;
+}
+
+body {
+  font: 1em/1.4 'Microsoft Yahei', 'PingFang SC', 'Avenir', 'Segoe UI', 'Hiragino Sans GB', 'STHeiti', 'Microsoft Sans Serif', 'WenQuanYi Micro Hei', sans-serif;
+  height: 100%;
+  color: #333;
+}
+
+a {
+  text-decoration: none;
+  color: #333;
+  outline: none;
+}
+
+i {
+  font-style: normal;
+}
+
+img {
+  max-width: 100%;
+  max-height: 100%;
+  vertical-align: middle;
+  object-fit: cover;
+}
+
+ul {
+  list-style: none;
+}
+
+body {
+  overflow-y: overlay;
+}
+
+/* 文本域取消下拉 */
+textarea {
+  resize: none !important;
+}
+
+/* 主题色 */
+:root {
+  --themeColor: #9F1927;
+}
+
+a {
+  color: var(--themeColor);
+}
+
+[hidden] {
+  display: none !important;
+}
+
+
+/* 兼容360浏览器的下拉框 */
+.ant-select-selector {
+  position: relative;
+  background-color: #ffffff;
+  border: 1px solid #d9d9d9;
+  transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
+}
+
+// antd的样式覆盖
+
+#root {
+  width: 100vw;
+  height: 100vh;
+  min-width: 1600px;
+  min-height: 900px;
+  overflow-y: auto;
+
+  /* 按钮的危险颜色 */
+  .ant-btn-text.ant-btn-dangerous {
+    color: var(--themeColor);
+  }
+
+
+
+  /* antd分页器样式 */
+  .ant-pagination .ant-pagination-item {
+    border-radius: 50%;
+    border: 1px solid #999;
+    background-color: transparent;
+  }
+
+  .ant-pagination .ant-pagination-item-active {
+    background-color: var(--themeColor);
+  }
+
+
+  .ant-pagination .ant-pagination-item-active a {
+    color: #fff;
+  }
+
+  .ant-pagination .ant-pagination-item:hover {
+    background-color: var(--themeColor);
+  }
+
+  .ant-pagination .ant-pagination-item:hover a {
+    color: #fff;
+  }
+
+  .ant-pagination-prev {
+    border-radius: 50%;
+    border: 1px solid #999;
+  }
+
+  .ant-pagination-prev:hover {
+    background-color: var(--themeColor);
+  }
+
+  .ant-pagination-prev:hover button {
+    color: #fff;
+  }
+
+
+
+  .ant-pagination-next {
+    border-radius: 50%;
+    border: 1px solid #999;
+  }
+
+
+  .ant-pagination-next:hover {
+    background-color: var(--themeColor);
+  }
+
+  .ant-pagination-next:hover button {
+    color: #fff;
+  }
+
+  .ant-pagination-disabled {
+    border: 1px solid #ccc;
+  }
+
+  .ant-pagination-disabled:hover {
+    background-color: transparent;
+  }
+
+  /* antd图片预览组件 */
+  .ant-image {
+    display: none !important;
+  }
+}
+
+
+/* 找不到页面 */
+.noFindPage {
+  opacity: 0;
+  transition: opacity .5s;
+}

+ 26 - 0
src/components/AsyncSpinLoding/index.module.scss

@@ -0,0 +1,26 @@
+.AsyncSpinLoding {
+  opacity: 0;
+  pointer-events: none;
+  transition: all .5s;
+  position: fixed;
+  z-index: 9998;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  // background-color: rgba(0, 0, 0, .6);
+  background-color: transparent;
+  :global{
+    .ant-spin-spinning{
+      position: absolute;
+      top: 50%;
+      left: 50%;
+      transform: translate(-50%,-50%);
+    }
+  }
+}
+
+.AsyncSpinLodingShow {
+  opacity: 1;
+  pointer-events: auto;
+}

+ 29 - 0
src/components/AsyncSpinLoding/index.tsx

@@ -0,0 +1,29 @@
+import styles from "./index.module.scss";
+import { Spin } from "antd";
+import React from "react";
+import classNames from "classnames";
+import { useSelector } from "react-redux";
+import { RootState } from "@/store";
+
+function AsyncSpinLoding() {
+  // 从仓库中获取查看图片的信息
+  const asyncLoding = useSelector(
+    (state: RootState) => state.loginStore.asyncLoding
+  );
+
+  return (
+    <div
+      id="AsyncSpinLoding"
+      className={classNames(
+        styles.AsyncSpinLoding,
+        asyncLoding ? styles.AsyncSpinLodingShow : ""
+      )}
+    >
+      <Spin size="large" />
+    </div>
+  );
+}
+
+const MemoAsyncSpinLoding = React.memo(AsyncSpinLoding);
+
+export default MemoAsyncSpinLoding;

+ 28 - 0
src/components/AuthButton/index.tsx

@@ -0,0 +1,28 @@
+import React, { ReactNode } from "react";
+import { Button, ButtonProps } from "antd";
+import { useSelector } from "react-redux";
+import { RootState } from "@/store";
+
+// 定义一个接口继承,过滤重复的字段id
+type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
+
+// 过滤接口 ButtonProps 里面的字段id,使用自己定义的 id
+interface Props extends Omit<ButtonProps, "id"> {
+  children: ReactNode;
+  id: number;
+  [x: string]: any;
+}
+
+function AuthButton({ children, id, ...rest }: Props) {
+  const buttonArr = useSelector(
+    (state: RootState) => state.loginStore.authButtonArr
+  );
+
+  return buttonArr.some((v: any) => v.id === id) ? (
+    <Button {...rest}>{children}</Button>
+  ) : null;
+}
+
+const MemoAuthButton = React.memo(AuthButton);
+
+export default MemoAuthButton;

+ 32 - 0
src/components/AuthRoute/index.tsx

@@ -0,0 +1,32 @@
+import { hasToken } from "@//utils/storage";
+import { MessageFu } from "@/utils/message";
+import React from "react";
+import { Redirect, Route } from "react-router-dom";
+
+type AtahType = {
+  path: string;
+  component: React.FC;
+  [x: string]: any;
+};
+
+export default function AuthRoute({ path, component: Com, ...rest }: AtahType) {
+  return (
+    <Route
+      path={path}
+      {...rest}
+      render={() => {
+        if (hasToken()) return <Com />;
+        else {
+          MessageFu.warning("登录失效!");
+          return (
+            <Redirect
+              to={{
+                pathname: "/login",
+              }}
+            />
+          );
+        }
+      }}
+    />
+  );
+}

+ 17 - 0
src/components/BreadTit/index.module.scss

@@ -0,0 +1,17 @@
+.BreadTit{
+  display: flex;
+  align-items: center;
+  width: 100%;
+  height: 100%;
+  :global{
+    .splitStr{
+      margin: 0 5px;
+    }
+    .breadTitRow{
+      font-size: 16px;
+    }
+    .active{
+      color: var(--themeColor);
+    }
+  }
+}

+ 17 - 0
src/components/BreadTit/index.tsx

@@ -0,0 +1,17 @@
+import React, { ReactNode } from "react";
+import styles from "./index.module.scss";
+
+type Props ={
+  children: ReactNode;
+}
+
+ function BreadTit({children}:Props) {
+  return (
+    <div className={styles.BreadTit}>
+      {children}
+    </div>
+  )
+}
+const MemoBreadTit = React.memo(BreadTit);
+
+export default MemoBreadTit;

+ 51 - 0
src/components/ImageLazy/index.module.scss

@@ -0,0 +1,51 @@
+.ImageLazy {
+  position: relative;
+
+  :global {
+    .lazyBox {
+      width: 100%;
+      height: 100%;
+      position: relative;
+
+      .adm-image {
+        width: 100%;
+        height: 100%;
+
+        img {
+          width: 100%;
+          height: 100%;
+        }
+      }
+
+      .lookImg {
+        cursor: pointer;
+        transition: opacity .3s;
+        opacity: 0;
+        pointer-events: none;
+        position: absolute;
+        top: 0;
+        left: 0;
+        width: 100%;
+        height: 100%;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        font-size: 18px;
+        color: #fff;
+        background-color: rgba(0, 0, 0, .6);
+
+        &>div {
+          font-size: 14px;
+        }
+      }
+
+      &:hover {
+        .lookImg {
+          opacity: 1;
+          pointer-events: auto;
+        }
+      }
+    }
+  }
+
+}

+ 63 - 0
src/components/ImageLazy/index.tsx

@@ -0,0 +1,63 @@
+import React, { useCallback, useState } from "react";
+import styles from "./index.module.scss";
+import { baseURL } from "@/utils/http";
+import imgLoding from "@/assets/img/login/loading.gif";
+import imgErr from "@/assets/img/login/IMGerror.png";
+import { EyeOutlined } from "@ant-design/icons";
+import { useDispatch } from "react-redux";
+import { Image } from "antd-mobile";
+
+type Props = {
+  width?: number;
+  height?: number;
+  src: string;
+  noLook?: boolean;
+};
+
+function ImageLazy({ width = 100, height = 100, src, noLook }: Props) {
+  const dispatch = useDispatch();
+
+  // 默认不能预览图片,加载成功之后能预览
+  const [lookImg, setLookImg] = useState(false);
+
+  // 图片加载完成
+  const onLoad = useCallback(() => {
+    setLookImg(true);
+  }, []);
+
+  // 点击预览图片
+  const lookBigImg = useCallback(() => {
+    dispatch({
+      type: "login/lookBigImg",
+      payload: { url: baseURL + src, show: true },
+    });
+  }, [dispatch, src]);
+
+  return (
+    <div className={styles.ImageLazy} style={{ width: width, height: height }}>
+      <div className="lazyBox">
+        <Image
+          lazy
+          onLoad={onLoad}
+          src={src ? baseURL + src : ""}
+          placeholder={<img src={imgLoding} alt="" />}
+          fallback={<img src={imgErr} alt="" />}
+          fit="cover"
+        />
+
+        {/* 图片预览 */}
+        {noLook || !lookImg ? null : (
+          <div className="lookImg" onClick={lookBigImg}>
+            <EyeOutlined />
+            &nbsp;
+            <div>预览</div>
+          </div>
+        )}
+      </div>
+    </div>
+  );
+}
+
+const MemoImageLazy = React.memo(ImageLazy);
+
+export default MemoImageLazy;

+ 32 - 0
src/components/LeftBar/index.module.scss

@@ -0,0 +1,32 @@
+.LeftBar{
+  padding-top: 20px;
+  :global{
+    .leftRow{
+      cursor: pointer;
+      width: 100%;
+      height: 70px;
+      font-size: 16px;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      &>div{
+        opacity: 0;
+      }
+      &:hover{
+        color: #D3B453;
+      }
+    }
+    .active{
+      background-color: var(--themeColor);
+      color: #D3B453;
+      pointer-events: none;
+      &>div{
+        opacity: 1;
+        width: 10px;
+        height: 10px;
+        background-color: #D3B453;
+        border-radius: 50%;
+      }
+    }
+  }
+}

+ 37 - 0
src/components/LeftBar/index.tsx

@@ -0,0 +1,37 @@
+import styles from "./index.module.scss";
+import classNames from "classnames";
+import history from "@/utils/history";
+import { useLocation } from "react-router-dom";
+import { useEffect, useState } from "react";
+export default function LeftBar({ data }: any) {
+  const cutRouter = (path: string) => {
+    history.push(path);
+  };
+  const location = useLocation();
+
+  const [pathId, setPathId] = useState(1);
+  useEffect(() => {
+    const arr = location.pathname.split("/");
+    let id = 1;
+    if (arr[2]) id = Number(arr[2]);
+
+    setPathId(id);
+  }, [location]);
+  return (
+    <div className={styles.LeftBar}>
+      {data.length > 0
+        ? data.map((v: any) => (
+            <div
+              onClick={() => cutRouter(v.path)}
+              className={classNames("leftRow", v.id === pathId ? "active" : "")}
+              key={v.id}
+            >
+              <div></div>
+              &emsp;{v.name}&emsp;
+              <div></div>
+            </div>
+          ))
+        : null}
+    </div>
+  );
+}

+ 297 - 0
src/components/LookObjTable/LookModal.tsx

@@ -0,0 +1,297 @@
+import React, { useCallback, useEffect, useMemo, useState } from "react";
+import classNames from "classnames";
+import { Button, Modal, Select, Table } from "antd";
+import { useDispatch, useSelector } from "react-redux";
+import { RootState } from "@/store";
+import ImageLazy from "../ImageLazy";
+import { getInfoInAPI } from "@/store/action/object1";
+import './index.css'
+
+type Props = {
+  show: boolean;
+  closeShow: any;
+  id: number;
+};
+
+function LookModal({ show, closeShow, id }: Props) {
+  const dispatch = useDispatch();
+  // 根据id发送请求把数据存到仓库
+
+  const getInfoInAPIFu = useCallback(async () => {
+    const res = await getInfoInAPI(id);
+    const info = res.data.entity;
+    const fileList = {
+      img: [],
+      video: [],
+      audio: [],
+      model: [],
+      doc: [],
+    } as any;
+    res.data.file.forEach((v: any) => {
+      fileList[v.type].push(v);
+    });
+
+    dispatch({
+      type: "object1/getOneGoodsInfo",
+      payload: { info, fileList },
+    });
+  }, [dispatch, id]);
+
+  useEffect(() => {
+    getInfoInAPIFu();
+  }, [getInfoInAPIFu]);
+
+  // 选择商品信息还是附近(默认商品信息)
+  const [titSelect, setTitSelect] = useState("tit1");
+
+  // 附件类型下拉框
+  const { info, fileList } = useSelector(
+    (state: RootState) => state.object1Store.oneGoodsInfo
+  );
+
+  // 外形尺寸和具体质量
+  let sizeRes = "";
+  if (info.size && info.size.length) {
+    const sizeArr = info.size.split(",");
+    sizeRes = `(通长)${sizeArr[0]}cm&emsp;(通宽)${sizeArr[1]}cm&emsp;(通高)${sizeArr[2]}cm`;
+  }
+
+  let qualityRes = "-";
+  if (info.quality && info.quality.length) {
+    const qualityArr = info.quality.split(",");
+    qualityRes = qualityArr[0] + qualityArr[1];
+  }
+
+  const fileSelectList = useMemo(() => {
+    return [
+      { id: 1, value: "img", label: "高清图片" },
+      { id: 2, value: "video", label: "视频文件" },
+      { id: 3, value: "audio", label: "音频文件" },
+      { id: 4, value: "model", label: "三维模型" },
+      { id: 5, value: "doc", label: "文档资料" },
+    ];
+  }, []);
+  const [fileSelect, setFileSelect] = useState("img");
+  const fileSelectChangeFu = (value: string) => {
+    setFileSelect(value);
+  };
+
+  // 里面表格格式
+  const columns2 = useMemo(() => {
+    const tempArr = [
+      {
+        title: "附件名称",
+        dataIndex: "name",
+      },
+      {
+        title: "上传时间",
+        dataIndex: "createTime",
+      },
+    ] as any;
+    if (fileSelect === "img") {
+      tempArr.unshift({
+        title: "缩略图",
+        render: (item: any) => (
+          <ImageLazy width={120} height={70} src={item.filePath} />
+        ),
+      });
+    }
+    return tempArr;
+  }, [fileSelect]);
+
+  return (
+    <>
+      <Modal
+        wrapClassName="lookObjTable"
+        destroyOnClose
+        open={show}
+        title="查看藏品"
+        onCancel={closeShow}
+        footer={
+          [] // 设置footer为空,去掉 取消 确定默认按钮
+        }
+      >
+        {/* 商品信息和附件切换 */}
+        <div className="ObjectAddTit">
+          <div
+            onClick={() => setTitSelect("tit1")}
+            className={classNames(titSelect === "tit1" ? "active" : "")}
+          >
+            藏品信息
+          </div>
+          <div
+            onClick={() => setTitSelect("tit2")}
+            className={classNames(
+              "ObjectAddTitTow",
+              titSelect === "tit2" ? "active" : ""
+            )}
+          >
+            附件
+          </div>
+        </div>
+
+        {/* 藏品信息 */}
+        <div className="goodsInfo" hidden={titSelect !== "tit1"}>
+          <div className="row">
+            <div>
+              <span>藏品名称:</span>
+              <p>{info.name}</p>
+            </div>
+            <div>
+              <span>原名:</span>
+              <p>{info.namePrimitive ? info.namePrimitive : "-"}</p>
+            </div>
+          </div>
+          <div className="row">
+            <div>
+              <span>藏品编号名称:</span>
+              <p>{info.dictNum}</p>
+            </div>
+            <div>
+              <span>藏品编号:</span>
+              <p>{info.num ? info.num : "-"}</p>
+            </div>
+          </div>
+          <div className="row">
+            <div>
+              <span>年代:</span>
+              <p>{info.dictAge}</p>
+            </div>
+            <div>
+              <span>文物质地:</span>
+              <p>{info.dictTexture}</p>
+            </div>
+          </div>
+          <div className="row">
+            <div>
+              <span>具体年代:</span>
+              <p>{info.ageSpecific ? info.ageSpecific : "-"}</p>
+            </div>
+            <div>
+              <span>文物类别:</span>
+              <p>{info.dictGoodType}</p>
+            </div>
+          </div>
+
+          <div className="row">
+            <div>
+              <span>实际数量:</span>
+              <p>{info.pcs}</p>
+            </div>
+            <div>
+              <span>文物级别:</span>
+              <p>{info.dictLevel}</p>
+            </div>
+          </div>
+          <div className="rowFull">
+            <span>外形尺寸:</span>
+            <p dangerouslySetInnerHTML={{ __html: sizeRes }}></p>
+          </div>
+          <div className="rowFull">
+            <span>具体尺寸:</span>
+            <p>{info.sizeSpecific}</p>
+          </div>
+
+          <div className="row">
+            <div>
+              <span>质量范围:</span>
+              <p>{info.dictQualityScope}</p>
+            </div>
+            <div>
+              <span>具体质量:</span>
+              <p>{qualityRes}</p>
+            </div>
+          </div>
+
+          <div className="row">
+            <div>
+              <span>完残程度:</span>
+              <p>{info.complete}</p>
+            </div>
+            <div>
+              <span>保存状态:</span>
+              <p>{info.repair ? info.repair : "-"}</p>
+            </div>
+          </div>
+
+          <div className="row">
+            <div>
+              <span>入藏时间范围:</span>
+              <p>{info.dictCheckInScope}</p>
+            </div>
+            <div>
+              <span>入藏年度:</span>
+              <p>{info.checkInYear ? info.checkInYear : "-"}</p>
+            </div>
+          </div>
+
+          <div className="row">
+            <div>
+              <span>著者:</span>
+              <p>{info.author ? info.author : "-"}</p>
+            </div>
+            <div>
+              <span>版本:</span>
+              <p>{info.vision ? info.vision : "-"}</p>
+            </div>
+          </div>
+
+          <div className="rowFull">
+            <span>存卷:</span>
+            <p>{info.onFile ? info.onFile : "-"}</p>
+          </div>
+          <div className="rowFull">
+            <span>来源说明:</span>
+            <p>{info.description ? info.description : "-"}</p>
+          </div>
+          <div className="rowFull">
+            <span>展示状态:</span>
+            <p>{info.display ? '是' : "否"}</p>
+          </div>
+        </div>
+
+        <div className="goodsFile" hidden={titSelect !== "tit2"}>
+          <div className="row">
+            <div className="rowTit">藏品图片:</div>
+            <div className="rowRr">
+              <ImageLazy width={120} height={120} src={info.thumb} />
+            </div>
+          </div>
+
+          <div className="row">
+            <div className="rowTit">附件类型:</div>
+            <div className="rowRr">
+              <Select
+                style={{ width: 150 }}
+                value={fileSelect}
+                onChange={fileSelectChangeFu}
+                options={fileSelectList}
+              />
+            </div>
+          </div>
+
+          {/* 表格主体 */}
+          <Table
+            size="small"
+            scroll={{ y: 300 }}
+            dataSource={fileList[fileSelect]}
+            columns={columns2}
+            rowKey="id"
+            pagination={false}
+          />
+        </div>
+
+        {/* 确定按钮 */}
+        <div className="goodsBtn">
+          <Button type="primary" onClick={closeShow}>
+            确定
+          </Button>
+        </div>
+      </Modal>
+    </>
+  );
+}
+
+const MemoLookModal = React.memo(LookModal);
+
+export default MemoLookModal;

+ 79 - 0
src/components/LookObjTable/index.css

@@ -0,0 +1,79 @@
+.lookObjTable .ant-modal {
+  top: 30px !important;
+  width: 1000px !important;
+}
+.lookObjTable .ObjectAddTit {
+  border-top: 1px solid #999999;
+  padding-top: 15px;
+  width: 100%;
+  height: 45px;
+  display: flex;
+  margin-bottom: 15px;
+}
+.lookObjTable .ObjectAddTit > div {
+  cursor: pointer;
+  width: 50%;
+  height: 30px;
+  border: 1px solid #999999;
+  text-align: center;
+  line-height: 28px;
+  border-radius: 5px 0 0 5px;
+}
+.lookObjTable .ObjectAddTit .ObjectAddTitTow {
+  border-radius: 0 5px 5px 0;
+}
+.lookObjTable .ObjectAddTit .active {
+  background-color: var(--themeColor);
+  color: #fff;
+}
+.lookObjTable .goodsInfo .row {
+  display: flex;
+  width: 100%;
+}
+.lookObjTable .goodsInfo .row > div {
+  padding: 5px;
+  width: 50%;
+  display: flex;
+  border: 1px solid #ccc;
+}
+.lookObjTable .goodsInfo .row > div span {
+  font-weight: 700;
+  display: inline-block;
+  width: 100px;
+  text-align: right;
+}
+.lookObjTable .goodsInfo .row > div p {
+  width: calc(100% - 110px);
+}
+.lookObjTable .goodsInfo .rowFull {
+  display: flex;
+  padding: 5px;
+  border: 1px solid #ccc;
+}
+.lookObjTable .goodsInfo .rowFull span {
+  font-weight: 700;
+  display: inline-block;
+  width: 100px;
+  text-align: right;
+}
+.lookObjTable .goodsInfo .rowFull p {
+  width: calc(100% - 110px);
+}
+.lookObjTable .goodsBtn {
+  margin-top: 20px;
+  text-align: center;
+}
+.lookObjTable .goodsFile .row {
+  width: 100%;
+  display: flex;
+  margin-bottom: 10px;
+}
+.lookObjTable .goodsFile .row .rowTit {
+  font-weight: 700;
+  display: inline-block;
+  width: 100px;
+  text-align: right;
+}
+.lookObjTable .goodsFile .row .rowRr {
+  width: calc(100% - 110px);
+}

+ 120 - 0
src/components/LookObjTable/index.less

@@ -0,0 +1,120 @@
+.lookObjTable {
+  .ant-modal {
+    top: 30px !important;
+    width: 1000px !important;
+  }
+
+  .ObjectAddTit {
+    border-top: 1px solid #999999;
+    padding-top: 15px;
+    width: 100%;
+    height: 45px;
+    display: flex;
+    margin-bottom: 15px;
+
+    &>div {
+      cursor: pointer;
+      width: 50%;
+      height: 30px;
+      border: 1px solid #999999;
+      text-align: center;
+      line-height: 28px;
+      border-radius: 5px 0 0 5px;
+    }
+
+    .ObjectAddTitTow {
+      border-radius: 0 5px 5px 0;
+    }
+
+    .active {
+      background-color: var(--themeColor);
+      color: #fff;
+    }
+  }
+
+  .goodsInfo {
+
+    .row {
+      display: flex;
+      width: 100%;
+
+      &>div {
+        padding: 5px;
+        width: 50%;
+        display: flex;
+        border: 1px solid #ccc;
+
+        span {
+          font-weight: 700;
+          display: inline-block;
+          width: 100px;
+          text-align: right;
+        }
+
+        p {
+          width: calc(100% - 110px);
+        }
+      }
+    }
+    .rowFull{
+      display: flex;
+      padding: 5px;
+      border: 1px solid #ccc;
+      span {
+        font-weight: 700;
+        display: inline-block;
+        width: 100px;
+        text-align: right;
+      }
+
+      p {
+        width: calc(100% - 110px);
+      }
+    }
+
+    // .rowThree {
+    //   display: flex;
+    //   width: 100%;
+
+    //   &>div {
+    //     padding: 5px;
+
+    //     border: 1px solid #ccc;
+    //     width: 33%;
+
+    //     span {
+    //       font-weight: 700;
+    //       display: inline-block;
+    //       width: 100px;
+    //       text-align: right;
+    //     }
+    //   }
+    // }
+
+  }
+
+  .goodsBtn {
+    margin-top: 20px;
+    text-align: center;
+  }
+
+  .goodsFile {
+
+    .row {
+      width: 100%;
+      display: flex;
+      margin-bottom: 10px;
+
+      .rowTit {
+        font-weight: 700;
+        display: inline-block;
+        width: 100px;
+        text-align: right;
+      }
+
+      .rowRr {
+        width: calc(100% - 110px);
+      }
+    }
+  }
+}

+ 95 - 0
src/components/LookObjTable/index.tsx

@@ -0,0 +1,95 @@
+import { Button, Table } from "antd";
+import React, { useCallback, useMemo, useRef, useState } from "react";
+import ImageLazy from "../ImageLazy";
+import LookModal from "./LookModal";
+
+type Props = {
+  data: any;
+  y: number;
+};
+
+function LookObjTable({ data, y }: Props) {
+  const lookIdRef = useRef(-1);
+
+  // 点击表格里面的查看
+  const lookGoods = useCallback((id: number) => {
+    lookIdRef.current = id;
+    setShow(true);
+  }, []);
+
+  // 外层表格格式
+  const columns = useMemo(() => {
+    const tempArr = [
+      {
+        title: "缩略图",
+        render: (item: any) => (
+          <ImageLazy width={120} height={70} src={item.thumb} />
+        ),
+      },
+      {
+        title: "藏品编号名称",
+        dataIndex: "dictNum",
+      },
+      {
+        title: "藏品编号",
+        render: (item: any) => (item.num ? item.num : "-"),
+      },
+      {
+        title: "藏品名称",
+        dataIndex: "name",
+      },
+      {
+        title: "类别",
+        dataIndex: "dictGoodType",
+      },
+      {
+        title: "完残程度",
+        dataIndex: "complete",
+      },
+      {
+        title: "年代",
+        dataIndex: "dictAge",
+      },
+      {
+        title: "操作",
+        render: (item: any) => (
+          <>
+            <Button type="text" danger onClick={() => lookGoods(item.id)}>
+              查看
+            </Button>
+          </>
+        ),
+      },
+    ];
+
+    return tempArr;
+  }, [lookGoods]);
+
+  // 控制弹窗的显示隐藏
+  const [show, setShow] = useState(false);
+
+  return (
+    <div>
+      <Table
+        size="small"
+        scroll={{ y }}
+        dataSource={data}
+        columns={columns}
+        rowKey="id"
+        pagination={false}
+      />
+      {/* 点击查看出来的对话框 */}
+      {show ? (
+        <LookModal
+          id={lookIdRef.current}
+          show={show}
+          closeShow={() => setShow(false)}
+        />
+      ) : null}
+    </div>
+  );
+}
+
+const MemoLookObjTable = React.memo(LookObjTable);
+
+export default MemoLookObjTable;

+ 29 - 0
src/components/Message/index.tsx

@@ -0,0 +1,29 @@
+import React, { useEffect } from "react";
+import { message } from "antd";
+import { useSelector } from "react-redux";
+import { RootState } from "@/store";
+
+function MessageCom() {
+  // 从仓库中获取 antd 轻提示信息
+  const messageReducerInfo = useSelector(
+    (state: RootState) => state.loginStore.message
+  );
+
+  const [messageApi, contextHolder] = message.useMessage();
+
+  useEffect(() => {
+    if (messageReducerInfo.txt) {
+      messageApi.open({
+        type: messageReducerInfo.type,
+        content: messageReducerInfo.txt,
+        duration: messageReducerInfo.duration,
+      });
+    }
+  }, [messageApi, messageReducerInfo]);
+
+  return <>{contextHolder}</>;
+}
+
+const MemoMessage = React.memo(MessageCom);
+
+export default MemoMessage;

+ 32 - 0
src/components/NotFound/index.tsx

@@ -0,0 +1,32 @@
+import history from "@/utils/history";
+import { Button, Result } from "antd";
+import { useEffect, useRef } from "react";
+
+export default function NotFound() {
+  const timeRef = useRef(-1);
+
+  useEffect(() => {
+    timeRef.current = window.setTimeout(() => {
+      const dom: any = document.querySelector(".noFindPage");
+      dom.style.opacity = 1;
+    }, 200);
+    return () => {
+      clearTimeout(timeRef.current);
+    };
+  }, []);
+
+  return (
+    <div className="noFindPage">
+      <Result
+        status="404"
+        title="404"
+        subTitle="页面找不到了!"
+        extra={
+          <Button onClick={() => history.push("/")} type="primary">
+            去首页
+          </Button>
+        }
+      />
+    </div>
+  );
+}

+ 157 - 0
src/components/ObjectAdd/index.css

@@ -0,0 +1,157 @@
+/* 添加新藏品的弹出框 */
+.ObjectAdd .ObjectAddTit {
+  border-top: 1px solid #999999;
+  padding-top: 15px;
+  width: 100%;
+  height: 45px;
+  display: flex;
+  margin-bottom: 15px;
+}
+.ObjectAdd .ObjectAddTit > div {
+  cursor: pointer;
+  width: 50%;
+  height: 30px;
+  border: 1px solid #999999;
+  text-align: center;
+  line-height: 28px;
+  border-radius: 5px 0 0 5px;
+}
+.ObjectAdd .ObjectAddTit .ObjectAddTitTow {
+  border-radius: 0 5px 5px 0;
+}
+.ObjectAdd .ObjectAddTit .active {
+  background-color: var(--themeColor);
+  color: #fff;
+}
+.ObjectAdd .ant-modal-close {
+  display: none;
+}
+.ObjectAdd .ant-modal {
+  top: 30px !important;
+  width: 1000px !important;
+}
+.ObjectAdd .formRow {
+  display: flex;
+  justify-content: space-between;
+  position: relative;
+}
+.ObjectAdd .formRow .bs {
+  position: absolute;
+  top: 7px;
+  left: 11px;
+  z-index: 10;
+  color: #ff4d4f;
+}
+.ObjectAdd .formRow .bs2 {
+  left: 70px;
+}
+.ObjectAdd .formRow .bs3 {
+  left: 516px;
+}
+.ObjectAdd .formRow .bs4 {
+  left: 39px;
+}
+.ObjectAdd .formRow .ant-form-item-label {
+  width: 119px;
+}
+.ObjectAdd .formRow .ant-form-item {
+  width: 50%;
+}
+.ObjectAdd .formRow .ant-form-item input {
+  width: 350px;
+}
+.ObjectAdd .formRow .ant-form-item .ant-select-in-form-item {
+  width: 358px;
+}
+.ObjectAdd .formRow .ant-form-item .ant-input-number {
+  width: 358px;
+}
+.ObjectAdd .formRow .formRowSelec input {
+  width: 298px;
+}
+.ObjectAdd .formRow .formRowSelec .ant-input-number {
+  width: 300px;
+}
+.ObjectAdd .formRowThree .formRowThreeRow {
+  margin-left: -120px;
+}
+.ObjectAdd .formRowThree .ant-form-item {
+  width: 25.5%;
+}
+.ObjectAdd .formRowThree .ant-form-item input {
+  width: 200px;
+}
+.ObjectAdd .formRowThree .ant-form-item .ant-input-number {
+  width: 200px;
+}
+.ObjectAdd .formRowFull .ant-form-item {
+  width: 100%;
+}
+.ObjectAdd .formRowFull .ant-form-item input {
+  width: 850px;
+}
+.ObjectAdd .formRowFull .ant-form-item .ant-select-in-form-item {
+  width: 858px;
+}
+.ObjectAdd .formRowLast {
+  margin-top: -20px;
+}
+.ObjectAdd .fileBox .fileBoxRow {
+  margin-bottom: 24px;
+  display: flex;
+}
+.ObjectAdd .fileBox .fileBoxRow .fileBoxRow_tit {
+  width: 119px;
+  text-align: right;
+}
+.ObjectAdd .fileBox .fileBoxRow .fileBoxRow_tit2 {
+  line-height: 30px;
+}
+.ObjectAdd .fileBox .fileBoxRow .fileBoxRowUpTit {
+  height: 32px;
+  line-height: 32px;
+  font-size: 14px;
+  color: #7e7c7c;
+}
+.ObjectAdd .fileBox .fileBoxRow .fileBoxRow_r {
+  padding-left: 20px;
+  position: relative;
+}
+.ObjectAdd .fileBox .fileBoxRow .fileBoxRow_r input {
+  display: none;
+}
+.ObjectAdd .fileBox .fileBoxRow .fileBoxRow_r .fileBoxRow_up {
+  cursor: pointer;
+  font-size: 30px;
+  width: 100px;
+  height: 100px;
+  background-color: #fafafa;
+  border: 1px dashed #ccc;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+.ObjectAdd .fileBox .fileBoxRow .fileBoxRow_r .fileBoxRow_r_img {
+  width: 120px;
+  height: 120px;
+  position: relative;
+}
+.ObjectAdd .fileBox .fileBoxRow .fileBoxRow_r .fileBoxRow_r_img .clearCover {
+  cursor: pointer;
+  z-index: 10;
+  position: absolute;
+  width: 50px;
+  height: 50px;
+  top: 50%;
+  transform: translateY(-50%);
+  right: -50px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  font-size: 24px;
+}
+.ObjectAdd .fileBox .fileBoxRow .fileBoxRow_r .fileBoxRow_r_tit {
+  margin-top: 5px;
+  font-size: 14px;
+  color: #7e7c7c;
+}

+ 211 - 0
src/components/ObjectAdd/index.less

@@ -0,0 +1,211 @@
+/* 添加新藏品的弹出框 */
+.ObjectAdd {
+
+  .ObjectAddTit {
+    border-top: 1px solid #999999;
+    padding-top: 15px;
+    width: 100%;
+    height: 45px;
+    display: flex;
+    margin-bottom: 15px;
+
+    &>div {
+      cursor: pointer;
+      width: 50%;
+      height: 30px;
+      border: 1px solid #999999;
+      text-align: center;
+      line-height: 28px;
+      border-radius: 5px 0 0 5px;
+    }
+
+    .ObjectAddTitTow {
+      border-radius: 0 5px 5px 0;
+    }
+
+    .active {
+      background-color: var(--themeColor);
+      color: #fff;
+    }
+  }
+
+  .ant-modal-close {
+    display: none;
+  }
+
+  .ant-modal {
+    top: 30px !important;
+    width: 1000px !important;
+  }
+
+  .formRow {
+    display: flex;
+    justify-content: space-between;
+    position: relative;
+
+    .bs {
+      position: absolute;
+      top: 7px;
+      left: 11px;
+      z-index: 10;
+      color: #ff4d4f;
+    }
+
+    .bs2 {
+      left: 70px;
+    }
+
+    .bs3 {
+      left: 516px;
+    }
+
+    .bs4 {
+      left: 39px;
+    }
+
+    .ant-form-item-label {
+      width: 119px;
+    }
+
+    .ant-form-item {
+      width: 50%;
+
+      input {
+        width: 350px;
+      }
+
+      .ant-select-in-form-item {
+        width: 358px;
+      }
+
+      .ant-input-number {
+        width: 358px;
+      }
+    }
+
+    .formRowSelec {
+      input {
+        width: 298px;
+      }
+
+      .ant-input-number {
+        width: 300px;
+      }
+
+    }
+  }
+
+  .formRowThree {
+
+    .formRowThreeRow {
+      margin-left: -120px;
+    }
+
+    .ant-form-item {
+
+      width: 25.5%;
+
+      input {
+        width: 200px;
+      }
+
+      .ant-input-number {
+        width: 200px;
+      }
+    }
+  }
+
+  .formRowFull {
+    .ant-form-item {
+      width: 100%;
+
+      input {
+        width: 850px;
+      }
+
+      .ant-select-in-form-item {
+        width: 858px;
+      }
+
+    }
+  }
+
+  .formRowLast {
+    margin-top: -20px;
+  }
+
+  // 附件
+  .fileBox {
+    .fileBoxRow {
+      margin-bottom: 24px;
+      display: flex;
+
+      .fileBoxRow_tit {
+        width: 119px;
+        text-align: right;
+      }
+
+      .fileBoxRow_tit2 {
+        line-height: 30px;
+      }
+
+      .fileBoxRowUpTit {
+        height: 32px;
+        line-height: 32px;
+        font-size: 14px;
+        color: #7e7c7c;
+      }
+
+      .fileBoxRow_r {
+        padding-left: 20px;
+        position: relative;
+
+        input {
+          display: none;
+        }
+
+        .fileBoxRow_up {
+          cursor: pointer;
+          font-size: 30px;
+          width: 100px;
+          height: 100px;
+          background-color: #fafafa;
+          border: 1px dashed #ccc;
+          display: flex;
+          justify-content: center;
+          align-items: center;
+
+
+        }
+
+        .fileBoxRow_r_img {
+          width: 120px;
+          height: 120px;
+          position: relative;
+
+          .clearCover {
+            cursor: pointer;
+            z-index: 10;
+            position: absolute;
+            width: 50px;
+            height: 50px;
+            top: 50%;
+            transform: translateY(-50%);
+            right: -50px;
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            font-size: 24px;
+          }
+        }
+
+        .fileBoxRow_r_tit {
+          margin-top: 5px;
+          font-size: 14px;
+          color: rgb(126, 124, 124);
+        }
+      }
+    }
+  }
+
+}

+ 9 - 0
src/components/ObjectAdd/index.module.scss

@@ -0,0 +1,9 @@
+.ObjectAdd{
+  position: fixed;
+  z-index: 10;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background-color: rgba(255,255,255,.6);
+}

+ 926 - 0
src/components/ObjectAdd/index.tsx

@@ -0,0 +1,926 @@
+import {
+  Button,
+  Cascader,
+  Form,
+  Input,
+  InputNumber,
+  Modal,
+  Popconfirm,
+  Radio,
+  Select,
+  Table,
+} from "antd";
+import { PlusOutlined, CloseCircleOutlined } from "@ant-design/icons";
+import TextArea from "antd/es/input/TextArea";
+import React, {
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
+import styles from "./index.module.scss";
+import classNames from "classnames";
+import "./index.css";
+import { useDispatch, useSelector } from "react-redux";
+import { RootState } from "@/store";
+import ImageLazy from "../ImageLazy";
+import {
+  delInfileAPI,
+  getInfoInAPI,
+  goodsSonAddAPI,
+  object1AddUpFileAPI,
+} from "@/store/action/object1";
+import { goodsChangeObj } from "@/utils/dataChange";
+import { editObj2GoodsAPI } from "@/store/action/object2";
+import { MessageFu } from "@/utils/message";
+const { Option } = Select;
+
+type Props = {
+  id: any;
+  colsePage: any;
+  editId?: any;
+  dirCode: any;
+  upInfoAPIFu?: any;
+};
+
+function ObjectAdd({ id, colsePage, editId, dirCode, upInfoAPIFu }: Props) {
+  const dispatch = useDispatch();
+
+  // 没有通过校验
+  const onFinishFailed = () => {
+    setTitSelect("tit1");
+    return MessageFu.warning("有表单不符号规则!");
+  };
+
+  // --------通过校验之后发送请求----------点击确定
+  const onFinish = async (values: any) => {
+    const oldObj = {} as any;
+    for (const k in values) {
+      if (values[k]) oldObj[k] = values[k];
+    }
+
+    // 外形尺寸 3个 字段 变成一个组数
+    const size = [values.size1, values.size2, values.size3];
+    // 具体质量 + 单位
+    let quality = [] as any;
+    if (values.quality) quality = [values.quality, danweiValue];
+    // 文件id
+    const fileIds = [] as any;
+
+    // 新的完整附件数据
+    const newDataFile = [] as any;
+
+    for (const k in results) {
+      results[k].forEach((v: any) => {
+        fileIds.push(v.id);
+        newDataFile.push(v);
+      });
+    }
+    // 年代
+    const dictAge = values.dictAge.join("/");
+    // 文物质地
+    const dictTexture = values.dictTexture.join("/");
+
+    const obj = {
+      ...oldObj,
+      id: id ? id : null,
+      size: size.join(","),
+      quality: quality.join(","),
+      thumb: cover,
+      dirCode: dirCode + "",
+      fileIds: fileIds.join(","),
+      registerId: dirCode,
+      dictAge,
+      dictTexture,
+      display: values.display,
+    };
+
+    // -----------------藏品修改
+    if (editId === "edit") {
+      // 表单信息对比
+      const oldData = oldDataRef.current.entity;
+
+      // 有变化的数据---更新前
+      const before = {} as any;
+      // 有变化的数据---更新后
+      const after = {} as any;
+
+      for (const k in obj) {
+        if (oldData[k] !== obj[k] && goodsChangeObj[k] && k !== "fileIds") {
+          before[k] = oldData[k];
+          after[k] = obj[k];
+        } else if (k === "fileIds") {
+          // 单独处理附件信息
+          before["fileIds"] = oldDataRef.current.file
+            .map((v: any) => v.id)
+            .join(",");
+          after["fileIds"] = newDataFile.map((v: any) => v.id).join(",");
+
+          before["fileIdsName"] = oldDataRef.current.file
+            .map((v: any) => v.name)
+            .join(",");
+          after["fileIdsName"] = newDataFile.map((v: any) => v.name).join(",");
+
+          if (
+            before["fileIds"] === after["fileIds"] ||
+            before["fileIdsName"] === after["fileIdsName"]
+          ) {
+            delete before["fileIds"];
+            delete after["fileIds"];
+            delete before["fileIdsName"];
+            delete after["fileIdsName"];
+          }
+        }
+      }
+      if (JSON.stringify(before) === "{}")
+        return MessageFu.warning("没有改变藏品的信息!");
+
+      const editData = {
+        afterJson: after,
+        beforeJson: before,
+        goodsIds: Number(id),
+      };
+
+      const res: any = await editObj2GoodsAPI(editData);
+      if (res.code === 0) {
+        MessageFu.success("编辑成功!");
+        // 通知父组件更新页面
+        upInfoAPIFu();
+        // 真正的删除 ----在藏品总账里面修改的时候不能删除
+        // if (delFileList.current.length) {
+        //   await delInfileAPI(delFileList.current.join(","));
+        // }
+        //  关闭弹窗
+        colsePage();
+      }
+
+      console.log("旧数据", before);
+      console.log("新数据", after);
+    } else {
+      // ------------藏品新增和编辑
+      await dispatch(goodsSonAddAPI(obj, id));
+
+      // 真正的删除
+      if (delFileList.current.length) {
+        await delInfileAPI(delFileList.current.join(","));
+      }
+      //  关闭弹窗
+      colsePage();
+    }
+  };
+
+  // 选择商品信息还是附近(默认商品信息)
+  const [titSelect, setTitSelect] = useState("tit1");
+
+  // 从仓库中获取各下拉数据
+  const options1 = useSelector(
+    (state: RootState) => state.loginStore.selectAll["藏品编号名称"]
+  );
+
+  const options2 = useSelector(
+    (state: RootState) => state.loginStore.selectAll["年代"]
+  );
+  const options3 = useSelector(
+    (state: RootState) => state.loginStore.selectAll["质地"]
+  );
+  const options4 = useSelector(
+    (state: RootState) => state.loginStore.selectAll["文物类别"]
+  );
+  const options5 = useSelector(
+    (state: RootState) => state.loginStore.selectAll["文物级别"]
+  );
+  const options6 = useSelector(
+    (state: RootState) => state.loginStore.selectAll["质量范围"]
+  );
+  const options7 = useSelector(
+    (state: RootState) => state.loginStore.selectAll["完残程度"]
+  );
+  const options8 = useSelector(
+    (state: RootState) => state.loginStore.selectAll["保存状态"]
+  );
+  const options9 = useSelector(
+    (state: RootState) => state.loginStore.selectAll["入藏时间范围"]
+  );
+
+  // 单位选择
+  const [danweiValue, setDanweiValue] = useState("kg");
+
+  // 把旧的数据保存起来--用于藏品修改的对比
+  const oldDataRef = useRef<any>({});
+
+  // 设置表单初始数据(区分编辑和新增)
+  const FormBoxRef = useRef<any>({});
+  const getInfoInAPIFu = useCallback(
+    async (id: number) => {
+      const res = await getInfoInAPI(id);
+
+      // 把旧的数据存起来
+      oldDataRef.current = res.data;
+
+      const data = res.data.entity;
+      const size = data.size.split(",");
+
+      const quality = data.quality ? data.quality.split(",") : ["", "kg"];
+
+      const dictAge = data.dictAge.split(",");
+      const dictTexture = data.dictTexture.split(",");
+      FormBoxRef.current.setFieldsValue({
+        ...data,
+        size1: size[0],
+        size2: size[1],
+        size3: size[2],
+        quality: quality[0],
+        dictAge,
+        dictTexture,
+      });
+      // 具体质量的单位
+      setDanweiValue(quality[1]);
+      // 藏品图片
+      setCover(data.thumb);
+
+      const fileData = {
+        img: [],
+        video: [],
+        audio: [],
+        model: [],
+        doc: [],
+      } as any;
+      res.data.file.forEach((v: any) => {
+        fileData[v.type].push(v);
+      });
+      // 修改附件仓库信息
+      dispatch({ type: "login/setFileData", payload: fileData });
+    },
+    [dispatch]
+  );
+
+  // 进页面看看是编辑就 从仓库获取信息,新增就设置默认值
+  useEffect(() => {
+    if (id) {
+      getInfoInAPIFu(id);
+    } else {
+      FormBoxRef.current.setFieldsValue({
+        dictNum: options1[0].name ? options1[0].name : null,
+        dictAge: ["地质年代", "显生宙", "新生代", "第四纪"],
+        dictTexture: ["单一质地", "有机质", "竹"],
+        dictGoodType: options4[0].name ? options4[0].name : null,
+        dictLevel: options5[0].name ? options5[0].name : null,
+        dictQualityScope: options6[0].name ? options6[0].name : null,
+        complete: options7[0].name ? options7[0].name : null,
+        dictCheckInScope: options9[0].name ? options9[0].name : null,
+        display: 0,
+      });
+      // 初始化附件仓库信息
+      dispatch({
+        type: "login/setFileData",
+        payload: {
+          img: [],
+          video: [],
+          audio: [],
+          model: [],
+          doc: [],
+        },
+      });
+    }
+  }, [
+    dispatch,
+    getInfoInAPIFu,
+    id,
+    options1,
+    options4,
+    options5,
+    options6,
+    options7,
+    options9,
+  ]);
+
+  // -----------关于藏品附件的信息
+  const [fileSelect, setFileSelect] = useState("img");
+
+  const fileSelectChangeFu = (value: string) => {
+    setFileSelect(value);
+  };
+
+  const results = useSelector((state: RootState) => state.loginStore.fileData);
+
+  // 点击表格里面的删除
+  const delOne = useCallback(
+    (id: number) => {
+      const data = {
+        ...results,
+        [fileSelect]: results[fileSelect].filter((v: any) => v.id !== id),
+      };
+      dispatch({ type: "login/setFileData", payload: data });
+
+      // 把删除的id存起来
+      delFileList.current.push(id);
+    },
+    [dispatch, fileSelect, results]
+  );
+
+  // 表格格式
+  const columns = useMemo(() => {
+    const tempArr = [
+      {
+        title: "附件名称",
+        dataIndex: "name",
+      },
+      {
+        title: "上传时间",
+        dataIndex: "createTime",
+      },
+      {
+        title: "操作",
+        render: (item: any) => (
+          <>
+            <Button type="text" danger onClick={() => upFileFu(item.id)}>
+              重传
+            </Button>
+            <Popconfirm
+              title="确定删除吗?"
+              okText="确定"
+              cancelText="取消"
+              onConfirm={() => delOne(item.id)}
+            >
+              <Button type="text" danger>
+                删除
+              </Button>
+            </Popconfirm>
+          </>
+        ),
+      },
+    ];
+
+    if (fileSelect === "img") {
+      tempArr.unshift({
+        title: "缩略图",
+        render: (item: any) => (
+          <div className="tableImg">
+            <ImageLazy width={120} height={70} src={item.filePath} />
+          </div>
+        ),
+      });
+    }
+
+    return tempArr;
+  }, [delOne, fileSelect]);
+
+  // 封面地址
+  const [cover, setCover] = useState("");
+
+  const fileSelectList = useMemo(() => {
+    return [
+      { id: 1, value: "img", label: "高清图片" },
+      { id: 2, value: "video", label: "视频文件" },
+      { id: 3, value: "audio", label: "音频文件" },
+      { id: 4, value: "model", label: "三维模型" },
+      { id: 5, value: "doc", label: "文档资料" },
+    ];
+  }, []);
+
+  // 上传附件的进度条
+  const UpAsyncLodingDom: any = document.querySelector("#UpAsyncLoding");
+  const progressDom: any = document.querySelector("#progress");
+
+  // 上传封面
+  const myInput = useRef<HTMLInputElement>(null);
+  const handeUpPhoto = async (e: React.ChangeEvent<HTMLInputElement>) => {
+    if (e.target.files) {
+      // 拿到files信息
+      const filesInfo = e.target.files[0];
+      // 校验格式
+      const type = ["image/jpeg", "image/png"];
+      if (!type.includes(filesInfo.type)) {
+        e.target.value = "";
+        return MessageFu.warning("只支持jpg、png格式!");
+      }
+      // 校验大小
+      if (filesInfo.size > 10 * 1024 * 1024) {
+        e.target.value = "";
+        return MessageFu.warning("最大支持10M!");
+      }
+      // 创建FormData对象
+      const fd = new FormData();
+      // 把files添加进FormData对象(‘photo’为后端需要的字段)
+      fd.append("type", "thumb");
+      fd.append("dirCode", dirCode);
+      fd.append("file", filesInfo);
+
+      e.target.value = "";
+
+      const res: any = await object1AddUpFileAPI(fd);
+      if (res.code === 0) {
+        MessageFu.success("上传成功!");
+        setCover(res.data.filePath);
+      }
+      UpAsyncLodingDom.style.opacity = 0;
+      progressDom.style.width = "0%";
+    }
+  };
+
+  // 上传附件
+  const myInput2 = useRef<HTMLInputElement>(null);
+
+  const upFileFlagRef = useRef(false);
+
+  const delFileList = useRef<any>([]);
+
+  const upFileFu = (flag?: any) => {
+    upFileFlagRef.current = flag ? flag : 0;
+    myInput2.current?.click();
+  };
+  const handeUpPhoto2 = useCallback(
+    async (e: React.ChangeEvent<HTMLInputElement>) => {
+      if (e.target.files) {
+        // 拿到files信息
+        const filesInfo = e.target.files[0];
+
+        let type = [] as string[];
+
+        let tit = "";
+
+        if (
+          fileSelect === "img" ||
+          fileSelect === "video" ||
+          fileSelect === "audio"
+        ) {
+          if (fileSelect === "img") {
+            type = ["image/jpeg", "image/png", "image/gif"];
+            tit = "只支持jpg、png、gif格式!";
+          } else if (fileSelect === "video") {
+            type = ["video/mp4"];
+            tit = "只支持mp4格式!";
+          } else if (fileSelect === "audio") {
+            type = ["audio/mpeg"];
+            tit = "只支持mp3格式!";
+          }
+          // 校验格式
+          if (!type.includes(filesInfo.type)) {
+            e.target.value = "";
+            return MessageFu.warning(tit);
+          }
+        }
+
+        // 创建FormData对象
+        const fd = new FormData();
+        // 把files添加进FormData对象(‘photo’为后端需要的字段)
+        fd.append("type", fileSelect);
+        fd.append("dirCode", dirCode);
+        fd.append("file", filesInfo);
+        e.target.value = "";
+
+        const res: any = await object1AddUpFileAPI(fd);
+        if (res.code === 0) {
+          MessageFu.success("上传成功!");
+          if (upFileFlagRef.current) {
+            // 重新上传
+            const id = upFileFlagRef.current;
+            const data = {
+              ...results,
+              [fileSelect]: results[fileSelect].map((v: any) => {
+                if (id !== v.id) return v;
+                else
+                  return {
+                    ...res.data,
+                  };
+              }),
+            };
+            dispatch({ type: "login/setFileData", payload: data });
+
+            // 把删除的id存起来
+            delFileList.current.push(upFileFlagRef.current);
+          } else {
+            // 新上传
+            const data = {
+              ...results,
+              [fileSelect]: [
+                {
+                  ...res.data,
+                },
+                ...results[fileSelect],
+              ],
+            };
+
+            dispatch({ type: "login/setFileData", payload: data });
+          }
+        }
+        UpAsyncLodingDom.style.opacity = 0;
+        progressDom.style.width = "0%";
+      }
+    },
+    [
+      UpAsyncLodingDom.style,
+      dirCode,
+      dispatch,
+      fileSelect,
+      progressDom.style,
+      results,
+    ]
+  );
+
+  return (
+    <div className={styles.ObjectAdd}>
+      <Modal
+        wrapClassName="ObjectAdd"
+        destroyOnClose
+        open={true}
+        title={id ? "编辑藏品" : "添加藏品"}
+        footer={
+          [] // 设置footer为空,去掉 取消 确定默认按钮
+        }
+      >
+        {/* 商品信息和附件切换 */}
+        <div className="ObjectAddTit">
+          <div
+            onClick={() => setTitSelect("tit1")}
+            className={classNames(titSelect === "tit1" ? "active" : "")}
+          >
+            藏品信息
+          </div>
+          <div
+            onClick={() => setTitSelect("tit2")}
+            className={classNames(
+              "ObjectAddTitTow",
+              titSelect === "tit2" ? "active" : ""
+            )}
+          >
+            附件
+          </div>
+        </div>
+
+        <Form
+          ref={FormBoxRef}
+          name="basic"
+          onFinish={onFinish}
+          onFinishFailed={onFinishFailed}
+          autoComplete="off"
+        >
+          {/* -----------------------表单信息 */}
+          <div className="formBox" hidden={titSelect === "tit2"}>
+            {/* 藏品名称----------原名 */}
+            <div className="formRow">
+              <Form.Item
+                label="藏品名称"
+                name="name"
+                rules={[{ required: true, message: "不能为空!" }]}
+                getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
+              >
+                <Input maxLength={50} showCount placeholder="请输入" />
+              </Form.Item>
+              <Form.Item label="原名" name="namePrimitive">
+                <Input maxLength={50} showCount placeholder="请输入" />
+              </Form.Item>
+            </div>
+            {/* 藏品编号名称----------藏品编号 */}
+            <div className="formRow">
+              <div className="bs">*</div>
+              <Form.Item label="藏品编号名称" name="dictNum">
+                <Select
+                  placeholder="请选择"
+                  options={options1.map((v: any) => ({
+                    label: v.name,
+                    value: v.name,
+                  }))}
+                />
+              </Form.Item>
+              <Form.Item label="藏品编号" name="num">
+                <Input maxLength={50} showCount placeholder="请输入" />
+              </Form.Item>
+            </div>
+            {/* 年代----------文物质地 */}
+            <div className="formRow">
+              <div className="bs bs2">*</div>
+              <div className="bs bs3">*</div>
+              <Form.Item label="年代" name="dictAge">
+                <Cascader
+                  allowClear={false}
+                  fieldNames={{
+                    label: "name",
+                    value: "name",
+                    children: "children",
+                  }}
+                  options={options2}
+                  placeholder="请选择"
+                />
+              </Form.Item>
+              <Form.Item label="文物质地" name="dictTexture">
+                <Cascader
+                  allowClear={false}
+                  fieldNames={{
+                    label: "name",
+                    value: "name",
+                    children: "children",
+                  }}
+                  options={options3}
+                  placeholder="请选择"
+                />
+              </Form.Item>
+            </div>
+            {/* 具体年代----------文物类别 */}
+            <div className="formRow">
+              <div className="bs bs3">*</div>
+              <Form.Item label="具体年代" name="ageSpecific">
+                <Input maxLength={50} showCount placeholder="请输入" />
+              </Form.Item>
+              <Form.Item label="文物类别" name="dictGoodType">
+                <Select
+                  placeholder="请选择"
+                  options={options4.map((v: any) => ({
+                    label: v.name,
+                    value: v.name,
+                  }))}
+                />
+              </Form.Item>
+            </div>
+            {/* 实际数量----------文物级别 */}
+            <div className="formRow">
+              <div className="bs bs3">*</div>
+              <Form.Item
+                rules={[{ required: true, message: "不能为空!" }]}
+                label="实际数量"
+                name="pcs"
+              >
+                <InputNumber min={1} maxLength={8} placeholder="请输入数字" />
+              </Form.Item>
+              <Form.Item label="文物级别" name="dictLevel">
+                <Select
+                  placeholder="请选择"
+                  options={options5.map((v: any) => ({
+                    label: v.name,
+                    value: v.name,
+                  }))}
+                />
+              </Form.Item>
+            </div>
+            {/* 外形尺寸----------通长,宽,高 */}
+            <div className="formRow formRowThree">
+              <Form.Item
+                rules={[{ required: true, message: "不能为空!" }]}
+                label="外形尺寸"
+                name="size1"
+              >
+                <InputNumber
+                  min={1}
+                  maxLength={8}
+                  placeholder="通长"
+                  addonAfter="cm"
+                />
+              </Form.Item>
+              <Form.Item
+                rules={[{ required: true, message: "不能为空!" }]}
+                label=""
+                name="size2"
+              >
+                <InputNumber
+                  min={1}
+                  maxLength={8}
+                  placeholder="通宽"
+                  addonAfter="cm"
+                />
+              </Form.Item>
+              <Form.Item
+                className="formRowThreeRow"
+                rules={[{ required: true, message: "不能为空!" }]}
+                label=""
+                name="size3"
+              >
+                <InputNumber
+                  min={1}
+                  maxLength={8}
+                  placeholder="通高"
+                  addonAfter="cm"
+                />
+              </Form.Item>
+            </div>
+            {/* 具体尺寸*/}
+            <div className="formRow formRowFull">
+              <Form.Item
+                label="具体尺寸"
+                name="sizeSpecific"
+                rules={[{ required: true, message: "不能为空!" }]}
+                getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
+              >
+                <Input maxLength={50} showCount placeholder="请输入" />
+              </Form.Item>
+            </div>
+            {/* 质量范围----------具体质量 */}
+            <div className="formRow">
+              <div className="bs bs4">*</div>
+              <Form.Item label="质量范围" name="dictQualityScope">
+                <Select
+                  placeholder="请选择"
+                  options={options6.map((v: any) => ({
+                    label: v.name,
+                    value: v.name,
+                  }))}
+                />
+              </Form.Item>
+              <Form.Item
+                className="formRowSelec"
+                label="具体质量"
+                name="quality"
+              >
+                <InputNumber
+                  min={1}
+                  maxLength={8}
+                  placeholder="请输入数字"
+                  addonAfter={
+                    <Select
+                      value={danweiValue}
+                      onChange={(val) => setDanweiValue(val)}
+                    >
+                      <Option value="kg">kg</Option>
+                      <Option value="g">g</Option>
+                    </Select>
+                  }
+                />
+                {/* <div className="formRowSelecSon">
+                
+              </div> */}
+              </Form.Item>
+            </div>
+            {/* 完残程度----------保存状态 */}
+            <div className="formRow">
+              <div className="bs bs4">*</div>
+              <Form.Item label="完残程度" name="complete">
+                <Select
+                  placeholder="请选择"
+                  options={options7.map((v: any) => ({
+                    label: v.name,
+                    value: v.name,
+                  }))}
+                />
+              </Form.Item>
+              <Form.Item label="保存状态" name="repair">
+                <Select
+                  placeholder="请选择"
+                  options={options8.map((v: any) => ({
+                    label: v.name,
+                    value: v.name,
+                  }))}
+                />
+              </Form.Item>
+            </div>
+            {/* 入藏时间范围----------入藏年度 */}
+            <div className="formRow">
+              <div className="bs">*</div>
+              <Form.Item label="入藏时间范围" name="dictCheckInScope">
+                <Select
+                  placeholder="请选择"
+                  options={options9.map((v: any) => ({
+                    label: v.name,
+                    value: v.name,
+                  }))}
+                />
+              </Form.Item>
+              <Form.Item label="入藏年度" name="checkInYear">
+                <InputNumber
+                  min={1000}
+                  maxLength={4}
+                  placeholder="请输入数字"
+                />
+              </Form.Item>
+            </div>
+            {/* 著者----------版本 */}
+            <div className="formRow">
+              <Form.Item label="著者" name="author">
+                <Input maxLength={50} showCount placeholder="请输入" />
+              </Form.Item>
+              <Form.Item label="版本" name="vision">
+                <Input maxLength={50} showCount placeholder="请输入" />
+              </Form.Item>
+            </div>
+            {/* 存卷*/}
+            <div className="formRow formRowFull">
+              <Form.Item label="存卷" name="onFile">
+                <Input maxLength={50} showCount placeholder="请输入" />
+              </Form.Item>
+            </div>
+            {/* 来源说明*/}
+            <div className="formRow formRowFull">
+              <Form.Item label="来源说明" name="description">
+                <TextArea
+                  rows={3}
+                  placeholder="请输入"
+                  showCount
+                  maxLength={255}
+                />
+              </Form.Item>
+            </div>
+            <div className="formRow formRowLast">
+              <Form.Item
+                rules={[{ required: true, message: "不能为空!" }]}
+                label="展示状态"
+                name="display"
+              >
+                <Radio.Group>
+                  <Radio value={1}> 是 </Radio>
+                  <Radio value={0}> 否 </Radio>
+                </Radio.Group>
+              </Form.Item>
+            </div>
+          </div>
+          {/* -----------------------附件信息 */}
+          <div hidden={titSelect === "tit1"}>
+            <div className="fileBox">
+              {/* 封面上传 */}
+              <div className="fileBoxRow">
+                <div className="fileBoxRow_tit">藏品图片:</div>
+                <div className="fileBoxRow_r">
+                  <div
+                    hidden={cover !== ""}
+                    className="fileBoxRow_up"
+                    onClick={() => myInput.current?.click()}
+                  >
+                    <PlusOutlined />
+                    <input
+                      type="file"
+                      accept=".png,.jpg"
+                      ref={myInput}
+                      onChange={(e) => handeUpPhoto(e)}
+                    />
+                  </div>
+                  <div className="fileBoxRow_r_img" hidden={cover === ""}>
+                    {cover ? (
+                      <ImageLazy width={120} height={120} src={cover} />
+                    ) : null}
+
+                    <Popconfirm
+                      title="确定删除吗?"
+                      okText="确定"
+                      cancelText="取消"
+                      onConfirm={() => setCover("")}
+                    >
+                      <div className="clearCover">
+                        <CloseCircleOutlined />
+                      </div>
+                    </Popconfirm>
+                  </div>
+                  <div className="fileBoxRow_r_tit">
+                    支持上传jpg,png格式,大小&lt;10M
+                  </div>
+                </div>
+              </div>
+              {/* 附件上传 */}
+              <div className="fileBoxRow">
+                <div className="fileBoxRow_tit fileBoxRow_tit2">上传附件:</div>
+                <div className="fileBoxRow_r">
+                  <input
+                    type="file"
+                    ref={myInput2}
+                    onChange={(e) => handeUpPhoto2(e)}
+                  />
+                  <Select
+                    style={{ width: 150 }}
+                    value={fileSelect}
+                    onChange={fileSelectChangeFu}
+                    options={fileSelectList}
+                  />
+                  &emsp;
+                  <Button onClick={() => upFileFu()}>上传附件</Button>
+                </div>
+                &emsp;
+                <div className="fileBoxRowUpTit">
+                  {fileSelect === "img" ? (
+                    <>支持上传jpg,png,gif格式</>
+                  ) : fileSelect === "video" ? (
+                    <>支持上传mp4格式</>
+                  ) : fileSelect === "audio" ? (
+                    <>支持上传mp3格式</>
+                  ) : (
+                    ""
+                  )}
+                </div>
+              </div>
+              {/* 表格主体 */}
+              <div className="addTableBox_table">
+                <Table
+                  size="small"
+                  scroll={{ y: 300 }}
+                  dataSource={results[fileSelect]}
+                  columns={columns}
+                  rowKey="id"
+                  pagination={false}
+                />
+              </div>
+            </div>
+          </div>
+
+          <br />
+          <Form.Item wrapperCol={{ offset: 20 }}>
+            <Button onClick={colsePage}>取消</Button>
+            &emsp;
+            <Button type="primary" htmlType="submit">
+              确定
+            </Button>
+          </Form.Item>
+        </Form>
+      </Modal>
+    </div>
+  );
+}
+
+const MemoObjectAdd = React.memo(ObjectAdd);
+
+export default MemoObjectAdd;

+ 10 - 0
src/components/SpinLoding/index.module.scss

@@ -0,0 +1,10 @@
+.SpinLoding {
+  position: relative;
+  z-index: 9999;
+  width: 100%;
+  height: 100%;
+  background-color: #fff;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}

+ 13 - 0
src/components/SpinLoding/index.tsx

@@ -0,0 +1,13 @@
+import styles from "./index.module.scss";
+import { Spin } from "antd";
+import React from "react";
+function SpinLoding() {
+  return (
+    <div className={styles.SpinLoding}>
+      <Spin size='large'/>
+    </div>
+  );
+}
+const MemoSpinLoding = React.memo(SpinLoding);
+
+export default MemoSpinLoding;

+ 36 - 0
src/components/UpAsyncLoding/index.module.scss

@@ -0,0 +1,36 @@
+.UpAsyncLoding {
+  opacity: 0;
+  pointer-events: none;
+  position: fixed;
+  z-index: 9997;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background-color: rgba(0, 0, 0, .4);
+
+  :global {
+    .progressBox {
+      position: absolute;
+      top: 60%;
+      left: 50%;
+      transform: translate(-50%, -50%);
+      width: 500px;
+      height: 6px;
+      border-radius: 3px;
+      border: 1px solid var(--themeColor);
+      overflow: hidden;
+
+      #progress {
+        position: absolute;
+        top: 0;
+        left: 0;
+        width: 0%;
+        height: 100%;
+        background-color: var(--themeColor);
+      }
+
+    }
+
+  }
+}

+ 15 - 0
src/components/UpAsyncLoding/index.tsx

@@ -0,0 +1,15 @@
+import React from "react";
+import styles from "./index.module.scss";
+function UpAsyncLoding() {
+  return (
+    <div id="UpAsyncLoding" className={styles.UpAsyncLoding}>
+      <div className="progressBox">
+        <div id="progress"></div>
+      </div>
+    </div>
+  );
+}
+
+const MemoUpAsyncLoding = React.memo(UpAsyncLoding);
+
+export default MemoUpAsyncLoding;

+ 41 - 0
src/index.tsx

@@ -0,0 +1,41 @@
+// import 'default-passive-events';
+
+import App from "./App";
+import store from "./store/index";
+
+import { Provider } from "react-redux";
+import { createRoot } from "react-dom/client";
+
+import { ConfigProvider } from "antd";
+
+// 兼容360浏览器
+import {
+  StyleProvider,
+  legacyLogicalPropertiesTransformer,
+} from "@ant-design/cssinjs";
+
+import "dayjs/locale/zh-cn";
+import locale from "antd/locale/zh_CN";
+
+const container = document.getElementById("root") as HTMLElement;
+const root = createRoot(container);
+
+root.render(
+  <ConfigProvider
+    locale={locale}
+    theme={{
+      token: {
+        colorPrimary: "#9F1927",
+      },
+    }}
+  >
+    <Provider store={store}>
+      <StyleProvider
+        hashPriority="high"
+        transformers={[legacyLogicalPropertiesTransformer]}
+      >
+        <App />
+      </StyleProvider>
+    </Provider>
+  </ConfigProvider>
+);

+ 208 - 0
src/pages/Home/index.module.scss

@@ -0,0 +1,208 @@
+.Home {
+
+
+  :global {
+    .homeMain {
+      height: 100%;
+
+      .title {
+        width: 100%;
+        height: 180px;
+        background-color: #fff;
+        border-radius: 5px;
+        padding: 10px 40px;
+        display: flex;
+        justify-content: space-between;
+
+        .titleL {
+
+          display: flex;
+          flex-direction: column;
+          justify-content: space-between;
+          padding: 26px 10px 10px 0px;
+          width: 270px;
+          border-right: 1px solid #999999;
+
+          &>h3 {
+            font-weight: 800;
+            font-size: 18px;
+            color: var(--themeColor);
+          }
+        }
+
+        .titleR {
+          width: calc(100% - 300px);
+          display: flex;
+          justify-content: space-around;
+          padding: 20px 0;
+          text-align: center;
+
+          .row {
+            cursor: pointer;
+
+            &>div {
+              width: 80px;
+              height: 80px;
+              background-size: 100% 100%;
+            }
+
+            .bac1 {
+              background-image: url('../../assets/img/login/icon_1.png');
+            }
+
+            .bac2 {
+              background-image: url('../../assets/img/login/icon_2.png');
+            }
+
+            .bac3 {
+              background-image: url('../../assets/img/login/icon_3.png');
+            }
+
+            .bac4 {
+              background-image: url('../../assets/img/login/icon_4.png');
+            }
+
+            .bac5 {
+              background-image: url('../../assets/img/login/icon_5.png');
+            }
+
+            &>p {
+              margin-top: 15px;
+            }
+
+            &:hover {
+              color: var(--themeColor);
+            }
+          }
+
+          .noAuth {
+            opacity: .5;
+            cursor: no-drop;
+            -webkit-filter: grayscale(100%);
+            -moz-filter: grayscale(100%);
+            -ms-filter: grayscale(100%);
+            -o-filter: grayscale(100%);
+            filter: grayscale(100%);
+            filter: gray;
+          }
+        }
+      }
+
+      .flooBox {
+        margin-top: 35px;
+        width: 100%;
+        height: calc(100% - 220px);
+        border-radius: 5px;
+        display: flex;
+        justify-content: space-between;
+
+        &>div {
+          background-color: #fff;
+          border-radius: 5px;
+          padding: 0px 30px;
+
+          .flooTit {
+            position: relative;
+            padding-left: 50px;
+            width: 100%;
+            height: 80px;
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+            color: var(--themeColor);
+            font-size: 22px;
+            font-weight: 700;
+            background: url('../../assets/img/login/Statistics.png') no-repeat left center;
+            background-size: 36px 40px;
+
+            &::before {
+              content: '';
+              position: absolute;
+              bottom: 0;
+              left: 0;
+              width: 100%;
+              height: 2px;
+              background-image: linear-gradient(to right, rgba(159, 25, 39, 1), rgba(159, 25, 39, .3));
+            }
+          }
+        }
+
+        .flooBoxL {
+          width: 960px;
+
+          .chartBox {
+            width: 100%;
+            height: calc(100% - 80px);
+            position: relative;
+
+            .chart {
+              width: 100%;
+              height: 100%;
+            }
+
+            .chartTit {
+              position: absolute;
+              z-index: 10;
+              top: 15px;
+              left: 17px;
+            }
+          }
+        }
+
+        .flooBoxR {
+          width: 600px;
+
+          .flooTit {
+            background: url('../../assets/img/login/remind.png') no-repeat left center;
+            background-size: 36px 40px;
+          }
+
+          .doneBox {
+            padding: 20px 35px 0;
+            width: 100%;
+            height: calc(100% - 80px);
+            display: flex;
+            flex-wrap: wrap;
+            margin-bottom: 3%;
+
+            .doneRow {
+              cursor: pointer;
+              padding: 15px;
+              width: 50%;
+              height: 30%;
+              border-bottom: 1px solid #999999;
+
+              .doneRow_tit {
+                margin-bottom: 30px;
+                font-weight: 700;
+              }
+
+              .doneRow_txt {
+                &>span {
+                  font-weight: 700;
+                  font-size: 30px;
+                  color: var(--themeColor);
+                }
+              }
+            }
+
+            .noneRow {
+              border: none;
+            }
+          }
+
+          .noAuth {
+            opacity: .5;
+            cursor: no-drop !important;
+            -webkit-filter: grayscale(100%);
+            -moz-filter: grayscale(100%);
+            -ms-filter: grayscale(100%);
+            -o-filter: grayscale(100%);
+            filter: grayscale(100%);
+            filter: gray;
+          }
+        }
+      }
+    }
+  }
+}

+ 291 - 0
src/pages/Home/index.tsx

@@ -0,0 +1,291 @@
+import { getTokenInfo } from "@/utils/storage";
+import { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import styles from "./index.module.scss";
+import dayjs from "dayjs";
+import { Button } from "antd";
+import classNames from "classnames";
+import * as echarts from "echarts/core";
+import { TooltipComponent, GridComponent } from "echarts/components";
+import { BarChart } from "echarts/charts";
+import { CanvasRenderer } from "echarts/renderers";
+import history from "@/utils/history";
+import { getStores2API1 } from "@/store/action/stores1";
+import { getHomeNumsAPI } from "@/store/action/login";
+import { useSelector } from "react-redux";
+import { RootState } from "@/store";
+import { MessageFu } from "@/utils/message";
+
+echarts.use([TooltipComponent, GridComponent, BarChart, CanvasRenderer]);
+
+export default function Home() {
+  // 顶部右侧数据
+  const tabListTemp = useMemo(() => {
+    return [
+      { id: 1, done: false, path: "/object", name: "藏品登记" },
+      { id: 2, done: false, path: "/object/2", name: "藏品总账" },
+      { id: 3, done: false, path: "/object/3", name: "入库管理" },
+      { id: 4, done: false, path: "/object/4", name: "出库管理" },
+      { id: 5, done: false, path: "/object/6", name: "藏品注销" },
+    ];
+  }, []);
+  // 右下方的数据
+  const tempDone = useMemo(() => {
+    return [
+      { id: 1, done: false, path: "/object", num: 0, name: "藏品登记" },
+      { id: 2, done: false, path: "/object/3", num: 0, name: "入库管理" },
+      { id: 3, done: false, path: "/object/4", num: 0, name: "出库管理" },
+      { id: 4, done: false, path: "/object/5", num: 0, name: "藏品修改" },
+      { id: 5, done: false, path: "/object/6", num: 0, name: "藏品注销" },
+      { id: 6, done: false, path: "/stores/3", num: 0, name: "藏品移库" },
+    ];
+  }, []);
+
+  const [tabList, setTabList] = useState(tabListTemp);
+  const powerInfo = useSelector(
+    (state: RootState) => state.loginStore.authPageArr
+  );
+
+  useEffect(() => {
+    powerInfo.forEach((v: any) => {
+      if (v.id === 100) tabListTemp[0].done = tempDone[0].done = true;
+      if (v.id === 200) tabListTemp[1].done = true;
+      if (v.id === 300) tabListTemp[2].done = tempDone[1].done = true;
+      if (v.id === 400) tabListTemp[3].done = tempDone[2].done = true;
+      if (v.id === 500) tempDone[3].done = true;
+      if (v.id === 600) tabListTemp[4].done = tempDone[4].done = true;
+      if (v.id === 800) tempDone[5].done = true;
+    });
+
+    window.setTimeout(() => {
+      setTabList(tabListTemp);
+    }, 100);
+  }, [powerInfo, tabListTemp, tempDone]);
+
+  // 实时时间
+  const [nowTime, setNowTime] = useState(
+    dayjs(Date.now()).format("YYYY年MM月DD HH:mm")
+  );
+
+  // 点击头部右侧和下面右侧
+  const toPageFu = useCallback((path: string, flag: boolean) => {
+    if (flag) return MessageFu.warning("没有该模块权限!");
+    history.push(path);
+  }, []);
+
+  const timeRef = useRef(-1);
+  useEffect(() => {
+    timeRef.current = window.setInterval(() => {
+      setNowTime(() => {
+        return dayjs(Date.now()).format("YYYY年MM月DD HH:mm");
+      });
+    }, 1000);
+    return () => {
+      clearInterval(timeRef.current);
+    };
+  }, []);
+
+  const userInfo = useMemo(() => {
+    return getTokenInfo().user;
+  }, []);
+
+  // -----------图表
+  const echartsFu = useCallback(async () => {
+    const res = await getStores2API1();
+    let list = res.data;
+
+    if (list && list.length && list.length > 7) {
+      list = list.slice(0, 7);
+    } else {
+      const num = 7 - list.length;
+      for (let i = 0; i < num; i++) {
+        if (i % 2 !== 0) list.unshift({ groupKey: "", count: null });
+        else list.push({ groupKey: "", count: null });
+      }
+    }
+    const chartDom: any = document.querySelector(".chart");
+    const myChart = echarts.init(chartDom);
+    const option = {
+      color: ["#9F1927"],
+      tooltip: {
+        trigger: "axis",
+        axisPointer: {
+          type: "shadow",
+        },
+      },
+      grid: {
+        left: "3%",
+        right: "4%",
+        bottom: "3%",
+        containLabel: true,
+      },
+      xAxis: [
+        {
+          type: "category",
+          data: list.map((v: any) => v.groupKey),
+          axisTick: {
+            show: false,
+            alignWithLabel: false,
+          },
+          axisLabel: {
+            interval: 0, //强制文字产生间隔
+            textStyle: {
+              color: "#000", //坐标值得具体的颜色
+              fontSize: 12,
+            },
+          },
+          axisLine: {
+            //Y轴坐标轴
+            show: true,
+            // 坐标的颜色和宽度
+            lineStyle: {
+              width: 2,
+              color: "#9F1927",
+            },
+          },
+        },
+      ],
+      yAxis: [
+        {
+          type: "value",
+          axisLabel: {
+            textStyle: {
+              color: "#000", //坐标值得具体的颜色
+            },
+          },
+          axisLine: {
+            //Y轴坐标轴
+            show: true,
+            lineStyle: {
+              width: 2,
+              color: "#9F1927",
+            },
+          },
+          // 隐藏背景坐标线段
+          splitLine: {
+            show: false,
+          },
+        },
+      ],
+      series: [
+        {
+          name: "",
+          type: "bar",
+          barMaxwidth: "50%",
+          data: list.map((v: any) => v.count),
+          itemStyle: {
+            normal: {
+              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                { offset: 0, color: "rgba(159, 25, 39, .5)" }, //渐变头部色
+                { offset: 1, color: "rgba(159, 25, 39, 1)" },
+              ]),
+
+              barBorderRadius: 2,
+              label: {
+                show: true, //开启显示
+                position: "top", //在上方显示
+                textStyle: {
+                  //数值样式
+                  color: "#9F1927",
+                  fontSize: 16,
+                },
+              },
+            },
+          },
+        },
+      ],
+    };
+    option && myChart.setOption(option);
+  }, []);
+
+  // 代办提醒
+  const [doneList, setDoneList] = useState(tempDone);
+
+  const doneNumsAPIFu = useCallback(async () => {
+    const res = await getHomeNumsAPI();
+    const data = [...tempDone];
+    res.data.forEach((v: any) => {
+      if (v.groupKey === "register") data[0].num = v.count;
+      else if (v.groupKey === "in") data[1].num = v.count;
+      else if (v.groupKey === "out") data[2].num = v.count;
+      else if (v.groupKey === "edit") data[3].num = v.count;
+      else if (v.groupKey === "cancel") data[4].num = v.count;
+      else if (v.groupKey === "move") data[5].num = v.count;
+    });
+    setDoneList(data);
+  }, [tempDone]);
+
+  useEffect(() => {
+    echartsFu();
+    doneNumsAPIFu();
+  }, [doneNumsAPIFu, echartsFu]);
+
+  return (
+    <div className={styles.Home}>
+      <div className="homeMain">
+        {/* 顶部页面 */}
+        <div className="title">
+          <div className="titleL">
+            <h3>
+              欢迎 {userInfo.realName} 进入
+              <br />
+              <br />
+              华东野战军博物馆馆藏系统!
+            </h3>
+            <p>{nowTime}</p>
+          </div>
+          <div className="titleR">
+            {tabList.map((v, i) => (
+              <div
+                onClick={() => toPageFu(v.path, !v.done)}
+                className={classNames("row", !v.done ? "noAuth" : "")}
+                key={v.id}
+              >
+                <div className={`bac${v.id}`}></div>
+                <p>{v.name}</p>
+              </div>
+            ))}
+          </div>
+        </div>
+        {/* 下面数据 */}
+        <div className="flooBox">
+          <div className="flooBoxL">
+            <div className="flooTit">
+              <div>藏馆统计</div>
+              <Button onClick={() => history.push("/stores/2")}>
+                查看更多
+              </Button>
+            </div>
+            {/* 图表 */}
+            <div className="chartBox">
+              <div className="chartTit">(件)</div>
+              <div className="chart"></div>
+            </div>
+          </div>
+          <div className="flooBoxR">
+            <div className="flooTit">
+              <div>审核提醒</div>
+            </div>
+            <div className="doneBox">
+              {doneList.map((v, i) => (
+                <div
+                  onClick={() => toPageFu(v.path, !v.done)}
+                  className={classNames(
+                    "doneRow",
+                    i >= 4 ? "noneRow" : "",
+                    !v.done ? "noAuth" : ""
+                  )}
+                  key={v.id}
+                >
+                  <div className="doneRow_tit">{v.name}</div>
+                  <div className="doneRow_txt">
+                    共有&emsp;<span>{v.num}</span>&emsp;待审核事项
+                  </div>
+                </div>
+              ))}
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+}

+ 221 - 0
src/pages/Layout/index.module.scss

@@ -0,0 +1,221 @@
+.Layout {
+  width: 100%;
+  height: 100%;
+
+  :global {
+    .topTitle {
+      width: 100%;
+      height: 90px;
+      background-color: var(--themeColor);
+
+      .main {
+        width: 1580px;
+        height: 100%;
+        margin: 0 auto;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+
+        &>div {
+          height: 100%;
+        }
+
+        .logo {
+          height: 35px;
+          width: 256px;
+          background-image: url('../../assets/img/login/LOGO.png');
+          background-size: 100% 100%;
+        }
+
+        .tabCut {
+          width: calc(100% - 800px);
+          display: flex;
+          align-items: center;
+          justify-content: space-around;
+
+          .row {
+            line-height: 90px;
+            cursor: pointer;
+            font-size: 18px;
+            color: #fff;
+            height: 100%;
+
+            &:hover {
+              color: #D3B453;
+            }
+          }
+
+          .active {
+            pointer-events: none;
+            color: #D3B453;
+            position: relative;
+
+            &::before {
+              content: '';
+              position: absolute;
+              bottom: 0;
+              left: 50%;
+              transform: translateX(-50%);
+              width: 56px;
+              height: 24px;
+              background-image: url('../../assets/img/login/flower.png');
+              background-size: 56px 24px;
+            }
+          }
+        }
+
+        .user {
+          padding-right: 10px;
+          cursor: pointer;
+          position: relative;
+          line-height: 90px;
+          padding-left: 60px;
+          background: url('../../assets/img/login/user_1.png') no-repeat left center;
+          background-size: 40px 40px;
+          font-size: 16px;
+          color: #fff;
+
+          .userSet {
+            width: 115px;
+            opacity: 0;
+            pointer-events: none;
+            transition: bottom .3s;
+            height: 100px;
+            background-color: var(--themeColor);
+            position: absolute;
+            left: 0;
+            bottom: -80px;
+
+            &>span {
+              display: block;
+              width: 100%;
+              height: 50%;
+              line-height: 50px;
+              text-align: center;
+
+              &:hover {
+                color: #D3B453;
+              }
+            }
+          }
+
+          &:hover {
+            .userSet {
+              opacity: 1;
+              pointer-events: auto;
+              bottom: -100px;
+            }
+          }
+        }
+      }
+    }
+
+    .pageMain {
+      width: 100%;
+      height: calc(100% - 90px);
+      background-image: url('../../assets/img/login/homeBg.jpg');
+      background-size: cover;
+
+      &>div {
+        width: 1600px;
+        margin: 0 auto;
+        padding-top: 35px;
+        height: calc(100% - 30px);
+
+        .leftBar {
+          float: left;
+          width: 220px;
+          height: 100%;
+          background-color: #fff;
+          border-radius: 5px;
+        }
+
+        .rightMain {
+          width: calc(100% - 250px);
+          height: 100%;
+          float: right;
+
+          &>div {
+            width: 100%;
+            height: 100%;
+          }
+
+          .breadTit {
+            padding-left: 35px;
+            background-color: #fff;
+            border-radius: 5px;
+            width: 100%;
+            height: 50px;
+            margin-bottom: 20px;
+          }
+
+          .objectSonMain {
+            background-color: #fff;
+            border-radius: 5px;
+            width: 100%;
+            height: calc(100% - 70px);
+
+            .objectSonMainTit {
+              width: calc(100% - 30px);
+              margin: 0 auto 20px;
+              height: 60px;
+              border-bottom: 2px solid #999999;
+              padding: 0 5px;
+              display: flex;
+
+              &>div {
+                cursor: pointer;
+                height: 100%;
+                line-height: 58px;
+                padding: 0 15px;
+                font-size: 16px;
+                margin-right: 50px;
+                &:hover{
+                  color: var(--themeColor);
+                }
+              }
+
+              .active {
+                font-weight: 700;
+                color: var(--themeColor);
+                pointer-events: none;
+                position: relative;
+
+                &::before {
+                  content: '';
+                  z-index: 10;
+                  position: absolute;
+                  bottom: -3px;
+                  left: 0;
+                  width: 100%;
+                  height: 4px;
+                  background-color: var(--themeColor);
+                }
+              }
+            }
+
+            .objectSonMainTable {
+              width: calc(100% - 60px);
+              margin: 0 auto;
+              height: calc(100% - 80px);
+              .tableSelectBox{
+                margin-bottom: 20px;
+                width: 100%;
+                height: 40px;
+                display: flex;
+                align-items: center;
+                .row{
+                  margin-right: 30px;
+                }
+              }
+              .tableMain{
+                width: 100%;
+                height: calc(100% - 60px);
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}

+ 272 - 0
src/pages/Layout/index.tsx

@@ -0,0 +1,272 @@
+import { getTokenInfo, removeTokenInfo } from "@/utils/storage";
+import styles from "./index.module.scss";
+import classNames from "classnames";
+import { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { Popconfirm, Button, Modal, Form, Input } from "antd";
+import React from "react";
+import { Route, Switch, useLocation } from "react-router-dom";
+import history from "@/utils/history";
+import SpinLoding from "@/components/SpinLoding";
+import AuthRoute from "@/components/AuthRoute";
+import { Base64 } from "js-base64";
+import encodeStr from "@/utils/pass";
+import {
+  getPowerInfoAPI,
+  getSelectAllAPI,
+  passWordEditAPI,
+} from "@/store/action/login";
+import { useDispatch, useSelector } from "react-redux";
+import { RootState } from "@/store";
+import { MessageFu } from "@/utils/message";
+
+const NotFound = React.lazy(() => import("../../components/NotFound"));
+
+function Layout() {
+  const dispatch = useDispatch();
+
+  const tabListTemp = useMemo(() => {
+    return [
+      {
+        id: 1,
+        done: true,
+        Com: React.lazy(() => import("../Home")),
+        path: "/",
+        name: "首页",
+      },
+      {
+        id: 2,
+        done: false,
+        Com: React.lazy(() => import("../Object")),
+        path: "/object",
+        name: "馆藏管理",
+      },
+      {
+        id: 3,
+        done: true,
+        Com: React.lazy(() => import("../Stores")),
+        path: "/stores",
+        name: "库房管理",
+      },
+    ];
+  }, []);
+
+  const [tabList, setTabList] = useState(tabListTemp);
+
+  // 进页面获取权限信息
+  const getPowerInfoAPIFu = useCallback(async () => {
+    const res = await getPowerInfoAPI();
+    const tempArr = res.data.filter((v: any) => v.authority);
+    // 页面权限信息存到仓库
+    dispatch({ type: "login/setAuthPageArr", payload: tempArr });
+    // 按钮的权限信息存到仓库
+    const buttonArr = [] as any;
+    tempArr.forEach((v: any) => {
+      v.children.forEach((v2: any) => {
+        if (v2.authority) buttonArr.push(v2);
+      });
+    });
+    dispatch({ type: "login/setAuthButtonArr", payload: buttonArr });
+  }, [dispatch]);
+
+  // 获取页面权限信息
+  const powerInfo = useSelector(
+    (state: RootState) => state.loginStore.authPageArr
+  );
+
+  useEffect(() => {
+    getPowerInfoAPIFu();
+  }, [getPowerInfoAPIFu]);
+
+  useEffect(() => {
+    const temp = getTokenInfo().user.isAdmin;
+    if (temp === 1 && tabListTemp.length <= 3)
+      tabListTemp.push({
+        id: 4,
+        done: true,
+        Com: React.lazy(() => import("../System")),
+        path: "/system",
+        name: "系统管理",
+      });
+
+    powerInfo.forEach((v: any) => {
+      if (
+        v.id === 100 ||
+        v.id === 200 ||
+        v.id === 300 ||
+        v.id === 400 ||
+        v.id === 500 ||
+        v.id === 600
+      ) {
+        tabListTemp[1].done = true;
+      }
+    });
+
+    setTabList(tabListTemp.filter((v) => v.done));
+  }, [powerInfo, tabListTemp]);
+
+  const location = useLocation();
+  const [path, setPath] = useState("");
+
+  useEffect(() => {
+    const arr = location.pathname.split("/");
+    let pathTemp = "/";
+    if (arr[1]) pathTemp = "/" + arr[1];
+
+    setPath(pathTemp);
+  }, [location]);
+
+  const userInfo = useMemo(() => {
+    return getTokenInfo().user;
+  }, []);
+
+  // 顶部路由跳转
+  const pathCutFu = (path: string) => {
+    history.push(path);
+  };
+
+  // 修改密码相关
+  const [open, setOpen] = useState(false);
+
+  // 拿到新密码的输入框的值
+  const oldPasswordValue = useRef("");
+
+  const checkPassWord = (rule: any, value: any = "") => {
+    if (value !== oldPasswordValue.current)
+      return Promise.reject("新密码不一致!");
+    else return Promise.resolve(value);
+  };
+
+  const onFinish = async (values: any) => {
+    // 通过校验之后发送请求
+    if (values.oldPassword === values.newPassword)
+      return MessageFu.warning("新旧密码不能相同!");
+    const obj = {
+      oldPassword: encodeStr(Base64.encode(values.oldPassword)),
+      newPassword: encodeStr(Base64.encode(values.newPassword)),
+    };
+    const res: any = await passWordEditAPI(obj);
+    if (res.code === 0) {
+      MessageFu.success("修改成功!");
+      loginExit();
+    }
+  };
+
+  // 点击退出登录
+  const loginExit = () => {
+    removeTokenInfo();
+    history.push("/login");
+  };
+
+  // 进来获取所有的下拉列表信息
+  useEffect(() => {
+    dispatch(getSelectAllAPI());
+  }, [dispatch]);
+
+  return (
+    <div className={styles.Layout}>
+      <div className="topTitle">
+        <div className="main">
+          <div className="logo"></div>
+          <div className="tabCut">
+            {tabList.map((v: any) => (
+              <div
+                onClick={() => pathCutFu(v.path)}
+                className={classNames("row", v.path === path ? "active" : "")}
+                key={v.id}
+              >
+                {v.name}
+              </div>
+            ))}
+          </div>
+          <div className="user">
+            {userInfo.realName}
+            <div className="userSet">
+              <span onClick={() => setOpen(true)}>修改密码</span>
+              <Popconfirm
+                title="确定退出吗?"
+                okText="确定"
+                cancelText="取消"
+                onConfirm={loginExit}
+              >
+                退出登录
+              </Popconfirm>
+            </div>
+          </div>
+        </div>
+      </div>
+      {/* 二级路由页面 */}
+      <div className="pageMain">
+        <React.Suspense fallback={<SpinLoding />}>
+          <Switch>
+            {tabList.map((v: any, i: number) => (
+              <AuthRoute
+                key={v.id}
+                exact={i === 0 ? true : false}
+                path={v.path}
+                component={v.Com}
+              />
+            ))}
+            <Route path="*" component={NotFound} />
+          </Switch>
+        </React.Suspense>
+      </div>
+      {/* 点击修改密码打开的对话框 */}
+      <Modal
+        destroyOnClose
+        open={open}
+        title="修改密码"
+        onCancel={() => setOpen(false)}
+        footer={
+          [] // 设置footer为空,去掉 取消 确定默认按钮
+        }
+      >
+        <Form
+          name="basic"
+          labelCol={{ span: 5 }}
+          wrapperCol={{ span: 16 }}
+          onFinish={onFinish}
+          autoComplete="off"
+        >
+          <Form.Item
+            label="旧密码"
+            name="oldPassword"
+            rules={[{ required: true, message: "不能为空!" }]}
+          >
+            <Input.Password maxLength={15} />
+          </Form.Item>
+
+          <Form.Item
+            label="新密码"
+            name="newPassword"
+            rules={[{ required: true, message: "不能为空!" }]}
+          >
+            <Input.Password
+              maxLength={15}
+              onChange={(e) => (oldPasswordValue.current = e.target.value)}
+            />
+          </Form.Item>
+
+          <Form.Item
+            label="确定新密码"
+            name="checkPass"
+            rules={[{ validator: checkPassWord }]}
+          >
+            <Input.Password maxLength={15} />
+          </Form.Item>
+
+          <Form.Item wrapperCol={{ offset: 14, span: 16 }}>
+            <Button onClick={() => setOpen(false)}>取消</Button>
+            &emsp;
+            <Button type="primary" htmlType="submit">
+              确定
+            </Button>
+          </Form.Item>
+        </Form>
+      </Modal>
+    </div>
+  );
+}
+
+const MemoLayout = React.memo(Layout);
+
+export default MemoLayout;

+ 91 - 0
src/pages/Login/index.module.scss

@@ -0,0 +1,91 @@
+.Login {
+  width: 100%;
+  height: 100%;
+  background-image: url('../../assets/img/login/bg.jpg');
+  background-size: cover;
+  position: relative;
+
+  :global {
+    .main {
+      padding-top: 20px;
+      position: absolute;
+      top: 50%;
+      transform: translateY(-50%);
+      width: 100%;
+      height: 380px;
+      background-color: rgba(255, 255, 255, .8);
+      backdrop-filter: blur(4px);
+      text-align: center;
+
+      &>h1 {
+        font-size: 44px;
+        color: var(--themeColor);
+      }
+
+      &>p {
+        margin: 10px 0 40px;
+        font-size: 18px;
+      }
+
+      .inputBox {
+        width: 1000px;
+        margin: 0 auto;
+        display: flex;
+        justify-content: space-between;
+
+        .ant-input-prefix {
+          margin-right: 10px;
+
+          .anticon {
+            width: 24px;
+            height: 24px;
+
+            svg {
+              width: 100%;
+              height: 100%;
+            }
+          }
+        }
+
+        .ant-input {
+          font-size: 18px;
+          width: 45%;
+          height: 60px;
+          line-height: 60px;
+          background-clip: content-box;
+        }
+
+        input:-webkit-autofill {
+          // color: #666 !important;
+          // -webkit-text-fill-color: #666 !important; //文字颜色
+          background-image: none;
+          -webkit-box-shadow: 0 0 0px 1000px transparent inset !important; //填充阴影,可以用来遮住背景色
+          background-color: transparent;
+          transition: background-color 50000s ease-in-out 0s; //背景色透明  生效时长  过渡效果  启用时延迟的时间
+
+        }
+
+        .ant-input-affix-wrapper {
+          padding: 0 11px;
+          width: 45%;
+          height: 60px;
+
+          .ant-input {
+            background-color: transparent;
+            width: 100%;
+            height: 60px;
+          }
+        }
+      }
+
+      .loginBtn {
+        margin-top: 50px;
+
+        .ant-btn {
+          width: 400px;
+          height: 60px;
+        }
+      }
+    }
+  }
+}

+ 82 - 0
src/pages/Login/index.tsx

@@ -0,0 +1,82 @@
+import styles from "./index.module.scss";
+
+import { Input, Button } from "antd";
+import { UserOutlined, LockOutlined } from "@ant-design/icons";
+import { useEffect, useState } from "react";
+import { Base64 } from "js-base64";
+import encodeStr from "@/utils/pass";
+import { userLoginAPI } from "@/store/action/login";
+import { setTokenInfo } from "@/utils/storage";
+import history from "@/utils/history";
+import { useDispatch } from "react-redux";
+import { MessageFu } from "@/utils/message";
+
+export default function Login() {
+  const dispatch = useDispatch();
+
+  // 进登录页面把权限的信息初始化,防止登录成功之后进到首页,数据渲染问题
+  useEffect(() => {
+    dispatch({ type: "login/setAuthPageArr", payload: [] });
+  }, [dispatch]);
+
+  // 账号密码
+  const [userName, setUserName] = useState("");
+  const [passWord, setPassWord] = useState("");
+
+  // 键盘按下回车事件
+  const keyUpEntFu = (e: React.KeyboardEvent<HTMLInputElement>) => {
+    if (e.key === "Enter") loginClickFu();
+  };
+  // 点击登录
+  const loginClickFu = async () => {
+    // 非空判断
+    if (userName === "") return MessageFu.warning("请填写用户账号!");
+    else if (passWord === "") return MessageFu.warning("请填写登录密码!");
+    const obj = {
+      userName,
+      passWord: encodeStr(Base64.encode(passWord)),
+    };
+    const res: any = await userLoginAPI(obj);
+    if (res.code === 0) {
+      MessageFu.success("登录成功");
+      // 用户信息存到本地
+      setTokenInfo(res.data);
+      history.push("/");
+    }
+  };
+
+  return (
+    <div className={styles.Login}>
+      <div className="main">
+        <h1>华东野战军博物馆馆藏系统</h1>
+        <p>LESHAN DAFO MUSEUMCOLLECTION MANAGEMENT SYSTEM</p>
+        {/* 账号密码输入框 */}
+        <div className="inputBox">
+          <Input
+            onKeyUp={(e) => keyUpEntFu(e)}
+            value={userName}
+            onChange={(e) => setUserName(e.target.value.trim())}
+            prefix={<UserOutlined />}
+            placeholder="请输入用户名称"
+            maxLength={15}
+          />
+          <Input.Password
+            onKeyUp={(e) => keyUpEntFu(e)}
+            value={passWord}
+            onChange={(e) => setPassWord(e.target.value.trim())}
+            prefix={<LockOutlined />}
+            placeholder="请输入登录密码"
+            maxLength={15}
+          />
+        </div>
+
+        {/* 登录按钮 */}
+        <div className="loginBtn">
+          <Button type="primary" size="large" onClick={loginClickFu}>
+            登 录
+          </Button>
+        </div>
+      </div>
+    </div>
+  );
+}

+ 5 - 0
src/pages/Object/index.module.scss

@@ -0,0 +1,5 @@
+.Object{
+  :global{
+    
+  }
+}

+ 249 - 0
src/pages/Object/index.tsx

@@ -0,0 +1,249 @@
+import styles from "./index.module.scss";
+import LeftBar from "@/components/LeftBar";
+import React, { useEffect, useMemo, useState } from "react";
+import SpinLoding from "@/components/SpinLoding";
+import { Route, Switch } from "react-router-dom";
+import AuthRoute from "@/components/AuthRoute";
+import NotFound from "@/components/NotFound";
+import history from "@/utils/history";
+import { useDispatch, useSelector } from "react-redux";
+import { RootState } from "@/store";
+import { getStores1ListAPI } from "@/store/action/stores1";
+
+const LookObject1 = React.lazy(
+  () => import("../ObjectSon/Object1/LookObject1")
+);
+const LookObject2 = React.lazy(
+  () => import("../ObjectSon/Object2/LookObject2")
+);
+const LookObject3 = React.lazy(
+  () => import("../ObjectSon/Object3/LookObject3")
+);
+
+const LookObject4 = React.lazy(
+  () => import("../ObjectSon/Object4/LookObject4")
+);
+const LookObject5 = React.lazy(
+  () => import("../ObjectSon/Object5/LookObject5")
+);
+const LookObject6 = React.lazy(
+  () => import("../ObjectSon/Object6/LookObject6")
+);
+
+export default function Object() {
+  const dispatch = useDispatch();
+  // 进入馆藏管理的时候调用仓库数据
+  useEffect(() => {
+    dispatch(getStores1ListAPI());
+  }, [dispatch]);
+
+  const powerInfo = useSelector(
+    (state: RootState) => state.loginStore.authPageArr
+  );
+
+  const dataTemp = useMemo(() => {
+    return [
+      {
+        id: 1,
+        name: "藏品登记",
+        done: false,
+        Com: React.lazy(() => import("../ObjectSon/Object1")),
+        path: "/object",
+      },
+      {
+        id: 2,
+        done: false,
+        name: "藏品总账",
+        Com: React.lazy(() => import("../ObjectSon/Object2")),
+        path: "/object/2",
+      },
+      {
+        id: 3,
+        done: false,
+        name: "入库管理",
+        Com: React.lazy(() => import("../ObjectSon/Object3")),
+        path: "/object/3",
+      },
+      {
+        id: 4,
+        done: false,
+        name: "出库管理",
+        Com: React.lazy(() => import("../ObjectSon/Object4")),
+        path: "/object/4",
+      },
+      {
+        id: 5,
+        done: false,
+        name: "藏品修改",
+        Com: React.lazy(() => import("../ObjectSon/Object5")),
+        path: "/object/5",
+      },
+      {
+        id: 6,
+        done: false,
+        name: "藏品注销",
+        Com: React.lazy(() => import("../ObjectSon/Object6")),
+        path: "/object/6",
+      },
+    ];
+  }, []);
+
+  const dataInTemp = useMemo(() => {
+    return [
+      {
+        id: 1001,
+        done: false,
+        name: "藏品登记新增",
+        Com: React.lazy(() => import("../ObjectSon/Object1/AddObject1")),
+        path: "/object/1/add",
+      },
+      {
+        id: 1002,
+        done: false,
+        name: "藏品登记审核",
+        Com: React.lazy(() => import("../ObjectSon/Object1/AuditObject1")),
+        path: "/object/1/audit",
+      },
+      {
+        id: 3001,
+        done: false,
+        name: "入库管理新增",
+        Com: React.lazy(() => import("../ObjectSon/Object3/AddObject3")),
+        path: "/object/3/add",
+      },
+      {
+        id: 3002,
+        done: false,
+        name: "入库管理审核",
+        Com: React.lazy(() => import("../ObjectSon/Object3/AuditObject3")),
+        path: "/object/3/audit",
+      },
+      {
+        id: 4001,
+        done: false,
+        name: "出库管理新增",
+        Com: React.lazy(() => import("../ObjectSon/Object4/AddObject4")),
+        path: "/object/4/add",
+      },
+      {
+        id: 4002,
+        done: false,
+        name: "出库管理审核",
+        Com: React.lazy(() => import("../ObjectSon/Object4/AuditObject4")),
+        path: "/object/4/audit",
+      },
+      {
+        id: 5001,
+        done: false,
+        name: "出库管理审核",
+        Com: React.lazy(() => import("../ObjectSon/Object5/AuditObject5")),
+        path: "/object/5/audit",
+      },
+      {
+        id: 6001,
+        done: false,
+        name: "藏品注销新增",
+        Com: React.lazy(() => import("../ObjectSon/Object6/AddObject6")),
+        path: "/object/6/add",
+      },
+      {
+        id: 6002,
+        done: false,
+        name: "藏品注销审核",
+        Com: React.lazy(() => import("../ObjectSon/Object6/AuditObject6")),
+        path: "/object/6/audit",
+      },
+    ];
+  }, []);
+
+  const [data, setData] = useState(dataTemp);
+  const [dataIn, setDadaIn] = useState(dataInTemp);
+
+  useEffect(() => {
+    powerInfo.forEach((v: any) => {
+      if (v.id === 100) {
+        dataTemp[0].done = true;
+        v.children.forEach((v2: any) => {
+          if (v2.id === 102 && v2.authority) dataInTemp[0].done = true;
+          if (v2.id === 105 && v2.authority) dataInTemp[1].done = true;
+        });
+      }
+      if (v.id === 200) dataTemp[1].done = true;
+      if (v.id === 300) {
+        dataTemp[2].done = true;
+        v.children.forEach((v2: any) => {
+          if (v2.id === 302 && v2.authority) dataInTemp[2].done = true;
+          if (v2.id === 305 && v2.authority) dataInTemp[3].done = true;
+        });
+      }
+      if (v.id === 400) {
+        dataTemp[3].done = true;
+        v.children.forEach((v2: any) => {
+          if (v2.id === 402 && v2.authority) dataInTemp[4].done = true;
+          if (v2.id === 405 && v2.authority) dataInTemp[5].done = true;
+        });
+      }
+      if (v.id === 500) {
+        v.children.forEach((v2: any) => {
+          if (v2.id === 505 && v2.authority) dataInTemp[6].done = true;
+        });
+        dataTemp[4].done = true;
+      }
+      if (v.id === 600) {
+        dataTemp[5].done = true;
+        v.children.forEach((v2: any) => {
+          if (v2.id === 602 && v2.authority) dataInTemp[7].done = true;
+          if (v2.id === 605 && v2.authority) dataInTemp[8].done = true;
+        });
+      }
+    });
+    setData(dataTemp.filter((v) => v.done));
+    setDadaIn(dataInTemp.filter((v) => v.done));
+  }, [dataInTemp, dataTemp, powerInfo]);
+
+  useEffect(() => {
+    // 没有藏品登记页面的权限 跳到有权限的页面
+    if (data[0].id !== 1) {
+      // 并且不是从藏品总账里面点击操作记录的查看进来
+      if (!window.location.href.includes("look")) history.replace(data[0].path);
+    }
+  }, [data]);
+
+  return (
+    <div className={styles.Object}>
+      <div className="leftBar">
+        <LeftBar data={data} />
+      </div>
+      {/* 三级路由页面 */}
+      <div className="rightMain">
+        <React.Suspense fallback={<SpinLoding />}>
+          <Switch>
+            {data.map((v: any, i: number) => (
+              <AuthRoute
+                exact
+                // exact={i === 0 ? true : false}
+                key={v.id}
+                path={v.path}
+                component={v.Com}
+              />
+            ))}
+            {/* 查看页面,无需权限 */}
+            <AuthRoute path="/object/1/look" component={LookObject1} />
+            <AuthRoute path="/object/2/look" component={LookObject2} />
+            <AuthRoute path="/object/3/look" component={LookObject3} />
+            <AuthRoute path="/object/4/look" component={LookObject4} />
+            <AuthRoute path="/object/5/look" component={LookObject5} />
+            <AuthRoute path="/object/6/look" component={LookObject6} />
+
+            {/* 新增 */}
+            {dataIn.map((v: any) => (
+              <AuthRoute key={v.id} path={v.path} component={v.Com} />
+            ))}
+
+            <Route path="*" component={NotFound} />
+          </Switch>
+        </React.Suspense>
+      </div>
+    </div>
+  );
+}

+ 87 - 0
src/pages/ObjectSon/Object1/AddObject1/index.module.scss

@@ -0,0 +1,87 @@
+.AddObject1 {
+  :global {
+    .objectSonMain {
+      padding: 10px 30px;
+
+      .addInfoTop {
+        .row {
+          display: flex;
+          justify-content: space-between;
+          height: 50px;
+
+          &>div {
+            width: 45%;
+            height: 50px;
+            align-items: center;
+            display: flex;
+
+            &>span {
+              width: 80px;
+            }
+
+            .bs {
+              position: relative;
+
+              &::before {
+                content: '*';
+                position: absolute;
+                z-index: 10;
+                top: 2px;
+                left: -10px;
+                color: #ff4d4f;
+
+              }
+            }
+          }
+        }
+
+        .rowAll {
+          display: flex;
+          margin-top: 6px;
+
+          &>span {
+            width: 80px;
+          }
+
+          .ant-input-textarea {
+            width: calc(100% - 80px);
+          }
+        }
+      }
+
+      .addTableBox {
+        margin-top: 30px;
+        width: 100%;
+        height: 430px;
+
+        .addTableBox_Tit {
+          height: 40px;
+          display: flex;
+          align-items: center;
+          justify-content: space-between;
+
+          .addTableBox_TitL {
+            display: flex;
+            align-items: center;
+            color: var(--themeColor);
+            font-size: 20px;
+            font-weight: 700;
+          }
+        }
+
+        .addTableBox_table {
+          width: 100%;
+          height: calc(100% - 90px);
+        }
+
+        .addTableBox_btn {
+          width: 100%;
+          height: 40px;
+          display: flex;
+          justify-content: center;
+          margin-top: 10px;
+        }
+      }
+    }
+  }
+}

+ 341 - 0
src/pages/ObjectSon/Object1/AddObject1/index.tsx

@@ -0,0 +1,341 @@
+import BreadTit from "@/components/BreadTit";
+import history, { urlParameter } from "@/utils/history";
+import { Input, Select, Table, Popconfirm, Button } from "antd";
+import TextArea from "antd/es/input/TextArea";
+import React, {
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
+import { useLocation } from "react-router-dom";
+import styles from "./index.module.scss";
+import ObjectAdd from "@/components/ObjectAdd";
+import ImageLazy from "@/components/ImageLazy/index";
+import { useDispatch, useSelector } from "react-redux";
+import { RootState } from "@/store";
+import {
+  delInTablesAPI,
+  getObj1InfoTableAPI,
+  object1AddAPI,
+  object1infoOutAPI,
+} from "@/store/action/object1";
+import _ from "lodash";
+import { MessageFu } from "@/utils/message";
+function AddObject1() {
+  const dispatch = useDispatch();
+
+  // 从仓库中获取藏品来源下拉数据
+  const options = useSelector(
+    (state: RootState) => state.loginStore.selectAll["文物来源"]
+  );
+  // 顶部数据
+  const [addInfoTop, setAddInfoTop] = useState<any>({});
+
+  // 进入页面新增请求函数
+  const object1AddAPIFu = useCallback(async () => {
+    const res = await object1AddAPI();
+    setAddInfoTop({
+      ...res.data,
+      sourceName:null
+      // sourceName: options[0].name ? options[0].name : "",
+    });
+  }, []);
+
+  // 通过id获取详情函数
+  const object1infoOutAPIFu = useCallback(
+    async (id: number) => {
+      const res = await object1infoOutAPI(id);
+      setAddInfoTop(res.data);
+      // 获取表格详情信息
+      dispatch(getObj1InfoTableAPI(id));
+    },
+    [dispatch]
+  );
+
+  // 获取地址栏参数
+  const location = useLocation();
+  const [urlParam, setUrlParam] = useState<any>({});
+  useEffect(() => {
+    const obj = urlParameter(location.search);
+    setUrlParam(obj);
+
+    if (obj.id) {
+      // 如果是编辑
+      object1infoOutAPIFu(obj.id);
+    } else object1AddAPIFu();
+  }, [location, object1AddAPIFu, object1infoOutAPIFu]);
+
+  // 藏品来源下拉框
+  const handleChange = (value: string) => {
+    setAddInfoTop({ ...addInfoTop, sourceName: value });
+  };
+
+  // 表格数据
+
+  // 选中的表格数据
+  const [tableSelectList, setTableSelectList] = useState([]);
+
+  // 从仓库拿表格信息
+  const results = useSelector(
+    (state: RootState) => state.loginStore.goodsTableList
+  );
+
+  // 前端删除成功的id集合,用于点击存入草稿或者提交成功之后 传入后端删除
+  const delIds = useRef<any>([]);
+
+  // 点击删除
+  const delTableListFu = useCallback(() => {
+    console.log("多个删除", tableSelectList);
+    const data = _.differenceBy(results, tableSelectList, "id");
+    dispatch({ type: "login/setGoodsSonList", payload: data });
+    setTableSelectList(data);
+
+    // 删除的id存起来
+    tableSelectList.forEach((v: any) => {
+      delIds.current.push(v.id);
+    });
+  }, [dispatch, results, tableSelectList]);
+
+  const delOne = useCallback(
+    (id: number) => {
+      const data = results.filter((v: any) => v.id !== id);
+      dispatch({ type: "login/setGoodsSonList", payload: data });
+      console.log("单个删除", id);
+      // 删除的id存起来
+      delIds.current.push(id);
+    },
+
+    [dispatch, results]
+  );
+
+  const rowSelection = {
+    onChange: (selectedRowKeys: any, selectedRows: any) => {
+      setTableSelectList(selectedRows);
+    },
+  };
+
+  // 点击添加或者编辑
+
+  const addId = useRef<any>(null);
+
+  const addPageFu = useCallback((id?: any) => {
+    addId.current = id;
+    setAddPage(true);
+  }, []);
+
+  const columns = useMemo(() => {
+    return [
+      {
+        title: "缩略图",
+        render: (item: any) => (
+          <ImageLazy width={120} height={70} src={item.thumb} />
+        ),
+      },
+
+      {
+        title: "藏品编号名称",
+        dataIndex: "dictNum",
+      },
+      {
+        title: "藏品编号",
+        render: (item: any) => (item.num ? item.num : "-"),
+      },
+      {
+        title: "藏品名称",
+        dataIndex: "name",
+      },
+      {
+        title: "类别",
+        dataIndex: "dictGoodType",
+      },
+      {
+        title: "完残程度",
+        dataIndex: "complete",
+      },
+      {
+        title: "年代",
+        dataIndex: "dictAge",
+      },
+      {
+        title: "操作",
+        render: (item: any) => (
+          <>
+            <Button type="text" danger onClick={() => addPageFu(item.id)}>
+              编辑
+            </Button>
+            <Popconfirm
+              title="确定删除吗?"
+              okText="确定"
+              cancelText="取消"
+              onConfirm={() => delOne(item.id)}
+            >
+              <Button type="text" danger>
+                删除
+              </Button>
+            </Popconfirm>
+          </>
+        ),
+      },
+    ];
+  }, [addPageFu, delOne]);
+
+  // 点击返回
+  const cancelFu = useCallback(() => {
+    history.push({
+      pathname: `/object`,
+      state: { k: urlParam.k ? urlParam.k : "1", d: urlParam.d },
+    });
+  }, [urlParam.d, urlParam.k]);
+
+  // 点击提交
+  const submitFu = useCallback(
+    async (val: number) => {
+      if (!addInfoTop.sourceName) return MessageFu.warning("请选择藏品来源!");
+
+      if (results.length === 0)
+        return MessageFu.warning("至少需要添加一条藏品信息!");
+      const obj = {
+        ...addInfoTop,
+        status: val,
+      };
+      const res: any = await object1AddAPI(obj);
+      if (res.code === 0) {
+        // 真正进行删除
+        if (delIds.current.length) {
+          const res2: any = await delInTablesAPI(delIds.current.join(","));
+          if (res2.code === 0) {
+            MessageFu.success("操作成功!");
+            cancelFu();
+          }
+        } else {
+          MessageFu.success("操作成功!");
+          cancelFu();
+        }
+      }
+    },
+    [addInfoTop, cancelFu, results.length]
+  );
+
+  // 点击添加或者编辑出来页面
+  const [addPage, setAddPage] = useState(false);
+
+  return (
+    <div className={styles.AddObject1}>
+      <div className="breadTit">
+        <BreadTit>
+          <div className="breadTitRow">藏品登记</div>
+          <div className="splitStr">/</div>
+          <div className="breadTitRow active">
+            {urlParam.id ? "编辑" : "新增"}
+          </div>
+        </BreadTit>
+      </div>
+
+      <div className="objectSonMain">
+        {/* 上面的信息展示 */}
+        <div className="addInfoTop">
+          <div className="row">
+            <div>
+              <span className="bs">登记编号:</span>
+              <Input style={{ width: 400 }} value={addInfoTop.num} disabled />
+            </div>
+            <div>
+              <span className="bs">登记人员:</span>
+              <Input
+                style={{ width: 400 }}
+                value={addInfoTop.creatorName}
+                disabled
+              />
+            </div>
+          </div>
+          <div className="row">
+            <div>
+              <span className="bs">藏品来源:</span>
+              <Select
+                placeholder="请选择"
+                style={{ width: 400 }}
+                value={addInfoTop.sourceName}
+                onChange={handleChange}
+                options={options.map((v: any) => ({
+                  label: v.name,
+                  value: v.name,
+                }))}
+              />
+            </div>
+          </div>
+          <div className="rowAll">
+            <span>登记说明:</span>
+            <TextArea
+              value={addInfoTop.description}
+              onChange={(e) =>
+                setAddInfoTop({ ...addInfoTop, description: e.target.value })
+              }
+              rows={3}
+              placeholder="请输入"
+              showCount
+              maxLength={255}
+            />
+          </div>
+        </div>
+
+        {/* 下面的表格 */}
+        <div className="addTableBox">
+          <div className="addTableBox_Tit">
+            <div className="addTableBox_TitL">藏品信息</div>
+            <div className="addTableBox_TitR">
+              <Button onClick={() => addPageFu(null)}>添加</Button>
+              &emsp;
+              <Popconfirm
+                disabled={tableSelectList.length === 0}
+                title="确定删除吗?"
+                okText="确定"
+                cancelText="取消"
+                onConfirm={delTableListFu}
+              >
+                <Button disabled={tableSelectList.length === 0}>删除</Button>
+              </Popconfirm>
+            </div>
+          </div>
+          {/* 表格主体 */}
+          <div className="addTableBox_table">
+            <Table
+              size="small"
+              scroll={{ y: 300 }}
+              rowSelection={{
+                type: "checkbox",
+                ...rowSelection,
+              }}
+              dataSource={results}
+              columns={columns}
+              rowKey="id"
+              pagination={false}
+            />
+          </div>
+          <div className="addTableBox_btn">
+            <Button onClick={() => submitFu(0)}>存入草稿</Button>
+            &emsp;
+            <Button type="primary" onClick={() => submitFu(1)}>
+              提交
+            </Button>
+            &emsp;
+            <Button onClick={cancelFu}>返回</Button>
+          </div>
+        </div>
+      </div>
+      {/* 点击添加或者编辑出来的页面 */}
+      {addPage ? (
+        <ObjectAdd
+          id={addId.current}
+          colsePage={() => setAddPage(false)}
+          dirCode={addInfoTop.id}
+        />
+      ) : null}
+    </div>
+  );
+}
+
+const MemoAddObject1 = React.memo(AddObject1);
+
+export default MemoAddObject1;

+ 81 - 0
src/pages/ObjectSon/Object1/AuditObject1/index.module.scss

@@ -0,0 +1,81 @@
+.AuditObject1 {
+  :global {
+    .objectSonMain {
+      padding: 10px 30px;
+
+      .topTit {
+        font-size: 16px;
+        font-weight: 700;
+        color: var(--themeColor);
+        margin-bottom: 12px;
+      }
+
+      .topInfo {
+        .topInfoRow {
+          display: flex;
+
+          &>div {
+            width: 33.33%;
+            border: 1px solid #ccc;
+            height: 34px;
+            line-height: 32px;
+            display: flex;
+
+            .one {
+              font-weight: 700;
+              width: 80px;
+              text-align: right;
+            }
+          }
+        }
+
+        .topInfoTex {
+          cursor: pointer;
+          border: 1px solid #ccc;
+          padding: 5px 10px 4px;
+          display: -webkit-box;
+          overflow: hidden;
+          white-space: normal !important;
+          text-overflow: ellipsis;
+          word-wrap: break-word;
+          -webkit-line-clamp: 2;
+          -webkit-box-orient: vertical;
+
+          &>span {
+            font-weight: 700;
+          }
+        }
+
+      }
+
+      .goodsInfo{
+        .inputBox1{
+          margin-bottom: 10px;
+          display: flex;
+          align-items: center;
+          .inputBoxTit{
+            font-weight: 700;
+            width: 90px;
+            &>span{
+              position: relative;
+              top: 3px;
+              color: #ff4d4f;
+            }
+          }
+          .inputBoxText{
+            width: calc(100% - 90px);
+          }
+        }
+        .inputBox2{
+          align-items: flex-start;
+        }
+      }
+
+      .backBtn {
+        margin-top: 12px;
+        display: flex;
+        justify-content: center;
+      }
+    }
+  }
+}

+ 164 - 0
src/pages/ObjectSon/Object1/AuditObject1/index.tsx

@@ -0,0 +1,164 @@
+import BreadTit from "@/components/BreadTit";
+import LookObjTable from "@/components/LookObjTable";
+import { RootState } from "@/store";
+import {
+  auditObject1API,
+  getObj1InfoTableAPI2,
+  object1infoOutAPI,
+} from "@/store/action/object1";
+import { statusObj } from "@/utils/dataChange";
+import history, { urlParameter } from "@/utils/history";
+import { MessageFu } from "@/utils/message";
+import { Button, Select } from "antd";
+import TextArea from "antd/es/input/TextArea";
+import React, { useCallback, useEffect, useRef, useState } from "react";
+import { useDispatch, useSelector } from "react-redux";
+import { useLocation } from "react-router-dom";
+import styles from "./index.module.scss";
+const { Option } = Select;
+
+function AuditObject1() {
+  const dispatch = useDispatch();
+  // 获取地址栏参数
+  const location = useLocation();
+  const urlParamRef = useRef<any>({});
+  useEffect(() => {
+    urlParamRef.current = urlParameter(location.search);
+    // console.log("地址栏参数", urlParamRef.current);
+  }, [location]);
+
+  // 点击返回
+  const cancelFu = () => {
+    history.push({
+      pathname: `/object`,
+      state: {
+        k: urlParamRef.current.k ? urlParamRef.current.k : "1",
+        d: urlParamRef.current.d,
+      },
+    });
+  };
+
+  const getInfo = useCallback(async () => {
+    const id = urlParamRef.current.id;
+    const res1 = await object1infoOutAPI(id);
+    const res2 = await getObj1InfoTableAPI2(id);
+    const info = res1.data;
+    info.statusTxt = statusObj[info.status];
+    const list = res2.data;
+    dispatch({ type: "object1/getLookInfo", payload: { info, list } });
+  }, [dispatch]);
+
+  useEffect(() => {
+    getInfo();
+  }, [getInfo]);
+
+  const { info, list: tableList } = useSelector(
+    (state: RootState) => state.object1Store.lookInfo
+  );
+
+  // 审核结果筛选
+  const [value, setValue] = useState(3);
+  const valueChangeFu = (val: number) => {
+    setValue(val);
+  };
+  // 审核说明
+  const [value2, setValue2] = useState("");
+
+  // 点击确定
+  const btnOkFu = async () => {
+    const txt = value2.replaceAll(" ", "").replaceAll("\n", "");
+    if (txt === "") return MessageFu.warning("审核说明不能为空!");
+    const res: any = await auditObject1API({
+      id: Number(urlParamRef.current.id),
+      reason: value2,
+      status: value,
+    });
+    if (res.code === 0) {
+      MessageFu.success("操作成功!");
+      cancelFu();
+    }
+  };
+
+  return (
+    <div className={styles.AuditObject1}>
+      <div className="breadTit">
+        <BreadTit>
+          <div className="breadTitRow">藏品登记</div>
+          <div className="splitStr">/</div>
+          <div className="breadTitRow active">审核</div>
+        </BreadTit>
+      </div>
+      <div className="objectSonMain">
+        <div className="topTit">入库信息</div>
+        <div className="topInfo">
+          <div className="topInfoRow">
+            <div>
+              <div className="one">登记编号:</div>
+              <div>{info.num}</div>
+            </div>
+            <div>
+              <div className="one">登记人员:</div>
+              <div>{info.creatorName}</div>
+            </div>
+            <div>
+              <div className="one">藏品来源:</div>
+              <div>{info.sourceName}</div>
+            </div>
+          </div>
+          <div className="topInfoTex" title={info.description}>
+            <span>登记说明:</span>
+            {info.description ? info.description : "-"}
+          </div>
+        </div>
+        <br />
+        <div className="topTit">藏品信息</div>
+
+        <div className="goodsInfo">
+          {/* 封装的表格信息 */}
+          <LookObjTable data={tableList} y={245} />
+          <br />
+          <div className="inputBox1">
+            <div className="inputBoxTit">
+              <span>* </span>审核结果:
+            </div>
+            <Select
+              style={{ width: 150 }}
+              value={value}
+              onChange={(val) => valueChangeFu(val)}
+            >
+              <Option value={3}>通过</Option>
+              <Option value={2}>不通过</Option>
+            </Select>
+          </div>
+          <div className="inputBox1 inputBox2">
+            <div className="inputBoxTit">
+              <span>* </span>审核说明:
+            </div>
+            <div className="inputBoxText">
+              <TextArea
+                value={value2}
+                onChange={(e) => setValue2(e.target.value)}
+                rows={3}
+                placeholder="请输入"
+                showCount
+                maxLength={255}
+              />
+            </div>
+          </div>
+        </div>
+
+        <div className="backBtn">
+          <Button onClick={btnOkFu} type="primary">
+            提交
+          </Button>
+          &emsp;
+          <Button onClick={cancelFu}>返回</Button>
+        </div>
+      </div>
+    </div>
+  );
+}
+
+const MemoAuditObject1 = React.memo(AuditObject1);
+
+export default MemoAuditObject1;

+ 56 - 0
src/pages/ObjectSon/Object1/LookObject1/index.module.scss

@@ -0,0 +1,56 @@
+.LookObject1 {
+  :global {
+    .objectSonMain {
+      padding: 10px 30px;
+
+      .topTit {
+        font-size: 16px;
+        font-weight: 700;
+        color: var(--themeColor);
+        margin-bottom: 12px;
+      }
+
+      .topInfo {
+        .topInfoRow {
+          display: flex;
+          &>div {
+            width: 50%;
+            border: 1px solid #ccc;
+            height: 34px;
+            line-height: 32px;
+            display: flex;
+
+            .one {
+              font-weight: 700;
+              width: 80px;
+              text-align: right;
+            }
+          }
+        }
+
+        .topInfoTex {
+          cursor: pointer;
+          border: 1px solid #ccc;
+          padding: 5px 10px 4px;
+          display: -webkit-box;
+          overflow: hidden;
+          white-space: normal !important;
+          text-overflow: ellipsis;
+          word-wrap: break-word;
+          -webkit-line-clamp: 2;
+          -webkit-box-orient: vertical;
+
+          &>span {
+            font-weight: 700;
+          }
+        }
+
+      }
+      .backBtn{
+        margin-top: 12px;
+        display: flex;
+        justify-content: center;
+      }
+    }
+  }
+}

+ 128 - 0
src/pages/ObjectSon/Object1/LookObject1/index.tsx

@@ -0,0 +1,128 @@
+import AuthButton from "@/components/AuthButton";
+import BreadTit from "@/components/BreadTit";
+import LookObjTable from "@/components/LookObjTable";
+import history, { urlParameter } from "@/utils/history";
+import { Button } from "antd";
+import React, { useCallback, useEffect, useRef } from "react";
+import { useLocation } from "react-router-dom";
+import styles from "./index.module.scss";
+import {
+  getObj1InfoTableAPI2,
+  object1infoOutAPI,
+} from "@/store/action/object1";
+import { useDispatch, useSelector } from "react-redux";
+import { RootState } from "@/store";
+import { statusObj } from "@/utils/dataChange";
+function LookObject1() {
+  const dispatch = useDispatch();
+
+  // 获取地址栏参数
+  const location = useLocation();
+  const urlParamRef = useRef<any>({});
+  useEffect(() => {
+    urlParamRef.current = urlParameter(location.search);
+    // console.log("地址栏参数", urlParamRef.current);
+  }, [location]);
+
+  // 点击返回
+  const cancelFu = () => {
+    history.push({
+      pathname: `/object`,
+      state: {
+        k: urlParamRef.current.k ? urlParamRef.current.k : "1",
+        d: urlParamRef.current.d,
+      },
+    });
+  };
+
+  const getInfo = useCallback(async () => {
+    const id = urlParamRef.current.id;
+    const res1 = await object1infoOutAPI(id);
+    const res2 = await getObj1InfoTableAPI2(id);
+    const info = res1.data;
+    info.statusTxt = statusObj[info.status];
+    const list = res2.data;
+    dispatch({ type: "object1/getLookInfo", payload: { info, list } });
+  }, [dispatch]);
+
+  useEffect(() => {
+    getInfo();
+  }, [getInfo]);
+
+  const { info, list: tableList } = useSelector(
+    (state: RootState) => state.object1Store.lookInfo
+  );
+
+  return (
+    <div className={styles.LookObject1}>
+      <div className="breadTit">
+        <BreadTit>
+          <div className="breadTitRow">藏品登记</div>
+          <div className="splitStr">/</div>
+          <div className="breadTitRow active">查看</div>
+        </BreadTit>
+      </div>
+
+      <div className="objectSonMain">
+        <div className="topTit">登记信息</div>
+        <div className="topInfo">
+          <div className="topInfoRow">
+            <div>
+              <div className="one">登记编号:</div>
+              <div>{info.num}</div>
+            </div>
+            <div>
+              <div className="one">登记人员:</div>
+              <div>{info.creatorName}</div>
+            </div>
+          </div>
+          <div className="topInfoRow">
+            <div>
+              <div className="one">藏品来源:</div>
+              <div>{info.sourceName}</div>
+            </div>
+            <div>
+              <div className="one">审核结果:</div>
+              <div>{info.statusTxt}</div>
+            </div>
+          </div>
+          <div className="topInfoTex" title={info.description}>
+            <span>登记说明:</span>
+            {info.description ? info.description : "-"}
+          </div>
+          <div className="topInfoTex" title={info.reason}>
+            <span>审核说明:</span>
+            {info.reason ? info.reason : "-"}
+          </div>
+        </div>
+        <br />
+        <div className="topTit">藏品信息</div>
+        <div className="goodsInfo">
+          {/* 封装的表格信息 */}
+          <LookObjTable data={tableList} y={340} />
+        </div>
+        <div className="backBtn">
+          {info.status === 2 ? (
+            <AuthButton
+              id={102}
+              type="primary"
+              onClick={() =>
+                history.push(
+                  `/object/1/add?k=${urlParamRef.current.k}&d=${urlParamRef.current.d}&id=${urlParamRef.current.id}`
+                )
+              }
+            >
+              编辑
+            </AuthButton>
+          ) : null}
+          &emsp;
+          <Button onClick={cancelFu}>返回</Button>
+        </div>
+      </div>
+    </div>
+  );
+}
+
+const MemoLookObject1 = React.memo(LookObject1);
+
+export default MemoLookObject1;

+ 5 - 0
src/pages/ObjectSon/Object1/index.module.scss

@@ -0,0 +1,5 @@
+.Object1{
+  :global{
+    
+  }
+}

+ 339 - 0
src/pages/ObjectSon/Object1/index.tsx

@@ -0,0 +1,339 @@
+import BreadTit from "@/components/BreadTit";
+import classNames from "classnames";
+import { useEffect, useMemo, useRef, useState } from "react";
+import styles from "./index.module.scss";
+import {
+  Select,
+  Input,
+  DatePicker,
+  Table,
+  Button,
+  Popconfirm,
+} from "antd";
+import AuthButton from "@/components/AuthButton";
+import history from "@/utils/history";
+import { useLocation } from "react-router-dom";
+import { useDispatch, useSelector } from "react-redux";
+import { RootState } from "@/store";
+import {
+  getObject1List,
+  getObject1ListNum,
+  object1DelAPI,
+} from "@/store/action/object1";
+import { MessageFu } from "@/utils/message";
+
+const { RangePicker } = DatePicker;
+
+export default function Object1() {
+  const dispatch = useDispatch();
+
+  // 获取顶部数量
+
+  useEffect(() => {
+    dispatch(getObject1ListNum());
+  }, [dispatch]);
+
+  // 顶部的状态改变了,统一管理,传到二级页码
+  const statusRef = useRef<null | number>(null);
+
+  const dataTit = useSelector(
+    (state: RootState) => state.object1Store.infoNum1
+  );
+
+  // 封装发送请求的函数
+  const getList = () => {
+    const data = {
+      ...tableSelect,
+      pageNum: pageNumRef.current,
+      status: statusRef.current,
+    };
+    dispatch(getObject1List(data));
+  };
+
+  // 获取地址栏参数
+  const location = useLocation();
+
+  const pageNumRef = useRef(1);
+
+  // 如果有参数 根据参数页码在次发送请求
+  useEffect(() => {
+    const urlParam = location.state || {};
+    setTableSelect({
+      ...tableSelect,
+      pageNum: Number(urlParam.k) ? Number(urlParam.k) : 1,
+      // eslint-disable-next-line eqeqeq
+      status: urlParam.d && urlParam.d != "null" ? Number(urlParam.d) : null,
+    });
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, [location]);
+
+  // 顶部筛选
+  const [tableSelect, setTableSelect] = useState({
+    status: null as null | number,
+    sourceName: null,
+    searchKey: "",
+    startTime: "",
+    endTime: "",
+    pageSize: 10,
+    pageNum: 1,
+  });
+
+  // 当前页码统一
+  useEffect(() => {
+    pageNumRef.current = tableSelect.pageNum;
+  }, [tableSelect.pageNum]);
+
+  // 顶部状态统一
+  useEffect(() => {
+    statusRef.current = tableSelect.status;
+  }, [tableSelect.status]);
+
+  // 防止返回的时候发送了2次请求来对应页码
+
+  const getListRef = useRef(-1);
+
+  useEffect(() => {
+    clearTimeout(getListRef.current);
+    getListRef.current = window.setTimeout(() => {
+      getList();
+    }, 100);
+
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, [tableSelect]);
+
+  // 藏品来源下拉框筛选
+  const handleChange = (value: any) => {
+    setTableSelect({ ...tableSelect, sourceName: value, pageNum: 1 });
+  };
+
+  // 登记人员输入
+  const nameTime = useRef(-1);
+  const nameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+    clearTimeout(nameTime.current);
+    nameTime.current = window.setTimeout(() => {
+      setTableSelect({ ...tableSelect, searchKey: e.target.value, pageNum: 1 });
+    }, 500);
+  };
+
+  // 时间选择器改变
+  const timeChange = (date: any, dateString: any) => {
+    let startTime = "";
+    let endTime = "";
+    if (dateString[0] && dateString[1]) {
+      startTime = dateString[0] + " 00:00:00";
+      endTime = dateString[1] + " 23:59:59";
+    }
+    setTableSelect({ ...tableSelect, startTime, endTime, pageNum: 1 });
+  };
+
+  // 点击新增或者编辑按钮
+  const addObject = (id?: any) => {
+    // 清空详情页面里面的附件表格信息
+    dispatch({ type: "login/setGoodsSonList", payload: [] });
+
+    if (id)
+      history.push(
+        `/object/1/add?k=${pageNumRef.current}&d=${statusRef.current}&id=${id}`
+      );
+    else
+      history.push(
+        `/object/1/add?k=${pageNumRef.current}&d=${statusRef.current}`
+      );
+  };
+
+  // 点击删除按钮
+  const delOne = async (id: number) => {
+    const res: any = await object1DelAPI(id);
+    if (res.code === 0) {
+      MessageFu.success("删除成功!");
+      getList();
+      dispatch(getObject1ListNum());
+    }
+  };
+
+  // ---------关于表格
+
+  // 页码变化
+  const paginationChange = (pageNum: number, pageSize: number) => {
+    setTableSelect({ ...tableSelect, pageNum, pageSize });
+  };
+
+  const results = useSelector((state: RootState) => state.object1Store.info1);
+
+  const columns = useMemo(() => {
+    return [
+      {
+        title: "登记编号",
+        dataIndex: "num",
+      },
+      {
+        title: "藏品来源",
+        dataIndex: "sourceName",
+      },
+      {
+        title: "登记人员",
+        dataIndex: "creatorName",
+      },
+      {
+        title: "创建日期",
+        dataIndex: "createTime",
+      },
+      {
+        title: "完成日期",
+        render: (item: any) => (item.day && item.status === 3 ? item.day : "-"),
+      },
+      {
+        title: "状态",
+        dataIndex: "statusTxt",
+      },
+
+      {
+        title: "操作",
+        render: (item: any) => (
+          <>
+            <Button
+              type="text"
+              danger
+              onClick={() =>
+                history.push(
+                  `/object/1/look?k=${pageNumRef.current}&d=${statusRef.current}&id=${item.id}`
+                )
+              }
+            >
+              查看
+            </Button>
+            {item.status === 0 || item.status === 2 ? (
+              <AuthButton
+                id={102}
+                type="text"
+                danger
+                onClick={() => addObject(item.id)}
+              >
+                编辑
+              </AuthButton>
+            ) : null}
+
+            {item.status === 1 ? (
+              <AuthButton
+                id={105}
+                onClick={() =>
+                  history.push(
+                    `/object/1/audit?k=${pageNumRef.current}&d=${statusRef.current}&id=${item.id}`
+                  )
+                }
+                type="text"
+                danger
+              >
+                审核
+              </AuthButton>
+            ) : null}
+
+            {item.status === 0 || item.status === 2 ? (
+              <Popconfirm
+                title="确定删除吗?"
+                okText="确定"
+                cancelText="取消"
+                onConfirm={() => delOne(item.id)}
+              >
+                <AuthButton id={103} type="text" danger>
+                  删除
+                </AuthButton>
+              </Popconfirm>
+            ) : null}
+          </>
+        ),
+      },
+    ];
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, []);
+
+  // 从仓库中获取藏品来源下拉数据
+  const options = useSelector(
+    (state: RootState) => state.loginStore.selectAll["文物来源"]
+  );
+
+  return (
+    <div className={styles.Object1}>
+      <div className="breadTit">
+        <BreadTit>
+          <div className="breadTitRow active">藏品登记</div>
+        </BreadTit>
+      </div>
+      <div className="objectSonMain">
+        {/* 顶部筛选 */}
+        <div className="objectSonMainTit">
+          {dataTit.map((v: any) => (
+            <div
+              key={v.id}
+              onClick={() =>
+                setTableSelect({ ...tableSelect, status: v.id, pageNum: 1 })
+              }
+              className={classNames(
+                v.id === tableSelect.status ? "active" : ""
+              )}
+            >
+              {v.name}({v.num})
+            </div>
+          ))}
+        </div>
+        <div className="objectSonMainTable">
+          {/* 表格数据筛选 */}
+          <div className="tableSelectBox">
+            <div className="row">
+              <span>藏品来源:</span>
+              <Select
+                placeholder="请选择"
+                allowClear
+                style={{ width: 150 }}
+                value={tableSelect.sourceName}
+                onChange={handleChange}
+                options={options.map((v: any) => ({
+                  label: v.name,
+                  value: v.name,
+                }))}
+              />
+            </div>
+            <div className="row">
+              <span>登记人员:</span>
+              <Input
+                maxLength={10}
+                style={{ width: 150 }}
+                placeholder="请输入"
+                allowClear
+                onChange={(e) => nameChange(e)}
+              />
+            </div>
+            <div className="row">
+              <span>创建日期:</span>
+              <RangePicker onChange={timeChange} />
+            </div>
+            <div className="row">
+              <AuthButton id={102} type="primary" onClick={() => addObject()}>
+                新增
+              </AuthButton>
+            </div>
+          </div>
+
+          {/* 表格主体 */}
+          <div className="tableMain">
+            <Table
+              scroll={{ y: 428 }}
+              dataSource={results.list}
+              columns={columns}
+              rowKey="id"
+              pagination={{
+                showQuickJumper: true,
+                position: ["bottomCenter"],
+                showSizeChanger: true,
+                current: tableSelect.pageNum,
+                pageSize: tableSelect.pageSize,
+                total: results.total,
+                onChange: paginationChange,
+              }}
+            />
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+}

+ 26 - 0
src/pages/ObjectSon/Object2/LookObject2/index.css

@@ -0,0 +1,26 @@
+.LookObject2Model .ant-modal {
+  width: 1000px !important;
+}
+.LookObject2Model .moveLocBox {
+  border-top: 1px solid #ccc;
+  padding-top: 10px;
+}
+.LookObject2Model .moveLocBox > div {
+  display: flex;
+  align-items: center;
+  margin-bottom: 15px;
+}
+.LookObject2Model .moveLocBox > div > span {
+  font-weight: 700;
+  width: 80px;
+}
+.LookObject2Model .moveLocBox > div > p {
+  width: calc(100% - 90px);
+}
+.LookObject2Model .moveLocBox .moveBtn {
+  justify-content: center;
+}
+.LookObject2Model .logBox {
+  border-top: 1px solid #ccc;
+  padding-top: 10px;
+}

+ 28 - 0
src/pages/ObjectSon/Object2/LookObject2/index.less

@@ -0,0 +1,28 @@
+.LookObject2Model{
+  .ant-modal{
+    width: 1000px !important;
+  }
+  .moveLocBox{
+    border-top: 1px solid #ccc;
+    padding-top: 10px;
+    &>div{
+      display: flex;
+      align-items: center;
+      margin-bottom: 15px;
+      &>span{
+        font-weight: 700;
+        width: 80px;
+      }
+      &>p{
+        width: calc(100% - 90px);
+      }
+    }
+    .moveBtn{
+      justify-content: center;
+    }
+  }
+  .logBox{
+    border-top: 1px solid #ccc;
+    padding-top: 10px;
+  }
+}

+ 127 - 0
src/pages/ObjectSon/Object2/LookObject2/index.module.scss

@@ -0,0 +1,127 @@
+.LookObject2 {
+  :global {
+    .objectSonMain {
+      padding: 0px 30px;
+
+      .topInfoBox {
+        width: 100%;
+        display: flex;
+        height: 370px;
+
+        .topInfoBoxL {
+          display: flex;
+          align-items: center;
+          padding-top: 20px;
+          width: 300px;
+          margin-right: 20px;
+        }
+
+        .topInfoBoxR {
+          padding-top: 10px;
+          width: calc(100% - 320px);
+
+          .topInfoBoxRTit {
+            margin-bottom: 10px;
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: 0 10px 8px;
+            border-bottom: 1px solid #ccc;
+
+            &>div {
+              display: flex;
+              align-items: center;
+
+              &>h1 {
+                font-size: 20px;
+              }
+
+              .bs {
+                margin-left: 10px;
+                color: #fff;
+                height: 20px;
+                display: flex;
+                align-items: center;
+                padding: 0 5px;
+                border-radius: 3px;
+                font-size: 12px;
+              }
+
+              .bs1 {
+                border: 1px solid #ccc;
+                color: black;
+                // background-color: var(--themeColor);
+              }
+
+              .bs2 {
+                background-color: var(--themeColor);
+              }
+
+              .bs3 {
+                background-color: green;
+              }
+
+              .bs4 {
+                background-color: #999;
+              }
+            }
+          }
+
+          .topInfoBoxRTxt {
+            .row {
+              display: flex;
+              width: 100%;
+
+              &>div {
+                cursor: pointer;
+                padding: 0 5px;
+                width: 50%;
+                display: flex;
+                border: 1px solid #ccc;
+                height: 26px;
+                line-height: 24px;
+
+                span {
+                  font-weight: 700;
+                  display: inline-block;
+                  width: 100px;
+                  text-align: right;
+                }
+
+                p {
+                  overflow: hidden;
+                  text-overflow: ellipsis;
+                  white-space: nowrap;
+                  width: calc(100% - 110px);
+                }
+              }
+            }
+          }
+
+        }
+      }
+
+      .tableInfoBox {
+        width: 100%;
+        height: 265px;
+
+        .tableInfoBoxTit {
+          margin-bottom: 5px;
+          display: flex;
+          align-items: center;
+
+          .rowTit {
+            font-weight: 700;
+          }
+        }
+      }
+
+      .backBtn {
+        height: 40px;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+      }
+    }
+  }
+}

+ 534 - 0
src/pages/ObjectSon/Object2/LookObject2/index.tsx

@@ -0,0 +1,534 @@
+import AuthButton from "@/components/AuthButton";
+import BreadTit from "@/components/BreadTit";
+import ImageLazy from "@/components/ImageLazy";
+import ObjectAdd from "@/components/ObjectAdd";
+import { RootState } from "@/store";
+import history, { urlParameter } from "@/utils/history";
+import { Button, Cascader, Modal, Select, Table } from "antd";
+import React, {
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
+import { useDispatch, useSelector } from "react-redux";
+import { useLocation } from "react-router-dom";
+import styles from "./index.module.scss";
+import "./index.css";
+import LookObject2Log from "./table";
+import { baseURL } from "@/utils/http";
+import classNames from "classnames";
+import { editObj2StoresAPI, getObj2InfoInAPI } from "@/store/action/object2";
+import { obj3InStorage, storageStatusObj } from "@/utils/dataChange";
+import { getStores1ListAPI } from "@/store/action/stores1";
+import { MessageFu } from "@/utils/message";
+
+function LookObject2() {
+  const dispatch = useDispatch();
+  // 获取地址栏参数
+  const location = useLocation();
+  const urlParamRef = useRef<any>({});
+  useEffect(() => {
+    urlParamRef.current = urlParameter(location.search);
+    // console.log("地址栏参数", urlParamRef.current);
+  }, [location]);
+
+  // 进页面通过id获取单个藏品详情
+  const getObj2InfoInAPIFu = useCallback(async () => {
+    const id = urlParamRef.current.id;
+    const res = await getObj2InfoInAPI(id);
+    const info = res.data.entity;
+    const fileList = {
+      img: [],
+      video: [],
+      audio: [],
+      model: [],
+      doc: [],
+    } as any;
+    res.data.file.forEach((v: any) => {
+      fileList[v.type].push(v);
+    });
+
+    dispatch({
+      type: "object2/getOneGoodsInfo",
+      payload: { info, fileList },
+    });
+  }, [dispatch]);
+
+  // 申请编辑和申请移库之后需要重新发送请求来更新按钮状态
+  const upInfoAPIFu = useCallback(() => {
+    getObj2InfoInAPIFu();
+  }, [getObj2InfoInAPIFu]);
+
+  useEffect(() => {
+    getObj2InfoInAPIFu();
+    return () => {
+      // 退出页面的时候初始化仓库数据
+      dispatch({
+        type: "object2/getOneGoodsInfo",
+        payload: {
+          info: {},
+          fileList: {
+            img: [],
+            video: [],
+            audio: [],
+            model: [],
+            doc: [],
+          },
+        },
+      });
+    };
+  }, [dispatch, getObj2InfoInAPIFu]);
+
+  // 点击返回
+  const cancelFu = () => {
+    history.push({
+      pathname: `/object/2`,
+      state: {
+        k: urlParamRef.current.k ? urlParamRef.current.k : "1",
+        d: urlParamRef.current.d,
+      },
+    });
+  };
+
+  // 附件类型下拉框
+
+  const fileSelectList = useMemo(() => {
+    return [
+      { id: 1, value: "img", label: "高清图片" },
+      { id: 2, value: "video", label: "视频文件" },
+      { id: 3, value: "audio", label: "音频文件" },
+      { id: 4, value: "model", label: "三维模型" },
+      { id: 5, value: "doc", label: "文档资料" },
+    ];
+  }, []);
+  const [fileSelect, setFileSelect] = useState("img");
+  const fileSelectChangeFu = (value: string) => {
+    setFileSelect(value);
+  };
+
+  // 从仓库中获取信息
+  const { info, fileList } = useSelector(
+    (state: RootState) => state.object2Store.oneGoodsInfo
+  );
+
+  // 外形尺寸和具体质量
+  let sizeRes = "";
+  if (info.size && info.size.length) {
+    const sizeArr = info.size.split(",");
+    sizeRes = `(通长)${sizeArr[0]}cm&emsp;(通宽)${sizeArr[1]}cm&emsp;(通高)${sizeArr[2]}cm`;
+  }
+
+  let qualityRes = "-";
+  if (info.quality && info.quality.length) {
+    const qualityArr = info.quality.split(",");
+    qualityRes = qualityArr[0] + qualityArr[1];
+  }
+
+  // 里面表格格式
+  const columns = useMemo(() => {
+    const tempArr = [
+      {
+        title: "附件名称",
+        dataIndex: "name",
+      },
+      {
+        title: "上传人",
+        dataIndex: "creatorName",
+      },
+
+      {
+        title: "上传时间",
+        dataIndex: "updateTime",
+      },
+      {
+        title: "操作",
+        render: (item: any) => (
+          <a
+            target="_blank"
+            href={baseURL + item.filePath}
+            download
+            rel="noreferrer"
+          >
+            下载
+          </a>
+        ),
+      },
+    ] as any;
+    if (fileSelect === "img") {
+      tempArr.unshift({
+        title: "缩略图",
+        render: (item: any) => (
+          <ImageLazy width={80} height={50} src={item.filePath} />
+        ),
+      });
+    }
+    return tempArr;
+  }, [fileSelect]);
+
+  // 关于  申请编辑  申请移库  操作记录
+  const [titCut, setTitCut] = useState("");
+
+  const titCutFu = useCallback(
+    (val: string) => {
+      if (val === "2") {
+        dispatch(getStores1ListAPI());
+      }
+      setTitCut(val);
+    },
+    [dispatch]
+  );
+
+  // 从仓库中获取库房总信息
+  const moveOptions = useSelector(
+    (state: RootState) => state.stores1Store.infoList
+  );
+
+  const moveLocRef = useRef("");
+  const [moveLoc, setMoveLoc] = useState<any>([]);
+  const moveLocChangeFu = (val: any, names: any) => {
+    setMoveLoc(val);
+    let temp = "";
+    names.forEach((v: any, i: number) => {
+      if (i < names.length - 1) temp += v.name + "/";
+      else temp += v.name;
+    });
+    moveLocRef.current = temp;
+  };
+
+  const moveBtnOk = useCallback(async () => {
+    const oldLoc = info.storageAncestor;
+    const newLoc = moveLoc.join(",");
+    if (!newLoc) return MessageFu.warning("请选择移库位置!");
+    if (oldLoc === newLoc) return MessageFu.warning("位置不能相同!");
+    const obj = {
+      beforeJson: { id: oldLoc, name: obj3InStorage(info.storageAncestor) },
+      afterJson: { id: newLoc, name: moveLocRef.current },
+      goodsIds: info.id,
+    };
+
+    const res: any = await editObj2StoresAPI(obj);
+    if (res.code === 0) {
+      MessageFu.success("申请移库成功!");
+      // 更新页面
+      getObj2InfoInAPIFu();
+      setTitCut("");
+    }
+
+    console.log("移库点击提交", oldLoc, newLoc);
+  }, [getObj2InfoInAPIFu, info.id, info.storageAncestor, moveLoc]);
+
+  return (
+    <div className={styles.LookObject2}>
+      <div className="breadTit">
+        <BreadTit>
+          <div className="breadTitRow">藏品总账</div>
+          <div className="splitStr">/</div>
+          <div className="breadTitRow active">查看</div>
+        </BreadTit>
+      </div>
+      <div className="objectSonMain">
+        {/* 上面的信息 */}
+        <div className="topInfoBox">
+          <div className="topInfoBoxL">
+            {/* 解决有图片进页面显示 加载失败 图片 问题 */}
+            {info.id ? (
+              <ImageLazy src={info.thumb} width={300} height={300} />
+            ) : null}
+          </div>
+          <div className="topInfoBoxR">
+            <div className="topInfoBoxRTit">
+              <div>
+                <h1>{info.name}</h1>
+                <div
+                  className={classNames(
+                    "bs",
+                    info.storageStatus === "0"
+                      ? "bs1"
+                      : info.storageStatus === "in"
+                      ? "bs2"
+                      : info.storageStatus === "otu"
+                      ? "bs3"
+                      : "bs4"
+                  )}
+                >
+                  {storageStatusObj[info.storageStatus]}
+                </div>
+              </div>
+              <div>
+                <AuthButton
+                  id={202}
+                  disabled={info.tempEdit !== 0}
+                  size="small"
+                  type="primary"
+                  onClick={() => titCutFu("1")}
+                >
+                  申请编辑
+                </AuthButton>
+                &emsp;
+                {info.storageStatus !== "0" && info.storageStatus !== "temp" ? (
+                  <>
+                    <AuthButton
+                      id={205}
+                      disabled={
+                        info.tempMove !== 0 || info.storageStatus === "out"
+                      }
+                      size="small"
+                      type="primary"
+                      onClick={() => titCutFu("2")}
+                    >
+                      申请移库
+                    </AuthButton>
+                    &emsp;
+                  </>
+                ) : null}
+                <Button size="small" onClick={() => titCutFu("3")}>
+                  操作记录
+                </Button>
+              </div>
+            </div>
+            <div className="topInfoBoxRTxt">
+              <div className="row">
+                <div title={info.name}>
+                  <span>藏品名称:</span>
+                  <p>{info.name}</p>
+                </div>
+                <div title={info.namePrimitive}>
+                  <span>原名:</span>
+                  <p>{info.namePrimitive ? info.namePrimitive : "-"}</p>
+                </div>
+              </div>
+              <div className="row">
+                <div title={info.dictNum}>
+                  <span>藏品编号名称:</span>
+                  <p>{info.dictNum}</p>
+                </div>
+                <div title={info.num}>
+                  <span>藏品编号:</span>
+                  <p>{info.num ? info.num : "-"}</p>
+                </div>
+              </div>
+              <div className="row">
+                <div title={info.dictAge}>
+                  <span>年代:</span>
+                  <p>{info.dictAge}</p>
+                </div>
+                <div title={info.dictTexture}>
+                  <span>文物质地:</span>
+                  <p>{info.dictTexture}</p>
+                </div>
+              </div>
+              <div className="row">
+                <div title={info.ageSpecific}>
+                  <span>具体年代:</span>
+                  <p>{info.ageSpecific ? info.ageSpecific : "-"}</p>
+                </div>
+                <div title={info.dictGoodType}>
+                  <span>文物类别:</span>
+                  <p>{info.dictGoodType}</p>
+                </div>
+              </div>
+
+              <div className="row">
+                <div title={info.pcs}>
+                  <span>实际数量:</span>
+                  <p>{info.pcs}</p>
+                </div>
+                <div title={info.dictLevel}>
+                  <span>文物级别:</span>
+                  <p>{info.dictLevel}</p>
+                </div>
+              </div>
+              <div className="row">
+                <div title={sizeRes.replaceAll("&emsp;", " ")}>
+                  <span>外形尺寸:</span>
+                  <p dangerouslySetInnerHTML={{ __html: sizeRes }}></p>
+                </div>
+                <div title={info.sizeSpecific}>
+                  <span>具体尺寸:</span>
+                  <p>{info.sizeSpecific}</p>
+                </div>
+              </div>
+              <div className="row">
+                <div title={info.dictQualityScope}>
+                  <span>质量范围:</span>
+                  <p>{info.dictQualityScope}</p>
+                </div>
+                <div title={qualityRes}>
+                  <span>具体质量:</span>
+                  <p>{qualityRes}</p>
+                </div>
+              </div>
+
+              <div className="row">
+                <div title={info.complete}>
+                  <span>完残程度:</span>
+                  <p>{info.complete}</p>
+                </div>
+                <div title={info.repair}>
+                  <span>保存状态:</span>
+                  <p>{info.repair ? info.repair : "-"}</p>
+                </div>
+              </div>
+
+              <div className="row">
+                <div title={info.dictCheckInScope}>
+                  <span>入藏时间范围:</span>
+                  <p>{info.dictCheckInScope}</p>
+                </div>
+                <div title={info.checkInYear}>
+                  <span>入藏年度:</span>
+                  <p>{info.checkInYear ? info.checkInYear : "-"}</p>
+                </div>
+              </div>
+
+              <div className="row">
+                <div title={info.author}>
+                  <span>著者:</span>
+                  <p>{info.author ? info.author : "-"}</p>
+                </div>
+                <div title={info.vision}>
+                  <span>版本:</span>
+                  <p>{info.vision ? info.vision : "-"}</p>
+                </div>
+              </div>
+
+              <div className="row">
+                <div title={info.onFile}>
+                  <span>存卷:</span>
+                  <p>{info.onFile ? info.onFile : "-"}</p>
+                </div>
+                <div title={info.description}>
+                  <span>来源说明:</span>
+                  <p>{info.description ? info.description : "-"}</p>
+                </div>
+              </div>
+
+              <div className="row">
+                <div
+                  title={
+                    info.storageStatus !== "0" &&
+                    info.storageStatus !== "temp" &&
+                    info.storageAncestor
+                      ? obj3InStorage(info.storageAncestor)
+                      : ""
+                  }
+                >
+                  <span>入库位置:</span>
+                  <p>
+                    {info.storageStatus !== "0" &&
+                    info.storageStatus !== "temp" &&
+                    info.storageAncestor
+                      ? obj3InStorage(info.storageAncestor)
+                      : "-"}
+                  </p>
+                </div>
+                <div title={info.outLocation}>
+                  <span>出库位置:</span>
+                  <p>{info.outLocation ? info.outLocation : "-"}</p>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+        {/* 下面的表格 */}
+        <div className="tableInfoBox">
+          <div className="tableInfoBoxTit">
+            <div className="rowTit">数字资源:</div>
+            <div className="rowRr">
+              <Select
+                style={{ width: 150 }}
+                value={fileSelect}
+                onChange={fileSelectChangeFu}
+                options={fileSelectList}
+              />
+            </div>
+          </div>
+          {/* 表格主体 */}
+          <Table
+            size="small"
+            scroll={{ y: 190 }}
+            dataSource={fileList[fileSelect]}
+            columns={columns}
+            rowKey="id"
+            pagination={false}
+          />
+        </div>
+
+        <div className="backBtn">
+          <Button onClick={cancelFu}>返回</Button>
+        </div>
+      </div>
+
+      {/* 点击上面的编辑 移库 操作记录出来的弹窗 */}
+
+      {/*  申请编辑 */}
+      {titCut === "1" ? (
+        <ObjectAdd
+          upInfoAPIFu={upInfoAPIFu}
+          dirCode={info.dirCode}
+          id={urlParamRef.current.id}
+          colsePage={() => setTitCut("")}
+          editId="edit"
+        />
+      ) : (
+        <Modal
+          wrapClassName="LookObject2Model"
+          destroyOnClose
+          open={!!titCut}
+          title={titCut === "2" ? "申请移库" : "操作记录"}
+          onCancel={() => setTitCut("")}
+          footer={
+            [] // 设置footer为空,去掉 取消 确定默认按钮
+          }
+        >
+          {/* 申请移库 */}
+          {titCut === "2" ? (
+            <div className="moveLocBox">
+              <div>
+                <span>当前位置:</span>
+                <p>{obj3InStorage(info.storageAncestor)}</p>
+              </div>
+              <div>
+                <span>移库位置:</span>
+                <Cascader
+                  style={{ width: 300 }}
+                  allowClear={false}
+                  value={moveLoc}
+                  onChange={moveLocChangeFu}
+                  fieldNames={{
+                    label: "name",
+                    value: "id",
+                    children: "children",
+                  }}
+                  options={moveOptions}
+                  placeholder="请选择"
+                />
+              </div>
+              <br />
+              <br />
+              <div className="moveBtn">
+                <Button type="primary" onClick={moveBtnOk}>
+                  提交
+                </Button>
+              </div>
+            </div>
+          ) : null}
+
+          {/* 操作记录 */}
+          {titCut === "3" ? (
+            <div className="logBox">
+              {/* 表格 */}
+              <LookObject2Log id={info.id} />
+            </div>
+          ) : null}
+        </Modal>
+      )}
+    </div>
+  );
+}
+
+const MemoLookObject2 = React.memo(LookObject2);
+
+export default MemoLookObject2;

+ 127 - 0
src/pages/ObjectSon/Object2/LookObject2/table.tsx

@@ -0,0 +1,127 @@
+import { RootState } from "@/store";
+import { getObj2LogListAPI } from "@/store/action/object2";
+import { logTypeObj, logTypeOpenObj } from "@/utils/dataChange";
+import { Button, Table } from "antd";
+import React, {
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
+import { useDispatch, useSelector } from "react-redux";
+
+type Props = {
+  id: number;
+};
+
+function LookObject2Log({ id }: Props) {
+  const dispatch = useDispatch();
+
+  const powerInfo = useSelector(
+    (state: RootState) => state.loginStore.authPageArr
+  );
+
+  const pageNumRef = useRef(1);
+  const pagePageRef = useRef(10);
+  const [tableSelect, setTableSelect] = useState({
+    pageNum: 1,
+    pageSize: 10,
+    goodsId: id,
+  });
+
+  useEffect(() => {
+    pageNumRef.current = tableSelect.pageNum;
+    pagePageRef.current = tableSelect.pageSize;
+    dispatch(getObj2LogListAPI(tableSelect));
+  }, [dispatch, tableSelect]);
+
+  const paginationChange = (pageNum: number, pageSize: number) => {
+    pageNumRef.current = pageNum;
+    pagePageRef.current = pageSize;
+    setTableSelect({ ...tableSelect, pageNum, pageSize });
+  };
+
+  // 从仓库中获取列表信息
+  const logResults = useSelector(
+    (state: RootState) => state.object2Store.logList
+  );
+
+  // 点击操作记录里面的查看,打开新的页面
+  const openURL = useCallback((url: string) => {
+    window.open(url);
+  }, []);
+
+  const columns = useMemo(() => {
+    const tempArr = [
+      {
+        title: "序号",
+        render: (text: any, record: any, index: any) =>
+          index + 1 + (pageNumRef.current - 1) * pagePageRef.current,
+      },
+      {
+        title: "业务单号",
+        dataIndex: "num",
+      },
+      {
+        title: "业务类型",
+        render: (item: any) => logTypeObj[item.type],
+      },
+
+      {
+        title: "完成时间",
+        dataIndex: "day",
+      },
+      {
+        title: "操作",
+        render: (item: any) => {
+          let falg = false;
+          powerInfo.forEach((v: any) => {
+            if (
+              (item.type === "in" && v.id === 300) ||
+              (item.type === "out" && v.id === 400) ||
+              (item.type === "move" && v.id === 800) ||
+              (item.type === "edit" && v.id === 500)
+            )
+              falg = true;
+          });
+          return falg ? (
+            <Button
+              type="text"
+              danger
+              onClick={() => openURL(logTypeOpenObj[item.type] + item.id)}
+            >
+              查看
+            </Button>
+          ) : (
+            <span>&emsp; - </span>
+          );
+        },
+      },
+    ];
+    return tempArr;
+  }, [openURL, powerInfo]);
+
+  return (
+    <Table
+      size="small"
+      scroll={{ y: 300 }}
+      dataSource={logResults.list}
+      columns={columns}
+      rowKey="id"
+      pagination={{
+        showQuickJumper: true,
+        position: ["bottomCenter"],
+        showSizeChanger: true,
+        current: tableSelect.pageNum,
+        pageSize: tableSelect.pageSize,
+        total: logResults.total,
+        onChange: paginationChange,
+      }}
+    />
+  );
+}
+
+const MemoLookObject2Log = React.memo(LookObject2Log);
+
+export default MemoLookObject2Log;

+ 91 - 0
src/pages/ObjectSon/Object2/index.module.scss

@@ -0,0 +1,91 @@
+.Object2 {
+  :global {
+    .searchBox {
+      padding: 20px 35px;
+      display: flex;
+      align-items: center;
+
+      .row {
+        margin-right: 50px;
+
+        .cutShow {
+          cursor: pointer;
+          display: inline-block;
+          width: 50px;
+          height: 32px;
+          border: 1px solid #ccc;
+          border-radius: 4px 0 0 4px;
+          font-size: 20px;
+          text-align: center;
+        }
+
+        .cutShow2 {
+          border-radius: 0 4px 4px 0;
+        }
+
+        .active {
+          pointer-events: none;
+          color: #D3B453;
+          background-color: var(--themeColor);
+        }
+      }
+    }
+
+    .imgList {
+      padding: 0 30px;
+      width: 100%;
+      height: calc(100% - 176px);
+      overflow-y: auto;
+      display: flex;
+      flex-wrap: wrap;
+      .imgListNone{
+        color: var(--themeColor);
+        font-size: 20px;
+        margin: 0 auto;
+        padding-top: 200px;
+      }
+
+      .imgListRow {
+        cursor: pointer;
+        transition: all .3s;
+        width: 238px;
+        height: 240px;
+        border: 1px solid #ccc;
+        margin: 0 20px 20px 0;
+
+        &:nth-of-type(5n) {
+          margin: 0 0 20px 0;
+        }
+
+        &>p {
+          border-top: 1px solid #ccc;
+          width: 100%;
+          height: 40px;
+          line-height: 40px;
+          text-align: center;
+          padding: 0 3px;
+          overflow: hidden;
+          text-overflow: ellipsis;
+          white-space: nowrap;
+        }
+        &:hover{
+          box-shadow: 1px 1px 5px 1px #ccc;
+        }
+      }
+    }
+
+    .tableBox {
+      padding: 0 30px;
+      height: calc(100% - 176px);
+
+    }
+
+    .pageBox {
+      width: 100%;
+      display: flex;
+      height: 60px;
+      align-items: center;
+      justify-content: center;
+    }
+  }
+}

+ 420 - 0
src/pages/ObjectSon/Object2/index.tsx

@@ -0,0 +1,420 @@
+import BreadTit from "@/components/BreadTit";
+import { RootState } from "@/store";
+import { Button, Input, Pagination, Select, Table } from "antd";
+import { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { useDispatch, useSelector } from "react-redux";
+import styles from "./index.module.scss";
+import { UnorderedListOutlined, AppstoreOutlined } from "@ant-design/icons";
+import classNames from "classnames";
+import ImageLazy from "@/components/ImageLazy";
+import history from "@/utils/history";
+import { useLocation } from "react-router-dom";
+import dayjs from "dayjs";
+import ExportJsonExcel from "js-export-excel";
+import { getObject2List } from "@/store/action/object2";
+import http from "@/utils/http";
+import { Empty } from "antd";
+import { obj3InStorage } from "@/utils/dataChange";
+import { MessageFu } from "@/utils/message";
+const { Option } = Select;
+export default function Object2() {
+  const dispatch = useDispatch();
+
+  // 从仓库中获取藏品编号名称下拉数据
+  const options = useSelector(
+    (state: RootState) => state.loginStore.selectAll["藏品编号名称"]
+  );
+
+  // 从仓库中获取表格数据(图片展示数据)
+  const results = useSelector((state: RootState) => state.object2Store.info2);
+
+  // 封装发送请求的函数
+  const getList = () => {
+    const data = {
+      ...tableSelect,
+      pageNum: pageNumRef.current,
+      searchType: value,
+    };
+    dispatch(getObject2List(data));
+    // console.log("------", data);
+  };
+
+  // 获取地址栏参数
+  const location = useLocation();
+
+  // 如果有参数 根据参数页码在次发送请求
+  useEffect(() => {
+    const urlParam = location.state || {};
+    if (urlParam.k && urlParam.k !== "1")
+      setTableSelect({ ...tableSelect, pageNum: Number(urlParam.k) });
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, [location]);
+
+  // 筛选表格的数据
+  const [tableSelect, setTableSelect] = useState<any>({
+    dictNum: null,
+    searchKey: "",
+    pageSize: 10,
+    pageNum: 1,
+  });
+
+  // 图片页面和列表页面的切换
+  const [cutShow, setCutShow] = useState("cutShow1");
+
+  // 当前图片查看或者表格查看数据统一
+
+  const cutShowRef = useRef("1");
+
+  // 进页面如果 d的值为2 切换到表格显示
+  useEffect(() => {
+    const urlParam = location.state || {};
+    if (urlParam.d && urlParam.d === "2") {
+      cutShowRef.current = "2";
+      window.setTimeout(() => {
+        setCutShow("cutShow2");
+      }, 20);
+    }
+  }, [location.state]);
+
+  const cutShowFu = useCallback((val: string) => {
+    cutShowRef.current = val === "cutShow1" ? "1" : "2";
+    setCutShow(val);
+  }, []);
+
+  // 当前页码统一
+  const pageNumRef = useRef(1);
+  useEffect(() => {
+    pageNumRef.current = tableSelect.pageNum;
+  }, [tableSelect.pageNum]);
+
+  // 防止返回的时候发送了2次请求来对应页码
+  const getListRef = useRef(-1);
+
+  useEffect(() => {
+    clearTimeout(getListRef.current);
+    getListRef.current = window.setTimeout(() => {
+      getList();
+    }, 100);
+
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, [tableSelect]);
+
+  // 藏品来源下拉框改变
+  const handleChange = (val: string) => {
+    setTableSelect({ ...tableSelect, dictNum: val, pageNum: 1 });
+  };
+
+  // 搜索输入框下拉
+  const [value, setValue] = useState("name");
+  const valueChangeFu = (val: string) => {
+    if (tableSelect.searchKey) {
+      // 根据输入框的值重新发送请求
+      setTableSelect({ ...tableSelect, searchType: val });
+    }
+
+    setValue(val);
+  };
+  const nameTime = useRef(-1);
+  const nameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+    clearTimeout(nameTime.current);
+    nameTime.current = window.setTimeout(() => {
+      setTableSelect({ ...tableSelect, searchKey: e.target.value, pageNum: 1 });
+    }, 500);
+  };
+
+  // 关于表格的数据
+  const paginationChange = (pageNum: number, pageSize: number) => {
+    setTableSelect({ ...tableSelect, pageNum, pageSize });
+  };
+
+  const columns = useMemo(() => {
+    return [
+      {
+        title: "缩略图",
+        render: (item: any) => (
+          <ImageLazy width={120} height={70} src={item.thumb} />
+        ),
+      },
+      {
+        title: "藏品编号名称",
+        dataIndex: "dictNum",
+      },
+      {
+        title: "藏品编号",
+        render: (item: any) => (item.num ? item.num : "-"),
+      },
+      {
+        title: "藏品名称",
+        dataIndex: "name",
+      },
+      {
+        title: "年代",
+        dataIndex: "dictAge",
+      },
+      {
+        title: "质地",
+        dataIndex: "dictTexture",
+      },
+      {
+        title: "展示状态",
+        render: (item: any) => (item.display ? "是" : "否"),
+      },
+      {
+        title: "状态",
+        dataIndex: "storageStatusTxt",
+      },
+      {
+        title: "操作",
+        render: (item: any) => (
+          <>
+            <Button
+              type="text"
+              danger
+              onClick={() =>
+                history.push(
+                  `/object/2/look?k=${pageNumRef.current}&d=${cutShowRef.current}&id=${item.id}`
+                )
+              }
+            >
+              查看
+            </Button>
+          </>
+        ),
+      },
+    ];
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, []);
+
+  // 点击导出
+  const deriveFu = useCallback(async () => {
+    if (results.list.length === 0)
+      return MessageFu.warning("当前搜索条件没有数据!");
+    const name = dayjs(new Date()).format("YYYYMMDDHHmmss");
+    const res = await http.post("cms/ledger/pageList", {
+      ...tableSelect,
+      pageSize: 9999,
+      searchType: value,
+    });
+    const sheetData = res.data.records;
+    sheetData.forEach((v: any) => {
+      if (v.size && v.size.length) {
+        const sizeArr = v.size.split(",");
+        v.sizeRes = `(通长)${sizeArr[0]}cm;(通宽)${sizeArr[1]}cm;(通高)${sizeArr[2]}cm`;
+      }
+      v.qualityRes = "-";
+      if (v.quality && v.quality.length) {
+        const qualityArr = v.quality.split(",");
+        v.qualityRes = qualityArr[0] + qualityArr[1];
+      }
+      v.namePrimitive = v.namePrimitive ? v.namePrimitive : "-";
+      v.num = v.num ? v.num : "-";
+      v.ageSpecific = v.ageSpecific ? v.ageSpecific : "-";
+      v.repair = v.repair ? v.repair : "-";
+      v.checkInYear = v.checkInYear ? v.checkInYear : "-";
+      v.author = v.author ? v.author : "-";
+      v.vision = v.vision ? v.vision : "-";
+      v.onFile = v.onFile ? v.onFile : "-";
+      v.description = v.description ? v.description : "-";
+      v.storageAncestorName = v.storageAncestor
+        ? obj3InStorage(v.storageAncestor)
+        : "-";
+      v.outLocation = v.outLocation ? v.outLocation : "-";
+      v.display = v.display ? "是" : "否";
+    });
+    const option = {
+      fileName: name,
+      datas: [
+        {
+          sheetData,
+          sheetName: name,
+          sheetFilter: [
+            "name",
+            "namePrimitive",
+            "dictNum",
+            "num",
+            "dictAge",
+            "dictTexture",
+            "ageSpecific",
+            "dictGoodType",
+            "pcs",
+            "dictLevel",
+            "sizeRes",
+            "sizeSpecific",
+            "dictQualityScope",
+            "qualityRes",
+            "complete",
+            "repair",
+            "dictCheckInScope",
+            "checkInYear",
+            "author",
+            "vision",
+            "onFile",
+            "description",
+            "storageAncestorName",
+            "outLocation",
+            "display",
+          ],
+          sheetHeader: [
+            "藏品名称",
+            "原名",
+            "藏品编号名称",
+            "藏品编号",
+            "年代",
+            "文物质地",
+            "具体年代",
+            "文物类别",
+            "实际数量",
+            "文物级别",
+            "外形尺寸",
+            "具体尺寸",
+            "质量范围",
+            "具体质量",
+            "完残程度",
+            "保存状态",
+            "入藏时间范围",
+            "入藏年度",
+            "著者",
+            "版本",
+            "存卷",
+            "来源说明",
+            "入库位置",
+            "出库位置",
+            "展示状态",
+          ],
+          columnWidths: [
+            10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+            10, 10, 10, 10, 10, 10, 10,
+          ],
+        },
+      ],
+    };
+
+    const toExcel = new ExportJsonExcel(option); //new
+    toExcel.saveExcel(); //保存
+  }, [results.list, tableSelect, value]);
+
+  return (
+    <div className={styles.Object2}>
+      <div className="breadTit">
+        <BreadTit>
+          <div className="breadTitRow active">藏品总账</div>
+        </BreadTit>
+      </div>
+      <div className="objectSonMain">
+        {/* 顶部搜索和筛选 */}
+        <div className="searchBox">
+          <div className="row">
+            <span>藏品编号名称:</span>
+            <Select
+              placeholder="请选择"
+              allowClear
+              style={{ width: 150 }}
+              value={tableSelect.dictNum}
+              onChange={handleChange}
+              options={options.map((v: any) => ({
+                label: v.name,
+                value: v.name,
+              }))}
+            />
+          </div>
+          <div className="row">
+            <span>搜索:</span>
+            <Select
+              style={{ width: 100 }}
+              value={value}
+              onChange={(val) => valueChangeFu(val)}
+            >
+              <Option value="name">藏品名称</Option>
+              <Option value="age">藏品年代</Option>
+              <Option value="texture">藏品质地</Option>
+            </Select>
+            <Input
+              maxLength={10}
+              style={{ width: 150 }}
+              placeholder="请输入"
+              allowClear
+              onChange={(e) => nameChange(e)}
+            />
+          </div>
+          <div className="row">
+            <Button onClick={deriveFu}>导出</Button>
+          </div>
+          <div className="row">
+            <div
+              onClick={() => cutShowFu("cutShow1")}
+              className={classNames(
+                "cutShow",
+                cutShow === "cutShow1" ? "active" : ""
+              )}
+            >
+              <AppstoreOutlined />
+            </div>
+            <div
+              onClick={() => cutShowFu("cutShow2")}
+              className={classNames(
+                "cutShow",
+                "cutShow2",
+                cutShow === "cutShow2" ? "active" : ""
+              )}
+            >
+              <UnorderedListOutlined />
+            </div>
+          </div>
+        </div>
+        {/* 图片展示列表 */}
+        <div className="imgList" hidden={cutShow !== "cutShow1"}>
+          {results.list.length ? (
+            results.list.map((v: any) => (
+              <div
+                title={v.name}
+                onClick={() =>
+                  history.push(
+                    `/object/2/look?k=${pageNumRef.current}&d=${cutShowRef.current}&id=${v.id}`
+                  )
+                }
+                key={v.id}
+                className="imgListRow"
+              >
+                <ImageLazy
+                  noLook={true}
+                  width={236}
+                  height={200}
+                  src={v.thumb}
+                />
+                <p>{v.name}</p>
+              </div>
+            ))
+          ) : (
+            <div className="imgListNone">
+              <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
+            </div>
+          )}
+          {}
+        </div>
+
+        {/* 表格展示列表 */}
+        <div className="tableBox" hidden={cutShow !== "cutShow2"}>
+          <Table
+            scroll={{ y: 450 }}
+            dataSource={results.list}
+            columns={columns}
+            rowKey="id"
+            pagination={false}
+          />
+        </div>
+
+        {/* 从表格里面拆出来的分页 */}
+        <div className="pageBox" hidden={!results.total}>
+          <Pagination
+            showQuickJumper
+            showSizeChanger
+            current={tableSelect.pageNum}
+            pageSize={tableSelect.pageSize}
+            onChange={paginationChange}
+            total={results.total}
+          />
+        </div>
+      </div>
+    </div>
+  );
+}

+ 15 - 0
src/pages/ObjectSon/Object3/AddObject3/GoodsAll.css

@@ -0,0 +1,15 @@
+.GoodsAllModel .ant-modal {
+  width: 1000px !important;
+}
+.GoodsAllModel .ObjectAddTit {
+  border-top: 1px solid #999999;
+  padding-top: 15px;
+  margin-bottom: 15px;
+}
+.GoodsAllModel .ObjectAddTit .topSearch {
+  display: flex;
+  align-items: center;
+}
+.GoodsAllModel .moveBtn {
+  text-align: center;
+}

+ 29 - 0
src/pages/ObjectSon/Object3/AddObject3/GoodsAll.less

@@ -0,0 +1,29 @@
+.GoodsAllModel {
+  .ant-modal {
+    width: 1000px !important;
+  }
+
+  // .ant-table-wrapper table {
+  //   min-height: 300px;
+  // }
+
+  // .ant-table-row {
+  //   content-visibility: auto;
+  //   contain-intrinsic-size: 200px;
+  // }
+
+  .ObjectAddTit {
+    border-top: 1px solid #999999;
+    padding-top: 15px;
+    margin-bottom: 15px;
+
+    .topSearch {
+      display: flex;
+      align-items: center;
+    }
+  }
+
+  .moveBtn {
+    text-align: center;
+  }
+}

+ 166 - 0
src/pages/ObjectSon/Object3/AddObject3/GoodsAll.tsx

@@ -0,0 +1,166 @@
+import { Button, Input, Modal, Table } from "antd";
+import React, {
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
+import styles from "./index.module.scss";
+import "./GoodsAll.css";
+import { getGoodsListAllAPI } from "@/store/action/object3";
+import { useDispatch, useSelector } from "react-redux";
+import { RootState } from "@/store";
+import ImageLazy from "@/components/ImageLazy";
+import { MessageFu } from "@/utils/message";
+
+type Props = {
+  colsePage: any;
+  addPage:boolean
+};
+function GoodsAll({ colsePage,addPage }: Props) {
+  const dispatch = useDispatch();
+
+  // 进页面获取列表
+  useEffect(() => {
+    dispatch(getGoodsListAllAPI(""));
+  }, [dispatch]);
+
+  // 藏品名称
+  const [name, setName] = useState("");
+  const nameTime = useRef(-1);
+  const nameChange = useCallback(
+    (e: React.ChangeEvent<HTMLInputElement>) => {
+      setName(e.target.value);
+      clearTimeout(nameTime.current);
+      nameTime.current = window.setTimeout(() => {
+        dispatch(getGoodsListAllAPI(e.target.value));
+      }, 500);
+    },
+    [dispatch]
+  );
+
+  // 有关表格
+  const results = useSelector(
+    (state: RootState) => state.object3Store.goodsAllList
+  );
+
+  // 已经选中的表格数据
+  const resultsAc = useSelector(
+    (state: RootState) => state.object3Store.goodsTableList
+  );
+
+  const columns = useMemo(() => {
+    return [
+      {
+        title: "缩略图",
+        render: (item: any) => (
+          <ImageLazy width={120} height={70} src={item.thumb} />
+        ),
+      },
+      {
+        title: "藏品编号名称",
+        dataIndex: "dictNum",
+      },
+      {
+        title: "藏品编号",
+        render: (item: any) => (item.num ? item.num : "-"),
+      },
+      {
+        title: "藏品名称",
+        dataIndex: "name",
+      },
+      {
+        title: "类别",
+        dataIndex: "dictGoodType",
+      },
+    ];
+  }, []);
+
+  // 选中的表格数据
+  const [tableSelectList, setTableSelectList] = useState([]);
+  // 表格的多选
+
+  const rowSelection = useMemo(() => {
+    return {
+      onChange: (selectedRowKeys: any, selectedRows: any) => {
+        setTableSelectList(selectedRows);
+      },
+      // 默认选中
+      defaultSelectedRowKeys: resultsAc.map((v: any) => v.id),
+    };
+  }, [resultsAc]);
+
+  // 点击确定
+  const btnOkFu = useCallback(() => {
+    dispatch({
+      type: "object3/getGoodsTableList",
+      payload: tableSelectList.map((v: any) => {
+        return {
+          ...v,
+          storageAncestor: "",
+          storageAncestorName: "",
+        };
+      }),
+    });
+    MessageFu.success("添加成功!");
+    colsePage();
+  }, [colsePage, dispatch, tableSelectList]);
+
+  return (
+    <div className={styles.GoodsAll}>
+      <Modal
+        open={addPage}
+        destroyOnClose
+        wrapClassName="GoodsAllModel"
+        title="添加藏品"
+        onCancel={colsePage}
+        footer={
+          [] // 设置footer为空,去掉 取消 确定默认按钮
+        }
+      >
+        <div className="ObjectAddTit">
+          <div className="topSearch">
+            <span>藏品名称:</span>
+            <Input
+              value={name}
+              maxLength={10}
+              style={{ width: 300 }}
+              placeholder="请输入"
+              allowClear
+              onChange={(e) => nameChange(e)}
+            />
+          </div>
+        </div>
+        <div className="tableBox">
+          <Table
+            size="small"
+            scroll={{ y: 500 }}
+            rowSelection={{
+              type: "checkbox",
+              ...rowSelection,
+            }}
+            dataSource={results}
+            columns={columns}
+            rowKey="id"
+            pagination={false}
+          />
+        </div>
+        <br />
+        <div className="moveBtn">
+          <Button
+            type="primary"
+            onClick={btnOkFu}
+            disabled={tableSelectList.length === 0}
+          >
+            提交
+          </Button>
+        </div>
+      </Modal>
+    </div>
+  );
+}
+
+const MemoGoodsAll = React.memo(GoodsAll);
+
+export default MemoGoodsAll;

+ 87 - 0
src/pages/ObjectSon/Object3/AddObject3/index.module.scss

@@ -0,0 +1,87 @@
+.AddObject3 {
+  :global {
+    .objectSonMain {
+      padding: 10px 30px;
+
+      .addInfoTop {
+        .row {
+          display: flex;
+          justify-content: space-between;
+          height: 50px;
+
+          &>div {
+            width: 45%;
+            height: 50px;
+            align-items: center;
+            display: flex;
+
+            &>span {
+              width: 80px;
+            }
+
+            .bs {
+              position: relative;
+
+              &::before {
+                content: '*';
+                position: absolute;
+                z-index: 10;
+                top: 2px;
+                left: -10px;
+                color: #ff4d4f;
+
+              }
+            }
+          }
+        }
+
+        .rowAll {
+          display: flex;
+          margin-top: 6px;
+
+          &>span {
+            width: 80px;
+          }
+
+          .ant-input-textarea {
+            width: calc(100% - 80px);
+          }
+        }
+      }
+
+      .addTableBox {
+        margin-top: 30px;
+        width: 100%;
+        height: 485px;
+
+        .addTableBox_Tit {
+          height: 40px;
+          display: flex;
+          align-items: center;
+          justify-content: space-between;
+
+          .addTableBox_TitL {
+            display: flex;
+            align-items: center;
+            color: var(--themeColor);
+            font-size: 20px;
+            font-weight: 700;
+          }
+        }
+
+        .addTableBox_table {
+          width: 100%;
+          height: calc(100% - 90px);
+        }
+
+        .addTableBox_btn {
+          width: 100%;
+          height: 40px;
+          display: flex;
+          justify-content: center;
+          margin-top: 10px;
+        }
+      }
+    }
+  }
+}

+ 352 - 0
src/pages/ObjectSon/Object3/AddObject3/index.tsx

@@ -0,0 +1,352 @@
+import BreadTit from "@/components/BreadTit";
+import { object3AddAPI, object3infoOutAPI } from "@/store/action/object3";
+import history, { urlParameter } from "@/utils/history";
+import { Button, Cascader, Input, Popconfirm, Table } from "antd";
+import TextArea from "antd/es/input/TextArea";
+import React, {
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
+import { useLocation } from "react-router-dom";
+import _ from "lodash";
+import styles from "./index.module.scss";
+import { useDispatch, useSelector } from "react-redux";
+import { RootState } from "@/store";
+import ImageLazy from "@/components/ImageLazy";
+import GoodsAll from "./GoodsAll";
+import LookModal from "@/components/LookObjTable/LookModal";
+import { getStores1ListAPI } from "@/store/action/stores1";
+import { obj3InStorage } from "@/utils/dataChange";
+import { MessageFu } from "@/utils/message";
+function AddObject3() {
+  const dispatch = useDispatch();
+
+  // 进页面获取所有的库房信息
+  useEffect(() => {
+    dispatch(getStores1ListAPI());
+  }, [dispatch]);
+
+  // 从仓库中获取库房总信息
+  const storesAllList = useSelector(
+    (state: RootState) => state.stores1Store.infoList
+  );
+
+  // 从仓库拿表格信息
+  const results = useSelector(
+    (state: RootState) => state.object3Store.goodsTableList
+  );
+
+  // 针对表格信息扩展自己需要的 入库 级连 筛选信息
+  const storesLocArr = useRef<any>({});
+
+  // 级联筛选发生变化
+  const onChangeStores = useCallback(
+    (value: any, arr: any, id: number) => {
+      storesLocArr.current[id] = {
+        ids: value.join(","),
+        names: arr.map((v: any) => v.name).join("/"),
+      };
+    },
+    [storesLocArr]
+  );
+
+  // 顶部数据
+  const [addInfoTop, setAddInfoTop] = useState<any>({});
+
+  // 进入页面新增请求函数
+  const object3AddAPIFu = useCallback(async () => {
+    const res = await object3AddAPI();
+    setAddInfoTop(res.data);
+    // 初始化表格数据
+    dispatch({
+      type: "object3/getGoodsTableList",
+      payload: [],
+    });
+  }, [dispatch]);
+
+  // 通过id获取详情函数
+  const object3infoOutAPIFu = useCallback(
+    async (id: number) => {
+      const res = await object3infoOutAPI(id);
+      setAddInfoTop(res.data.entity);
+      // 设置表格信息
+      dispatch({
+        type: "object3/getGoodsTableList",
+        payload: res.data.child,
+      });
+      // 设置藏品位置ref
+      if (res.data.child && res.data.child.length) {
+        res.data.child.forEach((v: any) => {
+          storesLocArr.current[v.id] = {
+            ids: v.storageAncestor,
+            names: obj3InStorage(v.storageAncestor),
+          };
+        });
+      }
+    },
+    [dispatch]
+  );
+
+  // 获取地址栏参数
+  const location = useLocation();
+  const [urlParam, setUrlParam] = useState<any>({});
+  useEffect(() => {
+    const obj = urlParameter(location.search);
+    setUrlParam(obj);
+
+    if (obj.id) {
+      // 如果是编辑
+      object3infoOutAPIFu(obj.id);
+    } else object3AddAPIFu();
+  }, [location, object3AddAPIFu, object3infoOutAPIFu]);
+  // 点击返回
+  const cancelFu = useCallback(() => {
+    history.push({
+      pathname: `/object/3`,
+      state: { k: urlParam.k ? urlParam.k : "1", d: urlParam.d },
+    });
+  }, [urlParam.d, urlParam.k]);
+
+  // 点击提交
+  const submitFu = useCallback(async () => {
+    if (results.length === 0)
+      return MessageFu.warning("至少需要添加一条藏品信息!");
+
+    const objLength = Object.keys(storesLocArr.current).length;
+    if (objLength < results.length) return MessageFu.warning("请选择藏品位置!");
+
+    const obj = {
+      description: addInfoTop.description,
+      id: addInfoTop.id,
+      goodsIds: results.map((v: any) => v.id).join(","),
+      location: storesLocArr.current,
+    };
+
+    const res: any = await object3AddAPI(obj);
+    if (res.code === 0) {
+      MessageFu.success("操作成功!");
+      cancelFu();
+    }
+  }, [addInfoTop.description, addInfoTop.id, cancelFu, results]);
+
+  // 点击添加或者编辑出来页面
+  const [addPage, setAddPage] = useState(false);
+
+  // 选中的表格数据
+  const [tableSelectList, setTableSelectList] = useState([]);
+
+  // 表格的勾选状态
+  const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
+
+  // 表格的多选
+  const rowSelection = {
+    selectedRowKeys,
+    onChange: (selectedRowKeys: any, selectedRows: any) => {
+      setTableSelectList(selectedRows);
+      setSelectedRowKeys(selectedRowKeys);
+    },
+  };
+
+  // 点击添加
+  const addPageFu = useCallback((id?: any) => {
+    setAddPage(true);
+  }, []);
+
+  // 点击删除
+  const delTableListFu = useCallback(() => {
+    console.log("多个删除", tableSelectList);
+    const data = _.differenceBy(results, tableSelectList, "id");
+    dispatch({ type: "object3/getGoodsTableList", payload: data });
+    setTableSelectList(data);
+
+    // 清除表格的勾选状态
+    setSelectedRowKeys([]);
+
+    // 同时删除藏品位置信息
+    tableSelectList.forEach((v: any) => {
+      delete storesLocArr.current[v.id];
+    });
+  }, [dispatch, results, tableSelectList]);
+
+  // 控制弹窗的显示隐藏
+  const [show, setShow] = useState(false);
+  // 点击表格里面的查看
+  const lookIdRef = useRef(-1);
+  const lookGoods = useCallback((id: number) => {
+    lookIdRef.current = id;
+    setShow(true);
+  }, []);
+
+  // 表格数据
+  const columns = useMemo(() => {
+    return [
+      {
+        title: "缩略图",
+        render: (item: any) => (
+          <ImageLazy width={120} height={70} src={item.thumb} />
+        ),
+      },
+
+      {
+        title: "藏品编号名称",
+        dataIndex: "dictNum",
+      },
+      {
+        title: "藏品编号",
+        render: (item: any) => (item.num ? item.num : "-"),
+      },
+      {
+        title: "藏品名称",
+        dataIndex: "name",
+      },
+      {
+        title: "类别",
+        dataIndex: "dictGoodType",
+      },
+      {
+        title: "完残程度",
+        dataIndex: "complete",
+      },
+      {
+        title: "藏品位置",
+        width: 200,
+        render: (item: any) => (
+          <>
+            <Cascader
+              defaultValue={obj3InStorage(item.storageAncestor).split(",")}
+              allowClear={false}
+              fieldNames={{
+                label: "name",
+                value: "id",
+                children: "children",
+              }}
+              onChange={(value, selectedOptions) =>
+                onChangeStores(value, selectedOptions, item.id)
+              }
+              options={storesAllList}
+              placeholder="请选择"
+            />
+          </>
+        ),
+      },
+      {
+        title: "操作",
+        render: (item: any) => (
+          <>
+            <Button type="text" danger onClick={() => lookGoods(item.id)}>
+              查看
+            </Button>
+          </>
+        ),
+      },
+    ];
+  }, [lookGoods, onChangeStores, storesAllList]);
+
+  return (
+    <div className={styles.AddObject3}>
+      <div className="breadTit">
+        <BreadTit>
+          <div className="breadTitRow">入库管理</div>
+          <div className="splitStr">/</div>
+          <div className="breadTitRow active">
+            {urlParam.id ? "编辑" : "新增"}
+          </div>
+        </BreadTit>
+      </div>
+      <div className="objectSonMain">
+        {/* 上面的信息展示 */}
+        <div className="addInfoTop">
+          <div className="row">
+            <div>
+              <span className="bs">入库编号:</span>
+              <Input style={{ width: 400 }} value={addInfoTop.num} disabled />
+            </div>
+            <div>
+              <span className="bs">登记人员:</span>
+              <Input
+                style={{ width: 400 }}
+                value={addInfoTop.creatorName}
+                disabled
+              />
+            </div>
+          </div>
+          <div className="rowAll">
+            <span>入库说明:</span>
+            <TextArea
+              value={addInfoTop.description}
+              onChange={(e) =>
+                setAddInfoTop({ ...addInfoTop, description: e.target.value })
+              }
+              rows={3}
+              placeholder="请输入"
+              showCount
+              maxLength={255}
+            />
+          </div>
+        </div>
+        {/* 下面的表格 */}
+        <div className="addTableBox">
+          <div className="addTableBox_Tit">
+            <div className="addTableBox_TitL">藏品信息</div>
+            <div className="addTableBox_TitR">
+              <Button onClick={() => addPageFu(null)}>添加</Button>
+              &emsp;
+              <Popconfirm
+                disabled={tableSelectList.length === 0}
+                title="确定删除吗?"
+                okText="确定"
+                cancelText="取消"
+                onConfirm={delTableListFu}
+              >
+                <Button disabled={tableSelectList.length === 0}>删除</Button>
+              </Popconfirm>
+            </div>
+          </div>
+
+          {/* 表格主体 */}
+          <div className="addTableBox_table">
+            <Table
+              size="small"
+              scroll={{ y: 355 }}
+              rowSelection={{
+                type: "checkbox",
+                ...rowSelection,
+              }}
+              dataSource={results}
+              columns={columns}
+              rowKey="id"
+              pagination={false}
+            />
+          </div>
+
+          {/* 返回按钮 */}
+          <div className="addTableBox_btn">
+            <Button type="primary" onClick={submitFu}>
+              提交
+            </Button>
+            &emsp;
+            <Button onClick={cancelFu}>返回</Button>
+          </div>
+        </div>
+      </div>
+      {/* 点击添加或者编辑出来的页面 */}
+      {addPage ? <GoodsAll addPage={addPage} colsePage={() => setAddPage(false)} /> : null}
+
+      {/* 点击查看出来的对话框 */}
+      {show ? (
+        <LookModal
+          id={lookIdRef.current}
+          show={show}
+          closeShow={() => setShow(false)}
+        />
+      ) : null}
+    </div>
+  );
+}
+
+const MemoAddObject3 = React.memo(AddObject3);
+
+export default MemoAddObject3;

+ 86 - 0
src/pages/ObjectSon/Object3/AuditObject3/index.module.scss

@@ -0,0 +1,86 @@
+.AuditObject3 {
+  :global {
+    .objectSonMain {
+      padding: 10px 30px;
+
+      .topTit {
+        font-size: 16px;
+        font-weight: 700;
+        color: var(--themeColor);
+        margin-bottom: 12px;
+      }
+
+      .topInfo {
+        .topInfoRow {
+          display: flex;
+
+          &>div {
+            width: 50%;
+            border: 1px solid #ccc;
+            height: 34px;
+            line-height: 32px;
+            display: flex;
+
+            .one {
+              font-weight: 700;
+              width: 80px;
+              text-align: right;
+            }
+          }
+        }
+
+        .topInfoTex {
+          cursor: pointer;
+          border: 1px solid #ccc;
+          padding: 5px 10px 4px;
+          display: -webkit-box;
+          overflow: hidden;
+          white-space: normal !important;
+          text-overflow: ellipsis;
+          word-wrap: break-word;
+          -webkit-line-clamp: 2;
+          -webkit-box-orient: vertical;
+
+          &>span {
+            font-weight: 700;
+          }
+        }
+
+      }
+
+      .goodsInfo {
+        .inputBox1 {
+          margin-bottom: 10px;
+          display: flex;
+          align-items: center;
+
+          .inputBoxTit {
+            font-weight: 700;
+            width: 90px;
+
+            &>span {
+              position: relative;
+              top: 3px;
+              color: #ff4d4f;
+            }
+          }
+
+          .inputBoxText {
+            width: calc(100% - 90px);
+          }
+        }
+
+        .inputBox2 {
+          align-items: flex-start;
+        }
+      }
+
+      .backBtn {
+        margin-top: 12px;
+        display: flex;
+        justify-content: center;
+      }
+
+    }
+  }
+}

+ 234 - 0
src/pages/ObjectSon/Object3/AuditObject3/index.tsx

@@ -0,0 +1,234 @@
+import BreadTit from "@/components/BreadTit";
+import ImageLazy from "@/components/ImageLazy";
+import LookModal from "@/components/LookObjTable/LookModal";
+import { RootState } from "@/store";
+import { auditObject3API, object3infoOutAPI } from "@/store/action/object3";
+import { obj3InStorage, statusObj } from "@/utils/dataChange";
+import history, { urlParameter } from "@/utils/history";
+import { MessageFu } from "@/utils/message";
+import { Button, Select, Table } from "antd";
+import TextArea from "antd/es/input/TextArea";
+import React, {
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
+import { useDispatch, useSelector } from "react-redux";
+import { useLocation } from "react-router-dom";
+import styles from "./index.module.scss";
+const { Option } = Select;
+
+function AuditObject3() {
+  const dispatch = useDispatch();
+
+  // 获取地址栏参数
+  const location = useLocation();
+  const urlParamRef = useRef<any>({});
+  useEffect(() => {
+    urlParamRef.current = urlParameter(location.search);
+    // console.log("地址栏参数", urlParamRef.current);
+  }, [location]);
+
+  const { info, list: tableList } = useSelector(
+    (state: RootState) => state.object3Store.lookInfo
+  );
+
+  // 审核结果筛选
+  const [value, setValue] = useState(3);
+  const valueChangeFu = (val: number) => {
+    setValue(val);
+  };
+  // 审核说明
+  const [value2, setValue2] = useState("");
+
+  const getInfo = useCallback(async () => {
+    const id = urlParamRef.current.id;
+    const res1 = await object3infoOutAPI(id);
+    const info = res1.data.entity;
+    const list = res1.data.child;
+    info.statusTxt = statusObj[info.status];
+    dispatch({ type: "object3/getLookInfo", payload: { info, list } });
+  }, [dispatch]);
+
+  useEffect(() => {
+    getInfo();
+  }, [getInfo]);
+
+  // 控制弹窗的显示隐藏
+  const [show, setShow] = useState(false);
+  // 点击表格里面的查看
+  const lookIdRef = useRef(-1);
+
+  const lookGoods = useCallback((id: number) => {
+    lookIdRef.current = id;
+    setShow(true);
+  }, []);
+
+  // 点击返回
+  const cancelFu = useCallback(() => {
+    history.push({
+      pathname: `/object/3`,
+      state: {
+        k: urlParamRef.current.k ? urlParamRef.current.k : "1",
+        d: urlParamRef.current.d,
+      },
+    });
+  }, []);
+
+  // 点击确定
+  const btnOkFu = useCallback(async () => {
+    const txt = value2.replaceAll(" ", "").replaceAll("\n", "");
+    if (txt === "") return MessageFu.warning("审核说明不能为空!");
+    const res: any = await auditObject3API({
+      id: Number(urlParamRef.current.id),
+      reason: value2,
+      status: value,
+    });
+    if (res.code === 0) {
+      MessageFu.success("操作成功!");
+      cancelFu();
+    }
+  }, [cancelFu, value, value2]);
+
+  // 表格格式
+  const columns = useMemo(() => {
+    const tempArr = [
+      {
+        title: "缩略图",
+        render: (item: any) => (
+          <ImageLazy width={120} height={70} src={item.thumb} />
+        ),
+      },
+      {
+        title: "藏品编号名称",
+        dataIndex: "dictNum",
+      },
+      {
+        title: "藏品编号",
+        render: (item: any) => (item.num ? item.num : "-"),
+      },
+      {
+        title: "藏品名称",
+        dataIndex: "name",
+      },
+      {
+        title: "类别",
+        dataIndex: "dictGoodType",
+      },
+      {
+        title: "完残程度",
+        dataIndex: "complete",
+      },
+      {
+        title: "藏品位置",
+        render:(item:any)=>obj3InStorage(item.storageAncestor)
+      },
+      {
+        title: "操作",
+        render: (item: any) => (
+          <>
+            <Button type="text" danger onClick={() => lookGoods(item.id)}>
+              查看
+            </Button>
+          </>
+        ),
+      },
+    ];
+
+    return tempArr;
+  }, [lookGoods]);
+
+  return (
+    <div className={styles.AuditObject3}>
+      <div className="breadTit">
+        <BreadTit>
+          <div className="breadTitRow">入库管理</div>
+          <div className="splitStr">/</div>
+          <div className="breadTitRow active">审核</div>
+        </BreadTit>
+      </div>
+      <div className="objectSonMain">
+        <div className="topTit">入库信息</div>
+        <div className="topInfo">
+          <div className="topInfoRow">
+            <div>
+              <div className="one">入库编号:</div>
+              <div>{info.num}</div>
+            </div>
+            <div>
+              <div className="one">登记人员:</div>
+              <div>{info.creatorName}</div>
+            </div>
+          </div>
+          <div className="topInfoTex" title={info.description}>
+            <span>入库说明:</span>
+            {info.description ? info.description : "-"}
+          </div>
+        </div>
+        <br />
+        <div className="topTit">藏品信息</div>
+        <div className="goodsInfo">
+          {/* 表格信息 */}
+          <Table
+            size="small"
+            scroll={{ y: 245 }}
+            dataSource={tableList}
+            columns={columns}
+            rowKey="id"
+            pagination={false}
+          />
+          <br />
+          <div className="inputBox1">
+            <div className="inputBoxTit">
+              <span>* </span>审核结果:
+            </div>
+            <Select
+              style={{ width: 150 }}
+              value={value}
+              onChange={(val) => valueChangeFu(val)}
+            >
+              <Option value={3}>通过</Option>
+              <Option value={2}>不通过</Option>
+            </Select>
+          </div>
+          <div className="inputBox1 inputBox2">
+            <div className="inputBoxTit">
+              <span>* </span>审核说明:
+            </div>
+            <div className="inputBoxText">
+              <TextArea
+                value={value2}
+                onChange={(e) => setValue2(e.target.value)}
+                rows={3}
+                placeholder="请输入"
+                showCount
+                maxLength={255}
+              />
+            </div>
+          </div>
+        </div>
+        <div className="backBtn">
+          <Button onClick={btnOkFu} type="primary">
+            提交
+          </Button>
+          &emsp;
+          <Button onClick={cancelFu}>返回</Button>
+        </div>
+      </div>
+      {/* 点击查看出来的对话框 */}
+      {show ? (
+        <LookModal
+          id={lookIdRef.current}
+          show={show}
+          closeShow={() => setShow(false)}
+        />
+      ) : null}
+    </div>
+  );
+}
+
+const MemoAuditObject3 = React.memo(AuditObject3);
+
+export default MemoAuditObject3;

+ 56 - 0
src/pages/ObjectSon/Object3/LookObject3/index.module.scss

@@ -0,0 +1,56 @@
+.LookObject3 {
+  :global {
+    .objectSonMain {
+      padding: 10px 30px;
+
+      .topTit {
+        font-size: 16px;
+        font-weight: 700;
+        color: var(--themeColor);
+        margin-bottom: 12px;
+      }
+
+      .topInfo {
+        .topInfoRow {
+          display: flex;
+          &>div {
+            width: 33.33%;
+            border: 1px solid #ccc;
+            height: 34px;
+            line-height: 32px;
+            display: flex;
+
+            .one {
+              font-weight: 700;
+              width: 80px;
+              text-align: right;
+            }
+          }
+        }
+
+        .topInfoTex {
+          cursor: pointer;
+          border: 1px solid #ccc;
+          padding: 5px 10px 4px;
+          display: -webkit-box;
+          overflow: hidden;
+          white-space: normal !important;
+          text-overflow: ellipsis;
+          word-wrap: break-word;
+          -webkit-line-clamp: 2;
+          -webkit-box-orient: vertical;
+
+          &>span {
+            font-weight: 700;
+          }
+        }
+
+      }
+      .backBtn{
+        margin-top: 12px;
+        display: flex;
+        justify-content: center;
+      }
+    }
+  }
+}

+ 202 - 0
src/pages/ObjectSon/Object3/LookObject3/index.tsx

@@ -0,0 +1,202 @@
+import AuthButton from "@/components/AuthButton";
+import BreadTit from "@/components/BreadTit";
+import ImageLazy from "@/components/ImageLazy";
+import LookModal from "@/components/LookObjTable/LookModal";
+import { RootState } from "@/store";
+import { object3infoOutAPI } from "@/store/action/object3";
+import { obj3InStorage, statusObj } from "@/utils/dataChange";
+import history, { urlParameter } from "@/utils/history";
+import { Button, Table } from "antd";
+import React, {
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
+import { useDispatch, useSelector } from "react-redux";
+import { useLocation } from "react-router-dom";
+import styles from "./index.module.scss";
+function LookObject3() {
+  const dispatch = useDispatch();
+
+  // 获取地址栏参数
+  const location = useLocation();
+  const urlParamRef = useRef<any>({});
+  useEffect(() => {
+    urlParamRef.current = urlParameter(location.search);
+    // console.log("地址栏参数", urlParamRef.current);
+  }, [location]);
+
+  const getInfo = useCallback(async () => {
+    const id = urlParamRef.current.id;
+    const res1 = await object3infoOutAPI(id);
+    const info = res1.data.entity;
+    const list = res1.data.child;
+    info.statusTxt = statusObj[info.status];
+    dispatch({ type: "object3/getLookInfo", payload: { info, list } });
+  }, [dispatch]);
+
+  useEffect(() => {
+    getInfo();
+  }, [getInfo]);
+
+  const { info, list: tableList } = useSelector(
+    (state: RootState) => state.object3Store.lookInfo
+  );
+
+  // 点击返回
+  const cancelFu = () => {
+    history.push({
+      pathname: `/object/3`,
+      state: {
+        k: urlParamRef.current.k ? urlParamRef.current.k : "1",
+        d: urlParamRef.current.d,
+      },
+    });
+  };
+
+  // 控制弹窗的显示隐藏
+  const [show, setShow] = useState(false);
+  // 点击表格里面的查看
+  const lookIdRef = useRef(-1);
+
+  const lookGoods = useCallback((id: number) => {
+    lookIdRef.current = id;
+    setShow(true);
+  }, []);
+
+  // 表格格式
+  const columns = useMemo(() => {
+    const tempArr = [
+      {
+        title: "缩略图",
+        render: (item: any) => (
+          <ImageLazy width={120} height={70} src={item.thumb} />
+        ),
+      },
+      {
+        title: "藏品编号名称",
+        dataIndex: "dictNum",
+      },
+      {
+        title: "藏品编号",
+        render: (item: any) => (item.num ? item.num : "-"),
+      },
+      {
+        title: "藏品名称",
+        dataIndex: "name",
+      },
+      {
+        title: "类别",
+        dataIndex: "dictGoodType",
+      },
+      {
+        title: "完残程度",
+        dataIndex: "complete",
+      },
+      {
+        title: "藏品位置",
+        render: (item: any) => obj3InStorage(item.storageAncestor),
+      },
+      {
+        title: "入库状态",
+        render: (item: any) =>
+          info.statusTxt === "已完成" ? "已入库" : "未入库",
+      },
+      {
+        title: "操作",
+        render: (item: any) => (
+          <>
+            <Button type="text" danger onClick={() => lookGoods(item.id)}>
+              查看
+            </Button>
+          </>
+        ),
+      },
+    ];
+
+    return tempArr;
+  }, [info.statusTxt, lookGoods]);
+
+  return (
+    <div className={styles.LookObject3}>
+      <div className="breadTit">
+        <BreadTit>
+          <div className="breadTitRow">入库管理</div>
+          <div className="splitStr">/</div>
+          <div className="breadTitRow active">查看</div>
+        </BreadTit>
+      </div>
+      <div className="objectSonMain">
+        <div className="topTit">入库信息</div>
+        <div className="topInfo">
+          <div className="topInfoRow">
+            <div>
+              <div className="one">入库编号:</div>
+              <div>{info.num}</div>
+            </div>
+            <div>
+              <div className="one">入库人员:</div>
+              <div>{info.creatorName}</div>
+            </div>
+            <div>
+              <div className="one">审核结果:</div>
+              <div>{info.statusTxt}</div>
+            </div>
+          </div>
+          <div className="topInfoTex" title={info.description}>
+            <span>入库说明:</span>
+            {info.description ? info.description : "-"}
+          </div>
+          <div className="topInfoTex" title={info.reason}>
+            <span>审核说明:</span>
+            {info.reason ? info.reason : "-"}
+          </div>
+        </div>
+        <br />
+        <div className="topTit">藏品信息</div>
+        <div className="goodsInfo">
+          {/* 表格信息 */}
+          <Table
+            size="small"
+            scroll={{ y: 360 }}
+            dataSource={tableList}
+            columns={columns}
+            rowKey="id"
+            pagination={false}
+          />
+        </div>
+        <div className="backBtn">
+          {info.status === 2 ? (
+            <AuthButton
+              id={302}
+              type="primary"
+              onClick={() =>
+                history.push(
+                  `/object/3/add?k=${urlParamRef.current.k}&d=${urlParamRef.current.d}&id=${urlParamRef.current.id}`
+                )
+              }
+            >
+              编辑
+            </AuthButton>
+          ) : null}
+          &emsp;
+          <Button onClick={cancelFu}>返回</Button>
+        </div>
+      </div>
+      {/* 点击查看出来的对话框 */}
+      {show ? (
+        <LookModal
+          id={lookIdRef.current}
+          show={show}
+          closeShow={() => setShow(false)}
+        />
+      ) : null}
+    </div>
+  );
+}
+
+const MemoLookObject3 = React.memo(LookObject3);
+
+export default MemoLookObject3;

+ 5 - 0
src/pages/ObjectSon/Object3/index.module.scss

@@ -0,0 +1,5 @@
+.Object3{
+  :global{
+    
+  }
+}

+ 316 - 0
src/pages/ObjectSon/Object3/index.tsx

@@ -0,0 +1,316 @@
+import BreadTit from "@/components/BreadTit";
+import classNames from "classnames";
+import { useEffect, useMemo, useRef, useState } from "react";
+import styles from "./index.module.scss";
+import { Input, DatePicker, Table, Button, Popconfirm } from "antd";
+import AuthButton from "@/components/AuthButton";
+import history from "@/utils/history";
+import { useLocation } from "react-router-dom";
+import { useDispatch, useSelector } from "react-redux";
+import { RootState } from "@/store";
+import {
+  getObject3List,
+  getObject3ListNum,
+  object3DelAPI,
+} from "@/store/action/object3";
+import { MessageFu } from "@/utils/message";
+
+const { RangePicker } = DatePicker;
+
+export default function Object3() {
+  const dispatch = useDispatch();
+
+  // 获取顶部数量
+
+  useEffect(() => {
+    dispatch(getObject3ListNum());
+  }, [dispatch]);
+
+  // 顶部的状态改变了,统一管理,传到二级页码
+  const statusRef = useRef<null | number>(null);
+
+  const dataTit = useSelector(
+    (state: RootState) => state.object3Store.infoNum3
+  );
+
+  // 封装发送请求的函数
+  const getList = () => {
+    const data = {
+      ...tableSelect,
+      pageNum: pageNumRef.current,
+      status: statusRef.current,
+    };
+    dispatch(getObject3List(data));
+  };
+
+  // 获取地址栏参数
+  const location = useLocation();
+
+  const pageNumRef = useRef(1);
+
+  // 如果有参数 根据参数页码在次发送请求
+  useEffect(() => {
+    const urlParam = location.state || {};
+    setTableSelect({
+      ...tableSelect,
+      pageNum: Number(urlParam.k) ? Number(urlParam.k) : 1,
+      // eslint-disable-next-line eqeqeq
+      status: urlParam.d && urlParam.d != "null" ? Number(urlParam.d) : null,
+    });
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, [location]);
+
+  // 顶部筛选
+  const [tableSelect, setTableSelect] = useState({
+    status: null as null | number,
+    searchKey: "",
+    startTime: "",
+    endTime: "",
+    pageSize: 10,
+    pageNum: 1,
+  });
+
+  // 当前页码统一
+  useEffect(() => {
+    pageNumRef.current = tableSelect.pageNum;
+  }, [tableSelect.pageNum]);
+
+  // 顶部状态统一
+  useEffect(() => {
+    statusRef.current = tableSelect.status;
+  }, [tableSelect.status]);
+
+  // 防止返回的时候发送了2次请求来对应页码
+
+  const getListRef = useRef(-1);
+
+  useEffect(() => {
+    clearTimeout(getListRef.current);
+    getListRef.current = window.setTimeout(() => {
+      getList();
+    }, 100);
+
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, [tableSelect]);
+
+  // 登记人员输入
+  const nameTime = useRef(-1);
+  const nameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+    clearTimeout(nameTime.current);
+    nameTime.current = window.setTimeout(() => {
+      setTableSelect({ ...tableSelect, searchKey: e.target.value, pageNum: 1 });
+    }, 500);
+  };
+
+  // 时间选择器改变
+  const timeChange = (date: any, dateString: any) => {
+    let startTime = "";
+    let endTime = "";
+    if (dateString[0] && dateString[1]) {
+      startTime = dateString[0] + " 00:00:00";
+      endTime = dateString[1] + " 23:59:59";
+    }
+    setTableSelect({ ...tableSelect, startTime, endTime, pageNum: 1 });
+  };
+
+  // 点击新增或者编辑按钮
+  const addObject = (id?: any) => {
+    if (id)
+      history.push(
+        `/object/3/add?k=${pageNumRef.current}&d=${statusRef.current}&id=${id}`
+      );
+    else
+      history.push(
+        `/object/3/add?k=${pageNumRef.current}&d=${statusRef.current}`
+      );
+  };
+
+  // 点击删除按钮
+  const delOne = async (id: number) => {
+    const res: any = await object3DelAPI(id);
+    if (res.code === 0) {
+      MessageFu.success("删除成功!");
+      getList();
+      dispatch(getObject3ListNum());
+    }
+  };
+
+  // ---------关于表格
+
+  // 页码变化
+  const paginationChange = (pageNum: number, pageSize: number) => {
+    setTableSelect({ ...tableSelect, pageNum, pageSize });
+  };
+
+  const results = useSelector((state: RootState) => state.object3Store.info3);
+
+  const columns = useMemo(() => {
+    return [
+      {
+        title: "入库编号",
+        dataIndex: "num",
+      },
+      {
+        title: "登记人员",
+        dataIndex: "creatorName",
+      },
+      {
+        title: "入库说明",
+        render: (item: any) =>
+          item.description ? (
+            item.description.length >= 50 ? (
+              <span style={{ cursor: "pointer" }} title={item.description}>
+                {item.description.substring(0, 20) + "..."}
+              </span>
+            ) : (
+              item.description
+            )
+          ) : (
+            "-"
+          ),
+      },
+      {
+        title: "创建日期",
+        dataIndex: "createTime",
+      },
+      {
+        title: "完成日期",
+        render: (item: any) => (item.day && item.status === 3 ? item.day : "-"),
+      },
+      {
+        title: "状态",
+        dataIndex: "statusTxt",
+      },
+
+      {
+        title: "操作",
+        render: (item: any) => (
+          <>
+            <Button
+              type="text"
+              danger
+              onClick={() =>
+                history.push(
+                  `/object/3/look?k=${pageNumRef.current}&d=${statusRef.current}&id=${item.id}`
+                )
+              }
+            >
+              查看
+            </Button>
+
+            {item.status === 0 || item.status === 2 ? (
+              <AuthButton
+                id={302}
+                type="text"
+                danger
+                onClick={() => addObject(item.id)}
+              >
+                编辑
+              </AuthButton>
+            ) : null}
+
+            {item.status === 1 ? (
+              <AuthButton
+                id={305}
+                onClick={() =>
+                  history.push(
+                    `/object/3/audit?k=${pageNumRef.current}&d=${statusRef.current}&id=${item.id}`
+                  )
+                }
+                type="text"
+                danger
+              >
+                审核
+              </AuthButton>
+            ) : null}
+
+            {item.status === 0 || item.status === 2 ? (
+              <Popconfirm
+                title="确定删除吗?"
+                okText="确定"
+                cancelText="取消"
+                onConfirm={() => delOne(item.id)}
+              >
+                <AuthButton id={303} type="text" danger>
+                  删除
+                </AuthButton>
+              </Popconfirm>
+            ) : null}
+          </>
+        ),
+      },
+    ];
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, []);
+
+  return (
+    <div className={styles.Object1}>
+      <div className="breadTit">
+        <BreadTit>
+          <div className="breadTitRow active">入库管理</div>
+        </BreadTit>
+      </div>
+      <div className="objectSonMain">
+        {/* 顶部筛选 */}
+        <div className="objectSonMainTit">
+          {dataTit.map((v: any) => (
+            <div
+              key={v.id}
+              onClick={() =>
+                setTableSelect({ ...tableSelect, status: v.id, pageNum: 1 })
+              }
+              className={classNames(
+                v.id === tableSelect.status ? "active" : ""
+              )}
+            >
+              {v.name}({v.num})
+            </div>
+          ))}
+        </div>
+        <div className="objectSonMainTable">
+          {/* 表格数据筛选 */}
+          <div className="tableSelectBox">
+            <div className="row">
+              <span>登记人员:</span>
+              <Input
+                maxLength={10}
+                style={{ width: 150 }}
+                placeholder="请输入"
+                allowClear
+                onChange={(e) => nameChange(e)}
+              />
+            </div>
+            <div className="row">
+              <span>创建日期:</span>
+              <RangePicker onChange={timeChange} />
+            </div>
+            <div className="row">
+              <AuthButton id={302} type="primary" onClick={() => addObject()}>
+                申请入库
+              </AuthButton>
+            </div>
+          </div>
+
+          {/* 表格主体 */}
+          <div className="tableMain">
+            <Table
+              scroll={{ y: 428 }}
+              dataSource={results.list}
+              columns={columns}
+              rowKey="id"
+              pagination={{
+                showQuickJumper: true,
+                position: ["bottomCenter"],
+                showSizeChanger: true,
+                current: tableSelect.pageNum,
+                pageSize: tableSelect.pageSize,
+                total: results.total,
+                onChange: paginationChange,
+              }}
+            />
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+}

+ 165 - 0
src/pages/ObjectSon/Object4/AddObject4/GoodsAll.tsx

@@ -0,0 +1,165 @@
+import { Button, Input, Modal, Table } from "antd";
+import React, {
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
+import styles from "./index.module.scss";
+import { getGoodsListAllAPI } from "@/store/action/object4";
+import { useDispatch, useSelector } from "react-redux";
+import { RootState } from "@/store";
+import ImageLazy from "@/components/ImageLazy";
+import "../../Object3/AddObject3/GoodsAll.css";
+import { MessageFu } from "@/utils/message";
+
+type Props = {
+  colsePage: any;
+  addPage: boolean;
+};
+function GoodsAll({ colsePage, addPage }: Props) {
+  const dispatch = useDispatch();
+
+  // 进页面获取列表
+  useEffect(() => {
+    dispatch(getGoodsListAllAPI(""));
+  }, [dispatch]);
+
+  // 藏品名称
+  const [name, setName] = useState("");
+  const nameTime = useRef(-1);
+  const nameChange = useCallback(
+    (e: React.ChangeEvent<HTMLInputElement>) => {
+      setName(e.target.value);
+      clearTimeout(nameTime.current);
+      nameTime.current = window.setTimeout(() => {
+        dispatch(getGoodsListAllAPI(e.target.value));
+      }, 500);
+    },
+    [dispatch]
+  );
+
+  // 有关表格
+  const results = useSelector(
+    (state: RootState) => state.object4Store.goodsAllList
+  );
+
+  // 已经选中的表格数据
+  const resultsAc = useSelector(
+    (state: RootState) => state.object4Store.goodsTableList
+  );
+
+  const columns = useMemo(() => {
+    return [
+      {
+        title: "缩略图",
+        render: (item: any) => (
+          <ImageLazy width={120} height={70} src={item.thumb} />
+        ),
+      },
+      {
+        title: "藏品编号名称",
+        dataIndex: "dictNum",
+      },
+      {
+        title: "藏品编号",
+        render: (item: any) => (item.num ? item.num : "-"),
+      },
+      {
+        title: "藏品名称",
+        dataIndex: "name",
+      },
+      {
+        title: "类别",
+        dataIndex: "dictGoodType",
+      },
+    ];
+  }, []);
+
+  // 选中的表格数据
+  const [tableSelectList, setTableSelectList] = useState([]);
+  // 表格的多选
+
+  const rowSelection = useMemo(() => {
+    return {
+      onChange: (selectedRowKeys: any, selectedRows: any) => {
+        setTableSelectList(selectedRows);
+      },
+      // 默认选中
+      defaultSelectedRowKeys: resultsAc.map((v: any) => v.id),
+    };
+  }, [resultsAc]);
+
+  // 点击确定
+  const btnOkFu = useCallback(() => {
+    dispatch({
+      type: "object4/getGoodsTableList",
+      payload: tableSelectList.map((v: any) => {
+        return {
+          ...v,
+          outLocation: "",
+        };
+      }),
+    });
+    MessageFu.success("添加成功!");
+    colsePage();
+  }, [colsePage, dispatch, tableSelectList]);
+
+  return (
+    <div className={styles.GoodsAll}>
+      <Modal
+        open={addPage}
+        destroyOnClose
+        wrapClassName="GoodsAllModel"
+        title="添加藏品"
+        onCancel={colsePage}
+        footer={
+          [] // 设置footer为空,去掉 取消 确定默认按钮
+        }
+      >
+        <div className="ObjectAddTit">
+          <div className="topSearch">
+            <span>藏品名称:</span>
+            <Input
+              value={name}
+              maxLength={10}
+              style={{ width: 300 }}
+              placeholder="请输入"
+              allowClear
+              onChange={(e) => nameChange(e)}
+            />
+          </div>
+        </div>
+        <div className="tableBox">
+          <Table
+            size="small"
+            scroll={{ y: 500 }}
+            rowSelection={{
+              type: "checkbox",
+              ...rowSelection,
+            }}
+            dataSource={results}
+            columns={columns}
+            rowKey="id"
+            pagination={false}
+          />
+        </div>
+        <br />
+        <div className="moveBtn">
+          <Button
+            type="primary"
+            onClick={btnOkFu}
+            disabled={tableSelectList.length === 0}
+          >
+            提交
+          </Button>
+        </div>
+      </Modal>
+    </div>
+  );
+}
+
+const MemoGoodsAll = React.memo(GoodsAll);
+
+export default MemoGoodsAll;

+ 87 - 0
src/pages/ObjectSon/Object4/AddObject4/index.module.scss

@@ -0,0 +1,87 @@
+.AddObject4 {
+  :global {
+    .objectSonMain {
+      padding: 10px 30px;
+
+      .addInfoTop {
+        .row {
+          display: flex;
+          justify-content: space-between;
+          height: 50px;
+
+          &>div {
+            width: 33.33%;
+            height: 50px;
+            align-items: center;
+            display: flex;
+
+            &>span {
+              width: 80px;
+            }
+
+            .bs {
+              position: relative;
+
+              &::before {
+                content: '*';
+                position: absolute;
+                z-index: 10;
+                top: 2px;
+                left: -10px;
+                color: #ff4d4f;
+
+              }
+            }
+          }
+        }
+
+        .rowAll {
+          display: flex;
+          margin-top: 6px;
+
+          &>span {
+            width: 80px;
+          }
+
+          .ant-input-textarea {
+            width: calc(100% - 80px);
+          }
+        }
+      }
+
+      .addTableBox {
+        margin-top: 30px;
+        width: 100%;
+        height: 485px;
+
+        .addTableBox_Tit {
+          height: 40px;
+          display: flex;
+          align-items: center;
+          justify-content: space-between;
+
+          .addTableBox_TitL {
+            display: flex;
+            align-items: center;
+            color: var(--themeColor);
+            font-size: 20px;
+            font-weight: 700;
+          }
+        }
+
+        .addTableBox_table {
+          width: 100%;
+          height: calc(100% - 90px);
+        }
+
+        .addTableBox_btn {
+          width: 100%;
+          height: 40px;
+          display: flex;
+          justify-content: center;
+          margin-top: 10px;
+        }
+      }
+    }
+  }
+}

+ 365 - 0
src/pages/ObjectSon/Object4/AddObject4/index.tsx

@@ -0,0 +1,365 @@
+import BreadTit from "@/components/BreadTit";
+import { object4AddAPI, object4infoOutAPI } from "@/store/action/object4";
+import history, { urlParameter } from "@/utils/history";
+import { Button, Input, Popconfirm, Select, Table } from "antd";
+import TextArea from "antd/es/input/TextArea";
+import React, {
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
+import { useLocation } from "react-router-dom";
+import _ from "lodash";
+import styles from "./index.module.scss";
+import { useDispatch, useSelector } from "react-redux";
+import { RootState } from "@/store";
+import ImageLazy from "@/components/ImageLazy";
+import GoodsAll from "./GoodsAll";
+import LookModal from "@/components/LookObjTable/LookModal";
+import { MessageFu } from "@/utils/message";
+function AddObject4() {
+  const dispatch = useDispatch();
+
+  // 从仓库拿表格信息
+  const results = useSelector(
+    (state: RootState) => state.object4Store.goodsTableList
+  );
+
+  // 表格里面的输入框信息
+  const inputRef = useRef(-1);
+  const inputInfoRef = useRef<any>({});
+  const inputChangeFu = useCallback(
+    (e: React.ChangeEvent<HTMLInputElement>, id: number) => {
+      clearTimeout(inputRef.current);
+      inputRef.current = window.setTimeout(() => {
+        if (e.target.value.trim() === "") return;
+        inputInfoRef.current[id] = {
+          names: e.target.value.trim(),
+        };
+        console.log(inputInfoRef.current);
+      }, 500);
+    },
+    []
+  );
+
+  // 顶部数据
+  const [addInfoTop, setAddInfoTop] = useState<any>({
+    outType: "展览",
+  });
+
+  // 进入页面新增请求函数
+  const object4AddAPIFu = useCallback(async () => {
+    const res = await object4AddAPI();
+    setAddInfoTop({ ...res.data, outType: "展览" });
+    // 初始化表格数据
+    dispatch({
+      type: "object4/getGoodsTableList",
+      payload: [],
+    });
+  }, [dispatch]);
+
+  // 通过id获取详情函数
+  const object4infoOutAPIFu = useCallback(
+    async (id: number) => {
+      const res = await object4infoOutAPI(id);
+      setAddInfoTop(res.data.entity);
+      // 设置表格信息
+      dispatch({
+        type: "object4/getGoodsTableList",
+        payload: res.data.child,
+      });
+      // 设置出库位置ref
+      if (res.data.child && res.data.child.length) {
+        res.data.child.forEach((v: any) => {
+          inputInfoRef.current[v.id] = {
+            names: v.outLocation,
+          };
+        });
+      }
+    },
+    [dispatch]
+  );
+
+  // 获取地址栏参数
+  const location = useLocation();
+  const [urlParam, setUrlParam] = useState<any>({});
+  useEffect(() => {
+    const obj = urlParameter(location.search);
+    setUrlParam(obj);
+
+    if (obj.id) {
+      // 如果是编辑
+      object4infoOutAPIFu(obj.id);
+    } else object4AddAPIFu();
+  }, [location, object4AddAPIFu, object4infoOutAPIFu]);
+  // 点击返回
+  const cancelFu = useCallback(() => {
+    history.push({
+      pathname: `/object/4`,
+      state: { k: urlParam.k ? urlParam.k : "1", d: urlParam.d },
+    });
+  }, [urlParam.d, urlParam.k]);
+
+  // 点击提交
+  const submitFu = useCallback(async () => {
+    if (results.length === 0)
+      return MessageFu.warning("至少需要添加一条藏品信息!");
+
+    const objLength = Object.keys(inputInfoRef.current).length;
+    if (objLength < results.length) return MessageFu.warning("请输入出库位置!");
+
+    const obj = {
+      description: addInfoTop.description,
+      id: addInfoTop.id,
+      outType: addInfoTop.outType,
+      goodsIds: results.map((v: any) => v.id).join(","),
+      location: inputInfoRef.current,
+    };
+
+    const res: any = await object4AddAPI(obj);
+    if (res.code === 0) {
+      MessageFu.success("操作成功!");
+      cancelFu();
+    }
+  }, [
+    addInfoTop.description,
+    addInfoTop.id,
+    addInfoTop.outType,
+    cancelFu,
+    results,
+  ]);
+
+  // 点击添加或者编辑出来页面
+  const [addPage, setAddPage] = useState(false);
+
+  // 选中的表格数据
+  const [tableSelectList, setTableSelectList] = useState([]);
+
+  // 表格的勾选状态
+  const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
+
+  // 表格的多选
+  const rowSelection = {
+    selectedRowKeys,
+    onChange: (selectedRowKeys: any, selectedRows: any) => {
+      setTableSelectList(selectedRows);
+      setSelectedRowKeys(selectedRowKeys);
+    },
+  };
+
+  // 点击添加
+  const addPageFu = useCallback((id?: any) => {
+    setAddPage(true);
+  }, []);
+
+  // 点击删除
+  const delTableListFu = useCallback(() => {
+    console.log("多个删除", tableSelectList);
+    const data = _.differenceBy(results, tableSelectList, "id");
+    dispatch({ type: "object4/getGoodsTableList", payload: data });
+    setTableSelectList(data);
+
+    // 清除表格的勾选状态
+    setSelectedRowKeys([]);
+
+    // 同时删除出库位置信息
+    tableSelectList.forEach((v: any) => {
+      delete inputInfoRef.current[v.id];
+    });
+  }, [dispatch, results, tableSelectList]);
+
+  // 控制弹窗的显示隐藏
+  const [show, setShow] = useState(false);
+  // 点击表格里面的查看
+  const lookIdRef = useRef(-1);
+  const lookGoods = useCallback((id: number) => {
+    lookIdRef.current = id;
+    setShow(true);
+  }, []);
+
+  // 表格数据
+  const columns = useMemo(() => {
+    return [
+      {
+        title: "缩略图",
+        render: (item: any) => (
+          <ImageLazy width={120} height={70} src={item.thumb} />
+        ),
+      },
+
+      {
+        title: "藏品编号名称",
+        dataIndex: "dictNum",
+      },
+      {
+        title: "藏品编号",
+        render: (item: any) => (item.num ? item.num : "-"),
+      },
+      {
+        title: "藏品名称",
+        dataIndex: "name",
+      },
+      {
+        title: "类别",
+        dataIndex: "dictGoodType",
+      },
+      {
+        title: "完残程度",
+        dataIndex: "complete",
+      },
+      {
+        title: "出库位置",
+        width: 200,
+        render: (item: any) => (
+          <Input
+            defaultValue={item.outLocation}
+            onChange={(e) => inputChangeFu(e, item.id)}
+            maxLength={15}
+            showCount
+            placeholder="请输入"
+          />
+        ),
+      },
+      {
+        title: "操作",
+        render: (item: any) => (
+          <>
+            <Button type="text" danger onClick={() => lookGoods(item.id)}>
+              查看
+            </Button>
+          </>
+        ),
+      },
+    ];
+  }, [inputChangeFu, lookGoods]);
+
+  // 出库类型下拉框变化
+
+  const options = useSelector(
+    (state: RootState) => state.object4Store.typeSelectList
+  );
+
+  const typeChangeFu = useCallback(
+    (value: string) => {
+      setAddInfoTop({ ...addInfoTop, outType: value });
+    },
+    [addInfoTop]
+  );
+
+  return (
+    <div className={styles.AddObject4}>
+      <div className="breadTit">
+        <BreadTit>
+          <div className="breadTitRow">出库管理</div>
+          <div className="splitStr">/</div>
+          <div className="breadTitRow active">
+            {urlParam.id ? "编辑" : "新增"}
+          </div>
+        </BreadTit>
+      </div>
+      <div className="objectSonMain">
+        {/* 上面的信息展示 */}
+        <div className="addInfoTop">
+          <div className="row">
+            <div>
+              <span className="bs">出库编号:</span>
+              <Input style={{ width: 300 }} value={addInfoTop.num} disabled />
+            </div>
+            <div>
+              <span className="bs">登记人员:</span>
+              <Input
+                style={{ width: 300 }}
+                value={addInfoTop.creatorName}
+                disabled
+              />
+            </div>
+            <div>
+              <span className="bs">出库类型:</span>
+              <Select
+                style={{ width: 300 }}
+                placeholder="请选择"
+                value={addInfoTop.outType}
+                onChange={typeChangeFu}
+                options={options}
+              />
+            </div>
+          </div>
+          <div className="rowAll">
+            <span>出库说明:</span>
+            <TextArea
+              value={addInfoTop.description}
+              onChange={(e) =>
+                setAddInfoTop({ ...addInfoTop, description: e.target.value })
+              }
+              rows={3}
+              placeholder="请输入"
+              showCount
+              maxLength={255}
+            />
+          </div>
+        </div>
+        {/* 下面的表格 */}
+        <div className="addTableBox">
+          <div className="addTableBox_Tit">
+            <div className="addTableBox_TitL">藏品信息</div>
+            <div className="addTableBox_TitR">
+              <Button onClick={() => addPageFu(null)}>添加</Button>
+              &emsp;
+              <Popconfirm
+                disabled={tableSelectList.length === 0}
+                title="确定删除吗?"
+                okText="确定"
+                cancelText="取消"
+                onConfirm={delTableListFu}
+              >
+                <Button disabled={tableSelectList.length === 0}>删除</Button>
+              </Popconfirm>
+            </div>
+          </div>
+
+          {/* 表格主体 */}
+          <div className="addTableBox_table">
+            <Table
+              size="small"
+              scroll={{ y: 355 }}
+              rowSelection={{
+                type: "checkbox",
+                ...rowSelection,
+              }}
+              dataSource={results}
+              columns={columns}
+              rowKey="id"
+              pagination={false}
+            />
+          </div>
+
+          {/* 返回按钮 */}
+          <div className="addTableBox_btn">
+            <Button type="primary" onClick={submitFu}>
+              提交
+            </Button>
+            &emsp;
+            <Button onClick={cancelFu}>返回</Button>
+          </div>
+        </div>
+      </div>
+      {/* 点击添加或者编辑出来的页面 */}
+      {addPage ? <GoodsAll addPage={addPage} colsePage={() => setAddPage(false)} /> : null}
+
+      {/* 点击查看出来的对话框 */}
+      {show ? (
+        <LookModal
+          id={lookIdRef.current}
+          show={show}
+          closeShow={() => setShow(false)}
+        />
+      ) : null}
+    </div>
+  );
+}
+
+const MemoAddObject4 = React.memo(AddObject4);
+
+export default MemoAddObject4;

+ 86 - 0
src/pages/ObjectSon/Object4/AuditObject4/index.module.scss

@@ -0,0 +1,86 @@
+.AuditObject4 {
+  :global {
+    .objectSonMain {
+      padding: 10px 30px;
+
+      .topTit {
+        font-size: 16px;
+        font-weight: 700;
+        color: var(--themeColor);
+        margin-bottom: 12px;
+      }
+
+      .topInfo {
+        .topInfoRow {
+          display: flex;
+
+          &>div {
+            width: 33.33%;
+            border: 1px solid #ccc;
+            height: 34px;
+            line-height: 32px;
+            display: flex;
+
+            .one {
+              font-weight: 700;
+              width: 80px;
+              text-align: right;
+            }
+          }
+        }
+
+        .topInfoTex {
+          cursor: pointer;
+          border: 1px solid #ccc;
+          padding: 5px 10px 4px;
+          display: -webkit-box;
+          overflow: hidden;
+          white-space: normal !important;
+          text-overflow: ellipsis;
+          word-wrap: break-word;
+          -webkit-line-clamp: 2;
+          -webkit-box-orient: vertical;
+
+          &>span {
+            font-weight: 700;
+          }
+        }
+
+      }
+
+      .goodsInfo {
+        .inputBox1 {
+          margin-bottom: 10px;
+          display: flex;
+          align-items: center;
+
+          .inputBoxTit {
+            font-weight: 700;
+            width: 90px;
+
+            &>span {
+              position: relative;
+              top: 3px;
+              color: #ff4d4f;
+            }
+          }
+
+          .inputBoxText {
+            width: calc(100% - 90px);
+          }
+        }
+
+        .inputBox2 {
+          align-items: flex-start;
+        }
+      }
+
+      .backBtn {
+        margin-top: 12px;
+        display: flex;
+        justify-content: center;
+      }
+
+    }
+  }
+}

+ 238 - 0
src/pages/ObjectSon/Object4/AuditObject4/index.tsx

@@ -0,0 +1,238 @@
+import BreadTit from "@/components/BreadTit";
+import ImageLazy from "@/components/ImageLazy";
+import LookModal from "@/components/LookObjTable/LookModal";
+import { RootState } from "@/store";
+import { auditObject4API, object4infoOutAPI } from "@/store/action/object4";
+import { statusObj } from "@/utils/dataChange";
+import history, { urlParameter } from "@/utils/history";
+import { MessageFu } from "@/utils/message";
+import { Button, Select, Table } from "antd";
+import TextArea from "antd/es/input/TextArea";
+import React, {
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
+import { useDispatch, useSelector } from "react-redux";
+import { useLocation } from "react-router-dom";
+import styles from "./index.module.scss";
+const { Option } = Select;
+
+function AuditObject4() {
+  const dispatch = useDispatch();
+
+  // 获取地址栏参数
+  const location = useLocation();
+  const urlParamRef = useRef<any>({});
+  useEffect(() => {
+    urlParamRef.current = urlParameter(location.search);
+    // console.log("地址栏参数", urlParamRef.current);
+  }, [location]);
+
+  const { info, list: tableList } = useSelector(
+    (state: RootState) => state.object4Store.lookInfo
+  );
+
+  // 审核结果筛选
+  const [value, setValue] = useState(3);
+  const valueChangeFu = (val: number) => {
+    setValue(val);
+  };
+  // 审核说明
+  const [value2, setValue2] = useState("");
+
+  const getInfo = useCallback(async () => {
+    const id = urlParamRef.current.id;
+    const res1 = await object4infoOutAPI(id);
+    const info = res1.data.entity;
+    const list = res1.data.child;
+    info.statusTxt = statusObj[info.status];
+    dispatch({ type: "object4/getLookInfo", payload: { info, list } });
+  }, [dispatch]);
+
+  useEffect(() => {
+    getInfo();
+  }, [getInfo]);
+
+  // 控制弹窗的显示隐藏
+  const [show, setShow] = useState(false);
+  // 点击表格里面的查看
+  const lookIdRef = useRef(-1);
+
+  const lookGoods = useCallback((id: number) => {
+    lookIdRef.current = id;
+    setShow(true);
+  }, []);
+
+  // 点击返回
+  const cancelFu = useCallback(() => {
+    history.push({
+      pathname: `/object/4`,
+      state: {
+        k: urlParamRef.current.k ? urlParamRef.current.k : "1",
+        d: urlParamRef.current.d,
+      },
+    });
+  }, []);
+
+  // 点击确定
+  const btnOkFu = useCallback(async () => {
+    const txt = value2.replaceAll(" ", "").replaceAll("\n", "");
+    if (txt === "") return MessageFu.warning("审核说明不能为空!");
+    const res: any = await auditObject4API({
+      id: Number(urlParamRef.current.id),
+      reason: value2,
+      status: value,
+    });
+    if (res.code === 0) {
+      MessageFu.success("操作成功!");
+      cancelFu();
+    }
+  }, [cancelFu, value, value2]);
+
+  // 表格格式
+  const columns = useMemo(() => {
+    const tempArr = [
+      {
+        title: "缩略图",
+        render: (item: any) => (
+          <ImageLazy width={120} height={70} src={item.thumb} />
+        ),
+      },
+      {
+        title: "藏品编号名称",
+        dataIndex: "dictNum",
+      },
+      {
+        title: "藏品编号",
+        render: (item: any) => (item.num ? item.num : "-"),
+      },
+      {
+        title: "藏品名称",
+        dataIndex: "name",
+      },
+      {
+        title: "类别",
+        dataIndex: "dictGoodType",
+      },
+      {
+        title: "完残程度",
+        dataIndex: "complete",
+      },
+      {
+        title: "出库位置",
+        dataIndex: "outLocation",
+      },
+      {
+        title: "操作",
+        render: (item: any) => (
+          <>
+            <Button type="text" danger onClick={() => lookGoods(item.id)}>
+              查看
+            </Button>
+          </>
+        ),
+      },
+    ];
+
+    return tempArr;
+  }, [lookGoods]);
+
+  return (
+    <div className={styles.AuditObject4}>
+      <div className="breadTit">
+        <BreadTit>
+          <div className="breadTitRow">出库管理</div>
+          <div className="splitStr">/</div>
+          <div className="breadTitRow active">审核</div>
+        </BreadTit>
+      </div>
+      <div className="objectSonMain">
+        <div className="topTit">出库信息</div>
+        <div className="topInfo">
+          <div className="topInfoRow">
+            <div>
+              <div className="one">出库编号:</div>
+              <div>{info.num}</div>
+            </div>
+            <div>
+              <div className="one">出库类型:</div>
+              <div>{info.outType}</div>
+            </div>
+            <div>
+              <div className="one">登记人员:</div>
+              <div>{info.creatorName}</div>
+            </div>
+          </div>
+          <div className="topInfoTex" title={info.description}>
+            <span>出库说明:</span>
+            {info.description ? info.description : "-"}
+          </div>
+        </div>
+        <br />
+        <div className="topTit">藏品信息</div>
+        <div className="goodsInfo">
+          {/* 表格信息 */}
+          <Table
+            size="small"
+            scroll={{ y: 245 }}
+            dataSource={tableList}
+            columns={columns}
+            rowKey="id"
+            pagination={false}
+          />
+          <br />
+          <div className="inputBox1">
+            <div className="inputBoxTit">
+              <span>* </span>审核结果:
+            </div>
+            <Select
+              style={{ width: 150 }}
+              value={value}
+              onChange={(val) => valueChangeFu(val)}
+            >
+              <Option value={3}>通过</Option>
+              <Option value={2}>不通过</Option>
+            </Select>
+          </div>
+          <div className="inputBox1 inputBox2">
+            <div className="inputBoxTit">
+              <span>* </span>审核说明:
+            </div>
+            <div className="inputBoxText">
+              <TextArea
+                value={value2}
+                onChange={(e) => setValue2(e.target.value)}
+                rows={3}
+                placeholder="请输入"
+                showCount
+                maxLength={255}
+              />
+            </div>
+          </div>
+        </div>
+        <div className="backBtn">
+          <Button onClick={btnOkFu} type="primary">
+            提交
+          </Button>
+          &emsp;
+          <Button onClick={cancelFu}>返回</Button>
+        </div>
+      </div>
+      {/* 点击查看出来的对话框 */}
+      {show ? (
+        <LookModal
+          id={lookIdRef.current}
+          show={show}
+          closeShow={() => setShow(false)}
+        />
+      ) : null}
+    </div>
+  );
+}
+
+const MemoAuditObject4 = React.memo(AuditObject4);
+
+export default MemoAuditObject4;

+ 70 - 0
src/pages/ObjectSon/Object4/LookObject4/index.module.scss

@@ -0,0 +1,70 @@
+.LookObject4 {
+  :global {
+    .objectSonMain {
+      padding: 10px 30px;
+
+      .topTit {
+        font-size: 16px;
+        font-weight: 700;
+        color: var(--themeColor);
+        margin-bottom: 12px;
+        position: relative;
+
+      }
+
+      .topTit2 {
+        margin-bottom: 18px;
+
+        .titBtn {
+          position: absolute;
+          right: 5px;
+          top: 0px;
+        }
+      }
+
+      .topInfo {
+        .topInfoRow {
+          display: flex;
+
+          &>div {
+            width: 25%;
+            border: 1px solid #ccc;
+            height: 34px;
+            line-height: 32px;
+            display: flex;
+
+            .one {
+              font-weight: 700;
+              width: 80px;
+              text-align: right;
+            }
+          }
+        }
+
+        .topInfoTex {
+          cursor: pointer;
+          border: 1px solid #ccc;
+          padding: 5px 10px 4px;
+          display: -webkit-box;
+          overflow: hidden;
+          white-space: normal !important;
+          text-overflow: ellipsis;
+          word-wrap: break-word;
+          -webkit-line-clamp: 2;
+          -webkit-box-orient: vertical;
+
+          &>span {
+            font-weight: 700;
+          }
+        }
+
+      }
+
+      .backBtn {
+        margin-top: 12px;
+        display: flex;
+        justify-content: center;
+      }
+    }
+  }
+}

+ 264 - 0
src/pages/ObjectSon/Object4/LookObject4/index.tsx

@@ -0,0 +1,264 @@
+import AuthButton from "@/components/AuthButton";
+import BreadTit from "@/components/BreadTit";
+import ImageLazy from "@/components/ImageLazy";
+import LookModal from "@/components/LookObjTable/LookModal";
+import { RootState } from "@/store";
+import { object4infoOutAPI, returnObject4API } from "@/store/action/object4";
+import { statusObjCK2 } from "@/utils/dataChange";
+import history, { urlParameter } from "@/utils/history";
+import { MessageFu } from "@/utils/message";
+import { Button, Popconfirm, Table } from "antd";
+import React, {
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
+import { useDispatch, useSelector } from "react-redux";
+import { useLocation } from "react-router-dom";
+import styles from "./index.module.scss";
+function LookObject4() {
+  const dispatch = useDispatch();
+
+  // 获取地址栏参数
+  const location = useLocation();
+  const urlParamRef = useRef<any>({});
+  useEffect(() => {
+    urlParamRef.current = urlParameter(location.search);
+    // console.log("地址栏参数", urlParamRef.current);
+  }, [location]);
+
+  const getInfo = useCallback(async () => {
+    const id = urlParamRef.current.id;
+    const res1 = await object4infoOutAPI(id);
+    const info = res1.data.entity;
+    const list = res1.data.child;
+    info.statusTxt = statusObjCK2[info.status];
+    dispatch({ type: "object4/getLookInfo", payload: { info, list } });
+  }, [dispatch]);
+
+  useEffect(() => {
+    getInfo();
+  }, [getInfo]);
+
+  const { info, list: tableList } = useSelector(
+    (state: RootState) => state.object4Store.lookInfo
+  );
+
+  // 点击返回
+  const cancelFu = () => {
+    history.push({
+      pathname: `/object/4`,
+      state: {
+        k: urlParamRef.current.k ? urlParamRef.current.k : "1",
+        d: urlParamRef.current.d,
+      },
+    });
+  };
+
+  // 控制弹窗的显示隐藏
+  const [show, setShow] = useState(false);
+  // 点击表格里面的查看
+  const lookIdRef = useRef(-1);
+
+  const lookGoods = useCallback((id: number) => {
+    lookIdRef.current = id;
+    setShow(true);
+  }, []);
+
+  // 表格格式
+  const columns = useMemo(() => {
+    const tempArr = [
+      {
+        title: "缩略图",
+        render: (item: any) => (
+          <ImageLazy width={120} height={70} src={item.thumb} />
+        ),
+      },
+      {
+        title: "藏品编号名称",
+        dataIndex: "dictNum",
+      },
+      {
+        title: "藏品编号",
+        render: (item: any) => (item.num ? item.num : "-"),
+      },
+      {
+        title: "藏品名称",
+        dataIndex: "name",
+      },
+      {
+        title: "类别",
+        dataIndex: "dictGoodType",
+      },
+      {
+        title: "完残程度",
+        dataIndex: "complete",
+      },
+      {
+        title: "出库位置",
+        dataIndex: "outLocation",
+      },
+
+      {
+        title: "出库状态",
+        render: (item: any) =>
+          item.storageStatus === "out"
+            ? "待归还"
+            : item.storageStatus === "in"
+            ? "已归还"
+            : "-",
+      },
+      {
+        title: "操作",
+        render: (item: any) => (
+          <>
+            <Button type="text" danger onClick={() => lookGoods(item.id)}>
+              查看
+            </Button>
+          </>
+        ),
+      },
+    ];
+
+    return tempArr;
+  }, [lookGoods]);
+
+  // 选中的表格数据
+  const [tableSelectList, setTableSelectList] = useState([]);
+  // 表格的多选和禁选
+  const rowSelection = {
+    selectedRowKeys: tableSelectList,
+    onChange: (selectedRowKeys: any) => {
+      setTableSelectList(selectedRowKeys);
+    },
+    getCheckboxProps: (record: any) => ({
+      disabled: record.storageStatus === "in" || info.status !== 3, // 已归还的禁止选中
+    }),
+  };
+
+  // 点击归还
+  const returnGoodsFu = useCallback(async () => {
+    console.log("---------点击了归还", tableSelectList);
+    const obj = {
+      goodsIds: tableSelectList.join(","),
+      id: info.id,
+    };
+    const res: any = await returnObject4API(obj);
+    if (res.code === 0) {
+      MessageFu.success("归还成功!");
+      // 清空表格选中状态和数据
+      setTableSelectList([]);
+      getInfo();
+    }
+  }, [getInfo, info.id, tableSelectList]);
+
+  return (
+    <div className={styles.LookObject4}>
+      <div className="breadTit">
+        <BreadTit>
+          <div className="breadTitRow">出库管理</div>
+          <div className="splitStr">/</div>
+          <div className="breadTitRow active">
+            {info.status === 3 ? "查看-归还" : "查看"}
+          </div>
+        </BreadTit>
+      </div>
+      <div className="objectSonMain">
+        <div className="topTit">出库信息</div>
+        <div className="topInfo">
+          <div className="topInfoRow">
+            <div>
+              <div className="one">出库编号:</div>
+              <div>{info.num}</div>
+            </div>
+            <div>
+              <div className="one">出库人员:</div>
+              <div>{info.creatorName}</div>
+            </div>
+            <div>
+              <div className="one">出库类型:</div>
+              <div>{info.outType}</div>
+            </div>
+            <div>
+              <div className="one">审核结果:</div>
+              <div>{info.statusTxt}</div>
+            </div>
+          </div>
+          <div className="topInfoTex" title={info.description}>
+            <span>登记说明:</span>
+            {info.description ? info.description : "-"}
+          </div>
+          <div className="topInfoTex" title={info.reason}>
+            <span>审核说明:</span>
+            {info.reason ? info.reason : "-"}
+          </div>
+        </div>
+        <br />
+        <div className="topTit topTit2">
+          藏品信息
+          {info.status === 3 ? (
+            <div className="titBtn">
+              <Popconfirm
+                disabled={tableSelectList.length === 0}
+                title="确定归还吗?"
+                okText="确定"
+                cancelText="取消"
+                onConfirm={returnGoodsFu}
+              >
+                <AuthButton id={406} disabled={tableSelectList.length === 0}>
+                  归还
+                </AuthButton>
+              </Popconfirm>
+            </div>
+          ) : null}
+        </div>
+        <div className="goodsInfo">
+          {/* 表格信息 */}
+          <Table
+            size="small"
+            scroll={{ y: 360 }}
+            rowSelection={{
+              type: "checkbox",
+              ...rowSelection,
+            }}
+            dataSource={tableList}
+            columns={columns}
+            rowKey="id"
+            pagination={false}
+          />
+        </div>
+        <div className="backBtn">
+          {info.status === 2 ? (
+            <AuthButton
+              id={402}
+              type="primary"
+              onClick={() =>
+                history.push(
+                  `/object/4/add?k=${urlParamRef.current.k}&d=${urlParamRef.current.d}&id=${urlParamRef.current.id}`
+                )
+              }
+            >
+              编辑
+            </AuthButton>
+          ) : null}
+          &emsp;
+          <Button onClick={cancelFu}>返回</Button>
+        </div>
+      </div>
+      {/* 点击查看出来的对话框 */}
+      {show ? (
+        <LookModal
+          id={lookIdRef.current}
+          show={show}
+          closeShow={() => setShow(false)}
+        />
+      ) : null}
+    </div>
+  );
+}
+
+const MemoLookObject4 = React.memo(LookObject4);
+
+export default MemoLookObject4;

+ 5 - 0
src/pages/ObjectSon/Object4/index.module.scss

@@ -0,0 +1,5 @@
+.Object4{
+  :global{
+    
+  }
+}

+ 316 - 0
src/pages/ObjectSon/Object4/index.tsx

@@ -0,0 +1,316 @@
+import BreadTit from "@/components/BreadTit";
+import classNames from "classnames";
+import { useEffect, useMemo, useRef, useState } from "react";
+import styles from "./index.module.scss";
+import { Input, DatePicker, Table, Button, Popconfirm } from "antd";
+import AuthButton from "@/components/AuthButton";
+import history from "@/utils/history";
+import { useLocation } from "react-router-dom";
+import { useDispatch, useSelector } from "react-redux";
+import { RootState } from "@/store";
+import {
+  getObject4List,
+  getObject4ListNum,
+  object4DelAPI,
+} from "@/store/action/object4";
+import { MessageFu } from "@/utils/message";
+
+const { RangePicker } = DatePicker;
+
+export default function Object4() {
+  const dispatch = useDispatch();
+
+  // 获取顶部数量
+
+  useEffect(() => {
+    dispatch(getObject4ListNum());
+  }, [dispatch]);
+
+  // 顶部的状态改变了,统一管理,传到二级页码
+  const statusRef = useRef<null | number>(null);
+
+  const dataTit = useSelector(
+    (state: RootState) => state.object4Store.infoNum4
+  );
+
+  // 封装发送请求的函数
+  const getList = () => {
+    const data = {
+      ...tableSelect,
+      pageNum: pageNumRef.current,
+      status: statusRef.current,
+    };
+    dispatch(getObject4List(data));
+  };
+
+  // 获取地址栏参数
+  const location = useLocation();
+
+  const pageNumRef = useRef(1);
+
+  // 如果有参数 根据参数页码在次发送请求
+  useEffect(() => {
+    const urlParam = location.state || {};
+    setTableSelect({
+      ...tableSelect,
+      pageNum: Number(urlParam.k) ? Number(urlParam.k) : 1,
+      // eslint-disable-next-line eqeqeq
+      status: urlParam.d && urlParam.d != "null" ? Number(urlParam.d) : null,
+    });
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, [location]);
+
+  // 顶部筛选
+  const [tableSelect, setTableSelect] = useState({
+    status: null as null | number,
+    searchKey: "",
+    startTime: "",
+    endTime: "",
+    pageSize: 10,
+    pageNum: 1,
+  });
+
+  // 当前页码统一
+  useEffect(() => {
+    pageNumRef.current = tableSelect.pageNum;
+  }, [tableSelect.pageNum]);
+
+  // 顶部状态统一
+  useEffect(() => {
+    statusRef.current = tableSelect.status;
+  }, [tableSelect.status]);
+
+  // 防止返回的时候发送了2次请求来对应页码
+
+  const getListRef = useRef(-1);
+
+  useEffect(() => {
+    clearTimeout(getListRef.current);
+    getListRef.current = window.setTimeout(() => {
+      getList();
+    }, 100);
+
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, [tableSelect]);
+
+  // 登记人员输入
+  const nameTime = useRef(-1);
+  const nameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+    clearTimeout(nameTime.current);
+    nameTime.current = window.setTimeout(() => {
+      setTableSelect({ ...tableSelect, searchKey: e.target.value, pageNum: 1 });
+    }, 500);
+  };
+
+  // 时间选择器改变
+  const timeChange = (date: any, dateString: any) => {
+    let startTime = "";
+    let endTime = "";
+    if (dateString[0] && dateString[1]) {
+      startTime = dateString[0] + " 00:00:00";
+      endTime = dateString[1] + " 23:59:59";
+    }
+    setTableSelect({ ...tableSelect, startTime, endTime, pageNum: 1 });
+  };
+
+  // 点击新增或者编辑按钮
+  const addObject = (id?: any) => {
+    if (id)
+      history.push(
+        `/object/4/add?k=${pageNumRef.current}&d=${statusRef.current}&id=${id}`
+      );
+    else
+      history.push(
+        `/object/4/add?k=${pageNumRef.current}&d=${statusRef.current}`
+      );
+  };
+
+  // 点击删除按钮
+  const delOne = async (id: number) => {
+    const res: any = await object4DelAPI(id);
+    if (res.code === 0) {
+      MessageFu.success("删除成功!");
+      getList();
+      dispatch(getObject4ListNum());
+    }
+  };
+
+  // ---------关于表格
+
+  // 页码变化
+  const paginationChange = (pageNum: number, pageSize: number) => {
+    setTableSelect({ ...tableSelect, pageNum, pageSize });
+  };
+
+  const results = useSelector((state: RootState) => state.object4Store.info4);
+
+  const columns = useMemo(() => {
+    return [
+      {
+        title: "出库编号",
+        dataIndex: "num",
+      },
+      {
+        title: "登记人员",
+        dataIndex: "creatorName",
+      },
+      {
+        title: "出库说明",
+        render: (item: any) =>
+          item.description ? (
+            item.description.length >= 50 ? (
+              <span style={{ cursor: "pointer" }} title={item.description}>
+                {item.description.substring(0, 20) + "..."}
+              </span>
+            ) : (
+              item.description
+            )
+          ) : (
+            "-"
+          ),
+      },
+      {
+        title: "登记日期",
+        dataIndex: "createTime",
+      },
+      {
+        title: "出库日期",
+        render: (item: any) => (item.day && item.status === 3 ? item.day : "-"),
+      },
+      {
+        title: "状态",
+        dataIndex: "statusTxt",
+      },
+
+      {
+        title: "操作",
+        render: (item: any) => (
+          <>
+            <Button
+              type="text"
+              danger
+              onClick={() =>
+                history.push(
+                  `/object/4/look?k=${pageNumRef.current}&d=${statusRef.current}&id=${item.id}`
+                )
+              }
+            >
+              {item.status === 3 ? "查看/归还" : "查看"}
+            </Button>
+
+            {item.status === 0 || item.status === 2 ? (
+              <AuthButton
+                id={402}
+                type="text"
+                danger
+                onClick={() => addObject(item.id)}
+              >
+                编辑
+              </AuthButton>
+            ) : null}
+
+            {item.status === 1 ? (
+              <AuthButton
+                id={405}
+                onClick={() =>
+                  history.push(
+                    `/object/4/audit?k=${pageNumRef.current}&d=${statusRef.current}&id=${item.id}`
+                  )
+                }
+                type="text"
+                danger
+              >
+                审核
+              </AuthButton>
+            ) : null}
+
+            {item.status === 0 || item.status === 2 ? (
+              <Popconfirm
+                title="确定删除吗?"
+                okText="确定"
+                cancelText="取消"
+                onConfirm={() => delOne(item.id)}
+              >
+                <AuthButton id={403} type="text" danger>
+                  删除
+                </AuthButton>
+              </Popconfirm>
+            ) : null}
+          </>
+        ),
+      },
+    ];
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, []);
+
+  return (
+    <div className={styles.Object1}>
+      <div className="breadTit">
+        <BreadTit>
+          <div className="breadTitRow active">出库管理</div>
+        </BreadTit>
+      </div>
+      <div className="objectSonMain">
+        {/* 顶部筛选 */}
+        <div className="objectSonMainTit">
+          {dataTit.map((v: any) => (
+            <div
+              key={v.id}
+              onClick={() =>
+                setTableSelect({ ...tableSelect, status: v.id, pageNum: 1 })
+              }
+              className={classNames(
+                v.id === tableSelect.status ? "active" : ""
+              )}
+            >
+              {v.name}({v.num})
+            </div>
+          ))}
+        </div>
+        <div className="objectSonMainTable">
+          {/* 表格数据筛选 */}
+          <div className="tableSelectBox">
+            <div className="row">
+              <span>登记人员:</span>
+              <Input
+                maxLength={10}
+                style={{ width: 150 }}
+                placeholder="请输入"
+                allowClear
+                onChange={(e) => nameChange(e)}
+              />
+            </div>
+            <div className="row">
+              <span>创建日期:</span>
+              <RangePicker onChange={timeChange} />
+            </div>
+            <div className="row">
+              <AuthButton id={402} type="primary" onClick={() => addObject()}>
+                申请出库
+              </AuthButton>
+            </div>
+          </div>
+
+          {/* 表格主体 */}
+          <div className="tableMain">
+            <Table
+              scroll={{ y: 428 }}
+              dataSource={results.list}
+              columns={columns}
+              rowKey="id"
+              pagination={{
+                showQuickJumper: true,
+                position: ["bottomCenter"],
+                showSizeChanger: true,
+                current: tableSelect.pageNum,
+                pageSize: tableSelect.pageSize,
+                total: results.total,
+                onChange: paginationChange,
+              }}
+            />
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+}

+ 96 - 0
src/pages/ObjectSon/Object5/AuditObject5/index.module.scss

@@ -0,0 +1,96 @@
+.AuditObject5 {
+  :global {
+    .objectSonMain {
+      padding: 10px 30px;
+
+      .topTit {
+        font-size: 16px;
+        font-weight: 700;
+        color: var(--themeColor);
+        margin-bottom: 12px;
+      }
+      .topTit2 {
+        position: relative;
+        margin-bottom: 18px;
+
+        .titBtn {
+          position: absolute;
+          right: 5px;
+          top: 0px;
+        }
+      }
+
+      .topInfo {
+        .topInfoRow {
+          display: flex;
+
+          &>div {
+            width: 50%;
+            border: 1px solid #ccc;
+            height: 34px;
+            line-height: 32px;
+            display: flex;
+
+            .one {
+              font-weight: 700;
+              width: 80px;
+              text-align: right;
+            }
+          }
+        }
+
+        .topInfoTex {
+          cursor: pointer;
+          border: 1px solid #ccc;
+          padding: 5px 10px 4px;
+          display: -webkit-box;
+          overflow: hidden;
+          white-space: normal !important;
+          text-overflow: ellipsis;
+          word-wrap: break-word;
+          -webkit-line-clamp: 2;
+          -webkit-box-orient: vertical;
+
+          &>span {
+            font-weight: 700;
+          }
+        }
+
+      }
+
+      .goodsInfo {
+        .inputBox1 {
+          margin-bottom: 10px;
+          display: flex;
+          align-items: center;
+
+          .inputBoxTit {
+            font-weight: 700;
+            width: 90px;
+
+            &>span {
+              position: relative;
+              top: 3px;
+              color: #ff4d4f;
+            }
+          }
+
+          .inputBoxText {
+            width: calc(100% - 90px);
+          }
+        }
+
+        .inputBox2 {
+          align-items: flex-start;
+        }
+      }
+
+      .backBtn {
+        margin-top: 12px;
+        display: flex;
+        justify-content: center;
+      }
+
+    }
+  }
+}

+ 270 - 0
src/pages/ObjectSon/Object5/AuditObject5/index.tsx

@@ -0,0 +1,270 @@
+import BreadTit from "@/components/BreadTit";
+import ImageLazy from "@/components/ImageLazy";
+import LookModal from "@/components/LookObjTable/LookModal";
+import { RootState } from "@/store";
+import { auditObject5API, object5infoOutAPI } from "@/store/action/object5";
+import { goodsChangeObj, statusObj } from "@/utils/dataChange";
+import history, { urlParameter } from "@/utils/history";
+import { MessageFu } from "@/utils/message";
+import { Button, Select, Table } from "antd";
+import TextArea from "antd/es/input/TextArea";
+import React, {
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
+import { useDispatch, useSelector } from "react-redux";
+import { useLocation } from "react-router-dom";
+import styles from "./index.module.scss";
+const { Option } = Select;
+
+function AuditObject5() {
+  const dispatch = useDispatch();
+
+  // 获取地址栏参数
+  const location = useLocation();
+  const urlParamRef = useRef<any>({});
+  useEffect(() => {
+    urlParamRef.current = urlParameter(location.search);
+    // console.log("地址栏参数", urlParamRef.current);
+  }, [location]);
+
+  const { info, list: tableList } = useSelector(
+    (state: RootState) => state.object5Store.lookInfo
+  );
+
+  // 审核结果筛选
+  const [value, setValue] = useState(3);
+  const valueChangeFu = (val: number) => {
+    setValue(val);
+  };
+  // 审核说明
+  const [value2, setValue2] = useState("");
+
+  const getInfo = useCallback(async () => {
+    const id = urlParamRef.current.id;
+    const res = await object5infoOutAPI(id);
+    const info = res.data;
+
+    // 表格信息的整理
+    const list = [] as any;
+    const oldData = JSON.parse(res.data.before_json);
+    const newData = JSON.parse(res.data.after_json);
+    console.log(123, oldData, newData);
+
+    for (const k in newData) {
+      if (k !== "fileIds" && k !== "fileIdsName") {
+        if (k === "size") {
+          const oldValArr = oldData[k].split(",");
+          const newValArr = oldData[k].split(",");
+          list.push({
+            label: goodsChangeObj[k],
+            oldVal: `通长:${oldValArr[0]}cm,通宽:${oldValArr[1]}cm,通高:${oldValArr[2]}cm`,
+            newVal: `通长:${newValArr[0]}cm,通宽:${newValArr[1]}cm,通高:${newValArr[2]}cm`,
+          });
+        } else if (k === "display") {
+          list.push({
+            label: goodsChangeObj[k],
+            oldVal: oldData[k] === 1 ? "是" : "否",
+            newVal: newData[k] === 1 ? "是" : "否",
+          });
+        } else {
+          list.push({
+            label: goodsChangeObj[k],
+            oldVal: oldData[k],
+            newVal: newData[k],
+          });
+        }
+      } else if (k === "fileIdsName") {
+        list.push({
+          label: "附件信息",
+          oldVal: oldData[k],
+          newVal: newData[k],
+        });
+      }
+    }
+    info.statusTxt = statusObj[info.status];
+    dispatch({ type: "object5/getLookInfo", payload: { info, list } });
+  }, [dispatch]);
+
+  useEffect(() => {
+    getInfo();
+  }, [getInfo]);
+
+  // 控制弹窗的显示隐藏
+  const [show, setShow] = useState(false);
+  // 点击表格里面的查看
+  const lookIdRef = useRef(-1);
+
+  const lookGoods = useCallback((id: number) => {
+    lookIdRef.current = id;
+    setShow(true);
+  }, []);
+
+  // 点击返回
+  const cancelFu = useCallback(() => {
+    history.push({
+      pathname: `/object/5`,
+      state: {
+        k: urlParamRef.current.k ? urlParamRef.current.k : "1",
+        d: urlParamRef.current.d,
+      },
+    });
+  }, []);
+
+  // 点击确定
+  const btnOkFu = useCallback(async () => {
+    const txt = value2.replaceAll(" ", "").replaceAll("\n", "");
+    if (txt === "") return MessageFu.warning("审核说明不能为空!");
+    const res: any = await auditObject5API({
+      id: Number(urlParamRef.current.id),
+      reason: value2,
+      status: value,
+    });
+    if (res.code === 0) {
+      MessageFu.success("操作成功!");
+      cancelFu();
+    }
+  }, [cancelFu, value, value2]);
+
+  // 表格格式
+  const columns = useMemo(() => {
+    const tempArr = [
+      {
+        title: "修改内容",
+        dataIndex: "label",
+      },
+      {
+        title: "修改前",
+        render: (item: any) => {
+          return item.label === "藏品图片" && item.oldVal ? (
+            <ImageLazy width={120} height={70} src={item.oldVal} />
+          ) : item.oldVal ? (
+            item.oldVal
+          ) : (
+            "-"
+          );
+        },
+      },
+      {
+        title: "修改后",
+        render: (item: any) => {
+          return item.label === "藏品图片" && item.newVal ? (
+            <ImageLazy width={120} height={70} src={item.newVal} />
+          ) : item.newVal ? (
+            item.newVal
+          ) : (
+            "-"
+          );
+        },
+      },
+    ];
+
+    return tempArr;
+  }, []);
+
+  return (
+    <div className={styles.AuditObject5}>
+      <div className="breadTit">
+        <BreadTit>
+          <div className="breadTitRow">藏品修改</div>
+          <div className="splitStr">/</div>
+          <div className="breadTitRow active">审核</div>
+        </BreadTit>
+      </div>
+      <div className="objectSonMain">
+        <div className="topTit">修改信息</div>
+        <div className="topInfo">
+          <div className="topInfoRow">
+            <div>
+              <div className="one">藏品名称:</div>
+              <div>{info.name}</div>
+            </div>
+            <div>
+              <div className="one">藏品编号:</div>
+              <div>{info.dictNum}</div>
+            </div>
+          </div>
+          <div className="topInfoRow">
+            <div>
+              <div className="one">登记人员:</div>
+              <div>{info.creatorName}</div>
+            </div>
+            <div>
+              <div className="one">创建日期:</div>
+              <div>{info.create_time}</div>
+            </div>
+          </div>
+        </div>
+        <br />
+        <div className="topTit topTit2">
+          修改记录
+          <div className="titBtn">
+            <Button onClick={() => lookGoods(info.goods_ids)}>查看</Button>
+          </div>
+        </div>
+        <div className="goodsInfo">
+          {/* 表格信息 */}
+          <Table
+            size="small"
+            scroll={{ y: 245 }}
+            dataSource={tableList}
+            columns={columns}
+            rowKey="label"
+            pagination={false}
+          />
+          <br />
+          <div className="inputBox1">
+            <div className="inputBoxTit">
+              <span>* </span>审核结果:
+            </div>
+            <Select
+              style={{ width: 150 }}
+              value={value}
+              onChange={(val) => valueChangeFu(val)}
+            >
+              <Option value={3}>通过</Option>
+              <Option value={2}>不通过</Option>
+            </Select>
+          </div>
+          <div className="inputBox1 inputBox2">
+            <div className="inputBoxTit">
+              <span>* </span>审核说明:
+            </div>
+            <div className="inputBoxText">
+              <TextArea
+                value={value2}
+                onChange={(e) => setValue2(e.target.value)}
+                rows={3}
+                placeholder="请输入"
+                showCount
+                maxLength={255}
+              />
+            </div>
+          </div>
+        </div>
+        <div className="backBtn">
+          <Button onClick={btnOkFu} type="primary">
+            提交
+          </Button>
+          &emsp;
+          <Button onClick={cancelFu}>返回</Button>
+        </div>
+      </div>
+      {/* 点击查看出来的对话框 */}
+      {show ? (
+        <LookModal
+          id={lookIdRef.current}
+          show={show}
+          closeShow={() => setShow(false)}
+        />
+      ) : null}
+    </div>
+  );
+}
+
+const MemoAuditObject5 = React.memo(AuditObject5);
+
+export default MemoAuditObject5;

+ 96 - 0
src/pages/ObjectSon/Object5/LookObject5/index.module.scss

@@ -0,0 +1,96 @@
+.LookObject5 {
+  :global {
+    .objectSonMain {
+      padding: 10px 30px;
+
+      .topTit {
+        font-size: 16px;
+        font-weight: 700;
+        color: var(--themeColor);
+        margin-bottom: 12px;
+      }
+      .topTit2 {
+        position: relative;
+        margin-bottom: 18px;
+
+        .titBtn {
+          position: absolute;
+          right: 5px;
+          top: 0px;
+        }
+      }
+
+      .topInfo {
+        .topInfoRow {
+          display: flex;
+
+          &>div {
+            width: 50%;
+            border: 1px solid #ccc;
+            height: 34px;
+            line-height: 32px;
+            display: flex;
+
+            .one {
+              font-weight: 700;
+              width: 80px;
+              text-align: right;
+            }
+          }
+        }
+
+        .topInfoTex {
+          cursor: pointer;
+          border: 1px solid #ccc;
+          padding: 5px 10px 4px;
+          display: -webkit-box;
+          overflow: hidden;
+          white-space: normal !important;
+          text-overflow: ellipsis;
+          word-wrap: break-word;
+          -webkit-line-clamp: 2;
+          -webkit-box-orient: vertical;
+
+          &>span {
+            font-weight: 700;
+          }
+        }
+
+      }
+
+      .goodsInfo {
+        .inputBox1 {
+          margin-bottom: 10px;
+          display: flex;
+          align-items: center;
+
+          .inputBoxTit {
+            font-weight: 700;
+            width: 90px;
+
+            &>span {
+              position: relative;
+              top: 3px;
+              color: #ff4d4f;
+            }
+          }
+
+          .inputBoxText {
+            width: calc(100% - 90px);
+          }
+        }
+
+        .inputBox2 {
+          align-items: flex-start;
+        }
+      }
+
+      .backBtn {
+        margin-top: 12px;
+        display: flex;
+        justify-content: center;
+      }
+
+    }
+  }
+}

+ 219 - 0
src/pages/ObjectSon/Object5/LookObject5/index.tsx

@@ -0,0 +1,219 @@
+import BreadTit from "@/components/BreadTit";
+import ImageLazy from "@/components/ImageLazy";
+import LookModal from "@/components/LookObjTable/LookModal";
+import { RootState } from "@/store";
+import { object5infoOutAPI } from "@/store/action/object5";
+import { goodsChangeObj, statusObj } from "@/utils/dataChange";
+import history, { urlParameter } from "@/utils/history";
+import { Button, Table } from "antd";
+import React, {
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
+import { useDispatch, useSelector } from "react-redux";
+import { useLocation } from "react-router-dom";
+import styles from "./index.module.scss";
+
+function LookObject5() {
+  const dispatch = useDispatch();
+
+  // 获取地址栏参数
+  const location = useLocation();
+  const urlParamRef = useRef<any>({});
+  useEffect(() => {
+    urlParamRef.current = urlParameter(location.search);
+    // console.log("地址栏参数", urlParamRef.current);
+  }, [location]);
+
+  const { info, list: tableList } = useSelector(
+    (state: RootState) => state.object5Store.lookInfo
+  );
+
+  const getInfo = useCallback(async () => {
+    const id = urlParamRef.current.id;
+    const res = await object5infoOutAPI(id);
+    const info = res.data;
+
+    // 表格信息的整理
+    const list = [] as any;
+    const oldData = JSON.parse(res.data.before_json);
+    const newData = JSON.parse(res.data.after_json);
+    console.log(123, oldData, newData);
+
+    for (const k in newData) {
+      if (k !== "fileIds" && k !== "fileIdsName") {
+        if (k === "size") {
+          const oldValArr = oldData[k].split(",");
+          const newValArr = oldData[k].split(",");
+          list.push({
+            label: goodsChangeObj[k],
+            oldVal: `通长:${oldValArr[0]}cm,通宽:${oldValArr[1]}cm,通高:${oldValArr[2]}cm`,
+            newVal: `通长:${newValArr[0]}cm,通宽:${newValArr[1]}cm,通高:${newValArr[2]}cm`,
+          });
+        } else if (k === "display") {
+          list.push({
+            label: goodsChangeObj[k],
+            oldVal: oldData[k] === 1 ? "是" : "否",
+            newVal: newData[k] === 1 ? "是" : "否",
+          });
+        } else {
+          list.push({
+            label: goodsChangeObj[k],
+            oldVal: oldData[k],
+            newVal: newData[k],
+          });
+        }
+      } else if (k === "fileIdsName") {
+        list.push({
+          label: "附件信息",
+          oldVal: oldData[k],
+          newVal: newData[k],
+        });
+      }
+    }
+    info.statusTxt = statusObj[info.status];
+    dispatch({ type: "object5/getLookInfo", payload: { info, list } });
+  }, [dispatch]);
+
+  useEffect(() => {
+    getInfo();
+  }, [getInfo]);
+
+  // 控制弹窗的显示隐藏
+  const [show, setShow] = useState(false);
+  // 点击表格里面的查看
+  const lookIdRef = useRef(-1);
+
+  const lookGoods = useCallback((id: number) => {
+    lookIdRef.current = id;
+    setShow(true);
+  }, []);
+
+  // 点击返回
+  const cancelFu = useCallback(() => {
+    history.push({
+      pathname: `/object/5`,
+      state: {
+        k: urlParamRef.current.k ? urlParamRef.current.k : "1",
+        d: urlParamRef.current.d,
+      },
+    });
+  }, []);
+
+  // 表格格式
+  const columns = useMemo(() => {
+    const tempArr = [
+      {
+        title: "修改内容",
+        dataIndex: "label",
+      },
+      {
+        title: "修改前",
+        render: (item: any) => {
+          return item.label === "藏品图片" && item.oldVal ? (
+            <ImageLazy width={120} height={70} src={item.oldVal} />
+          ) : item.oldVal ? (
+            item.oldVal
+          ) : (
+            "-"
+          );
+        },
+      },
+      {
+        title: "修改后",
+        render: (item: any) => {
+          return item.label === "藏品图片" && item.newVal ? (
+            <ImageLazy width={120} height={70} src={item.newVal} />
+          ) : item.newVal ? (
+            item.newVal
+          ) : (
+            "-"
+          );
+        },
+      },
+    ];
+
+    return tempArr;
+  }, []);
+
+  return (
+    <div className={styles.LookObject5}>
+      <div className="breadTit">
+        <BreadTit>
+          <div className="breadTitRow">藏品修改</div>
+          <div className="splitStr">/</div>
+          <div className="breadTitRow active">查看</div>
+        </BreadTit>
+      </div>
+      <div className="objectSonMain">
+        <div className="topTit">修改信息</div>
+        <div className="topInfo">
+          <div className="topInfoRow">
+            <div>
+              <div className="one">藏品名称:</div>
+              <div>{info.name}</div>
+            </div>
+            <div>
+              <div className="one">藏品编号:</div>
+              <div>{info.dictNum}</div>
+            </div>
+          </div>
+          <div className="topInfoRow">
+            <div>
+              <div className="one">登记人员:</div>
+              <div>{info.creatorName}</div>
+            </div>
+            <div>
+              <div className="one">创建日期:</div>
+              <div>{info.create_time}</div>
+            </div>
+          </div>
+          <div className="topInfoTex">
+            <span>审核结果:</span>
+            {info.statusTxt}
+          </div>
+          <div className="topInfoTex" title={info.reason}>
+            <span>审核说明:</span>
+            {info.reason ? info.reason : "-"}
+          </div>
+        </div>
+        <br />
+        <div className="topTit topTit2">
+          修改记录
+          <div className="titBtn">
+            <Button onClick={() => lookGoods(info.goods_ids)}>查看</Button>
+          </div>
+        </div>
+        <div className="goodsInfo">
+          {/* 表格信息 */}
+          <Table
+            size="small"
+            scroll={{ y: 340 }}
+            dataSource={tableList}
+            columns={columns}
+            rowKey="label"
+            pagination={false}
+          />
+        </div>
+        <div className="backBtn">
+          <Button onClick={cancelFu}>返回</Button>
+        </div>
+      </div>
+      {/* 点击查看出来的对话框 */}
+      {show ? (
+        <LookModal
+          id={lookIdRef.current}
+          show={show}
+          closeShow={() => setShow(false)}
+        />
+      ) : null}
+    </div>
+  );
+}
+
+const MemoLookObject5 = React.memo(LookObject5);
+
+export default MemoLookObject5;

+ 5 - 0
src/pages/ObjectSon/Object5/index.module.scss

@@ -0,0 +1,5 @@
+.Object5{
+  :global{
+    
+  }
+}

+ 0 - 0
src/pages/ObjectSon/Object5/index.tsx


Деякі файли не було показано, через те що забагато файлів було змінено