Compare commits

...

37 Commits

Author SHA1 Message Date
Pan
de3b29b5f3 [release] 3.5.1 2018-01-05 13:29:57 +08:00
Pan
54acf1e0d5 perf[permission]:set role => roles 2018-01-05 11:38:34 +08:00
Pan
de08e49f19 fix[i18n]: fixed defalut language bug 2018-01-04 12:37:08 +08:00
Pan
2472107768 perf[i18n]: refine generateTitle function 2018-01-04 10:04:14 +08:00
Pan
d18902dfee perf[style]: refine css 2018-01-04 10:03:33 +08:00
Pan
9ba2648689 fix[sidebar]: fixed nest menu and noDropdown item bug 2018-01-03 13:40:22 +08:00
Pan
1d0b26cec8 perf[upload-excel]: support drag upload 2018-01-02 16:30:59 +08:00
Pan
ea7e139696 perf[core]: add postcss-url 2018-01-02 10:44:35 +08:00
Pan
deac95da1b [release] 3.5.0 2017-12-29 16:31:33 +08:00
Pan
2b51b987be fixed typo 2017-12-29 16:11:20 +08:00
花裤衩
45fef9b431 Feature/english (#381)
* perf[navbar]: set langSelect to component && refine errorLog component

* feat[login]:add 18n to login form

* fix[pagination]: fixed when selected page-sizes

* perf[i18n]:dashboard document svg permission

* perf[charts]: perf effect

* perf[i18n]:excel && zip

* perf[i18n]: table && errorLog && theme

* perf[i18n]: components

* perf[i18n]: direct use $t

* perf[i18n]: complex-table

* update README.md

* update README.md 📘

* perf[i18n]: refine code comments
2017-12-29 16:07:42 +08:00
Pan
83e56488d8 fix[pagination]: fixed when selected page-sizes 2017-12-25 15:49:52 +08:00
Pan
f3bfaa0f2b feat[login]:add 18n to login form 2017-12-25 15:49:52 +08:00
Pan
e261fbcbfb perf[navbar]: set langSelect to component && refine errorLog component 2017-12-25 15:49:52 +08:00
Pan
471c297f13 perf[css]: refine element-ui default size 2017-12-22 16:25:07 +08:00
Pan
093475ee50 [release] 3.4.1 2017-12-22 11:10:46 +08:00
Pan
cb3578e5c3 perf[drag-table]:refine the effect && fixed row-key bug 2017-12-22 11:09:54 +08:00
tlx6550
fe25c4d96b fix[tags-view]: fixed bug in DEL_OTHERS_VIEWS (#368) 2017-12-22 10:29:47 +08:00
Pan
7fef8568c2 perf: use import replace require.ensure 2017-12-21 13:25:29 +08:00
Pan
0dfa56c73e perf: element-ui css 2017-12-18 18:47:06 +08:00
Pan
642d48e372 fix :fixed typo 2017-12-18 10:25:53 +08:00
Pan
021c147b31 perf:code 2017-12-18 10:19:35 +08:00
Pan
04d9b849dd rm eventsource-polyfill 2017-12-15 14:25:36 +08:00
Pan
2e74f0d782 [release]: 3.4.0 2017-12-15 14:09:51 +08:00
花裤衩
dc162093cc refactor[core]: use webpack-dev-server now (#355)
* bump deps: update vue-splitpane version

* refactor[chore]: use webpack-dev-serve build

* perf:settings

* doc: uodate readme
2017-12-15 14:07:55 +08:00
Pan
8fd786c4ed perf[tags-view]: when closeOthersTags moveToCurrentTag 2017-12-14 11:01:07 +08:00
Pan
54e7ce2b76 bump deps: update some deps version 2017-12-14 10:53:57 +08:00
Pan
f5563b4eed [release] 3.3.0 2017-12-13 18:35:38 +08:00
Pan
0c80fba84c doc: update readme 2017-12-13 18:35:07 +08:00
Pan
4d3bbae183 perf[tags-view]:refine the style && format code 2017-12-13 18:33:12 +08:00
leij1ang
b6d97f1806 feat[tags-view]: tags-view add contextmenu (#343)
* add the menu by right-clicking the tags

* bug fixed

* refine
2017-12-13 17:43:03 +08:00
Pan
a68413cb8f refactor[errorLog]: save the logs in vuex 2017-12-13 13:52:14 +08:00
Pan
f9aaaa9091 perf[errorLog]:optimized error-log 2017-12-12 18:14:31 +08:00
Pan
85492f148f fix[scrollPane&&scrollBar]:fixed scroll bug in Firefox 2017-12-12 13:28:33 +08:00
Pan
b7ca786751 perf[chore]:set travis notifications:false 2017-12-11 14:20:23 +08:00
花裤衩
c32bd73b42 Update README.md 2017-12-11 14:06:14 +08:00
Pan
3aacd3cccd add[chore]:add travis 2017-12-11 13:59:49 +08:00
108 changed files with 2195 additions and 1620 deletions

View File

@@ -1,10 +1,12 @@
{
"presets": [
["env", {
"modules": false
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}],
"stage-2"
],
"plugins": ["transform-runtime"],
"comments": false
"plugins": ["transform-vue-jsx", "transform-runtime"]
}

View File

@@ -1,144 +1,199 @@
module.exports = {
root: true,
parser: 'babel-eslint',
parserOptions: {
sourceType: 'module'
},
env: {
browser: true,
node: true,
es6: true,
},
extends: 'eslint:recommended',
// required to lint *.vue files
plugins: [
'html'
],
// check if imports actually resolve
'settings': {
'import/resolver': {
'webpack': {
'config': 'build/webpack.base.conf.js'
}
}
},
// add your custom rules here
//it is base on https://github.com/vuejs/eslint-config-vue
'rules': {
'accessor-pairs': 2,
'arrow-spacing': [2, { 'before': true, 'after': true }],
'block-spacing': [2, 'always'],
'brace-style': [2, '1tbs', { 'allowSingleLine': true }],
'camelcase': [0, { 'properties': 'always' }],
'comma-dangle': [2, 'never'],
'comma-spacing': [2, { 'before': false, 'after': true }],
'comma-style': [2, 'last'],
'constructor-super': 2,
'curly': [2, 'multi-line'],
'dot-location': [2, 'property'],
'eol-last': 2,
'eqeqeq': [2, 'allow-null'],
'generator-star-spacing': [2, { 'before': true, 'after': true }],
'handle-callback-err': [2, '^(err|error)$' ],
'indent': [2, 2, { 'SwitchCase': 1 }],
'jsx-quotes': [2, 'prefer-single'],
'key-spacing': [2, { 'beforeColon': false, 'afterColon': true }],
'keyword-spacing': [2, { 'before': true, 'after': true }],
'new-cap': [2, { 'newIsCap': true, 'capIsNew': false }],
'new-parens': 2,
'no-array-constructor': 2,
'no-caller': 2,
'no-console': 'off',
'no-class-assign': 2,
'no-cond-assign': 2,
'no-const-assign': 2,
'no-control-regex': 0,
'no-delete-var': 2,
'no-dupe-args': 2,
'no-dupe-class-members': 2,
'no-dupe-keys': 2,
'no-duplicate-case': 2,
'no-empty-character-class': 2,
'no-empty-pattern': 2,
'no-eval': 2,
'no-ex-assign': 2,
'no-extend-native': 2,
'no-extra-bind': 2,
'no-extra-boolean-cast': 2,
'no-extra-parens': [2, 'functions'],
'no-fallthrough': 2,
'no-floating-decimal': 2,
'no-func-assign': 2,
'no-implied-eval': 2,
'no-inner-declarations': [2, 'functions'],
'no-invalid-regexp': 2,
'no-irregular-whitespace': 2,
'no-iterator': 2,
'no-label-var': 2,
'no-labels': [2, { 'allowLoop': false, 'allowSwitch': false }],
'no-lone-blocks': 2,
'no-mixed-spaces-and-tabs': 2,
'no-multi-spaces': 2,
'no-multi-str': 2,
'no-multiple-empty-lines': [2, { 'max': 1 }],
'no-native-reassign': 2,
'no-negated-in-lhs': 2,
'no-new-object': 2,
'no-new-require': 2,
'no-new-symbol': 2,
'no-new-wrappers': 2,
'no-obj-calls': 2,
'no-octal': 2,
'no-octal-escape': 2,
'no-path-concat': 2,
'no-proto': 2,
'no-redeclare': 2,
'no-regex-spaces': 2,
'no-return-assign': [2, 'except-parens'],
'no-self-assign': 2,
'no-self-compare': 2,
'no-sequences': 2,
'no-shadow-restricted-names': 2,
'no-spaced-func': 2,
'no-sparse-arrays': 2,
'no-this-before-super': 2,
'no-throw-literal': 2,
'no-trailing-spaces': 2,
'no-undef': 2,
'no-undef-init': 2,
'no-unexpected-multiline': 2,
'no-unmodified-loop-condition': 2,
'no-unneeded-ternary': [2, { 'defaultAssignment': false }],
'no-unreachable': 2,
'no-unsafe-finally': 2,
'no-unused-vars': [2, { 'vars': 'all', 'args': 'none' }],
'no-useless-call': 2,
'no-useless-computed-key': 2,
'no-useless-constructor': 2,
'no-useless-escape': 0,
'no-whitespace-before-property': 2,
'no-with': 2,
'one-var': [2, { 'initialized': 'never' }],
'operator-linebreak': [2, 'after', { 'overrides': { '?': 'before', ':': 'before' } }],
'padded-blocks': [2, 'never'],
'quotes': [2, 'single', { 'avoidEscape': true, 'allowTemplateLiterals': true }],
'semi': [2, 'never'],
'semi-spacing': [2, { 'before': false, 'after': true }],
'space-before-blocks': [2, 'always'],
'space-before-function-paren': [2, 'never'],
'space-in-parens': [2, 'never'],
'space-infix-ops': 2,
'space-unary-ops': [2, { 'words': true, 'nonwords': false }],
'spaced-comment': [2, 'always', { 'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ','] }],
'template-curly-spacing': [2, 'never'],
'use-isnan': 2,
'valid-typeof': 2,
'wrap-iife': [2, 'any'],
'yield-star-spacing': [2, 'both'],
'yoda': [2, 'never'],
'prefer-const': 2,
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
'object-curly-spacing': [2, 'always', { objectsInObjects: false }],
'array-bracket-spacing': [2, 'never']
root: true,
parser: 'babel-eslint',
parserOptions: {
sourceType: 'module'
},
env: {
browser: true,
node: true,
es6: true,
},
extends: 'eslint:recommended',
// required to lint *.vue files
plugins: [
'html'
],
// check if imports actually resolve
'settings': {
'import/resolver': {
'webpack': {
'config': 'build/webpack.base.conf.js'
}
}
},
// add your custom rules here
//it is base on https://github.com/vuejs/eslint-config-vue
'rules': {
'accessor-pairs': 2,
'arrow-spacing': [2, {
'before': true,
'after': true
}],
'block-spacing': [2, 'always'],
'brace-style': [2, '1tbs', {
'allowSingleLine': true
}],
'camelcase': [0, {
'properties': 'always'
}],
'comma-dangle': [2, 'never'],
'comma-spacing': [2, {
'before': false,
'after': true
}],
'comma-style': [2, 'last'],
'constructor-super': 2,
'curly': [2, 'multi-line'],
'dot-location': [2, 'property'],
'eol-last': 2,
'eqeqeq': [2, 'allow-null'],
'generator-star-spacing': [2, {
'before': true,
'after': true
}],
'handle-callback-err': [2, '^(err|error)$'],
'indent': [2, 2, {
'SwitchCase': 1
}],
'jsx-quotes': [2, 'prefer-single'],
'key-spacing': [2, {
'beforeColon': false,
'afterColon': true
}],
'keyword-spacing': [2, {
'before': true,
'after': true
}],
'new-cap': [2, {
'newIsCap': true,
'capIsNew': false
}],
'new-parens': 2,
'no-array-constructor': 2,
'no-caller': 2,
'no-console': 'off',
'no-class-assign': 2,
'no-cond-assign': 2,
'no-const-assign': 2,
'no-control-regex': 0,
'no-delete-var': 2,
'no-dupe-args': 2,
'no-dupe-class-members': 2,
'no-dupe-keys': 2,
'no-duplicate-case': 2,
'no-empty-character-class': 2,
'no-empty-pattern': 2,
'no-eval': 2,
'no-ex-assign': 2,
'no-extend-native': 2,
'no-extra-bind': 2,
'no-extra-boolean-cast': 2,
'no-extra-parens': [2, 'functions'],
'no-fallthrough': 2,
'no-floating-decimal': 2,
'no-func-assign': 2,
'no-implied-eval': 2,
'no-inner-declarations': [2, 'functions'],
'no-invalid-regexp': 2,
'no-irregular-whitespace': 2,
'no-iterator': 2,
'no-label-var': 2,
'no-labels': [2, {
'allowLoop': false,
'allowSwitch': false
}],
'no-lone-blocks': 2,
'no-mixed-spaces-and-tabs': 2,
'no-multi-spaces': 2,
'no-multi-str': 2,
'no-multiple-empty-lines': [2, {
'max': 1
}],
'no-native-reassign': 2,
'no-negated-in-lhs': 2,
'no-new-object': 2,
'no-new-require': 2,
'no-new-symbol': 2,
'no-new-wrappers': 2,
'no-obj-calls': 2,
'no-octal': 2,
'no-octal-escape': 2,
'no-path-concat': 2,
'no-proto': 2,
'no-redeclare': 2,
'no-regex-spaces': 2,
'no-return-assign': [2, 'except-parens'],
'no-self-assign': 2,
'no-self-compare': 2,
'no-sequences': 2,
'no-shadow-restricted-names': 2,
'no-spaced-func': 2,
'no-sparse-arrays': 2,
'no-this-before-super': 2,
'no-throw-literal': 2,
'no-trailing-spaces': 2,
'no-undef': 2,
'no-undef-init': 2,
'no-unexpected-multiline': 2,
'no-unmodified-loop-condition': 2,
'no-unneeded-ternary': [2, {
'defaultAssignment': false
}],
'no-unreachable': 2,
'no-unsafe-finally': 2,
'no-unused-vars': [2, {
'vars': 'all',
'args': 'none'
}],
'no-useless-call': 2,
'no-useless-computed-key': 2,
'no-useless-constructor': 2,
'no-useless-escape': 0,
'no-whitespace-before-property': 2,
'no-with': 2,
'one-var': [2, {
'initialized': 'never'
}],
'operator-linebreak': [2, 'after', {
'overrides': {
'?': 'before',
':': 'before'
}
}],
'padded-blocks': [2, 'never'],
'quotes': [2, 'single', {
'avoidEscape': true,
'allowTemplateLiterals': true
}],
'semi': [2, 'never'],
'semi-spacing': [2, {
'before': false,
'after': true
}],
'space-before-blocks': [2, 'always'],
'space-before-function-paren': [2, 'never'],
'space-in-parens': [2, 'never'],
'space-infix-ops': 2,
'space-unary-ops': [2, {
'words': true,
'nonwords': false
}],
'spaced-comment': [2, 'always', {
'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ',']
}],
'template-curly-spacing': [2, 'never'],
'use-isnan': 2,
'valid-typeof': 2,
'wrap-iife': [2, 'any'],
'yield-star-spacing': [2, 'both'],
'yoda': [2, 'never'],
'prefer-const': 2,
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
'object-curly-spacing': [2, 'always', {
objectsInObjects: false
}],
'array-bracket-spacing': [2, 'never']
}
}

13
.gitignore vendored
View File

@@ -2,9 +2,20 @@
node_modules/
dist/
gifs/
npm-debug.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
test/unit/coverage
test/e2e/reports
selenium-debug.log
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
package-lock.json

View File

@@ -2,6 +2,8 @@
module.exports = {
"plugins": {
"postcss-import": {},
"postcss-url": {},
// to edit target browsers: use "browserslist" field in package.json
"autoprefixer": {}
}

5
.travis.yml Executable file
View File

@@ -0,0 +1,5 @@
language: node_js
node_js: stable
script: npm run test
notifications:
email: false

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2017 PanJiaChen
Copyright (c) 2017-present PanJiaChen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,177 +0,0 @@
[![vue](https://img.shields.io/badge/vue-2.4.2-brightgreen.svg)](https://github.com/vuejs/vue)
[![element-ui](https://img.shields.io/badge/element--ui-1.4.2-brightgreen.svg)](https://github.com/ElemeFE/element)
[![license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE)
[![GitHub release](https://img.shields.io/github/release/PanJiaChen/vue-element-admin.svg)]()
## Intro
> In the past half year, I have been building a backend for management dashboard using Vue. Though the backend has contained greater than 70 pages and over 10 permissions, it still takes insignificant effort to maintain the project. So I decide to make it open source so as to share my development experience and progress on backend. The tech stack is mainly [Vue.js](https://github.com/vuejs/vue)+[Element](https://github.com/ElemeFE/element)+[axios](https://github.com/mzabriskie/axios). Since it's a personal project, all data requests are simulated with [Mock.js](https://github.com/nuysoft/Mock). **Note:** if anyone wants to modify or develop based on this project, please remove the mock files.
**Live demo:** http://panjiachen.github.io/vue-element-admin
**Note: element-ui@1.4.2 is used in the project, so vue 2.3.0+ is required.**
- vueAdmin-template: [vueAdmin-template](https://github.com/PanJiaChen/vueAdmin-template)  
- electron-vue-admin: [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
- Donate:[donate](https://github.com/PanJiaChen/vue-element-admin/blob/master/README-en.md#donate)
## Features
- Login/Logout
- Permission authentication
- Sidebar
- Breadcrumb
- Rich text editor
- Markdown editor
- JSON editor
- Drag & drop list
- SplitPane
- Dropzone
- Sticky
- CountTo
- ECharts
- 401, 404 error page
- Error log
- Export Excel
- Upload Excel
- Export Zip
- Table example
- Interactive table example
- Drag & drop table example
- Form example
- Multi-environments distribution
- Dashboard
- Two-factor authentication
- Collapsing sidebar (support nested routes)
- Mock data
- cache tabs example
- screenfull
- markdown2html
- views-tab
- clipboard
## Development
```bash
# Clone project
git clone https://github.com/PanJiaChen/vue-element-admin.git
# Install dependencies
npm install
# Or (not recommended for cnpm due to unknown bugs, use taobao mirror instead)
npm install --registry=https://registry.npm.taobao.org
# Run local dev server
npm run dev
```
Visit in browser: http://localhost:9527
## Distribution
```bash
# Build staged environment with webpack-bundle-analyzer
npm run build:sit-preview
# Build production environment
npm run build:prod
```
## Directory structure
```
├── build // build 
├── config // config
├── src // source code
│   ├── api // all requests
│   ├── assets // static resource like themes, fonts
│   ├── components // global public components
│   ├── directive // global directive
│   ├── filters // global filters
│   ├── mock // mock data
│   ├── router // router
│   ├── store // global status management
│   ├── styles // global styles
│   ├── utils // global public functions
│   ├── view // view
│   ├── App.vue // entry view
│   └── main.js // entry for loading components, initialization
├── static // third-party libraries not packed with Webpack
│   └── Tinymce // rich text
├── .babelrc // babel-loader config
├── eslintrc.js // eslint config
├── .gitignore // gitignore
├── favicon.ico // favicon
├── index.html // html template
└── package.json // package.json
```
## Changelog
Detailed changes for each release are documented in the [release notes](https://github.com/PanJiaChen/vue-element-admin/releases).
## Donate
If you find this project useful, you can buy me a cup of coffee
![donate](https://panjiachen.github.io/donate/donation.png)
## State Management
Only status of user and app configuration is managed by Vuex. Other data are managed by their own business pages.
## Demo
#### Two-factor authentication, supporting WeChat and QQ
![](https://github.com/PanJiaChen/vue-element-admin/blob/master/gifs/2login.gif)
#### Realtime switching themes
![](https://github.com/PanJiaChen/vue-element-admin/blob/master/gifs/theme.gif)
#### tabs
![tabs](https://github.com/PanJiaChen/vue-element-admin/blob/master/gifs/tabs.gif)<br />
#### Collapsing sidebar
![](https://github.com/PanJiaChen/vue-element-admin/blob/master/gifs/leftmenu.gif)
#### Drag & drop table
![](https://github.com/PanJiaChen/vue-element-admin/blob/master/gifs/order.gif)
#### Interactive table
![](https://github.com/PanJiaChen/vue-element-admin/blob/master/gifs/dynamictable.gif)
#### Uploading cropped avatar
![](https://github.com/PanJiaChen/vue-element-admin/blob/master/gifs/uploadAvatar.gif)
#### Error log
![](https://github.com/PanJiaChen/vue-element-admin/blob/master/gifs/errorlog.gif)
#### Rich text (integrated with Qiniu, watermark and customization)
![](https://github.com/PanJiaChen/vue-element-admin/blob/master/gifs/editor.gif)
#### Packaging table component
![](https://github.com/PanJiaChen/vue-element-admin/blob/master/gifs/table.gif)
#### Charts
![](https://github.com/PanJiaChen/vue-element-admin/blob/master/gifs/echarts.gif)
#### Exporting to Excel
![](https://github.com/PanJiaChen/vue-element-admin/blob/master/gifs/excel.png)
#### More
http://panjiachen.github.io/vue-element-admin
## License
MIT

213
README.md
View File

@@ -2,164 +2,151 @@
<img width="320" src="https://wpimg.wallstcn.com/ecc53a42-d79b-42e2-8852-5126b810a4c8.svg">
</p>
# vue-element-admin
<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">
</a>
<a href="https://github.com/ElemeFE/element">
<img src="https://img.shields.io/badge/element--ui-2.0.8-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">
</a>
<a href="https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/mashape/apistatus.svg" alt="license">
</a>
<a href="https://github.com/PanJiaChen/vue-element-admin/releases">
<img src="https://img.shields.io/github/release/PanJiaChen/vue-element-admin.svg" alt="GitHub release">
</a>
</p>
[![vue](https://img.shields.io/badge/vue-2.5.9-brightgreen.svg)](https://github.com/vuejs/vue)
[![element-ui](https://img.shields.io/badge/element--ui-2.0.7-brightgreen.svg)](https://github.com/ElemeFE/element)
[![license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE)
[![GitHub release](https://img.shields.io/github/release/PanJiaChen/vue-element-admin.svg)]()
English | [简体中文](./README.zh-CN.md)
**A magical vue admin.**
## Introduction
- [线上地址](http://panjiachen.github.io/vue-element-admin)
`vue-element-admin` is a production-ready solution for admin interfaces. Based on [Vue.js](https://github.com/vuejs/vue) and use the UI Toolkit -- [element](https://github.com/ElemeFE/element). `vue-element-admin` is a magical vue admin, it based on the newest development stack of vue, built-in i18n solution, typical templates for enterprise applications, lots of awesome features. It helps you build a large complex Single-Page Applications. I believe whatever your needs are, this project will help you.
- [使用文档](https://panjiachen.github.io/vue-element-admin-site/#/)
- [Preview](http://panjiachen.github.io/vue-element-admin)
- [English Document](https://github.com/PanJiaChen/vue-element-admin/blob/master/README-en.md)
- [Documentation](https://panjiachen.github.io/vue-element-admin-site/#/)
- [wiki](https://github.com/PanJiaChen/vue-element-admin/wiki)
- [donate](https://panjiachen.github.io/vue-element-admin-site/#/donate)
**本项目的定位是后台集成方案,不适合当基础模板来开发。**
- 模板建议使用: [vueAdmin-template](https://github.com/PanJiaChen/vueAdmin-template)  
- 桌面端: [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
**vue-element-admin is a admin interfaces integration solution, which is not suitable for secondary development as a base template.**
- Base template recommends using: [vueAdmin-template](https://github.com/PanJiaChen/vueAdmin-template)  
- Desktop: [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
**注意该项目目前使用element-ui@2.0.5版本,所以最低兼容 Vue 2.5.0**
**Note: This project uses element-ui@2.0.0+ version, so the minimum compatible vue@2.5.0**
楼主这里有一份调查[问卷](https://www.wjx.cn/m/16866569.aspx) 有空请填写一下,以表对本项目的支持~ps:不是给这个调查问卷网站做广告,所以填完问卷不用点上面抽奖有的没的那些东西
## Preparation
## 前序准备
You need to install [node](http://nodejs.org/) and [git](https://git-scm.com/) locally. The project is based on [ES2015+](http://es6.ruanyifeng.com/)、[vue](https://cn.vuejs.org/index.html)、[vuex](https://vuex.vuejs.org/zh-cn/)、[vue-router](https://router.vuejs.org/zh-cn/) 和 [element-ui](https://github.com/ElemeFE/element). All data requests for this project are simulated using [Mock.js](https://github.com/nuysoft/Mock). It would be helpful if you have pre-existing knowledge on those.
你的本地环境需要安装 [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/) 和 [element-ui](https://github.com/ElemeFE/element),提前了解和学习这些知识会对使用本项目有很大的帮助。
**This project is not a scaffolding and is more of an integrated solution.**
同时配套一个系列的教程文章,如何从零构建后一个完整的后台项目,建议大家先看完这些文章再来实践本项目
- [手摸手,带你用 vue 撸后台 系列一(基础篇)](https://juejin.im/post/59097cd7a22b9d0065fb61d2)
- [手摸手,带你用 vue 撸后台 系列二(登录权限篇)](https://juejin.im/post/591aa14f570c35006961acac)
- [手摸手,带你用 vue 撸后台 系列三 (实战篇)](https://juejin.im/post/593121aa0ce4630057f70d35)
- [手摸手,带你用 vue 撸后台 系列四(vueAdmin 一个极简的后台基础模板)](https://juejin.im/post/595b4d776fb9a06bbe7dba56)
- [手摸手,带你封装一个 vue component](https://segmentfault.com/a/1190000009090836)
- [手摸手,带你优雅的使用 icon](https://juejin.im/post/59bb864b5188257e7a427c09)
**This project does not support low version browsers (e.g. IE). Please add polyfill yourself if you need them.**
相应需求开了一个qq群 `591724180` 方便大家交流
<p align="center">
<img width="900" src="https://wpimg.wallstcn.com/a5894c1b-f6af-456e-82df-1151da0839bf.png">
</p>
或者可以加入该 **[圈子](https://jianshiapp.com/circles/1209)** 讨论问题
**如有问题请先看上述使用文档和文章,若不能满足,欢迎 issue 和 pr**
**本项目并不是一个脚手架,更倾向于是一个集成解决方案**
**该项目不支持低版本游览器(如ie)有需求请自行添加polyfill [详情](https://github.com/PanJiaChen/vue-element-admin/wiki#babel-polyfill)**
## 功能
- 登录/注销
- 权限验证
- 多环境发布
- 动态侧边栏(支持多级路由)
- 动态面包屑
- 国际化多语言
- 多种动态换肤
- 快捷导航(标签页)
- 富文本编辑器
- Markdown编辑器
- JSON编辑器
- Screenfull全屏
- 列表拖拽
- Svg Sprite 图标
## Features
```
- Login / Logout
- Permission authentication
- Multi-environment build
- Dynamic sidebar (supports multi-level routing)
- Dynamic breadcrumb
- I18n
- Customizable theme
- Tags-view(Tab page Support right-click operation)
- Rich text editor
- Markdown editor
- JSON editor
- Screenfull
- Drag and drop list
- Svg Sprite
- Dashboard
- 本地mock数据
- Echarts 图表
- Clipboard(剪贴复制)
- 401/404错误页面
- 错误日志
- 导出excel
- 导出zip
- 前端可视化excel
- Mock data
- Echarts
- Clipboard
- 401/404 error page
- Error log
- Export excel
- Export zip
- Front-end visualization excel
- Table example
- 动态table example
- 拖拽table example
- 内联编辑table example
- Dynamictable example
- Drag and drop table example
- Inline edit table example
- Form example
- 二步登录
- Two-step login
- SplitPane
- Dropzone
- Sticky
- CountTo
- Markdown2html
- Markdown to html
```
## Getting started
## 开发
```bash
# 克隆项目
git clone https://github.com/PanJiaChen/vue-element-admin.git
# clone the projice
git clone https://github.com/PanJiaChen/vue-element-admin.git
# 安装依赖
npm install
   
//or # 建议不要用cnpm  安装有各种诡异的bug 可以通过如下操作解决npm速度慢的问题
npm install --registry=https://registry.npm.taobao.org
# install dependency
npm install
# 本地开发 开启服务
npm run dev
# develop
npm run dev
```
浏览器访问 http://localhost:9527
## 发布
This will automatically open http://localhost:9527.
## Build
```bash
# 发布测试环境 带webpack ananalyzer
npm run build:sit-preview
# build for test environment
npm run build:sit
# 构建生成环境
npm run build:prod
# build for production environment
npm run build:prod
```
更多信息请参考 [使用文档](https://panjiachen.github.io/vue-element-admin-site/#/)
## Advanced
```bash
# --report to build with bundle size analytics
npm run build:prod --report
## 目录结构
```shell
├── build // 构建相关  
├── config // 配置相关
├── src // 源代码
│   ├── api // 所有请求
│   ├── assets // 主题 字体等静态资源
│   ├── components // 全局公用组件
│   ├── directive // 全局指令
│   ├── filtres // 全局 filter
│   ├── icons // 项目所有 svg icons
│   ├── lang // 国际化 language
│   ├── mock // 项目mock 模拟数据
│   ├── router // 路由
│   ├── store // 全局 store管理
│   ├── styles // 全局样式
│   ├── utils // 全局公用方法
│   ├── vendor // 公用vendor
│   ├── views // view
│   ├── App.vue // 入口页面
│   ├── main.js // 入口 加载组件 初始化等
│ └── permission.js // 权限管理
├── static // 第三方不打包资源
│   └── Tinymce // 富文本
├── .babelrc // babel-loader 配置
├── eslintrc.js // eslint 配置项
├── .gitignore // git 忽略项
├── favicon.ico // favicon图标
├── index.html // html模板
└── package.json // package.json
# --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
```
Refer to [Documentation](https://panjiachen.github.io/vue-element-admin-site/#/deploy) for more information
## Changelog
Detailed changes for each release are documented in the [release notes](https://github.com/PanJiaChen/vue-element-admin/releases).
## [查看更多demo](http://panjiachen.github.io/vue-element-admin)
![](https://wpimg.wallstcn.com/1bc334a6-32a8-4f29-a037-ac3f5ce32588.png)
## Online Demo
[Preview](http://panjiachen.github.io/vue-element-admin)
## Donate
If you find this project useful, you can buy me a cup of coffee
![donate](https://panjiachen.github.io/donate/donation.png)
If you find this project useful, you can buy author a glass of juice :tropical_drink:
![donate](https://wpimg.wallstcn.com/bd273f0d-83a0-4ef2-92e1-9ac8ed3746b9.png)
[Paypal Me](https://www.paypal.me/panfree23)
## License
MIT
[MIT](https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE)
Copyright (c) 2017-present PanJiaChen

165
README.zh-CN.md Normal file
View File

@@ -0,0 +1,165 @@
<p align="center">
<img width="320" src="https://wpimg.wallstcn.com/ecc53a42-d79b-42e2-8852-5126b810a4c8.svg">
</p>
<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">
</a>
<a href="https://github.com/ElemeFE/element">
<img src="https://img.shields.io/badge/element--ui-2.0.8-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">
</a>
<a href="https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/mashape/apistatus.svg" alt="license">
</a>
<a href="https://github.com/PanJiaChen/vue-element-admin/releases">
<img src="https://img.shields.io/github/release/PanJiaChen/vue-element-admin.svg" alt="GitHub release">
</a>
</p>
简体中文 | [English](./README.md)
## 简介
`vue-element-admin` 是一个后台集成解决方案,它基于 [Vue.js](https://github.com/vuejs/vue) 和 [element](https://github.com/ElemeFE/element)。它使用了最新的前端技术栈内置了i18国际化解决方案动态路由权限验证等很多功能特性相信不管你的需求是什么本项目都能帮助到你。
- [在线访问](http://panjiachen.github.io/vue-element-admin)
- [使用文档](https://panjiachen.github.io/vue-element-admin-site/#/)
- [wiki](https://github.com/PanJiaChen/vue-element-admin/wiki)
- [donate](https://panjiachen.github.io/vue-element-admin-site/#/donate)
**本项目的定位是后台集成方案,不适合当基础模板来开发。**
- 模板建议使用: [vueAdmin-template](https://github.com/PanJiaChen/vueAdmin-template)  
- 桌面端: [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
**注意:该项目使用 element-ui@2.0.0+ 版本,所以最低兼容 vue@2.5.0**
## 前序准备
你的本地环境需要安装 [node](http://nodejs.org/) 和 [git](https://git-scm.com/)。我们的技术栈基于 [ES2015+](http://es6.ruanyifeng.com/)、[vue](https://cn.vuejs.org/index.html)、[vuex](https://vuex.vuejs.org/zh-cn/)、[vue-router](https://router.vuejs.org/zh-cn/) and [element-ui](https://github.com/ElemeFE/element),所有的请求数据都使用[Mock.js](https://github.com/nuysoft/Mock)模拟,提前了解和学习这些知识会对使用本项目有很大的帮助。
同时配套一个系列的教程文章,如何从零构建后一个完整的后台项目,建议大家先看完这些文章再来实践本项目
- [手摸手,带你用 vue 撸后台 系列一(基础篇)](https://juejin.im/post/59097cd7a22b9d0065fb61d2)
- [手摸手,带你用 vue 撸后台 系列二(登录权限篇)](https://juejin.im/post/591aa14f570c35006961acac)
- [手摸手,带你用 vue 撸后台 系列三 (实战篇)](https://juejin.im/post/593121aa0ce4630057f70d35)
- [手摸手,带你用 vue 撸后台 系列四(vueAdmin 一个极简的后台基础模板)](https://juejin.im/post/595b4d776fb9a06bbe7dba56)
- [手摸手,带你封装一个 vue component](https://segmentfault.com/a/1190000009090836)
- [手摸手,带你优雅的使用 icon](https://juejin.im/post/59bb864b5188257e7a427c09)
响应需求开了一个qq群 `591724180` 方便大家交流
或者加入该群主 **[圈子](https://jianshiapp.com/circles/1209)** 楼主会经常分享一些技术相关的东西
**如有问题请先看上述使用文档和文章,若不能满足,欢迎 issue 和 pr**
**本项目并不是一个脚手架,更倾向于是一个集成解决方案**
**该项目不支持低版本浏览器(如ie)有需求请自行添加polyfill [详情](https://github.com/PanJiaChen/vue-element-admin/wiki#babel-polyfill)**
<p align="center">
<img width="900" src="https://wpimg.wallstcn.com/a5894c1b-f6af-456e-82df-1151da0839bf.png">
</p>
## 功能
```
- 登录/注销
- 权限验证
- 多环境发布
- 动态侧边栏(支持多级路由)
- 动态面包屑
- 国际化多语言
- 多种动态换肤
- 快捷导航(标签页)
- 富文本编辑器
- Markdown编辑器
- JSON编辑器
- Screenfull全屏
- 列表拖拽
- Svg Sprite 图标
- Dashboard
- 本地mock数据
- Echarts 图表
- Clipboard(剪贴复制)
- 401/404错误页面
- 错误日志
- 导出excel
- 导出zip
- 前端可视化excel
- Table example
- 动态table example
- 拖拽table example
- 内联编辑table example
- Form example
- 二步登录
- SplitPane
- Dropzone
- Sticky
- CountTo
- Markdown2html
```
## 开发
```bash
# 克隆项目
git clone https://github.com/PanJiaChen/vue-element-admin.git
# 安装依赖
npm install
   
# 建议不要用cnpm安装 会有各种诡异的bug 可以通过如下操作解决 npm 下载速度慢的问题
npm install --registry=https://registry.npm.taobao.org
# 启动服务
npm run dev
```
浏览器访问 http://localhost:9527
## 发布
```bash
# 构建测试环境
npm run build:sit
# 构建生成环境
npm run build:prod
```
## 其它
```bash
# --report to build with bundle size analytics
npm run build:prod --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
```
更多信息请参考 [使用文档](https://panjiachen.github.io/vue-element-admin-site/#/deploy)
## Changelog
Detailed changes for each release are documented in the [release notes](https://github.com/PanJiaChen/vue-element-admin/releases).
## Online Demo
[在线 Demo](http://panjiachen.github.io/vue-element-admin)
## Donate
如果你觉得这个项目帮助到了你,你可以帮作者买一杯果汁表示鼓励 :tropical_drink:
![donate](https://panjiachen.github.io/donate/donation.png)
[Paypal Me](https://www.paypal.me/panfree23)
## License
[MIT](https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE)
Copyright (c) 2017-present PanJiaChen

View File

@@ -1,39 +1,48 @@
require('./check-versions')();
var server = require('pushstate-server');
var opn = require('opn')
var ora = require('ora')
var rm = require('rimraf')
var path = require('path')
var chalk = require('chalk')
var webpack = require('webpack');
var config = require('../config');
var webpackConfig = require('./webpack.prod.conf');
'use strict'
require('./check-versions')()
var spinner = ora('building for ' + process.env.NODE_ENV + ' of ' + process.env.env_config+ ' mode...' )
const ora = require('ora')
const rm = require('rimraf')
const path = require('path')
const chalk = require('chalk')
const webpack = require('webpack')
const config = require('../config')
const webpackConfig = require('./webpack.prod.conf')
const server = require('pushstate-server')
var spinner = ora('building for '+ process.env.env_config+ ' environment...' )
spinner.start()
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
if (err) throw err
webpack(webpackConfig, (err, stats) => {
spinner.stop()
if (err) throw err
webpack(webpackConfig, function (err, stats) {
spinner.stop()
if (err) throw err
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false,
chunks: false,
chunkModules: false
}) + '\n\n')
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false,
chunks: false,
chunkModules: false
}) + '\n\n')
console.log(chalk.cyan(' Build complete.\n'))
if(process.env.npm_config_preview){
server.start({
port: 9528,
directory: './dist',
file: '/index.html'
});
console.log('> Listening at ' + 'http://localhost:9528' + '\n')
}
})
if (stats.hasErrors()) {
console.log(chalk.red(' Build failed with errors.\n'))
process.exit(1)
}
console.log(chalk.cyan(' Build complete.\n'))
console.log(chalk.yellow(
' Tip: built files are meant to be served over an HTTP server.\n' +
' Opening index.html over file:// won\'t work.\n'
))
if(process.env.npm_config_preview){
server.start({
port: 9526,
directory: './dist',
file: '/index.html'
});
console.log('> Listening at ' + 'http://localhost:9526' + '\n')
}
})
})

View File

@@ -1,45 +1,54 @@
var chalk = require('chalk')
var semver = require('semver')
var packageConfig = require('../package.json')
'use strict'
const chalk = require('chalk')
const semver = require('semver')
const packageConfig = require('../package.json')
const shell = require('shelljs')
function exec(cmd) {
return require('child_process').execSync(cmd).toString().trim()
function exec (cmd) {
return require('child_process').execSync(cmd).toString().trim()
}
var versionRequirements = [
{
name: 'node',
currentVersion: semver.clean(process.version),
versionRequirement: packageConfig.engines.node
},
{
name: 'npm',
currentVersion: exec('npm --version'),
versionRequirement: packageConfig.engines.npm
}
const versionRequirements = [
{
name: 'node',
currentVersion: semver.clean(process.version),
versionRequirement: packageConfig.engines.node
}
]
if (shell.which('npm')) {
versionRequirements.push({
name: 'npm',
currentVersion: exec('npm --version'),
versionRequirement: packageConfig.engines.npm
})
}
module.exports = function () {
var warnings = []
for (var i = 0; i < versionRequirements.length; i++) {
var mod = versionRequirements[i]
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
warnings.push(mod.name + ': ' +
chalk.red(mod.currentVersion) + ' should be ' +
chalk.green(mod.versionRequirement)
)
}
const warnings = []
for (let i = 0; i < versionRequirements.length; i++) {
const mod = versionRequirements[i]
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
warnings.push(mod.name + ': ' +
chalk.red(mod.currentVersion) + ' should be ' +
chalk.green(mod.versionRequirement)
)
}
}
if (warnings.length) {
console.log('')
console.log(chalk.yellow('To use this template, you must update following to modules:'))
console.log()
for (let i = 0; i < warnings.length; i++) {
const warning = warnings[i]
console.log(' ' + warning)
}
if (warnings.length) {
console.log('')
console.log(chalk.yellow('To use this template, you must update following to modules:'))
console.log()
for (var i = 0; i < warnings.length; i++) {
var warning = warnings[i]
console.log(' ' + warning)
}
console.log()
process.exit(1)
}
console.log()
process.exit(1)
}
}

View File

@@ -1,9 +0,0 @@
/* eslint-disable */
require('eventsource-polyfill')
var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
hotClient.subscribe(function (event) {
if (event.action === 'reload') {
window.location.reload()
}
})

View File

@@ -1,93 +0,0 @@
require('./check-versions')(); // 检查 Node 和 npm 版本
var config = require('../config');
if (!process.env.NODE_ENV) {
process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV)
}
var opn = require('opn')
var path = require('path');
var express = require('express');
var webpack = require('webpack');
var proxyMiddleware = require('http-proxy-middleware');
var webpackConfig = require('./webpack.dev.conf');
// default port where dev server listens for incoming traffic
var port = process.env.PORT || config.dev.port;
// automatically open browser, if not set will be false
var autoOpenBrowser = !!config.dev.autoOpenBrowser;
// Define HTTP proxies to your custom API backend
// https://github.com/chimurai/http-proxy-middleware
var proxyTable = config.dev.proxyTable;
var app = express();
var compiler = webpack(webpackConfig);
var devMiddleware = require('webpack-dev-middleware')(compiler, {
publicPath: webpackConfig.output.publicPath,
quiet: true
});
var hotMiddleware = require('webpack-hot-middleware')(compiler, {
log: false,
heartbeat: 2000
});
// force page reload when html-webpack-plugin template changes
// currently disabled until this is resolved:
// https://github.com/jantimon/html-webpack-plugin/issues/680
// compiler.plugin('compilation', function (compilation) {
// compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
// hotMiddleware.publish({ action: 'reload' })
// cb()
// })
// })
// proxy api requests
Object.keys(proxyTable).forEach(function (context) {
var options = proxyTable[context]
if (typeof options === 'string') {
options = {target: options}
}
app.use(proxyMiddleware(options.filter || context, options))
});
// handle fallback for HTML5 history API
app.use(require('connect-history-api-fallback')());
// serve webpack bundle output
app.use(devMiddleware);
// enable hot-reload and state-preserving
// compilation error display
app.use(hotMiddleware);
// serve pure static assets
var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory);
app.use(staticPath, express.static('./static'));
var uri = 'http://localhost:' + port
var _resolve
var readyPromise = new Promise(resolve => {
_resolve = resolve
})
console.log('> Starting dev server...')
devMiddleware.waitUntilValid(() => {
console.log('> Listening at ' + uri + '\n')
// when env is testing, don't need open it
if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
opn(uri)
}
_resolve()
})
var server = app.listen(port)
module.exports = {
ready: readyPromise,
close: () => {
server.close()
}
}

BIN
build/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -1,35 +1,38 @@
var path = require('path')
var config = require('../config')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
'use strict'
const path = require('path')
const config = require('../config')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const packageConfig = require('../package.json')
exports.assetsPath = function (_path) {
var assetsSubDirectory = process.env.NODE_ENV === 'production'
const assetsSubDirectory = process.env.NODE_ENV === 'production'
? config.build.assetsSubDirectory
: config.dev.assetsSubDirectory
return path.posix.join(assetsSubDirectory, _path)
}
exports.cssLoaders = function (options) {
options = options || {}
var cssLoader = {
const cssLoader = {
loader: 'css-loader',
options: {
minimize: process.env.NODE_ENV === 'production',
sourceMap: options.sourceMap
}
}
var postcssLoader = {
const postcssLoader = {
loader: 'postcss-loader',
options: {
sourceMap: true
sourceMap: options.sourceMap
}
}
// generate loader string to be used with extract text plugin
function generateLoaders (loader, loaderOptions) {
var loaders = options.usePostCSS !== false ? [cssLoader, postcssLoader] : [cssLoader]
const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
if (loader) {
loaders.push({
loader: loader + '-loader',
@@ -65,14 +68,34 @@ exports.cssLoaders = function (options) {
// Generate loaders for standalone style files (outside of .vue)
exports.styleLoaders = function (options) {
var output = []
var loaders = exports.cssLoaders(options)
for (var extension in loaders) {
var loader = loaders[extension]
const output = []
const loaders = exports.cssLoaders(options)
for (const extension in loaders) {
const loader = loaders[extension]
output.push({
test: new RegExp('\\.' + extension + '$'),
use: loader
})
}
return output
}
exports.createNotifierCallback = () => {
const notifier = require('node-notifier')
return (severity, errors) => {
if (severity !== 'error') return
const error = errors[0]
const filename = error.file && error.file.split('!').pop()
notifier.notify({
title: packageConfig.name,
message: severity + ': ' + error.name,
subtitle: filename || '',
icon: path.join(__dirname, 'logo.png')
})
}
}

View File

@@ -1,12 +1,22 @@
var utils = require('./utils')
var config = require('../config')
var isProduction = process.env.NODE_ENV === 'production'
'use strict'
const utils = require('./utils')
const config = require('../config')
const isProduction = process.env.NODE_ENV === 'production'
const sourceMapEnabled = isProduction
? config.build.productionSourceMap
: config.dev.cssSourceMap
module.exports = {
loaders: utils.cssLoaders({
sourceMap: isProduction
? config.build.productionSourceMap
: config.dev.cssSourceMap,
extract: isProduction
})
loaders: utils.cssLoaders({
sourceMap: sourceMapEnabled,
extract: isProduction
}),
cssSourceMap: sourceMapEnabled,
cacheBusting: config.dev.cacheBusting,
transformToRequire: {
video: ['src', 'poster'],
source: 'src',
img: 'src',
image: 'xlink:href'
}
}

View File

@@ -1,51 +1,46 @@
var path = require('path')
var utils = require('./utils')
var config = require('../config')
var vueLoaderConfig = require('./vue-loader.conf')
'use strict'
const path = require('path')
const utils = require('./utils')
const config = require('../config')
const vueLoaderConfig = require('./vue-loader.conf')
function resolve(dir) {
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
const createLintingRule = () => ({
test: /\.(js|vue)$/,
loader: 'eslint-loader',
enforce: 'pre',
include: [resolve('src'), resolve('test')],
options: {
formatter: require('eslint-friendly-formatter'),
emitWarning: !config.dev.showEslintErrorsInOverlay
}
})
module.exports = {
context: path.resolve(__dirname, '../'),
entry: {
app: './src/main.js'
},
output: {
path: config.build.assetsRoot,
filename: '[name].js',
publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
'src': path.resolve(__dirname, '../src'),
'assets': path.resolve(__dirname, '../src/assets'),
'components': path.resolve(__dirname, '../src/components'),
'views': path.resolve(__dirname, '../src/views'),
'styles': path.resolve(__dirname, '../src/styles'),
'api': path.resolve(__dirname, '../src/api'),
'utils': path.resolve(__dirname, '../src/utils'),
'store': path.resolve(__dirname, '../src/store'),
'router': path.resolve(__dirname, '../src/router'),
'mock': path.resolve(__dirname, '../src/mock'),
'vendor': path.resolve(__dirname, '../src/vendor'),
'static': path.resolve(__dirname, '../static')
}
},
module: {
rules: [
{
test: /\.(js|vue)$/,
loader: 'eslint-loader',
enforce: "pre",
include: [resolve('src'), resolve('test')],
options: {
formatter: require('eslint-friendly-formatter')
}
},
...(config.dev.useEslint ? [createLintingRule()] : []),
{
test: /\.vue$/,
loader: 'vue-loader',
@@ -54,7 +49,7 @@ module.exports = {
{
test: /\.js$/,
loader: 'babel-loader?cacheDirectory',
include: [resolve('src'), resolve('test')]
include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
},
{
test: /\.svg$/,
@@ -73,6 +68,14 @@ module.exports = {
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('media/[name].[hash:7].[ext]')
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
@@ -83,10 +86,16 @@ module.exports = {
}
]
},
//注入全局mixin
// sassResources: path.join(__dirname, '../src/styles/mixin.scss'),
// sassLoader: {
// data: path.join(__dirname, '../src/styles/index.scss')
// },
node: {
// prevent webpack from injecting useless setImmediate polyfill because Vue
// source contains it (although only uses it if it's native).
setImmediate: false,
// prevent webpack from injecting mocks to Node native modules
// that does not make sense for the client
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty'
}
}

View File

@@ -1,44 +1,88 @@
var utils = require('./utils')
var path = require('path')
var webpack = require('webpack')
var config = require('../config')
var merge = require('webpack-merge')
var baseWebpackConfig = require('./webpack.base.conf')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
'use strict'
const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const portfinder = require('portfinder')
// add hot-reload related code to entry chunks
Object.keys(baseWebpackConfig.entry).forEach(function (name) {
baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
})
function resolveApp(relativePath) {
return path.resolve(relativePath);
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
module.exports = merge(baseWebpackConfig, {
const HOST = process.env.HOST
const PORT = process.env.PORT && Number(process.env.PORT)
const devWebpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
},
// cheap-source-map is faster for development
devtool: '#cheap-source-map',
cache: true,
// cheap-module-eval-source-map is faster for development
devtool: config.dev.devtool,
// these devServer options should be customized in /config/index.js
devServer: {
clientLogLevel: 'warning',
historyApiFallback: true,
hot: true,
compress: true,
host: HOST || config.dev.host,
port: PORT || config.dev.port,
open: config.dev.autoOpenBrowser,
overlay: config.dev.errorOverlay
? { warnings: false, errors: true }
: false,
publicPath: config.dev.assetsPublicPath,
proxy: config.dev.proxyTable,
quiet: true, // necessary for FriendlyErrorsPlugin
watchOptions: {
poll: config.dev.poll,
}
},
plugins: [
new webpack.DefinePlugin({
'process.env': config.dev.env
'process.env': require('../config/dev.env')
}),
// https://github.com/glenjamin/webpack-hot-middleware#installation--usage
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
new webpack.NoEmitOnErrorsPlugin(),
// https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
favicon: resolveApp('favicon.ico'),
inject: true,
favicon: resolve('favicon.ico'),
title: 'vue-element-admin',
path: config.dev.assetsPublicPath + config.dev.assetsSubDirectory
}),
new FriendlyErrorsPlugin()
]
})
module.exports = new Promise((resolve, reject) => {
portfinder.basePort = process.env.PORT || config.dev.port
portfinder.getPort((err, port) => {
if (err) {
reject(err)
} else {
// publish the new Port, necessary for e2e tests
process.env.PORT = port
// add port to devServer config
devWebpackConfig.devServer.port = port
// Add FriendlyErrorsPlugin
devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
compilationSuccessInfo: {
messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
},
onErrors: config.dev.notifyOnErrors
? utils.createNotifierCallback()
: undefined
}))
resolve(devWebpackConfig)
}
})
})

View File

@@ -1,21 +1,23 @@
var path = require('path')
var utils = require('./utils')
var webpack = require('webpack')
var config = require('../config')
var merge = require('webpack-merge')
var baseWebpackConfig = require('./webpack.base.conf')
var CopyWebpackPlugin = require('copy-webpack-plugin')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
'use strict'
const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
var env = config.build[process.env.env_config+'Env']
function resolveApp(relativePath) {
return path.resolve(relativePath);
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
var webpackConfig = merge(baseWebpackConfig, {
const env = require('../config/'+process.env.env_config+'.env')
const webpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({
sourceMap: config.build.productionSourceMap,
@@ -23,64 +25,69 @@ var webpackConfig = merge(baseWebpackConfig, {
usePostCSS: true
})
},
devtool: config.build.productionSourceMap ? '#source-map' : false,
devtool: config.build.productionSourceMap ? config.build.devtool : false,
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash].js'),
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js'),
publicPath: config.build.assetsPublicPath
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
},
plugins: [
// http://vuejs.github.io/vue-loader/en/workflow/production.html
new webpack.DefinePlugin({
'process.env': env
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false
}
},
sourceMap: true,
sourceMap: config.build.productionSourceMap,
parallel: true
}),
// extract css into its own file
new ExtractTextPlugin({
filename: utils.assetsPath('css/[name].[contenthash].css')
filename: utils.assetsPath('css/[name].[contenthash].css'),
// Setting the following option to `false` will not extract CSS from codesplit chunks.
// Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
// increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
allChunks: false,
}),
// Compress extracted CSS. We are using this plugin so that possible
// duplicated CSS from different components can be deduped.
new OptimizeCSSPlugin(),
new OptimizeCSSPlugin({
cssProcessorOptions: config.build.productionSourceMap
? { safe: true, map: { inline: false } }
: { safe: true }
}),
// generate dist index.html with correct asset hash for caching.
// you can customize output by editing /index.html
// see https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: 'index.html',
filename: config.build.index,
template: 'index.html',
inject: true,
favicon: resolveApp('favicon.ico'),
favicon: resolve('favicon.ico'),
title: 'vue-element-admin',
path: config.build.assetsPublicPath + config.build.assetsSubDirectory,
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true
removeAttributeQuotes: true
// more options:
// https://github.com/kangax/html-minifier#options-quick-reference
},
path: config.build.assetsPublicPath + config.build.assetsSubDirectory,
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
chunksSortMode: 'dependency'
}),
// cache Module Identifiers
// keep module.id stable when vender modules does not change
new webpack.HashedModuleIdsPlugin(),
// enable scope hoisting
new webpack.optimize.ModuleConcatenationPlugin(),
// split vendor js into its own file
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function (module) {
minChunks (module) {
// any required modules inside node_modules are extracted to vendor
return (
module.resource &&
@@ -97,6 +104,15 @@ var webpackConfig = merge(baseWebpackConfig, {
name: 'manifest',
minChunks: Infinity
}),
// This instance extracts shared chunks from code splitted chunks and bundles them
// in a separate chunk, similar to the vendor chunk
// see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
new webpack.optimize.CommonsChunkPlugin({
name: 'app',
async: 'vendor-async',
children: true,
minChunks: 3
}),
// split echarts into its own file
new webpack.optimize.CommonsChunkPlugin({
async: 'echarts',
@@ -113,29 +129,47 @@ var webpackConfig = merge(baseWebpackConfig, {
return context && (context.indexOf('xlsx') >= 0);
}
}),
// This instance extracts shared chunks from code splitted chunks and bundles them
// in a separate chunk, similar to the vendor chunk
// see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
new webpack.optimize.CommonsChunkPlugin({
name: 'app',
async: 'vendor-async',
children: true,
minChunks: 3
// split codemirror into its own file
new webpack.optimize.CommonsChunkPlugin({
async: 'codemirror',
minChunks(module) {
var context = module.context;
return context && (context.indexOf('codemirror') >= 0);
}
}),
// copy custom static assets
new CopyWebpackPlugin([{
from: path.resolve(__dirname, '../static'),
to: config.build.assetsSubDirectory,
ignore: ['.*']
}])
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.build.assetsSubDirectory,
ignore: ['.*']
}
])
]
})
if (config.build.productionGzip) {
const CompressionWebpackPlugin = require('compression-webpack-plugin')
webpackConfig.plugins.push(
new CompressionWebpackPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp(
'\\.(' +
config.build.productionGzipExtensions.join('|') +
')$'
),
threshold: 10240,
minRatio: 0.8
})
)
}
if (config.build.bundleAnalyzerReport) {
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
module.exports = webpackConfig

View File

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

View File

@@ -1,39 +1,83 @@
'use strict'
// Template version: 1.2.6
// see http://vuejs-templates.github.io/webpack for documentation.
var path = require('path')
const path = require('path')
module.exports = {
build: {
sitEnv: require('./sit.env'),
prodEnv: require('./prod.env'),
index: path.resolve(__dirname, '../dist/index.html'),
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
assetsPublicPath: './', //请根据自己路径配置更改
productionSourceMap: false,
// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.
// Before setting to `true`, make sure to:
// npm install --save-dev compression-webpack-plugin
productionGzip: false,
productionGzipExtensions: ['js', 'css'],
// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build --report`
// Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report
},
dev: {
env: require('./dev.env'),
port: 9527,
autoOpenBrowser: true,
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {},
// CSS Sourcemaps off by default because relative paths are "buggy"
// with this option, according to the CSS-Loader README
// (https://github.com/webpack/css-loader#sourcemaps)
// In our experience, they generally work as expected,
// just be aware of this issue when enabling this option.
cssSourceMap: false
}
dev: {
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {},
// Various Dev Server settings
host: 'localhost', // can be overwritten by process.env.HOST
port: 9527, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
autoOpenBrowser: true,
errorOverlay: true,
notifyOnErrors: false,
poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
// Use Eslint Loader?
// If true, your code will be linted during bundling and
// linting errors and warnings will be shown in the console.
useEslint: true,
// If true, eslint errors and warnings will also be shown in the error overlay
// in the browser.
showEslintErrorsInOverlay: false,
/**
* Source Maps
*/
// https://webpack.js.org/configuration/devtool/#development
devtool: 'eval-source-map',
// If you have problems debugging vue-files in devtools,
// set this to false - it *may* help
// https://vue-loader.vuejs.org/en/options.html#cachebusting
cacheBusting: true,
// CSS Sourcemaps off by default because relative paths are "buggy"
// with this option, according to the CSS-Loader README
// (https://github.com/webpack/css-loader#sourcemaps)
// In our experience, they generally work as expected,
// just be aware of this issue when enabling this option.
cssSourceMap: false,
},
build: {
// Template for index.html
index: path.resolve(__dirname, '../dist/index.html'),
// Paths
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
// you can set by youself according to actual condition
assetsPublicPath: './',
/**
* Source Maps
*/
productionSourceMap: false,
// https://webpack.js.org/configuration/devtool/#production
devtool: '#source-map',
// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.
// Before setting to `true`, make sure to:
// npm install --save-dev compression-webpack-plugin
productionGzip: false,
productionGzipExtensions: ['js', 'css'],
// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build --report`
// Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report
}
}

View File

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

View File

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

View File

@@ -1,95 +1,93 @@
{
"name": "vue-element-admin",
"version": "3.2.0",
"description": "A Vue.js admin",
"version": "3.5.1",
"description": "A magical vue admin. Typical templates for enterprise applications. Newest development stack of vue. Lots of awesome features",
"author": "Pan <panfree23@gmail.com>",
"license": "MIT",
"private": true,
"scripts": {
"dev": "node build/dev-server.js",
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"build:prod": "cross-env NODE_ENV=production env_config=prod node build/build.js",
"build:sit": "cross-env NODE_ENV=production env_config=sit node build/build.js",
"build:sit-preview": "cross-env NODE_ENV=production env_config=sit npm_config_preview=true npm_config_report=true node build/build.js",
"lint": "eslint --ext .js,.vue src"
"lint": "eslint --ext .js,.vue src",
"test": "npm run lint"
},
"dependencies": {
"axios": "0.17.1",
"clipboard": "1.7.1",
"codemirror": "5.31.0",
"codemirror": "5.32.0",
"dropzone": "5.2.0",
"echarts": "3.8.5",
"element-ui": "2.0.7",
"element-ui": "2.0.8",
"file-saver": "1.3.3",
"font-awesome": "4.7.0",
"js-cookie": "2.2.0",
"jsonlint": "1.6.2",
"jszip": "3.1.4",
"jszip": "3.1.5",
"mockjs": "1.0.1-beta3",
"normalize.css": "7.0.0",
"nprogress": "0.2.0",
"screenfull": "3.3.2",
"showdown": "1.8.2",
"showdown": "1.8.5",
"simplemde": "1.11.2",
"sortablejs": "1.6.1",
"vue": "2.5.9",
"vue-count-to": "1.0.10",
"sortablejs": "1.7.0",
"vue": "2.5.10",
"vue-count-to": "1.0.13",
"vue-i18n": "7.3.2",
"vue-multiselect": "2.0.6",
"vue-multiselect": "2.0.8",
"vue-router": "3.0.1",
"vue-splitpane": "1.0.0",
"vue-splitpane": "1.0.2",
"vuedraggable": "2.15.0",
"vuex": "3.0.1",
"xlsx": "^0.11.7"
"xlsx": "^0.11.16"
},
"devDependencies": {
"autoprefixer": "7.1.6",
"autoprefixer": "7.2.3",
"babel-core": "6.26.0",
"babel-eslint": "8.0.2",
"babel-eslint": "8.0.3",
"babel-helper-vue-jsx-merge-props": "2.0.3",
"babel-loader": "7.1.2",
"babel-plugin-syntax-jsx": "6.18.0",
"babel-plugin-transform-runtime": "6.23.0",
"babel-plugin-transform-vue-jsx": "3.5.0",
"babel-preset-env": "1.6.1",
"babel-preset-stage-2": "6.24.1",
"babel-register": "6.26.0",
"chalk": "2.3.0",
"connect-history-api-fallback": "1.4.0",
"copy-webpack-plugin": "4.2.0",
"copy-webpack-plugin": "4.3.0",
"cross-env": "5.1.1",
"css-loader": "0.28.7",
"eslint": "4.11.0",
"eslint": "4.13.1",
"eslint-friendly-formatter": "3.0.0",
"eslint-import-resolver-webpack": "0.8.3",
"eslint-loader": "1.9.0",
"eslint-plugin-html": "3.2.2",
"eslint-plugin-import": "2.8.0",
"eventsource-polyfill": "0.9.6",
"express": "4.16.2",
"eslint-plugin-html": "4.0.1",
"extract-text-webpack-plugin": "3.0.2",
"file-loader": "0.11.2",
"file-loader": "1.1.5",
"friendly-errors-webpack-plugin": "1.6.1",
"function-bind": "1.1.0",
"html-webpack-plugin": "2.30.0",
"http-proxy-middleware": "0.17.4",
"node-sass": "^4.5.0",
"opn": "4.0.2",
"html-webpack-plugin": "2.30.1",
"node-notifier": "5.1.2",
"node-sass": "^4.7.2",
"optimize-css-assets-webpack-plugin": "3.2.0",
"ora": "1.1.0",
"postcss-loader": "^2.0.8",
"pushstate-server": "2.1.0",
"rimraf": "2.6.0",
"ora": "1.3.0",
"portfinder": "1.0.13",
"postcss-import": "11.0.0",
"postcss-loader": "2.0.9",
"postcss-url": "7.3.0",
"pushstate-server": "3.0.1",
"rimraf": "2.6.2",
"sass-loader": "6.0.6",
"script-loader": "0.7.2",
"semver": "5.3.0",
"style-loader": "0.19.0",
"svg-sprite-loader": "3.4.1",
"semver": "5.4.1",
"shelljs": "0.7.8",
"svg-sprite-loader": "3.5.2",
"uglifyjs-webpack-plugin": "1.1.3",
"url-loader": "0.6.2",
"vue-loader": "13.5.0",
"vue-style-loader": "3.0.3",
"vue-template-compiler": "2.5.9",
"webpack": "3.8.1",
"webpack-bundle-analyzer": "2.9.0",
"webpack-dev-middleware": "1.12.0",
"webpack-hot-middleware": "2.20.0",
"webpack-merge": "4.1.0"
"vue-template-compiler": "2.5.10",
"webpack": "3.10.0",
"webpack-bundle-analyzer": "2.9.1",
"webpack-dev-server": "2.9.7",
"webpack-merge": "4.1.1"
},
"engines": {
"node": ">= 4.0.0",

View File

@@ -9,8 +9,3 @@
name: 'APP'
}
</script>
<style lang="scss">
@import '~normalize.css/normalize.css'; // normalize.css 样式格式化
@import './styles/index.scss'; // 全局自定义样式
</style>

View File

@@ -46,13 +46,14 @@ export default {
this.chart.setOption({
backgroundColor: '#394056',
title: {
text: '请求数',
top: 20,
text: 'Requests',
textStyle: {
fontWeight: 'normal',
fontSize: 16,
color: '#F1F1F3'
},
left: '6%'
left: '1%'
},
tooltip: {
trigger: 'axis',
@@ -63,11 +64,12 @@ export default {
}
},
legend: {
top: 20,
icon: 'rect',
itemWidth: 14,
itemHeight: 5,
itemGap: 13,
data: ['移动', '电信', '联通'],
data: ['CMCC', 'CTCC', 'CUCC'],
right: '4%',
textStyle: {
fontSize: 12,
@@ -75,9 +77,10 @@ export default {
}
},
grid: {
top: 100,
left: '3%',
right: '4%',
bottom: '3%',
bottom: '2%',
containLabel: true
},
xAxis: [{
@@ -92,7 +95,7 @@ export default {
}],
yAxis: [{
type: 'value',
name: '单位(%',
name: '(%)',
axisTick: {
show: false
},
@@ -114,7 +117,7 @@ export default {
}
}],
series: [{
name: '移动',
name: 'CMCC',
type: 'line',
smooth: true,
symbol: 'circle',
@@ -148,7 +151,7 @@ export default {
},
data: [220, 182, 191, 134, 150, 120, 110, 125, 145, 122, 165, 122]
}, {
name: '电信',
name: 'CTCC',
type: 'line',
smooth: true,
symbol: 'circle',
@@ -182,7 +185,7 @@ export default {
},
data: [120, 110, 125, 145, 122, 165, 122, 220, 182, 191, 134, 150]
}, {
name: '联通',
name: 'CUCC',
type: 'line',
smooth: true,
symbol: 'circle',

View File

@@ -46,15 +46,16 @@ export default {
const xData = (function() {
const data = []
for (let i = 1; i < 13; i++) {
data.push(i + '月份')
data.push(i + 'month')
}
return data
}())
this.chart.setOption({
backgroundColor: '#344b58',
title: {
text: '统计',
x: '4%',
text: 'statistics',
x: '20',
top: '20',
textStyle: {
color: '#fff',
fontSize: '22'
@@ -81,12 +82,12 @@ export default {
}
},
legend: {
x: '15%',
x: '5%',
top: '10%',
textStyle: {
color: '#90979c'
},
data: ['', '', '平均']
data: ['female', 'male', 'average']
},
calculable: true,
xAxis: [{
@@ -158,9 +159,9 @@ export default {
end: 35
}],
series: [{
name: '',
name: 'female',
type: 'bar',
stack: '总量',
stack: 'total',
barMaxWidth: 35,
barGap: '10%',
itemStyle: {
@@ -195,9 +196,9 @@ export default {
},
{
name: '',
name: 'male',
type: 'bar',
stack: '总量',
stack: 'total',
itemStyle: {
normal: {
color: 'rgba(0,191,183,1)',
@@ -226,9 +227,9 @@ export default {
220
]
}, {
name: '平均',
name: 'average',
type: 'line',
stack: '总量',
stack: 'total',
symbolSize: 10,
symbol: 'circle',
itemStyle: {

View File

@@ -1,56 +0,0 @@
<template>
<div>
<el-badge :is-dot="true" style="line-height: 30px;" @click.native="dialogTableVisible=true">
<el-button size="small" type="primary">
<svg t="1492682037685" class="bug-svg" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1863"
xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64">
<path d="M969.142857 548.571429q0 14.848-10.861714 25.709714t-25.709714 10.861714l-128 0q0 97.718857-38.290286 165.705143l118.857143 119.442286q10.861714 10.861714 10.861714 25.709714t-10.861714 25.709714q-10.276571 10.861714-25.709714 10.861714t-25.709714-10.861714l-113.152-112.566857q-2.852571 2.852571-8.557714 7.424t-23.990857 16.274286-37.156571 20.845714-46.848 16.566857-55.442286 7.424l0-512-73.142857 0 0 512q-29.147429 0-58.002286-7.716571t-49.700571-18.870857-37.705143-22.272-24.868571-18.578286l-8.557714-8.009143-104.557714 118.272q-11.446857 11.995429-27.428571 11.995429-13.714286 0-24.576-9.142857-10.861714-10.276571-11.702857-25.417143t8.850286-26.587429l115.419429-129.718857q-33.133714-65.133714-33.133714-156.562286l-128 0q-14.848 0-25.709714-10.861714t-10.861714-25.709714 10.861714-25.709714 25.709714-10.861714l128 0 0-168.009143-98.852571-98.852571q-10.861714-10.861714-10.861714-25.709714t10.861714-25.709714 25.709714-10.861714 25.709714 10.861714l98.852571 98.852571 482.304 0 98.852571-98.852571q10.861714-10.861714 25.709714-10.861714t25.709714 10.861714 10.861714 25.709714-10.861714 25.709714l-98.852571 98.852571 0 168.009143 128 0q14.848 0 25.709714 10.861714t10.861714 25.709714zM694.857143 219.428571l-365.714286 0q0-75.995429 53.430857-129.426286t129.426286-53.430857 129.426286 53.430857 53.430857 129.426286z"
p-id="1864"></path>
</svg>
</el-button>
</el-badge>
<el-dialog title="bug日志" :visible.sync="dialogTableVisible">
<el-table :data="logsList">
<el-table-column label="message">
<template slot-scope="scope">
<div>msg:{{ scope.row.err.message }}</div>
<br/>
<div>url: {{scope.row.url}}</div>
</template>
</el-table-column>
<el-table-column label="stack">
<template slot-scope="scope">
{{ scope.row.err.stack}}
</template>
</el-table-column>
</el-table>
</el-dialog>
</div>
</template>
<script>
export default {
name: 'errLog',
props: {
logsList: {
type: Array
}
},
data() {
return {
dialogTableVisible: false
}
}
}
</script>
<style scoped>
.bug-svg {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
</style>

View File

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

View File

@@ -105,7 +105,7 @@
<script>
/* eslint-disable */
import {effectRipple, data2blob} from './utils';
import request from 'utils/request';
import request from '@/utils/request';
import langBag from './lang';
const mimes = {
'jpg': 'image/jpeg',

View File

@@ -0,0 +1,41 @@
<template>
<el-dropdown trigger="click" class='international' @command="handleSetLanguage">
<div>
<svg-icon class-name='international-icon' icon-class="language" />
</div>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="zh" :disabled="language==='zh'">中文</el-dropdown-item>
<el-dropdown-item command="en" :disabled="language==='en'">English</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
<script>
export default {
computed: {
language() {
return this.$store.getters.language
}
},
methods: {
handleSetLanguage(lang) {
this.$i18n.locale = lang
this.$store.dispatch('setLanguage', lang)
this.$message({
message: 'switch language success',
type: 'success'
})
}
}
}
</script>
<style scoped>
.international-icon {
font-size: 20px;
cursor: pointer;
vertical-align: -5px!important;
}
</style>

View File

@@ -1,5 +1,5 @@
<template>
<div class="scroll-container" ref="scrollContainer" @mousewheel="handleScroll">
<div class="scroll-container" ref="scrollContainer" @wheel.prevent="handleScroll" >
<div class="scroll-wrapper" ref="scrollWrapper" :style="{top: top + 'px'}">
<slot></slot>
</div>
@@ -8,6 +8,7 @@
<script>
const delta = 15
export default {
name: 'scrollBar',
data() {
@@ -17,19 +18,19 @@ export default {
},
methods: {
handleScroll(e) {
e.preventDefault()
const eventDelta = e.wheelDelta || -e.deltaY * 3
const $container = this.$refs.scrollContainer
const $containerHeight = $container.offsetHeight
const $wrapper = this.$refs.scrollWrapper
const $wrapperHeight = $wrapper.offsetHeight
if (e.wheelDelta > 0) {
this.top = Math.min(0, this.top + e.wheelDelta)
if (eventDelta > 0) {
this.top = Math.min(0, this.top + eventDelta)
} else {
if ($containerHeight - delta < $wrapperHeight) {
if (this.top < -($wrapperHeight - $containerHeight + delta)) {
this.top = this.top
} else {
this.top = Math.max(this.top + e.wheelDelta, $containerHeight - $wrapperHeight - delta)
this.top = Math.max(this.top + eventDelta, $containerHeight - $wrapperHeight - delta)
}
} else {
this.top = 0

View File

@@ -1,5 +1,5 @@
<template>
<div class="scroll-container" ref="scrollContainer" @mousewheel="handleScroll">
<div class="scroll-container" ref="scrollContainer" @wheel.prevent="handleScroll">
<div class="scroll-wrapper" ref="scrollWrapper" :style="{left: left + 'px'}">
<slot></slot>
</div>
@@ -18,20 +18,20 @@ export default {
},
methods: {
handleScroll(e) {
e.preventDefault()
const eventDelta = e.wheelDelta || -e.deltaY * 3
const $container = this.$refs.scrollContainer
const $containerWidth = $container.offsetWidth
const $wrapper = this.$refs.scrollWrapper
const $wrapperWidth = $wrapper.offsetWidth
if (e.wheelDelta > 0) {
this.left = Math.min(0, this.left + e.wheelDelta)
if (eventDelta > 0) {
this.left = Math.min(0, this.left + eventDelta)
} else {
if ($containerWidth - padding < $wrapperWidth) {
if (this.left < -($wrapperWidth - $containerWidth + padding)) {
this.left = this.left
} else {
this.left = Math.max(this.left + e.wheelDelta, $containerWidth - $wrapperWidth - padding)
this.left = Math.max(this.left + eventDelta, $containerWidth - $wrapperWidth - padding)
}
} else {
this.left = 0
@@ -64,6 +64,7 @@ export default {
white-space: nowrap;
position: relative;
overflow: hidden;
width: 100%;
.scroll-wrapper {
position: absolute;
}

View File

@@ -7,7 +7,6 @@
</el-upload>
<div class="image-preview image-app-preview">
<div class="image-preview-wrapper" v-show="imageUrl.length>1">
<div class='app-fake-conver'>&nbsp&nbsp全球 付费节目单 最热 经济</div>
<img :src="imageUrl">
<div class="image-preview-action">
<i @click="rmImage" class="el-icon-delete"></i>

View File

@@ -1,7 +1,10 @@
<template>
<div>
<el-button :loading="loading" type="primary" @click="handleUpload">select excel file</el-button>
<input id="excel-upload-input" type="file" accept=".xlsx, .xls" class="c-hide" @change="handkeFileChange">
<div id="drop" @drop="handleDrop" @dragover="handleDragover" @dragenter="handleDragover">
Drop excel file here or
<el-button style="margin-left:16px;" size="mini" type="primary" @click="handleUpload">browse</el-button>
</div>
</div>
</template>
@@ -22,16 +25,35 @@ export default {
generateDate({ header, results }) {
this.excelData.header = header
this.excelData.results = results
this.loading = false
this.$emit('on-selected-file', this.excelData)
},
handleDrop(e) {
e.stopPropagation()
e.preventDefault()
const files = e.dataTransfer.files
if (files.length !== 1) {
this.$message.error('Only support uploading one file!')
return
}
const itemFile = files[0] // only use files[0]
this.readerData(itemFile)
e.stopPropagation()
e.preventDefault()
},
handleDragover(e) {
e.stopPropagation()
e.preventDefault()
e.dataTransfer.dropEffect = 'copy'
},
handleUpload() {
document.getElementById('excel-upload-input').click()
},
handkeFileChange(e) {
this.loading = true
const files = e.target.files
const itemFile = files[0] // only use files[0]
this.readerData(itemFile)
},
readerData(itemFile) {
const reader = new FileReader()
reader.onload = e => {
const data = e.target.result
@@ -75,4 +97,16 @@ export default {
display: none;
z-index: -9999;
}
#drop{
border: 2px dashed #bbb;
width: 600px;
height: 160px;
line-height: 160px;
margin: 0 auto;
font-size: 24px;
border-radius: 5px;
text-align: center;
color: #bbb;
position: relative;
}
</style>

View File

@@ -1,14 +1,21 @@
import Vue from 'vue'
import errLog from '@/store/errLog'
import store from './store'
// 生产环境错误日志
if (process.env.NODE_ENV === 'production') {
Vue.config.errorHandler = function(err, vm) {
console.log(err, window.location.href)
errLog.pushLog({
// you can set only in production env show the error-log
// if (process.env.NODE_ENV === 'production') {
Vue.config.errorHandler = function(err, vm, info, a) {
// Don't ask me why I use Vue.nextTick, it just a hack.
// detail see https://forum.vuejs.org/t/dispatch-in-vue-config-errorhandler-has-some-problem/23500
Vue.nextTick(() => {
store.dispatch('addErrorLog', {
err,
url: window.location.href,
vm
vm,
info,
url: window.location.href
})
}
console.error(err, info)
})
}
// }

View File

@@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1503994155726" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8554" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><defs><style type="text/css"></style></defs><path d="M44.521739 0h44.521739v979.478261H44.521739zM267.130435 534.26087h44.521739v445.217391H267.130435zM489.73913 311.652174h44.52174v667.826087h-44.52174zM712.347826 712.347826h44.521739v267.130435h-44.521739zM934.956522 445.217391h44.521739v534.26087h-44.521739z" fill="" p-id="8555"></path></svg>

Before

Width:  |  Height:  |  Size: 678 B

View File

@@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1503994177895" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8894" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><defs><style type="text/css"></style></defs><path d="M712.347826 0h44.521739v979.478261h-44.521739zM267.130435 534.26087h44.521739v445.217391H267.130435zM489.73913 311.652174h44.52174v667.826087h-44.52174zM44.521739 712.347826h44.521739v267.130435H44.521739zM934.956522 445.217391h44.521739v534.26087h-44.521739z" fill="" p-id="8895"></path></svg>

Before

Width:  |  Height:  |  Size: 678 B

View File

@@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1503994166937" class="icon" style="" viewBox="0 0 1131 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8786" xmlns:xlink="http://www.w3.org/1999/xlink" width="70.6875" height="64"><defs><style type="text/css"></style></defs><path d="M0 0h53.894737v970.105263H0zM269.473684 431.157895h53.894737v538.947368H269.473684zM538.947368 161.684211h53.894737v808.421052h-53.894737zM808.421053 646.736842h53.894736v323.368421h-53.894736zM1077.894737 323.368421h53.894737v646.736842h-53.894737z" fill="" p-id="8787"></path></svg>

Before

Width:  |  Height:  |  Size: 673 B

View File

@@ -20,7 +20,7 @@ export default {
backToTop: 'BackToTop',
charts: 'Charts',
keyboardChart: 'Keyboard Chart',
lineChart: 'Line chart',
lineChart: 'Line Chart',
mixChart: 'Mix Chart',
example: 'Example',
Table: 'Table',
@@ -44,5 +44,83 @@ export default {
theme: 'Theme',
clipboardDemo: 'Clipboard',
i18n: 'I18n'
},
navbar: {
logOut: 'Log Out',
dashboard: 'Dashboard',
github: 'Github',
screenfull: 'screenfull',
theme: 'theme'
},
login: {
title: 'Login Form',
logIn: 'Log in',
username: 'Username',
password: 'Password',
any: 'any',
thirdparty: 'Or connect with',
thirdpartyTips: 'Can not be simulated on local, so please combine you own business simulation! ! !'
},
documentation: {
documentation: 'Documentation',
github: 'Github Repository'
},
permission: {
roles: 'Your roles',
switchRoles: 'Switch roles'
},
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.',
dropzoneTips: 'Because my business has special needs, and has to upload images to qiniu, so instead of a third party, I chose encapsulate it by myself. It is very simple, you can see the detail code in @/components/Dropzone.',
stickyTips: 'when the page is scrolled to the preset position will be sticky on the top.',
backToTopTips1: 'When the page is scrolled to the specified position, the Back to Top button appears in the lower right corner',
backToTopTips2: 'You can customize the style of the button, show / hide, height of appearance, height of the return. If you need a text prompt, you can use element-ui el-tooltip elements externally',
imageUploadTips: 'Since I was using only the vue@1 version, and it is not compatible with mockjs at the moment, I modified it myself, and if you are going to use it, it is better to use official version.'
},
table: {
dynamicTips1: 'Fixed header, sorted by header order',
dynamicTips2: 'Not fixed header, sorted by click order',
dragTips1: 'The default order',
dragTips2: 'The after dragging order',
title: 'Title',
importance: 'Imp',
type: 'Type',
remark: 'Remark',
search: 'Search',
add: 'Add',
export: 'Export',
reviewer: 'reviewer',
id: 'ID',
date: 'Date',
author: 'Author',
readings: 'Readings',
status: 'Status',
actions: 'Actions',
edit: 'Edit',
publish: 'Publish',
draft: 'Draft',
delete: 'Delete',
cancel: 'Cancel',
confirm: 'Confirm'
},
errorLog: {
tips: 'Please click the bug icon in the upper right corner',
description: 'Now the management system are basically the form of the spa, it enhances the user experience, but it also increases the possibility of page problems, a small negligence may lead to the entire page deadlock. Fortunately Vue provides a way to catch handling exceptions, where you can handle errors or report exceptions.',
documentation: 'Document introduction'
},
excel: {
export: 'Export',
selectedExport: 'Export selected items',
placeholder: 'Please enter the file name(default excel-list)'
},
zip: {
export: 'Export',
placeholder: 'Please enter the file name(default file)'
},
theme: {
change: 'Theme change',
documentation: 'Theme documentation',
tips: 'Tips: It is different from the theme-pick on the navbar is two different skinning methods, each with different application scenarios. Refer to the documentation for details.'
}
}

View File

@@ -20,7 +20,7 @@ const messages = {
}
const i18n = new VueI18n({
locale: Cookies.get('language') || 'zh', // set locale
locale: Cookies.get('language') || 'en', // set locale
messages // set locale messages
})

View File

@@ -44,5 +44,83 @@ export default {
theme: '换肤',
clipboardDemo: 'clipboard',
i18n: '国际化'
},
navbar: {
logOut: '退出登录',
dashboard: '首页',
github: '项目地址',
screenfull: '全屏',
theme: '换肤'
},
login: {
title: '系统登录',
logIn: '登录',
username: '账号',
password: '密码',
any: '随便填',
thirdparty: '第三方登录',
thirdpartyTips: '本地不能模拟,请结合自己业务进行模拟!!!'
},
documentation: {
documentation: '文档',
github: 'Github 地址'
},
permission: {
roles: '你的权限',
switchRoles: '切换权限'
},
components: {
documentation: '文档',
tinymceTips: '富文本是管理后台一个核心的功能但同时又是一个有很多坑的地方。在选择富文本的过程中我也走了不少的弯路市面上常见的富文本都基本用过了最终权衡了一下选择了Tinymce。更详细的富文本比较和介绍见',
dropzoneTips: '由于我司业务有特殊需求,而且要传七牛 所以没用第三方,选择了自己封装。代码非常的简单,具体代码你可以在这里看到 @/components/Dropzone',
stickyTips: '当页面滚动到预设的位置会吸附在顶部',
backToTopTips1: '页面滚动到指定位置会在右下角出现返回顶部按钮',
backToTopTips2: '可自定义按钮的样式、show/hide、出现的高度、返回的位置 如需文字提示可在外部使用Element的el-tooltip元素',
imageUploadTips: '由于我在使用时它只有vue@1版本而且和mockjs不兼容所以自己改造了一下如果大家要使用的话优先还是使用官方版本。'
},
table: {
dynamicTips1: '固定表头, 按照表头顺序排序',
dynamicTips2: '不固定表头, 按照点击顺序排序',
dragTips1: '默认顺序',
dragTips2: '拖拽后顺序',
title: '标题',
importance: '重要性',
type: '类型',
remark: '点评',
search: '搜索',
add: '添加',
export: '导出',
reviewer: '审核人',
id: '序号',
date: '时间',
author: '作者',
readings: '阅读数',
status: '状态',
actions: '操作',
edit: '编辑',
publish: '发布',
draft: '草稿',
delete: '删除',
cancel: '取 消',
confirm: '确 定'
},
errorLog: {
tips: '请点击右上角bug小图标',
description: '现在的管理后台基本都是spa的形式了它增强了用户体验但同时也会增加页面出问题的可能性可能一个小小的疏忽就导致整个页面的死锁。好在 Vue 官网提供了一个方法来捕获处理异常,你可以在其中进行错误处理或者异常上报。',
documentation: '文档介绍'
},
excel: {
export: '导出',
selectedExport: '导出已选择项',
placeholder: '请输入文件名(默认excel-list)'
},
zip: {
export: '导出',
placeholder: '请输入文件名(默认file)'
},
theme: {
change: '换肤',
documentation: '换肤文档',
tips: 'Tips: 它区别于 navbar 上的 theme-pick, 是两种不同的换肤方法,各自有不同的应用场景,具体请参考文档。'
}
}

View File

@@ -1,17 +1,26 @@
import Vue from 'vue'
import 'normalize.css/normalize.css'// A modern alternative to CSS resets
import Element from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import i18n from './lang' // 国际化
import '@/styles/index.scss' // global css
import App from './App'
import router from './router'
import store from './store'
import * as filters from './filters' // 全局filter
import i18n from './lang' // Internationalization
import './icons' // icon
import './errorLog'// error log
import './permission' // 权限
import './mock' // 该项目所有请求使用mockjs模拟
import './permission' // permission control
import './mock' // simulation data
import * as filters from './filters' // global filters
Vue.use(Element, {
size: 'medium', // set element-ui default size
i18n: (key, value) => i18n.t(key, value)
})

View File

@@ -8,9 +8,9 @@ for (let i = 0; i < count; i++) {
List.push(Mock.mock({
id: '@increment',
timestamp: +Mock.Random.date('T'),
author: '@cname',
auditor: '@cname',
title: '@ctitle(10, 20)',
author: '@first',
reviewer: '@first',
title: '@title(5, 10)',
forecast: '@float(0, 100, 2, 2)',
importance: '@integer(1, 3)',
'type|1': ['CN', 'US', 'JP', 'EU'],
@@ -43,7 +43,7 @@ export default {
}
},
getPv: () => ({
pvData: [{ key: 'PC网站', pv: 1024 }, { key: 'mobile网站', pv: 1024 }, { key: 'ios', pv: 1024 }, { key: 'android', pv: 1024 }]
pvData: [{ key: 'PC', pv: 1024 }, { key: 'mobile', pv: 1024 }, { key: 'ios', pv: 1024 }, { key: 'android', pv: 1024 }]
}),
getArticle: () => ({
id: 120000000001,

View File

@@ -2,14 +2,14 @@ import { param2Obj } from '@/utils'
const userMap = {
admin: {
role: ['admin'],
roles: ['admin'],
token: 'admin',
introduction: '我是超级管理员',
avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
name: 'Super Admin'
},
editor: {
role: ['editor'],
roles: ['editor'],
token: 'editor',
introduction: '我是编辑',
avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',

View File

@@ -1,60 +1,63 @@
import router from './router'
import store from './store'
import NProgress from 'nprogress' // Progress 进度条
import 'nprogress/nprogress.css'// Progress 进度条样式
import { getToken } from '@/utils/auth' // 验权
import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css'// progress bar style
import { getToken } from '@/utils/auth' // getToken from cookie
// permissiom judge
NProgress.configure({ showSpinner: false })// NProgress Configuration
// permissiom judge function
function hasPermission(roles, permissionRoles) {
if (roles.indexOf('admin') >= 0) return true // admin权限 直接通过
if (roles.indexOf('admin') >= 0) return true // admin permission passed directly
if (!permissionRoles) return true
return roles.some(role => permissionRoles.indexOf(role) >= 0)
}
const whiteList = ['/login', '/authredirect']// 不重定向白名单
const whiteList = ['/login', '/authredirect']// no redirect whitelist
router.beforeEach((to, from, next) => {
NProgress.start() // 开启Progress
if (getToken()) { // 判断是否有token
NProgress.start() // start progress bar
if (getToken()) { // determine if there has token
/* has token*/
if (to.path === '/login') {
next({ path: '/' })
NProgress.done() // router在hash模式下 手动改变hash 重定向回来 不会触发afterEach 暂时hack方案 pshistory模式下无问题可删除该行
NProgress.done() // if current page is dashboard will not trigger afterEach hook, so manually handle it
} else {
if (store.getters.roles.length === 0) { // 判断当前用户是否已拉取完user_info信息
store.dispatch('GetUserInfo').then(res => { // 拉取user_info
const roles = res.data.role
store.dispatch('GenerateRoutes', { roles }).then(() => { // 生成可访问的路由表
const roles = res.data.roles // note: roles must be a array! such as: ['editor','develop']
store.dispatch('GenerateRoutes', { roles }).then(() => { // 根据roles权限生成可访问的路由表
router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,replace: true so the navigation will not leave a history record
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
})
}).catch(() => {
store.dispatch('FedLogOut').then(() => {
Message.error('验证失败,请重新登录')
Message.error('Verification failed, please login again')
next({ path: '/login' })
})
})
} else {
// 没有动态改变权限的需求可直接next() 删除下方权限判断 ↓
if (hasPermission(store.getters.roles, to.meta.role)) {
if (hasPermission(store.getters.roles, to.meta.roles)) {
next()//
} else {
next({ path: '/401', query: { noGoBack: true }})
NProgress.done() // router在hash模式下 手动改变hash 重定向回来 不会触发afterEach 暂时hack方案 pshistory模式下无问题可删除该行
next({ path: '/401', replace: true, query: { noGoBack: true }})
}
// 可删 ↑
}
}
} else {
/* has no token*/
if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入
next()
} else {
next('/login') // 否则全部重定向到登录页
NProgress.done() // router在hash模式下 手动改变hash 重定向回来 不会触发afterEach 暂时hack方案 pshistory模式下无问题可删除该行
NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
}
}
})
router.afterEach(() => {
NProgress.done() // 结束Progress
NProgress.done() // finish progress bar
})

View File

@@ -9,12 +9,16 @@ Vue.use(Router)
/* Layout */
import Layout from '../views/layout/Layout'
/** note: submenu only apppear when children.length>=1
* detail see https://panjiachen.github.io/vue-element-admin-site/#/router-and-nav?id=sidebar
**/
/**
* hidden: true if `hidden:true` will not show in the sidebar(default is false)
* redirect: noredirect if `redirect:noredirect` will no redirct in the breadcrumb
* name:'router-name' the name is used by <keep-alive> (must set!!!)
* meta : {
role: ['admin','editor'] will control the page role (you can set multiple roles)
roles: ['admin','editor'] will control the page roles (you can set multiple roles)
title: 'title' the name show in submenu and breadcrumb (recommend set)
icon: 'svg-name' the icon show in the sidebar,
noCache: true if fasle ,the page will no be cached(default is false)
@@ -50,7 +54,7 @@ export const constantRouterMap = [
]
export default new Router({
// mode: 'history', //后端支持可开
// mode: 'history', // require service support
scrollBehavior: () => ({ y: 0 }),
routes: constantRouterMap
})
@@ -60,7 +64,7 @@ export const asyncRouterMap = [
path: '/permission',
component: Layout,
redirect: '/permission/index',
meta: { role: ['admin'] },
meta: { roles: ['admin'] }, // you can set roles in root nav
children: [{
path: 'index',
component: _import('permission/index'),
@@ -68,7 +72,7 @@ export const asyncRouterMap = [
meta: {
title: 'permission',
icon: 'lock',
role: ['admin']
roles: ['admin'] // or you can only set roles in sub nav
}
}]
},

View File

@@ -1,13 +0,0 @@
const errLog = {
state: {
errLog: []
},
pushLog(log) {
this.state.errLog.unshift(log)
},
clearLog() {
this.state.errLog = []
}
}
export default errLog

View File

@@ -11,6 +11,7 @@ const getters = {
roles: state => state.user.roles,
setting: state => state.user.setting,
permission_routers: state => state.permission.routers,
addRouters: state => state.permission.addRouters
addRouters: state => state.permission.addRouters,
errorLogs: state => state.errorLog.logs
}
export default getters

View File

@@ -1,6 +1,7 @@
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 user from './modules/user'
@@ -11,6 +12,7 @@ Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
app,
errorLog,
permission,
tagsView,
user

View File

@@ -5,7 +5,7 @@ const app = {
sidebar: {
opened: !+Cookies.get('sidebarStatus')
},
language: Cookies.get('language') || 'zh'
language: Cookies.get('language') || 'en'
},
mutations: {
TOGGLE_SIDEBAR: state => {

View File

@@ -0,0 +1,17 @@
const errorLog = {
state: {
logs: []
},
mutations: {
ADD_ERROR_LOG: (state, log) => {
state.logs.push(log)
}
},
actions: {
addErrorLog({ commit }, log) {
commit('ADD_ERROR_LOG', log)
}
}
}
export default errorLog

View File

@@ -6,8 +6,8 @@ import { asyncRouterMap, constantRouterMap } from '@/router'
* @param route
*/
function hasPermission(roles, route) {
if (route.meta && route.meta.role) {
return roles.some(role => route.meta.role.indexOf(role) >= 0)
if (route.meta && route.meta.roles) {
return roles.some(role => route.meta.roles.indexOf(role) >= 0)
} else {
return true
}

View File

@@ -25,10 +25,29 @@ const tagsView = {
for (const i of state.cachedViews) {
if (i === view.name) {
const index = state.cachedViews.indexOf(i)
state.cachedViews.splice(index, 1)
state.cachedViews.splice(index, index + 1)
break
}
}
},
DEL_OTHERS_VIEWS: (state, view) => {
for (const [i, v] of state.visitedViews.entries()) {
if (v.path === view.path) {
state.visitedViews = state.visitedViews.slice(i, i + 1)
break
}
}
for (const i of state.cachedViews) {
if (i === view.name) {
const index = state.cachedViews.indexOf(i)
state.cachedViews = state.cachedViews.slice(index, i + 1)
break
}
}
},
DEL_ALL_VIEWS: (state) => {
state.visitedViews = []
state.cachedViews = []
}
},
actions: {
@@ -40,6 +59,18 @@ const tagsView = {
commit('DEL_VISITED_VIEWS', view)
resolve([...state.visitedViews])
})
},
delOthersViews({ commit, state }, view) {
return new Promise((resolve) => {
commit('DEL_OTHERS_VIEWS', view)
resolve([...state.visitedViews])
})
},
delAllViews({ commit, state }) {
return new Promise((resolve) => {
commit('DEL_ALL_VIEWS')
resolve([...state.visitedViews])
})
}
}
}

View File

@@ -67,7 +67,7 @@ const user = {
reject('error')
}
const data = response.data
commit('SET_ROLES', data.role)
commit('SET_ROLES', data.roles)
commit('SET_NAME', data.name)
commit('SET_AVATAR', data.avatar)
commit('SET_INTRODUCTION', data.introduction)
@@ -116,13 +116,13 @@ const user = {
},
// 动态修改权限
ChangeRole({ commit }, role) {
ChangeRoles({ commit }, role) {
return new Promise(resolve => {
commit('SET_TOKEN', role)
setToken(role)
getUserInfo(role).then(response => {
const data = response.data
commit('SET_ROLES', data.role)
commit('SET_ROLES', data.roles)
commit('SET_NAME', data.name)
commit('SET_AVATAR', data.avatar)
commit('SET_INTRODUCTION', data.introduction)

View File

@@ -1,5 +1,9 @@
//覆盖一些element-ui样式
.el-breadcrumb__inner, .el-breadcrumb__inner a{
font-weight: 400!important;
}
.el-upload {
input[type="file"] {
display: none !important;
@@ -23,6 +27,13 @@
}
}
.fixed-width{
.el-button--mini{
padding: 7px 10px;
width: 60px;
}
}
.status-col {
.cell {
padding: 0 10px;

View File

@@ -102,7 +102,8 @@ code {
margin-bottom: 20px;
display: block;
line-height: 36px;
font-size: 14px;
font-size: 15px;
font-family: "Source Sans Pro", "Helvetica Neue", Arial, sans-serif;
a {
color: #337ab7;
cursor: pointer;

View File

@@ -3,7 +3,7 @@ import Clipboard from 'clipboard'
function clipboardSuccess() {
Vue.prototype.$message({
message: '复制成功',
message: 'Copy successfully',
type: 'success',
duration: 1500
})
@@ -11,7 +11,7 @@ function clipboardSuccess() {
function clipboardError() {
Vue.prototype.$message({
message: '复制失败',
message: 'Copy failed',
type: 'error'
})
}

View File

@@ -1,3 +1,10 @@
// translate router.meta.title, be used in breadcrumb sidebar tagsview
export function generateTitle(title) {
return this.$t('route.' + title) // $t :this method from vue-i18n ,inject in @/lang/index.js
const hasKey = this.$te('route.' + title)
const translatedTitle = this.$t('route.' + title) // $t :this method from vue-i18n, inject in @/lang/index.js
if (hasKey) {
return translatedTitle
}
return title
}

View File

@@ -3,17 +3,17 @@ import { Message } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'
// 创建axios实例
// create an axios instance
const service = axios.create({
baseURL: process.env.BASE_API, // api的base_url
timeout: 5000 // 请求超时时间
timeout: 5000 // request timeout
})
// request拦截器
// request interceptor
service.interceptors.request.use(config => {
// Do something before request is sent
if (store.getters.token) {
config.headers['X-Token'] = getToken() // 让每个请求携带token--['X-Token']为自定义key 请根据实际情况自行修改
config.headers['X-Token'] = getToken() // 让每个请求携带token-- ['X-Token']为自定义key 请根据实际情况自行修改
}
return config
}, error => {
@@ -22,7 +22,7 @@ service.interceptors.request.use(config => {
Promise.reject(error)
})
// respone拦截器
// respone interceptor
service.interceptors.response.use(
response => response,
/**

View File

@@ -1,6 +1,6 @@
/* eslint-disable */
require('script-loader!file-saver');
require('script-loader!vendor/Blob');
require('script-loader!@/vendor/Blob');
import XLSX from 'xlsx'
function generateArray(table) {

View File

@@ -1,8 +1,6 @@
<template>
<div class="components-container" style='height:100vh'>
<div class='chart-container'>
<chart height='100%' width='100%'></chart>
</div>
<div class='chart-container'>
<chart height='100%' width='100%'></chart>
</div>
</template>
@@ -18,8 +16,9 @@ export default {
<style scoped>
.chart-container{
position: relative;
padding: 20px;
width: 100%;
height: 90%;
height: 85vh;
}
</style>

View File

@@ -1,8 +1,6 @@
<template>
<div class="components-container" style='height:100vh'>
<div class='chart-container'>
<chart height='100%' width='100%'></chart>
</div>
<div class='chart-container'>
<chart height='100%' width='100%'></chart>
</div>
</template>
@@ -18,8 +16,9 @@ export default {
<style scoped>
.chart-container{
position: relative;
padding:20px;
width: 100%;
height: 80%;
height:85vh;
}
</style>

View File

@@ -1,8 +1,6 @@
<template>
<div class="components-container" style='height:100vh'>
<div class='chart-container'>
<chart height='100%' width='100%'></chart>
</div>
<div class='chart-container'>
<chart height='100%' width='100%'></chart>
</div>
</template>
@@ -18,9 +16,9 @@ export default {
<style scoped>
.chart-container{
position: relative;
padding: 20px;
width: 100%;
height: 90%;
padding-bottom: 40px;
height:85vh;
}
</style>

View File

@@ -2,11 +2,11 @@
<div class="app-container">
<el-tabs v-model="activeName">
<el-tab-pane label="use clipboard directly" name="directly">
<el-input v-model="inputData" placeholder="请输入内容" style='width:400px;'></el-input>
<el-input v-model="inputData" placeholder="Please input" style='width:400px;'></el-input>
<el-button type="primary" icon="document" @click='handleCopy(inputData,$event)'>copy</el-button>
</el-tab-pane>
<el-tab-pane label="use clipboard by v-directive" name="v-directive">
<el-input v-model="inputData" placeholder="请输入内容" style='width:400px;'></el-input>
<el-input v-model="inputData" placeholder="Please input" style='width:400px;'></el-input>
<el-button type="primary" icon="document" v-clipboard:copy='inputData' v-clipboard:success='clipboardSuccess'>copy</el-button>
</el-tab-pane>
</el-tabs>

View File

@@ -1,16 +1,16 @@
<template>
<div class="components-container">
<code>这里核心代码用的是
<a class="link-type" href="//github.com/dai-siki/vue-image-crop-upload"> vue-image-crop-upload</a>
由于我在使用时它只有vue@1版本而且有些业务的需求耦合到七牛等等原因吧自己改造了一下如果大家要使用的话优先还是使用官方component
<code>This is based on
<a class="link-type" href="//github.com/dai-siki/vue-image-crop-upload"> vue-image-crop-upload</a>.
{{$t('components.imageUploadTips')}}
</code>
<pan-thumb :image="image"></pan-thumb>
<el-button type="primary" icon="upload" style="position: absolute;bottom: 15px;margin-left: 40px;" @click="imagecropperShow=true">修改头像
<el-button type="primary" icon="upload" style="position: absolute;bottom: 15px;margin-left: 40px;" @click="imagecropperShow=true">Change avatar
</el-button>
<image-cropper :width="300" :height="300" url="https://httpbin.org/post" @close='close' @crop-upload-success="cropSuccess"
<image-cropper :width="300" :height="300" url="https://httpbin.org/post" @close='close' @crop-upload-success="cropSuccess" langType="en"
:key="imagecropperKey" v-show="imagecropperShow"></image-cropper>
</div>
</template>

View File

@@ -1,134 +1,121 @@
<template>
<div class="components-container">
<code>页面滚动到指定位置会在右下角出现返回顶部按钮</code>
<code>可自定义按钮的样式show/hide临界点返回的位置 如需文字提示可在外部使用Element的el-tooltip元素 </code>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<code>{{$t('components.backToTopTips1')}}</code>
<code>{{$t('components.backToTopTips2')}}</code>
<div class="placeholder-container">
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
</div>
<!--可自定义按钮的样式show/hide临界点返回的位置 -->
<!--如需文字提示可在外部添加element的<el-tooltip></el-tooltip>元素 -->
<el-tooltip placement="top" content="文字提示">
<el-tooltip placement="top" content="tooltip">
<back-to-top transitionName="fade" :customStyle="myBackToTopStyle" :visibilityHeight="300" :backPosition="50"></back-to-top>
</el-tooltip>
</div>
@@ -148,10 +135,16 @@ export default {
width: '40px',
height: '40px',
'border-radius': '4px',
'line-height': '45px', // 请保持与高度一致以垂直居中
background: '#e7eaf1'// 按钮的背景颜色
'line-height': '45px', // 请保持与高度一致以垂直居中 Please keep consistent with height to center vertically
background: '#e7eaf1'// 按钮的背景颜色 The background color of the button
}
}
}
}
</script>
</script>
<style scoped>
.placeholder-container div {
margin: 10px;
}
</style>

View File

@@ -4,7 +4,7 @@
<a href="https://github.com/SortableJS/Vue.Draggable" target="_blank">Vue.Draggable</a>
</code>
<div class="editor-container">
<dnd-list :list1="list1" :list2="list2" list1Title="头条列表" list2Title="文章池"></dnd-list>
<dnd-list :list1="list1" :list2="list2" list1Title="List" list2Title="Article pool"></dnd-list>
</div>
</div>
</template>

View File

@@ -1,8 +1,8 @@
<template>
<div class="components-container">
<code>
基于<a class="link-type" href="https://github.com/rowanwins/vue-dropzone"> dropzone </a>封装 ,
由于我司业务有特殊需求而且要传七牛 所以没用第三方 选择了自己封装
Based on <a class="link-type" href="https://github.com/rowanwins/vue-dropzone"> dropzone </a>.
{{$t('components.dropzoneTips')}}
</code>
<div class="editor-container">
<dropzone v-on:dropzone-removedFile="dropzoneR" v-on:dropzone-success="dropzoneS" id="myVueDropzone" url="https://httpbin.org/post"></dropzone>
@@ -19,11 +19,11 @@ export default {
methods: {
dropzoneS(file) {
console.log(file)
this.$message({ message: '上传成功', type: 'success' })
this.$message({ message: 'Upload success', type: 'success' })
},
dropzoneR(file) {
console.log(file)
this.$message({ message: '删除成功', type: 'success' })
this.$message({ message: 'Delete success', type: 'success' })
}
}
}

View File

@@ -1,14 +1,14 @@
<template>
<div class="components-container">
<code>Markdown 我们这里选用了
<a href="https://github.com/sparksuite/simplemde-markdown-editor" target="_blank">simplemde-markdown-editor</a> 简单的用vue封装了一下
<code>Markdown is based on
<a href="https://github.com/sparksuite/simplemde-markdown-editor" target="_blank">simplemde-markdown-editor</a> Simply encapsulated in Vue.
<a target="_blank" href="https://segmentfault.com/a/1190000009762198#articleHeader14">
相关文章 </a>
</code>
<div class="editor-container">
<markdown-editor id="contentEditor" ref="contentEditor" v-model="content" :height="300" :zIndex="20"></markdown-editor>
</div>
<el-button @click="markdown2Html" style="margin-top:80px;" type="primary" icon="el-icon-document">转为HTML</el-button>
<el-button @click="markdown2Html" style="margin-top:80px;" type="primary" icon="el-icon-document">To HTML</el-button>
<div v-html="html"></div>
</div>
</template>

View File

@@ -1,9 +1,9 @@
<template>
<div class="components-container">
<code>splitPane 如果你用过
<code><strong>SplitPane</strong> If you've used
<a href="http://codepen.io/" target="_blank"> codepen</a>,
<a href="https://jsfiddle.net/" target="_blank"> jsfiddle </a>就不会陌生了
<a href="https://github.com/PanJiaChen/vue-split-pane" target='_blank'>项目地址</a>
<a href="https://jsfiddle.net/" target="_blank"> jsfiddle </a>will not be unfamiliar.
<a href="https://github.com/PanJiaChen/vue-split-pane" target='_blank'> Github repository</a>
</code>
<split-pane v-on:resize="resize" split="vertical">
<template slot="paneL">

View File

@@ -2,8 +2,8 @@
<div>
<sticky className="sub-navbar">
<el-dropdown trigger="click">
<el-button>
平台<i class="el-icon-caret-bottom el-icon--right"></i>
<el-button plain>
Platform<i class="el-icon-caret-bottom el-icon--right"></i>
</el-button>
<el-dropdown-menu class="no-border" slot="dropdown">
<el-checkbox-group v-model="platforms" style="padding: 5px 15px;">
@@ -15,79 +15,79 @@
</el-dropdown>
<el-dropdown trigger="click">
<el-button>
外链<i class="el-icon-caret-bottom el-icon--right"></i>
<el-button plain>
Link<i class="el-icon-caret-bottom el-icon--right"></i>
</el-button>
<el-dropdown-menu class="no-padding no-border" style="width:300px" slot="dropdown">
<el-input placeholder="请输入内容" v-model="url">
<template slot="prepend">填写url</template>
<el-input placeholder="Please enter the content" v-model="url">
<template slot="prepend">Url</template>
</el-input>
</el-dropdown-menu>
</el-dropdown>
<div class="time-container">
<el-date-picker v-model="time" type="datetime" :picker-options="pickerOptions" format="yyyy-MM-dd HH:mm:ss" placeholder="发布时间">
<el-date-picker v-model="time" type="datetime" :picker-options="pickerOptions" format="yyyy-MM-dd HH:mm:ss" placeholder="Release time">
</el-date-picker>
</div>
<el-button style="margin-left: 10px;" type="success">发布
<el-button style="margin-left: 10px;" type="success">publish
</el-button>
</sticky>
<div class="components-container">
<code>Sticky header 当页面滚动到预设的位置会吸附在顶部</code>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<div>我是占位</div>
<code>Sticky header, {{$t('components.stickyTips')}}</code>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
</div>
</div>
</template>
@@ -105,9 +105,9 @@ export default {
url: '',
platforms: ['a-platform'],
platformsOptions: [
{ key: 'a-platform', name: '平台A' },
{ key: 'b-platform', name: '平台B' },
{ key: 'c-platform', name: '平台C' }
{ key: 'a-platform', name: 'platformA' },
{ key: 'b-platform', name: 'platformB' },
{ key: 'c-platform', name: 'platformC' }
],
pickerOptions: {
disabledDate(time) {
@@ -120,8 +120,11 @@ export default {
</script>
<style scoped>
.time-container {
display: inline-block;
}
</style>
.components-container div {
margin: 10px;
}
.time-container {
display: inline-block;
}
</style>

View File

@@ -1,10 +1,11 @@
<template>
<div class="components-container">
<code>公司做的后台主要是一个cms系统公司也是以自媒体为核心的所以富文本是后台很核心的功能在选择富文本的过程中也走了不少的弯路市面上常见的富文本都基本用过了最终选择了Tinymce
<a target="_blank" class="link-type" href="https://panjiachen.github.io/vue-element-admin-site/#/rich-editor">文档介绍</a>
<code>
{{$t('components.tinymceTips')}}
<a target="_blank" class="link-type" href="https://panjiachen.github.io/vue-element-admin-site/#/rich-editor"> {{$t('components.documentation')}}</a>
</code>
<div>
<tinymce :height="200" v-model="content"></tinymce>
<tinymce :height="300" v-model="content"></tinymce>
</div>
<div class="editor-content" v-html="content"></div>
</div>
@@ -18,7 +19,14 @@ export default {
components: { Tinymce },
data() {
return {
content: 'Tinymce'
content:
`<h1 style="text-align: center;">Welcome to the TinyMCE demo!</h1>
<ul>
<li>Our <a href="//www.tinymce.com/docs/">documentation</a> is a great resource for learning how to configure TinyMCE.</li>
<li>Have a specific question? Visit the <a href="https://community.tinymce.com/forum/">Community Forum</a>.</li>
<li>We also offer enterprise grade support as part of <a href="https://tinymce.com/pricing">TinyMCE premium subscriptions</a>.</li>
</ul>
`
}
}
}

View File

@@ -4,7 +4,7 @@
<script>
import echarts from 'echarts'
require('echarts/theme/macarons') // echarts 主题
require('echarts/theme/macarons') // echarts theme
import { debounce } from '@/utils'
const animationDuration = 6000

View File

@@ -4,7 +4,7 @@
<script>
import echarts from 'echarts'
require('echarts/theme/macarons') // echarts 主题
require('echarts/theme/macarons') // echarts theme
import { debounce } from '@/utils'
export default {

View File

@@ -7,7 +7,7 @@
</div>
<div class="card-panel-description">
<div class="card-panel-text">New Visits</div>
<count-to class="card-panel-num" :startVal="0" :endVal="102400" :duration="3600"></count-to>
<count-to class="card-panel-num" :startVal="0" :endVal="102400" :duration="2600"></count-to>
</div>
</div>
</el-col>
@@ -18,7 +18,7 @@
</div>
<div class="card-panel-description">
<div class="card-panel-text">Messages</div>
<count-to class="card-panel-num" :startVal="0" :endVal="81212" :duration="4000"></count-to>
<count-to class="card-panel-num" :startVal="0" :endVal="81212" :duration="3000"></count-to>
</div>
</div>
</el-col>
@@ -29,7 +29,7 @@
</div>
<div class="card-panel-description">
<div class="card-panel-text">Purchases</div>
<count-to class="card-panel-num" :startVal="0" :endVal="9280" :duration="4000"></count-to>
<count-to class="card-panel-num" :startVal="0" :endVal="9280" :duration="3200"></count-to>
</div>
</div>
</el-col>
@@ -40,7 +40,7 @@
</div>
<div class="card-panel-description">
<div class="card-panel-text">Shoppings</div>
<count-to class="card-panel-num" :startVal="0" :endVal="13600" :duration="4600"></count-to>
<count-to class="card-panel-num" :startVal="0" :endVal="13600" :duration="3600"></count-to>
</div>
</div>
</el-col>

View File

@@ -4,7 +4,7 @@
<script>
import echarts from 'echarts'
require('echarts/theme/macarons') // echarts 主题
require('echarts/theme/macarons') // echarts theme
import { debounce } from '@/utils'
export default {

View File

@@ -4,7 +4,7 @@
<script>
import echarts from 'echarts'
require('echarts/theme/macarons') // echarts 主题
require('echarts/theme/macarons') // echarts theme
import { debounce } from '@/utils'
const animationDuration = 3000

View File

@@ -44,7 +44,7 @@ export default {
fetchList().then(response => {
this.list = response.data.items.slice(0, 7)
})
} }
}
}
}
</script>

View File

@@ -1,13 +1,13 @@
<template>
<div class="dashboard-editor-container">
<div class=" clearfix">
<pan-thumb style="float: left" :image="avatar"> 你的权限:
<pan-thumb style="float: left" :image="avatar"> Your roles:
<span class="pan-info-roles" :key='item' v-for="item in roles">{{item}}</span>
</pan-thumb>
<github-corner></github-corner>
<div class="info-container">
<span class="display_name">{{name}}</span>
<span style="font-size:20px;padding-top:20px;display:inline-block;">普通编辑dashboard</span>
<span style="font-size:20px;padding-top:20px;display:inline-block;">editor : dashboard</span>
</div>
</div>
<div>

View File

@@ -1,16 +1,15 @@
<template>
<div class="app-container documentation-container">
<a class="document-btn" target='_blank' href="https://panjiachen.github.io/vue-element-admin-site/#/">文档</a>
<a class="document-btn" target='_blank' href="https://github.com/PanJiaChen/vue-element-admin/">Github 地址</a>
<a class="document-btn" target='_blank' href="https://panjiachen.github.io/vue-element-admin-site/#/">{{$t('documentation.documentation')}}</a>
<a class="document-btn" target='_blank' href="https://github.com/PanJiaChen/vue-element-admin/">{{$t('documentation.github')}}</a>
<dropdown-menu style="float:left;margin-left:50px;" title='系列文章' :items='articleList'></dropdown-menu>
</div>
</template>
<script>
import DropdownMenu from '@/components/Share/dropdownMenu'
export default {
name: 'clipboardDemo',
name: 'documentation',
components: { DropdownMenu },
data() {
return {

View File

@@ -5,3 +5,9 @@
<!--error code-->
</div>
</template>
<script>
export default {
name: 'errorTestA'
}
</script>

View File

@@ -0,0 +1,11 @@
<template>
<div></div>
</template>
<script>
export default {
created() {
this.b = b // eslint-disable-line
}
}
</script>

View File

@@ -1,21 +1,28 @@
<template>
<div class="errPage-container">
<err-code></err-code>
<h3>请点击右上角bug小图表</h3>
<errorA></errorA>
<errorB></errorB>
<!-- $t is vue-i18n global function to translate lang -->
<h3>{{$t('errorLog.tips')}}</h3>
<code>
现在的管理后台基本都是spa的形式了它增强了用户体验但同时也会增加页面出问题的可能性可能一个小小的疏忽就导致整个页面的死锁好在Vue官网提供了一个方法来捕获处理异常
{{$t('errorLog.description')}}
<a target="_blank" class="link-type" href="https://panjiachen.github.io/vue-element-admin-site/#/error?id=%e4%bb%a3%e7%a0%81">
{{$t('errorLog.documentation')}}
</a>
</code>
<a href="#"><img src='http://panjiachen.github.io/images/errHandler.png'></a>
<a href="#">
<img src='https://wpimg.wallstcn.com/360e4842-4db5-42d0-b078-f9a84a825546.gif'>
</a>
</div>
</template>
<script>
import errCode from './errcode'
import errorA from './errorTestA'
import errorB from './errorTestB'
export default {
name: 'errorLog',
components: { errCode }
components: { errorA, errorB }
}
</script>

View File

@@ -1,45 +1,45 @@
<template>
<el-table :data="list"border fit highlight-current-row style="width: 100%">
<el-table :data="list" border fit highlight-current-row style="width: 100%">
<el-table-column align="center" label="序号" width="65" v-loading="loading"
<el-table-column align="center" label="ID" width="65" v-loading="loading"
element-loading-text="请给我点时间">
<template slot-scope="scope">
<span>{{scope.row.id}}</span>
</template>
</el-table-column>
<el-table-column width="180px" align="center" label="时间">
<el-table-column width="180px" align="center" label="Date">
<template slot-scope="scope">
<span>{{scope.row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}')}}</span>
</template>
</el-table-column>
<el-table-column min-width="300px" label="标题">
<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>
</el-table-column>
<el-table-column width="110px" align="center" label="作者">
<el-table-column width="110px" align="center" label="Author">
<template slot-scope="scope">
<span>{{scope.row.author}}</span>
</template>
</el-table-column>
<el-table-column width="80px" label="重要性">
<el-table-column width="120px" label="Importance">
<template slot-scope="scope">
<svg-icon v-for="n in +scope.row.importance" icon-class="star" :key="n"></svg-icon>
</template>
</el-table-column>
<el-table-column align="center" label="阅读数" width="95">
<el-table-column align="center" label="Readings" width="95">
<template slot-scope="scope">
<span>{{scope.row.pageviews}}</span>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="状态" width="110">
<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}}</el-tag>
</template>

View File

@@ -20,10 +20,10 @@ export default {
data() {
return {
tabMapOptions: [
{ label: '中国', key: 'CN' },
{ label: '美国', key: 'US' },
{ label: '日本', key: 'JP' },
{ label: '欧元区', key: 'EU' }
{ label: 'China', key: 'CN' },
{ label: 'USA', key: 'US' },
{ label: 'Japan', key: 'JP' },
{ label: 'Eurozone', key: 'EU' }
],
activeName: 'CN',
createdTimes: 0

View File

@@ -1,84 +1,84 @@
<template>
<div class="app-container calendar-list-container">
<div class="filter-container">
<el-input @keyup.enter.native="handleFilter" style="width: 200px;" class="filter-item" placeholder="标题" v-model="listQuery.title">
<el-input @keyup.enter.native="handleFilter" style="width: 200px;" class="filter-item" :placeholder="$t('table.title')" v-model="listQuery.title">
</el-input>
<el-select clearable style="width: 90px" class="filter-item" v-model="listQuery.importance" placeholder="重要性">
<el-select clearable style="width: 90px" class="filter-item" v-model="listQuery.importance" :placeholder="$t('table.importance')">
<el-option v-for="item in importanceOptions" :key="item" :label="item" :value="item">
</el-option>
</el-select>
<el-select clearable class="filter-item" style="width: 130px" v-model="listQuery.type" placeholder="类型">
<el-select clearable class="filter-item" style="width: 130px" v-model="listQuery.type" :placeholder="$t('table.type')">
<el-option v-for="item in calendarTypeOptions" :key="item.key" :label="item.display_name+'('+item.key+')'" :value="item.key">
</el-option>
</el-select>
<el-select @change='handleFilter' style="width: 120px" class="filter-item" v-model="listQuery.sort" placeholder="排序">
<el-select @change='handleFilter' style="width: 140px" class="filter-item" v-model="listQuery.sort">
<el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key">
</el-option>
</el-select>
<el-button class="filter-item" type="primary" v-waves icon="el-icon-search" @click="handleFilter">搜索</el-button>
<el-button class="filter-item" style="margin-left: 10px;" @click="handleCreate" type="primary" icon="el-icon-edit">添加</el-button>
<el-button class="filter-item" type="primary" v-waves icon="el-icon-download" @click="handleDownload">导出</el-button>
<el-checkbox class="filter-item" style='margin-left:15px;' @change='tableKey=tableKey+1' v-model="showAuditor">显示审核人</el-checkbox>
<el-button class="filter-item" type="primary" v-waves icon="el-icon-search" @click="handleFilter">{{$t('table.search')}}</el-button>
<el-button class="filter-item" style="margin-left: 10px;" @click="handleCreate" type="primary" icon="el-icon-edit">{{$t('table.add')}}</el-button>
<el-button class="filter-item" type="primary" :loading="downloadLoading" v-waves icon="el-icon-download" @click="handleDownload">{{$t('table.export')}}</el-button>
<el-checkbox class="filter-item" style='margin-left:15px;' @change='tableKey=tableKey+1' v-model="showReviewer">{{$t('table.reviewer')}}</el-checkbox>
</div>
<el-table :key='tableKey' :data="list" v-loading="listLoading" element-loading-text="给我一点时间" border fit highlight-current-row
style="width: 100%">
<el-table-column align="center" label="序号" width="65">
<el-table-column align="center" :label="$t('table.id')" width="65">
<template slot-scope="scope">
<span>{{scope.row.id}}</span>
</template>
</el-table-column>
<el-table-column width="180px" align="center" label="时间">
<el-table-column width="150px" align="center" :label="$t('table.date')">
<template slot-scope="scope">
<span>{{scope.row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}')}}</span>
</template>
</el-table-column>
<el-table-column min-width="150px" label="标题">
<el-table-column min-width="150px" :label="$t('table.title')">
<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>
</el-table-column>
<el-table-column width="110px" align="center" label="作者">
<el-table-column width="110px" align="center" :label="$t('table.author')">
<template slot-scope="scope">
<span>{{scope.row.author}}</span>
</template>
</el-table-column>
<el-table-column width="110px" v-if='showAuditor' align="center" label="审核人">
<el-table-column width="110px" v-if='showReviewer' align="center" :label="$t('table.reviewer')">
<template slot-scope="scope">
<span style='color:red;'>{{scope.row.auditor}}</span>
<span style='color:red;'>{{scope.row.reviewer}}</span>
</template>
</el-table-column>
<el-table-column width="80px" label="重要性">
<el-table-column width="80px" :label="$t('table.importance')">
<template slot-scope="scope">
<svg-icon v-for="n in +scope.row.importance" icon-class="star" class="meta-item__icon" :key="n"></svg-icon>
</template>
</el-table-column>
<el-table-column align="center" label="阅读数" width="95">
<el-table-column align="center" :label="$t('table.readings')" width="95">
<template slot-scope="scope">
<span v-if="scope.row.pageviews" class="link-type" @click='handleFetchPv(scope.row.pageviews)'>{{scope.row.pageviews}}</span>
<span v-else></span>
<span v-else>0</span>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="状态" width="100">
<el-table-column class-name="status-col" :label="$t('table.status')" width="100">
<template slot-scope="scope">
<el-tag :type="scope.row.status | statusFilter">{{scope.row.status}}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="操作" width="220" class-name="small-padding">
<el-table-column align="center" :label="$t('table.actions')" width="230" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="handleUpdate(scope.row)">编辑</el-button>
<el-button v-if="scope.row.status!='published'" size="mini" type="success" @click="handleModifyStatus(scope.row,'published')">发布
<el-button type="primary" size="mini" @click="handleUpdate(scope.row)">{{$t('table.edit')}}</el-button>
<el-button v-if="scope.row.status!='published'" size="mini" type="success" @click="handleModifyStatus(scope.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="scope.row.status!='draft'" size="mini" @click="handleModifyStatus(scope.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="scope.row.status!='deleted'" size="mini" type="danger" @click="handleModifyStatus(scope.row,'deleted')">{{$t('table.delete')}}
</el-button>
</template>
</el-table-column>
</el-table>
<div v-show="!listLoading" class="pagination-container">
<div class="pagination-container">
<el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page.sync="listQuery.page"
:page-sizes="[10,20,30, 50]" :page-size="listQuery.limit" layout="total, sizes, prev, pager, next, jumper" :total="total">
</el-pagination>
@@ -86,47 +86,47 @@
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
<el-form :rules="rules" ref="dataForm" :model="temp" label-position="left" label-width="70px" style='width: 400px; margin-left:50px;'>
<el-form-item label="类型" prop="type">
<el-select class="filter-item" v-model="temp.type" placeholder="请选择">
<el-form-item :label="$t('table.type')" prop="type">
<el-select class="filter-item" v-model="temp.type" placeholder="Please select">
<el-option v-for="item in calendarTypeOptions" :key="item.key" :label="item.display_name" :value="item.key">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="时间" prop="timestamp">
<el-date-picker v-model="temp.timestamp" type="datetime" placeholder="选择日期时间">
<el-form-item :label="$t('table.date')" prop="timestamp">
<el-date-picker v-model="temp.timestamp" type="datetime" placeholder="Please pick a date">
</el-date-picker>
</el-form-item>
<el-form-item label="标题" prop="title">
<el-form-item :label="$t('table.title')" prop="title">
<el-input v-model="temp.title"></el-input>
</el-form-item>
<el-form-item label="状态">
<el-select class="filter-item" v-model="temp.status" placeholder="请选择">
<el-form-item :label="$t('table.status')">
<el-select class="filter-item" v-model="temp.status" placeholder="Please select">
<el-option v-for="item in statusOptions" :key="item" :label="item" :value="item">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="重要性">
<el-form-item :label="$t('table.importance')">
<el-rate style="margin-top:8px;" v-model="temp.importance" :colors="['#99A9BF', '#F7BA2A', '#FF9900']" :max='3'></el-rate>
</el-form-item>
<el-form-item label="点评">
<el-input type="textarea" :autosize="{ minRows: 2, maxRows: 4}" placeholder="请输入内容" v-model="temp.remark">
<el-form-item :label="$t('table.remark')">
<el-input type="textarea" :autosize="{ minRows: 2, maxRows: 4}" placeholder="Please input" v-model="temp.remark">
</el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false"> </el-button>
<el-button v-if="dialogStatus=='create'" type="primary" @click="createData"> </el-button>
<el-button v-else type="primary" @click="updateData"> </el-button>
<el-button @click="dialogFormVisible = false">{{$t('table.cancel')}}</el-button>
<el-button v-if="dialogStatus=='create'" type="primary" @click="createData">{{$t('table.confirm')}}</el-button>
<el-button v-else type="primary" @click="updateData">{{$t('table.confirm')}}</el-button>
</div>
</el-dialog>
<el-dialog title="阅读数统计" :visible.sync="dialogPvVisible">
<el-dialog title="Reading statistics" :visible.sync="dialogPvVisible">
<el-table :data="pvData" border fit highlight-current-row style="width: 100%">
<el-table-column prop="key" label="渠道"> </el-table-column>
<el-table-column prop="pv" label="pv"> </el-table-column>
<el-table-column prop="key" label="Channel"> </el-table-column>
<el-table-column prop="pv" label="Pv"> </el-table-column>
</el-table>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="dialogPvVisible = false"> </el-button>
<el-button type="primary" @click="dialogPvVisible = false">{{$t('table.confirm')}}</el-button>
</span>
</el-dialog>
@@ -139,13 +139,13 @@ import waves from '@/directive/waves' // 水波纹指令
import { parseTime } from '@/utils'
const calendarTypeOptions = [
{ key: 'CN', display_name: '中国' },
{ key: 'US', display_name: '美国' },
{ key: 'JP', display_name: '日本' },
{ key: 'EU', display_name: '欧元区' }
{ key: 'CN', display_name: 'China' },
{ key: 'US', display_name: 'USA' },
{ key: 'JP', display_name: 'Japan' },
{ key: 'EU', display_name: 'Eurozone' }
]
// arr to obj ,such as { CN : "中国", US : "美国" }
// arr to obj ,such as { CN : "China", US : "USA" }
const calendarTypeKeyValue = calendarTypeOptions.reduce((acc, cur) => {
acc[cur.key] = cur.display_name
return acc
@@ -172,9 +172,9 @@ export default {
},
importanceOptions: [1, 2, 3],
calendarTypeOptions,
sortOptions: [{ label: 'ID升序列', key: '+id' }, { label: 'ID降序', key: '-id' }],
sortOptions: [{ label: 'ID Ascending', key: '+id' }, { label: 'ID Descending', key: '-id' }],
statusOptions: ['published', 'draft', 'deleted'],
showAuditor: false,
showReviewer: false,
temp: {
id: undefined,
importance: 1,
@@ -187,8 +187,8 @@ export default {
dialogFormVisible: false,
dialogStatus: '',
textMap: {
update: '编辑',
create: '创建'
update: 'Edit',
create: 'Create'
},
dialogPvVisible: false,
pvData: [],
@@ -196,7 +196,8 @@ export default {
type: [{ required: true, message: 'type is required', trigger: 'change' }],
timestamp: [{ type: 'date', required: true, message: 'timestamp is required', trigger: 'change' }],
title: [{ required: true, message: 'title is required', trigger: 'blur' }]
}
},
downloadLoading: false
}
},
filters: {
@@ -266,7 +267,7 @@ export default {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
this.temp.id = parseInt(Math.random() * 100) + 1024 // mock a id
this.temp.author = '原创作者'
this.temp.author = 'vue-element-admin'
createArticle(this.temp).then(() => {
this.list.unshift(this.temp)
this.dialogFormVisible = false
@@ -330,12 +331,13 @@ export default {
})
},
handleDownload() {
require.ensure([], () => {
const { export_json_to_excel } = require('vendor/Export2Excel')
const tHeader = ['时间', '地区', '类型', '标题', '重要性']
const filterVal = ['timestamp', 'province', 'type', 'title', 'importance']
this.downloadLoading = true
import('@/vendor/Export2Excel').then(excel => {
const tHeader = ['timestamp', 'title', 'type', 'importance', 'status']
const filterVal = ['timestamp', 'title', 'type', 'importance', 'status']
const data = this.formatJson(filterVal, this.list)
export_json_to_excel(tHeader, data, 'table数据')
excel.export_json_to_excel(tHeader, data, 'table-list')
this.downloadLoading = false
})
},
formatJson(filterVal, jsonData) {

View File

@@ -1,60 +1,60 @@
<template>
<div class="app-container calendar-list-container">
<!-- Note that row-key is necessary to get a correct row order. -->
<el-table :data="list" row-key="id" v-loading.body="listLoading" border fit highlight-current-row style="width: 100%">
<el-table :data="list" v-loading.body="listLoading" border fit highlight-current-row style="width: 100%">
<el-table-column align="center" label="序号" width="65">
<el-table-column align="center" label="ID" width="65">
<template slot-scope="scope">
<span>{{scope.row.id}}</span>
</template>
</el-table-column>
<el-table-column width="180px" align="center" label="时间">
<el-table-column width="180px" align="center" label="Date">
<template slot-scope="scope">
<span>{{scope.row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}')}}</span>
</template>
</el-table-column>
<el-table-column min-width="300px" label="标题">
<el-table-column min-width="300px" label="Title">
<template slot-scope="scope">
<span>{{scope.row.title}}</span>
</template>
</el-table-column>
<el-table-column width="110px" align="center" label="作者">
<el-table-column width="110px" align="center" label="Author">
<template slot-scope="scope">
<span>{{scope.row.author}}</span>
</template>
</el-table-column>
<el-table-column width="80px" label="重要性">
<el-table-column width="100px" label="Importance">
<template slot-scope="scope">
<svg-icon v-for="n in +scope.row.importance" icon-class="star" class="meta-item__icon" :key="n"></svg-icon>
<svg-icon v-for="n in +scope.row.importance" icon-class="star" class="icon-star" :key="n"></svg-icon>
</template>
</el-table-column>
<el-table-column align="center" label="阅读数" width="95">
<el-table-column align="center" label="Readings" width="95">
<template slot-scope="scope">
<span>{{scope.row.pageviews}}</span>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="状态" width="110">
<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}}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="拖拽" width="95">
<el-table-column align="center" label="Drag" width="80">
<template slot-scope="scope">
<svg-icon class='drag-handler' icon-class="drag"></svg-icon>
</template>
</el-table-column>
</el-table>
<div class='show-d'>默认顺序 &nbsp; {{ olderList}}</div>
<div class='show-d'>拖拽后顺序{{newList}}</div>
<!-- $t is vue-i18n global function to translate lang (lang in @/lang) -->
<div class='show-d'>{{$t('table.dragTips1')}} : &nbsp; {{ oldList}}</div>
<div class='show-d'>{{$t('table.dragTips2')}} : {{newList}}</div>
</div>
</template>
@@ -75,7 +75,7 @@ export default {
limit: 10
},
sortable: null,
olderList: [],
oldList: [],
newList: []
}
},
@@ -99,8 +99,8 @@ export default {
this.list = response.data.items
this.total = response.data.total
this.listLoading = false
this.olderList = this.list.map(v => v.id)
this.newList = this.olderList.slice()
this.oldList = this.list.map(v => v.id)
this.newList = this.oldList.slice()
this.$nextTick(() => {
this.setSort()
})
@@ -109,7 +109,12 @@ export default {
setSort() {
const el = document.querySelectorAll('.el-table__body-wrapper > table > tbody')[0]
this.sortable = Sortable.create(el, {
ghostClass: 'sortable-ghost', // Class name for the drop placeholder,
onEnd: evt => {
const targetRow = this.list.splice(evt.oldIndex, 1)[0]
this.list.splice(evt.newIndex, 0, targetRow)
// for show the changes, you can delete in you code
const tempIndex = this.newList.splice(evt.oldIndex, 1)[0]
this.newList.splice(evt.newIndex, 0, tempIndex)
}
@@ -119,10 +124,21 @@ export default {
}
</script>
<style>
.sortable-ghost{
opacity: .8;
color: #fff!important;
background: #42b983!important;
}
</style>
<style scoped>
.icon-star{
margin-right:2px;
}
.drag-handler{
width: 30px;
height: 30px;
width: 20px;
height: 20px;
cursor: pointer;
}
.show-d{

View File

@@ -11,7 +11,7 @@
<el-table :data="tableData" :key='key' border fit highlight-current-row style="width: 100%">
<el-table-column prop="name" label="fruitName" width="180"></el-table-column>
<el-table-column :key='fruit' v-for='(fruit,index) in formThead' :label="fruit">
<el-table-column :key='fruit' v-for='fruit in formThead' :label="fruit">
<template slot-scope="scope">
{{scope.row[fruit]}}
</template>
@@ -22,7 +22,7 @@
</template>
<script>
const defaultFormThead = ['apple', 'banana'] // 默认选中项
const defaultFormThead = ['apple', 'banana']
export default {
data() {
@@ -42,15 +42,15 @@ export default {
}
],
key: 1, // table key
formTheadOptions: ['apple', 'banana', 'orange'], // 可选择表头
formTheadOptions: ['apple', 'banana', 'orange'],
checkboxVal: defaultFormThead, // checkboxVal
formThead: defaultFormThead // 默认表头
formThead: defaultFormThead // 默认表头 Default header
}
},
watch: {
checkboxVal(valArr) {
this.formThead = this.formTheadOptions.filter(i => valArr.indexOf(i) >= 0)
this.key = this.key + 1// 为了保证table 每次都会重渲 (牺牲性能保证效果,当然也可以不用)
this.key = this.key + 1// 为了保证table 每次都会重渲 In order to ensure the table will be re-rendered each time
}
}
}

View File

@@ -1,9 +1,9 @@
<template>
<div class="app-container">
<div style='margin:0 0 5px 20px'>固定表头 按照表头顺序排序</div>
<div style='margin:0 0 5px 20px'>{{$t('table.dynamicTips1')}}</div>
<fixed-thead></fixed-thead>
<div style='margin:30px 0 5px 20px'>不固定表头 按照点击顺序排序</div>
<div style='margin:30px 0 5px 20px'>{{$t('table.dynamicTips2')}}</div>
<unfixed-thead></unfixed-thead>
</div>
</template>

View File

@@ -12,7 +12,7 @@
<el-table :data="tableData" border fit highlight-current-row style="width: 100%">
<el-table-column prop="name" label="fruitName" width="180">
</el-table-column>
<el-table-column :key='fruit' v-for='(fruit,index) in formThead' :label="fruit">
<el-table-column :key='fruit' v-for='fruit in formThead' :label="fruit">
<template slot-scope="scope">
{{scope.row[fruit]}}
</template>

View File

@@ -3,37 +3,37 @@
<el-table :data="list" v-loading.body="listLoading" border fit highlight-current-row style="width: 100%">
<el-table-column align="center" label="序号" width="80">
<el-table-column align="center" label="ID" width="80">
<template slot-scope="scope">
<span>{{scope.row.id}}</span>
</template>
</el-table-column>
<el-table-column width="180px" align="center" label="时间">
<el-table-column width="180px" align="center" label="Date">
<template slot-scope="scope">
<span>{{scope.row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}')}}</span>
</template>
</el-table-column>
<el-table-column width="120px" align="center" label="作者">
<el-table-column width="120px" align="center" label="Author">
<template slot-scope="scope">
<span>{{scope.row.author}}</span>
</template>
</el-table-column>
<el-table-column width="100px" label="重要性">
<el-table-column width="100px" label="Importance">
<template slot-scope="scope">
<svg-icon v-for="n in +scope.row.importance" icon-class="star" class="meta-item__icon" :key="n"></svg-icon>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="状态" width="110">
<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}}</el-tag>
</template>
</el-table-column>
<el-table-column min-width="300px" label="标题">
<el-table-column min-width="300px" label="Title">
<template slot-scope="scope">
<template v-if="scope.row.edit">
<el-input class="edit-input" size="small" v-model="scope.row.title"></el-input>
@@ -43,10 +43,10 @@
</template>
</el-table-column>
<el-table-column align="center" label="编辑" width="120">
<el-table-column align="center" label="Actions" width="120">
<template slot-scope="scope">
<el-button v-if="scope.row.edit" type="success" @click="confirmEdit(scope.row)" size="small" icon="el-icon-circle-check-outline">完成</el-button>
<el-button v-else type="primary" @click='scope.row.edit=!scope.row.edit' size="small" icon="el-icon-edit">编辑</el-button>
<el-button v-if="scope.row.edit" type="success" @click="confirmEdit(scope.row)" size="small" icon="el-icon-circle-check-outline">Ok</el-button>
<el-button v-else type="primary" @click='scope.row.edit=!scope.row.edit' size="small" icon="el-icon-edit">Edit</el-button>
</template>
</el-table-column>
@@ -89,7 +89,9 @@ export default {
const items = response.data.items
this.list = items.map(v => {
this.$set(v, 'edit', false) // https://vuejs.org/v2/guide/reactivity.html
v.originalTitle = v.title // will be used when user click the cancel botton
return v
})
this.listLoading = false
@@ -122,6 +124,6 @@ export default {
.cancel-btn {
position: absolute;
right: 15px;
top: 13px;
top: 10px;
}
</style>

View File

@@ -1,29 +1,30 @@
<template>
<!-- $t is vue-i18n global function to translate lang -->
<div class="app-container">
<el-input style='width:240px;' placeholder="请输入文件名(默认excel-list)" prefix-icon="el-icon-document" v-model="filename"></el-input>
<el-button style='margin-bottom:20px;' type="primary" icon="document" @click="handleDownload" :loading="downloadLoading">导出excel</el-button>
<el-input style='width:340px;' :placeholder="$t('excel.placeholder')" prefix-icon="el-icon-document" v-model="filename"></el-input>
<el-button style='margin-bottom:20px;' type="primary" icon="document" @click="handleDownload" :loading="downloadLoading">{{$t('excel.export')}} excel</el-button>
<el-table :data="list" v-loading.body="listLoading" element-loading-text="拼命加载中" border fit highlight-current-row>
<el-table-column align="center" label='ID' width="95">
<el-table-column align="center" label='Id' width="95">
<template slot-scope="scope">
{{scope.$index}}
</template>
</el-table-column>
<el-table-column label="文章标题">
<el-table-column label="Title">
<template slot-scope="scope">
{{scope.row.title}}
</template>
</el-table-column>
<el-table-column label="作者" width="95" align="center">
<el-table-column label="Author" width="110" align="center">
<template slot-scope="scope">
<el-tag>{{scope.row.author}}</el-tag>
</template>
</el-table-column>
<el-table-column label="阅读数" width="115" align="center">
<el-table-column label="Readings" width="115" align="center">
<template slot-scope="scope">
{{scope.row.pageviews}}
</template>
</el-table-column>
<el-table-column align="center" prop="created_at" label="发布时间" width="220">
<el-table-column align="center" label="Date" width="220">
<template slot-scope="scope">
<i class="el-icon-time"></i>
<span>{{scope.row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}')}}</span>
@@ -35,7 +36,7 @@
<script>
import { fetchList } from '@/api/article'
import { parseTime } from 'utils'
import { parseTime } from '@/utils'
export default {
name: 'exportExcel',
@@ -60,13 +61,12 @@ export default {
},
handleDownload() {
this.downloadLoading = true
require.ensure([], () => {
const { export_json_to_excel } = require('vendor/Export2Excel')
const tHeader = ['序号', '文章标题', '作者', '阅读数', '发布时间']
import('@/vendor/Export2Excel').then(excel => {
const tHeader = ['Id', 'Title', 'Author', 'Readings', 'Date']
const filterVal = ['id', 'title', 'author', 'pageviews', 'display_time']
const list = this.list
const data = this.formatJson(filterVal, list)
export_json_to_excel(tHeader, data, this.filename)
excel.export_json_to_excel(tHeader, data, this.filename)
this.downloadLoading = false
})
},

View File

@@ -1,31 +1,32 @@
<template>
<div class="app-container">
<el-input style='width:240px;' placeholder="请输入文件名(默认excel-list)" prefix-icon="el-icon-document" v-model="filename"></el-input>
<el-button style='margin-bottom:20px' type="primary" icon="document" @click="handleDownload" :loading="downloadLoading">导出已选择项</el-button>
<!-- $t is vue-i18n global function to translate lang -->
<el-input style='width:340px;' :placeholder="$t('excel.placeholder')" prefix-icon="el-icon-document" v-model="filename"></el-input>
<el-button style='margin-bottom:20px' type="primary" icon="document" @click="handleDownload" :loading="downloadLoading">{{$t('excel.selectedExport')}}</el-button>
<el-table :data="list" v-loading.body="listLoading" element-loading-text="拼命加载中" border fit highlight-current-row @selection-change="handleSelectionChange"
ref="multipleTable">
<el-table-column type="selection" align="center"></el-table-column>
<el-table-column align="center" label='ID' width="95">
<el-table-column align="center" label='Id' width="95">
<template slot-scope="scope">
{{scope.$index}}
</template>
</el-table-column>
<el-table-column label="文章标题">
<el-table-column label="Title">
<template slot-scope="scope">
{{scope.row.title}}
</template>
</el-table-column>
<el-table-column label="作者" width="95" align="center">
<el-table-column label="Author" width="110" align="center">
<template slot-scope="scope">
<el-tag>{{scope.row.author}}</el-tag>
</template>
</el-table-column>
<el-table-column label="阅读数" width="115" align="center">
<el-table-column label="Readings" width="115" align="center">
<template slot-scope="scope">
{{scope.row.pageviews}}
</template>
</el-table-column>
<el-table-column align="center" prop="created_at" label="发布时间" width="220">
<el-table-column align="center" label="PDate" width="220">
<template slot-scope="scope">
<i class="el-icon-time"></i>
<span>{{scope.row.display_time}}</span>
@@ -66,19 +67,18 @@ export default {
handleDownload() {
if (this.multipleSelection.length) {
this.downloadLoading = true
require.ensure([], () => {
const { export_json_to_excel } = require('vendor/Export2Excel')
const tHeader = ['序号', '文章标题', '作者', '阅读数', '发布时间']
import('@/vendor/Export2Excel').then(excel => {
const tHeader = ['Id', 'Title', 'Author', 'Readings', 'Date']
const filterVal = ['id', 'title', 'author', 'pageviews', 'display_time']
const list = this.multipleSelection
const data = this.formatJson(filterVal, list)
export_json_to_excel(tHeader, data, this.filename)
excel.export_json_to_excel(tHeader, data, this.filename)
this.$refs.multipleTable.clearSelection()
this.downloadLoading = false
})
} else {
this.$message({
message: '请至少选择一条记录',
message: 'Please select at least one item',
type: 'warning'
})
}

View File

@@ -9,7 +9,7 @@
</template>
<script>
import UploadExcelComponent from 'components/UploadExcel/index.vue'
import UploadExcelComponent from '@/components/UploadExcel/index.vue'
export default {
name: 'uploadExcel',

View File

@@ -10,7 +10,7 @@
</router-link>
<el-dropdown trigger="click">
<el-button>{{!postForm.comment_disabled?'评论已打开':'评论已关闭'}}
<el-button plain>{{!postForm.comment_disabled?'评论已打开':'评论已关闭'}}
<i class="el-icon-caret-bottom el-icon--right"></i>
</el-button>
<el-dropdown-menu class="no-padding" slot="dropdown">
@@ -24,7 +24,7 @@
</el-dropdown>
<el-dropdown trigger="click">
<el-button>平台
<el-button plain>平台
<i class="el-icon-caret-bottom el-icon--right"></i>
</el-button>
<el-dropdown-menu class="no-border" slot="dropdown">
@@ -37,7 +37,7 @@
</el-dropdown>
<el-dropdown trigger="click">
<el-button>
<el-button plain>
外链
<i class="el-icon-caret-bottom el-icon--right"></i>
</el-button>

View File

@@ -10,7 +10,7 @@
</template>
<script>
import { Navbar, Sidebar, AppMain, TagsView } from 'views/layout/components'
import { Navbar, Sidebar, AppMain, TagsView } from './components'
export default {
name: 'layout',

View File

@@ -13,7 +13,6 @@ export default {
name: 'AppMain',
computed: {
cachedViews() {
// console.log(this.$store.state.tagsView.cachedViews)
return this.$store.state.tagsView.cachedViews
}
// key() {

View File

@@ -5,24 +5,15 @@
<breadcrumb class="breadcrumb-container"></breadcrumb>
<div class="right-menu">
<error-log class="errLog-container right-menu-item"></error-log>
<error-log v-if="log.length>0" class="errLog-container right-menu-item" :logsList="log"></error-log>
<el-tooltip effect="dark" content="全屏" placement="bottom">
<el-tooltip effect="dark" :content="$t('navbar.screenfull')" placement="bottom">
<screenfull class="screenfull right-menu-item"></screenfull>
</el-tooltip>
<el-dropdown trigger="click" class='international' @command="handleSetLanguage">
<div>
<svg-icon class-name='right-menu-item international-icon' icon-class="language" />
</div>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="zh" :disabled="language==='zh'">中文</el-dropdown-item>
<el-dropdown-item command="en" :disabled="language==='en'">English</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<lang-select class="international right-menu-item"></lang-select>
<el-tooltip effect="dark" content="换肤" placement="bottom">
<el-tooltip effect="dark" :content="$t('navbar.theme')" placement="bottom">
<theme-picker class="theme-switch right-menu-item"></theme-picker>
</el-tooltip>
@@ -34,16 +25,16 @@
<el-dropdown-menu slot="dropdown">
<router-link to="/">
<el-dropdown-item>
首页
{{$t('navbar.dashboard')}}
</el-dropdown-item>
</router-link>
<a target='_blank' href="https://github.com/PanJiaChen/vue-element-admin/">
<el-dropdown-item>
项目地址
{{$t('navbar.github')}}
</el-dropdown-item>
</a>
<el-dropdown-item divided>
<span @click="logout" style="display:block;">退出登录</span>
<span @click="logout" style="display:block;">{{$t('navbar.logOut')}}</span>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
@@ -55,47 +46,34 @@
import { mapGetters } from 'vuex'
import Breadcrumb from '@/components/Breadcrumb'
import Hamburger from '@/components/Hamburger'
import ThemePicker from '@/components/ThemePicker'
import ErrorLog from '@/components/ErrorLog'
import Screenfull from '@/components/Screenfull'
import ErrorLog from '@/components/ErrLog'
import errLogStore from 'store/errLog'
import LangSelect from '@/components/LangSelect'
import ThemePicker from '@/components/ThemePicker'
export default {
components: {
Breadcrumb,
Hamburger,
ThemePicker,
ErrorLog,
Screenfull
},
data() {
return {
log: errLogStore.state.errLog
}
Screenfull,
LangSelect,
ThemePicker
},
computed: {
...mapGetters([
'sidebar',
'name',
'avatar',
'language'
'avatar'
])
},
methods: {
toggleSideBar() {
this.$store.dispatch('toggleSideBar')
},
handleSetLanguage(lang) {
this.$i18n.locale = lang
this.$store.dispatch('setLanguage', lang)
this.$message({
message: 'switch language success',
type: 'success'
})
},
logout() {
this.$store.dispatch('LogOut').then(() => {
location.reload()// 为了重新实例化vue-router对象 避免bug
location.reload()// In order to re-instantiate the vue-router object to avoid bugs
})
}
}
@@ -135,11 +113,6 @@ export default {
}
.international{
vertical-align: top;
.international-icon{
font-size: 20px;
cursor: pointer;
vertical-align: -5px;
}
}
.theme-switch {
vertical-align: 15px;
@@ -167,6 +140,3 @@ export default {
}
}
</style>

View File

@@ -3,7 +3,7 @@
<template v-for="item in routes" v-if="!item.hidden&&item.children">
<router-link v-if="item.children.length===1 && !item.children[0].children" :to="item.path+'/'+item.children[0].path" :key="item.children[0].name">
<el-menu-item :index="item.path+'/'+item.children[0].path" class='submenu-title-noDropdown'>
<el-menu-item :index="item.path+'/'+item.children[0].path" :class="{'submenu-title-noDropdown':!isNest}">
<svg-icon v-if="item.children[0].meta&&item.children[0].meta.icon" :icon-class="item.children[0].meta.icon"></svg-icon>
<span v-if="item.children[0].meta&&item.children[0].meta.title">{{generateTitle(item.children[0].meta.title)}}</span>
</el-menu-item>
@@ -16,7 +16,7 @@
</template>
<template v-for="child in item.children" v-if="!child.hidden">
<sidebar-item class="nest-menu" v-if="child.children&&child.children.length>0" :routes="[child]" :key="child.path"></sidebar-item>
<sidebar-item :is-nest="true" class="nest-menu" v-if="child.children&&child.children.length>0" :routes="[child]" :key="child.path"></sidebar-item>
<router-link v-else :to="item.path+'/'+child.path" :key="child.name">
<el-menu-item :index="item.path+'/'+child.path">
@@ -39,6 +39,10 @@ export default {
props: {
routes: {
type: Array
},
isNest: {
type: Boolean,
default: false
}
},
methods: {

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