Compare commits

..

2 Commits

Author SHA1 Message Date
Pan
56f67f0538 perf[chore]: webpack 2018-01-31 15:33:34 +08:00
Pan
e504ae533a refactor[tinymce]: import tinymce from npm 2018-01-31 14:13:20 +08:00
230 changed files with 4666 additions and 6947 deletions

View File

@@ -8,10 +8,5 @@
}], }],
"stage-2" "stage-2"
], ],
"plugins": ["transform-vue-jsx", "transform-runtime"], "plugins": ["transform-vue-jsx", "transform-runtime"]
"env": {
"development":{
"plugins": ["dynamic-import-node"]
}
}
} }

View File

@@ -1,7 +1,7 @@
module.exports = { module.exports = {
root: true, root: true,
parser: 'babel-eslint',
parserOptions: { parserOptions: {
parser: 'babel-eslint',
sourceType: 'module' sourceType: 'module'
}, },
env: { env: {
@@ -9,19 +9,22 @@ module.exports = {
node: true, node: true,
es6: true, es6: true,
}, },
extends: ['plugin:vue/recommended', 'eslint:recommended'], extends: 'eslint:recommended',
// required to lint *.vue files
plugins: [
'html'
],
// check if imports actually resolve
'settings': {
'import/resolver': {
'webpack': {
'config': 'build/webpack.base.conf.js'
}
}
},
// add your custom rules here // add your custom rules here
//it is base on https://github.com/vuejs/eslint-config-vue //it is base on https://github.com/vuejs/eslint-config-vue
rules: { 'rules': {
"vue/max-attributes-per-line": [2, {
"singleline": 10,
"multiline": {
"max": 1,
"allowFirstLine": false
}
}],
"vue/name-property-casing": ["error", "PascalCase"],
'accessor-pairs': 2, 'accessor-pairs': 2,
'arrow-spacing': [2, { 'arrow-spacing': [2, {
'before': true, 'before': true,
@@ -193,3 +196,4 @@ module.exports = {
'array-bracket-spacing': [2, 'never'] 'array-bracket-spacing': [2, 'never']
} }
} }

1
.gitignore vendored
View File

@@ -4,7 +4,6 @@ dist/
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
**/*.log
test/unit/coverage test/unit/coverage
test/e2e/reports test/e2e/reports

161
README.md
View File

@@ -3,134 +3,99 @@
</p> </p>
<p align="center"> <p align="center">
<a href="https://github.com/vuejs/vue"> <a href="https://github.com/vuejs/vue">
<img src="https://img.shields.io/badge/vue-2.5.17-brightgreen.svg" alt="vue"> <img src="https://img.shields.io/badge/vue-2.5.10-brightgreen.svg" alt="vue">
</a> </a>
<a href="https://github.com/ElemeFE/element"> <a href="https://github.com/ElemeFE/element">
<img src="https://img.shields.io/badge/element--ui-2.4.6-brightgreen.svg" alt="element-ui"> <img src="https://img.shields.io/badge/element--ui-2.0.8-brightgreen.svg" alt="element-ui">
</a> </a>
<a href="https://travis-ci.org/PanJiaChen/vue-element-admin" rel="nofollow"> <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"> <img src="https://travis-ci.org/PanJiaChen/vue-element-admin.svg?branch=master" alt="Build Status">
</a> </a>
<a href="https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE"> <a href="https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/mashape/apistatus.svg" alt="license"> <img src="https://img.shields.io/github/license/mashape/apistatus.svg" alt="license">
</a> </a>
<a href="https://github.com/PanJiaChen/vue-element-admin/releases"> <a href="https://github.com/PanJiaChen/vue-element-admin/releases">
<img src="https://img.shields.io/github/release/PanJiaChen/vue-element-admin.svg" alt="GitHub release"> <img src="https://img.shields.io/github/release/PanJiaChen/vue-element-admin.svg" alt="GitHub release">
</a> </a>
</p> </p>
English | [简体中文](./README.zh-CN.md) English | [简体中文](./README.zh-CN.md)
## Introduction ## Introduction
[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). `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.
It is a magical vue admin based on the newest development stack of vue, built-in i18n solution, typical templates for enterprise applications, lots of awesome features. It helps you build a large complex Single-Page Applications. I believe whatever your needs are, this project will help you.
- [Preview](http://panjiachen.github.io/vue-element-admin) - [Preview](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)
- [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/) 国内用户可访问该地址在线预览 - Base template recommends using: [vueAdmin-template](https://github.com/PanJiaChen/vueAdmin-template)  
- Desktop: [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
**This project is positioned as a background integration solution and is not suitable for secondary development as a basic template.** **Note: This project uses element-ui@2.0.0+ version, so the minimum compatible vue@2.5.0**
- Base template recommends using: [vue-admin-template](https://github.com/PanJiaChen/vue-admin-template)
- Desktop: [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
**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+**
**Start using `webpack4` from `v3.8.0`. If you still want to continue using `webpack3`, please use this branch [webpack3](https://github.com/PanJiaChen/vue-element-admin/tree/webpack3)**
## Preparation ## 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/), [axios](https://github.com/axios/axios) and [element-ui](https://github.com/ElemeFE/element), all request data is simulated using [Mock.js](https://github.com/nuysoft/Mock). You need to install [node](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.
Understanding and learning this knowledge in advance will greatly help the use of this project.
--- **This project is not a scaffolding and is more of an integrated solution.**
**This project does not support low version browsers (e.g. IE). Please add polyfill yourself if you need them.**
<p align="center"> <p align="center">
<img width="900" src="https://wpimg.wallstcn.com/a5894c1b-f6af-456e-82df-1151da0839bf.png"> <img width="900" src="https://wpimg.wallstcn.com/a5894c1b-f6af-456e-82df-1151da0839bf.png">
</p> </p>
## Features ## Features
``` ```
- Login / Logout - Login / Logout
- Permission authentication
- Permission Authentication
- Page permission
- Directive permission
- Two-step login
- Multi-environment build - Multi-environment build
- dev sit stage prod - Dynamic sidebar (supports multi-level routing)
- Dynamic breadcrumb
- Global Features - I18n
- I18n - Customizable theme
- Multiple dynamic themes - Tags-view(Tab page Support right-click operation)
- Dynamic sidebar (supports multi-level routing) - Rich text editor
- Dynamic breadcrumb - Markdown editor
- Tags-view(Tab page Support right-click operation) - JSON editor
- Svg Sprite - Screenfull
- Mock data - Drag and drop list
- Screenfull - Svg Sprite
- 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 - Dashboard
- Guide Page - Mock data
- Echarts - Echarts
- Clipboard - 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
- Dropzone
- Sticky
- CountTo
- Markdown to html - Markdown to html
``` ```
## Getting started ## Getting started
```bash ```bash
# clone the project # clone the projice
git clone https://github.com/PanJiaChen/vue-element-admin.git git clone https://github.com/PanJiaChen/vue-element-admin.git
# install dependency # install dependency
@@ -143,7 +108,6 @@ npm run dev
This will automatically open http://localhost:9527. This will automatically open http://localhost:9527.
## Build ## Build
```bash ```bash
# build for test environment # build for test environment
npm run build:sit npm run build:sit
@@ -153,14 +117,10 @@ npm run build:prod
``` ```
## Advanced ## Advanced
```bash ```bash
# --report to build with bundle size analytics # --report to build with bundle size analytics
npm run build:prod --report 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 # --preview to start a server in local to preview
npm run build:prod --preview npm run build:prod --preview
@@ -171,26 +131,21 @@ npm run lint
npm run lint -- --fix npm run lint -- --fix
``` ```
Refer to [Documentation](https://panjiachen.github.io/vue-element-admin-site/guide/essentials/deploy.html) for more information Refer to [Documentation](https://panjiachen.github.io/vue-element-admin-site/#/deploy) for more information
## Changelog ## Changelog
Detailed changes for each release are documented in the [release notes](https://github.com/PanJiaChen/vue-element-admin/releases). Detailed changes for each release are documented in the [release notes](https://github.com/PanJiaChen/vue-element-admin/releases).
## Online Demo ## Online Demo
[Preview](http://panjiachen.github.io/vue-element-admin) [Preview](http://panjiachen.github.io/vue-element-admin)
## Donate ## Donate
If you find this project useful, you can buy author a glass of juice :tropical_drink: If you find this project useful, you can buy author a glass of juice :tropical_drink:
![donate](https://wpimg.wallstcn.com/bd273f0d-83a0-4ef2-92e1-9ac8ed3746b9.png) ![donate](https://wpimg.wallstcn.com/bd273f0d-83a0-4ef2-92e1-9ac8ed3746b9.png)
[Paypal Me](https://www.paypal.me/panfree23) [Paypal Me](https://www.paypal.me/panfree23)
[Buy me a coffee](https://www.buymeacoffee.com/Pan)
## License ## License
[MIT](https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE) [MIT](https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE)

View File

@@ -3,178 +3,137 @@
</p> </p>
<p align="center"> <p align="center">
<a href="https://github.com/vuejs/vue"> <a href="https://github.com/vuejs/vue">
<img src="https://img.shields.io/badge/vue-2.5.10-brightgreen.svg" alt="vue"> <img src="https://img.shields.io/badge/vue-2.5.10-brightgreen.svg" alt="vue">
</a> </a>
<a href="https://github.com/ElemeFE/element"> <a href="https://github.com/ElemeFE/element">
<img src="https://img.shields.io/badge/element--ui-2.3.2-brightgreen.svg" alt="element-ui"> <img src="https://img.shields.io/badge/element--ui-2.0.8-brightgreen.svg" alt="element-ui">
</a> </a>
<a href="https://travis-ci.org/PanJiaChen/vue-element-admin" rel="nofollow"> <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"> <img src="https://travis-ci.org/PanJiaChen/vue-element-admin.svg?branch=master" alt="Build Status">
</a> </a>
<a href="https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE"> <a href="https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/mashape/apistatus.svg" alt="license"> <img src="https://img.shields.io/github/license/mashape/apistatus.svg" alt="license">
</a> </a>
<a href="https://github.com/PanJiaChen/vue-element-admin/releases"> <a href="https://github.com/PanJiaChen/vue-element-admin/releases">
<img src="https://img.shields.io/github/release/PanJiaChen/vue-element-admin.svg" alt="GitHub release"> <img src="https://img.shields.io/github/release/PanJiaChen/vue-element-admin.svg" alt="GitHub release">
</a> </a>
</p> </p>
简体中文 | [English](./README.md) 简体中文 | [English](./README.md)
## 简介 ## 简介
[vue-element-admin](http://panjiachen.github.io/vue-element-admin) 是一个后台集成解决方案,它基于 [vue](https://github.com/vuejs/vue) 和 [element](https://github.com/ElemeFE/element)。它使用了最新的前端技术栈,内置了 i18 国际化解决方案,动态路由,权限验证,提炼了典型的业务模型,提供了丰富的功能组件,它可以帮助你快速搭建企业级中后台产品原型。相信不管你的需求是什么,本项目都能帮助到你。 `vue-element-admin` 是一个后台集成解决方案,它基于 [Vue.js](https://github.com/vuejs/vue) 和 [element](https://github.com/ElemeFE/element)。它使用了最新的前端技术栈内置了i18国际化解决方案动态路由权限验证等很多功能特性,相信不管你的需求是什么,本项目都能帮助到你。
- [在线访问](http://panjiachen.github.io/vue-element-admin) - [在线访问](http://panjiachen.github.io/vue-element-admin)
- [使用文档](https://panjiachen.github.io/vue-element-admin-site/zh/) - [使用文档](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)
- [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/zh/donate/)
- [Gitee](https://panjiachen.gitee.io/vue-element-admin/) 国内用户可访问该地址在线预览
- [国内访问文档](https://panjiachen.gitee.io/vue-element-admin-site/zh/) 方便没翻墙的用户查看文档
**本项目的定位是后台集成方案,不适合当基础模板来开发。** **本项目的定位是后台集成方案,不适合当基础模板来开发。**
- 模板建议使用: [vueAdmin-template](https://github.com/PanJiaChen/vueAdmin-template)  
- 桌面端: [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
- 模板建议使用: [vue-admin-template](https://github.com/PanJiaChen/vue-admin-template) **注意:该项目使用 element-ui@2.0.0+ 版本,所以最低兼容 vue@2.5.0**
- 桌面端: [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
群主 **[圈子](https://jianshiapp.com/circles/1209)** 楼主会经常分享一些技术相关的东西,或者加入[qq 群](https://github.com/PanJiaChen/vue-element-admin/issues/602)
**注意:该项目使用 element-ui@2.3.0+ 版本,所以最低兼容 vue@2.5.0+**
**从`v3.8.0`开始使用`webpack4`。所以若还想使用`webpack3`开发,请使用该分支[webpack3](https://github.com/PanJiaChen/vue-element-admin/tree/webpack3)**
**该项目不支持低版本浏览器(如 ie),有需求请自行添加 polyfill [详情](https://github.com/PanJiaChen/vue-element-admin/wiki#babel-polyfill)**
## 前序准备 ## 前序准备
你需要在本地安装 [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)模拟,提前了解和学习这些知识会对使用本项目有很大的帮助。 的本地环境需要安装 [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)模拟,提前了解和学习这些知识会对使用本项目有很大的帮助。
同时配套一个系列的教程文章,如何从零构建后一个完整的后台项目,建议大家先看完这些文章再来实践本项目 同时配套一个系列的教程文章,如何从零构建后一个完整的后台项目,建议大家先看完这些文章再来实践本项目
- [手摸手,带你用 vue 撸后台 系列一(基础篇)](https://juejin.im/post/59097cd7a22b9d0065fb61d2)
- [手摸手,带你用 vue 撸后台 系列二(登录权限篇)](https://juejin.im/post/591aa14f570c35006961acac)
- [手摸手,带你用 vue 撸后台 系列三 (实战篇)](https://juejin.im/post/593121aa0ce4630057f70d35)
- [手摸手,带你用 vue 撸后台 系列四(vueAdmin 一个极简的后台基础模板)](https://juejin.im/post/595b4d776fb9a06bbe7dba56)
- [手摸手,带你封装一个 vue component](https://segmentfault.com/a/1190000009090836)
- [手摸手,带你优雅的使用 icon](https://juejin.im/post/59bb864b5188257e7a427c09)
- [手摸手,带你用 vue 撸后台 系列一(基础篇)](https://juejin.im/post/59097cd7a22b9d0065fb61d2) 响应需求开了一个qq群 `591724180` 方便大家交流
- [手摸手,带你用 vue 撸后台 系列二(登录权限篇)](https://juejin.im/post/591aa14f570c35006961acac)
- [手摸手,带你用 vue 撸后台 系列三 (实战篇)](https://juejin.im/post/593121aa0ce4630057f70d35)
- [手摸手,带你用 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)
**如有问题请先看上述使用文档和文章,若不能满足,欢迎 issue 和 pr** 或者加入该群主 **[圈子](https://jianshiapp.com/circles/1209)** 楼主会经常分享一些技术相关的东西
**如有问题请先看上述使用文档和文章,若不能满足,欢迎 issue 和 pr**
**本项目并不是一个脚手架,更倾向于是一个集成解决方案**
**该项目不支持低版本浏览器(如ie)有需求请自行添加polyfill [详情](https://github.com/PanJiaChen/vue-element-admin/wiki#babel-polyfill)**
<p align="center"> <p align="center">
<img width="900" src="https://wpimg.wallstcn.com/a5894c1b-f6af-456e-82df-1151da0839bf.png"> <img width="900" src="https://wpimg.wallstcn.com/a5894c1b-f6af-456e-82df-1151da0839bf.png">
</p> </p>
## 功能 ## 功能
``` ```
- 登录 / 注销 - 登录/注销
- 权限验证 - 权限验证
- 页面权限
- 指令权限
- 二步登录
- 多环境发布 - 多环境发布
- dev sit stage prod - 动态侧边栏(支持多级路由)
- 动态面包屑
- 全局功能 - 国际化多语言
- 国际化多语言 - 多种动态换肤
- 多种动态换肤 - 快捷导航(标签页)
- 动态侧边栏(支持多级路由嵌套) - 富文本编辑器
- 动态面包屑 - Markdown编辑器
- 快捷导航(标签页) - JSON编辑器
- Svg Sprite 图标 - Screenfull全屏
- 本地mock数据 - 列表拖拽
- Screenfull全屏 - Svg Sprite 图标
- 自适应收缩侧边栏
- 编辑器
- 富文本
- Markdown
- JSON 等多格式
- Excel
- 导出excel
- 导出zip
- 导入excel
- 前端可视化excel
- 表格
- 动态表格
- 拖拽表格
- 树形表格
- 内联编辑
- 错误页面
- 401
- 404
- 組件
- 头像上传
- 返回顶部
- 拖拽Dialog
- 拖拽看板
- 列表拖拽
- SplitPane
- Dropzone
- Sticky
- CountTo
- 综合实例
- 错误日志
- Dashboard - Dashboard
- 引导页 - 本地mock数据
- Echarts 图表 - Echarts 图表
- Clipboard(剪贴复制) - Clipboard(剪贴复制)
- 401/404错误页面
- 错误日志
- 导出excel
- 导出zip
- 前端可视化excel
- 树形table
- Table example
- 动态table example
- 拖拽table example
- 内联编辑table example
- Form example
- 二步登录
- SplitPane
- Dropzone
- Sticky
- CountTo
- Markdown2html - Markdown2html
``` ```
## 开发 ## 开发
```bash ```bash
# 克隆项目 # 克隆项目
git clone https://github.com/PanJiaChen/vue-element-admin.git git clone https://github.com/PanJiaChen/vue-element-admin.git
# 安装依赖 # 安装依赖
npm install npm install
   
# 建议不要用cnpm安装 会有各种诡异的bug 可以通过如下操作解决 npm 下载速度慢的问题 # 建议不要用cnpm安装 会有各种诡异的bug 可以通过如下操作解决 npm 下载速度慢的问题
npm install --registry=https://registry.npm.taobao.org npm install --registry=https://registry.npm.taobao.org
# 启动服务 # 启动服务
npm run dev npm run dev
``` ```
浏览器访问 http://localhost:9527 浏览器访问 http://localhost:9527
## 发布 ## 发布
```bash ```bash
# 构建测试环境 # 构建测试环境
npm run build:sit npm run build:sit
# 构建生环境 # 构建生环境
npm run build:prod npm run build:prod
``` ```
## 其它 ## 其它
```bash ```bash
# --report to build with bundle size analytics # --report to build with bundle size analytics
npm run build:prod 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 # --preview to start a server in local to preview
npm run build:prod --preview npm run build:prod --preview
@@ -186,18 +145,15 @@ npm run lint
npm run lint -- --fix npm run lint -- --fix
``` ```
更多信息请参考 [使用文档](https://panjiachen.github.io/vue-element-admin-site/zh/) 更多信息请参考 [使用文档](https://panjiachen.github.io/vue-element-admin-site/#/deploy)
## Changelog ## Changelog
Detailed changes for each release are documented in the [release notes](https://github.com/PanJiaChen/vue-element-admin/releases). Detailed changes for each release are documented in the [release notes](https://github.com/PanJiaChen/vue-element-admin/releases).
## Online Demo ## Online Demo
[在线 Demo](http://panjiachen.github.io/vue-element-admin) [在线 Demo](http://panjiachen.github.io/vue-element-admin)
## Donate ## Donate
如果你觉得这个项目帮助到了你,你可以帮作者买一杯果汁表示鼓励 :tropical_drink: 如果你觉得这个项目帮助到了你,你可以帮作者买一杯果汁表示鼓励 :tropical_drink:
![donate](https://panjiachen.github.io/donate/donation.png) ![donate](https://panjiachen.github.io/donate/donation.png)

View File

@@ -8,12 +8,9 @@ const chalk = require('chalk')
const webpack = require('webpack') const webpack = require('webpack')
const config = require('../config') const config = require('../config')
const webpackConfig = require('./webpack.prod.conf') const webpackConfig = require('./webpack.prod.conf')
var connect = require('connect') const server = require('pushstate-server')
var serveStatic = require('serve-static')
const spinner = ora( var spinner = ora('building for '+ process.env.env_config+ ' environment...' )
'building for ' + process.env.env_config + ' environment...'
)
spinner.start() spinner.start()
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
@@ -21,47 +18,31 @@ rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
webpack(webpackConfig, (err, stats) => { webpack(webpackConfig, (err, stats) => {
spinner.stop() spinner.stop()
if (err) throw err if (err) throw err
process.stdout.write( process.stdout.write(stats.toString({
stats.toString({ colors: true,
colors: true, modules: false,
modules: false, children: false,
children: false, chunks: false,
chunks: false, chunkModules: false
chunkModules: false }) + '\n\n')
}) + '\n\n'
)
if (stats.hasErrors()) { if (stats.hasErrors()) {
console.log(chalk.red(' Build failed with errors.\n')) console.log(chalk.red(' Build failed with errors.\n'))
process.exit(1) process.exit(1)
} }
console.log(chalk.cyan(' Build complete.\n')) console.log(chalk.cyan(' Build complete.\n'))
console.log( console.log(chalk.yellow(
chalk.yellow( ' Tip: built files are meant to be served over an HTTP server.\n' +
' Tip: built files are meant to be served over an HTTP server.\n' + ' Opening index.html over file:// won\'t work.\n'
" Opening index.html over file:// won't work.\n" ))
) if(process.env.npm_config_preview){
) server.start({
port: 9526,
if (process.env.npm_config_preview) { directory: './dist',
const port = 9526 file: '/index.html'
const host = 'http://localhost:' + port });
const basePath = config.build.assetsPublicPath console.log('> Listening at ' + 'http://localhost:9526' + '\n')
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}`)
)
})
} }
}) })
}) })

View File

@@ -4,11 +4,8 @@ const semver = require('semver')
const packageConfig = require('../package.json') const packageConfig = require('../package.json')
const shell = require('shelljs') const shell = require('shelljs')
function exec(cmd) { function exec (cmd) {
return require('child_process') return require('child_process').execSync(cmd).toString().trim()
.execSync(cmd)
.toString()
.trim()
} }
const versionRequirements = [ const versionRequirements = [
@@ -27,30 +24,23 @@ if (shell.which('npm')) {
}) })
} }
module.exports = function() { module.exports = function () {
const warnings = [] const warnings = []
for (let i = 0; i < versionRequirements.length; i++) { for (let i = 0; i < versionRequirements.length; i++) {
const mod = versionRequirements[i] const mod = versionRequirements[i]
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
warnings.push( warnings.push(mod.name + ': ' +
mod.name + chalk.red(mod.currentVersion) + ' should be ' +
': ' + chalk.green(mod.versionRequirement)
chalk.red(mod.currentVersion) +
' should be ' +
chalk.green(mod.versionRequirement)
) )
} }
} }
if (warnings.length) { if (warnings.length) {
console.log('') console.log('')
console.log( console.log(chalk.yellow('To use this template, you must update following to modules:'))
chalk.yellow(
'To use this template, you must update following to modules:'
)
)
console.log() console.log()
for (let i = 0; i < warnings.length; i++) { for (let i = 0; i < warnings.length; i++) {

View File

@@ -1,19 +1,18 @@
'use strict' 'use strict'
const path = require('path') const path = require('path')
const config = require('../config') const config = require('../config')
const MiniCssExtractPlugin = require('mini-css-extract-plugin') const ExtractTextPlugin = require('extract-text-webpack-plugin')
const packageConfig = require('../package.json') const packageConfig = require('../package.json')
exports.assetsPath = function(_path) { exports.assetsPath = function (_path) {
const assetsSubDirectory = const assetsSubDirectory = process.env.NODE_ENV === 'production'
process.env.NODE_ENV === 'production' ? config.build.assetsSubDirectory
? config.build.assetsSubDirectory : config.dev.assetsSubDirectory
: config.dev.assetsSubDirectory
return path.posix.join(assetsSubDirectory, _path) return path.posix.join(assetsSubDirectory, _path)
} }
exports.cssLoaders = function(options) { exports.cssLoaders = function (options) {
options = options || {} options = options || {}
const cssLoader = { const cssLoader = {
@@ -31,22 +30,8 @@ exports.cssLoaders = function(options) {
} }
// generate loader string to be used with extract text plugin // generate loader string to be used with extract text plugin
function generateLoaders(loader, loaderOptions) { function generateLoaders (loader, loaderOptions) {
const loaders = [] const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
// 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) { if (loader) {
loaders.push({ loaders.push({
@@ -57,16 +42,24 @@ exports.cssLoaders = function(options) {
}) })
} }
return loaders // 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)
}
} }
// https://vue-loader.vuejs.org/en/configurations/extract-css.html // https://vue-loader.vuejs.org/en/configurations/extract-css.html
return { return {
css: generateLoaders(), css: generateLoaders(),
postcss: generateLoaders(), postcss: generateLoaders(),
less: generateLoaders('less'), less: generateLoaders('less'),
sass: generateLoaders('sass', { sass: generateLoaders('sass', { indentedSyntax: true }),
indentedSyntax: true
}),
scss: generateLoaders('sass'), scss: generateLoaders('sass'),
stylus: generateLoaders('stylus'), stylus: generateLoaders('stylus'),
styl: generateLoaders('stylus') styl: generateLoaders('stylus')
@@ -74,7 +67,7 @@ exports.cssLoaders = function(options) {
} }
// Generate loaders for standalone style files (outside of .vue) // Generate loaders for standalone style files (outside of .vue)
exports.styleLoaders = function(options) { exports.styleLoaders = function (options) {
const output = [] const output = []
const loaders = exports.cssLoaders(options) const loaders = exports.cssLoaders(options)

View File

@@ -1,5 +1,22 @@
'use strict' '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 = { module.exports = {
//You can set the vue-loader configuration by yourself. loaders: utils.cssLoaders({
sourceMap: sourceMapEnabled,
extract: isProduction
}),
cssSourceMap: sourceMapEnabled,
cacheBusting: config.dev.cacheBusting,
transformToRequire: {
video: ['src', 'poster'],
source: 'src',
img: 'src',
image: 'xlink:href'
}
} }

View File

@@ -2,10 +2,9 @@
const path = require('path') const path = require('path')
const utils = require('./utils') const utils = require('./utils')
const config = require('../config') const config = require('../config')
const { VueLoaderPlugin } = require('vue-loader')
const vueLoaderConfig = require('./vue-loader.conf') const vueLoaderConfig = require('./vue-loader.conf')
function resolve(dir) { function resolve (dir) {
return path.join(__dirname, '..', dir) return path.join(__dirname, '..', dir)
} }
@@ -28,15 +27,15 @@ module.exports = {
output: { output: {
path: config.build.assetsRoot, path: config.build.assetsRoot,
filename: '[name].js', filename: '[name].js',
publicPath: publicPath: process.env.NODE_ENV === 'production'
process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath
? config.build.assetsPublicPath : config.dev.assetsPublicPath
: config.dev.assetsPublicPath
}, },
resolve: { resolve: {
extensions: ['.js', '.vue', '.json'], extensions: ['.js', '.vue', '.json'],
alias: { alias: {
'@': resolve('src') 'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
} }
}, },
module: { module: {
@@ -50,11 +49,7 @@ module.exports = {
{ {
test: /\.js$/, test: /\.js$/,
loader: 'babel-loader?cacheDirectory', loader: 'babel-loader?cacheDirectory',
include: [ include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
resolve('src'),
resolve('test'),
resolve('node_modules/webpack-dev-server/client')
]
}, },
{ {
test: /\.svg$/, test: /\.svg$/,
@@ -91,7 +86,6 @@ module.exports = {
} }
] ]
}, },
plugins: [new VueLoaderPlugin()],
node: { node: {
// prevent webpack from injecting useless setImmediate polyfill because Vue // prevent webpack from injecting useless setImmediate polyfill because Vue
// source contains it (although only uses it if it's native). // source contains it (although only uses it if it's native).

View File

@@ -1,15 +1,16 @@
'use strict' 'use strict'
const path = require('path')
const utils = require('./utils') const utils = require('./utils')
const webpack = require('webpack') const webpack = require('webpack')
const config = require('../config') const config = require('../config')
const merge = require('webpack-merge') const merge = require('webpack-merge')
const path = require('path')
const baseWebpackConfig = require('./webpack.base.conf') const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const portfinder = require('portfinder') const portfinder = require('portfinder')
function resolve(dir) { function resolve (dir) {
return path.join(__dirname, '..', dir) return path.join(__dirname, '..', dir)
} }
@@ -17,12 +18,8 @@ const HOST = process.env.HOST
const PORT = process.env.PORT && Number(process.env.PORT) const PORT = process.env.PORT && Number(process.env.PORT)
const devWebpackConfig = merge(baseWebpackConfig, { const devWebpackConfig = merge(baseWebpackConfig, {
mode: 'development',
module: { module: {
rules: utils.styleLoaders({ rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
sourceMap: config.dev.cssSourceMap,
usePostCSS: true
})
}, },
// cheap-module-eval-source-map is faster for development // cheap-module-eval-source-map is faster for development
devtool: config.dev.devtool, devtool: config.dev.devtool,
@@ -30,8 +27,13 @@ const devWebpackConfig = merge(baseWebpackConfig, {
// these devServer options should be customized in /config/index.js // these devServer options should be customized in /config/index.js
devServer: { devServer: {
clientLogLevel: 'warning', clientLogLevel: 'warning',
historyApiFallback: true, historyApiFallback: {
rewrites: [
{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
],
},
hot: true, hot: true,
contentBase: false, // since we use CopyWebpackPlugin.
compress: true, compress: true,
host: HOST || config.dev.host, host: HOST || config.dev.host,
port: PORT || config.dev.port, port: PORT || config.dev.port,
@@ -43,7 +45,7 @@ const devWebpackConfig = merge(baseWebpackConfig, {
proxy: config.dev.proxyTable, proxy: config.dev.proxyTable,
quiet: true, // necessary for FriendlyErrorsPlugin quiet: true, // necessary for FriendlyErrorsPlugin
watchOptions: { watchOptions: {
poll: config.dev.poll poll: config.dev.poll,
} }
}, },
plugins: [ plugins: [
@@ -51,15 +53,24 @@ const devWebpackConfig = merge(baseWebpackConfig, {
'process.env': require('../config/dev.env') 'process.env': require('../config/dev.env')
}), }),
new webpack.HotModuleReplacementPlugin(), 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 // https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
filename: 'index.html', filename: 'index.html',
template: 'index.html', template: 'index.html',
inject: true, inject: true,
favicon: resolve('favicon.ico'), favicon: resolve('favicon.ico'),
title: 'vue-element-admin', title: 'vue-element-admin'
path: config.dev.assetsPublicPath + config.dev.assetsSubDirectory }),
}) // copy custom static assets
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.dev.assetsSubDirectory,
ignore: ['.*']
}
])
] ]
}) })
@@ -75,20 +86,14 @@ module.exports = new Promise((resolve, reject) => {
devWebpackConfig.devServer.port = port devWebpackConfig.devServer.port = port
// Add FriendlyErrorsPlugin // Add FriendlyErrorsPlugin
devWebpackConfig.plugins.push( devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
new FriendlyErrorsPlugin({ compilationSuccessInfo: {
compilationSuccessInfo: { messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
messages: [ },
`Your application is running here: http://${ onErrors: config.dev.notifyOnErrors
devWebpackConfig.devServer.host ? utils.createNotifierCallback()
}:${port}` : undefined
] }))
},
onErrors: config.dev.notifyOnErrors
? utils.createNotifierCallback()
: undefined
})
)
resolve(devWebpackConfig) resolve(devWebpackConfig)
} }

View File

@@ -7,23 +7,17 @@ const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf') const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin') const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin')
const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin') const ExtractTextPlugin = require('extract-text-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin') const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin') const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
function resolve(dir) { function resolve (dir) {
return path.join(__dirname, '..', 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, { const webpackConfig = merge(baseWebpackConfig, {
mode: 'production',
module: { module: {
rules: utils.styleLoaders({ rules: utils.styleLoaders({
sourceMap: config.build.productionSourceMap, sourceMap: config.build.productionSourceMap,
@@ -34,18 +28,37 @@ const webpackConfig = merge(baseWebpackConfig, {
devtool: config.build.productionSourceMap ? config.build.devtool : false, devtool: config.build.productionSourceMap ? config.build.devtool : false,
output: { output: {
path: config.build.assetsRoot, path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash:8].js'), filename: utils.assetsPath('js/[name].[chunkhash].js'),
chunkFilename: utils.assetsPath('js/[name].[chunkhash:8].js') chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
}, },
plugins: [ plugins: [
// http://vuejs.github.io/vue-loader/en/workflow/production.html // http://vuejs.github.io/vue-loader/en/workflow/production.html
new webpack.DefinePlugin({ new webpack.DefinePlugin({
'process.env': env 'process.env': env
}), }),
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false
}
},
sourceMap: config.build.productionSourceMap,
parallel: true
}),
// extract css into its own file // extract css into its own file
new MiniCssExtractPlugin({ new ExtractTextPlugin({
filename: utils.assetsPath('css/[name].[contenthash:8].css'), filename: utils.assetsPath('css/[name].[contenthash].css'),
chunkFilename: utils.assetsPath('css/[name].[contenthash:8].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 }
}), }),
// generate dist index.html with correct asset hash for caching. // generate dist index.html with correct asset hash for caching.
// you can customize output by editing /index.html // you can customize output by editing /index.html
@@ -56,41 +69,82 @@ const webpackConfig = merge(baseWebpackConfig, {
inject: true, inject: true,
favicon: resolve('favicon.ico'), favicon: resolve('favicon.ico'),
title: 'vue-element-admin', title: 'vue-element-admin',
path: config.build.assetsPublicPath + config.build.assetsSubDirectory,
minify: { minify: {
removeComments: true, removeComments: true,
collapseWhitespace: true, collapseWhitespace: true,
removeAttributeQuotes: true removeAttributeQuotes: true
// more options: // more options:
// https://github.com/kangax/html-minifier#options-quick-reference // https://github.com/kangax/html-minifier#options-quick-reference
} },
// default sort mode uses toposort which cannot handle cyclic deps // necessary to consistently work with multiple chunks via CommonsChunkPlugin
// in certain cases, and in webpack 4, chunk order in HTML doesn't chunksSortMode: 'dependency'
// 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 // keep module.id stable when vender modules does not change
new webpack.HashedModuleIdsPlugin(), 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 tinymce into its own file
new webpack.optimize.CommonsChunkPlugin({
async: 'tinymce',
minChunks(module) {
var context = module.context;
return context && (context.indexOf('tinymce') >= 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 // copy custom static assets
new CopyWebpackPlugin([ new CopyWebpackPlugin([
{ {
@@ -99,48 +153,7 @@ const webpackConfig = merge(baseWebpackConfig, {
ignore: ['.*'] 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) { if (config.build.productionGzip) {
@@ -151,7 +164,9 @@ if (config.build.productionGzip) {
asset: '[path].gz[query]', asset: '[path].gz[query]',
algorithm: 'gzip', algorithm: 'gzip',
test: new RegExp( test: new RegExp(
'\\.(' + config.build.productionGzipExtensions.join('|') + ')$' '\\.(' +
config.build.productionGzipExtensions.join('|') +
')$'
), ),
threshold: 10240, threshold: 10240,
minRatio: 0.8 minRatio: 0.8
@@ -159,28 +174,9 @@ if (config.build.productionGzip) {
) )
} }
if (config.build.generateAnalyzerReport || config.build.bundleAnalyzerReport) { if (config.build.bundleAnalyzerReport) {
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer') const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
.BundleAnalyzerPlugin webpackConfig.plugins.push(new 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 module.exports = webpackConfig

View File

@@ -1,5 +1,5 @@
module.exports = { module.exports = {
NODE_ENV: '"development"', NODE_ENV: '"development"',
ENV_CONFIG: '"dev"', ENV_CONFIG: '"dev"',
BASE_API: '"https://api-dev"' BASE_API: '"https://api-dev"'
} }

View File

@@ -6,16 +6,14 @@ const path = require('path')
module.exports = { module.exports = {
dev: { dev: {
// Paths // Paths
assetsSubDirectory: 'static', assetsSubDirectory: 'static',
assetsPublicPath: '/', assetsPublicPath: '/',
proxyTable: {}, proxyTable: {},
// Various Dev Server settings // 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 port: 9527, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
autoOpenBrowser: true, autoOpenBrowser: true,
errorOverlay: true, errorOverlay: true,
@@ -35,14 +33,19 @@ module.exports = {
*/ */
// https://webpack.js.org/configuration/devtool/#development // https://webpack.js.org/configuration/devtool/#development
devtool: 'cheap-source-map', 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,
// CSS Sourcemaps off by default because relative paths are "buggy" // CSS Sourcemaps off by default because relative paths are "buggy"
// with this option, according to the CSS-Loader README // with this option, according to the CSS-Loader README
// (https://github.com/webpack/css-loader#sourcemaps) // (https://github.com/webpack/css-loader#sourcemaps)
// In our experience, they generally work as expected, // In our experience, they generally work as expected,
// just be aware of this issue when enabling this option. // just be aware of this issue when enabling this option.
cssSourceMap: false cssSourceMap: false,
}, },
build: { build: {
@@ -53,21 +56,16 @@ module.exports = {
assetsRoot: path.resolve(__dirname, '../dist'), assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static', assetsSubDirectory: 'static',
/** // you can set by youself according to actual condition
* You can set by youself according to actual condition assetsPublicPath: './',
* 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: '/',
/** /**
* Source Maps * Source Maps
*/ */
productionSourceMap: false, productionSourceMap: false,
// https://webpack.js.org/configuration/devtool/#production // https://webpack.js.org/configuration/devtool/#production
devtool: 'source-map', devtool: '#source-map',
// Gzip off by default as many popular static hosts such as // Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you. // Surge or Netlify already gzip all static assets for you.
@@ -78,11 +76,8 @@ module.exports = {
// Run the build command with an extra argument to // Run the build command with an extra argument to
// View the bundle analyzer report after build finishes: // View the bundle analyzer report after build finishes:
// `npm run build:prod --report` // `npm run build --report`
// Set to `true` or `false` to always turn it on or off // Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report || false, bundleAnalyzerReport: process.env.npm_config_report
// `npm run build:prod --generate_report`
generateAnalyzerReport: process.env.npm_config_generate_report || false
} }
} }

View File

@@ -1,5 +1,5 @@
module.exports = { module.exports = {
NODE_ENV: '"production"', NODE_ENV: '"production"',
ENV_CONFIG: '"prod"', ENV_CONFIG: '"prod"',
BASE_API: '"https://api-prod"' BASE_API: '"https://api-prod"'
} }

View File

@@ -1,5 +1,5 @@
module.exports = { module.exports = {
NODE_ENV: '"production"', NODE_ENV: '"production"',
ENV_CONFIG: '"sit"', ENV_CONFIG: '"sit"',
BASE_API: '"https://api-sit"' BASE_API: '"https://api-sit"'
} }

View File

@@ -1,15 +1,14 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="renderer" content="webkit"> <meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>vue-element-admin</title> <title>vue-element-admin</title>
</head> </head>
<body> <body>
<script src=<%= htmlWebpackPlugin.options.path %>/tinymce4.7.5/tinymce.min.js></script> <div id="app"></div>
<div id="app"></div> <!-- built files will be auto injected -->
<!-- built files will be auto injected --> </body>
</body>
</html> </html>

View File

@@ -1,124 +1,97 @@
{ {
"name": "vue-element-admin", "name": "vue-element-admin",
"version": "3.8.1", "version": "3.6.1",
"description": "A magical vue admin. Typical templates for enterprise applications. Newest development stack of vue. Lots of awesome features", "description": "A magical vue admin. Typical templates for enterprise applications. Newest development stack of vue. Lots of awesome features",
"author": "Pan <panfree23@gmail.com>", "author": "Pan <panfree23@gmail.com>",
"license": "MIT", "license": "MIT",
"private": true,
"scripts": { "scripts": {
"dev": "cross-env BABEL_ENV=development webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"build:prod": "cross-env NODE_ENV=production env_config=prod node build/build.js", "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", "build:sit": "cross-env NODE_ENV=production env_config=sit node build/build.js",
"lint": "eslint --ext .js,.vue src", "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",
"element-ui",
"admin",
"management-system",
"admin-template"
],
"repository": {
"type": "git",
"url": "git+https://github.com/PanJiaChen/vue-element-admin.git"
},
"bugs": {
"url": "https://github.com/PanJiaChen/vue-element-admin/issues"
}, },
"dependencies": { "dependencies": {
"axios": "0.18.0", "axios": "0.17.1",
"clipboard": "1.7.1", "clipboard": "1.7.1",
"codemirror": "5.39.2", "codemirror": "5.32.0",
"connect": "3.6.6",
"driver.js": "0.5.2",
"dropzone": "5.2.0", "dropzone": "5.2.0",
"echarts": "4.1.0", "echarts": "3.8.5",
"element-ui": "2.4.6", "element-ui": "2.0.8",
"file-saver": "1.3.8", "file-saver": "1.3.3",
"font-awesome": "4.7.0", "font-awesome": "4.7.0",
"js-cookie": "2.2.0", "js-cookie": "2.2.0",
"jsonlint": "1.6.3", "jsonlint": "1.6.2",
"jszip": "3.1.5", "jszip": "3.1.5",
"mockjs": "1.0.1-beta3", "mockjs": "1.0.1-beta3",
"normalize.css": "7.0.0", "normalize.css": "7.0.0",
"nprogress": "0.2.0", "nprogress": "0.2.0",
"screenfull": "3.3.2", "screenfull": "3.3.2",
"showdown": "1.8.6", "showdown": "1.8.5",
"simplemde": "1.11.2", "simplemde": "1.11.2",
"sortablejs": "1.7.0", "sortablejs": "1.7.0",
"vue": "2.5.17", "tinymce": "4.7.6",
"vue": "2.5.10",
"vue-count-to": "1.0.13", "vue-count-to": "1.0.13",
"vue-i18n": "7.3.2", "vue-i18n": "7.3.2",
"vue-multiselect": "2.1.0", "vue-multiselect": "2.0.8",
"vue-router": "3.0.1", "vue-router": "3.0.1",
"vue-splitpane": "1.0.2", "vue-splitpane": "1.0.2",
"vuedraggable": "^2.16.0", "vuedraggable": "2.15.0",
"vuex": "3.0.1", "vuex": "3.0.1",
"xlsx": "^0.11.16" "xlsx": "^0.11.16"
}, },
"devDependencies": { "devDependencies": {
"autoprefixer": "8.5.0", "autoprefixer": "7.2.3",
"babel-core": "6.26.3", "babel-core": "6.26.0",
"babel-eslint": "8.2.6", "babel-eslint": "8.0.3",
"babel-helper-vue-jsx-merge-props": "2.0.3", "babel-helper-vue-jsx-merge-props": "2.0.3",
"babel-loader": "7.1.5", "babel-loader": "7.1.2",
"babel-plugin-dynamic-import-node": "2.0.0",
"babel-plugin-syntax-jsx": "6.18.0", "babel-plugin-syntax-jsx": "6.18.0",
"babel-plugin-transform-runtime": "6.23.0", "babel-plugin-transform-runtime": "6.23.0",
"babel-plugin-transform-vue-jsx": "3.7.0", "babel-plugin-transform-vue-jsx": "3.5.0",
"babel-preset-env": "1.7.0", "babel-preset-env": "1.6.1",
"babel-preset-stage-2": "6.24.1", "babel-preset-stage-2": "6.24.1",
"chalk": "2.4.1", "chalk": "2.3.0",
"copy-webpack-plugin": "4.5.2", "copy-webpack-plugin": "4.3.0",
"cross-env": "5.2.0", "cross-env": "5.1.1",
"css-loader": "1.0.0", "css-loader": "0.28.7",
"eslint": "4.19.1", "eslint": "4.13.1",
"eslint-friendly-formatter": "4.0.1", "eslint-friendly-formatter": "3.0.0",
"eslint-loader": "2.0.0", "eslint-loader": "1.9.0",
"eslint-plugin-vue": "4.7.1", "eslint-plugin-html": "4.0.1",
"file-loader": "1.1.11", "extract-text-webpack-plugin": "3.0.2",
"friendly-errors-webpack-plugin": "1.7.0", "file-loader": "1.1.5",
"hash-sum": "1.0.2", "friendly-errors-webpack-plugin": "1.6.1",
"html-webpack-plugin": "^4.0.0-alpha", "html-webpack-plugin": "2.30.1",
"husky": "0.14.3", "node-notifier": "5.1.2",
"lint-staged": "7.2.2",
"mini-css-extract-plugin": "0.4.1",
"node-notifier": "5.2.1",
"node-sass": "^4.7.2", "node-sass": "^4.7.2",
"optimize-css-assets-webpack-plugin": "5.0.0", "optimize-css-assets-webpack-plugin": "3.2.0",
"ora": "3.0.0", "ora": "1.3.0",
"portfinder": "1.0.13", "portfinder": "1.0.13",
"postcss-import": "11.1.0", "postcss-import": "11.0.0",
"postcss-loader": "2.1.6", "postcss-loader": "2.0.9",
"postcss-url": "7.3.2", "postcss-url": "7.3.0",
"pushstate-server": "3.0.1",
"rimraf": "2.6.2", "rimraf": "2.6.2",
"sass-loader": "7.0.3", "sass-loader": "6.0.6",
"script-ext-html-webpack-plugin": "2.0.1",
"script-loader": "0.7.2", "script-loader": "0.7.2",
"semver": "5.5.0", "semver": "5.4.1",
"serve-static": "1.13.2", "shelljs": "0.7.8",
"shelljs": "0.8.2", "svg-sprite-loader": "3.5.2",
"svg-sprite-loader": "3.8.0", "uglifyjs-webpack-plugin": "1.1.3",
"uglifyjs-webpack-plugin": "1.2.7", "url-loader": "0.6.2",
"url-loader": "1.0.1", "vue-loader": "13.5.0",
"vue-loader": "15.3.0", "vue-style-loader": "3.0.3",
"vue-style-loader": "4.1.2", "vue-template-compiler": "2.5.10",
"vue-template-compiler": "2.5.17", "webpack": "3.10.0",
"webpack": "4.16.5", "webpack-bundle-analyzer": "2.9.1",
"webpack-bundle-analyzer": "2.13.1", "webpack-dev-server": "2.9.7",
"webpack-cli": "3.1.0", "webpack-merge": "4.1.1"
"webpack-dev-server": "3.1.5",
"webpack-merge": "4.1.4"
}, },
"engines": { "engines": {
"node": ">= 6.0.0", "node": ">= 4.0.0",
"npm": ">= 3.0.0" "npm": ">= 3.0.0"
}, },
"browserslist": [ "browserslist": [

View File

@@ -1,11 +1,11 @@
<template> <template>
<div id="app"> <div id="app">
<router-view/> <router-view></router-view>
</div> </div>
</template> </template>
<script> <script>
export default{ export default{
name: 'App' name: 'APP'
} }
</script> </script>

View File

@@ -8,11 +8,10 @@ export function fetchList(query) {
}) })
} }
export function fetchArticle(id) { export function fetchArticle() {
return request({ return request({
url: '/article/detail', url: '/article/detail',
method: 'get', method: 'get'
params: { id }
}) })
} }

View File

@@ -1,10 +1,10 @@
<template> <template>
<transition :name="transitionName"> <transition :name="transitionName">
<div v-show="visible" :style="customStyle" class="back-to-ceiling" @click="backToTop"> <div class="back-to-ceiling" @click="backToTop" v-show="visible" :style="customStyle">
<svg width="16" height="16" viewBox="0 0 17 17" xmlns="http://www.w3.org/2000/svg" class="Icon Icon--backToTopArrow" aria-hidden="true" style="height: 16px; width: 16px;"> <svg width="16" height="16" viewBox="0 0 17 17" xmlns="http://www.w3.org/2000/svg" class="Icon Icon--backToTopArrow" aria-hidden="true" style="height: 16px; width: 16px;">
<title>回到顶部</title> <title>回到顶部</title>
<g> <g>
<path d="M12.036 15.59c0 .55-.453.995-.997.995H5.032c-.55 0-.997-.445-.997-.996V8.584H1.03c-1.1 0-1.36-.633-.578-1.416L7.33.29c.39-.39 1.026-.385 1.412 0l6.878 6.88c.782.78.523 1.415-.58 1.415h-3.004v7.004z" fill-rule="evenodd"/> <path d="M12.036 15.59c0 .55-.453.995-.997.995H5.032c-.55 0-.997-.445-.997-.996V8.584H1.03c-1.1 0-1.36-.633-.578-1.416L7.33.29c.39-.39 1.026-.385 1.412 0l6.878 6.88c.782.78.523 1.415-.58 1.415h-3.004v7.004z" fill-rule="evenodd"></path>
</g> </g>
</svg> </svg>
</div> </div>
@@ -25,16 +25,14 @@ export default {
}, },
customStyle: { customStyle: {
type: Object, type: Object,
default: function() { default: {
return { right: '50px',
right: '50px', bottom: '50px',
bottom: '50px', width: '40px',
width: '40px', height: '40px',
height: '40px', 'border-radius': '4px',
'border-radius': '4px', 'line-height': '45px',
'line-height': '45px', background: '#e7eaf1'
background: '#e7eaf1'
}
} }
}, },
transitionName: { transitionName: {

View File

@@ -1,9 +1,9 @@
<template> <template>
<el-breadcrumb class="app-breadcrumb" separator="/"> <el-breadcrumb class="app-breadcrumb" separator="/">
<transition-group name="breadcrumb"> <transition-group name="breadcrumb">
<el-breadcrumb-item v-for="(item,index) in levelList" v-if="item.meta.title" :key="item.path"> <el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path" v-if='item.meta.title'>
<span v-if="item.redirect===&quot;noredirect&quot;||index==levelList.length-1" class="no-redirect">{{ generateTitle(item.meta.title) }}</span> <span v-if='item.redirect==="noredirect"||index==levelList.length-1' class="no-redirect">{{generateTitle(item.meta.title)}}</span>
<router-link v-else :to="item.redirect||item.path">{{ generateTitle(item.meta.title) }}</router-link> <router-link v-else :to="item.redirect||item.path">{{generateTitle(item.meta.title)}}</router-link>
</el-breadcrumb-item> </el-breadcrumb-item>
</transition-group> </transition-group>
</el-breadcrumb> </el-breadcrumb>
@@ -13,6 +13,9 @@
import { generateTitle } from '@/utils/i18n' import { generateTitle } from '@/utils/i18n'
export default { export default {
created() {
this.getBreadcrumb()
},
data() { data() {
return { return {
levelList: null levelList: null
@@ -23,15 +26,12 @@ export default {
this.getBreadcrumb() this.getBreadcrumb()
} }
}, },
created() {
this.getBreadcrumb()
},
methods: { methods: {
generateTitle, generateTitle,
getBreadcrumb() { getBreadcrumb() {
let matched = this.$route.matched.filter(item => item.name) let matched = this.$route.matched.filter(item => item.name)
const first = matched[0] const first = matched[0]
if (first && first.name.trim().toLocaleLowerCase() !== 'Dashboard'.toLocaleLowerCase()) { if (first && first.name !== 'dashboard') {
matched = [{ path: '/dashboard', meta: { title: 'dashboard' }}].concat(matched) matched = [{ path: '/dashboard', meta: { title: 'dashboard' }}].concat(matched)
} }
this.levelList = matched this.levelList = matched

View File

@@ -1,13 +1,11 @@
<template> <template>
<div :class="className" :id="id" :style="{height:height,width:width}"/> <div :class="className" :id="id" :style="{height:height,width:width}"></div>
</template> </template>
<script> <script>
import echarts from 'echarts' import echarts from 'echarts'
import resize from './mixins/resize'
export default { export default {
mixins: [resize],
props: { props: {
className: { className: {
type: String, type: String,

View File

@@ -1,13 +1,11 @@
<template> <template>
<div :class="className" :id="id" :style="{height:height,width:width}"/> <div :class="className" :id="id" :style="{height:height,width:width}"></div>
</template> </template>
<script> <script>
import echarts from 'echarts' import echarts from 'echarts'
import resize from './mixins/resize'
export default { export default {
mixins: [resize],
props: { props: {
className: { className: {
type: String, type: String,

View File

@@ -1,13 +1,11 @@
<template> <template>
<div :class="className" :id="id" :style="{height:height,width:width}"/> <div :class="className" :id="id" :style="{height:height,width:width}"></div>
</template> </template>
<script> <script>
import echarts from 'echarts' import echarts from 'echarts'
import resize from './mixins/resize'
export default { export default {
mixins: [resize],
props: { props: {
className: { className: {
type: String, type: String,
@@ -33,6 +31,7 @@ export default {
}, },
mounted() { mounted() {
this.initChart() this.initChart()
this.chart = null
}, },
beforeDestroy() { beforeDestroy() {
if (!this.chart) { if (!this.chart) {

View File

@@ -1,15 +0,0 @@
import { debounce } from '@/utils'
export default {
mounted() {
this.__resizeHanlder = debounce(() => {
if (this.chart) {
this.chart.resize()
}
}, 100)
window.addEventListener('resize', this.__resizeHanlder)
},
beforeDestroy() {
window.removeEventListener('resize', this.__resizeHanlder)
}
}

View File

@@ -1,23 +1,23 @@
<template> <template>
<div class="dndList"> <div class="dndList">
<div :style="{width:width1}" class="dndList-list"> <div class="dndList-list" :style="{width:width1}">
<h3>{{ list1Title }}</h3> <h3>{{list1Title}}</h3>
<draggable :list="list1" :options="{group:'article'}" class="dragArea"> <draggable :list="list1" class="dragArea" :options="{group:'article'}">
<div v-for="element in list1" :key="element.id" class="list-complete-item"> <div class="list-complete-item" v-for="element in list1" :key='element.id'>
<div class="list-complete-item-handle">[{{ element.author }}] {{ element.title }}</div> <div class="list-complete-item-handle">[{{element.author}}] {{element.title}}</div>
<div style="position:absolute;right:0px;"> <div style="position:absolute;right:0px;">
<span style="float: right ;margin-top: -20px;margin-right:5px;" @click="deleteEle(element)"> <span style="float: right ;margin-top: -20px;margin-right:5px;" @click="deleteEle(element)">
<i style="color:#ff4949" class="el-icon-delete"/> <i style="color:#ff4949" class="el-icon-delete"></i>
</span> </span>
</div> </div>
</div> </div>
</draggable> </draggable>
</div> </div>
<div :style="{width:width2}" class="dndList-list"> <div class="dndList-list" :style="{width:width2}">
<h3>{{ list2Title }}</h3> <h3>{{list2Title}}</h3>
<draggable :list="filterList2" :options="{group:'article'}" class="dragArea"> <draggable :list="filterList2" class="dragArea" :options="{group:'article'}">
<div v-for="element in filterList2" :key="element.id" class="list-complete-item"> <div class="list-complete-item" v-for="element in filterList2" :key='element.id'>
<div class="list-complete-item-handle2" @click="pushEle(element)"> [{{ element.author }}] {{ element.title }}</div> <div class='list-complete-item-handle2' @click="pushEle(element)"> [{{element.author}}] {{element.title}}</div>
</div> </div>
</draggable> </draggable>
</div> </div>
@@ -30,6 +30,16 @@ import draggable from 'vuedraggable'
export default { export default {
name: 'DndList', name: 'DndList',
components: { draggable }, components: { draggable },
computed: {
filterList2() {
return this.list2.filter(v => {
if (this.isNotInList1(v)) {
return v
}
return false
})
}
},
props: { props: {
list1: { list1: {
type: Array, type: Array,
@@ -60,16 +70,6 @@ export default {
default: '48%' default: '48%'
} }
}, },
computed: {
filterList2() {
return this.list2.filter(v => {
if (this.isNotInList1(v)) {
return v
}
return false
})
}
},
methods: { methods: {
isNotInList1(v) { isNotInList1(v) {
return this.list1.every(k => v.id !== k.id) return this.list1.every(k => v.id !== k.id)

View File

@@ -1,5 +1,5 @@
<template> <template>
<div :ref="id" :action="url" :id="id" class="dropzone"> <div :ref="id" :action="url" class="dropzone" :id="id">
<input type="file" name="file"> <input type="file" name="file">
</div> </div>
</template> </template>
@@ -12,81 +12,12 @@ import 'dropzone/dist/dropzone.css'
Dropzone.autoDiscover = false Dropzone.autoDiscover = false
export default { export default {
props: {
id: {
type: String,
required: true
},
url: {
type: String,
required: true
},
clickable: {
type: Boolean,
default: true
},
defaultMsg: {
type: String,
default: '上传图片'
},
acceptedFiles: {
type: String,
default: ''
},
thumbnailHeight: {
type: Number,
default: 200
},
thumbnailWidth: {
type: Number,
default: 200
},
showRemoveLink: {
type: Boolean,
default: true
},
maxFilesize: {
type: Number,
default: 2
},
maxFiles: {
type: Number,
default: 3
},
autoProcessQueue: {
type: Boolean,
default: true
},
useCustomDropzoneOptions: {
type: Boolean,
default: false
},
defaultImg: {
default: '',
type: [String, Array]
},
couldPaste: {
type: Boolean,
default: false
}
},
data() { data() {
return { return {
dropzone: '', dropzone: '',
initOnce: true initOnce: true
} }
}, },
watch: {
defaultImg(val) {
if (val.length === 0) {
this.initOnce = false
return
}
if (!this.initOnce) return
this.initImages(val)
this.initOnce = false
}
},
mounted() { mounted() {
const element = document.getElementById(this.id) const element = document.getElementById(this.id)
const vm = this const vm = this
@@ -164,10 +95,6 @@ export default {
vm.$emit('dropzone-successmultiple', file, error, xhr) vm.$emit('dropzone-successmultiple', file, error, xhr)
}) })
}, },
destroyed() {
document.removeEventListener('paste', this.pasteImg)
this.dropzone.destroy()
},
methods: { methods: {
removeAllFiles() { removeAllFiles() {
this.dropzone.removeAllFiles(true) this.dropzone.removeAllFiles(true)
@@ -201,6 +128,76 @@ export default {
} }
} }
},
destroyed() {
document.removeEventListener('paste', this.pasteImg)
this.dropzone.destroy()
},
watch: {
defaultImg(val) {
if (val.length === 0) {
this.initOnce = false
return
}
if (!this.initOnce) return
this.initImages(val)
this.initOnce = false
}
},
props: {
id: {
type: String,
required: true
},
url: {
type: String,
required: true
},
clickable: {
type: Boolean,
default: true
},
defaultMsg: {
type: String,
default: '上传图片'
},
acceptedFiles: {
type: String
},
thumbnailHeight: {
type: Number,
default: 200
},
thumbnailWidth: {
type: Number,
default: 200
},
showRemoveLink: {
type: Boolean,
default: true
},
maxFilesize: {
type: Number,
default: 2
},
maxFiles: {
type: Number,
default: 3
},
autoProcessQueue: {
type: Boolean,
default: true
},
useCustomDropzoneOptions: {
type: Boolean,
default: false
},
defaultImg: {
default: false
},
couldPaste: {
default: false
}
} }
} }
</script> </script>

View File

@@ -2,24 +2,15 @@
<div v-if="errorLogs.length>0"> <div v-if="errorLogs.length>0">
<el-badge :is-dot="true" style="line-height: 30px;" @click.native="dialogTableVisible=true"> <el-badge :is-dot="true" style="line-height: 30px;" @click.native="dialogTableVisible=true">
<el-button size="small" type="danger" class="bug-btn"> <el-button size="small" type="danger" class="bug-btn">
<svg <svg t="1492682037685" class="bug-svg" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1863"
t="1492682037685" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128">
class="bug-svg" <path d="M969.142857 548.571429q0 14.848-10.861714 25.709714t-25.709714 10.861714l-128 0q0 97.718857-38.290286 165.705143l118.857143 119.442286q10.861714 10.861714 10.861714 25.709714t-10.861714 25.709714q-10.276571 10.861714-25.709714 10.861714t-25.709714-10.861714l-113.152-112.566857q-2.852571 2.852571-8.557714 7.424t-23.990857 16.274286-37.156571 20.845714-46.848 16.566857-55.442286 7.424l0-512-73.142857 0 0 512q-29.147429 0-58.002286-7.716571t-49.700571-18.870857-37.705143-22.272-24.868571-18.578286l-8.557714-8.009143-104.557714 118.272q-11.446857 11.995429-27.428571 11.995429-13.714286 0-24.576-9.142857-10.861714-10.276571-11.702857-25.417143t8.850286-26.587429l115.419429-129.718857q-33.133714-65.133714-33.133714-156.562286l-128 0q-14.848 0-25.709714-10.861714t-10.861714-25.709714 10.861714-25.709714 25.709714-10.861714l128 0 0-168.009143-98.852571-98.852571q-10.861714-10.861714-10.861714-25.709714t10.861714-25.709714 25.709714-10.861714 25.709714 10.861714l98.852571 98.852571 482.304 0 98.852571-98.852571q10.861714-10.861714 25.709714-10.861714t25.709714 10.861714 10.861714 25.709714-10.861714 25.709714l-98.852571 98.852571 0 168.009143 128 0q14.848 0 25.709714 10.861714t10.861714 25.709714zM694.857143 219.428571l-365.714286 0q0-75.995429 53.430857-129.426286t129.426286-53.430857 129.426286 53.430857 53.430857 129.426286z"
viewBox="0 0 1024 1024" p-id="1864"></path>
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="1863"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="128"
height="128">
<path
d="M969.142857 548.571429q0 14.848-10.861714 25.709714t-25.709714 10.861714l-128 0q0 97.718857-38.290286 165.705143l118.857143 119.442286q10.861714 10.861714 10.861714 25.709714t-10.861714 25.709714q-10.276571 10.861714-25.709714 10.861714t-25.709714-10.861714l-113.152-112.566857q-2.852571 2.852571-8.557714 7.424t-23.990857 16.274286-37.156571 20.845714-46.848 16.566857-55.442286 7.424l0-512-73.142857 0 0 512q-29.147429 0-58.002286-7.716571t-49.700571-18.870857-37.705143-22.272-24.868571-18.578286l-8.557714-8.009143-104.557714 118.272q-11.446857 11.995429-27.428571 11.995429-13.714286 0-24.576-9.142857-10.861714-10.276571-11.702857-25.417143t8.850286-26.587429l115.419429-129.718857q-33.133714-65.133714-33.133714-156.562286l-128 0q-14.848 0-25.709714-10.861714t-10.861714-25.709714 10.861714-25.709714 25.709714-10.861714l128 0 0-168.009143-98.852571-98.852571q-10.861714-10.861714-10.861714-25.709714t10.861714-25.709714 25.709714-10.861714 25.709714 10.861714l98.852571 98.852571 482.304 0 98.852571-98.852571q10.861714-10.861714 25.709714-10.861714t25.709714 10.861714 10.861714 25.709714-10.861714 25.709714l-98.852571 98.852571 0 168.009143 128 0q14.848 0 25.709714 10.861714t10.861714 25.709714zM694.857143 219.428571l-365.714286 0q0-75.995429 53.430857-129.426286t129.426286-53.430857 129.426286 53.430857 53.430857 129.426286z"
p-id="1864"/>
</svg> </svg>
</el-button> </el-button>
</el-badge> </el-badge>
<el-dialog :visible.sync="dialogTableVisible" title="Error Log" width="80%"> <el-dialog title="Error Log" :visible.sync="dialogTableVisible" width="80%">
<el-table :data="errorLogs" border> <el-table :data="errorLogs" border>
<el-table-column label="Message"> <el-table-column label="Message">
<template slot-scope="scope"> <template slot-scope="scope">
@@ -27,21 +18,21 @@
<span class="message-title">Msg:</span> <span class="message-title">Msg:</span>
<el-tag type="danger">{{ scope.row.err.message }}</el-tag> <el-tag type="danger">{{ scope.row.err.message }}</el-tag>
</div> </div>
<br> <br/>
<div> <div>
<span class="message-title" style="padding-right: 10px;">Info: </span> <span class="message-title" style="padding-right: 10px;">Info: </span>
<el-tag type="warning">{{ scope.row.vm.$vnode.tag }} error in {{ scope.row.info }}</el-tag> <el-tag type="warning">{{scope.row.vm.$vnode.tag}} error in {{scope.row.info}}</el-tag>
</div> </div>
<br> <br/>
<div> <div>
<span class="message-title" style="padding-right: 16px;">Url: </span> <span class="message-title" style="padding-right: 16px;">Url: </span>
<el-tag type="success">{{ scope.row.url }}</el-tag> <el-tag type="success">{{scope.row.url}}</el-tag>
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="Stack"> <el-table-column label="Stack">
<template slot-scope="scope"> <template slot-scope="scope">
{{ scope.row.err.stack }} {{ scope.row.err.stack}}
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@@ -52,7 +43,7 @@
<script> <script>
export default { export default {
name: 'ErrorLog', name: 'errorLog',
data() { data() {
return { return {
dialogTableVisible: false dialogTableVisible: false

View File

@@ -1,21 +1,12 @@
<template> <template>
<a href="https://github.com/PanJiaChen/vue-element-admin" target="_blank" class="github-corner" aria-label="View source on Github"> <a href="https://github.com/PanJiaChen/vue-element-admin" target="_blank" class="github-corner" aria-label="View source on Github">
<svg <svg width="80" height="80" viewBox="0 0 250 250" style="fill:#40c9c6; color:#fff; position: absolute; top: 84px; border: 0; right: 0;"
width="80"
height="80"
viewBox="0 0 250 250"
style="fill:#40c9c6; color:#fff;"
aria-hidden="true"> aria-hidden="true">
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"/> <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
<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"
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" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
fill="currentColor" <path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
style="transform-origin: 130px 106px;" fill="currentColor" class="octo-body"></path>
class="octo-arm"/>
<path
d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
fill="currentColor"
class="octo-body"/>
</svg> </svg>
</a> </a>
</template> </template>

View File

@@ -1,34 +1,20 @@
<template> <template>
<div> <div>
<svg <svg t="1492500959545" @click="toggleClick" class="wscn-icon hamburger" :class="{'is-active':isActive}" style="" viewBox="0 0 1024 1024"
:class="{'is-active':isActive}" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1691" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64">
t="1492500959545" <path d="M966.8023 568.849776 57.196677 568.849776c-31.397081 0-56.850799-25.452695-56.850799-56.850799l0 0c0-31.397081 25.452695-56.849776 56.850799-56.849776l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.849776l0 0C1023.653099 543.397081 998.200404 568.849776 966.8023 568.849776z"
class="hamburger" p-id="1692"></path>
style="" <path d="M966.8023 881.527125 57.196677 881.527125c-31.397081 0-56.850799-25.452695-56.850799-56.849776l0 0c0-31.397081 25.452695-56.849776 56.850799-56.849776l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.849776l0 0C1023.653099 856.07443 998.200404 881.527125 966.8023 881.527125z"
viewBox="0 0 1024 1024" p-id="1693"></path>
version="1.1" <path d="M966.8023 256.17345 57.196677 256.17345c-31.397081 0-56.850799-25.452695-56.850799-56.849776l0 0c0-31.397081 25.452695-56.850799 56.850799-56.850799l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.850799l0 0C1023.653099 230.720755 998.200404 256.17345 966.8023 256.17345z"
xmlns="http://www.w3.org/2000/svg" p-id="1694"></path>
p-id="1691"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="64"
height="64"
@click="toggleClick">
<path
d="M966.8023 568.849776 57.196677 568.849776c-31.397081 0-56.850799-25.452695-56.850799-56.850799l0 0c0-31.397081 25.452695-56.849776 56.850799-56.849776l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.849776l0 0C1023.653099 543.397081 998.200404 568.849776 966.8023 568.849776z"
p-id="1692"/>
<path
d="M966.8023 881.527125 57.196677 881.527125c-31.397081 0-56.850799-25.452695-56.850799-56.849776l0 0c0-31.397081 25.452695-56.849776 56.850799-56.849776l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.849776l0 0C1023.653099 856.07443 998.200404 881.527125 966.8023 881.527125z"
p-id="1693"/>
<path
d="M966.8023 256.17345 57.196677 256.17345c-31.397081 0-56.850799-25.452695-56.850799-56.849776l0 0c0-31.397081 25.452695-56.850799 56.850799-56.850799l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.850799l0 0C1023.653099 230.720755 998.200404 256.17345 966.8023 256.17345z"
p-id="1694"/>
</svg> </svg>
</div> </div>
</template> </template>
<script> <script>
export default { export default {
name: 'Hamburger', name: 'hamburger',
props: { props: {
isActive: { isActive: {
type: Boolean, type: Boolean,
@@ -44,16 +30,16 @@ export default {
<style scoped> <style scoped>
.hamburger { .hamburger {
display: inline-block; display: inline-block;
cursor: pointer; cursor: pointer;
width: 20px; width: 20px;
height: 20px; height: 20px;
transform: rotate(90deg); transform: rotate(90deg);
transition: .38s; transition: .38s;
transform-origin: 50% 50%; transform-origin: 50% 50%;
} }
.hamburger.is-active { .hamburger.is-active {
transform: rotate(0deg); transform: rotate(0deg);
} }
</style> </style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,41 @@
const langBag = {
zh: {
hint: '点击,或拖动图片至此处',
loading: '正在上传……',
noSupported: '浏览器不支持该功能请使用IE10以上或其他现在浏览器',
success: '上传成功',
fail: '图片上传失败',
preview: '头像预览',
btn: {
off: '取消',
close: '关闭',
back: '上一步',
save: '保存'
},
error: {
onlyImg: '仅限图片格式',
outOfSize: '单文件大小不能超过 ',
lowestPx: '图片最低像素为(宽*高):'
}
},
en: {
hint: 'Click, or drag the file here',
loading: 'Uploading……',
noSupported: 'Browser does not support, please use IE10+ or other browsers',
success: 'Upload success',
fail: 'Upload failed',
preview: 'Preview',
btn: {
off: 'Cancel',
close: 'Close',
back: 'Back',
save: 'Save'
},
error: {
onlyImg: 'Image only',
outOfSize: 'Image exceeds size limit: ',
lowestPx: 'The lowest pixel in the image: '
}
}
}
export default langBag

View File

@@ -0,0 +1,691 @@
@charset "UTF-8";
@-webkit-keyframes vicp_progress {
0% {
background-position-y: 0;
}
100% {
background-position-y: 40px;
}
}
@keyframes vicp_progress {
0% {
background-position-y: 0;
}
100% {
background-position-y: 40px;
}
}
@-webkit-keyframes vicp {
0% {
opacity: 0;
-webkit-transform: scale(0) translatey(-60px);
transform: scale(0) translatey(-60px);
}
100% {
opacity: 1;
-webkit-transform: scale(1) translatey(0);
transform: scale(1) translatey(0);
}
}
@keyframes vicp {
0% {
opacity: 0;
-webkit-transform: scale(0) translatey(-60px);
transform: scale(0) translatey(-60px);
}
100% {
opacity: 1;
-webkit-transform: scale(1) translatey(0);
transform: scale(1) translatey(0);
}
}
.vue-image-crop-upload {
position: fixed;
display: block;
-webkit-box-sizing: border-box;
box-sizing: border-box;
z-index: 10000;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.65);
-webkit-tap-highlight-color: transparent;
-moz-tap-highlight-color: transparent;
}
.vue-image-crop-upload .vicp-wrap {
-webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
position: fixed;
display: block;
-webkit-box-sizing: border-box;
box-sizing: border-box;
z-index: 10000;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
width: 600px;
height: 330px;
padding: 25px;
background-color: #fff;
border-radius: 2px;
-webkit-animation: vicp 0.12s ease-in;
animation: vicp 0.12s ease-in;
}
.vue-image-crop-upload .vicp-wrap .vicp-close {
position: absolute;
right: -30px;
top: -30px;
}
.vue-image-crop-upload .vicp-wrap .vicp-close .vicp-icon4 {
position: relative;
display: block;
width: 30px;
height: 30px;
cursor: pointer;
-webkit-transition: -webkit-transform 0.18s;
transition: -webkit-transform 0.18s;
transition: transform 0.18s;
transition: transform 0.18s, -webkit-transform 0.18s;
-webkit-transform: rotate(0);
-ms-transform: rotate(0);
transform: rotate(0);
}
.vue-image-crop-upload .vicp-wrap .vicp-close .vicp-icon4::after, .vue-image-crop-upload .vicp-wrap .vicp-close .vicp-icon4::before {
-webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
content: '';
position: absolute;
top: 12px;
left: 4px;
width: 20px;
height: 3px;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
background-color: #fff;
}
.vue-image-crop-upload .vicp-wrap .vicp-close .vicp-icon4::after {
-webkit-transform: rotate(-45deg);
-ms-transform: rotate(-45deg);
transform: rotate(-45deg);
}
.vue-image-crop-upload .vicp-wrap .vicp-close .vicp-icon4:hover {
-webkit-transform: rotate(90deg);
-ms-transform: rotate(90deg);
transform: rotate(90deg);
}
.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area {
position: relative;
padding: 35px;
height: 200px;
background-color: rgba(0, 0, 0, 0.03);
text-align: center;
border: 1px dashed rgba(0, 0, 0, 0.08);
overflow: hidden;
}
.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-icon1 {
display: block;
margin: 0 auto 6px;
width: 42px;
height: 42px;
overflow: hidden;
}
.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-icon1 .vicp-icon1-arrow {
display: block;
margin: 0 auto;
width: 0;
height: 0;
border-bottom: 14.7px solid rgba(0, 0, 0, 0.3);
border-left: 14.7px solid transparent;
border-right: 14.7px solid transparent;
}
.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-icon1 .vicp-icon1-body {
display: block;
width: 12.6px;
height: 14.7px;
margin: 0 auto;
background-color: rgba(0, 0, 0, 0.3);
}
.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-icon1 .vicp-icon1-bottom {
-webkit-box-sizing: border-box;
box-sizing: border-box;
display: block;
height: 12.6px;
border: 6px solid rgba(0, 0, 0, 0.3);
border-top: none;
}
.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-hint {
display: block;
padding: 15px;
font-size: 14px;
color: #666;
line-height: 30px;
}
.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-no-supported-hint {
display: block;
position: absolute;
top: 0;
left: 0;
padding: 30px;
width: 100%;
height: 60px;
line-height: 30px;
background-color: #eee;
text-align: center;
color: #666;
font-size: 14px;
}
.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area:hover {
cursor: pointer;
border-color: rgba(0, 0, 0, 0.1);
background-color: rgba(0, 0, 0, 0.05);
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop {
overflow: hidden;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left {
float: left;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-img-container {
position: relative;
display: block;
width: 240px;
height: 180px;
background-color: #e5e5e0;
overflow: hidden;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-img-container .vicp-img {
position: absolute;
display: block;
cursor: move;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-img-container .vicp-img-shade {
-webkit-box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18);
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18);
position: absolute;
background-color: rgba(241, 242, 243, 0.8);
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-img-container .vicp-img-shade.vicp-img-shade-1 {
top: 0;
left: 0;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-img-container .vicp-img-shade.vicp-img-shade-2 {
bottom: 0;
right: 0;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range {
position: relative;
margin: 30px 0;
width: 240px;
height: 18px;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon5,
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon6 {
position: absolute;
top: 0;
width: 18px;
height: 18px;
border-radius: 100%;
background-color: rgba(0, 0, 0, 0.08);
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon5:hover,
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon6:hover {
-webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12);
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12);
cursor: pointer;
background-color: rgba(0, 0, 0, 0.14);
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon5 {
left: 0;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon5::before {
position: absolute;
content: '';
display: block;
left: 3px;
top: 8px;
width: 12px;
height: 2px;
background-color: #fff;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon6 {
right: 0;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon6::before {
position: absolute;
content: '';
display: block;
left: 3px;
top: 8px;
width: 12px;
height: 2px;
background-color: #fff;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon6::after {
position: absolute;
content: '';
display: block;
top: 3px;
left: 8px;
width: 2px;
height: 12px;
background-color: #fff;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range] {
display: block;
padding-top: 5px;
margin: 0 auto;
width: 180px;
height: 8px;
vertical-align: top;
background: transparent;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
cursor: pointer;
/* 滑块
---------------------------------------------------------------*/
/* 轨道
---------------------------------------------------------------*/
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:focus {
outline: none;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-webkit-slider-thumb {
-webkit-box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18);
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18);
-webkit-appearance: none;
appearance: none;
margin-top: -3px;
width: 12px;
height: 12px;
background-color: #61c091;
border-radius: 100%;
border: none;
-webkit-transition: 0.2s;
transition: 0.2s;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-moz-range-thumb {
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18);
-moz-appearance: none;
appearance: none;
width: 12px;
height: 12px;
background-color: #61c091;
border-radius: 100%;
border: none;
-webkit-transition: 0.2s;
transition: 0.2s;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-ms-thumb {
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18);
appearance: none;
width: 12px;
height: 12px;
background-color: #61c091;
border: none;
border-radius: 100%;
-webkit-transition: 0.2s;
transition: 0.2s;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:active::-moz-range-thumb {
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
width: 14px;
height: 14px;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:active::-ms-thumb {
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
width: 14px;
height: 14px;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:active::-webkit-slider-thumb {
-webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
margin-top: -4px;
width: 14px;
height: 14px;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-webkit-slider-runnable-track {
-webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12);
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12);
width: 100%;
height: 6px;
cursor: pointer;
border-radius: 2px;
border: none;
background-color: rgba(68, 170, 119, 0.3);
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-moz-range-track {
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12);
width: 100%;
height: 6px;
cursor: pointer;
border-radius: 2px;
border: none;
background-color: rgba(68, 170, 119, 0.3);
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-ms-track {
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12);
width: 100%;
cursor: pointer;
background: transparent;
border-color: transparent;
color: transparent;
height: 6px;
border-radius: 2px;
border: none;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-ms-fill-lower {
background-color: rgba(68, 170, 119, 0.3);
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-ms-fill-upper {
background-color: rgba(68, 170, 119, 0.15);
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:focus::-webkit-slider-runnable-track {
background-color: rgba(68, 170, 119, 0.5);
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:focus::-moz-range-track {
background-color: rgba(68, 170, 119, 0.5);
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:focus::-ms-fill-lower {
background-color: rgba(68, 170, 119, 0.45);
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:focus::-ms-fill-upper {
background-color: rgba(68, 170, 119, 0.25);
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right {
float: right;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right .vicp-preview {
height: 150px;
overflow: hidden;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right .vicp-preview .vicp-preview-item {
position: relative;
padding: 5px;
width: 100px;
height: 100px;
float: left;
margin-right: 16px;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right .vicp-preview .vicp-preview-item span {
position: absolute;
bottom: -30px;
width: 100%;
font-size: 14px;
color: #bbb;
display: block;
text-align: center;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right .vicp-preview .vicp-preview-item img {
position: absolute;
display: block;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
padding: 3px;
background-color: #fff;
border: 1px solid rgba(0, 0, 0, 0.15);
overflow: hidden;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right .vicp-preview .vicp-preview-item:last-child {
margin-right: 0;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right .vicp-preview .vicp-preview-item:last-child img {
border-radius: 100%;
}
.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload {
position: relative;
padding: 35px;
height: 200px;
background-color: rgba(0, 0, 0, 0.03);
text-align: center;
border: 1px dashed #ddd;
}
.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-loading {
display: block;
padding: 15px;
font-size: 16px;
color: #999;
line-height: 30px;
}
.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-progress-wrap {
margin-top: 12px;
background-color: rgba(0, 0, 0, 0.08);
border-radius: 3px;
}
.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-progress-wrap .vicp-progress {
position: relative;
display: block;
height: 5px;
border-radius: 3px;
background-color: #4a7;
-webkit-box-shadow: 0 2px 6px 0 rgba(68, 170, 119, 0.3);
box-shadow: 0 2px 6px 0 rgba(68, 170, 119, 0.3);
-webkit-transition: width 0.15s linear;
transition: width 0.15s linear;
background-image: -webkit-linear-gradient(135deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);
background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);
background-size: 40px 40px;
-webkit-animation: vicp_progress 0.5s linear infinite;
animation: vicp_progress 0.5s linear infinite;
}
.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-progress-wrap .vicp-progress::after {
content: '';
position: absolute;
display: block;
top: -3px;
right: -3px;
width: 9px;
height: 9px;
border: 1px solid rgba(245, 246, 247, 0.7);
-webkit-box-shadow: 0 1px 4px 0 rgba(68, 170, 119, 0.7);
box-shadow: 0 1px 4px 0 rgba(68, 170, 119, 0.7);
border-radius: 100%;
background-color: #4a7;
}
.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-error,
.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-success {
height: 100px;
line-height: 100px;
}
.vue-image-crop-upload .vicp-wrap .vicp-operate {
position: absolute;
right: 20px;
bottom: 20px;
}
.vue-image-crop-upload .vicp-wrap .vicp-operate a {
position: relative;
float: left;
display: block;
margin-left: 10px;
width: 100px;
height: 36px;
line-height: 36px;
text-align: center;
cursor: pointer;
font-size: 14px;
color: #4a7;
border-radius: 2px;
overflow: hidden;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.vue-image-crop-upload .vicp-wrap .vicp-operate a:hover {
background-color: rgba(0, 0, 0, 0.03);
}
.vue-image-crop-upload .vicp-wrap .vicp-error,
.vue-image-crop-upload .vicp-wrap .vicp-success {
display: block;
font-size: 14px;
line-height: 24px;
height: 24px;
color: #d10;
text-align: center;
vertical-align: top;
}
.vue-image-crop-upload .vicp-wrap .vicp-success {
color: #4a7;
}
.vue-image-crop-upload .vicp-wrap .vicp-icon3 {
position: relative;
display: inline-block;
width: 20px;
height: 20px;
top: 4px;
}
.vue-image-crop-upload .vicp-wrap .vicp-icon3::after {
position: absolute;
top: 3px;
left: 6px;
width: 6px;
height: 10px;
border-width: 0 2px 2px 0;
border-color: #4a7;
border-style: solid;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
content: '';
}
.vue-image-crop-upload .vicp-wrap .vicp-icon2 {
position: relative;
display: inline-block;
width: 20px;
height: 20px;
top: 4px;
}
.vue-image-crop-upload .vicp-wrap .vicp-icon2::after, .vue-image-crop-upload .vicp-wrap .vicp-icon2::before {
content: '';
position: absolute;
top: 9px;
left: 4px;
width: 13px;
height: 2px;
background-color: #d10;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}
.vue-image-crop-upload .vicp-wrap .vicp-icon2::after {
-webkit-transform: rotate(-45deg);
-ms-transform: rotate(-45deg);
transform: rotate(-45deg);
}
.e-ripple {
position: absolute;
border-radius: 100%;
background-color: rgba(0, 0, 0, 0.15);
background-clip: padding-box;
pointer-events: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-transform: scale(0);
-ms-transform: scale(0);
transform: scale(0);
opacity: 1;
}
.e-ripple.z-active {
opacity: 0;
-webkit-transform: scale(2);
-ms-transform: scale(2);
transform: scale(2);
-webkit-transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out;
transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out;
transition: opacity 1.2s ease-out, transform 0.6s ease-out;
transition: opacity 1.2s ease-out, transform 0.6s ease-out, -webkit-transform 0.6s ease-out;
}

View File

@@ -0,0 +1,58 @@
/* eslint-disable */
/**
*
* @param e
* @param arg_opts
* @returns {boolean}
*/
export function effectRipple(e, arg_opts) {
let opts = Object.assign({
ele: e.target, // 波纹作用元素
type: 'hit', // hit点击位置扩散 center中心点扩展
bgc: 'rgba(0, 0, 0, 0.15)' // 波纹颜色
}, arg_opts),
target = opts.ele;
if (target) {
let rect = target.getBoundingClientRect(),
ripple = target.querySelector('.e-ripple');
if (!ripple) {
ripple = document.createElement('span');
ripple.className = 'e-ripple';
ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px';
target.appendChild(ripple);
} else {
ripple.className = 'e-ripple';
}
switch (opts.type) {
case 'center':
ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px';
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.backgroundColor = opts.bgc;
ripple.className = 'e-ripple z-active';
return false;
}
}
// database64文件格式转换为2进制
/**
*
* @param data
* @param mime
* @returns {*}
*/
export function data2blob(data, mime) {
// dataURL 的格式为 “data:image/png;base64,****”,逗号之前都是一些说明性的文字,我们只需要逗号之后的就行了
data = data.split(',')[1];
data = window.atob(data);
var ia = new Uint8Array(data.length);
for (var i = 0; i < data.length; i++) {
ia[i] = data.charCodeAt(i);
}
// canvas.toDataURL 返回的默认格式就是 image/png
return new Blob([ia], {type: mime});
};

View File

@@ -1,19 +0,0 @@
/**
* database64文件格式转换为2进制
*
* @param {[String]} data dataURL 的格式为 “data:image/png;base64,****”,逗号之前都是一些说明性的文字,我们只需要逗号之后的就行了
* @param {[String]} mime [description]
* @return {[blob]} [description]
*/
export default function(data, mime) {
data = data.split(',')[1]
data = window.atob(data)
var ia = new Uint8Array(data.length)
for (var i = 0; i < data.length; i++) {
ia[i] = data.charCodeAt(i)
}
// canvas.toDataURL 返回的默认格式就是 image/png
return new Blob([ia], {
type: mime
})
}

View File

@@ -1,39 +0,0 @@
/**
* 点击波纹效果
*
* @param {[event]} e [description]
* @param {[Object]} arg_opts [description]
* @return {[bollean]} [description]
*/
export default function(e, arg_opts) {
var opts = Object.assign({
ele: e.target, // 波纹作用元素
type: 'hit', // hit点击位置扩散center中心点扩展
bgc: 'rgba(0, 0, 0, 0.15)' // 波纹颜色
}, arg_opts)
var target = opts.ele
if (target) {
var rect = target.getBoundingClientRect()
var ripple = target.querySelector('.e-ripple')
if (!ripple) {
ripple = document.createElement('span')
ripple.className = 'e-ripple'
ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px'
target.appendChild(ripple)
} else {
ripple.className = 'e-ripple'
}
switch (opts.type) {
case 'center':
ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px'
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.backgroundColor = opts.bgc
ripple.className = 'e-ripple z-active'
return false
}
}

View File

@@ -1,232 +0,0 @@
export default {
zh: {
hint: '点击,或拖动图片至此处',
loading: '正在上传……',
noSupported: '浏览器不支持该功能请使用IE10以上或其他现在浏览器',
success: '上传成功',
fail: '图片上传失败',
preview: '头像预览',
btn: {
off: '取消',
close: '关闭',
back: '上一步',
save: '保存'
},
error: {
onlyImg: '仅限图片格式',
outOfSize: '单文件大小不能超过 ',
lowestPx: '图片最低像素为(宽*高):'
}
},
'zh-tw': {
hint: '點擊,或拖動圖片至此處',
loading: '正在上傳……',
noSupported: '瀏覽器不支持該功能請使用IE10以上或其他現代瀏覽器',
success: '上傳成功',
fail: '圖片上傳失敗',
preview: '頭像預覽',
btn: {
off: '取消',
close: '關閉',
back: '上一步',
save: '保存'
},
error: {
onlyImg: '僅限圖片格式',
outOfSize: '單文件大小不能超過 ',
lowestPx: '圖片最低像素為(寬*高):'
}
},
en: {
hint: 'Click or drag the file here to upload',
loading: 'Uploading…',
noSupported: 'Browser is not supported, please use IE10+ or other browsers',
success: 'Upload success',
fail: 'Upload failed',
preview: 'Preview',
btn: {
off: 'Cancel',
close: 'Close',
back: 'Back',
save: 'Save'
},
error: {
onlyImg: 'Image only',
outOfSize: 'Image exceeds size limit: ',
lowestPx: 'Image\'s size is too low. Expected at least: '
}
},
ro: {
hint: 'Atinge sau trage fișierul aici',
loading: 'Se încarcă',
noSupported: 'Browser-ul tău nu suportă acest feature. Te rugăm încearcă cu alt browser.',
success: 'S-a încărcat cu succes',
fail: 'A apărut o problemă la încărcare',
preview: 'Previzualizează',
btn: {
off: 'Anulează',
close: 'Închide',
back: 'Înapoi',
save: 'Salvează'
},
error: {
onlyImg: 'Doar imagini',
outOfSize: 'Imaginea depășește limita de: ',
loewstPx: 'Imaginea este prea mică; Minim: '
}
},
ru: {
hint: 'Нажмите, или перетащите файл в это окно',
loading: 'Загружаю……',
noSupported: 'Ваш браузер не поддерживается, пожалуйста, используйте IE10 + или другие браузеры',
success: 'Загрузка выполнена успешно',
fail: 'Ошибка загрузки',
preview: 'Предпросмотр',
btn: {
off: 'Отменить',
close: 'Закрыть',
back: 'Назад',
save: 'Сохранить'
},
error: {
onlyImg: 'Только изображения',
outOfSize: 'Изображение превышает предельный размер: ',
lowestPx: 'Минимальный размер изображения: '
}
},
'pt-br': {
hint: 'Clique ou arraste o arquivo aqui para carregar',
loading: 'Carregando…',
noSupported: 'Browser não suportado, use o IE10+ ou outro browser',
success: 'Sucesso ao carregar imagem',
fail: 'Falha ao carregar imagem',
preview: 'Pré-visualizar',
btn: {
off: 'Cancelar',
close: 'Fechar',
back: 'Voltar',
save: 'Salvar'
},
error: {
onlyImg: 'Apenas imagens',
outOfSize: 'A imagem excede o limite de tamanho: ',
lowestPx: 'O tamanho da imagem é muito pequeno. Tamanho mínimo: '
}
},
fr: {
hint: 'Cliquez ou glissez le fichier ici.',
loading: 'Téléchargement…',
noSupported: 'Votre navigateur n\'est pas supporté. Utilisez IE10 + ou un autre navigateur s\'il vous plaît.',
success: 'Téléchargement réussit',
fail: 'Téléchargement echoué',
preview: 'Aperçu',
btn: {
off: 'Annuler',
close: 'Fermer',
back: 'Retour',
save: 'Enregistrer'
},
error: {
onlyImg: 'Image uniquement',
outOfSize: 'L\'image sélectionnée dépasse la taille maximum: ',
lowestPx: 'L\'image sélectionnée est trop petite. Dimensions attendues: '
}
},
nl: {
hint: 'Klik hier of sleep een afbeelding in dit vlak',
loading: 'Uploaden…',
noSupported: 'Je browser wordt helaas niet ondersteund. Gebruik IE10+ of een andere browser.',
success: 'Upload succesvol',
fail: 'Upload mislukt',
preview: 'Voorbeeld',
btn: {
off: 'Annuleren',
close: 'Sluiten',
back: 'Terug',
save: 'Opslaan'
},
error: {
onlyImg: 'Alleen afbeeldingen',
outOfSize: 'De afbeelding is groter dan: ',
lowestPx: 'De afbeelding is te klein! Minimale afmetingen: '
}
},
tr: {
hint: 'Tıkla veya yüklemek istediğini buraya sürükle',
loading: 'Yükleniyor…',
noSupported: 'Tarayıcı desteklenmiyor, lütfen IE10+ veya farklı tarayıcı kullanın',
success: 'Yükleme başarılı',
fail: 'Yüklemede hata oluştu',
preview: 'Önizle',
btn: {
off: 'İptal',
close: 'Kapat',
back: 'Geri',
save: 'Kaydet'
},
error: {
onlyImg: 'Sadece resim',
outOfSize: 'Resim yükleme limitini aşıyor: ',
lowestPx: 'Resmin boyutu çok küçük. En az olması gereken: '
}
},
'es-MX': {
hint: 'Selecciona o arrastra una imagen',
loading: 'Subiendo...',
noSupported: 'Tu navegador no es soportado, porfavor usa IE10+ u otros navegadores mas recientes',
success: 'Subido exitosamente',
fail: 'Sucedió un error',
preview: 'Vista previa',
btn: {
off: 'Cancelar',
close: 'Cerrar',
back: 'Atras',
save: 'Guardar'
},
error: {
onlyImg: 'Unicamente imagenes',
outOfSize: 'La imagen excede el tamaño maximo:',
lowestPx: 'La imagen es demasiado pequeño. Se espera por lo menos:'
}
},
de: {
hint: 'Klick hier oder zieh eine Datei hier rein zum Hochladen',
loading: 'Hochladen…',
noSupported: 'Browser wird nicht unterstützt, bitte verwende IE10+ oder andere Browser',
success: 'Upload erfolgreich',
fail: 'Upload fehlgeschlagen',
preview: 'Vorschau',
btn: {
off: 'Abbrechen',
close: 'Schließen',
back: 'Zurück',
save: 'Speichern'
},
error: {
onlyImg: 'Nur Bilder',
outOfSize: 'Das Bild ist zu groß: ',
lowestPx: 'Das Bild ist zu klein. Mindestens: '
}
},
ja: {
hint: 'クリック・ドラッグしてファイルをアップロード',
loading: 'アップロード中...',
noSupported: 'このブラウザは対応されていません。IE10+かその他の主要ブラウザをお使いください。',
success: 'アップロード成功',
fail: 'アップロード失敗',
preview: 'プレビュー',
btn: {
off: 'キャンセル',
close: '閉じる',
back: '戻る',
save: '保存'
},
error: {
onlyImg: '画像のみ',
outOfSize: '画像サイズが上限を超えています。上限: ',
lowestPx: '画像が小さすぎます。最小サイズ: '
}
}
}

View File

@@ -1,7 +0,0 @@
export default {
'jpg': 'image/jpeg',
'png': 'image/png',
'gif': 'image/gif',
'svg': 'image/svg+xml',
'psd': 'image/photoshop'
}

View File

@@ -1,6 +1,6 @@
<template> <template>
<div class="json-editor"> <div class="json-editor">
<textarea ref="textarea"/> <textarea ref="textarea"></textarea>
</div> </div>
</template> </template>
@@ -15,14 +15,13 @@ import 'codemirror/addon/lint/lint'
import 'codemirror/addon/lint/json-lint' import 'codemirror/addon/lint/json-lint'
export default { export default {
name: 'JsonEditor', name: 'jsonEditor',
/* eslint-disable vue/require-prop-types */
props: ['value'],
data() { data() {
return { return {
jsonEditor: false jsonEditor: false
} }
}, },
props: ['value'],
watch: { watch: {
value(value) { value(value) {
const editor_value = this.jsonEditor.getValue() const editor_value = this.jsonEditor.getValue()

View File

@@ -1,89 +0,0 @@
<template>
<div class="board-column">
<div class="board-column-header">
{{ headerText }}
</div>
<draggable
:list="list"
:options="options"
class="board-column-content">
<div v-for="element in list" :key="element.id" class="board-item">
{{ element.name }} {{ element.id }}
</div>
</draggable>
</div>
</template>
<script>
import draggable from 'vuedraggable'
export default {
name: 'DragKanbanDemo',
components: {
draggable
},
props: {
headerText: {
type: String,
default: 'Header'
},
options: {
type: Object,
default() {
return {}
}
},
list: {
type: Array,
default() {
return []
}
}
}
}
</script>
<style lang="scss">
.board-column {
min-width: 300px;
min-height: 100px;
height: auto;
overflow: hidden;
background: #f0f0f0;
border-radius: 3px;
.board-column-header {
height: 50px;
line-height: 50px;
overflow: hidden;
padding: 0 20px;
text-align: center;
background: #333;
color: #fff;
border-radius: 3px 3px 0 0;
}
.board-column-content {
height: auto;
overflow: hidden;
border: 10px solid transparent;
min-height: 60px;
display: flex;
justify-content: flex-start;
flex-direction: column;
align-items: center;
.board-item {
cursor: pointer;
width: 100%;
height: 64px;
margin: 5px 0;
background-color: #fff;
text-align: left;
line-height: 54px;
padding: 5px 10px;
box-sizing: border-box;
box-shadow: 0px 1px 3px 0 rgba(0,0,0,0.2);
}
}
}
</style>

View File

@@ -1,11 +1,11 @@
<template> <template>
<el-dropdown trigger="click" class="international" @command="handleSetLanguage"> <el-dropdown trigger="click" class='international' @command="handleSetLanguage">
<div> <div>
<svg-icon class-name="international-icon" icon-class="language" /> <svg-icon class-name='international-icon' icon-class="language" />
</div> </div>
<el-dropdown-menu slot="dropdown"> <el-dropdown-menu slot="dropdown">
<el-dropdown-item :disabled="language==='zh'" command="zh">中文</el-dropdown-item> <el-dropdown-item command="zh" :disabled="language==='zh'">中文</el-dropdown-item>
<el-dropdown-item :disabled="language==='en'" command="en">English</el-dropdown-item> <el-dropdown-item command="en" :disabled="language==='en'">English</el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</el-dropdown> </el-dropdown>
</template> </template>
@@ -38,3 +38,4 @@ export default {
} }
</style> </style>

View File

@@ -1,103 +1,28 @@
<template> <template>
<div :class="computedClasses" class="material-input__component"> <div class="material-input__component" :class="computedClasses">
<div :class="{iconClass:icon}"> <div :class="{iconClass:icon}">
<i v-if="icon" :class="['el-icon-' + icon]" class="el-input__icon material-input__icon"/> <i class="el-input__icon material-input__icon" :class="['el-icon-' + icon]" v-if="icon"></i>
<input <input v-if="type === 'email'" type="email" class="material-input" :name="name" :placeholder="fillPlaceHolder" v-model="currentValue"
v-if="type === 'email'" :readonly="readonly" :disabled="disabled" :autoComplete="autoComplete" :required="required" @focus="handleMdFocus"
:name="name" @blur="handleMdBlur" @input="handleModelInput">
:placeholder="fillPlaceHolder" <input v-if="type === 'url'" type="url" class="material-input" :name="name" :placeholder="fillPlaceHolder" v-model="currentValue"
v-model="currentValue" :readonly="readonly" :disabled="disabled" :autoComplete="autoComplete" :required="required" @focus="handleMdFocus"
:readonly="readonly" @blur="handleMdBlur" @input="handleModelInput">
:disabled="disabled" <input v-if="type === 'number'" type="number" class="material-input" :name="name" :placeholder="fillPlaceHolder" v-model="currentValue"
:autoComplete="autoComplete" :step="step" :readonly="readonly" :disabled="disabled" :autoComplete="autoComplete" :max="max" :min="min" :minlength="minlength"
:required="required" :maxlength="maxlength" :required="required" @focus="handleMdFocus" @blur="handleMdBlur" @input="handleModelInput">
type="email" <input v-if="type === 'password'" type="password" class="material-input" :name="name" :placeholder="fillPlaceHolder" v-model="currentValue"
class="material-input" :readonly="readonly" :disabled="disabled" :autoComplete="autoComplete" :max="max" :min="min" :required="required" @focus="handleMdFocus"
@focus="handleMdFocus" @blur="handleMdBlur" @input="handleModelInput">
@blur="handleMdBlur" <input v-if="type === 'tel'" type="tel" class="material-input" :name="name" :placeholder="fillPlaceHolder" v-model="currentValue"
@input="handleModelInput"> :readonly="readonly" :disabled="disabled" :autoComplete="autoComplete" :required="required" @focus="handleMdFocus"
<input @blur="handleMdBlur" @input="handleModelInput">
v-if="type === 'url'" <input v-if="type === 'text'" type="text" class="material-input" :name="name" :placeholder="fillPlaceHolder" v-model="currentValue"
:name="name" :readonly="readonly" :disabled="disabled" :autoComplete="autoComplete" :minlength="minlength" :maxlength="maxlength"
:placeholder="fillPlaceHolder" :required="required" @focus="handleMdFocus" @blur="handleMdBlur" @input="handleModelInput">
v-model="currentValue" <span class="material-input-bar"></span>
:readonly="readonly"
:disabled="disabled"
:autoComplete="autoComplete"
:required="required"
type="url"
class="material-input"
@focus="handleMdFocus"
@blur="handleMdBlur"
@input="handleModelInput">
<input
v-if="type === 'number'"
:name="name"
:placeholder="fillPlaceHolder"
v-model="currentValue"
:step="step"
:readonly="readonly"
:disabled="disabled"
:autoComplete="autoComplete"
:max="max"
:min="min"
:minlength="minlength"
:maxlength="maxlength"
:required="required"
type="number"
class="material-input"
@focus="handleMdFocus"
@blur="handleMdBlur"
@input="handleModelInput">
<input
v-if="type === 'password'"
:name="name"
:placeholder="fillPlaceHolder"
v-model="currentValue"
:readonly="readonly"
:disabled="disabled"
:autoComplete="autoComplete"
:max="max"
:min="min"
:required="required"
type="password"
class="material-input"
@focus="handleMdFocus"
@blur="handleMdBlur"
@input="handleModelInput">
<input
v-if="type === 'tel'"
:name="name"
:placeholder="fillPlaceHolder"
v-model="currentValue"
:readonly="readonly"
:disabled="disabled"
:autoComplete="autoComplete"
:required="required"
type="tel"
class="material-input"
@focus="handleMdFocus"
@blur="handleMdBlur"
@input="handleModelInput">
<input
v-if="type === 'text'"
:name="name"
:placeholder="fillPlaceHolder"
v-model="currentValue"
:readonly="readonly"
:disabled="disabled"
:autoComplete="autoComplete"
:minlength="minlength"
:maxlength="maxlength"
:required="required"
type="text"
class="material-input"
@focus="handleMdFocus"
@blur="handleMdBlur"
@input="handleModelInput">
<span class="material-input-bar"/>
<label class="material-label"> <label class="material-label">
<slot/> <slot></slot>
</label> </label>
</div> </div>
</div> </div>
@@ -107,9 +32,8 @@
// source:https://github.com/wemake-services/vue-material-input/blob/master/src/components/MaterialInput.vue // source:https://github.com/wemake-services/vue-material-input/blob/master/src/components/MaterialInput.vue
export default { export default {
name: 'MdInput', name: 'md-input',
props: { props: {
/* eslint-disable */
icon: String, icon: String,
name: String, name: String,
type: { type: {
@@ -138,13 +62,6 @@ export default {
default: true default: true
} }
}, },
data() {
return {
currentValue: this.value,
focus: false,
fillPlaceHolder: null
}
},
computed: { computed: {
computedClasses() { computedClasses() {
return { return {
@@ -159,6 +76,13 @@ export default {
this.currentValue = newValue this.currentValue = newValue
} }
}, },
data() {
return {
currentValue: this.value,
focus: false,
fillPlaceHolder: null
}
},
methods: { methods: {
handleModelInput(event) { handleModelInput(event) {
const value = event.target.value const value = event.target.value

View File

@@ -1,6 +1,7 @@
<template> <template>
<div :style="{height:height+'px',zIndex:zIndex}" class="simplemde-container"> <div class="simplemde-container" :style="{height:height+'px',zIndex:zIndex}">
<textarea :id="id"/> <textarea :id="id">
</textarea>
</div> </div>
</template> </template>
@@ -10,16 +11,11 @@ import 'simplemde/dist/simplemde.min.css'
import SimpleMDE from 'simplemde' import SimpleMDE from 'simplemde'
export default { export default {
name: 'SimplemdeMd', name: 'simplemde-md',
props: { props: {
value: { value: String,
type: String,
default: ''
},
id: { id: {
type: String, type: String
required: false,
default: 'markdown-editor-' + +new Date()
}, },
autofocus: { autofocus: {
type: Boolean, type: Boolean,
@@ -38,10 +34,7 @@ export default {
default: 10 default: 10
}, },
toolbar: { toolbar: {
type: Array, type: Array
default: function() {
return []
}
} }
}, },
data() { data() {
@@ -58,10 +51,10 @@ export default {
}, },
mounted() { mounted() {
this.simplemde = new SimpleMDE({ this.simplemde = new SimpleMDE({
element: document.getElementById(this.id), element: document.getElementById(this.id || 'markdown-editor-' + +new Date()),
autoDownloadFontAwesome: false, autoDownloadFontAwesome: false,
autofocus: this.autofocus, autofocus: this.autofocus,
toolbar: this.toolbar.length > 0 ? this.toolbar : undefined, toolbar: this.toolbar,
spellChecker: false, spellChecker: false,
insertTexts: { insertTexts: {
link: ['[', ']( )'] link: ['[', ']( )']

View File

@@ -1,12 +1,12 @@
<template> <template>
<div :style="{zIndex:zIndex,height:height,width:width}" class="pan-item"> <div class="pan-item" :style="{zIndex:zIndex,height:height,width:width}">
<div class="pan-info"> <div class="pan-info">
<div class="pan-info-roles-container"> <div class="pan-info-roles-container">
<slot/> <slot></slot>
</div> </div>
</div> </div>
<img :src="image" class="pan-thumb"> <img class="pan-thumb" :src="image">
</div> </div>
</template> </template>
<script> <script>
@@ -19,7 +19,7 @@ export default {
}, },
zIndex: { zIndex: {
type: Number, type: Number,
default: 1 default: 100
}, },
width: { width: {
type: String, type: String,
@@ -35,106 +35,106 @@ export default {
<style scoped> <style scoped>
.pan-item { .pan-item {
width: 200px; width: 200px;
height: 200px; height: 200px;
border-radius: 50%; border-radius: 50%;
display: inline-block; display: inline-block;
position: relative; position: relative;
cursor: default; cursor: default;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
} }
.pan-info-roles-container { .pan-info-roles-container {
padding: 20px; padding: 20px;
text-align: center; text-align: center;
} }
.pan-thumb { .pan-thumb {
width: 100%; width: 100%;
height: 100%; height: 100%;
background-size: 100%; background-size: 100%;
border-radius: 50%; border-radius: 50%;
overflow: hidden; overflow: hidden;
position: absolute; position: absolute;
transform-origin: 95% 40%; transform-origin: 95% 40%;
transition: all 0.3s ease-in-out; transition: all 0.3s ease-in-out;
} }
.pan-thumb:after { .pan-thumb:after {
content: ''; content: '';
width: 8px; width: 8px;
height: 8px; height: 8px;
position: absolute; position: absolute;
border-radius: 50%; border-radius: 50%;
top: 40%; top: 40%;
left: 95%; left: 95%;
margin: -4px 0 0 -4px; margin: -4px 0 0 -4px;
background: radial-gradient(ellipse at center, rgba(14, 14, 14, 1) 0%, rgba(125, 126, 125, 1) 100%); background: radial-gradient(ellipse at center, rgba(14, 14, 14, 1) 0%, rgba(125, 126, 125, 1) 100%);
box-shadow: 0 0 1px rgba(255, 255, 255, 0.9); box-shadow: 0 0 1px rgba(255, 255, 255, 0.9);
} }
.pan-info { .pan-info {
position: absolute; position: absolute;
width: inherit; width: inherit;
height: inherit; height: inherit;
border-radius: 50%; border-radius: 50%;
overflow: hidden; overflow: hidden;
box-shadow: inset 0 0 0 5px rgba(0, 0, 0, 0.05); box-shadow: inset 0 0 0 5px rgba(0, 0, 0, 0.05);
} }
.pan-info h3 { .pan-info h3 {
color: #fff; color: #fff;
text-transform: uppercase; text-transform: uppercase;
position: relative; position: relative;
letter-spacing: 2px; letter-spacing: 2px;
font-size: 18px; font-size: 18px;
margin: 0 60px; margin: 0 60px;
padding: 22px 0 0 0; padding: 22px 0 0 0;
height: 85px; height: 85px;
font-family: 'Open Sans', Arial, sans-serif; font-family: 'Open Sans', Arial, sans-serif;
text-shadow: 0 0 1px #fff, 0 1px 2px rgba(0, 0, 0, 0.3); text-shadow: 0 0 1px #fff, 0 1px 2px rgba(0, 0, 0, 0.3);
} }
.pan-info p { .pan-info p {
color: #fff; color: #fff;
padding: 10px 5px; padding: 10px 5px;
font-style: italic; font-style: italic;
margin: 0 30px; margin: 0 30px;
font-size: 12px; font-size: 12px;
border-top: 1px solid rgba(255, 255, 255, 0.5); border-top: 1px solid rgba(255, 255, 255, 0.5);
} }
.pan-info p a { .pan-info p a {
display: block; display: block;
color: #333; color: #333;
width: 80px; width: 80px;
height: 80px; height: 80px;
background: rgba(255, 255, 255, 0.3); background: rgba(255, 255, 255, 0.3);
border-radius: 50%; border-radius: 50%;
color: #fff; color: #fff;
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
text-transform: uppercase; text-transform: uppercase;
font-size: 9px; font-size: 9px;
letter-spacing: 1px; letter-spacing: 1px;
padding-top: 24px; padding-top: 24px;
margin: 7px auto 0; margin: 7px auto 0;
font-family: 'Open Sans', Arial, sans-serif; font-family: 'Open Sans', Arial, sans-serif;
opacity: 0; opacity: 0;
transition: transform 0.3s ease-in-out 0.2s, opacity 0.3s ease-in-out 0.2s, background 0.2s linear 0s; transition: transform 0.3s ease-in-out 0.2s, opacity 0.3s ease-in-out 0.2s, background 0.2s linear 0s;
transform: translateX(60px) rotate(90deg); transform: translateX(60px) rotate(90deg);
} }
.pan-info p a:hover { .pan-info p a:hover {
background: rgba(255, 255, 255, 0.5); background: rgba(255, 255, 255, 0.5);
} }
.pan-item:hover .pan-thumb { .pan-item:hover .pan-thumb {
transform: rotate(-110deg); transform: rotate(-110deg);
} }
.pan-item:hover .pan-info p a { .pan-item:hover .pan-info p a {
opacity: 1; opacity: 1;
transform: translateX(0px) rotate(0deg); transform: translateX(0px) rotate(0deg);
} }
</style> </style>

View File

@@ -1,28 +1,15 @@
<template> <template>
<div> <div>
<svg <svg t="1508738709248" @click='click' class="screenfull-svg" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
t="1508738709248" p-id="2069" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32">
class="screenfull-svg" <path d="M333.493443 428.647617 428.322206 333.832158 262.572184 168.045297 366.707916 64.444754 64.09683 64.444754 63.853283 366.570793 167.283957 262.460644Z"
viewBox="0 0 1024 1024" p-id="2070"></path>
version="1.1" <path d="M854.845439 760.133334 688.61037 593.95864 593.805144 688.764889 759.554142 854.56096 655.44604 958.161503 958.055079 958.161503 958.274066 656.035464Z"
xmlns="http://www.w3.org/2000/svg" p-id="2071"></path>
p-id="2069" <path d="M688.535669 428.550403 854.31025 262.801405 957.935352 366.921787 957.935352 64.34754 655.809313 64.081481 759.919463 167.535691 593.70793 333.731874Z"
xmlns:xlink="http://www.w3.org/1999/xlink" p-id="2072"></path>
width="32" <path d="M333.590658 594.033341 167.8171 759.804852 64.218604 655.67219 64.218604 958.270996 366.342596 958.502263 262.234493 855.071589 428.421466 688.86108Z"
height="32" p-id="2073"></path>
@click="click">
<path
d="M333.493443 428.647617 428.322206 333.832158 262.572184 168.045297 366.707916 64.444754 64.09683 64.444754 63.853283 366.570793 167.283957 262.460644Z"
p-id="2070"/>
<path
d="M854.845439 760.133334 688.61037 593.95864 593.805144 688.764889 759.554142 854.56096 655.44604 958.161503 958.055079 958.161503 958.274066 656.035464Z"
p-id="2071"/>
<path
d="M688.535669 428.550403 854.31025 262.801405 957.935352 366.921787 957.935352 64.34754 655.809313 64.081481 759.919463 167.535691 593.70793 333.731874Z"
p-id="2072"/>
<path
d="M333.590658 594.033341 167.8171 759.804852 64.218604 655.67219 64.218604 958.270996 366.342596 958.502263 262.234493 855.071589 428.421466 688.86108Z"
p-id="2073"/>
</svg> </svg>
</div> </div>
</template> </template>
@@ -31,7 +18,7 @@
import screenfull from 'screenfull' import screenfull from 'screenfull'
export default { export default {
name: 'Screenfull', name: 'screenfull',
props: { props: {
width: { width: {
type: Number, type: Number,

View File

@@ -0,0 +1,57 @@
<template>
<div class="scroll-container" ref="scrollContainer" @wheel.prevent="handleScroll" >
<div class="scroll-wrapper" ref="scrollWrapper" :style="{top: top + 'px'}">
<slot></slot>
</div>
</div>
</template>
<script>
const delta = 15
export default {
name: 'scrollBar',
data() {
return {
top: 0
}
},
methods: {
handleScroll(e) {
const eventDelta = e.wheelDelta || -e.deltaY * 3
const $container = this.$refs.scrollContainer
const $containerHeight = $container.offsetHeight
const $wrapper = this.$refs.scrollWrapper
const $wrapperHeight = $wrapper.offsetHeight
if (eventDelta > 0) {
this.top = Math.min(0, this.top + eventDelta)
} else {
if ($containerHeight - delta < $wrapperHeight) {
if (this.top < -($wrapperHeight - $containerHeight + delta)) {
this.top = this.top
} else {
this.top = Math.max(this.top + eventDelta, $containerHeight - $wrapperHeight - delta)
}
} else {
this.top = 0
}
}
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
@import '../../styles/variables.scss';
.scroll-container {
position: relative;
width: 100%;
height: 100%;
background-color: $menuBg;
.scroll-wrapper {
position: absolute;
width: 100%!important;
}
}
</style>

View File

@@ -1,7 +1,7 @@
<template> <template>
<div ref="scrollContainer" class="scroll-container" @wheel.prevent="handleScroll"> <div class="scroll-container" ref="scrollContainer" @wheel.prevent="handleScroll">
<div ref="scrollWrapper" :style="{left: left + 'px'}" class="scroll-wrapper"> <div class="scroll-wrapper" ref="scrollWrapper" :style="{left: left + 'px'}">
<slot/> <slot></slot>
</div> </div>
</div> </div>
</template> </template>
@@ -10,7 +10,7 @@
const padding = 15 // tag's padding const padding = 15 // tag's padding
export default { export default {
name: 'ScrollPane', name: 'scrollPane',
data() { data() {
return { return {
left: 0 left: 0

View File

@@ -1,10 +1,10 @@
<template> <template>
<div :class="{active:isActive}" class="share-dropdown-menu"> <div class="share-dropdown-menu" :class="{active:isActive}">
<div class="share-dropdown-menu-wrapper"> <div class="share-dropdown-menu-wrapper">
<span class="share-dropdown-menu-title" @click.self="clickTitle">{{ title }}</span> <span class="share-dropdown-menu-title" @click.self="clickTitle">{{title}}</span>
<div v-for="(item,index) of items" :key="index" class="share-dropdown-menu-item"> <div class="share-dropdown-menu-item" v-for="(item,index) of items" :key='index'>
<a v-if="item.href" :href="item.href" target="_blank">{{ item.title }}</a> <a v-if="item.href" :href="item.href" target="_blank">{{item.title}}</a>
<span v-else>{{ item.title }}</span> <span v-else>{{item.title}}</span>
</div> </div>
</div> </div>
</div> </div>
@@ -14,10 +14,7 @@
export default { export default {
props: { props: {
items: { items: {
type: Array, type: Array
default: function() {
return []
}
}, },
title: { title: {
type: String, type: String,
@@ -38,7 +35,7 @@ export default {
</script> </script>
<style rel="stylesheet/scss" lang="scss" > <style rel="stylesheet/scss" lang="scss" >
$n: 8; //和items.length 相同 $n: 6; //和items.length 相同
$t: .1s; $t: .1s;
.share-dropdown-menu { .share-dropdown-menu {
width: 250px; width: 250px;

View File

@@ -21,30 +21,29 @@ export default {
default: 1 default: 1
}, },
className: { className: {
type: String, type: String
default: ''
} }
}, },
data() { data() {
return { return {
active: false, active: false,
position: '', position: '',
currentTop: '',
width: undefined, width: undefined,
height: undefined, height: undefined,
isSticky: false child: null,
stickyHeight: 0
} }
}, },
mounted() { mounted() {
this.height = this.$el.getBoundingClientRect().height this.height = this.$el.getBoundingClientRect().height
window.addEventListener('scroll', this.handleScroll) window.addEventListener('scroll', this.handleScroll)
window.addEventListener('resize', this.handleReize)
}, },
activated() { activated() {
this.handleScroll() this.handleScroll()
}, },
destroyed() { destroyed() {
window.removeEventListener('scroll', this.handleScroll) window.removeEventListener('scroll', this.handleScroll)
window.removeEventListener('resize', this.handleReize)
}, },
methods: { methods: {
sticky() { sticky() {
@@ -54,7 +53,6 @@ export default {
this.position = 'fixed' this.position = 'fixed'
this.active = true this.active = true
this.width = this.width + 'px' this.width = this.width + 'px'
this.isSticky = true
}, },
reset() { reset() {
if (!this.active) { if (!this.active) {
@@ -63,21 +61,15 @@ export default {
this.position = '' this.position = ''
this.width = 'auto' this.width = 'auto'
this.active = false this.active = false
this.isSticky = false
}, },
handleScroll() { handleScroll() {
this.width = this.$el.getBoundingClientRect().width this.width = this.$el.getBoundingClientRect().width
const offsetTop = this.$el.getBoundingClientRect().top const offsetTop = this.$el.getBoundingClientRect().top
if (offsetTop < this.stickyTop) { if (offsetTop <= this.stickyTop) {
this.sticky() this.sticky()
return return
} }
this.reset() this.reset()
},
handleReize() {
if (this.isSticky) {
this.width = this.$el.getBoundingClientRect().width + 'px'
}
} }
} }
} }

View File

@@ -1,20 +1,19 @@
<template> <template>
<svg :class="svgClass" aria-hidden="true"> <svg :class="svgClass" aria-hidden="true">
<use :xlink:href="iconName"/> <use :xlink:href="iconName"></use>
</svg> </svg>
</template> </template>
<script> <script>
export default { export default {
name: 'SvgIcon', name: 'svg-icon',
props: { props: {
iconClass: { iconClass: {
type: String, type: String,
required: true required: true
}, },
className: { className: {
type: String, type: String
default: ''
} }
}, },
computed: { computed: {

View File

@@ -1,8 +1,8 @@
<template> <template>
<a :class="className" class="link--mallki" href="#"> <a class="link--mallki" :class="className" href="#">
{{ text }} {{text}}
<span :data-letters="text"/> <span :data-letters="text"></span>
<span :data-letters="text"/> <span :data-letters="text"></span>
</a> </a>
</template> </template>
@@ -10,8 +10,7 @@
export default { export default {
props: { props: {
className: { className: {
type: String, type: String
default: ''
}, },
text: { text: {
type: String, type: String,
@@ -21,9 +20,9 @@ export default {
} }
</script> </script>
<style> <style>
/* Mallki */ /* Mallki */
.link--mallki { .link--mallki {
font-weight: 800; font-weight: 800;
color: #4dd9d5; color: #4dd9d5;
@@ -32,10 +31,10 @@ export default {
transition: color 0.5s 0.25s; transition: color 0.5s 0.25s;
overflow: hidden; overflow: hidden;
position: relative; position: relative;
display: inline-block; display: inline-block;
line-height: 1; line-height: 1;
outline: none; outline: none;
text-decoration: none; text-decoration: none;
} }
.link--mallki:hover { .link--mallki:hover {
@@ -110,4 +109,5 @@ export default {
-webkit-transition-timing-function: cubic-bezier(0.2, 1, 0.3, 1); -webkit-transition-timing-function: cubic-bezier(0.2, 1, 0.3, 1);
transition-timing-function: cubic-bezier(0.2, 1, 0.3, 1); transition-timing-function: cubic-bezier(0.2, 1, 0.3, 1);
} }
</style> </style>

View File

@@ -1,8 +1,8 @@
<template> <template>
<el-color-picker <el-color-picker
v-model="theme"
class="theme-picker" class="theme-picker"
popper-class="theme-picker-dropdown"/> popper-class="theme-picker-dropdown"
v-model="theme"></el-color-picker>
</template> </template>
<script> <script>

View File

@@ -1,18 +1,10 @@
<template> <template>
<div class="upload-container"> <div class="upload-container">
<el-button :style="{background:color,borderColor:color}" icon="el-icon-upload" size="mini" type="primary" @click=" dialogVisible=true">上传图片 <el-button icon='upload' :style="{background:color,borderColor:color}" @click=" dialogVisible=true" type="primary">上传图片
</el-button> </el-button>
<el-dialog :visible.sync="dialogVisible"> <el-dialog :visible.sync="dialogVisible">
<el-upload <el-upload class="editor-slide-upload" action="https://httpbin.org/post" :multiple="true" :file-list="fileList" :show-file-list="true"
:multiple="true" list-type="picture-card" :on-remove="handleRemove" :on-success="handleSuccess" :before-upload="beforeUpload">
:file-list="fileList"
:show-file-list="true"
:on-remove="handleRemove"
:on-success="handleSuccess"
:before-upload="beforeUpload"
class="editor-slide-upload"
action="https://httpbin.org/post"
list-type="picture-card">
<el-button size="small" type="primary">点击上传</el-button> <el-button size="small" type="primary">点击上传</el-button>
</el-upload> </el-upload>
<el-button @click="dialogVisible = false"> </el-button> <el-button @click="dialogVisible = false"> </el-button>
@@ -25,11 +17,11 @@
// import { getToken } from 'api/qiniu' // import { getToken } from 'api/qiniu'
export default { export default {
name: 'EditorSlideUpload', name: 'editorSlideUpload',
props: { props: {
color: { color: {
type: String, type: String,
default: '#1890ff' default: '#20a0ff'
} }
}, },
data() { data() {
@@ -95,10 +87,9 @@ export default {
</script> </script>
<style rel="stylesheet/scss" lang="scss" scoped> <style rel="stylesheet/scss" lang="scss" scoped>
.editor-slide-upload { .upload-container {
margin-bottom: 20px; .editor-slide-upload {
/deep/ .el-upload--picture-card { margin-bottom: 20px;
width: 100%; }
} }
}
</style> </style>

View File

@@ -1,24 +1,34 @@
<template> <template>
<div :class="{fullscreen:fullscreen}" class="tinymce-container editor-container"> <div class="tinymce-container editor-container">
<textarea :id="tinymceId" class="tinymce-textarea"/> <textarea class="tinymce-textarea" :id="tinymceId"></textarea>
<div class="editor-custom-btn-container"> <div class="editor-custom-btn-container">
<editorImage color="#1890ff" class="editor-upload-btn" @successCBK="imageSuccessCBK"/> <editorImage color="#20a0ff" class="editor-upload-btn" @successCBK="imageSuccessCBK"></editorImage>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import editorImage from './components/editorImage' // Import Tinymce
import tinymce from 'tinymce/tinymce'
tinymce.baseURL = 'static/tinymce'
// A theme is also required
import 'tinymce/themes/modern/theme'
// Any plugins you want to use has to be imported
import plugins from './plugins' import plugins from './plugins'
import toolbar from './toolbar' import toolbar from './toolbar'
import editorImage from './components/editorImage'
export default { export default {
name: 'Tinymce', name: 'tinymce',
components: { editorImage }, components: { editorImage },
props: { props: {
id: { id: {
type: String, type: String
default: 'vue-tinymce-' + +new Date()
}, },
value: { value: {
type: String, type: String,
@@ -32,8 +42,7 @@ export default {
} }
}, },
menubar: { menubar: {
type: String, default: 'file edit insert view format table help'
default: 'file edit insert view format table'
}, },
height: { height: {
type: Number, type: Number,
@@ -45,15 +54,13 @@ export default {
return { return {
hasChange: false, hasChange: false,
hasInit: false, hasInit: false,
tinymceId: this.id, tinymceId: this.id || 'vue-tinymce-' + +new Date()
fullscreen: false
} }
}, },
watch: { watch: {
value(val) { value(val) {
if (!this.hasChange && this.hasInit) { if (!this.hasChange && this.hasInit) {
this.$nextTick(() => this.$nextTick(() => tinymce.get(this.tinymceId).setContent(val))
window.tinymce.get(this.tinymceId).setContent(val || ''))
} }
} }
}, },
@@ -66,43 +73,36 @@ export default {
deactivated() { deactivated() {
this.destroyTinymce() this.destroyTinymce()
}, },
destroyed() {
this.destroyTinymce()
},
methods: { methods: {
initTinymce() { initTinymce() {
const _this = this const _this = this
window.tinymce.init({ tinymce.init({
selector: `#${this.tinymceId}`, selector: `#${this.tinymceId}`,
// language: 'zh_CN',
height: this.height, height: this.height,
body_class: 'panel-body ',
object_resizing: false,
toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar, toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar,
menubar: this.menubar, menubar: this.menubar,
plugins: plugins, plugins: plugins,
body_class: 'panel-body ',
object_resizing: false,
end_container_on_empty_block: true, end_container_on_empty_block: true,
powerpaste_word_import: 'clean', powerpaste_word_import: 'clean',
code_dialog_height: 450, code_dialog_height: 450,
code_dialog_width: 1000, code_dialog_width: 1000,
advlist_bullet_styles: 'square', advlist_bullet_styles: 'square',
advlist_number_styles: 'default', advlist_number_styles: 'default',
imagetools_cors_hosts: ['www.tinymce.com', 'codepen.io'], imagetools_cors_hosts: ['wpimg.wallstcn.com', 'wallstreetcn.com'],
imagetools_toolbar: 'watermark',
default_link_target: '_blank', default_link_target: '_blank',
link_title: false, link_title: false,
nonbreaking_force_tab: true, // inserting nonbreaking space &nbsp; need Nonbreaking Space Plugin
init_instance_callback: editor => { init_instance_callback: editor => {
if (_this.value) { if (_this.value) {
editor.setContent(_this.value) editor.setContent(_this.value)
} }
_this.hasInit = true _this.hasInit = true
editor.on('NodeChange Change KeyUp SetContent', () => { editor.on('NodeChange Change KeyUp', () => {
this.hasChange = true this.hasChange = true
this.$emit('input', editor.getContent()) this.$emit('input', editor.getContent({ format: 'raw' }))
})
},
setup(editor) {
editor.on('FullscreenStateChanged', (e) => {
_this.fullscreen = e.state
}) })
} }
// 整合七牛上传 // 整合七牛上传
@@ -141,32 +141,32 @@ export default {
}) })
}, },
destroyTinymce() { destroyTinymce() {
if (window.tinymce.get(this.tinymceId)) { if (tinymce.get(this.tinymceId)) {
window.tinymce.get(this.tinymceId).destroy() tinymce.get(this.tinymceId).destroy()
} }
}, },
setContent(value) { setContent(value) {
window.tinymce.get(this.tinymceId).setContent(value) tinymce.get(this.tinymceId).setContent(value)
}, },
getContent() { getContent() {
window.tinymce.get(this.tinymceId).getContent() tinymce.get(this.tinymceId).getContent()
}, },
imageSuccessCBK(arr) { imageSuccessCBK(arr) {
const _this = this const _this = this
arr.forEach(v => { arr.forEach(v => {
window.tinymce.get(_this.tinymceId).insertContent(`<img class="wscnph" src="${v.url}" >`) tinymce.get(_this.tinymceId).insertContent(`<img class="wscnph" src="${v.url}" >`)
}) })
} }
},
destroyed() {
this.destroyTinymce()
} }
} }
</script> </script>
<style scoped> <style scoped>
.tinymce-container { .tinymce-container {
position: relative; position: relative
}
.tinymce-container>>>.mce-fullscreen {
z-index: 10000;
} }
.tinymce-textarea { .tinymce-textarea {
visibility: hidden; visibility: hidden;
@@ -174,13 +174,9 @@ export default {
} }
.editor-custom-btn-container { .editor-custom-btn-container {
position: absolute; position: absolute;
right: 4px; right: 15px;
top: 4px;
/*z-index: 2005;*/ /*z-index: 2005;*/
} top: 18px;
.fullscreen .editor-custom-btn-container {
z-index: 10000;
position: fixed;
} }
.editor-upload-btn { .editor-upload-btn {
display: inline-block; display: inline-block;

View File

@@ -1,7 +1,49 @@
// Any plugins you want to use has to be imported // Any plugins you want to use has to be imported
// Detail plugins list see https://www.tinymce.com/docs/plugins/ // 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 link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount'] import 'tinymce/plugins/advlist'
import 'tinymce/plugins/anchor'
import 'tinymce/plugins/autolink'
import 'tinymce/plugins/autoresize'
import 'tinymce/plugins/autosave'
import 'tinymce/plugins/bbcode'
import 'tinymce/plugins/charmap'
import 'tinymce/plugins/code'
import 'tinymce/plugins/colorpicker'
import 'tinymce/plugins/colorpicker'
import 'tinymce/plugins/contextmenu'
import 'tinymce/plugins/directionality'
import 'tinymce/plugins/fullpage'
import 'tinymce/plugins/fullscreen'
import 'tinymce/plugins/hr'
import 'tinymce/plugins/image'
import 'tinymce/plugins/imagetools'
import 'tinymce/plugins/importcss'
import 'tinymce/plugins/insertdatetime'
import 'tinymce/plugins/legacyoutput'
import 'tinymce/plugins/link'
import 'tinymce/plugins/lists'
import 'tinymce/plugins/media'
import 'tinymce/plugins/nonbreaking'
import 'tinymce/plugins/noneditable'
import 'tinymce/plugins/pagebreak'
import 'tinymce/plugins/paste'
import 'tinymce/plugins/preview'
import 'tinymce/plugins/print'
import 'tinymce/plugins/save'
import 'tinymce/plugins/searchreplace'
import 'tinymce/plugins/spellchecker'
import 'tinymce/plugins/tabfocus'
import 'tinymce/plugins/table'
import 'tinymce/plugins/template'
import 'tinymce/plugins/textcolor'
import 'tinymce/plugins/textpattern'
import 'tinymce/plugins/visualblocks'
import 'tinymce/plugins/visualchars'
import 'tinymce/plugins/wordcount'
import 'tinymce/plugins/help'
import 'tinymce/plugins/toc'
const plugins = ['advlist anchor autolink autoresize autosave bbcode code colorpicker colorpicker contextmenu directionality fullpage 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 help toc']
export default plugins export default plugins

View File

@@ -1,6 +1,6 @@
// Here is a list of the toolbar // Here is a list of the toolbar
// Detail list see https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols // 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', 'hr bullist numlist link image charmap preview anchor pagebreak fullscreen insertdatetime media table emoticons forecolor backcolor']
export default toolbar export default toolbar

View File

@@ -2,27 +2,25 @@
<el-table :data="formatData" :row-style="showRow" v-bind="$attrs"> <el-table :data="formatData" :row-style="showRow" v-bind="$attrs">
<el-table-column v-if="columns.length===0" width="150"> <el-table-column v-if="columns.length===0" width="150">
<template slot-scope="scope"> <template slot-scope="scope">
<span v-for="space in scope.row._level" :key="space" class="ms-tree-space"/> <span v-for="space in scope.row._level" class="ms-tree-space" :key="space"></span>
<span v-if="iconShow(0,scope.row)" class="tree-ctrl" @click="toggleExpanded(scope.$index)"> <span class="tree-ctrl" v-if="iconShow(0,scope.row)" @click="toggleExpanded(scope.$index)">
<i v-if="!scope.row._expanded" class="el-icon-plus"/> <i v-if="!scope.row._expanded" class="el-icon-plus"></i>
<i v-else class="el-icon-minus"/> <i v-else class="el-icon-minus"></i>
</span> </span>
{{ scope.$index }} {{scope.$index}}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column v-for="(column, index) in columns" v-else :key="column.value" :label="column.text" :width="column.width"> <el-table-column v-else v-for="(column, index) in columns" :key="column.value" :label="column.text" :width="column.width">
<template slot-scope="scope"> <template slot-scope="scope">
<!-- Todo --> <span v-if="index === 0" v-for="space in scope.row._level" class="ms-tree-space" :key="space"></span>
<!-- eslint-disable-next-line vue/no-confusing-v-for-v-if --> <span class="tree-ctrl" v-if="iconShow(index,scope.row)" @click="toggleExpanded(scope.$index)">
<span v-for="space in scope.row._level" v-if="index === 0" :key="space" class="ms-tree-space"/> <i v-if="!scope.row._expanded" class="el-icon-plus"></i>
<span v-if="iconShow(index,scope.row)" class="tree-ctrl" @click="toggleExpanded(scope.$index)"> <i v-else class="el-icon-minus"></i>
<i v-if="!scope.row._expanded" class="el-icon-plus"/>
<i v-else class="el-icon-minus"/>
</span> </span>
{{ scope.row[column.value] }} {{scope.row[column.value]}}
</template> </template>
</el-table-column> </el-table-column>
<slot/> <slot></slot>
</el-table> </el-table>
</template> </template>
@@ -33,9 +31,8 @@
*/ */
import treeToArray from './eval' import treeToArray from './eval'
export default { export default {
name: 'TreeTable', name: 'treeTable',
props: { props: {
/* eslint-disable */
data: { data: {
type: [Array, Object], type: [Array, Object],
required: true required: true

View File

@@ -1,25 +1,19 @@
<template> <template>
<div class="upload-container"> <div class="upload-container">
<el-upload <el-upload class="image-uploader" :data="dataObj" drag :multiple="false" :show-file-list="false" action="https://httpbin.org/post"
:data="dataObj" :on-success="handleImageScucess">
:multiple="false" <i class="el-icon-upload"></i>
:show-file-list="false" <div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
:on-success="handleImageSuccess" </el-upload>
class="image-uploader" <div class="image-preview">
drag <div class="image-preview-wrapper" v-show="imageUrl.length>1">
action="https://httpbin.org/post"> <img :src="imageUrl+'?imageView2/1/w/200/h/200'">
<i class="el-icon-upload"/> <div class="image-preview-action">
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div> <i @click="rmImage" class="el-icon-delete"></i>
</el-upload> </div>
<div class="image-preview"> </div>
<div v-show="imageUrl.length>1" class="image-preview-wrapper">
<img :src="imageUrl+'?imageView2/1/w/200/h/200'">
<div class="image-preview-action">
<i class="el-icon-delete" @click="rmImage"/>
</div> </div>
</div>
</div> </div>
</div>
</template> </template>
<script> <script>
@@ -27,11 +21,13 @@
import { getToken } from '@/api/qiniu' import { getToken } from '@/api/qiniu'
export default { export default {
name: 'SingleImageUpload', name: 'singleImageUpload',
props: { props: {
value: { value: String
type: String, },
default: '' computed: {
imageUrl() {
return this.value
} }
}, },
data() { data() {
@@ -40,11 +36,6 @@ export default {
dataObj: { token: '', key: '' } dataObj: { token: '', key: '' }
} }
}, },
computed: {
imageUrl() {
return this.value
}
},
methods: { methods: {
rmImage() { rmImage() {
this.emitInput('') this.emitInput('')
@@ -52,7 +43,7 @@ export default {
emitInput(val) { emitInput(val) {
this.$emit('input', val) this.$emit('input', val)
}, },
handleImageSuccess() { handleImageScucess() {
this.emitInput(this.tempUrl) this.emitInput(this.tempUrl)
}, },
beforeUpload() { beforeUpload() {

View File

@@ -1,36 +1,32 @@
<template> <template>
<div class="singleImageUpload2 upload-container"> <div class="singleImageUpload2 upload-container">
<el-upload <el-upload class="image-uploader" :data="dataObj" drag :multiple="false" :show-file-list="false" action="https://httpbin.org/post"
:data="dataObj" :on-success="handleImageScucess">
:multiple="false" <i class="el-icon-upload"></i>
:show-file-list="false" <div class="el-upload__text">Drag或<em>点击上传</em></div>
:on-success="handleImageSuccess" </el-upload>
class="image-uploader" <div v-show="imageUrl.length>0" class="image-preview">
drag <div class="image-preview-wrapper" v-show="imageUrl.length>1">
action="https://httpbin.org/post"> <img :src="imageUrl">
<i class="el-icon-upload"/> <div class="image-preview-action">
<div class="el-upload__text">Drag或<em>点击上传</em></div> <i @click="rmImage" class="el-icon-delete"></i>
</el-upload> </div>
<div v-show="imageUrl.length>0" class="image-preview"> </div>
<div v-show="imageUrl.length>1" class="image-preview-wrapper"> </div>
<img :src="imageUrl"> </div>
<div class="image-preview-action">
<i class="el-icon-delete" @click="rmImage"/>
</div>
</div>
</div>
</div>
</template> </template>
<script> <script>
import { getToken } from '@/api/qiniu' import { getToken } from '@/api/qiniu'
export default { export default {
name: 'SingleImageUpload2', name: 'singleImageUpload2',
props: { props: {
value: { value: String
type: String, },
default: '' computed: {
imageUrl() {
return this.value
} }
}, },
data() { data() {
@@ -39,11 +35,6 @@ export default {
dataObj: { token: '', key: '' } dataObj: { token: '', key: '' }
} }
}, },
computed: {
imageUrl() {
return this.value
}
},
methods: { methods: {
rmImage() { rmImage() {
this.emitInput('') this.emitInput('')
@@ -51,7 +42,7 @@ export default {
emitInput(val) { emitInput(val) {
this.$emit('input', val) this.$emit('input', val)
}, },
handleImageSuccess() { handleImageScucess() {
this.emitInput(this.tempUrl) this.emitInput(this.tempUrl)
}, },
beforeUpload() { beforeUpload() {
@@ -75,53 +66,53 @@ export default {
<style rel="stylesheet/scss" lang="scss" scoped> <style rel="stylesheet/scss" lang="scss" scoped>
.upload-container { .upload-container {
width: 100%; width: 100%;
height: 100%; height: 100%;
position: relative; position: relative;
.image-uploader { .image-uploader {
height: 100%; height: 100%;
} }
.image-preview { .image-preview {
width: 100%; width: 100%;
height: 100%; height: 100%;
position: absolute; position: absolute;
left: 0px; left: 0px;
top: 0px; top: 0px;
border: 1px dashed #d9d9d9; border: 1px dashed #d9d9d9;
.image-preview-wrapper { .image-preview-wrapper {
position: relative; position: relative;
width: 100%; width: 100%;
height: 100%; height: 100%;
img { img {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
} }
.image-preview-action { .image-preview-action {
position: absolute; position: absolute;
width: 100%; width: 100%;
height: 100%; height: 100%;
left: 0; left: 0;
top: 0; top: 0;
cursor: default; cursor: default;
text-align: center; text-align: center;
color: #fff; color: #fff;
opacity: 0; opacity: 0;
font-size: 20px; font-size: 20px;
background-color: rgba(0, 0, 0, .5); background-color: rgba(0, 0, 0, .5);
transition: opacity .3s; transition: opacity .3s;
cursor: pointer; cursor: pointer;
text-align: center; text-align: center;
line-height: 200px; line-height: 200px;
.el-icon-delete { .el-icon-delete {
font-size: 36px; font-size: 36px;
} }
} }
&:hover { &:hover {
.image-preview-action { .image-preview-action {
opacity: 1; opacity: 1;
} }
} }
} }
} }
</style> </style>

View File

@@ -1,44 +1,40 @@
<template> <template>
<div class="upload-container"> <div class="upload-container">
<el-upload <el-upload class="image-uploader" :data="dataObj" drag :multiple="false" :show-file-list="false" action="https://httpbin.org/post"
:data="dataObj" :on-success="handleImageScucess">
:multiple="false" <i class="el-icon-upload"></i>
:show-file-list="false" <div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
:on-success="handleImageSuccess" </el-upload>
class="image-uploader" <div class="image-preview image-app-preview">
drag <div class="image-preview-wrapper" v-show="imageUrl.length>1">
action="https://httpbin.org/post"> <img :src="imageUrl">
<i class="el-icon-upload"/> <div class="image-preview-action">
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div> <i @click="rmImage" class="el-icon-delete"></i>
</el-upload> </div>
<div class="image-preview image-app-preview"> </div>
<div v-show="imageUrl.length>1" class="image-preview-wrapper"> </div>
<img :src="imageUrl"> <div class="image-preview">
<div class="image-preview-action"> <div class="image-preview-wrapper" v-show="imageUrl.length>1">
<i class="el-icon-delete" @click="rmImage"/> <img :src="imageUrl">
</div> <div class="image-preview-action">
</div> <i @click="rmImage" class="el-icon-delete"></i>
</div> </div>
<div class="image-preview"> </div>
<div v-show="imageUrl.length>1" class="image-preview-wrapper"> </div>
<img :src="imageUrl"> </div>
<div class="image-preview-action">
<i class="el-icon-delete" @click="rmImage"/>
</div>
</div>
</div>
</div>
</template> </template>
<script> <script>
import { getToken } from '@/api/qiniu' import { getToken } from '@/api/qiniu'
export default { export default {
name: 'SingleImageUpload3', name: 'singleImageUpload',
props: { props: {
value: { value: String
type: String, },
default: '' computed: {
imageUrl() {
return this.value
} }
}, },
data() { data() {
@@ -47,11 +43,6 @@ export default {
dataObj: { token: '', key: '' } dataObj: { token: '', key: '' }
} }
}, },
computed: {
imageUrl() {
return this.value
}
},
methods: { methods: {
rmImage() { rmImage() {
this.emitInput('') this.emitInput('')
@@ -59,7 +50,7 @@ export default {
emitInput(val) { emitInput(val) {
this.$emit('input', val) this.$emit('input', val)
}, },
handleImageSuccess(file) { handleImageScucess(file) {
this.emitInput(file.files.file) this.emitInput(file.files.file)
}, },
beforeUpload() { beforeUpload() {
@@ -85,70 +76,70 @@ export default {
<style rel="stylesheet/scss" lang="scss" scoped> <style rel="stylesheet/scss" lang="scss" scoped>
@import "src/styles/mixin.scss"; @import "src/styles/mixin.scss";
.upload-container { .upload-container {
width: 100%; width: 100%;
position: relative; position: relative;
@include clearfix; @include clearfix;
.image-uploader { .image-uploader {
width: 35%; width: 35%;
float: left; float: left;
} }
.image-preview { .image-preview {
width: 200px; width: 200px;
height: 200px; height: 200px;
position: relative; position: relative;
border: 1px dashed #d9d9d9; border: 1px dashed #d9d9d9;
float: left; float: left;
margin-left: 50px; margin-left: 50px;
.image-preview-wrapper { .image-preview-wrapper {
position: relative; position: relative;
width: 100%; width: 100%;
height: 100%; height: 100%;
img { img {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
} }
.image-preview-action { .image-preview-action {
position: absolute; position: absolute;
width: 100%; width: 100%;
height: 100%; height: 100%;
left: 0; left: 0;
top: 0; top: 0;
cursor: default; cursor: default;
text-align: center; text-align: center;
color: #fff; color: #fff;
opacity: 0; opacity: 0;
font-size: 20px; font-size: 20px;
background-color: rgba(0, 0, 0, .5); background-color: rgba(0, 0, 0, .5);
transition: opacity .3s; transition: opacity .3s;
cursor: pointer; cursor: pointer;
text-align: center; text-align: center;
line-height: 200px; line-height: 200px;
.el-icon-delete { .el-icon-delete {
font-size: 36px; font-size: 36px;
} }
} }
&:hover { &:hover {
.image-preview-action { .image-preview-action {
opacity: 1; opacity: 1;
} }
} }
} }
.image-app-preview { .image-app-preview {
width: 320px; width: 320px;
height: 180px; height: 180px;
position: relative; position: relative;
border: 1px dashed #d9d9d9; border: 1px dashed #d9d9d9;
float: left; float: left;
margin-left: 50px; margin-left: 50px;
.app-fake-conver { .app-fake-conver {
height: 44px; height: 44px;
position: absolute; position: absolute;
width: 100%; // background: rgba(0, 0, 0, .1); width: 100%; // background: rgba(0, 0, 0, .1);
text-align: center; text-align: center;
line-height: 64px; line-height: 64px;
color: #fff; color: #fff;
} }
} }
} }
</style> </style>

View File

@@ -1,9 +1,9 @@
<template> <template>
<div> <div>
<input id="excel-upload-input" ref="excel-upload-input" type="file" accept=".xlsx, .xls" @change="handleClick"> <input id="excel-upload-input" type="file" accept=".xlsx, .xls" class="c-hide" @change="handkeFileChange">
<div id="drop" @drop="handleDrop" @dragover="handleDragover" @dragenter="handleDragover"> <div id="drop" @drop="handleDrop" @dragover="handleDragover" @dragenter="handleDragover">
Drop excel file here or Drop excel file here or
<el-button :loading="loading" style="margin-left:16px;" size="mini" type="primary" @click="handleUpload">Browse</el-button> <el-button style="margin-left:16px;" size="mini" type="primary" @click="handleUpload">browse</el-button>
</div> </div>
</div> </div>
</template> </template>
@@ -12,10 +12,6 @@
import XLSX from 'xlsx' import XLSX from 'xlsx'
export default { export default {
props: {
beforeUpload: Function, // eslint-disable-line
onSuccess: Function// eslint-disable-line
},
data() { data() {
return { return {
loading: false, loading: false,
@@ -29,24 +25,18 @@ export default {
generateDate({ header, results }) { generateDate({ header, results }) {
this.excelData.header = header this.excelData.header = header
this.excelData.results = results this.excelData.results = results
this.onSuccess && this.onSuccess(this.excelData) this.$emit('on-selected-file', this.excelData)
}, },
handleDrop(e) { handleDrop(e) {
e.stopPropagation() e.stopPropagation()
e.preventDefault() e.preventDefault()
if (this.loading) return
const files = e.dataTransfer.files const files = e.dataTransfer.files
if (files.length !== 1) { if (files.length !== 1) {
this.$message.error('Only support uploading one file!') this.$message.error('Only support uploading one file!')
return return
} }
const rawFile = files[0] // only use files[0] const itemFile = files[0] // only use files[0]
this.readerData(itemFile)
if (!this.isExcel(rawFile)) {
this.$message.error('Only supports upload .xlsx, .xls, .csv suffix files')
return false
}
this.upload(rawFile)
e.stopPropagation() e.stopPropagation()
e.preventDefault() e.preventDefault()
}, },
@@ -58,42 +48,24 @@ export default {
handleUpload() { handleUpload() {
document.getElementById('excel-upload-input').click() document.getElementById('excel-upload-input').click()
}, },
handleClick(e) { handkeFileChange(e) {
const files = e.target.files const files = e.target.files
const rawFile = files[0] // only use files[0] const itemFile = files[0] // only use files[0]
if (!rawFile) return this.readerData(itemFile)
this.upload(rawFile)
}, },
upload(rawFile) { readerData(itemFile) {
this.$refs['excel-upload-input'].value = null // fix can't select the same excel const reader = new FileReader()
reader.onload = e => {
if (!this.beforeUpload) { const data = e.target.result
this.readerData(rawFile) const fixedData = this.fixdata(data)
return 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 })
} }
const before = this.beforeUpload(rawFile) reader.readAsArrayBuffer(itemFile)
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) { fixdata(data) {
let o = '' let o = ''
@@ -115,9 +87,6 @@ export default {
headers.push(hdr) headers.push(hdr)
} }
return headers return headers
},
isExcel(file) {
return /\.(xlsx|xls|csv)$/.test(file.name)
} }
} }
} }

View File

@@ -1,77 +0,0 @@
export default{
bind(el, binding, vnode) {
const dialogHeaderEl = el.querySelector('.el-dialog__header')
const dragDom = el.querySelector('.el-dialog')
dialogHeaderEl.style.cssText += ';cursor:move;'
dragDom.style.cssText += ';top:0px;'
// 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
const getStyle = (function() {
if (window.document.currentStyle) {
return (dom, attr) => dom.currentStyle[attr]
} else {
return (dom, attr) => getComputedStyle(dom, false)[attr]
}
})()
dialogHeaderEl.onmousedown = (e) => {
// 鼠标按下,计算当前元素距离可视区的距离
const disX = e.clientX - dialogHeaderEl.offsetLeft
const disY = e.clientY - dialogHeaderEl.offsetTop
const dragDomWidth = dragDom.offsetWidth
const dragDomheight = dragDom.offsetHeight
const screenWidth = document.body.clientWidth
const screenHeight = document.body.clientHeight
const minDragDomLeft = dragDom.offsetLeft
const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth
const minDragDomTop = dragDom.offsetTop
const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight
// 获取到的值带px 正则匹配替换
let styL = getStyle(dragDom, 'left')
let styT = getStyle(dragDom, 'top')
if (styL.includes('%')) {
styL = +document.body.clientWidth * (+styL.replace(/\%/g, '') / 100)
styT = +document.body.clientHeight * (+styT.replace(/\%/g, '') / 100)
} else {
styL = +styL.replace(/\px/g, '')
styT = +styT.replace(/\px/g, '')
}
document.onmousemove = function(e) {
// 通过事件委托,计算移动的距离
let left = e.clientX - disX
let top = e.clientY - disY
// 边界处理
if (-(left) > minDragDomLeft) {
left = -minDragDomLeft
} else if (left > maxDragDomLeft) {
left = maxDragDomLeft
}
if (-(top) > minDragDomTop) {
top = -minDragDomTop
} else if (top > maxDragDomTop) {
top = maxDragDomTop
}
// 移动当前元素
dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`
// emit onDrag event
vnode.child.$emit('dragDialog')
}
document.onmouseup = function(e) {
document.onmousemove = null
document.onmouseup = null
}
}
}
}

View File

@@ -1,13 +0,0 @@
import drag from './drag'
const install = function(Vue) {
Vue.directive('el-drag-dialog', drag)
}
if (window.Vue) {
window['el-drag-dialog'] = drag
Vue.use(install); // eslint-disable-line
}
drag.install = install
export default drag

View File

@@ -1,13 +0,0 @@
import permission from './permission'
const install = function(Vue) {
Vue.directive('permission', permission)
}
if (window.Vue) {
window['permission'] = permission
Vue.use(install); // eslint-disable-line
}
permission.install = install
export default permission

View File

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

View File

@@ -29,8 +29,8 @@ export default{
ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px' ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px'
break break
default: default:
ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.documentElement.scrollTop || document.body.scrollTop) + 'px' 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.documentElement.scrollLeft || document.body.scrollLeft) + 'px' ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.body.scrollLeft) + 'px'
} }
ripple.style.backgroundColor = opts.color ripple.style.backgroundColor = opts.color
ripple.className = 'waves-ripple z-active' ripple.className = 'waves-ripple z-active'

View File

@@ -1,6 +1,3 @@
// set function parseTime,formatTime to filter
export { parseTime, formatTime } from '@/utils'
function pluralize(time, label) { function pluralize(time, label) {
if (time === 1) { if (time === 1) {
return time + label return time + label
@@ -19,8 +16,67 @@ export function timeAgo(time) {
} }
} }
export function parseTime(time, cFormat) {
if (arguments.length === 0) {
return null
}
if ((time + '').length === 10) {
time = +time * 1000
}
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
let date
if (typeof time === 'object') {
date = time
} else {
date = new Date(parseInt(time))
}
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay()
}
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
let value = formatObj[key]
if (key === 'a') return ['一', '二', '三', '四', '五', '六', '日'][value - 1]
if (result.length > 0 && value < 10) {
value = '0' + value
}
return value || 0
})
return time_str
}
export function formatTime(time, option) {
time = +time * 1000
const d = new Date(time)
const now = Date.now()
const diff = (now - d) / 1000
if (diff < 30) {
return '刚刚'
} else if (diff < 3600) { // less 1 hour
return Math.ceil(diff / 60) + '分钟前'
} else if (diff < 3600 * 24) {
return Math.ceil(diff / 3600) + '小时前'
} else if (diff < 3600 * 24 * 2) {
return '1天前'
}
if (option) {
return parseTime(time, option)
} else {
return d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分'
}
}
/* 数字 格式化*/ /* 数字 格式化*/
export function numberFormatter(num, digits) { export function nFormatter(num, digits) {
const si = [ const si = [
{ value: 1E18, symbol: 'E' }, { value: 1E18, symbol: 'E' },
{ value: 1E15, symbol: 'P' }, { value: 1E15, symbol: 'P' },
@@ -37,6 +93,12 @@ export function numberFormatter(num, digits) {
return num.toString() return num.toString()
} }
export function html2Text(val) {
const div = document.createElement('div')
div.innerHTML = val
return div.textContent || div.innerText
}
export function toThousandslsFilter(num) { export function toThousandslsFilter(num) {
return (+num || 0).toString().replace(/^-?\d+/g, m => m.replace(/(?=(?!\b)(\d{3})+$)/g, ',')) return (+num || 0).toString().replace(/^-?\d+/g, m => m.replace(/(?=(?!\b)(\d{3})+$)/g, ','))
} }

View File

@@ -1 +0,0 @@
<?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="1525760397212" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2919" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M860 504c-19.9 0-36 16.1-36 36 0 1.4 0.1 2.7 0.2 4h-0.2v344H136V200h376c19.9 0 36-16.1 36-36s-16.1-36-36-36H136c-39.8 0-72 32.2-72 72v688c0 39.8 32.2 72 72 72h688c39.8 0 72-32.2 72-72V544h-0.2c0.1-1.3 0.2-2.6 0.2-4 0-19.9-16.1-36-36-36z" p-id="2920"></path><path d="M1002.7 100.3L923.4 21c-28.1-28.1-73.9-27.9-102 0.2L424.2 418.4c-2.9 2.9-5.2 6.4-6.8 10.2L317.6 664c-5.6 13.2-1.7 26.5 6.8 35.1 8.5 8.6 21.9 12.5 35.2 6.9l235.5-99.7c3.8-1.6 7.2-3.9 10.2-6.8l397.2-397.2c28.1-28.1 28.3-73.9 0.2-102zM559.8 543l-137.4 58.2 58.2-137.4L759.4 185l79.2 79.2L559.8 543z m391.7-391.7l-62 62-79.2-79.2 62-62 0.2-0.2 79.2 79.2-0.2 0.2z" p-id="2921"></path></svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1 +0,0 @@
<?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="1526033837694" class="icon" style="" viewBox="0 0 1117 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10274" xmlns:xlink="http://www.w3.org/1999/xlink" width="218.1640625" height="200"><defs><style type="text/css"></style></defs><path d="M53.865 558.08l289.92 121.6 560-492.16-491.52 530.56 371.84 140.8c8.96 3.2 19.2-1.28 22.4-10.24V848l260.48-816.64-1014.4 494.72c-8.96 4.48-12.16 14.72-8.32 23.68 2.56 3.84 5.76 7.04 9.6 8.32z m357.76 434.56l144.64-155.52-144.64-58.88v214.4z" p-id="10275"></path></svg>

Before

Width:  |  Height:  |  Size: 664 B

View File

@@ -1 +0,0 @@
<?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="1525761666409" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10880" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M107.2 212.8m-67.2 0a4.2 4.2 0 1 0 134.4 0 4.2 4.2 0 1 0-134.4 0Z" p-id="10881"></path><path d="M980.8 145.6 297.6 145.6c-9.6 0-16 8-16 16l0 102.4c0 9.6 8 16 16 16l683.2 0c9.6 0 16-8 16-16l0-102.4C996.8 152 988.8 145.6 980.8 145.6z" p-id="10882"></path><path d="M96 497.6m-67.2 0a4.2 4.2 0 1 0 134.4 0 4.2 4.2 0 1 0-134.4 0Z" p-id="10883"></path><path d="M968 430.4 284.8 430.4c-9.6 0-16 8-16 16l0 102.4c0 9.6 8 16 16 16l683.2 0c9.6 0 16-8 16-16l0-102.4C984 438.4 977.6 430.4 968 430.4z" p-id="10884"></path><path d="M96 795.2m-67.2 0a4.2 4.2 0 1 0 134.4 0 4.2 4.2 0 1 0-134.4 0Z" p-id="10885"></path><path d="M968 728 284.8 728c-9.6 0-16 8-16 16l0 102.4c0 9.6 8 16 16 16l683.2 0c9.6 0 16-8 16-16l0-102.4C984 736 977.6 728 968 728z" p-id="10886"></path></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1 +0,0 @@
<?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>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -3,10 +3,7 @@ export default {
dashboard: 'Dashboard', dashboard: 'Dashboard',
introduction: 'Introduction', introduction: 'Introduction',
documentation: 'Documentation', documentation: 'Documentation',
guide: 'Guide',
permission: 'Permission', permission: 'Permission',
pagePermission: 'Page Permission',
directivePermission: 'Directive Permission',
icons: 'Icons', icons: 'Icons',
components: 'Components', components: 'Components',
componentIndex: 'Introduction', componentIndex: 'Introduction',
@@ -21,21 +18,11 @@ export default {
countTo: 'CountTo', countTo: 'CountTo',
componentMixin: 'Mixin', componentMixin: 'Mixin',
backToTop: 'BackToTop', backToTop: 'BackToTop',
dragDialog: 'Drag Dialog',
dragKanban: 'Drag Kanban',
charts: 'Charts', charts: 'Charts',
keyboardChart: 'Keyboard Chart', keyboardChart: 'Keyboard Chart',
lineChart: 'Line Chart', lineChart: 'Line Chart',
mixChart: 'Mix Chart', mixChart: 'Mix Chart',
example: 'Example', 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', Table: 'Table',
dynamicTable: 'Dynamic Table', dynamicTable: 'Dynamic Table',
dragTable: 'Drag Table', dragTable: 'Drag Table',
@@ -45,9 +32,8 @@ export default {
customTreeTable: 'Custom TreeTable', customTreeTable: 'Custom TreeTable',
tab: 'Tab', tab: 'Tab',
form: 'Form', form: 'Form',
createArticle: 'Create Article', createForm: 'Create Form',
editArticle: 'Edit Article', editForm: 'Edit Form',
articleList: 'Article List',
errorPages: 'Error Pages', errorPages: 'Error Pages',
page401: '401', page401: '401',
page404: '404', page404: '404',
@@ -86,10 +72,6 @@ export default {
roles: 'Your roles', roles: 'Your roles',
switchRoles: 'Switch roles' switchRoles: 'Switch roles'
}, },
guide: {
description: 'The guide page is useful for some people who entered the project for the first time. You can briefly introduce the features of the project. Demo is based on ',
button: 'Show Guide'
},
components: { components: {
documentation: 'Documentation', documentation: 'Documentation',
tinymceTips: 'Rich text editor is a core part of management system, but at the same time is a place with lots of problems. In the process of selecting rich texts, I also walked a lot of detours. The common rich text editors in the market are basically used, and the finally chose Tinymce. See documentation for more detailed rich text editor comparisons and introductions.', tinymceTips: 'Rich text editor is a core part of management system, but at the same time is a place with lots of problems. In the process of selecting rich texts, I also walked a lot of detours. The common rich text editors in the market are basically used, and the finally chose Tinymce. See documentation for more detailed rich text editor comparisons and introductions.',
@@ -132,7 +114,7 @@ export default {
}, },
excel: { excel: {
export: 'Export', export: 'Export',
selectedExport: 'Export Selected Items', selectedExport: 'Export selected items',
placeholder: 'Please enter the file name(default excel-list)' placeholder: 'Please enter the file name(default excel-list)'
}, },
zip: { zip: {
@@ -140,13 +122,8 @@ export default {
placeholder: 'Please enter the file name(default file)' placeholder: 'Please enter the file name(default file)'
}, },
theme: { theme: {
change: 'Change Theme', change: 'Theme change',
documentation: 'Theme documentation', documentation: 'Theme documentation',
tips: 'Tips: It is different from the theme-pick on the navbar is two different skinning methods, each with different application scenarios. Refer to the documentation for details.' tips: 'Tips: It is different from the theme-pick on the navbar is two different skinning methods, each with different application scenarios. Refer to the documentation for details.'
},
tagsView: {
close: 'Close',
closeOthers: 'Close Others',
closeAll: 'Close All'
} }
} }

View File

@@ -20,11 +20,8 @@ const messages = {
} }
const i18n = new VueI18n({ const i18n = new VueI18n({
// set locale locale: Cookies.get('language') || 'en', // set locale
// options: en or zh messages // set locale messages
locale: Cookies.get('language') || 'en',
// set locale messages
messages
}) })
export default i18n export default i18n

View File

@@ -3,10 +3,7 @@ export default {
dashboard: '首页', dashboard: '首页',
introduction: '简述', introduction: '简述',
documentation: '文档', documentation: '文档',
guide: '引导页',
permission: '权限测试页', permission: '权限测试页',
pagePermission: '页面权限',
directivePermission: '指令权限',
icons: '图标', icons: '图标',
components: '组件', components: '组件',
componentIndex: '介绍', componentIndex: '介绍',
@@ -21,21 +18,11 @@ export default {
countTo: 'CountTo', countTo: 'CountTo',
componentMixin: '小组件', componentMixin: '小组件',
backToTop: '返回顶部', backToTop: '返回顶部',
dragDialog: '拖拽 Dialog',
dragKanban: '可拖拽看板',
charts: '图表', charts: '图表',
keyboardChart: '键盘图表', keyboardChart: '键盘图表',
lineChart: '折线图', lineChart: '折线图',
mixChart: '混合图表', mixChart: '混合图表',
example: '综合实例', 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', Table: 'Table',
dynamicTable: '动态Table', dynamicTable: '动态Table',
dragTable: '拖拽Table', dragTable: '拖拽Table',
@@ -45,9 +32,8 @@ export default {
customTreeTable: '自定义树表', customTreeTable: '自定义树表',
tab: 'Tab', tab: 'Tab',
form: '表单', form: '表单',
createArticle: '创建文章', createForm: '创建表单',
editArticle: '编辑文章', editForm: '编辑表单',
articleList: '文章列表',
errorPages: '错误页面', errorPages: '错误页面',
page401: '401', page401: '401',
page404: '404', page404: '404',
@@ -86,10 +72,6 @@ export default {
roles: '你的权限', roles: '你的权限',
switchRoles: '切换权限' switchRoles: '切换权限'
}, },
guide: {
description: '引导页对于一些第一次进入项目的人很有用,你可以简单介绍下项目的功能。本 Demo 是基于',
button: '打开引导'
},
components: { components: {
documentation: '文档', documentation: '文档',
tinymceTips: '富文本是管理后台一个核心的功能但同时又是一个有很多坑的地方。在选择富文本的过程中我也走了不少的弯路市面上常见的富文本都基本用过了最终权衡了一下选择了Tinymce。更详细的富文本比较和介绍见', tinymceTips: '富文本是管理后台一个核心的功能但同时又是一个有很多坑的地方。在选择富文本的过程中我也走了不少的弯路市面上常见的富文本都基本用过了最终权衡了一下选择了Tinymce。更详细的富文本比较和介绍见',
@@ -143,10 +125,5 @@ export default {
change: '换肤', change: '换肤',
documentation: '换肤文档', documentation: '换肤文档',
tips: 'Tips: 它区别于 navbar 上的 theme-pick, 是两种不同的换肤方法,各自有不同的应用场景,具体请参考文档。' tips: 'Tips: 它区别于 navbar 上的 theme-pick, 是两种不同的换肤方法,各自有不同的应用场景,具体请参考文档。'
},
tagsView: {
close: '关闭',
closeOthers: '关闭其它',
closeAll: '关闭所有'
} }
} }

View File

@@ -1,6 +1,6 @@
import Vue from 'vue' import Vue from 'vue'
import 'normalize.css/normalize.css' // A modern alternative to CSS resets import 'normalize.css/normalize.css'// A modern alternative to CSS resets
import Element from 'element-ui' import Element from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css' import 'element-ui/lib/theme-chalk/index.css'
@@ -13,7 +13,7 @@ import store from './store'
import i18n from './lang' // Internationalization import i18n from './lang' // Internationalization
import './icons' // icon import './icons' // icon
import './errorLog' // error log import './errorLog'// error log
import './permission' // permission control import './permission' // permission control
import './mock' // simulation data import './mock' // simulation data
@@ -36,5 +36,6 @@ new Vue({
router, router,
store, store,
i18n, i18n,
render: h => h(App) template: '<App/>',
components: { App }
}) })

View File

@@ -4,9 +4,6 @@ import { param2Obj } from '@/utils'
const List = [] const List = []
const count = 100 const count = 100
const baseContent = '<p>我是测试数据我是测试数据</p><p><img src="https://wpimg.wallstcn.com/4c69009c-0fd4-4153-b112-6cb53d1cf943"></p>'
const image_uri = 'https://wpimg.wallstcn.com/e4558086-631c-425c-9430-56ffb46e70b3'
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
List.push(Mock.mock({ List.push(Mock.mock({
id: '@increment', id: '@increment',
@@ -14,17 +11,12 @@ for (let i = 0; i < count; i++) {
author: '@first', author: '@first',
reviewer: '@first', reviewer: '@first',
title: '@title(5, 10)', title: '@title(5, 10)',
content_short: '我是测试数据',
content: baseContent,
forecast: '@float(0, 100, 2, 2)', forecast: '@float(0, 100, 2, 2)',
importance: '@integer(1, 3)', importance: '@integer(1, 3)',
'type|1': ['CN', 'US', 'JP', 'EU'], 'type|1': ['CN', 'US', 'JP', 'EU'],
'status|1': ['published', 'draft', 'deleted'], 'status|1': ['published', 'draft', 'deleted'],
display_time: '@datetime', display_time: '@datetime',
comment_disabled: true, pageviews: '@integer(300, 5000)'
pageviews: '@integer(300, 5000)',
image_uri,
platforms: ['a-platform']
})) }))
} }
@@ -53,14 +45,22 @@ export default {
getPv: () => ({ getPv: () => ({
pvData: [{ key: 'PC', pv: 1024 }, { key: 'mobile', pv: 1024 }, { key: 'ios', pv: 1024 }, { key: 'android', pv: 1024 }] pvData: [{ key: 'PC', pv: 1024 }, { key: 'mobile', pv: 1024 }, { key: 'ios', pv: 1024 }, { key: 'android', pv: 1024 }]
}), }),
getArticle: (config) => { getArticle: () => ({
const { id } = param2Obj(config.url) id: 120000000001,
for (const article of List) { author: { key: 'mockPan' },
if (article.id === +id) { source_name: '原创作者',
return article category_item: [{ key: 'global', name: '全球' }],
} comment_disabled: true,
} content: '<p>我是测试数据我是测试数据</p><p><img class="wscnph" src="https://wpimg.wallstcn.com/4c69009c-0fd4-4153-b112-6cb53d1cf943" data-wscntype="image" data-wscnh="300" data-wscnw="400" data-mce-src="https://wpimg.wallstcn.com/4c69009c-0fd4-4153-b112-6cb53d1cf943"></p>"',
}, content_short: '我是测试数据',
display_time: +new Date(),
image_uri: 'https://wpimg.wallstcn.com/e4558086-631c-425c-9430-56ffb46e70b3',
platforms: ['a-platform'],
source_uri: 'https://github.com/PanJiaChen/vue-element-admin',
status: 'published',
tags: [],
title: 'vue-element-admin'
}),
createArticle: () => ({ createArticle: () => ({
data: 'success' data: 'success'
}), }),

View File

@@ -7,7 +7,7 @@ import { getToken } from '@/utils/auth' // getToken from cookie
NProgress.configure({ showSpinner: false })// NProgress Configuration NProgress.configure({ showSpinner: false })// NProgress Configuration
// permission judge function // permissiom judge function
function hasPermission(roles, permissionRoles) { function hasPermission(roles, permissionRoles) {
if (roles.indexOf('admin') >= 0) return true // admin permission passed directly if (roles.indexOf('admin') >= 0) return true // admin permission passed directly
if (!permissionRoles) return true if (!permissionRoles) return true
@@ -31,16 +31,16 @@ router.beforeEach((to, from, next) => {
router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表 router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
}) })
}).catch((err) => { }).catch(() => {
store.dispatch('FedLogOut').then(() => { store.dispatch('FedLogOut').then(() => {
Message.error(err || 'Verification failed, please login again') Message.error('Verification failed, please login again')
next({ path: '/' }) next({ path: '/login' })
}) })
}) })
} else { } else {
// 没有动态改变权限的需求可直接next() 删除下方权限判断 ↓ // 没有动态改变权限的需求可直接next() 删除下方权限判断 ↓
if (hasPermission(store.getters.roles, to.meta.roles)) { if (hasPermission(store.getters.roles, to.meta.roles)) {
next() next()//
} else { } else {
next({ path: '/401', replace: true, query: { noGoBack: true }}) next({ path: '/401', replace: true, query: { noGoBack: true }})
} }

View File

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

View File

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

View File

@@ -1,20 +1,17 @@
import Vue from 'vue' import Vue from 'vue'
import Router from 'vue-router' import Router from 'vue-router'
const _import = require('./_import_' + process.env.NODE_ENV)
// in development-env not use lazy-loading, because lazy-loading too many pages will cause webpack hot update too slow. so only in production use lazy-loading;
// detail: https://panjiachen.github.io/vue-element-admin-site/#/lazy-loading
Vue.use(Router) Vue.use(Router)
/* Layout */ /* Layout */
import Layout from '@/views/layout/Layout' import Layout from '../views/layout/Layout'
/* Router Modules */
import componentsRouter from './modules/components'
import chartsRouter from './modules/charts'
import tableRouter from './modules/table'
import nestedRouter from './modules/nested'
/** note: submenu only apppear when children.length>=1 /** note: submenu only apppear when children.length>=1
* detail see https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html * detail see https://panjiachen.github.io/vue-element-admin-site/#/router-and-nav?id=sidebar
**/ **/
/** /**
* hidden: true if `hidden:true` will not show in the sidebar(default is false) * hidden: true if `hidden:true` will not show in the sidebar(default is false)
@@ -27,68 +24,35 @@ import nestedRouter from './modules/nested'
roles: ['admin','editor'] will control the page roles (you can set multiple roles) roles: ['admin','editor'] will control the page roles (you can set multiple roles)
title: 'title' the name show in submenu and breadcrumb (recommend set) title: 'title' the name show in submenu and breadcrumb (recommend set)
icon: 'svg-name' the icon show in the sidebar, icon: 'svg-name' the icon show in the sidebar,
noCache: true if true ,the page will no be cached(default is false) noCache: true if fasle ,the page will no be cached(default is false)
} }
**/ **/
export const constantRouterMap = [ export const constantRouterMap = [
{ { path: '/login', component: _import('login/index'), hidden: true },
path: '/login', { path: '/authredirect', component: _import('login/authredirect'), hidden: true },
component: () => import('@/views/login/index'), { path: '/404', component: _import('errorPage/404'), hidden: true },
hidden: true { path: '/401', component: _import('errorPage/401'), hidden: true },
},
{
path: '/authredirect',
component: () => import('@/views/login/authredirect'),
hidden: true
},
{
path: '/404',
component: () => import('@/views/errorPage/404'),
hidden: true
},
{
path: '/401',
component: () => import('@/views/errorPage/401'),
hidden: true
},
{ {
path: '', path: '',
component: Layout, component: Layout,
redirect: 'dashboard', redirect: 'dashboard',
children: [ children: [{
{ path: 'dashboard',
path: 'dashboard', component: _import('dashboard/index'),
component: () => import('@/views/dashboard/index'), name: 'dashboard',
name: 'Dashboard', meta: { title: 'dashboard', icon: 'dashboard', noCache: true }
meta: { title: 'dashboard', icon: 'dashboard', noCache: true } }]
}
]
}, },
{ {
path: '/documentation', path: '/documentation',
component: Layout, component: Layout,
redirect: '/documentation/index', redirect: '/documentation/index',
children: [ children: [{
{ path: 'index',
path: 'index', component: _import('documentation/index'),
component: () => import('@/views/documentation/index'), name: 'documentation',
name: 'Documentation', meta: { title: 'documentation', icon: 'documentation', noCache: true }
meta: { title: 'documentation', icon: 'documentation', noCache: true } }]
}
]
},
{
path: '/guide',
component: Layout,
redirect: '/guide/index',
children: [
{
path: 'index',
component: () => import('@/views/guide/index'),
name: 'Guide',
meta: { title: 'guide', icon: 'guide', noCache: true }
}
]
} }
] ]
@@ -103,95 +67,114 @@ export const asyncRouterMap = [
path: '/permission', path: '/permission',
component: Layout, component: Layout,
redirect: '/permission/index', redirect: '/permission/index',
alwaysShow: true, // will always show the root menu meta: { roles: ['admin'] }, // you can set roles in root nav
meta: { children: [{
title: 'permission', path: 'index',
icon: 'lock', component: _import('permission/index'),
roles: ['admin', 'editor'] // you can set roles in root nav name: 'permission',
}, meta: {
children: [ title: 'permission',
{ icon: 'lock',
path: 'page', roles: ['admin'] // or you can only set roles in sub nav
component: () => import('@/views/permission/page'),
name: 'PagePermission',
meta: {
title: 'pagePermission',
roles: ['admin'] // or you can only set roles in sub nav
}
},
{
path: 'directive',
component: () => import('@/views/permission/directive'),
name: 'DirectivePermission',
meta: {
title: 'directivePermission'
// if do not set roles, means: this page does not require permission
}
} }
] }]
}, },
{ {
path: '/icon', path: '/icon',
component: Layout, component: Layout,
children: [{
path: 'index',
component: _import('svg-icons/index'),
name: 'icons',
meta: { title: 'icons', icon: 'icon', noCache: true }
}]
},
{
path: '/components',
component: Layout,
redirect: 'noredirect',
name: 'component-demo',
meta: {
title: 'components',
icon: 'component'
},
children: [ children: [
{ { path: 'tinymce', component: _import('components-demo/tinymce'), name: 'tinymce-demo', meta: { title: 'tinymce' }},
path: 'index', { path: 'markdown', component: _import('components-demo/markdown'), name: 'markdown-demo', meta: { title: 'markdown' }},
component: () => import('@/views/svg-icons/index'), { path: 'json-editor', component: _import('components-demo/jsonEditor'), name: 'jsonEditor-demo', meta: { title: 'jsonEditor' }},
name: 'Icons', { path: 'dnd-list', component: _import('components-demo/dndList'), name: 'dndList-demo', meta: { title: 'dndList' }},
meta: { title: 'icons', icon: 'icon', noCache: true } { path: 'splitpane', component: _import('components-demo/splitpane'), name: 'splitpane-demo', meta: { title: 'splitPane' }},
} { path: 'avatar-upload', component: _import('components-demo/avatarUpload'), name: 'avatarUpload-demo', meta: { title: 'avatarUpload' }},
{ path: 'dropzone', component: _import('components-demo/dropzone'), name: 'dropzone-demo', meta: { title: 'dropzone' }},
{ path: 'sticky', component: _import('components-demo/sticky'), name: 'sticky-demo', meta: { title: 'sticky' }},
{ path: 'count-to', component: _import('components-demo/countTo'), name: 'countTo-demo', meta: { title: 'countTo' }},
{ path: 'mixin', component: _import('components-demo/mixin'), name: 'componentMixin-demo', meta: { title: 'componentMixin' }},
{ path: 'back-to-top', component: _import('components-demo/backToTop'), name: 'backToTop-demo', meta: { title: 'backToTop' }}
] ]
}, },
/** When your routing table is too long, you can split it into small modules**/ {
componentsRouter, path: '/charts',
chartsRouter, component: Layout,
nestedRouter, redirect: 'noredirect',
tableRouter, name: 'charts',
meta: {
title: 'charts',
icon: 'chart'
},
children: [
{ path: 'keyboard', component: _import('charts/keyboard'), name: 'keyboardChart', meta: { title: 'keyboardChart', noCache: true }},
{ path: 'line', component: _import('charts/line'), name: 'lineChart', meta: { title: 'lineChart', noCache: true }},
{ path: 'mixchart', component: _import('charts/mixChart'), name: 'mixChart', meta: { title: 'mixChart', noCache: true }}
]
},
{ {
path: '/example', path: '/example',
component: Layout, component: Layout,
redirect: '/example/list', redirect: '/example/table/complex-table',
name: 'Example', name: 'example',
meta: { meta: {
title: 'example', title: 'example',
icon: 'example' icon: 'example'
}, },
children: [ children: [
{ {
path: 'create', path: '/example/table',
component: () => import('@/views/example/create'), component: _import('example/table/index'),
name: 'CreateArticle', redirect: '/example/table/complex-table',
meta: { title: 'createArticle', icon: 'edit' } name: 'Table',
meta: {
title: 'Table',
icon: 'table'
},
children: [
{ path: 'dynamic-table', component: _import('example/table/dynamicTable/index'), name: 'dynamicTable', meta: { title: 'dynamicTable' }},
{ path: 'drag-table', component: _import('example/table/dragTable'), name: 'dragTable', meta: { title: 'dragTable' }},
{ path: 'inline-edit-table', component: _import('example/table/inlineEditTable'), name: 'inlineEditTable', meta: { title: 'inlineEditTable' }},
{ path: 'tree-table', component: _import('example/table/treeTable/treeTable'), name: 'treeTableDemo', meta: { title: 'treeTable' }},
{ path: 'custom-tree-table', component: _import('example/table/treeTable/customTreeTable'), name: 'customTreeTableDemo', meta: { title: 'customTreeTable' }},
{ path: 'complex-table', component: _import('example/table/complexTable'), name: 'complexTable', meta: { title: 'complexTable' }}
]
}, },
{ { path: 'tab/index', icon: 'tab', component: _import('example/tab/index'), name: 'tab', meta: { title: 'tab' }}
path: 'edit/:id(\\d+)',
component: () => import('@/views/example/edit'),
name: 'EditArticle',
meta: { title: 'editArticle', noCache: true },
hidden: true
},
{
path: 'list',
component: () => import('@/views/example/list'),
name: 'ArticleList',
meta: { title: 'articleList', icon: 'list' }
}
] ]
}, },
{ {
path: '/tab', path: '/form',
component: Layout, component: Layout,
redirect: 'noredirect',
name: 'form',
meta: {
title: 'form',
icon: 'form'
},
children: [ children: [
{ { path: 'create-form', component: _import('form/create'), name: 'createForm', meta: { title: 'createForm', icon: 'table' }},
path: 'index', { path: 'edit-form', component: _import('form/edit'), name: 'editForm', meta: { title: 'editForm', icon: 'table' }}
component: () => import('@/views/tab/index'),
name: 'Tab',
meta: { title: 'tab', icon: 'tab' }
}
] ]
}, },
@@ -199,24 +182,14 @@ export const asyncRouterMap = [
path: '/error', path: '/error',
component: Layout, component: Layout,
redirect: 'noredirect', redirect: 'noredirect',
name: 'ErrorPages', name: 'errorPages',
meta: { meta: {
title: 'errorPages', title: 'errorPages',
icon: '404' icon: '404'
}, },
children: [ children: [
{ { path: '401', component: _import('errorPage/401'), name: 'page401', meta: { title: 'page401', noCache: true }},
path: '401', { path: '404', component: _import('errorPage/404'), name: 'page404', meta: { title: 'page404', noCache: true }}
component: () => import('@/views/errorPage/401'),
name: 'Page401',
meta: { title: 'page401', noCache: true }
},
{
path: '404',
component: () => import('@/views/errorPage/404'),
name: 'Page404',
meta: { title: 'page404', noCache: true }
}
] ]
}, },
@@ -224,44 +197,22 @@ export const asyncRouterMap = [
path: '/error-log', path: '/error-log',
component: Layout, component: Layout,
redirect: 'noredirect', redirect: 'noredirect',
children: [ children: [{ path: 'log', component: _import('errorLog/index'), name: 'errorLog', meta: { title: 'errorLog', icon: 'bug' }}]
{
path: 'log',
component: () => import('@/views/errorLog/index'),
name: 'ErrorLog',
meta: { title: 'errorLog', icon: 'bug' }
}
]
}, },
{ {
path: '/excel', path: '/excel',
component: Layout, component: Layout,
redirect: '/excel/export-excel', redirect: '/excel/export-excel',
name: 'Excel', name: 'excel',
meta: { meta: {
title: 'excel', title: 'excel',
icon: 'excel' icon: 'excel'
}, },
children: [ children: [
{ { path: 'export-excel', component: _import('excel/exportExcel'), name: 'exportExcel', meta: { title: 'exportExcel' }},
path: 'export-excel', { path: 'export-selected-excel', component: _import('excel/selectExcel'), name: 'selectExcel', meta: { title: 'selectExcel' }},
component: () => import('@/views/excel/exportExcel'), { path: 'upload-excel', component: _import('excel/uploadExcel'), name: 'uploadExcel', meta: { title: 'uploadExcel' }}
name: 'ExportExcel',
meta: { title: 'exportExcel' }
},
{
path: 'export-selected-excel',
component: () => import('@/views/excel/selectExcel'),
name: 'EelectExcel',
meta: { title: 'selectExcel' }
},
{
path: 'upload-excel',
component: () => import('@/views/excel/uploadExcel'),
name: 'UploadExcel',
meta: { title: 'uploadExcel' }
}
] ]
}, },
@@ -271,55 +222,27 @@ export const asyncRouterMap = [
redirect: '/zip/download', redirect: '/zip/download',
alwaysShow: true, alwaysShow: true,
meta: { title: 'zip', icon: 'zip' }, meta: { title: 'zip', icon: 'zip' },
children: [ children: [{ path: 'download', component: _import('zip/index'), name: 'exportZip', meta: { title: 'exportZip' }}]
{
path: 'download',
component: () => import('@/views/zip/index'),
name: 'ExportZip',
meta: { title: 'exportZip' }
}
]
}, },
{ {
path: '/theme', path: '/theme',
component: Layout, component: Layout,
redirect: 'noredirect', redirect: 'noredirect',
children: [ children: [{ path: 'index', component: _import('theme/index'), name: 'theme', meta: { title: 'theme', icon: 'theme' }}]
{
path: 'index',
component: () => import('@/views/theme/index'),
name: 'Theme',
meta: { title: 'theme', icon: 'theme' }
}
]
}, },
{ {
path: '/clipboard', path: '/clipboard',
component: Layout, component: Layout,
redirect: 'noredirect', redirect: 'noredirect',
children: [ children: [{ path: 'index', component: _import('clipboard/index'), name: 'clipboardDemo', meta: { title: 'clipboardDemo', icon: 'clipboard' }}]
{
path: 'index',
component: () => import('@/views/clipboard/index'),
name: 'ClipboardDemo',
meta: { title: 'clipboardDemo', icon: 'clipboard' }
}
]
}, },
{ {
path: '/i18n', path: '/i18n',
component: Layout, component: Layout,
children: [ children: [{ path: 'index', component: _import('i18n-demo/index'), name: 'i18n', meta: { title: 'i18n', icon: 'international' }}]
{
path: 'index',
component: () => import('@/views/i18n-demo/index'),
name: 'I18n',
meta: { title: 'i18n', icon: 'international' }
}
]
}, },
{ path: '*', redirect: '/404', hidden: true } { path: '*', redirect: '/404', hidden: true }

View File

@@ -1,36 +0,0 @@
/** When your routing table is too long, you can split it into small modules**/
import Layout from '@/views/layout/Layout'
const chartsRouter = {
path: '/charts',
component: Layout,
redirect: 'noredirect',
name: 'Charts',
meta: {
title: 'charts',
icon: 'chart'
},
children: [
{
path: 'keyboard',
component: () => import('@/views/charts/keyboard'),
name: 'KeyboardChart',
meta: { title: 'keyboardChart', noCache: true }
},
{
path: 'line',
component: () => import('@/views/charts/line'),
name: 'LineChart',
meta: { title: 'lineChart', noCache: true }
},
{
path: 'mixchart',
component: () => import('@/views/charts/mixChart'),
name: 'MixChart',
meta: { title: 'mixChart', noCache: true }
}
]
}
export default chartsRouter

View File

@@ -1,96 +0,0 @@
/** When your routing table is too long, you can split it into small modules**/
import Layout from '@/views/layout/Layout'
const componentsRouter = {
path: '/components',
component: Layout,
redirect: 'noredirect',
name: 'ComponentDemo',
meta: {
title: 'components',
icon: 'component'
},
children: [
{
path: 'tinymce',
component: () => import('@/views/components-demo/tinymce'),
name: 'TinymceDemo',
meta: { title: 'tinymce' }
},
{
path: 'markdown',
component: () => import('@/views/components-demo/markdown'),
name: 'MarkdownDemo',
meta: { title: 'markdown' }
},
{
path: 'json-editor',
component: () => import('@/views/components-demo/jsonEditor'),
name: 'JsonEditorDemo',
meta: { title: 'jsonEditor' }
},
{
path: 'splitpane',
component: () => import('@/views/components-demo/splitpane'),
name: 'SplitpaneDemo',
meta: { title: 'splitPane' }
},
{
path: 'avatar-upload',
component: () => import('@/views/components-demo/avatarUpload'),
name: 'AvatarUploadDemo',
meta: { title: 'avatarUpload' }
},
{
path: 'dropzone',
component: () => import('@/views/components-demo/dropzone'),
name: 'DropzoneDemo',
meta: { title: 'dropzone' }
},
{
path: 'sticky',
component: () => import('@/views/components-demo/sticky'),
name: 'StickyDemo',
meta: { title: 'sticky' }
},
{
path: 'count-to',
component: () => import('@/views/components-demo/countTo'),
name: 'CountToDemo',
meta: { title: 'countTo' }
},
{
path: 'mixin',
component: () => import('@/views/components-demo/mixin'),
name: 'ComponentMixinDemo',
meta: { title: 'componentMixin' }
},
{
path: 'back-to-top',
component: () => import('@/views/components-demo/backToTop'),
name: 'BackToTopDemo',
meta: { title: 'backToTop' }
},
{
path: 'drag-dialog',
component: () => import('@/views/components-demo/dragDialog'),
name: 'DragDialogDemo',
meta: { title: 'dragDialog' }
},
{
path: 'dnd-list',
component: () => import('@/views/components-demo/dndList'),
name: 'DndListDemo',
meta: { title: 'dndList' }
},
{
path: 'drag-kanban',
component: () => import('@/views/components-demo/dragKanban'),
name: 'DragKanbanDemo',
meta: { title: 'dragKanban' }
}
]
}
export default componentsRouter

View File

@@ -1,66 +0,0 @@
/** When your routing table is too long, you can split it into small modules**/
import Layout from '@/views/layout/Layout'
const nestedRouter = {
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' }
}
]
}
export default nestedRouter

View File

@@ -1,53 +0,0 @@
/** When your routing table is too long, you can split it into small modules**/
import Layout from '@/views/layout/Layout'
const tableRouter = {
path: '/table',
component: Layout,
redirect: '/table/complex-table',
name: 'Table',
meta: {
title: 'Table',
icon: 'table'
},
children: [
{
path: 'dynamic-table',
component: () => import('@/views/table/dynamicTable/index'),
name: 'DynamicTable',
meta: { title: 'dynamicTable' }
},
{
path: 'drag-table',
component: () => import('@/views/table/dragTable'),
name: 'DragTable',
meta: { title: 'dragTable' }
},
{
path: 'inline-edit-table',
component: () => import('@/views/table/inlineEditTable'),
name: 'InlineEditTable',
meta: { title: 'inlineEditTable' }
},
{
path: 'tree-table',
component: () => import('@/views/table/treeTable/treeTable'),
name: 'TreeTableDemo',
meta: { title: 'treeTable' }
},
{
path: 'custom-tree-table',
component: () => import('@/views/table/treeTable/customTreeTable'),
name: 'CustomTreeTableDemo',
meta: { title: 'customTreeTable' }
},
{
path: 'complex-table',
component: () => import('@/views/table/complexTable'),
name: 'ComplexTable',
meta: { title: 'complexTable' }
}
]
}
export default tableRouter

View File

@@ -1,7 +1,6 @@
const getters = { const getters = {
sidebar: state => state.app.sidebar, sidebar: state => state.app.sidebar,
language: state => state.app.language, language: state => state.app.language,
device: state => state.app.device,
visitedViews: state => state.tagsView.visitedViews, visitedViews: state => state.tagsView.visitedViews,
cachedViews: state => state.tagsView.cachedViews, cachedViews: state => state.tagsView.cachedViews,
token: state => state.user.token, token: state => state.user.token,

View File

@@ -3,10 +3,8 @@ import Cookies from 'js-cookie'
const app = { const app = {
state: { state: {
sidebar: { sidebar: {
opened: !+Cookies.get('sidebarStatus'), opened: !+Cookies.get('sidebarStatus')
withoutAnimation: false
}, },
device: 'desktop',
language: Cookies.get('language') || 'en' language: Cookies.get('language') || 'en'
}, },
mutations: { mutations: {
@@ -17,15 +15,6 @@ const app = {
Cookies.set('sidebarStatus', 0) Cookies.set('sidebarStatus', 0)
} }
state.sidebar.opened = !state.sidebar.opened state.sidebar.opened = !state.sidebar.opened
state.sidebar.withoutAnimation = false
},
CLOSE_SIDEBAR: (state, withoutAnimation) => {
Cookies.set('sidebarStatus', 1)
state.sidebar.opened = false
state.sidebar.withoutAnimation = withoutAnimation
},
TOGGLE_DEVICE: (state, device) => {
state.device = device
}, },
SET_LANGUAGE: (state, language) => { SET_LANGUAGE: (state, language) => {
state.language = language state.language = language
@@ -36,12 +25,6 @@ const app = {
toggleSideBar({ commit }) { toggleSideBar({ commit }) {
commit('TOGGLE_SIDEBAR') commit('TOGGLE_SIDEBAR')
}, },
closeSideBar({ commit }, { withoutAnimation }) {
commit('CLOSE_SIDEBAR', withoutAnimation)
},
toggleDevice({ commit }, device) {
commit('TOGGLE_DEVICE', device)
},
setLanguage({ commit }, language) { setLanguage({ commit }, language) {
commit('SET_LANGUAGE', language) commit('SET_LANGUAGE', language)
} }

View File

@@ -6,11 +6,11 @@ const tagsView = {
mutations: { mutations: {
ADD_VISITED_VIEWS: (state, view) => { ADD_VISITED_VIEWS: (state, view) => {
if (state.visitedViews.some(v => v.path === view.path)) return if (state.visitedViews.some(v => v.path === view.path)) return
state.visitedViews.push( state.visitedViews.push({
Object.assign({}, view, { name: view.name,
title: view.meta.title || 'no-name' path: view.path,
}) title: view.meta.title || 'no-name'
) })
if (!view.meta.noCache) { if (!view.meta.noCache) {
state.cachedViews.push(view.name) state.cachedViews.push(view.name)
} }
@@ -40,12 +40,12 @@ const tagsView = {
for (const i of state.cachedViews) { for (const i of state.cachedViews) {
if (i === view.name) { if (i === view.name) {
const index = state.cachedViews.indexOf(i) const index = state.cachedViews.indexOf(i)
state.cachedViews = state.cachedViews.slice(index, index + 1) state.cachedViews = state.cachedViews.slice(index, i + 1)
break break
} }
} }
}, },
DEL_ALL_VIEWS: state => { DEL_ALL_VIEWS: (state) => {
state.visitedViews = [] state.visitedViews = []
state.cachedViews = [] state.cachedViews = []
} }
@@ -55,19 +55,19 @@ const tagsView = {
commit('ADD_VISITED_VIEWS', view) commit('ADD_VISITED_VIEWS', view)
}, },
delVisitedViews({ commit, state }, view) { delVisitedViews({ commit, state }, view) {
return new Promise(resolve => { return new Promise((resolve) => {
commit('DEL_VISITED_VIEWS', view) commit('DEL_VISITED_VIEWS', view)
resolve([...state.visitedViews]) resolve([...state.visitedViews])
}) })
}, },
delOthersViews({ commit, state }, view) { delOthersViews({ commit, state }, view) {
return new Promise(resolve => { return new Promise((resolve) => {
commit('DEL_OTHERS_VIEWS', view) commit('DEL_OTHERS_VIEWS', view)
resolve([...state.visitedViews]) resolve([...state.visitedViews])
}) })
}, },
delAllViews({ commit, state }) { delAllViews({ commit, state }) {
return new Promise(resolve => { return new Promise((resolve) => {
commit('DEL_ALL_VIEWS') commit('DEL_ALL_VIEWS')
resolve([...state.visitedViews]) resolve([...state.visitedViews])
}) })

View File

@@ -67,13 +67,7 @@ const user = {
reject('error') reject('error')
} }
const data = response.data const data = response.data
commit('SET_ROLES', data.roles)
if (data.roles && data.roles.length > 0) { // 验证返回的roles是否是一个非空数组
commit('SET_ROLES', data.roles)
} else {
reject('getInfo: roles must be a non-null array !')
}
commit('SET_NAME', data.name) commit('SET_NAME', data.name)
commit('SET_AVATAR', data.avatar) commit('SET_AVATAR', data.avatar)
commit('SET_INTRODUCTION', data.introduction) commit('SET_INTRODUCTION', data.introduction)

View File

@@ -46,6 +46,7 @@
border-radius: 8px; border-radius: 8px;
border: none; border: none;
outline: none; outline: none;
margin-right: 25px;
transition: 600ms ease all; transition: 600ms ease all;
position: relative; position: relative;
display: inline-block; display: inline-block;

View File

@@ -44,7 +44,7 @@
} }
} }
//暂时性解决dialog 问题 https://github.com/ElemeFE/element/issues/2461 //暂时性解决diolag 问题 https://github.com/ElemeFE/element/issues/2461
.el-dialog { .el-dialog {
transform: none; transform: none;
left: 0; left: 0;
@@ -73,10 +73,3 @@
} }
} }
} }
//dropdown
.el-dropdown-menu{
a{
display: block
}
}

View File

@@ -2,118 +2,99 @@
// 主体区域 // 主体区域
.main-container { .main-container {
min-height: 100%; min-height: 100%;
transition: margin-left .28s; transition: margin-left 0.28s;
margin-left: 180px; margin-left: 180px;
position: relative; } // 侧边栏
}
// 侧边栏
.sidebar-container { .sidebar-container {
transition: width 0.28s; transition: width 0.28s;
width: 180px !important; width: 180px!important;
height: 100%; height: 100%;
position: fixed; position: fixed;
font-size: 0px;
top: 0; top: 0;
bottom: 0; bottom: 0;
left: 0; left: 0;
z-index: 1001; z-index: 1001;
overflow: hidden;
//reset element-ui css
.horizontal-collapse-transition {
transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;
}
.scrollbar-wrapper {
overflow-x: hidden!important;
.el-scrollbar__view {
height: 100%;
}
}
.is-horizontal {
display: none;
}
a { a {
display: inline-block; display: inline-block;
width: 100%; width: 100%;
overflow: hidden;
} }
.svg-icon { .svg-icon {
margin-right: 16px; margin-right: 16px;
} }
.el-menu { .el-menu {
border: none; border: none;
height: 100%; width: 100%;
width: 100% !important;
} }
} }
.hideSidebar { .hideSidebar {
.sidebar-container { .sidebar-container,.sidebar-container .el-menu {
width: 36px !important; width: 36px!important;
// overflow: inherit;
} }
.main-container { .main-container {
margin-left: 36px; margin-left: 36px;
} }
}
.hideSidebar {
.submenu-title-noDropdown { .submenu-title-noDropdown {
padding-left: 10px !important; padding-left: 10px!important;
position: relative; position: relative;
.el-tooltip { span {
padding: 0 10px !important; height: 0;
width: 0;
overflow: hidden;
visibility: hidden;
transition: opacity .3s cubic-bezier(.55, 0, .1, 1);
opacity: 0;
display: inline-block;
}
&:hover {
span {
display: block;
border-radius: 3px;
z-index: 1002;
width: 140px;
height: 56px;
visibility: visible;
position: absolute;
right: -145px;
text-align: left;
text-indent: 20px;
top: 0px;
background-color: $subMenuBg!important;
opacity: 1;
}
} }
} }
.el-submenu { .el-submenu {
overflow: hidden;
&>.el-submenu__title { &>.el-submenu__title {
padding-left: 10px !important; padding-left: 10px!important;
&>span {
display: none;
}
.el-submenu__icon-arrow { .el-submenu__icon-arrow {
display: none; display: none;
} }
} }
} .nest-menu {
.el-menu--collapse { .el-submenu__icon-arrow {
.el-submenu { display: block!important;
&>.el-submenu__title { }
&>span { span {
height: 0; display: inline-block!important;
width: 0;
overflow: hidden;
visibility: hidden;
display: inline-block;
}
} }
} }
} }
} }
.sidebar-container .nest-menu .el-submenu>.el-submenu__title, .nest-menu .el-submenu>.el-submenu__title,
.sidebar-container .el-submenu .el-menu-item { .el-submenu .el-menu-item {
min-width: 180px !important; min-width: 180px!important;
background-color: $subMenuBg !important; background-color: $subMenuBg!important;
&:hover { &:hover {
background-color: $menuHover !important; background-color: $menuHover!important;
} }
} }
.el-menu--collapse .el-menu .el-submenu { .el-menu--collapse .el-menu .el-submenu{
min-width: 180px !important; min-width: 180px!important;
}
//适配移动端
.mobile {
.main-container {
margin-left: 0px;
}
.sidebar-container {
transition: transform .28s;
width: 180px !important;
}
&.hideSidebar {
.sidebar-container {
transition-duration: 0.3s;
transform: translate3d(-180px, 0, 0);
}
}
}
.withoutAnimation {
.main-container,
.sidebar-container {
transition: none;
}
} }
} }

View File

@@ -11,21 +11,7 @@
opacity: 0; opacity: 0;
} }
/*fade-transform*/ /*fade*/
.fade-transform-leave-active,
.fade-transform-enter-active {
transition: all .5s;
}
.fade-transform-enter {
opacity: 0;
transform: translateX(-30px);
}
.fade-transform-leave-to {
opacity: 0;
transform: translateX(30px);
}
/*breadcrumb transition*/
.breadcrumb-enter-active, .breadcrumb-enter-active,
.breadcrumb-leave-active { .breadcrumb-leave-active {
transition: all .5s; transition: all .5s;

View File

@@ -25,7 +25,7 @@ export function parseTime(time, cFormat) {
} }
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => { const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
let value = formatObj[key] let value = formatObj[key]
if (key === 'a') { return ['一', '二', '三', '四', '五', '六', '日'][value - 1] } if (key === 'a') return ['一', '二', '三', '四', '五', '六', '日'][value - 1]
if (result.length > 0 && value < 10) { if (result.length > 0 && value < 10) {
value = '0' + value value = '0' + value
} }
@@ -43,8 +43,7 @@ export function formatTime(time, option) {
if (diff < 30) { if (diff < 30) {
return '刚刚' return '刚刚'
} else if (diff < 3600) { } else if (diff < 3600) { // less 1 hour
// less 1 hour
return Math.ceil(diff / 60) + '分钟前' return Math.ceil(diff / 60) + '分钟前'
} else if (diff < 3600 * 24) { } else if (diff < 3600 * 24) {
return Math.ceil(diff / 3600) + '小时前' return Math.ceil(diff / 3600) + '小时前'
@@ -54,17 +53,7 @@ export function formatTime(time, option) {
if (option) { if (option) {
return parseTime(time, option) return parseTime(time, option)
} else { } else {
return ( return d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分'
d.getMonth() +
1 +
'月' +
d.getDate() +
'日' +
d.getHours() +
'时' +
d.getMinutes() +
'分'
)
} }
} }
@@ -92,11 +81,9 @@ export function getQueryObject(url) {
export function getByteLen(val) { export function getByteLen(val) {
let len = 0 let len = 0
for (let i = 0; i < val.length; i++) { for (let i = 0; i < val.length; i++) {
if (val[i].match(/[^\x00-\xff]/gi) != null) { if (val[i].match(/[^\x00-\xff]/ig) != null) {
len += 1 len += 1
} else { } else { len += 0.5 }
len += 0.5
}
} }
return Math.floor(len) return Math.floor(len)
} }
@@ -113,12 +100,11 @@ export function cleanArray(actual) {
export function param(json) { export function param(json) {
if (!json) return '' if (!json) return ''
return cleanArray( return cleanArray(Object.keys(json).map(key => {
Object.keys(json).map(key => { if (json[key] === undefined) return ''
if (json[key] === undefined) return '' return encodeURIComponent(key) + '=' +
return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]) encodeURIComponent(json[key])
}) })).join('&')
).join('&')
} }
export function param2Obj(url) { export function param2Obj(url) {
@@ -126,14 +112,7 @@ export function param2Obj(url) {
if (!search) { if (!search) {
return {} return {}
} }
return JSON.parse( return JSON.parse('{"' + decodeURIComponent(search).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g, '":"') + '"}')
'{"' +
decodeURIComponent(search)
.replace(/"/g, '\\"')
.replace(/&/g, '","')
.replace(/=/g, '":"') +
'"}'
)
} }
export function html2Text(val) { export function html2Text(val) {
@@ -152,21 +131,23 @@ export function objectMerge(target, source) {
if (Array.isArray(source)) { if (Array.isArray(source)) {
return source.slice() return source.slice()
} }
Object.keys(source).forEach(property => { for (const property in source) {
const sourceProperty = source[property] if (source.hasOwnProperty(property)) {
if (typeof sourceProperty === 'object') { const sourceProperty = source[property]
target[property] = objectMerge(target[property], sourceProperty) if (typeof sourceProperty === 'object') {
} else { target[property] = objectMerge(target[property], sourceProperty)
continue
}
target[property] = sourceProperty target[property] = sourceProperty
} }
}) }
return target return target
} }
export function scrollTo(element, to, duration) { export function scrollTo(element, to, duration) {
if (duration <= 0) return if (duration <= 0) return
const difference = to - element.scrollTop const difference = to - element.scrollTop
const perTick = (difference / duration) * 10 const perTick = difference / duration * 10
setTimeout(() => { setTimeout(() => {
console.log(new Date()) console.log(new Date())
element.scrollTop = element.scrollTop + perTick element.scrollTop = element.scrollTop + perTick
@@ -184,9 +165,7 @@ export function toggleClass(element, className) {
if (nameIndex === -1) { if (nameIndex === -1) {
classString += '' + className classString += '' + className
} else { } else {
classString = classString = classString.substr(0, nameIndex) + classString.substr(nameIndex + className.length)
classString.substr(0, nameIndex) +
classString.substr(nameIndex + className.length)
} }
element.className = classString element.className = classString
} }
@@ -200,8 +179,7 @@ export const pickerOptions = [
end.setTime(start.getTime()) end.setTime(start.getTime())
picker.$emit('pick', [start, end]) picker.$emit('pick', [start, end])
} }
}, }, {
{
text: '最近一周', text: '最近一周',
onClick(picker) { onClick(picker) {
const end = new Date(new Date().toDateString()) const end = new Date(new Date().toDateString())
@@ -209,8 +187,7 @@ export const pickerOptions = [
start.setTime(end.getTime() - 3600 * 1000 * 24 * 7) start.setTime(end.getTime() - 3600 * 1000 * 24 * 7)
picker.$emit('pick', [start, end]) picker.$emit('pick', [start, end])
} }
}, }, {
{
text: '最近一个月', text: '最近一个月',
onClick(picker) { onClick(picker) {
const end = new Date(new Date().toDateString()) const end = new Date(new Date().toDateString())
@@ -218,8 +195,7 @@ export const pickerOptions = [
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30) start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
picker.$emit('pick', [start, end]) picker.$emit('pick', [start, end])
} }
}, }, {
{
text: '最近三个月', text: '最近三个月',
onClick(picker) { onClick(picker) {
const end = new Date(new Date().toDateString()) const end = new Date(new Date().toDateString())
@@ -227,8 +203,7 @@ export const pickerOptions = [
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90) start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
picker.$emit('pick', [start, end]) picker.$emit('pick', [start, end])
} }
} }]
]
export function getTime(type) { export function getTime(type) {
if (type === 'start') { if (type === 'start') {
@@ -273,26 +248,20 @@ 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) { export function deepClone(source) {
if (!source && typeof source !== 'object') { if (!source && typeof source !== 'object') {
throw new Error('error arguments', 'shallowClone') throw new Error('error arguments', 'shallowClone')
} }
const targetObj = source.constructor === Array ? [] : {} const targetObj = source.constructor === Array ? [] : {}
Object.keys(source).forEach(keys => { for (const keys in source) {
if (source[keys] && typeof source[keys] === 'object') { if (source.hasOwnProperty(keys)) {
targetObj[keys] = deepClone(source[keys]) if (source[keys] && typeof source[keys] === 'object') {
} else { targetObj[keys] = source[keys].constructor === Array ? [] : {}
targetObj[keys] = source[keys] targetObj[keys] = deepClone(source[keys])
} else {
targetObj[keys] = source[keys]
}
} }
}) }
return targetObj return targetObj
} }
export function uniqueArr(arr) {
return Array.from(new Set(arr))
}

View File

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

View File

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

View File

@@ -26,7 +26,7 @@ export function validateUpperCase(str) {
} }
/* 大小写字母*/ /* 大小写字母*/
export function validateAlphabets(str) { export function validatAlphabets(str) {
const reg = /^[A-Za-z]+$/ const reg = /^[A-Za-z]+$/
return reg.test(str) return reg.test(str)
} }
@@ -40,3 +40,4 @@ export function validateEmail(email) {
const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
return re.test(email) return re.test(email)
} }

309
src/vendor/Blob.js vendored
View File

@@ -16,161 +16,164 @@
/*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */ /*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */
(function (view) { (function (view) {
"use strict"; "use strict";
view.URL = view.URL || view.webkitURL; view.URL = view.URL || view.webkitURL;
if (view.Blob && view.URL) { if (view.Blob && view.URL) {
try { try {
new Blob; new Blob;
return; return;
} catch (e) {} } 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;
} }
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) { // Internally we use a BlobBuilder implementation to base Blob off of
var type = options ? (options.type || "") : ""; // in order to support older browsers that only have BlobBuilder
var builder = new BlobBuilder(); var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || (function(view) {
if (blobParts) { var
for (var i = 0, len = blobParts.length; i < len; i++) { get_class = function(object) {
builder.append(blobParts[i]); return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1];
} }
} , FakeBlobBuilder = function BlobBuilder() {
return builder.getBlob(type); 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]);
}
}
return builder.getBlob(type);
};
}(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this)); }(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this));

View File

@@ -4,179 +4,139 @@ require('script-loader!@/vendor/Blob');
import XLSX from 'xlsx' import XLSX from 'xlsx'
function generateArray(table) { function generateArray(table) {
var out = []; var out = [];
var rows = table.querySelectorAll('tr'); var rows = table.querySelectorAll('tr');
var ranges = []; var ranges = [];
for (var R = 0; R < rows.length; ++R) { for (var R = 0; R < rows.length; ++R) {
var outRow = []; var outRow = [];
var row = rows[R]; var row = rows[R];
var columns = row.querySelectorAll('td'); var columns = row.querySelectorAll('td');
for (var C = 0; C < columns.length; ++C) { for (var C = 0; C < columns.length; ++C) {
var cell = columns[C]; var cell = columns[C];
var colspan = cell.getAttribute('colspan'); var colspan = cell.getAttribute('colspan');
var rowspan = cell.getAttribute('rowspan'); var rowspan = cell.getAttribute('rowspan');
var cellValue = cell.innerText; var cellValue = cell.innerText;
if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue; if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue;
//Skip ranges //Skip ranges
ranges.forEach(function (range) { ranges.forEach(function (range) {
if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) { 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); 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);
} }
}); 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);
} }
out.push(outRow); return [out, ranges];
}
return [out, ranges];
}; };
function datenum(v, date1904) { function datenum(v, date1904) {
if (date1904) v += 1462; if (date1904) v += 1462;
var epoch = Date.parse(v); var epoch = Date.parse(v);
return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000); return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
} }
function sheet_from_array_of_arrays(data, opts) { function sheet_from_array_of_arrays(data, opts) {
var ws = {}; var ws = {};
var range = { var range = {s: {c: 10000000, r: 10000000}, e: {c: 0, r: 0}};
s: { for (var R = 0; R != data.length; ++R) {
c: 10000000, for (var C = 0; C != data[R].length; ++C) {
r: 10000000 if (range.s.r > R) range.s.r = R;
}, if (range.s.c > C) range.s.c = C;
e: { if (range.e.r < R) range.e.r = R;
c: 0, if (range.e.c < C) range.e.c = C;
r: 0 var cell = {v: data[R][C]};
} if (cell.v == null) continue;
}; var cell_ref = XLSX.utils.encode_cell({c: C, r: R});
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'; if (typeof cell.v === 'number') cell.t = 'n';
else if (typeof cell.v === 'boolean') cell.t = 'b'; else if (typeof cell.v === 'boolean') cell.t = 'b';
else if (cell.v instanceof Date) { else if (cell.v instanceof Date) {
cell.t = 'n'; cell.t = 'n';
cell.z = XLSX.SSF._table[14]; cell.z = XLSX.SSF._table[14];
cell.v = datenum(cell.v); cell.v = datenum(cell.v);
} else cell.t = 's'; }
else cell.t = 's';
ws[cell_ref] = cell; ws[cell_ref] = cell;
}
} }
} if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);
if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range); return ws;
return ws;
} }
function Workbook() { function Workbook() {
if (!(this instanceof Workbook)) return new Workbook(); if (!(this instanceof Workbook)) return new Workbook();
this.SheetNames = []; this.SheetNames = [];
this.Sheets = {}; this.Sheets = {};
} }
function s2ab(s) { function s2ab(s) {
var buf = new ArrayBuffer(s.length); var buf = new ArrayBuffer(s.length);
var view = new Uint8Array(buf); var view = new Uint8Array(buf);
for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF; for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
return buf; return buf;
} }
export function export_table_to_excel(id) { export function export_table_to_excel(id) {
var theTable = document.getElementById(id); var theTable = document.getElementById(id);
var oo = generateArray(theTable); var oo = generateArray(theTable);
var ranges = oo[1]; var ranges = oo[1];
/* original data */ /* original data */
var data = oo[0]; var data = oo[0];
var ws_name = "SheetJS"; var ws_name = "SheetJS";
var wb = new Workbook(), var wb = new Workbook(), ws = sheet_from_array_of_arrays(data);
ws = sheet_from_array_of_arrays(data);
/* add ranges to worksheet */ /* add ranges to worksheet */
// ws['!cols'] = ['apple', 'banan']; // ws['!cols'] = ['apple', 'banan'];
ws['!merges'] = ranges; ws['!merges'] = ranges;
/* add worksheet to workbook */ /* add worksheet to workbook */
wb.SheetNames.push(ws_name); wb.SheetNames.push(ws_name);
wb.Sheets[ws_name] = ws; wb.Sheets[ws_name] = ws;
var wbout = XLSX.write(wb, { var wbout = XLSX.write(wb, {bookType: 'xlsx', bookSST: false, type: 'binary'});
bookType: 'xlsx',
bookSST: false,
type: 'binary'
});
saveAs(new Blob([s2ab(wbout)], { saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), "test.xlsx")
type: "application/octet-stream"
}), "test.xlsx")
} }
export function export_json_to_excel({ export function export_json_to_excel(th, jsonData, defaultTitle) {
header,
data, /* original data */
filename,
autoWidth = true var data = jsonData;
} = {}) { data.unshift(th);
/* original data */ var ws_name = "SheetJS";
filename = filename || 'excel-list'
data = [...data] var wb = new Workbook(), ws = sheet_from_array_of_arrays(data);
data.unshift(header);
var ws_name = "SheetJS";
var wb = new Workbook(),
ws = sheet_from_array_of_arrays(data);
if (autoWidth) {
/*设置worksheet每列的最大宽度*/ /*设置worksheet每列的最大宽度*/
const colWidth = data.map(row => row.map(val => { const colWidth = data.map(row => row.map(val => {
/*先判断是否为null/undefined*/ /*先判断是否为null/undefined*/
if (val == null) { if (val == null) {
return { return {'wch': 10};
'wch': 10
};
} }
/*再判断是否为中文*/ /*再判断是否为中文*/
else if (val.toString().charCodeAt(0) > 255) { else if (val.toString().charCodeAt(0) > 255) {
return { return {'wch': val.toString().length * 2};
'wch': val.toString().length * 2
};
} else { } else {
return { return {'wch': val.toString().length};
'wch': val.toString().length
};
} }
})) }))
/*以第一行为初始值*/ /*以第一行为初始值*/
@@ -189,18 +149,12 @@ export function export_json_to_excel({
} }
} }
ws['!cols'] = result; ws['!cols'] = result;
}
/* add worksheet to workbook */ /* add worksheet to workbook */
wb.SheetNames.push(ws_name); wb.SheetNames.push(ws_name);
wb.Sheets[ws_name] = ws; wb.Sheets[ws_name] = ws;
var wbout = XLSX.write(wb, { var wbout = XLSX.write(wb, {bookType: 'xlsx', bookSST: false, type: 'binary'});
bookType: 'xlsx', var title = defaultTitle || 'excel-list'
bookSST: false, saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), title + ".xlsx")
type: 'binary'
});
saveAs(new Blob([s2ab(wbout)], {
type: "application/octet-stream"
}), filename + ".xlsx");
} }

View File

@@ -14,9 +14,7 @@ export function export_txt_to_zip(th, jsonData, txtName, zipName) {
txtData += `${tempStr}\r\n` txtData += `${tempStr}\r\n`
}) })
zip.file(`${txt_name}.txt`, txtData) zip.file(`${txt_name}.txt`, txtData)
zip.generateAsync({ zip.generateAsync({type:"blob"}).then((blob) => {
type: "blob"
}).then((blob) => {
saveAs(blob, `${zip_name}.zip`) saveAs(blob, `${zip_name}.zip`)
}, (err) => { }, (err) => {
alert('导出失败') alert('导出失败')

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