tremble 4 年 前
コミット
5a85d03468
100 ファイル変更24048 行追加0 行削除
  1. 0 0
      backstage/.browserslistrc
  2. 0 0
      backstage/.eslintrc.js
  3. 21 0
      backstage/.gitignore
  4. 24 0
      backstage/README.md
  5. 0 0
      backstage/babel.config.js
  6. 11843 0
      backstage/package-lock.json
  7. 31 0
      backstage/package.json
  8. 0 0
      backstage/public/favicon.ico
  9. 19 0
      backstage/public/index.html
  10. 31 0
      backstage/src/App.vue
  11. 0 0
      backstage/src/assets/css/color.less
  12. 4 0
      backstage/src/assets/css/globalVars.less
  13. 303 0
      backstage/src/assets/css/public.less
  14. 112 0
      backstage/src/assets/css/reset.less
  15. 539 0
      backstage/src/assets/font/demo.css
  16. 607 0
      backstage/src/assets/font/demo_index.html
  17. BIN
      backstage/src/assets/font/download.zip
  18. 93 0
      backstage/src/assets/font/iconfont.css
  19. BIN
      backstage/src/assets/font/iconfont.eot
  20. 1 0
      backstage/src/assets/font/iconfont.js
  21. 142 0
      backstage/src/assets/font/iconfont.json
  22. 83 0
      backstage/src/assets/font/iconfont.svg
  23. BIN
      backstage/src/assets/font/iconfont.ttf
  24. BIN
      backstage/src/assets/font/iconfont.woff
  25. BIN
      backstage/src/assets/font/iconfont.woff2
  26. BIN
      backstage/src/assets/img/01jk.png
  27. BIN
      backstage/src/assets/img/01jks.png
  28. BIN
      backstage/src/assets/img/02fb.png
  29. BIN
      backstage/src/assets/img/03mg.png
  30. BIN
      backstage/src/assets/img/04fb.png
  31. BIN
      backstage/src/assets/img/4dage-logo.png
  32. BIN
      backstage/src/assets/img/bg.jpg
  33. BIN
      backstage/src/assets/img/biyan.png
  34. BIN
      backstage/src/assets/img/delete.png
  35. BIN
      backstage/src/assets/img/edit.png
  36. BIN
      backstage/src/assets/img/logo.png
  37. BIN
      backstage/src/assets/img/logout.png
  38. BIN
      backstage/src/assets/img/noPicture.png
  39. BIN
      backstage/src/assets/img/user.png
  40. BIN
      backstage/src/assets/img/wenli.png
  41. BIN
      backstage/src/assets/img/zhoushan-logo.jpg
  42. 71 0
      backstage/src/components/Editor.vue
  43. 266 0
      backstage/src/components/Tinymce/index.vue
  44. 5 0
      backstage/src/components/Tinymce/plugins.js
  45. 3 0
      backstage/src/components/Tinymce/toolbar.js
  46. 58 0
      backstage/src/components/crumbs/index.vue
  47. 79 0
      backstage/src/components/main-top/index.vue
  48. 60 0
      backstage/src/configue/base.js
  49. 3 0
      backstage/src/configue/bus.js
  50. 137 0
      backstage/src/configue/http.js
  51. 130 0
      backstage/src/main.js
  52. 679 0
      backstage/src/pages/content/Carousel.vue
  53. 569 0
      backstage/src/pages/content/Collection.vue
  54. 424 0
      backstage/src/pages/content/Comment.vue
  55. 704 0
      backstage/src/pages/content/Dynamic.vue
  56. 853 0
      backstage/src/pages/content/Spirit.vue
  57. 374 0
      backstage/src/pages/edit/carousel.vue
  58. 710 0
      backstage/src/pages/edit/collection.vue
  59. 447 0
      backstage/src/pages/edit/dynamic.vue
  60. 422 0
      backstage/src/pages/edit/spirit.vue
  61. 36 0
      backstage/src/pages/home/index.vue
  62. 135 0
      backstage/src/pages/layout/aside.vue
  63. 47 0
      backstage/src/pages/layout/footer.vue
  64. 146 0
      backstage/src/pages/layout/head.vue
  65. 56 0
      backstage/src/pages/layout/index.vue
  66. 224 0
      backstage/src/pages/login/index.vue
  67. 3 0
      backstage/src/pages/login/style.css
  68. 363 0
      backstage/src/pages/show/parts.vue
  69. 245 0
      backstage/src/pages/show/roam.vue
  70. 236 0
      backstage/src/pages/show/structure.vue
  71. 485 0
      backstage/src/pages/system/Download.vue
  72. 411 0
      backstage/src/pages/system/Menu.vue
  73. 309 0
      backstage/src/pages/system/Parameter.vue
  74. 281 0
      backstage/src/pages/system/Password.vue
  75. 664 0
      backstage/src/pages/system/User.vue
  76. 265 0
      backstage/src/pages/system/Worklog.vue
  77. 107 0
      backstage/src/router/index.js
  78. 25 0
      backstage/src/util/commonfn.js
  79. 147 0
      backstage/src/util/index.js
  80. 1 0
      backstage/theme/alert.css
  81. 1 0
      backstage/theme/aside.css
  82. 1 0
      backstage/theme/autocomplete.css
  83. 1 0
      backstage/theme/avatar.css
  84. 1 0
      backstage/theme/backtop.css
  85. 1 0
      backstage/theme/badge.css
  86. 1 0
      backstage/theme/base.css
  87. 0 0
      backstage/theme/breadcrumb-item.css
  88. 1 0
      backstage/theme/breadcrumb.css
  89. 0 0
      backstage/theme/button-group.css
  90. 1 0
      backstage/theme/button.css
  91. 1 0
      backstage/theme/calendar.css
  92. 1 0
      backstage/theme/card.css
  93. 1 0
      backstage/theme/carousel-item.css
  94. 1 0
      backstage/theme/carousel.css
  95. 1 0
      backstage/theme/cascader-panel.css
  96. 1 0
      backstage/theme/cascader.css
  97. 0 0
      backstage/theme/checkbox-button.css
  98. 0 0
      backstage/theme/checkbox-group.css
  99. 1 0
      backstage/theme/checkbox.css
  100. 0 0
      backstage/theme/col.css

.browserslistrc → backstage/.browserslistrc


.eslintrc.js → backstage/.eslintrc.js


+ 21 - 0
backstage/.gitignore

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

+ 24 - 0
backstage/README.md

@@ -0,0 +1,24 @@
+# sh_backstage
+
+## 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/).

babel.config.js → backstage/babel.config.js


ファイルの差分が大きいため隠しています
+ 11843 - 0
backstage/package-lock.json


+ 31 - 0
backstage/package.json

@@ -0,0 +1,31 @@
+{
+  "name": "sh_backstage",
+  "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.19.2",
+    "core-js": "^3.6.4",
+    "element-ui": "^2.13.2",
+    "js-base64": "^2.6.2",
+    "vue": "^2.6.11",
+    "vue-router": "^3.1.6",
+    "vue2-editor": "^2.10.2"
+  },
+  "devDependencies": {
+    "@vue/cli-plugin-babel": "~4.3.0",
+    "@vue/cli-plugin-eslint": "~4.3.0",
+    "@vue/cli-plugin-router": "~4.3.0",
+    "@vue/cli-service": "~4.3.0",
+    "babel-eslint": "^10.1.0",
+    "eslint": "^6.7.2",
+    "eslint-plugin-vue": "^6.2.2",
+    "less": "^3.0.4",
+    "less-loader": "^5.0.0",
+    "vue-template-compiler": "^2.6.11"
+  }
+}

public/static/images/icons/favicon.ico → backstage/public/favicon.ico


+ 19 - 0
backstage/public/index.html

@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+  <head>
+    <meta charset="utf-8">
+    <meta name="google" content="notranslate" />
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    <meta name="renderer" content="webkit">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no, minimal-ui">
+    <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>

+ 31 - 0
backstage/src/App.vue

@@ -0,0 +1,31 @@
+<template>
+  <div id="app">
+    <router-view/>
+  </div>
+</template>
+
+<script>
+import '@/assets/css/reset.less'
+import '@/assets/css/public.less'
+import '@/assets/font/iconfont.css'
+import Vue from 'vue'
+
+
+export default {
+  name: 'App',
+  created(){
+    if (window.localStorage.getItem('userInfo')) {
+      Vue.prototype.$role = JSON.parse(window.localStorage.getItem('userInfo')).role
+    }
+  }
+}
+
+</script>
+
+<style>
+  #app{
+    width: 100%;
+    height: 100%;
+    background-color: #f0f0f2;
+  }
+</style>

+ 0 - 0
backstage/src/assets/css/color.less


+ 4 - 0
backstage/src/assets/css/globalVars.less

@@ -0,0 +1,4 @@
+@cdn:'https://4d-tjw.oss-cn-shenzhen.aliyuncs.com/shls_museum/images/';
+@inputH:30px;
+@theme:#9D362F;
+@sub-theme:#C58C63;

+ 303 - 0
backstage/src/assets/css/public.less

@@ -0,0 +1,303 @@
+*{
+  font-family: 'Microsoft YaHei';
+  word-break: break-all;
+}
+input::-webkit-inner-spin-button{
+  appearance: none;
+  margin: 0;
+}
+
+.layout{
+  width: 100%;
+  height: 100%;
+}
+
+.layout-con{
+  width: 100%;
+  height: 100%;
+  position: fixed;
+}
+
+
+.paddingmore .el-input__inner{
+  padding: 0 60px 0 15px!important;
+}
+
+.middle-title, .register-title,.forget-title{
+  color: #4d4d4d;
+  font-size: 1.75rem;
+  text-align: center;
+  margin-bottom: 3.4375rem;
+  line-height: 1.5;
+}
+
+.register-title,.forget-title{
+  margin-top: 1.25rem;
+}
+
+.middle{
+  position: absolute;
+  top: 30vh;
+  left: 50vw;
+  width: 23.75rem;
+  height: 18.75rem;
+  transform: translate(-50%,-50%);
+}
+.middle-subtitle{
+  font-size: .875rem;
+  font-weight: 500;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+.middle-subtitle::before,.middle-subtitle::after{
+  content: '';
+  height: 0rem;
+  width: 1.25rem;
+  border: .03125rem solid #000; 
+  display: inline-block;
+  margin: 0 .625rem;
+}
+
+.form-bottom{
+  display: flex;
+  justify-content: space-between;
+  padding: 0 .625rem;
+  color: #888;
+}
+
+.form-bottom span{
+  cursor: pointer;
+}
+
+.bottom-div{
+  text-align: center;
+  cursor: pointer;
+  color: #888888;
+}
+
+.test-btn{
+  color: #67c23a!important;
+}
+
+.view-btn{
+  color: #409eff!important;
+}
+.re-apply-btn{
+  color: #f56c6c!important;
+}
+.apply-btn{
+  color: #ec652d!important;
+}
+
+.noCursor .el-upload{
+  cursor: no-drop!important;
+}
+.o-span{
+  cursor: pointer;
+  color: rgb(7, 152, 244);
+}
+
+.o-delete{
+    cursor: pointer;
+    color: #707070;
+    margin-left: 5px;
+    &:hover{
+      color: #409EFF;
+    }
+}
+
+
+.e-pagination{
+  display: flex;
+  justify-content: flex-end;
+  margin-top: 1.5625rem;
+}
+
+:root{
+  --bg_color:rgba(255, 255, 255, 1);
+  --font_color:rgba(0, 0, 0, 0.88);
+  --font_color1:rgba(0, 0, 0, 0.38);
+}
+
+.theme{
+  background: #f2ecde!important;
+}
+
+.theme-color{
+  color: var(--font_color)!important;
+}
+
+.theme-color1{
+  color: var(--font_color1)!important;
+}
+
+.card,.top-body {
+  background: var(--bg_color)!important;
+  border-radius: 2px!important;
+  color: #000!important;
+  border: none!important;
+}
+.top-body {
+  margin: 0 auto 1rem!important;
+}
+
+.info-right{
+  padding-right: 15px;
+}
+
+.el-icon-upload{
+  margin: 20px auto!important;
+}
+
+.el-upload__text p{
+  line-height:1.2!important;
+  font-size:12px!important;
+  margin-bottom: 10px!important;
+  color: #ccc!important;
+}
+
+
+.li-img{
+  max-height: 237px;
+  overflow: hidden;
+  width: 100%;
+  position: relative;
+  img{
+  }
+  .eye{
+    z-index: 9999;
+    position: absolute;
+    right: 10px;
+    top: 10px;
+    width: 20px!important;
+  }
+}
+.collection-con .nodata{
+  width: 100%;
+  padding-top: 10%;
+  text-align: center;
+}
+
+.tab{
+  padding-top: 20px;
+  >li{
+    display: inline-block;
+    margin-right: 20px;
+    padding-bottom: 10px;
+    cursor: pointer;
+  }
+  .active{
+    color: #B63C25;
+    border-bottom: 2px solid #B63C25;
+  }
+}
+
+.main-title {
+  margin: 50px 0;
+  .eidtor{
+    margin: 20px;
+  }
+}
+.title {
+  color: #707070;
+  font-weight: bold;
+  font-size: 14px;
+  margin-bottom: 20px;
+}
+
+.reason{
+  background: #fff;
+  height: 40px;
+  width: 100%;
+  margin-bottom: 10px;
+  line-height: 40px;
+  padding-left: 20px;
+}
+
+
+.text-con{
+  border: 1px solid #ccc;
+  padding: 10px;
+  min-height: 100px;
+  width: calc(100% - 10px) ;
+  margin-left: 20px;
+  color: #ccc;
+}
+
+.icenter{
+  /deep/.el-input__inner{
+    text-align: center;
+  }
+}
+
+.image-slot{
+  min-height: 100px;
+  height: 100%;
+  min-width: 230px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  background: #dddddd;
+  padding: 0 10px;
+  >i{
+    font-size: 40px;
+  }
+}
+
+@media screen and (max-width: 1700px) {
+  html,body{
+    font-size: 15px;
+  }
+}
+
+.con-btn{
+  margin-right: 20px;
+}
+
+.o-span{
+  padding-left: 10px;
+}
+.el-tabs__item{
+  font-size: 16px!important;
+  font-weight: bold!important;
+}
+
+.elInput{
+  width:200px!important;
+  margin:0 20px!important;
+}
+.elSelect{
+  width:150px!important;
+}
+
+@media screen and (max-width: 1400px) {
+  html,body{
+    font-size: 13px;
+  }
+  .collection-con{
+    height: 58vh!important;
+  }
+  .elInput{
+    width:150px!important;
+    margin:0 20px!important;
+  }
+  .elSelect{
+    width:100px!important;
+  }
+}
+
+
+@media screen and (max-width: 1300px) {
+  html,body{
+    font-size: 12px;
+  }
+  .collection-con{
+    height: 61vh!important;
+  }
+  .elInput{
+    width:130px!important;
+    margin:0 10px!important;
+  }
+}

+ 112 - 0
backstage/src/assets/css/reset.less

@@ -0,0 +1,112 @@
+
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, embed, 
+figure, figcaption, footer, header, hgroup, 
+menu, nav, output, ruby, section, summary,
+time, mark, audio, video {
+  font-family: "Microsoft YaHei","Microsoft JhengHei";;
+	margin: 0;
+	padding: 0;
+	border: 0;
+	font-size: 100%;
+	vertical-align: baseline;
+}
+*{
+  box-sizing: border-box;
+}
+/* HTML5 display-role reset for older browsers */
+article, aside, details, figcaption, figure, 
+footer, header, hgroup, menu, nav, section, main {
+	display: block;
+}
+html{
+	height: 100%;
+}
+body {
+	line-height: 1;
+	height: 100%;
+}
+html,body{
+	font-size: 16px;
+}
+ol, ul {
+	list-style: none;
+}
+blockquote, q {
+	quotes: none;
+}
+blockquote:before, blockquote:after,
+q:before, q:after {
+	content: '';
+	content: none;
+}
+table {
+	border-collapse: collapse;
+	border-spacing: 0;
+}
+button{outline:none; border: none;}
+input, button, select, textarea {
+outline: none;
+appearance: none;
+-webkit-appearance: none;
+border-radius: 0;
+-webkit-appearance: textfield;
+    background-color: white;
+    -webkit-rtl-ordering: logical;
+    cursor: text;
+    padding: 1px;
+    border-color: initial;
+    border-image: initial;
+}
+input::-webkit-outer-spin-button,input::-webkit-inner-spin-button{
+        -webkit-appearance:textfield;
+}
+input[type="number"]{
+        -moz-appearance:textfield;
+}
+input:focus { outline: none; } 
+select::-ms-expand {
+display: none;
+}
+
+
+::-webkit-scrollbar-track-piece {  
+	background-color:#ffffff;  
+}  
+::-webkit-scrollbar {  
+	width:8px;  
+	height:13px;  
+}  
+::-webkit-scrollbar-thumb {  
+	background-color:#e5e5e5;  
+	background-clip:padding-box;  
+	min-height:20px;  
+	border-radius: 3px;
+}  
+::-webkit-scrollbar-thumb:hover {  
+	background-color:#929292;  
+}  
+
+table th,
+table td {
+  text-align: center !important;
+}
+
+table th > .cell{
+	color: #000;
+}
+
+textarea{
+  height: 8rem!important;
+}
+
+/** 文本输入框的 X **/input::-ms-clear{display: none;}
+/** 密码输入框的 X **/input::-ms-reveal{display: none;}

+ 539 - 0
backstage/src/assets/font/demo.css

@@ -0,0 +1,539 @@
+/* Logo 字体 */
+@font-face {
+  font-family: "iconfont logo";
+  src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
+  src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
+    url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
+    url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
+    url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
+}
+
+.logo {
+  font-family: "iconfont logo";
+  font-size: 160px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+/* tabs */
+.nav-tabs {
+  position: relative;
+}
+
+.nav-tabs .nav-more {
+  position: absolute;
+  right: 0;
+  bottom: 0;
+  height: 42px;
+  line-height: 42px;
+  color: #666;
+}
+
+#tabs {
+  border-bottom: 1px solid #eee;
+}
+
+#tabs li {
+  cursor: pointer;
+  width: 100px;
+  height: 40px;
+  line-height: 40px;
+  text-align: center;
+  font-size: 16px;
+  border-bottom: 2px solid transparent;
+  position: relative;
+  z-index: 1;
+  margin-bottom: -1px;
+  color: #666;
+}
+
+
+#tabs .active {
+  border-bottom-color: #f00;
+  color: #222;
+}
+
+.tab-container .content {
+  display: none;
+}
+
+/* 页面布局 */
+.main {
+  padding: 30px 100px;
+  width: 960px;
+  margin: 0 auto;
+}
+
+.main .logo {
+  color: #333;
+  text-align: left;
+  margin-bottom: 30px;
+  line-height: 1;
+  height: 110px;
+  margin-top: -50px;
+  overflow: hidden;
+  *zoom: 1;
+}
+
+.main .logo a {
+  font-size: 160px;
+  color: #333;
+}
+
+.helps {
+  margin-top: 40px;
+}
+
+.helps pre {
+  padding: 20px;
+  margin: 10px 0;
+  border: solid 1px #e7e1cd;
+  background-color: #fffdef;
+  overflow: auto;
+}
+
+.icon_lists {
+  width: 100% !important;
+  overflow: hidden;
+  *zoom: 1;
+}
+
+.icon_lists li {
+  width: 100px;
+  margin-bottom: 10px;
+  margin-right: 20px;
+  text-align: center;
+  list-style: none !important;
+  cursor: default;
+}
+
+.icon_lists li .code-name {
+  line-height: 1.2;
+}
+
+.icon_lists .icon {
+  display: block;
+  height: 100px;
+  line-height: 100px;
+  font-size: 42px;
+  margin: 10px auto;
+  color: #333;
+  -webkit-transition: font-size 0.25s linear, width 0.25s linear;
+  -moz-transition: font-size 0.25s linear, width 0.25s linear;
+  transition: font-size 0.25s linear, width 0.25s linear;
+}
+
+.icon_lists .icon:hover {
+  font-size: 100px;
+}
+
+.icon_lists .svg-icon {
+  /* 通过设置 font-size 来改变图标大小 */
+  width: 1em;
+  /* 图标和文字相邻时,垂直对齐 */
+  vertical-align: -0.15em;
+  /* 通过设置 color 来改变 SVG 的颜色/fill */
+  fill: currentColor;
+  /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
+      normalize.css 中也包含这行 */
+  overflow: hidden;
+}
+
+.icon_lists li .name,
+.icon_lists li .code-name {
+  color: #666;
+}
+
+/* markdown 样式 */
+.markdown {
+  color: #666;
+  font-size: 14px;
+  line-height: 1.8;
+}
+
+.highlight {
+  line-height: 1.5;
+}
+
+.markdown img {
+  vertical-align: middle;
+  max-width: 100%;
+}
+
+.markdown h1 {
+  color: #404040;
+  font-weight: 500;
+  line-height: 40px;
+  margin-bottom: 24px;
+}
+
+.markdown h2,
+.markdown h3,
+.markdown h4,
+.markdown h5,
+.markdown h6 {
+  color: #404040;
+  margin: 1.6em 0 0.6em 0;
+  font-weight: 500;
+  clear: both;
+}
+
+.markdown h1 {
+  font-size: 28px;
+}
+
+.markdown h2 {
+  font-size: 22px;
+}
+
+.markdown h3 {
+  font-size: 16px;
+}
+
+.markdown h4 {
+  font-size: 14px;
+}
+
+.markdown h5 {
+  font-size: 12px;
+}
+
+.markdown h6 {
+  font-size: 12px;
+}
+
+.markdown hr {
+  height: 1px;
+  border: 0;
+  background: #e9e9e9;
+  margin: 16px 0;
+  clear: both;
+}
+
+.markdown p {
+  margin: 1em 0;
+}
+
+.markdown>p,
+.markdown>blockquote,
+.markdown>.highlight,
+.markdown>ol,
+.markdown>ul {
+  width: 80%;
+}
+
+.markdown ul>li {
+  list-style: circle;
+}
+
+.markdown>ul li,
+.markdown blockquote ul>li {
+  margin-left: 20px;
+  padding-left: 4px;
+}
+
+.markdown>ul li p,
+.markdown>ol li p {
+  margin: 0.6em 0;
+}
+
+.markdown ol>li {
+  list-style: decimal;
+}
+
+.markdown>ol li,
+.markdown blockquote ol>li {
+  margin-left: 20px;
+  padding-left: 4px;
+}
+
+.markdown code {
+  margin: 0 3px;
+  padding: 0 5px;
+  background: #eee;
+  border-radius: 3px;
+}
+
+.markdown strong,
+.markdown b {
+  font-weight: 600;
+}
+
+.markdown>table {
+  border-collapse: collapse;
+  border-spacing: 0px;
+  empty-cells: show;
+  border: 1px solid #e9e9e9;
+  width: 95%;
+  margin-bottom: 24px;
+}
+
+.markdown>table th {
+  white-space: nowrap;
+  color: #333;
+  font-weight: 600;
+}
+
+.markdown>table th,
+.markdown>table td {
+  border: 1px solid #e9e9e9;
+  padding: 8px 16px;
+  text-align: left;
+}
+
+.markdown>table th {
+  background: #F7F7F7;
+}
+
+.markdown blockquote {
+  font-size: 90%;
+  color: #999;
+  border-left: 4px solid #e9e9e9;
+  padding-left: 0.8em;
+  margin: 1em 0;
+}
+
+.markdown blockquote p {
+  margin: 0;
+}
+
+.markdown .anchor {
+  opacity: 0;
+  transition: opacity 0.3s ease;
+  margin-left: 8px;
+}
+
+.markdown .waiting {
+  color: #ccc;
+}
+
+.markdown h1:hover .anchor,
+.markdown h2:hover .anchor,
+.markdown h3:hover .anchor,
+.markdown h4:hover .anchor,
+.markdown h5:hover .anchor,
+.markdown h6:hover .anchor {
+  opacity: 1;
+  display: inline-block;
+}
+
+.markdown>br,
+.markdown>p>br {
+  clear: both;
+}
+
+
+.hljs {
+  display: block;
+  background: white;
+  padding: 0.5em;
+  color: #333333;
+  overflow-x: auto;
+}
+
+.hljs-comment,
+.hljs-meta {
+  color: #969896;
+}
+
+.hljs-string,
+.hljs-variable,
+.hljs-template-variable,
+.hljs-strong,
+.hljs-emphasis,
+.hljs-quote {
+  color: #df5000;
+}
+
+.hljs-keyword,
+.hljs-selector-tag,
+.hljs-type {
+  color: #a71d5d;
+}
+
+.hljs-literal,
+.hljs-symbol,
+.hljs-bullet,
+.hljs-attribute {
+  color: #0086b3;
+}
+
+.hljs-section,
+.hljs-name {
+  color: #63a35c;
+}
+
+.hljs-tag {
+  color: #333333;
+}
+
+.hljs-title,
+.hljs-attr,
+.hljs-selector-id,
+.hljs-selector-class,
+.hljs-selector-attr,
+.hljs-selector-pseudo {
+  color: #795da3;
+}
+
+.hljs-addition {
+  color: #55a532;
+  background-color: #eaffea;
+}
+
+.hljs-deletion {
+  color: #bd2c00;
+  background-color: #ffecec;
+}
+
+.hljs-link {
+  text-decoration: underline;
+}
+
+/* 代码高亮 */
+/* PrismJS 1.15.0
+https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
+/**
+ * prism.js default theme for JavaScript, CSS and HTML
+ * Based on dabblet (http://dabblet.com)
+ * @author Lea Verou
+ */
+code[class*="language-"],
+pre[class*="language-"] {
+  color: black;
+  background: none;
+  text-shadow: 0 1px white;
+  font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
+  text-align: left;
+  white-space: pre;
+  word-spacing: normal;
+  word-break: normal;
+  word-wrap: normal;
+  line-height: 1.5;
+
+  -moz-tab-size: 4;
+  -o-tab-size: 4;
+  tab-size: 4;
+
+  -webkit-hyphens: none;
+  -moz-hyphens: none;
+  -ms-hyphens: none;
+  hyphens: none;
+}
+
+pre[class*="language-"]::-moz-selection,
+pre[class*="language-"] ::-moz-selection,
+code[class*="language-"]::-moz-selection,
+code[class*="language-"] ::-moz-selection {
+  text-shadow: none;
+  background: #b3d4fc;
+}
+
+pre[class*="language-"]::selection,
+pre[class*="language-"] ::selection,
+code[class*="language-"]::selection,
+code[class*="language-"] ::selection {
+  text-shadow: none;
+  background: #b3d4fc;
+}
+
+@media print {
+
+  code[class*="language-"],
+  pre[class*="language-"] {
+    text-shadow: none;
+  }
+}
+
+/* Code blocks */
+pre[class*="language-"] {
+  padding: 1em;
+  margin: .5em 0;
+  overflow: auto;
+}
+
+:not(pre)>code[class*="language-"],
+pre[class*="language-"] {
+  background: #f5f2f0;
+}
+
+/* Inline code */
+:not(pre)>code[class*="language-"] {
+  padding: .1em;
+  border-radius: .3em;
+  white-space: normal;
+}
+
+.token.comment,
+.token.prolog,
+.token.doctype,
+.token.cdata {
+  color: slategray;
+}
+
+.token.punctuation {
+  color: #999;
+}
+
+.namespace {
+  opacity: .7;
+}
+
+.token.property,
+.token.tag,
+.token.boolean,
+.token.number,
+.token.constant,
+.token.symbol,
+.token.deleted {
+  color: #905;
+}
+
+.token.selector,
+.token.attr-name,
+.token.string,
+.token.char,
+.token.builtin,
+.token.inserted {
+  color: #690;
+}
+
+.token.operator,
+.token.entity,
+.token.url,
+.language-css .token.string,
+.style .token.string {
+  color: #9a6e3a;
+  background: hsla(0, 0%, 100%, .5);
+}
+
+.token.atrule,
+.token.attr-value,
+.token.keyword {
+  color: #07a;
+}
+
+.token.function,
+.token.class-name {
+  color: #DD4A68;
+}
+
+.token.regex,
+.token.important,
+.token.variable {
+  color: #e90;
+}
+
+.token.important,
+.token.bold {
+  font-weight: bold;
+}
+
+.token.italic {
+  font-style: italic;
+}
+
+.token.entity {
+  cursor: help;
+}

+ 607 - 0
backstage/src/assets/font/demo_index.html

@@ -0,0 +1,607 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8"/>
+  <title>IconFont Demo</title>
+  <link rel="shortcut icon" href="https://img.alicdn.com/tps/i4/TB1_oz6GVXXXXaFXpXXJDFnIXXX-64-64.ico" type="image/x-icon"/>
+  <link rel="stylesheet" href="https://g.alicdn.com/thx/cube/1.3.2/cube.min.css">
+  <link rel="stylesheet" href="demo.css">
+  <link rel="stylesheet" href="iconfont.css">
+  <script src="iconfont.js"></script>
+  <!-- jQuery -->
+  <script src="https://a1.alicdn.com/oss/uploads/2018/12/26/7bfddb60-08e8-11e9-9b04-53e73bb6408b.js"></script>
+  <!-- 代码高亮 -->
+  <script src="https://a1.alicdn.com/oss/uploads/2018/12/26/a3f714d0-08e6-11e9-8a15-ebf944d7534c.js"></script>
+</head>
+<body>
+  <div class="main">
+    <h1 class="logo"><a href="https://www.iconfont.cn/" title="iconfont 首页" target="_blank">&#xe86b;</a></h1>
+    <div class="nav-tabs">
+      <ul id="tabs" class="dib-box">
+        <li class="dib active"><span>Unicode</span></li>
+        <li class="dib"><span>Font class</span></li>
+        <li class="dib"><span>Symbol</span></li>
+      </ul>
+      
+      <a href="https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=1855608" target="_blank" class="nav-more">查看项目</a>
+      
+    </div>
+    <div class="tab-container">
+      <div class="content unicode" style="display: block;">
+          <ul class="icon_lists dib-box">
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe6f1;</span>
+                <div class="name">previous</div>
+                <div class="code-name">&amp;#xe6f1;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe6f2;</span>
+                <div class="name">Page_ele</div>
+                <div class="code-name">&amp;#xe6f2;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe6f3;</span>
+                <div class="name">next</div>
+                <div class="code-name">&amp;#xe6f3;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe6f4;</span>
+                <div class="name">sys_nav_index</div>
+                <div class="code-name">&amp;#xe6f4;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe6f5;</span>
+                <div class="name">search</div>
+                <div class="code-name">&amp;#xe6f5;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe6f6;</span>
+                <div class="name">sys_edit</div>
+                <div class="code-name">&amp;#xe6f6;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe6f7;</span>
+                <div class="name">Page</div>
+                <div class="code-name">&amp;#xe6f7;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe6f8;</span>
+                <div class="name">sys_down</div>
+                <div class="code-name">&amp;#xe6f8;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe6f9;</span>
+                <div class="name">sys_nav_work</div>
+                <div class="code-name">&amp;#xe6f9;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe6fa;</span>
+                <div class="name">sys_nav_system</div>
+                <div class="code-name">&amp;#xe6fa;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe6fb;</span>
+                <div class="name">tit_ele</div>
+                <div class="code-name">&amp;#xe6fb;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe6ef;</span>
+                <div class="name">mode</div>
+                <div class="code-name">&amp;#xe6ef;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe6f0;</span>
+                <div class="name">nav_guestbook</div>
+                <div class="code-name">&amp;#xe6f0;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe6e9;</span>
+                <div class="name">guestbook_btnbg_first</div>
+                <div class="code-name">&amp;#xe6e9;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe6ea;</span>
+                <div class="name">btnbg_normal</div>
+                <div class="code-name">&amp;#xe6ea;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe6eb;</span>
+                <div class="name">guestbook_success</div>
+                <div class="code-name">&amp;#xe6eb;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe6ec;</span>
+                <div class="name">guestbook_close</div>
+                <div class="code-name">&amp;#xe6ec;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe6ed;</span>
+                <div class="name">logo_tit</div>
+                <div class="code-name">&amp;#xe6ed;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe6ee;</span>
+                <div class="name">logo_normal</div>
+                <div class="code-name">&amp;#xe6ee;</div>
+              </li>
+          
+          </ul>
+          <div class="article markdown">
+          <h2 id="unicode-">Unicode 引用</h2>
+          <hr>
+
+          <p>Unicode 是字体在网页端最原始的应用方式,特点是:</p>
+          <ul>
+            <li>兼容性最好,支持 IE6+,及所有现代浏览器。</li>
+            <li>支持按字体的方式去动态调整图标大小,颜色等等。</li>
+            <li>但是因为是字体,所以不支持多色。只能使用平台里单色的图标,就算项目里有多色图标也会自动去色。</li>
+          </ul>
+          <blockquote>
+            <p>注意:新版 iconfont 支持多色图标,这些多色图标在 Unicode 模式下将不能使用,如果有需求建议使用symbol 的引用方式</p>
+          </blockquote>
+          <p>Unicode 使用步骤如下:</p>
+          <h3 id="-font-face">第一步:拷贝项目下面生成的 <code>@font-face</code></h3>
+<pre><code class="language-css"
+>@font-face {
+  font-family: 'iconfont';
+  src: url('iconfont.eot');
+  src: url('iconfont.eot?#iefix') format('embedded-opentype'),
+      url('iconfont.woff2') format('woff2'),
+      url('iconfont.woff') format('woff'),
+      url('iconfont.ttf') format('truetype'),
+      url('iconfont.svg#iconfont') format('svg');
+}
+</code></pre>
+          <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
+<pre><code class="language-css"
+>.iconfont {
+  font-family: "iconfont" !important;
+  font-size: 16px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+</code></pre>
+          <h3 id="-">第三步:挑选相应图标并获取字体编码,应用于页面</h3>
+<pre>
+<code class="language-html"
+>&lt;span class="iconfont"&gt;&amp;#x33;&lt;/span&gt;
+</code></pre>
+          <blockquote>
+            <p>"iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
+          </blockquote>
+          </div>
+      </div>
+      <div class="content font-class">
+        <ul class="icon_lists dib-box">
+          
+          <li class="dib">
+            <span class="icon iconfont iconprevious"></span>
+            <div class="name">
+              previous
+            </div>
+            <div class="code-name">.iconprevious
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconPage_ele"></span>
+            <div class="name">
+              Page_ele
+            </div>
+            <div class="code-name">.iconPage_ele
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconnext"></span>
+            <div class="name">
+              next
+            </div>
+            <div class="code-name">.iconnext
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconsys_nav_index"></span>
+            <div class="name">
+              sys_nav_index
+            </div>
+            <div class="code-name">.iconsys_nav_index
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconsearch"></span>
+            <div class="name">
+              search
+            </div>
+            <div class="code-name">.iconsearch
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconsys_edit"></span>
+            <div class="name">
+              sys_edit
+            </div>
+            <div class="code-name">.iconsys_edit
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconPage"></span>
+            <div class="name">
+              Page
+            </div>
+            <div class="code-name">.iconPage
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconsys_down"></span>
+            <div class="name">
+              sys_down
+            </div>
+            <div class="code-name">.iconsys_down
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconsys_nav_work"></span>
+            <div class="name">
+              sys_nav_work
+            </div>
+            <div class="code-name">.iconsys_nav_work
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconsys_nav_system"></span>
+            <div class="name">
+              sys_nav_system
+            </div>
+            <div class="code-name">.iconsys_nav_system
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icontit_ele"></span>
+            <div class="name">
+              tit_ele
+            </div>
+            <div class="code-name">.icontit_ele
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconmode"></span>
+            <div class="name">
+              mode
+            </div>
+            <div class="code-name">.iconmode
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconnav_guestbook"></span>
+            <div class="name">
+              nav_guestbook
+            </div>
+            <div class="code-name">.iconnav_guestbook
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconguestbook_btnbg_first"></span>
+            <div class="name">
+              guestbook_btnbg_first
+            </div>
+            <div class="code-name">.iconguestbook_btnbg_first
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconbtnbg_normal"></span>
+            <div class="name">
+              btnbg_normal
+            </div>
+            <div class="code-name">.iconbtnbg_normal
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconguestbook_success"></span>
+            <div class="name">
+              guestbook_success
+            </div>
+            <div class="code-name">.iconguestbook_success
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconguestbook_close"></span>
+            <div class="name">
+              guestbook_close
+            </div>
+            <div class="code-name">.iconguestbook_close
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconlogo_tit"></span>
+            <div class="name">
+              logo_tit
+            </div>
+            <div class="code-name">.iconlogo_tit
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconlogo_normal"></span>
+            <div class="name">
+              logo_normal
+            </div>
+            <div class="code-name">.iconlogo_normal
+            </div>
+          </li>
+          
+        </ul>
+        <div class="article markdown">
+        <h2 id="font-class-">font-class 引用</h2>
+        <hr>
+
+        <p>font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。</p>
+        <p>与 Unicode 使用方式相比,具有如下特点:</p>
+        <ul>
+          <li>兼容性良好,支持 IE8+,及所有现代浏览器。</li>
+          <li>相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。</li>
+          <li>因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。</li>
+          <li>不过因为本质上还是使用的字体,所以多色图标还是不支持的。</li>
+        </ul>
+        <p>使用步骤如下:</p>
+        <h3 id="-fontclass-">第一步:引入项目下面生成的 fontclass 代码:</h3>
+<pre><code class="language-html">&lt;link rel="stylesheet" href="./iconfont.css"&gt;
+</code></pre>
+        <h3 id="-">第二步:挑选相应图标并获取类名,应用于页面:</h3>
+<pre><code class="language-html">&lt;span class="iconfont iconxxx"&gt;&lt;/span&gt;
+</code></pre>
+        <blockquote>
+          <p>"
+            iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
+        </blockquote>
+      </div>
+      </div>
+      <div class="content symbol">
+          <ul class="icon_lists dib-box">
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconprevious"></use>
+                </svg>
+                <div class="name">previous</div>
+                <div class="code-name">#iconprevious</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconPage_ele"></use>
+                </svg>
+                <div class="name">Page_ele</div>
+                <div class="code-name">#iconPage_ele</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconnext"></use>
+                </svg>
+                <div class="name">next</div>
+                <div class="code-name">#iconnext</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconsys_nav_index"></use>
+                </svg>
+                <div class="name">sys_nav_index</div>
+                <div class="code-name">#iconsys_nav_index</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconsearch"></use>
+                </svg>
+                <div class="name">search</div>
+                <div class="code-name">#iconsearch</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconsys_edit"></use>
+                </svg>
+                <div class="name">sys_edit</div>
+                <div class="code-name">#iconsys_edit</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconPage"></use>
+                </svg>
+                <div class="name">Page</div>
+                <div class="code-name">#iconPage</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconsys_down"></use>
+                </svg>
+                <div class="name">sys_down</div>
+                <div class="code-name">#iconsys_down</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconsys_nav_work"></use>
+                </svg>
+                <div class="name">sys_nav_work</div>
+                <div class="code-name">#iconsys_nav_work</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconsys_nav_system"></use>
+                </svg>
+                <div class="name">sys_nav_system</div>
+                <div class="code-name">#iconsys_nav_system</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icontit_ele"></use>
+                </svg>
+                <div class="name">tit_ele</div>
+                <div class="code-name">#icontit_ele</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconmode"></use>
+                </svg>
+                <div class="name">mode</div>
+                <div class="code-name">#iconmode</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconnav_guestbook"></use>
+                </svg>
+                <div class="name">nav_guestbook</div>
+                <div class="code-name">#iconnav_guestbook</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconguestbook_btnbg_first"></use>
+                </svg>
+                <div class="name">guestbook_btnbg_first</div>
+                <div class="code-name">#iconguestbook_btnbg_first</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconbtnbg_normal"></use>
+                </svg>
+                <div class="name">btnbg_normal</div>
+                <div class="code-name">#iconbtnbg_normal</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconguestbook_success"></use>
+                </svg>
+                <div class="name">guestbook_success</div>
+                <div class="code-name">#iconguestbook_success</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconguestbook_close"></use>
+                </svg>
+                <div class="name">guestbook_close</div>
+                <div class="code-name">#iconguestbook_close</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconlogo_tit"></use>
+                </svg>
+                <div class="name">logo_tit</div>
+                <div class="code-name">#iconlogo_tit</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconlogo_normal"></use>
+                </svg>
+                <div class="name">logo_normal</div>
+                <div class="code-name">#iconlogo_normal</div>
+            </li>
+          
+          </ul>
+          <div class="article markdown">
+          <h2 id="symbol-">Symbol 引用</h2>
+          <hr>
+
+          <p>这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇<a href="">文章</a>
+            这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:</p>
+          <ul>
+            <li>支持多色图标了,不再受单色限制。</li>
+            <li>通过一些技巧,支持像字体那样,通过 <code>font-size</code>, <code>color</code> 来调整样式。</li>
+            <li>兼容性较差,支持 IE9+,及现代浏览器。</li>
+            <li>浏览器渲染 SVG 的性能一般,还不如 png。</li>
+          </ul>
+          <p>使用步骤如下:</p>
+          <h3 id="-symbol-">第一步:引入项目下面生成的 symbol 代码:</h3>
+<pre><code class="language-html">&lt;script src="./iconfont.js"&gt;&lt;/script&gt;
+</code></pre>
+          <h3 id="-css-">第二步:加入通用 CSS 代码(引入一次就行):</h3>
+<pre><code class="language-html">&lt;style&gt;
+.icon {
+  width: 1em;
+  height: 1em;
+  vertical-align: -0.15em;
+  fill: currentColor;
+  overflow: hidden;
+}
+&lt;/style&gt;
+</code></pre>
+          <h3 id="-">第三步:挑选相应图标并获取类名,应用于页面:</h3>
+<pre><code class="language-html">&lt;svg class="icon" aria-hidden="true"&gt;
+  &lt;use xlink:href="#icon-xxx"&gt;&lt;/use&gt;
+&lt;/svg&gt;
+</code></pre>
+          </div>
+      </div>
+
+    </div>
+  </div>
+  <script>
+  $(document).ready(function () {
+      $('.tab-container .content:first').show()
+
+      $('#tabs li').click(function (e) {
+        var tabContent = $('.tab-container .content')
+        var index = $(this).index()
+
+        if ($(this).hasClass('active')) {
+          return
+        } else {
+          $('#tabs li').removeClass('active')
+          $(this).addClass('active')
+
+          tabContent.hide().eq(index).fadeIn()
+        }
+      })
+    })
+  </script>
+</body>
+</html>

BIN
backstage/src/assets/font/download.zip


ファイルの差分が大きいため隠しています
+ 93 - 0
backstage/src/assets/font/iconfont.css


BIN
backstage/src/assets/font/iconfont.eot


ファイルの差分が大きいため隠しています
+ 1 - 0
backstage/src/assets/font/iconfont.js


+ 142 - 0
backstage/src/assets/font/iconfont.json

@@ -0,0 +1,142 @@
+{
+  "id": "1855608",
+  "name": "郑州信息工程大学",
+  "font_family": "iconfont",
+  "css_prefix_text": "icon",
+  "description": "展示界面和数字史馆管理系统",
+  "glyphs": [
+    {
+      "icon_id": "15079056",
+      "name": "previous",
+      "font_class": "previous",
+      "unicode": "e6f1",
+      "unicode_decimal": 59121
+    },
+    {
+      "icon_id": "15079057",
+      "name": "Page_ele",
+      "font_class": "Page_ele",
+      "unicode": "e6f2",
+      "unicode_decimal": 59122
+    },
+    {
+      "icon_id": "15079058",
+      "name": "next",
+      "font_class": "next",
+      "unicode": "e6f3",
+      "unicode_decimal": 59123
+    },
+    {
+      "icon_id": "15079059",
+      "name": "sys_nav_index",
+      "font_class": "sys_nav_index",
+      "unicode": "e6f4",
+      "unicode_decimal": 59124
+    },
+    {
+      "icon_id": "15079060",
+      "name": "search",
+      "font_class": "search",
+      "unicode": "e6f5",
+      "unicode_decimal": 59125
+    },
+    {
+      "icon_id": "15079061",
+      "name": "sys_edit",
+      "font_class": "sys_edit",
+      "unicode": "e6f6",
+      "unicode_decimal": 59126
+    },
+    {
+      "icon_id": "15079062",
+      "name": "Page",
+      "font_class": "Page",
+      "unicode": "e6f7",
+      "unicode_decimal": 59127
+    },
+    {
+      "icon_id": "15079063",
+      "name": "sys_down",
+      "font_class": "sys_down",
+      "unicode": "e6f8",
+      "unicode_decimal": 59128
+    },
+    {
+      "icon_id": "15079064",
+      "name": "sys_nav_work",
+      "font_class": "sys_nav_work",
+      "unicode": "e6f9",
+      "unicode_decimal": 59129
+    },
+    {
+      "icon_id": "15079065",
+      "name": "sys_nav_system",
+      "font_class": "sys_nav_system",
+      "unicode": "e6fa",
+      "unicode_decimal": 59130
+    },
+    {
+      "icon_id": "15079066",
+      "name": "tit_ele",
+      "font_class": "tit_ele",
+      "unicode": "e6fb",
+      "unicode_decimal": 59131
+    },
+    {
+      "icon_id": "15079018",
+      "name": "mode",
+      "font_class": "mode",
+      "unicode": "e6ef",
+      "unicode_decimal": 59119
+    },
+    {
+      "icon_id": "15079019",
+      "name": "nav_guestbook",
+      "font_class": "nav_guestbook",
+      "unicode": "e6f0",
+      "unicode_decimal": 59120
+    },
+    {
+      "icon_id": "15078995",
+      "name": "guestbook_btnbg_first",
+      "font_class": "guestbook_btnbg_first",
+      "unicode": "e6e9",
+      "unicode_decimal": 59113
+    },
+    {
+      "icon_id": "15078996",
+      "name": "btnbg_normal",
+      "font_class": "btnbg_normal",
+      "unicode": "e6ea",
+      "unicode_decimal": 59114
+    },
+    {
+      "icon_id": "15078997",
+      "name": "guestbook_success",
+      "font_class": "guestbook_success",
+      "unicode": "e6eb",
+      "unicode_decimal": 59115
+    },
+    {
+      "icon_id": "15078998",
+      "name": "guestbook_close",
+      "font_class": "guestbook_close",
+      "unicode": "e6ec",
+      "unicode_decimal": 59116
+    },
+    {
+      "icon_id": "15078999",
+      "name": "logo_tit",
+      "font_class": "logo_tit",
+      "unicode": "e6ed",
+      "unicode_decimal": 59117
+    },
+    {
+      "icon_id": "15079000",
+      "name": "logo_normal",
+      "font_class": "logo_normal",
+      "unicode": "e6ee",
+      "unicode_decimal": 59118
+    }
+  ]
+}

ファイルの差分が大きいため隠しています
+ 83 - 0
backstage/src/assets/font/iconfont.svg


BIN
backstage/src/assets/font/iconfont.ttf


BIN
backstage/src/assets/font/iconfont.woff


BIN
backstage/src/assets/font/iconfont.woff2


BIN
backstage/src/assets/img/01jk.png


BIN
backstage/src/assets/img/01jks.png


BIN
backstage/src/assets/img/02fb.png


BIN
backstage/src/assets/img/03mg.png


BIN
backstage/src/assets/img/04fb.png


BIN
backstage/src/assets/img/4dage-logo.png


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


BIN
backstage/src/assets/img/biyan.png


BIN
backstage/src/assets/img/delete.png


BIN
backstage/src/assets/img/edit.png


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


BIN
backstage/src/assets/img/logout.png


BIN
backstage/src/assets/img/noPicture.png


BIN
backstage/src/assets/img/user.png


BIN
backstage/src/assets/img/wenli.png


BIN
backstage/src/assets/img/zhoushan-logo.jpg


+ 71 - 0
backstage/src/components/Editor.vue

@@ -0,0 +1,71 @@
+<template>
+  <div>
+    <VueEditor
+      ref="editor"
+      @text-change="textChange"
+      v-model="editContent"
+      @image-added="handleImageAdded"
+      useCustomImageHandler
+    ></VueEditor>
+  </div>
+</template>
+
+<script>
+import { VueEditor } from "vue2-editor";
+
+export default {
+  props: {
+    content: {
+      default: "",
+      type: String,
+    },
+  },
+  data() {
+    return {
+        editContent:''
+    };
+  },
+  watch:{
+      content(newVal){
+        this.editContent = newVal
+      }
+  },
+  mounted(){
+      this.$nextTick(()=>{
+        this.editContent = this.content
+      })
+  },
+  components: {
+    VueEditor,
+  },
+  methods: {
+    textChange() {
+      this.$emit("onchange", this.editContent);
+    },
+    async handleImageAdded(file, Editor, cursorLocation, resetUploader) {
+      var formData = new FormData();
+      formData.append("file", file);
+      let result = await this.$http({
+        method: "post",
+        data: formData,
+        headers: {
+          token: window.localStorage.getItem("token"),
+          'Content-Type':'multipart/form-data'
+        },
+        url: `/manage/file/upload`,
+      });
+      let url = result.data.urlPath;
+      console.log(url);
+      Editor.insertEmbed(
+        cursorLocation,
+        "image",
+        url
+      );
+      resetUploader();
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+</style>

+ 266 - 0
backstage/src/components/Tinymce/index.vue

@@ -0,0 +1,266 @@
+<template>
+  <div class="tinymce-container editor-container" :class="{fullscreen:fullscreen}">
+    <textarea :id="tinymceId" class="tinymce-textarea" />
+  </div>
+</template>
+
+<script>
+import plugins from "./plugins"; // 见下文
+import toolbar from "./toolbar"; // 见下文
+
+// 上传html片段接口根据自己项目更换
+// import { uploadHtml } from "@/api/upload";
+export default {
+  name: "Tinymce",
+  props: {
+    // 默认填充到富文本的html文件
+    htmlstr: {
+      type: String,
+      default: "",
+    },
+    toolbar: {
+      type: Array,
+      default() {
+        return [];
+      },
+    },
+    menubar: {
+      type: Boolean,
+      default: true,
+    },
+    height: {
+      type: Number,
+      default: 400,
+    },
+  },
+  data() {
+    return {
+      hasChange: false,
+      hasInit: false,
+      tinymceId: "vue-tinymce-" + +new Date(),
+      fullscreen: false,
+      value: "",
+      editorContent: "",
+    };
+  },
+  watch: {
+    value(val) {
+      this.$nextTick(() =>
+        window.tinymce.get(this.tinymceId).setContent(val || "")
+      );
+    },
+    htmlstr(val) {
+      this.value = val
+      if (this.isUrl) {
+        this.loadUrl(val);
+      }
+    },
+  },
+  created() {
+      this.value = this.htmlstr + "";
+      this.editorContent = this.htmlstr + "";
+  },
+  mounted() {
+    this.initTinymce();
+  },
+  activated() {
+    this.initTinymce();
+  },
+  deactivated() {
+    this.destroyTinymce();
+  },
+  destroyed() {
+    this.destroyTinymce();
+  },
+  methods: {
+    initTinymce() {
+      window.tinymce.init({
+        branding:false,
+        fontsize_formats: "12px 14px 16px 18px 20px 24px 36px",
+        language: "zh_CN",
+        language_url: "./static/tinymce/langs/zh_CN.js",
+        selector: `#${this.tinymceId}`,
+        height: this.height,
+        body_class: "panel-body ",
+        object_resizing: true,
+        toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar,
+        menubar: this.menubar,
+        plugins: plugins,
+        end_container_on_empty_block: true,
+        powerpaste_word_import: "clean",
+        paste_data_images:true,
+        code_dialog_height: 450,
+        code_dialog_width: 1000,
+        advlist_bullet_styles: "square",
+        advlist_number_styles: "default",
+        default_link_target: "_blank",
+        link_title: false,
+        file_picker_types:"file",
+        file_picker_callback:(cb,value,meta)=>{
+          console.log(cb,value,meta)          
+        },
+        file_upload_handler:async (blobInfo,success,failure)=>{
+          let form = new FormData()
+          console.log(blobInfo.file)
+          form.append('file',blobInfo.file)
+           await this.$http({
+            method: "post",
+            data: form,
+            headers: {
+              token: window.localStorage.getItem("token"),
+              'Content-Type':'multipart/form-data'
+            },
+            url: `/manage/file/upload`,
+          }).then(result=>{
+            let url = result.data.urlPath;
+            success(url)
+          }).catch(err=>{
+            failure(err)
+          })
+        },
+        images_upload_handler:async (blobInfo,success,failure)=>{
+          let form = new FormData()
+          console.log(failure)
+          form.append('file',blobInfo.blob(),blobInfo.filename())
+          await this.$http({
+            method: "post",
+            data: form,
+            headers: {
+              token: window.localStorage.getItem("token"),
+              'Content-Type':'multipart/form-data'
+            },
+            url: `/manage/file/upload`,
+          }).then(result=>{
+            let url = result.data.urlPath;
+            success(url)
+          }).catch(()=>{
+            this.$alert("上传失败,请稍后再试", "提示", {
+              confirmButtonText: "确定",
+              callback: () => {
+                this.loading.close()
+              },
+            });
+          })
+        },
+        init_instance_callback: (editor) => {
+          if (this.value) {
+            editor.setContent(this.value);
+          }
+          this.hasInit = true;
+          editor.on("Blur", () => {
+            this.hasChange = true;
+            this.$emit("blur", editor.getContent());
+            this.editorContent = editor.getContent();
+          });
+        },
+        setup(editor) {
+          editor.on("FullscreenStateChanged", (e) => {
+            this.fullscreen = e.state;
+          });
+        },
+      });
+    },
+    destroyTinymce() {
+      if (window.tinymce.get(this.tinymceId)) {
+        window.tinymce.get(this.tinymceId).destroy();
+      }
+    },
+    loadUrl(url) {
+      this.editorContent = url
+      // if (url && url.length > 0) {
+      //   this.$http
+      //     .get(url)
+      //     .then((response) => {
+      //       // 处理HTML显示
+      //       this.value = response.data;
+      //       this.editorContent = response.data;
+      //       this.$emit("subLoadUrlToHtml", response.data);
+      //       this.$emit("input", response.data);
+      //     })
+      //     .catch(() => {
+      //       this.value = "服务器数据加载失败,请重试!";
+      //     });
+      // }
+    },
+    // 设置编辑器内容
+    setContent(value) {
+      window.tinymce.get(this.tinymceId).setContent(value);
+    },
+    // 获取编辑器内容
+    getContent() {
+      window.tinymce.get(this.tinymceId).getContent();
+    },
+    // 图片上传成功后填充到富文本编辑器
+    async imageSuccess(urlList) {
+      try {
+        let imageTemplateList = "";
+        urlList.forEach((item) => {
+          const image = `<img style="max-width:100%;" src="${item}">`;
+          imageTemplateList = imageTemplateList + image;
+        });
+        window.tinymce.get(this.tinymceId).insertContent(imageTemplateList);
+        this.$message({
+          message: "上传成功!",
+          type: "success",
+        });
+      } catch (error) {
+        console.log(error);
+        this.$message({
+          message: error,
+          type: "error",
+        });
+      }
+    },
+    // 编辑器内容上传到cos,调用返回url
+    // async content2Url() {
+    //   try {
+    //     const res = await uploadHtml(this.editorContent);
+    //     return res;
+    //   } catch (error) {
+    //     this.$message({
+    //       message: error.data.message,
+    //       type: "error",
+    //     });
+    //   }
+    // },
+  },
+};
+</script>
+<style lang="less" scoped>
+#tinymce {
+  background-color: blue;
+  p {
+    margin: 0;
+  }
+}
+.tinymce-container {
+  position: relative;
+}
+.tinymce-container{
+    /deep/ .mce-fullscreen {
+        z-index: 10000;
+    }
+}
+.tinymce-textarea {
+  visibility: hidden;
+  z-index: -1;
+}
+.editor-custom-btn-container {
+  position: absolute;
+  right: 4px;
+  top: 4px;
+  /*z-index: 2005;*/
+}
+.fullscreen .editor-custom-btn-container {
+  z-index: 10000;
+  position: fixed;
+}
+.editor-upload-btn {
+  display: inline-block;
+}
+// 隐藏底部logo栏
+.mce-edit-area + .mce-statusbar {
+  opacity: 0;
+  height: 0;
+}
+</style>

+ 5 - 0
backstage/src/components/Tinymce/plugins.js

@@ -0,0 +1,5 @@
+const plugins = [
+    'advlist anchor autolink autosave code indent2em lineheight codesample directionality emoticons fullscreen hr image imagetools importcss insertdatetime link lists media nonbreaking noneditable pagebreak  preview print save searchreplace tabfocus table template  textpattern visualblocks visualchars wordcount paste axupimgs'
+  ]
+  
+export default plugins

+ 3 - 0
backstage/src/components/Tinymce/toolbar.js

@@ -0,0 +1,3 @@
+const toolbar = ['formatselect fontsizeselect forecolor backcolor bold italic underline strikethrough alignleft aligncenter alignright outdent indent  indent2em lineheight  removeformat  hr undo redo image media axupimgs']
+
+export default toolbar

+ 58 - 0
backstage/src/components/crumbs/index.vue

@@ -0,0 +1,58 @@
+<!--  -->
+<template>
+<div class='crumbs card'><span v-for="(item,i) in data" :key='i'>{{item.name + (i === data.length - 1?'':' > ')}}</span></div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+
+export default {
+// import引入的组件需要注入到对象中才能使用
+  props: {
+    data: {
+      default: () => [],
+      type: Array
+    }
+  },
+  components: {},
+  data () {
+    // 这里存放数据
+    return {
+
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {},
+  // 监控data中的数据变化
+  watch: {},
+  // 方法集合
+  methods: {
+
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created () {
+
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {
+
+  },
+  beforeCreate () {}, // 生命周期 - 创建之前
+  beforeMount () {}, // 生命周期 - 挂载之前
+  beforeUpdate () {}, // 生命周期 - 更新之前
+  updated () {}, // 生命周期 - 更新之后
+  beforeDestroy () {}, // 生命周期 - 销毁之前
+  destroyed () {}, // 生命周期 - 销毁完成
+  activated () {} // 如果页面有keep-alive缓存功能,这个函数会触发
+}
+</script>
+<style scoped>
+.crumbs{
+  color: #532F1C!important;
+  padding: 20px;
+  font-size: 1.25rem;
+  font-weight: bold;
+}
+
+</style>

+ 79 - 0
backstage/src/components/main-top/index.vue

@@ -0,0 +1,79 @@
+<!--  -->
+<template>
+<div class='main-top'>
+  <crumbs :data="crumb" />
+  <div v-if="status" class="edit-status">{{`(${statusStr[status]})`}}</div>
+  <slot name='con'></slot>
+</div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+import Crumbs from '../crumbs'
+
+export default {
+  
+// import引入的组件需要注入到对象中才能使用
+  props: {
+    crumb: {
+      default: () => [],
+      type: Array
+    },
+    status:{
+      default: 0,
+      type: Number
+    }
+  },
+  components: {Crumbs},
+  data () {
+    // 这里存放数据
+    return {
+
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {},
+  // 监控data中的数据变化
+  watch: {},
+  // 方法集合
+  methods: {
+
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created () {
+
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {
+
+  },
+  beforeCreate () {}, // 生命周期 - 创建之前
+  beforeMount () {}, // 生命周期 - 挂载之前
+  beforeUpdate () {}, // 生命周期 - 更新之前
+  updated () {}, // 生命周期 - 更新之后
+  beforeDestroy () {}, // 生命周期 - 销毁之前
+  destroyed () {}, // 生命周期 - 销毁完成
+  activated () {} // 如果页面有keep-alive缓存功能,这个函数会触发
+}
+</script>
+<style lang="less" scoped>
+.main-top{
+  background:#fff;
+  width: 100%;
+  margin-bottom: 20px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding-right: 20px;
+  position: relative;
+  .edit-status{
+    position: absolute;
+    transform: translate(-50%,-50%);
+    left: 50%;
+    top: 50%;
+    letter-spacing: 3px;
+  }
+}
+
+</style>

+ 60 - 0
backstage/src/configue/base.js

@@ -0,0 +1,60 @@
+const base = {
+  reg: {
+    idCard: /^\d{6}(18|19|20)?\d{2}(0[1-9]|1[012])(0[1-9]|[12]\d|3[01])\d{3}(\d|[xX])$/,
+    phone: /^1(?:3\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8\d|9\d)\d{8}$/,
+    email: /^([a-zA-Z0-9]+[_|_|.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|_|.]?)*[a-zA-Z0-9]+\.[a-zA-Z]{2,3}$/
+  },
+  isImg: function (url) {
+    var temp = []
+    temp = url.toString().split('.')
+    if (temp[temp.length - 1].toLowerCase() === 'png' || temp[temp.length - 1].toLowerCase() === 'jpg') {
+      return true
+    } else {
+      return false
+    }
+  },
+  timestampToTime: function (timestamp) {
+    var date = new Date(Number(timestamp))// 时间戳为10位需*1000,时间戳为13位的话不需乘1000
+
+    var Y = date.getFullYear() + '-'
+
+    var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
+
+    var D = date.getDate() + ' '
+
+    // var h = date.getHours() + ':'
+
+    // var m = date.getMinutes() + ':'
+
+    // var s = date.getSeconds()
+
+    return Y + M + D
+  },
+  dateFormat: function (fmt, that) {
+    var o = {
+      'M+': that.getMonth() + 1, // 月份
+      'd+': that.getDate(), // 日
+      'h+': that.getHours(), // 小时
+      'm+': that.getMinutes(), // 分
+      's+': that.getSeconds(), // 秒
+      'q+': Math.floor((that.getMonth() + 3) / 3), // 季度
+      S: that.getMilliseconds() // 毫秒
+    }
+    if (/(y+)/.test(fmt)) {
+      fmt = fmt.replace(
+        RegExp.$1,
+        (that.getFullYear() + '').substr(4 - RegExp.$1.length)
+      )
+    }
+    for (var k in o) {
+      if (new RegExp('(' + k + ')').test(fmt)) {
+        fmt = fmt.replace(
+          RegExp.$1,
+          RegExp.$1.length === 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length)
+        )
+      }
+    }
+    return fmt
+  }
+}
+export { base }

+ 3 - 0
backstage/src/configue/bus.js

@@ -0,0 +1,3 @@
+import Vue from 'vue'
+
+export default new Vue()

+ 137 - 0
backstage/src/configue/http.js

@@ -0,0 +1,137 @@
+import axios from 'axios'
+// import qs from 'qs'
+import Vue from 'vue'
+import router from '../router'
+
+const vue = new Vue()
+
+var isProduction = process.env.NODE_ENV === 'production'
+let loading = ''
+// 配置请求域名
+
+// const serverName = isProduction ? '' : 'http://192.168.0.135:8009'
+const serverName = isProduction ? '' : 'http://8.135.106.227:8009/'
+// http://192.168.0.44:8100/web/index.html#/
+
+const serverLocation = window.location.hostname
+
+axios.defaults.baseURL = serverName
+axios.defaults.headers['X-Requested-with'] = 'XMLHttpRequest'
+axios.defaults.headers['token'] =  window.localStorage.getItem('token')
+
+const expectUrls = ['/manage/file/upload']
+
+axios.interceptors.request.use(function (config) {
+  loading = vue.$loading({
+    lock: true,
+    text: '加载中',
+    spinner: 'el-icon-loading',
+    background: 'rgba(0, 0, 0, 0.7)'
+  });
+  for (let i = 0; i < expectUrls.length; i++) {
+    const element = expectUrls[i];
+    if(element == config.url){
+      return config
+    }
+  }
+  if (config.method === 'post') {
+    config.data = {
+      ...config.data,
+      rnd: Math.random()
+    }
+    // config.data = qs.stringify(config.data)
+  } else if (config.method === 'get') {
+    config.params = {
+      rnd: Math.random(),
+      ...config.params
+    }
+    // config.params = qs.stringify(config.params)
+  }
+ 
+  return config
+}, function (error) {
+  // 对请求错误做些什么
+  return Promise.reject(error)
+})
+
+// 配置response拦截器
+axios.interceptors.response.use(
+  response => {
+    let data = response.data
+    let code = Number(response.data.code)
+    loading.close()
+    switch (code) {
+      case -1:
+        break
+        case 4500:
+          vue.$alert('没有获得授权,请联系系统管理员', '提示', {
+            confirmButtonText: '确定',
+            callback: function () {
+            }
+          })
+          break
+        case 5001:
+
+          if (window.localStorage.getItem('token')) {
+            window.localStorage.setItem('token', '')
+            vue.$alert('登录状态失效,请重新登录', '提示', {
+              confirmButtonText: '确定',
+              callback: function () {
+                window.localStorage.setItem('userInfo', '')
+                router.push('/login')
+              }
+            })
+          }
+          else{
+            vue.$alert('登录状态失效,请重新登录', '提示', {
+              confirmButtonText: '确定',
+              callback: function () {
+                window.localStorage.setItem('userInfo', '')
+                router.push('/login')
+              }
+            })
+          }
+          break
+        case 5002:
+          if (window.localStorage.getItem('token')) {
+            window.localStorage.setItem('token', '')
+            vue.$alert('登录状态失效,请重新登录', '提示', {
+              confirmButtonText: '确定',
+              callback: function () {
+                window.localStorage.setItem('userInfo', '')
+                router.push('/login')
+              }
+            })
+          }
+          break
+        case 500:
+          vue.$alert(data.message || '服务器错误', '提示', {
+            confirmButtonText: '确定',
+            callback: function () {
+            }
+          })
+          break
+      case 0:
+        break
+    }
+    // tryHideFullScreenLoading()
+    return data
+  },
+  error => {
+    loading.close()
+    window.localStorage.setItem('userInfo', '')
+    // router.push('/login')
+    if (error.response) {
+      let data = error.response.data
+      vue.$alert(data.msg||'服务器错误,请稍后再试', '提示', {
+        confirmButtonText: '确定',
+        callback: function () {
+          window.localStorage.setItem('userInfo', '')
+          router.push('/login')
+        }
+      })
+    }
+    return Promise.reject(error)
+  }
+)
+export { serverName, axios, serverLocation }

+ 130 - 0
backstage/src/main.js

@@ -0,0 +1,130 @@
+import Vue from 'vue'
+import App from './App.vue'
+import router from './router'
+import ElementUI from 'element-ui';
+import {base} from '@/configue/base'
+import 'element-ui/lib/theme-chalk/index.css';
+import {axios, serverName} from './configue/http'
+import '../theme/index.css'
+import {isImage,isTypeBySend} from "@/util/index.js";
+
+Vue.use(ElementUI)
+Vue.config.productionTip = false
+
+Vue.prototype.$base = base
+Vue.prototype.$bus = new Vue()
+Vue.prototype.$http = axios
+Vue.prototype.isImage = isImage
+Vue.prototype.isTypeBySend = isTypeBySend
+
+Vue.prototype.MAXLENGTH = 200
+Vue.prototype.FONTLENGTH = 25
+Vue.prototype.PAGESIZES = [25,50,75,100]
+Vue.prototype.loading = {
+  close(){
+
+  }
+}
+
+
+Vue.prototype.loadOption = {
+  lock: true,
+  text: '上传中',
+  spinner: 'el-icon-loading',
+  background: 'rgba(0, 0, 0, 0.7)'
+}
+
+
+Vue.prototype.statusStr = {
+  1: "草稿中",
+  2: "待审核",
+  3: "审核不通过",
+  4: "审核通过"
+};
+
+
+let tmpYear = []
+for (let i = 2000; i <= 2050; i++) {
+  tmpYear.push({
+    id:i,
+    name:i+'年'
+  })
+}
+
+Vue.prototype.yearStr = [
+  {
+    id:1001,
+    name:'革命战争时期'
+  },
+  {
+    id:1002,
+    name:'和平建设时期'
+  },
+  {
+    id:1003,
+    name:'军区联勤时期'
+  }
+].concat(tmpYear);
+
+
+Vue.prototype.typeStr = {
+  news: "新闻",
+  notice: "公告"
+};
+
+
+Vue.prototype.statusArr = [
+  {
+    id:1,
+    name:'草稿中'
+  },
+  {
+    id:2,
+    name:'待审核'
+  },
+  {
+    id:3,
+    name:'审核不通过'
+  },
+  {
+    id:4,
+    name:'审核通过'
+  }
+]
+
+Vue.prototype.sysRole=[
+  {
+    name:'系统管理员',
+    id:'sys_admin'
+  },
+  {
+    name:'普通用户',
+    id:'sys_normal'
+  },
+]
+
+Vue.prototype.collectType = {
+  1:'专题图库',
+  2:'视频文件',
+  3:'模型文件'
+};
+
+
+Vue.prototype.juese = {
+  sys_admin: "系统管理员",
+  sys_high: "高级管理员",
+  sys_normal: "普通用户",
+  sys_visitor: "游客",
+};
+
+Vue.prototype.sex = {
+  0: "男",
+  1: "女"
+};
+
+Vue.prototype.$serverName = serverName// 挂载到Vue实例上面
+
+new Vue({
+  router,
+  render: h => h(App)
+}).$mount('#app')

+ 679 - 0
backstage/src/pages/content/Carousel.vue

@@ -0,0 +1,679 @@
+<!--  -->
+<template>
+  <div>
+    <main-top :crumb="crumbData">
+      <div slot="con">
+        <div style="display:flex; align-items: center;">
+            <el-button style="margin-left:20px" type="primary" @click="show('', '')">上传党史军史</el-button>
+        </div>
+      </div>
+    </main-top>
+    <div class="table-interface">
+      <div class="top-body">
+        <div class="info-top">
+          <div class="info-left">
+            <span>党史军史名称:</span>
+            <el-input class="elInput" v-model="inputKey" placeholder="请输入党史军史名称"></el-input>
+            <span style="margin-left:1.25rem;">新建时间:</span>
+            <el-date-picker
+              v-model="time"
+              type="daterange"
+              range-separator="-"
+              value-format="yyyy-MM-dd HH:mm:ss"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期">
+            </el-date-picker>
+            <el-button style="margin-left:1.25rem;" type="primary" @click="getInformation">查询</el-button>
+            <el-button @click="reset">重置</el-button>
+          </div>
+          <div class="info-right">
+          </div>
+        </div>
+        <el-table :data="tableData" height="61vh" class="collection-con" 
+        :tree-props="{children: 'children'}"
+        row-key="id"
+        style="width: 100%">
+          <el-table-column
+            v-for="(item, idx) in data"
+            :key="idx"
+            :sortable="item.prop==='viewCount'"
+            :prop="item.prop"
+            :label="item.label"
+          >
+            <template slot-scope="scope" >
+              <el-image 
+                 v-if="item.type === 'image'"
+                style="width:auto;height:125px;"
+                :src="scope.row[item.prop]">
+                <div slot="error" class="image-slot">
+                  <i class="el-icon-picture-outline"></i>
+                </div>
+              </el-image>
+              <span v-else v-html="scope.row[item.prop]||'-'"></span>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作">
+            <template slot-scope="scope" >
+                <span class="o-span" v-if="($role=='sys_admin'||$role=='sys_high')" @click="show(scope.row,'edit')">编辑</span>
+                <span class="o-span" v-if="($role=='sys_admin'||$role=='sys_high')" @click="del(scope.row)">删除</span>
+            </template>
+          </el-table-column>
+        </el-table>
+        <div class="e-pagination">
+          <el-pagination
+            @current-change="handleCurrentChange"
+            :current-page.sync="currentPage"
+            @size-change="handleSizeChange"
+            :page-size="size"
+            :page-sizes="PAGESIZES"
+            layout="total, sizes, prev, pager, next, jumper"
+            :total="total"
+          ></el-pagination>
+        </div>
+      </div>
+    </div>
+    <el-dialog :title="editTitle" :visible.sync="dialogFormVisible" width="50%">
+      <div class="add-con">
+        <div class="add-left">
+          <el-form :model="form" ref="knowledgeform">
+            <el-form-item
+              label="党史军史名称:"
+              :label-width="formLabelWidth"
+              prop="name"
+              :rules="{
+                required: true, message: '党史军史名称不能为空', trigger: 'blur'
+              }"
+            >
+              <el-input
+                v-model="form.name"
+                placeholder="请输入党史军史名称"
+                autocomplete="off"
+                :maxlength="25"
+                show-word-limit
+              ></el-input>
+            </el-form-item>
+
+            <el-form-item
+              label="知识链接:"
+              :label-width="formLabelWidth"
+              prop="knowledgeUrl"
+              :rules="{
+                required: true, message: '知识链接不能为空', trigger: 'blur'
+              }"
+            >
+              <el-input
+                v-model="form.knowledgeUrl"
+                placeholder="请输入https:// 链接"
+                autocomplete="off"
+              >
+            </el-input>
+             
+            </el-form-item>
+
+            <el-form-item :label="'链接图标:'"
+              prop="thumb"
+              :label-width="formLabelWidth"
+              :rules="{
+                required: true, message: `链接图标不能为空`, trigger: 'change'
+              }"
+             required>
+              <el-upload
+                class="upload-demo"
+                :action="uploadUrl"
+                :headers="{ token }"
+                :on-success="handleFileSuccess"
+                :on-error="handleError"
+                :before-upload="beforefileUpload"
+                :show-file-list="false"
+              >
+                <el-button size="small" type="primary">点击上传</el-button>
+              </el-upload>
+              <img v-if="form.thumb" style="max-width:300px;" :src="form.thumb" alt="">
+              <p style="color:#999;line-height:1;margin-top:20px">格式要求:</p>
+              <p style="color:#999;" v-for="(item,i) in desc[1]" :key="i"> 
+                {{item}}
+              </p>
+            </el-form-item>
+          </el-form>
+        </div>
+      </div>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="save">保 存</el-button>
+        <el-button @click="dialogFormVisible = false">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+
+import MainTop from "@/components/main-top";
+
+const crumbData = [
+  {
+    name: "党史军史管理",
+    id: 4,
+  },
+];
+
+
+let data = [
+  {
+    prop: "idx",
+    label: "序列"
+  },
+  {
+    prop: "name",
+    label: "党史军史名称"
+  },
+  {
+    prop: "knowledgeUrl",
+    label: "知识链接"
+  },
+ 
+  {
+    prop: "thumb",
+    label: "链接图标",
+    type:'image'
+  },
+  {
+    prop: "submitName",
+    label: "创建人"
+  },
+  {
+    prop: "createTime",
+    label: "创建时间"
+  }
+];
+
+let desc = {
+  1:['1、支持png和jpg的图片格式','2、最大可支持100M','3、建议分辨率:100*50']
+}
+
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    MainTop,
+  },
+  data() {
+    return {
+      desc,
+      data,
+      crumbData,
+      time: "",
+      tableData: [],
+      inputKey: "",
+      currentPage: 1,
+      size: 25,
+      total: 0,
+      loading: false,
+      type: 1,
+      form: {
+        "id": "",
+        "knowledgeUrl": "",
+        "name": "",
+        "thumb": ""
+      },
+      dialogFormVisible: false,
+      showType:'',
+      formLabelWidth: "150px",
+      token: window.localStorage.getItem("token"),
+      uploadUrl: `${this.$serverName}manage/file/upload`,
+      uploadFlieUrl: `${this.$serverName}manage/fodder/uploadOriginal`
+    };
+  },
+  computed:{
+      editTitle(){
+        return this.showType ?'编辑党史军史':'新增党史军史'
+      },
+  },
+  watch: {
+    currentPage() {
+      this.refresh();
+    },
+    size() {
+      this.refresh();
+    },
+    type() {
+      this.refresh();
+    }
+  },
+  mounted() {
+    this.refresh()
+  },
+
+  methods: {
+    reset(){
+      this.inputKey=''
+      this.time=''
+    },
+  
+    handleError(){
+      this.$alert("上传失败,请稍后再试", "提示", {
+        confirmButtonText: "确定",
+        callback: () => {
+          this.loading.close()
+        },
+      });
+    },
+    handleFileSuccess(res) {
+      this.loading.close()
+      let { data } = res;
+      this.form.thumb = data.urlPath;
+    },
+    beforefileUpload(file){
+      let typeArr = ['.png','jpeg','jpg']
+      let type = this.isTypeBySend(file.name,typeArr);
+      if (!type) {
+        this.$message.error('只允许指定文件格式的文件');
+        return type
+      }
+      
+      let size = 100
+      const isLt = file.size / 1024 / 1024 < size;
+
+      if (!isLt) {
+        this.$message.error(`上传文件大小不能超过 ${size}MB!`);
+        return isLt;
+      }
+      this.loading = this.$loading(this.loadOption)
+    },
+    beforeAvatarUpload(file) {
+      let type = this.isImage(file.name);
+      if (!type) {
+        this.$message.error('只允许上传图片');
+        return type
+      }
+      const isLt2M = file.size / 1024 / 1024 < 20;
+
+      if (!isLt2M) {
+        this.$message.error("上传头像图片大小不能超过 20MB!");
+        return isLt2M;
+      }
+      this.loading = this.$loading(this.loadOption)
+    },
+
+    handleChange(item) {
+      console.log(item);
+    },
+
+
+    async getDetail(id) {
+      let result = await this.$http({
+        method: "get",
+        headers: {
+          token: window.localStorage.getItem("token"),
+        },
+        url: `/manage/knowledge/detail/${id}`,
+      });
+
+      this.form = result.data;
+    },
+  
+
+    show(item = "", type) {
+      this.dialogFormVisible = true;
+
+      this.showType = type;
+      this.form = {
+        id: "",
+        knowledgeUrl: "",
+        name: "",
+        thumb: ""
+      };
+
+      if (type === "edit") {
+        this.getDetail(item.id)
+      }
+    },
+    del(item) {
+      this.$confirm("删除后,信息将无法恢复,是否继续?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      })
+        .then(() => {
+          this.$http
+            .get(`/manage/knowledge/remove/${item.id}`, {
+              headers: {
+                token: window.localStorage.getItem("token"),
+              },
+            })
+            .then((res) => {
+              if (res.code === 0) {
+                this.$alert("删除成功", "提示", {
+                  confirmButtonText: "确定",
+                  callback: () => {
+                    this.refresh();
+                  },
+                });
+              } else {
+                this.$notify.error({
+                  title: "错误",
+                  message: res.msg,
+                });
+              }
+            });
+        })
+        .catch(() => {
+          this.$message({
+            type: "info",
+            message: "已取消删除",
+          });
+        });
+    },
+    async save() {
+      this.$refs["knowledgeform"].validate(async (valid) => {
+        if (valid) {
+          let {
+            id,
+            knowledgeUrl,
+            name,
+            thumb
+          } = this.form;
+          
+          knowledgeUrl = knowledgeUrl.substr(0,8).toLowerCase()=="https://"?knowledgeUrl:("https://"+knowledgeUrl);
+          let params = {
+            id,
+            knowledgeUrl,
+            name,
+            thumb
+          };
+          let result = await this.$http({
+            method: "post",
+            data: params,
+            headers: {
+              token: window.localStorage.getItem("token"),
+            },
+            url: `/manage/knowledge/save`,
+          });
+
+          if (result.code === 0) {
+            this.$alert("保存成功", "提示", {
+              confirmButtonText: "确定",
+              callback: () => {
+                this.getInformation();
+              },
+            });
+          } else if (result.code === 60005) {
+            return
+          } else {
+            this.$notify.error({
+              title: "错误",
+              message: result.msg,
+            });
+          }
+        }
+      });
+    },
+
+    refresh() {
+      this.loading = true;
+      this.getInformation();
+      this.loading = false;
+    },
+    handleCurrentChange(val) {
+      this.currentPage = val;
+    },
+    handleSizeChange(val) {
+      this.size = val;
+    },
+    async getInformation() {
+      this.dialogFormVisible = false;
+      let params = {
+        pageNum: this.currentPage,
+        pageSize: this.size,
+        searchKey: this.inputKey,
+        type: this.type,
+        startTime: this.time? (this.time[0].split(' ')[0] + ' 00:00:00') : '',
+        endTime: this.time? (this.time[1].split(' ')[0] + ' 23:59:59') : ''
+      };
+
+      let result = await this.$http({
+        method: "post",
+        data: params,
+        headers: {
+          token: window.localStorage.getItem("token"),
+        },
+        url: "/manage/knowledge/list",
+      });
+
+      if (result.code !== 0) {
+        return;
+      }
+      this.tableData = result.data.list;
+      this.total = result.data.total;
+      this.tableData.forEach((item, i) => {
+        item["idx"] = i + 1;
+      });
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.top-body {
+  border-top: 0.0625rem solid #e6e6e6;
+  line-height: 1.5;
+  padding: 0 1.25rem 1.25rem;
+  align-items: center;
+  box-sizing: border-box;
+  background: #fff;
+  margin: 1rem 0;
+}
+
+.top-body .top-con {
+  font-weight: bold;
+}
+.table-title {
+  padding: 1rem 1rem 1rem 0;
+  font-size: 1rem;
+  color: #2d2d2d;
+  border-bottom: 0.0625rem solid #ccc;
+  display: flex;
+  justify-content: space-between;
+}
+
+.more {
+  color: #ec652d;
+  cursor: pointer;
+}
+
+.top-right {
+  background-color: #ec652d;
+  height: 3.5625rem;
+  line-height: 3.5625rem;
+  color: #fff;
+  padding: 0 1.875rem;
+  border-radius: 0.3125rem;
+  font-size: 1.125rem;
+  font-weight: bold;
+}
+
+.search-body {
+  background-color: #fff;
+  margin: 1.25rem 0;
+}
+
+.interface-table {
+  height: calc(100% - 17.1875rem);
+  overflow-x: hidden;
+  background-color: #fff;
+  padding: 0 1.125rem 2.375rem;
+  box-sizing: border-box;
+}
+
+.zan-con {
+  display: flex;
+  width: 100%;
+  justify-content: space-around;
+  margin-top: 1.5rem;
+  text-align: center;
+  font-size: 1.125rem;
+}
+
+.zan-con .line {
+  height: 8rem;
+  width: 0.0625rem;
+  background: #ccc;
+}
+
+.zan-con .zan-contain {
+  display: flex;
+  justify-content: space-between;
+  flex-direction: column;
+}
+
+.zan-con .zan-contain p {
+  font-size: 2.25rem;
+}
+
+.zan-con .zan-contain p:first-child {
+  font-size: 0.875rem;
+  line-height: 1.5;
+}
+
+.zan-sub {
+  text-align: right;
+  margin-top: 1.25rem;
+  color: #a5a5a5;
+  font-size: 0.875rem;
+}
+
+.table-interface {
+  height: calc(100% - 3rem);
+}
+
+.info-top {
+  padding: 1.25rem 0;
+  display: flex;
+  justify-content: space-between;
+  border-bottom: 0.0625rem #a5a5a5 solid;
+}
+
+.o-span {
+  cursor: pointer;
+  color: rgb(7, 152, 244);
+}
+
+.collection-con {
+  width: 100%;
+  overflow-x: hidden;
+  overflow-y: auto;
+  height: 60vh;
+}
+
+.collection-con ul {
+  display: flex;
+  flex-wrap: wrap;
+  margin-top: 1.25rem;
+}
+
+.collection-con ul li {
+  width: 24%;
+  margin-right: 1%;
+  cursor: pointer;
+  font-size: 0.8rem;
+  color: #2d2d2d;
+  margin-bottom: 1.25rem;
+}
+
+.collection-con ul .li-img {
+  position: relative;
+  height: 250px;
+  font-size: 0;
+}
+
+.collection-con ul .elchbox{
+  position: absolute;
+  left: 10px;
+  top: 5px;
+}
+
+
+
+
+
+.collection-con ul .li-img div span {
+  margin-right: 0.625rem;
+}
+
+.collection-con ul img {
+  width: 100%;
+}
+
+.collection-con {
+  li {
+    position: relative;
+    .li-txt {
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      background: #dedede;
+      font-size: 16px;
+      line-height: 2;
+    }
+    .oper {
+      position: absolute;
+      top: 10px;
+      right: 10px;
+      display: none;
+      > img {
+        width: 40px;
+        height: 40px;
+        background: #b63c25;
+        margin-left: 20px;
+        border-radius: 4px;
+        padding: 5px;
+      }
+    }
+    &:hover {
+      .oper {
+        display: block;
+      }
+    }
+  }
+}
+
+.add-con {
+  display: flex;
+  justify-content: space-between;
+}
+
+.add-left {
+  flex: 1;
+}
+
+.add-right {
+  flex: 1;
+  margin-left: 10%;
+}
+</style>
+
+<style>
+.avatar-uploader .el-upload {
+  border: 1px dashed #d9d9d9;
+  border-radius: 6px;
+  cursor: pointer;
+  position: relative;
+  overflow: hidden;
+}
+.avatar-uploader .el-upload:hover {
+  border-color: #409eff;
+}
+.avatar-uploader-icon {
+  font-size: 28px;
+  color: #8c939d;
+  width: 178px;
+  height: 178px;
+  line-height: 178px;
+  text-align: center;
+}
+.avatar {
+  width: 178px;
+  /* height: 178px; */
+  display: block;
+}
+</style>

+ 569 - 0
backstage/src/pages/content/Collection.vue

@@ -0,0 +1,569 @@
+<!--  -->
+<template>
+  <div>
+    <main-top :crumb="crumbData">
+      <div slot="con">
+        <el-button type="primary" @click="$router.push({name:'edit-collection',query:{type},params:{type:0,id:'none'}})">新增精品典藏</el-button>
+      </div>
+    </main-top>
+    <div class="table-interface">
+      <div class="con-left">
+        <el-menu :default-active="`1-${type}`" @select="handleSelect" class="el-menu-vertical-demo" :collapse="isCollapse">
+          <el-submenu index="1">
+            <template slot="title">
+              <i class="el-icon-menu"></i>
+              <span slot="title">精品典藏</span>
+            </template>
+            <el-menu-item v-for="(item,i) in collectionType" :key="i" :index="'1-'+item.id">{{item.name}}</el-menu-item>
+          </el-submenu>
+        </el-menu>
+        <div class="sousuo" @click="isCollapse=!isCollapse">
+          <i :class="isCollapse?'el-icon-arrow-right':'el-icon-arrow-left'"></i>
+        </div>
+      </div>
+      <div class="top-body">
+        <ul class="tab">
+          <li @click="status = 5" :class="{active:status !== 4}">待审核</li>
+          <li @click="status = 4" :class="{active:status === 4}">审核通过</li>
+        </ul>
+        <div class="info-top">
+          <div class="info-left">
+            <span>发布时间:</span>
+            <el-date-picker
+              v-model="time"
+              type="daterange"
+              range-separator="-"
+              value-format="yyyy-MM-dd HH:mm:ss"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期">
+            </el-date-picker>
+            <span style="margin-left:1.25rem;">状态:</span>
+            <el-select style="width:6.25rem;" v-model="status" placeholder="请选择">
+              <el-option label="全部" :value="5"></el-option>
+              <el-option v-for="(item,i) in statusArr" :key="i" :label="item.name" :value="item.id"></el-option>
+            </el-select>
+            <span style="margin-left:1.25rem;">标题关键字:</span>
+            <el-input class="elInput" v-model="inputKey" placeholder="请输入标题关键字搜索"></el-input>
+            <el-button type="primary" @click="getInformation">查询</el-button>
+            <el-button @click="reset">重置</el-button>
+          </div>
+          <div class="info-right">
+          </div>
+        </div>
+        <el-table :data="tableData" height="55vh" class="collection-con" style="width: 100%">
+          <el-table-column
+            v-for="(item, idx) in data"
+            :key="idx"
+            :sortable="item.prop==='viewCount'"
+            :prop="item.prop"
+            :width="item.prop === 'thumb'?250:'auto'"
+            :label="item.label"
+          >
+            <template slot-scope="scope" >
+              <el-switch
+                v-model="scope.row[item.prop]"
+                v-if="item.prop === 'display'"
+                v-show="status === 4"
+                :disabled="!($role=='sys_admin'||$role=='sys_high')"
+                @change="changeState(scope.row)"
+                inactive-color="#ccc">
+              </el-switch>
+              <el-image 
+                v-else-if="item.prop === 'thumb'"
+                style="width:auto;height:125px;"
+                :src="scope.row[item.prop]"
+                :preview-src-list="[scope.row[item.prop]]">
+                <div slot="error" class="image-slot">
+                  <i class="el-icon-picture-outline"></i>
+                </div>
+              </el-image>
+              <el-input type="number" class="icenter" v-else-if="(item.prop === 'sort')&&($role=='sys_admin'||$role=='sys_high')" @blur="handleSort(scope.row)" v-model="scope.row[item.prop]"></el-input>
+              <span v-else v-html="scope.row[item.prop]||'-'"></span>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作">
+            <template slot-scope="scope" >
+              <template v-if="status !== 4">
+                <template v-if="scope.row.statusStr === '待审核'">
+                  <span class="o-span" @click="goto(scope.row)" v-if="scope.row.statusStr === '待审核'">
+                    {{($role=='sys_admin'||$role=='sys_high') ? '审核':'查看'}}
+                  </span>
+                </template>
+                <template v-else>
+                  <span class="o-span" @click="goto(scope.row)">编辑</span>
+                  <span class="o-span" @click="del(scope.row)">删除</span>
+                </template>
+              </template>
+              <template v-else>
+                <span class="o-span" @click="goto(scope.row)">查看</span>
+                <span class="o-span" v-if="(!scope.row.display)&&($role=='sys_admin'||$role=='sys_high')" @click="del(scope.row)">删除</span>
+              </template>
+            </template>
+          </el-table-column>
+        </el-table>
+        <div class="e-pagination">
+          <el-pagination
+            @current-change="handleCurrentChange"
+            :current-page.sync="currentPage"
+            @size-change="handleSizeChange"
+            :page-size="size"
+            :page-sizes="PAGESIZES"
+            layout="total, sizes, prev, pager, next, jumper"
+            :total="total"
+          ></el-pagination>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+
+import MainTop from '@/components/main-top'
+import {changeState} from '@/util/commonfn.js'
+const crumbData = [
+  {
+    name: '精品典藏管理',
+    id: 4
+  }
+]
+
+let collectionType = [
+  {
+    id:3,
+    name:'实物模型'
+  },
+  {
+    id:1,
+    name:'专题图库'
+  },
+  {
+    id:2,
+    name:'视频档案'
+  }
+]
+
+
+
+let data = [
+  {
+    prop: "idx",
+    label: "编号"
+  },
+  {
+    prop: "title",
+    label: "标题"
+  },
+  {
+    prop: "thumb",
+    label: "封面图片"
+  },
+  {
+    prop: "sort",
+    label: "排序"
+  },
+  {
+    prop: "viewCount",
+    label: "阅读"
+  },
+  {
+    prop: "submitName",
+    label: "发布人"
+  },
+  {
+    prop: "createTime",
+    label: "发布时间"
+  },
+  {
+    prop: "statusStr",
+    label: "所属状态"
+  }
+];
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    MainTop,
+  },
+  data () {
+    return {
+      isCollapse:false,
+      crumbData,
+      time:'',
+      tableData: [],
+      inputKey: '',
+      currentPage: 1,
+      size: 25,
+      total: 0,
+      collectionType,
+      loading: false,
+      status:5,
+      type:this.$route.params.type
+    }
+  },
+  computed:{
+    data(){
+      data.pop()
+      let item = this.status === 4?{
+        prop: "display",
+        label: "是否显示"
+      }:{
+        prop: "statusStr",
+        label: "所属状态"
+      }
+      data.push(item)
+      return data
+    }
+  },
+  watch: {
+    currentPage () {
+      this.refresh()
+    },
+    size () {
+      this.refresh()
+    },
+    status () {
+      this.refresh()
+    },
+    type(){
+      this.refresh()
+    }
+  },
+  mounted () {
+    this.refresh()
+  },
+ 
+  methods: {
+    handleSort(item=""){
+      if(!item||!item.sort)return
+      this.$http.get(`/manage/goods/sort/${item.id}/${item.sort}`, {
+        headers: {
+          token: window.localStorage.getItem("token"),
+        },
+      })
+      .then((res) => {
+        if (res.code === 0) {
+          // this.refresh();
+        } else {
+          this.$notify.error({
+            title: "错误",
+            message: res.msg
+          });
+        }
+      });
+    },
+    changeState(item) {
+      changeState(item,'goods',()=>{
+          this.refresh();
+      })
+    },
+    handleSelect(index){
+      this.type = Number(index.substr(-1))
+      this.$router.push({
+        name:'collection',
+        params:{
+          type:this.type
+        }
+      })
+      // this.refresh()
+    },
+    reset(){
+      this.inputKey=''
+      this.status=5
+    },
+    del (item) {
+      let ids = item.id
+      this.$confirm('删除后,信息将无法恢复,是否继续?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        this.$http.get(`/manage/goods/remove/${ids}`,{headers: {
+          token: window.localStorage.getItem('token')
+        }}).then(res => {
+          if (res.code === 0) {
+            this.$alert('删除成功', '提示', {
+              confirmButtonText: '确定',
+              callback: () => {
+                this.refresh()
+              }
+            })
+          } else {
+            this.$notify.error({
+              title: '错误',
+              message: res.msg
+            })
+          }
+        })
+      }).catch(() => {
+        this.$message({
+          type: 'info',
+          message: '已取消删除'
+        })
+      })
+    },
+    
+    gotoShow (item) {
+      this.$router.push({ name: 'show-collection', params: { id: item.id } })
+    },
+
+    goto (item) {
+      this.$router.push({ name: 'edit-collection', params: { type: 1,id:item.id } })
+    },
+ 
+    refresh () {
+      this.loading = true
+      this.getInformation()
+      this.loading = false
+    },
+    handleCurrentChange (val) {
+      this.currentPage = val
+    },
+    handleSizeChange(val){
+      this.size = val
+    },
+    async getInformation () {
+      let params = {
+        pageNum:this.currentPage,
+        pageSize: this.size,
+        searchKey: this.inputKey,
+        startTime: this.time? (this.time[0].split(' ')[0] + ' 00:00:00') : '',
+        endTime: this.time? (this.time[1].split(' ')[0] + ' 23:59:59') : '',
+        status: this.status,
+        type:this.type
+      }
+
+      let result = await this.$http({
+        method: 'post',
+        data: params,
+        headers: {
+          token: window.localStorage.getItem('token')
+        },
+        url: '/manage/goods/list'
+      })
+
+      if (result.code !== 0) {
+        return
+      }
+      this.tableData = result.data.list
+      this.total = result.data.total
+      this.tableData.forEach((item, i) => {
+        item["idx"] = i + 1;
+        item["typeStr"] = this.typeStr[item.type];
+        item["statusStr"] = this.statusStr[item.status];
+        item["createTime"] = item["createTime"].split(' ')[0] + '<br/>' +item["createTime"].split(' ')[1]
+        item["display"] = Boolean(item["display"]);
+      });
+    }
+  }
+}
+</script>
+
+
+<style lang="less" scoped>
+.top-body{
+  border-top: .0625rem solid #e6e6e6;
+  line-height: 1.5;
+  padding: 0 1.25rem 1.25rem;
+  align-items: center;
+  box-sizing: border-box;
+  background: #fff;
+  margin: 1rem 0;
+  width: 90%;
+}
+
+.top-body .top-con{
+  font-weight: bold;
+}
+.table-title{
+  padding: 1rem 1rem 1rem 0 ;
+  font-size: 1rem;
+  color: #2d2d2d;
+  border-bottom: .0625rem solid #ccc;
+  display: flex;
+  justify-content: space-between;
+}
+
+.more{
+  color: #ec652d;
+  cursor: pointer;
+}
+
+.top-right{
+  background-color: #ec652d;
+  height: 3.5625rem;
+  line-height: 3.5625rem;
+  color: #fff;
+  padding: 0 1.875rem;
+  border-radius: .3125rem;
+  font-size: 1.125rem;
+  font-weight: bold;
+}
+
+.search-body{
+  background-color: #fff;
+  margin: 1.25rem 0;
+}
+
+.interface-table{
+  height: calc(100% - 17.1875rem);
+  overflow-x: hidden;
+  background-color: #fff;
+  padding: 0 1.125rem 2.375rem;
+  box-sizing: border-box;
+}
+
+.zan-con{
+  display: flex;
+  width: 100%;
+  justify-content: space-around;
+  margin-top: 1.5rem;
+  text-align: center;
+  font-size: 1.125rem;
+}
+
+.zan-con .line{
+  height: 8rem;
+  width: .0625rem;
+  background: #ccc;
+}
+
+.zan-con .zan-contain{
+  display: flex;
+  justify-content: space-between;
+  flex-direction: column;
+}
+
+.zan-con .zan-contain p{
+  font-size: 2.25rem;
+}
+
+.zan-con .zan-contain p:first-child{
+  font-size: .875rem;
+  line-height: 1.5;
+}
+
+.zan-sub{
+  text-align: right;
+  margin-top: 1.25rem;
+  color: #a5a5a5;
+  font-size: .875rem;
+}
+
+.table-interface{
+  height: calc(100% - 3rem);
+  display: flex;
+  justify-content: space-between;
+  background-color: #fff;
+  .con-left{
+    border-right: solid 1px #e5e5e5;
+    position: relative;
+    margin-right: 30px;
+    .sousuo{
+      position: absolute;
+      top: 50%;
+      transform: translateY(-50%);
+      right: -30px;
+      cursor: pointer;
+    }
+  }
+}
+
+
+.info-top{
+  padding: 1.25rem 0;
+  display: flex;
+  justify-content: space-between;
+  border-bottom: .0625rem #a5a5a5 solid;
+}
+
+.o-span{
+  cursor: pointer;
+  color: rgb(7, 152, 244);
+}
+
+.collection-con{
+  width: 100%;
+  overflow-x: hidden;
+  overflow-y: auto;
+  height: 55vh;
+}
+
+
+
+.collection-con ul{
+  display: flex;
+  flex-wrap: wrap;
+  margin-top: 1.25rem;
+}
+
+.collection-con ul li {
+  width: 24%;
+  margin-right: 1%;
+  cursor: pointer;
+  font-size: .8rem;
+  color: #2d2d2d;
+  margin-bottom: 1.25rem;
+}
+
+.collection-con ul .li-img {
+  position: relative;
+  
+}
+.collection-con ul .li-img div{
+  position: absolute;
+  left: .625rem;
+  bottom: .625rem;
+  color: #fff;
+}
+
+.collection-con ul .li-img div span{
+  margin-right: .625rem;
+}
+
+
+.collection-con ul img {
+  width: 100%;
+}
+
+.collection-con{
+  .li-txt,.li-name{
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+  }
+  .li-name{
+    margin-top: .5rem;
+    div{
+      font-size: 1rem;
+      font-weight: bold;
+      color: #532F1C;
+      &:last-of-type{
+        color: #707070;
+        font-size: .875rem;
+        font-weight: normal;
+        span{
+          margin-left: .30rem;
+          &:last-of-type{
+            &:hover{
+              color: #409EFF;
+            }
+          }
+          &:hover{
+            color: #c56351;
+          }
+        }
+      }
+    }
+  }
+}
+
+.el-menu-vertical-demo:not(.el-menu--collapse) {
+  width: 200px;
+  min-height: 400px;
+}
+.el-menu{
+  border-right: none!important;
+}
+</style>

+ 424 - 0
backstage/src/pages/content/Comment.vue

@@ -0,0 +1,424 @@
+<!--  -->
+<template>
+  <div>
+    <main-top :crumb="crumbData">
+    </main-top>
+    <div class="table-interface">
+      <div class="top-body">
+        <div class="info-top">
+          <div class="info-left">
+            <span>发布时间:</span>
+            <el-date-picker
+              v-model="time"
+              type="daterange"
+              range-separator="-"
+              value-format="yyyy-MM-dd HH:mm:ss"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期">
+            </el-date-picker>
+            <span style="margin-left:1.25rem;">评论内容关键字:</span>
+            <el-input class="elInput" v-model="inputKey" placeholder="请输入评论内容关键字搜索"></el-input>
+            <el-button type="primary" @click="getInformation">查询</el-button>
+            <el-button @click="reset">重置</el-button>
+          </div>
+          <div class="info-right">
+          </div>
+        </div>
+        <el-table :data="tableData" height="61vh" class="collection-con" 
+        :tree-props="{children: 'children'}"
+        row-key="id"
+        style="width: 100%">
+          <el-table-column
+            v-for="(item, idx) in data"
+            :key="idx"
+            :sortable="item.prop==='viewCount'"
+            :prop="item.prop"
+            :label="item.label"
+          >
+            <template slot-scope="scope" >
+              <el-switch
+                v-model="scope.row[item.prop]"
+                v-if="item.prop === 'display'"
+                @change="changeState(scope.row)"
+                :disabled="!($role=='sys_admin'||$role=='sys_high')"
+                inactive-color="#ccc">
+              </el-switch>
+              <span v-else v-html="scope.row[item.prop]||'-'"></span>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作">
+            <template slot-scope="scope" >
+              <template></template>
+              <span v-if="scope.row.qiyong === '注销'">已注销</span>
+              <template v-else>
+                <span class="o-span" v-if="($role=='sys_admin'||$role=='sys_high')" @click="del(scope.row)">删除</span>
+              </template>
+            </template>
+          </el-table-column>
+        </el-table>
+        <div class="e-pagination">
+          <el-pagination
+            @current-change="handleCurrentChange"
+            :current-page.sync="currentPage"
+            @size-change="handleSizeChange"
+            :page-size="size"
+            :page-sizes="PAGESIZES"
+            layout="total, sizes, prev, pager, next, jumper"
+            :total="total"
+          ></el-pagination>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+
+import MainTop from '@/components/main-top'
+import {changeState} from '@/util/commonfn.js'
+const crumbData = [
+  {
+    name: '留言管理',
+    id: 4
+  }
+]
+
+let spiritType = [
+  {
+    id:'news',
+    name:'新闻'
+  },
+  {
+    id:'notice',
+    name:'公告'
+  }
+]
+
+
+let data = [
+      {
+        prop: "idx",
+        label: "编号"
+      },
+      {
+        prop: "nickName",
+        label: "用户昵称"
+      },
+      {
+        prop: "msg",
+        label: "评论内容"
+      },
+      {
+        prop: "updateTime",
+        label: "发布时间"
+      },
+      {
+        prop: "display",
+        label: "是否显示"
+      }
+    ];
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    MainTop,
+  },
+  data () {
+    return {
+      data,
+      crumbData,
+      time:'',
+      tableData: [],
+      inputKey: '',
+      currentPage: 1,
+      size: 25,
+      total: 0,
+      loading: false,
+      spiritType
+    }
+  },
+  watch: {
+    currentPage () {
+      this.refresh()
+    },
+    size () {
+      this.refresh()
+    }
+  },
+  mounted () {
+    this.refresh()
+  },
+ 
+  methods: {
+    changeState(item) {
+      changeState(item,'comment',()=>{
+          this.refresh();
+      })
+    },
+    reset(){
+      this.inputKey=''
+      this.time = ''
+    },
+    del (item) {
+      let ids = item.id
+      this.$confirm('删除后,信息将无法恢复,是否继续?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        this.$http.get(`/manage/comment/removes/${ids}`,{headers: {
+          token: window.localStorage.getItem('token')
+        }}).then(res => {
+          if (res.code === 0) {
+            this.$alert('删除成功', '提示', {
+              confirmButtonText: '确定',
+              callback: () => {
+                this.refresh()
+              }
+            })
+          } else {
+            this.$notify.error({
+              title: '错误',
+              message: res.msg
+            })
+          }
+        })
+      }).catch(() => {
+        this.$message({
+          type: 'info',
+          message: '已取消删除'
+        })
+      })
+    },
+
+    refresh () {
+      this.loading = true
+      this.getInformation()
+      this.loading = false
+    },
+    handleCurrentChange (val) {
+      this.currentPage = val
+    },
+    handleSizeChange(val){
+      this.size = val
+    },
+    async getInformation () {
+      let params = {
+          pageNum:this.currentPage,
+          pageSize: this.size,
+          searchKey: this.inputKey,
+          startTime: this.time? (this.time[0].split(' ')[0] + ' 00:00:00') : '',
+          endTime: this.time? (this.time[1].split(' ')[0] + ' 23:59:59') : ''
+      }
+
+      let result = await this.$http({
+        method: 'post',
+        data: params,
+        headers: {
+          token: window.localStorage.getItem('token')
+        },
+        url: '/manage/comment/list'
+      })
+
+      if (result.code !== 0) {
+        return
+      }
+      this.tableData = result.data.list
+      this.total = result.data.total
+      this.tableData.forEach((item, i) => {
+        item["idx"] = i + 1;
+        item["typeStr"] = this.typeStr[item.type];
+        item["display"] = Boolean(item["display"]);
+        item.children&&item.children.forEach((sub)=>{
+          sub["display"] = Boolean(sub["display"]);
+        })
+      });
+    }
+  }
+}
+</script>
+
+
+<style lang="less" scoped>
+.top-body{
+  border-top: .0625rem solid #e6e6e6;
+  line-height: 1.5;
+  padding: 0 1.25rem 1.25rem;
+  align-items: center;
+  box-sizing: border-box;
+  background: #fff;
+  margin: 1rem 0;
+}
+
+.top-body .top-con{
+  font-weight: bold;
+}
+.table-title{
+  padding: 1rem 1rem 1rem 0 ;
+  font-size: 1rem;
+  color: #2d2d2d;
+  border-bottom: .0625rem solid #ccc;
+  display: flex;
+  justify-content: space-between;
+}
+
+.more{
+  color: #ec652d;
+  cursor: pointer;
+}
+
+.top-right{
+  background-color: #ec652d;
+  height: 3.5625rem;
+  line-height: 3.5625rem;
+  color: #fff;
+  padding: 0 1.875rem;
+  border-radius: .3125rem;
+  font-size: 1.125rem;
+  font-weight: bold;
+}
+
+.search-body{
+  background-color: #fff;
+  margin: 1.25rem 0;
+}
+
+.interface-table{
+  height: calc(100% - 17.1875rem);
+  overflow-x: hidden;
+  background-color: #fff;
+  padding: 0 1.125rem 2.375rem;
+  box-sizing: border-box;
+}
+
+.zan-con{
+  display: flex;
+  width: 100%;
+  justify-content: space-around;
+  margin-top: 1.5rem;
+  text-align: center;
+  font-size: 1.125rem;
+}
+
+.zan-con .line{
+  height: 8rem;
+  width: .0625rem;
+  background: #ccc;
+}
+
+.zan-con .zan-contain{
+  display: flex;
+  justify-content: space-between;
+  flex-direction: column;
+}
+
+.zan-con .zan-contain p{
+  font-size: 2.25rem;
+}
+
+.zan-con .zan-contain p:first-child{
+  font-size: .875rem;
+  line-height: 1.5;
+}
+
+.zan-sub{
+  text-align: right;
+  margin-top: 1.25rem;
+  color: #a5a5a5;
+  font-size: .875rem;
+}
+
+.table-interface{
+  height: calc(100% - 3rem);
+}
+
+
+.info-top{
+  padding: 1.25rem 0;
+  display: flex;
+  justify-content: space-between;
+  border-bottom: .0625rem #a5a5a5 solid;
+}
+
+.o-span{
+  cursor: pointer;
+  color: rgb(7, 152, 244);
+}
+
+.collection-con{
+  width: 100%;
+  overflow-x: hidden;
+  overflow-y: auto;
+  height: 61vh;
+}
+
+
+
+.collection-con ul{
+  display: flex;
+  flex-wrap: wrap;
+  margin-top: 1.25rem;
+}
+
+.collection-con ul li {
+  width: 24%;
+  margin-right: 1%;
+  cursor: pointer;
+  font-size: .8rem;
+  color: #2d2d2d;
+  margin-bottom: 1.25rem;
+}
+
+.collection-con ul .li-img {
+  position: relative;
+  
+}
+.collection-con ul .li-img div{
+  position: absolute;
+  left: .625rem;
+  bottom: .625rem;
+  color: #fff;
+}
+
+.collection-con ul .li-img div span{
+  margin-right: .625rem;
+}
+
+
+.collection-con ul img {
+  width: 100%;
+}
+
+.collection-con{
+  .li-txt,.li-name{
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+  }
+  .li-name{
+    margin-top: .5rem;
+    div{
+      font-size: 1rem;
+      font-weight: bold;
+      color: #532F1C;
+      &:last-of-type{
+        color: #707070;
+        font-size: .875rem;
+        font-weight: normal;
+        span{
+          margin-left: .30rem;
+          &:last-of-type{
+            &:hover{
+              color: #409EFF;
+            }
+          }
+          &:hover{
+            color: #c56351;
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 704 - 0
backstage/src/pages/content/Dynamic.vue

@@ -0,0 +1,704 @@
+<!--  -->
+<template>
+  <div>
+    <main-top :crumb="crumbData"></main-top>
+    <div class="table-interface">
+      <div class="top-body">
+        <div class="info-top">
+          <div class="info-left">
+            <span style="margin-left:1.25rem;">标题关键字:</span>
+            <el-input class="elInput paddingmore" :maxlength="50"
+                show-word-limit v-model="inputKey" placeholder="请输入场景名称"></el-input>
+            <el-button type="primary" @click="getInformation">查询</el-button>
+          </div>
+          <div class="info-right"></div>
+        </div>
+        <el-table :data="tableData" height="55vh" class="collection-con" style="width: 100%">
+          <el-table-column
+            v-for="(item, idx) in data"
+            :key="idx"
+            :prop="item.prop"
+            :label="item.label"
+            :width="item.prop === 'thumb'?400:'auto'"
+          >
+            <template slot-scope="scope">
+              <img
+                v-if="item.prop === 'thumb'"
+                :src="scope.row[item.prop]"
+                style="width:auto;height:128px;"
+                alt
+              />
+              <span v-else>{{scope.row[item.prop]||'-'}}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作">
+            <template slot-scope="scope">
+                <span class="o-span" @click="goto(scope.row)">查看</span>
+                <span class="o-span" @click="gotoEdit(scope.row)">编辑</span>
+            </template>
+          </el-table-column>
+        </el-table>
+        <div class="e-pagination">
+          <el-pagination
+            @current-change="handleCurrentChange"
+            :current-page.sync="currentPage"
+            @size-change="handleSizeChange"
+            :page-size="size"
+            :page-sizes="PAGESIZES"
+            layout="total, sizes, prev, pager, next, jumper"
+            :total="total"
+          ></el-pagination>
+        </div>
+      </div>
+    </div>
+    <el-dialog :title="editTitle" :visible.sync="dialogFormVisible" width="40%">
+      <div class="add-con">
+        <div class="add-left">
+          <el-form :model="form">
+            <el-form-item
+              label="视频名称:"
+              prop="title"
+              :label-width="formLabelWidth"
+              :rules="{
+                required: true, message: '视频名称不能为空', trigger: 'blur'
+              }"
+            >
+              <el-input
+                v-model="form.title"
+                placeholder="请输入视频名称"
+                autocomplete="off"
+                :maxlength="15"
+                show-word-limit
+              ></el-input>
+            </el-form-item>
+
+            <el-form-item label="封面图片:">
+              <el-upload
+                class="avatar-uploader"
+                :action="uploadUrl"
+                :headers="{
+                  token,
+                }"
+                :show-file-list="false"
+                :on-success="handleThumbSuccess"
+                :on-error="handleError"
+                :before-upload="beforeAvatarUpload"
+              >
+                <img v-if="form.thumb" :src="form.thumb" class="avatar" />
+                <i v-else class="el-icon-plus avatar-uploader-icon"></i>
+              </el-upload>
+              <p style="color:#999;margin-left:60px;line-height:1">1、支持png、jpg、gif和jpeg的图片格式</p>
+              <p style="color:#999;margin-left:60px;">2、用于列表展示</p>
+            </el-form-item>
+
+            <el-form-item label="视频文件:">
+              <el-upload
+                class="upload-demo"
+                :action="uploadUrl"
+                :headers="{ token }"
+                :on-success="handleFileSuccess"
+                :on-error="handleError"
+                :before-upload="beforefileUpload"
+                :show-file-list="false"
+              >
+                <el-button size="small" type="primary">点击上传</el-button>
+              </el-upload>
+              <p v-if="form.fileName" style="color:#999;margin-left:80px;">{{form.fileName}}</p>
+              <p style="color:#999;margin-left:60px;">1、支持视频文件,最大为1.5GB大小</p>
+            </el-form-item>
+          </el-form>
+        </div>
+        <div class="add-right">
+          <el-form ref="videoform" :model="form">
+            <el-form-item label="素材描述:" prop="nickName" :label-width="formLabelWidth">
+              <el-input
+                v-model="form.description"
+                placeholder="请输入素材描述"
+                type="textarea"
+                autocomplete="off"
+                :autosize="{ minRows: 4}"
+                :maxlength="500"
+                show-word-limit
+              ></el-input>
+            </el-form-item>
+          </el-form>
+        </div>
+      </div>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="save()">保 存</el-button>
+        <el-button @click="dialogFormVisible = false">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+
+import MainTop from "@/components/main-top";
+const crumbData = [
+  {
+    name: "场景管理",
+    id: 4,
+  },
+];
+
+let spiritType = [
+  {
+    id: "news",
+    name: "新闻",
+  },
+  {
+    id: "notice",
+    name: "公告",
+  },
+];
+
+let data = [
+  {
+    prop: "idx",
+    label: "编号",
+  },
+  {
+    prop: "sceneCode",
+    label: "场景",
+  },
+  {
+    prop: "sceneTitle",
+    label: "名称",
+  },
+  {
+    prop: "submitName",
+    label: "发布人",
+  },
+  {
+    prop: "createTime",
+    label: "发布时间",
+  }
+];
+
+let videodata = [
+  {
+    prop: "idx",
+    label: "编号",
+  },
+  {
+    prop: "title",
+    label: "视频名称",
+  },
+
+  {
+    prop: "thumb",
+    label: "封面图片",
+  },
+
+  {
+    prop: "video",
+    label: "视频文件",
+  },
+
+  {
+    prop: "submitName",
+    label: "发布人",
+  },
+  {
+    prop: "createTime",
+    label: "发布时间",
+  },
+  {
+    prop: "display",
+    label: "是否显示",
+  },
+];
+
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    MainTop,
+  },
+  data() {
+    return {
+      crumbData,
+      time: "",
+      tableData: [],
+      inputKey: "",
+      currentPage: 1,
+      size: 25,
+      total: 0,
+      loading: false,
+      status: 4,
+      spiritType,
+      fetchurl: "/manage/scene/list",
+      type: 1,
+      form: {
+        description: "",
+        id: "",
+        img: "",
+        status: "",
+        thumb: "",
+        title: "",
+        video: "",
+        fileId: "",
+        fileName: "",
+      },
+      dialogFormVisible: false,
+      showType: "",
+      formLabelWidth: "120px",
+      file: "",
+      token: window.localStorage.getItem("token"),
+      uploadUrl: `${this.$serverName}manage/file/upload`,
+    };
+  },
+  computed: {
+    data() {
+      let tmp = this.status === 5 ? videodata : data;
+      return tmp;
+    },
+    editTitle() {
+      return this.showType ? "编辑视频" : "新增视频";
+    },
+  },
+  watch: {
+    currentPage() {
+      this.refresh();
+    },
+    size() {
+      this.refresh();
+    }
+  },
+  mounted() {
+    this.refresh();
+  },
+
+  methods: {
+    handleThumbSuccess(res) {
+      let { data } = res;
+      this.form.thumb = data.urlPath;
+      this.form.img = data.urlPath;
+    },
+    handleError() {
+      this.$alert("上传失败,请稍后再试", "提示", {
+        confirmButtonText: "确定",
+        callback: () => {
+          this.loading.close();
+        },
+      });
+    },
+    handleFileSuccess(res) {
+      let { data } = res;
+      this.form.fileName = data.fileName;
+      this.form.fileId = data.id;
+      this.form.video = data.urlPath;
+    },
+    beforefileUpload(file) {
+      let typeArr = ["avi", "mov", "rmvb", "rm", "flv", "mp4", "3gp"];
+      let type = this.isTypeBySend(file.name, typeArr);
+      if (!type) {
+        this.$message.error("只允许指定文件格式的文件");
+        return type;
+      }
+
+      let size = 1500;
+      const isLt = file.size / 1024 / 1024 < size;
+
+      if (!isLt) {
+        this.$message.error(`上传文件大小不能超过 ${size}MB!`);
+      }
+      return isLt;
+    },
+    beforeAvatarUpload(file) {
+      const isLt2M = file.size / 1024 / 1024 < 20;
+      if (!isLt2M) {
+        this.$message.error("上传头像图片大小不能超过 20MB!");
+      }
+      return isLt2M;
+    },
+    show(item = "", type) {
+      this.dialogFormVisible = true;
+      (this.file = ""), (this.showType = type);
+      this.form = {
+        description: "",
+        id: "",
+        img: "",
+        status: "",
+        thumb: "",
+        title: "",
+        video: "",
+        fileId: "",
+        fileName: "",
+      };
+
+      if (type === "edit") {
+        this.getDetail(item.id);
+      }
+    },
+    async getDetail(id) {
+      let result = await this.$http({
+        method: "get",
+        headers: {
+          token: window.localStorage.getItem("token"),
+        },
+        url: `/manage/video/detail/${id}`,
+      });
+
+      this.form = result.data;
+    },
+
+    goto(item) {
+      window.open(`/web/#/?m=${item.sceneCode}`, "_blank");
+    },
+
+    gotoEdit(item) {
+      window.open(`/edit-backstage/edit.html?m=${item.sceneCode}`, "_blank");
+    },
+
+    del(item) {
+      let url = this.status === 5 ? "video" : "scene";
+      let ids = item.id;
+      this.$confirm("删除后,信息将无法恢复,是否继续?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      })
+        .then(() => {
+          this.$http
+            .get(`/manage/${url}/removes/${ids}`, {
+              headers: {
+                token: window.localStorage.getItem("token"),
+              },
+            })
+            .then((res) => {
+              if (res.code === 0) {
+                this.$alert("删除成功", "提示", {
+                  confirmButtonText: "确定",
+                  callback: () => {
+                    this.refresh();
+                  },
+                });
+              } else {
+                this.$notify.error({
+                  title: "错误",
+                  message: res.msg,
+                });
+              }
+            });
+        })
+        .catch(() => {
+          this.$message({
+            type: "info",
+            message: "已取消删除",
+          });
+        });
+    },
+
+    refresh() {
+      this.loading = true;
+      this.getInformation();
+      this.loading = false;
+    },
+    handleCurrentChange(val) {
+      this.currentPage = val;
+    },
+    handleSizeChange(val) {
+      this.size = val;
+    },
+    async save() {
+      this.$refs["videoform"].validate(async (valid) => {
+        if (valid) {
+          let {
+            description,
+            id = "",
+            img,
+            status,
+            thumb,
+            title,
+            video,
+            fileId,
+            fileName,
+          } = this.form;
+
+          let params = {
+            id,
+            description,
+            fileId,
+            fileName,
+            img,
+            status,
+            thumb,
+            title,
+            video,
+          };
+          let result = await this.$http({
+            method: "post",
+            data: params,
+            headers: {
+              token: window.localStorage.getItem("token"),
+            },
+            url: `/manage/video/save`,
+          });
+
+          if (result.code === 0) {
+            this.$alert("保存成功", "提示", {
+              confirmButtonText: "确定",
+              callback: () => {
+                this.getInformation();
+              },
+            });
+          } else {
+            this.$notify.error({
+              title: "错误",
+              message: result.msg,
+            });
+          }
+        }
+      });
+    },
+    async getInformation() {
+      this.dialogFormVisible = false;
+      let params = {
+        pageNum: this.currentPage,
+        pageSize: this.size,
+        searchKey: this.inputKey,
+      };
+
+      let result = await this.$http({
+        method: "post",
+        data: params,
+        headers: {
+          token: window.localStorage.getItem("token"),
+        },
+        url: this.fetchurl,
+      });
+
+      if (result.code !== 0) {
+        return;
+      }
+      this.tableData = result.data.list;
+      this.total = result.data.total;
+      this.tableData.forEach((item, i) => {
+        item["idx"] = i + 1;
+        item["display"] = Boolean(item["display"]);
+      });
+    },
+  },
+};
+</script>
+
+
+<style lang="less" scoped>
+.top-body {
+  border-top: 0.0625rem solid #e6e6e6;
+  line-height: 1.5;
+  padding: 0 1.25rem 1.25rem;
+  align-items: center;
+  box-sizing: border-box;
+  background: #fff;
+  margin: 1rem 0;
+}
+
+.top-body .top-con {
+  font-weight: bold;
+}
+.table-title {
+  padding: 1rem 1rem 1rem 0;
+  font-size: 1rem;
+  color: #2d2d2d;
+  border-bottom: 0.0625rem solid #ccc;
+  display: flex;
+  justify-content: space-between;
+}
+
+.more {
+  color: #ec652d;
+  cursor: pointer;
+}
+
+.top-right {
+  background-color: #ec652d;
+  height: 3.5625rem;
+  line-height: 3.5625rem;
+  color: #fff;
+  padding: 0 1.875rem;
+  border-radius: 0.3125rem;
+  font-size: 1.125rem;
+  font-weight: bold;
+}
+
+.search-body {
+  background-color: #fff;
+  margin: 1.25rem 0;
+}
+
+.interface-table {
+  height: calc(100% - 17.1875rem);
+  overflow-x: hidden;
+  background-color: #fff;
+  padding: 0 1.125rem 2.375rem;
+  box-sizing: border-box;
+}
+
+.zan-con {
+  display: flex;
+  width: 100%;
+  justify-content: space-around;
+  margin-top: 1.5rem;
+  text-align: center;
+  font-size: 1.125rem;
+}
+
+.zan-con .line {
+  height: 8rem;
+  width: 0.0625rem;
+  background: #ccc;
+}
+
+.zan-con .zan-contain {
+  display: flex;
+  justify-content: space-between;
+  flex-direction: column;
+}
+
+.zan-con .zan-contain p {
+  font-size: 2.25rem;
+}
+
+.zan-con .zan-contain p:first-child {
+  font-size: 0.875rem;
+  line-height: 1.5;
+}
+
+.zan-sub {
+  text-align: right;
+  margin-top: 1.25rem;
+  color: #a5a5a5;
+  font-size: 0.875rem;
+}
+
+.table-interface {
+  height: calc(100% - 3rem);
+}
+
+.info-top {
+  padding: 1.25rem 0;
+  display: flex;
+  justify-content: space-between;
+  border-bottom: 0.0625rem #a5a5a5 solid;
+}
+
+.o-span {
+  cursor: pointer;
+  color: rgb(7, 152, 244);
+}
+
+.collection-con {
+  width: 100%;
+  overflow-x: hidden;
+  overflow-y: auto;
+  height: 55vh;
+}
+
+.collection-con ul {
+  display: flex;
+  flex-wrap: wrap;
+  margin-top: 1.25rem;
+}
+
+.collection-con ul li {
+  width: 24%;
+  margin-right: 1%;
+  cursor: pointer;
+  font-size: 0.8rem;
+  color: #2d2d2d;
+  margin-bottom: 1.25rem;
+}
+
+.collection-con ul .li-img {
+  position: relative;
+}
+.collection-con ul .li-img div {
+  position: absolute;
+  left: 0.625rem;
+  bottom: 0.625rem;
+  color: #fff;
+}
+
+.collection-con ul .li-img div span {
+  margin-right: 0.625rem;
+}
+
+.collection-con ul img {
+  width: 100%;
+}
+
+.collection-con {
+  .li-txt,
+  .li-name {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+  }
+  .li-name {
+    margin-top: 0.5rem;
+    div {
+      font-size: 1rem;
+      font-weight: bold;
+      color: #532f1c;
+      &:last-of-type {
+        color: #707070;
+        font-size: 0.875rem;
+        font-weight: normal;
+        span {
+          margin-left: 0.3rem;
+          &:last-of-type {
+            &:hover {
+              color: #409eff;
+            }
+          }
+          &:hover {
+            color: #c56351;
+          }
+        }
+      }
+    }
+  }
+}
+.add-con {
+  display: flex;
+  justify-content: space-between;
+}
+
+.add-left {
+  flex: 1;
+}
+
+.add-right {
+  flex: 1;
+  margin-left: 10%;
+}
+</style>
+<style>
+.avatar-uploader .el-upload {
+  border: 1px dashed #d9d9d9;
+  border-radius: 6px;
+  cursor: pointer;
+  position: relative;
+  overflow: hidden;
+}
+.avatar-uploader .el-upload:hover {
+  border-color: #409eff;
+}
+.avatar-uploader-icon {
+  font-size: 28px;
+  color: #8c939d;
+  width: 178px;
+  height: 178px;
+  line-height: 178px;
+  text-align: center;
+}
+.avatar {
+  width: 178px;
+  /* height: 178px; */
+  display: block;
+}
+</style>

+ 853 - 0
backstage/src/pages/content/Spirit.vue

@@ -0,0 +1,853 @@
+<!--  -->
+<template>
+  <div>
+    <main-top :crumb="crumbData">
+      <div slot="con">
+        <div style="display:flex; align-items: center;">
+            <el-button style="margin-left:20px" type="primary" @click="show('', '')">新增文物</el-button>
+        </div>
+      </div>
+    </main-top>
+    <div class="table-interface">
+      <div class="top-body">
+        <div class="info-top">
+          <div class="info-left">
+            <span>新增时间:</span>
+            <el-date-picker
+              v-model="time"
+              type="daterange"
+              range-separator="-"
+              value-format="yyyy-MM-dd hh:mm:ss"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期">
+            </el-date-picker>
+         
+            <span style="margin-left:1.25rem;">文物分类:</span>
+            <el-select style="width:8.25rem;" v-model="type" placeholder="请选择">
+              <el-option label="全部" :value="''"></el-option>
+              <el-option label="实物模型" value="model">实物模型</el-option>
+              <el-option label="专题图库" value="img">专题图库</el-option>
+              <el-option label="视频档案" value="video">视频档案</el-option>
+            </el-select>
+
+              <span style="margin-left:1.25rem;">文物标题:</span>
+            <el-input class="elInput" v-model="inputKey" placeholder="请输入文物名称"></el-input>
+
+            <el-button style="margin-left:1.25rem;" type="primary" @click="getInformation">查询</el-button>
+            <el-button @click="reset">重置</el-button>
+          </div>
+          <div class="info-right">
+          </div>
+        </div>
+        <el-table :data="tableData" height="61vh" class="collection-con" 
+        :tree-props="{children: 'children'}"
+        row-key="id"
+        style="width: 100%">
+          <el-table-column
+            v-for="(item, idx) in data"
+            :key="idx"
+            :sortable="item.prop==='viewCount'"
+            :prop="item.prop"
+            :label="item.label"
+          >
+            <template slot-scope="scope" >
+              <el-image 
+                 v-if="item.type === 'image'"
+                style="width:auto;height:125px;"
+                :src="scope.row[item.prop]">
+                <div slot="error" class="image-slot">
+                  <i class="el-icon-picture-outline"></i>
+                </div>
+              </el-image>
+              <span v-else v-html="scope.row[item.prop]||'-'"></span>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作">
+            <template slot-scope="scope" >
+                <span class="o-span" v-if="($role=='sys_admin'||$role=='sys_high')" @click="show(scope.row,'edit')">编辑</span>
+                <span class="o-span" v-if="($role=='sys_admin'||$role=='sys_high')" @click="del(scope.row)">删除</span>
+            </template>
+          </el-table-column>
+        </el-table>
+        <div class="e-pagination">
+          <el-pagination
+            @current-change="handleCurrentChange"
+            :current-page.sync="currentPage"
+            @size-change="handleSizeChange"
+            :page-size="size"
+            :page-sizes="PAGESIZES"
+            layout="total, sizes, prev, pager, next, jumper"
+            :total="total"
+          ></el-pagination>
+        </div>
+      </div>
+    </div>
+    <el-dialog :title="editTitle" :visible.sync="dialogFormVisible" width="60%">
+      <div class="add-con" v-if="dialogFormVisible && changeType">
+        <div class="add-left">
+          <el-form :model="form" ref="fodderform">
+            <el-form-item label="文物分类:" :rules="{
+              required:true
+            }" :label-width="formLabelWidth">
+              <el-radio-group :disabled="showType==='edit'" v-model="form.type">
+                <el-radio :label="'model'">实物模型</el-radio>
+                <el-radio :label="'img'">专题图库</el-radio>
+                <el-radio :label="'video'">视频档案</el-radio>
+              </el-radio-group>
+            </el-form-item>
+
+            <el-form-item
+              label="标题:"
+              :label-width="formLabelWidth"
+              prop="name"
+              :rules="{
+                required: true, message: '标题不能为空', trigger: 'blur'
+              }"
+            >
+              <el-input
+                v-model="form.name"
+                placeholder="请输入文物名称"
+                autocomplete="off"
+                maxlength="25"
+                class="paddingmore"
+                show-word-limit
+              ></el-input>
+            </el-form-item>
+
+            <el-form-item
+              label="所属展区:"
+              :label-width="formLabelWidth"
+              :rules="{
+                required: true, message: '所属展区不能为空', trigger: 'change'
+              }"
+            >
+                
+              <el-select
+                style="width:100%;"
+                v-model="form.zoneId"
+                placeholder="请选择所属展区"
+              >
+                <el-option
+                  v-for="(item, i) in typeli"
+                  :key="i"
+                  :label="item.name"
+                  :value="item.id"
+                ></el-option>
+              </el-select>
+            </el-form-item>
+
+            <el-form-item :label="'文物文件:'"
+              prop="filePath"
+              :label-width="formLabelWidth"
+              :rules="{
+                required: true, message: `文物文件不能为空`, trigger: 'change'
+              }"
+             required>
+              <el-upload
+                class="upload-demo"
+                :action="uploadFlieUrl+form.type"
+                :headers="{ token }"
+                :on-success="handleFileSuccess"
+                :on-error="handleError"
+                :before-upload="beforefileUpload"
+                :show-file-list="false"
+              >
+                <el-button size="small" type="primary">点击上传</el-button>
+              </el-upload>
+              <el-input v-model="form.filePath" v-show="false"></el-input>
+
+              <p v-if="form.type=='img'">
+                <img v-if="form.filePath" style="max-width:200px;" :src="form.filePath" alt="">
+              </p>
+              <p v-else-if="form.type == 'video'">
+                <video style="max-width:200px;" v-if="form.filePath" :src="form.filePath" autoplay controls></video>
+              </p>
+              <template v-else>
+                <p v-if="form.filePath" style="color:#999;">链接地址:<a target="_blank" :href="`/model-page/Model.html?m=${getLastName(form.filePath)}`">{{`/model-page/Model.html?m=${getLastName(form.filePath)}`}}</a></p>
+              </template>
+
+              <p style="color:#999;line-height:1;margin-top:20px">格式要求:</p>
+              <p style="color:#999;" v-for="(item,i) in desc[form.type]" :key="i"> 
+                {{item}}
+              </p>
+            </el-form-item>
+          </el-form>
+        </div>
+        <div class="add-right">
+          <el-form>
+            <el-form-item label="封面图片:"
+              prop="thumb"
+              :rules="{
+                required: true, message: '文物缩略图不能为空', trigger: 'change'
+              }"
+            >
+           
+              <el-upload
+                class="avatar-uploader"
+                :action="uploadUrl+'thumb'"
+                :headers="{
+                  token
+                }"
+                :show-file-list="false"
+                :on-error="handleError"
+                :on-success="handleThumbSuccess"
+                :before-upload="beforeAvatarUpload"
+              >
+                <img v-if="form.thumb" :src="form.thumb" class="avatar" />
+                <i v-else class="el-icon-plus avatar-uploader-icon"></i>
+              </el-upload>
+              <p style="color:#999;margin-left:60px;">格式要求:</p>
+              <p style="color:#999;margin-left:60px;line-height:1">
+                1、支持png、jpg、gif和jpeg的图片格式
+              </p>
+              <p style="color:#999;margin-left:60px;">2、用于列表展示</p>
+            </el-form-item>
+            <el-form-item label="文物描述:" prop="description">
+              <el-input
+                v-model="form.description"
+                placeholder="请输入文物描述"
+                type="textarea"
+                autocomplete="off"
+                :autosize="{ minRows: 4}"
+                :maxlength="200"
+                show-word-limit
+              ></el-input>
+            </el-form-item>
+          </el-form>
+           
+        </div>
+      </div>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="save">保 存</el-button>
+        <el-button @click="dialogFormVisible = false">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+
+import MainTop from "@/components/main-top";
+
+const crumbData = [
+  {
+    name: "文物管理",
+    id: 4,
+  },
+];
+
+
+let data = [
+  {
+    prop: "idx",
+    label: "序列"
+  },
+  {
+    prop: "name",
+    label: "文物标题"
+  },
+  {
+    prop: "typeStr",
+    label: "文物分类"
+  },
+  {
+    prop: "categoryStr",
+    label: "所属展区"
+  },
+  
+  {
+    prop: "thumb",
+    label: "封面图片",
+    type:'image'
+  },
+  {
+    prop: "createTime",
+    label: "新增时间"
+  }
+];
+
+let desc = {
+  img:['1、支持png、jpg、gif和jpeg的图片格式','2、最大支持20M'],
+  video:['1、支持.mp4,.avi,.rmvb和.mov格式','2、最大支持500M'],
+  model:['1、支持.4dage格式','2、最大为500M大小。']
+}
+
+let typeStrs = {
+  model:'实物模型',
+  img:'专题图库',
+  video:'视频档案'
+}
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    MainTop,
+  },
+  data() {
+    return {
+      desc,
+      data,
+      fileList:[],
+      duoci:0,
+      crumbData,
+      time: "",
+      tableData: [],
+      inputKey: "",
+      currentPage: 1,
+      size: 25,
+      total: 0,
+      loading: false,
+      type: '',
+      form: {
+        id: "",
+        name: "",
+        thumb: "",
+        type: 'model',
+        filePath:'',
+        zoneId: "",
+        fileName:'',
+        description:''
+      },
+      changeType:true,
+      dialogFormVisible: false,
+      showType:'',
+      formLabelWidth: "120px",
+      token: window.localStorage.getItem("token"),
+      uploadUrl: `${this.$serverName}/manage/goods/upload/`,
+      uploadFlieUrl: `${this.$serverName}/manage/goods/upload/`,
+      typeli:[]
+    };
+  },
+  computed:{
+      editTitle(){
+        return this.showType ?'编辑文物':'新增文物'
+      },
+  },
+  watch: {
+    currentPage() {
+      this.refresh();
+    },
+    size() {
+      this.refresh();
+    },
+    type() {
+      // this.refresh();
+    },
+    'form.type':function () {
+        if (this.showType != 'edit') {
+          this.changeType = false
+          this.form.filePath = ''
+          this.form.fileName = ''
+          setTimeout(() => {
+            this.changeType = true 
+          });
+        }
+    }
+  },
+  mounted() {
+    this.getTypeList()
+  },
+
+  methods: {
+    async getTypeList () {
+      let params = {
+          pageNum:1,
+          pageSize: 1000,
+          searchKey: ""
+      }
+      let result = await this.$http({
+        method: 'post',
+        data: params,
+        headers: {
+          token: window.localStorage.getItem('token')
+        },
+        url: '/manage/zone/list'
+      })
+
+      if (result.code !== 0) {
+        return
+      }
+
+      this.typeli = result.data
+      this.refresh()
+    },
+    getLastName(data){
+      return data.substring(data.lastIndexOf("/")+1)
+    },
+    reset(){
+      this.inputKey=''
+      this.time=''
+      this.type=''
+      this.refresh()
+    },
+    handleThumbSuccess(res) {
+      this.loading.close()
+      let { data } = res;
+      this.form.thumb = data.filePath;
+    },
+    handleError(){
+      this.$alert("上传失败,请稍后再试", "提示", {
+        confirmButtonText: "确定",
+        callback: () => {
+          this.loading.close()
+        },
+      });
+    },
+    handleFileSuccess(res) {
+      this.loading.close()
+      let { data } = res;
+      this.form.filePath = data.filePath;
+      this.form.fileName = data.fileName;
+    },
+    beforefileUpload(file){
+      let typeArr = this.form.type === 'img' ? ['.png','jpeg','gif','jpg'] : (this.form.type === 'video'? ['avi','mov','rmvb','rm','flv','mp4','3gp']:['4dage'])
+      let type = this.isTypeBySend(file.name,typeArr);
+      if (!type) {
+        this.$message.error('只允许指定文件格式的文件');
+        return type
+      }
+      
+      let size = this.form.type === 'img' ? 20 : 500
+      const isLt = file.size / 1024 / 1024 < size;
+
+      if (!isLt) {
+        this.$message.error(`上传文件大小不能超过 ${size}MB!`);
+        return isLt;
+      }
+      this.loading = this.$loading(this.loadOption)
+    },
+    beforeAvatarUpload(file) {
+      let type = this.isImage(file.name);
+      if (!type) {
+        this.$message.error('只允许上传图片');
+        return type
+      }
+      const isLt2M = file.size / 1024 / 1024 < 20;
+
+      if (!isLt2M) {
+        this.$message.error("上传头像图片大小不能超过 20MB!");
+        return isLt2M;
+      }
+      this.loading = this.$loading(this.loadOption)
+    },
+
+    handleChange(item) {
+      console.log(item);
+    },
+
+
+    async getDetail(id) {
+      let result = await this.$http({
+        method: "get",
+        headers: {
+          token: window.localStorage.getItem("token"),
+        },
+        url: `/manage/goods/detail/${id}`,
+      });
+
+      this.form = result.data;
+      console.log(this.form);
+    },
+    checkAll(val){
+      let arr = []
+      this.tableData.forEach(item=>{
+        item.check = val
+        arr.push(item)
+      })
+      this.tableData = arr
+    },
+    handleCheck(item,i){
+      this.$set(this.tableData,i,item)
+    },
+    show(item = "", type) {
+      this.dialogFormVisible = true;
+
+      this.showType = type;
+      this.form = {
+        id: "",
+        name: "",
+        thumb: "",
+        type: 'model',
+        filePath:'',
+        zoneId: "",
+        fileName:'',
+        description:''
+      };
+
+      if (type === "edit") {
+        this.getDetail(item.id)
+      }
+    },
+    del(item) {
+      this.$confirm("删除后,信息将无法恢复,是否继续?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      })
+        .then(() => {
+          this.$http
+            .get(`/manage/goods/removes/${item.id}`, {
+              headers: {
+                token: window.localStorage.getItem("token"),
+              },
+            })
+            .then((res) => {
+              if (res.code === 0) {
+                this.$alert("删除成功", "提示", {
+                  confirmButtonText: "确定",
+                  callback: () => {
+                    this.refresh();
+                  },
+                });
+              } else {
+                this.$notify.error({
+                  title: "错误",
+                  message: res.msg,
+                });
+              }
+            });
+        })
+        .catch(() => {
+          this.$message({
+            type: "info",
+            message: "已取消删除",
+          });
+        });
+    },
+    async save() {
+      this.$refs["fodderform"].validate(async (valid) => {
+        if (valid) {
+          let {
+            id,
+            name,
+            thumb,
+            type,
+            filePath,
+            fileName,
+            description,
+            zoneId
+          } = this.form;
+          if (!thumb) {
+            return this.$alert("文物缩略图不能为空", "提示", {
+              confirmButtonText: "确定",
+              callback: () => {
+              },
+            });
+          }
+          
+          let params = {
+            id,
+            filePath,
+            fileName,
+            description,
+            name,
+            zoneId,
+            thumb,
+            type
+          };
+          let result = await this.$http({
+            method: "post",
+            data: params,
+            headers: {
+              token: window.localStorage.getItem("token"),
+            },
+            url: `/manage/goods/save`,
+          });
+
+          if (result.code === 0) {
+            this.$alert("保存成功", "提示", {
+              confirmButtonText: "确定",
+              callback: () => {
+                this.getInformation();
+              },
+            });
+          } else if (result.code === 60005) {
+            return
+          } else {
+            this.$notify.error({
+              title: "错误",
+              message: result.msg,
+            });
+          }
+        }
+      });
+    },
+
+    refresh() {
+      this.loading = true;
+      this.getInformation();
+      this.loading = false;
+    },
+    handleCurrentChange(val) {
+      this.currentPage = val;
+    },
+    handleSizeChange(val) {
+      this.size = val;
+    },
+    async getInformation() {
+      this.dialogFormVisible = false;
+      let params = {
+        pageNum: this.currentPage,
+        pageSize: this.size,
+        type:this.type,
+        searchKey: this.inputKey,
+        startTime: this.time? (this.time[0].split(' ')[0] + ' 00:00:00') : '',
+        endTime: this.time? (this.time[1].split(' ')[0] + ' 23:59:59') : ''
+      };
+
+      let result = await this.$http({
+        method: "post",
+        data: params,
+        headers: {
+          token: window.localStorage.getItem("token"),
+        },
+        url: "/manage/goods/list",
+      });
+
+      if (result.code !== 0) {
+        return;
+      }
+      this.tableData = result.data.list;
+      this.total = result.data.total;
+      this.tableData.forEach((item, i) => {
+        item["idx"] = i + 1;
+        item["typeStr"] = typeStrs[item.type];
+        let category = this.typeli.filter(i=>i.id==item.zoneId).length>0 && (this.typeli.filter(i=>i.id==item.zoneId))[0].name
+        item["categoryStr"] = category
+      });
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.top-body {
+  border-top: 0.0625rem solid #e6e6e6;
+  line-height: 1.5;
+  padding: 0 1.25rem 1.25rem;
+  align-items: center;
+  box-sizing: border-box;
+  background: #fff;
+  margin: 1rem 0;
+}
+
+.top-body .top-con {
+  font-weight: bold;
+}
+.table-title {
+  padding: 1rem 1rem 1rem 0;
+  font-size: 1rem;
+  color: #2d2d2d;
+  border-bottom: 0.0625rem solid #ccc;
+  display: flex;
+  justify-content: space-between;
+}
+
+.more {
+  color: #ec652d;
+  cursor: pointer;
+}
+
+.top-right {
+  background-color: #ec652d;
+  height: 3.5625rem;
+  line-height: 3.5625rem;
+  color: #fff;
+  padding: 0 1.875rem;
+  border-radius: 0.3125rem;
+  font-size: 1.125rem;
+  font-weight: bold;
+}
+
+.search-body {
+  background-color: #fff;
+  margin: 1.25rem 0;
+}
+
+.interface-table {
+  height: calc(100% - 17.1875rem);
+  overflow-x: hidden;
+  background-color: #fff;
+  padding: 0 1.125rem 2.375rem;
+  box-sizing: border-box;
+}
+
+.zan-con {
+  display: flex;
+  width: 100%;
+  justify-content: space-around;
+  margin-top: 1.5rem;
+  text-align: center;
+  font-size: 1.125rem;
+}
+
+.zan-con .line {
+  height: 8rem;
+  width: 0.0625rem;
+  background: #ccc;
+}
+
+.zan-con .zan-contain {
+  display: flex;
+  justify-content: space-between;
+  flex-direction: column;
+}
+
+.zan-con .zan-contain p {
+  font-size: 2.25rem;
+}
+
+.zan-con .zan-contain p:first-child {
+  font-size: 0.875rem;
+  line-height: 1.5;
+}
+
+.zan-sub {
+  text-align: right;
+  margin-top: 1.25rem;
+  color: #a5a5a5;
+  font-size: 0.875rem;
+}
+
+.table-interface {
+  height: calc(100% - 3rem);
+}
+
+.info-top {
+  padding: 1.25rem 0;
+  display: flex;
+  justify-content: space-between;
+  border-bottom: 0.0625rem #a5a5a5 solid;
+}
+
+.o-span {
+  cursor: pointer;
+  color: rgb(7, 152, 244);
+}
+
+.collection-con {
+  width: 100%;
+  overflow-x: hidden;
+  overflow-y: auto;
+  height: 60vh;
+}
+
+.collection-con ul {
+  display: flex;
+  flex-wrap: wrap;
+  margin-top: 1.25rem;
+}
+
+.collection-con ul li {
+  width: 24%;
+  margin-right: 1%;
+  cursor: pointer;
+  font-size: 0.8rem;
+  color: #2d2d2d;
+  margin-bottom: 1.25rem;
+}
+
+.collection-con ul .li-img {
+  position: relative;
+  height: 250px;
+  font-size: 0;
+}
+
+.collection-con ul .elchbox{
+  position: absolute;
+  left: 10px;
+  top: 5px;
+}
+
+
+
+
+
+.collection-con ul .li-img div span {
+  margin-right: 0.625rem;
+}
+
+.collection-con ul img {
+  width: 100%;
+}
+
+.collection-con {
+  li {
+    position: relative;
+    .li-txt {
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      background: #dedede;
+      font-size: 16px;
+      line-height: 2;
+    }
+    .oper {
+      position: absolute;
+      top: 10px;
+      right: 10px;
+      display: none;
+      > img {
+        width: 40px;
+        height: 40px;
+        background: #b63c25;
+        margin-left: 20px;
+        border-radius: 4px;
+        padding: 5px;
+      }
+    }
+    &:hover {
+      .oper {
+        display: block;
+      }
+    }
+  }
+}
+
+.add-con {
+  display: flex;
+  justify-content: space-between;
+}
+
+.add-left {
+  flex: 1;
+}
+
+.add-right {
+  flex: 1;
+  margin-left: 10%;
+}
+
+
+</style>
+
+<style>
+.avatar-uploader .el-upload {
+  border: 1px dashed #d9d9d9;
+  border-radius: 6px;
+  cursor: pointer;
+  position: relative;
+  overflow: hidden;
+}
+.avatar-uploader .el-upload:hover {
+  border-color: #409eff;
+}
+.avatar-uploader-icon {
+  font-size: 28px;
+  color: #8c939d;
+  width: 178px;
+  height: 178px;
+  line-height: 178px;
+  text-align: center;
+}
+.avatar {
+  width: 178px;
+  /* height: 178px; */
+  display: block;
+}
+
+.el-textarea__inner{
+  padding-right: 60px!important;
+}
+</style>

+ 374 - 0
backstage/src/pages/edit/carousel.vue

@@ -0,0 +1,374 @@
+<!--  -->
+<template>
+  <div>
+    <main-top :crumb="crumbData" :status="form.status">
+      <div slot="con" class="con-btn">
+        <el-button type="primary" @click="back">返回</el-button>
+      </div>
+    </main-top>
+    <div class="reason" v-if="form.status===3">审核不通过原因:{{form.reason}}</div>
+    <div class="edit-body">
+      <div
+        class="edit-arch"
+        :style="{height: form.status === 3?`calc(100% - 11.5rem - 20px)`:`calc(100% - 8.5rem - 20px)`}"
+      >
+        <div class="title">基本信息:</div>
+        <el-form
+          ref="partform"
+          :rules="rules"
+          :model="form"
+          label-width="120px"
+          :disabled="form.status===2||form.status===4"
+        >
+          <el-form-item label="标题" style="width:618px;" prop="title">
+            <el-input
+              placeholder="请输入标题"
+              :maxlength="FONTLENGTH*0.6"
+              show-word-limit
+              v-model="form.title"
+            ></el-input>
+          </el-form-item>
+
+          <el-form-item label="场景链接" style="width:618px;" prop="webSite">
+            <el-input placeholder="请输入场景链接" v-model="form.webSite"></el-input>
+          </el-form-item>
+
+          <el-form-item label="封面图片" prop="img">
+            <el-upload
+              class="avatar-uploader"
+              :class="{'noCursor':(($role=='sys_admin'||$role=='sys_high')&&(form.status===2||form.status===4))}"
+              :action="uploadUrl"
+              :headers="{
+              token,
+            }"
+              :show-file-list="false"
+              :on-success="handleThumbSuccess"
+              :on-error="handleError"
+              :before-upload="beforeAvatarUpload"
+            >
+              <img v-if="form.thumb" :src="form.thumb" class="avatar" />
+              <i v-else class="el-icon-plus avatar-uploader-icon"></i>
+            </el-upload>
+            <span
+              style="color:#999"
+              v-if="!(($role=='sys_admin'||$role=='sys_high')&&(form.status===2||form.status===4))"
+            >支持png、jpg、gif和jpeg的图片格式;最大支持20M。</span>
+          </el-form-item>
+        </el-form>
+        <el-form label-width="120px" v-if="$role=='sys_admin'||$role=='sys_high'">
+          <el-form-item
+            v-if="($role=='sys_admin'||$role=='sys_high')&&(form.status===2||form.status===4)"
+            label="显示设置"
+          >
+            <el-switch
+              v-model="display"
+              inactive-color="#ccc"
+              @change="changeState({id:form.id,display:display})"
+            ></el-switch>
+          </el-form-item>
+        </el-form>
+      </div>
+      <div class="btnclass" v-if="form.status !== 4">
+        <template v-if="form.status!==2">
+          <el-button @click="save(1)">保存</el-button>
+          <el-button type="primary" @click="save(2)">提交</el-button>
+        </template>
+        <template v-else>
+          <template v-if="$role=='sys_admin'||$role=='sys_high'">
+            <el-button @click="dialogFormVisible = true">审核不通过</el-button>
+            <el-button type="primary" @click="()=>{reason='',examine(4)}">审核通过</el-button>
+          </template>
+        </template>
+      </div>
+    </div>
+    <el-dialog title="审核不通过" :visible.sync="dialogFormVisible">
+      <el-form :model="form">
+        <el-form-item
+          label="选择原因"
+          :rules="[
+          { required: true, message: '选择原因不能为空'},
+        ]"
+        >
+          <el-radio-group v-model="reason">
+            <el-radio :label="'信息内容待调整'">信息内容待调整</el-radio>
+            <el-radio :label="'标题需进行修改'">标题需进行修改</el-radio>
+            <el-radio :label="2">自定义</el-radio>
+          </el-radio-group>
+          <el-input
+            v-model="zidingyi"
+            v-if="reason===2"
+            type="textarea"
+            :autosize="{ minRows: 2, maxRows: 4}"
+          ></el-input>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="dialogFormVisible = false">取 消</el-button>
+        <el-button type="primary" @click="examine(3)">确 定</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+
+import MainTop from "@/components/main-top";
+import { changeState } from "@/util/commonfn.js";
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    MainTop,
+  },
+  data() {
+    const crumbData = [
+      {
+        name: "史馆动态信息",
+        id: 0,
+      },
+      {
+        name: this.$route.params.type ? "编辑史馆动态" : "新增史馆动态",
+        id: 1,
+      },
+    ];
+
+    return {
+      crumbData,
+      reason: "信息内容待调整",
+      zidingyi: "",
+      dialogFormVisible: false,
+      form: {
+        id: "",
+        img: "",
+        thumb: "",
+        title: "",
+        webSite: "",
+      },
+      display: true,
+      type: this.$route.params.type,
+      id: this.$route.params.id,
+      token: window.localStorage.getItem("token"),
+      uploadUrl: `${this.$serverName}manage/file/upload`,
+      rules: {
+        title: [{ required: true, message: "请输入标题", trigger: "blur" }],
+        webSite: [
+          { required: true, message: "请输入场景链接", trigger: "blur" },
+        ],
+        img: [{ required: true, message: "请上传封面图片", trigger: "blur" }],
+      },
+    };
+  },
+  methods: {
+    back() {
+      if(this.form.status===2){
+          this.$router.back();
+      }
+      else{
+        this.$confirm("点击返回后, 编辑的信息将无法保存,是否继续?", "提示", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
+        }).then(() => {
+          this.$router.back();
+        });
+      }
+    },
+    changeState(item) {
+      changeState(item, "slideshow");
+    },
+
+    async examine(status) {
+      this.$confirm("请再次确认是否进行此审核操作?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      })
+        .then(async () => {
+          let result = await this.$http({
+            method: "post",
+            data: {
+              id: this.form.id,
+              reason: this.reason === 2 ? this.zidingyi : this.reason,
+              status,
+            },
+            headers: {
+              token: window.localStorage.getItem("token"),
+            },
+            url: `/manage/slideshow/audit`,
+          });
+
+          if (result.code === 0) {
+            this.$alert("审核成功", "提示", {
+              confirmButtonText: "确定",
+              callback: () => {
+                this.$router.back();
+              },
+            });
+          } else {
+            this.$notify.error({
+              title: "错误",
+              message: result.msg,
+            });
+          }
+        })
+        .catch(() => {
+          this.$message({
+            type: "info",
+            message: "已取消",
+          });
+        });
+    },
+    handleError(){
+      this.$alert("上传失败,请稍后再试", "提示", {
+        confirmButtonText: "确定",
+        callback: () => {
+          this.loading.close()
+        },
+      });
+    },
+    handleThumbSuccess(res) {
+      this.loading.close();
+      let { data } = res;
+      this.form.thumb = data.thumb;
+      this.form.img = data.urlPath;
+    },
+    beforeAvatarUpload(file) {
+      let type = this.isImage(file.name);
+      if (!type) {
+        this.$message.error("只允许上传图片");
+        return type;
+      }
+      const isLt2M = file.size / 1024 / 1024 < 20;
+
+      if (!isLt2M) {
+        this.$message.error("上传头像图片大小不能超过 20MB!");
+        return isLt2M;
+      }
+      this.loading = this.$loading(this.loadOption);
+    },
+
+    async postStatus(status){
+       let { id = "", img, thumb, title, webSite } = this.form;
+          let params = {
+            id,
+            img,
+            thumb,
+            title,
+            webSite,
+            status,
+          };
+          let result = await this.$http({
+            method: "post",
+            data: params,
+            headers: {
+              token: window.localStorage.getItem("token"),
+            },
+            url: `/manage/slideshow/save`,
+          });
+
+          if (result.code === 0) {
+            this.$alert("操作成功", "提示", {
+              confirmButtonText: "确定",
+              callback: () => {
+                this.$router.back();
+              },
+            });
+          } else {
+            this.$notify.error({
+              title: "错误",
+              message: result.msg,
+            });
+          }
+    },
+
+    save(status) {
+      this.$refs["partform"].validate(async (valid) => {
+        if (valid) {
+          if(status == 2){
+             this.$confirm("请再次确认是否进行此审核操作?", "提示", {
+                confirmButtonText: "确定",
+                cancelButtonText: "取消",
+                type: "warning",
+              }).then(async () => {
+                  this.postStatus(status)
+                  })
+                .catch(() => {
+                  this.$message({
+                    type: "info",
+                    message: "已取消",
+                  });
+                });
+          }
+          else{
+            this.postStatus(status)
+          }         
+        }
+      });
+    },
+    async getDetail() {
+      let result = await this.$http({
+        method: "get",
+        headers: {
+          token: window.localStorage.getItem("token"),
+        },
+        url: `/manage/slideshow/detail/${this.id}`,
+      });
+
+      this.form = result.data;
+      this.display = Boolean(this.form.display);
+    },
+  },
+  mounted() {
+    if (this.id && this.id !== "none") {
+      this.getDetail();
+    }
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.edit-body {
+  background: #fff;
+  height: 100%;
+  .edit-arch {
+    overflow-y: auto;
+    overflow-x: hidden;
+    height: calc(100% - 8.5rem - 20px);
+    background-color: #fff;
+    padding: 20px 40px 20px 20px;
+  }
+  .btnclass {
+    text-align: center;
+    border: 1px solid #ccc;
+    padding: 10px 0;
+    width: calc(100% - 80px);
+    margin: 0 auto;
+  }
+}
+</style>
+<style>
+.avatar-uploader .el-upload {
+  border: 1px dashed #d9d9d9;
+  border-radius: 6px;
+  cursor: pointer;
+  position: relative;
+  overflow: hidden;
+}
+.avatar-uploader .el-upload:hover {
+  border-color: #409eff;
+}
+.avatar-uploader-icon {
+  font-size: 28px;
+  color: #8c939d;
+  width: 178px;
+  height: 178px;
+  line-height: 178px;
+  text-align: center;
+}
+.avatar {
+  width: 178px;
+  /* height: 178px; */
+  display: block;
+}
+</style>

+ 710 - 0
backstage/src/pages/edit/collection.vue

@@ -0,0 +1,710 @@
+<!--  -->
+<template>
+  <div>
+    <main-top :crumb="crumbData" :status="form.status">
+      <div slot="con" class="con-btn">
+        <el-button type="primary" @click="back">返回</el-button>
+      </div>
+    </main-top>
+    <div class="reason" v-if="form.status===3">
+      审核不通过原因:{{form.reason}}
+    </div>
+    <div class="edit-body">
+      <div class="edit-arch" :style="{height: form.status === 3?`calc(100% - 11.5rem - 20px)`:`calc(100% - 8.5rem - 20px)`}">
+        <div class="title">基本信息:</div>
+        <el-form ref="partform" :rules="rules" :model="form" label-width="120px" :disabled="form.status===2||(!($role=='sys_admin'||$role=='sys_high')&&(form.status===4))">
+          <el-form-item label="标题" prop="title">
+            <el-input
+              placeholder="请输入标题"
+              :maxlength="FONTLENGTH"
+              show-word-limit
+              v-model="form.title"
+              :disabled="form.status===2||(!($role=='sys_admin'||$role=='sys_high')&&(form.status===4))"
+            ></el-input>
+          </el-form-item>
+
+          <el-form-item label="精品类型" prop="type">
+            <el-radio-group :disabled="$route.params.type==1" v-model="form.type">
+              <el-radio :label="3">实物模型</el-radio>
+              <el-radio :label="1">专题图库</el-radio>
+              <el-radio :label="2">视频档案</el-radio>
+            </el-radio-group>
+          </el-form-item>
+
+          <template v-if="form.type === 3">
+            <el-form-item label="封面图片" prop="thumb">
+              <el-upload
+                :disabled="form.status===2||(!($role=='sys_admin'||$role=='sys_high')&&(form.status===4))"
+                class="avatar-uploader"
+                :action="uploadUrl"
+                :headers="{token}"
+                :class="{'noCursor':(($role=='sys_admin'||$role=='sys_high')&&(form.status===2))}"
+                :show-file-list="false"
+                :on-success="handleThumbSuccess"
+                :on-error="handleError"
+                :before-upload="beforeAvatarUpload"
+              >
+                <img v-if="form.thumb" :src="form.thumb" class="avatar" />
+                <i v-else class="el-icon-plus avatar-uploader-icon"></i>
+              </el-upload>
+              <span style="color:#999"
+              v-if="!(($role=='sys_admin'||$role=='sys_high')&&(form.status===2))"
+              >支持png、jpg、gif和jpeg的图片格式;最大支持20M。</span>
+            </el-form-item>
+          
+
+            <el-form-item label="模型文件" required :key="'moxing'">
+              <el-upload
+                class="upload-demo"
+                drag
+                :disabled="form.status===2||(!($role=='sys_admin'||$role=='sys_high')&&(form.status===4))"
+                :action="uploadModelUrl"
+                :headers="{token}"
+                :show-file-list="false"
+                :before-upload="beforeMultipleUpload"
+                :on-error="handleError"
+                :on-success="handleSuccess"
+              >
+                <i class="el-icon-upload"></i>
+                <div class="el-upload__text">
+                  将文件拖到此处,或
+                  <em>点击上传</em>
+                  <p>仅支持.4dage格式的模型文件,大小不得超过500m</p>
+                </div>
+              </el-upload>
+              <p v-if="filesIds[0]" style="color:#999">{{filesIds[0].fileName}}</p>
+            </el-form-item>
+
+              <el-form-item label="模型介绍">
+              <el-input
+                placeholder="请输入模型介绍"
+                :maxlength="250"
+                show-word-limit
+                type="textarea"
+                v-model="form.description"
+              ></el-input>
+            </el-form-item>
+          </template>
+
+          <template v-else-if="form.type === 1">
+            <el-form-item label="年份" required :key="'tukuyear'">
+              <el-select v-model="form.year" placeholder="请选择">
+                <el-option v-for="(item,i) in yearStr" :key="i" :label="item.name" :value="item.id"></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item label="专题图库"  required :key="'tuku'">
+              <el-upload
+                :action="uploadUrl"
+                :disabled="form.status===2||(!($role=='sys_admin'||$role=='sys_high')&&(form.status===4))"
+                :headers="{token}"
+                list-type="picture-card"
+                :file-list="fileList"
+                :before-upload="beforeAvatarUpload"
+                  multiple
+                :on-error="handleError"
+                :on-success="handlePictureCardSuccess"
+              >
+
+                <i class="el-icon-plus"></i>
+                <div class="card-con" slot="file" slot-scope="{file}">
+                  <img
+                    class="el-upload-list__item-thumbnail"
+                    :src="file.url" alt=""
+                  >
+                  <span class="el-upload-list__item-actions">
+                    <span
+                      class="el-upload-list__item-delete"
+                    >
+                      <i v-if="form.status!==2" class="el-icon-delete" @click="del(file)"></i>
+                    </span>
+                  </span>
+                  <div class="cover" v-if="file.cover">封面图片</div>
+                  <div class="btn-con">
+                
+                    <el-button class="queren" :disabled="form.status===2||(!($role=='sys_admin'||$role=='sys_high')&&(form.status===4))" v-if="!file.cover" @click="setCover(file)" size="mini" type="primary">设为封面</el-button>
+                    <!-- <el-upload
+                      :action="uploadUrl"
+                      :headers="{token}"
+                      :on-success="handleReloadSuccess"
+                      :before-upload="showLoading"
+                      :disabled="form.status===2"
+                      :show-file-list="false">
+                      <el-button @click="reupload(file)" size="mini">重新上传</el-button>
+                    </el-upload> -->
+                  </div>
+                </div>
+              </el-upload>
+              <div style="color:#999;margin-top:40px" >格式要求:支持png、jpg、gif和jpeg的图片格式;最大支持20M;最多可以上传18张图片</div>
+            </el-form-item>
+          </template>
+
+          <template v-else>
+            <el-form-item label="年份" required :key="'shipinyear'">
+              <el-select v-model="form.year" placeholder="请选择">
+                <el-option v-for="(item,i) in yearStr" :key="i" :label="item.name" :value="item.id"></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item label="封面图片" prop="thumb" >
+              <el-upload
+                class="avatar-uploader"
+                :action="uploadUrl"
+                :headers="{token}"
+                :class="{'noCursor':(($role=='sys_admin'||$role=='sys_high')&&(form.status===2))}"
+                :disabled="form.status===2||(!($role=='sys_admin'||$role=='sys_high')&&(form.status===4))"
+                :show-file-list="false"
+                :on-success="handleThumbSuccess"
+                :on-error="handleError"
+                :before-upload="beforeAvatarUpload"
+              >
+                <img v-if="form.thumb" :src="form.thumb" class="avatar" />
+                <i v-else class="el-icon-plus avatar-uploader-icon"></i>
+              </el-upload>
+              <span style="color:#999"
+                v-if="!(($role=='sys_admin'||$role=='sys_high')&&(form.status===2))"
+              >支持png、jpg、gif和jpeg的图片格式;最大支持20M。</span>
+            </el-form-item>
+
+             <el-form-item label="视频文件" required :key="'shipin'">
+              <el-upload
+                class="upload-demo"
+                drag
+                :action="uploadUrl"
+                :disabled="form.status===2||(!($role=='sys_admin'||$role=='sys_high')&&(form.status===4))"
+                :headers="{token}"
+                :show-file-list="false"
+                :before-upload="beforeMultipleUpload"
+                :on-error="handleError"
+                :on-success="handleSuccess"
+              >
+                <i class="el-icon-upload"></i>
+                <div class="el-upload__text">
+                  将文件拖到此处,或
+                  <em>点击上传</em>
+                  <p style="padding:0 10px;">支持AVI、mov、rmvb、rm、FLV、mp4、3GP等格式的视频文件,大小不得超过2GB</p>
+                </div>
+              </el-upload>
+              <p v-if="filesIds[0]" style="color:#999">{{filesIds[0].fileName}}</p>
+            </el-form-item>
+          </template>
+        </el-form>
+        <el-form
+          label-width="120px"
+          v-if="$role=='sys_admin'||$role=='sys_high'"
+        >
+          <el-form-item v-if="($role=='sys_admin'||$role=='sys_high')&&(form.status===2)" label="显示设置">
+            <el-switch v-model="display" inactive-color="#ccc" @change="changeState({id:form.id,display:display})">
+          </el-switch>
+          </el-form-item>
+        </el-form>
+      </div>
+      <template v-if="form.status !== 4">
+        <div class="btnclass" v-if="form.status!==2">
+          <el-button @click="save(1)">保存</el-button>
+          <el-button type="primary" @click="save(2)">提交</el-button>
+        </div>
+        <template  v-else>
+          <div class="btnclass" v-if="$role=='sys_admin'||$role=='sys_high'">
+            <el-button @click="dialogFormVisible = true">审核不通过</el-button>
+            <el-button type="primary" @click="()=>{reason='',examine(4)}">审核通过</el-button>
+          </div>
+        </template>
+      </template>
+      <template v-if="form.status === 4 && $role=='sys_admin'||$role=='sys_high'">
+        <div class="btnclass">
+          <el-button @click="save(1)">保存</el-button>
+          <el-button type="primary" @click="save(2)">提交</el-button>
+        </div>
+      </template>
+    </div>
+    <el-dialog title="审核不通过" :visible.sync="dialogFormVisible">
+      <el-form :model="form" >
+        <el-form-item label="选择原因" :rules="[
+          { required: true, message: '选择原因不能为空'},
+        ]" >
+            <el-radio-group v-model="reason">
+              <el-radio :label="'信息内容待调整'">信息内容待调整</el-radio>
+              <el-radio :label="'标题需进行修改'">标题需进行修改</el-radio>
+              <el-radio :label="2">自定义</el-radio>
+            </el-radio-group>
+            <el-input v-model="zidingyi" v-if="reason===2" type="textarea" :autosize="{ minRows: 2, maxRows: 4}"></el-input>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="dialogFormVisible = false">取 消</el-button>
+        <el-button type="primary" @click="examine(3)">确 定</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+
+import MainTop from "@/components/main-top";
+import {changeState} from '@/util/commonfn.js'
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    MainTop
+  },
+  watch:{
+    'form.type':function (newVal) {
+      if (this.type==0) {
+        this.form={
+          fileIds: "",
+          description:"",
+          id: '',
+          thumb: "",
+          thumbFileId:'',
+          title: "",
+          type: newVal,
+          year: 2020
+        }
+      this.filesIds=[]
+      }
+    }
+  },
+  data() {
+    const crumbData = [
+      {
+        name: "精品典藏信息",
+        id: 0
+      },
+      {
+        name: this.$route.params.type ? "编辑精品典藏" : "新增精品典藏",
+        id: 1
+      }
+    ];
+
+    return {
+      crumbData,
+      reason:'信息内容待调整',
+      zidingyi:'',
+      dialogFormVisible:false,
+      currentIdx:1,
+      form: {
+        fileIds: "",
+        description:"",
+        id: '',
+        thumb: "",
+        thumbFileId:'',
+        title: "",
+        year:2020,
+        type: parseInt(this.$route.query.type)
+      },
+      display: true,
+      filesIds:[],
+      fileList: [],
+      type: this.$route.params.type,
+      id: this.$route.params.id,
+      token: window.localStorage.getItem("token"),
+      uploadUrl: `${this.$serverName}manage/file/upload?compress=1`,
+      uploadModelUrl: `${this.$serverName}manage/goods/upload`,
+      rules: {
+        title: [{ required: true, message: "请输入标题", trigger: "blur" }],
+        type: [{ required: true, message: "请选择精品类型", trigger: "blur" }],
+        thumb: [{ required: true, message: "请上传封面图片", trigger: "blur" }],
+      }
+    };
+  },
+  methods: {
+    showLoading(){
+      this.loading = this.$loading(this.loadOption)
+    },
+    back() {
+      if(this.form.status===2){
+          this.$router.back();
+      }
+      else{
+        this.$confirm("点击返回后, 编辑的信息将无法保存,是否继续?", "提示", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
+        }).then(() => {
+          this.$router.back();
+        });
+      }
+    },
+    changeState(item) {
+      changeState(item,'goods')
+    },
+     del (item) {
+      this.$confirm('删除后,信息将无法恢复,是否继续?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        this.$http.get(`/manage/file/remove/${item.id}`,{headers: {
+          token: window.localStorage.getItem('token')
+        }}).then(res => {
+          if (res.code === 0) {
+            this.$alert('删除成功', '提示', {
+              confirmButtonText: '确定',
+              callback: () => {
+                let tmp = []
+                this.fileList.forEach(sub => {
+                  if (sub.id === item.id) {
+                    return 
+                  }
+                  tmp.push(sub)
+                });
+                this.fileList = tmp
+              }
+            })
+          } else {
+            this.$notify.error({
+              title: '错误',
+              message: res.msg
+            })
+          }
+        })
+      }).catch(() => {
+        this.$message({
+          type: 'info',
+          message: '已取消删除'
+        })
+      })
+    },
+    async setCover(item){
+      let {id} = item
+      this.fileList.forEach((sub) => {
+          if (sub.id == id) {
+              sub.cover = true
+          }
+          else{
+              sub.cover = false
+          }
+      });
+    },
+    async examine(status) {
+      this.$confirm("请再次确认是否进行此审核操作?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      })
+        .then(async () => {
+          let result = await this.$http({
+            method: "post",
+            data: {
+              id: this.form.id,
+              reason: this.reason === 2 ? this.zidingyi : this.reason,
+              status,
+            },
+            headers: {
+              token: window.localStorage.getItem("token"),
+            },
+            url: `/manage/goods/audit`,
+          });
+
+          if (result.code === 0) {
+            this.$alert("审核成功", "提示", {
+              confirmButtonText: "确定",
+              callback: () => {
+                this.$router.back();
+              },
+            });
+          } else {
+            this.$notify.error({
+              title: "错误",
+              message: result.msg,
+            });
+          }
+        })
+        .catch(() => {
+          this.$message({
+            type: "info",
+            message: "已取消",
+          });
+        });
+    },
+    // reupload(item){
+    //   this.activeFlie = item
+    // },
+    // handleReloadSuccess(res){
+    //   this.loading.close()
+    //   let { data } = res;
+    //   let activeIdx = ''
+    //   this.fileList.forEach((sub,i) => {
+    //     if (sub.id == this.activeFlie.id) {
+    //       activeIdx = i
+    //     }
+    //   });
+
+    //   this.fileList.splice(activeIdx,1,{
+    //     url: data.urlPath,
+    //     name: data.fileName,
+    //     id: data.id,
+    //     cover:data.cover
+    //   });
+    // },
+    handlePictureCardSuccess(res){
+      this.currentIdx += 1
+      let cuidx = this.currentIdx
+      this.loading.close()
+      let { data } = res;
+      setTimeout(() => {
+        this.fileList.push({
+          url: data.urlPath,
+          name: data.fileName,
+          id: data.id,
+          cover:data.cover
+        }); 
+      }, 1000 + 100 * cuidx);
+    },
+    handleThumbSuccess(res) {
+      this.loading.close()
+      let { data } = res;
+      this.form.thumb = data.thumb;
+    },
+    handleError(){
+      this.$alert("上传失败,请稍后再试", "提示", {
+        confirmButtonText: "确定",
+        callback: () => {
+          this.loading.close()
+        },
+      });
+    },
+    beforeAvatarUpload(file) {
+      this.currentIdx = 0
+      let type = this.isImage(file.name);
+      if (!type) {
+        this.$message.error('只允许上传图片');
+        return type
+      }
+      const isLt2M = file.size / 1024 / 1024 < 20;
+      if (!isLt2M) {
+        this.$message.error("上传头像图片大小不能超过 20MB!");
+        return isLt2M;
+      }
+      this.showLoading()
+
+    },
+
+    handleSuccess(res) {
+      this.loading.close()
+      let { data } = res;
+      if (this.form.type !== 1) {
+        this.filesIds = []
+      }
+      this.filesIds.push(data)
+    },
+
+    beforeMultipleUpload(file){
+      let typeArr = this.form.type !== 2 ? ['.4dage'] : ['avi','mov','rmvb','rm','flv','mp4','3gp']
+      let type = this.isTypeBySend(file.name,typeArr);
+      if (!type) {
+        this.$message.error('只允许指定文件格式的文件');
+        return type
+      }
+      
+      let size = this.form.type === 2 ? 2000 : 500
+      const isLt500M = file.size / 1024 / 1024 < size;
+
+      if (!isLt500M) {
+        this.$message.error(`上传文件大小不能超过 ${size}MB!`);
+        return isLt500M;
+      }
+      this.showLoading()
+    },
+
+    
+    async postStatus(status){
+      let { id = "", thumb, title, fileUrl,year,description, type } = this.form;
+          let ids = type === 1 ? this.fileList : this.filesIds;
+          let thumbFileId = this.fileList.filter((item) => item.cover);
+          if(type === 1){
+            if(thumbFileId.length<=0){
+              return this.$notify.error({
+                title: "错误",
+                message: '请设置封面图片',
+              });
+            }
+          }
+          if(ids.length <= 0){
+            return this.$notify.error({
+              title: "错误",
+              message: `${this.collectType[type]}不能为空`,
+            });
+          }
+          let params = {
+            fileIds: ids.map((item) => item.id).join(","),
+            id,
+            description,
+            thumb,
+            title,
+            thumbFileId: thumbFileId.length > 0 ? thumbFileId[0].id : "",
+            type,
+            fileUrl,
+            status,
+            year
+          };
+          let result = await this.$http({
+            method: "post",
+            data: params,
+            headers: {
+              token: window.localStorage.getItem("token"),
+            },
+            url: `/manage/goods/save`,
+          });
+
+          if (result.code === 0) {
+            this.$alert("操作成功", "提示", {
+              confirmButtonText: "确定",
+              callback: () => {
+                this.$router.back();
+              },
+            });
+          } else {
+            this.$notify.error({
+              title: "错误",
+              message: result.msg,
+            });
+          }
+    },
+
+    save(status) {
+      this.$refs["partform"].validate(async (valid) => {
+        if (valid) {
+         if(status == 2){
+             this.$confirm("请再次确认是否进行此审核操作?", "提示", {
+                confirmButtonText: "确定",
+                cancelButtonText: "取消",
+                type: "warning",
+              }).then(async () => {
+                  this.postStatus(status)
+                  })
+                .catch(() => {
+                  this.$message({
+                    type: "info",
+                    message: "已取消",
+                  });
+                });
+          }
+          else{
+            this.postStatus(status)
+          }         
+        }
+      });
+    },
+
+    async getDetail() {
+      let result = await this.$http({
+        method: "get",
+        headers: {
+          token: window.localStorage.getItem("token")
+        },
+        url: `/manage/goods/detail/${this.id}`
+      });
+
+      this.form = result.data.entity;
+
+      this.form.year = this.form.year || 2020
+      this.display = Boolean(this.form.display)
+      this.filesIds = result.data.file;
+      this.fileList = result.data.file.map((item) => {
+        return {
+          name: item.fileName,
+          url: item.urlPath,
+          id: item.id,
+          cover: item.cover
+        };
+      });
+    }
+  },
+  mounted() {
+    if (this.id && this.id !== "none") {
+      this.getDetail();
+    }
+    else{
+      setTimeout(() => {
+        this.fileList = []
+      });
+    }
+  }
+};
+</script>
+
+<style lang="less" scoped>
+.edit-body {
+  background: #fff;
+  height: 100%;
+  .edit-arch {
+    overflow-y: auto;
+    overflow-x: hidden;
+    height: calc(100% - 8.5rem - 20px);
+    background-color: #fff;
+    padding: 20px 40px 20px 20px;
+  }
+  .btnclass {
+    text-align: center;
+    border: 1px solid #ccc;
+    padding: 10px 0;
+    width: calc(100% - 80px);
+    margin: 0 auto;
+  }
+}
+</style>
+<style lang="less">
+.avatar-uploader .el-upload {
+  border: 1px dashed #d9d9d9;
+  border-radius: 6px;
+  cursor: pointer;
+  position: relative;
+  overflow: hidden;
+}
+.avatar-uploader .el-upload:hover {
+  border-color: #409eff;
+}
+.avatar-uploader-icon {
+  font-size: 28px;
+  color: #8c939d;
+  width: 178px;
+  height: 178px;
+  line-height: 178px;
+  text-align: center;
+}
+.avatar {
+  width: 178px;
+  /* height: 178px; */
+  display: block;
+}
+.el-upload-list--picture-card .el-upload-list__item{
+  overflow: visible!important;
+  margin-top: 40px;
+}
+
+.el-upload--picture-card{
+  margin-top: 40px;
+}
+
+.card-con{
+  height: 100%;
+  position: relative;
+  .btn-con{
+    position: absolute;
+    justify-content: center;
+    bottom: -40px;
+    display: flex;
+    width: 100%;
+    .queren{
+      margin-right: 4px!important;
+    }
+    .el-button--mini, .el-button--mini.is-round{
+      padding: 7px 10px!important;
+    }
+  }
+
+  .cover{
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    background-color: rgba(0, 0, 0, 0.4);
+    width: 100%;
+    padding: 4px 0;
+    color: #fff;
+    text-align: center;
+    pointer-events: none;
+  }
+}
+</style>

