Compare commits
52 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
378ca2c217 | ||
|
1df59cc4b6 | ||
|
312a2ca8ed | ||
|
f3733c0b37 | ||
|
0ef14ff5c6 | ||
|
c57c6045c9 | ||
|
fe190b6188 | ||
|
48a966fe1c | ||
|
63d39727ac | ||
|
1e0b9c0055 | ||
|
5f20bfc780 | ||
|
8851a68066 | ||
|
878628b0ed | ||
|
e254fc6c1a | ||
|
513eb66d97 | ||
|
62e1c851c8 | ||
|
f0a01f0fd1 | ||
|
aa7eab58f9 | ||
|
77cb6b1f43 | ||
|
5fbf1cf5da | ||
|
6a5197ad51 | ||
|
9b7a9a64e5 | ||
|
89ce53e185 | ||
|
9e04f58163 | ||
|
d98c5032f8 | ||
|
a575670cef | ||
|
44fa96f142 | ||
|
e4481a9d34 | ||
|
572a2d9c34 | ||
|
5070e20dea | ||
|
59789d92cf | ||
|
775f6f5f3a | ||
|
2687b2eb3c | ||
|
76327a8f26 | ||
|
03b708870b | ||
|
bdc31cea1a | ||
|
ae2ca072f5 | ||
|
d995cdb332 | ||
|
cbc3ddd827 | ||
|
9cf00fd63a | ||
|
03691739e1 | ||
|
3f479664b6 | ||
|
cbee7b6f20 | ||
|
2a590a2087 | ||
|
c93fcefe54 | ||
|
9f8ac37497 | ||
|
8c685cc4c6 | ||
|
e40fd27775 | ||
|
f3ccd9f04e | ||
|
914a4ec62c | ||
|
739aef4387 | ||
|
a7942636c6 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,6 +4,7 @@ dist/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
**/*.log
|
||||
|
||||
test/unit/coverage
|
||||
test/e2e/reports
|
||||
|
111
README.md
111
README.md
@@ -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,36 @@ 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.**
|
||||
- [Gitee](https://panjiachen.gitee.io/vue-element-admin/) 国内用户可访问该地址在线预览
|
||||
|
||||
**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 +61,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
|
||||
```
|
||||
|
||||
@@ -125,6 +151,9 @@ npm run build:prod
|
||||
# --report to build with bundle size analytics
|
||||
npm run build:prod --report
|
||||
|
||||
# --generate a bundle size analytics. default: bundle-report.html
|
||||
npm run build:prod --generate_report
|
||||
|
||||
# --preview to start a server in local to preview
|
||||
npm run build:prod --preview
|
||||
|
||||
@@ -135,7 +164,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).
|
||||
|
111
README.zh-CN.md
111
README.zh-CN.md
@@ -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,27 @@
|
||||
|
||||
## 简介
|
||||
|
||||
`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/)
|
||||
|
||||
- [Gitee](https://panjiachen.gitee.io/vue-element-admin/) 国内用户可访问该地址在线预览
|
||||
|
||||
**本项目的定位是后台集成方案,不适合当基础模板来开发。**
|
||||
- 模板建议使用: [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)
|
||||
@@ -53,7 +53,8 @@
|
||||
- [手摸手,带你用 vue 撸后台 系列四(vueAdmin 一个极简的后台基础模板)](https://juejin.im/post/595b4d776fb9a06bbe7dba56)
|
||||
- [手摸手,带你封装一个 vue component](https://segmentfault.com/a/1190000009090836)
|
||||
- [手摸手,带你优雅的使用 icon](https://juejin.im/post/59bb864b5188257e7a427c09)
|
||||
|
||||
- [手摸手,带你用合理的姿势使用webpack4(上)](https://juejin.im/post/5b56909a518825195f499806)
|
||||
- [手摸手,带你用合理的姿势使用webpack4(下)](https://juejin.im/post/5b5d6d6f6fb9a04fea58aabc)
|
||||
|
||||
或者加入该群主 **[圈子](https://jianshiapp.com/circles/1209)** 楼主会经常分享一些技术相关的东西
|
||||
|
||||
@@ -61,6 +62,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 +72,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
|
||||
```
|
||||
|
||||
@@ -129,14 +155,17 @@ npm run dev
|
||||
# 构建测试环境
|
||||
npm run build:sit
|
||||
|
||||
# 构建生成环境
|
||||
# 构建生产环境
|
||||
npm run build:prod
|
||||
```
|
||||
|
||||
## 其它
|
||||
```bash
|
||||
# --report to build with bundle size analytics
|
||||
npm run build:prod --report
|
||||
npm run build:prod
|
||||
|
||||
# --generate a bundle size analytics. default: bundle-report.html
|
||||
npm run build:prod --generate_report
|
||||
|
||||
# --preview to start a server in local to preview
|
||||
npm run build:prod --preview
|
||||
@@ -148,7 +177,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).
|
||||
|
@@ -8,9 +8,12 @@ const chalk = require('chalk')
|
||||
const webpack = require('webpack')
|
||||
const config = require('../config')
|
||||
const webpackConfig = require('./webpack.prod.conf')
|
||||
const server = require('pushstate-server')
|
||||
var connect = require('connect')
|
||||
var serveStatic = require('serve-static')
|
||||
|
||||
var spinner = ora('building for '+ process.env.env_config+ ' environment...' )
|
||||
const spinner = ora(
|
||||
'building for ' + process.env.env_config + ' environment...'
|
||||
)
|
||||
spinner.start()
|
||||
|
||||
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
|
||||
@@ -18,31 +21,47 @@ rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
|
||||
webpack(webpackConfig, (err, stats) => {
|
||||
spinner.stop()
|
||||
if (err) throw err
|
||||
process.stdout.write(stats.toString({
|
||||
colors: true,
|
||||
modules: false,
|
||||
children: false,
|
||||
chunks: false,
|
||||
chunkModules: false
|
||||
}) + '\n\n')
|
||||
process.stdout.write(
|
||||
stats.toString({
|
||||
colors: true,
|
||||
modules: false,
|
||||
children: false,
|
||||
chunks: false,
|
||||
chunkModules: false
|
||||
}) + '\n\n'
|
||||
)
|
||||
|
||||
if (stats.hasErrors()) {
|
||||
console.log(chalk.red(' Build failed with errors.\n'))
|
||||
console.log(chalk.red(' Build failed with errors.\n'))
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
console.log(chalk.cyan(' Build complete.\n'))
|
||||
console.log(chalk.yellow(
|
||||
' Tip: built files are meant to be served over an HTTP server.\n' +
|
||||
' Opening index.html over file:// won\'t work.\n'
|
||||
))
|
||||
if(process.env.npm_config_preview){
|
||||
server.start({
|
||||
port: 9526,
|
||||
directory: './dist',
|
||||
file: '/index.html'
|
||||
});
|
||||
console.log('> Listening at ' + 'http://localhost:9526' + '\n')
|
||||
console.log(chalk.cyan(' Build complete.\n'))
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
' Tip: built files are meant to be served over an HTTP server.\n' +
|
||||
" Opening index.html over file:// won't work.\n"
|
||||
)
|
||||
)
|
||||
|
||||
if (process.env.npm_config_preview) {
|
||||
const port = 9526
|
||||
const host = 'http://localhost:' + port
|
||||
const basePath = config.build.assetsPublicPath
|
||||
const app = connect()
|
||||
|
||||
app.use(
|
||||
basePath,
|
||||
serveStatic('./dist', {
|
||||
index: ['index.html', '/']
|
||||
})
|
||||
)
|
||||
|
||||
app.listen(port, function() {
|
||||
console.log(
|
||||
chalk.green(`> Listening at http://localhost:${port}${basePath}`)
|
||||
)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@@ -4,8 +4,11 @@ const semver = require('semver')
|
||||
const packageConfig = require('../package.json')
|
||||
const shell = require('shelljs')
|
||||
|
||||
function exec (cmd) {
|
||||
return require('child_process').execSync(cmd).toString().trim()
|
||||
function exec(cmd) {
|
||||
return require('child_process')
|
||||
.execSync(cmd)
|
||||
.toString()
|
||||
.trim()
|
||||
}
|
||||
|
||||
const versionRequirements = [
|
||||
@@ -24,23 +27,30 @@ if (shell.which('npm')) {
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = function () {
|
||||
module.exports = function() {
|
||||
const warnings = []
|
||||
|
||||
for (let i = 0; i < versionRequirements.length; i++) {
|
||||
const mod = versionRequirements[i]
|
||||
|
||||
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
|
||||
warnings.push(mod.name + ': ' +
|
||||
chalk.red(mod.currentVersion) + ' should be ' +
|
||||
chalk.green(mod.versionRequirement)
|
||||
warnings.push(
|
||||
mod.name +
|
||||
': ' +
|
||||
chalk.red(mod.currentVersion) +
|
||||
' should be ' +
|
||||
chalk.green(mod.versionRequirement)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (warnings.length) {
|
||||
console.log('')
|
||||
console.log(chalk.yellow('To use this template, you must update following to modules:'))
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
'To use this template, you must update following to modules:'
|
||||
)
|
||||
)
|
||||
console.log()
|
||||
|
||||
for (let i = 0; i < warnings.length; i++) {
|
||||
|
@@ -1,18 +1,19 @@
|
||||
'use strict'
|
||||
const path = require('path')
|
||||
const config = require('../config')
|
||||
const ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
|
||||
const packageConfig = require('../package.json')
|
||||
|
||||
exports.assetsPath = function (_path) {
|
||||
const assetsSubDirectory = process.env.NODE_ENV === 'production'
|
||||
? config.build.assetsSubDirectory
|
||||
: config.dev.assetsSubDirectory
|
||||
exports.assetsPath = function(_path) {
|
||||
const assetsSubDirectory =
|
||||
process.env.NODE_ENV === 'production'
|
||||
? config.build.assetsSubDirectory
|
||||
: config.dev.assetsSubDirectory
|
||||
|
||||
return path.posix.join(assetsSubDirectory, _path)
|
||||
}
|
||||
|
||||
exports.cssLoaders = function (options) {
|
||||
exports.cssLoaders = function(options) {
|
||||
options = options || {}
|
||||
|
||||
const cssLoader = {
|
||||
@@ -30,8 +31,22 @@ exports.cssLoaders = function (options) {
|
||||
}
|
||||
|
||||
// generate loader string to be used with extract text plugin
|
||||
function generateLoaders (loader, loaderOptions) {
|
||||
const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
|
||||
function generateLoaders(loader, loaderOptions) {
|
||||
const loaders = []
|
||||
|
||||
// Extract CSS when that option is specified
|
||||
// (which is the case during production build)
|
||||
if (options.extract) {
|
||||
loaders.push(MiniCssExtractPlugin.loader)
|
||||
} else {
|
||||
loaders.push('vue-style-loader')
|
||||
}
|
||||
|
||||
loaders.push(cssLoader)
|
||||
|
||||
if (options.usePostCSS) {
|
||||
loaders.push(postcssLoader)
|
||||
}
|
||||
|
||||
if (loader) {
|
||||
loaders.push({
|
||||
@@ -42,24 +57,16 @@ exports.cssLoaders = function (options) {
|
||||
})
|
||||
}
|
||||
|
||||
// Extract CSS when that option is specified
|
||||
// (which is the case during production build)
|
||||
if (options.extract) {
|
||||
return ExtractTextPlugin.extract({
|
||||
use: loaders,
|
||||
fallback: 'vue-style-loader'
|
||||
})
|
||||
} else {
|
||||
return ['vue-style-loader'].concat(loaders)
|
||||
}
|
||||
return loaders
|
||||
}
|
||||
|
||||
// https://vue-loader.vuejs.org/en/configurations/extract-css.html
|
||||
return {
|
||||
css: generateLoaders(),
|
||||
postcss: generateLoaders(),
|
||||
less: generateLoaders('less'),
|
||||
sass: generateLoaders('sass', { indentedSyntax: true }),
|
||||
sass: generateLoaders('sass', {
|
||||
indentedSyntax: true
|
||||
}),
|
||||
scss: generateLoaders('sass'),
|
||||
stylus: generateLoaders('stylus'),
|
||||
styl: generateLoaders('stylus')
|
||||
@@ -67,7 +74,7 @@ exports.cssLoaders = function (options) {
|
||||
}
|
||||
|
||||
// Generate loaders for standalone style files (outside of .vue)
|
||||
exports.styleLoaders = function (options) {
|
||||
exports.styleLoaders = function(options) {
|
||||
const output = []
|
||||
const loaders = exports.cssLoaders(options)
|
||||
|
||||
|
@@ -1,22 +1,5 @@
|
||||
'use strict'
|
||||
const utils = require('./utils')
|
||||
const config = require('../config')
|
||||
const isProduction = process.env.NODE_ENV === 'production'
|
||||
const sourceMapEnabled = isProduction
|
||||
? config.build.productionSourceMap
|
||||
: config.dev.cssSourceMap
|
||||
|
||||
module.exports = {
|
||||
loaders: utils.cssLoaders({
|
||||
sourceMap: sourceMapEnabled,
|
||||
extract: isProduction
|
||||
}),
|
||||
cssSourceMap: sourceMapEnabled,
|
||||
cacheBusting: config.dev.cacheBusting,
|
||||
transformToRequire: {
|
||||
video: ['src', 'poster'],
|
||||
source: 'src',
|
||||
img: 'src',
|
||||
image: 'xlink:href'
|
||||
}
|
||||
//You can set the vue-loader configuration by yourself.
|
||||
}
|
||||
|
@@ -2,9 +2,10 @@
|
||||
const path = require('path')
|
||||
const utils = require('./utils')
|
||||
const config = require('../config')
|
||||
const { VueLoaderPlugin } = require('vue-loader')
|
||||
const vueLoaderConfig = require('./vue-loader.conf')
|
||||
|
||||
function resolve (dir) {
|
||||
function resolve(dir) {
|
||||
return path.join(__dirname, '..', dir)
|
||||
}
|
||||
|
||||
@@ -27,15 +28,15 @@ module.exports = {
|
||||
output: {
|
||||
path: config.build.assetsRoot,
|
||||
filename: '[name].js',
|
||||
publicPath: process.env.NODE_ENV === 'production'
|
||||
? config.build.assetsPublicPath
|
||||
: config.dev.assetsPublicPath
|
||||
publicPath:
|
||||
process.env.NODE_ENV === 'production'
|
||||
? config.build.assetsPublicPath
|
||||
: config.dev.assetsPublicPath
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.js', '.vue', '.json'],
|
||||
alias: {
|
||||
'vue$': 'vue/dist/vue.esm.js',
|
||||
'@': resolve('src'),
|
||||
'@': resolve('src')
|
||||
}
|
||||
},
|
||||
module: {
|
||||
@@ -49,7 +50,11 @@ module.exports = {
|
||||
{
|
||||
test: /\.js$/,
|
||||
loader: 'babel-loader?cacheDirectory',
|
||||
include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
|
||||
include: [
|
||||
resolve('src'),
|
||||
resolve('test'),
|
||||
resolve('node_modules/webpack-dev-server/client')
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.svg$/,
|
||||
@@ -86,6 +91,7 @@ module.exports = {
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [new VueLoaderPlugin()],
|
||||
node: {
|
||||
// prevent webpack from injecting useless setImmediate polyfill because Vue
|
||||
// source contains it (although only uses it if it's native).
|
||||
|
@@ -9,7 +9,7 @@ const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
|
||||
const portfinder = require('portfinder')
|
||||
|
||||
function resolve (dir) {
|
||||
function resolve(dir) {
|
||||
return path.join(__dirname, '..', dir)
|
||||
}
|
||||
|
||||
@@ -17,8 +17,12 @@ const HOST = process.env.HOST
|
||||
const PORT = process.env.PORT && Number(process.env.PORT)
|
||||
|
||||
const devWebpackConfig = merge(baseWebpackConfig, {
|
||||
mode: 'development',
|
||||
module: {
|
||||
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
|
||||
rules: utils.styleLoaders({
|
||||
sourceMap: config.dev.cssSourceMap,
|
||||
usePostCSS: true
|
||||
})
|
||||
},
|
||||
// cheap-module-eval-source-map is faster for development
|
||||
devtool: config.dev.devtool,
|
||||
@@ -39,7 +43,7 @@ const devWebpackConfig = merge(baseWebpackConfig, {
|
||||
proxy: config.dev.proxyTable,
|
||||
quiet: true, // necessary for FriendlyErrorsPlugin
|
||||
watchOptions: {
|
||||
poll: config.dev.poll,
|
||||
poll: config.dev.poll
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
@@ -47,8 +51,6 @@ const devWebpackConfig = merge(baseWebpackConfig, {
|
||||
'process.env': require('../config/dev.env')
|
||||
}),
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
|
||||
new webpack.NoEmitOnErrorsPlugin(),
|
||||
// https://github.com/ampedandwired/html-webpack-plugin
|
||||
new HtmlWebpackPlugin({
|
||||
filename: 'index.html',
|
||||
@@ -57,7 +59,7 @@ const devWebpackConfig = merge(baseWebpackConfig, {
|
||||
favicon: resolve('favicon.ico'),
|
||||
title: 'vue-element-admin',
|
||||
path: config.dev.assetsPublicPath + config.dev.assetsSubDirectory
|
||||
}),
|
||||
})
|
||||
]
|
||||
})
|
||||
|
||||
@@ -73,14 +75,20 @@ module.exports = new Promise((resolve, reject) => {
|
||||
devWebpackConfig.devServer.port = port
|
||||
|
||||
// Add FriendlyErrorsPlugin
|
||||
devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
|
||||
compilationSuccessInfo: {
|
||||
messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
|
||||
},
|
||||
onErrors: config.dev.notifyOnErrors
|
||||
? utils.createNotifierCallback()
|
||||
: undefined
|
||||
}))
|
||||
devWebpackConfig.plugins.push(
|
||||
new FriendlyErrorsPlugin({
|
||||
compilationSuccessInfo: {
|
||||
messages: [
|
||||
`Your application is running here: http://${
|
||||
devWebpackConfig.devServer.host
|
||||
}:${port}`
|
||||
]
|
||||
},
|
||||
onErrors: config.dev.notifyOnErrors
|
||||
? utils.createNotifierCallback()
|
||||
: undefined
|
||||
})
|
||||
)
|
||||
|
||||
resolve(devWebpackConfig)
|
||||
}
|
||||
|
@@ -7,17 +7,23 @@ const merge = require('webpack-merge')
|
||||
const baseWebpackConfig = require('./webpack.base.conf')
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin')
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
const ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
|
||||
const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin')
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
|
||||
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
|
||||
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
|
||||
|
||||
function resolve (dir) {
|
||||
function resolve(dir) {
|
||||
return path.join(__dirname, '..', dir)
|
||||
}
|
||||
|
||||
const env = require('../config/'+process.env.env_config+'.env')
|
||||
const env = require('../config/' + process.env.env_config + '.env')
|
||||
|
||||
// For NamedChunksPlugin
|
||||
const seen = new Set()
|
||||
const nameLength = 4
|
||||
|
||||
const webpackConfig = merge(baseWebpackConfig, {
|
||||
mode: 'production',
|
||||
module: {
|
||||
rules: utils.styleLoaders({
|
||||
sourceMap: config.build.productionSourceMap,
|
||||
@@ -28,37 +34,18 @@ const webpackConfig = merge(baseWebpackConfig, {
|
||||
devtool: config.build.productionSourceMap ? config.build.devtool : false,
|
||||
output: {
|
||||
path: config.build.assetsRoot,
|
||||
filename: utils.assetsPath('js/[name].[chunkhash].js'),
|
||||
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
|
||||
filename: utils.assetsPath('js/[name].[chunkhash:8].js'),
|
||||
chunkFilename: utils.assetsPath('js/[name].[chunkhash:8].js')
|
||||
},
|
||||
plugins: [
|
||||
// http://vuejs.github.io/vue-loader/en/workflow/production.html
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': env
|
||||
}),
|
||||
new UglifyJsPlugin({
|
||||
uglifyOptions: {
|
||||
compress: {
|
||||
warnings: false
|
||||
}
|
||||
},
|
||||
sourceMap: config.build.productionSourceMap,
|
||||
parallel: true
|
||||
}),
|
||||
// extract css into its own file
|
||||
new ExtractTextPlugin({
|
||||
filename: utils.assetsPath('css/[name].[contenthash].css'),
|
||||
// Setting the following option to `false` will not extract CSS from codesplit chunks.
|
||||
// Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
|
||||
// increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
|
||||
allChunks: false,
|
||||
}),
|
||||
// Compress extracted CSS. We are using this plugin so that possible
|
||||
// duplicated CSS from different components can be deduped.
|
||||
new OptimizeCSSPlugin({
|
||||
cssProcessorOptions: config.build.productionSourceMap
|
||||
? { safe: true, map: { inline: false } }
|
||||
: { safe: true }
|
||||
new MiniCssExtractPlugin({
|
||||
filename: utils.assetsPath('css/[name].[contenthash:8].css'),
|
||||
chunkFilename: utils.assetsPath('css/[name].[contenthash:8].css')
|
||||
}),
|
||||
// generate dist index.html with correct asset hash for caching.
|
||||
// you can customize output by editing /index.html
|
||||
@@ -76,68 +63,34 @@ const webpackConfig = merge(baseWebpackConfig, {
|
||||
removeAttributeQuotes: true
|
||||
// more options:
|
||||
// https://github.com/kangax/html-minifier#options-quick-reference
|
||||
},
|
||||
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
|
||||
chunksSortMode: 'dependency'
|
||||
}
|
||||
// default sort mode uses toposort which cannot handle cyclic deps
|
||||
// in certain cases, and in webpack 4, chunk order in HTML doesn't
|
||||
// matter anyway
|
||||
}),
|
||||
new ScriptExtHtmlWebpackPlugin({
|
||||
//`runtime` must same as runtimeChunk name. default is `runtime`
|
||||
inline: /runtime\..*\.js$/
|
||||
}),
|
||||
// keep chunk.id stable when chunk has no name
|
||||
new webpack.NamedChunksPlugin(chunk => {
|
||||
if (chunk.name) {
|
||||
return chunk.name
|
||||
}
|
||||
const modules = Array.from(chunk.modulesIterable)
|
||||
if (modules.length > 1) {
|
||||
const hash = require('hash-sum')
|
||||
const joinedHash = hash(modules.map(m => m.id).join('_'))
|
||||
let len = nameLength
|
||||
while (seen.has(joinedHash.substr(0, len))) len++
|
||||
seen.add(joinedHash.substr(0, len))
|
||||
return `chunk-${joinedHash.substr(0, len)}`
|
||||
} else {
|
||||
return modules[0].id
|
||||
}
|
||||
}),
|
||||
// keep module.id stable when vender modules does not change
|
||||
new webpack.HashedModuleIdsPlugin(),
|
||||
// enable scope hoisting
|
||||
new webpack.optimize.ModuleConcatenationPlugin(),
|
||||
// split vendor js into its own file
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
name: 'vendor',
|
||||
minChunks (module) {
|
||||
// any required modules inside node_modules are extracted to vendor
|
||||
return (
|
||||
module.resource &&
|
||||
/\.js$/.test(module.resource) &&
|
||||
module.resource.indexOf(
|
||||
path.join(__dirname, '../node_modules')
|
||||
) === 0
|
||||
)
|
||||
}
|
||||
}),
|
||||
// extract webpack runtime and module manifest to its own file in order to
|
||||
// prevent vendor hash from being updated whenever app bundle is updated
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
name: 'manifest',
|
||||
minChunks: Infinity
|
||||
}),
|
||||
// This instance extracts shared chunks from code splitted chunks and bundles them
|
||||
// in a separate chunk, similar to the vendor chunk
|
||||
// see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
name: 'app',
|
||||
async: 'vendor-async',
|
||||
children: true,
|
||||
minChunks: 3
|
||||
}),
|
||||
// split echarts into its own file
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
async: 'echarts',
|
||||
minChunks(module) {
|
||||
var context = module.context;
|
||||
return context && (context.indexOf('echarts') >= 0 || context.indexOf('zrender') >= 0);
|
||||
}
|
||||
}),
|
||||
// split xlsx into its own file
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
async: 'xlsx',
|
||||
minChunks(module) {
|
||||
var context = module.context;
|
||||
return context && (context.indexOf('xlsx') >= 0);
|
||||
}
|
||||
}),
|
||||
// split codemirror into its own file
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
async: 'codemirror',
|
||||
minChunks(module) {
|
||||
var context = module.context;
|
||||
return context && (context.indexOf('codemirror') >= 0);
|
||||
}
|
||||
}),
|
||||
|
||||
// copy custom static assets
|
||||
new CopyWebpackPlugin([
|
||||
{
|
||||
@@ -146,7 +99,48 @@ const webpackConfig = merge(baseWebpackConfig, {
|
||||
ignore: ['.*']
|
||||
}
|
||||
])
|
||||
]
|
||||
],
|
||||
optimization: {
|
||||
splitChunks: {
|
||||
chunks: 'all',
|
||||
cacheGroups: {
|
||||
libs: {
|
||||
name: 'chunk-libs',
|
||||
test: /[\\/]node_modules[\\/]/,
|
||||
priority: 10,
|
||||
chunks: 'initial' // 只打包初始时依赖的第三方
|
||||
},
|
||||
elementUI: {
|
||||
name: 'chunk-elementUI', // 单独将 elementUI 拆包
|
||||
priority: 20, // 权重要大于 libs 和 app 不然会被打包进 libs 或者 app
|
||||
test: /[\\/]node_modules[\\/]element-ui[\\/]/
|
||||
},
|
||||
commons: {
|
||||
name: 'chunk-comomns',
|
||||
test: resolve('src/components'), // 可自定义拓展你的规则
|
||||
minChunks: 3, // 最小公用次数
|
||||
priority: 5,
|
||||
reuseExistingChunk: true
|
||||
}
|
||||
}
|
||||
},
|
||||
runtimeChunk: 'single',
|
||||
minimizer: [
|
||||
new UglifyJsPlugin({
|
||||
uglifyOptions: {
|
||||
mangle: {
|
||||
safari10: true
|
||||
}
|
||||
},
|
||||
sourceMap: config.build.productionSourceMap,
|
||||
cache: true,
|
||||
parallel: true
|
||||
}),
|
||||
// Compress extracted CSS. We are using this plugin so that possible
|
||||
// duplicated CSS from different components can be deduped.
|
||||
new OptimizeCSSAssetsPlugin()
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
if (config.build.productionGzip) {
|
||||
@@ -157,9 +151,7 @@ if (config.build.productionGzip) {
|
||||
asset: '[path].gz[query]',
|
||||
algorithm: 'gzip',
|
||||
test: new RegExp(
|
||||
'\\.(' +
|
||||
config.build.productionGzipExtensions.join('|') +
|
||||
')$'
|
||||
'\\.(' + config.build.productionGzipExtensions.join('|') + ')$'
|
||||
),
|
||||
threshold: 10240,
|
||||
minRatio: 0.8
|
||||
@@ -167,9 +159,28 @@ if (config.build.productionGzip) {
|
||||
)
|
||||
}
|
||||
|
||||
if (config.build.bundleAnalyzerReport) {
|
||||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
|
||||
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
|
||||
if (config.build.generateAnalyzerReport || config.build.bundleAnalyzerReport) {
|
||||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
|
||||
.BundleAnalyzerPlugin
|
||||
|
||||
if (config.build.bundleAnalyzerReport) {
|
||||
webpackConfig.plugins.push(
|
||||
new BundleAnalyzerPlugin({
|
||||
analyzerPort: 8080,
|
||||
generateStatsFile: false
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
if (config.build.generateAnalyzerReport) {
|
||||
webpackConfig.plugins.push(
|
||||
new BundleAnalyzerPlugin({
|
||||
analyzerMode: 'static',
|
||||
reportFilename: 'bundle-report.html',
|
||||
openAnalyzer: false
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = webpackConfig
|
||||
|
@@ -13,7 +13,10 @@ module.exports = {
|
||||
proxyTable: {},
|
||||
|
||||
// Various Dev Server settings
|
||||
host: 'localhost', // can be overwritten by process.env.HOST
|
||||
|
||||
// can be overwritten by process.env.HOST
|
||||
// if you want dev by ip, please set host: '0.0.0.0'
|
||||
host: 'localhost',
|
||||
port: 9527, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
|
||||
autoOpenBrowser: true,
|
||||
errorOverlay: true,
|
||||
@@ -33,12 +36,7 @@ module.exports = {
|
||||
*/
|
||||
|
||||
// https://webpack.js.org/configuration/devtool/#development
|
||||
devtool: '#cheap-source-map',
|
||||
|
||||
// If you have problems debugging vue-files in devtools,
|
||||
// set this to false - it *may* help
|
||||
// https://vue-loader.vuejs.org/en/options.html#cachebusting
|
||||
cacheBusting: true,
|
||||
devtool: 'cheap-source-map',
|
||||
|
||||
// CSS Sourcemaps off by default because relative paths are "buggy"
|
||||
// with this option, according to the CSS-Loader README
|
||||
@@ -56,16 +54,21 @@ 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',
|
||||
devtool: 'source-map',
|
||||
|
||||
// Gzip off by default as many popular static hosts such as
|
||||
// Surge or Netlify already gzip all static assets for you.
|
||||
@@ -76,8 +79,11 @@ module.exports = {
|
||||
|
||||
// Run the build command with an extra argument to
|
||||
// View the bundle analyzer report after build finishes:
|
||||
// `npm run build --report`
|
||||
// `npm run build:prod --report`
|
||||
// Set to `true` or `false` to always turn it on or off
|
||||
bundleAnalyzerReport: process.env.npm_config_report
|
||||
bundleAnalyzerReport: process.env.npm_config_report || false,
|
||||
|
||||
// `npm run build:prod --generate_report`
|
||||
generateAnalyzerReport: process.env.npm_config_generate_report || false
|
||||
}
|
||||
}
|
||||
|
113
package.json
113
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vue-element-admin",
|
||||
"version": "3.7.0",
|
||||
"version": "3.7.3",
|
||||
"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",
|
||||
@@ -9,7 +9,14 @@
|
||||
"build:prod": "cross-env NODE_ENV=production env_config=prod node build/build.js",
|
||||
"build:sit": "cross-env NODE_ENV=production env_config=sit node build/build.js",
|
||||
"lint": "eslint --ext .js,.vue src",
|
||||
"test": "npm run lint"
|
||||
"test": "npm run lint",
|
||||
"precommit": "lint-staged"
|
||||
},
|
||||
"lint-staged": {
|
||||
"src/**/*.{js,vue}": [
|
||||
"eslint --fix",
|
||||
"git add"
|
||||
]
|
||||
},
|
||||
"keywords": [
|
||||
"vue",
|
||||
@@ -26,14 +33,15 @@
|
||||
"url": "https://github.com/PanJiaChen/vue-element-admin/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "0.17.1",
|
||||
"axios": "0.18.0",
|
||||
"clipboard": "1.7.1",
|
||||
"codemirror": "5.32.0",
|
||||
"codemirror": "5.39.2",
|
||||
"connect": "3.6.6",
|
||||
"driver.js": "0.5.2",
|
||||
"dropzone": "5.2.0",
|
||||
"echarts": "3.8.5",
|
||||
"element-ui": "2.3.2",
|
||||
"file-saver": "1.3.3",
|
||||
"echarts": "4.1.0",
|
||||
"element-ui": "2.4.6",
|
||||
"file-saver": "1.3.8",
|
||||
"font-awesome": "4.7.0",
|
||||
"js-cookie": "2.2.0",
|
||||
"jsonlint": "1.6.3",
|
||||
@@ -42,13 +50,13 @@
|
||||
"normalize.css": "7.0.0",
|
||||
"nprogress": "0.2.0",
|
||||
"screenfull": "3.3.2",
|
||||
"showdown": "1.8.5",
|
||||
"showdown": "1.8.6",
|
||||
"simplemde": "1.11.2",
|
||||
"sortablejs": "1.7.0",
|
||||
"vue": "2.5.10",
|
||||
"vue": "2.5.17",
|
||||
"vue-count-to": "1.0.13",
|
||||
"vue-i18n": "7.3.2",
|
||||
"vue-multiselect": "2.0.8",
|
||||
"vue-multiselect": "2.1.0",
|
||||
"vue-router": "3.0.1",
|
||||
"vue-splitpane": "1.0.2",
|
||||
"vuedraggable": "^2.16.0",
|
||||
@@ -56,56 +64,61 @@
|
||||
"xlsx": "^0.11.16"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "7.2.3",
|
||||
"babel-core": "6.26.0",
|
||||
"babel-eslint": "8.0.3",
|
||||
"autoprefixer": "8.5.0",
|
||||
"babel-core": "6.26.3",
|
||||
"babel-eslint": "8.2.6",
|
||||
"babel-helper-vue-jsx-merge-props": "2.0.3",
|
||||
"babel-loader": "7.1.2",
|
||||
"babel-plugin-dynamic-import-node": "^1.2.0",
|
||||
"babel-loader": "7.1.5",
|
||||
"babel-plugin-dynamic-import-node": "2.0.0",
|
||||
"babel-plugin-syntax-jsx": "6.18.0",
|
||||
"babel-plugin-transform-runtime": "6.23.0",
|
||||
"babel-plugin-transform-vue-jsx": "3.5.0",
|
||||
"babel-preset-env": "1.6.1",
|
||||
"babel-plugin-transform-vue-jsx": "3.7.0",
|
||||
"babel-preset-env": "1.7.0",
|
||||
"babel-preset-stage-2": "6.24.1",
|
||||
"chalk": "2.3.0",
|
||||
"copy-webpack-plugin": "4.3.0",
|
||||
"cross-env": "5.1.1",
|
||||
"css-loader": "0.28.7",
|
||||
"eslint": "4.13.1",
|
||||
"eslint-friendly-formatter": "3.0.0",
|
||||
"eslint-loader": "1.9.0",
|
||||
"eslint-plugin-html": "4.0.1",
|
||||
"extract-text-webpack-plugin": "3.0.2",
|
||||
"file-loader": "1.1.5",
|
||||
"friendly-errors-webpack-plugin": "1.6.1",
|
||||
"html-webpack-plugin": "2.30.1",
|
||||
"node-notifier": "5.1.2",
|
||||
"chalk": "2.4.1",
|
||||
"copy-webpack-plugin": "4.5.2",
|
||||
"cross-env": "5.2.0",
|
||||
"css-loader": "1.0.0",
|
||||
"eslint": "4.19.1",
|
||||
"eslint-friendly-formatter": "4.0.1",
|
||||
"eslint-loader": "2.0.0",
|
||||
"eslint-plugin-html": "4.0.5",
|
||||
"file-loader": "1.1.11",
|
||||
"friendly-errors-webpack-plugin": "1.7.0",
|
||||
"hash-sum": "^1.0.2",
|
||||
"html-webpack-plugin": "^4.0.0-alpha",
|
||||
"husky": "0.14.3",
|
||||
"lint-staged": "7.2.2",
|
||||
"mini-css-extract-plugin": "0.4.1",
|
||||
"node-notifier": "5.2.1",
|
||||
"node-sass": "^4.7.2",
|
||||
"optimize-css-assets-webpack-plugin": "3.2.0",
|
||||
"ora": "1.3.0",
|
||||
"optimize-css-assets-webpack-plugin": "5.0.0",
|
||||
"ora": "3.0.0",
|
||||
"portfinder": "1.0.13",
|
||||
"postcss-import": "11.0.0",
|
||||
"postcss-loader": "2.0.9",
|
||||
"postcss-url": "7.3.0",
|
||||
"pushstate-server": "3.0.1",
|
||||
"postcss-import": "11.1.0",
|
||||
"postcss-loader": "2.1.6",
|
||||
"postcss-url": "7.3.2",
|
||||
"rimraf": "2.6.2",
|
||||
"sass-loader": "6.0.6",
|
||||
"sass-loader": "7.0.3",
|
||||
"script-ext-html-webpack-plugin": "2.0.1",
|
||||
"script-loader": "0.7.2",
|
||||
"semver": "5.4.1",
|
||||
"shelljs": "0.7.8",
|
||||
"svg-sprite-loader": "3.5.2",
|
||||
"uglifyjs-webpack-plugin": "1.1.3",
|
||||
"url-loader": "0.6.2",
|
||||
"vue-loader": "13.7.2",
|
||||
"vue-style-loader": "3.0.3",
|
||||
"vue-template-compiler": "2.5.10",
|
||||
"webpack": "3.10.0",
|
||||
"webpack-bundle-analyzer": "2.9.1",
|
||||
"webpack-dev-server": "2.9.7",
|
||||
"webpack-merge": "4.1.1"
|
||||
"semver": "5.5.0",
|
||||
"serve-static": "1.13.2",
|
||||
"shelljs": "0.8.2",
|
||||
"svg-sprite-loader": "3.8.0",
|
||||
"uglifyjs-webpack-plugin": "1.2.7",
|
||||
"url-loader": "1.0.1",
|
||||
"vue-loader": "15.3.0",
|
||||
"vue-style-loader": "4.1.2",
|
||||
"vue-template-compiler": "2.5.17",
|
||||
"webpack": "4.16.5",
|
||||
"webpack-bundle-analyzer": "2.13.1",
|
||||
"webpack-cli": "3.1.0",
|
||||
"webpack-dev-server": "3.1.5",
|
||||
"webpack-merge": "4.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4.0.0",
|
||||
"node": ">= 6.0.0",
|
||||
"npm": ">= 3.0.0"
|
||||
},
|
||||
"browserslist": [
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<a href="https://github.com/PanJiaChen/vue-element-admin" target="_blank" class="github-corner" aria-label="View source on Github">
|
||||
<svg width="80" height="80" viewBox="0 0 250 250" style="fill:#40c9c6; color:#fff; position: absolute; top: 84px; border: 0; right: 0;"
|
||||
<svg width="80" height="80" viewBox="0 0 250 250" style="fill:#40c9c6; color:#fff;"
|
||||
aria-hidden="true">
|
||||
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
|
||||
<path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
|
||||
|
@@ -35,7 +35,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" >
|
||||
$n: 6; //和items.length 相同
|
||||
$n: 8; //和items.length 相同
|
||||
$t: .1s;
|
||||
.share-dropdown-menu {
|
||||
width: 250px;
|
||||
|
@@ -23,6 +23,7 @@ export default {
|
||||
|
||||
<style>
|
||||
/* Mallki */
|
||||
|
||||
.link--mallki {
|
||||
font-weight: 800;
|
||||
color: #4dd9d5;
|
||||
@@ -31,10 +32,10 @@ export default {
|
||||
transition: color 0.5s 0.25s;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.link--mallki:hover {
|
||||
@@ -109,5 +110,4 @@ display: inline-block;
|
||||
-webkit-transition-timing-function: cubic-bezier(0.2, 1, 0.3, 1);
|
||||
transition-timing-function: cubic-bezier(0.2, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<div class="upload-container">
|
||||
<el-button icon='el-icon-upload' size="mini" :style="{background:color,borderColor:color}" @click=" dialogVisible=true" type="primary">上传图片
|
||||
</el-button>
|
||||
<el-dialog append-to-body :visible.sync="dialogVisible">
|
||||
<el-dialog :visible.sync="dialogVisible">
|
||||
<el-upload class="editor-slide-upload" action="https://httpbin.org/post" :multiple="true" :file-list="fileList" :show-file-list="true"
|
||||
list-type="picture-card" :on-remove="handleRemove" :on-success="handleSuccess" :before-upload="beforeUpload">
|
||||
<el-button size="small" type="primary">点击上传</el-button>
|
||||
|
@@ -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 || ''))
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -83,6 +84,7 @@ export default {
|
||||
imagetools_cors_hosts: ['www.tinymce.com', 'codepen.io'],
|
||||
default_link_target: '_blank',
|
||||
link_title: false,
|
||||
nonbreaking_force_tab: true, // inserting nonbreaking space need Nonbreaking Space Plugin
|
||||
init_instance_callback: editor => {
|
||||
if (_this.value) {
|
||||
editor.setContent(_this.value)
|
||||
|
@@ -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
|
||||
|
@@ -1,6 +1,6 @@
|
||||
// Here is a list of the toolbar
|
||||
// Detail list see https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols
|
||||
|
||||
const toolbar = ['bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen']
|
||||
const toolbar = ['bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen']
|
||||
|
||||
export default toolbar
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="upload-container">
|
||||
<el-upload class="image-uploader" :data="dataObj" drag :multiple="false" :show-file-list="false" action="https://httpbin.org/post"
|
||||
:on-success="handleImageScucess">
|
||||
:on-success="handleImageSuccess">
|
||||
<i class="el-icon-upload"></i>
|
||||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||
</el-upload>
|
||||
@@ -43,7 +43,7 @@ export default {
|
||||
emitInput(val) {
|
||||
this.$emit('input', val)
|
||||
},
|
||||
handleImageScucess() {
|
||||
handleImageSuccess() {
|
||||
this.emitInput(this.tempUrl)
|
||||
},
|
||||
beforeUpload() {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="singleImageUpload2 upload-container">
|
||||
<el-upload class="image-uploader" :data="dataObj" drag :multiple="false" :show-file-list="false" action="https://httpbin.org/post"
|
||||
:on-success="handleImageScucess">
|
||||
:on-success="handleImageSuccess">
|
||||
<i class="el-icon-upload"></i>
|
||||
<div class="el-upload__text">Drag或<em>点击上传</em></div>
|
||||
</el-upload>
|
||||
@@ -42,7 +42,7 @@ export default {
|
||||
emitInput(val) {
|
||||
this.$emit('input', val)
|
||||
},
|
||||
handleImageScucess() {
|
||||
handleImageSuccess() {
|
||||
this.emitInput(this.tempUrl)
|
||||
},
|
||||
beforeUpload() {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="upload-container">
|
||||
<el-upload class="image-uploader" :data="dataObj" drag :multiple="false" :show-file-list="false" action="https://httpbin.org/post"
|
||||
:on-success="handleImageScucess">
|
||||
:on-success="handleImageSuccess">
|
||||
<i class="el-icon-upload"></i>
|
||||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||
</el-upload>
|
||||
@@ -50,7 +50,7 @@ export default {
|
||||
emitInput(val) {
|
||||
this.$emit('input', val)
|
||||
},
|
||||
handleImageScucess(file) {
|
||||
handleImageSuccess(file) {
|
||||
this.emitInput(file.files.file)
|
||||
},
|
||||
beforeUpload() {
|
||||
|
@@ -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,24 @@ 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]
|
||||
|
||||
if (!this.isExcel(rawFile)) {
|
||||
this.$message.error('Only supports upload .xlsx, .xls, .csv suffix files')
|
||||
return false
|
||||
}
|
||||
this.upload(rawFile)
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
},
|
||||
@@ -48,26 +58,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 = ''
|
||||
@@ -89,6 +115,9 @@ export default {
|
||||
headers.push(hdr)
|
||||
}
|
||||
return headers
|
||||
},
|
||||
isExcel(file) {
|
||||
return /\.(xlsx|xls|csv)$/.test(file.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -29,8 +29,8 @@ export default{
|
||||
ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px'
|
||||
break
|
||||
default:
|
||||
ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.body.scrollTop) + 'px'
|
||||
ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.body.scrollLeft) + 'px'
|
||||
ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.documentElement.scrollTop || document.body.scrollTop) + 'px'
|
||||
ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.documentElement.scrollLeft || document.body.scrollLeft) + 'px'
|
||||
}
|
||||
ripple.style.backgroundColor = opts.color
|
||||
ripple.className = 'waves-ripple z-active'
|
||||
|
1
src/icons/svg/nested.svg
Normal file
1
src/icons/svg/nested.svg
Normal 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 |
@@ -28,6 +28,14 @@ export default {
|
||||
lineChart: 'Line Chart',
|
||||
mixChart: 'Mix Chart',
|
||||
example: 'Example',
|
||||
nested: 'Nested Routes',
|
||||
menu1: 'Menu 1',
|
||||
'menu1-1': 'Menu 1-1',
|
||||
'menu1-2': 'Menu 1-2',
|
||||
'menu1-2-1': 'Menu 1-2-1',
|
||||
'menu1-2-2': 'Menu 1-2-2',
|
||||
'menu1-3': 'Menu 1-3',
|
||||
menu2: 'Menu 2',
|
||||
Table: 'Table',
|
||||
dynamicTable: 'Dynamic Table',
|
||||
dragTable: 'Drag Table',
|
||||
|
@@ -20,8 +20,11 @@ const messages = {
|
||||
}
|
||||
|
||||
const i18n = new VueI18n({
|
||||
locale: Cookies.get('language') || 'en', // set locale
|
||||
messages // set locale messages
|
||||
// set locale
|
||||
// options: en or zh
|
||||
locale: Cookies.get('language') || 'en',
|
||||
// set locale messages
|
||||
messages
|
||||
})
|
||||
|
||||
export default i18n
|
||||
|
@@ -28,6 +28,14 @@ export default {
|
||||
lineChart: '折线图',
|
||||
mixChart: '混合图表',
|
||||
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',
|
||||
Table: 'Table',
|
||||
dynamicTable: '动态Table',
|
||||
dragTable: '拖拽Table',
|
||||
|
@@ -36,6 +36,5 @@ new Vue({
|
||||
router,
|
||||
store,
|
||||
i18n,
|
||||
template: '<App/>',
|
||||
components: { App }
|
||||
render: h => h(App)
|
||||
})
|
||||
|
@@ -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
|
||||
|
@@ -1 +0,0 @@
|
||||
module.exports = file => require('@/views/' + file + '.vue').default // vue-loader at least v13.0.0+
|
@@ -1 +0,0 @@
|
||||
module.exports = file => () => import('@/views/' + file + '.vue')
|
@@ -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,67 @@ export const asyncRouterMap = [
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
path: '/nested',
|
||||
component: Layout,
|
||||
redirect: '/nested/menu1/menu1-1',
|
||||
name: 'nested',
|
||||
meta: {
|
||||
title: 'nested',
|
||||
icon: 'nested'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'menu1',
|
||||
component: () => import('@/views/nested/menu1/index'), // Parent router-view
|
||||
name: 'menu1',
|
||||
meta: { title: 'menu1' },
|
||||
redirect: '/nested/menu1/menu1-1',
|
||||
children: [
|
||||
{
|
||||
path: 'menu1-1',
|
||||
component: () => import('@/views/nested/menu1/menu1-1'),
|
||||
name: 'menu1-1',
|
||||
meta: { title: 'menu1-1' }
|
||||
},
|
||||
{
|
||||
path: 'menu1-2',
|
||||
component: () => import('@/views/nested/menu1/menu1-2'),
|
||||
name: 'menu1-2',
|
||||
redirect: '/nested/menu1/menu1-2/menu1-2-1',
|
||||
meta: { title: 'menu1-2' },
|
||||
children: [
|
||||
{
|
||||
path: 'menu1-2-1',
|
||||
component: () => import('@/views/nested/menu1/menu1-2/menu1-2-1'),
|
||||
name: 'menu1-2-1',
|
||||
meta: { title: 'menu1-2-1' }
|
||||
},
|
||||
{
|
||||
path: 'menu1-2-2',
|
||||
component: () => import('@/views/nested/menu1/menu1-2/menu1-2-2'),
|
||||
name: 'menu1-2-2',
|
||||
meta: { title: 'menu1-2-2' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'menu1-3',
|
||||
component: () => import('@/views/nested/menu1/menu1-3'),
|
||||
name: 'menu1-3',
|
||||
meta: { title: 'menu1-3' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'menu2',
|
||||
name: 'menu2',
|
||||
component: () => import('@/views/nested/menu2/index'),
|
||||
meta: { title: 'menu2' }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
path: '/error',
|
||||
component: Layout,
|
||||
|
@@ -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)
|
||||
}
|
||||
@@ -40,7 +38,7 @@ const tagsView = {
|
||||
for (const i of state.cachedViews) {
|
||||
if (i === view.name) {
|
||||
const index = state.cachedViews.indexOf(i)
|
||||
state.cachedViews = state.cachedViews.slice(index, i + 1)
|
||||
state.cachedViews = state.cachedViews.slice(index, index + 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@
|
||||
min-height: 100%;
|
||||
transition: margin-left .28s;
|
||||
margin-left: 180px;
|
||||
position: relative;
|
||||
}
|
||||
// 侧边栏
|
||||
.sidebar-container {
|
||||
|
@@ -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
26
src/utils/permission.js
Normal 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
|
||||
}
|
||||
}
|
||||
|
@@ -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',
|
||||
|
307
src/vendor/Blob.js
vendored
307
src/vendor/Blob.js
vendored
@@ -16,164 +16,161 @@
|
||||
/*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */
|
||||
|
||||
(function (view) {
|
||||
"use strict";
|
||||
"use strict";
|
||||
|
||||
view.URL = view.URL || view.webkitURL;
|
||||
view.URL = view.URL || view.webkitURL;
|
||||
|
||||
if (view.Blob && view.URL) {
|
||||
try {
|
||||
new Blob;
|
||||
return;
|
||||
} catch (e) {}
|
||||
if (view.Blob && view.URL) {
|
||||
try {
|
||||
new Blob;
|
||||
return;
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// Internally we use a BlobBuilder implementation to base Blob off of
|
||||
// in order to support older browsers that only have BlobBuilder
|
||||
var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || (function (view) {
|
||||
var
|
||||
get_class = function (object) {
|
||||
return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1];
|
||||
},
|
||||
FakeBlobBuilder = function BlobBuilder() {
|
||||
this.data = [];
|
||||
},
|
||||
FakeBlob = function Blob(data, type, encoding) {
|
||||
this.data = data;
|
||||
this.size = data.length;
|
||||
this.type = type;
|
||||
this.encoding = encoding;
|
||||
},
|
||||
FBB_proto = FakeBlobBuilder.prototype,
|
||||
FB_proto = FakeBlob.prototype,
|
||||
FileReaderSync = view.FileReaderSync,
|
||||
FileException = function (type) {
|
||||
this.code = this[this.name = type];
|
||||
},
|
||||
file_ex_codes = (
|
||||
"NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR " +
|
||||
"NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR"
|
||||
).split(" "),
|
||||
file_ex_code = file_ex_codes.length,
|
||||
real_URL = view.URL || view.webkitURL || view,
|
||||
real_create_object_URL = real_URL.createObjectURL,
|
||||
real_revoke_object_URL = real_URL.revokeObjectURL,
|
||||
URL = real_URL,
|
||||
btoa = view.btoa,
|
||||
atob = view.atob
|
||||
|
||||
,
|
||||
ArrayBuffer = view.ArrayBuffer,
|
||||
Uint8Array = view.Uint8Array;
|
||||
FakeBlob.fake = FB_proto.fake = true;
|
||||
while (file_ex_code--) {
|
||||
FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1;
|
||||
}
|
||||
|
||||
// Internally we use a BlobBuilder implementation to base Blob off of
|
||||
// in order to support older browsers that only have BlobBuilder
|
||||
var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || (function(view) {
|
||||
var
|
||||
get_class = function(object) {
|
||||
return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1];
|
||||
}
|
||||
, FakeBlobBuilder = function BlobBuilder() {
|
||||
this.data = [];
|
||||
}
|
||||
, FakeBlob = function Blob(data, type, encoding) {
|
||||
this.data = data;
|
||||
this.size = data.length;
|
||||
this.type = type;
|
||||
this.encoding = encoding;
|
||||
}
|
||||
, FBB_proto = FakeBlobBuilder.prototype
|
||||
, FB_proto = FakeBlob.prototype
|
||||
, FileReaderSync = view.FileReaderSync
|
||||
, FileException = function(type) {
|
||||
this.code = this[this.name = type];
|
||||
}
|
||||
, file_ex_codes = (
|
||||
"NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR "
|
||||
+ "NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR"
|
||||
).split(" ")
|
||||
, file_ex_code = file_ex_codes.length
|
||||
, real_URL = view.URL || view.webkitURL || view
|
||||
, real_create_object_URL = real_URL.createObjectURL
|
||||
, real_revoke_object_URL = real_URL.revokeObjectURL
|
||||
, URL = real_URL
|
||||
, btoa = view.btoa
|
||||
, atob = view.atob
|
||||
|
||||
, ArrayBuffer = view.ArrayBuffer
|
||||
, Uint8Array = view.Uint8Array
|
||||
;
|
||||
FakeBlob.fake = FB_proto.fake = true;
|
||||
while (file_ex_code--) {
|
||||
FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1;
|
||||
}
|
||||
if (!real_URL.createObjectURL) {
|
||||
URL = view.URL = {};
|
||||
}
|
||||
URL.createObjectURL = function(blob) {
|
||||
var
|
||||
type = blob.type
|
||||
, data_URI_header
|
||||
;
|
||||
if (type === null) {
|
||||
type = "application/octet-stream";
|
||||
}
|
||||
if (blob instanceof FakeBlob) {
|
||||
data_URI_header = "data:" + type;
|
||||
if (blob.encoding === "base64") {
|
||||
return data_URI_header + ";base64," + blob.data;
|
||||
} else if (blob.encoding === "URI") {
|
||||
return data_URI_header + "," + decodeURIComponent(blob.data);
|
||||
} if (btoa) {
|
||||
return data_URI_header + ";base64," + btoa(blob.data);
|
||||
} else {
|
||||
return data_URI_header + "," + encodeURIComponent(blob.data);
|
||||
}
|
||||
} else if (real_create_object_URL) {
|
||||
return real_create_object_URL.call(real_URL, blob);
|
||||
}
|
||||
};
|
||||
URL.revokeObjectURL = function(object_URL) {
|
||||
if (object_URL.substring(0, 5) !== "data:" && real_revoke_object_URL) {
|
||||
real_revoke_object_URL.call(real_URL, object_URL);
|
||||
}
|
||||
};
|
||||
FBB_proto.append = function(data/*, endings*/) {
|
||||
var bb = this.data;
|
||||
// decode data to a binary string
|
||||
if (Uint8Array && (data instanceof ArrayBuffer || data instanceof Uint8Array)) {
|
||||
var
|
||||
str = ""
|
||||
, buf = new Uint8Array(data)
|
||||
, i = 0
|
||||
, buf_len = buf.length
|
||||
;
|
||||
for (; i < buf_len; i++) {
|
||||
str += String.fromCharCode(buf[i]);
|
||||
}
|
||||
bb.push(str);
|
||||
} else if (get_class(data) === "Blob" || get_class(data) === "File") {
|
||||
if (FileReaderSync) {
|
||||
var fr = new FileReaderSync;
|
||||
bb.push(fr.readAsBinaryString(data));
|
||||
} else {
|
||||
// async FileReader won't work as BlobBuilder is sync
|
||||
throw new FileException("NOT_READABLE_ERR");
|
||||
}
|
||||
} else if (data instanceof FakeBlob) {
|
||||
if (data.encoding === "base64" && atob) {
|
||||
bb.push(atob(data.data));
|
||||
} else if (data.encoding === "URI") {
|
||||
bb.push(decodeURIComponent(data.data));
|
||||
} else if (data.encoding === "raw") {
|
||||
bb.push(data.data);
|
||||
}
|
||||
} else {
|
||||
if (typeof data !== "string") {
|
||||
data += ""; // convert unsupported types to strings
|
||||
}
|
||||
// decode UTF-16 to binary string
|
||||
bb.push(unescape(encodeURIComponent(data)));
|
||||
}
|
||||
};
|
||||
FBB_proto.getBlob = function(type) {
|
||||
if (!arguments.length) {
|
||||
type = null;
|
||||
}
|
||||
return new FakeBlob(this.data.join(""), type, "raw");
|
||||
};
|
||||
FBB_proto.toString = function() {
|
||||
return "[object BlobBuilder]";
|
||||
};
|
||||
FB_proto.slice = function(start, end, type) {
|
||||
var args = arguments.length;
|
||||
if (args < 3) {
|
||||
type = null;
|
||||
}
|
||||
return new FakeBlob(
|
||||
this.data.slice(start, args > 1 ? end : this.data.length)
|
||||
, type
|
||||
, this.encoding
|
||||
);
|
||||
};
|
||||
FB_proto.toString = function() {
|
||||
return "[object Blob]";
|
||||
};
|
||||
FB_proto.close = function() {
|
||||
this.size = this.data.length = 0;
|
||||
};
|
||||
return FakeBlobBuilder;
|
||||
}(view));
|
||||
|
||||
view.Blob = function Blob(blobParts, options) {
|
||||
var type = options ? (options.type || "") : "";
|
||||
var builder = new BlobBuilder();
|
||||
if (blobParts) {
|
||||
for (var i = 0, len = blobParts.length; i < len; i++) {
|
||||
builder.append(blobParts[i]);
|
||||
}
|
||||
if (!real_URL.createObjectURL) {
|
||||
URL = view.URL = {};
|
||||
}
|
||||
URL.createObjectURL = function (blob) {
|
||||
var
|
||||
type = blob.type,
|
||||
data_URI_header;
|
||||
if (type === null) {
|
||||
type = "application/octet-stream";
|
||||
}
|
||||
if (blob instanceof FakeBlob) {
|
||||
data_URI_header = "data:" + type;
|
||||
if (blob.encoding === "base64") {
|
||||
return data_URI_header + ";base64," + blob.data;
|
||||
} else if (blob.encoding === "URI") {
|
||||
return data_URI_header + "," + decodeURIComponent(blob.data);
|
||||
}
|
||||
return builder.getBlob(type);
|
||||
if (btoa) {
|
||||
return data_URI_header + ";base64," + btoa(blob.data);
|
||||
} else {
|
||||
return data_URI_header + "," + encodeURIComponent(blob.data);
|
||||
}
|
||||
} else if (real_create_object_URL) {
|
||||
return real_create_object_URL.call(real_URL, blob);
|
||||
}
|
||||
};
|
||||
URL.revokeObjectURL = function (object_URL) {
|
||||
if (object_URL.substring(0, 5) !== "data:" && real_revoke_object_URL) {
|
||||
real_revoke_object_URL.call(real_URL, object_URL);
|
||||
}
|
||||
};
|
||||
FBB_proto.append = function (data /*, endings*/ ) {
|
||||
var bb = this.data;
|
||||
// decode data to a binary string
|
||||
if (Uint8Array && (data instanceof ArrayBuffer || data instanceof Uint8Array)) {
|
||||
var
|
||||
str = "",
|
||||
buf = new Uint8Array(data),
|
||||
i = 0,
|
||||
buf_len = buf.length;
|
||||
for (; i < buf_len; i++) {
|
||||
str += String.fromCharCode(buf[i]);
|
||||
}
|
||||
bb.push(str);
|
||||
} else if (get_class(data) === "Blob" || get_class(data) === "File") {
|
||||
if (FileReaderSync) {
|
||||
var fr = new FileReaderSync;
|
||||
bb.push(fr.readAsBinaryString(data));
|
||||
} else {
|
||||
// async FileReader won't work as BlobBuilder is sync
|
||||
throw new FileException("NOT_READABLE_ERR");
|
||||
}
|
||||
} else if (data instanceof FakeBlob) {
|
||||
if (data.encoding === "base64" && atob) {
|
||||
bb.push(atob(data.data));
|
||||
} else if (data.encoding === "URI") {
|
||||
bb.push(decodeURIComponent(data.data));
|
||||
} else if (data.encoding === "raw") {
|
||||
bb.push(data.data);
|
||||
}
|
||||
} else {
|
||||
if (typeof data !== "string") {
|
||||
data += ""; // convert unsupported types to strings
|
||||
}
|
||||
// decode UTF-16 to binary string
|
||||
bb.push(unescape(encodeURIComponent(data)));
|
||||
}
|
||||
};
|
||||
FBB_proto.getBlob = function (type) {
|
||||
if (!arguments.length) {
|
||||
type = null;
|
||||
}
|
||||
return new FakeBlob(this.data.join(""), type, "raw");
|
||||
};
|
||||
FBB_proto.toString = function () {
|
||||
return "[object BlobBuilder]";
|
||||
};
|
||||
FB_proto.slice = function (start, end, type) {
|
||||
var args = arguments.length;
|
||||
if (args < 3) {
|
||||
type = null;
|
||||
}
|
||||
return new FakeBlob(
|
||||
this.data.slice(start, args > 1 ? end : this.data.length), type, this.encoding
|
||||
);
|
||||
};
|
||||
FB_proto.toString = function () {
|
||||
return "[object Blob]";
|
||||
};
|
||||
FB_proto.close = function () {
|
||||
this.size = this.data.length = 0;
|
||||
};
|
||||
return FakeBlobBuilder;
|
||||
}(view));
|
||||
|
||||
view.Blob = function Blob(blobParts, options) {
|
||||
var type = options ? (options.type || "") : "";
|
||||
var builder = new BlobBuilder();
|
||||
if (blobParts) {
|
||||
for (var i = 0, len = blobParts.length; i < len; i++) {
|
||||
builder.append(blobParts[i]);
|
||||
}
|
||||
}
|
||||
return builder.getBlob(type);
|
||||
};
|
||||
}(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this));
|
||||
|
284
src/vendor/Export2Excel.js
vendored
284
src/vendor/Export2Excel.js
vendored
@@ -4,155 +4,203 @@ require('script-loader!@/vendor/Blob');
|
||||
import XLSX from 'xlsx'
|
||||
|
||||
function generateArray(table) {
|
||||
var out = [];
|
||||
var rows = table.querySelectorAll('tr');
|
||||
var ranges = [];
|
||||
for (var R = 0; R < rows.length; ++R) {
|
||||
var outRow = [];
|
||||
var row = rows[R];
|
||||
var columns = row.querySelectorAll('td');
|
||||
for (var C = 0; C < columns.length; ++C) {
|
||||
var cell = columns[C];
|
||||
var colspan = cell.getAttribute('colspan');
|
||||
var rowspan = cell.getAttribute('rowspan');
|
||||
var cellValue = cell.innerText;
|
||||
if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue;
|
||||
var out = [];
|
||||
var rows = table.querySelectorAll('tr');
|
||||
var ranges = [];
|
||||
for (var R = 0; R < rows.length; ++R) {
|
||||
var outRow = [];
|
||||
var row = rows[R];
|
||||
var columns = row.querySelectorAll('td');
|
||||
for (var C = 0; C < columns.length; ++C) {
|
||||
var cell = columns[C];
|
||||
var colspan = cell.getAttribute('colspan');
|
||||
var rowspan = cell.getAttribute('rowspan');
|
||||
var cellValue = cell.innerText;
|
||||
if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue;
|
||||
|
||||
//Skip ranges
|
||||
ranges.forEach(function (range) {
|
||||
if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) {
|
||||
for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null);
|
||||
}
|
||||
});
|
||||
|
||||
//Handle Row Span
|
||||
if (rowspan || colspan) {
|
||||
rowspan = rowspan || 1;
|
||||
colspan = colspan || 1;
|
||||
ranges.push({s: {r: R, c: outRow.length}, e: {r: R + rowspan - 1, c: outRow.length + colspan - 1}});
|
||||
}
|
||||
;
|
||||
|
||||
//Handle Value
|
||||
outRow.push(cellValue !== "" ? cellValue : null);
|
||||
|
||||
//Handle Colspan
|
||||
if (colspan) for (var k = 0; k < colspan - 1; ++k) outRow.push(null);
|
||||
//Skip ranges
|
||||
ranges.forEach(function (range) {
|
||||
if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) {
|
||||
for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null);
|
||||
}
|
||||
out.push(outRow);
|
||||
});
|
||||
|
||||
//Handle Row Span
|
||||
if (rowspan || colspan) {
|
||||
rowspan = rowspan || 1;
|
||||
colspan = colspan || 1;
|
||||
ranges.push({
|
||||
s: {
|
||||
r: R,
|
||||
c: outRow.length
|
||||
},
|
||||
e: {
|
||||
r: R + rowspan - 1,
|
||||
c: outRow.length + colspan - 1
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
//Handle Value
|
||||
outRow.push(cellValue !== "" ? cellValue : null);
|
||||
|
||||
//Handle Colspan
|
||||
if (colspan)
|
||||
for (var k = 0; k < colspan - 1; ++k) outRow.push(null);
|
||||
}
|
||||
return [out, ranges];
|
||||
out.push(outRow);
|
||||
}
|
||||
return [out, ranges];
|
||||
};
|
||||
|
||||
function datenum(v, date1904) {
|
||||
if (date1904) v += 1462;
|
||||
var epoch = Date.parse(v);
|
||||
return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
|
||||
if (date1904) v += 1462;
|
||||
var epoch = Date.parse(v);
|
||||
return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
|
||||
}
|
||||
|
||||
function sheet_from_array_of_arrays(data, opts) {
|
||||
var ws = {};
|
||||
var range = {s: {c: 10000000, r: 10000000}, e: {c: 0, r: 0}};
|
||||
for (var R = 0; R != data.length; ++R) {
|
||||
for (var C = 0; C != data[R].length; ++C) {
|
||||
if (range.s.r > R) range.s.r = R;
|
||||
if (range.s.c > C) range.s.c = C;
|
||||
if (range.e.r < R) range.e.r = R;
|
||||
if (range.e.c < C) range.e.c = C;
|
||||
var cell = {v: data[R][C]};
|
||||
if (cell.v == null) continue;
|
||||
var cell_ref = XLSX.utils.encode_cell({c: C, r: R});
|
||||
|
||||
if (typeof cell.v === 'number') cell.t = 'n';
|
||||
else if (typeof cell.v === 'boolean') cell.t = 'b';
|
||||
else if (cell.v instanceof Date) {
|
||||
cell.t = 'n';
|
||||
cell.z = XLSX.SSF._table[14];
|
||||
cell.v = datenum(cell.v);
|
||||
}
|
||||
else cell.t = 's';
|
||||
|
||||
ws[cell_ref] = cell;
|
||||
}
|
||||
var ws = {};
|
||||
var range = {
|
||||
s: {
|
||||
c: 10000000,
|
||||
r: 10000000
|
||||
},
|
||||
e: {
|
||||
c: 0,
|
||||
r: 0
|
||||
}
|
||||
if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);
|
||||
return ws;
|
||||
};
|
||||
for (var R = 0; R != data.length; ++R) {
|
||||
for (var C = 0; C != data[R].length; ++C) {
|
||||
if (range.s.r > R) range.s.r = R;
|
||||
if (range.s.c > C) range.s.c = C;
|
||||
if (range.e.r < R) range.e.r = R;
|
||||
if (range.e.c < C) range.e.c = C;
|
||||
var cell = {
|
||||
v: data[R][C]
|
||||
};
|
||||
if (cell.v == null) continue;
|
||||
var cell_ref = XLSX.utils.encode_cell({
|
||||
c: C,
|
||||
r: R
|
||||
});
|
||||
|
||||
if (typeof cell.v === 'number') cell.t = 'n';
|
||||
else if (typeof cell.v === 'boolean') cell.t = 'b';
|
||||
else if (cell.v instanceof Date) {
|
||||
cell.t = 'n';
|
||||
cell.z = XLSX.SSF._table[14];
|
||||
cell.v = datenum(cell.v);
|
||||
} else cell.t = 's';
|
||||
|
||||
ws[cell_ref] = cell;
|
||||
}
|
||||
}
|
||||
if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);
|
||||
return ws;
|
||||
}
|
||||
|
||||
function Workbook() {
|
||||
if (!(this instanceof Workbook)) return new Workbook();
|
||||
this.SheetNames = [];
|
||||
this.Sheets = {};
|
||||
if (!(this instanceof Workbook)) return new Workbook();
|
||||
this.SheetNames = [];
|
||||
this.Sheets = {};
|
||||
}
|
||||
|
||||
function s2ab(s) {
|
||||
var buf = new ArrayBuffer(s.length);
|
||||
var view = new Uint8Array(buf);
|
||||
for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
|
||||
return buf;
|
||||
var buf = new ArrayBuffer(s.length);
|
||||
var view = new Uint8Array(buf);
|
||||
for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
|
||||
return buf;
|
||||
}
|
||||
|
||||
export function export_table_to_excel(id) {
|
||||
var theTable = document.getElementById(id);
|
||||
var oo = generateArray(theTable);
|
||||
var ranges = oo[1];
|
||||
var theTable = document.getElementById(id);
|
||||
var oo = generateArray(theTable);
|
||||
var ranges = oo[1];
|
||||
|
||||
/* original data */
|
||||
var data = oo[0];
|
||||
var ws_name = "SheetJS";
|
||||
/* original data */
|
||||
var data = oo[0];
|
||||
var ws_name = "SheetJS";
|
||||
|
||||
var wb = new Workbook(), ws = sheet_from_array_of_arrays(data);
|
||||
var wb = new Workbook(),
|
||||
ws = sheet_from_array_of_arrays(data);
|
||||
|
||||
/* add ranges to worksheet */
|
||||
// ws['!cols'] = ['apple', 'banan'];
|
||||
ws['!merges'] = ranges;
|
||||
/* add ranges to worksheet */
|
||||
// ws['!cols'] = ['apple', 'banan'];
|
||||
ws['!merges'] = ranges;
|
||||
|
||||
/* add worksheet to workbook */
|
||||
wb.SheetNames.push(ws_name);
|
||||
wb.Sheets[ws_name] = ws;
|
||||
/* add worksheet to workbook */
|
||||
wb.SheetNames.push(ws_name);
|
||||
wb.Sheets[ws_name] = ws;
|
||||
|
||||
var wbout = XLSX.write(wb, {bookType: 'xlsx', bookSST: false, type: 'binary'});
|
||||
var wbout = XLSX.write(wb, {
|
||||
bookType: 'xlsx',
|
||||
bookSST: false,
|
||||
type: 'binary'
|
||||
});
|
||||
|
||||
saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), "test.xlsx")
|
||||
saveAs(new Blob([s2ab(wbout)], {
|
||||
type: "application/octet-stream"
|
||||
}), "test.xlsx")
|
||||
}
|
||||
|
||||
export function export_json_to_excel({header, data, filename='excel-list', autoWidth=true}={}) {
|
||||
/* original data */
|
||||
data=[...data]
|
||||
data.unshift(header);
|
||||
var ws_name = "SheetJS";
|
||||
var wb = new Workbook(), ws = sheet_from_array_of_arrays(data);
|
||||
export function export_json_to_excel({
|
||||
header,
|
||||
data,
|
||||
filename,
|
||||
autoWidth = true
|
||||
} = {}) {
|
||||
/* original data */
|
||||
filename = filename || 'excel-list'
|
||||
data = [...data]
|
||||
data.unshift(header);
|
||||
var ws_name = "SheetJS";
|
||||
var wb = new Workbook(),
|
||||
ws = sheet_from_array_of_arrays(data);
|
||||
|
||||
if(autoWidth){
|
||||
/*设置worksheet每列的最大宽度*/
|
||||
const colWidth = data.map(row => row.map(val => {
|
||||
/*先判断是否为null/undefined*/
|
||||
if (val == null) {
|
||||
return {'wch': 10};
|
||||
}
|
||||
/*再判断是否为中文*/
|
||||
else if (val.toString().charCodeAt(0) > 255) {
|
||||
return {'wch': val.toString().length * 2};
|
||||
} else {
|
||||
return {'wch': val.toString().length};
|
||||
}
|
||||
}))
|
||||
/*以第一行为初始值*/
|
||||
let result = colWidth[0];
|
||||
for (let i = 1; i < colWidth.length; i++) {
|
||||
for (let j = 0; j < colWidth[i].length; j++) {
|
||||
if (result[j]['wch'] < colWidth[i][j]['wch']) {
|
||||
result[j]['wch'] = colWidth[i][j]['wch'];
|
||||
}
|
||||
if (autoWidth) {
|
||||
/*设置worksheet每列的最大宽度*/
|
||||
const colWidth = data.map(row => row.map(val => {
|
||||
/*先判断是否为null/undefined*/
|
||||
if (val == null) {
|
||||
return {
|
||||
'wch': 10
|
||||
};
|
||||
}
|
||||
/*再判断是否为中文*/
|
||||
else if (val.toString().charCodeAt(0) > 255) {
|
||||
return {
|
||||
'wch': val.toString().length * 2
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
'wch': val.toString().length
|
||||
};
|
||||
}
|
||||
}))
|
||||
/*以第一行为初始值*/
|
||||
let result = colWidth[0];
|
||||
for (let i = 1; i < colWidth.length; i++) {
|
||||
for (let j = 0; j < colWidth[i].length; j++) {
|
||||
if (result[j]['wch'] < colWidth[i][j]['wch']) {
|
||||
result[j]['wch'] = colWidth[i][j]['wch'];
|
||||
}
|
||||
}
|
||||
ws['!cols'] = result;
|
||||
}
|
||||
ws['!cols'] = result;
|
||||
}
|
||||
|
||||
/* add worksheet to workbook */
|
||||
wb.SheetNames.push(ws_name);
|
||||
wb.Sheets[ws_name] = ws;
|
||||
/* add worksheet to workbook */
|
||||
wb.SheetNames.push(ws_name);
|
||||
wb.Sheets[ws_name] = ws;
|
||||
|
||||
var wbout = XLSX.write(wb, {bookType: 'xlsx', bookSST: false, type: 'binary'});
|
||||
saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), filename + ".xlsx");
|
||||
var wbout = XLSX.write(wb, {
|
||||
bookType: 'xlsx',
|
||||
bookSST: false,
|
||||
type: 'binary'
|
||||
});
|
||||
saveAs(new Blob([s2ab(wbout)], {
|
||||
type: "application/octet-stream"
|
||||
}), filename + ".xlsx");
|
||||
}
|
||||
|
4
src/vendor/Export2Zip.js
vendored
4
src/vendor/Export2Zip.js
vendored
@@ -14,7 +14,9 @@ export function export_txt_to_zip(th, jsonData, txtName, zipName) {
|
||||
txtData += `${tempStr}\r\n`
|
||||
})
|
||||
zip.file(`${txt_name}.txt`, txtData)
|
||||
zip.generateAsync({type:"blob"}).then((blob) => {
|
||||
zip.generateAsync({
|
||||
type: "blob"
|
||||
}).then((blob) => {
|
||||
saveAs(blob, `${zip_name}.zip`)
|
||||
}, (err) => {
|
||||
alert('导出失败')
|
||||
|
@@ -34,7 +34,7 @@ export default {
|
||||
},
|
||||
clipboardSuccess() {
|
||||
this.$message({
|
||||
message: '复制成功',
|
||||
message: 'Copy successfully',
|
||||
type: 'success',
|
||||
duration: 1500
|
||||
})
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<div class="components-container">
|
||||
<code>Markdown is based on
|
||||
<a href="https://github.com/sparksuite/simplemde-markdown-editor" target="_blank">simplemde-markdown-editor</a> ,Simply encapsulated in Vue.
|
||||
<a target="_blank" href="https://segmentfault.com/a/1190000009762198#articleHeader14">
|
||||
<a target="_blank" href="https://juejin.im/post/593121aa0ce4630057f70d35#heading-15">
|
||||
相关文章 </a>
|
||||
</code>
|
||||
<div class="editor-container">
|
||||
|
@@ -7,19 +7,19 @@
|
||||
</div>
|
||||
<div style="margin-bottom:50px;">
|
||||
<el-col :span="4" class="text-center">
|
||||
<router-link class="pan-btn blue-btn" to="/components/index">Components</router-link>
|
||||
<router-link class="pan-btn blue-btn" to="/documentation/index">Documentation</router-link>
|
||||
</el-col>
|
||||
<el-col :span="4" class="text-center">
|
||||
<router-link class="pan-btn light-blue-btn" to="/charts/index">Charts</router-link>
|
||||
<router-link class="pan-btn light-blue-btn" to="/icon/index">Icons</router-link>
|
||||
</el-col>
|
||||
<el-col :span="4" class="text-center">
|
||||
<router-link class="pan-btn pink-btn" to="/excel/download">Excel</router-link>
|
||||
<router-link class="pan-btn pink-btn" to="/excel/export-excel">Excel</router-link>
|
||||
</el-col>
|
||||
<el-col :span="4" class="text-center">
|
||||
<router-link class="pan-btn green-btn" to="/example/table/complex-table">Table</router-link>
|
||||
<router-link class="pan-btn green-btn" to="/table/complex-table">Table</router-link>
|
||||
</el-col>
|
||||
<el-col :span="4" class="text-center">
|
||||
<router-link class="pan-btn tiffany-btn" to="/form/edit-form">Form</router-link>
|
||||
<router-link class="pan-btn tiffany-btn" to="/example/create">Form</router-link>
|
||||
</el-col>
|
||||
<el-col :span="4" class="text-center">
|
||||
<router-link class="pan-btn yellow-btn" to="/theme/index">Theme</router-link>
|
||||
@@ -130,12 +130,14 @@ export default {
|
||||
title: [{ required: true, trigger: 'change', validator: validate }]
|
||||
},
|
||||
articleList: [
|
||||
{ title: '基础篇', href: 'https://segmentfault.com/a/1190000009275424' },
|
||||
{ title: '登录权限篇', href: 'https://segmentfault.com/a/1190000009506097' },
|
||||
{ title: '实战篇', href: 'https://segmentfault.com/a/1190000009762198' },
|
||||
{ title: 'vueAdmin-template 篇', href: 'https://segmentfault.com/a/1190000010043013' },
|
||||
{ title: '基础篇', href: 'https://juejin.im/post/59097cd7a22b9d0065fb61d2' },
|
||||
{ title: '登录权限篇', href: 'https://juejin.im/post/591aa14f570c35006961acac' },
|
||||
{ title: '实战篇', href: 'https://juejin.im/post/593121aa0ce4630057f70d35' },
|
||||
{ title: 'vueAdmin-template 篇', href: 'https://juejin.im/post/595b4d776fb9a06bbe7dba56' },
|
||||
{ title: '自行封装 component', href: 'https://segmentfault.com/a/1190000009090836' },
|
||||
{ title: '优雅的使用 icon', href: 'https://segmentfault.com/a/https://segmentfault.com/a/1190000012213278' }
|
||||
{ title: '优雅的使用 icon', href: 'https://juejin.im/post/59bb864b5188257e7a427c09' },
|
||||
{ title: 'webpack4(上)', href: 'https://juejin.im/post/59bb864b5188257e7a427c09' },
|
||||
{ title: 'webpack4(下)', href: 'https://juejin.im/post/5b5d6d6f6fb9a04fea58aabc' }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@@ -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>
|
||||
|
@@ -72,7 +72,7 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setLocalStorgae() {
|
||||
setLocalStorage() {
|
||||
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(this.todos))
|
||||
},
|
||||
addTodo(e) {
|
||||
@@ -82,30 +82,30 @@ export default {
|
||||
text,
|
||||
done: false
|
||||
})
|
||||
this.setLocalStorgae()
|
||||
this.setLocalStorage()
|
||||
}
|
||||
e.target.value = ''
|
||||
},
|
||||
toggleTodo(val) {
|
||||
val.done = !val.done
|
||||
this.setLocalStorgae()
|
||||
this.setLocalStorage()
|
||||
},
|
||||
deleteTodo(todo) {
|
||||
this.todos.splice(this.todos.indexOf(todo), 1)
|
||||
this.setLocalStorgae()
|
||||
this.setLocalStorage()
|
||||
},
|
||||
editTodo({ todo, value }) {
|
||||
todo.text = value
|
||||
this.setLocalStorgae()
|
||||
this.setLocalStorage()
|
||||
},
|
||||
clearCompleted() {
|
||||
this.todos = this.todos.filter(todo => !todo.done)
|
||||
this.setLocalStorgae()
|
||||
this.setLocalStorage()
|
||||
},
|
||||
toggleAll({ done }) {
|
||||
this.todos.forEach(todo => {
|
||||
todo.done = done
|
||||
this.setLocalStorgae()
|
||||
this.setLocalStorage()
|
||||
})
|
||||
}
|
||||
},
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<el-table :data="list" style="width: 100%;padding-top: 15px;">
|
||||
<el-table-column label="Order_No" min-width="200">
|
||||
<template slot-scope="scope">
|
||||
{{scope.row.order_no}}
|
||||
{{scope.row.order_no | orderNoFilter}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="Price" width="195" align="center">
|
||||
@@ -34,6 +34,9 @@ export default {
|
||||
pending: 'danger'
|
||||
}
|
||||
return statusMap[status]
|
||||
},
|
||||
orderNoFilter(str) {
|
||||
return str.substring(0, 30)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<div class="dashboard-editor-container">
|
||||
<github-corner></github-corner>
|
||||
|
||||
<github-corner style="position: absolute; top: 0px; border: 0; right: 0;"></github-corner>
|
||||
|
||||
<panel-group @handleSetLineChartData="handleSetLineChartData"></panel-group>
|
||||
|
||||
@@ -30,10 +31,10 @@
|
||||
<el-col :xs="{span: 24}" :sm="{span: 24}" :md="{span: 24}" :lg="{span: 12}" :xl="{span: 12}" style="padding-right:8px;margin-bottom:30px;">
|
||||
<transaction-table></transaction-table>
|
||||
</el-col>
|
||||
<el-col :xs="{span: 24}" :sm="{span: 12}" :md="{span: 12}" :lg="{span: 6}" :xl="{span: 5}" style="margin-bottom:30px;">
|
||||
<el-col :xs="{span: 24}" :sm="{span: 12}" :md="{span: 12}" :lg="{span: 6}" :xl="{span: 6}" style="margin-bottom:30px;">
|
||||
<todo-list></todo-list>
|
||||
</el-col>
|
||||
<el-col :xs="{span: 24}" :sm="{span: 12}" :md="{span: 12}" :lg="{span: 6}" :xl="{span: 5}" style="margin-bottom:30px;" >
|
||||
<el-col :xs="{span: 24}" :sm="{span: 12}" :md="{span: 12}" :lg="{span: 6}" :xl="{span: 6}" style="margin-bottom:30px;">
|
||||
<box-card></box-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -41,6 +42,7 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import GithubCorner from '@/components/GithubCorner'
|
||||
import PanelGroup from './components/PanelGroup'
|
||||
|
@@ -4,10 +4,10 @@
|
||||
<pan-thumb style="float: left" :image="avatar"> Your roles:
|
||||
<span class="pan-info-roles" :key='item' v-for="item in roles">{{item}}</span>
|
||||
</pan-thumb>
|
||||
<github-corner></github-corner>
|
||||
<github-corner style="position: absolute; top: 0px; border: 0; right: 0;"></github-corner>
|
||||
<div class="info-container">
|
||||
<span class="display_name">{{name}}</span>
|
||||
<span style="font-size:20px;padding-top:20px;display:inline-block;">editor : dashboard</span>
|
||||
<span style="font-size:20px;padding-top:20px;display:inline-block;">Editor's Dashboard</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
|
@@ -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>
|
||||
@@ -14,12 +14,14 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
articleList: [
|
||||
{ title: '基础篇', href: 'https://segmentfault.com/a/1190000009275424' },
|
||||
{ title: '登录权限篇', href: 'https://segmentfault.com/a/1190000009506097' },
|
||||
{ title: '实战篇', href: 'https://segmentfault.com/a/1190000009762198' },
|
||||
{ title: 'vueAdmin-template 篇', href: 'https://segmentfault.com/a/1190000010043013' },
|
||||
{ title: '基础篇', href: 'https://juejin.im/post/59097cd7a22b9d0065fb61d2' },
|
||||
{ title: '登录权限篇', href: 'https://juejin.im/post/591aa14f570c35006961acac' },
|
||||
{ title: '实战篇', href: 'https://juejin.im/post/593121aa0ce4630057f70d35' },
|
||||
{ title: 'vueAdmin-template 篇', href: 'https://juejin.im/post/595b4d776fb9a06bbe7dba56' },
|
||||
{ title: '自行封装 component', href: 'https://segmentfault.com/a/1190000009090836' },
|
||||
{ title: '优雅的使用 icon', href: 'https://segmentfault.com/a/1190000012213278' }
|
||||
{ title: '优雅的使用 icon', href: 'https://juejin.im/post/59bb864b5188257e7a427c09' },
|
||||
{ title: 'webpack4(上)', href: 'https://juejin.im/post/59bb864b5188257e7a427c09' },
|
||||
{ title: 'webpack4(下)', href: 'https://juejin.im/post/5b5d6d6f6fb9a04fea58aabc' }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@@ -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>
|
||||
|
@@ -57,6 +57,7 @@ export default {
|
||||
.pan-back-btn {
|
||||
background: #008489;
|
||||
color: #fff;
|
||||
border: none!important;
|
||||
}
|
||||
.pan-gif {
|
||||
margin: 0 auto;
|
||||
|
@@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<div style="background:#f0f2f5;margin-top: -20px;height:100%;">
|
||||
<div class="wscn-http404-container">
|
||||
<div class="wscn-http404">
|
||||
<div class="pic-404">
|
||||
<img class="pic-404__parent" :src="img_404" alt="404">
|
||||
<img class="pic-404__child left" :src="img_404_cloud" alt="404">
|
||||
<img class="pic-404__child mid" :src="img_404_cloud" alt="404">
|
||||
<img class="pic-404__child right" :src="img_404_cloud" alt="404">
|
||||
<img class="pic-404__parent" src="@/assets/404_images/404.png" alt="404">
|
||||
<img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404">
|
||||
<img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404">
|
||||
<img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404">
|
||||
</div>
|
||||
<div class="bullshit">
|
||||
<div class="bullshit__oops">OOPS!</div>
|
||||
@@ -21,37 +21,33 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import img_404 from '@/assets/404_images/404.png'
|
||||
import img_404_cloud from '@/assets/404_images/404_cloud.png'
|
||||
|
||||
export default {
|
||||
name: 'page404',
|
||||
data() {
|
||||
return {
|
||||
img_404,
|
||||
img_404_cloud
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
message() {
|
||||
return '特朗普说这个页面你不能进......'
|
||||
return '网管说这个页面你不能进......'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.wscn-http404-container{
|
||||
transform: translate(-50%,-50%);
|
||||
position: absolute;
|
||||
top: 40%;
|
||||
left: 50%;
|
||||
}
|
||||
.wscn-http404 {
|
||||
position: relative;
|
||||
width: 1200px;
|
||||
margin: 20px auto 60px;
|
||||
padding: 0 100px;
|
||||
padding: 0 50px;
|
||||
overflow: hidden;
|
||||
.pic-404 {
|
||||
position: relative;
|
||||
float: left;
|
||||
width: 600px;
|
||||
padding: 150px 0;
|
||||
overflow: hidden;
|
||||
&__parent {
|
||||
width: 100%;
|
||||
@@ -163,7 +159,7 @@ export default {
|
||||
position: relative;
|
||||
float: left;
|
||||
width: 300px;
|
||||
padding: 150px 0;
|
||||
padding: 30px 0;
|
||||
overflow: hidden;
|
||||
&__oops {
|
||||
font-size: 32px;
|
||||
@@ -179,7 +175,8 @@ export default {
|
||||
&__headline {
|
||||
font-size: 20px;
|
||||
line-height: 24px;
|
||||
color: #1482f0;
|
||||
color: #222;
|
||||
font-weight: bold;
|
||||
opacity: 0;
|
||||
margin-bottom: 10px;
|
||||
animation-name: slideUp;
|
||||
|
@@ -16,7 +16,7 @@
|
||||
|
||||
<Warning />
|
||||
|
||||
<el-col :span="21">
|
||||
<el-col :span="24">
|
||||
<el-form-item style="margin-bottom: 40px;" prop="title">
|
||||
<MDinput name="name" v-model="postForm.title" required :maxlength="100">
|
||||
标题
|
||||
@@ -34,14 +34,14 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="8">
|
||||
<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-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="8">
|
||||
<el-col :span="6">
|
||||
<el-form-item label-width="60px" label="重要性:" class="postInfo-container-item">
|
||||
<el-rate style="margin-top:8px;" v-model="postForm.importance" :max='3' :colors="['#99A9BF', '#F7BA2A', '#FF9900']" :low-threshold="1"
|
||||
:high-threshold="3">
|
||||
|
@@ -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>
|
||||
|
@@ -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}}
|
||||
|
@@ -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">
|
||||
|
@@ -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,22 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selected(data) {
|
||||
this.tableData = data.results
|
||||
this.tableHeader = data.header
|
||||
beforeUpload(file) {
|
||||
const isLt1M = file.size / 1024 / 1024 < 1
|
||||
|
||||
if (isLt1M) {
|
||||
return true
|
||||
}
|
||||
|
||||
this.$message({
|
||||
message: 'Please do not upload files larger than 1m in size.',
|
||||
type: 'warning'
|
||||
})
|
||||
return false
|
||||
},
|
||||
handleSuccess({ results, header }) {
|
||||
this.tableData = results
|
||||
this.tableHeader = header
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -33,6 +33,7 @@ export default {
|
||||
classObj() {
|
||||
return {
|
||||
hideSidebar: !this.sidebar.opened,
|
||||
openSidebar: this.sidebar.opened,
|
||||
withoutAnimation: this.sidebar.withoutAnimation,
|
||||
mobile: this.device === 'mobile'
|
||||
}
|
||||
@@ -53,6 +54,10 @@ export default {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
&.mobile.openSidebar{
|
||||
position: fixed;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
.drawer-bg {
|
||||
background: #000;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<section class="app-main" style="min-height: 100%">
|
||||
<section class="app-main">
|
||||
<transition name="fade-transform" mode="out-in">
|
||||
<keep-alive :include="cachedViews">
|
||||
<router-view :key="key"></router-view>
|
||||
@@ -21,3 +21,13 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.app-main {
|
||||
/*84 = navbar + tags-view = 50 +34 */
|
||||
min-height: calc(100vh - 84px);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
@@ -1,26 +1,24 @@
|
||||
<template>
|
||||
<div class="menu-wrapper">
|
||||
<template v-for="item in routes" v-if="!item.hidden&&item.children">
|
||||
<div v-if="!item.hidden&&item.children" class="menu-wrapper">
|
||||
|
||||
<router-link v-if="hasOneShowingChildren(item.children) && !item.children[0].children&&!item.alwaysShow" :to="item.path+'/'+item.children[0].path"
|
||||
:key="item.children[0].name">
|
||||
<el-menu-item :index="item.path+'/'+item.children[0].path" :class="{'submenu-title-noDropdown':!isNest}">
|
||||
<svg-icon v-if="item.children[0].meta&&item.children[0].meta.icon" :icon-class="item.children[0].meta.icon"></svg-icon>
|
||||
<span v-if="item.children[0].meta&&item.children[0].meta.title" slot="title">{{generateTitle(item.children[0].meta.title)}}</span>
|
||||
<router-link v-if="hasOneShowingChild(item.children) && !onlyOneChild.children&&!item.alwaysShow" :to="resolvePath(onlyOneChild.path)">
|
||||
<el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}">
|
||||
<svg-icon v-if="onlyOneChild.meta&&onlyOneChild.meta.icon" :icon-class="onlyOneChild.meta.icon"></svg-icon>
|
||||
<span v-if="onlyOneChild.meta&&onlyOneChild.meta.title" slot="title">{{generateTitle(onlyOneChild.meta.title)}}</span>
|
||||
</el-menu-item>
|
||||
</router-link>
|
||||
|
||||
<el-submenu v-else :index="item.name||item.path" :key="item.name">
|
||||
<el-submenu v-else :index="item.name||item.path">
|
||||
<template slot="title">
|
||||
<svg-icon v-if="item.meta&&item.meta.icon" :icon-class="item.meta.icon"></svg-icon>
|
||||
<span v-if="item.meta&&item.meta.title" slot="title">{{generateTitle(item.meta.title)}}</span>
|
||||
</template>
|
||||
|
||||
<template v-for="child in item.children" v-if="!child.hidden">
|
||||
<sidebar-item :is-nest="true" class="nest-menu" v-if="child.children&&child.children.length>0" :routes="[child]" :key="child.path"></sidebar-item>
|
||||
<sidebar-item :is-nest="true" class="nest-menu" v-if="child.children&&child.children.length>0" :item="child" :key="child.path" :base-path="resolvePath(child.path)"></sidebar-item>
|
||||
|
||||
<router-link v-else :to="item.path+'/'+child.path" :key="child.name">
|
||||
<el-menu-item :index="item.path+'/'+child.path">
|
||||
<router-link v-else :to="resolvePath(child.path)" :key="child.name">
|
||||
<el-menu-item :index="resolvePath(child.path)">
|
||||
<svg-icon v-if="child.meta&&child.meta.icon" :icon-class="child.meta.icon"></svg-icon>
|
||||
<span v-if="child.meta&&child.meta.title" slot="title">{{generateTitle(child.meta.title)}}</span>
|
||||
</el-menu-item>
|
||||
@@ -28,34 +26,54 @@
|
||||
</template>
|
||||
</el-submenu>
|
||||
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import path from 'path'
|
||||
import { generateTitle } from '@/utils/i18n'
|
||||
|
||||
export default {
|
||||
name: 'SidebarItem',
|
||||
props: {
|
||||
routes: {
|
||||
type: Array
|
||||
// route object
|
||||
item: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
isNest: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
basePath: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
onlyOneChild: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
hasOneShowingChildren(children) {
|
||||
hasOneShowingChild(children) {
|
||||
const showingChildren = children.filter(item => {
|
||||
return !item.hidden
|
||||
if (item.hidden) {
|
||||
return false
|
||||
} else {
|
||||
// temp set(will be used if only has one showing child )
|
||||
this.onlyOneChild = item
|
||||
return true
|
||||
}
|
||||
})
|
||||
if (showingChildren.length === 1) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
resolvePath(...paths) {
|
||||
return path.resolve(this.basePath, ...paths)
|
||||
},
|
||||
generateTitle
|
||||
}
|
||||
}
|
||||
|
@@ -9,7 +9,7 @@
|
||||
text-color="#bfcbd9"
|
||||
active-text-color="#409EFF"
|
||||
>
|
||||
<sidebar-item :routes="permission_routers"></sidebar-item>
|
||||
<sidebar-item v-for="route in permission_routers" :key="route.name" :item="route" :base-path="route.path"></sidebar-item>
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
|
@@ -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()
|
||||
})
|
||||
@@ -104,7 +104,8 @@ export default {
|
||||
openMenu(tag, e) {
|
||||
this.visible = true
|
||||
this.selectedTag = tag
|
||||
this.left = e.clientX
|
||||
const offsetLeft = this.$el.getBoundingClientRect().left // container margin left
|
||||
this.left = e.clientX - offsetLeft + 15 // 15: margin right
|
||||
this.top = e.clientY
|
||||
},
|
||||
closeMenu() {
|
||||
|
@@ -1,22 +1,27 @@
|
||||
<template>
|
||||
<div class="login-container">
|
||||
|
||||
<el-form class="login-form" autoComplete="on" :model="loginForm" :rules="loginRules" ref="loginForm" label-position="left">
|
||||
|
||||
<div class="title-container">
|
||||
<h3 class="title">{{$t('login.title')}}</h3>
|
||||
<lang-select class="set-language"></lang-select>
|
||||
</div>
|
||||
|
||||
<el-form-item prop="username">
|
||||
<span class="svg-container svg-container_login">
|
||||
<svg-icon icon-class="user" />
|
||||
</span>
|
||||
<el-input name="username" type="text" v-model="loginForm.username" autoComplete="on" placeholder="username" />
|
||||
<el-input name="username" type="text" v-model="loginForm.username" autoComplete="on" :placeholder="$t('login.username')"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="password">
|
||||
<span class="svg-container">
|
||||
<svg-icon icon-class="password" />
|
||||
</span>
|
||||
<el-input name="password" :type="passwordType" @keyup.enter.native="handleLogin" v-model="loginForm.password" autoComplete="on" placeholder="password" />
|
||||
<el-input name="password" :type="passwordType" @keyup.enter.native="handleLogin" v-model="loginForm.password" autoComplete="on"
|
||||
:placeholder="$t('login.password')" />
|
||||
<span class="show-pwd" @click="showPwd">
|
||||
<svg-icon icon-class="eye" />
|
||||
</span>
|
||||
@@ -137,36 +142,50 @@ export default {
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss">
|
||||
$bg:#2d3a4b;
|
||||
$light_gray:#eee;
|
||||
/* 修复input 背景不协调 和光标变色 */
|
||||
/* Detail see https://github.com/PanJiaChen/vue-element-admin/pull/927 */
|
||||
|
||||
$bg:#283443;
|
||||
$light_gray:#eee;
|
||||
$cursor: #fff;
|
||||
|
||||
/* reset element-ui css */
|
||||
.login-container {
|
||||
.el-input {
|
||||
display: inline-block;
|
||||
height: 47px;
|
||||
width: 85%;
|
||||
input {
|
||||
background: transparent;
|
||||
border: 0px;
|
||||
-webkit-appearance: none;
|
||||
border-radius: 0px;
|
||||
padding: 12px 5px 12px 15px;
|
||||
color: $light_gray;
|
||||
height: 47px;
|
||||
&:-webkit-autofill {
|
||||
-webkit-box-shadow: 0 0 0px 1000px $bg inset !important;
|
||||
-webkit-text-fill-color: #fff !important;
|
||||
@supports (-webkit-mask: none) and (not (cater-color: $cursor)) {
|
||||
.login-container .el-input input{
|
||||
color: $cursor;
|
||||
&::first-line {
|
||||
color: $light_gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
.el-form-item {
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
border-radius: 5px;
|
||||
color: #454545;
|
||||
|
||||
/* reset element-ui css */
|
||||
.login-container {
|
||||
.el-input {
|
||||
display: inline-block;
|
||||
height: 47px;
|
||||
width: 85%;
|
||||
input {
|
||||
background: transparent;
|
||||
border: 0px;
|
||||
-webkit-appearance: none;
|
||||
border-radius: 0px;
|
||||
padding: 12px 5px 12px 15px;
|
||||
color: $light_gray;
|
||||
height: 47px;
|
||||
caret-color: $cursor;
|
||||
&:-webkit-autofill {
|
||||
-webkit-box-shadow: 0 0 0px 1000px $bg inset !important;
|
||||
-webkit-text-fill-color: $cursor !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.el-form-item {
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
border-radius: 5px;
|
||||
color: #454545;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
@@ -211,7 +230,6 @@ $light_gray:#eee;
|
||||
position: relative;
|
||||
.title {
|
||||
font-size: 26px;
|
||||
font-weight: 400;
|
||||
color: $light_gray;
|
||||
margin: 0px auto 40px auto;
|
||||
text-align: center;
|
||||
|
7
src/views/nested/menu1/index.vue
Normal file
7
src/views/nested/menu1/index.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<template >
|
||||
<div style="padding:30px;">
|
||||
<el-alert title="menu 1" :closable="false">
|
||||
<router-view />
|
||||
</el-alert>
|
||||
</div>
|
||||
</template>
|
7
src/views/nested/menu1/menu1-1/index.vue
Normal file
7
src/views/nested/menu1/menu1-1/index.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<template >
|
||||
<div style="padding:30px;">
|
||||
<el-alert title="menu 1-1" type="success" :closable="false">
|
||||
<router-view />
|
||||
</el-alert>
|
||||
</div>
|
||||
</template>
|
7
src/views/nested/menu1/menu1-2/index.vue
Normal file
7
src/views/nested/menu1/menu1-2/index.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<div style="padding:30px;">
|
||||
<el-alert title="menu 1-2" type="success" :closable="false">
|
||||
<router-view />
|
||||
</el-alert>
|
||||
</div>
|
||||
</template>
|
5
src/views/nested/menu1/menu1-2/menu1-2-1/index.vue
Normal file
5
src/views/nested/menu1/menu1-2/menu1-2-1/index.vue
Normal file
@@ -0,0 +1,5 @@
|
||||
<template functional>
|
||||
<div style="padding:30px;">
|
||||
<el-alert title="menu 1-2-1" type="warning" :closable="false" />
|
||||
</div>
|
||||
</template>
|
5
src/views/nested/menu1/menu1-2/menu1-2-2/index.vue
Normal file
5
src/views/nested/menu1/menu1-2/menu1-2-2/index.vue
Normal file
@@ -0,0 +1,5 @@
|
||||
<template functional>
|
||||
<div style="padding:30px;">
|
||||
<el-alert title="menu 1-2-2" type="warning" :closable="false" />
|
||||
</div>
|
||||
</template>
|
5
src/views/nested/menu1/menu1-3/index.vue
Normal file
5
src/views/nested/menu1/menu1-3/index.vue
Normal file
@@ -0,0 +1,5 @@
|
||||
<template functional>
|
||||
<div style="padding:30px;">
|
||||
<el-alert title="menu 1-3" type="success" :closable="false" />
|
||||
</div>
|
||||
</template>
|
5
src/views/nested/menu2/index.vue
Normal file
5
src/views/nested/menu2/index.vue
Normal file
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div style="padding:30px;">
|
||||
<el-alert title="menu 2" :closable="false" />
|
||||
</div>
|
||||
</template>
|
@@ -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++
|
||||
}
|
||||
|
@@ -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">
|
||||
|
@@ -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() {
|
||||
|
@@ -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">
|
||||
|
@@ -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
|
||||
|
@@ -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>
|
||||
|
@@ -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}}
|
||||
|
Reference in New Issue
Block a user