shaogen1995 пре 12 часа
комит
b0f338d433
100 измењених фајлова са 46872 додато и 0 уклоњено
  1. 12 0
      .editorconfig
  2. 3 0
      .env
  3. 23 0
      .gitignore
  4. 11 0
      .prettierrc.js
  5. 39 0
      .vscode/settings.json
  6. 20 0
      README.md
  7. 10 0
      config-overrides.js
  8. 30467 0
      package-lock.json
  9. 66 0
      package.json
  10. 8 0
      path.tsconfig.json
  11. 1 0
      public/baseUrl.js
  12. BIN
      public/favicon.ico
  13. 41 0
      public/index.html
  14. 3797 0
      public/modelLook/assets/index-BcLjdLAW.js
  15. 3797 0
      public/modelLook/assets/index-P16QZIgH.js
  16. BIN
      public/modelLook/assets/tinyusdz-DnE9aqql.wasm
  17. 25 0
      public/modelLook/index.html
  18. 68 0
      src/App.tsx
  19. BIN
      src/assets/img/login/IMGerror.png
  20. BIN
      src/assets/img/login/LOGO.png
  21. BIN
      src/assets/img/login/Statistics.png
  22. BIN
      src/assets/img/login/bg.jpg
  23. BIN
      src/assets/img/login/flower.png
  24. BIN
      src/assets/img/login/homeBg.jpg
  25. BIN
      src/assets/img/login/icon_1.png
  26. BIN
      src/assets/img/login/icon_2.png
  27. BIN
      src/assets/img/login/icon_3.png
  28. BIN
      src/assets/img/login/icon_4.png
  29. BIN
      src/assets/img/login/icon_5.png
  30. BIN
      src/assets/img/login/loading.gif
  31. BIN
      src/assets/img/login/remind.png
  32. BIN
      src/assets/img/login/user_1.png
  33. 77 0
      src/assets/styles/base.css
  34. 152 0
      src/assets/styles/base.less
  35. 26 0
      src/components/AsyncSpinLoding/index.module.scss
  36. 29 0
      src/components/AsyncSpinLoding/index.tsx
  37. 24 0
      src/components/AuthButton/index.tsx
  38. 32 0
      src/components/AuthRoute/index.tsx
  39. 17 0
      src/components/BreadTit/index.module.scss
  40. 17 0
      src/components/BreadTit/index.tsx
  41. 51 0
      src/components/ImageLazy/index.module.scss
  42. 64 0
      src/components/ImageLazy/index.tsx
  43. 32 0
      src/components/LeftBar/index.module.scss
  44. 37 0
      src/components/LeftBar/index.tsx
  45. 98 0
      src/components/LookDom/index.module.scss
  46. 81 0
      src/components/LookDom/index.tsx
  47. 25 0
      src/components/LookModel/index.module.scss
  48. 35 0
      src/components/LookModel/index.tsx
  49. 319 0
      src/components/LookObjTable/LookModal.tsx
  50. 83 0
      src/components/LookObjTable/index.css
  51. 121 0
      src/components/LookObjTable/index.less
  52. 91 0
      src/components/LookObjTable/index.tsx
  53. 29 0
      src/components/Message/index.tsx
  54. 32 0
      src/components/NotFound/index.tsx
  55. 157 0
      src/components/ObjectAdd/index.css
  56. 211 0
      src/components/ObjectAdd/index.less
  57. 9 0
      src/components/ObjectAdd/index.module.scss
  58. 868 0
      src/components/ObjectAdd/index.tsx
  59. 10 0
      src/components/SpinLoding/index.module.scss
  60. 13 0
      src/components/SpinLoding/index.tsx
  61. 36 0
      src/components/UpAsyncLoding/index.module.scss
  62. 15 0
      src/components/UpAsyncLoding/index.tsx
  63. 41 0
      src/index.tsx
  64. 208 0
      src/pages/Home/index.module.scss
  65. 285 0
      src/pages/Home/index.tsx
  66. 224 0
      src/pages/Layout/index.module.scss
  67. 256 0
      src/pages/Layout/index.tsx
  68. 103 0
      src/pages/Login/index.module.scss
  69. 88 0
      src/pages/Login/index.tsx
  70. 5 0
      src/pages/Object/index.module.scss
  71. 249 0
      src/pages/Object/index.tsx
  72. 86 0
      src/pages/ObjectSon/Object1/AddObject1/index.module.scss
  73. 318 0
      src/pages/ObjectSon/Object1/AddObject1/index.tsx
  74. 81 0
      src/pages/ObjectSon/Object1/AuditObject1/index.module.scss
  75. 164 0
      src/pages/ObjectSon/Object1/AuditObject1/index.tsx
  76. 56 0
      src/pages/ObjectSon/Object1/LookObject1/index.module.scss
  77. 128 0
      src/pages/ObjectSon/Object1/LookObject1/index.tsx
  78. 5 0
      src/pages/ObjectSon/Object1/index.module.scss
  79. 339 0
      src/pages/ObjectSon/Object1/index.tsx
  80. 26 0
      src/pages/ObjectSon/Object2/LookObject2/index.css
  81. 28 0
      src/pages/ObjectSon/Object2/LookObject2/index.less
  82. 127 0
      src/pages/ObjectSon/Object2/LookObject2/index.module.scss
  83. 537 0
      src/pages/ObjectSon/Object2/LookObject2/index.tsx
  84. 127 0
      src/pages/ObjectSon/Object2/LookObject2/table.tsx
  85. 91 0
      src/pages/ObjectSon/Object2/index.module.scss
  86. 405 0
      src/pages/ObjectSon/Object2/index.tsx
  87. 15 0
      src/pages/ObjectSon/Object3/AddObject3/GoodsAll.css
  88. 29 0
      src/pages/ObjectSon/Object3/AddObject3/GoodsAll.less
  89. 152 0
      src/pages/ObjectSon/Object3/AddObject3/GoodsAll.tsx
  90. 86 0
      src/pages/ObjectSon/Object3/AddObject3/index.module.scss
  91. 327 0
      src/pages/ObjectSon/Object3/AddObject3/index.tsx
  92. 86 0
      src/pages/ObjectSon/Object3/AuditObject3/index.module.scss
  93. 218 0
      src/pages/ObjectSon/Object3/AuditObject3/index.tsx
  94. 56 0
      src/pages/ObjectSon/Object3/LookObject3/index.module.scss
  95. 189 0
      src/pages/ObjectSon/Object3/LookObject3/index.tsx
  96. 5 0
      src/pages/ObjectSon/Object3/index.module.scss
  97. 316 0
      src/pages/ObjectSon/Object3/index.tsx
  98. 151 0
      src/pages/ObjectSon/Object4/AddObject4/GoodsAll.tsx
  99. 86 0
      src/pages/ObjectSon/Object4/AddObject4/index.module.scss
  100. 0 0
      src/pages/ObjectSon/Object4/AddObject4/index.tsx

