shaogen1995 1 ماه پیش
کامیت
f7a4754211
63فایلهای تغییر یافته به همراه33371 افزوده شده و 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. 5 0
      后台大场景编辑/README.md
  7. 10 0
      后台大场景编辑/config-overrides.js
  8. 30285 0
      后台大场景编辑/package-lock.json
  9. 66 0
      后台大场景编辑/package.json
  10. 8 0
      后台大场景编辑/path.tsconfig.json
  11. BIN
      后台大场景编辑/public/favicon.ico
  12. 26 0
      后台大场景编辑/public/index.html
  13. 39 0
      后台大场景编辑/src/App.tsx
  14. BIN
      后台大场景编辑/src/assets/img/loginBg.jpg
  15. BIN
      后台大场景编辑/src/assets/img/logo.png
  16. BIN
      后台大场景编辑/src/assets/img/user.png
  17. 181 0
      后台大场景编辑/src/assets/styles/base.css
  18. 241 0
      后台大场景编辑/src/assets/styles/base.less
  19. 21 0
      后台大场景编辑/src/components/AsyncSpinLoding/index.module.scss
  20. 15 0
      后台大场景编辑/src/components/AsyncSpinLoding/index.tsx
  21. 32 0
      后台大场景编辑/src/components/AuthRoute/index.tsx
  22. 29 0
      后台大场景编辑/src/components/Message/index.tsx
  23. 26 0
      后台大场景编辑/src/components/NotFound/index.tsx
  24. 10 0
      后台大场景编辑/src/components/SpinLoding/index.module.scss
  25. 13 0
      后台大场景编辑/src/components/SpinLoding/index.tsx
  26. 43 0
      后台大场景编辑/src/components/UpAsyncLoding/index.module.scss
  27. 38 0
      后台大场景编辑/src/components/UpAsyncLoding/index.tsx
  28. 38 0
      后台大场景编辑/src/index.tsx
  29. 29 0
      后台大场景编辑/src/pages/A1List/index.module.scss
  30. 196 0
      后台大场景编辑/src/pages/A1List/index.tsx
  31. 44 0
      后台大场景编辑/src/pages/A9User/UserAdd/index.module.scss
  32. 153 0
      后台大场景编辑/src/pages/A9User/UserAdd/index.tsx
  33. 42 0
      后台大场景编辑/src/pages/A9User/index.module.scss
  34. 277 0
      后台大场景编辑/src/pages/A9User/index.tsx
  35. 161 0
      后台大场景编辑/src/pages/Layout/index.module.scss
  36. 225 0
      后台大场景编辑/src/pages/Layout/index.tsx
  37. 137 0
      后台大场景编辑/src/pages/Login copy/index.module.scss
  38. 78 0
      后台大场景编辑/src/pages/Login copy/index.tsx
  39. 41 0
      后台大场景编辑/src/pages/Login/index.module.scss
  40. 75 0
      后台大场景编辑/src/pages/Login/index.tsx
  41. 6 0
      后台大场景编辑/src/pages/初始化组件/index.module.scss
  42. 14 0
      后台大场景编辑/src/pages/初始化组件/index.tsx
  43. 41 0
      后台大场景编辑/src/store/action/A1List.ts
  44. 54 0
      后台大场景编辑/src/store/action/A9User.ts
  45. 17 0
      后台大场景编辑/src/store/action/layout.ts
  46. 20 0
      后台大场景编辑/src/store/index.ts
  47. 27 0
      后台大场景编辑/src/store/reducer/A1List.ts
  48. 27 0
      后台大场景编辑/src/store/reducer/A9User.ts
  49. 16 0
      后台大场景编辑/src/store/reducer/index.ts
  50. 39 0
      后台大场景编辑/src/store/reducer/layout.ts
  51. 14 0
      后台大场景编辑/src/types/api/A1List.d.ts
  52. 33 0
      后台大场景编辑/src/types/api/A9User.d.ts
  53. 8 0
      后台大场景编辑/src/types/declaration.d.ts
  54. 3 0
      后台大场景编辑/src/types/index.d.ts
  55. 8 0
      后台大场景编辑/src/utils/dataChange.ts
  56. 35 0
      后台大场景编辑/src/utils/domShow.ts
  57. 17 0
      后台大场景编辑/src/utils/history.ts
  58. 93 0
      后台大场景编辑/src/utils/http.ts
  59. 50 0
      后台大场景编辑/src/utils/message.ts
  60. 100 0
      后台大场景编辑/src/utils/pass.ts
  61. 17 0
      后台大场景编辑/src/utils/pcOrH5.ts
  62. 33 0
      后台大场景编辑/src/utils/storage.ts
  63. 27 0
      后台大场景编辑/tsconfig.json

+ 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: 90, // 一行的字符数,如果超过会进行换行
+  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
+}

+ 5 - 0
后台大场景编辑/README.md

@@ -0,0 +1,5 @@
+1.测试服务器存放位置:--/data/data01/beiren_bigScene_local_data
+
+2.测试域名:https://sit-beirenbigscene.4dage.com
+
+3.部署之后 backUrl 按需求修改域名

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

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 30285 - 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",
+    "axios": "^1.1.3",
+    "dayjs": "^1.11.7",
+    "echarts": "^5.4.0",
+    "js-base64": "^3.7.3",
+    "js-export-excel": "^1.1.4",
+    "react": "^18.2.0",
+    "react-dnd": "^16.0.1",
+    "react-dnd-html5-backend": "^16.0.1",
+    "react-dom": "^18.2.0",
+    "react-lazyimg-component": "^1.0.1",
+    "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",
+    "react-app-rewired": "^2.2.1"
+  },
+  "homepage": "."
+}

+ 8 - 0
后台大场景编辑/path.tsconfig.json

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

BIN
后台大场景编辑/public/favicon.ico


+ 26 - 0
后台大场景编辑/public/index.html

@@ -0,0 +1,26 @@
+<!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" />
+    <meta name="description" content="Web site created using create-react-app" />
+    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
+
+    <script>
+      // 甲方服务器域名
+      // const backUrl = 'http://192.168.20.55:3000'
+      const backUrl = 'https://sit-beirenbigscene.4dage.com/backstage'
+
+      // 第三方网址跳转域名
+      const threeUrl = `https://dev.itfinspread.com:8003/#/sso?redirectUrl=${backUrl}`
+    </script>
+
+    <title>北人亦创国际会展中心-场景编辑后台</title>
+  </head>
+  <body>
+    <noscript>You need to enable JavaScript to run this app.</noscript>
+    <div id="root"></div>
+  </body>
+</html>

+ 39 - 0
后台大场景编辑/src/App.tsx

@@ -0,0 +1,39 @@
+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 UpAsyncLoding from './components/UpAsyncLoding'
+import MessageCom from './components/Message'
+const Layout = React.lazy(() => import('./pages/Layout'))
+const Login = React.lazy(() => import('./pages/Login'))
+
+export default function App() {
+  return (
+    <>
+      {/* 关于路由 */}
+      <Router history={history}>
+        <React.Suspense fallback={<SpinLoding />}>
+          <Switch>
+            {/* 测试页面 */}
+            <Route path='/' component={Login} exact />
+            <Route path='/login' component={Login} />
+            <AuthRoute path='/' component={Layout} />
+          </Switch>
+        </React.Suspense>
+      </Router>
+
+      {/* 发送请求的加载组件 */}
+      <AsyncSpinLoding />
+
+      {/* 上传附件的进度条元素 */}
+      <UpAsyncLoding />
+
+      {/* antd 轻提示 ---兼容360浏览器 */}
+      <MessageCom />
+    </>
+  )
+}

BIN
后台大场景编辑/src/assets/img/loginBg.jpg


BIN
后台大场景编辑/src/assets/img/logo.png


BIN
后台大场景编辑/src/assets/img/user.png


+ 181 - 0
后台大场景编辑/src/assets/styles/base.css

