├── src ├── assets │ ├── scss │ │ ├── common │ │ │ ├── global.scss │ │ │ ├── transition.scss │ │ │ └── var.scss │ │ ├── mixins │ │ │ ├── config.scss │ │ │ ├── utils.scss │ │ │ ├── function.scss │ │ │ ├── _button.scss │ │ │ └── mixins.scss │ │ └── theme.scss │ └── img │ │ └── background.jpg ├── plugins │ ├── index.js │ ├── draggable.js │ ├── store.js │ └── element.js ├── utils │ ├── event-bus.js │ ├── util.js │ ├── fetch.js │ └── dom.js ├── constants │ └── password.js ├── components │ ├── multi-tab │ │ ├── index.js │ │ ├── __tests__ │ │ │ └── multi-tab.test.js │ │ └── multi-tab.vue │ └── layouts │ │ ├── index.js │ │ ├── route-view.js │ │ ├── global-layout.vue │ │ ├── menu.js │ │ └── page-layout.vue ├── App.vue ├── store │ ├── modules │ │ ├── app.js │ │ └── auth.js │ ├── index.js │ └── getters.js ├── views │ ├── hello.vue │ ├── component.vue │ ├── page-403.vue │ ├── page-404.vue │ └── login.vue ├── main.js ├── api │ └── auth.js ├── router.js ├── config │ └── route.config.js └── permission.js ├── .browserslistrc ├── .env ├── .env.test1 ├── babel.config.js ├── public ├── favicon.ico └── index.html ├── tests └── unit │ ├── .eslintrc.js │ ├── setup.js │ └── utils.js ├── postcss.config.js ├── .editorconfig ├── .gitignore ├── .eslintrc.js ├── vue.config.js ├── README.md ├── jest.config.js └── package.json /src/assets/scss/common/global.scss: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | -------------------------------------------------------------------------------- /src/plugins/index.js: -------------------------------------------------------------------------------- 1 | import './element' 2 | import './store' 3 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | VUE_APP_BASEURL=/api/ 2 | VUE_APP_PROXY_TARGET=http://127.0.0.1:3000 3 | -------------------------------------------------------------------------------- /.env.test1: -------------------------------------------------------------------------------- 1 | VUE_APP_BASEURL=/api/ 2 | VUE_APP_PROXY_TARGET=http://127.0.0.1:3000 3 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wayejs/waye-pro/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/utils/event-bus.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | export const EventBus = new Vue() 3 | -------------------------------------------------------------------------------- /tests/unit/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | jest: true 4 | } 5 | } -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {} 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/constants/password.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 三个姓陈的 + 一个姓林 3 | */ 4 | export const PASSPORD_HASH = 'cccl' 5 | -------------------------------------------------------------------------------- /src/components/multi-tab/index.js: -------------------------------------------------------------------------------- 1 | import MultiTab from './multi-tab.vue' 2 | export default MultiTab 3 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/assets/img/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wayejs/waye-pro/HEAD/src/assets/img/background.jpg -------------------------------------------------------------------------------- /src/plugins/draggable.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import draggable from 'vuedraggable' 3 | Vue.component('draggable', draggable) 4 | -------------------------------------------------------------------------------- /src/assets/scss/mixins/config.scss: -------------------------------------------------------------------------------- 1 | $namespace: 'ql'; 2 | $element-separator: '__'; 3 | $modifier-separator: '--'; 4 | $state-prefix: 'is-'; 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,ts,tsx,vue}] 2 | indent_style = space 3 | indent_size = 2 4 | trim_trailing_whitespace = true 5 | insert_final_newline = true 6 | -------------------------------------------------------------------------------- /src/components/layouts/index.js: -------------------------------------------------------------------------------- 1 | import GlobalLayout from './global-layout.vue' 2 | import RouteView from './route-view' 3 | export { 4 | GlobalLayout, 5 | RouteView 6 | } 7 | -------------------------------------------------------------------------------- /src/plugins/store.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueStorage from 'vue-ls' 3 | 4 | Vue.use(VueStorage, { 5 | namespace: 'wypro__', 6 | name: 'ls', 7 | storage: 'local' 8 | }) 9 | -------------------------------------------------------------------------------- /src/store/modules/app.js: -------------------------------------------------------------------------------- 1 | const app = { 2 | state: { 3 | multiTab: true 4 | }, 5 | mutations: { 6 | 7 | }, 8 | actions: { 9 | 10 | } 11 | } 12 | 13 | export default app 14 | -------------------------------------------------------------------------------- /src/views/hello.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | -------------------------------------------------------------------------------- /src/views/component.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 16 | -------------------------------------------------------------------------------- /src/assets/scss/theme.scss: -------------------------------------------------------------------------------- 1 | @import "common/var.scss"; 2 | 3 | @import "common/global.scss"; 4 | 5 | /* 改变 icon 字体路径变量,必需 */ 6 | $--font-path: '~element-ui/lib/theme-chalk/fonts'; 7 | 8 | @import "~element-ui/packages/theme-chalk/src/index"; 9 | @import "~@waye/ui/packages/theme/src/index"; 10 | -------------------------------------------------------------------------------- /tests/unit/setup.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Element from 'element-ui' 3 | 4 | import clickoutside from 'element-ui/lib/utils/clickoutside' 5 | 6 | import Waye from '@waye/ui' 7 | 8 | Vue.use(Element, { size: 'medium' }) 9 | Vue.use(Waye) 10 | 11 | Vue.directive('clickoutside', clickoutside) 12 | -------------------------------------------------------------------------------- /src/views/page-403.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 16 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import './plugins' 3 | import App from './App.vue' 4 | import router from './router' 5 | import store from './store' 6 | 7 | import './permission' 8 | 9 | Vue.config.productionTip = false 10 | 11 | new Vue({ 12 | router, 13 | store, 14 | render: h => h(App) 15 | }).$mount('#app') 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | .history 23 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | import auth from './modules/auth' 5 | import app from './modules/app' 6 | import getters from './getters' 7 | Vue.use(Vuex) 8 | 9 | const store = new Vuex.Store({ 10 | modules: { 11 | app, 12 | auth 13 | }, 14 | getters 15 | }) 16 | 17 | export default store 18 | -------------------------------------------------------------------------------- /src/store/getters.js: -------------------------------------------------------------------------------- 1 | const getters = { 2 | userName: state => state.auth.userName, 3 | userCode: state => state.auth.userCode, 4 | userId: state => state.auth.userId, 5 | permissions: state => state.auth.permissions, 6 | asyncRoutes: state => state.auth.asyncRoutes, 7 | 8 | multiTab: state => state.app.multiTab 9 | } 10 | 11 | export default getters 12 | -------------------------------------------------------------------------------- /src/plugins/element.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Element from 'element-ui' 3 | import 'element-ui/lib/theme-chalk/reset.css' 4 | import '@/assets/scss/theme.scss' 5 | 6 | import clickoutside from 'element-ui/lib/utils/clickoutside' 7 | 8 | import Waye from '@waye/ui' 9 | 10 | Vue.use(Element, { size: 'medium' }) 11 | Vue.use(Waye) 12 | 13 | Vue.directive('clickoutside', clickoutside) 14 | -------------------------------------------------------------------------------- /src/components/layouts/route-view.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'QlRouteView', 3 | render () { 4 | const { $route: { meta }, $store: { getters } } = this 5 | 6 | const inKeep = ( 7 | 8 | 9 | 10 | ) 11 | const notKeep = ( 12 | 13 | ) 14 | return meta.keepAlive || getters.multiTab ? inKeep : notKeep 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | 'extends': [ 7 | 'plugin:vue/essential', 8 | '@vue/standard' 9 | ], 10 | rules: { 11 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 12 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' 13 | }, 14 | parserOptions: { 15 | parser: 'babel-eslint' 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | 3 | const plugins = [ 4 | new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /zh-cn/) 5 | ] 6 | 7 | module.exports = { 8 | lintOnSave: true, 9 | configureWebpack: { 10 | plugins 11 | }, 12 | 13 | devServer: { 14 | port: '9000', 15 | proxy: { 16 | '/api': { 17 | target: process.env.VUE_APP_PROXY_TARGET, 18 | ws: false, 19 | changeOrigin: true 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/components/layouts/global-layout.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 24 | -------------------------------------------------------------------------------- /src/api/auth.js: -------------------------------------------------------------------------------- 1 | import { post, getFileUrl, postByForm } from '../utils/fetch' 2 | 3 | export function login (params) { 4 | return post('system/mgr/login', params) 5 | } 6 | 7 | export function logout () { 8 | return post('system/mgr/logout') 9 | } 10 | 11 | /** 12 | * 获取验证码 13 | * @export 14 | * @param {any} time 时间戳 15 | */ 16 | export function validCodeUrl (time) { 17 | return getFileUrl('system/kaptcha', { time }) 18 | } 19 | 20 | /** 21 | * 修改密码 22 | * @param {*} params 参数 23 | */ 24 | export function changePwd (params) { 25 | return postByForm('system/user/modifyPwd', params) 26 | } 27 | -------------------------------------------------------------------------------- /src/assets/scss/common/transition.scss: -------------------------------------------------------------------------------- 1 | .fade-enter-active, .fade-leave-active { 2 | transition: opacity .5s ease; 3 | } 4 | .fade-enter, .fade-leave-active { 5 | opacity: 0 6 | } 7 | .slide-left-enter, .slide-right-leave-active { 8 | opacity: 0; 9 | -webkit-transform: translate(30px, 0); 10 | transform: translate(30px, 0); 11 | } 12 | .slide-left-leave-active, .slide-right-enter { 13 | opacity: 0; 14 | -webkit-transform: translate(-30px, 0); 15 | transform: translate(-30px, 0); 16 | } 17 | .slide-view { 18 | position: absolute; 19 | width: 100%; 20 | transition: all .5s cubic-bezier(.55,0,.1,1); 21 | } 22 | -------------------------------------------------------------------------------- /src/components/multi-tab/__tests__/multi-tab.test.js: -------------------------------------------------------------------------------- 1 | import { shallowMount } from '@vue/test-utils' 2 | import MultiTab from '@/components/multi-tab' 3 | 4 | const $route = { 5 | fullPath: '/about', 6 | meta: { 7 | title: '关于我们' 8 | }, 9 | path: '/about' 10 | } 11 | const $router = { 12 | push () { 13 | 14 | } 15 | } 16 | 17 | describe('MultiTab', () => { 18 | it('init multi-tab', async () => { 19 | const wrapper = shallowMount(MultiTab, { 20 | mocks: { 21 | $route, 22 | $router 23 | } 24 | }) 25 | 26 | expect(wrapper.vm.activeRoute).toBe('/about') 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /src/router.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | 4 | const baseRoutes = [ 5 | { 6 | path: '/login', 7 | name: 'login', 8 | component: () => import(/* webpackChunkName: "login" */ '@/views/login') 9 | }, 10 | { 11 | path: '/403', 12 | name: 'page403', 13 | component: () => import(/* webpackChunkName: "403" */ '@/views/page-403') 14 | }, { 15 | path: '/404', 16 | name: 'page404', 17 | component: () => import(/* webpackChunkName: "404" */ '@/views/page-404') 18 | } 19 | ] 20 | 21 | Vue.use(Router) 22 | const router = new Router({ 23 | routes: baseRoutes 24 | }) 25 | export default router 26 | -------------------------------------------------------------------------------- /tests/unit/utils.js: -------------------------------------------------------------------------------- 1 | // import moment from 'moment' 2 | // import MockDate from 'mockdate' 3 | import Vue from 'vue' 4 | 5 | // export function setMockDate (dateString = '2017-09-18T03:30:07.795') { 6 | // MockDate.set(moment(dateString)) 7 | // } 8 | 9 | // export function resetMockDate () { 10 | // MockDate.reset() 11 | // } 12 | 13 | export function asyncExpect (fn, timeout) { 14 | return new Promise(resolve => { 15 | if (typeof timeout === 'number') { 16 | setTimeout(() => { 17 | fn() 18 | resolve() 19 | }, timeout) 20 | } else { 21 | Vue.nextTick(() => { 22 | fn() 23 | resolve() 24 | }) 25 | } 26 | }) 27 | } 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # waye-pro 2 | 3 | > 通过 vue-cli 生成的项目脚手架 4 | 5 | ## 使用 6 | 7 | ```bash 8 | git clone https://github.com/wayejs/waye-pro.git project-xxx 9 | cd project-xxx 10 | git remote remove origin 11 | git remote add origin [新的git仓库链接] 12 | git push -u origin master 13 | yarn install 14 | ``` 15 | 16 | ### Compiles and hot-reloads for development 17 | ``` 18 | yarn run serve 19 | ``` 20 | 21 | ### Compiles and minifies for production 22 | ``` 23 | yarn run build 24 | ``` 25 | 26 | ### Run your tests 27 | ``` 28 | yarn run test 29 | ``` 30 | 31 | ### Lints and fixes files 32 | ``` 33 | yarn run lint 34 | ``` 35 | 36 | ### Customize configuration 37 | See [Configuration Reference](https://cli.vuejs.org/config/). 38 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | waye-pro 10 | 11 | 12 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/views/page-404.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 31 | -------------------------------------------------------------------------------- /src/assets/scss/mixins/utils.scss: -------------------------------------------------------------------------------- 1 | @mixin utils-user-select($value) { 2 | -moz-user-select: $value; 3 | -webkit-user-select: $value; 4 | -ms-user-select: $value; 5 | } 6 | 7 | @mixin utils-clearfix { 8 | $selector: &; 9 | 10 | @at-root { 11 | #{$selector}::before, 12 | #{$selector}::after { 13 | display: table; 14 | content: ""; 15 | } 16 | #{$selector}::after { 17 | clear: both 18 | } 19 | } 20 | } 21 | 22 | @mixin utils-vertical-center { 23 | $selector: &; 24 | 25 | @at-root { 26 | #{$selector}::after { 27 | display: inline-block; 28 | content: ""; 29 | height: 100%; 30 | vertical-align: middle 31 | } 32 | } 33 | } 34 | 35 | @mixin utils-ellipsis { 36 | overflow: hidden; 37 | text-overflow: ellipsis; 38 | white-space: nowrap; 39 | } -------------------------------------------------------------------------------- /src/config/route.config.js: -------------------------------------------------------------------------------- 1 | import { RouteView, GlobalLayout } from '@/components/layouts' 2 | const mainRouteConfig = [ 3 | { 4 | name: 'dashboard', 5 | icon: 'data-montior', 6 | label: '面板', 7 | component: RouteView, 8 | children: [ 9 | { 10 | name: 'hello', 11 | component: () => import('@/views/hello'), 12 | label: 'Hello' 13 | }, 14 | { 15 | name: 'component', 16 | component: () => import('@/views/component'), 17 | label: '组件' 18 | } 19 | ] 20 | } 21 | ] 22 | const root = { 23 | path: '/', 24 | name: 'home', 25 | label: '首页', 26 | redirect: '/hello', 27 | component: GlobalLayout, 28 | children: mainRouteConfig 29 | } 30 | 31 | const asyncRouterConfig = [ 32 | root, 33 | { 34 | path: '*', 35 | redirect: '/404', 36 | hidden: true 37 | } 38 | ] 39 | 40 | export { 41 | mainRouteConfig 42 | } 43 | export default asyncRouterConfig 44 | -------------------------------------------------------------------------------- /src/utils/util.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 去掉重复 3 | * @param {Array} value 数组值 4 | */ 5 | export function uniq (value) { 6 | let setValue = new Set(value) 7 | return Array.from(setValue) 8 | } 9 | 10 | export const getSearchParams = () => { 11 | let params = decodeURI(location.search.slice(1)) 12 | let ret = {} 13 | if (params) { 14 | params = params.split('&') 15 | params.forEach(param => { 16 | let item = param.split('=') 17 | ret[item[0]] = item[1] 18 | }) 19 | } 20 | return ret 21 | } 22 | 23 | /** 24 | * 分组数据 25 | * @param {*} data 数据列表 26 | * @param {*} size 每组显示的条数 27 | */ 28 | export const groupBy = (data, size) => { 29 | const result = data.reduce((r, t) => { 30 | r.current.push(t) 31 | if (r.current.length === size) { 32 | r.list.push(r.current) 33 | r.current = [] 34 | } 35 | return r 36 | }, { list: [], current: [] }) 37 | 38 | if (result.current.length) { 39 | result.list.push(result.current) 40 | } 41 | 42 | return result.list 43 | } 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/assets/scss/mixins/function.scss: -------------------------------------------------------------------------------- 1 | @import "config"; 2 | 3 | /* BEM support Func 4 | -------------------------- */ 5 | @function selectorToString($selector) { 6 | $selector: inspect($selector); 7 | $selector: str-slice($selector, 2, -2); 8 | @return $selector; 9 | } 10 | 11 | @function containsModifier($selector) { 12 | $selector: selectorToString($selector); 13 | 14 | @if str-index($selector, $modifier-separator) { 15 | @return true; 16 | } @else { 17 | @return false; 18 | } 19 | } 20 | 21 | @function containWhenFlag($selector) { 22 | $selector: selectorToString($selector); 23 | 24 | @if str-index($selector, '.' + $state-prefix) { 25 | @return true 26 | } @else { 27 | @return false 28 | } 29 | } 30 | 31 | @function containPseudoClass($selector) { 32 | $selector: selectorToString($selector); 33 | 34 | @if str-index($selector, ':') { 35 | @return true 36 | } @else { 37 | @return false 38 | } 39 | } 40 | 41 | @function hitAllSpecialNestRule($selector) { 42 | 43 | @return containsModifier($selector) or containWhenFlag($selector) or containPseudoClass($selector); 44 | } 45 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | collectCoverage: process.env.COVERAGE === 'true', 3 | collectCoverageFrom: [ 4 | '**/*.{js,vue}', 5 | '!**/node_modules/**', 6 | '!**/lib/**', 7 | '!**/tests/**', 8 | '!**/build/**' 9 | ], 10 | coverageReporters: ['html', 'text-summary'], 11 | moduleFileExtensions: ['js', 'jsx', 'json', 'vue'], 12 | setupFiles: ['tests/unit/setup'], 13 | transform: { 14 | '^.+\\.vue$': 'vue-jest', 15 | '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 16 | 'jest-transform-stub', 17 | '^.+\\.js?$': 'babel-jest', 18 | '^.+\\.jsx?$': 'babel-jest' 19 | }, 20 | moduleNameMapper: { 21 | '^@/(.*)$': '/src/$1' 22 | }, 23 | snapshotSerializers: ['jest-serializer-vue'], 24 | testMatch: [ 25 | '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.test.(js|jsx|ts|tsx)' 26 | ], 27 | testPathIgnorePatterns: ['/.history/', '/node_modules/'], 28 | testURL: 'http://localhost/', 29 | transformIgnorePatterns: ['/node_modules/'], 30 | watchPlugins: [ 31 | 'jest-watch-typeahead/filename', 32 | 'jest-watch-typeahead/testname' 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "waye-pro", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint", 9 | "test": "vue-cli-service test:unit" 10 | }, 11 | "dependencies": { 12 | "@waye/ui": "^0.1.5", 13 | "axios": "^0.19.0", 14 | "core-js": "^2.6.5", 15 | "element-ui": "^2.11.1", 16 | "lodash": "^4.17.15", 17 | "md5": "^2.2.1", 18 | "moment": "^2.24.0", 19 | "nprogress": "^0.2.0", 20 | "vue": "^2.6.10", 21 | "vue-ls": "^3.2.1", 22 | "vue-router": "^3.0.3", 23 | "vuex": "^3.0.1" 24 | }, 25 | "devDependencies": { 26 | "@vue/cli-plugin-babel": "^3.11.0", 27 | "@vue/cli-plugin-eslint": "^3.11.0", 28 | "@vue/cli-service": "^3.11.0", 29 | "@vue/eslint-config-standard": "^4.0.0", 30 | "@vue/cli-plugin-unit-jest": "^3.8.0", 31 | "@vue/test-utils": "^1.0.0-beta.29", 32 | "babel-cli": "^6.26.0", 33 | "babel-core": "7.0.0-bridge.0", 34 | "babel-eslint": "^10.0.1", 35 | "babel-jest": "^24.8.0", 36 | "eslint": "^5.16.0", 37 | "eslint-plugin-vue": "^5.0.0", 38 | "node-sass": "^4.9.0", 39 | "sass-loader": "^7.1.0", 40 | "vue-template-compiler": "^2.6.10" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/components/multi-tab/multi-tab.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 60 | 61 | 82 | -------------------------------------------------------------------------------- /src/permission.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import router from './router' 3 | import store from './store' 4 | 5 | import NProgress from 'nprogress' 6 | import 'nprogress/nprogress.css' 7 | 8 | import { SUPER_ADMIN } from './store/modules/auth' 9 | NProgress.configure({ 10 | showSpinner: false 11 | }) 12 | 13 | const whiteList = ['login'] 14 | router.beforeEach((to, from, next) => { 15 | NProgress.start() 16 | if (whiteList.includes(to.name)) { 17 | next() 18 | } else if (Vue.ls.get('userInfo')) { 19 | if (store.getters.asyncRoutes.length === 0) { 20 | store 21 | .dispatch('genAsyncRoutes', store.getters.permissions) 22 | .then(() => { 23 | router.addRoutes(store.getters.asyncRoutes) 24 | const redirect = decodeURIComponent(from.query.redirect || to.path) 25 | if (to.path === redirect) { 26 | next({ 27 | ...to, 28 | replace: true 29 | }) 30 | } else { 31 | next({ 32 | path: redirect 33 | }) 34 | } 35 | NProgress.done() 36 | }).catch(() => { 37 | NProgress.done() 38 | }) 39 | } else { 40 | next() 41 | } 42 | } else { 43 | next({ 44 | path: '/login', 45 | query: { 46 | redirect: to.fullpath 47 | } 48 | }) 49 | } 50 | }) 51 | 52 | router.afterEach(() => { 53 | NProgress.done() 54 | }) 55 | 56 | /** 57 | * 操作权限指令 58 | * 用法 59 | * - 在操作按钮组件上 使用 v-action:[actionName], 如下 60 | * 添加 61 | * 编辑 62 | * - 用户没有对应操作权限时,操作按钮会不显示 63 | * - actionName = view|add|edit|delete|import|export|disable|enable|reset, 目前只有add 和 edit 64 | */ 65 | const vAction = Vue.directive('action', { 66 | bind: function (el, binding, vnode) { 67 | const actionName = binding.arg 68 | const permissions = store.getters.permissions 69 | const code = vnode.context.$route.meta.code 70 | let isAdmin = store.getters.userCode === SUPER_ADMIN 71 | let action = `${code}:${actionName}` 72 | 73 | if (!permissions.includes(action) && !isAdmin) { 74 | setTimeout(() => { 75 | if (el.parentNode == null) { 76 | el.style.display = 'none' 77 | } else { 78 | el.parentNode.removeChild(el) 79 | } 80 | }, 10) 81 | } 82 | } 83 | }) 84 | 85 | export { 86 | vAction 87 | } 88 | -------------------------------------------------------------------------------- /src/assets/scss/mixins/_button.scss: -------------------------------------------------------------------------------- 1 | @import "../common/var"; 2 | @mixin button-plain($color) { 3 | color: $color; 4 | background: mix($--color-white, $color, 90%); 5 | border-color: mix($--color-white, $color, 60%); 6 | 7 | &:hover, 8 | &:focus { 9 | background: $color; 10 | border-color: $color; 11 | color: $--color-white; 12 | } 13 | 14 | &:active { 15 | background: mix($--color-black, $color, $--button-active-shade-percent); 16 | border-color: mix($--color-black, $color, $--button-active-shade-percent); 17 | color: $--color-white; 18 | outline: none; 19 | } 20 | 21 | &.is-disabled { 22 | &, 23 | &:hover, 24 | &:focus, 25 | &:active { 26 | color: mix($--color-white, $color, 40%); 27 | background-color: mix($--color-white, $color, 90%); 28 | border-color: mix($--color-white, $color, 80%); 29 | } 30 | } 31 | } 32 | 33 | @mixin button-variant($color, $background-color, $border-color) { 34 | color: $color; 35 | background-color: $background-color; 36 | border-color: $border-color; 37 | 38 | &:hover, 39 | &:focus { 40 | background: mix($--color-white, $background-color, $--button-hover-tint-percent); 41 | border-color: mix($--color-white, $border-color, $--button-hover-tint-percent); 42 | color: $color; 43 | } 44 | 45 | &:active { 46 | background: mix($--color-black, $background-color, $--button-active-shade-percent); 47 | border-color: mix($--color-black, $border-color, $--button-active-shade-percent); 48 | color: $color; 49 | outline: none; 50 | } 51 | 52 | &.is-active { 53 | background: mix($--color-black, $background-color, $--button-active-shade-percent); 54 | border-color: mix($--color-black, $border-color, $--button-active-shade-percent); 55 | color: $color; 56 | } 57 | 58 | &.is-disabled { 59 | &, 60 | &:hover, 61 | &:focus, 62 | &:active { 63 | color: $--color-white; 64 | background-color: mix($background-color, $--color-white); 65 | border-color: mix($border-color, $--color-white); 66 | } 67 | } 68 | 69 | &.is-plain { 70 | @include button-plain($background-color); 71 | } 72 | } 73 | 74 | @mixin button-size($padding-vertical, $padding-horizontal, $font-size, $border-radius) { 75 | padding: $padding-vertical $padding-horizontal; 76 | font-size: $font-size; 77 | border-radius: $border-radius; 78 | &.is-round { 79 | padding: $padding-vertical $padding-horizontal; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/components/layouts/menu.js: -------------------------------------------------------------------------------- 1 | import { mapGetters } from 'vuex' 2 | export default { 3 | name: 'QlMenu', 4 | props: { 5 | collapse: Boolean 6 | }, 7 | computed: { 8 | ...mapGetters(['asyncRoutes']), 9 | 10 | activeIndex () { 11 | return this.$route.path 12 | }, 13 | 14 | menuList () { 15 | let home = this.asyncRoutes.find(item => item.name === 'home') 16 | return home ? home.children : [] 17 | } 18 | }, 19 | methods: { 20 | genMenus (menus) { 21 | let h = this.$createElement 22 | let elements = [] 23 | menus.forEach(item => { 24 | if (item.children) { 25 | elements.push( 26 | h('el-submenu', { 27 | props: { 28 | popperClass: 'ql-layout__submenu', 29 | index: item.path 30 | }, 31 | key: item.path 32 | }, [ 33 | h('div', { 34 | slot: 'title' 35 | }, [ 36 | h('wy-icon', { 37 | props: { 38 | name: item.icon 39 | } 40 | }), 41 | h('span', { 42 | slot: 'title' 43 | }, item.label || item.meta.title) 44 | ]), 45 | this.genMenus(item.children) 46 | ]) 47 | ) 48 | } else { 49 | elements.push( 50 | h('el-menu-item', { 51 | props: { 52 | index: item.path 53 | }, 54 | key: item.path 55 | }, [ 56 | h('wy-icon', { 57 | props: { 58 | name: item.icon 59 | } 60 | }), 61 | h('span', { 62 | slot: 'title' 63 | }, item.label || item.meta.title) 64 | ]) 65 | ) 66 | } 67 | }) 68 | return elements 69 | } 70 | }, 71 | render (h) { 72 | if (!this.menuList.length) { 73 | return h('div', { 74 | class: { 75 | 'ql-layout__aside-empty': true, 76 | 'is-collapse': this.collapse 77 | } 78 | }, '无菜单权限,请联系客服') 79 | } 80 | return h('el-menu', { 81 | class: { 82 | 'ql-layout__aside-menu': true 83 | }, 84 | props: { 85 | router: true, 86 | collapse: this.collapse, 87 | defaultActive: this.activeIndex, 88 | backgroundColor: '#00142a', 89 | activeTextColor: '#409EFF', 90 | textColor: 'hsla(0, 0%, 100%, .65)', 91 | mode: 'vertical' 92 | } 93 | }, this.genMenus(this.menuList)) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/store/modules/auth.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import { login, logout } from '../../api/auth' 3 | import navConfig from '@/config/route.config' 4 | import cloneDeep from 'lodash/cloneDeep' 5 | let wayeUserInfo = Vue.ls.get('userInfo') 6 | let codeMap = {} 7 | 8 | export const SUPER_ADMIN = 'admin' 9 | const parseRoutes = (data, parentPath = '') => { 10 | data.forEach(item => { 11 | let name = item.name 12 | item.path = item.path || `${parentPath}/${name}` 13 | 14 | if (item.children) { 15 | let path = `${parentPath}/${name}` 16 | if (name === 'home') { 17 | path = '' 18 | } 19 | parseRoutes(item.children, path) 20 | } else { 21 | item.meta = item.meta || {} 22 | item.meta.title = item.label || '' 23 | item.meta.code = item.meta.code || item.code 24 | codeMap[item.code] = item.path 25 | } 26 | }) 27 | } 28 | const filterAsyncRouter = (data, filterPaths) => { 29 | return data.filter(route => { 30 | if (hasPermission(filterPaths, route)) { 31 | if (route.children) { 32 | route.children = filterAsyncRouter(route.children, filterPaths) 33 | } 34 | 35 | return true 36 | } 37 | return false 38 | }) 39 | } 40 | 41 | const hasPermission = (paths, route) => { 42 | if (route.hidden) { 43 | return true 44 | } 45 | return paths.some(path => path.indexOf(route.path) > -1) 46 | } 47 | 48 | const accessedRoutePaths = (permissions, codeMap) => { 49 | let result = [] 50 | permissions.forEach(code => { 51 | if (codeMap[code]) { 52 | result.push(codeMap[code]) 53 | } 54 | }) 55 | return result 56 | } 57 | const user = { 58 | state: { 59 | userName: wayeUserInfo ? wayeUserInfo.userName : '', 60 | userCode: wayeUserInfo ? wayeUserInfo.userCode : '', 61 | userId: wayeUserInfo ? wayeUserInfo.userId : '', 62 | permissions: wayeUserInfo ? wayeUserInfo.permissions : [], 63 | asyncRoutes: [] 64 | }, 65 | 66 | mutations: { 67 | setUserName (state, name) { 68 | state.userName = name 69 | }, 70 | 71 | setUserCode (state, code) { 72 | state.userCode = code 73 | }, 74 | 75 | setUserId (state, id) { 76 | state.userId = id 77 | }, 78 | 79 | setPermissions (state, val) { 80 | state.permissions = val 81 | }, 82 | setAsyncRoutes (state, list) { 83 | state.asyncRoutes = list 84 | } 85 | }, 86 | 87 | actions: { 88 | login ({ commit, dispatch }, userInfo) { 89 | return login(userInfo) 90 | .then(data => { 91 | Vue.ls.set('userInfo', data) 92 | Vue.ls.set('unloginCount', 0) 93 | Vue.ls.set('userId', data.userId) 94 | 95 | commit('setUserName', data.userName) 96 | commit('setUserCode', data.userCode) 97 | commit('setUserId', data.userId) 98 | commit('setPermissions', data.permissions) 99 | 100 | return data 101 | }) 102 | }, 103 | 104 | genAsyncRoutes ({ commit, state }, permissions) { 105 | return new Promise(resolve => { 106 | let routes 107 | let routeData = cloneDeep(navConfig) 108 | parseRoutes(routeData) 109 | if (state.userCode === SUPER_ADMIN) { 110 | routes = routeData 111 | } else { 112 | let filterPaths = accessedRoutePaths(permissions, codeMap) 113 | routes = filterAsyncRouter(routeData, filterPaths) 114 | } 115 | commit('setAsyncRoutes', routes) 116 | resolve() 117 | }) 118 | }, 119 | 120 | logout ({ commit }) { 121 | return logout().then(() => { 122 | Vue.ls.remove('userInfo') 123 | Vue.ls.remove('unloginCount') 124 | commit('setUserName', '') 125 | commit('setUserCode', '') 126 | commit('setUserId', '') 127 | commit('setPermissions', '') 128 | commit('setAsyncRoutes', []) 129 | return true 130 | }) 131 | } 132 | } 133 | } 134 | 135 | export default user 136 | -------------------------------------------------------------------------------- /src/assets/scss/mixins/mixins.scss: -------------------------------------------------------------------------------- 1 | @import "function"; 2 | @import "../common/var"; 3 | 4 | /* Break-points 5 | -------------------------- */ 6 | @mixin res($key, $map: $--breakpoints) { 7 | // 循环断点Map,如果存在则返回 8 | @if map-has-key($map, $key) { 9 | @media only screen and #{inspect(map-get($map, $key))} { 10 | @content; 11 | } 12 | } @else { 13 | @warn "Undefeined points: `#{$map}`"; 14 | } 15 | } 16 | 17 | /* Scrollbar 18 | -------------------------- */ 19 | @mixin scroll-bar { 20 | $--scrollbar-thumb-background: #b4bccc; 21 | $--scrollbar-track-background: #fff; 22 | 23 | &::-webkit-scrollbar { 24 | z-index: 11; 25 | width: 6px; 26 | 27 | &:horizontal { 28 | height: 6px; 29 | } 30 | 31 | &-thumb { 32 | border-radius: 5px; 33 | width: 6px; 34 | background: $--scrollbar-thumb-background; 35 | } 36 | 37 | &-corner { 38 | background: $--scrollbar-track-background; 39 | } 40 | 41 | &-track { 42 | background: $--scrollbar-track-background; 43 | 44 | &-piece { 45 | background: $--scrollbar-track-background; 46 | width: 6px; 47 | } 48 | } 49 | } 50 | } 51 | 52 | /* Placeholder 53 | -------------------------- */ 54 | @mixin placeholder { 55 | &::-webkit-input-placeholder { 56 | @content 57 | } 58 | 59 | &::-moz-placeholder { 60 | @content 61 | } 62 | 63 | &:-ms-input-placeholder { 64 | @content 65 | } 66 | } 67 | 68 | /* BEM 69 | -------------------------- */ 70 | @mixin b($block) { 71 | $B: $namespace+'-'+$block !global; 72 | 73 | .#{$B} { 74 | @content; 75 | } 76 | } 77 | 78 | @mixin e($element) { 79 | $E: $element !global; 80 | $selector: &; 81 | $currentSelector: ""; 82 | @each $unit in $element { 83 | $currentSelector: #{$currentSelector + "." + $B + $element-separator + $unit + ","}; 84 | } 85 | 86 | @if hitAllSpecialNestRule($selector) { 87 | @at-root { 88 | #{$selector} { 89 | #{$currentSelector} { 90 | @content; 91 | } 92 | } 93 | } 94 | } @else { 95 | @at-root { 96 | #{$currentSelector} { 97 | @content; 98 | } 99 | } 100 | } 101 | } 102 | 103 | @mixin m($modifier) { 104 | $selector: &; 105 | $currentSelector: ""; 106 | @each $unit in $modifier { 107 | $currentSelector: #{$currentSelector + & + $modifier-separator + $unit + ","}; 108 | } 109 | 110 | @at-root { 111 | #{$currentSelector} { 112 | @content; 113 | } 114 | } 115 | } 116 | 117 | @mixin configurable-m($modifier, $E-flag: false) { 118 | $selector: &; 119 | $interpolation: ''; 120 | 121 | @if $E-flag { 122 | $interpolation: $element-separator + $E-flag; 123 | } 124 | 125 | @at-root { 126 | #{$selector} { 127 | .#{$B+$interpolation+$modifier-separator+$modifier} { 128 | @content; 129 | } 130 | } 131 | } 132 | } 133 | 134 | @mixin spec-selector($specSelector: '', $element: $E, $modifier: false, $block: $B) { 135 | $modifierCombo: ''; 136 | 137 | @if $modifier { 138 | $modifierCombo: $modifier-separator + $modifier; 139 | } 140 | 141 | @at-root { 142 | #{&}#{$specSelector}.#{$block+$element-separator+$element+$modifierCombo} { 143 | @content 144 | } 145 | } 146 | } 147 | 148 | @mixin meb($modifier: false, $element: $E, $block: $B) { 149 | $selector: &; 150 | $modifierCombo: ''; 151 | 152 | @if $modifier { 153 | $modifierCombo: $modifier-separator + $modifier; 154 | } 155 | 156 | @at-root { 157 | #{$selector} { 158 | .#{$block+$element-separator+$element+$modifierCombo} { 159 | @content 160 | } 161 | } 162 | } 163 | } 164 | 165 | @mixin when($state) { 166 | @at-root { 167 | &.#{$state-prefix + $state} { 168 | @content; 169 | } 170 | } 171 | } 172 | 173 | @mixin extend-rule($name) { 174 | @extend #{'%shared-'+$name}; 175 | } 176 | 177 | @mixin share-rule($name) { 178 | $rule-name: '%shared-'+$name; 179 | 180 | @at-root #{$rule-name} { 181 | @content 182 | } 183 | } 184 | 185 | @mixin pseudo($pseudo) { 186 | @at-root #{&}#{':#{$pseudo}'} { 187 | @content 188 | } 189 | } 190 | 191 | -------------------------------------------------------------------------------- /src/views/login.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 101 | 167 | -------------------------------------------------------------------------------- /src/utils/fetch.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import Vue from 'vue' 3 | import qs from 'qs' 4 | import { Message } from 'element-ui' 5 | import router from '../router' 6 | import { EventBus } from './event-bus' 7 | export const BASE_URL = process.env.VUE_APP_BASEURL 8 | const MSG_UNLOGIN = '登录超时,请重新登录' 9 | const MSG_ERROR = '系统异常,请联系客服' 10 | const goLogin = message => { 11 | message = message || MSG_UNLOGIN 12 | const count = Vue.ls.get('unloginCount') || 0 13 | // 多个请求触发 401 时,只提示及跳转一次 14 | if (count >= 1 || router.currentRoute.name === 'login') { 15 | return 16 | } 17 | EventBus.$emit('ql.logout.success') 18 | Vue.ls.set('unloginCount', count + 1) 19 | Message({ 20 | message, 21 | type: 'error', 22 | duration: 5 * 1000 23 | }) 24 | Vue.ls.remove('userInfo') 25 | router.push({ 26 | path: '/login', 27 | query: { redirect: router.currentRoute.fullPath } 28 | }) 29 | } 30 | 31 | axios.defaults.baseURL = BASE_URL 32 | 33 | axios.interceptors.request.use(config => { 34 | return config 35 | }) 36 | 37 | // Add a response interceptor 38 | axios.interceptors.response.use( 39 | response => { 40 | let data = response.data 41 | let showMessage = response.config.headers.showMessage 42 | const status = +data.status 43 | if (status === -100) { 44 | goLogin() 45 | } else if (status === -1) { 46 | showMessage && 47 | Message({ 48 | message: data.msg, 49 | type: 'error', 50 | duration: 3000 51 | }) 52 | return Promise.reject(data.msg) 53 | } else { 54 | let serverTime = data.serverTime 55 | let now = Date.now() 56 | if (serverTime) { 57 | Vue.ls.set('timeDiff', serverTime - now) 58 | } 59 | return data.data === undefined ? data : data.data 60 | } 61 | }, 62 | error => { 63 | let message = error.response.data && error.response.data.msg 64 | if (error.response && error.response.status === 401) { 65 | goLogin(message || MSG_UNLOGIN) 66 | } else { 67 | let showMessage = error.response.config.headers.showMessage 68 | showMessage && 69 | Message({ 70 | message: message || MSG_ERROR, 71 | type: 'error', 72 | duration: 5 * 1000 73 | }) 74 | } 75 | console.error(error) 76 | return Promise.reject(error) 77 | } 78 | ) 79 | 80 | /** 81 | * 查询 82 | * @param {*} url 接口地址 83 | * @param {*} params 参数 84 | * @param {*} showMessage 显示出错提示 85 | */ 86 | export function get (url, params = {}, showMessage = true) { 87 | if (typeof params === 'boolean') { 88 | showMessage = params 89 | params = {} 90 | } 91 | return axios({ 92 | url, 93 | params, 94 | headers: { showMessage }, 95 | method: 'get' 96 | }) 97 | } 98 | 99 | /** 100 | * post 请求 101 | * @param {*} url 接口地址 102 | * @param {*} data json 数据 103 | * @param {*} showMessage 显示出错提示 104 | */ 105 | export function post (url, data = {}, showMessage = true) { 106 | return axios({ 107 | url, 108 | method: 'post', 109 | data, 110 | headers: { 'Content-Type': 'application/json', showMessage } 111 | }) 112 | } 113 | 114 | /** 115 | * 用 formData 方式 post 116 | * @param {*} url 接口地址 117 | * @param {*} params json 数据 118 | * @param {*} showMessage 显示出错提示 119 | */ 120 | export function postByForm (url, data = {}, showMessage = true) { 121 | return axios({ 122 | url, 123 | method: 'post', 124 | data: qs.stringify(data), 125 | headers: { 126 | 'Content-Type': 'application/x-www-form-urlencoded', 127 | showMessage 128 | } 129 | }) 130 | } 131 | 132 | /** 133 | * 获取文件地址,如 pdf, excel, text 下载地址 134 | * @param {*} path 路径 135 | * @param {*} params 查询参数 136 | */ 137 | export function getFileUrl (path, params = {}) { 138 | let queryData = [] 139 | let queryString = '' 140 | Object.keys(params).forEach(key => { 141 | queryData.push(`${key}=${params[key]}`) 142 | }) 143 | 144 | if (queryData.length) { 145 | queryString = `?${queryData.join('&')}` 146 | } 147 | return `${BASE_URL}${path}${queryString}` 148 | } 149 | 150 | /** 151 | * put 请求 152 | * @param {*} url 接口地址 153 | * @param {*} data json 数据 154 | */ 155 | export function put (url, data = {}, byJson = true) { 156 | return axios({ 157 | url, 158 | method: 'put', 159 | data: byJson ? data : qs.stringify(data), 160 | headers: { 161 | showMessage: true, 162 | 'Content-Type': byJson 163 | ? 'application/json' 164 | : 'application/x-www-form-urlencoded' 165 | } 166 | }) 167 | } 168 | 169 | /** 170 | * delete 请求 171 | * @param {*} url 接口地址 172 | * @param {*} data json 数据 173 | */ 174 | export function deleteData (url, data = {}) { 175 | return axios({ 176 | url, 177 | method: 'delete', 178 | data, 179 | headers: { 'Content-Type': 'application/json', showMessage: true } 180 | }) 181 | } 182 | -------------------------------------------------------------------------------- /src/utils/dom.js: -------------------------------------------------------------------------------- 1 | /* istanbul ignore next */ 2 | // element-ui/lib/utils/dom.js 3 | import Vue from 'vue' 4 | 5 | const isServer = Vue.prototype.$isServer 6 | const SPECIAL_CHARS_REGEXP = /([\\:\\-\\_]+(.))/g 7 | const MOZ_HACK_REGEXP = /^moz([A-Z])/ 8 | const ieVersion = isServer ? 0 : Number(document.documentMode) 9 | 10 | /* istanbul ignore next */ 11 | const trim = function (string) { 12 | return (string || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, '') 13 | } 14 | /* istanbul ignore next */ 15 | const camelCase = function (name) { 16 | return name.replace(SPECIAL_CHARS_REGEXP, function (_, separator, letter, offset) { 17 | return offset ? letter.toUpperCase() : letter 18 | }).replace(MOZ_HACK_REGEXP, 'Moz$1') 19 | } 20 | 21 | /* istanbul ignore next */ 22 | export const on = (function () { 23 | if (!isServer && document.addEventListener) { 24 | return function (element, event, handler) { 25 | if (element && event && handler) { 26 | element.addEventListener(event, handler, false) 27 | } 28 | } 29 | } else { 30 | return function (element, event, handler) { 31 | if (element && event && handler) { 32 | element.attachEvent('on' + event, handler) 33 | } 34 | } 35 | } 36 | })() 37 | 38 | /* istanbul ignore next */ 39 | export const off = (function () { 40 | if (!isServer && document.removeEventListener) { 41 | return function (element, event, handler) { 42 | if (element && event) { 43 | element.removeEventListener(event, handler, false) 44 | } 45 | } 46 | } else { 47 | return function (element, event, handler) { 48 | if (element && event) { 49 | element.detachEvent('on' + event, handler) 50 | } 51 | } 52 | } 53 | })() 54 | 55 | /* istanbul ignore next */ 56 | export const once = function (el, event, fn) { 57 | var listener = function () { 58 | if (fn) { 59 | fn.apply(this, arguments) 60 | } 61 | off(el, event, listener) 62 | } 63 | on(el, event, listener) 64 | } 65 | 66 | /* istanbul ignore next */ 67 | export function hasClass (el, cls) { 68 | if (!el || !cls) return false 69 | if (cls.indexOf(' ') !== -1) throw new Error('className should not contain space.') 70 | if (el.classList) { 71 | return el.classList.contains(cls) 72 | } else { 73 | return (' ' + el.className + ' ').indexOf(' ' + cls + ' ') > -1 74 | } 75 | } 76 | 77 | /* istanbul ignore next */ 78 | export function addClass (el, cls) { 79 | if (!el) return 80 | var curClass = el.className 81 | var classes = (cls || '').split(' ') 82 | 83 | for (var i = 0, j = classes.length; i < j; i++) { 84 | var clsName = classes[i] 85 | if (!clsName) continue 86 | 87 | if (el.classList) { 88 | el.classList.add(clsName) 89 | } else { 90 | if (!hasClass(el, clsName)) { 91 | curClass += ' ' + clsName 92 | } 93 | } 94 | } 95 | if (!el.classList) { 96 | el.className = curClass 97 | } 98 | } 99 | 100 | /* istanbul ignore next */ 101 | export function removeClass (el, cls) { 102 | if (!el || !cls) return 103 | var classes = cls.split(' ') 104 | var curClass = ' ' + el.className + ' ' 105 | 106 | for (var i = 0, j = classes.length; i < j; i++) { 107 | var clsName = classes[i] 108 | if (!clsName) continue 109 | 110 | if (el.classList) { 111 | el.classList.remove(clsName) 112 | } else { 113 | if (hasClass(el, clsName)) { 114 | curClass = curClass.replace(' ' + clsName + ' ', ' ') 115 | } 116 | } 117 | } 118 | if (!el.classList) { 119 | el.className = trim(curClass) 120 | } 121 | } 122 | 123 | /* istanbul ignore next */ 124 | export const getStyle = ieVersion < 9 ? function (element, styleName) { 125 | if (isServer) return 126 | if (!element || !styleName) return null 127 | styleName = camelCase(styleName) 128 | if (styleName === 'float') { 129 | styleName = 'styleFloat' 130 | } 131 | try { 132 | switch (styleName) { 133 | case 'opacity': 134 | try { 135 | return element.filters.item('alpha').opacity / 100 136 | } catch (e) { 137 | return 1.0 138 | } 139 | default: 140 | return (element.style[styleName] || element.currentStyle ? element.currentStyle[styleName] : null) 141 | } 142 | } catch (e) { 143 | return element.style[styleName] 144 | } 145 | } : function (element, styleName) { 146 | if (isServer) return 147 | if (!element || !styleName) return null 148 | styleName = camelCase(styleName) 149 | if (styleName === 'float') { 150 | styleName = 'cssFloat' 151 | } 152 | try { 153 | var computed = document.defaultView.getComputedStyle(element, '') 154 | return element.style[styleName] || computed ? computed[styleName] : null 155 | } catch (e) { 156 | return element.style[styleName] 157 | } 158 | } 159 | 160 | /* istanbul ignore next */ 161 | export function setStyle (element, styleName, value) { 162 | if (!element || !styleName) return 163 | 164 | if (typeof styleName === 'object') { 165 | for (var prop in styleName) { 166 | if (styleName.hasOwnProperty(prop)) { 167 | setStyle(element, prop, styleName[prop]) 168 | } 169 | } 170 | } else { 171 | styleName = camelCase(styleName) 172 | if (styleName === 'opacity' && ieVersion < 9) { 173 | element.style.filter = isNaN(value) ? '' : 'alpha(opacity=' + value * 100 + ')' 174 | } else { 175 | element.style[styleName] = value 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/components/layouts/page-layout.vue: -------------------------------------------------------------------------------- 1 | 68 | 69 | 185 | 412 | -------------------------------------------------------------------------------- /src/assets/scss/common/var.scss: -------------------------------------------------------------------------------- 1 | /* Element Chalk Variables */ 2 | 3 | /* Transition 4 | -------------------------- */ 5 | $--all-transition: all .3s cubic-bezier(.645,.045,.355,1) !default; 6 | $--fade-transition: opacity 300ms cubic-bezier(0.23, 1, 0.32, 1) !default; 7 | $--fade-linear-transition: opacity 200ms linear !default; 8 | $--md-fade-transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1), opacity 300ms cubic-bezier(0.23, 1, 0.32, 1) !default; 9 | $--border-transition-base: border-color .2s cubic-bezier(.645,.045,.355,1) !default; 10 | $--color-transition-base: color .2s cubic-bezier(.645,.045,.355,1) !default; 11 | 12 | /* Colors 13 | -------------------------- */ 14 | $--color-white: #fff !default; 15 | $--color-black: #000 !default; 16 | 17 | $--color-primary: #409EFF !default; 18 | $--color-primary-light-1: mix($--color-white, $--color-primary, 10%) !default; /* #53a8ff */ 19 | $--color-primary-light-2: mix($--color-white, $--color-primary, 20%) !default; /* #66b1ff */ 20 | $--color-primary-light-3: mix($--color-white, $--color-primary, 30%) !default; /* #79bbff */ 21 | $--color-primary-light-4: mix($--color-white, $--color-primary, 40%) !default; /* #8cc5ff */ 22 | $--color-primary-light-5: mix($--color-white, $--color-primary, 50%) !default; /* #a0cfff */ 23 | $--color-primary-light-6: mix($--color-white, $--color-primary, 60%) !default; /* #b3d8ff */ 24 | $--color-primary-light-7: mix($--color-white, $--color-primary, 70%) !default; /* #c6e2ff */ 25 | $--color-primary-light-8: mix($--color-white, $--color-primary, 80%) !default; /* #d9ecff */ 26 | $--color-primary-light-9: mix($--color-white, $--color-primary, 90%) !default; /* #ecf5ff */ 27 | 28 | $--color-success: #4CAF50 !default; 29 | $--color-warning: #FFC107 !default; 30 | $--color-danger: #FF5252 !default; 31 | $--color-info: #909399 !default; 32 | 33 | $--color-success-light: mix($--color-white, $--color-success, 80%) !default; 34 | $--color-warning-light: mix($--color-white, $--color-warning, 80%) !default; 35 | $--color-danger-light: mix($--color-white, $--color-danger, 80%) !default; 36 | $--color-info-light: mix($--color-white, $--color-info, 80%) !default; 37 | 38 | $--color-success-lighter: mix($--color-white, $--color-success, 90%) !default; 39 | $--color-warning-lighter: mix($--color-white, $--color-warning, 90%) !default; 40 | $--color-danger-lighter: mix($--color-white, $--color-danger, 90%) !default; 41 | $--color-info-lighter: mix($--color-white, $--color-info, 90%) !default; 42 | 43 | $--color-text-primary: #303133 !default; 44 | $--color-text-regular: #606266 !default; 45 | $--color-text-secondary: #909399 !default; 46 | $--color-text-placeholder: #c0c4cc !default; 47 | 48 | /* Link 49 | -------------------------- */ 50 | $--link-color: $--color-primary-light-2 !default; 51 | $--link-hover-color: $--color-primary !default; 52 | 53 | /* Background 54 | -------------------------- */ 55 | $--background-color-base: #f5f7fa !default; 56 | 57 | /* Border 58 | -------------------------- */ 59 | $--border-width-base: 1px !default; 60 | $--border-style-base: solid !default; 61 | $--border-color-base: #dcdfe6 !default; 62 | $--border-color-light: #e4e7ed !default; 63 | $--border-color-lighter: #ebeef5 !default; 64 | $--border-color-extra-light: #f2f6fc !default; 65 | $--border-color-hover: $--color-text-placeholder !default; 66 | $--border-base: $--border-width-base $--border-style-base $--border-color-base !default; 67 | $--border-radius-base: 4px !default; 68 | $--border-radius-small: 2px !default; 69 | $--border-radius-circle: 100% !default; 70 | 71 | /* Box-shadow 72 | -------------------------- */ 73 | $--box-shadow-base: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04) !default; 74 | $--box-shadow-dark: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .12) !default; 75 | $--box-shadow-light: 0 2px 12px 0 rgba(0, 0, 0, 0.1) !default; 76 | $--box-shadow-light-1: 0 2px 1px -1px rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 1px 3px 0 rgba(0,0,0,.12)!default; 77 | /* Fill 78 | -------------------------- */ 79 | $--fill-base: $--color-white !default; 80 | 81 | /* Font 82 | -------------------------- */ 83 | $--font-path: 'fonts' !default; 84 | $--font-size-base: 14px !default; 85 | $--font-size-small: 13px !default; 86 | $--font-size-large: 18px !default; 87 | $--font-color-disabled-base: #bbb !default; 88 | $--font-weight-primary: 500 !default; 89 | $--font-line-height-primary: 24px !default; 90 | 91 | /* Size 92 | -------------------------- */ 93 | $--size-base: 14px !default; 94 | 95 | /* z-index 96 | -------------------------- */ 97 | $--index-normal: 1 !default; 98 | $--index-top: 1000 !default; 99 | $--index-popper: 2000 !default; 100 | 101 | /* Disable base 102 | -------------------------- */ 103 | $--disabled-fill-base: $--background-color-base !default; 104 | $--disabled-color-base: $--color-text-placeholder !default; 105 | $--disabled-border-base: $--border-color-light !default; 106 | 107 | /* Icon 108 | -------------------------- */ 109 | $--icon-color: #666 !default; 110 | $--icon-color-base: $--color-info !default; 111 | 112 | /* Checkbox 113 | -------------------------- */ 114 | $--checkbox-font-size: 14px !default; 115 | $--checkbox-font-weight: $--font-weight-primary !default; 116 | $--checkbox-color: $--color-text-regular !default; 117 | $--checkbox-input-height: 14px !default; 118 | $--checkbox-input-width: 14px !default; 119 | $--checkbox-input-border-radius: $--border-radius-small !default; 120 | $--checkbox-input-fill: $--color-white !default; 121 | $--checkbox-input-border: $--border-base !default; 122 | $--checkbox-input-border-color: $--border-color-base !default; 123 | $--checkbox-icon-color: $--color-white !default; 124 | 125 | $--checkbox-disabled-input-border-color: $--border-color-base !default; 126 | $--checkbox-disabled-input-fill: #edf2fc !default; 127 | $--checkbox-disabled-icon-color: $--color-text-placeholder !default; 128 | 129 | $--checkbox-disabled-checked-input-fill: $--border-color-extra-light !default; 130 | $--checkbox-disabled-checked-input-border-color: $--border-color-base !default; 131 | $--checkbox-disabled-checked-icon-color: $--color-text-placeholder !default; 132 | 133 | $--checkbox-checked-text-color: $--color-primary !default; 134 | $--checkbox-checked-input-border-color: $--color-primary !default; 135 | $--checkbox-checked-input-fill: $--color-primary !default; 136 | $--checkbox-checked-icon-color: $--fill-base !default; 137 | 138 | $--checkbox-input-border-color-hover: $--color-primary !default; 139 | 140 | $--checkbox-bordered-height: 40px !default; 141 | $--checkbox-bordered-padding: 9px 20px 9px 10px !default; 142 | $--checkbox-bordered-medium-padding: 7px 20px 7px 10px !default; 143 | $--checkbox-bordered-small-padding: 5px 15px 5px 10px !default; 144 | $--checkbox-bordered-mini-padding: 3px 15px 3px 10px !default; 145 | $--checkbox-bordered-medium-input-height: 14px !default; 146 | $--checkbox-bordered-medium-input-width: 14px !default; 147 | $--checkbox-bordered-medium-height: 36px !default; 148 | $--checkbox-bordered-small-input-height: 12px !default; 149 | $--checkbox-bordered-small-input-width: 12px !default; 150 | $--checkbox-bordered-small-height: 32px !default; 151 | $--checkbox-bordered-mini-input-height: 12px !default; 152 | $--checkbox-bordered-mini-input-width: 12px !default; 153 | $--checkbox-bordered-mini-height: 28px !default; 154 | 155 | $--checkbox-button-font-size: $--font-size-base !default; 156 | $--checkbox-button-checked-fill: $--color-primary !default; 157 | $--checkbox-button-checked-color: $--color-white !default; 158 | $--checkbox-button-checked-border-color: $--color-primary !default; 159 | 160 | 161 | 162 | /* Radio 163 | -------------------------- */ 164 | $--radio-font-size: 14px !default; 165 | $--radio-font-weight: $--font-weight-primary !default; 166 | $--radio-color: $--color-text-regular !default; 167 | $--radio-input-height: 14px !default; 168 | $--radio-input-width: 14px !default; 169 | $--radio-input-border-radius: $--border-radius-circle !default; 170 | $--radio-input-fill: $--color-white !default; 171 | $--radio-input-border: $--border-base !default; 172 | $--radio-input-border-color: $--border-color-base !default; 173 | $--radio-icon-color: $--color-white !default; 174 | 175 | $--radio-disabled-input-border-color: $--disabled-border-base !default; 176 | $--radio-disabled-input-fill: $--disabled-fill-base !default; 177 | $--radio-disabled-icon-color: $--disabled-fill-base !default; 178 | 179 | $--radio-disabled-checked-input-border-color: $--disabled-border-base !default; 180 | $--radio-disabled-checked-input-fill: $--disabled-fill-base !default; 181 | $--radio-disabled-checked-icon-color: $--color-text-placeholder !default; 182 | 183 | $--radio-checked-text-color: $--color-primary !default; 184 | $--radio-checked-input-border-color: $--color-primary !default; 185 | $--radio-checked-input-fill: $--color-white !default; 186 | $--radio-checked-icon-color: $--color-primary !default; 187 | 188 | $--radio-input-border-color-hover: $--color-primary !default; 189 | 190 | $--radio-bordered-height: 40px !default; 191 | $--radio-bordered-padding: 12px 20px 0 10px !default; 192 | $--radio-bordered-medium-padding: 10px 20px 0 10px !default; 193 | $--radio-bordered-small-padding: 8px 15px 0 10px !default; 194 | $--radio-bordered-mini-padding: 6px 15px 0 10px !default; 195 | $--radio-bordered-medium-input-height: 14px !default; 196 | $--radio-bordered-medium-input-width: 14px !default; 197 | $--radio-bordered-medium-height: 36px !default; 198 | $--radio-bordered-small-input-height: 12px !default; 199 | $--radio-bordered-small-input-width: 12px !default; 200 | $--radio-bordered-small-height: 32px !default; 201 | $--radio-bordered-mini-input-height: 12px !default; 202 | $--radio-bordered-mini-input-width: 12px !default; 203 | $--radio-bordered-mini-height: 28px !default; 204 | 205 | $--radio-button-font-size: $--font-size-base !default; 206 | $--radio-button-checked-fill: $--color-primary !default; 207 | $--radio-button-checked-color: $--color-white !default; 208 | $--radio-button-checked-border-color: $--color-primary !default; 209 | $--radio-button-disabled-checked-fill: $--border-color-extra-light !default; 210 | 211 | /* Select 212 | -------------------------- */ 213 | $--select-border-color-hover: $--border-color-hover !default; 214 | $--select-disabled-border: $--disabled-border-base !default; 215 | $--select-font-size: $--font-size-base !default; 216 | $--select-close-hover-color: $--color-text-secondary !default; 217 | 218 | $--select-input-color: $--color-text-placeholder !default; 219 | $--select-multiple-input-color: #666 !default; 220 | $--select-input-focus-background: $--color-primary !default; 221 | $--select-input-font-size: 14px !default; 222 | 223 | $--select-option-color: $--color-text-regular !default; 224 | $--select-option-disabled-color: $--color-text-placeholder !default; 225 | $--select-option-disabled-background: $--color-white !default; 226 | $--select-option-height: 34px !default; 227 | $--select-option-hover-background: $--background-color-base !default; 228 | $--select-option-selected: $--color-primary !default; 229 | $--select-option-selected-hover: $--background-color-base !default; 230 | 231 | $--select-group-color: $--color-info !default; 232 | $--select-group-height: 30px !default; 233 | $--select-group-font-size: 12px !default; 234 | 235 | $--select-dropdown-background: $--color-white !default; 236 | $--select-dropdown-shadow: $--box-shadow-light !default; 237 | $--select-dropdown-empty-color: #999 !default; 238 | $--select-dropdown-max-height: 274px !default; 239 | $--select-dropdown-padding: 6px 0 !default; 240 | $--select-dropdown-empty-padding: 10px 0 !default; 241 | $--select-dropdown-border: solid 1px $--border-color-light !default; 242 | 243 | /* Alert 244 | -------------------------- */ 245 | $--alert-padding: 8px 16px !default; 246 | $--alert-border-radius: $--border-radius-base !default; 247 | $--alert-title-font-size: 13px !default; 248 | $--alert-description-font-size: 12px !default; 249 | $--alert-close-font-size: 12px !default; 250 | $--alert-close-customed-font-size: 13px !default; 251 | 252 | $--alert-success-color: $--color-success-lighter !default; 253 | $--alert-info-color: $--color-info-lighter !default; 254 | $--alert-warning-color: $--color-warning-lighter !default; 255 | $--alert-danger-color: $--color-danger-lighter !default; 256 | 257 | $--alert-icon-size: 16px !default; 258 | $--alert-icon-large-size: 28px !default; 259 | 260 | /* Message Box 261 | -------------------------- */ 262 | $--msgbox-width: 420px !default; 263 | $--msgbox-border-radius: 4px !default; 264 | $--msgbox-font-size: $--font-size-large !default; 265 | $--msgbox-content-font-size: $--font-size-base !default; 266 | $--msgbox-content-color: $--color-text-regular !default; 267 | $--msgbox-error-font-size: 12px !default; 268 | $--msgbox-padding-primary: 15px !default; 269 | 270 | $--msgbox-success-color: $--color-success !default; 271 | $--msgbox-info-color: $--color-info !default; 272 | $--msgbox-warning-color: $--color-warning !default; 273 | $--msgbox-danger-color: $--color-danger !default; 274 | 275 | /* Message 276 | -------------------------- */ 277 | $--message-shadow: $--box-shadow-base !default; 278 | $--message-min-width: 380px !default; 279 | $--message-background-color: #edf2fc !default; 280 | $--message-padding: 15px 15px 15px 20px !default; 281 | $--message-content-color: $--color-text-regular !default; 282 | $--message-close-color: $--color-text-placeholder !default; 283 | $--message-close-size: 16px !default; 284 | $--message-close-hover-color: $--color-text-secondary !default; 285 | 286 | $--message-success-color: $--color-success !default; 287 | $--message-info-color: $--color-info !default; 288 | $--message-warning-color: $--color-warning !default; 289 | $--message-danger-color: $--color-danger !default; 290 | 291 | /* Notification 292 | -------------------------- */ 293 | $--notification-width: 330px !default; 294 | $--notification-padding: 14px 26px 14px 13px !default; 295 | $--notification-radius: 8px !default; 296 | $--notification-shadow: $--box-shadow-light !default; 297 | $--notification-border-color: $--border-color-lighter !default; 298 | $--notification-icon-size: 24px !default; 299 | $--notification-close-font-size: $--message-close-size !default; 300 | $--notification-group-margin: 13px !default; 301 | $--notification-font-size: $--font-size-base !default; 302 | $--notification-color: $--color-text-regular !default; 303 | $--notification-title-font-size: 16px !default; 304 | $--notification-title-color: $--color-text-primary !default; 305 | 306 | $--notification-close-color: $--color-text-secondary !default; 307 | $--notification-close-hover-color: $--color-text-regular !default; 308 | 309 | $--notification-success-color: $--color-success !default; 310 | $--notification-info-color: $--color-info !default; 311 | $--notification-warning-color: $--color-warning !default; 312 | $--notification-danger-color: $--color-danger !default; 313 | 314 | /* Input 315 | -------------------------- */ 316 | $--input-font-size: $--font-size-base !default; 317 | $--input-color: $--color-text-regular !default; 318 | $--input-width: 140px !default; 319 | $--input-height: 40px !default; 320 | $--input-border: $--border-base !default; 321 | $--input-border-color: $--border-color-base !default; 322 | $--input-border-radius: $--border-radius-base !default; 323 | $--input-border-color-hover: $--border-color-hover !default; 324 | $--input-fill: $--color-white !default; 325 | $--input-fill-disabled: $--disabled-fill-base !default; 326 | $--input-color-disabled: $--font-color-disabled-base !default; 327 | $--input-icon-color: $--color-text-placeholder !default; 328 | $--input-placeholder-color: $--color-text-placeholder !default; 329 | $--input-max-width: 314px !default; 330 | 331 | $--input-hover-border: $--border-color-hover !default; 332 | $--input-clear-hover-color: $--color-text-secondary !default; 333 | 334 | $--input-focus-border: $--color-primary !default; 335 | $--input-focus-fill: $--color-white !default; 336 | 337 | $--input-disabled-fill: $--disabled-fill-base !default; 338 | $--input-disabled-border: $--disabled-border-base !default; 339 | $--input-disabled-color: $--disabled-color-base !default; 340 | $--input-disabled-placeholder-color: $--color-text-placeholder !default; 341 | 342 | $--input-medium-font-size: 14px !default; 343 | $--input-medium-height: 36px !default; 344 | 345 | $--input-small-font-size: 13px !default; 346 | $--input-small-height: 32px !default; 347 | 348 | $--input-mini-font-size: 12px !default; 349 | $--input-mini-height: 28px !default; 350 | 351 | /* Cascader 352 | -------------------------- */ 353 | $--cascader-menu-fill: $--fill-base !default; 354 | $--cascader-menu-font-size: $--font-size-base !default; 355 | $--cascader-menu-radius: $--border-radius-base !default; 356 | $--cascader-menu-border: $--border-base !default; 357 | $--cascader-menu-border-color: $--border-color-base !default; 358 | $--cascader-menu-border-width: $--border-width-base !default; 359 | $--cascader-menu-color: $--color-text-regular !default; 360 | $--cascader-menu-option-color-active: $--color-text-secondary !default; 361 | $--cascader-menu-option-fill-active: rgba($--color-text-secondary, 0.12) !default; 362 | $--cascader-menu-option-color-hover: $--color-text-regular !default; 363 | $--cascader-menu-option-fill-hover: rgba($--color-text-primary, 0.06) !default; 364 | $--cascader-menu-option-color-disabled: #999 !default; 365 | $--cascader-menu-option-fill-disabled: rgba($--color-black, 0.06) !default; 366 | $--cascader-menu-option-empty-color: #666 !default; 367 | $--cascader-menu-group-color: #999 !default; 368 | $--cascader-menu-shadow: 0 1px 2px rgba($--color-black, 0.14), 0 0 3px rgba($--color-black, 0.14) !default; 369 | $--cascader-menu-option-pinyin-color: #999 !default; 370 | $--cascader-menu-submenu-shadow: 1px 1px 2px rgba($--color-black, 0.14), 1px 0 2px rgba($--color-black, 0.14) !default; 371 | 372 | /* Group 373 | -------------------------- */ 374 | $--group-option-flex: 0 0 (1/5) * 100% !default; 375 | $--group-option-offset-bottom: 12px !default; 376 | $--group-option-fill-hover: rgba($--color-black, 0.06) !default; 377 | $--group-title-color: $--color-black !default; 378 | $--group-title-font-size: $--font-size-base !default; 379 | $--group-title-width: 66px !default; 380 | 381 | /* Tab 382 | -------------------------- */ 383 | $--tab-font-size: $--font-size-base !default; 384 | $--tab-border-line: 1px solid #e4e4e4 !default; 385 | $--tab-header-color-active: $--color-text-secondary !default; 386 | $--tab-header-color-hover: $--color-text-regular !default; 387 | $--tab-header-color: $--color-text-regular !default; 388 | $--tab-header-fill-active: rgba($--color-black, 0.06) !default; 389 | $--tab-header-fill-hover: rgba($--color-black, 0.06) !default; 390 | $--tab-vertical-header-width: 90px !default; 391 | $--tab-vertical-header-count-color: $--color-white !default; 392 | $--tab-vertical-header-count-fill: $--color-text-secondary !default; 393 | 394 | /* Button 395 | -------------------------- */ 396 | $--button-font-size: 14px !default; 397 | $--button-font-weight: $--font-weight-primary !default; 398 | $--button-border-radius: $--border-radius-base !default; 399 | $--button-padding-vertical: 12px !default; 400 | $--button-padding-horizontal: 20px !default; 401 | 402 | $--button-medium-font-size: 14px !default; 403 | $--button-medium-border-radius: $--border-radius-base !default; 404 | $--button-medium-padding-vertical: 10px !default; 405 | $--button-medium-padding-horizontal: 20px !default; 406 | 407 | $--button-small-font-size: 12px !default; 408 | $--button-small-border-radius: #{$--border-radius-base - 1} !default; 409 | $--button-small-padding-vertical: 9px !default; 410 | $--button-small-padding-horizontal: 15px !default; 411 | 412 | $--button-mini-font-size: 12px !default; 413 | $--button-mini-border-radius: #{$--border-radius-base - 1} !default; 414 | $--button-mini-padding-vertical: 7px !default; 415 | $--button-mini-padding-horizontal: 15px !default; 416 | 417 | $--button-default-color: $--color-text-regular !default; 418 | $--button-default-fill: $--color-white !default; 419 | $--button-default-border: $--border-color-base !default; 420 | 421 | $--button-disabled-color: $--color-text-placeholder !default; 422 | $--button-disabled-fill: $--color-white !default; 423 | $--button-disabled-border: $--border-color-lighter !default; 424 | 425 | $--button-primary-border: $--color-primary !default; 426 | $--button-primary-color: $--color-white !default; 427 | $--button-primary-fill: $--color-primary !default; 428 | 429 | $--button-success-border: $--color-success !default; 430 | $--button-success-color: $--color-white !default; 431 | $--button-success-fill: $--color-success !default; 432 | 433 | $--button-warning-border: $--color-warning !default; 434 | $--button-warning-color: $--color-white !default; 435 | $--button-warning-fill: $--color-warning !default; 436 | 437 | $--button-danger-border: $--color-danger !default; 438 | $--button-danger-color: $--color-white !default; 439 | $--button-danger-fill: $--color-danger !default; 440 | 441 | $--button-info-border: $--color-info !default; 442 | $--button-info-color: $--color-white !default; 443 | $--button-info-fill: $--color-info !default; 444 | 445 | $--button-hover-tint-percent: 20% !default; 446 | $--button-active-shade-percent: 10% !default; 447 | 448 | 449 | /* cascader 450 | -------------------------- */ 451 | $--cascader-height: 200px !default; 452 | 453 | /* Switch 454 | -------------------------- */ 455 | $--switch-on-color: $--color-primary !default; 456 | $--switch-off-color: $--border-color-base !default; 457 | $--switch-disabled-color: $--border-color-lighter !default; 458 | $--switch-disabled-text-color: $--color-text-placeholder !default; 459 | 460 | $--switch-font-size: $--font-size-base !default; 461 | $--switch-core-border-radius: 10px !default; 462 | $--switch-width: 40px !default; 463 | $--switch-height: 20px !default; 464 | $--switch-button-size: 16px !default; 465 | 466 | /* Dialog 467 | -------------------------- */ 468 | $--dialog-background-color: $--color-primary-light-4 !default; 469 | $--dialog-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3) !default; 470 | $--dialog-close-hover-color: $--color-primary !default; 471 | $--dialog-title-font-size: $--font-size-large !default; 472 | $--dialog-font-size: 14px !default; 473 | $--dialog-line-height: $--font-line-height-primary !default; 474 | $--dialog-padding-primary: 20px !default; 475 | 476 | /* Table 477 | -------------------------- */ 478 | $--table-border-color: $--border-color-lighter !default; 479 | $--table-border: 1px solid $--table-border-color !default; 480 | $--table-text-color: $--color-text-regular !default; 481 | $--table-header-color: $--color-white !default; 482 | $--table-row-hover-background: #f0fcff !default; 483 | $--table-current-row-background: $--color-primary-light-9 !default; 484 | $--table-header-background: #80d8ff !default; 485 | $--table-footer-background: $--color-text-placeholder !default; 486 | $--table-fixed-box-shadow: 0 0 10px rgba(0, 0, 0, .12) !default; 487 | 488 | /* Pagination 489 | -------------------------- */ 490 | $--pagination-font-size: 13px !default; 491 | $--pagination-fill: $--color-white !default; 492 | $--pagination-color: $--color-text-primary !default; 493 | $--pagination-border-radius: 3px !default; 494 | $--pagination-button-color: $--color-text-primary !default; 495 | $--pagination-button-width: 35.5px !default; 496 | $--pagination-button-height: 28px !default; 497 | $--pagination-button-disabled-color: $--color-text-placeholder !default; 498 | $--pagination-button-disabled-fill: $--color-white !default; 499 | $--pagination-hover-fill: $--color-primary !default; 500 | $--pagination-hover-color: $--color-white !default; 501 | 502 | /* Popover 503 | -------------------------- */ 504 | $--popover-fill: $--color-white !default; 505 | $--popover-font-size: $--font-size-base !default; 506 | $--popover-border-color: $--border-color-lighter !default; 507 | $--popover-arrow-size: 6px !default; 508 | $--popover-padding: 12px !default; 509 | $--popover-padding-large: 18px 20px !default; 510 | $--popover-title-font-size: 16px !default; 511 | $--popover-title-color: $--color-text-primary !default; 512 | 513 | /* Tooltip 514 | -------------------------- */ 515 | $--tooltip-fill: $--color-text-primary !default; 516 | $--tooltip-color: $--color-white !default; 517 | $--tooltip-font-size: 12px !default; 518 | $--tooltip-border-color: $--color-text-primary !default; 519 | $--tooltip-arrow-size: 6px !default; 520 | $--tooltip-padding: 10px !default; 521 | 522 | /* Tag 523 | -------------------------- */ 524 | $--tag-padding: 0 10px !default; 525 | $--tag-fill: rgba($--color-primary, 0.10) !default; 526 | $--tag-color: $--color-primary !default; 527 | $--tag-border: rgba($--color-primary, 0.20) !default; 528 | $--tag-font-size: 12px !default; 529 | $--tag-border-radius: 4px !default; 530 | 531 | $--tag-info-fill: rgba($--color-info, 0.10) !default; 532 | $--tag-info-border: rgba($--color-info, 0.20) !default; 533 | $--tag-info-color: $--color-info !default; 534 | 535 | $--tag-primary-fill: rgba($--color-primary, 0.10) !default; 536 | $--tag-primary-border: rgba($--color-primary, 0.20) !default; 537 | $--tag-primary-color: $--color-primary !default; 538 | 539 | $--tag-success-fill: rgba($--color-success, 0.10) !default; 540 | $--tag-success-border: rgba($--color-success, 0.20) !default; 541 | $--tag-success-color: $--color-success !default; 542 | 543 | $--tag-warning-fill: rgba($--color-warning, 0.10) !default; 544 | $--tag-warning-border: rgba($--color-warning, 0.20) !default; 545 | $--tag-warning-color: $--color-warning !default; 546 | 547 | $--tag-danger-fill: rgba($--color-danger, 0.10) !default; 548 | $--tag-danger-border: rgba($--color-danger, 0.20) !default; 549 | $--tag-danger-color: $--color-danger !default; 550 | 551 | /* Tree 552 | -------------------------- */ 553 | $--tree-node-hover-color: $--background-color-base !default; 554 | $--tree-text-color: $--color-text-regular !default; 555 | $--tree-expand-icon-color: $--color-text-placeholder !default; 556 | 557 | /* Dropdown 558 | -------------------------- */ 559 | $--dropdown-menu-box-shadow: $--box-shadow-light !default; 560 | $--dropdown-menuItem-hover-fill: $--color-primary-light-9 !default; 561 | $--dropdown-menuItem-hover-color: $--link-color !default; 562 | 563 | /* Badge 564 | -------------------------- */ 565 | $--badge-fill: $--color-danger !default; 566 | $--badge-radius: 10px !default; 567 | $--badge-font-size: 12px !default; 568 | $--badge-padding: 6px !default; 569 | $--badge-size: 18px !default; 570 | 571 | /* Card 572 | --------------------------*/ 573 | $--card-border-color: $--border-color-lighter !default; 574 | $--card-border-radius: 4px !default; 575 | $--card-padding: 20px !default; 576 | 577 | /* Slider 578 | --------------------------*/ 579 | $--slider-main-background-color: $--color-primary !default; 580 | $--slider-runway-background-color: $--border-color-light !default; 581 | $--slider-button-hover-color: mix($--color-primary, black, 97%) !default; 582 | $--slider-stop-background-color: $--color-white !default; 583 | $--slider-disable-color: $--color-text-placeholder !default; 584 | 585 | $--slider-margin: 16px 0 !default; 586 | $--slider-border-radius: 3px !default; 587 | $--slider-height: 6px !default; 588 | $--slider-button-size: 16px !default; 589 | $--slider-button-wrapper-size: 36px !default; 590 | $--slider-button-wrapper-offset: -15px !default; 591 | 592 | /* Steps 593 | --------------------------*/ 594 | $--steps-border-color: $--disabled-border-base !default; 595 | $--steps-border-radius: 4px !default; 596 | $--steps-padding: 20px !default; 597 | 598 | /* Menu 599 | --------------------------*/ 600 | $--menu-item-color: $--color-text-primary !default; 601 | $--menu-item-fill: $--color-white !default; 602 | $--menu-item-hover-fill: $--color-primary-light-9 !default; 603 | 604 | /* Rate 605 | --------------------------*/ 606 | $--rate-height: 20px !default; 607 | $--rate-font-size: $--font-size-base !default; 608 | $--rate-icon-size: 18px !default; 609 | $--rate-icon-margin: 6px !default; 610 | $--rate-icon-color: $--color-text-placeholder !default; 611 | 612 | /* DatePicker 613 | --------------------------*/ 614 | $--datepicker-color: $--color-text-regular !default; 615 | $--datepicker-off-color: $--color-text-placeholder !default; 616 | $--datepicker-header-color: $--color-text-regular !default; 617 | $--datepicker-icon-color: $--color-text-primary !default; 618 | $--datepicker-border-color: $--disabled-border-base !default; 619 | $--datepicker-inner-border-color: #e4e4e4 !default; 620 | $--datepicker-inrange-color: $--border-color-extra-light !default; 621 | $--datepicker-inrange-hover-color: $--border-color-extra-light !default; 622 | $--datepicker-active-color: $--color-primary !default; 623 | $--datepicker-text-hover-color: $--color-primary !default; 624 | $--datepicker-cell-hover-color: #fff !default; 625 | 626 | /* Loading 627 | --------------------------*/ 628 | $--loading-spinner-size: 42px !default; 629 | $--loading-fullscreen-spinner-size: 50px !default; 630 | 631 | /* Scrollbar 632 | --------------------------*/ 633 | $--scrollbar-background-color: rgba($--color-text-secondary, .3) !default; 634 | $--scrollbar-hover-background-color: rgba($--color-text-secondary, .5) !default; 635 | 636 | /* Carousel 637 | --------------------------*/ 638 | $--carousel-arrow-font-size: 12px !default; 639 | $--carousel-arrow-size: 36px !default; 640 | $--carousel-arrow-background: rgba(31, 45, 61, 0.11) !default; 641 | $--carousel-arrow-hover-background: rgba(31, 45, 61, 0.23) !default; 642 | $--carousel-indicator-width: 30px !default; 643 | $--carousel-indicator-height: 2px !default; 644 | $--carousel-indicator-padding-horizontal: 4px !default; 645 | $--carousel-indicator-padding-vertical: 12px !default; 646 | $--carousel-indicator-out-color: $--border-color-hover !default; 647 | 648 | /* Collapse 649 | --------------------------*/ 650 | $--collapse-border-color: $--border-color-lighter !default; 651 | $--collapse-header-height: 48px !default; 652 | $--collapse-header-padding: 20px !default; 653 | $--collapse-header-fill: $--color-white !default; 654 | $--collapse-header-color: $--color-text-primary !default; 655 | $--collapse-header-size: 13px !default; 656 | $--collapse-content-fill: $--color-white !default; 657 | $--collapse-content-size: 13px !default; 658 | $--collapse-content-color: $--color-text-primary !default; 659 | 660 | /* Transfer 661 | --------------------------*/ 662 | $--transfer-border-color: $--border-color-lighter !default; 663 | $--transfer-border-radius: $--border-radius-base !default; 664 | $--transfer-panel-width: 200px !default; 665 | $--transfer-panel-header-height: 40px !default; 666 | $--transfer-panel-header-background: $--background-color-base !default; 667 | $--transfer-panel-footer-height: 40px !default; 668 | $--transfer-panel-body-height: 246px !default; 669 | $--transfer-item-height: 30px !default; 670 | $--transfer-item-hover-background: $--color-text-secondary !default; 671 | $--transfer-filter-height: 32px !default; 672 | 673 | /* Header 674 | --------------------------*/ 675 | $--header-padding: 0 20px !default; 676 | 677 | /* Footer 678 | --------------------------*/ 679 | $--footer-padding: 0 20px !default; 680 | 681 | /* Main 682 | --------------------------*/ 683 | $--main-padding: 20px !default; 684 | 685 | /* Break-point 686 | --------------------------*/ 687 | $--sm: 768px !default; 688 | $--md: 992px !default; 689 | $--lg: 1200px !default; 690 | $--xl: 1920px !default; 691 | 692 | $--breakpoints: ( 693 | 'xs' : (max-width: $--sm), 694 | 'sm' : (min-width: $--sm), 695 | 'md' : (min-width: $--md), 696 | 'lg' : (min-width: $--lg), 697 | 'xl' : (min-width: $--xl) 698 | ); 699 | 700 | $--breakpoints-spec: ( 701 | 'xs-only' : (max-width: $--sm - 1), 702 | 'sm-and-up' : (min-width: $--sm), 703 | 'sm-only': "(min-width: #{$--sm}) and (max-width: #{$--md} - 1)", 704 | 'sm-and-down': (max-width: $--md - 1), 705 | 'md-and-up' : (min-width: $--md), 706 | 'md-only': "(min-width: #{$--md}) and (max-width: #{$--lg } - 1)", 707 | 'md-and-down': (max-width: $--lg - 1), 708 | 'lg-and-up' : (min-width: $--lg), 709 | 'lg-only': "(min-width: #{$--lg}) and (max-width: #{$--xl } - 1)", 710 | 'lg-and-down': (max-width: $--xl - 1), 711 | 'xl-only' : (min-width: $--xl), 712 | ); 713 | $--header-height: 60px; 714 | --------------------------------------------------------------------------------