Merge remote-tracking branch 'PanJiaChen/master'

This commit is contained in:
James ZHANG 2018-05-07 10:28:03 +08:00
commit aae2c2edff
18 changed files with 382 additions and 105 deletions

View File

@ -1,10 +1,9 @@
{ {
"name": "vue-element-admin", "name": "vue-element-admin",
"version": "3.6.5", "version": "3.6.6",
"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",
"private": true,
"scripts": { "scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"build:prod": "cross-env NODE_ENV=production env_config=prod node build/build.js", "build:prod": "cross-env NODE_ENV=production env_config=prod node build/build.js",
@ -12,6 +11,20 @@
"lint": "eslint --ext .js,.vue src", "lint": "eslint --ext .js,.vue src",
"test": "npm run lint" "test": "npm run lint"
}, },
"keywords": [
"vue",
"element-ui",
"admin",
"management-system",
"admin-template"
],
"repository": {
"type": "git",
"url": "git+https://github.com/PanJiaChen/vue-element-admin.git"
},
"bugs": {
"url": "https://github.com/PanJiaChen/vue-element-admin/issues"
},
"dependencies": { "dependencies": {
"axios": "0.17.1", "axios": "0.17.1",
"clipboard": "1.7.1", "clipboard": "1.7.1",
@ -37,7 +50,7 @@
"vue-multiselect": "2.0.8", "vue-multiselect": "2.0.8",
"vue-router": "3.0.1", "vue-router": "3.0.1",
"vue-splitpane": "1.0.2", "vue-splitpane": "1.0.2",
"vuedraggable": "2.15.0", "vuedraggable": "^2.16.0",
"vuex": "3.0.1", "vuex": "3.0.1",
"xlsx": "0.11.16" "xlsx": "0.11.16"
}, },

View File

@ -0,0 +1,89 @@
<template>
<div class="board-column">
<div class="board-column-header">
{{headerText}}
</div>
<draggable
class="board-column-content"
:list="list"
:options="options">
<div class="board-item" v-for="element in list" :key="element.id">
{{element.name}} {{element.id}}
</div>
</draggable>
</div>
</template>
<script>
import draggable from 'vuedraggable'
export default {
name: 'dragKanban-demo',
components: {
draggable
},
props: {
headerText: {
type: String,
default: 'Header'
},
options: {
type: Object,
default() {
return {}
}
},
list: {
type: Array,
default() {
return []
}
}
}
}
</script>
<style lang="scss">
.board-column {
min-width: 300px;
min-height: 100px;
height: auto;
overflow: hidden;
background: #f0f0f0;
border-radius: 3px;
.board-column-header {
height: 50px;
line-height: 50px;
overflow: hidden;
padding: 0 20px;
text-align: center;
background: #333;
color: #fff;
border-radius: 3px 3px 0 0;
}
.board-column-content {
height: auto;
overflow: hidden;
border: 10px solid transparent;
min-height: 60px;
display: flex;
justify-content: flex-start;
flex-direction: column;
align-items: center;
.board-item {
cursor: pointer;
width: 100%;
height: 64px;
margin: 5px 0;
background-color: #fff;
text-align: left;
line-height: 54px;
padding: 5px 10px;
box-sizing: border-box;
box-shadow: 0px 1px 3px 0 rgba(0,0,0,0.2);
}
}
}
</style>

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

@ -2,39 +2,67 @@ export default{
bind(el, binding) { bind(el, binding) {
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 = 'cursor:move;' dialogHeaderEl.style.cssText += ';cursor:move;'
dragDom.style.cssText += ';top:0px;'
// 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null); // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null) const getStyle = (function() {
if (window.document.currentStyle) {
return (dom, attr) => dom.currentStyle[attr]
} else {
return (dom, attr) => getComputedStyle(dom, false)[attr]
}
})()
dialogHeaderEl.onmousedown = (e) => { dialogHeaderEl.onmousedown = (e) => {
// 鼠标按下,计算当前元素距离可视区的距离 // 鼠标按下,计算当前元素距离可视区的距离
const disX = e.clientX - dialogHeaderEl.offsetLeft const disX = e.clientX - dialogHeaderEl.offsetLeft
const disY = e.clientY - dialogHeaderEl.offsetTop const disY = e.clientY - dialogHeaderEl.offsetTop
// 获取到的值带px 正则匹配替换 const dragDomWidth = dragDom.offsetWidth
let styL, styT const dragDomheight = dragDom.offsetHeight
// 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px const screenWidth = document.body.clientWidth
if (sty.left.includes('%')) { const screenHeight = document.body.clientHeight
styL = +document.body.clientWidth * (+sty.left.replace(/\%/g, '') / 100)
styT = +document.body.clientHeight * (+sty.top.replace(/\%/g, '') / 100) const minDragDomLeft = dragDom.offsetLeft
const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth
const minDragDomTop = dragDom.offsetTop
const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight
// 获取到的值带px 正则匹配替换
let styL = getStyle(dragDom, 'left')
let styT = getStyle(dragDom, 'top')
if (styL.includes('%')) {
styL = +document.body.clientWidth * (+styL.replace(/\%/g, '') / 100)
styT = +document.body.clientHeight * (+styT.replace(/\%/g, '') / 100)
} else { } else {
styL = +sty.left.replace(/\px/g, '') styL = +styL.replace(/\px/g, '')
styT = +sty.top.replace(/\px/g, '') styT = +styT.replace(/\px/g, '')
} }
document.onmousemove = function(e) { document.onmousemove = function(e) {
// 通过事件委托,计算移动的距离 // 通过事件委托,计算移动的距离
const l = e.clientX - disX let left = e.clientX - disX
const t = e.clientY - disY let top = e.clientY - disY
// 边界处理
if (-(left) > minDragDomLeft) {
left = -minDragDomLeft
} else if (left > maxDragDomLeft) {
left = maxDragDomLeft
}
if (-(top) > minDragDomTop) {
top = -minDragDomTop
} else if (top > maxDragDomTop) {
top = maxDragDomTop
}
// 移动当前元素 // 移动当前元素
dragDom.style.left = `${l + styL}px` dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`
dragDom.style.top = `${t + styT}px`
// 将此时的位置传出去
// binding.value({x:e.pageX,y:e.pageY})
} }
document.onmouseup = function(e) { document.onmouseup = function(e) {

View File

@ -19,6 +19,7 @@ export default {
componentMixin: 'Mixin', componentMixin: 'Mixin',
backToTop: 'BackToTop', backToTop: 'BackToTop',
dragDialog: 'Drag Dialog', dragDialog: 'Drag Dialog',
dragKanban: 'Drag Kanban',
charts: 'Charts', charts: 'Charts',
keyboardChart: 'Keyboard Chart', keyboardChart: 'Keyboard Chart',
lineChart: 'Line Chart', lineChart: 'Line Chart',

View File

@ -19,6 +19,7 @@ export default {
componentMixin: '小组件', componentMixin: '小组件',
backToTop: '返回顶部', backToTop: '返回顶部',
dragDialog: '拖拽 Dialog', dragDialog: '拖拽 Dialog',
dragKanban: '可拖拽看板',
charts: '图表', charts: '图表',
keyboardChart: '键盘图表', keyboardChart: '键盘图表',
lineChart: '折线图', lineChart: '折线图',

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

@ -112,7 +112,8 @@ export const asyncRouterMap = [
{ path: 'count-to', component: _import('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('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('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('components-demo/dragDialog'), name: 'dragDialog-demo', meta: { title: 'dragDialog' }} { path: 'drag-dialog', component: _import('components-demo/dragDialog'), name: 'dragDialog-demo', meta: { title: 'dragDialog' }},
{ path: 'drag-kanban', component: _import('components-demo/dragKanban'), name: 'dragKanban-demo', meta: { title: 'dragKanban' }}
] ]
}, },

View File

@ -1,6 +1,7 @@
const getters = { const getters = {
sidebar: state => state.app.sidebar, sidebar: state => state.app.sidebar,
language: state => state.app.language, language: state => state.app.language,
device: state => state.app.device,
visitedViews: state => state.tagsView.visitedViews, visitedViews: state => state.tagsView.visitedViews,
cachedViews: state => state.tagsView.cachedViews, cachedViews: state => state.tagsView.cachedViews,
token: state => state.user.token, token: state => state.user.token,

View File

@ -3,8 +3,10 @@ import Cookies from 'js-cookie'
const app = { const app = {
state: { state: {
sidebar: { sidebar: {
opened: !+Cookies.get('sidebarStatus') opened: !+Cookies.get('sidebarStatus'),
withoutAnimation: false
}, },
device: 'desktop',
language: Cookies.get('language') || 'en' language: Cookies.get('language') || 'en'
}, },
mutations: { mutations: {
@ -15,6 +17,15 @@ const app = {
Cookies.set('sidebarStatus', 0) Cookies.set('sidebarStatus', 0)
} }
state.sidebar.opened = !state.sidebar.opened state.sidebar.opened = !state.sidebar.opened
state.sidebar.withoutAnimation = false
},
CLOSE_SIDEBAR: (state, withoutAnimation) => {
Cookies.set('sidebarStatus', 1)
state.sidebar.opened = false
state.sidebar.withoutAnimation = withoutAnimation
},
TOGGLE_DEVICE: (state, device) => {
state.device = device
}, },
SET_LANGUAGE: (state, language) => { SET_LANGUAGE: (state, language) => {
state.language = language state.language = language
@ -25,6 +36,12 @@ const app = {
toggleSideBar({ commit }) { toggleSideBar({ commit }) {
commit('TOGGLE_SIDEBAR') commit('TOGGLE_SIDEBAR')
}, },
closeSideBar({ commit }, { withoutAnimation }) {
commit('CLOSE_SIDEBAR', withoutAnimation)
},
toggleDevice({ commit }, device) {
commit('TOGGLE_DEVICE', device)
},
setLanguage({ commit }, language) { setLanguage({ commit }, language) {
commit('SET_LANGUAGE', language) commit('SET_LANGUAGE', language)
} }

View File

@ -67,7 +67,13 @@ const user = {
reject('error') reject('error')
} }
const data = response.data const data = response.data
commit('SET_ROLES', data.roles)
if (data.roles && data.roles.length > 0) { // 验证返回的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

@ -7,10 +7,7 @@
} }
// 侧边栏 // 侧边栏
.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;
@ -20,15 +17,30 @@
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 {
height: calc(100% + 15px);
.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;
} }
} }
@ -47,23 +59,30 @@
} }
} }
.el-submenu { .el-submenu {
overflow: hidden;
&>.el-submenu__title { &>.el-submenu__title {
padding-left: 10px !important; padding-left: 10px !important;
&>span {
height: 0;
width: 0;
overflow: hidden;
visibility: hidden;
display: inline-block;
}
.el-submenu__icon-arrow { .el-submenu__icon-arrow {
display: none; display: none;
} }
} }
} }
.el-menu--collapse {
.el-submenu {
&>.el-submenu__title {
&>span {
height: 0;
width: 0;
overflow: hidden;
visibility: hidden;
display: inline-block;
}
}
}
}
} }
.nest-menu .el-submenu>.el-submenu__title, .sidebar-container .nest-menu .el-submenu>.el-submenu__title,
.el-submenu .el-menu-item { .sidebar-container .el-submenu .el-menu-item {
min-width: 180px !important; min-width: 180px !important;
background-color: $subMenuBg !important; background-color: $subMenuBg !important;
&:hover { &:hover {
@ -73,4 +92,27 @@
.el-menu--collapse .el-menu .el-submenu { .el-menu--collapse .el-menu .el-submenu {
min-width: 180px !important; min-width: 180px !important;
} }
//适配移动端
.mobile {
.main-container {
margin-left: 0px;
}
.sidebar-container {
transition: transform .28s;
width: 180px !important;
}
&.hideSidebar {
.sidebar-container {
transition-duration: 0.3s;
transform: translate3d(-180px, 0, 0);
}
}
}
.withoutAnimation {
.main-container,
.sidebar-container {
transition: none;
}
}
} }

View File

@ -0,0 +1,68 @@
<template>
<div class="components-container board">
<Kanban :key="1" class="kanban todo" :list="list1" :options="options" header-text="Todo"/>
<Kanban :key="2" class="kanban working" :list="list2" :options="options" header-text="Working"/>
<Kanban :key="3" class="kanban done" :list="list3" :options="options" header-text="Done"/>
</div>
</template>
<script>
import Kanban from '@/components/Kanban'
export default {
name: 'dragKanban-demo',
components: {
Kanban
},
data() {
return {
options: {
group: 'mission'
},
list1: [
{ name: 'Mission', id: 1 },
{ name: 'Mission', id: 2 },
{ name: 'Mission', id: 3 },
{ name: 'Mission', id: 4 }
],
list2: [
{ name: 'Mission', id: 5 },
{ name: 'Mission', id: 6 },
{ name: 'Mission', id: 7 }
],
list3: [
{ name: 'Mission', id: 8 },
{ name: 'Mission', id: 9 },
{ name: 'Mission', id: 10 }
]
}
}
}
</script>
<style lang="scss">
.board {
width: 1000px;
margin-left: 20px;
display: flex;
justify-content: space-around;
flex-direction: row;
align-items: flex-start;
}
.kanban {
&.todo {
.board-column-header {
background: #4A9FF9;
}
}
&.working {
.board-column-header {
background: #f9944a;
}
}
&.done {
.board-column-header {
background: #2ac06d;
}
}
}
</style>

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

@ -1,5 +1,6 @@
<template> <template>
<div class="app-wrapper" :class="{hideSidebar:!sidebar.opened}"> <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>
@ -11,6 +12,7 @@
<script> <script>
import { Navbar, Sidebar, AppMain, TagsView } from './components' import { Navbar, Sidebar, AppMain, TagsView } from './components'
import ResizeMixin from './mixin/ResizeHandler'
export default { export default {
name: 'layout', name: 'layout',
@ -20,9 +22,25 @@ export default {
AppMain, AppMain,
TagsView TagsView
}, },
mixins: [ResizeMixin],
computed: { computed: {
sidebar() { sidebar() {
return this.$store.state.app.sidebar return this.$store.state.app.sidebar
},
device() {
return this.$store.state.app.device
},
classObj() {
return {
hideSidebar: !this.sidebar.opened,
withoutAnimation: this.sidebar.withoutAnimation,
mobile: this.device === 'mobile'
}
}
},
methods: {
handleClickOutside() {
this.$store.dispatch('closeSideBar', { withoutAnimation: false })
} }
} }
} }
@ -36,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,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

@ -0,0 +1,41 @@
import store from '@/store'
const { body } = document
const WIDTH = 1024
const RATIO = 3
export default {
watch: {
$route(route) {
if (this.device === 'mobile' && this.sidebar.opened) {
store.dispatch('closeSideBar', { withoutAnimation: false })
}
}
},
beforeMount() {
window.addEventListener('resize', this.resizeHandler)
},
mounted() {
const isMobile = this.isMobile()
if (isMobile) {
store.dispatch('toggleDevice', 'mobile')
store.dispatch('closeSideBar', { withoutAnimation: true })
}
},
methods: {
isMobile() {
const rect = body.getBoundingClientRect()
return rect.width - RATIO < WIDTH
},
resizeHandler() {
if (!document.hidden) {
const isMobile = this.isMobile()
store.dispatch('toggleDevice', isMobile ? 'mobile' : 'desktop')
if (isMobile) {
store.dispatch('closeSideBar', { withoutAnimation: true })
}
}
}
}
}