Merge remote-tracking branch 'origin/0.8.X' into 0.8.X
This commit is contained in:
@@ -303,4 +303,17 @@
|
||||
|
||||
#app .sidebar-container .nest-menu .el-submenu .el-submenu__title {
|
||||
margin: 0 4px;
|
||||
}
|
||||
|
||||
// 二级菜单叶子页面小圆点
|
||||
.el-menu-item.is-page-item::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: var(--current-color, #409EFF);
|
||||
margin-right: 8px;
|
||||
flex-shrink: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
@@ -2,7 +2,8 @@
|
||||
<div class="navbar">
|
||||
<hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
|
||||
|
||||
<breadcrumb id="breadcrumb-container" class="breadcrumb-container" v-if="!topNav"/>
|
||||
<breadcrumb id="breadcrumb-container" class="breadcrumb-container" v-show="topNav || !showSubNav"/>
|
||||
<sub-nav id="subnav-container" class="subnav-container" v-if="!topNav" @has-items="showSubNav = $event"/>
|
||||
<top-nav id="topmenu-container" class="topmenu-container" v-if="topNav"/>
|
||||
|
||||
<div class="right-menu">
|
||||
@@ -52,6 +53,7 @@
|
||||
import { mapGetters } from 'vuex'
|
||||
import Breadcrumb from '@/components/Breadcrumb'
|
||||
import TopNav from '@/components/TopNav'
|
||||
import SubNav from './SubNav'
|
||||
import Hamburger from '@/components/Hamburger'
|
||||
import Screenfull from '@/components/Screenfull'
|
||||
// import SizeSelect from '@/components/SizeSelect'
|
||||
@@ -64,6 +66,7 @@ export default {
|
||||
components: {
|
||||
Breadcrumb,
|
||||
TopNav,
|
||||
SubNav,
|
||||
Hamburger,
|
||||
Screenfull,
|
||||
// SizeSelect,
|
||||
@@ -74,7 +77,8 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
hasWarning: false,
|
||||
warningTimer: null
|
||||
warningTimer: null,
|
||||
showSubNav: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -153,6 +157,8 @@ export default {
|
||||
height: 50px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
// 金属质感渐变背景
|
||||
// background: #454c51;
|
||||
// border-bottom: 1px solid #8d939b;
|
||||
@@ -162,7 +168,7 @@ export default {
|
||||
.hamburger-container {
|
||||
line-height: 46px;
|
||||
height: 100%;
|
||||
float: left;
|
||||
flex-shrink: 0;
|
||||
cursor: pointer;
|
||||
transition: all .3s ease;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
@@ -177,10 +183,16 @@ export default {
|
||||
}
|
||||
|
||||
.breadcrumb-container {
|
||||
float: left;
|
||||
flex-shrink: 0;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.subnav-container {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.topmenu-container {
|
||||
position: absolute;
|
||||
left: 50px;
|
||||
@@ -192,7 +204,8 @@ export default {
|
||||
}
|
||||
|
||||
.right-menu {
|
||||
float: right;
|
||||
flex-shrink: 0;
|
||||
margin-left: auto;
|
||||
height: 100%;
|
||||
line-height: 50px;
|
||||
|
||||
|
||||
@@ -1,6 +1,23 @@
|
||||
<template>
|
||||
<div v-if="!item.hidden">
|
||||
<template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow">
|
||||
<template v-if="depth >= 1">
|
||||
<template v-if="hasVisibleChild">
|
||||
<app-link :to="subMenuRoute">
|
||||
<el-menu-item :index="basePath" :class="{'submenu-title-noDropdown':!isNest}">
|
||||
<item :icon="item.meta && item.meta.icon" :title="item.meta && item.meta.title" />
|
||||
</el-menu-item>
|
||||
</app-link>
|
||||
</template>
|
||||
<template v-else>
|
||||
<app-link :to="selfRoute">
|
||||
<el-menu-item :index="basePath" :class="{'submenu-title-noDropdown':!isNest, 'is-page-item': true}">
|
||||
<item :icon="item.meta && item.meta.icon" :title="item.meta && item.meta.title" />
|
||||
</el-menu-item>
|
||||
</app-link>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<template v-else-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow">
|
||||
<app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path, onlyOneChild.query)">
|
||||
<el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}">
|
||||
<item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title" />
|
||||
@@ -18,6 +35,7 @@
|
||||
:is-nest="true"
|
||||
:item="child"
|
||||
:base-path="resolvePath(child.path)"
|
||||
:depth="depth + 1"
|
||||
class="nest-menu"
|
||||
/>
|
||||
</el-submenu>
|
||||
@@ -48,12 +66,38 @@ export default {
|
||||
basePath: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
depth: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
data() {
|
||||
this.onlyOneChild = null
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
hasVisibleChild() {
|
||||
if (!this.item.children || !this.item.children.length) return false
|
||||
return this.item.children.some(c => !c.hidden)
|
||||
},
|
||||
selfRoute() {
|
||||
if (this.item.query) {
|
||||
return { path: this.basePath, query: JSON.parse(this.item.query) }
|
||||
}
|
||||
return this.basePath
|
||||
},
|
||||
subMenuRoute() {
|
||||
const title = (this.item.meta && this.item.meta.title) || this.item.name || ''
|
||||
return {
|
||||
path: '/redirect/subMenu',
|
||||
query: {
|
||||
parent: this.basePath,
|
||||
title: title
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
hasOneShowingChild(children = [], parent) {
|
||||
if (!children) {
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import path from 'path'
|
||||
import { mapGetters, mapState } from "vuex";
|
||||
import Logo from "./Logo";
|
||||
import SidebarItem from "./SidebarItem";
|
||||
@@ -36,12 +37,15 @@ export default {
|
||||
...mapGetters(["sidebarRouters", "sidebar"]),
|
||||
activeMenu() {
|
||||
const route = this.$route;
|
||||
const { meta, path } = route;
|
||||
// if set path, the sidebar will highlight the path you set
|
||||
const { meta, path: routePath } = route;
|
||||
if (meta.activeMenu) {
|
||||
return meta.activeMenu;
|
||||
}
|
||||
return path;
|
||||
const level2 = this.findLevel2Parent(routePath);
|
||||
if (level2) {
|
||||
return level2;
|
||||
}
|
||||
return routePath;
|
||||
},
|
||||
showLogo() {
|
||||
return this.$store.state.settings.sidebarLogo;
|
||||
@@ -53,5 +57,28 @@ export default {
|
||||
return !this.sidebar.opened;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
findLevel2Parent(currentPath) {
|
||||
return this.walkTree(this.sidebarRouters, currentPath, 0, '');
|
||||
},
|
||||
walkTree(routes, currentPath, depth, basePath) {
|
||||
for (const route of routes) {
|
||||
if (route.hidden) continue;
|
||||
const fullPath = basePath ? path.resolve(basePath, route.path) : route.path;
|
||||
if (currentPath !== fullPath && !currentPath.startsWith(fullPath + '/')) continue;
|
||||
if (depth >= 1) {
|
||||
return fullPath;
|
||||
}
|
||||
if (route.children && route.children.length) {
|
||||
const result = this.walkTree(route.children, currentPath, depth + 1, fullPath);
|
||||
if (result) return result;
|
||||
}
|
||||
if (currentPath === fullPath) {
|
||||
return depth >= 1 ? fullPath : null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
306
klp-ui/src/layout/components/SubNav.vue
Normal file
306
klp-ui/src/layout/components/SubNav.vue
Normal file
@@ -0,0 +1,306 @@
|
||||
<template>
|
||||
<div v-if="subNavItems.length > 0" class="sub-nav">
|
||||
<span class="sub-nav__arrow" :class="{ 'is-disabled': scrollLeft <= 0 }" @click="scrollTo('left')">
|
||||
<i class="el-icon-arrow-left" />
|
||||
</span>
|
||||
<div ref="subNavWrap" class="sub-nav__wrap" @mousewheel="handleWheel">
|
||||
<div ref="subNavInner" class="sub-nav__inner">
|
||||
<template v-for="item in subNavItems">
|
||||
<el-dropdown
|
||||
v-if="item._hasChildren"
|
||||
:key="item._ownPath"
|
||||
trigger="click"
|
||||
class="sub-nav__dropdown"
|
||||
@command="handleCommand"
|
||||
>
|
||||
<span class="sub-nav__item sub-nav__item--dropdown" :class="{ 'is-active': isActive(item) }">
|
||||
{{ item._title }}
|
||||
<i class="el-icon-arrow-down el-icon--right" />
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item
|
||||
v-for="child in item._children"
|
||||
:key="child._ownPath"
|
||||
:command="child._fullPath"
|
||||
:class="{ 'is-active': isChildActive(child) }"
|
||||
>
|
||||
{{ child._title }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
<router-link
|
||||
v-else
|
||||
:key="item._ownPath"
|
||||
:to="item._fullPath"
|
||||
class="sub-nav__item"
|
||||
:class="{ 'is-active': isActive(item) }"
|
||||
>
|
||||
{{ item._title }}
|
||||
</router-link>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<span class="sub-nav__arrow" :class="{ 'is-disabled': !canScrollRight }" @click="scrollTo('right')">
|
||||
<i class="el-icon-arrow-right" />
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import path from 'path'
|
||||
|
||||
export default {
|
||||
name: 'SubNav',
|
||||
data() {
|
||||
return {
|
||||
scrollLeft: 0,
|
||||
canScrollRight: false,
|
||||
resizeObserver: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
subNavItems() {
|
||||
const sidebarRouters = this.$store.state.permission.sidebarRouters
|
||||
const items = this.getThirdLevelMenus(sidebarRouters)
|
||||
return items
|
||||
},
|
||||
hasItems() {
|
||||
return this.subNavItems.length > 0
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
$route() {
|
||||
this.$nextTick(() => {
|
||||
this.checkArrows()
|
||||
this.scrollToActive()
|
||||
})
|
||||
},
|
||||
hasItems(val) {
|
||||
this.$emit('has-items', val)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.checkArrows()
|
||||
this.scrollToActive()
|
||||
})
|
||||
if (window.ResizeObserver) {
|
||||
this.resizeObserver = new ResizeObserver(() => {
|
||||
this.checkArrows()
|
||||
})
|
||||
if (this.$refs.subNavWrap) {
|
||||
this.resizeObserver.observe(this.$refs.subNavWrap)
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.resizeObserver) {
|
||||
this.resizeObserver.disconnect()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleCommand(command) {
|
||||
this.$router.push(command)
|
||||
},
|
||||
isActive(item) {
|
||||
return this.$route.path === item._ownPath || this.$route.path.startsWith(item._ownPath + '/')
|
||||
},
|
||||
isChildActive(child) {
|
||||
return this.$route.path === child._ownPath || this.$route.path.startsWith(child._ownPath + '/')
|
||||
},
|
||||
getThirdLevelMenus(routes) {
|
||||
let currentPath = this.$route.path
|
||||
if (currentPath === '/redirect/subMenu' && this.$route.query.parent) {
|
||||
currentPath = this.$route.query.parent
|
||||
}
|
||||
return this.findActiveLevel2(routes, currentPath, 0, '') || []
|
||||
},
|
||||
findActiveLevel2(routes, currentPath, depth, basePath) {
|
||||
for (const route of routes) {
|
||||
if (route.hidden) continue
|
||||
if (!route.children || route.children.length === 0) continue
|
||||
|
||||
const fullPath = basePath ? path.resolve(basePath, route.path) : route.path
|
||||
if (currentPath !== fullPath && !currentPath.startsWith(fullPath + '/')) continue
|
||||
|
||||
if (currentPath === fullPath && depth >= 1) {
|
||||
if (depth === 1) {
|
||||
return this.buildItems(route.children, fullPath)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const nested = this.findActiveLevel2(route.children, currentPath, depth + 1, fullPath)
|
||||
if (nested) return nested
|
||||
|
||||
if (depth === 1) {
|
||||
return this.buildItems(route.children, fullPath)
|
||||
}
|
||||
}
|
||||
return null
|
||||
},
|
||||
buildItems(children, parentPath) {
|
||||
return children.filter(c => !c.hidden).map(c => this.buildItem(c, parentPath))
|
||||
},
|
||||
buildItem(c, parentPath) {
|
||||
const ownPath = c.path.startsWith('/') ? c.path : path.resolve(parentPath, c.path)
|
||||
const title = (c.meta && c.meta.title) || c.name || c.path
|
||||
const hasChildren = c.children && c.children.length > 0 && c.children.some(gc => !gc.hidden)
|
||||
const item = {
|
||||
...c,
|
||||
_ownPath: ownPath,
|
||||
_fullPath: ownPath,
|
||||
_title: title,
|
||||
_hasChildren: hasChildren
|
||||
}
|
||||
if (hasChildren) {
|
||||
item._children = this.buildItems(c.children, ownPath)
|
||||
}
|
||||
return item
|
||||
},
|
||||
handleWheel(e) {
|
||||
if (!this.$refs.subNavWrap) return
|
||||
const delta = e.deltaY || e.wheelDelta
|
||||
this.$refs.subNavWrap.scrollLeft += delta
|
||||
this.scrollLeft = this.$refs.subNavWrap.scrollLeft
|
||||
this.checkArrows()
|
||||
},
|
||||
scrollTo(direction) {
|
||||
if (!this.$refs.subNavWrap) return
|
||||
const wrap = this.$refs.subNavWrap
|
||||
const scrollAmount = 200
|
||||
if (direction === 'left') {
|
||||
wrap.scrollLeft = Math.max(0, wrap.scrollLeft - scrollAmount)
|
||||
} else {
|
||||
wrap.scrollLeft = Math.min(wrap.scrollWidth - wrap.clientWidth, wrap.scrollLeft + scrollAmount)
|
||||
}
|
||||
this.scrollLeft = wrap.scrollLeft
|
||||
this.checkArrows()
|
||||
},
|
||||
checkArrows() {
|
||||
if (!this.$refs.subNavWrap) return
|
||||
const wrap = this.$refs.subNavWrap
|
||||
this.scrollLeft = wrap.scrollLeft
|
||||
this.canScrollRight = wrap.scrollWidth > wrap.clientWidth + wrap.scrollLeft + 1
|
||||
},
|
||||
scrollToActive() {
|
||||
if (!this.$refs.subNavWrap) return
|
||||
const activeEl = this.$refs.subNavWrap.querySelector('.sub-nav__item.is-active')
|
||||
if (activeEl) {
|
||||
const wrap = this.$refs.subNavWrap
|
||||
const activeLeft = activeEl.offsetLeft
|
||||
const activeRight = activeLeft + activeEl.offsetWidth
|
||||
if (activeLeft < wrap.scrollLeft) {
|
||||
wrap.scrollLeft = activeLeft
|
||||
} else if (activeRight > wrap.scrollLeft + wrap.clientWidth) {
|
||||
wrap.scrollLeft = activeRight - wrap.clientWidth
|
||||
}
|
||||
this.scrollLeft = wrap.scrollLeft
|
||||
this.checkArrows()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.sub-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 50px;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding: 0 4px;
|
||||
|
||||
&__arrow {
|
||||
flex-shrink: 0;
|
||||
width: 24px;
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
color: #606266;
|
||||
font-size: 12px;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover:not(.is-disabled) {
|
||||
background: rgba(0, 0, 0, 0.06);
|
||||
color: #111;
|
||||
}
|
||||
|
||||
&.is-disabled {
|
||||
color: #c0c4cc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
&__wrap {
|
||||
flex: 1;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
min-width: 0;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&__dropdown {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__item {
|
||||
flex-shrink: 0;
|
||||
padding: 6px 14px;
|
||||
margin: 0 2px;
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
border-radius: 4px;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
color: #111;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
background: var(--current-color, #409EFF);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&.is-active:hover {
|
||||
background: var(--current-color, #409EFF);
|
||||
color: #fff;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
&--dropdown {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
|
||||
.el-icon--right {
|
||||
margin-left: 2px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .sub-nav__dropdown .el-dropdown-menu__item.is-active {
|
||||
color: var(--current-color, #409EFF);
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
@@ -35,6 +35,12 @@ export const constantRoutes = [
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
children: [
|
||||
{
|
||||
path: 'subMenu',
|
||||
component: () => import('@/views/redirectMenu'),
|
||||
name: 'RedirectMenu',
|
||||
meta: { title: '子菜单' }
|
||||
},
|
||||
{
|
||||
path: '/redirect/:path(.*)',
|
||||
component: () => import('@/views/redirect')
|
||||
|
||||
@@ -23,5 +23,6 @@ const getters = {
|
||||
bomMap: state => state.category.bomMap,
|
||||
financialAccounts: state => state.finance.financialAccounts,
|
||||
processList: state => state.craft.processList,
|
||||
productionLines: state => state.productionLine.productionLines,
|
||||
}
|
||||
export default getters
|
||||
|
||||
@@ -9,6 +9,7 @@ import settings from './modules/settings'
|
||||
import category from './modules/category'
|
||||
import finance from './modules/finance'
|
||||
import craft from './modules/craft'
|
||||
import productionLine from './modules/productionLine'
|
||||
import getters from './getters'
|
||||
|
||||
Vue.use(Vuex)
|
||||
@@ -23,7 +24,8 @@ const store = new Vuex.Store({
|
||||
settings,
|
||||
category,
|
||||
finance,
|
||||
craft
|
||||
craft,
|
||||
productionLine
|
||||
},
|
||||
getters
|
||||
})
|
||||
|
||||
27
klp-ui/src/store/modules/productionLine.js
Normal file
27
klp-ui/src/store/modules/productionLine.js
Normal file
@@ -0,0 +1,27 @@
|
||||
import { listProductionLine } from "@/api/wms/productionLine";
|
||||
|
||||
const state = {
|
||||
productionLines: [],
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
SET_PRODUCTION_LINES(state, list) {
|
||||
state.productionLines = list;
|
||||
},
|
||||
}
|
||||
|
||||
const actions = {
|
||||
getProductionLines({ commit, state }) {
|
||||
if (state.productionLines.length > 0) return Promise.resolve()
|
||||
return listProductionLine({ pageNum: 1, pageSize: 100 }).then(response => {
|
||||
commit('SET_PRODUCTION_LINES', response.rows || []);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state,
|
||||
mutations,
|
||||
actions
|
||||
}
|
||||
@@ -494,9 +494,9 @@
|
||||
<script>
|
||||
import { listRollInfo, getRollInfo, addRollInfo, updateRollInfo, delRollInfo } from '@/api/mes/roll/rollInfo'
|
||||
import { listRollGrind, addRollGrind, updateRollGrind, delRollGrind, getMonthlyStats } from '@/api/mes/roll/rollGrind'
|
||||
import { listProductionLine } from '@/api/wms/productionLine'
|
||||
import { listData, addData, updateData, delData } from '@/api/system/dict/data'
|
||||
import rollLineMixin from '../rollLineMixin'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'GrindRoom',
|
||||
@@ -504,7 +504,6 @@ export default {
|
||||
dicts: ['mes_roll_operator'],
|
||||
data() {
|
||||
return {
|
||||
productionLines: [],
|
||||
filterLineId: null,
|
||||
lineTabOrder: [],
|
||||
|
||||
@@ -549,6 +548,8 @@ export default {
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters(['productionLines']),
|
||||
|
||||
currentUserName() {
|
||||
return this.$store.state.user.name || this.$store.getters.name || ''
|
||||
},
|
||||
@@ -606,9 +607,7 @@ export default {
|
||||
this.filterLineId = this.lineId
|
||||
const uid = this.$store.state.user?.userId || 0
|
||||
try { this.lineTabOrder = JSON.parse(localStorage.getItem(`grind_line_order_${uid}`) || '[]') } catch { /* ignore */ }
|
||||
listProductionLine({ pageNum: 1, pageSize: 100 }).then(res => {
|
||||
this.productionLines = res.rows || []
|
||||
})
|
||||
this.$store.dispatch('productionLine/getProductionLines')
|
||||
this.loadRolls()
|
||||
},
|
||||
|
||||
|
||||
252
klp-ui/src/views/redirectMenu.vue
Normal file
252
klp-ui/src/views/redirectMenu.vue
Normal file
@@ -0,0 +1,252 @@
|
||||
<template>
|
||||
<div class="submenu-page">
|
||||
<div class="submenu-page__header">
|
||||
<span class="submenu-page__parent-title">{{ parentTitle }}</span>
|
||||
</div>
|
||||
<div v-if="tree.length" class="submenu-page__list">
|
||||
<div v-for="group in tree" :key="group._ownPath" class="submenu-group">
|
||||
<div class="submenu-group__title" @click="goTo(group)">
|
||||
<span class="submenu-group__icon">
|
||||
<svg-icon v-if="group.meta && group.meta.icon" :icon-class="group.meta.icon" />
|
||||
<i v-else class="el-icon-folder-opened" />
|
||||
</span>
|
||||
<span class="submenu-group__text">{{ group._title }}</span>
|
||||
<span v-if="group._hasChildren" class="submenu-group__count">{{ group._children.length }}项</span>
|
||||
<i v-if="group._hasChildren" class="el-icon-arrow-right submenu-group__arrow" />
|
||||
</div>
|
||||
<div v-if="group._hasChildren" class="submenu-group__items">
|
||||
<div
|
||||
v-for="child in group._children"
|
||||
:key="child._ownPath"
|
||||
class="submenu-item"
|
||||
:class="{ 'submenu-item--leaf': !child._hasChildren }"
|
||||
@click="goTo(child)"
|
||||
>
|
||||
<i v-if="child._hasChildren" class="el-icon-folder submenu-item__folder" />
|
||||
<i v-else class="el-icon-document submenu-item__doc" />
|
||||
<span class="submenu-item__text">{{ child._title }}</span>
|
||||
<i v-if="child._hasChildren" class="el-icon-arrow-right submenu-item__arrow" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="submenu-page__empty">暂无可访问的菜单</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import path from 'path'
|
||||
|
||||
export default {
|
||||
name: 'RedirectMenu',
|
||||
computed: {
|
||||
parentPath() {
|
||||
return this.$route.query.parent || ''
|
||||
},
|
||||
parentTitle() {
|
||||
return this.$route.query.title || this.parentPath
|
||||
},
|
||||
tree() {
|
||||
const sidebarRouters = this.$store.state.permission.sidebarRouters
|
||||
const children = this.findChildren(sidebarRouters, this.parentPath, '')
|
||||
if (!children) return []
|
||||
return this.buildTree(children, this.parentPath)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
findChildren(routes, targetPath, basePath) {
|
||||
for (const route of routes) {
|
||||
if (route.hidden) continue
|
||||
const fullPath = basePath ? path.resolve(basePath, route.path) : route.path
|
||||
if (fullPath === targetPath) {
|
||||
return route.children || []
|
||||
}
|
||||
if (route.children && route.children.length > 0) {
|
||||
const result = this.findChildren(route.children, targetPath, fullPath)
|
||||
if (result) return result
|
||||
}
|
||||
}
|
||||
return null
|
||||
},
|
||||
buildTree(children, parentPath) {
|
||||
return children
|
||||
.filter(c => !c.hidden)
|
||||
.map(c => {
|
||||
const ownPath = c.path.startsWith('/') ? c.path : path.resolve(parentPath, c.path)
|
||||
const title = (c.meta && c.meta.title) || c.name || c.path || ''
|
||||
const hasChildren = c.children && c.children.length > 0 && c.children.some(gc => !gc.hidden)
|
||||
const item = {
|
||||
...c,
|
||||
_ownPath: ownPath,
|
||||
_fullPath: ownPath,
|
||||
_title: title,
|
||||
_hasChildren: hasChildren
|
||||
}
|
||||
if (hasChildren) {
|
||||
item._children = this.buildTree(c.children, ownPath)
|
||||
}
|
||||
return item
|
||||
})
|
||||
},
|
||||
goTo(item) {
|
||||
if (item._hasChildren) {
|
||||
this.$router.push({
|
||||
path: '/redirect/subMenu',
|
||||
query: { parent: item._ownPath, title: item._title }
|
||||
})
|
||||
} else if (item.path) {
|
||||
this.$router.push(item._fullPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.submenu-page {
|
||||
padding: 16px 20px;
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.submenu-page__header {
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.submenu-page__parent-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.submenu-page__list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.submenu-page__empty {
|
||||
text-align: center;
|
||||
padding: 40px 0;
|
||||
color: #909399;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.submenu-group {
|
||||
width: calc(50% - 6px);
|
||||
min-width: 280px;
|
||||
background: #fafbfc;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
transition: border-color 0.2s;
|
||||
|
||||
&:hover {
|
||||
border-color: #c0c4cc;
|
||||
}
|
||||
}
|
||||
|
||||
.submenu-group__title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
cursor: pointer;
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
transition: background 0.15s;
|
||||
|
||||
&:hover {
|
||||
background: #f5f7fa;
|
||||
}
|
||||
}
|
||||
|
||||
.submenu-group__icon {
|
||||
font-size: 16px;
|
||||
color: var(--current-color, #409EFF);
|
||||
margin-right: 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.submenu-group__text {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.submenu-group__count {
|
||||
font-size: 11px;
|
||||
color: #909399;
|
||||
margin-right: 6px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.submenu-group__arrow {
|
||||
font-size: 12px;
|
||||
color: #c0c4cc;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.submenu-group__items {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.submenu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px 16px 8px 24px;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s;
|
||||
|
||||
&:hover {
|
||||
background: #f0f2f5;
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
}
|
||||
}
|
||||
|
||||
.submenu-item__folder {
|
||||
font-size: 13px;
|
||||
color: #e6a23c;
|
||||
margin-right: 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.submenu-item__doc {
|
||||
font-size: 13px;
|
||||
color: #909399;
|
||||
margin-right: 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.submenu-item__text {
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.submenu-item__arrow {
|
||||
font-size: 12px;
|
||||
color: #c0c4cc;
|
||||
flex-shrink: 0;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.submenu-item--leaf {
|
||||
.submenu-item__doc {
|
||||
color: #c0c4cc;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user