@@ -0,0 +1,181 @@
+* {
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
+}
+html {
+  height: 100%;
+  font-size: 14px;
+}
+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: black;
+}
+a {
+  text-decoration: none;
+  color: black;
+  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: auto;
+  overflow-y: overlay;
+}
+/* 文本域取消下拉 */
+textarea {
+  resize: none !important;
+}
+/* 主题色 */
+:root {
+  --themeColor: #165dff;
+}
+/* 找不到页面 */
+.noFindPage {
+  opacity: 0;
+  transition: opacity 0.5s;
+}
+/* 兼容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: auto;
+  overflow-y: overlay;
+  /* 普通文字按钮的颜色 */
+  /* 按钮的危险颜色 */
+  /* antd分页器样式 */
+  /* 表格的图片居中 */
+  /* antd图片预览组件 */
+  /* antd表格居中 */
+}
+#root a {
+  color: var(--themeColor);
+}
+#root .ant-btn-text {
+  color: var(--themeColor);
+}
+#root .ant-btn-text:disabled {
+  cursor: not-allowed;
+  color: rgba(0, 0, 0, 0.25);
+}
+#root .ant-btn-text.ant-btn-dangerous {
+  color: #ff1a1a;
+}
+#root .ant-pagination .ant-pagination-item {
+  border-radius: 50%;
+  border: 1px solid #999;
+  background-color: transparent !important;
+}
+#root .ant-pagination .ant-pagination-item-active {
+  background-color: var(--themeColor) !important;
+}
+#root .ant-pagination .ant-pagination-item-active a {
+  color: #fff !important;
+}
+#root .ant-pagination .ant-pagination-item:hover {
+  background-color: var(--themeColor) !important;
+}
+#root .ant-pagination .ant-pagination-item:hover a {
+  color: #fff !important;
+}
+#root .ant-pagination-prev {
+  border-radius: 50% !important;
+  border: 1px solid #999;
+}
+#root .ant-pagination-prev:hover {
+  background-color: var(--themeColor);
+}
+#root .ant-pagination-prev:hover button {
+  color: #fff;
+}
+#root .ant-pagination-next {
+  border-radius: 50% !important;
+  border: 1px solid #999;
+}
+#root .ant-pagination-next:hover {
+  background-color: var(--themeColor);
+}
+#root .ant-pagination-next:hover button {
+  color: #fff;
+}
+#root .ant-pagination-disabled {
+  border: 1px solid #ccc;
+}
+#root .ant-pagination-disabled:hover {
+  background-color: transparent;
+}
+#root .tableImgAuto {
+  display: flex;
+  justify-content: center;
+}
+#root .ant-image {
+  display: none;
+}
+#root .ant-table-cell {
+  text-align: center !important;
+}
+#root .ant-table-thead .ant-table-cell {
+  background-color: var(--themeColor);
+  color: #fff;
+}
+#root .ant-picker .ant-picker-input > input {
+  text-align: center;
+}
+[hidden] {
+  display: none !important;
+}
+#upInput {
+  display: none;
+}
+#upInput2 {
+  display: none;
+}
+.pageTitle {
+  font-size: 18px;
+  font-weight: 700;
+  position: absolute;
+  z-index: 110;
+  top: -85px;
+  left: -18px;
+  padding-left: 40px;
+  color: #fff;
+  height: 70px;
+  line-height: 70px;
+}
+.mySorrl::-webkit-scrollbar {
+  /*滚动条整体样式*/
+  width: 5px;
+  /*高宽分别对应横竖滚动条的尺寸*/
+  height: 1px;
+}
+.mySorrl::-webkit-scrollbar-thumb {
+  /*滚动条里面小方块*/
+  border-radius: 10px;
+  -webkit-box-shadow: inset 0 0 5px transparent;
+  background: var(--themeColor);
+}
+.mySorrl::-webkit-scrollbar-track {
+  /*滚动条里面轨道*/
+  -webkit-box-shadow: inset 0 0 5px transparent;
+  border-radius: 10px;
+  background: transparent;
+}

+ 241 - 0
后台大场景编辑/src/assets/styles/base.less

@@ -0,0 +1,241 @@
+* {
+  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: black;
+}
+
+a {
+  text-decoration: none;
+  color: black;
+  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: auto;
+  overflow-y: overlay;
+}
+
+/* 文本域取消下拉 */
+textarea {
+  resize: none !important;
+}
+
+/* 主题色 */
+:root {
+  --themeColor: #165dff;
+}
+
+/* 找不到页面 */
+.noFindPage {
+  opacity: 0;
+  transition: opacity 0.5s;
+}
+
+/* 兼容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: auto;
+  overflow-y: overlay;
+
+  a {
+    color: var(--themeColor);
+  }
+
+  /* 普通文字按钮的颜色 */
+  .ant-btn-text {
+    color: var(--themeColor);
+  }
+
+  .ant-btn-text:disabled {
+    cursor: not-allowed;
+    color: rgba(0, 0, 0, 0.25);
+  }
+
+  /* 按钮的危险颜色 */
+  .ant-btn-text.ant-btn-dangerous {
+    color: #ff1a1a;
+  }
+
+  /* antd分页器样式 */
+  .ant-pagination .ant-pagination-item {
+    border-radius: 50%;
+    border: 1px solid #999;
+    background-color: transparent !important;
+  }
+
+  .ant-pagination .ant-pagination-item-active {
+    background-color: var(--themeColor) !important;
+  }
+
+  .ant-pagination .ant-pagination-item-active a {
+    color: #fff !important;
+  }
+
+  .ant-pagination .ant-pagination-item:hover {
+    background-color: var(--themeColor) !important;
+  }
+
+  .ant-pagination .ant-pagination-item:hover a {
+    color: #fff !important;
+  }
+
+  .ant-pagination-prev {
+    border-radius: 50% !important;
+    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% !important;
+    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;
+  }
+
+  /* 表格的图片居中 */
+  .tableImgAuto {
+    display: flex;
+    justify-content: center;
+  }
+
+  /* antd图片预览组件 */
+  .ant-image {
+    display: none;
+  }
+
+  /* antd表格居中 */
+
+  .ant-table-cell {
+    text-align: center !important;
+  }
+
+  // 表头颜色
+  .ant-table-thead .ant-table-cell {
+    background-color: var(--themeColor);
+    color: #fff;
+  }
+
+  // 时间选择器居中
+  .ant-picker .ant-picker-input > input {
+    text-align: center;
+  }
+}
+
+[hidden] {
+  display: none !important;
+}
+
+#upInput {
+  display: none;
+}
+
+#upInput2 {
+  display: none;
+}
+
+// 页面标题
+.pageTitle {
+  font-size: 18px;
+  font-weight: 700;
+  position: absolute;
+  z-index: 110;
+  top: -85px;
+  left: -18px;
+  padding-left: 40px;
+  color: #fff;
+  height: 70px;
+  line-height: 70px;
+
+  // &::before {
+  //   position: absolute;
+  //   left: 20px;
+  //   top: 50%;
+  //   transform: translateY(-50%);
+  //   content: '';
+  //   width: 6px;
+  //   height: 20px;
+  //   background-color: var(--themeColor);
+  // }
+}
+
+// 滚动条
+.mySorrl::-webkit-scrollbar {
+  /*滚动条整体样式*/
+  width: 5px;
+  /*高宽分别对应横竖滚动条的尺寸*/
+  height: 1px;
+}
+
+.mySorrl::-webkit-scrollbar-thumb {
+  /*滚动条里面小方块*/
+  border-radius: 10px;
+  -webkit-box-shadow: inset 0 0 5px transparent;
+  background: var(--themeColor);
+}
+
+.mySorrl::-webkit-scrollbar-track {
+  /*滚动条里面轨道*/
+  -webkit-box-shadow: inset 0 0 5px transparent;
+  border-radius: 10px;
+  background: transparent;
+}

+ 21 - 0
后台大场景编辑/src/components/AsyncSpinLoding/index.module.scss

@@ -0,0 +1,21 @@
+.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%);
+    }
+  }
+}

+ 15 - 0
后台大场景编辑/src/components/AsyncSpinLoding/index.tsx

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

+ 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'
+              }}
+            />
+          )
+        }
+      }}
+    />
+  )
+}

+ 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.A0layout.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;

+ 26 - 0
后台大场景编辑/src/components/NotFound/index.tsx

@@ -0,0 +1,26 @@
+import { Result } from "antd";
+import { useEffect, useRef } from "react";
+
+export default function NotFound() {
+  const timeRef = useRef(-1);
+
+  useEffect(() => {
+    timeRef.current = window.setTimeout(() => {
+      const dom: HTMLDivElement = document.querySelector(".noFindPage")!;
+      dom.style.opacity = "1";
+    }, 300);
+    return () => {
+      clearTimeout(timeRef.current);
+    };
+  }, []);
+
+  return (
+    <div className="noFindPage">
+      <Result
+        status="404"
+        title="404"
+        subTitle="页面找不到或没有权限,请联系管理员!"
+      />
+    </div>
+  );
+}

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

+ 43 - 0
后台大场景编辑/src/components/UpAsyncLoding/index.module.scss

@@ -0,0 +1,43 @@
+.UpAsyncLoding {
+  opacity: 0;
+  pointer-events: none;
+  position: fixed;
+  z-index: 10000;
+  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);
+      }
+
+    }
+
+    .closeUpBtn {
+      position: absolute;
+      top: 70%;
+      left: 50%;
+      transform: translate(-50%, -50%);
+    }
+
+  }
+}

+ 38 - 0
后台大场景编辑/src/components/UpAsyncLoding/index.tsx

@@ -0,0 +1,38 @@
+import store, { RootState } from "@/store";
+import { Button } from "antd";
+import React, { useCallback } from "react";
+import { useSelector } from "react-redux";
+import styles from "./index.module.scss";
+function UpAsyncLoding() {
+  // 从仓库中获取取消上传的函数
+  const closeUpFile = useSelector(
+    (state: RootState) => state.A0layout.closeUpFile
+  );
+
+  const btnClose = useCallback(() => {
+    closeUpFile.fu();
+
+    setTimeout(() => {
+      store.dispatch({
+        type: "layout/closeUpFile",
+        payload: { fu: () => {}, state: false },
+      });
+    }, 200);
+  }, [closeUpFile]);
+
+  return (
+    <div id="UpAsyncLoding" className={styles.UpAsyncLoding}>
+      <div className="progressBox">
+        <div id="progress"></div>
+      </div>
+      {/* 手动取消上传按钮 */}
+      <div className="closeUpBtn">
+        <Button onClick={btnClose}>取消上传</Button>
+      </div>
+    </div>
+  );
+}
+
+const MemoUpAsyncLoding = React.memo(UpAsyncLoding);
+
+export default MemoUpAsyncLoding;

