This commit is contained in:
花裤衩 2020-06-21 21:29:17 +08:00
commit fa26b7991d
23 changed files with 247 additions and 149 deletions

View File

@ -3,12 +3,3 @@ ENV = 'development'
# base api # base api
VUE_APP_BASE_API = '/dev-api' VUE_APP_BASE_API = '/dev-api'
# vue-cli uses the VUE_CLI_BABEL_TRANSPILE_MODULES environment variable,
# to control whether the babel-plugin-dynamic-import-node plugin is enabled.
# It only does one thing by converting all import() to require().
# This configuration can significantly increase the speed of hot updates,
# when you have a large number of pages.
# Detail: https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/babel-preset-app/index.js
VUE_CLI_BABEL_TRANSPILE_MODULES = true

View File

@ -1,5 +1,14 @@
module.exports = { module.exports = {
presets: [ presets: [
'@vue/app' // https://github.com/vuejs/vue-cli/tree/master/packages/@vue/babel-preset-app
] '@vue/cli-plugin-babel/preset'
],
'env': {
'development': {
// babel-plugin-dynamic-import-node plugin only does one thing by converting all import() to require().
// This plugin can significantly increase the speed of hot updates, when you have a large number of pages.
// https://panjiachen.github.io/vue-element-admin-site/guide/advanced/lazy-loading.html
'plugins': ['dynamic-import-node']
}
}
} }

View File