+ 12 - 0
.editorconfig

@@ -0,0 +1,12 @@
+root = true # 控制配置文件 .editorconfig 是否生效的字段
+ 
+[**] # 匹配全部文件
+indent_style = space # 缩进风格,可选space|tab
+indent_size = 2 # 缩进的空格数
+charset = utf-8 # 设置字符集
+trim_trailing_whitespace = true # 删除一行中的前后空格
+insert_final_newline = true # 设为true表示使文件以一个空白行结尾
+end_of_line = lf
+ 
+[**.md] # 匹配md文件
+trim_trailing_whitespace = false

+ 3 - 0
.env

@@ -0,0 +1,3 @@
+# .env.production
+GENERATE_SOURCEMAP = false
+# 关闭映射

+ 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*

+ 11 - 0
.prettierrc.js

@@ -0,0 +1,11 @@
+module.exports = {
+  printWidth: 100, // 一行的字符数,如果超过会进行换行
+  tabWidth: 2, // 一个tab代表几个空格数,默认就是2
+  useTabs: false, // 是否启用tab取代空格符缩进,.editorconfig设置空格缩进,所以设置为false
+  semi: false, // 行尾是否使用分号,默认为true
+  singleQuote: true, // 字符串是否使用单引号
+  trailingComma: "none", // 对象或数组末尾是否添加逗号 none| es5| all
+  jsxSingleQuote: true, // 在jsx里是否使用单引号,你看着办
+  bracketSpacing: true, // 对象大括号直接是否有空格,默认为true,效果:{ foo: bar }
+  arrowParens: "avoid", // 箭头函数如果只有一个参数则省略括号
+};

+ 39 - 0
.vscode/settings.json

@@ -0,0 +1,39 @@
+{
+  "search.exclude": {
+    "/node_modules": true,
+    "dist": true,
+    "pnpm-lock.sh": true
+  },
+  "editor.formatOnSave": true,
+  "[javascript]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "[javascriptreact]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "[typescript]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "[typescriptreact]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "[json]": {
+    "editor.defaultFormatter": "vscode.json-language-features"
+  },
+  "[html]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "[markdown]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "[css]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "[less]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "[scss]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "liveServer.settings.port": 5502
+}

+ 20 - 0
README.md

@@ -0,0 +1,20 @@
+1.版本类型问题
+
+找到 import { useDispatch } from "react-redux";
+
+按住 Ctrl 点击 useDispatch 出来 useDispatch.d.ts 类型声明文件
+(路径 node_modules>react-redux>es>hooks>useDispatch.d.ts)
+
+找到最后面的 AnyAction,按住 Ctrl 点击 进入
+(路径 node_modules>redux>index.d.ts)
+
+找到
+export interface Action<T = any> {
+type: T
+}
+
+在 type 后面加一个 ? 即=>
+
+export interface Action<T = any> {
+type?: T
+}

+ 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-weizhoubwg.4dage.com'

BIN
public/favicon.ico


+ 41 - 0
public/index.html

@@ -0,0 +1,41 @@
+<!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>

Разлика између датотеке није приказан због своје велике величине
+ 3797 - 0
public/modelLook/assets/index-BcLjdLAW.js


Разлика између датотеке није приказан због своје велике величине
+ 3797 - 0
public/modelLook/assets/index-P16QZIgH.js


BIN
public/modelLook/assets/tinyusdz-DnE9aqql.wasm


+ 25 - 0
public/modelLook/index.html

@@ -0,0 +1,25 @@
+<!doctype html>
+<html lang="zh-CN">
+  <head>
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <meta name="color-scheme" content="light" />
+    <title>GLB 加载演示</title>
+    <style>
+      html,
+      body,
+      #root {
+        height: 100%;
+        background: transparent;
+      }
+      body {
+        margin: 0;
+        overscroll-behavior: none;
+      }
+    </style>
+    <script type="module" crossorigin src="./assets/index-BcLjdLAW.js"></script>
+  </head>
+  <body>
+    <div id="root"></div>
+  </body>
+</html>

+ 68 - 0
src/App.tsx

@@ -0,0 +1,68 @@
+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'
+import LookModel from './components/LookModel'
+import LookDom from './components/LookDom'
+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 />
+
+      {/* 查看4dage模型 */}
+      <LookModel />
+
+      {/* 查看视频音频 ---'glb', 'fbx模型*/}
+      <LookDom />
+    </>
+  )
+}

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


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

@@ -0,0 +1,77 @@
+* {
+  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-image {
+  display: none !important;
+}
+/* 找不到页面 */
+.noFindPage {
+  opacity: 0;
+  transition: opacity 0.5s;
+}

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

@@ -0,0 +1,152 @@
+* {
+  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 0.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;

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

@@ -0,0 +1,24 @@
+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;
+        }
+      }
+    }
+  }
+
+}

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

@@ -0,0 +1,64 @@
+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
+  srcDa: string
+  noLook?: boolean
+}
+
+function ImageLazy({ width = 100, height = 100, src, noLook, srcDa }: Props) {
+  const dispatch = useDispatch()
+
+  // 默认不能预览图片,加载成功之后能预览
+  const [lookImg, setLookImg] = useState(false)
+
+  // 图片加载完成
+  const onLoad = useCallback(() => {
+    setLookImg(true)
+  }, [])
+
+  // 点击预览图片
+  const lookBigImg = useCallback(() => {
+    dispatch({
+      type: 'login/lookBigImg',
+      payload: { url: baseURL + (srcDa ? srcDa : src), show: true }
+    })
+  }, [dispatch, src, srcDa])
+
+  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>
+  );
+}

+ 98 - 0
src/components/LookDom/index.module.scss

@@ -0,0 +1,98 @@
+.LookDom {
+  transition: opacity 0.3s;
+  position: fixed;
+  z-index: 9991;
+  opacity: 0;
+  pointer-events: none;
+  top: 0;
+  left: 0;
+  width: 100vw;
+  height: 100vh;
+  background-color: rgba(0, 0, 0, 0.6);
+
+  :global {
+    .close {
+      color: #fff;
+      position: absolute;
+      right: 70px;
+      top: 70px;
+      font-size: 30px;
+      cursor: pointer;
+      z-index: 10;
+    }
+    .closeModel {
+      right: aoto;
+      left: 40px;
+      top: 40px;
+      img {
+        width: 50px;
+      }
+    }
+
+    .viedoBox {
+      position: absolute;
+      top: 50%;
+      left: 50%;
+      transform: translate(-50%, -50%);
+      width: 800px;
+      height: 500px;
+
+      video {
+        width: 100%;
+        height: 100%;
+      }
+    }
+
+    .audioBox {
+      position: absolute;
+      top: 50%;
+      left: 50%;
+      transform: translate(-50%, -50%);
+      width: 500px;
+      height: 60px;
+
+      audio {
+        width: 100%;
+        height: 100%;
+      }
+    }
+
+    .modelBox {
+      background-color: rgba(0, 0, 0, 0.7);
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+
+      iframe {
+        width: 100%;
+        height: 100%;
+      }
+
+      .modelBtn {
+        cursor: pointer;
+        position: absolute;
+        z-index: 10;
+        right: 30px;
+        bottom: 50px;
+        img {
+          display: block;
+          margin: 15px 0;
+        }
+      }
+      .modelName {
+        pointer-events: none;
+        position: absolute;
+        z-index: 10;
+        bottom: 80px;
+        left: 50%;
+        transform: translateX(-50%);
+        font-size: 18px;
+        font-weight: 700;
+        color: var(--themeColor);
+        letter-spacing: 4px;
+      }
+    }
+  }
+}

+ 81 - 0
src/components/LookDom/index.tsx

@@ -0,0 +1,81 @@
+import React from 'react'
+import { CloseCircleOutlined } from '@ant-design/icons'
+import styles from './index.module.scss'
+import { useSelector } from 'react-redux'
+import store, { RootState } from '@/store'
+import { baseURL } from '@/utils/http'
+import classNames from 'classnames'
+
+function LookDom() {
+  const { src, type, flag, name } = useSelector((state: RootState) => state.loginStore.lookDom)
+
+  // 控制放大缩小复位
+  // const modelMoveFu = useCallback((fuName: 'resetView' | 'zoomIn' | 'zoomOut') => {
+  //   const dom: any = document.querySelector('#modelIframe')
+  //   if (dom && dom.contentWindow && dom.contentWindow.sceneFc[fuName]) {
+  //     dom.contentWindow.sceneFc[fuName]()
+  //   }
+  // }, [])
+
+  return (
+    <div className={styles.LookDom} style={src ? { opacity: 1, pointerEvents: 'auto' } : {}}>
+      {src ? (
+        <>
+          {type === 'video' ? (
+            <div className='viedoBox'>
+              <video autoPlay controls src={flag ? src : baseURL + src}></video>
+            </div>
+          ) : type === 'audio' ? (
+            <div className='audioBox'>
+              <audio autoPlay controls src={flag ? src : baseURL + src}></audio>
+            </div>
+          ) : (
+            <div className='modelBox'>
+              <iframe
+                id='modelIframe'
+                src={`./modelLook/index.html?src=${baseURL + src}&min=0.3&max=3`}
+                frameBorder='0'
+                title='model'
+              ></iframe>
+              {/* <div className='modelBtn'>
+                <img
+                  onClick={() => modelMoveFu('zoomIn')}
+                  src={require('@/assets/img/model+.png')}
+                  alt=''
+                />
+                <img
+                  onClick={() => modelMoveFu('zoomOut')}
+                  src={require('@/assets/img/model-.png')}
+                  alt=''
+                />
+                <img
+                  onClick={() => modelMoveFu('resetView')}
+                  src={require('@/assets/img/modelR.png')}
+                  alt=''
+                />
+              </div> */}
+
+              {name ? <div className='modelName'>{name}</div> : null}
+            </div>
+          )}
+
+          <div
+            className={classNames('close', type === 'model' ? 'closeModel' : '')}
+            onClick={() =>
+              store.dispatch({
+                type: 'layout/lookDom',
+                payload: { src: '', type: '', flag: false, name: '' }
+              })
+            }
+          >
+            <CloseCircleOutlined rev={undefined} />
+          </div>
+        </>
+      ) : null}
+    </div>
+  )
+}
+
+const MemoLookDom = React.memo(LookDom)
+
+export default MemoLookDom

