shaogen1995 9 hónapja
commit
2b71ae01df
100 módosított fájl, 65593 hozzáadás és 0 törlés
  1. 5 0
      houtai/.editorconfig
  2. 23 0
      houtai/.gitignore
  3. 24 0
      houtai/README.md
  4. 5 0
      houtai/babel.config.js
  5. 29975 0
      houtai/package-lock.json
  6. 69 0
      houtai/package.json
  7. BIN
      houtai/public/favicon.ico
  8. 17 0
      houtai/public/index.html
  9. 9 0
      houtai/src/App.vue
  10. 17 0
      houtai/src/apis/login.js
  11. 73 0
      houtai/src/apis/system.js
  12. 41 0
      houtai/src/apis/tab1.js
  13. 231 0
      houtai/src/assets/css/base.css
  14. BIN
      houtai/src/assets/img/1.png
  15. BIN
      houtai/src/assets/img/2.png
  16. BIN
      houtai/src/assets/img/3.png
  17. BIN
      houtai/src/assets/img/4.png
  18. BIN
      houtai/src/assets/img/bg.jpg
  19. BIN
      houtai/src/assets/imgAdd/comment.png
  20. BIN
      houtai/src/assets/imgAdd/like.png
  21. BIN
      houtai/src/assets/imgAdd/likeAc.png
  22. BIN
      houtai/src/assets/imgAdd/mesBac.png
  23. BIN
      houtai/src/assets/imgAdd/touxiang.png
  24. 21 0
      houtai/src/main.js
  25. 71 0
      houtai/src/router/index.js
  26. 106 0
      houtai/src/utils/pass.js
  27. 38 0
      houtai/src/utils/request.js
  28. 358 0
      houtai/src/views/layout/index.vue
  29. 159 0
      houtai/src/views/login.vue
  30. 106 0
      houtai/src/views/tab1/index.vue
  31. 534 0
      houtai/src/views/tab1Main/five.vue
  32. 524 0
      houtai/src/views/tab1Main/four.vue
  33. 206 0
      houtai/src/views/tab1Main/index.vue
  34. 329 0
      houtai/src/views/tab1Main/one.vue
  35. 182 0
      houtai/src/views/tab1Main/three.vue
  36. 223 0
      houtai/src/views/tab1Main/tow.vue
  37. 232 0
      houtai/src/views/tab2/Dialog.vue
  38. 243 0
      houtai/src/views/tab2/index.vue
  39. 335 0
      houtai/src/views/tab3/index.vue
  40. 169 0
      houtai/src/views/tab4/index.vue
  41. 3 0
      houtai/vue.config.js
  42. 23 0
      传统村落pc/.gitignore
  43. 19 0
      传统村落pc/README.md
  44. 5 0
      传统村落pc/babel.config.js
  45. 29381 0
      传统村落pc/package-lock.json
  46. 33 0
      传统村落pc/package.json
  47. 1421 0
      传统村落pc/public/config/config.json
  48. BIN
      传统村落pc/public/data/1.png
  49. BIN
      传统村落pc/public/data/10.png
  50. BIN
      传统村落pc/public/data/11.png
  51. BIN
      传统村落pc/public/data/12.png
  52. 178 0
      传统村落pc/public/data/12village.json
  53. BIN
      传统村落pc/public/data/2.png
  54. BIN
      传统村落pc/public/data/3.png
  55. BIN
      传统村落pc/public/data/4.png
  56. BIN
      传统村落pc/public/data/5.png
  57. BIN
      传统村落pc/public/data/6.png
  58. BIN
      传统村落pc/public/data/7.png
  59. BIN
      传统村落pc/public/data/8.png
  60. BIN
      传统村落pc/public/data/9.png
  61. BIN
      传统村落pc/public/data/conBsAc.png
  62. BIN
      传统村落pc/public/data/fence.png
  63. 8 0
      传统村落pc/public/data/jmc.json
  64. 14 0
      传统村落pc/public/data/jmq.json
  65. 104 0
      传统村落pc/public/data/jmz.json
  66. BIN
      传统村落pc/public/data/lable.png
  67. BIN
      传统村落pc/public/data/lableA.png
  68. BIN
      传统村落pc/public/data/lableIcon.png
  69. BIN
      传统村落pc/public/data/lableIconA.png
  70. BIN
      传统村落pc/public/data/layer.png
  71. BIN
      传统村落pc/public/favicon.ico
  72. 52 0
      传统村落pc/public/index.html
  73. 1 0
      传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_0.json
  74. 1 0
      传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_1.json
  75. 1 0
      传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_10.json
  76. 1 0
      传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_11.json
  77. 1 0
      传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_12.json
  78. 1 0
      传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_13.json
  79. 1 0
      传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_14.json
  80. 1 0
      传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_15.json
  81. 1 0
      传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_16.json
  82. 1 0
      传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_17.json
  83. 1 0
      传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_18.json
  84. 1 0
      传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_19.json
  85. 1 0
      传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_2.json
  86. 1 0
      传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_20.json
  87. 1 0
      传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_21.json
  88. 1 0
      传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_22.json
  89. 1 0
      传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_23.json
  90. 1 0
      传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_24.json
  91. 1 0
      传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_25.json
  92. 1 0
      传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_26.json
  93. 1 0
      传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_27.json
  94. 1 0
      传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_3.json
  95. 1 0
      传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_4.json
  96. 1 0
      传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_5.json
  97. 1 0
      传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_6.json
  98. 1 0
      传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_7.json
  99. 1 0
      传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_8.json
  100. 0 0
      传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_9.json

+ 5 - 0
houtai/.editorconfig

@@ -0,0 +1,5 @@
+[*.{js,jsx,ts,tsx,vue}]
+indent_style = space
+indent_size = 2
+trim_trailing_whitespace = true
+insert_final_newline = true

+ 23 - 0
houtai/.gitignore

@@ -0,0 +1,23 @@
+.DS_Store
+node_modules
+/dist
+
+
+# local env files
+.env.local
+.env.*.local
+
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?

+ 24 - 0
houtai/README.md