+ 447 - 0
backstage/src/pages/edit/dynamic.vue

@@ -0,0 +1,447 @@
+<!--  -->
+<template>
+  <div>
+    <main-top :crumb="crumbData" :status="form.status">
+      <div slot="con" class="con-btn">
+        <el-button type="primary" @click="back">返回</el-button>
+      </div>
+    </main-top>
+    <div class="reason" v-if="form.status===3">审核不通过原因:{{form.reason}}</div>
+    <div class="edit-body">
+      <div
+        class="edit-arch"
+        :style="{height: form.status === 3?`calc(100% - 11.5rem - 20px)`:`calc(100% - 8.5rem - 20px)`}"
+      >
+        <div class="title">基本信息:</div>
+        <el-form
+          ref="partform"
+          :rules="rules"
+          :model="form"
+          label-width="120px"
+          :disabled="form.status===2||form.status===4"
+        >
+          <el-form-item label="动态类型" prop="type">
+            <el-radio-group v-model="form.type">
+              <el-radio :label="'news'">新闻</el-radio>
+              <el-radio :label="'notice'">公告</el-radio>
+            </el-radio-group>
+          </el-form-item>
+
+          <el-form-item label="标题" prop="title">
+            <el-input
+              placeholder="请输入标题"
+              :maxlength="FONTLENGTH"
+              show-word-limit
+              v-model="form.title"
+            ></el-input>
+          </el-form-item>
+
+          <el-form-item label="作者" prop="author">
+            <el-input
+              placeholder="请输入作者"
+              :maxlength="30"
+              show-word-limit
+              v-model="form.author"
+            ></el-input>
+          </el-form-item>
+
+          <el-form-item label="封面图片">
+            <el-upload
+              :class="{'noCursor':(($role=='sys_admin'||$role=='sys_high')&&(form.status===2||form.status===4))}"
+              class="avatar-uploader"
+              :action="uploadUrl"
+              :headers="{
+                token,
+              }"
+              :show-file-list="false"
+              :on-success="handleThumbSuccess"
+              :on-error="handleError"
+              :before-upload="beforeAvatarUpload"
+            >
+              <img v-if="form.img" :src="form.img" class="avatar" />
+              <i v-else class="el-icon-plus avatar-uploader-icon"></i>
+            </el-upload>
+            <span
+              style="color:#999"
+              v-if="!(($role=='sys_admin'||$role=='sys_high')&&(form.status===2||form.status===4))"
+            >支持png、jpg、gif和jpeg的图片格式;最大支持20M。</span>
+          </el-form-item>
+
+          <el-form-item label="单位">
+            <el-input
+              type="textarea"
+              :maxlength="MAXLENGTH * 0.25"
+              show-word-limit
+              v-model="form.description"
+            ></el-input>
+          </el-form-item>
+        </el-form>
+        <el-form label-width="120px" v-if="$role=='sys_admin'||$role=='sys_high'">
+          <el-form-item
+            v-if="($role=='sys_admin'||$role=='sys_high')&&(form.status===2||form.status===4)"
+            label="显示设置"
+          >
+            <el-switch
+              v-model="display"
+              inactive-color="#ccc"
+              @change="changeState({id:form.id,display:display})"
+            ></el-switch>
+          </el-form-item>
+        </el-form>
+
+        <div class="main-title">
+          <div class="title">信息正文:</div>
+          <div class="text-con" v-if="form.status===2||form.status===4" v-html="form.content"></div>
+          <!-- <Editor @onchange="data=>{form.content = data}" v-else :content="form.content"></Editor> -->
+          <tinymce
+            v-else
+            ref="tinymce"
+            :height="500"
+            :htmlstr="form.content"
+            @blur="handleBlur"
+            />
+        </div>
+      </div>
+      <div class="btnclass" v-if="form.status !== 4">
+        <template v-if="form.status!==2">
+          <el-button @click="save(1)">保存</el-button>
+          <el-button type="primary" @click="save(2)">提交</el-button>
+        </template>
+        <template v-else>
+          <template v-if="$role=='sys_admin'||$role=='sys_high'">
+            <el-button @click="dialogFormVisible = true">审核不通过</el-button>
+            <el-button type="primary" @click="()=>{reason='',examine(4)}">审核通过</el-button>
+          </template>
+        </template>
+      </div>
+    </div>
+
+    <el-dialog title="审核不通过" :visible.sync="dialogFormVisible">
+      <el-form :model="form">
+        <el-form-item
+          label="选择原因"
+          :rules="[
+          { required: true, message: '选择原因不能为空'},
+        ]"
+        >
+          <el-radio-group v-model="reason">
+            <el-radio :label="'信息内容待调整'">信息内容待调整</el-radio>
+            <el-radio :label="'标题需进行修改'">标题需进行修改</el-radio>
+            <el-radio :label="2">自定义</el-radio>
+          </el-radio-group>
+          <el-input
+            v-model="zidingyi"
+            v-if="reason===2"
+            type="textarea"
+            :autosize="{ minRows: 2, maxRows: 4}"
+          ></el-input>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="dialogFormVisible = false">取 消</el-button>
+        <el-button type="primary" @click="examine(3)">确 定</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+
+import MainTop from "@/components/main-top";
+// import Editor from "@/components/Editor";
+import Tinymce from "@/components/Tinymce/index.vue";
+
+
+import { changeState } from "@/util/commonfn.js";
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    MainTop,
+    // Editor,
+    Tinymce
+  },
+  computed: {
+    crumbData() {
+      return [
+        {
+          name: "史馆动态信息",
+          id: 0,
+        },
+        {
+          name: this.$route.params.type
+            ? this.form.status !== 2 || this.form.status !== 4
+              ? "史馆动态"
+              : "编辑史馆动态"
+            : "新增史馆动态",
+          id: 1,
+        },
+      ];
+    },
+  },
+  data() {
+    return {
+      reason: "信息内容待调整",
+      zidingyi: "",
+      dialogFormVisible: false,
+      display: true,
+      form: {
+        content: "",
+        description: "",
+        display: false,
+        id: "",
+        img: "",
+        thumb: "",
+        author:"",
+        title: "",
+        type: "news",
+      },
+      type: this.$route.params.type,
+      id: this.$route.params.id,
+      token: window.localStorage.getItem("token"),
+      uploadUrl: `${this.$serverName}manage/file/upload`,
+      rules: {
+        type: [{ required: true, message: "请选择动态类型", trigger: "blur" }],
+        author: [{ required: true, message: "请输入作者", trigger: "blur" }],
+        title: [{ required: true, message: "请输入标题", trigger: "blur" }],
+        img: [{ required: true, message: "请上传封面图片", trigger: "blur" }],
+      },
+    };
+  },
+  methods: {
+    handleBlur(data){
+      this.form.content = data
+    },
+    getContent(data){
+      console.log(data);
+    },
+    back() {
+      if(this.form.status===2){
+          this.$router.back();
+      }
+      else{
+        this.$confirm("点击返回后, 编辑的信息将无法保存,是否继续?", "提示", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
+        }).then(() => {
+          this.$router.back();
+        });
+      }
+    },
+    changeState(item) {
+      changeState(item, "news");
+    },
+    handleError(){
+      this.$alert("上传失败,请稍后再试", "提示", {
+        confirmButtonText: "确定",
+        callback: () => {
+          this.loading.close()
+        },
+      });
+    },
+    handleThumbSuccess(res) {
+      this.loading.close();
+      let { data } = res;
+
+      this.form.img = data.urlPath;
+      this.form.thumb = data.thumb;
+    },
+    beforeAvatarUpload(file) {
+      let type = this.isImage(file.name);
+      if (!type) {
+        this.$message.error("只允许上传图片");
+        return type;
+      }
+      const isLt2M = file.size / 1024 / 1024 < 20;
+
+      if (!isLt2M) {
+        this.$message.error("上传头像图片大小不能超过 20MB!");
+        return isLt2M;
+      }
+      this.loading = this.$loading(this.loadOption);
+    },
+
+    async examine(status) {
+      this.$confirm("请再次确认是否进行此审核操作?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      })
+        .then(async () => {
+          let result = await this.$http({
+            method: "post",
+            data: {
+              id: this.form.id,
+              reason: this.reason === 2 ? this.zidingyi : this.reason,
+              status,
+            },
+            headers: {
+              token: window.localStorage.getItem("token"),
+            },
+            url: `/manage/news/audit`,
+          });
+
+          if (result.code === 0) {
+            this.$alert("审核成功", "提示", {
+              confirmButtonText: "确定",
+              callback: () => {
+                this.$router.back();
+              },
+            });
+          } else {
+            this.$notify.error({
+              title: "错误",
+              message: result.msg,
+            });
+          }
+        })
+        .catch(() => {
+          this.$message({
+            type: "info",
+            message: "已取消",
+          });
+        });
+    },
+
+    async postStatus(status){
+        let {
+            id = "",
+            content,
+            description,
+            display,
+            img,
+            thumb,
+            author,
+            title,
+            type,
+          } = this.form;
+          let params = {
+            id,
+            content,
+            description,
+            display: display * 1,
+            img,
+            thumb,
+            author,
+            title,
+            type,
+            status,
+          };
+          let result = await this.$http({
+            method: "post",
+            data: params,
+            headers: {
+              token: window.localStorage.getItem("token"),
+            },
+            url: `/manage/news/save`,
+          });
+
+          if (result.code === 0) {
+            this.$alert("操作成功", "提示", {
+              confirmButtonText: "确定",
+              callback: () => {
+                this.$router.back();
+              },
+            });
+          } else {
+            this.$notify.error({
+              title: "错误",
+              message: result.msg,
+            });
+          }
+
+    },
+    save(status) {
+      this.$refs["partform"].validate(async (valid) => {
+        if (valid) {
+          if(status == 2){
+             this.$confirm("请再次确认是否进行此审核操作?", "提示", {
+                confirmButtonText: "确定",
+                cancelButtonText: "取消",
+                type: "warning",
+              }).then(async () => {
+                  this.postStatus(status)
+                  })
+                .catch(() => {
+                  this.$message({
+                    type: "info",
+                    message: "已取消",
+                  });
+                });
+          }
+          else{
+            this.postStatus(status)
+          }         
+        }
+      });
+    },
+    async getDetail() {
+      let result = await this.$http({
+        method: "get",
+        headers: {
+          token: window.localStorage.getItem("token"),
+        },
+        url: `/manage/news/detail/${this.id}`,
+      });
+
+      this.form = result.data;
+      this.display = Boolean(this.form.display);
+      this.form["display"] = Boolean(this.form["display"]);
+    },
+  },
+  mounted() {
+    if (this.id && this.id !== "none") {
+      this.getDetail();
+    }
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.edit-body {
+  background: #fff;
+  height: 100%;
+  .edit-arch {
+    overflow-y: auto;
+    overflow-x: hidden;
+    height: calc(100% - 11.5rem - 20px);
+    background-color: #fff;
+    padding: 20px 40px 20px 20px;
+  }
+
+  .btnclass {
+    text-align: center;
+    border: 1px solid #ccc;
+    padding: 10px 0;
+    width: calc(100% - 80px);
+    margin: 0 auto;
+  }
+}
+</style>
+<style>
+.avatar-uploader .el-upload {
+  border: 1px dashed #d9d9d9;
+  border-radius: 6px;
+  cursor: pointer;
+  position: relative;
+  overflow: hidden;
+}
+.avatar-uploader .el-upload:hover {
+  border-color: #409eff;
+}
+.avatar-uploader-icon {
+  font-size: 28px;
+  color: #8c939d;
+  width: 178px;
+  height: 178px;
+  line-height: 178px;
+  text-align: center;
+}
+.avatar {
+  width: 178px;
+  /* height: 178px; */
+  display: block;
+}
+</style>

+ 422 - 0
backstage/src/pages/edit/spirit.vue

@@ -0,0 +1,422 @@
+<!--  -->
+<template>
+  <div>
+    <main-top :crumb="crumbData" :status="form.status">
+      <div slot="con" class="con-btn">
+        <el-button type="primary" @click="back">返回</el-button>
+      </div>
+    </main-top>
+    <div class="reason" v-if="form.status===3">审核不通过原因:{{form.reason}}</div>
+    <div class="edit-body">
+      <div
+        class="edit-arch"
+        :style="{height: form.status === 3?`calc(100% - 11.5rem - 20px)`:`calc(100% - 8.5rem - 20px)`}"
+      >
+        <div class="title">基本信息:</div>
+        <el-form
+          ref="partform"
+          :rules="rules"
+          :model="form"
+          label-width="120px"
+          :disabled="form.status===2||form.status===4"
+        >
+          <el-form-item label="标题" prop="title">
+            <el-input
+              placeholder="请输入标题"
+              :maxlength="FONTLENGTH"
+              show-word-limit
+              v-model="form.title"
+            ></el-input>
+          </el-form-item>
+
+          <el-form-item label="归属单位" prop="title">
+            <el-input
+              placeholder="请输入归属单位"
+              :maxlength="FONTLENGTH * 2"
+              show-word-limit
+              v-model="form.unit"
+            ></el-input>
+          </el-form-item>
+
+          <el-form-item label="作者" prop="author">
+            <el-input
+              placeholder="请输入作者"
+              :maxlength="30"
+              show-word-limit
+              v-model="form.author"
+            ></el-input>
+          </el-form-item>
+
+          <el-form-item label="封面图片" prop="img">
+            <el-upload
+              class="avatar-uploader"
+              :class="{'noCursor':(($role=='sys_admin'||$role=='sys_high')&&(form.status===2||form.status===4))}"
+              :action="uploadUrl"
+              :headers="{
+                token,
+              }"
+              :show-file-list="false"
+              :on-success="handleThumbSuccess"
+              :on-error="handleError"
+              :before-upload="beforeAvatarUpload"
+            >
+              <img v-if="form.img" :src="form.img" class="avatar" />
+              <i v-else class="el-icon-plus avatar-uploader-icon"></i>
+            </el-upload>
+            <span
+              style="color:#999"
+              v-if="!(($role=='sys_admin'||$role=='sys_high')&&(form.status===2||form.status===4))"
+            >支持png、jpg、gif和jpeg的图片格式;最大支持20M。</span>
+          </el-form-item>
+        </el-form>
+        <el-form label-width="120px" v-if="$role=='sys_admin'||$role=='sys_high'">
+          <el-form-item
+            v-if="($role=='sys_admin'||$role=='sys_high')&&(form.status===2||form.status===4)"
+            label="显示设置"
+          >
+            <el-switch
+              v-model="display"
+              inactive-color="#ccc"
+              @change="changeState({id:form.id,display:display})"
+            ></el-switch>
+          </el-form-item>
+        </el-form>
+
+        <div class="main-title">
+          <div class="title">信息正文:</div>
+          <div class="text-con" v-if="form.status===2||form.status===4" v-html="form.content"></div>
+          <tinymce
+            v-else
+            ref="tinymce"
+            :height="500"
+            :htmlstr="form.content"
+            @blur="handleBlur"
+            />
+        </div>
+      </div>
+      <div class="btnclass" v-if="form.status !== 4">
+        <template v-if="form.status!==2">
+          <el-button @click="save(1)">保存</el-button>
+          <el-button type="primary" @click="save(2)">提交</el-button>
+        </template>
+        <template v-else>
+          <template v-if="$role=='sys_admin'||$role=='sys_high'">
+            <el-button @click="dialogFormVisible = true">审核不通过</el-button>
+            <el-button type="primary" @click="()=>{reason='',examine(4)}">审核通过</el-button>
+          </template>
+        </template>
+      </div>
+    </div>
+
+    <el-dialog title="审核不通过" :visible.sync="dialogFormVisible">
+      <el-form :model="form">
+        <el-form-item
+          label="选择原因"
+          :rules="[
+          { required: true, message: '选择原因不能为空'},
+        ]"
+        >
+          <el-radio-group v-model="reason">
+            <el-radio :label="'信息内容待调整'">信息内容待调整</el-radio>
+            <el-radio :label="'标题需进行修改'">标题需进行修改</el-radio>
+            <el-radio :label="2">自定义</el-radio>
+          </el-radio-group>
+          <el-input
+            v-model="zidingyi"
+            v-if="reason===2"
+            type="textarea"
+            :autosize="{ minRows: 2, maxRows: 4}"
+          ></el-input>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="dialogFormVisible = false">取 消</el-button>
+        <el-button type="primary" @click="examine(3)">确 定</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+
+import MainTop from "@/components/main-top";
+import Tinymce from "@/components/Tinymce/index.vue";
+
+import { changeState } from "@/util/commonfn.js";
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    MainTop,
+    Tinymce
+  },
+  data() {
+    const crumbData = [
+      {
+        name: "特有精神信息",
+        id: 0,
+      },
+      {
+        name: this.$route.params.type ? "编辑特有精神" : "新增特有精神",
+        id: 1,
+      },
+    ];
+
+    return {
+      crumbData,
+      reason: "信息内容待调整",
+      zidingyi: "",
+      dialogFormVisible: false,
+      form: {
+        content: "",
+        display: false,
+        author:"",
+        id: "",
+        img: "",
+        thumb: "",
+        title: "",
+        unit: "",
+      },
+      display: true,
+      type: this.$route.params.type,
+      id: this.$route.params.id,
+      token: window.localStorage.getItem("token"),
+      uploadUrl: `${this.$serverName}manage/file/upload`,
+      rules: {
+        title: [{ required: true, message: "请输入标题", trigger: "blur" }],
+        author: [{ required: true, message: "请输入作者", trigger: "blur" }],
+        unit: [{ required: true, message: "请输入归属单位", trigger: "blur" }],
+        img: [{ required: true, message: "请上传封面图片", trigger: "blur" }],
+      },
+    };
+  },
+  methods: {
+    handleBlur(data){
+      this.form.content = data
+    },
+    back() {
+      if(this.form.status===2){
+          this.$router.back();
+      }
+      else{
+        this.$confirm("点击返回后, 编辑的信息将无法保存,是否继续?", "提示", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
+        }).then(() => {
+          this.$router.back();
+        });
+      }
+    },
+    changeState(item) {
+      changeState(item, "spirit");
+    },
+    handleError(){
+      this.$alert("上传失败,请稍后再试", "提示", {
+        confirmButtonText: "确定",
+        callback: () => {
+          this.loading.close()
+        },
+      });
+    },
+    handleThumbSuccess(res) {
+      this.loading.close();
+      let { data } = res;
+      this.form.thumb = data.thumb;
+      this.form.img = data.urlPath;
+    },
+    beforeAvatarUpload(file) {
+      let type = this.isImage(file.name);
+      if (!type) {
+        this.$message.error("只允许上传图片");
+        return type;
+      }
+      const isLt2M = file.size / 1024 / 1024 < 20;
+
+      if (!isLt2M) {
+        this.$message.error("上传头像图片大小不能超过 20MB!");
+        return isLt2M;
+      }
+      this.loading = this.$loading(this.loadOption);
+    },
+
+    async examine(status) {
+      this.$confirm("请再次确认是否进行此审核操作?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      })
+        .then(async () => {
+          let result = await this.$http({
+            method: "post",
+            data: {
+              id: this.form.id,
+              reason: this.reason === 2 ? this.zidingyi : this.reason,
+              status,
+            },
+            headers: {
+              token: window.localStorage.getItem("token"),
+            },
+            url: `/manage/spirit/audit`,
+          });
+
+          if (result.code === 0) {
+            this.$alert("审核成功", "提示", {
+              confirmButtonText: "确定",
+              callback: () => {
+                this.$router.back();
+              },
+            });
+          } else {
+            this.$notify.error({
+              title: "错误",
+              message: result.msg,
+            });
+          }
+        })
+        .catch(() => {
+          this.$message({
+            type: "info",
+            message: "已取消",
+          });
+        });
+    },
+
+    async postStatus(status){
+          let {
+            id = "",
+            content,
+            display,
+            img,
+            thumb,
+            title,
+            author,
+            unit,
+          } = this.form;
+          let params = {
+            id,
+            content,
+            unit,
+            display: display * 1,
+            img,
+            author,
+            thumb,
+            title,
+            status,
+          };
+          let result = await this.$http({
+            method: "post",
+            data: params,
+            headers: {
+              token: window.localStorage.getItem("token"),
+            },
+            url: `/manage/spirit/save`,
+          });
+
+          if (result.code === 0) {
+            this.$alert("操作成功", "提示", {
+              confirmButtonText: "确定",
+              callback: () => {
+                this.$router.back();
+              },
+            });
+          } else {
+            this.$notify.error({
+              title: "错误",
+              message: result.msg,
+            });
+          }
+    },
+
+    save(status) {
+      this.$refs["partform"].validate(async (valid) => {
+        if (valid) {
+          if(status == 2){
+             this.$confirm("请再次确认是否进行此审核操作?", "提示", {
+                confirmButtonText: "确定",
+                cancelButtonText: "取消",
+                type: "warning",
+              }).then(async () => {
+                  this.postStatus(status)
+                  })
+                .catch(() => {
+                  this.$message({
+                    type: "info",
+                    message: "已取消",
+                  });
+                });
+          }
+          else{
+            this.postStatus(status)
+          }      
+        }
+      });
+    },
+    async getDetail() {
+      let result = await this.$http({
+        method: "get",
+        headers: {
+          token: window.localStorage.getItem("token"),
+        },
+        url: `/manage/spirit/detail/${this.id}`,
+      });
+
+      this.form = result.data;
+      this.display = Boolean(this.form.display);
+      this.form["display"] = Boolean(this.form["display"]);
+    },
+  },
+  mounted() {
+    if (this.id && this.id !== "none") {
+      this.getDetail();
+    }
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.edit-body {
+  background: #fff;
+  height: 100%;
+  .edit-arch {
+    overflow-y: auto;
+    overflow-x: hidden;
+    height: calc(100% - 8.5rem - 20px);
+    background-color: #fff;
+    padding: 20px 40px 20px 20px;
+  }
+  .btnclass {
+    text-align: center;
+    border: 1px solid #ccc;
+    padding: 10px 0;
+    width: calc(100% - 80px);
+    margin: 0 auto;
+  }
+}
+</style>
+<style>
+.avatar-uploader .el-upload {
+  border: 1px dashed #d9d9d9;
+  border-radius: 6px;
+  cursor: pointer;
+  position: relative;
+  overflow: hidden;
+}
+.avatar-uploader .el-upload:hover {
+  border-color: #409eff;
+}
+.avatar-uploader-icon {
+  font-size: 28px;
+  color: #8c939d;
+  width: 178px;
+  height: 178px;
+  line-height: 178px;
+  text-align: center;
+}
+.avatar {
+  width: 178px;
+  /* height: 178px; */
+  display: block;
+}
+</style>

+ 36 - 0
backstage/src/pages/home/index.vue

@@ -0,0 +1,36 @@
+<template>
+  <div>
+    <div class="home-layout">
+      <video src="https://4d-tjw.oss-cn-shenzhen.aliyuncs.com/shls_museum/video/Final_new_web.mp4" autoplay loop></video>
+    </div>
+  </div>
+</template>
+
+<script>
+
+const crumbData = [
+  {
+    name: '首页',
+    id: 1
+  }
+]
+export default {
+  data(){
+    return {
+      crumbData
+    }
+  }
+}
+</script>
+
+
+<style lang="less" scoped>
+.home-layout{
+  overflow-y: auto;
+  overflow-x: hidden;
+  height: 100%;
+  video{
+    width: 100%;
+  }
+}
+</style>

+ 135 - 0
backstage/src/pages/layout/aside.vue

@@ -0,0 +1,135 @@
+<!--  -->
+<template>
+<div class="aside">
+  <div class="aside-list">
+    <div class="aside-item">
+      <div :class="{active:activeIdx>=2&&activeIdx<=6}"><i class="iconfont iconsys_nav_work"></i>内容管理</div>
+      <div @click="go(2,'/dynamic')" :class="{activeFont:activeIdx === 2}">场景管理</div>
+      <div @click="go(3,'/spirit')" :class="{activeFont:activeIdx === 3}">文物管理</div>
+    </div>
+    <div class="aside-item">
+      <div :class="{active:activeIdx>=7&&activeIdx<=11}"><i class="iconfont iconsys_nav_system"></i>系统管理</div>
+      <template v-if="$role=='sys_admin'">
+        <div @click="go(7,'/menu')" :class="{activeFont:activeIdx === 7}">展区名称管理</div>
+        <div @click="go(10,'/worklog')" :class="{activeFont:activeIdx === 10}">日志管理</div>
+      </template>
+      <div @click="go(11,'/password')" :class="{activeFont:activeIdx === 11}">密码修改</div>
+    </div>
+  </div>
+</div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {},
+  data () {
+    // 这里存放数据
+    return {
+      // activeIdx: 0
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {
+    activeIdx: {
+      get: function () {
+        return this.$route.meta.index
+      },
+      set: function () {
+      }
+    }
+  },
+  // 监控data中的数据变化
+  watch: {
+
+  },
+  // 方法集合
+  methods: {
+    go (id, url) {
+      this.activeIdx = this.$route.meta.index
+      this.$router.push(url)
+    }
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created () {
+
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {
+  }
+
+}
+</script>
+
+<style scoped>
+.aside-list{
+  width: 100%;
+  padding-top: 1.5rem;
+}
+.aside-item {
+  padding: .625rem 0;
+  text-align: center;
+}
+
+
+.aside-item:last-child{
+  border-bottom:none;
+}
+
+.aside-item div{
+  height: 2.875rem;
+  line-height: 2.875rem;
+  margin: .0625rem 0;
+  width: 89%;
+  padding-left: 5.7rem;
+  font-size: .875rem;
+  cursor: pointer;
+  color: #707070;
+  text-align: left;
+}
+.aside-item div:not(:first-child):hover{
+  color: #B63C25;
+}
+
+.aside-item div:first-child{
+  padding-left: 3.81rem;
+  display: flex;
+  align-items: center;
+  color: #532F1C;
+  font-size: 1.25rem;
+  font-weight: bold;
+  margin-bottom: 0.2rem;
+}
+
+.aside-item div:first-child img{
+  width: 1.1rem;
+  height: 1.1rem;
+  margin-right: .625rem;
+}
+
+.aside-item .iconfont{
+  color: #532F1C;
+  margin-right: .8125rem;
+}
+
+.aside-item .active{
+  background-color: #B63C25;
+  border-top-right-radius: 80px;
+  border-bottom-right-radius: 80px;
+  color: #F2ECDE!important;
+}
+
+.aside-item .active .iconfont{
+  color: #F2ECDE;
+}
+
+
+.aside-item .activeFont{
+  color: #B63C25;
+}
+
+
+</style>

+ 47 - 0
backstage/src/pages/layout/footer.vue

@@ -0,0 +1,47 @@
+<!--  -->
+<template>
+<div class=''></div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+
+export default {
+// import引入的组件需要注入到对象中才能使用
+  components: {},
+  data () {
+    // 这里存放数据
+    return {
+
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {},
+  // 监控data中的数据变化
+  watch: {},
+  // 方法集合
+  methods: {
+
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created () {
+
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {
+
+  },
+  beforeCreate () {}, // 生命周期 - 创建之前
+  beforeMount () {}, // 生命周期 - 挂载之前
+  beforeUpdate () {}, // 生命周期 - 更新之前
+  updated () {}, // 生命周期 - 更新之后
+  beforeDestroy () {}, // 生命周期 - 销毁之前
+  destroyed () {}, // 生命周期 - 销毁完成
+  activated () {} // 如果页面有keep-alive缓存功能,这个函数会触发
+}
+</script>
+<style scoped>
+/* 引入公共css类 */
+
+</style>

+ 146 - 0
backstage/src/pages/layout/head.vue

@@ -0,0 +1,146 @@
+<!--  -->
+<template>
+<div class='header card'>
+  <div class="header-title">
+    <img src="@/assets/img/logo.png" alt="">
+    <span>数字史馆管理系统</span>
+  </div>
+  <div class="header-user">
+    <div class="avatars">
+      <span>{{userName}}</span>
+    </div>
+    <div @click="logout" class="logout"><img src="@/assets/img/logout.png" alt="">退出</div>
+  </div>
+</div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {},
+  data () {
+    return {
+      head: '',
+      userName:''
+    }
+  },
+  // 监控data中的数据变化
+  watch: {},
+  // 方法集合
+  methods: {
+    logout () {
+      this.$http({
+        method: 'get',
+        url: '/admin/logout',
+        headers: {
+          token: window.localStorage.getItem('token')
+        }
+      }).then(res => {
+        if (res.code === 0) {
+          window.localStorage.setItem('token', '')
+          window.localStorage.setItem('role', '')
+
+          this.$alert('退出成功', '提示', {
+            confirmButtonText: '确定',
+            callback: () => {
+              window.localStorage.setItem('userInfo', '')
+              this.$router.push('/login')
+            }
+          })
+        }
+      })
+    }
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created () {
+
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {
+    let userInfo = JSON.parse(window.localStorage.getItem('userInfo')) || ''
+    this.userName = userInfo.nickName
+    this.head = userInfo.img
+
+    
+    // this.updateInfo()
+  },
+  beforeCreate () {}, // 生命周期 - 创建之前
+  beforeMount () {}, // 生命周期 - 挂载之前
+  beforeUpdate () {}, // 生命周期 - 更新之前
+  updated () {}, // 生命周期 - 更新之后
+  beforeDestroy () {}, // 生命周期 - 销毁之前
+  destroyed () {}, // 生命周期 - 销毁完成
+  activated () {} // 如果页面有keep-alive缓存功能,这个函数会触发
+}
+</script>
+
+<style scoped>
+.header {
+  height: 4.25rem;
+  width: 100%;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  line-height: 4.25rem;
+  padding: 0 1.875rem;
+  box-sizing: border-box;
+  background-color: #B63C25!important;
+}
+
+.header-title {
+  color: #fff;
+  font-size: 1.25rem;
+  text-align: center;
+  vertical-align: middle;
+}
+.header-title img{
+  vertical-align: middle;
+  max-width: 50px;
+  margin-right: 4px;
+}
+.header-title span{
+  vertical-align: middle;
+  display: inline-block;
+  font-size: 24px;
+  letter-spacing: 2px;
+  font-weight: bold;
+}
+.header-user {
+  display: flex;
+  align-items: center;
+}
+
+.header-user .logout img {
+  width: 20px;
+  height: 20px;
+  margin-right: 10px;
+}
+
+.header-user .logout {
+  display: flex;
+  align-items: center;
+  cursor: pointer;
+  color: #fff;
+}
+
+.header-user .avatars{
+  display: flex;
+  align-items: center;
+  height: 3rem;
+  margin-right: 10px;
+  color: #fff;
+}
+
+.header-user .avatars span{
+  margin-left: 10px;
+}
+
+.header-user .avatars img{
+  width:3rem;
+  height:3rem;
+  border-radius:50%;
+}
+</style>

+ 56 - 0
backstage/src/pages/layout/index.vue

@@ -0,0 +1,56 @@
+<template>
+<div class="body theme" >
+  <Head />
+  <div class="aside-con card">
+    <Aside />
+  </div>
+    <router-view class="main-view" />
+  <Footer />
+</div>
+</template>
+
+<script>
+import Head from './head'
+import Footer from './footer'
+import Aside from './aside'
+
+export default {
+  components: {
+    Head,
+    Aside,
+    Footer
+  },
+  computed: {
+    userId () {
+      return window.localStorage.getItem('userInfo')
+    }
+  },
+  mounted () {
+    if (!this.userId) {
+      this.$router.push('/login')
+    }
+  }
+}
+</script>
+
+<style scoped>
+.body{
+  width: 100%;
+  height: 100%;
+}
+.aside-con {
+  width: 15rem;
+  position: fixed;
+  height: calc(100% - 8rem);
+  margin: 1.875rem 1.25rem 1.875rem 1.875rem;
+  border-radius: .625rem;
+  overflow: hidden;
+  background-color: #ccc;
+}
+.main-view{
+  margin: 1.875rem 1.875rem 0 18.25rem;
+  width: calc(100% - 20.125rem);
+  height: calc(100% - 8rem);
+  overflow: hidden;
+}
+</style>

+ 224 - 0
backstage/src/pages/login/index.vue

@@ -0,0 +1,224 @@
+<!--  -->
+<template>
+<div class='layout'>
+  <div class="layout-con">
+    <div class="logo">
+      <img :src="require('@/assets/img/logo.png')" alt="">
+      <span></span>
+    </div>
+    <img class="bg" :src="require('@/assets/img/bg.jpg')" alt="">
+    <div class="middle">
+      <div class="middle-left">
+        <div>数字史馆管理系统</div>
+      </div>
+      <div class="middle-right">
+        <el-form class="middle-form" :rules="ruleLogin" status-icon :model="formLogin" ref="formLogin">
+          <div>欢迎登录</div>
+          <el-form-item prop="username">
+            <el-input v-model="formLogin.username" placeholder="账号"></el-input>
+          </el-form-item>
+          <el-form-item prop="password">
+            <el-input @keyup.enter.native="submitForm('formLogin')" v-model="formLogin.password" placeholder="密码" type="password"></el-input>
+          </el-form-item>
+          <el-form-item>
+            <el-button  @click="submitForm('formLogin')" type="primary" >登录</el-button>
+          </el-form-item>
+        </el-form>
+      </div>
+    </div>
+  </div>
+</div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+import { encodeStr } from '@/util'
+import { Base64 } from 'js-base64'
+
+import Vue from 'vue'
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {},
+  data () {
+    var checkUsername = (rule, value, callback) => {
+      if (!value) {
+        return callback(new Error('账号不能为空'))
+      } else {
+        callback()
+      }
+    }
+    var validatePass = (rule, value, callback) => {
+      if (value === '') {
+        callback(new Error('请输入密码'))
+      } else {
+        callback()
+      }
+    }
+    // 这里存放数据
+    return {
+      formLogin: {
+        username: '',
+        password: '',
+        region: 'high'
+      },
+      ruleLogin: {
+        username: [
+          { validator: checkUsername, trigger: 'blur' }
+        ],
+        password: [
+          { validator: validatePass, trigger: 'blur' }
+        ]
+      }
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {
+    userId () {
+      return window.localStorage.getItem('userInfo')
+    }
+  },
+  // 监控data中的数据变化
+  watch: {},
+  // 方法集合
+  methods: {
+    submitForm (formName) {
+      this.$refs[formName].validate((valid) => {
+        console.log(encodeStr(Base64.encode(this.formLogin.password)));
+        let data = {
+          userName: this.formLogin.username,
+          password: this.formLogin.password,
+          from:'cms'
+        }
+        if (valid) {
+          this.$http.post('/admin/login', data).then(res => {
+            if (res.code === 0) {
+              window.localStorage.setItem('token', String(res.data.token))
+              window.localStorage.setItem('userInfo', JSON.stringify(res.data.user))
+              window.localStorage.setItem('role', res.data.user.role)
+              Vue.prototype.$role = res.data.user.role
+              this.$router.push('/')
+            } else {
+              this.$notify.error({
+                title: '错误',
+                message: res.msg
+              })
+            }
+          })
+        } else {
+          return false
+        }
+      })
+    }
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created () {
+
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {
+    if (this.userId) {
+      this.$router.push({path:'/'})
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+.layout{
+  overflow: hidden;
+  .layout-con{
+     width: 100%;
+     position: relative;
+     .logo{
+       position: absolute;
+       left: 35px;
+       top: 40px;
+       z-index: 999;
+       cursor: pointer;
+       height: auto;
+       display: flex;
+        align-items: center;
+        span{
+          color: #fff;
+          font-size: 24px;
+          font-weight: bold;
+        }
+     }
+    .bg{
+      width: 100%;
+      top: 50%;
+      left: 50%;
+      transform: translate(-50%,-50%);
+      z-index: 0;
+      position: fixed;
+      user-select: none;
+    }
+    .middle{
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      width: 800px;
+      min-height: 56vh;
+      position: absolute;
+      transform: translate(-50%,-50%);
+      left: 50%;
+      top: 50%;
+      .middle-left{
+        background: rgba(#9D362F, 0.7);
+        width: 50%;
+        height: 100%;
+        position: relative;
+        >div{
+          width: 100%;
+          text-align: center;
+          position: absolute;
+          top: 50%;
+          left: 50%;
+          transform: translate(-50%,-50%);
+          font-size: 32px;
+          text-shadow: 0 4px 6px rgba(0, 0, 0, 0.7);
+          color: #f2ecde;
+          line-height: 40px;
+        }
+        img{
+          position: absolute;
+          width: 100%;
+          left: 0;
+        }
+        .top{
+          top: 0;
+        }
+        .bottom{
+          bottom: 0;
+          transform: rotate(180deg);
+        }
+      }
+      .middle-right{
+        position: relative;
+        height: 100%;
+        background: rgba(#fff, 0.7);
+        width: 50%;
+        .middle-form{
+          width: 80%;
+          position: absolute;
+          top: 50%;
+          left: 50%;
+          transform: translate(-50%,-50%);
+          >div{
+            text-align: center;
+            margin-bottom: 50px;
+            font-size: 24px;
+          }
+        }
+      }
+    }
+  }
+}
+</style>
+
+<style scoped>
+.el-button--primary {
+  width: 100%;
+}
+</style>

+ 3 - 0
backstage/src/pages/login/style.css

@@ -0,0 +1,3 @@
+.el-button--primary {
+  width: 100%;
+}

+ 363 - 0
backstage/src/pages/show/parts.vue

@@ -0,0 +1,363 @@
+<!--  -->
+<template>
+  <div>
+    <main-top :crumb="crumbData">
+      <div slot="con" class="con-btn">
+        <el-button type="primary" @click="$router.back()">返回</el-button>
+      </div>
+    </main-top>
+      <div class="table-interface">
+        <div class="collection-con">
+          <div class="c-left">
+            <iframe v-if="part.displayModel == 0" :src="part.webSite" frameborder="0"></iframe>
+            <img v-else :src="part.ico" alt="">
+          </div>
+          <div class="c-right theme-color">
+            <div class="c-h1">{{part.name}}</div>
+            <p class="c-title">
+              <span>部件详细信息</span>
+              <i class="iconfont iconsys_edit" @click="goto"></i>
+            </p>
+            <p>部件类别:{{part.typeName}}</p>
+            <p>所属楼体:{{BLOCK[part.block]}}</p>
+            <p>部件位置:<span v-html="ZONE[part.zone]"></span></p>
+            <p>是否在程序显示:
+              <el-switch
+                @change="handleShow"
+                v-model="part.display">
+              </el-switch>
+            </p>
+            <p class="c-title" style="margin-bottom:0">
+              <span>部件介绍</span>
+            </p>
+            <p v-html="part.content||'暂无介绍'" style="margin-top:20px"></p>
+
+            <p class="c-title">
+              <span>多媒体资料</span>
+              <i class="iconfont iconsys_down" @click="apply(file)"></i>
+            </p>
+            
+            <div class="media-cls">
+              <p v-if="file.length===0" style="color:#999">暂无内容</p>
+              <p v-for="(sub,idx) in file" :key="idx">{{sub.fileName}}</p>
+            </div>
+          </div>
+        </div>
+      </div>
+          <el-dialog title="提交下载申请" :visible.sync="dialogFormVisible" width="40%">
+      <div class="dialog">
+        <p>请选择要下载的内容:</p>
+        <div class="content">
+            <el-checkbox-group v-model="checkList" @change='handleChange'>
+              <div v-for="(sub,idx) in activeFiles" :key="idx">
+                <el-checkbox :label="sub.id" :false-label="''" :true-label="''">{{sub.fileName}}</el-checkbox>
+              </div>
+            </el-checkbox-group>
+        </div>
+        <p>请填写下载用途:</p>
+        <div class="content textarea">
+          <el-input
+            type="textarea"
+            :rows="2"
+            placeholder="请输入内容"
+            v-model="textarea">
+          </el-input>
+        </div>
+        <p>请选择审核人:</p>
+        <ul class="content">
+          <li v-for="(item,i) in adminList" :key="i">
+            <el-radio v-model="radio" :label="item.id">{{item.userName}}</el-radio>
+          </li>
+        </ul>
+      </div>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="dialogFormVisible = false">取 消</el-button>
+        <el-button type="primary" @click="applyDownload">确 定</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+
+import MainTop from '@/components/main-top'
+
+const crumbData = [
+  {
+    name: '部件信息',
+    id: 0
+  },
+  {
+    name: '查看详情',
+    id: 1
+  }
+]
+
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    MainTop
+  },
+  data () {
+    return {
+      crumbData,
+      id: this.$route.params.id,
+      type: 'picture',
+      realType: '',
+      file:'',
+      part:'',
+      value1:true,      
+      dialogFormVisible: false,
+      checkList: [],
+      textarea:'',
+      activeFiles:'',
+      adminList:[],
+      radio:''
+    }
+  },
+  methods: {
+     handleChange(){
+    },
+    async getDetail(){
+      let result = await this.$http({
+        method: 'get',
+        headers: {
+          token: window.localStorage.getItem('token')
+        },
+        url: `/manage/part/detail/${this.id}`
+      })
+
+      this.dialogFormVisible = false
+      this.file = result.data.file
+      this.part = result.data.part
+      this.part.display = Boolean(this.part.display)
+      
+    },
+    apply(files){
+      if (files.length===0) {
+        return
+      }
+      this.dialogFormVisible = true
+      this.activeFiles = files
+
+      this.getAdminList()
+    },
+
+    async applyDownload(){
+      
+      if (!this.radio||!this.checkList.length) {
+        return this.$alert("请填写勾选信息", "提示", {
+            confirmButtonText: "确定",
+            callback: () => {
+              return
+            },
+          })
+      }
+      
+      let params ={
+        auditId: this.radio,
+        fileIds: this.checkList.join(','),
+        fkId: '',
+        name: this.part.name,
+        reason: this.textarea,
+        type: 'part'
+      }
+    let result = await this.$http({
+        method: 'post',
+        data:params,
+        headers: {
+          token: window.localStorage.getItem('token')
+        },
+        url: '/manage/download/save'
+      })
+
+      
+      if (result.code === 0) {
+        this.$alert('申请成功', '提示', {
+          confirmButtonText: '确定',
+          callback: () => {
+            this.getDetail()
+          }
+        })
+      } else {
+        this.$notify.error({
+          title: '错误',
+          message: result.msg
+        })
+      }
+
+    },
+
+    async getAdminList(){
+    let result = await this.$http({
+        method: 'get',
+        headers: {
+          token: window.localStorage.getItem('token')
+        },
+        url: '/manage/download/admin'
+      })
+
+      this.adminList = result.data
+    },
+
+    goto () {
+      this.$router.push({ name: 'edit-parts', params: { type: 1,id:this.part.id } })
+    },
+    async handleShow(e){
+      this.part['display'] = e
+      let params = {}
+      Object.assign(params,this.part,{
+        display:e|0
+      })
+      await this.$http({
+        method: "post",
+        data: params,
+        headers: {
+          token: window.localStorage.getItem("token")
+        },
+        url: `/manage/part/save`
+      });
+    }
+  },
+  mounted () {
+    this.getDetail()
+  }
+}
+</script>
+
+<style lang="less" scoped>
+.top-body{
+  border-top: .0625rem solid #e6e6e6;
+  line-height: 1.5;
+  padding: 1.25rem;
+  align-items: center;
+  box-sizing: border-box;
+  background: #fff;
+  margin: 1rem 0;
+}
+
+
+.table-interface{
+  overflow-y: auto;
+  overflow-x: hidden;
+  height: calc(100% - 3rem);
+  background: #fff;
+  padding: 2.625rem 1.25rem;
+}
+
+.collection-con{
+  display: flex;
+}
+
+.c-left{
+  flex: 3;
+  margin-right: 20px;
+}
+
+.c-left p{
+  margin: 10px 20px;
+}
+
+.c-left iframe,.c-left img{
+  width: 100%;
+  min-height: 70vh;
+}
+
+.c-right{
+  flex: 1;
+  .c-h1{
+    color: #B63C25;
+    font-size: 1.25rem;
+    font-weight: bold;
+  }
+}
+
+.c-right .c-title{
+  font-weight: bold;
+  display: flex;
+  justify-content: space-between;
+  border-bottom: 1px solid #532F1C;
+  color: #532F1C;
+  font-size: 1.125rem;
+}
+
+.edit{
+  cursor: pointer;
+}
+.c-right p{
+  margin: 40px 20px 40px 0;
+  word-break: break-all;
+  line-height: 1.5;
+  font-size: 1rem;
+}
+.con-btn{
+  padding-right: 1.25rem;
+}
+
+.media-cls{
+  color: rgba(0, 176, 255, 1);
+  cursor: pointer;
+  margin-top: 20px;
+  p{
+    line-height: 2;
+    margin: 0;
+  }
+}
+
+.iconfont{
+  cursor: pointer;
+}
+
+.dialog{
+  .content{
+    border: .0625rem solid #ccc;
+    padding: .625rem;
+    margin: .625rem auto;
+    div,li{
+      line-height: 2;
+    }
+  }
+  .textarea{
+    padding: 0;
+    border: none;
+  }
+}
+
+</style>
+
+<style>
+.ql-picker-label {
+  line-height: 1;
+  overflow: hidden;
+}
+
+.avatar-uploader .el-upload {
+  border: 1px dashed #000;
+  border-radius: 6px;
+  cursor: pointer;
+  position: relative;
+  overflow: hidden;
+}
+.avatar-uploader .el-upload:hover {
+  border-color: #409eff;
+}
+.avatar-uploader-icon {
+  font-size: 28px;
+  color: #8c939d;
+  width: 178px;
+  height: 178px;
+  line-height: 178px;
+  text-align: center;
+}
+.avatar {
+  width: 178px;
+  height: 178px;
+  display: block;
+}
+.iconfont{
+  font-size: 20px;
+}
+
+</style>

+ 245 - 0
backstage/src/pages/show/roam.vue

@@ -0,0 +1,245 @@
+<!--  -->
+<template>
+  <div>
+    <main-top :crumb="crumbData">
+      <div slot="con" class="con-btn">
+        <el-button type="primary" @click="$router.back()">返回</el-button>
+      </div>
+    </main-top>
+      <div class="table-interface">
+        <div class="collection-con">
+          <div class="c-left">
+            <video v-if="detail.displayModel === 2" :src="detail.video||detail.webSite" autoplay loop></video>
+            <iframe v-else :src="detail.webSite" frameborder="0"></iframe>
+          </div>
+          <div class="c-right theme-color">
+            <div class="c-h1">{{detail.name}}</div>
+            <p class="c-title">
+              <span>漫游信息</span>
+              <i class="iconfont iconsys_edit" @click="goto"></i>
+            </p>
+            <p>漫游类别:{{detail.typeName}}</p>
+            <p>所属楼体:{{BLOCK[detail.block]}}</p>
+            <p>漫游位置:<span v-html="ZONE[detail.zone]"></span></p>
+            <p>是否在程序显示:
+              <el-switch
+                @change="handleShow"
+                v-model="detail.display">
+              </el-switch>
+            </p>
+            <p class="c-title" style="margin-bottom:0">
+              <span>漫游介绍</span>
+            </p>
+            <p v-html="detail.content||'暂无介绍'" style="margin-top:20px"></p>
+
+          </div>
+        </div>
+      </div>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+
+import MainTop from '@/components/main-top'
+
+const crumbData = [
+  {
+    name: '漫游信息',
+    id: 0
+  },
+  {
+    name: '查看详情',
+    id: 1
+  }
+]
+
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    MainTop
+  },
+  data () {
+    return {
+      crumbData,
+      detail: '',
+      id: this.$route.params.id,
+      type: 'picture',
+      realType: '',
+      value1:true
+    }
+  },
+  methods: {
+    async getDetail(){
+      let result = await this.$http({
+        method: 'get',
+        headers: {
+          token: window.localStorage.getItem('token')
+        },
+        url: `/manage/roam/detail/${this.id}`
+      })
+
+      this.detail = result.data
+      this.detail.display = Boolean(this.detail.display)
+
+    },
+
+    goto () {
+      this.$router.push({ name: 'edit-roam', params: { type: 1, id:this.detail.id} })
+    },
+    async handleShow(e){
+      this.detail['display'] = e
+      let params = {}
+      Object.assign(params,this.detail,{
+        display:e|0
+      })
+      await this.$http({
+        method: "post",
+        data: params,
+        headers: {
+          token: window.localStorage.getItem("token")
+        },
+        url: `/manage/roam/save`
+      });
+    }
+  },
+  mounted () {
+    this.getDetail()
+  }
+}
+</script>
+<style lang="less" scoped>
+.top-body{
+  border-top: .0625rem solid #e6e6e6;
+  line-height: 1.5;
+  padding: 1.25rem;
+  align-items: center;
+  box-sizing: border-box;
+  background: #fff;
+  margin: 1rem 0;
+}
+
+
+.table-interface{
+  overflow-y: auto;
+  overflow-x: hidden;
+  height: calc(100% - 3rem);
+  background: #fff;
+  padding: 2.625rem 1.25rem;
+}
+
+.collection-con{
+  display: flex;
+}
+
+.c-left{
+  flex: 3;
+  margin-right: 20px;
+}
+
+.c-left p{
+  margin: 10px 20px;
+}
+
+.c-left iframe,.c-left video{
+  width: 100%;
+  min-height: 70vh;
+}
+
+.c-right{
+  flex: 1;
+  .c-h1{
+    color: #B63C25;
+    font-size: 1.25rem;
+    font-weight: bold;
+  }
+}
+
+.c-right .c-title{
+  font-weight: bold;
+  display: flex;
+  justify-content: space-between;
+  border-bottom: 1px solid #532F1C;
+  color: #532F1C;
+  font-size: 1.125rem;
+}
+
+.edit{
+  cursor: pointer;
+}
+.c-right p{
+  margin: 40px 20px 40px 0;
+  word-break: break-all;
+  line-height: 1.5;
+  font-size: 1rem;
+}
+.con-btn{
+  padding-right: 1.25rem;
+}
+
+.media-cls{
+  color: rgba(0, 176, 255, 1);
+  cursor: pointer;
+  margin-top: 20px;
+  p{
+    line-height: 2;
+    margin: 0;
+  }
+}
+
+.iconfont{
+  cursor: pointer;
+}
+
+.dialog{
+  .content{
+    border: .0625rem solid #ccc;
+    padding: .625rem;
+    margin: .625rem auto;
+    div,li{
+      line-height: 2;
+    }
+  }
+  .textarea{
+    padding: 0;
+    border: none;
+  }
+}
+
+</style>
+
+<style>
+.ql-picker-label {
+  line-height: 1;
+  overflow: hidden;
+}
+
+.avatar-uploader .el-upload {
+  border: 1px dashed #000;
+  border-radius: 6px;
+  cursor: pointer;
+  position: relative;
+  overflow: hidden;
+}
+.avatar-uploader .el-upload:hover {
+  border-color: #409eff;
+}
+.avatar-uploader-icon {
+  font-size: 28px;
+  color: #8c939d;
+  width: 178px;
+  height: 178px;
+  line-height: 178px;
+  text-align: center;
+}
+.avatar {
+  width: 178px;
+  height: 178px;
+  display: block;
+}
+.iconfont{
+  font-size: 20px;
+}
+
+</style>

+ 236 - 0
backstage/src/pages/show/structure.vue

@@ -0,0 +1,236 @@
+<!--  -->
+<template>
+  <div>
+    <main-top :crumb="crumbData">
+      <div slot="con" class="con-btn">
+        <el-button type="primary" @click="$router.back()">返回</el-button>
+      </div>
+    </main-top>
+      <div class="table-interface">
+                <div class="collection-con">
+          <div class="c-left">
+            <iframe v-if="detail.webSite" :src="detail.webSite||detail.ico" frameborder="0"></iframe>
+            <img v-else :src="detail.ico" alt="">
+          </div>
+          <div class="c-right theme-color">
+            <div class="c-h1">{{detail.name}}</div>
+            <p class="c-title">
+              <span>结构信息</span>
+              <i class="iconfont iconsys_edit" @click="goto"></i>
+            </p>
+            <p>所属楼体:{{BLOCK[detail.block]}}</p>
+            <p class="c-title" style="margin-bottom:0">
+              <span>结构介绍</span>
+            </p>
+            <p v-html="detail.content||'暂无介绍'" style="margin-top:20px"></p>
+          </div>
+        </div>
+      </div>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+
+import MainTop from '@/components/main-top'
+
+const crumbData = [
+  {
+    name: '结构信息',
+    id: 0
+  },
+  {
+    name: '查看详情',
+    id: 1
+  }
+]
+
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    MainTop
+  },
+  data () {
+    return {
+      crumbData,
+      detail: '',
+      id: this.$route.params.id,
+      type: 'picture',
+      realType: '',
+      value1:true
+    }
+  },
+  methods: {
+    async getDetail(){
+      let result = await this.$http({
+        method: 'get',
+        headers: {
+          token: window.localStorage.getItem('token')
+        },
+        url: `/manage/structure/detail/${this.id}`
+      })
+
+      this.detail = result.data
+    },
+
+    goto () {
+      this.$router.push({ name: 'edit-structure', params: { type: 1, id:this.detail.id} })
+    },
+    async handleShow(e){
+      this.detail['display'] = e
+      console.log(this.detail['display']);
+      let params = {}
+      Object.assign(params,this.detail,{
+        display:e|0
+      })
+      await this.$http({
+        method: "post",
+        data: params,
+        headers: {
+          token: window.localStorage.getItem("token")
+        },
+        url: `/manage/structure/save`
+      });
+    }
+  },
+  mounted () {
+    this.getDetail()
+  }
+}
+</script>
+
+<style lang="less" scoped>
+.top-body{
+  border-top: .0625rem solid #e6e6e6;
+  line-height: 1.5;
+  padding: 1.25rem;
+  align-items: center;
+  box-sizing: border-box;
+  background: #fff;
+  margin: 1rem 0;
+}
+
+
+.table-interface{
+  overflow-y: auto;
+  overflow-x: hidden;
+  height: calc(100% - 3rem);
+  background: #fff;
+  padding: 2.625rem 1.25rem;
+}
+
+.collection-con{
+  display: flex;
+}
+
+.c-left{
+  flex: 3;
+  margin-right: 20px;
+}
+
+.c-left p{
+  margin: 10px 20px;
+}
+
+.c-left iframe,.c-left img{
+  width: 100%;
+  min-height: 70vh;
+}
+
+.c-right{
+  flex: 1;
+  .c-h1{
+    color: #B63C25;
+    font-size: 1.25rem;
+    font-weight: bold;
+  }
+}
+
+.c-right .c-title{
+  font-weight: bold;
+  display: flex;
+  justify-content: space-between;
+  border-bottom: 1px solid #532F1C;
+  color: #532F1C;
+  font-size: 1.125rem;
+}
+
+.edit{
+  cursor: pointer;
+}
+.c-right p{
+  margin: 40px 20px 40px 0;
+  word-break: break-all;
+  line-height: 1.5;
+  font-size: 1rem;
+}
+.con-btn{
+  padding-right: 1.25rem;
+}
+
+.media-cls{
+  color: rgba(0, 176, 255, 1);
+  cursor: pointer;
+  margin-top: 20px;
+  p{
+    line-height: 2;
+    margin: 0;
+  }
+}
+
+.iconfont{
+  cursor: pointer;
+}
+
+.dialog{
+  .content{
+    border: .0625rem solid #ccc;
+    padding: .625rem;
+    margin: .625rem auto;
+    div,li{
+      line-height: 2;
+    }
+  }
+  .textarea{
+    padding: 0;
+    border: none;
+  }
+}
+
+</style>
+
+<style>
+.ql-picker-label {
+  line-height: 1;
+  overflow: hidden;
+}
+
+.avatar-uploader .el-upload {
+  border: 1px dashed #000;
+  border-radius: 6px;
+  cursor: pointer;
+  position: relative;
+  overflow: hidden;
+}
+.avatar-uploader .el-upload:hover {
+  border-color: #409eff;
+}
+.avatar-uploader-icon {
+  font-size: 28px;
+  color: #8c939d;
+  width: 178px;
+  height: 178px;
+  line-height: 178px;
+  text-align: center;
+}
+.avatar {
+  width: 178px;
+  height: 178px;
+  display: block;
+}
+.iconfont{
+  font-size: 20px;
+}
+
+</style>

+ 485 - 0
backstage/src/pages/system/Download.vue

@@ -0,0 +1,485 @@
+<!--  -->
+<template>
+  <div>
+    <main-top :crumb="crumbData"></main-top>
+    <div class="table-interface">
+      <div class="top-body">
+        <div class="info-top">
+          <div class="info-left">
+            <span>按功能模块查看:</span>
+            <el-select v-model="typeId" placeholder="请选择">
+              <el-option label="全部" value></el-option>
+              <el-option v-for="(item, i) in typeArr" :key="i" :label="item.name" :value="item.id"></el-option>
+            </el-select>
+            <span style="margin-left:20px;">按状态查看:</span>
+            <el-select v-model="statusId" placeholder="请选择">
+              <el-option label="全部" value></el-option>
+              <el-option
+                v-for="(item, i) in statusArr"
+                :key="i"
+                :label="item.name"
+                :value="item.id"
+              ></el-option>
+            </el-select>
+            <el-input style="width:220px;margin:0 20px;" v-model="inputKey" placeholder="请输入关键字搜索"></el-input>
+            <el-button type="primary" @click="getInformation">查询</el-button>
+            <el-button @click="reset">重置</el-button>
+          </div>
+          <!-- <div class="info-right">
+            <el-button type="primary" @click="$router.push({name:'edit-parts',params:{type:0,id:'none'}})">新增文物</el-button>
+          </div>-->
+        </div>
+        <el-table :data="tableData" style="width: 100%">
+          <el-table-column
+            v-for="(item, idx) in data"
+            :key="idx"
+            :prop="item.prop"
+            :label="item.label"
+          >
+            <template slot-scope="scope">
+              <span>{{ scope.row[item.prop] || "-" }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作">
+            <template slot-scope="scope">
+              <span @click="show(scope.row)" class="o-span">查看详情</span>
+            </template>
+          </el-table-column>
+        </el-table>
+        <div class="e-pagination">
+          <el-pagination
+            @current-change="handleCurrentChange"
+            :current-page.sync="currentPage"
+            :page-size="size"
+:page-sizes="PAGESIZES"
+            layout="prev, pager, next, jumper"
+            :total="total"
+          ></el-pagination>
+        </div>
+
+        <el-dialog title="审核申请内容" :visible.sync="dialogFormVisible" width="40%">
+          <div class="add-con">
+            <h2 v-if="form.status!==1">你的申请{{statusStr[form.status]}}!</h2>
+
+            <el-form :model="form">
+              <el-form-item label="申请人:" :label-width="formLabelWidth">
+                <span>{{ form.userName || "-" }}</span>
+              </el-form-item>
+              <el-form-item label="申请时间:" :label-width="formLabelWidth">
+                <span>{{ form.createTime || "-" }}</span>
+              </el-form-item>
+              <el-form-item label="申请原因:" :label-width="formLabelWidth">
+                <span>{{ form.reason || "-" }}</span>
+              </el-form-item>
+            </el-form>
+            <template v-if="form.status !== 3||$role=='admin'">
+               <div class="file_cls">申请下载的内容:</div>
+                <ul class="file_ul">
+                  <el-checkbox-group v-model="checkList" @change='handleChange'>
+                    <div style="line-height:2" v-for="(sub,idx) in files" :key="idx">
+                      <el-checkbox :label="sub.id" :false-label="''" :true-label="''">{{sub.fileName}}</el-checkbox>
+                    </div>
+                  </el-checkbox-group>
+                  
+                  <el-button v-if="form.status !== 1||$role=='admin'" size="mini" style="margin-top:20px" @click="downLoad" type="primary">下载</el-button>
+                </ul>
+            </template>
+          </div>
+          <div slot="footer" class="dialog-footer" v-if="$role=='admin'" >
+            <el-button type="primary" @click="shenHe(2)">{{form.status === 1?'同 意':'确 定'}}</el-button>
+            <el-button @click="shenHe(3)">{{form.status === 1?'驳 回':'返 回'}}</el-button>
+          </div>
+        </el-dialog>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+
+import MainTop from "@/components/main-top";
+const crumbData = [
+  {
+    name: "下载管理",
+    id: 5
+  }
+];
+
+const TYPENAME = {
+  part: "部件信息管理",
+  building: "建筑物信息管理"
+};
+
+const STATUSNAME = {
+  1: "待审核",
+  2: "已同意",
+  3: "已驳回"
+};
+
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    MainTop
+  },
+  data() {
+    // 这里存放数据
+    let data = [
+      {
+        prop: "idx",
+        label: "序号"
+      },
+      {
+        prop: "typeName",
+        label: "资料所属模块"
+      },
+      {
+        prop: "name",
+        label: "部件/建筑物名称"
+      },
+      {
+        prop: "userName",
+        label: "申请人"
+      },
+      {
+        prop: "createTime",
+        label: "申请时间"
+      },
+      {
+        prop: "statusStr",
+        label: "当前状态"
+      }
+    ];
+
+    const statusArr = [
+      {
+        id: 1,
+        name: "待审核"
+      },
+      {
+        id: 2,
+        name: "已同意"
+      },
+      {
+        id: 3,
+        name: "已驳回"
+      }
+    ];
+
+    const statusStr =       {
+        2: "已同意",
+        3: "已驳回",
+      }
+    ;
+
+    const typeArr = [
+      {
+        id: "part",
+        name: "部件信息管理"
+      },
+      {
+        id: "building",
+        name: "建筑物信息管理"
+      }
+    ];
+    return {
+      crumbData,
+      data,
+      checkList:[],
+      infoName: "",
+      currentPage: 1,
+      dialogFormVisible: false,
+      size: 25,
+      total: 0,
+      tableData: [],
+      statusArr,
+      typeArr,
+      statusStr,
+      statusId: "",
+      typeId: "",
+      inputKey: "",
+      form: {
+        createTime: "",
+        fileIds: "",
+        id: 0,
+        name: "",
+        reason: "",
+        status: 0,
+        type: "",
+        updateTime: "",
+        userName: ""
+      },
+      formLabelWidth: "120px",
+      files:[]
+    };
+  },
+  watch: {
+    currentPage() {
+      this.refresh();
+    },
+    size() {
+      this.refresh();
+    }
+  },
+  mounted() {
+    this.refresh();
+  },
+  methods: {
+    handleChange(){},
+    async downLoad(){
+      if (!this.checkList.length) {
+        return this.$alert("请勾选下载内容", "提示", {
+            confirmButtonText: "确定",
+            callback: () => {
+              return
+            },
+        })
+      }
+      // let loading = this.$loading({
+      //   lock: true,
+      //   text: 'Loading',
+      //   spinner: 'el-icon-loading',
+      //   background: 'rgba(0, 0, 0, 0.7)'
+      // });
+      let result = await this.$http({
+        method: "get",
+        headers: {
+          token: window.localStorage.getItem("token")
+        },
+        url: `/manage/download/download/${this.checkList.join(',')}`
+      });
+      // loading.close()
+
+      if (result.code === 0) {
+        window.location.href = result.msg
+      } else {
+        this.$notify.error({
+          title: "错误",
+          message: '下载失败'
+        });
+      }
+    },
+    async shenHe(status = 3) {
+      if (this.form.status!==1) {
+        return this.dialogFormVisible = false
+      }
+      let result = await this.$http({
+        method: "get",
+        headers: {
+          token: window.localStorage.getItem("token")
+        },
+        url: `/manage/download/audit/${this.form.id}/${status}`
+      });
+
+      if (result.code === 0) {
+        this.$alert("审核成功", "提示", {
+          confirmButtonText: "确定",
+          callback: () => {
+            this.refresh();
+          }
+        });
+      } else {
+        this.$notify.error({
+          title: "错误",
+          message: result.msg
+        });
+      }
+    },
+    refresh() {
+      this.dialogFormVisible = false;
+      this.loading = true;
+      this.getInformation();
+      this.loading = false;
+    },
+    handleCurrentChange(val) {
+      this.currentPage = val;
+    },
+    reset() {
+      this.inputKey = "";
+      this.statusId = "";
+      this.typeId = ""
+    },
+
+    async getDetail(id){
+      let result = await this.$http({
+        method: 'get',
+        headers: {
+          token: window.localStorage.getItem('token')
+        },
+        url: `/manage/download/detail/${id}`
+      })
+
+      this.form = result.data.obj
+      this.files = result.data.file
+    },
+
+    show(item = {}) {
+      this.dialogFormVisible = true;
+      this.checkList = []
+      this.getDetail(item.id)
+    },
+
+    async getInformation() {
+      let params = {
+        pageNum: this.currentPage,
+        pageSize: this.size,
+        searchKey: this.inputKey,
+        type:this.typeId,
+        status:this.statusId
+      };
+
+      let result = await this.$http({
+        method: "post",
+        data: params,
+        headers: {
+          token: window.localStorage.getItem("token")
+        },
+        url: "/manage/download/list"
+      });
+
+      if (result.code !== 0) {
+        return;
+      }
+      this.tableData = result.data.list;
+      this.total = result.data.total;
+      this.tableData.forEach((item, i) => {
+        item["idx"] = i + 1;
+        item["typeName"] = TYPENAME[item.type];
+        item["statusStr"] = STATUSNAME[item.status];
+      });
+    }
+  }
+};
+</script>
+
+<style lang="less" scoped>
+.top-body {
+  border-top: 0.0625rem solid #e6e6e6;
+  line-height: 1.5;
+  padding: 0 1.25rem 1.25rem;
+  align-items: center;
+  box-sizing: border-box;
+  background: #fff;
+  margin: 1rem 0;
+}
+
+.top-body .top-con {
+  font-weight: bold;
+}
+.table-title {
+  padding: 1rem 1rem 1rem 0;
+  font-size: 1rem;
+  color: #2d2d2d;
+  border-bottom: 1px solid #ccc;
+  display: flex;
+  justify-content: space-between;
+}
+
+.more {
+  color: #ec652d;
+  cursor: pointer;
+}
+.add-con{
+  h2{
+    text-align: center;
+    font-size: 20px;
+    margin-bottom: 30px;
+  }
+}
+.top-right {
+  background-color: #ec652d;
+  height: 3.5625rem;
+  line-height: 3.5625rem;
+  color: #fff;
+  padding: 0 1.875rem;
+  border-radius: 0.3125rem;
+  font-size: 1.125rem;
+  font-weight: bold;
+}
+
+.search-body {
+  background-color: #fff;
+  margin: 1.25rem 0;
+}
+
+.interface-table {
+  height: calc(100% - 17.1875rem);
+  overflow-x: hidden;
+  background-color: #fff;
+  padding: 0 1.125rem 2.375rem;
+  box-sizing: border-box;
+}
+
+.file_cls{
+  padding-left: 38px;  
+  margin-bottom: 20px;
+}
+
+.file_ul{
+  border: 1px solid #ccc;
+  padding: 20px 40px;  
+  margin-bottom: 20px;
+}
+
+.file_ul li{
+  color: #409eff;
+  margin-bottom: 6px;
+  cursor: pointer;
+}
+
+.zan-con {
+  display: flex;
+  width: 100%;
+  justify-content: space-around;
+  margin-top: 1.5rem;
+  text-align: center;
+  font-size: 1.125rem;
+}
+
+.zan-con .line {
+  height: 8rem;
+  width: 1px;
+  background: #ccc;
+}
+
+.zan-con .zan-contain {
+  display: flex;
+  justify-content: space-between;
+  flex-direction: column;
+}
+
+.zan-con .zan-contain p {
+  font-size: 2.25rem;
+}
+
+.zan-con .zan-contain p:first-child {
+  font-size: 0.875rem;
+  line-height: 1.5;
+}
+
+.zan-sub {
+  text-align: right;
+  margin-top: 1.25rem;
+  color: #a5a5a5;
+  font-size: 0.875rem;
+}
+
+.table-interface {
+  overflow-y: auto;
+  overflow-x: hidden;
+  height: calc(100% - 3rem);
+}
+
+.info-top {
+  padding: 20px 0;
+  display: flex;
+  justify-content: space-between;
+  border-bottom: 1px #a5a5a5 solid;
+}
+
+.o-span {
+  cursor: pointer;
+  color: rgb(7, 152, 244);
+}
+</style>

+ 411 - 0
backstage/src/pages/system/Menu.vue

@@ -0,0 +1,411 @@
+<!--  -->
+<template>
+  <div>
+    <main-top :crumb="crumbData"></main-top>
+    <div class="table-interface">
+      <div class="top-body">
+        <el-table :data="tableData" height="68vh" class="collection-con" style="width: 100%">
+          <el-table-column
+            v-for="(item, idx) in data"
+            :key="idx"
+            :prop="item.prop"
+            :label="item.label"
+          >
+            <template slot-scope="scope" >
+              <span v-html="scope.row[item.prop]||'-'"></span>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作">
+            <template slot-scope="scope" >
+              <span class="o-span" @click="show(scope.row)">修改展区名称</span>
+            </template>
+          </el-table-column>
+        </el-table>
+        <div class="e-pagination">
+          <el-pagination
+            @current-change="handleCurrentChange"
+            :current-page.sync="currentPage"
+            @size-change="handleSizeChange"
+            :page-size="size"
+            :page-sizes="PAGESIZES"
+            layout="total, sizes, prev, pager, next, jumper"
+            :total="total"
+          ></el-pagination>
+        </div>
+      </div>
+    </div>
+    <el-dialog title="修改展区名称" :visible.sync="dialogFormVisible" width="40%">
+      <div class="add-con">
+        <div class="add-left">
+          <el-form ref="userform" :model="form">
+            <el-form-item label="展区名称:" :rules="[
+              { required: true, message: '展区名称不能为空'}
+            ]" :label-width="formLabelWidth">
+              <el-input maxlength="10" show-word-limit placeholder="请输入展区名称"  v-model="tmpName" autocomplete="off"></el-input>
+            </el-form-item>
+          </el-form>
+        </div>
+      </div>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="dialogFormVisible = false">取 消</el-button>
+        <el-button type="primary" @click="save">确定</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+
+import MainTop from '@/components/main-top'
+const crumbData = [
+  {
+    name: '展区名称管理',
+    id: 4
+  }
+]
+
+let typeArr = [
+  {
+    name:'图片',
+    id:1
+  },
+  {
+    name:'视频',
+    id:2
+  },
+  {
+    name:'模型',
+    id:3
+  }
+]
+
+let data = [
+      {
+        prop: "idx",
+        label: "编号"
+      },
+      {
+        prop: "name",
+        label: "展区名称"
+      },
+      {
+        prop: "updateTime",
+        label: "修改时间"
+      }
+    ];
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    MainTop,
+  },
+  data () {
+    return {
+      data,
+      crumbData,
+      typeArr,
+      tableData: [],
+      form: {
+        id: "",
+        name: "",
+        parentId: "",
+        sort: ""
+      },
+      currentPage: 1,
+      size: 25,
+      total: 0,
+      loading: false,
+      dialogFormVisible: false,
+      formLabelWidth: "120px",
+      tmpName:'',
+      tmpType:''
+    }
+  },
+  watch: {
+    currentPage () {
+      this.refresh()
+    },
+    size () {
+      this.refresh()
+    }
+  },
+  mounted () {
+    this.refresh()
+  },
+ 
+  methods: {
+    
+    show(item = "") {
+      this.dialogFormVisible = true;
+      this.tmpName = item.name
+      this.tmpType = item.type
+      this.form = item;
+    },
+    async save() {
+      this.$refs["userform"].validate(async (valid) => {
+        if (valid) {
+        let { id ="" } = this.form;
+      
+          // let params = {
+          //   id,name:this.tmpName,  parentId, sort,type:this.tmpType
+          // };
+          if (!this.tmpName.trim()) {
+             return  this.$alert("请输入展区名称", "提示", {
+                confirmButtonText: "确定"
+            });
+          }
+          let result = await this.$http({
+            method: 'post',
+            data: {
+              id:id,
+              name:this.tmpName.trim()
+            },
+            headers: {
+              token: window.localStorage.getItem('token')
+            },
+            url: '/manage/zone/edit'
+          });
+
+          if (result.code === 0) {
+            this.$alert("操作成功", "提示", {
+              confirmButtonText: "确定",
+              callback: () => {
+                this.getInformation()
+              }
+            });
+          } else {
+            this.$notify.error({
+              title: "错误",
+              message: result.msg
+            });
+          }
+        }
+      })
+     
+    },
+    refresh () {
+      this.loading = true
+      this.getInformation()
+      this.loading = false
+    },
+    handleCurrentChange (val) {
+      this.currentPage = val
+    },
+    handleSizeChange(val){
+      this.size = val
+    },
+    async getInformation () {
+      this.dialogFormVisible = false
+      let params = {
+          pageNum:this.currentPage,
+          pageSize: this.size,
+          searchKey: ""
+      }
+
+      let result = await this.$http({
+        method: 'post',
+        data: params,
+        headers: {
+          token: window.localStorage.getItem('token')
+        },
+        url: '/manage/zone/list'
+      })
+
+      if (result.code !== 0) {
+        return
+      }
+      this.tableData = result.data
+      this.total = result.data.total
+      this.tableData.forEach((item, i) => {
+        item["idx"] = i + 1;
+      });
+    }
+  }
+}
+</script>
+
+
+<style lang="less" scoped>
+.top-body{
+  border-top: .0625rem solid #e6e6e6;
+  line-height: 1.5;
+  padding: 0 1.25rem 1.25rem;
+  align-items: center;
+  box-sizing: border-box;
+  background: #fff;
+  margin: 1rem 0;
+  padding-top: 20px;
+}
+
+.top-body .top-con{
+  font-weight: bold;
+}
+.table-title{
+  padding: 1rem 1rem 1rem 0 ;
+  font-size: 1rem;
+  color: #2d2d2d;
+  border-bottom: .0625rem solid #ccc;
+  display: flex;
+  justify-content: space-between;
+}
+
+.more{
+  color: #ec652d;
+  cursor: pointer;
+}
+
+.top-right{
+  background-color: #ec652d;
+  height: 3.5625rem;
+  line-height: 3.5625rem;
+  color: #fff;
+  padding: 0 1.875rem;
+  border-radius: .3125rem;
+  font-size: 1.125rem;
+  font-weight: bold;
+}
+
+.search-body{
+  background-color: #fff;
+  margin: 1.25rem 0;
+}
+
+.interface-table{
+  height: calc(100% - 17.1875rem);
+  overflow-x: hidden;
+  background-color: #fff;
+  padding: 0 1.125rem 2.375rem;
+  box-sizing: border-box;
+}
+
+.zan-con{
+  display: flex;
+  width: 100%;
+  justify-content: space-around;
+  margin-top: 1.5rem;
+  text-align: center;
+  font-size: 1.125rem;
+}
+
+.zan-con .line{
+  height: 8rem;
+  width: .0625rem;
+  background: #ccc;
+}
+
+.zan-con .zan-contain{
+  display: flex;
+  justify-content: space-between;
+  flex-direction: column;
+}
+
+.zan-con .zan-contain p{
+  font-size: 2.25rem;
+}
+
+.zan-con .zan-contain p:first-child{
+  font-size: .875rem;
+  line-height: 1.5;
+}
+
+.zan-sub{
+  text-align: right;
+  margin-top: 1.25rem;
+  color: #a5a5a5;
+  font-size: .875rem;
+}
+
+.table-interface{
+  height: calc(100% - 3rem);
+}
+
+
+.info-top{
+  padding: 1.25rem 0;
+  display: flex;
+  justify-content: space-between;
+  border-bottom: .0625rem #a5a5a5 solid;
+}
+
+.o-span{
+  cursor: pointer;
+  color: rgb(7, 152, 244);
+}
+
+.collection-con{
+  width: 100%;
+  overflow-x: hidden;
+  overflow-y: auto;
+  height: 68vh;
+}
+
+
+
+.collection-con ul{
+  display: flex;
+  flex-wrap: wrap;
+  margin-top: 1.25rem;
+}
+
+.collection-con ul li {
+  width: 24%;
+  margin-right: 1%;
+  cursor: pointer;
+  font-size: .8rem;
+  color: #2d2d2d;
+  margin-bottom: 1.25rem;
+}
+
+.collection-con ul .li-img {
+  position: relative;
+  
+}
+.collection-con ul .li-img div{
+  position: absolute;
+  left: .625rem;
+  bottom: .625rem;
+  color: #fff;
+}
+
+.collection-con ul .li-img div span{
+  margin-right: .625rem;
+}
+
+
+.collection-con ul img {
+  width: 100%;
+}
+
+.collection-con{
+  .li-txt,.li-name{
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+  }
+  .li-name{
+    margin-top: .5rem;
+    div{
+      font-size: 1rem;
+      font-weight: bold;
+      color: #532F1C;
+      &:last-of-type{
+        color: #707070;
+        font-size: .875rem;
+        font-weight: normal;
+        span{
+          margin-left: .30rem;
+          &:last-of-type{
+            &:hover{
+              color: #409EFF;
+            }
+          }
+          &:hover{
+            color: #c56351;
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 309 - 0
backstage/src/pages/system/Parameter.vue

@@ -0,0 +1,309 @@
+<!--  -->
+<template>
+  <div>
+    <main-top :crumb="crumbData"></main-top>
+    <div class="table-interface">
+      <div class="con-left">
+        <el-menu default-active="1-1" @select="handleSelect" class="el-menu-vertical-demo" :collapse="isCollapse">
+          <el-submenu index="1">
+            <template slot="title">
+              <i class="el-icon-menu"></i>
+              <span slot="title">参数管理</span>
+            </template>
+            <el-menu-item v-for="(item,i) in collectionType" :key="i" :index="'1-'+item.id">{{item.name}}</el-menu-item>
+          </el-submenu>
+        </el-menu>
+        <div class="sousuo" @click="isCollapse=!isCollapse">
+          <i :class="isCollapse?'el-icon-arrow-right':'el-icon-arrow-left'"></i>
+        </div>
+      </div>
+      <div class="top-body">
+        <el-form>
+          <el-form-item label="主题色" :rules="[
+              { required: true, message: '模型名称不能为空'}
+            ]">
+            <el-radio-group v-model="colorId">
+              <el-radio class="e-radio" v-for="(item,i) in colors" :key="i" :label="item.id">
+               <span class="el-span"><i class="colorBlock" :style="{background:item.color}"></i> <span>{{item.name}}</span></span>
+              </el-radio>
+            </el-radio-group>
+          </el-form-item>
+        </el-form>
+        <p class="sub">备注:选择变更颜色后,门户主题色将进行改变,请谨慎选择。</p>
+        <div class="btn-con">
+          <el-button type="primary" @click="save">提交</el-button>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+
+import MainTop from '@/components/main-top'
+const crumbData = [
+  {
+    name: '参数管理',
+    id: 4
+  }
+]
+
+let collectionType = [
+  {
+    id:1,
+    name:'主题色管理'
+  }
+]
+
+
+  const colors = [
+    {
+      name:'酒红色',
+      id:'juhong',
+      color:'#b9412e'
+    },
+    {
+      name:'橄榄绿',
+      id:'ganlanlv',
+      color:'#548550'
+    },
+    {
+      name:'暗黄色',
+      id:'anhuang',
+      color:'#c48f48'
+    },
+    {
+      name:'天蓝色',
+      id:'tianlan',
+      color:'#09bce1'
+    }
+  ]
+
+
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    MainTop,
+  },
+  data () {
+    return {
+      isCollapse:false,
+      crumbData,
+      time:'',
+      tableData: [],
+      inputKey: '',
+      currentPage: 1,
+      size: 25,
+      total: 0,
+      collectionType,
+      loading: false,
+      status:5,
+      type:1,
+      colors,
+      colorId:'',
+      colorName:''
+    }
+  },
+
+  watch:{
+    colorId(newVal){
+       this.colors.forEach(item=>{
+        if (item.id === newVal) {
+          this.colorName = item.name
+        }
+      })
+      
+    }
+  },
+ 
+  mounted () {
+    this.getTheme()
+  },
+ 
+  methods: {
+    handleSelect(index){
+      this.type = Number(index.substr(-1))
+    },
+    async getTheme(){
+       let result = await this.$http({
+        method: 'get',
+        headers: {
+          token: window.localStorage.getItem('token')
+        },
+        url: '/manage/topic/get'
+      })
+      this.colors = result.data
+      this.colors.forEach(item=>{
+        if (item.display) {
+          this.colorId = item.id
+          this.colorName = item.name
+        }
+      })
+    },
+   
+    save () {
+      this.$confirm(`提交后,门户主题色将改变为“${this.colorName}”,是否继续?`, '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        this.$http.get(`/manage/topic/set/${this.colorId}`,{headers: {
+          token: window.localStorage.getItem('token')
+        }}).then(res => {
+          if (res.code === 0) {
+            this.$alert('修改成功', '提示', {
+              confirmButtonText: '确定',
+              callback: () => {
+              }
+            })
+          } else {
+            this.$notify.error({
+              title: '错误',
+              message: res.msg
+            })
+          }
+        })
+      }).catch(() => {
+        this.$message({
+          type: 'info',
+          message: '已取消'
+        })
+      })
+    },
+  }
+}
+</script>
+
+
+<style lang="less" scoped>
+.top-body{
+  border-top: .0625rem solid #e6e6e6;
+  line-height: 1.5;
+  padding: 20px 1.25rem 1.25rem;
+  align-items: center;
+  box-sizing: border-box;
+  background: #fff;
+  margin: 1rem 0;
+  width: 90%;
+
+}
+
+.top-body .top-con{
+  font-weight: bold;
+}
+.table-title{
+  padding: 1rem 1rem 1rem 0 ;
+  font-size: 1rem;
+  color: #2d2d2d;
+  border-bottom: .0625rem solid #ccc;
+  display: flex;
+  justify-content: space-between;
+}
+
+.more{
+  color: #ec652d;
+  cursor: pointer;
+}
+
+.top-right{
+  background-color: #ec652d;
+  height: 3.5625rem;
+  line-height: 3.5625rem;
+  color: #fff;
+  padding: 0 1.875rem;
+  border-radius: .3125rem;
+  font-size: 1.125rem;
+  font-weight: bold;
+}
+
+.search-body{
+  background-color: #fff;
+  margin: 1.25rem 0;
+}
+
+.interface-table{
+  height: calc(100% - 17.1875rem);
+  overflow-x: hidden;
+  background-color: #fff;
+  padding: 0 1.125rem 2.375rem;
+  box-sizing: border-box;
+}
+
+.table-interface{
+  height: calc(100% - 3rem);
+  display: flex;
+  justify-content: space-between;
+  background-color: #fff;
+  .con-left{
+    border-right: solid 1px #e5e5e5;
+    position: relative;
+    margin-right: 30px;
+    .sousuo{
+      position: absolute;
+      top: 50%;
+      transform: translateY(-50%);
+      right: -30px;
+      cursor: pointer;
+    }
+  }
+}
+
+.e-radio{
+  .el-span{
+    display: inline-flex;
+    vertical-align: middle;
+    position: relative;
+    margin-right: 20px;
+    width: 60px;
+    >span{
+      position: absolute;
+      top: 50%;
+      left: 26px;
+      transform: translateY(-50%);
+    }
+  }
+  .colorBlock{
+    display: inline-block;
+    width: 20px;
+    height: 20px;
+    border-radius: 5px;
+    margin-right: 6px;
+  }
+ 
+
+}
+
+.sub{
+  color: #999;
+  font-size: 14px;
+  margin-left: 60px;
+}
+
+.btn-con{
+  text-align: center;
+  margin-top: 80px;
+}
+
+.info-top{
+  padding: 1.25rem 0;
+  display: flex;
+  justify-content: space-between;
+  border-bottom: .0625rem #a5a5a5 solid;
+}
+
+.o-span{
+  cursor: pointer;
+  color: rgb(7, 152, 244);
+}
+
+
+.el-menu-vertical-demo:not(.el-menu--collapse) {
+  width: 200px;
+  min-height: 400px;
+}
+.el-menu{
+  border-right: none!important;
+}
+</style>

+ 281 - 0
backstage/src/pages/system/Password.vue

@@ -0,0 +1,281 @@
+<!--  -->
+<template>
+  <div>
+    <main-top :crumb="crumbData"></main-top>
+    <div class="table-interface">
+      <div class="top-body">
+        <div class="body-middle">
+          <el-form
+            :rules="ruleForget"
+            status-icon
+            :model="formForget"
+            ref="formForget"
+          >
+            <el-form-item prop="oldPass">
+              <el-input
+                v-model="formForget.oldPass"
+                placeholder="当前密码"
+                type="password"
+              ></el-input>
+            </el-form-item>
+            <el-form-item prop="newPassword">
+              <el-input
+                v-model="formForget.newPassword"
+                placeholder="新密码"
+                type="password"
+              ></el-input>
+            </el-form-item>
+            <el-form-item prop="comfirm">
+              <el-input
+                v-model="formForget.comfirm"
+                placeholder="重复新密码"
+                type="password"
+              ></el-input>
+            </el-form-item>
+            <el-form-item>
+              <el-button @click="updatePass" type="primary">确定</el-button>
+              <el-button @click="reset">取消</el-button>
+            </el-form-item>
+          </el-form>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+
+import MainTop from "@/components/main-top";
+// import { encodeStr } from '@/util'
+// import { Base64 } from 'js-base64'
+const crumbData = [
+  {
+    name: "密码修改",
+    id: 8,
+  },
+];
+
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    MainTop,
+  },
+  data() {
+    var validatePass = (rule, value, callback) => {
+      if (value === "") {
+        callback(new Error("请输入密码"));
+      } else {
+        if (this.formForget.newPassword !== "") {
+          this.$refs.formForget.validateField("comfirm");
+        }
+        callback();
+      }
+    };
+    var validatePass2 = (rule, value, callback) => {
+      if (value === "") {
+        callback(new Error("请再次输入密码"));
+      } else if (value !== this.formForget.newPassword) {
+        callback(new Error("两次输入密码不一致!"));
+      } else {
+        callback();
+      }
+    };
+
+    // 这里存放数据
+    return {
+      crumbData,
+      formForget: {
+        oldPass: "",
+        newPassword: "",
+        comfirm: "",
+      },
+      ruleForget: {
+        newPassword: [{ validator: validatePass, trigger: "blur" }],
+        oldPass: [{ validator: validatePass, trigger: "blur" }],
+        comfirm: [{ validator: validatePass2, trigger: "blur" }],
+      },
+    };
+  },
+  methods: {
+    reset(){
+      this.$router.back()
+    },
+    async logout (msg = '') {
+      await this.$http({
+        method: 'get',
+        url: '/admin/logout',
+        headers: {
+          token: window.localStorage.getItem('token')
+        }
+      }).then(res => {
+        if (res.code === 0) {
+          window.localStorage.setItem('token', '')
+          this.$alert(msg||'退出成功', '提示', {
+            confirmButtonText: '确定',
+            callback: () => {
+              window.localStorage.setItem('userInfo', '')
+              this.$router.push('/login')
+            }
+          })
+        }
+      })
+    },
+    updatePass() {
+      this.$refs["formForget"].validate(async (valid) => {
+        if (valid) {
+            // let temp = encodeStr(Base64.encode(this.formForget.newPassword), Base64.encode(this.formForget.oldPass))
+
+            let result = await this.$http({
+              method: "post",
+              data: {
+                // newPassword: temp[0],
+                // oldPassword: temp[1]
+                newPassword: this.formForget.newPassword,
+                oldPassword: this.formForget.oldPass
+              },
+              headers: {
+                token: window.localStorage.getItem("token"),
+              },
+              url: `/manage/user/updatePwd`,
+            });
+
+          if (result.code === 0) {
+            this.$alert("修改成功", "提示", {
+              confirmButtonText: "确定",
+              callback: () => {
+                this.logout('成功修改密码,请重新登录。')
+              },
+            });
+          } else {
+            this.$notify.error({
+              title: "错误",
+              message: result.msg,
+            });
+          }
+        } else {
+          return this.$notify.error({
+              title: "错误",
+              message: '修改失败',
+            });
+        }
+      });
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.top-body {
+  border-top: 0.0625rem solid #e6e6e6;
+  line-height: 1.5;
+  padding: 1.25rem 1.25rem;
+  align-items: center;
+  box-sizing: border-box;
+  background: #fff;
+  margin: 1rem 0;
+}
+
+.top-body .top-con {
+  font-weight: bold;
+}
+.table-title {
+  padding: 1rem 1rem 1rem 0;
+  font-size: 1rem;
+  color: #2d2d2d;
+  border-bottom: 1px solid #ccc;
+  display: flex;
+  justify-content: space-between;
+}
+
+.more {
+  color: #ec652d;
+  cursor: pointer;
+}
+
+.top-right {
+  background-color: #ec652d;
+  height: 3.5625rem;
+  line-height: 3.5625rem;
+  color: #fff;
+  padding: 0 1.875rem;
+  border-radius: 0.3125rem;
+  font-size: 1.125rem;
+  font-weight: bold;
+}
+
+.search-body {
+  background-color: #fff;
+  margin: 1.25rem 0;
+}
+
+.interface-table {
+  height: calc(100% - 17.1875rem);
+  overflow-x: hidden;
+  background-color: #fff;
+  padding: 0 1.125rem 2.375rem;
+  box-sizing: border-box;
+}
+
+.zan-con {
+  display: flex;
+  width: 100%;
+  justify-content: space-around;
+  margin-top: 1.5rem;
+  text-align: center;
+  font-size: 1.125rem;
+}
+
+.zan-con .line {
+  height: 8rem;
+  width: 1px;
+  background: #ccc;
+}
+
+.zan-con .zan-contain {
+  display: flex;
+  justify-content: space-between;
+  flex-direction: column;
+}
+
+.zan-con .zan-contain p {
+  font-size: 2.25rem;
+}
+
+.zan-con .zan-contain p:first-child {
+  font-size: 0.875rem;
+  line-height: 1.5;
+}
+
+.zan-sub {
+  text-align: right;
+  margin-top: 1.25rem;
+  color: #a5a5a5;
+  font-size: 0.875rem;
+}
+
+.table-interface {
+  overflow-y: auto;
+  overflow-x: hidden;
+  height: calc(100% - 3rem);
+}
+
+.info-top {
+  padding: 20px 0;
+  display: flex;
+  justify-content: space-between;
+  border-bottom: 1px #a5a5a5 solid;
+}
+
+.o-span {
+  cursor: pointer;
+  color: rgb(7, 152, 244);
+}
+
+.body-middle {
+  width: 20%;
+  margin: 40px auto;
+  text-align: center;
+}
+</style>

+ 664 - 0
backstage/src/pages/system/User.vue

@@ -0,0 +1,664 @@
+<!--  -->
+<template>
+  <div>
+    <main-top :crumb="crumbData">
+      <div slot="con">
+        <div style="display:flex; align-items: center;">
+            <el-button style="margin-left:20px" type="primary" @click="show('', 'add')">新增用户</el-button>
+        </div>
+      </div>
+    </main-top>
+    <div class="table-interface">
+      <div class="top-body">
+        <div class="info-top">
+          <div class="info-left">
+            <span>注册时间:</span>
+            <el-date-picker
+              v-model="time"
+              type="daterange"
+              range-separator="-"
+              value-format="yyyy-MM-dd HH:mm:ss"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期"
+            >
+            </el-date-picker>
+            <el-button style="margin-left:20px" type="primary" @click="getInformation">查询</el-button>
+            <el-button @click="inputKey='',time=''">重置</el-button>
+          </div>
+          <div class="info-right">
+          </div>
+        </div>
+        <el-table :data="tableData" style="width: 100%">
+          <el-table-column
+            v-for="(item, idx) in data"
+            :key="idx"
+            :prop="item.prop"
+            :label="item.label"
+          >
+            <template slot-scope="scope">
+              <el-switch
+                v-model="scope.row[item.prop]"
+                v-if="item.prop === 'status'"
+                @change="changeState(scope.row)"
+                inactive-color="#ccc"
+              >
+              </el-switch>
+              <span v-html="scope.row[item.prop] || '-'" v-else></span>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作">
+            <template slot-scope="scope">
+              <template>
+                <span class="o-span" @click="show(scope.row, 'edit')"
+                  >修改</span
+                >
+                <span class="o-span" @click="reset(scope.row)">重置密码</span>
+              </template>
+            </template>
+          </el-table-column>
+        </el-table>
+        <div class="e-pagination">
+          
+          <el-pagination
+            @current-change="handleCurrentChange"
+            :current-page.sync="currentPage"
+            :page-size="size"
+            :page-sizes="PAGESIZES"
+            layout="total, sizes, prev, pager, next, jumper"
+            :total="total"
+          ></el-pagination>
+        </div>
+      </div>
+    </div>
+    <el-dialog :title="editTitle" :visible.sync="dialogFormVisible" width="40%">
+      <div class="add-con">
+        <div class="add-left">
+          <el-form ref="userform" :rules="rules" :model="form">
+            <el-form-item
+              label="账号:"
+              prop="userName"
+              maxlength="15"
+              show-word-limit
+              :label-width="formLabelWidth"
+            >
+              <el-input
+                :disabled="type === 'edit'"
+                placeholder="请输入账号"
+                v-model="form.userName"
+                autocomplete="off"
+              ></el-input>
+            </el-form-item>
+            <el-form-item
+              label="用户角色:"
+              prop="role"
+              :label-width="formLabelWidth"
+            >
+              <el-select v-model="form.role" placeholder="请选择角色">
+                <el-option
+                  v-for="(item, i) in sysRole"
+                  :key="i"
+                  :label="item.name"
+                  :value="item.id"
+                ></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item
+              label="联系电话:"
+              prop="phone"
+              :label-width="formLabelWidth"
+            >
+              <el-input
+                v-model="form.phone"
+                placeholder="请输入联系电话"
+                autocomplete="off"
+              ></el-input>
+            </el-form-item>
+           
+            <el-form-item label="是否启用:" :label-width="formLabelWidth">
+              <el-switch v-model="form.status" inactive-color="#ccc">
+              </el-switch>
+            </el-form-item>
+          </el-form>
+        </div>
+        <div class="add-right">
+          <el-upload
+            class="avatar-uploader"
+            :action="uploadUrl"
+            :headers="{
+              token,
+            }"
+            :show-file-list="false"
+            :on-success="handleAvatarSuccess"
+            :before-upload="beforeAvatarUpload"
+            :on-error="handleError"
+          >
+            <img v-if="form.img" :src="form.img" class="avatar" />
+            <i v-else class="el-icon-plus avatar-uploader-icon"></i>
+          </el-upload>
+          <div style="margin:10px auto;text-align:center">用户头像</div>
+        </div>
+      </div>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="save">保 存</el-button>
+        <el-button @click="dialogFormVisible = false">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+
+import MainTop from "@/components/main-top";
+
+const crumbData = [
+  {
+    name: "账号管理",
+    id: 6,
+  },
+];
+
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    MainTop,
+  },
+  computed:{  },
+  data() {
+    // 这里存放数据
+    let data = [
+      {
+        prop: "idx",
+        label: "编号",
+      },
+      {
+        prop: "userName",
+        label: "账号",
+      },
+      {
+        prop: "roleName",
+        label: "角色",
+      },
+      {
+        prop: "phone",
+        label: "联系电话",
+      },
+      {
+        prop: "createTime",
+        label: "新建时间",
+      },
+      {
+        prop: "status",
+        label: "启用状态",
+      },
+    ];
+
+    return {
+      crumbData,
+      data,
+      region: "",
+      infoName: "",
+      tableData: [],
+      dialogFormVisible: false,
+      form: {
+        id: "",
+        phone: "",
+        role: "",
+        status: true,
+        thumb: "",
+        userName: "",
+        img:''
+      },
+      inputKey: "",
+      currentPage: 1,
+      size: 25,
+      total: 0,
+      formLabelWidth: "120px",
+      imageUrl: "",
+      type: "add",
+      editTitle: "新增用户",
+      token: window.localStorage.getItem("token"),
+      uploadUrl: `${this.$serverName}manage/file/upload`,
+      time:'',
+      role:'',
+      rules: {
+        userName: [
+          { required: true, message: "请输入用户账号", trigger: "blur" },
+        ],
+        nickName: [
+          { required: true, message: "请输入用户昵称", trigger: "blur" },
+        ],
+        realName: [
+          { required: true, message: "请输入真实姓名", trigger: "blur" },
+        ],
+        unit: [{ required: true, message: "请输入单位名称", trigger: "blur" }],
+        role: [{ required: true, message: "请选择角色", trigger: "blur" }],
+      },
+      status:1
+    };
+  },
+  watch: {
+    currentPage() {
+      this.refresh();
+    },
+    status(){
+      this.role = ''
+      this.currentPage === 1?this.refresh():this.currentPage= 1
+    },
+    size() {
+      this.refresh();
+    },
+    type(newVal) {
+      this.editTitle = newVal === "add" ? "新增用户" : "修改用户信息";
+    },
+  },
+  mounted() {
+    this.refresh();
+  },
+  methods: {
+    handleChange(item) {
+      console.log(item);
+    },
+    reset(item) {
+      this.$confirm("此操作将重置该用户密码, 是否继续?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      })
+        .then(() => {
+          this.$http({
+            method: "get",
+            headers: {
+              token: window.localStorage.getItem("token"),
+            },
+            url:`/manage/user/resetPass/${item.id}`
+          }).then((res) => {
+            if (res.code === 0) {
+              this.$alert("重置成功", "提示", {
+                confirmButtonText: "确定",
+                callback: () => {},
+              });
+            } else {
+              this.$notify.error({
+                title: "错误",
+                message: res.msg,
+              });
+            }
+          });
+        })
+        .catch(() => {
+          this.$message({
+            type: "info",
+            message: "已取消",
+          });
+        });
+    },
+    goto(item) {
+      window.localStorage.setItem("editInfo", JSON.stringify(item));
+      this.$router.push({ name: "edit-information", params: { type: 1 } });
+      this.$bus.$emit("editinfo", item);
+    },
+
+    refresh() {
+      this.loading = true;
+      this.getInformation();
+      this.loading = false;
+    },
+    handleCurrentChange(val) {
+      this.currentPage = val;
+    },
+    handleError(){
+      this.$alert("上传失败,请稍后再试", "提示", {
+        confirmButtonText: "确定",
+        callback: () => {
+          this.loading.close()
+        },
+      });
+    },
+
+    handleAvatarSuccess(res) {
+      this.loading.close()
+      let { data } = res;
+      this.form.thumb = data.thumb;
+      this.form.img = data.urlPath;
+    },
+    beforeAvatarUpload(file) {
+      let type = this.isImage(file.name);
+      if (!type) {
+        this.$message.error('只允许上传图片');
+        return type
+      }
+      const isLt2M = file.size / 1024 / 1024 < 2;
+
+      if (!isLt2M) {
+        this.$message.error("上传头像图片大小不能超过 2MB!");
+        return isLt2M
+      }
+      this.loading = this.$loading(this.loadOption)
+    },
+
+    async save() {
+      this.$refs["userform"].validate(async (valid) => {
+        if (valid) {
+          let {
+            id,
+            phone,
+            role,
+            status,
+            thumb,
+            img,
+            userName,
+          } = this.form;
+          let params = {
+            id,
+            phone,
+            role,
+            img,
+            status: status * 1,
+            thumb,
+            userName,
+            nickName:userName
+          };
+          let result = await this.$http({
+            method: "post",
+            data: params,
+            headers: {
+              token: window.localStorage.getItem("token"),
+            },
+            url: `/manage/user/save`,
+          });
+
+          if (result.code === 0) {
+            this.$alert("操作成功", "提示", {
+              confirmButtonText: "确定",
+              callback: () => {
+                this.getInformation();
+              },
+            });
+          } else {
+            this.$notify.error({
+              title: "错误",
+              message: result.msg,
+            });
+          }
+        }
+      });
+    },
+    show(item = "", type) {
+      this.dialogFormVisible = true;
+      this.type = type;
+      this.form = {
+        id: "",
+        phone: "",
+        role: "",
+        status: true,
+        thumb: "",
+        userName: "",
+        img:''
+      };
+
+      if (type === "edit") {
+        this.form = item;
+      }
+    },
+    changeState(item) {
+      this.$http
+        .get(`/manage/user/setStatus/${item.id}/${item.status * 1}`, {
+          headers: {
+            token: window.localStorage.getItem("token"),
+          },
+        })
+        .then((res) => {
+          if (res.code === 0) {
+            this.$message({
+              message: (!item.status ?'停用': '启用') + '账号成功',
+              type: 'success'
+            });
+            this.refresh();
+          } else {
+            this.$notify.error({
+              title: "错误",
+              message: res.msg,
+            });
+            this.refresh();
+          }
+        });
+    },
+
+    del(item) {
+      this.$confirm("此操作将删除该用户, 是否继续?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      })
+        .then(() => {
+          this.$http
+            .get(`/manage/user/removes/${item.id}`, {
+              headers: {
+                token: window.localStorage.getItem("token"),
+              },
+            })
+            .then((res) => {
+              if (res.code === 0) {
+                this.$alert("删除成功", "提示", {
+                  confirmButtonText: "确定",
+                  callback: () => {
+                    this.refresh();
+                  },
+                });
+              } else {
+                this.$notify.error({
+                  title: "错误",
+                  message: res.msg,
+                });
+              }
+            });
+        })
+        .catch(() => {
+          this.$message({
+            type: "info",
+            message: "已取消删除",
+          });
+        });
+    },
+
+    async getInformation() {
+      this.dialogFormVisible = false;
+      console.log(this.time);
+      let params = {
+        pageNum: this.currentPage,
+        pageSize: this.size,
+        searchKey: this.inputKey,
+        startTime: this.time? (this.time[0].split(' ')[0] + ' 00:00:00') : '',
+        endTime: this.time? (this.time[1].split(' ')[0] + ' 23:59:59') : '',
+        role: this.role,
+        type:this.status
+      };
+
+      let result = await this.$http({
+        method: "post",
+        data: params,
+        headers: {
+          token: window.localStorage.getItem("token"),
+        },
+        url: "/manage/user/list",
+      });
+
+      if (result.code !== 0) {
+        return;
+      }
+      this.tableData = result.data.list;
+      this.total = result.data.total;
+      this.tableData.forEach((item, i) => {
+        item["roleName"] = this.juese[item["role"]];
+        item["sexStr"] = this.sex[item["sex"]];
+        item["createTime"] = item["createTime"].split(' ')[0] + '<br/>' +item["createTime"].split(' ')[1]
+        item["status"] = Boolean(item["status"]);
+        item["idx"] = i + 1;
+      });
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.top-body {
+  border-top: 0.0625rem solid #e6e6e6;
+  line-height: 1.5;
+  padding: 0 1.25rem 1.25rem;
+  align-items: center;
+  box-sizing: border-box;
+  background: #fff;
+  margin: 1rem 0;
+}
+
+.top-body .top-con {
+  font-weight: bold;
+}
+.table-title {
+  padding: 1rem 1rem 1rem 0;
+  font-size: 1rem;
+  color: #2d2d2d;
+  border-bottom: 1px solid #ccc;
+  display: flex;
+  justify-content: space-between;
+}
+
+.more {
+  color: #ec652d;
+  cursor: pointer;
+}
+
+.top-right {
+  background-color: #ec652d;
+  height: 3.5625rem;
+  line-height: 3.5625rem;
+  color: #fff;
+  padding: 0 1.875rem;
+  border-radius: 0.3125rem;
+  font-size: 1.125rem;
+  font-weight: bold;
+}
+
+.search-body {
+  background-color: #fff;
+  margin: 1.25rem 0;
+}
+
+.interface-table {
+  height: calc(100% - 17.1875rem);
+  overflow-x: hidden;
+  background-color: #fff;
+  padding: 0 1.125rem 2.375rem;
+  box-sizing: border-box;
+}
+
+.zan-con {
+  display: flex;
+  width: 100%;
+  justify-content: space-around;
+  margin-top: 1.5rem;
+  text-align: center;
+  font-size: 1.125rem;
+}
+
+.zan-con .line {
+  height: 8rem;
+  width: 1px;
+  background: #ccc;
+}
+
+.zan-con .zan-contain {
+  display: flex;
+  justify-content: space-between;
+  flex-direction: column;
+}
+
+.zan-con .zan-contain p {
+  font-size: 2.25rem;
+}
+
+.zan-con .zan-contain p:first-child {
+  font-size: 0.875rem;
+  line-height: 1.5;
+}
+
+.zan-sub {
+  text-align: right;
+  margin-top: 1.25rem;
+  color: #a5a5a5;
+  font-size: 0.875rem;
+}
+
+.table-interface {
+  overflow-y: auto;
+  overflow-x: hidden;
+  height: calc(100% - 3rem);
+}
+
+.info-top {
+  padding: 20px 0;
+  display: flex;
+  justify-content: space-between;
+  border-bottom: 1px #a5a5a5 solid;
+}
+
+.o-span {
+  cursor: pointer;
+  color: rgb(7, 152, 244);
+}
+
+.add-con {
+  display: flex;
+  justify-content: space-between;
+}
+
+.add-left {
+  flex: 4;
+}
+
+.add-right {
+  flex: 1;
+  margin-left: 10%;
+}
+
+.avatar-img {
+  text-align: center;
+  margin-top: 10px;
+  &::before{
+    content: '*';
+    color: #F56C6C;
+  }
+}
+
+.dialog-footer {
+  text-align: center;
+}
+</style>
+
+<style>
+.avatar-uploader .el-upload {
+  border: 1px dashed #d9d9d9;
+  border-radius: 6px;
+  cursor: pointer;
+  position: relative;
+  overflow: hidden;
+}
+.avatar-uploader .el-upload:hover {
+  border-color: #409eff;
+}
+.avatar-uploader-icon {
+  font-size: 28px;
+  color: #8c939d;
+  width: 178px;
+  height: 178px;
+  line-height: 178px;
+  text-align: center;
+}
+.avatar {
+  width: 178px;
+  height: 178px;
+  display: block;
+}
+</style>

+ 265 - 0
backstage/src/pages/system/Worklog.vue

@@ -0,0 +1,265 @@
+<!--  -->
+<template>
+  <div>
+    <main-top :crumb="crumbData"></main-top>
+    <div class="table-interface">
+      <div class="top-body">
+        <div class="info-top">
+          <div class="info-left">
+            <el-input style="width:220px;margin:0 20px;" v-model="inputKey" placeholder="请输入关键字"></el-input>
+            <el-button type="primary" @click="refresh">查询</el-button>
+            <el-button @click="inputKey = '',refresh()">重置</el-button>
+          </div>
+        </div>
+        <el-table :data="tableData" style="width: 100%">
+          <el-table-column
+            v-for="(item, idx) in data"
+            :key="idx"
+            :prop="item.prop"
+            :label="item.label"
+          >
+          <template slot-scope="scope">
+            <span v-html="scope.row[item.prop]||'-'"></span>
+          </template>
+          </el-table-column>
+        </el-table>
+        <div class="e-pagination">
+          <el-pagination
+            @current-change="handleCurrentChange"
+            :current-page.sync="currentPage"
+            :page-size="size"
+:page-sizes="PAGESIZES"
+            layout="prev, pager, next, jumper"
+            :total="total"
+          ></el-pagination>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+
+import MainTop from '@/components/main-top'
+const crumbData = [
+  {
+    name: '日志管理',
+    id: 7
+  }
+]
+
+
+const TYPENAME = {
+  'part':'部件信息管理',
+  'building':'建筑物信息管理'
+
+}
+
+const STATUSNAME = {
+  1:'待审核',
+  2:'已同意',
+  3:'已驳回'
+}
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    MainTop
+  },
+  watch: {
+    currentPage () {
+      this.refresh()
+    },
+    size () {
+      this.refresh()
+    }
+  },
+  mounted () {
+    this.refresh()
+  },
+  methods:{
+     refresh () {
+      this.loading = true
+      this.getInformation()
+      this.loading = false
+    },
+     handleCurrentChange (val) {
+      this.currentPage = val
+    },
+     async getInformation () {
+      let params = {
+          pageNum:this.currentPage,
+          pageSize: this.size,
+          searchKey: this.inputKey
+      }
+
+      let result = await this.$http({
+        method: 'post',
+        data: params,
+        headers: {
+          token: window.localStorage.getItem('token')
+        },
+        url: '/manage/log/list'
+      })
+
+      if (result.code !== 0) {
+        return
+      }
+      this.tableData = result.data.list
+      this.total = result.data.total
+      this.tableData.forEach((item, i) => {
+        item['idx'] = i + 1
+        item['typeName'] = TYPENAME[item.type]
+        item['statusStr'] = STATUSNAME[item.status]
+      })
+    }
+  },
+  data () {
+    // 这里存放数据
+    let data = [
+      {
+        prop: 'idx',
+        label: '序号'
+      },
+      {
+        prop: 'userName',
+        label: '账号'
+      },
+      {
+        prop: 'type',
+        label: '操作模块'
+      },
+      {
+        prop: 'description',
+        label: '操作事件'
+      },
+      {
+        prop: 'createTime',
+        label: '操作时间'
+      }
+    ]
+
+    return {
+      crumbData,
+      data,
+      region: 'all',
+      region1: 'all',
+      inputKey: '',
+      tableData: [],
+      currentPage: 1,
+      size: 25,
+      total:0
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+.top-body{
+  border-top: .0625rem solid #e6e6e6;
+  line-height: 1.5;
+  padding: 0 1.25rem 1.25rem;
+  align-items: center;
+  box-sizing: border-box;
+  background: #fff;
+  margin: 1rem 0;
+}
+
+.top-body .top-con{
+  font-weight: bold;
+}
+.table-title{
+  padding: 1rem 1rem 1rem 0 ;
+  font-size: 1rem;
+  color: #2d2d2d;
+  border-bottom: 1px solid #ccc;
+  display: flex;
+  justify-content: space-between;
+}
+
+.more{
+  color: #ec652d;
+  cursor: pointer;
+}
+
+.top-right{
+  background-color: #ec652d;
+  height: 3.5625rem;
+  line-height: 3.5625rem;
+  color: #fff;
+  padding: 0 1.875rem;
+  border-radius: .3125rem;
+  font-size: 1.125rem;
+  font-weight: bold;
+}
+
+.search-body{
+  background-color: #fff;
+  margin: 1.25rem 0;
+}
+
+.interface-table{
+  height: calc(100% - 17.1875rem);
+  overflow-x: hidden;
+  background-color: #fff;
+  padding: 0 1.125rem 2.375rem;
+  box-sizing: border-box;
+}
+
+.zan-con{
+  display: flex;
+  width: 100%;
+  justify-content: space-around;
+  margin-top: 1.5rem;
+  text-align: center;
+  font-size: 1.125rem;
+}
+
+.zan-con .line{
+  height: 8rem;
+  width: 1px;
+  background: #ccc;
+}
+
+.zan-con .zan-contain{
+  display: flex;
+  justify-content: space-between;
+  flex-direction: column;
+}
+
+.zan-con .zan-contain p{
+  font-size: 2.25rem;
+}
+
+.zan-con .zan-contain p:first-child{
+  font-size: .875rem;
+  line-height: 1.5;
+}
+
+.zan-sub{
+  text-align: right;
+  margin-top: 1.25rem;
+  color: #a5a5a5;
+  font-size: .875rem;
+}
+
+.table-interface{
+  overflow-y: auto;
+  overflow-x: hidden;
+  height: calc(100% - 3rem);
+}
+
+
+.info-top{
+  padding: 20px 0;
+  display: flex;
+  justify-content: space-between;
+  border-bottom: 1px #a5a5a5 solid;
+}
+
+.o-span{
+  cursor: pointer;
+  color: rgb(7, 152, 244);
+}
+</style>

+ 107 - 0
backstage/src/router/index.js

@@ -0,0 +1,107 @@
+import Vue from "vue";
+import VueRouter from "vue-router";
+
+Vue.use(VueRouter);
+const originalPush = VueRouter.prototype.push;
+VueRouter.prototype.push = function push(location) {
+  return originalPush.call(this, location).catch((err) => err);
+};
+
+const routes = [
+  {
+    path: "/",
+    name: "Layout",
+    component: () => import( "../pages/layout/index.vue"),
+    children:[
+      {
+        path:'/',
+        redirect:'/dynamic'
+      },
+      {
+        path:'/dynamic',
+        name: 'dynamic',
+        component:  () => import( "../pages/content/Dynamic.vue"),
+        meta: {index: 2}
+      },
+      {
+        path:'/edit-dynamic/:type/:id',
+        name: 'edit-dynamic',
+        component:  () => import( "../pages/edit/dynamic.vue"),
+        meta: {index: 2}
+      },
+      {
+        path:'/spirit',
+        name: 'spirit',
+        component:  () => import( "../pages/content/Spirit.vue"),
+        meta: {index: 3}
+      },
+      {
+        path:'/edit-spirit/:type/:id',
+        name: 'edit-spirit',
+        component:  () => import( "../pages/edit/spirit.vue"),
+        meta: {index: 3}
+      },
+      
+      {
+        path:'/collection/:type',
+        name: 'collection',
+        component:  () => import( "../pages/content/Collection.vue"),
+        meta: {index: 4}
+      },
+     
+      {
+        path:'/edit-collection/:type/:id',
+        name: 'edit-collection',
+        component:  () => import( "../pages/edit/collection.vue"),
+        meta: {index: 4}
+      },
+
+      {
+        path:'/menu',
+        name: 'menu',
+        component:  () => import( "../pages/system/Menu.vue"),
+        meta: {index: 7,role:'sys_admin'}
+      },
+      {
+        path:'/parameter',
+        name: 'parameter',
+        component:  () => import( "../pages/system/Parameter.vue"),
+        meta: {index: 8,role:'sys_admin'}
+      },
+      
+      {
+        path:'/worklog',
+        name: 'worklog',
+        component:  () => import( "../pages/system/Worklog.vue"),
+        meta: {index: 10,role:'sys_admin'}
+      },
+      {
+        path:'/password',
+        name: 'password',
+        component:  () => import( "../pages/system/Password.vue"),
+        meta: {index: 11}
+      }
+    ]
+  },
+  {
+    path: '/login',
+    name: 'login',
+    component: () => import( "../pages/login/")
+  }
+];
+
+const router = new VueRouter({
+  routes,
+});
+
+router.beforeEach((to,from,next)=>{
+  let role = window.localStorage.getItem('role')
+  if(to.meta.role==="sys_admin"){
+    role==='sys_admin'?next():next('/')
+  }
+  else{
+    next();
+  }
+})
+
+export default router;

+ 25 - 0
backstage/src/util/commonfn.js

@@ -0,0 +1,25 @@
+import Vue from 'vue'
+
+let vue = new Vue()
+
+function changeState(item, type, cb=()=>{}) {
+  vue.$http
+    .get(`/manage/${type}/display/${item.id}/${item.display * 1}`, {
+      headers: {
+        token: window.localStorage.getItem("token"),
+      },
+    })
+    .then((res) => {
+      if (res.code === 0) {
+        cb(res)
+        // this.refresh();
+      } else {
+        vue.$notify.error({
+          title: "错误",
+          message: res.msg
+        });
+      }
+    });
+}
+
+export {changeState};

+ 147 - 0
backstage/src/util/index.js

@@ -0,0 +1,147 @@
+function randomWord (randomFlag, min, max) {
+  let str = ''
+  let range = min
+  let 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++) {
+    let pos = Math.round(Math.random() * (arr.length - 1))
+    str += arr[pos]
+  }
+  return str
+}
+module.exports = {
+  getPosition: function (dom, parent) {
+    var ret = {
+      x: 0,
+      y: 0
+    }
+    while (dom && dom !== parent && dom !== document.documentElement) {
+      ret.x += dom.offsetLeft
+      ret.y += dom.offsetTop
+      dom = dom.offsetParent
+    }
+    return ret
+  },
+  reg: {
+    idCard: /^\d{6}(18|19|20)?\d{2}(0[1-9]|1[012])(0[1-9]|[12]\d|3[01])\d{3}(\d|[xX])$/,
+    phone: /^1(?:3\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8\d|9\d)\d{8}$/,
+    email: /^[A-Za-z\d]+([_.-][A-Za-z\d]+)*@([A-Za-z\d]+[-.])+[A-Za-z\d]{2,4}$/,
+    guhua: /^(\(\d{3,4}\)|\d{3,4}-|\s)?\d{7,14}$/
+  },
+  isEmptyObject: function (obj) {
+    for (var key in obj) {
+      return false
+    }
+    return true
+  },
+  
+  isWide: function () {
+    return Math.round(devicePixelRatio * 100) === 100 ? window.innerWidth > 1600 : true
+  },
+  isWorkPhone: function (phone) {
+    let phones = [
+      /^1(?:3\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8\d|9\d)\d{8}$/,
+      /^(\+?213|0)(5|6|7)\d{8}$/,
+      /^(!?(\+?963)|0)?9\d{8}$/,
+      /^(!?(\+?966)|0)?5\d{8}$/,
+      /^(\+?1)?[2-9]\d{2}[2-9](?!11)\d{6}$/,
+      /^(\+?420)? ?[1-9][0-9]{2} ?[0-9]{3} ?[0-9]{3}$/,
+      /^(\+?49[ .-])?([(]{1}[0-9]{1,6}[)])?([0-9 .-/]{3,20})((x|ext|extension)[ ]?[0-9]{1,4})?$/,
+      /^(\+?45)?(\d{8})$/,
+      /^(\+?30)?(69\d{8})$/,
+      /^(\+?61|0)4\d{8}$/,
+      /^(\+?44|0)7\d{9}$/,
+      /^(\+?852-?)?[569]\d{3}-?\d{4}$/,
+      /^(\+?91|0)?[789]\d{9}$/,
+      /^(\+?64|0)2\d{7,9}$/,
+      /^(\+?27|0)\d{9}$/,
+      /^(\+?26)?09[567]\d{7}$/,
+      /^(\+?34)?(6\d{1}|7[1234])\d{7}$/,
+      /^(\+?358|0)\s?(4(0|1|2|4|5)?|50)\s?(\d\s?){4,8}\d$/,
+      /^(\+?33|0)[67]\d{8}$/,
+      /^(\+972|0)([23489]|5[0248]|77)[1-9]\d{6}/,
+      /^(\+?36)(20|30|70)\d{7}$/,
+      /^(\+?39)?\s?3\d{2} ?\d{6,7}$/,
+      /^(\+?81|0)\d{1,4}[ -]?\d{1,4}[ -]?\d{4}$/,
+      /^(\+?6?01){1}(([145]{1}(-|\s)?\d{7,8})|([236789]{1}(\s|-)?\d{7}))$/,
+      /^(\+?47)?[49]\d{7}$/,
+      /^(\+?32|0)4?\d{8}$/,
+      /^(\+?47)?[49]\d{7}$/,
+      /^(\+?48)? ?[5-8]\d ?\d{3} ?\d{2} ?\d{2}$/,
+      /^(\+?55|0)-?[1-9]{2}-?[2-9]{1}\d{3,4}-?\d{4}$/,
+      /^(\+?351)?9[1236]\d{7}$/,
+      /^(\+?7|8)?9\d{9}$/,
+      /^(\+3816|06)[- \d]{5,9}$/,
+      /^(\+?90|0)?5\d{9}$/,
+      /^(\+?84|0)?((1(2([0-9])|6([2-9])|88|99))|(9((?!5)[0-9])))([0-9]{7})$/,
+      /^(\+?0?86-?)?1[345789]\d{9}$/,
+      /^(\+?886-?|0)?9\d{8}$/
+    ]
+    for (let i = 0; i < phones.length; i++) {
+      if (phones[i].test(phone)) {
+        return true
+      }
+    }
+    return false
+  },
+  formatDate: function (time) {
+    var date = new Date(time)
+    var year = date.getFullYear()
+    var month = date.getMonth() + 1
+    var day = date.getDate()
+    var hour = date.getHours()
+    var minute = date.getMinutes()
+    var second = date.getSeconds()
+    return year + '-' + (String(month).length > 1 ? month : '0' + month) + '-' +
+      (String(day).length > 1 ? day : '0' + day)
+      + ' ' + (String(hour).length > 1 ? hour : '0' + hour) + ':' + (String(minute).length > 1 ? minute : '0' + minute) +
+       ':' + (String(second).length > 1 ? second : '0' + second)
+  },
+
+  encodeStr: function (str, strv = '') {
+    const NUM = 2
+    const front = randomWord(false, 8)
+    const middle = randomWord(false, 8)
+    const end = randomWord(false, 8)
+
+    let str1 = str.substring(0, NUM)
+    let str2 = str.substring(NUM)
+
+    if (strv) {
+      let strv1 = strv.substring(0, NUM)
+      let strv2 = strv.substring(NUM)
+      return [front + str2 + middle + str1 + end, front + strv2 + middle + strv1 + end]
+    }
+
+    return front + str2 + middle + str1 + end
+  },
+
+  isImage: function (fileName) {
+    if (typeof fileName !== 'string') return;
+    let name = fileName.toLowerCase();
+    return name.endsWith('.png') || name.endsWith('.jpeg') || name.endsWith('.jpg') || name.endsWith('.gif');
+  },
+  isTypeBySend: function (fileName, typeArr) {
+    if (typeof fileName !== 'string') return;
+    let name = fileName.toLowerCase();
+    let boo = false
+    typeArr.forEach(item => {
+      console.log(name.endsWith(item));
+      if (name.endsWith(item)) {
+        boo = true
+      }
+    });
+    return boo
+  },
+  handleError:function(){
+    this.$alert("上传失败,请稍后再试", "提示", {
+      confirmButtonText: "确定",
+      callback: () => {
+        this.loading.close()
+      },
+    });
+  }
+}

ファイルの差分が大きいため隠しています
+ 1 - 0
backstage/theme/alert.css


+ 1 - 0
backstage/theme/aside.css

@@ -0,0 +1 @@
+.el-aside{overflow:auto;-webkit-box-sizing:border-box;box-sizing:border-box;-ms-flex-negative:0;flex-shrink:0}

ファイルの差分が大きいため隠しています
+ 1 - 0
backstage/theme/autocomplete.css


ファイルの差分が大きいため隠しています
+ 1 - 0
backstage/theme/avatar.css


+ 1 - 0
backstage/theme/backtop.css

@@ -0,0 +1 @@
+.el-backtop{position:fixed;background-color:#FFF;width:40px;height:40px;border-radius:50%;color:#532F1C;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;font-size:20px;-webkit-box-shadow:0 0 6px rgba(0,0,0,.12);box-shadow:0 0 6px rgba(0,0,0,.12);cursor:pointer;z-index:5}.el-backtop:hover{background-color:#F2F6FC}

ファイルの差分が大きいため隠しています
+ 1 - 0
backstage/theme/badge.css


ファイルの差分が大きいため隠しています
+ 1 - 0
backstage/theme/base.css


+ 0 - 0
backstage/theme/breadcrumb-item.css


ファイルの差分が大きいため隠しています
+ 1 - 0
backstage/theme/breadcrumb.css


+ 0 - 0
backstage/theme/button-group.css


ファイルの差分が大きいため隠しています
+ 1 - 0
backstage/theme/button.css


ファイルの差分が大きいため隠しています
+ 1 - 0
backstage/theme/calendar.css


+ 1 - 0
backstage/theme/card.css

@@ -0,0 +1 @@
+.el-card{border-radius:4px;border:1px solid #EBEEF5;background-color:#FFF;overflow:hidden;color:#303133;-webkit-transition:.3s;transition:.3s}.el-card.is-always-shadow,.el-card.is-hover-shadow:focus,.el-card.is-hover-shadow:hover{-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-card__header{padding:18px 20px;border-bottom:1px solid #EBEEF5;-webkit-box-sizing:border-box;box-sizing:border-box}.el-card__body{padding:20px}

ファイルの差分が大きいため隠しています
+ 1 - 0
backstage/theme/carousel-item.css


ファイルの差分が大きいため隠しています
+ 1 - 0
backstage/theme/carousel.css


ファイルの差分が大きいため隠しています
+ 1 - 0
backstage/theme/cascader-panel.css


ファイルの差分が大きいため隠しています
+ 1 - 0
backstage/theme/cascader.css


+ 0 - 0
backstage/theme/checkbox-button.css


+ 0 - 0
backstage/theme/checkbox-group.css


ファイルの差分が大きいため隠しています
+ 1 - 0
backstage/theme/checkbox.css


+ 0 - 0
backstage/theme/col.css


この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません