Browse Source

update: add new theme & add user manage

ArvinQi 6 years ago
parent
commit
07f6a09820
100 changed files with 1616 additions and 83 deletions
  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. 82 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. 2 2
      dist/200.html
  23. 1 0
      dist/_nuxt/0acbd09528b0afc3c26e.js
  24. 1 0
      dist/_nuxt/0d7809cee78645c38829.js
  25. 1 0
      dist/_nuxt/11b53fbcc5a01ef62a96.js
  26. 1 0
      dist/_nuxt/13c11810c9489e129bfd.js
  27. 0 1
      dist/_nuxt/17e1d4c8f313b7768167.js
  28. 1 0
      dist/_nuxt/1aa77fc3756bd63223ff.js
  29. 0 1
      dist/_nuxt/1c8058778177c352d171.js
  30. 2 0
      dist/_nuxt/27bd24a074497b64ace7.js
  31. 1 0
      dist/_nuxt/2846b2cded0c910c028c.js
  32. 0 1
      dist/_nuxt/300f56801156fa7c5df8.js
  33. 0 1
      dist/_nuxt/3b9c88b26bbc93e84862.js
  34. 1 0
      dist/_nuxt/42411812009633ea3fe5.js
  35. 0 1
      dist/_nuxt/475efeb9f0014beda531.js
  36. 1 0
      dist/_nuxt/4e0144fb308038d183f4.js
  37. 1 0
      dist/_nuxt/5e019343977f1bd2d0a9.js
  38. 0 1
      dist/_nuxt/61732149178519557fae.js
  39. 0 1
      dist/_nuxt/6fd060a533856f79eed7.js
  40. 0 1
      dist/_nuxt/71604b98d0b00f59a156.js
  41. 0 1
      dist/_nuxt/765eb226a83f346e9801.js
  42. 1 0
      dist/_nuxt/83048c7c155510711507.js
  43. 1 0
      dist/_nuxt/83fb4aa32e89f8fa1fdc.js
  44. 0 2
      dist/_nuxt/859db025c9bb12b28eb3.js
  45. 0 1
      dist/_nuxt/8859b1fa8aa6716d09fa.js
  46. 0 1
      dist/_nuxt/923de8f2056bfdc309e9.js
  47. 0 1
      dist/_nuxt/9554e121be732f95b758.js
  48. 1 0
      dist/_nuxt/98a17df2b53bb0b4e49f.js
  49. 29 0
      dist/_nuxt/LICENSES
  50. 0 1
      dist/_nuxt/a703d1baf58945626373.js
  51. 2 0
      dist/_nuxt/abd526b9a3a6c78082c1.js
  52. 0 2
      dist/_nuxt/ae110e300ff91279fa88.js
  53. 1 0
      dist/_nuxt/ae2c164acac3fcf64c52.js
  54. 1 0
      dist/_nuxt/b7e9c93dadd0d9ee2e94.js
  55. 1 0
      dist/_nuxt/bbb32bc241e2da98e1ee.js
  56. 0 1
      dist/_nuxt/bc2546130f7259bd44c7.js
  57. 1 0
      dist/_nuxt/c05718ef4109c8c69e2f.js
  58. 0 1
      dist/_nuxt/c73c73cb5e70642a86ce.js
  59. 1 0
      dist/_nuxt/cc37d19bece584e4ec2c.js
  60. 0 1
      dist/_nuxt/d1ec47237892490a446f.js
  61. 1 0
      dist/_nuxt/d2ee2c15c5a16e87441d.js
  62. 1 0
      dist/_nuxt/e1599602ab7ce5a5b384.js
  63. 0 1
      dist/_nuxt/e41a7a762d2844a42f06.js
  64. 0 1
      dist/_nuxt/e65ca75c9796cfe4c028.js
  65. 0 1
      dist/_nuxt/ede21b65272903ef08e6.js
  66. 0 1
      dist/_nuxt/effaecad9ba37d6d2ff4.js
  67. 1 0
      dist/_nuxt/f0849ddaf800b5c31470.js
  68. 0 1
      dist/_nuxt/f248e7183ff8c190f84e.js
  69. 1 0
      dist/_nuxt/f65de1e8f302a7ca9415.js
  70. 1 0
      dist/_nuxt/f8844696c18fb2f87e56.js
  71. 1 0
      dist/_nuxt/fd0098c7426bb5b4b9da.js
  72. 0 1
      dist/_nuxt/ff0c439f8f6e75973709.js
  73. 1 0
      dist/_nuxt/ff4f31ad07d9fee96435.js
  74. 0 1
      dist/_nuxt/ff8798d3214ec2d86594.js
  75. BIN
      dist/_nuxt/fonts/2fad952.woff
  76. BIN
      dist/_nuxt/fonts/535877f.woff
  77. BIN
      dist/_nuxt/fonts/6f0a763.ttf
  78. BIN
      dist/_nuxt/fonts/732389d.ttf
  79. 2 2
      dist/index.html
  80. 2 2
      dist/login/index.html
  81. 2 2
      dist/main/404/index.html
  82. 9 0
      dist/main/cats/index.html
  83. 2 2
      dist/main/cert_check/index.html
  84. 2 2
      dist/main/cert_edit/index.html
  85. 2 2
      dist/main/cert_pro/index.html
  86. 2 2
      dist/main/cloud_balance/index.html
  87. 2 2
      dist/main/cloud_developer/index.html
  88. 2 2
      dist/main/cloud_job/index.html
  89. 2 2
      dist/main/dev_check/index.html
  90. 2 2
      dist/main/dev_check_detail/index.html
  91. 2 2
      dist/main/dev_show/index.html
  92. 2 2
      dist/main/gongmall/index.html
  93. 2 2
      dist/main/group_list/index.html
  94. 2 2
      dist/main/index.html
  95. 2 2
      dist/main/vip_manager/index.html
  96. 2 2
      dist/main/vip_order/index.html
  97. 2 2
      dist/main/vip_setting/index.html
  98. 2 2
      dist/main/wage_details/index.html
  99. 2 2
      dist/main/wage_settlement/index.html
  100. 0 0
      hooks/applypatch-msg

