xxm 6 лет назад
Родитель
Сommit
4899f6e2ad
100 измененных файлов с 1757 добавлено и 104 удалено
  1. BIN
      .DS_Store
  2. 41 0
      api/article.js
  3. 8 0
      api/qiniu.js
  4. 17 0
      api/remote-search.js
  5. 38 0
      api/role.js
  6. 24 0
      api/user.js
  7. 111 0
      components/BackToTop/index.vue
  8. 105 0
      components/Breadcrumb/index.vue
  9. 78 0
      components/ErrorLog/index.vue
  10. 44 0
      components/Hamburger/index.vue
  11. 180 0
      components/HeaderSearch/index.vue
  12. 145 0
      components/RightPanel/index.vue
  13. 60 0
      components/Screenfull/index.vue
  14. 57 0
      components/SizeSelect/index.vue
  15. 175 0
      components/ThemePicker/index.vue
  16. 111 0
      components/Tinymce/components/EditorImage.vue
  17. 59 0
      components/Tinymce/dynamicLoadScript.js
  18. 237 0
      components/Tinymce/index.vue
  19. 7 0
      components/Tinymce/plugins.js
  20. 6 0
      components/Tinymce/toolbar.js
  21. 28 14
      components/menu/data.js
  22. 1 0
      dist/_nuxt/0acbd09528b0afc3c26e.js
  23. 1 0
      dist/_nuxt/0d7809cee78645c38829.js
  24. 1 0
      dist/_nuxt/11b53fbcc5a01ef62a96.js
  25. 0 1
      dist/_nuxt/17e1d4c8f313b7768167.js
  26. 1 0
      dist/_nuxt/1aa77fc3756bd63223ff.js
  27. 0 1
      dist/_nuxt/1c8058778177c352d171.js
  28. 2 0
      dist/_nuxt/27bd24a074497b64ace7.js
  29. 1 0
      dist/_nuxt/2846b2cded0c910c028c.js
  30. 0 1
      dist/_nuxt/300f56801156fa7c5df8.js
  31. 0 1
      dist/_nuxt/3b9c88b26bbc93e84862.js
  32. 1 0
      dist/_nuxt/42411812009633ea3fe5.js
  33. 0 1
      dist/_nuxt/475efeb9f0014beda531.js
  34. 1 0
      dist/_nuxt/4e0144fb308038d183f4.js
  35. 1 0
      dist/_nuxt/55897e4ed2c98d947b06.js
  36. 0 1
      dist/_nuxt/6fd060a533856f79eed7.js
  37. 0 1
      dist/_nuxt/71604b98d0b00f59a156.js
  38. 0 1
      dist/_nuxt/765eb226a83f346e9801.js
  39. 1 0
      dist/_nuxt/83fb4aa32e89f8fa1fdc.js
  40. 0 2
      dist/_nuxt/859db025c9bb12b28eb3.js
  41. 0 1
      dist/_nuxt/8859b1fa8aa6716d09fa.js
  42. 0 1
      dist/_nuxt/923de8f2056bfdc309e9.js
  43. 1 0
      dist/_nuxt/98a17df2b53bb0b4e49f.js
  44. 29 0
      dist/_nuxt/LICENSES
  45. 0 1
      dist/_nuxt/a372492e12d2a859de9b.js
  46. 1 0
      dist/_nuxt/a5ccaac32c4a03021dfb.js
  47. 0 1
      dist/_nuxt/a703d1baf58945626373.js
  48. 2 0
      dist/_nuxt/abd526b9a3a6c78082c1.js
  49. 0 2
      dist/_nuxt/ae110e300ff91279fa88.js
  50. 1 0
      dist/_nuxt/ae2c164acac3fcf64c52.js
  51. 1 0
      dist/_nuxt/b7e9c93dadd0d9ee2e94.js
  52. 1 0
      dist/_nuxt/bbb32bc241e2da98e1ee.js
  53. 0 1
      dist/_nuxt/bc2546130f7259bd44c7.js
  54. 1 0
      dist/_nuxt/c05718ef4109c8c69e2f.js
  55. 1 0
      dist/_nuxt/c1e7a132a58d8ed1eb49.js
  56. 1 0
      dist/_nuxt/cc37d19bece584e4ec2c.js
  57. 0 1
      dist/_nuxt/d1ec47237892490a446f.js
  58. 1 0
      dist/_nuxt/d2ee2c15c5a16e87441d.js
  59. 1 0
      dist/_nuxt/e1599602ab7ce5a5b384.js
  60. 0 1
      dist/_nuxt/e41a7a762d2844a42f06.js
  61. 0 1
      dist/_nuxt/e65ca75c9796cfe4c028.js
  62. 0 1
      dist/_nuxt/ede21b65272903ef08e6.js
  63. 0 1
      dist/_nuxt/effaecad9ba37d6d2ff4.js
  64. 1 0
      dist/_nuxt/f0849ddaf800b5c31470.js
  65. 0 1
      dist/_nuxt/f248e7183ff8c190f84e.js
  66. 0 1
      dist/_nuxt/f36ddbdaab5dd96aa611.js
  67. 1 0
      dist/_nuxt/f65de1e8f302a7ca9415.js
  68. 1 0
      dist/_nuxt/f8844696c18fb2f87e56.js
  69. 1 0
      dist/_nuxt/fd0098c7426bb5b4b9da.js
  70. 0 1
      dist/_nuxt/ff0c439f8f6e75973709.js
  71. 1 0
      dist/_nuxt/ff4f31ad07d9fee96435.js
  72. 0 1
      dist/_nuxt/ff8798d3214ec2d86594.js
  73. BIN
      dist/_nuxt/fonts/2fad952.woff
  74. BIN
      dist/_nuxt/fonts/535877f.woff
  75. BIN
      dist/_nuxt/fonts/6f0a763.ttf
  76. BIN
      dist/_nuxt/fonts/732389d.ttf
  77. 8 0
      dist/index.html
  78. 8 0
      dist/login/index.html
  79. 8 0
      dist/main/404/index.html
  80. 9 0
      dist/main/cats/index.html
  81. 8 0
      dist/main/cert_check/index.html
  82. 8 0
      dist/main/cert_edit/index.html
  83. 8 0
      dist/main/cert_pro/index.html
  84. 8 0
      dist/main/cloud_balance/index.html
  85. 8 0
      dist/main/cloud_developer/index.html
  86. 8 0
      dist/main/cloud_job/index.html
  87. 8 0
      dist/main/dev_check/index.html
  88. 8 0
      dist/main/dev_check_detail/index.html
  89. 8 0
      dist/main/dev_show/index.html
  90. 8 0
      dist/main/gongmall/index.html
  91. 8 0
      dist/main/group_list/index.html
  92. 8 0
      dist/main/index.html
  93. 8 0
      dist/main/vip_manager/index.html
  94. 8 0
      dist/main/vip_order/index.html
  95. 8 0
      dist/main/vip_setting/index.html
  96. 8 0
      dist/main/wage_details/index.html
  97. 8 0
      dist/main/wage_settlement/index.html
  98. 0 49
      hooks/applypatch-msg
  99. 0 15
      hooks/applypatch-msg.sample
  100. 0 0
      hooks/commit-msg

+ 41 - 0
api/article.js

@@ -0,0 +1,41 @@
+import request from '@/utils/request'
+
+export function fetchList(query) {
+  return request({
+    url: '/article/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function fetchArticle(id) {
+  return request({
+    url: '/article/detail',
+    method: 'get',
+    params: { id }
+  })
+}
+
+export function fetchPv(pv) {
+  return request({
+    url: '/article/pv',
+    method: 'get',
+    params: { pv }
+  })
+}
+
+export function createArticle(data) {
+  return request({
+    url: '/article/create',
+    method: 'post',
+    data
+  })
+}
+
+export function updateArticle(data) {
+  return request({
+    url: '/article/update',
+    method: 'post',
+    data
+  })
+}

+ 8 - 0
api/qiniu.js

@@ -0,0 +1,8 @@
+import request from '@/utils/request'
+
+export function getToken() {
+  return request({
+    url: '/qiniu/upload/token', // 假地址 自行替换
+    method: 'get'
+  })
+}

+ 17 - 0
api/remote-search.js

@@ -0,0 +1,17 @@
+import request from '@/utils/request'
+
+export function searchUser(name) {
+  return request({
+    url: '/search/user',
+    method: 'get',
+    params: { name }
+  })
+}
+
+export function transactionList(query) {
+  return request({
+    url: '/transaction/list',
+    method: 'get',
+    params: query
+  })
+}

+ 38 - 0
api/role.js

@@ -0,0 +1,38 @@
+import request from '@/utils/request'
+
+export function getRoutes() {
+  return request({
+    url: '/routes',
+    method: 'get'
+  })
+}
+
+export function getRoles() {
+  return request({
+    url: '/roles',
+    method: 'get'
+  })
+}
+
+export function addRole(data) {
+  return request({
+    url: '/role',
+    method: 'post',
+    data
+  })
+}
+
+export function updateRole(id, data) {
+  return request({
+    url: `/role/${id}`,
+    method: 'put',
+    data
+  })
+}
+
+export function deleteRole(id) {
+  return request({
+    url: `/role/${id}`,
+    method: 'delete'
+  })
+}

+ 24 - 0
api/user.js

@@ -0,0 +1,24 @@
+import request from '@/utils/request'
+
+export function login(data) {
+  return request({
+    url: '/user/login',
+    method: 'post',
+    data
+  })
+}
+
+export function getInfo(token) {
+  return request({
+    url: '/user/info',
+    method: 'get',
+    params: { token }
+  })
+}
+
+export function logout() {
+  return request({
+    url: '/user/logout',
+    method: 'post'
+  })
+}

+ 111 - 0
components/BackToTop/index.vue

@@ -0,0 +1,111 @@
+<template>
+  <transition :name="transitionName">
+    <div v-show="visible" :style="customStyle" class="back-to-ceiling" @click="backToTop">
+      <svg width="16" height="16" viewBox="0 0 17 17" xmlns="http://www.w3.org/2000/svg" class="Icon Icon--backToTopArrow" aria-hidden="true" style="height:16px;width:16px"><path d="M12.036 15.59a1 1 0 0 1-.997.995H5.032a.996.996 0 0 1-.997-.996V8.584H1.03c-1.1 0-1.36-.633-.578-1.416L7.33.29a1.003 1.003 0 0 1 1.412 0l6.878 6.88c.782.78.523 1.415-.58 1.415h-3.004v7.004z" /></svg>
+    </div>
+  </transition>
+</template>
+
+<script>
+export default {
+  name: 'BackToTop',
+  props: {
+    visibilityHeight: {
+      type: Number,
+      default: 400
+    },
+    backPosition: {
+      type: Number,
+      default: 0
+    },
+    customStyle: {
+      type: Object,
+      default: function() {
+        return {
+          right: '50px',
+          bottom: '50px',
+          width: '40px',
+          height: '40px',
+          'border-radius': '4px',
+          'line-height': '45px',
+          background: '#e7eaf1'
+        }
+      }
+    },
+    transitionName: {
+      type: String,
+      default: 'fade'
+    }
+  },
+  data() {
+    return {
+      visible: false,
+      interval: null,
+      isMoving: false
+    }
+  },
+  mounted() {
+    window.addEventListener('scroll', this.handleScroll)
+  },
+  beforeDestroy() {
+    window.removeEventListener('scroll', this.handleScroll)
+    if (this.interval) {
+      clearInterval(this.interval)
+    }
+  },
+  methods: {
+    handleScroll() {
+      this.visible = window.pageYOffset > this.visibilityHeight
+    },
+    backToTop() {
+      if (this.isMoving) return
+      const start = window.pageYOffset
+      let i = 0
+      this.isMoving = true
+      this.interval = setInterval(() => {
+        const next = Math.floor(this.easeInOutQuad(10 * i, start, -start, 500))
+        if (next <= this.backPosition) {
+          window.scrollTo(0, this.backPosition)
+          clearInterval(this.interval)
+          this.isMoving = false
+        } else {
+          window.scrollTo(0, next)
+        }
+        i++
+      }, 16.7)
+    },
+    easeInOutQuad(t, b, c, d) {
+      if ((t /= d / 2) < 1) return c / 2 * t * t + b
+      return -c / 2 * (--t * (t - 2) - 1) + b
+    }
+  }
+}
+</script>
+
+<style scoped>
+.back-to-ceiling {
+  position: fixed;
+  display: inline-block;
+  text-align: center;
+  cursor: pointer;
+}
+
+.back-to-ceiling:hover {
+  background: #d5dbe7;
+}
+
+.fade-enter-active,
+.fade-leave-active {
+  transition: opacity .5s;
+}
+
+.fade-enter,
+.fade-leave-to {
+  opacity: 0
+}
+
+.back-to-ceiling .Icon {
+  fill: #9aaabf;
+  background: none;
+}
+</style>

+ 105 - 0
components/Breadcrumb/index.vue

@@ -0,0 +1,105 @@
+<template>
+  <el-breadcrumb class="app-breadcrumb" separator="/">
+    <transition-group name="breadcrumb">
+      <el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path">
+        <span
+          v-if="item.redirect==='noRedirect'||index==levelList.length-1"
+          class="no-redirect"
+        >{{ item.title }}</span>
+        <a v-else @click.prevent="handleLink(item)">{{ item.title }}</a>
+      </el-breadcrumb-item>
+    </transition-group>
+  </el-breadcrumb>
+</template>
+
+<script>
+import pathToRegexp from "path-to-regexp";
+import menus from "@/components/menu/data";
+
+const menusMap = {};
+for (let i = 0, len = menus.length; i < len; i++) {
+  const menu1 = menus[i];
+  const subs = menu1.subs;
+  for (let j = 0, len1 = subs.length; j < len1; j++) {
+    menusMap[subs[j].path] = [
+      {
+        ...menu1
+      },
+      {
+        ...subs[j]
+      }
+    ];
+  }
+}
+export default {
+  data() {
+    return {
+      levelList: null
+    };
+  },
+  watch: {
+    $route(route) {
+      // if you go to the redirect page, do not update the breadcrumbs
+      if (route.path.startsWith("/redirect/")) {
+        return;
+      }
+      this.getBreadcrumb();
+    }
+  },
+  created() {
+    this.getBreadcrumb();
+  },
+  methods: {
+    getBreadcrumb() {
+      // only show routes with meta.title
+      let matched = this.$route.matched.filter(item => item.name);
+      const first = matched[0];
+
+      if (!this.isDashboard(first)) {
+        matched = [{ path: "/", title: "Home", redirect: "noRedirect" }].concat(
+          matched
+        );
+      }
+      this.levelList = [{ path: "/", title: "首页" }].concat(
+        menusMap[first.path]
+      );
+      console.log(this.levelList);
+    },
+    isDashboard(route) {
+      const name = route && route.name;
+      if (!name) {
+        return false;
+      }
+      return name.trim().toLocaleLowerCase() === "".toLocaleLowerCase();
+    },
+    pathCompile(path) {
+      // To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561
+      const { params } = this.$route;
+      var toPath = pathToRegexp.compile(path);
+      return toPath(params);
+    },
+    handleLink(item) {
+      const { redirect, path } = item;
+      if (redirect) {
+        this.$router.push(redirect);
+        return;
+      }
+      this.$router.push(this.pathCompile(path));
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.app-breadcrumb.el-breadcrumb {
+  display: inline-block;
+  font-size: 14px;
+  line-height: 50px;
+  margin-left: 8px;
+
+  .no-redirect {
+    color: #97a8be;
+    cursor: text;
+  }
+}
+</style>

+ 78 - 0
components/ErrorLog/index.vue

@@ -0,0 +1,78 @@
+<template>
+  <div v-if="errorLogs.length>0">
+    <el-badge :is-dot="true" style="line-height: 25px;margin-top: -5px;" @click.native="dialogTableVisible=true">
+      <el-button style="padding: 8px 10px;" size="small" type="danger">
+        <svg-icon icon-class="bug" />
+      </el-button>
+    </el-badge>
+
+    <el-dialog :visible.sync="dialogTableVisible" width="80%" append-to-body>
+      <div slot="title">
+        <span style="padding-right: 10px;">Error Log</span>
+        <el-button size="mini" type="primary" icon="el-icon-delete" @click="clearAll">Clear All</el-button>
+      </div>
+      <el-table :data="errorLogs" border>
+        <el-table-column label="Message">
+          <template slot-scope="{row}">
+            <div>
+              <span class="message-title">Msg:</span>
+              <el-tag type="danger">
+                {{ row.err.message }}
+              </el-tag>
+            </div>
+            <br>
+            <div>
+              <span class="message-title" style="padding-right: 10px;">Info: </span>
+              <el-tag type="warning">
+                {{ row.vm.$vnode.tag }} error in {{ row.info }}
+              </el-tag>
+            </div>
+            <br>
+            <div>
+              <span class="message-title" style="padding-right: 16px;">Url: </span>
+              <el-tag type="success">
+                {{ row.url }}
+              </el-tag>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="Stack">
+          <template slot-scope="scope">
+            {{ scope.row.err.stack }}
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'ErrorLog',
+  data() {
+    return {
+      dialogTableVisible: false
+    }
+  },
+  computed: {
+    errorLogs() {
+      return this.$store.getters.errorLogs
+    }
+  },
+  methods: {
+    clearAll() {
+      this.dialogTableVisible = false
+      this.$store.dispatch('errorLog/clearErrorLog')
+    }
+  }
+}
+</script>
+
+<style scoped>
+.message-title {
+  font-size: 16px;
+  color: #333;
+  font-weight: bold;
+  padding-right: 8px;
+}
+</style>

+ 44 - 0
components/Hamburger/index.vue

@@ -0,0 +1,44 @@
+<template>
+  <div style="padding: 0 15px;" @click="toggleClick">
+    <svg
+      :class="{'is-active':isActive}"
+      class="hamburger"
+      viewBox="0 0 1024 1024"
+      xmlns="http://www.w3.org/2000/svg"
+      width="64"
+      height="64"
+    >
+      <path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z" />
+    </svg>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'Hamburger',
+  props: {
+    isActive: {
+      type: Boolean,
+      default: false
+    }
+  },
+  methods: {
+    toggleClick() {
+      this.$emit('toggleClick')
+    }
+  }
+}
+</script>
+
+<style scoped>
+.hamburger {
+  display: inline-block;
+  vertical-align: middle;
+  width: 20px;
+  height: 20px;
+}
+
+.hamburger.is-active {
+  transform: rotate(180deg);
+}
+</style>

+ 180 - 0
components/HeaderSearch/index.vue

@@ -0,0 +1,180 @@
+<template>
+  <div :class="{'show':show}" class="header-search">
+    <svg-icon class-name="search-icon" icon-class="search" @click.stop="click" />
+    <el-select
+      ref="headerSearchSelect"
+      v-model="search"
+      :remote-method="querySearch"
+      filterable
+      default-first-option
+      remote
+      placeholder="Search"
+      class="header-search-select"
+      @change="change"
+    >
+      <el-option v-for="item in options" :key="item.path" :value="item" :label="item.title.join(' > ')" />
+    </el-select>
+  </div>
+</template>
+
+<script>
+// fuse is a lightweight fuzzy-search module
+// make search results more in line with expectations
+import Fuse from 'fuse.js'
+import path from 'path'
+
+export default {
+  name: 'HeaderSearch',
+  data() {
+    return {
+      search: '',
+      options: [],
+      searchPool: [],
+      show: false,
+      fuse: undefined
+    }
+  },
+  computed: {
+    routes() {
+      return this.$store.getters.permission_routes
+    }
+  },
+  watch: {
+    routes() {
+      this.searchPool = this.generateRoutes(this.routes)
+    },
+    searchPool(list) {
+      this.initFuse(list)
+    },
+    show(value) {
+      if (value) {
+        document.body.addEventListener('click', this.close)
+      } else {
+        document.body.removeEventListener('click', this.close)
+      }
+    }
+  },
+  mounted() {
+    this.searchPool = this.generateRoutes(this.routes)
+  },
+  methods: {
+    click() {
+      this.show = !this.show
+      if (this.show) {
+        this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.focus()
+      }
+    },
+    close() {
+      this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.blur()
+      this.options = []
+      this.show = false
+    },
+    change(val) {
+      this.$router.push(val.path)
+      this.search = ''
+      this.options = []
+      this.$nextTick(() => {
+        this.show = false
+      })
+    },
+    initFuse(list) {
+      this.fuse = new Fuse(list, {
+        shouldSort: true,
+        threshold: 0.4,
+        location: 0,
+        distance: 100,
+        maxPatternLength: 32,
+        minMatchCharLength: 1,
+        keys: [{
+          name: 'title',
+          weight: 0.7
+        }, {
+          name: 'path',
+          weight: 0.3
+        }]
+      })
+    },
+    // Filter out the routes that can be displayed in the sidebar
+    // And generate the internationalized title
+    generateRoutes(routes, basePath = '/', prefixTitle = []) {
+      let res = []
+
+      for (const router of routes) {
+        // skip hidden router
+        if (router.hidden) { continue }
+
+        const data = {
+          path: path.resolve(basePath, router.path),
+          title: [...prefixTitle]
+        }
+
+        if (router.meta && router.meta.title) {
+          data.title = [...data.title, router.meta.title]
+
+          if (router.redirect !== 'noRedirect') {
+            // only push the routes with title
+            // special case: need to exclude parent router without redirect
+            res.push(data)
+          }
+        }
+
+        // recursive child routes
+        if (router.children) {
+          const tempRoutes = this.generateRoutes(router.children, data.path, data.title)
+          if (tempRoutes.length >= 1) {
+            res = [...res, ...tempRoutes]
+          }
+        }
+      }
+      return res
+    },
+    querySearch(query) {
+      if (query !== '') {
+        this.options = this.fuse.search(query)
+      } else {
+        this.options = []
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.header-search {
+  font-size: 0 !important;
+
+  .search-icon {
+    cursor: pointer;
+    font-size: 18px;
+    vertical-align: middle;
+  }
+
+  .header-search-select {
+    font-size: 18px;
+    transition: width 0.2s;
+    width: 0;
+    overflow: hidden;
+    background: transparent;
+    border-radius: 0;
+    display: inline-block;
+    vertical-align: middle;
+
+    /deep/ .el-input__inner {
+      border-radius: 0;
+      border: 0;
+      padding-left: 0;
+      padding-right: 0;
+      box-shadow: none !important;
+      border-bottom: 1px solid #d9d9d9;
+      vertical-align: middle;
+    }
+  }
+
+  &.show {
+    .header-search-select {
+      width: 210px;
+      margin-left: 10px;
+    }
+  }
+}
+</style>

+ 145 - 0
components/RightPanel/index.vue

@@ -0,0 +1,145 @@
+<template>
+  <div ref="rightPanel" :class="{show:show}" class="rightPanel-container">
+    <div class="rightPanel-background" />
+    <div class="rightPanel">
+      <div class="handle-button" :style="{'top':buttonTop+'px','background-color':theme}" @click="show=!show">
+        <i :class="show?'el-icon-close':'el-icon-setting'" />
+      </div>
+      <div class="rightPanel-items">
+        <slot />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { addClass, removeClass } from '@/utils'
+
+export default {
+  name: 'RightPanel',
+  props: {
+    clickNotClose: {
+      default: false,
+      type: Boolean
+    },
+    buttonTop: {
+      default: 250,
+      type: Number
+    }
+  },
+  data() {
+    return {
+      show: false
+    }
+  },
+  computed: {
+    theme() {
+      return this.$store.state.settings.theme
+    }
+  },
+  watch: {
+    show(value) {
+      if (value && !this.clickNotClose) {
+        this.addEventClick()
+      }
+      if (value) {
+        addClass(document.body, 'showRightPanel')
+      } else {
+        removeClass(document.body, 'showRightPanel')
+      }
+    }
+  },
+  mounted() {
+    this.insertToBody()
+  },
+  beforeDestroy() {
+    const elx = this.$refs.rightPanel
+    elx.remove()
+  },
+  methods: {
+    addEventClick() {
+      window.addEventListener('click', this.closeSidebar)
+    },
+    closeSidebar(evt) {
+      const parent = evt.target.closest('.rightPanel')
+      if (!parent) {
+        this.show = false
+        window.removeEventListener('click', this.closeSidebar)
+      }
+    },
+    insertToBody() {
+      const elx = this.$refs.rightPanel
+      const body = document.querySelector('body')
+      body.insertBefore(elx, body.firstChild)
+    }
+  }
+}
+</script>
+
+<style>
+.showRightPanel {
+  overflow: hidden;
+  position: relative;
+  width: calc(100% - 15px);
+}
+</style>
+
+<style lang="scss" scoped>
+.rightPanel-background {
+  position: fixed;
+  top: 0;
+  left: 0;
+  opacity: 0;
+  transition: opacity .3s cubic-bezier(.7, .3, .1, 1);
+  background: rgba(0, 0, 0, .2);
+  z-index: -1;
+}
+
+.rightPanel {
+  width: 100%;
+  max-width: 260px;
+  height: 100vh;
+  position: fixed;
+  top: 0;
+  right: 0;
+  box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, .05);
+  transition: all .25s cubic-bezier(.7, .3, .1, 1);
+  transform: translate(100%);
+  background: #fff;
+  z-index: 40000;
+}
+
+.show {
+  transition: all .3s cubic-bezier(.7, .3, .1, 1);
+
+  .rightPanel-background {
+    z-index: 20000;
+    opacity: 1;
+    width: 100%;
+    height: 100%;
+  }
+
+  .rightPanel {
+    transform: translate(0);
+  }
+}
+
+.handle-button {
+  width: 48px;
+  height: 48px;
+  position: absolute;
+  left: -48px;
+  text-align: center;
+  font-size: 24px;
+  border-radius: 6px 0 0 6px !important;
+  z-index: 0;
+  pointer-events: auto;
+  cursor: pointer;
+  color: #fff;
+  line-height: 48px;
+  i {
+    font-size: 24px;
+    line-height: 48px;
+  }
+}
+</style>

+ 60 - 0
components/Screenfull/index.vue

@@ -0,0 +1,60 @@
+<template>
+  <div>
+    <svg-icon :icon-class="isFullscreen?'exit-fullscreen':'fullscreen'" @click="click" />
+  </div>
+</template>
+
+<script>
+import screenfull from 'screenfull'
+
+export default {
+  name: 'Screenfull',
+  data() {
+    return {
+      isFullscreen: false
+    }
+  },
+  mounted() {
+    this.init()
+  },
+  beforeDestroy() {
+    this.destroy()
+  },
+  methods: {
+    click() {
+      if (!screenfull.enabled) {
+        this.$message({
+          message: 'you browser can not work',
+          type: 'warning'
+        })
+        return false
+      }
+      screenfull.toggle()
+    },
+    change() {
+      this.isFullscreen = screenfull.isFullscreen
+    },
+    init() {
+      if (screenfull.enabled) {
+        screenfull.on('change', this.change)
+      }
+    },
+    destroy() {
+      if (screenfull.enabled) {
+        screenfull.off('change', this.change)
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+.screenfull-svg {
+  display: inline-block;
+  cursor: pointer;
+  fill: #5a5e66;;
+  width: 20px;
+  height: 20px;
+  vertical-align: 10px;
+}
+</style>

+ 57 - 0
components/SizeSelect/index.vue

@@ -0,0 +1,57 @@
+<template>
+  <el-dropdown trigger="click" @command="handleSetSize">
+    <div>
+      <svg-icon class-name="size-icon" icon-class="size" />
+    </div>
+    <el-dropdown-menu slot="dropdown">
+      <el-dropdown-item v-for="item of sizeOptions" :key="item.value" :disabled="size===item.value" :command="item.value">
+        {{
+          item.label }}
+      </el-dropdown-item>
+    </el-dropdown-menu>
+  </el-dropdown>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      sizeOptions: [
+        { label: 'Default', value: 'default' },
+        { label: 'Medium', value: 'medium' },
+        { label: 'Small', value: 'small' },
+        { label: 'Mini', value: 'mini' }
+      ]
+    }
+  },
+  computed: {
+    size() {
+      return this.$store.getters.size
+    }
+  },
+  methods: {
+    handleSetSize(size) {
+      this.$ELEMENT.size = size
+      this.$store.dispatch('app/setSize', size)
+      this.refreshView()
+      this.$message({
+        message: 'Switch Size Success',
+        type: 'success'
+      })
+    },
+    refreshView() {
+      // In order to make the cached page re-rendered
+      this.$store.dispatch('tagsView/delAllCachedViews', this.$route)
+
+      const { fullPath } = this.$route
+
+      this.$nextTick(() => {
+        this.$router.replace({
+          path: '/redirect' + fullPath
+        })
+      })
+    }
+  }
+
+}
+</script>

+ 175 - 0
components/ThemePicker/index.vue

@@ -0,0 +1,175 @@
+<template>
+  <el-color-picker
+    v-model="theme"
+    :predefine="['#409EFF', '#1890ff', '#304156','#212121','#11a983', '#13c2c2', '#6959CD', '#f5222d', ]"
+    class="theme-picker"
+    popper-class="theme-picker-dropdown"
+  />
+</template>
+
+<script>
+const version = require('element-ui/package.json').version // element-ui version from node_modules
+const ORIGINAL_THEME = '#409EFF' // default color
+
+export default {
+  data() {
+    return {
+      chalk: '', // content of theme-chalk css
+      theme: ''
+    }
+  },
+  computed: {
+    defaultTheme() {
+      return this.$store.state.settings.theme
+    }
+  },
+  watch: {
+    defaultTheme: {
+      handler: function(val, oldVal) {
+        this.theme = val
+      },
+      immediate: true
+    },
+    async theme(val) {
+      const oldVal = this.chalk ? this.theme : ORIGINAL_THEME
+      if (typeof val !== 'string') return
+      const themeCluster = this.getThemeCluster(val.replace('#', ''))
+      const originalCluster = this.getThemeCluster(oldVal.replace('#', ''))
+      console.log(themeCluster, originalCluster)
+
+      const $message = this.$message({
+        message: '  Compiling the theme',
+        customClass: 'theme-message',
+        type: 'success',
+        duration: 0,
+        iconClass: 'el-icon-loading'
+      })
+
+      const getHandler = (variable, id) => {
+        return () => {
+          const originalCluster = this.getThemeCluster(ORIGINAL_THEME.replace('#', ''))
+          const newStyle = this.updateStyle(this[variable], originalCluster, themeCluster)
+
+          let styleTag = document.getElementById(id)
+          if (!styleTag) {
+            styleTag = document.createElement('style')
+            styleTag.setAttribute('id', id)
+            document.head.appendChild(styleTag)
+          }
+          styleTag.innerText = newStyle
+        }
+      }
+
+      if (!this.chalk) {
+        const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`
+        await this.getCSSString(url, 'chalk')
+      }
+
+      const chalkHandler = getHandler('chalk', 'chalk-style')
+
+      chalkHandler()
+
+      const styles = [].slice.call(document.querySelectorAll('style'))
+        .filter(style => {
+          const text = style.innerText
+          return new RegExp(oldVal, 'i').test(text) && !/Chalk Variables/.test(text)
+        })
+      styles.forEach(style => {
+        const { innerText } = style
+        if (typeof innerText !== 'string') return
+        style.innerText = this.updateStyle(innerText, originalCluster, themeCluster)
+      })
+
+      this.$emit('change', val)
+
+      $message.close()
+    }
+  },
+
+  methods: {
+    updateStyle(style, oldCluster, newCluster) {
+      let newStyle = style
+      oldCluster.forEach((color, index) => {
+        newStyle = newStyle.replace(new RegExp(color, 'ig'), newCluster[index])
+      })
+      return newStyle
+    },
+
+    getCSSString(url, variable) {
+      return new Promise(resolve => {
+        const xhr = new XMLHttpRequest()
+        xhr.onreadystatechange = () => {
+          if (xhr.readyState === 4 && xhr.status === 200) {
+            this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, '')
+            resolve()
+          }
+        }
+        xhr.open('GET', url)
+        xhr.send()
+      })
+    },
+
+    getThemeCluster(theme) {
+      const tintColor = (color, tint) => {
+        let red = parseInt(color.slice(0, 2), 16)
+        let green = parseInt(color.slice(2, 4), 16)
+        let blue = parseInt(color.slice(4, 6), 16)
+
+        if (tint === 0) { // when primary color is in its rgb space
+          return [red, green, blue].join(',')
+        } else {
+          red += Math.round(tint * (255 - red))
+          green += Math.round(tint * (255 - green))
+          blue += Math.round(tint * (255 - blue))
+
+          red = red.toString(16)
+          green = green.toString(16)
+          blue = blue.toString(16)
+
+          return `#${red}${green}${blue}`
+        }
+      }
+
+      const shadeColor = (color, shade) => {
+        let red = parseInt(color.slice(0, 2), 16)
+        let green = parseInt(color.slice(2, 4), 16)
+        let blue = parseInt(color.slice(4, 6), 16)
+
+        red = Math.round((1 - shade) * red)
+        green = Math.round((1 - shade) * green)
+        blue = Math.round((1 - shade) * blue)
+
+        red = red.toString(16)
+        green = green.toString(16)
+        blue = blue.toString(16)
+
+        return `#${red}${green}${blue}`
+      }
+
+      const clusters = [theme]
+      for (let i = 0; i <= 9; i++) {
+        clusters.push(tintColor(theme, Number((i / 10).toFixed(2))))
+      }
+      clusters.push(shadeColor(theme, 0.1))
+      return clusters
+    }
+  }
+}
+</script>
+
+<style>
+.theme-message,
+.theme-picker-dropdown {
+  z-index: 99999 !important;
+}
+
+.theme-picker .el-color-picker__trigger {
+  height: 26px !important;
+  width: 26px !important;
+  padding: 2px;
+}
+
+.theme-picker-dropdown .el-color-dropdown__link-btn {
+  display: none;
+}
+</style>

+ 111 - 0
components/Tinymce/components/EditorImage.vue

@@ -0,0 +1,111 @@
+<template>
+  <div class="upload-container">
+    <el-button :style="{background:color,borderColor:color}" icon="el-icon-upload" size="mini" type="primary" @click=" dialogVisible=true">
+      upload
+    </el-button>
+    <el-dialog :visible.sync="dialogVisible">
+      <el-upload
+        :multiple="true"
+        :file-list="fileList"
+        :show-file-list="true"
+        :on-remove="handleRemove"
+        :on-success="handleSuccess"
+        :before-upload="beforeUpload"
+        class="editor-slide-upload"
+        action="https://httpbin.org/post"
+        list-type="picture-card"
+      >
+        <el-button size="small" type="primary">
+          Click upload
+        </el-button>
+      </el-upload>
+      <el-button @click="dialogVisible = false">
+        Cancel
+      </el-button>
+      <el-button type="primary" @click="handleSubmit">
+        Confirm
+      </el-button>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+// import { getToken } from 'api/qiniu'
+
+export default {
+  name: 'EditorSlideUpload',
+  props: {
+    color: {
+      type: String,
+      default: '#1890ff'
+    }
+  },
+  data() {
+    return {
+      dialogVisible: false,
+      listObj: {},
+      fileList: []
+    }
+  },
+  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('Please wait for all images to be uploaded successfully. If there is a network problem, please refresh the page and upload again!')
+        return
+      }
+      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 = response.files.file
+          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 _URL = window.URL || window.webkitURL
+      const fileName = file.uid
+      this.listObj[fileName] = {}
+      return new Promise((resolve, reject) => {
+        const img = new Image()
+        img.src = _URL.createObjectURL(file)
+        img.onload = function() {
+          _self.listObj[fileName] = { hasSuccess: false, uid: file.uid, width: this.width, height: this.height }
+        }
+        resolve(true)
+      })
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.editor-slide-upload {
+  margin-bottom: 20px;
+  /deep/ .el-upload--picture-card {
+    width: 100%;
+  }
+}
+</style>

+ 59 - 0
components/Tinymce/dynamicLoadScript.js

@@ -0,0 +1,59 @@
+let callbacks = []
+
+function loadedTinymce() {
+  // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2144
+  // check is successfully downloaded script
+  return window.tinymce
+}
+
+const dynamicLoadScript = (src, callback) => {
+  const existingScript = document.getElementById(src)
+  const cb = callback || function() {}
+
+  if (!existingScript) {
+    const script = document.createElement('script')
+    script.src = src // src url for the third-party library being loaded.
+    script.id = src
+    document.body.appendChild(script)
+    callbacks.push(cb)
+    const onEnd = 'onload' in script ? stdOnEnd : ieOnEnd
+    onEnd(script)
+  }
+
+  if (existingScript && cb) {
+    if (loadedTinymce()) {
+      cb(null, existingScript)
+    } else {
+      callbacks.push(cb)
+    }
+  }
+
+  function stdOnEnd(script) {
+    script.onload = function() {
+      // this.onload = null here is necessary
+      // because even IE9 works not like others
+      this.onerror = this.onload = null
+      for (const cb of callbacks) {
+        cb(null, script)
+      }
+      callbacks = null
+    }
+    script.onerror = function() {
+      this.onerror = this.onload = null
+      cb(new Error('Failed to load ' + src), script)
+    }
+  }
+
+  function ieOnEnd(script) {
+    script.onreadystatechange = function() {
+      if (this.readyState !== 'complete' && this.readyState !== 'loaded') return
+      this.onreadystatechange = null
+      for (const cb of callbacks) {
+        cb(null, script) // there is no way to catch loading errors in IE8
+      }
+      callbacks = null
+    }
+  }
+}
+
+export default dynamicLoadScript

+ 237 - 0
components/Tinymce/index.vue

@@ -0,0 +1,237 @@
+<template>
+  <div :class="{fullscreen:fullscreen}" class="tinymce-container" :style="{width:containerWidth}">
+    <textarea :id="tinymceId" class="tinymce-textarea" />
+    <div class="editor-custom-btn-container">
+      <editorImage color="#1890ff" class="editor-upload-btn" @successCBK="imageSuccessCBK" />
+    </div>
+  </div>
+</template>
+
+<script>
+/**
+ * docs:
+ * https://panjiachen.github.io/vue-element-admin-site/feature/component/rich-editor.html#tinymce
+ */
+import editorImage from './components/EditorImage'
+import plugins from './plugins'
+import toolbar from './toolbar'
+import load from './dynamicLoadScript'
+
+// why use this cdn, detail see https://github.com/PanJiaChen/tinymce-all-in-one
+const tinymceCDN = 'https://cdn.jsdelivr.net/npm/tinymce-all-in-one@4.9.3/tinymce.min.js'
+
+export default {
+  name: 'Tinymce',
+  components: { editorImage },
+  props: {
+    id: {
+      type: String,
+      default: function() {
+        return 'vue-tinymce-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '')
+      }
+    },
+    value: {
+      type: String,
+      default: ''
+    },
+    toolbar: {
+      type: Array,
+      required: false,
+      default() {
+        return []
+      }
+    },
+    menubar: {
+      type: String,
+      default: 'file edit insert view format table'
+    },
+    height: {
+      type: [Number, String],
+      required: false,
+      default: 360
+    },
+    width: {
+      type: [Number, String],
+      required: false,
+      default: 'auto'
+    }
+  },
+  data() {
+    return {
+      hasChange: false,
+      hasInit: false,
+      tinymceId: this.id,
+      fullscreen: false,
+      languageTypeList: {
+        'en': 'en',
+        'zh': 'zh_CN',
+        'es': 'es_MX',
+        'ja': 'ja'
+      }
+    }
+  },
+  computed: {
+    containerWidth() {
+      const width = this.width
+      if (/^[\d]+(\.[\d]+)?$/.test(width)) { // matches `100`, `'100'`
+        return `${width}px`
+      }
+      return width
+    }
+  },
+  watch: {
+    value(val) {
+      if (!this.hasChange && this.hasInit) {
+        this.$nextTick(() =>
+          window.tinymce.get(this.tinymceId).setContent(val || ''))
+      }
+    }
+  },
+  mounted() {
+    this.init()
+  },
+  activated() {
+    if (window.tinymce) {
+      this.initTinymce()
+    }
+  },
+  deactivated() {
+    this.destroyTinymce()
+  },
+  destroyed() {
+    this.destroyTinymce()
+  },
+  methods: {
+    init() {
+      // dynamic load tinymce from cdn
+      load(tinymceCDN, (err) => {
+        if (err) {
+          this.$message.error(err.message)
+          return
+        }
+        this.initTinymce()
+      })
+    },
+    initTinymce() {
+      const _this = this
+      window.tinymce.init({
+        selector: `#${this.tinymceId}`,
+        language: this.languageTypeList['en'],
+        height: this.height,
+        body_class: 'panel-body ',
+        object_resizing: false,
+        toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar,
+        menubar: this.menubar,
+        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,
+        nonbreaking_force_tab: true, // inserting nonbreaking space &nbsp; need Nonbreaking Space Plugin
+        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())
+          })
+        },
+        setup(editor) {
+          editor.on('FullscreenStateChanged', (e) => {
+            _this.fullscreen = e.state
+          })
+        }
+        // 整合七牛上传
+        // images_dataimg_filter(img) {
+        //   setTimeout(() => {
+        //     const $image = $(img);
+        //     $image.removeAttr('width');
+        //     $image.removeAttr('height');
+        //     if ($image[0].height && $image[0].width) {
+        //       $image.attr('data-wscntype', 'image');
+        //       $image.attr('data-wscnh', $image[0].height);
+        //       $image.attr('data-wscnw', $image[0].width);
+        //       $image.addClass('wscnph');
+        //     }
+        //   }, 0);
+        //   return img
+        // },
+        // images_upload_handler(blobInfo, success, failure, progress) {
+        //   progress(0);
+        //   const token = _this.$store.getters.token;
+        //   getToken(token).then(response => {
+        //     const url = response.data.qiniu_url;
+        //     const formData = new FormData();
+        //     formData.append('token', response.data.qiniu_token);
+        //     formData.append('key', response.data.qiniu_key);
+        //     formData.append('file', blobInfo.blob(), url);
+        //     upload(formData).then(() => {
+        //       success(url);
+        //       progress(100);
+        //     })
+        //   }).catch(err => {
+        //     failure('出现未知问题,刷新页面,或者联系程序员')
+        //     console.log(err);
+        //   });
+        // },
+      })
+    },
+    destroyTinymce() {
+      const tinymce = window.tinymce.get(this.tinymceId)
+      if (this.fullscreen) {
+        tinymce.execCommand('mceFullScreen')
+      }
+
+      if (tinymce) {
+        tinymce.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}" >`)
+      })
+    }
+  }
+}
+</script>
+
+<style scoped>
+.tinymce-container {
+  position: relative;
+  line-height: normal;
+}
+.tinymce-container>>>.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;
+}
+</style>

+ 7 - 0
components/Tinymce/plugins.js

@@ -0,0 +1,7 @@
+// Any plugins you want to use has to be imported
+// Detail plugins list see https://www.tinymce.com/docs/plugins/
+// Custom builds see https://www.tinymce.com/download/custom-builds/
+
+const plugins = ['advlist anchor autolink autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount']
+
+export default plugins

+ 6 - 0
components/Tinymce/toolbar.js

@@ -0,0 +1,6 @@
+// Here is a list of the toolbar
+// Detail list see https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols
+
+const toolbar = ['searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent  blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen']
+
+export default toolbar

+ 28 - 14
components/menu/data.js

@@ -1,71 +1,85 @@
+const baseUrl = '/main/'
 export default [{
   title: '财务',
+  icon: 'coin',
+  path: '',
   subs: [
     {
       title: '云端结算',
-      path: 'cloud_balance',
+      path: baseUrl + 'cloud_balance',
     },
     {
       title: '提现账单',
-      // path: 'withdraw',
+      path: baseUrl + 'withdraw',
     },
     {
       title: '工资结算',
-      path: 'wage_settlement',
+      path: 'baseUrl + wage_settlement',
       hidden: true
     }
   ]
 },
 {
   title: '运营',
+  icon: 'service',
+  path: '',
   subs: [
     {
       title: '云端开发者展示',
-      path: 'dev_show',
+      path: baseUrl + 'dev_show',
     },
   ]
 },
 {
   title: '项目',
+  icon: 's-management',
+  path: '',
   subs: [{
     title: '云端工作',
-    path: 'cloud_job',
+    path: baseUrl + 'cloud_job',
   },
   {
     title: '协作群组',
-    path: 'group_list',
+    path: baseUrl + 'group_list',
   },
   {
     title: '工猫结算订单',
-    path: 'gongmall',
+    path: baseUrl + 'gongmall',
   }]
 },
 {
   title: '用户与认证',
+  icon: 's-check',
+  path: '',
   subs: [{
+    title: '开发者管理',
+    path: baseUrl + 'cats',
+  },{
     title: '开发者审核',
-    path: 'dev_check',
+    path: baseUrl + 'dev_check',
   }, {
     title: '认证开发者',
-    path: 'cert_check',
+    path: baseUrl + 'cert_check',
   }, {
     title: '认证产品',
-    path: 'cert_pro',
+    path: baseUrl + 'cert_pro',
   }, {
     title: '云端开发者认证表',
-    path: 'cloud_developer',
+    path: baseUrl + 'cloud_developer',
   },]
 },
 {
   title: '会员',
+  icon: 'user-solid',
+  path: '',
   subs: [{
     title: '会员管理',
-    path: 'vip_manager',
+    path: baseUrl + 'vip_manager',
   }, {
     title: '会员订单',
-    path: 'vip_order',
+    path: baseUrl + 'vip_order',
   }, {
     title: '会员设置',
-    path: 'vip_setting',
+    path: baseUrl + 'vip_setting',
   }]
 },]

Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/_nuxt/0acbd09528b0afc3c26e.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/_nuxt/0d7809cee78645c38829.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/_nuxt/11b53fbcc5a01ef62a96.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 1
dist/_nuxt/17e1d4c8f313b7768167.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/_nuxt/1aa77fc3756bd63223ff.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 1
dist/_nuxt/1c8058778177c352d171.js


