Compare commits

...

21 Commits

Author SHA1 Message Date
Pan
31d9da8b9f [release] 3.7.0 2018-06-01 14:11:02 +08:00
Pan
600e75d0a2 bump jsonlint #728 2018-06-01 13:34:45 +08:00
Pan
9ba1ea6933 perf[el-dragDialog]: add dragDialog callback function 2018-06-01 10:38:27 +08:00
Pan
320e941d9a fix[Sticky]: fixed bug in resize #725 2018-05-31 17:16:24 +08:00
花裤衩
d0f6d3f1f6 chore: use babel-plugin-dynamic-import-node to lazy-loading (#727)
detail see https://panjiachen.github.io/vue-element-admin-site/#/zh-cn/lazy-loading?id=%E8%B7%AF%E7%94%B1%E6%87%92%E5%8A%A0%E8%BD%BD
2018-05-31 16:53:08 +08:00
Pan
0375542009 fix[Sticky]: fixed bug in resize #724 2018-05-31 13:22:12 +08:00
Pan
03e5f762b3 fix[Sticky]: fixed bug in resize #721 2018-05-31 10:50:38 +08:00
Heedong Im
bd0227feed Upgrade vue-loader (#723)
The vue-loader build error caused by Prettier have been fixed
2018-05-31 10:08:00 +08:00
Pan
20aad46416 fix[permission]: directive demo typo 2018-05-30 15:27:44 +08:00
花裤衩
4fc25241fe refactor example demo (#713)
* refactor: Adjust the example directory structure

* perf form demo

* refine editor-slide-upload css

* refine demo
2018-05-30 15:25:08 +08:00
Pan
6327869106 perf[transition]: refine transition animation 2018-05-29 14:31:55 +08:00
Pan
c861dd10cf fix: [404.vue]: return-home button href bug 2018-05-28 15:33:47 +08:00
花裤衩
0a196f79ba Add guide page #534 (#707)
detail see #534
2018-05-28 14:36:06 +08:00
Pan
d2d323bb02 fix: [sidebar.css] : style bug in windows #702 2018-05-28 11:01:07 +08:00
Pan
66613f0373 perf[filter]: remove duplicate code #661 2018-05-12 23:17:14 +08:00
Pan
6795c26d02 fix[Tinymce]: custom-btn bug in fullscreen 2018-05-09 14:36:17 +08:00
花裤衩
597df4844a feature[permission]: add v-permission (#653) 2018-05-08 22:15:34 +08:00
Pan
1e103cf151 perf[dashboard]: add resonsive dashboard 2018-05-05 15:42:46 +08:00
花裤衩
99d53ee0ca refactor[ScrollBar]: use el-scrollbar (#646) 2018-05-05 15:26:08 +08:00
Pan
f9d510ea78 perf[el-dragDialog]: use curring 2018-05-04 10:26:29 +08:00
Pan
9fbb028124 perf[permission]: add the verification of roles 2018-05-02 17:37:47 +08:00
61 changed files with 839 additions and 467 deletions

View File

@@ -8,5 +8,10 @@
}], }],
"stage-2" "stage-2"
], ],
"plugins": ["transform-vue-jsx", "transform-runtime"] "plugins": ["transform-vue-jsx", "transform-runtime"],
"env": {
"development":{
"plugins": ["dynamic-import-node"]
}
}
} }

View File

@@ -72,6 +72,7 @@ You need to install [node](http://nodejs.org/) and [git](https://git-scm.com/) l
- Drag and drop list - Drag and drop list
- Svg Sprite - Svg Sprite
- Dashboard - Dashboard
- Guide Page
- Mock data - Mock data
- Echarts - Echarts
- Clipboard - Clipboard

View File

@@ -84,6 +84,7 @@
- 列表拖拽 - 列表拖拽
- Svg Sprite 图标 - Svg Sprite 图标
- Dashboard - Dashboard
- 引导页
- 本地mock数据 - 本地mock数据
- Echarts 图表 - Echarts 图表
- Clipboard(剪贴复制) - Clipboard(剪贴复制)

View File

@@ -1,11 +1,11 @@
{ {
"name": "vue-element-admin", "name": "vue-element-admin",
"version": "3.6.6", "version": "3.7.0",
"description": "A magical vue admin. Typical templates for enterprise applications. Newest development stack of vue. Lots of awesome features", "description": "A magical vue admin. Typical templates for enterprise applications. Newest development stack of vue. Lots of awesome features",
"author": "Pan <panfree23@gmail.com>", "author": "Pan <panfree23@gmail.com>",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", "dev": "cross-env BABEL_ENV=development webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"build:prod": "cross-env NODE_ENV=production env_config=prod node build/build.js", "build:prod": "cross-env NODE_ENV=production env_config=prod node build/build.js",
"build:sit": "cross-env NODE_ENV=production env_config=sit node build/build.js", "build:sit": "cross-env NODE_ENV=production env_config=sit node build/build.js",
"lint": "eslint --ext .js,.vue src", "lint": "eslint --ext .js,.vue src",
@@ -29,13 +29,14 @@
"axios": "0.17.1", "axios": "0.17.1",
"clipboard": "1.7.1", "clipboard": "1.7.1",
"codemirror": "5.32.0", "codemirror": "5.32.0",
"driver.js": "0.5.2",
"dropzone": "5.2.0", "dropzone": "5.2.0",
"echarts": "3.8.5", "echarts": "3.8.5",
"element-ui": "2.3.2", "element-ui": "2.3.2",
"file-saver": "1.3.3", "file-saver": "1.3.3",
"font-awesome": "4.7.0", "font-awesome": "4.7.0",
"js-cookie": "2.2.0", "js-cookie": "2.2.0",
"jsonlint": "1.6.2", "jsonlint": "1.6.3",
"jszip": "3.1.5", "jszip": "3.1.5",
"mockjs": "1.0.1-beta3", "mockjs": "1.0.1-beta3",
"normalize.css": "7.0.0", "normalize.css": "7.0.0",
@@ -60,6 +61,7 @@
"babel-eslint": "8.0.3", "babel-eslint": "8.0.3",
"babel-helper-vue-jsx-merge-props": "2.0.3", "babel-helper-vue-jsx-merge-props": "2.0.3",
"babel-loader": "7.1.2", "babel-loader": "7.1.2",
"babel-plugin-dynamic-import-node": "^1.2.0",
"babel-plugin-syntax-jsx": "6.18.0", "babel-plugin-syntax-jsx": "6.18.0",
"babel-plugin-transform-runtime": "6.23.0", "babel-plugin-transform-runtime": "6.23.0",
"babel-plugin-transform-vue-jsx": "3.5.0", "babel-plugin-transform-vue-jsx": "3.5.0",
@@ -94,7 +96,7 @@
"svg-sprite-loader": "3.5.2", "svg-sprite-loader": "3.5.2",
"uglifyjs-webpack-plugin": "1.1.3", "uglifyjs-webpack-plugin": "1.1.3",
"url-loader": "0.6.2", "url-loader": "0.6.2",
"vue-loader": "13.5.0", "vue-loader": "13.7.2",
"vue-style-loader": "3.0.3", "vue-style-loader": "3.0.3",
"vue-template-compiler": "2.5.10", "vue-template-compiler": "2.5.10",
"webpack": "3.10.0", "webpack": "3.10.0",

View File

@@ -8,10 +8,11 @@ export function fetchList(query) {
}) })
} }
export function fetchArticle() { export function fetchArticle(id) {
return request({ return request({
url: '/article/detail', url: '/article/detail',
method: 'get' method: 'get',
params: { id }
}) })
} }

View File

@@ -1,57 +0,0 @@
<template>
<div class="scroll-container" ref="scrollContainer" @wheel.prevent="handleScroll" >
<div class="scroll-wrapper" ref="scrollWrapper" :style="{top: top + 'px'}">
<slot></slot>
</div>
</div>
</template>
<script>
const delta = 15
export default {
name: 'scrollBar',
data() {
return {
top: 0
}
},
methods: {
handleScroll(e) {
const eventDelta = e.wheelDelta || -e.deltaY * 3
const $container = this.$refs.scrollContainer
const $containerHeight = $container.offsetHeight
const $wrapper = this.$refs.scrollWrapper
const $wrapperHeight = $wrapper.offsetHeight
if (eventDelta > 0) {
this.top = Math.min(0, this.top + eventDelta)
} else {
if ($containerHeight - delta < $wrapperHeight) {
if (this.top < -($wrapperHeight - $containerHeight + delta)) {
this.top = this.top
} else {
this.top = Math.max(this.top + eventDelta, $containerHeight - $wrapperHeight - delta)
}
} else {
this.top = 0
}
}
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
@import '../../styles/variables.scss';
.scroll-container {
position: relative;
width: 100%;
height: 100%;
background-color: $menuBg;
.scroll-wrapper {
position: absolute;
width: 100%!important;
}
}
</style>

View File

@@ -28,22 +28,22 @@ export default {
return { return {
active: false, active: false,
position: '', position: '',
currentTop: '',
width: undefined, width: undefined,
height: undefined, height: undefined,
child: null, isSticky: false
stickyHeight: 0
} }
}, },
mounted() { mounted() {
this.height = this.$el.getBoundingClientRect().height this.height = this.$el.getBoundingClientRect().height
window.addEventListener('scroll', this.handleScroll) window.addEventListener('scroll', this.handleScroll)
window.addEventListener('resize', this.handleReize)
}, },
activated() { activated() {
this.handleScroll() this.handleScroll()
}, },
destroyed() { destroyed() {
window.removeEventListener('scroll', this.handleScroll) window.removeEventListener('scroll', this.handleScroll)
window.removeEventListener('resize', this.handleReize)
}, },
methods: { methods: {
sticky() { sticky() {
@@ -53,6 +53,7 @@ export default {
this.position = 'fixed' this.position = 'fixed'
this.active = true this.active = true
this.width = this.width + 'px' this.width = this.width + 'px'
this.isSticky = true
}, },
reset() { reset() {
if (!this.active) { if (!this.active) {
@@ -61,15 +62,21 @@ export default {
this.position = '' this.position = ''
this.width = 'auto' this.width = 'auto'
this.active = false this.active = false
this.isSticky = false
}, },
handleScroll() { handleScroll() {
this.width = this.$el.getBoundingClientRect().width this.width = this.$el.getBoundingClientRect().width
const offsetTop = this.$el.getBoundingClientRect().top const offsetTop = this.$el.getBoundingClientRect().top
if (offsetTop <= this.stickyTop) { if (offsetTop < this.stickyTop) {
this.sticky() this.sticky()
return return
} }
this.reset() this.reset()
},
handleReize() {
if (this.isSticky) {
this.width = this.$el.getBoundingClientRect().width + 'px'
}
} }
} }
} }

View File

@@ -87,9 +87,10 @@ export default {
</script> </script>
<style rel="stylesheet/scss" lang="scss" scoped> <style rel="stylesheet/scss" lang="scss" scoped>
.upload-container { .editor-slide-upload {
.editor-slide-upload {
margin-bottom: 20px; margin-bottom: 20px;
/deep/ .el-upload--picture-card {
width: 100%;
} }
} }
</style> </style>

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="tinymce-container editor-container"> <div class="tinymce-container editor-container" :class="{fullscreen:fullscreen}">
<textarea class="tinymce-textarea" :id="tinymceId"></textarea> <textarea class="tinymce-textarea" :id="tinymceId"></textarea>
<div class="editor-custom-btn-container"> <div class="editor-custom-btn-container">
<editorImage color="#1890ff" class="editor-upload-btn" @successCBK="imageSuccessCBK"></editorImage> <editorImage color="#1890ff" class="editor-upload-btn" @successCBK="imageSuccessCBK"></editorImage>
@@ -43,7 +43,8 @@ export default {
return { return {
hasChange: false, hasChange: false,
hasInit: false, hasInit: false,
tinymceId: this.id || 'vue-tinymce-' + +new Date() tinymceId: this.id || 'vue-tinymce-' + +new Date(),
fullscreen: false
} }
}, },
watch: { watch: {
@@ -91,6 +92,11 @@ export default {
this.hasChange = true this.hasChange = true
this.$emit('input', editor.getContent()) this.$emit('input', editor.getContent())
}) })
},
setup(editor) {
editor.on('FullscreenStateChanged', (e) => {
_this.fullscreen = e.state
})
} }
// 整合七牛上传 // 整合七牛上传
// images_dataimg_filter(img) { // images_dataimg_filter(img) {
@@ -168,6 +174,10 @@ export default {
top: 4px; top: 4px;
/*z-index: 2005;*/ /*z-index: 2005;*/
} }
.fullscreen .editor-custom-btn-container {
z-index: 10000;
position: fixed;
}
.editor-upload-btn { .editor-upload-btn {
display: inline-block; display: inline-block;
} }

View File

@@ -1,6 +1,6 @@
// Here is a list of the toolbar // Here is a list of the toolbar
// Detail list see https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols // Detail list see https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols
const toolbar = ['bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak fullscreen insertdatetime media table emoticons forecolor backcolor'] const toolbar = ['bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen']
export default toolbar export default toolbar

View File

@@ -1,18 +1,18 @@
export default{ export default{
bind(el, binding) { bind(el, binding, vnode) {
const dialogHeaderEl = el.querySelector('.el-dialog__header') const dialogHeaderEl = el.querySelector('.el-dialog__header')
const dragDom = el.querySelector('.el-dialog') const dragDom = el.querySelector('.el-dialog')
dialogHeaderEl.style.cssText += ';cursor:move;' dialogHeaderEl.style.cssText += ';cursor:move;'
dragDom.style.cssText += ';top:0px;' dragDom.style.cssText += ';top:0px;'
// 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null); // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
function getStyle(dom, attr) { const getStyle = (function() {
if (dom.currentStyle) { if (window.document.currentStyle) {
return dom.currentStyle[attr] return (dom, attr) => dom.currentStyle[attr]
} else { } else {
return getComputedStyle(dom, false)[attr] return (dom, attr) => getComputedStyle(dom, false)[attr]
}
} }
})()
dialogHeaderEl.onmousedown = (e) => { dialogHeaderEl.onmousedown = (e) => {
// 鼠标按下,计算当前元素距离可视区的距离 // 鼠标按下,计算当前元素距离可视区的距离
@@ -63,6 +63,9 @@ export default{
// 移动当前元素 // 移动当前元素
dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;` dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`
// emit onDrag event
vnode.child.$emit('dragDialog')
} }
document.onmouseup = function(e) { document.onmouseup = function(e) {

View File

@@ -0,0 +1,13 @@
import permission from './permission'
const install = function(Vue) {
Vue.directive('permission', permission)
}
if (window.Vue) {
window['permission'] = permission
Vue.use(install); // eslint-disable-line
}
permission.install = install
export default permission

View File

@@ -0,0 +1,23 @@
import store from '@/store'
export default{
inserted(el, binding, vnode) {
const { value } = binding
const roles = store.getters && store.getters.roles
if (value && value instanceof Array && value.length > 0) {
const permissionRoles = value
const hasPermission = roles.some(role => {
return permissionRoles.includes(role)
})
if (!hasPermission) {
el.parentNode && el.parentNode.removeChild(el)
}
} else {
throw new Error(`need roles! Like v-permission="['admin','editor']"`)
}
}
}

View File

@@ -1,3 +1,6 @@
// set function parseTime,formatTime to filter
export { parseTime, formatTime } from '@/utils'
function pluralize(time, label) { function pluralize(time, label) {
if (time === 1) { if (time === 1) {
return time + label return time + label
@@ -16,67 +19,8 @@ export function timeAgo(time) {
} }
} }
export function parseTime(time, cFormat) {
if (arguments.length === 0) {
return null
}
if ((time + '').length === 10) {
time = +time * 1000
}
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
let date
if (typeof time === 'object') {
date = time
} else {
date = new Date(parseInt(time))
}
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay()
}
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
let value = formatObj[key]
if (key === 'a') return ['一', '二', '三', '四', '五', '六', '日'][value - 1]
if (result.length > 0 && value < 10) {
value = '0' + value
}
return value || 0
})
return time_str
}
export function formatTime(time, option) {
time = +time * 1000
const d = new Date(time)
const now = Date.now()
const diff = (now - d) / 1000
if (diff < 30) {
return '刚刚'
} else if (diff < 3600) { // less 1 hour
return Math.ceil(diff / 60) + '分钟前'
} else if (diff < 3600 * 24) {
return Math.ceil(diff / 3600) + '小时前'
} else if (diff < 3600 * 24 * 2) {
return '1天前'
}
if (option) {
return parseTime(time, option)
} else {
return d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分'
}
}
/* 数字 格式化*/ /* 数字 格式化*/
export function nFormatter(num, digits) { export function numberFormatter(num, digits) {
const si = [ const si = [
{ value: 1E18, symbol: 'E' }, { value: 1E18, symbol: 'E' },
{ value: 1E15, symbol: 'P' }, { value: 1E15, symbol: 'P' },
@@ -93,12 +37,6 @@ export function nFormatter(num, digits) {
return num.toString() return num.toString()
} }
export function html2Text(val) {
const div = document.createElement('div')
div.innerHTML = val
return div.textContent || div.innerText
}
export function toThousandslsFilter(num) { export function toThousandslsFilter(num) {
return (+num || 0).toString().replace(/^-?\d+/g, m => m.replace(/(?=(?!\b)(\d{3})+$)/g, ',')) return (+num || 0).toString().replace(/^-?\d+/g, m => m.replace(/(?=(?!\b)(\d{3})+$)/g, ','))
} }

1
src/icons/svg/edit.svg Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1525760397212" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2919" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M860 504c-19.9 0-36 16.1-36 36 0 1.4 0.1 2.7 0.2 4h-0.2v344H136V200h376c19.9 0 36-16.1 36-36s-16.1-36-36-36H136c-39.8 0-72 32.2-72 72v688c0 39.8 32.2 72 72 72h688c39.8 0 72-32.2 72-72V544h-0.2c0.1-1.3 0.2-2.6 0.2-4 0-19.9-16.1-36-36-36z" p-id="2920"></path><path d="M1002.7 100.3L923.4 21c-28.1-28.1-73.9-27.9-102 0.2L424.2 418.4c-2.9 2.9-5.2 6.4-6.8 10.2L317.6 664c-5.6 13.2-1.7 26.5 6.8 35.1 8.5 8.6 21.9 12.5 35.2 6.9l235.5-99.7c3.8-1.6 7.2-3.9 10.2-6.8l397.2-397.2c28.1-28.1 28.3-73.9 0.2-102zM559.8 543l-137.4 58.2 58.2-137.4L759.4 185l79.2 79.2L559.8 543z m391.7-391.7l-62 62-79.2-79.2 62-62 0.2-0.2 79.2 79.2-0.2 0.2z" p-id="2921"></path></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

1
src/icons/svg/guide.svg Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1526033837694" class="icon" style="" viewBox="0 0 1117 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10274" xmlns:xlink="http://www.w3.org/1999/xlink" width="218.1640625" height="200"><defs><style type="text/css"></style></defs><path d="M53.865 558.08l289.92 121.6 560-492.16-491.52 530.56 371.84 140.8c8.96 3.2 19.2-1.28 22.4-10.24V848l260.48-816.64-1014.4 494.72c-8.96 4.48-12.16 14.72-8.32 23.68 2.56 3.84 5.76 7.04 9.6 8.32z m357.76 434.56l144.64-155.52-144.64-58.88v214.4z" p-id="10275"></path></svg>

After

Width:  |  Height:  |  Size: 664 B

1
src/icons/svg/list.svg Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1525761666409" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10880" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M107.2 212.8m-67.2 0a4.2 4.2 0 1 0 134.4 0 4.2 4.2 0 1 0-134.4 0Z" p-id="10881"></path><path d="M980.8 145.6 297.6 145.6c-9.6 0-16 8-16 16l0 102.4c0 9.6 8 16 16 16l683.2 0c9.6 0 16-8 16-16l0-102.4C996.8 152 988.8 145.6 980.8 145.6z" p-id="10882"></path><path d="M96 497.6m-67.2 0a4.2 4.2 0 1 0 134.4 0 4.2 4.2 0 1 0-134.4 0Z" p-id="10883"></path><path d="M968 430.4 284.8 430.4c-9.6 0-16 8-16 16l0 102.4c0 9.6 8 16 16 16l683.2 0c9.6 0 16-8 16-16l0-102.4C984 438.4 977.6 430.4 968 430.4z" p-id="10884"></path><path d="M96 795.2m-67.2 0a4.2 4.2 0 1 0 134.4 0 4.2 4.2 0 1 0-134.4 0Z" p-id="10885"></path><path d="M968 728 284.8 728c-9.6 0-16 8-16 16l0 102.4c0 9.6 8 16 16 16l683.2 0c9.6 0 16-8 16-16l0-102.4C984 736 977.6 728 968 728z" p-id="10886"></path></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -3,7 +3,10 @@ export default {
dashboard: 'Dashboard', dashboard: 'Dashboard',
introduction: 'Introduction', introduction: 'Introduction',
documentation: 'Documentation', documentation: 'Documentation',
guide: 'Guide',
permission: 'Permission', permission: 'Permission',
pagePermission: 'Page Permission',
directivePermission: 'Directive Permission',
icons: 'Icons', icons: 'Icons',
components: 'Components', components: 'Components',
componentIndex: 'Introduction', componentIndex: 'Introduction',
@@ -34,8 +37,9 @@ export default {
customTreeTable: 'Custom TreeTable', customTreeTable: 'Custom TreeTable',
tab: 'Tab', tab: 'Tab',
form: 'Form', form: 'Form',
createForm: 'Create Form', createArticle: 'Create Article',
editForm: 'Edit Form', editArticle: 'Edit Article',
articleList: 'Article List',
errorPages: 'Error Pages', errorPages: 'Error Pages',
page401: '401', page401: '401',
page404: '404', page404: '404',
@@ -74,6 +78,10 @@ export default {
roles: 'Your roles', roles: 'Your roles',
switchRoles: 'Switch roles' switchRoles: 'Switch roles'
}, },
guide: {
description: 'The guide page is useful for some people who entered the project for the first time. You can briefly introduce the features of the project. Demo is based on ',
button: 'Show Guide'
},
components: { components: {
documentation: 'Documentation', documentation: 'Documentation',
tinymceTips: 'Rich text editor is a core part of management system, but at the same time is a place with lots of problems. In the process of selecting rich texts, I also walked a lot of detours. The common rich text editors in the market are basically used, and the finally chose Tinymce. See documentation for more detailed rich text editor comparisons and introductions.', tinymceTips: 'Rich text editor is a core part of management system, but at the same time is a place with lots of problems. In the process of selecting rich texts, I also walked a lot of detours. The common rich text editors in the market are basically used, and the finally chose Tinymce. See documentation for more detailed rich text editor comparisons and introductions.',

View File

@@ -3,7 +3,10 @@ export default {
dashboard: '首页', dashboard: '首页',
introduction: '简述', introduction: '简述',
documentation: '文档', documentation: '文档',
guide: '引导页',
permission: '权限测试页', permission: '权限测试页',
pagePermission: '页面权限',
directivePermission: '指令权限',
icons: '图标', icons: '图标',
components: '组件', components: '组件',
componentIndex: '介绍', componentIndex: '介绍',
@@ -34,8 +37,9 @@ export default {
customTreeTable: '自定义树表', customTreeTable: '自定义树表',
tab: 'Tab', tab: 'Tab',
form: '表单', form: '表单',
createForm: '创建表单', createArticle: '创建文章',
editForm: '编辑表单', editArticle: '编辑文章',
articleList: '文章列表',
errorPages: '错误页面', errorPages: '错误页面',
page401: '401', page401: '401',
page404: '404', page404: '404',
@@ -74,6 +78,10 @@ export default {
roles: '你的权限', roles: '你的权限',
switchRoles: '切换权限' switchRoles: '切换权限'
}, },
guide: {
description: '引导页对于一些第一次进入项目的人很有用,你可以简单介绍下项目的功能。本 Demo 是基于',
button: '打开引导'
},
components: { components: {
documentation: '文档', documentation: '文档',
tinymceTips: '富文本是管理后台一个核心的功能但同时又是一个有很多坑的地方。在选择富文本的过程中我也走了不少的弯路市面上常见的富文本都基本用过了最终权衡了一下选择了Tinymce。更详细的富文本比较和介绍见', tinymceTips: '富文本是管理后台一个核心的功能但同时又是一个有很多坑的地方。在选择富文本的过程中我也走了不少的弯路市面上常见的富文本都基本用过了最终权衡了一下选择了Tinymce。更详细的富文本比较和介绍见',

View File

@@ -4,6 +4,9 @@ import { param2Obj } from '@/utils'
const List = [] const List = []
const count = 100 const count = 100
const baseContent = '<p>我是测试数据我是测试数据</p><p><img src="https://wpimg.wallstcn.com/4c69009c-0fd4-4153-b112-6cb53d1cf943"></p>'
const image_uri = 'https://wpimg.wallstcn.com/e4558086-631c-425c-9430-56ffb46e70b3'
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
List.push(Mock.mock({ List.push(Mock.mock({
id: '@increment', id: '@increment',
@@ -11,12 +14,17 @@ for (let i = 0; i < count; i++) {
author: '@first', author: '@first',
reviewer: '@first', reviewer: '@first',
title: '@title(5, 10)', title: '@title(5, 10)',
content_short: '我是测试数据',
content: baseContent,
forecast: '@float(0, 100, 2, 2)', forecast: '@float(0, 100, 2, 2)',
importance: '@integer(1, 3)', importance: '@integer(1, 3)',
'type|1': ['CN', 'US', 'JP', 'EU'], 'type|1': ['CN', 'US', 'JP', 'EU'],
'status|1': ['published', 'draft', 'deleted'], 'status|1': ['published', 'draft', 'deleted'],
display_time: '@datetime', display_time: '@datetime',
pageviews: '@integer(300, 5000)' comment_disabled: true,
pageviews: '@integer(300, 5000)',
image_uri,
platforms: ['a-platform']
})) }))
} }
@@ -45,22 +53,14 @@ export default {
getPv: () => ({ getPv: () => ({
pvData: [{ key: 'PC', pv: 1024 }, { key: 'mobile', pv: 1024 }, { key: 'ios', pv: 1024 }, { key: 'android', pv: 1024 }] pvData: [{ key: 'PC', pv: 1024 }, { key: 'mobile', pv: 1024 }, { key: 'ios', pv: 1024 }, { key: 'android', pv: 1024 }]
}), }),
getArticle: () => ({ getArticle: (config) => {
id: 120000000001, const { id } = param2Obj(config.url)
author: { key: 'mockPan' }, for (const article of List) {
source_name: '原创作者', if (article.id === +id) {
category_item: [{ key: 'global', name: '全球' }], return article
comment_disabled: true, }
content: '<p>我是测试数据我是测试数据</p><p><img class="wscnph" src="https://wpimg.wallstcn.com/4c69009c-0fd4-4153-b112-6cb53d1cf943" data-wscntype="image" data-wscnh="300" data-wscnw="400" data-mce-src="https://wpimg.wallstcn.com/4c69009c-0fd4-4153-b112-6cb53d1cf943"></p>"', }
content_short: '我是测试数据', },
display_time: +new Date(),
image_uri: 'https://wpimg.wallstcn.com/e4558086-631c-425c-9430-56ffb46e70b3',
platforms: ['a-platform'],
source_uri: 'https://github.com/PanJiaChen/vue-element-admin',
status: 'published',
tags: [],
title: 'vue-element-admin'
}),
createArticle: () => ({ createArticle: () => ({
data: 'success' data: 'success'
}), }),

View File

@@ -31,10 +31,10 @@ router.beforeEach((to, from, next) => {
router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表 router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
}) })
}).catch(() => { }).catch((err) => {
store.dispatch('FedLogOut').then(() => { store.dispatch('FedLogOut').then(() => {
Message.error('Verification failed, please login again') Message.error(err || 'Verification failed, please login again')
next({ path: '/login' }) next({ path: '/' })
}) })
}) })
} else { } else {

View File

@@ -1,13 +1,10 @@
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 too many pages will cause webpack hot update too slow. so only in production use lazy-loading;
// detail: https://panjiachen.github.io/vue-element-admin-site/#/lazy-loading
Vue.use(Router) Vue.use(Router)
/* Layout */ /* Layout */
import Layout from '../views/layout/Layout' import Layout from '@/views/layout/Layout'
/** note: submenu only apppear when children.length>=1 /** note: submenu only apppear when children.length>=1
* detail see https://panjiachen.github.io/vue-element-admin-site/#/router-and-nav?id=sidebar * detail see https://panjiachen.github.io/vue-element-admin-site/#/router-and-nav?id=sidebar
@@ -28,17 +25,17 @@ import Layout from '../views/layout/Layout'
} }
**/ **/
export const constantRouterMap = [ export const constantRouterMap = [
{ path: '/login', component: _import('login/index'), hidden: true }, { path: '/login', component: () => import('@/views/login/index'), hidden: true },
{ path: '/authredirect', component: _import('login/authredirect'), hidden: true }, { path: '/authredirect', component: () => import('@/views/login/authredirect'), hidden: true },
{ path: '/404', component: _import('errorPage/404'), hidden: true }, { path: '/404', component: () => import('@/views/errorPage/404'), hidden: true },
{ path: '/401', component: _import('errorPage/401'), hidden: true }, { path: '/401', component: () => import('@/views/errorPage/401'), hidden: true },
{ {
path: '', path: '',
component: Layout, component: Layout,
redirect: 'dashboard', redirect: 'dashboard',
children: [{ children: [{
path: 'dashboard', path: 'dashboard',
component: _import('dashboard/index'), component: () => import('@/views/dashboard/index'),
name: 'dashboard', name: 'dashboard',
meta: { title: 'dashboard', icon: 'dashboard', noCache: true } meta: { title: 'dashboard', icon: 'dashboard', noCache: true }
}] }]
@@ -49,10 +46,21 @@ export const constantRouterMap = [
redirect: '/documentation/index', redirect: '/documentation/index',
children: [{ children: [{
path: 'index', path: 'index',
component: _import('documentation/index'), component: () => import('@/views/documentation/index'),
name: 'documentation', name: 'documentation',
meta: { title: 'documentation', icon: 'documentation', noCache: true } meta: { title: 'documentation', icon: 'documentation', noCache: true }
}] }]
},
{
path: '/guide',
component: Layout,
redirect: '/guide/index',
children: [{
path: 'index',
component: () => import('@/views/guide/index'),
name: 'guide',
meta: { title: 'guide', icon: 'guide', noCache: true }
}]
} }
] ]
@@ -67,16 +75,28 @@ export const asyncRouterMap = [
path: '/permission', path: '/permission',
component: Layout, component: Layout,
redirect: '/permission/index', redirect: '/permission/index',
meta: { roles: ['admin'] }, // you can set roles in root nav alwaysShow: true, // will always show the root menu
children: [{
path: 'index',
component: _import('permission/index'),
name: 'permission',
meta: { meta: {
title: 'permission', title: 'permission',
icon: 'lock', icon: 'lock',
roles: ['admin', 'editor'] // you can set roles in root nav
},
children: [{
path: 'page',
component: () => import('@/views/permission/page'),
name: 'pagePermission',
meta: {
title: 'pagePermission',
roles: ['admin'] // or you can only set roles in sub nav roles: ['admin'] // or you can only set roles in sub nav
} }
}, {
path: 'directive',
component: () => import('@/views/permission/directive'),
name: 'directivePermission',
meta: {
title: 'directivePermission'
// if do not set roles, means: this page does not require permission
}
}] }]
}, },
@@ -85,7 +105,7 @@ export const asyncRouterMap = [
component: Layout, component: Layout,
children: [{ children: [{
path: 'index', path: 'index',
component: _import('svg-icons/index'), component: () => import('@/views/svg-icons/index'),
name: 'icons', name: 'icons',
meta: { title: 'icons', icon: 'icon', noCache: true } meta: { title: 'icons', icon: 'icon', noCache: true }
}] }]
@@ -101,19 +121,19 @@ export const asyncRouterMap = [
icon: 'component' icon: 'component'
}, },
children: [ children: [
{ path: 'tinymce', component: _import('components-demo/tinymce'), name: 'tinymce-demo', meta: { title: 'tinymce' }}, { path: 'tinymce', component: () => import('@/views/components-demo/tinymce'), name: 'tinymce-demo', meta: { title: 'tinymce' }},
{ path: 'markdown', component: _import('components-demo/markdown'), name: 'markdown-demo', meta: { title: 'markdown' }}, { path: 'markdown', component: () => import('@/views/components-demo/markdown'), name: 'markdown-demo', meta: { title: 'markdown' }},
{ path: 'json-editor', component: _import('components-demo/jsonEditor'), name: 'jsonEditor-demo', meta: { title: 'jsonEditor' }}, { path: 'json-editor', component: () => import('@/views/components-demo/jsonEditor'), name: 'jsonEditor-demo', meta: { title: 'jsonEditor' }},
{ path: 'dnd-list', component: _import('components-demo/dndList'), name: 'dndList-demo', meta: { title: 'dndList' }}, { path: 'splitpane', component: () => import('@/views/components-demo/splitpane'), name: 'splitpane-demo', meta: { title: 'splitPane' }},
{ path: 'splitpane', component: _import('components-demo/splitpane'), name: 'splitpane-demo', meta: { title: 'splitPane' }}, { path: 'avatar-upload', component: () => import('@/views/components-demo/avatarUpload'), name: 'avatarUpload-demo', meta: { title: 'avatarUpload' }},
{ path: 'avatar-upload', component: _import('components-demo/avatarUpload'), name: 'avatarUpload-demo', meta: { title: 'avatarUpload' }}, { path: 'dropzone', component: () => import('@/views/components-demo/dropzone'), name: 'dropzone-demo', meta: { title: 'dropzone' }},
{ path: 'dropzone', component: _import('components-demo/dropzone'), name: 'dropzone-demo', meta: { title: 'dropzone' }}, { path: 'sticky', component: () => import('@/views/components-demo/sticky'), name: 'sticky-demo', meta: { title: 'sticky' }},
{ path: 'sticky', component: _import('components-demo/sticky'), name: 'sticky-demo', meta: { title: 'sticky' }}, { path: 'count-to', component: () => import('@/views/components-demo/countTo'), name: 'countTo-demo', meta: { title: 'countTo' }},
{ path: 'count-to', component: _import('components-demo/countTo'), name: 'countTo-demo', meta: { title: 'countTo' }}, { path: 'mixin', component: () => import('@/views/components-demo/mixin'), name: 'componentMixin-demo', meta: { title: 'componentMixin' }},
{ path: 'mixin', component: _import('components-demo/mixin'), name: 'componentMixin-demo', meta: { title: 'componentMixin' }}, { path: 'back-to-top', component: () => import('@/views/components-demo/backToTop'), name: 'backToTop-demo', meta: { title: 'backToTop' }},
{ path: 'back-to-top', component: _import('components-demo/backToTop'), name: 'backToTop-demo', meta: { title: 'backToTop' }}, { path: 'drag-dialog', component: () => import('@/views/components-demo/dragDialog'), name: 'dragDialog-demo', meta: { title: 'dragDialog' }},
{ path: 'drag-dialog', component: _import('components-demo/dragDialog'), name: 'dragDialog-demo', meta: { title: 'dragDialog' }}, { path: 'dnd-list', component: () => import('@/views/components-demo/dndList'), name: 'dndList-demo', meta: { title: 'dndList' }},
{ path: 'drag-kanban', component: _import('components-demo/dragKanban'), name: 'dragKanban-demo', meta: { title: 'dragKanban' }} { path: 'drag-kanban', component: () => import('@/views/components-demo/dragKanban'), name: 'dragKanban-demo', meta: { title: 'dragKanban' }}
] ]
}, },
@@ -127,56 +147,55 @@ export const asyncRouterMap = [
icon: 'chart' icon: 'chart'
}, },
children: [ children: [
{ path: 'keyboard', component: _import('charts/keyboard'), name: 'keyboardChart', meta: { title: 'keyboardChart', noCache: true }}, { path: 'keyboard', component: () => import('@/views/charts/keyboard'), name: 'keyboardChart', meta: { title: 'keyboardChart', noCache: true }},
{ path: 'line', component: _import('charts/line'), name: 'lineChart', meta: { title: 'lineChart', noCache: true }}, { path: 'line', component: () => import('@/views/charts/line'), name: 'lineChart', meta: { title: 'lineChart', noCache: true }},
{ path: 'mixchart', component: _import('charts/mixChart'), name: 'mixChart', meta: { title: 'mixChart', noCache: true }} { path: 'mixchart', component: () => import('@/views/charts/mixChart'), name: 'mixChart', meta: { title: 'mixChart', noCache: true }}
]
},
{
path: '/tab',
component: Layout,
children: [{
path: 'index',
component: () => import('@/views/tab/index'),
name: 'tab',
meta: { title: 'tab', icon: 'tab' }
}]
},
{
path: '/table',
component: Layout,
redirect: '/table/complex-table',
name: 'table',
meta: {
title: 'Table',
icon: 'table'
},
children: [
{ path: 'dynamic-table', component: () => import('@/views/table/dynamicTable/index'), name: 'dynamicTable', meta: { title: 'dynamicTable' }},
{ path: 'drag-table', component: () => import('@/views/table/dragTable'), name: 'dragTable', meta: { title: 'dragTable' }},
{ path: 'inline-edit-table', component: () => import('@/views/table/inlineEditTable'), name: 'inlineEditTable', meta: { title: 'inlineEditTable' }},
{ path: 'tree-table', component: () => import('@/views/table/treeTable/treeTable'), name: 'treeTableDemo', meta: { title: 'treeTable' }},
{ path: 'custom-tree-table', component: () => import('@/views/table/treeTable/customTreeTable'), name: 'customTreeTableDemo', meta: { title: 'customTreeTable' }},
{ path: 'complex-table', component: () => import('@/views/table/complexTable'), name: 'complexTable', meta: { title: 'complexTable' }}
] ]
}, },
{ {
path: '/example', path: '/example',
component: Layout, component: Layout,
redirect: '/example/table/complex-table', redirect: '/example/list',
name: 'example', name: 'example',
meta: { meta: {
title: 'example', title: 'example',
icon: 'example' icon: 'example'
}, },
children: [ children: [
{ { path: 'create', component: () => import('@/views/example/create'), name: 'createArticle', meta: { title: 'createArticle', icon: 'edit' }},
path: '/example/table', { path: 'edit/:id(\\d+)', component: () => import('@/views/example/edit'), name: 'editArticle', meta: { title: 'editArticle', noCache: true }, hidden: true },
component: _import('example/table/index'), { path: 'list', component: () => import('@/views/example/list'), name: 'articleList', meta: { title: 'articleList', icon: 'list' }}
redirect: '/example/table/complex-table',
name: 'Table',
meta: {
title: 'Table',
icon: 'table'
},
children: [
{ path: 'dynamic-table', component: _import('example/table/dynamicTable/index'), name: 'dynamicTable', meta: { title: 'dynamicTable' }},
{ path: 'drag-table', component: _import('example/table/dragTable'), name: 'dragTable', meta: { title: 'dragTable' }},
{ path: 'inline-edit-table', component: _import('example/table/inlineEditTable'), name: 'inlineEditTable', meta: { title: 'inlineEditTable' }},
{ path: 'tree-table', component: _import('example/table/treeTable/treeTable'), name: 'treeTableDemo', meta: { title: 'treeTable' }},
{ path: 'custom-tree-table', component: _import('example/table/treeTable/customTreeTable'), name: 'customTreeTableDemo', meta: { title: 'customTreeTable' }},
{ path: 'complex-table', component: _import('example/table/complexTable'), name: 'complexTable', meta: { title: 'complexTable' }}
]
},
{ path: 'tab/index', icon: 'tab', component: _import('example/tab/index'), name: 'tab', meta: { title: 'tab' }}
]
},
{
path: '/form',
component: Layout,
redirect: 'noredirect',
name: 'form',
meta: {
title: 'form',
icon: 'form'
},
children: [
{ path: 'create-form', component: _import('form/create'), name: 'createForm', meta: { title: 'createForm', icon: 'table' }},
{ path: 'edit-form', component: _import('form/edit'), name: 'editForm', meta: { title: 'editForm', icon: 'table' }}
] ]
}, },
@@ -190,8 +209,8 @@ export const asyncRouterMap = [
icon: '404' icon: '404'
}, },
children: [ children: [
{ path: '401', component: _import('errorPage/401'), name: 'page401', meta: { title: 'page401', noCache: true }}, { path: '401', component: () => import('@/views/errorPage/401'), name: 'page401', meta: { title: 'page401', noCache: true }},
{ path: '404', component: _import('errorPage/404'), name: 'page404', meta: { title: 'page404', noCache: true }} { path: '404', component: () => import('@/views/errorPage/404'), name: 'page404', meta: { title: 'page404', noCache: true }}
] ]
}, },
@@ -199,7 +218,7 @@ export const asyncRouterMap = [
path: '/error-log', path: '/error-log',
component: Layout, component: Layout,
redirect: 'noredirect', redirect: 'noredirect',
children: [{ path: 'log', component: _import('errorLog/index'), name: 'errorLog', meta: { title: 'errorLog', icon: 'bug' }}] children: [{ path: 'log', component: () => import('@/views/errorLog/index'), name: 'errorLog', meta: { title: 'errorLog', icon: 'bug' }}]
}, },
{ {
@@ -212,9 +231,9 @@ export const asyncRouterMap = [
icon: 'excel' icon: 'excel'
}, },
children: [ children: [
{ path: 'export-excel', component: _import('excel/exportExcel'), name: 'exportExcel', meta: { title: 'exportExcel' }}, { path: 'export-excel', component: () => import('@/views/excel/exportExcel'), name: 'exportExcel', meta: { title: 'exportExcel' }},
{ path: 'export-selected-excel', component: _import('excel/selectExcel'), name: 'selectExcel', meta: { title: 'selectExcel' }}, { path: 'export-selected-excel', component: () => import('@/views/excel/selectExcel'), name: 'selectExcel', meta: { title: 'selectExcel' }},
{ path: 'upload-excel', component: _import('excel/uploadExcel'), name: 'uploadExcel', meta: { title: 'uploadExcel' }} { path: 'upload-excel', component: () => import('@/views/excel/uploadExcel'), name: 'uploadExcel', meta: { title: 'uploadExcel' }}
] ]
}, },
@@ -224,27 +243,27 @@ export const asyncRouterMap = [
redirect: '/zip/download', redirect: '/zip/download',
alwaysShow: true, alwaysShow: true,
meta: { title: 'zip', icon: 'zip' }, meta: { title: 'zip', icon: 'zip' },
children: [{ path: 'download', component: _import('zip/index'), name: 'exportZip', meta: { title: 'exportZip' }}] children: [{ path: 'download', component: () => import('@/views/zip/index'), name: 'exportZip', meta: { title: 'exportZip' }}]
}, },
{ {
path: '/theme', path: '/theme',
component: Layout, component: Layout,
redirect: 'noredirect', redirect: 'noredirect',
children: [{ path: 'index', component: _import('theme/index'), name: 'theme', meta: { title: 'theme', icon: 'theme' }}] children: [{ path: 'index', component: () => import('@/views/theme/index'), name: 'theme', meta: { title: 'theme', icon: 'theme' }}]
}, },
{ {
path: '/clipboard', path: '/clipboard',
component: Layout, component: Layout,
redirect: 'noredirect', redirect: 'noredirect',
children: [{ path: 'index', component: _import('clipboard/index'), name: 'clipboardDemo', meta: { title: 'clipboardDemo', icon: 'clipboard' }}] children: [{ path: 'index', component: () => import('@/views/clipboard/index'), name: 'clipboardDemo', meta: { title: 'clipboardDemo', icon: 'clipboard' }}]
}, },
{ {
path: '/i18n', path: '/i18n',
component: Layout, component: Layout,
children: [{ path: 'index', component: _import('i18n-demo/index'), name: 'i18n', meta: { title: 'i18n', icon: 'international' }}] children: [{ path: 'index', component: () => import('@/views/i18n-demo/index'), name: 'i18n', meta: { title: 'i18n', icon: 'international' }}]
}, },
{ path: '*', redirect: '/404', hidden: true } { path: '*', redirect: '/404', hidden: true }

View File

@@ -67,7 +67,13 @@ const user = {
reject('error') reject('error')
} }
const data = response.data const data = response.data
if (data.roles && data.roles.length > 0) { // 验证返回的roles是否是一个非空数组
commit('SET_ROLES', data.roles) commit('SET_ROLES', data.roles)
} else {
reject('getInfo: roles must be a non-null array !')
}
commit('SET_NAME', data.name) commit('SET_NAME', data.name)
commit('SET_AVATAR', data.avatar) commit('SET_AVATAR', data.avatar)
commit('SET_INTRODUCTION', data.introduction) commit('SET_INTRODUCTION', data.introduction)

View File

@@ -1,18 +1,13 @@
#app { #app {
// 主体区域 // 主体区域
.main-container { .main-container {
min-height: 100%; min-height: 100%;
transition: margin-left .28s; transition: margin-left .28s;
margin-left: 180px; margin-left: 180px;
} }
// 侧边栏 // 侧边栏
.sidebar-container { .sidebar-container {
.horizontal-collapse-transition { transition: width 0.28s;
transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;
}
transition: width .28s;
width: 180px !important; width: 180px !important;
height: 100%; height: 100%;
position: fixed; position: fixed;
@@ -22,19 +17,33 @@
left: 0; left: 0;
z-index: 1001; z-index: 1001;
overflow: hidden; overflow: hidden;
//reset element-ui css
.horizontal-collapse-transition {
transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;
}
.scrollbar-wrapper {
overflow-x: hidden!important;
.el-scrollbar__view {
height: 100%;
}
}
.is-horizontal {
display: none;
}
a { a {
display: inline-block; display: inline-block;
width: 100%; width: 100%;
overflow: hidden;
} }
.svg-icon { .svg-icon {
margin-right: 16px; margin-right: 16px;
} }
.el-menu { .el-menu {
border: none; border: none;
height: 100%;
width: 100% !important; width: 100% !important;
} }
} }
.hideSidebar { .hideSidebar {
.sidebar-container { .sidebar-container {
width: 36px !important; width: 36px !important;
@@ -50,8 +59,17 @@
} }
} }
.el-submenu { .el-submenu {
overflow: hidden;
&>.el-submenu__title { &>.el-submenu__title {
padding-left: 10px !important; padding-left: 10px !important;
.el-submenu__icon-arrow {
display: none;
}
}
}
.el-menu--collapse {
.el-submenu {
&>.el-submenu__title {
&>span { &>span {
height: 0; height: 0;
width: 0; width: 0;
@@ -59,13 +77,10 @@
visibility: hidden; visibility: hidden;
display: inline-block; display: inline-block;
} }
.el-submenu__icon-arrow {
display: none;
} }
} }
} }
} }
.sidebar-container .nest-menu .el-submenu>.el-submenu__title, .sidebar-container .nest-menu .el-submenu>.el-submenu__title,
.sidebar-container .el-submenu .el-menu-item { .sidebar-container .el-submenu .el-menu-item {
min-width: 180px !important; min-width: 180px !important;
@@ -84,7 +99,6 @@
margin-left: 0px; margin-left: 0px;
} }
.sidebar-container { .sidebar-container {
top: 50px;
transition: transform .28s; transition: transform .28s;
width: 180px !important; width: 180px !important;
} }
@@ -95,7 +109,6 @@
} }
} }
} }
.withoutAnimation { .withoutAnimation {
.main-container, .main-container,
.sidebar-container { .sidebar-container {

View File

@@ -11,7 +11,21 @@
opacity: 0; opacity: 0;
} }
/*fade*/ /*fade-transform*/
.fade-transform-leave-active,
.fade-transform-enter-active {
transition: all .5s;
}
.fade-transform-enter {
opacity: 0;
transform: translateX(-30px);
}
.fade-transform-leave-to {
opacity: 0;
transform: translateX(30px);
}
/*breadcrumb transition*/
.breadcrumb-enter-active, .breadcrumb-enter-active,
.breadcrumb-leave-active { .breadcrumb-leave-active {
transition: all .5s; transition: all .5s;

View File

@@ -261,3 +261,7 @@ export function deepClone(source) {
}) })
return targetObj return targetObj
} }
export function uniqueArr(arr) {
return Array.from(new Set(arr))
}

View File

@@ -1,7 +1,11 @@
<template> <template>
<div class="components-container"> <div class="components-container">
<el-button type="primary" @click="dialogTableVisible = true">open a Drag Dialog</el-button> <el-button type="primary" @click="dialogTableVisible = true">open a Drag Dialog</el-button>
<el-dialog v-el-drag-dialog title="Shipping address" :visible.sync="dialogTableVisible"> <el-dialog v-el-drag-dialog @dragDialog="handleDrag" title="Shipping address" :visible.sync="dialogTableVisible">
<el-select ref="select" v-model="value" placeholder="请选择">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value">
</el-option>
</el-select>
<el-table :data="gridData"> <el-table :data="gridData">
<el-table-column property="date" label="Date" width="150"></el-table-column> <el-table-column property="date" label="Date" width="150"></el-table-column>
<el-table-column property="name" label="Name" width="200"></el-table-column> <el-table-column property="name" label="Name" width="200"></el-table-column>
@@ -20,6 +24,13 @@ export default {
data() { data() {
return { return {
dialogTableVisible: false, dialogTableVisible: false,
options: [
{ value: '选项1', label: '黄金糕' },
{ value: '选项2', label: '双皮奶' },
{ value: '选项3', label: '蚵仔煎' },
{ value: '选项4', label: '龙须面' }
],
value: '',
gridData: [{ gridData: [{
date: '2016-05-02', date: '2016-05-02',
name: 'John Smith', name: 'John Smith',
@@ -38,6 +49,12 @@ export default {
address: 'No.1518, Jinshajiang Road, Putuo District' address: 'No.1518, Jinshajiang Road, Putuo District'
}] }]
} }
},
methods: {
// v-el-drag-dialog onDrag callback function
handleDrag() {
this.$refs.select.blur()
}
} }
} }
</script> </script>

View File

@@ -1,6 +1,6 @@
<template> <template>
<el-table :data="list" style="width: 100%;padding-top: 15px;"> <el-table :data="list" style="width: 100%;padding-top: 15px;">
<el-table-column label="Order_No" show-overflow-tooltip> <el-table-column label="Order_No" min-width="200">
<template slot-scope="scope"> <template slot-scope="scope">
{{scope.row.order_no}} {{scope.row.order_no}}
</template> </template>
@@ -42,7 +42,7 @@ export default {
methods: { methods: {
fetchData() { fetchData() {
fetchList().then(response => { fetchList().then(response => {
this.list = response.data.items.slice(0, 7) this.list = response.data.items.slice(0, 8)
}) })
} }
} }

View File

@@ -30,10 +30,10 @@
<el-col :xs="{span: 24}" :sm="{span: 24}" :md="{span: 24}" :lg="{span: 12}" :xl="{span: 12}" style="padding-right:8px;margin-bottom:30px;"> <el-col :xs="{span: 24}" :sm="{span: 24}" :md="{span: 24}" :lg="{span: 12}" :xl="{span: 12}" style="padding-right:8px;margin-bottom:30px;">
<transaction-table></transaction-table> <transaction-table></transaction-table>
</el-col> </el-col>
<el-col :xs="{span: 12}" :sm="{span: 12}" :md="{span: 12}" :lg="{span: 6}" :xl="{span: 5}"> <el-col :xs="{span: 24}" :sm="{span: 12}" :md="{span: 12}" :lg="{span: 6}" :xl="{span: 5}" style="margin-bottom:30px;">
<todo-list></todo-list> <todo-list></todo-list>
</el-col> </el-col>
<el-col :xs="{span: 12}" :sm="{span: 12}" :md="{span: 12}" :lg="{span: 6}" :xl="{span: 5}"> <el-col :xs="{span: 24}" :sm="{span: 12}" :md="{span: 12}" :lg="{span: 6}" :xl="{span: 5}" style="margin-bottom:30px;" >
<box-card></box-card> <box-card></box-card>
</el-col> </el-col>
</el-row> </el-row>

View File

@@ -14,7 +14,7 @@
</div> </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>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -3,100 +3,51 @@
<el-form class="form-container" :model="postForm" :rules="rules" ref="postForm"> <el-form class="form-container" :model="postForm" :rules="rules" ref="postForm">
<sticky :className="'sub-navbar '+postForm.status"> <sticky :className="'sub-navbar '+postForm.status">
<template v-if="fetchSuccess"> <CommentDropdown v-model="postForm.comment_disabled" />
<PlatformDropdown v-model="postForm.platforms" />
<router-link style="margin-right:15px;" v-show='isEdit' :to="{ path:'create-form'}"> <SourceUrlDropdown v-model="postForm.source_uri" />
<el-button type="info">创建form</el-button> <el-button v-loading="loading" style="margin-left: 10px;" type="success" @click="submitForm">发布
</router-link>
<el-dropdown trigger="click">
<el-button plain>{{!postForm.comment_disabled?'评论已打开':'评论已关闭'}}
<i class="el-icon-caret-bottom el-icon--right"></i>
</el-button>
<el-dropdown-menu class="no-padding" slot="dropdown">
<el-dropdown-item>
<el-radio-group style="padding: 10px;" v-model="postForm.comment_disabled">
<el-radio :label="true">关闭评论</el-radio>
<el-radio :label="false">打开评论</el-radio>
</el-radio-group>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-dropdown trigger="click">
<el-button plain>平台
<i class="el-icon-caret-bottom el-icon--right"></i>
</el-button>
<el-dropdown-menu class="no-border" slot="dropdown">
<el-checkbox-group v-model="postForm.platforms" style="padding: 5px 15px;">
<el-checkbox v-for="item in platformsOptions" :label="item.key" :key="item.key">
{{item.name}}
</el-checkbox>
</el-checkbox-group>
</el-dropdown-menu>
</el-dropdown>
<el-dropdown trigger="click">
<el-button plain>
外链
<i class="el-icon-caret-bottom el-icon--right"></i>
</el-button>
<el-dropdown-menu class="no-padding no-border" style="width:300px" slot="dropdown">
<el-form-item label-width="0px" style="margin-bottom: 0px" prop="source_uri">
<el-input placeholder="请输入内容" v-model="postForm.source_uri">
<template slot="prepend">填写url</template>
</el-input>
</el-form-item>
</el-dropdown-menu>
</el-dropdown>
<el-button v-loading="loading" style="margin-left: 10px;" type="success" @click="submitForm()">发布
</el-button> </el-button>
<el-button v-loading="loading" type="warning" @click="draftForm">草稿</el-button> <el-button v-loading="loading" type="warning" @click="draftForm">草稿</el-button>
</template>
<template v-else>
<el-tag>发送异常错误,刷新页面,或者联系程序员</el-tag>
</template>
</sticky> </sticky>
<div class="createPost-main-container"> <div class="createPost-main-container">
<el-row> <el-row>
<Warning />
<el-col :span="21"> <el-col :span="21">
<el-form-item style="margin-bottom: 40px;" prop="title"> <el-form-item style="margin-bottom: 40px;" prop="title">
<MDinput name="name" v-model="postForm.title" required :maxlength="100"> <MDinput name="name" v-model="postForm.title" required :maxlength="100">
标题 标题
</MDinput> </MDinput>
<span v-show="postForm.title.length>=26" class='title-prompt'>app可能会显示不全</span>
</el-form-item> </el-form-item>
<div class="postInfo-container"> <div class="postInfo-container">
<el-row> <el-row>
<el-col :span="8"> <el-col :span="8">
<el-form-item label-width="45px" label="作者:" class="postInfo-container-item"> <el-form-item label-width="45px" label="作者:" class="postInfo-container-item">
<multiselect v-model="postForm.author" :options="userLIstOptions" @search-change="getRemoteUserList" placeholder="搜索用户" selectLabel="选择" <el-select v-model="postForm.author" filterable remote placeholder="搜索用户" :remote-method="getRemoteUserList">
deselectLabel="删除" track-by="key" :internalSearch="false" label="key"> <el-option v-for="(item,index) in userListOptions" :key="item+index" :label="item" :value="item">
<span slot='noResult'>无结果</span> </el-option>
</multiselect> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8">
<el-tooltip class="item" effect="dark" content="将替换作者" placement="top">
<el-form-item label-width="50px" label="来源:" class="postInfo-container-item">
<el-input placeholder="将替换作者" style='min-width:150px;' v-model="postForm.source_name">
</el-input>
</el-form-item>
</el-tooltip>
</el-col>
<el-col :span="8"> <el-col :span="8">
<el-form-item label-width="80px" label="发布时间:" class="postInfo-container-item"> <el-form-item label-width="80px" label="发布时间:" class="postInfo-container-item">
<el-date-picker v-model="postForm.display_time" type="datetime" format="yyyy-MM-dd HH:mm:ss" placeholder="选择日期时间"> <el-date-picker v-model="postForm.display_time" type="datetime" format="yyyy-MM-dd HH:mm:ss" placeholder="选择日期时间">
</el-date-picker> </el-date-picker>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8">
<el-form-item label-width="60px" label="重要性:" class="postInfo-container-item">
<el-rate style="margin-top:8px;" v-model="postForm.importance" :max='3' :colors="['#99A9BF', '#F7BA2A', '#FF9900']" :low-threshold="1"
:high-threshold="3">
</el-rate>
</el-form-item>
</el-col>
</el-row> </el-row>
</div> </div>
</el-col> </el-col>
@@ -109,11 +60,11 @@
</el-form-item> </el-form-item>
<div class="editor-container"> <div class="editor-container">
<tinymce :height=400 ref="editor" v-model="postForm.content"></tinymce> <Tinymce :height=400 ref="editor" v-model="postForm.content" />
</div> </div>
<div style="margin-bottom: 20px;"> <div style="margin-bottom: 20px;">
<Upload v-model="postForm.image_uri"></Upload> <Upload v-model="postForm.image_uri" />
</div> </div>
</div> </div>
</el-form> </el-form>
@@ -131,6 +82,8 @@ import Sticky from '@/components/Sticky' // 粘性header组件
import { validateURL } from '@/utils/validate' import { validateURL } from '@/utils/validate'
import { fetchArticle } from '@/api/article' import { fetchArticle } from '@/api/article'
import { userSearch } from '@/api/remoteSearch' import { userSearch } from '@/api/remoteSearch'
import Warning from './Warning'
import { CommentDropdown, PlatformDropdown, SourceUrlDropdown } from './Dropdown'
const defaultForm = { const defaultForm = {
status: 'draft', status: 'draft',
@@ -139,16 +92,16 @@ const defaultForm = {
content_short: '', // content_short: '', //
source_uri: '', // source_uri: '', //
image_uri: '', // image_uri: '', //
source_name: '', //
display_time: undefined, // display_time: undefined, //
id: undefined, id: undefined,
platforms: ['a-platform'], platforms: ['a-platform'],
comment_disabled: false comment_disabled: false,
importance: 0
} }
export default { export default {
name: 'articleDetail', name: 'articleDetail',
components: { Tinymce, MDinput, Upload, Multiselect, Sticky }, components: { Tinymce, MDinput, Upload, Multiselect, Sticky, Warning, CommentDropdown, PlatformDropdown, SourceUrlDropdown },
props: { props: {
isEdit: { isEdit: {
type: Boolean, type: Boolean,
@@ -184,14 +137,8 @@ export default {
} }
return { return {
postForm: Object.assign({}, defaultForm), postForm: Object.assign({}, defaultForm),
fetchSuccess: true,
loading: false, loading: false,
userLIstOptions: [], userListOptions: [],
platformsOptions: [
{ key: 'a-platform', name: 'a-platform' },
{ key: 'b-platform', name: 'b-platform' },
{ key: 'c-platform', name: 'c-platform' }
],
rules: { rules: {
image_uri: [{ validator: validateRequire }], image_uri: [{ validator: validateRequire }],
title: [{ validator: validateRequire }], title: [{ validator: validateRequire }],
@@ -207,17 +154,20 @@ export default {
}, },
created() { created() {
if (this.isEdit) { if (this.isEdit) {
this.fetchData() const id = this.$route.params && this.$route.params.id
this.fetchData(id)
} else { } else {
this.postForm = Object.assign({}, defaultForm) this.postForm = Object.assign({}, defaultForm)
} }
}, },
methods: { methods: {
fetchData() { fetchData(id) {
fetchArticle().then(response => { fetchArticle(id).then(response => {
this.postForm = response.data this.postForm = response.data
// Just for test
this.postForm.title += ` Article Id:${this.postForm.id}`
this.postForm.content_short += ` Article Id:${this.postForm.id}`
}).catch(err => { }).catch(err => {
this.fetchSuccess = false
console.log(err) console.log(err)
}) })
}, },
@@ -260,10 +210,7 @@ export default {
getRemoteUserList(query) { getRemoteUserList(query) {
userSearch(query).then(response => { userSearch(query).then(response => {
if (!response.data.items) return if (!response.data.items) return
console.log(response) this.userListOptions = response.data.items.map(v => v.name)
this.userLIstOptions = response.data.items.map(v => ({
key: v.name
}))
}) })
} }
} }
@@ -271,15 +218,8 @@ export default {
</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";
.title-prompt{ .createPost-container {
position: absolute;
right: 0px;
font-size: 12px;
top:10px;
color:#ff4949;
}
.createPost-container {
position: relative; position: relative;
.createPost-main-container { .createPost-main-container {
padding: 40px 45px 20px 50px; padding: 40px 45px 20px 50px;
@@ -309,6 +249,5 @@ export default {
right: -10px; right: -10px;
top: 0px; top: 0px;
} }
} }
</style> </style>

View File

@@ -0,0 +1,31 @@
<template>
<el-dropdown trigger="click" :show-timeout="100">
<el-button plain>{{!comment_disabled?'评论已打开':'评论已关闭'}}
<i class="el-icon-caret-bottom el-icon--right"></i>
</el-button>
<el-dropdown-menu class="no-padding" slot="dropdown">
<el-dropdown-item>
<el-radio-group style="padding: 10px;" v-model="comment_disabled">
<el-radio :label="true">关闭评论</el-radio>
<el-radio :label="false">打开评论</el-radio>
</el-radio-group>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
<script>
export default {
props: ['value'],
computed: {
comment_disabled: {
get() {
return this.value
},
set(val) {
this.$emit('input', val)
}
}
}
}
</script>

View File

@@ -0,0 +1,40 @@
<template>
<el-dropdown :hide-on-click="false" :show-timeout="100" trigger="click">
<el-button plain>
平台({{platforms.length}})
<i class="el-icon-caret-bottom el-icon--right"></i>
</el-button>
<el-dropdown-menu class="no-border" slot="dropdown">
<el-checkbox-group v-model="platforms" style="padding: 5px 15px;">
<el-checkbox v-for="item in platformsOptions" :label="item.key" :key="item.key">
{{item.name}}
</el-checkbox>
</el-checkbox-group>
</el-dropdown-menu>
</el-dropdown>
</template>
<script>
export default {
props: ['value'],
data() {
return {
platformsOptions: [
{ key: 'a-platform', name: 'a-platform' },
{ key: 'b-platform', name: 'b-platform' },
{ key: 'c-platform', name: 'c-platform' }
]
}
},
computed: {
platforms: {
get() {
return this.value
},
set(val) {
this.$emit('input', val)
}
}
}
}
</script>

View File

@@ -0,0 +1,31 @@
<template>
<el-dropdown :show-timeout="100" trigger="click">
<el-button plain>
外链
<i class="el-icon-caret-bottom el-icon--right"></i>
</el-button>
<el-dropdown-menu class="no-padding no-border" style="width:400px" slot="dropdown">
<el-form-item label-width="0px" style="margin-bottom: 0px" prop="source_uri">
<el-input placeholder="请输入内容" v-model="source_uri">
<template slot="prepend">填写url</template>
</el-input>
</el-form-item>
</el-dropdown-menu>
</el-dropdown>
</template>
<script>
export default {
props: ['value'],
computed: {
source_uri: {
get() {
return this.value
},
set(val) {
this.$emit('input', val)
}
}
}
}
</script>

View File

@@ -0,0 +1,3 @@
export { default as CommentDropdown } from './Comment'
export { default as PlatformDropdown } from './Platform'
export { default as SourceUrlDropdown } from './SourceUrl'

View File

@@ -0,0 +1,9 @@
<template>
<p class="warn-content">
创建和编辑页面是不能被keep-alive 缓存的因为keep-alive 的include 目前不支持根据路由来缓存所以目前都是基于component name 来缓存的如果你想要实现缓存的效果可以使用localstorage 等游览器缓存方案或者不要使用keep-alive
的include直接缓存所有页面详情见
<a href="https://panjiachen.github.io/vue-element-admin-site/#/zh-cn/tags-view?id=%E7%BC%93%E5%AD%98%E4%B8%8D%E9%80%82%E5%90%88%E5%9C%BA%E6%99%AF"
target="_blank">文档</a>
</p>
</template>

121
src/views/example/list.vue Normal file
View File

@@ -0,0 +1,121 @@
<template>
<div class="app-container">
<el-table :data="list" v-loading.body="listLoading" border fit highlight-current-row style="width: 100%">
<el-table-column align="center" label="ID" width="80">
<template slot-scope="scope">
<span>{{scope.row.id}}</span>
</template>
</el-table-column>
<el-table-column width="180px" align="center" label="Date">
<template slot-scope="scope">
<span>{{scope.row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}')}}</span>
</template>
</el-table-column>
<el-table-column width="120px" align="center" label="Author">
<template slot-scope="scope">
<span>{{scope.row.author}}</span>
</template>
</el-table-column>
<el-table-column width="100px" label="Importance">
<template slot-scope="scope">
<svg-icon v-for="n in +scope.row.importance" icon-class="star" class="meta-item__icon" :key="n"></svg-icon>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="Status" width="110">
<template slot-scope="scope">
<el-tag :type="scope.row.status | statusFilter">{{scope.row.status}}</el-tag>
</template>
</el-table-column>
<el-table-column min-width="300px" label="Title">
<template slot-scope="scope">
<router-link class="link-type" :to="'/example/edit/'+scope.row.id">
<span>{{ scope.row.title }}</span>
</router-link>
</template>
</el-table-column>
<el-table-column align="center" label="Actions" width="120">
<template slot-scope="scope">
<router-link :to="'/example/edit/'+scope.row.id">
<el-button type="primary" size="small" icon="el-icon-edit">Edit</el-button>
</router-link>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="listQuery.page"
:page-sizes="[10,20,30, 50]" :page-size="listQuery.limit" layout="total, sizes, prev, pager, next, jumper" :total="total">
</el-pagination>
</div>
</div>
</template>
<script>
import { fetchList } from '@/api/article'
export default {
name: 'articleList',
data() {
return {
list: null,
total: 0,
listLoading: true,
listQuery: {
page: 1,
limit: 10
}
}
},
filters: {
statusFilter(status) {
const statusMap = {
published: 'success',
draft: 'info',
deleted: 'danger'
}
return statusMap[status]
}
},
created() {
this.getList()
},
methods: {
getList() {
this.listLoading = true
fetchList(this.listQuery).then(response => {
this.list = response.data.items
this.total = response.data.total
this.listLoading = false
})
},
handleSizeChange(val) {
this.listQuery.limit = val
this.getList()
},
handleCurrentChange(val) {
this.listQuery.page = val
this.getList()
}
}
}
</script>
<style scoped>
.edit-input {
padding-right: 100px;
}
.cancel-btn {
position: absolute;
right: 15px;
top: 10px;
}
</style>

View File

@@ -1,18 +0,0 @@
<template>
<transition name="fade" mode="out-in">
<keep-alive :include='cachedViews'>
<router-view></router-view>
</keep-alive>
</transition>
</template>
<script>
export default {
name: 'TableMain',
computed: {
cachedViews() {
return this.$store.state.tagsView.cachedViews
}
}
}
</script>

View File

@@ -0,0 +1,52 @@
const steps = [
{
element: '.hamburger-container',
popover: {
title: 'Hamburger',
description: 'Open && Close sidebar',
position: 'bottom'
}
},
{
element: '.breadcrumb-container',
popover: {
title: 'Breadcrumb',
description: 'Indicate the current page location',
position: 'bottom'
}
},
{
element: '.screenfull',
popover: {
title: 'Screenfull',
description: 'Bring the page into fullscreen',
position: 'left'
}
},
{
element: '.international-icon',
popover: {
title: 'Switch language',
description: 'Switch the system language',
position: 'left'
}
},
{
element: '.theme-switch',
popover: {
title: 'Theme Switch',
description: 'Custom switch system theme',
position: 'left'
}
},
{
element: '.tags-view-container',
popover: {
title: 'Tags view',
description: 'The history of the page you visited',
position: 'bottom'
}
}
]
export default steps

34
src/views/guide/index.vue Normal file
View File

@@ -0,0 +1,34 @@
<template>
<div class="app-container">
<p class="warn-content">
{{$t('guide.description')}}
<a href="https://github.com/kamranahmedse/driver.js" target="_blank">driver.js.
</a>
</p>
<el-button icon='el-icon-question' type="primary" @click.prevent.stop="guide">{{$t('guide.button')}}</el-button>
</div>
</template>
<script>
import * as Driver from 'driver.js' // import driver.js
import 'driver.js/dist/driver.min.css' // import driver.js css
import steps from './defineSteps'
export default {
name: 'guide',
data() {
return {
driver: null
}
},
mounted() {
this.driver = new Driver()
},
methods: {
guide() {
this.driver.defineSteps(steps)
this.driver.start()
}
}
}
</script>

View File

@@ -1,5 +1,6 @@
<template> <template>
<div class="app-wrapper" :class="classObj"> <div class="app-wrapper" :class="classObj">
<div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside"></div>
<sidebar class="sidebar-container"></sidebar> <sidebar class="sidebar-container"></sidebar>
<div class="main-container"> <div class="main-container">
<navbar></navbar> <navbar></navbar>
@@ -36,6 +37,11 @@ export default {
mobile: this.device === 'mobile' mobile: this.device === 'mobile'
} }
} }
},
methods: {
handleClickOutside() {
this.$store.dispatch('closeSideBar', { withoutAnimation: false })
}
} }
} }
</script> </script>
@@ -48,4 +54,13 @@ export default {
height: 100%; height: 100%;
width: 100%; width: 100%;
} }
.drawer-bg {
background: #000;
opacity: 0.3;
width: 100%;
top: 0;
height: 100%;
position: absolute;
z-index: 999;
}
</style> </style>

View File

@@ -1,8 +1,8 @@
<template> <template>
<section class="app-main" style="min-height: 100%"> <section class="app-main" style="min-height: 100%">
<transition name="fade" mode="out-in"> <transition name="fade-transform" mode="out-in">
<keep-alive :include="cachedViews"> <keep-alive :include="cachedViews">
<router-view></router-view> <router-view :key="key"></router-view>
</keep-alive> </keep-alive>
</transition> </transition>
</section> </section>
@@ -14,10 +14,10 @@ export default {
computed: { computed: {
cachedViews() { cachedViews() {
return this.$store.state.tagsView.cachedViews return this.$store.state.tagsView.cachedViews
},
key() {
return this.$route.fullPath
} }
// key() {
// return this.$route.name !== undefined ? this.$route.name + +new Date() : this.$route + +new Date()
// }
} }
} }
</script> </script>

View File

@@ -1,5 +1,5 @@
<template> <template>
<scroll-bar> <el-scrollbar wrapClass="scrollbar-wrapper">
<el-menu <el-menu
mode="vertical" mode="vertical"
:show-timeout="200" :show-timeout="200"
@@ -11,16 +11,15 @@
> >
<sidebar-item :routes="permission_routers"></sidebar-item> <sidebar-item :routes="permission_routers"></sidebar-item>
</el-menu> </el-menu>
</scroll-bar> </el-scrollbar>
</template> </template>
<script> <script>
import { mapGetters } from 'vuex' import { mapGetters } from 'vuex'
import SidebarItem from './SidebarItem' import SidebarItem from './SidebarItem'
import ScrollBar from '@/components/ScrollBar'
export default { export default {
components: { SidebarItem, ScrollBar }, components: { SidebarItem },
computed: { computed: {
...mapGetters([ ...mapGetters([
'permission_routers', 'permission_routers',

View File

@@ -59,7 +59,7 @@ export default {
return false return false
}, },
isActive(route) { isActive(route) {
return route.path === this.$route.path || route.name === this.$route.name return route.path === this.$route.path
}, },
addViewTags() { addViewTags() {
const route = this.generateRoute() const route = this.generateRoute()

View File

@@ -0,0 +1,30 @@
<template>
<div>
<div style="margin-bottom:15px;">{{$t('permission.roles')}} {{roles}}</div>
{{$t('permission.switchRoles')}}
<el-radio-group v-model="switchRoles">
<el-radio-button label="editor"></el-radio-button>
<el-radio-button label="admin"></el-radio-button>
</el-radio-group>
</div>
</template>
<script>
export default {
computed: {
roles() {
return this.$store.getters.roles
},
switchRoles: {
get() {
return this.roles[0]
},
set(val) {
this.$store.dispatch('ChangeRoles', val).then(() => {
this.$emit('change')
})
}
}
}
}
</script>

View File

@@ -0,0 +1,59 @@
<template>
<div class="app-container">
<switch-roles @change="handleRolesChange" />
<div :key="key" style="margin-top:30px;">
<span v-permission="['admin']" class="permission-alert">
Only
<el-tag class="permission-tag" size="small">admin</el-tag> can see this
</span>
<span v-permission="['editor']" class="permission-alert">
Only
<el-tag class="permission-tag" size="small">editor</el-tag> can see this
</span>
<span v-permission="['admin','editor']" class="permission-alert">
Both
<el-tag class="permission-tag" size="small">admin</el-tag> and
<el-tag class="permission-tag" size="small">editor</el-tag> can see this
</span>
</div>
</div>
</template>
<script>
import permission from '@/directive/permission/index.js' // 权限判断指令
import SwitchRoles from './components/SwitchRoles'
export default{
name: 'directivePermission',
components: { SwitchRoles },
directives: { permission },
data() {
return {
key: 1 // 为了能每次切换权限的时候重新初始化指令
}
},
methods: {
handleRolesChange() {
this.key++
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.app-container {
/deep/ .permission-alert {
width: 320px;
margin-top: 30px;
background-color: #f0f9eb;
color: #67c23a;
padding: 8px 16px;
border-radius: 4px;
display: block;
}
/deep/ .permission-tag{
background-color: #ecf5ff;
}
}
</style>

View File

@@ -1,34 +0,0 @@
<template>
<div class="app-container">
<div style="margin-bottom:15px;">{{$t('permission.roles')}} {{roles}}</div>
{{$t('permission.switchRoles')}}
<el-radio-group v-model="switchRoles">
<el-radio-button label="editor"></el-radio-button>
</el-radio-group>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default{
name: 'permission',
data() {
return {
switchRoles: ''
}
},
computed: {
...mapGetters([
'roles'
])
},
watch: {
switchRoles(val) {
this.$store.dispatch('ChangeRoles', val).then(() => {
this.$router.push({ path: '/permission/index?' + +new Date() })
})
}
}
}
</script>

View File

@@ -0,0 +1,19 @@
<template>
<div class="app-container">
<switch-roles @change="handleRolesChange" />
</div>
</template>
<script>
import SwitchRoles from './components/SwitchRoles'
export default{
name: 'pagePermission',
components: { SwitchRoles },
methods: {
handleRolesChange() {
this.$router.push({ path: '/permission/index?' + +new Date() })
}
}
}
</script>

View File

@@ -1,6 +1,8 @@
<template> <template>
<div class="tab-container"> <div class="tab-container">
<el-tag>mounted times {{createdTimes}}</el-tag> <el-tag>mounted times {{createdTimes}}</el-tag>
<el-alert style="width:200px;display:inline-block;vertical-align: middle;margin-left:30px;" title="Tab with keep-alive" type="success" :closable="false">
</el-alert>
<el-tabs style='margin-top:15px;' v-model="activeName" type="border-card"> <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"> <el-tab-pane v-for="item in tabMapOptions" :label="item.label" :key='item.key' :name="item.key">
<keep-alive> <keep-alive>

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="app-container calendar-list-container"> <div class="app-container">
<div class="filter-container"> <div class="filter-container">
<el-input @keyup.enter.native="handleFilter" style="width: 200px;" class="filter-item" :placeholder="$t('table.title')" v-model="listQuery.title"> <el-input @keyup.enter.native="handleFilter" style="width: 200px;" class="filter-item" :placeholder="$t('table.title')" v-model="listQuery.title">
</el-input> </el-input>

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="app-container calendar-list-container"> <div class="app-container">
<!-- Note that row-key is necessary to get a correct row order. --> <!-- Note that row-key is necessary to get a correct row order. -->
<el-table :data="list" row-key="id" v-loading.body="listLoading" border fit highlight-current-row style="width: 100%"> <el-table :data="list" row-key="id" v-loading.body="listLoading" border fit highlight-current-row style="width: 100%">

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="app-container calendar-list-container"> <div class="app-container">
<el-table :data="list" v-loading.body="listLoading" border fit highlight-current-row style="width: 100%"> <el-table :data="list" v-loading.body="listLoading" border fit highlight-current-row style="width: 100%">