add prettier and deep fixed

This commit is contained in:
unknown 2019-06-12 17:33:02 +08:00
parent 0f5ea1867a
commit 521316e256
204 changed files with 8795 additions and 7015 deletions

View File

@ -1,198 +1,38 @@
module.exports = { module.exports = {
root: true, root: true,
parserOptions: {
parser: 'babel-eslint',
sourceType: 'module'
},
env: { env: {
browser: true, browser: true,
node: true, node: true,
es6: true, es6: true
},
parser: "vue-eslint-parser",
parserOptions: {
parser: "@typescript-eslint/parser",
sourceType: "module"
}, },
extends: ['plugin:vue/recommended', 'eslint:recommended'],
// add your custom rules here plugins: [
//it is base on https://github.com/vuejs/eslint-config-vue "vue",
"@typescript-eslint",
"jsdoc",
"eslint-comments",
"prettier"
],
rules: { rules: {
"vue/max-attributes-per-line": [2, { "no-console": process.env.NODE_ENV === "production" ? "error" : "off",
"singleline": 10, "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
"multiline": {
"max": 1, "prettier/prettier": ["error"]
"allowFirstLine": false },
} extends: ["plugin:vue/essential", "prettier"] // activate vue related rules
}], // extends: [
"vue/singleline-html-element-content-newline": "off", // "eslint:recommended",
"vue/multiline-html-element-content-newline":"off", // "plugin:vue/recommended",
"vue/name-property-casing": ["error", "PascalCase"], // "plugin:vue/base",
"vue/no-v-html": "off", // "@vue/standard",
'accessor-pairs': 2, // "@vue/typescript",
'arrow-spacing': [2, { // "plugin:prettier/recommended"
'before': true, // ]
'after': true };
}],
'block-spacing': [2, 'always'],
'brace-style': [2, '1tbs', {
'allowSingleLine': true
}],
'camelcase': [0, {
'properties': 'always'
}],
'comma-dangle': [2, 'never'],
'comma-spacing': [2, {
'before': false,
'after': true
}],
'comma-style': [2, 'last'],
'constructor-super': 2,
'curly': [2, 'multi-line'],
'dot-location': [2, 'property'],
'eol-last': 2,
'eqeqeq': ["error", "always", {"null": "ignore"}],
'generator-star-spacing': [2, {
'before': true,
'after': true
}],
'handle-callback-err': [2, '^(err|error)$'],
'indent': [2, 2, {
'SwitchCase': 1
}],
'jsx-quotes': [2, 'prefer-single'],
'key-spacing': [2, {
'beforeColon': false,
'afterColon': true
}],
'keyword-spacing': [2, {
'before': true,
'after': true
}],
'new-cap': [2, {
'newIsCap': true,
'capIsNew': false
}],
'new-parens': 2,
'no-array-constructor': 2,
'no-caller': 2,
'no-console': 'off',
'no-class-assign': 2,
'no-cond-assign': 2,
'no-const-assign': 2,
'no-control-regex': 0,
'no-delete-var': 2,
'no-dupe-args': 2,
'no-dupe-class-members': 2,
'no-dupe-keys': 2,
'no-duplicate-case': 2,
'no-empty-character-class': 2,
'no-empty-pattern': 2,
'no-eval': 2,
'no-ex-assign': 2,
'no-extend-native': 2,
'no-extra-bind': 2,
'no-extra-boolean-cast': 2,
'no-extra-parens': [2, 'functions'],
'no-fallthrough': 2,
'no-floating-decimal': 2,
'no-func-assign': 2,
'no-implied-eval': 2,
'no-inner-declarations': [2, 'functions'],
'no-invalid-regexp': 2,
'no-irregular-whitespace': 2,
'no-iterator': 2,
'no-label-var': 2,
'no-labels': [2, {
'allowLoop': false,
'allowSwitch': false
}],
'no-lone-blocks': 2,
'no-mixed-spaces-and-tabs': 2,
'no-multi-spaces': 2,
'no-multi-str': 2,
'no-multiple-empty-lines': [2, {
'max': 1
}],
'no-native-reassign': 2,
'no-negated-in-lhs': 2,
'no-new-object': 2,
'no-new-require': 2,
'no-new-symbol': 2,
'no-new-wrappers': 2,
'no-obj-calls': 2,
'no-octal': 2,
'no-octal-escape': 2,
'no-path-concat': 2,
'no-proto': 2,
'no-redeclare': 2,
'no-regex-spaces': 2,
'no-return-assign': [2, 'except-parens'],
'no-self-assign': 2,
'no-self-compare': 2,
'no-sequences': 2,
'no-shadow-restricted-names': 2,
'no-spaced-func': 2,
'no-sparse-arrays': 2,
'no-this-before-super': 2,
'no-throw-literal': 2,
'no-trailing-spaces': 2,
'no-undef': 2,
'no-undef-init': 2,
'no-unexpected-multiline': 2,
'no-unmodified-loop-condition': 2,
'no-unneeded-ternary': [2, {
'defaultAssignment': false
}],
'no-unreachable': 2,
'no-unsafe-finally': 2,
'no-unused-vars': [2, {
'vars': 'all',
'args': 'none'
}],
'no-useless-call': 2,
'no-useless-computed-key': 2,
'no-useless-constructor': 2,
'no-useless-escape': 0,
'no-whitespace-before-property': 2,
'no-with': 2,
'one-var': [2, {
'initialized': 'never'
}],
'operator-linebreak': [2, 'after', {
'overrides': {
'?': 'before',
':': 'before'
}
}],
'padded-blocks': [2, 'never'],
'quotes': [2, 'single', {
'avoidEscape': true,
'allowTemplateLiterals': true
}],
'semi': [2, 'never'],
'semi-spacing': [2, {
'before': false,
'after': true
}],
'space-before-blocks': [2, 'always'],
'space-before-function-paren': [2, 'never'],
'space-in-parens': [2, 'never'],
'space-infix-ops': 2,
'space-unary-ops': [2, {
'words': true,
'nonwords': false
}],
'spaced-comment': [2, 'always', {
'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ',']
}],
'template-curly-spacing': [2, 'never'],
'use-isnan': 2,
'valid-typeof': 2,
'wrap-iife': [2, 'any'],
'yield-star-spacing': [2, 'both'],
'yoda': [2, 'never'],
'prefer-const': 2,
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
'object-curly-spacing': [2, 'always', {
objectsInObjects: false
}],
'array-bracket-spacing': [2, 'never']
}
}

6
.prettierignore Normal file
View File

@ -0,0 +1,6 @@
.prettierrc.js
.eslintrc.js
jest.config.js
vue.config.js
postcss.config.js
settings.js

8
.prettierrc.js Normal file
View File

@ -0,0 +1,8 @@
/** eslint-disable */
module.exports =
{
"endOfLine":"auto"
}

View File

@ -1,51 +1,64 @@
import Mock from 'mockjs' import Mock from "mockjs";
const List = [] const List = [];
const count = 100 const count = 100;
const baseContent = '<p>I am testing data, I am testing data.</p><p><img src="https://wpimg.wallstcn.com/4c69009c-0fd4-4153-b112-6cb53d1cf943"></p>' const baseContent =
const image_uri = 'https://wpimg.wallstcn.com/e4558086-631c-425c-9430-56ffb46e70b3' '<p>I am testing data, I am testing data.</p><p><img src="https://wpimg.wallstcn.com/4c69009c-0fd4-4153-b112-6cb53d1cf943"></p>';
const image_uri =
"https://wpimg.wallstcn.com/e4558086-631c-425c-9430-56ffb46e70b3";
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
List.push(Mock.mock({ List.push(
id: '@increment', Mock.mock({
timestamp: +Mock.Random.date('T'), id: "@increment",
author: '@first', timestamp: +Mock.Random.date("T"),
reviewer: '@first', author: "@first",
title: '@title(5, 10)', reviewer: "@first",
content_short: 'mock data', title: "@title(5, 10)",
content_short: "mock data",
content: baseContent, content: baseContent,
forecast: '@float(0, 100, 2, 2)', forecast: "@float(0, 100, 2, 2)",
importance: '@integer(1, 3)', importance: "@integer(1, 3)",
'type|1': ['CN', 'US', 'JP', 'EU'], "type|1": ["CN", "US", "JP", "EU"],
'status|1': ['published', 'draft', 'deleted'], "status|1": ["published", "draft", "deleted"],
display_time: '@datetime', display_time: "@datetime",
comment_disabled: true, comment_disabled: true,
pageviews: '@integer(300, 5000)', pageviews: "@integer(300, 5000)",
image_uri, image_uri,
platforms: ['a-platform'] platforms: ["a-platform"]
})) })
);
} }
export default [ export default [
{ {
url: '/article/list', url: "/article/list",
type: 'get', type: "get",
response: config => { response: config => {
const { importance, type, title, page = 1, limit = 20, sort } = config.query const {
importance,
type,
title,
page = 1,
limit = 20,
sort
} = config.query;
let mockList = List.filter(item => { let mockList = List.filter(item => {
if (importance && item.importance !== +importance) return false if (importance && item.importance !== +importance) return false;
if (type && item.type !== type) return false if (type && item.type !== type) return false;
if (title && item.title.indexOf(title) < 0) return false if (title && item.title.indexOf(title) < 0) return false;
return true return true;
}) });
if (sort === '-id') { if (sort === "-id") {
mockList = mockList.reverse() mockList = mockList.reverse();
} }
const pageList = mockList.filter((item, index) => index < limit * page && index >= limit * (page - 1)) const pageList = mockList.filter(
(item, index) => index < limit * page && index >= limit * (page - 1)
);
return { return {
code: 20000, code: 20000,
@ -53,64 +66,63 @@ export default [
total: mockList.length, total: mockList.length,
items: pageList items: pageList
} }
} };
} }
}, },
{ {
url: '/article/detail', url: "/article/detail",
type: 'get', type: "get",
response: config => { response: config => {
const { id } = config.query const { id } = config.query;
for (const article of List) { for (const article of List) {
if (article.id === +id) { if (article.id === +id) {
return { return {
code: 20000, code: 20000,
data: article data: article
} };
} }
} }
} }
}, },
{ {
url: '/article/pv', url: "/article/pv",
type: 'get', type: "get",
response: _ => { response: _ => {
return { return {
code: 20000, code: 20000,
data: { data: {
pvData: [ pvData: [
{ key: 'PC', pv: 1024 }, { key: "PC", pv: 1024 },
{ key: 'mobile', pv: 1024 }, { key: "mobile", pv: 1024 },
{ key: 'ios', pv: 1024 }, { key: "ios", pv: 1024 },
{ key: 'android', pv: 1024 } { key: "android", pv: 1024 }
] ]
} }
} };
} }
}, },
{ {
url: '/article/create', url: "/article/create",
type: 'post', type: "post",
response: _ => { response: _ => {
return { return {
code: 20000, code: 20000,
data: 'success' data: "success"
} };
} }
}, },
{ {
url: '/article/update', url: "/article/update",
type: 'post', type: "post",
response: _ => { response: _ => {
return { return {
code: 20000, code: 20000,
data: 'success' data: "success"
};
} }
} }
} ];
]

View File

@ -1,17 +1,12 @@
import Mock from 'mockjs' import Mock from "mockjs";
import { param2Obj } from '../src/utils' import { param2Obj } from "../src/utils";
import user from './user' import user from "./user";
import role from './role' import role from "./role";
import article from './article' import article from "./article";
import search from './remote-search' import search from "./remote-search";
const mocks = [ const mocks = [...user, ...role, ...article, ...search];
...user,
...role,
...article,
...search
]
// for front mock // for front mock
// please use it cautiously, it will redefine XMLHttpRequest, // please use it cautiously, it will redefine XMLHttpRequest,
@ -19,38 +14,42 @@ const mocks = [
export function mockXHR() { export function mockXHR() {
// mock patch // mock patch
// https://github.com/nuysoft/Mock/issues/300 // https://github.com/nuysoft/Mock/issues/300
Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send;
Mock.XHR.prototype.send = function() { Mock.XHR.prototype.send = function() {
if (this.custom.xhr) { if (this.custom.xhr) {
this.custom.xhr.withCredentials = this.withCredentials || false this.custom.xhr.withCredentials = this.withCredentials || false;
if (this.responseType) { if (this.responseType) {
this.custom.xhr.responseType = this.responseType this.custom.xhr.responseType = this.responseType;
} }
} }
this.proxy_send(...arguments) this.proxy_send(...arguments);
} };
function XHR2ExpressReqWrap(respond) { function XHR2ExpressReqWrap(respond) {
return function(options) { return function(options) {
let result = null let result = null;
if (respond instanceof Function) { if (respond instanceof Function) {
const { body, type, url } = options const { body, type, url } = options;
// https://expressjs.com/en/4x/api.html#req // https://expressjs.com/en/4x/api.html#req
result = respond({ result = respond({
method: type, method: type,
body: JSON.parse(body), body: JSON.parse(body),
query: param2Obj(url) query: param2Obj(url)
}) });
} else { } else {
result = respond result = respond;
}
return Mock.mock(result)
} }
return Mock.mock(result);
};
} }
for (const i of mocks) { for (const i of mocks) {
Mock.mock(new RegExp(i.url), i.type || 'get', XHR2ExpressReqWrap(i.response)) Mock.mock(
new RegExp(i.url),
i.type || "get",
XHR2ExpressReqWrap(i.response)
);
} }
} }
@ -58,13 +57,15 @@ export function mockXHR() {
const responseFake = (url, type, respond) => { const responseFake = (url, type, respond) => {
return { return {
url: new RegExp(`/mock${url}`), url: new RegExp(`/mock${url}`),
type: type || 'get', type: type || "get",
response(req, res) { response(req, res) {
res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond)) res.json(
Mock.mock(respond instanceof Function ? respond(req, res) : respond)
);
} }
} };
} };
export default mocks.map(route => { export default mocks.map(route => {
return responseFake(route.url, route.type, route.response) return responseFake(route.url, route.type, route.response);
}) });

View File

@ -1,68 +1,76 @@
const chokidar = require('chokidar') const chokidar = require("chokidar");
const bodyParser = require('body-parser') const bodyParser = require("body-parser");
const chalk = require('chalk') const chalk = require("chalk");
const path = require('path') const path = require("path");
const mockDir = path.join(process.cwd(), 'mock') const mockDir = path.join(process.cwd(), "mock");
function registerRoutes(app) { function registerRoutes(app) {
let mockLastIndex let mockLastIndex;
const { default: mocks } = require('./index.js') const { default: mocks } = require("./index.js");
for (const mock of mocks) { for (const mock of mocks) {
app[mock.type](mock.url, mock.response) app[mock.type](mock.url, mock.response);
mockLastIndex = app._router.stack.length mockLastIndex = app._router.stack.length;
} }
const mockRoutesLength = Object.keys(mocks).length const mockRoutesLength = Object.keys(mocks).length;
return { return {
mockRoutesLength: mockRoutesLength, mockRoutesLength: mockRoutesLength,
mockStartIndex: mockLastIndex - mockRoutesLength mockStartIndex: mockLastIndex - mockRoutesLength
} };
} }
function unregisterRoutes() { function unregisterRoutes() {
Object.keys(require.cache).forEach(i => { Object.keys(require.cache).forEach(i => {
if (i.includes(mockDir)) { if (i.includes(mockDir)) {
delete require.cache[require.resolve(i)] delete require.cache[require.resolve(i)];
} }
}) });
} }
module.exports = app => { module.exports = app => {
// es6 polyfill // es6 polyfill
require('@babel/register') require("@babel/register");
// parse app.body // parse app.body
// https://expressjs.com/en/4x/api.html#req.body // https://expressjs.com/en/4x/api.html#req.body
app.use(bodyParser.json()) app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ app.use(
bodyParser.urlencoded({
extended: true extended: true
})) })
);
const mockRoutes = registerRoutes(app) const mockRoutes = registerRoutes(app);
var mockRoutesLength = mockRoutes.mockRoutesLength var mockRoutesLength = mockRoutes.mockRoutesLength;
var mockStartIndex = mockRoutes.mockStartIndex var mockStartIndex = mockRoutes.mockStartIndex;
// watch files, hot reload mock server // watch files, hot reload mock server
chokidar.watch(mockDir, { chokidar
.watch(mockDir, {
ignored: /mock-server/, ignored: /mock-server/,
ignoreInitial: true ignoreInitial: true
}).on('all', (event, path) => { })
if (event === 'change' || event === 'add') { .on("all", (event, path) => {
if (event === "change" || event === "add") {
try { try {
// remove mock routes stack // remove mock routes stack
app._router.stack.splice(mockStartIndex, mockRoutesLength) app._router.stack.splice(mockStartIndex, mockRoutesLength);
// clear routes cache // clear routes cache
unregisterRoutes() unregisterRoutes();
const mockRoutes = registerRoutes(app) const mockRoutes = registerRoutes(app);
mockRoutesLength = mockRoutes.mockRoutesLength mockRoutesLength = mockRoutes.mockRoutesLength;
mockStartIndex = mockRoutes.mockStartIndex mockStartIndex = mockRoutes.mockStartIndex;
console.log(chalk.magentaBright(`\n > Mock Server hot reload success! changed ${path}`)) console.log(
chalk.magentaBright(
`\n > Mock Server hot reload success! changed ${path}`
)
);
} catch (error) { } catch (error) {
console.log(chalk.redBright(error)) console.log(chalk.redBright(error));
} }
} }
}) });
} };

View File

@ -1,51 +1,55 @@
import Mock from 'mockjs' import Mock from "mockjs";
const NameList = [] const NameList = [];
const count = 100 const count = 100;
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
NameList.push(Mock.mock({ NameList.push(
name: '@first' Mock.mock({
})) name: "@first"
})
);
} }
NameList.push({ name: 'mock-Pan' }) NameList.push({ name: "mock-Pan" });
export default [ export default [
// username search // username search
{ {
url: '/search/user', url: "/search/user",
type: 'get', type: "get",
response: config => { response: config => {
const { name } = config.query const { name } = config.query;
const mockNameList = NameList.filter(item => { const mockNameList = NameList.filter(item => {
const lowerCaseName = item.name.toLowerCase() const lowerCaseName = item.name.toLowerCase();
return !(name && lowerCaseName.indexOf(name.toLowerCase()) < 0) return !(name && lowerCaseName.indexOf(name.toLowerCase()) < 0);
}) });
return { return {
code: 20000, code: 20000,
data: { items: mockNameList } data: { items: mockNameList }
} };
} }
}, },
// transaction list // transaction list
{ {
url: '/transaction/list', url: "/transaction/list",
type: 'get', type: "get",
response: _ => { response: _ => {
return { return {
code: 20000, code: 20000,
data: { data: {
total: 20, total: 20,
'items|20': [{ "items|20": [
order_no: '@guid()', {
timestamp: +Mock.Random.date('T'), order_no: "@guid()",
username: '@name()', timestamp: +Mock.Random.date("T"),
price: '@float(1000, 15000, 0, 2)', username: "@name()",
'status|1': ['success', 'pending'] price: "@float(1000, 15000, 0, 2)",
}] "status|1": ["success", "pending"]
}
]
}
};
} }
} }
} ];
}
]

View File

@ -1,98 +1,101 @@
import Mock from 'mockjs' import Mock from "mockjs";
import { deepClone } from '../../src/utils/index.js' import { deepClone } from "../../src/utils/index.js";
import { asyncRoutes, constantRoutes } from './routes.js' import { asyncRoutes, constantRoutes } from "./routes.js";
const routes = deepClone([...constantRoutes, ...asyncRoutes]) const routes = deepClone([...constantRoutes, ...asyncRoutes]);
const roles = [ const roles = [
{ {
key: 'admin', key: "admin",
name: 'admin', name: "admin",
description: 'Super Administrator. Have access to view all pages.', description: "Super Administrator. Have access to view all pages.",
routes: routes routes: routes
}, },
{ {
key: 'editor', key: "editor",
name: 'editor', name: "editor",
description: 'Normal Editor. Can see all pages except permission page', description: "Normal Editor. Can see all pages except permission page",
routes: routes.filter(i => i.path !== '/permission')// just a mock routes: routes.filter(i => i.path !== "/permission") // just a mock
}, },
{ {
key: 'visitor', key: "visitor",
name: 'visitor', name: "visitor",
description: 'Just a visitor. Can only see the home page and the document page', description:
routes: [{ "Just a visitor. Can only see the home page and the document page",
path: '', routes: [
redirect: 'dashboard', {
path: "",
redirect: "dashboard",
children: [ children: [
{ {
path: 'dashboard', path: "dashboard",
name: 'Dashboard', name: "Dashboard",
meta: { title: 'dashboard', icon: 'dashboard' } meta: { title: "dashboard", icon: "dashboard" }
} }
] ]
}]
} }
] ]
}
];
export default [ export default [
// mock get all routes form server // mock get all routes form server
{ {
url: '/routes', url: "/routes",
type: 'get', type: "get",
response: _ => { response: _ => {
return { return {
code: 20000, code: 20000,
data: routes data: routes
} };
} }
}, },
// mock get all roles form server // mock get all roles form server
{ {
url: '/roles', url: "/roles",
type: 'get', type: "get",
response: _ => { response: _ => {
return { return {
code: 20000, code: 20000,
data: roles data: roles
} };
} }
}, },
// add role // add role
{ {
url: '/role', url: "/role",
type: 'post', type: "post",
response: { response: {
code: 20000, code: 20000,
data: { data: {
key: Mock.mock('@integer(300, 5000)') key: Mock.mock("@integer(300, 5000)")
} }
} }
}, },
// update role // update role
{ {
url: '/role/[A-Za-z0-9]', url: "/role/[A-Za-z0-9]",
type: 'put', type: "put",
response: { response: {
code: 20000, code: 20000,
data: { data: {
status: 'success' status: "success"
} }
} }
}, },
// delete role // delete role
{ {
url: '/role/[A-Za-z0-9]', url: "/role/[A-Za-z0-9]",
type: 'delete', type: "delete",
response: { response: {
code: 20000, code: 20000,
data: { data: {
status: 'success' status: "success"
} }
} }
} }
] ];

View File

@ -2,524 +2,524 @@
export const constantRoutes = [ export const constantRoutes = [
{ {
path: '/redirect', path: "/redirect",
component: 'layout/Layout', component: "layout/Layout",
hidden: true, hidden: true,
children: [ children: [
{ {
path: '/redirect/:path*', path: "/redirect/:path*",
component: 'views/redirect/index' component: "views/redirect/index"
} }
] ]
}, },
{ {
path: '/login', path: "/login",
component: 'views/login/index', component: "views/login/index",
hidden: true hidden: true
}, },
{ {
path: '/auth-redirect', path: "/auth-redirect",
component: 'views/login/auth-redirect', component: "views/login/auth-redirect",
hidden: true hidden: true
}, },
{ {
path: '/404', path: "/404",
component: 'views/error-page/404', component: "views/error-page/404",
hidden: true hidden: true
}, },
{ {
path: '/401', path: "/401",
component: 'views/error-page/401', component: "views/error-page/401",
hidden: true hidden: true
}, },
{ {
path: '', path: "",
component: 'layout/Layout', component: "layout/Layout",
redirect: 'dashboard', redirect: "dashboard",
children: [ children: [
{ {
path: 'dashboard', path: "dashboard",
component: 'views/dashboard/index', component: "views/dashboard/index",
name: 'Dashboard', name: "Dashboard",
meta: { title: 'Dashboard', icon: 'dashboard', affix: true } meta: { title: "Dashboard", icon: "dashboard", affix: true }
} }
] ]
}, },
{ {
path: '/documentation', path: "/documentation",
component: 'layout/Layout', component: "layout/Layout",
children: [ children: [
{ {
path: 'index', path: "index",
component: 'views/documentation/index', component: "views/documentation/index",
name: 'Documentation', name: "Documentation",
meta: { title: 'Documentation', icon: 'documentation', affix: true } meta: { title: "Documentation", icon: "documentation", affix: true }
} }
] ]
}, },
{ {
path: '/guide', path: "/guide",
component: 'layout/Layout', component: "layout/Layout",
redirect: '/guide/index', redirect: "/guide/index",
children: [ children: [
{ {
path: 'index', path: "index",
component: 'views/guide/index', component: "views/guide/index",
name: 'Guide', name: "Guide",
meta: { title: 'Guide', icon: 'guide', noCache: true } meta: { title: "Guide", icon: "guide", noCache: true }
} }
] ]
} }
] ];
export const asyncRoutes = [ export const asyncRoutes = [
{ {
path: '/permission', path: "/permission",
component: 'layout/Layout', component: "layout/Layout",
redirect: '/permission/index', redirect: "/permission/index",
alwaysShow: true, alwaysShow: true,
meta: { meta: {
title: 'Permission', title: "Permission",
icon: 'lock', icon: "lock",
roles: ['admin', 'editor'] roles: ["admin", "editor"]
}, },
children: [ children: [
{ {
path: 'page', path: "page",
component: 'views/permission/page', component: "views/permission/page",
name: 'PagePermission', name: "PagePermission",
meta: { meta: {
title: 'Page Permission', title: "Page Permission",
roles: ['admin'] roles: ["admin"]
} }
}, },
{ {
path: 'directive', path: "directive",
component: 'views/permission/directive', component: "views/permission/directive",
name: 'DirectivePermission', name: "DirectivePermission",
meta: { meta: {
title: 'Directive Permission' title: "Directive Permission"
} }
}, },
{ {
path: 'role', path: "role",
component: 'views/permission/role', component: "views/permission/role",
name: 'RolePermission', name: "RolePermission",
meta: { meta: {
title: 'Role Permission', title: "Role Permission",
roles: ['admin'] roles: ["admin"]
} }
} }
] ]
}, },
{ {
path: '/icon', path: "/icon",
component: 'layout/Layout', component: "layout/Layout",
children: [ children: [
{ {
path: 'index', path: "index",
component: 'views/icons/index', component: "views/icons/index",
name: 'Icons', name: "Icons",
meta: { title: 'Icons', icon: 'icon', noCache: true } meta: { title: "Icons", icon: "icon", noCache: true }
} }
] ]
}, },
{ {
path: '/components', path: "/components",
component: 'layout/Layout', component: "layout/Layout",
redirect: 'noRedirect', redirect: "noRedirect",
name: 'ComponentDemo', name: "ComponentDemo",
meta: { meta: {
title: 'Components', title: "Components",
icon: 'component' icon: "component"
}, },
children: [ children: [
{ {
path: 'tinymce', path: "tinymce",
component: 'views/components-demo/tinymce', component: "views/components-demo/tinymce",
name: 'TinymceDemo', name: "TinymceDemo",
meta: { title: 'Tinymce' } meta: { title: "Tinymce" }
}, },
{ {
path: 'markdown', path: "markdown",
component: 'views/components-demo/markdown', component: "views/components-demo/markdown",
name: 'MarkdownDemo', name: "MarkdownDemo",
meta: { title: 'Markdown' } meta: { title: "Markdown" }
}, },
{ {
path: 'json-editor', path: "json-editor",
component: 'views/components-demo/json-editor', component: "views/components-demo/json-editor",
name: 'JsonEditorDemo', name: "JsonEditorDemo",
meta: { title: 'Json Editor' } meta: { title: "Json Editor" }
}, },
{ {
path: 'split-pane', path: "split-pane",
component: 'views/components-demo/split-pane', component: "views/components-demo/split-pane",
name: 'SplitpaneDemo', name: "SplitpaneDemo",
meta: { title: 'SplitPane' } meta: { title: "SplitPane" }
}, },
{ {
path: 'avatar-upload', path: "avatar-upload",
component: 'views/components-demo/avatar-upload', component: "views/components-demo/avatar-upload",
name: 'AvatarUploadDemo', name: "AvatarUploadDemo",
meta: { title: 'Avatar Upload' } meta: { title: "Avatar Upload" }
}, },
{ {
path: 'dropzone', path: "dropzone",
component: 'views/components-demo/dropzone', component: "views/components-demo/dropzone",
name: 'DropzoneDemo', name: "DropzoneDemo",
meta: { title: 'Dropzone' } meta: { title: "Dropzone" }
}, },
{ {
path: 'sticky', path: "sticky",
component: 'views/components-demo/sticky', component: "views/components-demo/sticky",
name: 'StickyDemo', name: "StickyDemo",
meta: { title: 'Sticky' } meta: { title: "Sticky" }
}, },
{ {
path: 'count-to', path: "count-to",
component: 'views/components-demo/count-to', component: "views/components-demo/count-to",
name: 'CountToDemo', name: "CountToDemo",
meta: { title: 'Count To' } meta: { title: "Count To" }
}, },
{ {
path: 'mixin', path: "mixin",
component: 'views/components-demo/mixin', component: "views/components-demo/mixin",
name: 'ComponentMixinDemo', name: "ComponentMixinDemo",
meta: { title: 'componentMixin' } meta: { title: "componentMixin" }
}, },
{ {
path: 'back-to-top', path: "back-to-top",
component: 'views/components-demo/back-to-top', component: "views/components-demo/back-to-top",
name: 'BackToTopDemo', name: "BackToTopDemo",
meta: { title: 'Back To Top' } meta: { title: "Back To Top" }
}, },
{ {
path: 'drag-dialog', path: "drag-dialog",
component: 'views/components-demo/drag-dialog', component: "views/components-demo/drag-dialog",
name: 'DragDialogDemo', name: "DragDialogDemo",
meta: { title: 'Drag Dialog' } meta: { title: "Drag Dialog" }
}, },
{ {
path: 'drag-select', path: "drag-select",
component: 'views/components-demo/drag-select', component: "views/components-demo/drag-select",
name: 'DragSelectDemo', name: "DragSelectDemo",
meta: { title: 'Drag Select' } meta: { title: "Drag Select" }
}, },
{ {
path: 'dnd-list', path: "dnd-list",
component: 'views/components-demo/dnd-list', component: "views/components-demo/dnd-list",
name: 'DndListDemo', name: "DndListDemo",
meta: { title: 'Dnd List' } meta: { title: "Dnd List" }
}, },
{ {
path: 'drag-kanban', path: "drag-kanban",
component: 'views/components-demo/drag-kanban', component: "views/components-demo/drag-kanban",
name: 'DragKanbanDemo', name: "DragKanbanDemo",
meta: { title: 'Drag Kanban' } meta: { title: "Drag Kanban" }
} }
] ]
}, },
{ {
path: '/charts', path: "/charts",
component: 'layout/Layout', component: "layout/Layout",
redirect: 'noRedirect', redirect: "noRedirect",
name: 'Charts', name: "Charts",
meta: { meta: {
title: 'Charts', title: "Charts",
icon: 'chart' icon: "chart"
}, },
children: [ children: [
{ {
path: 'keyboard', path: "keyboard",
component: 'views/charts/keyboard', component: "views/charts/keyboard",
name: 'KeyboardChart', name: "KeyboardChart",
meta: { title: 'Keyboard Chart', noCache: true } meta: { title: "Keyboard Chart", noCache: true }
}, },
{ {
path: 'line', path: "line",
component: 'views/charts/line', component: "views/charts/line",
name: 'LineChart', name: "LineChart",
meta: { title: 'Line Chart', noCache: true } meta: { title: "Line Chart", noCache: true }
}, },
{ {
path: 'mixchart', path: "mixchart",
component: 'views/charts/mixChart', component: "views/charts/mixChart",
name: 'MixChart', name: "MixChart",
meta: { title: 'Mix Chart', noCache: true } meta: { title: "Mix Chart", noCache: true }
} }
] ]
}, },
{ {
path: '/nested', path: "/nested",
component: 'layout/Layout', component: "layout/Layout",
redirect: '/nested/menu1/menu1-1', redirect: "/nested/menu1/menu1-1",
name: 'Nested', name: "Nested",
meta: { meta: {
title: 'Nested', title: "Nested",
icon: 'nested' icon: "nested"
}, },
children: [ children: [
{ {
path: 'menu1', path: "menu1",
component: 'views/nested/menu1/index', component: "views/nested/menu1/index",
name: 'Menu1', name: "Menu1",
meta: { title: 'Menu1' }, meta: { title: "Menu1" },
redirect: '/nested/menu1/menu1-1', redirect: "/nested/menu1/menu1-1",
children: [ children: [
{ {
path: 'menu1-1', path: "menu1-1",
component: 'views/nested/menu1/menu1-1', component: "views/nested/menu1/menu1-1",
name: 'Menu1-1', name: "Menu1-1",
meta: { title: 'Menu1-1' } meta: { title: "Menu1-1" }
}, },
{ {
path: 'menu1-2', path: "menu1-2",
component: 'views/nested/menu1/menu1-2', component: "views/nested/menu1/menu1-2",
name: 'Menu1-2', name: "Menu1-2",
redirect: '/nested/menu1/menu1-2/menu1-2-1', redirect: "/nested/menu1/menu1-2/menu1-2-1",
meta: { title: 'Menu1-2' }, meta: { title: "Menu1-2" },
children: [ children: [
{ {
path: 'menu1-2-1', path: "menu1-2-1",
component: 'views/nested/menu1/menu1-2/menu1-2-1', component: "views/nested/menu1/menu1-2/menu1-2-1",
name: 'Menu1-2-1', name: "Menu1-2-1",
meta: { title: 'Menu1-2-1' } meta: { title: "Menu1-2-1" }
}, },
{ {
path: 'menu1-2-2', path: "menu1-2-2",
component: 'views/nested/menu1/menu1-2/menu1-2-2', component: "views/nested/menu1/menu1-2/menu1-2-2",
name: 'Menu1-2-2', name: "Menu1-2-2",
meta: { title: 'Menu1-2-2' } meta: { title: "Menu1-2-2" }
} }
] ]
}, },
{ {
path: 'menu1-3', path: "menu1-3",
component: 'views/nested/menu1/menu1-3', component: "views/nested/menu1/menu1-3",
name: 'Menu1-3', name: "Menu1-3",
meta: { title: 'Menu1-3' } meta: { title: "Menu1-3" }
} }
] ]
}, },
{ {
path: 'menu2', path: "menu2",
name: 'Menu2', name: "Menu2",
component: 'views/nested/menu2/index', component: "views/nested/menu2/index",
meta: { title: 'Menu2' } meta: { title: "Menu2" }
} }
] ]
}, },
{ {
path: '/example', path: "/example",
component: 'layout/Layout', component: "layout/Layout",
redirect: '/example/list', redirect: "/example/list",
name: 'Example', name: "Example",
meta: { meta: {
title: 'Example', title: "Example",
icon: 'example' icon: "example"
}, },
children: [ children: [
{ {
path: 'create', path: "create",
component: 'views/example/create', component: "views/example/create",
name: 'CreateArticle', name: "CreateArticle",
meta: { title: 'Create Article', icon: 'edit' } meta: { title: "Create Article", icon: "edit" }
}, },
{ {
path: 'edit/:id(\\d+)', path: "edit/:id(\\d+)",
component: 'views/example/edit', component: "views/example/edit",
name: 'EditArticle', name: "EditArticle",
meta: { title: 'Edit Article', noCache: true }, meta: { title: "Edit Article", noCache: true },
hidden: true hidden: true
}, },
{ {
path: 'list', path: "list",
component: 'views/example/list', component: "views/example/list",
name: 'ArticleList', name: "ArticleList",
meta: { title: 'Article List', icon: 'list' } meta: { title: "Article List", icon: "list" }
} }
] ]
}, },
{ {
path: '/tab', path: "/tab",
component: 'layout/Layout', component: "layout/Layout",
children: [ children: [
{ {
path: 'index', path: "index",
component: 'views/tab/index', component: "views/tab/index",
name: 'Tab', name: "Tab",
meta: { title: 'Tab', icon: 'tab' } meta: { title: "Tab", icon: "tab" }
} }
] ]
}, },
{ {
path: '/error', path: "/error",
component: 'layout/Layout', component: "layout/Layout",
redirect: 'noRedirect', redirect: "noRedirect",
name: 'ErrorPages', name: "ErrorPages",
meta: { meta: {
title: 'Error Pages', title: "Error Pages",
icon: '404' icon: "404"
}, },
children: [ children: [
{ {
path: '401', path: "401",
component: 'views/error-page/401', component: "views/error-page/401",
name: 'Page401', name: "Page401",
meta: { title: 'Page 401', noCache: true } meta: { title: "Page 401", noCache: true }
}, },
{ {
path: '404', path: "404",
component: 'views/error-page/404', component: "views/error-page/404",
name: 'Page404', name: "Page404",
meta: { title: 'Page 404', noCache: true } meta: { title: "Page 404", noCache: true }
} }
] ]
}, },
{ {
path: '/error-log', path: "/error-log",
component: 'layout/Layout', component: "layout/Layout",
redirect: 'noRedirect', redirect: "noRedirect",
children: [ children: [
{ {
path: 'log', path: "log",
component: 'views/error-log/index', component: "views/error-log/index",
name: 'ErrorLog', name: "ErrorLog",
meta: { title: 'Error Log', icon: 'bug' } meta: { title: "Error Log", icon: "bug" }
} }
] ]
}, },
{ {
path: '/excel', path: "/excel",
component: 'layout/Layout', component: "layout/Layout",
redirect: '/excel/export-excel', redirect: "/excel/export-excel",
name: 'Excel', name: "Excel",
meta: { meta: {
title: 'Excel', title: "Excel",
icon: 'excel' icon: "excel"
}, },
children: [ children: [
{ {
path: 'export-excel', path: "export-excel",
component: 'views/excel/export-excel', component: "views/excel/export-excel",
name: 'ExportExcel', name: "ExportExcel",
meta: { title: 'Export Excel' } meta: { title: "Export Excel" }
}, },
{ {
path: 'export-selected-excel', path: "export-selected-excel",
component: 'views/excel/select-excel', component: "views/excel/select-excel",
name: 'SelectExcel', name: "SelectExcel",
meta: { title: 'Select Excel' } meta: { title: "Select Excel" }
}, },
{ {
path: 'export-merge-header', path: "export-merge-header",
component: 'views/excel/merge-header', component: "views/excel/merge-header",
name: 'MergeHeader', name: "MergeHeader",
meta: { title: 'Merge Header' } meta: { title: "Merge Header" }
}, },
{ {
path: 'upload-excel', path: "upload-excel",
component: 'views/excel/upload-excel', component: "views/excel/upload-excel",
name: 'UploadExcel', name: "UploadExcel",
meta: { title: 'Upload Excel' } meta: { title: "Upload Excel" }
} }
] ]
}, },
{ {
path: '/zip', path: "/zip",
component: 'layout/Layout', component: "layout/Layout",
redirect: '/zip/download', redirect: "/zip/download",
alwaysShow: true, alwaysShow: true,
meta: { title: 'Zip', icon: 'zip' }, meta: { title: "Zip", icon: "zip" },
children: [ children: [
{ {
path: 'download', path: "download",
component: 'views/zip/index', component: "views/zip/index",
name: 'ExportZip', name: "ExportZip",
meta: { title: 'Export Zip' } meta: { title: "Export Zip" }
} }
] ]
}, },
{ {
path: '/pdf', path: "/pdf",
component: 'layout/Layout', component: "layout/Layout",
redirect: '/pdf/index', redirect: "/pdf/index",
children: [ children: [
{ {
path: 'index', path: "index",
component: 'views/pdf/index', component: "views/pdf/index",
name: 'PDF', name: "PDF",
meta: { title: 'PDF', icon: 'pdf' } meta: { title: "PDF", icon: "pdf" }
} }
] ]
}, },
{ {
path: '/pdf/download', path: "/pdf/download",
component: 'views/pdf/download', component: "views/pdf/download",
hidden: true hidden: true
}, },
{ {
path: '/theme', path: "/theme",
component: 'layout/Layout', component: "layout/Layout",
redirect: 'noRedirect', redirect: "noRedirect",
children: [ children: [
{ {
path: 'index', path: "index",
component: 'views/theme/index', component: "views/theme/index",
name: 'Theme', name: "Theme",
meta: { title: 'Theme', icon: 'theme' } meta: { title: "Theme", icon: "theme" }
} }
] ]
}, },
{ {
path: '/clipboard', path: "/clipboard",
component: 'layout/Layout', component: "layout/Layout",
redirect: 'noRedirect', redirect: "noRedirect",
children: [ children: [
{ {
path: 'index', path: "index",
component: 'views/clipboard/index', component: "views/clipboard/index",
name: 'ClipboardDemo', name: "ClipboardDemo",
meta: { title: 'Clipboard Demo', icon: 'clipboard' } meta: { title: "Clipboard Demo", icon: "clipboard" }
} }
] ]
}, },
{ {
path: '/i18n', path: "/i18n",
component: 'layout/Layout', component: "layout/Layout",
children: [ children: [
{ {
path: 'index', path: "index",
component: 'views/i18n-demo/index', component: "views/i18n-demo/index",
name: 'I18n', name: "I18n",
meta: { title: 'I18n', icon: 'international' } meta: { title: "I18n", icon: "international" }
} }
] ]
}, },
{ {
path: 'external-link', path: "external-link",
component: 'layout/Layout', component: "layout/Layout",
children: [ children: [
{ {
path: 'https://github.com/PanJiaChen/vue-element-admin', path: "https://github.com/PanJiaChen/vue-element-admin",
meta: { title: 'External Link', icon: 'link' } meta: { title: "External Link", icon: "link" }
} }
] ]
}, },
{ path: '*', redirect: '/404', hidden: true } { path: "*", redirect: "/404", hidden: true }
] ];

View File

@ -1,84 +1,85 @@
const tokens = { const tokens = {
admin: { admin: {
token: 'admin-token' token: "admin-token"
}, },
editor: { editor: {
token: 'editor-token' token: "editor-token"
} }
} };
const users = { const users = {
'admin-token': { "admin-token": {
roles: ['admin'], roles: ["admin"],
introduction: 'I am a super administrator', introduction: "I am a super administrator",
avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif', avatar:
name: 'Super Admin' "https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif",
name: "Super Admin"
}, },
'editor-token': { "editor-token": {
roles: ['editor'], roles: ["editor"],
introduction: 'I am an editor', introduction: "I am an editor",
avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif', avatar:
name: 'Normal Editor' "https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif",
name: "Normal Editor"
} }
} };
export default [ export default [
// user login // user login
{ {
url: '/user/login', url: "/user/login",
type: 'post', type: "post",
response: config => { response: config => {
const { username } = config.body const { username } = config.body;
const token = tokens[username] const token = tokens[username];
// mock error // mock error
if (!token) { if (!token) {
return { return {
code: 60204, code: 60204,
message: 'Account and password are incorrect.' message: "Account and password are incorrect."
} };
} }
return { return {
code: 20000, code: 20000,
data: token data: token
} };
} }
}, },
// get user info // get user info
{ {
url: '/user/info\.*', url: "/user/info.*",
type: 'get', type: "get",
response: config => { response: config => {
const { token } = config.query const { token } = config.query;
const info = users[token] const info = users[token];
// mock error // mock error
if (!info) { if (!info) {
return { return {
code: 50008, code: 50008,
message: 'Login failed, unable to get user details.' message: "Login failed, unable to get user details."
} };
} }
return { return {
code: 20000, code: 20000,
data: info data: info
} };
} }
}, },
// user logout // user logout
{ {
url: '/user/logout', url: "/user/logout",
type: 'post', type: "post",
response: _ => { response: _ => {
return { return {
code: 20000, code: 20000,
data: 'success' data: "success"
};
} }
} }
} ];
]

View File

@ -13,6 +13,7 @@
"test:unit": "jest --clearCache && vue-cli-service test:unit", "test:unit": "jest --clearCache && vue-cli-service test:unit",
"test:ci": "npm run lint && npm run test:unit", "test:ci": "npm run lint && npm run test:unit",
"svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml", "svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml",
"format": "prettier \"./{src,tests,mock}/{**/*,*}.{ts,spec.ts,vue,js,spec.js}\" --write",
"new": "plop" "new": "plop"
}, },
"husky": { "husky": {
@ -86,7 +87,8 @@
"chokidar": "2.1.5", "chokidar": "2.1.5",
"connect": "3.6.6", "connect": "3.6.6",
"eslint": "5.15.3", "eslint": "5.15.3",
"eslint-plugin-vue": "5.2.2", "eslint-config-prettier": "^4.3.0",
"eslint-plugin-vue": "^5.2.2",
"html-webpack-plugin": "3.2.0", "html-webpack-plugin": "3.2.0",
"husky": "1.3.1", "husky": "1.3.1",
"lint-staged": "8.1.5", "lint-staged": "8.1.5",

View File

@ -6,6 +6,6 @@
<script> <script>
export default { export default {
name: 'App' name: "App"
} };
</script> </script>

View File

@ -1,41 +1,41 @@
import request from '@/utils/request' import request from "@/utils/request";
export function fetchList(query) { export function fetchList(query) {
return request({ return request({
url: '/article/list', url: "/article/list",
method: 'get', method: "get",
params: query params: query
}) });
} }
export function fetchArticle(id) { export function fetchArticle(id) {
return request({ return request({
url: '/article/detail', url: "/article/detail",
method: 'get', method: "get",
params: { id } params: { id }
}) });
} }
export function fetchPv(pv) { export function fetchPv(pv) {
return request({ return request({
url: '/article/pv', url: "/article/pv",
method: 'get', method: "get",
params: { pv } params: { pv }
}) });
} }
export function createArticle(data) { export function createArticle(data) {
return request({ return request({
url: '/article/create', url: "/article/create",
method: 'post', method: "post",
data data
}) });
} }
export function updateArticle(data) { export function updateArticle(data) {
return request({ return request({
url: '/article/update', url: "/article/update",
method: 'post', method: "post",
data data
}) });
} }

View File

@ -1,8 +1,8 @@
import request from '@/utils/request' import request from "@/utils/request";
export function getToken() { export function getToken() {
return request({ return request({
url: '/qiniu/upload/token', // 假地址 自行替换 url: "/qiniu/upload/token", // 假地址 自行替换
method: 'get' method: "get"
}) });
} }

View File

@ -1,17 +1,17 @@
import request from '@/utils/request' import request from "@/utils/request";
export function searchUser(name) { export function searchUser(name) {
return request({ return request({
url: '/search/user', url: "/search/user",
method: 'get', method: "get",
params: { name } params: { name }
}) });
} }
export function transactionList(query) { export function transactionList(query) {
return request({ return request({
url: '/transaction/list', url: "/transaction/list",
method: 'get', method: "get",
params: query params: query
}) });
} }

View File

@ -1,38 +1,38 @@
import request from '@/utils/request' import request from "@/utils/request";
export function getRoutes() { export function getRoutes() {
return request({ return request({
url: '/routes', url: "/routes",
method: 'get' method: "get"
}) });
} }
export function getRoles() { export function getRoles() {
return request({ return request({
url: '/roles', url: "/roles",
method: 'get' method: "get"
}) });
} }
export function addRole(data) { export function addRole(data) {
return request({ return request({
url: '/role', url: "/role",
method: 'post', method: "post",
data data
}) });
} }
export function updateRole(id, data) { export function updateRole(id, data) {
return request({ return request({
url: `/role/${id}`, url: `/role/${id}`,
method: 'put', method: "put",
data data
}) });
} }
export function deleteRole(id) { export function deleteRole(id) {
return request({ return request({
url: `/role/${id}`, url: `/role/${id}`,
method: 'delete' method: "delete"
}) });
} }

View File

@ -1,24 +1,24 @@
import request from '@/utils/request' import request from "@/utils/request";
export function login(data) { export function login(data) {
return request({ return request({
url: '/user/login', url: "/user/login",
method: 'post', method: "post",
data data
}) });
} }
export function getInfo(token) { export function getInfo(token) {
return request({ return request({
url: '/user/info', url: "/user/info",
method: 'get', method: "get",
params: { token } params: { token }
}) });
} }
export function logout() { export function logout() {
return request({ return request({
url: '/user/logout', url: "/user/logout",
method: 'post' method: "post"
}) });
} }

View File

@ -1,14 +1,31 @@
<template> <template>
<transition :name="transitionName"> <transition :name="transitionName">
<div v-show="visible" :style="customStyle" class="back-to-ceiling" @click="backToTop"> <div
<svg width="16" height="16" viewBox="0 0 17 17" xmlns="http://www.w3.org/2000/svg" class="Icon Icon--backToTopArrow" aria-hidden="true" style="height:16px;width:16px"><path d="M12.036 15.59a1 1 0 0 1-.997.995H5.032a.996.996 0 0 1-.997-.996V8.584H1.03c-1.1 0-1.36-.633-.578-1.416L7.33.29a1.003 1.003 0 0 1 1.412 0l6.878 6.88c.782.78.523 1.415-.58 1.415h-3.004v7.004z" /></svg> v-show="visible"
:style="customStyle"
class="back-to-ceiling"
@click="backToTop"
>
<svg
width="16"
height="16"
viewBox="0 0 17 17"
xmlns="http://www.w3.org/2000/svg"
class="Icon Icon--backToTopArrow"
aria-hidden="true"
style="height:16px;width:16px"
>
<path
d="M12.036 15.59a1 1 0 0 1-.997.995H5.032a.996.996 0 0 1-.997-.996V8.584H1.03c-1.1 0-1.36-.633-.578-1.416L7.33.29a1.003 1.003 0 0 1 1.412 0l6.878 6.88c.782.78.523 1.415-.58 1.415h-3.004v7.004z"
/>
</svg>
</div> </div>
</transition> </transition>
</template> </template>
<script> <script>
export default { export default {
name: 'BackToTop', name: "BackToTop",
props: { props: {
visibilityHeight: { visibilityHeight: {
type: Number, type: Number,
@ -22,19 +39,19 @@ export default {
type: Object, type: Object,
default: function() { default: function() {
return { return {
right: '50px', right: "50px",
bottom: '50px', bottom: "50px",
width: '40px', width: "40px",
height: '40px', height: "40px",
'border-radius': '4px', "border-radius": "4px",
'line-height': '45px', "line-height": "45px",
background: '#e7eaf1' background: "#e7eaf1"
} };
} }
}, },
transitionName: { transitionName: {
type: String, type: String,
default: 'fade' default: "fade"
} }
}, },
data() { data() {
@ -42,44 +59,44 @@ export default {
visible: false, visible: false,
interval: null, interval: null,
isMoving: false isMoving: false
} };
}, },
mounted() { mounted() {
window.addEventListener('scroll', this.handleScroll) window.addEventListener("scroll", this.handleScroll);
}, },
beforeDestroy() { beforeDestroy() {
window.removeEventListener('scroll', this.handleScroll) window.removeEventListener("scroll", this.handleScroll);
if (this.interval) { if (this.interval) {
clearInterval(this.interval) clearInterval(this.interval);
} }
}, },
methods: { methods: {
handleScroll() { handleScroll() {
this.visible = window.pageYOffset > this.visibilityHeight this.visible = window.pageYOffset > this.visibilityHeight;
}, },
backToTop() { backToTop() {
if (this.isMoving) return if (this.isMoving) return;
const start = window.pageYOffset const start = window.pageYOffset;
let i = 0 let i = 0;
this.isMoving = true this.isMoving = true;
this.interval = setInterval(() => { this.interval = setInterval(() => {
const next = Math.floor(this.easeInOutQuad(10 * i, start, -start, 500)) const next = Math.floor(this.easeInOutQuad(10 * i, start, -start, 500));
if (next <= this.backPosition) { if (next <= this.backPosition) {
window.scrollTo(0, this.backPosition) window.scrollTo(0, this.backPosition);
clearInterval(this.interval) clearInterval(this.interval);
this.isMoving = false this.isMoving = false;
} else { } else {
window.scrollTo(0, next) window.scrollTo(0, next);
} }
i++ i++;
}, 16.7) }, 16.7);
}, },
easeInOutQuad(t, b, c, d) { easeInOutQuad(t, b, c, d) {
if ((t /= d / 2) < 1) return c / 2 * t * t + b if ((t /= d / 2) < 1) return (c / 2) * t * t + b;
return -c / 2 * (--t * (t - 2) - 1) + b return (-c / 2) * (--t * (t - 2) - 1) + b;
} }
} }
} };
</script> </script>
<style scoped> <style scoped>
@ -96,12 +113,12 @@ export default {
.fade-enter-active, .fade-enter-active,
.fade-leave-active { .fade-leave-active {
transition: opacity .5s; transition: opacity 0.5s;
} }
.fade-enter, .fade-enter,
.fade-leave-to { .fade-leave-to {
opacity: 0 opacity: 0;
} }
.back-to-ceiling .Icon { .back-to-ceiling .Icon {

View File

@ -1,8 +1,12 @@
<template> <template>
<el-breadcrumb class="app-breadcrumb" separator="/"> <el-breadcrumb class="app-breadcrumb" separator="/">
<transition-group name="breadcrumb"> <transition-group name="breadcrumb">
<el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path"> <el-breadcrumb-item v-for="(item, index) in levelList" :key="item.path">
<span v-if="item.redirect==='noRedirect'||index==levelList.length-1" class="no-redirect">{{ item.meta.title }}</span> <span
v-if="item.redirect === 'noRedirect' || index == levelList.length - 1"
class="no-redirect"
>{{ item.meta.title }}</span
>
<a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a> <a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a>
</el-breadcrumb-item> </el-breadcrumb-item>
</transition-group> </transition-group>
@ -10,61 +14,69 @@
</template> </template>
<script> <script>
import pathToRegexp from 'path-to-regexp' import pathToRegexp from "path-to-regexp";
export default { export default {
data() { data() {
return { return {
levelList: null levelList: null
} };
}, },
watch: { watch: {
$route(route) { $route(route) {
// if you go to the redirect page, do not update the breadcrumbs // if you go to the redirect page, do not update the breadcrumbs
if (route.path.startsWith('/redirect/')) { if (route.path.startsWith("/redirect/")) {
return return;
} }
this.getBreadcrumb() this.getBreadcrumb();
} }
}, },
created() { created() {
this.getBreadcrumb() this.getBreadcrumb();
}, },
methods: { methods: {
getBreadcrumb() { getBreadcrumb() {
// only show routes with meta.title // only show routes with meta.title
let matched = this.$route.matched.filter(item => item.meta && item.meta.title) let matched = this.$route.matched.filter(
const first = matched[0] item => item.meta && item.meta.title
);
const first = matched[0];
if (!this.isDashboard(first)) { if (!this.isDashboard(first)) {
matched = [{ path: '/dashboard', meta: { title: 'Dashboard' }}].concat(matched) matched = [{ path: "/dashboard", meta: { title: "Dashboard" } }].concat(
matched
);
} }
this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false) this.levelList = matched.filter(
item => item.meta && item.meta.title && item.meta.breadcrumb !== false
);
}, },
isDashboard(route) { isDashboard(route) {
const name = route && route.name const name = route && route.name;
if (!name) { if (!name) {
return false return false;
} }
return name.trim().toLocaleLowerCase() === 'Dashboard'.toLocaleLowerCase() return (
name.trim().toLocaleLowerCase() === "Dashboard".toLocaleLowerCase()
);
}, },
pathCompile(path) { pathCompile(path) {
// To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561 // To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561
const { params } = this.$route const { params } = this.$route;
var toPath = pathToRegexp.compile(path) var toPath = pathToRegexp.compile(path);
return toPath(params) return toPath(params);
}, },
handleLink(item) { handleLink(item) {
const { redirect, path } = item const { redirect, path } = item;
if (redirect) { if (redirect) {
this.$router.push(redirect) this.$router.push(redirect);
return return;
} }
this.$router.push(this.pathCompile(path)) this.$router.push(this.pathCompile(path));
} }
} }
} };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -1,78 +1,88 @@
<template> <template>
<div :id="id" :class="className" :style="{height:height,width:width}" /> <div :id="id" :class="className" :style="{ height: height, width: width }" />
</template> </template>
<script> <script>
import echarts from 'echarts' import echarts from "echarts";
import resize from './mixins/resize' import resize from "./mixins/resize";
export default { export default {
mixins: [resize], mixins: [resize],
props: { props: {
className: { className: {
type: String, type: String,
default: 'chart' default: "chart"
}, },
id: { id: {
type: String, type: String,
default: 'chart' default: "chart"
}, },
width: { width: {
type: String, type: String,
default: '200px' default: "200px"
}, },
height: { height: {
type: String, type: String,
default: '200px' default: "200px"
} }
}, },
data() { data() {
return { return {
chart: null chart: null
} };
}, },
mounted() { mounted() {
this.initChart() this.initChart();
}, },
beforeDestroy() { beforeDestroy() {
if (!this.chart) { if (!this.chart) {
return return;
} }
this.chart.dispose() this.chart.dispose();
this.chart = null this.chart = null;
}, },
methods: { methods: {
initChart() { initChart() {
this.chart = echarts.init(document.getElementById(this.id)) this.chart = echarts.init(document.getElementById(this.id));
const xAxisData = [] const xAxisData = [];
const data = [] const data = [];
const data2 = [] const data2 = [];
for (let i = 0; i < 50; i++) { for (let i = 0; i < 50; i++) {
xAxisData.push(i) xAxisData.push(i);
data.push((Math.sin(i / 5) * (i / 5 - 10) + i / 6) * 5) data.push((Math.sin(i / 5) * (i / 5 - 10) + i / 6) * 5);
data2.push((Math.sin(i / 5) * (i / 5 + 10) + i / 6) * 3) data2.push((Math.sin(i / 5) * (i / 5 + 10) + i / 6) * 3);
} }
this.chart.setOption({ this.chart.setOption({
backgroundColor: '#08263a', backgroundColor: "#08263a",
grid: { grid: {
left: '5%', left: "5%",
right: '5%' right: "5%"
}, },
xAxis: [{ xAxis: [
{
show: false, show: false,
data: xAxisData data: xAxisData
}, { },
{
show: false, show: false,
data: xAxisData data: xAxisData
}], }
],
visualMap: { visualMap: {
show: false, show: false,
min: 0, min: 0,
max: 50, max: 50,
dimension: 0, dimension: 0,
inRange: { inRange: {
color: ['#4a657a', '#308e92', '#b1cfa5', '#f5d69f', '#f5898b', '#ef5055'] color: [
"#4a657a",
"#308e92",
"#b1cfa5",
"#f5d69f",
"#f5898b",
"#ef5055"
]
} }
}, },
yAxis: { yAxis: {
@ -81,22 +91,23 @@ export default {
}, },
axisLabel: { axisLabel: {
textStyle: { textStyle: {
color: '#4a657a' color: "#4a657a"
} }
}, },
splitLine: { splitLine: {
show: true, show: true,
lineStyle: { lineStyle: {
color: '#08263f' color: "#08263f"
} }
}, },
axisTick: { axisTick: {
show: false show: false
} }
}, },
series: [{ series: [
name: 'back', {
type: 'bar', name: "back",
type: "bar",
data: data2, data: data2,
z: 1, z: 1,
itemStyle: { itemStyle: {
@ -104,33 +115,35 @@ export default {
opacity: 0.4, opacity: 0.4,
barBorderRadius: 5, barBorderRadius: 5,
shadowBlur: 3, shadowBlur: 3,
shadowColor: '#111' shadowColor: "#111"
} }
} }
}, { },
name: 'Simulate Shadow', {
type: 'line', name: "Simulate Shadow",
type: "line",
data, data,
z: 2, z: 2,
showSymbol: false, showSymbol: false,
animationDelay: 0, animationDelay: 0,
animationEasing: 'linear', animationEasing: "linear",
animationDuration: 1200, animationDuration: 1200,
lineStyle: { lineStyle: {
normal: { normal: {
color: 'transparent' color: "transparent"
} }
}, },
areaStyle: { areaStyle: {
normal: { normal: {
color: '#08263a', color: "#08263a",
shadowBlur: 50, shadowBlur: 50,
shadowColor: '#000' shadowColor: "#000"
} }
} }
}, { },
name: 'front', {
type: 'bar', name: "front",
type: "bar",
data, data,
xAxisIndex: 1, xAxisIndex: 1,
z: 3, z: 3,
@ -139,17 +152,18 @@ export default {
barBorderRadius: 5 barBorderRadius: 5
} }
} }
}], }
animationEasing: 'elasticOut', ],
animationEasingUpdate: 'elasticOut', animationEasing: "elasticOut",
animationEasingUpdate: "elasticOut",
animationDelay(idx) { animationDelay(idx) {
return idx * 20 return idx * 20;
}, },
animationDelayUpdate(idx) { animationDelayUpdate(idx) {
return idx * 20 return idx * 20;
} }
}) });
} }
} }
} };
</script> </script>

View File

@ -1,109 +1,125 @@
<template> <template>
<div :id="id" :class="className" :style="{height:height,width:width}" /> <div :id="id" :class="className" :style="{ height: height, width: width }" />
</template> </template>
<script> <script>
import echarts from 'echarts' import echarts from "echarts";
import resize from './mixins/resize' import resize from "./mixins/resize";
export default { export default {
mixins: [resize], mixins: [resize],
props: { props: {
className: { className: {
type: String, type: String,
default: 'chart' default: "chart"
}, },
id: { id: {
type: String, type: String,
default: 'chart' default: "chart"
}, },
width: { width: {
type: String, type: String,
default: '200px' default: "200px"
}, },
height: { height: {
type: String, type: String,
default: '200px' default: "200px"
} }
}, },
data() { data() {
return { return {
chart: null chart: null
} };
}, },
mounted() { mounted() {
this.initChart() this.initChart();
}, },
beforeDestroy() { beforeDestroy() {
if (!this.chart) { if (!this.chart) {
return return;
} }
this.chart.dispose() this.chart.dispose();
this.chart = null this.chart = null;
}, },
methods: { methods: {
initChart() { initChart() {
this.chart = echarts.init(document.getElementById(this.id)) this.chart = echarts.init(document.getElementById(this.id));
this.chart.setOption({ this.chart.setOption({
backgroundColor: '#394056', backgroundColor: "#394056",
title: { title: {
top: 20, top: 20,
text: 'Requests', text: "Requests",
textStyle: { textStyle: {
fontWeight: 'normal', fontWeight: "normal",
fontSize: 16, fontSize: 16,
color: '#F1F1F3' color: "#F1F1F3"
}, },
left: '1%' left: "1%"
}, },
tooltip: { tooltip: {
trigger: 'axis', trigger: "axis",
axisPointer: { axisPointer: {
lineStyle: { lineStyle: {
color: '#57617B' color: "#57617B"
} }
} }
}, },
legend: { legend: {
top: 20, top: 20,
icon: 'rect', icon: "rect",
itemWidth: 14, itemWidth: 14,
itemHeight: 5, itemHeight: 5,
itemGap: 13, itemGap: 13,
data: ['CMCC', 'CTCC', 'CUCC'], data: ["CMCC", "CTCC", "CUCC"],
right: '4%', right: "4%",
textStyle: { textStyle: {
fontSize: 12, fontSize: 12,
color: '#F1F1F3' color: "#F1F1F3"
} }
}, },
grid: { grid: {
top: 100, top: 100,
left: '2%', left: "2%",
right: '2%', right: "2%",
bottom: '2%', bottom: "2%",
containLabel: true containLabel: true
}, },
xAxis: [{ xAxis: [
type: 'category', {
type: "category",
boundaryGap: false, boundaryGap: false,
axisLine: { axisLine: {
lineStyle: { lineStyle: {
color: '#57617B' color: "#57617B"
} }
}, },
data: ['13:00', '13:05', '13:10', '13:15', '13:20', '13:25', '13:30', '13:35', '13:40', '13:45', '13:50', '13:55'] data: [
}], "13:00",
yAxis: [{ "13:05",
type: 'value', "13:10",
name: '(%)', "13:15",
"13:20",
"13:25",
"13:30",
"13:35",
"13:40",
"13:45",
"13:50",
"13:55"
]
}
],
yAxis: [
{
type: "value",
name: "(%)",
axisTick: { axisTick: {
show: false show: false
}, },
axisLine: { axisLine: {
lineStyle: { lineStyle: {
color: '#57617B' color: "#57617B"
} }
}, },
axisLabel: { axisLabel: {
@ -114,15 +130,17 @@ export default {
}, },
splitLine: { splitLine: {
lineStyle: { lineStyle: {
color: '#57617B' color: "#57617B"
} }
} }
}], }
series: [{ ],
name: 'CMCC', series: [
type: 'line', {
name: "CMCC",
type: "line",
smooth: true, smooth: true,
symbol: 'circle', symbol: "circle",
symbolSize: 5, symbolSize: 5,
showSymbol: false, showSymbol: false,
lineStyle: { lineStyle: {
@ -132,31 +150,41 @@ export default {
}, },
areaStyle: { areaStyle: {
normal: { normal: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ color: new echarts.graphic.LinearGradient(
0,
0,
0,
1,
[
{
offset: 0, offset: 0,
color: 'rgba(137, 189, 27, 0.3)' color: "rgba(137, 189, 27, 0.3)"
}, { },
{
offset: 0.8, offset: 0.8,
color: 'rgba(137, 189, 27, 0)' color: "rgba(137, 189, 27, 0)"
}], false), }
shadowColor: 'rgba(0, 0, 0, 0.1)', ],
false
),
shadowColor: "rgba(0, 0, 0, 0.1)",
shadowBlur: 10 shadowBlur: 10
} }
}, },
itemStyle: { itemStyle: {
normal: { normal: {
color: 'rgb(137,189,27)', color: "rgb(137,189,27)",
borderColor: 'rgba(137,189,2,0.27)', borderColor: "rgba(137,189,2,0.27)",
borderWidth: 12 borderWidth: 12
} }
}, },
data: [220, 182, 191, 134, 150, 120, 110, 125, 145, 122, 165, 122] data: [220, 182, 191, 134, 150, 120, 110, 125, 145, 122, 165, 122]
}, { },
name: 'CTCC', {
type: 'line', name: "CTCC",
type: "line",
smooth: true, smooth: true,
symbol: 'circle', symbol: "circle",
symbolSize: 5, symbolSize: 5,
showSymbol: false, showSymbol: false,
lineStyle: { lineStyle: {
@ -166,31 +194,41 @@ export default {
}, },
areaStyle: { areaStyle: {
normal: { normal: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ color: new echarts.graphic.LinearGradient(
0,
0,
0,
1,
[
{
offset: 0, offset: 0,
color: 'rgba(0, 136, 212, 0.3)' color: "rgba(0, 136, 212, 0.3)"
}, { },
{
offset: 0.8, offset: 0.8,
color: 'rgba(0, 136, 212, 0)' color: "rgba(0, 136, 212, 0)"
}], false), }
shadowColor: 'rgba(0, 0, 0, 0.1)', ],
false
),
shadowColor: "rgba(0, 0, 0, 0.1)",
shadowBlur: 10 shadowBlur: 10
} }
}, },
itemStyle: { itemStyle: {
normal: { normal: {
color: 'rgb(0,136,212)', color: "rgb(0,136,212)",
borderColor: 'rgba(0,136,212,0.2)', borderColor: "rgba(0,136,212,0.2)",
borderWidth: 12 borderWidth: 12
} }
}, },
data: [120, 110, 125, 145, 122, 165, 122, 220, 182, 191, 134, 150] data: [120, 110, 125, 145, 122, 165, 122, 220, 182, 191, 134, 150]
}, { },
name: 'CUCC', {
type: 'line', name: "CUCC",
type: "line",
smooth: true, smooth: true,
symbol: 'circle', symbol: "circle",
symbolSize: 5, symbolSize: 5,
showSymbol: false, showSymbol: false,
lineStyle: { lineStyle: {
@ -200,28 +238,39 @@ export default {
}, },
areaStyle: { areaStyle: {
normal: { normal: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ color: new echarts.graphic.LinearGradient(
0,
0,
0,
1,
[
{
offset: 0, offset: 0,
color: 'rgba(219, 50, 51, 0.3)' color: "rgba(219, 50, 51, 0.3)"
}, { },
{
offset: 0.8, offset: 0.8,
color: 'rgba(219, 50, 51, 0)' color: "rgba(219, 50, 51, 0)"
}], false), }
shadowColor: 'rgba(0, 0, 0, 0.1)', ],
false
),
shadowColor: "rgba(0, 0, 0, 0.1)",
shadowBlur: 10 shadowBlur: 10
} }
}, },
itemStyle: { itemStyle: {
normal: { normal: {
color: 'rgb(219,50,51)', color: "rgb(219,50,51)",
borderColor: 'rgba(219,50,51,0.2)', borderColor: "rgba(219,50,51,0.2)",
borderWidth: 12 borderWidth: 12
} }
}, },
data: [220, 182, 125, 145, 122, 191, 134, 150, 120, 110, 165, 122] data: [220, 182, 125, 145, 122, 191, 134, 150, 120, 110, 165, 122]
}] }
}) ]
});
} }
} }
} };
</script> </script>

View File

@ -1,103 +1,104 @@
<template> <template>
<div :id="id" :class="className" :style="{height:height,width:width}" /> <div :id="id" :class="className" :style="{ height: height, width: width }" />
</template> </template>
<script> <script>
import echarts from 'echarts' import echarts from "echarts";
import resize from './mixins/resize' import resize from "./mixins/resize";
export default { export default {
mixins: [resize], mixins: [resize],
props: { props: {
className: { className: {
type: String, type: String,
default: 'chart' default: "chart"
}, },
id: { id: {
type: String, type: String,
default: 'chart' default: "chart"
}, },
width: { width: {
type: String, type: String,
default: '200px' default: "200px"
}, },
height: { height: {
type: String, type: String,
default: '200px' default: "200px"
} }
}, },
data() { data() {
return { return {
chart: null chart: null
} };
}, },
mounted() { mounted() {
this.initChart() this.initChart();
}, },
beforeDestroy() { beforeDestroy() {
if (!this.chart) { if (!this.chart) {
return return;
} }
this.chart.dispose() this.chart.dispose();
this.chart = null this.chart = null;
}, },
methods: { methods: {
initChart() { initChart() {
this.chart = echarts.init(document.getElementById(this.id)) this.chart = echarts.init(document.getElementById(this.id));
const xData = (function() { const xData = (function() {
const data = [] const data = [];
for (let i = 1; i < 13; i++) { for (let i = 1; i < 13; i++) {
data.push(i + 'month') data.push(i + "month");
} }
return data return data;
}()) })();
this.chart.setOption({ this.chart.setOption({
backgroundColor: '#344b58', backgroundColor: "#344b58",
title: { title: {
text: 'statistics', text: "statistics",
x: '20', x: "20",
top: '20', top: "20",
textStyle: { textStyle: {
color: '#fff', color: "#fff",
fontSize: '22' fontSize: "22"
}, },
subtextStyle: { subtextStyle: {
color: '#90979c', color: "#90979c",
fontSize: '16' fontSize: "16"
} }
}, },
tooltip: { tooltip: {
trigger: 'axis', trigger: "axis",
axisPointer: { axisPointer: {
textStyle: { textStyle: {
color: '#fff' color: "#fff"
} }
} }
}, },
grid: { grid: {
left: '5%', left: "5%",
right: '5%', right: "5%",
borderWidth: 0, borderWidth: 0,
top: 150, top: 150,
bottom: 95, bottom: 95,
textStyle: { textStyle: {
color: '#fff' color: "#fff"
} }
}, },
legend: { legend: {
x: '5%', x: "5%",
top: '10%', top: "10%",
textStyle: { textStyle: {
color: '#90979c' color: "#90979c"
}, },
data: ['female', 'male', 'average'] data: ["female", "male", "average"]
}, },
calculable: true, calculable: true,
xAxis: [{ xAxis: [
type: 'category', {
type: "category",
axisLine: { axisLine: {
lineStyle: { lineStyle: {
color: '#90979c' color: "#90979c"
} }
}, },
splitLine: { splitLine: {
@ -111,18 +112,19 @@ export default {
}, },
axisLabel: { axisLabel: {
interval: 0 interval: 0
}, },
data: xData data: xData
}], }
yAxis: [{ ],
type: 'value', yAxis: [
{
type: "value",
splitLine: { splitLine: {
show: false show: false
}, },
axisLine: { axisLine: {
lineStyle: { lineStyle: {
color: '#90979c' color: "#90979c"
} }
}, },
axisTick: { axisTick: {
@ -134,50 +136,53 @@ export default {
splitArea: { splitArea: {
show: false show: false
} }
}], }
dataZoom: [{ ],
dataZoom: [
{
show: true, show: true,
height: 30, height: 30,
xAxisIndex: [ xAxisIndex: [0],
0
],
bottom: 30, bottom: 30,
start: 10, start: 10,
end: 80, end: 80,
handleIcon: 'path://M306.1,413c0,2.2-1.8,4-4,4h-59.8c-2.2,0-4-1.8-4-4V200.8c0-2.2,1.8-4,4-4h59.8c2.2,0,4,1.8,4,4V413z', handleIcon:
handleSize: '110%', "path://M306.1,413c0,2.2-1.8,4-4,4h-59.8c-2.2,0-4-1.8-4-4V200.8c0-2.2,1.8-4,4-4h59.8c2.2,0,4,1.8,4,4V413z",
handleSize: "110%",
handleStyle: { handleStyle: {
color: '#d3dee5' color: "#d3dee5"
}, },
textStyle: { textStyle: {
color: '#fff' }, color: "#fff"
borderColor: '#90979c' },
borderColor: "#90979c"
}, { },
type: 'inside', {
type: "inside",
show: true, show: true,
height: 15, height: 15,
start: 1, start: 1,
end: 35 end: 35
}], }
series: [{ ],
name: 'female', series: [
type: 'bar', {
stack: 'total', name: "female",
type: "bar",
stack: "total",
barMaxWidth: 35, barMaxWidth: 35,
barGap: '10%', barGap: "10%",
itemStyle: { itemStyle: {
normal: { normal: {
color: 'rgba(255,144,128,1)', color: "rgba(255,144,128,1)",
label: { label: {
show: true, show: true,
textStyle: { textStyle: {
color: '#fff' color: "#fff"
}, },
position: 'insideTop', position: "insideTop",
formatter(p) { formatter(p) {
return p.value > 0 ? p.value : '' return p.value > 0 ? p.value : "";
} }
} }
} }
@ -199,18 +204,18 @@ export default {
}, },
{ {
name: 'male', name: "male",
type: 'bar', type: "bar",
stack: 'total', stack: "total",
itemStyle: { itemStyle: {
normal: { normal: {
color: 'rgba(0,191,183,1)', color: "rgba(0,191,183,1)",
barBorderRadius: 0, barBorderRadius: 0,
label: { label: {
show: true, show: true,
position: 'top', position: "top",
formatter(p) { formatter(p) {
return p.value > 0 ? p.value : '' return p.value > 0 ? p.value : "";
} }
} }
} }
@ -229,21 +234,22 @@ export default {
381, 381,
220 220
] ]
}, { },
name: 'average', {
type: 'line', name: "average",
stack: 'total', type: "line",
stack: "total",
symbolSize: 10, symbolSize: 10,
symbol: 'circle', symbol: "circle",
itemStyle: { itemStyle: {
normal: { normal: {
color: 'rgba(252,230,48,1)', color: "rgba(252,230,48,1)",
barBorderRadius: 0, barBorderRadius: 0,
label: { label: {
show: true, show: true,
position: 'top', position: "top",
formatter(p) { formatter(p) {
return p.value > 0 ? p.value : '' return p.value > 0 ? p.value : "";
} }
} }
} }
@ -264,8 +270,8 @@ export default {
] ]
} }
] ]
}) });
} }
} }
} };
</script> </script>

View File

@ -1,34 +1,42 @@
import { debounce } from '@/utils' import { debounce } from "@/utils";
export default { export default {
data() { data() {
return { return {
$_sidebarElm: null $_sidebarElm: null
} };
}, },
mounted() { mounted() {
this.__resizeHandler = debounce(() => { this.__resizeHandler = debounce(() => {
if (this.chart) { if (this.chart) {
this.chart.resize() this.chart.resize();
} }
}, 100) }, 100);
window.addEventListener('resize', this.__resizeHandler) window.addEventListener("resize", this.__resizeHandler);
this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0] this.$_sidebarElm = document.getElementsByClassName("sidebar-container")[0];
this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler) this.$_sidebarElm &&
this.$_sidebarElm.addEventListener(
"transitionend",
this.$_sidebarResizeHandler
);
}, },
beforeDestroy() { beforeDestroy() {
window.removeEventListener('resize', this.__resizeHandler) window.removeEventListener("resize", this.__resizeHandler);
this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler) this.$_sidebarElm &&
this.$_sidebarElm.removeEventListener(
"transitionend",
this.$_sidebarResizeHandler
);
}, },
methods: { methods: {
// use $_ for mixins properties // use $_ for mixins properties
// https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
$_sidebarResizeHandler(e) { $_sidebarResizeHandler(e) {
if (e.propertyName === 'width') { if (e.propertyName === "width") {
this.__resizeHandler() this.__resizeHandler();
} }
} }
} }
} };

View File

@ -1,24 +1,40 @@
<template> <template>
<div class="dndList"> <div class="dndList">
<div :style="{width:width1}" class="dndList-list"> <div :style="{ width: width1 }" class="dndList-list">
<h3>{{ list1Title }}</h3> <h3>{{ list1Title }}</h3>
<draggable :set-data="setData" :list="list1" group="article" class="dragArea"> <draggable
<div v-for="element in list1" :key="element.id" class="list-complete-item"> :set-data="setData"
:list="list1"
group="article"
class="dragArea"
>
<div
v-for="element in list1"
:key="element.id"
class="list-complete-item"
>
<div class="list-complete-item-handle"> <div class="list-complete-item-handle">
{{ element.id }}[{{ element.author }}] {{ element.title }} {{ element.id }}[{{ element.author }}] {{ element.title }}
</div> </div>
<div style="position:absolute;right:0px;"> <div style="position:absolute;right:0px;">
<span style="float: right ;margin-top: -20px;margin-right:5px;" @click="deleteEle(element)"> <span
style="float: right ;margin-top: -20px;margin-right:5px;"
@click="deleteEle(element)"
>
<i style="color:#ff4949" class="el-icon-delete" /> <i style="color:#ff4949" class="el-icon-delete" />
</span> </span>
</div> </div>
</div> </div>
</draggable> </draggable>
</div> </div>
<div :style="{width:width2}" class="dndList-list"> <div :style="{ width: width2 }" class="dndList-list">
<h3>{{ list2Title }}</h3> <h3>{{ list2Title }}</h3>
<draggable :list="list2" group="article" class="dragArea"> <draggable :list="list2" group="article" class="dragArea">
<div v-for="element in list2" :key="element.id" class="list-complete-item"> <div
v-for="element in list2"
:key="element.id"
class="list-complete-item"
>
<div class="list-complete-item-handle2" @click="pushEle(element)"> <div class="list-complete-item-handle2" @click="pushEle(element)">
{{ element.id }} [{{ element.author }}] {{ element.title }} {{ element.id }} [{{ element.author }}] {{ element.title }}
</div> </div>
@ -29,79 +45,79 @@
</template> </template>
<script> <script>
import draggable from 'vuedraggable' import draggable from "vuedraggable";
export default { export default {
name: 'DndList', name: "DndList",
components: { draggable }, components: { draggable },
props: { props: {
list1: { list1: {
type: Array, type: Array,
default() { default() {
return [] return [];
} }
}, },
list2: { list2: {
type: Array, type: Array,
default() { default() {
return [] return [];
} }
}, },
list1Title: { list1Title: {
type: String, type: String,
default: 'list1' default: "list1"
}, },
list2Title: { list2Title: {
type: String, type: String,
default: 'list2' default: "list2"
}, },
width1: { width1: {
type: String, type: String,
default: '48%' default: "48%"
}, },
width2: { width2: {
type: String, type: String,
default: '48%' default: "48%"
} }
}, },
methods: { methods: {
isNotInList1(v) { isNotInList1(v) {
return this.list1.every(k => v.id !== k.id) return this.list1.every(k => v.id !== k.id);
}, },
isNotInList2(v) { isNotInList2(v) {
return this.list2.every(k => v.id !== k.id) return this.list2.every(k => v.id !== k.id);
}, },
deleteEle(ele) { deleteEle(ele) {
for (const item of this.list1) { for (const item of this.list1) {
if (item.id === ele.id) { if (item.id === ele.id) {
const index = this.list1.indexOf(item) const index = this.list1.indexOf(item);
this.list1.splice(index, 1) this.list1.splice(index, 1);
break break;
} }
} }
if (this.isNotInList2(ele)) { if (this.isNotInList2(ele)) {
this.list2.unshift(ele) this.list2.unshift(ele);
} }
}, },
pushEle(ele) { pushEle(ele) {
for (const item of this.list2) { for (const item of this.list2) {
if (item.id === ele.id) { if (item.id === ele.id) {
const index = this.list2.indexOf(item) const index = this.list2.indexOf(item);
this.list2.splice(index, 1) this.list2.splice(index, 1);
break break;
} }
} }
if (this.isNotInList1(ele)) { if (this.isNotInList1(ele)) {
this.list1.push(ele) this.list1.push(ele);
} }
}, },
setData(dataTransfer) { setData(dataTransfer) {
// to avoid Firefox bug // to avoid Firefox bug
// Detail see : https://github.com/RubaXa/Sortable/issues/1012 // Detail see : https://github.com/RubaXa/Sortable/issues/1012
dataTransfer.setData('Text', '') dataTransfer.setData("Text", "");
} }
} }
} };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -152,11 +168,11 @@ export default {
} }
.list-complete-item.sortable-chosen { .list-complete-item.sortable-chosen {
background: #4AB7BD; background: #4ab7bd;
} }
.list-complete-item.sortable-ghost { .list-complete-item.sortable-ghost {
background: #30B08F; background: #30b08f;
} }
.list-complete-enter, .list-complete-enter,

View File

@ -1,14 +1,21 @@
<template> <template>
<el-select ref="dragSelect" v-model="selectVal" v-bind="$attrs" class="drag-select" multiple v-on="$listeners"> <el-select
ref="dragSelect"
v-model="selectVal"
v-bind="$attrs"
class="drag-select"
multiple
v-on="$listeners"
>
<slot /> <slot />
</el-select> </el-select>
</template> </template>
<script> <script>
import Sortable from 'sortablejs' import Sortable from "sortablejs";
export default { export default {
name: 'DragSelect', name: "DragSelect",
props: { props: {
value: { value: {
type: Array, type: Array,
@ -18,41 +25,43 @@ export default {
computed: { computed: {
selectVal: { selectVal: {
get() { get() {
return [...this.value] return [...this.value];
}, },
set(val) { set(val) {
this.$emit('input', [...val]) this.$emit("input", [...val]);
} }
} }
}, },
mounted() { mounted() {
this.setSort() this.setSort();
}, },
methods: { methods: {
setSort() { setSort() {
const el = this.$refs.dragSelect.$el.querySelectorAll('.el-select__tags > span')[0] const el = this.$refs.dragSelect.$el.querySelectorAll(
".el-select__tags > span"
)[0];
this.sortable = Sortable.create(el, { this.sortable = Sortable.create(el, {
ghostClass: 'sortable-ghost', // Class name for the drop placeholder, ghostClass: "sortable-ghost", // Class name for the drop placeholder,
setData: function(dataTransfer) { setData: function(dataTransfer) {
dataTransfer.setData('Text', '') dataTransfer.setData("Text", "");
// to avoid Firefox bug // to avoid Firefox bug
// Detail see : https://github.com/RubaXa/Sortable/issues/1012 // Detail see : https://github.com/RubaXa/Sortable/issues/1012
}, },
onEnd: evt => { onEnd: evt => {
const targetRow = this.value.splice(evt.oldIndex, 1)[0] const targetRow = this.value.splice(evt.oldIndex, 1)[0];
this.value.splice(evt.newIndex, 0, targetRow) this.value.splice(evt.newIndex, 0, targetRow);
} }
}) });
} }
} }
} };
</script> </script>
<style scoped> <style scoped>
.drag-select >>> .sortable-ghost { .drag-select >>> .sortable-ghost {
opacity: .8; opacity: 0.8;
color: #fff!important; color: #fff !important;
background: #42b983!important; background: #42b983 !important;
} }
.drag-select >>> .el-tag { .drag-select >>> .el-tag {

View File

@ -1,15 +1,15 @@
<template> <template>
<div :id="id" :ref="id" :action="url" class="dropzone"> <div :id="id" :ref="id" :action="url" class="dropzone">
<input type="file" name="file"> <input type="file" name="file" />
</div> </div>
</template> </template>
<script> <script>
import Dropzone from 'dropzone' import Dropzone from "dropzone";
import 'dropzone/dist/dropzone.css' import "dropzone/dist/dropzone.css";
// import { getToken } from 'api/qiniu'; // import { getToken } from 'api/qiniu';
Dropzone.autoDiscover = false Dropzone.autoDiscover = false;
export default { export default {
props: { props: {
@ -27,11 +27,11 @@ export default {
}, },
defaultMsg: { defaultMsg: {
type: String, type: String,
default: '上传图片' default: "上传图片"
}, },
acceptedFiles: { acceptedFiles: {
type: String, type: String,
default: '' default: ""
}, },
thumbnailHeight: { thumbnailHeight: {
type: Number, type: Number,
@ -62,7 +62,7 @@ export default {
default: false default: false
}, },
defaultImg: { defaultImg: {
default: '', default: "",
type: [String, Array] type: [String, Array]
}, },
couldPaste: { couldPaste: {
@ -72,58 +72,70 @@ export default {
}, },
data() { data() {
return { return {
dropzone: '', dropzone: "",
initOnce: true initOnce: true
} };
}, },
watch: { watch: {
defaultImg(val) { defaultImg(val) {
if (val.length === 0) { if (val.length === 0) {
this.initOnce = false this.initOnce = false;
return return;
} }
if (!this.initOnce) return if (!this.initOnce) return;
this.initImages(val) this.initImages(val);
this.initOnce = false this.initOnce = false;
} }
}, },
mounted() { mounted() {
const element = document.getElementById(this.id) const element = document.getElementById(this.id);
const vm = this const vm = this;
this.dropzone = new Dropzone(element, { this.dropzone = new Dropzone(element, {
clickable: this.clickable, clickable: this.clickable,
thumbnailWidth: this.thumbnailWidth, thumbnailWidth: this.thumbnailWidth,
thumbnailHeight: this.thumbnailHeight, thumbnailHeight: this.thumbnailHeight,
maxFiles: this.maxFiles, maxFiles: this.maxFiles,
maxFilesize: this.maxFilesize, maxFilesize: this.maxFilesize,
dictRemoveFile: 'Remove', dictRemoveFile: "Remove",
addRemoveLinks: this.showRemoveLink, addRemoveLinks: this.showRemoveLink,
acceptedFiles: this.acceptedFiles, acceptedFiles: this.acceptedFiles,
autoProcessQueue: this.autoProcessQueue, autoProcessQueue: this.autoProcessQueue,
dictDefaultMessage: '<i style="margin-top: 3em;display: inline-block" class="material-icons">' + this.defaultMsg + '</i><br>Drop files here to upload', dictDefaultMessage:
dictMaxFilesExceeded: '只能一个图', '<i style="margin-top: 3em;display: inline-block" class="material-icons">' +
previewTemplate: '<div class="dz-preview dz-file-preview"> <div class="dz-image" style="width:' + this.thumbnailWidth + 'px;height:' + this.thumbnailHeight + 'px" ><img style="width:' + this.thumbnailWidth + 'px;height:' + this.thumbnailHeight + 'px" data-dz-thumbnail /></div> <div class="dz-details"><div class="dz-size"><span data-dz-size></span></div> <div class="dz-progress"><span class="dz-upload" data-dz-uploadprogress></span></div> <div class="dz-error-message"><span data-dz-errormessage></span></div> <div class="dz-success-mark"> <i class="material-icons">done</i> </div> <div class="dz-error-mark"><i class="material-icons">error</i></div></div>', this.defaultMsg +
"</i><br>Drop files here to upload",
dictMaxFilesExceeded: "只能一个图",
previewTemplate:
'<div class="dz-preview dz-file-preview"> <div class="dz-image" style="width:' +
this.thumbnailWidth +
"px;height:" +
this.thumbnailHeight +
'px" ><img style="width:' +
this.thumbnailWidth +
"px;height:" +
this.thumbnailHeight +
'px" data-dz-thumbnail /></div> <div class="dz-details"><div class="dz-size"><span data-dz-size></span></div> <div class="dz-progress"><span class="dz-upload" data-dz-uploadprogress></span></div> <div class="dz-error-message"><span data-dz-errormessage></span></div> <div class="dz-success-mark"> <i class="material-icons">done</i> </div> <div class="dz-error-mark"><i class="material-icons">error</i></div></div>',
init() { init() {
const val = vm.defaultImg const val = vm.defaultImg;
if (!val) return if (!val) return;
if (Array.isArray(val)) { if (Array.isArray(val)) {
if (val.length === 0) return if (val.length === 0) return;
val.map((v, i) => { val.map((v, i) => {
const mockFile = { name: 'name' + i, size: 12345, url: v } const mockFile = { name: "name" + i, size: 12345, url: v };
this.options.addedfile.call(this, mockFile) this.options.addedfile.call(this, mockFile);
this.options.thumbnail.call(this, mockFile, v) this.options.thumbnail.call(this, mockFile, v);
mockFile.previewElement.classList.add('dz-success') mockFile.previewElement.classList.add("dz-success");
mockFile.previewElement.classList.add('dz-complete') mockFile.previewElement.classList.add("dz-complete");
vm.initOnce = false vm.initOnce = false;
return true return true;
}) });
} else { } else {
const mockFile = { name: 'name', size: 12345, url: val } const mockFile = { name: "name", size: 12345, url: val };
this.options.addedfile.call(this, mockFile) this.options.addedfile.call(this, mockFile);
this.options.thumbnail.call(this, mockFile, val) this.options.thumbnail.call(this, mockFile, val);
mockFile.previewElement.classList.add('dz-success') mockFile.previewElement.classList.add("dz-success");
mockFile.previewElement.classList.add('dz-complete') mockFile.previewElement.classList.add("dz-complete");
vm.initOnce = false vm.initOnce = false;
} }
}, },
accept: (file, done) => { accept: (file, done) => {
@ -135,136 +147,137 @@ export default {
// file.url = response.data.qiniu_url; // file.url = response.data.qiniu_url;
// done(); // done();
// }) // })
done() done();
}, },
sending: (file, xhr, formData) => { sending: (file, xhr, formData) => {
// formData.append('token', file.token); // formData.append('token', file.token);
// formData.append('key', file.key); // formData.append('key', file.key);
vm.initOnce = false vm.initOnce = false;
} }
}) });
if (this.couldPaste) { if (this.couldPaste) {
document.addEventListener('paste', this.pasteImg) document.addEventListener("paste", this.pasteImg);
} }
this.dropzone.on('success', file => { this.dropzone.on("success", file => {
vm.$emit('dropzone-success', file, vm.dropzone.element) vm.$emit("dropzone-success", file, vm.dropzone.element);
}) });
this.dropzone.on('addedfile', file => { this.dropzone.on("addedfile", file => {
vm.$emit('dropzone-fileAdded', file) vm.$emit("dropzone-fileAdded", file);
}) });
this.dropzone.on('removedfile', file => { this.dropzone.on("removedfile", file => {
vm.$emit('dropzone-removedFile', file) vm.$emit("dropzone-removedFile", file);
}) });
this.dropzone.on('error', (file, error, xhr) => { this.dropzone.on("error", (file, error, xhr) => {
vm.$emit('dropzone-error', file, error, xhr) vm.$emit("dropzone-error", file, error, xhr);
}) });
this.dropzone.on('successmultiple', (file, error, xhr) => { this.dropzone.on("successmultiple", (file, error, xhr) => {
vm.$emit('dropzone-successmultiple', file, error, xhr) vm.$emit("dropzone-successmultiple", file, error, xhr);
}) });
}, },
destroyed() { destroyed() {
document.removeEventListener('paste', this.pasteImg) document.removeEventListener("paste", this.pasteImg);
this.dropzone.destroy() this.dropzone.destroy();
}, },
methods: { methods: {
removeAllFiles() { removeAllFiles() {
this.dropzone.removeAllFiles(true) this.dropzone.removeAllFiles(true);
}, },
processQueue() { processQueue() {
this.dropzone.processQueue() this.dropzone.processQueue();
}, },
pasteImg(event) { pasteImg(event) {
const items = (event.clipboardData || event.originalEvent.clipboardData).items const items = (event.clipboardData || event.originalEvent.clipboardData)
if (items[0].kind === 'file') { .items;
this.dropzone.addFile(items[0].getAsFile()) if (items[0].kind === "file") {
this.dropzone.addFile(items[0].getAsFile());
} }
}, },
initImages(val) { initImages(val) {
if (!val) return if (!val) return;
if (Array.isArray(val)) { if (Array.isArray(val)) {
val.map((v, i) => { val.map((v, i) => {
const mockFile = { name: 'name' + i, size: 12345, url: v } const mockFile = { name: "name" + i, size: 12345, url: v };
this.dropzone.options.addedfile.call(this.dropzone, mockFile) this.dropzone.options.addedfile.call(this.dropzone, mockFile);
this.dropzone.options.thumbnail.call(this.dropzone, mockFile, v) this.dropzone.options.thumbnail.call(this.dropzone, mockFile, v);
mockFile.previewElement.classList.add('dz-success') mockFile.previewElement.classList.add("dz-success");
mockFile.previewElement.classList.add('dz-complete') mockFile.previewElement.classList.add("dz-complete");
return true return true;
}) });
} else { } else {
const mockFile = { name: 'name', size: 12345, url: val } const mockFile = { name: "name", size: 12345, url: val };
this.dropzone.options.addedfile.call(this.dropzone, mockFile) this.dropzone.options.addedfile.call(this.dropzone, mockFile);
this.dropzone.options.thumbnail.call(this.dropzone, mockFile, val) this.dropzone.options.thumbnail.call(this.dropzone, mockFile, val);
mockFile.previewElement.classList.add('dz-success') mockFile.previewElement.classList.add("dz-success");
mockFile.previewElement.classList.add('dz-complete') mockFile.previewElement.classList.add("dz-complete");
} }
} }
} }
} };
</script> </script>
<style scoped> <style scoped>
.dropzone { .dropzone {
border: 2px solid #E5E5E5; border: 2px solid #e5e5e5;
font-family: 'Roboto', sans-serif; font-family: "Roboto", sans-serif;
color: #777; color: #777;
transition: background-color .2s linear; transition: background-color 0.2s linear;
padding: 5px; padding: 5px;
} }
.dropzone:hover { .dropzone:hover {
background-color: #F6F6F6; background-color: #f6f6f6;
} }
i { i {
color: #CCC; color: #ccc;
} }
.dropzone .dz-image img { .dropzone .dz-image img {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
.dropzone input[name='file'] { .dropzone input[name="file"] {
display: none; display: none;
} }
.dropzone .dz-preview .dz-image { .dropzone .dz-preview .dz-image {
border-radius: 0px; border-radius: 0px;
} }
.dropzone .dz-preview:hover .dz-image img { .dropzone .dz-preview:hover .dz-image img {
transform: none; transform: none;
filter: none; filter: none;
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
.dropzone .dz-preview .dz-details { .dropzone .dz-preview .dz-details {
bottom: 0px; bottom: 0px;
top: 0px; top: 0px;
color: white; color: white;
background-color: rgba(33, 150, 243, 0.8); background-color: rgba(33, 150, 243, 0.8);
transition: opacity .2s linear; transition: opacity 0.2s linear;
text-align: left; text-align: left;
} }
.dropzone .dz-preview .dz-details .dz-filename span, .dropzone .dz-preview .dz-details .dz-size span { .dropzone .dz-preview .dz-details .dz-filename span,
.dropzone .dz-preview .dz-details .dz-size span {
background-color: transparent; background-color: transparent;
} }
.dropzone .dz-preview .dz-details .dz-filename:not(:hover) span { .dropzone .dz-preview .dz-details .dz-filename:not(:hover) span {
border: none; border: none;
} }
.dropzone .dz-preview .dz-details .dz-filename:hover span { .dropzone .dz-preview .dz-details .dz-filename:hover span {
background-color: transparent; background-color: transparent;
border: none; border: none;
} }
.dropzone .dz-preview .dz-remove { .dropzone .dz-preview .dz-remove {
position: absolute; position: absolute;
z-index: 30; z-index: 30;
color: white; color: white;
@ -279,19 +292,21 @@ export default {
font-weight: 800; font-weight: 800;
letter-spacing: 1.1px; letter-spacing: 1.1px;
opacity: 0; opacity: 0;
} }
.dropzone .dz-preview:hover .dz-remove { .dropzone .dz-preview:hover .dz-remove {
opacity: 1; opacity: 1;
} }
.dropzone .dz-preview .dz-success-mark, .dropzone .dz-preview .dz-error-mark { .dropzone .dz-preview .dz-success-mark,
.dropzone .dz-preview .dz-error-mark {
margin-left: -40px; margin-left: -40px;
margin-top: -50px; margin-top: -50px;
} }
.dropzone .dz-preview .dz-success-mark i, .dropzone .dz-preview .dz-error-mark i { .dropzone .dz-preview .dz-success-mark i,
.dropzone .dz-preview .dz-error-mark i {
color: white; color: white;
font-size: 5rem; font-size: 5rem;
} }
</style> </style>

View File

@ -1,6 +1,10 @@
<template> <template>
<div v-if="errorLogs.length>0"> <div v-if="errorLogs.length > 0">
<el-badge :is-dot="true" style="line-height: 25px;margin-top: -5px;" @click.native="dialogTableVisible=true"> <el-badge
:is-dot="true"
style="line-height: 25px;margin-top: -5px;"
@click.native="dialogTableVisible = true"
>
<el-button style="padding: 8px 10px;" size="small" type="danger"> <el-button style="padding: 8px 10px;" size="small" type="danger">
<svg-icon icon-class="bug" /> <svg-icon icon-class="bug" />
</el-button> </el-button>
@ -9,27 +13,37 @@
<el-dialog :visible.sync="dialogTableVisible" width="80%" append-to-body> <el-dialog :visible.sync="dialogTableVisible" width="80%" append-to-body>
<div slot="title"> <div slot="title">
<span style="padding-right: 10px;">Error Log</span> <span style="padding-right: 10px;">Error Log</span>
<el-button size="mini" type="primary" icon="el-icon-delete" @click="clearAll">Clear All</el-button> <el-button
size="mini"
type="primary"
icon="el-icon-delete"
@click="clearAll"
>Clear All</el-button
>
</div> </div>
<el-table :data="errorLogs" border> <el-table :data="errorLogs" border>
<el-table-column label="Message"> <el-table-column label="Message">
<template slot-scope="{row}"> <template slot-scope="{ row }">
<div> <div>
<span class="message-title">Msg:</span> <span class="message-title">Msg:</span>
<el-tag type="danger"> <el-tag type="danger">
{{ row.err.message }} {{ row.err.message }}
</el-tag> </el-tag>
</div> </div>
<br> <br />
<div> <div>
<span class="message-title" style="padding-right: 10px;">Info: </span> <span class="message-title" style="padding-right: 10px;"
>Info:
</span>
<el-tag type="warning"> <el-tag type="warning">
{{ row.vm.$vnode.tag }} error in {{ row.info }} {{ row.vm.$vnode.tag }} error in {{ row.info }}
</el-tag> </el-tag>
</div> </div>
<br> <br />
<div> <div>
<span class="message-title" style="padding-right: 16px;">Url: </span> <span class="message-title" style="padding-right: 16px;"
>Url:
</span>
<el-tag type="success"> <el-tag type="success">
{{ row.url }} {{ row.url }}
</el-tag> </el-tag>
@ -48,24 +62,24 @@
<script> <script>
export default { export default {
name: 'ErrorLog', name: "ErrorLog",
data() { data() {
return { return {
dialogTableVisible: false dialogTableVisible: false
} };
}, },
computed: { computed: {
errorLogs() { errorLogs() {
return this.$store.getters.errorLogs return this.$store.getters.errorLogs;
} }
}, },
methods: { methods: {
clearAll() { clearAll() {
this.dialogTableVisible = false this.dialogTableVisible = false;
this.$store.dispatch('errorLog/clearErrorLog') this.$store.dispatch("errorLog/clearErrorLog");
} }
} }
} };
</script> </script>
<style scoped> <style scoped>

View File

@ -1,5 +1,10 @@
<template> <template>
<a href="https://github.com/PanJiaChen/vue-element-admin" target="_blank" class="github-corner" aria-label="View source on Github"> <a
href="https://github.com/PanJiaChen/vue-element-admin"
target="_blank"
class="github-corner"
aria-label="View source on Github"
>
<svg <svg
width="80" width="80"
height="80" height="80"
@ -25,30 +30,30 @@
<style scoped> <style scoped>
.github-corner:hover .octo-arm { .github-corner:hover .octo-arm {
animation: octocat-wave 560ms ease-in-out animation: octocat-wave 560ms ease-in-out;
} }
@keyframes octocat-wave { @keyframes octocat-wave {
0%, 0%,
100% { 100% {
transform: rotate(0) transform: rotate(0);
} }
20%, 20%,
60% { 60% {
transform: rotate(-25deg) transform: rotate(-25deg);
} }
40%, 40%,
80% { 80% {
transform: rotate(10deg) transform: rotate(10deg);
} }
} }
@media (max-width:500px) { @media (max-width: 500px) {
.github-corner:hover .octo-arm { .github-corner:hover .octo-arm {
animation: none animation: none;
} }
.github-corner .octo-arm { .github-corner .octo-arm {
animation: octocat-wave 560ms ease-in-out animation: octocat-wave 560ms ease-in-out;
} }
} }
</style> </style>

View File

@ -1,21 +1,23 @@
<template> <template>
<div style="padding: 0 15px;" @click="toggleClick"> <div style="padding: 0 15px;" @click="toggleClick">
<svg <svg
:class="{'is-active':isActive}" :class="{ 'is-active': isActive }"
class="hamburger" class="hamburger"
viewBox="0 0 1024 1024" viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="64" width="64"
height="64" height="64"
> >
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z" /> <path
d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z"
/>
</svg> </svg>
</div> </div>
</template> </template>
<script> <script>
export default { export default {
name: 'Hamburger', name: "Hamburger",
props: { props: {
isActive: { isActive: {
type: Boolean, type: Boolean,
@ -24,10 +26,10 @@ export default {
}, },
methods: { methods: {
toggleClick() { toggleClick() {
this.$emit('toggleClick') this.$emit("toggleClick");
} }
} }
} };
</script> </script>
<style scoped> <style scoped>

View File

@ -1,6 +1,10 @@
<template> <template>
<div :class="{'show':show}" class="header-search"> <div :class="{ show: show }" class="header-search">
<svg-icon class-name="search-icon" icon-class="search" @click.stop="click" /> <svg-icon
class-name="search-icon"
icon-class="search"
@click.stop="click"
/>
<el-select <el-select
ref="headerSearchSelect" ref="headerSearchSelect"
v-model="search" v-model="search"
@ -12,7 +16,12 @@
class="header-search-select" class="header-search-select"
@change="change" @change="change"
> >
<el-option v-for="item in options" :key="item.path" :value="item" :label="item.title.join(' > ')" /> <el-option
v-for="item in options"
:key="item.path"
:value="item"
:label="item.title.join(' > ')"
/>
</el-select> </el-select>
</div> </div>
</template> </template>
@ -20,62 +29,62 @@
<script> <script>
// fuse is a lightweight fuzzy-search module // fuse is a lightweight fuzzy-search module
// make search results more in line with expectations // make search results more in line with expectations
import Fuse from 'fuse.js' import Fuse from "fuse.js";
import path from 'path' import path from "path";
export default { export default {
name: 'HeaderSearch', name: "HeaderSearch",
data() { data() {
return { return {
search: '', search: "",
options: [], options: [],
searchPool: [], searchPool: [],
show: false, show: false,
fuse: undefined fuse: undefined
} };
}, },
computed: { computed: {
routes() { routes() {
return this.$store.getters.permission_routes return this.$store.getters.permission_routes;
} }
}, },
watch: { watch: {
routes() { routes() {
this.searchPool = this.generateRoutes(this.routes) this.searchPool = this.generateRoutes(this.routes);
}, },
searchPool(list) { searchPool(list) {
this.initFuse(list) this.initFuse(list);
}, },
show(value) { show(value) {
if (value) { if (value) {
document.body.addEventListener('click', this.close) document.body.addEventListener("click", this.close);
} else { } else {
document.body.removeEventListener('click', this.close) document.body.removeEventListener("click", this.close);
} }
} }
}, },
mounted() { mounted() {
this.searchPool = this.generateRoutes(this.routes) this.searchPool = this.generateRoutes(this.routes);
}, },
methods: { methods: {
click() { click() {
this.show = !this.show this.show = !this.show;
if (this.show) { if (this.show) {
this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.focus() this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.focus();
} }
}, },
close() { close() {
this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.blur() this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.blur();
this.options = [] this.options = [];
this.show = false this.show = false;
}, },
change(val) { change(val) {
this.$router.push(val.path) this.$router.push(val.path);
this.search = '' this.search = "";
this.options = [] this.options = [];
this.$nextTick(() => { this.$nextTick(() => {
this.show = false this.show = false;
}) });
}, },
initFuse(list) { initFuse(list) {
this.fuse = new Fuse(list, { this.fuse = new Fuse(list, {
@ -85,58 +94,67 @@ export default {
distance: 100, distance: 100,
maxPatternLength: 32, maxPatternLength: 32,
minMatchCharLength: 1, minMatchCharLength: 1,
keys: [{ keys: [
name: 'title', {
name: "title",
weight: 0.7 weight: 0.7
}, { },
name: 'path', {
name: "path",
weight: 0.3 weight: 0.3
}] }
}) ]
});
}, },
// Filter out the routes that can be displayed in the sidebar // Filter out the routes that can be displayed in the sidebar
// And generate the internationalized title // And generate the internationalized title
generateRoutes(routes, basePath = '/', prefixTitle = []) { generateRoutes(routes, basePath = "/", prefixTitle = []) {
let res = [] let res = [];
for (const router of routes) { for (const router of routes) {
// skip hidden router // skip hidden router
if (router.hidden) { continue } if (router.hidden) {
continue;
}
const data = { const data = {
path: path.resolve(basePath, router.path), path: path.resolve(basePath, router.path),
title: [...prefixTitle] title: [...prefixTitle]
} };
if (router.meta && router.meta.title) { if (router.meta && router.meta.title) {
data.title = [...data.title, router.meta.title] data.title = [...data.title, router.meta.title];
if (router.redirect !== 'noRedirect') { if (router.redirect !== "noRedirect") {
// only push the routes with title // only push the routes with title
// special case: need to exclude parent router without redirect // special case: need to exclude parent router without redirect
res.push(data) res.push(data);
} }
} }
// recursive child routes // recursive child routes
if (router.children) { if (router.children) {
const tempRoutes = this.generateRoutes(router.children, data.path, data.title) const tempRoutes = this.generateRoutes(
router.children,
data.path,
data.title
);
if (tempRoutes.length >= 1) { if (tempRoutes.length >= 1) {
res = [...res, ...tempRoutes] res = [...res, ...tempRoutes];
} }
} }
} }
return res return res;
}, },
querySearch(query) { querySearch(query) {
if (query !== '') { if (query !== "") {
this.options = this.fuse.search(query) this.options = this.fuse.search(query);
} else { } else {
this.options = [] this.options = [];
} }
} }
} }
} };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -159,7 +177,7 @@ export default {
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
/deep/ .el-input__inner { ::v-deep .el-input__inner {
border-radius: 0; border-radius: 0;
border: 0; border: 0;
padding-left: 0; padding-left: 0;

File diff suppressed because it is too large Load Diff

View File

@ -6,14 +6,14 @@
* @return {[blob]} [description] * @return {[blob]} [description]
*/ */
export default function(data, mime) { export default function(data, mime) {
data = data.split(',')[1] data = data.split(",")[1];
data = window.atob(data) data = window.atob(data);
var ia = new Uint8Array(data.length) var ia = new Uint8Array(data.length);
for (var i = 0; i < data.length; i++) { for (var i = 0; i < data.length; i++) {
ia[i] = data.charCodeAt(i) ia[i] = data.charCodeAt(i);
} }
// canvas.toDataURL 返回的默认格式就是 image/png // canvas.toDataURL 返回的默认格式就是 image/png
return new Blob([ia], { return new Blob([ia], {
type: mime type: mime
}) });
} }

View File

@ -6,34 +6,48 @@
* @return {[bollean]} [description] * @return {[bollean]} [description]
*/ */
export default function(e, arg_opts) { export default function(e, arg_opts) {
var opts = Object.assign({ var opts = Object.assign(
{
ele: e.target, // 波纹作用元素 ele: e.target, // 波纹作用元素
type: 'hit', // hit点击位置扩散center中心点扩展 type: "hit", // hit点击位置扩散center中心点扩展
bgc: 'rgba(0, 0, 0, 0.15)' // 波纹颜色 bgc: "rgba(0, 0, 0, 0.15)" // 波纹颜色
}, arg_opts) },
var target = opts.ele arg_opts
);
var target = opts.ele;
if (target) { if (target) {
var rect = target.getBoundingClientRect() var rect = target.getBoundingClientRect();
var ripple = target.querySelector('.e-ripple') var ripple = target.querySelector(".e-ripple");
if (!ripple) { if (!ripple) {
ripple = document.createElement('span') ripple = document.createElement("span");
ripple.className = 'e-ripple' ripple.className = "e-ripple";
ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px' ripple.style.height = ripple.style.width =
target.appendChild(ripple) Math.max(rect.width, rect.height) + "px";
target.appendChild(ripple);
} else { } else {
ripple.className = 'e-ripple' ripple.className = "e-ripple";
} }
switch (opts.type) { switch (opts.type) {
case 'center': case "center":
ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px' ripple.style.top = rect.height / 2 - ripple.offsetHeight / 2 + "px";
ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px' ripple.style.left = rect.width / 2 - ripple.offsetWidth / 2 + "px";
break break;
default: default:
ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.body.scrollTop) + 'px' ripple.style.top =
ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.body.scrollLeft) + 'px' e.pageY -
rect.top -
ripple.offsetHeight / 2 -
document.body.scrollTop +
"px";
ripple.style.left =
e.pageX -
rect.left -
ripple.offsetWidth / 2 -
document.body.scrollLeft +
"px";
} }
ripple.style.backgroundColor = opts.bgc ripple.style.backgroundColor = opts.bgc;
ripple.className = 'e-ripple z-active' ripple.className = "e-ripple z-active";
return false return false;
} }
} }

View File

@ -1,232 +1,240 @@
export default { export default {
zh: { zh: {
hint: '点击,或拖动图片至此处', hint: "点击,或拖动图片至此处",
loading: '正在上传……', loading: "正在上传……",
noSupported: '浏览器不支持该功能请使用IE10以上或其他现在浏览器', noSupported: "浏览器不支持该功能请使用IE10以上或其他现在浏览器",
success: '上传成功', success: "上传成功",
fail: '图片上传失败', fail: "图片上传失败",
preview: '头像预览', preview: "头像预览",
btn: { btn: {
off: '取消', off: "取消",
close: '关闭', close: "关闭",
back: '上一步', back: "上一步",
save: '保存' save: "保存"
}, },
error: { error: {
onlyImg: '仅限图片格式', onlyImg: "仅限图片格式",
outOfSize: '单文件大小不能超过 ', outOfSize: "单文件大小不能超过 ",
lowestPx: '图片最低像素为(宽*高):' lowestPx: "图片最低像素为(宽*高):"
} }
}, },
'zh-tw': { "zh-tw": {
hint: '點擊,或拖動圖片至此處', hint: "點擊,或拖動圖片至此處",
loading: '正在上傳……', loading: "正在上傳……",
noSupported: '瀏覽器不支持該功能請使用IE10以上或其他現代瀏覽器', noSupported: "瀏覽器不支持該功能請使用IE10以上或其他現代瀏覽器",
success: '上傳成功', success: "上傳成功",
fail: '圖片上傳失敗', fail: "圖片上傳失敗",
preview: '頭像預覽', preview: "頭像預覽",
btn: { btn: {
off: '取消', off: "取消",
close: '關閉', close: "關閉",
back: '上一步', back: "上一步",
save: '保存' save: "保存"
}, },
error: { error: {
onlyImg: '僅限圖片格式', onlyImg: "僅限圖片格式",
outOfSize: '單文件大小不能超過 ', outOfSize: "單文件大小不能超過 ",
lowestPx: '圖片最低像素為(寬*高):' lowestPx: "圖片最低像素為(寬*高):"
} }
}, },
en: { en: {
hint: 'Click or drag the file here to upload', hint: "Click or drag the file here to upload",
loading: 'Uploading…', loading: "Uploading…",
noSupported: 'Browser is not supported, please use IE10+ or other browsers', noSupported: "Browser is not supported, please use IE10+ or other browsers",
success: 'Upload success', success: "Upload success",
fail: 'Upload failed', fail: "Upload failed",
preview: 'Preview', preview: "Preview",
btn: { btn: {
off: 'Cancel', off: "Cancel",
close: 'Close', close: "Close",
back: 'Back', back: "Back",
save: 'Save' save: "Save"
}, },
error: { error: {
onlyImg: 'Image only', onlyImg: "Image only",
outOfSize: 'Image exceeds size limit: ', outOfSize: "Image exceeds size limit: ",
lowestPx: 'Image\'s size is too low. Expected at least: ' lowestPx: "Image's size is too low. Expected at least: "
} }
}, },
ro: { ro: {
hint: 'Atinge sau trage fișierul aici', hint: "Atinge sau trage fișierul aici",
loading: 'Se încarcă', loading: "Se încarcă",
noSupported: 'Browser-ul tău nu suportă acest feature. Te rugăm încearcă cu alt browser.', noSupported:
success: 'S-a încărcat cu succes', "Browser-ul tău nu suportă acest feature. Te rugăm încearcă cu alt browser.",
fail: 'A apărut o problemă la încărcare', success: "S-a încărcat cu succes",
preview: 'Previzualizează', fail: "A apărut o problemă la încărcare",
preview: "Previzualizează",
btn: { btn: {
off: 'Anulează', off: "Anulează",
close: 'Închide', close: "Închide",
back: 'Înapoi', back: "Înapoi",
save: 'Salvează' save: "Salvează"
}, },
error: { error: {
onlyImg: 'Doar imagini', onlyImg: "Doar imagini",
outOfSize: 'Imaginea depășește limita de: ', outOfSize: "Imaginea depășește limita de: ",
loewstPx: 'Imaginea este prea mică; Minim: ' loewstPx: "Imaginea este prea mică; Minim: "
} }
}, },
ru: { ru: {
hint: 'Нажмите, или перетащите файл в это окно', hint: "Нажмите, или перетащите файл в это окно",
loading: 'Загружаю……', loading: "Загружаю……",
noSupported: 'Ваш браузер не поддерживается, пожалуйста, используйте IE10 + или другие браузеры', noSupported:
success: 'Загрузка выполнена успешно', "Ваш браузер не поддерживается, пожалуйста, используйте IE10 + или другие браузеры",
fail: 'Ошибка загрузки', success: "Загрузка выполнена успешно",
preview: 'Предпросмотр', fail: "Ошибка загрузки",
preview: "Предпросмотр",
btn: { btn: {
off: 'Отменить', off: "Отменить",
close: 'Закрыть', close: "Закрыть",
back: 'Назад', back: "Назад",
save: 'Сохранить' save: "Сохранить"
}, },
error: { error: {
onlyImg: 'Только изображения', onlyImg: "Только изображения",
outOfSize: 'Изображение превышает предельный размер: ', outOfSize: "Изображение превышает предельный размер: ",
lowestPx: 'Минимальный размер изображения: ' lowestPx: "Минимальный размер изображения: "
} }
}, },
'pt-br': { "pt-br": {
hint: 'Clique ou arraste o arquivo aqui para carregar', hint: "Clique ou arraste o arquivo aqui para carregar",
loading: 'Carregando…', loading: "Carregando…",
noSupported: 'Browser não suportado, use o IE10+ ou outro browser', noSupported: "Browser não suportado, use o IE10+ ou outro browser",
success: 'Sucesso ao carregar imagem', success: "Sucesso ao carregar imagem",
fail: 'Falha ao carregar imagem', fail: "Falha ao carregar imagem",
preview: 'Pré-visualizar', preview: "Pré-visualizar",
btn: { btn: {
off: 'Cancelar', off: "Cancelar",
close: 'Fechar', close: "Fechar",
back: 'Voltar', back: "Voltar",
save: 'Salvar' save: "Salvar"
}, },
error: { error: {
onlyImg: 'Apenas imagens', onlyImg: "Apenas imagens",
outOfSize: 'A imagem excede o limite de tamanho: ', outOfSize: "A imagem excede o limite de tamanho: ",
lowestPx: 'O tamanho da imagem é muito pequeno. Tamanho mínimo: ' lowestPx: "O tamanho da imagem é muito pequeno. Tamanho mínimo: "
} }
}, },
fr: { fr: {
hint: 'Cliquez ou glissez le fichier ici.', hint: "Cliquez ou glissez le fichier ici.",
loading: 'Téléchargement…', loading: "Téléchargement…",
noSupported: 'Votre navigateur n\'est pas supporté. Utilisez IE10 + ou un autre navigateur s\'il vous plaît.', noSupported:
success: 'Téléchargement réussit', "Votre navigateur n'est pas supporté. Utilisez IE10 + ou un autre navigateur s'il vous plaît.",
fail: 'Téléchargement echoué', success: "Téléchargement réussit",
preview: 'Aperçu', fail: "Téléchargement echoué",
preview: "Aperçu",
btn: { btn: {
off: 'Annuler', off: "Annuler",
close: 'Fermer', close: "Fermer",
back: 'Retour', back: "Retour",
save: 'Enregistrer' save: "Enregistrer"
}, },
error: { error: {
onlyImg: 'Image uniquement', onlyImg: "Image uniquement",
outOfSize: 'L\'image sélectionnée dépasse la taille maximum: ', outOfSize: "L'image sélectionnée dépasse la taille maximum: ",
lowestPx: 'L\'image sélectionnée est trop petite. Dimensions attendues: ' lowestPx: "L'image sélectionnée est trop petite. Dimensions attendues: "
} }
}, },
nl: { nl: {
hint: 'Klik hier of sleep een afbeelding in dit vlak', hint: "Klik hier of sleep een afbeelding in dit vlak",
loading: 'Uploaden…', loading: "Uploaden…",
noSupported: 'Je browser wordt helaas niet ondersteund. Gebruik IE10+ of een andere browser.', noSupported:
success: 'Upload succesvol', "Je browser wordt helaas niet ondersteund. Gebruik IE10+ of een andere browser.",
fail: 'Upload mislukt', success: "Upload succesvol",
preview: 'Voorbeeld', fail: "Upload mislukt",
preview: "Voorbeeld",
btn: { btn: {
off: 'Annuleren', off: "Annuleren",
close: 'Sluiten', close: "Sluiten",
back: 'Terug', back: "Terug",
save: 'Opslaan' save: "Opslaan"
}, },
error: { error: {
onlyImg: 'Alleen afbeeldingen', onlyImg: "Alleen afbeeldingen",
outOfSize: 'De afbeelding is groter dan: ', outOfSize: "De afbeelding is groter dan: ",
lowestPx: 'De afbeelding is te klein! Minimale afmetingen: ' lowestPx: "De afbeelding is te klein! Minimale afmetingen: "
} }
}, },
tr: { tr: {
hint: 'Tıkla veya yüklemek istediğini buraya sürükle', hint: "Tıkla veya yüklemek istediğini buraya sürükle",
loading: 'Yükleniyor…', loading: "Yükleniyor…",
noSupported: 'Tarayıcı desteklenmiyor, lütfen IE10+ veya farklı tarayıcı kullanın', noSupported:
success: 'Yükleme başarılı', "Tarayıcı desteklenmiyor, lütfen IE10+ veya farklı tarayıcı kullanın",
fail: 'Yüklemede hata oluştu', success: "Yükleme başarılı",
preview: 'Önizle', fail: "Yüklemede hata oluştu",
preview: "Önizle",
btn: { btn: {
off: 'İptal', off: "İptal",
close: 'Kapat', close: "Kapat",
back: 'Geri', back: "Geri",
save: 'Kaydet' save: "Kaydet"
}, },
error: { error: {
onlyImg: 'Sadece resim', onlyImg: "Sadece resim",
outOfSize: 'Resim yükleme limitini aşıyor: ', outOfSize: "Resim yükleme limitini aşıyor: ",
lowestPx: 'Resmin boyutu çok küçük. En az olması gereken: ' lowestPx: "Resmin boyutu çok küçük. En az olması gereken: "
} }
}, },
'es-MX': { "es-MX": {
hint: 'Selecciona o arrastra una imagen', hint: "Selecciona o arrastra una imagen",
loading: 'Subiendo...', loading: "Subiendo...",
noSupported: 'Tu navegador no es soportado, porfavor usa IE10+ u otros navegadores mas recientes', noSupported:
success: 'Subido exitosamente', "Tu navegador no es soportado, porfavor usa IE10+ u otros navegadores mas recientes",
fail: 'Sucedió un error', success: "Subido exitosamente",
preview: 'Vista previa', fail: "Sucedió un error",
preview: "Vista previa",
btn: { btn: {
off: 'Cancelar', off: "Cancelar",
close: 'Cerrar', close: "Cerrar",
back: 'Atras', back: "Atras",
save: 'Guardar' save: "Guardar"
}, },
error: { error: {
onlyImg: 'Unicamente imagenes', onlyImg: "Unicamente imagenes",
outOfSize: 'La imagen excede el tamaño maximo:', outOfSize: "La imagen excede el tamaño maximo:",
lowestPx: 'La imagen es demasiado pequeño. Se espera por lo menos:' lowestPx: "La imagen es demasiado pequeño. Se espera por lo menos:"
} }
}, },
de: { de: {
hint: 'Klick hier oder zieh eine Datei hier rein zum Hochladen', hint: "Klick hier oder zieh eine Datei hier rein zum Hochladen",
loading: 'Hochladen…', loading: "Hochladen…",
noSupported: 'Browser wird nicht unterstützt, bitte verwende IE10+ oder andere Browser', noSupported:
success: 'Upload erfolgreich', "Browser wird nicht unterstützt, bitte verwende IE10+ oder andere Browser",
fail: 'Upload fehlgeschlagen', success: "Upload erfolgreich",
preview: 'Vorschau', fail: "Upload fehlgeschlagen",
preview: "Vorschau",
btn: { btn: {
off: 'Abbrechen', off: "Abbrechen",
close: 'Schließen', close: "Schließen",
back: 'Zurück', back: "Zurück",
save: 'Speichern' save: "Speichern"
}, },
error: { error: {
onlyImg: 'Nur Bilder', onlyImg: "Nur Bilder",
outOfSize: 'Das Bild ist zu groß: ', outOfSize: "Das Bild ist zu groß: ",
lowestPx: 'Das Bild ist zu klein. Mindestens: ' lowestPx: "Das Bild ist zu klein. Mindestens: "
} }
}, },
ja: { ja: {
hint: 'クリック・ドラッグしてファイルをアップロード', hint: "クリック・ドラッグしてファイルをアップロード",
loading: 'アップロード中...', loading: "アップロード中...",
noSupported: 'このブラウザは対応されていません。IE10+かその他の主要ブラウザをお使いください。', noSupported:
success: 'アップロード成功', "このブラウザは対応されていません。IE10+かその他の主要ブラウザをお使いください。",
fail: 'アップロード失敗', success: "アップロード成功",
preview: 'プレビュー', fail: "アップロード失敗",
preview: "プレビュー",
btn: { btn: {
off: 'キャンセル', off: "キャンセル",
close: '閉じる', close: "閉じる",
back: '戻る', back: "戻る",
save: '保存' save: "保存"
}, },
error: { error: {
onlyImg: '画像のみ', onlyImg: "画像のみ",
outOfSize: '画像サイズが上限を超えています。上限: ', outOfSize: "画像サイズが上限を超えています。上限: ",
lowestPx: '画像が小さすぎます。最小サイズ: ' lowestPx: "画像が小さすぎます。最小サイズ: "
} }
} }
} };

View File

@ -1,7 +1,7 @@
export default { export default {
'jpg': 'image/jpeg', jpg: "image/jpeg",
'png': 'image/png', png: "image/png",
'gif': 'image/gif', gif: "image/gif",
'svg': 'image/svg+xml', svg: "image/svg+xml",
'psd': 'image/photoshop' psd: "image/photoshop"
} };

View File

@ -5,57 +5,57 @@
</template> </template>
<script> <script>
import CodeMirror from 'codemirror' import CodeMirror from "codemirror";
import 'codemirror/addon/lint/lint.css' import "codemirror/addon/lint/lint.css";
import 'codemirror/lib/codemirror.css' import "codemirror/lib/codemirror.css";
import 'codemirror/theme/rubyblue.css' import "codemirror/theme/rubyblue.css";
require('script-loader!jsonlint') require("script-loader!jsonlint");
import 'codemirror/mode/javascript/javascript' import "codemirror/mode/javascript/javascript";
import 'codemirror/addon/lint/lint' import "codemirror/addon/lint/lint";
import 'codemirror/addon/lint/json-lint' import "codemirror/addon/lint/json-lint";
export default { export default {
name: 'JsonEditor', name: "JsonEditor",
/* eslint-disable vue/require-prop-types */ /* eslint-disable vue/require-prop-types */
props: ['value'], props: ["value"],
data() { data() {
return { return {
jsonEditor: false jsonEditor: false
} };
}, },
watch: { watch: {
value(value) { value(value) {
const editorValue = this.jsonEditor.getValue() const editorValue = this.jsonEditor.getValue();
if (value !== editorValue) { if (value !== editorValue) {
this.jsonEditor.setValue(JSON.stringify(this.value, null, 2)) this.jsonEditor.setValue(JSON.stringify(this.value, null, 2));
} }
} }
}, },
mounted() { mounted() {
this.jsonEditor = CodeMirror.fromTextArea(this.$refs.textarea, { this.jsonEditor = CodeMirror.fromTextArea(this.$refs.textarea, {
lineNumbers: true, lineNumbers: true,
mode: 'application/json', mode: "application/json",
gutters: ['CodeMirror-lint-markers'], gutters: ["CodeMirror-lint-markers"],
theme: 'rubyblue', theme: "rubyblue",
lint: true lint: true
}) });
this.jsonEditor.setValue(JSON.stringify(this.value, null, 2)) this.jsonEditor.setValue(JSON.stringify(this.value, null, 2));
this.jsonEditor.on('change', cm => { this.jsonEditor.on("change", cm => {
this.$emit('changed', cm.getValue()) this.$emit("changed", cm.getValue());
this.$emit('input', cm.getValue()) this.$emit("input", cm.getValue());
}) });
}, },
methods: { methods: {
getValue() { getValue() {
return this.jsonEditor.getValue() return this.jsonEditor.getValue();
} }
} }
} };
</script> </script>
<style scoped> <style scoped>
.json-editor{ .json-editor {
height: 100%; height: 100%;
position: relative; position: relative;
} }
@ -63,10 +63,10 @@ export default {
height: auto; height: auto;
min-height: 300px; min-height: 300px;
} }
.json-editor >>> .CodeMirror-scroll{ .json-editor >>> .CodeMirror-scroll {
min-height: 300px; min-height: 300px;
} }
.json-editor >>> .cm-s-rubyblue span.cm-string { .json-editor >>> .cm-s-rubyblue span.cm-string {
color: #F08047; color: #f08047;
} }
</style> </style>

View File

@ -17,28 +17,28 @@
</template> </template>
<script> <script>
import draggable from 'vuedraggable' import draggable from "vuedraggable";
export default { export default {
name: 'DragKanbanDemo', name: "DragKanbanDemo",
components: { components: {
draggable draggable
}, },
props: { props: {
headerText: { headerText: {
type: String, type: String,
default: 'Header' default: "Header"
}, },
options: { options: {
type: Object, type: Object,
default() { default() {
return {} return {};
} }
}, },
list: { list: {
type: Array, type: Array,
default() { default() {
return [] return [];
} }
} }
}, },
@ -46,10 +46,10 @@ export default {
setData(dataTransfer) { setData(dataTransfer) {
// to avoid Firefox bug // to avoid Firefox bug
// Detail see : https://github.com/RubaXa/Sortable/issues/1012 // Detail see : https://github.com/RubaXa/Sortable/issues/1012
dataTransfer.setData('Text', '') dataTransfer.setData("Text", "");
} }
} }
} };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.board-column { .board-column {
@ -96,4 +96,3 @@ export default {
} }
} }
</style> </style>

View File

@ -1,7 +1,11 @@
<template> <template>
<div :class="computedClasses" class="material-input__component"> <div :class="computedClasses" class="material-input__component">
<div :class="{iconClass:icon}"> <div :class="{ iconClass: icon }">
<i v-if="icon" :class="['el-icon-' + icon]" class="el-input__icon material-input__icon" /> <i
v-if="icon"
:class="['el-icon-' + icon]"
class="el-input__icon material-input__icon"
/>
<input <input
v-if="type === 'email'" v-if="type === 'email'"
v-model="currentValue" v-model="currentValue"
@ -16,7 +20,7 @@
@focus="handleMdFocus" @focus="handleMdFocus"
@blur="handleMdBlur" @blur="handleMdBlur"
@input="handleModelInput" @input="handleModelInput"
> />
<input <input
v-if="type === 'url'" v-if="type === 'url'"
v-model="currentValue" v-model="currentValue"
@ -31,7 +35,7 @@
@focus="handleMdFocus" @focus="handleMdFocus"
@blur="handleMdBlur" @blur="handleMdBlur"
@input="handleModelInput" @input="handleModelInput"
> />
<input <input
v-if="type === 'number'" v-if="type === 'number'"
v-model="currentValue" v-model="currentValue"
@ -51,7 +55,7 @@
@focus="handleMdFocus" @focus="handleMdFocus"
@blur="handleMdBlur" @blur="handleMdBlur"
@input="handleModelInput" @input="handleModelInput"
> />
<input <input
v-if="type === 'password'" v-if="type === 'password'"
v-model="currentValue" v-model="currentValue"
@ -68,7 +72,7 @@
@focus="handleMdFocus" @focus="handleMdFocus"
@blur="handleMdBlur" @blur="handleMdBlur"
@input="handleModelInput" @input="handleModelInput"
> />
<input <input
v-if="type === 'tel'" v-if="type === 'tel'"
v-model="currentValue" v-model="currentValue"
@ -83,7 +87,7 @@
@focus="handleMdFocus" @focus="handleMdFocus"
@blur="handleMdBlur" @blur="handleMdBlur"
@input="handleModelInput" @input="handleModelInput"
> />
<input <input
v-if="type === 'text'" v-if="type === 'text'"
v-model="currentValue" v-model="currentValue"
@ -100,7 +104,7 @@
@focus="handleMdFocus" @focus="handleMdFocus"
@blur="handleMdBlur" @blur="handleMdBlur"
@input="handleModelInput" @input="handleModelInput"
> />
<span class="material-input-bar" /> <span class="material-input-bar" />
<label class="material-label"> <label class="material-label">
<slot /> <slot />
@ -113,14 +117,14 @@
// source:https://github.com/wemake-services/vue-material-input/blob/master/src/components/MaterialInput.vue // source:https://github.com/wemake-services/vue-material-input/blob/master/src/components/MaterialInput.vue
export default { export default {
name: 'MdInput', name: "MdInput",
props: { props: {
/* eslint-disable */ /* eslint-disable */
icon: String, icon: String,
name: String, name: String,
type: { type: {
type: String, type: String,
default: 'text' default: "text"
}, },
value: [String, Number], value: [String, Number],
placeholder: String, placeholder: String,
@ -137,7 +141,7 @@ export default {
}, },
autoComplete: { autoComplete: {
type: String, type: String,
default: 'off' default: "off"
}, },
validateEvent: { validateEvent: {
type: Boolean, type: Boolean,
@ -149,94 +153,94 @@ export default {
currentValue: this.value, currentValue: this.value,
focus: false, focus: false,
fillPlaceHolder: null fillPlaceHolder: null
} };
}, },
computed: { computed: {
computedClasses() { computedClasses() {
return { return {
'material--active': this.focus, "material--active": this.focus,
'material--disabled': this.disabled, "material--disabled": this.disabled,
'material--raised': Boolean(this.focus || this.currentValue) // has value "material--raised": Boolean(this.focus || this.currentValue) // has value
} };
} }
}, },
watch: { watch: {
value(newValue) { value(newValue) {
this.currentValue = newValue this.currentValue = newValue;
} }
}, },
methods: { methods: {
handleModelInput(event) { handleModelInput(event) {
const value = event.target.value const value = event.target.value;
this.$emit('input', value) this.$emit("input", value);
if (this.$parent.$options.componentName === 'ElFormItem') { if (this.$parent.$options.componentName === "ElFormItem") {
if (this.validateEvent) { if (this.validateEvent) {
this.$parent.$emit('el.form.change', [value]) this.$parent.$emit("el.form.change", [value]);
} }
} }
this.$emit('change', value) this.$emit("change", value);
}, },
handleMdFocus(event) { handleMdFocus(event) {
this.focus = true this.focus = true;
this.$emit('focus', event) this.$emit("focus", event);
if (this.placeholder && this.placeholder !== '') { if (this.placeholder && this.placeholder !== "") {
this.fillPlaceHolder = this.placeholder this.fillPlaceHolder = this.placeholder;
} }
}, },
handleMdBlur(event) { handleMdBlur(event) {
this.focus = false this.focus = false;
this.$emit('blur', event) this.$emit("blur", event);
this.fillPlaceHolder = null this.fillPlaceHolder = null;
if (this.$parent.$options.componentName === 'ElFormItem') { if (this.$parent.$options.componentName === "ElFormItem") {
if (this.validateEvent) { if (this.validateEvent) {
this.$parent.$emit('el.form.blur', [this.currentValue]) this.$parent.$emit("el.form.blur", [this.currentValue]);
} }
} }
} }
} }
} };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
// Fonts: // Fonts:
$font-size-base: 16px; $font-size-base: 16px;
$font-size-small: 18px; $font-size-small: 18px;
$font-size-smallest: 12px; $font-size-smallest: 12px;
$font-weight-normal: normal; $font-weight-normal: normal;
$font-weight-bold: bold; $font-weight-bold: bold;
$apixel: 1px; $apixel: 1px;
// Utils // Utils
$spacer: 12px; $spacer: 12px;
$transition: 0.2s ease all; $transition: 0.2s ease all;
$index: 0px; $index: 0px;
$index-has-icon: 30px; $index-has-icon: 30px;
// Theme: // Theme:
$color-white: white; $color-white: white;
$color-grey: #9E9E9E; $color-grey: #9e9e9e;
$color-grey-light: #E0E0E0; $color-grey-light: #e0e0e0;
$color-blue: #2196F3; $color-blue: #2196f3;
$color-red: #F44336; $color-red: #f44336;
$color-black: black; $color-black: black;
// Base clases: // Base clases:
%base-bar-pseudo { %base-bar-pseudo {
content: ''; content: "";
height: 1px; height: 1px;
width: 0; width: 0;
bottom: 0; bottom: 0;
position: absolute; position: absolute;
transition: $transition; transition: $transition;
} }
// Mixins: // Mixins:
@mixin slided-top() { @mixin slided-top() {
top: - ($font-size-base + $spacer); top: -($font-size-base + $spacer);
left: 0; left: 0;
font-size: $font-size-base; font-size: $font-size-base;
font-weight: $font-weight-bold; font-weight: $font-weight-bold;
} }
// Component: // Component:
.material-input__component { .material-input__component {
margin-top: 36px; margin-top: 36px;
position: relative; position: relative;
* { * {
@ -319,9 +323,9 @@ export default {
} }
} }
} }
} }
.material-input__component { .material-input__component {
background: $color-white; background: $color-white;
.material-input { .material-input {
background: none; background: none;
@ -356,5 +360,5 @@ export default {
} }
} }
} }
} }
</style> </style>

View File

@ -1,31 +1,31 @@
// doc: https://nhnent.github.io/tui.editor/api/latest/ToastUIEditor.html#ToastUIEditor // doc: https://nhnent.github.io/tui.editor/api/latest/ToastUIEditor.html#ToastUIEditor
export default { export default {
minHeight: '200px', minHeight: "200px",
previewStyle: 'vertical', previewStyle: "vertical",
useCommandShortcut: true, useCommandShortcut: true,
useDefaultHTMLSanitizer: true, useDefaultHTMLSanitizer: true,
usageStatistics: false, usageStatistics: false,
hideModeSwitch: false, hideModeSwitch: false,
toolbarItems: [ toolbarItems: [
'heading', "heading",
'bold', "bold",
'italic', "italic",
'strike', "strike",
'divider', "divider",
'hr', "hr",
'quote', "quote",
'divider', "divider",
'ul', "ul",
'ol', "ol",
'task', "task",
'indent', "indent",
'outdent', "outdent",
'divider', "divider",
'table', "table",
'image', "image",
'link', "link",
'divider', "divider",
'code', "code",
'codeblock' "codeblock"
] ]
} };

View File

@ -4,115 +4,119 @@
<script> <script>
// deps for editor // deps for editor
import 'codemirror/lib/codemirror.css' // codemirror import "codemirror/lib/codemirror.css"; // codemirror
import 'tui-editor/dist/tui-editor.css' // editor ui import "tui-editor/dist/tui-editor.css"; // editor ui
import 'tui-editor/dist/tui-editor-contents.css' // editor content import "tui-editor/dist/tui-editor-contents.css"; // editor content
import Editor from 'tui-editor' import Editor from "tui-editor";
import defaultOptions from './default-options' import defaultOptions from "./default-options";
export default { export default {
name: 'MarddownEditor', name: "MarddownEditor",
props: { props: {
value: { value: {
type: String, type: String,
default: '' default: ""
}, },
id: { id: {
type: String, type: String,
required: false, required: false,
default() { default() {
return 'markdown-editor-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '') return (
"markdown-editor-" +
+new Date() +
((Math.random() * 1000).toFixed(0) + "")
);
} }
}, },
options: { options: {
type: Object, type: Object,
default() { default() {
return defaultOptions return defaultOptions;
} }
}, },
mode: { mode: {
type: String, type: String,
default: 'markdown' default: "markdown"
}, },
height: { height: {
type: String, type: String,
required: false, required: false,
default: '300px' default: "300px"
}, },
language: { language: {
type: String, type: String,
required: false, required: false,
default: 'en_US' // https://github.com/nhnent/tui.editor/tree/master/src/js/langs default: "en_US" // https://github.com/nhnent/tui.editor/tree/master/src/js/langs
} }
}, },
data() { data() {
return { return {
editor: null editor: null
} };
}, },
computed: { computed: {
editorOptions() { editorOptions() {
const options = Object.assign({}, defaultOptions, this.options) const options = Object.assign({}, defaultOptions, this.options);
options.initialEditType = this.mode options.initialEditType = this.mode;
options.height = this.height options.height = this.height;
options.language = this.language options.language = this.language;
return options return options;
} }
}, },
watch: { watch: {
value(newValue, preValue) { value(newValue, preValue) {
if (newValue !== preValue && newValue !== this.editor.getValue()) { if (newValue !== preValue && newValue !== this.editor.getValue()) {
this.editor.setValue(newValue) this.editor.setValue(newValue);
} }
}, },
language(val) { language(val) {
this.destroyEditor() this.destroyEditor();
this.initEditor() this.initEditor();
}, },
height(newValue) { height(newValue) {
this.editor.height(newValue) this.editor.height(newValue);
}, },
mode(newValue) { mode(newValue) {
this.editor.changeMode(newValue) this.editor.changeMode(newValue);
} }
}, },
mounted() { mounted() {
this.initEditor() this.initEditor();
}, },
destroyed() { destroyed() {
this.destroyEditor() this.destroyEditor();
}, },
methods: { methods: {
initEditor() { initEditor() {
this.editor = new Editor({ this.editor = new Editor({
el: document.getElementById(this.id), el: document.getElementById(this.id),
...this.editorOptions ...this.editorOptions
}) });
if (this.value) { if (this.value) {
this.editor.setValue(this.value) this.editor.setValue(this.value);
} }
this.editor.on('change', () => { this.editor.on("change", () => {
this.$emit('input', this.editor.getValue()) this.$emit("input", this.editor.getValue());
}) });
}, },
destroyEditor() { destroyEditor() {
if (!this.editor) return if (!this.editor) return;
this.editor.off('change') this.editor.off("change");
this.editor.remove() this.editor.remove();
}, },
setValue(value) { setValue(value) {
this.editor.setValue(value) this.editor.setValue(value);
}, },
getValue() { getValue() {
return this.editor.getValue() return this.editor.getValue();
}, },
setHtml(value) { setHtml(value) {
this.editor.setHtml(value) this.editor.setHtml(value);
}, },
getHtml() { getHtml() {
return this.editor.getHtml() return this.editor.getHtml();
} }
} }
} };
</script> </script>

View File

@ -1,5 +1,5 @@
<template> <template>
<div :class="{'hidden':hidden}" class="pagination-container"> <div :class="{ hidden: hidden }" class="pagination-container">
<el-pagination <el-pagination
:background="background" :background="background"
:current-page.sync="currentPage" :current-page.sync="currentPage"
@ -15,10 +15,10 @@
</template> </template>
<script> <script>
import { scrollTo } from '@/utils/scroll-to' import { scrollTo } from "@/utils/scroll-to";
export default { export default {
name: 'Pagination', name: "Pagination",
props: { props: {
total: { total: {
required: true, required: true,
@ -35,12 +35,12 @@ export default {
pageSizes: { pageSizes: {
type: Array, type: Array,
default() { default() {
return [10, 20, 30, 50] return [10, 20, 30, 50];
} }
}, },
layout: { layout: {
type: String, type: String,
default: 'total, sizes, prev, pager, next, jumper' default: "total, sizes, prev, pager, next, jumper"
}, },
background: { background: {
type: Boolean, type: Boolean,
@ -58,36 +58,36 @@ export default {
computed: { computed: {
currentPage: { currentPage: {
get() { get() {
return this.page return this.page;
}, },
set(val) { set(val) {
this.$emit('update:page', val) this.$emit("update:page", val);
} }
}, },
pageSize: { pageSize: {
get() { get() {
return this.limit return this.limit;
}, },
set(val) { set(val) {
this.$emit('update:limit', val) this.$emit("update:limit", val);
} }
} }
}, },
methods: { methods: {
handleSizeChange(val) { handleSizeChange(val) {
this.$emit('pagination', { page: this.currentPage, limit: val }) this.$emit("pagination", { page: this.currentPage, limit: val });
if (this.autoScroll) { if (this.autoScroll) {
scrollTo(0, 800) scrollTo(0, 800);
} }
}, },
handleCurrentChange(val) { handleCurrentChange(val) {
this.$emit('pagination', { page: val, limit: this.pageSize }) this.$emit("pagination", { page: val, limit: this.pageSize });
if (this.autoScroll) { if (this.autoScroll) {
scrollTo(0, 800) scrollTo(0, 800);
} }
} }
} }
} };
</script> </script>
<style scoped> <style scoped>

View File

@ -1,18 +1,21 @@
<template> <template>
<div :style="{zIndex:zIndex,height:height,width:width}" class="pan-item"> <div
:style="{ zIndex: zIndex, height: height, width: width }"
class="pan-item"
>
<div class="pan-info"> <div class="pan-info">
<div class="pan-info-roles-container"> <div class="pan-info-roles-container">
<slot /> <slot />
</div> </div>
</div> </div>
<!-- eslint-disable-next-line --> <!-- eslint-disable-next-line -->
<div :style="{backgroundImage: `url(${image})`}" class="pan-thumb"></div> <div :style="{ backgroundImage: `url(${image})` }" class="pan-thumb"></div>
</div> </div>
</template> </template>
<script> <script>
export default { export default {
name: 'PanThumb', name: "PanThumb",
props: { props: {
image: { image: {
type: String, type: String,
@ -24,14 +27,14 @@ export default {
}, },
width: { width: {
type: String, type: String,
default: '150px' default: "150px"
}, },
height: { height: {
type: String, type: String,
default: '150px' default: "150px"
} }
} }
} };
</script> </script>
<style scoped> <style scoped>
@ -93,7 +96,7 @@ export default {
margin: 0 60px; margin: 0 60px;
padding: 22px 0 0 0; padding: 22px 0 0 0;
height: 85px; height: 85px;
font-family: 'Open Sans', Arial, sans-serif; font-family: "Open Sans", Arial, sans-serif;
text-shadow: 0 0 1px #fff, 0 1px 2px rgba(0, 0, 0, 0.3); text-shadow: 0 0 1px #fff, 0 1px 2px rgba(0, 0, 0, 0.3);
} }
@ -121,9 +124,10 @@ export default {
letter-spacing: 1px; letter-spacing: 1px;
padding-top: 24px; padding-top: 24px;
margin: 7px auto 0; margin: 7px auto 0;
font-family: 'Open Sans', Arial, sans-serif; font-family: "Open Sans", Arial, sans-serif;
opacity: 0; opacity: 0;
transition: transform 0.3s ease-in-out 0.2s, opacity 0.3s ease-in-out 0.2s, background 0.2s linear 0s; transition: transform 0.3s ease-in-out 0.2s, opacity 0.3s ease-in-out 0.2s,
background 0.2s linear 0s;
transform: translateX(60px) rotate(90deg); transform: translateX(60px) rotate(90deg);
} }

View File

@ -1,9 +1,13 @@
<template> <template>
<div ref="rightPanel" :class="{show:show}" class="rightPanel-container"> <div ref="rightPanel" :class="{ show: show }" class="rightPanel-container">
<div class="rightPanel-background" /> <div class="rightPanel-background" />
<div class="rightPanel"> <div class="rightPanel">
<div class="handle-button" :style="{'top':buttonTop+'px','background-color':theme}" @click="show=!show"> <div
<i :class="show?'el-icon-close':'el-icon-setting'" /> class="handle-button"
:style="{ top: buttonTop + 'px', 'background-color': theme }"
@click="show = !show"
>
<i :class="show ? 'el-icon-close' : 'el-icon-setting'" />
</div> </div>
<div class="rightPanel-items"> <div class="rightPanel-items">
<slot /> <slot />
@ -13,10 +17,10 @@
</template> </template>
<script> <script>
import { addClass, removeClass } from '@/utils' import { addClass, removeClass } from "@/utils";
export default { export default {
name: 'RightPanel', name: "RightPanel",
props: { props: {
clickNotClose: { clickNotClose: {
default: false, default: false,
@ -30,50 +34,50 @@ export default {
data() { data() {
return { return {
show: false show: false
} };
}, },
computed: { computed: {
theme() { theme() {
return this.$store.state.settings.theme return this.$store.state.settings.theme;
} }
}, },
watch: { watch: {
show(value) { show(value) {
if (value && !this.clickNotClose) { if (value && !this.clickNotClose) {
this.addEventClick() this.addEventClick();
} }
if (value) { if (value) {
addClass(document.body, 'showRightPanel') addClass(document.body, "showRightPanel");
} else { } else {
removeClass(document.body, 'showRightPanel') removeClass(document.body, "showRightPanel");
} }
} }
}, },
mounted() { mounted() {
this.insertToBody() this.insertToBody();
}, },
beforeDestroy() { beforeDestroy() {
const elx = this.$refs.rightPanel const elx = this.$refs.rightPanel;
elx.remove() elx.remove();
}, },
methods: { methods: {
addEventClick() { addEventClick() {
window.addEventListener('click', this.closeSidebar) window.addEventListener("click", this.closeSidebar);
}, },
closeSidebar(evt) { closeSidebar(evt) {
const parent = evt.target.closest('.rightPanel') const parent = evt.target.closest(".rightPanel");
if (!parent) { if (!parent) {
this.show = false this.show = false;
window.removeEventListener('click', this.closeSidebar) window.removeEventListener("click", this.closeSidebar);
} }
}, },
insertToBody() { insertToBody() {
const elx = this.$refs.rightPanel const elx = this.$refs.rightPanel;
const body = document.querySelector('body') const body = document.querySelector("body");
body.insertBefore(elx, body.firstChild) body.insertBefore(elx, body.firstChild);
} }
} }
} };
</script> </script>
<style> <style>
@ -90,8 +94,8 @@ export default {
top: 0; top: 0;
left: 0; left: 0;
opacity: 0; opacity: 0;
transition: opacity .3s cubic-bezier(.7, .3, .1, 1); transition: opacity 0.3s cubic-bezier(0.7, 0.3, 0.1, 1);
background: rgba(0, 0, 0, .2); background: rgba(0, 0, 0, 0.2);
z-index: -1; z-index: -1;
} }
@ -102,15 +106,15 @@ export default {
position: fixed; position: fixed;
top: 0; top: 0;
right: 0; right: 0;
box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, .05); box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, 0.05);
transition: all .25s cubic-bezier(.7, .3, .1, 1); transition: all 0.25s cubic-bezier(0.7, 0.3, 0.1, 1);
transform: translate(100%); transform: translate(100%);
background: #fff; background: #fff;
z-index: 40000; z-index: 40000;
} }
.show { .show {
transition: all .3s cubic-bezier(.7, .3, .1, 1); transition: all 0.3s cubic-bezier(0.7, 0.3, 0.1, 1);
.rightPanel-background { .rightPanel-background {
z-index: 20000; z-index: 20000;

View File

@ -1,58 +1,61 @@
<template> <template>
<div> <div>
<svg-icon :icon-class="isFullscreen?'exit-fullscreen':'fullscreen'" @click="click" /> <svg-icon
:icon-class="isFullscreen ? 'exit-fullscreen' : 'fullscreen'"
@click="click"
/>
</div> </div>
</template> </template>
<script> <script>
import screenfull from 'screenfull' import screenfull from "screenfull";
export default { export default {
name: 'Screenfull', name: "Screenfull",
data() { data() {
return { return {
isFullscreen: false isFullscreen: false
} };
}, },
mounted() { mounted() {
this.init() this.init();
}, },
beforeDestroy() { beforeDestroy() {
this.destroy() this.destroy();
}, },
methods: { methods: {
click() { click() {
if (!screenfull.enabled) { if (!screenfull.enabled) {
this.$message({ this.$message({
message: 'you browser can not work', message: "you browser can not work",
type: 'warning' type: "warning"
}) });
return false return false;
} }
screenfull.toggle() screenfull.toggle();
}, },
change() { change() {
this.isFullscreen = screenfull.isFullscreen this.isFullscreen = screenfull.isFullscreen;
}, },
init() { init() {
if (screenfull.enabled) { if (screenfull.enabled) {
screenfull.on('change', this.change) screenfull.on("change", this.change);
} }
}, },
destroy() { destroy() {
if (screenfull.enabled) { if (screenfull.enabled) {
screenfull.off('change', this.change) screenfull.off("change", this.change);
} }
} }
} }
} };
</script> </script>
<style scoped> <style scoped>
.screenfull-svg { .screenfull-svg {
display: inline-block; display: inline-block;
cursor: pointer; cursor: pointer;
fill: #5a5e66;; fill: #5a5e66;
width: 20px; width: 20px;
height: 20px; height: 20px;
vertical-align: 10px; vertical-align: 10px;

View File

@ -1,9 +1,17 @@
<template> <template>
<div :class="{active:isActive}" class="share-dropdown-menu"> <div :class="{ active: isActive }" class="share-dropdown-menu">
<div class="share-dropdown-menu-wrapper"> <div class="share-dropdown-menu-wrapper">
<span class="share-dropdown-menu-title" @click.self="clickTitle">{{ title }}</span> <span class="share-dropdown-menu-title" @click.self="clickTitle">{{
<div v-for="(item,index) of items" :key="index" class="share-dropdown-menu-item"> title
<a v-if="item.href" :href="item.href" target="_blank">{{ item.title }}</a> }}</span>
<div
v-for="(item, index) of items"
:key="index"
class="share-dropdown-menu-item"
>
<a v-if="item.href" :href="item.href" target="_blank">{{
item.title
}}</a>
<span v-else>{{ item.title }}</span> <span v-else>{{ item.title }}</span>
</div> </div>
</div> </div>
@ -16,30 +24,30 @@ export default {
items: { items: {
type: Array, type: Array,
default: function() { default: function() {
return [] return [];
} }
}, },
title: { title: {
type: String, type: String,
default: 'vue' default: "vue"
} }
}, },
data() { data() {
return { return {
isActive: false isActive: false
} };
}, },
methods: { methods: {
clickTitle() { clickTitle() {
this.isActive = !this.isActive this.isActive = !this.isActive;
} }
} }
} };
</script> </script>
<style lang="scss" > <style lang="scss">
$n: 9; //items.length $n: 9; //items.length
$t: .1s; $t: 0.1s;
.share-dropdown-menu { .share-dropdown-menu {
width: 250px; width: 250px;
position: relative; position: relative;
@ -55,7 +63,7 @@ $t: .1s;
font-size: 20px; font-size: 20px;
text-align: center; text-align: center;
z-index: 2; z-index: 2;
transform: translate3d(0,0,0); transform: translate3d(0, 0, 0);
} }
&-wrapper { &-wrapper {
position: relative; position: relative;
@ -78,7 +86,7 @@ $t: .1s;
@for $i from 1 through $n { @for $i from 1 through $n {
&:nth-of-type(#{$i}) { &:nth-of-type(#{$i}) {
z-index: -1; z-index: -1;
transition-delay: $i*$t; transition-delay: $i * $t;
transform: translate3d(0, -60px, 0); transform: translate3d(0, -60px, 0);
} }
} }
@ -90,8 +98,8 @@ $t: .1s;
.share-dropdown-menu-item { .share-dropdown-menu-item {
@for $i from 1 through $n { @for $i from 1 through $n {
&:nth-of-type(#{$i}) { &:nth-of-type(#{$i}) {
transition-delay: ($n - $i)*$t; transition-delay: ($n - $i) * $t;
transform: translate3d(0, ($i - 1)*60px, 0); transform: translate3d(0, ($i - 1) * 60px, 0);
} }
} }
} }

View File

@ -4,9 +4,13 @@
<svg-icon class-name="size-icon" icon-class="size" /> <svg-icon class-name="size-icon" icon-class="size" />
</div> </div>
<el-dropdown-menu slot="dropdown"> <el-dropdown-menu slot="dropdown">
<el-dropdown-item v-for="item of sizeOptions" :key="item.value" :disabled="size===item.value" :command="item.value"> <el-dropdown-item
{{ v-for="item of sizeOptions"
item.label }} :key="item.value"
:disabled="size === item.value"
:command="item.value"
>
{{ item.label }}
</el-dropdown-item> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</el-dropdown> </el-dropdown>
@ -17,41 +21,40 @@ export default {
data() { data() {
return { return {
sizeOptions: [ sizeOptions: [
{ label: 'Default', value: 'default' }, { label: "Default", value: "default" },
{ label: 'Medium', value: 'medium' }, { label: "Medium", value: "medium" },
{ label: 'Small', value: 'small' }, { label: "Small", value: "small" },
{ label: 'Mini', value: 'mini' } { label: "Mini", value: "mini" }
] ]
} };
}, },
computed: { computed: {
size() { size() {
return this.$store.getters.size return this.$store.getters.size;
} }
}, },
methods: { methods: {
handleSetSize(size) { handleSetSize(size) {
this.$ELEMENT.size = size this.$ELEMENT.size = size;
this.$store.dispatch('app/setSize', size) this.$store.dispatch("app/setSize", size);
this.refreshView() this.refreshView();
this.$message({ this.$message({
message: 'Switch Size Success', message: "Switch Size Success",
type: 'success' type: "success"
}) });
}, },
refreshView() { refreshView() {
// In order to make the cached page re-rendered // In order to make the cached page re-rendered
this.$store.dispatch('tagsView/delAllCachedViews', this.$route) this.$store.dispatch("tagsView/delAllCachedViews", this.$route);
const { fullPath } = this.$route const { fullPath } = this.$route;
this.$nextTick(() => { this.$nextTick(() => {
this.$router.replace({ this.$router.replace({
path: '/redirect' + fullPath path: "/redirect" + fullPath
}) });
}) });
} }
} }
};
}
</script> </script>

View File

@ -1,8 +1,14 @@
<template> <template>
<div :style="{height:height+'px',zIndex:zIndex}"> <div :style="{ height: height + 'px', zIndex: zIndex }">
<div <div
:class="className" :class="className"
:style="{top:(isSticky ? stickyTop +'px' : ''),zIndex:zIndex,position:position,width:width,height:height+'px'}" :style="{
top: isSticky ? stickyTop + 'px' : '',
zIndex: zIndex,
position: position,
width: width,
height: height + 'px'
}"
> >
<slot> <slot>
<div>sticky</div> <div>sticky</div>
@ -13,7 +19,7 @@
<script> <script>
export default { export default {
name: 'Sticky', name: "Sticky",
props: { props: {
stickyTop: { stickyTop: {
type: Number, type: Number,
@ -25,67 +31,67 @@ export default {
}, },
className: { className: {
type: String, type: String,
default: '' default: ""
} }
}, },
data() { data() {
return { return {
active: false, active: false,
position: '', position: "",
width: undefined, width: undefined,
height: undefined, height: undefined,
isSticky: false isSticky: false
} };
}, },
mounted() { mounted() {
this.height = this.$el.getBoundingClientRect().height this.height = this.$el.getBoundingClientRect().height;
window.addEventListener('scroll', this.handleScroll) window.addEventListener("scroll", this.handleScroll);
window.addEventListener('resize', this.handleResize) window.addEventListener("resize", this.handleResize);
}, },
activated() { activated() {
this.handleScroll() this.handleScroll();
}, },
destroyed() { destroyed() {
window.removeEventListener('scroll', this.handleScroll) window.removeEventListener("scroll", this.handleScroll);
window.removeEventListener('resize', this.handleResize) window.removeEventListener("resize", this.handleResize);
}, },
methods: { methods: {
sticky() { sticky() {
if (this.active) { if (this.active) {
return return;
} }
this.position = 'fixed' this.position = "fixed";
this.active = true this.active = true;
this.width = this.width + 'px' this.width = this.width + "px";
this.isSticky = true this.isSticky = true;
}, },
handleReset() { handleReset() {
if (!this.active) { if (!this.active) {
return return;
} }
this.reset() this.reset();
}, },
reset() { reset() {
this.position = '' this.position = "";
this.width = 'auto' this.width = "auto";
this.active = false this.active = false;
this.isSticky = false this.isSticky = false;
}, },
handleScroll() { handleScroll() {
const width = this.$el.getBoundingClientRect().width const width = this.$el.getBoundingClientRect().width;
this.width = width || 'auto' this.width = width || "auto";
const offsetTop = this.$el.getBoundingClientRect().top const offsetTop = this.$el.getBoundingClientRect().top;
if (offsetTop < this.stickyTop) { if (offsetTop < this.stickyTop) {
this.sticky() this.sticky();
return return;
} }
this.handleReset() this.handleReset();
}, },
handleResize() { handleResize() {
if (this.isSticky) { if (this.isSticky) {
this.width = this.$el.getBoundingClientRect().width + 'px' this.width = this.$el.getBoundingClientRect().width + "px";
} }
} }
} }
} };
</script> </script>

View File

@ -1,5 +1,10 @@
<template> <template>
<div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" /> <div
v-if="isExternal"
:style="styleExternalIcon"
class="svg-external-icon svg-icon"
v-on="$listeners"
/>
<svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners"> <svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
<use :xlink:href="iconName" /> <use :xlink:href="iconName" />
</svg> </svg>
@ -7,10 +12,10 @@
<script> <script>
// doc: https://panjiachen.github.io/vue-element-admin-site/feature/component/svg-icon.html#usage // doc: https://panjiachen.github.io/vue-element-admin-site/feature/component/svg-icon.html#usage
import { isExternal } from '@/utils/validate' import { isExternal } from "@/utils/validate";
export default { export default {
name: 'SvgIcon', name: "SvgIcon",
props: { props: {
iconClass: { iconClass: {
type: String, type: String,
@ -18,31 +23,31 @@ export default {
}, },
className: { className: {
type: String, type: String,
default: '' default: ""
} }
}, },
computed: { computed: {
isExternal() { isExternal() {
return isExternal(this.iconClass) return isExternal(this.iconClass);
}, },
iconName() { iconName() {
return `#icon-${this.iconClass}` return `#icon-${this.iconClass}`;
}, },
svgClass() { svgClass() {
if (this.className) { if (this.className) {
return 'svg-icon ' + this.className return "svg-icon " + this.className;
} else { } else {
return 'svg-icon' return "svg-icon";
} }
}, },
styleExternalIcon() { styleExternalIcon() {
return { return {
mask: `url(${this.iconClass}) no-repeat 50% 50%`, mask: `url(${this.iconClass}) no-repeat 50% 50%`,
'-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%` "-webkit-mask": `url(${this.iconClass}) no-repeat 50% 50%`
};
} }
} }
} };
}
</script> </script>
<style scoped> <style scoped>
@ -56,7 +61,7 @@ export default {
.svg-external-icon { .svg-external-icon {
background-color: currentColor; background-color: currentColor;
mask-size: cover!important; mask-size: cover !important;
display: inline-block; display: inline-block;
} }
</style> </style>

View File

@ -11,14 +11,14 @@ export default {
props: { props: {
className: { className: {
type: String, type: String,
default: '' default: ""
}, },
text: { text: {
type: String, type: String,
default: 'vue-element-admin' default: "vue-element-admin"
} }
} }
} };
</script> </script>
<style> <style>
@ -27,7 +27,7 @@ export default {
.link--mallki { .link--mallki {
font-weight: 800; font-weight: 800;
color: #4dd9d5; color: #4dd9d5;
font-family: 'Dosis', sans-serif; font-family: "Dosis", sans-serif;
-webkit-transition: color 0.5s 0.25s; -webkit-transition: color 0.5s 0.25s;
transition: color 0.5s 0.25s; transition: color 0.5s 0.25s;
overflow: hidden; overflow: hidden;
@ -45,7 +45,7 @@ export default {
} }
.link--mallki::before { .link--mallki::before {
content: ''; content: "";
width: 100%; width: 100%;
height: 6px; height: 6px;
margin: -3px 0 0 0; margin: -3px 0 0 0;

View File

@ -1,160 +1,183 @@
<template> <template>
<el-color-picker <el-color-picker
v-model="theme" v-model="theme"
:predefine="['#409EFF', '#1890ff', '#304156','#212121','#11a983', '#13c2c2', '#6959CD', '#f5222d', ]" :predefine="[
'#409EFF',
'#1890ff',
'#304156',
'#212121',
'#11a983',
'#13c2c2',
'#6959CD',
'#f5222d'
]"
class="theme-picker" class="theme-picker"
popper-class="theme-picker-dropdown" popper-class="theme-picker-dropdown"
/> />
</template> </template>
<script> <script>
const version = require('element-ui/package.json').version // element-ui version from node_modules const version = require("element-ui/package.json").version; // element-ui version from node_modules
const ORIGINAL_THEME = '#409EFF' // default color const ORIGINAL_THEME = "#409EFF"; // default color
export default { export default {
data() { data() {
return { return {
chalk: '', // content of theme-chalk css chalk: "", // content of theme-chalk css
theme: '' theme: ""
} };
}, },
computed: { computed: {
defaultTheme() { defaultTheme() {
return this.$store.state.settings.theme return this.$store.state.settings.theme;
} }
}, },
watch: { watch: {
defaultTheme: { defaultTheme: {
handler: function(val, oldVal) { handler: function(val, oldVal) {
this.theme = val this.theme = val;
}, },
immediate: true immediate: true
}, },
async theme(val) { async theme(val) {
const oldVal = this.chalk ? this.theme : ORIGINAL_THEME const oldVal = this.chalk ? this.theme : ORIGINAL_THEME;
if (typeof val !== 'string') return if (typeof val !== "string") return;
const themeCluster = this.getThemeCluster(val.replace('#', '')) const themeCluster = this.getThemeCluster(val.replace("#", ""));
const originalCluster = this.getThemeCluster(oldVal.replace('#', '')) const originalCluster = this.getThemeCluster(oldVal.replace("#", ""));
console.log(themeCluster, originalCluster) console.log(themeCluster, originalCluster);
const $message = this.$message({ const $message = this.$message({
message: ' Compiling the theme', message: " Compiling the theme",
customClass: 'theme-message', customClass: "theme-message",
type: 'success', type: "success",
duration: 0, duration: 0,
iconClass: 'el-icon-loading' iconClass: "el-icon-loading"
}) });
const getHandler = (variable, id) => { const getHandler = (variable, id) => {
return () => { return () => {
const originalCluster = this.getThemeCluster(ORIGINAL_THEME.replace('#', '')) const originalCluster = this.getThemeCluster(
const newStyle = this.updateStyle(this[variable], originalCluster, themeCluster) ORIGINAL_THEME.replace("#", "")
);
const newStyle = this.updateStyle(
this[variable],
originalCluster,
themeCluster
);
let styleTag = document.getElementById(id) let styleTag = document.getElementById(id);
if (!styleTag) { if (!styleTag) {
styleTag = document.createElement('style') styleTag = document.createElement("style");
styleTag.setAttribute('id', id) styleTag.setAttribute("id", id);
document.head.appendChild(styleTag) document.head.appendChild(styleTag);
}
styleTag.innerText = newStyle
}
} }
styleTag.innerText = newStyle;
};
};
if (!this.chalk) { if (!this.chalk) {
const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css` const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`;
await this.getCSSString(url, 'chalk') await this.getCSSString(url, "chalk");
} }
const chalkHandler = getHandler('chalk', 'chalk-style') const chalkHandler = getHandler("chalk", "chalk-style");
chalkHandler() chalkHandler();
const styles = [].slice.call(document.querySelectorAll('style')) const styles = [].slice
.call(document.querySelectorAll("style"))
.filter(style => { .filter(style => {
const text = style.innerText const text = style.innerText;
return new RegExp(oldVal, 'i').test(text) && !/Chalk Variables/.test(text) return (
}) new RegExp(oldVal, "i").test(text) && !/Chalk Variables/.test(text)
);
});
styles.forEach(style => { styles.forEach(style => {
const { innerText } = style const { innerText } = style;
if (typeof innerText !== 'string') return if (typeof innerText !== "string") return;
style.innerText = this.updateStyle(innerText, originalCluster, themeCluster) style.innerText = this.updateStyle(
}) innerText,
originalCluster,
themeCluster
);
});
this.$emit('change', val) this.$emit("change", val);
$message.close() $message.close();
} }
}, },
methods: { methods: {
updateStyle(style, oldCluster, newCluster) { updateStyle(style, oldCluster, newCluster) {
let newStyle = style let newStyle = style;
oldCluster.forEach((color, index) => { oldCluster.forEach((color, index) => {
newStyle = newStyle.replace(new RegExp(color, 'ig'), newCluster[index]) newStyle = newStyle.replace(new RegExp(color, "ig"), newCluster[index]);
}) });
return newStyle return newStyle;
}, },
getCSSString(url, variable) { getCSSString(url, variable) {
return new Promise(resolve => { return new Promise(resolve => {
const xhr = new XMLHttpRequest() const xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => { xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && xhr.status === 200) { if (xhr.readyState === 4 && xhr.status === 200) {
this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, '') this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, "");
resolve() resolve();
} }
} };
xhr.open('GET', url) xhr.open("GET", url);
xhr.send() xhr.send();
}) });
}, },
getThemeCluster(theme) { getThemeCluster(theme) {
const tintColor = (color, tint) => { const tintColor = (color, tint) => {
let red = parseInt(color.slice(0, 2), 16) let red = parseInt(color.slice(0, 2), 16);
let green = parseInt(color.slice(2, 4), 16) let green = parseInt(color.slice(2, 4), 16);
let blue = parseInt(color.slice(4, 6), 16) let blue = parseInt(color.slice(4, 6), 16);
if (tint === 0) { // when primary color is in its rgb space if (tint === 0) {
return [red, green, blue].join(',') // when primary color is in its rgb space
return [red, green, blue].join(",");
} else { } else {
red += Math.round(tint * (255 - red)) red += Math.round(tint * (255 - red));
green += Math.round(tint * (255 - green)) green += Math.round(tint * (255 - green));
blue += Math.round(tint * (255 - blue)) blue += Math.round(tint * (255 - blue));
red = red.toString(16) red = red.toString(16);
green = green.toString(16) green = green.toString(16);
blue = blue.toString(16) blue = blue.toString(16);
return `#${red}${green}${blue}` return `#${red}${green}${blue}`;
}
} }
};
const shadeColor = (color, shade) => { const shadeColor = (color, shade) => {
let red = parseInt(color.slice(0, 2), 16) let red = parseInt(color.slice(0, 2), 16);
let green = parseInt(color.slice(2, 4), 16) let green = parseInt(color.slice(2, 4), 16);
let blue = parseInt(color.slice(4, 6), 16) let blue = parseInt(color.slice(4, 6), 16);
red = Math.round((1 - shade) * red) red = Math.round((1 - shade) * red);
green = Math.round((1 - shade) * green) green = Math.round((1 - shade) * green);
blue = Math.round((1 - shade) * blue) blue = Math.round((1 - shade) * blue);
red = red.toString(16) red = red.toString(16);
green = green.toString(16) green = green.toString(16);
blue = blue.toString(16) blue = blue.toString(16);
return `#${red}${green}${blue}` return `#${red}${green}${blue}`;
} };
const clusters = [theme] const clusters = [theme];
for (let i = 0; i <= 9; i++) { for (let i = 0; i <= 9; i++) {
clusters.push(tintColor(theme, Number((i / 10).toFixed(2)))) clusters.push(tintColor(theme, Number((i / 10).toFixed(2))));
} }
clusters.push(shadeColor(theme, 0.1)) clusters.push(shadeColor(theme, 0.1));
return clusters return clusters;
} }
} }
} };
</script> </script>
<style> <style>

View File

@ -1,6 +1,12 @@
<template> <template>
<div class="upload-container"> <div class="upload-container">
<el-button :style="{background:color,borderColor:color}" icon="el-icon-upload" size="mini" type="primary" @click=" dialogVisible=true"> <el-button
:style="{ background: color, borderColor: color }"
icon="el-icon-upload"
size="mini"
type="primary"
@click="dialogVisible = true"
>
upload upload
</el-button> </el-button>
<el-dialog :visible.sync="dialogVisible"> <el-dialog :visible.sync="dialogVisible">
@ -33,11 +39,11 @@
// import { getToken } from 'api/qiniu' // import { getToken } from 'api/qiniu'
export default { export default {
name: 'EditorSlideUpload', name: "EditorSlideUpload",
props: { props: {
color: { color: {
type: String, type: String,
default: '#1890ff' default: "#1890ff"
} }
}, },
data() { data() {
@ -45,66 +51,75 @@ export default {
dialogVisible: false, dialogVisible: false,
listObj: {}, listObj: {},
fileList: [] fileList: []
} };
}, },
methods: { methods: {
checkAllSuccess() { checkAllSuccess() {
return Object.keys(this.listObj).every(item => this.listObj[item].hasSuccess) return Object.keys(this.listObj).every(
item => this.listObj[item].hasSuccess
);
}, },
handleSubmit() { handleSubmit() {
const arr = Object.keys(this.listObj).map(v => this.listObj[v]) const arr = Object.keys(this.listObj).map(v => this.listObj[v]);
if (!this.checkAllSuccess()) { if (!this.checkAllSuccess()) {
this.$message('Please wait for all images to be uploaded successfully. If there is a network problem, please refresh the page and upload again!') this.$message(
return "Please wait for all images to be uploaded successfully. If there is a network problem, please refresh the page and upload again!"
);
return;
} }
this.$emit('successCBK', arr) this.$emit("successCBK", arr);
this.listObj = {} this.listObj = {};
this.fileList = [] this.fileList = [];
this.dialogVisible = false this.dialogVisible = false;
}, },
handleSuccess(response, file) { handleSuccess(response, file) {
const uid = file.uid const uid = file.uid;
const objKeyArr = Object.keys(this.listObj) const objKeyArr = Object.keys(this.listObj);
for (let i = 0, len = objKeyArr.length; i < len; i++) { for (let i = 0, len = objKeyArr.length; i < len; i++) {
if (this.listObj[objKeyArr[i]].uid === uid) { if (this.listObj[objKeyArr[i]].uid === uid) {
this.listObj[objKeyArr[i]].url = response.files.file this.listObj[objKeyArr[i]].url = response.files.file;
this.listObj[objKeyArr[i]].hasSuccess = true this.listObj[objKeyArr[i]].hasSuccess = true;
return return;
} }
} }
}, },
handleRemove(file) { handleRemove(file) {
const uid = file.uid const uid = file.uid;
const objKeyArr = Object.keys(this.listObj) const objKeyArr = Object.keys(this.listObj);
for (let i = 0, len = objKeyArr.length; i < len; i++) { for (let i = 0, len = objKeyArr.length; i < len; i++) {
if (this.listObj[objKeyArr[i]].uid === uid) { if (this.listObj[objKeyArr[i]].uid === uid) {
delete this.listObj[objKeyArr[i]] delete this.listObj[objKeyArr[i]];
return return;
} }
} }
}, },
beforeUpload(file) { beforeUpload(file) {
const _self = this const _self = this;
const _URL = window.URL || window.webkitURL const _URL = window.URL || window.webkitURL;
const fileName = file.uid const fileName = file.uid;
this.listObj[fileName] = {} this.listObj[fileName] = {};
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const img = new Image() const img = new Image();
img.src = _URL.createObjectURL(file) img.src = _URL.createObjectURL(file);
img.onload = function() { img.onload = function() {
_self.listObj[fileName] = { hasSuccess: false, uid: file.uid, width: this.width, height: this.height } _self.listObj[fileName] = {
} hasSuccess: false,
resolve(true) uid: file.uid,
}) width: this.width,
height: this.height
};
};
resolve(true);
});
} }
} }
} };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.editor-slide-upload { .editor-slide-upload {
margin-bottom: 20px; margin-bottom: 20px;
/deep/ .el-upload--picture-card { ::v-deep .el-upload--picture-card {
width: 100%; width: 100%;
} }
} }

View File

@ -1,30 +1,30 @@
let callbacks = [] let callbacks = [];
function loadedTinymce() { function loadedTinymce() {
// to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2144 // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2144
// check is successfully downloaded script // check is successfully downloaded script
return window.tinymce return window.tinymce;
} }
const dynamicLoadScript = (src, callback) => { const dynamicLoadScript = (src, callback) => {
const existingScript = document.getElementById(src) const existingScript = document.getElementById(src);
const cb = callback || function() {} const cb = callback || function() {};
if (!existingScript) { if (!existingScript) {
const script = document.createElement('script') const script = document.createElement("script");
script.src = src // src url for the third-party library being loaded. script.src = src; // src url for the third-party library being loaded.
script.id = src script.id = src;
document.body.appendChild(script) document.body.appendChild(script);
callbacks.push(cb) callbacks.push(cb);
const onEnd = 'onload' in script ? stdOnEnd : ieOnEnd const onEnd = "onload" in script ? stdOnEnd : ieOnEnd;
onEnd(script) onEnd(script);
} }
if (existingScript && cb) { if (existingScript && cb) {
if (loadedTinymce()) { if (loadedTinymce()) {
cb(null, existingScript) cb(null, existingScript);
} else { } else {
callbacks.push(cb) callbacks.push(cb);
} }
} }
@ -32,28 +32,29 @@ const dynamicLoadScript = (src, callback) => {
script.onload = function() { script.onload = function() {
// this.onload = null here is necessary // this.onload = null here is necessary
// because even IE9 works not like others // because even IE9 works not like others
this.onerror = this.onload = null this.onerror = this.onload = null;
for (const cb of callbacks) { for (const cb of callbacks) {
cb(null, script) cb(null, script);
}
callbacks = null
} }
callbacks = null;
};
script.onerror = function() { script.onerror = function() {
this.onerror = this.onload = null this.onerror = this.onload = null;
cb(new Error('Failed to load ' + src), script) cb(new Error("Failed to load " + src), script);
} };
} }
function ieOnEnd(script) { function ieOnEnd(script) {
script.onreadystatechange = function() { script.onreadystatechange = function() {
if (this.readyState !== 'complete' && this.readyState !== 'loaded') return if (this.readyState !== "complete" && this.readyState !== "loaded")
this.onreadystatechange = null return;
this.onreadystatechange = null;
for (const cb of callbacks) { for (const cb of callbacks) {
cb(null, script) // there is no way to catch loading errors in IE8 cb(null, script); // there is no way to catch loading errors in IE8
} }
callbacks = null callbacks = null;
};
} }
} };
}
export default dynamicLoadScript export default dynamicLoadScript;

View File

@ -1,8 +1,16 @@
<template> <template>
<div :class="{fullscreen:fullscreen}" class="tinymce-container" :style="{width:containerWidth}"> <div
:class="{ fullscreen: fullscreen }"
class="tinymce-container"
:style="{ width: containerWidth }"
>
<textarea :id="tinymceId" class="tinymce-textarea" /> <textarea :id="tinymceId" class="tinymce-textarea" />
<div class="editor-custom-btn-container"> <div class="editor-custom-btn-container">
<editorImage color="#1890ff" class="editor-upload-btn" @successCBK="imageSuccessCBK" /> <editorImage
color="#1890ff"
class="editor-upload-btn"
@successCBK="imageSuccessCBK"
/>
</div> </div>
</div> </div>
</template> </template>
@ -12,38 +20,43 @@
* docs: * docs:
* https://panjiachen.github.io/vue-element-admin-site/feature/component/rich-editor.html#tinymce * https://panjiachen.github.io/vue-element-admin-site/feature/component/rich-editor.html#tinymce
*/ */
import editorImage from './components/EditorImage' import editorImage from "./components/EditorImage";
import plugins from './plugins' import plugins from "./plugins";
import toolbar from './toolbar' import toolbar from "./toolbar";
import load from './dynamicLoadScript' import load from "./dynamicLoadScript";
// why use this cdn, detail see https://github.com/PanJiaChen/tinymce-all-in-one // why use this cdn, detail see https://github.com/PanJiaChen/tinymce-all-in-one
const tinymceCDN = 'https://cdn.jsdelivr.net/npm/tinymce-all-in-one@4.9.3/tinymce.min.js' const tinymceCDN =
"https://cdn.jsdelivr.net/npm/tinymce-all-in-one@4.9.3/tinymce.min.js";
export default { export default {
name: 'Tinymce', name: "Tinymce",
components: { editorImage }, components: { editorImage },
props: { props: {
id: { id: {
type: String, type: String,
default: function() { default: function() {
return 'vue-tinymce-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '') return (
"vue-tinymce-" +
+new Date() +
((Math.random() * 1000).toFixed(0) + "")
);
} }
}, },
value: { value: {
type: String, type: String,
default: '' default: ""
}, },
toolbar: { toolbar: {
type: Array, type: Array,
required: false, required: false,
default() { default() {
return [] return [];
} }
}, },
menubar: { menubar: {
type: String, type: String,
default: 'file edit insert view format table' default: "file edit insert view format table"
}, },
height: { height: {
type: [Number, String], type: [Number, String],
@ -53,7 +66,7 @@ export default {
width: { width: {
type: [Number, String], type: [Number, String],
required: false, required: false,
default: 'auto' default: "auto"
} }
}, },
data() { data() {
@ -63,90 +76,92 @@ export default {
tinymceId: this.id, tinymceId: this.id,
fullscreen: false, fullscreen: false,
languageTypeList: { languageTypeList: {
'en': 'en', en: "en",
'zh': 'zh_CN', zh: "zh_CN",
'es': 'es_MX', es: "es_MX",
'ja': 'ja' ja: "ja"
}
} }
};
}, },
computed: { computed: {
containerWidth() { containerWidth() {
const width = this.width const width = this.width;
if (/^[\d]+(\.[\d]+)?$/.test(width)) { // matches `100`, `'100'` if (/^[\d]+(\.[\d]+)?$/.test(width)) {
return `${width}px` // matches `100`, `'100'`
return `${width}px`;
} }
return width return width;
} }
}, },
watch: { watch: {
value(val) { value(val) {
if (!this.hasChange && this.hasInit) { if (!this.hasChange && this.hasInit) {
this.$nextTick(() => this.$nextTick(() =>
window.tinymce.get(this.tinymceId).setContent(val || '')) window.tinymce.get(this.tinymceId).setContent(val || "")
);
} }
} }
}, },
mounted() { mounted() {
this.init() this.init();
}, },
activated() { activated() {
if (window.tinymce) { if (window.tinymce) {
this.initTinymce() this.initTinymce();
} }
}, },
deactivated() { deactivated() {
this.destroyTinymce() this.destroyTinymce();
}, },
destroyed() { destroyed() {
this.destroyTinymce() this.destroyTinymce();
}, },
methods: { methods: {
init() { init() {
// dynamic load tinymce from cdn // dynamic load tinymce from cdn
load(tinymceCDN, (err) => { load(tinymceCDN, err => {
if (err) { if (err) {
this.$message.error(err.message) this.$message.error(err.message);
return return;
} }
this.initTinymce() this.initTinymce();
}) });
}, },
initTinymce() { initTinymce() {
const _this = this const _this = this;
window.tinymce.init({ window.tinymce.init({
selector: `#${this.tinymceId}`, selector: `#${this.tinymceId}`,
language: this.languageTypeList['en'], language: this.languageTypeList["en"],
height: this.height, height: this.height,
body_class: 'panel-body ', body_class: "panel-body ",
object_resizing: false, object_resizing: false,
toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar, toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar,
menubar: this.menubar, menubar: this.menubar,
plugins: plugins, plugins: plugins,
end_container_on_empty_block: true, end_container_on_empty_block: true,
powerpaste_word_import: 'clean', powerpaste_word_import: "clean",
code_dialog_height: 450, code_dialog_height: 450,
code_dialog_width: 1000, code_dialog_width: 1000,
advlist_bullet_styles: 'square', advlist_bullet_styles: "square",
advlist_number_styles: 'default', advlist_number_styles: "default",
imagetools_cors_hosts: ['www.tinymce.com', 'codepen.io'], imagetools_cors_hosts: ["www.tinymce.com", "codepen.io"],
default_link_target: '_blank', default_link_target: "_blank",
link_title: false, link_title: false,
nonbreaking_force_tab: true, // inserting nonbreaking space &nbsp; need Nonbreaking Space Plugin nonbreaking_force_tab: true, // inserting nonbreaking space &nbsp; need Nonbreaking Space Plugin
init_instance_callback: editor => { init_instance_callback: editor => {
if (_this.value) { if (_this.value) {
editor.setContent(_this.value) editor.setContent(_this.value);
} }
_this.hasInit = true _this.hasInit = true;
editor.on('NodeChange Change KeyUp SetContent', () => { editor.on("NodeChange Change KeyUp SetContent", () => {
this.hasChange = true this.hasChange = true;
this.$emit('input', editor.getContent()) this.$emit("input", editor.getContent());
}) });
}, },
setup(editor) { setup(editor) {
editor.on('FullscreenStateChanged', (e) => { editor.on("FullscreenStateChanged", e => {
_this.fullscreen = e.state _this.fullscreen = e.state;
}) });
} }
// //
// images_dataimg_filter(img) { // images_dataimg_filter(img) {
@ -181,32 +196,34 @@ export default {
// console.log(err); // console.log(err);
// }); // });
// }, // },
}) });
}, },
destroyTinymce() { destroyTinymce() {
const tinymce = window.tinymce.get(this.tinymceId) const tinymce = window.tinymce.get(this.tinymceId);
if (this.fullscreen) { if (this.fullscreen) {
tinymce.execCommand('mceFullScreen') tinymce.execCommand("mceFullScreen");
} }
if (tinymce) { if (tinymce) {
tinymce.destroy() tinymce.destroy();
} }
}, },
setContent(value) { setContent(value) {
window.tinymce.get(this.tinymceId).setContent(value) window.tinymce.get(this.tinymceId).setContent(value);
}, },
getContent() { getContent() {
window.tinymce.get(this.tinymceId).getContent() window.tinymce.get(this.tinymceId).getContent();
}, },
imageSuccessCBK(arr) { imageSuccessCBK(arr) {
const _this = this const _this = this;
arr.forEach(v => { arr.forEach(v => {
window.tinymce.get(_this.tinymceId).insertContent(`<img class="wscnph" src="${v.url}" >`) window.tinymce
}) .get(_this.tinymceId)
.insertContent(`<img class="wscnph" src="${v.url}" >`);
});
} }
} }
} };
</script> </script>
<style scoped> <style scoped>
@ -214,7 +231,7 @@ export default {
position: relative; position: relative;
line-height: normal; line-height: normal;
} }
.tinymce-container>>>.mce-fullscreen { .tinymce-container >>> .mce-fullscreen {
z-index: 10000; z-index: 10000;
} }
.tinymce-textarea { .tinymce-textarea {

View File

@ -2,6 +2,8 @@
// Detail plugins list see https://www.tinymce.com/docs/plugins/ // Detail plugins list see https://www.tinymce.com/docs/plugins/
// Custom builds see https://www.tinymce.com/download/custom-builds/ // Custom builds see https://www.tinymce.com/download/custom-builds/
const plugins = ['advlist anchor autolink autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount'] const plugins = [
"advlist anchor autolink autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount"
];
export default plugins export default plugins;

View File

@ -1,6 +1,9 @@
// Here is a list of the toolbar // Here is a list of the toolbar
// Detail list see https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols // Detail list see https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols
const toolbar = ['searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen'] const toolbar = [
"searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample",
"hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen"
];
export default toolbar export default toolbar;

View File

@ -10,13 +10,11 @@
action="https://httpbin.org/post" action="https://httpbin.org/post"
> >
<i class="el-icon-upload" /> <i class="el-icon-upload" />
<div class="el-upload__text"> <div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
将文件拖到此处<em>点击上传</em>
</div>
</el-upload> </el-upload>
<div class="image-preview"> <div class="image-preview">
<div v-show="imageUrl.length>1" class="image-preview-wrapper"> <div v-show="imageUrl.length > 1" class="image-preview-wrapper">
<img :src="imageUrl+'?imageView2/1/w/200/h/200'"> <img :src="imageUrl + '?imageView2/1/w/200/h/200'" />
<div class="image-preview-action"> <div class="image-preview-action">
<i class="el-icon-delete" @click="rmImage" /> <i class="el-icon-delete" @click="rmImage" />
</div> </div>
@ -26,60 +24,62 @@
</template> </template>
<script> <script>
import { getToken } from '@/api/qiniu' import { getToken } from "@/api/qiniu";
export default { export default {
name: 'SingleImageUpload', name: "SingleImageUpload",
props: { props: {
value: { value: {
type: String, type: String,
default: '' default: ""
} }
}, },
data() { data() {
return { return {
tempUrl: '', tempUrl: "",
dataObj: { token: '', key: '' } dataObj: { token: "", key: "" }
} };
}, },
computed: { computed: {
imageUrl() { imageUrl() {
return this.value return this.value;
} }
}, },
methods: { methods: {
rmImage() { rmImage() {
this.emitInput('') this.emitInput("");
}, },
emitInput(val) { emitInput(val) {
this.$emit('input', val) this.$emit("input", val);
}, },
handleImageSuccess() { handleImageSuccess() {
this.emitInput(this.tempUrl) this.emitInput(this.tempUrl);
}, },
beforeUpload() { beforeUpload() {
const _self = this const _self = this;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
getToken().then(response => { getToken()
const key = response.data.qiniu_key .then(response => {
const token = response.data.qiniu_token const key = response.data.qiniu_key;
_self._data.dataObj.token = token const token = response.data.qiniu_token;
_self._data.dataObj.key = key _self._data.dataObj.token = token;
this.tempUrl = response.data.qiniu_url _self._data.dataObj.key = key;
resolve(true) this.tempUrl = response.data.qiniu_url;
}).catch(err => { resolve(true);
console.log(err)
reject(false)
})
}) })
.catch(err => {
console.log(err);
reject(false);
});
});
} }
} }
} };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "~@/styles/mixin.scss"; @import "~@/styles/mixin.scss";
.upload-container { .upload-container {
width: 100%; width: 100%;
position: relative; position: relative;
@include clearfix; @include clearfix;
@ -114,8 +114,8 @@ export default {
color: #fff; color: #fff;
opacity: 0; opacity: 0;
font-size: 20px; font-size: 20px;
background-color: rgba(0, 0, 0, .5); background-color: rgba(0, 0, 0, 0.5);
transition: opacity .3s; transition: opacity 0.3s;
cursor: pointer; cursor: pointer;
text-align: center; text-align: center;
line-height: 200px; line-height: 200px;
@ -129,6 +129,5 @@ export default {
} }
} }
} }
} }
</style> </style>

View File

@ -10,13 +10,11 @@
action="https://httpbin.org/post" action="https://httpbin.org/post"
> >
<i class="el-icon-upload" /> <i class="el-icon-upload" />
<div class="el-upload__text"> <div class="el-upload__text">Drag或<em>点击上传</em></div>
Drag或<em>点击上传</em>
</div>
</el-upload> </el-upload>
<div v-show="imageUrl.length>0" class="image-preview"> <div v-show="imageUrl.length > 0" class="image-preview">
<div v-show="imageUrl.length>1" class="image-preview-wrapper"> <div v-show="imageUrl.length > 1" class="image-preview-wrapper">
<img :src="imageUrl"> <img :src="imageUrl" />
<div class="image-preview-action"> <div class="image-preview-action">
<i class="el-icon-delete" @click="rmImage" /> <i class="el-icon-delete" @click="rmImage" />
</div> </div>
@ -26,54 +24,56 @@
</template> </template>
<script> <script>
import { getToken } from '@/api/qiniu' import { getToken } from "@/api/qiniu";
export default { export default {
name: 'SingleImageUpload2', name: "SingleImageUpload2",
props: { props: {
value: { value: {
type: String, type: String,
default: '' default: ""
} }
}, },
data() { data() {
return { return {
tempUrl: '', tempUrl: "",
dataObj: { token: '', key: '' } dataObj: { token: "", key: "" }
} };
}, },
computed: { computed: {
imageUrl() { imageUrl() {
return this.value return this.value;
} }
}, },
methods: { methods: {
rmImage() { rmImage() {
this.emitInput('') this.emitInput("");
}, },
emitInput(val) { emitInput(val) {
this.$emit('input', val) this.$emit("input", val);
}, },
handleImageSuccess() { handleImageSuccess() {
this.emitInput(this.tempUrl) this.emitInput(this.tempUrl);
}, },
beforeUpload() { beforeUpload() {
const _self = this const _self = this;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
getToken().then(response => { getToken()
const key = response.data.qiniu_key .then(response => {
const token = response.data.qiniu_token const key = response.data.qiniu_key;
_self._data.dataObj.token = token const token = response.data.qiniu_token;
_self._data.dataObj.key = key _self._data.dataObj.token = token;
this.tempUrl = response.data.qiniu_url _self._data.dataObj.key = key;
resolve(true) this.tempUrl = response.data.qiniu_url;
}).catch(() => { resolve(true);
reject(false)
})
}) })
.catch(() => {
reject(false);
});
});
} }
} }
} };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -111,8 +111,8 @@ export default {
color: #fff; color: #fff;
opacity: 0; opacity: 0;
font-size: 20px; font-size: 20px;
background-color: rgba(0, 0, 0, .5); background-color: rgba(0, 0, 0, 0.5);
transition: opacity .3s; transition: opacity 0.3s;
cursor: pointer; cursor: pointer;
text-align: center; text-align: center;
line-height: 200px; line-height: 200px;

View File

@ -10,21 +10,19 @@
action="https://httpbin.org/post" action="https://httpbin.org/post"
> >
<i class="el-icon-upload" /> <i class="el-icon-upload" />
<div class="el-upload__text"> <div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
将文件拖到此处<em>点击上传</em>
</div>
</el-upload> </el-upload>
<div class="image-preview image-app-preview"> <div class="image-preview image-app-preview">
<div v-show="imageUrl.length>1" class="image-preview-wrapper"> <div v-show="imageUrl.length > 1" class="image-preview-wrapper">
<img :src="imageUrl"> <img :src="imageUrl" />
<div class="image-preview-action"> <div class="image-preview-action">
<i class="el-icon-delete" @click="rmImage" /> <i class="el-icon-delete" @click="rmImage" />
</div> </div>
</div> </div>
</div> </div>
<div class="image-preview"> <div class="image-preview">
<div v-show="imageUrl.length>1" class="image-preview-wrapper"> <div v-show="imageUrl.length > 1" class="image-preview-wrapper">
<img :src="imageUrl"> <img :src="imageUrl" />
<div class="image-preview-action"> <div class="image-preview-action">
<i class="el-icon-delete" @click="rmImage" /> <i class="el-icon-delete" @click="rmImage" />
</div> </div>
@ -34,55 +32,57 @@
</template> </template>
<script> <script>
import { getToken } from '@/api/qiniu' import { getToken } from "@/api/qiniu";
export default { export default {
name: 'SingleImageUpload3', name: "SingleImageUpload3",
props: { props: {
value: { value: {
type: String, type: String,
default: '' default: ""
} }
}, },
data() { data() {
return { return {
tempUrl: '', tempUrl: "",
dataObj: { token: '', key: '' } dataObj: { token: "", key: "" }
} };
}, },
computed: { computed: {
imageUrl() { imageUrl() {
return this.value return this.value;
} }
}, },
methods: { methods: {
rmImage() { rmImage() {
this.emitInput('') this.emitInput("");
}, },
emitInput(val) { emitInput(val) {
this.$emit('input', val) this.$emit("input", val);
}, },
handleImageSuccess(file) { handleImageSuccess(file) {
this.emitInput(file.files.file) this.emitInput(file.files.file);
}, },
beforeUpload() { beforeUpload() {
const _self = this const _self = this;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
getToken().then(response => { getToken()
const key = response.data.qiniu_key .then(response => {
const token = response.data.qiniu_token const key = response.data.qiniu_key;
_self._data.dataObj.token = token const token = response.data.qiniu_token;
_self._data.dataObj.key = key _self._data.dataObj.token = token;
this.tempUrl = response.data.qiniu_url _self._data.dataObj.key = key;
resolve(true) this.tempUrl = response.data.qiniu_url;
}).catch(err => { resolve(true);
console.log(err)
reject(false)
})
}) })
.catch(err => {
console.log(err);
reject(false);
});
});
} }
} }
} };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -122,8 +122,8 @@ export default {
color: #fff; color: #fff;
opacity: 0; opacity: 0;
font-size: 20px; font-size: 20px;
background-color: rgba(0, 0, 0, .5); background-color: rgba(0, 0, 0, 0.5);
transition: opacity .3s; transition: opacity 0.3s;
cursor: pointer; cursor: pointer;
text-align: center; text-align: center;
line-height: 200px; line-height: 200px;

View File

@ -1,9 +1,26 @@
<template> <template>
<div> <div>
<input ref="excel-upload-input" class="excel-upload-input" type="file" accept=".xlsx, .xls" @change="handleClick"> <input
<div class="drop" @drop="handleDrop" @dragover="handleDragover" @dragenter="handleDragover"> ref="excel-upload-input"
class="excel-upload-input"
type="file"
accept=".xlsx, .xls"
@change="handleClick"
/>
<div
class="drop"
@drop="handleDrop"
@dragover="handleDragover"
@dragenter="handleDragover"
>
Drop excel file here or Drop excel file here or
<el-button :loading="loading" style="margin-left:16px;" size="mini" type="primary" @click="handleUpload"> <el-button
:loading="loading"
style="margin-left:16px;"
size="mini"
type="primary"
@click="handleUpload"
>
Browse Browse
</el-button> </el-button>
</div> </div>
@ -11,12 +28,12 @@
</template> </template>
<script> <script>
import XLSX from 'xlsx' import XLSX from "xlsx";
export default { export default {
props: { props: {
beforeUpload: Function, // eslint-disable-line beforeUpload: Function, // eslint-disable-line
onSuccess: Function// eslint-disable-line onSuccess: Function // eslint-disable-line
}, },
data() { data() {
return { return {
@ -25,105 +42,108 @@ export default {
header: null, header: null,
results: null results: null
} }
} };
}, },
methods: { methods: {
generateData({ header, results }) { generateData({ header, results }) {
this.excelData.header = header this.excelData.header = header;
this.excelData.results = results this.excelData.results = results;
this.onSuccess && this.onSuccess(this.excelData) this.onSuccess && this.onSuccess(this.excelData);
}, },
handleDrop(e) { handleDrop(e) {
e.stopPropagation() e.stopPropagation();
e.preventDefault() e.preventDefault();
if (this.loading) return if (this.loading) return;
const files = e.dataTransfer.files const files = e.dataTransfer.files;
if (files.length !== 1) { if (files.length !== 1) {
this.$message.error('Only support uploading one file!') this.$message.error("Only support uploading one file!");
return return;
} }
const rawFile = files[0] // only use files[0] const rawFile = files[0]; // only use files[0]
if (!this.isExcel(rawFile)) { if (!this.isExcel(rawFile)) {
this.$message.error('Only supports upload .xlsx, .xls, .csv suffix files') this.$message.error(
return false "Only supports upload .xlsx, .xls, .csv suffix files"
);
return false;
} }
this.upload(rawFile) this.upload(rawFile);
e.stopPropagation() e.stopPropagation();
e.preventDefault() e.preventDefault();
}, },
handleDragover(e) { handleDragover(e) {
e.stopPropagation() e.stopPropagation();
e.preventDefault() e.preventDefault();
e.dataTransfer.dropEffect = 'copy' e.dataTransfer.dropEffect = "copy";
}, },
handleUpload() { handleUpload() {
this.$refs['excel-upload-input'].click() this.$refs["excel-upload-input"].click();
}, },
handleClick(e) { handleClick(e) {
const files = e.target.files const files = e.target.files;
const rawFile = files[0] // only use files[0] const rawFile = files[0]; // only use files[0]
if (!rawFile) return if (!rawFile) return;
this.upload(rawFile) this.upload(rawFile);
}, },
upload(rawFile) { upload(rawFile) {
this.$refs['excel-upload-input'].value = null // fix can't select the same excel this.$refs["excel-upload-input"].value = null; // fix can't select the same excel
if (!this.beforeUpload) { if (!this.beforeUpload) {
this.readerData(rawFile) this.readerData(rawFile);
return return;
} }
const before = this.beforeUpload(rawFile) const before = this.beforeUpload(rawFile);
if (before) { if (before) {
this.readerData(rawFile) this.readerData(rawFile);
} }
}, },
readerData(rawFile) { readerData(rawFile) {
this.loading = true this.loading = true;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const reader = new FileReader() const reader = new FileReader();
reader.onload = e => { reader.onload = e => {
const data = e.target.result const data = e.target.result;
const workbook = XLSX.read(data, { type: 'array' }) const workbook = XLSX.read(data, { type: "array" });
const firstSheetName = workbook.SheetNames[0] const firstSheetName = workbook.SheetNames[0];
const worksheet = workbook.Sheets[firstSheetName] const worksheet = workbook.Sheets[firstSheetName];
const header = this.getHeaderRow(worksheet) const header = this.getHeaderRow(worksheet);
const results = XLSX.utils.sheet_to_json(worksheet) const results = XLSX.utils.sheet_to_json(worksheet);
this.generateData({ header, results }) this.generateData({ header, results });
this.loading = false this.loading = false;
resolve() resolve();
} };
reader.readAsArrayBuffer(rawFile) reader.readAsArrayBuffer(rawFile);
}) });
}, },
getHeaderRow(sheet) { getHeaderRow(sheet) {
const headers = [] const headers = [];
const range = XLSX.utils.decode_range(sheet['!ref']) const range = XLSX.utils.decode_range(sheet["!ref"]);
let C let C;
const R = range.s.r const R = range.s.r;
/* start in the first row */ /* start in the first row */
for (C = range.s.c; C <= range.e.c; ++C) { /* walk every column in the range */ for (C = range.s.c; C <= range.e.c; ++C) {
const cell = sheet[XLSX.utils.encode_cell({ c: C, r: R })] /* walk every column in the range */
const cell = sheet[XLSX.utils.encode_cell({ c: C, r: R })];
/* find the cell in the first row */ /* find the cell in the first row */
let hdr = 'UNKNOWN ' + C // <-- replace with your desired default let hdr = "UNKNOWN " + C; // <-- replace with your desired default
if (cell && cell.t) hdr = XLSX.utils.format_cell(cell) if (cell && cell.t) hdr = XLSX.utils.format_cell(cell);
headers.push(hdr) headers.push(hdr);
} }
return headers return headers;
}, },
isExcel(file) { isExcel(file) {
return /\.(xlsx|xls|csv)$/.test(file.name) return /\.(xlsx|xls|csv)$/.test(file.name);
} }
} }
} };
</script> </script>
<style scoped> <style scoped>
.excel-upload-input{ .excel-upload-input {
display: none; display: none;
z-index: -9999; z-index: -9999;
} }
.drop{ .drop {
border: 2px dashed #bbb; border: 2px dashed #bbb;
width: 600px; width: 600px;
height: 160px; height: 160px;

View File

@ -1,49 +1,57 @@
// Inspired by https://github.com/Inndy/vue-clipboard2 // Inspired by https://github.com/Inndy/vue-clipboard2
const Clipboard = require('clipboard') const Clipboard = require("clipboard");
if (!Clipboard) { if (!Clipboard) {
throw new Error('you should npm install `clipboard` --save at first ') throw new Error("you should npm install `clipboard` --save at first ");
} }
export default { export default {
bind(el, binding) { bind(el, binding) {
if (binding.arg === 'success') { if (binding.arg === "success") {
el._v_clipboard_success = binding.value el._v_clipboard_success = binding.value;
} else if (binding.arg === 'error') { } else if (binding.arg === "error") {
el._v_clipboard_error = binding.value el._v_clipboard_error = binding.value;
} else { } else {
const clipboard = new Clipboard(el, { const clipboard = new Clipboard(el, {
text() { return binding.value }, text() {
action() { return binding.arg === 'cut' ? 'cut' : 'copy' } return binding.value;
}) },
clipboard.on('success', e => { action() {
const callback = el._v_clipboard_success return binding.arg === "cut" ? "cut" : "copy";
callback && callback(e) // eslint-disable-line }
}) });
clipboard.on('error', e => { clipboard.on("success", e => {
const callback = el._v_clipboard_error const callback = el._v_clipboard_success;
callback && callback(e) // eslint-disable-line callback && callback(e); // eslint-disable-line
}) });
el._v_clipboard = clipboard clipboard.on("error", e => {
const callback = el._v_clipboard_error;
callback && callback(e); // eslint-disable-line
});
el._v_clipboard = clipboard;
} }
}, },
update(el, binding) { update(el, binding) {
if (binding.arg === 'success') { if (binding.arg === "success") {
el._v_clipboard_success = binding.value el._v_clipboard_success = binding.value;
} else if (binding.arg === 'error') { } else if (binding.arg === "error") {
el._v_clipboard_error = binding.value el._v_clipboard_error = binding.value;
} else { } else {
el._v_clipboard.text = function() { return binding.value } el._v_clipboard.text = function() {
el._v_clipboard.action = function() { return binding.arg === 'cut' ? 'cut' : 'copy' } return binding.value;
};
el._v_clipboard.action = function() {
return binding.arg === "cut" ? "cut" : "copy";
};
} }
}, },
unbind(el, binding) { unbind(el, binding) {
if (binding.arg === 'success') { if (binding.arg === "success") {
delete el._v_clipboard_success delete el._v_clipboard_success;
} else if (binding.arg === 'error') { } else if (binding.arg === "error") {
delete el._v_clipboard_error delete el._v_clipboard_error;
} else { } else {
el._v_clipboard.destroy() el._v_clipboard.destroy();
delete el._v_clipboard delete el._v_clipboard;
} }
} }
} };

View File

@ -1,13 +1,13 @@
import Clipboard from './clipboard' import Clipboard from "./clipboard";
const install = function(Vue) { const install = function(Vue) {
Vue.directive('Clipboard', Clipboard) Vue.directive("Clipboard", Clipboard);
} };
if (window.Vue) { if (window.Vue) {
window.clipboard = Clipboard window.clipboard = Clipboard;
Vue.use(install); // eslint-disable-line Vue.use(install); // eslint-disable-line
} }
Clipboard.install = install Clipboard.install = install;
export default Clipboard export default Clipboard;

View File

@ -1,77 +1,77 @@
export default { export default {
bind(el, binding, vnode) { bind(el, binding, vnode) {
const dialogHeaderEl = el.querySelector('.el-dialog__header') const dialogHeaderEl = el.querySelector(".el-dialog__header");
const dragDom = el.querySelector('.el-dialog') const dragDom = el.querySelector(".el-dialog");
dialogHeaderEl.style.cssText += ';cursor:move;' dialogHeaderEl.style.cssText += ";cursor:move;";
dragDom.style.cssText += ';top:0px;' dragDom.style.cssText += ";top:0px;";
// 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null); // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
const getStyle = (function() { const getStyle = (function() {
if (window.document.currentStyle) { if (window.document.currentStyle) {
return (dom, attr) => dom.currentStyle[attr] return (dom, attr) => dom.currentStyle[attr];
} else { } else {
return (dom, attr) => getComputedStyle(dom, false)[attr] return (dom, attr) => getComputedStyle(dom, false)[attr];
} }
})() })();
dialogHeaderEl.onmousedown = (e) => { dialogHeaderEl.onmousedown = e => {
// 鼠标按下,计算当前元素距离可视区的距离 // 鼠标按下,计算当前元素距离可视区的距离
const disX = e.clientX - dialogHeaderEl.offsetLeft const disX = e.clientX - dialogHeaderEl.offsetLeft;
const disY = e.clientY - dialogHeaderEl.offsetTop const disY = e.clientY - dialogHeaderEl.offsetTop;
const dragDomWidth = dragDom.offsetWidth const dragDomWidth = dragDom.offsetWidth;
const dragDomHeight = dragDom.offsetHeight const dragDomHeight = dragDom.offsetHeight;
const screenWidth = document.body.clientWidth const screenWidth = document.body.clientWidth;
const screenHeight = document.body.clientHeight const screenHeight = document.body.clientHeight;
const minDragDomLeft = dragDom.offsetLeft const minDragDomLeft = dragDom.offsetLeft;
const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth;
const minDragDomTop = dragDom.offsetTop const minDragDomTop = dragDom.offsetTop;
const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomHeight const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomHeight;
// 获取到的值带px 正则匹配替换 // 获取到的值带px 正则匹配替换
let styL = getStyle(dragDom, 'left') let styL = getStyle(dragDom, "left");
let styT = getStyle(dragDom, 'top') let styT = getStyle(dragDom, "top");
if (styL.includes('%')) { if (styL.includes("%")) {
styL = +document.body.clientWidth * (+styL.replace(/\%/g, '') / 100) styL = +document.body.clientWidth * (+styL.replace(/\%/g, "") / 100);
styT = +document.body.clientHeight * (+styT.replace(/\%/g, '') / 100) styT = +document.body.clientHeight * (+styT.replace(/\%/g, "") / 100);
} else { } else {
styL = +styL.replace(/\px/g, '') styL = +styL.replace(/\px/g, "");
styT = +styT.replace(/\px/g, '') styT = +styT.replace(/\px/g, "");
} }
document.onmousemove = function(e) { document.onmousemove = function(e) {
// 通过事件委托,计算移动的距离 // 通过事件委托,计算移动的距离
let left = e.clientX - disX let left = e.clientX - disX;
let top = e.clientY - disY let top = e.clientY - disY;
// 边界处理 // 边界处理
if (-(left) > minDragDomLeft) { if (-left > minDragDomLeft) {
left = -minDragDomLeft left = -minDragDomLeft;
} else if (left > maxDragDomLeft) { } else if (left > maxDragDomLeft) {
left = maxDragDomLeft left = maxDragDomLeft;
} }
if (-(top) > minDragDomTop) { if (-top > minDragDomTop) {
top = -minDragDomTop top = -minDragDomTop;
} else if (top > maxDragDomTop) { } else if (top > maxDragDomTop) {
top = maxDragDomTop top = maxDragDomTop;
} }
// 移动当前元素 // 移动当前元素
dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;` dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`;
// emit onDrag event // emit onDrag event
vnode.child.$emit('dragDialog') vnode.child.$emit("dragDialog");
} };
document.onmouseup = function(e) { document.onmouseup = function(e) {
document.onmousemove = null document.onmousemove = null;
document.onmouseup = null document.onmouseup = null;
};
};
} }
} };
}
}

View File

@ -1,13 +1,13 @@
import drag from './drag' import drag from "./drag";
const install = function(Vue) { const install = function(Vue) {
Vue.directive('el-drag-dialog', drag) Vue.directive("el-drag-dialog", drag);
} };
if (window.Vue) { if (window.Vue) {
window['el-drag-dialog'] = drag window["el-drag-dialog"] = drag;
Vue.use(install); // eslint-disable-line Vue.use(install); // eslint-disable-line
} }
drag.install = install drag.install = install;
export default drag export default drag;

View File

@ -1,4 +1,7 @@
import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event' import {
addResizeListener,
removeResizeListener
} from "element-ui/src/utils/resize-event";
/** /**
* How to use * How to use
@ -8,34 +11,35 @@ import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/re
*/ */
const doResize = (el, binding, vnode) => { const doResize = (el, binding, vnode) => {
const { componentInstance: $table } = vnode const { componentInstance: $table } = vnode;
const { value } = binding const { value } = binding;
if (!$table.height) { if (!$table.height) {
throw new Error(`el-$table must set the height. Such as height='100px'`) throw new Error(`el-$table must set the height. Such as height='100px'`);
} }
const bottomOffset = (value && value.bottomOffset) || 30 const bottomOffset = (value && value.bottomOffset) || 30;
if (!$table) return if (!$table) return;
const height = window.innerHeight - el.getBoundingClientRect().top - bottomOffset const height =
$table.layout.setHeight(height) window.innerHeight - el.getBoundingClientRect().top - bottomOffset;
$table.doLayout() $table.layout.setHeight(height);
} $table.doLayout();
};
export default { export default {
bind(el, binding, vnode) { bind(el, binding, vnode) {
el.resizeListener = () => { el.resizeListener = () => {
doResize(el, binding, vnode) doResize(el, binding, vnode);
} };
// parameter 1 is must be "Element" type // parameter 1 is must be "Element" type
addResizeListener(window.document.body, el.resizeListener) addResizeListener(window.document.body, el.resizeListener);
}, },
inserted(el, binding, vnode) { inserted(el, binding, vnode) {
doResize(el, binding, vnode) doResize(el, binding, vnode);
}, },
unbind(el) { unbind(el) {
removeResizeListener(window.document.body, el.resizeListener) removeResizeListener(window.document.body, el.resizeListener);
} }
} };

View File

@ -1,13 +1,13 @@
import adaptive from './adaptive' import adaptive from "./adaptive";
const install = function(Vue) { const install = function(Vue) {
Vue.directive('el-height-adaptive-table', adaptive) Vue.directive("el-height-adaptive-table", adaptive);
} };
if (window.Vue) { if (window.Vue) {
window['el-height-adaptive-table'] = adaptive window["el-height-adaptive-table"] = adaptive;
Vue.use(install); // eslint-disable-line Vue.use(install); // eslint-disable-line
} }
adaptive.install = install adaptive.install = install;
export default adaptive export default adaptive;

View File

@ -1,13 +1,13 @@
import permission from './permission' import permission from "./permission";
const install = function(Vue) { const install = function(Vue) {
Vue.directive('permission', permission) Vue.directive("permission", permission);
} };
if (window.Vue) { if (window.Vue) {
window['permission'] = permission window["permission"] = permission;
Vue.use(install); // eslint-disable-line Vue.use(install); // eslint-disable-line
} }
permission.install = install permission.install = install;
export default permission export default permission;

View File

@ -1,22 +1,22 @@
import store from '@/store' import store from "@/store";
export default { export default {
inserted(el, binding, vnode) { inserted(el, binding, vnode) {
const { value } = binding const { value } = binding;
const roles = store.getters && store.getters.roles const roles = store.getters && store.getters.roles;
if (value && value instanceof Array && value.length > 0) { if (value && value instanceof Array && value.length > 0) {
const permissionRoles = value const permissionRoles = value;
const hasPermission = roles.some(role => { const hasPermission = roles.some(role => {
return permissionRoles.includes(role) return permissionRoles.includes(role);
}) });
if (!hasPermission) { if (!hasPermission) {
el.parentNode && el.parentNode.removeChild(el) el.parentNode && el.parentNode.removeChild(el);
} }
} else { } else {
throw new Error(`need roles! Like v-permission="['admin','editor']"`) throw new Error(`need roles! Like v-permission="['admin','editor']"`);
} }
} }
} };

View File

@ -1,91 +1,90 @@
const vueSticky = {} const vueSticky = {};
let listenAction let listenAction;
vueSticky.install = Vue => { vueSticky.install = Vue => {
Vue.directive('sticky', { Vue.directive("sticky", {
inserted(el, binding) { inserted(el, binding) {
const params = binding.value || {} const params = binding.value || {};
const stickyTop = params.stickyTop || 0 const stickyTop = params.stickyTop || 0;
const zIndex = params.zIndex || 1000 const zIndex = params.zIndex || 1000;
const elStyle = el.style const elStyle = el.style;
elStyle.position = '-webkit-sticky' elStyle.position = "-webkit-sticky";
elStyle.position = 'sticky' elStyle.position = "sticky";
// if the browser support css stickyCurrently Safari, Firefox and Chrome Canary // if the browser support css stickyCurrently Safari, Firefox and Chrome Canary
// if (~elStyle.position.indexOf('sticky')) { // if (~elStyle.position.indexOf('sticky')) {
// elStyle.top = `${stickyTop}px`; // elStyle.top = `${stickyTop}px`;
// elStyle.zIndex = zIndex; // elStyle.zIndex = zIndex;
// return // return
// } // }
const elHeight = el.getBoundingClientRect().height const elHeight = el.getBoundingClientRect().height;
const elWidth = el.getBoundingClientRect().width const elWidth = el.getBoundingClientRect().width;
elStyle.cssText = `top: ${stickyTop}px; z-index: ${zIndex}` elStyle.cssText = `top: ${stickyTop}px; z-index: ${zIndex}`;
const parentElm = el.parentNode || document.documentElement const parentElm = el.parentNode || document.documentElement;
const placeholder = document.createElement('div') const placeholder = document.createElement("div");
placeholder.style.display = 'none' placeholder.style.display = "none";
placeholder.style.width = `${elWidth}px` placeholder.style.width = `${elWidth}px`;
placeholder.style.height = `${elHeight}px` placeholder.style.height = `${elHeight}px`;
parentElm.insertBefore(placeholder, el) parentElm.insertBefore(placeholder, el);
let active = false let active = false;
const getScroll = (target, top) => { const getScroll = (target, top) => {
const prop = top ? 'pageYOffset' : 'pageXOffset' const prop = top ? "pageYOffset" : "pageXOffset";
const method = top ? 'scrollTop' : 'scrollLeft' const method = top ? "scrollTop" : "scrollLeft";
let ret = target[prop] let ret = target[prop];
if (typeof ret !== 'number') { if (typeof ret !== "number") {
ret = window.document.documentElement[method] ret = window.document.documentElement[method];
}
return ret
} }
return ret;
};
const sticky = () => { const sticky = () => {
if (active) { if (active) {
return return;
} }
if (!elStyle.height) { if (!elStyle.height) {
elStyle.height = `${el.offsetHeight}px` elStyle.height = `${el.offsetHeight}px`;
} }
elStyle.position = 'fixed' elStyle.position = "fixed";
elStyle.width = `${elWidth}px` elStyle.width = `${elWidth}px`;
placeholder.style.display = 'inline-block' placeholder.style.display = "inline-block";
active = true active = true;
} };
const reset = () => { const reset = () => {
if (!active) { if (!active) {
return return;
} }
elStyle.position = '' elStyle.position = "";
placeholder.style.display = 'none' placeholder.style.display = "none";
active = false active = false;
} };
const check = () => { const check = () => {
const scrollTop = getScroll(window, true) const scrollTop = getScroll(window, true);
const offsetTop = el.getBoundingClientRect().top const offsetTop = el.getBoundingClientRect().top;
if (offsetTop < stickyTop) { if (offsetTop < stickyTop) {
sticky() sticky();
} else { } else {
if (scrollTop < elHeight + stickyTop) { if (scrollTop < elHeight + stickyTop) {
reset() reset();
}
} }
} }
};
listenAction = () => { listenAction = () => {
check() check();
} };
window.addEventListener('scroll', listenAction) window.addEventListener("scroll", listenAction);
}, },
unbind() { unbind() {
window.removeEventListener('scroll', listenAction) window.removeEventListener("scroll", listenAction);
} }
}) });
} };
export default vueSticky
export default vueSticky;

View File

@ -1,13 +1,13 @@
import waves from './waves' import waves from "./waves";
const install = function(Vue) { const install = function(Vue) {
Vue.directive('waves', waves) Vue.directive("waves", waves);
} };
if (window.Vue) { if (window.Vue) {
window.waves = waves window.waves = waves;
Vue.use(install); // eslint-disable-line Vue.use(install); // eslint-disable-line
} }
waves.install = install waves.install = install;
export default waves export default waves;

View File

@ -1,72 +1,80 @@
import './waves.css' import "./waves.css";
const context = '@@wavesContext' const context = "@@wavesContext";
function handleClick(el, binding) { function handleClick(el, binding) {
function handle(e) { function handle(e) {
const customOpts = Object.assign({}, binding.value) const customOpts = Object.assign({}, binding.value);
const opts = Object.assign({ const opts = Object.assign(
{
ele: el, // 波纹作用元素 ele: el, // 波纹作用元素
type: 'hit', // hit 点击位置扩散 center中心点扩展 type: "hit", // hit 点击位置扩散 center中心点扩展
color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色 color: "rgba(0, 0, 0, 0.15)" // 波纹颜色
}, },
customOpts customOpts
) );
const target = opts.ele const target = opts.ele;
if (target) { if (target) {
target.style.position = 'relative' target.style.position = "relative";
target.style.overflow = 'hidden' target.style.overflow = "hidden";
const rect = target.getBoundingClientRect() const rect = target.getBoundingClientRect();
let ripple = target.querySelector('.waves-ripple') let ripple = target.querySelector(".waves-ripple");
if (!ripple) { if (!ripple) {
ripple = document.createElement('span') ripple = document.createElement("span");
ripple.className = 'waves-ripple' ripple.className = "waves-ripple";
ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px' ripple.style.height = ripple.style.width =
target.appendChild(ripple) Math.max(rect.width, rect.height) + "px";
target.appendChild(ripple);
} else { } else {
ripple.className = 'waves-ripple' ripple.className = "waves-ripple";
} }
switch (opts.type) { switch (opts.type) {
case 'center': case "center":
ripple.style.top = rect.height / 2 - ripple.offsetHeight / 2 + 'px' ripple.style.top = rect.height / 2 - ripple.offsetHeight / 2 + "px";
ripple.style.left = rect.width / 2 - ripple.offsetWidth / 2 + 'px' ripple.style.left = rect.width / 2 - ripple.offsetWidth / 2 + "px";
break break;
default: default:
ripple.style.top = ripple.style.top =
(e.pageY - rect.top - ripple.offsetHeight / 2 - document.documentElement.scrollTop || (e.pageY -
document.body.scrollTop) + 'px' rect.top -
ripple.offsetHeight / 2 -
document.documentElement.scrollTop || document.body.scrollTop) +
"px";
ripple.style.left = ripple.style.left =
(e.pageX - rect.left - ripple.offsetWidth / 2 - document.documentElement.scrollLeft || (e.pageX -
document.body.scrollLeft) + 'px' rect.left -
ripple.offsetWidth / 2 -
document.documentElement.scrollLeft || document.body.scrollLeft) +
"px";
} }
ripple.style.backgroundColor = opts.color ripple.style.backgroundColor = opts.color;
ripple.className = 'waves-ripple z-active' ripple.className = "waves-ripple z-active";
return false return false;
} }
} }
if (!el[context]) { if (!el[context]) {
el[context] = { el[context] = {
removeHandle: handle removeHandle: handle
} };
} else { } else {
el[context].removeHandle = handle el[context].removeHandle = handle;
} }
return handle return handle;
} }
export default { export default {
bind(el, binding) { bind(el, binding) {
el.addEventListener('click', handleClick(el, binding), false) el.addEventListener("click", handleClick(el, binding), false);
}, },
update(el, binding) { update(el, binding) {
el.removeEventListener('click', el[context].removeHandle, false) el.removeEventListener("click", el[context].removeHandle, false);
el.addEventListener('click', handleClick(el, binding), false) el.addEventListener("click", handleClick(el, binding), false);
}, },
unbind(el) { unbind(el) {
el.removeEventListener('click', el[context].removeHandle, false) el.removeEventListener("click", el[context].removeHandle, false);
el[context] = null el[context] = null;
delete el[context] delete el[context];
} }
} };

View File

@ -1,5 +1,5 @@
// import parseTime, formatTime and set to filter // import parseTime, formatTime and set to filter
export { parseTime, formatTime } from '@/utils' export { parseTime, formatTime } from "@/utils";
/** /**
* Show plural label if time is plural number * Show plural label if time is plural number
@ -9,22 +9,22 @@ export { parseTime, formatTime } from '@/utils'
*/ */
function pluralize(time, label) { function pluralize(time, label) {
if (time === 1) { if (time === 1) {
return time + label return time + label;
} }
return time + label + 's' return time + label + "s";
} }
/** /**
* @param {number} time * @param {number} time
*/ */
export function timeAgo(time) { export function timeAgo(time) {
const between = Date.now() / 1000 - Number(time) const between = Date.now() / 1000 - Number(time);
if (between < 3600) { if (between < 3600) {
return pluralize(~~(between / 60), ' minute') return pluralize(~~(between / 60), " minute");
} else if (between < 86400) { } else if (between < 86400) {
return pluralize(~~(between / 3600), ' hour') return pluralize(~~(between / 3600), " hour");
} else { } else {
return pluralize(~~(between / 86400), ' day') return pluralize(~~(between / 86400), " day");
} }
} }
@ -36,19 +36,23 @@ export function timeAgo(time) {
*/ */
export function numberFormatter(num, digits) { export function numberFormatter(num, digits) {
const si = [ const si = [
{ value: 1E18, symbol: 'E' }, { value: 1e18, symbol: "E" },
{ value: 1E15, symbol: 'P' }, { value: 1e15, symbol: "P" },
{ value: 1E12, symbol: 'T' }, { value: 1e12, symbol: "T" },
{ value: 1E9, symbol: 'G' }, { value: 1e9, symbol: "G" },
{ value: 1E6, symbol: 'M' }, { value: 1e6, symbol: "M" },
{ value: 1E3, symbol: 'k' } { value: 1e3, symbol: "k" }
] ];
for (let i = 0; i < si.length; i++) { for (let i = 0; i < si.length; i++) {
if (num >= si[i].value) { if (num >= si[i].value) {
return (num / si[i].value + 0.1).toFixed(digits).replace(/\.0+$|(\.[0-9]*[1-9])0+$/, '$1') + si[i].symbol return (
(num / si[i].value + 0.1)
.toFixed(digits)
.replace(/\.0+$|(\.[0-9]*[1-9])0+$/, "$1") + si[i].symbol
);
} }
} }
return num.toString() return num.toString();
} }
/** /**
@ -56,7 +60,9 @@ export function numberFormatter(num, digits) {
* @param {number} num * @param {number} num
*/ */
export function toThousandFilter(num) { export function toThousandFilter(num) {
return (+num || 0).toString().replace(/^-?\d+/g, m => m.replace(/(?=(?!\b)(\d{3})+$)/g, ',')) return (+num || 0)
.toString()
.replace(/^-?\d+/g, m => m.replace(/(?=(?!\b)(\d{3})+$)/g, ","));
} }
/** /**
@ -64,5 +70,5 @@ export function toThousandFilter(num) {
* @param {String} string * @param {String} string
*/ */
export function uppercaseFirst(string) { export function uppercaseFirst(string) {
return string.charAt(0).toUpperCase() + string.slice(1) return string.charAt(0).toUpperCase() + string.slice(1);
} }

View File

@ -1,9 +1,9 @@
import Vue from 'vue' import Vue from "vue";
import SvgIcon from '@/components/SvgIcon'// svg component import SvgIcon from "@/components/SvgIcon"; // svg component
// register globally // register globally
Vue.component('svg-icon', SvgIcon) Vue.component("svg-icon", SvgIcon);
const req = require.context('./svg', false, /\.svg$/) const req = require.context("./svg", false, /\.svg$/);
const requireAll = requireContext => requireContext.keys().map(requireContext) const requireAll = requireContext => requireContext.keys().map(requireContext);
requireAll(req) requireAll(req);

View File

@ -10,16 +10,16 @@
<script> <script>
export default { export default {
name: 'AppMain', name: "AppMain",
computed: { computed: {
cachedViews() { cachedViews() {
return this.$store.state.tagsView.cachedViews return this.$store.state.tagsView.cachedViews;
}, },
key() { key() {
return this.$route.path return this.$route.path;
} }
} }
} };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -31,7 +31,7 @@ export default {
overflow: hidden; overflow: hidden;
} }
.fixed-header+.app-main { .fixed-header + .app-main {
padding-top: 50px; padding-top: 50px;
} }
@ -41,7 +41,7 @@ export default {
min-height: calc(100vh - 84px); min-height: calc(100vh - 84px);
} }
.fixed-header+.app-main { .fixed-header + .app-main {
padding-top: 84px; padding-top: 84px;
} }
} }

View File

@ -1,11 +1,16 @@
<template> <template>
<div class="navbar"> <div class="navbar">
<hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" /> <hamburger
id="hamburger-container"
:is-active="sidebar.opened"
class="hamburger-container"
@toggleClick="toggleSideBar"
/>
<breadcrumb id="breadcrumb-container" class="breadcrumb-container" /> <breadcrumb id="breadcrumb-container" class="breadcrumb-container" />
<div class="right-menu"> <div class="right-menu">
<template v-if="device!=='mobile'"> <template v-if="device !== 'mobile'">
<search id="header-search" class="right-menu-item" /> <search id="header-search" class="right-menu-item" />
<error-log class="errLog-container right-menu-item hover-effect" /> <error-log class="errLog-container right-menu-item hover-effect" />
@ -15,12 +20,14 @@
<el-tooltip content="Global Size" effect="dark" placement="bottom"> <el-tooltip content="Global Size" effect="dark" placement="bottom">
<size-select id="size-select" class="right-menu-item hover-effect" /> <size-select id="size-select" class="right-menu-item hover-effect" />
</el-tooltip> </el-tooltip>
</template> </template>
<el-dropdown class="avatar-container right-menu-item hover-effect" trigger="click"> <el-dropdown
class="avatar-container right-menu-item hover-effect"
trigger="click"
>
<div class="avatar-wrapper"> <div class="avatar-wrapper">
<img :src="avatar+'?imageView2/1/w/80/h/80'" class="user-avatar"> <img :src="avatar + '?imageView2/1/w/80/h/80'" class="user-avatar" />
<i class="el-icon-caret-bottom" /> <i class="el-icon-caret-bottom" />
</div> </div>
<el-dropdown-menu slot="dropdown"> <el-dropdown-menu slot="dropdown">
@ -30,10 +37,16 @@
<router-link to="/"> <router-link to="/">
<el-dropdown-item>Dashboard</el-dropdown-item> <el-dropdown-item>Dashboard</el-dropdown-item>
</router-link> </router-link>
<a target="_blank" href="https://github.com/PanJiaChen/vue-element-admin/"> <a
target="_blank"
href="https://github.com/PanJiaChen/vue-element-admin/"
>
<el-dropdown-item>Github</el-dropdown-item> <el-dropdown-item>Github</el-dropdown-item>
</a> </a>
<a target="_blank" href="https://panjiachen.github.io/vue-element-admin-site/#/"> <a
target="_blank"
href="https://panjiachen.github.io/vue-element-admin-site/#/"
>
<el-dropdown-item>Docs</el-dropdown-item> <el-dropdown-item>Docs</el-dropdown-item>
</a> </a>
<el-dropdown-item divided> <el-dropdown-item divided>
@ -46,13 +59,13 @@
</template> </template>
<script> <script>
import { mapGetters } from 'vuex' import { mapGetters } from "vuex";
import Breadcrumb from '@/components/Breadcrumb' import Breadcrumb from "@/components/Breadcrumb";
import Hamburger from '@/components/Hamburger' import Hamburger from "@/components/Hamburger";
import ErrorLog from '@/components/ErrorLog' import ErrorLog from "@/components/ErrorLog";
import Screenfull from '@/components/Screenfull' import Screenfull from "@/components/Screenfull";
import SizeSelect from '@/components/SizeSelect' import SizeSelect from "@/components/SizeSelect";
import Search from '@/components/HeaderSearch' import Search from "@/components/HeaderSearch";
export default { export default {
components: { components: {
@ -64,22 +77,18 @@ export default {
Search Search
}, },
computed: { computed: {
...mapGetters([ ...mapGetters(["sidebar", "avatar", "device"])
'sidebar',
'avatar',
'device'
])
}, },
methods: { methods: {
toggleSideBar() { toggleSideBar() {
this.$store.dispatch('app/toggleSideBar') this.$store.dispatch("app/toggleSideBar");
}, },
async logout() { async logout() {
await this.$store.dispatch('user/logout') await this.$store.dispatch("user/logout");
this.$router.push(`/login?redirect=${this.$route.fullPath}`) this.$router.push(`/login?redirect=${this.$route.fullPath}`);
} }
} }
} };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -88,18 +97,18 @@ export default {
overflow: hidden; overflow: hidden;
position: relative; position: relative;
background: #fff; background: #fff;
box-shadow: 0 1px 4px rgba(0,21,41,.08); box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
.hamburger-container { .hamburger-container {
line-height: 46px; line-height: 46px;
height: 100%; height: 100%;
float: left; float: left;
cursor: pointer; cursor: pointer;
transition: background .3s; transition: background 0.3s;
-webkit-tap-highlight-color:transparent; -webkit-tap-highlight-color: transparent;
&:hover { &:hover {
background: rgba(0, 0, 0, .025) background: rgba(0, 0, 0, 0.025);
} }
} }
@ -131,10 +140,10 @@ export default {
&.hover-effect { &.hover-effect {
cursor: pointer; cursor: pointer;
transition: background .3s; transition: background 0.3s;
&:hover { &:hover {
background: rgba(0, 0, 0, .025) background: rgba(0, 0, 0, 0.025);
} }
} }
} }

View File

@ -5,7 +5,10 @@
<div class="drawer-item"> <div class="drawer-item">
<span>Theme Color</span> <span>Theme Color</span>
<theme-picker style="float: right;height: 26px;margin: -3px 8px 0 0;" @change="themeChange" /> <theme-picker
style="float: right;height: 26px;margin: -3px 8px 0 0;"
@change="themeChange"
/>
</div> </div>
<div class="drawer-item"> <div class="drawer-item">
@ -22,63 +25,62 @@
<span>Sidebar Logo</span> <span>Sidebar Logo</span>
<el-switch v-model="sidebarLogo" class="drawer-switch" /> <el-switch v-model="sidebarLogo" class="drawer-switch" />
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import ThemePicker from '@/components/ThemePicker' import ThemePicker from "@/components/ThemePicker";
export default { export default {
components: { ThemePicker }, components: { ThemePicker },
data() { data() {
return {} return {};
}, },
computed: { computed: {
fixedHeader: { fixedHeader: {
get() { get() {
return this.$store.state.settings.fixedHeader return this.$store.state.settings.fixedHeader;
}, },
set(val) { set(val) {
this.$store.dispatch('settings/changeSetting', { this.$store.dispatch("settings/changeSetting", {
key: 'fixedHeader', key: "fixedHeader",
value: val value: val
}) });
} }
}, },
tagsView: { tagsView: {
get() { get() {
return this.$store.state.settings.tagsView return this.$store.state.settings.tagsView;
}, },
set(val) { set(val) {
this.$store.dispatch('settings/changeSetting', { this.$store.dispatch("settings/changeSetting", {
key: 'tagsView', key: "tagsView",
value: val value: val
}) });
} }
}, },
sidebarLogo: { sidebarLogo: {
get() { get() {
return this.$store.state.settings.sidebarLogo return this.$store.state.settings.sidebarLogo;
}, },
set(val) { set(val) {
this.$store.dispatch('settings/changeSetting', { this.$store.dispatch("settings/changeSetting", {
key: 'sidebarLogo', key: "sidebarLogo",
value: val value: val
}) });
} }
} }
}, },
methods: { methods: {
themeChange(val) { themeChange(val) {
this.$store.dispatch('settings/changeSetting', { this.$store.dispatch("settings/changeSetting", {
key: 'theme', key: "theme",
value: val value: val
}) });
} }
} }
} };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -90,19 +92,19 @@ export default {
.drawer-title { .drawer-title {
margin-bottom: 12px; margin-bottom: 12px;
color: rgba(0, 0, 0, .85); color: rgba(0, 0, 0, 0.85);
font-size: 14px; font-size: 14px;
line-height: 22px; line-height: 22px;
} }
.drawer-item { .drawer-item {
color: rgba(0, 0, 0, .65); color: rgba(0, 0, 0, 0.65);
font-size: 14px; font-size: 14px;
padding: 12px 0; padding: 12px 0;
} }
.drawer-switch { .drawer-switch {
float: right float: right;
} }
} }
</style> </style>

View File

@ -1,26 +1,26 @@
export default { export default {
computed: { computed: {
device() { device() {
return this.$store.state.app.device return this.$store.state.app.device;
} }
}, },
mounted() { mounted() {
// In order to fix the click on menu on the ios device will trigger the mouseleave bug // In order to fix the click on menu on the ios device will trigger the mouseleave bug
// https://github.com/PanJiaChen/vue-element-admin/issues/1135 // https://github.com/PanJiaChen/vue-element-admin/issues/1135
this.fixBugIniOS() this.fixBugIniOS();
}, },
methods: { methods: {
fixBugIniOS() { fixBugIniOS() {
const $subMenu = this.$refs.subMenu const $subMenu = this.$refs.subMenu;
if ($subMenu) { if ($subMenu) {
const handleMouseleave = $subMenu.handleMouseleave const handleMouseleave = $subMenu.handleMouseleave;
$subMenu.handleMouseleave = (e) => { $subMenu.handleMouseleave = e => {
if (this.device === 'mobile') { if (this.device === "mobile") {
return return;
} }
handleMouseleave(e) handleMouseleave(e);
};
} }
} }
} }
} };
}

View File

@ -1,29 +1,29 @@
<script> <script>
export default { export default {
name: 'MenuItem', name: "MenuItem",
functional: true, functional: true,
props: { props: {
icon: { icon: {
type: String, type: String,
default: '' default: ""
}, },
title: { title: {
type: String, type: String,
default: '' default: ""
} }
}, },
render(h, context) { render(h, context) {
const { icon, title } = context.props const { icon, title } = context.props;
const vnodes = [] const vnodes = [];
if (icon) { if (icon) {
vnodes.push(<svg-icon icon-class={icon}/>) vnodes.push(<svg-icon icon-class={icon} />);
} }
if (title) { if (title) {
vnodes.push(<span slot='title'>{(title)}</span>) vnodes.push(<span slot="title">{title}</span>);
} }
return vnodes return vnodes;
} }
} };
</script> </script>

View File

@ -1,4 +1,3 @@
<template> <template>
<!-- eslint-disable vue/require-component-is --> <!-- eslint-disable vue/require-component-is -->
<component v-bind="linkProps(to)"> <component v-bind="linkProps(to)">
@ -7,7 +6,7 @@
</template> </template>
<script> <script>
import { isExternal } from '@/utils/validate' import { isExternal } from "@/utils/validate";
export default { export default {
props: { props: {
@ -20,17 +19,17 @@ export default {
linkProps(url) { linkProps(url) {
if (isExternal(url)) { if (isExternal(url)) {
return { return {
is: 'a', is: "a",
href: url, href: url,
target: '_blank', target: "_blank",
rel: 'noopener' rel: "noopener"
} };
} }
return { return {
is: 'router-link', is: "router-link",
to: url to: url
};
} }
} }
} };
}
</script> </script>

View File

@ -1,13 +1,18 @@
<template> <template>
<div class="sidebar-logo-container" :class="{'collapse':collapse}"> <div class="sidebar-logo-container" :class="{ collapse: collapse }">
<transition name="sidebarLogoFade"> <transition name="sidebarLogoFade">
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/"> <router-link
<img v-if="logo" :src="logo" class="sidebar-logo"> v-if="collapse"
<h1 v-else class="sidebar-title">{{ title }} </h1> key="collapse"
class="sidebar-logo-link"
to="/"
>
<img v-if="logo" :src="logo" class="sidebar-logo" />
<h1 v-else class="sidebar-title">{{ title }}</h1>
</router-link> </router-link>
<router-link v-else key="expand" class="sidebar-logo-link" to="/"> <router-link v-else key="expand" class="sidebar-logo-link" to="/">
<img v-if="logo" :src="logo" class="sidebar-logo"> <img v-if="logo" :src="logo" class="sidebar-logo" />
<h1 class="sidebar-title">{{ title }} </h1> <h1 class="sidebar-title">{{ title }}</h1>
</router-link> </router-link>
</transition> </transition>
</div> </div>
@ -15,7 +20,7 @@
<script> <script>
export default { export default {
name: 'SidebarLogo', name: "SidebarLogo",
props: { props: {
collapse: { collapse: {
type: Boolean, type: Boolean,
@ -24,11 +29,12 @@ export default {
}, },
data() { data() {
return { return {
title: 'Vue Element Admin', title: "Vue Element Admin",
logo: 'https://wpimg.wallstcn.com/69a1c46c-eb1c-4b46-8bd4-e9e686ef5251.png' logo:
"https://wpimg.wallstcn.com/69a1c46c-eb1c-4b46-8bd4-e9e686ef5251.png"
};
} }
} };
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -1,16 +1,37 @@
<template> <template>
<div v-if="!item.hidden" class="menu-wrapper"> <div v-if="!item.hidden" class="menu-wrapper">
<template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow"> <template
v-if="
hasOneShowingChild(item.children, item) &&
(!onlyOneChild.children || onlyOneChild.noShowingChildren) &&
!item.alwaysShow
"
>
<app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)"> <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)">
<el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}"> <el-menu-item
<item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title" /> :index="resolvePath(onlyOneChild.path)"
:class="{ 'submenu-title-noDropdown': !isNest }"
>
<item
:icon="onlyOneChild.meta.icon || (item.meta && item.meta.icon)"
:title="onlyOneChild.meta.title"
/>
</el-menu-item> </el-menu-item>
</app-link> </app-link>
</template> </template>
<el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body> <el-submenu
v-else
ref="subMenu"
:index="resolvePath(item.path)"
popper-append-to-body
>
<template slot="title"> <template slot="title">
<item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" /> <item
v-if="item.meta"
:icon="item.meta && item.meta.icon"
:title="item.meta.title"
/>
</template> </template>
<sidebar-item <sidebar-item
v-for="child in item.children" v-for="child in item.children"
@ -25,14 +46,14 @@
</template> </template>
<script> <script>
import path from 'path' import path from "path";
import { isExternal } from '@/utils/validate' import { isExternal } from "@/utils/validate";
import Item from './Item' import Item from "./Item";
import AppLink from './Link' import AppLink from "./Link";
import FixiOSBug from './FixiOSBug' import FixiOSBug from "./FixiOSBug";
export default { export default {
name: 'SidebarItem', name: "SidebarItem",
components: { Item, AppLink }, components: { Item, AppLink },
mixins: [FixiOSBug], mixins: [FixiOSBug],
props: { props: {
@ -47,49 +68,49 @@ export default {
}, },
basePath: { basePath: {
type: String, type: String,
default: '' default: ""
} }
}, },
data() { data() {
// To fix https://github.com/PanJiaChen/vue-admin-template/issues/237 // To fix https://github.com/PanJiaChen/vue-admin-template/issues/237
// TODO: refactor with render function // TODO: refactor with render function
this.onlyOneChild = null this.onlyOneChild = null;
return {} return {};
}, },
methods: { methods: {
hasOneShowingChild(children = [], parent) { hasOneShowingChild(children = [], parent) {
const showingChildren = children.filter(item => { const showingChildren = children.filter(item => {
if (item.hidden) { if (item.hidden) {
return false return false;
} else { } else {
// Temp set(will be used if only has one showing child) // Temp set(will be used if only has one showing child)
this.onlyOneChild = item this.onlyOneChild = item;
return true return true;
} }
}) });
// When there is only one child router, the child router is displayed by default // When there is only one child router, the child router is displayed by default
if (showingChildren.length === 1) { if (showingChildren.length === 1) {
return true return true;
} }
// Show parent if there are no child router to display // Show parent if there are no child router to display
if (showingChildren.length === 0) { if (showingChildren.length === 0) {
this.onlyOneChild = { ... parent, path: '', noShowingChildren: true } this.onlyOneChild = { ...parent, path: "", noShowingChildren: true };
return true return true;
} }
return false return false;
}, },
resolvePath(routePath) { resolvePath(routePath) {
if (isExternal(routePath)) { if (isExternal(routePath)) {
return routePath return routePath;
} }
if (isExternal(this.basePath)) { if (isExternal(this.basePath)) {
return this.basePath return this.basePath;
} }
return path.resolve(this.basePath, routePath) return path.resolve(this.basePath, routePath);
} }
} }
} };
</script> </script>

View File

@ -1,5 +1,5 @@
<template> <template>
<div :class="{'has-logo':showLogo}"> <div :class="{ 'has-logo': showLogo }">
<logo v-if="showLogo" :collapse="isCollapse" /> <logo v-if="showLogo" :collapse="isCollapse" />
<el-scrollbar wrap-class="scrollbar-wrapper"> <el-scrollbar wrap-class="scrollbar-wrapper">
<el-menu <el-menu
@ -12,43 +12,45 @@
:collapse-transition="false" :collapse-transition="false"
mode="vertical" mode="vertical"
> >
<sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path" /> <sidebar-item
v-for="route in permission_routes"
:key="route.path"
:item="route"
:base-path="route.path"
/>
</el-menu> </el-menu>
</el-scrollbar> </el-scrollbar>
</div> </div>
</template> </template>
<script> <script>
import { mapGetters } from 'vuex' import { mapGetters } from "vuex";
import Logo from './Logo' import Logo from "./Logo";
import SidebarItem from './SidebarItem' import SidebarItem from "./SidebarItem";
import variables from '@/styles/variables.scss' import variables from "@/styles/variables.scss";
export default { export default {
components: { SidebarItem, Logo }, components: { SidebarItem, Logo },
computed: { computed: {
...mapGetters([ ...mapGetters(["permission_routes", "sidebar"]),
'permission_routes',
'sidebar'
]),
activeMenu() { activeMenu() {
const route = this.$route const route = this.$route;
const { meta, path } = route const { meta, path } = route;
// if set path, the sidebar will highlight the path you set // if set path, the sidebar will highlight the path you set
if (meta.activeMenu) { if (meta.activeMenu) {
return meta.activeMenu return meta.activeMenu;
} }
return path return path;
}, },
showLogo() { showLogo() {
return this.$store.state.settings.sidebarLogo return this.$store.state.settings.sidebarLogo;
}, },
variables() { variables() {
return variables return variables;
}, },
isCollapse() { isCollapse() {
return !this.sidebar.opened return !this.sidebar.opened;
} }
} }
} };
</script> </script>

View File

@ -1,70 +1,81 @@
<template> <template>
<el-scrollbar ref="scrollContainer" :vertical="false" class="scroll-container" @wheel.native.prevent="handleScroll"> <el-scrollbar
ref="scrollContainer"
:vertical="false"
class="scroll-container"
@wheel.native.prevent="handleScroll"
>
<slot /> <slot />
</el-scrollbar> </el-scrollbar>
</template> </template>
<script> <script>
const tagAndTagSpacing = 4 // tagAndTagSpacing const tagAndTagSpacing = 4; // tagAndTagSpacing
export default { export default {
name: 'ScrollPane', name: "ScrollPane",
data() { data() {
return { return {
left: 0 left: 0
} };
}, },
computed: { computed: {
scrollWrapper() { scrollWrapper() {
return this.$refs.scrollContainer.$refs.wrap return this.$refs.scrollContainer.$refs.wrap;
} }
}, },
methods: { methods: {
handleScroll(e) { handleScroll(e) {
const eventDelta = e.wheelDelta || -e.deltaY * 40 const eventDelta = e.wheelDelta || -e.deltaY * 40;
const $scrollWrapper = this.scrollWrapper const $scrollWrapper = this.scrollWrapper;
$scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4 $scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4;
}, },
moveToTarget(currentTag) { moveToTarget(currentTag) {
const $container = this.$refs.scrollContainer.$el const $container = this.$refs.scrollContainer.$el;
const $containerWidth = $container.offsetWidth const $containerWidth = $container.offsetWidth;
const $scrollWrapper = this.scrollWrapper const $scrollWrapper = this.scrollWrapper;
const tagList = this.$parent.$refs.tag const tagList = this.$parent.$refs.tag;
let firstTag = null let firstTag = null;
let lastTag = null let lastTag = null;
// find first tag and last tag // find first tag and last tag
if (tagList.length > 0) { if (tagList.length > 0) {
firstTag = tagList[0] firstTag = tagList[0];
lastTag = tagList[tagList.length - 1] lastTag = tagList[tagList.length - 1];
} }
if (firstTag === currentTag) { if (firstTag === currentTag) {
$scrollWrapper.scrollLeft = 0 $scrollWrapper.scrollLeft = 0;
} else if (lastTag === currentTag) { } else if (lastTag === currentTag) {
$scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth $scrollWrapper.scrollLeft =
$scrollWrapper.scrollWidth - $containerWidth;
} else { } else {
// find preTag and nextTag // find preTag and nextTag
const currentIndex = tagList.findIndex(item => item === currentTag) const currentIndex = tagList.findIndex(item => item === currentTag);
const prevTag = tagList[currentIndex - 1] const prevTag = tagList[currentIndex - 1];
const nextTag = tagList[currentIndex + 1] const nextTag = tagList[currentIndex + 1];
// the tag's offsetLeft after of nextTag // the tag's offsetLeft after of nextTag
const afterNextTagOffsetLeft = nextTag.$el.offsetLeft + nextTag.$el.offsetWidth + tagAndTagSpacing const afterNextTagOffsetLeft =
nextTag.$el.offsetLeft + nextTag.$el.offsetWidth + tagAndTagSpacing;
// the tag's offsetLeft before of prevTag // the tag's offsetLeft before of prevTag
const beforePrevTagOffsetLeft = prevTag.$el.offsetLeft - tagAndTagSpacing const beforePrevTagOffsetLeft =
prevTag.$el.offsetLeft - tagAndTagSpacing;
if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) { if (
$scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth afterNextTagOffsetLeft >
$scrollWrapper.scrollLeft + $containerWidth
) {
$scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth;
} else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) { } else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
$scrollWrapper.scrollLeft = beforePrevTagOffsetLeft $scrollWrapper.scrollLeft = beforePrevTagOffsetLeft;
} }
} }
} }
} }
} };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -73,7 +84,7 @@ export default {
position: relative; position: relative;
overflow: hidden; overflow: hidden;
width: 100%; width: 100%;
/deep/ { ::v-deep {
.el-scrollbar__bar { .el-scrollbar__bar {
bottom: 0px; bottom: 0px;
} }

View File

@ -5,20 +5,33 @@
v-for="tag in visitedViews" v-for="tag in visitedViews"
ref="tag" ref="tag"
:key="tag.path" :key="tag.path"
:class="isActive(tag)?'active':''" :class="isActive(tag) ? 'active' : ''"
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }" :to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
tag="span" tag="span"
class="tags-view-item" class="tags-view-item"
@click.middle.native="closeSelectedTag(tag)" @click.middle.native="closeSelectedTag(tag)"
@contextmenu.prevent.native="openMenu(tag,$event)" @contextmenu.prevent.native="openMenu(tag, $event)"
> >
{{ tag.title }} {{ tag.title }}
<span v-if="!tag.meta.affix" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" /> <span
v-if="!tag.meta.affix"
class="el-icon-close"
@click.prevent.stop="closeSelectedTag(tag)"
/>
</router-link> </router-link>
</scroll-pane> </scroll-pane>
<ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu"> <ul
v-show="visible"
:style="{ left: left + 'px', top: top + 'px' }"
class="contextmenu"
>
<li @click="refreshSelectedTag(selectedTag)">Refresh</li> <li @click="refreshSelectedTag(selectedTag)">Refresh</li>
<li v-if="!(selectedTag.meta&&selectedTag.meta.affix)" @click="closeSelectedTag(selectedTag)">Close</li> <li
v-if="!(selectedTag.meta && selectedTag.meta.affix)"
@click="closeSelectedTag(selectedTag)"
>
Close
</li>
<li @click="closeOthersTags">Close Others</li> <li @click="closeOthersTags">Close Others</li>
<li @click="closeAllTags(selectedTag)">Close All</li> <li @click="closeAllTags(selectedTag)">Close All</li>
</ul> </ul>
@ -26,8 +39,8 @@
</template> </template>
<script> <script>
import ScrollPane from './ScrollPane' import ScrollPane from "./ScrollPane";
import path from 'path' import path from "path";
export default { export default {
components: { ScrollPane }, components: { ScrollPane },
@ -38,157 +51,161 @@ export default {
left: 0, left: 0,
selectedTag: {}, selectedTag: {},
affixTags: [] affixTags: []
} };
}, },
computed: { computed: {
visitedViews() { visitedViews() {
return this.$store.state.tagsView.visitedViews return this.$store.state.tagsView.visitedViews;
}, },
routes() { routes() {
return this.$store.state.permission.routes return this.$store.state.permission.routes;
} }
}, },
watch: { watch: {
$route() { $route() {
this.addTags() this.addTags();
this.moveToCurrentTag() this.moveToCurrentTag();
}, },
visible(value) { visible(value) {
if (value) { if (value) {
document.body.addEventListener('click', this.closeMenu) document.body.addEventListener("click", this.closeMenu);
} else { } else {
document.body.removeEventListener('click', this.closeMenu) document.body.removeEventListener("click", this.closeMenu);
} }
} }
}, },
mounted() { mounted() {
this.initTags() this.initTags();
this.addTags() this.addTags();
}, },
methods: { methods: {
isActive(route) { isActive(route) {
return route.path === this.$route.path return route.path === this.$route.path;
}, },
filterAffixTags(routes, basePath = '/') { filterAffixTags(routes, basePath = "/") {
let tags = [] let tags = [];
routes.forEach(route => { routes.forEach(route => {
if (route.meta && route.meta.affix) { if (route.meta && route.meta.affix) {
const tagPath = path.resolve(basePath, route.path) const tagPath = path.resolve(basePath, route.path);
tags.push({ tags.push({
fullPath: tagPath, fullPath: tagPath,
path: tagPath, path: tagPath,
name: route.name, name: route.name,
meta: { ...route.meta } meta: { ...route.meta }
}) });
} }
if (route.children) { if (route.children) {
const tempTags = this.filterAffixTags(route.children, route.path) const tempTags = this.filterAffixTags(route.children, route.path);
if (tempTags.length >= 1) { if (tempTags.length >= 1) {
tags = [...tags, ...tempTags] tags = [...tags, ...tempTags];
} }
} }
}) });
return tags return tags;
}, },
initTags() { initTags() {
const affixTags = this.affixTags = this.filterAffixTags(this.routes) const affixTags = (this.affixTags = this.filterAffixTags(this.routes));
for (const tag of affixTags) { for (const tag of affixTags) {
// Must have tag name // Must have tag name
if (tag.name) { if (tag.name) {
this.$store.dispatch('tagsView/addVisitedView', tag) this.$store.dispatch("tagsView/addVisitedView", tag);
} }
} }
}, },
addTags() { addTags() {
const { name } = this.$route const { name } = this.$route;
if (name) { if (name) {
this.$store.dispatch('tagsView/addView', this.$route) this.$store.dispatch("tagsView/addView", this.$route);
} }
return false return false;
}, },
moveToCurrentTag() { moveToCurrentTag() {
const tags = this.$refs.tag const tags = this.$refs.tag;
this.$nextTick(() => { this.$nextTick(() => {
for (const tag of tags) { for (const tag of tags) {
if (tag.to.path === this.$route.path) { if (tag.to.path === this.$route.path) {
this.$refs.scrollPane.moveToTarget(tag) this.$refs.scrollPane.moveToTarget(tag);
// when query is different then update // when query is different then update
if (tag.to.fullPath !== this.$route.fullPath) { if (tag.to.fullPath !== this.$route.fullPath) {
this.$store.dispatch('tagsView/updateVisitedView', this.$route) this.$store.dispatch("tagsView/updateVisitedView", this.$route);
} }
break break;
} }
} }
}) });
}, },
refreshSelectedTag(view) { refreshSelectedTag(view) {
this.$store.dispatch('tagsView/delCachedView', view).then(() => { this.$store.dispatch("tagsView/delCachedView", view).then(() => {
const { fullPath } = view const { fullPath } = view;
this.$nextTick(() => { this.$nextTick(() => {
this.$router.replace({ this.$router.replace({
path: '/redirect' + fullPath path: "/redirect" + fullPath
}) });
}) });
}) });
}, },
closeSelectedTag(view) { closeSelectedTag(view) {
this.$store.dispatch('tagsView/delView', view).then(({ visitedViews }) => { this.$store
.dispatch("tagsView/delView", view)
.then(({ visitedViews }) => {
if (this.isActive(view)) { if (this.isActive(view)) {
this.toLastView(visitedViews, view) this.toLastView(visitedViews, view);
} }
}) });
}, },
closeOthersTags() { closeOthersTags() {
this.$router.push(this.selectedTag) this.$router.push(this.selectedTag);
this.$store.dispatch('tagsView/delOthersViews', this.selectedTag).then(() => { this.$store
this.moveToCurrentTag() .dispatch("tagsView/delOthersViews", this.selectedTag)
}) .then(() => {
this.moveToCurrentTag();
});
}, },
closeAllTags(view) { closeAllTags(view) {
this.$store.dispatch('tagsView/delAllViews').then(({ visitedViews }) => { this.$store.dispatch("tagsView/delAllViews").then(({ visitedViews }) => {
if (this.affixTags.some(tag => tag.path === view.path)) { if (this.affixTags.some(tag => tag.path === view.path)) {
return return;
} }
this.toLastView(visitedViews, view) this.toLastView(visitedViews, view);
}) });
}, },
toLastView(visitedViews, view) { toLastView(visitedViews, view) {
const latestView = visitedViews.slice(-1)[0] const latestView = visitedViews.slice(-1)[0];
if (latestView) { if (latestView) {
this.$router.push(latestView) this.$router.push(latestView);
} else { } else {
// now the default is to redirect to the home page if there is no tags-view, // now the default is to redirect to the home page if there is no tags-view,
// you can adjust it according to your needs. // you can adjust it according to your needs.
if (view.name === 'Dashboard') { if (view.name === "Dashboard") {
// to reload home page // to reload home page
this.$router.replace({ path: '/redirect' + view.fullPath }) this.$router.replace({ path: "/redirect" + view.fullPath });
} else { } else {
this.$router.push('/') this.$router.push("/");
} }
} }
}, },
openMenu(tag, e) { openMenu(tag, e) {
const menuMinWidth = 105 const menuMinWidth = 105;
const offsetLeft = this.$el.getBoundingClientRect().left // container margin left const offsetLeft = this.$el.getBoundingClientRect().left; // container margin left
const offsetWidth = this.$el.offsetWidth // container width const offsetWidth = this.$el.offsetWidth; // container width
const maxLeft = offsetWidth - menuMinWidth // left boundary const maxLeft = offsetWidth - menuMinWidth; // left boundary
const left = e.clientX - offsetLeft + 15 // 15: margin right const left = e.clientX - offsetLeft + 15; // 15: margin right
if (left > maxLeft) { if (left > maxLeft) {
this.left = maxLeft this.left = maxLeft;
} else { } else {
this.left = left this.left = left;
} }
this.top = e.clientY this.top = e.clientY;
this.visible = true this.visible = true;
this.selectedTag = tag this.selectedTag = tag;
}, },
closeMenu() { closeMenu() {
this.visible = false this.visible = false;
} }
} }
} };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -197,7 +214,7 @@ export default {
width: 100%; width: 100%;
background: #fff; background: #fff;
border-bottom: 1px solid #d8dce5; border-bottom: 1px solid #d8dce5;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04); box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
.tags-view-wrapper { .tags-view-wrapper {
.tags-view-item { .tags-view-item {
display: inline-block; display: inline-block;
@ -223,7 +240,7 @@ export default {
color: #fff; color: #fff;
border-color: #42b983; border-color: #42b983;
&::before { &::before {
content: ''; content: "";
background: #fff; background: #fff;
display: inline-block; display: inline-block;
width: 8px; width: 8px;
@ -246,7 +263,7 @@ export default {
font-size: 12px; font-size: 12px;
font-weight: 400; font-weight: 400;
color: #333; color: #333;
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3); box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
li { li {
margin: 0; margin: 0;
padding: 7px 16px; padding: 7px 16px;
@ -269,10 +286,10 @@ export default {
vertical-align: 2px; vertical-align: 2px;
border-radius: 50%; border-radius: 50%;
text-align: center; text-align: center;
transition: all .3s cubic-bezier(.645, .045, .355, 1); transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
transform-origin: 100% 50%; transform-origin: 100% 50%;
&:before { &:before {
transform: scale(.6); transform: scale(0.6);
display: inline-block; display: inline-block;
vertical-align: -3px; vertical-align: -3px;
} }

View File

@ -1,5 +1,5 @@
export { default as AppMain } from './AppMain' export { default as AppMain } from "./AppMain";
export { default as Navbar } from './Navbar' export { default as Navbar } from "./Navbar";
export { default as Settings } from './Settings' export { default as Settings } from "./Settings";
export { default as Sidebar } from './Sidebar/index.vue' export { default as Sidebar } from "./Sidebar/index.vue";
export { default as TagsView } from './TagsView/index.vue' export { default as TagsView } from "./TagsView/index.vue";

View File

@ -1,9 +1,13 @@
<template> <template>
<div :class="classObj" class="app-wrapper"> <div :class="classObj" class="app-wrapper">
<div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside" /> <div
v-if="device === 'mobile' && sidebar.opened"
class="drawer-bg"
@click="handleClickOutside"
/>
<sidebar class="sidebar-container" /> <sidebar class="sidebar-container" />
<div :class="{hasTagsView:needTagsView}" class="main-container"> <div :class="{ hasTagsView: needTagsView }" class="main-container">
<div :class="{'fixed-header':fixedHeader}"> <div :class="{ 'fixed-header': fixedHeader }">
<navbar /> <navbar />
<tags-view v-if="needTagsView" /> <tags-view v-if="needTagsView" />
</div> </div>
@ -16,13 +20,13 @@
</template> </template>
<script> <script>
import RightPanel from '@/components/RightPanel' import RightPanel from "@/components/RightPanel";
import { AppMain, Navbar, Settings, Sidebar, TagsView } from './components' import { AppMain, Navbar, Settings, Sidebar, TagsView } from "./components";
import ResizeMixin from './mixin/ResizeHandler' import ResizeMixin from "./mixin/ResizeHandler";
import { mapState } from 'vuex' import { mapState } from "vuex";
export default { export default {
name: 'Layout', name: "Layout",
components: { components: {
AppMain, AppMain,
Navbar, Navbar,
@ -45,23 +49,23 @@ export default {
hideSidebar: !this.sidebar.opened, hideSidebar: !this.sidebar.opened,
openSidebar: this.sidebar.opened, openSidebar: this.sidebar.opened,
withoutAnimation: this.sidebar.withoutAnimation, withoutAnimation: this.sidebar.withoutAnimation,
mobile: this.device === 'mobile' mobile: this.device === "mobile"
} };
} }
}, },
methods: { methods: {
handleClickOutside() { handleClickOutside() {
this.$store.dispatch('app/closeSideBar', { withoutAnimation: false }) this.$store.dispatch("app/closeSideBar", { withoutAnimation: false });
} }
} }
} };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "~@/styles/mixin.scss"; @import "~@/styles/mixin.scss";
@import "~@/styles/variables.scss"; @import "~@/styles/variables.scss";
.app-wrapper { .app-wrapper {
@include clearfix; @include clearfix;
position: relative; position: relative;
height: 100%; height: 100%;
@ -71,9 +75,9 @@ export default {
position: fixed; position: fixed;
top: 0; top: 0;
} }
} }
.drawer-bg { .drawer-bg {
background: #000; background: #000;
opacity: 0.3; opacity: 0.3;
width: 100%; width: 100%;
@ -81,22 +85,22 @@ export default {
height: 100%; height: 100%;
position: absolute; position: absolute;
z-index: 999; z-index: 999;
} }
.fixed-header { .fixed-header {
position: fixed; position: fixed;
top: 0; top: 0;
right: 0; right: 0;
z-index: 9; z-index: 9;
width: calc(100% - #{$sideBarWidth}); width: calc(100% - #{$sideBarWidth});
transition: width 0.28s; transition: width 0.28s;
} }
.hideSidebar .fixed-header { .hideSidebar .fixed-header {
width: calc(100% - 54px) width: calc(100% - 54px);
} }
.mobile .fixed-header { .mobile .fixed-header {
width: 100%; width: 100%;
} }
</style> </style>

View File

@ -1,45 +1,45 @@
import store from '@/store' import store from "@/store";
const { body } = document const { body } = document;
const WIDTH = 992 // refer to Bootstrap's responsive design const WIDTH = 992; // refer to Bootstrap's responsive design
export default { export default {
watch: { watch: {
$route(route) { $route(route) {
if (this.device === 'mobile' && this.sidebar.opened) { if (this.device === "mobile" && this.sidebar.opened) {
store.dispatch('app/closeSideBar', { withoutAnimation: false }) store.dispatch("app/closeSideBar", { withoutAnimation: false });
} }
} }
}, },
beforeMount() { beforeMount() {
window.addEventListener('resize', this.$_resizeHandler) window.addEventListener("resize", this.$_resizeHandler);
}, },
beforeDestroy() { beforeDestroy() {
window.removeEventListener('resize', this.$_resizeHandler) window.removeEventListener("resize", this.$_resizeHandler);
}, },
mounted() { mounted() {
const isMobile = this.$_isMobile() const isMobile = this.$_isMobile();
if (isMobile) { if (isMobile) {
store.dispatch('app/toggleDevice', 'mobile') store.dispatch("app/toggleDevice", "mobile");
store.dispatch('app/closeSideBar', { withoutAnimation: true }) store.dispatch("app/closeSideBar", { withoutAnimation: true });
} }
}, },
methods: { methods: {
// use $_ for mixins properties // use $_ for mixins properties
// https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
$_isMobile() { $_isMobile() {
const rect = body.getBoundingClientRect() const rect = body.getBoundingClientRect();
return rect.width - 1 < WIDTH return rect.width - 1 < WIDTH;
}, },
$_resizeHandler() { $_resizeHandler() {
if (!document.hidden) { if (!document.hidden) {
const isMobile = this.$_isMobile() const isMobile = this.$_isMobile();
store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop') store.dispatch("app/toggleDevice", isMobile ? "mobile" : "desktop");
if (isMobile) { if (isMobile) {
store.dispatch('app/closeSideBar', { withoutAnimation: true }) store.dispatch("app/closeSideBar", { withoutAnimation: true });
} }
} }
} }
} }
} };

View File

@ -1,23 +1,23 @@
import Vue from 'vue' import Vue from "vue";
import Cookies from 'js-cookie' import Cookies from "js-cookie";
import 'normalize.css/normalize.css' // a modern alternative to CSS resets import "normalize.css/normalize.css"; // a modern alternative to CSS resets
import Element from 'element-ui' import Element from "element-ui";
import './styles/element-variables.scss' import "./styles/element-variables.scss";
import '@/styles/index.scss' // global css import "@/styles/index.scss"; // global css
import App from './App' import App from "./App";
import store from './store' import store from "./store";
import router from './router' import router from "./router";
import './icons' // icon import "./icons"; // icon
import './permission' // permission control import "./permission"; // permission control
import './utils/error-log' // error log import "./utils/error-log"; // error log
import * as filters from './filters' // global filters import * as filters from "./filters"; // global filters
/** /**
* If you don't want to use mock-server * If you don't want to use mock-server
@ -27,25 +27,25 @@ import * as filters from './filters' // global filters
* Currently MockJs will be used in the production environment, * Currently MockJs will be used in the production environment,
* please remove it before going online! ! ! * please remove it before going online! ! !
*/ */
import { mockXHR } from '../mock' import { mockXHR } from "../mock";
if (process.env.NODE_ENV === 'production') { if (process.env.NODE_ENV === "production") {
mockXHR() mockXHR();
} }
Vue.use(Element, { Vue.use(Element, {
size: Cookies.get('size') || 'medium' // set element-ui default size size: Cookies.get("size") || "medium" // set element-ui default size
}) });
// register global utility filters // register global utility filters
Object.keys(filters).forEach(key => { Object.keys(filters).forEach(key => {
Vue.filter(key, filters[key]) Vue.filter(key, filters[key]);
}) });
Vue.config.productionTip = false Vue.config.productionTip = false;
new Vue({ new Vue({
el: '#app', el: "#app",
router, router,
store, store,
render: h => h(App) render: h => h(App)
}) });

View File

@ -1,56 +1,59 @@
import router from './router' import router from "./router";
import store from './store' import store from "./store";
import { Message } from 'element-ui' import { Message } from "element-ui";
import NProgress from 'nprogress' // progress bar import NProgress from "nprogress"; // progress bar
import 'nprogress/nprogress.css' // progress bar style import "nprogress/nprogress.css"; // progress bar style
import { getToken } from '@/utils/auth' // get token from cookie import { getToken } from "@/utils/auth"; // get token from cookie
import getPageTitle from '@/utils/get-page-title' import getPageTitle from "@/utils/get-page-title";
NProgress.configure({ showSpinner: false }) // NProgress Configuration NProgress.configure({ showSpinner: false }); // NProgress Configuration
const whiteList = ['/login', '/auth-redirect'] // no redirect whitelist const whiteList = ["/login", "/auth-redirect"]; // no redirect whitelist
router.beforeEach(async(to, from, next) => { router.beforeEach(async (to, from, next) => {
// start progress bar // start progress bar
NProgress.start() NProgress.start();
// set page title // set page title
document.title = getPageTitle(to.meta.title) document.title = getPageTitle(to.meta.title);
// determine whether the user has logged in // determine whether the user has logged in
const hasToken = getToken() const hasToken = getToken();
if (hasToken) { if (hasToken) {
if (to.path === '/login') { if (to.path === "/login") {
// if is logged in, redirect to the home page // if is logged in, redirect to the home page
next({ path: '/' }) next({ path: "/" });
NProgress.done() NProgress.done();
} else { } else {
// determine whether the user has obtained his permission roles through getInfo // determine whether the user has obtained his permission roles through getInfo
const hasRoles = store.getters.roles && store.getters.roles.length > 0 const hasRoles = store.getters.roles && store.getters.roles.length > 0;
if (hasRoles) { if (hasRoles) {
next() next();
} else { } else {
try { try {
// get user info // get user info
// note: roles must be a object array! such as: ['admin'] or ,['developer','editor'] // note: roles must be a object array! such as: ['admin'] or ,['developer','editor']
const { roles } = await store.dispatch('user/getInfo') const { roles } = await store.dispatch("user/getInfo");
// generate accessible routes map based on roles // generate accessible routes map based on roles
const accessRoutes = await store.dispatch('permission/generateRoutes', roles) const accessRoutes = await store.dispatch(
"permission/generateRoutes",
roles
);
// dynamically add accessible routes // dynamically add accessible routes
router.addRoutes(accessRoutes) router.addRoutes(accessRoutes);
// hack method to ensure that addRoutes is complete // hack method to ensure that addRoutes is complete
// set the replace: true, so the navigation will not leave a history record // set the replace: true, so the navigation will not leave a history record
next({ ...to, replace: true }) next({ ...to, replace: true });
} catch (error) { } catch (error) {
// remove token and go to login page to re-login // remove token and go to login page to re-login
await store.dispatch('user/resetToken') await store.dispatch("user/resetToken");
Message.error(error || 'Has Error') Message.error(error || "Has Error");
next(`/login?redirect=${to.path}`) next(`/login?redirect=${to.path}`);
NProgress.done() NProgress.done();
} }
} }
} }
@ -59,16 +62,16 @@ router.beforeEach(async(to, from, next) => {
if (whiteList.indexOf(to.path) !== -1) { if (whiteList.indexOf(to.path) !== -1) {
// in the free login whitelist, go directly // in the free login whitelist, go directly
next() next();
} else { } else {
// other pages that do not have permission to access are redirected to the login page. // other pages that do not have permission to access are redirected to the login page.
next(`/login?redirect=${to.path}`) next(`/login?redirect=${to.path}`);
NProgress.done() NProgress.done();
} }
} }
}) });
router.afterEach(() => { router.afterEach(() => {
// finish progress bar // finish progress bar
NProgress.done() NProgress.done();
}) });

View File

@ -1,16 +1,16 @@
import Vue from 'vue' import Vue from "vue";
import Router from 'vue-router' import Router from "vue-router";
Vue.use(Router) Vue.use(Router);
/* Layout */ /* Layout */
import Layout from '@/layout' import Layout from "@/layout";
/* Router Modules */ /* Router Modules */
import componentsRouter from './modules/components' import componentsRouter from "./modules/components";
import chartsRouter from './modules/charts' import chartsRouter from "./modules/charts";
import tableRouter from './modules/table' import tableRouter from "./modules/table";
import nestedRouter from './modules/nested' import nestedRouter from "./modules/nested";
/** /**
* Note: sub-menu only appear when route children.length >= 1 * Note: sub-menu only appear when route children.length >= 1
@ -40,89 +40,89 @@ import nestedRouter from './modules/nested'
*/ */
export const constantRoutes = [ export const constantRoutes = [
{ {
path: '/redirect', path: "/redirect",
component: Layout, component: Layout,
hidden: true, hidden: true,
children: [ children: [
{ {
path: '/redirect/:path*', path: "/redirect/:path*",
component: () => import('@/views/redirect/index') component: () => import("@/views/redirect/index")
} }
] ]
}, },
{ {
path: '/login', path: "/login",
component: () => import('@/views/login/index'), component: () => import("@/views/login/index"),
hidden: true hidden: true
}, },
{ {
path: '/auth-redirect', path: "/auth-redirect",
component: () => import('@/views/login/auth-redirect'), component: () => import("@/views/login/auth-redirect"),
hidden: true hidden: true
}, },
{ {
path: '/404', path: "/404",
component: () => import('@/views/error-page/404'), component: () => import("@/views/error-page/404"),
hidden: true hidden: true
}, },
{ {
path: '/401', path: "/401",
component: () => import('@/views/error-page/401'), component: () => import("@/views/error-page/401"),
hidden: true hidden: true
}, },
{ {
path: '/', path: "/",
component: Layout, component: Layout,
redirect: '/dashboard', redirect: "/dashboard",
children: [ children: [
{ {
path: 'dashboard', path: "dashboard",
component: () => import('@/views/dashboard/index'), component: () => import("@/views/dashboard/index"),
name: 'Dashboard', name: "Dashboard",
meta: { title: 'Dashboard', icon: 'dashboard', affix: true } meta: { title: "Dashboard", icon: "dashboard", affix: true }
} }
] ]
}, },
{ {
path: '/documentation', path: "/documentation",
component: Layout, component: Layout,
children: [ children: [
{ {
path: 'index', path: "index",
component: () => import('@/views/documentation/index'), component: () => import("@/views/documentation/index"),
name: 'Documentation', name: "Documentation",
meta: { title: 'Documentation', icon: 'documentation', affix: true } meta: { title: "Documentation", icon: "documentation", affix: true }
} }
] ]
}, },
{ {
path: '/guide', path: "/guide",
component: Layout, component: Layout,
redirect: '/guide/index', redirect: "/guide/index",
children: [ children: [
{ {
path: 'index', path: "index",
component: () => import('@/views/guide/index'), component: () => import("@/views/guide/index"),
name: 'Guide', name: "Guide",
meta: { title: 'Guide', icon: 'guide', noCache: true } meta: { title: "Guide", icon: "guide", noCache: true }
} }
] ]
}, },
{ {
path: '/profile', path: "/profile",
component: Layout, component: Layout,
redirect: '/profile/index', redirect: "/profile/index",
hidden: true, hidden: true,
children: [ children: [
{ {
path: 'index', path: "index",
component: () => import('@/views/profile/index'), component: () => import("@/views/profile/index"),
name: 'Profile', name: "Profile",
meta: { title: 'Profile', icon: 'user', noCache: true } meta: { title: "Profile", icon: "user", noCache: true }
} }
] ]
} }
] ];
/** /**
* asyncRoutes * asyncRoutes
@ -130,56 +130,56 @@ export const constantRoutes = [
*/ */
export const asyncRoutes = [ export const asyncRoutes = [
{ {
path: '/permission', path: "/permission",
component: Layout, component: Layout,
redirect: '/permission/page', redirect: "/permission/page",
alwaysShow: true, // will always show the root menu alwaysShow: true, // will always show the root menu
name: 'Permission', name: "Permission",
meta: { meta: {
title: 'Permission', title: "Permission",
icon: 'lock', icon: "lock",
roles: ['admin', 'editor'] // you can set roles in root nav roles: ["admin", "editor"] // you can set roles in root nav
}, },
children: [ children: [
{ {
path: 'page', path: "page",
component: () => import('@/views/permission/page'), component: () => import("@/views/permission/page"),
name: 'PagePermission', name: "PagePermission",
meta: { meta: {
title: 'Page Permission', title: "Page Permission",
roles: ['admin'] // or you can only set roles in sub nav roles: ["admin"] // or you can only set roles in sub nav
} }
}, },
{ {
path: 'directive', path: "directive",
component: () => import('@/views/permission/directive'), component: () => import("@/views/permission/directive"),
name: 'DirectivePermission', name: "DirectivePermission",
meta: { meta: {
title: 'Directive Permission' title: "Directive Permission"
// if do not set roles, means: this page does not require permission // if do not set roles, means: this page does not require permission
} }
}, },
{ {
path: 'role', path: "role",
component: () => import('@/views/permission/role'), component: () => import("@/views/permission/role"),
name: 'RolePermission', name: "RolePermission",
meta: { meta: {
title: 'Role Permission', title: "Role Permission",
roles: ['admin'] roles: ["admin"]
} }
} }
] ]
}, },
{ {
path: '/icon', path: "/icon",
component: Layout, component: Layout,
children: [ children: [
{ {
path: 'index', path: "index",
component: () => import('@/views/icons/index'), component: () => import("@/views/icons/index"),
name: 'Icons', name: "Icons",
meta: { title: 'Icons', icon: 'icon', noCache: true } meta: { title: "Icons", icon: "icon", noCache: true }
} }
] ]
}, },
@ -191,214 +191,219 @@ export const asyncRoutes = [
tableRouter, tableRouter,
{ {
path: '/example', path: "/example",
component: Layout, component: Layout,
redirect: '/example/list', redirect: "/example/list",
name: 'Example', name: "Example",
meta: { meta: {
title: 'Example', title: "Example",
icon: 'example' icon: "example"
}, },
children: [ children: [
{ {
path: 'create', path: "create",
component: () => import('@/views/example/create'), component: () => import("@/views/example/create"),
name: 'CreateArticle', name: "CreateArticle",
meta: { title: 'Create Article', icon: 'edit' } meta: { title: "Create Article", icon: "edit" }
}, },
{ {
path: 'edit/:id(\\d+)', path: "edit/:id(\\d+)",
component: () => import('@/views/example/edit'), component: () => import("@/views/example/edit"),
name: 'EditArticle', name: "EditArticle",
meta: { title: 'Edit Article', noCache: true, activeMenu: '/example/list' }, meta: {
title: "Edit Article",
noCache: true,
activeMenu: "/example/list"
},
hidden: true hidden: true
}, },
{ {
path: 'list', path: "list",
component: () => import('@/views/example/list'), component: () => import("@/views/example/list"),
name: 'ArticleList', name: "ArticleList",
meta: { title: 'Article List', icon: 'list' } meta: { title: "Article List", icon: "list" }
} }
] ]
}, },
{ {
path: '/tab', path: "/tab",
component: Layout, component: Layout,
children: [ children: [
{ {
path: 'index', path: "index",
component: () => import('@/views/tab/index'), component: () => import("@/views/tab/index"),
name: 'Tab', name: "Tab",
meta: { title: 'Tab', icon: 'tab' } meta: { title: "Tab", icon: "tab" }
} }
] ]
}, },
{ {
path: '/error', path: "/error",
component: Layout, component: Layout,
redirect: 'noRedirect', redirect: "noRedirect",
name: 'ErrorPages', name: "ErrorPages",
meta: { meta: {
title: 'Error Pages', title: "Error Pages",
icon: '404' icon: "404"
}, },
children: [ children: [
{ {
path: '401', path: "401",
component: () => import('@/views/error-page/401'), component: () => import("@/views/error-page/401"),
name: 'Page401', name: "Page401",
meta: { title: '401', noCache: true } meta: { title: "401", noCache: true }
}, },
{ {
path: '404', path: "404",
component: () => import('@/views/error-page/404'), component: () => import("@/views/error-page/404"),
name: 'Page404', name: "Page404",
meta: { title: '404', noCache: true } meta: { title: "404", noCache: true }
} }
] ]
}, },
{ {
path: '/error-log', path: "/error-log",
component: Layout, component: Layout,
children: [ children: [
{ {
path: 'log', path: "log",
component: () => import('@/views/error-log/index'), component: () => import("@/views/error-log/index"),
name: 'ErrorLog', name: "ErrorLog",
meta: { title: 'Error Log', icon: 'bug' } meta: { title: "Error Log", icon: "bug" }
} }
] ]
}, },
{ {
path: '/excel', path: "/excel",
component: Layout, component: Layout,
redirect: '/excel/export-excel', redirect: "/excel/export-excel",
name: 'Excel', name: "Excel",
meta: { meta: {
title: 'Excel', title: "Excel",
icon: 'excel' icon: "excel"
}, },
children: [ children: [
{ {
path: 'export-excel', path: "export-excel",
component: () => import('@/views/excel/export-excel'), component: () => import("@/views/excel/export-excel"),
name: 'ExportExcel', name: "ExportExcel",
meta: { title: 'Export Excel' } meta: { title: "Export Excel" }
}, },
{ {
path: 'export-selected-excel', path: "export-selected-excel",
component: () => import('@/views/excel/select-excel'), component: () => import("@/views/excel/select-excel"),
name: 'SelectExcel', name: "SelectExcel",
meta: { title: 'Export Selected' } meta: { title: "Export Selected" }
}, },
{ {
path: 'export-merge-header', path: "export-merge-header",
component: () => import('@/views/excel/merge-header'), component: () => import("@/views/excel/merge-header"),
name: 'MergeHeader', name: "MergeHeader",
meta: { title: 'Merge Header' } meta: { title: "Merge Header" }
}, },
{ {
path: 'upload-excel', path: "upload-excel",
component: () => import('@/views/excel/upload-excel'), component: () => import("@/views/excel/upload-excel"),
name: 'UploadExcel', name: "UploadExcel",
meta: { title: 'Upload Excel' } meta: { title: "Upload Excel" }
} }
] ]
}, },
{ {
path: '/zip', path: "/zip",
component: Layout, component: Layout,
redirect: '/zip/download', redirect: "/zip/download",
alwaysShow: true, alwaysShow: true,
name: 'Zip', name: "Zip",
meta: { title: 'Zip', icon: 'zip' }, meta: { title: "Zip", icon: "zip" },
children: [ children: [
{ {
path: 'download', path: "download",
component: () => import('@/views/zip/index'), component: () => import("@/views/zip/index"),
name: 'ExportZip', name: "ExportZip",
meta: { title: 'Export Zip' } meta: { title: "Export Zip" }
} }
] ]
}, },
{ {
path: '/pdf', path: "/pdf",
component: Layout, component: Layout,
redirect: '/pdf/index', redirect: "/pdf/index",
children: [ children: [
{ {
path: 'index', path: "index",
component: () => import('@/views/pdf/index'), component: () => import("@/views/pdf/index"),
name: 'PDF', name: "PDF",
meta: { title: 'PDF', icon: 'pdf' } meta: { title: "PDF", icon: "pdf" }
} }
] ]
}, },
{ {
path: '/pdf/download', path: "/pdf/download",
component: () => import('@/views/pdf/download'), component: () => import("@/views/pdf/download"),
hidden: true hidden: true
}, },
{ {
path: '/theme', path: "/theme",
component: Layout, component: Layout,
children: [ children: [
{ {
path: 'index', path: "index",
component: () => import('@/views/theme/index'), component: () => import("@/views/theme/index"),
name: 'Theme', name: "Theme",
meta: { title: 'Theme', icon: 'theme' } meta: { title: "Theme", icon: "theme" }
} }
] ]
}, },
{ {
path: '/clipboard', path: "/clipboard",
component: Layout, component: Layout,
children: [ children: [
{ {
path: 'index', path: "index",
component: () => import('@/views/clipboard/index'), component: () => import("@/views/clipboard/index"),
name: 'ClipboardDemo', name: "ClipboardDemo",
meta: { title: 'Clipboard', icon: 'clipboard' } meta: { title: "Clipboard", icon: "clipboard" }
} }
] ]
}, },
{ {
path: 'external-link', path: "external-link",
component: Layout, component: Layout,
children: [ children: [
{ {
path: 'https://github.com/PanJiaChen/vue-element-admin', path: "https://github.com/PanJiaChen/vue-element-admin",
meta: { title: 'External Link', icon: 'link' } meta: { title: "External Link", icon: "link" }
} }
] ]
}, },
// 404 page must be placed at the end !!! // 404 page must be placed at the end !!!
{ path: '*', redirect: '/404', hidden: true } { path: "*", redirect: "/404", hidden: true }
] ];
const createRouter = () => new Router({ const createRouter = () =>
new Router({
// mode: 'history', // require service support // mode: 'history', // require service support
scrollBehavior: () => ({ y: 0 }), scrollBehavior: () => ({ y: 0 }),
routes: constantRoutes routes: constantRoutes
}) });
const router = createRouter() const router = createRouter();
// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465 // Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() { export function resetRouter() {
const newRouter = createRouter() const newRouter = createRouter();
router.matcher = newRouter.matcher // reset router router.matcher = newRouter.matcher; // reset router
} }
export default router export default router;

View File

@ -1,36 +1,36 @@
/** When your routing table is too long, you can split it into small modules**/ /** When your routing table is too long, you can split it into small modules**/
import Layout from '@/layout' import Layout from "@/layout";
const chartsRouter = { const chartsRouter = {
path: '/charts', path: "/charts",
component: Layout, component: Layout,
redirect: 'noRedirect', redirect: "noRedirect",
name: 'Charts', name: "Charts",
meta: { meta: {
title: 'Charts', title: "Charts",
icon: 'chart' icon: "chart"
}, },
children: [ children: [
{ {
path: 'keyboard', path: "keyboard",
component: () => import('@/views/charts/keyboard'), component: () => import("@/views/charts/keyboard"),
name: 'KeyboardChart', name: "KeyboardChart",
meta: { title: 'Keyboard Chart', noCache: true } meta: { title: "Keyboard Chart", noCache: true }
}, },
{ {
path: 'line', path: "line",
component: () => import('@/views/charts/line'), component: () => import("@/views/charts/line"),
name: 'LineChart', name: "LineChart",
meta: { title: 'Line Chart', noCache: true } meta: { title: "Line Chart", noCache: true }
}, },
{ {
path: 'mix-chart', path: "mix-chart",
component: () => import('@/views/charts/mix-chart'), component: () => import("@/views/charts/mix-chart"),
name: 'MixChart', name: "MixChart",
meta: { title: 'Mix Chart', noCache: true } meta: { title: "Mix Chart", noCache: true }
} }
] ]
} };
export default chartsRouter export default chartsRouter;

View File

@ -1,102 +1,102 @@
/** When your routing table is too long, you can split it into small modules **/ /** When your routing table is too long, you can split it into small modules **/
import Layout from '@/layout' import Layout from "@/layout";
const componentsRouter = { const componentsRouter = {
path: '/components', path: "/components",
component: Layout, component: Layout,
redirect: 'noRedirect', redirect: "noRedirect",
name: 'ComponentDemo', name: "ComponentDemo",
meta: { meta: {
title: 'Components', title: "Components",
icon: 'component' icon: "component"
}, },
children: [ children: [
{ {
path: 'tinymce', path: "tinymce",
component: () => import('@/views/components-demo/tinymce'), component: () => import("@/views/components-demo/tinymce"),
name: 'TinymceDemo', name: "TinymceDemo",
meta: { title: 'Tinymce' } meta: { title: "Tinymce" }
}, },
{ {
path: 'markdown', path: "markdown",
component: () => import('@/views/components-demo/markdown'), component: () => import("@/views/components-demo/markdown"),
name: 'MarkdownDemo', name: "MarkdownDemo",
meta: { title: 'Markdown' } meta: { title: "Markdown" }
}, },
{ {
path: 'json-editor', path: "json-editor",
component: () => import('@/views/components-demo/json-editor'), component: () => import("@/views/components-demo/json-editor"),
name: 'JsonEditorDemo', name: "JsonEditorDemo",
meta: { title: 'JSON Editor' } meta: { title: "JSON Editor" }
}, },
{ {
path: 'split-pane', path: "split-pane",
component: () => import('@/views/components-demo/split-pane'), component: () => import("@/views/components-demo/split-pane"),
name: 'SplitpaneDemo', name: "SplitpaneDemo",
meta: { title: 'SplitPane' } meta: { title: "SplitPane" }
}, },
{ {
path: 'avatar-upload', path: "avatar-upload",
component: () => import('@/views/components-demo/avatar-upload'), component: () => import("@/views/components-demo/avatar-upload"),
name: 'AvatarUploadDemo', name: "AvatarUploadDemo",
meta: { title: 'Upload' } meta: { title: "Upload" }
}, },
{ {
path: 'dropzone', path: "dropzone",
component: () => import('@/views/components-demo/dropzone'), component: () => import("@/views/components-demo/dropzone"),
name: 'DropzoneDemo', name: "DropzoneDemo",
meta: { title: 'Dropzone' } meta: { title: "Dropzone" }
}, },
{ {
path: 'sticky', path: "sticky",
component: () => import('@/views/components-demo/sticky'), component: () => import("@/views/components-demo/sticky"),
name: 'StickyDemo', name: "StickyDemo",
meta: { title: 'Sticky' } meta: { title: "Sticky" }
}, },
{ {
path: 'count-to', path: "count-to",
component: () => import('@/views/components-demo/count-to'), component: () => import("@/views/components-demo/count-to"),
name: 'CountToDemo', name: "CountToDemo",
meta: { title: 'Count To' } meta: { title: "Count To" }
}, },
{ {
path: 'mixin', path: "mixin",
component: () => import('@/views/components-demo/mixin'), component: () => import("@/views/components-demo/mixin"),
name: 'ComponentMixinDemo', name: "ComponentMixinDemo",
meta: { title: 'Component Mixin' } meta: { title: "Component Mixin" }
}, },
{ {
path: 'back-to-top', path: "back-to-top",
component: () => import('@/views/components-demo/back-to-top'), component: () => import("@/views/components-demo/back-to-top"),
name: 'BackToTopDemo', name: "BackToTopDemo",
meta: { title: 'Back To Top' } meta: { title: "Back To Top" }
}, },
{ {
path: 'drag-dialog', path: "drag-dialog",
component: () => import('@/views/components-demo/drag-dialog'), component: () => import("@/views/components-demo/drag-dialog"),
name: 'DragDialogDemo', name: "DragDialogDemo",
meta: { title: 'Drag Dialog' } meta: { title: "Drag Dialog" }
}, },
{ {
path: 'drag-select', path: "drag-select",
component: () => import('@/views/components-demo/drag-select'), component: () => import("@/views/components-demo/drag-select"),
name: 'DragSelectDemo', name: "DragSelectDemo",
meta: { title: 'Drag Select' } meta: { title: "Drag Select" }
}, },
{ {
path: 'dnd-list', path: "dnd-list",
component: () => import('@/views/components-demo/dnd-list'), component: () => import("@/views/components-demo/dnd-list"),
name: 'DndListDemo', name: "DndListDemo",
meta: { title: 'Dnd List' } meta: { title: "Dnd List" }
}, },
{ {
path: 'drag-kanban', path: "drag-kanban",
component: () => import('@/views/components-demo/drag-kanban'), component: () => import("@/views/components-demo/drag-kanban"),
name: 'DragKanbanDemo', name: "DragKanbanDemo",
meta: { title: 'Drag Kanban' } meta: { title: "Drag Kanban" }
} }
] ]
} };
export default componentsRouter export default componentsRouter;

View File

@ -1,66 +1,66 @@
/** When your routing table is too long, you can split it into small modules **/ /** When your routing table is too long, you can split it into small modules **/
import Layout from '@/layout' import Layout from "@/layout";
const nestedRouter = { const nestedRouter = {
path: '/nested', path: "/nested",
component: Layout, component: Layout,
redirect: '/nested/menu1/menu1-1', redirect: "/nested/menu1/menu1-1",
name: 'Nested', name: "Nested",
meta: { meta: {
title: 'Nested Routes', title: "Nested Routes",
icon: 'nested' icon: "nested"
}, },
children: [ children: [
{ {
path: 'menu1', path: "menu1",
component: () => import('@/views/nested/menu1/index'), // Parent router-view component: () => import("@/views/nested/menu1/index"), // Parent router-view
name: 'Menu1', name: "Menu1",
meta: { title: 'Menu 1' }, meta: { title: "Menu 1" },
redirect: '/nested/menu1/menu1-1', redirect: "/nested/menu1/menu1-1",
children: [ children: [
{ {
path: 'menu1-1', path: "menu1-1",
component: () => import('@/views/nested/menu1/menu1-1'), component: () => import("@/views/nested/menu1/menu1-1"),
name: 'Menu1-1', name: "Menu1-1",
meta: { title: 'Menu 1-1' } meta: { title: "Menu 1-1" }
}, },
{ {
path: 'menu1-2', path: "menu1-2",
component: () => import('@/views/nested/menu1/menu1-2'), component: () => import("@/views/nested/menu1/menu1-2"),
name: 'Menu1-2', name: "Menu1-2",
redirect: '/nested/menu1/menu1-2/menu1-2-1', redirect: "/nested/menu1/menu1-2/menu1-2-1",
meta: { title: 'Menu 1-2' }, meta: { title: "Menu 1-2" },
children: [ children: [
{ {
path: 'menu1-2-1', path: "menu1-2-1",
component: () => import('@/views/nested/menu1/menu1-2/menu1-2-1'), component: () => import("@/views/nested/menu1/menu1-2/menu1-2-1"),
name: 'Menu1-2-1', name: "Menu1-2-1",
meta: { title: 'Menu 1-2-1' } meta: { title: "Menu 1-2-1" }
}, },
{ {
path: 'menu1-2-2', path: "menu1-2-2",
component: () => import('@/views/nested/menu1/menu1-2/menu1-2-2'), component: () => import("@/views/nested/menu1/menu1-2/menu1-2-2"),
name: 'Menu1-2-2', name: "Menu1-2-2",
meta: { title: 'Menu 1-2-2' } meta: { title: "Menu 1-2-2" }
} }
] ]
}, },
{ {
path: 'menu1-3', path: "menu1-3",
component: () => import('@/views/nested/menu1/menu1-3'), component: () => import("@/views/nested/menu1/menu1-3"),
name: 'Menu1-3', name: "Menu1-3",
meta: { title: 'Menu 1-3' } meta: { title: "Menu 1-3" }
} }
] ]
}, },
{ {
path: 'menu2', path: "menu2",
name: 'Menu2', name: "Menu2",
component: () => import('@/views/nested/menu2/index'), component: () => import("@/views/nested/menu2/index"),
meta: { title: 'Menu 2' } meta: { title: "Menu 2" }
} }
] ]
} };
export default nestedRouter export default nestedRouter;

View File

@ -1,41 +1,41 @@
/** When your routing table is too long, you can split it into small modules **/ /** When your routing table is too long, you can split it into small modules **/
import Layout from '@/layout' import Layout from "@/layout";
const tableRouter = { const tableRouter = {
path: '/table', path: "/table",
component: Layout, component: Layout,
redirect: '/table/complex-table', redirect: "/table/complex-table",
name: 'Table', name: "Table",
meta: { meta: {
title: 'Table', title: "Table",
icon: 'table' icon: "table"
}, },
children: [ children: [
{ {
path: 'dynamic-table', path: "dynamic-table",
component: () => import('@/views/table/dynamic-table/index'), component: () => import("@/views/table/dynamic-table/index"),
name: 'DynamicTable', name: "DynamicTable",
meta: { title: 'Dynamic Table' } meta: { title: "Dynamic Table" }
}, },
{ {
path: 'drag-table', path: "drag-table",
component: () => import('@/views/table/drag-table'), component: () => import("@/views/table/drag-table"),
name: 'DragTable', name: "DragTable",
meta: { title: 'Drag Table' } meta: { title: "Drag Table" }
}, },
{ {
path: 'inline-edit-table', path: "inline-edit-table",
component: () => import('@/views/table/inline-edit-table'), component: () => import("@/views/table/inline-edit-table"),
name: 'InlineEditTable', name: "InlineEditTable",
meta: { title: 'Inline Edit' } meta: { title: "Inline Edit" }
}, },
{ {
path: 'complex-table', path: "complex-table",
component: () => import('@/views/table/complex-table'), component: () => import("@/views/table/complex-table"),
name: 'ComplexTable', name: "ComplexTable",
meta: { title: 'Complex Table' } meta: { title: "Complex Table" }
} }
] ]
} };
export default tableRouter export default tableRouter;

View File

@ -1,5 +1,5 @@
module.exports = { module.exports = {
title: 'Vue Element Admin', title: "Vue Element Admin",
/** /**
* @type {boolean} true | false * @type {boolean} true | false
@ -31,5 +31,5 @@ module.exports = {
* The default is only used in the production env * The default is only used in the production env
* If you want to also use it in dev, you can pass ['production', 'development'] * If you want to also use it in dev, you can pass ['production', 'development']
*/ */
errorLog: 'production' errorLog: "production"
} };

View File

@ -11,5 +11,5 @@ const getters = {
roles: state => state.user.roles, roles: state => state.user.roles,
permission_routes: state => state.permission.routes, permission_routes: state => state.permission.routes,
errorLogs: state => state.errorLog.logs errorLogs: state => state.errorLog.logs
} };
export default getters export default getters;

View File

@ -1,25 +1,25 @@
import Vue from 'vue' import Vue from "vue";
import Vuex from 'vuex' import Vuex from "vuex";
import getters from './getters' import getters from "./getters";
Vue.use(Vuex) Vue.use(Vuex);
// https://webpack.js.org/guides/dependency-management/#requirecontext // https://webpack.js.org/guides/dependency-management/#requirecontext
const modulesFiles = require.context('./modules', true, /\.js$/) const modulesFiles = require.context("./modules", true, /\.js$/);
// you do not need `import app from './modules/app'` // you do not need `import app from './modules/app'`
// it will auto require all vuex module from modules file // it will auto require all vuex module from modules file
const modules = modulesFiles.keys().reduce((modules, modulePath) => { const modules = modulesFiles.keys().reduce((modules, modulePath) => {
// set './app.js' => 'app' // set './app.js' => 'app'
const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1') const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, "$1");
const value = modulesFiles(modulePath) const value = modulesFiles(modulePath);
modules[moduleName] = value.default modules[moduleName] = value.default;
return modules return modules;
}, {}) }, {});
const store = new Vuex.Store({ const store = new Vuex.Store({
modules, modules,
getters getters
}) });
export default store export default store;

View File

@ -1,56 +1,58 @@
import Cookies from 'js-cookie' import Cookies from "js-cookie";
const state = { const state = {
sidebar: { sidebar: {
opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true, opened: Cookies.get("sidebarStatus")
? !!+Cookies.get("sidebarStatus")
: true,
withoutAnimation: false withoutAnimation: false
}, },
device: 'desktop', device: "desktop",
size: Cookies.get('size') || 'medium' size: Cookies.get("size") || "medium"
} };
const mutations = { const mutations = {
TOGGLE_SIDEBAR: state => { TOGGLE_SIDEBAR: state => {
state.sidebar.opened = !state.sidebar.opened state.sidebar.opened = !state.sidebar.opened;
state.sidebar.withoutAnimation = false state.sidebar.withoutAnimation = false;
if (state.sidebar.opened) { if (state.sidebar.opened) {
Cookies.set('sidebarStatus', 1) Cookies.set("sidebarStatus", 1);
} else { } else {
Cookies.set('sidebarStatus', 0) Cookies.set("sidebarStatus", 0);
} }
}, },
CLOSE_SIDEBAR: (state, withoutAnimation) => { CLOSE_SIDEBAR: (state, withoutAnimation) => {
Cookies.set('sidebarStatus', 0) Cookies.set("sidebarStatus", 0);
state.sidebar.opened = false state.sidebar.opened = false;
state.sidebar.withoutAnimation = withoutAnimation state.sidebar.withoutAnimation = withoutAnimation;
}, },
TOGGLE_DEVICE: (state, device) => { TOGGLE_DEVICE: (state, device) => {
state.device = device state.device = device;
}, },
SET_SIZE: (state, size) => { SET_SIZE: (state, size) => {
state.size = size state.size = size;
Cookies.set('size', size) Cookies.set("size", size);
} }
} };
const actions = { const actions = {
toggleSideBar({ commit }) { toggleSideBar({ commit }) {
commit('TOGGLE_SIDEBAR') commit("TOGGLE_SIDEBAR");
}, },
closeSideBar({ commit }, { withoutAnimation }) { closeSideBar({ commit }, { withoutAnimation }) {
commit('CLOSE_SIDEBAR', withoutAnimation) commit("CLOSE_SIDEBAR", withoutAnimation);
}, },
toggleDevice({ commit }, device) { toggleDevice({ commit }, device) {
commit('TOGGLE_DEVICE', device) commit("TOGGLE_DEVICE", device);
}, },
setSize({ commit }, size) { setSize({ commit }, size) {
commit('SET_SIZE', size) commit("SET_SIZE", size);
} }
} };
export default { export default {
namespaced: true, namespaced: true,
state, state,
mutations, mutations,
actions actions
} };

View File

@ -1,28 +1,28 @@
const state = { const state = {
logs: [] logs: []
} };
const mutations = { const mutations = {
ADD_ERROR_LOG: (state, log) => { ADD_ERROR_LOG: (state, log) => {
state.logs.push(log) state.logs.push(log);
}, },
CLEAR_ERROR_LOG: (state) => { CLEAR_ERROR_LOG: state => {
state.logs.splice(0) state.logs.splice(0);
} }
} };
const actions = { const actions = {
addErrorLog({ commit }, log) { addErrorLog({ commit }, log) {
commit('ADD_ERROR_LOG', log) commit("ADD_ERROR_LOG", log);
}, },
clearErrorLog({ commit }) { clearErrorLog({ commit }) {
commit('CLEAR_ERROR_LOG') commit("CLEAR_ERROR_LOG");
} }
} };
export default { export default {
namespaced: true, namespaced: true,
state, state,
mutations, mutations,
actions actions
} };

View File

@ -1,4 +1,4 @@
import { asyncRoutes, constantRoutes } from '@/router' import { asyncRoutes, constantRoutes } from "@/router";
/** /**
* Use meta.role to determine if the current user has permission * Use meta.role to determine if the current user has permission
@ -7,9 +7,9 @@ import { asyncRoutes, constantRoutes } from '@/router'
*/ */
function hasPermission(roles, route) { function hasPermission(roles, route) {
if (route.meta && route.meta.roles) { if (route.meta && route.meta.roles) {
return roles.some(role => route.meta.roles.includes(role)) return roles.some(role => route.meta.roles.includes(role));
} else { } else {
return true return true;
} }
} }
@ -19,51 +19,51 @@ function hasPermission(roles, route) {
* @param roles * @param roles
*/ */
export function filterAsyncRoutes(routes, roles) { export function filterAsyncRoutes(routes, roles) {
const res = [] const res = [];
routes.forEach(route => { routes.forEach(route => {
const tmp = { ...route } const tmp = { ...route };
if (hasPermission(roles, tmp)) { if (hasPermission(roles, tmp)) {
if (tmp.children) { if (tmp.children) {
tmp.children = filterAsyncRoutes(tmp.children, roles) tmp.children = filterAsyncRoutes(tmp.children, roles);
} }
res.push(tmp) res.push(tmp);
} }
}) });
return res return res;
} }
const state = { const state = {
routes: [], routes: [],
addRoutes: [] addRoutes: []
} };
const mutations = { const mutations = {
SET_ROUTES: (state, routes) => { SET_ROUTES: (state, routes) => {
state.addRoutes = routes state.addRoutes = routes;
state.routes = constantRoutes.concat(routes) state.routes = constantRoutes.concat(routes);
} }
} };
const actions = { const actions = {
generateRoutes({ commit }, roles) { generateRoutes({ commit }, roles) {
return new Promise(resolve => { return new Promise(resolve => {
let accessedRoutes let accessedRoutes;
if (roles.includes('admin')) { if (roles.includes("admin")) {
accessedRoutes = asyncRoutes || [] accessedRoutes = asyncRoutes || [];
} else { } else {
accessedRoutes = filterAsyncRoutes(asyncRoutes, roles) accessedRoutes = filterAsyncRoutes(asyncRoutes, roles);
} }
commit('SET_ROUTES', accessedRoutes) commit("SET_ROUTES", accessedRoutes);
resolve(accessedRoutes) resolve(accessedRoutes);
}) });
} }
} };
export default { export default {
namespaced: true, namespaced: true,
state, state,
mutations, mutations,
actions actions
} };

View File

@ -1,165 +1,165 @@
const state = { const state = {
visitedViews: [], visitedViews: [],
cachedViews: [] cachedViews: []
} };
const mutations = { const mutations = {
ADD_VISITED_VIEW: (state, view) => { ADD_VISITED_VIEW: (state, view) => {
if (state.visitedViews.some(v => v.path === view.path)) return if (state.visitedViews.some(v => v.path === view.path)) return;
state.visitedViews.push( state.visitedViews.push(
Object.assign({}, view, { Object.assign({}, view, {
title: view.meta.title || 'no-name' title: view.meta.title || "no-name"
}) })
) );
}, },
ADD_CACHED_VIEW: (state, view) => { ADD_CACHED_VIEW: (state, view) => {
if (state.cachedViews.includes(view.name)) return if (state.cachedViews.includes(view.name)) return;
if (!view.meta.noCache) { if (!view.meta.noCache) {
state.cachedViews.push(view.name) state.cachedViews.push(view.name);
} }
}, },
DEL_VISITED_VIEW: (state, view) => { DEL_VISITED_VIEW: (state, view) => {
for (const [i, v] of state.visitedViews.entries()) { for (const [i, v] of state.visitedViews.entries()) {
if (v.path === view.path) { if (v.path === view.path) {
state.visitedViews.splice(i, 1) state.visitedViews.splice(i, 1);
break break;
} }
} }
}, },
DEL_CACHED_VIEW: (state, view) => { DEL_CACHED_VIEW: (state, view) => {
for (const i of state.cachedViews) { for (const i of state.cachedViews) {
if (i === view.name) { if (i === view.name) {
const index = state.cachedViews.indexOf(i) const index = state.cachedViews.indexOf(i);
state.cachedViews.splice(index, 1) state.cachedViews.splice(index, 1);
break break;
} }
} }
}, },
DEL_OTHERS_VISITED_VIEWS: (state, view) => { DEL_OTHERS_VISITED_VIEWS: (state, view) => {
state.visitedViews = state.visitedViews.filter(v => { state.visitedViews = state.visitedViews.filter(v => {
return v.meta.affix || v.path === view.path return v.meta.affix || v.path === view.path;
}) });
}, },
DEL_OTHERS_CACHED_VIEWS: (state, view) => { DEL_OTHERS_CACHED_VIEWS: (state, view) => {
for (const i of state.cachedViews) { for (const i of state.cachedViews) {
if (i === view.name) { if (i === view.name) {
const index = state.cachedViews.indexOf(i) const index = state.cachedViews.indexOf(i);
state.cachedViews = state.cachedViews.slice(index, index + 1) state.cachedViews = state.cachedViews.slice(index, index + 1);
break break;
} }
} }
}, },
DEL_ALL_VISITED_VIEWS: state => { DEL_ALL_VISITED_VIEWS: state => {
// keep affix tags // keep affix tags
const affixTags = state.visitedViews.filter(tag => tag.meta.affix) const affixTags = state.visitedViews.filter(tag => tag.meta.affix);
state.visitedViews = affixTags state.visitedViews = affixTags;
}, },
DEL_ALL_CACHED_VIEWS: state => { DEL_ALL_CACHED_VIEWS: state => {
state.cachedViews = [] state.cachedViews = [];
}, },
UPDATE_VISITED_VIEW: (state, view) => { UPDATE_VISITED_VIEW: (state, view) => {
for (let v of state.visitedViews) { for (let v of state.visitedViews) {
if (v.path === view.path) { if (v.path === view.path) {
v = Object.assign(v, view) v = Object.assign(v, view);
break break;
} }
} }
} }
} };
const actions = { const actions = {
addView({ dispatch }, view) { addView({ dispatch }, view) {
dispatch('addVisitedView', view) dispatch("addVisitedView", view);
dispatch('addCachedView', view) dispatch("addCachedView", view);
}, },
addVisitedView({ commit }, view) { addVisitedView({ commit }, view) {
commit('ADD_VISITED_VIEW', view) commit("ADD_VISITED_VIEW", view);
}, },
addCachedView({ commit }, view) { addCachedView({ commit }, view) {
commit('ADD_CACHED_VIEW', view) commit("ADD_CACHED_VIEW", view);
}, },
delView({ dispatch, state }, view) { delView({ dispatch, state }, view) {
return new Promise(resolve => { return new Promise(resolve => {
dispatch('delVisitedView', view) dispatch("delVisitedView", view);
dispatch('delCachedView', view) dispatch("delCachedView", view);
resolve({ resolve({
visitedViews: [...state.visitedViews], visitedViews: [...state.visitedViews],
cachedViews: [...state.cachedViews] cachedViews: [...state.cachedViews]
}) });
}) });
}, },
delVisitedView({ commit, state }, view) { delVisitedView({ commit, state }, view) {
return new Promise(resolve => { return new Promise(resolve => {
commit('DEL_VISITED_VIEW', view) commit("DEL_VISITED_VIEW", view);
resolve([...state.visitedViews]) resolve([...state.visitedViews]);
}) });
}, },
delCachedView({ commit, state }, view) { delCachedView({ commit, state }, view) {
return new Promise(resolve => { return new Promise(resolve => {
commit('DEL_CACHED_VIEW', view) commit("DEL_CACHED_VIEW", view);
resolve([...state.cachedViews]) resolve([...state.cachedViews]);
}) });
}, },
delOthersViews({ dispatch, state }, view) { delOthersViews({ dispatch, state }, view) {
return new Promise(resolve => { return new Promise(resolve => {
dispatch('delOthersVisitedViews', view) dispatch("delOthersVisitedViews", view);
dispatch('delOthersCachedViews', view) dispatch("delOthersCachedViews", view);
resolve({ resolve({
visitedViews: [...state.visitedViews], visitedViews: [...state.visitedViews],
cachedViews: [...state.cachedViews] cachedViews: [...state.cachedViews]
}) });
}) });
}, },
delOthersVisitedViews({ commit, state }, view) { delOthersVisitedViews({ commit, state }, view) {
return new Promise(resolve => { return new Promise(resolve => {
commit('DEL_OTHERS_VISITED_VIEWS', view) commit("DEL_OTHERS_VISITED_VIEWS", view);
resolve([...state.visitedViews]) resolve([...state.visitedViews]);
}) });
}, },
delOthersCachedViews({ commit, state }, view) { delOthersCachedViews({ commit, state }, view) {
return new Promise(resolve => { return new Promise(resolve => {
commit('DEL_OTHERS_CACHED_VIEWS', view) commit("DEL_OTHERS_CACHED_VIEWS", view);
resolve([...state.cachedViews]) resolve([...state.cachedViews]);
}) });
}, },
delAllViews({ dispatch, state }, view) { delAllViews({ dispatch, state }, view) {
return new Promise(resolve => { return new Promise(resolve => {
dispatch('delAllVisitedViews', view) dispatch("delAllVisitedViews", view);
dispatch('delAllCachedViews', view) dispatch("delAllCachedViews", view);
resolve({ resolve({
visitedViews: [...state.visitedViews], visitedViews: [...state.visitedViews],
cachedViews: [...state.cachedViews] cachedViews: [...state.cachedViews]
}) });
}) });
}, },
delAllVisitedViews({ commit, state }) { delAllVisitedViews({ commit, state }) {
return new Promise(resolve => { return new Promise(resolve => {
commit('DEL_ALL_VISITED_VIEWS') commit("DEL_ALL_VISITED_VIEWS");
resolve([...state.visitedViews]) resolve([...state.visitedViews]);
}) });
}, },
delAllCachedViews({ commit, state }) { delAllCachedViews({ commit, state }) {
return new Promise(resolve => { return new Promise(resolve => {
commit('DEL_ALL_CACHED_VIEWS') commit("DEL_ALL_CACHED_VIEWS");
resolve([...state.cachedViews]) resolve([...state.cachedViews]);
}) });
}, },
updateVisitedView({ commit }, view) { updateVisitedView({ commit }, view) {
commit('UPDATE_VISITED_VIEW', view) commit("UPDATE_VISITED_VIEW", view);
} }
} };
export default { export default {
namespaced: true, namespaced: true,
state, state,
mutations, mutations,
actions actions
} };

Some files were not shown because too many files have changed in this diff Show More