add prettier and deep fixed
This commit is contained in:
parent
0f5ea1867a
commit
521316e256
.eslintrc.js.prettierignore.prettierrc.js
mock
package.jsonsrc
App.vue
api
components
BackToTop
Breadcrumb
Charts
DndList
DragSelect
Dropzone
ErrorLog
GithubCorner
Hamburger
HeaderSearch
ImageCropper
JsonEditor
Kanban
MDinput
MarkdownEditor
Pagination
PanThumb
RightPanel
Screenfull
Share
SizeSelect
Sticky
SvgIcon
TextHoverEffect
ThemePicker
Tinymce
Upload
UploadExcel
directive
filters
icons
layout
main.jspermission.jsrouter
settings.jsstore
220
.eslintrc.js
220
.eslintrc.js
|
@ -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"
|
||||
// ]
|
||||
};
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
.prettierrc.js
|
||||
.eslintrc.js
|
||||
jest.config.js
|
||||
vue.config.js
|
||||
postcss.config.js
|
||||
settings.js
|
|
@ -0,0 +1,8 @@
|
|||
/** eslint-disable */
|
||||
module.exports =
|
||||
{
|
||||
|
||||
"endOfLine":"auto"
|
||||
|
||||
|
||||
}
|
124
mock/article.js
124
mock/article.js
|
@ -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"
|
||||
};
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
];
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
]
|
||||
];
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
];
|
||||
|
|
|
@ -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 }
|
||||
];
|
||||
|
|
69
mock/user.js
69
mock/user.js
|
@ -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"
|
||||
};
|
||||
}
|
||||
}
|
||||
]
|
||||
];
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -6,6 +6,6 @@
|
|||
|
||||
<script>
|
||||
export default {
|
||||
name: 'App'
|
||||
}
|
||||
name: "App"
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -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
|
||||
})
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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
|
||||
})
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
@ -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
|
||||
})
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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: "画像が小さすぎます。最小サイズ: "
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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"
|
||||
};
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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%;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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']"`);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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 sticky(Currently 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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
46
src/main.js
46
src/main.js
|
@ -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)
|
||||
})
|
||||
});
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue