shaogen1995 2 years ago
parent
commit
6719c6258a

+ 2 - 2
houtai/src/utils/request.js

@@ -1,7 +1,7 @@
 import axios from 'axios'
 const service = axios.create({
-  baseURL: 'http://192.168.20.55:8032', // 本地调试
-  // baseURL: 'https://hnbwg.4dage.com', // 线上调试
+  // baseURL: 'http://192.168.20.55:8032', // 本地调试
+  baseURL: 'https://hnbwg.4dage.com', // 线上调试
   // baseURL: '', // build
   timeout: 5000
 })

+ 147 - 139
houtai/src/views/tab2/add.vue

@@ -6,71 +6,35 @@
       {{ ruleForm.id ? "编辑" : "新增" }}
     </div>
     <div class="conten">
-      <el-form
-        :model="ruleForm"
-        ref="ruleForm"
-        label-width="120px"
-        class="demo-ruleForm"
-      >
+      <el-form :model="ruleForm" ref="ruleForm" label-width="120px" class="demo-ruleForm">
         <!-- 名称 -->
         <div class="checkBox2">
           <el-form-item label="名称:">
             <i class="biaoshi biaoshi2"></i>
-            <el-input
-              v-model="ruleForm.name"
-              maxlength="25"
-              show-word-limit
-            ></el-input>
+            <el-input v-model="ruleForm.name" maxlength="10" show-word-limit></el-input>
           </el-form-item>
           <!-- 总登记号 -->
           <el-form-item label="总登记号:">
-            <el-input
-              v-model="ruleForm.registerNum"
-              maxlength="25"
-              show-word-limit
-            ></el-input>
+            <el-input v-model="ruleForm.registerNum" maxlength="25" show-word-limit></el-input>
           </el-form-item>
         </div>
         <div class="checkBox">
           <!-- 类型 -->
           <el-form-item label="类别:">
-            <el-select
-              v-model="ruleForm.dictTextureId"
-              clearable
-              placeholder="请选择类别"
-            >
-              <el-option
-                v-for="i in dictTextureArr"
-                :key="i.value"
-                :label="i.label"
-                :value="i.value"
-              >
+            <el-select v-model="ruleForm.dictTextureId" clearable placeholder="请选择类别">
+              <el-option v-for="i in dictTextureArr" :key="i.value" :label="i.label" :value="i.value">
               </el-option>
             </el-select>
           </el-form-item>
           <!-- 年代 -->
           <el-form-item label="年代:">
-            <el-cascader
-              ref="elCascader"
-              clearable
-              v-model="dictAgeId"
-              :options="options"
-              @change="handleChange"
-            ></el-cascader>
+            <el-cascader ref="elCascader" clearable v-model="dictAgeId" :options="options" @change="handleChange">
+            </el-cascader>
           </el-form-item>
           <!-- 级别 -->
           <el-form-item label="级别:">
-            <el-select
-              v-model="ruleForm.dictLevelId"
-              clearable
-              placeholder="请选择级别"
-            >
-              <el-option
-                v-for="i in dictLevelArr"
-                :key="i.value"
-                :label="i.label"
-                :value="i.value"
-              >
+            <el-select v-model="ruleForm.dictLevelId" clearable placeholder="请选择级别">
+              <el-option v-for="i in dictLevelArr" :key="i.value" :label="i.label" :value="i.value">
               </el-option>
             </el-select>
           </el-form-item>
@@ -79,11 +43,7 @@
         <el-form-item label="尺寸:" class="fromSize">
           <div class="sizeRow">
             <span>长:</span>
-            <el-input
-              v-model="chang"
-              maxlength="8"
-              :onkeyup="clearNoNum('chang')"
-            ></el-input>
+            <el-input v-model="chang" maxlength="8" :onkeyup="clearNoNum('chang')"></el-input>
             <el-select v-model="changDanWei" placeholder="请选择类别">
               <el-option v-for="i in sizeData" :key="i" :label="i" :value="i">
               </el-option>
@@ -91,11 +51,7 @@
           </div>
           <div class="sizeRow sizeRow2">
             <span>宽:</span>
-            <el-input
-              v-model="kuang"
-              maxlength="8"
-              :onkeyup="clearNoNum('kuang')"
-            ></el-input>
+            <el-input v-model="kuang" maxlength="8" :onkeyup="clearNoNum('kuang')"></el-input>
             <el-select v-model="kuangDanWei" placeholder="请选择类别">
               <el-option v-for="i in sizeData" :key="i" :label="i" :value="i">
               </el-option>
@@ -103,11 +59,7 @@
           </div>
           <div class="sizeRow sizeRow2">
             <span>高:</span>