BIN
.DS_Store


+ 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>

+ 82 - 0
components/Breadcrumb/index.vue

@@ -0,0 +1,82 @@
+<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.meta.title }}</span>
+        <a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a>
+      </el-breadcrumb-item>
+    </transition-group>
+  </el-breadcrumb>
+</template>
+
+<script>
+import pathToRegexp from 'path-to-regexp'
+
+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.meta && item.meta.title)
+      const first = matched[0]
+
+      if (!this.isDashboard(first)) {
+        matched = [{ path: '/dashboard', meta: { title: 'Dashboard' }}].concat(matched)
+      }
+
+      this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
+    },
+    isDashboard(route) {
+      const name = route && route.name
+      if (!name) {
+        return false
+      }
+      return name.trim().toLocaleLowerCase() === 'Dashboard'.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',
   }]
 },]

File diff suppressed because it is too large
+ 2 - 2
dist/200.html


File diff suppressed because it is too large
+ 1 - 0
dist/_nuxt/0acbd09528b0afc3c26e.js


File diff suppressed because it is too large
+ 1 - 0
dist/_nuxt/0d7809cee78645c38829.js


File diff suppressed because it is too large
+ 1 - 0
dist/_nuxt/11b53fbcc5a01ef62a96.js


File diff suppressed because it is too large
+ 1 - 0
dist/_nuxt/13c11810c9489e129bfd.js


File diff suppressed because it is too large
+ 0 - 1
dist/_nuxt/17e1d4c8f313b7768167.js


File diff suppressed because it is too large
+ 1 - 0
dist/_nuxt/1aa77fc3756bd63223ff.js


File diff suppressed because it is too large
+ 0 - 1
dist/_nuxt/1c8058778177c352d171.js


File diff suppressed because it is too large
+ 2 - 0
dist/_nuxt/27bd24a074497b64ace7.js


File diff suppressed because it is too large
+ 1 - 0
dist/_nuxt/2846b2cded0c910c028c.js


File diff suppressed because it is too large
+ 0 - 1
dist/_nuxt/300f56801156fa7c5df8.js


File diff suppressed because it is too large
+ 0 - 1
dist/_nuxt/3b9c88b26bbc93e84862.js


File diff suppressed because it is too large
+ 1 - 0
dist/_nuxt/42411812009633ea3fe5.js


File diff suppressed because it is too large
+ 0 - 1
dist/_nuxt/475efeb9f0014beda531.js


File diff suppressed because it is too large
+ 1 - 0
dist/_nuxt/4e0144fb308038d183f4.js


File diff suppressed because it is too large
+ 1 - 0
dist/_nuxt/5e019343977f1bd2d0a9.js


File diff suppressed because it is too large
+ 0 - 1
dist/_nuxt/61732149178519557fae.js


File diff suppressed because it is too large
+ 0 - 1
dist/_nuxt/6fd060a533856f79eed7.js


File diff suppressed because it is too large
+ 0 - 1
dist/_nuxt/71604b98d0b00f59a156.js


File diff suppressed because it is too large
+ 0 - 1
dist/_nuxt/765eb226a83f346e9801.js


File diff suppressed because it is too large
+ 1 - 0
dist/_nuxt/83048c7c155510711507.js


File diff suppressed because it is too large
+ 1 - 0
dist/_nuxt/83fb4aa32e89f8fa1fdc.js


File diff suppressed because it is too large
+ 0 - 2
dist/_nuxt/859db025c9bb12b28eb3.js


File diff suppressed because it is too large
+ 0 - 1
dist/_nuxt/8859b1fa8aa6716d09fa.js


File diff suppressed because it is too large
+ 0 - 1
dist/_nuxt/923de8f2056bfdc309e9.js


File diff suppressed because it is too large
+ 0 - 1
dist/_nuxt/9554e121be732f95b758.js


