Compare commits

...

43 Commits

Author SHA1 Message Date
Pan
5cd245ac15 split tabs-view to separate files 2017-06-23 17:56:45 +08:00
Pan
0c3063f46c Release 1.0.3 2017-06-23 17:38:30 +08:00
Pan
91cb0ac5ca add view tabs 2017-06-23 17:36:13 +08:00
Pan
7549eb8044 fix eslint && refine code 2017-06-23 15:39:13 +08:00
孙晨光
1072572ac6 update: 递归过滤异步路由表 2017-06-23 15:25:58 +08:00
Pan
046d1369d2 not use Lazy Loading In dev 2017-06-21 16:59:02 +08:00
Pan
657937c7a5 Revert "refine css"
This reverts commit 6de521d671.
2017-06-21 09:46:47 +08:00
Pan
6de521d671 refine css 2017-06-20 23:14:50 +08:00
Pan
dbf9638922 refine dynamictable example 2017-06-20 23:02:36 +08:00
轩辕Rowboat
aaa64a8ccf update format code
没用编辑器,所以代码没格式化/(ㄒoㄒ)/~~
2017-06-20 15:44:14 +08:00
轩辕Rowboat
ff8d7ada73 千分符考虑小数点 2017-06-20 15:44:14 +08:00
Pan
bf480ca6b4 fix router path bug 2017-06-19 18:14:31 +08:00
Pan
803201af3a fix error link 2017-06-19 18:02:19 +08:00
Pan
e97dbf6115 use babel-preset-env 2017-06-19 17:59:56 +08:00
Pan
cf1fb6cd4d refine demo code 2017-06-15 15:35:14 +08:00
Pan
7287894cb8 Release 1.0.2 2017-06-15 14:44:26 +08:00
Pan
29402cc889 fix typo 2017-06-15 14:35:48 +08:00
Pan
a1a96c38d7 add tabs example 2017-06-15 14:33:02 +08:00
Pan
2927e08d06 change mock data 2017-06-15 13:44:48 +08:00
Pan
5a8755ec9f add screenfull 2017-06-15 13:23:49 +08:00
Pan
3a2bf13a1f refine code 2017-06-15 11:05:41 +08:00
Pan
4a6ef1af17 degrade eslint
eslint4.0 has some problem
2017-06-15 10:49:27 +08:00
Pan
42b5875d36 upgrade babel-core 2017-06-15 10:39:03 +08:00
Pan
4af4df702c upgrade package.json 2017-06-15 10:38:12 +08:00
Pan
72d9a406b4 refine introduction document 2017-06-14 18:02:35 +08:00
Pan
bd607b4728 refine code && document 2017-06-14 18:02:12 +08:00
Pan
f82ec2d5d7 rm redundant code 2017-06-14 17:37:21 +08:00
Pan
c412d17b70 refine tinymce && tinymce demo 2017-06-14 17:34:32 +08:00
Pan
09deec1e5e update iconfont 2017-06-14 16:50:11 +08:00
Pan
acaa6bb269 refine sidebar css 2017-06-13 16:48:47 +08:00
Pan
7d6917a5ef Create README.md 2017-06-13 13:44:01 +08:00
Pan
dd73827112 fix avatar component bug 2017-06-13 11:04:54 +08:00
Pan
4fc649e828 refine node-sass version 2017-06-12 18:01:29 +08:00
Pan
5165ec26ae Create README.md 2017-06-12 17:50:19 +08:00
Pan
d2827bf047 add edit and create demo 2017-06-12 16:05:40 +08:00
Pan
e9d37a94d3 add markdown to html 2017-06-12 15:25:56 +08:00
Pan
9607d07cc0 Create README.md 2017-06-12 09:48:38 +08:00
Pan
ecf7558e8e refine permission example 2017-06-06 10:46:20 +08:00
Pan
703c0c5cc5 fix lint 2017-06-02 18:14:39 +08:00
Pan
46b6a6e19f add qiniu upload example 2017-06-02 18:14:19 +08:00
Pan
e8fb41d0ff update read 2017-05-31 18:28:06 +08:00
Pan
f2847862e0 replace readme gifs 2017-05-31 17:04:26 +08:00
Pan
e6bae82fe5 replace img 2017-05-31 16:49:24 +08:00
66 changed files with 944 additions and 1008 deletions

View File

@@ -1,5 +1,8 @@
{ {
"presets": ["es2015", "stage-2"], "presets": [
["env", { "modules": false }],
"stage-2"
],
"plugins": ["transform-runtime"], "plugins": ["transform-runtime"],
"comments": false "comments": false
} }

View File

@@ -52,6 +52,10 @@ Join the group on QQ 591724180.
- Two-factor authentication - Two-factor authentication
- Collapsing sidebar (support nested routes) - Collapsing sidebar (support nested routes)
- Mock data - Mock data
- cache tabs example
- screenfull
- markdown2html
- views-tab
## Development ## Development
@@ -111,6 +115,9 @@ npm run build:prod
└── package.json // package.json └── package.json // package.json
``` ```
## Changelog
Detailed changes for each release are documented in the [release notes](https://github.com/PanJiaChen/vue-element-admin/releases).
## State Management ## State Management
Only status of user and app configuration is managed by Vuex. Other data are managed by their own business pages. Only status of user and app configuration is managed by Vuex. Other data are managed by their own business pages.

View File

@@ -8,19 +8,24 @@
**注意该项目目前使用element-ui@1.3.3版本,所以最低兼容 Vue 2.3.0** **注意该项目目前使用element-ui@1.3.3版本,所以最低兼容 Vue 2.3.0**
## 前言 ## 前言
> 这半年来一直在用vue写管理后台目前后台已经有七十多个页面十几种权限但维护成本依然很低所以准备开源分享一下后台开发的经验和成果。目前的技术栈主要的采用vue+element+axios.由于是个人项目所以数据请求都是用了mockjs模拟。注意在次项目基础上改造开发时请移除mock文件。 > 这半年来一直在用vue写管理后台目前后台已经有百来个个页面十几种权限但维护成本依然很低所以准备开源分享一下后台开发的经验和成果。目前的技术栈主要的采用vue+element+axios由webpack2打包.由于是个人项目所以数据请求都是用了mockjs模拟。注意在次项目基础上改造开发时请移除mock文件。
后续会出一系列的教程配套文章,如何从零构建后台项目框架,如何做完整的用户系统(如权限验证,二次登录等),如何二次开发组件(如富文本),如何整合七牛等等文章,各种后台开发经验等等。莫急~~ 写了一个系列的教程配套文章,如何从零构建后一个完整的后台项目:
相应需求开了一个qq群 591724180 方便大家交流
- [wiki](https://github.com/PanJiaChen/vue-element-admin/wiki) - [wiki](https://github.com/PanJiaChen/vue-element-admin/wiki)
- [手摸手,带你用 vue 撸后台 系列一(基础篇)](https://juejin.im/post/59097cd7a22b9d0065fb61d2) - [手摸手,带你用 vue 撸后台 系列一(基础篇)](https://juejin.im/post/59097cd7a22b9d0065fb61d2)
- [手摸手,带你用 vue 撸后台 系列二(登录权限篇)](https://juejin.im/post/591aa14f570c35006961acac) - [手摸手,带你用 vue 撸后台 系列二(登录权限篇)](https://juejin.im/post/591aa14f570c35006961acac)
- [ 手摸手,带你封装一个vue component](https://segmentfault.com/a/1190000009090836) - [手摸手,带你用 vue 撸后台 系列三 (实战篇)](https://juejin.im/post/593121aa0ce4630057f70d35)
- [手摸手带你封装一个vue component](https://segmentfault.com/a/1190000009090836)
相应需求开了一个qq群 591724180 方便大家交流
**如有问题请先看上述文章和Wiki,若不能满足欢迎issue和pr~**
**该项目并不是一个脚手架,更倾向于是一个集成解决方案方案**
**该项目不支持低版本游览器有需求请自行添加polyfill[详情](https://github.com/PanJiaChen/vue-element-admin/wiki#babel-polyfill)**
**如有问题请先看上述问题和Wiki,不能满足欢迎issue和pr~**
## 功能 ## 功能
- 登录/注销 - 登录/注销
@@ -42,12 +47,17 @@
- table example - table example
- 动态table example - 动态table example
- 拖拽table example - 拖拽table example
- 内联编辑table example
- form example - form example
- 多环境发布 - 多环境发布
- dashboard - dashboard
- 二次登录 - 二次登录
- 动态侧边栏(支持多级路由) - 动态侧边栏(支持多级路由)
- mock数据 - mock数据
- cache tabs example
- screenfull
- markdown2html
- views-tab
## 开发 ## 开发
@@ -104,6 +114,9 @@
``` ```
## Changelog
Detailed changes for each release are documented in the [release notes](https://github.com/PanJiaChen/vue-element-admin/releases).
## 状态管理 ## 状态管理
后台只有user和app配置相关状态使用vuex存在全局其它数据都由每个业务页面自己管理。 后台只有user和app配置相关状态使用vuex存在全局其它数据都由每个业务页面自己管理。

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 486 KiB

After

Width:  |  Height:  |  Size: 532 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 605 KiB

After

Width:  |  Height:  |  Size: 601 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 968 KiB

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@@ -1,6 +1,6 @@
{ {
"name": "juicy", "name": "juicy",
"version": "1.0.1", "version": "1.0.3",
"description": "A Vue.js admin", "description": "A Vue.js admin",
"author": "Pan <panfree23@gmail.com>", "author": "Pan <panfree23@gmail.com>",
"license": "MIT", "license": "MIT",
@@ -13,57 +13,59 @@
"lint": "eslint --ext .js,.vue src" "lint": "eslint --ext .js,.vue src"
}, },
"dependencies": { "dependencies": {
"axios": "0.15.3", "axios": "0.16.2",
"codemirror": "5.25.2", "codemirror": "5.26.0",
"dropzone": "4.3.0", "dropzone": "5.1.0",
"echarts": "3.4.0", "echarts": "3.6.1",
"element-ui": "1.3.3", "element-ui": "1.3.6",
"file-saver": "1.3.3", "file-saver": "1.3.3",
"jquery": "3.1.1", "jquery": "3.1.1",
"js-cookie": "2.1.3", "js-cookie": "2.1.4",
"jsonlint": "1.6.2", "jsonlint": "1.6.2",
"mockjs": "1.0.1-beta3", "mockjs": "1.0.1-beta3",
"normalize.css": "3.0.2", "normalize.css": "3.0.2",
"nprogress": "0.2.0", "nprogress": "0.2.0",
"screenfull": "3.2.2",
"showdown": "1.7.1",
"simplemde": "1.11.2", "simplemde": "1.11.2",
"sortablejs": "1.5.1", "sortablejs": "1.5.1",
"vue": "2.3.3", "vue": "2.3.3",
"vue-count-to": "1.0.5", "vue-count-to": "1.0.5",
"vue-multiselect": "2.0.0-beta.15", "vue-multiselect": "2.0.0-beta.15",
"vue-router": "2.5.3", "vue-router": "2.5.3",
"vuedraggable": "2.8.4", "vuedraggable": "2.13.1",
"vuex": "2.3.1", "vuex": "2.3.1",
"xlsx": "0.8.1" "xlsx": "0.8.1"
}, },
"devDependencies": { "devDependencies": {
"autoprefixer": "6.7.2", "autoprefixer": "7.1.1",
"babel-core": "6.22.1", "babel-core": "6.25.0",
"babel-eslint": "7.1.1", "babel-eslint": "7.2.3",
"babel-loader": "6.2.10", "babel-loader": "7.0.0",
"babel-plugin-transform-runtime": "6.22.0", "babel-plugin-transform-runtime": "6.23.0",
"babel-preset-es2015": "6.22.0", "babel-preset-env": "1.5.2",
"babel-preset-stage-2": "6.22.0", "babel-preset-stage-2": "6.24.1",
"babel-register": "6.22.0", "babel-register": "6.24.1",
"chalk": "1.1.3", "chalk": "1.1.3",
"connect-history-api-fallback": "1.3.0", "connect-history-api-fallback": "1.3.0",
"copy-webpack-plugin": "4.0.1", "copy-webpack-plugin": "4.0.1",
"cross-env": "4.0.0", "cross-env": "5.0.1",
"css-loader": "0.28.0", "css-loader": "0.28.4",
"eslint": "3.19.0", "eslint": "3.19.0",
"eslint-friendly-formatter": "2.0.7", "eslint-friendly-formatter": "3.0.0",
"eslint-import-resolver-webpack": "0.8.1", "eslint-import-resolver-webpack": "0.8.1",
"eslint-loader": "1.7.1", "eslint-loader": "1.7.1",
"eslint-plugin-html": "2.0.1", "eslint-plugin-html": "3.0.0",
"eslint-plugin-import": "2.2.0", "eslint-plugin-import": "2.3.0",
"eventsource-polyfill": "0.9.6", "eventsource-polyfill": "0.9.6",
"express": "4.14.1", "express": "4.15.3",
"extract-text-webpack-plugin": "2.0.0", "extract-text-webpack-plugin": "2.1.2",
"file-loader": "0.10.0", "file-loader": "0.11.2",
"friendly-errors-webpack-plugin": "1.1.3", "friendly-errors-webpack-plugin": "1.6.1",
"function-bind": "1.1.0", "function-bind": "1.1.0",
"html-webpack-plugin": "2.28.0", "html-webpack-plugin": "2.28.0",
"http-proxy-middleware": "0.17.3", "http-proxy-middleware": "0.17.4",
"node-sass": "4.5.2", "node-sass": "^4.5.0",
"opn": "4.0.2", "opn": "4.0.2",
"optimize-css-assets-webpack-plugin": "1.3.0", "optimize-css-assets-webpack-plugin": "1.3.0",
"ora": "1.1.0", "ora": "1.1.0",
@@ -74,13 +76,12 @@
"semver": "5.3.0", "semver": "5.3.0",
"style-loader": "0.17.0", "style-loader": "0.17.0",
"url-loader": "0.5.8", "url-loader": "0.5.8",
"vue-loader": "12.0.4", "vue-loader": "12.2.1",
"vue-style-loader": "2.0.5", "vue-style-loader": "3.0.1",
"vue-template-compiler": "2.3.3", "vue-template-compiler": "2.3.3",
"webpack": "2.5.1", "webpack": "2.6.1",
"webpack-bundle-analyzer": "2.2.1", "webpack-bundle-analyzer": "2.8.2",
"webpack-dashboard": "0.2.1", "webpack-dev-middleware": "1.10.2",
"webpack-dev-middleware": "1.10.0",
"webpack-hot-middleware": "2.18.0", "webpack-hot-middleware": "2.18.0",
"webpack-merge": "4.1.0" "webpack-merge": "4.1.0"
}, },

View File

@@ -1,28 +1,8 @@
// import fetch, { tpFetch } from 'utils/fetch'; import fetch from 'utils/fetch';
// export function getToken() { export function getToken() {
// return fetch({ return fetch({
// url: '/qiniu/upload/token', url: '/qiniu/upload/token', // 假地址 自行替换
// method: 'get' method: 'get'
// }); });
// } }
// export function upload(data) {
// return tpFetch({
// url: 'https://upload.qbox.me',
// method: 'post',
// data
// });
// }
// /* 外部uri转七牛uri*/
// export function netUpload(token, net_url) {
// const imgData = {
// net_url
// };
// return fetch({
// url: '/qiniu/upload/net/async',
// method: 'post',
// data: imgData
// });
// }

File diff suppressed because one or more lines are too long

View File

@@ -51,7 +51,6 @@
backgroundColor: '#344b58', backgroundColor: '#344b58',
title: { title: {
text: '统计', text: '统计',
subtext: 'from http://gallery.echartsjs.com',
x: '4%', x: '4%',
textStyle: { textStyle: {
color: '#fff', color: '#fff',

View File

@@ -328,6 +328,7 @@
// 关闭控件 // 关闭控件
off() { off() {
this.show = false; this.show = false;
this.$emit('close');
}, },
// 设置步骤 // 设置步骤
setStep(step) { setStep(step) {

View File

@@ -0,0 +1,57 @@
<template>
<svg @click='click' class="icon screenfull" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" t="1497503607356" viewBox="0 0 1024 1024" version="1.1" p-id="4109" :fill='fill' :width="width" :height="height"><path d="M604.157933 512l204.484208 204.484208 82.942037-82.942037c10.364045-10.952446 26.498514-13.83817 40.309054-8.067746 13.249769 5.742794 22.465664 18.99154 22.465664 33.977859l0 258.042008c0 20.168342-16.695241 36.863582-36.863582 36.863582L659.452283 954.357873c-14.986319 0-28.236088-9.215896-33.977859-23.025413-5.770424-13.249769-2.885723-29.384237 8.067746-39.748283l82.942037-82.942037L512 604.157933 307.515792 808.642141l82.942037 82.942037c10.952446 10.364045 13.83817 26.498514 8.067746 39.748283-5.742794 13.809517-18.99154 23.025413-33.977859 23.025413L106.504686 954.357873c-20.168342 0-36.863582-16.695241-36.863582-36.863582L69.641103 659.452283c0-14.986319 9.215896-28.236088 23.025413-33.977859 13.249769-5.770424 29.384237-2.8847 39.748283 8.067746l82.942037 82.942037 204.484208-204.484208L215.357859 307.515792l-82.942037 82.942037c-6.890944 6.918573-16.10684 10.952446-25.911136 10.952446-4.593622 0-9.804297-1.14815-13.83817-2.8847-13.809517-5.742794-23.025413-18.99154-23.025413-33.977859L69.641103 106.504686c0-20.168342 16.695241-36.863582 36.863582-36.863582L364.546693 69.641103c14.986319 0 28.236088 9.215896 33.977859 23.025413 5.770424 13.249769 2.8847 29.384237-8.067746 39.748283l-82.942037 82.942037 204.484208 204.484208L716.484208 215.357859l-82.942037-82.942037c-10.952446-10.364045-13.83817-26.498514-8.067746-39.748283 5.742794-13.809517 18.99154-23.025413 33.977859-23.025413l258.042008 0c20.168342 0 36.863582 16.695241 36.863582 36.863582l0 258.042008c0 14.986319-9.215896 28.236088-22.465664 33.977859-4.593622 1.736551-9.804297 2.8847-14.397918 2.8847-9.804297 0-19.020192-4.033873-25.911136-10.952446l-82.942037-82.942037L604.157933 512z" p-id="4110"/></svg>
</template>
<script>
import screenfull from 'screenfull';
export default {
name: 'hamburger',
props: {
width: {
type: Number,
default: 22
},
height: {
type: Number,
default: 22
},
fill: {
type: String,
default: '#48576a'
}
},
data() {
return {
isFullscreen: false
}
},
methods: {
click() {
if (!screenfull.enabled) {
this.$message({
message: 'you browser can not work',
type: 'warning'
});
return false;
}
if (this.isFullscreen) {
screenfull.exit();
this.isFullscreen = false;
} else {
screenfull.request();
this.isFullscreen = true;
}
}
}
}
</script>
<style scoped>
.screenfull {
display: inline-block;
cursor: pointer;
vertical-align: -0.15em;
}
</style>

View File

@@ -1,119 +0,0 @@
<template>
<div class="upload-container">
<el-button :style="{background:color,borderColor:color}" @click=" dialogVisible=true" type="primary">上传音频
</el-button>
<el-dialog v-model="dialogVisible">
<el-form ref="form" :model="form" :rules="rules" label-width="100px" label-position="right">
<el-upload
class="editor-audio-upload"
action="https://upload.qbox.me"
:data="dataObj"
:show-file-list="true"
:file-list="audioList"
:on-success="handleAudioScucess"
:on-change="handleAudioChange"
:before-upload="audioBeforeUpload">
<el-button size="small" type="primary">上传音频</el-button>
</el-upload>
<el-form-item prop="url" label="音频URL">
<el-input v-model="form.url"></el-input>
</el-form-item>
<el-form-item prop="title" label="音频标题">
<el-input v-model="form.title"></el-input>
</el-form-item>
<el-form-item label="音频文本">
<el-input type="textarea" :autosize="{ minRows: 2}" v-model="form.text"></el-input>
</el-form-item>
</el-form>
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="handleSubmit"> </el-button>
</el-dialog>
</div>
</template>
<script>
import { getToken } from 'api/qiniu';
export default {
name: 'editorAudioUpload',
props: {
color: {
type: String,
default: '#20a0ff'
}
},
data() {
return {
dialogVisible: false,
dataObj: { token: '', key: '' },
audioList: [],
tempAudioUrl: '',
form: {
title: '',
url: '',
text: ''
},
rules: {
title: [
{ required: true, trigger: 'blur' }
],
url: [
{ required: true, trigger: 'blur' }
]
}
};
},
methods: {
handleSubmit() {
this.$refs.form.validate(valid => {
if (valid) {
this.$emit('successCBK', this.form);
this.dialogVisible = false;
this.form = {
title: '',
url: '',
text: ''
}
} else {
this.$message('填写有误');
return false;
}
});
},
handleAudioChange(file, fileList) {
this.audioList = fileList.slice(-1);
},
handleAudioScucess() {
this.form.url = this.tempAudioUrl
},
audioBeforeUpload() {
const _self = this;
return new Promise((resolve, reject) => {
getToken().then(response => {
const key = response.data.qiniu_key;
const token = response.data.qiniu_token;
_self._data.dataObj.token = token;
_self._data.dataObj.key = key;
this.tempAudioUrl = response.data.qiniu_url;
resolve(true);
}).catch(err => {
console.log(err);
reject(false)
});
});
}
}
};
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.upload-container {
.editor-audio-upload {
button {
float: left;
margin-left: 30px;
margin-bottom: 20px;
}
}
}
</style>

View File

@@ -1,88 +0,0 @@
<template>
<div class="upload-container">
<el-button icon='upload' :style="{background:color,borderColor:color}" @click=" dialogVisible=true" type="primary">上传图片
</el-button>
<el-dialog v-model="dialogVisible">
<el-upload
class="editor-slide-upload"
action="https://httpbin.org/post"
:data="dataObj"
:multiple="true"
:file-list="fileList"
:show-file-list="true"
list-type="picture-card"
:on-remove="handleRemove"
:on-success="handleImageScucess">
<el-button size="small" type="primary">点击上传</el-button>
</el-upload>
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="handleSubmit"> </el-button>
</el-dialog>
</div>
</template>
<script>
import { getToken } from 'api/qiniu';
export default {
name: 'editorSlideUpload',
props: {
color: {
type: String,
default: '#20a0ff'
}
},
data() {
return {
dialogVisible: false,
dataObj: { token: '', key: '' },
list: [],
fileList: []
};
},
methods: {
handleSubmit() {
const arr = this.list.map(v => v.url);
this.$emit('successCBK', arr);
this.list = [];
this.fileList = [];
this.dialogVisible = false;
},
handleRemove(file) {
const key = file.response.key;
for (let i = 0, len = this.list.length; i < len; i++) {
if (this.list[i].key === key) {
this.list.splice(i, 1);
return
}
}
},
handleImageScucess(file) {
this.list.push({ url: file.files.file });
},
beforeUpload() {
const _self = this;
return new Promise((resolve, reject) => {
getToken().then(response => {
const key = response.data.qiniu_key;
const token = response.data.qiniu_token;
_self._data.dataObj.token = token;
_self._data.dataObj.key = key;
this.list.push({ key, url: response.data.qiniu_url });
resolve(true);
}).catch(err => {
console.log(err);
reject(false)
});
});
}
}
};
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.upload-container {
.editor-slide-upload {
margin-bottom: 20px;
}
}
</style>

View File

@@ -1,82 +0,0 @@
<template>
<div class="upload-container">
<el-button :style="{background:color,borderColor:color}" @click=" dialogVisible=true" type="primary">上传轮播图
</el-button>
<el-dialog v-model="dialogVisible">
<el-upload
class="editor-slide-upload"
action="https://upload.qbox.me"
:data="dataObj"
:multiple="true"
:show-file-list="true"
list-type="picture-card"
:on-remove="handleRemove"
:before-upload="beforeUpload">
<el-button size="small" type="primary">点击上传</el-button>
</el-upload>
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="handleSubmit"> </el-button>
</el-dialog>
</div>
</template>
<script>
import { getToken } from 'api/qiniu';
export default {
name: 'editorSlideUpload',
props: {
color: {
type: String,
default: '#20a0ff'
}
},
data() {
return {
dialogVisible: false,
dataObj: { token: '', key: '' },
list: []
};
},
methods: {
handleSubmit() {
const arr = this.list.map(v => v.url);
this.$emit('successCBK', arr);
this.list = [];
this.dialogVisible = false;
},
handleRemove(file) {
const key = file.response.key;
for (let i = 0, len = this.list.length; i < len; i++) {
if (this.list[i].key === key) {
this.list.splice(i, 1);
return
}
}
},
beforeUpload() {
const _self = this;
return new Promise((resolve, reject) => {
getToken().then(response => {
const key = response.data.qiniu_key;
const token = response.data.qiniu_token;
_self._data.dataObj.token = token;
_self._data.dataObj.key = key;
this.list.push({ key, url: response.data.qiniu_url });
resolve(true);
}).catch(err => {
console.log(err);
reject(false)
});
});
}
}
};
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.upload-container {
.editor-slide-upload {
margin-bottom: 20px;
}
}
</style>

View File

@@ -1,167 +0,0 @@
<template>
<div class="upload-container">
<el-button :style="{background:color,borderColor:color}" @click=" dialogVisible=true" type="primary">上传视频</el-button>
<el-dialog v-model="dialogVisible">
<el-form ref="form" :model="form" :rules="rules" label-width="140px" label-position="left">
<el-upload
class="editor-video-upload"
action="https://upload.qbox.me"
:data="dataObj"
:show-file-list="true"
:file-list="videoList"
:on-success="handleVideoScucess"
:on-change="handleVideoChange"
:before-upload="videoBeforeUpload">
<el-button size="small" type="primary">上传视频</el-button>
</el-upload>
<el-form-item prop="url" label="视频URL">
<el-input v-model="form.url"></el-input>
</el-form-item>
<el-form-item prop="title" label="视频标题">
<el-input v-model="form.title"></el-input>
</el-form-item>
<el-form-item label="上传视频封面图">
</el-form-item>
<el-upload
class="image-uploader"
action="https://upload.qbox.me"
:show-file-list="false"
:data="dataObj"
:on-success="handleImageScucess"
:before-upload="beforeImageUpload">
<img v-if="form.image" :src="form.image" class="image-uploader-image">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</el-form>
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="handleSubmit"> </el-button>
</el-dialog>
</div>
</template>
<script>
import { getToken } from 'api/qiniu';
export default {
name: 'editorVideoUpload',
props: {
color: {
type: String,
default: '#20a0ff'
}
},
data() {
return {
dialogVisible: false,
dataObj: { token: '', key: '' },
videoList: [],
tempVideoUrl: '',
tempImageUrl: '',
form: {
title: '',
url: '',
image: ''
},
rules: {
url: [
{ required: true, trigger: 'blur' }
],
title: [
{ required: true, trigger: 'blur' }
]
}
};
},
methods: {
handleSubmit() {
this.$refs.form.validate(valid => {
if (valid) {
if (this.form.image.length > 0) {
this.$emit('successCBK', this.form);
this.dialogVisible = false;
this.form = {
title: '',
url: '',
image: ''
}
} else {
this.$message('请上传图片');
}
} else {
this.$message('填写有误');
return false;
}
});
},
handleVideoChange(file, fileList) {
this.videoList = fileList.slice(-1);
},
handleVideoScucess() {
this.form.url = this.tempVideoUrl
},
videoBeforeUpload() {
const _self = this;
return new Promise((resolve, reject) => {
getToken().then(response => {
const key = response.data.qiniu_key;
const token = response.data.qiniu_token;
_self._data.dataObj.token = token;
_self._data.dataObj.key = key;
this.tempVideoUrl = response.data.qiniu_url;
resolve(true);
}).catch(err => {
console.log(err);
reject(false)
});
});
},
handleImageScucess() {
this.form.image = this.tempImageUrl
},
beforeImageUpload() {
const _self = this;
return new Promise((resolve, reject) => {
getToken().then(response => {
const key = response.data.qiniu_key;
const token = response.data.qiniu_token;
_self._data.dataObj.token = token;
_self._data.dataObj.key = key;
this.tempImageUrl = response.data.qiniu_url;
resolve(true);
}).catch(err => {
console.log(err);
reject(false)
});
});
}
}
};
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.upload-container {
.editor-video-upload {
button {
float: left;
}
}
.image-uploader {
margin: 5px auto;
width: 400px;
height: 200px;
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
line-height: 200px;
i {
font-size: 28px;
color: #8c939d;
}
.image-uploader-image {
height: 200px;
}
}
}
</style>

View File

@@ -1,29 +1,13 @@
<template> <template>
<div class='tinymce-container editor-container'> <div class='tinymce-container editor-container'>
<textarea class='tinymce-textarea' :id="id"></textarea> <textarea class='tinymce-textarea' :id="id"></textarea>
<!--业务需求可删除-->
<div class="editor-custom-btn-container">
<editorSlide v-if="customButton.indexOf('editorSlide')>=0" color="#3A71A8" class="editor-upload-btn" @successCBK="slideSuccessCBK"></editorSlide>
<editorAudio v-if="customButton.indexOf('editorAudio')>=0" color="#30B08F" class="editor-upload-btn" @successCBK="aduioSuccessCBK"></editorAudio>
<editorVideo v-if="customButton.indexOf('editorVideo')>=0" color="#E65D6E" class="editor-upload-btn" @successCBK="videoSuccessCBK"></editorVideo>
<editorImage v-if="customButton.indexOf('editorImage')>=0" color="#20a0ff" class="editor-upload-btn" @successCBK="imageSuccessCBK"></editorImage>
<!--业务需求可删除-->
</div>
</div> </div>
</template> </template>
<script> <script>
// tinymce 在最外层目录static下 由index.html直接引入挂载在window下。不通过impot不打包
// 业务需求可删除
import editorAudio from './components/editorAudio';
import editorVideo from './components/editorVideo';
import editorSlide from './components/editorSlide';
import editorImage from './components/editorImage';
// import { getToken, upload } from 'api/qiniu'; // 七牛 // import { getToken, upload } from 'api/qiniu'; // 七牛
// 业务需求可删除
export default { export default {
name: 'tinymce', name: 'tinymce',
components: { editorImage, editorAudio, editorSlide, editorVideo }, // 业务需求可删除
props: { props: {
id: { id: {
type: String, type: String,
@@ -33,13 +17,6 @@
type: String, type: String,
default: '' default: ''
}, },
customButton: {
type: Array,
required: false,
default() {
return ['editorAudio', 'editorImage']
}
},
toolbar: { toolbar: {
type: Array, type: Array,
required: false, required: false,
@@ -92,15 +69,12 @@
imagetools_toolbar: 'watermark', imagetools_toolbar: 'watermark',
default_link_target: '_blank', default_link_target: '_blank',
link_title: false, link_title: false,
textcolor_map: [
'1482f0', '1482f0',
'4595e6', '4595e6'],
init_instance_callback: editor => { init_instance_callback: editor => {
if (_this.value) { if (_this.value) {
editor.setContent(_this.value) editor.setContent(_this.value)
} }
_this.hasInit = true; _this.hasInit = true;
editor.on('Change', () => { editor.on('NodeChange Change KeyUp', () => {
this.hasChange = true; this.hasChange = true;
this.$emit('input', editor.getContent({ format: 'raw' })); this.$emit('input', editor.getContent({ format: 'raw' }));
}); });
@@ -172,55 +146,6 @@
} }
}); });
}, },
methods: {
/* 业务代码可删除 start*/
imageSuccessCBK(arr) {
console.log(arr)
const _this = this;
arr.forEach(v => {
const node = document.createElement('img');
node.setAttribute('src', v);
node.onload = function() {
$(this).addClass('wscnph');
$(this).attr('data-wscntype', 'image');
$(this).attr('data-wscnh', this.height);
$(this).attr('data-wscnw', this.width);
tinymce.get(_this.id).insertContent(node.outerHTML)
}
})
},
slideSuccessCBK(arr) {
const node = document.createElement('img');
node.setAttribute('data-wscntype', 'slide');
node.setAttribute('data-uri', arr.toString());
node.setAttribute('data-wscnh', '190');
node.setAttribute('data-wscnw', '200');
node.setAttribute('src', ' https://wdl.wallstreetcn.com/6410b47d-a54c-4826-9bc1-c3e5df31280c');
node.className = 'wscnph editor-placeholder';
tinymce.get(this.id).insertContent(node.outerHTML)
},
videoSuccessCBK(form) {
const node = document.createElement('img');
node.setAttribute('data-wscntype', 'video');
node.setAttribute('data-uri', form.url);
node.setAttribute('data-cover-img-uri', form.image);
node.setAttribute('data-title', form.title);
node.setAttribute('src', 'https://wdl.wallstreetcn.com/07aeb3e7-f4ca-4207-befb-c987b3dc7011');
node.className = 'wscnph editor-placeholder';
tinymce.get(this.id).insertContent(node.outerHTML)
},
aduioSuccessCBK(form) {
const node = document.createElement('img');
node.setAttribute('data-wscntype', 'audio');
node.setAttribute('data-uri', form.url);
node.setAttribute('data-title', form.title);
node.setAttribute('data-text', form.text);
node.setAttribute('src', 'https://wdl.wallstreetcn.com/2ed0c8c8-fb82-499d-b81c-3fd1de114eae');
node.className = 'wscnph editor-placeholder';
tinymce.get(this.id).insertContent(node.outerHTML)
}
/* 业务代码可删除 end*/
},
destroyed() { destroyed() {
tinymce.get(this.id).destroy(); tinymce.get(this.id).destroy();
} }
@@ -240,7 +165,6 @@
.editor-custom-btn-container { .editor-custom-btn-container {
position: absolute; position: absolute;
right: 15px; right: 15px;
/*z-index: 2005;*/
top: 18px; top: 18px;
} }

View File

@@ -1,99 +1,91 @@
(function() { const vueSticky = {};
const vueSticky = {}; let listenAction;
let listenAction; vueSticky.install = Vue => {
vueSticky.install = Vue => { Vue.directive('sticky', {
Vue.directive('sticky', { inserted(el, binding) {
inserted(el, binding) { const params = binding.value || {},
const params = binding.value || {}, stickyTop = params.stickyTop || 0,
stickyTop = params.stickyTop || 0, zIndex = params.zIndex || 1000,
zIndex = params.zIndex || 1000, elStyle = el.style;
elStyle = el.style;
elStyle.position = '-webkit-sticky'; elStyle.position = '-webkit-sticky';
elStyle.position = 'sticky'; elStyle.position = 'sticky';
// if the browser support css stickyCurrently Safari, Firefox and Chrome Canary // if the browser support css stickyCurrently Safari, Firefox and Chrome Canary
// if (~elStyle.position.indexOf('sticky')) { // if (~elStyle.position.indexOf('sticky')) {
// elStyle.top = `${stickyTop}px`; // elStyle.top = `${stickyTop}px`;
// elStyle.zIndex = zIndex; // elStyle.zIndex = zIndex;
// return // return
// } // }
const elHeight = el.getBoundingClientRect().height; const elHeight = el.getBoundingClientRect().height;
const elWidth = el.getBoundingClientRect().width; const elWidth = el.getBoundingClientRect().width;
elStyle.cssText = `top: ${stickyTop}px; z-index: ${zIndex}`; elStyle.cssText = `top: ${stickyTop}px; z-index: ${zIndex}`;
const parentElm = el.parentNode || document.documentElement; const parentElm = el.parentNode || document.documentElement;
const placeholder = document.createElement('div'); const placeholder = document.createElement('div');
placeholder.style.display = 'none';
placeholder.style.width = `${elWidth}px`;
placeholder.style.height = `${elHeight}px`;
parentElm.insertBefore(placeholder, el)
let active = false;
const getScroll = (target, top) => {
const prop = top ? 'pageYOffset' : 'pageXOffset';
const method = top ? 'scrollTop' : 'scrollLeft';
let ret = target[prop];
if (typeof ret !== 'number') {
ret = window.document.documentElement[method];
}
return ret;
};
const sticky = () => {
if (active) {
return
}
if (!elStyle.height) {
elStyle.height = `${el.offsetHeight}px`
}
elStyle.position = 'fixed';
elStyle.width = `${elWidth}px`;
placeholder.style.display = 'inline-block';
active = true
};
const reset = () => {
if (!active) {
return
}
elStyle.position = '';
placeholder.style.display = 'none'; placeholder.style.display = 'none';
placeholder.style.width = `${elWidth}px`; active = false;
placeholder.style.height = `${elHeight}px`; };
parentElm.insertBefore(placeholder, el)
let active = false; const check = () => {
const scrollTop = getScroll(window, true);
const getScroll = (target, top) => { const offsetTop = el.getBoundingClientRect().top;
const prop = top ? 'pageYOffset' : 'pageXOffset'; if (offsetTop < stickyTop) {
const method = top ? 'scrollTop' : 'scrollLeft'; sticky();
let ret = target[prop]; } else {
if (typeof ret !== 'number') { if (scrollTop < elHeight + stickyTop) {
ret = window.document.documentElement[method]; reset()
} }
return ret; }
}; };
listenAction = () => {
check()
};
const sticky = () => { window.addEventListener('scroll', listenAction)
if (active) { },
return
}
if (!elStyle.height) {
elStyle.height = `${el.offsetHeight}px`
}
elStyle.position = 'fixed'; unbind() {
elStyle.width = `${elWidth}px`; window.removeEventListener('scroll', listenAction)
placeholder.style.display = 'inline-block'; }
active = true })
}; };
const reset = () => { export default vueSticky
if (!active) {
return
}
elStyle.position = '';
placeholder.style.display = 'none';
active = false;
};
const check = () => {
const scrollTop = getScroll(window, true);
const offsetTop = el.getBoundingClientRect().top;
if (offsetTop < stickyTop) {
sticky();
} else {
if (scrollTop < elHeight + stickyTop) {
reset()
}
}
};
listenAction = () => {
check()
};
window.addEventListener('scroll', listenAction)
},
unbind() {
window.removeEventListener('scroll', listenAction)
}
})
};
if (typeof exports == 'object') {
module.exports = vueSticky
} else if (typeof define == 'function' && define.amd) {
define([], () => vueSticky)
} else if (window.Vue) {
window.vueSticky = vueSticky;
Vue.use(vueSticky)
}
}());

View File

@@ -1,54 +1,47 @@
import './waves.css'; import './waves.css';
(function() {
const vueWaves = {}; const vueWaves = {};
vueWaves.install = (Vue, options = {}) => { vueWaves.install = (Vue, options = {}) => {
Vue.directive('waves', { Vue.directive('waves', {
bind(el, binding) { bind(el, binding) {
el.addEventListener('click', e => { el.addEventListener('click', e => {
const customOpts = Object.assign(options, binding.value); const customOpts = Object.assign(options, binding.value);
const opts = Object.assign({ const opts = Object.assign({
ele: el, // 波纹作用元素 ele: el, // 波纹作用元素
type: 'hit', // hit点击位置扩散center中心点扩展 type: 'hit', // hit点击位置扩散center中心点扩展
color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色 color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色
}, customOpts), }, customOpts),
target = opts.ele; target = opts.ele;
if (target) { if (target) {
target.style.position = 'relative'; target.style.position = 'relative';
target.style.overflow = 'hidden'; target.style.overflow = 'hidden';
const rect = target.getBoundingClientRect(); const rect = target.getBoundingClientRect();
let ripple = target.querySelector('.waves-ripple'); let ripple = target.querySelector('.waves-ripple');
if (!ripple) { if (!ripple) {
ripple = document.createElement('span'); ripple = document.createElement('span');
ripple.className = 'waves-ripple'; ripple.className = 'waves-ripple';
ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px'; ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px';
target.appendChild(ripple); target.appendChild(ripple);
} else { } else {
ripple.className = 'waves-ripple'; ripple.className = 'waves-ripple';
} }
switch (opts.type) { switch (opts.type) {
case 'center': case 'center':
ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px'; ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px';
ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px'; ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px';
break; break;
default: default:
ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.body.scrollTop) + 'px'; ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.body.scrollTop) + 'px';
ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.body.scrollLeft) + 'px'; ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.body.scrollLeft) + 'px';
} }
ripple.style.backgroundColor = opts.color; ripple.style.backgroundColor = opts.color;
ripple.className = 'waves-ripple z-active'; ripple.className = 'waves-ripple z-active';
return false; return false;
} }
}, false); }, false);
} }
}) })
}; };
if (typeof exports == 'object') {
module.exports = vueWaves export default vueWaves;
} else if (typeof define == 'function' && define.amd) {
define([], () => vueWaves)
} else if (window.Vue) {
window.vueWaves = vueWaves;
Vue.use(vueWaves)
}
}());

View File

@@ -104,5 +104,5 @@ export function html2Text(val) {
export function toThousandslsFilter(num) { export function toThousandslsFilter(num) {
return (+num || 0).toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,'); return (+num || 0).toString().replace(/^-?\d+/g, m => m.replace(/(?=(?!\b)(\d{3})+$)/g, ','));
} }

View File

@@ -13,7 +13,7 @@ for (let i = 0; i < count; i++) {
title: '@ctitle(10, 20)', title: '@ctitle(10, 20)',
forecast: '@float(0, 100, 2, 2)', forecast: '@float(0, 100, 2, 2)',
importance: '@integer(1, 3)', importance: '@integer(1, 3)',
'type|1': ['FD', 'FE', 'BI', 'VN'], 'type|1': ['CN', 'US', 'JP', 'EU'],
'status|1': ['published', 'draft', 'deleted'], 'status|1': ['published', 'draft', 'deleted'],
pageviews: '@integer(300, 5000)' pageviews: '@integer(300, 5000)'
})); }));

View File

@@ -5,7 +5,7 @@ const userMap = {
role: ['admin'], role: ['admin'],
token: 'admin', token: 'admin',
introduction: '我是超级管理员', introduction: '我是超级管理员',
avatar: 'https://wdl.wallstreetcn.com/48a3e1e0-ea2c-4a4e-9928-247645e3428b', avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
name: '超级管理员小潘', name: '超级管理员小潘',
uid: '001' uid: '001'
}, },
@@ -13,7 +13,7 @@ const userMap = {
role: ['editor'], role: ['editor'],
token: 'editor', token: 'editor',
introduction: '我是编辑', introduction: '我是编辑',
avatar: 'https://wdl.wallstreetcn.com/48a3e1e0-ea2c-4a4e-9928-247645e3428b', avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
name: '普通编辑小张', name: '普通编辑小张',
uid: '002' uid: '002'
@@ -22,7 +22,7 @@ const userMap = {
role: ['develop'], role: ['develop'],
token: 'develop', token: 'develop',
introduction: '我是开发', introduction: '我是开发',
avatar: 'https://wdl.wallstreetcn.com/48a3e1e0-ea2c-4a4e-9928-247645e3428b', avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
name: '工程师小王', name: '工程师小王',
uid: '003' uid: '003'
} }

View File

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

View File

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

View File

@@ -1,65 +1,70 @@
import Vue from 'vue'; import Vue from 'vue';
import Router from 'vue-router'; import Router from 'vue-router';
const _import = require('./_import_' + process.env.NODE_ENV);
// in development env not use Lazy Loading,because Lazy Loading large page will cause webpack hot update too slow
// so only in production use Lazy Loading
/* layout */ /* layout */
import Layout from '../views/layout/Layout'; import Layout from '../views/layout/Layout';
/* login */ /* login */
import Login from '../views/login/'; const Login = _import('login/index');
const authRedirect = () => import('../views/login/authredirect'); const authRedirect = _import('login/authredirect');
const sendPWD = () => import('../views/login/sendpwd'); const sendPWD = _import('login/sendpwd');
const reset = () => import('../views/login/reset'); const reset = _import('login/reset');
/* dashboard */ /* dashboard */
const dashboard = () => import('../views/dashboard/index'); const dashboard = _import('dashboard/index');
/* Introduction */ /* Introduction */
const Introduction = () => import('../views/introduction/index'); const Introduction = _import('introduction/index');
/* components */ /* components */
const componentsIndex = () => import('../views/components/index'); const componentsIndex = _import('components/index');
const Tinymce = () => import('../views/components/tinymce'); const Tinymce = _import('components/tinymce');
const Markdown = () => import('../views/components/markdown'); const Markdown = _import('components/markdown');
const JsonEditor = () => import('../views/components/jsoneditor'); const JsonEditor = _import('components/jsoneditor');
const DndList = () => import('../views/components/dndlist'); const DndList = _import('components/dndlist');
const AvatarUpload = () => import('../views/components/avatarUpload'); const AvatarUpload = _import('components/avatarUpload');
const Dropzone = () => import('../views/components/dropzone'); const Dropzone = _import('components/dropzone');
const Sticky = () => import('../views/components/sticky'); const Sticky = _import('components/sticky');
const SplitPane = () => import('../views/components/splitpane'); const SplitPane = _import('components/splitpane');
const CountTo = () => import('../views/components/countTo'); const CountTo = _import('components/countTo');
const Mixin = () => import('../views/components/mixin'); const Mixin = _import('components/mixin');
/* charts */ /* charts */
const chartIndex = () => import('../views/charts/index'); const chartIndex = _import('charts/index');
const KeyboardChart = () => import('../views/charts/keyboard'); const KeyboardChart = _import('charts/keyboard');
const KeyboardChart2 = () => import('../views/charts/keyboard2'); const KeyboardChart2 = _import('charts/keyboard2');
const LineMarker = () => import('../views/charts/line'); const LineMarker = _import('charts/line');
const MixChart = () => import('../views/charts/mixchart'); const MixChart = _import('charts/mixchart');
/* error page */ /* error page */
const Err404 = () => import('../views/error/404'); const Err404 = _import('error/404');
const Err401 = () => import('../views/error/401'); const Err401 = _import('error/401');
/* error log */ /* error log */
const ErrorLog = () => import('../views/errlog/index'); const ErrorLog = _import('errlog/index');
/* excel */ /* excel */
const ExcelDownload = () => import('../views/excel/index'); const ExcelDownload = _import('excel/index');
/* theme */ /* theme */
const Theme = () => import('../views/theme/index'); const Theme = _import('theme/index');
/* example*/ /* example*/
const TableLayout = () => import('../views/example/table/index'); const TableLayout = _import('example/table/index');
const DynamicTable = () => import('../views/example/table/dynamictable'); const DynamicTable = _import('example/table/dynamictable');
const Table = () => import('../views/example/table/table'); const Table = _import('example/table/table');
const DragTable = () => import('../views/example/table/dragTable'); const DragTable = _import('example/table/dragTable');
const InlineEditTable = () => import('../views/example/table/inlineEditTable'); const InlineEditTable = _import('example/table/inlineEditTable');
const Form1 = () => import('../views/example/form1');
const Form = _import('example/form');
const Tab = _import('example/tab/index');
/* permission */ /* permission */
const Permission = () => import('../views/permission/index'); const Permission = _import('permission/index');
Vue.use(Router); Vue.use(Router);
@@ -194,10 +199,10 @@ export const asyncRouterMap = [
icon: 'zonghe', icon: 'zonghe',
children: [ children: [
{ {
path: '/table', path: '/example/table',
component: TableLayout, component: TableLayout,
redirect: '/table/table', redirect: '/example/table/table',
name: 'table', name: 'Table',
children: [ children: [
{ path: 'dynamictable', component: DynamicTable, name: '动态table' }, { path: 'dynamictable', component: DynamicTable, name: '动态table' },
{ path: 'dragtable', component: DragTable, name: '拖拽table' }, { path: 'dragtable', component: DragTable, name: '拖拽table' },
@@ -205,7 +210,10 @@ export const asyncRouterMap = [
{ path: 'table', component: Table, name: '综合table' } { path: 'table', component: Table, name: '综合table' }
] ]
}, },
{ path: 'form1', component: Form1, name: '综合form1' } { path: 'form/edit', component: Form, name: '编辑Form', meta: { isEdit: true } },
{ path: 'form/create', component: Form, name: '创建Form' },
{ path: 'tab/index', component: Tab, name: 'Tab' }
] ]
}, },
{ path: '*', redirect: '/404', hidden: true } { path: '*', redirect: '/404', hidden: true }

View File

@@ -1,5 +1,6 @@
const getters = { const getters = {
sidebar: state => state.app.sidebar, sidebar: state => state.app.sidebar,
visitedViews: state => state.app.visitedViews,
token: state => state.user.token, token: state => state.user.token,
avatar: state => state.user.avatar, avatar: state => state.user.avatar,
name: state => state.user.name, name: state => state.user.name,

View File

@@ -6,7 +6,8 @@ const app = {
opened: !+Cookies.get('sidebarStatus') opened: !+Cookies.get('sidebarStatus')
}, },
theme: 'default', theme: 'default',
livenewsChannels: Cookies.get('livenewsChannels') || '[]' livenewsChannels: Cookies.get('livenewsChannels') || '[]',
visitedViews: []
}, },
mutations: { mutations: {
TOGGLE_SIDEBAR: state => { TOGGLE_SIDEBAR: state => {
@@ -16,11 +17,25 @@ const app = {
Cookies.set('sidebarStatus', 0); Cookies.set('sidebarStatus', 0);
} }
state.sidebar.opened = !state.sidebar.opened; state.sidebar.opened = !state.sidebar.opened;
},
ADD_VISITED_VIEWS: (state, view) => {
if (state.visitedViews.includes(view)) return
state.visitedViews.push(view)
},
DEL_VISITED_VIEWS: (state, view) => {
const index = state.visitedViews.indexOf(view)
state.visitedViews.splice(index, 1)
} }
}, },
actions: { actions: {
ToggleSideBar: ({ commit }) => { ToggleSideBar: ({ commit }) => {
commit('TOGGLE_SIDEBAR') commit('TOGGLE_SIDEBAR')
},
addVisitedViews: ({ commit }, view) => {
commit('ADD_VISITED_VIEWS', view)
},
delVisitedViews: ({ commit }, view) => {
commit('DEL_VISITED_VIEWS', view)
} }
} }
}; };

View File

@@ -1,5 +1,10 @@
import { asyncRouterMap, constantRouterMap } from 'src/router'; import { asyncRouterMap, constantRouterMap } from 'src/router';
/**
* 通过meta.role判断是否与当前用户权限匹配
* @param roles
* @param route
*/
function hasPermission(roles, route) { function hasPermission(roles, route) {
if (route.meta && route.meta.role) { if (route.meta && route.meta.role) {
return roles.some(role => route.meta.role.indexOf(role) >= 0) return roles.some(role => route.meta.role.indexOf(role) >= 0)
@@ -8,40 +13,45 @@ function hasPermission(roles, route) {
} }
} }
/**
* 递归过滤异步路由表,返回符合用户角色权限的路由表
* @param asyncRouterMap
* @param roles
*/
function filterAsyncRouter(asyncRouterMap, roles) {
const accessedRouters = asyncRouterMap.filter(route => {
if (hasPermission(roles, route)) {
if (route.children && route.children.length) {
route.children = filterAsyncRouter(route.children, roles)
}
return true
}
return false
})
return accessedRouters
}
const permission = { const permission = {
state: { state: {
routers: constantRouterMap, routers: constantRouterMap,
addRouters: [] addRouters: []
}, },
mutations: { mutations: {
SET_ROUTERS: (state, routers) => { SET_ROUTERS: (state, routers) => {
state.addRouters = routers; state.addRouters = routers;
state.routers = constantRouterMap.concat(routers); state.routers = constantRouterMap.concat(routers);
} }
}, },
actions: { actions: {
GenerateRoutes({ commit }, data) { GenerateRoutes({ commit }, data) {
return new Promise(resolve => { return new Promise(resolve => {
const { roles } = data; const { roles } = data
const accessedRouters = asyncRouterMap.filter(v => { let accessedRouters
if (roles.indexOf('admin') >= 0) return true; if (roles.indexOf('admin') >= 0) {
if (hasPermission(roles, v)) { accessedRouters = asyncRouterMap
if (v.children && v.children.length > 0) { } else {
v.children = v.children.filter(child => { accessedRouters = filterAsyncRouter(asyncRouterMap, roles)
if (hasPermission(roles, child)) { }
return child
}
return false;
});
return v
} else {
return v
}
}
return false;
});
commit('SET_ROUTERS', accessedRouters); commit('SET_ROUTERS', accessedRouters);
resolve(); resolve();
}) })
@@ -49,5 +59,4 @@ const permission = {
} }
}; };
export default permission; export default permission;

View File

@@ -132,6 +132,16 @@ const user = {
Cookies.remove('Admin-Token'); Cookies.remove('Admin-Token');
resolve(); resolve();
}); });
},
// 动态修改权限
ChangeRole({ commit }, role) {
return new Promise(resolve => {
commit('SET_ROLES', [role]);
commit('SET_TOKEN', role);
Cookies.set('Admin-Token', role);
resolve();
})
} }
} }
}; };

View File

@@ -24,27 +24,34 @@ service.interceptors.request.use(config => {
// respone拦截器 // respone拦截器
service.interceptors.response.use( service.interceptors.response.use(
response => response response => response,
/** /**
* 下面的注释为通过response自定义code来标示请求状态当code返回如下情况为权限有问题登出并返回到登录页 * 下面的注释为通过response自定义code来标示请求状态当code返回如下情况为权限有问题登出并返回到登录页
* 如通过xmlhttprequest 状态码标识 逻辑可写在下面error中 * 如通过xmlhttprequest 状态码标识 逻辑可写在下面error中
*/ */
// const code = response.data.code; // const res = response.data;
// // 50014:Token 过期了 50012:其他客户端登录了 50008:非法的token // if (res.code !== 20000) {
// if (code === 50008 || code === 50014 || code === 50012) { // Message({
// Message({ // message: res.message,
// message: res.message, // type: 'error',
// type: 'error', // duration: 5 * 1000
// duration: 5 * 1000 // });
// }); // // 50008:非法的token; 50012:其他客户端登录了; 50014:Token 过期了;
// // 登出 // if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
// store.dispatch('FedLogOut').then(() => { // MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', {
// router.push({ path: '/login' }) // confirmButtonText: '重新登录',
// }); // cancelButtonText: '取消',
// } else { // type: 'warning'
// return response // }).then(() => {
// } // store.dispatch('FedLogOut').then(() => {
, // location.reload();// 为了重新实例化vue-router对象 避免bug
// });
// })
// }
// return Promise.reject(error);
// } else {
// return response.data;
// }
error => { error => {
console.log('err' + error);// for debug console.log('err' + error);// for debug
Message({ Message({

View File

@@ -1,7 +1,7 @@
<template> <template>
<div class="components-container" > <div class="components-container" >
<code> <code>
这里的所有的图表都基于echarts,实例代码来源<a href='http://gallery.echartsjs.com/explore.html#sort=rank~timeframe=all~author=all' target='_blank'>gallery</a><br/>其实echarts封装的很好了用vue封装是很简单的事情建议大家自己来封装 这里的所有的图表都基于ECharts,实例代码来源<a href='http://gallery.echartsjs.com/explore.html#sort=rank~timeframe=all~author=all' target='_blank'>gallery</a><br/>其实ECharts封装的很好了用vue封装是很简单的事情建议大家自己来封装<a target='_blank' class='lin' href="https://segmentfault.com/a/1190000009762198#articleHeader16">相关教程</a>
</code> </code>
</div> </div>
</template> </template>

View File

@@ -1,14 +1,13 @@
<template> <template>
<div class="components-container" style='height:100vh'> <div class="components-container" style='height:100vh'>
<div class='chart-container'> <div class='chart-container'>
<keyboardChart height='100%' width='100%' /> <keyboardChart height='100%' width='100%' />
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import keyboardChart from 'components/Charts/keyboard'; import keyboardChart from 'components/Charts/keyboard';
export default { export default {
components: { keyboardChart } components: { keyboardChart }
}; };
@@ -16,9 +15,9 @@
<style scoped> <style scoped>
.chart-container{ .chart-container{
position: relative; position: relative;
width: 100%; width: 100%;
height: 90%; height: 90%;
} }
</style> </style>

View File

@@ -1,14 +1,13 @@
<template> <template>
<div class="components-container" style='height:100vh'> <div class="components-container" style='height:100vh'>
<div class='chart-container'> <div class='chart-container'>
<keyboardChart2 id='apple' height='100%' width='100%' /> <keyboardChart2 id='apple' height='100%' width='100%' />
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import keyboardChart2 from 'components/Charts/keyboard2'; import keyboardChart2 from 'components/Charts/keyboard2';
export default { export default {
components: { keyboardChart2 } components: { keyboardChart2 }
}; };
@@ -16,9 +15,9 @@
<style scoped> <style scoped>
.chart-container{ .chart-container{
position: relative; position: relative;
width: 100%; width: 100%;
height: 90%; height: 90%;
} }
</style> </style>

View File

@@ -1,16 +1,13 @@
<template> <template>
<div class="components-container" style='height:100vh'> <div class="components-container" style='height:100vh'>
https://github.com/ecomfe/echarts/blob/master/index.js
http://echarts.baidu.com/tutorial.html
<div class='chart-container'> <div class='chart-container'>
<lineMarker height='100%' width='100%' /> <lineMarker height='100%' width='100%' />
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import lineMarker from 'components/Charts/lineMarker'; import lineMarker from 'components/Charts/lineMarker';
export default { export default {
components: { lineMarker } components: { lineMarker }
}; };
@@ -18,9 +15,9 @@
<style scoped> <style scoped>
.chart-container{ .chart-container{
position: relative; position: relative;
width: 100%; width: 100%;
height: 80%; height: 80%;
} }
</style> </style>

View File

@@ -1,14 +1,13 @@
<template> <template>
<div class="components-container" style='height:100vh'> <div class="components-container" style='height:100vh'>
<div class='chart-container'> <div class='chart-container'>
<mixchart id='apple' height='100%' width='100%' /> <mixchart id='apple' height='100%' width='100%' />
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import mixchart from 'components/Charts/mixchart'; import mixchart from 'components/Charts/mixchart';
export default { export default {
components: { mixchart } components: { mixchart }
}; };
@@ -16,10 +15,10 @@
<style scoped> <style scoped>
.chart-container{ .chart-container{
position: relative; position: relative;
width: 100%; width: 100%;
height: 90%; height: 90%;
padding-bottom: 40px; padding-bottom: 40px;
} }
</style> </style>

View File

@@ -9,9 +9,11 @@
<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">修改头像
</el-button> </el-button>
<ImageCropper :width="300" :height="300" url="https://httpbin.org/post" @crop-upload-success="cropSuccess" :key="imagecropperKey" v-show="imagecropperShow" /> <ImageCropper :width="300" :height="300" url="https://httpbin.org/post" @close='close' @crop-upload-success="cropSuccess"
:key="imagecropperKey" v-show="imagecropperShow" />
</div> </div>
</template> </template>
<script> <script>
import ImageCropper from 'components/ImageCropper'; import ImageCropper from 'components/ImageCropper';
import PanThumb from 'components/PanThumb'; import PanThumb from 'components/PanThumb';
@@ -29,6 +31,9 @@
this.imagecropperShow = false; this.imagecropperShow = false;
this.imagecropperKey = this.imagecropperKey + 1; this.imagecropperKey = this.imagecropperKey + 1;
this.image = resData.files.avatar; this.image = resData.files.avatar;
},
close() {
this.imagecropperShow = false;
} }
} }
}; };

View File

@@ -1,8 +1,8 @@
<template> <template>
<div class="components-container"> <div class="components-container">
<code>countTo 组件 <a href='https://github.com/PanJiaChen/vue-countTo' target='_blank'>线上地址</a></code> <code> <a href='https://github.com/PanJiaChen/vue-countTo' target='_blank'>countTo component</a></code>
<count-to ref='example' class='example' :start-val='_startVal' :end-val='_endVal' :duration='_duration' :decimals='_decimals' <count-to ref='example' class='example' :start-val='_startVal' :end-val='_endVal' :duration='_duration' :decimals='_decimals'
:separator='_separator' :prefix='_prefix' :suffix='_suffix' :autoplay='false' /> :separator='_separator' :prefix='_prefix' :suffix='_suffix' :autoplay='false' />
<div style='margin-left: 25%;margin-top: 40px;'> <div style='margin-left: 25%;margin-top: 40px;'>
<label class="label" for="startValInput">startVal: <input type="number" v-model.number='setStartVal' name='startValInput' /></label> <label class="label" for="startValInput">startVal: <input type="number" v-model.number='setStartVal' name='startValInput' /></label>
@@ -20,9 +20,9 @@
:separator=&#x27;{{_separator}}&#x27; :prefix=&#x27;{{_prefix}}&#x27; :suffix=&#x27;{{_suffix}}&#x27; :autoplay=false&gt;</code> :separator=&#x27;{{_separator}}&#x27; :prefix=&#x27;{{_prefix}}&#x27; :suffix=&#x27;{{_suffix}}&#x27; :autoplay=false&gt;</code>
</div> </div>
</template> </template>
<script> <script>
import countTo from 'vue-count-to'; import countTo from 'vue-count-to';
export default { export default {
components: { countTo }, components: { countTo },
data() { data() {

View File

@@ -1,6 +1,6 @@
<template> <template>
<div class="components-container"> <div class="components-container">
<code>拖拽https://github.com/SortableJS/Vue.Draggable 项目Vue.Draggable</code> <code>drag-list base on <a href="https://github.com/SortableJS/Vue.Draggable" target="_blank">Vue.Draggable</a></code>
<div class="editor-container"> <div class="editor-container">
<DndList :list1="list1" :list2="list2" list1Title="头条列表" list2Title="文章池" /> <DndList :list1="list1" :list2="list2" list1Title="头条列表" list2Title="文章池" />
</div> </div>

View File

@@ -1,7 +1,7 @@
<template> <template>
<div class="components-container"> <div class="components-container">
<code>这里暂时列出了自己在项目中自己封装和用到的组件如有补充可以提<a href='https://github.com/PanJiaChen/vue-element-admin/issues' target='_blank'>issue</a><br/> <code>这里暂时列出了自己在项目中用到的组件和一些自己封装的组件如有补充可以提<a target='_blank' href='https://github.com/PanJiaChen/vue-element-admin/issues'> issue </a><br/>
我个人崇尚自己封装组件因为很多组件会和业务后高度的耦合很多时候第三方封装是满足不了需求的如有需要可以看楼主之前写过的一篇<a href='https://segmentfault.com/a/1190000009090836' target='_blank'>文章</a> 我个人崇尚自己封装组件因为很多组件会和业务后高度的耦合而且第三方封装的组件灵活性可控性都不高如有需要可以看楼主之前写过的一篇<a href='https://segmentfault.com/a/1190000009090836' target='_blank'>文章</a>
</code> </code>
</div> </div>
</template> </template>

View File

@@ -1,11 +1,12 @@
<template> <template>
<div class="components-container" style='height:100vh'> <div class="components-container" style='height:100vh'>
<code>有校验</code> <code>jsonEditor is base on <a href="https://github.com/codemirror/CodeMirror" target="_blank">CodeMirrorr</a>,lint base on json-lint </code>
<div class="editor-container"> <div class="editor-container">
<json-editor ref="jsonEditor" v-model="value"></json-editor> <json-editor ref="jsonEditor" v-model="value"></json-editor>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import jsonEditor from 'components/jsonEditor'; import jsonEditor from 'components/jsonEditor';
const jsonData = '[{"items":[{"market_type":"forexdata","symbol":"XAUUSD"},{"market_type":"forexdata","symbol":"UKOIL"},{"market_type":"forexdata","symbol":"CORN"}],"name":""},{"items":[{"market_type":"forexdata","symbol":"XAUUSD"},{"market_type":"forexdata","symbol":"XAGUSD"},{"market_type":"forexdata","symbol":"AUTD"},{"market_type":"forexdata","symbol":"AGTD"}],"name":"贵金属"},{"items":[{"market_type":"forexdata","symbol":"CORN"},{"market_type":"forexdata","symbol":"WHEAT"},{"market_type":"forexdata","symbol":"SOYBEAN"},{"market_type":"forexdata","symbol":"SUGAR"}],"name":"农产品"},{"items":[{"market_type":"forexdata","symbol":"UKOIL"},{"market_type":"forexdata","symbol":"USOIL"},{"market_type":"forexdata","symbol":"NGAS"}],"name":"能源化工"}]'; const jsonData = '[{"items":[{"market_type":"forexdata","symbol":"XAUUSD"},{"market_type":"forexdata","symbol":"UKOIL"},{"market_type":"forexdata","symbol":"CORN"}],"name":""},{"items":[{"market_type":"forexdata","symbol":"XAUUSD"},{"market_type":"forexdata","symbol":"XAGUSD"},{"market_type":"forexdata","symbol":"AUTD"},{"market_type":"forexdata","symbol":"AGTD"}],"name":"贵金属"},{"items":[{"market_type":"forexdata","symbol":"CORN"},{"market_type":"forexdata","symbol":"WHEAT"},{"market_type":"forexdata","symbol":"SOYBEAN"},{"market_type":"forexdata","symbol":"SUGAR"}],"name":"农产品"},{"items":[{"market_type":"forexdata","symbol":"UKOIL"},{"market_type":"forexdata","symbol":"USOIL"},{"market_type":"forexdata","symbol":"NGAS"}],"name":"能源化工"}]';
@@ -21,8 +22,8 @@
<style scoped> <style scoped>
.editor-container{ .editor-container{
position: relative; position: relative;
height: 100%; height: 100%;
} }
</style> </style>

View File

@@ -1,19 +1,30 @@
<template> <template>
<div class="components-container"> <div class="components-container">
<code>公司做的后台主要是一个cms系统公司也是以自媒体为核心的所以富文本是后台很核心的功能在选择富文本的过程中也走了不少的弯路市面上常见的富文本都基本用过了最终选择了tinymce</code> <code>Markdown 我们这里选用了 <a href="https://github.com/sparksuite/simplemde-markdown-editor" target="_blank">simplemde-markdown-editor</a> 简单的用vue封装了一下<a target='_blank' href='https://segmentfault.com/a/1190000009762198#articleHeader14'> 相关文章 </a></code>
<div class="editor-container"> <div class="editor-container">
<MdEditor id='contentEditor' ref="contentEditor" v-model='content' :height="300" :zIndex='20'></MdEditor> <MdEditor id='contentEditor' ref="contentEditor" v-model='content' :height="300" :zIndex='20'></MdEditor>
</div> </div>
<el-button @click='markdown2Html' style="margin-top:80px;" type="primary">转为HTML<i class="el-icon-document el-icon--right"></i></el-button>
<div v-html="html"></div>
</div> </div>
</template> </template>
<script> <script>
import MdEditor from 'components/MdEditor'; import MdEditor from 'components/MdEditor';
export default { export default {
components: { MdEditor }, components: { MdEditor },
data() { data() {
return { return {
content: 'Simplemde' content: '## Simplemde',
html: ''
}
},
methods: {
markdown2Html() {
import('showdown').then(showdown => {
const converter = new showdown.Converter();
this.html = converter.makeHtml(this.content)
})
} }
} }
}; };

View File

@@ -1,14 +1,13 @@
<template> <template>
<div class="components-container"> <div class="components-container">
<code>公司做的后台主要是一个cms系统公司也是以自媒体为核心的所以富文本是后台很核心的功能在选择富文本的过程中也走了不少的弯路市面上常见的富文本都基本用过了最终选择了tinymce</code> <code>公司做的后台主要是一个cms系统公司也是以自媒体为核心的所以富文本是后台很核心的功能在选择富文本的过程中也走了不少的弯路市面上常见的富文本都基本用过了最终选择了Tinymce<a target='_blank' href='https://segmentfault.com/a/1190000009762198#articleHeader13'> 相关文章 </a></code>
<div class="editor-container"> <div>
<Tinymce :height=200 ref="editor" v-model="content"></Tinymce> <Tinymce :height=200 ref="editor" v-model="content"></Tinymce>
</div> </div>
<!--<div class='editor-content'> <div class='editor-content' v-html='content'></div>
{{content}}
</div>-->
</div> </div>
</template> </template>
<script> <script>
import Tinymce from 'components/Tinymce'; import Tinymce from 'components/Tinymce';
@@ -18,11 +17,14 @@
return { return {
content: 'Tinymce' content: 'Tinymce'
} }
},
methods: {
} }
}; };
</script> </script>
<style scoped>
.editor-content{
margin-top: 20px;
}
</style>

View File

@@ -40,8 +40,8 @@
<router-link class="pan-btn light-blue-btn" to="/charts/index">图表</router-link> <router-link class="pan-btn light-blue-btn" to="/charts/index">图表</router-link>
<router-link class="pan-btn red-btn" to="/errorpage/404">错误页面</router-link> <router-link class="pan-btn red-btn" to="/errorpage/404">错误页面</router-link>
<router-link class="pan-btn pink-btn" to="/excel/download">导出excel</router-link> <router-link class="pan-btn pink-btn" to="/excel/download">导出excel</router-link>
<router-link class="pan-btn green-btn" to="/example/table">table</router-link> <router-link class="pan-btn green-btn" to="/example/table/table">Table</router-link>
<router-link class="pan-btn tiffany-btn" to="/example/form1">form</router-link> <router-link class="pan-btn tiffany-btn" to="/example/form/edit">Form</router-link>
</div> </div>
<div class="clearfix main-dashboard-container"> <div class="clearfix main-dashboard-container">

View File

@@ -1,26 +1,19 @@
<template> <template>
<div class="errPage-container"> <div class="errPage-container">
<!--error code--> <err-code/>
<err-code/>
<!--error code-->
<h3>请点击右上角bug小图表</h3> <h3>请点击右上角bug小图表</h3>
<code> <code>
现在的管理后台基本都是spa的形式了它增强了用户体验但同时也会怎增加页面出问题的可能性可能一个小小的疏忽就导致整个页面的死锁好在Vue官网提供了一个方法来捕获处理异常 现在的管理后台基本都是spa的形式了它增强了用户体验但同时也会怎增加页面出问题的可能性可能一个小小的疏忽就导致整个页面的死锁好在Vue官网提供了一个方法来捕获处理异常
</code> </code>
<a href="#"><img src='../../../documentImg/code1.png'></a> <a href="#"><img src='../../../documentImg/code1.png'></a>
</div> </div>
</template> </template>
<script> <script>
import errCode from './errcode'; import errCode from './errcode';
// import code1Img from
export default { export default {
components: { errCode }, components: { errCode },
data() {
return {
}
},
methods: { methods: {
back() { back() {
this.$router.go(-1) this.$router.go(-1)
@@ -28,6 +21,7 @@
} }
}; };
</script> </script>
<style scoped> <style scoped>
.errPage-container{ .errPage-container{
padding: 30px; padding: 30px;

View File

@@ -4,7 +4,7 @@
<el-row> <el-row>
<el-col :span="12"> <el-col :span="12">
<h1 class="text-jumbo text-ginormous">Oops!</h1> <h1 class="text-jumbo text-ginormous">Oops!</h1>
gif来源<a href='https://zh.airbnb.com/' target='_blank'>airbnb</a> 页面 gif来源<a href='https://zh.airbnb.com/' target='_blank'>airbnb</a> 页面
<h2>你没有权限去该页面</h2> <h2>你没有权限去该页面</h2>
<h6>如有不满请联系你领导</h6> <h6>如有不满请联系你领导</h6>
<ul class="list-unstyled"> <ul class="list-unstyled">
@@ -26,9 +26,9 @@
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script> <script>
import errGif from 'assets/401.gif'; import errGif from 'assets/401.gif';
export default { export default {
data() { data() {
return { return {
@@ -50,38 +50,38 @@
</script> </script>
<style rel="stylesheet/scss" lang="scss" scoped> <style rel="stylesheet/scss" lang="scss" scoped>
.errPage-container { .errPage-container {
width: 800px; width: 800px;
margin: 100px auto; margin: 100px auto;
.pan-back-btn { .pan-back-btn {
background: #008489; background: #008489;
color: #fff; color: #fff;
}
.pan-gif {
margin: 0 auto;
display: block;
}
.pan-img{
display: block;
margin: 0 auto;
}
.text-jumbo {
font-size: 60px;
font-weight: 700;
color: #484848;
}
.list-unstyled {
font-size: 14px;
li {
padding-bottom: 5px;
}
a {
color: #008489;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
} }
.pan-gif {
margin: 0 auto;
display: block;
}
.pan-img {
display: block;
margin: 0 auto;
}
.text-jumbo {
font-size: 60px;
font-weight: 700;
color: #484848;
}
.list-unstyled {
font-size: 14px;
li {
padding-bottom: 5px;
}
a {
color: #008489;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
}
</style> </style>

View File

@@ -9,7 +9,7 @@
</div> </div>
<div class="bullshit"> <div class="bullshit">
<div class="bullshit__oops">OOPS!</div> <div class="bullshit__oops">OOPS!</div>
<div class="bullshit__info">版权所有<a class='link-type' href='https://wallstreetcn.com' target='_blank'>华尔街见闻</a></div> <div class="bullshit__info">版权所有<a class='link-type' href='https://wallstreetcn.com' target='_blank'>华尔街见闻</a></div>
<div class="bullshit__headline">{{ message }}</div> <div class="bullshit__headline">{{ message }}</div>
<div class="bullshit__info">请检查您输入的网址是否正确请点击以下按钮返回主页或者发送错误报告</div> <div class="bullshit__info">请检查您输入的网址是否正确请点击以下按钮返回主页或者发送错误报告</div>
<a href="/" class="bullshit__return-home">返回首页</a> <a href="/" class="bullshit__return-home">返回首页</a>
@@ -17,6 +17,7 @@
</div> </div>
</div> </div>
</template> </template>
<script> <script>
export default { export default {
computed: { computed: {
@@ -26,6 +27,7 @@
} }
} }
</script> </script>
<style rel="stylesheet/scss" lang="scss" scoped> <style rel="stylesheet/scss" lang="scss" scoped>
.wscn-http404 { .wscn-http404 {
position: relative; position: relative;

View File

@@ -7,6 +7,9 @@
<div style="display:inline-block"> <div style="display:inline-block">
<el-dropdown trigger="click"> <el-dropdown trigger="click">
<router-link style="margin-right:15px;" v-show='isEdit' :to="{ path:'create'}">
<el-button type="info">创建form</el-button>
</router-link>
<el-button>{{!postForm.comment_disabled?'评论已打开':'评论已关闭'}}<i class="el-icon-caret-bottom el-icon--right"></i></el-button> <el-button>{{!postForm.comment_disabled?'评论已打开':'评论已关闭'}}<i class="el-icon-caret-bottom el-icon--right"></i></el-button>
<el-dropdown-menu class="no-padding no-hover" slot="dropdown"> <el-dropdown-menu class="no-padding no-hover" slot="dropdown">
<el-dropdown-item> <el-dropdown-item>
@@ -185,10 +188,16 @@
computed: { computed: {
contentShortLength() { contentShortLength() {
return this.postForm.content_short.length return this.postForm.content_short.length
},
isEdit() {
return this.$route.meta.isEdit // meta
// return this.$route.path.indexOf('edit') !== -1 //
} }
}, },
created() { created() {
this.fetchData(); if (this.isEdit) {
this.fetchData();
}
}, },
methods: { methods: {
fetchData() { fetchData() {

View File

@@ -0,0 +1,98 @@
<template>
<el-table :data="list" border fit highlight-current-row style="width: 100%">
<el-table-column align="center" label="序号" width="65">
<template scope="scope">
<span>{{scope.row.id}}</span>
</template>
</el-table-column>
<el-table-column width="180px" align="center" label="时间">
<template scope="scope">
<span>{{scope.row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}')}}</span>
</template>
</el-table-column>
<el-table-column min-width="300px" label="标题">
<template scope="scope">
<span class="link-type" @click="handleUpdate(scope.row)">{{scope.row.title}}</span>
<el-tag>{{scope.row.type}}</el-tag>
</template>
</el-table-column>
<el-table-column width="110px" align="center" label="作者">
<template scope="scope">
<span>{{scope.row.author}}</span>
</template>
</el-table-column>
<el-table-column width="80px" label="重要性">
<template scope="scope">
<wscn-icon-svg v-for="n in +scope.row.importance" icon-class="wujiaoxing" class="meta-item__icon" :key="n" />
</template>
</el-table-column>
<el-table-column align="center" label="阅读数" width="95">
<template scope="scope">
<span>{{scope.row.pageviews}}</span>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="状态" width="90">
<template scope="scope">
<el-tag :type="scope.row.status | statusFilter">{{scope.row.status}}</el-tag>
</template>
</el-table-column>
</el-table>
</template>
<script>
import { fetchList } from 'api/article_table';
export default {
name: 'articleDetail',
props: {
type: {
type: String,
default: 'CN'
}
},
data() {
return {
list: null,
total: null,
listQuery: {
page: 1,
limit: 5,
type: this.type,
sort: '+id'
}
}
},
filters: {
statusFilter(status) {
const statusMap = {
published: 'success',
draft: 'gray',
deleted: 'danger'
};
return statusMap[status]
}
},
created() {
this.getList();
},
methods: {
getList() {
this.$emit('create'); // for test
fetchList(this.listQuery).then(response => {
this.list = response.data.items;
this.total = response.data.total;
})
}
}
}
</script>

View File

@@ -0,0 +1,44 @@
<template>
<div class="tab-container">
<el-tag type="primary">mounted times {{createdTimes}}</el-tag>
<el-tabs style='margin-top:15px;' v-model="activeName" type="border-card">
<el-tab-pane v-for="item in tabMapOptions" :label="item.label" :key='item.key' :name="item.key">
<keep-alive>
<tab-pane v-if='activeName==item.key' :type='item.key' @create='showCreatedTimes'></tab-pane>
</keep-alive>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import tabPane from './components/tabPane'
export default {
name: 'tabDemo',
components: { tabPane },
data() {
return {
tabMapOptions: [
{ label: '中国', key: 'CN' },
{ label: '美国', key: 'US' },
{ label: '日本', key: 'JP' },
{ label: '欧元区', key: 'EU' }
],
activeName: 'CN',
createdTimes: 0
}
},
methods: {
showCreatedTimes() {
this.createdTimes = this.createdTimes + 1;
}
}
}
</script>
<style scoped>
.tab-container{
margin: 30px;
}
</style>

View File

@@ -1,39 +1,19 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<div class="filter-container"> <div style='margin:0 0 5px 20px'>固定表头 按照表头顺序排序</div>
<el-checkbox-group v-model="formThead"> <fixed-thead/>
<el-checkbox label="apple">apple</el-checkbox>
<el-checkbox label="banana">banana</el-checkbox> <div style='margin:30px 0 5px 20px'>不固定表头 按照点击顺序排序</div>
<el-checkbox label="orange">orange</el-checkbox> <unfixed-thead/>
</el-checkbox-group>
</div>
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="name" label="名称" width="180">
</el-table-column>
<el-table-column :key='fruit' v-for='(fruit,index) in formThead' :label="fruit">
<template scope="scope">
{{scope.row.list[index].value}}
</template>
</el-table-column>
</el-table>
</div> </div>
</template> </template>
<script> <script>
import fixedThead from './dynamictable/fixedThead'
import unfixedThead from './dynamictable/unfixedThead'
export default {
export default { components: { fixedThead, unfixedThead }
data() { };
return {
tableData: [{
name: '水果',
list: [{ name: 'apple', value: 10 }, { name: 'banana', value: 20 }, { name: 'orange', value: 20 }]
}, {
name: '水果2',
list: [{ name: 'apple2', value: 12 }, { name: 'banana2', value: 22 }, { name: 'orange', value: 20 }]
}],
formThead: ['apple', 'banana']
}
}
};
</script> </script>

View File

@@ -0,0 +1,61 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-checkbox-group v-model="checkboxVal" >
<el-checkbox label="apple">apple</el-checkbox>
<el-checkbox label="banana">banana</el-checkbox>
<el-checkbox label="orange">orange</el-checkbox>
</el-checkbox-group>
</div>
<el-table :data="tableData" :key='key' 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">
<template scope="scope">
{{scope.row.list[index].value}}
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
const defaultFormThead = ['apple', 'banana']; // 默认选中项
export default {
data() {
return {
tableData: [
{
name: 'fruit1',
list: [
{ name: 'apple1', value: 10 },
{ name: 'banana1', value: 20 },
{ name: 'orange1', value: 20 }
]
},
{
name: 'fruit2',
list: [
{ name: 'apple2', value: 12 },
{ name: 'banana2', value: 22 },
{ name: 'orange2', value: 20 }
]
}
],
key: 1, // table key
formTheadOptions: ['apple', 'banana', 'orange'], // 可选择表头
checkboxVal: defaultFormThead, // checkboxVal
formThead: defaultFormThead // 默认表头
}
},
watch: {
checkboxVal(valArr) {
this.formThead = this.formTheadOptions.filter(i => valArr.indexOf(i) >= 0);
this.key = this.key + 1;// 为了保证table 每次都会重渲 (牺牲性能保证效果,当然也可以不用)
}
}
};
</script>

View File

@@ -0,0 +1,51 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-checkbox-group v-model="formThead">
<el-checkbox label="apple">apple</el-checkbox>
<el-checkbox label="banana">banana</el-checkbox>
<el-checkbox label="orange">orange</el-checkbox>
</el-checkbox-group>
</div>
<el-table :data="tableData" 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">
<template scope="scope">
{{scope.row.list[index].value}}
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
data() {
return {
tableData: [
{
name: 'fruit1',
list: [
{ name: 'apple1', value: 10 },
{ name: 'banana1', value: 20 },
{ name: 'orange1', value: 20 }
]
},
{
name: 'fruit2',
list: [
{ name: 'apple2', value: 12 },
{ name: 'banana2', value: 22 },
{ name: 'orange2', value: 20 }
]
}
],
formThead: ['apple', 'banana']
}
}
};
</script>

View File

@@ -22,10 +22,10 @@
<el-button class="filter-item" type="primary" v-waves icon="search" @click="handleFilter">搜索</el-button> <el-button class="filter-item" type="primary" v-waves icon="search" @click="handleFilter">搜索</el-button>
<el-button class="filter-item" style="margin-left: 10px;" @click="handleCreate" type="primary" icon="edit">添加</el-button> <el-button class="filter-item" style="margin-left: 10px;" @click="handleCreate" type="primary" icon="edit">添加</el-button>
<el-button class="filter-item" type="primary" icon="document" @click="handleDownload">导出</el-button> <el-button class="filter-item" type="primary" icon="document" @click="handleDownload">导出</el-button>
<el-checkbox class="filter-item" @change='tableKey=tableKey+1' v-model="showAuditor">显示审核人</el-checkbox> <el-checkbox class="filter-item" @change='tableKey=tableKey+1' v-model="showAuditor">显示审核人</el-checkbox>
</div> </div>
<el-table :key='tableKey' :data="list" v-loading.body="listLoading" border fit highlight-current-row style="width: 100%"> <el-table :key='tableKey' :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="序号" width="65">
<template scope="scope"> <template scope="scope">
@@ -58,10 +58,9 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column width="80px" label="重要性"> <el-table-column width="80px" label="重要性">
<template scope="scope"> <template scope="scope">
<wscn-icon-svg v-for="n in +scope.row.importance" icon-class="wujiaoxing" class="meta-item__icon" :key="n" /> <wscn-icon-svg v-for="n in +scope.row.importance" icon-class="wujiaoxing" class="meta-item__icon" :key="n" />
</template> </template>
</el-table-column> </el-table-column>
@@ -71,28 +70,28 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column class-name="status-col" label="状态" width="90"> <el-table-column class-name="status-col" label="状态" width="90">
<template scope="scope"> <template scope="scope">
<el-tag :type="scope.row.status | statusFilter">{{scope.row.status}}</el-tag> <el-tag :type="scope.row.status | statusFilter">{{scope.row.status}}</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column align="center" label="操作" width="150"> <el-table-column align="center" label="操作" width="150">
<template scope="scope"> <template scope="scope">
<el-button v-if="scope.row.status!='published'" size="small" type="success" @click="handleModifyStatus(scope.row,'published')">发布 <el-button v-if="scope.row.status!='published'" size="small" type="success" @click="handleModifyStatus(scope.row,'published')">发布
</el-button> </el-button>
<el-button v-if="scope.row.status!='draft'" size="small" @click="handleModifyStatus(scope.row,'draft')">草稿 <el-button v-if="scope.row.status!='draft'" size="small" @click="handleModifyStatus(scope.row,'draft')">草稿
</el-button> </el-button>
<el-button v-if="scope.row.status!='deleted'" size="small" type="danger" @click="handleModifyStatus(scope.row,'deleted')">删除 <el-button v-if="scope.row.status!='deleted'" size="small" type="danger" @click="handleModifyStatus(scope.row,'deleted')">删除
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<div v-show="!listLoading" class="pagination-container"> <div v-show="!listLoading" class="pagination-container">
<el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page.sync="listQuery.page" :page-sizes="[10,20,30, 50]" <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page.sync="listQuery.page"
:page-size="listQuery.limit" layout="total, sizes, prev, pager, next, jumper" :total="total"> :page-sizes="[10,20,30, 50]" :page-size="listQuery.limit" layout="total, sizes, prev, pager, next, jumper" :total="total">
</el-pagination> </el-pagination>
</div> </div>
@@ -138,14 +137,14 @@
</el-dialog> </el-dialog>
<el-dialog title="阅读数统计" :visible.sync="dialogPvVisible" size="small"> <el-dialog title="阅读数统计" :visible.sync="dialogPvVisible" size="small">
<el-table :data="pvData" border fit highlight-current-row style="width: 100%"> <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="key" label="渠道"> </el-table-column>
<el-table-column prop="pv" label="pv"> </el-table-column> <el-table-column prop="pv" label="pv"> </el-table-column>
</el-table> </el-table>
<span slot="footer" class="dialog-footer"> <span slot="footer" class="dialog-footer">
<el-button type="primary" @click="dialogPvVisible = false"> </el-button> <el-button type="primary" @click="dialogPvVisible = false"> </el-button>
</span> </span>
</el-dialog > </el-dialog>
</div> </div>
</template> </template>
@@ -155,10 +154,10 @@
import { parseTime } from 'utils'; import { parseTime } from 'utils';
const calendarTypeOptions = [ const calendarTypeOptions = [
{ key: 'FD', display_name: '经济数据' }, { key: 'CN', display_name: '中国' },
{ key: 'FE', display_name: '财经大事' }, { key: 'US', display_name: '美国' },
{ key: 'BI', display_name: '国债发行' }, { key: 'JP', display_name: '日本' },
{ key: 'VN', display_name: '假期报告' } { key: 'EU', display_name: '欧元区' }
]; ];
// arr to obj // arr to obj

View File

@@ -32,6 +32,7 @@
</el-table> </el-table>
</div> </div>
</template> </template>
<script> <script>
import { getList } from 'api/article'; import { getList } from 'api/article';
export default { export default {

View File

@@ -1,15 +1,24 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<div class="wrapper"> <div class="wrapper">
<code> <code>
这半年来一直在用vue写管理后台目前后台已经有七十多个页面十几种权限但维护成本依然很低效率依然很高所以准备开源分享一下后台开发的经验和成果目前的技术栈主要的采用vue+element+axios.由于是个人项目所以数据请求都是用了mockjs代替会出一系列的教程配套文章如如何从零构建后台项目框架如何做完整的用户系统如权限验证二次登录等如何二次开发组件如富文本如何整合七牛等等文章各种后台开发经验等等莫急~~ 这半年来一直在用vue写管理后台目前后台已经有百来个个页面十几种权限但维护成本依然很低所以准备开源分享一下后台开发的经验和成果目前的技术栈主要的采用vue+element+axios由webpack2打包.由于是个人项目所以数据请求都是用了mockjs模拟注意在次项目基础上改造开发时请移除mock文件
写了一个系列的教程配套文章如何从零构建后一个完整的后台项目:
<ul>
<li><a target='_blank' class='lin' href="https://github.com/PanJiaChen/vue-element-admin/wiki">wiki</a></li>
<li><a target='_blank' href="https://juejin.im/post/59097cd7a22b9d0065fb61d2">手摸手带你用 vue 撸后台 系列一(基础篇)</a></li>
<li><a target='_blank' href="https://juejin.im/post/591aa14f570c35006961acac">手摸手带你用 vue 撸后台 系列二(登录权限篇)</a></li>
<li><a target='_blank' href="https://juejin.im/post/593121aa0ce4630057f70d35">手摸手带你用 vue 撸后台 系列三 (实战篇)</a></li>
<li><a target='_blank' href="https://segmentfault.com/a/1190000009090836">手摸手带你封装一个vue component</a></li>
</ul>
</code> </code>
</div> </div>
</div> </div>
</template> </template>
<style scoped> <style scoped>
.wrapper{ .wrapper{
width: 800px; width: 800px;
margin: 30px auto; margin: 30px auto;
} }
</style> </style>

View File

@@ -11,9 +11,7 @@
name: 'AppMain', name: 'AppMain',
computed: { computed: {
key() { key() {
return this.$route.name !== undefined return this.$route.name !== undefined ? this.$route.name + +new Date() : this.$route + +new Date()
? this.$route.name + +new Date()
: this.$route + +new Date()
} }
} }
} }

View File

@@ -1,7 +1,7 @@
<template> <template>
<div class="app-wrapper" :class="{hideSidebar:!sidebar.opened}"> <div class="app-wrapper" :class="{hideSidebar:!sidebar.opened}">
<div class="sidebar-wrapper"> <div class="sidebar-wrapper">
<Sidebar class="sidebar-container"/> <Sidebar class="sidebar-container" />
</div> </div>
<div class="main-container"> <div class="main-container">
<Navbar/> <Navbar/>
@@ -27,9 +27,9 @@
} }
} }
</script> </script>
<style rel="stylesheet/scss" lang="scss" scoped> <style rel="stylesheet/scss" lang="scss" scoped>
@import "src/styles/mixin.scss"; @import "src/styles/mixin.scss";
.app-wrapper { .app-wrapper {
@include clearfix; @include clearfix;
position: relative; position: relative;
@@ -59,12 +59,17 @@
bottom: 0; bottom: 0;
left: 0; left: 0;
z-index: 1001; z-index: 1001;
overflow-x: hidden; overflow: hidden;
transition: all .28s ease-out; transition: all .28s ease-out;
@include scrollBar;
} }
.sidebar-container { .sidebar-container {
transition: all .28s ease-out; transition: all .28s ease-out;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: -17px;
overflow-y: scroll;
} }
.main-container { .main-container {
min-height: 100%; min-height: 100%;

View File

@@ -34,6 +34,7 @@
} }
} }
</script> </script>
<style rel="stylesheet/scss" lang="scss" scoped> <style rel="stylesheet/scss" lang="scss" scoped>
.app-levelbar.el-breadcrumb { .app-levelbar.el-breadcrumb {
display: inline-block; display: inline-block;

View File

@@ -1,20 +1,22 @@
<template> <template>
<el-menu class="navbar" mode="horizontal"> <el-menu class="navbar" mode="horizontal">
<Hamburger class="hamburger-container" :toggleClick="toggleSideBar" :isActive="sidebar.opened"></Hamburger> <hamburger class="hamburger-container" :toggleClick="toggleSideBar" :isActive="sidebar.opened"></hamburger>
<levelbar></levelbar> <levelbar></levelbar>
<ErrLog v-if="log.length>0" class="errLog-container" :logsList="log"></ErrLog> <tabs-view></tabs-view>
<error-log v-if="log.length>0" class="errLog-container" :logsList="log"></error-log>
<screenfull class='screenfull'></screenfull>
<el-dropdown class="avatar-container" trigger="click"> <el-dropdown class="avatar-container" trigger="click">
<div class="avatar-wrapper"> <div class="avatar-wrapper">
<img class="user-avatar" :src="avatar+'?imageView2/1/w/80/h/80'"> <img class="user-avatar" :src="avatar+'?imageView2/1/w/80/h/80'">
<i class="el-icon-caret-bottom"/> <i class="el-icon-caret-bottom"></i>
</div> </div>
<el-dropdown-menu class="user-dropdown" slot="dropdown"> <el-dropdown-menu class="user-dropdown" slot="dropdown">
<router-link class='inlineBlock' to="/"> <router-link class='inlineBlock' to="/">
<el-dropdown-item> <el-dropdown-item>
首页 首页
</el-dropdown-item> </el-dropdown-item>
</router-link> </router-link>
<router-link class='inlineBlock' to="/admin/profile"> <router-link class='inlineBlock' to="/admin/profile">
<el-dropdown-item> <el-dropdown-item>
设置 设置
</el-dropdown-item> </el-dropdown-item>
@@ -28,15 +30,19 @@
<script> <script>
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import Levelbar from './Levelbar'; import Levelbar from './Levelbar';
import TabsView from './TabsView';
import Hamburger from 'components/Hamburger'; import Hamburger from 'components/Hamburger';
import ErrLog from 'components/ErrLog'; import Screenfull from 'components/Screenfull';
import ErrorLog from 'components/ErrLog';
import errLogStore from 'store/errLog'; import errLogStore from 'store/errLog';
export default { export default {
components: { components: {
Levelbar, Levelbar,
TabsView,
Hamburger, Hamburger,
ErrLog ErrorLog,
Screenfull
}, },
data() { data() {
return { return {
@@ -62,6 +68,7 @@
} }
} }
</script> </script>
<style rel="stylesheet/scss" lang="scss" scoped> <style rel="stylesheet/scss" lang="scss" scoped>
.navbar { .navbar {
height: 50px; height: 50px;
@@ -78,6 +85,12 @@
position: absolute; position: absolute;
right: 150px; right: 150px;
} }
.screenfull{
position: absolute;
right: 90px;
top: 16px;
color: red;
}
.avatar-container { .avatar-container {
height: 50px; height: 50px;
display: inline-block; display: inline-block;

View File

@@ -6,9 +6,9 @@
<script> <script>
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import SidebarItem from './SidebarItem'; import SidebarItem from './SidebarItem';
export default { export default {
components:{SidebarItem}, components: { SidebarItem },
computed: { computed: {
...mapGetters([ ...mapGetters([
'permission_routers' 'permission_routers'
@@ -16,6 +16,7 @@
} }
} }
</script> </script>
<style rel="stylesheet/scss" lang="scss" scoped> <style rel="stylesheet/scss" lang="scss" scoped>
.el-menu { .el-menu {
min-height: 100%; min-height: 100%;

View File

@@ -34,6 +34,7 @@
} }
} }
</script> </script>
<style rel="stylesheet/scss" lang="scss" scoped> <style rel="stylesheet/scss" lang="scss" scoped>
.wscn-icon { .wscn-icon {
margin-right: 10px; margin-right: 10px;

View File

@@ -0,0 +1,45 @@
<template>
<div class='tabs-view-container'>
<router-link class="tabs-view" v-for="tag in Array.from(visitedViews)" :to="tag.path" :key="tag.path">
<el-tag :closable="true" @close='closeViewTabs(tag,$event)'>
{{tag.name}}
</el-tag>
</router-link>
</div>
</template>
<script>
export default {
computed: {
visitedViews() {
return this.$store.state.app.visitedViews.slice(-6)
}
},
methods: {
closeViewTabs(view, $event) {
this.$store.dispatch('delVisitedViews', view)
$event.preventDefault()
},
addViewTabs() {
this.$store.dispatch('addVisitedViews', this.$route.matched[this.$route.matched.length - 1])
}
},
watch: {
$route() {
this.addViewTabs()
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.tabs-view-container{
display: inline-block;
vertical-align: top;
margin-left: 10px;
.tabs-view{
margin-left: 10px;
}
}
</style>

View File

@@ -1,10 +1,10 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<div style='margin-bottom:15px;'>你的权限 {{roles}}</div> <div style='margin-bottom:15px;'>你的权限 {{roles}}</div>
切换权限 切换权限
<el-radio-group v-model="role"> <el-radio-group v-model="role">
<el-radio-button label="editor"></el-radio-button> <el-radio-button label="editor"></el-radio-button>
</el-radio-group> </el-radio-group>
</div> </div>
</template> </template>
@@ -23,8 +23,9 @@
}, },
watch: { watch: {
role(val) { role(val) {
this.$store.commit('SET_ROLES', [val]); this.$store.dispatch('ChangeRole', val).then(() => {
this.$router.push({ path: '/permission/index?' + +new Date() }); this.$router.push({ path: '/permission/index?' + +new Date() });
})
} }
} }
} }

View File

@@ -0,0 +1,44 @@
<template>
<el-upload
action="https://upload.qbox.me"
:data="dataObj"
drag
:multiple="true"
:before-upload="beforeUpload">
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
</el-upload>
</template>
<script>
import { getToken } from 'api/qiniu'; // 获取七牛token 后端通过Access Key,Secret Key,bucket等生成token
// 七牛官方sdk https://developer.qiniu.com/sdk#official-sdk
export default{
data() {
return {
dataObj: { token: '', key: '' },
image_uri: [],
fileList: []
}
},
methods: {
beforeUpload() {
const _self = this;
return new Promise((resolve, reject) => {
getToken().then(response => {
const key = response.data.qiniu_key;
const token = response.data.qiniu_token;
_self._data.dataObj.token = token;
_self._data.dataObj.key = key;
resolve(true);
}).catch(err => {
console.log(err)
reject(false)
});
});
}
}
}
</script>

View File

@@ -76,17 +76,17 @@ export default {
</script> </script>
<style scoped> <style scoped>
.box-card{ .box-card{
width: 400px; width: 400px;
margin: 20px auto; margin: 20px auto;
} }
.block{ .block{
padding: 30px 24px; padding: 30px 24px;
} }
.alert-item{ .alert-item{
margin-bottom: 10px; margin-bottom: 10px;
} }
.tag-item{ .tag-item{
margin-right: 15px; margin-right: 15px;
} }
</style> </style>