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 = {
root: true,
parserOptions: {
parser: 'babel-eslint',
sourceType: 'module'
},
env: {
browser: 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
//it is base on https://github.com/vuejs/eslint-config-vue
plugins: [
"vue",
"@typescript-eslint",
"jsdoc",
"eslint-comments",
"prettier"
],
rules: {
"vue/max-attributes-per-line": [2, {
"singleline": 10,
"multiline": {
"max": 1,
"allowFirstLine": false
}
}],
"vue/singleline-html-element-content-newline": "off",
"vue/multiline-html-element-content-newline":"off",
"vue/name-property-casing": ["error", "PascalCase"],
"vue/no-v-html": "off",
'accessor-pairs': 2,
'arrow-spacing': [2, {
'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']
}
}
"no-console": process.env.NODE_ENV === "production" ? "error" : "off",
"no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
"prettier/prettier": ["error"]
},
extends: ["plugin:vue/essential", "prettier"] // activate vue related rules
// extends: [
// "eslint:recommended",
// "plugin:vue/recommended",
// "plugin:vue/base",
// "@vue/standard",
// "@vue/typescript",
// "plugin:prettier/recommended"
// ]
};

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

View File

@ -1,17 +1,12 @@
import Mock from 'mockjs'
import { param2Obj } from '../src/utils'
import Mock from "mockjs";
import { param2Obj } from "../src/utils";
import user from './user'
import role from './role'
import article from './article'
import search from './remote-search'
import user from "./user";
import role from "./role";
import article from "./article";
import search from "./remote-search";
const mocks = [
...user,
...role,
...article,
...search
]
const mocks = [...user, ...role, ...article, ...search];
// for front mock
// please use it cautiously, it will redefine XMLHttpRequest,
@ -19,38 +14,42 @@ const mocks = [
export function mockXHR() {
// mock patch
// 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() {
if (this.custom.xhr) {
this.custom.xhr.withCredentials = this.withCredentials || false
this.custom.xhr.withCredentials = this.withCredentials || false;
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) {
return function(options) {
let result = null
let result = null;
if (respond instanceof Function) {
const { body, type, url } = options
const { body, type, url } = options;
// https://expressjs.com/en/4x/api.html#req
result = respond({
method: type,
body: JSON.parse(body),
query: param2Obj(url)
})
});
} else {
result = respond
result = respond;
}
return Mock.mock(result)
}
return Mock.mock(result);
};
}
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) => {
return {
url: new RegExp(`/mock${url}`),
type: type || 'get',
type: type || "get",
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 => {
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 bodyParser = require('body-parser')
const chalk = require('chalk')
const path = require('path')
const chokidar = require("chokidar");
const bodyParser = require("body-parser");
const chalk = require("chalk");
const path = require("path");
const mockDir = path.join(process.cwd(), 'mock')
const mockDir = path.join(process.cwd(), "mock");
function registerRoutes(app) {
let mockLastIndex
const { default: mocks } = require('./index.js')
let mockLastIndex;
const { default: mocks } = require("./index.js");
for (const mock of mocks) {
app[mock.type](mock.url, mock.response)
mockLastIndex = app._router.stack.length
app[mock.type](mock.url, mock.response);
mockLastIndex = app._router.stack.length;
}
const mockRoutesLength = Object.keys(mocks).length
const mockRoutesLength = Object.keys(mocks).length;
return {
mockRoutesLength: mockRoutesLength,
mockStartIndex: mockLastIndex - mockRoutesLength
}
};
}
function unregisterRoutes() {
Object.keys(require.cache).forEach(i => {
if (i.includes(mockDir)) {
delete require.cache[require.resolve(i)]
delete require.cache[require.resolve(i)];
}
})
});
}
module.exports = app => {
// es6 polyfill
require('@babel/register')
require("@babel/register");
// parse app.body
// https://expressjs.com/en/4x/api.html#req.body
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({
extended: true
}))
app.use(bodyParser.json());
app.use(
bodyParser.urlencoded({
extended: true
})
);
const mockRoutes = registerRoutes(app)
var mockRoutesLength = mockRoutes.mockRoutesLength
var mockStartIndex = mockRoutes.mockStartIndex
const mockRoutes = registerRoutes(app);
var mockRoutesLength = mockRoutes.mockRoutesLength;
var mockStartIndex = mockRoutes.mockStartIndex;
// watch files, hot reload mock server
chokidar.watch(mockDir, {
ignored: /mock-server/,
ignoreInitial: true
}).on('all', (event, path) => {
if (event === 'change' || event === 'add') {
try {
// remove mock routes stack
app._router.stack.splice(mockStartIndex, mockRoutesLength)
chokidar
.watch(mockDir, {
ignored: /mock-server/,
ignoreInitial: true
})
.on("all", (event, path) => {
if (event === "change" || event === "add") {
try {
// remove mock routes stack
app._router.stack.splice(mockStartIndex, mockRoutesLength);
// clear routes cache
unregisterRoutes()
// clear routes cache
unregisterRoutes();
const mockRoutes = registerRoutes(app)
mockRoutesLength = mockRoutes.mockRoutesLength
mockStartIndex = mockRoutes.mockStartIndex
const mockRoutes = registerRoutes(app);
mockRoutesLength = mockRoutes.mockRoutesLength;
mockStartIndex = mockRoutes.mockStartIndex;
console.log(chalk.magentaBright(`\n > Mock Server hot reload success! changed ${path}`))
} catch (error) {
console.log(chalk.redBright(error))
console.log(
chalk.magentaBright(
`\n > Mock Server hot reload success! changed ${path}`
)
);
} catch (error) {
console.log(chalk.redBright(error));
}
}
}
})
}
});
};

View File

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

View File

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

View File

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

View File

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

View File

@ -13,6 +13,7 @@
"test:unit": "jest --clearCache && vue-cli-service test:unit",
"test:ci": "npm run lint && npm run test:unit",
"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"
},
"husky": {
@ -86,7 +87,8 @@
"chokidar": "2.1.5",
"connect": "3.6.6",
"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",
"husky": "1.3.1",
"lint-staged": "8.1.5",

View File

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

View File

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

View File

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

View File

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

View File

@ -1,38 +1,38 @@
import request from '@/utils/request'
import request from "@/utils/request";
export function getRoutes() {
return request({
url: '/routes',
method: 'get'
})
url: "/routes",
method: "get"
});
}
export function getRoles() {
return request({
url: '/roles',
method: 'get'
})
url: "/roles",
method: "get"
});
}
export function addRole(data) {
return request({
url: '/role',
method: 'post',
url: "/role",
method: "post",
data
})
});
}
export function updateRole(id, data) {
return request({
url: `/role/${id}`,
method: 'put',
method: "put",
data
})
});
}
export function deleteRole(id) {
return request({
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) {
return request({
url: '/user/login',
method: 'post',
url: "/user/login",
method: "post",
data
})
});
}
export function getInfo(token) {
return request({
url: '/user/info',
method: 'get',
url: "/user/info",
method: "get",
params: { token }
})
});
}
export function logout() {
return request({
url: '/user/logout',
method: 'post'
})
url: "/user/logout",
method: "post"
});
}

View File