+ 38 - 0
后台大场景编辑/src/index.tsx

@@ -0,0 +1,38 @@
+// 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: '#165dff'
+      }
+    }}
+  >
+    <Provider store={store}>
+      <StyleProvider
+        hashPriority='high'
+        transformers={[legacyLogicalPropertiesTransformer]}
+      >
+        <App />
+      </StyleProvider>
+    </Provider>
+  </ConfigProvider>
+)

+ 29 - 0
后台大场景编辑/src/pages/A1List/index.module.scss

@@ -0,0 +1,29 @@
+.A1List {
+  padding: 20px 30px 0;
+
+  :global {
+
+    .pageTitleToLeft{
+      left: -200px;
+    }
+
+    .tableBox {
+      margin-top: 20px;
+      height: calc(100% - 65px);
+      border: 1px solid #f0f0f0;
+      border-radius: 6px;
+
+      .ant-table-body {
+        height: 605px;
+        overflow-y: auto !important;
+        overflow-y: overlay !important;
+
+        .ant-table-row {
+          .ant-table-cell {
+            padding: 10px;
+          }
+        }
+      }
+    }
+  }
+}

+ 196 - 0
后台大场景编辑/src/pages/A1List/index.tsx

@@ -0,0 +1,196 @@
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
+import styles from './index.module.scss'
+import { Button, Input, Popconfirm, Table } from 'antd'
+import { useDispatch, useSelector } from 'react-redux'
+import { RootState } from '@/store'
+import { A1tableType } from '@/types'
+import { A1_APIdel, A1_APIgetList, isTokenFlagAPI } from '@/store/action/A1List'
+import { MessageFu } from '@/utils/message'
+import history from '@/utils/history'
+import { getTokenInfo, removeTokenInfo } from '@/utils/storage'
+import clasNames from 'classnames'
+
+const isTokenFlagFu = (val: boolean, url: string) => {
+  if (val) {
+    // token 有效
+    window.open(url)
+  } else {
+    // token 失效
+    MessageFu.warning('登录失效,请重新登录!')
+    removeTokenInfo()
+    history.push('/login')
+  }
+}
+
+function A1List() {
+  const userInfo = useMemo(() => {
+    return getTokenInfo().user
+  }, [])
+
+  const dispatch = useDispatch()
+
+  // 顶部筛选
+  const [tableSelect, setTableSelect] = useState({
+    pageNum: 1,
+    pageSize: 10,
+    searchKey: ''
+  })
+
+  const getListFu = useCallback(() => {
+    dispatch(A1_APIgetList(tableSelect))
+  }, [dispatch, tableSelect])
+
+  useEffect(() => {
+    getListFu()
+  }, [getListFu])
+
+  // 场景名称的输入
+  const nameTime = useRef(-1)
+  const nameChange = useCallback(
+    (e: React.ChangeEvent<HTMLInputElement>) => {
+      clearTimeout(nameTime.current)
+      nameTime.current = window.setTimeout(() => {
+        setTableSelect({
+          ...tableSelect,
+          searchKey: e.target.value,
+          pageNum: 1
+        })
+      }, 500)
+    },
+    [tableSelect]
+  )
+
+  const { tableInfo } = useSelector((state: RootState) => state.A1List)
+
+  // 页码变化
+  const paginationChange = useCallback(
+    () => (pageNum: number, pageSize: number) => {
+      setTableSelect({ ...tableSelect, pageNum, pageSize })
+    },
+    [tableSelect]
+  )
+
+  const lookFu = useCallback(async (code: string) => {
+    const res = await isTokenFlagAPI()
+    if (res.code === 0) {
+      isTokenFlagFu(res.data, `/webScene/index.html?m=${code}`)
+    }
+  }, [])
+
+  const editFu = useCallback(async (code: string) => {
+    const res = await isTokenFlagAPI()
+    if (res.code === 0) {
+      isTokenFlagFu(res.data, `/scene/edit.html?m=${code}`)
+    }
+  }, [])
+
+  const delTableFu = useCallback(
+    async (id: number) => {
+      const res = await A1_APIdel(id)
+      if (res.code === 0) {
+        MessageFu.success('删除成功!')
+        getListFu()
+      }
+    },
+    [getListFu]
+  )
+
+  const columns = useMemo(() => {
+    return [
+      {
+        title: '编号',
+        dataIndex: 'sceneCode'
+      },
+      {
+        title: '园区名称',
+        render: (item: A1tableType) => item.unit || '-'
+      },
+      {
+        title: '场景名称',
+        dataIndex: 'name'
+      },
+
+      {
+        title: '发布时间',
+        dataIndex: 'createTime'
+      },
+
+      {
+        title: '操作',
+        render: (item: A1tableType) => (
+          <>
+            <Button size='small' type='text' onClick={() => lookFu(item.sceneCode)}>
+              查看
+            </Button>
+
+            <Button size='small' type='text' onClick={() => editFu(item.sceneCode)}>
+              编辑
+            </Button>
+            <Popconfirm
+              title='删除后无法恢复,是否删除?'
+              okText='删除'
+              cancelText='取消'
+              okButtonProps={{ loading: false }}
+              onConfirm={() => delTableFu(item.id)}
+            >
+              <Button size='small' type='text' danger>
+                删除
+              </Button>
+            </Popconfirm>
+          </>
+        )
+      }
+    ]
+  }, [delTableFu, editFu, lookFu])
+
+  return (
+    <div className={styles.A1List}>
+      <div
+        className={clasNames(
+          'pageTitle',
+          userInfo.isAdmin !== 1 ? 'pageTitleToLeft' : ''
+        )}
+      >
+        数字孪生建模平台
+      </div>
+      <div className='A1Top'>
+        <div className='selectBox'>
+          <div className='selectBoxL'>
+            <div className='row'>
+              <span>场景名称:</span>
+              <Input
+                maxLength={30}
+                style={{ width: 300 }}
+                placeholder='请输入'
+                allowClear
+                onChange={e => nameChange(e)}
+              />
+            </div>
+          </div>
+        </div>
+      </div>
+      {/* 表格主体 */}
+      <div className='tableBox'>
+        <Table
+          scroll={{ y: 605 }}
+          dataSource={tableInfo.list}
+          columns={columns}
+          rowKey='id'
+          pagination={{
+            showQuickJumper: true,
+            position: ['bottomCenter'],
+            // showSizeChanger: true,
+            current: tableSelect.pageNum,
+            pageSize: tableSelect.pageSize,
+            total: tableInfo.total,
+            onChange: paginationChange()
+          }}
+        />
+      </div>
+    </div>
+  )
+}
+
+const MemoA1List = React.memo(A1List)
+
+export default MemoA1List

+ 44 - 0
后台大场景编辑/src/pages/A9User/UserAdd/index.module.scss

@@ -0,0 +1,44 @@
+.userAdd {
+  :global {
+    .ant-modal-close {
+      display: none;
+    }
+
+    .userAddMain {
+      border-top: 1px solid #999999;
+      padding-top: 15px;
+      width: 100%;
+
+      .passTit {
+        color: #ff4d4f;
+        font-size: 14px;
+        padding-left: 98px;
+      }
+
+      .fromBox {
+        margin-bottom: 20px;
+        display: flex;
+
+        // align-items: center;
+        .lable {
+          text-align: right;
+          width: 98.33px;
+
+          &>span {
+            position: relative;
+            top: 2px;
+            color: #ff4d4f;
+          }
+        }
+
+        .checkBox {
+          &>div {
+            margin-bottom: 5px;
+          }
+        }
+      }
+
+    }
+  }
+
+}

+ 153 - 0
后台大场景编辑/src/pages/A9User/UserAdd/index.tsx