-            <el-input
-              v-model="gao"
-              maxlength="8"
-              :onkeyup="clearNoNum('gao')"
-            ></el-input>
+            <el-input v-model="gao" maxlength="8" :onkeyup="clearNoNum('gao')"></el-input>
             <el-select v-model="gaoDanWei" placeholder="请选择类别">
               <el-option v-for="i in sizeData" :key="i" :label="i" :value="i">
               </el-option>
@@ -118,30 +70,15 @@
         <!-- 图片 -->
         <el-form-item label="图片:">
           <i class="biaoshi biaoshi1"></i>
-          <el-upload
-            accept=".png,.jpg,.jpeg,.gif"
-            :data="{ type: 'img' }"
-            class="avatar-uploader"
-            :action="baseURL + '/api/cms/goods/upload'"
-            :headers="{ token }"
-            :show-file-list="true"
-            :before-upload="beforethumbUpload"
-            :on-success="upload_thumb_success"
-          >
+          <el-upload accept=".png,.jpg,.jpeg,.gif" :data="{ type: 'img' }" class="avatar-uploader"
+            :action="baseURL + '/api/cms/goods/upload'" :headers="{ token }" :show-file-list="true"
+            :before-upload="beforethumbUpload" :on-success="upload_thumb_success">
             <div v-if="ruleForm.thumb" class="imgdiv">
-              <img
-                style="
-                  width: 150px;
-                  height: 150px;
+              <img style="width: 150px;height: 150px;
                   display: block;
                   object-fit: cover;
-                "
-                :src="baseURL + ruleForm.thumb"
-              />
-              <i
-                class="el-icon-circle-close"
-                @click.stop="ruleForm.thumb = ''"
-              ></i>
+                " :src="baseURL + ruleForm.thumb" />
+              <i class="el-icon-circle-close" @click.stop="ruleForm.thumb = ''"></i>
             </div>
             <i v-else class="el-icon-plus avatar-uploader-icon"></i>
           </el-upload>
@@ -152,57 +89,46 @@
         <!-- 说明 -->
         <el-form-item label="说明:">
           <div class="txtBtn">
-            <el-button
-              :disabled="ruleForm.description.length >= 228"
-              size="small"
-              round
-              @click="ruleForm.description += '&emsp;&emsp;'"
-              >首行缩进</el-button
-            >
-            <el-button
-              :disabled="ruleForm.description.length >= 235"
-              size="small"
-              round
-              @click="ruleForm.description += '<br/>'"
-              >换行</el-button
-            >
+            <el-button :disabled="ruleForm.description.length >= 128" size="small" round
+              @click="ruleForm.description += '&emsp;&emsp;'">首行缩进</el-button>
+            <el-button :disabled="ruleForm.description.length >= 135" size="small" round
+              @click="ruleForm.description += '<br/>'">换行</el-button>
           </div>
-          <el-input
-            type="textarea"
-            v-model="ruleForm.description"
-            maxlength="240"
-            show-word-limit
-          ></el-input>
+          <el-input type="textarea" v-model="ruleForm.description" maxlength="140" show-word-limit></el-input>
         </el-form-item>
         <!-- 附件 -->
-        <el-form-item label="模型文件:" v-if="ruleForm.type === 'model'">
-          <i class="biaoshi"></i>
-          <el-upload
-            accept=".4dage"
-            multiple
-            drag
-            class="upload-demo"
-            :data="{ type: 'model' }"
-            :file-list="fileList"
-            :action="baseURL + '/api/cms/goods/upload'"
-            :headers="{ token }"
-            :before-upload="beforeFujian"
-            :on-success="successFujian"
-            :before-remove="beforeRemove"
-            :on-remove="handleRemove"
-            :limit="1"
-            :on-exceed="handleExceed"
-            :show-file-list="true"
-          >
-            <i class="el-icon-upload"></i>
-            <div class="el-upload__text">
-              将文件拖到此处,或<em>点击上传</em>
-            </div>
-            <div class="el-upload__text smEl">
-              仅支持.4dage格式的模型文件,大小不得超过500MB
-            </div>
-          </el-upload>
-        </el-form-item>
+        <div class="rowFrom">
+          <el-form-item label="模型文件:" v-if="ruleForm.type === 'model'">
+            <i class="biaoshi"></i>
+            <el-upload accept=".4dage" multiple drag class="upload-demo" :data="{ type: 'model' }" :file-list="fileList"
+              :action="baseURL + '/api/cms/goods/upload'" :headers="{ token }" :before-upload="beforeFujian"
+              :on-success="successFujian" :before-remove="beforeRemove" :on-remove="handleRemove" :limit="1"
+              :on-exceed="handleExceed" :show-file-list="true">
+              <i class="el-icon-upload"></i>
+              <div class="el-upload__text">
+                将文件拖到此处,或<em>点击上传</em>
+              </div>
+              <div class="el-upload__text smEl">
+                仅支持.4dage格式的模型文件,大小不得超过500MB
+              </div>
+            </el-upload>
+          </el-form-item>
+          &emsp;&emsp;&emsp;&emsp;&emsp;&emsp;
+          <el-form-item label="音频文件:" v-if="ruleForm.type === 'model'">
+            <el-upload accept=".mp3" multiple drag class="upload-demo" :data="{ type: 'audio' }" :file-list="fileList2"
+              :action="baseURL + '/api/cms/goods/upload'" :headers="{ token }" :before-upload="beforeFujian2"
+              :on-success="successFujian2" :before-remove="beforeRemove2" :on-remove="handleRemove2" :limit="1"
+              :on-exceed="handleExceed2" :show-file-list="true">
+              <i class="el-icon-upload"></i>
+              <div class="el-upload__text">
+                将文件拖到此处,或<em>点击上传</em>
+              </div>
+              <div class="el-upload__text smEl">
+                仅支持MP3格式的音频文件,大小不得超过10MB
+              </div>
+            </el-upload>
+          </el-form-item>
+        </div>
       </el-form>
     </div>
     <!-- 底部按钮 -->