@ -1,14 +1,31 @@
<template>
<transition :name="transitionName">
<div 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
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>
</transition>
</template>
<script>
export default {
name: 'BackToTop',
name: "BackToTop",
props: {
visibilityHeight: {
type: Number,
@ -22,19 +39,19 @@ export default {
type: Object,
default: function() {
return {
right: '50px',
bottom: '50px',
width: '40px',
height: '40px',
'border-radius': '4px',
'line-height': '45px',
background: '#e7eaf1'
}
right: "50px",
bottom: "50px",
width: "40px",
height: "40px",
"border-radius": "4px",
"line-height": "45px",
background: "#e7eaf1"
};
}
},
transitionName: {
type: String,
default: 'fade'
default: "fade"
}
},
data() {
@ -42,44 +59,44 @@ export default {
visible: false,
interval: null,
isMoving: false
}
};
},
mounted() {
window.addEventListener('scroll', this.handleScroll)
window.addEventListener("scroll", this.handleScroll);
},
beforeDestroy() {
window.removeEventListener('scroll', this.handleScroll)
window.removeEventListener("scroll", this.handleScroll);
if (this.interval) {
clearInterval(this.interval)
clearInterval(this.interval);
}
},
methods: {
handleScroll() {
this.visible = window.pageYOffset > this.visibilityHeight
this.visible = window.pageYOffset > this.visibilityHeight;
},
backToTop() {
if (this.isMoving) return
const start = window.pageYOffset
let i = 0
this.isMoving = true
if (this.isMoving) return;
const start = window.pageYOffset;
let i = 0;
this.isMoving = true;
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) {
window.scrollTo(0, this.backPosition)
clearInterval(this.interval)
this.isMoving = false
window.scrollTo(0, this.backPosition);
clearInterval(this.interval);
this.isMoving = false;
} else {
window.scrollTo(0, next)
window.scrollTo(0, next);
}
i++
}, 16.7)
i++;
}, 16.7);
},
easeInOutQuad(t, b, c, d) {
if ((t /= d / 2) < 1) return c / 2 * t * t + b
return -c / 2 * (--t * (t - 2) - 1) + b
if ((t /= d / 2) < 1) return (c / 2) * t * t + b;
return (-c / 2) * (--t * (t - 2) - 1) + b;
}
}
}
};
</script>
<style scoped>
@ -96,12 +113,12 @@ export default {
.fade-enter-active,
.fade-leave-active {
transition: opacity .5s;
transition: opacity 0.5s;
}
.fade-enter,
.fade-leave-to {
opacity: 0
opacity: 0;
}
.back-to-ceiling .Icon {

View File

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

View File

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

View File

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

View File

@ -1,271 +1,277 @@
<template>
<div :id="id" :class="className" :style="{height:height,width:width}" />
<div :id="id" :class="className" :style="{ height: height, width: width }" />
</template>
<script>
import echarts from 'echarts'
import resize from './mixins/resize'
import echarts from "echarts";
import resize from "./mixins/resize";
export default {
mixins: [resize],
props: {
className: {
type: String,
default: 'chart'
default: "chart"
},
id: {
type: String,
default: 'chart'
default: "chart"
},
width: {
type: String,
default: '200px'
default: "200px"
},
height: {
type: String,
default: '200px'
default: "200px"
}
},
data() {
return {
chart: null
}
};
},
mounted() {
this.initChart()
this.initChart();
},
beforeDestroy() {
if (!this.chart) {
return
return;
}
this.chart.dispose()
this.chart = null
this.chart.dispose();
this.chart = null;
},
methods: {
initChart() {
this.chart = echarts.init(document.getElementById(this.id))
this.chart = echarts.init(document.getElementById(this.id));
const xData = (function() {
const data = []
const data = [];
for (let i = 1; i < 13; i++) {
data.push(i + 'month')
data.push(i + "month");
}
return data
}())
return data;
})();
this.chart.setOption({
backgroundColor: '#344b58',
backgroundColor: "#344b58",
title: {
text: 'statistics',
x: '20',
top: '20',
text: "statistics",
x: "20",
top: "20",
textStyle: {
color: '#fff',
fontSize: '22'
color: "#fff",
fontSize: "22"
},
subtextStyle: {
color: '#90979c',
fontSize: '16'
color: "#90979c",
fontSize: "16"
}
},
tooltip: {
trigger: 'axis',
trigger: "axis",
axisPointer: {
textStyle: {
color: '#fff'
color: "#fff"
}
}
},
grid: {
left: '5%',
right: '5%',
left: "5%",
right: "5%",
borderWidth: 0,
top: 150,
bottom: 95,
textStyle: {
color: '#fff'
color: "#fff"
}
},
legend: {
x: '5%',
top: '10%',
x: "5%",
top: "10%",
textStyle: {
color: '#90979c'
color: "#90979c"
},
data: ['female', 'male', 'average']
data: ["female", "male", "average"]
},
calculable: true,
xAxis: [{
type: 'category',
axisLine: {
lineStyle: {
color: '#90979c'
}
},
splitLine: {
show: false
},
axisTick: {
show: false
},
splitArea: {
show: false
},
axisLabel: {
interval: 0
},
data: xData
}],
yAxis: [{
type: 'value',
splitLine: {
show: false
},
axisLine: {
lineStyle: {
color: '#90979c'
}
},
axisTick: {
show: false
},
axisLabel: {
interval: 0
},
splitArea: {
show: false
xAxis: [
{
type: "category",
axisLine: {
lineStyle: {
color: "#90979c"
}
},
splitLine: {
show: false
},
axisTick: {
show: false
},
splitArea: {
show: false
},
axisLabel: {
interval: 0
},
data: xData
}
}],
dataZoom: [{
show: true,
height: 30,
xAxisIndex: [
0
],
bottom: 30,
start: 10,
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',
handleSize: '110%',
handleStyle: {
color: '#d3dee5'
],
yAxis: [
{
type: "value",
splitLine: {
show: false
},
axisLine: {
lineStyle: {
color: "#90979c"
}
},
axisTick: {
show: false
},
axisLabel: {
interval: 0
},
splitArea: {
show: false
}
}
],
dataZoom: [
{
show: true,
height: 30,
xAxisIndex: [0],
bottom: 30,
start: 10,
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",
handleSize: "110%",
handleStyle: {
color: "#d3dee5"
},
textStyle: {
color: "#fff"
},
borderColor: "#90979c"
},
textStyle: {
color: '#fff' },
borderColor: '#90979c'
}, {
type: 'inside',
show: true,
height: 15,
start: 1,
end: 35
}],
series: [{
name: 'female',
type: 'bar',
stack: 'total',
barMaxWidth: 35,
barGap: '10%',
itemStyle: {
normal: {
color: 'rgba(255,144,128,1)',
label: {
show: true,
textStyle: {
color: '#fff'
},
position: 'insideTop',
formatter(p) {
return p.value > 0 ? p.value : ''
{
type: "inside",
show: true,
height: 15,
start: 1,
end: 35
}
],
series: [
{
name: "female",
type: "bar",
stack: "total",
barMaxWidth: 35,
barGap: "10%",
itemStyle: {
normal: {
color: "rgba(255,144,128,1)",
label: {
show: true,
textStyle: {
color: "#fff"
},
position: "insideTop",
formatter(p) {
return p.value > 0 ? p.value : "";
}
}
}
}
},
data: [
709,
1917,
2455,
2610,
1719,
1433,
1544,
3285,
5208,
3372,
2484,
4078
]
},
data: [
709,
1917,
2455,
2610,
1719,
1433,
1544,
3285,
5208,
3372,
2484,
4078
]
},
{
name: 'male',
type: 'bar',
stack: 'total',
itemStyle: {
normal: {
color: 'rgba(0,191,183,1)',
barBorderRadius: 0,
label: {
show: true,
position: 'top',
formatter(p) {
return p.value > 0 ? p.value : ''
{
name: "male",
type: "bar",
stack: "total",
itemStyle: {
normal: {
color: "rgba(0,191,183,1)",
barBorderRadius: 0,
label: {
show: true,
position: "top",
formatter(p) {
return p.value > 0 ? p.value : "";
}
}
}
}
},
data: [
327,
1776,
507,
1200,
800,
482,
204,
1390,
1001,
951,
381,
220
]
},
data: [
327,
1776,
507,
1200,
800,
482,
204,
1390,
1001,
951,
381,
220
]
}, {
name: 'average',
type: 'line',
stack: 'total',
symbolSize: 10,
symbol: 'circle',
itemStyle: {
normal: {
color: 'rgba(252,230,48,1)',
barBorderRadius: 0,
label: {
show: true,
position: 'top',
formatter(p) {
return p.value > 0 ? p.value : ''
{
name: "average",
type: "line",
stack: "total",
symbolSize: 10,
symbol: "circle",
itemStyle: {
normal: {
color: "rgba(252,230,48,1)",
barBorderRadius: 0,
label: {
show: true,
position: "top",
formatter(p) {
return p.value > 0 ? p.value : "";
}
}
}
}
},
data: [
1036,
3693,
2962,
3810,
2519,
1915,
1748,
4675,
6209,
4323,
2865,
4298
]
}
},
data: [
1036,
3693,
2962,
3810,
2519,
1915,
1748,
4675,
6209,
4323,
2865,
4298
]
}
]
})
});
}
}
}
};
</script>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,10 @@
<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
width="80"
height="80"
@ -25,30 +30,30 @@
<style scoped>
.github-corner:hover .octo-arm {
animation: octocat-wave 560ms ease-in-out
animation: octocat-wave 560ms ease-in-out;
}
@keyframes octocat-wave {
0%,
100% {
transform: rotate(0)
transform: rotate(0);
}
20%,
60% {
transform: rotate(-25deg)
transform: rotate(-25deg);
}
40%,
80% {
transform: rotate(10deg)
transform: rotate(10deg);
}
}
@media (max-width:500px) {
@media (max-width: 500px) {
.github-corner:hover .octo-arm {
animation: none
animation: none;
}
.github-corner .octo-arm {
animation: octocat-wave 560ms ease-in-out
animation: octocat-wave 560ms ease-in-out;
}
}
</style>

View File

@ -1,21 +1,23 @@
<template>
<div style="padding: 0 15px;" @click="toggleClick">
<svg
:class="{'is-active':isActive}"
:class="{ 'is-active': isActive }"
class="hamburger"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
width="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>
</div>
</template>
<script>
export default {
name: 'Hamburger',
name: "Hamburger",
props: {
isActive: {
type: Boolean,
@ -24,10 +26,10 @@ export default {
},
methods: {
toggleClick() {
this.$emit('toggleClick')
this.$emit("toggleClick");
}
}
}
};
</script>
<style scoped>

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,18 +1,21 @@
<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-roles-container">
<slot />
</div>
</div>
<!-- eslint-disable-next-line -->
<div :style="{backgroundImage: `url(${image})`}" class="pan-thumb"></div>
<div :style="{ backgroundImage: `url(${image})` }" class="pan-thumb"></div>
</div>
</template>
<script>
export default {
name: 'PanThumb',
name: "PanThumb",
props: {
image: {
type: String,
@ -24,14 +27,14 @@ export default {
},
width: {
type: String,
default: '150px'
default: "150px"
},
height: {
type: String,
default: '150px'
default: "150px"
}
}
}
};
</script>
<style scoped>
@ -93,7 +96,7 @@ export default {
margin: 0 60px;
padding: 22px 0 0 0;
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);
}
@ -121,9 +124,10 @@ export default {
letter-spacing: 1px;
padding-top: 24px;
margin: 7px auto 0;
font-family: 'Open Sans', Arial, sans-serif;
font-family: "Open Sans", Arial, sans-serif;
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);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,10 @@
<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">
<use :xlink:href="iconName" />
</svg>
@ -7,10 +12,10 @@
<script>
// 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 {
name: 'SvgIcon',
name: "SvgIcon",
props: {
iconClass: {
type: String,
@ -18,31 +23,31 @@ export default {
},
className: {
type: String,
default: ''
default: ""
}
},
computed: {
isExternal() {
return isExternal(this.iconClass)
return isExternal(this.iconClass);
},
iconName() {
return `#icon-${this.iconClass}`
return `#icon-${this.iconClass}`;
},
svgClass() {
if (this.className) {
return 'svg-icon ' + this.className
return "svg-icon " + this.className;
} else {
return 'svg-icon'
return "svg-icon";
}
},
styleExternalIcon() {
return {
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>
<style scoped>
@ -56,7 +61,7 @@ export default {
.svg-external-icon {
background-color: currentColor;
mask-size: cover!important;
mask-size: cover !important;
display: inline-block;
}
</style>

View File

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

View File

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

View File

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

View File

@ -1,30 +1,30 @@
let callbacks = []
let callbacks = [];
function loadedTinymce() {
// to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2144
// check is successfully downloaded script
return window.tinymce
return window.tinymce;
}
const dynamicLoadScript = (src, callback) => {
const existingScript = document.getElementById(src)
const cb = callback || function() {}
const existingScript = document.getElementById(src);
const cb = callback || function() {};
if (!existingScript) {
const script = document.createElement('script')
script.src = src // src url for the third-party library being loaded.
script.id = src
document.body.appendChild(script)
callbacks.push(cb)
const onEnd = 'onload' in script ? stdOnEnd : ieOnEnd
onEnd(script)
const script = document.createElement("script");
script.src = src; // src url for the third-party library being loaded.
script.id = src;
document.body.appendChild(script);
callbacks.push(cb);
const onEnd = "onload" in script ? stdOnEnd : ieOnEnd;
onEnd(script);
}
if (existingScript && cb) {
if (loadedTinymce()) {
cb(null, existingScript)
cb(null, existingScript);
} else {
callbacks.push(cb)
callbacks.push(cb);
}
}
@ -32,28 +32,29 @@ const dynamicLoadScript = (src, callback) => {
script.onload = function() {
// this.onload = null here is necessary
// because even IE9 works not like others
this.onerror = this.onload = null
this.onerror = this.onload = null;
for (const cb of callbacks) {
cb(null, script)
cb(null, script);
}
callbacks = null
}
callbacks = null;
};
script.onerror = function() {
this.onerror = this.onload = null
cb(new Error('Failed to load ' + src), script)
}
this.onerror = this.onload = null;
cb(new Error("Failed to load " + src), script);
};
}
function ieOnEnd(script) {
script.onreadystatechange = function() {
if (this.readyState !== 'complete' && this.readyState !== 'loaded') return
this.onreadystatechange = null
if (this.readyState !== "complete" && this.readyState !== "loaded")
return;
this.onreadystatechange = null;
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>
<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" />
<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>
</template>
@ -12,38 +20,43 @@
* docs:
* https://panjiachen.github.io/vue-element-admin-site/feature/component/rich-editor.html#tinymce
*/
import editorImage from './components/EditorImage'
import plugins from './plugins'
import toolbar from './toolbar'
import load from './dynamicLoadScript'
import editorImage from "./components/EditorImage";
import plugins from "./plugins";
import toolbar from "./toolbar";
import load from "./dynamicLoadScript";
// 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 {
name: 'Tinymce',
name: "Tinymce",
components: { editorImage },
props: {
id: {
type: String,
default: function() {
return 'vue-tinymce-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '')
return (
"vue-tinymce-" +
+new Date() +
((Math.random() * 1000).toFixed(0) + "")
);
}
},
value: {
type: String,
default: ''
default: ""
},
toolbar: {
type: Array,
required: false,
default() {
return []
return [];
}
},
menubar: {
type: String,
default: 'file edit insert view format table'
default: "file edit insert view format table"
},
height: {
type: [Number, String],
@ -53,7 +66,7 @@ export default {
width: {
type: [Number, String],
required: false,
default: 'auto'
default: "auto"
}
},
data() {
@ -63,90 +76,92 @@ export default {
tinymceId: this.id,
fullscreen: false,
languageTypeList: {
'en': 'en',
'zh': 'zh_CN',
'es': 'es_MX',
'ja': 'ja'
en: "en",
zh: "zh_CN",
es: "es_MX",
ja: "ja"
}
}
};
},
computed: {
containerWidth() {
const width = this.width
if (/^[\d]+(\.[\d]+)?$/.test(width)) { // matches `100`, `'100'`
return `${width}px`
const width = this.width;
if (/^[\d]+(\.[\d]+)?$/.test(width)) {
// matches `100`, `'100'`
return `${width}px`;
}
return width
return width;
}
},
watch: {
value(val) {
if (!this.hasChange && this.hasInit) {
this.$nextTick(() =>
window.tinymce.get(this.tinymceId).setContent(val || ''))
window.tinymce.get(this.tinymceId).setContent(val || "")
);
}
}
},
mounted() {
this.init()
this.init();
},
activated() {
if (window.tinymce) {
this.initTinymce()
this.initTinymce();
}
},
deactivated() {
this.destroyTinymce()
this.destroyTinymce();
},
destroyed() {
this.destroyTinymce()
this.destroyTinymce();
},
methods: {
init() {
// dynamic load tinymce from cdn
load(tinymceCDN, (err) => {
load(tinymceCDN, err => {
if (err) {
this.$message.error(err.message)
return
this.$message.error(err.message);
return;
}
this.initTinymce()
})
this.initTinymce();
});
},
initTinymce() {
const _this = this
const _this = this;
window.tinymce.init({
selector: `#${this.tinymceId}`,
language: this.languageTypeList['en'],
language: this.languageTypeList["en"],
height: this.height,
body_class: 'panel-body ',
body_class: "panel-body ",
object_resizing: false,
toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar,
menubar: this.menubar,
plugins: plugins,
end_container_on_empty_block: true,
powerpaste_word_import: 'clean',
powerpaste_word_import: "clean",
code_dialog_height: 450,
code_dialog_width: 1000,
advlist_bullet_styles: 'square',
advlist_number_styles: 'default',
imagetools_cors_hosts: ['www.tinymce.com', 'codepen.io'],
default_link_target: '_blank',
advlist_bullet_styles: "square",
advlist_number_styles: "default",
imagetools_cors_hosts: ["www.tinymce.com", "codepen.io"],
default_link_target: "_blank",
link_title: false,
nonbreaking_force_tab: true, // inserting nonbreaking space &nbsp; need Nonbreaking Space Plugin
init_instance_callback: editor => {
if (_this.value) {
editor.setContent(_this.value)
editor.setContent(_this.value);
}
_this.hasInit = true
editor.on('NodeChange Change KeyUp SetContent', () => {
this.hasChange = true
this.$emit('input', editor.getContent())
})
_this.hasInit = true;
editor.on("NodeChange Change KeyUp SetContent", () => {
this.hasChange = true;
this.$emit("input", editor.getContent());
});
},
setup(editor) {
editor.on('FullscreenStateChanged', (e) => {
_this.fullscreen = e.state
})
editor.on("FullscreenStateChanged", e => {
_this.fullscreen = e.state;
});
}
//
// images_dataimg_filter(img) {
@ -181,32 +196,34 @@ export default {
// console.log(err);
// });
// },
})
});
},
destroyTinymce() {
const tinymce = window.tinymce.get(this.tinymceId)
const tinymce = window.tinymce.get(this.tinymceId);
if (this.fullscreen) {
tinymce.execCommand('mceFullScreen')
tinymce.execCommand("mceFullScreen");
}
if (tinymce) {
tinymce.destroy()
tinymce.destroy();
}
},
setContent(value) {
window.tinymce.get(this.tinymceId).setContent(value)
window.tinymce.get(this.tinymceId).setContent(value);
},
getContent() {
window.tinymce.get(this.tinymceId).getContent()
window.tinymce.get(this.tinymceId).getContent();
},
imageSuccessCBK(arr) {
const _this = this
const _this = this;
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>
<style scoped>
@ -214,7 +231,7 @@ export default {
position: relative;
line-height: normal;
}
.tinymce-container>>>.mce-fullscreen {
.tinymce-container >>> .mce-fullscreen {
z-index: 10000;
}
.tinymce-textarea {

View File

@ -2,6 +2,8 @@
// Detail plugins list see https://www.tinymce.com/docs/plugins/
// 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
// 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"
>
<i class="el-icon-upload" />
<div class="el-upload__text">
将文件拖到此处<em>点击上传</em>
</div>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
</el-upload>
<div class="image-preview">
<div v-show="imageUrl.length>1" class="image-preview-wrapper">
<img :src="imageUrl+'?imageView2/1/w/200/h/200'">
<div v-show="imageUrl.length > 1" class="image-preview-wrapper">
<img :src="imageUrl + '?imageView2/1/w/200/h/200'" />
<div class="image-preview-action">
<i class="el-icon-delete" @click="rmImage" />
</div>
@ -26,109 +24,110 @@
</template>
<script>
import { getToken } from '@/api/qiniu'
import { getToken } from "@/api/qiniu";
export default {
name: 'SingleImageUpload',
name: "SingleImageUpload",
props: {
value: {
type: String,
default: ''
default: ""
}
},
data() {
return {
tempUrl: '',
dataObj: { token: '', key: '' }
}
tempUrl: "",
dataObj: { token: "", key: "" }
};
},
computed: {
imageUrl() {
return this.value
return this.value;
}
},
methods: {
rmImage() {
this.emitInput('')
this.emitInput("");
},
emitInput(val) {
this.$emit('input', val)
this.$emit("input", val);
},
handleImageSuccess() {
this.emitInput(this.tempUrl)
this.emitInput(this.tempUrl);
},
beforeUpload() {
const _self = this
const _self = this;
return new Promise((resolve, reject) => {
getToken().then(response => {
const key = response.data.qiniu_key
const token = response.data.qiniu_token
_self._data.dataObj.token = token
_self._data.dataObj.key = key
this.tempUrl = response.data.qiniu_url
resolve(true)
}).catch(err => {
console.log(err)
reject(false)
})
})
getToken()
.then(response => {
const key = response.data.qiniu_key;
const token = response.data.qiniu_token;
_self._data.dataObj.token = token;
_self._data.dataObj.key = key;
this.tempUrl = response.data.qiniu_url;
resolve(true);
})
.catch(err => {
console.log(err);
reject(false);
});
});
}
}
}
};
</script>
<style lang="scss" scoped>
@import "~@/styles/mixin.scss";
.upload-container {
@import "~@/styles/mixin.scss";
.upload-container {
width: 100%;
position: relative;
@include clearfix;
.image-uploader {
width: 60%;
float: left;
}
.image-preview {
width: 200px;
height: 200px;
position: relative;
border: 1px dashed #d9d9d9;
float: left;
margin-left: 50px;
.image-preview-wrapper {
position: relative;
width: 100%;
height: 100%;
img {
width: 100%;
position: relative;
@include clearfix;
.image-uploader {
width: 60%;
float: left;
}
.image-preview {
width: 200px;
height: 200px;
position: relative;
border: 1px dashed #d9d9d9;
float: left;
margin-left: 50px;
.image-preview-wrapper {
position: relative;
width: 100%;
height: 100%;
img {
width: 100%;
height: 100%;
}
}
.image-preview-action {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
cursor: default;
text-align: center;
color: #fff;
opacity: 0;
font-size: 20px;
background-color: rgba(0, 0, 0, .5);
transition: opacity .3s;
cursor: pointer;
text-align: center;
line-height: 200px;
.el-icon-delete {
font-size: 36px;
}
}
&:hover {
.image-preview-action {
opacity: 1;
}
}
}
height: 100%;
}
}
.image-preview-action {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
cursor: default;
text-align: center;
color: #fff;
opacity: 0;
font-size: 20px;
background-color: rgba(0, 0, 0, 0.5);
transition: opacity 0.3s;
cursor: pointer;
text-align: center;
line-height: 200px;
.el-icon-delete {
font-size: 36px;
}
}
&:hover {
.image-preview-action {
opacity: 1;
}
}
}
}
</style>

View File

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

View File

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

View File

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

View File

@ -1,49 +1,57 @@
// Inspired by https://github.com/Inndy/vue-clipboard2
const Clipboard = require('clipboard')
const Clipboard = require("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 {
bind(el, binding) {
if (binding.arg === 'success') {
el._v_clipboard_success = binding.value
} else if (binding.arg === 'error') {
el._v_clipboard_error = binding.value
if (binding.arg === "success") {
el._v_clipboard_success = binding.value;
} else if (binding.arg === "error") {
el._v_clipboard_error = binding.value;
} else {
const clipboard = new Clipboard(el, {
text() { return binding.value },
action() { return binding.arg === 'cut' ? 'cut' : 'copy' }
})
clipboard.on('success', e => {
const callback = el._v_clipboard_success
callback && callback(e) // eslint-disable-line
})
clipboard.on('error', e => {
const callback = el._v_clipboard_error
callback && callback(e) // eslint-disable-line
})
el._v_clipboard = clipboard
text() {
return binding.value;
},
action() {
return binding.arg === "cut" ? "cut" : "copy";
}
});
clipboard.on("success", e => {
const callback = el._v_clipboard_success;
callback && callback(e); // eslint-disable-line
});
clipboard.on("error", e => {
const callback = el._v_clipboard_error;
callback && callback(e); // eslint-disable-line
});
el._v_clipboard = clipboard;
}
},
update(el, binding) {
if (binding.arg === 'success') {
el._v_clipboard_success = binding.value
} else if (binding.arg === 'error') {
el._v_clipboard_error = binding.value
if (binding.arg === "success") {
el._v_clipboard_success = binding.value;
} else if (binding.arg === "error") {
el._v_clipboard_error = binding.value;
} else {
el._v_clipboard.text = function() { return binding.value }
el._v_clipboard.action = function() { return binding.arg === 'cut' ? 'cut' : 'copy' }
el._v_clipboard.text = function() {
return binding.value;
};
el._v_clipboard.action = function() {
return binding.arg === "cut" ? "cut" : "copy";
};
}
},
unbind(el, binding) {
if (binding.arg === 'success') {
delete el._v_clipboard_success
} else if (binding.arg === 'error') {
delete el._v_clipboard_error
if (binding.arg === "success") {
delete el._v_clipboard_success;
} else if (binding.arg === "error") {
delete el._v_clipboard_error;
} else {
el._v_clipboard.destroy()
delete el._v_clipboard
el._v_clipboard.destroy();
delete el._v_clipboard;
}
}
}
};

View File

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

View File

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

View File

@ -1,13 +1,13 @@
import drag from './drag'
import drag from "./drag";
const install = function(Vue) {
Vue.directive('el-drag-dialog', drag)
}
Vue.directive("el-drag-dialog", drag);
};
if (window.Vue) {
window['el-drag-dialog'] = drag
window["el-drag-dialog"] = drag;
Vue.use(install); // eslint-disable-line
}
drag.install = install
export default drag
drag.install = install;
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
@ -8,34 +11,35 @@ import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/re
*/
const doResize = (el, binding, vnode) => {
const { componentInstance: $table } = vnode
const { componentInstance: $table } = vnode;
const { value } = binding
const { value } = binding;
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
$table.layout.setHeight(height)
$table.doLayout()
}
const height =
window.innerHeight - el.getBoundingClientRect().top - bottomOffset;
$table.layout.setHeight(height);
$table.doLayout();
};
export default {
bind(el, binding, vnode) {
el.resizeListener = () => {
doResize(el, binding, vnode)
}
doResize(el, binding, vnode);
};
// parameter 1 is must be "Element" type
addResizeListener(window.document.body, el.resizeListener)
addResizeListener(window.document.body, el.resizeListener);
},
inserted(el, binding, vnode) {
doResize(el, binding, vnode)
doResize(el, binding, vnode);
},
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) {
Vue.directive('el-height-adaptive-table', adaptive)
}
Vue.directive("el-height-adaptive-table", adaptive);
};
if (window.Vue) {
window['el-height-adaptive-table'] = adaptive
window["el-height-adaptive-table"] = adaptive;
Vue.use(install); // eslint-disable-line
}
adaptive.install = install
export default adaptive
adaptive.install = install;
export default adaptive;

View File

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

View File

@ -1,22 +1,22 @@
import store from '@/store'
import store from "@/store";
export default {
inserted(el, binding, vnode) {
const { value } = binding
const roles = store.getters && store.getters.roles
const { value } = binding;
const roles = store.getters && store.getters.roles;
if (value && value instanceof Array && value.length > 0) {
const permissionRoles = value
const permissionRoles = value;
const hasPermission = roles.some(role => {
return permissionRoles.includes(role)
})
return permissionRoles.includes(role);
});
if (!hasPermission) {
el.parentNode && el.parentNode.removeChild(el)
el.parentNode && el.parentNode.removeChild(el);
}
} 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 = {}
let listenAction
const vueSticky = {};
let listenAction;
vueSticky.install = Vue => {
Vue.directive('sticky', {
Vue.directive("sticky", {
inserted(el, binding) {
const params = binding.value || {}
const stickyTop = params.stickyTop || 0
const zIndex = params.zIndex || 1000
const elStyle = el.style
const params = binding.value || {};
const stickyTop = params.stickyTop || 0;
const zIndex = params.zIndex || 1000;
const elStyle = el.style;
elStyle.position = '-webkit-sticky'
elStyle.position = 'sticky'
elStyle.position = "-webkit-sticky";
elStyle.position = "sticky";
// if the browser support css stickyCurrently Safari, Firefox and Chrome Canary
// if (~elStyle.position.indexOf('sticky')) {
// elStyle.top = `${stickyTop}px`;
// elStyle.zIndex = zIndex;
// return
// }
const elHeight = el.getBoundingClientRect().height
const elWidth = el.getBoundingClientRect().width
elStyle.cssText = `top: ${stickyTop}px; z-index: ${zIndex}`
const elHeight = el.getBoundingClientRect().height;
const elWidth = el.getBoundingClientRect().width;
elStyle.cssText = `top: ${stickyTop}px; z-index: ${zIndex}`;
const parentElm = el.parentNode || document.documentElement
const placeholder = document.createElement('div')
placeholder.style.display = 'none'
placeholder.style.width = `${elWidth}px`
placeholder.style.height = `${elHeight}px`
parentElm.insertBefore(placeholder, el)
const parentElm = el.parentNode || document.documentElement;
const placeholder = document.createElement("div");
placeholder.style.display = "none";
placeholder.style.width = `${elWidth}px`;
placeholder.style.height = `${elHeight}px`;
parentElm.insertBefore(placeholder, el);
let active = false
let active = false;
const getScroll = (target, top) => {
const prop = top ? 'pageYOffset' : 'pageXOffset'
const method = top ? 'scrollTop' : 'scrollLeft'
let ret = target[prop]
if (typeof ret !== 'number') {
ret = window.document.documentElement[method]
const prop = top ? "pageYOffset" : "pageXOffset";
const method = top ? "scrollTop" : "scrollLeft";
let ret = target[prop];
if (typeof ret !== "number") {
ret = window.document.documentElement[method];
}
return ret
}
return ret;
};
const sticky = () => {
if (active) {
return
return;
}
if (!elStyle.height) {
elStyle.height = `${el.offsetHeight}px`
elStyle.height = `${el.offsetHeight}px`;
}
elStyle.position = 'fixed'
elStyle.width = `${elWidth}px`
placeholder.style.display = 'inline-block'
active = true
}
elStyle.position = "fixed";
elStyle.width = `${elWidth}px`;
placeholder.style.display = "inline-block";
active = true;
};
const reset = () => {
if (!active) {
return
return;
}
elStyle.position = ''
placeholder.style.display = 'none'
active = false
}
elStyle.position = "";
placeholder.style.display = "none";
active = false;
};
const check = () => {
const scrollTop = getScroll(window, true)
const offsetTop = el.getBoundingClientRect().top
const scrollTop = getScroll(window, true);
const offsetTop = el.getBoundingClientRect().top;
if (offsetTop < stickyTop) {
sticky()
sticky();
} else {
if (scrollTop < elHeight + stickyTop) {
reset()
reset();
}
}
}
};
listenAction = () => {
check()
}
check();
};
window.addEventListener('scroll', listenAction)
window.addEventListener("scroll", listenAction);
},
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) {
Vue.directive('waves', waves)
}
Vue.directive("waves", waves);
};
if (window.Vue) {
window.waves = waves
window.waves = waves;
Vue.use(install); // eslint-disable-line
}
waves.install = install
export default waves
waves.install = install;
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 handle(e) {
const customOpts = Object.assign({}, binding.value)
const opts = Object.assign({
ele: el, // 波纹作用元素
type: 'hit', // hit 点击位置扩散 center中心点扩展
color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色
},
customOpts
)
const target = opts.ele
const customOpts = Object.assign({}, binding.value);
const opts = Object.assign(
{
ele: el, // 波纹作用元素
type: "hit", // hit 点击位置扩散 center中心点扩展
color: "rgba(0, 0, 0, 0.15)" // 波纹颜色
},
customOpts
);
const target = opts.ele;
if (target) {
target.style.position = 'relative'
target.style.overflow = 'hidden'
const rect = target.getBoundingClientRect()
let ripple = target.querySelector('.waves-ripple')
target.style.position = "relative";
target.style.overflow = "hidden";
const rect = target.getBoundingClientRect();
let ripple = target.querySelector(".waves-ripple");
if (!ripple) {
ripple = document.createElement('span')
ripple.className = 'waves-ripple'
ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px'
target.appendChild(ripple)
ripple = document.createElement("span");
ripple.className = "waves-ripple";
ripple.style.height = ripple.style.width =
Math.max(rect.width, rect.height) + "px";
target.appendChild(ripple);
} else {
ripple.className = 'waves-ripple'
ripple.className = "waves-ripple";
}
switch (opts.type) {
case 'center':
ripple.style.top = rect.height / 2 - ripple.offsetHeight / 2 + 'px'
ripple.style.left = rect.width / 2 - ripple.offsetWidth / 2 + 'px'
break
case "center":
ripple.style.top = rect.height / 2 - ripple.offsetHeight / 2 + "px";
ripple.style.left = rect.width / 2 - ripple.offsetWidth / 2 + "px";
break;
default:
ripple.style.top =
(e.pageY - rect.top - ripple.offsetHeight / 2 - document.documentElement.scrollTop ||
document.body.scrollTop) + 'px'
(e.pageY -
rect.top -
ripple.offsetHeight / 2 -
document.documentElement.scrollTop || document.body.scrollTop) +
"px";
ripple.style.left =
(e.pageX - rect.left - ripple.offsetWidth / 2 - document.documentElement.scrollLeft ||
document.body.scrollLeft) + 'px'
(e.pageX -
rect.left -
ripple.offsetWidth / 2 -
document.documentElement.scrollLeft || document.body.scrollLeft) +
"px";
}
ripple.style.backgroundColor = opts.color
ripple.className = 'waves-ripple z-active'
return false
ripple.style.backgroundColor = opts.color;
ripple.className = "waves-ripple z-active";
return false;
}
}
if (!el[context]) {
el[context] = {
removeHandle: handle
}
};
} else {
el[context].removeHandle = handle
el[context].removeHandle = handle;
}
return handle
return handle;
}
export default {
bind(el, binding) {
el.addEventListener('click', handleClick(el, binding), false)
el.addEventListener("click", handleClick(el, binding), false);
},
update(el, binding) {
el.removeEventListener('click', el[context].removeHandle, false)
el.addEventListener('click', handleClick(el, binding), false)
el.removeEventListener("click", el[context].removeHandle, false);
el.addEventListener("click", handleClick(el, binding), false);
},
unbind(el) {
el.removeEventListener('click', el[context].removeHandle, false)
el[context] = null
delete el[context]
el.removeEventListener("click", el[context].removeHandle, false);
el[context] = null;
delete el[context];
}
}
};

View File

@ -1,5 +1,5 @@
// 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
@ -9,22 +9,22 @@ export { parseTime, formatTime } from '@/utils'
*/
function pluralize(time, label) {
if (time === 1) {
return time + label
return time + label;
}
return time + label + 's'
return time + label + "s";
}
/**
* @param {number} time
*/
export function timeAgo(time) {
const between = Date.now() / 1000 - Number(time)
const between = Date.now() / 1000 - Number(time);
if (between < 3600) {
return pluralize(~~(between / 60), ' minute')
return pluralize(~~(between / 60), " minute");
} else if (between < 86400) {
return pluralize(~~(between / 3600), ' hour')
return pluralize(~~(between / 3600), " hour");
} else {
return pluralize(~~(between / 86400), ' day')
return pluralize(~~(between / 86400), " day");
}
}
@ -36,19 +36,23 @@ export function timeAgo(time) {
*/
export function numberFormatter(num, digits) {
const si = [
{ value: 1E18, symbol: 'E' },
{ value: 1E15, symbol: 'P' },
{ value: 1E12, symbol: 'T' },
{ value: 1E9, symbol: 'G' },
{ value: 1E6, symbol: 'M' },
{ value: 1E3, symbol: 'k' }
]
{ value: 1e18, symbol: "E" },
{ value: 1e15, symbol: "P" },
{ value: 1e12, symbol: "T" },
{ value: 1e9, symbol: "G" },
{ value: 1e6, symbol: "M" },
{ value: 1e3, symbol: "k" }
];
for (let i = 0; i < si.length; i++) {
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
*/
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
*/
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 SvgIcon from '@/components/SvgIcon'// svg component
import Vue from "vue";
import SvgIcon from "@/components/SvgIcon"; // svg component
// register globally
Vue.component('svg-icon', SvgIcon)
Vue.component("svg-icon", SvgIcon);
const req = require.context('./svg', false, /\.svg$/)
const requireAll = requireContext => requireContext.keys().map(requireContext)
requireAll(req)
const req = require.context("./svg", false, /\.svg$/);
const requireAll = requireContext => requireContext.keys().map(requireContext);
requireAll(req);

View File

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

View File

@ -1,11 +1,16 @@
<template>
<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" />
<div class="right-menu">
<template v-if="device!=='mobile'">
<template v-if="device !== 'mobile'">
<search id="header-search" class="right-menu-item" />
<error-log class="errLog-container right-menu-item hover-effect" />
@ -15,12 +20,14 @@
<el-tooltip content="Global Size" effect="dark" placement="bottom">
<size-select id="size-select" class="right-menu-item hover-effect" />
</el-tooltip>
</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">
<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" />
</div>
<el-dropdown-menu slot="dropdown">
@ -30,10 +37,16 @@
<router-link to="/">
<el-dropdown-item>Dashboard</el-dropdown-item>
</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>
</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>
</a>
<el-dropdown-item divided>
@ -46,13 +59,13 @@
</template>
<script>
import { mapGetters } from 'vuex'
import Breadcrumb from '@/components/Breadcrumb'
import Hamburger from '@/components/Hamburger'
import ErrorLog from '@/components/ErrorLog'
import Screenfull from '@/components/Screenfull'
import SizeSelect from '@/components/SizeSelect'
import Search from '@/components/HeaderSearch'
import { mapGetters } from "vuex";
import Breadcrumb from "@/components/Breadcrumb";
import Hamburger from "@/components/Hamburger";
import ErrorLog from "@/components/ErrorLog";
import Screenfull from "@/components/Screenfull";
import SizeSelect from "@/components/SizeSelect";
import Search from "@/components/HeaderSearch";
export default {
components: {
@ -64,22 +77,18 @@ export default {
Search
},
computed: {
...mapGetters([
'sidebar',
'avatar',
'device'
])
...mapGetters(["sidebar", "avatar", "device"])
},
methods: {
toggleSideBar() {
this.$store.dispatch('app/toggleSideBar')
this.$store.dispatch("app/toggleSideBar");
},
async logout() {
await this.$store.dispatch('user/logout')
this.$router.push(`/login?redirect=${this.$route.fullPath}`)
await this.$store.dispatch("user/logout");
this.$router.push(`/login?redirect=${this.$route.fullPath}`);
}
}
}
};
</script>
<style lang="scss" scoped>
@ -88,18 +97,18 @@ export default {
overflow: hidden;
position: relative;
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 {
line-height: 46px;
height: 100%;
float: left;
cursor: pointer;
transition: background .3s;
-webkit-tap-highlight-color:transparent;
transition: background 0.3s;
-webkit-tap-highlight-color: transparent;
&:hover {
background: rgba(0, 0, 0, .025)
background: rgba(0, 0, 0, 0.025);
}
}
@ -131,10 +140,10 @@ export default {
&.hover-effect {
cursor: pointer;
transition: background .3s;
transition: background 0.3s;
&:hover {
background: rgba(0, 0, 0, .025)
background: rgba(0, 0, 0, 0.025);
}
}
}

View File

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

View File

@ -1,26 +1,26 @@
export default {
computed: {
device() {
return this.$store.state.app.device
return this.$store.state.app.device;
}
},
mounted() {
// 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
this.fixBugIniOS()
this.fixBugIniOS();
},
methods: {
fixBugIniOS() {
const $subMenu = this.$refs.subMenu
const $subMenu = this.$refs.subMenu;
if ($subMenu) {
const handleMouseleave = $subMenu.handleMouseleave
$subMenu.handleMouseleave = (e) => {
if (this.device === 'mobile') {
return
const handleMouseleave = $subMenu.handleMouseleave;
$subMenu.handleMouseleave = e => {
if (this.device === "mobile") {
return;
}
handleMouseleave(e)
}
handleMouseleave(e);
};
}
}
}
}
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,45 +1,45 @@
import store from '@/store'
import store from "@/store";
const { body } = document
const WIDTH = 992 // refer to Bootstrap's responsive design
const { body } = document;
const WIDTH = 992; // refer to Bootstrap's responsive design
export default {
watch: {
$route(route) {
if (this.device === 'mobile' && this.sidebar.opened) {
store.dispatch('app/closeSideBar', { withoutAnimation: false })
if (this.device === "mobile" && this.sidebar.opened) {
store.dispatch("app/closeSideBar", { withoutAnimation: false });
}
}
},
beforeMount() {
window.addEventListener('resize', this.$_resizeHandler)
window.addEventListener("resize", this.$_resizeHandler);
},
beforeDestroy() {
window.removeEventListener('resize', this.$_resizeHandler)
window.removeEventListener("resize", this.$_resizeHandler);
},
mounted() {
const isMobile = this.$_isMobile()
const isMobile = this.$_isMobile();
if (isMobile) {
store.dispatch('app/toggleDevice', 'mobile')
store.dispatch('app/closeSideBar', { withoutAnimation: true })
store.dispatch("app/toggleDevice", "mobile");
store.dispatch("app/closeSideBar", { withoutAnimation: true });
}
},
methods: {
// use $_ for mixins properties
// https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
$_isMobile() {
const rect = body.getBoundingClientRect()
return rect.width - 1 < WIDTH
const rect = body.getBoundingClientRect();
return rect.width - 1 < WIDTH;
},
$_resizeHandler() {
if (!document.hidden) {
const isMobile = this.$_isMobile()
store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop')
const isMobile = this.$_isMobile();
store.dispatch("app/toggleDevice", isMobile ? "mobile" : "desktop");
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 './styles/element-variables.scss'
import Element from "element-ui";
import "./styles/element-variables.scss";
import '@/styles/index.scss' // global css
import "@/styles/index.scss"; // global css
import App from './App'
import store from './store'
import router from './router'
import App from "./App";
import store from "./store";
import router from "./router";
import './icons' // icon
import './permission' // permission control
import './utils/error-log' // error log
import "./icons"; // icon
import "./permission"; // permission control
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
@ -27,25 +27,25 @@ import * as filters from './filters' // global filters
* Currently MockJs will be used in the production environment,
* please remove it before going online! ! !
*/
import { mockXHR } from '../mock'
if (process.env.NODE_ENV === 'production') {
mockXHR()
import { mockXHR } from "../mock";
if (process.env.NODE_ENV === "production") {
mockXHR();
}
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
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({
el: '#app',
el: "#app",
router,
store,
render: h => h(App)
})
});

View File

@ -1,56 +1,59 @@
import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import { getToken } from '@/utils/auth' // get token from cookie
import getPageTitle from '@/utils/get-page-title'
import router from "./router";
import store from "./store";
import { Message } from "element-ui";
import NProgress from "nprogress"; // progress bar
import "nprogress/nprogress.css"; // progress bar style
import { getToken } from "@/utils/auth"; // get token from cookie
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
NProgress.start()
NProgress.start();
// set page title
document.title = getPageTitle(to.meta.title)
document.title = getPageTitle(to.meta.title);
// determine whether the user has logged in
const hasToken = getToken()
const hasToken = getToken();
if (hasToken) {
if (to.path === '/login') {
if (to.path === "/login") {
// if is logged in, redirect to the home page
next({ path: '/' })
NProgress.done()
next({ path: "/" });
NProgress.done();
} else {
// 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) {
next()
next();
} else {
try {
// get user info
// 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
const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
const accessRoutes = await store.dispatch(
"permission/generateRoutes",
roles
);
// dynamically add accessible routes
router.addRoutes(accessRoutes)
router.addRoutes(accessRoutes);
// hack method to ensure that addRoutes is complete
// set the replace: true, so the navigation will not leave a history record
next({ ...to, replace: true })
next({ ...to, replace: true });
} catch (error) {
// remove token and go to login page to re-login
await store.dispatch('user/resetToken')
Message.error(error || 'Has Error')
next(`/login?redirect=${to.path}`)
NProgress.done()
await store.dispatch("user/resetToken");
Message.error(error || "Has Error");
next(`/login?redirect=${to.path}`);
NProgress.done();
}
}
}
@ -59,16 +62,16 @@ router.beforeEach(async(to, from, next) => {
if (whiteList.indexOf(to.path) !== -1) {
// in the free login whitelist, go directly
next()
next();
} else {
// other pages that do not have permission to access are redirected to the login page.
next(`/login?redirect=${to.path}`)
NProgress.done()
next(`/login?redirect=${to.path}`);
NProgress.done();
}
}
})
});
router.afterEach(() => {
// finish progress bar
NProgress.done()
})
NProgress.done();
});

View File

@ -1,16 +1,16 @@
import Vue from 'vue'
import Router from 'vue-router'
import Vue from "vue";
import Router from "vue-router";
Vue.use(Router)
Vue.use(Router);
/* Layout */
import Layout from '@/layout'
import Layout from "@/layout";
/* Router Modules */
import componentsRouter from './modules/components'
import chartsRouter from './modules/charts'
import tableRouter from './modules/table'
import nestedRouter from './modules/nested'
import componentsRouter from "./modules/components";
import chartsRouter from "./modules/charts";
import tableRouter from "./modules/table";
import nestedRouter from "./modules/nested";
/**
* Note: sub-menu only appear when route children.length >= 1
@ -40,89 +40,89 @@ import nestedRouter from './modules/nested'
*/
export const constantRoutes = [
{
path: '/redirect',
path: "/redirect",
component: Layout,
hidden: true,
children: [
{
path: '/redirect/:path*',
component: () => import('@/views/redirect/index')
path: "/redirect/:path*",
component: () => import("@/views/redirect/index")
}
]
},
{
path: '/login',
component: () => import('@/views/login/index'),
path: "/login",
component: () => import("@/views/login/index"),
hidden: true
},
{
path: '/auth-redirect',
component: () => import('@/views/login/auth-redirect'),
path: "/auth-redirect",
component: () => import("@/views/login/auth-redirect"),
hidden: true
},
{
path: '/404',
component: () => import('@/views/error-page/404'),
path: "/404",
component: () => import("@/views/error-page/404"),
hidden: true
},
{
path: '/401',
component: () => import('@/views/error-page/401'),
path: "/401",
component: () => import("@/views/error-page/401"),
hidden: true
},
{
path: '/',
path: "/",
component: Layout,
redirect: '/dashboard',
redirect: "/dashboard",
children: [
{
path: 'dashboard',
component: () => import('@/views/dashboard/index'),
name: 'Dashboard',
meta: { title: 'Dashboard', icon: 'dashboard', affix: true }
path: "dashboard",
component: () => import("@/views/dashboard/index"),
name: "Dashboard",
meta: { title: "Dashboard", icon: "dashboard", affix: true }
}
]
},
{
path: '/documentation',
path: "/documentation",
component: Layout,
children: [
{
path: 'index',
component: () => import('@/views/documentation/index'),
name: 'Documentation',
meta: { title: 'Documentation', icon: 'documentation', affix: true }
path: "index",
component: () => import("@/views/documentation/index"),
name: "Documentation",
meta: { title: "Documentation", icon: "documentation", affix: true }
}
]
},
{
path: '/guide',
path: "/guide",
component: Layout,
redirect: '/guide/index',
redirect: "/guide/index",
children: [
{
path: 'index',
component: () => import('@/views/guide/index'),
name: 'Guide',
meta: { title: 'Guide', icon: 'guide', noCache: true }
path: "index",
component: () => import("@/views/guide/index"),
name: "Guide",
meta: { title: "Guide", icon: "guide", noCache: true }
}
]
},
{
path: '/profile',
path: "/profile",
component: Layout,
redirect: '/profile/index',
redirect: "/profile/index",
hidden: true,
children: [
{
path: 'index',
component: () => import('@/views/profile/index'),
name: 'Profile',
meta: { title: 'Profile', icon: 'user', noCache: true }
path: "index",
component: () => import("@/views/profile/index"),
name: "Profile",
meta: { title: "Profile", icon: "user", noCache: true }
}
]
}
]
];
/**
* asyncRoutes
@ -130,56 +130,56 @@ export const constantRoutes = [
*/
export const asyncRoutes = [
{
path: '/permission',
path: "/permission",
component: Layout,
redirect: '/permission/page',
redirect: "/permission/page",
alwaysShow: true, // will always show the root menu
name: 'Permission',
name: "Permission",
meta: {
title: 'Permission',
icon: 'lock',
roles: ['admin', 'editor'] // you can set roles in root nav
title: "Permission",
icon: "lock",
roles: ["admin", "editor"] // you can set roles in root nav
},
children: [
{
path: 'page',
component: () => import('@/views/permission/page'),
name: 'PagePermission',
path: "page",
component: () => import("@/views/permission/page"),
name: "PagePermission",
meta: {
title: 'Page Permission',
roles: ['admin'] // or you can only set roles in sub nav
title: "Page Permission",
roles: ["admin"] // or you can only set roles in sub nav
}
},
{
path: 'directive',
component: () => import('@/views/permission/directive'),
name: 'DirectivePermission',
path: "directive",
component: () => import("@/views/permission/directive"),
name: "DirectivePermission",
meta: {
title: 'Directive Permission'
title: "Directive Permission"
// if do not set roles, means: this page does not require permission
}
},
{
path: 'role',
component: () => import('@/views/permission/role'),
name: 'RolePermission',
path: "role",
component: () => import("@/views/permission/role"),
name: "RolePermission",
meta: {
title: 'Role Permission',
roles: ['admin']
title: "Role Permission",
roles: ["admin"]
}
}
]
},
{
path: '/icon',
path: "/icon",
component: Layout,
children: [
{
path: 'index',
component: () => import('@/views/icons/index'),
name: 'Icons',
meta: { title: 'Icons', icon: 'icon', noCache: true }
path: "index",
component: () => import("@/views/icons/index"),
name: "Icons",
meta: { title: "Icons", icon: "icon", noCache: true }
}
]
},
@ -191,214 +191,219 @@ export const asyncRoutes = [
tableRouter,
{
path: '/example',
path: "/example",
component: Layout,
redirect: '/example/list',
name: 'Example',
redirect: "/example/list",
name: "Example",
meta: {
title: 'Example',
icon: 'example'
title: "Example",
icon: "example"
},
children: [
{
path: 'create',
component: () => import('@/views/example/create'),
name: 'CreateArticle',
meta: { title: 'Create Article', icon: 'edit' }
path: "create",
component: () => import("@/views/example/create"),
name: "CreateArticle",
meta: { title: "Create Article", icon: "edit" }
},
{
path: 'edit/:id(\\d+)',
component: () => import('@/views/example/edit'),
name: 'EditArticle',
meta: { title: 'Edit Article', noCache: true, activeMenu: '/example/list' },
path: "edit/:id(\\d+)",
component: () => import("@/views/example/edit"),
name: "EditArticle",
meta: {
title: "Edit Article",
noCache: true,
activeMenu: "/example/list"
},
hidden: true
},
{
path: 'list',
component: () => import('@/views/example/list'),
name: 'ArticleList',
meta: { title: 'Article List', icon: 'list' }
path: "list",
component: () => import("@/views/example/list"),
name: "ArticleList",
meta: { title: "Article List", icon: "list" }
}
]
},
{
path: '/tab',
path: "/tab",
component: Layout,
children: [
{
path: 'index',
component: () => import('@/views/tab/index'),
name: 'Tab',
meta: { title: 'Tab', icon: 'tab' }
path: "index",
component: () => import("@/views/tab/index"),
name: "Tab",
meta: { title: "Tab", icon: "tab" }
}
]
},
{
path: '/error',
path: "/error",
component: Layout,
redirect: 'noRedirect',
name: 'ErrorPages',
redirect: "noRedirect",
name: "ErrorPages",
meta: {
title: 'Error Pages',
icon: '404'
title: "Error Pages",
icon: "404"
},
children: [
{
path: '401',
component: () => import('@/views/error-page/401'),
name: 'Page401',
meta: { title: '401', noCache: true }
path: "401",
component: () => import("@/views/error-page/401"),
name: "Page401",
meta: { title: "401", noCache: true }
},
{
path: '404',
component: () => import('@/views/error-page/404'),
name: 'Page404',
meta: { title: '404', noCache: true }
path: "404",
component: () => import("@/views/error-page/404"),
name: "Page404",
meta: { title: "404", noCache: true }
}
]
},
{
path: '/error-log',
path: "/error-log",
component: Layout,
children: [
{
path: 'log',
component: () => import('@/views/error-log/index'),
name: 'ErrorLog',
meta: { title: 'Error Log', icon: 'bug' }
path: "log",
component: () => import("@/views/error-log/index"),
name: "ErrorLog",
meta: { title: "Error Log", icon: "bug" }
}
]
},
{
path: '/excel',
path: "/excel",
component: Layout,
redirect: '/excel/export-excel',
name: 'Excel',
redirect: "/excel/export-excel",
name: "Excel",
meta: {
title: 'Excel',
icon: 'excel'
title: "Excel",
icon: "excel"
},
children: [
{
path: 'export-excel',
component: () => import('@/views/excel/export-excel'),
name: 'ExportExcel',
meta: { title: 'Export Excel' }
path: "export-excel",
component: () => import("@/views/excel/export-excel"),
name: "ExportExcel",
meta: { title: "Export Excel" }
},
{
path: 'export-selected-excel',
component: () => import('@/views/excel/select-excel'),
name: 'SelectExcel',
meta: { title: 'Export Selected' }
path: "export-selected-excel",
component: () => import("@/views/excel/select-excel"),
name: "SelectExcel",
meta: { title: "Export Selected" }
},
{
path: 'export-merge-header',
component: () => import('@/views/excel/merge-header'),
name: 'MergeHeader',
meta: { title: 'Merge Header' }
path: "export-merge-header",
component: () => import("@/views/excel/merge-header"),
name: "MergeHeader",
meta: { title: "Merge Header" }
},
{
path: 'upload-excel',
component: () => import('@/views/excel/upload-excel'),
name: 'UploadExcel',
meta: { title: 'Upload Excel' }
path: "upload-excel",
component: () => import("@/views/excel/upload-excel"),
name: "UploadExcel",
meta: { title: "Upload Excel" }
}
]
},
{
path: '/zip',
path: "/zip",
component: Layout,
redirect: '/zip/download',
redirect: "/zip/download",
alwaysShow: true,
name: 'Zip',
meta: { title: 'Zip', icon: 'zip' },
name: "Zip",
meta: { title: "Zip", icon: "zip" },
children: [
{
path: 'download',
component: () => import('@/views/zip/index'),
name: 'ExportZip',
meta: { title: 'Export Zip' }
path: "download",
component: () => import("@/views/zip/index"),
name: "ExportZip",
meta: { title: "Export Zip" }
}
]
},
{
path: '/pdf',
path: "/pdf",
component: Layout,
redirect: '/pdf/index',
redirect: "/pdf/index",
children: [
{
path: 'index',
component: () => import('@/views/pdf/index'),
name: 'PDF',
meta: { title: 'PDF', icon: 'pdf' }
path: "index",
component: () => import("@/views/pdf/index"),
name: "PDF",
meta: { title: "PDF", icon: "pdf" }
}
]
},
{
path: '/pdf/download',
component: () => import('@/views/pdf/download'),
path: "/pdf/download",
component: () => import("@/views/pdf/download"),
hidden: true
},
{
path: '/theme',
path: "/theme",
component: Layout,
children: [
{
path: 'index',
component: () => import('@/views/theme/index'),
name: 'Theme',
meta: { title: 'Theme', icon: 'theme' }
path: "index",
component: () => import("@/views/theme/index"),
name: "Theme",
meta: { title: "Theme", icon: "theme" }
}
]
},
{
path: '/clipboard',
path: "/clipboard",
component: Layout,
children: [
{
path: 'index',
component: () => import('@/views/clipboard/index'),
name: 'ClipboardDemo',
meta: { title: 'Clipboard', icon: 'clipboard' }
path: "index",
component: () => import("@/views/clipboard/index"),
name: "ClipboardDemo",
meta: { title: "Clipboard", icon: "clipboard" }
}
]
},
{
path: 'external-link',
path: "external-link",
component: Layout,
children: [
{
path: 'https://github.com/PanJiaChen/vue-element-admin',
meta: { title: 'External Link', icon: 'link' }
path: "https://github.com/PanJiaChen/vue-element-admin",
meta: { title: "External Link", icon: "link" }
}
]
},
// 404 page must be placed at the end !!!
{ path: '*', redirect: '/404', hidden: true }
]
{ path: "*", redirect: "/404", hidden: true }
];
const createRouter = () => new Router({
// mode: 'history', // require service support
scrollBehavior: () => ({ y: 0 }),
routes: constantRoutes
})
const createRouter = () =>
new Router({
// mode: 'history', // require service support
scrollBehavior: () => ({ y: 0 }),
routes: constantRoutes
});
const router = createRouter()
const router = createRouter();
// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
const newRouter = createRouter()
router.matcher = newRouter.matcher // reset router
const newRouter = createRouter();
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**/
import Layout from '@/layout'
import Layout from "@/layout";
const chartsRouter = {
path: '/charts',
path: "/charts",
component: Layout,
redirect: 'noRedirect',
name: 'Charts',
redirect: "noRedirect",
name: "Charts",
meta: {
title: 'Charts',
icon: 'chart'
title: "Charts",
icon: "chart"
},
children: [
{
path: 'keyboard',
component: () => import('@/views/charts/keyboard'),
name: 'KeyboardChart',
meta: { title: 'Keyboard Chart', noCache: true }
path: "keyboard",
component: () => import("@/views/charts/keyboard"),
name: "KeyboardChart",
meta: { title: "Keyboard Chart", noCache: true }
},
{
path: 'line',
component: () => import('@/views/charts/line'),
name: 'LineChart',
meta: { title: 'Line Chart', noCache: true }
path: "line",
component: () => import("@/views/charts/line"),
name: "LineChart",
meta: { title: "Line Chart", noCache: true }
},
{
path: 'mix-chart',
component: () => import('@/views/charts/mix-chart'),
name: 'MixChart',
meta: { title: 'Mix Chart', noCache: true }
path: "mix-chart",
component: () => import("@/views/charts/mix-chart"),
name: "MixChart",
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 **/
import Layout from '@/layout'
import Layout from "@/layout";
const componentsRouter = {
path: '/components',
path: "/components",
component: Layout,
redirect: 'noRedirect',
name: 'ComponentDemo',
redirect: "noRedirect",
name: "ComponentDemo",
meta: {
title: 'Components',
icon: 'component'
title: "Components",
icon: "component"
},
children: [
{
path: 'tinymce',
component: () => import('@/views/components-demo/tinymce'),
name: 'TinymceDemo',
meta: { title: 'Tinymce' }
path: "tinymce",
component: () => import("@/views/components-demo/tinymce"),
name: "TinymceDemo",
meta: { title: "Tinymce" }
},
{
path: 'markdown',
component: () => import('@/views/components-demo/markdown'),
name: 'MarkdownDemo',
meta: { title: 'Markdown' }
path: "markdown",
component: () => import("@/views/components-demo/markdown"),
name: "MarkdownDemo",
meta: { title: "Markdown" }
},
{
path: 'json-editor',
component: () => import('@/views/components-demo/json-editor'),
name: 'JsonEditorDemo',
meta: { title: 'JSON Editor' }
path: "json-editor",
component: () => import("@/views/components-demo/json-editor"),
name: "JsonEditorDemo",
meta: { title: "JSON Editor" }
},
{
path: 'split-pane',
component: () => import('@/views/components-demo/split-pane'),
name: 'SplitpaneDemo',
meta: { title: 'SplitPane' }
path: "split-pane",
component: () => import("@/views/components-demo/split-pane"),
name: "SplitpaneDemo",
meta: { title: "SplitPane" }
},
{
path: 'avatar-upload',
component: () => import('@/views/components-demo/avatar-upload'),
name: 'AvatarUploadDemo',
meta: { title: 'Upload' }
path: "avatar-upload",
component: () => import("@/views/components-demo/avatar-upload"),
name: "AvatarUploadDemo",
meta: { title: "Upload" }
},
{
path: 'dropzone',
component: () => import('@/views/components-demo/dropzone'),
name: 'DropzoneDemo',
meta: { title: 'Dropzone' }
path: "dropzone",
component: () => import("@/views/components-demo/dropzone"),
name: "DropzoneDemo",
meta: { title: "Dropzone" }
},
{
path: 'sticky',
component: () => import('@/views/components-demo/sticky'),
name: 'StickyDemo',
meta: { title: 'Sticky' }
path: "sticky",
component: () => import("@/views/components-demo/sticky"),
name: "StickyDemo",
meta: { title: "Sticky" }
},
{
path: 'count-to',
component: () => import('@/views/components-demo/count-to'),
name: 'CountToDemo',
meta: { title: 'Count To' }
path: "count-to",
component: () => import("@/views/components-demo/count-to"),
name: "CountToDemo",
meta: { title: "Count To" }
},
{
path: 'mixin',
component: () => import('@/views/components-demo/mixin'),
name: 'ComponentMixinDemo',
meta: { title: 'Component Mixin' }
path: "mixin",
component: () => import("@/views/components-demo/mixin"),
name: "ComponentMixinDemo",
meta: { title: "Component Mixin" }
},
{
path: 'back-to-top',
component: () => import('@/views/components-demo/back-to-top'),
name: 'BackToTopDemo',
meta: { title: 'Back To Top' }
path: "back-to-top",
component: () => import("@/views/components-demo/back-to-top"),
name: "BackToTopDemo",
meta: { title: "Back To Top" }
},
{
path: 'drag-dialog',
component: () => import('@/views/components-demo/drag-dialog'),
name: 'DragDialogDemo',
meta: { title: 'Drag Dialog' }
path: "drag-dialog",
component: () => import("@/views/components-demo/drag-dialog"),
name: "DragDialogDemo",
meta: { title: "Drag Dialog" }
},
{
path: 'drag-select',
component: () => import('@/views/components-demo/drag-select'),
name: 'DragSelectDemo',
meta: { title: 'Drag Select' }
path: "drag-select",
component: () => import("@/views/components-demo/drag-select"),
name: "DragSelectDemo",
meta: { title: "Drag Select" }
},
{
path: 'dnd-list',
component: () => import('@/views/components-demo/dnd-list'),
name: 'DndListDemo',
meta: { title: 'Dnd List' }
path: "dnd-list",
component: () => import("@/views/components-demo/dnd-list"),
name: "DndListDemo",
meta: { title: "Dnd List" }
},
{
path: 'drag-kanban',
component: () => import('@/views/components-demo/drag-kanban'),
name: 'DragKanbanDemo',
meta: { title: 'Drag Kanban' }
path: "drag-kanban",
component: () => import("@/views/components-demo/drag-kanban"),
name: "DragKanbanDemo",
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 **/
import Layout from '@/layout'
import Layout from "@/layout";
const nestedRouter = {
path: '/nested',
path: "/nested",
component: Layout,
redirect: '/nested/menu1/menu1-1',
name: 'Nested',
redirect: "/nested/menu1/menu1-1",
name: "Nested",
meta: {
title: 'Nested Routes',
icon: 'nested'
title: "Nested Routes",
icon: "nested"
},
children: [
{
path: 'menu1',
component: () => import('@/views/nested/menu1/index'), // Parent router-view
name: 'Menu1',
meta: { title: 'Menu 1' },
redirect: '/nested/menu1/menu1-1',
path: "menu1",
component: () => import("@/views/nested/menu1/index"), // Parent router-view
name: "Menu1",
meta: { title: "Menu 1" },
redirect: "/nested/menu1/menu1-1",
children: [
{
path: 'menu1-1',
component: () => import('@/views/nested/menu1/menu1-1'),
name: 'Menu1-1',
meta: { title: 'Menu 1-1' }
path: "menu1-1",
component: () => import("@/views/nested/menu1/menu1-1"),
name: "Menu1-1",
meta: { title: "Menu 1-1" }
},
{
path: 'menu1-2',
component: () => import('@/views/nested/menu1/menu1-2'),
name: 'Menu1-2',
redirect: '/nested/menu1/menu1-2/menu1-2-1',
meta: { title: 'Menu 1-2' },
path: "menu1-2",
component: () => import("@/views/nested/menu1/menu1-2"),
name: "Menu1-2",
redirect: "/nested/menu1/menu1-2/menu1-2-1",
meta: { title: "Menu 1-2" },
children: [
{
path: 'menu1-2-1',
component: () => import('@/views/nested/menu1/menu1-2/menu1-2-1'),
name: 'Menu1-2-1',
meta: { title: 'Menu 1-2-1' }
path: "menu1-2-1",
component: () => import("@/views/nested/menu1/menu1-2/menu1-2-1"),
name: "Menu1-2-1",
meta: { title: "Menu 1-2-1" }
},
{
path: 'menu1-2-2',
component: () => import('@/views/nested/menu1/menu1-2/menu1-2-2'),
name: 'Menu1-2-2',
meta: { title: 'Menu 1-2-2' }
path: "menu1-2-2",
component: () => import("@/views/nested/menu1/menu1-2/menu1-2-2"),
name: "Menu1-2-2",
meta: { title: "Menu 1-2-2" }
}
]
},
{
path: 'menu1-3',
component: () => import('@/views/nested/menu1/menu1-3'),
name: 'Menu1-3',
meta: { title: 'Menu 1-3' }
path: "menu1-3",
component: () => import("@/views/nested/menu1/menu1-3"),
name: "Menu1-3",
meta: { title: "Menu 1-3" }
}
]
},
{
path: 'menu2',
name: 'Menu2',
component: () => import('@/views/nested/menu2/index'),
meta: { title: 'Menu 2' }
path: "menu2",
name: "Menu2",
component: () => import("@/views/nested/menu2/index"),
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 **/
import Layout from '@/layout'
import Layout from "@/layout";
const tableRouter = {
path: '/table',
path: "/table",
component: Layout,
redirect: '/table/complex-table',
name: 'Table',
redirect: "/table/complex-table",
name: "Table",
meta: {
title: 'Table',
icon: 'table'
title: "Table",
icon: "table"
},
children: [
{
path: 'dynamic-table',
component: () => import('@/views/table/dynamic-table/index'),
name: 'DynamicTable',
meta: { title: 'Dynamic Table' }
path: "dynamic-table",
component: () => import("@/views/table/dynamic-table/index"),
name: "DynamicTable",
meta: { title: "Dynamic Table" }
},
{
path: 'drag-table',
component: () => import('@/views/table/drag-table'),
name: 'DragTable',
meta: { title: 'Drag Table' }
path: "drag-table",
component: () => import("@/views/table/drag-table"),
name: "DragTable",
meta: { title: "Drag Table" }
},
{
path: 'inline-edit-table',
component: () => import('@/views/table/inline-edit-table'),
name: 'InlineEditTable',
meta: { title: 'Inline Edit' }
path: "inline-edit-table",
component: () => import("@/views/table/inline-edit-table"),
name: "InlineEditTable",
meta: { title: "Inline Edit" }
},
{
path: 'complex-table',
component: () => import('@/views/table/complex-table'),
name: 'ComplexTable',
meta: { title: 'Complex Table' }
path: "complex-table",
component: () => import("@/views/table/complex-table"),
name: "ComplexTable",
meta: { title: "Complex Table" }
}
]
}
export default tableRouter
};
export default tableRouter;

View File

@ -1,5 +1,5 @@
module.exports = {
title: 'Vue Element Admin',
title: "Vue Element Admin",
/**
* @type {boolean} true | false
@ -31,5 +31,5 @@ module.exports = {
* The default is only used in the production env
* 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,
permission_routes: state => state.permission.routes,
errorLogs: state => state.errorLog.logs
}
export default getters
};
export default getters;

View File

@ -1,25 +1,25 @@
import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
import Vue from "vue";
import Vuex from "vuex";
import getters from "./getters";
Vue.use(Vuex)
Vue.use(Vuex);
// 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'`
// it will auto require all vuex module from modules file
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
// set './app.js' => 'app'
const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
const value = modulesFiles(modulePath)
modules[moduleName] = value.default
return modules
}, {})
const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, "$1");
const value = modulesFiles(modulePath);
modules[moduleName] = value.default;
return modules;
}, {});
const store = new Vuex.Store({
modules,
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 = {
sidebar: {
opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true,
opened: Cookies.get("sidebarStatus")
? !!+Cookies.get("sidebarStatus")
: true,
withoutAnimation: false
},
device: 'desktop',
size: Cookies.get('size') || 'medium'
}
device: "desktop",
size: Cookies.get("size") || "medium"
};
const mutations = {
TOGGLE_SIDEBAR: state => {
state.sidebar.opened = !state.sidebar.opened
state.sidebar.withoutAnimation = false
state.sidebar.opened = !state.sidebar.opened;
state.sidebar.withoutAnimation = false;
if (state.sidebar.opened) {
Cookies.set('sidebarStatus', 1)
Cookies.set("sidebarStatus", 1);
} else {
Cookies.set('sidebarStatus', 0)
Cookies.set("sidebarStatus", 0);
}
},
CLOSE_SIDEBAR: (state, withoutAnimation) => {
Cookies.set('sidebarStatus', 0)
state.sidebar.opened = false
state.sidebar.withoutAnimation = withoutAnimation
Cookies.set("sidebarStatus", 0);
state.sidebar.opened = false;
state.sidebar.withoutAnimation = withoutAnimation;
},
TOGGLE_DEVICE: (state, device) => {
state.device = device
state.device = device;
},
SET_SIZE: (state, size) => {
state.size = size
Cookies.set('size', size)
state.size = size;
Cookies.set("size", size);
}
}
};
const actions = {
toggleSideBar({ commit }) {
commit('TOGGLE_SIDEBAR')
commit("TOGGLE_SIDEBAR");
},
closeSideBar({ commit }, { withoutAnimation }) {
commit('CLOSE_SIDEBAR', withoutAnimation)
commit("CLOSE_SIDEBAR", withoutAnimation);
},
toggleDevice({ commit }, device) {
commit('TOGGLE_DEVICE', device)
commit("TOGGLE_DEVICE", device);
},
setSize({ commit }, size) {
commit('SET_SIZE', size)
commit("SET_SIZE", size);
}
}
};
export default {
namespaced: true,
state,
mutations,
actions
}
};

View File

@ -1,28 +1,28 @@
const state = {
logs: []
}
};
const mutations = {
ADD_ERROR_LOG: (state, log) => {
state.logs.push(log)
state.logs.push(log);
},
CLEAR_ERROR_LOG: (state) => {
state.logs.splice(0)
CLEAR_ERROR_LOG: state => {
state.logs.splice(0);
}
}
};
const actions = {
addErrorLog({ commit }, log) {
commit('ADD_ERROR_LOG', log)
commit("ADD_ERROR_LOG", log);
},
clearErrorLog({ commit }) {
commit('CLEAR_ERROR_LOG')
commit("CLEAR_ERROR_LOG");
}
}
};
export default {
namespaced: true,
state,
mutations,
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
@ -7,9 +7,9 @@ import { asyncRoutes, constantRoutes } from '@/router'
*/
function hasPermission(roles, route) {
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 {
return true
return true;
}
}
@ -19,51 +19,51 @@ function hasPermission(roles, route) {
* @param roles
*/
export function filterAsyncRoutes(routes, roles) {
const res = []
const res = [];
routes.forEach(route => {
const tmp = { ...route }
const tmp = { ...route };
if (hasPermission(roles, tmp)) {
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 = {
routes: [],
addRoutes: []
}
};
const mutations = {
SET_ROUTES: (state, routes) => {
state.addRoutes = routes
state.routes = constantRoutes.concat(routes)
state.addRoutes = routes;
state.routes = constantRoutes.concat(routes);
}
}
};
const actions = {
generateRoutes({ commit }, roles) {
return new Promise(resolve => {
let accessedRoutes
if (roles.includes('admin')) {
accessedRoutes = asyncRoutes || []
let accessedRoutes;
if (roles.includes("admin")) {
accessedRoutes = asyncRoutes || [];
} else {
accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
accessedRoutes = filterAsyncRoutes(asyncRoutes, roles);
}
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
})
commit("SET_ROUTES", accessedRoutes);
resolve(accessedRoutes);
});
}
}
};
export default {
namespaced: true,
state,
mutations,
actions
}
};

View File

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

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