@@ -0,0 +1,153 @@
+import {
+  A9API_getUserInfoByIdAPI,
+  A9API_userSaveAPI,
+} from "@/store/action/A9User";
+import { SaveUserType } from "@/types";
+import { MessageFu } from "@/utils/message";
+import { Button, Form, Input, Modal, Popconfirm } from "antd";
+import React, { useCallback, useEffect, useRef } from "react";
+import styles from "./index.module.scss";
+
+type Props = {
+  id: any;
+  closePage: () => void;
+  upTableList: () => void;
+  addTableList: () => void;
+};
+
+function UserAdd({ id, closePage, upTableList, addTableList }: Props) {
+  // 没有通过校验
+  const onFinishFailed = useCallback(() => {
+    // return MessageFu.warning("有表单不符号规则!");
+  }, []);
+
+  // 设置表单初始数据(区分编辑和新增)
+  const FormBoxRef = useRef<any>({});
+
+  const getInfoInAPIFu = useCallback(async (id: number) => {
+    const res = await A9API_getUserInfoByIdAPI(id);
+    FormBoxRef.current.setFieldsValue(res.data);
+  }, []);
+
+  useEffect(() => {
+    if (id) getInfoInAPIFu(id);
+    FormBoxRef.current.setFieldsValue({ myRoleName: "管理员" });
+  }, [getInfoInAPIFu, id]);
+
+  // 通过校验点击确定
+  const onFinish = useCallback(
+    async (values: any) => {
+      const obj: SaveUserType = {
+        ...values,
+        id: id ? id : null,
+        roleId: 2,
+      };
+
+      const res: any = await A9API_userSaveAPI(obj);
+
+      if (res.code === 0) {
+        MessageFu.success(id ? "编辑成功!" : "新增成功!");
+        if (id) upTableList();
+        else addTableList();
+
+        closePage();
+      }
+      // console.log("通过校验,点击确定");
+    },
+    [addTableList, closePage, id, upTableList]
+  );
+
+  return (
+    <Modal
+      wrapClassName={styles.userAdd}
+      destroyOnClose
+      open={true}
+      title={id ? "编辑用户" : "新增用户"}
+      footer={
+        [] // 设置footer为空,去掉 取消 确定默认按钮
+      }
+    >
+      <div className="userAddMain">
+        <Form
+          ref={FormBoxRef}
+          name="basic"
+          labelCol={{ span: 5 }}
+          onFinish={onFinish}
+          onFinishFailed={onFinishFailed}
+          autoComplete="off"
+        >
+          <Form.Item
+            label="用户名"
+            name="userName"
+            rules={[{ required: true, message: "请输入账号名!" }]}
+            getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
+          >
+            <Input
+              disabled={id}
+              maxLength={15}
+              showCount
+              placeholder="请输入内容"
+            />
+          </Form.Item>
+
+          <Form.Item
+            label="用户角色"
+            name="myRoleName"
+            style={{ display: "none" }}
+          >
+            <Input disabled />
+          </Form.Item>
+
+          <Form.Item
+            label="真实姓名"
+            name="realName"
+            rules={[{ required: true, message: "请输入真实姓名!" }]}
+            getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
+          >
+            <Input maxLength={8} showCount placeholder="请输入内容" />
+          </Form.Item>
+
+          <Form.Item
+            label="手机号"
+            name="phone"
+            rules={[
+              { required: true, message: "请输入手机号!" },
+              // { max: 11, min: 11, message: "长度为11!" },
+              {
+                pattern: /^1[3-9][0-9]{9}$/,
+                message: "请输入正确格式的手机号!",
+              },
+            ]}
+            getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
+          >
+            <Input maxLength={11} showCount placeholder="请输入11位手机号" />
+          </Form.Item>
+
+          {id ? null : <div className="passTit">* 默认密码 123456</div>}
+
+          {/* 确定和取消按钮 */}
+          <br />
+          <Form.Item wrapperCol={{ offset: 9, span: 16 }}>
+            <Button type="primary" htmlType="submit">
+              提交
+            </Button>
+            &emsp;
+            <Popconfirm
+              title="放弃编辑后,信息将不会保存!"
+              okText="放弃"
+              cancelText="取消"
+              okButtonProps={{ loading: false }}
+              onConfirm={closePage}
+            >
+              <Button>取消</Button>
+            </Popconfirm>
+          </Form.Item>
+        </Form>
+      </div>
+    </Modal>
+  );
+}
+
+const MemoUserAdd = React.memo(UserAdd);
+
+export default MemoUserAdd;

+ 42 - 0
后台大场景编辑/src/pages/A9User/index.module.scss

@@ -0,0 +1,42 @@
+.A9User {
+  padding: 20px 30px 0;
+
+  :global {
+    .userTop {
+
+      .selectBox {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+
+        .row {
+          margin-right: 20px;
+        }
+
+        .selectBoxL {
+          display: flex;
+          align-items: center;
+        }
+      }
+    }
+
+    .tableBox {
+      margin-top: 20px;
+      height: calc(100% - 65px);
+      border: 1px solid #f0f0f0;
+      border-radius: 6px;
+
+      .ant-table-body {
+        height: 605px;
+        overflow-y: auto !important;
+        overflow-y: overlay !important;
+
+        .ant-table-row {
+          .ant-table-cell {
+            padding: 10px;
+          }
+        }
+      }
+    }
+  }
+}

+ 277 - 0
后台大场景编辑/src/pages/A9User/index.tsx

@@ -0,0 +1,277 @@
+import { RootState } from "@/store";
+import {
+  A9API_getUserListAPI,
+  A9API_userDisplayAPI,
+  A9API_userPassResetAPI,
+  A9API_userRemoveAPI,
+} from "@/store/action/A9User";
+import { UserTableAPIType, UserTableListType } from "@/types";
+import { MessageFu } from "@/utils/message";
+import { Input, Button, Table, Switch, Popconfirm } from "antd";
+import React, {
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
+import { useDispatch, useSelector } from "react-redux";
+import styles from "./index.module.scss";
+import UserAdd from "./UserAdd";
+
+function A9User() {
+  const dispatch = useDispatch();
+
+  // 顶部筛选
+  const [tableSelect, setTableSelect] = useState<UserTableAPIType>({
+    pageNum: 1,
+    pageSize: 10,
+    realName: "",
+  });
+
+  // 封装发送请求的函数
+
+  const getList = useCallback(async () => {
+    dispatch(A9API_getUserListAPI(tableSelect));
+  }, [dispatch, tableSelect]);
+
+  // 防止发送了2次请求来对应页码
+
+  const getListRef = useRef(-1);
+
+  useEffect(() => {
+    clearTimeout(getListRef.current);
+    getListRef.current = window.setTimeout(() => {
+      getList();
+    }, 100);
+  }, [getList, tableSelect]);
+
+  // 真实姓名的输入
+  const realNameTime = useRef(-1);
+  const realNameChange = useCallback(
+    (e: React.ChangeEvent<HTMLInputElement>) => {
+      clearTimeout(realNameTime.current);
+      realNameTime.current = window.setTimeout(() => {
+        setTableSelect({
+          ...tableSelect,
+          realName: e.target.value,
+          pageNum: 1,
+        });
+      }, 500);
+    },
+    [tableSelect]
+  );
+
+  // 点击重置
+  const [inputKey, setInputKey] = useState(1);
+  const resetSelectFu = useCallback(() => {
+    // 把2个输入框和时间选择器清空
+    setInputKey(Date.now());
+    setTableSelect({
+      pageNum: 1,
+      pageSize: 10,
+      realName: "",
+    });
+  }, []);
+
+  // 从仓库中获取表格数据
+  const tableInfo = useSelector((state: RootState) => state.A9User.tableInfo);
+
+  // 页码变化
+  const paginationChange = useCallback(
+    () => (pageNum: number, pageSize: number) => {
+      setTableSelect({ ...tableSelect, pageNum, pageSize });
+    },
+    [tableSelect]
+  );
+
+  // 切换表格中的启用停用状态
+  const isEnabledClickFu = useCallback(
+    async (val: boolean, id: number) => {
+      const isDisable = val ? 1 : 0;
+      const res: any = await A9API_userDisplayAPI(id, isDisable);
+      if (res.code === 0) getList();
+    },
+    [getList]
+  );
+
+  // 点击删除
+  const delTableFu = useCallback(
+    async (id: number) => {
+      const res: any = await A9API_userRemoveAPI(id);
+      if (res.code === 0) {
+        MessageFu.success("删除成功!");
+        getList();
+      }
+    },
+    [getList]
+  );
+
+  // 点击重置密码
+  const resetPassFu = useCallback(async (id: number) => {
+    const res: any = await A9API_userPassResetAPI(id);
+    if (res.code === 0) MessageFu.success("重置成功!");
+  }, []);
+
+  // 0------------点击新增或者编辑出来的页面
+  const [editPageShow, setEditPageShow] = useState(false);
+  const editId = useRef(0);
+
+  const openEditPageFu = useCallback(
+    (id: number) => {
+      if (id === 0 && tableInfo.list.length >= 30)
+        return MessageFu.warning("最多支持30个用户!");
+
+      editId.current = id;
+      setEditPageShow(true);
+    },
+    [tableInfo.list.length]
+  );
+
+  const columns = useMemo(() => {
+    return [
+      // {
+      //   width: 80,
+      //   title: "序号",
+      //   render: (text: any, record: any, index: any) =>
+      //     index + 1 + (pageNumRef.current - 1) * pagePageRef.current,
+      // },
+      {
+        title: "用户名",
+        dataIndex: "userName",
+      },
+      // {
+      //   title: "用户角色",
+      //   render: (item: UserTableListType) =>
+      //     item.isAdmin === 1 ? "超级管理员" : "管理员",
+      // },
+      {
+        title: "真实姓名",
+        dataIndex: "realName",
+      },
+      {
+        title: "手机号",
+        dataIndex: "phone",
+      },
+      {
+        title: "注册时间",
+        dataIndex: "createTime",
+      },
+
+      {
+        title: "启用状态",
+        render: (item: UserTableListType) => (
+          <Switch
+            disabled={item.isAdmin === 1}
+            checkedChildren="启用"
+            unCheckedChildren="停用"
+            checked={item.isEnabled === 1}
+            onChange={(val) => isEnabledClickFu(val, item.id!)}
+          />
+        ),
+      },
+
+      {
+        title: "操作",
+        render: (item: UserTableListType) => {
+          return item.isAdmin === 1 ? (
+            "-"
+          ) : (
+            <>
+              <Popconfirm
+                title="密码重制后为123456,是否重置?"
+                okText="重置"
+                cancelText="取消"
+                okButtonProps={{ loading: false }}
+                onConfirm={() => resetPassFu(item.id!)}
+              >
+                <Button size="small" type="text">
+                  重置密码
+                </Button>
+              </Popconfirm>
+
+              <Button
+                size="small"
+                type="text"
+                onClick={() => openEditPageFu(item.id!)}
+              >
+                编辑
+              </Button>
+              <Popconfirm
+                title="删除后无法恢复,是否删除?"
+                okText="删除"
+                cancelText="取消"
+                okButtonProps={{ loading: false }}
+                onConfirm={() => delTableFu(item.id!)}
+              >
+                <Button size="small" type="text" danger>
+                  删除
+                </Button>
+              </Popconfirm>
+            </>
+          );
+        },
+      },
+    ];
+  }, [delTableFu, isEnabledClickFu, openEditPageFu, resetPassFu]);
+
+  return (
+    <div className={styles.A9User}>
+      <div className="pageTitle">用户管理</div>
+      <div className="userTop">
+        <div className="selectBox">
+          <div className="selectBoxL">
+            <div className="row">
+              <span>真实姓名:</span>
+              <Input
+                key={inputKey}
+                maxLength={8}
+                style={{ width: 200 }}
+                placeholder="请输入"
+                allowClear
+                onChange={(e) => realNameChange(e)}
+              />
+            </div>
+          </div>
+          <div className="selectBoxR">
+            <Button type="primary" onClick={() => openEditPageFu(0)}>
+              新增
+            </Button>
+          </div>
+        </div>
+      </div>
+      {/* 表格主体 */}
+      <div className="tableBox">
+        <Table
+          scroll={{ y: 605 }}
+          dataSource={tableInfo.list}
+          columns={columns}
+          rowKey="id"
+          pagination={{
+            showQuickJumper: true,
+            position: ["bottomCenter"],
+            // showSizeChanger: true,
+            current: tableSelect.pageNum,
+            pageSize: tableSelect.pageSize,
+            total: tableInfo.total,
+            onChange: paginationChange(),
+          }}
+        />
+      </div>
+
+      {/* 点击新增或者编辑 */}
+      {editPageShow ? (
+        <UserAdd
+          id={editId.current}
+          closePage={() => setEditPageShow(false)}
+          upTableList={getList}
+          addTableList={resetSelectFu}
+        />
+      ) : null}
+    </div>
+  );
+}
+
+const MemoA9User = React.memo(A9User);
+
+export default MemoA9User;

+ 161 - 0
后台大场景编辑/src/pages/Layout/index.module.scss

@@ -0,0 +1,161 @@
+.Layout {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  overflow: hidden;
+  position: relative;
+
+  :global {
+    .layoutLeft {
+      position: relative;
+      z-index: 30;
+      width: 220px;
+      height: 100%;
+      background-color: #e9f2f9;
+
+      .layoutLeftTop {
+        height: 70px;
+        background-color: var(--themeColor);
+        display: flex;
+        justify-content: center;
+        align-items: center;
+
+        & > img {
+          width: 180px;
+        }
+      }
+
+      .layoutLeftMain {
+        height: calc(100% - 70px);
+
+        .mainBoxL2Row {
+          cursor: pointer;
+          height: 50px;
+          line-height: 50px;
+          font-size: 16px;
+          position: relative;
+          margin-top: 16px;
+          color: #fff;
+          text-align: center;
+
+          &:hover {
+            color: #fff;
+            background-color: var(--themeColor);
+          }
+        }
+
+        .active {
+          color: #fff;
+          pointer-events: none;
+          background-color: var(--themeColor);
+        }
+      }
+    }
+
+    .layoutRight {
+      width: calc(100% - 220px);
+      height: 100%;
+
+      .layoutRightTop {
+        height: 70px;
+        background-color: var(--themeColor);
+        display: flex;
+        justify-content: flex-end;
+        position: relative;
+        z-index: 30;
+
+        .user {
+          margin-right: 40px;
+          padding-right: 40px;
+          display: flex;
+          align-items: center;
+          padding-left: 55px;
+          cursor: pointer;
+          position: relative;
+          // background: url('../../assets/img/user.png') no-repeat left center;
+          // background-size: 40px 40px;
+          font-size: 16px;
+          color: #fff;
+
+          .userInco {
+            margin-left: 10px;
+            color: #fff;
+          }
+
+          .userInco1 {
+            display: none;
+          }
+
+          .userSet {
+            border-radius: 10px;
+            overflow: hidden;
+            width: 140px;
+            opacity: 0;
+            pointer-events: none;
+            transition: bottom 0.3s;
+            height: 120px;
+            position: absolute;
+            left: 50%;
+            transform: translateX(-50%);
+            bottom: -80px;
+            padding-top: 20px;
+            color: rgb(226, 223, 223);
+
+            & > span {
+              background-color: var(--themeColor);
+              display: block;
+              width: 100%;
+              height: 50px;
+              line-height: 50px;
+              text-align: center;
+              border-radius: 10px;
+
+              &:first-child {
+                border-radius: 10px 10px 0 0;
+              }
+
+              &:hover {
+                color: #fff;
+              }
+            }
+          }
+
+          &:hover {
+            .userSet {
+              opacity: 1;
+              pointer-events: auto;
+              bottom: -110px;
+            }
+
+            .userInco1 {
+              display: block;
+            }
+
+            .userInco2 {
+              display: none;
+            }
+          }
+        }
+      }
+
+      .layoutRightMain {
+        height: calc(100% - 70px);
+        padding: 15px 20px;
+        background-color: #eef0f4;
+
+        .mainBoxR {
+          width: 100%;
+          height: 100%;
+          background-color: #fff;
+          border-radius: 10px;
+
+          & > div {
+            width: 100%;
+            height: 100%;
+            position: relative;
+          }
+        }
+      }
+    }
+  }
+}

+ 225 - 0
后台大场景编辑/src/pages/Layout/index.tsx

@@ -0,0 +1,225 @@
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
+import { CaretUpOutlined, CaretDownOutlined } from '@ant-design/icons'
+import styles from './index.module.scss'
+import SpinLoding from '@/components/SpinLoding'
+import { Route, Switch, useLocation } from 'react-router-dom'
+import AuthRoute from '@/components/AuthRoute'
+import classNames from 'classnames'
+import history from '@/utils/history'
+import { Button, Form, Input, Modal, Popconfirm } from 'antd'
+import { Base64 } from 'js-base64'
+import encodeStr from '@/utils/pass'
+import { passWordEditAPI } from '@/store/action/layout'
+import { getTokenInfo, removeTokenInfo } from '@/utils/storage'
+import { MessageFu } from '@/utils/message'
+import logonImg from '@/assets/img/logo.png'
+
+const NotFound = React.lazy(() => import('@/components/NotFound'))
+
+function Layout() {
+  const list = useMemo(() => {
+    return [
+      {
+        id: 100,
+        name: '场景管理',
+        path: '/list',
+        Com: React.lazy(() => import('../A1List'))
+      }
+    ]
+  }, [])
+
+  useEffect(() => {
+    // 如果是超级管理员
+    const userInfo = getTokenInfo().user
+    if (userInfo.isAdmin === 1) {
+      list.push({
+        id: 900,
+        name: '用户管理',
+        path: '/user',
+        Com: React.lazy(() => import('../A9User'))
+      })
+    }
+  }, [list])
+
+  // 点击跳转
+  const pathCutFu = useCallback((path: string) => {
+    history.push(path)
+  }, [])
+
+  // 当前路径选中的左侧菜单
+  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 [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')
+  }
+
+  return (
+    <div className={styles.Layout}>
+      {/* 左边 */}
+      <div className='layoutLeft'>
+        <div className='layoutLeftTop'>
+          {userInfo.isAdmin === 1 ? <img src={logonImg} alt='' /> : null}
+        </div>
+        {/* 左边主体 */}
+        <div className='layoutLeftMain'>
+          {list.map(v => (
+            <div
+              key={v.id}
+              onClick={() => pathCutFu(v.path)}
+              className={classNames('mainBoxL2Row', v.path === path ? 'active' : '')}
+            >
+              <div className='txt'>{v.name}</div>
+            </div>
+          ))}
+        </div>
+      </div>
+      {/* 右边 */}
+      <div className='layoutRight'>
+        <div className='layoutRightTop'>
+          {/* 用户相关 */}
+          <div className='user'>
+            {userInfo.realName}
+            <div className='userInco userInco1'>
+              <CaretUpOutlined />
+            </div>
+            <div className='userInco userInco2'>
+              <CaretDownOutlined />
+            </div>
+            <div className='userSet'>
+              <span onClick={() => setOpen(true)} hidden>
+                修改密码
+              </span>
+              <Popconfirm
+                placement='bottom'
+                title='确定退出吗?'
+                okText='确定'
+                cancelText='取消'
+                okButtonProps={{ loading: false }}
+                onConfirm={loginExit}
+              >
+                退出登录
+              </Popconfirm>
+            </div>
+          </div>
+        </div>
+        {/* 右边主体 */}
+        <div className='layoutRightMain'>
+          {/* 二级路由页面 */}
+          <div className='mainBoxR'>
+            <React.Suspense fallback={<SpinLoding />}>
+              <Switch>
+                {list.map(v => (
+                  <AuthRoute key={v.id} exact path={v.path} component={v.Com} />
+                ))}
+
+                <Route path='*' component={NotFound} />
+              </Switch>
+            </React.Suspense>
+          </div>
+        </div>
+      </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: '不能为空!' },
+              { min: 6, max: 15, message: '密码长度为6-15个字符!' }
+            ]}
+          >
+            <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>
+  )
+}
+
+// 使用 React.memo 来优化组件,避免组件的无效更新,类似 类组件里面的PureComponent
+const MemoLayout = React.memo(Layout)
+export default MemoLayout

