Compare commits
121 Commits
v3.9.1
...
tag/3.11.0
Author | SHA1 | Date | |
---|---|---|---|
|
1592a0a364 | ||
|
53f1820d09 | ||
|
20f6150741 | ||
|
7703005013 | ||
|
9a5c404ef8 | ||
|
9d975b5eff | ||
|
331173ffee | ||
|
f890685d8d | ||
|
fb30079477 | ||
|
3100d0cff4 | ||
|
ae6bbf7858 | ||
|
63dba8b7d8 | ||
|
ccb227fbec | ||
|
715fe3e684 | ||
|
abb1d91a9e | ||
|
d1f32a05a8 | ||
|
e601da38d0 | ||
|
b8d47bd847 | ||
|
8d242a002f | ||
|
763b31d915 | ||
|
c963f56686 | ||
|
17f0d84b35 | ||
|
f38d5810d9 | ||
|
cf48ed218b | ||
|
9574643e92 | ||
|
8edf209498 | ||
|
698df4942d | ||
|
1e2fc52ac9 | ||
|
3e92814f13 | ||
|
5ca6f79836 | ||
|
dc6030bce6 | ||
|
fc9e7249e7 | ||
|
9677406002 | ||
|
1e06f1da67 | ||
|
aa2eb7d40f | ||
|
6255f54f41 | ||
|
c2495545a5 | ||
|
fc277c75ee | ||
|
e544c01e63 | ||
|
c0f378e50a | ||
|
6b88c41ffc | ||
|
2e89ef444b | ||
|
fe950801c9 | ||
|
070a3343f2 | ||
|
48e47e86f1 | ||
|
4e0559833f | ||
|
8c18c000d4 | ||
|
9ed5db044f | ||
|
0fed69f367 | ||
|
a1d6ad3453 | ||
|
c71f3110fb | ||
|
fe381503eb | ||
|
24ee761809 | ||
|
0b6e7515ce | ||
|
dd9fb09a29 | ||
|
6e42b4c896 | ||
|
3b9abde89a | ||
|
4b3a41c535 | ||
|
b44000c9de | ||
|
727c1a45dd | ||
|
9daeb1cb34 | ||
|
3153a1b10b | ||
|
23055c9b2c | ||
|
8ef6c7ee7d | ||
|
eb1d3381f4 | ||
|
8cf279fbe1 | ||
|
ac8eddf3d7 | ||
|
6234db924e | ||
|
d84a17688d | ||
|
f767fab4cc | ||
|
77a40745bb | ||
|
109c393cc6 | ||
|
c54e99d0a9 | ||
|
5ea614fe5d | ||
|
1bb9283edd | ||
|
9cea3c3abe | ||
|
68a19d55de | ||
|
3dd29ab07a | ||
|
c98d578757 | ||
|
311796a14b | ||
|
cc7572820e | ||
|
deb6840174 | ||
|
07c3211989 | ||
|
afdb893dcd | ||
|
90ee05df93 | ||
|
6f1db28ea2 | ||
|
a56145509d | ||
|
1b394ae417 | ||
|
c2a2fec067 | ||
|
999ea3a443 | ||
|
46d6f455af | ||
|
4d6e9a5358 | ||
|
c68e68897b | ||
|
b37a789f63 | ||
|
54dc9ddfaa | ||
|
c6843f9646 | ||
|
ea60729c0a | ||
|
4a07432468 | ||
|
a51d72f42e | ||
|
c13b574794 | ||
|
9df740bcf1 | ||
|
4a7b50ed46 | ||
|
a0862ca547 | ||
|
9f1552d98f | ||
|
a25c63a580 | ||
|
1701fcce5d | ||
|
1d684b7632 | ||
|
cbdad9cb1b | ||
|
1a345a7c65 | ||
|
ec58373a52 | ||
|
bf2629ffba | ||
|
baa7172a70 | ||
|
bc003fd78b | ||
|
41a5615ee5 | ||
|
df23405bde | ||
|
f94aa1aca3 | ||
|
96bda5515c | ||
|
0f6d830c19 | ||
|
e8ab82c2c1 | ||
|
a284c1f007 | ||
|
323408f8d9 |
@@ -21,6 +21,8 @@ module.exports = {
|
||||
"allowFirstLine": false
|
||||
}
|
||||
}],
|
||||
"vue/singleline-html-element-content-newline": "off",
|
||||
"vue/multiline-html-element-content-newline":"off",
|
||||
"vue/name-property-casing": ["error", "PascalCase"],
|
||||
'accessor-pairs': 2,
|
||||
'arrow-spacing': [2, {
|
||||
|
20
README.md
@@ -7,7 +7,7 @@
|
||||
<img src="https://img.shields.io/badge/vue-2.5.17-brightgreen.svg" alt="vue">
|
||||
</a>
|
||||
<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.4.11-brightgreen.svg" alt="element-ui">
|
||||
</a>
|
||||
<a href="https://travis-ci.org/PanJiaChen/vue-element-admin" rel="nofollow">
|
||||
<img src="https://travis-ci.org/PanJiaChen/vue-element-admin.svg?branch=master" alt="Build Status">
|
||||
@@ -34,6 +34,8 @@ English | [简体中文](./README.zh-CN.md)
|
||||
|
||||
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.
|
||||
|
||||
**[v4.0](https://github.com/PanJiaChen/vue-element-admin/tree/v4.0) has in beta. It built on vue-cli@3, optimized a lot of code and added a lot of new features. Welcome to use and make suggestions.**
|
||||
|
||||
- [Preview](http://panjiachen.github.io/vue-element-admin)
|
||||
|
||||
- [Documentation](https://panjiachen.github.io/vue-element-admin-site/)
|
||||
@@ -69,6 +71,11 @@ Understanding and learning this knowledge in advance will greatly help the use o
|
||||
<img width="900" src="https://wpimg.wallstcn.com/a5894c1b-f6af-456e-82df-1151da0839bf.png">
|
||||
</p>
|
||||
|
||||
## Sponsors
|
||||
Become a sponsor and get your logo on our README on GitHub with a link to your site. [[Become a sponsor]](https://www.patreon.com/panjiachen)
|
||||
|
||||
<a href="https://flatlogic.com/admin-dashboards?from=vue-element-admin"><img width="150px" src="https://wpimg.wallstcn.com/9c0b719b-5551-4c1e-b776-63994632d94a.png" /></a><p>Admin Dashboard Templates made with Vue, React and Angular.</p>
|
||||
|
||||
## Features
|
||||
|
||||
```
|
||||
@@ -87,7 +94,7 @@ Understanding and learning this knowledge in advance will greatly help the use o
|
||||
- Multiple dynamic themes
|
||||
- Dynamic sidebar (supports multi-level routing)
|
||||
- Dynamic breadcrumb
|
||||
- Tags-view(Tab page Support right-click operation)
|
||||
- Tags-view (Tab page Support right-click operation)
|
||||
- Svg Sprite
|
||||
- Mock data
|
||||
- Screenfull
|
||||
@@ -118,6 +125,7 @@ Understanding and learning this knowledge in advance will greatly help the use o
|
||||
- Avatar Upload
|
||||
- Back To Top
|
||||
- Drag Dialog
|
||||
- Drag Select
|
||||
- Drag Kanban
|
||||
- Drag List
|
||||
- SplitPane
|
||||
@@ -198,6 +206,14 @@ If you find this project useful, you can buy author a glass of juice :tropical_d
|
||||
|
||||
[Buy me a coffee](https://www.buymeacoffee.com/Pan)
|
||||
|
||||
## Browsers support
|
||||
|
||||
Modern browsers and Internet Explorer 10+.
|
||||
|
||||
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
|
||||
| --------- | --------- | --------- | --------- |
|
||||
| IE10, IE11, Edge| last 2 versions| last 2 versions| last 2 versions
|
||||
|
||||
## License
|
||||
|
||||
[MIT](https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE)
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<img src="https://img.shields.io/badge/vue-2.5.10-brightgreen.svg" alt="vue">
|
||||
</a>
|
||||
<a href="https://github.com/ElemeFE/element">
|
||||
<img src="https://img.shields.io/badge/element--ui-2.3.2-brightgreen.svg" alt="element-ui">
|
||||
<img src="https://img.shields.io/badge/element--ui-2.4.11-brightgreen.svg" alt="element-ui">
|
||||
</a>
|
||||
<a href="https://travis-ci.org/PanJiaChen/vue-element-admin" rel="nofollow">
|
||||
<img src="https://travis-ci.org/PanJiaChen/vue-element-admin.svg?branch=master" alt="Build Status">
|
||||
@@ -30,7 +30,9 @@
|
||||
|
||||
## 简介
|
||||
|
||||
[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](http://panjiachen.github.io/vue-element-admin) 是一个后台集成解决方案,它基于 [vue](https://github.com/vuejs/vue) 和 [element](https://github.com/ElemeFE/element)。它使用了最新的前端技术栈,内置了 i18n 国际化解决方案,动态路由,权限验证,提炼了典型的业务模型,提供了丰富的功能组件,它可以帮助你快速搭建企业级中后台产品原型。相信不管你的需求是什么,本项目都能帮助到你。
|
||||
|
||||
**[v4.0](https://github.com/PanJiaChen/vue-element-admin/tree/v4.0) 已经进入 beta 测试阶段。 它基于 vue-cli@3 进行构建,优化了大量代码(尤其是权限和 mock),并且增加了不少新特性。欢迎使用并提出建议。**
|
||||
|
||||
- [在线访问](http://panjiachen.github.io/vue-element-admin)
|
||||
|
||||
@@ -40,7 +42,7 @@
|
||||
|
||||
- [Wiki](https://github.com/PanJiaChen/vue-element-admin/wiki)
|
||||
|
||||
- [Donate](https://panjiachen.github.io/vue-element-admin-site/zh/donate/)
|
||||
- [Donate](https://panjiachen.gitee.io/vue-element-admin-site/zh/donate)
|
||||
|
||||
- [Gitee](https://panjiachen.gitee.io/vue-element-admin/) 国内用户可访问该地址在线预览
|
||||
|
||||
@@ -52,7 +54,7 @@
|
||||
- 桌面端: [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
|
||||
- Typescript版: [vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template) (鸣谢: [@Armour](https://github.com/Armour))
|
||||
|
||||
群主 **[圈子](https://jianshiapp.com/circles/1209)** 楼主会经常分享一些技术相关的东西,或者加入[qq 群](https://github.com/PanJiaChen/vue-element-admin/issues/602)
|
||||
群主 **[圈子](https://jianshiapp.com/circles/1209)** 楼主会经常分享一些技术相关的东西,或者加入[qq 群](https://github.com/PanJiaChen/vue-element-admin/issues/602)或者关注[微博](https://weibo.com/u/3423485724?is_all=1)
|
||||
|
||||
**注意:该项目使用 element-ui@2.3.0+ 版本,所以最低兼容 vue@2.5.0+**
|
||||
|
||||
@@ -81,6 +83,11 @@
|
||||
<img width="900" src="https://wpimg.wallstcn.com/a5894c1b-f6af-456e-82df-1151da0839bf.png">
|
||||
</p>
|
||||
|
||||
## Sponsors
|
||||
Become a sponsor and get your logo on our README on GitHub with a link to your site. [[Become a sponsor]](https://www.patreon.com/panjiachen)
|
||||
|
||||
<a href="https://flatlogic.com/admin-dashboards?from=vue-element-admin"><img width="150px" src="https://wpimg.wallstcn.com/9c0b719b-5551-4c1e-b776-63994632d94a.png" /></a><p>Admin Dashboard Templates made with Vue, React and Angular.</p>
|
||||
|
||||
## 功能
|
||||
|
||||
```
|
||||
@@ -130,6 +137,7 @@
|
||||
- 头像上传
|
||||
- 返回顶部
|
||||
- 拖拽Dialog
|
||||
- 拖拽Select
|
||||
- 拖拽看板
|
||||
- 列表拖拽
|
||||
- SplitPane
|
||||
@@ -208,8 +216,18 @@ Detailed changes for each release are documented in the [release notes](https://
|
||||
如果你觉得这个项目帮助到了你,你可以帮作者买一杯果汁表示鼓励 :tropical_drink:
|
||||

|
||||
|
||||
[更多捐赠方式](https://panjiachen.gitee.io/vue-element-admin-site/zh/donate)
|
||||
|
||||
[Paypal Me](https://www.paypal.me/panfree23)
|
||||
|
||||
## Browsers support
|
||||
|
||||
Modern browsers and Internet Explorer 10+.
|
||||
|
||||
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
|
||||
| --------- | --------- | --------- | --------- |
|
||||
| IE10, IE11, Edge| last 2 versions| last 2 versions| last 2 versions
|
||||
|
||||
## License
|
||||
|
||||
[MIT](https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE)
|
||||
|
@@ -150,7 +150,6 @@ if (config.build.productionGzip) {
|
||||
|
||||
webpackConfig.plugins.push(
|
||||
new CompressionWebpackPlugin({
|
||||
asset: '[path].gz[query]',
|
||||
algorithm: 'gzip',
|
||||
test: new RegExp(
|
||||
'\\.(' + config.build.productionGzipExtensions.join('|') + ')$'
|
||||
|
25
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vue-element-admin",
|
||||
"version": "3.9.1",
|
||||
"version": "3.11.0",
|
||||
"description": "A magical vue admin. Typical templates for enterprise applications. Newest development stack of vue. Lots of awesome features",
|
||||
"author": "Pan <panfree23@gmail.com>",
|
||||
"license": "MIT",
|
||||
@@ -37,27 +37,26 @@
|
||||
"axios": "0.18.0",
|
||||
"clipboard": "1.7.1",
|
||||
"codemirror": "5.39.2",
|
||||
"connect": "3.6.6",
|
||||
"driver.js": "0.5.2",
|
||||
"driver.js": "0.8.1",
|
||||
"dropzone": "5.2.0",
|
||||
"echarts": "4.1.0",
|
||||
"element-ui": "2.4.6",
|
||||
"element-ui": "2.4.11",
|
||||
"file-saver": "1.3.8",
|
||||
"font-awesome": "4.7.0",
|
||||
"fuse.js": "3.4.2",
|
||||
"js-cookie": "2.2.0",
|
||||
"jsonlint": "1.6.3",
|
||||
"jszip": "3.1.5",
|
||||
"mockjs": "1.0.1-beta3",
|
||||
"normalize.css": "7.0.0",
|
||||
"nprogress": "0.2.0",
|
||||
"screenfull": "3.3.3",
|
||||
"screenfull": "4.0.0",
|
||||
"showdown": "1.8.6",
|
||||
"simplemde": "1.11.2",
|
||||
"sortablejs": "1.7.0",
|
||||
"tui-editor": "1.2.7",
|
||||
"vue": "2.5.17",
|
||||
"vue-count-to": "1.0.13",
|
||||
"vue-i18n": "7.3.2",
|
||||
"vue-router": "3.0.1",
|
||||
"vue-router": "3.0.2",
|
||||
"vue-splitpane": "1.0.2",
|
||||
"vuedraggable": "^2.16.0",
|
||||
"vuex": "3.0.1",
|
||||
@@ -76,13 +75,15 @@
|
||||
"babel-preset-env": "1.7.0",
|
||||
"babel-preset-stage-2": "6.24.1",
|
||||
"chalk": "2.4.1",
|
||||
"compression-webpack-plugin": "2.0.0",
|
||||
"connect": "3.6.6",
|
||||
"copy-webpack-plugin": "4.5.2",
|
||||
"cross-env": "5.2.0",
|
||||
"css-loader": "1.0.0",
|
||||
"eslint": "4.19.1",
|
||||
"eslint": "5.15.2",
|
||||
"eslint-friendly-formatter": "4.0.1",
|
||||
"eslint-loader": "2.0.0",
|
||||
"eslint-plugin-vue": "4.7.1",
|
||||
"eslint-loader": "2.1.2",
|
||||
"eslint-plugin-vue": "5.2.2",
|
||||
"file-loader": "1.1.11",
|
||||
"friendly-errors-webpack-plugin": "1.7.0",
|
||||
"hash-sum": "1.0.2",
|
||||
@@ -116,7 +117,7 @@
|
||||
"webpack": "4.16.5",
|
||||
"webpack-bundle-analyzer": "2.13.1",
|
||||
"webpack-cli": "3.1.0",
|
||||
"webpack-dev-server": "3.1.5",
|
||||
"webpack-dev-server": "3.1.14",
|
||||
"webpack-merge": "4.1.4"
|
||||
},
|
||||
"engines": {
|
||||
|
@@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<router-view/>
|
||||
<router-view />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default{
|
||||
export default {
|
||||
name: 'App'
|
||||
}
|
||||
</script>
|
||||
|
38
src/api/role.js
Normal file
@@ -0,0 +1,38 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function getRoutes() {
|
||||
return request({
|
||||
url: '/routes',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function getRoles() {
|
||||
return request({
|
||||
url: '/roles',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteRole(id) {
|
||||
return request({
|
||||
url: `/roles/${id}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
export function addRole(data) {
|
||||
return request({
|
||||
url: '/roles',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function updateRole(key, data) {
|
||||
return request({
|
||||
url: `/roles/${key}`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
@@ -4,7 +4,7 @@
|
||||
<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>
|
||||
<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" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
|
@@ -1,9 +1,11 @@
|
||||
<template>
|
||||
<el-breadcrumb class="app-breadcrumb" separator="/">
|
||||
<transition-group name="breadcrumb">
|
||||
<el-breadcrumb-item v-for="(item,index) in levelList" v-if="item.meta.title" :key="item.path">
|
||||
<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>
|
||||
<el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path">
|
||||
<span v-if="item.redirect==='noredirect'||index==levelList.length-1" class="no-redirect">
|
||||
{{ generateTitle(item.meta.title) }}
|
||||
</span>
|
||||
<a v-else @click.prevent="handleLink(item)">{{ generateTitle(item.meta.title) }}</a>
|
||||
</el-breadcrumb-item>
|
||||
</transition-group>
|
||||
</el-breadcrumb>
|
||||
@@ -30,20 +32,28 @@ export default {
|
||||
methods: {
|
||||
generateTitle,
|
||||
getBreadcrumb() {
|
||||
const { params } = this.$route
|
||||
let matched = this.$route.matched.filter(item => {
|
||||
if (item.name) {
|
||||
// To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561
|
||||
var toPath = pathToRegexp.compile(item.path)
|
||||
item.path = toPath(params)
|
||||
return true
|
||||
}
|
||||
})
|
||||
let matched = this.$route.matched.filter(item => item.name)
|
||||
|
||||
const first = matched[0]
|
||||
if (first && first.name.trim().toLocaleLowerCase() !== 'Dashboard'.toLocaleLowerCase()) {
|
||||
matched = [{ path: '/dashboard', meta: { title: 'dashboard' }}].concat(matched)
|
||||
}
|
||||
this.levelList = matched
|
||||
|
||||
this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
|
||||
},
|
||||
pathCompile(path) {
|
||||
// To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561
|
||||
const { params } = this.$route
|
||||
var toPath = pathToRegexp.compile(path)
|
||||
return toPath(params)
|
||||
},
|
||||
handleLink(item) {
|
||||
const { redirect, path } = item
|
||||
if (redirect) {
|
||||
this.$router.push(redirect)
|
||||
return
|
||||
}
|
||||
this.$router.push(this.pathCompile(path))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,7 +64,7 @@ export default {
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
line-height: 50px;
|
||||
margin-left: 10px;
|
||||
margin-left: 8px;
|
||||
.no-redirect {
|
||||
color: #97a8be;
|
||||
cursor: text;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div :class="className" :id="id" :style="{height:height,width:width}"/>
|
||||
<div :id="id" :class="className" :style="{height:height,width:width}" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div :class="className" :id="id" :style="{height:height,width:width}"/>
|
||||
<div :id="id" :class="className" :style="{height:height,width:width}" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div :class="className" :id="id" :style="{height:height,width:width}"/>
|
||||
<div :id="id" :class="className" :style="{height:height,width:width}" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@@ -1,6 +1,11 @@
|
||||
import { debounce } from '@/utils'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
sidebarElm: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.__resizeHandler = debounce(() => {
|
||||
if (this.chart) {
|
||||
@@ -9,14 +14,13 @@ export default {
|
||||
}, 100)
|
||||
window.addEventListener('resize', this.__resizeHandler)
|
||||
|
||||
const sidebarElm = document.getElementsByClassName('sidebar-container')[0]
|
||||
sidebarElm.addEventListener('transitionend', this.sidebarResizeHandler)
|
||||
this.sidebarElm = document.getElementsByClassName('sidebar-container')[0]
|
||||
this.sidebarElm && this.sidebarElm.addEventListener('transitionend', this.sidebarResizeHandler)
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.__resizeHandler)
|
||||
|
||||
const sidebarElm = document.getElementsByClassName('sidebar-container')[0]
|
||||
sidebarElm.removeEventListener('transitionend', this.sidebarResizeHandler)
|
||||
this.sidebarElm && this.sidebarElm.removeEventListener('transitionend', this.sidebarResizeHandler)
|
||||
},
|
||||
methods: {
|
||||
sidebarResizeHandler(e) {
|
||||
|
@@ -4,10 +4,10 @@
|
||||
<h3>{{ list1Title }}</h3>
|
||||
<draggable :list="list1" :options="{group:'article'}" class="dragArea">
|
||||
<div v-for="element in list1" :key="element.id" class="list-complete-item">
|
||||
<div class="list-complete-item-handle">[{{ element.author }}] {{ element.title }}</div>
|
||||
<div class="list-complete-item-handle">{{ element.id }}[{{ element.author }}] {{ element.title }}</div>
|
||||
<div style="position:absolute;right:0px;">
|
||||
<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" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -15,9 +15,9 @@
|
||||
</div>
|
||||
<div :style="{width:width2}" class="dndList-list">
|
||||
<h3>{{ list2Title }}</h3>
|
||||
<draggable :list="filterList2" :options="{group:'article'}" class="dragArea">
|
||||
<div v-for="element in filterList2" :key="element.id" class="list-complete-item">
|
||||
<div class="list-complete-item-handle2" @click="pushEle(element)"> [{{ element.author }}] {{ element.title }}</div>
|
||||
<draggable :list="list2" :options="{group:'article'}" class="dragArea">
|
||||
<div v-for="element in list2" :key="element.id" class="list-complete-item">
|
||||
<div class="list-complete-item-handle2" @click="pushEle(element)">{{ element.id }} [{{ element.author }}] {{ element.title }}</div>
|
||||
</div>
|
||||
</draggable>
|
||||
</div>
|
||||
@@ -60,16 +60,6 @@ export default {
|
||||
default: '48%'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filterList2() {
|
||||
return this.list2.filter(v => {
|
||||
if (this.isNotInList1(v)) {
|
||||
return v
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isNotInList1(v) {
|
||||
return this.list1.every(k => v.id !== k.id)
|
||||
@@ -90,7 +80,16 @@ export default {
|
||||
}
|
||||
},
|
||||
pushEle(ele) {
|
||||
this.list1.push(ele)
|
||||
for (const item of this.list2) {
|
||||
if (item.id === ele.id) {
|
||||
const index = this.list2.indexOf(item)
|
||||
this.list2.splice(index, 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
if (this.isNotInList1(ele)) {
|
||||
this.list1.push(ele)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
61
src/components/DragSelect/index.vue
Normal file
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<el-select ref="dragSelect" v-model="selectVal" v-bind="$attrs" class="drag-select" multiple v-on="$listeners">
|
||||
<slot />
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Sortable from 'sortablejs'
|
||||
|
||||
export default {
|
||||
name: 'DragSelect',
|
||||
props: {
|
||||
value: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
selectVal: {
|
||||
get() {
|
||||
return [...this.value]
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('input', [...val])
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.setSort()
|
||||
},
|
||||
methods: {
|
||||
setSort() {
|
||||
const el = this.$refs.dragSelect.$el.querySelectorAll('.el-select__tags > span')[0]
|
||||
this.sortable = Sortable.create(el, {
|
||||
ghostClass: 'sortable-ghost', // Class name for the drop placeholder,
|
||||
setData: function(dataTransfer) {
|
||||
dataTransfer.setData('Text', '')
|
||||
// to avoid Firefox bug
|
||||
// Detail see : https://github.com/RubaXa/Sortable/issues/1012
|
||||
},
|
||||
onEnd: evt => {
|
||||
const targetRow = this.value.splice(evt.oldIndex, 1)[0]
|
||||
this.value.splice(evt.newIndex, 0, targetRow)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.drag-select >>> .sortable-ghost{
|
||||
opacity: .8;
|
||||
color: #fff!important;
|
||||
background: #42b983!important;
|
||||
}
|
||||
|
||||
.drag-select >>> .el-tag{
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div :ref="id" :action="url" :id="id" class="dropzone">
|
||||
<div :id="id" :ref="id" :action="url" class="dropzone">
|
||||
<input type="file" name="file">
|
||||
</div>
|
||||
</template>
|
||||
|
@@ -1,21 +1,8 @@
|
||||
<template>
|
||||
<div v-if="errorLogs.length>0">
|
||||
<el-badge :is-dot="true" style="line-height: 30px;" @click.native="dialogTableVisible=true">
|
||||
<el-button size="small" type="danger" class="bug-btn">
|
||||
<svg
|
||||
t="1492682037685"
|
||||
class="bug-svg"
|
||||
viewBox="0 0 1024 1024"
|
||||
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>
|
||||
<el-badge :is-dot="true" style="line-height: 25px;margin-top: -5px;" @click.native="dialogTableVisible=true">
|
||||
<el-button style="padding: 8px 10px;" size="small" type="danger">
|
||||
<svg-icon icon-class="bug" />
|
||||
</el-button>
|
||||
</el-badge>
|
||||
|
||||
@@ -67,16 +54,6 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.bug-btn.el-button--small {
|
||||
padding: 9px 10px;
|
||||
}
|
||||
.bug-svg {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
.message-title {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
|
@@ -5,17 +5,20 @@
|
||||
height="80"
|
||||
viewBox="0 0 250 250"
|
||||
style="fill:#40c9c6; color:#fff;"
|
||||
aria-hidden="true">
|
||||
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"/>
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" />
|
||||
<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"
|
||||
fill="currentColor"
|
||||
style="transform-origin: 130px 106px;"
|
||||
class="octo-arm"/>
|
||||
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"/>
|
||||
class="octo-body"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
</template>
|
||||
|
@@ -1,27 +1,14 @@
|
||||
<template>
|
||||
<div>
|
||||
<div style="padding: 0 15px;" @click="toggleClick">
|
||||
<svg
|
||||
:class="{'is-active':isActive}"
|
||||
t="1492500959545"
|
||||
class="hamburger"
|
||||
style=""
|
||||
viewBox="0 0 1024 1024"
|
||||
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"
|
||||
@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"/>
|
||||
>
|
||||
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z" />
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
@@ -45,15 +32,12 @@ export default {
|
||||
<style scoped>
|
||||
.hamburger {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
transform: rotate(90deg);
|
||||
transition: .38s;
|
||||
transform-origin: 50% 50%;
|
||||
}
|
||||
|
||||
.hamburger.is-active {
|
||||
transform: rotate(0deg);
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
</style>
|
||||
|
188
src/components/HeaderSearch/index.vue
Normal file
@@ -0,0 +1,188 @@
|
||||
<template>
|
||||
<div :class="{'show':show}" class="header-search">
|
||||
<svg-icon class-name="search-icon" icon-class="search" @click.stop="click" />
|
||||
<el-select
|
||||
ref="headerSearchSelect"
|
||||
v-model="search"
|
||||
:remote-method="querySearch"
|
||||
filterable
|
||||
default-first-option
|
||||
remote
|
||||
placeholder="Search"
|
||||
class="header-search-select"
|
||||
@change="change"
|
||||
>
|
||||
<el-option v-for="item in options" :key="item.path" :value="item" :label="item.title.join(' > ')" />
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Fuse from 'fuse.js'
|
||||
import path from 'path'
|
||||
import i18n from '@/lang'
|
||||
|
||||
export default {
|
||||
name: 'HeaderSearch',
|
||||
data() {
|
||||
return {
|
||||
search: '',
|
||||
options: [],
|
||||
searchPool: [],
|
||||
show: false,
|
||||
fuse: undefined
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
routes() {
|
||||
return this.$store.getters.permission_routes
|
||||
},
|
||||
lang() {
|
||||
return this.$store.getters.language
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
lang() {
|
||||
this.searchPool = this.generateRoutes(this.routes)
|
||||
},
|
||||
routes() {
|
||||
this.searchPool = this.generateRoutes(this.routes)
|
||||
},
|
||||
searchPool(list) {
|
||||
this.initFuse(list)
|
||||
},
|
||||
show(value) {
|
||||
if (value) {
|
||||
document.body.addEventListener('click', this.close)
|
||||
} else {
|
||||
document.body.removeEventListener('click', this.close)
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.searchPool = this.generateRoutes(this.routes)
|
||||
},
|
||||
methods: {
|
||||
click() {
|
||||
this.show = !this.show
|
||||
if (this.show) {
|
||||
this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.focus()
|
||||
}
|
||||
},
|
||||
close() {
|
||||
this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.blur()
|
||||
this.options = []
|
||||
this.show = false
|
||||
},
|
||||
change(val) {
|
||||
this.$router.push(val.path)
|
||||
this.search = ''
|
||||
this.options = []
|
||||
this.$nextTick(() => {
|
||||
this.show = false
|
||||
})
|
||||
},
|
||||
initFuse(list) {
|
||||
this.fuse = new Fuse(list, {
|
||||
shouldSort: true,
|
||||
threshold: 0.4,
|
||||
location: 0,
|
||||
distance: 100,
|
||||
maxPatternLength: 32,
|
||||
minMatchCharLength: 1,
|
||||
keys: [{
|
||||
name: 'title',
|
||||
weight: 0.7
|
||||
}, {
|
||||
name: 'path',
|
||||
weight: 0.3
|
||||
}]
|
||||
})
|
||||
},
|
||||
// Filter out the routes that can be displayed in the sidebar
|
||||
// And generate the internationalized title
|
||||
generateRoutes(routes, basePath = '/', prefixTitle = []) {
|
||||
let res = []
|
||||
|
||||
for (const router of routes) {
|
||||
// skip hidden router
|
||||
if (router.hidden) { continue }
|
||||
|
||||
const data = {
|
||||
path: path.resolve(basePath, router.path),
|
||||
title: [...prefixTitle]
|
||||
}
|
||||
|
||||
if (router.meta && router.meta.title) {
|
||||
// generate internationalized title
|
||||
const i18ntitle = i18n.t(`route.${router.meta.title}`)
|
||||
|
||||
data.title = [...data.title, i18ntitle]
|
||||
|
||||
if (router.redirect !== 'noredirect') {
|
||||
// only push the routes with title
|
||||
// special case: need to exclude parent router without redirect
|
||||
res.push(data)
|
||||
}
|
||||
}
|
||||
|
||||
// recursive child routes
|
||||
if (router.children) {
|
||||
const tempRoutes = this.generateRoutes(router.children, data.path, data.title)
|
||||
if (tempRoutes.length >= 1) {
|
||||
res = [...res, ...tempRoutes]
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
},
|
||||
querySearch(query) {
|
||||
if (query !== '') {
|
||||
this.options = this.fuse.search(query)
|
||||
} else {
|
||||
this.options = []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.header-search {
|
||||
font-size: 0 !important;
|
||||
|
||||
.search-icon {
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.header-search-select {
|
||||
font-size: 18px;
|
||||
transition: width 0.2s;
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
background: transparent;
|
||||
border-radius: 0;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
|
||||
/deep/ .el-input__inner {
|
||||
border-radius: 0;
|
||||
border: 0;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
box-shadow: none !important;
|
||||
border-bottom: 1px solid #d9d9d9;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
&.show {
|
||||
.header-search-select {
|
||||
width: 210px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -2,22 +2,22 @@
|
||||
<div v-show="value" class="vue-image-crop-upload">
|
||||
<div class="vicp-wrap">
|
||||
<div class="vicp-close" @click="off">
|
||||
<i class="vicp-icon4"/>
|
||||
<i class="vicp-icon4" />
|
||||
</div>
|
||||
|
||||
<div v-show="step == 1" class="vicp-step1">
|
||||
<div class="vicp-drop-area" @dragleave="preventDefault" @dragover="preventDefault" @dragenter="preventDefault" @click="handleClick" @drop="handleChange">
|
||||
<i v-show="loading != 1" class="vicp-icon1">
|
||||
<i class="vicp-icon1-arrow"/>
|
||||
<i class="vicp-icon1-body"/>
|
||||
<i class="vicp-icon1-bottom"/>
|
||||
<i class="vicp-icon1-arrow" />
|
||||
<i class="vicp-icon1-body" />
|
||||
<i class="vicp-icon1-bottom" />
|
||||
</i>
|
||||
<span v-show="loading !== 1" class="vicp-hint">{{ lang.hint }}</span>
|
||||
<span v-show="!isSupported" class="vicp-no-supported-hint">{{ lang.noSupported }}</span>
|
||||
<input v-show="false" v-if="step == 1" ref="fileinput" type="file" @change="handleChange">
|
||||
</div>
|
||||
<div v-show="hasError" class="vicp-error">
|
||||
<i class="vicp-icon2"/> {{ errorMsg }}
|
||||
<i class="vicp-icon2" /> {{ errorMsg }}
|
||||
</div>
|
||||
<div class="vicp-operate">
|
||||
<a @click="off" @mousedown="ripple">{{ lang.btn.off }}</a>
|
||||
@@ -48,15 +48,16 @@
|
||||
@mousedown="imgStartMove"
|
||||
@mousemove="imgMove"
|
||||
@mouseup="createImg"
|
||||
@mouseout="createImg">
|
||||
<div :style="sourceImgShadeStyle" class="vicp-img-shade vicp-img-shade-1"/>
|
||||
<div :style="sourceImgShadeStyle" class="vicp-img-shade vicp-img-shade-2"/>
|
||||
@mouseout="createImg"
|
||||
>
|
||||
<div :style="sourceImgShadeStyle" class="vicp-img-shade vicp-img-shade-1" />
|
||||
<div :style="sourceImgShadeStyle" class="vicp-img-shade vicp-img-shade-2" />
|
||||
</div>
|
||||
|
||||
<div class="vicp-range">
|
||||
<input :value="scale.range" type="range" step="1" min="0" max="100" @input="zoomChange">
|
||||
<i class="vicp-icon5" @mousedown="startZoomSub" @mouseout="endZoomSub" @mouseup="endZoomSub"/>
|
||||
<i class="vicp-icon6" @mousedown="startZoomAdd" @mouseout="endZoomAdd" @mouseup="endZoomAdd"/>
|
||||
<i class="vicp-icon5" @mousedown="startZoomSub" @mouseout="endZoomSub" @mouseup="endZoomSub" />
|
||||
<i class="vicp-icon6" @mousedown="startZoomAdd" @mouseout="endZoomAdd" @mouseup="endZoomAdd" />
|
||||
</div>
|
||||
|
||||
<div v-if="!noRotate" class="vicp-rotate">
|
||||
@@ -87,13 +88,13 @@
|
||||
<div class="vicp-upload">
|
||||
<span v-show="loading === 1" class="vicp-loading">{{ lang.loading }}</span>
|
||||
<div class="vicp-progress-wrap">
|
||||
<span v-show="loading === 1" :style="progressStyle" class="vicp-progress"/>
|
||||
<span v-show="loading === 1" :style="progressStyle" class="vicp-progress" />
|
||||
</div>
|
||||
<div v-show="hasError" class="vicp-error">
|
||||
<i class="vicp-icon2"/> {{ errorMsg }}
|
||||
<i class="vicp-icon2" /> {{ errorMsg }}
|
||||
</div>
|
||||
<div v-show="loading === 2" class="vicp-success">
|
||||
<i class="vicp-icon3"/> {{ lang.success }}
|
||||
<i class="vicp-icon3" /> {{ lang.success }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="vicp-operate">
|
||||
@@ -101,7 +102,7 @@
|
||||
<a @click="off" @mousedown="ripple">{{ lang.btn.close }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<canvas v-show="false" ref="canvas" :width="width" :height="height"/>
|
||||
<canvas v-show="false" ref="canvas" :width="width" :height="height" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="json-editor">
|
||||
<textarea ref="textarea"/>
|
||||
<textarea ref="textarea" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@@ -6,7 +6,8 @@
|
||||
<draggable
|
||||
:list="list"
|
||||
:options="options"
|
||||
class="board-column-content">
|
||||
class="board-column-content"
|
||||
>
|
||||
<div v-for="element in list" :key="element.id" class="board-item">
|
||||
{{ element.name }} {{ element.id }}
|
||||
</div>
|
||||
@@ -41,7 +42,7 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
<style lang="scss" scoped>
|
||||
.board-column {
|
||||
min-width: 300px;
|
||||
min-height: 100px;
|
||||
@@ -81,7 +82,7 @@ export default {
|
||||
line-height: 54px;
|
||||
padding: 5px 10px;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0px 1px 3px 0 rgba(0,0,0,0.2);
|
||||
box-shadow: 0px 1px 3px 0 rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -4,8 +4,15 @@
|
||||
<svg-icon class-name="international-icon" icon-class="language" />
|
||||
</div>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item :disabled="language==='zh'" command="zh">中文</el-dropdown-item>
|
||||
<el-dropdown-item :disabled="language==='en'" command="en">English</el-dropdown-item>
|
||||
<el-dropdown-item :disabled="language==='zh'" command="zh">
|
||||
中文
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :disabled="language==='en'" command="en">
|
||||
English
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :disabled="language==='es'" command="es">
|
||||
Español
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
@@ -29,12 +36,3 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.international-icon {
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
vertical-align: -5px!important;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<div :class="computedClasses" class="material-input__component">
|
||||
<div :class="{iconClass:icon}">
|
||||
<i v-if="icon" :class="['el-icon-' + icon]" class="el-input__icon material-input__icon"/>
|
||||
<i v-if="icon" :class="['el-icon-' + icon]" class="el-input__icon material-input__icon" />
|
||||
<input
|
||||
v-if="type === 'email'"
|
||||
v-model="currentValue"
|
||||
:name="name"
|
||||
:placeholder="fillPlaceHolder"
|
||||
v-model="currentValue"
|
||||
:readonly="readonly"
|
||||
:disabled="disabled"
|
||||
:autoComplete="autoComplete"
|
||||
@@ -15,12 +15,13 @@
|
||||
class="material-input"
|
||||
@focus="handleMdFocus"
|
||||
@blur="handleMdBlur"
|
||||
@input="handleModelInput">
|
||||
@input="handleModelInput"
|
||||
>
|
||||
<input
|
||||
v-if="type === 'url'"
|
||||
v-model="currentValue"
|
||||
:name="name"
|
||||
:placeholder="fillPlaceHolder"
|
||||
v-model="currentValue"
|
||||
:readonly="readonly"
|
||||
:disabled="disabled"
|
||||
:autoComplete="autoComplete"
|
||||
@@ -29,12 +30,13 @@
|
||||
class="material-input"
|
||||
@focus="handleMdFocus"
|
||||
@blur="handleMdBlur"
|
||||
@input="handleModelInput">
|
||||
@input="handleModelInput"
|
||||
>
|
||||
<input
|
||||
v-if="type === 'number'"
|
||||
v-model="currentValue"
|
||||
:name="name"
|
||||
:placeholder="fillPlaceHolder"
|
||||
v-model="currentValue"
|
||||
:step="step"
|
||||
:readonly="readonly"
|
||||
:disabled="disabled"
|
||||
@@ -48,12 +50,13 @@
|
||||
class="material-input"
|
||||
@focus="handleMdFocus"
|
||||
@blur="handleMdBlur"
|
||||
@input="handleModelInput">
|
||||
@input="handleModelInput"
|
||||
>
|
||||
<input
|
||||
v-if="type === 'password'"
|
||||
v-model="currentValue"
|
||||
:name="name"
|
||||
:placeholder="fillPlaceHolder"
|
||||
v-model="currentValue"
|
||||
:readonly="readonly"
|
||||
:disabled="disabled"
|
||||
:autoComplete="autoComplete"
|
||||
@@ -64,12 +67,13 @@
|
||||
class="material-input"
|
||||
@focus="handleMdFocus"
|
||||
@blur="handleMdBlur"
|
||||
@input="handleModelInput">
|
||||
@input="handleModelInput"
|
||||
>
|
||||
<input
|
||||
v-if="type === 'tel'"
|
||||
v-model="currentValue"
|
||||
:name="name"
|
||||
:placeholder="fillPlaceHolder"
|
||||
v-model="currentValue"
|
||||
:readonly="readonly"
|
||||
:disabled="disabled"
|
||||
:autoComplete="autoComplete"
|
||||
@@ -78,12 +82,13 @@
|
||||
class="material-input"
|
||||
@focus="handleMdFocus"
|
||||
@blur="handleMdBlur"
|
||||
@input="handleModelInput">
|
||||
@input="handleModelInput"
|
||||
>
|
||||
<input
|
||||
v-if="type === 'text'"
|
||||
v-model="currentValue"
|
||||
:name="name"
|
||||
:placeholder="fillPlaceHolder"
|
||||
v-model="currentValue"
|
||||
:readonly="readonly"
|
||||
:disabled="disabled"
|
||||
:autoComplete="autoComplete"
|
||||
@@ -94,10 +99,11 @@
|
||||
class="material-input"
|
||||
@focus="handleMdFocus"
|
||||
@blur="handleMdBlur"
|
||||
@input="handleModelInput">
|
||||
<span class="material-input-bar"/>
|
||||
@input="handleModelInput"
|
||||
>
|
||||
<span class="material-input-bar" />
|
||||
<label class="material-label">
|
||||
<slot/>
|
||||
<slot />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
31
src/components/MarkdownEditor/defaultOptions.js
Normal file
@@ -0,0 +1,31 @@
|
||||
// doc: https://nhnent.github.io/tui.editor/api/latest/ToastUIEditor.html#ToastUIEditor
|
||||
export default {
|
||||
minHeight: '200px',
|
||||
previewStyle: 'vertical',
|
||||
useCommandShortcut: true,
|
||||
useDefaultHTMLSanitizer: true,
|
||||
usageStatistics: false,
|
||||
hideModeSwitch: false,
|
||||
toolbarItems: [
|
||||
'heading',
|
||||
'bold',
|
||||
'italic',
|
||||
'strike',
|
||||
'divider',
|
||||
'hr',
|
||||
'quote',
|
||||
'divider',
|
||||
'ul',
|
||||
'ol',
|
||||
'task',
|
||||
'indent',
|
||||
'outdent',
|
||||
'divider',
|
||||
'table',
|
||||
'image',
|
||||
'link',
|
||||
'divider',
|
||||
'code',
|
||||
'codeblock'
|
||||
]
|
||||
}
|
@@ -1,16 +1,18 @@
|
||||
<template>
|
||||
<div :style="{height:height+'px',zIndex:zIndex}" class="simplemde-container">
|
||||
<textarea :id="id"/>
|
||||
</div>
|
||||
<div :id="id" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import 'font-awesome/css/font-awesome.min.css'
|
||||
import 'simplemde/dist/simplemde.min.css'
|
||||
import SimpleMDE from 'simplemde'
|
||||
// deps for editor
|
||||
import 'codemirror/lib/codemirror.css' // codemirror
|
||||
import 'tui-editor/dist/tui-editor.css' // editor ui
|
||||
import 'tui-editor/dist/tui-editor-contents.css' // editor content
|
||||
|
||||
import Editor from 'tui-editor'
|
||||
import defaultOptions from './defaultOptions'
|
||||
|
||||
export default {
|
||||
name: 'SimplemdeMd',
|
||||
name: 'MarddownEditor',
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
@@ -19,105 +21,98 @@ export default {
|
||||
id: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'markdown-editor-' + +new Date()
|
||||
default() {
|
||||
return 'markdown-editor-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '')
|
||||
}
|
||||
},
|
||||
autofocus: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
options: {
|
||||
type: Object,
|
||||
default() {
|
||||
return defaultOptions
|
||||
}
|
||||
},
|
||||
placeholder: {
|
||||
mode: {
|
||||
type: String,
|
||||
default: ''
|
||||
default: 'markdown'
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 150
|
||||
type: String,
|
||||
required: false,
|
||||
default: '300px'
|
||||
},
|
||||
zIndex: {
|
||||
type: Number,
|
||||
default: 10
|
||||
},
|
||||
toolbar: {
|
||||
type: Array,
|
||||
default: function() {
|
||||
return []
|
||||
}
|
||||
language: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'en_US' // https://github.com/nhnent/tui.editor/tree/master/src/js/langs
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
simplemde: null,
|
||||
hasChange: false
|
||||
editor: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
editorOptions() {
|
||||
const options = Object.assign({}, defaultOptions, this.options)
|
||||
options.initialEditType = this.mode
|
||||
options.height = this.height
|
||||
options.language = this.language
|
||||
return options
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(val) {
|
||||
if (val === this.simplemde.value() && !this.hasChange) return
|
||||
this.simplemde.value(val)
|
||||
value(newValue, preValue) {
|
||||
if (newValue !== preValue && newValue !== this.editor.getValue()) {
|
||||
this.editor.setValue(newValue)
|
||||
}
|
||||
},
|
||||
language(val) {
|
||||
this.destroyEditor()
|
||||
this.initEditor()
|
||||
},
|
||||
height(newValue) {
|
||||
this.editor.height(newValue)
|
||||
},
|
||||
mode(newValue) {
|
||||
this.editor.changeMode(newValue)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.simplemde = new SimpleMDE({
|
||||
element: document.getElementById(this.id),
|
||||
autoDownloadFontAwesome: false,
|
||||
autofocus: this.autofocus,
|
||||
toolbar: this.toolbar.length > 0 ? this.toolbar : undefined,
|
||||
spellChecker: false,
|
||||
insertTexts: {
|
||||
link: ['[', ']( )']
|
||||
},
|
||||
// hideIcons: ['guide', 'heading', 'quote', 'image', 'preview', 'side-by-side', 'fullscreen'],
|
||||
placeholder: this.placeholder
|
||||
})
|
||||
if (this.value) {
|
||||
this.simplemde.value(this.value)
|
||||
}
|
||||
this.simplemde.codemirror.on('change', () => {
|
||||
if (this.hasChange) {
|
||||
this.hasChange = true
|
||||
}
|
||||
this.$emit('input', this.simplemde.value())
|
||||
})
|
||||
this.initEditor()
|
||||
},
|
||||
destroyed() {
|
||||
this.simplemde.toTextArea()
|
||||
this.simplemde = null
|
||||
this.destroyEditor()
|
||||
},
|
||||
methods: {
|
||||
initEditor() {
|
||||
this.editor = new Editor({
|
||||
el: document.getElementById(this.id),
|
||||
...this.editorOptions
|
||||
})
|
||||
if (this.value) {
|
||||
this.editor.setValue(this.value)
|
||||
}
|
||||
this.editor.on('change', () => {
|
||||
this.$emit('input', this.editor.getValue())
|
||||
})
|
||||
},
|
||||
destroyEditor() {
|
||||
if (!this.editor) return
|
||||
this.editor.off('change')
|
||||
this.editor.remove()
|
||||
},
|
||||
setValue(value) {
|
||||
this.editor.setValue(value)
|
||||
},
|
||||
getValue() {
|
||||
return this.editor.getValue()
|
||||
},
|
||||
setHtml(value) {
|
||||
this.editor.setHtml(value)
|
||||
},
|
||||
getHtml() {
|
||||
return this.editor.getHtml()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.simplemde-container>>>.CodeMirror {
|
||||
min-height: 150px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.simplemde-container>>>.CodeMirror-scroll {
|
||||
min-height: 150px;
|
||||
}
|
||||
|
||||
.simplemde-container>>>.CodeMirror-code {
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
|
||||
.simplemde-container>>>.editor-statusbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.simplemde-container>>>.CodeMirror .CodeMirror-code .cm-link {
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
.simplemde-container>>>.CodeMirror .CodeMirror-code .cm-string.cm-url {
|
||||
color: #2d3b4d;
|
||||
}
|
||||
|
||||
.simplemde-container>>>.CodeMirror .CodeMirror-code .cm-formatting-link-string.cm-url {
|
||||
padding: 0 2px;
|
||||
color: #E61E1E;
|
||||
}
|
||||
.simplemde-container >>> .editor-toolbar.fullscreen,
|
||||
.simplemde-container >>> .CodeMirror-fullscreen {
|
||||
z-index: 1003;
|
||||
}
|
||||
</style>
|
||||
|
101
src/components/Pagination/index.vue
Normal file
@@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<div :class="{'hidden':hidden}" class="pagination-container">
|
||||
<el-pagination
|
||||
:background="background"
|
||||
:current-page.sync="currentPage"
|
||||
:page-size.sync="pageSize"
|
||||
:layout="layout"
|
||||
:page-sizes="pageSizes"
|
||||
:total="total"
|
||||
v-bind="$attrs"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { scrollTo } from '@/utils/scrollTo'
|
||||
|
||||
export default {
|
||||
name: 'Pagination',
|
||||
props: {
|
||||
total: {
|
||||
required: true,
|
||||
type: Number
|
||||
},
|
||||
page: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
limit: {
|
||||
type: Number,
|
||||
default: 20
|
||||
},
|
||||
pageSizes: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [10, 20, 30, 50]
|
||||
}
|
||||
},
|
||||
layout: {
|
||||
type: String,
|
||||
default: 'total, sizes, prev, pager, next, jumper'
|
||||
},
|
||||
background: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
autoScroll: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
hidden: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
currentPage: {
|
||||
get() {
|
||||
return this.page
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('update:page', val)
|
||||
}
|
||||
},
|
||||
pageSize: {
|
||||
get() {
|
||||
return this.limit
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('update:limit', val)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSizeChange(val) {
|
||||
this.$emit('pagination', { page: this.currentPage, limit: val })
|
||||
if (this.autoScroll) {
|
||||
scrollTo(0, 800)
|
||||
}
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.$emit('pagination', { page: val, limit: this.pageSize })
|
||||
if (this.autoScroll) {
|
||||
scrollTo(0, 800)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.pagination-container {
|
||||
background: #fff;
|
||||
padding: 32px 16px;
|
||||
}
|
||||
.pagination-container.hidden {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
@@ -2,7 +2,7 @@
|
||||
<div :style="{zIndex:zIndex,height:height,width:width}" class="pan-item">
|
||||
<div class="pan-info">
|
||||
<div class="pan-info-roles-container">
|
||||
<slot/>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
<img :src="image" class="pan-thumb">
|
||||
|
@@ -1,29 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<svg
|
||||
t="1508738709248"
|
||||
class="screenfull-svg"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="2069"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="32"
|
||||
height="32"
|
||||
@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-icon :icon-class="isFullscreen?'exit-fullscreen':'fullscreen'" @click="click" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -32,25 +9,14 @@ import screenfull from 'screenfull'
|
||||
|
||||
export default {
|
||||
name: 'Screenfull',
|
||||
props: {
|
||||
width: {
|
||||
type: Number,
|
||||
default: 22
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 22
|
||||
},
|
||||
fill: {
|
||||
type: String,
|
||||
default: '#48576a'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isFullscreen: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
},
|
||||
methods: {
|
||||
click() {
|
||||
if (!screenfull.enabled) {
|
||||
@@ -61,6 +27,13 @@ export default {
|
||||
return false
|
||||
}
|
||||
screenfull.toggle()
|
||||
},
|
||||
init() {
|
||||
if (screenfull.enabled) {
|
||||
screenfull.on('change', () => {
|
||||
this.isFullscreen = screenfull.isFullscreen
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,56 +0,0 @@
|
||||
<template>
|
||||
<el-scrollbar ref="scrollContainer" :vertical="false" class="scroll-container" @wheel.native.prevent="handleScroll">
|
||||
<slot/>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const padding = 15 // tag's padding
|
||||
|
||||
export default {
|
||||
name: 'ScrollPane',
|
||||
data() {
|
||||
return {
|
||||
left: 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleScroll(e) {
|
||||
const eventDelta = e.wheelDelta || -e.deltaY * 40
|
||||
const $scrollWrapper = this.$refs.scrollContainer.$refs.wrap
|
||||
$scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
|
||||
},
|
||||
moveToTarget($target) {
|
||||
const $container = this.$refs.scrollContainer.$el
|
||||
const $containerWidth = $container.offsetWidth
|
||||
const $scrollWrapper = this.$refs.scrollContainer.$refs.wrap
|
||||
const $targetLeft = $target.offsetLeft
|
||||
const $targetWidth = $target.offsetWidth
|
||||
if ($targetLeft > $containerWidth) {
|
||||
// tag in the right
|
||||
$scrollWrapper.scrollLeft = $targetLeft - $containerWidth + $targetWidth + padding
|
||||
} else {
|
||||
// tag in the left
|
||||
$scrollWrapper.scrollLeft = $targetLeft - padding
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.scroll-container {
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
/deep/ {
|
||||
.el-scrollbar__bar {
|
||||
bottom: 0px;
|
||||
}
|
||||
.el-scrollbar__wrap {
|
||||
height: 49px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -4,15 +4,25 @@
|
||||
<svg-icon class-name="size-icon" icon-class="size" />
|
||||
</div>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item :disabled="size==='medium'" command="medium">Medium</el-dropdown-item>
|
||||
<el-dropdown-item :disabled="size==='small'" command="small">Small</el-dropdown-item>
|
||||
<el-dropdown-item :disabled="size==='mini'" command="mini">Mini</el-dropdown-item>
|
||||
<el-dropdown-item v-for="item of sizeOptions" :key="item.value" :disabled="size===item.value" :command="item.value">
|
||||
{{ item.label }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
sizeOptions: [
|
||||
{ label: 'Default', value: 'default' },
|
||||
{ label: 'Medium', value: 'medium' },
|
||||
{ label: 'Small', value: 'small' },
|
||||
{ label: 'Mini', value: 'mini' }
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
size() {
|
||||
return this.$store.getters.size
|
||||
@@ -44,12 +54,3 @@ export default {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.size-icon {
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
vertical-align: -4px!important;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
@@ -1,6 +1,9 @@
|
||||
<template>
|
||||
<div :style="{height:height+'px',zIndex:zIndex}">
|
||||
<div :class="className" :style="{top:stickyTop+'px',zIndex:zIndex,position:position,width:width,height:height+'px'}">
|
||||
<div
|
||||
:class="className"
|
||||
:style="{top:(isSticky ? stickyTop +'px' : ''),zIndex:zIndex,position:position,width:width,height:height+'px'}"
|
||||
>
|
||||
<slot>
|
||||
<div>sticky</div>
|
||||
</slot>
|
||||
@@ -56,23 +59,27 @@ export default {
|
||||
this.width = this.width + 'px'
|
||||
this.isSticky = true
|
||||
},
|
||||
reset() {
|
||||
handleReset() {
|
||||
if (!this.active) {
|
||||
return
|
||||
}
|
||||
this.reset()
|
||||
},
|
||||
reset() {
|
||||
this.position = ''
|
||||
this.width = 'auto'
|
||||
this.active = false
|
||||
this.isSticky = false
|
||||
},
|
||||
handleScroll() {
|
||||
this.width = this.$el.getBoundingClientRect().width
|
||||
const width = this.$el.getBoundingClientRect().width
|
||||
this.width = width || 'auto'
|
||||
const offsetTop = this.$el.getBoundingClientRect().top
|
||||
if (offsetTop < this.stickyTop) {
|
||||
this.sticky()
|
||||
return
|
||||
}
|
||||
this.reset()
|
||||
this.handleReset()
|
||||
},
|
||||
handleReize() {
|
||||
if (this.isSticky) {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<svg :class="svgClass" aria-hidden="true">
|
||||
<use :xlink:href="iconName"/>
|
||||
<svg :class="svgClass" aria-hidden="true" v-on="$listeners">
|
||||
<use :xlink:href="iconName" />
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
|
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<a :class="className" class="link--mallki" href="#">
|
||||
{{ text }}
|
||||
<span :data-letters="text"/>
|
||||
<span :data-letters="text"/>
|
||||
<span :data-letters="text" />
|
||||
<span :data-letters="text" />
|
||||
</a>
|
||||
</template>
|
||||
|
||||
|
@@ -1,8 +1,10 @@
|
||||
<template>
|
||||
<el-color-picker
|
||||
v-model="theme"
|
||||
:predefine="['#409EFF', '#11a983', '#13c2c2', '#6959CD', '#f5222d', '#eb2f96', '#DB7093', '#e6a23c', '#8B8989', '#212121']"
|
||||
class="theme-picker"
|
||||
popper-class="theme-picker-dropdown"/>
|
||||
popper-class="theme-picker-dropdown"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -18,7 +20,8 @@ export default {
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
theme(val, oldVal) {
|
||||
theme(val) {
|
||||
const oldVal = this.theme
|
||||
if (typeof val !== 'string') return
|
||||
const themeCluster = this.getThemeCluster(val.replace('#', ''))
|
||||
const originalCluster = this.getThemeCluster(oldVal.replace('#', ''))
|
||||
@@ -66,11 +69,18 @@ export default {
|
||||
|
||||
methods: {
|
||||
updateStyle(style, oldCluster, newCluster) {
|
||||
let newStyle = style
|
||||
const colorOverrides = [] // only capture color overides
|
||||
oldCluster.forEach((color, index) => {
|
||||
newStyle = newStyle.replace(new RegExp(color, 'ig'), newCluster[index])
|
||||
const value = newCluster[index]
|
||||
const color_plain = color.replace(/([()])/g, '\\$1')
|
||||
const repl = new RegExp(`(^|})([^{]+{[^{}]+)${color_plain}\\b([^}]*)(?=})`, 'gi')
|
||||
const nestRepl = new RegExp(color_plain, 'ig') // for greed matching before the 'color'
|
||||
let v
|
||||
while ((v = repl.exec(style))) {
|
||||
colorOverrides.push(v[2].replace(nestRepl, value) + value + v[3] + '}') // '}' not captured in the regexp repl to reserve it as locator-boundary
|
||||
}
|
||||
})
|
||||
return newStyle
|
||||
return colorOverrides.join('')
|
||||
},
|
||||
|
||||
getCSSString(url, callback, variable) {
|
||||
@@ -135,7 +145,10 @@ export default {
|
||||
|
||||
<style>
|
||||
.theme-picker .el-color-picker__trigger {
|
||||
vertical-align: middle;
|
||||
margin-top: 12px;
|
||||
height: 26px!important;
|
||||
width: 26px!important;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.theme-picker-dropdown .el-color-dropdown__link-btn {
|
||||
|
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<div class="upload-container">
|
||||
<el-button :style="{background:color,borderColor:color}" icon="el-icon-upload" size="mini" type="primary" @click=" dialogVisible=true">上传图片
|
||||
<el-button :style="{background:color,borderColor:color}" icon="el-icon-upload" size="mini" type="primary" @click=" dialogVisible=true">
|
||||
上传图片
|
||||
</el-button>
|
||||
<el-dialog :visible.sync="dialogVisible">
|
||||
<el-upload
|
||||
@@ -12,7 +13,8 @@
|
||||
:before-upload="beforeUpload"
|
||||
class="editor-slide-upload"
|
||||
action="https://httpbin.org/post"
|
||||
list-type="picture-card">
|
||||
list-type="picture-card"
|
||||
>
|
||||
<el-button size="small" type="primary">点击上传</el-button>
|
||||
</el-upload>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
|
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div :class="{fullscreen:fullscreen}" class="tinymce-container editor-container">
|
||||
<textarea :id="tinymceId" class="tinymce-textarea"/>
|
||||
<textarea :id="tinymceId" class="tinymce-textarea" />
|
||||
<div class="editor-custom-btn-container">
|
||||
<editorImage color="#1890ff" class="editor-upload-btn" @successCBK="imageSuccessCBK"/>
|
||||
<editorImage color="#1890ff" class="editor-upload-btn" @successCBK="imageSuccessCBK" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -157,8 +157,13 @@ export default {
|
||||
})
|
||||
},
|
||||
destroyTinymce() {
|
||||
if (window.tinymce.get(this.tinymceId)) {
|
||||
window.tinymce.get(this.tinymceId).destroy()
|
||||
const tinymce = window.tinymce.get(this.tinymceId)
|
||||
if (this.fullscreen) {
|
||||
tinymce.execCommand('mceFullScreen')
|
||||
}
|
||||
|
||||
if (tinymce) {
|
||||
tinymce.destroy()
|
||||
}
|
||||
},
|
||||
setContent(value) {
|
||||
@@ -180,6 +185,7 @@ export default {
|
||||
<style scoped>
|
||||
.tinymce-container {
|
||||
position: relative;
|
||||
line-height: normal;
|
||||
}
|
||||
.tinymce-container>>>.mce-fullscreen {
|
||||
z-index: 10000;
|
||||
|
@@ -2,6 +2,6 @@
|
||||
// Detail plugins list see https://www.tinymce.com/docs/plugins/
|
||||
// Custom builds see https://www.tinymce.com/download/custom-builds/
|
||||
|
||||
const plugins = ['advlist anchor autolink autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools importcss insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount']
|
||||
const plugins = ['advlist anchor autolink autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount']
|
||||
|
||||
export default plugins
|
||||
|
@@ -1,6 +1,6 @@
|
||||
// Here is a list of the toolbar
|
||||
// Detail list see https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols
|
||||
|
||||
const toolbar = ['bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen']
|
||||
const toolbar = ['searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen']
|
||||
|
||||
export default toolbar
|
||||
|
220
src/components/TreeTable/README.md
Normal file
@@ -0,0 +1,220 @@
|
||||
|
||||
- [Enlgish](#Brief)
|
||||
|
||||
# 中文
|
||||
|
||||
## 写在前面
|
||||
|
||||
此组件仅提供一个创建 `TreeTable` 的解决思路。它基于`element-ui`的 table 组件实现,通过`el-table`的`row-style`方法,在里面判断元素是否需要隐藏或者显示,从而实现`TreeTable`的展开与收起。
|
||||
|
||||
并且本组件充分利用 `vue` 插槽的特性来方便用户自定义。
|
||||
|
||||
`evel.js` 里面,`addAttrs` 方法会给数据添加几个属性,`treeTotable` 会对数组扁平化。这些操作都不会破坏源数据,只是会新增属性。
|
||||
|
||||
## Props 说明
|
||||
|
||||
| Attribute | Description | Type | Default |
|
||||
| :--------------: | :--------------------------------- | :-----: | :------: |
|
||||
| data | 原始展示数据 | Array | [] |
|
||||
| columns | 列属性 | Array | [] |
|
||||
| defaultExpandAll | 默认是否全部展开 | Boolean | false |
|
||||
| defaultChildren | 指定子树为节点对象的某个属性值 | String | children | |
|
||||
| indent | 相邻级节点间的水平缩进,单位为像素 | Number | 50 |
|
||||
|
||||
> 任何 `el-table` 的属性都支持,例如`border`、`fit`、`size`或者`@select`、`@cell-click`等方法。详情属性见`el-table`文档。
|
||||
|
||||
---
|
||||
|
||||
### 代码示例
|
||||
|
||||
```html
|
||||
<tree-table :data="data" :columns="columns" border>
|
||||
```
|
||||
|
||||
#### data(**必填**)
|
||||
|
||||
```js
|
||||
const data = [
|
||||
{
|
||||
name:'1'
|
||||
children: [
|
||||
{
|
||||
name: '1-1'
|
||||
},
|
||||
{
|
||||
name: '1-2'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: `2`
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
#### columns(**必填**)
|
||||
|
||||
- label: 显示在表头的文字
|
||||
- key: 对应 data 的 key。treeTable 将显示相应的 value
|
||||
- expand: `true` or `false`。若为 true,则在该列显示展开收起图标
|
||||
- checkbox: `true` or `false`。若为 true,则在该列显示`checkbox`
|
||||
- width: 每列的宽度,为一个数字(可选)。例如`200`
|
||||
- align: 对齐方式 `left/center/right`
|
||||
- header-align: 表头对齐方式 `left/center/right`
|
||||
|
||||
```javascript
|
||||
const columns = [
|
||||
{
|
||||
label: 'Checkbox',
|
||||
checkbox: true
|
||||
},
|
||||
{
|
||||
label: '',
|
||||
key: 'id',
|
||||
expand: true
|
||||
},
|
||||
{
|
||||
label: 'Event',
|
||||
key: 'event',
|
||||
width: 200,
|
||||
align: 'left'
|
||||
},
|
||||
{
|
||||
label: 'Scope',
|
||||
key: 'scope'
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
> 树表组件将会根据 columns 的 key 属性生成具名插槽,如果你需要对列数据进行自定义,通过插槽即可实现
|
||||
|
||||
```html
|
||||
<template slot="your key" slot-scope="{scope}">
|
||||
<el-tag>level: {{ scope.row._level }}</el-tag>
|
||||
<el-tag>expand: {{ scope.row._expand }}</el-tag>
|
||||
<el-tag>select: {{ scope.row._select }}</el-tag>
|
||||
</template>
|
||||
```
|
||||
|
||||
## Events
|
||||
|
||||
目前提供了几个方法,不过只是`beta`版本,之后很可能会修改。
|
||||
|
||||
```js
|
||||
this.$refs.TreeTable.addChild(row, data) //添加子元素
|
||||
this.$refs.TreeTable.addBrother(row, data) //添加兄弟元素
|
||||
this.$refs.TreeTable.delete(row) //删除该元素
|
||||
```
|
||||
|
||||
## 其他
|
||||
|
||||
如果有其他的需求,请参考[el-table](http://element-cn.eleme.io/#/en-US/component/table)的 api 自行修改 index.vue
|
||||
|
||||
# English
|
||||
|
||||
## Brief
|
||||
|
||||
This component only provides a solution for creating `TreeTable`. It is based on the `element-ui` table component. It uses the `row-style` method of `el-table` to determine whether the element needs to be hidden or displayed.
|
||||
|
||||
And this component makes full use of the features of the `vue` slot to make it user-friendly.
|
||||
|
||||
In `evel.js`, the `addAttrs` method adds several properties to the data, and `treeTotable` flattens the array. None of these operations will destroy the source data, just add properties.
|
||||
|
||||
## Props
|
||||
|
||||
| Attribute | Description | Type | Default |
|
||||
| :--------------: | :----------------------------------------------------------- | :-----: | :------: |
|
||||
| data | original display data | Array | [] |
|
||||
| columns | column attribute | Array | [] |
|
||||
| defaultExpandAll | whether to expand all nodes by default | Boolean | false |
|
||||
| defaultChildren | specify which node object is used as the node's subtree | String | children | |
|
||||
| indent | horizontal indentation of nodes in adjacent levels in pixels | Number | 50 |
|
||||
|
||||
> Any of the `el-table` properties are supported, such as `border`, `fit`, `size` or `@select`, `@cell-click`. See the ʻel-table` documentation for details.
|
||||
|
||||
---
|
||||
|
||||
### Example
|
||||
|
||||
```html
|
||||
<tree-table :data="data" :columns="columns" border>
|
||||
```
|
||||
|
||||
#### data(**Required**)
|
||||
|
||||
```js
|
||||
const data = [
|
||||
{
|
||||
name:'1'
|
||||
children: [
|
||||
{
|
||||
name: '1-1'
|
||||
},
|
||||
{
|
||||
name: '1-2'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: `2`
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
#### columns(**Required**)
|
||||
|
||||
- label: text displayed in the header
|
||||
- key: data.key will show in column
|
||||
- expand: `true` or `false`
|
||||
- checkbox: `true` or `false`
|
||||
- width: column width 。such as `200`
|
||||
- align: alignment `left/center/right`
|
||||
- header-align: alignment of the table header `left/center/right`
|
||||
|
||||
```javascript
|
||||
const columns = [
|
||||
{
|
||||
label: 'Checkbox',
|
||||
checkbox: true
|
||||
},
|
||||
{
|
||||
label: '',
|
||||
key: 'id',
|
||||
expand: true
|
||||
},
|
||||
{
|
||||
label: 'Event',
|
||||
key: 'event',
|
||||
width: 200,
|
||||
align: 'left'
|
||||
},
|
||||
{
|
||||
label: 'Scope',
|
||||
key: 'scope'
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
> The tree table component will generate a named slot based on the key property of columns. If you need to customize the column data, you can do it through the slot.
|
||||
|
||||
```html
|
||||
<template slot="your key" slot-scope="{scope}">
|
||||
<el-tag>level: {{ scope.row._level }}</el-tag>
|
||||
<el-tag>expand: {{ scope.row._expand }}</el-tag>
|
||||
<el-tag>select: {{ scope.row._select }}</el-tag>
|
||||
</template>
|
||||
```
|
||||
|
||||
## Events
|
||||
|
||||
Several methods are currently available, but only the `beta` version, which is likely to be modified later.
|
||||
|
||||
```js
|
||||
this.$refs.TreeTable.addChild(row, data) //Add child elements
|
||||
this.$refs.TreeTable.addBrother(row, data) //Add a sibling element
|
||||
this.$refs.TreeTable.delete(row) //Delete the element
|
||||
```
|
||||
|
||||
## Other
|
||||
|
||||
If you have other requirements, please refer to the [el-table](http://element-cn.eleme.io/#/en-US/component/table) api to modify the index.vue
|
@@ -1,29 +1,48 @@
|
||||
/**
|
||||
* @Author: jianglei
|
||||
* @Date: 2017-10-12 12:06:49
|
||||
*/
|
||||
'use strict'
|
||||
import Vue from 'vue'
|
||||
export default function treeToArray(data, expandAll, parent = null, level = null) {
|
||||
|
||||
// Flattened array
|
||||
export default function treeToArray(data, children = 'children') {
|
||||
let tmp = []
|
||||
Array.from(data).forEach(function(record) {
|
||||
if (record._expanded === undefined) {
|
||||
Vue.set(record, '_expanded', expandAll)
|
||||
}
|
||||
let _level = 1
|
||||
if (level !== undefined && level !== null) {
|
||||
_level = level + 1
|
||||
}
|
||||
Vue.set(record, '_level', _level)
|
||||
// 如果有父元素
|
||||
if (parent) {
|
||||
Vue.set(record, 'parent', parent)
|
||||
}
|
||||
tmp.push(record)
|
||||
if (record.children && record.children.length > 0) {
|
||||
const children = treeToArray(record.children, expandAll, record, _level)
|
||||
tmp = tmp.concat(children)
|
||||
data.forEach((item, index) => {
|
||||
Vue.set(item, '_index', index)
|
||||
tmp.push(item)
|
||||
if (item[children] && item[children].length > 0) {
|
||||
const res = treeToArray(item[children], children)
|
||||
tmp = tmp.concat(res)
|
||||
}
|
||||
})
|
||||
return tmp
|
||||
}
|
||||
|
||||
export function addAttrs(data, { parent = null, preIndex = false, level = 0, expand = false, children = 'children', show = true, select = false } = {}) {
|
||||
data.forEach((item, index) => {
|
||||
const _id = (preIndex ? `${preIndex}-${index}` : index) + ''
|
||||
Vue.set(item, '_id', _id)
|
||||
Vue.set(item, '_level', level)
|
||||
Vue.set(item, '_expand', expand)
|
||||
Vue.set(item, '_parent', parent)
|
||||
Vue.set(item, '_show', show)
|
||||
Vue.set(item, '_select', select)
|
||||
if (item[children] && item[children].length > 0) {
|
||||
addAttrs(item[children], {
|
||||
parent: item,
|
||||
level: level + 1,
|
||||
expand,
|
||||
preIndex: _id,
|
||||
children,
|
||||
status,
|
||||
select
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function cleanParentAttr(data, children = 'children') {
|
||||
data.forEach(item => {
|
||||
item._parent = null
|
||||
if (item[children] && item[children].length > 0) {
|
||||
addAttrs(item[children], children)
|
||||
}
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
@@ -1,127 +1,193 @@
|
||||
<template>
|
||||
<el-table :data="formatData" :row-style="showRow" v-bind="$attrs">
|
||||
<el-table-column v-if="columns.length===0" width="150">
|
||||
<el-table :data="tableData" :row-style="showRow" v-bind="$attrs" v-on="$listeners">
|
||||
<slot name="selection" />
|
||||
<slot name="pre-column" />
|
||||
<el-table-column
|
||||
v-for="item in columns"
|
||||
:key="item.key"
|
||||
:label="item.label"
|
||||
:width="item.width"
|
||||
:align="item.align||'center'"
|
||||
:header-align="item.headerAlign"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<span v-for="space in scope.row._level" :key="space" class="ms-tree-space"/>
|
||||
<span v-if="iconShow(0,scope.row)" class="tree-ctrl" @click="toggleExpanded(scope.$index)">
|
||||
<i v-if="!scope.row._expanded" class="el-icon-plus"/>
|
||||
<i v-else class="el-icon-minus"/>
|
||||
</span>
|
||||
{{ scope.$index }}
|
||||
<slot :scope="scope" :name="item.key">
|
||||
<template v-if="item.expand">
|
||||
<span :style="{'padding-left':+scope.row._level*indent + 'px'} " />
|
||||
<span v-show="showSperadIcon(scope.row)" class="tree-ctrl" @click="toggleExpanded(scope.$index)">
|
||||
<i v-if="!scope.row._expand" class="el-icon-plus" />
|
||||
<i v-else class="el-icon-minus" />
|
||||
</span>
|
||||
</template>
|
||||
<template v-if="item.checkbox">
|
||||
<el-checkbox
|
||||
v-if="scope.row[defaultChildren]&&scope.row[defaultChildren].length>0"
|
||||
v-model="scope.row._select"
|
||||
:style="{'padding-left':+scope.row._level*indent + 'px'} "
|
||||
:indeterminate="scope.row._select"
|
||||
@change="handleCheckAllChange(scope.row)"
|
||||
/>
|
||||
<el-checkbox
|
||||
v-else
|
||||
v-model="scope.row._select"
|
||||
:style="{'padding-left':+scope.row._level*indent + 'px'} "
|
||||
@change="handleCheckAllChange(scope.row)"
|
||||
/>
|
||||
</template>
|
||||
{{ scope.row[item.key] }}
|
||||
</slot>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-for="(column, index) in columns" v-else :key="column.value" :label="column.text" :width="column.width">
|
||||
<template slot-scope="scope">
|
||||
<!-- Todo -->
|
||||
<!-- eslint-disable-next-line vue/no-confusing-v-for-v-if -->
|
||||
<span v-for="space in scope.row._level" v-if="index === 0" :key="space" class="ms-tree-space"/>
|
||||
<span v-if="iconShow(index,scope.row)" class="tree-ctrl" @click="toggleExpanded(scope.$index)">
|
||||
<i v-if="!scope.row._expanded" class="el-icon-plus"/>
|
||||
<i v-else class="el-icon-minus"/>
|
||||
</span>
|
||||
{{ scope.row[column.value] }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<slot/>
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
Auth: Lei.j1ang
|
||||
Created: 2018/1/19-13:59
|
||||
*/
|
||||
import treeToArray from './eval'
|
||||
import treeToArray, { addAttrs } from './eval.js'
|
||||
|
||||
export default {
|
||||
name: 'TreeTable',
|
||||
props: {
|
||||
/* eslint-disable */
|
||||
data: {
|
||||
type: [Array, Object],
|
||||
required: true
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => []
|
||||
},
|
||||
columns: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
evalFunc: Function,
|
||||
evalArgs: Array,
|
||||
expandAll: {
|
||||
defaultExpandAll: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
defaultChildren: {
|
||||
type: String,
|
||||
default: 'children'
|
||||
},
|
||||
indent: {
|
||||
type: Number,
|
||||
default: 50
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
guard: 1
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 格式化数据源
|
||||
formatData: function() {
|
||||
let tmp
|
||||
if (!Array.isArray(this.data)) {
|
||||
tmp = [this.data]
|
||||
} else {
|
||||
tmp = this.data
|
||||
children() {
|
||||
return this.defaultChildren
|
||||
},
|
||||
tableData() {
|
||||
const data = this.data
|
||||
if (this.data.length === 0) {
|
||||
return []
|
||||
}
|
||||
const func = this.evalFunc || treeToArray
|
||||
const args = this.evalArgs ? Array.concat([tmp, this.expandAll], this.evalArgs) : [tmp, this.expandAll]
|
||||
return func.apply(null, args)
|
||||
addAttrs(data, {
|
||||
expand: this.defaultExpandAll,
|
||||
children: this.defaultChildren
|
||||
})
|
||||
|
||||
const retval = treeToArray(data, this.defaultChildren)
|
||||
return retval
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showRow: function(row) {
|
||||
const show = (row.row.parent ? (row.row.parent._expanded && row.row.parent._show) : true)
|
||||
row.row._show = show
|
||||
return show ? 'animation:treeTableShow 1s;-webkit-animation:treeTableShow 1s;' : 'display:none;'
|
||||
addBrother(row, data) {
|
||||
if (row._parent) {
|
||||
row._parent.children.push(data)
|
||||
} else {
|
||||
this.data.push(data)
|
||||
}
|
||||
},
|
||||
// 切换下级是否展开
|
||||
toggleExpanded: function(trIndex) {
|
||||
const record = this.formatData[trIndex]
|
||||
record._expanded = !record._expanded
|
||||
addChild(row, data) {
|
||||
if (!row.children) {
|
||||
this.$set(row, 'children', [])
|
||||
}
|
||||
row.children.push(data)
|
||||
},
|
||||
// 图标显示
|
||||
iconShow(index, record) {
|
||||
return (index === 0 && record.children && record.children.length > 0)
|
||||
delete(row) {
|
||||
const { _index, _parent } = row
|
||||
if (_parent) {
|
||||
_parent.children.splice(_index, 1)
|
||||
} else {
|
||||
this.data.splice(_index, 1)
|
||||
}
|
||||
},
|
||||
getData() {
|
||||
return this.tableData
|
||||
},
|
||||
showRow: function({ row }) {
|
||||
const parent = row._parent
|
||||
const show = parent ? parent._expand && parent._show : true
|
||||
row._show = show
|
||||
return show
|
||||
? 'animation:treeTableShow 1s;-webkit-animation:treeTableShow 1s;'
|
||||
: 'display:none;'
|
||||
},
|
||||
showSperadIcon(record) {
|
||||
return record[this.children] && record[this.children].length > 0
|
||||
},
|
||||
toggleExpanded(trIndex) {
|
||||
const record = this.tableData[trIndex]
|
||||
const expand = !record._expand
|
||||
record._expand = expand
|
||||
},
|
||||
handleCheckAllChange(row) {
|
||||
this.selcetRecursion(row, row._select, this.defaultChildren)
|
||||
this.isIndeterminate = row._select
|
||||
},
|
||||
selcetRecursion(row, select, children = 'children') {
|
||||
if (select) {
|
||||
this.$set(row, '_expand', true)
|
||||
this.$set(row, '_show', true)
|
||||
}
|
||||
const sub_item = row[children]
|
||||
if (sub_item && sub_item.length > 0) {
|
||||
sub_item.map(child => {
|
||||
child._select = select
|
||||
this.selcetRecursion(child, select, children)
|
||||
})
|
||||
}
|
||||
},
|
||||
updateTreeNode(item) {
|
||||
return new Promise(resolve => {
|
||||
const { _id, _parent } = item
|
||||
const index = _id.split('-').slice(-1)[0] // get last index
|
||||
if (_parent) {
|
||||
_parent.children.splice(index, 1, item)
|
||||
resolve(this.data)
|
||||
} else {
|
||||
this.data.splice(index, 1, item)
|
||||
resolve(this.data)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style rel="stylesheet/css">
|
||||
@keyframes treeTableShow {
|
||||
from {opacity: 0;}
|
||||
to {opacity: 1;}
|
||||
}
|
||||
@-webkit-keyframes treeTableShow {
|
||||
from {opacity: 0;}
|
||||
to {opacity: 1;}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" rel="stylesheet/scss" scoped>
|
||||
$color-blue: #2196F3;
|
||||
$space-width: 18px;
|
||||
.ms-tree-space {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
display: inline-block;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
width: $space-width;
|
||||
height: 14px;
|
||||
&::before {
|
||||
content: ""
|
||||
}
|
||||
<style>
|
||||
@keyframes treeTableShow {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
.processContainer{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
table td {
|
||||
line-height: 26px;
|
||||
}
|
||||
@-webkit-keyframes treeTableShow {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.tree-ctrl{
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
color: $color-blue;
|
||||
margin-left: -$space-width;
|
||||
}
|
||||
.tree-ctrl {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
color: #2196f3;
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,89 +1,220 @@
|
||||
|
||||
- [Enlgish](#Brief)
|
||||
|
||||
# 中文
|
||||
|
||||
## 写在前面
|
||||
此组件仅提供一个创建TreeTable的解决思路
|
||||
|
||||
## prop说明
|
||||
#### *data*
|
||||
**必填**
|
||||
此组件仅提供一个创建 `TreeTable` 的解决思路。它基于`element-ui`的 table 组件实现,通过`el-table`的`row-style`方法,在里面判断元素是否需要隐藏或者显示,从而实现`TreeTable`的展开与收起。
|
||||
|
||||
原始数据,要求是一个数组或者对象
|
||||
```javascript
|
||||
[{
|
||||
key1: value1,
|
||||
key2: value2,
|
||||
children: [{
|
||||
key1: value1
|
||||
并且本组件充分利用 `vue` 插槽的特性来方便用户自定义。
|
||||
|
||||
`evel.js` 里面,`addAttrs` 方法会给数据添加几个属性,`treeTotable` 会对数组扁平化。这些操作都不会破坏源数据,只是会新增属性。
|
||||
|
||||
## Props 说明
|
||||
|
||||
| Attribute | Description | Type | Default |
|
||||
| :--------------: | :--------------------------------- | :-----: | :------: |
|
||||
| data | 原始展示数据 | Array | [] |
|
||||
| columns | 列属性 | Array | [] |
|
||||
| defaultExpandAll | 默认是否全部展开 | Boolean | false |
|
||||
| defaultChildren | 指定子树为节点对象的某个属性值 | String | children | |
|
||||
| indent | 相邻级节点间的水平缩进,单位为像素 | Number | 50 |
|
||||
|
||||
> 任何 `el-table` 的属性都支持,例如`border`、`fit`、`size`或者`@select`、`@cell-click`等方法。详情属性见`el-table`文档。
|
||||
|
||||
---
|
||||
|
||||
### 代码示例
|
||||
|
||||
```html
|
||||
<tree-table :data="data" :columns="columns" border>
|
||||
```
|
||||
|
||||
#### data(**必填**)
|
||||
|
||||
```js
|
||||
const data = [
|
||||
{
|
||||
name:'1'
|
||||
children: [
|
||||
{
|
||||
name: '1-1'
|
||||
},
|
||||
{
|
||||
key1: value1
|
||||
}]
|
||||
},
|
||||
{
|
||||
key1: value1
|
||||
}]
|
||||
```
|
||||
或者
|
||||
```javascript
|
||||
{
|
||||
key1: value1,
|
||||
key2: value2,
|
||||
children: [{
|
||||
key1: value1
|
||||
name: '1-2'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: `2`
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
#### columns(**必填**)
|
||||
|
||||
- label: 显示在表头的文字
|
||||
- key: 对应 data 的 key。treeTable 将显示相应的 value
|
||||
- expand: `true` or `false`。若为 true,则在该列显示展开收起图标
|
||||
- checkbox: `true` or `false`。若为 true,则在该列显示`checkbox`
|
||||
- width: 每列的宽度,为一个数字(可选)。例如`200`
|
||||
- align: 对齐方式 `left/center/right`
|
||||
- header-align: 表头对齐方式 `left/center/right`
|
||||
|
||||
```javascript
|
||||
const columns = [
|
||||
{
|
||||
label: 'Checkbox',
|
||||
checkbox: true
|
||||
},
|
||||
{
|
||||
label: '',
|
||||
key: 'id',
|
||||
expand: true
|
||||
},
|
||||
{
|
||||
label: 'Event',
|
||||
key: 'event',
|
||||
width: 200,
|
||||
align: 'left'
|
||||
},
|
||||
{
|
||||
label: 'Scope',
|
||||
key: 'scope'
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
> 树表组件将会根据 columns 的 key 属性生成具名插槽,如果你需要对列数据进行自定义,通过插槽即可实现
|
||||
|
||||
```html
|
||||
<template slot="your key" slot-scope="{scope}">
|
||||
<el-tag>level: {{ scope.row._level }}</el-tag>
|
||||
<el-tag>expand: {{ scope.row._expand }}</el-tag>
|
||||
<el-tag>select: {{ scope.row._select }}</el-tag>
|
||||
</template>
|
||||
```
|
||||
|
||||
## Events
|
||||
|
||||
目前提供了几个方法,不过只是`beta`版本,之后很可能会修改。
|
||||
|
||||
```js
|
||||
this.$refs.TreeTable.addChild(row, data) //添加子元素
|
||||
this.$refs.TreeTable.addBrother(row, data) //添加兄弟元素
|
||||
this.$refs.TreeTable.delete(row) //删除该元素
|
||||
```
|
||||
|
||||
## 其他
|
||||
|
||||
如果有其他的需求,请参考[el-table](http://element-cn.eleme.io/#/en-US/component/table)的 api 自行修改 index.vue
|
||||
|
||||
# English
|
||||
|
||||
## Brief
|
||||
|
||||
This component only provides a solution for creating `TreeTable`. It is based on the `element-ui` table component. It uses the `row-style` method of `el-table` to determine whether the element needs to be hidden or displayed.
|
||||
|
||||
And this component makes full use of the features of the `vue` slot to make it user-friendly.
|
||||
|
||||
In `evel.js`, the `addAttrs` method adds several properties to the data, and `treeTotable` flattens the array. None of these operations will destroy the source data, just add properties.
|
||||
|
||||
## Props
|
||||
|
||||
| Attribute | Description | Type | Default |
|
||||
| :--------------: | :----------------------------------------------------------- | :-----: | :------: |
|
||||
| data | original display data | Array | [] |
|
||||
| columns | column attribute | Array | [] |
|
||||
| defaultExpandAll | whether to expand all nodes by default | Boolean | false |
|
||||
| defaultChildren | specify which node object is used as the node's subtree | String | children | |
|
||||
| indent | horizontal indentation of nodes in adjacent levels in pixels | Number | 50 |
|
||||
|
||||
> Any of the `el-table` properties are supported, such as `border`, `fit`, `size` or `@select`, `@cell-click`. See the ʻel-table` documentation for details.
|
||||
|
||||
---
|
||||
|
||||
### Example
|
||||
|
||||
```html
|
||||
<tree-table :data="data" :columns="columns" border>
|
||||
```
|
||||
|
||||
#### data(**Required**)
|
||||
|
||||
```js
|
||||
const data = [
|
||||
{
|
||||
name:'1'
|
||||
children: [
|
||||
{
|
||||
name: '1-1'
|
||||
},
|
||||
{
|
||||
key1: value1
|
||||
}]
|
||||
}
|
||||
```
|
||||
name: '1-2'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: `2`
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
#### columns
|
||||
列属性,要求是一个数组
|
||||
#### columns(**Required**)
|
||||
|
||||
1. text: 显示在表头的文字
|
||||
2. value: 对应data的key。treeTable将显示相应的value
|
||||
3. width: 每列的宽度,为一个数字(可选)
|
||||
|
||||
如果你想要每个字段都有自定义的样式或者嵌套其他组件,columns可不提供,直接像在el-table一样写即可,如果没有自定义内容,提供columns将更加的便捷方便
|
||||
|
||||
如果你有几个字段是需要自定义的,几个不需要,那么可以将不需要自定义的字段放入columns,将需要自定义的内容放入到slot中,详情见后文
|
||||
```javascript
|
||||
[{
|
||||
value:string,
|
||||
text:string,
|
||||
width:number
|
||||
},{
|
||||
value:string,
|
||||
text:string,
|
||||
width:number
|
||||
}]
|
||||
```
|
||||
- label: text displayed in the header
|
||||
- key: data.key will show in column
|
||||
- expand: `true` or `false`
|
||||
- checkbox: `true` or `false`
|
||||
- width: column width 。such as `200`
|
||||
- align: alignment `left/center/right`
|
||||
- header-align: alignment of the table header `left/center/right`
|
||||
|
||||
#### expandAll
|
||||
是否默认全部展开,boolean值,默认为false
|
||||
```javascript
|
||||
const columns = [
|
||||
{
|
||||
label: 'Checkbox',
|
||||
checkbox: true
|
||||
},
|
||||
{
|
||||
label: '',
|
||||
key: 'id',
|
||||
expand: true
|
||||
},
|
||||
{
|
||||
label: 'Event',
|
||||
key: 'event',
|
||||
width: 200,
|
||||
align: 'left'
|
||||
},
|
||||
{
|
||||
label: 'Scope',
|
||||
key: 'scope'
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
#### evalFunc
|
||||
解析函数,function,非必须
|
||||
> The tree table component will generate a named slot based on the key property of columns. If you need to customize the column data, you can do it through the slot.
|
||||
|
||||
如果不提供,将使用默认的[evalFunc](./eval.js)
|
||||
```html
|
||||
<template slot="your key" slot-scope="{scope}">
|
||||
<el-tag>level: {{ scope.row._level }}</el-tag>
|
||||
<el-tag>expand: {{ scope.row._expand }}</el-tag>
|
||||
<el-tag>select: {{ scope.row._select }}</el-tag>
|
||||
</template>
|
||||
```
|
||||
|
||||
如果提供了evalFunc,那么会用提供的evalFunc去解析data,并返回treeTable渲染所需要的值。如何编写一个evalFunc,请参考[*eval.js*](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/components/TreeTable/eval.js)或[*customEval.js*](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/example/table/treeTable/customEval.js)
|
||||
## Events
|
||||
|
||||
#### evalArgs
|
||||
解析函数的参数,是一个数组
|
||||
Several methods are currently available, but only the `beta` version, which is likely to be modified later.
|
||||
|
||||
**请注意,自定义的解析函数参数第一个为this.data,第二个参数为, this.expandAll,你不需要在evalArgs填写。一定记住,这两个参数是强制性的,并且位置不可颠倒** *this.data为需要解析的数据,this.expandAll为是否默认展开*
|
||||
```js
|
||||
this.$refs.TreeTable.addChild(row, data) //Add child elements
|
||||
this.$refs.TreeTable.addBrother(row, data) //Add a sibling element
|
||||
this.$refs.TreeTable.delete(row) //Delete the element
|
||||
```
|
||||
|
||||
如你的解析函数需要的参数为`(this.data, this.expandAll,1,2,3,4)`,那么你只需要将`[1,2,3,4]`赋值给`evalArgs`就可以了
|
||||
|
||||
如果你的解析函数参数只有`(this.data, this.expandAll)`,那么就可以不用填写evalArgs了
|
||||
|
||||
具体可参考[*customEval.js*](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/example/table/treeTable/customEval.js)的函数参数和[customTreeTable](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/example/table/treeTable/customTreeTable.vue)的`evalArgs`属性值
|
||||
## Other
|
||||
|
||||
## slot
|
||||
这是一个自定义列的插槽。
|
||||
|
||||
默认情况下,treeTable只有一行行展示数据的功能。但是一般情况下,我们会要给行加上一个操作按钮或者根据当行数据展示不同的样式,这时我们就需要自定义列了。请参考[customTreeTable](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/example/table/treeTable/customTreeTable.vue),[实例效果](http://panjiachen.github.io/vue-element-admin/#/example/table/custom-tree-table)
|
||||
|
||||
`slot`和`columns属性`可同时存在,columns里面的数据列会在slot自定义列的左边展示
|
||||
|
||||
## 其他
|
||||
如果有其他的需求,请参考[el-table](http://element-cn.eleme.io/#/en-US/component/table)的api自行修改index.vue
|
||||
If you have other requirements, please refer to the [el-table](http://element-cn.eleme.io/#/en-US/component/table) api to modify the index.vue
|
||||
|
@@ -7,15 +7,16 @@
|
||||
:on-success="handleImageSuccess"
|
||||
class="image-uploader"
|
||||
drag
|
||||
action="https://httpbin.org/post">
|
||||
<i class="el-icon-upload"/>
|
||||
action="https://httpbin.org/post"
|
||||
>
|
||||
<i class="el-icon-upload" />
|
||||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||
</el-upload>
|
||||
<div class="image-preview">
|
||||
<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"/>
|
||||
<i class="el-icon-delete" @click="rmImage" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -7,15 +7,16 @@
|
||||
:on-success="handleImageSuccess"
|
||||
class="image-uploader"
|
||||
drag
|
||||
action="https://httpbin.org/post">
|
||||
<i class="el-icon-upload"/>
|
||||
action="https://httpbin.org/post"
|
||||
>
|
||||
<i class="el-icon-upload" />
|
||||
<div class="el-upload__text">Drag或<em>点击上传</em></div>
|
||||
</el-upload>
|
||||
<div v-show="imageUrl.length>0" class="image-preview">
|
||||
<div v-show="imageUrl.length>1" class="image-preview-wrapper">
|
||||
<img :src="imageUrl">
|
||||
<div class="image-preview-action">
|
||||
<i class="el-icon-delete" @click="rmImage"/>
|
||||
<i class="el-icon-delete" @click="rmImage" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -7,15 +7,16 @@
|
||||
:on-success="handleImageSuccess"
|
||||
class="image-uploader"
|
||||
drag
|
||||
action="https://httpbin.org/post">
|
||||
<i class="el-icon-upload"/>
|
||||
action="https://httpbin.org/post"
|
||||
>
|
||||
<i class="el-icon-upload" />
|
||||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||
</el-upload>
|
||||
<div class="image-preview image-app-preview">
|
||||
<div v-show="imageUrl.length>1" class="image-preview-wrapper">
|
||||
<img :src="imageUrl">
|
||||
<div class="image-preview-action">
|
||||
<i class="el-icon-delete" @click="rmImage"/>
|
||||
<i class="el-icon-delete" @click="rmImage" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -23,7 +24,7 @@
|
||||
<div v-show="imageUrl.length>1" class="image-preview-wrapper">
|
||||
<img :src="imageUrl">
|
||||
<div class="image-preview-action">
|
||||
<i class="el-icon-delete" @click="rmImage"/>
|
||||
<i class="el-icon-delete" @click="rmImage" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -83,7 +84,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
@import "src/styles/mixin.scss";
|
||||
@import "~@/styles/mixin.scss";
|
||||
.upload-container {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
@@ -82,8 +82,7 @@ export default {
|
||||
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 workbook = XLSX.read(data, { type: 'array' })
|
||||
const firstSheetName = workbook.SheetNames[0]
|
||||
const worksheet = workbook.Sheets[firstSheetName]
|
||||
const header = this.getHeaderRow(worksheet)
|
||||
@@ -95,14 +94,6 @@ export default {
|
||||
reader.readAsArrayBuffer(rawFile)
|
||||
})
|
||||
},
|
||||
fixData(data) {
|
||||
let o = ''
|
||||
let l = 0
|
||||
const w = 10240
|
||||
for (; l < data.byteLength / w; ++l) o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w, l * w + w)))
|
||||
o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w)))
|
||||
return o
|
||||
},
|
||||
getHeaderRow(sheet) {
|
||||
const headers = []
|
||||
const range = XLSX.utils.decode_range(sheet['!ref'])
|
||||
|
@@ -1,4 +1,4 @@
|
||||
export default{
|
||||
export default {
|
||||
bind(el, binding, vnode) {
|
||||
const dialogHeaderEl = el.querySelector('.el-dialog__header')
|
||||
const dragDom = el.querySelector('.el-dialog')
|
||||
|
42
src/directive/el-table/adaptive.js
Normal file
@@ -0,0 +1,42 @@
|
||||
|
||||
import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event'
|
||||
|
||||
/**
|
||||
* How to use
|
||||
* <el-table height="100px" v-el-height-adaptive-table="{bottomOffset: 30}">...</el-table>
|
||||
* el-table height is must be set
|
||||
* bottomOffset: 30(default) // The height of the table from the bottom of the page.
|
||||
*/
|
||||
|
||||
const doResize = (el, binding, vnode) => {
|
||||
const { componentInstance: $table } = vnode
|
||||
|
||||
const { value } = binding
|
||||
|
||||
if (!$table.height) {
|
||||
throw new Error(`el-$table must set the height. Such as height='100px'`)
|
||||
}
|
||||
const bottomOffset = (value && value.bottomOffset) || 30
|
||||
|
||||
if (!$table) return
|
||||
|
||||
const height = window.innerHeight - el.getBoundingClientRect().top - bottomOffset
|
||||
$table.layout.setHeight(height)
|
||||
$table.doLayout()
|
||||
}
|
||||
|
||||
export default {
|
||||
bind(el, binding, vnode) {
|
||||
el.resizeListener = () => {
|
||||
doResize(el, binding, vnode)
|
||||
}
|
||||
|
||||
addResizeListener(el, el.resizeListener)
|
||||
},
|
||||
inserted(el, binding, vnode) {
|
||||
doResize(el, binding, vnode)
|
||||
},
|
||||
unbind(el) {
|
||||
removeResizeListener(el, el.resizeListener)
|
||||
}
|
||||
}
|
14
src/directive/el-table/index.js
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
import adaptive from './adaptive'
|
||||
|
||||
const install = function(Vue) {
|
||||
Vue.directive('el-height-adaptive-table', adaptive)
|
||||
}
|
||||
|
||||
if (window.Vue) {
|
||||
window['el-height-adaptive-table'] = adaptive
|
||||
Vue.use(install); // eslint-disable-line
|
||||
}
|
||||
|
||||
adaptive.install = install
|
||||
export default adaptive
|
@@ -1,7 +1,7 @@
|
||||
|
||||
import store from '@/store'
|
||||
|
||||
export default{
|
||||
export default {
|
||||
inserted(el, binding, vnode) {
|
||||
const { value } = binding
|
||||
const roles = store.getters && store.getters.roles
|
||||
|
@@ -1,42 +1,73 @@
|
||||
import './waves.css'
|
||||
|
||||
export default{
|
||||
bind(el, binding) {
|
||||
el.addEventListener('click', e => {
|
||||
const customOpts = Object.assign({}, binding.value)
|
||||
const opts = Object.assign({
|
||||
const context = '@@wavesContext'
|
||||
|
||||
function handleClick(el, binding) {
|
||||
function handle(e) {
|
||||
const customOpts = Object.assign({}, binding.value)
|
||||
const opts = Object.assign(
|
||||
{
|
||||
ele: el, // 波纹作用元素
|
||||
type: 'hit', // hit 点击位置扩散 center中心点扩展
|
||||
color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色
|
||||
}, customOpts)
|
||||
const target = opts.ele
|
||||
if (target) {
|
||||
target.style.position = 'relative'
|
||||
target.style.overflow = 'hidden'
|
||||
const rect = target.getBoundingClientRect()
|
||||
let ripple = target.querySelector('.waves-ripple')
|
||||
if (!ripple) {
|
||||
ripple = document.createElement('span')
|
||||
ripple.className = 'waves-ripple'
|
||||
ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px'
|
||||
target.appendChild(ripple)
|
||||
} else {
|
||||
ripple.className = 'waves-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.documentElement.scrollTop || document.body.scrollTop) + 'px'
|
||||
ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.documentElement.scrollLeft || document.body.scrollLeft) + 'px'
|
||||
}
|
||||
ripple.style.backgroundColor = opts.color
|
||||
ripple.className = 'waves-ripple z-active'
|
||||
return false
|
||||
},
|
||||
customOpts
|
||||
)
|
||||
const target = opts.ele
|
||||
if (target) {
|
||||
target.style.position = 'relative'
|
||||
target.style.overflow = 'hidden'
|
||||
const rect = target.getBoundingClientRect()
|
||||
let ripple = target.querySelector('.waves-ripple')
|
||||
if (!ripple) {
|
||||
ripple = document.createElement('span')
|
||||
ripple.className = 'waves-ripple'
|
||||
ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px'
|
||||
target.appendChild(ripple)
|
||||
} else {
|
||||
ripple.className = 'waves-ripple'
|
||||
}
|
||||
}, false)
|
||||
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.documentElement.scrollTop ||
|
||||
document.body.scrollTop) + 'px'
|
||||
ripple.style.left =
|
||||
(e.pageX - rect.left - ripple.offsetWidth / 2 - document.documentElement.scrollLeft ||
|
||||
document.body.scrollLeft) + 'px'
|
||||
}
|
||||
ripple.style.backgroundColor = opts.color
|
||||
ripple.className = 'waves-ripple z-active'
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if (!el[context]) {
|
||||
el[context] = {
|
||||
removeHandle: handle
|
||||
}
|
||||
} else {
|
||||
el[context].removeHandle = handle
|
||||
}
|
||||
|
||||
return handle
|
||||
}
|
||||
|
||||
export default {
|
||||
bind(el, binding) {
|
||||
el.addEventListener('click', handleClick(el, binding), false)
|
||||
},
|
||||
update(el, binding) {
|
||||
el.removeEventListener('click', el[context].removeHandle, false)
|
||||
el.addEventListener('click', handleClick(el, binding), false)
|
||||
},
|
||||
unbind(el) {
|
||||
el.removeEventListener('click', el[context].removeHandle, false)
|
||||
el[context] = null
|
||||
delete el[context]
|
||||
}
|
||||
}
|
||||
|
@@ -1 +1 @@
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><g><path d="M106.133 67.2a4.797 4.797 0 0 0-4.8 4.8c0 .187.014.36.027.533h-.027V118.4H9.6V26.667h50.133c2.654 0 4.8-2.147 4.8-4.8 0-2.654-2.146-4.8-4.8-4.8H9.6a9.594 9.594 0 0 0-9.6 9.6V118.4c0 5.307 4.293 9.6 9.6 9.6h91.733c5.307 0 9.6-4.293 9.6-9.6V72.533h-.026c.013-.173.026-.346.026-.533 0-2.653-2.146-4.8-4.8-4.8z"/><path d="M125.16 13.373L114.587 2.8c-3.747-3.747-9.854-3.72-13.6.027l-52.96 52.96a4.264 4.264 0 0 0-.907 1.36L33.813 88.533c-.746 1.76-.226 3.534.907 4.68 1.133 1.147 2.92 1.667 4.693.92l31.4-13.293c.507-.213.96-.52 1.36-.907l52.96-52.96c3.747-3.746 3.774-9.853.027-13.6zM66.107 72.4l-18.32 7.76 7.76-18.32L92.72 24.667l10.56 10.56L66.107 72.4zm52.226-52.227l-8.266 8.267-10.56-10.56 8.266-8.267.027-.026 10.56 10.56-.027.026z"/></g></svg>
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M106.133 67.2a4.797 4.797 0 0 0-4.8 4.8c0 .187.014.36.027.533h-.027V118.4H9.6V26.667h50.133c2.654 0 4.8-2.147 4.8-4.8 0-2.654-2.146-4.8-4.8-4.8H9.6a9.594 9.594 0 0 0-9.6 9.6V118.4c0 5.307 4.293 9.6 9.6 9.6h91.733c5.307 0 9.6-4.293 9.6-9.6V72.533h-.026c.013-.173.026-.346.026-.533 0-2.653-2.146-4.8-4.8-4.8z"/><path d="M125.16 13.373L114.587 2.8c-3.747-3.747-9.854-3.72-13.6.027l-52.96 52.96a4.264 4.264 0 0 0-.907 1.36L33.813 88.533c-.746 1.76-.226 3.534.907 4.68 1.133 1.147 2.92 1.667 4.693.92l31.4-13.293c.507-.213.96-.52 1.36-.907l52.96-52.96c3.747-3.746 3.774-9.853.027-13.6zM66.107 72.4l-18.32 7.76 7.76-18.32L92.72 24.667l10.56 10.56L66.107 72.4zm52.226-52.227l-8.266 8.267-10.56-10.56 8.266-8.267.027-.026 10.56 10.56-.027.026z"/></svg>
|
Before Width: | Height: | Size: 825 B After Width: | Height: | Size: 818 B |
@@ -1 +1 @@
|
||||
<svg width="128" height="96" xmlns="http://www.w3.org/2000/svg"><g><path d="M64.125 56.975L120.188.912A12.476 12.476 0 0 0 115.5 0h-103c-1.588 0-3.113.3-4.513.838l56.138 56.137z"/><path d="M64.125 68.287l-62.3-62.3A12.42 12.42 0 0 0 0 12.5v71C0 90.4 5.6 96 12.5 96h103c6.9 0 12.5-5.6 12.5-12.5v-71a12.47 12.47 0 0 0-1.737-6.35L64.125 68.287z"/></g></svg>
|
||||
<svg width="128" height="96" xmlns="http://www.w3.org/2000/svg"><path d="M64.125 56.975L120.188.912A12.476 12.476 0 0 0 115.5 0h-103c-1.588 0-3.113.3-4.513.838l56.138 56.137z"/><path d="M64.125 68.287l-62.3-62.3A12.42 12.42 0 0 0 0 12.5v71C0 90.4 5.6 96 12.5 96h103c6.9 0 12.5-5.6 12.5-12.5v-71a12.47 12.47 0 0 0-1.737-6.35L64.125 68.287z"/></svg>
|
Before Width: | Height: | Size: 354 B After Width: | Height: | Size: 347 B |
@@ -1 +1 @@
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><g><path d="M78.208 16.576v8.384h38.72v5.376h-38.72v8.704h38.72v5.376h-38.72v8.576h38.72v5.376h-38.72v8.576h38.72v5.376h-38.72v8.576h38.72v5.376h-38.72v8.512h38.72v5.376h-38.72v11.136H128v-94.72H78.208zM0 114.368L72.128 128V0L0 13.632v100.736z"/><path d="M28.672 82.56h-11.2l14.784-23.488-14.08-22.592h11.52l8.192 14.976 8.448-14.976h11.136l-14.08 22.208L58.368 82.56H46.656l-8.768-15.68z"/></g></svg>
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M78.208 16.576v8.384h38.72v5.376h-38.72v8.704h38.72v5.376h-38.72v8.576h38.72v5.376h-38.72v8.576h38.72v5.376h-38.72v8.576h38.72v5.376h-38.72v8.512h38.72v5.376h-38.72v11.136H128v-94.72H78.208zM0 114.368L72.128 128V0L0 13.632v100.736z"/><path d="M28.672 82.56h-11.2l14.784-23.488-14.08-22.592h11.52l8.192 14.976 8.448-14.976h11.136l-14.08 22.208L58.368 82.56H46.656l-8.768-15.68z"/></svg>
|
Before Width: | Height: | Size: 466 B After Width: | Height: | Size: 459 B |
1
src/icons/svg/exit-fullscreen.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M49.217 41.329l-.136-35.24c-.06-2.715-2.302-4.345-5.022-4.405h-3.65c-2.712-.06-4.866 2.303-4.806 5.016l.152 19.164-24.151-23.79a6.698 6.698 0 0 0-9.499 0 6.76 6.76 0 0 0 0 9.526l23.93 23.713-18.345.074c-2.712-.069-5.228 1.813-5.64 5.02v3.462c.069 2.721 2.31 4.97 5.022 5.03l35.028-.207c.052.005.087.025.133.025l2.457.054a4.626 4.626 0 0 0 3.436-1.38c.88-.874 1.205-2.096 1.169-3.462l-.262-2.465c0-.048.182-.081.182-.136h.002zm52.523 51.212l18.32-.073c2.713.06 5.224-1.609 5.64-4.815v-3.462c-.068-2.722-2.317-4.97-5.021-5.04l-34.58.21c-.053 0-.086-.021-.138-.021l-2.451-.06a4.64 4.64 0 0 0-3.445 1.381c-.885.868-1.201 2.094-1.174 3.46l.27 2.46c.005.06-.177.095-.177.141l.141 34.697c.069 2.713 2.31 4.338 5.022 4.397l3.45.006c2.705.062 4.867-2.31 4.8-5.026l-.153-18.752 24.151 23.946a6.69 6.69 0 0 0 9.494 0 6.747 6.747 0 0 0 0-9.523L101.74 92.54v.001zM48.125 80.662a4.636 4.636 0 0 0-3.437-1.382l-2.457.06c-.05 0-.082.022-.137.022l-35.025-.21c-2.712.07-4.957 2.318-5.022 5.04v3.462c.409 3.206 2.925 4.874 5.633 4.814l18.554.06-24.132 23.928c-2.62 2.626-2.62 6.89 0 9.524a6.694 6.694 0 0 0 9.496 0l24.155-23.79-.155 18.866c-.06 2.722 2.094 5.093 4.801 5.025h3.65c2.72-.069 4.962-1.685 5.022-4.406l.141-34.956c0-.05-.182-.082-.182-.136l.262-2.46c.03-1.366-.286-2.592-1.166-3.46h-.001zM80.08 47.397a4.62 4.62 0 0 0 3.443 1.374l2.45-.054c.055 0 .088-.02.143-.028l35.08.21c2.712-.062 4.953-2.312 5.021-5.033l.009-3.463c-.417-3.211-2.937-5.084-5.64-5.025l-18.615-.073 23.917-23.715c2.63-2.623 2.63-6.879.008-9.513a6.691 6.691 0 0 0-9.494 0L92.251 26.016l.155-19.312c.065-2.713-2.097-5.085-4.802-5.025h-3.45c-2.713.069-4.954 1.693-5.022 4.406l-.139 35.247c0 .054.18.088.18.136l-.267 2.465c-.028 1.366.288 2.588 1.174 3.463v.001z"/></svg>
|
After Width: | Height: | Size: 1.8 KiB |
1
src/icons/svg/eye-open.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="128" height="128"><defs><style/></defs><path d="M512 128q69.675 0 135.51 21.163t115.498 54.997 93.483 74.837 73.685 82.006 51.67 74.837 32.17 54.827L1024 512q-2.347 4.992-6.315 13.483T998.87 560.17t-31.658 51.669-44.331 59.99-56.832 64.34-69.504 60.16-82.347 51.5-94.848 34.687T512 896q-69.675 0-135.51-21.163t-115.498-54.826-93.483-74.326-73.685-81.493-51.67-74.496-32.17-54.997L0 513.707q2.347-4.992 6.315-13.483t18.816-34.816 31.658-51.84 44.331-60.33 56.832-64.683 69.504-60.331 82.347-51.84 94.848-34.816T512 128.085zm0 85.333q-46.677 0-91.648 12.331t-81.152 31.83-70.656 47.146-59.648 54.485-48.853 57.686-37.675 52.821-26.325 43.99q12.33 21.674 26.325 43.52t37.675 52.351 48.853 57.003 59.648 53.845T339.2 767.02t81.152 31.488T512 810.667t91.648-12.331 81.152-31.659 70.656-46.848 59.648-54.186 48.853-57.344 37.675-52.651T927.957 512q-12.33-21.675-26.325-43.648t-37.675-52.65-48.853-57.345-59.648-54.186-70.656-46.848-81.152-31.659T512 213.334zm0 128q70.656 0 120.661 50.006T682.667 512 632.66 632.661 512 682.667 391.339 632.66 341.333 512t50.006-120.661T512 341.333zm0 85.334q-35.328 0-60.33 25.002T426.666 512t25.002 60.33T512 597.334t60.33-25.002T597.334 512t-25.002-60.33T512 426.666z"/></svg>
|
After Width: | Height: | Size: 1.3 KiB |
1
src/icons/svg/fullscreen.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M38.47 52L52 38.462l-23.648-23.67L43.209 0H.035L0 43.137l14.757-14.865L38.47 52zm74.773 47.726L89.526 76 76 89.536l23.648 23.672L84.795 128h43.174L128 84.863l-14.757 14.863zM89.538 52l23.668-23.648L128 43.207V.038L84.866 0 99.73 14.76 76 38.472 89.538 52zM38.46 76L14.792 99.651 0 84.794v43.173l43.137.033-14.865-14.757L52 89.53 38.46 76z"/></svg>
|
After Width: | Height: | Size: 421 B |
@@ -1 +1 @@
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><g><path d="M84.742 36.8c2.398 7.2 5.595 12.8 11.19 18.4 4.795-4.8 7.992-11.2 10.39-18.4h-21.58zm-52.748 40h20.78l-10.39-28-10.39 28z"/><path d="M111.916 0H16.009C7.218 0 .025 7.2.025 16v96c0 8.8 7.193 16 15.984 16h95.907c8.791 0 15.984-7.2 15.984-16V16c0-8.8-6.394-16-15.984-16zM72.754 103.2c-1.598 1.6-3.197 1.6-4.795 1.6-.8 0-2.398 0-3.197-.8-.8-.8-1.599 0-1.599-.8s-.799-1.6-1.598-3.2c-.8-1.6-.8-2.4-1.599-4l-3.196-8.8H28.797L25.6 96c-1.598 3.2-2.398 5.6-3.197 7.2-.8 1.6-2.398 1.6-4.795 1.6-1.599 0-3.197-.8-4.796-1.6-1.598-1.6-2.397-2.4-2.397-4 0-.8 0-1.6.799-3.2.8-1.6.8-2.4 1.598-4l17.583-44.8c.8-1.6.8-3.2 1.599-4.8.799-1.6 1.598-3.2 2.397-4 .8-.8 1.599-2.4 3.197-3.2 1.599-.8 3.197-.8 4.796-.8 1.598 0 3.196 0 4.795.8 1.598.8 2.398 1.6 3.197 3.2.799.8 1.598 2.4 2.397 4 .8 1.6 1.599 3.2 2.398 5.6l17.583 44c1.598 3.2 2.398 5.6 2.398 7.2-.8.8-1.599 2.4-2.398 4zM116.711 72c-8.791-3.2-15.185-7.2-20.78-12-5.594 5.6-12.787 9.6-21.579 12l-2.397-4c8.791-2.4 15.984-5.6 21.579-11.2C87.939 51.2 83.144 44 81.545 36h-7.992v-3.2h21.58c-1.6-2.4-3.198-5.6-4.796-8l2.397-.8c1.599 2.4 3.997 5.6 5.595 8.8h19.98v4h-7.992c-2.397 8-6.393 15.2-11.189 20 5.595 4.8 11.988 8.8 20.78 11.2l-3.197 4z"/></g></svg>
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M84.742 36.8c2.398 7.2 5.595 12.8 11.19 18.4 4.795-4.8 7.992-11.2 10.39-18.4h-21.58zm-52.748 40h20.78l-10.39-28-10.39 28z"/><path d="M111.916 0H16.009C7.218 0 .025 7.2.025 16v96c0 8.8 7.193 16 15.984 16h95.907c8.791 0 15.984-7.2 15.984-16V16c0-8.8-6.394-16-15.984-16zM72.754 103.2c-1.598 1.6-3.197 1.6-4.795 1.6-.8 0-2.398 0-3.197-.8-.8-.8-1.599 0-1.599-.8s-.799-1.6-1.598-3.2c-.8-1.6-.8-2.4-1.599-4l-3.196-8.8H28.797L25.6 96c-1.598 3.2-2.398 5.6-3.197 7.2-.8 1.6-2.398 1.6-4.795 1.6-1.599 0-3.197-.8-4.796-1.6-1.598-1.6-2.397-2.4-2.397-4 0-.8 0-1.6.799-3.2.8-1.6.8-2.4 1.598-4l17.583-44.8c.8-1.6.8-3.2 1.599-4.8.799-1.6 1.598-3.2 2.397-4 .8-.8 1.599-2.4 3.197-3.2 1.599-.8 3.197-.8 4.796-.8 1.598 0 3.196 0 4.795.8 1.598.8 2.398 1.6 3.197 3.2.799.8 1.598 2.4 2.397 4 .8 1.6 1.599 3.2 2.398 5.6l17.583 44c1.598 3.2 2.398 5.6 2.398 7.2-.8.8-1.599 2.4-2.398 4zM116.711 72c-8.791-3.2-15.185-7.2-20.78-12-5.594 5.6-12.787 9.6-21.579 12l-2.397-4c8.791-2.4 15.984-5.6 21.579-11.2C87.939 51.2 83.144 44 81.545 36h-7.992v-3.2h21.58c-1.6-2.4-3.198-5.6-4.796-8l2.397-.8c1.599 2.4 3.997 5.6 5.595 8.8h19.98v4h-7.992c-2.397 8-6.393 15.2-11.189 20 5.595 4.8 11.988 8.8 20.78 11.2l-3.197 4z"/></svg>
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
@@ -1 +1 @@
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><g><path d="M115.625 127.937H.063V12.375h57.781v12.374H12.438v90.813h90.813V70.156h12.374z"/><path d="M116.426 2.821l8.753 8.753-56.734 56.734-8.753-8.745z"/><path d="M127.893 37.982h-12.375V12.375H88.706V0h39.187z"/></g></svg>
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M115.625 127.937H.063V12.375h57.781v12.374H12.438v90.813h90.813V70.156h12.374z"/><path d="M116.426 2.821l8.753 8.753-56.734 56.734-8.753-8.745z"/><path d="M127.893 37.982h-12.375V12.375H88.706V0h39.187z"/></svg>
|
Before Width: | Height: | Size: 292 B After Width: | Height: | Size: 285 B |
1
src/icons/svg/pdf.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="128" height="128"><path d="M869.073 277.307H657.111V65.344l211.962 211.963zm-238.232 26.27V65.344l-476.498-.054v416.957h714.73v-178.67H630.841zm-335.836 360.57c-5.07-3.064-10.944-5.133-17.61-6.201-6.67-1.064-13.603-1.6-20.81-1.6h-48.821v85.641h48.822c7.206 0 14.14-.532 20.81-1.6 6.665-1.065 12.54-3.133 17.609-6.202 5.064-3.063 9.134-7.406 12.208-13.007 3.065-5.602 4.6-12.937 4.6-22.011 0-9.07-1.535-16.408-4.6-22.01-3.074-5.603-7.144-9.94-12.208-13.01zM35.82 541.805v416.904h952.358V541.805H35.821zm331.421 191.179c-3.6 11.071-9.343 20.879-17.209 29.413-7.874 8.542-18.078 15.408-30.617 20.61-12.544 5.206-27.747 7.807-45.621 7.807h-66.036v102.45h-62.831V607.517h128.867c17.874 0 33.077 2.6 45.62 7.802 12.541 5.207 22.745 12.076 30.618 20.615 7.866 8.538 13.604 18.277 17.21 29.212 3.6 10.943 5.401 22.278 5.401 34.018 0 11.477-1.8 22.752-5.402 33.819zM644.9 806.417c-5.343 17.61-13.408 32.818-24.212 45.627-10.807 12.803-24.283 22.879-40.423 30.213-16.146 7.343-35.155 11.007-57.03 11.007h-123.26V607.518h123.26c18.41 0 35.552 2.941 51.428 8.808 15.873 5.869 29.618 14.671 41.22 26.412 11.608 11.744 20.674 26.411 27.217 44.02 6.535 17.61 9.803 38.288 9.803 62.035 0 20.81-2.67 40.02-8.003 57.624zm245.362-146.07h-138.07v66.03h119.66v48.829h-119.66v118.058h-62.83V607.518h200.9v52.829h-.001zm-318.2 25.611c-6.402-8.266-14.877-14.604-25.412-19.01-10.544-4.402-23.551-6.602-39.019-6.602h-44.825v180.088h56.029c9.07 0 17.872-1.463 26.415-4.401 8.535-2.932 16.14-7.802 22.812-14.609 6.665-6.8 12.007-15.667 16.007-26.61 4.003-10.94 6.003-24.275 6.003-40.021 0-14.408-1.4-27.416-4.202-39.019-2.8-11.607-7.406-21.542-13.808-29.816zm0 0"/></svg>
|
After Width: | Height: | Size: 1.7 KiB |
@@ -1 +1 @@
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><g><path d="M95.648 118.762c0 5.035-3.563 9.121-7.979 9.121H7.98c-4.416 0-7.979-4.086-7.979-9.121C0 100.519 15.408 83.47 31.152 76.75c-9.099-6.43-15.216-17.863-15.216-30.987v-9.128c0-20.16 14.293-36.518 31.893-36.518s31.894 16.358 31.894 36.518v9.122c0 13.137-6.123 24.556-15.216 30.993 15.738 6.726 31.141 23.769 31.141 42.012z"/><path d="M106.032 118.252h15.867c3.376 0 6.101-3.125 6.101-6.972 0-13.957-11.787-26.984-23.819-32.123 6.955-4.919 11.638-13.66 11.638-23.704v-6.985c0-15.416-10.928-27.926-24.39-27.926-1.674 0-3.306.193-4.89.561 1.936 4.713 3.018 9.974 3.018 15.526v9.121c0 13.137-3.056 23.111-11.066 30.993 14.842 4.41 27.312 23.42 27.541 41.509z"/></g></svg>
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M95.648 118.762c0 5.035-3.563 9.121-7.979 9.121H7.98c-4.416 0-7.979-4.086-7.979-9.121C0 100.519 15.408 83.47 31.152 76.75c-9.099-6.43-15.216-17.863-15.216-30.987v-9.128c0-20.16 14.293-36.518 31.893-36.518s31.894 16.358 31.894 36.518v9.122c0 13.137-6.123 24.556-15.216 30.993 15.738 6.726 31.141 23.769 31.141 42.012z"/><path d="M106.032 118.252h15.867c3.376 0 6.101-3.125 6.101-6.972 0-13.957-11.787-26.984-23.819-32.123 6.955-4.919 11.638-13.66 11.638-23.704v-6.985c0-15.416-10.928-27.926-24.39-27.926-1.674 0-3.306.193-4.89.561 1.936 4.713 3.018 9.974 3.018 15.526v9.121c0 13.137-3.056 23.111-11.066 30.993 14.842 4.41 27.312 23.42 27.541 41.509z"/></svg>
|
Before Width: | Height: | Size: 738 B After Width: | Height: | Size: 731 B |
1
src/icons/svg/search.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M124.884 109.812L94.256 79.166c-.357-.357-.757-.629-1.129-.914a50.366 50.366 0 0 0 8.186-27.59C101.327 22.689 78.656 0 50.67 0 22.685 0 0 22.688 0 50.663c0 27.989 22.685 50.663 50.656 50.663 10.186 0 19.643-3.03 27.6-8.201.286.385.557.771.9 1.114l30.628 30.632a10.633 10.633 0 0 0 7.543 3.129c2.728 0 5.457-1.043 7.543-3.115 4.171-4.157 4.171-10.915.014-15.073M50.671 85.338C31.557 85.338 16 69.78 16 50.663c0-19.102 15.557-34.661 34.67-34.661 19.115 0 34.657 15.559 34.657 34.675 0 19.102-15.557 34.661-34.656 34.661"/></svg>
|
After Width: | Height: | Size: 600 B |
@@ -1 +1 @@
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><g><path d="M.006.064h127.988v31.104H.006V.064zm0 38.016h38.396v41.472H.006V38.08zm0 48.384h38.396v41.472H.006V86.464zM44.802 38.08h38.396v41.472H44.802V38.08zm0 48.384h38.396v41.472H44.802V86.464zM89.598 38.08h38.396v41.472H89.598zm0 48.384h38.396v41.472H89.598z"/><path d="M.006.064h127.988v31.104H.006V.064zm0 38.016h38.396v41.472H.006V38.08zm0 48.384h38.396v41.472H.006V86.464zM44.802 38.08h38.396v41.472H44.802V38.08zm0 48.384h38.396v41.472H44.802V86.464zM89.598 38.08h38.396v41.472H89.598zm0 48.384h38.396v41.472H89.598z"/></g></svg>
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M.006.064h127.988v31.104H.006V.064zm0 38.016h38.396v41.472H.006V38.08zm0 48.384h38.396v41.472H.006V86.464zM44.802 38.08h38.396v41.472H44.802V38.08zm0 48.384h38.396v41.472H44.802V86.464zM89.598 38.08h38.396v41.472H89.598zm0 48.384h38.396v41.472H89.598z"/><path d="M.006.064h127.988v31.104H.006V.064zm0 38.016h38.396v41.472H.006V38.08zm0 48.384h38.396v41.472H.006V86.464zM44.802 38.08h38.396v41.472H44.802V38.08zm0 48.384h38.396v41.472H44.802V86.464zM89.598 38.08h38.396v41.472H89.598zm0 48.384h38.396v41.472H89.598z"/></svg>
|
Before Width: | Height: | Size: 604 B After Width: | Height: | Size: 597 B |
1
src/icons/svg/tree-table.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M44.8 0h79.543C126.78 0 128 1.422 128 4.267v23.466c0 2.845-1.219 4.267-3.657 4.267H44.8c-2.438 0-3.657-1.422-3.657-4.267V4.267C41.143 1.422 42.362 0 44.8 0zm22.857 48h56.686c2.438 0 3.657 1.422 3.657 4.267v23.466c0 2.845-1.219 4.267-3.657 4.267H67.657C65.22 80 64 78.578 64 75.733V52.267C64 49.422 65.219 48 67.657 48zm0 48h56.686c2.438 0 3.657 1.422 3.657 4.267v23.466c0 2.845-1.219 4.267-3.657 4.267H67.657C65.22 128 64 126.578 64 123.733v-23.466C64 97.422 65.219 96 67.657 96zM50.286 68.267c2.02 0 3.657-1.91 3.657-4.267 0-2.356-1.638-4.267-3.657-4.267H17.37V32h6.4c2.02 0 3.658-1.91 3.658-4.267V4.267C27.429 1.91 25.79 0 23.77 0H3.657C1.637 0 0 1.91 0 4.267v23.466C0 30.09 1.637 32 3.657 32h6.4v80c0 2.356 1.638 4.267 3.657 4.267h36.572c2.02 0 3.657-1.91 3.657-4.267 0-2.356-1.638-4.267-3.657-4.267H17.37V68.267h32.915z"/></svg>
|
After Width: | Height: | Size: 906 B |
@@ -1 +1 @@
|
||||
<svg width="128" height="110" xmlns="http://www.w3.org/2000/svg"><g><path d="M86.635 33.334c1.467 0 2.917.113 4.358.283C87.078 14.392 67.58.111 45.321.111 20.44.111.055 17.987.055 40.687c0 13.104 6.781 23.863 18.115 32.209l-4.527 14.352 15.82-8.364c5.666 1.182 10.207 2.395 15.858 2.395 1.42 0 2.829-.073 4.227-.189-.886-3.19-1.398-6.53-1.398-9.996 0-20.845 16.98-37.76 38.485-37.76zm-24.34-12.936c3.407 0 5.665 2.363 5.665 5.954 0 3.576-2.258 5.97-5.666 5.97-3.392 0-6.795-2.395-6.795-5.97 0-3.591 3.403-5.954 6.795-5.954zM30.616 32.323c-3.393 0-6.818-2.395-6.818-5.971 0-3.591 3.425-5.954 6.818-5.954 3.392 0 5.65 2.363 5.65 5.954 0 3.576-2.258 5.97-5.65 5.97z"/><path d="M127.945 70.52c0-19.075-18.108-34.623-38.448-34.623-21.537 0-38.5 15.548-38.5 34.623 0 19.108 16.963 34.622 38.5 34.622 4.508 0 9.058-1.2 13.584-2.395l12.414 7.167-3.404-11.923c9.087-7.184 15.854-16.712 15.854-27.471zm-50.928-5.97c-2.254 0-4.53-2.362-4.53-4.773 0-2.378 2.276-4.771 4.53-4.771 3.422 0 5.665 2.393 5.665 4.771 0 2.41-2.243 4.773-5.665 4.773zm24.897 0c-2.24 0-4.498-2.362-4.498-4.773 0-2.378 2.258-4.771 4.498-4.771 3.392 0 5.665 2.393 5.665 4.771 0 2.41-2.273 4.773-5.665 4.773z"/></g></svg>
|
||||
<svg width="128" height="110" xmlns="http://www.w3.org/2000/svg"><path d="M86.635 33.334c1.467 0 2.917.113 4.358.283C87.078 14.392 67.58.111 45.321.111 20.44.111.055 17.987.055 40.687c0 13.104 6.781 23.863 18.115 32.209l-4.527 14.352 15.82-8.364c5.666 1.182 10.207 2.395 15.858 2.395 1.42 0 2.829-.073 4.227-.189-.886-3.19-1.398-6.53-1.398-9.996 0-20.845 16.98-37.76 38.485-37.76zm-24.34-12.936c3.407 0 5.665 2.363 5.665 5.954 0 3.576-2.258 5.97-5.666 5.97-3.392 0-6.795-2.395-6.795-5.97 0-3.591 3.403-5.954 6.795-5.954zM30.616 32.323c-3.393 0-6.818-2.395-6.818-5.971 0-3.591 3.425-5.954 6.818-5.954 3.392 0 5.65 2.363 5.65 5.954 0 3.576-2.258 5.97-5.65 5.97z"/><path d="M127.945 70.52c0-19.075-18.108-34.623-38.448-34.623-21.537 0-38.5 15.548-38.5 34.623 0 19.108 16.963 34.622 38.5 34.622 4.508 0 9.058-1.2 13.584-2.395l12.414 7.167-3.404-11.923c9.087-7.184 15.854-16.712 15.854-27.471zm-50.928-5.97c-2.254 0-4.53-2.362-4.53-4.773 0-2.378 2.276-4.771 4.53-4.771 3.422 0 5.665 2.393 5.665 4.771 0 2.41-2.243 4.773-5.665 4.773zm24.897 0c-2.24 0-4.498-2.362-4.498-4.773 0-2.378 2.258-4.771 4.498-4.771 3.392 0 5.665 2.393 5.665 4.771 0 2.41-2.273 4.773-5.665 4.773z"/></svg>
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -6,6 +6,7 @@ export default {
|
||||
guide: 'Guide',
|
||||
permission: 'Permission',
|
||||
pagePermission: 'Page Permission',
|
||||
rolePermission: 'Role Permission',
|
||||
directivePermission: 'Directive Permission',
|
||||
icons: 'Icons',
|
||||
components: 'Components',
|
||||
@@ -22,6 +23,7 @@ export default {
|
||||
componentMixin: 'Mixin',
|
||||
backToTop: 'BackToTop',
|
||||
dragDialog: 'Drag Dialog',
|
||||
dragSelect: 'Drag Select',
|
||||
dragKanban: 'Drag Kanban',
|
||||
charts: 'Charts',
|
||||
keyboardChart: 'Keyboard Chart',
|
||||
@@ -55,8 +57,10 @@ export default {
|
||||
excel: 'Excel',
|
||||
exportExcel: 'Export Excel',
|
||||
selectExcel: 'Export Selected',
|
||||
mergeHeader: 'Merge Header',
|
||||
uploadExcel: 'Upload Excel',
|
||||
zip: 'Zip',
|
||||
pdf: 'PDF',
|
||||
exportZip: 'Export Zip',
|
||||
theme: 'Theme',
|
||||
clipboardDemo: 'Clipboard',
|
||||
@@ -67,7 +71,6 @@ export default {
|
||||
logOut: 'Log Out',
|
||||
dashboard: 'Dashboard',
|
||||
github: 'Github',
|
||||
screenfull: 'Screenfull',
|
||||
theme: 'Theme',
|
||||
size: 'Global Size'
|
||||
},
|
||||
@@ -85,8 +88,14 @@ export default {
|
||||
github: 'Github Repository'
|
||||
},
|
||||
permission: {
|
||||
addRole: 'New Role',
|
||||
editPermission: 'Edit Permission',
|
||||
roles: 'Your roles',
|
||||
switchRoles: 'Switch roles'
|
||||
switchRoles: 'Switch roles',
|
||||
tips: 'In some cases it is not suitable to use v-permission, such as element Tab component or el-table-column and other asynchronous rendering dom cases which can only be achieved by manually setting the v-if.',
|
||||
delete: 'Delete',
|
||||
confirm: 'Confirm',
|
||||
cancel: 'Cancel'
|
||||
},
|
||||
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 ',
|
||||
@@ -141,6 +150,9 @@ export default {
|
||||
export: 'Export',
|
||||
placeholder: 'Please enter the file name(default file)'
|
||||
},
|
||||
pdf: {
|
||||
tips: 'Here we use window.print() to implement the feature of downloading pdf.'
|
||||
},
|
||||
theme: {
|
||||
change: 'Change Theme',
|
||||
documentation: 'Theme documentation',
|
||||
|
167
src/lang/es.js
Executable file
@@ -0,0 +1,167 @@
|
||||
export default {
|
||||
route: {
|
||||
dashboard: 'Panel de control',
|
||||
introduction: 'Introducción',
|
||||
documentation: 'Documentación',
|
||||
guide: 'Guía',
|
||||
permission: 'Permisos',
|
||||
rolePermission: 'Permisos de rol',
|
||||
pagePermission: 'Permisos de la página',
|
||||
directivePermission: 'Permisos de la directiva',
|
||||
icons: 'Iconos',
|
||||
components: 'Componentes',
|
||||
componentIndex: 'Introducción',
|
||||
tinymce: 'Tinymce',
|
||||
markdown: 'Markdown',
|
||||
jsonEditor: 'Editor JSON',
|
||||
dndList: 'Lista Dnd',
|
||||
splitPane: 'Panel dividido',
|
||||
avatarUpload: 'Subir avatar',
|
||||
dropzone: 'Subir ficheros',
|
||||
sticky: 'Sticky',
|
||||
countTo: 'CountTo',
|
||||
componentMixin: 'Mixin',
|
||||
backToTop: 'Ir arriba',
|
||||
dragDialog: 'Drag Dialog',
|
||||
dragSelect: 'Drag Select',
|
||||
dragKanban: 'Drag Kanban',
|
||||
charts: 'Gráficos',
|
||||
keyboardChart: 'Keyboard Chart',
|
||||
lineChart: 'Gráfico de líneas',
|
||||
mixChart: 'Mix Chart',
|
||||
example: 'Ejemplo',
|
||||
nested: 'Rutas anidadass',
|
||||
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: 'Tabla',
|
||||
dynamicTable: 'Tabla dinámica',
|
||||
dragTable: 'Arrastrar tabla',
|
||||
inlineEditTable: 'Editor',
|
||||
complexTable: 'Complex Table',
|
||||
treeTable: 'Tree Table',
|
||||
customTreeTable: 'Custom TreeTable',
|
||||
tab: 'Pestaña',
|
||||
form: 'Formulario',
|
||||
createArticle: 'Crear artículo',
|
||||
editArticle: 'Editar artículo',
|
||||
articleList: 'Listado de artículos',
|
||||
errorPages: 'Páginas de error',
|
||||
page401: '401',
|
||||
page404: '404',
|
||||
errorLog: 'Registro de errores',
|
||||
excel: 'Excel',
|
||||
exportExcel: 'Exportar a Excel',
|
||||
selectExcel: 'Export seleccionado',
|
||||
mergeHeader: 'Merge Header',
|
||||
uploadExcel: 'Subir Excel',
|
||||
zip: 'Zip',
|
||||
pdf: 'PDF',
|
||||
exportZip: 'Exportar a Zip',
|
||||
theme: 'Tema',
|
||||
clipboardDemo: 'Clipboard',
|
||||
i18n: 'I18n',
|
||||
externalLink: 'Enlace externo'
|
||||
},
|
||||
navbar: {
|
||||
logOut: 'Salir',
|
||||
dashboard: 'Panel de control',
|
||||
github: 'Github',
|
||||
theme: 'Tema',
|
||||
size: 'Tamaño global'
|
||||
},
|
||||
login: {
|
||||
title: 'Formulario de acceso',
|
||||
logIn: 'Acceso',
|
||||
username: 'Usuario',
|
||||
password: 'Contraseña',
|
||||
any: 'nada',
|
||||
thirdparty: 'Conectar con',
|
||||
thirdpartyTips: 'No se puede simular en local, así que combine su propia simulación de negocios. ! !'
|
||||
},
|
||||
documentation: {
|
||||
documentation: 'Documentación',
|
||||
github: 'Repositorio Github'
|
||||
},
|
||||
permission: {
|
||||
addRole: 'Nuevo rol',
|
||||
editPermission: 'Permiso de edición',
|
||||
roles: 'Tus permisos',
|
||||
switchRoles: 'Cambiar permisos',
|
||||
tips: 'In some cases it is not suitable to use v-permission, such as element Tab component or el-table-column and other asynchronous rendering dom cases which can only be achieved by manually setting the v-if.',
|
||||
delete: 'Borrar',
|
||||
confirm: 'Confirmar',
|
||||
cancel: 'Cancelar'
|
||||
},
|
||||
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: 'Ver guía'
|
||||
},
|
||||
components: {
|
||||
documentation: 'Documentación',
|
||||
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.',
|
||||
dropzoneTips: 'Because my business has special needs, and has to upload images to qiniu, so instead of a third party, I chose encapsulate it by myself. It is very simple, you can see the detail code in @/components/Dropzone.',
|
||||
stickyTips: 'when the page is scrolled to the preset position will be sticky on the top.',
|
||||
backToTopTips1: 'When the page is scrolled to the specified position, the Back to Top button appears in the lower right corner',
|
||||
backToTopTips2: 'You can customize the style of the button, show / hide, height of appearance, height of the return. If you need a text prompt, you can use element-ui el-tooltip elements externally',
|
||||
imageUploadTips: 'Since I was using only the vue@1 version, and it is not compatible with mockjs at the moment, I modified it myself, and if you are going to use it, it is better to use official version.'
|
||||
},
|
||||
table: {
|
||||
dynamicTips1: 'Fixed header, sorted by header order',
|
||||
dynamicTips2: 'Not fixed header, sorted by click order',
|
||||
dragTips1: 'Orden por defecto',
|
||||
dragTips2: 'The after dragging order',
|
||||
title: 'Título',
|
||||
importance: 'Importancia',
|
||||
type: 'Tipo',
|
||||
remark: 'Remark',
|
||||
search: 'Buscar',
|
||||
add: 'Añadir',
|
||||
export: 'Exportar',
|
||||
reviewer: 'reviewer',
|
||||
id: 'ID',
|
||||
date: 'Fecha',
|
||||
author: 'Autor',
|
||||
readings: 'Lector',
|
||||
status: 'Estado',
|
||||
actions: 'Acciones',
|
||||
edit: 'Editar',
|
||||
publish: 'Publicar',
|
||||
draft: 'Draft',
|
||||
delete: 'Eliminar',
|
||||
cancel: 'Cancelar',
|
||||
confirm: 'Confirmar'
|
||||
},
|
||||
errorLog: {
|
||||
tips: 'Please click the bug icon in the upper right corner',
|
||||
description: 'Now the management system are basically the form of the spa, it enhances the user experience, but it also increases the possibility of page problems, a small negligence may lead to the entire page deadlock. Fortunately Vue provides a way to catch handling exceptions, where you can handle errors or report exceptions.',
|
||||
documentation: 'Documento de introducción'
|
||||
},
|
||||
excel: {
|
||||
export: 'Exportar',
|
||||
selectedExport: 'Exportar seleccionados',
|
||||
placeholder: 'Por favor escribe un nombre de fichero'
|
||||
},
|
||||
zip: {
|
||||
export: 'Exportar',
|
||||
placeholder: 'Por favor escribe un nombre de fichero'
|
||||
},
|
||||
pdf: {
|
||||
tips: 'Here we use window.print() to implement the feature of downloading pdf.'
|
||||
},
|
||||
theme: {
|
||||
change: 'Cambiar tema',
|
||||
documentation: 'Documentación del tema',
|
||||
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: {
|
||||
refresh: 'Actualizar',
|
||||
close: 'Cerrar',
|
||||
closeOthers: 'Cerrar otros',
|
||||
closeAll: 'Cerrar todos'
|
||||
}
|
||||
}
|
@@ -3,8 +3,10 @@ import VueI18n from 'vue-i18n'
|
||||
import Cookies from 'js-cookie'
|
||||
import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang
|
||||
import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'// element-ui lang
|
||||
import elementEsLocale from 'element-ui/lib/locale/lang/es'// element-ui lang
|
||||
import enLocale from './en'
|
||||
import zhLocale from './zh'
|
||||
import esLocale from './es'
|
||||
|
||||
Vue.use(VueI18n)
|
||||
|
||||
@@ -16,12 +18,16 @@ const messages = {
|
||||
zh: {
|
||||
...zhLocale,
|
||||
...elementZhLocale
|
||||
},
|
||||
es: {
|
||||
...esLocale,
|
||||
...elementEsLocale
|
||||
}
|
||||
}
|
||||
|
||||
const i18n = new VueI18n({
|
||||
// set locale
|
||||
// options: en or zh
|
||||
// options: en | zh | es
|
||||
locale: Cookies.get('language') || 'en',
|
||||
// set locale messages
|
||||
messages
|
||||
|
@@ -5,6 +5,7 @@ export default {
|
||||
documentation: '文档',
|
||||
guide: '引导页',
|
||||
permission: '权限测试页',
|
||||
rolePermission: '角色权限',
|
||||
pagePermission: '页面权限',
|
||||
directivePermission: '指令权限',
|
||||
icons: '图标',
|
||||
@@ -22,6 +23,7 @@ export default {
|
||||
componentMixin: '小组件',
|
||||
backToTop: '返回顶部',
|
||||
dragDialog: '拖拽 Dialog',
|
||||
dragSelect: '拖拽 Select',
|
||||
dragKanban: '可拖拽看板',
|
||||
charts: '图表',
|
||||
keyboardChart: '键盘图表',
|
||||
@@ -53,10 +55,12 @@ export default {
|
||||
page404: '404',
|
||||
errorLog: '错误日志',
|
||||
excel: 'Excel',
|
||||
exportExcel: 'Export Excel',
|
||||
selectExcel: 'Export Selected',
|
||||
uploadExcel: 'Upload Excel',
|
||||
exportExcel: '导出 Excel',
|
||||
selectExcel: '导出 已选择项',
|
||||
mergeHeader: '导出 多级表头',
|
||||
uploadExcel: '上传 Excel',
|
||||
zip: 'Zip',
|
||||
pdf: 'PDF',
|
||||
exportZip: 'Export Zip',
|
||||
theme: '换肤',
|
||||
clipboardDemo: 'Clipboard',
|
||||
@@ -67,7 +71,6 @@ export default {
|
||||
logOut: '退出登录',
|
||||
dashboard: '首页',
|
||||
github: '项目地址',
|
||||
screenfull: '全屏',
|
||||
theme: '换肤',
|
||||
size: '布局大小'
|
||||
},
|
||||
@@ -85,8 +88,14 @@ export default {
|
||||
github: 'Github 地址'
|
||||
},
|
||||
permission: {
|
||||
addRole: '新增角色',
|
||||
editPermission: '编辑权限',
|
||||
roles: '你的权限',
|
||||
switchRoles: '切换权限'
|
||||
switchRoles: '切换权限',
|
||||
tips: '在某些情况下,不适合使用 v-permission。例如:Element-UI 的 Tab 组件或 el-table-column 以及其它动态渲染 dom 的场景。你只能通过手动设置 v-if 来实现。',
|
||||
delete: '删除',
|
||||
confirm: '确定',
|
||||
cancel: '取消'
|
||||
},
|
||||
guide: {
|
||||
description: '引导页对于一些第一次进入项目的人很有用,你可以简单介绍下项目的功能。本 Demo 是基于',
|
||||
@@ -141,6 +150,9 @@ export default {
|
||||
export: '导出',
|
||||
placeholder: '请输入文件名(默认file)'
|
||||
},
|
||||
pdf: {
|
||||
tips: '这里使用 window.print() 来实现下载pdf的功能'
|
||||
},
|
||||
theme: {
|
||||
change: '换肤',
|
||||
documentation: '换肤文档',
|
||||
|
@@ -10,8 +10,8 @@ import 'element-ui/lib/theme-chalk/index.css'
|
||||
import '@/styles/index.scss' // global css
|
||||
|
||||
import App from './App'
|
||||
import router from './router'
|
||||
import store from './store'
|
||||
import router from './router'
|
||||
|
||||
import i18n from './lang' // Internationalization
|
||||
import './icons' // icon
|
||||
|
@@ -3,6 +3,20 @@ import loginAPI from './login'
|
||||
import articleAPI from './article'
|
||||
import remoteSearchAPI from './remoteSearch'
|
||||
import transactionAPI from './transaction'
|
||||
import roleAPI from './role'
|
||||
|
||||
// 修复在使用 MockJS 情况下,设置 withCredentials = true,且未被拦截的跨域请求丢失 Cookies 的问题
|
||||
// https://github.com/nuysoft/Mock/issues/300
|
||||
Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send
|
||||
Mock.XHR.prototype.send = function() {
|
||||
if (this.custom.xhr) {
|
||||
this.custom.xhr.withCredentials = this.withCredentials || false
|
||||
if (this.responseType) {
|
||||
this.custom.xhr.responseType = this.responseType
|
||||
}
|
||||
}
|
||||
this.proxy_send(...arguments)
|
||||
}
|
||||
|
||||
// Mock.setup({
|
||||
// timeout: '350-600'
|
||||
@@ -13,6 +27,13 @@ Mock.mock(/\/login\/login/, 'post', loginAPI.loginByUsername)
|
||||
Mock.mock(/\/login\/logout/, 'post', loginAPI.logout)
|
||||
Mock.mock(/\/user\/info\.*/, 'get', loginAPI.getUserInfo)
|
||||
|
||||
// 角色相关
|
||||
Mock.mock(/\/routes/, 'get', roleAPI.getRoutes)
|
||||
Mock.mock(/\/roles/, 'get', roleAPI.getRoles)
|
||||
Mock.mock(/\/roles$/, 'post', roleAPI.addRole)
|
||||
Mock.mock(/\/roles\/[A-Za-z0-9]+/, 'put', roleAPI.updateRole)
|
||||
Mock.mock(/\/roles\/[A-Za-z0-9]+/, 'delete', roleAPI.deleteRole)
|
||||
|
||||
// 文章相关
|
||||
Mock.mock(/\/article\/list/, 'get', articleAPI.getList)
|
||||
Mock.mock(/\/article\/detail/, 'get', articleAPI.getArticle)
|
||||
|
61
src/mock/role.js
Normal file
@@ -0,0 +1,61 @@
|
||||
import Mock from 'mockjs'
|
||||
import { deepClone } from '@/utils'
|
||||
import { filterAsyncRoutes } from '@/store/modules/permission'
|
||||
import { asyncRoutes, constantRoutes } from '@/router'
|
||||
|
||||
const routes = deepClone([...constantRoutes, ...asyncRoutes])
|
||||
|
||||
const roles = [
|
||||
{
|
||||
key: 'admin',
|
||||
name: 'admin',
|
||||
description: 'Super Administrator. Have access to view all pages.',
|
||||
routes: routes
|
||||
},
|
||||
{
|
||||
key: 'editor',
|
||||
name: 'editor',
|
||||
description: 'Normal Editor. Can see all pages except permission page',
|
||||
routes: filterAsyncRoutes(routes, ['editor'])
|
||||
},
|
||||
{
|
||||
key: 'visitor',
|
||||
name: 'visitor',
|
||||
description: 'Just a visitor. Can only see the home page and the document page',
|
||||
routes: [{
|
||||
path: '',
|
||||
redirect: 'dashboard',
|
||||
children: [
|
||||
{
|
||||
path: 'dashboard',
|
||||
name: 'Dashboard',
|
||||
meta: { title: 'dashboard', icon: 'dashboard' }
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
]
|
||||
|
||||
export default {
|
||||
getRoutes() {
|
||||
return routes
|
||||
},
|
||||
getRoles() {
|
||||
return roles
|
||||
},
|
||||
addRole() {
|
||||
return Mock.mock('@integer(300, 5000)')
|
||||
},
|
||||
updateRole() {
|
||||
const res = {
|
||||
data: 'success'
|
||||
}
|
||||
return res
|
||||
},
|
||||
deleteRole() {
|
||||
const res = {
|
||||
data: 'success'
|
||||
}
|
||||
return res
|
||||
}
|
||||
}
|
@@ -2,41 +2,49 @@ import router from './router'
|
||||
import store from './store'
|
||||
import { Message } from 'element-ui'
|
||||
import NProgress from 'nprogress' // progress bar
|
||||
import 'nprogress/nprogress.css'// progress bar style
|
||||
import { getToken } from '@/utils/auth' // getToken from cookie
|
||||
import 'nprogress/nprogress.css' // progress bar style
|
||||
import { getToken } from '@/utils/auth' // get token from cookie
|
||||
|
||||
NProgress.configure({ showSpinner: false })// NProgress Configuration
|
||||
NProgress.configure({ showSpinner: false }) // NProgress Configuration
|
||||
|
||||
// permission judge function
|
||||
function hasPermission(roles, permissionRoles) {
|
||||
if (roles.indexOf('admin') >= 0) return true // admin permission passed directly
|
||||
if (roles.includes('admin')) return true // admin permission passed directly
|
||||
if (!permissionRoles) return true
|
||||
return roles.some(role => permissionRoles.indexOf(role) >= 0)
|
||||
}
|
||||
|
||||
const whiteList = ['/login', '/auth-redirect']// no redirect whitelist
|
||||
const whiteList = ['/login', '/auth-redirect'] // no redirect whitelist
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
NProgress.start() // start progress bar
|
||||
if (getToken()) { // determine if there has token
|
||||
if (getToken()) {
|
||||
// determine if there has token
|
||||
|
||||
/* has token*/
|
||||
if (to.path === '/login') {
|
||||
next({ path: '/' })
|
||||
NProgress.done() // if current page is dashboard will not trigger afterEach hook, so manually handle it
|
||||
} else {
|
||||
if (store.getters.roles.length === 0) { // 判断当前用户是否已拉取完user_info信息
|
||||
store.dispatch('GetUserInfo').then(res => { // 拉取user_info
|
||||
const roles = res.data.roles // note: roles must be a array! such as: ['editor','develop']
|
||||
store.dispatch('GenerateRoutes', { roles }).then(() => { // 根据roles权限生成可访问的路由表
|
||||
router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
|
||||
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
|
||||
if (store.getters.roles.length === 0) {
|
||||
// 判断当前用户是否已拉取完user_info信息
|
||||
store
|
||||
.dispatch('GetUserInfo')
|
||||
.then(res => {
|
||||
// 拉取user_info
|
||||
const roles = res.data.roles // note: roles must be a object array! such as: [{id: '1', name: 'editor'}, {id: '2', name: 'developer'}]
|
||||
store.dispatch('GenerateRoutes', { roles }).then(accessRoutes => {
|
||||
// 根据roles权限生成可访问的路由表
|
||||
router.addRoutes(accessRoutes) // 动态添加可访问路由表
|
||||
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
|
||||
})
|
||||
})
|
||||
}).catch((err) => {
|
||||
store.dispatch('FedLogOut').then(() => {
|
||||
Message.error(err || 'Verification failed, please login again')
|
||||
next({ path: '/' })
|
||||
.catch(err => {
|
||||
store.dispatch('FedLogOut').then(() => {
|
||||
Message.error(err)
|
||||
next({ path: '/' })
|
||||
})
|
||||
})
|
||||
})
|
||||
} else {
|
||||
// 没有动态改变权限的需求可直接next() 删除下方权限判断 ↓
|
||||
if (hasPermission(store.getters.roles, to.meta.roles)) {
|
||||
@@ -49,7 +57,8 @@ router.beforeEach((to, from, next) => {
|
||||
}
|
||||
} else {
|
||||
/* has no token*/
|
||||
if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入
|
||||
if (whiteList.indexOf(to.path) !== -1) {
|
||||
// 在免登录白名单,直接进入
|
||||
next()
|
||||
} else {
|
||||
next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页
|
||||
|
@@ -10,9 +10,10 @@ import Layout from '@/views/layout/Layout'
|
||||
import componentsRouter from './modules/components'
|
||||
import chartsRouter from './modules/charts'
|
||||
import tableRouter from './modules/table'
|
||||
import treeTableRouter from './modules/tree-table'
|
||||
import nestedRouter from './modules/nested'
|
||||
|
||||
/** note: Submenu only appear when children.length>=1
|
||||
/** note: sub-menu only appear when children.length>=1
|
||||
* detail see https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
|
||||
**/
|
||||
|
||||
@@ -24,13 +25,15 @@ import nestedRouter from './modules/nested'
|
||||
* redirect: noredirect if `redirect:noredirect` will no redirect in the breadcrumb
|
||||
* name:'router-name' the name is used by <keep-alive> (must set!!!)
|
||||
* meta : {
|
||||
roles: ['admin','editor'] will control the page roles (you can set multiple roles)
|
||||
title: 'title' the name show in submenu and breadcrumb (recommend set)
|
||||
icon: 'svg-name' the icon show in the sidebar,
|
||||
noCache: true if true ,the page will no be cached(default is false)
|
||||
roles: ['admin','editor'] will control the page roles (you can set multiple roles)
|
||||
title: 'title' the name show in sub-menu and breadcrumb (recommend set)
|
||||
icon: 'svg-name' the icon show in the sidebar
|
||||
noCache: true if true, the page will no be cached(default is false)
|
||||
breadcrumb: false if false, the item will hidden in breadcrumb(default is true)
|
||||
affix: true if true, the tag will affix in the tags-view
|
||||
}
|
||||
**/
|
||||
export const constantRouterMap = [
|
||||
export const constantRoutes = [
|
||||
{
|
||||
path: '/redirect',
|
||||
component: Layout,
|
||||
@@ -71,20 +74,19 @@ export const constantRouterMap = [
|
||||
path: 'dashboard',
|
||||
component: () => import('@/views/dashboard/index'),
|
||||
name: 'Dashboard',
|
||||
meta: { title: 'dashboard', icon: 'dashboard', noCache: true }
|
||||
meta: { title: 'dashboard', icon: 'dashboard', noCache: true, affix: true }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/documentation',
|
||||
component: Layout,
|
||||
redirect: '/documentation/index',
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
component: () => import('@/views/documentation/index'),
|
||||
name: 'Documentation',
|
||||
meta: { title: 'documentation', icon: 'documentation', noCache: true }
|
||||
meta: { title: 'documentation', icon: 'documentation', affix: true }
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -106,10 +108,10 @@ export const constantRouterMap = [
|
||||
export default new Router({
|
||||
// mode: 'history', // require service support
|
||||
scrollBehavior: () => ({ y: 0 }),
|
||||
routes: constantRouterMap
|
||||
routes: constantRoutes
|
||||
})
|
||||
|
||||
export const asyncRouterMap = [
|
||||
export const asyncRoutes = [
|
||||
{
|
||||
path: '/permission',
|
||||
component: Layout,
|
||||
@@ -138,6 +140,15 @@ export const asyncRouterMap = [
|
||||
title: 'directivePermission'
|
||||
// if do not set roles, means: this page does not require permission
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'role',
|
||||
component: () => import('@/views/permission/role'),
|
||||
name: 'RolePermission',
|
||||
meta: {
|
||||
title: 'rolePermission',
|
||||
roles: ['admin']
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -160,6 +171,7 @@ export const asyncRouterMap = [
|
||||
chartsRouter,
|
||||
nestedRouter,
|
||||
tableRouter,
|
||||
treeTableRouter,
|
||||
|
||||
{
|
||||
path: '/example',
|
||||
@@ -267,6 +279,12 @@ export const asyncRouterMap = [
|
||||
name: 'SelectExcel',
|
||||
meta: { title: 'selectExcel' }
|
||||
},
|
||||
{
|
||||
path: 'export-merge-header',
|
||||
component: () => import('@/views/excel/mergeHeader'),
|
||||
name: 'MergeHeader',
|
||||
meta: { title: 'mergeHeader' }
|
||||
},
|
||||
{
|
||||
path: 'upload-excel',
|
||||
component: () => import('@/views/excel/uploadExcel'),
|
||||
@@ -292,6 +310,25 @@ export const asyncRouterMap = [
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
path: '/pdf',
|
||||
component: Layout,
|
||||
redirect: '/pdf/index',
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
component: () => import('@/views/pdf/index'),
|
||||
name: 'PDF',
|
||||
meta: { title: 'pdf', icon: 'pdf' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/pdf/download',
|
||||
component: () => import('@/views/pdf/download'),
|
||||
hidden: true
|
||||
},
|
||||
|
||||
{
|
||||
path: '/theme',
|
||||
component: Layout,
|
||||
|
@@ -78,6 +78,12 @@ const componentsRouter = {
|
||||
name: 'DragDialogDemo',
|
||||
meta: { title: 'dragDialog' }
|
||||
},
|
||||
{
|
||||
path: 'drag-select',
|
||||
component: () => import('@/views/components-demo/dragSelect'),
|
||||
name: 'DragSelectDemo',
|
||||
meta: { title: 'dragSelect' }
|
||||
},
|
||||
{
|
||||
path: 'dnd-list',
|
||||
component: () => import('@/views/components-demo/dndList'),
|
||||
|
@@ -30,18 +30,6 @@ const tableRouter = {
|
||||
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'),
|
||||
|
29
src/router/modules/tree-table.js
Normal file
@@ -0,0 +1,29 @@
|
||||
/** When your routing table is too long, you can split it into small modules**/
|
||||
|
||||
import Layout from '@/views/layout/Layout'
|
||||
|
||||
const treeTableRouter = {
|
||||
path: '/tree-table',
|
||||
component: Layout,
|
||||
redirect: '/table/complex-table',
|
||||
name: 'TreeTable',
|
||||
meta: {
|
||||
title: 'treeTable',
|
||||
icon: 'tree-table'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
component: () => import('@/views/tree-table/index'),
|
||||
name: 'TreeTableDemo',
|
||||
meta: { title: 'treeTable' }
|
||||
},
|
||||
{
|
||||
path: 'custom',
|
||||
component: () => import('@/views/tree-table/custom'),
|
||||
name: 'CustomTreeTableDemo',
|
||||
meta: { title: 'customTreeTable' }
|
||||
}
|
||||
]
|
||||
}
|
||||
export default treeTableRouter
|
@@ -12,8 +12,8 @@ const getters = {
|
||||
status: state => state.user.status,
|
||||
roles: state => state.user.roles,
|
||||
setting: state => state.user.setting,
|
||||
permission_routers: state => state.permission.routers,
|
||||
addRouters: state => state.permission.addRouters,
|
||||
permission_routes: state => state.permission.routes,
|
||||
addRoutes: state => state.permission.addRoutes,
|
||||
errorLogs: state => state.errorLog.logs
|
||||
}
|
||||
export default getters
|
||||
|
@@ -3,7 +3,7 @@ import Cookies from 'js-cookie'
|
||||
const app = {
|
||||
state: {
|
||||
sidebar: {
|
||||
opened: !+Cookies.get('sidebarStatus'),
|
||||
opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true,
|
||||
withoutAnimation: false
|
||||
},
|
||||
device: 'desktop',
|
||||
@@ -12,16 +12,16 @@ const app = {
|
||||
},
|
||||
mutations: {
|
||||
TOGGLE_SIDEBAR: state => {
|
||||
state.sidebar.opened = !state.sidebar.opened
|
||||
state.sidebar.withoutAnimation = false
|
||||
if (state.sidebar.opened) {
|
||||
Cookies.set('sidebarStatus', 1)
|
||||
} else {
|
||||
Cookies.set('sidebarStatus', 0)
|
||||
}
|
||||
state.sidebar.opened = !state.sidebar.opened
|
||||
state.sidebar.withoutAnimation = false
|
||||
},
|
||||
CLOSE_SIDEBAR: (state, withoutAnimation) => {
|
||||
Cookies.set('sidebarStatus', 1)
|
||||
Cookies.set('sidebarStatus', 0)
|
||||
state.sidebar.opened = false
|
||||
state.sidebar.withoutAnimation = withoutAnimation
|
||||
},
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { asyncRouterMap, constantRouterMap } from '@/router'
|
||||
import { asyncRoutes, constantRoutes } from '@/router'
|
||||
|
||||
/**
|
||||
* 通过meta.role判断是否与当前用户权限匹配
|
||||
@@ -15,17 +15,17 @@ function hasPermission(roles, route) {
|
||||
|
||||
/**
|
||||
* 递归过滤异步路由表,返回符合用户角色权限的路由表
|
||||
* @param routes asyncRouterMap
|
||||
* @param routes asyncRoutes
|
||||
* @param roles
|
||||
*/
|
||||
function filterAsyncRouter(routes, roles) {
|
||||
export function filterAsyncRoutes(routes, roles) {
|
||||
const res = []
|
||||
|
||||
routes.forEach(route => {
|
||||
const tmp = { ...route }
|
||||
if (hasPermission(roles, tmp)) {
|
||||
if (tmp.children) {
|
||||
tmp.children = filterAsyncRouter(tmp.children, roles)
|
||||
tmp.children = filterAsyncRoutes(tmp.children, roles)
|
||||
}
|
||||
res.push(tmp)
|
||||
}
|
||||
@@ -36,27 +36,27 @@ function filterAsyncRouter(routes, roles) {
|
||||
|
||||
const permission = {
|
||||
state: {
|
||||
routers: constantRouterMap,
|
||||
addRouters: []
|
||||
routes: [],
|
||||
addRoutes: []
|
||||
},
|
||||
mutations: {
|
||||
SET_ROUTERS: (state, routers) => {
|
||||
state.addRouters = routers
|
||||
state.routers = constantRouterMap.concat(routers)
|
||||
SET_ROUTES: (state, routes) => {
|
||||
state.addRoutes = routes
|
||||
state.routes = constantRoutes.concat(routes)
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
GenerateRoutes({ commit }, data) {
|
||||
return new Promise(resolve => {
|
||||
const { roles } = data
|
||||
let accessedRouters
|
||||
let accessedRoutes
|
||||
if (roles.includes('admin')) {
|
||||
accessedRouters = asyncRouterMap
|
||||
accessedRoutes = asyncRoutes
|
||||
} else {
|
||||
accessedRouters = filterAsyncRouter(asyncRouterMap, roles)
|
||||
accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
|
||||
}
|
||||
commit('SET_ROUTERS', accessedRouters)
|
||||
resolve()
|
||||
commit('SET_ROUTES', accessedRoutes)
|
||||
resolve(accessedRoutes)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -38,12 +38,9 @@ const tagsView = {
|
||||
},
|
||||
|
||||
DEL_OTHERS_VISITED_VIEWS: (state, view) => {
|
||||
for (const [i, v] of state.visitedViews.entries()) {
|
||||
if (v.path === view.path) {
|
||||
state.visitedViews = state.visitedViews.slice(i, i + 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
state.visitedViews = state.visitedViews.filter(v => {
|
||||
return v.meta.affix || v.path === view.path
|
||||
})
|
||||
},
|
||||
DEL_OTHERS_CACHED_VIEWS: (state, view) => {
|
||||
for (const i of state.cachedViews) {
|
||||
@@ -56,7 +53,9 @@ const tagsView = {
|
||||
},
|
||||
|
||||
DEL_ALL_VISITED_VIEWS: state => {
|
||||
state.visitedViews = []
|
||||
// keep affix tags
|
||||
const affixTags = state.visitedViews.filter(tag => tag.meta.affix)
|
||||
state.visitedViews = affixTags
|
||||
},
|
||||
DEL_ALL_CACHED_VIEWS: state => {
|
||||
state.cachedViews = []
|
||||
|
@@ -63,15 +63,16 @@ const user = {
|
||||
GetUserInfo({ commit, state }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
getUserInfo(state.token).then(response => {
|
||||
if (!response.data) { // 由于mockjs 不支持自定义状态码只能这样hack
|
||||
reject('error')
|
||||
// 由于mockjs 不支持自定义状态码只能这样hack
|
||||
if (!response.data) {
|
||||
reject('Verification failed, please login again.')
|
||||
}
|
||||
const data = response.data
|
||||
|
||||
if (data.roles && data.roles.length > 0) { // 验证返回的roles是否是一个非空数组
|
||||
commit('SET_ROLES', data.roles)
|
||||
} else {
|
||||
reject('getInfo: roles must be a non-null array !')
|
||||
reject('getInfo: roles must be a non-null array!')
|
||||
}
|
||||
|
||||
commit('SET_NAME', data.name)
|
||||
|
@@ -2,8 +2,10 @@
|
||||
|
||||
@mixin colorBtn($color) {
|
||||
background: $color;
|
||||
|
||||
&:hover {
|
||||
color: $color;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
background: $color;
|
||||
@@ -49,14 +51,17 @@
|
||||
transition: 600ms ease all;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
||||
&:hover {
|
||||
background: #fff;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
width: 100%;
|
||||
transition: 600ms ease all;
|
||||
}
|
||||
}
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
content: '';
|
||||
@@ -67,6 +72,7 @@
|
||||
width: 0;
|
||||
transition: 400ms ease all;
|
||||
}
|
||||
|
||||
&::after {
|
||||
right: inherit;
|
||||
top: inherit;
|
||||
@@ -91,4 +97,3 @@
|
||||
font-size: 14px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
|
@@ -1,82 +1,85 @@
|
||||
//覆盖一些element-ui样式
|
||||
//覆盖一些element-ui样式
|
||||
|
||||
.el-breadcrumb__inner, .el-breadcrumb__inner a{
|
||||
font-weight: 400!important;
|
||||
.el-breadcrumb__inner,
|
||||
.el-breadcrumb__inner a {
|
||||
font-weight: 400 !important;
|
||||
}
|
||||
|
||||
.el-upload {
|
||||
input[type="file"] {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
.el-upload {
|
||||
input[type="file"] {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-upload__input {
|
||||
display: none;
|
||||
}
|
||||
.el-upload__input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.cell {
|
||||
.el-tag {
|
||||
margin-right: 0px;
|
||||
}
|
||||
}
|
||||
.cell {
|
||||
.el-tag {
|
||||
margin-right: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.small-padding {
|
||||
.cell {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
}
|
||||
.small-padding {
|
||||
.cell {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.fixed-width{
|
||||
.el-button--mini{
|
||||
.fixed-width {
|
||||
.el-button--mini {
|
||||
padding: 7px 10px;
|
||||
width: 60px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.status-col {
|
||||
.cell {
|
||||
padding: 0 10px;
|
||||
text-align: center;
|
||||
.el-tag {
|
||||
margin-right: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.status-col {
|
||||
.cell {
|
||||
padding: 0 10px;
|
||||
text-align: center;
|
||||
|
||||
//暂时性解决dialog 问题 https://github.com/ElemeFE/element/issues/2461
|
||||
.el-dialog {
|
||||
transform: none;
|
||||
left: 0;
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.el-tag {
|
||||
margin-right: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//文章页textarea修改样式
|
||||
.article-textarea {
|
||||
textarea {
|
||||
padding-right: 40px;
|
||||
resize: none;
|
||||
border: none;
|
||||
border-radius: 0px;
|
||||
border-bottom: 1px solid #bfcbd9;
|
||||
}
|
||||
}
|
||||
//暂时性解决dialog 问题 https://github.com/ElemeFE/element/issues/2461
|
||||
.el-dialog {
|
||||
transform: none;
|
||||
left: 0;
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
//element ui upload
|
||||
.upload-container {
|
||||
.el-upload {
|
||||
width: 100%;
|
||||
.el-upload-dragger {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
//文章页textarea修改样式
|
||||
.article-textarea {
|
||||
textarea {
|
||||
padding-right: 40px;
|
||||
resize: none;
|
||||
border: none;
|
||||
border-radius: 0px;
|
||||
border-bottom: 1px solid #bfcbd9;
|
||||
}
|
||||
}
|
||||
|
||||
//element ui upload
|
||||
.upload-container {
|
||||
.el-upload {
|
||||
width: 100%;
|
||||
|
||||
.el-upload-dragger {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//dropdown
|
||||
.el-dropdown-menu{
|
||||
a{
|
||||
.el-dropdown-menu {
|
||||
a {
|
||||
display: block
|
||||
}
|
||||
}
|
||||
|
@@ -22,7 +22,7 @@ html {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#app{
|
||||
#app {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@@ -53,9 +53,9 @@ a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
div:focus{
|
||||
div:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.fr {
|
||||
float: right;
|
||||
@@ -104,23 +104,26 @@ code {
|
||||
line-height: 36px;
|
||||
font-size: 15px;
|
||||
font-family: "Source Sans Pro", "Helvetica Neue", Arial, sans-serif;
|
||||
|
||||
a {
|
||||
color: #337ab7;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: rgb(32, 160, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.warn-content{
|
||||
background: rgba(66,185,131,.1);
|
||||
.warn-content {
|
||||
background: rgba(66, 185, 131, .1);
|
||||
border-radius: 2px;
|
||||
padding: 16px;
|
||||
padding: 1rem;
|
||||
line-height: 1.6rem;
|
||||
word-spacing: .05rem;
|
||||
a{
|
||||
|
||||
a {
|
||||
color: #42b983;
|
||||
font-weight: 600;
|
||||
}
|
||||
@@ -153,13 +156,16 @@ code {
|
||||
padding-right: 20px;
|
||||
transition: 600ms ease position;
|
||||
background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%);
|
||||
|
||||
.subtitle {
|
||||
font-size: 20px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&.draft {
|
||||
background: #d0d0d0;
|
||||
}
|
||||
|
||||
&.deleted {
|
||||
background: #d0d0d0;
|
||||
}
|
||||
@@ -169,6 +175,7 @@ code {
|
||||
.link-type:focus {
|
||||
color: #337ab7;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: rgb(32, 160, 255);
|
||||
}
|
||||
@@ -176,6 +183,7 @@ code {
|
||||
|
||||
.filter-container {
|
||||
padding-bottom: 10px;
|
||||
|
||||
.filter-item {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
|
@@ -10,9 +10,11 @@
|
||||
&::-webkit-scrollbar-track-piece {
|
||||
background: #d3dce6;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #99a9bf;
|
||||
border-radius: 20px;
|
||||
@@ -37,21 +39,25 @@
|
||||
$transparent-border-style: $width solid transparent;
|
||||
height: 0;
|
||||
width: 0;
|
||||
|
||||
@if $direction==up {
|
||||
border-bottom: $color-border-style;
|
||||
border-left: $transparent-border-style;
|
||||
border-right: $transparent-border-style;
|
||||
}
|
||||
|
||||
@else if $direction==right {
|
||||
border-left: $color-border-style;
|
||||
border-top: $transparent-border-style;
|
||||
border-bottom: $transparent-border-style;
|
||||
}
|
||||
|
||||
@else if $direction==down {
|
||||
border-top: $color-border-style;
|
||||
border-left: $transparent-border-style;
|
||||
border-right: $transparent-border-style;
|
||||
}
|
||||
|
||||
@else if $direction==left {
|
||||
border-right: $color-border-style;
|
||||
border-top: $transparent-border-style;
|
||||
|
@@ -1,15 +1,17 @@
|
||||
#app {
|
||||
// 主体区域
|
||||
|
||||
// 主体区域 Main container
|
||||
.main-container {
|
||||
min-height: 100%;
|
||||
transition: margin-left .28s;
|
||||
margin-left: 180px;
|
||||
margin-left: $sideBarWidth;
|
||||
position: relative;
|
||||
}
|
||||
// 侧边栏
|
||||
|
||||
// 侧边栏 Sidebar container
|
||||
.sidebar-container {
|
||||
transition: width 0.28s;
|
||||
width: 180px !important;
|
||||
width: $sideBarWidth !important;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
font-size: 0px;
|
||||
@@ -18,62 +20,107 @@
|
||||
left: 0;
|
||||
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;
|
||||
overflow-x: hidden !important;
|
||||
|
||||
.el-scrollbar__view {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.el-scrollbar__bar.is-vertical{
|
||||
|
||||
.el-scrollbar__bar.is-vertical {
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
.is-horizontal {
|
||||
display: none;
|
||||
}
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.el-menu {
|
||||
border: none;
|
||||
height: 100%;
|
||||
width: 100% !important;
|
||||
}
|
||||
.is-active > .el-submenu__title{
|
||||
color: #f4f4f5!important;
|
||||
}
|
||||
}
|
||||
.hideSidebar {
|
||||
.sidebar-container {
|
||||
width: 36px !important;
|
||||
}
|
||||
.main-container {
|
||||
margin-left: 36px;
|
||||
}
|
||||
.submenu-title-noDropdown {
|
||||
padding-left: 10px !important;
|
||||
position: relative;
|
||||
.el-tooltip {
|
||||
padding: 0 10px !important;
|
||||
|
||||
// menu hover
|
||||
.submenu-title-noDropdown,
|
||||
.el-submenu__title {
|
||||
&:hover {
|
||||
background-color: $menuHover !important;
|
||||
}
|
||||
}
|
||||
|
||||
.is-active>.el-submenu__title {
|
||||
color: $subMenuActiveText !important;
|
||||
}
|
||||
|
||||
& .nest-menu .el-submenu>.el-submenu__title,
|
||||
& .el-submenu .el-menu-item {
|
||||
min-width: $sideBarWidth !important;
|
||||
background-color: $subMenuBg !important;
|
||||
|
||||
&:hover {
|
||||
background-color: $subMenuHover !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hideSidebar {
|
||||
.sidebar-container {
|
||||
width: 54px !important;
|
||||
}
|
||||
|
||||
.main-container {
|
||||
margin-left: 54px;
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
.submenu-title-noDropdown {
|
||||
padding: 0 !important;
|
||||
position: relative;
|
||||
|
||||
.el-tooltip {
|
||||
padding: 0 !important;
|
||||
.svg-icon {
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-submenu {
|
||||
overflow: hidden;
|
||||
|
||||
&>.el-submenu__title {
|
||||
padding-left: 10px !important;
|
||||
padding: 0 !important;
|
||||
.svg-icon {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.el-submenu__icon-arrow {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-menu--collapse {
|
||||
.el-submenu {
|
||||
&>.el-submenu__title {
|
||||
@@ -88,35 +135,33 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.sidebar-container .nest-menu .el-submenu>.el-submenu__title,
|
||||
.sidebar-container .el-submenu .el-menu-item {
|
||||
min-width: 180px !important;
|
||||
background-color: $subMenuBg !important;
|
||||
&:hover {
|
||||
background-color: $menuHover !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-menu--collapse .el-menu .el-submenu {
|
||||
min-width: 180px !important;
|
||||
min-width: $sideBarWidth !important;
|
||||
}
|
||||
|
||||
//适配移动端
|
||||
// 适配移动端, Mobile responsive
|
||||
.mobile {
|
||||
.main-container {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
.sidebar-container {
|
||||
transition: transform .28s;
|
||||
width: 180px !important;
|
||||
width: $sideBarWidth !important;
|
||||
}
|
||||
|
||||
&.hideSidebar {
|
||||
.sidebar-container {
|
||||
pointer-events: none;
|
||||
transition-duration: 0.3s;
|
||||
transform: translate3d(-180px, 0, 0);
|
||||
transform: translate3d(-$sideBarWidth, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.withoutAnimation {
|
||||
|
||||
.main-container,
|
||||
.sidebar-container {
|
||||
transition: none;
|
||||
@@ -124,10 +169,38 @@
|
||||
}
|
||||
}
|
||||
|
||||
.el-menu--vertical{
|
||||
& >.el-menu{
|
||||
.svg-icon{
|
||||
// when menu collapsed
|
||||
.el-menu--vertical {
|
||||
&>.el-menu {
|
||||
.svg-icon {
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.nest-menu .el-submenu>.el-submenu__title,
|
||||
.el-menu-item {
|
||||
&:hover {
|
||||
// you can use $subMenuHover
|
||||
background-color: $menuHover !important;
|
||||
}
|
||||
}
|
||||
|
||||
// the scroll bar appears when the subMenu is too long
|
||||
>.el-menu--popup {
|
||||
max-height: 100vh;
|
||||
overflow-y: auto;
|
||||
|
||||
&::-webkit-scrollbar-track-piece {
|
||||
background: #d3dce6;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #99a9bf;
|
||||
border-radius: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -16,10 +16,12 @@
|
||||
.fade-transform-enter-active {
|
||||
transition: all .5s;
|
||||
}
|
||||
|
||||
.fade-transform-enter {
|
||||
opacity: 0;
|
||||
transform: translateX(-30px);
|
||||
}
|
||||
|
||||
.fade-transform-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(30px);
|
||||
@@ -44,4 +46,3 @@
|
||||
.breadcrumb-leave-active {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
|
@@ -1,3 +1,4 @@
|
||||
// base color
|
||||
$blue:#324157;
|
||||
$light-blue:#3A71A8;
|
||||
$red:#C03639;
|
||||
@@ -8,6 +9,27 @@ $yellow:#FEC171;
|
||||
$panGreen: #30B08F;
|
||||
|
||||
//sidebar
|
||||
$menuText:#bfcbd9;
|
||||
$menuActiveText:#409EFF;
|
||||
$subMenuActiveText:#f4f4f5; //https://github.com/ElemeFE/element/issues/12951
|
||||
|
||||
$menuBg:#304156;
|
||||
$menuHover:#263445;
|
||||
|
||||
$subMenuBg:#1f2d3d;
|
||||
$menuHover:#001528;
|
||||
$subMenuHover:#001528;
|
||||
|
||||
$sideBarWidth: 210px;
|
||||
|
||||
// the :export directive is the magic sauce for webpack
|
||||
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
|
||||
:export {
|
||||
menuText: $menuText;
|
||||
menuActiveText: $menuActiveText;
|
||||
subMenuActiveText: $subMenuActiveText;
|
||||
menuBg: $menuBg;
|
||||
menuHover: $menuHover;
|
||||
subMenuBg: $subMenuBg;
|
||||
subMenuHover: $subMenuHover;
|
||||
sideBarWidth: $sideBarWidth;
|
||||
}
|
||||
|
@@ -1,8 +0,0 @@
|
||||
/**
|
||||
* Created by jiachenpan on 17/3/8.
|
||||
*/
|
||||
export default function createUniqueString() {
|
||||
const timestamp = +new Date() + ''
|
||||
const randomNum = parseInt((1 + Math.random()) * 65536) + ''
|
||||
return (+(randomNum + timestamp)).toString(32)
|
||||
}
|
@@ -11,7 +11,12 @@ export function parseTime(time, cFormat) {
|
||||
if (typeof time === 'object') {
|
||||
date = time
|
||||
} else {
|
||||
if (('' + time).length === 10) time = parseInt(time) * 1000
|
||||
if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
|
||||
time = parseInt(time)
|
||||
}
|
||||
if ((typeof time === 'number') && (time.toString().length === 10)) {
|
||||
time = time * 1000
|
||||
}
|
||||
date = new Date(time)
|
||||
}
|
||||
const formatObj = {
|
||||
@@ -86,20 +91,19 @@ export function getQueryObject(url) {
|
||||
}
|
||||
|
||||
/**
|
||||
*get getByteLen
|
||||
* @param {Sting} val input value
|
||||
* @param {Sting} input value
|
||||
* @returns {number} output value
|
||||
*/
|
||||
export function getByteLen(val) {
|
||||
let len = 0
|
||||
for (let i = 0; i < val.length; i++) {
|
||||
if (val[i].match(/[^\x00-\xff]/gi) != null) {
|
||||
len += 1
|
||||
} else {
|
||||
len += 0.5
|
||||
}
|
||||
export function byteLength(str) {
|
||||
// returns the byte length of an utf8 string
|
||||
let s = str.length
|
||||
for (var i = str.length - 1; i >= 0; i--) {
|
||||
const code = str.charCodeAt(i)
|
||||
if (code > 0x7f && code <= 0x7ff) s++
|
||||
else if (code > 0x7ff && code <= 0xffff) s += 2
|
||||
if (code >= 0xDC00 && code <= 0xDFFF) i--
|
||||
}
|
||||
return Math.floor(len)
|
||||
return s
|
||||
}
|
||||
|
||||
export function cleanArray(actual) {
|
||||
@@ -132,7 +136,8 @@ export function param2Obj(url) {
|
||||
decodeURIComponent(search)
|
||||
.replace(/"/g, '\\"')
|
||||
.replace(/&/g, '","')
|
||||
.replace(/=/g, '":"') +
|
||||
.replace(/=/g, '":"')
|
||||
.replace(/\+/g, ' ') +
|
||||
'"}'
|
||||
)
|
||||
}
|
||||
@@ -164,17 +169,6 @@ export function objectMerge(target, source) {
|
||||
return target
|
||||
}
|
||||
|
||||
export function scrollTo(element, to, duration) {
|
||||
if (duration <= 0) return
|
||||
const difference = to - element.scrollTop
|
||||
const perTick = (difference / duration) * 10
|
||||
setTimeout(() => {
|
||||
element.scrollTop = element.scrollTop + perTick
|
||||
if (element.scrollTop === to) return
|
||||
scrollTo(element, to, duration - 10)
|
||||
}, 10)
|
||||
}
|
||||
|
||||
export function toggleClass(element, className) {
|
||||
if (!element || !className) {
|
||||
return
|
||||
@@ -245,7 +239,7 @@ export function debounce(func, wait, immediate) {
|
||||
// 据上一次触发时间间隔
|
||||
const last = +new Date() - timestamp
|
||||
|
||||
// 上次被包装函数被调用时间间隔last小于设定时间间隔wait
|
||||
// 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
|
||||
if (last < wait && last > 0) {
|
||||
timeout = setTimeout(later, wait - last)
|
||||
} else {
|
||||
@@ -280,7 +274,7 @@ export function debounce(func, wait, immediate) {
|
||||
*/
|
||||
export function deepClone(source) {
|
||||
if (!source && typeof source !== 'object') {
|
||||
throw new Error('error arguments', 'shallowClone')
|
||||
throw new Error('error arguments', 'deepClone')
|
||||
}
|
||||
const targetObj = source.constructor === Array ? [] : {}
|
||||
Object.keys(source).forEach(keys => {
|
||||
@@ -296,3 +290,9 @@ export function deepClone(source) {
|
||||
export function uniqueArr(arr) {
|
||||
return Array.from(new Set(arr))
|
||||
}
|
||||
|
||||
export function createUniqueString() {
|
||||
const timestamp = +new Date() + ''
|
||||
const randomNum = parseInt((1 + Math.random()) * 65536) + ''
|
||||
return (+(randomNum + timestamp)).toString(32)
|
||||
}
|
||||
|
50
src/utils/scrollTo.js
Normal file
@@ -0,0 +1,50 @@
|
||||
Math.easeInOutQuad = function(t, b, c, d) {
|
||||
t /= d / 2
|
||||
if (t < 1) {
|
||||
return c / 2 * t * t + b
|
||||
}
|
||||
t--
|
||||
return -c / 2 * (t * (t - 2) - 1) + b
|
||||
}
|
||||
|
||||
// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
|
||||
var requestAnimFrame = (function() {
|
||||
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) }
|
||||
})()
|
||||
|
||||
// because it's so fucking difficult to detect the scrolling element, just move them all
|
||||
function move(amount) {
|
||||
document.documentElement.scrollTop = amount
|
||||
document.body.parentNode.scrollTop = amount
|
||||
document.body.scrollTop = amount
|
||||
}
|
||||
|
||||
function position() {
|
||||
return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop
|
||||
}
|
||||
|
||||
export function scrollTo(to, duration, callback) {
|
||||
const start = position()
|
||||
const change = to - start
|
||||
const increment = 20
|
||||
let currentTime = 0
|
||||
duration = (typeof (duration) === 'undefined') ? 500 : duration
|
||||
var animateScroll = function() {
|
||||
// increment the time
|
||||
currentTime += increment
|
||||
// find the value with the quadratic in-out easing function
|
||||
var val = Math.easeInOutQuad(currentTime, start, change, duration)
|
||||
// move the document.body
|
||||
move(val)
|
||||
// do the animation unless its over
|
||||
if (currentTime < duration) {
|
||||
requestAnimFrame(animateScroll)
|
||||
} else {
|
||||
if (callback && typeof (callback) === 'function') {
|
||||
// the animation is done so lets callback
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
animateScroll()
|
||||
}
|
@@ -2,31 +2,35 @@
|
||||
* Created by jiachenpan on 16/11/18.
|
||||
*/
|
||||
|
||||
export function isvalidUsername(str) {
|
||||
export function isExternal(path) {
|
||||
return /^(https?:|mailto:|tel:)/.test(path)
|
||||
}
|
||||
|
||||
export function validUsername(str) {
|
||||
const valid_map = ['admin', 'editor']
|
||||
return valid_map.indexOf(str.trim()) >= 0
|
||||
}
|
||||
|
||||
/* 合法uri*/
|
||||
export function validateURL(textval) {
|
||||
const urlregex = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
|
||||
return urlregex.test(textval)
|
||||
export function validURL(url) {
|
||||
const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
|
||||
return reg.test(url)
|
||||
}
|
||||
|
||||
/* 小写字母*/
|
||||
export function validateLowerCase(str) {
|
||||
export function validLowerCase(str) {
|
||||
const reg = /^[a-z]+$/
|
||||
return reg.test(str)
|
||||
}
|
||||
|
||||
/* 大写字母*/
|
||||
export function validateUpperCase(str) {
|
||||
export function validUpperCase(str) {
|
||||
const reg = /^[A-Z]+$/
|
||||
return reg.test(str)
|
||||
}
|
||||
|
||||
/* 大小写字母*/
|
||||
export function validateAlphabets(str) {
|
||||
export function validAlphabets(str) {
|
||||
const reg = /^[A-Za-z]+$/
|
||||
return reg.test(str)
|
||||
}
|
||||
@@ -36,7 +40,7 @@ export function validateAlphabets(str) {
|
||||
* @param email
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function validateEmail(email) {
|
||||
export function validEmail(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,}))$/
|
||||
return re.test(email)
|
||||
}
|
||||
|
14
src/vendor/Export2Excel.js
vendored
@@ -145,9 +145,11 @@ export function export_table_to_excel(id) {
|
||||
}
|
||||
|
||||
export function export_json_to_excel({
|
||||
multiHeader = [],
|
||||
header,
|
||||
data,
|
||||
filename,
|
||||
merges = [],
|
||||
autoWidth = true,
|
||||
bookType= 'xlsx'
|
||||
} = {}) {
|
||||
@@ -155,10 +157,22 @@ export function export_json_to_excel({
|
||||
filename = filename || 'excel-list'
|
||||
data = [...data]
|
||||
data.unshift(header);
|
||||
|
||||
for (let i = multiHeader.length-1; i > -1; i--) {
|
||||
data.unshift(multiHeader[i])
|
||||
}
|
||||
|
||||
var ws_name = "SheetJS";
|
||||
var wb = new Workbook(),
|
||||
ws = sheet_from_array_of_arrays(data);
|
||||
|
||||
if (merges.length > 0) {
|
||||
if (!ws['!merges']) ws['!merges'] = [];
|
||||
merges.forEach(item => {
|
||||
ws['!merges'].push(XLSX.utils.decode_range(item))
|
||||
})
|
||||
}
|
||||
|
||||
if (autoWidth) {
|
||||
/*设置worksheet每列的最大宽度*/
|
||||
const colWidth = data.map(row => row.map(val => {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="chart-container">
|
||||
<chart height="100%" width="100%"/>
|
||||
<chart height="100%" width="100%" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="chart-container">
|
||||
<chart height="100%" width="100%"/>
|
||||
<chart height="100%" width="100%" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="chart-container">
|
||||
<chart height="100%" width="100%"/>
|
||||
<chart height="100%" width="100%" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@@ -2,11 +2,11 @@
|
||||
<div class="app-container">
|
||||
<el-tabs v-model="activeName">
|
||||
<el-tab-pane label="use clipboard directly" name="directly">
|
||||
<el-input v-model="inputData" placeholder="Please input" style="width:400px;max-width:100%;"/>
|
||||
<el-input v-model="inputData" placeholder="Please input" style="width:400px;max-width:100%;" />
|
||||
<el-button type="primary" icon="document" @click="handleCopy(inputData,$event)">copy</el-button>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="use clipboard by v-directive" name="v-directive">
|
||||
<el-input v-model="inputData" placeholder="Please input" style="width:400px;max-width:100%;"/>
|
||||
<el-input v-model="inputData" placeholder="Please input" style="width:400px;max-width:100%;" />
|
||||
<el-button v-clipboard:copy="inputData" v-clipboard:success="clipboardSuccess" type="primary" icon="document">copy</el-button>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|