Compare commits

...

21 Commits
v3.7.0 ... pwa

Author SHA1 Message Date
Pan
775f6f5f3a [release] 3.7.1 2018-06-27 10:37:49 +08:00
Pan
2687b2eb3c perf[v-loading]: remove .body modifier #779 2018-06-26 17:10:21 +08:00
Pan
76327a8f26 update doc 2018-06-25 15:04:40 +08:00
Pan
03b708870b update doc 2018-06-25 14:54:02 +08:00
花裤衩
bdc31cea1a chore: use Runtime-only (#799)
Detail see [Runtime-Compiler-vs-Runtime-only](https://vuejs.org/v2/guide/installation.html#Runtime-Compiler-vs-Runtime-only)
2018-06-25 11:17:47 +08:00
Liu Xinyu
ae2ca072f5 fix: typo in readme (#798) 2018-06-25 10:09:19 +08:00
花裤衩
d995cdb332 add[example]: add nested routes example (#789) 2018-06-21 14:26:25 +08:00
Pan
cbc3ddd827 perf[uploadExcel]: add beforeUpload hock 2018-06-20 13:29:57 +08:00
HiiTea
9cf00fd63a fix typo (#784) 2018-06-19 17:53:30 +08:00
Pan
03691739e1 add:[permission]: add checkPermission function 2018-06-19 17:52:05 +08:00
Pan
3f479664b6 perf[utils.js]: Add code comments to deepClone and remove redundant code 2018-06-19 11:26:46 +08:00
临书
cbee7b6f20 fix[TagView-component]: loss route querystring when operating tags (#768) 2018-06-13 16:57:07 +08:00
临书
2a590a2087 Support route query in TagView (#765) 2018-06-12 13:53:56 +08:00
Pan
c93fcefe54 perf[UploadExcel-component]: tweaks 2018-06-12 10:53:08 +08:00
Pan
9f8ac37497 perf[UploadExcel-component]: set readerData to promise 2018-06-12 09:56:53 +08:00
Pan
8c685cc4c6 doc: add code comments to request.js 2018-06-11 13:04:11 +08:00
kuviki
e40fd27775 Fix typo (#747) 2018-06-07 13:26:10 +08:00
Pan
f3ccd9f04e perf[Tinymce]: remove legacyoutput #745 2018-06-06 19:46:31 +08:00
Pan
914a4ec62c chore: assetsPublicPath 2018-06-04 13:07:53 +08:00
Pan
739aef4387 Remove redundant code 2018-06-01 17:04:33 +08:00
Insua
a7942636c6 fix(Tinymce): When content is null,set content to '' to avoid error (#732)
When get rich editor value(null) from backend,it will occur error
```
Error in nextTick: "TypeError: Cannot read property 'replace' of null"
```
So I set val to '' to avoid it
2018-06-01 15:38:08 +08:00
38 changed files with 362 additions and 169 deletions

106
README.md
View File

@@ -7,7 +7,7 @@
<img src="https://img.shields.io/badge/vue-2.5.10-brightgreen.svg" alt="vue">
</a>
<a href="https://github.com/ElemeFE/element">
<img src="https://img.shields.io/badge/element--ui-2.3.0-brightgreen.svg" alt="element-ui">
<img src="https://img.shields.io/badge/element--ui-2.3.2-brightgreen.svg" alt="element-ui">
</a>
<a href="https://travis-ci.org/PanJiaChen/vue-element-admin" rel="nofollow">
<img src="https://travis-ci.org/PanJiaChen/vue-element-admin.svg?branch=master" alt="Build Status">
@@ -24,33 +24,34 @@ English | [简体中文](./README.zh-CN.md)
## Introduction
`vue-element-admin` is a production-ready solution for admin interfaces. Based on [Vue.js](https://github.com/vuejs/vue) and use the UI Toolkit -- [element](https://github.com/ElemeFE/element). `vue-element-admin` is a magical vue admin, it 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.
[vue-element-admin](http://panjiachen.github.io/vue-element-admin) is a front-end management background integration solution. It based on [vue](https://github.com/vuejs/vue) and use the UI Toolkit [element](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)
- [Documentation](https://panjiachen.github.io/vue-element-admin-site/#/)
- [Documentation](https://panjiachen.github.io/vue-element-admin-site/)
- [Gitter](https://gitter.im/vue-element-admin/discuss)
- [Wiki](https://github.com/PanJiaChen/vue-element-admin/wiki)
- [Donate](https://panjiachen.github.io/vue-element-admin-site/#/donate)
- [Donate](https://panjiachen.github.io/vue-element-admin-site/donate/)
**vue-element-admin is a admin interfaces integration solution, which is not suitable for secondary development as a base template.**
**This project is positioned as a background integration solution and is not suitable for secondary development as a basic template.**
- Base template recommends using: [vueAdmin-template](https://github.com/PanJiaChen/vueAdmin-template)  
- Desktop: [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
**Note: This project uses element-ui@2.3.0+ version, so the minimum compatible vue@2.5.0+**
## 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/) [element-ui](https://github.com/ElemeFE/element). All data requests for this project are simulated using [Mock.js](https://github.com/nuysoft/Mock). It would be helpful if you have pre-existing knowledge on those.
**This project is not a scaffolding and is more of an integrated solution.**
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/), [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.
**This project does not support low version browsers (e.g. IE). Please add polyfill yourself if you need them.**
**Note: This project uses element-ui@2.3.0+ version, so the minimum compatible vue@2.5.0+**
<p align="center">
<img width="900" src="https://wpimg.wallstcn.com/a5894c1b-f6af-456e-82df-1151da0839bf.png">
</p>
@@ -58,41 +59,64 @@ You need to install [node](http://nodejs.org/) and [git](https://git-scm.com/) l
## Features
```
- Login / Logout
- Permission authentication
- Permission Authentication
- Page permission
- Directive permission
- Two-step login
- Multi-environment build
- Dynamic sidebar (supports multi-level routing)
- Dynamic breadcrumb
- I18n
- Customizable theme
- Tags-view(Tab page Support right-click operation)
- Rich text editor
- Markdown editor
- JSON editor
- Screenfull
- Drag and drop list
- Svg Sprite
- dev sit stage prod
- Global Features
- I18n
- Multiple dynamic themes
- Dynamic sidebar (supports multi-level routing)
- Dynamic breadcrumb
- Tags-view(Tab page Support right-click operation)
- Svg Sprite
- Mock data
- Screenfull
- Responsive Sidebar
- Editor
- Rich Text Editor
- Markdown Editor
- JSON Editor
- Excel
- Export Excel
- Export zip
- Upload Excel
- Visualization Excel
- Table
- Dynamic Table
- Drag And Drop Table
- Tree Table
- Inline Edit Table
- Error Page
- 401
- 404
- Components
- Avatar Upload
- Back To Top
- Drag Dialog
- Drag Kanban
- Drag List
- SplitPane
- Dropzone
- Sticky
- CountTo
- Advanced Example
- Error Log
- Dashboard
- Guide Page
- Mock data
- Echarts
- Clipboard
- 401/404 error page
- Error log
- Export excel
- Export zip
- Front-end visualization excel
- Tree Table
- Table example
- Dynamictable example
- Drag and drop table example
- Inline edit table example
- Form example
- Two-step login
- SplitPane
- Drag Dialog
- Dropzone
- Sticky
- CountTo
- Markdown to html
```
@@ -135,7 +159,7 @@ npm run lint
npm run lint -- --fix
```
Refer to [Documentation](https://panjiachen.github.io/vue-element-admin-site/#/deploy) for more information
Refer to [Documentation](https://panjiachen.github.io/vue-element-admin-site/guide/essentials/deploy.html) for more information
## Changelog
Detailed changes for each release are documented in the [release notes](https://github.com/PanJiaChen/vue-element-admin/releases).

View File

@@ -7,7 +7,7 @@
<img src="https://img.shields.io/badge/vue-2.5.10-brightgreen.svg" alt="vue">
</a>
<a href="https://github.com/ElemeFE/element">
<img src="https://img.shields.io/badge/element--ui-2.3.0-brightgreen.svg" alt="element-ui">
<img src="https://img.shields.io/badge/element--ui-2.3.2-brightgreen.svg" alt="element-ui">
</a>
<a href="https://travis-ci.org/PanJiaChen/vue-element-admin" rel="nofollow">
<img src="https://travis-ci.org/PanJiaChen/vue-element-admin.svg?branch=master" alt="Build Status">
@@ -24,27 +24,25 @@
## 简介
`vue-element-admin` 是一个后台集成解决方案,它基于 [Vue.js](https://github.com/vuejs/vue) 和 [element](https://github.com/ElemeFE/element)。它使用了最新的前端技术栈内置了i18国际化解决方案动态路由权限验证等很多功能特性,相信不管你的需求是什么,本项目都能帮助到你。
[vue-element-admin](http://panjiachen.github.io/vue-element-admin) 是一个后台集成解决方案,它基于 [vue](https://github.com/vuejs/vue) 和 [element](https://github.com/ElemeFE/element)。它使用了最新的前端技术栈内置了i18国际化解决方案动态路由权限验证,提炼了典型的业务模型,提供了丰富的功能组件,它可以帮助你快速搭建企业级中后台产品原型。相信不管你的需求是什么,本项目都能帮助到你。
- [在线访问](http://panjiachen.github.io/vue-element-admin)
- [使用文档](https://panjiachen.github.io/vue-element-admin-site/#/zh-cn/)
- [使用文档](https://panjiachen.github.io/vue-element-admin-site/zh/)
- [Gitter讨论组](https://gitter.im/vue-element-admin/discuss)
- [Wiki](https://github.com/PanJiaChen/vue-element-admin/wiki)
- [Donate](https://panjiachen.github.io/vue-element-admin-site/#/zh-cn/donate)
- [Donate](https://panjiachen.github.io/vue-element-admin-site/zh/donate/)
**本项目的定位是后台集成方案,不适合当基础模板来开发。**
- 模板建议使用: [vueAdmin-template](https://github.com/PanJiaChen/vueAdmin-template)  
- 桌面端: [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
**注意:该项目使用 element-ui@2.3.0+ 版本,所以最低兼容 vue@2.5.0+**
## 前序准备
的本地环境需要安装 [node](http://nodejs.org/) 和 [git](https://git-scm.com/)。我们的技术栈基于 [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/) and [element-ui](https://github.com/ElemeFE/element),所有的请求数据都使用[Mock.js](https://github.com/nuysoft/Mock)模拟,提前了解和学习这些知识会对使用本项目有很大的帮助。
你需要在本地安装 [node](http://nodejs.org/) 和 [git](https://git-scm.com/)。本项目技术栈基于 [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/) 、[axios](https://github.com/axios/axios) 和 [element-ui](https://github.com/ElemeFE/element),所有的请求数据都使用[Mock.js](https://github.com/nuysoft/Mock)模拟,提前了解和学习这些知识会对使用本项目有很大的帮助。
同时配套一个系列的教程文章,如何从零构建后一个完整的后台项目,建议大家先看完这些文章再来实践本项目
- [手摸手,带你用 vue 撸后台 系列一(基础篇)](https://juejin.im/post/59097cd7a22b9d0065fb61d2)
@@ -61,6 +59,8 @@
**本项目并不是一个脚手架,更倾向于是一个集成解决方案**
**注意:该项目使用 element-ui@2.3.0+ 版本,所以最低兼容 vue@2.5.0+**
**该项目不支持低版本浏览器(如ie)有需求请自行添加polyfill [详情](https://github.com/PanJiaChen/vue-element-admin/wiki#babel-polyfill)**
<p align="center">
@@ -69,42 +69,65 @@
## 功能
```
- 登录/注销
- 登录 / 注销
- 权限验证
- 页面权限
- 指令权限
- 二步登录
- 多环境发布
- 动态侧边栏(支持多级路由)
- 动态面包屑
- 国际化多语言
- 多种动态换肤
- 快捷导航(标签页)
- 富文本编辑器
- Markdown编辑器
- JSON编辑器
- Screenfull全屏
- 列表拖拽
- Svg Sprite 图标
- dev sit stage prod
- 全局功能
- 国际化多语言
- 多种动态换肤
- 动态侧边栏(支持多级路由嵌套)
- 动态面包屑
- 快捷导航(标签页)
- Svg Sprite 图标
- 本地mock数据
- Screenfull全屏
- 自适应收缩侧边栏
- 编辑器
- 富文本
- Markdown
- JSON 等多格式
- Excel
- 导出excel
- 导出zip
- 导入excel
- 前端可视化excel
- 表格
- 动态表格
- 拖拽表格
- 树形表格
- 内联编辑
- 错误页面
- 401
- 404
- 組件
- 头像上传
- 返回顶部
- 拖拽Dialog
- 拖拽看板
- 列表拖拽
- SplitPane
- Dropzone
- Sticky
- CountTo
- 综合实例
- 错误日志
- Dashboard
- 引导页
- 本地mock数据
- Echarts 图表
- Clipboard(剪贴复制)
- 401/404错误页面
- 错误日志
- 导出excel
- 导出zip
- 前端可视化excel
- 树形table
- Table example
- 动态table example
- 拖拽table example
- 内联编辑table example
- Form example
- 二步登录
- SplitPane
- 拖拽 Dialog
- Dropzone
- Sticky
- CountTo
- Markdown2html
```
@@ -148,7 +171,7 @@ npm run lint
npm run lint -- --fix
```
更多信息请参考 [使用文档](https://panjiachen.github.io/vue-element-admin-site/#/zh-cn/deploy)
更多信息请参考 [使用文档](https://panjiachen.github.io/vue-element-admin-site/zh/)
## Changelog
Detailed changes for each release are documented in the [release notes](https://github.com/PanJiaChen/vue-element-admin/releases).

View File

@@ -34,7 +34,6 @@ module.exports = {
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
}
},

View File

@@ -56,13 +56,18 @@ module.exports = {
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
// you can set by youself according to actual condition
assetsPublicPath: './',
/**
* You can set by youself according to actual condition
* You will need to set this if you plan to deploy your site under a sub path,
* for example GitHub pages. If you plan to deploy your site to https://foo.github.io/bar/,
* then assetsPublicPath should be set to "/bar/".
* In most cases please use '/' !!!
*/
assetsPublicPath: '/vue-element-admin/', // If you are deployed on the root path, please use '/'
/**
* Source Maps
*/
productionSourceMap: false,
// https://webpack.js.org/configuration/devtool/#production
devtool: '#source-map',

View File

@@ -1,6 +1,6 @@
{
"name": "vue-element-admin",
"version": "3.7.0",
"version": "3.7.1",
"description": "A magical vue admin. Typical templates for enterprise applications. Newest development stack of vue. Lots of awesome features",
"author": "Pan <panfree23@gmail.com>",
"license": "MIT",

View File

@@ -50,7 +50,8 @@ export default {
watch: {
value(val) {
if (!this.hasChange && this.hasInit) {
this.$nextTick(() => window.tinymce.get(this.tinymceId).setContent(val))
this.$nextTick(() =>
window.tinymce.get(this.tinymceId).setContent(val || ''))
}
}
},

View File

@@ -2,6 +2,6 @@
// Detail plugins list see https://www.tinymce.com/docs/plugins/
// Custom builds see https://www.tinymce.com/download/custom-builds/
const plugins = ['advlist anchor autolink autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools importcss insertdatetime legacyoutput link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount']
const plugins = ['advlist anchor autolink autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools importcss insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount']
export default plugins

View File

@@ -1,9 +1,9 @@
<template>
<div>
<input id="excel-upload-input" ref="excel-upload-input" type="file" accept=".xlsx, .xls" class="c-hide" @change="handkeFileChange">
<input id="excel-upload-input" ref="excel-upload-input" type="file" accept=".xlsx, .xls" @change="handleClick">
<div id="drop" @drop="handleDrop" @dragover="handleDragover" @dragenter="handleDragover">
Drop excel file here or
<el-button style="margin-left:16px;" size="mini" type="primary" @click="handleUpload">browse</el-button>
<el-button :loading="loading" style="margin-left:16px;" size="mini" type="primary" @click="handleUpload">Browse</el-button>
</div>
</div>
</template>
@@ -12,6 +12,10 @@
import XLSX from 'xlsx'
export default {
props: {
beforeUpload: Function,
onSuccess: Function
},
data() {
return {
loading: false,
@@ -25,18 +29,19 @@ export default {
generateDate({ header, results }) {
this.excelData.header = header
this.excelData.results = results
this.$emit('on-selected-file', this.excelData)
this.onSuccess && this.onSuccess(this.excelData)
},
handleDrop(e) {
e.stopPropagation()
e.preventDefault()
if (this.loading) return
const files = e.dataTransfer.files
if (files.length !== 1) {
this.$message.error('Only support uploading one file!')
return
}
const itemFile = files[0] // only use files[0]
this.readerData(itemFile)
const rawFile = files[0] // only use files[0]
this.upload(rawFile)
e.stopPropagation()
e.preventDefault()
},
@@ -48,26 +53,42 @@ export default {
handleUpload() {
document.getElementById('excel-upload-input').click()
},
handkeFileChange(e) {
handleClick(e) {
const files = e.target.files
const itemFile = files[0] // only use files[0]
if (!itemFile) return
this.readerData(itemFile)
this.$refs['excel-upload-input'].value = null // fix can't select the same excel
const rawFile = files[0] // only use files[0]
if (!rawFile) return
this.upload(rawFile)
},
readerData(itemFile) {
const reader = new FileReader()
reader.onload = e => {
const data = e.target.result
const fixedData = this.fixdata(data)
const workbook = XLSX.read(btoa(fixedData), { type: 'base64' })
const firstSheetName = workbook.SheetNames[0]
const worksheet = workbook.Sheets[firstSheetName]
const header = this.get_header_row(worksheet)
const results = XLSX.utils.sheet_to_json(worksheet)
this.generateDate({ header, results })
upload(rawFile) {
this.$refs['excel-upload-input'].value = null // fix can't select the same excel
if (!this.beforeUpload) {
this.readerData(rawFile)
return
}
reader.readAsArrayBuffer(itemFile)
const before = this.beforeUpload(rawFile)
if (before) {
this.readerData(rawFile)
}
},
readerData(rawFile) {
this.loading = true
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = e => {
const data = e.target.result
const fixedData = this.fixdata(data)
const workbook = XLSX.read(btoa(fixedData), { type: 'base64' })
const firstSheetName = workbook.SheetNames[0]
const worksheet = workbook.Sheets[firstSheetName]
const header = this.get_header_row(worksheet)
const results = XLSX.utils.sheet_to_json(worksheet)
this.generateDate({ header, results })
this.loading = false
resolve()
}
reader.readAsArrayBuffer(rawFile)
})
},
fixdata(data) {
let o = ''

1
src/icons/svg/nested.svg Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1529559567446" class="icon" style="" viewBox="0 0 1167 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1767" xmlns:xlink="http://www.w3.org/1999/xlink" width="227.9296875" height="200"><defs><style type="text/css"></style></defs><path d="M0.015952 74.459413A2.286 2.286 1440 1 0 145.85218 74.459413 2.286 2.286 1440 1 0 0.015952 74.459413zM291.720312 1.525347 1166.801488 1.525347 1166.801488 147.361574 291.720312 147.361574zM291.720312 366.163773A2.286 2.286 1440 1 0 437.55654 366.163773 2.286 2.286 1440 1 0 291.720312 366.163773zM583.424672 293.229707 1166.801488 293.229707 1166.801488 439.065934 583.424672 439.065934zM291.720312 949.540588A2.286 2.286 1440 1 0 437.55654 949.540588 2.286 2.286 1440 1 0 291.720312 949.540588zM583.424672 876.638427 1166.801488 876.638427 1166.801488 1022.474654 583.424672 1022.474654zM583.424672 657.836228A2.286 2.286 1440 1 0 729.2609 657.836228 2.286 2.286 1440 1 0 583.424672 657.836228zM875.129032 584.934067 1166.801488 584.934067 1166.801488 730.770294 875.129032 730.770294z" p-id="1768"></path></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -28,6 +28,10 @@ export default {
lineChart: 'Line Chart',
mixChart: 'Mix Chart',
example: 'Example',
nested: 'Nested Routes',
bar: 'Bar',
barProfile: 'Profile',
barPosts: 'Posts',
Table: 'Table',
dynamicTable: 'Dynamic Table',
dragTable: 'Drag Table',

View File

@@ -28,6 +28,10 @@ export default {
lineChart: '折线图',
mixChart: '混合图表',
example: '综合实例',
nested: '路由嵌套',
bar: 'Bar',
barProfile: 'Profile',
barPosts: 'Posts',
Table: 'Table',
dynamicTable: '动态Table',
dragTable: '拖拽Table',

View File

@@ -36,6 +36,5 @@ new Vue({
router,
store,
i18n,
template: '<App/>',
components: { App }
render: h => h(App)
})

View File

@@ -7,7 +7,7 @@ import { getToken } from '@/utils/auth' // getToken from cookie
NProgress.configure({ showSpinner: false })// NProgress Configuration
// permissiom judge function
// permission judge function
function hasPermission(roles, permissionRoles) {
if (roles.indexOf('admin') >= 0) return true // admin permission passed directly
if (!permissionRoles) return true

View File

@@ -1 +0,0 @@
module.exports = file => require('@/views/' + file + '.vue').default // vue-loader at least v13.0.0+

View File

@@ -1 +0,0 @@
module.exports = file => () => import('@/views/' + file + '.vue')

View File

@@ -7,7 +7,7 @@ Vue.use(Router)
import Layout from '@/views/layout/Layout'
/** note: submenu only apppear when children.length>=1
* detail see https://panjiachen.github.io/vue-element-admin-site/#/router-and-nav?id=sidebar
* detail see https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
**/
/**
@@ -199,6 +199,39 @@ export const asyncRouterMap = [
]
},
{
path: '/nested',
component: Layout,
redirect: '/nested/bar/profile',
name: 'nested',
meta: {
title: 'nested',
icon: 'nested'
},
children: [
{
path: '/nested/bar', // Must write the full path
component: () => import('@/views/nested/bar/index'), // Parent router-view
name: 'bar',
meta: { title: 'bar' },
children: [
{
path: 'profile',
component: () => import('@/views/nested/bar/profile'),
name: 'bar-profile',
meta: { title: 'barProfile' }
},
{
path: 'posts',
component: () => import('@/views/nested/bar/posts'),
name: 'bar-posts',
meta: { title: 'barPosts' }
}
]
}
]
},
{
path: '/error',
component: Layout,

View File

@@ -6,11 +6,9 @@ const tagsView = {
mutations: {
ADD_VISITED_VIEWS: (state, view) => {
if (state.visitedViews.some(v => v.path === view.path)) return
state.visitedViews.push({
name: view.name,
path: view.path,
state.visitedViews.push(Object.assign({}, view, {
title: view.meta.title || 'no-name'
})
}))
if (!view.meta.noCache) {
state.cachedViews.push(view.name)
}

View File

@@ -246,6 +246,11 @@ 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
*/
export function deepClone(source) {
if (!source && typeof source !== 'object') {
throw new Error('error arguments', 'shallowClone')
@@ -253,7 +258,6 @@ export function deepClone(source) {
const targetObj = source.constructor === Array ? [] : {}
Object.keys(source).forEach((keys) => {
if (source[keys] && typeof source[keys] === 'object') {
targetObj[keys] = source[keys].constructor === Array ? [] : {}
targetObj[keys] = deepClone(source[keys])
} else {
targetObj[keys] = source[keys]

26
src/utils/permission.js Normal file
View File

@@ -0,0 +1,26 @@
import store from '@/store'
/**
* @param {Array} value
* @returns {Boolean}
* @example see @/views/permission/directive.vue
*/
export default function checkPermission(value) {
if (value && value instanceof Array && value.length > 0) {
const roles = store.getters && store.getters.roles
const permissionRoles = value
const hasPermission = roles.some(role => {
return permissionRoles.includes(role)
})
if (!hasPermission) {
return false
}
return true
} else {
console.error(`need roles! Like v-permission="['admin','editor']"`)
return false
}
}

View File

@@ -13,7 +13,8 @@ const service = axios.create({
service.interceptors.request.use(config => {
// Do something before request is sent
if (store.getters.token) {
config.headers['X-Token'] = getToken() // 让每个请求携带token-- ['X-Token']为自定义key 请根据实际情况自行修改
// 让每个请求携带token-- ['X-Token']为自定义key 请根据实际情况自行修改
config.headers['X-Token'] = getToken()
}
return config
}, error => {
@@ -26,34 +27,40 @@ service.interceptors.request.use(config => {
service.interceptors.response.use(
response => response,
/**
* 下面的注释为通过response自定义code来标示请求状态当code返回如下情况为权限有问题登出并返回到登录页
* 如通过xmlhttprequest 状态码标识 逻辑可写在下面error中
*/
// const res = response.data;
// if (res.code !== 20000) {
// Message({
// message: res.message,
// type: 'error',
// duration: 5 * 1000
// });
// // 50008:非法的token; 50012:其他客户端登录了; 50014:Token 过期了;
// if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
// MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', {
// confirmButtonText: '重新登录',
// cancelButtonText: '取消',
// type: 'warning'
// }).then(() => {
// store.dispatch('FedLogOut').then(() => {
// location.reload();// 为了重新实例化vue-router对象 避免bug
// });
* 下面的注释为通过response里,自定义code来标示请求状态
* 当code返回如下情况则说明权限有问题登出并返回到登录页
* 如想通过xmlhttprequest来状态码标识 逻辑可写在下面error中
* 以下代码均为样例,请结合自生需求加以修改,若不需要,则可删除
*/
// response => {
// const res = response.data
// if (res.code !== 20000) {
// Message({
// message: res.message,
// type: 'error',
// duration: 5 * 1000
// })
// // 50008:非法的token; 50012:其他客户端登录了; 50014:Token 过期了;
// if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
// // 请自行在引入 MessageBox
// // import { Message, MessageBox } from 'element-ui'
// MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', {
// confirmButtonText: '重新登录',
// cancelButtonText: '取消',
// type: 'warning'
// }).then(() => {
// store.dispatch('FedLogOut').then(() => {
// location.reload() // 为了重新实例化vue-router对象 避免bug
// })
// }
// return Promise.reject('error');
// } else {
// return response.data;
// })
// }
// return Promise.reject('error')
// } else {
// return response.data
// }
// },
error => {
console.log('err' + error)// for debug
console.log('err' + error) // for debug
Message({
message: error.message,
type: 'error',

View File

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

View File

@@ -1,6 +1,6 @@
<template>
<div class="app-container documentation-container">
<a class="document-btn" target='_blank' href="https://panjiachen.github.io/vue-element-admin-site/#/">{{$t('documentation.documentation')}}</a>
<a class="document-btn" target='_blank' href="https://panjiachen.github.io/vue-element-admin-site/">{{$t('documentation.documentation')}}</a>
<a class="document-btn" target='_blank' href="https://github.com/PanJiaChen/vue-element-admin/">{{$t('documentation.github')}}</a>
<dropdown-menu style="float:left;margin-left:50px;" title='系列文章' :items='articleList'></dropdown-menu>
</div>

View File

@@ -6,7 +6,7 @@
<h3>{{$t('errorLog.tips')}}</h3>
<code>
{{$t('errorLog.description')}}
<a target="_blank" class="link-type" href="https://panjiachen.github.io/vue-element-admin-site/#/error?id=%e4%bb%a3%e7%a0%81">
<a target="_blank" class="link-type" href="https://panjiachen.github.io/vue-element-admin-site/guide/advanced/error.html">
{{$t('errorLog.documentation')}}
</a>
</code>

View File

@@ -2,7 +2,7 @@
<p class="warn-content">
创建和编辑页面是不能被keep-alive 缓存的因为keep-alive 的include 目前不支持根据路由来缓存所以目前都是基于component name 来缓存的如果你想要实现缓存的效果可以使用localstorage 等游览器缓存方案或者不要使用keep-alive
的include直接缓存所有页面详情见
<a href="https://panjiachen.github.io/vue-element-admin-site/#/zh-cn/tags-view?id=%E7%BC%93%E5%AD%98%E4%B8%8D%E9%80%82%E5%90%88%E5%9C%BA%E6%99%AF"
<a href="https://panjiachen.github.io/vue-element-admin-site/guide/essentials/tags-view.html"
target="_blank">文档</a>
</p>
</template>

View File

@@ -11,7 +11,7 @@
</el-radio-group>
<el-button style='margin:0 0 20px 20px;' type="primary" icon="document" @click="handleDownload" :loading="downloadLoading">{{$t('excel.export')}} excel</el-button>
<el-table :data="list" v-loading.body="listLoading" element-loading-text="拼命加载中" border fit highlight-current-row>
<el-table :data="list" v-loading="listLoading" element-loading-text="拼命加载中" border fit highlight-current-row>
<el-table-column align="center" label='Id' width="95">
<template slot-scope="scope">
{{scope.$index}}

View File

@@ -3,7 +3,7 @@
<!-- $t is vue-i18n global function to translate lang -->
<el-input style='width:340px;' :placeholder="$t('excel.placeholder')" prefix-icon="el-icon-document" v-model="filename"></el-input>
<el-button style='margin-bottom:20px' type="primary" icon="document" @click="handleDownload" :loading="downloadLoading">{{$t('excel.selectedExport')}}</el-button>
<el-table :data="list" v-loading.body="listLoading" element-loading-text="拼命加载中" border fit highlight-current-row @selection-change="handleSelectionChange"
<el-table :data="list" v-loading="listLoading" element-loading-text="拼命加载中" border fit highlight-current-row @selection-change="handleSelectionChange"
ref="multipleTable">
<el-table-column type="selection" align="center"></el-table-column>
<el-table-column align="center" label='Id' width="95">

View File

@@ -1,6 +1,6 @@
<template>
<div class="app-container">
<upload-excel-component @on-selected-file='selected'></upload-excel-component>
<upload-excel-component :on-success='handleSuccess' :before-upload="beforeUpload"></upload-excel-component>
<el-table :data="tableData" border highlight-current-row style="width: 100%;margin-top:20px;">
<el-table-column v-for='item of tableHeader' :prop="item" :label="item" :key='item'>
</el-table-column>
@@ -21,9 +21,21 @@ export default {
}
},
methods: {
selected(data) {
this.tableData = data.results
this.tableHeader = data.header
beforeUpload(file) {
const isLt2M = file.size / 1024 / 1024 < 1
if (isLt2M) {
return true
}
this.$message({
message: 'Please do not upload files larger than 2m in size.',
type: 'warning'
})
return false
},
handleSuccess({ results, header }) {
this.tableData = results
this.tableHeader = header
}
}
}

View File

@@ -2,7 +2,7 @@
<div class="tags-view-container">
<scroll-pane class='tags-view-wrapper' ref='scrollPane'>
<router-link ref='tag' class="tags-view-item" :class="isActive(tag)?'active':''" v-for="tag in Array.from(visitedViews)"
:to="tag.path" :key="tag.path" @contextmenu.prevent.native="openMenu(tag,$event)">
:to="tag" :key="tag.path" @contextmenu.prevent.native="openMenu(tag,$event)">
{{generateTitle(tag.title)}}
<span class='el-icon-close' @click.prevent.stop='closeSelectedTag(tag)'></span>
</router-link>
@@ -72,7 +72,7 @@ export default {
const tags = this.$refs.tag
this.$nextTick(() => {
for (const tag of tags) {
if (tag.to === this.$route.path) {
if (tag.to.path === this.$route.path) {
this.$refs.scrollPane.moveToTarget(tag.$el)
break
}
@@ -84,7 +84,7 @@ export default {
if (this.isActive(view)) {
const latestView = views.slice(-1)[0]
if (latestView) {
this.$router.push(latestView.path)
this.$router.push(latestView)
} else {
this.$router.push('/')
}
@@ -92,7 +92,7 @@ export default {
})
},
closeOthersTags() {
this.$router.push(this.selectedTag.path)
this.$router.push(this.selectedTag)
this.$store.dispatch('delOthersViews', this.selectedTag).then(() => {
this.moveToCurrentTag()
})

View File

@@ -0,0 +1,7 @@
<template>
<div class="app-container">
<code>Parent View: Bar</code>
<img src="https://wpimg.wallstcn.com/be29a7d2-5ccf-4a2b-888d-8a6c2bbb7aac.png">
<router-view></router-view>
</div>
</template>

View File

@@ -0,0 +1,6 @@
<template>
<div style="margin-top:30px;">
<el-alert title="Children: Posts" type="warning" :closable="false">
</el-alert>
</div>
</template>

View File

@@ -0,0 +1,6 @@
<template>
<div style="margin-top:30px;">
<el-alert title="Children: Profile" type="success" :closable="false">
</el-alert>
</div>
</template>

View File

@@ -16,11 +16,23 @@
<el-tag class="permission-tag" size="small">editor</el-tag> can see this
</span>
</div>
<div style="margin-top:30px;" :key="'checkPermission'+key">
<code>In some cases it is not suitable to use v-permission, such as element Tab component which can only be achieved by manually setting the v-if.
<br> e.g.
</code>
<el-tabs type="border-card" style="width:500px;">
<el-tab-pane v-if="checkPermission(['admin'])" label="Admin">Admin can see this</el-tab-pane>
<el-tab-pane v-if="checkPermission(['editor'])" label="Editor">Editor can see this</el-tab-pane>
<el-tab-pane v-if="checkPermission(['admin','editor'])" label="Admin-OR-Editor">Both admin or editor can see this</el-tab-pane>
</el-tabs>
</div>
</div>
</template>
<script>
import permission from '@/directive/permission/index.js' // 权限判断指令
import checkPermission from '@/utils/permission' // 权限判断函数
import SwitchRoles from './components/SwitchRoles'
export default{
@@ -33,6 +45,7 @@ export default{
}
},
methods: {
checkPermission,
handleRolesChange() {
this.key++
}

View File

@@ -1,7 +1,7 @@
<template>
<div class="icons-container">
<p class="warn-content">
<a href="https://panjiachen.github.io/vue-element-admin-site/#/icon" target="_blank">Add and use
<a href="https://panjiachen.github.io/vue-element-admin-site/guide/advanced/icon.html" target="_blank">Add and use
</a>
</p>
<div class="icons-wrapper">

View File

@@ -21,8 +21,8 @@
<el-checkbox class="filter-item" style='margin-left:15px;' @change='tableKey=tableKey+1' v-model="showReviewer">{{$t('table.reviewer')}}</el-checkbox>
</div>
<el-table :key='tableKey' :data="list" v-loading="listLoading" element-loading-text="给我一点时间" border fit highlight-current-row
style="width: 100%">
<el-table :key='tableKey' :data="list" v-loading="listLoading" border fit highlight-current-row
style="width: 100%;min-height:1000px;">
<el-table-column align="center" :label="$t('table.id')" width="65">
<template slot-scope="scope">
<span>{{scope.row.id}}</span>
@@ -221,7 +221,11 @@ export default {
fetchList(this.listQuery).then(response => {
this.list = response.data.items
this.total = response.data.total
this.listLoading = false
// Just to simulate the time of the request
setTimeout(() => {
this.listLoading = false
}, 1.5 * 1000)
})
},
handleFilter() {

View File

@@ -1,7 +1,7 @@
<template>
<div class="app-container">
<!-- Note that row-key is necessary to get a correct row order. -->
<el-table :data="list" row-key="id" v-loading.body="listLoading" border fit highlight-current-row style="width: 100%">
<el-table :data="list" row-key="id" v-loading="listLoading" border fit highlight-current-row style="width: 100%">
<el-table-column align="center" label="ID" width="65">
<template slot-scope="scope">

View File

@@ -1,7 +1,7 @@
<template>
<div class="app-container">
<el-table :data="list" v-loading.body="listLoading" border fit highlight-current-row style="width: 100%">
<el-table :data="list" v-loading="listLoading" border fit highlight-current-row style="width: 100%">
<el-table-column align="center" label="ID" width="80">
<template slot-scope="scope">
@@ -89,9 +89,7 @@ export default {
const items = response.data.items
this.list = items.map(v => {
this.$set(v, 'edit', false) // https://vuejs.org/v2/guide/reactivity.html
v.originalTitle = v.title // will be used when user click the cancel botton
return v
})
this.listLoading = false

View File

@@ -2,7 +2,7 @@
<div class="app-container">
<el-card class="box-card">
<div slot="header">
<a class='link-type link-title' target="_blank" href='https://panjiachen.github.io/vue-element-admin-site/#/theme'>
<a class='link-type link-title' target="_blank" href='https://panjiachen.github.io/vue-element-admin-site/guide/advanced/theme.html'>
{{$t('theme.documentation')}}
</a>
</div>

View File

@@ -3,7 +3,7 @@
<!-- $t is vue-i18n global function to translate lang -->
<el-input style='width:300px;' :placeholder="$t('zip.placeholder')" prefix-icon="el-icon-document" v-model="filename"></el-input>
<el-button style='margin-bottom:20px;' type="primary" icon="document" @click="handleDownload" :loading="downloadLoading">{{$t('zip.export')}} zip</el-button>
<el-table :data="list" v-loading.body="listLoading" element-loading-text="拼命加载中" border fit highlight-current-row>
<el-table :data="list" v-loading="listLoading" element-loading-text="拼命加载中" border fit highlight-current-row>
<el-table-column align="center" label='ID' width="95">
<template slot-scope="scope">
{{scope.$index}}