1.3
This commit is contained in:
10
src/layout/components/AppMain.vue
Normal file
10
src/layout/components/AppMain.vue
Normal file
@@ -0,0 +1,10 @@
|
||||
<template>
|
||||
<section>
|
||||
<router-view />
|
||||
</section>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'AppMain'
|
||||
}
|
||||
</script>
|
||||
16
src/layout/components/Footer.vue
Normal file
16
src/layout/components/Footer.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<div class="footer">
|
||||
<span>© 中建材智能自动化研究院有限公司</span>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'footerPage'
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.footer {
|
||||
font-size: 12px;
|
||||
color: #c7c7c7;
|
||||
}
|
||||
</style>
|
||||
186
src/layout/components/Navbar.vue
Normal file
186
src/layout/components/Navbar.vue
Normal file
@@ -0,0 +1,186 @@
|
||||
<template>
|
||||
<div class="navbar-container">
|
||||
<div v-show="activeModule === 'home'" class="logo-box">
|
||||
<img src="./../../assets/logo.png" alt="中建材" class="logo-img" />
|
||||
<span class="title">G8.5 TFT-LCD 玻璃基板后工程段制造执行系统</span>
|
||||
</div>
|
||||
<breadcrumb v-show="activeModule !== 'home'" />
|
||||
<div class="fr">
|
||||
<div class="fr avatar-area">
|
||||
<el-dropdown>
|
||||
<div style="cursor: pointer">
|
||||
<img :src="require('./../../assets/home/avatar.png')" alt="" />
|
||||
<p>{{ userName }}</p>
|
||||
</div>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item @click.native="logout">退出</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<div class="top-time fr">
|
||||
<p>{{ topDate }}</p>
|
||||
<p>{{ topTime }}</p>
|
||||
</div>
|
||||
<ul class="topMenu-box">
|
||||
<li
|
||||
v-for="(item, i) in topModuleList"
|
||||
:key="i"
|
||||
@click="toggleModule(item)"
|
||||
>
|
||||
<div class="icon">
|
||||
<img
|
||||
:src="require('./../../assets/home/' + item.name + '.png')"
|
||||
:alt="item.label"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
class="module"
|
||||
:class="{ active: activeModule === item.name }"
|
||||
>{{ item.label }}</span
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import moment from 'moment'
|
||||
export default {
|
||||
name: 'NavbarPage',
|
||||
data() {
|
||||
return {
|
||||
topDate: '',
|
||||
topTime: '',
|
||||
timer: '',
|
||||
userName: this.$store.getters.username
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['topModuleList']),
|
||||
activeModule: {
|
||||
get: function () {
|
||||
return this.$store.getters.activeModule
|
||||
},
|
||||
set: function () {}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$store.dispatch('menu/getTopModuleList')
|
||||
this.getTime()
|
||||
},
|
||||
beforeDestroy() {
|
||||
clearInterval(this.timer)
|
||||
},
|
||||
methods: {
|
||||
getTime() {
|
||||
let _this = this
|
||||
this.timer = setInterval(function () {
|
||||
_this.topDate = moment().format('YYYY.MM.DD')
|
||||
_this.topTime = moment().format('HH : mm : ss')
|
||||
}, 1000)
|
||||
},
|
||||
toggleModule(item) {
|
||||
// 加载左侧菜单并跳转第一个菜单的页面
|
||||
const activeBox = { activeModule: item.name, activeMenu: '' }
|
||||
this.$store.dispatch('menu/setActiveModule', activeBox)
|
||||
},
|
||||
logout() {
|
||||
this.$store.dispatch('user/logout').then(() => {
|
||||
this.$router.push({ path: '/login' })
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
@import '@/styles/variables.module.scss';
|
||||
.navbar-container {
|
||||
height: 64px;
|
||||
background: #fff;
|
||||
box-shadow: 0px 1px 4px 0px rgba(0, 21, 41, 0.12);
|
||||
padding-right: 24px;
|
||||
.logo-box {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
.logo-img {
|
||||
height: 36px;
|
||||
width: 36px;
|
||||
margin: 16px 8px 12px 32px;
|
||||
}
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
width: 390px;
|
||||
color: #131415;
|
||||
position: absolute;
|
||||
top: 22px;
|
||||
}
|
||||
}
|
||||
.topMenu-box {
|
||||
display: flex;
|
||||
margin-top: 8px;
|
||||
li {
|
||||
margin-right: 24px;
|
||||
margin-left: 24px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
.icon {
|
||||
height: 32px;
|
||||
img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
.module {
|
||||
font-size: 10px;
|
||||
color: #000000;
|
||||
}
|
||||
.active {
|
||||
color: #0b58ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
.top-time {
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
color: #000000;
|
||||
margin: 12px 18px 0 0;
|
||||
p:first-child {
|
||||
margin-bottom: 9px;
|
||||
}
|
||||
p {
|
||||
width: 81px;
|
||||
text-align-last: justify;
|
||||
text-align: justify;
|
||||
}
|
||||
}
|
||||
.avatar-area {
|
||||
text-align: center;
|
||||
padding-top: 5px;
|
||||
div {
|
||||
display: inline-block;
|
||||
}
|
||||
img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
p {
|
||||
font-size: 12px;
|
||||
color: #000000;
|
||||
width: 50px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
.avatar-area::before {
|
||||
display: inline-block;
|
||||
content: '';
|
||||
margin-right: 15px;
|
||||
width: 1px;
|
||||
height: 47px;
|
||||
background-color: #dcdfe6;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
3
src/layout/components/index.js
Normal file
3
src/layout/components/index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
export { default as AppMain } from './AppMain'
|
||||
export { default as MenuList } from './menu/MenuList'
|
||||
export { default as Navbar } from './Navbar'
|
||||
35
src/layout/components/menu/Logo.vue
Normal file
35
src/layout/components/menu/Logo.vue
Normal file
@@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<div class="logo-box">
|
||||
<img src="./../../../assets/logo.png" alt="中建材" class="logo-img" />
|
||||
<div class="title">
|
||||
<p>G8.5 TFT-LCD</p>
|
||||
<p>玻璃基板后工程段制造执行系统</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'LogoVue'
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.logo-box {
|
||||
display: inline-block;
|
||||
width: 247px;
|
||||
height: 64px;
|
||||
background-color: #001529;
|
||||
position: relative;
|
||||
.logo-img {
|
||||
height: 36px;
|
||||
margin: 14px 8px 0 16px;
|
||||
}
|
||||
.title {
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: #fff;
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
91
src/layout/components/menu/MenuList.vue
Normal file
91
src/layout/components/menu/MenuList.vue
Normal file
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<div class="menu-container">
|
||||
<logo-vue />
|
||||
<el-menu :default-active="activeMenu">
|
||||
<template v-for="(item, i) in leftMenuList">
|
||||
<el-menu-item
|
||||
v-if="
|
||||
!(
|
||||
(item.name === 'offShelfPackaging' && role === '加工人员') ||
|
||||
(item.name === 'reLaunching' && role === '质检人员')
|
||||
)
|
||||
"
|
||||
:key="item.name + i"
|
||||
:index="item.name"
|
||||
@click="toggleMenu(item)"
|
||||
>
|
||||
<!-- <el-menu-item
|
||||
:key="item.name + i"
|
||||
:index="item.name"
|
||||
@click="toggleMenu(item)"
|
||||
> -->
|
||||
<i class="el-icon-s-operation"></i>
|
||||
<span slot="title">{{ item.meta.title }}</span>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</el-menu>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import variables from '@/styles/variables.module.scss'
|
||||
import LogoVue from './Logo.vue'
|
||||
export default {
|
||||
name: 'MenuList',
|
||||
computed: {
|
||||
variables() {
|
||||
return variables
|
||||
},
|
||||
leftMenuList() {
|
||||
return this.$store.state.menu.leftMenuList
|
||||
},
|
||||
activeModule() {
|
||||
return this.$store.state.menu.activeModule
|
||||
},
|
||||
activeMenu: {
|
||||
get: function () {
|
||||
return this.$store.getters.activeMenu
|
||||
},
|
||||
set: function () {}
|
||||
},
|
||||
role() {
|
||||
return this.$store.getters.roles
|
||||
}
|
||||
},
|
||||
components: {
|
||||
LogoVue
|
||||
},
|
||||
mounted() {},
|
||||
methods: {
|
||||
toggleMenu(e) {
|
||||
this.$store.dispatch('menu/setActiveMenu', e.name)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
activeModule: function (newVal, oldVal) {
|
||||
console.log(newVal)
|
||||
console.log(oldVal)
|
||||
},
|
||||
activeMenu: function (newVal) {
|
||||
this.$router.push({ name: newVal })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.menu-container {
|
||||
height: 100%;
|
||||
.el-menu {
|
||||
height: calc(100vh - 64px);
|
||||
background-color: #001529;
|
||||
.el-menu-item,
|
||||
.el-menu-item i {
|
||||
color: #fff;
|
||||
}
|
||||
.el-menu-item:focus,
|
||||
.el-menu-item:hover,
|
||||
.el-menu-item.is-active {
|
||||
background-color: #0b58ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
104
src/layout/components/tagsView/ScrollPane.vue
Normal file
104
src/layout/components/tagsView/ScrollPane.vue
Normal file
@@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<el-scrollbar
|
||||
ref="scrollContainer"
|
||||
:vertical="false"
|
||||
class="scroll-container"
|
||||
@wheel.native.prevent="handleScroll"
|
||||
>
|
||||
<slot />
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const tagAndTagSpacing = 4 // tagAndTagSpacing
|
||||
|
||||
export default {
|
||||
name: 'ScrollPane',
|
||||
data() {
|
||||
return {
|
||||
left: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
scrollWrapper() {
|
||||
return this.$refs.scrollContainer.$refs.wrap
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.scrollWrapper.addEventListener('scroll', this.emitScroll, true)
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.scrollWrapper.removeEventListener('scroll', this.emitScroll)
|
||||
},
|
||||
methods: {
|
||||
handleScroll(e) {
|
||||
const eventDelta = e.wheelDelta || -e.deltaY * 40
|
||||
const $scrollWrapper = this.scrollWrapper
|
||||
$scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
|
||||
},
|
||||
emitScroll() {
|
||||
this.$emit('scroll')
|
||||
},
|
||||
moveToTarget(currentTag) {
|
||||
const $container = this.$refs.scrollContainer.$el
|
||||
const $containerWidth = $container.offsetWidth
|
||||
const $scrollWrapper = this.scrollWrapper
|
||||
const tagList = this.$parent.$refs.tag
|
||||
|
||||
let firstTag = null
|
||||
let lastTag = null
|
||||
|
||||
// find first tag and last tag
|
||||
if (tagList.length > 0) {
|
||||
firstTag = tagList[0]
|
||||
lastTag = tagList[tagList.length - 1]
|
||||
}
|
||||
|
||||
if (firstTag === currentTag) {
|
||||
$scrollWrapper.scrollLeft = 0
|
||||
} else if (lastTag === currentTag) {
|
||||
$scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth
|
||||
} else {
|
||||
// find preTag and nextTag
|
||||
const currentIndex = tagList.findIndex((item) => item === currentTag)
|
||||
const prevTag = tagList[currentIndex - 1]
|
||||
const nextTag = tagList[currentIndex + 1]
|
||||
|
||||
// the tag's offsetLeft after of nextTag
|
||||
const afterNextTagOffsetLeft =
|
||||
nextTag.$el.offsetLeft + nextTag.$el.offsetWidth + tagAndTagSpacing
|
||||
|
||||
// the tag's offsetLeft before of prevTag
|
||||
const beforePrevTagOffsetLeft =
|
||||
prevTag.$el.offsetLeft - tagAndTagSpacing
|
||||
|
||||
if (
|
||||
afterNextTagOffsetLeft >
|
||||
$scrollWrapper.scrollLeft + $containerWidth
|
||||
) {
|
||||
$scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth
|
||||
} else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
|
||||
$scrollWrapper.scrollLeft = beforePrevTagOffsetLeft
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.scroll-container {
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
::v-deep {
|
||||
.el-scrollbar__bar {
|
||||
bottom: 0px;
|
||||
}
|
||||
.el-scrollbar__wrap {
|
||||
height: 49px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
116
src/layout/components/tagsView/index.vue
Normal file
116
src/layout/components/tagsView/index.vue
Normal file
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<div class="tags-view-container">
|
||||
<scroll-pane ref="scrollPane" class="tags-view-wrapper">
|
||||
<router-link
|
||||
v-for="tag in visitedViews"
|
||||
ref="tag"
|
||||
:key="tag.path"
|
||||
:class="isActive(tag) ? 'active' : ''"
|
||||
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
|
||||
class="tags-view-item"
|
||||
>
|
||||
{{ tag.title }}
|
||||
<span
|
||||
class="el-icon-close"
|
||||
@click.prevent.stop="closeSelectedTag(tag)"
|
||||
/>
|
||||
</router-link>
|
||||
</scroll-pane>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import ScrollPane from './ScrollPane'
|
||||
export default {
|
||||
components: { ScrollPane },
|
||||
name: 'TagesView',
|
||||
computed: {
|
||||
visitedViews() {
|
||||
return this.$store.state.tagsView.visitedViews
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
$route() {
|
||||
this.addTags()
|
||||
this.moveToCurrentTag()
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.addTags()
|
||||
},
|
||||
methods: {
|
||||
isActive(route) {
|
||||
return route.path === this.$route.path
|
||||
},
|
||||
addTags() {
|
||||
const { name } = this.$route
|
||||
if (name) {
|
||||
this.$store.dispatch('tagsView/addView', this.$route)
|
||||
}
|
||||
return false
|
||||
},
|
||||
moveToCurrentTag() {
|
||||
const tags = this.$refs.tag
|
||||
this.$nextTick(() => {
|
||||
for (const tag of tags) {
|
||||
if (tag.to.path === this.$route.path) {
|
||||
this.$refs.scrollPane.moveToTarget(tag)
|
||||
// when query is different then update
|
||||
if (tag.to.fullPath !== this.$route.fullPath) {
|
||||
this.$store.dispatch('tagsView/updateVisitedView', this.$route)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
closeSelectedTag(view) {
|
||||
this.$store
|
||||
.dispatch('tagsView/delView', view)
|
||||
.then(({ visitedViews }) => {
|
||||
if (this.isActive(view)) {
|
||||
this.toLastView(visitedViews, view)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.tags-view-container {
|
||||
height: 40px;
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
.tags-view-wrapper {
|
||||
.tags-view-item {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
border: 1px solid #f4f4f4;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
background: #f4f4f4;
|
||||
border-radius: 4px;
|
||||
padding: 0 8px;
|
||||
font-size: 12px;
|
||||
margin-left: 5px;
|
||||
margin-top: 8px;
|
||||
&:first-of-type {
|
||||
margin-left: 32px;
|
||||
}
|
||||
&:last-of-type {
|
||||
margin-right: 15px;
|
||||
}
|
||||
&:hover {
|
||||
color: rgba(89, 89, 89, 1);
|
||||
background: #efefef;
|
||||
}
|
||||
&.active {
|
||||
background-color: #3e8ef7;
|
||||
color: #fff;
|
||||
border-color: #3e8ef7;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
70
src/layout/index.vue
Normal file
70
src/layout/index.vue
Normal file
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<el-container class="itemContainer">
|
||||
<el-aside
|
||||
width="248px"
|
||||
style="background-color: rgb(238, 241, 246)"
|
||||
v-show="activeModule !== 'home'"
|
||||
>
|
||||
<menu-list />
|
||||
</el-aside>
|
||||
<el-container class="sectionBox">
|
||||
<el-header>
|
||||
<navbar />
|
||||
</el-header>
|
||||
<el-container>
|
||||
<div class="app-container">
|
||||
<tags-view v-show="activeModule !== 'home'" />
|
||||
<app-main class="app-main" />
|
||||
</div>
|
||||
<el-footer height="30px">
|
||||
<div class="footerFont">©中建材智能自动化研究院有限公司</div>
|
||||
</el-footer>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</template>
|
||||
<script>
|
||||
import { AppMain, MenuList, Navbar } from './components'
|
||||
import TagsView from './components/tagsView'
|
||||
import { mapGetters } from 'vuex'
|
||||
export default {
|
||||
name: 'LayoutPage',
|
||||
data() {
|
||||
return {
|
||||
sHome: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['activeModule'])
|
||||
},
|
||||
components: { AppMain, MenuList, Navbar, TagsView }
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import '@/styles/variables.module.scss';
|
||||
.itemContainer {
|
||||
min-width: 1500px;
|
||||
.el-header {
|
||||
padding: 0;
|
||||
}
|
||||
.sectionBox {
|
||||
height: 100%;
|
||||
background-color: $background-color-secondary;
|
||||
.app-container {
|
||||
height: calc(100vh - #{$navbarHeight} - #{$footerHeight} - 10px);
|
||||
margin-top: 7px;
|
||||
.app-main {
|
||||
height: calc(100vh - #{$navbarHeight} - #{$footerHeight} - 50px);
|
||||
}
|
||||
}
|
||||
.footerFont {
|
||||
font-size: 14px;
|
||||
line-height: $footerHeight;
|
||||
color: $color-text-secondary;
|
||||
}
|
||||
}
|
||||
.el-footer {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user