+ 25 - 0
src/components/LookModel/index.module.scss

@@ -0,0 +1,25 @@
+.LookModel {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background-color: rgba(0, 0, 0, 0.8);
+  overflow: hidden;
+  z-index: 99999;
+
+  :global {
+    iframe {
+      width: 100%;
+      height: 100%;
+    }
+    .LookBtnx {
+      position: absolute;
+      right: 20px;
+      top: 20px;
+      cursor: pointer;
+      color: #fff;
+      font-size: 30px;
+    }
+  }
+}

+ 35 - 0
src/components/LookModel/index.tsx

@@ -0,0 +1,35 @@
+import React from 'react'
+import styles from './index.module.scss'
+import { baseURL } from '@/utils/http'
+import { CloseCircleOutlined } from '@ant-design/icons'
+import { useSelector } from 'react-redux'
+import store, { RootState } from '@/store'
+
+function LookModel() {
+  const lookModel = useSelector((state: RootState) => state.loginStore.lookModel)
+
+  return (
+    <>
+      {lookModel ? (
+        <div className={styles.LookModel}>
+          <iframe
+            src={`${baseURL}/web/model/model.html?m=${lookModel}`}
+            frameBorder='0'
+            title='model'
+          ></iframe>
+
+          <div
+            className='LookBtnx'
+            onClick={() => store.dispatch({ type: 'login/lookModel', payload: '' })}
+          >
+            <CloseCircleOutlined />
+          </div>
+        </div>
+      ) : null}
+    </>
+  )
+}
+
+const MemoLookModel = React.memo(LookModel)
+
+export default MemoLookModel

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

@@ -0,0 +1,319 @@
+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 store, { RootState } from '@/store'
+import ImageLazy from '../ImageLazy'
+import { getInfoInAPI } from '@/store/action/object1'
+import './index.css'
+import { baseURL } from '@/utils/http'
+import { lookModelClick, lookModelFu } from '@/utils/modelLook'
+
+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: '附件名称',
+        render: (item: any) => {
+          const name: string = item.name || ''
+          return (
+            <>
+              {item.name}{' '}
+              {name.endsWith('.4dage') ? (
+                <span
+                  className='lookObjTableLookBtn'
+                  onClick={() =>
+                    store.dispatch({ type: 'login/lookModel', payload: baseURL + item.filePath })
+                  }
+                >
+                  [ 预 览 ]
+                </span>
+              ) : lookModelFu(name) ? (
+                <span
+                  className='lookObjTableLookBtn'
+                  onClick={() => lookModelClick(item.filePath, '')}
+                >
+                  [ 预 览 ]
+                </span>
+              ) : null}
+            </>
+          )
+        }
+      },
+      {
+        title: '上传时间',
+        dataIndex: 'createTime'
+      }
+    ] as any
+    if (fileSelect === 'img') {
+      tempArr.unshift({
+        title: '缩略图',
+        render: (item: any) => (
+          <ImageLazy width={120} height={70} src={item.thumb} srcDa={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} srcDa={info.thumbPc} />
+            </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

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

@@ -0,0 +1,83 @@
+.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);
+}
+.lookObjTable .lookObjTableLookBtn {
+  cursor: pointer;
+  color: var(--themeColor);
+}

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

@@ -0,0 +1,121 @@
+.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);
+      }
+    }
+  }
+  .lookObjTableLookBtn {
+    cursor: pointer;
+    color: var(--themeColor);
+  }
+}

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