@@ -254,7 +180,9 @@ export default {
         filePath: '',
         fileName: ''
       },
-      fileList: []
+      fileList: [],
+      // ---------音频
+      fileList2: []
     }
   },
   // 监听属性 类似于data概念
@@ -382,7 +310,48 @@ export default {
     },
     handleExceed (files, fileList) {
       this.$message.warning('只能上传一个文件,请删除原文件后操作')
+    },
+
+    // 上传音频-------------
+    beforeFujian2 (file) {
+      console.log('附件上传前222', file)
+      const sizeOk = file.size / 1024 / 1024 < 10
+      const typeOk = file.type === 'audio/mpeg'
+      return new Promise((resolve, reject) => {
+        if (!sizeOk) {
+          this.$message.error('音频大小超过10M!')
+          reject(file)
+        } else if (!typeOk) {
+          this.$message.error('音频格式有误!')
+          reject(file)
+        } else {
+          resolve(file)
+        }
+      })
+    },
+    successFujian2 (file) {
+      console.log('上传附件成功222', file)
+      if (file.code === 0) {
+        this.ruleForm.audioPath = file.data.filePath
+        this.ruleForm.audioName = file.data.fileName
+        this.$message.success('上传成功')
+      } else if (file.code === -1) {
+        this.$message.warning('上传失败,不支持的文件格式')
+      }
+    },
+    beforeRemove2 (file, fileList) {
+      if (file && file.status === 'success') {
+        return this.$confirm(`确定移除 ${file.name}?`)
+      }
+    },
+    handleRemove2 (file, fileList) {
+      this.ruleForm.audioPath = ''
+      this.ruleForm.audioName = ''
+    },
+    handleExceed2 (files, fileList) {
+      this.$message.warning('只能上传一个文件,请删除原文件后操作')
     }