@ -1,4 +1,4 @@
import Mock from 'mockjs' const Mock = require('mockjs')
const List = [] const List = []
const count = 100 const count = 100
@ -27,7 +27,7 @@ for (let i = 0; i < count; i++) {
})) }))
} }
export default [ module.exports = [
{ {
url: '/vue-element-admin/article/list', url: '/vue-element-admin/article/list',
type: 'get', type: 'get',

View File

@ -1,10 +1,10 @@
import Mock from 'mockjs' const Mock = require('mockjs')
import { param2Obj } from '../src/utils' const { param2Obj } = require('./utils')
import user from './user' const user = require('./user')
import role from './role' const role = require('./role')
import article from './article' const article = require('./article')
import search from './remote-search' const search = require('./remote-search')
const mocks = [ const mocks = [
...user, ...user,
@ -16,7 +16,7 @@ const mocks = [
// for front mock // for front mock
// please use it cautiously, it will redefine XMLHttpRequest, // please use it cautiously, it will redefine XMLHttpRequest,
// which will cause many of your third-party libraries to be invalidated(like progress event). // which will cause many of your third-party libraries to be invalidated(like progress event).
export function mockXHR() { function mockXHR() {
// mock patch // mock patch
// https://github.com/nuysoft/Mock/issues/300 // https://github.com/nuysoft/Mock/issues/300
Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send
@ -54,4 +54,7 @@ export function mockXHR() {
} }
} }
export default mocks module.exports = {
mocks,
mockXHR
}

View File

@ -8,7 +8,7 @@ const mockDir = path.join(process.cwd(), 'mock')
function registerRoutes(app) { function registerRoutes(app) {
let mockLastIndex let mockLastIndex
const { default: mocks } = require('./index.js') const { mocks } = require('./index.js')
const mocksForServer = mocks.map(route => { const mocksForServer = mocks.map(route => {
return responseFake(route.url, route.type, route.response) return responseFake(route.url, route.type, route.response)
}) })
@ -44,9 +44,6 @@ const responseFake = (url, type, respond) => {
} }
module.exports = app => { module.exports = app => {
// es6 polyfill
require('@babel/register')
// parse app.body // parse app.body
// https://expressjs.com/en/4x/api.html#req.body // https://expressjs.com/en/4x/api.html#req.body
app.use(bodyParser.json()) app.use(bodyParser.json())

View File

@ -1,4 +1,4 @@
import Mock from 'mockjs' const Mock = require('mockjs')
const NameList = [] const NameList = []
const count = 100 const count = 100
@ -10,7 +10,7 @@ for (let i = 0; i < count; i++) {
} }
NameList.push({ name: 'mock-Pan' }) NameList.push({ name: 'mock-Pan' })
export default [ module.exports = [
// username search // username search
{ {
url: '/vue-element-admin/search/user', url: '/vue-element-admin/search/user',

View File

@ -1,6 +1,6 @@
import Mock from 'mockjs' const Mock = require('mockjs')
import { deepClone } from '../../src/utils/index.js' const { deepClone } = require('../utils')
import { asyncRoutes, constantRoutes } from './routes.js' const { asyncRoutes, constantRoutes } = require('./routes.js')
const routes = deepClone([...constantRoutes, ...asyncRoutes]) const routes = deepClone([...constantRoutes, ...asyncRoutes])
@ -35,7 +35,7 @@ const roles = [
} }
] ]
export default [ module.exports = [
// mock get all routes form server // mock get all routes form server
{ {
url: '/vue-element-admin/routes', url: '/vue-element-admin/routes',

View File

@ -1,6 +1,6 @@
// Just a mock data // Just a mock data
export const constantRoutes = [ const constantRoutes = [
{ {
path: '/redirect', path: '/redirect',
component: 'layout/Layout', component: 'layout/Layout',
@ -72,7 +72,7 @@ export const constantRoutes = [
} }
] ]
export const asyncRoutes = [ const asyncRoutes = [
{ {
path: '/permission', path: '/permission',
component: 'layout/Layout', component: 'layout/Layout',
@ -523,3 +523,8 @@ export const asyncRoutes = [
{ path: '*', redirect: '/404', hidden: true } { path: '*', redirect: '/404', hidden: true }
] ]
module.exports = {
constantRoutes,
asyncRoutes
}

View File

@ -23,7 +23,7 @@ const users = {
} }
} }
export default [ module.exports = [
// user login // user login
{ {
url: '/vue-element-admin/user/login', url: '/vue-element-admin/user/login',

48
mock/utils.js Normal file
View File

@ -0,0 +1,48 @@
/**
* @param {string} url
* @returns {Object}
*/
function param2Obj(url) {
const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ')
if (!search) {
return {}
}
const obj = {}
const searchArr = search.split('&')
searchArr.forEach(v => {
const index = v.indexOf('=')
if (index !== -1) {
const name = v.substring(0, index)
const val = v.substring(index + 1, v.length)
obj[name] = val
}
})
return obj
}
/**
* 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}
*/
function deepClone(source) {
if (!source && typeof source !== 'object') {
throw new Error('error arguments', 'deepClone')
}
const targetObj = source.constructor === Array ? [] : {}
Object.keys(source).forEach(keys => {
if (source[keys] && typeof source[keys] === 'object') {
targetObj[keys] = deepClone(source[keys])
} else {
targetObj[keys] = source[keys]
}
})
return targetObj
}
module.exports = {
param2Obj,
deepClone
}

View File

@ -1,56 +1,29 @@
{ {
"name": "vue-element-admin", "name": "vue-element-admin",
"version": "4.3.0", "version": "4.3.1",
"description": "A magical vue admin. An out-of-box UI solution for enterprise applications. Newest development stack of vue. Lots of awesome features", "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>", "author": "Pan <panfree23@gmail.com>",
"license": "MIT",
"scripts": { "scripts": {
"dev": "vue-cli-service serve", "dev": "vue-cli-service serve",
"lint": "eslint --ext .js,.vue src",
"build:prod": "vue-cli-service build", "build:prod": "vue-cli-service build",
"build:stage": "vue-cli-service build --mode staging", "build:stage": "vue-cli-service build --mode staging",
"preview": "node build/index.js --preview", "preview": "node build/index.js --preview",
"lint": "eslint --ext .js,.vue src", "new": "plop",
"svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml",
"test:unit": "jest --clearCache && vue-cli-service test:unit", "test:unit": "jest --clearCache && vue-cli-service test:unit",
"test:ci": "npm run lint && npm run test:unit", "test:ci": "npm run lint && npm run test:unit",
"svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml",
"new": "plop",
"deploy": "bash deploy.sh" "deploy": "bash deploy.sh"
}, },
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"src/**/*.{js,vue}": [
"eslint --fix",
"git add"
]
},
"keywords": [
"vue",
"admin",
"dashboard",
"element-ui",
"boilerplate",
"admin-template",
"management-system"
],
"repository": {
"type": "git",
"url": "git+https://github.com/PanJiaChen/vue-element-admin.git"
},
"bugs": {
"url": "https://github.com/PanJiaChen/vue-element-admin/issues"
},
"dependencies": { "dependencies": {
"axios": "0.18.1", "axios": "0.18.1",
"clipboard": "2.0.4", "clipboard": "2.0.4",
"codemirror": "5.45.0", "codemirror": "5.45.0",
"core-js": "3.6.5",
"driver.js": "0.9.5", "driver.js": "0.9.5",
"dropzone": "5.5.1", "dropzone": "5.5.1",
"echarts": "4.2.1", "echarts": "4.2.1",
"element-ui": "2.13.0", "element-ui": "2.13.2",
"file-saver": "2.0.1", "file-saver": "2.0.1",
"fuse.js": "3.4.4", "fuse.js": "3.4.4",
"js-cookie": "2.2.0", "js-cookie": "2.2.0",
@ -62,7 +35,6 @@
"pinyin": "2.9.0", "pinyin": "2.9.0",
"screenfull": "4.2.0", "screenfull": "4.2.0",
"script-loader": "0.7.2", "script-loader": "0.7.2",
"showdown": "1.9.0",
"sortablejs": "1.8.4", "sortablejs": "1.8.4",
"tui-editor": "1.3.3", "tui-editor": "1.3.3",
"vue": "2.6.10", "vue": "2.6.10",
@ -76,42 +48,68 @@
"xlsx": "0.14.1" "xlsx": "0.14.1"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "7.0.0", "@vue/cli-plugin-babel": "4.4.4",
"@babel/register": "7.0.0", "@vue/cli-plugin-eslint": "4.4.4",
"@vue/cli-plugin-babel": "3.5.3", "@vue/cli-plugin-unit-jest": "4.4.4",
"@vue/cli-plugin-eslint": "^3.9.1", "@vue/cli-service": "4.4.4",
"@vue/cli-plugin-unit-jest": "3.5.3",
"@vue/cli-service": "3.5.3",
"@vue/test-utils": "1.0.0-beta.29", "@vue/test-utils": "1.0.0-beta.29",
"autoprefixer": "^9.5.1", "autoprefixer": "9.5.1",
"babel-core": "7.0.0-bridge.0", "babel-eslint": "10.1.0",
"babel-eslint": "10.0.1",
"babel-jest": "23.6.0", "babel-jest": "23.6.0",
"babel-plugin-dynamic-import-node": "2.3.3",
"chalk": "2.4.2", "chalk": "2.4.2",
"chokidar": "2.1.5", "chokidar": "2.1.5",
"connect": "3.6.6", "connect": "3.6.6",
"eslint": "5.15.3", "eslint": "6.7.2",
"eslint-plugin-vue": "5.2.2", "eslint-plugin-vue": "6.2.2",
"html-webpack-plugin": "3.2.0", "html-webpack-plugin": "3.2.0",
"husky": "1.3.1", "husky": "1.3.1",
"lint-staged": "8.1.5", "lint-staged": "8.1.5",
"mockjs": "1.0.1-beta3", "mockjs": "1.0.1-beta3",
"plop": "2.3.0", "plop": "2.3.0",
"runjs": "^4.3.2", "runjs": "4.3.2",
"sass": "^1.26.2", "sass": "1.26.2",
"sass-loader": "^7.1.0", "sass-loader": "8.0.2",
"script-ext-html-webpack-plugin": "2.1.3", "script-ext-html-webpack-plugin": "2.1.3",
"serve-static": "^1.13.2", "serve-static": "1.13.2",
"svg-sprite-loader": "4.1.3", "svg-sprite-loader": "4.1.3",
"svgo": "1.2.0", "svgo": "1.2.0",
"vue-template-compiler": "2.6.10" "vue-template-compiler": "2.6.10"
}, },
"browserslist": [
"> 1%",
"last 2 versions"
],
"bugs": {
"url": "https://github.com/PanJiaChen/vue-element-admin/issues"
},
"engines": { "engines": {
"node": ">=8.9", "node": ">=8.9",
"npm": ">= 3.0.0" "npm": ">= 3.0.0"
}, },
"browserslist": [ "keywords": [
"> 1%", "vue",
"last 2 versions" "admin",
"dashboard",
"element-ui",
"boilerplate",
"admin-template",
"management-system"
],
"license": "MIT",
"lint-staged": {
"src/**/*.{js,vue}": [
"eslint --fix",
"git add"
] ]
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"repository": {
"type": "git",
"url": "git+https://github.com/PanJiaChen/vue-element-admin.git"
}
} }

View File

@ -1,9 +1,2 @@
exports.notEmpty = name => { exports.notEmpty = name => v =>
return v => { !v || v.trim() === '' ? `${name} is required` : true
if (!v || v.trim === '') {
return `${name} is required`
} else {
return true
}
}
}

View File

@ -248,6 +248,7 @@ export default {
// //
isSupported, isSupported,
// //
// eslint-disable-next-line no-prototype-builtins
isSupportTouch: document.hasOwnProperty('ontouchstart'), isSupportTouch: document.hasOwnProperty('ontouchstart'),
// //
step: 1, // 1 2 3 step: 1, // 1 2 3

View File

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

View File

@ -17,8 +17,12 @@ export default {
const vnodes = [] const vnodes = []
if (icon) { if (icon) {
if (icon.includes('el-icon')) {
vnodes.push(<i class={[icon, 'sub-el-icon']} />)
} else {
vnodes.push(<svg-icon icon-class={icon}/>) vnodes.push(<svg-icon icon-class={icon}/>)
} }
}
if (title) { if (title) {
vnodes.push(<span slot='title'>{(title)}</span>) vnodes.push(<span slot='title'>{(title)}</span>)
@ -27,3 +31,11 @@ export default {
} }
} }
</script> </script>
<style scoped>
.sub-el-icon {
color: currentColor;
width: 1em;
height: 1em;
}
</style>

View File

@ -25,7 +25,7 @@ import nestedRouter from './modules/nested'
* meta : { * meta : {
roles: ['admin','editor'] 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 sidebar 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'/'el-icon-x' the icon show in the sidebar
noCache: true if set true, the page will no be cached(default is false) 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 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) breadcrumb: false if set false, the item will hidden in breadcrumb(default is true)
@ -197,7 +197,7 @@ export const asyncRoutes = [
name: 'Example', name: 'Example',
meta: { meta: {
title: 'example', title: 'example',
icon: 'example' icon: 'el-icon-s-help'
}, },
children: [ children: [
{ {

View File

@ -14,6 +14,7 @@ const state = {
const mutations = { const mutations = {
CHANGE_SETTING: (state, { key, value }) => { CHANGE_SETTING: (state, { key, value }) => {
// eslint-disable-next-line no-prototype-builtins
if (state.hasOwnProperty(key)) { if (state.hasOwnProperty(key)) {
state[key] = value state[key] = value
} }

View File

@ -103,8 +103,7 @@ const actions = {
}, },
// dynamically modify permissions // dynamically modify permissions
changeRoles({ commit, dispatch }, role) { async changeRoles({ commit, dispatch }, role) {
return new Promise(async resolve => {
const token = role + '-token' const token = role + '-token'
commit('SET_TOKEN', token) commit('SET_TOKEN', token)
@ -116,15 +115,11 @@ const actions = {
// generate accessible routes map based on roles // generate accessible routes map based on roles
const accessRoutes = await dispatch('permission/generateRoutes', roles, { root: true }) const accessRoutes = await dispatch('permission/generateRoutes', roles, { root: true })
// dynamically add accessible routes // dynamically add accessible routes
router.addRoutes(accessRoutes) router.addRoutes(accessRoutes)
// reset visited views and cached views // reset visited views and cached views
dispatch('tagsView/delAllViews', null, { root: true }) dispatch('tagsView/delAllViews', null, { root: true })
resolve()
})
} }
} }

View File

@ -57,6 +57,11 @@
margin-right: 16px; margin-right: 16px;
} }
.sub-el-icon {
margin-right: 12px;
margin-left: -2px;
}
.el-menu { .el-menu {
border: none; border: none;
height: 100%; height: 100%;
@ -105,6 +110,10 @@
.svg-icon { .svg-icon {
margin-left: 20px; margin-left: 20px;
} }
.sub-el-icon {
margin-left: 19px;
}
} }
} }
@ -118,6 +127,10 @@
margin-left: 20px; margin-left: 20px;
} }
.sub-el-icon {
margin-left: 19px;
}
.el-submenu__icon-arrow { .el-submenu__icon-arrow {
display: none; display: none;
} }
@ -178,6 +191,10 @@
.svg-icon { .svg-icon {
margin-right: 16px; margin-right: 16px;
} }
.sub-el-icon {
margin-right: 12px;
margin-left: -2px;
}
} }
.nest-menu .el-submenu>.el-submenu__title, .nest-menu .el-submenu>.el-submenu__title,

View File

@ -162,19 +162,21 @@ export function param(json) {
* @returns {Object} * @returns {Object}
*/ */
export function param2Obj(url) { export function param2Obj(url) {
const search = url.split('?')[1] const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ')
if (!search) { if (!search) {
return {} return {}
} }
return JSON.parse( const obj = {}
'{"' + const searchArr = search.split('&')
decodeURIComponent(search) searchArr.forEach(v => {
.replace(/"/g, '\\"') const index = v.indexOf('=')
.replace(/&/g, '","') if (index !== -1) {
.replace(/=/g, '":"') const name = v.substring(0, index)
.replace(/\+/g, ' ') + const val = v.substring(index + 1, v.length)
'"}' obj[name] = val
) }
})
return obj
} }
/** /**

View File

@ -0,0 +1,14 @@
import { param2Obj } from '@/utils/index.js'
describe('Utils:param2Obj', () => {
const url = 'https://github.com/PanJiaChen/vue-element-admin?name=bill&age=29&sex=1&field=dGVzdA==&key=%E6%B5%8B%E8%AF%95'
it('param2Obj test', () => {
expect(param2Obj(url)).toEqual({
name: 'bill',
age: '29',
sex: '1',
field: window.btoa('test'),
key: '测试'
})
})
})

View File

@ -49,8 +49,11 @@ module.exports = {
} }
}, },
chainWebpack(config) { chainWebpack(config) {
config.plugins.delete('preload') // TODO: need test // it can improve the speed of the first screen, it is recommended to turn on preload
config.plugins.delete('prefetch') // TODO: need test // config.plugins.delete('preload')
// when there are many pages, it will cause too many meaningless requests
config.plugins.delete('prefetch')
// set svg-sprite-loader // set svg-sprite-loader
config.module config.module