+ 137 - 0
后台大场景编辑/src/pages/Login copy/index.module.scss

@@ -0,0 +1,137 @@
+.Login {
+  width: 100%;
+  height: 100%;
+  background-image: url('../../assets/img/loginBg.jpg');
+  background-size: cover;
+  position: relative;
+  position: relative;
+
+  &::before {
+    content: '';
+    position: absolute;
+    z-index: 9;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    background-color: rgba(0, 0, 0, .7);
+  }
+
+
+  :global {
+    .main {
+      z-index: 10;
+      position: absolute;
+      top: 48%;
+      left: 50%;
+      transform: translate(-50%, -50%);
+      width: 400px;
+      text-align: center;
+
+      &>h3 {
+        font-size: 24px;
+        letter-spacing: 6px;
+        color: #fff;
+      }
+
+      .inputBox {
+        width: 100%;
+
+        input::-webkit-input-placeholder {
+          /* WebKit browsers */
+          color: rgba(255, 255, 255, .5);
+        }
+
+        input:-moz-placeholder {
+          /* Mozilla Firefox 4 to 18 */
+          color: rgba(255, 255, 255, .5);
+        }
+
+        input::-moz-placeholder {
+          /* Mozilla Firefox 19+ */
+          color: rgba(255, 255, 255, .5);
+        }
+
+        input:-ms-input-placeholder {
+          /* Internet Explorer 10+ */
+          color: rgba(255, 255, 255, .5);
+        }
+
+
+        .inputBoxRow {
+          margin-top: 30px;
+
+          .ant-input-suffix .ant-input-password-icon {
+            color: #fff;
+            font-size: 22px;
+          }
+        }
+
+        .ant-input-prefix {
+          margin-right: 10px;
+
+          .anticon {
+            padding-right: 10px;
+            width: 36px;
+            height: 36px;
+
+            svg {
+              width: 100%;
+              height: 100%;
+            }
+          }
+        }
+
+        .ant-input {
+          color: #fff;
+          font-size: 18px;
+          width: 45%;
+          height: 60px;
+          line-height: 60px;
+          background-clip: content-box;
+        }
+
+        input:-webkit-autofill {
+          font-size: 18px !important;
+          -webkit-text-fill-color: #fff !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 {
+          background-color: transparent;
+          padding: 0 11px;
+          width: 100%;
+          height: 60px;
+          border: none;
+          border: 1px solid #fff;
+          color: #fff;
+
+          .ant-input {
+            background-color: transparent;
+            width: 100%;
+            height: 60px;
+          }
+        }
+
+        // .ant-input-affix-wrapper-focused {
+        //   box-shadow: none
+        // }
+      }
+
+      .loginBtn {
+        margin-top: 30px;
+
+        .ant-btn {
+          width: 100%;
+          font-size: 20px;
+          height: 50px;
+        }
+      }
+
+    }
+  }
+}

+ 78 - 0
后台大场景编辑/src/pages/Login copy/index.tsx

@@ -0,0 +1,78 @@
+import styles from "./index.module.scss";
+
+import { Input, Button } from "antd";
+import { UserOutlined, LockOutlined } from "@ant-design/icons";
+import { useState } from "react";
+import { Base64 } from "js-base64";
+import encodeStr from "@/utils/pass";
+import { setTokenInfo } from "@/utils/storage";
+import history from "@/utils/history";
+import { MessageFu } from "@/utils/message";
+import { userLoginAPI } from "@/store/action/layout";
+
+export default function Login() {
+  // 账号密码
+  const [userName, setUserName] = useState("admin");
+  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("/");
+    } else if (res.code === 3014)
+      MessageFu.warning("用户名不存在或密码错误,请联系管理员!");
+  };
+
+  return (
+    <div className={styles.Login}>
+      <div className="main">
+        <h3>本地大场景管理</h3>
+        {/* 账号密码输入框 */}
+        <div className="inputBox">
+          <div className="inputBoxRow">
+            <Input
+              onKeyUp={(e) => keyUpEntFu(e)}
+              value={userName}
+              onChange={(e) => setUserName(e.target.value.trim())}
+              prefix={<UserOutlined />}
+              placeholder="请输入用户名"
+              maxLength={15}
+            />
+          </div>
+          <div className="inputBoxRow">
+            <Input.Password
+              onKeyUp={(e) => keyUpEntFu(e)}
+              value={passWord}
+              onChange={(e) => setPassWord(e.target.value.trim())}
+              prefix={<LockOutlined />}
+              placeholder="请输入用户密码"
+              maxLength={15}
+            />
+          </div>
+        </div>
+
+        {/* 登录按钮 */}
+        <div className="loginBtn">
+          <Button type="primary" size="large" onClick={loginClickFu}>
+            登 录
+          </Button>
+        </div>
+      </div>
+    </div>
+  );
+}

+ 41 - 0
后台大场景编辑/src/pages/Login/index.module.scss

@@ -0,0 +1,41 @@
+.Login {
+  width: 100%;
+  height: 100%;
+  background-image: url('../../assets/img/loginBg.jpg');
+  background-size: cover;
+
+  &::before {
+    content: '';
+    position: absolute;
+    z-index: 9;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    background-color: rgba(0, 0, 0, 0.7);
+  }
+  :global {
+    .LoginBtn {
+      position: absolute;
+      top: 55%;
+      left: 50%;
+      transform: translate(-50%, -50%);
+      z-index: 10;
+    }
+
+    .codeLoding {
+      position: absolute;
+      z-index: 100;
+      width: 100%;
+      height: 100%;
+      top: 0;
+      left: 0;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      color: #fff;
+      font-size: 24px;
+      letter-spacing: 4px;
+    }
+  }
+}

+ 75 - 0
后台大场景编辑/src/pages/Login/index.tsx

@@ -0,0 +1,75 @@
+import React, { useCallback, useEffect, useState } from 'react'
+import styles from './index.module.scss'
+import { Button } from 'antd'
+import { API_login, isTokenFlagAPI } from '@/store/action/A1List'
+import { MessageFu } from '@/utils/message'
+import { setTokenInfo } from '@/utils/storage'
+import history from '@/utils/history'
+import { isLocal } from '@/utils/http'
+function Login() {
+  const loginFu = useCallback(() => {
+    window.location.href = threeUrl
+  }, [])
+
+  const [code, setCode] = useState('')
+
+  const getUserInfo = useCallback(async (code: string) => {
+    setCode(code)
+    try {
+      const res = await API_login({ code, redirectUrl: backUrl })
+      if (res.code === 0) {
+        if (res.data.token) {
+          MessageFu.success('登录成功')
+          // 用户信息存到本地
+          setTokenInfo(res.data)
+          window.location.href = `${isLocal ? '' : '/backstage'}/#/list`
+        } else {
+          MessageFu.warning('token为空')
+          setCode('')
+        }
+      } else setCode('')
+    } catch (error) {
+      setCode('')
+    }
+  }, [])
+
+  const checkTokenFu = useCallback(async () => {
+    // 检查token有效直接跳list页面
+    const res = await isTokenFlagAPI()
+    if (res.code === 0) {
+      if (res.data) {
+        history.replace('/list')
+      }
+    }
+  }, [])
+
+  useEffect(() => {
+    // 创建 URLSearchParams 对象解析查询字符串
+    const searchParams = new URLSearchParams(window.location.search)
+    const code = searchParams.get('code') // 获取 code 参数
+    if (code) {
+      getUserInfo(code)
+    } else {
+      checkTokenFu()
+    }
+  }, [checkTokenFu, getUserInfo])
+
+  return (
+    <div className={styles.Login}>
+      <div className='LoginBtn'>
+        <Button type='primary' size='large' onClick={loginFu} hidden={!!code}>
+          孪生数字底座平台授权登录
+        </Button>
+      </div>
+
+      {/* 获取用户信息加载中 */}
+      <div className='codeLoding' hidden={!code}>
+        授权中,请稍后...
+      </div>
+    </div>
+  )
+}
+
+const MemoLogin = React.memo(Login)
+
+export default MemoLogin

+ 6 - 0
后台大场景编辑/src/pages/初始化组件/index.module.scss

@@ -0,0 +1,6 @@
+.AAAAA{
+  background-color: aqua;
+  :global{
+    background-color: red;
+  }
+}

+ 14 - 0
后台大场景编辑/src/pages/初始化组件/index.tsx

