Merge pull request #8 from PanJiaChen/master

pull
This commit is contained in:
toruksmakto 2019-04-18 03:51:51 +08:00 committed by GitHub
commit 7f40044fc9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
127 changed files with 1000 additions and 694 deletions

View File

@ -1,4 +1,4 @@
# http://editorconfig.org
# https://editorconfig.org
root = true
[*]

View File

@ -30,11 +30,11 @@ English | [简体中文](./README.zh-CN.md)
## Introduction
[vue-element-admin](http://panjiachen.github.io/vue-element-admin) is a production-ready front-end solution for admin interfaces. It based on [vue](https://github.com/vuejs/vue) and use the UI Toolkit [element-ui](https://github.com/ElemeFE/element).
[vue-element-admin](https://panjiachen.github.io/vue-element-admin) is a production-ready front-end solution for admin interfaces. It based on [vue](https://github.com/vuejs/vue) and use the UI Toolkit [element-ui](https://github.com/ElemeFE/element).
It is a magical vue admin based on the newest development stack of vue, built-in i18n solution, typical templates for enterprise applications, lots of awesome features. It helps you build a large complex Single-Page Applications. I believe whatever your needs are, this project will help you.
- [Preview](http://panjiachen.github.io/vue-element-admin)
- [Preview](https://panjiachen.github.io/vue-element-admin)
- [Documentation](https://panjiachen.github.io/vue-element-admin-site/)
@ -50,13 +50,13 @@ It is a magical vue admin based on the newest development stack of vue, built-in
- Desktop: [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
- Typescript: [vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template) (Credits: [@Armour](https://github.com/Armour))
**The current version is `4.0-beta`. If you find a problem, please put [issue](https://github.com/PanJiaChen/vue-element-admin/issues/new). If you want to use the old version - stable version, you can switch branch to [tag/3.11.0](https://github.com/PanJiaChen/vue-element-admin/tree/tag/3.11.0)**
**The current version is `v4.0+` build on `vue-cli`. If you find a problem, please put [issue](https://github.com/PanJiaChen/vue-element-admin/issues/new). If you want to use the old version , you can switch branch to [tag/3.11.0](https://github.com/PanJiaChen/vue-element-admin/tree/tag/3.11.0), it does not rely on `vue-cli'**
**This project does not support low version browsers (e.g. IE). Please add polyfill by yourself.**
## Preparation
You need to install [node](http://nodejs.org/) and [git](https://git-scm.com/) locally. The project is based on [ES2015+](http://es6.ruanyifeng.com/), [vue](https://cn.vuejs.org/index.html), [vuex](https://vuex.vuejs.org/zh-cn/), [vue-router](https://router.vuejs.org/zh-cn/), [vue-cli](https://github.com/vuejs/vue-cli) , [axios](https://github.com/axios/axios) and [element-ui](https://github.com/ElemeFE/element), all request data is simulated using [Mock.js](https://github.com/nuysoft/Mock).
You need to install [node](https://nodejs.org/) and [git](https://git-scm.com/) locally. The project is based on [ES2015+](https://es6.ruanyifeng.com/), [vue](https://cn.vuejs.org/index.html), [vuex](https://vuex.vuejs.org/zh-cn/), [vue-router](https://router.vuejs.org/zh-cn/), [vue-cli](https://github.com/vuejs/vue-cli) , [axios](https://github.com/axios/axios) and [element-ui](https://github.com/ElemeFE/element), all request data is simulated using [Mock.js](https://github.com/nuysoft/Mock).
Understanding and learning this knowledge in advance will greatly help the use of this project.
<p align="center">
@ -187,7 +187,7 @@ Detailed changes for each release are documented in the [release notes](https://
## Online Demo
[Preview](http://panjiachen.github.io/vue-element-admin)
[Preview](https://panjiachen.github.io/vue-element-admin)
## Donate
@ -203,7 +203,7 @@ If you find this project useful, you can buy author a glass of juice :tropical_d
Modern browsers and Internet Explorer 10+.
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](https://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](https://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](https://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](https://godban.github.io/browsers-support-badges/)</br>Safari |
| --------- | --------- | --------- | --------- |
| IE10, IE11, Edge| last 2 versions| last 2 versions| last 2 versions

View File

@ -30,9 +30,9 @@
## 简介
[vue-element-admin](http://panjiachen.github.io/vue-element-admin) 是一个后台前端解决方案,它基于 [vue](https://github.com/vuejs/vue) 和 [element-ui](https://github.com/ElemeFE/element)实现。它使用了最新的前端技术栈,内置了 i18n 国际化解决方案,动态路由,权限验证,提炼了典型的业务模型,提供了丰富的功能组件,它可以帮助你快速搭建企业级中后台产品原型。相信不管你的需求是什么,本项目都能帮助到你。
[vue-element-admin](https://panjiachen.github.io/vue-element-admin) 是一个后台前端解决方案,它基于 [vue](https://github.com/vuejs/vue) 和 [element-ui](https://github.com/ElemeFE/element)实现。它使用了最新的前端技术栈,内置了 i18n 国际化解决方案,动态路由,权限验证,提炼了典型的业务模型,提供了丰富的功能组件,它可以帮助你快速搭建企业级中后台产品原型。相信不管你的需求是什么,本项目都能帮助到你。
- [在线预览](http://panjiachen.github.io/vue-element-admin)
- [在线预览](https://panjiachen.github.io/vue-element-admin)
- [使用文档](https://panjiachen.github.io/vue-element-admin-site/zh/)
@ -50,7 +50,7 @@
- 桌面端: [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
- Typescript 版: [vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template) (鸣谢: [@Armour](https://github.com/Armour))
**目前版本为 `4.0-beta`,若发现问题,欢迎提[issue](https://github.com/PanJiaChen/vue-element-admin/issues/new)。若你想使用旧版本-稳定版,可以切换分支到[tag/3.11.0](https://github.com/PanJiaChen/vue-element-admin/tree/tag/3.11.0)**
**目前版本为 `v4.0+` 基于 `vue-cli` 进行构建,若发现问题,欢迎提[issue](https://github.com/PanJiaChen/vue-element-admin/issues/new)。若你想使用旧版本,可以切换分支到[tag/3.11.0](https://github.com/PanJiaChen/vue-element-admin/tree/tag/3.11.0),它不依赖 `vue-cli`**
**该项目不支持低版本浏览器(如 ie),有需求请自行添加 polyfill [详情](https://github.com/PanJiaChen/vue-element-admin/wiki#babel-polyfill)**
@ -204,7 +204,7 @@ Detailed changes for each release are documented in the [release notes](https://
## Online Demo
[在线 Demo](http://panjiachen.github.io/vue-element-admin)
[在线 Demo](https://panjiachen.github.io/vue-element-admin)
## Donate
@ -221,7 +221,7 @@ Detailed changes for each release are documented in the [release notes](https://
Modern browsers and Internet Explorer 10+.
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](https://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](https://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](https://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](https://godban.github.io/browsers-support-badges/)</br>Safari |
| --------- | --------- | --------- | --------- |
| IE10, IE11, Edge| last 2 versions| last 2 versions| last 2 versions

View File

@ -26,7 +26,7 @@ if (process.env.npm_config_preview || rawArgv.includes('--preview')) {
app.listen(port, function () {
console.log(chalk.green(`> Preview at http://localhost:${port}${publicPath}`))
if (report) {
console.log(chalk.green(`> Report at http://localhost:${port}${publicPath}/report.html`))
console.log(chalk.green(`> Report at http://localhost:${port}${publicPath}report.html`))
}
})

View File

@ -3,7 +3,7 @@ import Mock from 'mockjs'
const List = []
const count = 100
const baseContent = '<p>我是测试数据我是测试数据</p><p><img src="https://wpimg.wallstcn.com/4c69009c-0fd4-4153-b112-6cb53d1cf943"></p>'
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++) {

View File

@ -1,11 +1,23 @@
import Mock from 'mockjs'
import mocks from './mocks'
import { param2Obj } from '../src/utils'
const MOCK_API_BASE = '/mock'
import user from './user'
import role from './role'
import article from './article'
import search from './remote-search'
const mocks = [
...user,
...role,
...article,
...search
]
// for front mock
// please use it cautiously, it will redefine XMLHttpRequest,
// which will cause many of your third-party libraries to be invalidated(like progress event).
export function mockXHR() {
// 修复在使用 MockJS 情况下,设置 withCredentials = true且未被拦截的跨域请求丢失 Cookies 的问题
// mock patch
// https://github.com/nuysoft/Mock/issues/300
Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send
Mock.XHR.prototype.send = function() {
@ -42,9 +54,10 @@ export function mockXHR() {
}
}
// for mock server
const responseFake = (url, type, respond) => {
return {
url: new RegExp(`${MOCK_API_BASE}${url}`),
url: new RegExp(`/mock${url}`),
type: type || 'get',
response(req, res) {
res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond))

62
mock/mock-server.js Normal file
View File

@ -0,0 +1,62 @@
const chokidar = require('chokidar')
const bodyParser = require('body-parser')
const chalk = require('chalk')
function registerRoutes(app) {
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
}
const mockRoutesLength = Object.keys(mocks).length
return {
mockRoutesLength: mockRoutesLength,
mockStartIndex: mockLastIndex - mockRoutesLength
}
}
function unregisterRoutes() {
Object.keys(require.cache).forEach(i => {
if (i.includes('/mock')) {
delete require.cache[require.resolve(i)]
}
})
}
module.exports = app => {
// es6 polyfill
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
}))
const mockRoutes = registerRoutes(app)
var mockRoutesLength = mockRoutes.mockRoutesLength
var mockStartIndex = mockRoutes.mockStartIndex
// watch files, hot reload mock server
chokidar.watch(('./mock'), {
ignored: 'mock/mock-server.js',
persistent: true,
ignoreInitial: true
}).on('all', (event, path) => {
if (event === 'change' || event === 'add') {
// remove mock routes stack
app._router.stack.splice(mockStartIndex, mockRoutesLength)
// clear routes cache
unregisterRoutes()
const mockRoutes = registerRoutes(app)
mockRoutesLength = mockRoutes.mockRoutesLength
mockStartIndex = mockRoutes.mockStartIndex
console.log(chalk.magentaBright(`\n > Mock Server hot reload success! changed ${path}`))
}
})
}

View File

@ -1,12 +0,0 @@
import user from './user'
import role from './role'
import article from './article'
import search from './remoteSearch'
export default [
...user,
...role,
...article,
...search
]

View File

@ -19,17 +19,17 @@ export const constantRoutes = [
},
{
path: '/auth-redirect',
component: 'views/login/authRedirect',
component: 'views/login/auth-redirect',
hidden: true
},
{
path: '/404',
component: 'views/errorPage/404',
component: 'views/error-page/404',
hidden: true
},
{
path: '/401',
component: 'views/errorPage/401',
component: 'views/error-page/401',
hidden: true
},
{
@ -119,7 +119,7 @@ export const asyncRoutes = [
children: [
{
path: 'index',
component: 'views/svg-icons/index',
component: 'views/icons/index',
name: 'Icons',
meta: { title: 'icons', icon: 'icon', noCache: true }
}
@ -129,7 +129,7 @@ export const asyncRoutes = [
{
path: '/components',
component: 'layout/Layout',
redirect: 'noredirect',
redirect: 'noRedirect',
name: 'ComponentDemo',
meta: {
title: 'components',
@ -150,19 +150,19 @@ export const asyncRoutes = [
},
{
path: 'json-editor',
component: 'views/components-demo/jsonEditor',
component: 'views/components-demo/json-editor',
name: 'JsonEditorDemo',
meta: { title: 'jsonEditor' }
},
{
path: 'splitpane',
component: 'views/components-demo/splitpane',
path: 'split-pane',
component: 'views/components-demo/split-pane',
name: 'SplitpaneDemo',
meta: { title: 'splitPane' }
},
{
path: 'avatar-upload',
component: 'views/components-demo/avatarUpload',
component: 'views/components-demo/avatar-upload',
name: 'AvatarUploadDemo',
meta: { title: 'avatarUpload' }
},
@ -180,7 +180,7 @@ export const asyncRoutes = [
},
{
path: 'count-to',
component: 'views/components-demo/countTo',
component: 'views/components-demo/count-to',
name: 'CountToDemo',
meta: { title: 'countTo' }
},
@ -192,31 +192,31 @@ export const asyncRoutes = [
},
{
path: 'back-to-top',
component: 'views/components-demo/backToTop',
component: 'views/components-demo/back-to-top',
name: 'BackToTopDemo',
meta: { title: 'backToTop' }
},
{
path: 'drag-dialog',
component: 'views/components-demo/dragDialog',
component: 'views/components-demo/drag-dialog',
name: 'DragDialogDemo',
meta: { title: 'dragDialog' }
},
{
path: 'drag-select',
component: 'views/components-demo/dragSelect',
component: 'views/components-demo/drag-select',
name: 'DragSelectDemo',
meta: { title: 'dragSelect' }
},
{
path: 'dnd-list',
component: 'views/components-demo/dndList',
component: 'views/components-demo/dnd-list',
name: 'DndListDemo',
meta: { title: 'dndList' }
},
{
path: 'drag-kanban',
component: 'views/components-demo/dragKanban',
component: 'views/components-demo/drag-kanban',
name: 'DragKanbanDemo',
meta: { title: 'dragKanban' }
}
@ -225,7 +225,7 @@ export const asyncRoutes = [
{
path: '/charts',
component: 'layout/Layout',
redirect: 'noredirect',
redirect: 'noRedirect',
name: 'Charts',
meta: {
title: 'charts',
@ -361,7 +361,7 @@ export const asyncRoutes = [
{
path: '/error',
component: 'layout/Layout',
redirect: 'noredirect',
redirect: 'noRedirect',
name: 'ErrorPages',
meta: {
title: 'errorPages',
@ -370,13 +370,13 @@ export const asyncRoutes = [
children: [
{
path: '401',
component: 'views/errorPage/401',
component: 'views/error-page/401',
name: 'Page401',
meta: { title: 'page401', noCache: true }
},
{
path: '404',
component: 'views/errorPage/404',
component: 'views/error-page/404',
name: 'Page404',
meta: { title: 'page404', noCache: true }
}
@ -386,11 +386,11 @@ export const asyncRoutes = [
{
path: '/error-log',
component: 'layout/Layout',
redirect: 'noredirect',
redirect: 'noRedirect',
children: [
{
path: 'log',
component: 'views/errorLog/index',
component: 'views/error-log/index',
name: 'ErrorLog',
meta: { title: 'errorLog', icon: 'bug' }
}
@ -409,25 +409,25 @@ export const asyncRoutes = [
children: [
{
path: 'export-excel',
component: 'views/excel/exportExcel',
component: 'views/excel/export-excel',
name: 'ExportExcel',
meta: { title: 'exportExcel' }
},
{
path: 'export-selected-excel',
component: 'views/excel/selectExcel',
component: 'views/excel/select-excel',
name: 'SelectExcel',
meta: { title: 'selectExcel' }
},
{
path: 'export-merge-header',
component: 'views/excel/mergeHeader',
component: 'views/excel/merge-header',
name: 'MergeHeader',
meta: { title: 'mergeHeader' }
},
{
path: 'upload-excel',
component: 'views/excel/uploadExcel',
component: 'views/excel/upload-excel',
name: 'UploadExcel',
meta: { title: 'uploadExcel' }
}
@ -472,7 +472,7 @@ export const asyncRoutes = [
{
path: '/theme',
component: 'layout/Layout',
redirect: 'noredirect',
redirect: 'noRedirect',
children: [
{
path: 'index',
@ -486,7 +486,7 @@ export const asyncRoutes = [
{
path: '/clipboard',
component: 'layout/Layout',
redirect: 'noredirect',
redirect: 'noRedirect',
children: [
{
path: 'index',

View File

@ -1,6 +1,6 @@
{
"name": "vue-element-admin",
"version": "4.0.0",
"version": "4.0.1",
"description": "A magical vue admin. An out-of-box UI solution for enterprise applications. Newest development stack of vue. Lots of awesome features",
"author": "Pan <panfree23@gmail.com>",
"license": "MIT",
@ -75,6 +75,7 @@
"@babel/core": "7.0.0",
"@babel/register": "7.0.0",
"@vue/cli-plugin-babel": "3.5.3",
"@vue/cli-plugin-eslint": "3.5.1",
"@vue/cli-plugin-unit-jest": "3.5.3",
"@vue/cli-service": "3.5.3",
"@vue/test-utils": "1.0.0-beta.29",
@ -82,6 +83,7 @@
"babel-eslint": "10.0.1",
"babel-jest": "23.6.0",
"chalk": "2.4.2",
"chokidar": "2.1.5",
"connect": "3.6.6",
"eslint": "5.15.3",
"eslint-plugin-vue": "5.2.2",

View File

@ -1,12 +1,7 @@
<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;">
<title>回到顶部</title>
<g>
<path d="M12.036 15.59c0 .55-.453.995-.997.995H5.032c-.55 0-.997-.445-.997-.996V8.584H1.03c-1.1 0-1.36-.633-.578-1.416L7.33.29c.39-.39 1.026-.385 1.412 0l6.878 6.88c.782.78.523 1.415-.58 1.415h-3.004v7.004z" fill-rule="evenodd" />
</g>
</svg>
<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>

View File

@ -2,7 +2,7 @@
<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">{{
<span v-if="item.redirect==='noRedirect'||index==levelList.length-1" class="no-redirect">{{
generateTitle(item.meta.title) }}</span>
<a v-else @click.prevent="handleLink(item)">{{ generateTitle(item.meta.title) }}</a>
</el-breadcrumb-item>
@ -31,15 +31,23 @@ export default {
methods: {
generateTitle,
getBreadcrumb() {
let matched = this.$route.matched.filter(item => item.name)
// only show routes with meta.title
let matched = this.$route.matched.filter(item => item.meta && item.meta.title)
const first = matched[0]
if (first && first.name.trim().toLocaleLowerCase() !== 'Dashboard'.toLocaleLowerCase()) {
if (!this.isDashboard(first)) {
matched = [{ path: '/dashboard', meta: { title: 'dashboard' }}].concat(matched)
}
this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
},
isDashboard(route) {
const name = route && route.name
if (!name) {
return false
}
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

View File

@ -0,0 +1,155 @@
<template>
<div :id="id" :class="className" :style="{height:height,width:width}" />
</template>
<script>
import echarts from 'echarts'
import resize from './mixins/resize'
export default {
mixins: [resize],
props: {
className: {
type: String,
default: 'chart'
},
id: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '200px'
},
height: {
type: String,
default: '200px'
}
},
data() {
return {
chart: null
}
},
mounted() {
this.initChart()
},
beforeDestroy() {
if (!this.chart) {
return
}
this.chart.dispose()
this.chart = null
},
methods: {
initChart() {
this.chart = echarts.init(document.getElementById(this.id))
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)
}
this.chart.setOption({
backgroundColor: '#08263a',
grid: {
left: '5%',
right: '5%'
},
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']
}
},
yAxis: {
axisLine: {
show: false
},
axisLabel: {
textStyle: {
color: '#4a657a'
}
},
splitLine: {
show: true,
lineStyle: {
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'
}
},
areaStyle: {
normal: {
color: '#08263a',
shadowBlur: 50,
shadowColor: '#000'
}
}
}, {
name: 'front',
type: 'bar',
data,
xAxisIndex: 1,
z: 3,
itemStyle: {
normal: {
barBorderRadius: 5
}
}
}],
animationEasing: 'elasticOut',
animationEasingUpdate: 'elasticOut',
animationDelay(idx) {
return idx * 20
},
animationDelayUpdate(idx) {
return idx * 20
}
})
}
}
}
</script>

View File

@ -1,156 +0,0 @@
<template>
<div :id="id" :class="className" :style="{height:height,width:width}" />
</template>
<script>
import echarts from 'echarts'
import resize from './mixins/resize'
export default {
mixins: [resize],
props: {
className: {
type: String,
default: 'chart'
},
id: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '200px'
},
height: {
type: String,
default: '200px'
}
},
data() {
return {
chart: null
}
},
mounted() {
this.initChart()
},
beforeDestroy() {
if (!this.chart) {
return
}
this.chart.dispose()
this.chart = null
},
methods: {
initChart() {
this.chart = echarts.init(document.getElementById(this.id))
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)
}
this.chart.setOption(
{
backgroundColor: '#08263a',
grid: {
left: '5%',
right: '5%'
},
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']
}
},
yAxis: {
axisLine: {
show: false
},
axisLabel: {
textStyle: {
color: '#4a657a'
}
},
splitLine: {
show: true,
lineStyle: {
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'
}
},
areaStyle: {
normal: {
color: '#08263a',
shadowBlur: 50,
shadowColor: '#000'
}
}
}, {
name: 'front',
type: 'bar',
data,
xAxisIndex: 1,
z: 3,
itemStyle: {
normal: {
barBorderRadius: 5
}
}
}],
animationEasing: 'elasticOut',
animationEasingUpdate: 'elasticOut',
animationDelay(idx) {
return idx * 20
},
animationDelayUpdate(idx) {
return idx * 20
}
})
}
}
}
</script>

View File

@ -3,7 +3,7 @@ import { debounce } from '@/utils'
export default {
data() {
return {
sidebarElm: null
$_sidebarElm: null
}
},
mounted() {
@ -14,16 +14,18 @@ export default {
}, 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)
this.sidebarElm && this.sidebarElm.removeEventListener('transitionend', this.sidebarResizeHandler)
this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler)
},
methods: {
sidebarResizeHandler(e) {
// use $_ for mixins properties
// https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
$_sidebarResizeHandler(e) {
if (e.propertyName === 'width') {
this.__resizeHandler()
}

View File

@ -49,13 +49,13 @@ export default {
</script>
<style scoped>
.drag-select >>> .sortable-ghost{
.drag-select >>> .sortable-ghost {
opacity: .8;
color: #fff!important;
background: #42b983!important;
}
.drag-select >>> .el-tag{
.drag-select >>> .el-tag {
cursor: pointer;
}
</style>

View File

@ -121,7 +121,7 @@ export default {
data.title = [...data.title, i18ntitle]
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)

View File

@ -25,8 +25,8 @@ export default {
},
watch: {
value(value) {
const editor_value = this.jsonEditor.getValue()
if (value !== editor_value) {
const editorValue = this.jsonEditor.getValue()
if (value !== editorValue) {
this.jsonEditor.setValue(JSON.stringify(this.value, null, 2))
}
}

View File

@ -15,6 +15,7 @@
</draggable>
</div>
</template>
<script>
import draggable from 'vuedraggable'

View File

@ -9,7 +9,7 @@ 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 './defaultOptions'
import defaultOptions from './default-options'
export default {
name: 'MarddownEditor',

View File

@ -15,7 +15,7 @@
</template>
<script>
import { scrollTo } from '@/utils/scrollTo'
import { scrollTo } from '@/utils/scroll-to'
export default {
name: 'Pagination',

View File

@ -8,19 +8,28 @@
</template>
<script>
const version = require('element-ui/package.json').version // element-ui version from node_modules
const ORIGINAL_THEME = '#409EFF' // default color
import defaultSettings from '@/settings'
export default {
data() {
return {
chalk: '', // content of theme-chalk css
theme: defaultSettings.theme
theme: ''
}
},
computed: {
defaultTheme() {
return this.$store.state.settings.theme
}
},
watch: {
defaultTheme: {
handler: function(val, oldVal) {
this.theme = val
},
immediate: true
},
async theme(val) {
const oldVal = this.chalk ? this.theme : ORIGINAL_THEME
if (typeof val !== 'string') return

View File

@ -1,7 +1,7 @@
<template>
<div class="upload-container">
<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">
<el-upload
@ -16,14 +16,14 @@
list-type="picture-card"
>
<el-button size="small" type="primary">
点击上传
Click upload
</el-button>
</el-upload>
<el-button @click="dialogVisible = false">
Cancel
</el-button>
<el-button type="primary" @click="handleSubmit">
Confirm
</el-button>
</el-dialog>
</div>
@ -54,7 +54,7 @@ export default {
handleSubmit() {
const arr = Object.keys(this.listObj).map(v => this.listObj[v])
if (!this.checkAllSuccess()) {
this.$message('请等待所有图片上传成功 或 出现了网络问题,请刷新页面重新上传!')
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)

View File

@ -8,7 +8,7 @@
</template>
<script>
import editorImage from './components/editorImage'
import editorImage from './components/EditorImage'
import plugins from './plugins'
import toolbar from './toolbar'

View File

@ -26,7 +26,6 @@
</template>
<script>
//
import { getToken } from '@/api/qiniu'
export default {

View File

@ -1,11 +1,10 @@
import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event'
/**
* How to use
* <el-table height="100px" v-el-height-adaptive-table="{bottomOffset: 30}">...</el-table>
* el-table height is must be set
* bottomOffset: 30(default) // The height of the table from the bottom of the page.
* bottomOffset: 30(default) // The height of the table from the bottom of the page.
*/
const doResize = (el, binding, vnode) => {
@ -30,13 +29,13 @@ export default {
el.resizeListener = () => {
doResize(el, binding, vnode)
}
addResizeListener(el, el.resizeListener)
// parameter 1 is must be "Element" type
addResizeListener(window.document.body, el.resizeListener)
},
inserted(el, binding, vnode) {
doResize(el, binding, vnode)
},
unbind(el) {
removeResizeListener(el, el.resizeListener)
removeResizeListener(window.document.body, el.resizeListener)
}
}

View File

@ -1,4 +1,3 @@
import adaptive from './adaptive'
const install = function(Vue) {

View File

@ -1,4 +1,3 @@
import store from '@/store'
export default {

View File

@ -1,6 +1,12 @@
// set function parseTime,formatTime to filter
// import parseTime, formatTime and set to filter
export { parseTime, formatTime } from '@/utils'
/**
* Show plural label if time is plural number
* @param {number} time
* @param {string} label
* @return {string}
*/
function pluralize(time, label) {
if (time === 1) {
return time + label
@ -8,6 +14,9 @@ function pluralize(time, label) {
return time + label + 's'
}
/**
* @param {number} time
*/
export function timeAgo(time) {
const between = Date.now() / 1000 - Number(time)
if (between < 3600) {
@ -19,7 +28,12 @@ export function timeAgo(time) {
}
}
/* 数字 格式化*/
/**
* Number formatting
* like 10000 => 10k
* @param {number} num
* @param {number} digits
*/
export function numberFormatter(num, digits) {
const si = [
{ value: 1E18, symbol: 'E' },
@ -37,6 +51,10 @@ export function numberFormatter(num, digits) {
return num.toString()
}
/**
* 10000 => "10,000"
* @param {number} num
*/
export function toThousandFilter(num) {
return (+num || 0).toString().replace(/^-?\d+/g, m => m.replace(/(?=(?!\b)(\d{3})+$)/g, ','))
}

View File

@ -1,7 +1,6 @@
export default {
route: {
dashboard: 'Dashboard',
introduction: 'Introduction',
documentation: 'Documentation',
guide: 'Guide',
permission: 'Permission',
@ -10,7 +9,6 @@ export default {
directivePermission: 'Directive Permission',
icons: 'Icons',
components: 'Components',
componentIndex: 'Introduction',
tinymce: 'Tinymce',
markdown: 'Markdown',
jsonEditor: 'JSON Editor',
@ -19,9 +17,9 @@ export default {
avatarUpload: 'Avatar Upload',
dropzone: 'Dropzone',
sticky: 'Sticky',
countTo: 'CountTo',
countTo: 'Count To',
componentMixin: 'Mixin',
backToTop: 'BackToTop',
backToTop: 'Back To Top',
dragDialog: 'Drag Dialog',
dragSelect: 'Drag Select',
dragKanban: 'Drag Kanban',
@ -74,7 +72,7 @@ export default {
},
login: {
title: 'Login Form',
logIn: 'Log in',
logIn: 'Login',
username: 'Username',
password: 'Password',
any: 'any',
@ -87,10 +85,10 @@ export default {
},
permission: {
addRole: 'New Role',
editPermission: 'Edit Permission',
editPermission: 'Edit',
roles: 'Your roles',
switchRoles: 'Switch roles',
tips: 'In some cases it is not suitable to use v-permission, such as element Tab component or el-table-column and other asynchronous rendering dom cases which can only be achieved by manually setting the v-if.',
tips: 'In some cases, using v-permission will have no effect. For example: Element-UI el-tab or el-table-column and other scenes that dynamically render dom. You can only do this with v-if.',
delete: 'Delete',
confirm: 'Confirm',
cancel: 'Cancel'
@ -101,7 +99,7 @@ export default {
},
components: {
documentation: 'Documentation',
tinymceTips: 'Rich text editor is a core part of management system, but at the same time is a place with lots of problems. In the process of selecting rich texts, I also walked a lot of detours. The common rich text editors in the market are basically used, and the finally chose Tinymce. See documentation for more detailed rich text editor comparisons and introductions.',
tinymceTips: 'Rich text is a core feature of the management backend, but at the same time it is a place with lots of pits. In the process of selecting rich texts, I also took a lot of detours. The common rich texts on the market have been basically used, and I finally chose Tinymce. See the more detailed rich text comparison and introduction.',
dropzoneTips: 'Because my business has special needs, and has to upload images to qiniu, so instead of a third party, I chose encapsulate it by myself. It is very simple, you can see the detail code in @/components/Dropzone.',
stickyTips: 'when the page is scrolled to the preset position will be sticky on the top.',
backToTopTips1: 'When the page is scrolled to the specified position, the Back to Top button appears in the lower right corner',
@ -134,6 +132,9 @@ export default {
cancel: 'Cancel',
confirm: 'Confirm'
},
example: {
warning: 'Creating and editing pages cannot be cached by keep-alive because keep-alive include does not currently support caching based on routes, so it is currently cached based on component name. If you want to achieve a similar caching effect, you can use a browser caching scheme such as localStorage. Or do not use keep-alive include to cache all pages directly. See details'
},
errorLog: {
tips: 'Please click the bug icon in the upper right corner',
description: 'Now the management system are basically the form of the spa, it enhances the user experience, but it also increases the possibility of page problems, a small negligence may lead to the entire page deadlock. Fortunately Vue provides a way to catch handling exceptions, where you can handle errors or report exceptions.',
@ -142,14 +143,14 @@ export default {
excel: {
export: 'Export',
selectedExport: 'Export Selected Items',
placeholder: 'Please enter the file name(default excel-list)'
placeholder: 'Please enter the file name (default excel-list)'
},
zip: {
export: 'Export',
placeholder: 'Please enter the file name(default file)'
placeholder: 'Please enter the file name (default file)'
},
pdf: {
tips: 'Here we use window.print() to implement the feature of downloading pdf.'
tips: 'Here we use window.print() to implement the feature of downloading PDF.'
},
theme: {
change: 'Change Theme',

View File

@ -1,7 +1,6 @@
export default {
route: {
dashboard: 'Panel de control',
introduction: 'Introducción',
documentation: 'Documentación',
guide: 'Guía',
permission: 'Permisos',
@ -10,7 +9,6 @@ export default {
directivePermission: 'Permisos de la directiva',
icons: 'Iconos',
components: 'Componentes',
componentIndex: 'Introducción',
tinymce: 'Tinymce',
markdown: 'Markdown',
jsonEditor: 'Editor JSON',
@ -134,6 +132,9 @@ export default {
cancel: 'Cancelar',
confirm: 'Confirmar'
},
example: {
warning: 'Creating and editing pages cannot be cached by keep-alive because keep-alive include does not currently support caching based on routes, so it is currently cached based on component name. If you want to achieve a similar caching effect, you can use a browser caching scheme such as localStorage. Or do not use keep-alive include to cache all pages directly. See details'
},
errorLog: {
tips: 'Please click the bug icon in the upper right corner',
description: 'Now the management system are basically the form of the spa, it enhances the user experience, but it also increases the possibility of page problems, a small negligence may lead to the entire page deadlock. Fortunately Vue provides a way to catch handling exceptions, where you can handle errors or report exceptions.',

View File

@ -1,7 +1,6 @@
export default {
route: {
dashboard: '首页',
introduction: '简述',
documentation: '文档',
guide: '引导页',
permission: '权限测试页',
@ -10,16 +9,15 @@ export default {
directivePermission: '指令权限',
icons: '图标',
components: '组件',
componentIndex: '介绍',
tinymce: '富文本编辑器',
markdown: 'Markdown',
jsonEditor: 'JSON编辑器',
jsonEditor: 'JSON 编辑器',
dndList: '列表拖拽',
splitPane: 'Splitpane',
avatarUpload: '头像上传',
dropzone: 'Dropzone',
sticky: 'Sticky',
countTo: 'CountTo',
countTo: 'Count To',
componentMixin: '小组件',
backToTop: '返回顶部',
dragDialog: '拖拽 Dialog',
@ -32,17 +30,17 @@ export default {
example: '综合实例',
nested: '路由嵌套',
menu1: '菜单1',
'menu1-1': '菜单1-1',
'menu1-2': '菜单1-2',
'menu1-2-1': '菜单1-2-1',
'menu1-2-2': '菜单1-2-2',
'menu1-3': '菜单1-3',
menu2: '菜单2',
'menu1-1': '菜单 1-1',
'menu1-2': '菜单 1-2',
'menu1-2-1': '菜单 1-2-1',
'menu1-2-2': '菜单 1-2-2',
'menu1-3': '菜单 1-3',
menu2: '菜单 2',
Table: 'Table',
dynamicTable: '动态Table',
dragTable: '拖拽Table',
inlineEditTable: 'Table内编辑',
complexTable: '综合Table',
dynamicTable: '动态 Table',
dragTable: '拖拽 Table',
inlineEditTable: 'Table 内编辑',
complexTable: '综合 Table',
tab: 'Tab',
form: '表单',
createArticle: '创建文章',
@ -90,7 +88,7 @@ export default {
editPermission: '编辑权限',
roles: '你的权限',
switchRoles: '切换权限',
tips: '在某些情况下,不适合使用 v-permission。例如Element-UI 的 Tab 组件或 el-table-column 以及其它动态渲染 dom 的场景。你只能通过手动设置 v-if 来实现。',
tips: '在某些情况下,不适合使用 v-permission。例如Element-UI 的 el-tab 或 el-table-column 以及其它动态渲染 dom 的场景。你只能通过手动设置 v-if 来实现。',
delete: '删除',
confirm: '确定',
cancel: '取消'
@ -134,6 +132,9 @@ export default {
cancel: '取 消',
confirm: '确 定'
},
example: {
warning: '创建和编辑页面是不能被 keep-alive 缓存的因为keep-alive 的 include 目前不支持根据路由来缓存,所以目前都是基于 component name 来进行缓存的。如果你想类似的实现缓存效果,可以使用 localStorage 等浏览器缓存方案。或者不要使用 keep-alive 的 include直接缓存所有页面。详情见'
},
errorLog: {
tips: '请点击右上角bug小图标',
description: '现在的管理后台基本都是spa的形式了它增强了用户体验但同时也会增加页面出问题的可能性可能一个小小的疏忽就导致整个页面的死锁。好在 Vue 官网提供了一个方法来捕获处理异常,你可以在其中进行错误处理或者异常上报。',

View File

@ -86,6 +86,9 @@ export default {
if (isExternal(routePath)) {
return routePath
}
if (isExternal(this.basePath)) {
return this.basePath
}
return path.resolve(this.basePath, routePath)
},

View File

@ -3,10 +3,11 @@
<logo v-if="showLogo" :collapse="isCollapse" />
<el-scrollbar wrap-class="scrollbar-wrapper">
<el-menu
:default-active="$route.path"
:default-active="activeMenu"
:collapse="isCollapse"
:background-color="variables.menuBg"
:text-color="variables.menuText"
:unique-opened="false"
:active-text-color="variables.menuActiveText"
:collapse-transition="false"
mode="vertical"
@ -30,6 +31,15 @@ export default {
'permission_routes',
'sidebar'
]),
activeMenu() {
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 path
},
showLogo() {
return this.$store.state.settings.sidebarLogo
},

View File

@ -145,7 +145,7 @@ export default {
closeSelectedTag(view) {
this.$store.dispatch('tagsView/delView', view).then(({ visitedViews }) => {
if (this.isActive(view)) {
this.toLastView(visitedViews)
this.toLastView(visitedViews, view)
}
})
},
@ -160,16 +160,22 @@ export default {
if (this.affixTags.some(tag => tag.path === view.path)) {
return
}
this.toLastView(visitedViews)
this.toLastView(visitedViews, view)
})
},
toLastView(visitedViews) {
toLastView(visitedViews, view) {
const latestView = visitedViews.slice(-1)[0]
if (latestView) {
this.$router.push(latestView)
} else {
// You can set another route
this.$router.push('/')
// 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') {
// to reload home page
this.$router.replace({ path: '/redirect' + view.fullPath })
} else {
this.$router.push('/')
}
}
},
openMenu(tag, e) {

View File

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

View File

@ -17,19 +17,19 @@
<script>
import RightPanel from '@/components/RightPanel'
import { Navbar, Sidebar, AppMain, TagsView, Settings } from './components'
import { AppMain, Navbar, Settings, Sidebar, TagsView } from './components'
import ResizeMixin from './mixin/ResizeHandler'
import { mapState } from 'vuex'
export default {
name: 'Layout',
components: {
RightPanel,
Navbar,
Sidebar,
AppMain,
TagsView,
Settings
Navbar,
RightPanel,
Settings,
Sidebar,
TagsView
},
mixins: [ResizeMixin],
computed: {

View File

@ -12,26 +12,28 @@ export default {
}
},
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 })
}
},
methods: {
isMobile() {
// 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
},
resizeHandler() {
$_resizeHandler() {
if (!document.hidden) {
const isMobile = this.isMobile()
const isMobile = this.$_isMobile()
store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop')
if (isMobile) {

View File

@ -2,7 +2,7 @@ import Vue from 'vue'
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'
@ -13,24 +13,28 @@ import App from './App'
import store from './store'
import router from './router'
import i18n from './lang' // Internationalization
import i18n from './lang' // internationalization
import './icons' // icon
import './permission' // permission control
import './utils/errorLog' // error log
import './utils/error-log' // error log
import * as filters from './filters' // global filters
import { mockXHR } from '../mock' // simulation data
// mock api in github pages site build
if (process.env.NODE_ENV === 'production') { mockXHR() }
/**
* If you don't want to use mock-server
* you want to use mockjs for request interception
* you can execute:
*
* import { mockXHR } from '../mock'
* mockXHR()
*/
Vue.use(Element, {
size: Cookies.get('size') || 'medium', // set element-ui default size
i18n: (key, value) => i18n.t(key, value)
})
// register global utility filters.
// register global utility filters
Object.keys(filters).forEach(key => {
Vue.filter(key, filters[key])
})

View File

@ -4,6 +4,7 @@ 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
@ -13,6 +14,9 @@ router.beforeEach(async(to, from, next) => {
// start progress bar
NProgress.start()
// set page title
document.title = getPageTitle(to.meta.title)
// determine whether the user has logged in
const hasToken = getToken()

View File

@ -12,32 +12,32 @@ import chartsRouter from './modules/charts'
import tableRouter from './modules/table'
import nestedRouter from './modules/nested'
/** note: sub-menu only appear when children.length>=1
* detail see https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
**/
/**
* hidden: true if `hidden:true` will not show in the sidebar(default is false)
* alwaysShow: true if set true, will always show the root menu, whatever its child routes length
* if not set alwaysShow, only more than one route under the children
* it will becomes nested mode, otherwise not show the root menu
* redirect: noredirect if `redirect:noredirect` will no redirect in the breadcrumb
* name:'router-name' the name is used by <keep-alive> (must set!!!)
* meta : {
roles: ['admin','editor'] will control the page roles (you can set multiple roles)
title: 'title' the name show in sub-menu and breadcrumb (recommend set)
* Note: sub-menu only appear when route children.length >= 1
* Detail see: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
*
* hidden: true if set true, item will not show in the sidebar(default is false)
* alwaysShow: true if set true, will always show the root menu
* if not set alwaysShow, when item has more than one children route,
* it will becomes nested mode, otherwise not show the root menu
* redirect: noRedirect if set noRedirect will no redirect in the breadcrumb
* name:'router-name' the name is used by <keep-alive> (must set!!!)
* meta : {
roles: ['admin','editor'] control the page roles (you can set multiple roles)
title: 'title' the name show in sidebar and breadcrumb (recommend set)
icon: 'svg-name' the icon show in the sidebar
noCache: true if true, the page will no be cached(default is false)
breadcrumb: false if false, the item will hidden in breadcrumb(default is true)
affix: true if true, the tag will affix in the tags-view
noCache: true if set true, the page will no be cached(default is false)
affix: true if set true, the tag will affix in the tags-view
breadcrumb: false if set false, the item will hidden in breadcrumb(default is true)
activeMenu: '/example/list' if set path, the sidebar will highlight the path you set
}
**/
*/
/**
* constantRoutes
* a base page that does not have permission requirements
* all roles can be accessed
* */
*/
export const constantRoutes = [
{
path: '/redirect',
@ -57,17 +57,17 @@ export const constantRoutes = [
},
{
path: '/auth-redirect',
component: () => import('@/views/login/authRedirect'),
component: () => import('@/views/login/auth-redirect'),
hidden: true
},
{
path: '/404',
component: () => import('@/views/errorPage/404'),
component: () => import('@/views/error-page/404'),
hidden: true
},
{
path: '/401',
component: () => import('@/views/errorPage/401'),
component: () => import('@/views/error-page/401'),
hidden: true
},
{
@ -113,13 +113,14 @@ export const constantRoutes = [
/**
* asyncRoutes
* the routes that need to be dynamically loaded based on user roles
*/
*/
export const asyncRoutes = [
{
path: '/permission',
component: Layout,
redirect: '/permission/index',
redirect: '/permission/page',
alwaysShow: true, // will always show the root menu
name: 'Permission',
meta: {
title: 'permission',
icon: 'lock',
@ -162,7 +163,7 @@ export const asyncRoutes = [
children: [
{
path: 'index',
component: () => import('@/views/svg-icons/index'),
component: () => import('@/views/icons/index'),
name: 'Icons',
meta: { title: 'icons', icon: 'icon', noCache: true }
}
@ -195,7 +196,7 @@ export const asyncRoutes = [
path: 'edit/:id(\\d+)',
component: () => import('@/views/example/edit'),
name: 'EditArticle',
meta: { title: 'editArticle', noCache: true },
meta: { title: 'editArticle', noCache: true, activeMenu: '/example/list' },
hidden: true
},
{
@ -223,7 +224,7 @@ export const asyncRoutes = [
{
path: '/error',
component: Layout,
redirect: 'noredirect',
redirect: 'noRedirect',
name: 'ErrorPages',
meta: {
title: 'errorPages',
@ -232,13 +233,13 @@ export const asyncRoutes = [
children: [
{
path: '401',
component: () => import('@/views/errorPage/401'),
component: () => import('@/views/error-page/401'),
name: 'Page401',
meta: { title: 'page401', noCache: true }
},
{
path: '404',
component: () => import('@/views/errorPage/404'),
component: () => import('@/views/error-page/404'),
name: 'Page404',
meta: { title: 'page404', noCache: true }
}
@ -248,11 +249,10 @@ export const asyncRoutes = [
{
path: '/error-log',
component: Layout,
redirect: 'noredirect',
children: [
{
path: 'log',
component: () => import('@/views/errorLog/index'),
component: () => import('@/views/error-log/index'),
name: 'ErrorLog',
meta: { title: 'errorLog', icon: 'bug' }
}
@ -271,25 +271,25 @@ export const asyncRoutes = [
children: [
{
path: 'export-excel',
component: () => import('@/views/excel/exportExcel'),
component: () => import('@/views/excel/export-excel'),
name: 'ExportExcel',
meta: { title: 'exportExcel' }
},
{
path: 'export-selected-excel',
component: () => import('@/views/excel/selectExcel'),
component: () => import('@/views/excel/select-excel'),
name: 'SelectExcel',
meta: { title: 'selectExcel' }
},
{
path: 'export-merge-header',
component: () => import('@/views/excel/mergeHeader'),
component: () => import('@/views/excel/merge-header'),
name: 'MergeHeader',
meta: { title: 'mergeHeader' }
},
{
path: 'upload-excel',
component: () => import('@/views/excel/uploadExcel'),
component: () => import('@/views/excel/upload-excel'),
name: 'UploadExcel',
meta: { title: 'uploadExcel' }
}
@ -301,6 +301,7 @@ export const asyncRoutes = [
component: Layout,
redirect: '/zip/download',
alwaysShow: true,
name: 'Zip',
meta: { title: 'zip', icon: 'zip' },
children: [
{
@ -334,7 +335,6 @@ export const asyncRoutes = [
{
path: '/theme',
component: Layout,
redirect: 'noredirect',
children: [
{
path: 'index',
@ -348,7 +348,6 @@ export const asyncRoutes = [
{
path: '/clipboard',
component: Layout,
redirect: 'noredirect',
children: [
{
path: 'index',

View File

@ -5,7 +5,7 @@ import Layout from '@/layout'
const chartsRouter = {
path: '/charts',
component: Layout,
redirect: 'noredirect',
redirect: 'noRedirect',
name: 'Charts',
meta: {
title: 'charts',
@ -25,8 +25,8 @@ const chartsRouter = {
meta: { title: 'lineChart', noCache: true }
},
{
path: 'mixchart',
component: () => import('@/views/charts/mixChart'),
path: 'mix-chart',
component: () => import('@/views/charts/mix-chart'),
name: 'MixChart',
meta: { title: 'mixChart', noCache: true }
}

View File

@ -1,11 +1,11 @@
/** When your routing table is too long, you can split it into small modules**/
/** When your routing table is too long, you can split it into small modules **/
import Layout from '@/layout'
const componentsRouter = {
path: '/components',
component: Layout,
redirect: 'noredirect',
redirect: 'noRedirect',
name: 'ComponentDemo',
meta: {
title: 'components',
@ -26,19 +26,19 @@ const componentsRouter = {
},
{
path: 'json-editor',
component: () => import('@/views/components-demo/jsonEditor'),
component: () => import('@/views/components-demo/json-editor'),
name: 'JsonEditorDemo',
meta: { title: 'jsonEditor' }
},
{
path: 'splitpane',
component: () => import('@/views/components-demo/splitpane'),
path: 'split-pane',
component: () => import('@/views/components-demo/split-pane'),
name: 'SplitpaneDemo',
meta: { title: 'splitPane' }
},
{
path: 'avatar-upload',
component: () => import('@/views/components-demo/avatarUpload'),
component: () => import('@/views/components-demo/avatar-upload'),
name: 'AvatarUploadDemo',
meta: { title: 'avatarUpload' }
},
@ -56,7 +56,7 @@ const componentsRouter = {
},
{
path: 'count-to',
component: () => import('@/views/components-demo/countTo'),
component: () => import('@/views/components-demo/count-to'),
name: 'CountToDemo',
meta: { title: 'countTo' }
},
@ -68,31 +68,31 @@ const componentsRouter = {
},
{
path: 'back-to-top',
component: () => import('@/views/components-demo/backToTop'),
component: () => import('@/views/components-demo/back-to-top'),
name: 'BackToTopDemo',
meta: { title: 'backToTop' }
},
{
path: 'drag-dialog',
component: () => import('@/views/components-demo/dragDialog'),
component: () => import('@/views/components-demo/drag-dialog'),
name: 'DragDialogDemo',
meta: { title: 'dragDialog' }
},
{
path: 'drag-select',
component: () => import('@/views/components-demo/dragSelect'),
component: () => import('@/views/components-demo/drag-select'),
name: 'DragSelectDemo',
meta: { title: 'dragSelect' }
},
{
path: 'dnd-list',
component: () => import('@/views/components-demo/dndList'),
component: () => import('@/views/components-demo/dnd-list'),
name: 'DndListDemo',
meta: { title: 'dndList' }
},
{
path: 'drag-kanban',
component: () => import('@/views/components-demo/dragKanban'),
component: () => import('@/views/components-demo/drag-kanban'),
name: 'DragKanbanDemo',
meta: { title: 'dragKanban' }
}

View File

@ -1,4 +1,4 @@
/** When your routing table is too long, you can split it into small modules**/
/** When your routing table is too long, you can split it into small modules **/
import Layout from '@/layout'

View File

@ -1,4 +1,4 @@
/** When your routing table is too long, you can split it into small modules**/
/** When your routing table is too long, you can split it into small modules **/
import Layout from '@/layout'
@ -14,25 +14,25 @@ const tableRouter = {
children: [
{
path: 'dynamic-table',
component: () => import('@/views/table/dynamicTable/index'),
component: () => import('@/views/table/dynamic-table/index'),
name: 'DynamicTable',
meta: { title: 'dynamicTable' }
},
{
path: 'drag-table',
component: () => import('@/views/table/dragTable'),
component: () => import('@/views/table/drag-table'),
name: 'DragTable',
meta: { title: 'dragTable' }
},
{
path: 'inline-edit-table',
component: () => import('@/views/table/inlineEditTable'),
component: () => import('@/views/table/inline-edit-table'),
name: 'InlineEditTable',
meta: { title: 'inlineEditTable' }
},
{
path: 'complex-table',
component: () => import('@/views/table/complexTable'),
component: () => import('@/views/table/complex-table'),
name: 'ComplexTable',
meta: { title: 'complexTable' }
}

View File

@ -1,7 +1,5 @@
import variables from '@/styles/element-variables.scss'
export default {
theme: variables.theme,
module.exports = {
title: 'Vue Element Admin',
/**
* @type {boolean} true | false

View File

@ -1,7 +1,7 @@
import { asyncRoutes, constantRoutes } from '@/router'
/**
* 通过meta.role判断是否与当前用户权限匹配
* Use meta.role to determine if the current user has permission
* @param roles
* @param route
*/
@ -14,7 +14,7 @@ function hasPermission(roles, route) {
}
/**
* 递归过滤异步路由表返回符合用户角色权限的路由表
* Filter asynchronous routing tables by recursion
* @param routes asyncRoutes
* @param roles
*/

View File

@ -1,8 +1,10 @@
import variables from '@/styles/element-variables.scss'
import defaultSettings from '@/settings'
const { showSettings, tagsView, fixedHeader, sidebarLogo, theme } = defaultSettings
const { showSettings, tagsView, fixedHeader, sidebarLogo } = defaultSettings
const state = {
theme: theme,
theme: variables.theme,
showSettings: showSettings,
tagsView: tagsView,
fixedHeader: fixedHeader,

View File

@ -1,4 +1,4 @@
//覆盖一些element-ui样式
// cover some element-ui styles
.el-breadcrumb__inner,
.el-breadcrumb__inner a {
@ -46,7 +46,7 @@
}
}
//暂时性解决dialog 问题 https://github.com/ElemeFE/element/issues/2461
// to fixed https://github.com/ElemeFE/element/issues/2461
.el-dialog {
transform: none;
left: 0;
@ -54,18 +54,7 @@
margin: 0 auto;
}
//文章页textarea修改样式
.article-textarea {
textarea {
padding-right: 40px;
resize: none;
border: none;
border-radius: 0px;
border-bottom: 1px solid #bfcbd9;
}
}
//element ui upload
// refine element ui upload
.upload-container {
.el-upload {
width: 100%;
@ -77,9 +66,14 @@
}
}
//dropdown
// dropdown
.el-dropdown-menu {
a {
display: block
}
}
// fix date-picker ui bug in filter-item
.el-range-editor.el-input__inner {
display: inline-flex !important;
}

View File

@ -96,14 +96,18 @@ div:focus {
}
}
code {
aside {
background: #eef1f6;
padding: 15px 16px;
padding: 8px 24px;
margin-bottom: 20px;
border-radius: 2px;
display: block;
line-height: 36px;
font-size: 15px;
font-family: "Source Sans Pro", "Helvetica Neue", Arial, sans-serif;
line-height: 32px;
font-size: 16px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
color: #2c3e50;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
a {
color: #337ab7;
@ -115,20 +119,6 @@ code {
}
}
.warn-content {
background: rgba(66, 185, 131, .1);
border-radius: 2px;
padding: 16px;
padding: 1rem;
line-height: 1.6rem;
word-spacing: .05rem;
a {
color: #42b983;
font-weight: 600;
}
}
//main-container全局样式
.app-container {
padding: 20px;

View File

@ -1,6 +1,5 @@
#app {
// 主体区域 Main container
.main-container {
min-height: 100%;
transition: margin-left .28s;
@ -8,10 +7,10 @@
position: relative;
}
// 侧边栏 Sidebar container
.sidebar-container {
transition: width 0.28s;
width: $sideBarWidth !important;
background-color: $menuBg;
height: 100%;
position: fixed;
font-size: 0px;
@ -21,17 +20,13 @@
z-index: 1001;
overflow: hidden;
//reset element-ui css
// reset element-ui css
.horizontal-collapse-transition {
transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;
}
.scrollbar-wrapper {
overflow-x: hidden !important;
.el-scrollbar__view {
height: 100%;
}
}
.el-scrollbar__bar.is-vertical {
@ -152,7 +147,7 @@
min-width: $sideBarWidth !important;
}
// 适配移动端, Mobile responsive
// mobile responsive
.mobile {
.main-container {
margin-left: 0px;

View File

@ -1,6 +1,6 @@
//global transition css
// global transition css
/*fade*/
/* fade */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.28s;
@ -11,7 +11,7 @@
opacity: 0;
}
/*fade-transform*/
/* fade-transform */
.fade-transform-leave-active,
.fade-transform-enter-active {
transition: all .5s;
@ -27,7 +27,7 @@
transform: translateX(30px);
}
/*breadcrumb transition*/
/* breadcrumb transition */
.breadcrumb-enter-active,
.breadcrumb-leave-active {
transition: all .5s;

View File

@ -8,10 +8,10 @@ $tiffany: #4AB7BD;
$yellow:#FEC171;
$panGreen: #30B08F;
//sidebar
// sidebar
$menuText:#bfcbd9;
$menuActiveText:#409EFF;
$subMenuActiveText:#f4f4f5; //https://github.com/ElemeFE/element/issues/12951
$subMenuActiveText:#f4f4f5; // https://github.com/ElemeFE/element/issues/12951
$menuBg:#304156;
$menuHover:#263445;

View File

@ -4,10 +4,10 @@ import { isString, isArray } from '@/utils/validate'
import settings from '@/settings'
// you can set in settings.js
// errorLog:'production' | ['production','development']
// errorLog:'production' | ['production', 'development']
const { errorLog: needErrorLog } = settings
function checkNeed(arg) {
function checkNeed() {
const env = process.env.NODE_ENV
if (isString(needErrorLog)) {
return env === needErrorLog

View File

@ -0,0 +1,13 @@
import defaultSettings from '@/settings'
import i18n from '@/lang'
const title = defaultSettings.title || 'Vue Element Admin'
export default function getPageTitle(key) {
const hasKey = i18n.te(`route.${key}`)
if (hasKey) {
const pageName = i18n.t(`route.${key}`)
return `${pageName} - ${title}`
}
return `${title}`
}

View File

@ -2,6 +2,12 @@
* Created by jiachenpan on 16/11/18.
*/
/**
* Parse the time to string
* @param {(Object|string|number)} time
* @param {string} cFormat
* @returns {string}
*/
export function parseTime(time, cFormat) {
if (arguments.length === 0) {
return null
@ -40,6 +46,11 @@ export function parseTime(time, cFormat) {
return time_str
}
/**
* @param {number} time
* @param {string} option
* @returns {string}
*/
export function formatTime(time, option) {
if (('' + time).length === 10) {
time = parseInt(time) * 1000
@ -78,7 +89,10 @@ export function formatTime(time, option) {
}
}
// 格式化时间
/**
* @param {string} url
* @returns {Object}
*/
export function getQueryObject(url) {
url = url == null ? window.location.href : url
const search = url.substring(url.lastIndexOf('?') + 1)
@ -95,7 +109,7 @@ export function getQueryObject(url) {
}
/**
* @param {Sting} input value
* @param {string} input value
* @returns {number} output value
*/
export function byteLength(str) {
@ -110,6 +124,10 @@ export function byteLength(str) {
return s
}
/**
* @param {Array} actual
* @returns {Array}
*/
export function cleanArray(actual) {
const newArray = []
for (let i = 0; i < actual.length; i++) {
@ -120,6 +138,10 @@ export function cleanArray(actual) {
return newArray
}
/**
* @param {Object} json
* @returns {Array}
*/
export function param(json) {
if (!json) return ''
return cleanArray(
@ -130,6 +152,10 @@ export function param(json) {
).join('&')
}
/**
* @param {string} url
* @returns {Object}
*/
export function param2Obj(url) {
const search = url.split('?')[1]
if (!search) {
@ -146,16 +172,23 @@ export function param2Obj(url) {
)
}
/**
* @param {string} val
* @returns {string}
*/
export function html2Text(val) {
const div = document.createElement('div')
div.innerHTML = val
return div.textContent || div.innerText
}
/**
* Merges two objects, giving the last one precedence
* @param {Object} target
* @param {(Object|Array)} source
* @returns {Object}
*/
export function objectMerge(target, source) {
/* Merges two objects,
giving the last one precedence */
if (typeof target !== 'object') {
target = {}
}
@ -173,6 +206,10 @@ export function objectMerge(target, source) {
return target
}
/**
* @param {HTMLElement} element
* @param {string} className
*/
export function toggleClass(element, className) {
if (!element || !className) {
return
@ -189,45 +226,10 @@ export function toggleClass(element, className) {
element.className = classString
}
export const pickerOptions = [
{
text: '今天',
onClick(picker) {
const end = new Date()
const start = new Date(new Date().toDateString())
end.setTime(start.getTime())
picker.$emit('pick', [start, end])
}
},
{
text: '最近一周',
onClick(picker) {
const end = new Date(new Date().toDateString())
const start = new Date()
start.setTime(end.getTime() - 3600 * 1000 * 24 * 7)
picker.$emit('pick', [start, end])
}
},
{
text: '最近一个月',
onClick(picker) {
const end = new Date(new Date().toDateString())
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
picker.$emit('pick', [start, end])
}
},
{
text: '最近三个月',
onClick(picker) {
const end = new Date(new Date().toDateString())
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
picker.$emit('pick', [start, end])
}
}
]
/**
* @param {string} type
* @returns {Date}
*/
export function getTime(type) {
if (type === 'start') {
return new Date().getTime() - 3600 * 1000 * 24 * 90
@ -236,6 +238,12 @@ export function getTime(type) {
}
}
/**
* @param {Function} func
* @param {number} wait
* @param {boolean} immediate
* @return {*}
*/
export function debounce(func, wait, immediate) {
let timeout, args, context, timestamp, result
@ -275,6 +283,8 @@ export function debounce(func, wait, immediate) {
* This is just a simple version of deep copy
* Has a lot of edge cases bug
* If you want to use a perfect deep copy, use lodash's _.cloneDeep
* @param {Object} source
* @returns {Object}
*/
export function deepClone(source) {
if (!source && typeof source !== 'object') {
@ -291,22 +301,47 @@ export function deepClone(source) {
return targetObj
}
/**
* @param {Array} arr
* @returns {Array}
*/
export function uniqueArr(arr) {
return Array.from(new Set(arr))
}
/**
* @returns {string}
*/
export function createUniqueString() {
const timestamp = +new Date() + ''
const randomNum = parseInt((1 + Math.random()) * 65536) + ''
return (+(randomNum + timestamp)).toString(32)
}
/**
* Check if an element has a class
* @param {HTMLElement} elm
* @param {string} cls
* @returns {boolean}
*/
export function hasClass(ele, cls) {
return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'))
}
/**
* Add class to element
* @param {HTMLElement} elm
* @param {string} cls
*/
export function addClass(ele, cls) {
if (!hasClass(ele, cls)) ele.className += ' ' + cls
}
/**
* Remove class from element
* @param {HTMLElement} elm
* @param {string} cls
*/
export function removeClass(ele, cls) {
if (hasClass(ele, cls)) {
const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)')

View File

@ -5,7 +5,6 @@
* @param {Number} w
* @param {Number} h
*/
export default function openWindow(url, title, w, h) {
// Fixes dual-screen position Most browsers Firefox
const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left

View File

@ -5,23 +5,25 @@ import { getToken } from '@/utils/auth'
// create an axios instance
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // api 的 base_url
withCredentials: true, // 跨域请求时发送 cookies
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
withCredentials: true, // send cookies when cross-domain requests
timeout: 5000 // request timeout
})
// request interceptor
service.interceptors.request.use(
config => {
// Do something before request is sent
// do something before request is sent
if (store.getters.token) {
// 让每个请求携带token-- ['X-Token']为自定义key 请根据实际情况自行修改
// let each request carry token --['X-Token'] as a custom key.
// please modify it according to the actual situation.
config.headers['X-Token'] = getToken()
}
return config
},
error => {
// Do something with request error
// do something with request error
console.log(error) // for debug
return Promise.reject(error)
}
@ -33,35 +35,37 @@ service.interceptors.response.use(
* If you want to get information such as headers or status
* Please return response => response
*/
/**
* 下面的注释为通过在response里自定义code来标示请求状态
* 当code返回如下情况则说明权限有问题登出并返回到登录页
* 如想通过 XMLHttpRequest 来状态码标识 逻辑可写在下面error中
* 以下代码均为样例请结合自生需求加以修改若不需要则可删除
* Determine the request status by custom code
* Here is just an example
* You can also judge the status by HTTP Status Code.
*/
response => {
const res = response.data
// if the custom code is not 20000, it is judged as an error.
if (res.code !== 20000) {
Message({
message: res.message,
message: res.message || 'error',
type: 'error',
duration: 5 * 1000
})
// 50008:非法的token; 50012:其他客户端登录了; 50014:Token 过期了;
// 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
// 请自行在引入 MessageBox
// import { Message, MessageBox } from 'element-ui'
MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', {
confirmButtonText: '重新登录',
cancelButtonText: '取消',
// to re-login
MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
confirmButtonText: 'Re-Login',
cancelButtonText: 'Cancel',
type: 'warning'
}).then(() => {
store.dispatch('user/resetToken').then(() => {
location.reload() // 为了重新实例化vue-router对象 避免bug
location.reload()
})
})
}
return Promise.reject('error')
return Promise.reject(res.message || 'error')
} else {
return res
}

View File

@ -12,7 +12,10 @@ var requestAnimFrame = (function() {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) }
})()
// because it's so fucking difficult to detect the scrolling element, just move them all
/**
* Because it's so fucking difficult to detect the scrolling element, just move them all
* @param {number} amount
*/
function move(amount) {
document.documentElement.scrollTop = amount
document.body.parentNode.scrollTop = amount
@ -23,6 +26,11 @@ function position() {
return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop
}
/**
* @param {number} to
* @param {number} duration
* @param {Function} callback
*/
export function scrollTo(to, duration, callback) {
const start = position()
const change = to - start

View File

@ -1,41 +1,72 @@
/**
* Created by jiachenpan on 16/11/18.
*/
/**
* @param {string} path
* @returns {Boolean}
*/
export function isExternal(path) {
return /^(https?:|mailto:|tel:)/.test(path)
}
/**
* @param {string} str
* @returns {Boolean}
*/
export function validUsername(str) {
const valid_map = ['admin', 'editor']
return valid_map.indexOf(str.trim()) >= 0
}
/**
* @param {string} url
* @returns {Boolean}
*/
export function validURL(url) {
const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
return reg.test(url)
}
/**
* @param {string} str
* @returns {Boolean}
*/
export function validLowerCase(str) {
const reg = /^[a-z]+$/
return reg.test(str)
}
/**
* @param {string} str
* @returns {Boolean}
*/
export function validUpperCase(str) {
const reg = /^[A-Z]+$/
return reg.test(str)
}
/**
* @param {string} str
* @returns {Boolean}
*/
export function validAlphabets(str) {
const reg = /^[A-Za-z]+$/
return reg.test(str)
}
/**
* @param {string} email
* @returns {Boolean}
*/
export function validEmail(email) {
const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
return reg.test(email)
}
/**
* @param {string} str
* @returns {Boolean}
*/
export function isString(str) {
if (typeof str === 'string' || str instanceof String) {
return true
@ -43,6 +74,10 @@ export function isString(str) {
return false
}
/**
* @param {Array} arg
* @returns {Boolean}
*/
export function isArray(arg) {
if (typeof Array.isArray === 'undefined') {
return Object.prototype.toString.call(arg) === '[object Array]'

View File

@ -5,7 +5,7 @@
</template>
<script>
import Chart from '@/components/Charts/keyboard'
import Chart from '@/components/Charts/Keyboard'
export default {
name: 'KeyboardChart',

View File

@ -5,7 +5,7 @@
</template>
<script>
import Chart from '@/components/Charts/lineMarker'
import Chart from '@/components/Charts/LineMarker'
export default {
name: 'LineChart',

View File

@ -5,7 +5,7 @@
</template>
<script>
import Chart from '@/components/Charts/mixChart'
import Chart from '@/components/Charts/MixChart'
export default {
name: 'MixChart',

View File

@ -1,9 +1,9 @@
<template>
<div class="components-container">
<code>This is based on
<aside>This is based on
<a class="link-type" href="//github.com/dai-siki/vue-image-crop-upload"> vue-image-crop-upload</a>.
{{ $t('components.imageUploadTips') }}
</code>
</aside>
<pan-thumb :image="image" />

View File

@ -1,7 +1,7 @@
<template>
<div class="components-container">
<code>{{ $t('components.backToTopTips1') }}</code>
<code>{{ $t('components.backToTopTips2') }}</code>
<aside>{{ $t('components.backToTopTips1') }}</aside>
<aside>{{ $t('components.backToTopTips2') }}</aside>
<div class="placeholder-container">
<div>placeholder</div>
<div>placeholder</div>
@ -113,8 +113,7 @@
<div>placeholder</div>
<div>placeholder</div>
</div>
<!--可自定义按钮的样式show/hide临界点返回的位置 -->
<!--如需文字提示可在外部添加element的<el-tooltip></el-tooltip> -->
<!-- you can add element-ui's tooltip -->
<el-tooltip placement="top" content="tooltip">
<back-to-top :custom-style="myBackToTopStyle" :visibility-height="300" :back-position="50" transition-name="fade" />
</el-tooltip>
@ -129,6 +128,7 @@ export default {
components: { BackToTop },
data() {
return {
// customizable button style, show/hide critical point, return position
myBackToTopStyle: {
right: '50px',
bottom: '50px',

View File

@ -1,8 +1,8 @@
<template>
<div class="components-container">
<p class="warn-content">
<aside>
<a href="https://github.com/PanJiaChen/vue-countTo" target="_blank">countTo-component</a>
</p>
</aside>
<count-to
ref="example"
:start-val="_startVal"
@ -45,9 +45,9 @@
<input v-model="setSuffix" name="suffixInput">
</label>
</div>
<code>&lt;count-to :start-val=&#x27;{{ _startVal }}&#x27; :end-val=&#x27;{{ _endVal }}&#x27; :duration=&#x27;{{ _duration }}&#x27;
<aside>&lt;count-to :start-val=&#x27;{{ _startVal }}&#x27; :end-val=&#x27;{{ _endVal }}&#x27; :duration=&#x27;{{ _duration }}&#x27;
:decimals=&#x27;{{ _decimals }}&#x27; :separator=&#x27;{{ _separator }}&#x27; :prefix=&#x27;{{ _prefix }}&#x27; :suffix=&#x27;{{ _suffix }}&#x27;
:autoplay=false&gt;</code>
:autoplay=false&gt;</aside>
</div>
</template>

View File

@ -1,8 +1,8 @@
<template>
<div class="components-container">
<code>drag-list base on
<aside>drag-list base on
<a href="https://github.com/SortableJS/Vue.Draggable" target="_blank">Vue.Draggable</a>
</code>
</aside>
<div class="editor-container">
<dnd-list :list1="list1" :list2="list2" list1-title="List" list2-title="Article pool" />
</div>

View File

@ -17,7 +17,7 @@
</template>
<script>
import elDragDialog from '@/directive/el-dragDialog' // base on element-ui
import elDragDialog from '@/directive/el-drag-dialog' // base on element-ui
export default {
name: 'DragDialogDemo',

View File

@ -1,9 +1,9 @@
<template>
<div class="components-container">
<code>
<aside>
Based on <a class="link-type" href="https://github.com/rowanwins/vue-dropzone"> dropzone </a>.
{{ $t('components.dropzoneTips') }}
</code>
</aside>
<div class="editor-container">
<dropzone id="myVueDropzone" url="https://httpbin.org/post" @dropzone-removedFile="dropzoneR" @dropzone-success="dropzoneS" />
</div>

View File

@ -1,10 +1,10 @@
<template>
<div class="components-container">
<code>Json-Editor is base on <a href="https://github.com/codemirror/CodeMirror" target="_blank">CodeMirrorr</a>. Lint
<aside>Json-Editor is base on <a href="https://github.com/codemirror/CodeMirror" target="_blank">CodeMirrorr</a>. Lint
base on <a
href="https://github.com/codemirror/CodeMirror/blob/master/addon/lint/json-lint.js"
target="_blank"
>json-lint</a>.</code>
>json-lint</a>.</aside>
<div class="editor-container">
<json-editor ref="jsonEditor" v-model="value" />
</div>

View File

@ -1,13 +1,13 @@
<template>
<div class="components-container">
<code>Markdown is based on
<aside>Markdown is based on
<a href="https://github.com/nhnent/tui.editor" target="_blank">tui.editor</a> simply wrapped with Vue.
<a
target="_blank"
href="https://panjiachen.github.io/vue-element-admin-site/feature/component/markdown-editor.html"
>
Documentation </a>
</code>
</aside>
<div class="editor-container">
<el-tag class="tag-title">

View File

@ -115,7 +115,7 @@
import PanThumb from '@/components/PanThumb'
import MdInput from '@/components/MDinput'
import Mallki from '@/components/TextHoverEffect/Mallki'
import DropdownMenu from '@/components/Share/dropdownMenu'
import DropdownMenu from '@/components/Share/DropdownMenu'
import waves from '@/directive/waves/index.js' //
export default {

View File

@ -1,10 +1,10 @@
<template>
<div class="components-container">
<code><strong>SplitPane</strong> If you've used
<a href="http://codepen.io/" target="_blank"> codepen</a>,
<aside><strong>SplitPane</strong> If you've used
<a href="https://codepen.io/" target="_blank"> codepen</a>,
<a href="https://jsfiddle.net/" target="_blank"> jsfiddle </a>will not be unfamiliar.
<a href="https://github.com/PanJiaChen/vue-split-pane" target="_blank"> Github repository</a>
</code>
</aside>
<split-pane split="vertical" @resize="resize">
<template slot="paneL">
<div class="left-container" />

View File

@ -28,7 +28,7 @@
</el-dropdown>
<div class="time-container">
<el-date-picker v-model="time" :picker-options="pickerOptions" type="datetime" format="yyyy-MM-dd HH:mm:ss" placeholder="Release time" />
<el-date-picker v-model="time" type="datetime" format="yyyy-MM-dd HH:mm:ss" placeholder="Release time" />
</div>
<el-button style="margin-left: 10px;" type="success">
@ -37,7 +37,7 @@
</sticky>
<div class="components-container">
<code>Sticky header, {{ $t('components.stickyTips') }}</code>
<aside>Sticky header, {{ $t('components.stickyTips') }}</aside>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>

View File

@ -1,9 +1,9 @@
<template>
<div class="components-container">
<code>
<aside>
{{ $t('components.tinymceTips') }}
<a target="_blank" class="link-type" href="https://panjiachen.github.io/vue-element-admin-site/component/rich-editor.html"> {{ $t('components.documentation') }}</a>
</code>
</aside>
<div>
<tinymce v-model="content" :height="300" />
</div>

View File

@ -21,7 +21,7 @@
</template>
<script>
import { transactionList } from '@/api/remoteSearch'
import { transactionList } from '@/api/remote-search'
export default {
filters: {

View File

@ -7,7 +7,7 @@
</div>
</template>
<script>
import DropdownMenu from '@/components/Share/dropdownMenu'
import DropdownMenu from '@/components/Share/DropdownMenu'
export default {
name: 'Documentation',

View File

@ -1,15 +1,15 @@
<template>
<div class="errPage-container">
<errorA />
<errorB />
<ErrorA />
<ErrorB />
<!-- $t is vue-i18n global function to translate lang -->
<h3>{{ $t('errorLog.tips') }}</h3>
<code>
<aside>
{{ $t('errorLog.description') }}
<a target="_blank" class="link-type" href="https://panjiachen.github.io/vue-element-admin-site/guide/advanced/error.html">
{{ $t('errorLog.documentation') }}
</a>
</code>
</aside>
<a href="#">
<img src="https://wpimg.wallstcn.com/360e4842-4db5-42d0-b078-f9a84a825546.gif">
</a>
@ -17,12 +17,12 @@
</template>
<script>
import errorA from './errorTestA'
import errorB from './errorTestB'
import ErrorA from './components/ErrorTestA'
import ErrorB from './components/ErrorTestB'
export default {
name: 'ErrorLog',
components: { errorA, errorB }
components: { ErrorA, ErrorB }
}
</script>

View File

@ -6,10 +6,10 @@
<PlatformDropdown v-model="postForm.platforms" />
<SourceUrlDropdown v-model="postForm.source_uri" />
<el-button v-loading="loading" style="margin-left: 10px;" type="success" @click="submitForm">
发布
Publush
</el-button>
<el-button v-loading="loading" type="warning" @click="draftForm">
草稿
Draft
</el-button>
</sticky>
@ -20,28 +20,28 @@
<el-col :span="24">
<el-form-item style="margin-bottom: 40px;" prop="title">
<MDinput v-model="postForm.title" :maxlength="100" name="name" required>
标题
Title
</MDinput>
</el-form-item>
<div class="postInfo-container">
<el-row>
<el-col :span="8">
<el-form-item label-width="45px" label="作者:" class="postInfo-container-item">
<el-select v-model="postForm.author" :remote-method="getRemoteUserList" filterable default-first-option remote placeholder="搜索用户">
<el-form-item label-width="60px" label="Author:" class="postInfo-container-item">
<el-select v-model="postForm.author" :remote-method="getRemoteUserList" filterable default-first-option remote placeholder="Search user">
<el-option v-for="(item,index) in userListOptions" :key="item+index" :label="item" :value="item" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="10">
<el-form-item label-width="80px" label="发布时间:" class="postInfo-container-item">
<el-date-picker v-model="postForm.display_time" type="datetime" format="yyyy-MM-dd HH:mm:ss" placeholder="选择日期时间" />
<el-form-item label-width="120px" label="Publush Time:" class="postInfo-container-item">
<el-date-picker v-model="postForm.display_time" type="datetime" format="yyyy-MM-dd HH:mm:ss" placeholder="Select date and time" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label-width="60px" label="重要性:" class="postInfo-container-item">
<el-form-item label-width="90px" label="Importance:" class="postInfo-container-item">
<el-rate
v-model="postForm.importance"
:max="3"
@ -57,9 +57,9 @@
</el-col>
</el-row>
<el-form-item style="margin-bottom: 40px;" label-width="45px" label="摘要:">
<el-input v-model="postForm.content_short" :rows="1" type="textarea" class="article-textarea" autosize placeholder="请输入内容" />
<span v-show="contentShortLength" class="word-counter">{{ contentShortLength }}</span>
<el-form-item style="margin-bottom: 40px;" label-width="70px" label="Summary:">
<el-input v-model="postForm.content_short" :rows="1" type="textarea" class="article-textarea" autosize placeholder="Please enter the content" />
<span v-show="contentShortLength" class="word-counter">{{ contentShortLength }}words</span>
</el-form-item>
<el-form-item prop="content" style="margin-bottom: 30px;">
@ -76,12 +76,12 @@
<script>
import Tinymce from '@/components/Tinymce'
import Upload from '@/components/Upload/singleImage3'
import Upload from '@/components/Upload/SingleImage3'
import MDinput from '@/components/MDinput'
import Sticky from '@/components/Sticky' // header
import { validURL } from '@/utils/validate'
import { fetchArticle } from '@/api/article'
import { searchUser } from '@/api/remoteSearch'
import { searchUser } from '@/api/remote-search'
import Warning from './Warning'
import { CommentDropdown, PlatformDropdown, SourceUrlDropdown } from './Dropdown'
@ -236,24 +236,39 @@ export default {
<style lang="scss" scoped>
@import "~@/styles/mixin.scss";
.createPost-container {
position: relative;
.createPost-main-container {
padding: 40px 45px 20px 50px;
.postInfo-container {
position: relative;
@include clearfix;
margin-bottom: 10px;
.postInfo-container-item {
float: left;
}
}
}
.word-counter {
width: 40px;
position: absolute;
right: -10px;
right: 10px;
top: 0px;
}
}
.article-textarea /deep/ {
textarea {
padding-right: 40px;
resize: none;
border: none;
border-radius: 0px;
border-bottom: 1px solid #bfcbd9;
}
}
</style>

View File

@ -1,17 +1,17 @@
<template>
<el-dropdown :show-timeout="100" trigger="click">
<el-button plain>
{{ !comment_disabled?'评论已打开':'评论已关闭' }}
{{ !comment_disabled?'Comment: opened':'Comment: closed' }}
<i class="el-icon-caret-bottom el-icon--right" />
</el-button>
<el-dropdown-menu slot="dropdown" class="no-padding">
<el-dropdown-item>
<el-radio-group v-model="comment_disabled" style="padding: 10px;">
<el-radio :label="true">
关闭评论
Close comment
</el-radio>
<el-radio :label="false">
打开评论
Open comment
</el-radio>
</el-radio-group>
</el-dropdown-item>

View File

@ -1,7 +1,7 @@
<template>
<el-dropdown :hide-on-click="false" :show-timeout="100" trigger="click">
<el-button plain>
平台({{ platforms.length }})
Platfroms({{ platforms.length }})
<i class="el-icon-caret-bottom el-icon--right" />
</el-button>
<el-dropdown-menu slot="dropdown" class="no-border">

View File

@ -1,14 +1,14 @@
<template>
<el-dropdown :show-timeout="100" trigger="click">
<el-button plain>
外链
Link
<i class="el-icon-caret-bottom el-icon--right" />
</el-button>
<el-dropdown-menu slot="dropdown" class="no-padding no-border" style="width:400px">
<el-form-item label-width="0px" style="margin-bottom: 0px" prop="source_uri">
<el-input v-model="source_uri" placeholder="请输入内容">
<el-input v-model="source_uri" placeholder="Please enter the content">
<template slot="prepend">
填写url
URL
</template>
</el-input>
</el-form-item>

View File

@ -1,11 +1,10 @@
<template>
<p class="warn-content">
创建和编辑页面是不能被keep-alive 缓存的因为keep-alive 的include 目前不支持根据路由来缓存所以目前都是基于component name 来缓存的如果你想要实现缓存的效果可以使用localstorage 等浏览器缓存方案或者不要使用keep-alive
的include直接缓存所有页面详情见
<aside>
{{ $t('example.warning') }}
<a
href="https://panjiachen.github.io/vue-element-admin-site/guide/essentials/tags-view.html"
target="_blank"
>文档</a>
</p>
>Document</a>
</aside>
</template>

View File

@ -2,7 +2,7 @@
<div style="display:inline-block;">
<!-- $t is vue-i18n global function to translate lang -->
<label class="radio-label" style="padding-left:0;">Filename: </label>
<el-input v-model="filename" :placeholder="$t('excel.placeholder')" style="width:340px;" prefix-icon="el-icon-document" />
<el-input v-model="filename" :placeholder="$t('excel.placeholder')" style="width:350px;" prefix-icon="el-icon-document" />
</div>
</template>

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