| @ -0,0 +1,2 @@ | ||||
| > 1% | ||||
| last 2 versions | ||||
| @ -0,0 +1,5 @@ | ||||
| [*.{js,jsx,ts,tsx,vue}] | ||||
| indent_style = space | ||||
| indent_size = 4 | ||||
| trim_trailing_whitespace = true | ||||
| insert_final_newline = true | ||||
| @ -0,0 +1,19 @@ | ||||
| module.exports = { | ||||
|   root: true, | ||||
|   env: { | ||||
|     node: true | ||||
|   }, | ||||
|   'extends': [ | ||||
|     'plugin:vue/essential', | ||||
|     '@vue/standard' | ||||
|   ], | ||||
|   rules: { | ||||
|     'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', | ||||
|     'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', | ||||
|     "space-before-function-paren": 0, | ||||
|     "indent": [2, 4] | ||||
|   }, | ||||
|   parserOptions: { | ||||
|     parser: 'babel-eslint' | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,21 @@ | ||||
| .DS_Store | ||||
| node_modules | ||||
| /dist | ||||
| 
 | ||||
| # local env files | ||||
| .env.local | ||||
| .env.*.local | ||||
| 
 | ||||
| # Log files | ||||
| npm-debug.log* | ||||
| yarn-debug.log* | ||||
| yarn-error.log* | ||||
| 
 | ||||
| # Editor directories and files | ||||
| .idea | ||||
| .vscode | ||||
| *.suo | ||||
| *.ntvs* | ||||
| *.njsproj | ||||
| *.sln | ||||
| *.sw? | ||||
| @ -0,0 +1,5 @@ | ||||
| { | ||||
|     "tabWidth": 4, | ||||
|     "singleQuote":true, | ||||
| 	"semi":false | ||||
| } | ||||
| @ -0,0 +1,3 @@ | ||||
| module.exports = { | ||||
|     presets: ['@vue/app'] | ||||
| } | ||||
| @ -0,0 +1,22 @@ | ||||
| module.exports = { | ||||
|     moduleFileExtensions: ['js', 'jsx', 'json', 'vue'], | ||||
|     transform: { | ||||
|         '^.+\\.vue$': 'vue-jest', | ||||
|         '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': | ||||
|             'jest-transform-stub', | ||||
|         '^.+\\.jsx?$': 'babel-jest' | ||||
|     }, | ||||
|     transformIgnorePatterns: ['/node_modules/'], | ||||
|     moduleNameMapper: { | ||||
|         '^@/(.*)$': '<rootDir>/src/$1' | ||||
|     }, | ||||
|     snapshotSerializers: ['jest-serializer-vue'], | ||||
|     testMatch: [ | ||||
|         '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)' | ||||
|     ], | ||||
|     testURL: 'http://localhost/', | ||||
|     watchPlugins: [ | ||||
|         'jest-watch-typeahead/filename', | ||||
|         'jest-watch-typeahead/testname' | ||||
|     ] | ||||
| } | ||||
| @ -0,0 +1,49 @@ | ||||
| { | ||||
|   "name": "vue-admin-permission", | ||||
|   "version": "0.1.0", | ||||
|   "private": true, | ||||
|   "author": "Hui <543245444@qq.com>", | ||||
|   "scripts": { | ||||
|     "serve": "vue-cli-service serve", | ||||
|     "serve:randy": "cross-env API_TYPE=randy vue-cli-service serve", | ||||
|     "serve:peter": "cross-env API_TYPE=peter vue-cli-service serve", | ||||
|     "build": "vue-cli-service build", | ||||
|     "analyze": "vue-cli-service build", | ||||
|     "lint": "vue-cli-service lint", | ||||
|     "test:unit": "vue-cli-service test:unit", | ||||
|     "dll": "webpack -p --progress --config ./webpack.dll.conf.js" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "axios": "^0.19.0", | ||||
|     "core-js": "^2.6.5", | ||||
|     "element-ui": "^2.9.2", | ||||
|     "moment": "^2.24.0", | ||||
|     "vue": "^2.6.10", | ||||
|     "vue-router": "^3.0.3", | ||||
|     "vuex": "^3.0.1" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@vue/cli-plugin-babel": "^3.8.0", | ||||
|     "@vue/cli-plugin-eslint": "^3.8.0", | ||||
|     "@vue/cli-plugin-unit-jest": "^3.8.0", | ||||
|     "@vue/cli-service": "^3.8.0", | ||||
|     "@vue/eslint-config-standard": "^4.0.0", | ||||
|     "@vue/test-utils": "1.0.0-beta.29", | ||||
|     "add-asset-html-webpack-plugin": "^3.1.3", | ||||
|     "babel-core": "7.0.0-bridge.0", | ||||
|     "babel-eslint": "^10.0.1", | ||||
|     "babel-jest": "^23.6.0", | ||||
|     "clean-webpack-plugin": "^1.0.1", | ||||
|     "compression-webpack-plugin": "^3.0.0", | ||||
|     "cross-env": "^5.2.0", | ||||
|     "eslint": "^5.16.0", | ||||
|     "eslint-plugin-vue": "^5.0.0", | ||||
|     "qiniu-webpack-plugin": "^0.4.2", | ||||
|     "sass": "^1.18.0", | ||||
|     "sass-loader": "^7.1.0", | ||||
|     "svg-sprite-loader": "^3.9.2", | ||||
|     "vue-template-compiler": "^2.6.10", | ||||
|     "webpack-bundle-analyzer": "^2.13.1", | ||||
|     "webpack-cli": "^3.2.3" | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,5 @@ | ||||
| module.exports = { | ||||
|     plugins: { | ||||
|         autoprefixer: {} | ||||
|     } | ||||
| } | ||||
| After Width: | Height: | Size: 4.2 KiB | 
| @ -0,0 +1,24 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
|   <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||||
|     <meta name="viewport" content="width=device-width,initial-scale=1.0"> | ||||
|     <link rel="icon" href="<%= BASE_URL %>favicon.ico"> | ||||
|     <title>loveRandy</title> | ||||
|   </head> | ||||
|   <body> | ||||
|     <noscript> | ||||
|       <strong>We're sorry but vue-admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> | ||||
|     </noscript> | ||||
|     <div id="app"></div> | ||||
|     <!-- built files will be auto injected --> | ||||
|     <div class="ajax-loading" id="ajaxLoading" style="display: none;"> | ||||
|         <div class="overlay"></div> | ||||
|         <div class="loading"> | ||||
|           <img src="https://media.number-7.cn/ebike-h5/static/images/common/loading.gif" alt=""> | ||||
|           <span>加载中,请稍后...</span> | ||||
|         </div> | ||||
|       </div> | ||||
|   </body> | ||||
| </html> | ||||
| @ -0,0 +1,20 @@ | ||||
| <template> | ||||
|   <div id="app"> | ||||
|     <router-view /> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| export default { | ||||
|     name: 'App' | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <style lang="scss"> | ||||
| #app { | ||||
|     height: 100%; | ||||
|     > div { | ||||
|         height: 100%; | ||||
|     } | ||||
| } | ||||
| </style> | ||||
| @ -0,0 +1,29 @@ | ||||
| import axios from '@/config/httpConfig' | ||||
| 
 | ||||
| export function fetchPermission() { | ||||
|     return axios.get('/user/info') | ||||
| } | ||||
| 
 | ||||
| // 获取用户列表
 | ||||
| export function getUserList() { | ||||
|     return axios.get('/user/list') | ||||
| } | ||||
| // 获取角色列表
 | ||||
| export function getRoleList() { | ||||
|     return axios.get('/role/list') | ||||
| } | ||||
| // 获取所有权限
 | ||||
| export function getAllPermissiion() { | ||||
|     return axios.get('/permission/all') | ||||
| } | ||||
| // 获取一级权限列表
 | ||||
| export function getFirstLevel() { | ||||
|     return axios.get('/permission/resource') | ||||
| } | ||||
| // 获取次级权限列表
 | ||||
| export function getNextLevel(id) { | ||||
|     return axios.get(`/permission/level?id=${id}`) | ||||
| } | ||||
| export function login(data) { | ||||
|     return axios.post('/user/login', data) | ||||
| } | ||||
| After Width: | Height: | Size: 1.6 KiB | 
| @ -0,0 +1,45 @@ | ||||
| <template> | ||||
|   <svg | ||||
|     :class="svgClass" | ||||
|     aria-hidden="true" | ||||
|   > | ||||
|     <use :xlink:href="iconName"></use> | ||||
|   </svg> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| export default { | ||||
|     name: 'svg-icon', | ||||
|     props: { | ||||
|         iconClass: { | ||||
|             type: String, | ||||
|             required: true | ||||
|         }, | ||||
|         className: { | ||||
|             type: String | ||||
|         } | ||||
|     }, | ||||
|     computed: { | ||||
|         iconName() { | ||||
|             return `#icon-${this.iconClass}` | ||||
|         }, | ||||
|         svgClass() { | ||||
|             if (this.className) { | ||||
|                 return 'svg-icon ' + this.className | ||||
|             } else { | ||||
|                 return 'svg-icon' | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <style scoped> | ||||
| .svg-icon { | ||||
|     width: 1em; | ||||
|     height: 1em; | ||||
|     vertical-align: -0.15em; | ||||
|     fill: currentColor; | ||||
|     overflow: hidden; | ||||
| } | ||||
| </style> | ||||
| @ -0,0 +1,63 @@ | ||||
| <template> | ||||
|   <div class='menu-container'> | ||||
|     <template v-for='v in menuList'> | ||||
|       <el-submenu | ||||
|         :index='v.name' | ||||
|         v-if='v.children&&v.children.length>0' | ||||
|         :key='v.name' | ||||
|       > | ||||
|         <template slot='title'> | ||||
|           <!-- <i | ||||
|             class='iconfont' | ||||
|             :class='v.meta.icon' | ||||
|           ></i> --> | ||||
|           <svg-icon v-if="v.meta&&v.meta.icon" :icon-class="v.meta.icon"></svg-icon> | ||||
|           <span>{{v.meta.name}}</span> | ||||
|         </template> | ||||
|         <el-menu-item-group> | ||||
|           <my-nav :menuList='v.children'></my-nav> | ||||
|         </el-menu-item-group> | ||||
|       </el-submenu> | ||||
|       <el-menu-item | ||||
|         :key='v.name' | ||||
|         :index='v.name' | ||||
|         @click='gotoRoute(v.name)' | ||||
|         v-else | ||||
|       > | ||||
|         <!-- <i | ||||
|           class='iconfont' | ||||
|           :class='v.meta.icon' | ||||
|         ></i> --> | ||||
|         <svg-icon v-if="v.meta&&v.meta.icon" :icon-class="v.meta.icon"></svg-icon> | ||||
|         <span slot='title'>{{v.meta.name}}</span> | ||||
|       </el-menu-item> | ||||
|     </template> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| export default { | ||||
|     name: 'my-nav', | ||||
|     props: { | ||||
|         menuList: { | ||||
|             type: Array, | ||||
|             default: () => { | ||||
|                 return [] | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|     methods: { | ||||
|         gotoRoute(name) { | ||||
|             this.$router.push({ name }) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <style lang='scss'> | ||||
| .menu-container { | ||||
|   .svg-icon{ | ||||
|     margin-right:10px; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
| @ -0,0 +1,2 @@ | ||||
| const baseUrl = process.env.NODE_ENV === 'production' ? '/' : '/api' | ||||
| export default baseUrl | ||||
| @ -0,0 +1,133 @@ | ||||
| import axios from 'axios' | ||||
| import store from '@/store/index.js' | ||||
| import baseURL from './baseUrl' | ||||
| import { Message } from 'element-ui' | ||||
| const http = {} | ||||
| 
 | ||||
| var instance = axios.create({ | ||||
|     timeout: 5000, | ||||
|     baseURL, | ||||
|     validateStatus(status) { | ||||
|         switch (status) { | ||||
|         case 400: | ||||
|             Message.error('请求出错') | ||||
|             break | ||||
|         case 401: | ||||
|             Message.warning({ | ||||
|                 message: '授权失败,请重新登录' | ||||
|             }) | ||||
|             store.commit('LOGIN_OUT') | ||||
|             setTimeout(() => { | ||||
|                 window.location.reload() | ||||
|             }, 1000) | ||||
|             return | ||||
|         case 403: | ||||
|             Message.warning({ | ||||
|                 message: '拒绝访问' | ||||
|             }) | ||||
|             break | ||||
|         case 404: | ||||
|             Message.warning({ | ||||
|                 message: '请求错误,未找到该资源' | ||||
|             }) | ||||
|             break | ||||
|         case 500: | ||||
|             Message.warning({ | ||||
|                 message: '服务端错误' | ||||
|             }) | ||||
|             break | ||||
|         } | ||||
|         return status >= 200 && status < 300 | ||||
|     } | ||||
| }) | ||||
| 
 | ||||
| // 添加请求拦截器
 | ||||
| instance.interceptors.request.use( | ||||
|     function(config) { | ||||
|         // 请求头添加token
 | ||||
|         if (store.state.UserToken) { | ||||
|             config.headers.Authorization = `Bearer ${store.state.UserToken}` | ||||
|         } | ||||
|         return config | ||||
|     }, | ||||
|     function(error) { | ||||
|         return Promise.reject(error) | ||||
|     } | ||||
| ) | ||||
| 
 | ||||
| // 响应拦截器即异常处理
 | ||||
| instance.interceptors.response.use( | ||||
|     response => { | ||||
|         return response.data | ||||
|     }, | ||||
|     err => { | ||||
|         if (err && err.response) { | ||||
|         } else { | ||||
|             err.message = '连接服务器失败' | ||||
|         } | ||||
|         // Message.error({
 | ||||
|         //     message: err.message
 | ||||
|         // })
 | ||||
|         return Promise.reject(err.response) | ||||
|     } | ||||
| ) | ||||
| 
 | ||||
| http.get = function(url, options) { | ||||
|     let loading | ||||
|     if (!options || options.isShowLoading !== false) { | ||||
|         loading = document.getElementById('ajaxLoading') | ||||
|         loading.style.display = 'block' | ||||
|     } | ||||
|     return new Promise((resolve, reject) => { | ||||
|         instance | ||||
|             .get(url, options) | ||||
|             .then(response => { | ||||
|                 if (!options || options.isShowLoading !== false) { | ||||
|                     loading = document.getElementById('ajaxLoading') | ||||
|                     loading.style.display = 'none' | ||||
|                 } | ||||
|                 if (response.code === 1) { | ||||
|                     resolve(response.data) | ||||
|                 } else { | ||||
|                     Message.error({ | ||||
|                         message: response.msg | ||||
|                     }) | ||||
|                     reject(response.msg) | ||||
|                 } | ||||
|             }) | ||||
|             .catch(e => { | ||||
|                 console.log(e) | ||||
|             }) | ||||
|     }) | ||||
| } | ||||
| 
 | ||||
| http.post = function(url, data, options) { | ||||
|     let loading | ||||
|     if (!options || options.isShowLoading !== false) { | ||||
|         loading = document.getElementById('ajaxLoading') | ||||
|         loading.style.display = 'block' | ||||
|     } | ||||
|     return new Promise((resolve, reject) => { | ||||
|         instance | ||||
|             .post(url, data, options) | ||||
|             .then(response => { | ||||
|                 if (!options || options.isShowLoading !== false) { | ||||
|                     loading = document.getElementById('ajaxLoading') | ||||
|                     loading.style.display = 'none' | ||||
|                 } | ||||
|                 if (response.code === 1) { | ||||
|                     resolve(response.data) | ||||
|                 } else { | ||||
|                     Message.error({ | ||||
|                         message: response.msg | ||||
|                     }) | ||||
|                     reject(response.message) | ||||
|                 } | ||||
|             }) | ||||
|             .catch(e => { | ||||
|                 console.log(e) | ||||
|             }) | ||||
|     }) | ||||
| } | ||||
| 
 | ||||
| export default http | ||||
| @ -0,0 +1,19 @@ | ||||
| function formatNumber(n) { | ||||
|     const str = n.toString() | ||||
|     return str[1] ? str : `0${str}` | ||||
| } | ||||
| 
 | ||||
| export function formatTime(date) { | ||||
|     const year = date.getFullYear() | ||||
|     const month = date.getMonth() + 1 | ||||
|     const day = date.getDate() | ||||
| 
 | ||||
|     const hour = date.getHours() | ||||
|     const minute = date.getMinutes() | ||||
|     const second = date.getSeconds() | ||||
| 
 | ||||
|     const t1 = [year, month, day].map(formatNumber).join('/') | ||||
|     const t2 = [hour, minute, second].map(formatNumber).join(':') | ||||
| 
 | ||||
|     return `${t1} ${t2}` | ||||
| } | ||||
| @ -0,0 +1,10 @@ | ||||
| import Vue from 'vue' | ||||
| import SvgIcon from '@/components/SvgIcon'// svg组件
 | ||||
| 
 | ||||
| // register globally
 | ||||
| Vue.component('svg-icon', SvgIcon) | ||||
| 
 | ||||
| const requireAll = requireContext => requireContext.keys().map(requireContext) | ||||
| const req = require.context('./svg', false, /\.svg$/) | ||||
| // console.log(req)
 | ||||
| requireAll(req) | ||||
| After Width: | Height: | Size: 852 B | 
| After Width: | Height: | Size: 1.8 KiB | 
| After Width: | Height: | Size: 1.3 KiB | 
| After Width: | Height: | Size: 1.2 KiB | 
| After Width: | Height: | Size: 1.2 KiB | 
| After Width: | Height: | Size: 532 B | 
| After Width: | Height: | Size: 2.4 KiB | 
| After Width: | Height: | Size: 777 B | 
| @ -0,0 +1,58 @@ | ||||
| import Vue from 'vue' | ||||
| import App from '@/App' | ||||
| import store from '@/store/index' | ||||
| import router from '@/router/index' | ||||
| 
 | ||||
| import ElementUI from 'element-ui' | ||||
| import 'element-ui/lib/theme-chalk/index.css' | ||||
| import './styles/index.scss' | ||||
| 
 | ||||
| import axios from './config/httpConfig' | ||||
| import * as globalFilter from './filters/filters' | ||||
| import '@/icons' | ||||
| 
 | ||||
| Vue.prototype.$http = axios | ||||
| 
 | ||||
| for (var key in globalFilter) { | ||||
|     Vue.filter(key, globalFilter[key]) | ||||
| } | ||||
| 
 | ||||
| Vue.use(ElementUI) | ||||
| 
 | ||||
| Vue.config.productionTip = false | ||||
| 
 | ||||
| router.beforeEach((to, from, next) => { | ||||
|     if (!store.state.UserToken) { | ||||
|         if (to.matched.length > 0 && !to.matched.some(record => record.meta.requiresAuth)) { | ||||
|             next() | ||||
|         } else { | ||||
|             next({ path: '/login' }) | ||||
|         } | ||||
|     } else { | ||||
|         if (!store.state.permission.permissionList) { | ||||
|             store.dispatch('permission/FETCH_PERMISSION').then(() => { | ||||
|                 next({ path: to.path }) | ||||
|             }) | ||||
|         } else { | ||||
|             if (to.path !== '/login') { | ||||
|                 next() | ||||
|             } else { | ||||
|                 next(from.fullPath) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| }) | ||||
| 
 | ||||
| router.afterEach((to, from, next) => { | ||||
|     var routerList = to.matched | ||||
|     store.commit('setCrumbList', routerList) | ||||
|     store.commit('permission/SET_CURRENT_MENU', to.name) | ||||
| }) | ||||
| 
 | ||||
| /* eslint-disable no-new */ | ||||
| new Vue({ | ||||
|     el: '#app', | ||||
|     router, | ||||
|     store, | ||||
|     render: h => h(App) | ||||
| }) | ||||
| @ -0,0 +1,220 @@ | ||||
| <template> | ||||
|     <div style="background:#f0f2f5;margin-top: -20px;height:100%;"> | ||||
|         <div class="wscn-http404"> | ||||
|             <div class="pic-404"></div> | ||||
|             <div class="bullshit"> | ||||
|                 <div class="bullshit__headline">{{ message }}</div> | ||||
|                 <div class="bullshit__info">请检查您输入的网址是否正确</div> | ||||
|                 <a @click="backToHome" class="bullshit__return-home">返回首页</a> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| export default { | ||||
|     name: 'page404', | ||||
|     data() { | ||||
|         return {} | ||||
|     }, | ||||
|     methods: { | ||||
|         backToHome() { | ||||
|             this.$router.push('/') | ||||
|         } | ||||
|     }, | ||||
|     computed: { | ||||
|         message() { | ||||
|             return '找不到页面找不到页面找不到页面找不到页面找不到页面找不到页面找不到页面找不到页面找不到页面找不到页面找不到页面找不到页面找不到页面找不到页面找不到页面' | ||||
|         } | ||||
|     } | ||||
| } | ||||
| </script> | ||||
| <style rel="stylesheet/scss" lang="scss" scoped> | ||||
| .wscn-http404 { | ||||
|     position: relative; | ||||
|     width: 1200px; | ||||
|     margin: 20px auto 60px; | ||||
|     padding: 0 100px; | ||||
|     overflow: hidden; | ||||
|     .pic-404 { | ||||
|         position: relative; | ||||
|         float: left; | ||||
|         width: 600px; | ||||
|         padding: 150px 0; | ||||
|         overflow: hidden; | ||||
|         &__parent { | ||||
|             width: 100%; | ||||
|         } | ||||
|         &__child { | ||||
|             position: absolute; | ||||
|             &.left { | ||||
|                 width: 80px; | ||||
|                 top: 17px; | ||||
|                 left: 220px; | ||||
|                 opacity: 0; | ||||
|                 animation-name: cloudLeft; | ||||
|                 animation-duration: 2s; | ||||
|                 animation-timing-function: linear; | ||||
|                 animation-fill-mode: forwards; | ||||
|                 animation-delay: 1s; | ||||
|             } | ||||
|             &.mid { | ||||
|                 width: 46px; | ||||
|                 top: 10px; | ||||
|                 left: 420px; | ||||
|                 opacity: 0; | ||||
|                 animation-name: cloudMid; | ||||
|                 animation-duration: 2s; | ||||
|                 animation-timing-function: linear; | ||||
|                 animation-fill-mode: forwards; | ||||
|                 animation-delay: 1.2s; | ||||
|             } | ||||
|             &.right { | ||||
|                 width: 62px; | ||||
|                 top: 100px; | ||||
|                 left: 500px; | ||||
|                 opacity: 0; | ||||
|                 animation-name: cloudRight; | ||||
|                 animation-duration: 2s; | ||||
|                 animation-timing-function: linear; | ||||
|                 animation-fill-mode: forwards; | ||||
|                 animation-delay: 1s; | ||||
|             } | ||||
|             @keyframes cloudLeft { | ||||
|                 0% { | ||||
|                     top: 17px; | ||||
|                     left: 220px; | ||||
|                     opacity: 0; | ||||
|                 } | ||||
|                 20% { | ||||
|                     top: 33px; | ||||
|                     left: 188px; | ||||
|                     opacity: 1; | ||||
|                 } | ||||
|                 80% { | ||||
|                     top: 81px; | ||||
|                     left: 92px; | ||||
|                     opacity: 1; | ||||
|                 } | ||||
|                 100% { | ||||
|                     top: 97px; | ||||
|                     left: 60px; | ||||
|                     opacity: 0; | ||||
|                 } | ||||
|             } | ||||
|             @keyframes cloudMid { | ||||
|                 0% { | ||||
|                     top: 10px; | ||||
|                     left: 420px; | ||||
|                     opacity: 0; | ||||
|                 } | ||||
|                 20% { | ||||
|                     top: 40px; | ||||
|                     left: 360px; | ||||
|                     opacity: 1; | ||||
|                 } | ||||
|                 70% { | ||||
|                     top: 130px; | ||||
|                     left: 180px; | ||||
|                     opacity: 1; | ||||
|                 } | ||||
|                 100% { | ||||
|                     top: 160px; | ||||
|                     left: 120px; | ||||
|                     opacity: 0; | ||||
|                 } | ||||
|             } | ||||
|             @keyframes cloudRight { | ||||
|                 0% { | ||||
|                     top: 100px; | ||||
|                     left: 500px; | ||||
|                     opacity: 0; | ||||
|                 } | ||||
|                 20% { | ||||
|                     top: 120px; | ||||
|                     left: 460px; | ||||
|                     opacity: 1; | ||||
|                 } | ||||
|                 80% { | ||||
|                     top: 180px; | ||||
|                     left: 340px; | ||||
|                     opacity: 1; | ||||
|                 } | ||||
|                 100% { | ||||
|                     top: 200px; | ||||
|                     left: 300px; | ||||
|                     opacity: 0; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     .bullshit { | ||||
|         position: relative; | ||||
|         float: left; | ||||
|         width: 300px; | ||||
|         padding: 150px 0; | ||||
|         overflow: hidden; | ||||
|         &__oops { | ||||
|             font-size: 32px; | ||||
|             font-weight: bold; | ||||
|             line-height: 40px; | ||||
|             color: #1482f0; | ||||
|             opacity: 0; | ||||
|             margin-bottom: 20px; | ||||
|             animation-name: slideUp; | ||||
|             animation-duration: 0.5s; | ||||
|             animation-fill-mode: forwards; | ||||
|         } | ||||
|         &__headline { | ||||
|             font-size: 20px; | ||||
|             line-height: 24px; | ||||
|             color: #1482f0; | ||||
|             opacity: 0; | ||||
|             margin-bottom: 10px; | ||||
|             animation-name: slideUp; | ||||
|             animation-duration: 0.5s; | ||||
|             animation-delay: 0.1s; | ||||
|             animation-fill-mode: forwards; | ||||
|         } | ||||
|         &__info { | ||||
|             font-size: 13px; | ||||
|             line-height: 21px; | ||||
|             color: grey; | ||||
|             opacity: 0; | ||||
|             margin-bottom: 30px; | ||||
|             animation-name: slideUp; | ||||
|             animation-duration: 0.5s; | ||||
|             animation-delay: 0.2s; | ||||
|             animation-fill-mode: forwards; | ||||
|         } | ||||
|         &__return-home { | ||||
|             display: block; | ||||
|             float: left; | ||||
|             width: 110px; | ||||
|             height: 36px; | ||||
|             background: #1482f0; | ||||
|             border-radius: 100px; | ||||
|             text-align: center; | ||||
|             color: #ffffff; | ||||
|             opacity: 0; | ||||
|             font-size: 14px; | ||||
|             line-height: 36px; | ||||
|             cursor: pointer; | ||||
|             animation-name: slideUp; | ||||
|             animation-duration: 0.5s; | ||||
|             animation-delay: 0.3s; | ||||
|             animation-fill-mode: forwards; | ||||
|         } | ||||
|         @keyframes slideUp { | ||||
|             0% { | ||||
|                 transform: translateY(60px); | ||||
|                 opacity: 0; | ||||
|             } | ||||
|             100% { | ||||
|                 transform: translateY(0); | ||||
|                 opacity: 1; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| </style> | ||||
| @ -0,0 +1,35 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <el-progress | ||||
|       type="circle" | ||||
|       :percentage="0" | ||||
|     ></el-progress> | ||||
|     <el-progress | ||||
|       type="circle" | ||||
|       :percentage="25" | ||||
|     ></el-progress> | ||||
|     <el-progress | ||||
|       type="circle" | ||||
|       :percentage="100" | ||||
|       status="success" | ||||
|     ></el-progress> | ||||
|     <el-progress | ||||
|       type="circle" | ||||
|       :percentage="70" | ||||
|       status="warning" | ||||
|     ></el-progress> | ||||
|     <el-progress | ||||
|       type="circle" | ||||
|       :percentage="50" | ||||
|       status="exception" | ||||
|     ></el-progress> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| export default { | ||||
|     data() { | ||||
|         return {} | ||||
|     } | ||||
| } | ||||
| </script> | ||||
| @ -0,0 +1,22 @@ | ||||
| <template> | ||||
|   <div class="demo-image__placeholder"> | ||||
|     <div class="block"> | ||||
|       <el-image :src="src"> | ||||
|       <div slot="placeholder" class="image-slot"> | ||||
|         加载中<span class="dot">...</span> | ||||
|       </div> | ||||
|     </el-image> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| export default { | ||||
|     data() { | ||||
|         return { | ||||
|             src: | ||||
|                 'https://cube.elemecdn.com/6/94/4d3ea53c084bad6931a56d5158a48jpeg.jpeg' | ||||
|         } | ||||
|     } | ||||
| } | ||||
| </script> | ||||
| @ -0,0 +1,16 @@ | ||||
| <template> | ||||
|     <div> | ||||
|        <router-view></router-view> | ||||
|     </div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| export default { | ||||
|     data() { | ||||
|         return {} | ||||
|     }, | ||||
|     mounted() { | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| </script> | ||||
| @ -0,0 +1,13 @@ | ||||
| <template> | ||||
|     <div> | ||||
|       <p>home</p> | ||||
|     </div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| export default { | ||||
|     data() { | ||||
|         return {} | ||||
|     } | ||||
| } | ||||
| </script> | ||||
| @ -0,0 +1,13 @@ | ||||
| <template> | ||||
|     <div> | ||||
|        one | ||||
|     </div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| export default { | ||||
|     data() { | ||||
|         return {} | ||||
|     } | ||||
| } | ||||
| </script> | ||||
| @ -0,0 +1,22 @@ | ||||
| <template> | ||||
|   <div class="demo-image__placeholder"> | ||||
|     <div class="block"> | ||||
|       <el-image :src="src"> | ||||
|       <div slot="placeholder" class="image-slot"> | ||||
|         加载中<span class="dot">...</span> | ||||
|       </div> | ||||
|     </el-image> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| export default { | ||||
|     data() { | ||||
|         return { | ||||
|             src: | ||||
|                 'https://cube.elemecdn.com/6/94/4d3ea53c084bad6931a56d5158a48jpeg.jpeg' | ||||
|         } | ||||
|     } | ||||
| } | ||||
| </script> | ||||
| @ -0,0 +1,22 @@ | ||||
| <template> | ||||
|     <div> | ||||
|         <router-view></router-view> | ||||
|         <TotalStatus/> | ||||
|     </div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| import TotalStatus from './component/total-status' | ||||
| 
 | ||||
| export default { | ||||
|     data() { | ||||
|         return {} | ||||
|     }, | ||||
|     mounted() { | ||||
| 
 | ||||
|     }, | ||||
|     components: { | ||||
|         TotalStatus | ||||
|     } | ||||
| } | ||||
| </script> | ||||
| @ -0,0 +1,15 @@ | ||||
| <template> | ||||
|     <router-view class="content"></router-view> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| export default { | ||||
|     data() { | ||||
|         return {} | ||||
|     } | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <style lang="scss" scoped> | ||||
| 
 | ||||
| </style> | ||||
| @ -0,0 +1,25 @@ | ||||
| <template> | ||||
|     <div class="main-container"> | ||||
|         <TopAside/> | ||||
|         <Content/> | ||||
|     </div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| import TopAside from './top-aside' | ||||
| import Content from './content' | ||||
| import { mapState } from 'vuex' | ||||
| 
 | ||||
| export default { | ||||
|     data() { | ||||
|         return {} | ||||
|     }, | ||||
|     computed: { | ||||
|         ...mapState(['isSidebarNavCollapse']) | ||||
|     }, | ||||
|     components: { | ||||
|         TopAside, | ||||
|         Content | ||||
|     } | ||||
| } | ||||
| </script> | ||||
| @ -0,0 +1,32 @@ | ||||
| <template> | ||||
|     <el-menu | ||||
|         :collapse="isSidebarNavCollapse" | ||||
|         background-color="#304156" | ||||
|         text-color="#eee" | ||||
|         active-text-color="#4dbcff" | ||||
|         :default-active="currentMenu" | ||||
|     > | ||||
|         <DynamicMenu :menuList="sidebarMenu"></DynamicMenu> | ||||
|     </el-menu> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| import DynamicMenu from '@/components/dynamic-menu' | ||||
| import { mapState } from 'vuex' | ||||
| 
 | ||||
| export default { | ||||
|     data() { | ||||
|         return { | ||||
|             isCollapse: true | ||||
|         } | ||||
|     }, | ||||
|     computed: { | ||||
|         ...mapState(['isSidebarNavCollapse']), | ||||
|         ...mapState('permission', ['sidebarMenu', 'currentMenu']) | ||||
|     }, | ||||
|     methods: {}, | ||||
|     components: { | ||||
|         DynamicMenu | ||||
|     } | ||||
| } | ||||
| </script> | ||||
| @ -0,0 +1,28 @@ | ||||
| <template> | ||||
|     <div :class="{navCollapsed:isSidebarNavCollapse}"> | ||||
|         <sidebarNav  class="sidebar"/> | ||||
|         <mainContent/> | ||||
|     </div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| import sidebarNav from './component/sidebar-nav' | ||||
| import mainContent from './component/main-content/index' | ||||
| import { mapState } from 'vuex' | ||||
| export default { | ||||
|     data() { | ||||
|         return {} | ||||
|     }, | ||||
|     computed: { | ||||
|         ...mapState(['isSidebarNavCollapse']) | ||||
|     }, | ||||
|     components: { | ||||
|         sidebarNav, | ||||
|         mainContent | ||||
|     } | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <style lang="scss" scoped> | ||||
| 
 | ||||
| </style> | ||||
| @ -0,0 +1,16 @@ | ||||
| <template> | ||||
|     <div> | ||||
|        <router-view></router-view> | ||||
|     </div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| export default { | ||||
|     data() { | ||||
|         return {} | ||||
|     }, | ||||
|     mounted() { | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| </script> | ||||
| @ -0,0 +1,28 @@ | ||||
| <template> | ||||
|   <div class="demo-image__lazy"> | ||||
|     <el-image | ||||
|       v-for="url in urls" | ||||
|       :key="url" | ||||
|       :src="url" | ||||
|       lazy | ||||
|     ></el-image> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| export default { | ||||
|     data() { | ||||
|         return { | ||||
|             urls: [ | ||||
|                 'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg', | ||||
|                 'https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg', | ||||
|                 'https://fuss10.elemecdn.com/0/6f/e35ff375812e6b0020b6b4e8f9583jpeg.jpeg', | ||||
|                 'https://fuss10.elemecdn.com/9/bb/e27858e973f5d7d3904835f46abbdjpeg.jpeg', | ||||
|                 'https://fuss10.elemecdn.com/d/e6/c4d93a3805b3ce3f323f7974e6f78jpeg.jpeg', | ||||
|                 'https://fuss10.elemecdn.com/3/28/bbf893f792f03a54408b3b7a7ebf0jpeg.jpeg', | ||||
|                 'https://fuss10.elemecdn.com/2/11/6535bcfb26e4c79b48ddde44f4b6fjpeg.jpeg' | ||||
|             ] | ||||
|         } | ||||
|     } | ||||
| } | ||||
| </script> | ||||
| @ -0,0 +1,13 @@ | ||||
| <template> | ||||
|     <div> | ||||
|        <router-view></router-view> | ||||
|     </div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| export default { | ||||
|     data() { | ||||
|         return {} | ||||
|     } | ||||
| } | ||||
| </script> | ||||
| @ -0,0 +1,37 @@ | ||||
| <template> | ||||
|   <ul | ||||
|     class="infinite-list" | ||||
|     v-infinite-scroll="load" | ||||
|   > | ||||
|     <template v-for="(i,key) in count"> | ||||
|       <li | ||||
|         :key="key" | ||||
|         class="infinite-list-item" | ||||
|       >{{ i }}</li> | ||||
|     </template> | ||||
|   </ul> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| export default { | ||||
|     data() { | ||||
|         return { | ||||
|             count: 0 | ||||
|         } | ||||
|     }, | ||||
|     methods: { | ||||
|         load() { | ||||
|             this.count += 2 | ||||
|         } | ||||
|     } | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <style scope> | ||||
| .infinite-list li{ | ||||
|     background:#f0f1f2; | ||||
|     line-height:40px; | ||||
|     margin-bottom:10px; | ||||
|     text-align: center; | ||||
| } | ||||
| </style> | ||||
| @ -0,0 +1,40 @@ | ||||
| <template> | ||||
|   <el-card class="box-card"> | ||||
|     <div | ||||
|       slot="header" | ||||
|       class="clearfix" | ||||
|     > | ||||
|       <span>卡片名称</span> | ||||
|       <el-button | ||||
|         style="float: right; padding: 3px 0" | ||||
|         type="text" | ||||
|       >操作按钮</el-button> | ||||
|     </div> | ||||
|     <div | ||||
|       v-for="o in 4" | ||||
|       :key="o" | ||||
|       class="text item" | ||||
|     > | ||||
|       {{'列表内容 ' + o }} | ||||
|     </div> | ||||
|   </el-card> | ||||
| </template> | ||||
| 
 | ||||
| <style> | ||||
| .text { | ||||
|     font-size: 14px; | ||||
| } | ||||
| 
 | ||||
| .item { | ||||
|     margin-bottom: 18px; | ||||
| } | ||||
| 
 | ||||
| .clearfix:before, | ||||
| .clearfix:after { | ||||
|     display: table; | ||||
|     content: ''; | ||||
| } | ||||
| .clearfix:after { | ||||
|     clear: both; | ||||
| } | ||||
| </style> | ||||
| @ -0,0 +1,16 @@ | ||||
| <template> | ||||
|     <div> | ||||
|        <router-view></router-view> | ||||
|     </div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| export default { | ||||
|     data() { | ||||
|         return {} | ||||
|     }, | ||||
|     mounted() { | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| </script> | ||||
| @ -0,0 +1,444 @@ | ||||
| 
 | ||||
| <template lang="html"> | ||||
| <el-card class="box-card"> | ||||
|   <div> | ||||
|     <div style="margin-bottom:10px;"> | ||||
|       <el-button type="success" icon="el-icon-plus" size="small" @click="handleNewCategory(1,0)">新增菜单</el-button> | ||||
|     </div> | ||||
|     <el-table | ||||
|       v-loading="loading" | ||||
|       :data="list" | ||||
|       highlight-current-row | ||||
|       border | ||||
|       size="small"> | ||||
|       <el-table-column | ||||
|         label="分类名称" | ||||
|         align="left" | ||||
|       > | ||||
|         <template slot-scope="scope"> | ||||
|           <a :style="`margin-left: ${(scope.row.categoryLevel - 1) * 20}px`" class="category-row"> | ||||
|             <i class="block-icon" v-if="scope.row.categoryLevel<3" :class="[expandData[scope.row.id] ? 'el-icon-minus' : 'el-icon-plus']" @click="toggleExpend(scope.row)"></i> | ||||
|             <span v-if="scope.row.categoryLevel>=3" style="margin-left: 20px"></span> | ||||
|             <span class="category-name">{{scope.row.categoryLevel}} {{ scope.row.permissionName }} | ||||
|               <i class="el-icon-edit" @click="showEdit(scope.row)"></i> | ||||
|             </span> | ||||
|           </a> | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|       <el-table-column | ||||
|         label="操作" | ||||
|         width="350" | ||||
|       > | ||||
|         <template slot-scope="scope"> | ||||
|   <div> | ||||
|     <el-button | ||||
|       size="small" | ||||
|       v-if="scope.row.permissionLevel<3" | ||||
|       @click="handleNewCategory(scope.row.permissionLevel+1,scope.row.id)" | ||||
|     >添加子分类</el-button> | ||||
|   </div> | ||||
| </template> | ||||
|       </el-table-column> | ||||
|     </el-table> | ||||
|     <!-- 编辑类目弹窗 --> | ||||
|     <el-dialog | ||||
|       title="编辑类目" | ||||
|       :visible.sync="visible" | ||||
|       width="30%"> | ||||
|       <el-form label-width="100px"> | ||||
|         <el-row> | ||||
|           <el-col :span="24" > | ||||
|             <el-form-item label="权限名称"> | ||||
|               <el-input v-model="editBuild.permissionName"></el-input> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="权限代码"> | ||||
|               <el-input :disabled="true" v-model="editBuild.permissionCode"></el-input> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="模块名"> | ||||
|               <el-input v-model="editBuild.module"></el-input> | ||||
|             </el-form-item> | ||||
|           </el-col> | ||||
|         </el-row> | ||||
|       </el-form> | ||||
|       <span slot="footer" class="dialog-footer"> | ||||
|         <el-button @click="visible = false">取 消</el-button> | ||||
|         <el-button type="primary" @click="update">确 定</el-button> | ||||
|       </span> | ||||
|     </el-dialog> | ||||
|     <!-- 新增类目弹窗 --> | ||||
|     <el-dialog | ||||
|       title="新增类目" | ||||
|       :visible.sync="newVisible" | ||||
|       width="50%"> | ||||
|       <el-form label-width="100px"> | ||||
|         <el-row> | ||||
|           <el-col :span="24" > | ||||
|             <el-form-item label="权限名称"> | ||||
|               <el-input v-model="firstBuild.permissionName"></el-input> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="权限代码"> | ||||
|               <el-input v-model="firstBuild.permissionCode"></el-input> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="模块名"> | ||||
|               <el-input v-model="firstBuild.module"></el-input> | ||||
|             </el-form-item> | ||||
|           </el-col> | ||||
|         </el-row> | ||||
|       </el-form> | ||||
|       <span slot="footer" class="dialog-footer"> | ||||
|         <el-button @click="cancelNewCategory">取 消</el-button> | ||||
|         <el-button type="primary" @click="setNewCategory">确 定</el-button> | ||||
|       </span> | ||||
|     </el-dialog> | ||||
|   </div> | ||||
| </el-card> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| import { getFirstLevel, getNextLevel } from '@/api/permission' | ||||
| export default { | ||||
|     name: 'coolefyCategory', | ||||
|     data() { | ||||
|         return { | ||||
|             firstBuild: { | ||||
|                 permissionName: '', | ||||
|                 module: '', | ||||
|                 permissionCode: '' | ||||
|             }, | ||||
|             editBuild: { | ||||
|                 permissionName: '', | ||||
|                 module: '', | ||||
|                 permissionCode: '' | ||||
|             }, | ||||
|             loading: false, | ||||
|             visible: false, | ||||
|             newVisible: false, | ||||
|             secondVisible: false, | ||||
|             thirdVisible: false, | ||||
|             totalRecord: 100, | ||||
|             checked: false, | ||||
|             indeterminate: false, | ||||
|             data: [], | ||||
|             list: [], | ||||
|             expandData: {}, | ||||
|             checkedData: {}, | ||||
|             indeterminateData: {}, | ||||
|             searchData: { | ||||
|                 pageSize: 10, | ||||
|                 pageNumber: 1, | ||||
|                 operateType: '', | ||||
|                 startDate: '', | ||||
|                 endDate: '' | ||||
|             }, | ||||
|             currentRow: '', | ||||
|             form: { | ||||
|                 parentId: '', | ||||
|                 id: '', | ||||
|                 cnCategoryName: '', | ||||
|                 sort: '', | ||||
|                 categoryImg: '' | ||||
|             }, | ||||
|             cnCategoryName: '', | ||||
|             cnCategoryNameTwo: '', | ||||
|             levelOne: {}, | ||||
| 
 | ||||
|             // 新建类目 | ||||
|             newCategoryType: '', | ||||
|             parentId: 0, | ||||
|             isContact: false, | ||||
|             dialogVisible: false, | ||||
|             putawayName: '', | ||||
|             putawayCategoryId: '', | ||||
|             categoryData: { categoryId: '', categoryTreePath: '' }, | ||||
|             categoryList: [], // 所有类目 | ||||
|             pageNumber: 1, | ||||
|             notPutVisible: false, | ||||
|             webCategoryId: '', | ||||
|             webPutaway: [], | ||||
|             notBelongWeb: [], | ||||
|             newPutawayWeb: [], | ||||
|             putawayCategoryName: '', | ||||
|             // 翻译 | ||||
|             isTranslate: false, | ||||
|             translateVisible: false, | ||||
|             translateCncnCategoryName: '', | ||||
|             formLabelWidth: '100px', | ||||
| 
 | ||||
|             isEdit: true, | ||||
|             webArr: [] | ||||
|         } | ||||
|     }, | ||||
|     mounted() { | ||||
|         this.search() | ||||
|     }, | ||||
|     methods: { | ||||
|         handleNewCategory(type, id) { | ||||
|             this.newVisible = true | ||||
|             this.newCategoryType = type | ||||
|             if (id) { | ||||
|                 this.parentId = id | ||||
|             } else { | ||||
|                 this.parentId = 0 | ||||
|             } | ||||
|         }, | ||||
|         setNewCategory() { | ||||
|             // 新增类目 | ||||
|             var formData = new URLSearchParams() | ||||
|             for (var key in this.firstBuild) { | ||||
|                 if (!this.firstBuild[key]) { | ||||
|                     this.$message.error('请补充完数据后再操作') | ||||
|                     return | ||||
|                 } | ||||
|                 formData.append(key, this.firstBuild[key]) | ||||
|             } | ||||
|             if (this.newCategoryType === 1) { | ||||
|                 formData.append('parentId', this.parentId) | ||||
|                 formData.append('resourceLevel', 1) | ||||
|                 formData.append('permissionLevel', 1) | ||||
|             } else if (this.newCategoryType === 2) { | ||||
|                 formData.append('parentId', this.parentId) | ||||
|                 formData.append('resourceLevel', 2) | ||||
|                 formData.append('permissionLevel', 2) | ||||
|             } else if (this.newCategoryType === 3) { | ||||
|                 formData.append('parentId', this.parentId) | ||||
|                 formData.append('resourceLevel', 3) | ||||
|                 formData.append('permissionLevel', 3) | ||||
|             } | ||||
|         }, | ||||
|         cancelNewCategory() { | ||||
|             this.newVisible = false | ||||
|             this.newCategory = this.initCategory | ||||
|         }, | ||||
|         handleSelectTwo(item) { | ||||
|             this.form.parentId = item.id | ||||
|         }, | ||||
|         handleSelect(item) { | ||||
|             this.form.parentId = item.id | ||||
|             this.cnCategoryNameTwo = '' | ||||
|             this.levelOne = item | ||||
|         }, | ||||
| 
 | ||||
|         update() { | ||||
|             var formData = new URLSearchParams() | ||||
|             for (var key in this.editBuild) { | ||||
|                 if (!this.editBuild[key]) { | ||||
|                     this.$message.error('请补充完数据后再操作') | ||||
|                     return | ||||
|                 } | ||||
|                 formData.append(key, this.editBuild[key]) | ||||
|             } | ||||
|             formData.append('parentId', this.currentRow.parentId) | ||||
|             formData.append('id', this.currentRow.id) | ||||
|             formData.append('resourceLevel', this.currentRow.permissionLevel) | ||||
|             formData.append('permissionLevel', this.currentRow.permissionLevel) | ||||
|         }, | ||||
|         showEdit(row) { | ||||
|             this.visible = true | ||||
|             this.editBuild = { | ||||
|                 permissionName: row.permissionName, | ||||
|                 module: row.module, | ||||
|                 permissionCode: row.permissionCode | ||||
|             } | ||||
|             this.currentRow = row | ||||
|         }, | ||||
|         search() { | ||||
|             getFirstLevel().then(res => { | ||||
|                 let data = res || [] | ||||
|                 data = data.map(item => { | ||||
|                     item.childList = [] | ||||
|                     item.isLoadChild = false | ||||
|                     item.cnCategoryName = item.permissionName | ||||
|                     item.categoryLevel = item.resourceLevel | ||||
|                     return item | ||||
|                 }) | ||||
|                 this.data = data | ||||
|                 this.list = data | ||||
|             }) | ||||
|         }, | ||||
|         findSourceDataById(id, list) { | ||||
|             const len = list.length | ||||
|             for (let i = 0; i < len; i += 1) { | ||||
|                 const item = list[i] | ||||
|                 if (item.id === id) { | ||||
|                     return item | ||||
|                 } | ||||
|                 if (item.childList) { | ||||
|                     const ret = this.findSourceDataById(id, item.childList) | ||||
|                     if (ret) { | ||||
|                         return ret | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         toggleExpend(d) { | ||||
|             let { data } = this | ||||
|             if (!d.isLoadChild) { | ||||
|                 getNextLevel(d.id).then(r => { | ||||
|                     r.forEach((item, index) => { | ||||
|                         item.cnCategoryName = item.permissionName | ||||
|                         item.categoryLevel = item.resourceLevel | ||||
|                     }) | ||||
|                     const childList = r || [] | ||||
|                     this.expandData[d.id] = true | ||||
|                     d.isLoadChild = true | ||||
|                     d.childList = childList | ||||
|                     this.list = this.makeFlatData(data) | ||||
|                 }) | ||||
|             } else { | ||||
|                 this.expandData[d.id] = !this.expandData[d.id] | ||||
|                 this.list = this.makeFlatData(data) | ||||
|             } | ||||
|         }, | ||||
|         makeFlatData(list, level = 0) { | ||||
|             const len = list.length | ||||
|             let data = [] | ||||
|             let i = 0 | ||||
| 
 | ||||
|             for (; i < len; i += 1) { | ||||
|                 const item = list[i] | ||||
|                 data.push(item) | ||||
|                 if (item.childList && this.expandData[item.id]) { | ||||
|                     const childList = this.makeFlatData( | ||||
|                         item.childList, | ||||
|                         level + 1 | ||||
|                     ) | ||||
|                     data = data.concat(childList) | ||||
|                 } | ||||
|             } | ||||
|             return data | ||||
|         }, | ||||
|         back() { | ||||
|             this.isContact = false | ||||
|             this.webArr = [] | ||||
|             this.webPutaway = [] | ||||
|             this.webCategoryId = '' | ||||
|         } | ||||
|     } | ||||
| } | ||||
| </script> | ||||
| <style lang="scss" scoped> | ||||
| .block-icon { | ||||
|     border: 1px solid #1ab394; | ||||
|     border-radius: 2px; | ||||
|     color: #1ab394; | ||||
|     display: inline-block; | ||||
|     height: 14px; | ||||
|     line-height: 14px; | ||||
|     text-align: center; | ||||
|     vertical-align: middle; | ||||
|     width: 14px; | ||||
| } | ||||
| .category-row { | ||||
|     display: inline-block; | ||||
|     width: 100%; | ||||
|     i.el-icon-edit { | ||||
|         color: #1ab394; | ||||
|         display: none; | ||||
|         margin-left: 20px; | ||||
|         font-size: 16px; | ||||
|     } | ||||
|     &:hover { | ||||
|         i { | ||||
|             display: inline-block; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| .putaway-header { | ||||
|     font-size: 14px; | ||||
|     color: #606266; | ||||
|     margin-bottom: 10px; | ||||
| } | ||||
| .el-tag { | ||||
|     margin-left: 10px; | ||||
|     margin-bottom: 10px; | ||||
| } | ||||
| .button-new-tag { | ||||
|     margin-left: 10px; | ||||
|     height: 32px; | ||||
|     line-height: 30px; | ||||
|     padding-top: 0; | ||||
|     padding-bottom: 0; | ||||
| } | ||||
| .input-new-tag { | ||||
|     width: 90px; | ||||
|     margin-left: 10px; | ||||
|     vertical-align: bottom; | ||||
| } | ||||
| 
 | ||||
| .header-title { | ||||
|     margin-bottom: 10px; | ||||
|     color: #606266; | ||||
| } | ||||
| 
 | ||||
| .not-belong-Web { | ||||
|     min-height: 450px; | ||||
| } | ||||
| .category-item { | ||||
|     display: inline-block; | ||||
|     min-width: 220px; | ||||
|     vertical-align: top; | ||||
|     .title { | ||||
|         text-align: center; | ||||
|         font-size: 16px; | ||||
|         line-height: 35px; | ||||
|     } | ||||
|     .content { | ||||
|         width: 100%; | ||||
|         border: 1px solid #888; | ||||
|         height: 750px; | ||||
|         box-sizing: border-box; | ||||
|         overflow-y: hidden; | ||||
|         padding: 10px 0; | ||||
|         border-radius: 3px; | ||||
|     } | ||||
| } | ||||
| .category-list { | ||||
|     border: 1px solid #ccc; | ||||
|     height: 100%; | ||||
|     box-sizing: border-box; | ||||
|     overflow-y: auto; | ||||
|     width: 200px; | ||||
|     display: inline-block; | ||||
|     margin: 0 5px; | ||||
|     font-size: 0; | ||||
|     li { | ||||
|         padding: 5px; | ||||
|         width: 100%; | ||||
|         font-size: 14px; | ||||
|         box-sizing: border-box; | ||||
|         cursor: pointer; | ||||
|         i { | ||||
|             float: right; | ||||
|         } | ||||
|         &.active { | ||||
|             background: #58b7ff; | ||||
|             color: #fff !important; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| .colorBlue { | ||||
|     color: #58b7ff; | ||||
| } | ||||
| .translate-item { | ||||
|     margin-bottom: 10px; | ||||
|     .left { | ||||
|         display: inline-block; | ||||
|         margin-right: 10px; | ||||
|         width: 80px; | ||||
|         text-align: right; | ||||
|     } | ||||
|     .right { | ||||
|         display: inline-block; | ||||
|     } | ||||
|     .category-word { | ||||
|         display: inline-block; | ||||
|         margin-right: 10px; | ||||
|     } | ||||
| } | ||||
| </style> | ||||
| <style> | ||||
| .bgc-yellow input { | ||||
|     background-color: #fff000; | ||||
| } | ||||
| </style> | ||||
| @ -0,0 +1,149 @@ | ||||
| /* 订单管理 */ | ||||
| const Order = () => import('@/pages/order-manage') | ||||
| const OrderList = () => import('@/pages/order-manage/order-list') | ||||
| const ProductManage = () => import('@/pages/order-manage/product-manage') | ||||
| const ProductionList = () => | ||||
|     import('@/pages/order-manage/product-manage/production-list') | ||||
| const ReviewManage = () => | ||||
|     import('@/pages/order-manage/product-manage/review-manage') | ||||
| const ReturnGoods = () => import('@/pages/order-manage/return-goods') | ||||
| 
 | ||||
| /* 产品管理 */ | ||||
| const Goods = () => import('@/pages/goods-manage') | ||||
| const GoodsList = () => import('@/pages/goods-manage/goods-list') | ||||
| const GoodsClassify = () => import('@/pages/goods-manage/goods-classify') | ||||
| // 权限管理
 | ||||
| const Permission = () => import('@/pages/permission') | ||||
| const UserManage = () => import('@/pages/permission/user-manage') | ||||
| const RoleManage = () => import('@/pages/permission/role-manage') | ||||
| const MenuManage = () => import('@/pages/permission/menu-manage') | ||||
| /* 需要权限判断的路由 */ | ||||
| const dynamicRoutes = [ | ||||
|     { | ||||
|         path: '/order', | ||||
|         component: Order, | ||||
|         name: 'order-manage', | ||||
|         meta: { | ||||
|             name: '订单管理', | ||||
|             icon: 'example' | ||||
|         }, | ||||
|         children: [ | ||||
|             { | ||||
|                 path: 'list', | ||||
|                 name: 'order-list', | ||||
|                 component: OrderList, | ||||
|                 meta: { | ||||
|                     name: '订单列表', | ||||
|                     icon: 'table' | ||||
|                 } | ||||
|             }, | ||||
|             { | ||||
|                 path: 'product', | ||||
|                 name: 'product-manage', | ||||
|                 component: ProductManage, | ||||
|                 meta: { | ||||
|                     name: '生产管理', | ||||
|                     icon: 'user' | ||||
|                 }, | ||||
|                 children: [ | ||||
|                     { | ||||
|                         path: 'list', | ||||
|                         name: 'product-list', | ||||
|                         component: ProductionList, | ||||
|                         meta: { | ||||
|                             name: '生产列表', | ||||
|                             icon: 'table' | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         path: 'review', | ||||
|                         name: 'review-manage', | ||||
|                         component: ReviewManage, | ||||
|                         meta: { | ||||
|                             name: '审核管理', | ||||
|                             icon: 'eye' | ||||
|                         } | ||||
|                     } | ||||
|                 ] | ||||
|             }, | ||||
|             { | ||||
|                 path: 'returnGoods', | ||||
|                 name: 'return-goods', | ||||
|                 component: ReturnGoods, | ||||
|                 meta: { | ||||
|                     name: '退货管理', | ||||
|                     icon: 'nested' | ||||
|                 } | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         path: '/goods', | ||||
|         component: Goods, | ||||
|         name: 'goods', | ||||
|         meta: { | ||||
|             name: '产品管理', | ||||
|             icon: 'user' | ||||
|         }, | ||||
|         children: [ | ||||
|             { | ||||
|                 path: 'list', | ||||
|                 name: 'goods-list', | ||||
|                 component: GoodsList, | ||||
|                 meta: { | ||||
|                     name: '产品列表', | ||||
|                     icon: 'table' | ||||
|                 } | ||||
|             }, | ||||
|             { | ||||
|                 path: 'classify', | ||||
|                 name: 'goods-classify', | ||||
|                 component: GoodsClassify, | ||||
|                 meta: { | ||||
|                     name: '产品分类', | ||||
|                     icon: 'tree' | ||||
|                 } | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         path: '/permission', | ||||
|         component: Permission, | ||||
|         name: 'permission', | ||||
|         meta: { | ||||
|             name: '权限管理', | ||||
|             icon: 'table' | ||||
|         }, | ||||
|         children: [ | ||||
|             { | ||||
|                 path: 'user', | ||||
|                 name: 'user-manage', | ||||
|                 component: UserManage, | ||||
|                 meta: { | ||||
|                     name: '用户管理', | ||||
|                     icon: 'table' | ||||
|                 } | ||||
|             }, | ||||
|             { | ||||
|                 path: 'role', | ||||
|                 name: 'role-manage', | ||||
|                 component: RoleManage, | ||||
|                 meta: { | ||||
|                     name: '角色管理', | ||||
|                     icon: 'eye' | ||||
|                 } | ||||
|             }, | ||||
|             { | ||||
|                 path: 'menu', | ||||
|                 name: 'menu-manage', | ||||
|                 component: MenuManage, | ||||
|                 meta: { | ||||
|                     name: '菜单管理', | ||||
|                     icon: 'tree' | ||||
|                 } | ||||
|             } | ||||
|         ] | ||||
|     } | ||||
| ] | ||||
| 
 | ||||
| export default dynamicRoutes | ||||
| @ -0,0 +1,54 @@ | ||||
| import Vue from 'vue' | ||||
| import Router from 'vue-router' | ||||
| 
 | ||||
| import Login from '@/pages/login/login' | ||||
| import NotFound from '@/pages/errorPage/404' | ||||
| import Forbidden from '@/pages/errorPage/403' | ||||
| import Layout from '@/pages/layout/index' | ||||
| import Home from '@/pages/home/index' | ||||
| 
 | ||||
| Vue.use(Router) | ||||
| 
 | ||||
| /* 初始路由 */ | ||||
| export default new Router({ | ||||
|     routes: [ | ||||
|         { | ||||
|             path: '/login', | ||||
|             component: Login | ||||
|         } | ||||
|     ] | ||||
| }) | ||||
| 
 | ||||
| /* 准备动态添加的路由 */ | ||||
| export const DynamicRoutes = [ | ||||
|     { | ||||
|         path: '', | ||||
|         component: Layout, | ||||
|         name: 'container', | ||||
|         redirect: 'home', | ||||
|         meta: { | ||||
|             requiresAuth: true, | ||||
|             name: '首页' | ||||
|         }, | ||||
|         children: [ | ||||
|             { | ||||
|                 id: 1, | ||||
|                 path: 'home', | ||||
|                 component: Home, | ||||
|                 name: 'home', | ||||
|                 meta: { | ||||
|                     name: '首页', | ||||
|                     icon: 'tree' | ||||
|                 } | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         path: '/403', | ||||
|         component: Forbidden | ||||
|     }, | ||||
|     { | ||||
|         path: '*', | ||||
|         component: NotFound | ||||
|     } | ||||
| ] | ||||
| @ -0,0 +1,3 @@ | ||||
| export default { | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,3 @@ | ||||
| export default { | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,18 @@ | ||||
| import Vue from 'vue' | ||||
| import Vuex from 'vuex' | ||||
| 
 | ||||
| import state from './state' | ||||
| import getters from './getters' | ||||
| import modules from './modules' | ||||
| import actions from './actions' | ||||
| import mutations from './mutations' | ||||
| 
 | ||||
| Vue.use(Vuex) | ||||
| 
 | ||||
| export default new Vuex.Store({ | ||||
|     state, | ||||
|     getters, | ||||
|     mutations, | ||||
|     actions, | ||||
|     modules | ||||
| }) | ||||
| @ -0,0 +1,5 @@ | ||||
| import permission from './modules/permission' | ||||
| 
 | ||||
| export default { | ||||
|     permission | ||||
| } | ||||
| @ -0,0 +1,59 @@ | ||||
| import { fetchPermission } from '@/api/permission' | ||||
| import router, { DynamicRoutes } from '@/router/index' | ||||
| import { recursionRouter } from '@/utils/recursion-router' | ||||
| import dynamicRouter from '@/router/dynamic-router' | ||||
| 
 | ||||
| export default { | ||||
|     namespaced: true, | ||||
|     state: { | ||||
|         permissionList: null /** 所有路由 */, | ||||
|         sidebarMenu: [] /** 导航菜单 */, | ||||
|         currentMenu: '' /** 当前active导航菜单 */, | ||||
|         control_list: [] /** 完整的权限列表 */, | ||||
|         avatar: ''/** 头像 */, | ||||
|         account: ''/** 用户角色 */ | ||||
|     }, | ||||
|     getters: {}, | ||||
|     mutations: { | ||||
|         SET_AVATAR(state, avatar) { | ||||
|             state.avatar = avatar | ||||
|         }, | ||||
|         SET_ACCOUNT(state, account) { | ||||
|             state.account = account | ||||
|         }, | ||||
|         SET_PERMISSION(state, routes) { | ||||
|             state.permissionList = routes | ||||
|         }, | ||||
|         CLEAR_PERMISSION(state) { | ||||
|             state.permissionList = null | ||||
|         }, | ||||
|         SET_MENU(state, menu) { | ||||
|             state.sidebarMenu = menu | ||||
|         }, | ||||
|         CLEAR_MENU(state) { | ||||
|             state.sidebarMenu = [] | ||||
|         }, | ||||
|         SET_CURRENT_MENU(state, currentMenu) { | ||||
|             state.currentMenu = currentMenu | ||||
|         }, | ||||
|         SET_CONTROL_LIST(state, list) { | ||||
|             state.control_list = list | ||||
|         } | ||||
|     }, | ||||
|     actions: { | ||||
|         async FETCH_PERMISSION({ commit, state }) { | ||||
|             let permissionList = await fetchPermission() | ||||
|             commit('SET_AVATAR', permissionList.avatar) | ||||
|             commit('SET_ACCOUNT', permissionList.name) | ||||
|             let routes = recursionRouter(permissionList.data, dynamicRouter) | ||||
|             let MainContainer = DynamicRoutes.find(v => v.path === '') | ||||
|             let children = MainContainer.children | ||||
|             commit('SET_CONTROL_LIST', [...children, ...dynamicRouter]) | ||||
|             children.push(...routes) | ||||
|             commit('SET_MENU', children) | ||||
|             let initialRoutes = router.options.routes | ||||
|             router.addRoutes(DynamicRoutes) | ||||
|             commit('SET_PERMISSION', [...initialRoutes, ...DynamicRoutes]) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,14 @@ | ||||
| export default { | ||||
|     LOGIN_IN(state, token) { | ||||
|         state.UserToken = token | ||||
|     }, | ||||
|     LOGIN_OUT(state) { | ||||
|         state.UserToken = '' | ||||
|     }, | ||||
|     toggleNavCollapse(state) { | ||||
|         state.isSidebarNavCollapse = !state.isSidebarNavCollapse | ||||
|     }, | ||||
|     setCrumbList(state, list) { | ||||
|         state.crumbList = list | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,12 @@ | ||||
| export default { | ||||
|     get UserToken() { | ||||
|         return localStorage.getItem('token') | ||||
|     }, | ||||
|     set UserToken(value) { | ||||
|         localStorage.setItem('token', value) | ||||
|     }, | ||||
|     /* 导航菜单是否折叠 */ | ||||
|     isSidebarNavCollapse: false, | ||||
|     /* 面包屑导航列表 */ | ||||
|     crumbList: [] | ||||
| } | ||||
| @ -0,0 +1,39 @@ | ||||
| $mainColor:#151519; | ||||
| 
 | ||||
| @mixin table-center { | ||||
|     display: table-cell; | ||||
|     vertical-align: middle; | ||||
|     text-align: center; | ||||
| } | ||||
| 
 | ||||
| @mixin poa-center($w, $h) { | ||||
|     position: absolute; | ||||
|     width: $w; | ||||
|     height: $h; | ||||
|     left: 50%; | ||||
|     top: 50%; | ||||
|     transition: translate(-50%, -50%) | ||||
| } | ||||
| 
 | ||||
| @mixin flex-center { | ||||
|     display: flex; | ||||
|     justify-content: center; | ||||
|     align-items: center; | ||||
| } | ||||
| 
 | ||||
| @mixin t-overflow($line:1) { | ||||
|     @if $line==1 { | ||||
|         overflow: hidden; | ||||
|         text-overflow: ellipsis; | ||||
|         white-space: nowrap; | ||||
|     } | ||||
|     @else { | ||||
|         display: -webkit-box; | ||||
|         -webkit-line-clamp: $line; | ||||
|         -webkit-box-orient: vertical; | ||||
|         overflow: hidden; | ||||
|         text-overflow: ellipsis; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| @ -0,0 +1,171 @@ | ||||
| @charset "utf-8"; | ||||
| 
 | ||||
| html { | ||||
| 
 | ||||
|   color: #000; | ||||
|   background: #fff; | ||||
|   overflow-y: scroll; | ||||
|   -webkit-text-size-adjust: 100%; | ||||
|   -ms-text-size-adjust: 100%; | ||||
| } | ||||
| 
 | ||||
| html * { | ||||
|   outline: none; | ||||
|   -webkit-text-size-adjust: none; | ||||
|   -webkit-tap-highlight-color: rgba(0, 0, 0, 0); | ||||
|   box-sizing: border-box; | ||||
| } | ||||
| 
 | ||||
| html, | ||||
| body { | ||||
|   font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif; | ||||
|   height: 100%; | ||||
|   width: 100%;  | ||||
|   overflow: auto; | ||||
| } | ||||
| 
 | ||||
| body, | ||||
| div, | ||||
| dl, | ||||
| dt, | ||||
| dd, | ||||
| ul, | ||||
| ol, | ||||
| li, | ||||
| h1, | ||||
| h2, | ||||
| h3, | ||||
| h4, | ||||
| h5, | ||||
| h6, | ||||
| pre, | ||||
| code, | ||||
| form, | ||||
| fieldset, | ||||
| legend, | ||||
| input, | ||||
| textarea, | ||||
| p, | ||||
| blockquote, | ||||
| th, | ||||
| td, | ||||
| hr, | ||||
| button, | ||||
| article, | ||||
| aside, | ||||
| details, | ||||
| figcaption, | ||||
| figure, | ||||
| footer, | ||||
| header, | ||||
| hgroup, | ||||
| menu, | ||||
| nav, | ||||
| section { | ||||
|   margin: 0; | ||||
|   padding: 0; | ||||
| } | ||||
| 
 | ||||
| input, | ||||
| select, | ||||
| textarea { | ||||
|   font-size: 100%; | ||||
| } | ||||
| 
 | ||||
| table { | ||||
|   border-collapse: collapse; | ||||
|   border-spacing: 0; | ||||
| } | ||||
| 
 | ||||
| fieldset, | ||||
| img { | ||||
|   border: 0; | ||||
| } | ||||
| 
 | ||||
| abbr, | ||||
| acronym { | ||||
|   border: 0; | ||||
|   font-variant: normal; | ||||
| } | ||||
| 
 | ||||
| del { | ||||
|   text-decoration: line-through; | ||||
| } | ||||
| 
 | ||||
| address, | ||||
| caption, | ||||
| cite, | ||||
| code, | ||||
| dfn, | ||||
| em, | ||||
| th, | ||||
| i, | ||||
| var { | ||||
|   font-style: normal; | ||||
|   font-weight: 500; | ||||
| } | ||||
| 
 | ||||
| ol, | ||||
| ul { | ||||
|   list-style: none; | ||||
| } | ||||
| 
 | ||||
| caption, | ||||
| th { | ||||
|   text-align: left; | ||||
| } | ||||
| 
 | ||||
| h1, | ||||
| h2, | ||||
| h3, | ||||
| h4, | ||||
| h5, | ||||
| h6 { | ||||
|   font-size: 100%; | ||||
|   font-weight: 500; | ||||
| } | ||||
| 
 | ||||
| q:before, | ||||
| q:after { | ||||
|   content: ''; | ||||
| } | ||||
| 
 | ||||
| sub, | ||||
| sup { | ||||
|   font-size: 75%; | ||||
|   line-height: 0; | ||||
|   position: relative; | ||||
|   vertical-align: baseline; | ||||
| } | ||||
| 
 | ||||
| sup { | ||||
|   top: -0.5em; | ||||
| } | ||||
| 
 | ||||
| sub { | ||||
|   bottom: -0.25em; | ||||
| } | ||||
| 
 | ||||
| a:hover { | ||||
|   text-decoration: underline; | ||||
| } | ||||
| 
 | ||||
| ins, | ||||
| a, | ||||
| a:active, | ||||
| a:visited, | ||||
| a:link { | ||||
|   text-decoration: none; | ||||
| } | ||||
| 
 | ||||
| .clearfix { | ||||
|   &:after { | ||||
|     display: table; | ||||
|     clear: both; | ||||
|     content: ""; | ||||
|     visibility: hidden; | ||||
|     ; | ||||
|     height: 0; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,81 @@ | ||||
| .el-menu { | ||||
|     border: none; | ||||
|     .iconfont { | ||||
|         color: #fff; | ||||
|         font-size: 16px; | ||||
|     } | ||||
|      .el-submenu__title { | ||||
|         &:hover { | ||||
|             background: none !important; | ||||
|         } | ||||
|         i.el-submenu__icon-arrow { | ||||
|             color: #ddd; | ||||
|             font-size: 15px; | ||||
|         } | ||||
|     }  | ||||
|     .el-menu-item-group__title { | ||||
|         padding: 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| .el-breadcrumb { | ||||
|     display: inline-block; | ||||
|     vertical-align: middle; | ||||
|     font-size: 14px; | ||||
|     margin-left: 5px; | ||||
|     .el-breadcrumb__inner { | ||||
|         &.is-link { | ||||
|             display: inline-block; | ||||
|             font-weight: normal; | ||||
|             color: #424040 !important; | ||||
|         } | ||||
|     } | ||||
|     .is-last-link .is-link { | ||||
|         font-weight: normal; | ||||
|         color: #999 !important; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|   | ||||
|   .sidebar-container { | ||||
|     transition: width .28s; | ||||
|     width: 180px ; | ||||
|     height: 100%; | ||||
|     position: fixed; | ||||
|     top: 0; | ||||
|     bottom: 0; | ||||
|     left: 0; | ||||
|     z-index: 1001; | ||||
|     background: rgba(0,0,0,0.5) | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|    | ||||
|     .sidebar-container.navCollapsed { | ||||
|       width: 63px ; | ||||
|     } | ||||
| 
 | ||||
|     .navCollapsed{ | ||||
|          .el-submenu { | ||||
|             &>.el-submenu__title { | ||||
|                 &>span { | ||||
|                 display: none; | ||||
|                 } | ||||
|                 .el-submenu__icon-arrow { | ||||
|                 display: none; | ||||
|                 } | ||||
|             } | ||||
|             } | ||||
|    | ||||
|     } | ||||
|     | ||||
| 
 | ||||
| @ -0,0 +1,66 @@ | ||||
| /* 侧边栏 */ | ||||
| .sidebar { | ||||
|     width: 200px !important; | ||||
|     height: 100%; | ||||
|     background: #304156; | ||||
|     transition: all 0.25s; | ||||
|     position: fixed; | ||||
|     top: 0; | ||||
|     bottom: 0; | ||||
|     left: 0; | ||||
|     z-index: 100; | ||||
|     overflow-x: hidden; | ||||
|     .iconfont { | ||||
|         margin-right: 8px; | ||||
|         color: #fff; | ||||
|         font-size: 18px; | ||||
|     } | ||||
|     >div.menu-container>li.el-menu-item, | ||||
|     >div.menu-container>li.el-submenu>.el-submenu__title { | ||||
|         border-bottom: 1px solid rgba(238, 238, 238, 0.1); | ||||
|     } | ||||
|     .el-menu-item { | ||||
|         background: #304156 !important; | ||||
|     } | ||||
|     /* 菜单hover时的背景 */ | ||||
|     .el-submenu__title:hover, | ||||
|     .el-menu-item:hover { | ||||
|         background: #223041 !important; | ||||
|     } | ||||
|     /* 菜单active时的背景 */ | ||||
|     /* .el-menu-item.is-active { | ||||
|         background: #293748 !important; | ||||
|     } */ | ||||
| } | ||||
| 
 | ||||
| /* 主体内容 */ | ||||
| .main-container { | ||||
|     min-height: 100%; | ||||
|     margin-left: 200px; | ||||
|     transition: margin-left 0.25s; | ||||
|     position: relative; | ||||
|     box-sizing: border-box; | ||||
|     padding: 60px 20px 0 20px; | ||||
| } | ||||
| 
 | ||||
| /* 折叠菜单下的样式 */ | ||||
| .navCollapsed { | ||||
|     .sidebar { | ||||
|         width: 64px !important; | ||||
|         ul { | ||||
|             display: none; | ||||
|         } | ||||
|         .iconfont+span { | ||||
|             display: none; | ||||
|         } | ||||
|         .el-submenu__icon-arrow { | ||||
|             display: none; | ||||
|         } | ||||
|     } | ||||
|     .main-container { | ||||
|         margin-left: 64px; | ||||
|     } | ||||
|     .aside__top{ | ||||
|         left:64px!important; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,5 @@ | ||||
| @import './normalize.scss'; | ||||
| @import './reset_element.scss'; | ||||
| @import './sidebar.scss'; | ||||
| @import './loading.scss'; | ||||
| @import url('//at.alicdn.com/t/font_641452_q3ah7ae4qvndn29.css'); | ||||
| @ -0,0 +1,35 @@ | ||||
| .ajax-loading { | ||||
|     display: none; | ||||
|     .loading { | ||||
|         position: fixed; | ||||
|         top: 50%; | ||||
|         left: 50%; | ||||
|         transform: translate(-50%, -50%); | ||||
|         padding: 0 40px; | ||||
|         height: 80px; | ||||
|         line-height: 80px; | ||||
|         background: rgba(0, 0, 0, 0.75); | ||||
|         border-radius: 6px; | ||||
|         text-align: center; | ||||
|         z-index: 9999; | ||||
|         font-size: 16px; | ||||
|         color: #fff; | ||||
|         img { | ||||
|             width: 32px; | ||||
|             vertical-align: middle; | ||||
|         } | ||||
|         span { | ||||
|             margin-left: 12px; | ||||
|         } | ||||
|     } | ||||
|     .overlay { | ||||
|         position: fixed; | ||||
|         left: 0; | ||||
|         right: 0; | ||||
|         top: 0; | ||||
|         bottom: 0; | ||||
|         z-index: 9998; | ||||
|         background: rgb(255, 255, 255); | ||||
|         opacity: 0.1; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,33 @@ | ||||
| /** | ||||
|  * | ||||
|  * @param  {Array} userRouter 后台返回的用户权限json | ||||
|  * @param  {Array} allRouter  前端配置好的所有动态路由的集合 | ||||
|  * @return {Array} realRoutes 过滤后的路由 | ||||
|  */ | ||||
| 
 | ||||
| export function recursionRouter(userRouter = [], allRouter = []) { | ||||
|     var realRoutes = allRouter | ||||
|         .filter(item => userRouter.includes(item.name)) | ||||
|         .map(item => ({ | ||||
|             ...item, | ||||
|             children: item.children | ||||
|                 ? recursionRouter(userRouter, item.children) | ||||
|                 : null | ||||
|         })) | ||||
|     return realRoutes | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * | ||||
|  * @param {Array} routes 用户过滤后的路由 | ||||
|  * | ||||
|  * 递归为所有有子路由的路由设置第一个children.path为默认路由 | ||||
|  */ | ||||
| export function setDefaultRoute(routes) { | ||||
|     routes.forEach((v, i) => { | ||||
|         if (v.children && v.children.length > 0) { | ||||
|             v.redirect = { name: v.children[0].name } | ||||
|             setDefaultRoute(v.children) | ||||
|         } | ||||
|     }) | ||||
| } | ||||
| @ -0,0 +1,5 @@ | ||||
| module.exports = { | ||||
|   env: { | ||||
|     jest: true | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,12 @@ | ||||
| import { shallowMount } from '@vue/test-utils' | ||||
| import HelloWorld from '@/components/HelloWorld.vue' | ||||
| 
 | ||||
| describe('HelloWorld.vue', () => { | ||||
|   it('renders props.msg when passed', () => { | ||||
|     const msg = 'new message' | ||||
|     const wrapper = shallowMount(HelloWorld, { | ||||
|       propsData: { msg } | ||||
|     }) | ||||
|     expect(wrapper.text()).toMatch(msg) | ||||
|   }) | ||||
| }) | ||||
| @ -0,0 +1,40 @@ | ||||
| const path = require('path') | ||||
| const webpack = require('webpack') | ||||
| const CleanWebpackPlugin = require('clean-webpack-plugin') | ||||
| 
 | ||||
| // dll文件存放的目录
 | ||||
| const dllPath = 'public/vendor' | ||||
| 
 | ||||
| module.exports = { | ||||
|     entry: { | ||||
|         // 需要提取的库文件
 | ||||
|         vendor: ['vue', 'vue-router', 'vuex', 'axios', 'element-ui'] | ||||
|     }, | ||||
|     output: { | ||||
|         path: path.join(__dirname, dllPath), | ||||
|         // 保证每次打包出来的文件名都是唯一的
 | ||||
|         filename: `[name].dll.${Math.ceil(Math.random() * 10000)}.js`, | ||||
|         // vendor.dll.js中暴露出的全局变量名
 | ||||
|         // 保持与 webpack.DllPlugin 中名称一致
 | ||||
|         library: '[name]_[hash]' | ||||
|     }, | ||||
|     plugins: [ | ||||
|         // 清除之前的dll文件
 | ||||
|         new CleanWebpackPlugin(['*.*'], { | ||||
|             root: path.join(__dirname, dllPath) | ||||
|         }), | ||||
|         // 设置环境变量
 | ||||
|         new webpack.DefinePlugin({ | ||||
|             'process.env': { | ||||
|                 NODE_ENV: 'production' | ||||
|             } | ||||
|         }), | ||||
|         // manifest.json 描述动态链接库包含了哪些内容
 | ||||
|         new webpack.DllPlugin({ | ||||
|             path: path.join(__dirname, dllPath, '[name]-manifest.json'), | ||||
|             // 保持与 output.library 中名称一致
 | ||||
|             name: '[name]_[hash]', | ||||
|             context: process.cwd() | ||||
|         }) | ||||
|     ] | ||||
| } | ||||
| @ -1,21 +0,0 @@ | ||||
| MIT License | ||||
| 
 | ||||
| Copyright (c) 2016-2023 vue-manage-system | ||||
| 
 | ||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| of this software and associated documentation files (the "Software"), to deal | ||||
| in the Software without restriction, including without limitation the rights | ||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| copies of the Software, and to permit persons to whom the Software is | ||||
| furnished to do so, subject to the following conditions: | ||||
| 
 | ||||
| The above copyright notice and this permission notice shall be included in all | ||||
| copies or substantial portions of the Software. | ||||
| 
 | ||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
| SOFTWARE. | ||||
| @ -1,5 +0,0 @@ | ||||
| // Generated by 'unplugin-auto-import'
 | ||||
| export {} | ||||
| declare global { | ||||
| 
 | ||||
| } | ||||
| @ -1,53 +0,0 @@ | ||||
| // generated by unplugin-vue-components
 | ||||
| // We suggest you to commit this file into source control
 | ||||
| // Read more: https://github.com/vuejs/core/pull/3399
 | ||||
| import '@vue/runtime-core' | ||||
| 
 | ||||
| export {} | ||||
| 
 | ||||
| declare module '@vue/runtime-core' { | ||||
|   export interface GlobalComponents { | ||||
|     ElAvatar: typeof import('element-plus/es')['ElAvatar'] | ||||
|     ElButton: typeof import('element-plus/es')['ElButton'] | ||||
|     ElCard: typeof import('element-plus/es')['ElCard'] | ||||
|     ElCascader: typeof import('element-plus/es')['ElCascader'] | ||||
|     ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] | ||||
|     ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup'] | ||||
|     ElCol: typeof import('element-plus/es')['ElCol'] | ||||
|     ElDatePicker: typeof import('element-plus/es')['ElDatePicker'] | ||||
|     ElDialog: typeof import('element-plus/es')['ElDialog'] | ||||
|     ElDropdown: typeof import('element-plus/es')['ElDropdown'] | ||||
|     ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem'] | ||||
|     ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu'] | ||||
|     ElForm: typeof import('element-plus/es')['ElForm'] | ||||
|     ElFormItem: typeof import('element-plus/es')['ElFormItem'] | ||||
|     ElIcon: typeof import('element-plus/es')['ElIcon'] | ||||
|     ElImage: typeof import('element-plus/es')['ElImage'] | ||||
|     ElInput: typeof import('element-plus/es')['ElInput'] | ||||
|     ElLink: typeof import('element-plus/es')['ElLink'] | ||||
|     ElMenu: typeof import('element-plus/es')['ElMenu'] | ||||
|     ElMenuItem: typeof import('element-plus/es')['ElMenuItem'] | ||||
|     ElOption: typeof import('element-plus/es')['ElOption'] | ||||
|     ElPagination: typeof import('element-plus/es')['ElPagination'] | ||||
|     ElProgress: typeof import('element-plus/es')['ElProgress'] | ||||
|     ElRadio: typeof import('element-plus/es')['ElRadio'] | ||||
|     ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] | ||||
|     ElRow: typeof import('element-plus/es')['ElRow'] | ||||
|     ElSelect: typeof import('element-plus/es')['ElSelect'] | ||||
|     ElSubMenu: typeof import('element-plus/es')['ElSubMenu'] | ||||
|     ElSwitch: typeof import('element-plus/es')['ElSwitch'] | ||||
|     ElTable: typeof import('element-plus/es')['ElTable'] | ||||
|     ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] | ||||
|     ElTabPane: typeof import('element-plus/es')['ElTabPane'] | ||||
|     ElTabs: typeof import('element-plus/es')['ElTabs'] | ||||
|     ElTag: typeof import('element-plus/es')['ElTag'] | ||||
|     ElTimePicker: typeof import('element-plus/es')['ElTimePicker'] | ||||
|     ElTooltip: typeof import('element-plus/es')['ElTooltip'] | ||||
|     ElUpload: typeof import('element-plus/es')['ElUpload'] | ||||
|     Header: typeof import('./src/components/header.vue')['default'] | ||||
|     RouterLink: typeof import('vue-router')['RouterLink'] | ||||
|     RouterView: typeof import('vue-router')['RouterView'] | ||||
|     Sidebar: typeof import('./src/components/sidebar.vue')['default'] | ||||
|     Tags: typeof import('./src/components/tags.vue')['default'] | ||||
|   } | ||||
| } | ||||
| @ -1,22 +0,0 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang=""> | ||||
| 
 | ||||
| <head> | ||||
|   <meta charset="utf-8"> | ||||
|   <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||||
|   <meta name="viewport" content="width=device-width,initial-scale=1.0"> | ||||
|   <title>vue-manage-system</title> | ||||
|   <link rel="stylesheet" href="https://at.alicdn.com/t/font_830376_qzecyukz0s.css"> | ||||
| </head> | ||||
| 
 | ||||
| <body> | ||||
|   <noscript> | ||||
|     <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. | ||||
|       Please enable it to continue.</strong> | ||||
|   </noscript> | ||||
|   <div id="app"></div> | ||||
|   <script type="module" src="/src/main.ts"></script> | ||||
|   <!-- built files will be auto injected --> | ||||
| </body> | ||||
| 
 | ||||
| </html> | ||||
| @ -1,38 +0,0 @@ | ||||
| { | ||||
| 	"name": "vue-manage-system", | ||||
| 	"version": "5.3.0", | ||||
| 	"private": true, | ||||
| 	"scripts": { | ||||
| 		"dev": "vite", | ||||
| 		"build": "vue-tsc --noEmit && vite build", | ||||
| 		"serve": "vite preview" | ||||
| 	}, | ||||
| 	"dependencies": { | ||||
| 		"@element-plus/icons-vue": "^2.0.9", | ||||
| 		"axios": "^0.27.2", | ||||
| 		"element-plus": "^2.2.14", | ||||
| 		"md-editor-v3": "^2.2.1", | ||||
| 		"pinia": "^2.0.20", | ||||
| 		"vue": "^3.2.37", | ||||
| 		"vue-cropperjs": "^5.0.0", | ||||
| 		"vue-router": "^4.1.3", | ||||
| 		"vue-schart": "^2.0.0", | ||||
| 		"wangeditor": "^4.7.15", | ||||
| 		"xlsx": "^0.18.5" | ||||
| 	}, | ||||
| 	"devDependencies": { | ||||
| 		"@vitejs/plugin-vue": "^3.0.0", | ||||
| 		"@vue/compiler-sfc": "^3.1.2", | ||||
| 		"typescript": "^4.6.4", | ||||
| 		"unplugin-auto-import": "^0.11.2", | ||||
| 		"unplugin-vue-components": "^0.22.4", | ||||
| 		"vite": "^3.0.0", | ||||
| 		"vite-plugin-vue-setup-extend": "^0.4.0", | ||||
| 		"vue-tsc": "^0.38.4" | ||||
| 	}, | ||||
| 	"browserslist": [ | ||||
| 		"> 1%", | ||||
| 		"last 2 versions", | ||||
| 		"not dead" | ||||
| 	] | ||||
| } | ||||
| @ -1,40 +0,0 @@ | ||||
| { | ||||
|     "list": [{ | ||||
|             "id": 1, | ||||
|             "name": "张三", | ||||
|             "money": 123, | ||||
|             "address": "广东省东莞市长安镇", | ||||
|             "state": "成功", | ||||
|             "date": "2019-11-1", | ||||
|             "thumb": "https://lin-xin.gitee.io/images/post/wms.png" | ||||
|         }, | ||||
|         { | ||||
|             "id": 2, | ||||
|             "name": "李四", | ||||
|             "money": 456, | ||||
|             "address": "广东省广州市白云区", | ||||
|             "state": "成功", | ||||
|             "date": "2019-10-11", | ||||
|             "thumb": "https://lin-xin.gitee.io/images/post/node3.png" | ||||
|         }, | ||||
|         { | ||||
|             "id": 3, | ||||
|             "name": "王五", | ||||
|             "money": 789, | ||||
|             "address": "湖南省长沙市", | ||||
|             "state": "失败", | ||||
|             "date": "2019-11-11", | ||||
|             "thumb": "https://lin-xin.gitee.io/images/post/parcel.png" | ||||
|         }, | ||||
|         { | ||||
|             "id": 4, | ||||
|             "name": "赵六", | ||||
|             "money": 1011, | ||||
|             "address": "福建省厦门市鼓浪屿", | ||||
|             "state": "成功", | ||||
|             "date": "2019-10-20", | ||||
|             "thumb": "https://lin-xin.gitee.io/images/post/notice.png" | ||||
|         } | ||||
|     ], | ||||
|     "pageTotal": 4 | ||||
| } | ||||
| @ -1,14 +0,0 @@ | ||||
| <template> | ||||
|     <el-config-provider :locale="zhCn"> | ||||
|         <router-view /> | ||||
|     </el-config-provider> | ||||
| </template> | ||||
| 
 | ||||
| <script setup lang="ts"> | ||||
| import { ElConfigProvider } from 'element-plus'; | ||||
| import zhCn from 'element-plus/es/locale/lang/zh-cn'; | ||||
| </script> | ||||
| <style> | ||||
| @import './assets/css/main.css'; | ||||
| @import './assets/css/color-dark.css'; | ||||
| </style> | ||||
| @ -1,8 +0,0 @@ | ||||
| import request from '../utils/request'; | ||||
| 
 | ||||
| export const fetchData = () => { | ||||
|     return request({ | ||||
|         url: './table.json', | ||||
|         method: 'get' | ||||
|     }); | ||||
| }; | ||||
| @ -1,23 +0,0 @@ | ||||
| .header{ | ||||
|     background-color: #242f42; | ||||
| } | ||||
| .login-wrap{ | ||||
|     background: #324157; | ||||
| } | ||||
| .plugins-tips{ | ||||
|     background: #eef1f6; | ||||
| } | ||||
| .plugins-tips a{ | ||||
|     color: #20a0ff; | ||||
| } | ||||
| 
 | ||||
| .tags-li.active { | ||||
|     border: 1px solid #409EFF; | ||||
|     background-color: #409EFF; | ||||
| } | ||||
| .message-title{ | ||||
|     color: #20a0ff; | ||||
| } | ||||
| .collapse-btn:hover{ | ||||
|     background: rgb(40,52,70); | ||||
| } | ||||
| @ -1,4 +0,0 @@ | ||||
| [class*=" el-icon-lx"], | ||||
| [class^=el-icon-lx] { | ||||
|     font-family: lx-iconfont !important; | ||||
| } | ||||
| @ -1,137 +0,0 @@ | ||||
| * { | ||||
|     margin: 0; | ||||
|     padding: 0; | ||||
| } | ||||
| 
 | ||||
| html, | ||||
| body, | ||||
| #app, | ||||
| .wrapper { | ||||
|     width: 100%; | ||||
|     height: 100%; | ||||
|     overflow: hidden; | ||||
| } | ||||
| 
 | ||||
| body { | ||||
|     font-family: 'PingFang SC', "Helvetica Neue", Helvetica, "microsoft yahei", arial, STHeiTi, sans-serif; | ||||
| } | ||||
| 
 | ||||
| a { | ||||
|     text-decoration: none | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| .content-box { | ||||
|     position: absolute; | ||||
|     left: 250px; | ||||
|     right: 0; | ||||
|     top: 70px; | ||||
|     bottom: 0; | ||||
|     padding-bottom: 30px; | ||||
|     -webkit-transition: left .3s ease-in-out; | ||||
|     transition: left .3s ease-in-out; | ||||
|     background: #f0f0f0; | ||||
| } | ||||
| 
 | ||||
| .content { | ||||
|     width: auto; | ||||
|     height: 100%; | ||||
|     padding: 10px; | ||||
|     overflow-y: scroll; | ||||
|     box-sizing: border-box; | ||||
| } | ||||
| 
 | ||||
| .content-collapse { | ||||
|     left: 65px; | ||||
| } | ||||
| 
 | ||||
| .container { | ||||
|     padding: 30px; | ||||
|     background: #fff; | ||||
|     border: 1px solid #ddd; | ||||
|     border-radius: 5px; | ||||
| } | ||||
| 
 | ||||
| .crumbs { | ||||
|     margin: 10px 0; | ||||
| } | ||||
| 
 | ||||
| .el-table th { | ||||
|     background-color: #f5f7fa !important; | ||||
| } | ||||
| 
 | ||||
| .pagination { | ||||
|     margin: 20px 0; | ||||
|     text-align: right; | ||||
| } | ||||
| 
 | ||||
| .plugins-tips { | ||||
|     padding: 20px 10px; | ||||
|     margin-bottom: 20px; | ||||
| } | ||||
| 
 | ||||
| .el-button+.el-tooltip { | ||||
|     margin-left: 10px; | ||||
| } | ||||
| 
 | ||||
| .el-table tr:hover { | ||||
|     background: #f6faff; | ||||
| } | ||||
| 
 | ||||
| .mgb20 { | ||||
|     margin-bottom: 20px; | ||||
| } | ||||
| 
 | ||||
| .move-enter-active, | ||||
| .move-leave-active { | ||||
|     transition: opacity .1s ease; | ||||
| } | ||||
| 
 | ||||
| .move-enter-from, | ||||
| .move-leave-to { | ||||
|     opacity: 0; | ||||
| } | ||||
| 
 | ||||
| /*BaseForm*/ | ||||
| 
 | ||||
| .form-box { | ||||
|     width: 600px; | ||||
| } | ||||
| 
 | ||||
| .form-box .line { | ||||
|     text-align: center; | ||||
| } | ||||
| 
 | ||||
| .el-time-panel__content::after, | ||||
| .el-time-panel__content::before { | ||||
|     margin-top: -7px; | ||||
| } | ||||
| 
 | ||||
| .el-time-spinner__wrapper .el-scrollbar__wrap:not(.el-scrollbar__wrap--hidden-default) { | ||||
|     padding-bottom: 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| [class*=" el-icon-"], [class^=el-icon-] { | ||||
|     speak: none; | ||||
|     font-style: normal; | ||||
|     font-weight: 400; | ||||
|     font-variant: normal; | ||||
|     text-transform: none; | ||||
|     line-height: 1; | ||||
|     vertical-align: baseline; | ||||
|     display: inline-block; | ||||
|     -webkit-font-smoothing: antialiased; | ||||
|     -moz-osx-font-smoothing: grayscale; | ||||
| } | ||||
| .el-sub-menu [class^=el-icon-] { | ||||
|     vertical-align: middle; | ||||
|     margin-right: 5px; | ||||
|     width: 24px; | ||||
|     text-align: center; | ||||
|     font-size: 18px; | ||||
| } | ||||
| 
 | ||||
| [hidden]{ | ||||
|     display: none !important; | ||||
| } | ||||
| Before Width: | Height: | Size: 6.0 KiB | 
| Before Width: | Height: | Size: 69 KiB | 
| @ -1,154 +0,0 @@ | ||||
| <template> | ||||
| 	<div class="header"> | ||||
| 		<!-- 折叠按钮 --> | ||||
| 		<div class="collapse-btn" @click="collapseChage"> | ||||
| 			<el-icon v-if="sidebar.collapse"><Expand /></el-icon> | ||||
| 			<el-icon v-else><Fold /></el-icon> | ||||
| 		</div> | ||||
| 		<div class="logo">后台管理系统</div> | ||||
| 		<div class="header-right"> | ||||
| 			<div class="header-user-con"> | ||||
| 				<!-- 消息中心 --> | ||||
| 				<div class="btn-bell" @click="router.push('/tabs')"> | ||||
| 					<el-tooltip | ||||
| 						effect="dark" | ||||
| 						:content="message ? `有${message}条未读消息` : `消息中心`" | ||||
| 						placement="bottom" | ||||
| 					> | ||||
| 						<i class="el-icon-lx-notice"></i> | ||||
| 					</el-tooltip> | ||||
| 					<span class="btn-bell-badge" v-if="message"></span> | ||||
| 				</div> | ||||
| 				<!-- 用户头像 --> | ||||
| 				<el-avatar class="user-avator" :size="30" :src="imgurl" /> | ||||
| 				<!-- 用户名下拉菜单 --> | ||||
| 				<el-dropdown class="user-name" trigger="click" @command="handleCommand"> | ||||
| 					<span class="el-dropdown-link"> | ||||
| 						{{ username }} | ||||
| 						<el-icon class="el-icon--right"> | ||||
| 							<arrow-down /> | ||||
| 						</el-icon> | ||||
| 					</span> | ||||
| 					<template #dropdown> | ||||
| 						<el-dropdown-menu> | ||||
| 							<a href="https://github.com/lin-xin/vue-manage-system" target="_blank"> | ||||
| 								<el-dropdown-item>项目仓库</el-dropdown-item> | ||||
| 							</a> | ||||
| 							<el-dropdown-item command="user">个人中心</el-dropdown-item> | ||||
| 							<el-dropdown-item divided command="loginout">退出登录</el-dropdown-item> | ||||
| 						</el-dropdown-menu> | ||||
| 					</template> | ||||
| 				</el-dropdown> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 	</div> | ||||
| </template> | ||||
| <script setup lang="ts"> | ||||
| import { onMounted } from 'vue'; | ||||
| import { useSidebarStore } from '../store/sidebar'; | ||||
| import { useRouter } from 'vue-router'; | ||||
| import imgurl from '../assets/img/img.jpg'; | ||||
| 
 | ||||
| const username: string | null = localStorage.getItem('ms_username'); | ||||
| const message: number = 2; | ||||
| 
 | ||||
| const sidebar = useSidebarStore(); | ||||
| // 侧边栏折叠 | ||||
| const collapseChage = () => { | ||||
| 	sidebar.handleCollapse(); | ||||
| }; | ||||
| 
 | ||||
| onMounted(() => { | ||||
| 	if (document.body.clientWidth < 1500) { | ||||
| 		collapseChage(); | ||||
| 	} | ||||
| }); | ||||
| 
 | ||||
| // 用户名下拉菜单选择事件 | ||||
| const router = useRouter(); | ||||
| const handleCommand = (command: string) => { | ||||
| 	if (command == 'loginout') { | ||||
| 		localStorage.removeItem('ms_username'); | ||||
| 		router.push('/login'); | ||||
| 	} else if (command == 'user') { | ||||
| 		router.push('/user'); | ||||
| 	} | ||||
| }; | ||||
| </script> | ||||
| <style scoped> | ||||
| .header { | ||||
| 	position: relative; | ||||
| 	box-sizing: border-box; | ||||
| 	width: 100%; | ||||
| 	height: 70px; | ||||
| 	font-size: 22px; | ||||
| 	color: #fff; | ||||
| } | ||||
| .collapse-btn { | ||||
| 	display: flex; | ||||
| 	justify-content: center; | ||||
| 	align-items: center; | ||||
| 	height: 100%; | ||||
| 	float: left; | ||||
| 	padding: 0 21px; | ||||
| 	cursor: pointer; | ||||
| } | ||||
| .header .logo { | ||||
| 	float: left; | ||||
| 	width: 250px; | ||||
| 	line-height: 70px; | ||||
| } | ||||
| .header-right { | ||||
| 	float: right; | ||||
| 	padding-right: 50px; | ||||
| } | ||||
| .header-user-con { | ||||
| 	display: flex; | ||||
| 	height: 70px; | ||||
| 	align-items: center; | ||||
| } | ||||
| .btn-fullscreen { | ||||
| 	transform: rotate(45deg); | ||||
| 	margin-right: 5px; | ||||
| 	font-size: 24px; | ||||
| } | ||||
| .btn-bell, | ||||
| .btn-fullscreen { | ||||
| 	position: relative; | ||||
| 	width: 30px; | ||||
| 	height: 30px; | ||||
| 	text-align: center; | ||||
| 	border-radius: 15px; | ||||
| 	cursor: pointer; | ||||
| 	display: flex; | ||||
| 	align-items: center; | ||||
| } | ||||
| .btn-bell-badge { | ||||
| 	position: absolute; | ||||
| 	right: 4px; | ||||
| 	top: 0px; | ||||
| 	width: 8px; | ||||
| 	height: 8px; | ||||
| 	border-radius: 4px; | ||||
| 	background: #f56c6c; | ||||
| 	color: #fff; | ||||
| } | ||||
| .btn-bell .el-icon-lx-notice { | ||||
| 	color: #fff; | ||||
| } | ||||
| .user-name { | ||||
| 	margin-left: 10px; | ||||
| } | ||||
| .user-avator { | ||||
| 	margin-left: 20px; | ||||
| } | ||||
| .el-dropdown-link { | ||||
| 	color: #fff; | ||||
| 	cursor: pointer; | ||||
| 	display: flex; | ||||
| 	align-items: center; | ||||
| } | ||||
| .el-dropdown-menu__item { | ||||
| 	text-align: center; | ||||
| } | ||||
| </style> | ||||
| @ -1,181 +0,0 @@ | ||||
| <template> | ||||
|     <div class="sidebar"> | ||||
|         <el-menu | ||||
|             class="sidebar-el-menu" | ||||
|             :default-active="onRoutes" | ||||
|             :collapse="sidebar.collapse" | ||||
|             background-color="#324157" | ||||
|             text-color="#bfcbd9" | ||||
|             active-text-color="#20a0ff" | ||||
|             unique-opened | ||||
|             router | ||||
|         > | ||||
|             <template v-for="item in items"> | ||||
|                 <template v-if="item.subs"> | ||||
|                     <el-sub-menu :index="item.index" :key="item.index" v-permiss="item.permiss"> | ||||
|                         <template #title> | ||||
|                             <el-icon> | ||||
|                                 <component :is="item.icon"></component> | ||||
|                             </el-icon> | ||||
|                             <span>{{ item.title }}</span> | ||||
|                         </template> | ||||
|                         <template v-for="subItem in item.subs"> | ||||
|                             <el-sub-menu | ||||
|                                 v-if="subItem.subs" | ||||
|                                 :index="subItem.index" | ||||
|                                 :key="subItem.index" | ||||
|                                 v-permiss="item.permiss" | ||||
|                             > | ||||
|                                 <template #title>{{ subItem.title }}</template> | ||||
|                                 <el-menu-item v-for="(threeItem, i) in subItem.subs" :key="i" :index="threeItem.index"> | ||||
|                                     {{ threeItem.title }} | ||||
|                                 </el-menu-item> | ||||
|                             </el-sub-menu> | ||||
|                             <el-menu-item v-else :index="subItem.index" v-permiss="item.permiss"> | ||||
|                                 {{ subItem.title }} | ||||
|                             </el-menu-item> | ||||
|                         </template> | ||||
|                     </el-sub-menu> | ||||
|                 </template> | ||||
|                 <template v-else> | ||||
|                     <el-menu-item :index="item.index" :key="item.index" v-permiss="item.permiss"> | ||||
|                         <el-icon> | ||||
|                             <component :is="item.icon"></component> | ||||
|                         </el-icon> | ||||
|                         <template #title>{{ item.title }}</template> | ||||
|                     </el-menu-item> | ||||
|                 </template> | ||||
|             </template> | ||||
|         </el-menu> | ||||
|     </div> | ||||
| </template> | ||||
| 
 | ||||
| <script setup lang="ts"> | ||||
| import { computed } from 'vue'; | ||||
| import { useSidebarStore } from '../store/sidebar'; | ||||
| import { useRoute } from 'vue-router'; | ||||
| 
 | ||||
| const items = [ | ||||
|     { | ||||
|         icon: 'Odometer', | ||||
|         index: '/dashboard', | ||||
|         title: '系统首页', | ||||
|         permiss: '1', | ||||
|     }, | ||||
|     { | ||||
|         icon: 'Calendar', | ||||
|         index: '1', | ||||
|         title: '表格相关', | ||||
|         permiss: '2', | ||||
|         subs: [ | ||||
|             { | ||||
|                 index: '/table', | ||||
|                 title: '常用表格', | ||||
|                 permiss: '2', | ||||
|             }, | ||||
|             { | ||||
|                 index: '/import', | ||||
|                 title: '导入Excel', | ||||
|                 permiss: '2', | ||||
|             }, | ||||
|             { | ||||
|                 index: '/export', | ||||
|                 title: '导出Excel', | ||||
|                 permiss: '2', | ||||
|             }, | ||||
|         ], | ||||
|     }, | ||||
|     { | ||||
|         icon: 'DocumentCopy', | ||||
|         index: '/tabs', | ||||
|         title: 'tab选项卡', | ||||
|         permiss: '3', | ||||
|     }, | ||||
|     { | ||||
|         icon: 'Edit', | ||||
|         index: '3', | ||||
|         title: '表单相关', | ||||
|         permiss: '4', | ||||
|         subs: [ | ||||
|             { | ||||
|                 index: '/form', | ||||
|                 title: '基本表单', | ||||
|                 permiss: '5', | ||||
|             }, | ||||
|             { | ||||
|                 index: '/upload', | ||||
|                 title: '文件上传', | ||||
|                 permiss: '6', | ||||
|             }, | ||||
|             { | ||||
|                 index: '4', | ||||
|                 title: '三级菜单', | ||||
|                 permiss: '7', | ||||
|                 subs: [ | ||||
|                     { | ||||
|                         index: '/editor', | ||||
|                         title: '富文本编辑器', | ||||
|                         permiss: '8', | ||||
|                     }, | ||||
|                     { | ||||
|                         index: '/markdown', | ||||
|                         title: 'markdown编辑器', | ||||
|                         permiss: '9', | ||||
|                     }, | ||||
|                 ], | ||||
|             }, | ||||
|         ], | ||||
|     }, | ||||
|     { | ||||
|         icon: 'Setting', | ||||
|         index: '/icon', | ||||
|         title: '自定义图标', | ||||
|         permiss: '10', | ||||
|     }, | ||||
|     { | ||||
|         icon: 'PieChart', | ||||
|         index: '/charts', | ||||
|         title: 'schart图表', | ||||
|         permiss: '11', | ||||
|     }, | ||||
|     { | ||||
|         icon: 'Warning', | ||||
|         index: '/permission', | ||||
|         title: '权限管理', | ||||
|         permiss: '13', | ||||
|     }, | ||||
|     { | ||||
|         icon: 'CoffeeCup', | ||||
|         index: '/donate', | ||||
|         title: '支持作者', | ||||
|         permiss: '14', | ||||
|     }, | ||||
| ]; | ||||
| 
 | ||||
| const route = useRoute(); | ||||
| const onRoutes = computed(() => { | ||||
|     return route.path; | ||||
| }); | ||||
| 
 | ||||
| const sidebar = useSidebarStore(); | ||||
| </script> | ||||
| 
 | ||||
| <style scoped> | ||||
| .sidebar { | ||||
|     display: block; | ||||
|     position: absolute; | ||||
|     left: 0; | ||||
|     top: 70px; | ||||
|     bottom: 0; | ||||
|     overflow-y: scroll; | ||||
| } | ||||
| .sidebar::-webkit-scrollbar { | ||||
|     width: 0; | ||||
| } | ||||
| .sidebar-el-menu:not(.el-menu--collapse) { | ||||
|     width: 250px; | ||||
| } | ||||
| .sidebar > ul { | ||||
|     height: 100%; | ||||
| } | ||||
| </style> | ||||
| @ -1,168 +0,0 @@ | ||||
| <template> | ||||
| 	<div class="tags" v-if="tags.show"> | ||||
| 		<ul> | ||||
| 			<li | ||||
| 				class="tags-li" | ||||
| 				v-for="(item, index) in tags.list" | ||||
| 				:class="{ active: isActive(item.path) }" | ||||
| 				:key="index" | ||||
| 			> | ||||
| 				<router-link :to="item.path" class="tags-li-title">{{ item.title }}</router-link> | ||||
| 				<el-icon @click="closeTags(index)"><Close /></el-icon> | ||||
| 			</li> | ||||
| 		</ul> | ||||
| 		<div class="tags-close-box"> | ||||
| 			<el-dropdown @command="handleTags"> | ||||
| 				<el-button size="small" type="primary"> | ||||
| 					标签选项 | ||||
| 					<el-icon class="el-icon--right"> | ||||
| 						<arrow-down /> | ||||
| 					</el-icon> | ||||
| 				</el-button> | ||||
| 				<template #dropdown> | ||||
| 					<el-dropdown-menu size="small"> | ||||
| 						<el-dropdown-item command="other">关闭其他</el-dropdown-item> | ||||
| 						<el-dropdown-item command="all">关闭所有</el-dropdown-item> | ||||
| 					</el-dropdown-menu> | ||||
| 				</template> | ||||
| 			</el-dropdown> | ||||
| 		</div> | ||||
| 	</div> | ||||
| </template> | ||||
| 
 | ||||
| <script setup lang="ts"> | ||||
| import { useTagsStore } from '../store/tags'; | ||||
| import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router'; | ||||
| 
 | ||||
| const route = useRoute(); | ||||
| const router = useRouter(); | ||||
| const isActive = (path: string) => { | ||||
| 	return path === route.fullPath; | ||||
| }; | ||||
| 
 | ||||
| const tags = useTagsStore(); | ||||
| // 关闭单个标签 | ||||
| const closeTags = (index: number) => { | ||||
| 	const delItem = tags.list[index]; | ||||
| 	tags.delTagsItem(index); | ||||
| 	const item = tags.list[index] ? tags.list[index] : tags.list[index - 1]; | ||||
| 	if (item) { | ||||
| 		delItem.path === route.fullPath && router.push(item.path); | ||||
| 	} else { | ||||
| 		router.push('/'); | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| // 设置标签 | ||||
| const setTags = (route: any) => { | ||||
| 	const isExist = tags.list.some(item => { | ||||
| 		return item.path === route.fullPath; | ||||
| 	}); | ||||
| 	if (!isExist) { | ||||
| 		if (tags.list.length >= 8) tags.delTagsItem(0); | ||||
| 		tags.setTagsItem({ | ||||
| 			name: route.name, | ||||
| 			title: route.meta.title, | ||||
| 			path: route.fullPath | ||||
| 		}); | ||||
| 	} | ||||
| }; | ||||
| setTags(route); | ||||
| onBeforeRouteUpdate(to => { | ||||
| 	setTags(to); | ||||
| }); | ||||
| 
 | ||||
| // 关闭全部标签 | ||||
| const closeAll = () => { | ||||
| 	tags.clearTags(); | ||||
| 	router.push('/'); | ||||
| }; | ||||
| // 关闭其他标签 | ||||
| const closeOther = () => { | ||||
| 	const curItem = tags.list.filter(item => { | ||||
| 		return item.path === route.fullPath; | ||||
| 	}); | ||||
| 	tags.closeTagsOther(curItem); | ||||
| }; | ||||
| const handleTags = (command: string) => { | ||||
| 	command === 'other' ? closeOther() : closeAll(); | ||||
| }; | ||||
| 
 | ||||
| // 关闭当前页面的标签页 | ||||
| // tags.closeCurrentTag({ | ||||
| //     $router: router, | ||||
| //     $route: route | ||||
| // }); | ||||
| </script> | ||||
| 
 | ||||
| <style> | ||||
| .tags { | ||||
| 	position: relative; | ||||
| 	height: 30px; | ||||
| 	overflow: hidden; | ||||
| 	background: #fff; | ||||
| 	padding-right: 120px; | ||||
| 	box-shadow: 0 5px 10px #ddd; | ||||
| } | ||||
| 
 | ||||
| .tags ul { | ||||
| 	box-sizing: border-box; | ||||
| 	width: 100%; | ||||
| 	height: 100%; | ||||
| } | ||||
| 
 | ||||
| .tags-li { | ||||
| 	display: flex; | ||||
| 	align-items: center; | ||||
| 	float: left; | ||||
| 	margin: 3px 5px 2px 3px; | ||||
| 	border-radius: 3px; | ||||
| 	font-size: 12px; | ||||
| 	overflow: hidden; | ||||
| 	cursor: pointer; | ||||
| 	height: 23px; | ||||
| 	border: 1px solid #e9eaec; | ||||
| 	background: #fff; | ||||
| 	padding: 0 5px 0 12px; | ||||
| 	color: #666; | ||||
| 	-webkit-transition: all 0.3s ease-in; | ||||
| 	-moz-transition: all 0.3s ease-in; | ||||
| 	transition: all 0.3s ease-in; | ||||
| } | ||||
| 
 | ||||
| .tags-li:not(.active):hover { | ||||
| 	background: #f8f8f8; | ||||
| } | ||||
| 
 | ||||
| .tags-li.active { | ||||
| 	color: #fff; | ||||
| } | ||||
| 
 | ||||
| .tags-li-title { | ||||
| 	float: left; | ||||
| 	max-width: 80px; | ||||
| 	overflow: hidden; | ||||
| 	white-space: nowrap; | ||||
| 	text-overflow: ellipsis; | ||||
| 	margin-right: 5px; | ||||
| 	color: #666; | ||||
| } | ||||
| 
 | ||||
| .tags-li.active .tags-li-title { | ||||
| 	color: #fff; | ||||
| } | ||||
| 
 | ||||
| .tags-close-box { | ||||
| 	position: absolute; | ||||
| 	right: 0; | ||||
| 	top: 0; | ||||
| 	box-sizing: border-box; | ||||
| 	padding-top: 1px; | ||||
| 	text-align: center; | ||||
| 	width: 110px; | ||||
| 	height: 30px; | ||||
| 	background: #fff; | ||||
| 	box-shadow: -3px 0 15px 3px rgba(0, 0, 0, 0.1); | ||||
| 	z-index: 10; | ||||
| } | ||||
| </style> | ||||
| @ -1,28 +0,0 @@ | ||||
| import { createApp } from 'vue'; | ||||
| import { createPinia } from 'pinia'; | ||||
| import * as ElementPlusIconsVue from '@element-plus/icons-vue'; | ||||
| import App from './App.vue'; | ||||
| import router from './router'; | ||||
| import { usePermissStore } from './store/permiss'; | ||||
| import 'element-plus/dist/index.css'; | ||||
| import './assets/css/icon.css'; | ||||
| 
 | ||||
| const app = createApp(App); | ||||
| app.use(createPinia()); | ||||
| app.use(router); | ||||
| 
 | ||||
| // 注册elementplus图标
 | ||||
| for (const [key, component] of Object.entries(ElementPlusIconsVue)) { | ||||
|     app.component(key, component); | ||||
| } | ||||
| // 自定义权限指令
 | ||||
| const permiss = usePermissStore(); | ||||
| app.directive('permiss', { | ||||
|     mounted(el, binding) { | ||||
|         if (!permiss.key.includes(String(binding.value))) { | ||||
|             el['hidden'] = true; | ||||
|         } | ||||
|     }, | ||||
| }); | ||||
| 
 | ||||
| app.mount('#app'); | ||||
| @ -1,23 +0,0 @@ | ||||
| import { defineStore } from 'pinia'; | ||||
| 
 | ||||
| interface ObjectList { | ||||
| 	[key: string]: string[]; | ||||
| } | ||||
| 
 | ||||
| export const usePermissStore = defineStore('permiss', { | ||||
| 	state: () => { | ||||
| 		const keys = localStorage.getItem('ms_keys'); | ||||
| 		return { | ||||
| 			key: keys ? JSON.parse(keys) : <string[]>[], | ||||
| 			defaultList: <ObjectList>{ | ||||
| 				admin: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16'], | ||||
| 				user: ['1', '2', '3', '11', '13', '14', '15'] | ||||
| 			} | ||||
| 		}; | ||||
| 	}, | ||||
| 	actions: { | ||||
| 		handleSet(val: string[]) { | ||||
| 			this.key = val; | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
| @ -1,15 +0,0 @@ | ||||
| import { defineStore } from 'pinia'; | ||||
| 
 | ||||
| export const useSidebarStore = defineStore('sidebar', { | ||||
| 	state: () => { | ||||
| 		return { | ||||
| 			collapse: false | ||||
| 		}; | ||||
| 	}, | ||||
| 	getters: {}, | ||||
| 	actions: { | ||||
| 		handleCollapse() { | ||||
| 			this.collapse = !this.collapse; | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||