@@ -0,0 +1,14 @@
+import React from "react";
+import styles from "./index.module.scss";
+ function AAAAA() {
+  
+  return (
+    <div className={styles.AAAAA}>
+      <h1>AAAAA</h1>
+    </div>
+  )
+}
+
+const MemoAAAAA = React.memo(AAAAA);
+
+export default MemoAAAAA;

+ 41 - 0
后台大场景编辑/src/store/action/A1List.ts

@@ -0,0 +1,41 @@
+import http from '@/utils/http'
+import { AppDispatch } from '..'
+/**
+ * 获取用户管理表格列表
+ */
+export const A1_APIgetList = (data: any): any => {
+  return async (dispatch: AppDispatch) => {
+    const res = await http.post('cms/scene/list', data)
+    if (res.code === 0) {
+      const obj = {
+        list: res.data.records,
+        total: res.data.total
+      }
+
+      dispatch({ type: 'A1/getList', payload: obj })
+    }
+  }
+}
+
+/**
+ * 删除
+ */
+export const A1_APIdel = (id: number) => {
+  return http.get(`cms/scene/removes/${id}`)
+}
+
+/**
+ * 检查登录状态
+ */
+export const isTokenFlagAPI = () => {
+  return http.get('admin/checkLogin')
+}
+
+// -------------第三方平台授权登录------------
+
+/**
+ * 通过code获取token和别的用户信息
+ */
+export const API_login = (data: any) => {
+  return http.post('admin/ssoLogin', data)
+}

+ 54 - 0
后台大场景编辑/src/store/action/A9User.ts

@@ -0,0 +1,54 @@
+import { SaveUserType, UserTableAPIType } from '@/types'
+import http from '@/utils/http'
+import { AppDispatch } from '..'
+/**
+ * 获取用户管理表格列表
+ */
+export const A9API_getUserListAPI = (data: UserTableAPIType): any => {
+  return async (dispatch: AppDispatch) => {
+    const res = await http.post('sys/user/list', data)
+    if (res.code === 0) {
+      const obj = {
+        list: res.data.records,
+        total: res.data.total
+      }
+
+      dispatch({ type: 'user/getList', payload: obj })
+    }
+  }
+}
+
+/**
+ * 用户-是否显示
+ */
+export const A9API_userDisplayAPI = (id: number, display: number) => {
+  return http.get(`sys/user/editStatus/${id}/${display}`)
+}
+
+/**
+ * 删除用户
+ */
+export const A9API_userRemoveAPI = (id: number) => {
+  return http.get(`sys/user/removes/${id}`)
+}
+
+/**
+ * 重置密码
+ */
+export const A9API_userPassResetAPI = (id: number) => {
+  return http.get(`sys/user/resetPass/${id}`)
+}
+
+/**
+ * 新增/修改用户信息
+ */
+export const A9API_userSaveAPI = (data: SaveUserType) => {
+  return http.post('sys/user/save', data)
+}
+
+/**
+ * 通过id获取角色详情
+ */
+export const A9API_getUserInfoByIdAPI = (id: number) => {
+  return http.get(`sys/user/detail/${id}`)
+}

+ 17 - 0
后台大场景编辑/src/store/action/layout.ts

@@ -0,0 +1,17 @@
+import http from "@/utils/http";
+// import { AppDispatch } from "..";
+
+/**
+ * 用户登录接口
+ */
+export const userLoginAPI = (data: any) => {
+  return http.post("admin/login", { ...data });
+};
+
+/**
+ * 修改密码接口
+ */
+export const passWordEditAPI = (data: any) => {
+  return http.post("sys/user/updatePwd", { ...data });
+};
+

+ 20 - 0
后台大场景编辑/src/store/index.ts

@@ -0,0 +1,20 @@
+// 导入 redux
+import { applyMiddleware, legacy_createStore as createStore } from 'redux'
+// 导入自己封装的  rootReducer 
+import rootReducer from './reducer'
+// 导入调试工具和 异步的 redux(用来发送异步请求)
+// 调试工具需要下载谷歌 扩展程序 我用的是 Redux DevTools 3.0.17
+import { composeWithDevTools } from 'redux-devtools-extension'
+import thunk from 'redux-thunk'
+
+// 创建仓库实例
+const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(thunk)))
+
+// 声明 RootState,在使用仓库的时候用来使用
+export type RootState = ReturnType<typeof store.getState>
+
+// 声明 AppDispatch,在异步请求的时候来使用
+export type AppDispatch = typeof store.dispatch
+
+// 导出仓库实例
+export default store

+ 27 - 0
后台大场景编辑/src/store/reducer/A1List.ts

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

+ 27 - 0
后台大场景编辑/src/store/reducer/A9User.ts

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

+ 16 - 0
后台大场景编辑/src/store/reducer/index.ts

@@ -0,0 +1,16 @@
+import { combineReducers } from "redux";
+
+import A0layout from "./layout";
+import A1List from "./A1List";
+
+import A9User from "./A9User";
+
+// 合并 reducer
+const rootReducer = combineReducers({
+  A0layout,
+  A1List,
+  A9User,
+});
+
+// 默认导出
+export default rootReducer;

+ 39 - 0
后台大场景编辑/src/store/reducer/layout.ts

@@ -0,0 +1,39 @@
+import { MessageType } from "@/utils/message";
+
+type CloseUpFileType = {
+  fu: () => void;
+  state: boolean;
+};
+
+// 初始化状态
+const initState = {
+  message: {
+    txt: "",
+    type: "info",
+    duration: 3,
+  } as MessageType,
+  // 上传文件点击取消
+  closeUpFile: {
+    fu: () => {},
+    state: false,
+  },
+};
+
+// 定义 action 类型
+type Props =
+  | { type: "layout/message"; payload: MessageType }
+  | { type: "layout/closeUpFile"; payload: CloseUpFileType };
+
+// 频道 reducer
+export default function layoutReducer(state = initState, action: Props) {
+  switch (action.type) {
+    // antd轻提示(兼容360浏览器)
+    case "layout/message":
+      return { ...state, message: action.payload };
+    // 上传文件点击取消
+    case "layout/closeUpFile":
+      return { ...state, closeUpFile: action.payload };
+    default:
+      return state;
+  }
+}

+ 14 - 0
后台大场景编辑/src/types/api/A1List.d.ts

@@ -0,0 +1,14 @@
+export type A1tableType = {
+  createTime: string
+  creatorName: string
+  display: number
+  id: number
+  name: string
+  path: string
+  sceneCode: string
+  star: number
+  updateTime: string
+  visit: number
+  webSite: string
+  unit: string
+}

+ 33 - 0
后台大场景编辑/src/types/api/A9User.d.ts

@@ -0,0 +1,33 @@
+export type UserTableAPIType={
+  pageNum:number
+  pageSize:number
+  realName:string
+}
+
+export type UserTableListType={
+  createTime: string;
+  creatorId: null;
+  creatorName: string;
+  id: number;
+  isAdmin: number;
+  isEnabled: number;
+  nickName: string;
+  phone: string;
+  realName: string;
+  roleId: null;
+  roleName: string;
+  sex: string;
+  thumb: string;
+  updateTime: string;
+  userName: string;
+}
+
+export type SaveUserType ={
+  id:number|null
+  userName:string
+  nickName:string
+  roleId:number
+  realName:string
+  modulePerms:string
+  exhibitionIds:string
+}

+ 8 - 0
后台大场景编辑/src/types/declaration.d.ts

@@ -0,0 +1,8 @@
+declare module 'history'
+declare module '*.scss'
+declare module '*.png'
+declare module '*.jpg'
+declare module '*.gif'
+declare module 'js-export-excel'
+declare const backUrl: string
+declare const threeUrl: string

+ 3 - 0
后台大场景编辑/src/types/index.d.ts

@@ -0,0 +1,3 @@
+export * from './api/A1List'
+export * from './api/A9User'
+

+ 8 - 0
后台大场景编辑/src/utils/dataChange.ts

@@ -0,0 +1,8 @@
+export const A1DataChangeObj ={
+  'visit_hot':'热点访问量',
+  'visit_product':'产品手册下载量',
+  'visit_questionnaire':'收集问卷',
+  'visit_scene':'线上展厅访问量',
+  'visit_user':'访客量',
+  'visit_zone':'导航区域访问量',
+}

+ 35 - 0
后台大场景编辑/src/utils/domShow.ts

@@ -0,0 +1,35 @@
+import store from "@/store";
+
+// 加载和上传的盒子的显示隐藏
+export const domShowFu = (ele: string, val: boolean) => {
+  const dom: HTMLDivElement = document.querySelector(ele)!;
+  if (val) {
+    dom.style.opacity = "1";
+    dom.style.pointerEvents = "auto";
+  } else {
+    dom.style.opacity = "0";
+    dom.style.pointerEvents = "none";
+  }
+};
+
+// 上传附件的进度条
+let progressDom: HTMLDivElement = document.querySelector("#progress")!;
+export const progressDomFu = (val: string) => {
+  if (!progressDom) progressDom = document.querySelector("#progress")!;
+  progressDom.style.width = val;
+};
+
+// 上传附件的dom操作
+export const fileDomInitialFu = () => {
+  // 隐藏进度条的dom
+  domShowFu("#UpAsyncLoding", false);
+  progressDomFu("0%");
+  // 初始化 上传附件 的状态
+  setTimeout(() => {
+    if (store.getState().A0layout.closeUpFile.state)
+      store.dispatch({
+        type: "layout/closeUpFile",
+        payload: { fu: () => {}, state: false },
+      });
+  }, 200);
+};

