This commit is contained in:
2023-01-03 09:33:30 +08:00
commit bd4d0e897b
190 changed files with 48628 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
<template>
<section>
<router-view />
</section>
</template>
<script>
export default {
name: 'AppMain'
}
</script>

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

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

View File

@@ -0,0 +1,3 @@
export { default as AppMain } from './AppMain'
export { default as MenuList } from './menu/MenuList'
export { default as Navbar } from './Navbar'

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

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

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

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