Merge branch 'master' into deploy
This commit is contained in:
commit
caffe6f470
|
@ -1,11 +1,23 @@
|
||||||
import Mock from 'mockjs'
|
import Mock from 'mockjs'
|
||||||
import mocks from './mocks'
|
|
||||||
import { param2Obj } from '../src/utils'
|
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 './remoteSearch'
|
||||||
|
|
||||||
|
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() {
|
export function mockXHR() {
|
||||||
// 修复在使用 MockJS 情况下,设置 withCredentials = true,且未被拦截的跨域请求丢失 Cookies 的问题
|
// mock patch
|
||||||
// https://github.com/nuysoft/Mock/issues/300
|
// https://github.com/nuysoft/Mock/issues/300
|
||||||
Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send
|
Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send
|
||||||
Mock.XHR.prototype.send = function() {
|
Mock.XHR.prototype.send = function() {
|
||||||
|
@ -42,9 +54,10 @@ export function mockXHR() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for mock server
|
||||||
const responseFake = (url, type, respond) => {
|
const responseFake = (url, type, respond) => {
|
||||||
return {
|
return {
|
||||||
url: new RegExp(`${MOCK_API_BASE}${url}`),
|
url: new RegExp(`/mock${url}`),
|
||||||
type: type || 'get',
|
type: type || 'get',
|
||||||
response(req, res) {
|
response(req, res) {
|
||||||
res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond))
|
res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond))
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
const chokidar = require('chokidar')
|
||||||
|
const bodyParser = require('body-parser')
|
||||||
|
const chalk = require('chalk')
|
||||||
|
|
||||||
|
function registerRoutes(app) {
|
||||||
|
const { default: mocks } = require('./index.js')
|
||||||
|
for (const mock of mocks) {
|
||||||
|
app[mock.type](mock.url, mock.response)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
mockRoutesLength: Object.keys(mocks).length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function unregisterRoutes() {
|
||||||
|
Object.keys(require.cache).forEach(i => {
|
||||||
|
if (i.includes('/mock')) {
|
||||||
|
delete require.cache[require.resolve(i)]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPath(path) {
|
||||||
|
var match = path.toString()
|
||||||
|
.replace('\\/?', '')
|
||||||
|
.replace('(?=\\/|$)', '$')
|
||||||
|
.match(/^\/\^((?:\\[.*+?^${}()|[\]\\\/]|[^.*+?^${}()|[\]\\\/])*)\$\//)
|
||||||
|
return match
|
||||||
|
? match[1].replace(/\\(.)/g, '$1').split('/')
|
||||||
|
: path.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMockRoutesIndex(app) {
|
||||||
|
for (let index = 0; index <= app._router.stack.length; index++) {
|
||||||
|
const r = app._router.stack[index]
|
||||||
|
if (r.route && r.route.path) {
|
||||||
|
const path = getPath(r.route.path)
|
||||||
|
if (path.includes('mock')) {
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = app => {
|
||||||
|
// es6 polyfill
|
||||||
|
require('@babel/register')
|
||||||
|
|
||||||
|
// parse app.body
|
||||||
|
// http://expressjs.com/en/4x/api.html#req.body
|
||||||
|
app.use(bodyParser.json())
|
||||||
|
app.use(bodyParser.urlencoded({
|
||||||
|
extended: true
|
||||||
|
}))
|
||||||
|
|
||||||
|
const { mockRoutesLength } = registerRoutes(app)
|
||||||
|
|
||||||
|
// 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') {
|
||||||
|
// find mock routes stack index
|
||||||
|
const index = getMockRoutesIndex(app)
|
||||||
|
|
||||||
|
// remove mock routes stack
|
||||||
|
app._router.stack.splice(index, mockRoutesLength)
|
||||||
|
|
||||||
|
// clear routes cache
|
||||||
|
unregisterRoutes()
|
||||||
|
|
||||||
|
registerRoutes(app)
|
||||||
|
|
||||||
|
console.log(chalk.magentaBright(`\n > Mock Server hot reload success! changed ${path}`))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -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
|
|
||||||
]
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ export const constantRoutes = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/auth-redirect',
|
path: '/auth-redirect',
|
||||||
component: 'views/login/authredirect',
|
component: 'views/login/authRedirect',
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -84,6 +84,7 @@
|
||||||
"babel-eslint": "10.0.1",
|
"babel-eslint": "10.0.1",
|
||||||
"babel-jest": "23.6.0",
|
"babel-jest": "23.6.0",
|
||||||
"chalk": "2.4.2",
|
"chalk": "2.4.2",
|
||||||
|
"chokidar": "2.1.5",
|
||||||
"connect": "3.6.6",
|
"connect": "3.6.6",
|
||||||
"eslint": "5.15.3",
|
"eslint": "5.15.3",
|
||||||
"eslint-plugin-vue": "5.2.2",
|
"eslint-plugin-vue": "5.2.2",
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div class="dndList">
|
<div class="dndList">
|
||||||
<div :style="{width:width1}" class="dndList-list">
|
<div :style="{width:width1}" class="dndList-list">
|
||||||
<h3>{{ list1Title }}</h3>
|
<h3>{{ list1Title }}</h3>
|
||||||
<draggable :list="list1" group="article" class="dragArea">
|
<draggable :set-data="setData" :list="list1" group="article" class="dragArea">
|
||||||
<div v-for="element in list1" :key="element.id" class="list-complete-item">
|
<div v-for="element in list1" :key="element.id" class="list-complete-item">
|
||||||
<div class="list-complete-item-handle">
|
<div class="list-complete-item-handle">
|
||||||
{{ element.id }}[{{ element.author }}] {{ element.title }}
|
{{ element.id }}[{{ element.author }}] {{ element.title }}
|
||||||
|
@ -94,6 +94,11 @@ export default {
|
||||||
if (this.isNotInList1(ele)) {
|
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', '')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-badge>
|
</el-badge>
|
||||||
|
|
||||||
<el-dialog :visible.sync="dialogTableVisible" title="Error Log" width="80%">
|
<el-dialog :visible.sync="dialogTableVisible" title="Error Log" width="80%" append-to-body>
|
||||||
<el-table :data="errorLogs" border>
|
<el-table :data="errorLogs" border>
|
||||||
<el-table-column label="Message">
|
<el-table-column label="Message">
|
||||||
<template slot-scope="{row}">
|
<template slot-scope="{row}">
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
:list="list"
|
:list="list"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
class="board-column-content"
|
class="board-column-content"
|
||||||
|
:set-data="setData"
|
||||||
>
|
>
|
||||||
<div v-for="element in list" :key="element.id" class="board-item">
|
<div v-for="element in list" :key="element.id" class="board-item">
|
||||||
{{ element.name }} {{ element.id }}
|
{{ element.name }} {{ element.id }}
|
||||||
|
@ -39,6 +40,13 @@ export default {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
setData(dataTransfer) {
|
||||||
|
// to avoid Firefox bug
|
||||||
|
// Detail see : https://github.com/RubaXa/Sortable/issues/1012
|
||||||
|
dataTransfer.setData('Text', '')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
export default {
|
export default {
|
||||||
route: {
|
route: {
|
||||||
dashboard: 'Dashboard',
|
dashboard: 'Dashboard',
|
||||||
introduction: 'Introduction',
|
|
||||||
documentation: 'Documentation',
|
documentation: 'Documentation',
|
||||||
guide: 'Guide',
|
guide: 'Guide',
|
||||||
permission: 'Permission',
|
permission: 'Permission',
|
||||||
|
@ -10,7 +9,6 @@ export default {
|
||||||
directivePermission: 'Directive Permission',
|
directivePermission: 'Directive Permission',
|
||||||
icons: 'Icons',
|
icons: 'Icons',
|
||||||
components: 'Components',
|
components: 'Components',
|
||||||
componentIndex: 'Introduction',
|
|
||||||
tinymce: 'Tinymce',
|
tinymce: 'Tinymce',
|
||||||
markdown: 'Markdown',
|
markdown: 'Markdown',
|
||||||
jsonEditor: 'JSON Editor',
|
jsonEditor: 'JSON Editor',
|
||||||
|
@ -88,10 +86,10 @@ export default {
|
||||||
},
|
},
|
||||||
permission: {
|
permission: {
|
||||||
addRole: 'New Role',
|
addRole: 'New Role',
|
||||||
editPermission: 'Edit Permission',
|
editPermission: 'Edit',
|
||||||
roles: 'Your roles',
|
roles: 'Your roles',
|
||||||
switchRoles: 'Switch 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',
|
delete: 'Delete',
|
||||||
confirm: 'Confirm',
|
confirm: 'Confirm',
|
||||||
cancel: 'Cancel'
|
cancel: 'Cancel'
|
||||||
|
@ -102,7 +100,7 @@ export default {
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
documentation: 'Documentation',
|
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.',
|
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.',
|
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',
|
backToTopTips1: 'When the page is scrolled to the specified position, the Back to Top button appears in the lower right corner',
|
||||||
|
@ -150,7 +148,7 @@ export default {
|
||||||
placeholder: 'Please enter the file name (default file)'
|
placeholder: 'Please enter the file name (default file)'
|
||||||
},
|
},
|
||||||
pdf: {
|
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: {
|
theme: {
|
||||||
change: 'Change Theme',
|
change: 'Change Theme',
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
export default {
|
export default {
|
||||||
route: {
|
route: {
|
||||||
dashboard: 'Panel de control',
|
dashboard: 'Panel de control',
|
||||||
introduction: 'Introducción',
|
|
||||||
documentation: 'Documentación',
|
documentation: 'Documentación',
|
||||||
guide: 'Guía',
|
guide: 'Guía',
|
||||||
permission: 'Permisos',
|
permission: 'Permisos',
|
||||||
|
@ -10,7 +9,6 @@ export default {
|
||||||
directivePermission: 'Permisos de la directiva',
|
directivePermission: 'Permisos de la directiva',
|
||||||
icons: 'Iconos',
|
icons: 'Iconos',
|
||||||
components: 'Componentes',
|
components: 'Componentes',
|
||||||
componentIndex: 'Introducción',
|
|
||||||
tinymce: 'Tinymce',
|
tinymce: 'Tinymce',
|
||||||
markdown: 'Markdown',
|
markdown: 'Markdown',
|
||||||
jsonEditor: 'Editor JSON',
|
jsonEditor: 'Editor JSON',
|
||||||
|
|
|
@ -24,11 +24,24 @@ const messages = {
|
||||||
...elementEsLocale
|
...elementEsLocale
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export function getLanguage() {
|
||||||
|
const chooseLanguage = Cookies.get('language')
|
||||||
|
if (chooseLanguage) return chooseLanguage
|
||||||
|
|
||||||
|
// if has not choose language
|
||||||
|
const language = (navigator.language || navigator.browserLanguage).toLowerCase()
|
||||||
|
const locales = Object.keys(messages)
|
||||||
|
for (const locale of locales) {
|
||||||
|
if (language.indexOf(locale) > -1) {
|
||||||
|
return locale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 'en'
|
||||||
|
}
|
||||||
const i18n = new VueI18n({
|
const i18n = new VueI18n({
|
||||||
// set locale
|
// set locale
|
||||||
// options: en | zh | es
|
// options: en | zh | es
|
||||||
locale: Cookies.get('language') || 'en',
|
locale: getLanguage(),
|
||||||
// set locale messages
|
// set locale messages
|
||||||
messages
|
messages
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
export default {
|
export default {
|
||||||
route: {
|
route: {
|
||||||
dashboard: '首页',
|
dashboard: '首页',
|
||||||
introduction: '简述',
|
|
||||||
documentation: '文档',
|
documentation: '文档',
|
||||||
guide: '引导页',
|
guide: '引导页',
|
||||||
permission: '权限测试页',
|
permission: '权限测试页',
|
||||||
|
@ -10,7 +9,6 @@ export default {
|
||||||
directivePermission: '指令权限',
|
directivePermission: '指令权限',
|
||||||
icons: '图标',
|
icons: '图标',
|
||||||
components: '组件',
|
components: '组件',
|
||||||
componentIndex: '介绍',
|
|
||||||
tinymce: '富文本编辑器',
|
tinymce: '富文本编辑器',
|
||||||
markdown: 'Markdown',
|
markdown: 'Markdown',
|
||||||
jsonEditor: 'JSON 编辑器',
|
jsonEditor: 'JSON 编辑器',
|
||||||
|
@ -91,7 +89,7 @@ export default {
|
||||||
editPermission: '编辑权限',
|
editPermission: '编辑权限',
|
||||||
roles: '你的权限',
|
roles: '你的权限',
|
||||||
switchRoles: '切换权限',
|
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: '删除',
|
delete: '删除',
|
||||||
confirm: '确定',
|
confirm: '确定',
|
||||||
cancel: '取消'
|
cancel: '取消'
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<logo v-if="showLogo" :collapse="isCollapse" />
|
<logo v-if="showLogo" :collapse="isCollapse" />
|
||||||
<el-scrollbar wrap-class="scrollbar-wrapper">
|
<el-scrollbar wrap-class="scrollbar-wrapper">
|
||||||
<el-menu
|
<el-menu
|
||||||
:default-active="$route.path"
|
:default-active="activeMenu"
|
||||||
:collapse="isCollapse"
|
:collapse="isCollapse"
|
||||||
:background-color="variables.menuBg"
|
:background-color="variables.menuBg"
|
||||||
:text-color="variables.menuText"
|
:text-color="variables.menuText"
|
||||||
|
@ -30,6 +30,15 @@ export default {
|
||||||
'permission_routes',
|
'permission_routes',
|
||||||
'sidebar'
|
'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() {
|
showLogo() {
|
||||||
return this.$store.state.settings.sidebarLogo
|
return this.$store.state.settings.sidebarLogo
|
||||||
},
|
},
|
||||||
|
|
|
@ -243,7 +243,7 @@ export default {
|
||||||
.contextmenu {
|
.contextmenu {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
z-index: 100;
|
z-index: 3000;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
padding: 5px 0;
|
padding: 5px 0;
|
||||||
|
|
|
@ -12,32 +12,32 @@ import chartsRouter from './modules/charts'
|
||||||
import tableRouter from './modules/table'
|
import tableRouter from './modules/table'
|
||||||
import nestedRouter from './modules/nested'
|
import nestedRouter from './modules/nested'
|
||||||
|
|
||||||
/** note: sub-menu only appear when 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)
|
* Note: sub-menu only appear when route children.length >= 1
|
||||||
* alwaysShow: true if set true, will always show the root menu, whatever its child routes length
|
* Detail see: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
|
||||||
* if not set alwaysShow, only more than one route under the children
|
*
|
||||||
|
* 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
|
* it will becomes nested mode, otherwise not show the root menu
|
||||||
* redirect: noredirect if `redirect:noredirect` will no redirect in the breadcrumb
|
* redirect: noredirect if `redirect:noredirect` will no redirect in the breadcrumb
|
||||||
* name:'router-name' the name is used by <keep-alive> (must set!!!)
|
* name:'router-name' the name is used by <keep-alive> (must set!!!)
|
||||||
* meta : {
|
* meta : {
|
||||||
roles: ['admin','editor'] will control the page roles (you can set multiple roles)
|
roles: ['admin','editor'] control the page roles (you can set multiple roles)
|
||||||
title: 'title' the name show in sub-menu and breadcrumb (recommend set)
|
title: 'title' the name show in sidebar and breadcrumb (recommend set)
|
||||||
icon: 'svg-name' the icon show in the sidebar
|
icon: 'svg-name' the icon show in the sidebar
|
||||||
noCache: true if true, the page will no be cached(default is false)
|
noCache: true if set 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 set true, the tag will affix in the tags-view
|
||||||
affix: true if 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
|
* constantRoutes
|
||||||
* a base page that does not have permission requirements
|
* a base page that does not have permission requirements
|
||||||
* all roles can be accessed
|
* all roles can be accessed
|
||||||
* */
|
*/
|
||||||
export const constantRoutes = [
|
export const constantRoutes = [
|
||||||
{
|
{
|
||||||
path: '/redirect',
|
path: '/redirect',
|
||||||
|
@ -57,7 +57,7 @@ export const constantRoutes = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/auth-redirect',
|
path: '/auth-redirect',
|
||||||
component: () => import('@/views/login/authredirect'),
|
component: () => import('@/views/login/authRedirect'),
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -195,7 +195,7 @@ export const asyncRoutes = [
|
||||||
path: 'edit/:id(\\d+)',
|
path: 'edit/:id(\\d+)',
|
||||||
component: () => import('@/views/example/edit'),
|
component: () => import('@/views/example/edit'),
|
||||||
name: 'EditArticle',
|
name: 'EditArticle',
|
||||||
meta: { title: 'editArticle', noCache: true },
|
meta: { title: 'editArticle', noCache: true, activeMenu: '/example/list' },
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import Vuex from 'vuex'
|
import Vuex from 'vuex'
|
||||||
import app from './modules/app'
|
|
||||||
import errorLog from './modules/errorLog'
|
|
||||||
import permission from './modules/permission'
|
|
||||||
import tagsView from './modules/tagsView'
|
|
||||||
import settings from './modules/settings'
|
|
||||||
import user from './modules/user'
|
|
||||||
import getters from './getters'
|
import getters from './getters'
|
||||||
|
|
||||||
Vue.use(Vuex)
|
Vue.use(Vuex)
|
||||||
|
|
||||||
|
// https://webpack.js.org/guides/dependency-management/#requirecontext
|
||||||
|
const modulesFiles = require.context('./modules', false, /\.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 store = new Vuex.Store({
|
const store = new Vuex.Store({
|
||||||
modules: {
|
modules,
|
||||||
app,
|
|
||||||
errorLog,
|
|
||||||
permission,
|
|
||||||
tagsView,
|
|
||||||
settings,
|
|
||||||
user
|
|
||||||
},
|
|
||||||
getters
|
getters
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import Cookies from 'js-cookie'
|
import Cookies from 'js-cookie'
|
||||||
|
import { getLanguage } from '@/lang/index'
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
sidebar: {
|
sidebar: {
|
||||||
|
@ -6,7 +7,7 @@ const state = {
|
||||||
withoutAnimation: false
|
withoutAnimation: false
|
||||||
},
|
},
|
||||||
device: 'desktop',
|
device: 'desktop',
|
||||||
language: Cookies.get('language') || 'en',
|
language: getLanguage(),
|
||||||
size: Cookies.get('size') || 'medium'
|
size: Cookies.get('size') || 'medium'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
logs: []
|
logs: []
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
visitedViews: [],
|
visitedViews: [],
|
||||||
cachedViews: []
|
cachedViews: []
|
||||||
|
|
|
@ -28,10 +28,6 @@
|
||||||
|
|
||||||
.scrollbar-wrapper {
|
.scrollbar-wrapper {
|
||||||
overflow-x: hidden !important;
|
overflow-x: hidden !important;
|
||||||
|
|
||||||
.el-scrollbar__view {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-scrollbar__bar.is-vertical {
|
.el-scrollbar__bar.is-vertical {
|
||||||
|
|
|
@ -23,7 +23,7 @@ service.interceptors.request.use(
|
||||||
error => {
|
error => {
|
||||||
// Do something with request error
|
// Do something with request error
|
||||||
console.log(error) // for debug
|
console.log(error) // for debug
|
||||||
Promise.reject(error)
|
return Promise.reject(error)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ service.interceptors.response.use(
|
||||||
const res = response.data
|
const res = response.data
|
||||||
if (res.code !== 20000) {
|
if (res.code !== 20000) {
|
||||||
Message({
|
Message({
|
||||||
message: res.message,
|
message: res.message || 'error',
|
||||||
type: 'error',
|
type: 'error',
|
||||||
duration: 5 * 1000
|
duration: 5 * 1000
|
||||||
})
|
})
|
||||||
|
@ -61,7 +61,7 @@ service.interceptors.response.use(
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return Promise.reject('error')
|
return Promise.reject(res.message || 'error')
|
||||||
} else {
|
} else {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div style="display:inline-block;">
|
<div style="display:inline-block;">
|
||||||
<!-- $t is vue-i18n global function to translate lang -->
|
<!-- $t is vue-i18n global function to translate lang -->
|
||||||
<label class="radio-label" style="padding-left:0;">Filename: </label>
|
<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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
<!-- $t is vue-i18n global function to translate lang -->
|
<!-- $t is vue-i18n global function to translate lang -->
|
||||||
<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" />
|
||||||
<el-button :loading="downloadLoading" style="margin-bottom:20px" type="primary" icon="document" @click="handleDownload">
|
<el-button :loading="downloadLoading" style="margin-bottom:20px" type="primary" icon="document" @click="handleDownload">
|
||||||
{{ $t('excel.selectedExport') }}
|
{{ $t('excel.selectedExport') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'Authredirect',
|
name: 'AuthRedirect',
|
||||||
created() {
|
created() {
|
||||||
const hash = window.location.search.slice(1)
|
const hash = window.location.search.slice(1)
|
||||||
if (window.localStorage) {
|
if (window.localStorage) {
|
|
@ -23,6 +23,7 @@
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-tooltip v-model="capsTooltip" content="Caps lock is On" placement="right" manual>
|
||||||
<el-form-item prop="password">
|
<el-form-item prop="password">
|
||||||
<span class="svg-container">
|
<span class="svg-container">
|
||||||
<svg-icon icon-class="password" />
|
<svg-icon icon-class="password" />
|
||||||
|
@ -35,12 +36,15 @@
|
||||||
:placeholder="$t('login.password')"
|
:placeholder="$t('login.password')"
|
||||||
name="password"
|
name="password"
|
||||||
auto-complete="on"
|
auto-complete="on"
|
||||||
|
@keyup.native="checkCapslock"
|
||||||
|
@blur="capsTooltip = false"
|
||||||
@keyup.enter.native="handleLogin"
|
@keyup.enter.native="handleLogin"
|
||||||
/>
|
/>
|
||||||
<span class="show-pwd" @click="showPwd">
|
<span class="show-pwd" @click="showPwd">
|
||||||
<svg-icon :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" />
|
<svg-icon :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" />
|
||||||
</span>
|
</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
</el-tooltip>
|
||||||
|
|
||||||
<el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">
|
<el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">
|
||||||
{{ $t('login.logIn') }}
|
{{ $t('login.logIn') }}
|
||||||
|
@ -77,7 +81,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { validUsername } from '@/utils/validate'
|
import { validUsername } from '@/utils/validate'
|
||||||
import LangSelect from '@/components/LangSelect'
|
import LangSelect from '@/components/LangSelect'
|
||||||
import SocialSign from './socialsignin'
|
import SocialSign from './socialSignin'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Login',
|
name: 'Login',
|
||||||
|
@ -107,6 +111,7 @@ export default {
|
||||||
password: [{ required: true, trigger: 'blur', validator: validatePassword }]
|
password: [{ required: true, trigger: 'blur', validator: validatePassword }]
|
||||||
},
|
},
|
||||||
passwordType: 'password',
|
passwordType: 'password',
|
||||||
|
capsTooltip: false,
|
||||||
loading: false,
|
loading: false,
|
||||||
showDialog: false,
|
showDialog: false,
|
||||||
redirect: undefined
|
redirect: undefined
|
||||||
|
@ -134,6 +139,18 @@ export default {
|
||||||
// window.removeEventListener('storage', this.afterQRScan)
|
// window.removeEventListener('storage', this.afterQRScan)
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
checkCapslock({ shiftKey, key } = {}) {
|
||||||
|
if (key && key.length === 1) {
|
||||||
|
if (shiftKey && (key >= 'a' && key <= 'z') || !shiftKey && (key >= 'A' && key <= 'Z')) {
|
||||||
|
this.capsTooltip = true
|
||||||
|
} else {
|
||||||
|
this.capsTooltip = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (key === 'CapsLock' && this.capsTooltip === true) {
|
||||||
|
this.capsTooltip = false
|
||||||
|
}
|
||||||
|
},
|
||||||
showPwd() {
|
showPwd() {
|
||||||
if (this.passwordType === 'password') {
|
if (this.passwordType === 'password') {
|
||||||
this.passwordType = ''
|
this.passwordType = ''
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div style="margin-bottom:15px;">
|
<div style="margin-bottom:15px;">
|
||||||
{{ $t('permission.roles') }}: {{ roles }}
|
{{ $t('permission.roles') }}: {{ roles }}
|
||||||
</div>
|
</div>
|
||||||
{{ $t('permission.switchRoles') }}:
|
{{ $t('permission.switchRoles') }}:
|
||||||
<el-radio-group v-model="switchRoles">
|
<el-radio-group v-model="switchRoles">
|
||||||
<el-radio-button label="editor" />
|
<el-radio-button label="editor" />
|
||||||
<el-radio-button label="admin" />
|
<el-radio-button label="admin" />
|
||||||
|
|
|
@ -54,10 +54,10 @@
|
||||||
</el-table>
|
</el-table>
|
||||||
<!-- $t is vue-i18n global function to translate lang (lang in @/lang) -->
|
<!-- $t is vue-i18n global function to translate lang (lang in @/lang) -->
|
||||||
<div class="show-d">
|
<div class="show-d">
|
||||||
{{ $t('table.dragTips1') }} : {{ oldList }}
|
<el-tag style="margin-right:12px;">{{ $t('table.dragTips1') }} :</el-tag> {{ oldList }}
|
||||||
</div>
|
</div>
|
||||||
<div class="show-d">
|
<div class="show-d">
|
||||||
{{ $t('table.dragTips2') }} : {{ newList }}
|
<el-tag>{{ $t('table.dragTips2') }} :</el-tag> {{ newList }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -113,9 +113,9 @@ export default {
|
||||||
this.sortable = Sortable.create(el, {
|
this.sortable = Sortable.create(el, {
|
||||||
ghostClass: 'sortable-ghost', // Class name for the drop placeholder,
|
ghostClass: 'sortable-ghost', // Class name for the drop placeholder,
|
||||||
setData: function(dataTransfer) {
|
setData: function(dataTransfer) {
|
||||||
dataTransfer.setData('Text', '')
|
|
||||||
// to avoid Firefox bug
|
// to avoid Firefox bug
|
||||||
// Detail see : https://github.com/RubaXa/Sortable/issues/1012
|
// Detail see : https://github.com/RubaXa/Sortable/issues/1012
|
||||||
|
dataTransfer.setData('Text', '')
|
||||||
},
|
},
|
||||||
onEnd: evt => {
|
onEnd: evt => {
|
||||||
const targetRow = this.list.splice(evt.oldIndex, 1)[0]
|
const targetRow = this.list.splice(evt.oldIndex, 1)[0]
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<!-- $t is vue-i18n global function to translate lang -->
|
<!-- $t is vue-i18n global function to translate lang -->
|
||||||
<el-input v-model="filename" :placeholder="$t('zip.placeholder')" style="width:300px;" prefix-icon="el-icon-document" />
|
<el-input v-model="filename" :placeholder="$t('zip.placeholder')" style="width:300px;" prefix-icon="el-icon-document" />
|
||||||
<el-button :loading="downloadLoading" style="margin-bottom:20px;" type="primary" icon="document" @click="handleDownload">
|
<el-button :loading="downloadLoading" style="margin-bottom:20px;" type="primary" icon="document" @click="handleDownload">
|
||||||
{{ $t('zip.export') }} zip
|
{{ $t('zip.export') }} Zip
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-table v-loading="listLoading" :data="list" element-loading-text="拼命加载中" border fit highlight-current-row>
|
<el-table v-loading="listLoading" :data="list" element-loading-text="拼命加载中" border fit highlight-current-row>
|
||||||
<el-table-column align="center" label="ID" width="95">
|
<el-table-column align="center" label="ID" width="95">
|
||||||
|
|
|
@ -41,22 +41,7 @@ module.exports = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
after(app) {
|
after: require('./mock/mock-server.js')
|
||||||
require('@babel/register')
|
|
||||||
const bodyParser = require('body-parser')
|
|
||||||
|
|
||||||
// parse app.body
|
|
||||||
// http://expressjs.com/en/4x/api.html#req.body
|
|
||||||
app.use(bodyParser.json())
|
|
||||||
app.use(bodyParser.urlencoded({
|
|
||||||
extended: true
|
|
||||||
}))
|
|
||||||
|
|
||||||
const { default: mocks } = require('./mock')
|
|
||||||
for (const mock of mocks) {
|
|
||||||
app[mock.type](mock.url, mock.response)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
configureWebpack: {
|
configureWebpack: {
|
||||||
// provide the app's title in webpack's name field, so that
|
// provide the app's title in webpack's name field, so that
|
||||||
|
|
Loading…
Reference in New Issue