diff --git a/.env b/.env new file mode 100644 index 0000000..54aec44 --- /dev/null +++ b/.env @@ -0,0 +1,4 @@ +# 正式环境 + +# 页面 title 前缀 +VUE_APP_TITLE=D2Admin diff --git a/.env.development b/.env.development new file mode 100644 index 0000000..c126ecf --- /dev/null +++ b/.env.development @@ -0,0 +1,4 @@ +# 开发环境 + +# 页面 title 前缀 +VUE_APP_TITLE=D2Admin Dev diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..d4a5a5a --- /dev/null +++ b/.eslintignore @@ -0,0 +1,11 @@ +# 忽略目录 +build/ +tests/ +node_modules/ + +# node 覆盖率文件 +coverage/ + +# 忽略文件 +**/*-min.js +**/*.min.js diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..a1fd69f --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,17 @@ +module.exports = { + root: true, + env: { + node: true + }, + 'extends': [ + 'plugin:vue/essential', + '@vue/standard' + ], + rules: { + 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', + 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' + }, + parserOptions: { + parser: 'babel-eslint' + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e67e1ec --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +.DS_Store +node_modules +/dist + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw* +package-lock.json +dev diff --git a/.postcssrc.js b/.postcssrc.js new file mode 100644 index 0000000..100cc01 --- /dev/null +++ b/.postcssrc.js @@ -0,0 +1,5 @@ +module.exports = { + plugins: { + autoprefixer: {} + } +} \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4b66e07 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 李杨 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100755 index 0000000..2cff96d --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# D2Admin 项目起始模板 + +只保留了基础功能,例如 + +* 主题系统 +* 登陆和注销 +* 数据持久化 +* 多标签页 +* 菜单和路由逻辑 +* ... + +[中文文档](http://d2admin.fairyever.com/zh/) + +**完整版** + +[Github 仓库](https://github.com/d2-projects/d2-admin) | +[码云仓库](https://gitee.com/fairyever/d2-admin) | +[在线预览地址](https://fairyever.gitee.io/d2-admin-preview) + +**简化版模板 (本项目)** + +[Github 仓库](https://github.com/d2-projects/d2-admin-start-kit) | +[码云仓库](https://gitee.com/fairyever/d2-admin-start-kit) | +[在线预览地址](https://fairyever.gitee.io/d2-admin-start-kit-preview/#/index) \ No newline at end of file diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000..ba17966 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,5 @@ +module.exports = { + presets: [ + '@vue/app' + ] +} diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..40186da --- /dev/null +++ b/jest.config.js @@ -0,0 +1,22 @@ +module.exports = { + moduleFileExtensions: [ + 'js', + 'jsx', + 'json', + 'vue' + ], + transform: { + '^.+\\.vue$': 'vue-jest', + '.+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub', + '^.+\\.jsx?$': 'babel-jest' + }, + moduleNameMapper: { + '^@/(.*)$': '/src/$1' + }, + snapshotSerializers: [ + 'jest-serializer-vue' + ], + testMatch: [ + '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)' + ] +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..19b648a --- /dev/null +++ b/package.json @@ -0,0 +1,50 @@ +{ + "name": "d2-admin", + "version": "1.1.5", + "private": true, + "scripts": { + "serve": "vue-cli-service serve --open", + "start": "vue-cli-service serve --open", + "dev": "vue-cli-service serve --open", + "build": "vue-cli-service build", + "lint": "vue-cli-service lint --fix", + "test:unit": "vue-cli-service test:unit" + }, + "dependencies": { + "axios": "^0.17.1", + "babel-polyfill": "^6.26.0", + "better-scroll": "^1.12.1", + "element-ui": "^2.4.4", + "js-cookie": "^2.2.0", + "lodash.uniqueid": "^4.0.1", + "lowdb": "^1.0.0", + "mockjs": "^1.0.1-beta3", + "particles.js": "^2.0.0", + "screenfull": "^3.3.2", + "ua-parser-js": "^0.7.18", + "vue": "^2.5.16", + "vue-i18n": "^7.4.2", + "vue-router": "^3.0.1", + "vuex": "^3.0.1" + }, + "devDependencies": { + "@kazupon/vue-i18n-loader": "^0.3.0", + "@vue/cli-plugin-babel": "^3.0.0-rc.4", + "@vue/cli-plugin-eslint": "^3.0.0-rc.4", + "@vue/cli-plugin-unit-jest": "^3.0.0-rc.4", + "@vue/cli-service": "^3.0.0-rc.4", + "@vue/eslint-config-standard": "^3.0.0-rc.4", + "@vue/test-utils": "^1.0.0-beta.20", + "babel-core": "7.0.0-bridge.0", + "babel-jest": "^23.0.1", + "node-sass": "^4.9.0", + "sass-loader": "^7.0.1", + "svg-sprite-loader": "^3.8.0", + "vue-template-compiler": "^2.5.16" + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not ie <= 8" + ] +} diff --git a/public/icon.ico b/public/icon.ico new file mode 100644 index 0000000..765befd Binary files /dev/null and b/public/icon.ico differ diff --git a/public/image/theme/d2/logo/all.png b/public/image/theme/d2/logo/all.png new file mode 100644 index 0000000..57b6511 Binary files /dev/null and b/public/image/theme/d2/logo/all.png differ diff --git a/public/image/theme/d2/logo/icon-only.png b/public/image/theme/d2/logo/icon-only.png new file mode 100644 index 0000000..6b3773e Binary files /dev/null and b/public/image/theme/d2/logo/icon-only.png differ diff --git a/public/image/theme/d2/preview@2x.png b/public/image/theme/d2/preview@2x.png new file mode 100644 index 0000000..e9efd92 Binary files /dev/null and b/public/image/theme/d2/preview@2x.png differ diff --git a/public/image/theme/line/bg.jpg b/public/image/theme/line/bg.jpg new file mode 100644 index 0000000..4945760 Binary files /dev/null and b/public/image/theme/line/bg.jpg differ diff --git a/public/image/theme/line/logo/all.png b/public/image/theme/line/logo/all.png new file mode 100644 index 0000000..57b6511 Binary files /dev/null and b/public/image/theme/line/logo/all.png differ diff --git a/public/image/theme/line/logo/icon-only.png b/public/image/theme/line/logo/icon-only.png new file mode 100644 index 0000000..6b3773e Binary files /dev/null and b/public/image/theme/line/logo/icon-only.png differ diff --git a/public/image/theme/line/preview@2x.png b/public/image/theme/line/preview@2x.png new file mode 100644 index 0000000..3b635a3 Binary files /dev/null and b/public/image/theme/line/preview@2x.png differ diff --git a/public/image/theme/star/bg.jpg b/public/image/theme/star/bg.jpg new file mode 100644 index 0000000..2176e39 Binary files /dev/null and b/public/image/theme/star/bg.jpg differ diff --git a/public/image/theme/star/logo/all.png b/public/image/theme/star/logo/all.png new file mode 100644 index 0000000..7075a57 Binary files /dev/null and b/public/image/theme/star/logo/all.png differ diff --git a/public/image/theme/star/logo/icon-only.png b/public/image/theme/star/logo/icon-only.png new file mode 100644 index 0000000..d2f4553 Binary files /dev/null and b/public/image/theme/star/logo/icon-only.png differ diff --git a/public/image/theme/star/preview@2x.png b/public/image/theme/star/preview@2x.png new file mode 100644 index 0000000..f6e4ccc Binary files /dev/null and b/public/image/theme/star/preview@2x.png differ diff --git a/public/image/theme/tomorrow-night-blue/logo/all.png b/public/image/theme/tomorrow-night-blue/logo/all.png new file mode 100644 index 0000000..bacd92a Binary files /dev/null and b/public/image/theme/tomorrow-night-blue/logo/all.png differ diff --git a/public/image/theme/tomorrow-night-blue/logo/icon-only.png b/public/image/theme/tomorrow-night-blue/logo/icon-only.png new file mode 100644 index 0000000..e3fc226 Binary files /dev/null and b/public/image/theme/tomorrow-night-blue/logo/icon-only.png differ diff --git a/public/image/theme/tomorrow-night-blue/preview@2x.png b/public/image/theme/tomorrow-night-blue/preview@2x.png new file mode 100644 index 0000000..04a4209 Binary files /dev/null and b/public/image/theme/tomorrow-night-blue/preview@2x.png differ diff --git a/public/image/theme/violet/logo/all.png b/public/image/theme/violet/logo/all.png new file mode 100644 index 0000000..7075a57 Binary files /dev/null and b/public/image/theme/violet/logo/all.png differ diff --git a/public/image/theme/violet/logo/icon-only.png b/public/image/theme/violet/logo/icon-only.png new file mode 100644 index 0000000..d2f4553 Binary files /dev/null and b/public/image/theme/violet/logo/icon-only.png differ diff --git a/public/image/theme/violet/preview@2x.png b/public/image/theme/violet/preview@2x.png new file mode 100644 index 0000000..a0c6a07 Binary files /dev/null and b/public/image/theme/violet/preview@2x.png differ diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..3c804e7 --- /dev/null +++ b/public/index.html @@ -0,0 +1,180 @@ + + + + + + + + <%= VUE_APP_TITLE %> + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
加载中
+
欢迎使用 D2Admin 进行你的开发,初次加载会比较慢,请耐心等待
+
如果很久很久都没有加载成功,请清空缓存重新加载页面
+
+ Github仓库 + 中文文档 +
+
+
+ + + diff --git a/public/is-mobile.html b/public/is-mobile.html new file mode 100644 index 0000000..9a9d59c --- /dev/null +++ b/public/is-mobile.html @@ -0,0 +1,13 @@ + + + + + + + 提示信息 + + +

请不要使用手机浏览

+ + + \ No newline at end of file diff --git a/public/script/ua-parser.min.js b/public/script/ua-parser.min.js new file mode 100644 index 0000000..ad74810 --- /dev/null +++ b/public/script/ua-parser.min.js @@ -0,0 +1,9 @@ +/*! + * UAParser.js v0.7.18 + * Lightweight JavaScript-based User-Agent string parser + * https://github.com/faisalman/ua-parser-js + * + * Copyright © 2012-2016 Faisal Salman + * Dual licensed under GPLv2 or MIT + */ +(function(window,undefined){"use strict";var LIBVERSION="0.7.18",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",EMBEDDED="embedded";var util={extend:function(regexes,extensions){var margedRegexes={};for(var i in regexes){if(extensions[i]&&extensions[i].length%2===0){margedRegexes[i]=extensions[i].concat(regexes[i])}else{margedRegexes[i]=regexes[i]}}return margedRegexes},has:function(str1,str2){if(typeof str1==="string"){return str2.toLowerCase().indexOf(str1.toLowerCase())!==-1}else{return false}},lowerize:function(str){return str.toLowerCase()},major:function(version){return typeof version===STR_TYPE?version.replace(/[^\d\.]/g,"").split(".")[0]:undefined},trim:function(str){return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"")}};var mapper={rgx:function(ua,arrays){var i=0,j,k,p,q,matches,match;while(i0){if(q.length==2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length==3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length==4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},str:function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j +
+ +
+ + + + + diff --git a/src/assets/style/animate/vue-transition.scss b/src/assets/style/animate/vue-transition.scss new file mode 100644 index 0000000..a396d0b --- /dev/null +++ b/src/assets/style/animate/vue-transition.scss @@ -0,0 +1,27 @@ +// 过渡动画 横向渐变 +.fade-transverse-leave-active, +.fade-transverse-enter-active { + transition: all .5s; +} +.fade-transverse-enter { + opacity: 0; + transform: translateX(-30px); +} +.fade-transverse-leave-to { + opacity: 0; + transform: translateX(30px); +} + +// 过渡动画 缩放渐变 +.fade-scale-leave-active, +.fade-scale-enter-active { + transition: all .5s; +} +.fade-scale-enter { + opacity: 0; + transform: scale(1.2); +} +.fade-scale-leave-to { + opacity: 0; + transform: scale(0.8); +} \ No newline at end of file diff --git a/src/assets/style/fixed/base.scss b/src/assets/style/fixed/base.scss new file mode 100644 index 0000000..4ef1dd2 --- /dev/null +++ b/src/assets/style/fixed/base.scss @@ -0,0 +1,13 @@ +// 优化显示 + +html, body { + margin: 0px; + height: 100%; + font-family: "Arial", "Microsoft YaHei", "黑体", "宋体", sans-serif; + #app { + @extend %full; + a { + text-decoration: none; + } + } +} \ No newline at end of file diff --git a/src/assets/style/fixed/element.scss b/src/assets/style/fixed/element.scss new file mode 100644 index 0000000..f627560 --- /dev/null +++ b/src/assets/style/fixed/element.scss @@ -0,0 +1,16 @@ +// element 样式补丁 + +.el-card { + &.is-always-shadow { + box-shadow: 0 0 8px 0 rgba(232,237,250,.6), 0 2px 4px 0 rgba(232,237,250,.5); + } + &.is-hover-shadow { + &:hover { + box-shadow: 0 0 8px 0 rgba(232,237,250,.6), 0 2px 4px 0 rgba(232,237,250,.5); + } + } +} + +.el-menu--horizontal { + border-bottom: none !important; +} \ No newline at end of file diff --git a/src/assets/style/public-class.scss b/src/assets/style/public-class.scss new file mode 100644 index 0000000..f7e1443 --- /dev/null +++ b/src/assets/style/public-class.scss @@ -0,0 +1,65 @@ +@import 'public'; + +// 补丁 base +@import '~@/assets/style/fixed/base.scss'; +// 补丁 element +@import '~@/assets/style/fixed/element.scss'; + +// 动画 +@import '~@/assets/style/animate/vue-transition.scss'; + +// 在这里写公用的class +// 注意 这个文件里只写class +// mixin等内容请在 public.scss 里书写 + +// 文字相关 +.#{$prefix}-text-center { + text-align: center; +} + +// 浮动相关 +.#{$prefix}-fl { + float: left; +} +.#{$prefix}-fr { + float: right; +} +.#{$prefix}-clearfix:before, +.#{$prefix}-clearfix:after { + display: table; + content: ""; +} +.#{$prefix}-clearfix:after { + clear: both +} + +// 边距相关 +$sizes: (0, 5, 10, 15, 20); + +@for $index from 1 to 6 { + .#{$prefix}-m-#{nth($sizes, $index)} { margin: #{nth($sizes, $index)}px !important; } + .#{$prefix}-mt-#{nth($sizes, $index)} { margin-top: #{nth($sizes, $index)}px !important; } + .#{$prefix}-mr-#{nth($sizes, $index)} { margin-right: #{nth($sizes, $index)}px !important; } + .#{$prefix}-mb-#{nth($sizes, $index)} { margin-bottom: #{nth($sizes, $index)}px !important; } + .#{$prefix}-ml-#{nth($sizes, $index)} { margin-left: #{nth($sizes, $index)}px !important; } + + .#{$prefix}-p-#{nth($sizes, $index)} { padding: #{nth($sizes, $index)}px !important; } + .#{$prefix}-pt-#{nth($sizes, $index)} { padding-top: #{nth($sizes, $index)}px !important; } + .#{$prefix}-pr-#{nth($sizes, $index)} { padding-right: #{nth($sizes, $index)}px !important; } + .#{$prefix}-pb-#{nth($sizes, $index)} { padding-bottom: #{nth($sizes, $index)}px !important; } + .#{$prefix}-pl-#{nth($sizes, $index)} { padding-left: #{nth($sizes, $index)}px !important; } +} + +// 快速使用 + +.#{$prefix}-m { margin: 20px !important; } +.#{$prefix}-mt { margin-top: 20px !important; } +.#{$prefix}-mr { margin-right: 20px !important; } +.#{$prefix}-mb { margin-bottom: 20px !important; } +.#{$prefix}-ml { margin-left: 20px !important; } + +.#{$prefix}-p { padding: 20px !important; } +.#{$prefix}-pt { padding-top: 20px !important; } +.#{$prefix}-pr { padding-right: 20px !important; } +.#{$prefix}-pb { padding-bottom: 20px !important; } +.#{$prefix}-pl { padding-left: 20px !important; } diff --git a/src/assets/style/public.scss b/src/assets/style/public.scss new file mode 100644 index 0000000..7e444df --- /dev/null +++ b/src/assets/style/public.scss @@ -0,0 +1,44 @@ +@import '~@/assets/style/unit/color.scss'; + +// 工具类名统一前缀 +$prefix: d2; + +// 禁止用户选中 鼠标变为手形 +%unable-select { + user-select: none; + cursor: pointer; +} + +// 填满父元素 +// 组要父元素 position: relative | absolute; +%full { + position: absolute; + top: 0px; + right: 0px; + bottom: 0px; + left: 0px; +} + +// flex 垂直水平居中 +%flex-center-row { + display: flex; + justify-content: center; + align-items: center; + flex-direction: row; +} +%flex-center-col { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; +} + +// 将元素模拟成卡片外观 +%card { + border: 1px solid #dddee1; + border-color: #e9eaec; + background: #fff; + border-radius: 4px; + font-size: 14px; + position: relative; +} \ No newline at end of file diff --git a/src/assets/style/theme/d2/index.scss b/src/assets/style/theme/d2/index.scss new file mode 100644 index 0000000..7b69d57 --- /dev/null +++ b/src/assets/style/theme/d2/index.scss @@ -0,0 +1,2 @@ +@import './setting.scss'; +@import '../theme.scss'; diff --git a/src/assets/style/theme/d2/setting.scss b/src/assets/style/theme/d2/setting.scss new file mode 100644 index 0000000..827d5af --- /dev/null +++ b/src/assets/style/theme/d2/setting.scss @@ -0,0 +1,59 @@ +// 主题名称 +$theme-name: 'd2'; +// 主题背景颜色 +$theme-bg-color: #ebf1f6; +// 主题背景图片遮罩 +$theme-bg-mask: rgba(#000, 0); + +// container组件 +$theme-container-background-color: rgba(#FFF, 1); +$theme-container-header-footer-background-color: #FFF; +$theme-container-border-inner: 1px solid #cfd7e5; +$theme-container-border-outer: 1px solid #cfd7e5; + +$theme-multiple-page-control-color: $color-text-normal; +$theme-multiple-page-control-color-active: #2f74ff; +$theme-multiple-page-control-nav-prev-color: #cfd7e5; +$theme-multiple-page-control-nav-next-color: #cfd7e5; +$theme-multiple-page-control-border-color: #cfd7e5; +$theme-multiple-page-control-border-color-active: #FFF; +$theme-multiple-page-control-background-color: rgba(#000, .03); +$theme-multiple-page-control-background-color-active: #FFF; + +// 顶栏和侧边栏中展开的菜单 hover 状态下 +$theme-menu-item-color-hover: #293849; +$theme-menu-item-background-color-hover: #ecf5ff; + +// 顶栏上的文字颜色 +$theme-header-item-color: $color-text-normal; +$theme-header-item-background-color: transparent; +// 顶栏上的项目在 hover 时 +$theme-header-item-color-hover: #2f74ff; +$theme-header-item-background-color-hover: rgba(#FFF, .5); +// 顶栏上的项目在 focus 时 +$theme-header-item-color-focus: #2f74ff; +$theme-header-item-background-color-focus: rgba(#FFF, .5); +// 顶栏上的项目在 active 时 +$theme-header-item-color-active: #2f74ff; +$theme-header-item-background-color-active: rgba(#FFF, .5); + +// 侧边栏上的文字颜色 +$theme-aside-item-color: $color-text-normal; +$theme-aside-item-background-color: transparent; +// 侧边栏上的项目在 hover 时 +$theme-aside-item-color-hover: #2f74ff; +$theme-aside-item-background-color-hover: rgba(#FFF, .5); +// 侧边栏上的项目在 focus 时 +$theme-aside-item-color-focus: #2f74ff; +$theme-aside-item-background-color-focus: rgba(#FFF, .5); +// 侧边栏上的项目在 active 时 +$theme-aside-item-color-active: #2f74ff; +$theme-aside-item-background-color-active: rgba(#FFF, .5); + +// 侧边栏菜单为空的时候显示的元素 +$theme-aside-menu-empty-icon-color: $color-text-normal; +$theme-aside-menu-empty-text-color: $color-text-normal; +$theme-aside-menu-empty-background-color: rgba(#000, .03); +$theme-aside-menu-empty-icon-color-hover: $color-text-main; +$theme-aside-menu-empty-text-color-hover: $color-text-main; +$theme-aside-menu-empty-background-color-hover: rgba(#000, .05); \ No newline at end of file diff --git a/src/assets/style/theme/line/index.scss b/src/assets/style/theme/line/index.scss new file mode 100644 index 0000000..7b69d57 --- /dev/null +++ b/src/assets/style/theme/line/index.scss @@ -0,0 +1,2 @@ +@import './setting.scss'; +@import '../theme.scss'; diff --git a/src/assets/style/theme/line/setting.scss b/src/assets/style/theme/line/setting.scss new file mode 100644 index 0000000..1b56ce2 --- /dev/null +++ b/src/assets/style/theme/line/setting.scss @@ -0,0 +1,59 @@ +// 主题名称 +$theme-name: 'line'; +// 主题背景颜色 +$theme-bg-color: #f8f8f9; +// 主题背景图片遮罩 +$theme-bg-mask: rgba(#000, 0); + +// container组件 +$theme-container-background-color: rgba(#FFF, .8); +$theme-container-header-footer-background-color: #FFF; +$theme-container-border-inner: 1px solid $color-border-2; +$theme-container-border-outer: 1px solid #cfd7e5; + +$theme-multiple-page-control-color: #FFF; +$theme-multiple-page-control-color-active: $color-text-normal; +$theme-multiple-page-control-nav-prev-color: #cfd7e5; +$theme-multiple-page-control-nav-next-color: #cfd7e5; +$theme-multiple-page-control-border-color: #cfd7e5; +$theme-multiple-page-control-border-color-active: #FFF; +$theme-multiple-page-control-background-color: #cfd7e5; +$theme-multiple-page-control-background-color-active: #FFF; + +// 顶栏和侧边栏中展开的菜单 hover 状态下 +$theme-menu-item-color-hover: #293849; +$theme-menu-item-background-color-hover: #EFEFEF; + +// 顶栏上的文字颜色 +$theme-header-item-color: $color-text-normal; +$theme-header-item-background-color: transparent; +// 顶栏上的项目在 hover 时 +$theme-header-item-color-hover: $color-text-main; +$theme-header-item-background-color-hover: rgba(#000, .02); +// 顶栏上的项目在 focus 时 +$theme-header-item-color-focus: $color-text-main; +$theme-header-item-background-color-focus: rgba(#000, .02); +// 顶栏上的项目在 active 时 +$theme-header-item-color-active: $color-text-main; +$theme-header-item-background-color-active: rgba(#000, .03); + +// 侧边栏上的文字颜色 +$theme-aside-item-color: $color-text-normal; +$theme-aside-item-background-color: transparent; +// 侧边栏上的项目在 hover 时 +$theme-aside-item-color-hover: $color-text-main; +$theme-aside-item-background-color-hover: rgba(#000, .02); +// 侧边栏上的项目在 focus 时 +$theme-aside-item-color-focus: $color-text-main; +$theme-aside-item-background-color-focus: rgba(#000, .02); +// 侧边栏上的项目在 active 时 +$theme-aside-item-color-active: $color-text-main; +$theme-aside-item-background-color-active: rgba(#000, .03); + +// 侧边栏菜单为空的时候显示的元素 +$theme-aside-menu-empty-icon-color: $color-text-normal; +$theme-aside-menu-empty-text-color: $color-text-normal; +$theme-aside-menu-empty-background-color: rgba(#000, .03); +$theme-aside-menu-empty-icon-color-hover: $color-text-main; +$theme-aside-menu-empty-text-color-hover: $color-text-main; +$theme-aside-menu-empty-background-color-hover: rgba(#000, .05); \ No newline at end of file diff --git a/src/assets/style/theme/list.js b/src/assets/style/theme/list.js new file mode 100644 index 0000000..c2c4a2e --- /dev/null +++ b/src/assets/style/theme/list.js @@ -0,0 +1,29 @@ +export default [ + { + title: 'd2admin 经典', + name: 'd2', + preview: 'image/theme/d2/preview@2x.png' + }, + { + title: '紫罗兰', + name: 'violet', + preview: 'image/theme/violet/preview@2x.png' + }, + { + title: '简约线条', + name: 'line', + backgroundImage: 'image/theme/line/bg.jpg', + preview: 'image/theme/line/preview@2x.png' + }, + { + title: '流星', + name: 'star', + backgroundImage: 'image/theme/star/bg.jpg', + preview: 'image/theme/star/preview@2x.png' + }, + { + title: 'Tomorrow Night Blue (vsCode)', + name: 'tomorrow-night-blue', + preview: 'image/theme/tomorrow-night-blue/preview@2x.png' + } +] diff --git a/src/assets/style/theme/register.scss b/src/assets/style/theme/register.scss new file mode 100644 index 0000000..92ef382 --- /dev/null +++ b/src/assets/style/theme/register.scss @@ -0,0 +1,8 @@ +@import '~@/assets/style/public.scss'; +@import '~@/assets/style/theme/theme-base.scss'; + +@import '~@/assets/style/theme/d2/index.scss'; +@import '~@/assets/style/theme/line/index.scss'; +@import '~@/assets/style/theme/star/index.scss'; +@import '~@/assets/style/theme/tomorrow-night-blue/index.scss'; +@import '~@/assets/style/theme/violet/index.scss'; \ No newline at end of file diff --git a/src/assets/style/theme/star/index.scss b/src/assets/style/theme/star/index.scss new file mode 100644 index 0000000..7b69d57 --- /dev/null +++ b/src/assets/style/theme/star/index.scss @@ -0,0 +1,2 @@ +@import './setting.scss'; +@import '../theme.scss'; diff --git a/src/assets/style/theme/star/setting.scss b/src/assets/style/theme/star/setting.scss new file mode 100644 index 0000000..88fd395 --- /dev/null +++ b/src/assets/style/theme/star/setting.scss @@ -0,0 +1,59 @@ +// 主题名称 +$theme-name: 'star'; +// 主题背景颜色 +$theme-bg-color: #EFF4F8; +// 主题背景图片遮罩 +$theme-bg-mask: rgba(#000, .3); + +// container组件 +$theme-container-background-color: rgba(#FFF, .9); +$theme-container-header-footer-background-color: #FFF; +$theme-container-border-inner: 1px solid $color-border-1; +$theme-container-border-outer: 1px solid #114450; + +$theme-multiple-page-control-color: #FFF; +$theme-multiple-page-control-color-active: $color-text-normal; +$theme-multiple-page-control-nav-prev-color: #FFF; +$theme-multiple-page-control-nav-next-color: #FFF; +$theme-multiple-page-control-border-color: #114450; +$theme-multiple-page-control-border-color-active: #FFF; +$theme-multiple-page-control-background-color: rgba(#FFF, .5); +$theme-multiple-page-control-background-color-active: #FFF; + +// 顶栏和侧边栏中展开的菜单 hover 状态下 +$theme-menu-item-color-hover: #293849; +$theme-menu-item-background-color-hover: #ecf5ff; + +// 顶栏上的文字颜色 +$theme-header-item-color: #FFF; +$theme-header-item-background-color: transparent; +// 顶栏上的项目在 hover 时 +$theme-header-item-color-hover: #FFF; +$theme-header-item-background-color-hover: rgba(#000, .2); +// 顶栏上的项目在 focus 时 +$theme-header-item-color-focus: #FFF; +$theme-header-item-background-color-focus: rgba(#000, .2); +// 顶栏上的项目在 active 时 +$theme-header-item-color-active: #FFF; +$theme-header-item-background-color-active: rgba(#000, .3); + +// 侧边栏上的文字颜色 +$theme-aside-item-color: #FFF; +$theme-aside-item-background-color: transparent; +// 侧边栏上的项目在 hover 时 +$theme-aside-item-color-hover: #FFF; +$theme-aside-item-background-color-hover: rgba(#000, .2); +// 侧边栏上的项目在 focus 时 +$theme-aside-item-color-focus: #FFF; +$theme-aside-item-background-color-focus: rgba(#000, .2); +// 侧边栏上的项目在 active 时 +$theme-aside-item-color-active: #FFF; +$theme-aside-item-background-color-active: rgba(#000, .3); + +// 侧边栏菜单为空的时候显示的元素 +$theme-aside-menu-empty-icon-color: #FFF; +$theme-aside-menu-empty-text-color: #FFF; +$theme-aside-menu-empty-background-color: rgba(#FFF, .2); +$theme-aside-menu-empty-icon-color-hover: #FFF; +$theme-aside-menu-empty-text-color-hover: #FFF; +$theme-aside-menu-empty-background-color-hover: rgba(#FFF, .3); \ No newline at end of file diff --git a/src/assets/style/theme/theme-base.scss b/src/assets/style/theme/theme-base.scss new file mode 100644 index 0000000..e8bc362 --- /dev/null +++ b/src/assets/style/theme/theme-base.scss @@ -0,0 +1,432 @@ +// 减小弹出菜单的项目高度 +.el-menu--popup { + .el-menu-item { + height: 36px; + line-height: 36px; + } + .el-submenu__title { + height: 36px; + line-height: 36px; + } +} + +// 整体框架结构 +.d2-layout-header-aside-group { + height: 100%; + width: 100%; + min-width: 900px; + background-size: cover; + background-position: center; + overflow: hidden; + position: relative; + // 背景上面的半透明遮罩 + .d2-layout-header-aside-mask { + @extend %full; + } + // 内容层 + .d2-layout-header-aside-content { + @extend %full; + $d2-theme-header-height: 60px; + .d2-theme-header { + position: absolute; + top: 0px; + left: 0px; + right: 0px; + height: $d2-theme-header-height; + } + .d2-theme-container { + position: absolute; + top: $d2-theme-header-height; + bottom: 0px; + left: 0px; + right: 0px; + display: flex; + flex-direction: row; + .d2-theme-container-aside { + transition: width .3s; + flex-grow: 0; + position: relative; + .d2-layout-header-aside-menu-side { + @extend %full; + overflow: hidden; + } + } + .d2-theme-container-main { + flex-grow: 1; + padding: 0px; + position: relative; + overflow: hidden; + display: flex; + flex-direction: column; + .d2-theme-container-main-header { + flex-grow: 0; + } + .d2-theme-container-main-body { + flex-grow: 1; + margin-top: 1px; + position: relative; + } + } + } + } +} + + + + + + + + +// 主题公用 +.d2-layout-header-aside-group { + &.grayMode { + -webkit-filter: grayscale(100%); + -moz-filter: grayscale(100%); + -ms-filter: grayscale(100%); + -o-filter: grayscale(100%); + filter: grayscale(100%); + filter: gray; + } + // 主体 + .d2-layout-header-aside-content { + // [布局] 顶栏 + .d2-theme-header { + // logo区域 + .logo-group { + transition: width .3s; + float: left; + text-align: center; + img { + height: 60px; + } + } + // 折叠侧边栏切换按钮 + .toggle-aside-btn { + float: left; + height: 60px; + width: 60px; + display: flex; + justify-content: center; + align-items: center; + @extend %unable-select; + i { + font-size: 20px; + margin-top: 4px; + } + } + // [菜单] 顶栏 + .el-menu { + float: left; + border-bottom: none; + background-color: transparent; + %header-menu-item { + @extend %unable-select; + i.fa { + font-size: 16px; + margin-right: 4px; + } + } + .el-menu-item { + @extend %header-menu-item; + border-bottom: none; + } + .el-submenu { + @extend %header-menu-item; + .el-submenu__title { + border-bottom: none; + } + } + } + // 顶栏右侧的按钮 + .d2-header-right { + float: right; + height: 60px; + display: flex; + align-items: center; + .btn-text { + padding: 14px 12px; + border-radius: 4px; + margin: 0px !important; + margin-left: 10px !important; + } + .el-dropdown { + @extend %unable-select; + } + } + } + // [布局] 顶栏下面 + .d2-theme-container { + // 侧边栏 + .d2-theme-container-aside { + %d2-theme-container-aside-menu-icon { + width: 20px; + text-align: center; + font-size: 16px; + } + // [菜单] 正常状态 + .el-menu { + @extend %unable-select; + background-color: transparent; + border-right: none; + .el-menu-item { + i { + @extend %d2-theme-container-aside-menu-icon; + } + } + } + .el-submenu { + @extend %unable-select; + .el-submenu__title { + i { + @extend %d2-theme-container-aside-menu-icon; + } + .el-submenu__icon-arrow { + margin-top: -10px; + } + } + } + // 菜单为空的时候显示的信息 + .d2-layout-header-aside-menu-empty { + height: 160px; + margin: 10px; + margin-top: 0px; + border-radius: 4px; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + @extend %unable-select; + i { + font-size: 30px; + margin-bottom: 10px; + } + span { + font-size: 14px; + } + } + // [菜单] 折叠状态 + .el-menu--collapse { + background-color: transparent; + .el-submenu__title { + text-align: center; + } + } + } + // 右下 主体 + .d2-theme-container-main { + // 主体部分分为多页面控制器 和主体 + .d2-theme-container-main-header { + // 多页面控制器 + .d2-multiple-page-control-group { + display: flex; + margin-right: 20px; + .d2-multiple-page-control-content { + flex-grow: 1; + position: relative; + .d2-multiple-page-control-content-inner { + position: absolute; + left: 0px; + right: 0px; + .d2-multiple-page-control { + .el-tabs__header.is-top { + margin: 0px; + } + .el-tabs__nav { + overflow: hidden; + } + } + } + } + .d2-multiple-page-control-btn { + position: relative; + bottom: -1px; + .el-dropdown { + .el-button-group { + .el-button:first-child { + border-bottom-left-radius: 0px; + } + .el-button:last-child { + border-bottom-right-radius: 0px; + } + } + } + } + } + } + // 主体 + .d2-theme-container-main-body { + // 布局组件 + .container-component { + @extend %full; + overflow: hidden; + // 填充式布局组件 + .d2-container-full { + position: absolute; + top: 0px; + right: 20px; + bottom: 0px; + left: 0px; + display: flex; + flex-direction: column; + overflow: hidden; + .d2-container-full__header { + padding: 20px; + } + .d2-container-full__body { + flex-grow: 1; + padding: 20px 20px; + overflow: auto; + position: relative; + } + .d2-container-full__footer { + padding: 20px; + } + } + // 填充式布局组件 - 滚动优化 + .d2-container-full-bs { + position: absolute; + top: 0px; + right: 20px; + bottom: 0px; + left: 0px; + display: flex; + flex-direction: column; + overflow: hidden; + .d2-container-full-bs__header { + padding: 20px; + } + .d2-container-full-bs__body { + flex-grow: 1; + overflow: hidden; + position: relative; + .d2-container-full-bs__body-wrapper-inner { + padding: 20px; + position: relative; + } + } + .d2-container-full-bs__footer { + padding: 20px; + } + } + // 隐形布局组件 + .d2-container-ghost { + position: absolute; + top: 0px; + right: 20px; + bottom: 0px; + left: 0px; + display: flex; + flex-direction: column; + overflow: hidden; + .d2-container-ghost__header { + padding: 20px; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + } + .d2-container-ghost__body { + flex-grow: 1; + overflow: auto; + position: relative; + } + .d2-container-ghost__footer { + padding: 20px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + } + } + // 隐形布局组件 - 滚动优化 + .d2-container-ghost-bs { + position: absolute; + top: 0px; + right: 20px; + bottom: 0px; + left: 0px; + display: flex; + flex-direction: column; + overflow: hidden; + .d2-container-ghost-bs__header { + padding: 20px; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + } + .d2-container-ghost-bs__body { + flex-grow: 1; + overflow: hidden; + position: relative; + } + .d2-container-ghost-bs__footer { + padding: 20px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + } + } + // 卡片式布局组件 + .d2-container-card { + position: absolute; + top: 0px; + right: 20px; + bottom: 0px; + left: 0px; + display: flex; + flex-direction: column; + overflow: hidden; + .d2-container-card__header { + padding: 20px; + } + .d2-container-card__body { + flex-grow: 1; + overflow: auto; + .d2-container-card__body-card { + position: relative; + margin-bottom: 20px; + padding: 20px; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + } + } + .d2-container-card__footer { + padding: 20px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + } + } + // 卡片式布局组件 - 滚动优化 + .d2-container-card-bs { + position: absolute; + top: 0px; + right: 20px; + bottom: 0px; + left: 0px; + display: flex; + flex-direction: column; + overflow: hidden; + .d2-container-card-bs__header { + padding: 20px; + } + .d2-container-card-bs__body { + position: relative; + flex-grow: 1; + overflow: hidden; + .d2-container-card-bs__body-wrapper-inner { + padding-bottom: 20px; + } + .d2-container-card-bs__body-card { + position: relative; + padding: 20px; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + } + } + .d2-container-card-bs__footer { + padding: 20px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/src/assets/style/theme/theme.scss b/src/assets/style/theme/theme.scss new file mode 100644 index 0000000..d08a658 --- /dev/null +++ b/src/assets/style/theme/theme.scss @@ -0,0 +1,389 @@ +// 每个主题特有的设置 +.theme-#{$theme-name} { + + .el-card { + &.d2-card { + border: $theme-container-border-outer; + .el-card__header { + border-bottom: $theme-container-border-outer; + } + } + } + + // 背景图片和遮罩 + .d2-layout-header-aside-group { + background-color: $theme-bg-color; + .d2-layout-header-aside-mask { + background: $theme-bg-mask; + } + } + + // 菜单项目 + @mixin theme-menu-hover-style { + color: $theme-menu-item-color-hover; + i.fa { + color: $theme-menu-item-color-hover; + } + background: $theme-menu-item-background-color-hover; + } + %el-menu-icon { + i.fa { + display: inline-block; + width: 15px; + text-align: center; + margin-right: 5px; + } + } + .el-submenu__title { + @extend %unable-select; + @extend %el-menu-icon; + } + .el-menu-item { + @extend %unable-select; + @extend %el-menu-icon; + } + .el-submenu__title:hover { + @include theme-menu-hover-style; + } + .el-menu-item:hover { + @include theme-menu-hover-style; + } + .el-menu--horizontal .el-menu-item:not(.is-disabled):hover { + @include theme-menu-hover-style; + } + .el-menu--horizontal .el-menu .el-submenu__title:hover { + @include theme-menu-hover-style; + } + + // 顶栏 + .d2-theme-header { + // 切换按钮 + .toggle-aside-btn { + i { + color: $theme-header-item-color; + background: $theme-header-item-background-color; + &:hover { + color: $theme-header-item-color-hover; + } + } + } + // 顶栏菜单 + .el-menu { + .el-menu-item { + transition: border-top-color 0s; + color: $theme-header-item-color; + background: $theme-header-item-background-color; + i.fa { color: inherit; } + &:hover { + color: $theme-header-item-color-hover; + background: $theme-header-item-background-color-hover; + i.fa { color: inherit; } + } + &:focus { + color: $theme-header-item-color-focus; + background: $theme-header-item-background-color-focus; + i.fa { color: inherit; } + } + &.is-active { + color: $theme-header-item-color-active; + background: $theme-header-item-background-color-active; + i.fa { color: inherit; } + } + } + .el-submenu { + .el-submenu__title { + transition: border-top-color 0s; + color: $theme-header-item-color; + background: $theme-header-item-background-color; + i.fa { color: inherit; } + .el-submenu__icon-arrow { + color: $theme-header-item-color; + } + &:hover { + color: $theme-header-item-color-hover; + background: $theme-header-item-background-color-hover; + i.fa { color: inherit; } + .el-submenu__icon-arrow { + color: $theme-header-item-color-hover; + } + } + &:focus { + color: $theme-header-item-color-focus; + background: $theme-header-item-background-color-focus; + i.fa { color: inherit; } + .el-submenu__icon-arrow { + color: $theme-header-item-color-focus; + } + } + } + } + } + // 顶栏右侧 + .d2-header-right { + .btn-text { + color: $theme-header-item-color; + &.can-hover { + &:hover { + color: $theme-header-item-color-hover; + background: $theme-header-item-background-color-hover; + } + } + } + } + } + // [布局] 顶栏下面 + .d2-theme-container { + // 侧边栏 + .d2-theme-container-aside { + // 菜单为空的时候显示的信息 + .d2-layout-header-aside-menu-empty { + background: $theme-aside-menu-empty-background-color; + i { + color: $theme-aside-menu-empty-icon-color; + } + span { + color: $theme-aside-menu-empty-text-color; + } + &:hover { + background: $theme-aside-menu-empty-background-color-hover; + i { + color: $theme-aside-menu-empty-icon-color-hover; + } + span { + color: $theme-aside-menu-empty-text-color-hover; + } + } + } + // [菜单] 正常状态 + .el-menu { + .el-menu-item { + color: $theme-aside-item-color; + background: $theme-aside-item-background-color; + i { + color: $theme-aside-item-color; + } + &:hover { + color: $theme-aside-item-color-hover; + background: $theme-aside-item-background-color-hover; + i { + color: $theme-aside-item-color-hover; + } + } + &:focus { + color: $theme-aside-item-color-focus; + background: $theme-aside-item-background-color-focus; + i { + color: $theme-aside-item-color-focus; + } + } + &.is-active { + color: $theme-aside-item-color-active; + background: $theme-aside-item-background-color-active; + i { + color: $theme-aside-item-color-active; + } + } + } + } + .el-submenu { + .el-submenu__title { + color: $theme-aside-item-color; + background: $theme-aside-item-background-color; + i { + color: $theme-aside-item-color; + } + .el-submenu__icon-arrow { + color: $theme-aside-item-color; + } + &:hover { + color: $theme-aside-item-color-hover; + background: $theme-aside-item-background-color-hover; + i { + color: $theme-aside-item-color-hover; + } + .el-submenu__icon-arrow { + color: $theme-aside-item-color-hover; + } + } + } + } + } + .d2-theme-container-main { + // 主体部分分为多页面控制器 和主体 + .d2-theme-container-main-header { + // 多页面控制器 + .d2-multiple-page-control { + .el-tabs__header.is-top { + border-bottom-color: $theme-multiple-page-control-border-color; + } + .el-tabs__nav { + border-color: $theme-multiple-page-control-border-color; + .el-tabs__item { + @extend %unable-select; + color: $theme-multiple-page-control-color; + background-color: $theme-multiple-page-control-background-color; + border-left-color: $theme-multiple-page-control-border-color; + &:first-child { + border-left: none; + &:hover { + padding: 0px 20px; + } + .el-icon-close { + display: none; + } + } + } + .el-tabs__item.is-active { + color: $theme-multiple-page-control-color-active; + background-color: $theme-multiple-page-control-background-color-active; + border-bottom-color: $theme-multiple-page-control-border-color-active; + } + } + %el-tabs__nav { + font-size: 20px; + } + .el-tabs__nav-prev { + @extend %el-tabs__nav; + color: $theme-multiple-page-control-nav-prev-color; + } + .el-tabs__nav-next { + @extend %el-tabs__nav; + color: $theme-multiple-page-control-nav-next-color; + } + } + // 多页控制器的关闭控制 + .d2-multiple-page-control-btn { + .el-dropdown { + .el-button-group { + .el-button { + border-color: $theme-multiple-page-control-border-color; + } + } + } + } + } + // 主体 + .d2-theme-container-main-body { + // 布局组件 + .container-component { + // [组件] + // d2-container-full 填充型 + .d2-container-full { + border: $theme-container-border-outer; + border-top: none; + border-bottom: none; + .d2-container-full__header { + border-bottom: $theme-container-border-inner; + background: $theme-container-header-footer-background-color; + } + .d2-container-full__body { + background: $theme-container-background-color; + } + .d2-container-full__footer { + border-top: $theme-container-border-inner; + background: $theme-container-header-footer-background-color; + } + } + // [组件] + // d2-container-full-bs 填充型 滚动优化 + .d2-container-full-bs { + border: $theme-container-border-outer; + border-top: none; + border-bottom: none; + .d2-container-full-bs__header { + border-bottom: $theme-container-border-inner; + background: $theme-container-header-footer-background-color; + } + .d2-container-full-bs__body { + background: $theme-container-background-color; + } + .d2-container-full-bs__footer { + border-top: $theme-container-border-inner; + background: $theme-container-header-footer-background-color; + } + } + // [组件] + // d2-container-ghost 隐形布局组件 + .d2-container-ghost { + .d2-container-ghost__header { + border-bottom: $theme-container-border-outer; + border-left: $theme-container-border-outer; + border-right: $theme-container-border-outer; + background: $theme-container-header-footer-background-color; + } + .d2-container-ghost__footer { + border-top: $theme-container-border-outer; + border-left: $theme-container-border-outer; + border-right: $theme-container-border-outer; + background: $theme-container-header-footer-background-color; + } + } + // [组件] + // d2-container-ghost-bs 隐形布局组件 滚动优化 + .d2-container-ghost-bs { + .d2-container-ghost-bs__header { + border-bottom: $theme-container-border-outer; + border-left: $theme-container-border-outer; + border-right: $theme-container-border-outer; + background: $theme-container-header-footer-background-color; + } + .d2-container-ghost-bs__footer { + border-top: $theme-container-border-outer; + border-left: $theme-container-border-outer; + border-right: $theme-container-border-outer; + background: $theme-container-header-footer-background-color; + } + } + // [组件] + // d2-container-card 卡片型 + .d2-container-card { + .d2-container-card__header { + border-bottom: $theme-container-border-inner; + border-left: $theme-container-border-outer; + border-right: $theme-container-border-outer; + background: $theme-container-header-footer-background-color; + } + .d2-container-card__body { + .d2-container-card__body-card { + background: $theme-container-background-color; + border-left: $theme-container-border-outer; + border-right: $theme-container-border-outer; + border-bottom: $theme-container-border-outer; + } + } + .d2-container-card__footer { + border-top: $theme-container-border-outer; + border-left: $theme-container-border-outer; + border-right: $theme-container-border-outer; + background: $theme-container-header-footer-background-color; + } + } + // [组件] + // d2-container-card-bs 卡片型 滚动优化 + .d2-container-card-bs { + .d2-container-card-bs__header { + border-bottom: $theme-container-border-inner; + border-left: $theme-container-border-outer; + border-right: $theme-container-border-outer; + background: $theme-container-header-footer-background-color; + } + .d2-container-card-bs__body { + .d2-container-card-bs__body-card { + background: $theme-container-background-color; + border-left: $theme-container-border-outer; + border-right: $theme-container-border-outer; + border-bottom: $theme-container-border-outer; + } + } + .d2-container-card-bs__footer { + border-top: $theme-container-border-outer; + border-left: $theme-container-border-outer; + border-right: $theme-container-border-outer; + background: $theme-container-header-footer-background-color; + } + } + } + } + } + } +} diff --git a/src/assets/style/theme/tomorrow-night-blue/index.scss b/src/assets/style/theme/tomorrow-night-blue/index.scss new file mode 100644 index 0000000..7b69d57 --- /dev/null +++ b/src/assets/style/theme/tomorrow-night-blue/index.scss @@ -0,0 +1,2 @@ +@import './setting.scss'; +@import '../theme.scss'; diff --git a/src/assets/style/theme/tomorrow-night-blue/setting.scss b/src/assets/style/theme/tomorrow-night-blue/setting.scss new file mode 100644 index 0000000..f80e797 --- /dev/null +++ b/src/assets/style/theme/tomorrow-night-blue/setting.scss @@ -0,0 +1,59 @@ +// 主题名称 +$theme-name: 'tomorrow-night-blue'; +// 主题背景颜色 +$theme-bg-color: #002253; +// 主题背景图片遮罩 +$theme-bg-mask: rgba(#000, 0); + +// container组件 +$theme-container-background-color: rgba(#FFF, 1); +$theme-container-header-footer-background-color: #FFF; +$theme-container-border-inner: 1px solid $color-border-1; +$theme-container-border-outer: 1px solid #002253; + +$theme-multiple-page-control-color: #FFF; +$theme-multiple-page-control-color-active: $color-text-normal; +$theme-multiple-page-control-nav-prev-color: #FFF; +$theme-multiple-page-control-nav-next-color: #FFF; +$theme-multiple-page-control-border-color: #002253; +$theme-multiple-page-control-border-color-active: #FFF; +$theme-multiple-page-control-background-color: rgba(#FFF, .2); +$theme-multiple-page-control-background-color-active: #FFF; + +// 顶栏和侧边栏中展开的菜单 hover 状态下 +$theme-menu-item-color-hover: #293849; +$theme-menu-item-background-color-hover: #ecf5ff; + +// 顶栏上的文字颜色 +$theme-header-item-color: #FF929A; +$theme-header-item-background-color: transparent; +// 顶栏上的项目在 hover 时 +$theme-header-item-color-hover: #FFEBA4; +$theme-header-item-background-color-hover: rgba(#FFF, .05); +// 顶栏上的项目在 focus 时 +$theme-header-item-color-focus: #FFB870; +$theme-header-item-background-color-focus: rgba(#FFF, .05); +// 顶栏上的项目在 active 时 +$theme-header-item-color-active: #FFB870; +$theme-header-item-background-color-active: rgba(#FFF, .05); + +// 侧边栏上的文字颜色 +$theme-aside-item-color: #FF929A; +$theme-aside-item-background-color: transparent; +// 侧边栏上的项目在 hover 时 +$theme-aside-item-color-hover: #FFEBA4; +$theme-aside-item-background-color-hover: rgba(#FFF, .05); +// 侧边栏上的项目在 focus 时 +$theme-aside-item-color-focus: #FFB870; +$theme-aside-item-background-color-focus: rgba(#FFF, .05); +// 侧边栏上的项目在 active 时 +$theme-aside-item-color-active: #FFB870; +$theme-aside-item-background-color-active: rgba(#FFF, .05); + +// 侧边栏菜单为空的时候显示的元素 +$theme-aside-menu-empty-icon-color: #FFB870; +$theme-aside-menu-empty-text-color: #FFB870; +$theme-aside-menu-empty-background-color: rgba(#FFF, .1); +$theme-aside-menu-empty-icon-color-hover: #FFEBA4; +$theme-aside-menu-empty-text-color-hover: #FFEBA4; +$theme-aside-menu-empty-background-color-hover: rgba(#FFF, .2); \ No newline at end of file diff --git a/src/assets/style/theme/violet/index.scss b/src/assets/style/theme/violet/index.scss new file mode 100644 index 0000000..753f026 --- /dev/null +++ b/src/assets/style/theme/violet/index.scss @@ -0,0 +1,9 @@ +@import './setting.scss'; +@import '../theme.scss'; + +.theme-#{$theme-name} { + .d2-layout-header-aside-group { + background: #bc00e3; + background: linear-gradient(120deg, #bc00e3 0%, #4EFFFB 100%); + } +} \ No newline at end of file diff --git a/src/assets/style/theme/violet/setting.scss b/src/assets/style/theme/violet/setting.scss new file mode 100644 index 0000000..441b1b2 --- /dev/null +++ b/src/assets/style/theme/violet/setting.scss @@ -0,0 +1,59 @@ +// 主题名称 +$theme-name: 'violet'; +// 主题背景颜色 +$theme-bg-color: #000; +// 主题背景图片遮罩 +$theme-bg-mask: rgba(#000, 0); + +// container组件 +$theme-container-background-color: #FFF; +$theme-container-header-footer-background-color: #FFF; +$theme-container-border-inner: 1px solid $color-border-2; +$theme-container-border-outer: 1px solid #8C40E2; + +$theme-multiple-page-control-color: #FFF; +$theme-multiple-page-control-color-active: $color-text-normal; +$theme-multiple-page-control-nav-prev-color: #FFF; +$theme-multiple-page-control-nav-next-color: #FFF; +$theme-multiple-page-control-border-color: #8C40E2; +$theme-multiple-page-control-border-color-active: #FFF; +$theme-multiple-page-control-background-color: rgba(#FFF, .3); +$theme-multiple-page-control-background-color-active: #FFF; + +// 顶栏和侧边栏中展开的菜单 hover 状态下 +$theme-menu-item-color-hover: #293849; +$theme-menu-item-background-color-hover: #ecf5ff; + +// 顶栏上的文字颜色 +$theme-header-item-color: #FFF; +$theme-header-item-background-color: transparent; +// 顶栏上的项目在 hover 时 +$theme-header-item-color-hover: #FFF; +$theme-header-item-background-color-hover: linear-gradient(-180deg, rgba(255,255,255,0.18) 0%, rgba(255,255,255,0.12) 100%); +// 顶栏上的项目在 focus 时 +$theme-header-item-color-focus: #FFF; +$theme-header-item-background-color-focus: linear-gradient(-180deg, rgba(255,255,255,0.18) 0%, rgba(255,255,255,0.12) 100%); +// 顶栏上的项目在 active 时 +$theme-header-item-color-active: #FFF; +$theme-header-item-background-color-active: linear-gradient(-180deg, rgba(255,255,255,0.18) 0%, rgba(255,255,255,0.12) 100%); + +// 侧边栏上的文字颜色 +$theme-aside-item-color: #FFF; +$theme-aside-item-background-color: transparent; +// 侧边栏上的项目在 hover 时 +$theme-aside-item-color-hover: #FFF; +$theme-aside-item-background-color-hover: linear-gradient(90deg, rgba(255,255,255,0.28) 0%, rgba(255,255,255,0.00) 100%); +// 侧边栏上的项目在 focus 时 +$theme-aside-item-color-focus: #FFF; +$theme-aside-item-background-color-focus: linear-gradient(90deg, rgba(255,255,255,0.28) 0%, rgba(255,255,255,0.00) 100%); +// 侧边栏上的项目在 active 时 +$theme-aside-item-color-active: #FFF; +$theme-aside-item-background-color-active: linear-gradient(90deg, rgba(255,255,255,0.28) 0%, rgba(255,255,255,0.00) 100%); + +// 侧边栏菜单为空的时候显示的元素 +$theme-aside-menu-empty-icon-color: #FFF; +$theme-aside-menu-empty-text-color: #FFF; +$theme-aside-menu-empty-background-color: rgba(#000, .1); +$theme-aside-menu-empty-icon-color-hover: #FFF; +$theme-aside-menu-empty-text-color-hover: #FFF; +$theme-aside-menu-empty-background-color-hover: rgba(#000, .15); \ No newline at end of file diff --git a/src/assets/style/unit/color.scss b/src/assets/style/unit/color.scss new file mode 100644 index 0000000..6eb123a --- /dev/null +++ b/src/assets/style/unit/color.scss @@ -0,0 +1,23 @@ +// 主色 +$color-primary: #409EFF; + +// 辅助色 +$color-info: #909399; +$color-success: #67C23A; +$color-warning: #E6A23C; +$color-danger: #F56C6C; + +// 文字 +$color-text-main: #303133; +$color-text-normal: #606266; +$color-text-sub: #909399; +$color-text-placehoder: #C0C4CC; + +// 边框 +$color-border-1: #DCDFE6; +$color-border-2: #E4E7ED; +$color-border-3: #EBEEF5; +$color-border-4: #F2F6FC; + +// 背景 +$color-bg: #f8f8f9; \ No newline at end of file diff --git a/src/assets/svg-icons/icons/d2admin.svg b/src/assets/svg-icons/icons/d2admin.svg new file mode 100644 index 0000000..3bdef83 --- /dev/null +++ b/src/assets/svg-icons/icons/d2admin.svg @@ -0,0 +1,13 @@ + + + D2Admin + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/svg-icons/index.js b/src/assets/svg-icons/index.js new file mode 100644 index 0000000..718a68b --- /dev/null +++ b/src/assets/svg-icons/index.js @@ -0,0 +1,7 @@ +import Vue from 'vue' + +const requireAll = requireContext => requireContext.keys().map(requireContext) +const req = require.context('./icons', false, /\.svg$/) +const iconMap = requireAll(req) + +Vue.prototype.$IconSvg = iconMap.map(e => e.default.id.slice(3)) diff --git a/src/components/d2-container/components/d2-container-card-bs.vue b/src/components/d2-container/components/d2-container-card-bs.vue new file mode 100644 index 0000000..3ee538c --- /dev/null +++ b/src/components/d2-container/components/d2-container-card-bs.vue @@ -0,0 +1,27 @@ + + + diff --git a/src/components/d2-container/components/d2-container-card.vue b/src/components/d2-container/components/d2-container-card.vue new file mode 100644 index 0000000..8e7e563 --- /dev/null +++ b/src/components/d2-container/components/d2-container-card.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/d2-container/components/d2-container-full-bs.vue b/src/components/d2-container/components/d2-container-full-bs.vue new file mode 100644 index 0000000..dc344c8 --- /dev/null +++ b/src/components/d2-container/components/d2-container-full-bs.vue @@ -0,0 +1,25 @@ + + + diff --git a/src/components/d2-container/components/d2-container-full.vue b/src/components/d2-container/components/d2-container-full.vue new file mode 100644 index 0000000..e789a24 --- /dev/null +++ b/src/components/d2-container/components/d2-container-full.vue @@ -0,0 +1,19 @@ + + + diff --git a/src/components/d2-container/components/d2-container-ghost-bs.vue b/src/components/d2-container/components/d2-container-ghost-bs.vue new file mode 100644 index 0000000..93ca21c --- /dev/null +++ b/src/components/d2-container/components/d2-container-ghost-bs.vue @@ -0,0 +1,23 @@ + + + diff --git a/src/components/d2-container/components/d2-container-ghost.vue b/src/components/d2-container/components/d2-container-ghost.vue new file mode 100644 index 0000000..64b37bf --- /dev/null +++ b/src/components/d2-container/components/d2-container-ghost.vue @@ -0,0 +1,13 @@ + diff --git a/src/components/d2-container/components/mixins/bs.js b/src/components/d2-container/components/mixins/bs.js new file mode 100644 index 0000000..899bb39 --- /dev/null +++ b/src/components/d2-container/components/mixins/bs.js @@ -0,0 +1,37 @@ +import BScroll from 'better-scroll' +export default { + props: { + betterScrollOptions: { + type: Object, + required: false, + default: () => ({}) + } + }, + data () { + return { + BS: null + } + }, + mounted () { + this.scrollInit() + }, + beforeDestroy () { + this.scrollDestroy() + }, + methods: { + scrollInit () { + this.BS = new BScroll(this.$refs.wrapper, Object.assign({ + mouseWheel: true, + scrollbar: { + fade: true, + interactive: false + } + }, this.betterScrollOptions)) + }, + scrollDestroy () { + if (this.BS) { + this.BS.destroy() + } + } + } +} diff --git a/src/components/d2-container/index.vue b/src/components/d2-container/index.vue new file mode 100644 index 0000000..17dbbeb --- /dev/null +++ b/src/components/d2-container/index.vue @@ -0,0 +1,75 @@ + + + diff --git a/src/components/d2-icon-svg/index.vue b/src/components/d2-icon-svg/index.vue new file mode 100644 index 0000000..86fa8a8 --- /dev/null +++ b/src/components/d2-icon-svg/index.vue @@ -0,0 +1,22 @@ + + + diff --git a/src/components/d2-icon/font-awesome-4.7.0/css/font-awesome.min.css b/src/components/d2-icon/font-awesome-4.7.0/css/font-awesome.min.css new file mode 100644 index 0000000..4fe37fe --- /dev/null +++ b/src/components/d2-icon/font-awesome-4.7.0/css/font-awesome.min.css @@ -0,0 +1,4 @@ +/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.7.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} diff --git a/src/components/d2-icon/font-awesome-4.7.0/fonts/FontAwesome.otf b/src/components/d2-icon/font-awesome-4.7.0/fonts/FontAwesome.otf new file mode 100644 index 0000000..401ec0f Binary files /dev/null and b/src/components/d2-icon/font-awesome-4.7.0/fonts/FontAwesome.otf differ diff --git a/src/components/d2-icon/font-awesome-4.7.0/fonts/fontawesome-webfont.eot b/src/components/d2-icon/font-awesome-4.7.0/fonts/fontawesome-webfont.eot new file mode 100644 index 0000000..e9f60ca Binary files /dev/null and b/src/components/d2-icon/font-awesome-4.7.0/fonts/fontawesome-webfont.eot differ diff --git a/src/components/d2-icon/font-awesome-4.7.0/fonts/fontawesome-webfont.svg b/src/components/d2-icon/font-awesome-4.7.0/fonts/fontawesome-webfont.svg new file mode 100644 index 0000000..855c845 --- /dev/null +++ b/src/components/d2-icon/font-awesome-4.7.0/fonts/fontawesome-webfont.svg @@ -0,0 +1,2671 @@ + + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/d2-icon/font-awesome-4.7.0/fonts/fontawesome-webfont.ttf b/src/components/d2-icon/font-awesome-4.7.0/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000..35acda2 Binary files /dev/null and b/src/components/d2-icon/font-awesome-4.7.0/fonts/fontawesome-webfont.ttf differ diff --git a/src/components/d2-icon/font-awesome-4.7.0/fonts/fontawesome-webfont.woff b/src/components/d2-icon/font-awesome-4.7.0/fonts/fontawesome-webfont.woff new file mode 100644 index 0000000..400014a Binary files /dev/null and b/src/components/d2-icon/font-awesome-4.7.0/fonts/fontawesome-webfont.woff differ diff --git a/src/components/d2-icon/font-awesome-4.7.0/fonts/fontawesome-webfont.woff2 b/src/components/d2-icon/font-awesome-4.7.0/fonts/fontawesome-webfont.woff2 new file mode 100644 index 0000000..4d13fc6 Binary files /dev/null and b/src/components/d2-icon/font-awesome-4.7.0/fonts/fontawesome-webfont.woff2 differ diff --git a/src/components/d2-icon/index.vue b/src/components/d2-icon/index.vue new file mode 100644 index 0000000..7c1080c --- /dev/null +++ b/src/components/d2-icon/index.vue @@ -0,0 +1,17 @@ + + + diff --git a/src/components/d2-page-cover/image/darkblue@2x.png b/src/components/d2-page-cover/image/darkblue@2x.png new file mode 100644 index 0000000..efe1577 Binary files /dev/null and b/src/components/d2-page-cover/image/darkblue@2x.png differ diff --git a/src/components/d2-page-cover/image/gray@2x.png b/src/components/d2-page-cover/image/gray@2x.png new file mode 100644 index 0000000..d005dad Binary files /dev/null and b/src/components/d2-page-cover/image/gray@2x.png differ diff --git a/src/components/d2-page-cover/index.vue b/src/components/d2-page-cover/index.vue new file mode 100644 index 0000000..d7ca9df --- /dev/null +++ b/src/components/d2-page-cover/index.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/src/components/index.js b/src/components/index.js new file mode 100644 index 0000000..62f5998 --- /dev/null +++ b/src/components/index.js @@ -0,0 +1,10 @@ +import Vue from 'vue' + +import d2Container from './d2-container' + +// 注意 有些组件使用异步加载会有影响 + +Vue.component('d2-container', d2Container) +Vue.component('d2-page-cover', () => import('./d2-page-cover')) +Vue.component('d2-icon', () => import('./d2-icon')) +Vue.component('d2-icon-svg', () => import('./d2-icon-svg/index.vue')) diff --git a/src/i18n/index.js b/src/i18n/index.js new file mode 100644 index 0000000..d78b5c7 --- /dev/null +++ b/src/i18n/index.js @@ -0,0 +1,17 @@ +import Vue from 'vue' +import VueI18n from 'vue-i18n' + +import cn from './lang/cn' +import ja from './lang/ja' +import en from './lang/en' + +Vue.use(VueI18n) + +export default new VueI18n({ + locale: 'cn', + messages: { + cn, + ja, + en + } +}) diff --git a/src/i18n/lang/cn/index.js b/src/i18n/lang/cn/index.js new file mode 100644 index 0000000..c38fff1 --- /dev/null +++ b/src/i18n/lang/cn/index.js @@ -0,0 +1,7 @@ +export default { + pub: { + pageHeader: { + demo: '示例' + } + } +} diff --git a/src/i18n/lang/en/index.js b/src/i18n/lang/en/index.js new file mode 100644 index 0000000..17400a4 --- /dev/null +++ b/src/i18n/lang/en/index.js @@ -0,0 +1,7 @@ +export default { + pub: { + pageHeader: { + demo: 'Demo' + } + } +} diff --git a/src/i18n/lang/ja/index.js b/src/i18n/lang/ja/index.js new file mode 100644 index 0000000..e7e4daa --- /dev/null +++ b/src/i18n/lang/ja/index.js @@ -0,0 +1,7 @@ +export default { + pub: { + pageHeader: { + demo: 'サンプル' + } + } +} diff --git a/src/layout/header-aside/components/components/menu-item/index.vue b/src/layout/header-aside/components/components/menu-item/index.vue new file mode 100644 index 0000000..bad42e3 --- /dev/null +++ b/src/layout/header-aside/components/components/menu-item/index.vue @@ -0,0 +1,25 @@ + + + diff --git a/src/layout/header-aside/components/components/menu-sub/index.vue b/src/layout/header-aside/components/components/menu-sub/index.vue new file mode 100644 index 0000000..f75a6e7 --- /dev/null +++ b/src/layout/header-aside/components/components/menu-sub/index.vue @@ -0,0 +1,37 @@ + + + diff --git a/src/layout/header-aside/components/header-fullscreen/index.vue b/src/layout/header-aside/components/header-fullscreen/index.vue new file mode 100644 index 0000000..68f801b --- /dev/null +++ b/src/layout/header-aside/components/header-fullscreen/index.vue @@ -0,0 +1,27 @@ + + + diff --git a/src/layout/header-aside/components/header-help/image/qq.jpg b/src/layout/header-aside/components/header-help/image/qq.jpg new file mode 100644 index 0000000..b27de32 Binary files /dev/null and b/src/layout/header-aside/components/header-help/image/qq.jpg differ diff --git a/src/layout/header-aside/components/header-help/image/we.jpg b/src/layout/header-aside/components/header-help/image/we.jpg new file mode 100644 index 0000000..12bb778 Binary files /dev/null and b/src/layout/header-aside/components/header-help/image/we.jpg differ diff --git a/src/layout/header-aside/components/header-help/index.vue b/src/layout/header-aside/components/header-help/index.vue new file mode 100644 index 0000000..6aeaa63 --- /dev/null +++ b/src/layout/header-aside/components/header-help/index.vue @@ -0,0 +1,51 @@ + + + diff --git a/src/layout/header-aside/components/header-theme/components/d2-theme-list/index.vue b/src/layout/header-aside/components/header-theme/components/d2-theme-list/index.vue new file mode 100644 index 0000000..9038b29 --- /dev/null +++ b/src/layout/header-aside/components/header-theme/components/d2-theme-list/index.vue @@ -0,0 +1,58 @@ + + + + + diff --git a/src/layout/header-aside/components/header-theme/index.vue b/src/layout/header-aside/components/header-theme/index.vue new file mode 100644 index 0000000..d8c1d23 --- /dev/null +++ b/src/layout/header-aside/components/header-theme/index.vue @@ -0,0 +1,26 @@ + + + diff --git a/src/layout/header-aside/components/header-user/index.vue b/src/layout/header-aside/components/header-user/index.vue new file mode 100644 index 0000000..7ed2401 --- /dev/null +++ b/src/layout/header-aside/components/header-user/index.vue @@ -0,0 +1,36 @@ + + + diff --git a/src/layout/header-aside/components/menu-header/index.vue b/src/layout/header-aside/components/menu-header/index.vue new file mode 100644 index 0000000..a52d4e5 --- /dev/null +++ b/src/layout/header-aside/components/menu-header/index.vue @@ -0,0 +1,30 @@ + + + diff --git a/src/layout/header-aside/components/menu-side/index.vue b/src/layout/header-aside/components/menu-side/index.vue new file mode 100644 index 0000000..44bae49 --- /dev/null +++ b/src/layout/header-aside/components/menu-side/index.vue @@ -0,0 +1,94 @@ + + + diff --git a/src/layout/header-aside/components/mixin/menu.js b/src/layout/header-aside/components/mixin/menu.js new file mode 100644 index 0000000..efd488b --- /dev/null +++ b/src/layout/header-aside/components/mixin/menu.js @@ -0,0 +1,13 @@ +export default { + methods: { + handleMenuSelect (index, indexPath) { + if (/^d2-menu-empty-\d+$/.test(index)) { + this.$message('功能正在开发') + } else { + this.$router.push({ + path: index + }) + } + } + } +} diff --git a/src/layout/header-aside/components/tabs/index.vue b/src/layout/header-aside/components/tabs/index.vue new file mode 100644 index 0000000..6d4b84a --- /dev/null +++ b/src/layout/header-aside/components/tabs/index.vue @@ -0,0 +1,118 @@ + + + diff --git a/src/layout/header-aside/index.js b/src/layout/header-aside/index.js new file mode 100644 index 0000000..2977bf9 --- /dev/null +++ b/src/layout/header-aside/index.js @@ -0,0 +1,3 @@ +import layout from './layout' + +export default layout diff --git a/src/layout/header-aside/layout.vue b/src/layout/header-aside/layout.vue new file mode 100644 index 0000000..6ce51d7 --- /dev/null +++ b/src/layout/header-aside/layout.vue @@ -0,0 +1,121 @@ + + + + + diff --git a/src/libs/db.js b/src/libs/db.js new file mode 100644 index 0000000..fb1c92e --- /dev/null +++ b/src/libs/db.js @@ -0,0 +1,18 @@ +import low from 'lowdb' +import LocalStorage from 'lowdb/adapters/LocalStorage' +import { version } from '../../package' + +const adapter = new LocalStorage(`d2admin-${version}`) +const db = low(adapter) + +// 初始化数据库 +db.defaults({ + themeActiveName: [], + pageOpenedList: [], + userInfo: [], + isMenuAsideCollapse: [], + database: [], + databasePublic: {} +}).write() + +export default db diff --git a/src/libs/util.js b/src/libs/util.js new file mode 100644 index 0000000..8c7ba6a --- /dev/null +++ b/src/libs/util.js @@ -0,0 +1,120 @@ +import Cookies from 'js-cookie' +import axios from 'axios' +import semver from 'semver' +import UaParser from 'ua-parser-js' +import { version } from '../../package.json' + +let util = { + cookies: {} +} + +/** + * @description 存储 cookie 值 + * @param {String} name cookie name + * @param {String} value cookie value + * @param {Object} setting cookie setting + */ +util.cookies.set = function (name = 'default', value = '', setting = {}) { + let cookieSetting = { + expires: 1 + } + Object.assign(cookieSetting, setting) + Cookies.set(`d2admin-${version}-${name}`, value, cookieSetting) +} + +/** + * @description 拿到 cookie 值 + * @param {String} name cookie name + */ +util.cookies.get = function (name = 'default') { + return Cookies.get(`d2admin-${version}-${name}`) +} + +/** + * @description 拿到 cookie 全部的值 + */ +util.cookies.getAll = function () { + return Cookies.get() +} + +/** + * @description 删除 cookie + * @param {String} name cookie name + */ +util.cookies.remove = function (name = 'default') { + return Cookies.remove(`d2admin-${version}-${name}`) +} + +/** + * @description 更新标题 + * @param {String} title 标题 + */ +util.title = function (titleText) { + window.document.title = `${process.env.VUE_APP_TITLE}${titleText ? ` | ${titleText}` : ''}` +} + +/** + * @description 获取所有的 UA 信息 + */ +util.ua = function () { + return new UaParser().getResult() +} + +/** + * @description 判断是否在其内 + * @param {*} ele element + * @param {Array} targetArr array + */ +util.isOneOf = function (ele, targetArr) { + if (targetArr.indexOf(ele) >= 0) { + return true + } else { + return false + } +} + +/** + * @description 打印一个 “胶囊” 样式的信息 + * @param {String} title title text + * @param {String} info info text + */ +util.logCapsule = function (title, info) { + console.log( + `%c ${title} %c ${info} %c`, + 'background:#29384b; padding: 1px; border-radius: 3px 0 0 3px; color: #fff', + 'background:#3488ff; padding: 1px; border-radius: 0 3px 3px 0; color: #fff', + 'background:transparent' + ) +} + +/** + * @description 检查版本更新 + * @param {Object} vm vue + */ +util.checkUpdate = function (vm) { + axios.get('https://api.github.com/repos/FairyEver/d2-admin/releases/latest') + .then(res => { + let versionGet = res.tag_name + const update = semver.lt(version, versionGet) + if (update) { + util.logCapsule('D2Admin', `New version ${res.name}`) + console.log(`版本号: ${res.tag_name} | 详情${res.html_url}`) + vm.$store.commit('d2adminReleasesUpdateSet', true) + } + vm.$store.commit('d2adminReleasesLatestSet', res) + }) + .catch(err => { + console.log('checkUpdate error', err) + }) +} + +/** + * @description 显示版本信息 + */ +util.showInfo = function showInfo () { + util.logCapsule('D2Admin', `v${version}`) + console.log('Github https://github.com/d2-projects/d2-admin') + console.log('Doc http://d2admin.fairyever.com/zh/') +} + +export default util diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..1b339fa --- /dev/null +++ b/src/main.js @@ -0,0 +1,85 @@ +import 'babel-polyfill' +import Vue from 'vue' +import App from './App' +import ElementUI from 'element-ui' +import 'element-ui/lib/theme-chalk/index.css' +import screenfull from 'screenfull' +import i18n from './i18n' +import '@/assets/svg-icons' +import '@/components' +import '@/plugin/axios' +import '@/mock/register' +import util from '@/libs/util.js' +import store from '@/store/index' +import menuHeader from '@/menu/header' +import menuAside from '@/menu/aside' +import pluginOpen from '@/plugin/open' +import { frameInRoutes } from '@/router/routes' +import router from './router' + +Vue.use(ElementUI) +Vue.use(pluginOpen) + +Vue.config.productionTip = false + +Vue.prototype.$env = process.env.NODE_ENV +Vue.prototype.$baseUrl = process.env.BASE_URL + +new Vue({ + router, + store, + i18n, + render: h => h(App), + created () { + // 处理路由 得到每一级的路由设置 + this.getAllPageFromRoutes() + // 设置顶栏菜单 + this.$store.commit('d2adminMenuHeaderSet', menuHeader) + // 设置侧边栏菜单 + this.$store.commit('d2adminMenuAsideSet', menuAside) + }, + mounted () { + // D2Admin 开发环境检查更新 + util.checkUpdate(this) + // 获取并记录用户 UA + this.$store.commit('d2adminUaGet') + // 展示系统信息 + util.showInfo() + // 用户登陆后从数据库加载一系列的设置 + this.$store.commit('d2adminLoginSuccessLoad') + // 初始化全屏监听 + this.fullscreenListenerInit() + }, + methods: { + /** + * 初始化全屏监听 + */ + fullscreenListenerInit () { + if (screenfull.enabled) { + screenfull.on('change', () => { + if (!screenfull.isFullscreen) { + this.$store.commit('d2adminFullScreenSet', false) + } + }) + } + }, + /** + * 处理路由 得到所有的页面 + */ + getAllPageFromRoutes () { + const pool = [] + const push = function (routes) { + routes.forEach(route => { + if (route.children) { + push(route.children) + } else { + const { meta, name, path } = route + pool.push({ meta, name, path }) + } + }) + } + push(frameInRoutes) + this.$store.commit('d2adminPagePoolSet', pool) + } + } +}).$mount('#app') diff --git a/src/menu/aside.js b/src/menu/aside.js new file mode 100644 index 0000000..59a0db1 --- /dev/null +++ b/src/menu/aside.js @@ -0,0 +1,8 @@ +// 菜单 侧边栏 +export default [ + { + path: '/index', + title: '首页', + icon: 'home' + } +] diff --git a/src/menu/header.js b/src/menu/header.js new file mode 100644 index 0000000..3d628f0 --- /dev/null +++ b/src/menu/header.js @@ -0,0 +1,8 @@ +// 菜单 顶栏 +export default [ + { + path: '/index', + title: '首页', + icon: 'home' + } +] diff --git a/src/mock/login/index.js b/src/mock/login/index.js new file mode 100644 index 0000000..9922298 --- /dev/null +++ b/src/mock/login/index.js @@ -0,0 +1,42 @@ +import Mock from 'mockjs' + +const userDB = [ + { + username: 'admin', + password: 'admin', + uuid: 'admin-uuid', + name: '管理员' + }, + { + username: 'editor', + password: 'editor', + uuid: 'editor-uuid', + name: '编辑' + }, + { + username: 'user1', + password: 'user1', + uuid: 'user1-uuid', + name: '用户1' + } +] + +Mock.mock('/login', 'post', ({url, type, body}) => { + const bodyObj = JSON.parse(body) + const user = userDB.find(e => e.username === bodyObj.username && e.password === bodyObj.password) + if (user) { + return { + code: 0, + msg: '登陆成功', + data: { + ...user, + token: 'd787syv8dys8cas80d9s0a0d8f79ads56f7s4d56f879a8as89fd980s7dg' + } + } + } else { + return { + code: 401, + msg: '用户名或密码错误' + } + } +}) diff --git a/src/mock/register.js b/src/mock/register.js new file mode 100644 index 0000000..368c4a8 --- /dev/null +++ b/src/mock/register.js @@ -0,0 +1,8 @@ +import Mock from 'mockjs' + +import '@/mock/login' + +// 设置全局延时 没有延时的话有时候会检测不到数据变化 建议保留 +Mock.setup({ + timeout: '300-600' +}) diff --git a/src/pages/error-page-404/index.js b/src/pages/error-page-404/index.js new file mode 100644 index 0000000..ddb7de8 --- /dev/null +++ b/src/pages/error-page-404/index.js @@ -0,0 +1,3 @@ +import page from './page' + +export default page diff --git a/src/pages/error-page-404/page.vue b/src/pages/error-page-404/page.vue new file mode 100644 index 0000000..a1c7843 --- /dev/null +++ b/src/pages/error-page-404/page.vue @@ -0,0 +1,25 @@ + + + diff --git a/src/pages/index/index.js b/src/pages/index/index.js new file mode 100644 index 0000000..ddb7de8 --- /dev/null +++ b/src/pages/index/index.js @@ -0,0 +1,3 @@ +import page from './page' + +export default page diff --git a/src/pages/index/page.vue b/src/pages/index/page.vue new file mode 100644 index 0000000..15ae8f3 --- /dev/null +++ b/src/pages/index/page.vue @@ -0,0 +1,28 @@ + + + diff --git a/src/pages/login/config/README.md b/src/pages/login/config/README.md new file mode 100644 index 0000000..87be229 --- /dev/null +++ b/src/pages/login/config/README.md @@ -0,0 +1,2 @@ +// 例子插件配置地址 +// https://vincentgarreau.com/particles.js/#default \ No newline at end of file diff --git a/src/pages/login/config/bubble.js b/src/pages/login/config/bubble.js new file mode 100644 index 0000000..7fd19c0 --- /dev/null +++ b/src/pages/login/config/bubble.js @@ -0,0 +1,112 @@ +/* eslint-disable */ + +export default { + "particles": { + "number": { + "value": 6, + "density": { + "enable": true, + "value_area": 800 + } + }, + "color": { + "value": "#444" + }, + "shape": { + "type": "polygon", + "stroke": { + "width": 0, + "color": "#000" + }, + "polygon": { + "nb_sides": 6 + }, + "image": { + "src": "img/github.svg", + "width": 100, + "height": 100 + } + }, + "opacity": { + "value": 0.3, + "random": true, + "anim": { + "enable": false, + "speed": 1, + "opacity_min": 0.1, + "sync": false + } + }, + "size": { + "value": 160, + "random": false, + "anim": { + "enable": true, + "speed": 10, + "size_min": 40, + "sync": false + } + }, + "line_linked": { + "enable": false, + "distance": 200, + "color": "#ffffff", + "opacity": 1, + "width": 2 + }, + "move": { + "enable": true, + "speed": 8, + "direction": "none", + "random": false, + "straight": false, + "out_mode": "out", + "bounce": false, + "attract": { + "enable": false, + "rotateX": 600, + "rotateY": 1200 + } + } + }, + "interactivity": { + "detect_on": "canvas", + "events": { + "onhover": { + "enable": false, + "mode": "grab" + }, + "onclick": { + "enable": false, + "mode": "push" + }, + "resize": true + }, + "modes": { + "grab": { + "distance": 400, + "line_linked": { + "opacity": 1 + } + }, + "bubble": { + "distance": 400, + "size": 40, + "duration": 2, + "opacity": 8, + "speed": 3 + }, + "repulse": { + "distance": 200, + "duration": 0.4 + }, + "push": { + "particles_nb": 4 + }, + "remove": { + "particles_nb": 2 + } + } + }, + "retina_detect": true +} diff --git a/src/pages/login/config/default.js b/src/pages/login/config/default.js new file mode 100644 index 0000000..b199917 --- /dev/null +++ b/src/pages/login/config/default.js @@ -0,0 +1,112 @@ +/* eslint-disable */ + +export default { + "particles": { + "number": { + "value": 80, + "density": { + "enable": true, + "value_area": 800 + } + }, + "color": { + "value": "#DCDFE6" + }, + "shape": { + "type": "circle", + "stroke": { + "width": 0, + "color": "#000000" + }, + "polygon": { + "nb_sides": 5 + }, + "image": { + "src": "img/github.svg", + "width": 100, + "height": 100 + } + }, + "opacity": { + "value": 1, + "random": false, + "anim": { + "enable": false, + "speed": 1, + "opacity_min": 0.1, + "sync": false + } + }, + "size": { + "value": 4, + "random": true, + "anim": { + "enable": false, + "speed": 40, + "size_min": 0.1, + "sync": false + } + }, + "line_linked": { + "enable": true, + "distance": 150, + "color": "#DCDFE6", + "opacity": 0.4, + "width": 2 + }, + "move": { + "enable": true, + "speed": 6, + "direction": "none", + "random": false, + "straight": false, + "out_mode": "out", + "bounce": false, + "attract": { + "enable": false, + "rotateX": 600, + "rotateY": 1200 + } + } + }, + "interactivity": { + "detect_on": "canvas", + "events": { + "onhover": { + "enable": true, + "mode": "repulse" + }, + "onclick": { + "enable": true, + "mode": "push" + }, + "resize": true + }, + "modes": { + "grab": { + "distance": 400, + "line_linked": { + "opacity": 1 + } + }, + "bubble": { + "distance": 400, + "size": 40, + "duration": 2, + "opacity": 8, + "speed": 3 + }, + "repulse": { + "distance": 200, + "duration": 0.4 + }, + "push": { + "particles_nb": 4 + }, + "remove": { + "particles_nb": 2 + } + } + }, + "retina_detect": true +} diff --git a/src/pages/login/config/nasa.js b/src/pages/login/config/nasa.js new file mode 100644 index 0000000..4baf4fe --- /dev/null +++ b/src/pages/login/config/nasa.js @@ -0,0 +1,112 @@ +/* eslint-disable */ + +export default { + "particles": { + "number": { + "value": 160, + "density": { + "enable": true, + "value_area": 800 + } + }, + "color": { + "value": "#ffffff" + }, + "shape": { + "type": "circle", + "stroke": { + "width": 0, + "color": "#000000" + }, + "polygon": { + "nb_sides": 5 + }, + "image": { + "src": "img/github.svg", + "width": 100, + "height": 100 + } + }, + "opacity": { + "value": 1, + "random": true, + "anim": { + "enable": true, + "speed": 1, + "opacity_min": 0, + "sync": false + } + }, + "size": { + "value": 3, + "random": true, + "anim": { + "enable": false, + "speed": 4, + "size_min": 0.3, + "sync": false + } + }, + "line_linked": { + "enable": false, + "distance": 150, + "color": "#ffffff", + "opacity": 0.4, + "width": 1 + }, + "move": { + "enable": true, + "speed": 1, + "direction": "none", + "random": true, + "straight": false, + "out_mode": "out", + "bounce": false, + "attract": { + "enable": false, + "rotateX": 600, + "rotateY": 600 + } + } + }, + "interactivity": { + "detect_on": "canvas", + "events": { + "onhover": { + "enable": true, + "mode": "bubble" + }, + "onclick": { + "enable": true, + "mode": "repulse" + }, + "resize": true + }, + "modes": { + "grab": { + "distance": 400, + "line_linked": { + "opacity": 1 + } + }, + "bubble": { + "distance": 250, + "size": 0, + "duration": 2, + "opacity": 0, + "speed": 3 + }, + "repulse": { + "distance": 400, + "duration": 0.4 + }, + "push": { + "particles_nb": 4 + }, + "remove": { + "particles_nb": 2 + } + } + }, + "retina_detect": true +} diff --git a/src/pages/login/config/snow.js b/src/pages/login/config/snow.js new file mode 100644 index 0000000..31948b4 --- /dev/null +++ b/src/pages/login/config/snow.js @@ -0,0 +1,112 @@ +/* eslint-disable */ + +export default { + "particles": { + "number": { + "value": 400, + "density": { + "enable": true, + "value_area": 800 + } + }, + "color": { + "value": "#fff" + }, + "shape": { + "type": "circle", + "stroke": { + "width": 0, + "color": "#000000" + }, + "polygon": { + "nb_sides": 5 + }, + "image": { + "src": "img/github.svg", + "width": 100, + "height": 100 + } + }, + "opacity": { + "value": 0.5, + "random": true, + "anim": { + "enable": false, + "speed": 1, + "opacity_min": 0.1, + "sync": false + } + }, + "size": { + "value": 10, + "random": true, + "anim": { + "enable": false, + "speed": 40, + "size_min": 0.1, + "sync": false + } + }, + "line_linked": { + "enable": false, + "distance": 500, + "color": "#ffffff", + "opacity": 0.4, + "width": 2 + }, + "move": { + "enable": true, + "speed": 6, + "direction": "bottom", + "random": false, + "straight": false, + "out_mode": "out", + "bounce": false, + "attract": { + "enable": false, + "rotateX": 600, + "rotateY": 1200 + } + } + }, + "interactivity": { + "detect_on": "canvas", + "events": { + "onhover": { + "enable": true, + "mode": "bubble" + }, + "onclick": { + "enable": true, + "mode": "repulse" + }, + "resize": true + }, + "modes": { + "grab": { + "distance": 400, + "line_linked": { + "opacity": 0.5 + } + }, + "bubble": { + "distance": 400, + "size": 4, + "duration": 0.3, + "opacity": 1, + "speed": 3 + }, + "repulse": { + "distance": 200, + "duration": 0.4 + }, + "push": { + "particles_nb": 4 + }, + "remove": { + "particles_nb": 2 + } + } + }, + "retina_detect": true +} diff --git a/src/pages/login/image/login-code.png b/src/pages/login/image/login-code.png new file mode 100644 index 0000000..14bf592 Binary files /dev/null and b/src/pages/login/image/login-code.png differ diff --git a/src/pages/login/image/logo.png b/src/pages/login/image/logo.png new file mode 100644 index 0000000..f29a9f7 Binary files /dev/null and b/src/pages/login/image/logo.png differ diff --git a/src/pages/login/index.js b/src/pages/login/index.js new file mode 100644 index 0000000..ddb7de8 --- /dev/null +++ b/src/pages/login/index.js @@ -0,0 +1,3 @@ +import page from './page' + +export default page diff --git a/src/pages/login/page.vue b/src/pages/login/page.vue new file mode 100644 index 0000000..3c9c3d7 --- /dev/null +++ b/src/pages/login/page.vue @@ -0,0 +1,147 @@ + + + + + diff --git a/src/pages/login/style.scss b/src/pages/login/style.scss new file mode 100644 index 0000000..42cf658 --- /dev/null +++ b/src/pages/login/style.scss @@ -0,0 +1,90 @@ +@import '~@/assets/style/public.scss'; +.login-page { + background-color: #EDF4FA; + height: 100%; + position: relative; + // 层 + .layer { + position: absolute; + height: 100%; + width: 100%; + &.flex-center { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + } + } + // 背景 + .bg { + canvas { + display: block; + margin: 0px; + padding: 0px; + } + } + // logo + .logo-group { + margin-top: -75px - 70px; + position: relative; + top: 75px; + img { + height: 140px; + } + } + // 登陆表单 + .form-group { + width: 300px; + // 重新设置卡片阴影 + .el-card { + box-shadow: 0 0 8px 0 rgba(232,237,250,.6), 0 2px 4px 0 rgba(232,237,250,.5); + .el-card__body { + padding-top: 70px; + } + } + // 登陆按钮 + .button-login { + width: 100%; + } + // 输入框左边的图表区域缩窄 + .el-input-group__prepend { + padding: 0px 14px; + } + .login-code { + height: 40px - 2px; + display: block; + margin: 0px -20px; + border-top-right-radius: 2px; + border-bottom-right-radius: 2px; + } + } + // 帮助按钮 + .button-help { + width: 300px; + margin-top: 20px; + } + .user-btn { + @extend %flex-center-col; + @extend %unable-select; + padding: 10px 0px; + border-radius: 4px; + &:hover { + background-color: $color-bg; + i { + color: $color-text-normal; + } + span { + color: $color-text-normal; + } + } + i { + font-size: 36px; + color: $color-text-sub; + } + span { + font-size: 12px; + margin-top: 10px; + color: $color-text-sub; + } + } +} \ No newline at end of file diff --git a/src/plugin/axios/index.js b/src/plugin/axios/index.js new file mode 100644 index 0000000..d2e137d --- /dev/null +++ b/src/plugin/axios/index.js @@ -0,0 +1,10 @@ +import Vue from 'vue' +import axios from 'axios' + +axios.interceptors.response.use(res => { + return res.data +}, err => { + return Promise.reject(err) +}) + +Vue.prototype.$axios = axios diff --git a/src/plugin/open/index.js b/src/plugin/open/index.js new file mode 100644 index 0000000..c5b6a96 --- /dev/null +++ b/src/plugin/open/index.js @@ -0,0 +1,8 @@ +export default { + install (Vue, options) { + // 打开一个url + Vue.prototype.$open = (url = 'https://github.com/d2-projects') => { + window.open(url) + } + } +} diff --git a/src/router/index.js b/src/router/index.js new file mode 100755 index 0000000..72b9324 --- /dev/null +++ b/src/router/index.js @@ -0,0 +1,47 @@ +import Vue from 'vue' +import VueRouter from 'vue-router' + +import util from '@/libs/util.js' + +// 路由数据 +import routes from './routes' + +Vue.use(VueRouter) + +let router = new VueRouter({ routes }) + +/** + * 路由拦截 + * 权限验证 + */ +router.beforeEach((to, from, next) => { + // 验证当前路由所有的匹配中是否需要有登陆验证的 + if (to.matched.some(r => r.meta.requiresAuth)) { + // 这里暂时将cookie里是否存有token作为验证是否登陆的条件 + // 请根据自身业务需要修改 + const token = util.cookies.get('token') + if (token && token !== 'undefined') { + next() + } else { + // 没有登陆的时候跳转到登陆界面 + next({ + name: 'login' + }) + } + } else { + // 不需要身份校验 直接通过 + next() + } +}) + +router.afterEach(to => { + // 需要的信息 + const app = router.app + const { name, params, query } = to + // 多页控制 打开新的页面 + app.$store.commit('d2adminPageOpenNew', { name, params, query }) + // 更改标题 + util.title(to.meta.title) +}) + +export default router diff --git a/src/router/routes.js b/src/router/routes.js new file mode 100644 index 0000000..97116b2 --- /dev/null +++ b/src/router/routes.js @@ -0,0 +1,58 @@ +// layout +import layoutHeaderAside from '@/layout/header-aside' + +const meta = { requiresAuth: true } + +/** + * 在主框架内显示 + */ +const frameIn = [ + // 首页 + { + path: '/', + redirect: { name: 'index' }, + component: layoutHeaderAside, + children: [ + { + path: 'index', + name: 'index', + meta, + component: () => import('@/pages/index') + } + ] + } +] + +/** + * 在主框架之外显示 + */ +const frameOut = [ + // 登陆 + { + path: '/login', + name: 'login', + component: () => import('@/pages/login') + } +] + +/** + * 错误页面 + */ +const errorPage = [ + // 404 + { + path: '*', + name: '404', + component: () => import('@/pages/error-page-404') + } +] + +// 导出需要显示菜单的 +export const frameInRoutes = frameIn + +// 重新组织后导出 +export default [ + ...frameIn, + ...frameOut, + ...errorPage +] diff --git a/src/store/index.js b/src/store/index.js new file mode 100644 index 0000000..66c6f3f --- /dev/null +++ b/src/store/index.js @@ -0,0 +1,12 @@ +import Vue from 'vue' +import Vuex from 'vuex' + +import d2admin from './modules/d2admin' + +Vue.use(Vuex) + +export default new Vuex.Store({ + modules: { + d2admin + } +}) diff --git a/src/store/modules/d2admin.js b/src/store/modules/d2admin.js new file mode 100644 index 0000000..2b2ad59 --- /dev/null +++ b/src/store/modules/d2admin.js @@ -0,0 +1,660 @@ +import screenfull from 'screenfull' +import util from '@/libs/util.js' +import db from '@/libs/db.js' +import themeList from '@/assets/style/theme/list.js' +import { version } from '../../../package' + +const pageOpenedDefult = { + name: 'index', + meta: { + title: '首页', + requiresAuth: false + } +} + +export default { + state: { + // 用户信息 + userInfo: { + name: '' + }, + // D2Admin 版本 + version, + // 最新版本的信息 + releasesLatest: {}, + // 有新版本 + releasesUpdate: false, + // 顶栏菜单 + menuHeader: [], + // 侧栏菜单 + menuAside: [], + // 全屏 + isFullScreen: false, + // 灰度 + isGrayMode: false, + // 侧边栏收缩 + isMenuAsideCollapse: false, + // 主题 + themeList, + // 现在激活的主题 + themeActiveName: themeList[0].name, // 这应该是一个名字 不是对象 + // 可以在多页 tab 模式下显示的页面 + pagePool: [], + // 当前显示的多页面列表 + pageOpenedList: [ + pageOpenedDefult + ], + // 当前页面 + pageCurrent: '', + // 用户 UA + ua: {} + }, + getters: { + /** + * @description 返回当前的主题信息 不是一个名字 而是当前激活主题的所有数据 + * @param {vuex state} state vuex state + */ + d2adminThemeActiveSetting (state) { + return state.themeList.find(theme => theme.name === state.themeActiveName) + }, + /** + * @description 从当前所有打开的多标签页里返回需要缓存的页面 name + * @param {*} state vuex state + */ + d2adminKeepAliveInclude (state) { + return state.pageOpenedList.filter(item => { + if (item.meta) { + if (item.meta.notCache) { + return false + } + } + return true + }).map(e => e.name) + } + }, + actions: { + /** + * 登陆 + * @param {Object} param0 context + * @param {Object} param1 { vue, username, password } + */ + d2adminLogin ({ state, commit, rootState }, { vm, username, password }) { + // 开始请求登录接口 + vm.$axios({ + method: 'post', + url: '/login', + data: { + username, + password + } + }) + .then(res => { + // 设置 cookie 一定要存 uuid 和 token 两个 cookie + // 整个系统依赖这两个数据进行校验和存储 + // uuid 是用户身份唯一标识 用户注册的时候确定 并且不可改变 不可重复 + // token 代表用户当前登录状态 建议在网络请求中携带 token,如有必要 token 需要定时更新,默认保存一天 + util.cookies.set('uuid', res.data.uuid) + util.cookies.set('token', res.data.token) + // 设置 vuex 用户信息 + commit('d2adminUserInfoSet', { + name: res.data.name + }) + // 用户登陆后从数据库加载一系列的设置 + commit('d2adminLoginSuccessLoad') + // 跳转路由 + vm.$router.push({ + name: 'index' + }) + }) + .catch(err => { + console.group('登陆结果') + console.log('err: ', err) + console.groupEnd() + }) + }, + /** + * 注销用户并返回登陆页面 + * @param {Object} param0 context + * @param {Object} confirm need confirm ? + */ + d2adminLogout ({ state, commit, rootState }, { vm, confirm }) { + /** + * @description 注销 + */ + function logout () { + // 删除cookie + util.cookies.remove('token') + util.cookies.remove('uuid') + // 跳转路由 + vm.$router.push({ + name: 'login' + }) + } + // 判断是否需要确认 + if (confirm) { + commit('d2adminGrayModeSet', true) + vm.$confirm('注销当前账户吗? 打开的标签页和用户设置将会被保存。', '确认操作', { + confirmButtonText: '确定注销', + cancelButtonText: '放弃', + type: 'warning' + }) + .then(() => { + commit('d2adminGrayModeSet', false) + logout() + }) + .catch(() => { + commit('d2adminGrayModeSet', false) + vm.$message('放弃注销用户') + }) + } else { + logout() + } + } + }, + mutations: { + /** + * @class 通用工具 + * @description 将 state 中某一项存储到数据库 如果已经有的话就更新数据 需要 uuid + * @param {vuex state} state vuex state + * @param {String} key key name + */ + d2adminUtilVuex2DbByUuid (state, key) { + const row = db.get(key).find({uuid: util.cookies.get('uuid')}) + if (row.value()) { + row.assign({value: state[key]}).write() + } else { + db.get(key).push({ + uuid: util.cookies.get('uuid'), + value: state[key] + }).write() + } + }, + /** + * @class 通用工具 + * @description 从数据库取值到 vuex 需要 uuid + * @param {vuex state} state vuex state + * @param {Object} param1 key and default value + */ + d2adminUtilDb2VuexByUuid (state, { key, defaultValue }) { + const row = db.get(key).find({uuid: util.cookies.get('uuid')}).value() + state[key] = row ? row.value : defaultValue + }, + /** + * @class 通用工具 + * @description 将 state 中某一项存储到数据库 如果已经有的话就更新数据 不需要 uuid 所有用户共享 + * @param {vuex state} state vuex state + * @param {String} key key name + */ + d2adminUtilVuex2Db (state, key) { + const row = db.get(key).find({pub: 'pub'}) + if (row.value()) { + row.assign({value: state[key]}).write() + } else { + db.get(key).push({ + pub: 'pub', + value: state[key] + }).write() + } + }, + /** + * @class 通用工具 + * @description 从数据库取值到 vuex 不需要 uuid 所有用户共享 + * @param {vuex state} state vuex state + * @param {Object} param1 key and default value + */ + d2adminUtilDb2Vuex (state, { key, defaultValue }) { + const row = db.get(key).find({pub: 'pub'}).value() + state[key] = row ? row.value : defaultValue + }, + /** + * @class 通用工具 + * @description 访问本地数据库 用户单独空间 没有初始化会自动初始化 + * @param {vuex state} state vuex state + * @param {Function} fn function + */ + d2adminUtilDatabaseUser (state, fn) { + const uuid = util.cookies.get('uuid') + const database = db.get('database').find({ uuid }) + if (database.value() === undefined) { + db.get('database').push({ + uuid, + value: {} + }).write() + if (fn) { + fn(db.get('database').find({ uuid }).get('value')) + } + } else { + if (fn) { + fn(database.get('value')) + } + } + }, + /** + * @class 通用工具 + * @description 访问本地数据库 清空用户单独空间 只负责删除 d2adminUtilDatabaseUser 会初始化 + * @param {vuex state} state vuex state + */ + d2adminUtilDatabaseUserClear (state) { + db.get('database') + .remove({ uuid: util.cookies.get('uuid') }) + .write() + }, + /** + * @class 通用工具 + * @description 访问本地数据库 这份数据是每个用户都可以访问的 + * @param {vuex state} state vuex state + * @param {Function} fn function + */ + d2adminUtilDatabase (state, fn) { + if (fn) { + fn(db.get('databasePublic')) + } + }, + /** + * @class 通用工具 + * @description 访问本地数据库 清空公用空间 + * @param {vuex state} state vuex state + */ + d2adminUtilDatabaseClear (state) { + db.set('databasePublic', {}) + .write() + }, + /** + * @class UA + * @description 记录 UA + * @param {vuex state} state vuex state + */ + d2adminUaGet (state) { + state.ua = util.ua() + }, + /** + * @class menuHeader + * @description 设置顶栏菜单 + * @param {vuex state} state vuex state + * @param {Array} menu menu setting + */ + d2adminMenuHeaderSet (state, menu) { + state.menuHeader = menu + }, + /** + * @class menuAside + * @description 设置侧边栏菜单 + * @param {vuex state} state vuex state + * @param {Array} menu menu setting + */ + d2adminMenuAsideSet (state, menu) { + state.menuAside = menu + }, + /** + * @class ... + * @description 用户登陆后从数据库加载一系列的设置 + * @param {vuex state} state vuex state + */ + d2adminLoginSuccessLoad (state) { + // DB -> store 加载用户名 + this.commit('d2adminUserInfoLoad') + // DB -> store 加载主题 + this.commit('d2adminThemeLoad') + // DB -> store 数据库加载上次退出时的多页列表 + this.commit('d2adminPageOpenedListLoad') + // DB -> store 数据库加载这个用户之前设置的侧边栏折叠状态 + this.commit('d2adminMenuAsideCollapseLoad') + }, + /** + * @description 设置用户名 + * @class userInfo + * @param {vuex state} state vuex state + * @param {String} userInfo userInfo + */ + d2adminUserInfoSet (state, userInfo) { + state.userInfo = userInfo + this.commit('d2adminUtilVuex2DbByUuid', 'userInfo') + }, + /** + * @description 从数据库取用户名 + * @class userInfo + * @param {vuex state} state vuex state + */ + d2adminUserInfoLoad (state) { + this.commit('d2adminUtilDb2VuexByUuid', { + key: 'userInfo', + defaultValue: { + name: '请重新登陆' + } + }) + }, + /** + * @description 设置是否有新的 D2Admin 版本 + * @class releasesUpdate + * @param {vuex state} state vuex state + * @param {Boolean} releasesUpdate can update + */ + d2adminReleasesUpdateSet (state, releasesUpdate) { + state.releasesUpdate = releasesUpdate + }, + /** + * @description 设置最新版本的信息 + * @class releasesLatest + * @param {vuex state} state vuex state + * @param {Object}} releases releases value + */ + d2adminReleasesLatestSet (state, releases) { + state.releasesLatest = releases + }, + /** + * @class pagePool + * @description 保存 pagePool (候选池) + * @param {vuex state} state vuex state + * @param {Array} pagePool tags + */ + d2adminPagePoolSet (state, pagePool) { + state.pagePool = pagePool + }, + /** + * @class pageCurrent + * @description 打开一个新的页面 + * @param {vuex state} state vuex state + * @param {Object} param1 { name, params, query } 路由信息 + */ + d2adminPageOpenNew (state, { name, params, query }) { + // 已经打开的页面 + let pageOpenedList = state.pageOpenedList + // 判断此页面是否已经打开 并且记录位置 + let pageOpendIndex = 0 + const pageOpend = pageOpenedList.find((page, index) => { + const same = page.name === name + pageOpendIndex = same ? index : pageOpendIndex + return same + }) + if (pageOpend) { + // 页面以前打开过 但是新的页面可能 name 一样,参数不一样 + this.commit('d2adminPageOpenedListUpdateItem', { index: pageOpendIndex, params, query }) + } else { + // 页面以前没有打开过 + let tag = state.pagePool.find(t => t.name === name) + if (tag) { + this.commit('d2adminTagIncreate', { tag, params, query }) + } + } + this.commit('d2adminPageCurrentSet', name) + }, + /** + * @class pageCurrent + * @description 设置当前激活的页面 name + * @param {vuex state} state vuex state + * @param {String} name new name + */ + d2adminPageCurrentSet (state, name) { + state.pageCurrent = name + }, + /** + * @class pageOpenedList + * @description 更新页面列表上的某一项 + * @param {vuex state} state vuex state + * @param {Object} param1 { index, params, query } 路由信息 + */ + d2adminPageOpenedListUpdateItem (state, { index, params, query }) { + // 更新页面列表某一项 + let page = state.pageOpenedList[index] + page.params = params || page.params + page.query = query || page.query + state.pageOpenedList.splice(index, 1, page) + // 更新设置到数据库 + this.commit('d2adminUtilVuex2DbByUuid', 'pageOpenedList') + }, + /** + * @class pageOpenedList + * @description 从数据库载入分页列表 + * @param {vuex state} state vuex state + */ + d2adminPageOpenedListLoad (state) { + this.commit('d2adminUtilDb2VuexByUuid', { + key: 'pageOpenedList', + defaultValue: [ + pageOpenedDefult + ] + }) + }, + /** + * @class pageOpenedList + * @description 新增一个 tag (打开一个页面) + * @param {vuex state} state vuex state + * @param {Object} param1 new tag info + */ + d2adminTagIncreate (state, { tag, params, query }) { + // 设置新的 tag 在新打开一个以前没打开过的页面时使用 + let newPage = tag + newPage.params = params || newPage.params + newPage.query = query || newPage.query + // 添加进当前显示的页面数组 + state.pageOpenedList.push(newPage) + // 更新设置到数据库 + this.commit('d2adminUtilVuex2DbByUuid', 'pageOpenedList') + }, + /** + * @class pageOpenedList + * @description 关闭一个 tag (关闭一个页面) + * @param {vuex state} state vuex state + * @param {Object} param1 { tagName: 要关闭的标签名字, vm: vue } + */ + d2adminTagClose (state, { tagName, vm }) { + // 下个新的页面 + let newPage = state.pageOpenedList[0] + const isCurrent = state.pageCurrent === tagName + // 如果关闭的页面就是当前显示的页面 + if (isCurrent) { + // 去找一个新的页面 + let len = state.pageOpenedList.length + for (let i = 1; i < len; i++) { + if (state.pageOpenedList[i].name === tagName) { + if (i < len - 1) { + newPage = state.pageOpenedList[i + 1] + } else { + newPage = state.pageOpenedList[i - 1] + } + break + } + } + } + // 找到这个页面在已经打开的数据里是第几个 + const index = state.pageOpenedList.findIndex(page => page.name === tagName) + if (index >= 0) { + state.pageOpenedList.splice(index, 1) + } + // 更新设置到数据库 + this.commit('d2adminUtilVuex2DbByUuid', 'pageOpenedList') + // 最后需要判断是否需要跳到首页 + if (isCurrent) { + const { name = '', params = {}, query = {} } = newPage + let routerObj = { + name, + params, + query + } + vm.$router.push(routerObj) + } + }, + /** + * @class pageOpenedList + * @description 关闭当前标签左边的标签 + * @param {vuex state} state vuex state + */ + d2adminTagCloseLeft (state) { + let currentIndex = 0 + state.pageOpenedList.forEach((page, index) => { + if (page.name === state.pageCurrent) { + currentIndex = index + } + }) + if (currentIndex > 0) { + state.pageOpenedList.splice(1, currentIndex - 1) + } + // 更新设置到数据库 + this.commit('d2adminUtilVuex2DbByUuid', 'pageOpenedList') + }, + /** + * @class pageOpenedList + * @description 关闭当前标签右边的标签 + * @param {vuex state} state vuex state + */ + d2adminTagCloseRight (state) { + let currentIndex = 0 + state.pageOpenedList.forEach((page, index) => { + if (page.name === state.pageCurrent) { + currentIndex = index + } + }) + state.pageOpenedList.splice(currentIndex + 1) + // 更新设置到数据库 + this.commit('d2adminUtilVuex2DbByUuid', 'pageOpenedList') + }, + /** + * @class pageOpenedList + * @description 关闭当前激活之外的 tag + * @param {vuex state} state vuex state + */ + d2adminTagCloseOther (state) { + let currentIndex = 0 + state.pageOpenedList.forEach((page, index) => { + if (page.name === state.pageCurrent) { + currentIndex = index + } + }) + if (currentIndex === 0) { + state.pageOpenedList.splice(1) + } else { + state.pageOpenedList.splice(currentIndex + 1) + state.pageOpenedList.splice(1, currentIndex - 1) + } + // 更新设置到数据库 + this.commit('d2adminUtilVuex2DbByUuid', 'pageOpenedList') + }, + /** + * @class pageOpenedList + * @description 关闭所有 tag + * @param {vuex state} state vuex state + * @param {Object} vm vue + */ + d2adminTagCloseAll (state, vm) { + state.pageOpenedList.splice(1) + // 更新设置到数据库 + this.commit('d2adminUtilVuex2DbByUuid', 'pageOpenedList') + // 关闭所有的标签页后需要判断一次现在是不是在首页 + if (vm.$route.name !== 'index') { + vm.$router.push({ + name: 'index' + }) + } + }, + /** + * 设置侧边栏展开或者收缩 + * @class isMenuAsideCollapse + * @param {vuex state} state vuex state + * @param {Boolean} collapse is collapse + */ + d2adminMenuAsideCollapseSet (state, collapse) { + state.isMenuAsideCollapse = collapse + this.commit('d2adminUtilVuex2DbByUuid', 'isMenuAsideCollapse') + }, + /** + * 切换侧边栏展开和收缩 + * @class isMenuAsideCollapse + * @param {vuex state} state vuex state + */ + d2adminMenuAsideCollapseToggle (state) { + state.isMenuAsideCollapse = !state.isMenuAsideCollapse + this.commit('d2adminUtilVuex2DbByUuid', 'isMenuAsideCollapse') + }, + /** + * 从数据库读取侧边栏展开或者收缩 + * @class isMenuAsideCollapse + * @param {vuex state} state vuex state + */ + d2adminMenuAsideCollapseLoad (state) { + this.commit('d2adminUtilDb2VuexByUuid', { + key: 'isMenuAsideCollapse', + defaultValue: false + }) + }, + /** + * @class isFullScreen + * @description 切换全屏 + * @param {vuex state} state vuex state + */ + d2adminFullScreenToggle () { + if (screenfull.isFullscreen) { + screenfull.exit() + this.commit('d2adminFullScreenSet', false) + } else { + screenfull.request() + this.commit('d2adminFullScreenSet', true) + } + }, + /** + * @class isFullScreen + * @description 设置 store 里的全屏状态 + * @param {vuex state} state vuex state + */ + d2adminFullScreenSet (state, isFullScreen) { + state.isFullScreen = isFullScreen + }, + /** + * @class isGrayMode + * @description 切换灰度状态 + * @param {vuex state} state vuex state + */ + d2adminGrayModeToggle (state) { + state.isGrayMode = !state.isGrayMode + }, + /** + * @class isGrayMode + * @description 设置灰度模式 + * @param {vuex state} state vuex state + * @param {Boolean} value new value + */ + d2adminGrayModeSet (state, value) { + state.isGrayMode = value + }, + /** + * @class themeActiveName + * @description 激活一个主题(应用到dom上) + * @param {vuex state} state vuex state + * @param {String} themeValue 需要激活的主题名称 + */ + d2adminThemeSet (state, themeName) { + // 检查这个主题在主题列表里是否存在 + const theme = state.themeList.find(e => e.name === themeName) + if (theme) { + // 设置 state + state.themeActiveName = themeName + } else { + // 设置为列表第一个主题 + state.themeActiveName = state.themeList[0].name + } + // 将 vuex 中的主题应用到 dom + this.commit('d2adminTheme2dom') + // 保存到数据库 + this.commit('d2adminUtilVuex2DbByUuid', 'themeActiveName') + }, + /** + * @class themeActiveName + * @description 从数据库加载主题设置 + * @param {vuex state} state vuex state + */ + d2adminThemeLoad (state) { + this.commit('d2adminUtilDb2VuexByUuid', { + key: 'themeActiveName', + defaultValue: state.themeList[0].name + }) + this.commit('d2adminTheme2dom') + }, + /** + * @class themeActiveName + * @description 将 vuex 中的主题应用到 dom + * @param {vuex state} state vuex state + */ + d2adminTheme2dom (state) { + document.body.className = `theme-${state.themeActiveName}` + } + } +} diff --git a/tests/unit/.eslintrc.js b/tests/unit/.eslintrc.js new file mode 100644 index 0000000..4e51c63 --- /dev/null +++ b/tests/unit/.eslintrc.js @@ -0,0 +1,8 @@ +module.exports = { + env: { + jest: true + }, + rules: { + 'import/no-extraneous-dependencies': 'off' + } +} \ No newline at end of file diff --git a/tests/unit/HelloWorld.spec.js b/tests/unit/HelloWorld.spec.js new file mode 100644 index 0000000..566048c --- /dev/null +++ b/tests/unit/HelloWorld.spec.js @@ -0,0 +1,14 @@ +// 暂时 D2 没有单元测试代码 此文件为自动生成 + +import { shallowMount } from '@vue/test-utils' +import HelloWorld from '@/components/HelloWorld.vue' + +describe('HelloWorld.vue', () => { + it('renders props.msg when passed', () => { + const msg = 'new message' + const wrapper = shallowMount(HelloWorld, { + propsData: { msg } + }) + expect(wrapper.text()).toMatch(msg) + }) +}) diff --git a/vue.config.js b/vue.config.js new file mode 100644 index 0000000..87e6b2e --- /dev/null +++ b/vue.config.js @@ -0,0 +1,56 @@ +const path = require('path') + +// 拼接路径 +function resolve (dir) { + return path.join(__dirname, dir) +} + +// 基础路径 注意发布之前要先修改这里 +const baseUrl = '/d2-admin-preview-dev/' + +module.exports = { + baseUrl: baseUrl, // 根据你的实际情况更改这里 + lintOnSave: true, + devServer: { + publicPath: baseUrl // 和 baseUrl 保持一致 + }, + // webpack 设置 + // 默认设置: https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-service/lib/config/base.js + chainWebpack: config => { + // i18n + config.module + .rule('i18n') + .resourceQuery(/blockType=i18n/) + .use('i18n') + .loader('@kazupon/vue-i18n-loader') + .end() + // svg + const svgRule = config.module.rule('svg') + svgRule.uses.clear() + svgRule + .include + .add(resolve('src/assets/svg-icons/icons')) + .end() + .use('svg-sprite-loader') + .loader('svg-sprite-loader') + .options({ + symbolId: 'd2-[name]' + }) + .end() + // image exclude + const imagesRule = config.module.rule('images') + imagesRule + .test(/\.(png|jpe?g|gif|webp|svg)(\?.*)?$/) + .exclude + .add(resolve('src/assets/svg-icons/icons')) + .end() + // 重新设置 alias + config.resolve.alias + .set('@', resolve('src')) + // babel-polyfill 加入 entry + const entry = config.entry('app') + entry + .add('babel-polyfill') + .end() + } +}