Compare commits

...

33 Commits

Author SHA1 Message Date
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
Pan
96032becf9 update version && release 2017-05-31 16:12:53 +08:00
Pan
9da96e2083 refine code 2017-05-31 16:06:41 +08:00
Pan
44166278ff update readme 2017-05-31 15:55:56 +08:00
Pan
327ed42e24 sidebar accept Nested Routes 2017-05-31 15:52:27 +08:00
Pan
677ef6dbe3 router Code Splitting use import replace require.ensure 2017-05-29 13:19:05 +08:00
55 changed files with 673 additions and 871 deletions

View File

@@ -50,8 +50,11 @@ Join the group on QQ 591724180.
- Multi-environments distribution - Multi-environments distribution
- Dashboard - Dashboard
- Two-factor authentication - Two-factor authentication
- Collapsing sidebar - Collapsing sidebar (support nested routes)
- Mock data - Mock data
- cache tabs example
- screenfull
- markdown2html
## Development ## Development
@@ -111,6 +114,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,16 @@
- 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
## 开发 ## 开发
@@ -104,6 +113,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.0", "version": "1.0.2",
"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-es2015": "6.24.1",
"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

@@ -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

@@ -1,64 +1,67 @@
import Vue from 'vue'; import Vue from 'vue';
import Router from 'vue-router'; import Router from 'vue-router';
/* layout*/ /* layout */
import Layout from '../views/layout/Layout'; import Layout from '../views/layout/Layout';
// dashboard /* login */
const dashboard = resolve => require(['../views/dashboard/index'], resolve);
/* error page*/
const Err404 = resolve => require(['../views/error/404'], resolve);
const Err401 = resolve => require(['../views/error/401'], resolve);
/* login*/
import Login from '../views/login/'; import Login from '../views/login/';
import authRedirect from '../views/login/authredirect'; const authRedirect = () => import('../views/login/authredirect');
import sendPWD from '../views/login/sendpwd'; const sendPWD = () => import('../views/login/sendpwd');
import reset from '../views/login/reset'; const reset = () => import('../views/login/reset');
/* Introduction*/ /* dashboard */
const Introduction = resolve => require(['../views/introduction/index'], resolve); const dashboard = () => import('../views/dashboard/index');
/* components*/ /* Introduction */
const componentsIndex = resolve => require(['../views/components/index'], resolve); const Introduction = () => import('../views/introduction/index');
const Tinymce = resolve => require(['../views/components/tinymce'], resolve);
const Markdown = resolve => require(['../views/components/markdown'], resolve); /* components */
const JsonEditor = resolve => require(['../views/components/jsoneditor'], resolve); const componentsIndex = () => import('../views/components/index');
const DndList = resolve => require(['../views/components/dndlist'], resolve); const Tinymce = () => import('../views/components/tinymce');
const AvatarUpload = resolve => require(['../views/components/avatarUpload'], resolve); const Markdown = () => import('../views/components/markdown');
const Dropzone = resolve => require(['../views/components/dropzone'], resolve); const JsonEditor = () => import('../views/components/jsoneditor');
const Sticky = resolve => require(['../views/components/sticky'], resolve); const DndList = () => import('../views/components/dndlist');
const SplitPane = resolve => require(['../views/components/splitpane'], resolve); const AvatarUpload = () => import('../views/components/avatarUpload');
const CountTo = resolve => require(['../views/components/countTo'], resolve); const Dropzone = () => import('../views/components/dropzone');
const Mixin = resolve => require(['../views/components/mixin'], resolve); const Sticky = () => import('../views/components/sticky');
const SplitPane = () => import('../views/components/splitpane');
const CountTo = () => import('../views/components/countTo');
const Mixin = () => import('../views/components/mixin');
/* charts*/ /* charts */
const chartIndex = resolve => require(['../views/charts/index'], resolve); const chartIndex = () => import('../views/charts/index');
const KeyboardChart = resolve => require(['../views/charts/keyboard'], resolve); const KeyboardChart = () => import('../views/charts/keyboard');
const KeyboardChart2 = resolve => require(['../views/charts/keyboard2'], resolve); const KeyboardChart2 = () => import('../views/charts/keyboard2');
const LineMarker = resolve => require(['../views/charts/line'], resolve); const LineMarker = () => import('../views/charts/line');
const MixChart = resolve => require(['../views/charts/mixchart'], resolve); const MixChart = () => import('../views/charts/mixchart');
/* error log*/ /* error page */
const ErrorLog = resolve => require(['../views/errlog/index'], resolve); const Err404 = () => import('../views/error/404');
const Err401 = () => import('../views/error/401');
/* excel*/ /* error log */
const ExcelDownload = resolve => require(['../views/excel/index'], resolve); const ErrorLog = () => import('../views/errlog/index');
/* theme*/ /* excel */
const Theme = resolve => require(['../views/theme/index'], resolve); const ExcelDownload = () => import('../views/excel/index');
/* theme */
const Theme = () => import('../views/theme/index');
/* example*/ /* example*/
const DynamicTable = resolve => require(['../views/example/dynamictable'], resolve); const TableLayout = () => import('../views/example/table/index');
const Table = resolve => require(['../views/example/table'], resolve); const DynamicTable = () => import('../views/example/table/dynamictable');
const DragTable = resolve => require(['../views/example/dragTable'], resolve); const Table = () => import('../views/example/table/table');
const InlineEditTable = resolve => require(['../views/example/inlineEditTable'], resolve); const DragTable = () => import('../views/example/table/dragTable');
const Form1 = resolve => require(['../views/example/form1'], resolve); const InlineEditTable = () => import('../views/example/table/inlineEditTable');
const Form = () => import('../views/example/form');
const Tab = () => import('../views/example/tab/index');
/* permission */ /* permission */
const Permission = resolve => require(['../views/permission/index'], resolve); const Permission = () => import('../views/permission/index');
Vue.use(Router); Vue.use(Router);
@@ -69,7 +72,7 @@ Vue.use(Router);
* redirect : if redirect:noredirect will not redirct in the levelbar * redirect : if redirect:noredirect will not redirct in the levelbar
* noDropdown : if noDropdown:true will not has submenu * noDropdown : if noDropdown:true will not has submenu
* meta : { role: ['admin'] } will control the page role * meta : { role: ['admin'] } will control the page role
*/ **/
export const constantRouterMap = [ export const constantRouterMap = [
{ path: '/login', component: Login, hidden: true }, { path: '/login', component: Login, hidden: true },
@@ -120,17 +123,17 @@ export const asyncRouterMap = [
name: '组件', name: '组件',
icon: 'zujian', icon: 'zujian',
children: [ children: [
{ path: 'index', component: componentsIndex, name: '介绍 ' }, { path: 'index', component: componentsIndex, name: '介绍 ' },
{ path: 'tinymce', component: Tinymce, name: '富文本编辑器' }, { path: 'tinymce', component: Tinymce, name: '富文本编辑器' },
{ path: 'markdown', component: Markdown, name: 'Markdown' }, { path: 'markdown', component: Markdown, name: 'Markdown' },
{ path: 'jsoneditor', component: JsonEditor, name: 'JSON编辑器' }, { path: 'jsoneditor', component: JsonEditor, name: 'JSON编辑器' },
{ path: 'dndlist', component: DndList, name: '列表拖拽' }, { path: 'dndlist', component: DndList, name: '列表拖拽' },
{ path: 'splitpane', component: SplitPane, name: 'SplitPane' }, { path: 'splitpane', component: SplitPane, name: 'SplitPane' },
{ path: 'avatarupload', component: AvatarUpload, name: '头像上传' }, { path: 'avatarupload', component: AvatarUpload, name: '头像上传' },
{ path: 'dropzone', component: Dropzone, name: 'Dropzone' }, { path: 'dropzone', component: Dropzone, name: 'Dropzone' },
{ path: 'sticky', component: Sticky, name: 'Sticky' }, { path: 'sticky', component: Sticky, name: 'Sticky' },
{ path: 'countto', component: CountTo, name: 'CountTo' }, { path: 'countto', component: CountTo, name: 'CountTo' },
{ path: 'mixin', component: Mixin, name: '小组件' } { path: 'mixin', component: Mixin, name: '小组件' }
] ]
}, },
{ {
@@ -140,11 +143,11 @@ export const asyncRouterMap = [
name: '图表', name: '图表',
icon: 'tubiaoleixingzhengchang', icon: 'tubiaoleixingzhengchang',
children: [ children: [
{ path: 'index', component: chartIndex, name: '介绍' }, { path: 'index', component: chartIndex, name: '介绍' },
{ path: 'keyboard', component: KeyboardChart, name: '键盘图表' }, { path: 'keyboard', component: KeyboardChart, name: '键盘图表' },
{ path: 'keyboard2', component: KeyboardChart2, name: '键盘图表2' }, { path: 'keyboard2', component: KeyboardChart2, name: '键盘图表2' },
{ path: 'line', component: LineMarker, name: '折线图' }, { path: 'line', component: LineMarker, name: '折线图' },
{ path: 'mixchart', component: MixChart, name: '混合图表' } { path: 'mixchart', component: MixChart, name: '混合图表' }
] ]
}, },
{ {
@@ -154,8 +157,8 @@ export const asyncRouterMap = [
name: '错误页面', name: '错误页面',
icon: '404', icon: '404',
children: [ children: [
{ path: '401', component: Err401, name: '401' }, { path: '401', component: Err401, name: '401' },
{ path: '404', component: Err404, name: '404' } { path: '404', component: Err404, name: '404' }
] ]
}, },
{ {
@@ -192,11 +195,22 @@ export const asyncRouterMap = [
name: '综合实例', name: '综合实例',
icon: 'zonghe', icon: 'zonghe',
children: [ children: [
{ path: 'dynamictable', component: DynamicTable, name: '动态table' }, {
{ path: 'dragtable', component: DragTable, name: '拖拽table' }, path: '/table',
{ path: 'inline_edit_table', component: InlineEditTable, name: 'table内编辑' }, component: TableLayout,
{ path: 'table', component: Table, name: '综合table' }, redirect: '/table/table',
{ path: 'form1', component: Form1, name: '综合form1' } name: 'Table',
children: [
{ path: 'dynamictable', component: DynamicTable, name: '动态table' },
{ path: 'dragtable', component: DragTable, name: '拖拽table' },
{ path: 'inline_edit_table', component: InlineEditTable, name: 'table内编辑' },
{ path: 'table', component: Table, name: '综合table' }
]
},
{ 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

@@ -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

@@ -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

@@ -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,7 +1,7 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<div class="filter-container"> <div class="filter-container">
<el-checkbox-group v-model="formThead"> <el-checkbox-group v-model="formThead">
<el-checkbox label="apple">apple</el-checkbox> <el-checkbox label="apple">apple</el-checkbox>
<el-checkbox label="banana">banana</el-checkbox> <el-checkbox label="banana">banana</el-checkbox>
<el-checkbox label="orange">orange</el-checkbox> <el-checkbox label="orange">orange</el-checkbox>
@@ -18,22 +18,21 @@
</el-table> </el-table>
</div> </div>
</template> </template>
<script> <script>
export default {
data() {
export default { return {
data() { tableData: [{
return { name: '水果',
tableData: [{ list: [{ name: 'apple', value: 10 }, { name: 'banana', value: 20 }, { name: 'orange', value: 20 }]
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 }]
name: '水果2', }],
list: [{ name: 'apple2', value: 12 }, { name: 'banana2', value: 22 }, { name: 'orange', value: 20 }] formThead: ['apple', 'banana']
}], }
formThead: ['apple', 'banana']
} }
} };
};
</script> </script>

View File

@@ -0,0 +1,3 @@
<template>
<router-view></router-view>
</template>

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,21 @@
<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> <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" />
</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>
@@ -29,14 +30,16 @@
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import Levelbar from './Levelbar'; import Levelbar from './Levelbar';
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,
Hamburger, Hamburger,
ErrLog ErrorLog,
Screenfull
}, },
data() { data() {
return { return {
@@ -62,6 +65,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 +82,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

@@ -1,30 +1,14 @@
<template> <template>
<el-menu :unique-opened='true' mode="vertical" theme="dark" :default-active="$route.path"> <el-menu mode="vertical" theme="dark" :default-active="$route.path">
<template v-for="item in permission_routers" v-if="!item.hidden"> <sidebar-item :routes='permission_routers'></sidebar-item>
<el-submenu :index="item.name" v-if="!item.noDropdown">
<template slot="title">
<wscn-icon-svg :icon-class="item.icon||'wenzhang1'" /> {{item.name}}
</template>
<router-link v-for="child in item.children" :key="child.path" v-if="!child.hidden" class="title-link" :to="item.path+'/'+child.path">
<el-menu-item :index="item.path+'/'+child.path">
{{child.name}}
</el-menu-item>
</router-link>
</el-submenu>
<router-link v-if="item.noDropdown&&item.children.length>0" :to="item.path+'/'+item.children[0].path">
<el-menu-item :index="item.path+'/'+item.children[0].path">
<wscn-icon-svg :icon-class="item.icon||'geren1'" /> {{item.children[0].name}}
</el-menu-item>
</router-link>
</template>
</el-menu> </el-menu>
</template> </template>
<script> <script>
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import SidebarItem from './SidebarItem';
export default { export default {
name: 'Sidebar', components: { SidebarItem },
computed: { computed: {
...mapGetters([ ...mapGetters([
'permission_routers' 'permission_routers'
@@ -37,11 +21,4 @@
.el-menu { .el-menu {
min-height: 100%; min-height: 100%;
} }
.wscn-icon {
margin-right: 10px;
}
.hideSidebar .title-link{
display: block;
text-indent: 10px;
}
</style> </style>

View File

@@ -0,0 +1,47 @@
<template>
<div>
<template v-for="item in routes">
<router-link v-if="!item.hidden&&item.noDropdown&&item.children.length>0" :to="item.path+'/'+item.children[0].path">
<el-menu-item :index="item.path+'/'+item.children[0].path">
<wscn-icon-svg v-if='item.icon' :icon-class="item.icon" /> {{item.children[0].name}}
</el-menu-item>
</router-link>
<el-submenu :index="item.name" v-if="!item.noDropdown&&!item.hidden">
<template slot="title">
<wscn-icon-svg v-if='item.icon' :icon-class="item.icon" /> {{item.name}}
</template>
<template v-for="child in item.children" v-if='!child.hidden'>
<sidebar-item class='menu-indent' v-if='child.children&&child.children.length>0' :routes='[child]'> </sidebar-item>
<router-link v-else class="menu-indent" :to="item.path+'/'+child.path">
<el-menu-item :index="item.path+'/'+child.path">
{{child.name}}
</el-menu-item>
</router-link>
</template>
</el-submenu>
</template>
</div>
</template>
<script>
export default {
name: 'SidebarItem',
props: {
routes: {
type: Array
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.wscn-icon {
margin-right: 10px;
}
.hideSidebar .menu-indent{
display: block;
text-indent: 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>