@@ -0,0 +1,91 @@
+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} srcDa={item.thumbPc} />
+        )
+      },
+      {
+        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);
+}

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

@@ -0,0 +1,868 @@
+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 store, { 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'
+import { baseURL } from '@/utils/http'
+import { lookModelClick, lookModelFu } from '@/utils/modelLook'
+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,
+      thumbPc: coverDa,
+      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)
+      setCoverDa(data.thumbPc)
+
+      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) => {
+          const name: string = item.name || ''
+
+          return (
+            <>
+              {name.endsWith('.4dage') ? (
+                <Button
+                  type='text'
+                  onClick={() =>
+                    store.dispatch({ type: 'login/lookModel', payload: baseURL + item.filePath })
+                  }
+                >
+                  预览
+                </Button>
+              ) : lookModelFu(name) ? (
+                <Button type='text' onClick={() => lookModelClick(item.filePath, '')}>
+                  预览
+                </Button>
+              ) : null}
+
+              <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.thumb} srcDa={item.filePath} />
+          </div>
+        )
+      })
+    }
+
+    return tempArr
+  }, [delOne, fileSelect])
+
+  // 封面地址
+  const [cover, setCover] = useState('')
+  const [coverDa, setCoverDa] = 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)
+
+      if (fileSelect === 'img' && filesInfo.size > 1 * 1024 * 1024) {
+        // 开启压缩图片
+        fd.append('isCompress', 'true')
+      }
+
+      e.target.value = ''
+
+      const res: any = await object1AddUpFileAPI(fd)
+      if (res.code === 0) {
+        MessageFu.success('上传成功!')
+        setCover(res.data.thumb)
+        setCoverDa(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)
+        fd.append('isDb', 'true')
+        if (fileSelect === 'img' && filesInfo.size > 1 * 1024 * 1024) {
+          // 开启压缩图片
+          fd.append('isCompress', 'true')
+        }
+
+        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} srcDa={coverDa} />
+                    ) : 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;
+          }
+        }
+      }
+    }
+  }
+}

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

@@ -0,0 +1,285 @@
+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>
+  )
+}

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

@@ -0,0 +1,224 @@
+.Layout {
+  width: 100%;
+  height: 100%;
+
+  :global {
+    .topTitle {
+      width: 100%;
+      height: 90px;
+      background-color: var(--themeColor);
+      // background-color: rgb(75, 40, 40);
+
+      .main {
+        width: 1580px;
+        height: 100%;
+        margin: 0 auto;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+
+        & > div {
+          height: 100%;
+        }
+
+        .logo {
+          opacity: 0;
+          height: 100%;
+          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;
+            font-weight: 700;
+            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 0.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);
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}

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

@@ -0,0 +1,256 @@
+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

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

@@ -0,0 +1,103 @@
+.Login {
+  width: 100%;
+  height: 100%;
+  background-image: url('../../assets/img/login/bg.jpg');
+  background-size: 100% 100%;
+  position: relative;
+
+  :global {
+    .loginTxt {
+      position: absolute;
+      top: 17%;
+      left: 0%;
+      width: 100%;
+      text-align: center;
+      color: var(--themeColor);
+      font-size: 40px;
+      font-weight: 700;
+      letter-spacing: 4px;
+      margin-top: 15px;
+    }
+
+    .main {
+      padding-top: 20px;
+      position: absolute;
+      top: 55%;
+      transform: translateY(-50%);
+      width: 100%;
+      height: 280px;
+      background-color: rgba(255, 255, 255, 0.4);
+      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;
+        }
+      }
+    }
+  }
+}

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

@@ -0,0 +1,88 @@
+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='loginTxt'>
+        {/*       
+        <p>文物数字资源管理</p>
+        <p>及展陈管理系统</p>
+       */}
+      </div>
+
+      <div className='main'>
+        <p> </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>
+  );
+}

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

@@ -0,0 +1,86 @@
+.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-affix-wrapper {
+            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;
+        }
+      }
+    }
+  }
+}

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

@@ -0,0 +1,318 @@
+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} srcDa={item.thumbPc} />
+        )
+      },
+
+      {
+        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;
+      }
+    }
+  }
+}

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

@@ -0,0 +1,537 @@
+import AuthButton from '@/components/AuthButton'
+import BreadTit from '@/components/BreadTit'
+import ImageLazy from '@/components/ImageLazy'
+import ObjectAdd from '@/components/ObjectAdd'
+import store, { 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'
+import { lookModelClick, lookModelFu } from '@/utils/modelLook'
+
+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) => {
+          const name: string = item.name || ''
+          return (
+            <>
+              {name.endsWith('.4dage') ? (
+                <Button
+                  type='text'
+                  onClick={() =>
+                    store.dispatch({ type: 'login/lookModel', payload: baseURL + item.filePath })
+                  }
+                >
+                  预览
+                </Button>
+              ) : lookModelFu(name) ? (
+                <Button type='text' onClick={() => lookModelClick(item.filePath, '')}>
+                  预览
+                </Button>
+              ) : null}
+              <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.thumb} srcDa={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} srcDa={info.thumbPc} 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;
+    }
+  }
+}

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

@@ -0,0 +1,405 @@
+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, storageStatusObj } 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} srcDa={item.thumbPc} />
+        )
+      },
+      {
+        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 ? '是' : '否'
+      v.storageStatusTxt = storageStatusObj[v.storageStatus]
+      v.thumb = v.thumb ? window.location.origin + v.thumb : '-'
+    })
+    const option = {
+      fileName: name,
+      datas: [
+        {
+          sheetData,
+          sheetName: name,
+          sheetFilter: [
+            'thumb',
+            'dictNum',
+            'num',
+            'name',
+            'dictAge',
+            'dictTexture',
+            'display',
+            'storageStatusTxt'
+            // "namePrimitive",
+            // "ageSpecific",
+            // "dictGoodType",
+            // "pcs",
+            // "dictLevel",
+            // "sizeRes",
+            // "sizeSpecific",
+            // "dictQualityScope",
+            // "qualityRes",
+            // "complete",
+            // "repair",
+            // "dictCheckInScope",
+            // "checkInYear",
+            // "author",
+            // "vision",
+            // "onFile",
+            // "description",
+            // "storageAncestorName",
+            // "outLocation",
+          ],
+          sheetHeader: [
+            '缩略图',
+            '藏品编号名称',
+            '藏品编号',
+            '藏品名称',
+            '年代',
+            '质地',
+            '展示状态',
+            '状态'
+            // "原名",
+            // "具体年代",
+            // "文物类别",
+            // "实际数量",
+            // "文物级别",
+            // "外形尺寸",
+            // "具体尺寸",
+            // "质量范围",
+            // "具体质量",
+            // "完残程度",
+            // "保存状态",
+            // "入藏时间范围",
+            // "入藏年度",
+            // "著者",
+            // "版本",
+            // "存卷",
+            // "来源说明",
+            // "入库位置",
+            // "出库位置",
+          ],
+          columnWidths: [
+            20, 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} srcDa={v.thumbPc} />
+                <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;
+  }
+}

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

@@ -0,0 +1,152 @@
+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} srcDa={item.thumbPc} />
+        )
+      },
+      {
+        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

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

@@ -0,0 +1,86 @@
+.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-affix-wrapper {
+            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;
+        }
+      }
+    }
+  }
+}

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

@@ -0,0 +1,327 @@
+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} srcDa={item.thumbPc} />
+        )
+      },
+
+      {
+        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;
+      }
+
+    }
+  }
+}

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

@@ -0,0 +1,218 @@
+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} srcDa={item.thumbPc} />
+        )
+      },
+      {
+        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;
+      }
+    }
+  }
+}

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

@@ -0,0 +1,189 @@
+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} srcDa={item.thumbPc} />
+        )
+      },
+      {
+        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>
+  );
+}

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

@@ -0,0 +1,151 @@
+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} srcDa={item.thumbPc} />
+        )
+      },
+      {
+        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

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

@@ -0,0 +1,86 @@
+.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-affix-wrapper {
+            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;
+        }
+      }
+    }
+  }
+}

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


Неке датотеке нису приказане због велике количине промена