Compare commits

..

39 Commits

Author SHA1 Message Date
Pan
aa9d48905f [release] 4.0.1 2019-04-10 15:54:47 +08:00
花裤衩
9cba45e971 fix[Sidebar]: fixed external link bug (#1870) 2019-04-10 10:12:16 +08:00
Pan
dbee6ff707 refactor[mock-server]: refactor mock-server #1860 2019-04-09 14:25:14 +08:00
derrick
b627d3d0ba fix[TagsView]: fixed close last page bug(#1866) 2019-04-09 12:56:57 +08:00
花裤衩
8f45dbe328 feature[Icons]: add element-ui icons demo (#1865) 2019-04-09 09:58:08 +08:00
Pan
342b7b428a fix[Chore]: add @vue/cli-plugin-eslint 2019-04-08 22:05:10 +08:00
Pan
c833cb6efa perf[Chore]: optimization.splitChunks adapt cnpm module name 2019-04-08 21:45:10 +08:00
Pan
afb62edc58 fix[Sidebar]: fixed sidebar background css bug 2019-04-08 18:10:41 +08:00
MaYuanhai
0358667a73 perf[Login]: password input add caps tooltip (#1845) 2019-04-08 14:13:07 +08:00
花裤衩
e3b6602bbf fix[Sidebar]: fixed sidebar scrollbar bug (#1853) 2019-04-08 13:02:41 +08:00
花裤衩
79e2a604af feature[Sidebar]: add activeMenu option (#1833) 2019-04-08 12:58:33 +08:00
Pan
083a4ada9d perf[request.js]: optimize error message 2019-04-08 11:08:06 +08:00
花裤衩
90b7c2fbde feature[Mock]: mock-server support hot reload (#1850)
https://github.com/PanJiaChen/vue-element-admin/issues/1849
2019-04-08 11:01:24 +08:00
花裤衩
f11839c8a4 docs: refine i18n (#1848) 2019-04-07 23:10:14 +08:00
anson
7492e2097f fix[ErrorLog]: fixed error log dialog z-index bug (#1844)
https://github.com/PanJiaChen/vue-element-admin/issues/1830
2019-04-04 17:51:44 +08:00
Pan
770753eff2 fix[request.js]: fixed missing return 2019-04-04 17:43:12 +08:00
花裤衩
0d40222b64 fix[Drag]: fixed drag bug in firefox (#1841) 2019-04-04 16:21:45 +08:00
Estelle00
e363c7a77b feature[Vuex]: auto import vuex modules(#1815) 2019-04-04 14:37:01 +08:00
花裤衩
c923726464 fix[TagsView]: fixed z-index bug (#1836) 2019-04-04 13:07:18 +08:00
花裤衩
43115e5538 perf: optimize page view name (#1835) 2019-04-04 12:58:37 +08:00
Pan
184125bdd3 fix[Guide]: fixed missing component id 2019-04-03 18:19:35 +08:00
Pan
25414f1fd9 fix[Guide]: fixed guide demo bug 2019-04-03 17:52:30 +08:00
Pan
cba0b789d0 chore: update issue template 2019-04-03 14:28:03 +08:00
Pan
4ad51be2db chore: add issue template 2019-04-03 14:23:44 +08:00
MaYuanhai
a1708e9b68 perf: select demo add default-first-option
其实这个属性还是看作者写菜单模糊搜索下拉框时候发现的,觉得体验非常棒。虽然文章模块这里只是一个示例,但是我觉得还是加上比较好。
2019-04-03 13:40:24 +08:00
Pan
55fa5acb85 perf: optimize the code of el-table slot-scope 2019-04-02 16:32:51 +08:00
Pan
a8c6e11ee6 fix[Css]: fixed github-corner css bug 2019-04-02 10:39:26 +08:00
Pan
26d1bf09fd fix[Mock]: fixed mock bug 2019-04-01 17:37:37 +08:00
花裤衩
b94e69be6f [release] 4.0.0 (#1291)
* fix[ExternalLink]: fixed bug when url include chinese #1182

* feature: support  Spanish(#1196)

* fix[MockJS]: fix bug with withCredentials after using mockjs  (#1194)

* 修复 Mock 导致请求丢失 Cookie 的问题

修复 Mock 导致 Cookie 丢失的问题,只有在 XHR.open() 周期时,自定义的 withCredentials 会被挂载,此时检查是否是未被拦截的 xhr,并挂载自定义的 withCredentials ,无则默认为 false

* update readme

* perf[tagsView]: refactor the moveToTarget function (#1195)

* fix[tagsView]:fixed visited view move to currentTag

* edit the scroll regular friendly

* tweak

* fix[tagsView]: fixed moveToCurrentTag bug

* feature: add pagination component (#1213)

* fix[TagsView]: fixed update tags title demo bug (#1223)

* chore: temporary hack cssnano bug #1222

* [release] 3.9.2

* chore: restore the hack of cssnano bug

https://github.com/cssnano/cssnano/issues/643

* add an example of sort data by table  (#1236)

* feature: add drag select component (#1249)

* feat: perfect migrate to @vue/cli-service, upgrade vue babel version (#1267)

* feat: perfect migrate to @vue/cli-service, upgrade vue babel version

1. update to @vue/cli-service@3.0.5, @babel/core@7.0.0
2. use vue-cli service replace config file in build/ and config/
3. upgrade vue and babel configuration
4. solve the svg-sprite config problem #980

refs: #932 #1087  #980 #1056

* fix: fix breadcrumb dependency

* fix: fix index template and static assets load with vue-cli 3

* fix: fix import driver.js in guide page

* refactor(mock): mak mock api compatible with both web-view and webpack server

1. 把 Mockjs 功能移到 server 端中间件,同时也兼容前端直接劫持 XHR
2. dev 环境下默认作为 express 中间件通过 webpack server 提供 mock api
3. prod 构建时,默认在前端用 Mockjs 劫持 XHR

benefits:
  - dev 开发调试时能直接看到 XHR 请求,方便调试网络,能和后端对接联调
  - 避开在开发时因为 Mockjs 引起的网络 bug
  - prod 构建时劫持 XHR,保证本项目的 Github Pages preview 能正常显示 (逻辑和 error-log 一样)
  - 前后台使用的 mock 是同一份代码,不会增加维护负担

ref: [#562](https://github.com/PanJiaChen/vue-element-admin/issues/562#issuecomment-378116233)

* update requires the lowest version of node

* add favicon

* fix(TreeTable): fix `Array.prototype.concat` on custom-tree-table page

* update

* add test

* fix bug

* fix[Charts]: fixed charts resize mixins bug  #1285 (#1290)

* perf[Tinymce]: add searchreplace plugin

* perf[avatar]:minimize the selected area of avatar on the mobile phone when user clicked avatar (#1304)

* refine css

* fix[DragSelect]: fixed querySelectorAll bug

* perf[DragSelect]: add $listeners

* fix link

* fix[Breadcurmb]: fixed pathCompile bug

* fix[Breadcurmb]: fixed router-link bug

* perf[style]: use webpack alias instead of hard code src path (#1338)

* perf[style]: use webpack alias instead of hard code src path

* add sponsors

* fix import path bug

* update vue-router to fixed url path for non ascii urls #1362

* fix[Pagination]: apply PageSizes property to el-pagination  (#1355)

Apply PageSizes property to el-pagination

* update dependence

* add tui.editor (#1374)

* tweak

* add preview

* fix return back bug

* update guide page

* fix[Tinymce]: fixed fullScreen bug #1400

* feat[Breadcrumb]: add hide Breadcrumb option #1442

* perf: use WeChat 7.0 new version icon color

* refactor[login]: refactor login page style

* perf[ScrollPane]: refine moveToTarget code (#1460)

* feature[PDF]: add PDF demo (#1469)

* perf[v-permission]: refine v-permission demo

* perf[Sidebar]: refine sidebar store #1473 (#1474)

* refine: GetUserInfo error message

* fix typo (#1505)

* perf: add sidebar width to variables.scss (#1494)

* tweak

* fix[ThemePicker]: fixed bug when oldVal is null (#1517)

* update README.md

* fix[Breadcrumb]: fixed eslint error (#1521)

* fix[DndList]: fixed drag bug (#1527)

https://github.com/PanJiaChen/vue-element-admin/issues/1524

* pref[Hamburger]: refactor Hamburger component (#1528)

* 美化侧栏菜单切换按钮

* tweak

* perf[Login Form]: optimize eye icon style (#1545)

* optimiz: eye icon style for login form

* change eye-open svg

* perf[Sticky]: export reset method (#1550)

* perf[Sticky]: refine width default value

* perf[utils]: refine parseTime function (#1546)

* 优化 parseTime

修复传入的时间戳是字符串类型,不能转换时间的问题
例:parseTime("1548221490638")

* Update index.js

* perf[UploadExcel]: optimized code (#1552)

* perf: adjust the import order to make it more elegant #1537

* perf[Sidebar]: use sass variables in vue template

* perf[Style]: optimize the sidebar style to make it better to set (#1568)

* perf[SizeSelect]: add default size option (#1566)

* fix[SIdebar]: fixed bug in mobile #1567 (#1569)

* perf: fixed eslint errors

* perf[Lang]: make up for miss keywords

* perf: optimize some code

* perf[Navbar]: refactor navbar style

* perf[Login]: refine css

* feature[Navbar]: add header-search component(#1591)

* fix[Screenfull]: fix screenfull click bug

* perf[Screenfull]: refactor screenfull component

* fix[Screenfull]: fix screenfull bug (#1603)

* fix typo

* fearure[TagsView]: add affix option (#1577)

* perf[utils]: optimize code

* perf[utils]: optimizate variable name

* perf[Navbar]: add scroll bar when the subMenu is too long (#1619)

* perf[ThemePicker]: refine updateStyle function (#554)

* theme replacing should cut tons of irrelevant css

* perf[ResizeHandler]: optimized the judgment of isMobile (#1633)

perf[ResizeHandler]: optimized the judgment of isMobile

*  fix[Sidebar]: fixed infinite loop bug(#1333)

* fixed infinite loop Bug when in hasOneShowingChild Edit the onlyOneChild

* tweak

* fix[Sidebar]: data should return a object

* perf[Sidebar]: optimize code logic (#1349)

* fix[TagsView]: fixed refresh affixed-tag bug (#1653)

* perf[utils.js]: refactor byteLength function (#1650)

* perf[TagsView]: refine code

* perf[TagsView]: set the scrollPane as a business component (#1660)

* fix[DragTable]: support multiple drag-table (#1666)

* perf[Tree-Table]: refactor tree-table

* perf[Tree-Table]: organize the structure and add documentation (#1673)

* fix[Sidebar]: fixed nested router hover bug

* update version

* set preserveWhitespace

* lint code

* fix jest test case

* update config

* bump

* remove empty file

* docs: add link

* fix[Sidebar]: fixed collapse animation problem (#1690)

* fix[Tree-Table]: fixed update item data bug (#1692)

* fix[Waves-Directive]: fixed v-waves does not support update (#1705)

* update husky

* rm cli-plugin-eslint

* add settings (#1707)

* refine settings

* fix[utils]: fixed param2Obj not decoding plus sign (#1712)

* feature[Directive]: add auto-height table directive (#1702)

* fix bug

* feature[Permission]: add role permission management page (#1605)

* feature[Excel]: support export merged header export (#1718)

* feature[Excel]: add export merge header excel demo

* lint

* refine theme color

* add role mock

* tweak mock

* fix[Excel]: fixed export merge-header excel bug

* refine code

* add ThemePicker to setting

* fix[HeaderSearch]: fixed bug in vue2.6+ (#1733)

* fix[Sticky]: fixed bug when set stickyTop

* perf[Sticky]: refine demo

* refine code

* tweak mock

* vuex add namespaced

* fix[Excel]: fixed export bug (#1736)

* rm

* refactor permission

* perf[ThemePicker]: add predefine (#1743)

* fix[Utils]: fixed deepClone error msg (#1748)

* feature: add fixedHeader settings

* fix style in mobile

* fix chore

* perf[Eslint]: update eslint rules

* feature: add create template (#1762)

* add comment

* update vue.config.js

* feature: add sidebar logo (#1767)

* rm

* perf settings

* bump

* refine script and css

* update

* refine settings

* refine config

* update docs

* refine

* rm

* fix jest

* add theme setting

* dump vue-cli

* perf: remove redundant code

* update element-ui

* fix sticky demo bug

* docs

* fixed password input  bug

* refine login form css

* remove tree-table

* update version

* mock error

* refine layout name

* refine
2019-04-01 17:07:16 +08:00
MaYuanhai
96d3cfa215 perf[Login]: optimize input focus interaction (#1798) 2019-03-29 12:55:07 +08:00
Pan
20f6150741 [release] 3.11.0 2019-03-28 14:15:32 +08:00
Pan
7703005013 perf: remove redundant code 2019-03-28 11:44:04 +08:00
zhu yu
9a5c404ef8 docs: fix typo (#1782)
i18 -> i18n
2019-03-27 15:18:30 +08:00
sun ao
9d975b5eff fix[Mock]: add xhr.responseType (#1779) 2019-03-27 13:31:34 +08:00
MaYuanhai
331173ffee perf[Css]: fixed typo (#1776) 2019-03-26 12:59:31 +08:00
Pan
f890685d8d docs 2019-03-25 18:39:54 +08:00
Pan
fb30079477 docs 2019-03-25 18:04:14 +08:00
Pan
3100d0cff4 fix[Sidebar]: fixed v-if meta bug 2019-03-22 15:11:22 +08:00
Yuga Sun
ae6bbf7858 fix[Utils]: fixed deepClone error msg (#1748) 2019-03-21 15:14:15 +08:00
107 changed files with 993 additions and 1810 deletions

View File

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

View File

@@ -1,2 +1,6 @@
VUE_APP_BASE_API = '/api'
# just a flag
ENV = 'production'
# base api
VUE_APP_BASE_API = '/prod-api'

View File

@@ -1,3 +1,8 @@
NODE_ENV=production
VUE_APP_BASE_API = '/api'
NODE_ENV = production
# just a flag
ENV = 'staging'
# base api
VUE_APP_BASE_API = '/stage-api'

View File

@@ -1,3 +1,4 @@
build/*.js
config/*.js
src/assets
public
dist

33
.github/ISSUE_TEMPLATE/bug_report.md vendored Executable file
View File

@@ -0,0 +1,33 @@
---
name: Bug report报告问题
about: Create a report to help us improve
---
<!--
注意:为更好的解决你的问题,请参考模板提供完整信息,准确描述问题,信息不全的 issue 将被关闭。
Note: In order to better solve your problem, please refer to the template to provide complete information, accurately describe the problem, and the incomplete information issue will be closed.
-->
## Bug report问题描述
#### Steps to reproduce问题复现步骤
<!--
1. [xxx]
2. [xxx]
3. [xxxx]
-->
#### Screenshot or Gif截图或动态图
#### Link to minimal reproduction最小可在线还原demo
<!--
Please only use Codepen, JSFiddle, CodeSandbox or a github repo
-->
#### Other relevant information格外信息
- Your OS:
- Node.js version:
- vue-element-admin version:

7
.github/ISSUE_TEMPLATE/feature_request.md vendored Executable file
View File

@@ -0,0 +1,7 @@
---
name: Feature Request新功能建议
about: Suggest an idea for this project
---
## Feature request新功能建议

14
.github/ISSUE_TEMPLATE/question.md vendored Executable file
View File

@@ -0,0 +1,14 @@
---
name: Question提问
about: Asking questions about use
---
## Question提问
<!--
提问之前,请确定你已经过自己的努力,尝试解决过这个问题。
若是代码相关问题,请不要只截图,请提供在线 demo以便节约彼此的时间。
Before asking a question, please make sure that you have tried your best to solve this problem.
If it's a code-related issue, please don't just take screenshots. Please provide an online demo to save each other's time.
-->

View File

@@ -4,10 +4,10 @@
<p align="center">
<a href="https://github.com/vuejs/vue">
<img src="https://img.shields.io/badge/vue-2.5.17-brightgreen.svg" alt="vue">
<img src="https://img.shields.io/badge/vue-2.6.10-brightgreen.svg" alt="vue">
</a>
<a href="https://github.com/ElemeFE/element">
<img src="https://img.shields.io/badge/element--ui-2.4.11-brightgreen.svg" alt="element-ui">
<img src="https://img.shields.io/badge/element--ui-2.7.0-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,7 @@ English | [简体中文](./README.zh-CN.md)
## Introduction
[vue-element-admin](http://panjiachen.github.io/vue-element-admin) is a front-end management background integration solution. It based on [vue](https://github.com/vuejs/vue) and use the UI Toolkit [element](https://github.com/ElemeFE/element).
[vue-element-admin](http://panjiachen.github.io/vue-element-admin) is a production-ready front-end solution for admin interfaces. It based on [vue](https://github.com/vuejs/vue) and use the UI Toolkit [element-ui](https://github.com/ElemeFE/element).
It is a magical vue admin based on the newest development stack of vue, built-in i18n solution, typical templates for enterprise applications, lots of awesome features. It helps you build a large complex Single-Page Applications. I believe whatever your needs are, this project will help you.
@@ -40,36 +40,31 @@ It is a magical vue admin based on the newest development stack of vue, built-in
- [Gitter](https://gitter.im/vue-element-admin/discuss)
- [Wiki](https://github.com/PanJiaChen/vue-element-admin/wiki)
- [Donate](https://panjiachen.github.io/vue-element-admin-site/donate/)
- [Gitee](https://panjiachen.gitee.io/vue-element-admin/) 国内用户可访问该地址在线预览
- [Wiki](https://github.com/PanJiaChen/vue-element-admin/wiki)
**This project is positioned as a background integration solution and is not suitable for secondary development as a basic template.**
- [Gitee](https://panjiachen.gitee.io/vue-element-admin/) 国内用户可访问该地址在线预览
- Base template recommends using: [vue-admin-template](https://github.com/PanJiaChen/vue-admin-template)
- Desktop: [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
- Typescript: [vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template) (Credits: [@Armour](https://github.com/Armour))
**This project does not support low version browsers (e.g. IE). Please add polyfill yourself if you need them.**
**The current version is `4.0-beta`. If you find a problem, please put [issue](https://github.com/PanJiaChen/vue-element-admin/issues/new). If you want to use the old version - stable version, you can switch branch to [tag/3.11.0](https://github.com/PanJiaChen/vue-element-admin/tree/tag/3.11.0)**
**Note: This project uses element-ui@2.3.0+ version, so the minimum compatible vue@2.5.0+**
**Start using `webpack4` from `v3.8.0`. If you still want to continue using `webpack3`, please use this branch [webpack3](https://github.com/PanJiaChen/vue-element-admin/tree/webpack3)**
**This project does not support low version browsers (e.g. IE). Please add polyfill by yourself.**
## Preparation
You need to install [node](http://nodejs.org/) and [git](https://git-scm.com/) locally. The project is based on [ES2015+](http://es6.ruanyifeng.com/), [vue](https://cn.vuejs.org/index.html), [vuex](https://vuex.vuejs.org/zh-cn/), [vue-router](https://router.vuejs.org/zh-cn/), [axios](https://github.com/axios/axios) and [element-ui](https://github.com/ElemeFE/element), all request data is simulated using [Mock.js](https://github.com/nuysoft/Mock).
You need to install [node](http://nodejs.org/) and [git](https://git-scm.com/) locally. The project is based on [ES2015+](http://es6.ruanyifeng.com/), [vue](https://cn.vuejs.org/index.html), [vuex](https://vuex.vuejs.org/zh-cn/), [vue-router](https://router.vuejs.org/zh-cn/), [vue-cli](https://github.com/vuejs/vue-cli) , [axios](https://github.com/axios/axios) and [element-ui](https://github.com/ElemeFE/element), all request data is simulated using [Mock.js](https://github.com/nuysoft/Mock).
Understanding and learning this knowledge in advance will greatly help the use of this project.
---
<p align="center">
<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>
@@ -82,6 +77,7 @@ Become a sponsor and get your logo on our README on GitHub with a link to your s
- Permission Authentication
- Page permission
- Directive permission
- Permission configuration page
- Two-step login
- Multi-environment build
@@ -105,14 +101,13 @@ Become a sponsor and get your logo on our README on GitHub with a link to your s
- Excel
- Export Excel
- Export zip
- Upload Excel
- Visualization Excel
- Export zip
- Table
- Dynamic Table
- Drag And Drop Table
- Tree Table
- Inline Edit Table
- Error Page
@@ -146,6 +141,9 @@ Become a sponsor and get your logo on our README on GitHub with a link to your s
# clone the project
git clone https://github.com/PanJiaChen/vue-element-admin.git
# enter the project directory
cd vue-element-admin
# install dependency
npm install
@@ -153,13 +151,13 @@ npm install
npm run dev
```
This will automatically open http://localhost:9527.
This will automatically open http://localhost:9527
## Build
```bash
# build for test environment
npm run build:sit
npm run build:stage
# build for production environment
npm run build:prod
@@ -168,19 +166,16 @@ npm run build:prod
## Advanced
```bash
# --report to build with bundle size analytics
npm run build:prod --report
# preview the release environment effect
npm run preview
# --generate a bundle size analytics. default: bundle-report.html
npm run build:prod --generate_report
# preview the release environment effect + static resource analysis
npm run preview -- --report
# --preview to start a server in local to preview
npm run build:prod --preview
# lint code
# code format check
npm run lint
# auto fix
# code format check and auto fix
npm run lint -- --fix
```

View File

@@ -4,10 +4,10 @@
<p align="center">
<a href="https://github.com/vuejs/vue">
<img src="https://img.shields.io/badge/vue-2.5.10-brightgreen.svg" alt="vue">
<img src="https://img.shields.io/badge/vue-2.6.10-brightgreen.svg" alt="vue">
</a>
<a href="https://github.com/ElemeFE/element">
<img src="https://img.shields.io/badge/element--ui-2.4.11-brightgreen.svg" alt="element-ui">
<img src="https://img.shields.io/badge/element--ui-2.7.0-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,41 +30,37 @@
## 简介
[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-ui](https://github.com/ElemeFE/element)实现。它使用了最新的前端技术栈,内置了 i18n 国际化解决方案,动态路由,权限验证,提炼了典型的业务模型,提供了丰富的功能组件,它可以帮助你快速搭建企业级中后台产品原型。相信不管你的需求是什么,本项目都能帮助到你。
- [在线访问](http://panjiachen.github.io/vue-element-admin)
- [在线预览](http://panjiachen.github.io/vue-element-admin)
- [使用文档](https://panjiachen.github.io/vue-element-admin-site/zh/)
- [Gitter 讨论组](https://gitter.im/vue-element-admin/discuss)
- [Wiki](https://github.com/PanJiaChen/vue-element-admin/wiki)
- [Donate](https://panjiachen.gitee.io/vue-element-admin-site/zh/donate)
- [Gitee](https://panjiachen.gitee.io/vue-element-admin/) 国内用户可访问该地址在线预览
- [Wiki](https://github.com/PanJiaChen/vue-element-admin/wiki)
- [国内访问文档](https://panjiachen.gitee.io/vue-element-admin-site/zh/) 方便没翻墙的用户查看文档
- [Gitee](https://panjiachen.gitee.io/vue-element-admin/) 在线预览(国内用户可访问该地址)
**本项目的定位是后台集成方案,不适合当基础模板来开发。**
- [国内访问文档](https://panjiachen.gitee.io/vue-element-admin-site/zh/) 文档(方便没翻墙的用户查看)
- 模板建议使用: [vue-admin-template](https://github.com/PanJiaChen/vue-admin-template)
- 基础模板建议使用: [vue-admin-template](https://github.com/PanJiaChen/vue-admin-template)
- 桌面端: [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))
- 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)
**注意:该项目使用 element-ui@2.3.0+ 版本,所以最低兼容 vue@2.5.0+**
**从`v3.8.0`开始使用`webpack4`。所以若还想使用`webpack3`开发,请使用该分支[webpack3](https://github.com/PanJiaChen/vue-element-admin/tree/webpack3)**
**目前版本为 `4.0-beta`,若发现问题,欢迎提[issue](https://github.com/PanJiaChen/vue-element-admin/issues/new)。若你想使用旧版本-稳定版,可以切换分支到[tag/3.11.0](https://github.com/PanJiaChen/vue-element-admin/tree/tag/3.11.0)**
**该项目不支持低版本浏览器(如 ie),有需求请自行添加 polyfill [详情](https://github.com/PanJiaChen/vue-element-admin/wiki#babel-polyfill)**
群主 **[圈子](https://jianshiapp.com/circles/1209)** 群主会经常分享一些技术相关的东西,或者加入 [qq 群](https://github.com/PanJiaChen/vue-element-admin/issues/602) 或者关注 [微博](https://weibo.com/u/3423485724?is_all=1)
## 前序准备
你需要在本地安装 [node](http://nodejs.org/) 和 [git](https://git-scm.com/)。本项目技术栈基于 [ES2015+](http://es6.ruanyifeng.com/)、[vue](https://cn.vuejs.org/index.html)、[vuex](https://vuex.vuejs.org/zh-cn/)、[vue-router](https://router.vuejs.org/zh-cn/) 、[axios](https://github.com/axios/axios) 和 [element-ui](https://github.com/ElemeFE/element),所有的请求数据都使用[Mock.js](https://github.com/nuysoft/Mock)模拟,提前了解和学习这些知识会对使用本项目有很大的帮助。
你需要在本地安装 [node](http://nodejs.org/) 和 [git](https://git-scm.com/)。本项目技术栈基于 [ES2015+](http://es6.ruanyifeng.com/)、[vue](https://cn.vuejs.org/index.html)、[vuex](https://vuex.vuejs.org/zh-cn/)、[vue-router](https://router.vuejs.org/zh-cn/) 、[vue-cli](https://github.com/vuejs/vue-cli) 、[axios](https://github.com/axios/axios) 和 [element-ui](https://github.com/ElemeFE/element),所有的请求数据都使用[Mock.js](https://github.com/nuysoft/Mock)进行模拟,提前了解和学习这些知识会对使用本项目有很大的帮助。
同时配套一个系列教程文章,如何从零构建后一个完整的后台项目,建议大家先看完这些文章再来实践本项目
同时配套系列教程文章,如何从零构建后一个完整的后台项目,建议大家先看完这些文章再来实践本项目
- [手摸手,带你用 vue 撸后台 系列一(基础篇)](https://juejin.im/post/59097cd7a22b9d0065fb61d2)
- [手摸手,带你用 vue 撸后台 系列二(登录权限篇)](https://juejin.im/post/591aa14f570c35006961acac)
@@ -82,6 +78,7 @@
</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>
@@ -94,6 +91,7 @@ Become a sponsor and get your logo on our README on GitHub with a link to your s
- 权限验证
- 页面权限
- 指令权限
- 权限配置
- 二步登录
- 多环境发布
@@ -106,7 +104,7 @@ Become a sponsor and get your logo on our README on GitHub with a link to your s
- 动态面包屑
- 快捷导航(标签页)
- Svg Sprite 图标
- 本地mock数据
- 本地/后端 mock 数据
- Screenfull全屏
- 自适应收缩侧边栏
@@ -117,14 +115,13 @@ Become a sponsor and get your logo on our README on GitHub with a link to your s
- Excel
- 导出excel
- 导出zip
- 导入excel
- 前端可视化excel
- 导出zip
- 表格
- 动态表格
- 拖拽表格
- 树形表格
- 内联编辑
- 错误页面
@@ -158,6 +155,9 @@ Become a sponsor and get your logo on our README on GitHub with a link to your s
# 克隆项目
git clone https://github.com/PanJiaChen/vue-element-admin.git
# 进入项目目录
cd vue-element-admin
# 安装依赖
npm install
@@ -174,7 +174,7 @@ npm run dev
```bash
# 构建测试环境
npm run build:sit
npm run build:stage
# 构建生产环境
npm run build:prod
@@ -183,19 +183,16 @@ npm run build:prod
## 其它
```bash
# --report to build with bundle size analytics
npm run build:prod
# 预览发布环境效果
npm run preview
# --generate a bundle size analytics. default: bundle-report.html
npm run build:prod --generate_report
# 预览发布环境效果 + 静态资源分析
npm run preview -- --report
# --preview to start a server in local to preview
npm run build:prod --preview
# lint code
# 代码格式检查
npm run lint
# auto fix
# 代码格式检查并自动修复
npm run lint -- --fix
```
@@ -218,6 +215,8 @@ Detailed changes for each release are documented in the [release notes](https://
[Paypal Me](https://www.paypal.me/panfree23)
[Buy me a coffee](https://www.buymeacoffee.com/Pan)
## Browsers support
Modern browsers and Internet Explorer 10+.

View File

@@ -1,52 +0,0 @@
#!/usr/bin/env node
'use strict'
const checker = require('../lib/checkSystem.js')
const colors = require('colors')
// set color theme
colors.setTheme({
success: 'green',
info: 'grey',
warn: 'yellow',
error: 'red',
boldWarn: ['bold', 'yellow'],
boldUnderlineSuccess: ['bold', 'underline', 'green'],
boldUnderlineError: ['bold', 'underline', 'red']
})
console.log('Checking versions...'.info, '\n')
checker(process.argv[2]).then((result) => {
// check if the process should exit prematurely
if (result.status != 0) {
console.log(colors[result.message.type](result.message.text))
process.exit(result.status)
}
// print out results for each package
result.packages.forEach((p) => {
if (p.type === 'success') {
console.log(('✔ ' + colors.bold(p.name) + ' was validated with ' + p.expectedVersion + '.').success)
} else if (p.type === 'warn') {
console.log((colors.bold(p.name) + ' was expected, but no validator found!').warn)
} else if (p.type === 'error' && p.commandError) {
console.log(('✘ Error validating ' + colors.bold(p.name) + ': ' + p.commandError).error)
} else if (p.type === 'error' && !p.commandError) {
console.log((
'✘ ' + colors.bold(p.name) +
' version is incorrect! Expected ' +
p.expectedVersion + ' but was ' + p.foundVersion + '.'
).error)
}
})
// print out a summary message
if (result.message.type === 'success') {
console.log('\n', result.message.text.boldUnderlineSuccess)
} else {
console.log('\n', result.message.text.boldUnderlineError)
process.exit(1)
}
})

View File

@@ -5,6 +5,8 @@ const rawArgv = process.argv.slice(2)
const args = rawArgv.join(' ')
if (process.env.npm_config_preview || rawArgv.includes('--preview')) {
const report = rawArgv.includes('--report')
run(`vue-cli-service build ${args}`)
const port = 9526
@@ -21,10 +23,12 @@ if (process.env.npm_config_preview || rawArgv.includes('--preview')) {
})
)
app.listen(port, function() {
console.log(
chalk.green(`> Listening at http://localhost:${port}${publicPath}`)
)
app.listen(port, function () {
console.log(chalk.green(`> Preview at http://localhost:${port}${publicPath}`))
if (report) {
console.log(chalk.green(`> Report at http://localhost:${port}${publicPath}report.html`))
}
})
} else {
run(`vue-cli-service build ${args}`)

View File

@@ -1,9 +1,9 @@
module.exports = {
verbose: true,
moduleFileExtensions: ['js', 'jsx', 'json', 'vue'],
// transformIgnorePatterns: [
// 'node_modules/(?!(babel-jest|jest-vue-preprocessor)/)'
// ],
transformIgnorePatterns: [
'node_modules/(?!(babel-jest|jest-vue-preprocessor)/)'
],
transform: {
'^.+\\.vue$': 'vue-jest',
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',

View File

@@ -1,122 +0,0 @@
'use strict'
const check = function(pathToPackage) {
const path = require('path')
const Promise = require('bluebird')
const exec = Promise.promisify(require('child_process').exec)
const _ = require('lodash')
const fs = require('fs')
const jsonfile = require('jsonfile')
const validaterRules = require('./validatorRules')
const promiseHelpers = require('./promiseHelpers')
let engines
const checkerResult = {
status: 0,
message: '',
packages: []
}
const packageJsonPath = pathToPackage || path.join(process.cwd(), 'package.json')
try {
fs.accessSync(packageJsonPath)
engines = jsonfile.readFileSync(packageJsonPath).engines
} catch (ex) {
checkerResult.message = {
text: '✘ No package.json found in the current directory so I can\'t validate what you need!',
type: 'error'
}
checkerResult.status = -1
return Promise.resolve(checkerResult)
}
if (!engines) {
checkerResult.message = {
text: '✘ No engines found in package.json so I can\'t validate what you need!',
type: 'error'
}
checkerResult.status = -1
return Promise.resolve(checkerResult)
}
const thingsToCheck = Object.getOwnPropertyNames(engines)
const validatorPromises = thingsToCheck.map(validate) // run the function over all items.
return promiseHelpers.allSettled(validatorPromises)
.then((inspections) => {
const environmentIsValid = _.every(inspections,
(inspection) => inspection.isFulfilled() && inspection.value())
if (environmentIsValid) {
checkerResult.message = {
text: 'Environment looks good!',
type: 'success'
}
} else {
checkerResult.message = {
text: 'Environment is invalid!',
type: 'error'
}
}
return checkerResult
})
function validate(name) {
// find it in the validators
const validator = validaterRules[name]
if (validator === undefined) {
checkerResult.packages.push({
name: name,
validatorFound: false,
type: 'warn'
})
return Promise.resolve(false)
}
// call the validator and pass in the version we expect
return execAndCheck(validator, engines[name]).then((results) => {
if (results.result) {
checkerResult.packages.push({
name: name,
validatorFound: true,
expectedVersion: engines[name],
foundVersion: engines[name],
type: 'success'
})
} else {
checkerResult.packages.push({
name: name,
validatorFound: true,
expectedVersion: engines[name],
foundVersion: results.reason.trim() || 'missing',
type: 'error'
})
}
return Promise.resolve(results.result)
}).catch((error) => {
checkerResult.packages.push({
name: name,
validatorFound: true,
expectedVersion: engines[name],
commandError: error,
type: 'error'
})
return Promise.reject()
})
}
function execAndCheck(validator, expectedVersion) {
return exec(validator.versionCheck).then((result) => {
return {
result: validator.versionValidate(result, expectedVersion),
reason: result
}
}).catch((e) => { throw e })
}
}
module.exports = check

View File

@@ -1,24 +0,0 @@
const Promise = require('bluebird')
/**
* Creates a promise that is resolved when all input promises have been
* settled. The returned Promise is resolved with an array of
* Promise.Inspection objects.
*
* This is the commonly accepted way of implementing allSettled() in Bluebird.
* See: http://bluebirdjs.com/docs/api/reflect.html
*
* @param promises - The array of input promises.
* @returns {Promise<Promise.Inspection[]>} A promise that will be resolved once
* all input Promises have settled. The returned Promise will be resolved with a
* corresponding array of Promise.Inspection objects.
*/
function allSettled(promises) {
'use strict'
const wrappedPromises = promises.map((curPromise) => curPromise.reflect())
return Promise.all(wrappedPromises)
}
module.exports = {
allSettled: allSettled
}

View File

@@ -1,141 +0,0 @@
'use strict'
const semver = require('semver')
module.exports = {
osx: {
versionCheck: 'sw_vers -productVersion',
versionValidate:
(detectedVersion, expectedVersion) => expectedVersion === detectedVersion.trim()
},
node: {
versionCheck: 'node -v',
versionValidate:
(detectedVersion, expectedVersion) => semver.satisfies(detectedVersion, expectedVersion)
},
npm: {
versionCheck: 'npm -v',
versionValidate:
(detectedVersion, expectedVersion) => semver.satisfies(detectedVersion, expectedVersion)
},
jx: {
versionCheck: 'jx -jxv',
versionValidate:
(result, version) => 'v' + version === result.trim()
},
cordova: {
versionCheck: 'cordova -v',
versionValidate:
(result, version) => version === result.trim()
},
appium: {
versionCheck: 'appium -v',
versionValidate:
(result, version) => version === result.trim()
},
'ios-deploy': {
versionCheck: 'ios-deploy -V',
versionValidate:
(result, version) => version === result.trim()
},
'ios-sim': {
versionCheck: 'ios-sim --version',
versionValidate:
(result, version) => version === result.trim()
},
bower: {
versionCheck: 'bower -v',
versionValidate:
(result, version) => semver.satisfies(result, version)
},
'ios-webkit-debug-proxy': {
versionCheck: 'brew list ios-webkit-debug-proxy --versions',
versionValidate:
(result, version) => result.includes(version)
},
'ideviceinstaller': {
versionCheck: 'brew list ideviceinstaller --versions',
versionValidate:
(result, version) => result.includes(version)
},
java: {
versionCheck: 'javac -version 2>&1',
versionValidate:
(result, version) => result.includes(version)
},
ant: {
versionCheck: 'ant -version',
versionValidate:
(result, version) => result.includes(version)
},
adb: {
versionCheck: 'adb version',
versionValidate:
(result, version) => result.includes(version)
},
git: {
versionCheck: 'git --version',
versionValidate:
(result, version) => {
// http://stackoverflow.com/questions/82064/a-regex-for-version-number-parsing
const found = result.match(/(\d+\.)?(\d+\.)?(\d+)/i)
return found[0] === version
}
},
windows: {
versionCheck: 'ver',
versionValidate:
(result, version) => result.includes(version)
},
'gulp-cli': {
versionCheck: 'npm list --depth=0 -g |grep gulp-cli',
versionValidate:
(result, version) => result.includes(version)
},
cocoapods: {
versionCheck: 'pod --version',
versionValidate:
(result, version) => version === result.trim()
},
xcodebuild: {
versionCheck: 'xcodebuild -version',
versionValidate:
(result, version) => result.includes(version)
},
carthage: {
versionCheck: 'carthage version',
versionValidate:
(result, version) => version === result.trim()
},
xcpretty: {
versionCheck: 'xcpretty -v',
versionValidate:
(result, version) => version === result.trim()
},
libimobiledevice: {
versionCheck: 'brew list --versions |grep libimobiledevice',
versionValidate:
(result, version) => result.includes(version)
},
'deviceconsole': {
versionCheck: 'npm list --depth=0 -g |grep deviceconsole',
versionValidate:
(result, version) => result.includes(version)
},
'check-engine': {
versionCheck: 'npm list --depth=0 -g |grep check-engine',
versionValidate:
(result, version) => result.includes(version)
},
yarn: {
versionCheck: 'yarn -v',
versionValidate:
(result, version) => semver.satisfies(result, version)
},
nsp: {
versionCheck: 'nsp --version',
versionValidate:
(result, version) => version === result.trim()
}
}

View File

@@ -1,16 +1,32 @@
import Mock from 'mockjs'
import mocks from './mocks'
import { param2Obj } from '../src/utils'
const MOCK_API_BASE = '/mock'
import user from './user'
import role from './role'
import article from './article'
import search from './remoteSearch'
const mocks = [
...user,
...role,
...article,
...search
]
// for front mock
// please use it cautiously, it will redefine XMLHttpRequest,
// which will cause many of your third-party libraries to be invalidated(like progress event).
export function mockXHR() {
// 修复在使用 MockJS 情况下,设置 withCredentials = true且未被拦截的跨域请求丢失 Cookies 的问题
// mock patch
// 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)
}
@@ -38,9 +54,10 @@ export function mockXHR() {
}
}
// for mock server
const responseFake = (url, type, respond) => {
return {
url: new RegExp(`${MOCK_API_BASE}${url}`),
url: new RegExp(`/mock${url}`),
type: type || 'get',
response(req, res) {
res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond))

62
mock/mock-server.js Normal file
View File

@@ -0,0 +1,62 @@
const chokidar = require('chokidar')
const bodyParser = require('body-parser')
const chalk = require('chalk')
function registerRoutes(app) {
let mockStartIndex
const { default: mocks } = require('./index.js')
for (const mock of mocks) {
app[mock.type](mock.url, mock.response)
mockStartIndex = app._router.stack.length
}
const mockRoutesLength = Object.keys(mocks).length
return {
mockRoutesLength: mockRoutesLength,
mockStartIndex: mockStartIndex - mockRoutesLength
}
}
function unregisterRoutes() {
Object.keys(require.cache).forEach(i => {
if (i.includes('/mock')) {
delete require.cache[require.resolve(i)]
}
})
}
module.exports = app => {
// es6 polyfill
require('@babel/register')
// parse app.body
// http://expressjs.com/en/4x/api.html#req.body
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({
extended: true
}))
const mockRoutes = registerRoutes(app)
var mockRoutesLength = mockRoutes.mockRoutesLength
var mockStartIndex = mockRoutes.mockStartIndex
// watch files, hot reload mock server
chokidar.watch(('./mock'), {
ignored: 'mock/mock-server.js',
persistent: true,
ignoreInitial: true
}).on('all', (event, path) => {
if (event === 'change' || event === 'add') {
// remove mock routes stack
app._router.stack.splice(mockStartIndex, mockRoutesLength)
// clear routes cache
unregisterRoutes()
const mockRoutes = registerRoutes(app)
mockRoutesLength = mockRoutes.mockRoutesLength
mockStartIndex = mockRoutes.mockStartIndex
console.log(chalk.magentaBright(`\n > Mock Server hot reload success! changed ${path}`))
}
})
}

View File

@@ -1,12 +0,0 @@
import user from './user'
import role from './role'
import article from './article'
import search from './remoteSearch'
export default [
...user,
...role,
...article,
...search
]

View File

@@ -19,7 +19,7 @@ export const constantRoutes = [
},
{
path: '/auth-redirect',
component: 'views/login/authredirect',
component: 'views/login/authRedirect',
hidden: true
},
{

View File

@@ -30,9 +30,19 @@ export default [
type: 'post',
response: config => {
const { username } = config.body
const token = tokens[username]
// mock error
if (!token) {
return {
code: 60204,
message: 'Account and password are incorrect.'
}
}
return {
code: 20000,
data: tokens[username]
data: token
}
}
},
@@ -43,9 +53,19 @@ export default [
type: 'get',
response: config => {
const { token } = config.query
const info = users[token]
// mock error
if (!info) {
return {
code: 50008,
message: 'Login failed, unable to get user details.'
}
}
return {
code: 20000,
data: users[token]
data: info
}
}
},

View File

@@ -1,18 +1,17 @@
{
"name": "vue-element-admin",
"version": "4.0.0",
"description": "A magical vue admin. Typical templates for enterprise applications. Newest development stack of vue. Lots of awesome features",
"version": "4.0.1",
"description": "A magical vue admin. An out-of-box UI solution for enterprise applications. Newest development stack of vue. Lots of awesome features",
"author": "Pan <panfree23@gmail.com>",
"license": "MIT",
"scripts": {
"dev": "vue-cli-service serve",
"build:prod": "vue-cli-service build",
"build:stage": "vue-cli-service build --mode staging",
"build:preview": "node build/index.js --preview",
"build:report": "node build/index.js --report",
"preview": "node build/index.js --preview",
"lint": "eslint --ext .js,.vue src",
"test": "npm run lint",
"test:unit": "vue-cli-service test:unit",
"test:ci": "npm run lint && npm run test:unit",
"svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml",
"new": "plop"
},
@@ -29,10 +28,12 @@
},
"keywords": [
"vue",
"element-ui",
"admin",
"management-system",
"admin-template"
"dashboard",
"element-ui",
"boilerplate",
"admin-template",
"management-system"
],
"repository": {
"type": "git",
@@ -43,49 +44,52 @@
},
"dependencies": {
"axios": "0.18.0",
"clipboard": "1.7.1",
"codemirror": "5.44.0",
"clipboard": "2.0.4",
"codemirror": "5.45.0",
"driver.js": "0.9.5",
"dropzone": "5.5.1",
"echarts": "4.1.0",
"element-ui": "2.6.1",
"echarts": "4.2.1",
"element-ui": "2.7.0",
"file-saver": "2.0.1",
"fuse.js": "3.4.4",
"js-cookie": "2.2.0",
"jsonlint": "1.6.3",
"jszip": "3.2.0",
"jszip": "3.2.1",
"normalize.css": "7.0.0",
"nprogress": "0.2.0",
"path-to-regexp": "2.4.0",
"screenfull": "4.0.1",
"screenfull": "4.2.0",
"showdown": "1.9.0",
"sortablejs": "1.8.3",
"tui-editor": "1.3.2",
"vue": "2.6.8",
"sortablejs": "1.8.4",
"tui-editor": "1.3.3",
"vue": "2.6.10",
"vue-count-to": "1.0.13",
"vue-i18n": "7.3.2",
"vue-router": "3.0.2",
"vue-splitpane": "1.0.2",
"vuedraggable": "2.17.0",
"vue-splitpane": "1.0.4",
"vuedraggable": "2.20.0",
"vuex": "3.1.0",
"xlsx": "0.14.1"
},
"devDependencies": {
"@babel/core": "7.0.0",
"@babel/register": "7.0.0",
"@vue/cli-plugin-babel": "3.5.0",
"@vue/cli-plugin-unit-jest": "3.5.0",
"@vue/cli-service": "3.5.0",
"@vue/cli-plugin-babel": "3.5.3",
"@vue/cli-plugin-eslint": "3.5.1",
"@vue/cli-plugin-unit-jest": "3.5.3",
"@vue/cli-service": "3.5.3",
"@vue/test-utils": "1.0.0-beta.29",
"babel-core": "7.0.0-bridge.0",
"babel-eslint": "10.0.1",
"babel-jest": "23.6.0",
"chalk": "2.4.2",
"chokidar": "2.1.5",
"connect": "3.6.6",
"eslint": "5.15.1",
"eslint": "5.15.3",
"eslint-plugin-vue": "5.2.2",
"html-webpack-plugin": "3.2.0",
"husky": "1.3.1",
"lint-staged": "7.2.2",
"lint-staged": "8.1.5",
"mockjs": "1.0.1-beta3",
"node-sass": "^4.9.0",
"plop": "2.3.0",
@@ -96,7 +100,7 @@
"serve-static": "^1.13.2",
"svg-sprite-loader": "4.1.3",
"svgo": "1.2.0",
"vue-template-compiler": "2.6.8"
"vue-template-compiler": "2.6.10"
},
"engines": {
"node": ">=8.9",

View File

@@ -1,6 +1,6 @@
import request from '@/utils/request'
export function userSearch(name) {
export function searchUser(name) {
return request({
url: '/search/user',
method: 'get',

View File

@@ -88,29 +88,29 @@ export default {
</script>
<style scoped>
.back-to-ceiling {
.back-to-ceiling {
position: fixed;
display: inline-block;
text-align: center;
cursor: pointer;
}
}
.back-to-ceiling:hover {
.back-to-ceiling:hover {
background: #d5dbe7;
}
}
.fade-enter-active,
.fade-leave-active {
.fade-enter-active,
.fade-leave-active {
transition: opacity .5s;
}
}
.fade-enter,
.fade-leave-to {
.fade-enter,
.fade-leave-to {
opacity: 0
}
}
.back-to-ceiling .Icon {
.back-to-ceiling .Icon {
fill: #9aaabf;
background: none;
}
}
</style>

View File

@@ -58,15 +58,16 @@ export default {
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.app-breadcrumb.el-breadcrumb {
<style lang="scss" scoped>
.app-breadcrumb.el-breadcrumb {
display: inline-block;
font-size: 14px;
line-height: 50px;
margin-left: 8px;
.no-redirect {
color: #97a8be;
cursor: text;
}
}
}
</style>

View File

@@ -2,7 +2,7 @@
<div class="dndList">
<div :style="{width:width1}" class="dndList-list">
<h3>{{ list1Title }}</h3>
<draggable :list="list1" :options="{group:'article'}" class="dragArea">
<draggable :set-data="setData" :list="list1" group="article" class="dragArea">
<div v-for="element in list1" :key="element.id" class="list-complete-item">
<div class="list-complete-item-handle">
{{ element.id }}[{{ element.author }}] {{ element.title }}
@@ -17,7 +17,7 @@
</div>
<div :style="{width:width2}" class="dndList-list">
<h3>{{ list2Title }}</h3>
<draggable :list="list2" :options="{group:'article'}" class="dragArea">
<draggable :list="list2" 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 }}
@@ -94,12 +94,17 @@ export default {
if (this.isNotInList1(ele)) {
this.list1.push(ele)
}
},
setData(dataTransfer) {
// to avoid Firefox bug
// Detail see : https://github.com/RubaXa/Sortable/issues/1012
dataTransfer.setData('Text', '')
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
<style lang="scss" scoped>
.dndList {
background: #fff;
padding-bottom: 40px;

View File

@@ -237,7 +237,7 @@ export default {
.dropzone .dz-preview:hover .dz-image img {
transform: none;
-webkit-filter: none;
filter: none;
width: 100%;
height: 100%;
}

View File

@@ -6,28 +6,28 @@
</el-button>
</el-badge>
<el-dialog :visible.sync="dialogTableVisible" title="Error Log" width="80%">
<el-dialog :visible.sync="dialogTableVisible" title="Error Log" width="80%" append-to-body>
<el-table :data="errorLogs" border>
<el-table-column label="Message">
<template slot-scope="scope">
<template slot-scope="{row}">
<div>
<span class="message-title">Msg:</span>
<el-tag type="danger">
{{ scope.row.err.message }}
{{ row.err.message }}
</el-tag>
</div>
<br>
<div>
<span class="message-title" style="padding-right: 10px;">Info: </span>
<el-tag type="warning">
{{ scope.row.vm.$vnode.tag }} error in {{ scope.row.info }}
{{ row.vm.$vnode.tag }} error in {{ row.info }}
</el-tag>
</div>
<br>
<div>
<span class="message-title" style="padding-right: 16px;">Url: </span>
<el-tag type="success">
{{ scope.row.url }}
{{ row.url }}
</el-tag>
</div>
</template>

View File

@@ -5,8 +5,9 @@
</div>
<draggable
:list="list"
:options="options"
v-bind="$attrs"
class="board-column-content"
:set-data="setData"
>
<div v-for="element in list" :key="element.id" class="board-item">
{{ element.name }} {{ element.id }}
@@ -39,6 +40,13 @@ export default {
return []
}
}
},
methods: {
setData(dataTransfer) {
// to avoid Firefox bug
// Detail see : https://github.com/RubaXa/Sortable/issues/1012
dataTransfer.setData('Text', '')
}
}
}
</script>

View File

@@ -197,7 +197,7 @@ export default {
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
<style lang="scss" scoped>
// Fonts:
$font-size-base: 16px;
$font-size-small: 18px;

View File

@@ -2,7 +2,9 @@
<div ref="rightPanel" :class="{show:show}" class="rightPanel-container">
<div class="rightPanel-background" />
<div class="rightPanel">
<el-button class="handle-button" :style="{'top':buttonTop+'px'}" type="primary" circle :icon="show?'el-icon-close':'el-icon-setting'" @click="show=!show" />
<div class="handle-button" :style="{'top':buttonTop+'px','background-color':theme}" @click="show=!show">
<i :class="show?'el-icon-close':'el-icon-setting'" />
</div>
<div class="rightPanel-items">
<slot />
</div>
@@ -21,7 +23,7 @@ export default {
type: Boolean
},
buttonTop: {
default: 240,
default: 250,
type: Number
}
},
@@ -30,6 +32,11 @@ export default {
show: false
}
},
computed: {
theme() {
return this.$store.state.settings.theme
}
},
watch: {
show(value) {
if (value && !this.clickNotClose) {
@@ -77,7 +84,7 @@ export default {
}
</style>
<style rel="stylesheet/scss" lang="scss" scoped>
<style lang="scss" scoped>
.rightPanel-background {
opacity: 0;
transition: opacity .3s cubic-bezier(.7, .3, .1, 1);
@@ -108,7 +115,6 @@ export default {
}
.show {
transition: all .3s cubic-bezier(.7, .3, .1, 1);
.rightPanel-background {
@@ -131,7 +137,16 @@ export default {
height: 48px;
pointer-events: auto;
z-index: 0;
cursor: pointer;
pointer-events: auto;
font-size: 24px;
text-align: center;
color: #fff;
line-height: 48px;
i {
font-size: 24px;
line-height: 48px;
}
}
</style>

View File

@@ -17,6 +17,9 @@ export default {
mounted() {
this.init()
},
beforeDestroy() {
this.destroy()
},
methods: {
click() {
if (!screenfull.enabled) {
@@ -28,11 +31,17 @@ export default {
}
screenfull.toggle()
},
change() {
this.isFullscreen = screenfull.isFullscreen
},
init() {
if (screenfull.enabled) {
screenfull.on('change', () => {
this.isFullscreen = screenfull.isFullscreen
})
screenfull.on('change', this.change)
}
},
destroy() {
if (screenfull.enabled) {
screenfull.off('change', this.change)
}
}
}

View File

@@ -37,7 +37,7 @@ export default {
}
</script>
<style rel="stylesheet/scss" lang="scss" >
<style lang="scss" >
$n: 8; //和items.length 相同
$t: .1s;
.share-dropdown-menu {

View File

@@ -1,7 +1,7 @@
<template>
<el-color-picker
v-model="theme"
:predefine="['#409EFF', '#11a983', '#13c2c2', '#6959CD', '#f5222d', '#eb2f96', '#DB7093', '#e6a23c', '#8B8989', '#212121']"
:predefine="['#409EFF', '#1890ff', '#304156','#212121','#11a983', '#13c2c2', '#6959CD', '#f5222d', ]"
class="theme-picker"
popper-class="theme-picker-dropdown"
/>
@@ -11,17 +11,18 @@
const version = require('element-ui/package.json').version // element-ui version from node_modules
const ORIGINAL_THEME = '#409EFF' // default color
import defaultSettings from '@/settings'
export default {
data() {
return {
chalk: '', // content of theme-chalk css
theme: ORIGINAL_THEME
theme: defaultSettings.theme
}
},
watch: {
async theme(val) {
const oldVal = this.theme
const oldVal = this.chalk ? this.theme : ORIGINAL_THEME
if (typeof val !== 'string') return
const themeCluster = this.getThemeCluster(val.replace('#', ''))
const originalCluster = this.getThemeCluster(oldVal.replace('#', ''))
@@ -70,6 +71,8 @@ export default {
style.innerText = this.updateStyle(innerText, originalCluster, themeCluster)
})
this.$emit('change', val)
$message.close()
}
},

View File

@@ -101,7 +101,7 @@ export default {
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
<style lang="scss" scoped>
.editor-slide-upload {
margin-bottom: 20px;
/deep/ .el-upload--picture-card {

View File

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

View File

@@ -1,48 +0,0 @@
import Vue from 'vue'
// Flattened array
export default function treeToArray(data, children = 'children') {
let tmp = []
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
}

View File

@@ -1,193 +0,0 @@
<template>
<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">
<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>
</template>
<script>
import treeToArray, { addAttrs } from './eval.js'
export default {
name: 'TreeTable',
props: {
data: {
type: Array,
required: true,
default: () => []
},
columns: {
type: Array,
default: () => []
},
defaultExpandAll: {
type: Boolean,
default: false
},
defaultChildren: {
type: String,
default: 'children'
},
indent: {
type: Number,
default: 50
}
},
data() {
return {
guard: 1
}
},
computed: {
children() {
return this.defaultChildren
},
tableData() {
const data = this.data
if (this.data.length === 0) {
return []
}
addAttrs(data, {
expand: this.defaultExpandAll,
children: this.defaultChildren
})
const retval = treeToArray(data, this.defaultChildren)
return retval
}
},
methods: {
addBrother(row, data) {
if (row._parent) {
row._parent.children.push(data)
} else {
this.data.push(data)
}
},
addChild(row, data) {
if (!row.children) {
this.$set(row, 'children', [])
}
row.children.push(data)
},
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>
@keyframes treeTableShow {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@-webkit-keyframes treeTableShow {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.tree-ctrl {
position: relative;
cursor: pointer;
color: #2196f3;
}
</style>

View File

@@ -78,7 +78,7 @@ export default {
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
<style lang="scss" scoped>
@import "~@/styles/mixin.scss";
.upload-container {
width: 100%;

View File

@@ -76,7 +76,7 @@ export default {
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
<style lang="scss" scoped>
.upload-container {
width: 100%;
height: 100%;

View File

@@ -85,7 +85,7 @@ export default {
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
<style lang="scss" scoped>
@import "~@/styles/mixin.scss";
.upload-container {
width: 100%;

View File

@@ -1,7 +1,6 @@
export default {
route: {
dashboard: 'Dashboard',
introduction: 'Introduction',
documentation: 'Documentation',
guide: 'Guide',
permission: 'Permission',
@@ -10,7 +9,6 @@ export default {
directivePermission: 'Directive Permission',
icons: 'Icons',
components: 'Components',
componentIndex: 'Introduction',
tinymce: 'Tinymce',
markdown: 'Markdown',
jsonEditor: 'JSON Editor',
@@ -19,9 +17,9 @@ export default {
avatarUpload: 'Avatar Upload',
dropzone: 'Dropzone',
sticky: 'Sticky',
countTo: 'CountTo',
countTo: 'Count To',
componentMixin: 'Mixin',
backToTop: 'BackToTop',
backToTop: 'Back To Top',
dragDialog: 'Drag Dialog',
dragSelect: 'Drag Select',
dragKanban: 'Drag Kanban',
@@ -43,8 +41,6 @@ export default {
dragTable: 'Drag Table',
inlineEditTable: 'Inline Edit',
complexTable: 'Complex Table',
treeTable: 'Tree Table',
customTreeTable: 'Custom TreeTable',
tab: 'Tab',
form: 'Form',
createArticle: 'Create Article',
@@ -76,7 +72,7 @@ export default {
},
login: {
title: 'Login Form',
logIn: 'Log in',
logIn: 'Login',
username: 'Username',
password: 'Password',
any: 'any',
@@ -89,10 +85,10 @@ export default {
},
permission: {
addRole: 'New Role',
editPermission: 'Edit Permission',
editPermission: 'Edit',
roles: 'Your 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.',
tips: 'In some cases, using v-permission will have no effect. For example: Element-UI el-tab or el-table-column and other scenes that dynamically render dom. You can only do this with v-if.',
delete: 'Delete',
confirm: 'Confirm',
cancel: 'Cancel'
@@ -103,7 +99,7 @@ export default {
},
components: {
documentation: 'Documentation',
tinymceTips: 'Rich text editor is a core part of management system, but at the same time is a place with lots of problems. In the process of selecting rich texts, I also walked a lot of detours. The common rich text editors in the market are basically used, and the finally chose Tinymce. See documentation for more detailed rich text editor comparisons and introductions.',
tinymceTips: 'Rich text is a core feature of the management backend, but at the same time it is a place with lots of pits. In the process of selecting rich texts, I also took a lot of detours. The common rich texts on the market have been basically used, and I finally chose Tinymce. See the more detailed rich text comparison and introduction.',
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',
@@ -144,14 +140,14 @@ export default {
excel: {
export: 'Export',
selectedExport: 'Export Selected Items',
placeholder: 'Please enter the file name(default excel-list)'
placeholder: 'Please enter the file name (default excel-list)'
},
zip: {
export: 'Export',
placeholder: 'Please enter the file name(default file)'
placeholder: 'Please enter the file name (default file)'
},
pdf: {
tips: 'Here we use window.print() to implement the feature of downloading pdf.'
tips: 'Here we use window.print() to implement the feature of downloading PDF.'
},
theme: {
change: 'Change Theme',
@@ -163,5 +159,12 @@ export default {
close: 'Close',
closeOthers: 'Close Others',
closeAll: 'Close All'
},
settings: {
title: 'Page style setting',
theme: 'Theme Color',
tagsView: 'Open Tags-View',
fixedHeader: 'Fixed Header',
sidebarLogo: 'Sidebar Logo'
}
}

View File

@@ -1,7 +1,6 @@
export default {
route: {
dashboard: 'Panel de control',
introduction: 'Introducción',
documentation: 'Documentación',
guide: 'Guía',
permission: 'Permisos',
@@ -10,7 +9,6 @@ export default {
directivePermission: 'Permisos de la directiva',
icons: 'Iconos',
components: 'Componentes',
componentIndex: 'Introducción',
tinymce: 'Tinymce',
markdown: 'Markdown',
jsonEditor: 'Editor JSON',
@@ -43,8 +41,6 @@ export default {
dragTable: 'Arrastrar tabla',
inlineEditTable: 'Editor',
complexTable: 'Complex Table',
treeTable: 'Tree Table',
customTreeTable: 'Custom TreeTable',
tab: 'Pestaña',
form: 'Formulario',
createArticle: 'Crear artículo',
@@ -163,5 +159,12 @@ export default {
close: 'Cerrar',
closeOthers: 'Cerrar otros',
closeAll: 'Cerrar todos'
},
settings: {
title: 'Page style setting',
theme: 'Theme Color',
tagsView: 'Open Tags-View',
fixedHeader: 'Fixed Header',
sidebarLogo: 'Sidebar Logo'
}
}

View File

@@ -24,11 +24,24 @@ const messages = {
...elementEsLocale
}
}
export function getLanguage() {
const chooseLanguage = Cookies.get('language')
if (chooseLanguage) return chooseLanguage
// if has not choose language
const language = (navigator.language || navigator.browserLanguage).toLowerCase()
const locales = Object.keys(messages)
for (const locale of locales) {
if (language.indexOf(locale) > -1) {
return locale
}
}
return 'en'
}
const i18n = new VueI18n({
// set locale
// options: en | zh | es
locale: Cookies.get('language') || 'en',
locale: getLanguage(),
// set locale messages
messages
})

View File

@@ -1,7 +1,6 @@
export default {
route: {
dashboard: '首页',
introduction: '简述',
documentation: '文档',
guide: '引导页',
permission: '权限测试页',
@@ -10,16 +9,15 @@ export default {
directivePermission: '指令权限',
icons: '图标',
components: '组件',
componentIndex: '介绍',
tinymce: '富文本编辑器',
markdown: 'Markdown',
jsonEditor: 'JSON编辑器',
jsonEditor: 'JSON 编辑器',
dndList: '列表拖拽',
splitPane: 'Splitpane',
avatarUpload: '头像上传',
dropzone: 'Dropzone',
sticky: 'Sticky',
countTo: 'CountTo',
countTo: 'Count To',
componentMixin: '小组件',
backToTop: '返回顶部',
dragDialog: '拖拽 Dialog',
@@ -32,19 +30,17 @@ export default {
example: '综合实例',
nested: '路由嵌套',
menu1: '菜单1',
'menu1-1': '菜单1-1',
'menu1-2': '菜单1-2',
'menu1-2-1': '菜单1-2-1',
'menu1-2-2': '菜单1-2-2',
'menu1-3': '菜单1-3',
menu2: '菜单2',
'menu1-1': '菜单 1-1',
'menu1-2': '菜单 1-2',
'menu1-2-1': '菜单 1-2-1',
'menu1-2-2': '菜单 1-2-2',
'menu1-3': '菜单 1-3',
menu2: '菜单 2',
Table: 'Table',
dynamicTable: '动态Table',
dragTable: '拖拽Table',
inlineEditTable: 'Table内编辑',
complexTable: '综合Table',
treeTable: '树形表格',
customTreeTable: '自定义树表',
dynamicTable: '动态 Table',
dragTable: '拖拽 Table',
inlineEditTable: 'Table 内编辑',
complexTable: '综合 Table',
tab: 'Tab',
form: '表单',
createArticle: '创建文章',
@@ -92,7 +88,7 @@ export default {
editPermission: '编辑权限',
roles: '你的权限',
switchRoles: '切换权限',
tips: '在某些情况下,不适合使用 v-permission。例如Element-UI 的 Tab 组件或 el-table-column 以及其它动态渲染 dom 的场景。你只能通过手动设置 v-if 来实现。',
tips: '在某些情况下,不适合使用 v-permission。例如Element-UI 的 el-tab 或 el-table-column 以及其它动态渲染 dom 的场景。你只能通过手动设置 v-if 来实现。',
delete: '删除',
confirm: '确定',
cancel: '取消'
@@ -163,5 +159,12 @@ export default {
close: '关闭',
closeOthers: '关闭其它',
closeAll: '关闭所有'
},
settings: {
title: '系统布局配置',
theme: '主题色',
tagsView: '开启 Tags-View',
fixedHeader: '固定 Header',
sidebarLogo: '侧边栏 Logo'
}
}

View File

@@ -22,9 +22,9 @@ export default {
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
<style lang="scss" scoped>
.app-main {
/*50= navbar 50 */
/* 50= navbar 50 */
min-height: calc(100vh - 50px);
width: 100%;
position: relative;
@@ -32,17 +32,17 @@ export default {
}
.fixed-header+.app-main {
margin-top: 50px;
padding-top: 50px;
}
.hasTagsView {
.app-main {
/*84 = navbar + tags-view = 50 + 34 */
/* 84 = navbar + tags-view = 50 + 34 */
min-height: calc(100vh - 84px);
}
.fixed-header+.app-main {
margin-top: 80px;
padding-top: 84px;
}
}
</style>

View File

@@ -1,19 +1,19 @@
<template>
<div class="navbar">
<hamburger :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
<hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
<breadcrumb class="breadcrumb-container" />
<breadcrumb id="breadcrumb-container" class="breadcrumb-container" />
<div class="right-menu">
<template v-if="device!=='mobile'">
<search class="right-menu-item" />
<search id="header-search" class="right-menu-item" />
<error-log class="errLog-container right-menu-item hover-effect" />
<screenfull class="right-menu-item hover-effect" />
<screenfull id="screenfull" class="right-menu-item hover-effect" />
<el-tooltip :content="$t('navbar.size')" effect="dark" placement="bottom">
<size-select class="right-menu-item hover-effect" />
<size-select id="size-select" class="right-menu-item hover-effect" />
</el-tooltip>
<lang-select class="right-menu-item hover-effect" />
@@ -85,13 +85,7 @@ export default {
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.hasTagsView {
.navbar {
border-bottom: none;
}
}
<style lang="scss" scoped>
.navbar {
height: 50px;
overflow: hidden;
@@ -105,6 +99,7 @@ export default {
float: left;
cursor: pointer;
transition: background .3s;
-webkit-tap-highlight-color:transparent;
&:hover {
background: rgba(0, 0, 0, .025)

View File

@@ -1,25 +1,28 @@
<template>
<div class="drawer-container">
<div>
<h3 class="drawer-title">
系统布局配置
</h3>
<h3 class="drawer-title">{{ $t('settings.title') }}</h3>
<div class="drawer-item">
<span>主题色</span>
<theme-picker style="float: right;height: 26px;margin: -3px 5px 0 0;" />
<span>{{ $t('settings.theme') }}</span>
<theme-picker style="float: right;height: 26px;margin: -3px 8px 0 0;" @change="themeChange" />
</div>
<div class="drawer-item">
<span>开启 Tags-View</span>
<span>{{ $t('settings.tagsView') }}</span>
<el-switch v-model="tagsView" class="drawer-switch" />
</div>
<div class="drawer-item">
<span>固定 Header</span>
<span>{{ $t('settings.fixedHeader') }}</span>
<el-switch v-model="fixedHeader" class="drawer-switch" />
</div>
<div class="drawer-item">
<span>{{ $t('settings.sidebarLogo') }}</span>
<el-switch v-model="sidebarLogo" class="drawer-switch" />
</div>
</div>
</div>
</template>
@@ -30,9 +33,7 @@ import ThemePicker from '@/components/ThemePicker'
export default {
components: { ThemePicker },
data() {
return {
sidebarLogo: true
}
return {}
},
computed: {
fixedHeader: {
@@ -56,12 +57,31 @@ export default {
value: val
})
}
},
sidebarLogo: {
get() {
return this.$store.state.settings.sidebarLogo
},
set(val) {
this.$store.dispatch('settings/changeSetting', {
key: 'sidebarLogo',
value: val
})
}
}
},
methods: {
themeChange(val) {
this.$store.dispatch('settings/changeSetting', {
key: 'theme',
value: val
})
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
<style lang="scss" scoped>
.drawer-container {
padding: 24px;
font-size: 14px;

View File

@@ -0,0 +1,82 @@
<template>
<div class="sidebar-logo-container" :class="{'collapse':collapse}">
<transition name="sidebarLogoFade">
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
<img v-if="logo" :src="logo" class="sidebar-logo">
<h1 v-else class="sidebar-title">{{ title }} </h1>
</router-link>
<router-link v-else key="expand" class="sidebar-logo-link" to="/">
<img v-if="logo" :src="logo" class="sidebar-logo">
<h1 class="sidebar-title">{{ title }} </h1>
</router-link>
</transition>
</div>
</template>
<script>
export default {
name: 'SidebarLogo',
props: {
collapse: {
type: Boolean,
required: true
}
},
data() {
return {
title: 'Vue Element Admin',
logo: 'https://wpimg.wallstcn.com/69a1c46c-eb1c-4b46-8bd4-e9e686ef5251.png'
}
}
}
</script>
<style lang="scss" scoped>
.sidebarLogoFade-enter-active {
transition: opacity 1.5s;
}
.sidebarLogoFade-enter,
.sidebarLogoFade-leave-to {
opacity: 0;
}
.sidebar-logo-container {
position: relative;
width: 100%;
height: 50px;
line-height: 50px;
background: #2b2f3a;
text-align: center;
overflow: hidden;
& .sidebar-logo-link {
height: 100%;
width: 100%;
& .sidebar-logo {
width: 32px;
height: 32px;
vertical-align: middle;
margin-right: 12px;
}
& .sidebar-title {
display: inline-block;
margin: 0;
color: #fff;
font-weight: 600;
line-height: 50px;
font-size: 14px;
font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
vertical-align: middle;
}
}
&.collapse {
.sidebar-logo {
margin-right: 0px;
}
}
}
</style>

View File

@@ -1,9 +1,9 @@
<template>
<div v-if="!item.hidden" class="menu-wrapper">
<template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow">
<app-link :to="resolvePath(onlyOneChild.path)">
<app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)">
<el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}">
<item v-if="onlyOneChild.meta" :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="generateTitle(onlyOneChild.meta.title)" />
<item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="generateTitle(onlyOneChild.meta.title)" />
</el-menu-item>
</app-link>
</template>
@@ -86,6 +86,9 @@ export default {
if (isExternal(routePath)) {
return routePath
}
if (isExternal(this.basePath)) {
return this.basePath
}
return path.resolve(this.basePath, routePath)
},

View File

@@ -1,7 +1,9 @@
<template>
<div :class="{'has-logo':showLogo}">
<logo v-if="showLogo" :collapse="isCollapse" />
<el-scrollbar wrap-class="scrollbar-wrapper">
<el-menu
:default-active="$route.path"
:default-active="activeMenu"
:collapse="isCollapse"
:background-color="variables.menuBg"
:text-color="variables.menuText"
@@ -12,20 +14,34 @@
<sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path" />
</el-menu>
</el-scrollbar>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import Logo from './Logo'
import SidebarItem from './SidebarItem'
import variables from '@/styles/variables.scss'
export default {
components: { SidebarItem },
components: { SidebarItem, Logo },
computed: {
...mapGetters([
'permission_routes',
'sidebar'
]),
activeMenu() {
const route = this.$route
const { meta, path } = route
// if set path, the sidebar will highlight the path you set
if (meta.activeMenu) {
return meta.activeMenu
}
return path
},
showLogo() {
return this.$store.state.settings.sidebarLogo
},
variables() {
return variables
},

View File

@@ -67,7 +67,7 @@ export default {
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
<style lang="scss" scoped>
.scroll-container {
white-space: nowrap;
position: relative;

View File

@@ -1,5 +1,5 @@
<template>
<div class="tags-view-container">
<div id="tags-view-container" class="tags-view-container">
<scroll-pane ref="scrollPane" class="tags-view-wrapper">
<router-link
v-for="tag in visitedViews"
@@ -145,7 +145,7 @@ export default {
closeSelectedTag(view) {
this.$store.dispatch('tagsView/delView', view).then(({ visitedViews }) => {
if (this.isActive(view)) {
this.toLastView(visitedViews)
this.toLastView(visitedViews, view)
}
})
},
@@ -160,17 +160,23 @@ export default {
if (this.affixTags.some(tag => tag.path === view.path)) {
return
}
this.toLastView(visitedViews)
this.toLastView(visitedViews, view)
})
},
toLastView(visitedViews) {
toLastView(visitedViews, view) {
const latestView = visitedViews.slice(-1)[0]
if (latestView) {
this.$router.push(latestView)
} else {
// You can set another route
// now the default is to redirect to the home page if there is no tags-view,
// you can adjust it according to your needs.
if (view.name === 'Dashboard') {
// to reload home page
this.$router.replace({ path: '/redirect' + view.fullPath })
} else {
this.$router.push('/')
}
}
},
openMenu(tag, e) {
const menuMinWidth = 105
@@ -196,7 +202,7 @@ export default {
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
<style lang="scss" scoped>
.tags-view-container {
height: 34px;
width: 100%;
@@ -243,7 +249,7 @@ export default {
.contextmenu {
margin: 0;
background: #fff;
z-index: 100;
z-index: 3000;
position: absolute;
list-style-type: none;
padding: 5px 0;
@@ -264,7 +270,7 @@ export default {
}
</style>
<style rel="stylesheet/scss" lang="scss">
<style lang="scss">
//reset element css of el-icon-close
.tags-view-wrapper {
.tags-view-item {

View File

@@ -57,7 +57,7 @@ export default {
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
<style lang="scss" scoped>
@import "~@/styles/mixin.scss";
@import "~@/styles/variables.scss";
@@ -66,11 +66,13 @@ export default {
position: relative;
height: 100%;
width: 100%;
&.mobile.openSidebar{
&.mobile.openSidebar {
position: fixed;
top: 0;
}
}
.drawer-bg {
background: #000;
opacity: 0.3;
@@ -80,7 +82,8 @@ export default {
position: absolute;
z-index: 999;
}
.fixed-header{
.fixed-header {
position: fixed;
top: 0;
right: 0;
@@ -88,10 +91,12 @@ export default {
width: calc(100% - #{$sideBarWidth});
transition: width 0.28s;
}
.hideSidebar .fixed-header{
.hideSidebar .fixed-header {
width: calc(100% - 54px)
}
.mobile .fixed-header{
.mobile .fixed-header {
width: 100%;
}
</style>

View File

@@ -14,6 +14,9 @@ export default {
beforeMount() {
window.addEventListener('resize', this.resizeHandler)
},
beforeDestroy() {
window.removeEventListener('resize', this.resizeHandler)
},
mounted() {
const isMobile = this.isMobile()
if (isMobile) {

View File

@@ -4,35 +4,40 @@ import Router from 'vue-router'
Vue.use(Router)
/* Layout */
import Layout from '@/layout/Layout'
import Layout from '@/layout'
/* Router Modules */
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: sub-menu only appear when children.length>=1
* detail see https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
**/
/**
* Note: sub-menu only appear when route children.length >= 1
* Detail see: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
*
* hidden: true if set true, item will not show in the sidebar(default is false)
* alwaysShow: true if set true, will always show the root menu
* if not set alwaysShow, when item has more than one children route,
* it will becomes nested mode, otherwise not show the root menu
* 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'] control the page roles (you can set multiple roles)
title: 'title' the name show in sidebar and breadcrumb (recommend set)
icon: 'svg-name' the icon show in the sidebar
noCache: true if set true, the page will no be cached(default is false)
affix: true if set true, the tag will affix in the tags-view
breadcrumb: false if set false, the item will hidden in breadcrumb(default is true)
activeMenu: '/example/list' if set path, the sidebar will highlight the path you set
}
*/
/**
* hidden: true if `hidden:true` will not show in the sidebar(default is false)
* alwaysShow: true if set true, will always show the root menu, whatever its child routes length
* if not set alwaysShow, only more than one route under the children
* it will becomes nested mode, otherwise not show the root menu
* 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 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
}
**/
* constantRoutes
* a base page that does not have permission requirements
* all roles can be accessed
*/
export const constantRoutes = [
{
path: '/redirect',
@@ -52,7 +57,7 @@ export const constantRoutes = [
},
{
path: '/auth-redirect',
component: () => import('@/views/login/authredirect'),
component: () => import('@/views/login/authRedirect'),
hidden: true
},
{
@@ -105,6 +110,10 @@ export const constantRoutes = [
}
]
/**
* asyncRoutes
* the routes that need to be dynamically loaded based on user roles
*/
export const asyncRoutes = [
{
path: '/permission',
@@ -160,12 +169,11 @@ export const asyncRoutes = [
]
},
/** When your routing table is too long, you can split it into small modules**/
/** when your routing map is too long, you can split it into small modules **/
componentsRouter,
chartsRouter,
nestedRouter,
tableRouter,
treeTableRouter,
{
path: '/example',
@@ -187,7 +195,7 @@ export const asyncRoutes = [
path: 'edit/:id(\\d+)',
component: () => import('@/views/example/edit'),
name: 'EditArticle',
meta: { title: 'editArticle', noCache: true },
meta: { title: 'editArticle', noCache: true, activeMenu: '/example/list' },
hidden: true
},
{

View File

@@ -1,6 +1,6 @@
/** When your routing table is too long, you can split it into small modules**/
import Layout from '@/layout/Layout'
import Layout from '@/layout'
const chartsRouter = {
path: '/charts',

View File

@@ -1,6 +1,6 @@
/** When your routing table is too long, you can split it into small modules**/
import Layout from '@/layout/Layout'
import Layout from '@/layout'
const componentsRouter = {
path: '/components',

View File

@@ -1,6 +1,6 @@
/** When your routing table is too long, you can split it into small modules**/
import Layout from '@/layout/Layout'
import Layout from '@/layout'
const nestedRouter = {
path: '/nested',

View File

@@ -1,6 +1,6 @@
/** When your routing table is too long, you can split it into small modules**/
import Layout from '@/layout/Layout'
import Layout from '@/layout'
const tableRouter = {
path: '/table',

View File

@@ -1,29 +0,0 @@
/** When your routing table is too long, you can split it into small modules**/
import Layout from '@/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

View File

@@ -1,5 +1,7 @@
import variables from '@/styles/element-variables.scss'
export default {
title: 'vue-element-admin',
theme: variables.theme,
/**
* @type {boolean} true | false
@@ -17,13 +19,19 @@ export default {
* @type {boolean} true | false
* @description Whether fix the header
*/
fixedHeader: true,
fixedHeader: false,
/**
* @type {string | array} 'production' | ['production','development']
* @type {boolean} true | false
* @description Whether show the logo in sidebar
*/
sidebarLogo: false,
/**
* @type {string | array} 'production' | ['production', 'development']
* @description Need show err logs component.
* The default is only used in the production env
* If you want to also use it in dev, you can pass ['production','development']
* If you want to also use it in dev, you can pass ['production', 'development']
*/
errorLog: 'production'
}

View File

@@ -1,24 +1,24 @@
import Vue from 'vue'
import Vuex from 'vuex'
import app from './modules/app'
import errorLog from './modules/errorLog'
import permission from './modules/permission'
import tagsView from './modules/tagsView'
import settings from './modules/settings'
import user from './modules/user'
import getters from './getters'
Vue.use(Vuex)
// https://webpack.js.org/guides/dependency-management/#requirecontext
const modulesFiles = require.context('./modules', false, /\.js$/)
// you do not need `import app from './modules/app'`
// it will auto require all vuex module from modules file
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
// set './app.js' => 'app'
const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
const value = modulesFiles(modulePath)
modules[moduleName] = value.default
return modules
}, {})
const store = new Vuex.Store({
modules: {
app,
errorLog,
permission,
tagsView,
settings,
user
},
modules,
getters
})

View File

@@ -1,4 +1,5 @@
import Cookies from 'js-cookie'
import { getLanguage } from '@/lang/index'
const state = {
sidebar: {
@@ -6,7 +7,7 @@ const state = {
withoutAnimation: false
},
device: 'desktop',
language: Cookies.get('language') || 'en',
language: getLanguage(),
size: Cookies.get('size') || 'medium'
}

View File

@@ -1,4 +1,3 @@
const state = {
logs: []
}

View File

@@ -1,10 +1,12 @@
import defaultSettings from '@/settings'
const { showSettings, tagsView, fixedHeader } = defaultSettings
const { showSettings, tagsView, fixedHeader, sidebarLogo, theme } = defaultSettings
const state = {
theme: theme,
showSettings: showSettings,
tagsView: tagsView,
fixedHeader: fixedHeader
fixedHeader: fixedHeader,
sidebarLogo: sidebarLogo
}
const mutations = {

View File

@@ -1,4 +1,3 @@
const state = {
visitedViews: [],
cachedViews: []

View File

@@ -23,3 +23,9 @@ $--table-border:1px solid#dfe6ec;
$--font-path: '~element-ui/lib/theme-chalk/fonts';
@import "~element-ui/packages/theme-chalk/src/index";
// the :export directive is the magic sauce for webpack
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
:export {
theme: $--color-primary;
}

View File

@@ -12,6 +12,7 @@
.sidebar-container {
transition: width 0.28s;
width: $sideBarWidth !important;
background-color: $menuBg;
height: 100%;
position: fixed;
font-size: 0px;
@@ -28,16 +29,22 @@
.scrollbar-wrapper {
overflow-x: hidden !important;
.el-scrollbar__view {
height: 100%;
}
}
.el-scrollbar__bar.is-vertical {
right: 0px;
}
.el-scrollbar {
height: 100%;
}
&.has-logo {
.el-scrollbar {
height: calc(100% - 50px);
}
}
.is-horizontal {
display: none;
}
@@ -100,6 +107,7 @@
.el-tooltip {
padding: 0 !important;
.svg-icon {
margin-left: 20px;
}
@@ -111,6 +119,7 @@
&>.el-submenu__title {
padding: 0 !important;
.svg-icon {
margin-left: 20px;
}

View File

@@ -1,4 +1,4 @@
//globl transition css
//global transition css
/*fade*/
.fade-enter-active,

View File

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

View File

@@ -26,10 +26,10 @@
<input v-model.number="setDuration" type="number" name="durationInput">
</label>
<div class="startBtn example-btn" @click="start">
开始
Start
</div>
<div class="pause-resume-btn example-btn" @click="pauseResume">
暂停/恢复
pause/resume
</div>
<br>
<label class="label" for="decimalsInput">decimals:

View File

@@ -1,8 +1,8 @@
<template>
<div class="components-container board">
<Kanban :key="1" :list="list1" :options="options" class="kanban todo" header-text="Todo" />
<Kanban :key="2" :list="list2" :options="options" class="kanban working" header-text="Working" />
<Kanban :key="3" :list="list3" :options="options" class="kanban done" header-text="Done" />
<Kanban :key="1" :list="list1" :group="group" class="kanban todo" header-text="Todo" />
<Kanban :key="2" :list="list2" :group="group" class="kanban working" header-text="Working" />
<Kanban :key="3" :list="list3" :group="group" class="kanban done" header-text="Done" />
</div>
</template>
<script>
@@ -15,9 +15,7 @@ export default {
},
data() {
return {
options: {
group: 'mission'
},
group: 'mission',
list1: [
{ name: 'Mission', id: 1 },
{ name: 'Mission', id: 2 },

View File

@@ -1,6 +1,10 @@
<template>
<div class="components-container">
<code>JsonEditor is base on <a href="https://github.com/codemirror/CodeMirror" target="_blank">CodeMirrorr</a> , lint base on json-lint </code>
<code>Json-Editor is base on <a href="https://github.com/codemirror/CodeMirror" target="_blank">CodeMirrorr</a>. Lint
base on <a
href="https://github.com/codemirror/CodeMirror/blob/master/addon/lint/json-lint.js"
target="_blank"
>json-lint</a>.</code>
<div class="editor-container">
<json-editor ref="jsonEditor" v-model="value" />
</div>

View File

@@ -1,8 +1,11 @@
<template>
<div class="components-container">
<code>Markdown is based on
<a href="https://github.com/nhnent/tui.editor" target="_blank">tui.editor</a> Simply encapsulated in Vue.
<a target="_blank" href="https://panjiachen.github.io/vue-element-admin-site/feature/component/markdown-editor.html">
<a href="https://github.com/nhnent/tui.editor" target="_blank">tui.editor</a> simply wrapped with Vue.
<a
target="_blank"
href="https://panjiachen.github.io/vue-element-admin-site/feature/component/markdown-editor.html"
>
Documentation </a>
</code>
@@ -10,33 +13,33 @@
<el-tag class="tag-title">
Basic:
</el-tag>
<markdown-editor v-model="content" height="300px" />
<markdown-editor v-model="content1" height="300px" />
</div>
<div class="editor-container">
<el-tag class="tag-title">
Markdown Mode:
</el-tag>
<markdown-editor ref="markdownEditor" v-model="content" :options="{hideModeSwitch:true,previewStyle:'tab'}" height="200px" />
<markdown-editor ref="markdownEditor" v-model="content2" :options="{hideModeSwitch:true,previewStyle:'tab'}" height="200px" />
</div>
<div class="editor-container">
<el-tag class="tag-title">
Customize Toolbar:
</el-tag>
<markdown-editor
ref="markdownEditor"
v-model="content"
:options="{ toolbarItems: ['heading','bold','italic']}"
/>
<markdown-editor v-model="content3" :options="{ toolbarItems: ['heading','bold','italic']}" />
</div>
<div class="editor-container">
<el-tag class="tag-title">
I18n:
</el-tag>
<el-alert :closable="false" title="You can change the language of the admin system to see the effect" type="success" />
<markdown-editor v-model="content" :language="language" height="300px" />
<el-alert
:closable="false"
title="You can change the language of the admin system to see the effect"
type="success"
/>
<markdown-editor ref="markdownEditor" v-model="content4" :language="language" height="300px" />
</div>
<el-button style="margin-top:80px;" type="primary" icon="el-icon-document" @click="getHtml">
@@ -62,7 +65,10 @@ export default {
components: { MarkdownEditor },
data() {
return {
content: content,
content1: content,
content2: content,
content3: content,
content4: content,
html: '',
languageTypeList: {
'en': 'en_US',

View File

@@ -1,6 +1,6 @@
<template>
<div>
<sticky class-name="sub-navbar">
<sticky :z-index="10" class-name="sub-navbar">
<el-dropdown trigger="click">
<el-button plain>
Platform<i class="el-icon-caret-bottom el-icon--right" />

View File

@@ -61,14 +61,14 @@ export default {
}
</script>
<style rel="stylesheet/scss" lang="scss" >
<style lang="scss" >
.box-card-component{
.el-card__header {
padding: 0px!important;
}
}
</style>
<style rel="stylesheet/scss" lang="scss" scoped>
<style lang="scss" scoped>
.box-card-component {
.box-card-header {
position: relative;

View File

@@ -70,7 +70,7 @@ export default {
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
<style lang="scss" scoped>
.panel-group {
margin-top: 18px;
.card-panel-col{

View File

@@ -11,9 +11,9 @@
</template>
</el-table-column>
<el-table-column label="Status" width="100" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.status | statusFilter">
{{ scope.row.status }}
<template slot-scope="{row}">
<el-tag :type="row.status | statusFilter">
{{ row.status }}
</el-tag>
</template>
</el-table-column>

View File

@@ -1,6 +1,6 @@
<template>
<div class="dashboard-editor-container">
<github-corner style="position: absolute; top: 0px; border: 0; right: 0;" />
<github-corner class="github-corner" />
<panel-group @handleSetLineChartData="handleSetLineChartData" />
@@ -96,10 +96,19 @@ export default {
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
<style lang="scss" scoped>
.dashboard-editor-container {
padding: 32px;
background-color: rgb(240, 242, 245);
position: relative;
.github-corner {
position: absolute;
top: 0px;
border: 0;
right: 0;
}
.chart-wrapper {
background: #fff;
padding: 16px 16px 0;

View File

@@ -40,7 +40,7 @@ export default {
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
<style lang="scss" scoped>
.emptyGif {
display: block;
width: 45%;

View File

@@ -29,7 +29,7 @@ export default {
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
<style lang="scss" scoped>
.documentation-container {
margin: 50px;
.document-btn {

View File

@@ -58,7 +58,7 @@ export default {
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
<style lang="scss" scoped>
.errPage-container {
width: 800px;
max-width: 100%;

View File

@@ -41,7 +41,7 @@ export default {
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
<style lang="scss" scoped>
.wscn-http404-container{
transform: translate(-50%,-50%);
position: absolute;

View File

@@ -1,7 +1,7 @@
<template>
<div class="createPost-container">
<el-form ref="postForm" :model="postForm" :rules="rules" class="form-container">
<sticky :class-name="'sub-navbar '+postForm.status">
<sticky :z-index="10" :class-name="'sub-navbar '+postForm.status">
<CommentDropdown v-model="postForm.comment_disabled" />
<PlatformDropdown v-model="postForm.platforms" />
<SourceUrlDropdown v-model="postForm.source_uri" />
@@ -28,7 +28,7 @@
<el-row>
<el-col :span="8">
<el-form-item label-width="45px" label="作者:" class="postInfo-container-item">
<el-select v-model="postForm.author" :remote-method="getRemoteUserList" filterable remote placeholder="搜索用户">
<el-select v-model="postForm.author" :remote-method="getRemoteUserList" filterable default-first-option remote placeholder="搜索用户">
<el-option v-for="(item,index) in userListOptions" :key="item+index" :label="item" :value="item" />
</el-select>
</el-form-item>
@@ -81,7 +81,7 @@ import MDinput from '@/components/MDinput'
import Sticky from '@/components/Sticky' // 粘性header组件
import { validURL } from '@/utils/validate'
import { fetchArticle } from '@/api/article'
import { userSearch } from '@/api/remoteSearch'
import { searchUser } from '@/api/remoteSearch'
import Warning from './Warning'
import { CommentDropdown, PlatformDropdown, SourceUrlDropdown } from './Dropdown'
@@ -225,7 +225,7 @@ export default {
this.postForm.status = 'draft'
},
getRemoteUserList(query) {
userSearch(query).then(response => {
searchUser(query).then(response => {
if (!response.data.items) return
this.userListOptions = response.data.items.map(v => v.name)
})
@@ -234,7 +234,7 @@ export default {
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
<style lang="scss" scoped>
@import "~@/styles/mixin.scss";
.createPost-container {
position: relative;

View File

@@ -26,17 +26,17 @@
</el-table-column>
<el-table-column class-name="status-col" label="Status" width="110">
<template slot-scope="scope">
<el-tag :type="scope.row.status | statusFilter">
{{ scope.row.status }}
<template slot-scope="{row}">
<el-tag :type="row.status | statusFilter">
{{ row.status }}
</el-tag>
</template>
</el-table-column>
<el-table-column min-width="300px" label="Title">
<template slot-scope="scope">
<router-link :to="'/example/edit/'+scope.row.id" class="link-type">
<span>{{ scope.row.title }}</span>
<template slot-scope="{row}">
<router-link :to="'/example/edit/'+row.id" class="link-type">
<span>{{ row.title }}</span>
</router-link>
</template>
</el-table-column>
@@ -95,14 +95,6 @@ export default {
this.total = response.data.total
this.listLoading = false
})
},
handleSizeChange(val) {
this.listQuery.limit = val
this.getList()
},
handleCurrentChange(val) {
this.listQuery.page = val
this.getList()
}
}
}

View File

@@ -2,7 +2,7 @@
<div style="display:inline-block;">
<!-- $t is vue-i18n global function to translate lang -->
<label class="radio-label" style="padding-left:0;">Filename: </label>
<el-input v-model="filename" :placeholder="$t('excel.placeholder')" style="width:340px;" prefix-icon="el-icon-document" />
<el-input v-model="filename" :placeholder="$t('excel.placeholder')" style="width:350px;" prefix-icon="el-icon-document" />
</div>
</template>

View File

@@ -1,7 +1,7 @@
<template>
<div class="app-container">
<!-- $t is vue-i18n global function to translate lang -->
<el-input v-model="filename" :placeholder="$t('excel.placeholder')" style="width:340px;" prefix-icon="el-icon-document" />
<el-input v-model="filename" :placeholder="$t('excel.placeholder')" style="width:350px;" prefix-icon="el-icon-document" />
<el-button :loading="downloadLoading" style="margin-bottom:20px" type="primary" icon="document" @click="handleDownload">
{{ $t('excel.selectedExport') }}
</el-button>

View File

@@ -1,6 +1,6 @@
const steps = [
{
element: '.hamburger-container',
element: '#hamburger-container',
popover: {
title: 'Hamburger',
description: 'Open && Close sidebar',
@@ -8,7 +8,7 @@ const steps = [
}
},
{
element: '.breadcrumb-container',
element: '#breadcrumb-container',
popover: {
title: 'Breadcrumb',
description: 'Indicate the current page location',
@@ -16,31 +16,31 @@ const steps = [
}
},
{
element: '.screenfull',
element: '#header-search',
popover: {
title: 'Page Search',
description: 'Page search, quick navigation',
position: 'left'
}
},
{
element: '#screenfull',
popover: {
title: 'Screenfull',
description: 'Bring the page into fullscreen',
description: 'Set the page into fullscreen',
position: 'left'
}
},
{
element: '.international-icon',
element: '#size-select',
popover: {
title: 'Switch language',
description: 'Switch the system language',
title: 'Switch Size',
description: 'Switch the system size',
position: 'left'
}
},
{
element: '.theme-switch',
popover: {
title: 'Theme Switch',
description: 'Custom switch system theme',
position: 'left'
}
},
{
element: '.tags-view-container',
element: '#tags-view-container',
popover: {
title: 'Tags view',
description: 'The history of the page you visited',

View File

@@ -1,6 +1,6 @@
<script>
export default {
name: 'Authredirect',
name: 'AuthRedirect',
created() {
const hash = window.location.search.slice(1)
if (window.localStorage) {

View File

@@ -14,6 +14,7 @@
<svg-icon icon-class="user" />
</span>
<el-input
ref="username"
v-model="loginForm.username"
:placeholder="$t('login.username')"
name="username"
@@ -22,22 +23,28 @@
/>
</el-form-item>
<el-tooltip v-model="capsTooltip" content="Caps lock is On" placement="right" manual>
<el-form-item prop="password">
<span class="svg-container">
<svg-icon icon-class="password" />
</span>
<el-input
:key="passwordType"
ref="password"
v-model="loginForm.password"
:type="passwordType"
:placeholder="$t('login.password')"
name="password"
auto-complete="on"
@keyup.native="checkCapslock"
@blur="capsTooltip = false"
@keyup.enter.native="handleLogin"
/>
<span class="show-pwd" @click="showPwd">
<svg-icon :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" />
</span>
</el-form-item>
</el-tooltip>
<el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">
{{ $t('login.logIn') }}
@@ -74,7 +81,7 @@
<script>
import { validUsername } from '@/utils/validate'
import LangSelect from '@/components/LangSelect'
import SocialSign from './socialsignin'
import SocialSign from './socialSignin'
export default {
name: 'Login',
@@ -97,13 +104,14 @@ export default {
return {
loginForm: {
username: 'admin',
password: '1111111'
password: '111111'
},
loginRules: {
username: [{ required: true, trigger: 'blur', validator: validateUsername }],
password: [{ required: true, trigger: 'blur', validator: validatePassword }]
},
passwordType: 'password',
capsTooltip: false,
loading: false,
showDialog: false,
redirect: undefined
@@ -120,16 +128,38 @@ export default {
created() {
// window.addEventListener('storage', this.afterQRScan)
},
mounted() {
if (this.loginForm.username === '') {
this.$refs.username.focus()
} else if (this.loginForm.password === '') {
this.$refs.password.focus()
}
},
destroyed() {
// window.removeEventListener('storage', this.afterQRScan)
},
methods: {
checkCapslock({ shiftKey, key } = {}) {
if (key && key.length === 1) {
if (shiftKey && (key >= 'a' && key <= 'z') || !shiftKey && (key >= 'A' && key <= 'Z')) {
this.capsTooltip = true
} else {
this.capsTooltip = false
}
}
if (key === 'CapsLock' && this.capsTooltip === true) {
this.capsTooltip = false
}
},
showPwd() {
if (this.passwordType === 'password') {
this.passwordType = ''
} else {
this.passwordType = 'password'
}
this.$nextTick(() => {
this.$refs.password.focus()
})
},
handleLogin() {
this.$refs.loginForm.validate(valid => {
@@ -171,21 +201,17 @@ export default {
}
</script>
<style rel="stylesheet/scss" lang="scss">
<style lang="scss">
/* 修复input 背景不协调 和光标变色 */
/* Detail see https://github.com/PanJiaChen/vue-element-admin/pull/927 */
$bg:#283443;
$light_gray:#eee;
$light_gray:#fff;
$cursor: #fff;
@supports (-webkit-mask: none) and (not (cater-color: $cursor)) {
.login-container .el-input input {
color: $cursor;
&::first-line {
color: $light_gray;
}
}
}
@@ -207,7 +233,7 @@ $cursor: #fff;
caret-color: $cursor;
&:-webkit-autofill {
-webkit-box-shadow: 0 0 0px 1000px $bg inset !important;
box-shadow: 0 0 0px 1000px $bg inset !important;
-webkit-text-fill-color: $cursor !important;
}
}
@@ -222,7 +248,7 @@ $cursor: #fff;
}
</style>
<style rel="stylesheet/scss" lang="scss" scoped>
<style lang="scss" scoped>
$bg:#2d3a4b;
$dark_gray:#889aa4;
$light_gray:#eee;

View File

@@ -35,7 +35,7 @@ export default {
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
<style lang="scss" scoped>
.social-signup-container {
margin: 20px 0;
.sign-btn {

View File

@@ -42,7 +42,7 @@ export default {
}
</script>
<style rel="stylesheet/scss" lang="scss">
<style lang="scss">
@mixin clearfix {
&:before {
display: table;

View File

@@ -1,9 +1,9 @@
<template>
<div>
<div style="margin-bottom:15px;">
{{ $t('permission.roles') }} {{ roles }}
{{ $t('permission.roles') }}: {{ roles }}
</div>
{{ $t('permission.switchRoles') }}
{{ $t('permission.switchRoles') }}:
<el-radio-group v-model="switchRoles">
<el-radio-button label="editor" />
<el-radio-button label="admin" />

View File

@@ -89,7 +89,7 @@ export default {
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
<style lang="scss" scoped>
.app-container {
/deep/ .permission-alert {
width: 320px;

View File

@@ -0,0 +1 @@
["info","error","success","warning","question","back","arrow-left","arrow-down","arrow-right","arrow-up","caret-left","caret-bottom","caret-top","caret-right","d-arrow-left","d-arrow-right","minus","plus","remove","circle-plus","remove-outline","circle-plus-outline","close","check","circle-close","circle-check","circle-close-outline","circle-check-outline","zoom-out","zoom-in","d-caret","sort","sort-down","sort-up","tickets","document","goods","sold-out","news","message","date","printer","time","bell","mobile-phone","service","view","menu","more","more-outline","star-on","star-off","location","location-outline","phone","phone-outline","picture","picture-outline","delete","search","edit","edit-outline","rank","refresh","share","setting","upload","upload2","download","loading"]

View File

@@ -4,7 +4,8 @@
<a href="https://panjiachen.github.io/vue-element-admin-site/guide/advanced/icon.html" target="_blank">Add and use
</a>
</p>
<div class="icons-wrapper">
<el-tabs type="border-card">
<el-tab-pane label="Icons">
<div v-for="item of iconsMap" :key="item" @click="handleClipboard(generateIconCode(item),$event)">
<el-tooltip placement="top">
<div slot="content">
@@ -16,25 +17,44 @@
</div>
</el-tooltip>
</div>
</el-tab-pane>
<el-tab-pane label="Element-UI Icons">
<div v-for="item of elementIcons" :key="item" @click="handleClipboard(generateElementIconCode(item),$event)">
<el-tooltip placement="top">
<div slot="content">
{{ generateElementIconCode(item) }}
</div>
<div class="icon-item">
<i :class="'el-icon-' + item" />
<span>{{ item }}</span>
</div>
</el-tooltip>
</div>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import icons from './requireIcons'
import clipboard from '@/utils/clipboard'
import icons from './requireIcons'
import elementIcons from './element-icon.json'
export default {
name: 'Icons',
data() {
return {
iconsMap: icons
iconsMap: icons,
elementIcons: elementIcons
}
},
methods: {
generateIconCode(symbol) {
return `<svg-icon icon-class="${symbol}" />`
},
generateElementIconCode(symbol) {
return `<i class="el-icon-${symbol}" />`
},
handleClipboard(text, event) {
clipboard(text, event)
}
@@ -42,29 +62,29 @@ export default {
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
<style lang="scss" scoped>
.icons-container {
margin: 10px 20px 0;
overflow: hidden;
.icons-wrapper {
margin: 0 auto;
}
.icon-item {
margin: 20px;
height: 110px;
height: 85px;
text-align: center;
width: 110px;
width: 100px;
float: left;
font-size: 30px;
color: #24292e;
cursor: pointer;
}
span {
display: block;
font-size: 24px;
font-size: 16px;
margin-top: 10px;
}
.disabled{
.disabled {
pointer-events: none;
}
}

View File

@@ -1,4 +1,3 @@
const req = require.context('../../icons/svg', false, /\.svg$/)
const requireAll = requireContext => requireContext.keys()

View File

@@ -19,9 +19,9 @@
</el-table-column>
<el-table-column min-width="300px" label="Title">
<template slot-scope="scope">
<span>{{ scope.row.title }}</span>
<el-tag>{{ scope.row.type }}</el-tag>
<template slot-scope="{row}">
<span>{{ row.title }}</span>
<el-tag>{{ row.type }}</el-tag>
</template>
</el-table-column>
@@ -44,9 +44,9 @@
</el-table-column>
<el-table-column class-name="status-col" label="Status" width="110">
<template slot-scope="scope">
<el-tag :type="scope.row.status | statusFilter">
{{ scope.row.status }}
<template slot-scope="{row}">
<el-tag :type="row.status | statusFilter">
{{ row.status }}
</el-tag>
</template>
</el-table-column>

View File

@@ -46,9 +46,9 @@
</template>
</el-table-column>
<el-table-column :label="$t('table.title')" min-width="150px">
<template slot-scope="scope">
<span class="link-type" @click="handleUpdate(scope.row)">{{ scope.row.title }}</span>
<el-tag>{{ scope.row.type | typeFilter }}</el-tag>
<template slot-scope="{row}">
<span class="link-type" @click="handleUpdate(row)">{{ row.title }}</span>
<el-tag>{{ row.type | typeFilter }}</el-tag>
</template>
</el-table-column>
<el-table-column :label="$t('table.author')" width="110px" align="center">
@@ -67,30 +67,30 @@
</template>
</el-table-column>
<el-table-column :label="$t('table.readings')" align="center" width="95">
<template slot-scope="scope">
<span v-if="scope.row.pageviews" class="link-type" @click="handleFetchPv(scope.row.pageviews)">{{ scope.row.pageviews }}</span>
<template slot-scope="{row}">
<span v-if="row.pageviews" class="link-type" @click="handleFetchPv(row.pageviews)">{{ row.pageviews }}</span>
<span v-else>0</span>
</template>
</el-table-column>
<el-table-column :label="$t('table.status')" class-name="status-col" width="100">
<template slot-scope="scope">
<el-tag :type="scope.row.status | statusFilter">
{{ scope.row.status }}
<template slot-scope="{row}">
<el-tag :type="row.status | statusFilter">
{{ row.status }}
</el-tag>
</template>
</el-table-column>
<el-table-column :label="$t('table.actions')" align="center" width="230" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="handleUpdate(scope.row)">
<template slot-scope="{row}">
<el-button type="primary" size="mini" @click="handleUpdate(row)">
{{ $t('table.edit') }}
</el-button>
<el-button v-if="scope.row.status!='published'" size="mini" type="success" @click="handleModifyStatus(scope.row,'published')">
<el-button v-if="row.status!='published'" size="mini" type="success" @click="handleModifyStatus(row,'published')">
{{ $t('table.publish') }}
</el-button>
<el-button v-if="scope.row.status!='draft'" size="mini" @click="handleModifyStatus(scope.row,'draft')">
<el-button v-if="row.status!='draft'" size="mini" @click="handleModifyStatus(row,'draft')">
{{ $t('table.draft') }}
</el-button>
<el-button v-if="scope.row.status!='deleted'" size="mini" type="danger" @click="handleModifyStatus(scope.row,'deleted')">
<el-button v-if="row.status!='deleted'" size="mini" type="danger" @click="handleModifyStatus(row,'deleted')">
{{ $t('table.delete') }}
</el-button>
</template>

View File

@@ -39,9 +39,9 @@
</el-table-column>
<el-table-column class-name="status-col" label="Status" width="110">
<template slot-scope="scope">
<el-tag :type="scope.row.status | statusFilter">
{{ scope.row.status }}
<template slot-scope="{row}">
<el-tag :type="row.status | statusFilter">
{{ row.status }}
</el-tag>
</template>
</el-table-column>
@@ -54,10 +54,10 @@
</el-table>
<!-- $t is vue-i18n global function to translate lang (lang in @/lang) -->
<div class="show-d">
{{ $t('table.dragTips1') }} : &nbsp; {{ oldList }}
<el-tag style="margin-right:12px;">{{ $t('table.dragTips1') }} :</el-tag> {{ oldList }}
</div>
<div class="show-d">
{{ $t('table.dragTips2') }} : {{ newList }}
<el-tag>{{ $t('table.dragTips2') }} :</el-tag> {{ newList }}
</div>
</div>
</template>
@@ -113,9 +113,9 @@ export default {
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
dataTransfer.setData('Text', '')
},
onEnd: evt => {
const targetRow = this.list.splice(evt.oldIndex, 1)[0]

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