+
   },
   // 生命周期 - 创建完成(可以访问当前this实例)
   async created () {
@@ -423,6 +392,9 @@ export default {
       this.ruleForm = resSon.data
       // 附件回显
       this.fileList = [{ name: resSon.data.fileName }]
+      // 音频回显
+      if (resSon.data.audioName) this.fileList2 = [{ name: resSon.data.audioName }]
+
       // 年代回显
       if (resSon.data.dictAgeId) {
         res.data.forEach((v) => {
@@ -453,20 +425,33 @@ export default {
     }
   },
   // 生命周期 - 挂载完成(可以访问DOM元素)
-  mounted () {},
-  beforeCreate () {}, // 生命周期 - 创建之前
-  beforeMount () {}, // 生命周期 - 挂载之前
-  beforeUpdate () {}, // 生命周期 - 更新之前
-  updated () {}, // 生命周期 - 更新之后
-  beforeDestroy () {}, // 生命周期 - 销毁之前
-  destroyed () {}, // 生命周期 - 销毁完成
-  activated () {} // 如果页面有keep-alive缓存功能,这个函数会触发
+  mounted () { },
+  beforeCreate () { }, // 生命周期 - 创建之前
+  beforeMount () { }, // 生命周期 - 挂载之前
+  beforeUpdate () { }, // 生命周期 - 更新之前
+  updated () { }, // 生命周期 - 更新之后
+  beforeDestroy () { }, // 生命周期 - 销毁之前
+  destroyed () { }, // 生命周期 - 销毁完成
+  activated () { } // 如果页面有keep-alive缓存功能,这个函数会触发
 }
 </script>
 <style lang='less' scoped>
 .tab2Add {
   width: 100%;
   height: 100%;
+
+  .rowFrom {
+    display: flex;
+
+    /deep/.el-upload-list {
+      width: 360px;
+    }
+
+    /deep/.el-upload-list li {
+      width: 360px !important;
+    }
+  }
+
   .top {
     margin-top: -20px;
     height: 50px;
@@ -475,18 +460,22 @@ export default {
     background-color: #f8f8f8;
     font-weight: 700;
   }
+
   .conten {
     padding-right: 300px;
     padding-top: 20px;
+
     .avatar-uploader .el-upload {
       border-radius: 6px;
       cursor: pointer;
       position: relative;
       overflow: hidden;
     }
+
     .avatar-uploader .el-upload:hover {
       border-color: #3e5eb3;
     }
+
     .avatar-uploader-icon {
       border: 1px dashed #ccc;
       font-size: 28px;
@@ -496,21 +485,26 @@ export default {
       line-height: 150px;
       text-align: center;
     }
+
     .biaoshi1::before {
       left: -64px;
     }
+
     .biaoshi2::before {
       top: -11px;
       left: -64px;
     }
+
     /deep/.el-form-item {
       margin-bottom: 12px;
     }
+
     /deep/.el-textarea textarea {
       margin-top: 10px;
       height: 80px;
       resize: none;
     }
+
     /deep/.el-textarea .el-input__count {
       position: absolute;
       bottom: -24px;
@@ -519,58 +513,72 @@ export default {
       height: 30px;
       line-height: 30px;
     }
+
     .smEl {
       color: #ccc;
       font-size: 12px;
       margin-top: -15px;
     }
   }
+
   .con_btn {
     position: absolute;
     bottom: 20px;
     left: 50%;
     transform: translateX(-50%);
   }
+
   .txtBtn {
     position: absolute;
     top: -32px;
     right: 4px;
   }
+
   /deep/.imgdiv .el-icon-circle-close {
     font-size: 20px;
   }
+
   .checkBox {
     display: flex;
   }
+
   .checkBox2 {
     width: 100%;
     display: flex;
-    & > div {
+
+    &>div {
       width: 50%;
     }
   }
+
   .fromSize {
     /deep/.el-form-item__content {
       display: flex;
     }
+
     /deep/.el-input {
       width: 100px;
     }
+
     /deep/.el-input--suffix {
       width: 90px;
     }
+
     /deep/.el-input__inner {
       padding: 0 5px !important;
     }
+
     .sizeRow {
       margin-right: 90px;
     }
+
     .sizeRow2 {
       /deep/.el-input {
         width: 110px;
       }
     }
   }
+
   /deep/.el-upload-list {
     margin-top: -12px;
   }

+ 23 - 0
webNew/.gitignore

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

+ 46 - 0
webNew/README.md

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

+ 10 - 0
webNew/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)

File diff suppressed because it is too large
+ 29590 - 0
webNew/package-lock.json


+ 59 - 0
webNew/package.json

@@ -0,0 +1,59 @@
+{
+  "name": "demo",
+  "version": "0.1.0",
+  "private": true,
+  "dependencies": {
+    "@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": "^4.24.2",
+    "axios": "^1.1.3",
+    "react": "^18.2.0",
+    "react-dom": "^18.2.0",
+    "react-lazy-load-image-component": "^1.5.6",
+    "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
webNew/path.tsconfig.json

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

BIN
webNew/public/favicon.ico


+ 43 - 0
webNew/public/index.html

@@ -0,0 +1,43 @@
+<!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" />
+    <!--
+      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>

+ 29 - 0
webNew/src/App.tsx

@@ -0,0 +1,29 @@
+import 'antd/dist/antd.css';
+import '@/assets/styles/base.css'
+// import { useDispatch, useSelector } from 'react-redux'
+// import { RootState } from './store'
+// import { addNunAction } from './store/action/login'
+
+// 关于路由
+import React from "react"
+import { Router, Route, Switch, Redirect } from "react-router-dom"
+import history from "./utils/history"
+const Home = React.lazy(() => import('./pages/Home'))
+
+export default function App() {
+  // const dispatch = useDispatch()
+  return (
+    <div>
+      {/* 关于路由 */}
+      <Router history={history}>
+        <React.Suspense fallback={<div>加载中...</div>}>
+          <Switch>
+            <Redirect exact path="/" to="Home" />
+            <Route path="/Home" component={Home} />
+          </Switch>
+        </React.Suspense>
+      </Router>
+    </div>
+
+  )
+}

BIN
webNew/src/assets/img/logo.png


+ 115 - 0
webNew/src/assets/styles/base.css

@@ -0,0 +1,115 @@
+* {
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
+}
+
+#root{
+  min-width: 1500px;
+  min-height: 800px;
+  overflow-y: auto;
+}
+
+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;
+}
+
+ul {
+  list-style: none;
+}
+
+/*修改提示文字的颜色*/
+.ant-input::-webkit-input-placeholder {
+  /* WebKit browsers */
+  color: rgba(0, 0, 0, 0.6);
+}
+
+.ant-input:-moz-placeholder {
+  /* Mozilla Firefox 4 to 18 */
+  color: rgba(0, 0, 0, 0.6);
+}
+
+.ant-input::-moz-placeholder {
+  /* Mozilla Firefox 19+ */
+  color: rgba(0, 0, 0, 0.6);
+}
+
+.ant-input:-ms-input-placeholder {
+  /* Internet Explorer 10+ */
+  color: rgba(0, 0, 0, 0.6);
+}
+
+
+.ant-input-affix-wrapper {
+  background-color: rgba(255, 255, 255, 0.7000);
+  border: none;
+}
+
+.ant-input-affix-wrapper:focus,
+.ant-input-affix-wrapper-focused {
+  border-color: transparent;
+}
+
+.ant-input-affix-wrapper:not(.ant-input-affix-wrapper-disabled):hover {
+  border-color: transparent;
+}
+
+.ant-input {
+  background-color: transparent;
+}
+.ant-pagination-item-active{
+  color: #961014;
+  border: none;
+}
+.ant-pagination-item-active{
+  background-color: transparent;
+  pointer-events: none;
+}
+.ant-pagination-item-active a{
+  color: #961014;
+}
+.ant-pagination-item-active:hover a{
+  color: #961014;
+}
+.ant-pagination-item:hover a{
+  color: #961014;
+}
+.ant-pagination-prev:hover .ant-pagination-item-link, .ant-pagination-next:hover .ant-pagination-item-link{
+  color: #961014;
+
+}
+.ant-pagination{
+  display: flex;
+  align-items: center;
+}
+.ant-pagination-item{
+  display: flex;
+  align-items: center;
+  width: 24px !important;
+  height: 24px !important;
+  line-height: 24px !important;
+}

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

@@ -0,0 +1,32 @@
+import history from '@/utils/history'
+import { hasToken } from '@//utils/storage'
+import React from 'react'
+import { Redirect, Route } from 'react-router-dom'
+
+type AtahType = {
+  path: string
+  component: React.FC
+}
+
+export default function AuthRoute({ path, component: Com }: AtahType) {
+  return (
+    <Route
+      path={path}
+      render={() => {
+        if (hasToken()) return <Com />
+        else {
+          alert('登录失效')
+          return (
+            <Redirect to={{
+              pathname: '/Login',
+              state: { from: history.location.pathname }
+            }}
+            />
+          )
+        }
+      }}
+    />
+  )
+}
+
+

+ 15 - 0
webNew/src/index.tsx

@@ -0,0 +1,15 @@
+import App from './App'
+import store from './store/index'
+import { Provider } from 'react-redux'
+
+import { createRoot } from 'react-dom/client';
+
+
+const container = document.getElementById('root') as HTMLElement;
+const root = createRoot(container);
+
+root.render(
+  <Provider store={store}>
+    <App />
+  </Provider>
+  );

+ 247 - 0
webNew/src/pages/Home/index.module.scss

@@ -0,0 +1,247 @@
+.Home {
+  width: 100%;
+  height: 100vh;
+  background-image: linear-gradient(to bottom, rgba(255, 255, 255, 1), rgba(207, 202, 189, 1));
+
+  :global {
+    .title {
+      width: 100%;
+      height: 90px;
+      background-color: #a7191e;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      position: relative;
+
+      .logo {
+        width: 215px;
+
+        &>img {
+          width: 100%;
+        }
+      }
+    }
+
+    .mainBox {
+      width: 1400px;
+      margin: 70px auto;
+      height: calc(100% - 170px);
+      position: relative;
+
+      .search {
+        position: absolute;
+        right: 0px;
+        top: -130px;
+        width: 270px;
+        height: 30px;
+
+        .searchBtn {
+          position: absolute;
+          top: 0;
+          left: 0;
+          cursor: pointer;
+          width: 30px;
+          height: 30px;
+          z-index: 2;
+        }
+        .searchClear{
+          position: absolute;
+          top: 0;
+          right: 0;
+          cursor: pointer;
+          width: 30px;
+          height: 30px;
+          z-index: 2;
+          display: flex;
+          justify-content: center;
+          align-items: center;
+        }
+      }
+
+      .sortBox {
+        font-size: 18px;
+        display: flex;
+        position: relative;
+        transition: height .3s;
+        overflow: hidden;
+        padding-bottom: 4px;
+
+        &::before {
+          position: absolute;
+          content: "";
+          bottom: 0px;
+          left: 0;
+          width: 100%;
+          height: 4px;
+          background-color: rgba(217, 217, 217, 1);
+        }
+
+        .xian {
+          position: absolute;
+          left: 0;
+          top: 42px;
+          width: 1200px;
+          height: 2px;
+          background-color: rgba(217, 217, 217, 1);
+        }
+
+        .all {
+          cursor: pointer;
+          text-align: center;
+          position: absolute;
+          top: 10px;
+          padding-bottom: 5px;
+
+          &.activeAll {
+            border-bottom: 4px solid #BE262B;
+          }
+        }
+
+
+        .sortBox_left {
+          padding-left: 60px;
+          width: 1230px;
+          display: flex;
+          flex-wrap: wrap;
+
+          &>div {
+            margin-top: 10px;
+            cursor: pointer;
+            margin-right: 26px;
+            padding-bottom: 10px;
+          }
+
+          .active {
+            position: relative;
+
+            &::before {
+              position: absolute;
+              content: "";
+              bottom: 0px;
+              left: 0;
+              width: 100%;
+              height: 4px;
+              background-color: #BE262B;
+            }
+          }
+        }
+
+
+
+        .sortBox_right {
+          display: flex;
+          justify-content: center;
+          align-items: center;
+          cursor: pointer;
+          position: absolute;
+          bottom: 8px;
+          right: 0;
+          width: 30px;
+          height: 30px;
+          background-color: rgba(217, 217, 217);
+          border-radius: 3px;
+        }
+      }
+
+      .modelBox::-webkit-scrollbar {
+        width: 6px;
+      }
+
+      .modelBox::-webkit-scrollbar-thumb {
+        border-radius: 10px;
+        background-color: #a7191e;
+      }
+
+      .modelBox::-webkit-scrollbar-track {
+        border-radius: 10px;
+      }
+
+      .modelBox {
+        margin-top: 20px;
+        box-shadow: 0px 0px 5px 3px #ccc;
+        background-color: rgba(255, 255, 255, 0.5000);
+        width: 100%;
+        height: calc(100% - 160px);
+        overflow-y: auto;
+        display: flex;
+        flex-wrap: wrap;
+        padding: 20px 20px 0;
+
+        .modelBox_row {
+          border-radius: 6px;
+          overflow: hidden;
+          cursor: pointer;
+          width: 300px;
+          height: 300px;
+          background-color: #CFCABD;
+          margin: 0 40px 60px 0;
+
+          &:nth-of-type(4n) {
+            margin-right: 0;
+          }
+
+          &>img {
+            width: 300px;
+            height: 260px;
+            object-fit: cover;
+          }
+
+          p {
+            height: 40px;
+            display: flex;
+            align-items: center;
+            background-color: #961014;
+            text-align: center;
+            color: #fff;
+            font-size: 16px;
+
+            .row_txt1 {
+              height: 100%;
+              width: 100px;
+              background-color: rgba(255, 255, 255, .6);
+              line-height: 40px;
+              border-right: 2px solid #fff;
+            }
+
+            .row_txt2 {
+              height: 100%;
+              flex: 1;
+              line-height: 40px;
+            }
+          }
+        }
+        .noneList{
+          width: 100%;
+          height: 100%;
+          display: flex;
+          justify-content: center;
+          align-items: center;
+          flex-direction: column;
+          .incoBox{
+            font-size: 24px;
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            width: 40px;
+            height: 40px;
+            margin-bottom: 15px;
+            background-color: #BE262B;
+            border-radius: 50%;
+            color: #fff;
+            font-weight: 700;
+          }
+          &>p{
+            font-weight: 700;
+            font-size: 20px;
+          }
+        }
+      }
+
+      .page {
+        margin-top: 10px;
+        display: flex;
+        justify-content: flex-end;
+      }
+    }
+  }
+}

+ 143 - 0
webNew/src/pages/Home/index.tsx

@@ -0,0 +1,143 @@
+import { baseURL } from '@/utils/http'
+import classNames from 'classnames'
+import styles from './index.module.scss'
+import logoImg from '@/assets/img/logo.png'
+import { Input, Pagination } from 'antd'
+import { SearchOutlined, UpOutlined, DownOutlined, ExclamationOutlined, CloseCircleOutlined } from '@ant-design/icons';
+import { useEffect, useRef, useState } from 'react'
+import { LazyLoadImage } from "react-lazy-load-image-component";
+import { useDispatch, useSelector } from 'react-redux';
+import { getListAction, getSortAction } from '@/store/action/home';
+import { RootState } from '@/store';
+export default function Home() {
+
+  const dispatch = useDispatch()
+  // 进页面获取后端文物数据
+  useEffect(() => {
+    dispatch(getSortAction())
+  }, [dispatch])
+
+  // 滚动盒子ref
+  const modelBoxRef = useRef<HTMLDivElement>(null)
+
+  // 输入框变量
+  const [value, setValue] = useState('')
+
+  // 清空输入框
+  const clearValueFu = () => {
+    setValue('')
+    setPageNum(1)
+    dispatch(getListAction({ ...fromData.current, dictTextureId: fromData.current.dictTextureId === -1 ? null : fromData.current.dictTextureId, searchKey: '', pageNum: 1 }))
+
+  }
+
+  const searchKeyUpFu = (e: React.KeyboardEvent<HTMLInputElement>) => {
+    if (e.key === 'Enter') searchBtnFu()
+  }
+  const searchBtnFu = () => {
+    getModelList()
+  }
+
+  // 分类的数据和高亮
+  const { sortList, modelInfo } = useSelector((state: RootState) => state.homeStore)
+  const { list, total } = modelInfo
+  const [sort, setSort] = useState(-1)
+  const [sortAll, setSortAll] = useState(false)
+  const [pageNum, setPageNum] = useState(1)
+
+  const fromData = useRef({
+    pageNum: 1,
+    pageSize: 30,
+    searchKey: '',
+    dictTextureId: -1
+  })
+  const pageChangeFu = (page: number) => {
+    fromData.current.pageNum = page
+    setPageNum(page)
+    getModelList()
+  }
+  // 封装一个发送请求获取列表的函数
+  const getModelList = () => {
+    const sroolDom = modelBoxRef.current
+    sroolDom!.scrollTop = 0
+    dispatch(getListAction({ ...fromData.current, dictTextureId: fromData.current.dictTextureId === -1 ? null : fromData.current.dictTextureId, searchKey: value }))
+  }
+  useEffect(() => {
+    fromData.current.dictTextureId = sort
+    fromData.current.pageNum=1
+    setPageNum(1)
+    getModelList()
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, [sort])
+
+  return (
+    <div className={styles.Home}>
+      <div className='title'>
+        <div className='logo'>
+          <img src={logoImg} alt="" />
+        </div>
+
+      </div>
+      {/* 分类选择 */}
+      <div className='mainBox'>
+        {/* 输入框 */}
+        <div className='search'>
+          <div className='searchBtn' onClick={searchBtnFu}></div>
+          {value.length > 0 ? (<div className='searchClear' onClick={clearValueFu}>
+            <CloseCircleOutlined />
+          </div>) : null}
+
+          <Input maxLength={10} placeholder='搜查文物' value={value} onChange={(e) => setValue(e.target.value)} onKeyUp={(e) => searchKeyUpFu(e)} prefix={<SearchOutlined />} />
+        </div>
+
+        {/* 筛选分类 */}
+        <div className='sortBox' style={{ height: sortAll ? '90px' : '46px' }}>
+          <div className='xian' hidden={!sortAll}></div>
+          <div className={classNames('all', sort === -1 ? 'activeAll' : '')} onClick={() => setSort(-1)}>全部</div>
+          <div className='sortBox_left'>
+            {sortList.map((item) => (
+              <div className={classNames(sort === item.id ? 'active' : '')} onClick={() => setSort(item.id)} key={item.id}>{item.name}</div>
+            ))}
+          </div>
+          <div className='sortBox_right' onClick={() => setSortAll(!sortAll)}>
+            {sortAll ? <DownOutlined /> : <UpOutlined />}
+
+          </div>
+        </div>
+
+        {/* 页面渲染主体 */}
+        <div className='modelBox' ref={modelBoxRef}>
+          {
+            list && list.length > 0 ?
+              list.map(item => (
+                <div className='modelBox_row' key={item.id}>
+                  <LazyLoadImage src={baseURL + item.thumb}
+                    width={300} height={260}
+                    alt=""
+                  />
+                  <p>
+                    {item.dictTextureName ? <span className='row_txt1'>{item.dictTextureName}</span> : null}
+                    <span className='row_txt2'>{item.name}</span>
+                  </p>
+                </div>
+              ))
+              :
+              <div className='noneList'>
+                <div className='incoBox'>
+                  <ExclamationOutlined />
+                </div>
+                <p>暂无内容</p>
+              </div>
+          }
+        </div>
+
+        {/* 分页器 */}
+        <div className='page'>
+          <Pagination size="small" current={pageNum} total={total} pageSize={30} hideOnSinglePage={true} onChange={pageChangeFu} />
+
+        </div>
+
+      </div>
+    </div>
+  )
+}

+ 25 - 0
webNew/src/store/action/home.ts

@@ -0,0 +1,25 @@
+import { AppDispatch } from ".."
+import http from "@/utils/http"
+
+// 获取分类
+export const getSortAction = () => {
+
+  return async (dispatch: AppDispatch) => {
+    const res = await http.post('/api/show/getTree', { type: 'texture' })
+    dispatch({ type: 'home/setSort', payload: res.data })
+  }
+}
+
+// 获取列表数据
+export const getListAction = (data: any) => {
+
+  return async (dispatch: AppDispatch) => {
+    const res = await http.post('/api/show/goods/list', data)
+    const list =res.data.records
+    const total =res.data.total
+    dispatch({ type: 'home/setList', payload: {list,total} })
+
+  }
+}
+
+

+ 15 - 0
webNew/src/store/index.ts

@@ -0,0 +1,15 @@
+import { applyMiddleware, legacy_createStore as createStore } from 'redux'
+import rootReducer from './reducer'
+import { composeWithDevTools } from 'redux-devtools-extension'
+import thunk from 'redux-thunk'
+
+// 创建仓库实例
+const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(thunk)))
+
+
+export type RootState = ReturnType<typeof store.getState>
+
+
+export type AppDispatch = typeof store.dispatch
+// 导出仓库实例
+export default store

+ 25 - 0
webNew/src/store/reducer/home.ts

@@ -0,0 +1,25 @@
+import { HomeSortType, HomeModelType } from "@/types"
+
+
+
+// 初始化状态应用注解
+const initState = {
+  sortList: [] as HomeSortType[],
+  modelInfo: {} as HomeModelType
+}
+
+type HomeActionType =
+  | { type: 'home/setSort', payload: HomeSortType[] }
+  | { type: 'home/setList', payload: HomeModelType }
+
+// 频道 reducer
+export default function loginReducer(state = initState, action: HomeActionType) {
+  switch (action.type) {
+    case 'home/setSort':
+      return { ...state, sortList: action.payload }
+    case 'home/setList':
+      return { ...state, modelInfo: action.payload }
+    default:
+      return state
+  }
+}

+ 9 - 0
webNew/src/store/reducer/index.ts

@@ -0,0 +1,9 @@
+import { combineReducers } from 'redux'
+import homeReducer from './home'
+
+// 合并 reducer
+const rootReducer = combineReducers({
+  homeStore: homeReducer,
+})
+
+export default rootReducer

+ 4 - 0
webNew/src/types/declaration.d.ts

@@ -0,0 +1,4 @@
+declare module 'history'
+declare module '*.scss';
+declare module '*.png';
+declare module 'react-lazy-load-image-component'

+ 2 - 0
webNew/src/types/index.d.ts

@@ -0,0 +1,2 @@
+export * from './store/home'
+

+ 40 - 0
webNew/src/types/store/home.d.ts

@@ -0,0 +1,40 @@
+export type HomeSortType = {
+  children?: [];
+  id: number;
+  name: string;
+  type: string;
+}
+
+export type modelItem = {
+  audioName: string;
+  audioPath: string;
+  createTime: string;
+  creatorId: number;
+  description: string;
+  dictAgeFront: string;
+  dictAgeId?: any;
+  dictAgeName: string;
+  dictLevelId?: any;
+  dictLevelName: string;
+  dictTextureId?: any;
+  dictTextureName: string;
+  display: number;
+  fileName: string;
+  filePath: string;
+  id: number;
+  name: string;
+  registerNum: string;
+  sizeHeight: string;
+  sizeLength: string;
+  sizeWidth: string;
+  sort: number;
+  thumb: string;
+  type: string;
+  updateTime: string;
+  visit: number;
+}
+
+export type HomeModelType = {
+  list: modelItem[]
+  total: number
+}

+ 3 - 0
webNew/src/utils/history.ts

@@ -0,0 +1,3 @@
+import { createHashHistory  } from 'history'
+const history = createHashHistory()
+export default history

+ 49 - 0
webNew/src/utils/http.ts

@@ -0,0 +1,49 @@
+import axios from 'axios'
+// import history from './history'
+// import { getTokenInfo } from './storage'
+// 请求基地址
+export const baseURL = process.env.NODE_ENV === "development" ? 'https://hnbwg.4dage.com' : ''
+
+// 创建 axios 实例
+const http = axios.create({
+  baseURL,
+  timeout: 5000,
+})
+
+// 请求拦截器
+http.interceptors.request.use(
+  function (config: any) {
+    // const { token } = getTokenInfo()
+    // if (token) config.headers.Authorization = `Bearer ${token}`
+    return config
+  },
+  function (err) {
+    return Promise.reject(err)
+  },
+)
+
+// 响应拦截器
+http.interceptors.response.use(
+  function (response) {
+    return response.data
+  },
+  async function (err) {
+    // 如果因为网络原因,response没有,给提示消息
+    if (!err.response) {
+      alert('网络繁忙,请稍后重试')
+    } else {
+      // 网络没问题,后台返回了有数据
+
+      // token过期
+      // if (err.response.status === 401) {
+      //   history.push('/Login')
+
+      // }
+    }
+
+    return Promise.reject(err)
+  },
+)
+
+// 导出 axios 实例
+export default http

+ 34 - 0
webNew/src/utils/storage.ts

@@ -0,0 +1,34 @@
+// ------------------------------------token的本地存储------------------------------------
+
+// 用户 Token 的本地缓存键名
+const TOKEN_KEY = 'itcast-geek-park-h5'
+
+/**
+ * 从本地缓存中获取 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
webNew/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"
+  ]
+}