@@ -0,0 +1,24 @@
+# houtai
+
+## Project setup
+```
+npm install
+```
+
+### Compiles and hot-reloads for development
+```
+npm run serve
+```
+
+### Compiles and minifies for production
+```
+npm run build
+```
+
+### Lints and fixes files
+```
+npm run lint
+```
+
+### Customize configuration
+See [Configuration Reference](https://cli.vuejs.org/config/).

+ 5 - 0
houtai/babel.config.js

@@ -0,0 +1,5 @@
+module.exports = {
+  presets: [
+    '@vue/cli-plugin-babel/preset'
+  ]
+}

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 29975 - 0
houtai/package-lock.json


+ 69 - 0
houtai/package.json

@@ -0,0 +1,69 @@
+{
+  "name": "houtai",
+  "version": "0.1.0",
+  "private": true,
+  "scripts": {
+    "serve": "vue-cli-service serve",
+    "build": "vue-cli-service build",
+    "lint": "vue-cli-service lint"
+  },
+  "dependencies": {
+    "axios": "^0.24.0",
+    "Base64": "^1.1.0",
+    "core-js": "^3.6.5",
+    "echarts": "^5.3.2",
+    "element-ui": "^2.15.6",
+    "js-base64": "^3.7.2",
+    "moment": "^2.29.1",
+    "vant": "^2.12.45",
+    "vue": "^2.6.11",
+    "vue-router": "^3.2.0",
+    "wangeditor": "^4.7.11"
+  },
+  "devDependencies": {
+    "@vue/cli-plugin-babel": "~4.5.0",
+    "@vue/cli-plugin-eslint": "~4.5.0",
+    "@vue/cli-plugin-router": "~4.5.0",
+    "@vue/cli-service": "~4.5.0",
+    "@vue/eslint-config-standard": "^5.1.2",
+    "babel-eslint": "^10.1.0",
+    "eslint": "^6.7.2",
+    "eslint-plugin-import": "^2.20.2",
+    "eslint-plugin-node": "^11.1.0",
+    "eslint-plugin-promise": "^4.2.1",
+    "eslint-plugin-standard": "^4.0.0",
+    "eslint-plugin-vue": "^6.2.2",
+    "less": "^3.0.4",
+    "less-loader": "^5.0.0",
+    "lint-staged": "^9.5.0",
+    "vue-template-compiler": "^2.6.11"
+  },
+  "eslintConfig": {
+    "root": true,
+    "env": {
+      "node": true
+    },
+    "extends": [
+      "plugin:vue/essential",
+      "@vue/standard"
+    ],
+    "parserOptions": {
+      "parser": "babel-eslint"
+    },
+    "rules": {}
+  },
+  "browserslist": [
+    "> 1%",
+    "last 2 versions",
+    "not dead"
+  ],
+  "gitHooks": {
+    "pre-commit": "lint-staged"
+  },
+  "lint-staged": {
+    "*.{js,jsx,vue}": [
+      "vue-cli-service lint",
+      "git add"
+    ]
+  }
+}

BIN
houtai/public/favicon.ico


+ 17 - 0
houtai/public/index.html

@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="">
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width,initial-scale=1.0">
+    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
+    <title>传统村落一张图-管理后台</title>
+  </head>
+  <body>
+    <noscript>
+      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
+    </noscript>
+    <div id="app"></div>
+    <!-- built files will be auto injected -->
+  </body>
+</html>

+ 9 - 0
houtai/src/App.vue

@@ -0,0 +1,9 @@
+<template>
+  <div id="app">
+    <Router-view/>
+  </div>
+</template>
+
+<style lang="less">
+
+</style>

+ 17 - 0
houtai/src/apis/login.js

@@ -0,0 +1,17 @@
+import axios from '../utils/request'
+// 用户登录接口
+export const userLogin = (data) => {
+  return axios({
+    method: 'post',
+    url: '/admin/login',
+    data
+  })
+}
+// 修改密码
+export const updatePwd = (data) => {
+  return axios({
+    method: 'post',
+    url: '/sys/user/updatePwd',
+    data
+  })
+}

+ 73 - 0
houtai/src/apis/system.js

@@ -0,0 +1,73 @@
+import axios from '../utils/request'
+// 获取用户列表
+export const userList = (data) => {
+  return axios({
+    method: 'post',
+    url: '/sys/user/list',
+    data
+  })
+}
+// 新增/编辑用户
+export const userSave = (data) => {
+  return axios({
+    method: 'post',
+    url: '/sys/user/save',
+    data
+  })
+}
+
+// 删除用户
+export const userDel = (ids) => {
+  return axios({
+    method: 'get',
+    url: `/sys/user/removes/${ids}`
+  })
+}
+
+// 启用、停用账户
+export const editStatus = (id, isEnabled) => {
+  return axios({
+    method: 'get',
+    url: `/sys/user/editStatus/${id}/${isEnabled}`
+  })
+}
+// 重置密码
+export const userResetPass = (id) => {
+  return axios({
+    method: 'get',
+    url: `/sys/user/resetPass/${id}`
+  })
+}
+
+// 操作日志
+export const logList = (data) => {
+  return axios({
+    method: 'post',
+    url: '/sys/log/list',
+    data
+  })
+}
+
+// 授权项目
+export const userAuths = (userId, projectIds) => {
+  return axios({
+    method: 'get',
+    url: `/sys/user/auth/${userId}?projectIds=${projectIds}`
+  })
+}
+
+// 统计-5分钟更新一次
+export const getStatistics = () => {
+  return axios({
+    method: 'get',
+    url: '/cms/village/getStatistics'
+  })
+}
+
+// 获取场景打卡列表
+export const sceneGetList = () => {
+  return axios({
+    method: 'get',
+    url: '/cms/scene/getList'
+  })
+}

+ 41 - 0
houtai/src/apis/tab1.js

@@ -0,0 +1,41 @@
+import axios from '../utils/request'
+// 获取列表
+export const villageList = () => {
+  return axios({
+    method: 'get',
+    url: '/cms/village/getList'
+  })
+}
+
+// 获取菜单树
+export const getTreeMenu = () => {
+  return axios({
+    method: 'get',
+    url: '/cms/village/getTreeMenu'
+  })
+}
+
+// 删除文件
+export const delFileApi = (fileIds) => {
+  return axios({
+    method: 'post',
+    url: `/cms/content/delFile/${fileIds}`
+  })
+}
+
+// 新增和编辑村落信息
+export const saveEntity = (data) => {
+  return axios({
+    method: 'post',
+    url: '/cms/content/saveEntity',
+    data
+  })
+}
+
+// 获取信息,用户回显数据
+export const contentList = (villageId, menuId) => {
+  return axios({
+    method: 'get',
+    url: `/cms/content/list/${villageId}/${menuId}`
+  })
+}

+ 231 - 0
houtai/src/assets/css/base.css

@@ -0,0 +1,231 @@
+* {
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
+}
+body {
+  background-color: transparent;
+}
+ul li {
+  list-style: none;
+}
+a {
+  text-decoration:none ;
+  color: #b9412e;
+}
+.insideTop {
+  position: relative;
+  padding-left: 30px;
+  font-weight: 700;
+  height: 40px;
+  margin-bottom: 12px;
+}
+.insideTop .add{
+  position: absolute;
+  top: -10px;
+  right: 30px;
+}
+.obstruct{
+  position: absolute;
+  left: 0px;
+  top: 60px;
+  width: 100%;
+  background-color: #f2ecde;
+  height: 12px;
+}
+.biaoshi{
+  position: relative;
+  font-style:normal
+}
+.biaoshi::before{
+  position: absolute;
+  top: 0px;
+  left: -92px;
+  content: '*';
+  color: #F56C6C;
+}
+
+.el-button--primary{
+  background-color: #b9412e;
+  border-color: #b9412e;
+}
+.el-button--primary:focus, .el-button--primary:hover {
+  background: #a30014;
+  border-color: #a30014;
+}
+.el-input.is-active .el-input__inner, .el-input__inner:focus {
+  border-color: #b9412e;
+}
+.cell{
+  text-align: center !important;
+}
+.table .el-input__inner{
+  text-align: center;
+}
+.el-input__inner{
+  line-height: normal;
+}
+input::-webkit-outer-spin-button,
+input::-webkit-inner-spin-button {
+-webkit-appearance: none;
+}
+input[type="number"] {
+-moz-appearance: textfield;
+}
+.el-dialog{
+  min-width: 652px;
+}
+.el-range-editor.is-active, .el-range-editor.is-active:hover{
+  border-color: #b9412e;
+}
+.el-date-table td.today span{
+  color: #b9412e;
+}
+.el-date-table td.end-date span, .el-date-table td.start-date span{
+  background-color: #b9412e;
+}
+.el-date-table td.available:hover{
+  color: #b9412e;
+}
+.el-select .el-input__inner:focus{
+  border-color: #b9412e;
+}
+.el-select-dropdown__item.selected{
+  color: #b9412e;
+}
+.el-select .el-input.is-focus .el-input__inner{
+  border-color: #b9412e;
+}
+.el-table--fit{
+  border-top: 1px solid #EBEEF5;
+}
+.table_img{
+  width: 100px;
+  height: 80px;
+  object-fit: cover;
+  border: 3px solid #ccc;
+}
+.table_name{
+  cursor: pointer;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;    
+}
+
+.el-button--text:focus, .el-button--text:hover{
+  color: #e06a58;
+}
+.upHint{
+  color: black;
+  height: 20px;
+}
+.el-radio__input.is-checked+.el-radio__label{
+  color: #964134;
+}
+.el-radio__input.is-checked .el-radio__inner{
+  border-color: #964134;
+  background: #964134;
+}
+.el-image__preview{
+  border: 5px solid #cccccc;
+  object-fit: cover;
+}
+.el-pager li{
+  border-radius: 5px;
+  margin: 0 5px;
+  min-width: 28px;
+  background-color: transparent;
+}
+.el-pagination .btn-next, .el-pagination .btn-prev{
+  background-color: transparent;
+  padding: 0;
+  min-width: 28px;
+}
+.el-pagination__sizes .el-input .el-input__inner:hover{
+  border-color: #b9412e;
+}
+.el-pager li:hover{
+  color: #b9412e;
+}
+.el-pager li.active{
+  color: #b9412e;
+  background-color: transparent;
+}
+.el-pagination button:hover{
+  color: #b9412e;
+}
+.el-upload--picture-card:hover, .el-upload:focus{
+  color: #b9412e;
+}
+.w-e-menu {
+	z-index: 2 !important;
+}
+.w-e-text-container {
+	z-index: 1 !important;
+}
+.w-e-toolbar {
+	z-index: 10 !important;
+}
+.w-e-toolbar .w-e-menu:nth-of-type(18) {
+	display: none !important;
+}
+.w-e-toolbar .w-e-menu:nth-of-type(19) {
+	display: none !important;
+}
+.w-e-toolbar .w-e-menu:nth-of-type(13) {
+	display: none !important;
+}
+.w-e-toolbar .w-e-menu:nth-of-type(14) {
+	display: none !important;
+}
+.w-e-toolbar .w-e-menu:nth-of-type(16) {
+	display: none !important;
+}
+.w-e-toolbar .w-e-menu:nth-of-type(20) {
+	display: none !important;
+}
+.w-e-toolbar .w-e-menu:nth-of-type(21) {
+	display: none !important;
+}
+.w-e-toolbar .w-e-menu:nth-of-type(26) {
+	display: none !important;
+}
+
+.el-month-table td.end-date .cell, .el-month-table td.start-date .cell{
+  background-color: #b9412e;
+}
+
+.el-month-table td.today .cell{
+  color: #b9412e;
+}
+.el-message-box__title{
+  color: #b9412e;
+}
+textarea{
+  resize: none !important;
+}
+.el-textarea__inner {
+  height: 150px;
+}
+.el-textarea .el-input__count {
+  bottom: -30px;
+  right: 0px;
+  background-color: transparent;
+}
+.el-upload-dragger .smEl {
+  color: #ccc;
+  font-size: 12px;
+  margin-top: -15px;
+}
+.el-upload-list{
+  width: 360px;
+}
+.el-textarea__inner:focus {
+  border-color: #b9412e;
+}
+.el-upload-list__item .el-icon-close-tip{
+  top: -8px;
+}
+.el-message-box__message p{
+  word-break:break-all;
+}

BIN
houtai/src/assets/img/1.png


BIN
houtai/src/assets/img/2.png


BIN
houtai/src/assets/img/3.png


BIN
houtai/src/assets/img/4.png


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


BIN
houtai/src/assets/imgAdd/comment.png


BIN
houtai/src/assets/imgAdd/like.png


BIN
houtai/src/assets/imgAdd/likeAc.png


BIN
houtai/src/assets/imgAdd/mesBac.png


BIN
houtai/src/assets/imgAdd/touxiang.png


+ 21 - 0
houtai/src/main.js

@@ -0,0 +1,21 @@
+import Vue from 'vue'
+import ElementUI from 'element-ui'
+import 'element-ui/lib/theme-chalk/index.css'
+import App from './App.vue'
+import router from './router'
+import './assets/css/base.css'
+
+Vue.config.productionTip = false
+// const imgErr = {
+//   data () {
+//     return {
+//       defaultImg: 'this.src="' + require('./assets/img/IMGerror.png') + '"'
+//     }
+//   }
+// }
+// Vue.mixin(imgErr)
+Vue.use(ElementUI)
+new Vue({
+  router,
+  render: h => h(App)
+}).$mount('#app')

+ 71 - 0
houtai/src/router/index.js

@@ -0,0 +1,71 @@
+import Vue from 'vue'
+import VueRouter from 'vue-router'
+import { Message } from 'element-ui'
+Vue.use(VueRouter)
+
+const routes = [
+  {
+    path: '/',
+    name: 'login',
+    component: () => import('../views/login.vue')
+  },
+  {
+    path: '/layout',
+    name: 'layout',
+    component: () => import('../views/layout/index.vue'),
+    children: [
+      {
+        path: 'tab1',
+        name: 'tab1',
+        meta: { myInd: 1 },
+        component: () => import('../views/tab1/index.vue')
+      },
+      {
+        path: 'tab1Main/:id/:k',
+        name: 'tab1Main',
+        meta: { myInd: 1 },
+        component: () => import('../views/tab1Main/index.vue')
+      },
+      {
+        path: 'tab2',
+        name: 'tab2',
+        meta: { myInd: 2 },
+        component: () => import('../views/tab2/index.vue')
+      },
+      {
+        path: 'tab3',
+        name: 'tab3',
+        meta: { myInd: 3 },
+        component: () => import('../views/tab3/index.vue')
+      },
+      {
+        path: 'tab4',
+        name: 'tab4',
+        meta: { myInd: 4 },
+        component: () => import('../views/tab4/index.vue')
+      }
+    ]
+  }
+]
+
+const router = new VueRouter({
+  // mode: 'history',
+  base: process.env.BASE_URL,
+  routes
+})
+
+router.beforeEach((to, from, next) => {
+  // 如果是去登录页,不需要验证,直接下一步
+  if (to.name === 'login') next()
+  // 否则要有token值才能下一步,不然就返回登录页
+  else {
+    const token = localStorage.getItem('JMYZU_token')
+    if (token) next()
+    else {
+      Message.warning('登录失效,请重新登录')
+      next({ name: 'login' })
+    }
+  }
+})
+
+export default router

+ 106 - 0
houtai/src/utils/pass.js

@@ -0,0 +1,106 @@
+/* eslint-disable */
+function NoToChinese (num) {
+  num = String(num)
+  var chnNumChar = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十']
+  if (num == 0) {
+    return chnNumChar[0]
+  }
+  let tmp = ''
+  for (let i = 0; i < num.length; i++) {
+    const ele = num.charAt(i)
+    tmp += chnNumChar[ele]
+  }
+
+  return tmp
+}
+
+function randomWord (randomFlag, min, max) {
+  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
+}
+
+module.exports = {
+  formatDate: function (time) {
+    var weekArr = ['日', '一', '二', '三', '四', '五', '六']
+    var date = new Date(time)
+    var year = date.getFullYear()
+    var month = date.getMonth() + 1
+    var day = date.getDate()
+    var week = '星期' + weekArr[date.getDay()]
+    if (window.innerWidth < 1700) {
+      return (
+        year +
+        '年' +
+        (String(month).length > 1 ? month : '0' + month) +
+        '月' +
+        (String(day).length > 1 ? day : '0' + day) +
+        '日' +
+        '<br/>' +
+        week
+      )
+    }
+    return (
+      year +
+      '年' +
+      (String(month).length > 1 ? month : '0' + month) +
+      '月' +
+      (String(day).length > 1 ? day : '0' + day) +
+      '日' +
+      ' ' +
+      week
+    )
+  },
+  smoothscrollpos: function (domName) {
+    if (domName == '/') {
+      return window.scrollTo(0, 0)
+    }
+    const smoothscroll = () => {
+      const dom = document.getElementById(domName)
+      // window.scrollTo({
+      //   top:dom.offsetTop - 100,
+      //   left:0,
+      //   behavior: "smooth"
+      // })
+      dom && window.scrollTo(0, dom.offsetTop - 120)
+    }
+    smoothscroll()
+  },
+
+  formatTime: function (time, fan = false) {
+    let t1 = time.split(' ')[0].split('-')
+    if (fan) {
+      t1 = t1.map((item) => {
+        const t = NoToChinese(item)
+        return t
+      })
+    }
+    return t1
+  },
+  encodeStr: function (str, 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
+  }
+}

+ 38 - 0
houtai/src/utils/request.js

@@ -0,0 +1,38 @@
+import axios from 'axios'
+const service = axios.create({
+  // baseURL: 'http://192.168.0.135:8016', // 本地调试
+  baseURL: process.env.NODE_ENV === 'development' ? 'http://project.4dage.com:8016' : '',
+  timeout: 5000
+})
+// 请求拦截器
+service.interceptors.request.use(function (config) {
+  // console.log('触发拦截器')
+  // 在发送请求之前做些什么:看看有没有token,如果有通过请求头的方式传递token
+  const token = localStorage.getItem('JMYZU_token')
+  if (token) { // 判断是否有token,有,则
+    // config.headers['Authorization'] = token
+    config.headers.token = token
+    config.headers.TRACE_ID = Date.now()
+  }
+
+  return config
+}, function (error) {
+  // 对请求错误做些什么
+  return Promise.reject(error)
+})
+
+// 添加响应拦截器
+service.interceptors.response.use(function (response) {
+  // console.log('触发相应拦截器', response)
+  // 对响应数据做点什么--response就是发送每个请求的返回值
+  if (response.data.code === 5001 || response.data.code === 5002) {
+    localStorage.removeItem('JMYZU_token')
+    location.reload(true)
+  }
+  return response.data
+}, function (error) {
+  // 对响应错误做点什么
+  return Promise.reject(error)
+})
+
+export default service

+ 358 - 0
houtai/src/views/layout/index.vue

@@ -0,0 +1,358 @@
+<template>
+  <div class="layout">
+    <div class="top">
+      <p>江门市中国传统村落一张图管理后台</p>
+      <div class="top_right" @mouseenter="cut = true" @mouseleave="cut = false">
+        <div class="user">
+          <span>{{ userInfo.userName }}</span>
+          <!-- 下箭头 -->
+          <div class="pull_down" v-if="cut"></div>
+          <div class="pull_up" v-else></div>
+        </div>
+        <!-- 点击箭头出来的ul -->
+        <ul class="user_handle" v-show="cut">
+          <li @click="isShow = true">修改密码</li>
+          <li @click="outLogin">退出登录</li>
+        </ul>
+      </div>
+    </div>
+    <div class="con">
+      <div class="left">
+        <div
+          class="biaoji el-icon-message"
+          :class="{ biaojiAc: $route.meta.myInd === 1 }"
+        >
+          内容管理
+        </div>
+        <ul>
+          <li
+            v-for="item in tab1"
+            :key="item.id"
+            :class="{ active: $route.meta.myInd === item.id }"
+            @click="push(item.url)"
+          >
+            {{ item.name }}
+          </li>
+        </ul>
+        <div
+          v-if="userAdmin"
+          class="biaoji el-icon-setting"
+          :class="{ biaojiAc: $route.meta.myInd > 1 }"
+        >
+          系统管理
+        </div>
+        <ul v-if="userAdmin">
+          <li
+            v-for="item in tab2"
+            :key="item.id"
+            :class="{ active: $route.meta.myInd === item.id }"
+            @click="push(item.url)"
+          >
+            {{ item.name }}
+          </li>
+        </ul>
+      </div>
+      <!-- 右侧内容 -->
+      <div class="right" @click="cut = false">
+        <Router-view />
+      </div>
+    </div>
+    <!-- 点击修改密码出现弹窗 -->
+    <el-dialog title="修改密码" :visible.sync="isShow" @close="btnX()">
+      <el-form :model="form" label-width="100px" :rules="rules" ref="ruleForm">
+        <el-form-item label="旧密码:" prop="oldPassword">
+          <el-input
+            v-model="form.oldPassword"
+            placeholder="请输入"
+            show-password
+          ></el-input>
+        </el-form-item>
+        <el-form-item label="新密码:" prop="newPassword">
+          <el-input
+            v-model="form.newPassword"
+            placeholder="请输入"
+            show-password
+          ></el-input>
+        </el-form-item>
+        <el-form-item label="确定新密码:" prop="checkPass">
+          <el-input
+            v-model="form.checkPass"
+            placeholder="请输入"
+            show-password
+          ></el-input>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="btnX">取 消</el-button>
+        <el-button type="primary" @click="btnOk">确 定</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { encodeStr } from '../../utils/pass'
+import { Base64 } from 'js-base64'
+import { updatePwd } from '@/apis/login'
+export default {
+  name: 'layout',
+  components: {},
+  data () {
+    // 这里存放数据
+    const validatePass2 = (rule, value, callback) => {
+      if (value !== this.form.newPassword) {
+        callback(new Error('两次输入密码不一致!'))
+      } else {
+        callback()
+      }
+    }
+    return {
+      userAdmin: false,
+
+      // 修改密码
+      form: {
+        oldPassword: '',
+        newPassword: '',
+        checkPass: ''
+      },
+      rules: {
+        checkPass: [{ validator: validatePass2, trigger: 'blur' }],
+        oldPassword: [{ required: true, message: '不能为空', trigger: 'blur' }],
+        newPassword: [
+          { required: true, message: '不能为空', trigger: 'blur' },
+          { max: 15, message: '不能超过15个字', trigger: 'blur' }
+        ]
+      },
+      userInfo: {},
+      cut: false,
+      isShow: false,
+      tab1: [{ name: '资料管理', id: 1, url: '/layout/tab1' }],
+      tab2: [
+        { name: '用户管理', id: 2, url: '/layout/tab2' },
+        { name: '数据统计', id: 3, url: '/layout/tab3' },
+        { name: '操作日志', id: 4, url: '/layout/tab4' }
+      ]
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {},
+  // 监控data中的数据变化
+  watch: {},
+  // 方法集合
+  methods: {
+    // 修改密码点击取消
+    btnX () {
+      this.$refs.ruleForm.resetFields()
+      this.cut = false
+      this.isShow = false
+      this.form = {
+        oldPassword: '',
+        newPassword: '',
+        checkPass: ''
+      }
+    },
+    // 修改密码点击确定
+    async btnOk () {
+      await this.$refs.ruleForm.validate()
+      try {
+        const data = {
+          oldPassword: encodeStr(Base64.encode(this.form.oldPassword)),
+          newPassword: encodeStr(Base64.encode(this.form.newPassword))
+        }
+        const res = await updatePwd(data)
+        if (res.code === 0) {
+          this.$message.success('修改成功')
+          localStorage.clear('SWKK_token')
+          localStorage.clear('SWKK_userInfo')
+          this.$router.push('/')
+        } else this.$message.error(res.msg)
+      } catch (error) {
+        console.log(error)
+      }
+    },
+    push (url) {
+      this.$router.push(url).catch(() => {})
+    },
+    // 点击退出登录
+    outLogin () {
+      this.cut = false
+      this.$confirm('确定退出吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      })
+        .then(() => {
+          localStorage.clear('JMYZU_token')
+          localStorage.clear('JMYZU_userInfo')
+          this.$router.push('/')
+          this.$message.success('退出成功!')
+        })
+        .catch(() => {
+          this.$message.info('已取消')
+        })
+    }
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created () {
+    // 获取用户名
+    const res = JSON.parse(localStorage.getItem('JMYZU_userInfo'))
+    if (res) {
+      this.userInfo = res
+      if (res.isAdmin === 1) this.userAdmin = true
+    } else {
+      localStorage.clear('JMYZU_token')
+      localStorage.clear('JMYZU_userInfo')
+      this.$router.push('/')
+    }
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {},
+  beforeCreate () {}, // 生命周期 - 创建之前
+  beforeMount () {}, // 生命周期 - 挂载之前
+  beforeUpdate () {}, // 生命周期 - 更新之前
+  updated () {}, // 生命周期 - 更新之后
+  beforeDestroy () {}, // 生命周期 - 销毁之前
+  destroyed () {}, // 生命周期 - 销毁完成
+  activated () {} // 如果页面有keep-alive缓存功能,这个函数会触发
+}
+</script>
+<style lang='less' scoped>
+.layout {
+  .top {
+    padding-left: 50px;
+    position: relative;
+    width: 100%;
+    min-width: 1860px;
+    box-shadow: 1px 3px 3px 3px rgb(196, 177, 177);
+    height: 70px;
+    background-color: #a30014;
+    display: flex;
+    align-items: center;
+    & > P {
+      color: #fff;
+      font-size: 24px;
+      font-weight: 700;
+    }
+    .top_right {
+      width: 150px;
+      position: absolute;
+      right: 30px;
+      top: 0;
+      height: 100%;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      .user {
+        color: #fff;
+        cursor: pointer;
+        position: relative;
+        display: flex;
+        align-items: center;
+        font-weight: 700;
+        img {
+          margin-right: 15px;
+          width: 34px;
+          height: 34px;
+          border-radius: 50%;
+          overflow: hidden;
+        }
+        .pull_down {
+          position: absolute;
+          right: -25px;
+          bottom: 4px;
+          display: inline-block;
+          width: 0;
+          height: 0;
+          border-left: 8px solid transparent;
+          border-right: 8px solid transparent;
+          border-bottom: 8px solid #fff;
+        }
+        .pull_up {
+          cursor: pointer;
+          position: absolute;
+          right: -25px;
+          bottom: 4px;
+          display: inline-block;
+          width: 0;
+          height: 0;
+          border-left: 8px solid transparent;
+          border-right: 8px solid transparent;
+          border-top: 8px solid #fff;
+        }
+      }
+      .user_handle {
+        z-index: 999;
+        padding: 10px 40px;
+        position: absolute;
+        right: 0;
+        bottom: -90px;
+        background-color: #a30014;
+        li {
+          color: #fff;
+          cursor: pointer;
+          margin: 10px 0;
+        }
+        li:hover {
+          color: orange;
+        }
+      }
+    }
+  }
+  .con {
+    min-width: 1860px;
+    min-height: 807px;
+    background-color: #f2ecde;
+    display: flex;
+    width: 100%;
+    padding: 30px;
+    height: calc(100vh - 70px);
+  }
+  .left {
+    min-width: 180px;
+    width: 180px;
+    height: 100%;
+    min-height: 770px;
+    background-color: #fff;
+    padding-top: 30px;
+    .biaoji {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      height: 40px;
+      width: 90%;
+      border-radius: 0 40px 40px 0;
+      font-weight: 700;
+      font-size: 20px;
+      padding-left: 20px;
+      padding-right: 30px;
+    }
+    .biaojiAc {
+      background-color: #a30014;
+      color: #fff;
+    }
+    ul {
+      li {
+        cursor: pointer;
+        padding-left: 55px;
+        margin: 30px 0;
+        &:hover {
+          color: #a30014;
+        }
+      }
+      .active {
+        color: #a30014;
+      }
+    }
+  }
+  .right {
+    position: relative;
+    padding: 20px 0 0 0;
+    min-width: 1500px;
+    flex: 1;
+    margin-left: 30px;
+    background-color: #fff;
+    height: 100%;
+    min-height: 770px;
+  }
+}
+</style>

+ 159 - 0
houtai/src/views/login.vue

@@ -0,0 +1,159 @@
+<!--  -->
+<template>
+  <div class="login">
+    <div class="top">江门市中国传统村落一张图</div>
+    <div class="con">
+      <div class="left">
+        <p>江门市中国传统村落</p>
+        <p>一张图管理后台</p>
+      </div>
+      <div class="right">
+        <p>欢迎登录</p>
+        <div class="input">
+          <el-form
+            :model="ruleForm"
+            :rules="rules"
+            ref="ruleForm"
+            class="demo-ruleForm"
+          >
+            <el-form-item prop="userName">
+              <el-input
+                v-model="ruleForm.userName"
+                placeholder="账号"
+              ></el-input>
+            </el-form-item>
+            <el-form-item prop="passWord">
+              <el-input
+                show-password
+                v-model="ruleForm.passWord"
+                placeholder="密码"
+              ></el-input>
+            </el-form-item>
+          </el-form>
+        </div>
+        <div class="btn" @click="login">登 录</div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { encodeStr } from '../utils/pass'
+import { Base64 } from 'js-base64'
+import { userLogin } from '@/apis/login'
+export default {
+  name: 'login',
+  components: {},
+  data () {
+    // 这里存放数据
+    return {
+      ruleForm: {
+        userName: '',
+        passWord: ''
+      },
+      rules: {
+        userName: [{ required: true, message: '不能为空', trigger: 'blur' }],
+        passWord: [{ required: true, message: '不能为空', trigger: 'blur' }]
+      }
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {},
+  // 监控data中的数据变化
+  watch: {},
+  // 方法集合
+  methods: {
+    async login () {
+      try {
+        await this.$refs.ruleForm.validate()
+        const data = {
+          userName: this.ruleForm.userName,
+          passWord: encodeStr(Base64.encode(this.ruleForm.passWord))
+        }
+        const res = await userLogin(data)
+        if (res.code === 0) {
+          localStorage.setItem('JMYZU_token', res.data.token)
+          localStorage.setItem('JMYZU_userInfo', JSON.stringify(res.data.user))
+          this.$router.push('/layout/tab1')
+          this.$message.success('登录成功')
+        } else this.$message.warning(res.msg)
+      } catch (error) {
+        console.log(error)
+      }
+    }
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created () {},
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {},
+  beforeCreate () {}, // 生命周期 - 创建之前
+  beforeMount () {}, // 生命周期 - 挂载之前
+  beforeUpdate () {}, // 生命周期 - 更新之前
+  updated () {}, // 生命周期 - 更新之后
+  beforeDestroy () {}, // 生命周期 - 销毁之前
+  destroyed () {}, // 生命周期 - 销毁完成
+  activated () {} // 如果页面有keep-alive缓存功能,这个函数会触发
+}
+</script>
+<style lang='less' scoped>
+.login {
+  width: 100vw;
+  height: 100vh;
+  background: url("../assets/img/bg.jpg");
+  background-size: cover;
+  .top {
+    letter-spacing: 10px;
+    font-size: 24px;
+    font-weight: 700;
+    color: #fff;
+    padding: 40px 0 0 35px;
+  }
+  .con {
+    display: flex;
+    margin: 180px auto 0;
+    width: 700px;
+    height: 400px;
+    .left {
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+      width: 350px;
+      height: 100%;
+      background-color: rgba(163, 0, 20, 0.8);
+      & > p {
+        width: 80%;
+        margin: 0 auto;
+        margin-bottom: 30px;
+        color: #fff;
+        font-weight: 700;
+        text-align: center;
+        font-size: 30px;
+      }
+    }
+    .right {
+      width: 350px;
+      background-color: rgba(255, 255, 255, 0.8);
+      & > p {
+        font-size: 24px;
+        text-align: center;
+        margin-top: 50px;
+      }
+      .input {
+        width: 90%;
+        margin: 50px auto;
+      }
+      .btn {
+        cursor: pointer;
+        width: 90%;
+        height: 40px;
+        line-height: 40px;
+        text-align: center;
+        margin: 50px auto 0;
+        background-color: #b9412e;
+        border-radius: 8px;
+        color: #fff;
+      }
+    }
+  }
+}
+</style>

+ 106 - 0
houtai/src/views/tab1/index.vue

@@ -0,0 +1,106 @@
+<template>
+  <div class="tab1">
+    <div class="insideTop">资料管理</div>
+    <div class="obstruct"></div>
+    <div class="conten">
+      <!-- <div class="search">
+        <span>村落名称:</span>
+        <el-input
+          type="text"
+          placeholder="请输入内容"
+          v-model="name"
+          maxlength="25"
+          show-word-limit
+        />
+        <el-button type="primary">查询</el-button>
+        <el-button>重置</el-button>
+      </div> -->
+      <div class="table">
+        <el-table :data="tableData" style="width: 100%">
+          <el-table-column label="编号" width="150">
+            <template slot-scope="scope">
+              {{ scope.$index + 1 }}
+            </template>
+          </el-table-column>
+          <!-- <el-table-column prop="id" label="编号" width="200"></el-table-column> -->
+          <el-table-column prop="name" label="村落名称"></el-table-column>
+          <el-table-column
+            prop="updateTime"
+            label="最近更新时间"
+          ></el-table-column>
+          <el-table-column label="操作">
+            <template #default="{ row }">
+              <el-button
+                type="text"
+                @click="$router.push(`/layout/tab1Main/${row.id}/1001`)"
+                >编辑</el-button
+              >
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { villageList } from '@/apis/tab1'
+export default {
+  name: 'tab1',
+  components: {},
+  data () {
+    // 这里存放数据
+    return {
+      name: '',
+      tableData: []
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {},
+  // 监控data中的数据变化
+  watch: {},
+  // 方法集合
+  methods: {},
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  async created () {
+    const res = await villageList()
+    this.tableData = res.data
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {},
+  beforeCreate () {}, // 生命周期 - 创建之前
+  beforeMount () {}, // 生命周期 - 挂载之前
+  beforeUpdate () {}, // 生命周期 - 更新之前
+  updated () {}, // 生命周期 - 更新之后
+  beforeDestroy () {}, // 生命周期 - 销毁之前
+  destroyed () {}, // 生命周期 - 销毁完成
+  activated () {} // 如果页面有keep-alive缓存功能,这个函数会触发
+}
+</script>
+<style lang='less' scoped>
+.tab1 {
+  width: 100%;
+  height: 100%;
+  .conten {
+    padding: 0 20px;
+    .search {
+      height: 70px;
+      display: flex;
+      align-items: center;
+      border-bottom: 1px solid black;
+      /deep/.el-input {
+        width: 300px;
+        margin-right: 12px;
+      }
+      /deep/.el-input__inner {
+        padding-right: 45px;
+      }
+    }
+    .table {
+      /deep/.el-table__row .el-table__cell {
+        padding: 3px 0;
+      }
+    }
+  }
+}
+</style>

+ 534 - 0
houtai/src/views/tab1Main/five.vue

@@ -0,0 +1,534 @@
+<!--  -->
+<template>
+  <div class="five">
+    <div class="main">
+      <div class="row" v-for="(item, index) in data" :key="item.id">
+        <div class="top">
+          <span
+            @click="data[index].flag = !data[index].flag"
+            :class="`${item.flag ? 'el-icon-arrow-up' : 'el-icon-arrow-down'}`"
+            >&emsp;{{ index + 1 }}</span
+          >
+          <el-input
+            v-model="item.name"
+            placeholder="请输入名称"
+            maxlength="25"
+          ></el-input>
+          <i
+            v-if="index !== 0"
+            class="el-icon-close"
+            @click="delMen(index)"
+          ></i>
+        </div>
+        <div class="txt" v-show="item.flag">
+          <div class="txtBox">
+            <span>产权归属:</span>
+            <el-input
+              v-model="item.txt1"
+              maxlength="25"
+              show-word-limit
+            ></el-input>
+          </div>
+          <div class="txtBox">
+            <span>占地面积:</span>
+            <el-input
+              v-model="item.txt2"
+              maxlength="25"
+              show-word-limit
+            ></el-input>
+          </div>
+          <div class="txtBox">
+            <span>建筑面积:</span>
+            <el-input
+              v-model="item.txt3"
+              maxlength="25"
+              show-word-limit
+            ></el-input>
+          </div>
+          <div class="txtBox">
+            <span>保护级别:</span>
+            <el-input
+              v-model="item.txt4"
+              maxlength="25"
+              show-word-limit
+            ></el-input>
+          </div>
+          <div class="txtBox">
+            <span>建筑概况:</span>
+            <el-input
+              type="textarea"
+              v-model="item.txt5"
+              maxlength="3000"
+              show-word-limit
+            ></el-input>
+          </div>
+          <div class="txtBox">
+            <span class="upImgSpan">上传图片:</span>
+
+            <div class="imgBox">
+              <div
+                @click="lookImg(val)"
+                class="imgdiv"
+                v-for="(val, indSon) in item.imgList"
+                :key="val.id"
+              >
+                <img :src="baseURL + val.filePath" alt="" />
+                <!-- 封面显示 -->
+                <div class="cover" v-if="item.imgActive === val.id">封面</div>
+                <!-- 下面的按钮 -->
+                <div
+                  class="handle"
+                  v-else
+                  @click.stop="goodsImgIndex(item, val.id)"
+                >
+                  设为封面
+                </div>
+                <!-- 删除 -->
+                <div
+                  class="delImg el-icon-delete"
+                  @click.stop="delGoodsImg(indSon, item, val.id)"
+                  v-if="item.imgActive !== val.id"
+                ></div>
+              </div>
+              <el-upload
+                v-if="item.imgList.length < 20"
+                accept=".png,.jpg,.jpeg"
+                :action="baseURL + '/cms/content/upload'"
+                :data="{
+                  menuId: menuId,
+                  type: 'img',
+                  villageId: villageId,
+                }"
+                :show-file-list="true"
+                class="avatar-uploader"
+                :headers="{ token }"
+                :before-upload="beforethumbUploadImg"
+                :on-success="
+                  (res) => {
+                    return upload_thumb_successImg(res, item);
+                  }
+                "
+              >
+                <div class="upImg">
+                  <i slot="default" class="el-icon-plus"></i>
+                </div>
+              </el-upload>
+            </div>
+            <p class="upHint">支持格式:jpg, png,最大支持 20M</p>
+          </div>
+        </div>
+      </div>
+    </div>
+    <!-- 底部按钮 -->
+    <div class="btnn">
+      <!-- 增加人物 -->
+      <div class="el-icon-plus" @click="addMen">增加内容</div>
+
+      <el-button @click="$router.push('/layout/tab1')">返 回</el-button>&emsp;
+      <el-button type="primary" @click="save">保 存</el-button>
+    </div>
+    <!-- 照片墙的查看照片 -->
+    <el-dialog :visible.sync="dialogVisible">
+      <img width="100%" :src="dialogImageUrl" alt="" />
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import axios from '@/utils/request'
+import { saveEntity, contentList, delFileApi } from '@/apis/tab1'
+export default {
+  components: {},
+  data () {
+    return {
+      // 服务器前缀地址
+      token: '',
+      baseURL: '',
+
+      // 村落ID
+      villageId: null,
+      // 菜单ID
+      menuId: null,
+      // 本身自己的id
+      id: null,
+      // 查看图片
+      imgListLook: [],
+      data: [
+        {
+          id: Date.now(),
+          name: '',
+          txt1: '',
+          txt2: '',
+          txt3: '',
+          txt4: '',
+          txt5: '',
+          flag: true,
+          imgActive: 0,
+          imgList: []
+        }
+      ],
+      // 照片墙数据
+      dialogImageUrl: '',
+      dialogVisible: false
+    }
+  },
+  computed: {},
+  methods: {
+    // ---------------照片墙
+    beforethumbUploadImg (file) {
+      // console.log(998, file)
+      // 限制图片大小和格式
+      const sizeOk = file.size / 1024 / 1024 < 20
+      const typeOk =
+        file.type === 'image/png' ||
+        (file.type === 'image/jpeg' && !file.name.includes('.jfif'))
+
+      return new Promise((resolve, reject) => {
+        if (!typeOk) {
+          this.$message.error('图片格式有误!')
+          reject(file)
+        } else if (!sizeOk) {
+          this.$message.error('图片大小超过20M!')
+          reject(file)
+        } else {
+          resolve(file)
+        }
+      })
+    },
+    upload_thumb_successImg (data, item) {
+      this.$message.success('上传图片成功')
+      item.imgList.push(data.data)
+      // 如果是第一张上传的图片,把id变成第一张
+      if (item.imgList.length === 1) item.imgActive = data.data.id
+    },
+    // 点击删除图片
+    delGoodsImg (indSon, item, id) {
+      this.$confirm('此操作将永久删除图片', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      })
+        .then(async () => {
+          item.imgList.splice(indSon, 1)
+          this.$message.success('删除成功')
+          await delFileApi(id)
+          const obj = {
+            menuId: this.menuId,
+            villageId: this.villageId,
+            contentJson: JSON.stringify(this.data),
+            id: this.id
+          }
+          await saveEntity(obj)
+        })
+        .catch(() => {})
+    },
+    // 点击设为封面
+    goodsImgIndex (item, id) {
+      item.imgActive = id
+      // this.ruleForm.thumb = this.imgList[index].filePath
+      this.$message.success('操作成功')
+    },
+    // 点击图片看大图
+    lookImg (val) {
+      this.dialogImageUrl = this.baseURL + val.filePath
+      this.dialogVisible = true
+    },
+
+    // 点击删除
+    delMen (index) {
+      this.data.splice(index, 1)
+    },
+    // 点击增加人物
+    addMen () {
+      this.data.push({
+        id: Date.now(),
+        name: '',
+        txt1: '',
+        txt2: '',
+        txt3: '',
+        txt4: '',
+        txt5: '',
+        flag: true,
+        imgActive: 0,
+        imgList: []
+      })
+    },
+    // 点击保存
+    async save () {
+      let tempFlag = false
+      this.data.forEach((v) => {
+        v.flag = true
+        if (
+          v.name.trim() === '' ||
+          v.txt1.trim() === '' ||
+          v.txt2.trim() === '' ||
+          v.txt3.trim() === '' ||
+          v.txt4.trim() === '' ||
+          v.imgList.length <= 0
+        ) {
+          tempFlag = true
+        }
+      })
+      if (tempFlag) return this.$message.warning('信息或图片不能为空')
+
+      const obj = {
+        menuId: this.menuId,
+        villageId: this.villageId,
+        contentJson: JSON.stringify(this.data),
+        id: this.id
+      }
+      const res = await saveEntity(obj)
+      if (res.code === 0) {
+        this.$message.success('操作成功')
+        this.id = res.data.id
+      } else this.$message.warning(res.msg)
+    }
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  async created () {
+    // 获取服务器前缀地址
+    this.baseURL = axios.defaults.baseURL
+    // 获取用户token
+    this.token = localStorage.getItem('JMYZU_token')
+    this.villageId = Number(this.$route.params.id)
+    this.menuId = Number(this.$route.params.k)
+    const res = await contentList(this.villageId, this.menuId)
+    if (res.data.length > 0) {
+      const temp = res.data[0]
+      this.id = temp.id
+      this.data = JSON.parse(temp.contentJson)
+    }
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {},
+  beforeCreate () {}, // 生命周期 - 创建之前
+  beforeMount () {}, // 生命周期 - 挂载之前
+  beforeUpdate () {}, // 生命周期 - 更新之前
+  updated () {}, // 生命周期 - 更新之后
+  beforeDestroy () {}, // 生命周期 - 销毁之前
+  destroyed () {}, // 生命周期 - 销毁完成
+  activated () {} // 如果页面有keep-alive缓存功能,这个函数会触发
+}
+</script>
+<style lang='less' scoped>
+.five {
+  /deep/.avatar-uploader .el-upload-list__item-name {
+    display: none;
+  }
+  /deep/.avatar-uploader
+    .el-upload-list__item.is-success
+    .el-upload-list__item-status-label {
+    display: none;
+  }
+  /deep/.el-upload-list__item .el-progress {
+    width: 500px;
+    position: static;
+    margin-bottom: 15px;
+  }
+  /deep/.el-upload-list__item {
+    width: 500px;
+  }
+  width: 100%;
+  height: 100%;
+  position: relative;
+  .main {
+    padding: 30px 20px;
+    height: calc(100% - 120px);
+    overflow-y: auto;
+    .row {
+      margin-bottom: 50px;
+      .top {
+        position: relative;
+        width: 100%;
+        padding: 0 20px;
+        height: 50px;
+        line-height: 50px;
+        background-color: #f2f2f2;
+        .el-icon-close {
+          cursor: pointer;
+          font-weight: 700;
+          font-size: 26px;
+          position: absolute;
+          top: 50%;
+          right: 24px;
+          transform: translateY(-50%);
+        }
+        & > span {
+          cursor: pointer;
+          font-weight: 700;
+          margin-right: 15px;
+        }
+        /deep/.el-input {
+          height: 36px;
+          width: 380px;
+        }
+        /deep/.el-input__inner {
+          color: black;
+          height: 36px;
+          line-height: 36px;
+          background-color: transparent;
+          border: none;
+          border-bottom: 1px solid black;
+          border-radius: 0;
+        }
+        /*修改提示文字的颜色*/
+        /deep/input::-webkit-input-placeholder {
+          /* WebKit browsers */
+          color: #999;
+        }
+        /deep/input:-moz-placeholder {
+          /* Mozilla Firefox 4 to 18 */
+          color: #999;
+        }
+        /deep/input::-moz-placeholder {
+          /* Mozilla Firefox 19+ */
+          color: #999;
+        }
+        /deep/input:-ms-input-placeholder {
+          /* Internet Explorer 10+ */
+          color: #999;
+        }
+      }
+      .txt {
+        width: 90%;
+        margin: 20px auto;
+        .txtBox {
+          margin-bottom: 10px;
+          position: relative;
+          width: 100%;
+          display: flex;
+          & > span {
+            width: 90px;
+            text-align: right;
+            line-height: 40px;
+            &::after {
+              position: absolute;
+              top: 0px;
+              left: -6px;
+              content: "*";
+              color: #f56c6c;
+            }
+          }
+          .upImgSpan {
+            width: 84px;
+            &::after {
+              left: -6px;
+            }
+          }
+          .imgBox {
+            display: flex;
+            flex-wrap: wrap;
+            /deep/.el-icon-plus {
+              font-size: 28px;
+              color: #8c939d;
+              display: flex;
+              align-items: center;
+              justify-content: center;
+              border: 1px dashed #ccc;
+              width: 148px;
+              height: 148px;
+              background-color: #fbfdff;
+              border: 1px dashed #c0ccda;
+              border-radius: 6px;
+            }
+            /deep/.el-icon-plus:hover {
+              border-color: #409eff;
+            }
+            .imgdiv {
+              border-radius: 5px;
+              overflow: hidden;
+              position: relative;
+              margin-right: 30px;
+              margin-bottom: 10px;
+              height: 148px;
+              & > img {
+                width: 148px;
+                height: 148px;
+                object-fit: cover;
+              }
+              .handle {
+                cursor: pointer;
+                color: #fff;
+                font-size: 16px;
+                display: flex;
+                justify-content: center;
+                align-items: center;
+                width: 100%;
+                z-index: 999;
+                position: absolute;
+                bottom: 0;
+                left: 0;
+                height: 30px;
+                background-color: rgba(0, 0, 0, 0.6);
+              }
+              .delImg {
+                cursor: pointer;
+                border-radius: 10px;
+                color: #fff;
+                background-color: #a30014;
+                padding: 3px;
+                font-size: 26px;
+                position: absolute;
+                top: 5px;
+                right: 5px;
+              }
+              .cover {
+                color: #fff;
+                pointer-events: none;
+                font-size: 16px;
+                display: flex;
+                justify-content: center;
+                align-items: center;
+                width: 100%;
+                z-index: 999;
+                position: absolute;
+                bottom: 0;
+                left: 0;
+                height: 30px;
+                background-color: rgba(163, 0, 20, 0.8);
+              }
+            }
+          }
+          .upHint {
+            position: absolute;
+            bottom: -25px;
+            left: 80px;
+            font-size: 12px;
+            color: #606266;
+          }
+        }
+        /deep/.el-textarea .el-input__count {
+          bottom: -20px;
+        }
+      }
+      /deep/.el-textarea__inner {
+        height: 80px;
+      }
+    }
+  }
+  .btnn {
+    border-top: 1px solid #d7d7d7;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    height: 70px;
+    width: 100%;
+    .el-icon-plus {
+      position: absolute;
+      top: -50px;
+      left: 50%;
+      transform: translateX(-50%);
+      font-weight: 700;
+      color: #02aff4;
+      cursor: pointer;
+      width: 100px;
+      text-align: center;
+      line-height: 40px;
+      height: 40px;
+    }
+  }
+}
+</style>

+ 524 - 0
houtai/src/views/tab1Main/four.vue

@@ -0,0 +1,524 @@
+<!--  -->
+<template>
+  <div class="four">
+    <div class="main">
+      <div class="row" v-for="(item, index) in data" :key="item.id">
+        <div class="top">
+          <span
+            @click="data[index].flag = !data[index].flag"
+            :class="`${item.flag ? 'el-icon-arrow-up' : 'el-icon-arrow-down'}`"
+            >&emsp;{{ index + 1 }}</span
+          >
+          <el-input
+            v-model="item.name"
+            placeholder="请输入名称"
+            maxlength="25"
+          ></el-input>
+          <i
+            v-if="index !== 0"
+            class="el-icon-close"
+            @click="delMen(index)"
+          ></i>
+        </div>
+        <div class="txt" v-show="item.flag">
+          <div class="txtBox">
+            <span>编号:</span>
+            <el-input
+              v-model="item.txt1"
+              maxlength="25"
+              show-word-limit
+            ></el-input>
+          </div>
+          <div class="txtBox">
+            <span>类型:</span>
+            <el-input
+              v-model="item.txt2"
+              maxlength="25"
+              show-word-limit
+            ></el-input>
+          </div>
+          <div class="txtBox">
+            <span>年代:</span>
+            <el-input
+              v-model="item.txt3"
+              maxlength="25"
+              show-word-limit
+            ></el-input>
+          </div>
+          <div class="txtBox">
+            <span>简介:</span>
+            <el-input
+              type="textarea"
+              v-model="item.txt4"
+              maxlength="3000"
+              show-word-limit
+            ></el-input>
+          </div>
+          <div class="txtBox">
+            <span class="upImgSpan">上传图片:</span>
+
+            <div class="imgBox">
+              <div
+                @click="lookImg(val)"
+                class="imgdiv"
+                v-for="(val, indSon) in item.imgList"
+                :key="val.id"
+              >
+                <img :src="baseURL + val.filePath" alt="" />
+                <!-- 封面显示 -->
+                <div class="cover" v-if="item.imgActive === val.id">封面</div>
+                <!-- 下面的按钮 -->
+                <div
+                  class="handle"
+                  v-else
+                  @click.stop="goodsImgIndex(item, val.id)"
+                >
+                  设为封面
+                </div>
+                <!-- 删除 -->
+                <div
+                  class="delImg el-icon-delete"
+                  @click.stop="delGoodsImg(indSon, item, val.id)"
+                  v-if="item.imgActive !== val.id"
+                ></div>
+              </div>
+              <el-upload
+                v-if="item.imgList.length < 20"
+                accept=".png,.jpg,.jpeg"
+                :action="baseURL + '/cms/content/upload'"
+                :data="{
+                  menuId: menuId,
+                  type: 'img',
+                  villageId: villageId,
+                }"
+                :show-file-list="true"
+                class="avatar-uploader"
+                :headers="{ token }"
+                :before-upload="beforethumbUploadImg"
+                :on-success="
+                  (res) => {
+                    return upload_thumb_successImg(res, item);
+                  }
+                "
+              >
+                <div class="upImg">
+                  <i slot="default" class="el-icon-plus"></i>
+                </div>
+              </el-upload>
+            </div>
+            <p class="upHint">支持格式:jpg, png,最大支持 20M</p>
+          </div>
+        </div>
+      </div>
+    </div>
+    <!-- 底部按钮 -->
+    <div class="btnn">
+      <!-- 增加人物 -->
+      <div class="el-icon-plus" @click="addMen">增加内容</div>
+
+      <el-button @click="$router.push('/layout/tab1')">返 回</el-button>&emsp;
+      <el-button type="primary" @click="save">保 存</el-button>
+    </div>
+    <!-- 照片墙的查看照片 -->
+    <el-dialog :visible.sync="dialogVisible">
+      <img width="100%" :src="dialogImageUrl" alt="" />
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import axios from '@/utils/request'
+import { saveEntity, contentList, delFileApi } from '@/apis/tab1'
+export default {
+  components: {},
+  data () {
+    return {
+      // 服务器前缀地址
+      token: '',
+      baseURL: '',
+
+      // 村落ID
+      villageId: null,
+      // 菜单ID
+      menuId: null,
+      // 本身自己的id
+      id: null,
+      // 查看图片
+      imgListLook: [],
+      data: [
+        {
+          id: Date.now(),
+          name: '',
+          txt1: '',
+          txt2: '',
+          txt3: '',
+          txt4: '',
+          flag: true,
+          imgActive: 0,
+          imgList: []
+        }
+      ],
+      // 照片墙数据
+      dialogImageUrl: '',
+      dialogVisible: false
+    }
+  },
+  computed: {},
+  methods: {
+    // ---------------照片墙
+    beforethumbUploadImg (file) {
+      // console.log(998, file)
+      // 限制图片大小和格式
+      const sizeOk = file.size / 1024 / 1024 < 20
+      const typeOk =
+        file.type === 'image/png' ||
+        (file.type === 'image/jpeg' && !file.name.includes('.jfif'))
+
+      return new Promise((resolve, reject) => {
+        if (!typeOk) {
+          this.$message.error('图片格式有误!')
+          reject(file)
+        } else if (!sizeOk) {
+          this.$message.error('图片大小超过20M!')
+          reject(file)
+        } else {
+          resolve(file)
+        }
+      })
+    },
+    upload_thumb_successImg (data, item) {
+      this.$message.success('上传图片成功')
+      item.imgList.push(data.data)
+      // 如果是第一张上传的图片,把id变成第一张
+      if (item.imgList.length === 1) item.imgActive = data.data.id
+    },
+    // 点击删除图片
+    delGoodsImg (indSon, item, id) {
+      this.$confirm('此操作将永久删除图片', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      })
+        .then(async () => {
+          item.imgList.splice(indSon, 1)
+          this.$message.success('删除成功')
+          await delFileApi(id)
+          const obj = {
+            menuId: this.menuId,
+            villageId: this.villageId,
+            contentJson: JSON.stringify(this.data),
+            id: this.id
+          }
+          await saveEntity(obj)
+        })
+        .catch(() => {})
+    },
+    // 点击设为封面
+    goodsImgIndex (item, id) {
+      item.imgActive = id
+      // this.ruleForm.thumb = this.imgList[index].filePath
+      this.$message.success('操作成功')
+    },
+    // 点击图片看大图
+    lookImg (val) {
+      this.dialogImageUrl = this.baseURL + val.filePath
+      this.dialogVisible = true
+    },
+
+    // 点击删除
+    delMen (index) {
+      this.data.splice(index, 1)
+    },
+    // 点击增加人物
+    addMen () {
+      this.data.push({
+        id: Date.now(),
+        name: '',
+        txt1: '',
+        txt2: '',
+        txt3: '',
+        txt4: '',
+        flag: true,
+        imgActive: 0,
+        imgList: []
+      })
+    },
+    // 点击保存
+    async save () {
+      let tempFlag = false
+      this.data.forEach((v) => {
+        v.flag = true
+        if (
+          v.name.trim() === '' ||
+          v.txt1.trim() === '' ||
+          v.txt2.trim() === '' ||
+          v.txt3.trim() === '' ||
+          v.txt4.trim() === '' ||
+          v.imgList.length <= 0
+        ) {
+          tempFlag = true
+        }
+      })
+      if (tempFlag) return this.$message.warning('信息或图片不能为空')
+
+      const obj = {
+        menuId: this.menuId,
+        villageId: this.villageId,
+        contentJson: JSON.stringify(this.data),
+        id: this.id
+      }
+      const res = await saveEntity(obj)
+      if (res.code === 0) {
+        this.$message.success('操作成功')
+        this.id = res.data.id
+      } else this.$message.warning(res.msg)
+    }
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  async created () {
+    // 获取服务器前缀地址
+    this.baseURL = axios.defaults.baseURL
+    // 获取用户token
+    this.token = localStorage.getItem('JMYZU_token')
+    this.villageId = Number(this.$route.params.id)
+    this.menuId = Number(this.$route.params.k)
+    const res = await contentList(this.villageId, this.menuId)
+    if (res.data.length > 0) {
+      const temp = res.data[0]
+      this.id = temp.id
+      this.data = JSON.parse(temp.contentJson)
+    }
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {},
+  beforeCreate () {}, // 生命周期 - 创建之前
+  beforeMount () {}, // 生命周期 - 挂载之前
+  beforeUpdate () {}, // 生命周期 - 更新之前
+  updated () {}, // 生命周期 - 更新之后
+  beforeDestroy () {}, // 生命周期 - 销毁之前
+  destroyed () {}, // 生命周期 - 销毁完成
+  activated () {} // 如果页面有keep-alive缓存功能,这个函数会触发
+}
+</script>
+<style lang='less' scoped>
+.four {
+  /deep/.avatar-uploader .el-upload-list__item-name {
+    display: none;
+  }
+  /deep/.avatar-uploader
+    .el-upload-list__item.is-success
+    .el-upload-list__item-status-label {
+    display: none;
+  }
+  /deep/.el-upload-list__item .el-progress {
+    width: 500px;
+    position: static;
+    margin-bottom: 15px;
+  }
+  /deep/.el-upload-list__item {
+    width: 500px;
+  }
+  width: 100%;
+  height: 100%;
+  position: relative;
+  .main {
+    padding: 30px 20px;
+    height: calc(100% - 120px);
+    overflow-y: auto;
+    .row {
+      margin-bottom: 50px;
+      .top {
+        position: relative;
+        width: 100%;
+        padding: 0 20px;
+        height: 50px;
+        line-height: 50px;
+        background-color: #f2f2f2;
+        .el-icon-close {
+          cursor: pointer;
+          font-weight: 700;
+          font-size: 26px;
+          position: absolute;
+          top: 50%;
+          right: 24px;
+          transform: translateY(-50%);
+        }
+        & > span {
+          cursor: pointer;
+          font-weight: 700;
+          margin-right: 15px;
+        }
+        /deep/.el-input {
+          height: 36px;
+          width: 380px;
+        }
+        /deep/.el-input__inner {
+          color: black;
+          height: 36px;
+          line-height: 36px;
+          background-color: transparent;
+          border: none;
+          border-bottom: 1px solid black;
+          border-radius: 0;
+        }
+        /*修改提示文字的颜色*/
+        /deep/input::-webkit-input-placeholder {
+          /* WebKit browsers */
+          color: #999;
+        }
+        /deep/input:-moz-placeholder {
+          /* Mozilla Firefox 4 to 18 */
+          color: #999;
+        }
+        /deep/input::-moz-placeholder {
+          /* Mozilla Firefox 19+ */
+          color: #999;
+        }
+        /deep/input:-ms-input-placeholder {
+          /* Internet Explorer 10+ */
+          color: #999;
+        }
+      }
+      .txt {
+        width: 90%;
+        margin: 20px auto;
+        .txtBox {
+          margin-bottom: 10px;
+          position: relative;
+          width: 100%;
+          display: flex;
+          & > span {
+            width: 85px;
+            text-align: right;
+            line-height: 40px;
+            &::after {
+              position: absolute;
+              top: 0px;
+              left: 20px;
+              content: "*";
+              color: #f56c6c;
+            }
+          }
+          .upImgSpan {
+            width: 80px;
+            &::after {
+              left: -10px;
+            }
+          }
+          .imgBox {
+            display: flex;
+            flex-wrap: wrap;
+            /deep/.el-icon-plus {
+              font-size: 28px;
+              color: #8c939d;
+              display: flex;
+              align-items: center;
+              justify-content: center;
+              border: 1px dashed #ccc;
+              width: 148px;
+              height: 148px;
+              background-color: #fbfdff;
+              border: 1px dashed #c0ccda;
+              border-radius: 6px;
+            }
+            /deep/.el-icon-plus:hover {
+              border-color: #409eff;
+            }
+            .imgdiv {
+              border-radius: 5px;
+              overflow: hidden;
+              position: relative;
+              margin-right: 30px;
+              margin-bottom: 10px;
+              height: 148px;
+              & > img {
+                width: 148px;
+                height: 148px;
+                object-fit: cover;
+              }
+              .handle {
+                cursor: pointer;
+                color: #fff;
+                font-size: 16px;
+                display: flex;
+                justify-content: center;
+                align-items: center;
+                width: 100%;
+                z-index: 999;
+                position: absolute;
+                bottom: 0;
+                left: 0;
+                height: 30px;
+                background-color: rgba(0, 0, 0, 0.6);
+              }
+              .delImg {
+                cursor: pointer;
+                border-radius: 10px;
+                color: #fff;
+                background-color: #a30014;
+                padding: 3px;
+                font-size: 26px;
+                position: absolute;
+                top: 5px;
+                right: 5px;
+              }
+              .cover {
+                color: #fff;
+                pointer-events: none;
+                font-size: 16px;
+                display: flex;
+                justify-content: center;
+                align-items: center;
+                width: 100%;
+                z-index: 999;
+                position: absolute;
+                bottom: 0;
+                left: 0;
+                height: 30px;
+                background-color: rgba(163, 0, 20, 0.8);
+              }
+            }
+          }
+          .upHint {
+            position: absolute;
+            bottom: -25px;
+            left: 80px;
+            font-size: 12px;
+            color: #606266;
+          }
+        }
+        /deep/.el-textarea .el-input__count {
+          bottom: -20px;
+        }
+      }
+      /deep/.el-textarea__inner {
+        height: 80px;
+      }
+    }
+  }
+  .btnn {
+    border-top: 1px solid #d7d7d7;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    height: 70px;
+    width: 100%;
+    .el-icon-plus {
+      position: absolute;
+      top: -50px;
+      left: 50%;
+      transform: translateX(-50%);
+      font-weight: 700;
+      color: #02aff4;
+      cursor: pointer;
+      width: 100px;
+      text-align: center;
+      line-height: 40px;
+      height: 40px;
+    }
+  }
+}
+</style>

+ 206 - 0
houtai/src/views/tab1Main/index.vue

@@ -0,0 +1,206 @@
+<template>
+  <div class="tab1Main">
+    <div class="insideTop">资料管理</div>
+    <div class="obstruct"></div>
+    <div class="conten">
+      <div class="top">编辑资料</div>
+      <div class="left">
+        <div class="name">{{ info.name }}</div>
+        <div
+          class="row"
+          v-for="(item, index) in data"
+          :key="index"
+          :class="{ open: tabInd == item.id }"
+        >
+          <div
+            class="rowOne el-icon-menu"
+            @click="cutTab(item.id)"
+            :class="{ noneClick: tabInd == item.id }"
+          >
+            <span class="txt">{{ item.name }}</span>
+            <i
+              :class="`${
+                tabInd == item.id ? 'el-icon-arrow-up' : 'el-icon-arrow-down'
+              }`"
+            ></i>
+          </div>
+          <div
+            class="rowList"
+            :class="{
+              active: $route.params.k == val.id,
+            }"
+            @click="cutSon(val.id)"
+            v-for="(val, ind) in item.children"
+            :key="ind"
+          >
+            {{ val.name }}
+          </div>
+        </div>
+      </div>
+      <div class="right">
+        <Tow v-if="towCom.includes($route.params.k)" :key="$route.params.k" />
+        <Three
+          v-else-if="threeCom.includes($route.params.k)"
+          :key="$route.params.k"
+        />
+        <Four
+          v-else-if="fourCom.includes($route.params.k)"
+          :key="$route.params.k"
+        />
+        <Five
+          v-else-if="fiveCom.includes($route.params.k)"
+          :key="$route.params.k"
+        />
+        <One v-else :key="$route.params.k" />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { villageList, getTreeMenu } from '@/apis/tab1'
+import One from './one.vue'
+import Tow from './tow.vue'
+import Three from './three.vue'
+import Four from './four.vue'
+import Five from './five.vue'
+export default {
+  name: 'tab1Main',
+  components: { One, Tow, Three, Four, Five },
+  data () {
+    // 这里存放数据
+    return {
+      tabInd: null,
+      tabSon: null,
+      data: [],
+      info: {},
+      towCom: '1002',
+      threeCom: '1006,3003,4006,5009',
+      fourCom: '2007',
+      fiveCom: '3001'
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {},
+  // 监控data中的数据变化
+  watch: {},
+  // 方法集合
+  methods: {
+    cutSon (id) {
+      this.tabSon = id
+      this.$router.push(`/layout/tab1Main/${this.$route.params.id}/${id}`)
+    },
+    cutTab (id) {
+      this.tabInd = id
+      const temp = (Number(id) + 1).toString()
+      this.tabSon = temp
+      this.$router.push(`/layout/tab1Main/${this.$route.params.id}/${temp}`)
+    }
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  async created () {
+    const res = await getTreeMenu()
+    this.data = res.data
+    const res2 = await villageList()
+    res2.data.forEach((v) => {
+      if (v.id === Number(this.$route.params.id)) this.info = v
+    })
+    // 如果没有该村落权限
+    if (!this.info.id) {
+      alert('没有该页面权限,请联系管理员!')
+      localStorage.clear('JMYZU_token')
+      localStorage.clear('JMYZU_userInfo')
+      this.$router.push('/')
+    }
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {
+    let k = this.$route.params.k
+    this.tabSon = k
+    const length = k.length
+    const arr = k.split('')
+    arr[length - 1] = '0'
+    k = arr.join('')
+    this.tabInd = k
+  },
+  beforeCreate () {}, // 生命周期 - 创建之前
+  beforeMount () {}, // 生命周期 - 挂载之前
+  beforeUpdate () {}, // 生命周期 - 更新之前
+  updated () {}, // 生命周期 - 更新之后
+  beforeDestroy () {}, // 生命周期 - 销毁之前
+  destroyed () {}, // 生命周期 - 销毁完成
+  activated () {} // 如果页面有keep-alive缓存功能,这个函数会触发
+}
+</script>
+<style lang='less' scoped>
+.tab1Main {
+  width: 100%;
+  height: 100%;
+  .conten {
+    display: flex;
+    position: relative;
+    padding: 34px 20px 0;
+    height: calc(100% - 50px);
+    .top {
+      position: absolute;
+      top: 0;
+      left: 0;
+      height: 34px;
+      line-height: 34px;
+      width: 100%;
+      background-color: #f2f1f0;
+      font-weight: 700;
+      padding-left: 30px;
+    }
+    .left {
+      padding-top: 10px;
+      width: 140px;
+      height: 100%;
+      border-right: 1px solid #d7d7d7;
+      .row {
+        margin-top: 20px;
+        .rowOne {
+          cursor: pointer;
+          width: 100%;
+          position: relative;
+          i {
+            position: absolute;
+            top: 50%;
+            right: 5px;
+            transform: translateY(-50%);
+          }
+          .txt {
+            margin-left: 10px;
+          }
+        }
+        .noneClick {
+          pointer-events: none;
+        }
+        .rowList {
+          cursor: pointer;
+          padding-left: 18px;
+          height: 0;
+          overflow: hidden;
+          transition: margin 0.3s;
+          &:hover {
+            color: #a30014;
+          }
+        }
+        .active {
+          pointer-events: none;
+          color: #a30014;
+        }
+      }
+      .open .rowList {
+        margin-top: 10px;
+        height: auto;
+      }
+    }
+    .right {
+      width: calc(100% - 140px);
+      height: 100%;
+      overflow-y: auto;
+    }
+  }
+}
+</style>

+ 329 - 0
houtai/src/views/tab1Main/one.vue

@@ -0,0 +1,329 @@
+<!--  -->
+<template>
+  <div class="one">
+    <div class="from">
+      <el-form
+        :model="ruleForm"
+        :rules="rules"
+        ref="ruleForm"
+        label-width="100px"
+        class="demo-ruleForm"
+      >
+        <!-- 文本域 -->
+        <el-form-item :label="txtobj[menuId]" prop="name">
+          <el-input
+            placeholder="请输入内容"
+            type="textarea"
+            v-model="ruleForm.name"
+            maxlength="5000"
+            show-word-limit
+          ></el-input>
+        </el-form-item>
+        <!-- 照片墙 -->
+        <el-form-item label="上传图片:">
+          <el-upload
+            accept=".png,.jpg,.jpeg"
+            :action="baseURL + '/cms/content/upload'"
+            :data="{
+              menuId: menuId,
+              type: 'img',
+              villageId: villageId,
+            }"
+            :file-list="imgUpList"
+            :headers="{ token }"
+            :limit="20"
+            :on-exceed="handleExceed"
+            list-type="picture-card"
+            :on-preview="handlePictureCardPreview"
+            :on-remove="handleRemove"
+            :before-remove="beforeRemove"
+            :before-upload="beforethumbUploadImg"
+            :on-success="upload_thumb_successImg"
+          >
+            <i class="el-icon-plus"></i>
+            <div slot="tip" class="el-upload__tip">
+              支持格式:jpg, png,最大支持 20M
+            </div>
+          </el-upload>
+        </el-form-item>
+        <!-- 上传视频 -->
+        <el-form-item label="上传视频:">
+          <el-upload
+            accept=".mp4"
+            multiple
+            drag
+            class="upload-demo"
+            :data="{
+              menuId: this.menuId,
+              type: 'video',
+              villageId: this.villageId,
+            }"
+            :file-list="fileList"
+            :action="baseURL + '/cms/content/upload'"
+            :headers="{ token }"
+            :before-upload="beforeFujian"
+            :on-success="successFujian"
+            :before-remove="beforeRemoveFujian"
+            :on-remove="handleRemoveFujian"
+            :limit="5"
+            :on-exceed="handleExceedFujian"
+            :show-file-list="true"
+          >
+            <i class="el-icon-upload"></i>
+            <div class="el-upload__text">
+              将文件拖到此处,或<em>点击上传</em>
+            </div>
+            <div class="el-upload__text smEl">
+              仅支持mp4格式的视频文件,大小不得超过500MB
+            </div>
+          </el-upload>
+        </el-form-item>
+      </el-form>
+    </div>
+    <!-- 底部按钮 -->
+    <div class="btnn">
+      <el-button @click="$router.push('/layout/tab1')">返 回</el-button>&emsp;
+      <el-button type="primary" @click="save">保 存</el-button>
+    </div>
+    <!-- 照片墙的查看照片 -->
+    <el-dialog :visible.sync="dialogVisible">
+      <img width="100%" :src="dialogImageUrl" alt="" />
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import axios from '@/utils/request'
+import { delFileApi, saveEntity, contentList } from '@/apis/tab1'
+export default {
+  name: 'mainOne',
+  components: {},
+  data () {
+    return {
+      txtobj: {
+        1001: '建村文化:',
+        1003: '历史事件:',
+        1004: '族谱家训:',
+        1005: '口述史:',
+        2001: '村落选址:',
+        2002: '村落格局:',
+        2003: '建村智慧:',
+        2004: '自然环境:',
+        2005: '风景名胜:',
+        2006: '文物古迹:',
+        3002: '村落风貌:',
+        4001: '节庆活动:',
+        4002: '祭祀崇礼:',
+        4003: '婚丧嫁娶:',
+        4004: '地方方言:',
+        4005: '特色文化:',
+        5001: '特色物产:',
+        5002: '商业集市:',
+        5003: '生产工艺:',
+        5004: '生产工具:',
+        5005: '服装服饰:',
+        5006: '美味美食:',
+        5007: '运输工具:',
+        5008: '村规民约:',
+        6001: '入村路线:',
+        6002: '村内导览:'
+      },
+      // 服务器前缀地址
+      token: '',
+      baseURL: '',
+      // 村落ID
+      villageId: null,
+      // 菜单ID
+      menuId: null,
+      ruleForm: {
+        name: '',
+        id: null
+      },
+      rules: {
+        name: [{ required: true, message: '不能为空', trigger: 'blur' }]
+      },
+      // 照片墙数据
+      dialogImageUrl: '',
+      dialogVisible: false,
+      imgIdArr: [],
+      imgUpList: [],
+      // 上传视频数据
+      fileList: [],
+      videoIdArr: []
+    }
+  },
+  computed: {},
+  methods: {
+    // 点击保存
+    async save () {
+      if (this.ruleForm.name.trim() === '') {
+        return this.$message.warning('文字信息不能为空')
+      }
+      const obj = {
+        menuId: this.menuId,
+        villageId: this.villageId,
+        content: this.ruleForm.name,
+        id: this.ruleForm.id,
+        imgIds: this.imgIdArr.join(','),
+        videoIds: this.videoIdArr.join(',')
+      }
+      const res = await saveEntity(obj)
+      if (res.code === 0) {
+        this.$message.success('保存成功')
+        this.ruleForm.id = res.data.id
+      } else this.$message.warning(res.msg)
+    },
+    // ---------------照片墙
+    beforeRemove (file, fileList) {
+      if (file && file.status === 'success') {
+        return this.$confirm(`此操作将永久删除 ${file.name}`)
+      }
+    },
+    async handleRemove (file, fileList) {
+      const id = file.id || file.response.data.id
+      const res = await delFileApi(id)
+      if (res.code === 0) {
+        this.$message.success('删除成功')
+        this.imgIdArr = this.imgIdArr.filter((v) => v !== id)
+      } else this.$message.warning(res.msg)
+    },
+    handlePictureCardPreview (file) {
+      this.dialogImageUrl = file.url
+      this.dialogVisible = true
+    },
+    beforethumbUploadImg (file) {
+      // console.log(998, file)
+      // 限制图片大小和格式
+      const sizeOk = file.size / 1024 / 1024 < 20
+      const typeOk =
+        file.type === 'image/png' ||
+        (file.type === 'image/jpeg' && !file.name.includes('.jfif'))
+
+      return new Promise((resolve, reject) => {
+        if (!typeOk) {
+          this.$message.error('图片格式有误!')
+          reject(file)
+        } else if (!sizeOk) {
+          this.$message.error('图片大小超过20M!')
+          reject(file)
+        } else {
+          resolve(file)
+        }
+      })
+    },
+    upload_thumb_successImg (data) {
+      this.$message.success('上传图片成功')
+      this.imgIdArr.push(data.data.id)
+    },
+    handleExceed (files, fileList) {
+      this.$message.warning('最多上传20张图片')
+    },
+
+    // -------------------上传视频
+    beforeFujian (file) {
+      console.log('附件上传前', file)
+      const sizeOk = file.size / 1024 / 1024 < 500
+      const typeOk = file.type === 'video/mp4'
+      return new Promise((resolve, reject) => {
+        if (!sizeOk) {
+          this.$message.error('视频大小超过500M!')
+          reject(file)
+        } else if (!typeOk) {
+          this.$message.error('视频格式有误!')
+          reject(file)
+        } else {
+          resolve(file)
+        }
+      })
+    },
+    successFujian (file) {
+      console.log('上传视频成功', file)
+      if (file.code === 0) {
+        this.$message.success('上传成功')
+        this.videoIdArr.push(file.data.id)
+      } else if (file.code === -1) {
+        this.$message.warning('上传失败,不支持的文件格式')
+      }
+    },
+    beforeRemoveFujian (file, fileList) {
+      if (file && file.status === 'success') {
+        return this.$confirm(`此操作将永久删除 ${file.name}`)
+      }
+    },
+    async handleRemoveFujian (file, fileList) {
+      const id = file.id || file.response.data.id
+      const res = await delFileApi(id)
+      if (res.code === 0) {
+        this.$message.success('删除成功')
+        this.videoIdArr = this.videoIdArr.filter((v) => v !== id)
+      } else this.$message.warning(res.msg)
+    },
+    handleExceedFujian (files, fileList) {
+      this.$message.warning('最多上传5个视频')
+    }
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  async created () {
+    this.villageId = Number(this.$route.params.id)
+    this.menuId = Number(this.$route.params.k)
+    // 获取服务器前缀地址
+    this.baseURL = axios.defaults.baseURL
+    // 获取用户token
+    this.token = localStorage.getItem('JMYZU_token')
+    const res = await contentList(this.villageId, this.menuId)
+    if (res.data.length > 0) {
+      const temp = res.data[0]
+      this.ruleForm.name = temp.content
+      this.ruleForm.id = temp.id
+      // 附件回显
+      this.fileList = temp.videos || []
+      // 图片回显
+      if (temp.images) {
+        temp.images.forEach((v) => {
+          v.url = this.baseURL + v.filePath
+        })
+      }
+      this.imgUpList = temp.images || []
+      // id存储
+      this.fileList.forEach((v) => {
+        this.videoIdArr.push(v.id)
+      })
+      this.imgUpList.forEach((v) => {
+        this.imgIdArr.push(v.id)
+      })
+    }
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {},
+  beforeCreate () {}, // 生命周期 - 创建之前
+  beforeMount () {}, // 生命周期 - 挂载之前
+  beforeUpdate () {}, // 生命周期 - 更新之前
+  updated () {}, // 生命周期 - 更新之后
+  beforeDestroy () {}, // 生命周期 - 销毁之前
+  destroyed () {}, // 生命周期 - 销毁完成
+  activated () {} // 如果页面有keep-alive缓存功能,这个函数会触发
+}
+</script>
+<style lang='less' scoped>
+.one {
+  width: 100%;
+  height: 100%;
+  position: relative;
+  .from {
+    padding: 30px 20px;
+    height: calc(100% - 70px);
+    overflow-y: auto;
+  }
+  .btnn {
+    border-top: 1px solid #d7d7d7;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    height: 70px;
+    width: 100%;
+  }
+}
+</style>

+ 182 - 0
houtai/src/views/tab1Main/three.vue

@@ -0,0 +1,182 @@
+<!--  -->
+<template>
+  <div class="three">
+    <div class="from">
+      <!-- 上传视频 -->
+      <el-form label-width="100px" class="demo-ruleForm">
+        <el-form-item label="上传视频:">
+          <el-upload
+            accept=".mp4"
+            multiple
+            drag
+            class="upload-demo"
+            :data="{
+              menuId: this.menuId,
+              type: 'video',
+              villageId: this.villageId,
+            }"
+            :file-list="fileList"
+            :action="baseURL + '/cms/content/upload'"
+            :headers="{ token }"
+            :before-upload="beforeFujian"
+            :on-success="successFujian"
+            :before-remove="beforeRemoveFujian"
+            :on-remove="handleRemoveFujian"
+            :limit="5"
+            :on-exceed="handleExceedFujian"
+            :show-file-list="true"
+          >
+            <i class="el-icon-upload"></i>
+            <div class="el-upload__text">
+              将文件拖到此处,或<em>点击上传</em>
+            </div>
+            <div class="el-upload__text smEl">
+              仅支持mp4格式的视频文件,大小不得超过500MB
+            </div>
+          </el-upload>
+        </el-form-item>
+      </el-form>
+    </div>
+    <!-- 底部按钮 -->
+    <div class="btnn">
+      <el-button @click="$router.push('/layout/tab1')">返 回</el-button>&emsp;
+      <el-button type="primary" @click="save">保 存</el-button>
+    </div>
+  </div>
+</template>
+
+<script>
+import axios from '@/utils/request'
+import { delFileApi, saveEntity, contentList } from '@/apis/tab1'
+export default {
+  components: {},
+  data () {
+    return {
+      // 服务器前缀地址
+      token: '',
+      baseURL: '',
+      // 村落ID
+      villageId: null,
+      // 菜单ID
+      menuId: null,
+      // 本身的id
+      id: null,
+      // 上传视频数据
+      fileList: [],
+      videoIdArr: []
+    }
+  },
+  computed: {},
+  methods: {
+    // 点击保存
+    async save () {
+      const obj = {
+        menuId: this.menuId,
+        villageId: this.villageId,
+        id: this.id,
+        videoIds: this.videoIdArr.join(',')
+      }
+      const res = await saveEntity(obj)
+      if (res.code === 0) {
+        this.$message.success('保存成功')
+        this.id = res.data.id
+      } else this.$message.warning(res.msg)
+    },
+    // -------------------上传视频
+    beforeFujian (file) {
+      console.log('附件上传前', file)
+      const sizeOk = file.size / 1024 / 1024 < 500
+      const typeOk = file.type === 'video/mp4'
+      return new Promise((resolve, reject) => {
+        if (!sizeOk) {
+          this.$message.error('视频大小超过500M!')
+          reject(file)
+        } else if (!typeOk) {
+          this.$message.error('视频格式有误!')
+          reject(file)
+        } else {
+          resolve(file)
+        }
+      })
+    },
+    successFujian (file) {
+      console.log('上传视频成功', file)
+      if (file.code === 0) {
+        this.$message.success('上传成功')
+        this.videoIdArr.push(file.data.id)
+      } else if (file.code === -1) {
+        this.$message.warning('上传失败,不支持的文件格式')
+      }
+    },
+    beforeRemoveFujian (file, fileList) {
+      if (file && file.status === 'success') {
+        return this.$confirm(`此操作将永久删除 ${file.name}`)
+      }
+    },
+    async handleRemoveFujian (file, fileList) {
+      const id = file.id || file.response.data.id
+      const res = await delFileApi(id)
+      if (res.code === 0) {
+        this.$message.success('删除成功')
+        this.videoIdArr = this.videoIdArr.filter((v) => v !== id)
+      } else this.$message.warning(res.msg)
+    },
+    handleExceedFujian (files, fileList) {
+      this.$message.warning('最多上传5个视频')
+    }
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  async created () {
+    this.villageId = Number(this.$route.params.id)
+    this.menuId = Number(this.$route.params.k)
+    // 获取服务器前缀地址
+    this.baseURL = axios.defaults.baseURL
+    // 获取用户token
+    this.token = localStorage.getItem('JMYZU_token')
+    const res = await contentList(this.villageId, this.menuId)
+    if (res.data.length > 0) {
+      const temp = res.data[0]
+      this.id = temp.id
+      // 附件回显
+      this.fileList = temp.videos || []
+      // id存储
+      this.fileList.forEach((v) => {
+        this.videoIdArr.push(v.id)
+      })
+    }
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {},
+  beforeCreate () {}, // 生命周期 - 创建之前
+  beforeMount () {}, // 生命周期 - 挂载之前
+  beforeUpdate () {}, // 生命周期 - 更新之前
+  updated () {}, // 生命周期 - 更新之后
+  beforeDestroy () {}, // 生命周期 - 销毁之前
+  destroyed () {}, // 生命周期 - 销毁完成
+  activated () {} // 如果页面有keep-alive缓存功能,这个函数会触发
+}
+</script>
+<style lang='less' scoped>
+.three {
+  width: 100%;
+  height: 100%;
+  position: relative;
+  .from {
+    padding: 30px 20px;
+    height: calc(100% - 70px);
+    overflow-y: auto;
+  }
+
+  .btnn {
+    border-top: 1px solid #d7d7d7;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    height: 70px;
+    width: 100%;
+  }
+}
+</style>

+ 223 - 0
houtai/src/views/tab1Main/tow.vue

@@ -0,0 +1,223 @@
+<!--  -->
+<template>
+  <div class="tow">
+    <div class="main">
+      <div class="row" v-for="(item, index) in data" :key="item.id">
+        <div class="top">
+          <span
+            @click="data[index].flag = !data[index].flag"
+            :class="`${item.flag ? 'el-icon-arrow-up' : 'el-icon-arrow-down'}`"
+            >&emsp;{{ index + 1 }}</span
+          >
+          <el-input
+            v-model="item.name"
+            placeholder="请输入人物名称"
+            maxlength="8"
+          ></el-input>
+          <i
+            v-if="index !== 0"
+            class="el-icon-close"
+            @click="delMen(index)"
+          ></i>
+        </div>
+        <div class="txt" v-show="item.flag">
+          <span>人物介绍:</span>
+          <el-input
+            type="textarea"
+            v-model="item.txt"
+            maxlength="3000"
+            show-word-limit
+          ></el-input>
+        </div>
+      </div>
+    </div>
+    <!-- 底部按钮 -->
+    <div class="btnn">
+      <!-- 增加人物 -->
+      <div class="el-icon-plus" @click="addMen">增加人物</div>
+
+      <el-button @click="$router.push('/layout/tab1')">返 回</el-button>&emsp;
+      <el-button type="primary" @click="save">保 存</el-button>
+    </div>
+  </div>
+</template>
+
+<script>
+import { saveEntity, contentList } from '@/apis/tab1'
+export default {
+  components: {},
+  data () {
+    return {
+      // 村落ID
+      villageId: null,
+      // 菜单ID
+      menuId: null,
+      // 本身自己的id
+      id: null,
+      data: [{ id: Date.now(), name: '', txt: '', flag: true }]
+    }
+  },
+  computed: {},
+  methods: {
+    // 点击删除
+    delMen (index) {
+      this.data.splice(index, 1)
+    },
+    // 点击增加人物
+    addMen () {
+      this.data.push({ id: Date.now(), name: '', txt: '', flag: true })
+    },
+    // 点击保存
+    async save () {
+      let tempFlag = false
+      this.data.forEach((v) => {
+        v.flag = true
+        if (v.name.trim() === '' || v.txt.trim() === '') tempFlag = true
+      })
+      if (tempFlag) return this.$message.warning('名称或介绍不能为空')
+
+      const obj = {
+        menuId: this.menuId,
+        villageId: this.villageId,
+        contentJson: JSON.stringify(this.data),
+        id: this.id
+      }
+      const res = await saveEntity(obj)
+      if (res.code === 0) {
+        this.$message.success('操作成功')
+        this.id = res.data.id
+      } else this.$message.warning(res.msg)
+    }
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  async created () {
+    this.villageId = Number(this.$route.params.id)
+    this.menuId = Number(this.$route.params.k)
+    const res = await contentList(this.villageId, this.menuId)
+    if (res.data.length > 0) {
+      const temp = res.data[0]
+      this.id = temp.id
+      this.data = JSON.parse(temp.contentJson)
+    }
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {},
+  beforeCreate () {}, // 生命周期 - 创建之前
+  beforeMount () {}, // 生命周期 - 挂载之前
+  beforeUpdate () {}, // 生命周期 - 更新之前
+  updated () {}, // 生命周期 - 更新之后
+  beforeDestroy () {}, // 生命周期 - 销毁之前
+  destroyed () {}, // 生命周期 - 销毁完成
+  activated () {} // 如果页面有keep-alive缓存功能,这个函数会触发
+}
+</script>
+<style lang='less' scoped>
+.tow {
+  width: 100%;
+  height: 100%;
+  position: relative;
+  .main {
+    padding: 30px 20px;
+    height: calc(100% - 120px);
+    overflow-y: auto;
+    .row {
+      margin-bottom: 50px;
+      .top {
+        position: relative;
+        width: 100%;
+        padding: 0 20px;
+        height: 50px;
+        line-height: 50px;
+        background-color: #f2f2f2;
+        .el-icon-close {
+          cursor: pointer;
+          font-weight: 700;
+          font-size: 26px;
+          position: absolute;
+          top: 50%;
+          right: 24px;
+          transform: translateY(-50%);
+        }
+        & > span {
+          cursor: pointer;
+          font-weight: 700;
+          margin-right: 15px;
+        }
+        /deep/.el-input {
+          height: 36px;
+          width: 145px;
+        }
+        /deep/.el-input__inner {
+          color: black;
+          height: 36px;
+          line-height: 36px;
+          background-color: transparent;
+          border: none;
+          border-bottom: 1px solid black;
+          border-radius: 0;
+        }
+        /*修改提示文字的颜色*/
+        /deep/input::-webkit-input-placeholder {
+          /* WebKit browsers */
+          color: #999;
+        }
+        /deep/input:-moz-placeholder {
+          /* Mozilla Firefox 4 to 18 */
+          color: #999;
+        }
+        /deep/input::-moz-placeholder {
+          /* Mozilla Firefox 19+ */
+          color: #999;
+        }
+        /deep/input:-ms-input-placeholder {
+          /* Internet Explorer 10+ */
+          color: #999;
+        }
+      }
+      .txt {
+        position: relative;
+        width: 90%;
+        margin: 20px auto;
+        display: flex;
+        & > span {
+          width: 100px;
+        }
+        &::after {
+          position: absolute;
+          top: 0px;
+          left: -10px;
+          content: "*";
+          color: #f56c6c;
+        }
+      }
+      /deep/.el-textarea__inner {
+        height: 100px;
+      }
+    }
+  }
+  .btnn {
+    border-top: 1px solid #d7d7d7;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    height: 70px;
+    width: 100%;
+    .el-icon-plus {
+      position: absolute;
+      top: -50px;
+      left: 50%;
+      transform: translateX(-50%);
+      font-weight: 700;
+      color: #02aff4;
+      cursor: pointer;
+      width: 100px;
+      text-align: center;
+      line-height: 40px;
+      height: 40px;
+    }
+  }
+}
+</style>

+ 232 - 0
houtai/src/views/tab2/Dialog.vue

@@ -0,0 +1,232 @@
+<template>
+  <el-dialog
+    :title="ruleForm.id ? '编辑用户' : '新增用户'"
+    :visible="dialogVisible"
+    @close="btnX"
+  >
+    <el-form
+      :model="ruleForm"
+      :rules="rules"
+      ref="ruleForm"
+      label-width="100px"
+      class="demo-ruleForm"
+    >
+      <el-form-item label="账号" prop="userName">
+        <el-input
+          @input="inputLimit"
+          :disabled="!!ruleForm.id"
+          v-model="ruleForm.userName"
+          maxlength="15"
+          show-word-limit
+        ></el-input>
+      </el-form-item>
+      <el-form-item label="电话" prop="phone">
+        <el-input
+          v-model="ruleForm.phone"
+          maxlength="11"
+          show-word-limit
+        ></el-input>
+      </el-form-item>
+      <!-- 授权 -->
+      <div class="limits" v-if="ruleForm.id && ruleForm.isAdmin !== 1">
+        <span>权限</span>&emsp;
+        <el-checkbox
+          :indeterminate="isIndeterminate"
+          v-model="checkAll"
+          @change="handleCheckAllChange"
+          >全选</el-checkbox
+        >
+        <el-checkbox-group
+          v-model="checkedCities"
+          @change="handleCheckedCitiesChange"
+        >
+          <el-checkbox
+            v-for="item in villageList"
+            :label="item.id"
+            :key="item.id"
+            >{{ item.name }}</el-checkbox
+          >
+        </el-checkbox-group>
+      </div>
+      <el-form-item label="是否启用:">
+        <el-radio
+          v-model="ruleForm.isEnabled"
+          :label="1"
+          :disabled="ruleForm.isAdmin === 1"
+          >是</el-radio
+        >
+        <el-radio
+          v-model="ruleForm.isEnabled"
+          :label="0"
+          :disabled="ruleForm.isAdmin === 1"
+          >否</el-radio
+        >
+      </el-form-item>
+      <div class="hint">* 默认密码请联系管理员</div>
+    </el-form>
+    <span slot="footer" class="dialog-footer">
+      <el-button @click="btnX">取 消</el-button>
+      <el-button type="primary" @click="btnOk">确 定</el-button>
+    </span>
+  </el-dialog>
+</template>
+
+<script>
+import { userSave, userAuths } from '@/apis/system'
+import { villageList } from '@/apis/tab1'
+
+export default {
+  name: 'tab2Dialog',
+  props: {
+    dialogVisible: {
+      type: Boolean,
+      default: false
+    }
+  },
+  components: {},
+  data () {
+    // 这里存放数据
+    return {
+      // 村落数据
+      villageList: [],
+      ruleForm: {
+        id: null,
+        isEnabled: 1,
+        phone: '',
+        sex: 0,
+        userName: ''
+      },
+      rules: {
+        userName: [{ required: true, message: '不能为空', trigger: 'blur' }],
+        phone: [
+          { required: true, message: '不能为空', trigger: 'blur' },
+          {
+            pattern: /^1[3-9]\d{9}$/,
+            message: '请输入合法手机号',
+            trigger: 'blur'
+          }
+        ]
+      },
+      // 关于授权的数据
+      checkAll: false,
+      checkedCities: [],
+      isIndeterminate: false
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {},
+  // 监控data中的数据变化
+  watch: {},
+  // 方法集合
+  methods: {
+    inputLimit (val) {
+      // 正则匹配非中英文及数字的字符
+      const reg = /[^\u4e00-\u9fa5a-zA-Z0-9]/
+      // 查找是否有非中英文及数字的字符
+      // 没有则返回-1,有则返回对应位置
+      const n = val.search(reg)
+      if (n !== -1) {
+        // 由于每次触发,通常只有最后一个字符是有可能异常的
+        // 所以这里是直接截取异常字符前面的部分
+        this.ruleForm.userName = val.slice(0, n)
+      }
+    },
+
+    // 关于授权的数据
+    handleCheckAllChange (val) {
+      this.checkedCities = val ? this.villageList.map((v) => v.id) : []
+      this.isIndeterminate = false
+    },
+    handleCheckedCitiesChange (value) {
+      const checkedCount = value.length
+      this.checkAll = checkedCount === this.villageList.length
+      this.isIndeterminate =
+        checkedCount > 0 && checkedCount < this.villageList.length
+    },
+    // 父组件点击编辑的时候调用
+    async setRuleForm (item) {
+      this.ruleForm = { ...item }
+      if (item.projectIds !== '') {
+        this.checkedCities = item.projectIds.split(',')
+        if (this.checkedCities.length >= 12) {
+          this.checkAll = true
+          this.isIndeterminate = false
+        } else this.isIndeterminate = true
+        this.checkedCities = this.checkedCities.map((v) => Number(v))
+      } else {
+        this.checkedCities = []
+        this.checkAll = this.isIndeterminate = false
+      }
+    },
+    // 点击取消
+    btnX () {
+      this.$refs.ruleForm.resetFields()
+      this.ruleForm = {
+        id: null,
+        isEnabled: 1,
+        phone: '',
+        sex: 0,
+        userName: ''
+      }
+      this.$emit('update:dialogVisible', false)
+    },
+    async btnOk () {
+      try {
+        await this.$refs.ruleForm.validate()
+        const res = await userSave(this.ruleForm)
+        if (res.code === 0) {
+          if (this.ruleForm.id) {
+            const res2 = await userAuths(
+              this.ruleForm.id,
+              this.checkedCities.join(',')
+            )
+            if (res2.code === 0) {
+              this.$emit('clickSon')
+              this.btnX()
+              this.$message.success('操作成功')
+            } else this.$message.warning(res2.msg)
+          } else {
+            this.$emit('clickSon')
+            this.btnX()
+            this.$message.success('操作成功')
+          }
+        } else this.$message.warning(res.msg)
+      } catch (error) {
+        console.log(error)
+      }
+    }
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  async created () {
+    const res = await villageList()
+    this.villageList = res.data
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {},
+  beforeCreate () {}, // 生命周期 - 创建之前
+  beforeMount () {}, // 生命周期 - 挂载之前
+  beforeUpdate () {}, // 生命周期 - 更新之前
+  updated () {}, // 生命周期 - 更新之后
+  beforeDestroy () {}, // 生命周期 - 销毁之前
+  destroyed () {}, // 生命周期 - 销毁完成
+  activated () {} // 如果页面有keep-alive缓存功能,这个函数会触发
+}
+</script>
+<style lang='less' scoped>
+.limits {
+  padding-left: 60px;
+  margin-bottom: 15px;
+  /deep/.el-checkbox-group .el-checkbox {
+    width: 80px;
+    margin-top: 10px;
+  }
+  /deep/.el-checkbox-group {
+    padding-left: 46px;
+  }
+}
+.hint {
+  color: red;
+  margin-left: 100px;
+  margin-top: -15px;
+}
+</style>

+ 243 - 0
houtai/src/views/tab2/index.vue

@@ -0,0 +1,243 @@
+<template>
+  <div class="tab2">
+    <div class="insideTop">
+      用户管理
+      <div class="addBtn">
+        <el-button type="primary" @click="dialogVisible = true">新增</el-button>
+      </div>
+    </div>
+    <div class="obstruct"></div>
+    <div class="conten">
+      <div class="top">
+        <el-input
+          style="width: 300px"
+          v-model="pageData.searchKey"
+          placeholder="请输入用户名"
+        ></el-input>
+        &emsp;&emsp;
+        <el-button type="primary" @click="userList(pageData)">查 询</el-button>
+        <el-button @click="reset">重 置</el-button>
+      </div>
+      <div class="table">
+        <el-table :data="tableData" style="width: 100%" height="550px">
+          <el-table-column label="编号" width="80">
+            <template slot-scope="scope">
+              {{
+                scope.$index + (pageData.pageNum - 1) * pageData.pageSize + 1
+              }}
+            </template>
+          </el-table-column>
+          <el-table-column prop="userName" label="账号"> </el-table-column>
+          <el-table-column prop="phone" label="联系电话"> </el-table-column>
+          <el-table-column prop="createTime" label="新建时间">
+          </el-table-column>
+
+          <el-table-column label="启用状态">
+            <template #default="{ row }">
+              <el-switch
+                :disabled="row.isAdmin === 1"
+                @change="changeSta($event, row.id)"
+                v-model="row.isEnabled"
+                active-color="#a30014"
+                inactive-color="#ccc"
+                :active-value="1"
+                :inactive-value="0"
+              >
+              </el-switch>
+              <!-- {{ row.isEnabled ? "启用" : "禁用" }} -->
+            </template>
+          </el-table-column>
+          <el-table-column label="操作">
+            <template #default="{ row }">
+              <el-button type="text" @click="editUser(row)">{{
+                row.isAdmin === 1 ? "编辑" : "编辑 / 授权"
+              }}</el-button>
+              <el-button
+                v-if="row.isAdmin !== 1"
+                type="text"
+                @click="resetPass(row.id)"
+                >重置密码</el-button
+              >
+              <el-button
+                v-if="row.isAdmin !== 1"
+                type="text"
+                style="color: #f56c6c"
+                @click="userDel(row.id)"
+                >删除</el-button
+              >
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+      <!-- 分页器 -->
+      <div class="paging">
+        <span>共 {{ total }} 条</span>
+        <el-pagination
+          :current-page="pageData.pageNum"
+          @current-change="currentChange"
+          @size-change="sizeChange"
+          layout="sizes,prev,pager,next,jumper"
+          :total="total"
+        >
+        </el-pagination>
+      </div>
+    </div>
+    <!-- 点击新增和编辑出现的弹窗 -->
+    <Dialog
+      ref="projectRef"
+      :dialogVisible.sync="dialogVisible"
+      @clickSon="userList(pageData)"
+    />
+  </div>
+</template>
+
+<script>
+import Dialog from './Dialog'
+import { userList, userDel, editStatus, userResetPass } from '@/apis/system'
+export default {
+  name: 'tab2',
+  components: { Dialog },
+  data () {
+    // 这里存放数据
+    return {
+      dialogVisible: false,
+      total: 0,
+      pageData: {
+        pageNum: 1,
+        pageSize: 10,
+        searchKey: ''
+      },
+      tableData: []
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {},
+  // 监控data中的数据变化
+  watch: {},
+  // 方法集合
+  methods: {
+    // 点击编辑,调用子组件弹窗的方法
+    editUser (item) {
+      this.$refs.projectRef.setRuleForm(item)
+      this.dialogVisible = true
+    },
+    // 点击重置密码
+    async resetPass (id) {
+      this.$confirm('确定重置吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      })
+        .then(async () => {
+          const res = await userResetPass(id)
+          if (res.code === 0) {
+            this.$message.success('重置成功!')
+          } else this.$message.warning(res.msg)
+        })
+        .catch(() => {
+          this.$message.info('已取消重置')
+        })
+    },
+    // 启用和停用状态
+    async changeSta (val, id) {
+      await editStatus(id, val)
+      this.$message.success('操作成功!')
+    },
+    // 点击删除
+    userDel (id) {
+      this.$confirm('确定删除吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      })
+        .then(async () => {
+          const res = await userDel(id)
+          if (res.code === 0) {
+            this.$message.success('删除成功!')
+            this.userList(this.pageData)
+          } else this.$message.warning(res.msg)
+        })
+        .catch(() => {
+          this.$message.info('已取消删除')
+        })
+    },
+    // 点击重置
+    reset () {
+      this.pageData.pageNum = 1
+      this.pageData.searchKey = ''
+      this.userList(this.pageData)
+    },
+    // 封装一个获取表列表的方法
+    async userList (data) {
+      const res = await userList(data)
+      this.total = res.data.total
+      this.tableData = res.data.records
+    },
+    // 分页器
+    currentChange (val) {
+      this.pageData.pageNum = val
+      this.userList(this.pageData)
+    },
+    sizeChange (val) {
+      this.pageData.pageNum = 1
+      this.pageData.pageSize = val
+      this.userList(this.pageData)
+    }
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created () {
+    this.userList(this.pageData)
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {
+    // 不是管理员进入回调到登录页
+    const userInfo = localStorage.getItem('JMYZU_userInfo')
+    if (userInfo) {
+      const isAdmin = JSON.parse(userInfo).isAdmin
+      if (isAdmin !== 1) {
+        alert('没有该页面权限,请联系管理员!')
+        localStorage.clear('JMYZU_token')
+        localStorage.clear('JMYZU_userInfo')
+        this.$router.push('/')
+      }
+    }
+  },
+  beforeCreate () {}, // 生命周期 - 创建之前
+  beforeMount () {}, // 生命周期 - 挂载之前
+  beforeUpdate () {}, // 生命周期 - 更新之前
+  updated () {}, // 生命周期 - 更新之后
+  beforeDestroy () {}, // 生命周期 - 销毁之前
+  destroyed () {}, // 生命周期 - 销毁完成
+  activated () {} // 如果页面有keep-alive缓存功能,这个函数会触发
+}
+</script>
+<style lang='less' scoped>
+.tab2 {
+  width: 100%;
+  height: 100%;
+  .insideTop {
+    position: relative;
+    .addBtn {
+      position: absolute;
+      top: -10px;
+      right: 20px;
+    }
+  }
+  .conten {
+    width: 100%;
+    height: calc(100% - 52px);
+    position: relative;
+    padding: 20px;
+    .table {
+      margin-top: 20px;
+    }
+    .paging {
+      display: flex;
+      align-items: center;
+      position: absolute;
+      bottom: 30px;
+      right: 20px;
+    }
+  }
+}
+</style>

+ 335 - 0
houtai/src/views/tab3/index.vue

@@ -0,0 +1,335 @@
+<template>
+  <div class="tab3">
+    <div class="insideTop">
+      数据统计
+      <div class="addBtn">
+        <el-button type="primary" v-show="0">导出</el-button>
+      </div>
+    </div>
+    <div class="obstruct"></div>
+    <div class="conten">
+      <!-- 上面盒子 -->
+      <div class="top">
+        <div class="topLft">
+          <div class="row" v-for="(item, index) in data1" :key="index">
+            <div class="inco">
+              <span class="tit">{{ item.name }}</span>
+              <p>{{ item.num }}</p>
+            </div>
+            <div class="img">
+              <img :src="require(`@/assets/img/${item.img}`)" alt="" />
+            </div>
+          </div>
+        </div>
+        <div class="topRight">
+          <span class="tit">各村落访问量</span>
+          <div id="echaBox"></div>
+        </div>
+      </div>
+      <!-- 下面盒子 -->
+      <div class="bottom">
+        <span class="tit">VR景点打卡排行</span>
+        <div class="botRowBox">
+          <div class="botRow" v-for="(item, index) in dakaData" :key="item.id">
+            <div class="botLeft">
+              <h3>NO.{{ index + 1 }}</h3>
+              <p>打卡数:{{ item.visit }}</p>
+              <p>{{ item.name }}</p>
+            </div>
+            <div class="botRight">
+              <img
+                :src="`/SWKK/local/4dkk/${item.sceneCode}/wwwroot/images/images${item.sceneCode}/thumbSmallImg.jpg?0.3393463684591038`"
+                alt=""
+              />
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { getStatistics, sceneGetList } from '@/apis/system'
+import * as echarts from 'echarts/core'
+import {
+  TitleComponent,
+  TooltipComponent,
+  GridComponent,
+  LegendComponent
+} from 'echarts/components'
+import { BarChart } from 'echarts/charts'
+import { CanvasRenderer } from 'echarts/renderers'
+echarts.use([
+  TitleComponent,
+  TooltipComponent,
+  GridComponent,
+  LegendComponent,
+  BarChart,
+  CanvasRenderer
+])
+export default {
+  name: 'tab3',
+  components: {},
+  data () {
+    // 这里存放数据
+    return {
+      data1: [
+        { name: '累计访问量', num: '0', img: '1.png' },
+        { name: '今日访问量', num: '0', img: '2.png' },
+        { name: '累计点赞数', num: '0', img: '3.png' },
+        { name: '页面分享数', num: '0', img: '4.png' }
+      ],
+      dakaData: []
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {},
+  // 监控data中的数据变化
+  watch: {},
+  // 方法集合
+  methods: {},
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  async created () {
+    // 获取列表
+    const res = await sceneGetList()
+    this.dakaData = res.data
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  async mounted () {
+    // 不是管理员进入回调到登录页
+    const userInfo = localStorage.getItem('JMYZU_userInfo')
+    if (userInfo) {
+      const isAdmin = JSON.parse(userInfo).isAdmin
+      if (isAdmin !== 1) {
+        alert('没有该页面权限,请联系管理员!')
+        localStorage.clear('JMYZU_token')
+        localStorage.clear('JMYZU_userInfo')
+        this.$router.push('/')
+      }
+    }
+
+    const res = await getStatistics()
+    this.data1[0].num = res.data.visitCount
+    this.data1[1].num = res.data.visitToday
+    this.data1[2].num = res.data.starCount
+    this.data1[3].num = res.data.shareCount
+    const objTemp = {
+      name: [],
+      num: []
+    }
+    res.data.village.forEach((v) => {
+      objTemp.name.push(v.name)
+      objTemp.num.push(v.visit)
+    })
+    const chartDom = document.getElementById('echaBox')
+    const myChart = echarts.init(chartDom)
+    const option = {
+      color: ['#02a7f0'],
+      tooltip: {
+        trigger: 'axis',
+        axisPointer: {
+          type: 'shadow'
+        }
+      },
+      grid: {
+        left: '3%',
+        right: '4%',
+        bottom: '3%',
+        containLabel: true
+      },
+      xAxis: {
+        type: 'value',
+        boundaryGap: [0, 0.01],
+        show: false
+      },
+      yAxis: {
+        inverse: true,
+        type: 'category',
+        data: objTemp.name,
+        axisTick: {
+          // y轴刻度线
+          show: false
+        },
+        axisLine: {
+          // y轴
+          show: false
+        }
+      },
+      series: [
+        {
+          type: 'bar',
+          data: objTemp.num,
+          itemStyle: {
+            normal: {
+              label: {
+                formatter: '{c}',
+                show: true,
+                position: 'right',
+                textStyle: {
+                  fontWeight: 'bolder',
+                  fontSize: '16',
+                  color: '#02a7f0'
+                }
+              }
+            }
+          }
+        }
+      ]
+    }
+
+    option && myChart.setOption(option)
+  },
+  beforeCreate () {}, // 生命周期 - 创建之前
+  beforeMount () {}, // 生命周期 - 挂载之前
+  beforeUpdate () {}, // 生命周期 - 更新之前
+  updated () {}, // 生命周期 - 更新之后
+  beforeDestroy () {}, // 生命周期 - 销毁之前
+  destroyed () {}, // 生命周期 - 销毁完成
+  activated () {} // 如果页面有keep-alive缓存功能,这个函数会触发
+}
+</script>
+<style lang='less' scoped>
+.botRowBox::-webkit-scrollbar {
+  /*滚动条整体样式*/
+  width: 10px; /*高宽分别对应横竖滚动条的尺寸*/
+  height: 1px;
+}
+.botRowBox::-webkit-scrollbar-thumb {
+  /*滚动条里面小方块*/
+  border-radius: 10px;
+  -webkit-box-shadow: inset 0 0 5px transparent;
+  background: #aaaaaa;
+}
+.botRowBox::-webkit-scrollbar-track {
+  /*滚动条里面轨道*/
+  -webkit-box-shadow: inset 0 0 5px transparent;
+  border-radius: 10px;
+  background: transparent;
+}
+.tab3 {
+  width: 100%;
+  height: 100%;
+  .insideTop {
+    position: relative;
+    .addBtn {
+      position: absolute;
+      top: -10px;
+      right: 20px;
+    }
+  }
+  .conten {
+    width: 100%;
+    height: calc(100% - 52px);
+    background-color: #f2ecde;
+    .tit {
+      font-weight: 700;
+      font-size: 18px;
+      color: #7f7f7f;
+    }
+    .top {
+      display: flex;
+      width: 100%;
+      height: 428px;
+      .topLft {
+        width: 50%;
+        height: 100%;
+        padding-right: 20px;
+        display: flex;
+        flex-wrap: wrap;
+        justify-content: space-between;
+        align-content: space-between;
+        .row {
+          width: 49%;
+          height: 48%;
+          background-color: #fff;
+          display: flex;
+          .inco {
+            padding: 0px 10px;
+            display: flex;
+            flex-direction: column;
+            justify-content: center;
+            align-items: center;
+            width: calc(100% - 120px);
+            height: 100%;
+            & > p {
+              margin-top: 15px;
+              font-size: 22px;
+              font-weight: 700;
+            }
+          }
+          .img {
+            width: 120px;
+            height: 100%;
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            & > img {
+              width: 70px;
+              height: 70px;
+            }
+          }
+        }
+      }
+      .topRight {
+        width: 50%;
+        height: 100%;
+        background-color: #fff;
+        position: relative;
+        padding: 12px 0 0 20px;
+        #echaBox {
+          position: absolute;
+          top: 0;
+          left: 0;
+          width: 100%;
+          height: 100%;
+        }
+      }
+    }
+    .bottom {
+      background-color: #fff;
+      padding: 20px 0 0 20px;
+      margin-top: 12px;
+      width: 100%;
+      height: calc(100% - 440px);
+      .botRowBox {
+        width: 100%;
+        height: calc(100% - 60px);
+        overflow-y: auto;
+        display: flex;
+        flex-wrap: wrap;
+        justify-content: space-between;
+        padding-right: 20px;
+        margin-top: 20px;
+        .botRow {
+          margin-bottom: 20px;
+          width: 30%;
+          height: 200px;
+          display: flex;
+          .botLeft {
+            flex: 1;
+            padding: 15px 10px 0 0;
+            & > h3 {
+              font-size: 24px;
+              text-align: center;
+            }
+            & > p {
+              text-align: center;
+              margin-top: 30px;
+            }
+          }
+          .botRight {
+            width: 300px;
+            height: 200px;
+            & > img {
+              width: 100%;
+              height: 100%;
+              object-fit: cover;
+            }
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 169 - 0
houtai/src/views/tab4/index.vue

@@ -0,0 +1,169 @@
+<template>
+  <div class="tab4">
+    <div class="insideTop">操作日志</div>
+    <div class="obstruct"></div>
+    <!-- 主要内容 -->
+    <div class="conten">
+      <div class="search">
+        <el-input
+          v-model="formData.searchKey"
+          placeholder="请输入关键字"
+          style="width: 240px"
+        ></el-input>
+        <!-- 右侧按钮 -->
+        <div class="search_btn">
+          <el-button type="primary" @click="searchBtn">查 询</el-button>
+        </div>
+      </div>
+      <!-- 表格 -->
+      <div class="table">
+        <el-table :data="tableData" style="width: 100%" height="529px">
+          <el-table-column label="序号" width="80">
+            <template slot-scope="scope">
+              {{
+                scope.$index + (formData.pageNum - 1) * formData.pageSize + 1
+              }}
+            </template>
+          </el-table-column>
+          <el-table-column prop="userName" label="账号"> </el-table-column>
+          <el-table-column prop="type" label="操作模块"> </el-table-column>
+          <el-table-column
+            prop="description"
+            label="操作事件"
+          ></el-table-column>
+          <el-table-column prop="createTime" label="操作时间"></el-table-column>
+        </el-table>
+      </div>
+    </div>
+    <!-- 分页 -->
+    <div class="paging">
+      <span>共 {{ total }} 条</span>
+      <el-pagination
+        layout="sizes,prev, pager, next,jumper"
+        :total="total"
+        :current-page="formData.pageNum"
+        @current-change="currentChange"
+        @size-change="sizeChange"
+      >
+      </el-pagination>
+    </div>
+  </div>
+</template>
+
+<script>
+import { logList } from '@/apis/system'
+export default {
+  name: 'tab4',
+  components: {},
+  data () {
+    // 这里存放数据
+    return {
+      total: 0,
+      formData: {
+        pageNum: 1,
+        pageSize: 10,
+        searchKey: ''
+      },
+      tableData: []
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {},
+  // 监控data中的数据变化
+  watch: {},
+  // 方法集合
+  methods: {
+    // 点击查询
+    searchBtn () {
+      this.formData.pageNum = 1
+      this.logList(this.formData)
+    },
+    // 封装获取列表的函数
+    async logList (data) {
+      const res = await logList(data)
+      this.total = res.data.total
+      this.tableData = res.data.records
+      // console.log(998, res)
+    },
+    // 分页器方法
+    currentChange (val) {
+      // console.log('当前页改变了', val)
+      this.formData.pageNum = val
+      this.logList(this.formData)
+    },
+    sizeChange (val) {
+      // console.log('条数改变了', val)
+      this.formData.pageNum = 1
+      this.formData.pageSize = val
+      this.logList(this.formData)
+    }
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created () {
+    this.logList(this.formData)
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {
+    // 不是管理员进入回调到登录页
+    const userInfo = localStorage.getItem('JMYZU_userInfo')
+    if (userInfo) {
+      const isAdmin = JSON.parse(userInfo).isAdmin
+      if (isAdmin !== 1) {
+        alert('没有该页面权限,请联系管理员!')
+        localStorage.clear('JMYZU_token')
+        localStorage.clear('JMYZU_userInfo')
+        this.$router.push('/')
+      }
+    }
+  },
+  beforeCreate () {}, // 生命周期 - 创建之前
+  beforeMount () {}, // 生命周期 - 挂载之前
+  beforeUpdate () {}, // 生命周期 - 更新之前
+  updated () {}, // 生命周期 - 更新之后
+  beforeDestroy () {}, // 生命周期 - 销毁之前
+  destroyed () {}, // 生命周期 - 销毁完成
+  activated () {} // 如果页面有keep-alive缓存功能,这个函数会触发
+}
+</script>
+<style lang='less' scoped>
+.tab4 {
+  height: 100%;
+  .insideTop .add {
+    right: 55px;
+  }
+  .conten {
+    position: relative;
+    padding: 15px 30px 0;
+    height: calc(100% - 52px);
+    .classify {
+      text-align: center;
+      width: 40px;
+      padding-bottom: 10px;
+      color: #b9412e;
+      border-bottom: 2px solid #b9412e;
+    }
+    .search {
+      display: flex;
+      margin-bottom: 20px;
+      position: relative;
+      height: 40px;
+      .search_k {
+        margin-left: 30px;
+      }
+      .search_btn {
+        margin-left: 30px;
+        display: flex;
+        justify-content: space-between;
+        width: 100px;
+      }
+    }
+  }
+  .paging {
+    display: flex;
+    align-items: center;
+    position: absolute;
+    bottom: 30px;
+    right: 20px;
+  }
+}
+</style>

+ 3 - 0
houtai/vue.config.js

@@ -0,0 +1,3 @@
+module.exports = {
+  publicPath: './'
+}

+ 23 - 0
传统村落pc/.gitignore

@@ -0,0 +1,23 @@
+.DS_Store
+node_modules
+/dist
+
+
+# local env files
+.env.local
+.env.*.local
+
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?

+ 19 - 0
传统村落pc/README.md

@@ -0,0 +1,19 @@
+# web
+
+## Project setup
+```
+npm install
+```
+
+### Compiles and hot-reloads for development
+```
+npm run serve
+```
+
+### Compiles and minifies for production
+```
+npm run build
+```
+
+### Customize configuration
+See [Configuration Reference](https://cli.vuejs.org/config/).

+ 5 - 0
传统村落pc/babel.config.js

@@ -0,0 +1,5 @@
+module.exports = {
+  presets: [
+    '@vue/cli-plugin-babel/preset'
+  ]
+}

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 29381 - 0
传统村落pc/package-lock.json


+ 33 - 0
传统村落pc/package.json

@@ -0,0 +1,33 @@
+{
+  "name": "web",
+  "version": "0.1.0",
+  "private": true,
+  "scripts": {
+    "serve": "vue-cli-service serve",
+    "build": "vue-cli-service build"
+  },
+  "dependencies": {
+    "axios": "^0.27.2",
+    "core-js": "^3.6.5",
+    "element-ui": "^2.15.8",
+    "mars3d": "^3.4.1",
+    "swiper": "^4.5.1",
+    "v-viewer": "^1.5.1",
+    "vue": "^2.6.11",
+    "vue-lazyload": "^1.3.3",
+    "vue-router": "^3.2.0"
+  },
+  "devDependencies": {
+    "@vue/cli-plugin-babel": "~4.5.13",
+    "@vue/cli-plugin-router": "~4.5.13",
+    "@vue/cli-service": "~4.5.13",
+    "less": "^3.0.4",
+    "less-loader": "^5.0.0",
+    "vue-template-compiler": "^2.6.11"
+  },
+  "browserslist": [
+    "> 1%",
+    "last 2 versions",
+    "not dead"
+  ]
+}

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1421 - 0
传统村落pc/public/config/config.json


BIN
传统村落pc/public/data/1.png


BIN
传统村落pc/public/data/10.png


BIN
传统村落pc/public/data/11.png


BIN
传统村落pc/public/data/12.png


+ 178 - 0
传统村落pc/public/data/12village.json

@@ -0,0 +1,178 @@
+{
+  "type": "FeatureCollection",
+  "name": "12village",
+  "crs": {
+    "type": "name",
+    "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" }
+  },
+  "features": [
+    {
+      "type": "Feature",
+      "properties": {
+        "id": 3,
+        "name": "卢边村",
+        "belong": "蓬江区潮连街道办",
+        "logo": "江门蓬江",
+        "lng": 113.11731577,
+        "lat": 22.63147116
+      },
+      "geometry": {
+        "type": "Point",
+        "coordinates": [113.11731577, 22.63147116]
+      }
+    },
+    {
+      "type": "Feature",
+      "properties": {
+        "id": 5,
+        "name": "仓前村",
+        "belong": "开平市塘口镇",
+        "logo": "江门开平",
+        "lng": 112.56293754,
+        "lat": 22.35548258
+      },
+      "geometry": {
+        "type": "Point",
+        "coordinates": [112.56293754, 22.35548258]
+      }
+    },
+    {
+      "type": "Feature",
+      "properties": {
+        "id": 1,
+        "name": "东宁村",
+        "belong": "台山市端芬镇",
+        "logo": "江门台山",
+        "lng": 112.77709365,
+        "lat": 22.03879416
+      },
+      "geometry": {
+        "type": "Point",
+        "coordinates": [112.77709365, 22.03879416]
+      }
+    },
+    {
+      "type": "Feature",
+      "properties": {
+        "id": 10,
+        "name": "浮石村",
+        "belong": "台山市斗山镇",
+        "logo": "江门台山",
+        "lng": 112.85166979,
+        "lat": 22.05615878
+      },
+      "geometry": {
+        "type": "Point",
+        "coordinates": [112.85166979, 22.05615878]
+      }
+    },
+    {
+      "type": "Feature",
+      "properties": {
+        "id": 11,
+        "name": "浮月村",
+        "belong": "台山市斗山镇",
+        "logo": "江门台山",
+        "lng": 112.8051281,
+        "lat": 22.03516245
+      },
+      "geometry": { "type": "Point", "coordinates": [112.8051281, 22.03516245] }
+    },
+    {
+      "type": "Feature",
+      "properties": {
+        "id": 12,
+        "name": "横江村",
+        "belong": "台山市斗山镇",
+        "logo": "江门台山",
+        "lng": 112.84664869,
+        "lat": 22.0355916
+      },
+      "geometry": { "type": "Point", "coordinates": [112.84664869, 22.0355916] }
+    },
+    {
+      "type": "Feature",
+      "properties": {
+        "id": 2,
+        "name": "良溪村",
+        "belong": "蓬江区棠下镇",
+        "logo": "江门蓬江",
+        "lng": 113.02345991,
+        "lat": 22.7054894
+      },
+      "geometry": { "type": "Point", "coordinates": [113.02345991, 22.7054894] }
+    },
+    {
+      "type": "Feature",
+      "properties": {
+        "id": 9,
+        "name": "马降龙村",
+        "belong": "开平市百合镇",
+        "logo": "江门开平",
+        "lng": 112.56387949000001,
+        "lat": 22.28532672
+      },
+      "geometry": {
+        "type": "Point",
+        "coordinates": [112.56387949, 22.28532672]
+      }
+    },
+    {
+      "type": "Feature",
+      "properties": {
+        "id": 4,
+        "name": "田心村",
+        "belong": "鹤山市鹤城镇",
+        "logo": "江门鹤山",
+        "lng": 112.80080438,
+        "lat": 22.59469271
+      },
+      "geometry": {
+        "type": "Point",
+        "coordinates": [112.80080438, 22.59469271]
+      }
+    },
+    {
+      "type": "Feature",
+      "properties": {
+        "id": 6,
+        "name": "霄南村",
+        "belong": "鹤山市龙口镇",
+        "logo": "江门鹤山",
+        "lng": 112.91765213,
+        "lat": 22.77494788
+      },
+      "geometry": {
+        "type": "Point",
+        "coordinates": [112.91765213, 22.77494788]
+      }
+    },
+    {
+      "type": "Feature",
+      "properties": {
+        "id": 7,
+        "name": "歇马村",
+        "belong": "恩平市圣堂镇",
+        "logo": "江门恩平",
+        "lng": 112.37006307,
+        "lat": 22.24249721
+      },
+      "geometry": {
+        "type": "Point",
+        "coordinates": [112.37006307, 22.24249721]
+      }
+    },
+    {
+      "type": "Feature",
+      "properties": {
+        "id": 8,
+        "name": "自力村",
+        "belong": "开平市塘口镇",
+        "logo": "江门开平",
+        "lng": 112.5747478,
+        "lat": 22.37665057
+      },
+      "geometry": { "type": "Point", "coordinates": [112.5747478, 22.37665057] }
+    }
+  ]
+}

BIN
传统村落pc/public/data/2.png


BIN
传统村落pc/public/data/3.png


BIN
传统村落pc/public/data/4.png


BIN
传统村落pc/public/data/5.png


BIN
传统村落pc/public/data/6.png


BIN
传统村落pc/public/data/7.png


BIN
传统村落pc/public/data/8.png


BIN
传统村落pc/public/data/9.png


BIN
传统村落pc/public/data/conBsAc.png


BIN
传统村落pc/public/data/fence.png


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 8 - 0
传统村落pc/public/data/jmc.json


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 14 - 0
传统村落pc/public/data/jmq.json


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 104 - 0
传统村落pc/public/data/jmz.json


BIN
传统村落pc/public/data/lable.png


BIN
传统村落pc/public/data/lableA.png


BIN
传统村落pc/public/data/lableIcon.png


BIN
传统村落pc/public/data/lableIconA.png


BIN
传统村落pc/public/data/layer.png


BIN
传统村落pc/public/favicon.ico


+ 52 - 0
传统村落pc/public/index.html

@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html lang="">
+  <head>
+    <meta charset="utf-8" />
+    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+    <meta name="viewport" content="width=device-width,initial-scale=1.0" />
+    <link rel="icon" href="<%= BASE_URL %>favicon.ico" />
+
+    <!-- 方式2:使用cdn引入的Cesium -->
+    <!-- <link href="https://unpkg.com/mars3d-cesium/Build/Cesium/Widgets/widgets.css" rel="stylesheet" type="text/css" />
+  <script src="https://unpkg.com/mars3d-cesium/Build/Cesium/Cesium.js" type="text/javascript"></script>
+  <script src="https://unpkg.com/@turf/turf/turf.min.js" type="text/javascript"></script> -->
+
+    <script type="text/javascript">
+      window.CESIUM_BASE_URL = "<%= BASE_URL %>lib/Cesium/";
+    </script>
+    <link
+      rel="stylesheet"
+      href="<%= BASE_URL %>lib/Cesium/Widgets/widgets.css"
+    />
+    <script
+      type="text/javascript"
+      src="<%= BASE_URL %>lib/Cesium/Cesium.js"
+    ></script>
+    <script src="./lib/turf/turf.min.js" type="text/javascript"></script>
+    <title>江门市传统村落一张图</title>
+    <style>
+      .mars3d-locationbar {
+        background-color: #000 !important;
+        z-index: 0 !important;
+      }
+      .mars3d-divlayer>div:nth-of-type(5){
+        z-index: 2 !important;
+      }
+      .mars3d-divlayer>div:nth-of-type(12){
+        z-index: 2 !important;
+      }
+    </style>
+  </head>
+
+  <body>
+    <noscript>
+      <strong
+        >We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work
+        properly without JavaScript enabled. Please enable it to
+        continue.</strong
+      >
+    </noscript>
+    <div id="app"></div>
+    <!-- built files will be auto injected -->
+  </body>
+</html>

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_0.json


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_1.json


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_10.json


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_11.json


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_12.json


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_13.json


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_14.json


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_15.json


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_16.json


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_17.json


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_18.json


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_19.json


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_2.json


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_20.json


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_21.json


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_22.json


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_23.json


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_24.json


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_25.json


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_26.json


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_27.json


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_3.json


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_4.json


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_5.json


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_6.json


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_7.json


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_8.json


+ 0 - 0
传统村落pc/public/lib/Cesium/Assets/IAU2006_XYS/IAU2006_XYS_9.json


Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott