commit
64c6e4c2e1
@ -0,0 +1,9 @@
|
|||||||
|
VUE_APP_PUBLIC_PATH=/
|
||||||
|
VUE_APP_NAME=Admin
|
||||||
|
VUE_APP_ROUTES_KEY=admin.routes
|
||||||
|
VUE_APP_PERMISSIONS_KEY=admin.permissions
|
||||||
|
VUE_APP_ROLES_KEY=admin.roles
|
||||||
|
VUE_APP_USER_KEY=admin.user
|
||||||
|
VUE_APP_SETTING_KEY=admin.setting
|
||||||
|
VUE_APP_TBAS_KEY=admin.tabs
|
||||||
|
VUE_APP_TBAS_TITLES_KEY=admin.tabs.titles
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
VUE_ENV = development
|
||||||
|
|
||||||
|
VUE_APP_PUBLIC_PATH = '/admin'
|
||||||
|
|
||||||
|
# VUE_APP_API_BASE_URL=https://mock.localhost.com
|
||||||
|
VUE_APP_API_URL=https://card.h888.fun/adminapi/v1
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
VUE_ENV = production
|
||||||
|
|
||||||
|
VUE_APP_PUBLIC_PATH = '/admin'
|
||||||
|
|
||||||
|
VUE_APP_API_URL=https://utel.vip/adminapi/v1
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
VUE_ENV = production
|
||||||
|
|
||||||
|
VUE_APP_PUBLIC_PATH = '/admin'
|
||||||
|
|
||||||
|
VUE_APP_API_URL=https://card.h888.fun/adminapi/v1
|
||||||
|
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
VUE_ENV = stage
|
||||||
|
|
||||||
|
VUE_APP_PUBLIC_PATH = '/admin'
|
||||||
|
|
||||||
|
VUE_APP_API_URL=https://utel.zltest.com.tw/adminapi/v1
|
||||||
|
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
.DS_Store
|
||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
admindb/
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
/test/unit/coverage/
|
||||||
|
/test/e2e/reports/
|
||||||
|
selenium-debug.log
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
package-lock.json
|
||||||
|
.env.production.local
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2018 iczer
|
||||||
|
|
||||||
|
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.
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV)
|
||||||
|
|
||||||
|
const plugins = []
|
||||||
|
if (IS_PROD) {
|
||||||
|
plugins.push('transform-remove-console')
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
'@vue/cli-plugin-babel/preset'
|
||||||
|
],
|
||||||
|
plugins
|
||||||
|
}
|
||||||
@ -0,0 +1,90 @@
|
|||||||
|
{
|
||||||
|
"name": "vue-antd-admin",
|
||||||
|
"version": "0.7.4",
|
||||||
|
"homepage": "https://iczer.github.io/vue-antd-admin",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"serve": "vue-cli-service serve",
|
||||||
|
"build": "vue-cli-service build",
|
||||||
|
"build:dev": "vue-cli-service build --mode development",
|
||||||
|
"build:sta": "vue-cli-service build --mode stage",
|
||||||
|
"build:slash": "vue-cli-service build --mode slash",
|
||||||
|
"lint": "vue-cli-service lint",
|
||||||
|
"predeploy": "yarn build",
|
||||||
|
"deploy": "gh-pages -d dist -b pages -r https://gitee.com/iczer/vue-antd-admin.git",
|
||||||
|
"docs:dev": "vuepress dev docs",
|
||||||
|
"docs:build": "vuepress build docs",
|
||||||
|
"docs:deploy": "vuepress build docs && gh-pages -d docs/.vuepress/dist -b master -r https://gitee.com/iczer/vue-antd-admin-docs.git"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@antv/data-set": "^0.11.4",
|
||||||
|
"animate.css": "^4.1.0",
|
||||||
|
"ant-design-vue": "1.7.2",
|
||||||
|
"axios": "^0.19.2",
|
||||||
|
"ckeditor4-vue": "^2.0.0",
|
||||||
|
"clipboard": "^2.0.6",
|
||||||
|
"core-js": "^3.6.5",
|
||||||
|
"date-fns": "^2.14.0",
|
||||||
|
"echarts": "^4.9.0",
|
||||||
|
"enquire.js": "^2.1.6",
|
||||||
|
"highlight.js": "^10.2.1",
|
||||||
|
"js-cookie": "^2.2.1",
|
||||||
|
"mockjs": "^1.1.0",
|
||||||
|
"nprogress": "^0.2.0",
|
||||||
|
"postcss-loader": "^4.3.0",
|
||||||
|
"qrcodejs2": "^0.0.2",
|
||||||
|
"raw-loader": "^4.0.2",
|
||||||
|
"viser-vue": "^2.4.8",
|
||||||
|
"vue": "^2.6.11",
|
||||||
|
"vue-clipboard2": "^0.3.3",
|
||||||
|
"vue-echarts": "^4.0.3",
|
||||||
|
"vue-element-resize-event": "^0.1.0",
|
||||||
|
"vue-i18n": "^8.18.2",
|
||||||
|
"vue-print-nb": "^1.7.5",
|
||||||
|
"vue-router": "^3.3.4",
|
||||||
|
"vuedraggable": "^2.23.2",
|
||||||
|
"vuex": "^3.4.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@ant-design/colors": "^4.0.1",
|
||||||
|
"@vue/cli-plugin-babel": "^4.4.0",
|
||||||
|
"@vue/cli-plugin-eslint": "^4.4.0",
|
||||||
|
"@vue/cli-service": "^4.4.0",
|
||||||
|
"@vuepress/plugin-back-to-top": "^1.5.2",
|
||||||
|
"babel-eslint": "^10.1.0",
|
||||||
|
"babel-plugin-transform-remove-console": "^6.9.4",
|
||||||
|
"babel-polyfill": "^6.26.0",
|
||||||
|
"compression-webpack-plugin": "^2.0.0",
|
||||||
|
"deepmerge": "^4.2.2",
|
||||||
|
"eslint": "^6.7.2",
|
||||||
|
"eslint-plugin-vue": "^6.2.2",
|
||||||
|
"fast-deep-equal": "^3.1.3",
|
||||||
|
"gh-pages": "^3.1.0",
|
||||||
|
"less-loader": "^6.1.1",
|
||||||
|
"style-resources-loader": "^1.3.2",
|
||||||
|
"vue-cli-plugin-style-resources-loader": "^0.1.4",
|
||||||
|
"vue-template-compiler": "^2.6.11",
|
||||||
|
"vuepress": "^1.5.2",
|
||||||
|
"webpack-theme-color-replacer": "1.3.18",
|
||||||
|
"whatwg-fetch": "^3.0.0"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"root": true,
|
||||||
|
"env": {
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"plugin:vue/essential",
|
||||||
|
"eslint:recommended"
|
||||||
|
],
|
||||||
|
"parserOptions": {
|
||||||
|
"parser": "babel-eslint"
|
||||||
|
},
|
||||||
|
"rules": {}
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
"> 1%",
|
||||||
|
"last 2 versions",
|
||||||
|
"not ie <= 10"
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,349 @@
|
|||||||
|
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
|
||||||
|
|
||||||
|
/* Document
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the line height in all browsers.
|
||||||
|
* 2. Prevent adjustments of font size after orientation changes in iOS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
html {
|
||||||
|
line-height: 1.15; /* 1 */
|
||||||
|
-webkit-text-size-adjust: 100%; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sections
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the margin in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the `main` element consistently in IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
main {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the font size and margin on `h1` elements within `section` and
|
||||||
|
* `article` contexts in Chrome, Firefox, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
margin: 0.67em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grouping content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in Firefox.
|
||||||
|
* 2. Show the overflow in Edge and IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
hr {
|
||||||
|
box-sizing: content-box; /* 1 */
|
||||||
|
height: 0; /* 1 */
|
||||||
|
overflow: visible; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
pre {
|
||||||
|
font-family: monospace, monospace; /* 1 */
|
||||||
|
font-size: 1em; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Text-level semantics
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the gray background on active links in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
a {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Remove the bottom border in Chrome 57-
|
||||||
|
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
abbr[title] {
|
||||||
|
border-bottom: none; /* 1 */
|
||||||
|
text-decoration: underline; /* 2 */
|
||||||
|
text-decoration: underline dotted; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
samp {
|
||||||
|
font-family: monospace, monospace; /* 1 */
|
||||||
|
font-size: 1em; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font size in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||||
|
* all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
font-size: 75%;
|
||||||
|
line-height: 0;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Embedded content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the border on images inside links in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
img {
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Forms
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Change the font styles in all browsers.
|
||||||
|
* 2. Remove the margin in Firefox and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
optgroup,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
font-family: inherit; /* 1 */
|
||||||
|
font-size: 100%; /* 1 */
|
||||||
|
line-height: 1.15; /* 1 */
|
||||||
|
margin: 0; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the overflow in IE.
|
||||||
|
* 1. Show the overflow in Edge.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input { /* 1 */
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||||
|
* 1. Remove the inheritance of text transform in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
select { /* 1 */
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
[type="button"],
|
||||||
|
[type="reset"],
|
||||||
|
[type="submit"] {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner border and padding in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button::-moz-focus-inner,
|
||||||
|
[type="button"]::-moz-focus-inner,
|
||||||
|
[type="reset"]::-moz-focus-inner,
|
||||||
|
[type="submit"]::-moz-focus-inner {
|
||||||
|
border-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore the focus styles unset by the previous rule.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button:-moz-focusring,
|
||||||
|
[type="button"]:-moz-focusring,
|
||||||
|
[type="reset"]:-moz-focusring,
|
||||||
|
[type="submit"]:-moz-focusring {
|
||||||
|
outline: 1px dotted ButtonText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the padding in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
padding: 0.35em 0.75em 0.625em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the text wrapping in Edge and IE.
|
||||||
|
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||||
|
* 3. Remove the padding so developers are not caught out when they zero out
|
||||||
|
* `fieldset` elements in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
legend {
|
||||||
|
box-sizing: border-box; /* 1 */
|
||||||
|
color: inherit; /* 2 */
|
||||||
|
display: table; /* 1 */
|
||||||
|
max-width: 100%; /* 1 */
|
||||||
|
padding: 0; /* 3 */
|
||||||
|
white-space: normal; /* 1 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||||
|
*/
|
||||||
|
|
||||||
|
progress {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the default vertical scrollbar in IE 10+.
|
||||||
|
*/
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in IE 10.
|
||||||
|
* 2. Remove the padding in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="checkbox"],
|
||||||
|
[type="radio"] {
|
||||||
|
box-sizing: border-box; /* 1 */
|
||||||
|
padding: 0; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="number"]::-webkit-inner-spin-button,
|
||||||
|
[type="number"]::-webkit-outer-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the odd appearance in Chrome and Safari.
|
||||||
|
* 2. Correct the outline style in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="search"] {
|
||||||
|
-webkit-appearance: textfield; /* 1 */
|
||||||
|
outline-offset: -2px; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner padding in Chrome and Safari on macOS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="search"]::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
* 2. Change font properties to `inherit` in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
::-webkit-file-upload-button {
|
||||||
|
-webkit-appearance: button; /* 1 */
|
||||||
|
font: inherit; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Interactive
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the correct display in Edge, IE 10+, and Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
details {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the correct display in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
summary {
|
||||||
|
display: list-item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Misc
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 10+.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
@ -0,0 +1,222 @@
|
|||||||
|
@charset "utf-8";
|
||||||
|
body,
|
||||||
|
div,
|
||||||
|
dl,
|
||||||
|
footer,
|
||||||
|
html,
|
||||||
|
img,
|
||||||
|
menu,
|
||||||
|
p,
|
||||||
|
span {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.5;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-webkit-touch-callout: none;
|
||||||
|
background-color: #fffff6 !important;
|
||||||
|
padding-bottom: 49px;
|
||||||
|
}
|
||||||
|
a,
|
||||||
|
a:hover,
|
||||||
|
a:visited {
|
||||||
|
color: #999;
|
||||||
|
text-decoration: none;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
@-webkit-keyframes pop-hide {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: scale(0.8);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
2% {
|
||||||
|
-webkit-transform: scale(1.1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
6% {
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
}
|
||||||
|
90% {
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
-webkit-transform: scale(0.9);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@-webkit-keyframes pop {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: scale(0.8);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
40% {
|
||||||
|
-webkit-transform: scale(1.1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@-webkit-keyframes slideup {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: translateY(100%);
|
||||||
|
}
|
||||||
|
40% {
|
||||||
|
-webkit-transform: translateY(-10%);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
-webkit-transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.left {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
.rel {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
a,
|
||||||
|
a:visited {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.text-icon {
|
||||||
|
font-family: base_icon;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
.my-account {
|
||||||
|
color: #333;
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
height: 6rem;
|
||||||
|
}
|
||||||
|
.account-bg {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
.account-bg img {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.my-account > img {
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
.my-account .user-info {
|
||||||
|
z-index: 1;
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
left: 70px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding-left: 1.9em;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
.my-account .uname {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #fff;
|
||||||
|
margin-top: 0.1em;
|
||||||
|
margin-bottom: 0.2em;
|
||||||
|
text-shadow: 0.07em 0.07em #333;
|
||||||
|
}
|
||||||
|
.my-account .umoney {
|
||||||
|
color: #fff;
|
||||||
|
margin-bottom: 0.06em;
|
||||||
|
text-shadow: 0.05em 0.05em #333;
|
||||||
|
}
|
||||||
|
.my-account .avatar_box {
|
||||||
|
position: absolute;
|
||||||
|
top: 1em;
|
||||||
|
left: 1em;
|
||||||
|
width: 5em;
|
||||||
|
height: 5em;
|
||||||
|
z-index: 1;
|
||||||
|
border-radius: 100%;
|
||||||
|
border: 2px solid #ffd44a;
|
||||||
|
-moz-border-radius: 100%;
|
||||||
|
-webkit-border-radius: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.my-account .avater {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.phone {
|
||||||
|
width: 105px;
|
||||||
|
float: left;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
.set {
|
||||||
|
position: absolute;
|
||||||
|
width: 60px;
|
||||||
|
right: 10px;
|
||||||
|
top: 20px;
|
||||||
|
z-index: 100;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 15px;
|
||||||
|
background-color: #fdaf00;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: -7px;
|
||||||
|
padding: 2px 2px;
|
||||||
|
}
|
||||||
|
.set a {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
.dl01 {
|
||||||
|
padding: 0 10px 10px;
|
||||||
|
background-color: #fff;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
.titleImg {
|
||||||
|
width: 25px;
|
||||||
|
height: 25px;
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-top: 15px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
.dl02 {
|
||||||
|
padding: 0 10px;
|
||||||
|
background-color: #fff;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.dl02 a .menu {
|
||||||
|
border-bottom: 1px solid #ffe9b7;
|
||||||
|
background: url(../images/right.png) no-repeat right center;
|
||||||
|
background-size: 10px;
|
||||||
|
}
|
||||||
|
.dl02 a .menu div {
|
||||||
|
padding-top: 16px;
|
||||||
|
font-size: 15px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
.dl02 a .menu div.left {
|
||||||
|
float: left;
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
|
.dl02 a .menu div.right {
|
||||||
|
float: left;
|
||||||
|
text-align: right;
|
||||||
|
width: 45%;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 484 B |
@ -0,0 +1,27 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-TW" class="beauty-scroll">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
|
||||||
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||||
|
<title><%= process.env.VUE_APP_NAME %></title>
|
||||||
|
<!-- require cdn assets css -->
|
||||||
|
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %>
|
||||||
|
<link rel="stylesheet" href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" />
|
||||||
|
<% } %>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||||
|
</noscript>
|
||||||
|
<div id="popContainer" class="beauty-scroll" style="height: 100vh; overflow-y: scroll">
|
||||||
|
<div id="app"></div>
|
||||||
|
</div>
|
||||||
|
<!-- require cdn assets js -->
|
||||||
|
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
|
||||||
|
<script type="text/javascript" src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
|
||||||
|
<% } %>
|
||||||
|
<!-- built files will be auto injected -->
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -0,0 +1,120 @@
|
|||||||
|
<template>
|
||||||
|
<a-config-provider :locale="locale" :get-popup-container="popContainer">
|
||||||
|
<router-view/>
|
||||||
|
</a-config-provider>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {enquireScreen} from './utils/util'
|
||||||
|
import {mapState, mapMutations} from 'vuex'
|
||||||
|
import themeUtil from '@/utils/themeUtil';
|
||||||
|
import {getI18nKey} from '@/utils/routerUtil'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'App',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
locale: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.setHtmlTitle()
|
||||||
|
this.setLanguage(this.lang)
|
||||||
|
enquireScreen(isMobile => this.setDevice(isMobile))
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.setWeekModeTheme(this.weekMode)
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
weekMode(val) {
|
||||||
|
this.setWeekModeTheme(val)
|
||||||
|
},
|
||||||
|
lang(val) {
|
||||||
|
this.setLanguage(val)
|
||||||
|
this.setHtmlTitle()
|
||||||
|
},
|
||||||
|
$route() {
|
||||||
|
this.setHtmlTitle()
|
||||||
|
},
|
||||||
|
'theme.mode': function(val) {
|
||||||
|
let closeMessage = this.$message.loading(`您選擇了主題模式 ${val}, 正在切換...`)
|
||||||
|
themeUtil.changeThemeColor(this.theme.color, val).then(closeMessage)
|
||||||
|
},
|
||||||
|
'theme.color': function(val) {
|
||||||
|
let closeMessage = this.$message.loading(`您選擇了主題色 ${val}, 正在切換...`)
|
||||||
|
themeUtil.changeThemeColor(val, this.theme.mode).then(closeMessage)
|
||||||
|
},
|
||||||
|
'layout': function() {
|
||||||
|
window.dispatchEvent(new Event('resize'))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState('setting', ['layout', 'theme', 'weekMode', 'lang'])
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapMutations('setting', ['setDevice']),
|
||||||
|
setWeekModeTheme(weekMode) {
|
||||||
|
if (weekMode) {
|
||||||
|
document.body.classList.add('week-mode')
|
||||||
|
} else {
|
||||||
|
document.body.classList.remove('week-mode')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setLanguage(lang) {
|
||||||
|
this.$i18n.locale = lang
|
||||||
|
switch (lang) {
|
||||||
|
case 'TW':
|
||||||
|
this.locale = require('ant-design-vue/es/locale-provider/zh_TW').default
|
||||||
|
break
|
||||||
|
case 'CN':
|
||||||
|
this.locale = require('ant-design-vue/es/locale-provider/zh_CN').default
|
||||||
|
break
|
||||||
|
case 'US':
|
||||||
|
default:
|
||||||
|
this.locale = require('ant-design-vue/es/locale-provider/en_US').default
|
||||||
|
break
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setHtmlTitle() {
|
||||||
|
const route = this.$route
|
||||||
|
const key = route.path === '/' ? 'home.name' : getI18nKey(route.matched[route.matched.length - 1].path)
|
||||||
|
document.title = process.env.VUE_APP_NAME + ' | ' + this.$t(key)
|
||||||
|
},
|
||||||
|
popContainer() {
|
||||||
|
return document.getElementById("popContainer")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
#id{
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-btn{
|
||||||
|
color: #239DFA;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-btn{
|
||||||
|
color: #F95838;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-drawer-header{
|
||||||
|
background-color: #87e8de !important;
|
||||||
|
.ant-drawer-title{
|
||||||
|
color: #FFF !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.ant-drawer-content-wrapper {
|
||||||
|
width: 60% !important;
|
||||||
|
}
|
||||||
|
@media only screen and (max-width: @screen-md) {
|
||||||
|
.ant-drawer-content-wrapper {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</style>
|
||||||
@ -0,0 +1,349 @@
|
|||||||
|
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
|
||||||
|
|
||||||
|
/* Document
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the line height in all browsers.
|
||||||
|
* 2. Prevent adjustments of font size after orientation changes in iOS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
html {
|
||||||
|
line-height: 1.15; /* 1 */
|
||||||
|
-webkit-text-size-adjust: 100%; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sections
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the margin in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the `main` element consistently in IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
main {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the font size and margin on `h1` elements within `section` and
|
||||||
|
* `article` contexts in Chrome, Firefox, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
margin: 0.67em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grouping content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in Firefox.
|
||||||
|
* 2. Show the overflow in Edge and IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
hr {
|
||||||
|
box-sizing: content-box; /* 1 */
|
||||||
|
height: 0; /* 1 */
|
||||||
|
overflow: visible; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
pre {
|
||||||
|
font-family: monospace, monospace; /* 1 */
|
||||||
|
font-size: 1em; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Text-level semantics
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the gray background on active links in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
a {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Remove the bottom border in Chrome 57-
|
||||||
|
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
abbr[title] {
|
||||||
|
border-bottom: none; /* 1 */
|
||||||
|
text-decoration: underline; /* 2 */
|
||||||
|
text-decoration: underline dotted; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
samp {
|
||||||
|
font-family: monospace, monospace; /* 1 */
|
||||||
|
font-size: 1em; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font size in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||||
|
* all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
font-size: 75%;
|
||||||
|
line-height: 0;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Embedded content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the border on images inside links in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
img {
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Forms
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Change the font styles in all browsers.
|
||||||
|
* 2. Remove the margin in Firefox and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
optgroup,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
font-family: inherit; /* 1 */
|
||||||
|
font-size: 100%; /* 1 */
|
||||||
|
line-height: 1.15; /* 1 */
|
||||||
|
margin: 0; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the overflow in IE.
|
||||||
|
* 1. Show the overflow in Edge.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input { /* 1 */
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||||
|
* 1. Remove the inheritance of text transform in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
select { /* 1 */
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
[type="button"],
|
||||||
|
[type="reset"],
|
||||||
|
[type="submit"] {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner border and padding in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button::-moz-focus-inner,
|
||||||
|
[type="button"]::-moz-focus-inner,
|
||||||
|
[type="reset"]::-moz-focus-inner,
|
||||||
|
[type="submit"]::-moz-focus-inner {
|
||||||
|
border-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore the focus styles unset by the previous rule.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button:-moz-focusring,
|
||||||
|
[type="button"]:-moz-focusring,
|
||||||
|
[type="reset"]:-moz-focusring,
|
||||||
|
[type="submit"]:-moz-focusring {
|
||||||
|
outline: 1px dotted ButtonText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the padding in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
padding: 0.35em 0.75em 0.625em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the text wrapping in Edge and IE.
|
||||||
|
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||||
|
* 3. Remove the padding so developers are not caught out when they zero out
|
||||||
|
* `fieldset` elements in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
legend {
|
||||||
|
box-sizing: border-box; /* 1 */
|
||||||
|
color: inherit; /* 2 */
|
||||||
|
display: table; /* 1 */
|
||||||
|
max-width: 100%; /* 1 */
|
||||||
|
padding: 0; /* 3 */
|
||||||
|
white-space: normal; /* 1 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||||
|
*/
|
||||||
|
|
||||||
|
progress {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the default vertical scrollbar in IE 10+.
|
||||||
|
*/
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in IE 10.
|
||||||
|
* 2. Remove the padding in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="checkbox"],
|
||||||
|
[type="radio"] {
|
||||||
|
box-sizing: border-box; /* 1 */
|
||||||
|
padding: 0; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="number"]::-webkit-inner-spin-button,
|
||||||
|
[type="number"]::-webkit-outer-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the odd appearance in Chrome and Safari.
|
||||||
|
* 2. Correct the outline style in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="search"] {
|
||||||
|
-webkit-appearance: textfield; /* 1 */
|
||||||
|
outline-offset: -2px; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner padding in Chrome and Safari on macOS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="search"]::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
* 2. Change font properties to `inherit` in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
::-webkit-file-upload-button {
|
||||||
|
-webkit-appearance: button; /* 1 */
|
||||||
|
font: inherit; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Interactive
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the correct display in Edge, IE 10+, and Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
details {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the correct display in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
summary {
|
||||||
|
display: list-item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Misc
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 10+.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
@ -0,0 +1,222 @@
|
|||||||
|
@charset "utf-8";
|
||||||
|
body,
|
||||||
|
div,
|
||||||
|
dl,
|
||||||
|
footer,
|
||||||
|
html,
|
||||||
|
img,
|
||||||
|
menu,
|
||||||
|
p,
|
||||||
|
span {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.5;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-webkit-touch-callout: none;
|
||||||
|
background-color: #fffff6 !important;
|
||||||
|
padding-bottom: 49px;
|
||||||
|
}
|
||||||
|
a,
|
||||||
|
a:hover,
|
||||||
|
a:visited {
|
||||||
|
color: #999;
|
||||||
|
text-decoration: none;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
@-webkit-keyframes pop-hide {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: scale(0.8);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
2% {
|
||||||
|
-webkit-transform: scale(1.1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
6% {
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
}
|
||||||
|
90% {
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
-webkit-transform: scale(0.9);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@-webkit-keyframes pop {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: scale(0.8);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
40% {
|
||||||
|
-webkit-transform: scale(1.1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@-webkit-keyframes slideup {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: translateY(100%);
|
||||||
|
}
|
||||||
|
40% {
|
||||||
|
-webkit-transform: translateY(-10%);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
-webkit-transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.left {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
.rel {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
a,
|
||||||
|
a:visited {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.text-icon {
|
||||||
|
font-family: base_icon;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
.my-account {
|
||||||
|
color: #333;
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
height: 6rem;
|
||||||
|
}
|
||||||
|
.account-bg {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
.account-bg img {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.my-account > img {
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
.my-account .user-info {
|
||||||
|
z-index: 1;
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
left: 70px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding-left: 1.9em;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
.my-account .uname {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #fff;
|
||||||
|
margin-top: 0.1em;
|
||||||
|
margin-bottom: 0.2em;
|
||||||
|
text-shadow: 0.07em 0.07em #333;
|
||||||
|
}
|
||||||
|
.my-account .umoney {
|
||||||
|
color: #fff;
|
||||||
|
margin-bottom: 0.06em;
|
||||||
|
text-shadow: 0.05em 0.05em #333;
|
||||||
|
}
|
||||||
|
.my-account .avatar_box {
|
||||||
|
position: absolute;
|
||||||
|
top: 1em;
|
||||||
|
left: 1em;
|
||||||
|
width: 5em;
|
||||||
|
height: 5em;
|
||||||
|
z-index: 1;
|
||||||
|
border-radius: 100%;
|
||||||
|
border: 2px solid #ffd44a;
|
||||||
|
-moz-border-radius: 100%;
|
||||||
|
-webkit-border-radius: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.my-account .avater {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.phone {
|
||||||
|
width: 105px;
|
||||||
|
float: left;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
.set {
|
||||||
|
position: absolute;
|
||||||
|
width: 60px;
|
||||||
|
right: 10px;
|
||||||
|
top: 20px;
|
||||||
|
z-index: 100;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 15px;
|
||||||
|
background-color: #fdaf00;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: -7px;
|
||||||
|
padding: 2px 2px;
|
||||||
|
}
|
||||||
|
.set a {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
.dl01 {
|
||||||
|
padding: 0 10px 10px;
|
||||||
|
background-color: #fff;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
.titleImg {
|
||||||
|
width: 25px;
|
||||||
|
height: 25px;
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-top: 15px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
.dl02 {
|
||||||
|
padding: 0 10px;
|
||||||
|
background-color: #fff;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.dl02 a .menu {
|
||||||
|
border-bottom: 1px solid #ffe9b7;
|
||||||
|
background: url(../images/right.png) no-repeat right center;
|
||||||
|
background-size: 10px;
|
||||||
|
}
|
||||||
|
.dl02 a .menu div {
|
||||||
|
padding-top: 16px;
|
||||||
|
font-size: 15px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
.dl02 a .menu div.left {
|
||||||
|
float: left;
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
|
.dl02 a .menu div.right {
|
||||||
|
float: left;
|
||||||
|
text-align: right;
|
||||||
|
width: 45%;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 596 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 18 KiB |
@ -0,0 +1,25 @@
|
|||||||
|
import {loadRoutes, loadGuards, setAppOptions} from '@/utils/routerUtil'
|
||||||
|
import {loadInterceptors} from '@/utils/request'
|
||||||
|
import guards from '@/router/guards'
|
||||||
|
import interceptors from '@/utils/axios-interceptors'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 啟動引導方法
|
||||||
|
* 應用啟動時需要執行的操作放在這裡
|
||||||
|
* @param router 應用的路由例項
|
||||||
|
* @param store 應用的 vuex.store 例項
|
||||||
|
* @param i18n 應用的 vue-i18n 例項
|
||||||
|
* @param i18n 應用的 message 例項
|
||||||
|
*/
|
||||||
|
function bootstrap({router, store, i18n, message}) {
|
||||||
|
// 設定應用配置
|
||||||
|
setAppOptions({router, store, i18n})
|
||||||
|
// 載入 axios 攔截器
|
||||||
|
loadInterceptors(interceptors, {router, store, i18n, message})
|
||||||
|
// 載入路由
|
||||||
|
loadRoutes()
|
||||||
|
// 載入路由守衛
|
||||||
|
loadGuards(guards, {router, store, i18n, message})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default bootstrap
|
||||||
@ -0,0 +1,172 @@
|
|||||||
|
import {isDef, isRegExp, remove} from '@/utils/util'
|
||||||
|
|
||||||
|
const patternTypes = [String, RegExp, Array]
|
||||||
|
|
||||||
|
function matches (pattern, name) {
|
||||||
|
if (Array.isArray(pattern)) {
|
||||||
|
if (pattern.indexOf(name) > -1) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
for (let item of pattern) {
|
||||||
|
if (isRegExp(item) && item.test(name)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else if (typeof pattern === 'string') {
|
||||||
|
return pattern.split(',').indexOf(name) > -1
|
||||||
|
} else if (isRegExp(pattern)) {
|
||||||
|
return pattern.test(name)
|
||||||
|
}
|
||||||
|
/* istanbul ignore next */
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function getComponentName (opts) {
|
||||||
|
return opts && (opts.Ctor.options.name || opts.tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getComponentKey (vnode) {
|
||||||
|
const {componentOptions, key} = vnode
|
||||||
|
return key == null
|
||||||
|
? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
|
||||||
|
: key + componentOptions.Ctor.cid
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFirstComponentChild (children) {
|
||||||
|
if (Array.isArray(children)) {
|
||||||
|
for (let i = 0; i < children.length; i++) {
|
||||||
|
const c = children[i]
|
||||||
|
if (isDef(c) && (isDef(c.componentOptions) || c.isAsyncPlaceholder)) {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function pruneCache (keepAliveInstance, filter) {
|
||||||
|
const { cache, keys, _vnode } = keepAliveInstance
|
||||||
|
for (const key in cache) {
|
||||||
|
const cachedNode = cache[key]
|
||||||
|
if (cachedNode) {
|
||||||
|
const name = getComponentName(cachedNode.componentOptions)
|
||||||
|
const componentKey = getComponentKey(cachedNode)
|
||||||
|
if (name && !filter(name, componentKey)) {
|
||||||
|
pruneCacheEntry(cache, key, keys, _vnode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function pruneCacheEntry2(cache, key, keys) {
|
||||||
|
const cached = cache[key]
|
||||||
|
if (cached) {
|
||||||
|
cached.componentInstance.$destroy()
|
||||||
|
}
|
||||||
|
cache[key] = null
|
||||||
|
remove(keys, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
function pruneCacheEntry (cache, key, keys, current) {
|
||||||
|
const cached = cache[key]
|
||||||
|
if (cached && (!current || cached.tag !== current.tag)) {
|
||||||
|
cached.componentInstance.$destroy()
|
||||||
|
}
|
||||||
|
cache[key] = null
|
||||||
|
remove(keys, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AKeepAlive',
|
||||||
|
abstract: true,
|
||||||
|
model: {
|
||||||
|
prop: 'clearCaches',
|
||||||
|
event: 'clear',
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
include: patternTypes,
|
||||||
|
exclude: patternTypes,
|
||||||
|
excludeKeys: patternTypes,
|
||||||
|
max: [String, Number],
|
||||||
|
clearCaches: Array
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
clearCaches: function(val) {
|
||||||
|
if (val && val.length > 0) {
|
||||||
|
const {cache, keys} = this
|
||||||
|
val.forEach(key => {
|
||||||
|
pruneCacheEntry2(cache, key, keys)
|
||||||
|
})
|
||||||
|
this.$emit('clear', [])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
this.cache = Object.create(null)
|
||||||
|
this.keys = []
|
||||||
|
},
|
||||||
|
|
||||||
|
destroyed () {
|
||||||
|
for (const key in this.cache) {
|
||||||
|
pruneCacheEntry(this.cache, key, this.keys)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted () {
|
||||||
|
this.$watch('include', val => {
|
||||||
|
pruneCache(this, (name) => matches(val, name))
|
||||||
|
})
|
||||||
|
this.$watch('exclude', val => {
|
||||||
|
pruneCache(this, (name) => !matches(val, name))
|
||||||
|
})
|
||||||
|
this.$watch('excludeKeys', val => {
|
||||||
|
pruneCache(this, (name, key) => !matches(val, key))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const slot = this.$slots.default
|
||||||
|
const vnode = getFirstComponentChild(slot)
|
||||||
|
const componentOptions = vnode && vnode.componentOptions
|
||||||
|
if (componentOptions) {
|
||||||
|
// check pattern
|
||||||
|
const name = getComponentName(componentOptions)
|
||||||
|
const componentKey = getComponentKey(vnode)
|
||||||
|
const { include, exclude, excludeKeys } = this
|
||||||
|
if (
|
||||||
|
// not included
|
||||||
|
(include && (!name || !matches(include, name))) ||
|
||||||
|
// excluded
|
||||||
|
(exclude && name && matches(exclude, name)) ||
|
||||||
|
(excludeKeys && componentKey && matches(excludeKeys, componentKey))
|
||||||
|
) {
|
||||||
|
return vnode
|
||||||
|
}
|
||||||
|
|
||||||
|
const { cache, keys } = this
|
||||||
|
const key = vnode.key == null
|
||||||
|
// same constructor may get registered as different local components
|
||||||
|
// so cid alone is not enough (#3269)
|
||||||
|
? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
|
||||||
|
: vnode.key + componentOptions.Ctor.cid
|
||||||
|
if (cache[key]) {
|
||||||
|
vnode.componentInstance = cache[key].componentInstance
|
||||||
|
// make current key freshest
|
||||||
|
remove(keys, key)
|
||||||
|
keys.push(key)
|
||||||
|
} else {
|
||||||
|
cache[key] = vnode
|
||||||
|
keys.push(key)
|
||||||
|
// prune oldest entry
|
||||||
|
if (this.max && keys.length > parseInt(this.max)) {
|
||||||
|
pruneCacheEntry(cache, keys[0], keys, this._vnode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vnode.data.keepAlive = true
|
||||||
|
}
|
||||||
|
return vnode || (slot && slot[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,78 @@
|
|||||||
|
<template>
|
||||||
|
<a-card :loading="loading" :body-style="{padding: '20px 24px 8px'}" :bordered="false">
|
||||||
|
<div class="chart-card-header">
|
||||||
|
<div class="meta">
|
||||||
|
<span class="chart-card-title">{{title}}</span>
|
||||||
|
<span class="chart-card-action">
|
||||||
|
<slot name="action"></slot>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="total"><span>{{total}}</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="chart-card-content">
|
||||||
|
<div class="content-fix">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="chart-card-footer">
|
||||||
|
<slot name="footer"></slot>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'ChartCard',
|
||||||
|
props: ['title', 'total', 'loading']
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.chart-card-header{
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.chart-card-header .meta{
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 100%;
|
||||||
|
color: @text-color-second;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
.chart-card-action{
|
||||||
|
cursor: pointer;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
.total {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
word-break: break-all;
|
||||||
|
white-space: nowrap;
|
||||||
|
margin-top: 4px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-size: 30px;
|
||||||
|
line-height: 38px;
|
||||||
|
height: 38px;
|
||||||
|
}
|
||||||
|
.chart-card-footer{
|
||||||
|
border-top: 1px solid @border-color-base;
|
||||||
|
padding-top: 9px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
.chart-card-content{
|
||||||
|
margin-bottom: 12px;
|
||||||
|
position: relative;
|
||||||
|
height: 46px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.chart-card-content .content-fix{
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
<template>
|
||||||
|
<div class="bar">
|
||||||
|
<h4>{{title}}</h4>
|
||||||
|
<div class="chart">
|
||||||
|
<v-chart :force-fit="true" height="312" :data="data" :padding="[24, 0, 0, 0]">
|
||||||
|
<v-tooltip />
|
||||||
|
<v-axis />
|
||||||
|
<v-bar position="x*y"/>
|
||||||
|
</v-chart>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
const data = []
|
||||||
|
for (let i = 0; i < 12; i += 1) {
|
||||||
|
data.push({
|
||||||
|
x: `${i + 1}月`,
|
||||||
|
y: Math.floor(Math.random() * 1000) + 200
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const tooltip = [
|
||||||
|
'x*y',
|
||||||
|
(x, y) => ({
|
||||||
|
name: x,
|
||||||
|
value: y
|
||||||
|
})
|
||||||
|
]
|
||||||
|
|
||||||
|
const scale = [{
|
||||||
|
dataKey: 'x',
|
||||||
|
min: 2
|
||||||
|
}, {
|
||||||
|
dataKey: 'y',
|
||||||
|
title: '时间',
|
||||||
|
min: 1,
|
||||||
|
max: 22
|
||||||
|
}]
|
||||||
|
export default {
|
||||||
|
name: 'Bar',
|
||||||
|
props: ['title'],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
scale,
|
||||||
|
tooltip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.bar{
|
||||||
|
position: relative;
|
||||||
|
.chart{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,67 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mini-chart">
|
||||||
|
<div class="chart-content" :style="{height: 46}">
|
||||||
|
<v-chart :force-fit="true" :height="height" :data="data" :padding="[36, 5, 18, 5]">
|
||||||
|
<v-tooltip />
|
||||||
|
<v-smooth-area position="x*y" />
|
||||||
|
</v-chart>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {format} from 'date-fns'
|
||||||
|
|
||||||
|
const data = []
|
||||||
|
const beginDay = new Date().getTime()
|
||||||
|
|
||||||
|
const fakeY = [7, 5, 4, 2, 4, 7, 5, 6, 5, 9, 6, 3, 1, 5, 3, 6, 5]
|
||||||
|
for (let i = 0; i < fakeY.length; i += 1) {
|
||||||
|
data.push({
|
||||||
|
x: format(new Date(beginDay + 1000 * 60 * 60 * 24 * i), 'yyyy-MM-dd'),
|
||||||
|
y: fakeY[i]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const tooltip = [
|
||||||
|
'x*y',
|
||||||
|
(x, y) => ({
|
||||||
|
name: x,
|
||||||
|
value: y
|
||||||
|
})
|
||||||
|
]
|
||||||
|
|
||||||
|
const scale = [{
|
||||||
|
dataKey: 'x',
|
||||||
|
min: 2
|
||||||
|
}, {
|
||||||
|
dataKey: 'y',
|
||||||
|
title: '时间',
|
||||||
|
min: 1,
|
||||||
|
max: 22
|
||||||
|
}]
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'MiniArea',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
scale,
|
||||||
|
tooltip,
|
||||||
|
height: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.mini-chart {
|
||||||
|
position: relative;
|
||||||
|
width: 100%
|
||||||
|
}
|
||||||
|
.mini-chart .chart-content{
|
||||||
|
position: absolute;
|
||||||
|
bottom: -28px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mini-chart">
|
||||||
|
<div class="chart-content" :style="{height: 46}">
|
||||||
|
<v-chart :force-fit="true" :height="height" :data="data" :padding="[36, 5, 18, 5]">
|
||||||
|
<v-tooltip />
|
||||||
|
<v-bar position="x*y" />
|
||||||
|
</v-chart>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {format} from 'date-fns'
|
||||||
|
|
||||||
|
const data = []
|
||||||
|
const beginDay = new Date().getTime()
|
||||||
|
|
||||||
|
const fakeY = [7, 5, 4, 2, 4, 7, 5, 6, 5, 9, 6, 3, 1, 5, 3, 6, 5]
|
||||||
|
for (let i = 0; i < fakeY.length; i += 1) {
|
||||||
|
data.push({
|
||||||
|
x: format(new Date(beginDay + 1000 * 60 * 60 * 24 * i), 'yyyy-MM-dd'),
|
||||||
|
y: fakeY[i]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const tooltip = [
|
||||||
|
'x*y',
|
||||||
|
(x, y) => ({
|
||||||
|
name: x,
|
||||||
|
value: y
|
||||||
|
})
|
||||||
|
]
|
||||||
|
|
||||||
|
const scale = [{
|
||||||
|
dataKey: 'x',
|
||||||
|
min: 2
|
||||||
|
}, {
|
||||||
|
dataKey: 'y',
|
||||||
|
title: '时间',
|
||||||
|
min: 1,
|
||||||
|
max: 22
|
||||||
|
}]
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'MiniBar',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
scale,
|
||||||
|
tooltip,
|
||||||
|
height: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@import "index.less";
|
||||||
|
</style>
|
||||||
@ -0,0 +1,56 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mini-progress">
|
||||||
|
<a-tooltip :title="'目标值:' + target + '%'">
|
||||||
|
<div class="target" :style="{left: target + '%'}">
|
||||||
|
<span :style="{backgroundColor: color}" />
|
||||||
|
<span :style="{backgroundColor: color}" />
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="progress" :style="{backgroundColor: color, width: percent + '%', height: height}" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'MiniProgress',
|
||||||
|
props: ['target', 'color', 'percent', 'height']
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.mini-progress {
|
||||||
|
padding: 5px 0;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
.wrap {
|
||||||
|
background-color: @layout-bg-color;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.progress {
|
||||||
|
transition: all 0.4s cubic-bezier(0.08, 0.82, 0.17, 1) 0s;
|
||||||
|
border-radius: 1px 0 0 1px;
|
||||||
|
background-color: #13C2C2;
|
||||||
|
width: 0;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.target {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
span {
|
||||||
|
border-radius: 100px;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 4px;
|
||||||
|
width: 2px;
|
||||||
|
}
|
||||||
|
span:last-child {
|
||||||
|
top: auto;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,80 @@
|
|||||||
|
<template>
|
||||||
|
<v-chart :forceFit="true" height="400" :data="data" :padding="[20, 20, 95, 20]" :scale="scale">
|
||||||
|
<v-tooltip />
|
||||||
|
<v-axis :dataKey="axis1Opts.dataKey" :line="axis1Opts.line" :tickLine="axis1Opts.tickLine" :grid="axis1Opts.grid" />
|
||||||
|
<v-axis :dataKey="axis2Opts.dataKey" :line="axis2Opts.line" :tickLine="axis2Opts.tickLine" :grid="axis2Opts.grid" />
|
||||||
|
<v-legend dataKey="user" marker="circle" :offset="30" />
|
||||||
|
<v-coord type="polar" radius="0.8" />
|
||||||
|
<v-line position="item*score" color="user" :size="2" />
|
||||||
|
<v-point position="item*score" color="user" :size="4" shape="circle" />
|
||||||
|
</v-chart>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const DataSet = require('@antv/data-set')
|
||||||
|
|
||||||
|
const sourceData = [
|
||||||
|
{item: '引用', a: 70, b: 30, c: 40},
|
||||||
|
{item: '口碑', a: 60, b: 70, c: 40},
|
||||||
|
{item: '产量', a: 50, b: 60, c: 40},
|
||||||
|
{item: '贡献', a: 40, b: 50, c: 40},
|
||||||
|
{item: '热度', a: 60, b: 70, c: 40},
|
||||||
|
{item: '引用', a: 70, b: 50, c: 40}
|
||||||
|
]
|
||||||
|
|
||||||
|
const dv = new DataSet.View().source(sourceData)
|
||||||
|
dv.transform({
|
||||||
|
type: 'fold',
|
||||||
|
fields: ['a', 'b', 'c'],
|
||||||
|
key: 'user',
|
||||||
|
value: 'score'
|
||||||
|
})
|
||||||
|
|
||||||
|
const scale = [{
|
||||||
|
dataKey: 'score',
|
||||||
|
min: 0,
|
||||||
|
max: 80
|
||||||
|
}]
|
||||||
|
|
||||||
|
const data = dv.rows
|
||||||
|
|
||||||
|
const axis1Opts = {
|
||||||
|
dataKey: 'item',
|
||||||
|
line: null,
|
||||||
|
tickLine: null,
|
||||||
|
grid: {
|
||||||
|
lineStyle: {
|
||||||
|
lineDash: null
|
||||||
|
},
|
||||||
|
hideFirstLine: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const axis2Opts = {
|
||||||
|
dataKey: 'score',
|
||||||
|
line: null,
|
||||||
|
tickLine: null,
|
||||||
|
grid: {
|
||||||
|
type: 'polygon',
|
||||||
|
lineStyle: {
|
||||||
|
lineDash: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Radar',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
sourceData,
|
||||||
|
data,
|
||||||
|
axis1Opts,
|
||||||
|
axis2Opts,
|
||||||
|
scale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
<template>
|
||||||
|
<div class="rank">
|
||||||
|
<h4 class="title">{{title}}</h4>
|
||||||
|
<ul class="list">
|
||||||
|
<li :key="index" v-for="(item, index) in list">
|
||||||
|
<span :class="index < 3 ? 'active' : null">{{index + 1}}</span>
|
||||||
|
<span >{{item.name}}</span>
|
||||||
|
<span >{{item.total}}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'RankingList',
|
||||||
|
props: ['title', 'list']
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.rank{
|
||||||
|
padding: 0 32px 32px 72px;
|
||||||
|
.title{
|
||||||
|
}
|
||||||
|
.list{
|
||||||
|
margin: 25px 0 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
li {
|
||||||
|
margin-top: 16px;
|
||||||
|
span {
|
||||||
|
color: @text-color-second;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
span:first-child {
|
||||||
|
background-color: @layout-bg-color;
|
||||||
|
border-radius: 20px;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-right: 24px;
|
||||||
|
height: 20px;
|
||||||
|
line-height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
span.active {
|
||||||
|
background-color: #314659 !important;
|
||||||
|
color: @text-color-inverse !important;
|
||||||
|
}
|
||||||
|
span:last-child {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,79 @@
|
|||||||
|
<template>
|
||||||
|
<div class="chart-trend">
|
||||||
|
{{term}}
|
||||||
|
<span>{{rate}}%</span>
|
||||||
|
<span :class="['chart-trend-icon', trend]" style=""><a-icon :type="'caret-' + trend" /></span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Trend',
|
||||||
|
props: {
|
||||||
|
term: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
target: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
isIncrease: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
percent: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
scale: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
trend: this.isIncrease ? 'up' : 'down',
|
||||||
|
rate: this.percent
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.trend = this.caulateTrend()
|
||||||
|
this.rate = this.caulateRate()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
caulateRate () {
|
||||||
|
return (this.percent === null ? Math.abs(this.value - this.target) * 100 / this.target : this.percent).toFixed(this.scale)
|
||||||
|
},
|
||||||
|
caulateTrend () {
|
||||||
|
let isIncrease = this.isIncrease === null ? this.value >= this.target : this.isIncrease
|
||||||
|
return isIncrease ? 'up' : 'down'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.chart-trend{
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 14px;
|
||||||
|
.chart-trend-icon{
|
||||||
|
font-size: 12px;
|
||||||
|
&.up{
|
||||||
|
color: @red-6;
|
||||||
|
}
|
||||||
|
&.down{
|
||||||
|
color: @green-6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
.mini-chart{
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
.chart-content{
|
||||||
|
position: absolute;
|
||||||
|
bottom: -28px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,157 @@
|
|||||||
|
<template>
|
||||||
|
<div class="theme-color" :style="{backgroundColor: color}" @click="toggle">
|
||||||
|
<a-icon v-if="sChecked" type="check" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const Group = {
|
||||||
|
name: 'ColorCheckboxGroup',
|
||||||
|
props: {
|
||||||
|
defaultValues: {
|
||||||
|
type: Array,
|
||||||
|
required: false,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
multiple: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
values: [],
|
||||||
|
options: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
colors () {
|
||||||
|
let colors = []
|
||||||
|
this.options.forEach(item => {
|
||||||
|
if (item.sChecked) {
|
||||||
|
colors.push(item.color)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return colors
|
||||||
|
}
|
||||||
|
},
|
||||||
|
provide () {
|
||||||
|
return {
|
||||||
|
groupContext: this
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
values(value) {
|
||||||
|
this.$emit('change', value, this.colors)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleChange (option) {
|
||||||
|
if (!option.checked) {
|
||||||
|
if (this.values.indexOf(option.value) > -1) {
|
||||||
|
this.values = this.values.filter(item => item != option.value)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!this.multiple) {
|
||||||
|
this.values = [option.value]
|
||||||
|
this.options.forEach(item => {
|
||||||
|
if (item.value != option.value) {
|
||||||
|
item.sChecked = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.values.push(option.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
render (h) {
|
||||||
|
const clear = h('div', {attrs: {style: 'clear: both'}})
|
||||||
|
return h(
|
||||||
|
'div',
|
||||||
|
{},
|
||||||
|
[this.$slots.default, clear]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ColorCheckbox',
|
||||||
|
Group: Group,
|
||||||
|
props: {
|
||||||
|
color: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: [String, Number],
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
checked: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
sChecked: this.initChecked()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
},
|
||||||
|
inject: ['groupContext'],
|
||||||
|
watch: {
|
||||||
|
'sChecked': function () {
|
||||||
|
const value = {
|
||||||
|
value: this.value,
|
||||||
|
color: this.color,
|
||||||
|
checked: this.sChecked
|
||||||
|
}
|
||||||
|
this.$emit('change', value)
|
||||||
|
const groupContext = this.groupContext
|
||||||
|
if (groupContext) {
|
||||||
|
groupContext.handleChange(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
const groupContext = this.groupContext
|
||||||
|
if (groupContext) {
|
||||||
|
groupContext.options.push(this)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggle () {
|
||||||
|
if (this.groupContext.multiple || !this.sChecked) {
|
||||||
|
this.sChecked = !this.sChecked
|
||||||
|
}
|
||||||
|
},
|
||||||
|
initChecked() {
|
||||||
|
let groupContext = this.groupContext
|
||||||
|
if (!groupContext) {
|
||||||
|
return this.checked
|
||||||
|
}else if (groupContext.multiple) {
|
||||||
|
return groupContext.defaultValues.indexOf(this.value) > -1
|
||||||
|
} else {
|
||||||
|
return groupContext.defaultValues[0] == this.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.theme-color{
|
||||||
|
float: left;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border-radius: 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-right: 8px;
|
||||||
|
text-align: center;
|
||||||
|
color: @base-bg-color;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
import ColorCheckbox from '@/components/checkbox/ColorCheckbox'
|
||||||
|
import ImgCheckbox from '@/components/checkbox/ImgCheckbox'
|
||||||
|
|
||||||
|
export {
|
||||||
|
ColorCheckbox,
|
||||||
|
ImgCheckbox
|
||||||
|
}
|
||||||
@ -0,0 +1,69 @@
|
|||||||
|
<template>
|
||||||
|
<div class="exception-page">
|
||||||
|
<div class="img">
|
||||||
|
<img :src="config[type].img" />
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<h1>{{config[type].title}}</h1>
|
||||||
|
<div class="desc">{{config[type].desc}}</div>
|
||||||
|
<div class="action">
|
||||||
|
<a-button type="primary" @click="backHome">返回首页</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Config from './typeConfig'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ExceptionPage',
|
||||||
|
props: ['type', 'homeRoute'],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
config: Config
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
backHome() {
|
||||||
|
if (this.homeRoute) {
|
||||||
|
this.$router.push(this.homeRoute)
|
||||||
|
}
|
||||||
|
this.$emit('backHome', this.type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.exception-page{
|
||||||
|
border-radius: 4px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background-color: @base-bg-color;
|
||||||
|
.img{
|
||||||
|
padding-right: 52px;
|
||||||
|
zoom: 1;
|
||||||
|
img{
|
||||||
|
max-width: 430px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.content{
|
||||||
|
h1{
|
||||||
|
color: #434e59;
|
||||||
|
font-size: 72px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 72px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
.desc{
|
||||||
|
color: @text-color-second;
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 28px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
const config = {
|
||||||
|
403: {
|
||||||
|
img: 'https://gw.alipayobjects.com/zos/rmsportal/wZcnGqRDyhPOEYFcZDnb.svg',
|
||||||
|
title: '403',
|
||||||
|
desc: '抱歉,你無權訪問該頁面'
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
img: 'https://gw.alipayobjects.com/zos/rmsportal/KpnpchXsobRgLElEozzI.svg',
|
||||||
|
title: '404',
|
||||||
|
desc: '抱歉,你訪問的頁面不存在或仍在開發中'
|
||||||
|
},
|
||||||
|
500: {
|
||||||
|
img: 'https://gw.alipayobjects.com/zos/rmsportal/RVRUAYdCGeYNBWoKiIwB.svg',
|
||||||
|
title: '500',
|
||||||
|
desc: '抱歉,伺服器出錯了'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default config
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
<template>
|
||||||
|
<a-input
|
||||||
|
:addon-after="addonAfter"
|
||||||
|
:addon-before="addonBefore"
|
||||||
|
:default-value="defaultValue"
|
||||||
|
:disabled="disabled"
|
||||||
|
:id="id"
|
||||||
|
:max-length="maxLength"
|
||||||
|
:prefix="prefix"
|
||||||
|
:size="size"
|
||||||
|
:suffix="suffix || lenSuffix"
|
||||||
|
:type="type"
|
||||||
|
:allow-clear="allowClear"
|
||||||
|
v-model="sValue"
|
||||||
|
:value="value"
|
||||||
|
@change="onChange"
|
||||||
|
@input="onInput"
|
||||||
|
@pressEnter="onPressEnter"
|
||||||
|
@keydown="onKeydown"
|
||||||
|
>
|
||||||
|
<template :slot="slot" v-for="slot in Object.keys($slots)">
|
||||||
|
<slot :name="slot"></slot>
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'IInput',
|
||||||
|
model: {
|
||||||
|
prop: 'value',
|
||||||
|
event: 'change.value'
|
||||||
|
},
|
||||||
|
props: ['addonAfter', 'addonBefore', 'defaultValue', 'disabled', 'id', 'maxLength', 'prefix', 'size', 'suffix', 'type', 'value', 'allowClear'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
sValue: this.value || this.defaultValue || ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
value(val) {
|
||||||
|
this.sValue = val
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
lenSuffix() {
|
||||||
|
return this.maxLength && `${(this.sValue + '').length}/${this.maxLength}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onChange(e) {
|
||||||
|
this.$emit('change', e)
|
||||||
|
this.$emit('change.value', e.target.value)
|
||||||
|
},
|
||||||
|
onInput(e) {
|
||||||
|
this.$emit('input', e)
|
||||||
|
},
|
||||||
|
onPressEnter(e) {
|
||||||
|
this.$emit('pressEnter', e)
|
||||||
|
},
|
||||||
|
onKeydown(e) {
|
||||||
|
this.$emit('keydown', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -0,0 +1,84 @@
|
|||||||
|
<template>
|
||||||
|
<a-menu
|
||||||
|
v-show="visible"
|
||||||
|
class="contextmenu"
|
||||||
|
:style="style"
|
||||||
|
:selectedKeys="selectedKeys"
|
||||||
|
@click="handleClick"
|
||||||
|
>
|
||||||
|
<a-menu-item :key="item.key" v-for="item in itemList">
|
||||||
|
<a-icon v-if="item.icon" :type="item.icon" />
|
||||||
|
<span>{{ item.text }}</span>
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Contextmenu',
|
||||||
|
props: {
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
itemList: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
target: null,
|
||||||
|
meta: null,
|
||||||
|
selectedKeys: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
style () {
|
||||||
|
return {
|
||||||
|
left: this.left + 'px',
|
||||||
|
top: this.top + 'px'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
window.addEventListener('click', this.closeMenu)
|
||||||
|
window.addEventListener('contextmenu', this.setPosition)
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
window.removeEventListener('click', this.closeMenu)
|
||||||
|
window.removeEventListener('contextmenu', this.setPosition)
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
closeMenu () {
|
||||||
|
this.$emit('update:visible', false)
|
||||||
|
},
|
||||||
|
setPosition (e) {
|
||||||
|
this.left = e.clientX
|
||||||
|
this.top = e.clientY
|
||||||
|
this.target = e.target
|
||||||
|
this.meta = e.meta
|
||||||
|
},
|
||||||
|
handleClick ({ key }) {
|
||||||
|
this.$emit('select', key, this.target, this.meta)
|
||||||
|
this.closeMenu()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.contextmenu{
|
||||||
|
position: fixed;
|
||||||
|
z-index: 1000;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: -4px 4px 16px 1px @shadow-color !important;
|
||||||
|
}
|
||||||
|
.ant-menu-item {
|
||||||
|
margin: 0 !important // 菜单项之间的缝隙会影响点击
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,56 @@
|
|||||||
|
<template>
|
||||||
|
<a-layout-sider :theme="sideTheme" :class="['side-menu', 'beauty-scroll', isMobile ? null : 'shadow']" width="200px" :collapsible="collapsible" v-model="collapsed" :trigger="null">
|
||||||
|
<div :class="['logo', theme]">
|
||||||
|
<router-link to="/dashboard/workplace">
|
||||||
|
<img src="@/assets/images/logo.png">
|
||||||
|
<h1>{{systemName}}</h1>
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
<i-menu :theme="theme" :collapsed="collapsed" :options="menuData" @select="onSelect" class="menu"/>
|
||||||
|
</a-layout-sider>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import IMenu from './menu'
|
||||||
|
import {mapState} from 'vuex'
|
||||||
|
export default {
|
||||||
|
name: 'SideMenu',
|
||||||
|
components: {IMenu},
|
||||||
|
props: {
|
||||||
|
collapsible: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
collapsed: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
menuData: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: 'dark'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
sideTheme() {
|
||||||
|
return this.theme == 'light' ? this.theme : 'dark'
|
||||||
|
},
|
||||||
|
...mapState('setting', ['isMobile', 'systemName'])
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onSelect (obj) {
|
||||||
|
this.$emit('menuSelect', obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@import "index";
|
||||||
|
</style>
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
.shadow{
|
||||||
|
box-shadow: 2px 0 6px rgba(0, 21, 41, .35);
|
||||||
|
}
|
||||||
|
.side-menu{
|
||||||
|
min-height: 100vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
z-index: 10;
|
||||||
|
.logo{
|
||||||
|
height: 64px;
|
||||||
|
position: relative;
|
||||||
|
line-height: 64px;
|
||||||
|
padding-left: 24px;
|
||||||
|
-webkit-transition: all .3s;
|
||||||
|
transition: all .3s;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: @layout-trigger-background;
|
||||||
|
&.light{
|
||||||
|
background-color: #fff;
|
||||||
|
h1{
|
||||||
|
color: @primary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h1{
|
||||||
|
color: @menu-dark-highlight-color;
|
||||||
|
font-size: 20px;
|
||||||
|
margin: 0 0 0 12px;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
img{
|
||||||
|
width: 32px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.menu{
|
||||||
|
padding: 16px 0;
|
||||||
|
}
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
<template>
|
||||||
|
<div :class="['page-header', layout, pageWidth]">
|
||||||
|
<div class="page-header-wide">
|
||||||
|
<div class="breadcrumb">
|
||||||
|
<a-breadcrumb>
|
||||||
|
<a-breadcrumb-item :key="index" v-for="(item, index) in breadcrumb">
|
||||||
|
<span>{{item}}</span>
|
||||||
|
</a-breadcrumb-item>
|
||||||
|
</a-breadcrumb>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {mapState} from 'vuex'
|
||||||
|
export default {
|
||||||
|
name: 'PageHeader',
|
||||||
|
props: {
|
||||||
|
title: {
|
||||||
|
type: [String, Boolean],
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
breadcrumb: {
|
||||||
|
type: Array,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
logo: {
|
||||||
|
type: String,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
avatar: {
|
||||||
|
type: String,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState('setting', ['layout', 'showPageTitle', 'pageWidth'])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@import "index";
|
||||||
|
</style>
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
.page-header{
|
||||||
|
background: @base-bg-color;
|
||||||
|
padding: 16px 24px;
|
||||||
|
&.head.fixed{
|
||||||
|
margin: auto;
|
||||||
|
max-width: 1400px;
|
||||||
|
}
|
||||||
|
.page-header-wide{
|
||||||
|
.breadcrumb{
|
||||||
|
margin: 0px 5px;
|
||||||
|
}
|
||||||
|
.detail{
|
||||||
|
display: flex;
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.avatar {
|
||||||
|
margin:0 24px 0 0;
|
||||||
|
}
|
||||||
|
.main{
|
||||||
|
width: 100%;
|
||||||
|
.title{
|
||||||
|
font-size: 20px;
|
||||||
|
color: @title-color;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
.content{
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
color: @text-color-second;
|
||||||
|
}
|
||||||
|
.extra{
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
<template>
|
||||||
|
<div class="result">
|
||||||
|
<div >
|
||||||
|
<a-icon :class="[isSuccess ? 'success' : 'error' ,'icon']" :type="isSuccess ? 'check-circle' : 'close-circle'" />
|
||||||
|
</div>
|
||||||
|
<div class="title" v-if="title">{{title}}</div>
|
||||||
|
<div class="desc" v-if="description">{{description}}</div>
|
||||||
|
<div class="content">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
<div class="action">
|
||||||
|
<slot name="action"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Result',
|
||||||
|
props: ['isSuccess', 'title', 'description']
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.result{
|
||||||
|
text-align: center;
|
||||||
|
width: 72%;
|
||||||
|
margin: 0 auto;
|
||||||
|
.icon{
|
||||||
|
font-size: 72px;
|
||||||
|
line-height: 72px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
.success {
|
||||||
|
color: @success-color;
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
color: @error-color;
|
||||||
|
}
|
||||||
|
.title{
|
||||||
|
font-size: 24px;
|
||||||
|
color: @title-color;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 32px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.desc{
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 22px;
|
||||||
|
color: @text-color-second;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
.content{
|
||||||
|
background-color: @background-color-light;
|
||||||
|
padding: 24px 40px;
|
||||||
|
border-radius: 2px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.action{
|
||||||
|
margin-top: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
<template>
|
||||||
|
<div class="setting-item">
|
||||||
|
<h3 v-if="title" class="title">{{title}}</h3>
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'SettingItem',
|
||||||
|
props: ['title']
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.setting-item{
|
||||||
|
margin-bottom: 24px;
|
||||||
|
.title{
|
||||||
|
font-size: 14px;
|
||||||
|
color: @title-color;
|
||||||
|
line-height: 22px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,117 @@
|
|||||||
|
module.exports = {
|
||||||
|
messages: {
|
||||||
|
CN: {
|
||||||
|
theme: {
|
||||||
|
title: '整体风格设置',
|
||||||
|
light: '亮色菜单风格',
|
||||||
|
dark: '暗色菜单风格',
|
||||||
|
night: '深夜模式',
|
||||||
|
color: '主题色'
|
||||||
|
},
|
||||||
|
navigate: {
|
||||||
|
title: '导航设置',
|
||||||
|
side: '侧边导航',
|
||||||
|
head: '顶部导航',
|
||||||
|
mix: '混合导航',
|
||||||
|
content: {
|
||||||
|
title: '内容区域宽度',
|
||||||
|
fluid: '流式',
|
||||||
|
fixed: '定宽'
|
||||||
|
},
|
||||||
|
fixedHeader: '固定Header',
|
||||||
|
fixedSideBar: '固定侧边栏',
|
||||||
|
},
|
||||||
|
other: {
|
||||||
|
title: '其他设置',
|
||||||
|
weekMode: '色弱模式',
|
||||||
|
multiPages: '多页签模式',
|
||||||
|
hideSetting: '隐藏设置抽屉'
|
||||||
|
},
|
||||||
|
animate: {
|
||||||
|
title: '页面切换动画',
|
||||||
|
disable: '禁用动画',
|
||||||
|
effect: '动画效果',
|
||||||
|
direction: '动画方向'
|
||||||
|
},
|
||||||
|
alert: '拷贝配置后,直接覆盖文件 src/config/config.js 中的全部内容,然后重启即可。(注意:仅会拷贝与默认配置不同的项)',
|
||||||
|
copy: '拷贝配置',
|
||||||
|
save: '保存配置',
|
||||||
|
reset: '重置配置',
|
||||||
|
},
|
||||||
|
TW: {
|
||||||
|
theme: {
|
||||||
|
title: '整體風格設置',
|
||||||
|
light: '亮色菜單風格',
|
||||||
|
dark: '暗色菜單風格',
|
||||||
|
night: '深夜模式',
|
||||||
|
color: '主題色'
|
||||||
|
},
|
||||||
|
navigate: {
|
||||||
|
title: '導航設置',
|
||||||
|
side: '側邊導航',
|
||||||
|
head: '頂部導航',
|
||||||
|
content: {
|
||||||
|
title: '內容區域寬度',
|
||||||
|
fluid: '流式',
|
||||||
|
fixed: '定寬'
|
||||||
|
},
|
||||||
|
fixedHeader: '固定Header',
|
||||||
|
fixedSideBar: '固定側邊欄',
|
||||||
|
},
|
||||||
|
other: {
|
||||||
|
title: '其他設置',
|
||||||
|
weekMode: '色弱模式',
|
||||||
|
multiPages: '多頁簽模式',
|
||||||
|
hideSetting: '隱藏設置抽屜'
|
||||||
|
},
|
||||||
|
animate: {
|
||||||
|
title: '頁面切換動畫',
|
||||||
|
disable: '禁用動畫',
|
||||||
|
effect: '動畫效果',
|
||||||
|
direction: '動畫方向'
|
||||||
|
},
|
||||||
|
alert: '拷貝配置后,直接覆蓋文件 src/config/config.js 中的全部內容,然後重啟即可。(注意:僅會拷貝與默認配置不同的項)',
|
||||||
|
copy: '拷貝配置',
|
||||||
|
save: '保存配置',
|
||||||
|
reset: '重置配置',
|
||||||
|
},
|
||||||
|
US: {
|
||||||
|
theme: {
|
||||||
|
title: 'Page Style Setting',
|
||||||
|
light: 'Light Style',
|
||||||
|
dark: 'Dark Style',
|
||||||
|
night: 'Night Style',
|
||||||
|
color: 'Theme Color'
|
||||||
|
},
|
||||||
|
navigate: {
|
||||||
|
title: 'Navigation Mode',
|
||||||
|
side: 'Side Menu Layout',
|
||||||
|
head: 'Top Menu Layout',
|
||||||
|
mix: 'Mix Menu Layout',
|
||||||
|
content: {
|
||||||
|
title: 'Content Width',
|
||||||
|
fluid: 'Fluid',
|
||||||
|
fixed: 'Fixed'
|
||||||
|
},
|
||||||
|
fixedHeader: 'Fixed Header',
|
||||||
|
fixedSideBar: 'Fixed SideBar',
|
||||||
|
},
|
||||||
|
other: {
|
||||||
|
title: 'Other Setting',
|
||||||
|
weekMode: 'Week Mode',
|
||||||
|
multiPages: 'Multi Pages',
|
||||||
|
hideSetting: 'Hide Setting Drawer'
|
||||||
|
},
|
||||||
|
animate: {
|
||||||
|
title: 'Page Toggle Animation',
|
||||||
|
disable: 'Disable',
|
||||||
|
effect: 'Effect',
|
||||||
|
direction: 'Direction'
|
||||||
|
},
|
||||||
|
alert: 'After copying the configuration code, directly cover all contents in the file src/config/config.js, then restart the server. (Note: only items that are different from the default configuration will be copied)',
|
||||||
|
copy: 'Copy Setting',
|
||||||
|
save: 'Save',
|
||||||
|
reset: 'Reset',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,144 @@
|
|||||||
|
<template>
|
||||||
|
<div class="standard-table">
|
||||||
|
<div class="alert">
|
||||||
|
<a-alert type="info" :show-icon="true" v-if="selectedRows">
|
||||||
|
<div class="message" slot="message">
|
||||||
|
已選擇 <a>{{selectedRows.length}}</a> 項 <a class="clear" @click="onClear">清空</a>
|
||||||
|
<template v-for="(item, index) in needTotalList" >
|
||||||
|
<div v-if="item.needTotal" :key="index">
|
||||||
|
{{item.title}}總計
|
||||||
|
<a>{{item.customRender ? item.customRender(item.total) : item.total}}</a>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</a-alert>
|
||||||
|
</div>
|
||||||
|
<a-table
|
||||||
|
:bordered="bordered"
|
||||||
|
:loading="loading"
|
||||||
|
:columns="columns"
|
||||||
|
:dataSource="dataSource"
|
||||||
|
:rowKey="rowKey"
|
||||||
|
:pagination="pagination"
|
||||||
|
:expandedRowKeys="expandedRowKeys"
|
||||||
|
:expandedRowRender="expandedRowRender"
|
||||||
|
@change="onChange"
|
||||||
|
:rowSelection="selectedRows ? {selectedRowKeys: selectedRowKeys, onChange: updateSelect} : undefined"
|
||||||
|
:scroll="scroll"
|
||||||
|
>
|
||||||
|
<template slot-scope="text, record, index" :slot="slot" v-for="slot in Object.keys($scopedSlots).filter(key => key !== 'expandedRowRender') ">
|
||||||
|
<slot :name="slot" v-bind="{text, record, index}"></slot>
|
||||||
|
</template>
|
||||||
|
<template :slot="slot" v-for="slot in Object.keys($slots)">
|
||||||
|
<slot :name="slot"></slot>
|
||||||
|
</template>
|
||||||
|
<template slot-scope="record, index, indent, expanded" :slot="$scopedSlots.expandedRowRender ? 'expandedRowRender' : ''">
|
||||||
|
<slot v-bind="{record, index, indent, expanded}" :name="$scopedSlots.expandedRowRender ? 'expandedRowRender' : ''"></slot>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'StandardTable',
|
||||||
|
props: {
|
||||||
|
// bordered: Boolean,
|
||||||
|
loading: [Boolean, Object],
|
||||||
|
columns: Array,
|
||||||
|
dataSource: Array,
|
||||||
|
rowKey: {
|
||||||
|
type: [String, Function],
|
||||||
|
default: 'key'
|
||||||
|
},
|
||||||
|
scroll: Object,
|
||||||
|
pagination: [Boolean, Object],
|
||||||
|
selectedRows: Array,
|
||||||
|
expandedRowKeys: Array,
|
||||||
|
expandedRowRender: Function
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
bordered: true,
|
||||||
|
needTotalList: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateSelect (selectedRowKeys, selectedRows) {
|
||||||
|
this.$emit('update:selectedRows', selectedRows)
|
||||||
|
this.$emit('selectedRowChange', selectedRowKeys, selectedRows)
|
||||||
|
},
|
||||||
|
initTotalList (columns) {
|
||||||
|
const totalList = columns.filter(item => item.needTotal)
|
||||||
|
.map(item => {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
total: 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return totalList
|
||||||
|
},
|
||||||
|
onClear() {
|
||||||
|
this.updateSelect([], [])
|
||||||
|
this.$emit('clear')
|
||||||
|
},
|
||||||
|
onChange(pagination, filters, sorter, {currentDataSource}) {
|
||||||
|
this.$emit('change', pagination, filters, sorter, {currentDataSource})
|
||||||
|
},
|
||||||
|
onShowSizeChange(current, size){
|
||||||
|
this.$emit('showSizeChange', current, size)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.needTotalList = this.initTotalList(this.columns)
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
selectedRows (selectedRows) {
|
||||||
|
this.needTotalList = this.needTotalList.map(item => {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
total: selectedRows.reduce((sum, val) => {
|
||||||
|
let v
|
||||||
|
try{
|
||||||
|
v = val[item.dataIndex] ? val[item.dataIndex] : eval(`val.${item.dataIndex}`);
|
||||||
|
}catch(_){
|
||||||
|
v = val[item.dataIndex];
|
||||||
|
}
|
||||||
|
v = !isNaN(parseFloat(v)) ? parseFloat(v) : 0;
|
||||||
|
return sum + v
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
selectedRowKeys() {
|
||||||
|
return this.selectedRows.map(record => {
|
||||||
|
return (typeof this.rowKey === 'function') ? this.rowKey(record) : record[this.rowKey]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.standard-table{
|
||||||
|
.alert{
|
||||||
|
margin-bottom: 16px;
|
||||||
|
.message{
|
||||||
|
a{
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.clear{
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination{
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</style>
|
||||||
@ -0,0 +1,155 @@
|
|||||||
|
<template>
|
||||||
|
<div class="action-columns" ref="root">
|
||||||
|
<a-popover v-model="visible" placement="bottomRight" trigger="click" :get-popup-container="() => $refs.root">
|
||||||
|
<div slot="title">
|
||||||
|
<a-checkbox :indeterminate="indeterminate" :checked="checkAll" @change="onCheckAllChange" class="check-all" />列展示
|
||||||
|
<a-button @click="resetColumns" style="float: right" type="link" size="small">重置</a-button>
|
||||||
|
</div>
|
||||||
|
<a-list style="width: 100%" size="small" :key="i" v-for="(col, i) in columns" slot="content">
|
||||||
|
<a-list-item>
|
||||||
|
<a-checkbox v-model="col.visible" @change="e => onCheckChange(e, col)"/>
|
||||||
|
<template v-if="col.title">
|
||||||
|
{{col.title}}:
|
||||||
|
</template>
|
||||||
|
<slot v-else-if="col.slots && col.slots.title" :name="col.slots.title"></slot>
|
||||||
|
<template slot="actions">
|
||||||
|
<a-tooltip title="固定在列头" :mouseEnterDelay="0.5" :get-popup-container="() => $refs.root">
|
||||||
|
<a-icon :class="['left', {active: col.fixed === 'left'}]" @click="fixColumn('left', col)" type="vertical-align-top" />
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip title="固定在列尾" :mouseEnterDelay="0.5" :get-popup-container="() => $refs.root">
|
||||||
|
<a-icon :class="['right', {active: col.fixed === 'right'}]" @click="fixColumn('right', col)" type="vertical-align-bottom" />
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip title="添加搜索" :mouseEnterDelay="0.5" :get-popup-container="() => $refs.root">
|
||||||
|
<a-icon :class="{active: col.searchAble}" @click="setSearch(col)" type="search" />
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
</a-list-item>
|
||||||
|
</a-list>
|
||||||
|
<a-icon class="action" type="setting" />
|
||||||
|
</a-popover>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import cloneDeep from 'lodash.clonedeep'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ActionColumns',
|
||||||
|
props: ['columns', 'visibleColumns'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
indeterminate: false,
|
||||||
|
checkAll: true,
|
||||||
|
checkedCounts: this.columns.length,
|
||||||
|
backColumns: cloneDeep(this.columns)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
checkedCounts(val) {
|
||||||
|
this.checkAll = val === this.columns.length
|
||||||
|
this.indeterminate = val > 0 && val < this.columns.length
|
||||||
|
},
|
||||||
|
columns(newVal, oldVal) {
|
||||||
|
if (newVal != oldVal) {
|
||||||
|
this.checkedCounts = newVal.length
|
||||||
|
this.formatColumns(newVal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.formatColumns(this.columns)
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onCheckChange(e, col) {
|
||||||
|
if (!col.visible) {
|
||||||
|
this.checkedCounts -= 1
|
||||||
|
} else {
|
||||||
|
this.checkedCounts += 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fixColumn(fixed, col) {
|
||||||
|
if (fixed !== col.fixed) {
|
||||||
|
this.$set(col, 'fixed', fixed)
|
||||||
|
} else {
|
||||||
|
this.$set(col, 'fixed', undefined)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setSearch(col) {
|
||||||
|
this.$set(col, 'searchAble', !col.searchAble)
|
||||||
|
if (!col.searchAble && col.search) {
|
||||||
|
this.resetSearch(col)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resetSearch(col) {
|
||||||
|
// col.search.value = col.dataType === 'boolean' ? false : undefined
|
||||||
|
col.search.value = undefined
|
||||||
|
col.search.backup = undefined
|
||||||
|
},
|
||||||
|
resetColumns() {
|
||||||
|
const {columns, backColumns} = this
|
||||||
|
let counts = columns.length
|
||||||
|
backColumns.forEach((back, index) => {
|
||||||
|
const column = columns[index]
|
||||||
|
column.visible = back.visible === undefined || back.visible
|
||||||
|
if (!column.visible) {
|
||||||
|
counts -= 1
|
||||||
|
}
|
||||||
|
if (back.fixed !== undefined) {
|
||||||
|
column.fixed = back.fixed
|
||||||
|
} else {
|
||||||
|
this.$set(column, 'fixed', undefined)
|
||||||
|
}
|
||||||
|
this.$set(column, 'searchAble', back.searchAble)
|
||||||
|
// column.searchAble = back.searchAble
|
||||||
|
this.resetSearch(column)
|
||||||
|
})
|
||||||
|
this.checkedCounts = counts
|
||||||
|
this.visible = false
|
||||||
|
this.$emit('reset', this.getConditions(columns))
|
||||||
|
},
|
||||||
|
onCheckAllChange(e) {
|
||||||
|
if (e.target.checked) {
|
||||||
|
this.checkedCounts = this.columns.length
|
||||||
|
this.columns.forEach(col => col.visible = true)
|
||||||
|
} else {
|
||||||
|
this.checkedCounts = 0
|
||||||
|
this.columns.forEach(col => col.visible = false)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getConditions(columns) {
|
||||||
|
const conditions = {}
|
||||||
|
columns.filter(item => item.search.value !== undefined && item.search.value !== '' && item.search.value !== null)
|
||||||
|
.forEach(col => {
|
||||||
|
conditions[col.dataIndex] = col.search.value
|
||||||
|
})
|
||||||
|
return conditions
|
||||||
|
},
|
||||||
|
formatColumns(columns) {
|
||||||
|
for (let col of columns) {
|
||||||
|
if (col.visible === undefined) {
|
||||||
|
this.$set(col, 'visible', true)
|
||||||
|
}
|
||||||
|
if (!col.visible) {
|
||||||
|
this.checkedCounts -= 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.action-columns{
|
||||||
|
display: inline-block;
|
||||||
|
.check-all{
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
.left,.right{
|
||||||
|
transform: rotate(-90deg);
|
||||||
|
}
|
||||||
|
.active{
|
||||||
|
color: @primary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
<template>
|
||||||
|
<div class="action-size" ref="root">
|
||||||
|
<a-tooltip title="密度">
|
||||||
|
<a-dropdown placement="bottomCenter" :trigger="['click']" :get-popup-container="() => $refs.root">
|
||||||
|
<a-icon class="action" type="column-height" />
|
||||||
|
<a-menu :selected-keys="[value]" slot="overlay" @click="onClick">
|
||||||
|
<a-menu-item key="default">
|
||||||
|
默认
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="middle">
|
||||||
|
中等
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="small">
|
||||||
|
紧密
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</a-dropdown>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'ActionSize',
|
||||||
|
props: ['value'],
|
||||||
|
inject: ['table'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
selectedKeys: ['middle']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onClick({key}) {
|
||||||
|
this.$emit('input', key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.action-size{
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,249 @@
|
|||||||
|
<template>
|
||||||
|
<div ref="table" :id="id" class="advanced-table">
|
||||||
|
<a-spin :spinning="loading">
|
||||||
|
<div :class="['header-bar', size]">
|
||||||
|
<div class="title">
|
||||||
|
<template v-if="title">{{title}}</template>
|
||||||
|
<slot v-else-if="$slots.title" name="title"></slot>
|
||||||
|
<template v-else>高级表格</template>
|
||||||
|
</div>
|
||||||
|
<div class="search">
|
||||||
|
<search-area :format-conditions="formatConditions" @change="onSearchChange" :columns="columns" >
|
||||||
|
<template :slot="slot" v-for="slot in slots">
|
||||||
|
<slot :name="slot"></slot>
|
||||||
|
</template>
|
||||||
|
</search-area>
|
||||||
|
</div>
|
||||||
|
<div class="actions">
|
||||||
|
<a-tooltip title="刷新">
|
||||||
|
<a-icon @click="refresh" class="action" :type="loading ? 'loading' : 'reload'" />
|
||||||
|
</a-tooltip>
|
||||||
|
<action-size v-model="sSize" class="action" />
|
||||||
|
<a-tooltip title="列配置">
|
||||||
|
<action-columns :columns="columns" @reset="onColumnsReset" class="action">
|
||||||
|
<template :slot="slot" v-for="slot in slots">
|
||||||
|
<slot :name="slot"></slot>
|
||||||
|
</template>
|
||||||
|
</action-columns>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip title="全屏">
|
||||||
|
<a-icon @click="toggleScreen" class="action" :type="fullScreen ? 'fullscreen-exit' : 'fullscreen'" />
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a-table
|
||||||
|
v-bind="{...$props, columns: visibleColumns, title: undefined, loading: false}"
|
||||||
|
:size="sSize"
|
||||||
|
@expandedRowsChange="onExpandedRowsChange"
|
||||||
|
@change="onChange"
|
||||||
|
@expand="onExpand"
|
||||||
|
>
|
||||||
|
<template slot-scope="text, record, index" :slot="slot" v-for="slot in scopedSlots ">
|
||||||
|
<slot :name="slot" v-bind="{text, record, index}"></slot>
|
||||||
|
</template>
|
||||||
|
<template :slot="slot" v-for="slot in slots">
|
||||||
|
<slot :name="slot"></slot>
|
||||||
|
</template>
|
||||||
|
<template slot-scope="record, index, indent, expanded" :slot="$scopedSlots.expandedRowRender ? 'expandedRowRender' : ''">
|
||||||
|
<slot v-bind="{record, index, indent, expanded}" :name="$scopedSlots.expandedRowRender ? 'expandedRowRender' : ''"></slot>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-spin>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ActionSize from '@/components/table/advance/ActionSize'
|
||||||
|
import ActionColumns from '@/components/table/advance/ActionColumns'
|
||||||
|
import SearchArea from '@/components/table/advance/SearchArea'
|
||||||
|
export default {
|
||||||
|
name: 'AdvanceTable',
|
||||||
|
components: {SearchArea, ActionColumns, ActionSize},
|
||||||
|
props: {
|
||||||
|
tableLayout: String,
|
||||||
|
bordered: Boolean,
|
||||||
|
childrenColumnName: {type: String, default: 'children'},
|
||||||
|
columns: Array,
|
||||||
|
components: Object,
|
||||||
|
dataSource: Array,
|
||||||
|
defaultExpandAllRows: Array[String],
|
||||||
|
expandedRowKeys: Array[String],
|
||||||
|
expandedRowRender: Function,
|
||||||
|
expandIcon: Function,
|
||||||
|
expandRowByClick: Boolean,
|
||||||
|
expandIconColumnIndex: Number,
|
||||||
|
footer: Function,
|
||||||
|
indentSize: Number,
|
||||||
|
loading: Boolean,
|
||||||
|
locale: Object,
|
||||||
|
pagination: [Object, Boolean],
|
||||||
|
rowClassName: Function,
|
||||||
|
rowKey: [String, Function],
|
||||||
|
rowSelection: Object,
|
||||||
|
scroll: Object,
|
||||||
|
showHeader: {type: Boolean, default: true},
|
||||||
|
size: String,
|
||||||
|
title: String,
|
||||||
|
customHeaderRow: Function,
|
||||||
|
customRow: Function,
|
||||||
|
getPopupContainer: Function,
|
||||||
|
transformCellText: Function,
|
||||||
|
formatConditions: Boolean
|
||||||
|
},
|
||||||
|
provide() {
|
||||||
|
return {
|
||||||
|
table: this
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
id: `${new Date().getTime()}-${Math.floor(Math.random() * 10)}`,
|
||||||
|
sSize: this.size || 'default',
|
||||||
|
fullScreen: false,
|
||||||
|
conditions: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
slots() {
|
||||||
|
return Object.keys(this.$slots).filter(slot => slot !== 'title')
|
||||||
|
},
|
||||||
|
scopedSlots() {
|
||||||
|
return Object.keys(this.$scopedSlots).filter(slot => slot !== 'expandedRowRender' && slot !== 'title')
|
||||||
|
},
|
||||||
|
visibleColumns(){
|
||||||
|
return this.columns.filter(col => col.visible)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.addListener()
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.removeListener()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
refresh() {
|
||||||
|
this.$emit('refresh', this.conditions)
|
||||||
|
},
|
||||||
|
onSearchChange(conditions, searchOptions) {
|
||||||
|
this.conditions = conditions
|
||||||
|
this.$emit('search', conditions, searchOptions)
|
||||||
|
},
|
||||||
|
toggleScreen() {
|
||||||
|
if (this.fullScreen) {
|
||||||
|
this.outFullScreen()
|
||||||
|
} else {
|
||||||
|
this.inFullScreen()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inFullScreen() {
|
||||||
|
const el = this.$refs.table
|
||||||
|
el.classList.add('beauty-scroll')
|
||||||
|
if (el.requestFullscreen) {
|
||||||
|
el.requestFullscreen()
|
||||||
|
return true
|
||||||
|
} else if (el.webkitRequestFullScreen) {
|
||||||
|
el.webkitRequestFullScreen()
|
||||||
|
return true
|
||||||
|
} else if (el.mozRequestFullScreen) {
|
||||||
|
el.mozRequestFullScreen()
|
||||||
|
return true
|
||||||
|
} else if (el.msRequestFullscreen) {
|
||||||
|
el.msRequestFullscreen()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
this.$message.warn('对不起,您的浏览器不支持全屏模式')
|
||||||
|
el.classList.remove('beauty-scroll')
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
outFullScreen() {
|
||||||
|
if (document.exitFullscreen) {
|
||||||
|
document.exitFullscreen()
|
||||||
|
} else if (document.webkitCancelFullScreen) {
|
||||||
|
document.webkitCancelFullScreen();
|
||||||
|
} else if (document.mozCancelFullScreen) {
|
||||||
|
document.mozCancelFullScreen()
|
||||||
|
} else if (document.msExitFullscreen) {
|
||||||
|
document.msExitFullscreen()
|
||||||
|
}
|
||||||
|
this.$refs.table.classList.remove('beauty-scroll')
|
||||||
|
},
|
||||||
|
onColumnsReset(conditions) {
|
||||||
|
this.$emit('reset', conditions)
|
||||||
|
},
|
||||||
|
onExpandedRowsChange(expandedRows) {
|
||||||
|
this.$emit('expandedRowsChange', expandedRows)
|
||||||
|
},
|
||||||
|
onChange(pagination, filters, sorter, options) {
|
||||||
|
this.$emit('change', pagination, filters, sorter, options)
|
||||||
|
},
|
||||||
|
onExpand(expanded, record) {
|
||||||
|
this.$emit('expand', expanded, record)
|
||||||
|
},
|
||||||
|
addListener() {
|
||||||
|
document.addEventListener('fullscreenchange', this.fullScreenListener)
|
||||||
|
document.addEventListener('webkitfullscreenchange', this.fullScreenListener)
|
||||||
|
document.addEventListener('mozfullscreenchange', this.fullScreenListener)
|
||||||
|
document.addEventListener('msfullscreenchange', this.fullScreenListener)
|
||||||
|
},
|
||||||
|
removeListener() {
|
||||||
|
document.removeEventListener('fullscreenchange', this.fullScreenListener)
|
||||||
|
document.removeEventListener('webkitfullscreenchange', this.fullScreenListener)
|
||||||
|
document.removeEventListener('mozfullscreenchange', this.fullScreenListener)
|
||||||
|
document.removeEventListener('msfullscreenchange', this.fullScreenListener)
|
||||||
|
},
|
||||||
|
fullScreenListener(e) {
|
||||||
|
if (e.target.id === this.id) {
|
||||||
|
this.fullScreen = !this.fullScreen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.advanced-table{
|
||||||
|
overflow-y: auto;
|
||||||
|
background-color: @component-background;
|
||||||
|
.header-bar{
|
||||||
|
padding: 16px 24px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: all 0.3s;
|
||||||
|
&.middle{
|
||||||
|
padding: 12px 16px;
|
||||||
|
}
|
||||||
|
&.small{
|
||||||
|
padding: 8px 12px;
|
||||||
|
border: 1px solid @border-color;
|
||||||
|
border-bottom: 0;
|
||||||
|
.title{
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.title{
|
||||||
|
transition: all 0.3s;
|
||||||
|
font-size: 18px;
|
||||||
|
color: @title-color;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
.search{
|
||||||
|
flex: 1;
|
||||||
|
text-align: right;
|
||||||
|
margin: 0 24px;
|
||||||
|
}
|
||||||
|
.actions{
|
||||||
|
text-align: right;
|
||||||
|
font-size: 17px;
|
||||||
|
color: @text-color;
|
||||||
|
.action{
|
||||||
|
margin: 0 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover{
|
||||||
|
color: @primary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,313 @@
|
|||||||
|
<template>
|
||||||
|
<div class="search-area" ref="root">
|
||||||
|
<div class="select-root" ref="selectRoot"></div>
|
||||||
|
<div class="search-item" :key="index" v-for="(col, index) in searchCols">
|
||||||
|
<div v-if="col.dataType === 'boolean'" :class="['title', {active: col.search.value !== undefined}]">
|
||||||
|
<template v-if="col.title">
|
||||||
|
{{col.title}}:
|
||||||
|
</template>
|
||||||
|
<slot v-else-if="col.slots && col.slots.title" :name="col.slots.title"></slot>
|
||||||
|
<a-switch @change="onSwitchChange(col)" class="switch" v-model="col.search.value" size="small"
|
||||||
|
:checked-children="(col.search.switchOptions && col.search.switchOptions.checkedText) || '是'"
|
||||||
|
:un-checked-children="(col.search.switchOptions && col.search.switchOptions.uncheckedText) || '否'"
|
||||||
|
/>
|
||||||
|
<a-icon v-if="col.search.value !== undefined" class="close" @click="e => onCloseClick(e, col)" type="close-circle" theme="filled" />
|
||||||
|
</div>
|
||||||
|
<div v-else-if="col.dataType === 'time'" :class="['title', {active: col.search.value}]">
|
||||||
|
<template v-if="col.title">
|
||||||
|
{{col.title}}:
|
||||||
|
</template>
|
||||||
|
<slot v-else-if="col.slots && col.slots.title" :name="col.slots.title"></slot>
|
||||||
|
<a-time-picker :format="col.search.format" v-model="col.search.value" placeholder="选择时间" @change="(time, timeStr) => onCalendarChange(time, timeStr, col)" @openChange="open => onCalendarOpenChange(open, col)" class="time-picker" size="small" :get-popup-container="() => $refs.root"/>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="col.dataType === 'date'" :class="['title', {active: col.search.value}]">
|
||||||
|
<template v-if="col.title">
|
||||||
|
{{col.title}}:
|
||||||
|
</template>
|
||||||
|
<slot v-else-if="col.slots && col.slots.title" :name="col.slots.title"></slot>
|
||||||
|
<a-date-picker :format="col.search.format" v-model="col.search.value" @change="onDateChange(col)" class="date-picker" size="small" :getCalendarContainer="() => $refs.root"/>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="col.dataType === 'datetime'" class="title datetime active">
|
||||||
|
<template v-if="col.title">
|
||||||
|
{{col.title}}:
|
||||||
|
</template>
|
||||||
|
<slot v-else-if="col.slots && col.slots.title" :name="col.slots.title"></slot>
|
||||||
|
<a-date-picker :format="col.search.format" v-model="col.search.value" @change="(date, dateStr) => onCalendarChange(date, dateStr, col)" @openChange="open => onCalendarOpenChange(open, col)" class="datetime-picker" size="small" show-time :getCalendarContainer="() => $refs.root"/>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="col.dataType === 'select'" :class="['title', {active: col.search.value !== undefined}]">
|
||||||
|
<template v-if="col.title">
|
||||||
|
{{col.title}}:
|
||||||
|
</template>
|
||||||
|
<slot v-else-if="col.slots && col.slots.title" :name="col.slots.title"></slot>
|
||||||
|
<a-select :allowClear="true" :options="col.search.selectOptions" v-model="col.search.value" placeholder="请选择..." @change="onSelectChange(col)" class="select" slot="content" size="small" :get-popup-container="() => $refs.selectRoot">
|
||||||
|
</a-select>
|
||||||
|
</div>
|
||||||
|
<div v-else :class="['title', {active: col.search.value}]">
|
||||||
|
<a-popover @visibleChange="onVisibleChange(col, index)" v-model="col.search.visible" placement="bottom" :trigger="['click']" :get-popup-container="() => $refs.root">
|
||||||
|
<template v-if="col.title">
|
||||||
|
{{col.title}}
|
||||||
|
</template>
|
||||||
|
<slot v-else-if="col.slots && col.slots.title" :name="col.slots.title"></slot>
|
||||||
|
<div class="value " v-if="col.search.value">: {{col.search.format && typeof col.search.format === 'function' ? col.search.format(col.search.value) : col.search.value}}</div>
|
||||||
|
<a-icon v-if="!col.search.value" class="icon-down" type="down"/>
|
||||||
|
<div class="operations" slot="content">
|
||||||
|
<a-button @click="onCancel(col)" class="btn" size="small" type="link">取消</a-button>
|
||||||
|
<a-button @click="onConfirm(col)" class="btn" size="small" type="primary">确认</a-button>
|
||||||
|
</div>
|
||||||
|
<div class="search-overlay" slot="title">
|
||||||
|
<a-input :id="`${searchIdPrefix}${index}`" :allow-clear="true" @keyup.esc="onCancel(col)" @keyup.enter="onConfirm(col)" v-model="col.search.value" size="small" />
|
||||||
|
</div>
|
||||||
|
</a-popover>
|
||||||
|
<a-icon v-if="col.search.value" @click="e => onCloseClick(e, col)" class="close" type="close-circle" theme="filled"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import fastEqual from 'fast-deep-equal'
|
||||||
|
import moment from 'moment'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'SearchArea',
|
||||||
|
props: ['columns', 'formatConditions'],
|
||||||
|
inject: ['table'],
|
||||||
|
created() {
|
||||||
|
this.formatColumns(this.columns)
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
columns(newVal, oldVal) {
|
||||||
|
if (newVal != oldVal) {
|
||||||
|
this.formatColumns(newVal)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
searchCols(newVal, oldVal) {
|
||||||
|
if (newVal.length != oldVal.length) {
|
||||||
|
const newConditions = this.getConditions(newVal)
|
||||||
|
const newSearchOptions = this.getSearchOptions(newVal)
|
||||||
|
if (!fastEqual(newConditions, this.conditions)) {
|
||||||
|
this.conditions = newConditions
|
||||||
|
this.searchOptions = newSearchOptions
|
||||||
|
this.$emit('change', this.conditions, this.searchOptions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
conditions: {},
|
||||||
|
searchOptions: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
searchCols() {
|
||||||
|
return this.columns.filter(item => item.searchAble)
|
||||||
|
},
|
||||||
|
searchIdPrefix() {
|
||||||
|
return this.table.id + '-ipt-'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onCloseClick(e, col) {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
col.search.value = undefined
|
||||||
|
const {backup, value} = col.search
|
||||||
|
if (backup !== value) {
|
||||||
|
this.backupAndEmitChange(col)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onCancel(col) {
|
||||||
|
col.search.value = col.search.backup
|
||||||
|
col.search.visible = false
|
||||||
|
},
|
||||||
|
onConfirm(col) {
|
||||||
|
const {backup, value} = col.search
|
||||||
|
col.search.visible = false
|
||||||
|
if (backup !== value) {
|
||||||
|
this.backupAndEmitChange(col)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSwitchChange(col) {
|
||||||
|
const {backup, value} = col.search
|
||||||
|
if (backup !== value) {
|
||||||
|
this.backupAndEmitChange(col)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSelectChange(col) {
|
||||||
|
this.backupAndEmitChange(col)
|
||||||
|
},
|
||||||
|
onCalendarOpenChange(open, col) {
|
||||||
|
col.search.visible = open
|
||||||
|
const {momentEqual, backupAndEmitChange} = this
|
||||||
|
const {value, backup, format} = col.search
|
||||||
|
if (!open && !momentEqual(value, backup, format)) {
|
||||||
|
backupAndEmitChange(col, moment(value))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onCalendarChange(date, dateStr, col) {
|
||||||
|
const {momentEqual, backupAndEmitChange} = this
|
||||||
|
const {value, backup, format} = col.search
|
||||||
|
if (!col.search.visible && !momentEqual(value, backup, format)) {
|
||||||
|
backupAndEmitChange(col, moment(value))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDateChange(col) {
|
||||||
|
const {momentEqual, backupAndEmitChange} = this
|
||||||
|
const {value, backup, format} = col.search
|
||||||
|
if (!momentEqual(value, backup, format)) {
|
||||||
|
backupAndEmitChange(col, moment(value))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getFormat(col) {
|
||||||
|
if (col.search && col.search.format) {
|
||||||
|
return col.search.format
|
||||||
|
}
|
||||||
|
const dataType = col.dataType
|
||||||
|
switch(dataType) {
|
||||||
|
case 'time': return 'HH:mm:ss'
|
||||||
|
case 'date': return 'YYYY-MM-DD'
|
||||||
|
case 'datetime': return 'YYYY-MM-DD HH:mm:ss'
|
||||||
|
default: return undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
backupAndEmitChange(col, backValue = col.search.value) {
|
||||||
|
const {getConditions, getSearchOptions} = this
|
||||||
|
col.search.backup = backValue
|
||||||
|
this.conditions = getConditions(this.searchCols)
|
||||||
|
this.searchOptions = getSearchOptions(this.searchCols)
|
||||||
|
this.$emit('change', this.conditions, this.searchOptions)
|
||||||
|
},
|
||||||
|
getConditions(columns) {
|
||||||
|
const conditions = {}
|
||||||
|
columns.filter(item => item.search.value !== undefined && item.search.value !== '' && item.search.value !== null)
|
||||||
|
.forEach(col => {
|
||||||
|
const {value, format} = col.search
|
||||||
|
if (this.formatConditions && format) {
|
||||||
|
if (typeof format === 'function') {
|
||||||
|
conditions[col.dataIndex] = format(col.search.value)
|
||||||
|
} else if (typeof format === 'string' && value.constructor.name === 'Moment') {
|
||||||
|
conditions[col.dataIndex] = value.format(format)
|
||||||
|
} else {
|
||||||
|
conditions[col.dataIndex] = value
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
conditions[col.dataIndex] = value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return conditions
|
||||||
|
},
|
||||||
|
getSearchOptions(columns) {
|
||||||
|
return columns.filter(item => item.search.value !== undefined && item.search.value !== '' && item.search.value !== null)
|
||||||
|
.map(({dataIndex, search}) => ({field: dataIndex, value: search.value, format: search.format}))
|
||||||
|
},
|
||||||
|
onVisibleChange(col, index) {
|
||||||
|
if (!col.search.visible) {
|
||||||
|
col.search.value = col.search.backup
|
||||||
|
} else {
|
||||||
|
let input = document.getElementById(`${this.searchIdPrefix}${index}`)
|
||||||
|
if (input) {
|
||||||
|
setTimeout(() => {input.focus()}, 0)
|
||||||
|
} else {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
input = document.getElementById(`${this.searchIdPrefix}${index}`)
|
||||||
|
input.focus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
momentEqual(target, source, format) {
|
||||||
|
if (target === source) {
|
||||||
|
return true
|
||||||
|
} else if (target && source && target.format(format) === source.format(format)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
formatColumns(columns) {
|
||||||
|
columns.forEach(item => {
|
||||||
|
this.$set(item, 'search', {...item.search, visible: false, value: undefined, format: this.getFormat(item)})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.search-area{
|
||||||
|
.select-root{
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
margin: -4px 0;
|
||||||
|
.search-item{
|
||||||
|
margin: 4px 4px;
|
||||||
|
display: inline-block;
|
||||||
|
.title{
|
||||||
|
padding: 4px 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 4px;
|
||||||
|
user-select: none;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
.close{
|
||||||
|
color: @text-color-second;
|
||||||
|
margin-left: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
vertical-align: middle;
|
||||||
|
:hover{
|
||||||
|
color: @text-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.switch{
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
.time-picker{
|
||||||
|
margin-left: 4px;
|
||||||
|
width: 96px;
|
||||||
|
}
|
||||||
|
.date-picker{
|
||||||
|
margin-left: 4px;
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
.datetime-picker{
|
||||||
|
margin-left: 4px;
|
||||||
|
width: 195px;
|
||||||
|
}
|
||||||
|
.value{
|
||||||
|
display: inline-block;
|
||||||
|
overflow: hidden;
|
||||||
|
flex:1;
|
||||||
|
vertical-align: middle;
|
||||||
|
max-width: 144px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
word-break: break-all;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
&.active{
|
||||||
|
background-color: @layout-bg-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.icon-down{
|
||||||
|
vertical-align: middle;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.search-overlay{
|
||||||
|
padding: 8px 0px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.select{
|
||||||
|
margin-left: 4px;
|
||||||
|
max-width: 144px;
|
||||||
|
min-width: 96px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.operations{
|
||||||
|
display: flex;
|
||||||
|
margin: -6px 0;
|
||||||
|
justify-content: space-between;
|
||||||
|
.btn{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
import AdvanceTable from './AdvanceTable'
|
||||||
|
export default AdvanceTable
|
||||||
@ -0,0 +1,79 @@
|
|||||||
|
<template>
|
||||||
|
<div class="task-group">
|
||||||
|
<div class="task-head">
|
||||||
|
<h3 class="title"><span v-if="count">{{count}}</span>{{title}}</h3>
|
||||||
|
<div class="actions" style="float: right">
|
||||||
|
<a-icon class="add" type="plus" draggable="true"/>
|
||||||
|
<a-icon class="more" style="margin-left: 8px" type="ellipsis" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="task-content">
|
||||||
|
<draggable :options="dragOptions">
|
||||||
|
<slot></slot>
|
||||||
|
</draggable>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Draggable from 'vuedraggable'
|
||||||
|
|
||||||
|
const dragOptions = {
|
||||||
|
sort: true,
|
||||||
|
scroll: true,
|
||||||
|
scrollSpeed: 2,
|
||||||
|
animation: 150,
|
||||||
|
ghostClass: 'dragable-ghost',
|
||||||
|
chosenClass: 'dragable-chose',
|
||||||
|
dragClass: 'dragable-drag'
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'TaskGroup',
|
||||||
|
components: {Draggable},
|
||||||
|
props: ['title', 'group'],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
dragOptions: {...dragOptions, group: this.group}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
count () {
|
||||||
|
return this.$slots.default.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.task-group{
|
||||||
|
width: 33.33%;
|
||||||
|
padding: 8px 8px;
|
||||||
|
background-color: @background-color-light;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid @shadow-color;
|
||||||
|
.task-head{
|
||||||
|
margin-bottom: 8px;
|
||||||
|
.title{
|
||||||
|
display: inline-block;
|
||||||
|
span{
|
||||||
|
display: inline-block;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin: 0 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 2px 6px;
|
||||||
|
background-color: @base-bg-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.actions{
|
||||||
|
display: inline-block;
|
||||||
|
float: right;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
i{
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
<template>
|
||||||
|
<a-card class="task-item" type="inner">
|
||||||
|
{{content}}
|
||||||
|
</a-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'TaskItem',
|
||||||
|
props: ['content']
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.task-item{
|
||||||
|
margin-bottom: 16px;
|
||||||
|
box-shadow: 0 1px 1px @shadow-color;
|
||||||
|
border-radius: 6px;
|
||||||
|
& :hover{
|
||||||
|
cursor: move;
|
||||||
|
box-shadow: 0 1px 2px @shadow-color;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="['step-item', link ? 'linkable' : null]"
|
||||||
|
@click="go"
|
||||||
|
>
|
||||||
|
<span :style="titleStyle">{{title}}</span>
|
||||||
|
<a-icon v-if="icon" :style="iconStyle" :type="icon" />
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const Group = {
|
||||||
|
name: 'AStepItemGroup',
|
||||||
|
props: {
|
||||||
|
align: {
|
||||||
|
type: String,
|
||||||
|
default: 'center',
|
||||||
|
validator(value) {
|
||||||
|
return ['left', 'center', 'right'].indexOf(value) != -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
render (h) {
|
||||||
|
return h(
|
||||||
|
'div',
|
||||||
|
{attrs: {style: `text-align: ${this.align}; margin-top: 8px`}},
|
||||||
|
[h('div', {attrs: {style: 'text-align: left; display: inline-block;'}}, [this.$slots.default])]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AStepItem',
|
||||||
|
Group: Group,
|
||||||
|
props: ['title', 'icon', 'link', 'titleStyle', 'iconStyle'],
|
||||||
|
methods: {
|
||||||
|
go () {
|
||||||
|
const link = this.link
|
||||||
|
if (link) {
|
||||||
|
this.$router.push(link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.step-item{
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
:global{
|
||||||
|
.ant-steps-item-process{
|
||||||
|
.linkable{
|
||||||
|
color: @primary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,69 @@
|
|||||||
|
<template>
|
||||||
|
<div class="avatar-list">
|
||||||
|
<slot>
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import AAvatar from 'ant-design-vue/es/avatar/Avatar'
|
||||||
|
import ATooltip from 'ant-design-vue/es/tooltip/Tooltip'
|
||||||
|
const Item = {
|
||||||
|
name: 'AvatarListItem',
|
||||||
|
props: {
|
||||||
|
size: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: 'small'
|
||||||
|
},
|
||||||
|
src: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
tips: {
|
||||||
|
type: String,
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
renderAvatar (h, size, src) {
|
||||||
|
return h(AAvatar, {props: {size: size, src: src}}, [])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
render (h) {
|
||||||
|
const avatar = this.renderAvatar(h, this.$props.size, this.$props.src)
|
||||||
|
return h(
|
||||||
|
'li',
|
||||||
|
{class: 'avatar-item'},
|
||||||
|
[this.$props.tips ? h(ATooltip, {props: {title: this.$props.tips}}, [avatar]) : avatar]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default {
|
||||||
|
name: 'AvatarList',
|
||||||
|
Item: Item
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.avatar-list {
|
||||||
|
display: inline-block;
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 8px;
|
||||||
|
font-size: 0;
|
||||||
|
.avatar-item {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-left: -8px;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
:global {
|
||||||
|
.ant-avatar {
|
||||||
|
border: 1px solid #fff;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,156 @@
|
|||||||
|
<template>
|
||||||
|
<div :class="['detail-list', size === 'small' ? 'small' : 'large', layout === 'vertical' ? 'vertical': 'horizontal']">
|
||||||
|
<div v-if="title" class="title">{{title}}</div>
|
||||||
|
<a-row>
|
||||||
|
<slot></slot>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ACol from 'ant-design-vue/es/grid/Col'
|
||||||
|
const Item = {
|
||||||
|
name: 'DetailListItem',
|
||||||
|
props: {
|
||||||
|
term: {
|
||||||
|
type: String,
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inject: {
|
||||||
|
col: {
|
||||||
|
type: Number
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
renderTerm (h, term) {
|
||||||
|
return term ? h(
|
||||||
|
'div',
|
||||||
|
{
|
||||||
|
attrs: {
|
||||||
|
class: 'term'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[term]
|
||||||
|
) : null
|
||||||
|
},
|
||||||
|
renderContent (h, content) {
|
||||||
|
return h(
|
||||||
|
'div',
|
||||||
|
{
|
||||||
|
attrs: {
|
||||||
|
class: 'content'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[content]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
render (h) {
|
||||||
|
const term = this.renderTerm(h, this.$props.term)
|
||||||
|
const content = this.renderContent(h, this.$slots.default)
|
||||||
|
return h(
|
||||||
|
ACol,
|
||||||
|
{
|
||||||
|
props: responsive[this.col]
|
||||||
|
},
|
||||||
|
[term, content]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const responsive = {
|
||||||
|
1: { xs: 24 },
|
||||||
|
2: { xs: 24, sm: 12 },
|
||||||
|
3: { xs: 24, sm: 12, md: 8 },
|
||||||
|
4: { xs: 24, sm: 12, md: 6 }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DetailList',
|
||||||
|
Item: Item,
|
||||||
|
props: {
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
col: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: 3
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: 'large'
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: 'horizontal'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
provide () {
|
||||||
|
return {
|
||||||
|
col: this.col > 4 ? 4 : this.col
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.detail-list{
|
||||||
|
.title {
|
||||||
|
font-size: 16px;
|
||||||
|
color: @title-color;
|
||||||
|
font-weight: bolder;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.term {
|
||||||
|
// Line-height is 22px IE dom height will calculate error
|
||||||
|
line-height: 20px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
margin-right: 8px;
|
||||||
|
color: @title-color;
|
||||||
|
white-space: nowrap;
|
||||||
|
display: table-cell;
|
||||||
|
&:after {
|
||||||
|
content: ':';
|
||||||
|
margin: 0 8px 0 2px;
|
||||||
|
position: relative;
|
||||||
|
top: -0.5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.content{
|
||||||
|
line-height: 22px;
|
||||||
|
width: 100%;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
color: @text-color;
|
||||||
|
display: table-cell;
|
||||||
|
}
|
||||||
|
&.small{
|
||||||
|
.title{
|
||||||
|
font-size: 14px;
|
||||||
|
color: @text-color;
|
||||||
|
font-weight: normal;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
.term,.content{
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.large{
|
||||||
|
.term,.content{
|
||||||
|
padding-bottom: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.vertical{
|
||||||
|
.term {
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
.term,.content{
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,142 @@
|
|||||||
|
<template>
|
||||||
|
<div >
|
||||||
|
<div :class="['mask', visible ? 'open' : 'close']" @click="close"></div>
|
||||||
|
<div :class="['drawer', placement, visible ? 'open' : 'close']">
|
||||||
|
<div ref="drawer" class="content beauty-scroll">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
<div v-if="showHandler" :class="['handler-container', placement, visible ? 'open' : 'close']" ref="handler" @click="toggle">
|
||||||
|
<slot v-if="$slots.handler" name="handler"></slot>
|
||||||
|
<div v-else class="handler">
|
||||||
|
<a-icon :type="visible ? 'close' : 'bars'" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Drawer',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
model: {
|
||||||
|
prop: 'visible',
|
||||||
|
event: 'change'
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
placement: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: 'left'
|
||||||
|
},
|
||||||
|
showHandler: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
open () {
|
||||||
|
this.$emit('change', true)
|
||||||
|
},
|
||||||
|
close () {
|
||||||
|
this.$emit('change', false)
|
||||||
|
},
|
||||||
|
toggle () {
|
||||||
|
this.$emit('change', !this.visible)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.mask{
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
top: 0;
|
||||||
|
background-color: @shadow-color;
|
||||||
|
transition: all 0.5s;
|
||||||
|
z-index: 100;
|
||||||
|
&.open{
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
&.close{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.drawer{
|
||||||
|
position: fixed;
|
||||||
|
transition: all 0.5s;
|
||||||
|
height: 100vh;
|
||||||
|
z-index: 100;
|
||||||
|
&.left{
|
||||||
|
left: 0px;
|
||||||
|
&.open{
|
||||||
|
.content{
|
||||||
|
box-shadow: 2px 0 8px @shadow-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.close{
|
||||||
|
transform: translateX(-100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.right{
|
||||||
|
right: 0px;
|
||||||
|
.content{
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
&.open{
|
||||||
|
.content{
|
||||||
|
box-shadow: -2px 0 8px @shadow-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.close{
|
||||||
|
transform: translateX(100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.content{
|
||||||
|
display: inline-block;
|
||||||
|
height: 100vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
.handler-container{
|
||||||
|
position: absolute;
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center;
|
||||||
|
transition: all 0.5s;
|
||||||
|
cursor: pointer;
|
||||||
|
top: 200px;
|
||||||
|
z-index: 100;
|
||||||
|
.handler {
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
background-color: @base-bg-color;
|
||||||
|
font-size: 26px;
|
||||||
|
box-shadow: 0 2px 8px @shadow-color;
|
||||||
|
line-height: 40px;
|
||||||
|
}
|
||||||
|
&.left{
|
||||||
|
right: -40px;
|
||||||
|
.handler{
|
||||||
|
border-radius: 0 5px 5px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.right{
|
||||||
|
left: -40px;
|
||||||
|
.handler{
|
||||||
|
border-radius: 5px 0 0 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
<template>
|
||||||
|
<div class="toolbar">
|
||||||
|
<div style="float: left">
|
||||||
|
<slot name="extra"></slot>
|
||||||
|
</div>
|
||||||
|
<div style="float: right">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'FooterToolBar'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.toolbar{
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
box-shadow: 0 -1px 2px @shadow-color;
|
||||||
|
background: @base-bg-color;
|
||||||
|
border-top: 1px solid @border-color-split;
|
||||||
|
padding: 12px 24px;
|
||||||
|
z-index: 9;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
<template>
|
||||||
|
<div class="head-info">
|
||||||
|
<span>{{title}}</span>
|
||||||
|
<p>{{content}}</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'HeadInfo',
|
||||||
|
props: ['title', 'content', 'bordered']
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.head-info{
|
||||||
|
text-align: center;
|
||||||
|
padding: 0 24px;
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-shrink: 0;
|
||||||
|
align-self: center;
|
||||||
|
span{
|
||||||
|
color: @text-color-second;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
p{
|
||||||
|
color: @text-color;
|
||||||
|
font-size: 24px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
<template>
|
||||||
|
<a-checkable-tag @change="$emit('click')" class="tag-default" v-model="checked">
|
||||||
|
<slot></slot>
|
||||||
|
</a-checkable-tag>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'TagSelectOption',
|
||||||
|
props: {
|
||||||
|
size: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: 'default'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
checked: false,
|
||||||
|
isTagSelectOption: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.tag-default{
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 0 8px;
|
||||||
|
height: auto;
|
||||||
|
margin-right: 24px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,96 @@
|
|||||||
|
<template>
|
||||||
|
<transition
|
||||||
|
v-if="!disabled"
|
||||||
|
:enter-active-class="`animated ${enterAnimate} page-toggle-enter-active`"
|
||||||
|
:leave-active-class="`animated ${leaveAnimate} page-toggle-leave-active`"
|
||||||
|
>
|
||||||
|
<slot></slot>
|
||||||
|
</transition>
|
||||||
|
<div v-else><slot></slot></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {preset as animates} from '@/config/default/animate.config'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'PageToggleTransition',
|
||||||
|
props: {
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
animate: {
|
||||||
|
type: String,
|
||||||
|
validator(value) {
|
||||||
|
return animates.findIndex(item => item.name == value) != -1
|
||||||
|
},
|
||||||
|
default: 'bounce'
|
||||||
|
},
|
||||||
|
direction: {
|
||||||
|
type: String,
|
||||||
|
validator(value) {
|
||||||
|
return ['x', 'y', 'left', 'right', 'up', 'down', 'downLeft', 'upRight', 'downRight', 'upLeft', 'downBig',
|
||||||
|
'upBig', 'downLeft', 'downRight', 'topRight', 'bottomLeft', 'topLeft', 'bottomRight', 'default'].indexOf(value) > -1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reverse: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
enterAnimate() {
|
||||||
|
return this.activeClass(false)
|
||||||
|
},
|
||||||
|
leaveAnimate() {
|
||||||
|
return this.activeClass(true)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
activeClass(isLeave) {
|
||||||
|
let animate = animates.find(item => this.animate == item.name)
|
||||||
|
if (animate == undefined) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
let direction = ''
|
||||||
|
if (this.direction == undefined) {
|
||||||
|
direction = animate.directions[0]
|
||||||
|
} else {
|
||||||
|
direction = animate.directions.find(item => item == this.direction)
|
||||||
|
}
|
||||||
|
direction = (direction == undefined || direction === 'default') ? '' : direction
|
||||||
|
if (direction != '') {
|
||||||
|
direction = isLeave && this.reverse ? this.reversePosition(direction, animate.directions) : direction
|
||||||
|
direction = direction[0].toUpperCase() + direction.substring(1)
|
||||||
|
}
|
||||||
|
let t = isLeave ? 'Out' : 'In'
|
||||||
|
return animate.name + t + direction
|
||||||
|
},
|
||||||
|
reversePosition(direction, directions) {
|
||||||
|
if(direction.length == 0 || direction == 'x' || direction == 'y') {
|
||||||
|
return direction
|
||||||
|
}
|
||||||
|
let index = directions.indexOf(direction)
|
||||||
|
index = (index % 2 == 1) ? index - 1 : index + 1
|
||||||
|
return directions[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.page-toggle-enter-active{
|
||||||
|
position: absolute !important;
|
||||||
|
animation-duration: 0.8s !important;
|
||||||
|
width: calc(100%) !important;
|
||||||
|
}
|
||||||
|
.page-toggle-leave-active{
|
||||||
|
position: absolute !important;
|
||||||
|
animation-duration: 0.8s !important;
|
||||||
|
width: calc(100%) !important;
|
||||||
|
}
|
||||||
|
.page-toggle-enter{
|
||||||
|
}
|
||||||
|
.page-toggle-leave-to{
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
// admin 配置
|
||||||
|
const ADMIN = {
|
||||||
|
palettes: ['#f5222d', '#fa541c', '#fadb14', '#3eaf7c', '#13c2c2', '#1890ff', '#722ed1', '#eb2f96'],
|
||||||
|
animates: require('./animate.config').preset,
|
||||||
|
theme: {
|
||||||
|
mode: {
|
||||||
|
DARK: 'dark',
|
||||||
|
LIGHT: 'light',
|
||||||
|
NIGHT: 'night'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
SIDE: 'side',
|
||||||
|
HEAD: 'head'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = ADMIN
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
const direct_s = ['left', 'right']
|
||||||
|
const direct_1 = ['left', 'right', 'down', 'up']
|
||||||
|
const direct_1_b = ['downBig', 'upBig', 'leftBig', 'rightBig']
|
||||||
|
const direct_2 = ['topLeft', 'bottomRight', 'topRight', 'bottomLeft']
|
||||||
|
const direct_3 = ['downLeft', 'upRight', 'downRight', 'upLeft']
|
||||||
|
|
||||||
|
// animate.css 配置
|
||||||
|
const ANIMATE = {
|
||||||
|
preset: [ //预设动画配置
|
||||||
|
{name: 'back', alias: '渐近', directions: direct_1},
|
||||||
|
{name: 'bounce', alias: '弹跳', directions: direct_1.concat('default')},
|
||||||
|
{name: 'fade', alias: '淡化', directions: direct_1.concat(direct_1_b).concat(direct_2).concat('default')},
|
||||||
|
{name: 'flip', alias: '翻转', directions: ['x', 'y']},
|
||||||
|
{name: 'lightSpeed', alias: '光速', directions: direct_s},
|
||||||
|
{name: 'rotate', alias: '旋转', directions: direct_3.concat('default')},
|
||||||
|
{name: 'roll', alias: '翻滚', directions: ['default']},
|
||||||
|
{name: 'zoom', alias: '缩放', directions: direct_1.concat('default')},
|
||||||
|
{name: 'slide', alias: '滑动', directions: direct_1},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
module.exports = ANIMATE
|
||||||
@ -0,0 +1,84 @@
|
|||||||
|
// antd 配置
|
||||||
|
const ANTD = {
|
||||||
|
primary: {
|
||||||
|
color: '#1890ff',
|
||||||
|
warning: '#faad14',
|
||||||
|
success: '#52c41a',
|
||||||
|
error: '#f5222d',
|
||||||
|
light: {
|
||||||
|
menuColors: ['#000c17', '#001529', '#002140']
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
|
menuColors: ['#000c17', '#001529', '#002140']
|
||||||
|
},
|
||||||
|
night: {
|
||||||
|
menuColors: ['#151515', '#1f1f1f', '#1e1e1e'],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
dark: {
|
||||||
|
'layout-body-background': '#f0f2f5',
|
||||||
|
'body-background': '#fff',
|
||||||
|
'component-background': '#fff',
|
||||||
|
'heading-color': 'rgba(0, 0, 0, 0.85)',
|
||||||
|
'text-color': 'rgba(0, 0, 0, 0.65)',
|
||||||
|
'text-color-inverse': '#fff',
|
||||||
|
'text-color-secondary': 'rgba(0, 0, 0, 0.45)',
|
||||||
|
'shadow-color': 'rgba(0, 0, 0, 0.15)',
|
||||||
|
'border-color-split': '#f0f0f0',
|
||||||
|
'background-color-light': '#fafafa',
|
||||||
|
'background-color-base': '#f5f5f5',
|
||||||
|
'table-selected-row-bg': '#fafafa',
|
||||||
|
'table-expanded-row-bg': '#fbfbfb',
|
||||||
|
'checkbox-check-color': '#fff',
|
||||||
|
'disabled-color': 'rgba(0, 0, 0, 0.25)',
|
||||||
|
'menu-dark-color': 'rgba(254, 254, 254, 0.65)',
|
||||||
|
'menu-dark-highlight-color': '#fefefe',
|
||||||
|
'menu-dark-arrow-color': '#fefefe',
|
||||||
|
'btn-primary-color': '#fff',
|
||||||
|
},
|
||||||
|
light: {
|
||||||
|
'layout-body-background': '#f0f2f5',
|
||||||
|
'body-background': '#fff',
|
||||||
|
'component-background': '#fff',
|
||||||
|
'heading-color': 'rgba(0, 0, 0, 0.85)',
|
||||||
|
'text-color': 'rgba(0, 0, 0, 0.65)',
|
||||||
|
'text-color-inverse': '#fff',
|
||||||
|
'text-color-secondary': 'rgba(0, 0, 0, 0.45)',
|
||||||
|
'shadow-color': 'rgba(0, 0, 0, 0.15)',
|
||||||
|
'border-color-split': '#f0f0f0',
|
||||||
|
'background-color-light': '#fafafa',
|
||||||
|
'background-color-base': '#f5f5f5',
|
||||||
|
'table-selected-row-bg': '#fafafa',
|
||||||
|
'table-expanded-row-bg': '#fbfbfb',
|
||||||
|
'checkbox-check-color': '#fff',
|
||||||
|
'disabled-color': 'rgba(0, 0, 0, 0.25)',
|
||||||
|
'menu-dark-color': 'rgba(1, 1, 1, 0.65)',
|
||||||
|
'menu-dark-highlight-color': '#fefefe',
|
||||||
|
'menu-dark-arrow-color': '#fefefe',
|
||||||
|
'btn-primary-color': '#fff',
|
||||||
|
},
|
||||||
|
night: {
|
||||||
|
'layout-body-background': '#000',
|
||||||
|
'body-background': '#141414',
|
||||||
|
'component-background': '#141414',
|
||||||
|
'heading-color': 'rgba(255, 255, 255, 0.85)',
|
||||||
|
'text-color': 'rgba(255, 255, 255, 0.85)',
|
||||||
|
'text-color-inverse': '#141414',
|
||||||
|
'text-color-secondary': 'rgba(255, 255, 255, 0.45)',
|
||||||
|
'shadow-color': 'rgba(255, 255, 255, 0.15)',
|
||||||
|
'border-color-split': '#303030',
|
||||||
|
'background-color-light': '#ffffff0a',
|
||||||
|
'background-color-base': '#2a2a2a',
|
||||||
|
'table-selected-row-bg': '#ffffff0a',
|
||||||
|
'table-expanded-row-bg': '#ffffff0b',
|
||||||
|
'checkbox-check-color': '#141414',
|
||||||
|
'disabled-color': 'rgba(255, 255, 255, 0.25)',
|
||||||
|
'menu-dark-color': 'rgba(254, 254, 254, 0.65)',
|
||||||
|
'menu-dark-highlight-color': '#fefefe',
|
||||||
|
'menu-dark-arrow-color': '#fefefe',
|
||||||
|
'btn-primary-color': '#141414',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
module.exports = ANTD
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
const ANTD = require('./antd.config')
|
||||||
|
const ADMIN = require('./admin.config')
|
||||||
|
const ANIMATE = require('./animate.config')
|
||||||
|
const setting = require('./setting.config')
|
||||||
|
|
||||||
|
module.exports = {ANTD, ADMIN, ANIMATE, setting}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
const deepMerge = require('deepmerge')
|
||||||
|
const _config = require('./config')
|
||||||
|
const {setting} = require('./default')
|
||||||
|
const config = deepMerge(setting, _config)
|
||||||
|
|
||||||
|
module.exports = config
|
||||||
@ -0,0 +1,175 @@
|
|||||||
|
<template>
|
||||||
|
<a-layout :class="['admin-layout', 'beauty-scroll']">
|
||||||
|
|
||||||
|
<drawer v-if="isMobile" v-model="drawerOpen">
|
||||||
|
<side-menu :theme="theme.mode" :menuData="menuData" :collapsed="false" :collapsible="false" @menuSelect="onMenuSelect"/>
|
||||||
|
</drawer>
|
||||||
|
<side-menu :class="[fixedSideBar ? 'fixed-side' : '']" :theme="theme.mode" v-else-if="layout === 'side' || layout === 'mix'" :menuData="sideMenuData" :collapsed="collapsed" :collapsible="true" />
|
||||||
|
|
||||||
|
<div v-if="fixedSideBar && !isMobile" :style="`width: ${sideMenuWidth}; min-width: ${sideMenuWidth};max-width: ${sideMenuWidth};`" class="virtual-side"></div>
|
||||||
|
|
||||||
|
<drawer v-if="!hideSetting" v-model="showSetting" placement="right">
|
||||||
|
<div class="setting" slot="handler">
|
||||||
|
<a-icon :type="showSetting ? 'close' : 'setting'"/>
|
||||||
|
</div>
|
||||||
|
<setting />
|
||||||
|
</drawer>
|
||||||
|
|
||||||
|
<a-layout class="admin-layout-main beauty-scroll">
|
||||||
|
<admin-header :class="[{'fixed-tabs': fixedTabs, 'fixed-header': fixedHeader, 'multi-page': multiPage}]" :style="headerStyle" :menuData="headMenuData" :collapsed="collapsed" @toggleCollapse="toggleCollapse"/>
|
||||||
|
|
||||||
|
<a-layout-header :class="['virtual-header', {'fixed-tabs' : fixedTabs, 'fixed-header': fixedHeader, 'multi-page': multiPage}]" v-show="fixedHeader"></a-layout-header>
|
||||||
|
|
||||||
|
<a-layout-content class="admin-layout-content" :style="`min-height: ${minHeight}px;`">
|
||||||
|
<div style="position: relative">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</a-layout-content>
|
||||||
|
|
||||||
|
<a-layout-footer style="padding: 0px">
|
||||||
|
<page-footer :link-list="footerLinks" :copyright="copyright" />
|
||||||
|
</a-layout-footer>
|
||||||
|
</a-layout>
|
||||||
|
</a-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import AdminHeader from './header/AdminHeader'
|
||||||
|
import PageFooter from './footer/PageFooter'
|
||||||
|
import Drawer from '../components/tool/Drawer'
|
||||||
|
import SideMenu from '../components/menu/SideMenu'
|
||||||
|
import Setting from '../components/setting/Setting'
|
||||||
|
import {mapState, mapMutations, mapGetters} from 'vuex'
|
||||||
|
|
||||||
|
// const minHeight = window.innerHeight - 64 - 122
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AdminLayout',
|
||||||
|
components: {Setting, SideMenu, Drawer, PageFooter, AdminHeader},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
minHeight: window.innerHeight - 64 - 122,
|
||||||
|
collapsed: false,
|
||||||
|
showSetting: false,
|
||||||
|
drawerOpen: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
provide() {
|
||||||
|
return {
|
||||||
|
adminLayout: this
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
$route(val) {
|
||||||
|
this.setActivated(val)
|
||||||
|
},
|
||||||
|
layout() {
|
||||||
|
this.setActivated(this.$route)
|
||||||
|
},
|
||||||
|
isMobile(val) {
|
||||||
|
if(!val) {
|
||||||
|
this.drawerOpen = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState('setting', ['isMobile', 'theme', 'layout', 'footerLinks', 'copyright', 'fixedHeader', 'fixedSideBar',
|
||||||
|
'fixedTabs', 'hideSetting', 'multiPage']),
|
||||||
|
...mapGetters('setting', ['firstMenu', 'subMenu', 'menuData']),
|
||||||
|
sideMenuWidth() {
|
||||||
|
return this.collapsed ? '80px' : '200px'
|
||||||
|
},
|
||||||
|
headerStyle() {
|
||||||
|
let width = (this.fixedHeader && this.layout !== 'head' && !this.isMobile) ? `calc(100% - ${this.sideMenuWidth})` : '100%'
|
||||||
|
let position = this.fixedHeader ? 'fixed' : 'static'
|
||||||
|
return `width: ${width}; position: ${position};`
|
||||||
|
},
|
||||||
|
headMenuData() {
|
||||||
|
const {layout, menuData, firstMenu} = this
|
||||||
|
return layout === 'mix' ? firstMenu : menuData
|
||||||
|
},
|
||||||
|
sideMenuData() {
|
||||||
|
const {layout, menuData, subMenu} = this
|
||||||
|
return layout === 'mix' ? subMenu : menuData
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapMutations('setting', ['correctPageMinHeight', 'setActivatedFirst']),
|
||||||
|
toggleCollapse () {
|
||||||
|
this.collapsed = !this.collapsed
|
||||||
|
},
|
||||||
|
onMenuSelect () {
|
||||||
|
this.toggleCollapse()
|
||||||
|
},
|
||||||
|
setActivated(route) {
|
||||||
|
if (this.layout === 'mix') {
|
||||||
|
let matched = route.matched
|
||||||
|
matched = matched.slice(0, matched.length - 1)
|
||||||
|
const {firstMenu} = this
|
||||||
|
for (let menu of firstMenu) {
|
||||||
|
if (matched.findIndex(item => item.path === menu.fullPath) !== -1) {
|
||||||
|
this.setActivatedFirst(menu.fullPath)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.correctPageMinHeight(this.minHeight - 24)
|
||||||
|
this.setActivated(this.$route)
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.correctPageMinHeight(-this.minHeight + 24)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.admin-layout{
|
||||||
|
.side-menu{
|
||||||
|
&.fixed-side{
|
||||||
|
position: fixed;
|
||||||
|
height: 100vh;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.virtual-side{
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
.virtual-header{
|
||||||
|
transition: all 0.2s;
|
||||||
|
opacity: 0;
|
||||||
|
&.fixed-tabs.multi-page:not(.fixed-header){
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.admin-layout-main{
|
||||||
|
.admin-header{
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: all 0.2s;
|
||||||
|
&.fixed-tabs.multi-page:not(.fixed-header){
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.admin-layout-content{
|
||||||
|
padding: 24px 5px 0;
|
||||||
|
/*overflow-x: hidden;*/
|
||||||
|
/*min-height: calc(100vh - 64px - 122px);*/
|
||||||
|
}
|
||||||
|
.setting{
|
||||||
|
background-color: @primary-color;
|
||||||
|
color: @base-bg-color;
|
||||||
|
border-radius: 5px 0 0 5px;
|
||||||
|
line-height: 40px;
|
||||||
|
font-size: 22px;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
box-shadow: -2px 0 8px @shadow-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
<template>
|
||||||
|
<page-toggle-transition :disabled="animate.disabled" :animate="animate.name" :direction="animate.direction">
|
||||||
|
<router-view />
|
||||||
|
</page-toggle-transition>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PageToggleTransition from '../components/transition/PageToggleTransition';
|
||||||
|
import {mapState} from 'vuex'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'BlankView',
|
||||||
|
components: {PageToggleTransition},
|
||||||
|
computed: {
|
||||||
|
...mapState('setting', ['multiPage', 'animate'])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
<template>
|
||||||
|
<div class="common-layout">
|
||||||
|
<div class="content"><slot></slot></div>
|
||||||
|
<page-footer :link-list="footerLinks" :copyright="copyright"></page-footer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PageFooter from '@/layouts/footer/PageFooter'
|
||||||
|
import {mapState} from 'vuex'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CommonLayout',
|
||||||
|
components: {PageFooter},
|
||||||
|
computed: {
|
||||||
|
...mapState('setting', ['footerLinks', 'copyright'])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.common-layout{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100vh;
|
||||||
|
overflow: auto;
|
||||||
|
background-color: @layout-body-background;
|
||||||
|
background-image: url('https://gw.alipayobjects.com/zos/rmsportal/TVYTbAXWheQpRcWDaDMu.svg');
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position-x: center;
|
||||||
|
background-position-y: 110px;
|
||||||
|
background-size: 100%;
|
||||||
|
.content{
|
||||||
|
padding: 32px 0;
|
||||||
|
flex: 1;
|
||||||
|
@media (min-width: 768px){
|
||||||
|
|
||||||
|
padding: 112px 0 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,144 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-layout">
|
||||||
|
<page-header ref="pageHeader" :style="`margin-top: ${multiPage ? 0 : -24}px`" :breadcrumb="breadcrumb" :title="pageTitle" :logo="logo" :avatar="avatar">
|
||||||
|
<slot name="action" slot="action"></slot>
|
||||||
|
<slot slot="content" name="headerContent"></slot>
|
||||||
|
<div slot="content" v-if="!this.$slots.headerContent && desc">
|
||||||
|
<p>{{desc}}</p>
|
||||||
|
<div v-if="this.linkList" class="link">
|
||||||
|
<template v-for="(link, index) in linkList">
|
||||||
|
<a :key="index" :href="link.href"><a-icon :type="link.icon" />{{link.title}}</a>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<slot v-if="this.$slots.extra" slot="extra" name="extra"></slot>
|
||||||
|
</page-header>
|
||||||
|
<div ref="page" :class="['page-content', layout, pageWidth]" >
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PageHeader from '@/components/page/header/PageHeader'
|
||||||
|
import {mapState, mapMutations} from 'vuex'
|
||||||
|
import {getI18nKey} from '@/utils/routerUtil'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'PageLayout',
|
||||||
|
components: {PageHeader},
|
||||||
|
props: ['desc', 'logo', 'title', 'avatar', 'linkList', 'extraImage'],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
page: {},
|
||||||
|
pageHeaderHeight: 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
$route() {
|
||||||
|
this.page = this.$route.meta.page
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updated() {
|
||||||
|
if (!this._inactive) {
|
||||||
|
this.updatePageHeight()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
activated() {
|
||||||
|
this.updatePageHeight()
|
||||||
|
},
|
||||||
|
deactivated() {
|
||||||
|
this.updatePageHeight(0)
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.updatePageHeight()
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.page = this.$route.meta.page
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.updatePageHeight(0)
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState('setting', ['layout', 'multiPage', 'pageMinHeight', 'pageWidth', 'customTitles']),
|
||||||
|
pageTitle() {
|
||||||
|
let pageTitle = this.page && this.page.title
|
||||||
|
return this.customTitle || (pageTitle && this.$t(pageTitle)) || this.title || this.routeName
|
||||||
|
},
|
||||||
|
routeName() {
|
||||||
|
const route = this.$route
|
||||||
|
return this.$t(getI18nKey(route.matched[route.matched.length - 1].path))
|
||||||
|
},
|
||||||
|
breadcrumb() {
|
||||||
|
let page = this.page
|
||||||
|
let breadcrumb = page && page.breadcrumb
|
||||||
|
if (breadcrumb) {
|
||||||
|
let i18nBreadcrumb = []
|
||||||
|
breadcrumb.forEach(item => {
|
||||||
|
i18nBreadcrumb.push(this.$t(item))
|
||||||
|
})
|
||||||
|
return i18nBreadcrumb
|
||||||
|
} else {
|
||||||
|
return this.getRouteBreadcrumb()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
marginCorrect() {
|
||||||
|
return this.multiPage ? 24 : 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapMutations('setting', ['correctPageMinHeight']),
|
||||||
|
getRouteBreadcrumb() {
|
||||||
|
let routes = this.$route.matched
|
||||||
|
const path = this.$route.path
|
||||||
|
let breadcrumb = []
|
||||||
|
routes.filter(item => path.includes(item.path))
|
||||||
|
.forEach(route => {
|
||||||
|
const path = route.path.length === 0 ? '/home' : route.path
|
||||||
|
breadcrumb.push(this.$t(getI18nKey(path)))
|
||||||
|
})
|
||||||
|
let pageTitle = this.page && this.page.title
|
||||||
|
if (this.customTitle || pageTitle) {
|
||||||
|
breadcrumb[breadcrumb.length - 1] = this.customTitle || pageTitle
|
||||||
|
}
|
||||||
|
return breadcrumb
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 用于计算页面内容最小高度
|
||||||
|
* @param newHeight
|
||||||
|
*/
|
||||||
|
updatePageHeight(newHeight = this.$refs.pageHeader.$el.offsetHeight + this.marginCorrect) {
|
||||||
|
this.correctPageMinHeight(this.pageHeaderHeight - newHeight)
|
||||||
|
this.pageHeaderHeight = newHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.page-header{
|
||||||
|
margin: 0 -24px 0;
|
||||||
|
}
|
||||||
|
.link{
|
||||||
|
/*margin-top: 16px;*/
|
||||||
|
line-height: 24px;
|
||||||
|
a{
|
||||||
|
font-size: 14px;
|
||||||
|
margin-right: 32px;
|
||||||
|
i{
|
||||||
|
font-size: 22px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.page-content{
|
||||||
|
position: relative;
|
||||||
|
padding: 5px 0 0;
|
||||||
|
&.side{
|
||||||
|
}
|
||||||
|
&.head.fixed{
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 1400px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
<template>
|
||||||
|
<page-layout :desc="desc" :linkList="linkList">
|
||||||
|
<div v-if="this.extraImage && !isMobile" slot="extra" class="extraImg">
|
||||||
|
<img :src="extraImage"/>
|
||||||
|
</div>
|
||||||
|
<page-toggle-transition :disabled="animate.disabled" :animate="animate.name" :direction="animate.direction">
|
||||||
|
<router-view ref="page" />
|
||||||
|
</page-toggle-transition>
|
||||||
|
</page-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PageLayout from './PageLayout'
|
||||||
|
import PageToggleTransition from '../components/transition/PageToggleTransition';
|
||||||
|
import {mapState} from 'vuex'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'PageView',
|
||||||
|
components: {PageToggleTransition, PageLayout},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
page: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState('setting', ['isMobile', 'multiPage', 'animate']),
|
||||||
|
desc() {
|
||||||
|
return this.page.desc
|
||||||
|
},
|
||||||
|
linkList() {
|
||||||
|
return this.page.linkList
|
||||||
|
},
|
||||||
|
extraImage() {
|
||||||
|
return this.page.extraImage
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.page = this.$refs.page
|
||||||
|
},
|
||||||
|
updated () {
|
||||||
|
this.page = this.$refs.page
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.extraImg{
|
||||||
|
margin-top: -60px;
|
||||||
|
text-align: center;
|
||||||
|
width: 195px;
|
||||||
|
img{
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
<template>
|
||||||
|
<div class="footer">
|
||||||
|
<div class="links">
|
||||||
|
<a target="_blank" :key="index" :href="item.link ? item.link : 'javascript: void(0)'" v-for="(item, index) in linkList">
|
||||||
|
<a-icon v-if="item.icon" :type="item.icon"/>{{item.name}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="copyright">
|
||||||
|
Copyright<a-icon type="copyright" />{{copyright}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'PageFooter',
|
||||||
|
props: ['copyright', 'linkList']
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.footer{
|
||||||
|
padding: 48px 16px 24px;
|
||||||
|
/*margin: 48px 0 24px;*/
|
||||||
|
text-align: center;
|
||||||
|
.copyright{
|
||||||
|
color: @text-color-second;
|
||||||
|
font-size: 14px;
|
||||||
|
i {
|
||||||
|
margin: 0 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.links{
|
||||||
|
margin-bottom: 8px;
|
||||||
|
a:not(:last-child) {
|
||||||
|
margin-right: 40px;
|
||||||
|
}
|
||||||
|
a{
|
||||||
|
color: @text-color-second;
|
||||||
|
-webkit-transition: all .3s;
|
||||||
|
transition: all .3s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
<template>
|
||||||
|
<a-layout-header :class="[headerTheme, 'admin-header']">
|
||||||
|
<div :class="['admin-header-wide', layout, pageWidth]">
|
||||||
|
<router-link v-if="isMobile || layout === 'head'" to="/" :class="['logo', isMobile ? null : 'pc', headerTheme]">
|
||||||
|
<img width="32" src="@/assets/images/logo.png" />
|
||||||
|
<h1 v-if="!isMobile">{{systemName}}</h1>
|
||||||
|
</router-link>
|
||||||
|
<!-- <a-divider v-if="isMobile" type="vertical" /> -->
|
||||||
|
<a-icon v-if="layout !== 'head' && !isMobile" class="trigger" :type="collapsed ? 'menu-unfold' : 'menu-fold'" @click="toggleCollapse"/>
|
||||||
|
<div v-if="layout !== 'side' && !isMobile" class="admin-header-menu" :style="`width: ${menuWidth};`">
|
||||||
|
<i-menu class="head-menu" :theme="headerTheme" mode="horizontal" :options="menuData" @select="onSelect"/>
|
||||||
|
</div>
|
||||||
|
<div :class="['admin-header-right', headerTheme]">
|
||||||
|
<header-avatar class="header-item"/>
|
||||||
|
<!-- <a-dropdown class="lang header-item">
|
||||||
|
<div>
|
||||||
|
<a-icon type="global"/> {{langAlias}}
|
||||||
|
</div>
|
||||||
|
<a-menu @click="val => setLang(val.key)" :selected-keys="[lang]" slot="overlay">
|
||||||
|
<a-menu-item v-for=" lang in langList" :key="lang.key">{{lang.key.toLowerCase() + ' ' + lang.name}}</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</a-dropdown> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-layout-header>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// import HeaderNotice from './HeaderNotice'
|
||||||
|
import HeaderAvatar from './HeaderAvatar'
|
||||||
|
import IMenu from '@/components/menu/menu'
|
||||||
|
import {mapState, mapMutations} from 'vuex'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AdminHeader',
|
||||||
|
components: {IMenu, HeaderAvatar},
|
||||||
|
props: ['collapsed', 'menuData'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
langList: [
|
||||||
|
{key: 'TW', name: '繁體中文', alias: '繁體'},
|
||||||
|
{key: 'CN', name: '简体中文', alias: '简体'},
|
||||||
|
{key: 'US', name: 'English', alias: 'EN'}
|
||||||
|
],
|
||||||
|
searchActive: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState('setting', ['theme', 'isMobile', 'layout', 'systemName', 'lang', 'pageWidth']),
|
||||||
|
headerTheme () {
|
||||||
|
if (this.layout == 'side' && this.theme.mode == 'dark' && !this.isMobile) {
|
||||||
|
return 'light'
|
||||||
|
}
|
||||||
|
return this.theme.mode
|
||||||
|
},
|
||||||
|
langAlias() {
|
||||||
|
let lang = this.langList.find(item => item.key == this.lang)
|
||||||
|
return lang.alias
|
||||||
|
},
|
||||||
|
menuWidth() {
|
||||||
|
const {layout, searchActive} = this
|
||||||
|
const headWidth = layout === 'head' ? '100% - 188px' : '100%'
|
||||||
|
const extraWidth = searchActive ? '600px' : '400px'
|
||||||
|
return `calc(${headWidth} - ${extraWidth})`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggleCollapse () {
|
||||||
|
this.$emit('toggleCollapse')
|
||||||
|
},
|
||||||
|
onSelect (obj) {
|
||||||
|
this.$emit('menuSelect', obj)
|
||||||
|
},
|
||||||
|
...mapMutations('setting', ['setLang'])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@import "index";
|
||||||
|
</style>
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
<template>
|
||||||
|
<a-dropdown>
|
||||||
|
<div class="header-avatar" style="cursor: pointer">
|
||||||
|
<!-- <a-avatar class="avatar" size="small" shape="circle" :src="user.avatar"/> -->
|
||||||
|
<span class="name">{{user.name}}</span>
|
||||||
|
</div>
|
||||||
|
<a-menu :class="['avatar-menu']" slot="overlay">
|
||||||
|
<!-- <a-menu-item>
|
||||||
|
<a-icon type="user" />
|
||||||
|
<span>個人中心</span>
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item>
|
||||||
|
<a-icon type="setting" />
|
||||||
|
<span>設定</span>
|
||||||
|
</a-menu-item> -->
|
||||||
|
<!-- <a-menu-divider /> -->
|
||||||
|
<a-menu-item @click="logout">
|
||||||
|
<a-icon style="margin-right: 8px;" type="poweroff" />
|
||||||
|
<span>登出</span>
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</a-dropdown>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {mapGetters} from 'vuex'
|
||||||
|
import {logout} from '@/services/admin'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'HeaderAvatar',
|
||||||
|
computed: {
|
||||||
|
...mapGetters('account', ['user']),
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
logout() {
|
||||||
|
logout()
|
||||||
|
this.$router.push('/login')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.header-avatar{
|
||||||
|
display: inline-flex;
|
||||||
|
.avatar, .name{
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
.avatar{
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
.name{
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.avatar-menu{
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
@ -0,0 +1,92 @@
|
|||||||
|
<template>
|
||||||
|
<a-dropdown :trigger="['click']" v-model="show">
|
||||||
|
<div slot="overlay">
|
||||||
|
<a-spin :spinning="loading">
|
||||||
|
<a-tabs class="dropdown-tabs" :tabBarStyle="{textAlign: 'center'}" :style="{width: '297px'}">
|
||||||
|
<a-tab-pane tab="通知" key="1">
|
||||||
|
<a-list class="tab-pane">
|
||||||
|
<a-list-item>
|
||||||
|
<a-list-item-meta title="你收到了 14 份新周报" description="一年前">
|
||||||
|
<a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png"/>
|
||||||
|
</a-list-item-meta>
|
||||||
|
</a-list-item>
|
||||||
|
<a-list-item>
|
||||||
|
<a-list-item-meta title="你推荐的 曲妮妮 已通过第三轮面试" description="一年前">
|
||||||
|
<a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png"/>
|
||||||
|
</a-list-item-meta>
|
||||||
|
</a-list-item>
|
||||||
|
<a-list-item>
|
||||||
|
<a-list-item-meta title="这种模板可以区分多种通知类型" description="一年前">
|
||||||
|
<a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png"/>
|
||||||
|
</a-list-item-meta>
|
||||||
|
</a-list-item>
|
||||||
|
</a-list>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane tab="消息" key="2">
|
||||||
|
<a-list class="tab-pane"></a-list>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane tab="待办" key="3">
|
||||||
|
<a-list class="tab-pane"></a-list>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</a-spin>
|
||||||
|
</div>
|
||||||
|
<span @click="fetchNotice" class="header-notice">
|
||||||
|
<a-badge class="notice-badge" count="12">
|
||||||
|
<a-icon :class="['header-notice-icon']" type="bell" />
|
||||||
|
</a-badge>
|
||||||
|
</span>
|
||||||
|
</a-dropdown>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'HeaderNotice',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchNotice () {
|
||||||
|
if (this.loading) {
|
||||||
|
this.loading = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.loadding = true
|
||||||
|
setTimeout(() => {
|
||||||
|
this.loadding = false
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.header-notice{
|
||||||
|
display: inline-block;
|
||||||
|
transition: all 0.3s;
|
||||||
|
span {
|
||||||
|
vertical-align: initial;
|
||||||
|
}
|
||||||
|
.notice-badge{
|
||||||
|
color: inherit;
|
||||||
|
.header-notice-icon{
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.dropdown-tabs{
|
||||||
|
background-color: @base-bg-color;
|
||||||
|
box-shadow: 0 2px 8px @shadow-color;
|
||||||
|
border-radius: 4px;
|
||||||
|
.tab-pane{
|
||||||
|
padding: 0 24px 12px;
|
||||||
|
min-height: 250px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,67 @@
|
|||||||
|
<template>
|
||||||
|
<div class="header-search">
|
||||||
|
<a-icon type="search" class="search-icon" @click="enterSearchMode"/>
|
||||||
|
<a-auto-complete
|
||||||
|
ref="input"
|
||||||
|
:getPopupContainer="e => {return e.parentNode || document.body}"
|
||||||
|
:dataSource="dataSource"
|
||||||
|
:class="['search-input', searchMode ? 'enter' : 'leave']"
|
||||||
|
placeholder="站内搜索"
|
||||||
|
@blur="leaveSearchMode"
|
||||||
|
>
|
||||||
|
</a-auto-complete>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'HeaderSearch',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
dataSource: ['选项一', '选项二'],
|
||||||
|
searchMode: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
enterSearchMode () {
|
||||||
|
this.searchMode = true
|
||||||
|
this.$emit('active', true)
|
||||||
|
setTimeout(() => this.$refs.input.focus(), 300)
|
||||||
|
},
|
||||||
|
leaveSearchMode () {
|
||||||
|
this.searchMode = false
|
||||||
|
setTimeout(() => this.$emit('active', false), 300)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.header-search{
|
||||||
|
.search-icon{
|
||||||
|
font-size: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.search-input{
|
||||||
|
border: 0;
|
||||||
|
border-bottom: 1px solid @border-color-split;
|
||||||
|
transition: width 0.3s ease-in-out;
|
||||||
|
input{
|
||||||
|
border: 0;
|
||||||
|
box-shadow: 0 0 0 0;
|
||||||
|
}
|
||||||
|
&.leave{
|
||||||
|
width: 0px;
|
||||||
|
input{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.enter{
|
||||||
|
width: 200px;
|
||||||
|
input:focus{
|
||||||
|
box-shadow: 0 0 0 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,92 @@
|
|||||||
|
.admin-header{
|
||||||
|
padding: 0;
|
||||||
|
z-index: 2;
|
||||||
|
box-shadow: @shadow-down;
|
||||||
|
position: relative;
|
||||||
|
background: @base-bg-color;
|
||||||
|
.head-menu{
|
||||||
|
height: 64px;
|
||||||
|
line-height: 64px;
|
||||||
|
vertical-align: middle;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
&.dark{
|
||||||
|
background: @header-bg-color-dark;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
&.night{
|
||||||
|
.head-menu{
|
||||||
|
background: @base-bg-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.admin-header-wide{
|
||||||
|
padding-left: 24px;
|
||||||
|
&.head.fixed{
|
||||||
|
max-width: 1400px;
|
||||||
|
margin: auto;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
&.side{
|
||||||
|
padding-right: 12px;
|
||||||
|
}
|
||||||
|
.logo {
|
||||||
|
height: 64px;
|
||||||
|
line-height: 58px;
|
||||||
|
vertical-align: top;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0 12px 0 24px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 20px;
|
||||||
|
color: inherit;
|
||||||
|
&.pc{
|
||||||
|
padding: 0 12px 0 0;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
h1{
|
||||||
|
color: inherit;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.trigger {
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 64px;
|
||||||
|
padding: 0 0px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color .3s;
|
||||||
|
&:hover{
|
||||||
|
color: @primary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.admin-header-menu{
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.admin-header-right{
|
||||||
|
float: right;
|
||||||
|
display: flex;
|
||||||
|
color: inherit;
|
||||||
|
.header-item{
|
||||||
|
color: inherit;
|
||||||
|
padding: 0 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
align-self: center;
|
||||||
|
a{
|
||||||
|
color: inherit;
|
||||||
|
i{
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
each(@theme-list, {
|
||||||
|
&.@{value} .header-item{
|
||||||
|
&:hover{
|
||||||
|
@class: ~'hover-bg-color-@{value}';
|
||||||
|
background-color: @@class;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,188 @@
|
|||||||
|
<template>
|
||||||
|
<div :class="['tabs-head', layout, pageWidth]">
|
||||||
|
<a-tabs
|
||||||
|
type="editable-card"
|
||||||
|
:class="['tabs-container', layout, pageWidth, {'affixed' : affixed, 'fixed-header' : fixedHeader, 'collapsed' : adminLayout.collapsed}]"
|
||||||
|
:active-key="active"
|
||||||
|
:hide-add="true"
|
||||||
|
>
|
||||||
|
<a-tooltip placement="left" :title="lockTitle" slot="tabBarExtraContent">
|
||||||
|
<a-icon
|
||||||
|
theme="filled"
|
||||||
|
@click="onLockClick"
|
||||||
|
class="header-lock"
|
||||||
|
:type="fixedTabs ? 'lock' : 'unlock'"
|
||||||
|
/>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tab-pane v-for="page in pageList" :key="page.path">
|
||||||
|
<div slot="tab" class="tab" @contextmenu="e => onContextmenu(page.path, e)">
|
||||||
|
<a-icon @click="onRefresh(page)" :class="['icon-sync', {'hide': page.path !== active && !page.loading}]" :type="page.loading ? 'loading' : 'sync'" />
|
||||||
|
<div class="title" @click="onTabClick(page.path)" >{{pageName(page)}}</div>
|
||||||
|
<a-icon v-if="!page.unclose" @click="onClose(page.path)" class="icon-close" type="close"/>
|
||||||
|
</div>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
<div v-if="affixed" class="virtual-tabs"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {mapState, mapMutations} from 'vuex'
|
||||||
|
import {getI18nKey} from '@/utils/routerUtil'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'TabsHead',
|
||||||
|
i18n: {
|
||||||
|
messages: {
|
||||||
|
TW: {
|
||||||
|
lock: '點擊鎖定頁簽頭',
|
||||||
|
unlock: '點擊解除鎖定',
|
||||||
|
},
|
||||||
|
CN: {
|
||||||
|
lock: '点击锁定页签头',
|
||||||
|
unlock: '点击解除锁定',
|
||||||
|
},
|
||||||
|
US: {
|
||||||
|
lock: 'click to lock the tabs head',
|
||||||
|
unlock: 'click to unlock',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
pageList: Array,
|
||||||
|
active: String,
|
||||||
|
fixed: Boolean
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
affixed: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inject:['adminLayout'],
|
||||||
|
created() {
|
||||||
|
this.affixed = this.fixedTabs
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState('setting', ['layout', 'pageWidth', 'fixedHeader', 'fixedTabs', 'customTitles']),
|
||||||
|
lockTitle() {
|
||||||
|
return this.$t(this.fixedTabs ? 'unlock' : 'lock')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapMutations('setting', ['setFixedTabs']),
|
||||||
|
onLockClick() {
|
||||||
|
this.setFixedTabs(!this.fixedTabs)
|
||||||
|
if (this.fixedTabs) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.affixed = true
|
||||||
|
}, 200)
|
||||||
|
} else {
|
||||||
|
this.affixed = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onTabClick(key) {
|
||||||
|
if (this.active !== key) {
|
||||||
|
this.$emit('change', key)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onClose(key) {
|
||||||
|
this.$emit('close', key)
|
||||||
|
},
|
||||||
|
onRefresh(page) {
|
||||||
|
this.$emit('refresh', page.path, page)
|
||||||
|
},
|
||||||
|
onContextmenu(pageKey, e) {
|
||||||
|
this.$emit('contextmenu', pageKey, e)
|
||||||
|
},
|
||||||
|
pageName(page) {
|
||||||
|
const pagePath = page.fullPath.split('?')[0]
|
||||||
|
const custom = this.customTitles.find(item => item.path === pagePath)
|
||||||
|
return (custom && custom.title) || page.title || this.$t(getI18nKey(page.keyPath))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.tab{
|
||||||
|
margin: 0 -16px;
|
||||||
|
padding: 0 16px;
|
||||||
|
font-size: 14px;
|
||||||
|
user-select: none;
|
||||||
|
transition: all 0.2s;
|
||||||
|
.title{
|
||||||
|
display: inline-block;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.icon-close{
|
||||||
|
font-size: 12px;
|
||||||
|
margin-left: 6px;
|
||||||
|
margin-right: -4px !important;
|
||||||
|
color: @text-color-second;
|
||||||
|
&:hover{
|
||||||
|
color: @text-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.icon-sync{
|
||||||
|
margin-left: -4px;
|
||||||
|
color: @primary-4;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
&:hover{
|
||||||
|
color: @primary-color;
|
||||||
|
}
|
||||||
|
font-size: 14px;
|
||||||
|
&.hide{
|
||||||
|
font-size: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.tabs-head{
|
||||||
|
margin: 0 auto;
|
||||||
|
&.head.fixed{
|
||||||
|
width: 1400px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.tabs-container{
|
||||||
|
margin: -16px auto 8px;
|
||||||
|
transition: top,left 0.2s;
|
||||||
|
.header-lock{
|
||||||
|
font-size: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: @primary-3;
|
||||||
|
&:hover{
|
||||||
|
color: @primary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.affixed{
|
||||||
|
margin: 0 auto;
|
||||||
|
top: 0px;
|
||||||
|
padding: 8px 24px 0;
|
||||||
|
position: fixed;
|
||||||
|
height: 48px;
|
||||||
|
z-index: 1;
|
||||||
|
background-color: @layout-body-background;
|
||||||
|
&.side,&.mix{
|
||||||
|
right: 0;
|
||||||
|
left: 200px;
|
||||||
|
&.collapsed{
|
||||||
|
left: 80px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.head{
|
||||||
|
width: inherit;
|
||||||
|
padding: 8px 0 0;
|
||||||
|
&.fluid{
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 8px 24px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.fixed-header{
|
||||||
|
top: 64px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.virtual-tabs{
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
module.exports = {
|
||||||
|
messages: {
|
||||||
|
TW: {
|
||||||
|
closeLeft: '關閉左側',
|
||||||
|
closeRight: '關閉右側',
|
||||||
|
closeOthers: '關閉其它',
|
||||||
|
refresh: '刷新頁面',
|
||||||
|
warn: '這是最後一頁,不能再關閉了',
|
||||||
|
},
|
||||||
|
CN: {
|
||||||
|
closeLeft: '关闭左侧',
|
||||||
|
closeRight: '关闭右侧',
|
||||||
|
closeOthers: '关闭其它',
|
||||||
|
refresh: '刷新页面',
|
||||||
|
warn: '这是最后一页,不能再关闭了',
|
||||||
|
},
|
||||||
|
US: {
|
||||||
|
closeLeft: 'close left',
|
||||||
|
closeRight: 'close right',
|
||||||
|
closeOthers: 'close others',
|
||||||
|
refresh: 'refresh the page',
|
||||||
|
warn: 'This is the last page, you can\'t close it',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
import TabsView from './TabsView'
|
||||||
|
export default TabsView
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import App from './App.vue'
|
||||||
|
import {initRouter} from './router'
|
||||||
|
import './theme/index.less'
|
||||||
|
import Antd from 'ant-design-vue'
|
||||||
|
import Viser from 'viser-vue'
|
||||||
|
// import '@/mock'
|
||||||
|
import store from './store'
|
||||||
|
import 'animate.css/source/animate.css'
|
||||||
|
import Plugins from '@/plugins'
|
||||||
|
import {initI18n} from '@/utils/i18n'
|
||||||
|
import bootstrap from '@/bootstrap'
|
||||||
|
import 'moment/locale/zh-tw'
|
||||||
|
import CKEditor from 'ckeditor4-vue';
|
||||||
|
import VueClipboard from 'vue-clipboard2'
|
||||||
|
|
||||||
|
const router = initRouter(store.state.setting.asyncRoutes)
|
||||||
|
const i18n = initI18n('TW', 'CN', 'US')
|
||||||
|
|
||||||
|
import * as ElResize from 'vue-element-resize-event'
|
||||||
|
Vue.use(ElResize)
|
||||||
|
|
||||||
|
Vue.use(Antd)
|
||||||
|
Vue.config.productionTip = false
|
||||||
|
Vue.use(Viser)
|
||||||
|
Vue.use(Plugins)
|
||||||
|
Vue.use(CKEditor)
|
||||||
|
Vue.use(VueClipboard)
|
||||||
|
|
||||||
|
bootstrap({router, store, i18n, message: Vue.prototype.$message})
|
||||||
|
|
||||||
|
new Vue({
|
||||||
|
router,
|
||||||
|
store,
|
||||||
|
i18n,
|
||||||
|
render: h => h(App),
|
||||||
|
}).$mount('#app')
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
const avatars = [
|
||||||
|
'https://gw.alipayobjects.com/zos/rmsportal/cnrhVkzwxjPwAaCfPbdc.png',
|
||||||
|
'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png',
|
||||||
|
'https://gw.alipayobjects.com/zos/rmsportal/gaOngJwsRYRaVAuXXcmB.png',
|
||||||
|
'https://gw.alipayobjects.com/zos/rmsportal/WhxKECPNujWoWEFNdnJE.png',
|
||||||
|
'https://gw.alipayobjects.com/zos/rmsportal/ubnKSIfAJTxIgXOKlciN.png',
|
||||||
|
'https://gw.alipayobjects.com/zos/rmsportal/jZUIxmJycoymBprLOUbT.png'
|
||||||
|
]
|
||||||
|
|
||||||
|
const positions = [
|
||||||
|
{
|
||||||
|
CN: 'Java工程师 | 蚂蚁金服-计算服务事业群-微信平台部',
|
||||||
|
TW: 'Java工程師 | 螞蟻金服-計算服務事業群-微信平台部',
|
||||||
|
US: 'Java engineer | Ant financial - Computing services business group - WeChat platform division'
|
||||||
|
},{
|
||||||
|
CN: '前端工程师 | 蚂蚁金服-计算服务事业群-VUE平台',
|
||||||
|
TW: '前端工程師 | 螞蟻金服-計算服務事業群-VUE平台',
|
||||||
|
US: 'Front-end engineer | Ant Financial - Computing services business group - VUE platform'
|
||||||
|
},{
|
||||||
|
CN: '前端工程师 | 蚂蚁金服-计算服务事业群-REACT平台',
|
||||||
|
TW: '前端工程師 | 螞蟻金服-計算服務事業群-REACT平台',
|
||||||
|
US: 'Front-end engineer | Ant Financial - Computing services business group - REACT platform'
|
||||||
|
},{
|
||||||
|
CN: '产品分析师 | 蚂蚁金服-计算服务事业群-IOS平台部',
|
||||||
|
TW: '產品分析師 | 螞蟻金服-計算服務事業群-IOS平台部',
|
||||||
|
US: 'Product analyst | Ant Financial - Computing services business group - IOS platform division'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const admins = ['ICZER', 'JACK', 'LUIS', 'DAVID']
|
||||||
|
|
||||||
|
export {positions, avatars, admins}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
import Mock from 'mockjs'
|
||||||
|
import {positions, avatars, admins} from '../common'
|
||||||
|
|
||||||
|
const Random = Mock.Random
|
||||||
|
|
||||||
|
const timeList = [
|
||||||
|
{
|
||||||
|
CN: '早上好',
|
||||||
|
TW: '早晨啊',
|
||||||
|
US: 'Good morning',
|
||||||
|
},{
|
||||||
|
CN: '上午好',
|
||||||
|
TW: '上午好',
|
||||||
|
US: 'Good morning',
|
||||||
|
},{
|
||||||
|
CN: '中午好',
|
||||||
|
TW: '中午好',
|
||||||
|
US: 'Good afternoon',
|
||||||
|
},{
|
||||||
|
CN: '下午好',
|
||||||
|
TW: '下午好',
|
||||||
|
US: 'Good afternoon',
|
||||||
|
},{
|
||||||
|
CN: '晚上好',
|
||||||
|
TW: '晚上好',
|
||||||
|
US: 'Good evening',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
Random.extend({
|
||||||
|
admin () {
|
||||||
|
return this.pick(admins)
|
||||||
|
},
|
||||||
|
timeFix () {
|
||||||
|
const time = new Date()
|
||||||
|
const hour = time.getHours()
|
||||||
|
return hour < 9
|
||||||
|
? timeList[0] : (hour <= 11 ? timeList[1] : (hour <= 13 ? timeList[2] : (hour <= 20 ? timeList[3] : timeList[4])))
|
||||||
|
},
|
||||||
|
avatar () {
|
||||||
|
return this.pick(avatars)
|
||||||
|
},
|
||||||
|
position () {
|
||||||
|
return this.pick(positions)
|
||||||
|
}
|
||||||
|
})
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
import Mock from 'mockjs'
|
||||||
|
import '@/mock/user/login'
|
||||||
|
import '@/mock/user/routes'
|
||||||
|
|
||||||
|
// 设置全局延时
|
||||||
|
Mock.setup({
|
||||||
|
timeout: '300-600'
|
||||||
|
})
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
import Mock from 'mockjs'
|
||||||
|
|
||||||
|
Mock.mock(`${process.env.VUE_APP_API_BASE_URL}/auth/getRoute`, 'get', () => {
|
||||||
|
console.log('mock routes')
|
||||||
|
let result = {}
|
||||||
|
result.code = 0
|
||||||
|
result.data = [{
|
||||||
|
router: 'root',
|
||||||
|
children: ['demo',
|
||||||
|
{
|
||||||
|
router: 'parent1',
|
||||||
|
children: [{
|
||||||
|
router: 'demo',
|
||||||
|
name: 'demo1',
|
||||||
|
authority: {
|
||||||
|
permission: 'demo',
|
||||||
|
role: 'admin'
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: 'parent2',
|
||||||
|
children: [{
|
||||||
|
router: 'demo',
|
||||||
|
name: 'demo2'
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: 'exception',
|
||||||
|
children: ['exp404', 'exp403', 'exp500'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: 'demo',
|
||||||
|
icon: 'file-ppt',
|
||||||
|
path: 'auth/demo',
|
||||||
|
name: '验权页面',
|
||||||
|
authority: {
|
||||||
|
permission: 'form',
|
||||||
|
role: 'manager'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
return result
|
||||||
|
})
|
||||||
@ -0,0 +1,163 @@
|
|||||||
|
<template>
|
||||||
|
<a-card>
|
||||||
|
<div>
|
||||||
|
<a-space class="operator">
|
||||||
|
</a-space>
|
||||||
|
<standard-table
|
||||||
|
:columns="columns"
|
||||||
|
:dataSource="dataSource"
|
||||||
|
:pagination="pagination"
|
||||||
|
@clear="onClear"
|
||||||
|
@change="onChange"
|
||||||
|
@selectedRowChange="onSelectChange"
|
||||||
|
@showSizeChange="onShowSizeChange"
|
||||||
|
:scroll="{x: 600}"
|
||||||
|
>
|
||||||
|
<div slot="description" slot-scope="{text}">
|
||||||
|
{{text}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div slot="action" slot-scope="{text, record}">
|
||||||
|
<a style="margin-right: 8px">
|
||||||
|
<a-icon type="edit"/>編輯
|
||||||
|
</a>
|
||||||
|
<a @click="deleteRecord(record.key)">
|
||||||
|
<a-icon type="delete" />刪除
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<router-link :to="`/list/query/detail/${record.key}`" >
|
||||||
|
<a-icon type="delete" />詳情
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template slot="statusTitle">
|
||||||
|
<a-icon @click.native="onStatusTitleClick" type="info-circle" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</standard-table>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import StandardTable from '@/components/table/StandardTable'
|
||||||
|
|
||||||
|
import { getAdminLogs } from '@/services/admin'
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '編號',
|
||||||
|
width: 50,
|
||||||
|
// dataIndex: 'id'
|
||||||
|
customRender: (text, record, index) => `${index + 1}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作者',
|
||||||
|
dataIndex: 'admin_name'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作日期',
|
||||||
|
dataIndex: 'time'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'IP位址',
|
||||||
|
dataIndex: 'ip',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作紀錄',
|
||||||
|
dataIndex: 'content',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AdminLog',
|
||||||
|
components: {StandardTable},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
advanced: true,
|
||||||
|
columns: columns,
|
||||||
|
pagination: {
|
||||||
|
size: 'small',
|
||||||
|
current: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
showSizeChanger: true,
|
||||||
|
showQuickJumper: true,
|
||||||
|
showTotal: total => `共 ${total} 筆資料`
|
||||||
|
},
|
||||||
|
dataSource: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created(){
|
||||||
|
this.genTable()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async genTable(){
|
||||||
|
try{
|
||||||
|
const {data : res} = await getAdminLogs({
|
||||||
|
current: this.pagination.current,
|
||||||
|
size: this.pagination.pageSize,
|
||||||
|
search: this.search
|
||||||
|
})
|
||||||
|
this.pagination.total = res.total
|
||||||
|
this.dataSource = res.data
|
||||||
|
}catch (e){
|
||||||
|
this.dataSource = []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deleteRecord(key) {
|
||||||
|
this.dataSource = this.dataSource.filter(item => item.key !== key)
|
||||||
|
},
|
||||||
|
toggleAdvanced () {
|
||||||
|
this.advanced = !this.advanced
|
||||||
|
},
|
||||||
|
remove () {
|
||||||
|
},
|
||||||
|
onClear() {
|
||||||
|
this.$message.info('您清空了勾選的所有行')
|
||||||
|
},
|
||||||
|
onStatusTitleClick() {
|
||||||
|
this.$message.info('你點選了狀態列表頭')
|
||||||
|
},
|
||||||
|
onChange() {
|
||||||
|
this.$message.info('表格狀態改變了')
|
||||||
|
},
|
||||||
|
onSelectChange() {
|
||||||
|
this.$message.info('選中行改變了')
|
||||||
|
},
|
||||||
|
addNew () {
|
||||||
|
this.dataSource.unshift({
|
||||||
|
key: this.dataSource.length,
|
||||||
|
no: 'NO ' + this.dataSource.length,
|
||||||
|
description: '這是一段描述',
|
||||||
|
callNo: Math.floor(Math.random() * 1000),
|
||||||
|
status: Math.floor(Math.random() * 10) % 4,
|
||||||
|
updatedAt: '2018-07-26'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleMenuClick (e) {
|
||||||
|
if (e.key === 'delete') {
|
||||||
|
this.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.search{
|
||||||
|
margin-bottom: 54px;
|
||||||
|
}
|
||||||
|
.fold{
|
||||||
|
width: calc(100% - 216px);
|
||||||
|
display: inline-block
|
||||||
|
}
|
||||||
|
.operator{
|
||||||
|
// margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 900px) {
|
||||||
|
.fold {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,196 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-card>
|
||||||
|
<div>
|
||||||
|
<a-space class="operator">
|
||||||
|
<a-button @click="handleAddDraw" type="primary" size="small">新增</a-button>
|
||||||
|
</a-space>
|
||||||
|
<standard-table
|
||||||
|
:columns="columns"
|
||||||
|
:dataSource="dataSource"
|
||||||
|
:pagination="false"
|
||||||
|
@clear="onClear"
|
||||||
|
@change="onChange"
|
||||||
|
@selectedRowChange="onSelectChange"
|
||||||
|
:scroll="{x: 600}"
|
||||||
|
>
|
||||||
|
|
||||||
|
<div slot="action" slot-scope="{text, record}">
|
||||||
|
<a class="edit-btn" @click="handleEditDraw(record.id)" style="margin-right: 8px">
|
||||||
|
<a-icon type="edit"/>編輯
|
||||||
|
</a>
|
||||||
|
<a class="delete-btn" @click="deleteRole(record.id)">
|
||||||
|
<a-icon type="delete" />刪除
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</standard-table>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
<add-form
|
||||||
|
:visible="showAddDraw"
|
||||||
|
@close="onAddDrawClose"
|
||||||
|
@submit="onAddSubmit"
|
||||||
|
>
|
||||||
|
</add-form>
|
||||||
|
<edit-form
|
||||||
|
:editid="editId"
|
||||||
|
:visible="showEditDraw"
|
||||||
|
@close="onEditDrawClose"
|
||||||
|
@submit="onEditSubmit"
|
||||||
|
>
|
||||||
|
</edit-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import StandardTable from '@/components/table/StandardTable'
|
||||||
|
import AddForm from './components/AddForm.vue'
|
||||||
|
import EditForm from './components/EditForm.vue'
|
||||||
|
|
||||||
|
import {getRoles, deleteRole} from '@/services/role'
|
||||||
|
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '#',
|
||||||
|
width: 50,
|
||||||
|
customRender: (text, record, index) => `${index + 1}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '角色名稱',
|
||||||
|
width: 200,
|
||||||
|
dataIndex: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '角色描述',
|
||||||
|
dataIndex: 'desc',
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
width: 180,
|
||||||
|
scopedSlots: { customRender: 'action' }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'RoleList',
|
||||||
|
i18n: require('./i18n'),
|
||||||
|
components: {StandardTable, AddForm, EditForm},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
advanced: true,
|
||||||
|
editId: 0,
|
||||||
|
columns: columns,
|
||||||
|
showAddDraw: false,
|
||||||
|
showEditDraw: false,
|
||||||
|
dataSource: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async mounted(){
|
||||||
|
this.genTable()
|
||||||
|
},
|
||||||
|
authorize: {
|
||||||
|
// deleteRecord: 'delete'
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async genTable(){
|
||||||
|
try{
|
||||||
|
const res = await getRoles()
|
||||||
|
this.dataSource = res.data.data
|
||||||
|
|
||||||
|
}catch (e){
|
||||||
|
this.dataSource = []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deleteRole(id) {
|
||||||
|
console.log(id)
|
||||||
|
this.$confirm({
|
||||||
|
title: '確認刪除?',
|
||||||
|
content: '確定刪除此筆資料',
|
||||||
|
okText: '確定',
|
||||||
|
okType: 'danger',
|
||||||
|
cancelText: '取消',
|
||||||
|
onOk: async () => {
|
||||||
|
let res = await deleteRole({id:id})
|
||||||
|
if(res.code === 200){
|
||||||
|
this.genTable()
|
||||||
|
this.$message.success('刪除成功')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onCancel() {
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
toggleAdvanced () {
|
||||||
|
this.advanced = !this.advanced
|
||||||
|
},
|
||||||
|
remove () {
|
||||||
|
},
|
||||||
|
onClear() {
|
||||||
|
this.$message.info('您清空了勾選的所有行')
|
||||||
|
},
|
||||||
|
onStatusTitleClick() {
|
||||||
|
this.$message.info('你點選了狀態列表頭')
|
||||||
|
},
|
||||||
|
onChange() {
|
||||||
|
this.$message.info('表格狀態改變了')
|
||||||
|
},
|
||||||
|
onSelectChange() {
|
||||||
|
this.$message.info('選中行改變了')
|
||||||
|
},
|
||||||
|
//新增
|
||||||
|
handleAddDraw () {
|
||||||
|
this.showAddDraw = true
|
||||||
|
},
|
||||||
|
onAddDrawClose() {
|
||||||
|
this.showAddDraw = false
|
||||||
|
},
|
||||||
|
onAddSubmit () {
|
||||||
|
this.genTable()
|
||||||
|
this.showAddDraw = false
|
||||||
|
this.$message.success('新增成功')
|
||||||
|
},
|
||||||
|
//編輯
|
||||||
|
handleEditDraw(id) {
|
||||||
|
this.editId = id
|
||||||
|
this.showEditDraw = true
|
||||||
|
},
|
||||||
|
onEditDrawClose() {
|
||||||
|
this.editId=0
|
||||||
|
this.showEditDraw = false
|
||||||
|
},
|
||||||
|
onEditSubmit () {
|
||||||
|
this.editId=0
|
||||||
|
this.genTable()
|
||||||
|
this.showEditDraw = false
|
||||||
|
this.$message.success('編輯成功')
|
||||||
|
},
|
||||||
|
handleMenuClick (e) {
|
||||||
|
if (e.key === 'delete') {
|
||||||
|
this.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.search{
|
||||||
|
margin-bottom: 54px;
|
||||||
|
}
|
||||||
|
.fold{
|
||||||
|
width: calc(100% - 216px);
|
||||||
|
display: inline-block
|
||||||
|
}
|
||||||
|
.operator{
|
||||||
|
// margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 900px) {
|
||||||
|
.fold {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,144 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-drawer
|
||||||
|
title="新增角色"
|
||||||
|
:destroyOnClose="true"
|
||||||
|
:visible="visible"
|
||||||
|
:body-style="{ paddingBottom: '80px' }"
|
||||||
|
@close="onClose"
|
||||||
|
>
|
||||||
|
<a-form-model
|
||||||
|
ref="ruleForm"
|
||||||
|
:model="form"
|
||||||
|
:rules="rules"
|
||||||
|
:label-col="labelCol"
|
||||||
|
:wrapper-col="wrapperCol"
|
||||||
|
>
|
||||||
|
<a-form-model-item label="角色名稱" ref="name" prop="name">
|
||||||
|
<a-input
|
||||||
|
v-model="form.name"
|
||||||
|
@blur="
|
||||||
|
() => {
|
||||||
|
$refs.name.onFieldBlur();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</a-form-model-item>
|
||||||
|
|
||||||
|
<a-form-model-item label="角色描述">
|
||||||
|
<a-input v-model="form.desc" type="textarea" />
|
||||||
|
</a-form-model-item>
|
||||||
|
<!-- <a-form-model-item label="角色權限">
|
||||||
|
<a-tree
|
||||||
|
v-model="form.permission"
|
||||||
|
checkable
|
||||||
|
:tree-data="treeData"
|
||||||
|
defaultExpandAll
|
||||||
|
@select="onSelect"
|
||||||
|
@check="onCheck"
|
||||||
|
>
|
||||||
|
<span slot="title0010" style="color: #1890ff">sss</span>
|
||||||
|
</a-tree>
|
||||||
|
</a-form-model-item> -->
|
||||||
|
|
||||||
|
</a-form-model>
|
||||||
|
<div
|
||||||
|
:style="{
|
||||||
|
position: 'absolute',
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
width: '100%',
|
||||||
|
borderTop: '1px solid #e9e9e9',
|
||||||
|
padding: '10px 16px',
|
||||||
|
background: '#fff',
|
||||||
|
textAlign: 'right',
|
||||||
|
zIndex: 1,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<a-button :style="{ marginRight: '8px' }" @click="onClose">
|
||||||
|
關閉
|
||||||
|
</a-button>
|
||||||
|
<a-button type="primary" @click="onSubmit">
|
||||||
|
送出
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</a-drawer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {addRole} from '@/services/role'
|
||||||
|
// import {getTree} from '@/services/right'
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AddForm',
|
||||||
|
data(){
|
||||||
|
return {
|
||||||
|
labelCol: { span: 8 },
|
||||||
|
wrapperCol: { span: 16 },
|
||||||
|
treeData: [],
|
||||||
|
form: {
|
||||||
|
name:'',
|
||||||
|
desc:'',
|
||||||
|
permission: [],
|
||||||
|
status: true,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
name: [
|
||||||
|
{ required: true, message: '請輸入角色名稱', trigger: 'blur' },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
visible: Boolean
|
||||||
|
},
|
||||||
|
async created(){
|
||||||
|
// let res = await getTree()
|
||||||
|
// console.log(res.data)
|
||||||
|
// this.treeData = res.data
|
||||||
|
},
|
||||||
|
methods:{
|
||||||
|
onClose(){
|
||||||
|
this.$refs.ruleForm.resetFields();
|
||||||
|
this.$emit('close',true)
|
||||||
|
},
|
||||||
|
onSubmit(){
|
||||||
|
console.log(this.form)
|
||||||
|
this.$refs.ruleForm.validate(async valid => {
|
||||||
|
if (valid) {
|
||||||
|
let res = await addRole(this.form)
|
||||||
|
if(res.code===200){
|
||||||
|
this.$refs.ruleForm.resetFields();
|
||||||
|
this.$emit('submit',true)
|
||||||
|
}else{
|
||||||
|
this.$message.error('新增失敗')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onSelect(selectedKeys, info) {
|
||||||
|
console.log('selected', selectedKeys, info);
|
||||||
|
},
|
||||||
|
onCheck(checkedKeys, info) {
|
||||||
|
console.log('onCheck', checkedKeys, info);
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.ant-drawer-header{
|
||||||
|
background-color: #87e8de !important;
|
||||||
|
.ant-drawer-title{
|
||||||
|
color: #FFF !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.ant-drawer-content-wrapper{
|
||||||
|
width: 50% !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,158 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-drawer
|
||||||
|
title="編輯角色"
|
||||||
|
:destroyOnClose="true"
|
||||||
|
:visible="visible"
|
||||||
|
:body-style="{ paddingBottom: '80px' }"
|
||||||
|
@close="onClose"
|
||||||
|
>
|
||||||
|
<a-spin :spinning="spinning">
|
||||||
|
<div class="spin-content">
|
||||||
|
<a-form-model
|
||||||
|
ref="ruleForm"
|
||||||
|
:model="form"
|
||||||
|
:rules="rules"
|
||||||
|
:label-col="labelCol"
|
||||||
|
:wrapper-col="wrapperCol"
|
||||||
|
>
|
||||||
|
<a-form-model-item label="角色名稱" ref="name" prop="name">
|
||||||
|
<a-input
|
||||||
|
v-model="form.name"
|
||||||
|
@blur="
|
||||||
|
() => {
|
||||||
|
$refs.name.onFieldBlur();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</a-form-model-item>
|
||||||
|
|
||||||
|
<a-form-model-item label="角色描述">
|
||||||
|
<a-input v-model="form.desc" type="textarea" />
|
||||||
|
</a-form-model-item>
|
||||||
|
<!-- <a-form-model-item label="角色權限">
|
||||||
|
<a-tree
|
||||||
|
v-model="form.permission"
|
||||||
|
checkable
|
||||||
|
:tree-data="treeData"
|
||||||
|
defaultExpandAll
|
||||||
|
@select="onSelect"
|
||||||
|
@check="onCheck"
|
||||||
|
>
|
||||||
|
<span slot="title0010" style="color: #1890ff">sss</span>
|
||||||
|
</a-tree>
|
||||||
|
</a-form-model-item> -->
|
||||||
|
|
||||||
|
</a-form-model>
|
||||||
|
</div>
|
||||||
|
</a-spin>
|
||||||
|
<div
|
||||||
|
:style="{
|
||||||
|
position: 'absolute',
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
width: '100%',
|
||||||
|
borderTop: '1px solid #e9e9e9',
|
||||||
|
padding: '10px 16px',
|
||||||
|
background: '#fff',
|
||||||
|
textAlign: 'right',
|
||||||
|
zIndex: 1,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<a-button :style="{ marginRight: '8px' }" @click="onClose">
|
||||||
|
關閉
|
||||||
|
</a-button>
|
||||||
|
<a-button type="primary" @click="onSubmit">
|
||||||
|
送出
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</a-drawer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {getRoleById, updateRole} from '@/services/role'
|
||||||
|
// import {getTree} from '@/services/right'
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'EditForm',
|
||||||
|
data(){
|
||||||
|
return {
|
||||||
|
spinning: false,
|
||||||
|
labelCol: { span: 8 },
|
||||||
|
wrapperCol: { span: 16 },
|
||||||
|
treeData: [],
|
||||||
|
form: {},
|
||||||
|
rules: {
|
||||||
|
name: [
|
||||||
|
{ required: true, message: '請輸入角色名稱', trigger: 'blur' },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
visible: Boolean,
|
||||||
|
editid: Number,
|
||||||
|
},
|
||||||
|
async created(){
|
||||||
|
// let res = await getTree()
|
||||||
|
// console.log(res.data)
|
||||||
|
// this.treeData = res.data
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
editid: async function (val) {
|
||||||
|
if(val){
|
||||||
|
this.spinning=true
|
||||||
|
let res=await getRoleById({id: val})
|
||||||
|
this.form = res.data
|
||||||
|
this.spinning=false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods:{
|
||||||
|
onClose(){
|
||||||
|
this.form={}
|
||||||
|
this.$refs.ruleForm.resetFields();
|
||||||
|
this.$emit('close',true)
|
||||||
|
},
|
||||||
|
onSubmit(){
|
||||||
|
console.log(this.form)
|
||||||
|
this.$refs.ruleForm.validate(async valid => {
|
||||||
|
if (valid || 1===2) {
|
||||||
|
let res = await updateRole(this.form)
|
||||||
|
if(res.code===200){
|
||||||
|
this.$refs.ruleForm.resetFields();
|
||||||
|
this.$emit('submit',true)
|
||||||
|
}else{
|
||||||
|
this.$message.error('編輯失敗')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onSelect(selectedKeys, info) {
|
||||||
|
console.log('selected', selectedKeys, info);
|
||||||
|
},
|
||||||
|
onCheck(checkedKeys, info) {
|
||||||
|
// console.log(checkedKeys.concat(info.halfCheckedKeys))
|
||||||
|
// this.form.permission=checkedKeys.concat(info.halfCheckedKeys)
|
||||||
|
console.log('onCheck', checkedKeys, info);
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.ant-drawer-header{
|
||||||
|
background-color: #87e8de !important;
|
||||||
|
.ant-drawer-title{
|
||||||
|
color: #FFF !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.ant-drawer-content-wrapper{
|
||||||
|
width: 50% !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue