zhuliu hai 11 meses
pai
achega
30fb5b6fa2
Modificáronse 51 ficheiros con 19254 adicións e 545 borrados
  1. 1 0
      .env.development
  2. 60 0
      mock/index.js
  3. 16813 512
      package-lock.json
  4. 13 0
      src/api/application.js
  5. 3 1
      src/api/index.js
  6. 51 0
      src/components/Breadcrumb/index.vue
  7. 44 0
      src/components/Hamburger/index.vue
  8. 57 0
      src/components/ScrollBar/index.vue
  9. 42 0
      src/components/SvgIcon/index.vue
  10. 128 0
      src/components/Tinymce/components/editorImage.vue
  11. 163 0
      src/components/Tinymce/index.vue
  12. 230 0
      src/components/Tinymce/zh_CN.js
  13. 121 0
      src/components/Upload/multiUpload.vue
  14. 122 0
      src/components/Upload/singleUpload.vue
  15. 9 0
      src/icons/index.js
  16. 1 0
      src/icons/svg/application.svg
  17. 1 0
      src/icons/svg/apply.svg
  18. 1 0
      src/icons/svg/home.svg
  19. 1 0
      src/icons/svg/vision.svg
  20. 7 0
      src/main.js
  21. 90 3
      src/router/index.js
  22. 3 0
      src/store/getters.js
  23. 6 0
      src/store/index.js
  24. 42 0
      src/store/modules/app.js
  25. 53 0
      src/store/modules/history.js
  26. 84 0
      src/store/modules/permission.js
  27. 29 0
      src/styles/element-ui.scss
  28. 159 0
      src/styles/index.scss
  29. 27 0
      src/styles/mixin.scss
  30. 105 0
      src/styles/sidebar.scss
  31. 32 0
      src/styles/transition.scss
  32. 4 0
      src/styles/variables.scss
  33. 61 0
      src/utils/common.js
  34. 21 0
      src/views/backStageManage/application/apply/components/addApply.vue
  35. 26 0
      src/views/backStageManage/application/apply/index.vue
  36. 33 0
      src/views/backStageManage/application/vision/index.vue
  37. 21 0
      src/views/backStageManage/home/index.vue
  38. 23 0
      src/views/backStageManage/index/index.vue
  39. 54 0
      src/views/backStageManage/layout/Layout.vue
  40. 46 0
      src/views/backStageManage/layout/components/AppMain.vue
  41. 140 0
      src/views/backStageManage/layout/components/Navbar.vue
  42. 65 0
      src/views/backStageManage/layout/components/Sidebar/SidebarItem.vue
  43. 38 0
      src/views/backStageManage/layout/components/Sidebar/index.vue
  44. 96 0
      src/views/backStageManage/layout/components/history.vue
  45. 4 0
      src/views/backStageManage/layout/components/index.js
  46. 41 0
      src/views/backStageManage/layout/mixin/ResizeHandler.js
  47. 37 0
      src/views/backStageManage/layout/mixin/index.js
  48. 21 0
      src/views/backStageManage/login/index.vue
  49. 1 1
      src/views/register/index.vue
  50. 3 2
      vue.config.js
  51. 21 26
      yarn.lock

+ 1 - 0
.env.development

@@ -0,0 +1 @@
+MOCK=true

+ 60 - 0
mock/index.js

@@ -0,0 +1,60 @@
+
+const Mock = require('mockjs')
+
+class Response{
+    success(data,message='请求成功') {
+        return {
+            code:200,
+            data:data,
+            message:message
+        }
+    }
+    error(data,message='请求失败') {
+        return {
+            code:500,
+            data:data,
+            message:message
+        }
+    }
+    any(code,data,message){
+        return {
+            code:code,
+            data:data,
+            message:message
+        }
+    }
+}
+const response = new Response()
+
+module.exports = function(app){
+    if(process.env.MOCK == 'true'){
+        app.post('/api/vision/list',function(rep,res){
+            let tableData = Mock.mock(
+                {
+                    'tableData|3' : [{
+                        'id|+1': 1,                   // 得到随机的id
+                        vision:"V@float(0,10,1,1)",
+                        createName: "@cname()",           // 随机生成中文名字
+                        createTime: "@date()",                // 随机生成日期                
+                        description: "@paragraph()",    // 描述
+                        ip: "@ip()",                    // ip地址
+                        email: "@email()",              // email
+                    }]
+                }
+            )
+            res.json(response.success(
+                {
+                    data:tableData.tableData
+                }
+            ))
+        })
+
+
+        app.get('//',(rep,res)=>{
+
+            let data = {}
+            res.json(response.error(data))
+        })
+    }
+}
+

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 16813 - 512
package-lock.json


+ 13 - 0
src/api/application.js

@@ -0,0 +1,13 @@
+import axios from '@/utils/axios'
+
+export default {
+    /**
+     * 获取应用版本
+     * @param {*} data 
+     * @returns 
+     */
+    getVision(data){
+        return axios.post('/vision/list',data)
+    },
+
+}

+ 3 - 1
src/api/index.js

@@ -2,9 +2,11 @@
 
 import permission from "./permission";
 import registerAndLogin from './registerAndLogin'
+import application from "./application";
 
 export default {
 
   ...permission,
-  ...registerAndLogin
+  ...registerAndLogin,
+  ...application
 }

+ 51 - 0
src/components/Breadcrumb/index.vue

@@ -0,0 +1,51 @@
+<template>
+  <el-breadcrumb class="app-breadcrumb" separator="/">
+    <transition-group name="breadcrumb">
+      <el-breadcrumb-item v-for="(item,index)  in levelList" :key="item.path" v-if="item.meta.title">
+        <span v-if="item.redirect==='noredirect'||index==levelList.length-1" class="no-redirect">{{item.meta.title}}</span>
+        <router-link v-else :to="item.redirect||item.path">{{item.meta.title}}</router-link>
+      </el-breadcrumb-item>
+    </transition-group>
+  </el-breadcrumb>
+</template>
+
+<script>
+export default {
+  created() {
+    this.getBreadcrumb()
+  },
+  data() {
+    return {
+      levelList: null
+    }
+  },
+  watch: {
+    $route() {
+      this.getBreadcrumb()
+    }
+  },
+  methods: {
+    getBreadcrumb() {
+      let matched = this.$route.matched.filter(item => item.name)
+      const first = matched[1]
+      if (first && first.name !== 'Administrator_home') {
+        matched = [{ path: '/administrator/home', meta: { title: '首页' }}].concat(matched)
+      }
+      this.levelList = matched
+    }
+  }
+}
+</script>
+
+<style rel="stylesheet/scss" lang="scss" scoped>
+  .app-breadcrumb.el-breadcrumb {
+    display: inline-block;
+    font-size: 14px;
+    line-height: 50px;
+    margin-left: 10px;
+    .no-redirect {
+      color: #97a8be;
+      cursor: text;
+    }
+  }
+</style>

+ 44 - 0
src/components/Hamburger/index.vue

@@ -0,0 +1,44 @@
+<template>
+  <div>
+    <svg t="1492500959545" @click="toggleClick" class="hamburger" :class="{'is-active':isActive}" style="" viewBox="0 0 1024 1024"
+      version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1691" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64">
+      <path d="M966.8023 568.849776 57.196677 568.849776c-31.397081 0-56.850799-25.452695-56.850799-56.850799l0 0c0-31.397081 25.452695-56.849776 56.850799-56.849776l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.849776l0 0C1023.653099 543.397081 998.200404 568.849776 966.8023 568.849776z"
+        p-id="1692"></path>
+      <path d="M966.8023 881.527125 57.196677 881.527125c-31.397081 0-56.850799-25.452695-56.850799-56.849776l0 0c0-31.397081 25.452695-56.849776 56.850799-56.849776l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.849776l0 0C1023.653099 856.07443 998.200404 881.527125 966.8023 881.527125z"
+        p-id="1693"></path>
+      <path d="M966.8023 256.17345 57.196677 256.17345c-31.397081 0-56.850799-25.452695-56.850799-56.849776l0 0c0-31.397081 25.452695-56.850799 56.850799-56.850799l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.850799l0 0C1023.653099 230.720755 998.200404 256.17345 966.8023 256.17345z"
+        p-id="1694"></path>
+    </svg>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'hamburger',
+  props: {
+    isActive: {
+      type: Boolean,
+      default: false
+    },
+    toggleClick: {
+      type: Function,
+      default: null
+    }
+  }
+}
+</script>
+
+<style scoped>
+.hamburger {
+	display: inline-block;
+	cursor: pointer;
+	width: 20px;
+	height: 20px;
+	transform: rotate(90deg);
+	transition: .38s;
+	transform-origin: 50% 50%;
+}
+.hamburger.is-active {
+	transform: rotate(0deg);
+}
+</style>

+ 57 - 0
src/components/ScrollBar/index.vue

@@ -0,0 +1,57 @@
+<template>
+  <div class="scroll-container" ref="scrollContainer" @wheel.prevent="handleScroll" >
+    <div class="scroll-wrapper" ref="scrollWrapper" :style="{top: top + 'px'}">
+      <slot></slot>
+    </div>
+  </div>
+</template>
+
+<script>
+const delta = 15
+
+export default {
+  name: 'scrollBar',
+  data() {
+    return {
+      top: 0
+    }
+  },
+  methods: {
+    handleScroll(e) {
+      const eventDelta = e.wheelDelta || -e.deltaY * 3
+      const $container = this.$refs.scrollContainer
+      const $containerHeight = $container.offsetHeight
+      const $wrapper = this.$refs.scrollWrapper
+      const $wrapperHeight = $wrapper.offsetHeight
+      if (eventDelta > 0) {
+        this.top = Math.min(0, this.top + eventDelta)
+      } else {
+        if ($containerHeight - delta < $wrapperHeight) {
+          if (this.top < -($wrapperHeight - $containerHeight + delta)) {
+            this.top = this.top
+          } else {
+            this.top = Math.max(this.top + eventDelta, $containerHeight - $wrapperHeight - delta)
+          }
+        } else {
+          this.top = 0
+        }
+      }
+    }
+  }
+}
+</script>
+
+<style rel="stylesheet/scss" lang="scss" scoped>
+@import '../../styles/variables.scss';
+
+.scroll-container {
+  position: relative;
+  width: 100%;
+  height: 100%;
+  background-color: $menuBg;
+  .scroll-wrapper {
+    position: absolute;
+     width: 100%!important;
+  }
+}
+</style>

+ 42 - 0
src/components/SvgIcon/index.vue

@@ -0,0 +1,42 @@
+<template>
+  <svg :class="svgClass" aria-hidden="true">
+    <use :xlink:href="iconName"></use>
+  </svg>
+</template>
+
+<script>
+export default {
+  name: 'svg-icon',
+  props: {
+    iconClass: {
+      type: String,
+      required: true
+    },
+    className: {
+      type: String
+    }
+  },
+  computed: {
+    iconName() {
+      return `#icon-${this.iconClass}`
+    },
+    svgClass() {
+      if (this.className) {
+        return 'svg-icon ' + this.className
+      } else {
+        return 'svg-icon'
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+.svg-icon {
+  width: 1.2em;
+  height: 1.2em;
+  vertical-align: -0.18em;
+  fill: currentColor;
+  overflow: hidden;
+}
+</style>

+ 128 - 0
src/components/Tinymce/components/editorImage.vue

@@ -0,0 +1,128 @@
+<template>
+  <div class="upload-container">
+    <el-button icon='el-icon-upload' size="mini" :style="{background:color,borderColor:color}"
+               @click=" dialogVisible=true" type="primary">上传图片
+    </el-button>
+    <el-dialog append-to-body :visible.sync="dialogVisible">
+      <el-upload class="editor-slide-upload"
+                 :action="useOss?ossUploadUrl:minioUploadUrl"
+                 :data="useOss?dataObj:null"
+                 :multiple="true"
+                 :file-list="fileList"
+                 :show-file-list="true"
+                 list-type="picture-card"
+                 :on-remove="handleRemove"
+                 :on-success="handleSuccess"
+                 :before-upload="beforeUpload">
+        <el-button size="small" type="primary">点击上传</el-button>
+      </el-upload>
+      <el-button @click="dialogVisible = false">取 消</el-button>
+      <el-button type="primary" @click="handleSubmit">确 定</el-button>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+  import {policy} from '@/api/oss'
+
+  export default {
+    name: 'editorSlideUpload',
+    props: {
+      color: {
+        type: String,
+        default: '#1890ff'
+      }
+    },
+    data() {
+      return {
+        dialogVisible: false,
+        listObj: {},
+        fileList: [],
+        dataObj: {
+          policy: '',
+          signature: '',
+          key: '',
+          ossaccessKeyId: '',
+          dir: '',
+          host: ''
+        },
+        useOss:false, //使用oss->true;使用MinIO->false
+        ossUploadUrl:'http://macro-oss.oss-cn-shenzhen.aliyuncs.com',
+        minioUploadUrl:'http://localhost:8080/minio/upload',
+      }
+    },
+    methods: {
+      checkAllSuccess() {
+        return Object.keys(this.listObj).every(item => this.listObj[item].hasSuccess)
+      },
+      handleSubmit() {
+        const arr = Object.keys(this.listObj).map(v => this.listObj[v])
+        if (!this.checkAllSuccess()) {
+          this.$message('请等待所有图片上传成功 或 出现了网络问题,请刷新页面重新上传!')
+          return
+        }
+        console.log(arr);
+        this.$emit('successCBK', arr);
+        this.listObj = {};
+        this.fileList = [];
+        this.dialogVisible = false;
+      },
+      handleSuccess(response, file) {
+        const uid = file.uid;
+        const objKeyArr = Object.keys(this.listObj)
+        for (let i = 0, len = objKeyArr.length; i < len; i++) {
+          if (this.listObj[objKeyArr[i]].uid === uid) {
+            this.listObj[objKeyArr[i]].url = this.dataObj.host + '/' + this.dataObj.dir + '/' + file.name;
+            if(!this.useOss){
+              //不使用oss直接获取图片路径
+              this.listObj[objKeyArr[i]].url = response.data.url;
+            }
+            this.listObj[objKeyArr[i]].hasSuccess = true;
+            return
+          }
+        }
+      },
+      handleRemove(file) {
+        const uid = file.uid;
+        const objKeyArr = Object.keys(this.listObj);
+        for (let i = 0, len = objKeyArr.length; i < len; i++) {
+          if (this.listObj[objKeyArr[i]].uid === uid) {
+            delete this.listObj[objKeyArr[i]];
+            return
+          }
+        }
+      },
+      beforeUpload(file) {
+        const _self = this
+        const fileName = file.uid;
+        this.listObj[fileName] = {};
+        if(!this.useOss){
+          //不使用oss不需要获取策略
+          this.listObj[fileName] = {hasSuccess: false, uid: file.uid, width: this.width, height: this.height};
+          return true;
+        }
+        return new Promise((resolve, reject) => {
+          policy().then(response => {
+            _self.dataObj.policy = response.data.policy;
+            _self.dataObj.signature = response.data.signature;
+            _self.dataObj.ossaccessKeyId = response.data.accessKeyId;
+            _self.dataObj.key = response.data.dir + '/${filename}';
+            _self.dataObj.dir = response.data.dir;
+            _self.dataObj.host = response.data.host;
+            _self.listObj[fileName] = {hasSuccess: false, uid: file.uid, width: this.width, height: this.height};
+            resolve(true)
+          }).catch(err => {
+            console.log(err)
+            reject(false)
+          })
+        })
+      }
+    }
+  }
+</script>
+
+<style rel="stylesheet/scss" lang="scss" scoped>
+  .upload-container .editor-slide-upload{
+    margin-bottom: 20px;
+  }
+</style>

+ 163 - 0
src/components/Tinymce/index.vue

@@ -0,0 +1,163 @@
+<template>
+  <div class="tinymce-container editor-container">
+    <textarea class="tinymce-textarea" :id="tinymceId"></textarea>
+    <div class="editor-custom-btn-container">
+      <editorImage color="#1890ff" class="editor-upload-btn" @successCBK="imageSuccessCBK"></editorImage>
+    </div>
+  </div>
+</template>
+
+<script>
+  import editorImage from './components/editorImage'
+  import '../../../static/tinymce4.7.5/langs/zh_CN'
+
+  const plugins = [
+ `advlist anchor autolink autosave code codesample colorpicker colorpicker
+  contextmenu directionality emoticons fullscreen hr image imagetools importcss insertdatetime
+  legacyoutput link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace
+  spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount`
+  ];
+  const toolbar = [
+    `bold italic underline strikethrough alignleft aligncenter
+  alignright outdent indent  blockquote undo redo removeformat code`,
+    `hr bullist numlist link image charmap	 preview anchor pagebreak
+    fullscreen insertdatetime media table forecolor backcolor`
+  ];
+  export default {
+    name: 'tinymce',
+    components: {editorImage},
+    props: {
+      id: {
+        type: String
+      },
+      value: {
+        type: String,
+        default: ''
+      },
+      toolbar: {
+        type: Array,
+        required: false,
+        default() {
+          return []
+        }
+      },
+      menubar: {
+        default: 'file edit insert view format table'
+      },
+      height: {
+        type: Number,
+        required: false,
+        default: 360
+      },
+      width: {
+        type: Number,
+        required: false,
+        default: 720
+      }
+    },
+    data() {
+      return {
+        hasChange: false,
+        hasInit: false,
+        tinymceId: this.id || 'vue-tinymce-' + +new Date()
+      }
+    },
+    watch: {
+      value(val) {
+        if (!this.hasChange && this.hasInit) {
+          this.$nextTick(() => window.tinymce.get(this.tinymceId).setContent(val))
+        }
+      }
+    },
+    mounted() {
+      this.initTinymce()
+    },
+    activated() {
+      this.initTinymce()
+    },
+    deactivated() {
+      this.destroyTinymce()
+    },
+    methods: {
+      initTinymce() {
+        const _this = this
+        window.tinymce.init({
+          selector: `#${this.tinymceId}`,
+          width: this.width,
+          height: this.height,
+          language: 'zh_CN',
+          body_class: 'panel-body ',
+          object_resizing: false,
+          toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar,
+          menubar: false,
+          plugins: plugins,
+          end_container_on_empty_block: true,
+          powerpaste_word_import: 'clean',
+          code_dialog_height: 450,
+          code_dialog_width: 1000,
+          advlist_bullet_styles: 'square',
+          advlist_number_styles: 'default',
+          imagetools_cors_hosts: ['www.tinymce.com', 'codepen.io'],
+          default_link_target: '_blank',
+          link_title: false,
+          init_instance_callback: editor => {
+            if (_this.value) {
+              editor.setContent(_this.value)
+            }
+            _this.hasInit = true
+            editor.on('NodeChange Change KeyUp SetContent', () => {
+              this.hasChange = true
+              this.$emit('input', editor.getContent())
+            })
+          }
+        })
+      },
+      destroyTinymce() {
+        if (window.tinymce.get(this.tinymceId)) {
+          window.tinymce.get(this.tinymceId).destroy()
+        }
+      },
+      setContent(value) {
+        window.tinymce.get(this.tinymceId).setContent(value)
+      },
+      getContent() {
+        window.tinymce.get(this.tinymceId).getContent()
+      },
+      imageSuccessCBK(arr) {
+        const _this = this
+        arr.forEach(v => {
+          window.tinymce.get(_this.tinymceId).insertContent(`<img class="wscnph" src="${v.url}" >`)
+        })
+      }
+    },
+    destroyed() {
+      this.destroyTinymce()
+    }
+  }
+</script>
+
+<style scoped>
+  .tinymce-container {
+    position: relative;
+  }
+
+  .tinymce-container >>> .mce-fullscreen {
+    z-index: 10000;
+  }
+
+  .tinymce-textarea {
+    visibility: hidden;
+    z-index: -1;
+  }
+
+  .editor-custom-btn-container {
+    position: absolute;
+    right: 10px;
+    top: 2px;
+    /*z-index: 2005;*/
+  }
+
+  .editor-upload-btn {
+    display: inline-block;
+  }
+</style>

+ 230 - 0
src/components/Tinymce/zh_CN.js

@@ -0,0 +1,230 @@
+tinymce.addI18n('zh_CN',{
+"Cut": "\u526a\u5207",
+"Heading 5": "\u6807\u98985",
+"Header 2": "\u6807\u98982",
+"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u4f60\u7684\u6d4f\u89c8\u5668\u4e0d\u652f\u6301\u5bf9\u526a\u8d34\u677f\u7684\u8bbf\u95ee\uff0c\u8bf7\u4f7f\u7528Ctrl+X\/C\/V\u952e\u8fdb\u884c\u590d\u5236\u7c98\u8d34\u3002",
+"Heading 4": "\u6807\u98984",
+"Div": "Div\u533a\u5757",
+"Heading 2": "\u6807\u98982",
+"Paste": "\u7c98\u8d34",
+"Close": "\u5173\u95ed",
+"Font Family": "\u5b57\u4f53",
+"Pre": "\u9884\u683c\u5f0f\u6587\u672c",
+"Align right": "\u53f3\u5bf9\u9f50",
+"New document": "\u65b0\u6587\u6863",
+"Blockquote": "\u5f15\u7528",
+"Numbered list": "\u7f16\u53f7\u5217\u8868",
+"Heading 1": "\u6807\u98981",
+"Headings": "\u6807\u9898",
+"Increase indent": "\u589e\u52a0\u7f29\u8fdb",
+"Formats": "\u683c\u5f0f",
+"Headers": "\u6807\u9898",
+"Select all": "\u5168\u9009",
+"Header 3": "\u6807\u98983",
+"Blocks": "\u533a\u5757",
+"Undo": "\u64a4\u6d88",
+"Strikethrough": "\u5220\u9664\u7ebf",
+"Bullet list": "\u9879\u76ee\u7b26\u53f7",
+"Header 1": "\u6807\u98981",
+"Superscript": "\u4e0a\u6807",
+"Clear formatting": "\u6e05\u9664\u683c\u5f0f",
+"Font Sizes": "\u5b57\u53f7",
+"Subscript": "\u4e0b\u6807",
+"Header 6": "\u6807\u98986",
+"Redo": "\u91cd\u590d",
+"Paragraph": "\u6bb5\u843d",
+"Ok": "\u786e\u5b9a",
+"Bold": "\u7c97\u4f53",
+"Code": "\u4ee3\u7801",
+"Italic": "\u659c\u4f53",
+"Align center": "\u5c45\u4e2d",
+"Header 5": "\u6807\u98985",
+"Heading 6": "\u6807\u98986",
+"Heading 3": "\u6807\u98983",
+"Decrease indent": "\u51cf\u5c11\u7f29\u8fdb",
+"Header 4": "\u6807\u98984",
+"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u5f53\u524d\u4e3a\u7eaf\u6587\u672c\u7c98\u8d34\u6a21\u5f0f\uff0c\u518d\u6b21\u70b9\u51fb\u53ef\u4ee5\u56de\u5230\u666e\u901a\u7c98\u8d34\u6a21\u5f0f\u3002",
+"Underline": "\u4e0b\u5212\u7ebf",
+"Cancel": "\u53d6\u6d88",
+"Justify": "\u4e24\u7aef\u5bf9\u9f50",
+"Inline": "\u6587\u672c",
+"Copy": "\u590d\u5236",
+"Align left": "\u5de6\u5bf9\u9f50",
+"Visual aids": "\u7f51\u683c\u7ebf",
+"Lower Greek": "\u5c0f\u5199\u5e0c\u814a\u5b57\u6bcd",
+"Square": "\u65b9\u5757",
+"Default": "\u9ed8\u8ba4",
+"Lower Alpha": "\u5c0f\u5199\u82f1\u6587\u5b57\u6bcd",
+"Circle": "\u7a7a\u5fc3\u5706",
+"Disc": "\u5b9e\u5fc3\u5706",
+"Upper Alpha": "\u5927\u5199\u82f1\u6587\u5b57\u6bcd",
+"Upper Roman": "\u5927\u5199\u7f57\u9a6c\u5b57\u6bcd",
+"Lower Roman": "\u5c0f\u5199\u7f57\u9a6c\u5b57\u6bcd",
+"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\u6807\u8bc6\u7b26\u5e94\u8be5\u4ee5\u5b57\u6bcd\u5f00\u5934\uff0c\u540e\u8ddf\u5b57\u6bcd\u3001\u6570\u5b57\u3001\u7834\u6298\u53f7\u3001\u70b9\u3001\u5192\u53f7\u6216\u4e0b\u5212\u7ebf\u3002",
+"Name": "\u540d\u79f0",
+"Anchor": "\u951a\u70b9",
+"Id": "\u6807\u8bc6\u7b26",
+"You have unsaved changes are you sure you want to navigate away?": "\u4f60\u8fd8\u6709\u6587\u6863\u5c1a\u672a\u4fdd\u5b58\uff0c\u786e\u5b9a\u8981\u79bb\u5f00\uff1f",
+"Restore last draft": "\u6062\u590d\u4e0a\u6b21\u7684\u8349\u7a3f",
+"Special character": "\u7279\u6b8a\u7b26\u53f7",
+"Source code": "\u6e90\u4ee3\u7801",
+"Language": "\u8bed\u8a00",
+"Insert\/Edit code sample": "\u63d2\u5165\/\u7f16\u8f91\u4ee3\u7801\u793a\u4f8b",
+"B": "B",
+"R": "R",
+"G": "G",
+"Color": "\u989c\u8272",
+"Right to left": "\u4ece\u53f3\u5230\u5de6",
+"Left to right": "\u4ece\u5de6\u5230\u53f3",
+"Emoticons": "\u8868\u60c5",
+"Robots": "\u673a\u5668\u4eba",
+"Document properties": "\u6587\u6863\u5c5e\u6027",
+"Title": "\u6807\u9898",
+"Keywords": "\u5173\u952e\u8bcd",
+"Encoding": "\u7f16\u7801",
+"Description": "\u63cf\u8ff0",
+"Author": "\u4f5c\u8005",
+"Fullscreen": "\u5168\u5c4f",
+"Horizontal line": "\u6c34\u5e73\u5206\u5272\u7ebf",
+"Horizontal space": "\u6c34\u5e73\u8fb9\u8ddd",
+"Insert\/edit image": "\u63d2\u5165\/\u7f16\u8f91\u56fe\u7247",
+"General": "\u666e\u901a",
+"Advanced": "\u9ad8\u7ea7",
+"Source": "\u5730\u5740",
+"Border": "\u8fb9\u6846",
+"Constrain proportions": "\u4fdd\u6301\u7eb5\u6a2a\u6bd4",
+"Vertical space": "\u5782\u76f4\u8fb9\u8ddd",
+"Image description": "\u56fe\u7247\u63cf\u8ff0",
+"Style": "\u6837\u5f0f",
+"Dimensions": "\u5927\u5c0f",
+"Insert image": "\u63d2\u5165\u56fe\u7247",
+"Image": "\u56fe\u7247",
+"Zoom in": "\u653e\u5927",
+"Contrast": "\u5bf9\u6bd4\u5ea6",
+"Back": "\u540e\u9000",
+"Gamma": "\u4f3d\u9a6c\u503c",
+"Flip horizontally": "\u6c34\u5e73\u7ffb\u8f6c",
+"Resize": "\u8c03\u6574\u5927\u5c0f",
+"Sharpen": "\u9510\u5316",
+"Zoom out": "\u7f29\u5c0f",
+"Image options": "\u56fe\u7247\u9009\u9879",
+"Apply": "\u5e94\u7528",
+"Brightness": "\u4eae\u5ea6",
+"Rotate clockwise": "\u987a\u65f6\u9488\u65cb\u8f6c",
+"Rotate counterclockwise": "\u9006\u65f6\u9488\u65cb\u8f6c",
+"Edit image": "\u7f16\u8f91\u56fe\u7247",
+"Color levels": "\u989c\u8272\u5c42\u6b21",
+"Crop": "\u88c1\u526a",
+"Orientation": "\u65b9\u5411",
+"Flip vertically": "\u5782\u76f4\u7ffb\u8f6c",
+"Invert": "\u53cd\u8f6c",
+"Date\/time": "\u65e5\u671f\/\u65f6\u95f4",
+"Insert date\/time": "\u63d2\u5165\u65e5\u671f\/\u65f6\u95f4",
+"Remove link": "\u5220\u9664\u94fe\u63a5",
+"Url": "\u5730\u5740",
+"Text to display": "\u663e\u793a\u6587\u5b57",
+"Anchors": "\u951a\u70b9",
+"Insert link": "\u63d2\u5165\u94fe\u63a5",
+"Link": "\u94fe\u63a5",
+"New window": "\u5728\u65b0\u7a97\u53e3\u6253\u5f00",
+"None": "\u65e0",
+"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u5c5e\u4e8e\u5916\u90e8\u94fe\u63a5\uff0c\u9700\u8981\u52a0\u4e0ahttp:\/\/:\u524d\u7f00\u5417\uff1f",
+"Paste or type a link": "\u7c98\u8d34\u6216\u8f93\u5165\u94fe\u63a5",
+"Target": "\u6253\u5f00\u65b9\u5f0f",
+"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u4e3a\u90ae\u4ef6\u5730\u5740\uff0c\u9700\u8981\u52a0\u4e0amailto:\u524d\u7f00\u5417\uff1f",
+"Insert\/edit link": "\u63d2\u5165\/\u7f16\u8f91\u94fe\u63a5",
+"Insert\/edit video": "\u63d2\u5165\/\u7f16\u8f91\u89c6\u9891",
+"Media": "\u5a92\u4f53",
+"Alternative source": "\u955c\u50cf",
+"Paste your embed code below:": "\u5c06\u5185\u5d4c\u4ee3\u7801\u7c98\u8d34\u5728\u4e0b\u9762:",
+"Insert video": "\u63d2\u5165\u89c6\u9891",
+"Poster": "\u5c01\u9762",
+"Insert\/edit media": "\u63d2\u5165\/\u7f16\u8f91\u5a92\u4f53",
+"Embed": "\u5185\u5d4c",
+"Nonbreaking space": "\u4e0d\u95f4\u65ad\u7a7a\u683c",
+"Page break": "\u5206\u9875\u7b26",
+"Paste as text": "\u7c98\u8d34\u4e3a\u6587\u672c",
+"Preview": "\u9884\u89c8",
+"Print": "\u6253\u5370",
+"Save": "\u4fdd\u5b58",
+"Could not find the specified string.": "\u672a\u627e\u5230\u641c\u7d22\u5185\u5bb9.",
+"Replace": "\u66ff\u6362",
+"Next": "\u4e0b\u4e00\u4e2a",
+"Whole words": "\u5168\u5b57\u5339\u914d",
+"Find and replace": "\u67e5\u627e\u548c\u66ff\u6362",
+"Replace with": "\u66ff\u6362\u4e3a",
+"Find": "\u67e5\u627e",
+"Replace all": "\u5168\u90e8\u66ff\u6362",
+"Match case": "\u533a\u5206\u5927\u5c0f\u5199",
+"Prev": "\u4e0a\u4e00\u4e2a",
+"Spellcheck": "\u62fc\u5199\u68c0\u67e5",
+"Finish": "\u5b8c\u6210",
+"Ignore all": "\u5168\u90e8\u5ffd\u7565",
+"Ignore": "\u5ffd\u7565",
+"Add to Dictionary": "\u6dfb\u52a0\u5230\u5b57\u5178",
+"Insert row before": "\u5728\u4e0a\u65b9\u63d2\u5165",
+"Rows": "\u884c",
+"Height": "\u9ad8",
+"Paste row after": "\u7c98\u8d34\u5230\u4e0b\u65b9",
+"Alignment": "\u5bf9\u9f50\u65b9\u5f0f",
+"Border color": "\u8fb9\u6846\u989c\u8272",
+"Column group": "\u5217\u7ec4",
+"Row": "\u884c",
+"Insert column before": "\u5728\u5de6\u4fa7\u63d2\u5165",
+"Split cell": "\u62c6\u5206\u5355\u5143\u683c",
+"Cell padding": "\u5355\u5143\u683c\u5185\u8fb9\u8ddd",
+"Cell spacing": "\u5355\u5143\u683c\u5916\u95f4\u8ddd",
+"Row type": "\u884c\u7c7b\u578b",
+"Insert table": "\u63d2\u5165\u8868\u683c",
+"Body": "\u8868\u4f53",
+"Caption": "\u6807\u9898",
+"Footer": "\u8868\u5c3e",
+"Delete row": "\u5220\u9664\u884c",
+"Paste row before": "\u7c98\u8d34\u5230\u4e0a\u65b9",
+"Scope": "\u8303\u56f4",
+"Delete table": "\u5220\u9664\u8868\u683c",
+"H Align": "\u6c34\u5e73\u5bf9\u9f50",
+"Top": "\u9876\u90e8\u5bf9\u9f50",
+"Header cell": "\u8868\u5934\u5355\u5143\u683c",
+"Column": "\u5217",
+"Row group": "\u884c\u7ec4",
+"Cell": "\u5355\u5143\u683c",
+"Middle": "\u5782\u76f4\u5c45\u4e2d",
+"Cell type": "\u5355\u5143\u683c\u7c7b\u578b",
+"Copy row": "\u590d\u5236\u884c",
+"Row properties": "\u884c\u5c5e\u6027",
+"Table properties": "\u8868\u683c\u5c5e\u6027",
+"Bottom": "\u5e95\u90e8\u5bf9\u9f50",
+"V Align": "\u5782\u76f4\u5bf9\u9f50",
+"Header": "\u8868\u5934",
+"Right": "\u53f3\u5bf9\u9f50",
+"Insert column after": "\u5728\u53f3\u4fa7\u63d2\u5165",
+"Cols": "\u5217",
+"Insert row after": "\u5728\u4e0b\u65b9\u63d2\u5165",
+"Width": "\u5bbd",
+"Cell properties": "\u5355\u5143\u683c\u5c5e\u6027",
+"Left": "\u5de6\u5bf9\u9f50",
+"Cut row": "\u526a\u5207\u884c",
+"Delete column": "\u5220\u9664\u5217",
+"Center": "\u5c45\u4e2d",
+"Merge cells": "\u5408\u5e76\u5355\u5143\u683c",
+"Insert template": "\u63d2\u5165\u6a21\u677f",
+"Templates": "\u6a21\u677f",
+"Background color": "\u80cc\u666f\u8272",
+"Custom...": "\u81ea\u5b9a\u4e49...",
+"Custom color": "\u81ea\u5b9a\u4e49\u989c\u8272",
+"No color": "\u65e0",
+"Text color": "\u6587\u5b57\u989c\u8272",
+"Table of Contents": "\u5185\u5bb9\u5217\u8868",
+"Show blocks": "\u663e\u793a\u533a\u5757\u8fb9\u6846",
+"Show invisible characters": "\u663e\u793a\u4e0d\u53ef\u89c1\u5b57\u7b26",
+"Words: {0}": "\u5b57\u6570\uff1a{0}",
+"Insert": "\u63d2\u5165",
+"File": "\u6587\u4ef6",
+"Edit": "\u7f16\u8f91",
+"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u5728\u7f16\u8f91\u533a\u6309ALT-F9\u6253\u5f00\u83dc\u5355\uff0c\u6309ALT-F10\u6253\u5f00\u5de5\u5177\u680f\uff0c\u6309ALT-0\u67e5\u770b\u5e2e\u52a9",
+"Tools": "\u5de5\u5177",
+"View": "\u89c6\u56fe",
+"Table": "\u8868\u683c",
+"Format": "\u683c\u5f0f"
+});

+ 121 - 0
src/components/Upload/multiUpload.vue

@@ -0,0 +1,121 @@
+<template> 
+  <div>
+    <el-upload
+      :action="useOss?ossUploadUrl:minioUploadUrl"
+      :data="useOss?dataObj:null"
+      list-type="picture-card"
+      :file-list="fileList"
+      :before-upload="beforeUpload"
+      :on-remove="handleRemove"
+      :on-success="handleUploadSuccess"
+      :on-preview="handlePreview"
+      :limit="maxCount"
+      :on-exceed="handleExceed"
+    >
+      <i class="el-icon-plus"></i>
+    </el-upload>
+    <el-dialog :visible.sync="dialogVisible">
+      <img width="100%" :src="dialogImageUrl" alt="">
+    </el-dialog>
+  </div>
+</template>
+<script>
+  import {policy} from '@/api/oss'
+
+  export default {
+    name: 'multiUpload',
+    props: {
+      //图片属性数组
+      value: Array,
+      //最大上传图片数量
+      maxCount:{
+        type:Number,
+        default:5
+      }
+    },
+    data() {
+      return {
+        dataObj: {
+          policy: '',
+          signature: '',
+          key: '',
+          ossaccessKeyId: '',
+          dir: '',
+          host: ''
+        },
+        dialogVisible: false,
+        dialogImageUrl:null,
+        useOss:false, //使用oss->true;使用MinIO->false
+        ossUploadUrl:'http://macro-oss.oss-cn-shenzhen.aliyuncs.com',
+        minioUploadUrl:'http://localhost:8080/minio/upload',
+      };
+    },
+    computed: {
+      fileList() {
+        let fileList=[];
+        for(let i=0;i<this.value.length;i++){
+          fileList.push({url:this.value[i]});
+        }
+        return fileList;
+      }
+    },
+    methods: {
+      emitInput(fileList) {
+        let value=[];
+        for(let i=0;i<fileList.length;i++){
+          value.push(fileList[i].url);
+        }
+        this.$emit('input', value)
+      },
+      handleRemove(file, fileList) {
+        this.emitInput(fileList);
+      },
+      handlePreview(file) {
+        this.dialogVisible = true;
+        this.dialogImageUrl=file.url;
+      },
+      beforeUpload(file) {
+        let _self = this;
+        if(!this.useOss){
+          //不使用oss不需要获取策略
+          return true;
+        }
+        return new Promise((resolve, reject) => {
+          policy().then(response => {
+            _self.dataObj.policy = response.data.policy;
+            _self.dataObj.signature = response.data.signature;
+            _self.dataObj.ossaccessKeyId = response.data.accessKeyId;
+            _self.dataObj.key = response.data.dir + '/${filename}';
+            _self.dataObj.dir = response.data.dir;
+            _self.dataObj.host = response.data.host;
+            resolve(true)
+          }).catch(err => {
+            console.log(err)
+            reject(false)
+          })
+        })
+      },
+      handleUploadSuccess(res, file) {
+        let url = this.dataObj.host + '/' + this.dataObj.dir + '/' + file.name;
+        if(!this.useOss){
+          //不使用oss直接获取图片路径
+          url = res.data.url;
+        }
+        this.fileList.push({name: file.name,url:url});
+        this.emitInput(this.fileList);
+      },
+      handleExceed(files, fileList) {
+        this.$message({
+          message: '最多只能上传'+this.maxCount+'张图片',
+          type: 'warning',
+          duration:1000
+        });
+      },
+    }
+  }
+</script>
+<style>
+
+</style>
+
+

+ 122 - 0
src/components/Upload/singleUpload.vue

@@ -0,0 +1,122 @@
+<template>
+  <div>
+    <el-upload
+      :action="useOss?ossUploadUrl:minioUploadUrl"
+      :data="useOss?dataObj:null"
+      list-type="picture"
+      :multiple="false"
+      :show-file-list="showFileList"
+      :file-list="fileList"
+      :before-upload="beforeUpload"
+      :on-remove="handleRemove"
+      :on-success="handleUploadSuccess"
+      :on-preview="handlePreview">
+      <el-button size="small" type="primary">点击上传</el-button>
+      <div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过10MB</div>
+    </el-upload>
+    <el-dialog :visible.sync="dialogVisible">
+      <img width="100%" :src="fileList[0].url" alt="">
+    </el-dialog>
+  </div>
+</template>
+<script>
+  import {policy} from '@/api/oss'
+
+  export default {
+    name: 'singleUpload',
+    props: {
+      value: String
+    },
+    computed: {
+      imageUrl() {
+        return this.value;
+      },
+      imageName() {
+        if (this.value != null && this.value !== '') {
+          return this.value.substr(this.value.lastIndexOf("/") + 1);
+        } else {
+          return null;
+        }
+      },
+      fileList() {
+        return [{
+          name: this.imageName,
+          url: this.imageUrl
+        }]
+      },
+      showFileList: {
+        get: function () {
+          return this.value !== null && this.value !== ''&& this.value!==undefined;
+        },
+        set: function (newValue) {
+        }
+      }
+    },
+    data() {
+      return {
+        dataObj: {
+          policy: '',
+          signature: '',
+          key: '',
+          ossaccessKeyId: '',
+          dir: '',
+          host: '',
+          // callback:'',
+        },
+        dialogVisible: false,
+        useOss:false, //使用oss->true;使用MinIO->false
+        ossUploadUrl:'http://macro-oss.oss-cn-shenzhen.aliyuncs.com',
+        minioUploadUrl:'/fileManager/uploadNormalFile',
+      };
+    },
+    methods: {
+      emitInput(val) {
+        this.$emit('input', val)
+      },
+      handleRemove(file, fileList) {
+        this.emitInput('');
+      },
+      handlePreview(file) {
+        this.dialogVisible = true;
+      },
+      beforeUpload(file) {
+        let _self = this;
+        if(!this.useOss){
+          //不使用oss不需要获取策略
+          return true;
+        }
+        return new Promise((resolve, reject) => {
+          policy().then(response => {
+            _self.dataObj.policy = response.data.policy;
+            _self.dataObj.signature = response.data.signature;
+            _self.dataObj.ossaccessKeyId = response.data.accessKeyId;
+            _self.dataObj.key = response.data.dir + '/${filename}';
+            _self.dataObj.dir = response.data.dir;
+            _self.dataObj.host = response.data.host;
+            // _self.dataObj.callback = response.data.callback;
+            resolve(true)
+          }).catch(err => {
+            console.log(err)
+            reject(false)
+          })
+        })
+      },
+      handleUploadSuccess(res, file) {
+        this.showFileList = true;
+        this.fileList.pop();
+        let url = this.dataObj.host + '/' + this.dataObj.dir + '/' + file.name;
+        if(!this.useOss){
+          //不使用oss直接获取图片路径
+          url = res.data.url;
+        }
+        this.fileList.push({name: file.name, url: url});
+        this.emitInput(this.fileList[0].url);
+      }
+    }
+  }
+</script>
+<style>
+
+</style>
+
+

+ 9 - 0
src/icons/index.js

@@ -0,0 +1,9 @@
+import Vue from 'vue'
+import SvgIcon from '@/components/SvgIcon'// svg组件
+
+// register globally
+Vue.component('svg-icon', SvgIcon)
+
+const requireAll = requireContext => requireContext.keys().map(requireContext)
+const req = require.context('./svg', false, /\.svg$/)
+requireAll(req)

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 0
src/icons/svg/application.svg


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 0
src/icons/svg/apply.svg


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 0
src/icons/svg/home.svg


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 0
src/icons/svg/vision.svg


+ 7 - 0
src/main.js

@@ -18,6 +18,8 @@ require('./assets/css/fix.scss')
 require('./assets/css/layout.less')
 require('./assets/css/theme.css')
 import "@/icons/iconfont/iconfont.css"
+import '@/icons' 
+import '@/styles/index.scss'
 import lodash from 'lodash'
 
 import {hasPermission} from './directives/permission';
@@ -29,6 +31,11 @@ Vue.prototype.$permission = hasPermission
 import model from './utils/model'
 Vue.use(model)
 
+//全局js
+import commonJS from '@/utils/common.js'
+Vue.prototype.$commonJS = commonJS
+
+
 Vue.config.productionTip = false
 Vue.prototype.$constants = constants
 Vue.prototype.$api = api

+ 90 - 3
src/router/index.js

@@ -1,6 +1,6 @@
 import Vue from 'vue'
 import VueRouter from 'vue-router'
-
+import Store from '../store'
 Vue.use(VueRouter)
 
 const originalPush = VueRouter.prototype.push
@@ -39,11 +39,85 @@ const routes = [
   }
  
 ]
+/* 后台管理系统Layout */
+import administrator_layout from '@/views/backStageManage/layout/Layout.vue'
+export const backStageManageRouterItem = [
+  {
+    path: '/administrator',
+    component: administrator_layout,
+    redirect: '/administrator/home',
+    meta: {title: '首页', icon: 'home'},
+    children: [
+      {
+        path: 'home',
+        name: 'Administrator_home',
+        component: () => import('@/views/backStageManage/home/index.vue'),
+        meta: {title: '首页', icon: 'home'}
+      },
+    ]
+  },
+
+  {
+    path: '/administrator',
+    name:'Administrator_application',
+    component: administrator_layout,
+    redirect: '/administrator/apply',
+    meta: {title: '应用管理', icon: 'application'},
+    children: [
+      {
+        path: 'addApply',
+        name: 'Administrator_addApply',
+        component: () => import('@/views/backStageManage/application/apply/components/addApply.vue'),
+        meta: {title: '添加应用功能', icon: 'apply',keepAlive:true},
+        hidden:true
+      },
+      {
+        path: 'apply',
+        name: 'Administrator_apply',
+        component: () => import('@/views/backStageManage/application/apply/index.vue'),
+        meta: {title: '应用功能清单', icon: 'apply'}
+      },
+      {
+        path: 'vision',
+        name: 'Administrator_vision',
+        component: () => import('@/views/backStageManage/application/vision/index.vue'),
+        meta: {title: '应用版本清单', icon: 'vision'}
+      },
+    ]
+  },
+]
+export const backStageManage = [
+  {
+    path: '/administrator',
+    name: 'Administrator',
+    component: { render(c) { return c('router-view') } },
+    redirect: '/administrator',
+    children: [
+      {
+        path: '/',
+        name:'Administrator_Index',
+        meta: {
+          title: '后台管理',
+        },
+        component: () => import('@/views/backStageManage/index/index.vue'),
+      },
+      {
+        path: 'login',
+        name:'Administrator_login',
+        meta: {
+          title: '后台管理-登录',
+        },
+        component: () => import('@/views/backStageManage/login/index.vue'),
+      },
+      ...backStageManageRouterItem
+    ]
+  }
+]
 
 const router = new VueRouter({
   mode: 'history',
   base: '/',
-  routes
+  routes:[...routes,...backStageManage]
 })
 
 router.beforeEach((to, from, next) => {
@@ -53,8 +127,21 @@ router.beforeEach((to, from, next) => {
   next();
 })
 
-router.afterEach((to, from) => {
 
+// 在你的路由配置文件中
+router.afterEach((to, from) => {
+  var obj = {
+    name:to.meta.title,
+    path:to.path,
+    route:{
+      name:to.name,
+      path:to.path,
+      query:to.query,
+      params:to.params
+    }
+  }
+  Store.commit('addHistory',obj);
+  Store.commit('addHistoryPath',to.path);
 });
 
 export default router

+ 3 - 0
src/store/getters.js

@@ -1,3 +1,6 @@
 export default {
   userinfo: state => state.user.userinfo,
+  sidebar: state => state.app.sidebar,
+  device: state => state.app.device,
+  routers: state => state.permission.routers,
 }

+ 6 - 0
src/store/index.js

@@ -2,12 +2,18 @@ import Vue from 'vue'
 import Vuex from 'vuex'
 import user from './modules/user'
 import getters from './getters'
+import app from './modules/app'
+import history from './modules/history'
+import permission from './modules/permission'
 
 Vue.use(Vuex)
 
 export default new Vuex.Store({
   modules: {
     user,
+    app,
+    history,
+    permission
   },
   getters
 })

+ 42 - 0
src/store/modules/app.js

@@ -0,0 +1,42 @@
+import Cookies from 'js-cookie'
+
+const app = {
+  state: {
+    sidebar: {
+      opened: !+Cookies.get('sidebarStatus'),
+      withoutAnimation: false
+    },
+    device: 'desktop'
+  },
+  mutations: {
+    TOGGLE_SIDEBAR: state => {
+      if (state.sidebar.opened) {
+        Cookies.set('sidebarStatus', 1)
+      } else {
+        Cookies.set('sidebarStatus', 0)
+      }
+      state.sidebar.opened = !state.sidebar.opened
+    },
+    CLOSE_SIDEBAR: (state, withoutAnimation) => {
+      Cookies.set('sidebarStatus', 1)
+      state.sidebar.opened = false
+      state.sidebar.withoutAnimation = withoutAnimation
+    },
+    TOGGLE_DEVICE: (state, device) => {
+      state.device = device
+    }
+  },
+  actions: {
+    ToggleSideBar: ({ commit }) => {
+      commit('TOGGLE_SIDEBAR')
+    },
+    CloseSideBar({ commit }, { withoutAnimation }) {
+      commit('CLOSE_SIDEBAR', withoutAnimation)
+    },
+    ToggleDevice({ commit }, device) {
+      commit('TOGGLE_DEVICE', device)
+    }
+  }
+}
+
+export default app

+ 53 - 0
src/store/modules/history.js

@@ -0,0 +1,53 @@
+export default {
+  state: {
+    history:[],
+    historyPath:[],
+    currentPath:null,
+  },
+
+  mutations: {
+    addHistoryPath(state,path){
+      let paths=state.historyPath.filter(item=>{
+          return item!==path;
+      })
+      paths.push(path);
+      state.historyPath=paths;
+      state.currentPath=path;
+    },
+    addHistory(state,history){
+      if(!history.name){
+        return
+      }
+        let index=-1;
+        state.history.forEach((item,i)=>{
+            if(item.path===history.path)
+                index=i;
+        })
+       
+        if(index!==-1){
+          state.history[index] = history
+            return;
+        }
+        state.history.push(history)
+    },
+    removeHistory(state,path){
+        state.history=state.history.filter(item=>{
+            return item.path!==path;
+        })
+    },
+    removeHistoryPath(state,path){
+        state.historyPath=state.historyPath.filter(item=>{
+            return item!==path;
+        })
+        if(state.historyPath.length>0)
+            state.currentPath=state.historyPath[state.historyPath.length-1];
+        else
+            state.currentPath=null;
+    }
+
+  },
+
+  actions: {
+
+  }
+}

+ 84 - 0
src/store/modules/permission.js

@@ -0,0 +1,84 @@
+import {backStageManageRouterItem} from '@/router/index';
+//判断是否有权限访问该菜单
+function hasPermission(menus, route) {
+  if (route.name) {
+    let currMenu = getMenu(route.name, menus);
+    if (currMenu!=null) {
+      //设置菜单的标题、图标和可见性
+      if (currMenu.title != null && currMenu.title !== '') {
+        route.meta.title = currMenu.title;
+      }
+      if (currMenu.icon != null && currMenu.title !== '') {
+        route.meta.icon = currMenu.icon;
+      }
+      if(currMenu.hidden!=null){
+        route.hidden = currMenu.hidden !== 0;
+      }
+      if (currMenu.sort != null && currMenu.sort !== '') {
+        route.sort = currMenu.sort;
+      }
+      return true;
+    } else {
+      route.sort = 0;
+      if (route.hidden !== undefined && route.hidden === true) {
+        route.sort=-1;
+        return true;
+      } else {
+        return false;
+      }
+    }
+  } else {
+    return true
+  }
+}
+
+//根据路由名称获取菜单
+function getMenu(name, menus) {
+  for (let i = 0; i < menus.length; i++) {
+    let menu = menus[i];
+    if (name===menu.name) {
+      return menu;
+    }
+  }
+  return null;
+}
+
+//对菜单进行排序
+function sortRouters(accessedRouters) {
+  for (let i = 0; i < accessedRouters.length; i++) {
+    let router = accessedRouters[i];
+    if(router.children && router.children.length > 0){
+      router.children.sort(compare("sort"));
+    }
+  }
+  accessedRouters.sort(compare("sort"));
+}
+
+//降序比较函数
+function compare(p){
+  return function(m,n){
+    let a = m[p];
+    let b = n[p];
+    return b - a;
+  }
+}
+const permission = {
+  state: {
+    routers: backStageManageRouterItem,
+    addRouters: []
+  },
+  mutations: {
+    SET_ROUTERS: (state, routers) => {
+      state.addRouters = routers;
+      state.routers = backStageManage.concat(routers);
+    }
+  },
+  actions: {
+    GenerateRoutes({ commit }, data) {
+      
+    }
+  }
+};
+
+export default permission;
+

+ 29 - 0
src/styles/element-ui.scss

@@ -0,0 +1,29 @@
+ //to reset element-ui default css
+.el-upload {
+  input[type="file"] {
+    display: none !important;
+  }
+}
+
+.el-upload__input {
+  display: none;
+}
+
+//暂时性解决diolag 问题 https://github.com/ElemeFE/element/issues/2461
+.el-dialog {
+  transform: none;
+  left: 0;
+  position: relative;
+  margin: 0 auto;
+}
+
+//element ui upload
+.upload-container {
+  .el-upload {
+    width: 100%;
+    .el-upload-dragger {
+      width: 100%;
+      height: 200px;
+    }
+  }
+}

+ 159 - 0
src/styles/index.scss

@@ -0,0 +1,159 @@
+@import './variables.scss';
+@import './mixin.scss';
+@import './transition.scss';
+@import './element-ui.scss';
+@import './sidebar.scss';
+
+body {
+  -moz-osx-font-smoothing: grayscale;
+  -webkit-font-smoothing: antialiased;
+  text-rendering: optimizeLegibility;
+  font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
+}
+
+html {
+  box-sizing: border-box;
+}
+
+*,
+*:before,
+*:after {
+  // box-sizing: inherit;
+}
+
+div:focus{
+  outline: none;
+}
+
+a:focus,
+a:active {
+  outline: none;
+}
+
+a,
+a:focus,
+a:hover {
+  cursor: pointer;
+  color: inherit;
+  text-decoration: none;
+}
+
+.clearfix {
+  &:after {
+    visibility: hidden;
+    display: block;
+    font-size: 0;
+    content: " ";
+    clear: both;
+    height: 0;
+  }
+}
+
+//main-container全局样式
+.app-main{
+  min-height: 100%
+}
+
+.app-container {
+  padding: 20px;
+}
+
+//搜索栏样式
+.filter-container {
+
+}
+
+//操作栏样式
+.operate-container {
+  margin-top: 20px;
+}
+
+.operate-container .btn-add {
+  float: right;
+  margin-left: 10px;
+}
+
+//表格栏样式
+.table-container {
+  margin-top: 20px;
+}
+
+//批量操作栏样式
+.batch-operate-container {
+  display: inline-block;
+  margin-top: 20px;
+}
+
+//分页栏样式
+.pagination-container {
+  display: inline-block;
+  float: right;
+  margin-top: 20px;
+}
+
+//添加、更新表单样式
+.form-container {
+  position: absolute;
+  left: 0;
+  right: 0;
+  width:800px;
+  padding: 35px 35px 15px 35px;
+  margin: 20px auto;
+}
+
+//主标题
+.font-extra-large {
+  font-size: 20px;
+  color: #303133;
+}
+
+//标题
+.font-title-large {
+  font-size: 18px;
+  color: #303133;
+}
+
+//小标题
+.font-title-medium {
+  font-size: 16px;
+  color: #303133;
+}
+
+//正文
+.font-medium {
+  font-size: 16px;
+  color: #606266;
+}
+
+//正文
+.font-small {
+  font-size: 14px;
+  color: #606266;
+}
+
+//正文(小)
+.font-extra-small {
+  font-size: 13px;
+  color: #606266;
+}
+
+.color-main {
+  color: #409EFF;
+}
+
+.color-success {
+  color: #67C23A;
+}
+
+.color-warning {
+  color: #E6A23C;
+}
+
+.color-danger {
+  color: #F56C6C;
+}
+
+.color-info {
+  color: #909399;
+}
+

+ 27 - 0
src/styles/mixin.scss

@@ -0,0 +1,27 @@
+@mixin clearfix {
+  &:after {
+    content: "";
+    display: table;
+    clear: both;
+  }
+}
+
+@mixin scrollBar {
+  &::-webkit-scrollbar-track-piece {
+    background: #d3dce6;
+  }
+  &::-webkit-scrollbar {
+    width: 6px;
+  }
+  &::-webkit-scrollbar-thumb {
+    background: #99a9bf;
+    border-radius: 20px;
+  }
+}
+
+@mixin relative {
+  position: relative;
+  width: 100%;
+  height: 100%;
+}
+

+ 105 - 0
src/styles/sidebar.scss

@@ -0,0 +1,105 @@
+#app {
+
+  // 主体区域
+  .main-container {
+    min-height: 100%;
+    transition: margin-left .28s;
+    margin-left: 180px;
+  }
+
+   // 侧边栏
+  .sidebar-container {
+    .horizontal-collapse-transition {
+      transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;
+    }
+    transition: width .28s;
+    width: 180px !important;
+    height: 100%;
+    position: fixed;
+    font-size: 0px;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    z-index: 1001;
+    overflow: hidden;
+    a {
+      display: inline-block;
+      width: 100%;
+    }
+    .svg-icon {
+      margin-right: 16px;
+    }
+    .el-menu {
+      border: none;
+      width: 100% !important;
+    }
+  }
+
+  .hideSidebar {
+    .sidebar-container {
+      width: 36px !important;
+    }
+    .main-container {
+      margin-left: 36px;
+    }
+    .submenu-title-noDropdown {
+      padding-left: 10px !important;
+      position: relative;
+      .el-tooltip {
+        padding: 0 10px !important;
+      }
+    }
+    .el-submenu {
+      &>.el-submenu__title {
+        padding-left: 10px !important;
+        &>span {
+          height: 0;
+          width: 0;
+          overflow: hidden;
+          visibility: hidden;
+          display: inline-block;
+        }
+        .el-submenu__icon-arrow {
+          display: none;
+        }
+      }
+    }
+  }
+
+  .sidebar-container .nest-menu .el-submenu>.el-submenu__title,
+  .sidebar-container .el-submenu .el-menu-item {
+    min-width: 180px !important;
+    background-color: $subMenuBg !important;
+    &:hover {
+      background-color: $menuHover !important;
+    }
+  }
+  .el-menu--collapse .el-menu .el-submenu {
+    min-width: 180px !important;
+  }
+
+  //适配移动端
+  .mobile {
+    .main-container {
+      margin-left: 0px;
+    }
+    .sidebar-container {
+      top: 50px;
+      transition: transform .28s;
+      width: 180px !important;
+    }
+    &.hideSidebar {
+      .sidebar-container {
+        transition-duration: 0.3s;
+        transform: translate3d(-180px, 0, 0);
+      }
+    }
+  }
+
+  .withoutAnimation {
+    .main-container,
+    .sidebar-container {
+      transition: none;
+    }
+  }
+}

+ 32 - 0
src/styles/transition.scss

@@ -0,0 +1,32 @@
+//globl transition css
+
+/*fade*/
+.fade-enter-active,
+.fade-leave-active {
+  transition: opacity 0.28s;
+}
+
+.fade-enter,
+.fade-leave-active {
+  opacity: 0;
+}
+
+/*fade*/
+.breadcrumb-enter-active,
+.breadcrumb-leave-active {
+  transition: all .5s;
+}
+
+.breadcrumb-enter,
+.breadcrumb-leave-active {
+  opacity: 0;
+  transform: translateX(20px);
+}
+
+.breadcrumb-move {
+  transition: all .5s;
+}
+
+.breadcrumb-leave-active {
+  position: absolute;
+}

+ 4 - 0
src/styles/variables.scss

@@ -0,0 +1,4 @@
+//sidebar
+$menuBg:#304156;
+$subMenuBg:#1f2d3d;
+$menuHover:#001528;

+ 61 - 0
src/utils/common.js

@@ -0,0 +1,61 @@
+
+import { Message } from 'element-ui'
+import Config from '@/config'
+export default {
+  //查看文件
+  getFile(guid) {
+    if(!guid){
+      return ''
+    }
+    return `/fileManager/downloadFile?fileId=${guid}`
+  },
+   //校验文件是否全部上传
+   validFile(data,arr) {
+    if (data && data.length > 0) {
+      for(var i =0;i<data.length;i++){
+        if(data[i] && data[i].guid){
+          arr.push(data[i].guid)
+        }else{
+          return true
+        }
+      }
+      return false
+    } else {
+      return false
+    }
+  },
+  //校验是否全部上传
+  checkUploadFile(data) {
+    if(!data){
+      return []
+    }
+    var arr = []
+    let allUpload = this.validFile(data,arr)
+    if (allUpload) {
+      Message.warning('文件未全部上传,请耐心等待')
+      return false
+    }
+
+    return arr
+  },
+
+  //导出
+  downLoad(guid){
+    const href = this.getFile(guid)
+    const anchor = document.createElement('a');
+    const fileName = 'download';
+    if ('download' in anchor) {
+      anchor.href = href;
+      anchor.setAttribute("download", fileName);
+      anchor.className = "download-js-link";
+      anchor.innerHTML = "downloading...";
+      anchor.style.display = "none";
+      document.body.appendChild(anchor);
+      setTimeout(function () {
+        anchor.click();
+        document.body.removeChild(anchor);
+      }, 66);
+      return true;
+    }
+  },
+}

+ 21 - 0
src/views/backStageManage/application/apply/components/addApply.vue

@@ -0,0 +1,21 @@
+<template>
+  <div></div>
+</template>
+
+<script>
+export default {
+  components: {},
+  props: {},
+  data() {
+    return {
+    };
+  },
+  watch: {},
+  computed: {},
+  created() {},
+  mounted() {},
+  methods: {},
+};
+</script>
+<style lang="scss" scoped>
+</style>

+ 26 - 0
src/views/backStageManage/application/apply/index.vue

@@ -0,0 +1,26 @@
+<template>
+  <div></div>
+</template>
+
+<script>
+export default {
+  components: {},
+  props: {},
+  data() {
+    return {
+    };
+  },
+  watch: {},
+  computed: {},
+  created() {},
+  mounted() {
+    console.log(2)
+  },
+  activated(){
+    console.log(3)
+  },
+  methods: {},
+};
+</script>
+<style lang="scss" scoped>
+</style>

+ 33 - 0
src/views/backStageManage/application/vision/index.vue

@@ -0,0 +1,33 @@
+<template>
+  <div></div>
+</template>
+
+<script>
+export default {
+  components: {},
+  props: {},
+  data() {
+    return {
+    };
+  },
+  watch: {},
+  computed: {},
+  created() {},
+  mounted() {
+    console.log(1)
+    this.getVisionList()
+  },
+  methods: {
+    getVisionList(){
+        this.$api.getVision().then(res => {
+            console.log(res);
+            this.value = res.data
+        }).catch(err => {
+            console.log(err);
+        })
+    }
+  },
+};
+</script>
+<style lang="scss" scoped>
+</style>

+ 21 - 0
src/views/backStageManage/home/index.vue

@@ -0,0 +1,21 @@
+<template>
+  <div>12</div>
+</template>
+
+<script>
+export default {
+  components: {},
+  props: {},
+  data() {
+    return {
+    };
+  },
+  watch: {},
+  computed: {},
+  created() {},
+  mounted() {},
+  methods: {},
+};
+</script>
+<style lang="scss" scoped>
+</style>

+ 23 - 0
src/views/backStageManage/index/index.vue

@@ -0,0 +1,23 @@
+<template>
+  <div></div>
+</template>
+
+<script>
+export default {
+  components: {},
+  props: {},
+  data() {
+    return {
+    };
+  },
+  watch: {},
+  computed: {},
+  created() {},
+  mounted() {
+    this.$router.push({path: '/administrator/login'})
+  },
+  methods: {},
+};
+</script>
+<style lang="scss" scoped>
+</style>

+ 54 - 0
src/views/backStageManage/layout/Layout.vue

@@ -0,0 +1,54 @@
+<template>
+  <div class="app-wrapper" :class="classObj">
+    <sidebar class="sidebar-container"></sidebar>
+    <div class="main-container">
+      <navbar></navbar>
+      <history></history>
+      <app-main></app-main>
+    </div>
+  </div>
+</template>
+
+<script>
+import { Navbar, Sidebar, AppMain,history } from './components'
+import ResizeMixin from './mixin/ResizeHandler'
+
+export default {
+  name: 'layout',
+  components: {
+    Navbar,
+    Sidebar,
+    AppMain,
+    history
+  },
+  mixins: [ResizeMixin],
+  computed: {
+    sidebar() {
+      return this.$store.state.app.sidebar
+    },
+    device() {
+      return this.$store.state.app.device
+    },
+    classObj() {
+      return {
+        hideSidebar: !this.sidebar.opened,
+        withoutAnimation: this.sidebar.withoutAnimation,
+        mobile: this.device === 'mobile'
+      }
+    },
+
+  },
+  created(){
+ },
+}
+</script>
+
+<style rel="stylesheet/scss" lang="scss" scoped>
+  @import "../../../styles/mixin.scss";
+  .app-wrapper {
+    @include clearfix;
+    position: relative;
+    height: 100%;
+    width: 100%;
+  }
+</style>

+ 46 - 0
src/views/backStageManage/layout/components/AppMain.vue

@@ -0,0 +1,46 @@
+<template>
+  <section class="app-main">
+    <transition name="fade" mode="out-in">
+      <!-- <router-view :key="key"></router-view> -->
+      
+      <template v-if="$route.meta.keepAlive">
+        <keep-alive>
+          <router-view></router-view>
+        </keep-alive>
+      </template>
+      <router-view v-else></router-view>
+    </transition>
+  </section>
+</template>
+
+<script>
+import { webSocket } from "../mixin";
+export default {
+  name: 'AppMain',
+  mixins: [webSocket],
+  computed: {
+    // key() {
+    //   return this.$route.name !== undefined ? this.$route.name + +new Date() : this.$route + +new Date()
+    // }
+    userId(){
+      return this.$s.getCookie('userId')
+    },
+    currentPath(){
+      return this.$store.state.history.currentPath;
+    },
+  },
+  watch:{
+  },
+  data() {
+    return {
+    }
+  },
+  created(){
+    // if(this.userId){
+    //   this.connectWebSocket(this.userId)
+    // }
+  },
+  methods: {
+  },
+}
+</script>

+ 140 - 0
src/views/backStageManage/layout/components/Navbar.vue

@@ -0,0 +1,140 @@
+<template>
+  <el-menu class="navbar" mode="horizontal">
+      <hamburger class="hamburger-container" :toggleClick="toggleSideBar" :isActive="sidebar.opened"></hamburger>
+      <breadcrumb></breadcrumb>
+    <div class="screenfull" @click="toggleFullScreen" v-if="!isScreenFull">
+      全屏
+    </div>
+      <el-dropdown class="avatar-container" trigger="click">
+        <div class="avatar-wrapper">
+          <img class="user-avatar" :src="$commonJS.getFile(avatar)">
+          <i class="el-icon-caret-bottom"></i>
+        </div>
+        <el-dropdown-menu class="user-dropdown" slot="dropdown">
+          <router-link class="inlineBlock" to="/">
+            <el-dropdown-item>
+              首页
+            </el-dropdown-item>
+          </router-link>
+          <el-dropdown-item divided>
+            <span @click="logout" style="display:block;">退出</span>
+          </el-dropdown-item>
+        </el-dropdown-menu>
+      </el-dropdown>
+
+  </el-menu>
+</template>
+
+<script>
+import { mapGetters } from 'vuex'
+import Breadcrumb from '@/components/Breadcrumb'
+import Hamburger from '@/components/Hamburger'
+
+export default {
+  components: {
+    Breadcrumb,
+    Hamburger
+  },
+  computed: {
+    ...mapGetters([
+      'sidebar',
+      // 'avatar'
+    ]),
+    avatar(){
+      return this.$s.getCookie('SET_AVATAR')
+    },
+
+  },
+  data() {
+    return {
+      isScreenFull:null
+    }
+  },
+  mounted(){
+    window.onresize = () => {
+        this.isScreenFull = this.getFullscreen()
+      }
+  },
+  methods: {
+    getFullscreen(){
+      return (
+        // 标准模式
+        document.fullscreenElement ||
+        // 兼容老旧浏览器的API
+        document.mozFullScreenElement ||
+        document.webkitFullscreenElement ||
+        document.msFullscreenElement
+      );
+    },
+    toggleSideBar() {
+      this.$store.dispatch('ToggleSideBar')
+    },
+    logout() {
+      this.$store.dispatch('LogOut').then(() => {
+        location.reload() // 为了重新实例化vue-router对象 避免bug
+      })
+    },
+    toggleFullScreen() {
+      const elem = document.documentElement;
+      if (elem.requestFullscreen) {
+        elem.requestFullscreen();
+      } else if (elem.mozRequestFullScreen) {
+        elem.mozRequestFullScreen();
+      } else if (elem.webkitRequestFullscreen) {
+        elem.webkitRequestFullscreen();
+      } else if (elem.msRequestFullscreen) {
+        elem.msRequestFullscreen();
+      }
+    }
+  }
+}
+</script>
+
+<style rel="stylesheet/scss" lang="scss" scoped>
+.navbar {
+  position: sticky;
+  top: 0;
+  background-color: white;
+  z-index: 999;
+  width: auto;
+  height: 50px;
+  line-height: 50px;
+  border-radius: 0px !important;
+  .hamburger-container {
+    line-height: 58px;
+    height: 50px;
+    float: left;
+    padding: 0 10px;
+  }
+  .screenfull {
+    position: absolute;
+    right: 90px;
+    top: 0px;
+    color: blue;
+    cursor: pointer;
+  }
+  .avatar-container {
+    height: 50px;
+    display: inline-block;
+    position: absolute;
+    right: 35px;
+    .avatar-wrapper {
+      cursor: pointer;
+      margin-top: 5px;
+      position: relative;
+      .user-avatar {
+        width: 40px;
+        height: 40px;
+        border-radius: 10px;
+      }
+      .el-icon-caret-bottom {
+        position: absolute;
+        right: -20px;
+        top: 25px;
+        font-size: 12px;
+      }
+    }
+  }
+}
+</style>
+

+ 65 - 0
src/views/backStageManage/layout/components/Sidebar/SidebarItem.vue

@@ -0,0 +1,65 @@
+<template>
+  <div class="menu-wrapper">
+    <template v-for="item in routes" v-if="!item.hidden&&item.children">
+
+      <router-link v-if="hasOneShowingChildren(item.children) && !item.children[0].children&&!item.alwaysShow" :to="item.path+'/'+item.children[0].path"
+        :key="item.children[0].name">
+        <el-menu-item :index="item.path+'/'+item.children[0].path" :class="{'submenu-title-noDropdown':!isNest}">
+          <svg-icon v-if="item.children[0].meta&&item.children[0].meta.icon" :icon-class="item.children[0].meta.icon"></svg-icon>
+          <span v-if="item.children[0].meta&&item.children[0].meta.title" slot="title">{{item.children[0].meta.title}}</span>
+        </el-menu-item>
+      </router-link>
+
+      <el-submenu v-else :index="item.name||item.path" :key="item.name">
+        <template slot="title">
+          <svg-icon v-if="item.meta&&item.meta.icon" :icon-class="item.meta.icon"></svg-icon>
+          <span v-if="item.meta&&item.meta.title" slot="title">{{item.meta.title}}</span>
+        </template>
+
+        <template v-for="child in item.children" v-if="!child.hidden">
+          <sidebar-item :is-nest="true" class="nest-menu" v-if="child.children&&child.children.length>0" :routes="[child]" :key="child.path"></sidebar-item>
+          <!--支持外链功能-->
+          <a v-else-if="child.path.startsWith('http')" v-bind:href="child.path" target="_blank" :key="child.name">
+            <el-menu-item :index="item.path+'/'+child.path">
+              <svg-icon v-if="child.meta&&child.meta.icon" :icon-class="child.meta.icon"></svg-icon>
+              <span v-if="child.meta&&child.meta.title" slot="title">{{child.meta.title}}</span>
+            </el-menu-item>
+          </a>
+          <router-link v-else :to="item.path+'/'+child.path" :key="child.name">
+            <el-menu-item :index="item.path+'/'+child.path">
+              <svg-icon v-if="child.meta&&child.meta.icon" :icon-class="child.meta.icon"></svg-icon>
+              <span v-if="child.meta&&child.meta.title" slot="title">{{child.meta.title}}</span>
+            </el-menu-item>
+          </router-link>
+        </template>
+      </el-submenu>
+
+    </template>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'SidebarItem',
+  props: {
+    routes: {
+      type: Array
+    },
+    isNest: {
+      type: Boolean,
+      default: false
+    }
+  },
+  methods: {
+    hasOneShowingChildren(children) {
+      const showingChildren = children.filter(item => {
+        return !item.hidden
+      })
+      if (showingChildren.length === 1) {
+        return true
+      }
+      return false
+    }
+  }
+}
+</script>

+ 38 - 0
src/views/backStageManage/layout/components/Sidebar/index.vue

@@ -0,0 +1,38 @@
+<template>
+  <scroll-bar>
+    <el-menu
+      mode="vertical"
+      :show-timeout="200"
+      :default-active="$route.path"
+      :collapse="isCollapse"
+      background-color="#304156"
+      text-color="#bfcbd9"
+      active-text-color="#409EFF"
+    >
+      <sidebar-item :routes="routes"></sidebar-item>
+    </el-menu>
+  </scroll-bar>
+</template>
+
+<script>
+import { mapGetters } from 'vuex'
+import SidebarItem from './SidebarItem'
+import ScrollBar from '@/components/ScrollBar'
+
+export default {
+  components: { SidebarItem, ScrollBar },
+  computed: {
+    ...mapGetters([
+      'sidebar',
+      'routers'
+    ]),
+    routes() {
+      // return this.$router.options.routes
+      return this.routers
+    },
+    isCollapse() {
+      return !this.sidebar.opened
+    }
+  }
+}
+</script>

+ 96 - 0
src/views/backStageManage/layout/components/history.vue

@@ -0,0 +1,96 @@
+<template>
+  <el-tabs type="card" v-if="history&&history.length>0"
+           :value="currentPath"
+           @tab-click="tabClick"
+           @tab-remove="tabRemove"
+           closable class="content">
+    <el-tab-pane v-for="item in history"
+                 :key="item.path"
+                 :label="item.name"
+                 :name="item.path">
+    </el-tab-pane>
+  </el-tabs>
+</template>
+
+<script>
+
+export default {
+  name: "Tab",
+  computed:{
+    history(){
+      return this.$store.state.history.history;
+    },
+    currentPath(){
+      return this.$store.state.history.currentPath;
+    }
+  },
+  mounted(){
+  },
+  methods:{
+    tabClick({name}){
+      this.$store.commit('addHistoryPath',name);
+      if(name!==this.$route.path){
+        //跳转历史的路由
+        var route = this.history.find(item=>{
+          return item.path == name
+        })
+        if(route){
+          this.$router.push(route.route)
+          return
+        }
+        this.$router.push(name);
+      }
+    },
+    tabRemove(path){
+      this.$store.commit('removeHistoryPath',path);
+      this.$store.commit('removeHistory',path);
+      this.tabClick({name:this.currentPath})
+    }
+  }
+}
+</script>
+
+<style scoped>
+.content{
+  margin-top: 5px;
+  /* height: 35px; */
+  user-select: none;
+}
+:deep(.el-tabs__nav-wrap){
+  height: 35px !important;
+  line-height: 35px !important;
+}
+:deep(.el-tabs__nav-next) , :deep(.el-tabs__nav-prev){
+  line-height: 35px !important;
+}
+:deep(.el-tabs__header){
+  height: 35px !important;
+  margin: 0;
+  border: none;
+}
+:deep(.el-tabs__item){
+  height: 30px;
+  margin-top: 2px;
+  margin-bottom: 2px;
+  border: 1px solid grey !important;
+  line-height: 30px;
+  padding: 0 10px 0 10px !important;
+  border-radius: 2px;
+}
+
+:deep(.el-tabs__item:not(:last-child)){
+  margin-right: 10px;
+}
+
+:deep(.is-active){
+  border-bottom:inherit;
+  background-color: lightcyan;
+}
+:deep(.el-tabs__nav){
+  border: none !important;
+  /*height: 35px;*/
+  /*1px solid #E4E7ED*/
+}
+
+</style>
+

+ 4 - 0
src/views/backStageManage/layout/components/index.js

@@ -0,0 +1,4 @@
+export { default as Navbar } from './Navbar'
+export { default as Sidebar } from './Sidebar'
+export { default as AppMain } from './AppMain'
+export { default as history } from './history'

+ 41 - 0
src/views/backStageManage/layout/mixin/ResizeHandler.js

@@ -0,0 +1,41 @@
+import store from '@/store'
+
+const { body } = document
+const WIDTH = 1024
+const RATIO = 3
+
+export default {
+  watch: {
+    $route(route) {
+      if (this.device === 'mobile' && this.sidebar.opened) {
+        store.dispatch('CloseSideBar', { withoutAnimation: false })
+      }
+    }
+  },
+  beforeMount() {
+    window.addEventListener('resize', this.resizeHandler)
+  },
+  mounted() {
+    const isMobile = this.isMobile()
+    if (isMobile) {
+      store.dispatch('ToggleDevice', 'mobile')
+      store.dispatch('CloseSideBar', { withoutAnimation: true })
+    }
+  },
+  methods: {
+    isMobile() {
+      const rect = body.getBoundingClientRect()
+      return rect.width - RATIO < WIDTH
+    },
+    resizeHandler() {
+      if (!document.hidden) {
+        const isMobile = this.isMobile()
+        store.dispatch('ToggleDevice', isMobile ? 'mobile' : 'desktop')
+
+        if (isMobile) {
+          store.dispatch('CloseSideBar', { withoutAnimation: true })
+        }
+      }
+    }
+  }
+}

+ 37 - 0
src/views/backStageManage/layout/mixin/index.js

@@ -0,0 +1,37 @@
+import Config from '@/config'
+import Store from '@/store'
+
+export const webSocket = {
+  methods: {
+    connectWebSocket(userId) {
+      let webSocket = new WebSocket(`${Config.WebSocketPath}/xiaoshi-weixinback/ws/` + userId)
+      Store.commit('SET_WEB_SOCKET', webSocket)
+      webSocket.onopen = () => {
+        console.log('WebSocket连接成功')
+      }
+      webSocket.onmessage = async (e) => {
+        if (e.data.indexOf('{') == -1) {
+          return false
+        }
+        const { code, data, message } = JSON.parse(e.data)
+
+        if (code == 111) {
+          if (data.complete) {
+            this.$message.success(`导入完成`)
+          } else {
+          }
+        }
+
+      }
+      webSocket.onerror = () => {
+        console.log('WebSocket连接失败')
+      }
+      webSocket.onclose = () => {
+        console.log('WebSocket连接关闭')
+      }
+    }
+  }
+}
+
+
+

+ 21 - 0
src/views/backStageManage/login/index.vue

@@ -0,0 +1,21 @@
+<template>
+  <div>1</div>
+</template>
+
+<script>
+export default {
+  components: {},
+  props: {},
+  data() {
+    return {
+    };
+  },
+  watch: {},
+  computed: {},
+  created() {},
+  mounted() {},
+  methods: {},
+};
+</script>
+<style lang="scss" scoped>
+</style>

+ 1 - 1
src/views/register/index.vue

@@ -266,7 +266,7 @@ export default {
 }
 .content{
     // height: 400px;
-    width: 250px;
+    width: 250px !important;
     min-height: 300px;
     height:fit-content;
     border-radius: 10px;

+ 3 - 2
vue.config.js

@@ -102,6 +102,7 @@ module.exports = {
       )
   },
   devServer: {
+    before:require('./mock/index.js'),
     port: 8087,
     overlay: {
       warnings: false,
@@ -109,8 +110,8 @@ module.exports = {
     },
     proxy: {
       '/permission': {
-        // target: 'http://192.168.0.57:8879',
-        target: 'http://192.168.1.24:8871',
+        // target: 'http://192.168.2.122:8880',
+        target: 'http://47.116.194.135:8085',
         ws: true,
         changeOrigin: true,
         pathRewrite:{

+ 21 - 26
yarn.lock

@@ -14,7 +14,7 @@
   "resolved" "https://registry.npmmirror.com/@babel/compat-data/download/@babel/compat-data-7.16.0.tgz"
   "version" "7.16.0"
 
-"@babel/core@^7.11.0":
+"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.11.0", "@babel/core@^7.12.0", "@babel/core@^7.13.0", "@babel/core@^7.4.0-0":
   "integrity" "sha1-xP9EBG9f4xBSXMnrTvUUfwxTdNQ="
   "resolved" "https://registry.npmmirror.com/@babel/core/download/@babel/core-7.16.0.tgz?cache=0&sync_timestamp=1635560597244&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2F%40babel%2Fcore%2Fdownload%2F%40babel%2Fcore-7.16.0.tgz"
   "version" "7.16.0"
@@ -1338,7 +1338,7 @@
   "resolved" "https://registry.npmmirror.com/@vue/cli-plugin-vuex/download/@vue/cli-plugin-vuex-4.5.15.tgz?cache=0&sync_timestamp=1635486198476&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2F%40vue%2Fcli-plugin-vuex%2Fdownload%2F%40vue%2Fcli-plugin-vuex-4.5.15.tgz"
   "version" "4.5.15"
 
-"@vue/cli-service@~4.5.0":
+"@vue/cli-service@^3.0.0 || ^4.0.0-0", "@vue/cli-service@~4.5.0":
   "integrity" "sha1-DpoYbVFVACfQ5o6VBCB3600RW0U="
   "resolved" "https://registry.npmmirror.com/@vue/cli-service/download/@vue/cli-service-4.5.15.tgz?cache=0&sync_timestamp=1635486199258&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2F%40vue%2Fcli-service%2Fdownload%2F%40vue%2Fcli-service-4.5.15.tgz"
   "version" "4.5.15"
@@ -1618,16 +1618,16 @@
   "resolved" "https://registry.nlark.com/acorn-walk/download/acorn-walk-7.2.0.tgz?cache=0&sync_timestamp=1630916608758&other_urls=https%3A%2F%2Fregistry.nlark.com%2Facorn-walk%2Fdownload%2Facorn-walk-7.2.0.tgz"
   "version" "7.2.0"
 
+"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", "acorn@^7.1.1", "acorn@^7.4.0":
+  "integrity" "sha1-/q7SVZc9LndVW4PbwIhRpsY1IPo="
+  "resolved" "https://registry.nlark.com/acorn/download/acorn-7.4.1.tgz?cache=0&sync_timestamp=1630916517167&other_urls=https%3A%2F%2Fregistry.nlark.com%2Facorn%2Fdownload%2Facorn-7.4.1.tgz"
+  "version" "7.4.1"
+
 "acorn@^6.4.1":
   "integrity" "sha1-NYZv1xBSjpLeEM8GAWSY5H454eY="
   "resolved" "https://registry.nlark.com/acorn/download/acorn-6.4.2.tgz?cache=0&sync_timestamp=1630916517167&other_urls=https%3A%2F%2Fregistry.nlark.com%2Facorn%2Fdownload%2Facorn-6.4.2.tgz"
   "version" "6.4.2"
 
-"acorn@^7.1.1", "acorn@^7.4.0":
-  "integrity" "sha1-/q7SVZc9LndVW4PbwIhRpsY1IPo="
-  "resolved" "https://registry.nlark.com/acorn/download/acorn-7.4.1.tgz?cache=0&sync_timestamp=1630916517167&other_urls=https%3A%2F%2Fregistry.nlark.com%2Facorn%2Fdownload%2Facorn-7.4.1.tgz"
-  "version" "7.4.1"
-
 "address@^1.1.2":
   "integrity" "sha1-vxEWycdYxRt6kz0pa3LCIe2UKLY="
   "resolved" "https://registry.nlark.com/address/download/address-1.1.2.tgz"
@@ -1651,7 +1651,7 @@
   "resolved" "https://registry.nlark.com/ajv-keywords/download/ajv-keywords-3.5.2.tgz"
   "version" "3.5.2"
 
-"ajv@^6.1.0", "ajv@^6.10.0", "ajv@^6.10.2", "ajv@^6.12.3", "ajv@^6.12.4", "ajv@^6.12.5":
+"ajv@^6.1.0", "ajv@^6.10.0", "ajv@^6.10.2", "ajv@^6.12.3", "ajv@^6.12.4", "ajv@^6.12.5", "ajv@^6.9.1", "ajv@>=5.0.0":
   "integrity" "sha1-uvWmLoArB9l3A0WG+MO69a3ybfQ="
   "resolved" "https://registry.npmmirror.com/ajv/download/ajv-6.12.6.tgz"
   "version" "6.12.6"
@@ -3030,7 +3030,7 @@
   dependencies:
     "base64-arraybuffer" "^0.2.0"
 
-"css-loader@^3.5.3":
+"css-loader@*", "css-loader@^3.5.3":
   "integrity" "sha1-Lkssfm4tJ/jI8o9hv/zS5ske9kU="
   "resolved" "https://registry.npmmirror.com/css-loader/download/css-loader-3.6.0.tgz?cache=0&sync_timestamp=1635968760879&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fcss-loader%2Fdownload%2Fcss-loader-3.6.0.tgz"
   "version" "3.6.0"
@@ -3770,7 +3770,7 @@
   "resolved" "https://registry.nlark.com/eslint-visitor-keys/download/eslint-visitor-keys-1.3.0.tgz"
   "version" "1.3.0"
 
-"eslint@^6.7.2":
+"eslint@^5.0.0 || ^6.0.0", "eslint@^6.7.2", "eslint@>= 1.6.0 < 7.0.0", "eslint@>= 4.12.1", "eslint@>=1.6.0 <7.0.0", "eslint@>=5.0.0":
   "integrity" "sha1-YiYtZylzn5J1cjgkMC+yJ8jJP/s="
   "resolved" "https://registry.npmmirror.com/eslint/download/eslint-6.8.0.tgz?cache=0&sync_timestamp=1636156197235&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Feslint%2Fdownload%2Feslint-6.8.0.tgz"
   "version" "6.8.0"
@@ -4088,7 +4088,7 @@
   dependencies:
     "flat-cache" "^2.0.1"
 
-"file-loader@^4.2.0":
+"file-loader@*", "file-loader@^4.2.0":
   "integrity" "sha1-eA8ED3KbPRgBnyBgX3I+hEuKWK8="
   "resolved" "https://registry.nlark.com/file-loader/download/file-loader-4.3.0.tgz"
   "version" "4.3.0"
@@ -4096,11 +4096,6 @@
     "loader-utils" "^1.2.3"
     "schema-utils" "^2.5.0"
 
-"file-uri-to-path@1.0.0":
-  "integrity" "sha1-VTp7hEb/b2hDWcRF8eN6BdrMM90="
-  "resolved" "https://registry.nlark.com/file-uri-to-path/download/file-uri-to-path-1.0.0.tgz"
-  "version" "1.0.0"
-
 "filesize@^3.6.1":
   "integrity" "sha1-CQuz7gG2+AGoqL6Z0xcQs0Irsxc="
   "resolved" "https://registry.npmmirror.com/filesize/download/filesize-3.6.1.tgz?cache=0&sync_timestamp=1635763993879&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Ffilesize%2Fdownload%2Ffilesize-3.6.1.tgz"
@@ -4677,7 +4672,7 @@
     "slash" "^1.0.0"
     "source-map-url" "^0.4.0"
 
-"html-webpack-plugin@^3.2.0":
+"html-webpack-plugin@^3.0.0 || ^4.0.0", "html-webpack-plugin@^3.2.0", "html-webpack-plugin@>=2.26.0":
   "integrity" "sha1-sBq71yOsqqeze2r0SS69oD2d03s="
   "resolved" "https://registry.npmmirror.com/html-webpack-plugin/download/html-webpack-plugin-3.2.0.tgz"
   "version" "3.2.0"
@@ -5564,7 +5559,7 @@
     "loader-utils" "^1.1.0"
     "pify" "^4.0.1"
 
-"less@^4.1.2":
+"less@^2.3.1 || ^3.0.0", "less@^4.1.2":
   "integrity" "sha512-EoQp/Et7OSOVu0aJknJOtlXZsnr8XE8KwuzTHOLeVSEx8pVWUICc8Q0VYRHgzyjX78nMEyC/oztWFbgyhtNfDA=="
   "resolved" "https://registry.npmmirror.com/less/-/less-4.1.2.tgz"
   "version" "4.1.2"
@@ -7149,7 +7144,7 @@
     "source-map" "^0.5.6"
     "supports-color" "^3.2.3"
 
-"postcss@^7.0.0", "postcss@^7.0.1", "postcss@^7.0.14", "postcss@^7.0.27", "postcss@^7.0.32", "postcss@^7.0.36", "postcss@^7.0.5", "postcss@^7.0.6":
+"postcss@^7.0.0", "postcss@^7.0.1", "postcss@^7.0.14", "postcss@^7.0.27", "postcss@^7.0.32", "postcss@^7.0.36", "postcss@^7.0.5", "postcss@^7.0.6", "postcss@>4 <9":
   "integrity" "sha1-liQ3XZZWMOLh8sAqk1yCpZy0gwk="
   "resolved" "https://registry.npmmirror.com/postcss/download/postcss-7.0.39.tgz?cache=0&sync_timestamp=1634821330333&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fpostcss%2Fdownload%2Fpostcss-7.0.39.tgz"
   "version" "7.0.39"
@@ -7772,7 +7767,7 @@
     "schema-utils" "^2.7.0"
     "semver" "^7.3.2"
 
-"sass@^1.43.4":
+"sass@^1.3.0", "sass@^1.43.4":
   "integrity" "sha512-ONy5bjppoohtNkFJRqdz1gscXamMzN3wQy1YH9qO2FiNpgjLhpz/IPRGg0PpCjyz/pWfCOaNEaiEGCcjOFAjqw=="
   "resolved" "https://registry.npmmirror.com/sass/-/sass-1.45.0.tgz"
   "version" "1.45.0"
@@ -9085,8 +9080,8 @@
   "version" "2.3.4"
 
 "vue-loader-v16@npm:vue-loader@^16.1.0":
-  "integrity" "sha1-1D5nXe9bqTRdbH8FkUwT2GGZcIc="
-  "resolved" "https://registry.npmmirror.com/vue-loader/download/vue-loader-16.8.3.tgz"
+  "integrity" "sha512-7vKN45IxsKxe5GcVCbc2qFU5aWzyiLrYJyUuMz4BQLKctCj/fmCa0w6fGiiQ2cLFetNcek1ppGJQDCup0c1hpA=="
+  "resolved" "https://registry.npmmirror.com/vue-loader/-/vue-loader-16.8.3.tgz"
   "version" "16.8.3"
   dependencies:
     "chalk" "^4.1.0"
@@ -9142,7 +9137,7 @@
     "hash-sum" "^1.0.2"
     "loader-utils" "^1.0.2"
 
-"vue-template-compiler@^2.6.11":
+"vue-template-compiler@^2.0.0", "vue-template-compiler@^2.6.11":
   "integrity" "sha1-ovDn2YVnDULJye4NBE/tdpD092M="
   "resolved" "https://registry.npmmirror.com/vue-template-compiler/download/vue-template-compiler-2.6.14.tgz"
   "version" "2.6.14"
@@ -9155,7 +9150,7 @@
   "resolved" "https://registry.npmmirror.com/vue-template-es2015-compiler/download/vue-template-es2015-compiler-1.9.1.tgz"
   "version" "1.9.1"
 
-"vue@^2.2.6", "vue@^2.6.11":
+"vue@^2 || ^3.0.0-0", "vue@^2.0.0", "vue@^2.2.0", "vue@^2.2.6", "vue@^2.5.17", "vue@^2.6.11":
   "integrity" "sha1-5RqlJQJQ1Wmj+606ilpofWA24jU="
   "resolved" "https://registry.npmmirror.com/vue/download/vue-2.6.14.tgz"
   "version" "2.6.14"
@@ -9307,7 +9302,7 @@
     "source-list-map" "^2.0.0"
     "source-map" "~0.6.1"
 
-"webpack@^4.0.0":
+"webpack@^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.0", "webpack@^2.0.0 || ^3.0.0 || ^4.0.0", "webpack@^3.0.0 || ^4.0.0-alpha.0 || ^4.0.0", "webpack@^3.0.0 || ^4.1.0 || ^5.0.0-0", "webpack@^4.0.0", "webpack@^4.0.0 || ^5.0.0", "webpack@^4.36.0 || ^5.0.0", "webpack@^4.4.0", "webpack@>=2", "webpack@>=2.0.0 <5.0.0", "webpack@>=4.0.0":
   "integrity" "sha1-v5tEBOogoHNgXgoBHRiNd8tq1UI="
   "resolved" "https://registry.npmmirror.com/webpack/download/webpack-4.46.0.tgz?cache=0&sync_timestamp=1636129648614&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fwebpack%2Fdownload%2Fwebpack-4.46.0.tgz"
   "version" "4.46.0"
@@ -9392,7 +9387,7 @@
   dependencies:
     "errno" "~0.1.7"
 
-"worker-loader@^2.0.0":
+"worker-loader@^2.0.0", "worker-loader@^3.0.8":
   "integrity" "sha512-tnvNp4K3KQOpfRnD20m8xltE3eWh89Ye+5oj7wXEEHKac1P4oZ6p9oTj8/8ExqoSBnk9nu5Pr4nKfQ1hn2APJw=="
   "resolved" "https://registry.npmmirror.com/worker-loader/-/worker-loader-2.0.0.tgz"
   "version" "2.0.0"