+ 17 - 0
后台大场景编辑/src/utils/history.ts

@@ -0,0 +1,17 @@
+import { createHashHistory  } from 'history'
+const history = createHashHistory()
+export default history
+
+export const urlParameter = (data: string) => {
+  if (data) {
+    const query = data.substring(data.indexOf("?") + 1);
+    const arr = query.split("&");
+    const params = {} as any;
+    arr.forEach((v) => {
+      const key = v.substring(0, v.indexOf("="));
+      const val = v.substring(v.indexOf("=") + 1);
+      params[key] = val;
+    });
+    return params;
+  } else return {};
+};

+ 93 - 0
后台大场景编辑/src/utils/http.ts

@@ -0,0 +1,93 @@
+import axios from 'axios'
+import history from './history'
+import { getTokenInfo, removeTokenInfo } from './storage'
+import store from '@/store'
+import { MessageFu } from './message'
+import { domShowFu } from './domShow'
+
+export const isLocal = process.env.NODE_ENV === 'development'
+
+// 请求基地址
+export const baseURL =
+  // 线下的图片地址需要加上/api/
+  // isLocal ? 'http://192.168.20.61:8102/api/' : ''
+  isLocal ? 'https://sit-beirenbigscene.4dage.com' : ''
+
+// 处理  类型“AxiosResponse<any, any>”上不存在属性“code”
+declare module 'axios' {
+  interface AxiosResponse {
+    code: number
+    // 这里追加你的参数
+  }
+}
+
+// 创建 axios 实例
+const http = axios.create({
+  // --------线下的地址不用加/api/
+  // baseURL: baseURL,
+
+  // --------打包或线上环境接口需要加上api/
+  baseURL: baseURL + '/api/',
+  timeout: 10000
+})
+
+let axajInd = 0
+
+// 请求拦截器
+http.interceptors.request.use(
+  function (config: any) {
+    // 发请求前打开加载提示
+    domShowFu('#AsyncSpinLoding', true)
+
+    axajInd++
+
+    const { token } = getTokenInfo()
+    if (token) config.headers.token = token
+    return config
+  },
+  function (err) {
+    return Promise.reject(err)
+  }
+)
+
+let timeId = -1
+
+// 响应拦截器
+http.interceptors.response.use(
+  function (response) {
+    // 请求回来的关闭加载提示
+    axajInd--
+    if (axajInd === 0) {
+      domShowFu('#AsyncSpinLoding', false)
+    }
+    if (response.data.code === 5001 || response.data.code === 5002) {
+      clearTimeout(timeId)
+      timeId = window.setTimeout(() => {
+        removeTokenInfo()
+        MessageFu.warning('登录失效!')
+        history.push('/login')
+      }, 200)
+    } else if (response.data.code === 0) {
+      // MessageFu.success(response.data.msg);
+    } else MessageFu.warning(response.data.msg)
+
+    return response.data
+  },
+  async function (err) {
+    clearTimeout(timeId)
+    timeId = window.setTimeout(() => {
+      axajInd = 0
+      domShowFu('#AsyncSpinLoding', false)
+      // 如果因为网络原因,response没有,给提示消息
+      if (!err.response) {
+        if (store.getState().A0layout.closeUpFile.state) MessageFu.warning('取消上传!')
+        else MessageFu.error('网络繁忙,请稍后重试!')
+      } else MessageFu.error('响应错误,请联系管理员!')
+    }, 100)
+
+    return Promise.reject(err)
+  }
+)
+
+// 导出 axios 实例
+export default http

+ 50 - 0
后台大场景编辑/src/utils/message.ts

@@ -0,0 +1,50 @@
+import store from "@/store";
+
+export type MessageType = {
+  txt: string;
+  type: "info" | "success" | "error" | "warning";
+  duration: number;
+};
+
+export const MessageFu = {
+  info: (txt: string, duration?: number) => {
+    store.dispatch({
+      type: "layout/message",
+      payload: {
+        txt,
+        type: "info",
+        duration: duration === undefined ? 3 : duration,
+      },
+    });
+  },
+  success: (txt: string, duration?: number) => {
+    store.dispatch({
+      type: "layout/message",
+      payload: {
+        txt,
+        type: "success",
+        duration: duration === undefined ? 3 : duration,
+      },
+    });
+  },
+  error: (txt: string, duration?: number) => {
+    store.dispatch({
+      type: "layout/message",
+      payload: {
+        txt,
+        type: "error",
+        duration: duration === undefined ? 3 : duration,
+      },
+    });
+  },
+  warning: (txt: string, duration?: number) => {
+    store.dispatch({
+      type: "layout/message",
+      payload: {
+        txt,
+        type: "warning",
+        duration: duration === undefined ? 3 : duration,
+      },
+    });
+  },
+};

+ 100 - 0
后台大场景编辑/src/utils/pass.ts

@@ -0,0 +1,100 @@
+function randomWord(randomFlag: boolean, min: number, max: number = 15) {
+  let str = "";
+  let range = min;
+  const arr = [
+    "0",
+    "1",
+    "2",
+    "3",
+    "4",
+    "5",
+    "6",
+    "7",
+    "8",
+    "9",
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "A",
+    "B",
+    "C",
+    "D",
+    "E",
+    "F",
+    "G",
+    "H",
+    "I",
+    "J",
+    "K",
+    "L",
+    "M",
+    "N",
+    "O",
+    "P",
+    "Q",
+    "R",
+    "S",
+    "T",
+    "U",
+    "V",
+    "W",
+    "X",
+    "Y",
+    "Z",
+  ];
+  // 随机产生
+  if (randomFlag) {
+    range = Math.round(Math.random() * (max - min)) + min;
+  }
+  for (var i = 0; i < range; i++) {
+    const pos = Math.round(Math.random() * (arr.length - 1));
+    str += arr[pos];
+  }
+  return str;
+}
+
+const encodeStr = (str: string, strv = "") => {
+  const NUM = 2;
+  const front = randomWord(false, 8);
+  const middle = randomWord(false, 8);
+  const end = randomWord(false, 8);
+
+  const str1 = str.substring(0, NUM);
+  const str2 = str.substring(NUM);
+
+  if (strv) {
+    const strv1 = strv.substring(0, NUM);
+    const strv2 = strv.substring(NUM);
+    return [
+      front + str2 + middle + str1 + end,
+      front + strv2 + middle + strv1 + end,
+    ];
+  }
+
+  return front + str2 + middle + str1 + end;
+};
+
+export default encodeStr;

+ 17 - 0
后台大场景编辑/src/utils/pcOrH5.ts

@@ -0,0 +1,17 @@
+export const isH5Fu = () => {
+  if (
+    window.navigator.userAgent.match(
+      /(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i
+    )
+  )
+    return true;
+  else return false;
+};
+
+export const isWeiFu = () => {
+  const ua: any = window.navigator.userAgent.toLowerCase();
+
+  // eslint-disable-next-line eqeqeq
+  if (ua.match(/MicroMessenger/i) == "micromessenger") return true;
+  else return false;
+};

+ 33 - 0
后台大场景编辑/src/utils/storage.ts

@@ -0,0 +1,33 @@
+// ------------------------------------token的本地存储------------------------------------
+
+// 用户 Token 的本地缓存键名,自己定义
+const TOKEN_KEY = 'BEIJING_BEIREN_HT_USER_INFO'
+
+/**
+ * 从本地缓存中获取 Token 信息
+ */
+export const getTokenInfo = (): any => {
+  return JSON.parse(localStorage.getItem(TOKEN_KEY) || '{}')
+}
+
+/**
+ * 将 Token 信息存入缓存
+ * @param {Object} tokenInfo 从后端获取到的 Token 信息
+ */
+export const setTokenInfo = (tokenInfo: any): void => {
+  localStorage.setItem(TOKEN_KEY, JSON.stringify(tokenInfo))
+}
+
+/**
+ * 删除本地缓存中的 Token 信息
+ */
+export const removeTokenInfo = (): void => {
+  localStorage.removeItem(TOKEN_KEY)
+}
+
+/**
+ * 判断本地缓存中是否存在 Token 信息
+ */
+export const hasToken = (): boolean => {
+  return Boolean(getTokenInfo().token)
+}

+ 27 - 0
后台大场景编辑/tsconfig.json

@@ -0,0 +1,27 @@
+{
+  "extends": "./path.tsconfig.json",
+  "compilerOptions": {
+    "target": "es5",
+    "lib": [
+      "dom",
+      "dom.iterable",
+      "esnext"
+    ],
+    "allowJs": true,
+    "skipLibCheck": true,
+    "esModuleInterop": true,
+    "allowSyntheticDefaultImports": true,
+    "strict": true,
+    "forceConsistentCasingInFileNames": true,
+    "noFallthroughCasesInSwitch": true,
+    "module": "esnext",
+    "moduleResolution": "node",
+    "resolveJsonModule": true,
+    "isolatedModules": true,
+    "noEmit": true,
+    "jsx": "react-jsx"
+  },
+  "include": [
+    "src"
+  ]
+}