├── static ├── .gitkeep ├── login.json └── permission.json ├── src ├── store │ ├── actions.js │ ├── getters.js │ ├── modules.js │ ├── state.js │ ├── mutations.js │ ├── index.js │ └── modules │ │ └── permission.js ├── config │ ├── baseUrl.js │ └── httpConfig.js ├── assets │ └── image │ │ └── a.png ├── styles │ ├── index.scss │ ├── _mixin.scss │ ├── _sidebar.scss │ ├── _reset_element.scss │ └── _normalize.scss ├── pages │ ├── home │ │ ├── component │ │ │ ├── todo-list.vue │ │ │ ├── total-status.vue │ │ │ └── charts.vue │ │ └── index.vue │ ├── order-manage │ │ ├── product-manage │ │ │ ├── index.vue │ │ │ ├── production-list │ │ │ │ └── index.vue │ │ │ └── review-manage │ │ │ │ └── index.vue │ │ ├── index.vue │ │ ├── order-list │ │ │ └── index.vue │ │ └── return-goods │ │ │ └── index.vue │ ├── goods-manage │ │ ├── index.vue │ │ ├── goods-list │ │ │ └── index.vue │ │ └── goods-classify │ │ │ └── index.vue │ ├── finance-manage │ │ ├── index.vue │ │ └── component │ │ │ ├── order-list │ │ │ └── index.vue │ │ │ ├── product-manage │ │ │ └── index.vue │ │ │ └── return-goods │ │ │ └── index.vue │ ├── layout │ │ ├── component │ │ │ ├── main-content │ │ │ │ ├── content.vue │ │ │ │ ├── index.vue │ │ │ │ └── top-aside.vue │ │ │ └── sidebar-nav.vue │ │ └── index.vue │ ├── login │ │ └── login.vue │ └── errorPage │ │ ├── 403.vue │ │ └── 404.vue ├── api │ └── permission.js ├── App.vue ├── filters │ └── filters.js ├── utils │ └── recursion-router.js ├── router │ ├── index.js │ └── dynamic-router.js ├── components │ └── dynamic-menu.vue └── main.js ├── .eslintignore ├── config ├── prod.env.js ├── dev.env.js └── index.js ├── .editorconfig ├── .gitignore ├── .babelrc ├── .postcssrc.js ├── index.html ├── README.md ├── .eslintrc.js └── package.json /static/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/store/actions.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/store/getters.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /config/ 3 | /dist/ 4 | /*.js 5 | -------------------------------------------------------------------------------- /src/config/baseUrl.js: -------------------------------------------------------------------------------- 1 | const baseUrl = 'http://localhost:8080' 2 | export default baseUrl 3 | -------------------------------------------------------------------------------- /src/assets/image/a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linrunzheng/vue-permission-control/HEAD/src/assets/image/a.png -------------------------------------------------------------------------------- /src/store/modules.js: -------------------------------------------------------------------------------- 1 | import permission from './modules/permission' 2 | 3 | export default { 4 | permission 5 | } 6 | -------------------------------------------------------------------------------- /static/login.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": 0, 3 | "message": "登录成功", 4 | "data": { 5 | "token":"abcdedfhijklmnokrjsuvmxyz" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /config/prod.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = { 3 | NODE_ENV: '"production"', 4 | BASE_API: '"https://easy-mock.com/mock/5950a2419adc231f356a6636/vue-admin"', 5 | } 6 | -------------------------------------------------------------------------------- /src/styles/index.scss: -------------------------------------------------------------------------------- 1 | @import './normalize.scss'; 2 | @import './reset_element.scss'; 3 | @import './sidebar.scss'; 4 | @import url('//at.alicdn.com/t/font_641452_q3ah7ae4qvndn29.css'); -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /src/pages/home/component/todo-list.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | .svn/ 5 | npm-debug.log* 6 | yarn-debug.log* 7 | yarn-error.log* 8 | 9 | # Editor directories and files 10 | .idea 11 | .vscode 12 | *.suo 13 | *.ntvs* 14 | *.njsproj 15 | *.sln 16 | -------------------------------------------------------------------------------- /src/api/permission.js: -------------------------------------------------------------------------------- 1 | import axios from '@/config/httpConfig' 2 | 3 | export function fetchPermission() { 4 | return axios.get('/static/permission.json') 5 | } 6 | 7 | export function login() { 8 | return axios.get('/static/login.json') 9 | } 10 | -------------------------------------------------------------------------------- /src/pages/order-manage/product-manage/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | -------------------------------------------------------------------------------- /config/dev.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const prodEnv = require('./prod.env') 4 | 5 | module.exports = merge(prodEnv, { 6 | NODE_ENV: '"development"', 7 | BASE_API: '"https://easy-mock.com/mock/5950a2419adc231f356a6636/vue-admin"', 8 | }) 9 | -------------------------------------------------------------------------------- /src/pages/goods-manage/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | -------------------------------------------------------------------------------- /src/pages/order-manage/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | -------------------------------------------------------------------------------- /src/pages/finance-manage/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false, 5 | "targets": { 6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] 7 | } 8 | }], 9 | "stage-2" 10 | ], 11 | "plugins": ["transform-vue-jsx", "transform-runtime"] 12 | } 13 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | "postcss-import": {}, 6 | "postcss-url": {}, 7 | // to edit target browsers: use "browserslist" field in package.json 8 | "autoprefixer": {} 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/pages/goods-manage/goods-list/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 18 | -------------------------------------------------------------------------------- /src/pages/layout/component/main-content/content.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | 13 | 16 | -------------------------------------------------------------------------------- /src/pages/order-manage/order-list/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 18 | -------------------------------------------------------------------------------- /src/pages/goods-manage/goods-classify/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 18 | -------------------------------------------------------------------------------- /src/pages/finance-manage/component/order-list/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 18 | -------------------------------------------------------------------------------- /src/pages/order-manage/return-goods/index.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 19 | -------------------------------------------------------------------------------- /src/pages/order-manage/product-manage/production-list/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 18 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | hongmu 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/pages/finance-manage/component/product-manage/index.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 19 | -------------------------------------------------------------------------------- /src/pages/finance-manage/component/return-goods/index.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 19 | -------------------------------------------------------------------------------- /src/store/state.js: -------------------------------------------------------------------------------- 1 | export default { 2 | get UserToken() { 3 | return localStorage.getItem('token') 4 | }, 5 | set UserToken(value) { 6 | localStorage.setItem('token', value) 7 | }, 8 | /* 导航菜单是否折叠 */ 9 | isSidebarNavCollapse: false, 10 | /* 面包屑导航列表 */ 11 | crumbList: [] 12 | } 13 | -------------------------------------------------------------------------------- /src/pages/order-manage/product-manage/review-manage/index.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 19 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 21 | -------------------------------------------------------------------------------- /src/store/mutations.js: -------------------------------------------------------------------------------- 1 | export default { 2 | LOGIN_IN(state, token) { 3 | state.UserToken = token 4 | }, 5 | LOGIN_OUT(state) { 6 | state.UserToken = '' 7 | }, 8 | toggleNavCollapse(state) { 9 | state.isSidebarNavCollapse = !state.isSidebarNavCollapse 10 | }, 11 | setCrumbList(state, list) { 12 | state.crumbList = list 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | import state from './state' 5 | import getters from './getters' 6 | import modules from './modules' 7 | import actions from './actions' 8 | import mutations from './mutations' 9 | 10 | Vue.use(Vuex) 11 | 12 | export default new Vuex.Store({ 13 | state, 14 | getters, 15 | mutations, 16 | actions, 17 | modules 18 | }) 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hongmu 2 | 3 | > A Vue.js project 4 | 5 | ## Build Setup 6 | 7 | ``` bash 8 | # install dependencies 9 | npm install 10 | 11 | # serve with hot reload at localhost:8080 12 | npm run dev 13 | 14 | # build for production with minification 15 | npm run build 16 | 17 | # build for production and view the bundle analyzer report 18 | npm run build --report 19 | ``` 20 | 21 | For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). 22 | -------------------------------------------------------------------------------- /src/pages/layout/component/main-content/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 26 | -------------------------------------------------------------------------------- /src/filters/filters.js: -------------------------------------------------------------------------------- 1 | function formatNumber(n) { 2 | const str = n.toString() 3 | return str[1] ? str : `0${str}` 4 | } 5 | 6 | export function formatTime(date) { 7 | const year = date.getFullYear() 8 | const month = date.getMonth() + 1 9 | const day = date.getDate() 10 | 11 | const hour = date.getHours() 12 | const minute = date.getMinutes() 13 | const second = date.getSeconds() 14 | 15 | const t1 = [year, month, day].map(formatNumber).join('/') 16 | const t2 = [hour, minute, second].map(formatNumber).join(':') 17 | 18 | return `${t1} ${t2}` 19 | } 20 | -------------------------------------------------------------------------------- /src/pages/home/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 29 | -------------------------------------------------------------------------------- /src/pages/layout/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 25 | 26 | 29 | -------------------------------------------------------------------------------- /src/pages/layout/component/sidebar-nav.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 33 | -------------------------------------------------------------------------------- /src/styles/_mixin.scss: -------------------------------------------------------------------------------- 1 | $mainColor:#151519; 2 | 3 | @mixin table-center { 4 | display: table-cell; 5 | vertical-align: middle; 6 | text-align: center; 7 | } 8 | 9 | @mixin poa-center($w, $h) { 10 | position: absolute; 11 | width: $w; 12 | height: $h; 13 | left: 50%; 14 | top: 50%; 15 | transition: translate(-50%, -50%) 16 | } 17 | 18 | @mixin flex-center { 19 | display: flex; 20 | justify-content: center; 21 | align-items: center; 22 | } 23 | 24 | @mixin t-overflow($line:1) { 25 | @if $line==1 { 26 | overflow: hidden; 27 | text-overflow: ellipsis; 28 | white-space: nowrap; 29 | } 30 | @else { 31 | display: -webkit-box; 32 | -webkit-line-clamp: $line; 33 | -webkit-box-orient: vertical; 34 | overflow: hidden; 35 | text-overflow: ellipsis; 36 | } 37 | } 38 | 39 | 40 | -------------------------------------------------------------------------------- /static/permission.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": 0, 3 | "message": "获取权限成功", 4 | "data": [ 5 | { 6 | "name": "订单管理", 7 | "children": [ 8 | { 9 | "name": "订单列表" 10 | }, 11 | { 12 | "name": "生产管理", 13 | "children": [ 14 | { 15 | "name": "生产列表" 16 | }, 17 | { 18 | "name": "审核管理" 19 | } 20 | ] 21 | }, 22 | { 23 | "name": "退货管理" 24 | } 25 | ] 26 | }, 27 | { 28 | "name": "产品管理", 29 | "children": [ 30 | { 31 | "name": "产品列表" 32 | }, 33 | { 34 | "name": "产品分类" 35 | } 36 | ] 37 | } 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | // https://eslint.org/docs/user-guide/configuring 2 | 3 | module.exports = { 4 | root: true, 5 | parserOptions: { 6 | parser: 'babel-eslint' 7 | }, 8 | env: { 9 | browser: true, 10 | }, 11 | extends: [ 12 | // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention 13 | // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules. 14 | 'plugin:vue/essential', 15 | // https://github.com/standard/standard/blob/master/docs/RULES-en.md 16 | 'standard' 17 | ], 18 | // required to lint *.vue files 19 | plugins: [ 20 | 'vue' 21 | ], 22 | // add your custom rules here 23 | rules: { 24 | "indent": ["error", 4], 25 | // allow async-await 26 | 'generator-star-spacing': 'off', 27 | 'space-before-function-paren': 'off', 28 | // allow debugger during development 29 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/utils/recursion-router.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {Array} userRouter 后台返回的用户权限json 4 | * @param {Array} allRouter 前端配置好的所有动态路由的集合 5 | * @return {Array} realRoutes 过滤后的路由 6 | */ 7 | 8 | export function recursionRouter(userRouter = [], allRouter = []) { 9 | var realRoutes = [] 10 | allRouter.forEach((v, i) => { 11 | userRouter.forEach((item, index) => { 12 | if (item.name === v.meta.name) { 13 | if (item.children && item.children.length > 0) { 14 | v.children = recursionRouter(item.children, v.children) 15 | } 16 | realRoutes.push(v) 17 | } 18 | }) 19 | }) 20 | return realRoutes 21 | } 22 | 23 | /** 24 | * 25 | * @param {Array} routes 用户过滤后的路由 26 | * 27 | * 递归为所有有子路由的路由设置第一个children.path为默认路由 28 | */ 29 | export function setDefaultRoute(routes) { 30 | routes.forEach((v, i) => { 31 | if (v.children && v.children.length > 0) { 32 | v.redirect = { name: v.children[0].name } 33 | setDefaultRoute(v.children) 34 | } 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /src/pages/home/component/total-status.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 24 | 25 | 46 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | 4 | import Login from 'pages/login/login' 5 | import NotFound from 'pages/errorPage/404' 6 | import Forbidden from 'pages/errorPage/403' 7 | import Layout from 'pages/layout/index' 8 | import Home from 'pages/home/index' 9 | 10 | Vue.use(Router) 11 | 12 | /* 初始路由 */ 13 | export default new Router({ 14 | routes: [ 15 | { 16 | path: '/login', 17 | component: Login 18 | } 19 | ] 20 | }) 21 | 22 | /* 准备动态添加的路由 */ 23 | export const DynamicRoutes = [ 24 | { 25 | path: '', 26 | component: Layout, 27 | name: 'container', 28 | redirect: 'home', 29 | meta: { 30 | requiresAuth: true, 31 | name: '首页' 32 | }, 33 | children: [ 34 | { 35 | path: 'home', 36 | component: Home, 37 | name: 'home', 38 | meta: { 39 | name: '首页', 40 | icon: 'icon-home' 41 | } 42 | } 43 | ] 44 | }, 45 | { 46 | path: '/403', 47 | component: Forbidden 48 | }, 49 | { 50 | path: '*', 51 | component: NotFound 52 | } 53 | ] 54 | -------------------------------------------------------------------------------- /src/components/dynamic-menu.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 39 | 40 | 43 | -------------------------------------------------------------------------------- /src/styles/_sidebar.scss: -------------------------------------------------------------------------------- 1 | /* 侧边栏 */ 2 | .sidebar { 3 | width: 200px !important; 4 | height: 100%; 5 | background: #304156; 6 | transition: all 0.25s; 7 | position: fixed; 8 | top: 0; 9 | bottom: 0; 10 | left: 0; 11 | z-index: 100; 12 | overflow-x: hidden; 13 | .iconfont { 14 | margin-right: 8px; 15 | color: #fff; 16 | font-size: 18px; 17 | } 18 | >div.menu-container>li.el-menu-item, 19 | >div.menu-container>li.el-submenu>.el-submenu__title { 20 | border-bottom: 1px solid rgba(238, 238, 238, 0.1); 21 | } 22 | .el-menu-item { 23 | background: #304156 !important; 24 | } 25 | /* 菜单hover时的背景 */ 26 | .el-submenu__title:hover, 27 | .el-menu-item:hover { 28 | background: #223041 !important; 29 | } 30 | /* 菜单active时的背景 */ 31 | /* .el-menu-item.is-active { 32 | background: #293748 !important; 33 | } */ 34 | } 35 | 36 | /* 主体内容 */ 37 | .main-container { 38 | min-height: 100%; 39 | margin-left: 200px; 40 | transition: margin-left 0.25s; 41 | position: relative; 42 | box-sizing: border-box; 43 | padding-top: 50px; 44 | } 45 | 46 | /* 折叠菜单下的样式 */ 47 | .navCollapsed { 48 | .sidebar { 49 | width: 64px !important; 50 | ul { 51 | display: none; 52 | } 53 | .iconfont+span { 54 | display: none; 55 | } 56 | .el-submenu__icon-arrow { 57 | display: none; 58 | } 59 | } 60 | .main-container { 61 | margin-left: 64px; 62 | } 63 | .aside__top{ 64 | left:64px!important; 65 | } 66 | } -------------------------------------------------------------------------------- /src/styles/_reset_element.scss: -------------------------------------------------------------------------------- 1 | .el-menu { 2 | border: none; 3 | .iconfont { 4 | color: #fff; 5 | font-size: 16px; 6 | } 7 | .el-submenu__title { 8 | &:hover { 9 | background: none !important; 10 | } 11 | i.el-submenu__icon-arrow { 12 | color: #ddd; 13 | font-size: 15px; 14 | } 15 | } 16 | .el-menu-item-group__title { 17 | padding: 0; 18 | } 19 | } 20 | 21 | .el-breadcrumb { 22 | display: inline-block; 23 | vertical-align: middle; 24 | font-size: 14px; 25 | margin-left: 5px; 26 | .el-breadcrumb__inner { 27 | &.is-link { 28 | display: inline-block; 29 | font-weight: normal; 30 | color: #424040 !important; 31 | } 32 | } 33 | .is-last-link .is-link { 34 | font-weight: normal; 35 | color: #999 !important; 36 | } 37 | } 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | .sidebar-container { 49 | transition: width .28s; 50 | width: 180px ; 51 | height: 100%; 52 | position: fixed; 53 | top: 0; 54 | bottom: 0; 55 | left: 0; 56 | z-index: 1001; 57 | background: rgba(0,0,0,0.5) 58 | } 59 | 60 | 61 | 62 | 63 | .sidebar-container.navCollapsed { 64 | width: 63px ; 65 | } 66 | 67 | .navCollapsed{ 68 | .el-submenu { 69 | &>.el-submenu__title { 70 | &>span { 71 | display: none; 72 | } 73 | .el-submenu__icon-arrow { 74 | display: none; 75 | } 76 | } 77 | } 78 | 79 | } 80 | 81 | 82 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from '@/App' 3 | import store from '@/store/index' 4 | import router from '@/router/index' 5 | 6 | import ElementUI from 'element-ui' 7 | import 'element-ui/lib/theme-chalk/index.css' 8 | import './styles/index.scss' 9 | 10 | import axios from './config/httpConfig' 11 | import * as globalFilter from './filters/filters' 12 | 13 | Vue.prototype.$http = axios 14 | 15 | Object.keys(globalFilter).forEach(key => { 16 | Vue.filter(key, globalFilter[key]) 17 | }) 18 | 19 | Vue.use(ElementUI) 20 | 21 | Vue.config.productionTip = false 22 | 23 | router.beforeEach((to, from, next) => { 24 | if (!store.state.UserToken) { 25 | if ( 26 | to.matched.length > 0 && 27 | !to.matched.some(record => record.meta.requiresAuth) 28 | ) { 29 | next() 30 | } else { 31 | next({ path: '/login' }) 32 | } 33 | } else { 34 | if (!store.state.permission.permissionList) { 35 | store.dispatch('permission/FETCH_PERMISSION').then(() => { 36 | next({ path: to.path }) 37 | }) 38 | } else { 39 | if (to.path !== '/login') { 40 | next() 41 | } else { 42 | next(from.fullPath) 43 | } 44 | } 45 | } 46 | }) 47 | 48 | router.afterEach((to, from, next) => { 49 | var routerList = to.matched 50 | store.commit('setCrumbList', routerList) 51 | store.commit('permission/SET_CURRENT_MENU', to.name) 52 | }) 53 | 54 | /* eslint-disable no-new */ 55 | new Vue({ 56 | el: '#app', 57 | router, 58 | store, 59 | components: { App }, 60 | template: '' 61 | }) 62 | -------------------------------------------------------------------------------- /src/pages/home/component/charts.vue: -------------------------------------------------------------------------------- 1 | 75 | 76 | 83 | -------------------------------------------------------------------------------- /src/store/modules/permission.js: -------------------------------------------------------------------------------- 1 | import { fetchPermission } from '@/api/permission' 2 | import router, { DynamicRoutes } from '@/router/index' 3 | import { recursionRouter, setDefaultRoute } from '@/utils/recursion-router' 4 | import dynamicRouter from '@/router/dynamic-router' 5 | 6 | export default { 7 | namespaced: true, 8 | state: { 9 | permissionList: null /** 所有路由 */, 10 | sidebarMenu: [] /** 导航菜单 */, 11 | currentMenu: '' /** 当前active导航菜单 */ 12 | }, 13 | getters: {}, 14 | mutations: { 15 | SET_PERMISSION(state, routes) { 16 | state.permissionList = routes 17 | }, 18 | CLEAR_PERMISSION(state) { 19 | state.permissionList = null 20 | }, 21 | SET_MENU(state, menu) { 22 | state.sidebarMenu = menu 23 | }, 24 | CLEAR_MENU(state) { 25 | state.sidebarMenu = [] 26 | }, 27 | SET_CURRENT_MENU(state, currentMenu) { 28 | state.currentMenu = currentMenu 29 | } 30 | }, 31 | actions: { 32 | async FETCH_PERMISSION({ commit, state }) { 33 | let permissionList = await fetchPermission() 34 | 35 | /* 根据权限筛选出我们设置好的路由并加入到path=''的children */ 36 | let routes = recursionRouter(permissionList, dynamicRouter) 37 | let MainContainer = DynamicRoutes.find(v => v.path === '') 38 | let children = MainContainer.children 39 | children.push(...routes) 40 | 41 | /* 生成左侧导航菜单 */ 42 | commit('SET_MENU', children) 43 | 44 | /* 45 | 为所有有children的菜单路由设置第一个children为默认路由 46 | 主要是供面包屑用,防止点击面包屑后进入某个路由下的 '' 路由,比如/manage/ 47 | 而我们的路由是 48 | [ 49 | /manage/menu1, 50 | /manage/menu2 51 | ] 52 | */ 53 | setDefaultRoute([MainContainer]) 54 | 55 | /* 初始路由 */ 56 | let initialRoutes = router.options.routes 57 | 58 | /* 动态添加路由 */ 59 | router.addRoutes(DynamicRoutes) 60 | 61 | /* 完整的路由表 */ 62 | commit('SET_PERMISSION', [...initialRoutes, ...DynamicRoutes]) 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hongmu", 3 | "version": "1.0.0", 4 | "description": "A Vue.js project", 5 | "author": "fengkz ", 6 | "private": true, 7 | "scripts": { 8 | "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", 9 | "start": "npm run dev", 10 | "lint": "eslint --ext .js,.vue src", 11 | "build": "node build/build.js" 12 | }, 13 | "dependencies": { 14 | "axios": "^0.18.0", 15 | "element-ui": "^2.3.6", 16 | "vue": "^2.5.2", 17 | "vue-router": "^3.0.1", 18 | "vuex": "^3.0.1" 19 | }, 20 | "devDependencies": { 21 | "autoprefixer": "^7.1.2", 22 | "babel-core": "^6.22.1", 23 | "babel-eslint": "^8.2.1", 24 | "babel-helper-vue-jsx-merge-props": "^2.0.3", 25 | "babel-loader": "^7.1.1", 26 | "babel-plugin-syntax-jsx": "^6.18.0", 27 | "babel-plugin-transform-runtime": "^6.22.0", 28 | "babel-plugin-transform-vue-jsx": "^3.5.0", 29 | "babel-preset-env": "^1.3.2", 30 | "babel-preset-stage-2": "^6.22.0", 31 | "chalk": "^2.0.1", 32 | "copy-webpack-plugin": "^4.0.1", 33 | "css-loader": "^0.28.0", 34 | "eslint": "^4.15.0", 35 | "eslint-config-standard": "^10.2.1", 36 | "eslint-friendly-formatter": "^3.0.0", 37 | "eslint-loader": "^1.7.1", 38 | "eslint-plugin-import": "^2.7.0", 39 | "eslint-plugin-node": "^5.2.0", 40 | "eslint-plugin-promise": "^3.4.0", 41 | "eslint-plugin-standard": "^3.0.1", 42 | "eslint-plugin-vue": "^4.0.0", 43 | "extract-text-webpack-plugin": "^3.0.0", 44 | "file-loader": "^1.1.4", 45 | "friendly-errors-webpack-plugin": "^1.6.1", 46 | "html-webpack-plugin": "^2.30.1", 47 | "node-notifier": "^5.1.2", 48 | "node-sass": "^4.8.3", 49 | "optimize-css-assets-webpack-plugin": "^3.2.0", 50 | "ora": "^1.2.0", 51 | "portfinder": "^1.0.13", 52 | "postcss-import": "^11.0.0", 53 | "postcss-loader": "^2.0.8", 54 | "postcss-url": "^7.2.1", 55 | "rimraf": "^2.6.0", 56 | "sass-loader": "^7.0.1", 57 | "semver": "^5.3.0", 58 | "shelljs": "^0.7.6", 59 | "uglifyjs-webpack-plugin": "^1.1.1", 60 | "url-loader": "^0.5.8", 61 | "vue-loader": "^13.3.0", 62 | "vue-style-loader": "^3.0.1", 63 | "vue-template-compiler": "^2.5.2", 64 | "webpack": "^3.6.0", 65 | "webpack-bundle-analyzer": "^2.9.0", 66 | "webpack-dev-server": "^2.9.1", 67 | "webpack-merge": "^4.1.0" 68 | }, 69 | "engines": { 70 | "node": ">= 6.0.0", 71 | "npm": ">= 3.0.0" 72 | }, 73 | "browserslist": [ 74 | "> 1%", 75 | "last 2 versions", 76 | "not ie <= 8" 77 | ] 78 | } 79 | -------------------------------------------------------------------------------- /src/styles/_normalize.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | html { 4 | 5 | color: #000; 6 | background: #fff; 7 | overflow-y: scroll; 8 | -webkit-text-size-adjust: 100%; 9 | -ms-text-size-adjust: 100%; 10 | } 11 | 12 | html * { 13 | outline: none; 14 | -webkit-text-size-adjust: none; 15 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 16 | box-sizing: border-box; 17 | } 18 | 19 | html, 20 | body { 21 | font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif; 22 | height: 100%; 23 | width: 100%; 24 | overflow: auto; 25 | } 26 | 27 | body, 28 | div, 29 | dl, 30 | dt, 31 | dd, 32 | ul, 33 | ol, 34 | li, 35 | h1, 36 | h2, 37 | h3, 38 | h4, 39 | h5, 40 | h6, 41 | pre, 42 | code, 43 | form, 44 | fieldset, 45 | legend, 46 | input, 47 | textarea, 48 | p, 49 | blockquote, 50 | th, 51 | td, 52 | hr, 53 | button, 54 | article, 55 | aside, 56 | details, 57 | figcaption, 58 | figure, 59 | footer, 60 | header, 61 | hgroup, 62 | menu, 63 | nav, 64 | section { 65 | margin: 0; 66 | padding: 0; 67 | } 68 | 69 | input, 70 | select, 71 | textarea { 72 | font-size: 100%; 73 | } 74 | 75 | table { 76 | border-collapse: collapse; 77 | border-spacing: 0; 78 | } 79 | 80 | fieldset, 81 | img { 82 | border: 0; 83 | } 84 | 85 | abbr, 86 | acronym { 87 | border: 0; 88 | font-variant: normal; 89 | } 90 | 91 | del { 92 | text-decoration: line-through; 93 | } 94 | 95 | address, 96 | caption, 97 | cite, 98 | code, 99 | dfn, 100 | em, 101 | th, 102 | i, 103 | var { 104 | font-style: normal; 105 | font-weight: 500; 106 | } 107 | 108 | ol, 109 | ul { 110 | list-style: none; 111 | } 112 | 113 | caption, 114 | th { 115 | text-align: left; 116 | } 117 | 118 | h1, 119 | h2, 120 | h3, 121 | h4, 122 | h5, 123 | h6 { 124 | font-size: 100%; 125 | font-weight: 500; 126 | } 127 | 128 | q:before, 129 | q:after { 130 | content: ''; 131 | } 132 | 133 | sub, 134 | sup { 135 | font-size: 75%; 136 | line-height: 0; 137 | position: relative; 138 | vertical-align: baseline; 139 | } 140 | 141 | sup { 142 | top: -0.5em; 143 | } 144 | 145 | sub { 146 | bottom: -0.25em; 147 | } 148 | 149 | a:hover { 150 | text-decoration: underline; 151 | } 152 | 153 | ins, 154 | a, 155 | a:active, 156 | a:visited, 157 | a:link { 158 | text-decoration: none; 159 | } 160 | 161 | .clearfix { 162 | &:after { 163 | display: table; 164 | clear: both; 165 | content: ""; 166 | visibility: hidden; 167 | ; 168 | height: 0; 169 | } 170 | } 171 | 172 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // Template version: 1.2.6 3 | // see http://vuejs-templates.github.io/webpack for documentation. 4 | 5 | const path = require('path') 6 | 7 | module.exports = { 8 | dev: { 9 | 10 | // Paths 11 | assetsSubDirectory: 'static', 12 | assetsPublicPath: '/', 13 | proxyTable: {}, 14 | 15 | // Various Dev Server settings 16 | host: 'localhost', // can be overwritten by process.env.HOST 17 | port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined 18 | autoOpenBrowser: true, 19 | errorOverlay: true, 20 | notifyOnErrors: false, 21 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- 22 | 23 | // Use Eslint Loader? 24 | // If true, your code will be linted during bundling and 25 | // linting errors and warnings will be shown in the console. 26 | useEslint: true, 27 | // If true, eslint errors and warnings will also be shown in the error overlay 28 | // in the browser. 29 | showEslintErrorsInOverlay: false, 30 | 31 | /** 32 | * Source Maps 33 | */ 34 | 35 | // https://webpack.js.org/configuration/devtool/#development 36 | devtool: 'cheap-source-map', 37 | 38 | // If you have problems debugging vue-files in devtools, 39 | // set this to false - it *may* help 40 | // https://vue-loader.vuejs.org/en/options.html#cachebusting 41 | cacheBusting: true, 42 | 43 | // CSS Sourcemaps off by default because relative paths are "buggy" 44 | // with this option, according to the CSS-Loader README 45 | // (https://github.com/webpack/css-loader#sourcemaps) 46 | // In our experience, they generally work as expected, 47 | // just be aware of this issue when enabling this option. 48 | cssSourceMap: false, 49 | }, 50 | 51 | build: { 52 | // Template for index.html 53 | index: path.resolve(__dirname, '../dist/index.html'), 54 | 55 | // Paths 56 | assetsRoot: path.resolve(__dirname, '../dist'), 57 | assetsSubDirectory: 'static', 58 | 59 | // you can set by youself according to actual condition 60 | assetsPublicPath: './', 61 | 62 | /** 63 | * Source Maps 64 | */ 65 | 66 | productionSourceMap: false, 67 | // https://webpack.js.org/configuration/devtool/#production 68 | devtool: '#source-map', 69 | 70 | // Gzip off by default as many popular static hosts such as 71 | // Surge or Netlify already gzip all static assets for you. 72 | // Before setting to `true`, make sure to: 73 | // npm install --save-dev compression-webpack-plugin 74 | productionGzip: false, 75 | productionGzipExtensions: ['js', 'css'], 76 | 77 | // Run the build command with an extra argument to 78 | // View the bundle analyzer report after build finishes: 79 | // `npm run build --report` 80 | // Set to `true` or `false` to always turn it on or off 81 | bundleAnalyzerReport: process.env.npm_config_report 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/config/httpConfig.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import store from '@/store/index.js' 3 | import baseURL from './baseUrl' 4 | import { Message } from 'element-ui' 5 | const http = {} 6 | 7 | var instance = axios.create({ 8 | timeout: 5000, 9 | baseURL 10 | }) 11 | 12 | // 添加请求拦截器 13 | instance.interceptors.request.use( 14 | function(config) { 15 | // 请求头添加token 16 | if (store.state.UserToken) { 17 | config.headers.Authorization = store.state.UserToken 18 | } 19 | return config 20 | }, 21 | function(error) { 22 | return Promise.reject(error) 23 | } 24 | ) 25 | 26 | // 响应拦截器即异常处理 27 | instance.interceptors.response.use( 28 | response => { 29 | return response.data 30 | }, 31 | err => { 32 | if (err && err.response) { 33 | switch (err.response.status) { 34 | case 400: 35 | err.message = '请求出错' 36 | break 37 | case 401: 38 | Message.warning({ 39 | message: '授权失败,请重新登录' 40 | }) 41 | store.commit('LOGIN_OUT') 42 | setTimeout(() => { 43 | window.location.reload() 44 | }, 1000) 45 | 46 | return 47 | case 403: 48 | err.message = '拒绝访问' 49 | break 50 | case 404: 51 | err.message = '请求错误,未找到该资源' 52 | break 53 | case 500: 54 | err.message = '服务器端出错' 55 | break 56 | } 57 | } else { 58 | err.message = '连接服务器失败' 59 | } 60 | Message.error({ 61 | message: err.message 62 | }) 63 | return Promise.reject(err.response) 64 | } 65 | ) 66 | 67 | http.get = function(url, options) { 68 | return new Promise((resolve, reject) => { 69 | instance 70 | .get(url, options) 71 | .then(response => { 72 | if (response.code === 0) { 73 | resolve(response.data) 74 | } else { 75 | Message.error({ 76 | message: response.message 77 | }) 78 | reject(response.message) 79 | } 80 | }) 81 | .catch(e => { 82 | console.log(e) 83 | }) 84 | }) 85 | } 86 | 87 | http.post = function(url, data, options) { 88 | return new Promise((resolve, reject) => { 89 | instance 90 | .post(url, data, options) 91 | .then(response => { 92 | if (response.code === 0) { 93 | resolve(response.data) 94 | } else { 95 | Message.error({ 96 | message: response.message 97 | }) 98 | reject(response.message) 99 | } 100 | }) 101 | .catch(e => { 102 | console.log(e) 103 | }) 104 | }) 105 | } 106 | 107 | export default http 108 | -------------------------------------------------------------------------------- /src/pages/login/login.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 43 | 44 | 117 | -------------------------------------------------------------------------------- /src/router/dynamic-router.js: -------------------------------------------------------------------------------- 1 | /* 订单管理 */ 2 | const Order = () => import('pages/order-manage') 3 | const OrderList = () => import('pages/order-manage/order-list') 4 | const ProductManage = () => import('pages/order-manage/product-manage') 5 | const ProductionList = () => 6 | import('pages/order-manage/product-manage/production-list') 7 | const ReviewManage = () => 8 | import('pages/order-manage/product-manage/review-manage') 9 | const ReturnGoods = () => import('pages/order-manage/return-goods') 10 | 11 | /* 产品管理 */ 12 | const Goods = () => import('pages/goods-manage') 13 | const GoodsList = () => import('pages/goods-manage/goods-list') 14 | const GoodsClassify = () => import('pages/goods-manage/goods-classify') 15 | 16 | /* 需要权限判断的路由 */ 17 | const dynamicRoutes = [ 18 | { 19 | path: '/order', 20 | component: Order, 21 | name: 'order-manage', 22 | meta: { 23 | name: '订单管理', 24 | icon: 'icon-email' 25 | }, 26 | children: [ 27 | { 28 | path: 'list', 29 | name: 'order-list', 30 | component: OrderList, 31 | meta: { 32 | name: '订单列表', 33 | icon: 'icon-quit' 34 | } 35 | }, 36 | { 37 | path: 'product', 38 | name: 'product-manage', 39 | component: ProductManage, 40 | meta: { 41 | name: '生产管理', 42 | icon: 'icon-service' 43 | }, 44 | children: [ 45 | { 46 | path: 'list', 47 | name: 'product-list', 48 | component: ProductionList, 49 | meta: { 50 | name: '生产列表', 51 | icon: 'icon-nav' 52 | } 53 | }, 54 | { 55 | path: 'review', 56 | name: 'review-manage', 57 | component: ReviewManage, 58 | meta: { 59 | name: '审核管理', 60 | icon: 'icon-finance-manage' 61 | } 62 | } 63 | ] 64 | }, 65 | { 66 | path: 'returnGoods', 67 | name: 'return-goods', 68 | component: ReturnGoods, 69 | meta: { 70 | name: '退货管理', 71 | icon: 'icon-product-manage' 72 | } 73 | } 74 | ] 75 | }, 76 | { 77 | path: '/goods', 78 | component: Goods, 79 | name: 'goods', 80 | meta: { 81 | name: '产品管理', 82 | icon: 'icon-order-manage' 83 | }, 84 | children: [ 85 | { 86 | path: 'list', 87 | name: 'goods-list', 88 | component: GoodsList, 89 | meta: { 90 | name: '产品列表', 91 | icon: 'icon-home' 92 | } 93 | }, 94 | { 95 | path: 'classify', 96 | name: 'goods-classify', 97 | component: GoodsClassify, 98 | meta: { 99 | name: '产品分类', 100 | icon: 'icon-product-manage' 101 | } 102 | } 103 | ] 104 | } 105 | ] 106 | 107 | export default dynamicRoutes 108 | -------------------------------------------------------------------------------- /src/pages/layout/component/main-content/top-aside.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 69 | 70 | 187 | -------------------------------------------------------------------------------- /src/pages/errorPage/403.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 32 | 221 | -------------------------------------------------------------------------------- /src/pages/errorPage/404.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 32 | 221 | --------------------------------------------------------------------------------