init
This commit is contained in:
2
src/components/main/components/a-back-top/index.js
Normal file
2
src/components/main/components/a-back-top/index.js
Normal file
@@ -0,0 +1,2 @@
|
||||
import ABackTop from './index.vue'
|
||||
export default ABackTop
|
||||
94
src/components/main/components/a-back-top/index.vue
Normal file
94
src/components/main/components/a-back-top/index.vue
Normal file
@@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<div :class="classes" :style="styles" @click="back">
|
||||
<slot>
|
||||
<div :class="innerClasses">
|
||||
<i class="ivu-icon ivu-icon-ios-arrow-up"></i>
|
||||
</div>
|
||||
</slot>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { scrollTop, on, off } from '@/utils/tools'
|
||||
const prefixCls = 'ivu-back-top'
|
||||
|
||||
export default {
|
||||
name: 'ABackTop',
|
||||
props: {
|
||||
height: {
|
||||
type: Number,
|
||||
default: 400
|
||||
},
|
||||
bottom: {
|
||||
type: Number,
|
||||
default: 30
|
||||
},
|
||||
right: {
|
||||
type: Number,
|
||||
default: 30
|
||||
},
|
||||
duration: {
|
||||
type: Number,
|
||||
default: 1000
|
||||
},
|
||||
container: {
|
||||
type: null,
|
||||
default: window
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
backTop: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// window.addEventListener('scroll', this.handleScroll, false)
|
||||
// window.addEventListener('resize', this.handleScroll, false)
|
||||
on(this.containerEle, 'scroll', this.handleScroll)
|
||||
on(this.containerEle, 'resize', this.handleScroll)
|
||||
},
|
||||
beforeDestroy() {
|
||||
// window.removeEventListener('scroll', this.handleScroll, false)
|
||||
// window.removeEventListener('resize', this.handleScroll, false)
|
||||
off(this.containerEle, 'scroll', this.handleScroll)
|
||||
off(this.containerEle, 'resize', this.handleScroll)
|
||||
},
|
||||
computed: {
|
||||
classes() {
|
||||
return [
|
||||
`${prefixCls}`,
|
||||
{
|
||||
[`${prefixCls}-show`]: this.backTop
|
||||
}
|
||||
]
|
||||
},
|
||||
styles() {
|
||||
return {
|
||||
bottom: `${this.bottom}px`,
|
||||
right: `${this.right}px`
|
||||
}
|
||||
},
|
||||
innerClasses() {
|
||||
return `${prefixCls}-inner`
|
||||
},
|
||||
containerEle() {
|
||||
return this.container === window
|
||||
? window
|
||||
: document.querySelector(this.container)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleScroll() {
|
||||
this.backTop = this.containerEle.scrollTop >= this.height
|
||||
},
|
||||
back() {
|
||||
let target =
|
||||
typeof this.container === 'string'
|
||||
? this.containerEle
|
||||
: document.documentElement || document.body
|
||||
const sTop = target.scrollTop
|
||||
scrollTop(this.containerEle, sTop, 0, this.duration)
|
||||
this.$emit('on-click')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
90
src/components/main/components/fullscreen/fullscreen.vue
Normal file
90
src/components/main/components/fullscreen/fullscreen.vue
Normal file
@@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<div v-if="showFullScreenBtn" class="full-screen-btn-con">
|
||||
<Tooltip :content="value ? '退出全屏' : '全屏'" placement="bottom">
|
||||
<Icon @click.native="handleChange" :type="value ? 'md-contract' : 'md-expand'" :size="23"></Icon>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Fullscreen',
|
||||
computed: {
|
||||
showFullScreenBtn() {
|
||||
return window.navigator.userAgent.indexOf('MSIE') < 0
|
||||
}
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleFullscreen() {
|
||||
let main = document.body
|
||||
if (this.value) {
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen()
|
||||
} else if (document.mozCancelFullScreen) {
|
||||
document.mozCancelFullScreen()
|
||||
} else if (document.webkitCancelFullScreen) {
|
||||
document.webkitCancelFullScreen()
|
||||
} else if (document.msExitFullscreen) {
|
||||
document.msExitFullscreen()
|
||||
}
|
||||
} else {
|
||||
if (main.requestFullscreen) {
|
||||
main.requestFullscreen()
|
||||
} else if (main.mozRequestFullScreen) {
|
||||
main.mozRequestFullScreen()
|
||||
} else if (main.webkitRequestFullScreen) {
|
||||
main.webkitRequestFullScreen()
|
||||
} else if (main.msRequestFullscreen) {
|
||||
main.msRequestFullscreen()
|
||||
}
|
||||
}
|
||||
},
|
||||
handleChange() {
|
||||
this.handleFullscreen()
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
let isFullscreen =
|
||||
document.fullscreenElement ||
|
||||
document.mozFullScreenElement ||
|
||||
document.webkitFullscreenElement ||
|
||||
document.fullScreen ||
|
||||
document.mozFullScreen ||
|
||||
document.webkitIsFullScreen
|
||||
isFullscreen = !!isFullscreen
|
||||
document.addEventListener('fullscreenchange', () => {
|
||||
this.$emit('input', !this.value)
|
||||
this.$emit('on-change', !this.value)
|
||||
})
|
||||
document.addEventListener('mozfullscreenchange', () => {
|
||||
this.$emit('input', !this.value)
|
||||
this.$emit('on-change', !this.value)
|
||||
})
|
||||
document.addEventListener('webkitfullscreenchange', () => {
|
||||
this.$emit('input', !this.value)
|
||||
this.$emit('on-change', !this.value)
|
||||
})
|
||||
document.addEventListener('msfullscreenchange', () => {
|
||||
this.$emit('input', !this.value)
|
||||
this.$emit('on-change', !this.value)
|
||||
})
|
||||
this.$emit('input', isFullscreen)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.full-screen-btn-con .ivu-tooltip-rel {
|
||||
height: 64px;
|
||||
line-height: 56px;
|
||||
i {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
2
src/components/main/components/fullscreen/index.js
Normal file
2
src/components/main/components/fullscreen/index.js
Normal file
@@ -0,0 +1,2 @@
|
||||
import Fullscreen from './fullscreen.vue'
|
||||
export default Fullscreen
|
||||
@@ -0,0 +1,4 @@
|
||||
.custom-bread-crumb{
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<div class="custom-bread-crumb">
|
||||
<Breadcrumb :style="{fontSize: `${fontSize}px`}">
|
||||
<BreadcrumbItem v-for="item in list" :to="item.to" :key="`bread-crumb-${item.name}`">
|
||||
<common-icon style="margin-right: 4px;" :type="item.icon || ''" />
|
||||
{{ showTitle(item) }}
|
||||
</BreadcrumbItem>
|
||||
</Breadcrumb>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { showTitle } from '@/utils/tools'
|
||||
import CommonIcon from '@component/common-icon/common-icon'
|
||||
import './custom-bread-crumb.less'
|
||||
export default {
|
||||
name: 'customBreadCrumb',
|
||||
components: {
|
||||
CommonIcon,
|
||||
},
|
||||
props: {
|
||||
list: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
fontSize: {
|
||||
type: Number,
|
||||
default: 14,
|
||||
},
|
||||
showIcon: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
showTitle(item) {
|
||||
return showTitle(item, this)
|
||||
},
|
||||
isCustomIcon(iconName) {
|
||||
return iconName.indexOf('_') === 0
|
||||
},
|
||||
getCustomIconName(iconName) {
|
||||
return iconName.slice(1)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,2 @@
|
||||
import customBreadCrumb from './custom-bread-crumb.vue'
|
||||
export default customBreadCrumb
|
||||
18
src/components/main/components/header-bar/header-bar.less
Normal file
18
src/components/main/components/header-bar/header-bar.less
Normal file
@@ -0,0 +1,18 @@
|
||||
.header-bar {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
height: 64px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
.custom-content-con {
|
||||
height: 64px;
|
||||
padding-right: 120px;
|
||||
line-height: 64px;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
}
|
||||
39
src/components/main/components/header-bar/header-bar.vue
Normal file
39
src/components/main/components/header-bar/header-bar.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<div class="header-bar">
|
||||
<sider-trigger :collapsed="collapsed" icon="md-menu" @on-change="handleCollpasedChange"></sider-trigger>
|
||||
<custom-bread-crumb show-icon style="margin-left: 30px;" :list="breadCrumbList"></custom-bread-crumb>
|
||||
<div class="custom-content-con">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import siderTrigger from './sider-trigger'
|
||||
import customBreadCrumb from './custom-bread-crumb'
|
||||
import './header-bar.less'
|
||||
export default {
|
||||
name: 'HeaderBar',
|
||||
components: {
|
||||
siderTrigger,
|
||||
customBreadCrumb,
|
||||
},
|
||||
props: {
|
||||
collapsed: Boolean,
|
||||
},
|
||||
computed: {
|
||||
breadCrumbList() {
|
||||
let route = this.$route.matched.map((p) => {
|
||||
let { name, path, meta } = p
|
||||
return { name, path, meta }
|
||||
})
|
||||
route.shift()
|
||||
return route
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleCollpasedChange(state) {
|
||||
this.$emit('on-coll-change', state)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
2
src/components/main/components/header-bar/index.js
Normal file
2
src/components/main/components/header-bar/index.js
Normal file
@@ -0,0 +1,2 @@
|
||||
import HeaderBar from './header-bar'
|
||||
export default HeaderBar
|
||||
@@ -0,0 +1,2 @@
|
||||
import siderTrigger from './sider-trigger.vue'
|
||||
export default siderTrigger
|
||||
@@ -0,0 +1,21 @@
|
||||
.trans{
|
||||
transition: transform .2s ease;
|
||||
}
|
||||
@size: 40px;
|
||||
.sider-trigger-a{
|
||||
padding: 6px;
|
||||
width: @size;
|
||||
height: @size;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
color: #5c6b77;
|
||||
margin-top: 12px;
|
||||
i{
|
||||
.trans;
|
||||
vertical-align: top;
|
||||
}
|
||||
&.collapsed i{
|
||||
transform: rotateZ(90deg);
|
||||
.trans;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<a @click="handleChange" type="text" :class="['sider-trigger-a', collapsed ? 'collapsed' : '']"><Icon :type="icon" :size="size" /></a>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'siderTrigger',
|
||||
props: {
|
||||
collapsed: Boolean,
|
||||
icon: {
|
||||
type: String,
|
||||
default: 'navicon-round'
|
||||
},
|
||||
size: {
|
||||
type: Number,
|
||||
default: 26
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleChange() {
|
||||
this.$emit('on-change', !this.collapsed)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
@import './sider-trigger.less';
|
||||
</style>
|
||||
2
src/components/main/components/language/index.js
Normal file
2
src/components/main/components/language/index.js
Normal file
@@ -0,0 +1,2 @@
|
||||
import Language from './language.vue'
|
||||
export default Language
|
||||
51
src/components/main/components/language/language.vue
Normal file
51
src/components/main/components/language/language.vue
Normal file
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<div>
|
||||
<Dropdown trigger="click" @on-click="selectLang">
|
||||
<a href="javascript:void(0)">
|
||||
{{ title }}
|
||||
<Icon :size="18" type="md-arrow-dropdown" />
|
||||
</a>
|
||||
<DropdownMenu slot="list">
|
||||
<DropdownItem v-for="(value, key) in localList" :name="key" :key="`lang-${key}`">{{ value }}</DropdownItem>
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Language',
|
||||
props: {
|
||||
lang: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
langList: {
|
||||
'zh-CN': '语言',
|
||||
'zh-TW': '語言',
|
||||
'en-US': 'Lang'
|
||||
},
|
||||
localList: {
|
||||
'zh-CN': '中文简体',
|
||||
'zh-TW': '中文繁体',
|
||||
'en-US': 'English'
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
lang(lang) {
|
||||
this.$i18n.locale = lang
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
title() {
|
||||
return this.langList[this.lang]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectLang(name) {
|
||||
this.$emit('on-lang-change', name)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
58
src/components/main/components/side-menu/collapsed-menu.vue
Normal file
58
src/components/main/components/side-menu/collapsed-menu.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<Dropdown ref="dropdown" @on-click="handleClick" :class="hideTitle ? '' : 'collased-menu-dropdown'" :transfer="hideTitle" :placement="placement">
|
||||
<a class="drop-menu-a" type="text" @mouseover="handleMousemove($event, children)" :style="{textAlign: !hideTitle ? 'left' : ''}">
|
||||
<common-icon :size="rootIconSize" :color="textColor" :type="parentItem.meta.icon" /><span class="menu-title" v-if="!hideTitle">{{
|
||||
showTitle(parentItem) }}</span>
|
||||
<Icon style="float: right;" v-if="!hideTitle" type="ios-arrow-forward" :size="16" />
|
||||
</a>
|
||||
<DropdownMenu ref="dropdown" slot="list">
|
||||
<template v-for="child in children">
|
||||
<collapsed-menu v-if="showChildren(child)" :icon-size="iconSize" :parent-item="child" :key="`drop-${child.name}`"></collapsed-menu>
|
||||
<DropdownItem v-else :key="`drop-${child.name}`" :name="child.name">
|
||||
<common-icon :size="iconSize" :type="child.meta.icon" /><span class="menu-title">{{ showTitle(child) }}</span>
|
||||
</DropdownItem>
|
||||
</template>
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
</template>
|
||||
<script>
|
||||
import mixin from './mixin'
|
||||
import itemMixin from './item-mixin'
|
||||
import { findNodeUpperByClasses } from '@/utils/tools'
|
||||
|
||||
export default {
|
||||
name: 'CollapsedMenu',
|
||||
mixins: [mixin, itemMixin],
|
||||
props: {
|
||||
hideTitle: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
rootIconSize: {
|
||||
type: Number,
|
||||
default: 16,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
placement: 'right-end',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClick(name) {
|
||||
this.$emit('on-click', name)
|
||||
},
|
||||
handleMousemove(event, children) {
|
||||
const { pageY } = event
|
||||
const height = children.length * 38
|
||||
const isOverflow = pageY + height < window.innerHeight
|
||||
this.placement = isOverflow ? 'right-start' : 'right-end'
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
let dropdown = findNodeUpperByClasses(this.$refs.dropdown.$el, ['ivu-select-dropdown', 'ivu-dropdown-transfer'])
|
||||
if (dropdown) dropdown.style.overflow = 'visible'
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
2
src/components/main/components/side-menu/index.js
Normal file
2
src/components/main/components/side-menu/index.js
Normal file
@@ -0,0 +1,2 @@
|
||||
import SideMenu from './side-menu.vue'
|
||||
export default SideMenu
|
||||
21
src/components/main/components/side-menu/item-mixin.js
Normal file
21
src/components/main/components/side-menu/item-mixin.js
Normal file
@@ -0,0 +1,21 @@
|
||||
export default {
|
||||
props: {
|
||||
parentItem: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
theme: String,
|
||||
iconSize: Number
|
||||
},
|
||||
computed: {
|
||||
parentName() {
|
||||
return this.parentItem.name
|
||||
},
|
||||
children() {
|
||||
return this.parentItem.children
|
||||
},
|
||||
textColor() {
|
||||
return this.theme === 'dark' ? '#fff' : '#495060'
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/components/main/components/side-menu/mixin.js
Normal file
18
src/components/main/components/side-menu/mixin.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import CommonIcon from "@component/common-icon";
|
||||
import { showTitle } from "@/utils/tools";
|
||||
export default {
|
||||
components: {
|
||||
CommonIcon
|
||||
},
|
||||
methods: {
|
||||
showTitle(item) {
|
||||
return showTitle(item, this);
|
||||
},
|
||||
showChildren(item) {
|
||||
return item.type === "菜单";
|
||||
},
|
||||
getNameOrHref(item, children0) {
|
||||
return item.href ? `isTurnByHref_${item.href}` : item.name;
|
||||
}
|
||||
}
|
||||
};
|
||||
25
src/components/main/components/side-menu/side-menu-item.vue
Normal file
25
src/components/main/components/side-menu/side-menu-item.vue
Normal file
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<Submenu :name="`${parentName}`">
|
||||
<template slot="title">
|
||||
<common-icon :type="parentItem.meta.icon || ''" />
|
||||
<span>{{ showTitle(parentItem) }}</span>
|
||||
</template>
|
||||
<template v-for="item in children">
|
||||
<template v-if="item.is_show_menu">
|
||||
<side-menu-item v-if="showChildren(item)" :key="`menu-${item.name}`" :parent-item="item"></side-menu-item>
|
||||
<menu-item v-else :name="getNameOrHref(item)" :key="`menu-${item.name}`">
|
||||
<common-icon :type="item.meta.icon || ''" /><span>{{ showTitle(item) }}</span>
|
||||
</menu-item>
|
||||
</template>
|
||||
</template>
|
||||
</Submenu>
|
||||
</template>
|
||||
<script>
|
||||
import mixin from './mixin'
|
||||
import itemMixin from './item-mixin'
|
||||
export default {
|
||||
name: 'SideMenuItem',
|
||||
mixins: [mixin, itemMixin],
|
||||
mounted() {},
|
||||
}
|
||||
</script>
|
||||
39
src/components/main/components/side-menu/side-menu.less
Normal file
39
src/components/main/components/side-menu/side-menu.less
Normal file
@@ -0,0 +1,39 @@
|
||||
.side-menu-wrapper {
|
||||
user-select: none;
|
||||
.menu-collapsed {
|
||||
padding-top: 10px;
|
||||
|
||||
.ivu-dropdown {
|
||||
width: 100%;
|
||||
.ivu-dropdown-rel a {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.ivu-tooltip {
|
||||
width: 100%;
|
||||
.ivu-tooltip-rel {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ivu-tooltip-popper .ivu-tooltip-content {
|
||||
.ivu-tooltip-arrow {
|
||||
border-right-color: #fff;
|
||||
}
|
||||
.ivu-tooltip-inner {
|
||||
background: #fff;
|
||||
color: #495060;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
a.drop-menu-a {
|
||||
display: inline-block;
|
||||
padding: 6px 15px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
color: #495060;
|
||||
}
|
||||
}
|
||||
.menu-title {
|
||||
padding-left: 6px;
|
||||
}
|
||||
112
src/components/main/components/side-menu/side-menu.vue
Normal file
112
src/components/main/components/side-menu/side-menu.vue
Normal file
@@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<div class="side-menu-wrapper">
|
||||
<slot></slot>
|
||||
<Menu ref="menu" v-show="!collapsed" :active-name="activeName" :open-names="openedNames" :accordion="true" :theme="theme" width="auto" @on-select="handleSelect">
|
||||
<template v-for="item in menuList">
|
||||
<template>
|
||||
<side-menu-item v-if="showChildren(item)" :key="`menu-${item.name}`" :parent-item="item"></side-menu-item>
|
||||
<menu-item v-else :name="getNameOrHref(item)" :key="`menu-${item.name}`">
|
||||
<common-icon :type="item.meta.icon || ''" /><span>{{ showTitle(item) }}</span>
|
||||
</menu-item>
|
||||
</template>
|
||||
</template>
|
||||
</Menu>
|
||||
<div class="menu-collapsed" v-show="collapsed" :list="menuList">
|
||||
<template v-for="item in menuList">
|
||||
<collapsed-menu v-if="item.type==='菜单'" @on-click="handleSelect" hide-title :root-icon-size="rootIconSize" :icon-size="iconSize" :theme="theme" :parent-item="item" :key="`drop-menu-${item.name}`">
|
||||
</collapsed-menu>
|
||||
<Tooltip transfer v-else :content="showTitle(item.children && item.children[0] ? item.children[0] : item)" placement="right" :key="`drop-menu-${item.name}`">
|
||||
<a @click="handleSelect(getNameOrHref(item, true))" class="drop-menu-a" :style="{textAlign: 'center'}">
|
||||
<common-icon :size="rootIconSize" :color="textColor" :type="item.meta.icon" />
|
||||
</a>
|
||||
</Tooltip>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import SideMenuItem from './side-menu-item.vue'
|
||||
import CollapsedMenu from './collapsed-menu.vue'
|
||||
import { getUnion } from '@/utils/tools'
|
||||
import mixin from './mixin'
|
||||
|
||||
export default {
|
||||
name: 'SideMenu',
|
||||
mixins: [mixin],
|
||||
components: {
|
||||
SideMenuItem,
|
||||
CollapsedMenu,
|
||||
},
|
||||
props: {
|
||||
menuList: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
collapsed: {
|
||||
type: Boolean,
|
||||
},
|
||||
theme: {
|
||||
type: String,
|
||||
default: 'light',
|
||||
},
|
||||
rootIconSize: {
|
||||
type: Number,
|
||||
default: 20,
|
||||
},
|
||||
iconSize: {
|
||||
type: Number,
|
||||
default: 16,
|
||||
},
|
||||
|
||||
activeName: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
openNames: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
openedNames: [],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSelect(name) {
|
||||
this.$emit('on-select', name)
|
||||
},
|
||||
getOpenedNamesByActiveName(name) {
|
||||
let names = this.$route.matched.map((item) => item.name).filter((item) => item !== name)
|
||||
return names
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
textColor() {
|
||||
return this.theme === 'dark' ? '#fff' : '#495060'
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
activeName(name) {
|
||||
this.openedNames = this.getOpenedNamesByActiveName(name)
|
||||
},
|
||||
openNames(newNames) {
|
||||
this.openedNames = newNames
|
||||
},
|
||||
openedNames() {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.menu.updateOpened()
|
||||
})
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
let openedNames = getUnion(this.openedNames, this.getOpenedNamesByActiveName(this.activeName))
|
||||
this.openedNames = openedNames
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
@import './side-menu.less';
|
||||
</style>
|
||||
63
src/components/main/components/terminal/index.vue
Normal file
63
src/components/main/components/terminal/index.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div class="terminal-box">
|
||||
<Icon :class="iconclass" type="ios-radio" @click="show" />
|
||||
<asyncModal ref="asyncModal" title="终端" top="30" :footer-hide="true">
|
||||
<Terminal></Terminal>
|
||||
</asyncModal>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import Terminal from './terminal.vue'
|
||||
export default {
|
||||
components: {
|
||||
Terminal
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters({
|
||||
isServerRun: 'isServerRun'
|
||||
}),
|
||||
iconclass() {
|
||||
let curClass = 'terminal-icon ml10 '
|
||||
if (this.isServerRun) {
|
||||
curClass += ' run'
|
||||
}
|
||||
return curClass
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
show() {
|
||||
this.$refs['asyncModal'].show()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.terminal-box {
|
||||
margin-top: 3px;
|
||||
}
|
||||
.terminal-icon {
|
||||
color: #c5c8ce;
|
||||
font-size: 22px;
|
||||
cursor: pointer;
|
||||
|
||||
&.run {
|
||||
animation: colorChange 0.4s infinite; /* 调用颜色变化动画 */
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes colorChange {
|
||||
0% {
|
||||
color: #19be6b; /* 初始颜色 */
|
||||
}
|
||||
|
||||
100% {
|
||||
color: red; /* 返回到初始颜色 */
|
||||
}
|
||||
}
|
||||
</style>
|
||||
62
src/components/main/components/terminal/terminal.vue
Normal file
62
src/components/main/components/terminal/terminal.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div class="content-view">
|
||||
<div class="log-box">
|
||||
<div class="pa5 flex" style="align-items: flex-end; justify-content: flex-end;">
|
||||
<Button class="ml10" size="small" @click="reloadLog">重新加载日志</Button>
|
||||
<Button type="error" size="small" @click="clearLog">清空日志</Button>
|
||||
</div>
|
||||
<div class="view-log-info" id="view-log-info" style="height:70vh;width: 100%;">{{ infoMsg }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import uiTool from '@/utils/uiTool'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
mounted() {},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
infoMsg: 'infoMsg'
|
||||
})
|
||||
},
|
||||
|
||||
watch: {
|
||||
infoMsg: {
|
||||
handler() {
|
||||
this.scrollEnd()
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clearLog() {
|
||||
this.$store.commit('clearInfoMsg')
|
||||
},
|
||||
reloadLog() {
|
||||
this.$store.dispatch('setInteverLog')
|
||||
},
|
||||
scrollEnd() {
|
||||
setTimeout(() => {
|
||||
let outerDiv = document.querySelector('#view-log-info')
|
||||
if (outerDiv) {
|
||||
outerDiv.scrollTop = outerDiv.scrollHeight + 5
|
||||
}
|
||||
}, 300)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.log-box {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
2
src/components/main/components/user/index.js
Normal file
2
src/components/main/components/user/index.js
Normal file
@@ -0,0 +1,2 @@
|
||||
import User from './user.vue'
|
||||
export default User
|
||||
12
src/components/main/components/user/user.less
Normal file
12
src/components/main/components/user/user.less
Normal file
@@ -0,0 +1,12 @@
|
||||
.user{
|
||||
&-avator-dropdown{
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
// height: 64px;
|
||||
vertical-align: middle;
|
||||
// line-height: 64px;
|
||||
.ivu-badge-dot{
|
||||
top: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
68
src/components/main/components/user/user.vue
Normal file
68
src/components/main/components/user/user.vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div class="user-avator-dropdown">
|
||||
<label class="loginName-box">{{userName}}</label>
|
||||
<Dropdown>
|
||||
|
||||
<Avatar :src="typeof userAvator === 'string' ? userAvator : userIcon" />
|
||||
|
||||
<Icon :size="18" type="md-arrow-dropdown"></Icon>
|
||||
<DropdownMenu slot="list">
|
||||
<a @click="logout">
|
||||
<DropdownItem name="logout">
|
||||
<Icon type="md-exit" />
|
||||
退出登录
|
||||
</DropdownItem>
|
||||
</a>
|
||||
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import './user.less'
|
||||
import { mapMutations, mapActions } from 'vuex'
|
||||
|
||||
const userIcon = require("@/assets/images/administrato.png").default
|
||||
|
||||
export default {
|
||||
name: 'User',
|
||||
props: {
|
||||
userName: String,
|
||||
userAvator: {
|
||||
type: [String, Object],
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
userIcon: userIcon
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['handleLogOut']),
|
||||
logout() {
|
||||
this.handleLogOut(this)
|
||||
},
|
||||
|
||||
message() {
|
||||
this.$router.push({
|
||||
name: 'message_page',
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.user-avator-dropdown {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.loginName-box {
|
||||
font-weight: bold;
|
||||
color: #4b81d3;
|
||||
margin-right: 10px;
|
||||
}
|
||||
</style>
|
||||
2
src/components/main/index.js
Normal file
2
src/components/main/index.js
Normal file
@@ -0,0 +1,2 @@
|
||||
import Main from './main.vue'
|
||||
export default Main
|
||||
109
src/components/main/main.less
Normal file
109
src/components/main/main.less
Normal file
@@ -0,0 +1,109 @@
|
||||
.main {
|
||||
.logo-con {
|
||||
height: 64px;
|
||||
padding: 10px;
|
||||
padding-left: 0px;
|
||||
color: white;
|
||||
|
||||
img {
|
||||
width: auto;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
.header-con {
|
||||
padding: 0px 10px;
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.main-content-con {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
color: darkgray;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.main-layout-con {
|
||||
display: flex;
|
||||
display: -webkit-flex;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tag-nav-wrapper {
|
||||
padding: 0;
|
||||
height: 40px;
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
padding: 0px 0.05rem;
|
||||
height: ~"calc(100% - 40px)";
|
||||
display: flex;
|
||||
display: -webkit-flex;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.left-sider {
|
||||
.ivu-layout-sider-children {
|
||||
overflow-y: scroll;
|
||||
margin-right: -10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ivu-menu-item > i {
|
||||
margin-right: 12px !important;
|
||||
}
|
||||
|
||||
.ivu-menu-submenu > .ivu-menu > .ivu-menu-item > i {
|
||||
margin-right: 8px !important;
|
||||
}
|
||||
|
||||
.collased-menu-dropdown {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
line-height: normal;
|
||||
padding: 7px 0 6px 16px;
|
||||
clear: both;
|
||||
font-size: 12px !important;
|
||||
white-space: nowrap;
|
||||
list-style: none;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
background: rgba(100, 100, 100, 0.1);
|
||||
}
|
||||
|
||||
& * {
|
||||
color: #515a6e;
|
||||
}
|
||||
|
||||
.ivu-menu-item > i {
|
||||
margin-right: 12px !important;
|
||||
}
|
||||
|
||||
.ivu-menu-submenu > .ivu-menu > .ivu-menu-item > i {
|
||||
margin-right: 8px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ivu-select-dropdown.ivu-dropdown-transfer {
|
||||
max-height: 400px;
|
||||
}
|
||||
|
||||
.police-box {
|
||||
margin-left: 10px;
|
||||
color: darkgoldenrod;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
|
||||
span {
|
||||
margin: 0px 5px;
|
||||
}
|
||||
}
|
||||
145
src/components/main/main.vue
Normal file
145
src/components/main/main.vue
Normal file
@@ -0,0 +1,145 @@
|
||||
<template>
|
||||
|
||||
<Layout style="height: 100%" class="main">
|
||||
|
||||
<Sider hide-trigger collapsible :width="256" :collapsed-width="64" v-model="collapsed" class="left-sider" :style="{overflow: 'hidden'}">
|
||||
<div class="sidebar-brand" @click="goHome" v-if="!collapsed">
|
||||
<img style="width:50px" :src="sysFormModel.logoUrl" />
|
||||
<span class="ml10">{{sysFormModel.title}}</span>
|
||||
</div>
|
||||
|
||||
<side-menu class="mt30" theme="dark" ref="sideMenu" :active-name="$route.name" :collapsed="collapsed" @on-select="turnToPage" :menu-list="menuList"> </side-menu>
|
||||
</Sider>
|
||||
|
||||
<Content class="main-content-con">
|
||||
<Layout class="main-layout-con">
|
||||
|
||||
<Header>
|
||||
<headerBar class="header-con" @on-coll-change="collpasedChange" :collapsed="collapsed">
|
||||
<Terminal></Terminal>
|
||||
<user :userName="userName" :user-avator="userAvator || ''" />
|
||||
</headerBar>
|
||||
</Header>
|
||||
<Layout>
|
||||
<Content class="content-wrapper">
|
||||
|
||||
<router-view />
|
||||
<loadFlower> </loadFlower>
|
||||
<ABackTop :height="100" :bottom="80" :right="50" container=".content-wrapper"></ABackTop>
|
||||
</Content>
|
||||
</Layout>
|
||||
</Layout>
|
||||
</Content>
|
||||
</Layout>
|
||||
|
||||
</template>
|
||||
<script>
|
||||
const { title } = require('../../config')
|
||||
import SideMenu from './components/side-menu'
|
||||
import HeaderBar from './components/header-bar'
|
||||
|
||||
import User from './components/user'
|
||||
import ABackTop from './components/a-back-top'
|
||||
import Fullscreen from './components/fullscreen'
|
||||
import Language from './components/language'
|
||||
import { mapGetters } from 'vuex'
|
||||
import headerBar from './components/header-bar'
|
||||
import loadFlower from '../load-flower/index'
|
||||
import Terminal from './components/terminal'
|
||||
|
||||
import './main.less'
|
||||
|
||||
export default {
|
||||
name: 'Main',
|
||||
components: {
|
||||
SideMenu,
|
||||
HeaderBar,
|
||||
Language,
|
||||
|
||||
Fullscreen,
|
||||
User,
|
||||
Terminal,
|
||||
ABackTop,
|
||||
loadFlower,
|
||||
headerBar
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
title,
|
||||
collapsed: false,
|
||||
isFullscreen: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
sysFormModel: 'sysFormModel',
|
||||
menuList: 'menuList',
|
||||
tagRouter: 'tagRouter',
|
||||
userName: 'userName',
|
||||
userAvator: 'avatorImgPath'
|
||||
}),
|
||||
cacheList() {
|
||||
return ['ParentView']
|
||||
}
|
||||
},
|
||||
watch: {},
|
||||
|
||||
created() {
|
||||
this.init()
|
||||
},
|
||||
methods: {
|
||||
async init() {
|
||||
await this.$store.dispatch('getSysTitle')
|
||||
},
|
||||
collpasedChange(collapsed) {
|
||||
this.collapsed = collapsed
|
||||
},
|
||||
goHome() {
|
||||
this.$router.push({ path: '/' })
|
||||
},
|
||||
turnToPage(route) {
|
||||
let { name, params, query } = {}
|
||||
if (typeof route === 'string') name = route
|
||||
else {
|
||||
name = route.name
|
||||
params = route.params
|
||||
query = route.query
|
||||
}
|
||||
if (name.indexOf('isTurnByHref_') > -1) {
|
||||
window.open(name.split('_')[1])
|
||||
return
|
||||
}
|
||||
this.$router.push({
|
||||
name,
|
||||
params,
|
||||
query
|
||||
})
|
||||
},
|
||||
handleCollapsedChange(state) {
|
||||
this.collapsed = state
|
||||
},
|
||||
|
||||
handleClick(item) {
|
||||
this.turnToPage(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.shop-select {
|
||||
z-index: 999999;
|
||||
}
|
||||
|
||||
.sidebar-brand {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
height: 60px;
|
||||
line-height: 25px;
|
||||
color: #fff;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
23
src/components/main/pageHead.vue
Normal file
23
src/components/main/pageHead.vue
Normal file
@@ -0,0 +1,23 @@
|
||||
<template>
|
||||
<div class="table-head-tool ss">
|
||||
<Button shape="circle" v-if="$route.meta.type==='功能'" icon="ios-undo" @click="goBack">返回</Button>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
created() {},
|
||||
|
||||
methods: {
|
||||
goBack() {
|
||||
this.$router.go(-1)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
</style>
|
||||
Reference in New Issue
Block a user