Разница между файлами не показана из-за своего большого размера
+ 2 - 0
dist/_nuxt/27bd24a074497b64ace7.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/_nuxt/2846b2cded0c910c028c.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 1
dist/_nuxt/300f56801156fa7c5df8.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 1
dist/_nuxt/3b9c88b26bbc93e84862.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/_nuxt/42411812009633ea3fe5.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 1
dist/_nuxt/475efeb9f0014beda531.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/_nuxt/4e0144fb308038d183f4.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/_nuxt/55897e4ed2c98d947b06.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 1
dist/_nuxt/6fd060a533856f79eed7.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 1
dist/_nuxt/71604b98d0b00f59a156.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 1
dist/_nuxt/765eb226a83f346e9801.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/_nuxt/83fb4aa32e89f8fa1fdc.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 2
dist/_nuxt/859db025c9bb12b28eb3.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 1
dist/_nuxt/8859b1fa8aa6716d09fa.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 1
dist/_nuxt/923de8f2056bfdc309e9.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/_nuxt/98a17df2b53bb0b4e49f.js


+ 29 - 0
dist/_nuxt/LICENSES

@@ -4,6 +4,20 @@
  * Released under the MIT License.
  */
 
+/**
+ * vuex v3.1.1
+ * (c) 2019 Evan You
+ * @license MIT
+ */
+
+/*!
+ * JavaScript Cookie v2.2.0
+ * https://github.com/js-cookie/js-cookie
+ *
+ * Copyright 2006, 2015 Klaus Hartl & Fagner Brack
+ * Released under the MIT license
+ */
+
 /*!
   * vue-router v3.0.6
   * (c) 2019 Evan You
@@ -30,12 +44,27 @@
  */
 
 /*!
+* screenfull
+* v4.2.1 - 2019-07-27
+* (c) Sindre Sorhus; MIT License
+*/
+
+/*!
  * vue-no-ssr v1.1.1
  * (c) 2018-present egoist <0x142857@gmail.com>
  * Released under the MIT License.
  */
 
 /*!
+ * Fuse.js v3.4.5 - Lightweight fuzzy-search (http://fusejs.io)
+ * 
+ * Copyright (c) 2012-2017 Kirollos Risk (http://kiro.me)
+ * All Rights Reserved. Apache Software License 2.0
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ */
+
+/*!
  * The buffer module from node.js, for the browser.
  *
  * @author   Feross Aboukhadijeh <feross@feross.org> <http://feross.org>

Разница между файлами не показана из-за своего большого размера
+ 0 - 1
dist/_nuxt/a372492e12d2a859de9b.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/_nuxt/a5ccaac32c4a03021dfb.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 1
dist/_nuxt/a703d1baf58945626373.js


Разница между файлами не показана из-за своего большого размера
+ 2 - 0
dist/_nuxt/abd526b9a3a6c78082c1.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 2
dist/_nuxt/ae110e300ff91279fa88.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/_nuxt/ae2c164acac3fcf64c52.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/_nuxt/b7e9c93dadd0d9ee2e94.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/_nuxt/bbb32bc241e2da98e1ee.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 1
dist/_nuxt/bc2546130f7259bd44c7.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/_nuxt/c05718ef4109c8c69e2f.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/_nuxt/c1e7a132a58d8ed1eb49.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/_nuxt/cc37d19bece584e4ec2c.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 1
dist/_nuxt/d1ec47237892490a446f.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/_nuxt/d2ee2c15c5a16e87441d.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/_nuxt/e1599602ab7ce5a5b384.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 1
dist/_nuxt/e41a7a762d2844a42f06.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 1
dist/_nuxt/e65ca75c9796cfe4c028.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 1
dist/_nuxt/ede21b65272903ef08e6.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 1
dist/_nuxt/effaecad9ba37d6d2ff4.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/_nuxt/f0849ddaf800b5c31470.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 1
dist/_nuxt/f248e7183ff8c190f84e.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 1
dist/_nuxt/f36ddbdaab5dd96aa611.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/_nuxt/f65de1e8f302a7ca9415.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/_nuxt/f8844696c18fb2f87e56.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/_nuxt/fd0098c7426bb5b4b9da.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 1
dist/_nuxt/ff0c439f8f6e75973709.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/_nuxt/ff4f31ad07d9fee96435.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 1
dist/_nuxt/ff8798d3214ec2d86594.js


BIN
dist/_nuxt/fonts/2fad952.woff


BIN
dist/_nuxt/fonts/535877f.woff


BIN
dist/_nuxt/fonts/6f0a763.ttf


BIN
dist/_nuxt/fonts/732389d.ttf


Разница между файлами не показана из-за своего большого размера
+ 8 - 0
dist/index.html


Разница между файлами не показана из-за своего большого размера
+ 8 - 0
dist/login/index.html


Разница между файлами не показана из-за своего большого размера
+ 8 - 0
dist/main/404/index.html


Разница между файлами не показана из-за своего большого размера
+ 9 - 0
dist/main/cats/index.html


Разница между файлами не показана из-за своего большого размера
+ 8 - 0
dist/main/cert_check/index.html


Разница между файлами не показана из-за своего большого размера
+ 8 - 0
dist/main/cert_edit/index.html


Разница между файлами не показана из-за своего большого размера
+ 8 - 0
dist/main/cert_pro/index.html


Разница между файлами не показана из-за своего большого размера
+ 8 - 0
dist/main/cloud_balance/index.html


Разница между файлами не показана из-за своего большого размера
+ 8 - 0
dist/main/cloud_developer/index.html


Разница между файлами не показана из-за своего большого размера
+ 8 - 0
dist/main/cloud_job/index.html


Разница между файлами не показана из-за своего большого размера
+ 8 - 0
dist/main/dev_check/index.html


Разница между файлами не показана из-за своего большого размера
+ 8 - 0
dist/main/dev_check_detail/index.html


Разница между файлами не показана из-за своего большого размера
+ 8 - 0
dist/main/dev_show/index.html


Разница между файлами не показана из-за своего большого размера
+ 8 - 0
dist/main/gongmall/index.html


Разница между файлами не показана из-за своего большого размера
+ 8 - 0
dist/main/group_list/index.html


Разница между файлами не показана из-за своего большого размера
+ 8 - 0
dist/main/index.html


Разница между файлами не показана из-за своего большого размера
+ 8 - 0
dist/main/vip_manager/index.html


Разница между файлами не показана из-за своего большого размера
+ 8 - 0
dist/main/vip_order/index.html


Разница между файлами не показана из-за своего большого размера
+ 8 - 0
dist/main/vip_setting/index.html


Разница между файлами не показана из-за своего большого размера
+ 8 - 0
dist/main/wage_details/index.html


Разница между файлами не показана из-за своего большого размера
+ 8 - 0
dist/main/wage_settlement/index.html


+ 0 - 49
hooks/applypatch-msg

@@ -1,49 +0,0 @@
-#!/bin/sh
-#yorkie 2.0.0
-
-command_exists () {
-  command -v "$1" >/dev/null 2>&1
-}
-
-has_hook_script () {
-  [ -f package.json ] && cat package.json | grep -q "\"$1\"[[:space:]]*:"
-}
-
-# OS X and Linux only
-load_nvm () {
-  # If nvm is not loaded, load it
-  command_exists nvm || {
-    export NVM_DIR="$1"
-    [ -s "$1/nvm.sh" ] && . "$1/nvm.sh"
-  }
-}
-
-# OS X and Linux only
-run_nvm () {
-  # If nvm has been loaded correctly, use project .nvmrc
-  command_exists nvm && [ -f .nvmrc ] && nvm use
-}
-
-cd "."
-
-# Check if applypatch-msg is defined, skip if not
-has_hook_script applypatch-msg || exit 0
-
-# Add common path where Node can be found
-# Brew standard installation path /usr/local/bin
-# Node standard installation path /usr/local
-export PATH="$PATH:/usr/local/bin:/usr/local"
-
-# Try to load nvm using path of standard installation
-load_nvm /Users/zweizhao/.nvm
-run_nvm
-
-# Export Git hook params
-export GIT_PARAMS="$*"
-
-# Run hook
-node "./node_modules/yorkie/src/runner.js" applypatch-msg || {
-  echo
-  echo "applypatch-msg hook failed (add --no-verify to bypass)"
-  exit 1
-}

+ 0 - 15
hooks/applypatch-msg.sample

@@ -1,15 +0,0 @@
-#!/bin/sh
-#
-# An example hook script to check the commit log message taken by
-# applypatch from an e-mail message.
-#
-# The hook should exit with non-zero status after issuing an
-# appropriate message if it wants to stop the commit.  The hook is
-# allowed to edit the commit message file.
-#
-# To enable this hook, rename this file to "applypatch-msg".
-
-. git-sh-setup
-commitmsg="$(git rev-parse --git-path hooks/commit-msg)"
-test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"}
-:

+ 0 - 0
hooks/commit-msg


Некоторые файлы не были показаны из-за большого количества измененных файлов