File diff suppressed because it is too large
+ 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>

File diff suppressed because it is too large
+ 0 - 1
dist/_nuxt/a703d1baf58945626373.js


File diff suppressed because it is too large
+ 2 - 0
dist/_nuxt/abd526b9a3a6c78082c1.js


File diff suppressed because it is too large
+ 0 - 2
dist/_nuxt/ae110e300ff91279fa88.js


File diff suppressed because it is too large
+ 1 - 0
dist/_nuxt/ae2c164acac3fcf64c52.js


File diff suppressed because it is too large
+ 1 - 0
dist/_nuxt/b7e9c93dadd0d9ee2e94.js


File diff suppressed because it is too large
+ 1 - 0
dist/_nuxt/bbb32bc241e2da98e1ee.js


File diff suppressed because it is too large
+ 0 - 1
dist/_nuxt/bc2546130f7259bd44c7.js


File diff suppressed because it is too large
+ 1 - 0
dist/_nuxt/c05718ef4109c8c69e2f.js


File diff suppressed because it is too large
+ 0 - 1
dist/_nuxt/c73c73cb5e70642a86ce.js


File diff suppressed because it is too large
+ 1 - 0
dist/_nuxt/cc37d19bece584e4ec2c.js


File diff suppressed because it is too large
+ 0 - 1
dist/_nuxt/d1ec47237892490a446f.js


File diff suppressed because it is too large
+ 1 - 0
dist/_nuxt/d2ee2c15c5a16e87441d.js


File diff suppressed because it is too large
+ 1 - 0
dist/_nuxt/e1599602ab7ce5a5b384.js


File diff suppressed because it is too large
+ 0 - 1
dist/_nuxt/e41a7a762d2844a42f06.js


File diff suppressed because it is too large
+ 0 - 1
dist/_nuxt/e65ca75c9796cfe4c028.js


File diff suppressed because it is too large
+ 0 - 1
dist/_nuxt/ede21b65272903ef08e6.js


File diff suppressed because it is too large
+ 0 - 1
dist/_nuxt/effaecad9ba37d6d2ff4.js


File diff suppressed because it is too large
+ 1 - 0
dist/_nuxt/f0849ddaf800b5c31470.js


File diff suppressed because it is too large
+ 0 - 1
dist/_nuxt/f248e7183ff8c190f84e.js


File diff suppressed because it is too large
+ 1 - 0
dist/_nuxt/f65de1e8f302a7ca9415.js


File diff suppressed because it is too large
+ 1 - 0
dist/_nuxt/f8844696c18fb2f87e56.js


File diff suppressed because it is too large
+ 1 - 0
dist/_nuxt/fd0098c7426bb5b4b9da.js


File diff suppressed because it is too large
+ 0 - 1
dist/_nuxt/ff0c439f8f6e75973709.js


File diff suppressed because it is too large
+ 1 - 0
dist/_nuxt/ff4f31ad07d9fee96435.js


File diff suppressed because it is too large
+ 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


File diff suppressed because it is too large
+ 2 - 2
dist/index.html


File diff suppressed because it is too large
+ 2 - 2
dist/login/index.html


File diff suppressed because it is too large
+ 2 - 2
dist/main/404/index.html


File diff suppressed because it is too large
+ 9 - 0
dist/main/cats/index.html


File diff suppressed because it is too large
+ 2 - 2
dist/main/cert_check/index.html


File diff suppressed because it is too large
+ 2 - 2
dist/main/cert_edit/index.html


File diff suppressed because it is too large
+ 2 - 2
dist/main/cert_pro/index.html


File diff suppressed because it is too large
+ 2 - 2
dist/main/cloud_balance/index.html


File diff suppressed because it is too large
+ 2 - 2
dist/main/cloud_developer/index.html


File diff suppressed because it is too large
+ 2 - 2
dist/main/cloud_job/index.html


File diff suppressed because it is too large
+ 2 - 2
dist/main/dev_check/index.html


File diff suppressed because it is too large
+ 2 - 2
dist/main/dev_check_detail/index.html


File diff suppressed because it is too large
+ 2 - 2
dist/main/dev_show/index.html


File diff suppressed because it is too large
+ 2 - 2
dist/main/gongmall/index.html


File diff suppressed because it is too large
+ 2 - 2
dist/main/group_list/index.html


File diff suppressed because it is too large
+ 2 - 2
dist/main/index.html


File diff suppressed because it is too large
+ 2 - 2
dist/main/vip_manager/index.html


File diff suppressed because it is too large
+ 2 - 2
dist/main/vip_order/index.html


File diff suppressed because it is too large
+ 2 - 2
dist/main/vip_setting/index.html


File diff suppressed because it is too large
+ 2 - 2
dist/main/wage_details/index.html


File diff suppressed because it is too large
+ 2 - 2
dist/main/wage_settlement/index.html


+ 0 - 0
hooks/applypatch-msg


Some files were not shown because too many files changed in this diff