mirror of
https://gitee.com/xiaonuobase/snowy.git
synced 2026-03-22 10:47:16 +08:00
【更新】大调整,详细如下:1、去掉了单页管理功能,所有单页可以挂载到某个菜单下面,这样打开某个单页左侧高亮它的上级菜单,就不会不知道哪个界面是哪里来的。2、增加了菜单显示隐藏功能。3、优化了路由相关,单独打开URL后可自动对应到对应的模块。4、取消了首页固定,默认设置菜单第一项则是首页,这样能有便于不同的角色锁定不同的首页。
This commit is contained in:
@@ -2,21 +2,21 @@
|
||||
<a-list :grid="grid" :data-source="props.dataSource" :pagination="pagination" :loading="loading">
|
||||
<template #renderItem="{ item, index }">
|
||||
<a-list-item :key="index">
|
||||
<a-badge-ribbon :text="item.badge.text" :color="item.badge.color?item.badge.color:''">
|
||||
<a-badge-ribbon :text="item.badge.text" :color="item.badge.color ? item.badge.color : ''">
|
||||
<a-card class="xn-card">
|
||||
<template #title>
|
||||
<a-tag color="orange">{{ item.title }}</a-tag>
|
||||
</template>
|
||||
<template #actions>
|
||||
<template v-for="{ key, label, icon, color} in props.actions">
|
||||
<template v-for="{ key, label, icon, color } in props.actions">
|
||||
<a-tooltip :title="label">
|
||||
<component :is="icon" @click="doAction(key, item)" :style="{color:color}"/>
|
||||
<component :is="icon" @click="doAction(key, item)" :style="{ color: color }" />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</template>
|
||||
<a-card-meta class="xn-card-meta">
|
||||
<template #avatar>
|
||||
<a-avatar shape="square" :size="64" :src="item.img"/>
|
||||
<a-avatar shape="square" :size="64" :src="item.img" />
|
||||
</template>
|
||||
<template #title>
|
||||
<span class="xn-card-meta-title">{{ item.subTitle }}</span>
|
||||
@@ -35,71 +35,71 @@
|
||||
</template>
|
||||
|
||||
<script setup name="xnCardList">
|
||||
import {message} from 'ant-design-vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
|
||||
const props = defineProps({
|
||||
// grid布局
|
||||
grid: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {gutter: 20, xs: 1, sm: 1, md: 2, lg: 2, xl: 3, xxl: 3, xxxl: 4}
|
||||
const props = defineProps({
|
||||
// grid布局
|
||||
grid: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return { gutter: 20, xs: 1, sm: 1, md: 2, lg: 2, xl: 3, xxl: 3, xxxl: 4 }
|
||||
}
|
||||
},
|
||||
// 数据源
|
||||
dataSource: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
// 分页
|
||||
page: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
// 动作
|
||||
actions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
// 数据源
|
||||
dataSource: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
// 分页
|
||||
page: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
// 动作
|
||||
actions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
const emit = defineEmits(['action', 'page-change'])
|
||||
})
|
||||
const emit = defineEmits(['action', 'page-change'])
|
||||
|
||||
// 分页参数
|
||||
const {current, size, total} = toRefs(props.page)
|
||||
const pagination = reactive({
|
||||
onChange: current => {
|
||||
emit('page-change', current)
|
||||
},
|
||||
current: current,
|
||||
pageSize: size,
|
||||
total: total
|
||||
})
|
||||
// 分页参数
|
||||
const { current, size, total } = toRefs(props.page)
|
||||
const pagination = reactive({
|
||||
onChange: (current) => {
|
||||
emit('page-change', current)
|
||||
},
|
||||
current: current,
|
||||
pageSize: size,
|
||||
total: total
|
||||
})
|
||||
|
||||
// 触发 action
|
||||
const doAction = (key, item) => {
|
||||
if (!item.record) {
|
||||
message.error('记录参数[record]错误')
|
||||
return
|
||||
// 触发 action
|
||||
const doAction = (key, item) => {
|
||||
if (!item.record) {
|
||||
message.error('记录参数[record]错误')
|
||||
return
|
||||
}
|
||||
emit('action', { key, record: item.record })
|
||||
}
|
||||
emit('action', {key, record: item.record})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.xn-card {
|
||||
background: linear-gradient(141.6deg, var(--primary-1) 0%, rgba(255, 255, 255, 0) 70%);
|
||||
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
|
||||
.xn-card {
|
||||
background: linear-gradient(141.6deg, var(--primary-1) 0%, rgba(255, 255, 255, 0) 70%);
|
||||
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
|
||||
|
||||
.xn-card-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.xn-card-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.xn-card-meta-title {
|
||||
font-size: 14px;
|
||||
.xn-card-meta-title {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -47,10 +47,9 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #actions>
|
||||
<a-button type="link" @click="doAction(key, item)"
|
||||
v-for="{ key, label, icon, color} in props.actions">
|
||||
<a-button type="link" @click="doAction(key, item)" v-for="{ key, label, icon, color } in props.actions">
|
||||
<template #icon>
|
||||
<component :is="icon" :style="{color:color}"/>
|
||||
<component :is="icon" :style="{ color: color }" />
|
||||
</template>
|
||||
{{ label }}
|
||||
</a-button>
|
||||
@@ -61,129 +60,129 @@
|
||||
</template>
|
||||
|
||||
<script setup name="xnDataList">
|
||||
import {message} from "ant-design-vue";
|
||||
import { message } from 'ant-design-vue'
|
||||
|
||||
const props = defineProps({
|
||||
// 数据源
|
||||
dataSource: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
// 分页
|
||||
page: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
// 动作
|
||||
actions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
const emit = defineEmits(['title', 'action', 'page-change'])
|
||||
const props = defineProps({
|
||||
// 数据源
|
||||
dataSource: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
// 分页
|
||||
page: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
// 动作
|
||||
actions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
const emit = defineEmits(['title', 'action', 'page-change'])
|
||||
|
||||
// 分页参数
|
||||
const {current, size, total} = toRefs(props.page)
|
||||
const pagination = reactive({
|
||||
onChange: current => {
|
||||
emit('page-change', current)
|
||||
},
|
||||
current: current,
|
||||
pageSize: size,
|
||||
total: total
|
||||
})
|
||||
// 分页参数
|
||||
const { current, size, total } = toRefs(props.page)
|
||||
const pagination = reactive({
|
||||
onChange: (current) => {
|
||||
emit('page-change', current)
|
||||
},
|
||||
current: current,
|
||||
pageSize: size,
|
||||
total: total
|
||||
})
|
||||
|
||||
// 出发 点击标题
|
||||
const clickTitle = (item) => {
|
||||
if (!item.record) {
|
||||
message.error('记录参数[record]错误')
|
||||
return
|
||||
// 出发 点击标题
|
||||
const clickTitle = (item) => {
|
||||
if (!item.record) {
|
||||
message.error('记录参数[record]错误')
|
||||
return
|
||||
}
|
||||
emit('title', { record: item.record })
|
||||
}
|
||||
emit('title', {record: item.record})
|
||||
}
|
||||
// 触发 action
|
||||
const doAction = (key, item) => {
|
||||
if (!item.record) {
|
||||
message.error('记录参数[record]错误')
|
||||
return
|
||||
// 触发 action
|
||||
const doAction = (key, item) => {
|
||||
if (!item.record) {
|
||||
message.error('记录参数[record]错误')
|
||||
return
|
||||
}
|
||||
emit('action', { key, record: item.record })
|
||||
}
|
||||
emit('action', {key, record: item.record})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.xn-data-list-item {
|
||||
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
|
||||
padding: 12px 15px;
|
||||
margin-bottom: 10px;
|
||||
.xn-data-list-item {
|
||||
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
|
||||
padding: 12px 15px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.xn-data-list-item-meta {
|
||||
align-items: center;
|
||||
padding-bottom: 15px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
.xn-data-list-item-meta {
|
||||
align-items: center;
|
||||
padding-bottom: 15px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
.xn-data-list-item-meta-title {
|
||||
.xn-data-list-item-meta-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.xn-data-list-item-meta-title-prefix {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--text-color);
|
||||
transition: all 0.3s;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.xn-data-list-item-meta-title-suffix {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.xn-content {
|
||||
//box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.xn-extra {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.xn-data-list-item-meta-title-prefix {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--text-color);
|
||||
transition: all 0.3s;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.xn-data-list-item-meta-title-suffix {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.xn-content {
|
||||
//box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.xn-extra {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.xn-statistics {
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
margin: 5px 20px;
|
||||
padding: 10px;
|
||||
color: var(--text-color);
|
||||
font-size: 14px;
|
||||
font-variant: tabular-nums;
|
||||
line-height: 1.5715;
|
||||
list-style: none;
|
||||
font-feature-settings: 'tnum';
|
||||
|
||||
&:hover {
|
||||
box-shadow: var(--card-shadow);
|
||||
}
|
||||
|
||||
.xn-statistic-title {
|
||||
margin-bottom: 4px;
|
||||
color: var(--text-color-secondary);
|
||||
.xn-statistics {
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
margin: 5px 20px;
|
||||
padding: 10px;
|
||||
color: var(--text-color);
|
||||
font-size: 14px;
|
||||
}
|
||||
font-variant: tabular-nums;
|
||||
line-height: 1.5715;
|
||||
list-style: none;
|
||||
font-feature-settings: 'tnum';
|
||||
|
||||
.xn-statistic-content {
|
||||
color: var(--heading-color);
|
||||
font-size: 15px;
|
||||
&:hover {
|
||||
box-shadow: var(--card-shadow);
|
||||
}
|
||||
|
||||
.xn-statistic-title {
|
||||
margin-bottom: 4px;
|
||||
color: var(--text-color-secondary);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.xn-statistic-content {
|
||||
color: var(--heading-color);
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -25,25 +25,12 @@ const routes = {
|
||||
children: []
|
||||
}
|
||||
],
|
||||
// 默认首页、个人中心
|
||||
// 默认首页为个人中心
|
||||
menu: [
|
||||
{
|
||||
id: '001',
|
||||
name: 'index',
|
||||
path: '/index',
|
||||
component: 'index/index',
|
||||
meta: {
|
||||
title: '首页',
|
||||
type: 'menu',
|
||||
icon: 'bank-outlined',
|
||||
affix: true
|
||||
},
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: '002',
|
||||
name: 'userCenter',
|
||||
path: '/userCenter',
|
||||
name: 'usercenter',
|
||||
path: '/usercenter',
|
||||
component: 'sys/user/userCenter',
|
||||
meta: {
|
||||
title: '个人中心',
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<a-alert message="无任何菜单" type="info" :closable="false" />
|
||||
</div>
|
||||
<template v-for="navMenu in navMenus" :key="navMenu">
|
||||
<a-menu-item v-if="!hasChildren(navMenu)" :key="navMenu.path">
|
||||
<a-menu-item v-if="!hasChildren(navMenu) & !hasHidden(navMenu)" :key="navMenu.path">
|
||||
<template v-if="navMenu.meta.icon" #icon>
|
||||
<component :is="navMenu.meta.icon" />
|
||||
</template>
|
||||
@@ -12,15 +12,15 @@
|
||||
:href="navMenu.path"
|
||||
target="_blank"
|
||||
@click.stop="() => {}"
|
||||
>{{ navMenu.meta.title }}</a
|
||||
>{{ navMenu.meta.title }}</a
|
||||
>
|
||||
<a v-else>{{ navMenu.meta.title }}</a>
|
||||
</a-menu-item>
|
||||
<a-sub-menu v-else :key="navMenu.path" :title="navMenu.meta.title">
|
||||
<a-sub-menu v-else-if="!hasHidden(navMenu)" :key="navMenu.path" :title="navMenu.meta.title">
|
||||
<template v-if="navMenu.meta.icon" #icon>
|
||||
<component :is="navMenu.meta.icon" />
|
||||
</template>
|
||||
<NavMenu :nav-menus="navMenu.children"></NavMenu>
|
||||
<NavMenu :nav-menus="navMenu.children" />
|
||||
</a-sub-menu>
|
||||
</template>
|
||||
</template>
|
||||
@@ -32,8 +32,15 @@
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
|
||||
const hasChildren = (item) => {
|
||||
return item.children && !item.children.every((item) => item.meta.hidden)
|
||||
}
|
||||
// 是否隐藏
|
||||
const hasHidden = (item) => {
|
||||
if (item.meta.hidden === true) {
|
||||
return true
|
||||
}
|
||||
// 为空跟false,都会显示
|
||||
return false
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
import XnContextMenu from '@/components/XnContextMenu/index.vue'
|
||||
import { globalStore, iframeStore, keepAliveStore, viewTagsStore } from '@/store'
|
||||
import { mapState, mapActions } from 'pinia'
|
||||
import routerUtil from '@/utils/routerUtil'
|
||||
|
||||
export default {
|
||||
name: 'Tags',
|
||||
@@ -95,7 +96,7 @@
|
||||
},
|
||||
created() {
|
||||
const module = this.$router.getMenu()
|
||||
const indexMenu = tool.data.get('MENU') ? tool.data.get('MENU')[0].children[0].path : this.$CONFIG.DASHBOARD_URL
|
||||
const indexMenu = routerUtil.getIndexMenu(module).path
|
||||
// eslint-disable-next-line eqeqeq
|
||||
const dashboardRoute = this.treeFind(module, (node) => node.path === indexMenu)
|
||||
if (dashboardRoute) {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
:theme="sideTheme"
|
||||
mode="inline"
|
||||
@select="onSelect"
|
||||
@openChange="onOpenChange"
|
||||
>
|
||||
<NavMenu :nav-menus="menu" />
|
||||
</a-menu>
|
||||
@@ -79,9 +80,14 @@
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<a-menu v-model:selectedKeys="doublerowSelectedKey" :theme="sideTheme" class="snowy-doublerow-layout-menu">
|
||||
<a-menu
|
||||
v-model:selectedKeys="doublerowSelectedKey"
|
||||
:theme="sideTheme"
|
||||
class="snowy-doublerow-layout-menu"
|
||||
v-for="item in menu"
|
||||
:key="item.path"
|
||||
>
|
||||
<a-menu-item
|
||||
v-for="item in menu"
|
||||
:key="item.path"
|
||||
style="
|
||||
text-align: center;
|
||||
@@ -93,8 +99,9 @@
|
||||
padding: 12px 0 !important;
|
||||
"
|
||||
@click="showMenu(item)"
|
||||
v-if="!item.meta.hidden"
|
||||
>
|
||||
<a v-if="item.meta && item.meta.type === 'link'" :href="item.path" target="_blank" @click.stop="() => {}"></a>
|
||||
<a v-if="item.meta && item.meta.type === 'link'" :href="item.path" target="_blank" @click.stop="() => {}" />
|
||||
<template #icon>
|
||||
<component :is="item.meta.icon" style="padding-left: 10px" />
|
||||
</template>
|
||||
@@ -176,7 +183,7 @@
|
||||
import TopBar from '@/layout/components/topbar.vue'
|
||||
import { globalStore, keepAliveStore } from '@/store'
|
||||
import { ThemeModeEnum } from '@/utils/enum'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import tool from '@/utils/tool'
|
||||
import { message } from 'ant-design-vue'
|
||||
|
||||
@@ -194,7 +201,6 @@
|
||||
const moduleMenuShow = ref(true)
|
||||
const doublerowSelectedKey = ref([])
|
||||
const layoutSiderDowbleMenu = ref(true)
|
||||
const currentRoute = ref()
|
||||
// computed计算方法 - start
|
||||
const layout = computed(() => {
|
||||
return store.layout
|
||||
@@ -241,42 +247,33 @@
|
||||
const secondMenuSideTheme = computed(() => {
|
||||
return theme.value === ThemeModeEnum.REAL_DARK ? ThemeModeEnum.DARK : ThemeModeEnum.LIGHT
|
||||
})
|
||||
// 转换外部链接的路由
|
||||
const filterUrl = (map) => {
|
||||
const newMap = []
|
||||
const traverse = (maps) => {
|
||||
maps &&
|
||||
maps.forEach((item) => {
|
||||
item.meta = item.meta ? item.meta : {}
|
||||
// 处理隐藏
|
||||
if (item.meta.hidden) {
|
||||
return false
|
||||
}
|
||||
// 处理iframe
|
||||
if (item.meta.type === 'iframe') {
|
||||
item.path = `/i/${item.name}`
|
||||
}
|
||||
// 递归循环
|
||||
if (item.children && item.children.length > 0) {
|
||||
item.children = filterUrl(item.children)
|
||||
}
|
||||
newMap.push(item)
|
||||
})
|
||||
}
|
||||
traverse(map)
|
||||
return newMap
|
||||
}
|
||||
// 路由监听高亮
|
||||
const showThis = () => {
|
||||
pMenu.value = route.meta.breadcrumb ? route.meta.breadcrumb[0] : {}
|
||||
// 展开的
|
||||
nextTick(() => {
|
||||
// 取得默认路由地址并设置展开
|
||||
const active = route.meta.active || route.path
|
||||
let active = route.meta.active || route.path
|
||||
// 如果是目录,必须往下找
|
||||
if (route.meta.type === 'catalog') {
|
||||
active = traverseChild(pMenu.value.children, active).path
|
||||
}
|
||||
selectedKeys.value = new Array(active)
|
||||
const pidKey = getParentKeys(pMenu.value.children, active)
|
||||
// 判断是隐藏的路由,找其上级
|
||||
if (route.meta.hidden && pidKey) {
|
||||
if (pidKey.length > 1) {
|
||||
selectedKeys.value = new Array(pidKey[1])
|
||||
}
|
||||
}
|
||||
const nextTickMenu = pMenu.value.children
|
||||
if (pidKey) {
|
||||
const modelPidKey = getParentKeys(moduleMenu.value, route.path)
|
||||
moduleMenu.value.forEach((item) => {
|
||||
if (modelPidKey.includes(item.path)) {
|
||||
tagSwitchModule(item.id)
|
||||
}
|
||||
})
|
||||
const parentPath = pidKey[pidKey.length - 1]
|
||||
if (layout.value === 'doublerow') {
|
||||
// 这一串操作下来只为取到最上面的路由的孩子们,最后成为双排菜单的第二排
|
||||
@@ -300,19 +297,17 @@
|
||||
moduleMenu.value = router.getMenu()
|
||||
// 获取缓存中的菜单模块是哪个
|
||||
const menuModuleId = tool.data.get('SNOWY_MENU_MODULE_ID')
|
||||
let initMenu = []
|
||||
if (menuModuleId) {
|
||||
// 防止切换一个无此应用的人
|
||||
const module = router.getMenu().filter((item) => item.id === menuModuleId)
|
||||
if (module.length > 0) {
|
||||
initMenu = module[0].children
|
||||
menu.value = module[0].children
|
||||
} else {
|
||||
initMenu = router.getMenu()[0].children
|
||||
menu.value = router.getMenu()[0].children
|
||||
}
|
||||
} else {
|
||||
initMenu = router.getMenu()[0].children
|
||||
menu.value = router.getMenu()[0].children
|
||||
}
|
||||
menu.value = filterUrl(initMenu)
|
||||
showThis()
|
||||
|
||||
onMounted(() => {
|
||||
@@ -321,18 +316,9 @@
|
||||
switchoverTopHeaderThemeColor()
|
||||
})
|
||||
watch(route, (newValue) => {
|
||||
currentRoute.value = route.path
|
||||
// 清理选中的
|
||||
selectedKeys.value = []
|
||||
showThis()
|
||||
if (layoutTagsOpen.value) {
|
||||
const pidKey = getParentKeys(moduleMenu.value, route.path)
|
||||
moduleMenu.value.forEach((item) => {
|
||||
if (pidKey.includes(item.path)) {
|
||||
tagSwitchModule(item.id)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
// 监听是否开启了顶栏颜色
|
||||
watch(layout, (newValue) => {
|
||||
@@ -400,6 +386,21 @@
|
||||
}
|
||||
})
|
||||
}
|
||||
// 菜单展开/关闭的回调
|
||||
const onOpenChange = (keys) => {
|
||||
if (sideUniqueOpen.value) {
|
||||
// 获取最新的
|
||||
const openKey = keys[keys.length - 1]
|
||||
if (keys.length > 1) {
|
||||
// 获取上级
|
||||
openKeys.value = getParentKeys(menu.value, openKey)
|
||||
} else {
|
||||
openKeys.value = Array.of(openKey) // new Array(openKey);
|
||||
}
|
||||
} else {
|
||||
openKeys.value = keys
|
||||
}
|
||||
}
|
||||
// 获取上级keys
|
||||
const getParentKeys = (data, val) => {
|
||||
const traverse = (array, val) => {
|
||||
@@ -422,13 +423,29 @@
|
||||
const showMenu = (route) => {
|
||||
pMenu.value = route
|
||||
if (pMenu.value.children) {
|
||||
nextMenu.value = filterUrl(pMenu.value.children)
|
||||
nextMenu.value = pMenu.value.children
|
||||
}
|
||||
if (!route.children || route.children.length === 0) {
|
||||
layoutSiderDowbleMenu.value = false
|
||||
router.push({ path: route.path })
|
||||
} else {
|
||||
layoutSiderDowbleMenu.value = true
|
||||
if (route.children) {
|
||||
let hidden = 0
|
||||
route.children.forEach((item) => {
|
||||
if (item.meta.hidden && item.meta.hidden === true) {
|
||||
hidden++
|
||||
}
|
||||
})
|
||||
// 如果全部都隐藏了,就跳转这个,不展开另一排
|
||||
if (hidden === route.children.length) {
|
||||
layoutSiderDowbleMenu.value = false
|
||||
router.push({ path: route.path })
|
||||
} else {
|
||||
layoutSiderDowbleMenu.value = true
|
||||
}
|
||||
} else {
|
||||
layoutSiderDowbleMenu.value = false
|
||||
}
|
||||
}
|
||||
if (layout.value === 'doublerow') {
|
||||
doublerowSelectedKey.value = [route.path]
|
||||
@@ -454,7 +471,7 @@
|
||||
const menus = moduleMenu.value.filter((item) => item.id === id)[0].children
|
||||
if (menus.length > 0) {
|
||||
// 正儿八百的菜单
|
||||
menu.value = filterUrl(menus)
|
||||
menu.value = menus
|
||||
const firstMenu = traverseChild(menu.value)
|
||||
const path = firstMenu.path
|
||||
// 如果是外链
|
||||
@@ -472,21 +489,22 @@
|
||||
}
|
||||
}
|
||||
// 通过标签切换应用
|
||||
const tagSwitchModule = (id, path) => {
|
||||
const tagSwitchModule = (id) => {
|
||||
// 将此模块的唯一值加入缓存
|
||||
tool.data.set('SNOWY_MENU_MODULE_ID', id)
|
||||
store.setModule(id)
|
||||
const menus = moduleMenu.value.filter((item) => item.id === id)[0].children
|
||||
// 正儿八百的菜单
|
||||
menu.value = filterUrl(menus)
|
||||
menu.value = moduleMenu.value.filter((item) => item.id === id)[0].children
|
||||
}
|
||||
// 遍历子集获取一个path
|
||||
// 遍历获取子集
|
||||
const traverseChild = (menu) => {
|
||||
if (menu[0].children !== undefined) {
|
||||
if (menu[0] && menu[0].children !== undefined) {
|
||||
if (menu[0].children.length > 0) {
|
||||
return traverseChild(menu[0].children)
|
||||
} else {
|
||||
return menu[0]
|
||||
if (menu[0].children[0] && menu[0].children[0].meta.hidden && menu[0].children[0].meta.hidden === true) {
|
||||
return menu[0]
|
||||
} else {
|
||||
return traverseChild(menu[0].children)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return menu[0]
|
||||
|
||||
@@ -86,8 +86,10 @@ router.beforeEach(async (to, from, next) => {
|
||||
next()
|
||||
return false
|
||||
} else {
|
||||
// 这里需要使用 localStorage 保存登录之前要访问的页面
|
||||
tool.data.set('LAST_VIEWS_PATH', to.fullPath)
|
||||
if (token) {
|
||||
// 有token的时候才保存登录之前要访问的页面
|
||||
tool.data.set('LAST_VIEWS_PATH', to.fullPath)
|
||||
}
|
||||
}
|
||||
if (!token) {
|
||||
next({
|
||||
@@ -149,7 +151,28 @@ router.getMenu = () => {
|
||||
const childrenApiMenu = apiMenu[0].children
|
||||
apiMenu[0].children = [...userMenu, ...childrenApiMenu]
|
||||
}
|
||||
return apiMenu
|
||||
return filterUrl(apiMenu)
|
||||
}
|
||||
|
||||
const filterUrl = (map) => {
|
||||
const newMap = []
|
||||
const traverse = (maps) => {
|
||||
maps &&
|
||||
maps.forEach((item) => {
|
||||
item.meta = item.meta ? item.meta : {}
|
||||
// 处理iframe
|
||||
if (item.meta.type === 'iframe') {
|
||||
item.path = `/${item.name}`
|
||||
}
|
||||
// 递归循环
|
||||
if (item.children && item.children.length > 0) {
|
||||
item.children = filterUrl(item.children)
|
||||
}
|
||||
newMap.push(item)
|
||||
})
|
||||
}
|
||||
traverse(map)
|
||||
return newMap
|
||||
}
|
||||
|
||||
// 转换
|
||||
@@ -160,7 +183,7 @@ const filterAsyncRouter = (routerMap) => {
|
||||
// 处理外部链接特殊路由
|
||||
if (item.meta.type === 'iframe') {
|
||||
item.meta.url = item.path
|
||||
item.path = `/i/${item.name}`
|
||||
item.path = `/${item.name}`
|
||||
}
|
||||
// MAP转路由对象
|
||||
const route = {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
*/
|
||||
import config from '@/config'
|
||||
import tool from '@/utils/tool'
|
||||
import routerUtil from '@/utils/routerUtil'
|
||||
|
||||
// 系统路由
|
||||
const routes = [
|
||||
@@ -17,7 +18,7 @@ const routes = [
|
||||
name: 'layout',
|
||||
path: '/',
|
||||
component: () => import('@/layout/index.vue'),
|
||||
redirect: tool.data.get('MENU') ? tool.data.get('MENU')[0].children[0].path : config.DASHBOARD_URL,
|
||||
redirect: tool.data.get('MENU') ? routerUtil.getIndexMenu(tool.data.get('MENU')).path : config.DASHBOARD_URL,
|
||||
children: []
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/**
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
import pinyin from 'js-pinyin'
|
||||
|
||||
// 中文转拼音 传入仅首字母
|
||||
|
||||
@@ -8,30 +8,30 @@
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
import { baseRequest } from '@/utils/request'
|
||||
|
||||
const request = (url, ...arg) => baseRequest(`/sys/spa/${url}`, ...arg)
|
||||
/**
|
||||
* 单页
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2022-09-22 22:33:20
|
||||
*/
|
||||
export default {
|
||||
// 获取菜单分页
|
||||
spaPage(data) {
|
||||
return request('page', data, 'get')
|
||||
},
|
||||
// 提交表单 edit为true时为编辑,默认为新增
|
||||
submitForm(data, edit = false) {
|
||||
return request(edit ? 'edit' : 'add', data)
|
||||
},
|
||||
// 删除菜单
|
||||
spaDelete(data) {
|
||||
return request('delete', data)
|
||||
},
|
||||
// 获取菜单详情
|
||||
spaDetail(data) {
|
||||
return request('detail', data, 'get')
|
||||
// 获取第一个界面
|
||||
const getIndexMenu = (menu) => {
|
||||
let indexMenu = menu[0].children[0]
|
||||
// 如果第一个菜单为目录,接着往下找
|
||||
if (indexMenu.meta.type === 'catalog') {
|
||||
indexMenu = traverseChild(menu)
|
||||
}
|
||||
return indexMenu
|
||||
}
|
||||
// 遍历进行判断,其中处理了被隐藏的
|
||||
const traverseChild = (menu) => {
|
||||
if (menu[0] && menu[0].children !== undefined) {
|
||||
if (menu[0].children.length > 0) {
|
||||
if (menu[0].children[0] && menu[0].children[0].meta.hidden && menu[0].children[0].meta.hidden === true) {
|
||||
return menu[0]
|
||||
} else {
|
||||
return traverseChild(menu[0].children)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return menu[0]
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
getIndexMenu
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import router from '@/router'
|
||||
import tool from '@/utils/tool'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { useGlobalStore } from '@/store'
|
||||
import routerUtil from '@/utils/routerUtil'
|
||||
|
||||
export const afterLogin = async (loginToken) => {
|
||||
tool.data.set('TOKEN', loginToken)
|
||||
@@ -16,7 +17,7 @@ export const afterLogin = async (loginToken) => {
|
||||
|
||||
// 获取用户的菜单
|
||||
const menu = await userCenterApi.userLoginMenu()
|
||||
let indexMenu = menu[0].children[0].path
|
||||
let indexMenu = routerUtil.getIndexMenu(menu).path
|
||||
tool.data.set('MENU', menu)
|
||||
// 重置系统默认应用
|
||||
tool.data.set('SNOWY_MENU_MODULE_ID', menu[0].id)
|
||||
@@ -36,7 +37,8 @@ export const afterLogin = async (loginToken) => {
|
||||
}
|
||||
})
|
||||
if (routerTag === 0) {
|
||||
indexMenu = menu[0].children[0].path
|
||||
// 取首页
|
||||
indexMenu = routerUtil.getIndexMenu(menu).path
|
||||
}
|
||||
}
|
||||
dictApi.dictTree().then((data) => {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
:destroy-on-close="true"
|
||||
@close="onClose"
|
||||
>
|
||||
<a-alert class="mb-3" message="温馨提示:排序第一为首页!若有多个模块根据授权可见情况而变化。" type="warning" />
|
||||
<a-form ref="formRef" :model="formData" :rules="formRules" layout="vertical">
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
@@ -105,6 +106,15 @@
|
||||
<a-button type="primary" @click="iconSelector.showIconModal(formData.icon)">选择</a-button>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="是否可见:" name="visible">
|
||||
<a-radio-group
|
||||
v-model:value="formData.visible"
|
||||
button-style="solid"
|
||||
:options="visibleOptions"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="排序:" name="sortCode">
|
||||
<a-input-number style="width: 100%" v-model:value="formData.sortCode" :max="100" />
|
||||
@@ -121,17 +131,17 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { required, rules } from '@/utils/formRules'
|
||||
import { required } from '@/utils/formRules'
|
||||
import SnowflakeId from 'snowflake-id'
|
||||
import tool from '@/utils/tool'
|
||||
import menuApi from '@/api/sys/resource/menuApi'
|
||||
import IconSelector from '@/components/Selector/iconSelector.vue'
|
||||
// 默认是关闭状态
|
||||
let visible = $ref(false)
|
||||
const visible = ref(false)
|
||||
const emit = defineEmits({ successful: null })
|
||||
const formRef = ref()
|
||||
const treeData = ref([])
|
||||
let iconSelector = ref()
|
||||
const iconSelector = ref()
|
||||
// 表单数据,也就是默认给一些数据
|
||||
const formData = ref({})
|
||||
// 默认展开的节点(顶级)
|
||||
@@ -142,13 +152,20 @@
|
||||
// 打开抽屉
|
||||
const onOpen = (record, module) => {
|
||||
moduleId.value = module
|
||||
visible = true
|
||||
formData.value = {
|
||||
menuType: 'MENU',
|
||||
sortCode: 99
|
||||
}
|
||||
visible.value = true
|
||||
if (record) {
|
||||
formData.value = Object.assign({}, record)
|
||||
formData.value = record
|
||||
// 因为版本升级后该字段无参数,所以默认为可见
|
||||
if (!record.visible) {
|
||||
formData.value.visible = 'true'
|
||||
}
|
||||
} else {
|
||||
formData.value = {
|
||||
menuType: 'MENU',
|
||||
visible: 'true',
|
||||
sortCode: 99
|
||||
}
|
||||
formData.value = Object.assign(formData.value, record)
|
||||
}
|
||||
// 获取菜单树并加入顶级
|
||||
const treeParam = {
|
||||
@@ -168,7 +185,7 @@
|
||||
// 关闭抽屉
|
||||
const onClose = () => {
|
||||
formRef.value.resetFields()
|
||||
visible = false
|
||||
visible.value = false
|
||||
}
|
||||
// 选择上级加载模块的选择框
|
||||
const parentChange = (value) => {
|
||||
@@ -197,10 +214,21 @@
|
||||
path: [required('请输入路由地址')],
|
||||
name: [required('请输入组件中name属性')],
|
||||
module: [required('请选择模块')],
|
||||
component: [required('请输入组件地址')]
|
||||
component: [required('请输入组件地址')],
|
||||
visible: [required('请选择是否可见')]
|
||||
}
|
||||
|
||||
const categoryOptions = tool.dictList('MENU_TYPE')
|
||||
const visibleOptions = [
|
||||
{
|
||||
label: '显示',
|
||||
value: 'true'
|
||||
},
|
||||
{
|
||||
label: '隐藏',
|
||||
value: 'false'
|
||||
}
|
||||
]
|
||||
// 验证并提交数据
|
||||
const onSubmit = () => {
|
||||
formRef.value
|
||||
|
||||
@@ -1,189 +0,0 @@
|
||||
<template>
|
||||
<xn-form-container
|
||||
:title="formData.id ? '编辑单页' : '增加单页'"
|
||||
:width="700"
|
||||
:visible="visible"
|
||||
:destroy-on-close="true"
|
||||
@close="onClose"
|
||||
>
|
||||
<a-form ref="formRef" :model="formData" :rules="formRules" layout="vertical">
|
||||
<a-alert style="margin-bottom: 10px" message="温馨提示:排序第一条为首页页面!" type="warning" closable />
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="单页名称:" name="title">
|
||||
<a-input v-model:value="formData.title" placeholder="请输入单页名称" allow-clear />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="单页类型:" name="menuType">
|
||||
<a-radio-group
|
||||
v-model:value="formData.menuType"
|
||||
button-style="solid"
|
||||
:options="categoryOptions"
|
||||
option-type="button"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item name="path">
|
||||
<template #label>
|
||||
<a-tooltip>
|
||||
<template #title>
|
||||
类型为内外链条时,输入https开头的链接即可(例:https://www.xiaonuo.vip),正常路由前面必须有反斜杠!
|
||||
</template>
|
||||
<question-circle-outlined />
|
||||
</a-tooltip>
|
||||
  {{ formData.menuType === 'MENU' ? '路由地址' : 'https链接地址' }}:
|
||||
</template>
|
||||
<a-input v-model:value="formData.path" placeholder="请输入路由地址" allow-clear />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12" v-if="formData.menuType === 'MENU'">
|
||||
<a-form-item name="component">
|
||||
<template #label>
|
||||
<a-tooltip>
|
||||
<template #title> 按规范可设置为代码组件文件夹名称,注:首字母无反斜杠哦! </template>
|
||||
<question-circle-outlined />
|
||||
</a-tooltip>
|
||||
  组件地址:
|
||||
</template>
|
||||
<a-input
|
||||
v-model:value="formData.component"
|
||||
addon-before="src/views/"
|
||||
placeholder="请输入组件地址"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12" v-if="formData.menuType === 'MENU'">
|
||||
<a-form-item name="name">
|
||||
<template #label>
|
||||
<a-tooltip>
|
||||
<template #title> 按规范可设置为代码组件文件夹名称,注:首字母无反斜杠哦! </template>
|
||||
<question-circle-outlined />
|
||||
</a-tooltip>
|
||||
  别名:
|
||||
</template>
|
||||
<a-input
|
||||
v-model:value="formData.name"
|
||||
addon-before="setup name="
|
||||
placeholder="请输入组件组件中name属性"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="图标:" name="icon">
|
||||
<a-input
|
||||
v-model:value="formData.icon"
|
||||
style="width: calc(100% - 70px)"
|
||||
placeholder="请选择图标"
|
||||
allow-clear
|
||||
/>
|
||||
<a-button type="primary" @click="iconSelector.showIconModal(formData.icon)">选择</a-button>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="排序:" name="sortCode">
|
||||
<a-input-number style="width: 100%" v-model:value="formData.sortCode" :max="100" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
<template #footer>
|
||||
<a-button style="margin-right: 8px" @click="onClose">关闭</a-button>
|
||||
<a-button type="primary" :loading="submitLoading" @click="onSubmit">保存</a-button>
|
||||
</template>
|
||||
<Icon-selector ref="iconSelector" @iconCallBack="iconCallBack" />
|
||||
</xn-form-container>
|
||||
</template>
|
||||
|
||||
<script setup name="spaForm">
|
||||
import { required } from '@/utils/formRules'
|
||||
import IconSelector from '@/components/Selector/iconSelector.vue'
|
||||
import spaApi from '@/api/sys/resource/spaApi'
|
||||
import tool from '@/utils/tool'
|
||||
// 默认是关闭状态
|
||||
let visible = $ref(false)
|
||||
const emit = defineEmits({ successful: null })
|
||||
const formRef = ref()
|
||||
const formData = ref({})
|
||||
const submitLoading = ref(false)
|
||||
const iconSelector = ref()
|
||||
// 打开抽屉
|
||||
const onOpen = (record) => {
|
||||
visible = true
|
||||
formData.value = {
|
||||
menuType: 'MENU',
|
||||
sortCode: 99
|
||||
}
|
||||
if (record) {
|
||||
formData.value = Object.assign({}, record)
|
||||
}
|
||||
}
|
||||
// 关闭抽屉
|
||||
const onClose = () => {
|
||||
formRef.value.resetFields()
|
||||
visible = false
|
||||
}
|
||||
|
||||
// 默认要校验的
|
||||
const formRules = {
|
||||
title: [required('请输入菜单名称')],
|
||||
menuType: [required('请选择菜单类型')],
|
||||
path: [required('请输入路由地址')],
|
||||
name: [required('请输入组件中name属性')],
|
||||
module: [required('请选择模块')],
|
||||
component: [required('请输入组件地址')]
|
||||
}
|
||||
|
||||
// 图标选择器回调
|
||||
const iconCallBack = (value) => {
|
||||
formData.value.icon = value
|
||||
}
|
||||
let categoryOptions = tool
|
||||
.dictList('MENU_TYPE')
|
||||
.filter((item) => {
|
||||
// 排除
|
||||
if (item.value !== 'CATALOG') {
|
||||
return item
|
||||
}
|
||||
})
|
||||
.map((item) => {
|
||||
return {
|
||||
value: item['value'],
|
||||
label: item['label'] + '页'
|
||||
}
|
||||
})
|
||||
|
||||
// 验证并提交数据
|
||||
const onSubmit = () => {
|
||||
formRef.value.validate().then(() => {
|
||||
const param = parameterChanges(formData.value)
|
||||
submitLoading.value = true
|
||||
spaApi
|
||||
.submitForm(param, param.id)
|
||||
.then(() => {
|
||||
visible = false
|
||||
emit('successful')
|
||||
})
|
||||
.finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
})
|
||||
}
|
||||
const parameterChanges = (data) => {
|
||||
if (!data.component) {
|
||||
return data
|
||||
}
|
||||
// 如果用户输入的组件path路径
|
||||
if (data.component.slice(0, 1) === '/') {
|
||||
data.component = data.component.slice(1)
|
||||
}
|
||||
return data
|
||||
}
|
||||
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||||
defineExpose({
|
||||
onOpen
|
||||
})
|
||||
</script>
|
||||
@@ -1,188 +0,0 @@
|
||||
<template>
|
||||
<a-card :bordered="false" :body-style="{ 'padding-bottom': '0px' }" class="mb-2">
|
||||
<a-form ref="formRef" name="advanced_search" :model="searchFormState" class="ant-advanced-search-form">
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="8">
|
||||
<a-form-item label="名称关键词" name="searchKey">
|
||||
<a-input v-model:value="searchFormState.searchKey" placeholder="请输入单页名称关键词" allow-clear />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="类型" name="menuType">
|
||||
<a-select
|
||||
v-model:value="searchFormState.menuType"
|
||||
:options="categoryOptions"
|
||||
placeholder="请选择类型"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-button type="primary" html-type="submit" @click="table.refresh()">查询</a-button>
|
||||
<a-button style="margin: 0 8px" @click="reset">重置</a-button>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</a-card>
|
||||
<a-card :bordered="false">
|
||||
<s-table
|
||||
ref="table"
|
||||
:columns="columns"
|
||||
:data="loadData"
|
||||
:alert="options.alert.show"
|
||||
bordered
|
||||
:row-key="(record) => record.id"
|
||||
:tool-config="toolConfig"
|
||||
:row-selection="options.rowSelection"
|
||||
>
|
||||
<template #operator class="table-operator">
|
||||
<a-space>
|
||||
<a-button type="primary" @click="form.onOpen()">
|
||||
<template #icon><plus-outlined /></template>
|
||||
新增单页
|
||||
</a-button>
|
||||
<xn-batch-delete :selectedRowKeys="selectedRowKeys" @batchDelete="deleteBatchSpa" />
|
||||
</a-space>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'icon'">
|
||||
<component :is="record.icon" />
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'menuType'">
|
||||
<a-tag v-if="record.menuType === 'MENU'" color="blue">
|
||||
{{ $TOOL.dictTypeData('MENU_TYPE', record.menuType) }}页
|
||||
</a-tag>
|
||||
<a-tag v-if="record.menuType === 'IFRAME'" color="purple">
|
||||
{{ $TOOL.dictTypeData('MENU_TYPE', record.menuType) }}页
|
||||
</a-tag>
|
||||
<a-tag v-if="record.menuType === 'LINK'" color="orange">
|
||||
{{ $TOOL.dictTypeData('MENU_TYPE', record.menuType) }}页
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'action'">
|
||||
<a-space>
|
||||
<a @click="form.onOpen(record)">编辑</a>
|
||||
<a-divider type="vertical" />
|
||||
<a-popconfirm title="确定要删除此单页吗?" @confirm="removeSpa(record)">
|
||||
<a-button type="link" danger size="small">删除</a-button>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</s-table>
|
||||
</a-card>
|
||||
<Form ref="form" @successful="table.refresh(true)" />
|
||||
</template>
|
||||
|
||||
<script setup name="sysSpa">
|
||||
import spaApi from '@/api/sys/resource/spaApi'
|
||||
import tool from '@/utils/tool'
|
||||
import Form from './form.vue'
|
||||
let searchFormState = reactive({})
|
||||
const formRef = ref()
|
||||
const table = ref(null)
|
||||
let form = ref()
|
||||
const toolConfig = { refresh: true, height: true, columnSetting: false, striped: false }
|
||||
const columns = [
|
||||
{
|
||||
title: '单页名称',
|
||||
dataIndex: 'title',
|
||||
width: 260
|
||||
},
|
||||
{
|
||||
title: '图标',
|
||||
dataIndex: 'icon'
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
dataIndex: 'menuType'
|
||||
},
|
||||
{
|
||||
title: '路由地址',
|
||||
dataIndex: 'path',
|
||||
ellipsis: true,
|
||||
width: 150
|
||||
},
|
||||
{
|
||||
title: '组件',
|
||||
dataIndex: 'component',
|
||||
ellipsis: true,
|
||||
width: 150
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
dataIndex: 'sortCode',
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
ellipsis: true,
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
width: '180px',
|
||||
align: 'center',
|
||||
scopedSlots: { customRender: 'action' }
|
||||
}
|
||||
]
|
||||
let selectedRowKeys = ref([])
|
||||
// 列表选择配置
|
||||
const options = {
|
||||
alert: {
|
||||
show: false,
|
||||
clear: () => {
|
||||
selectedRowKeys = ref([])
|
||||
}
|
||||
},
|
||||
rowSelection: {
|
||||
onChange: (selectedRowKey, selectedRows) => {
|
||||
selectedRowKeys.value = selectedRowKey
|
||||
}
|
||||
}
|
||||
}
|
||||
const categoryOptions = tool
|
||||
.dictList('MENU_TYPE')
|
||||
.filter((item) => {
|
||||
// 排除
|
||||
if (item.value !== 'CATALOG') {
|
||||
return item
|
||||
}
|
||||
})
|
||||
.map((item) => {
|
||||
return {
|
||||
value: item['value'],
|
||||
label: item['label'] + '页'
|
||||
}
|
||||
})
|
||||
// 列表数据
|
||||
const loadData = (parameter) => {
|
||||
return spaApi.spaPage(Object.assign(parameter, searchFormState)).then((res) => {
|
||||
return res
|
||||
})
|
||||
}
|
||||
// 重置
|
||||
const reset = () => {
|
||||
formRef.value.resetFields()
|
||||
table.value.refresh(true)
|
||||
}
|
||||
// 删除
|
||||
const removeSpa = (record) => {
|
||||
let params = [
|
||||
{
|
||||
id: record.id
|
||||
}
|
||||
]
|
||||
spaApi.spaDelete(params).then(() => {
|
||||
table.value.refresh(true)
|
||||
})
|
||||
}
|
||||
// 批量删除
|
||||
const deleteBatchSpa = (params) => {
|
||||
spaApi.spaDelete(params).then(() => {
|
||||
table.value.clearRefreshSelected()
|
||||
})
|
||||
}
|
||||
</script>
|
||||
@@ -84,7 +84,7 @@
|
||||
key: 'title',
|
||||
title: '菜单',
|
||||
dataIndex: 'title',
|
||||
width: 200
|
||||
width: 240
|
||||
},
|
||||
{
|
||||
key: 'button',
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
key: 'title',
|
||||
title: '菜单',
|
||||
dataIndex: 'title',
|
||||
width: 200
|
||||
width: 240
|
||||
},
|
||||
{
|
||||
key: 'button',
|
||||
|
||||
@@ -269,7 +269,7 @@ public class MobileMenuServiceImpl extends ServiceImpl<MobileMenuMapper, MobileM
|
||||
|
||||
@Override
|
||||
public List<Tree<String>> loginMobileMenuTree(List<String> menuIdList) {
|
||||
// 获取所有的菜单和模块以及单页面列表,并按分类和排序码排序
|
||||
// 获取所有的菜单和模块列表,并按分类和排序码排序
|
||||
List<MobileMenu> allModuleAndMenuAndSpaList = this.list(new LambdaQueryWrapper<MobileMenu>()
|
||||
.in(MobileMenu::getCategory, MobileResourceCategoryEnum.MODULE.getValue(), MobileResourceCategoryEnum.MENU.getValue())
|
||||
.orderByAsc(CollectionUtil.newArrayList(MobileMenu::getCategory, MobileMenu::getSortCode)));
|
||||
|
||||
@@ -30,10 +30,7 @@ public enum SysBuildInEnum {
|
||||
BUILD_IN_ROLE_CODE("superAdmin", "超管"),
|
||||
|
||||
/** 系统内置模块编码 */
|
||||
BUILD_IN_MODULE_CODE("system", "系统内置"),
|
||||
|
||||
/** 系统内置单页面编码 */
|
||||
BUILD_IN_SPA_CODE("system", "系统内置");
|
||||
BUILD_IN_MODULE_CODE("system", "系统内置");
|
||||
|
||||
private final String value;
|
||||
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
*
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
*
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
package vip.xiaonuo.sys.modular.resource.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import vip.xiaonuo.common.annotation.CommonLog;
|
||||
import vip.xiaonuo.common.pojo.CommonResult;
|
||||
import vip.xiaonuo.common.pojo.CommonValidList;
|
||||
import vip.xiaonuo.sys.modular.resource.entity.SysSpa;
|
||||
import vip.xiaonuo.sys.modular.resource.param.spa.SysSpaAddParam;
|
||||
import vip.xiaonuo.sys.modular.resource.param.spa.SysSpaEditParam;
|
||||
import vip.xiaonuo.sys.modular.resource.param.spa.SysSpaIdParam;
|
||||
import vip.xiaonuo.sys.modular.resource.param.spa.SysSpaPageParam;
|
||||
import vip.xiaonuo.sys.modular.resource.service.SysSpaService;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
|
||||
/**
|
||||
* 单页面控制器
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2022/6/27 14:14
|
||||
**/
|
||||
@Api(tags = "单页面控制器")
|
||||
@ApiSupport(author = "SNOWY_TEAM", order = 7)
|
||||
@RestController
|
||||
@Validated
|
||||
public class SysSpaController {
|
||||
|
||||
@Resource
|
||||
private SysSpaService sysSpaService;
|
||||
|
||||
/**
|
||||
* 获取单页面分页
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2022/4/24 20:00
|
||||
*/
|
||||
@ApiOperationSupport(order = 1)
|
||||
@ApiOperation("获取单页面分页")
|
||||
@GetMapping("/sys/spa/page")
|
||||
public CommonResult<Page<SysSpa>> page(SysSpaPageParam sysSpaPageParam) {
|
||||
return CommonResult.data(sysSpaService.page(sysSpaPageParam));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加单页面
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2022/4/24 20:47
|
||||
*/
|
||||
@ApiOperationSupport(order = 2)
|
||||
@ApiOperation("添加单页面")
|
||||
@CommonLog("添加单页面")
|
||||
@PostMapping("/sys/spa/add")
|
||||
public CommonResult<String> add(@RequestBody @Valid SysSpaAddParam sysSpaAddParam) {
|
||||
sysSpaService.add(sysSpaAddParam);
|
||||
return CommonResult.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑单页面
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2022/4/24 20:47
|
||||
*/
|
||||
@ApiOperationSupport(order = 3)
|
||||
@ApiOperation("编辑单页面")
|
||||
@CommonLog("编辑单页面")
|
||||
@PostMapping("/sys/spa/edit")
|
||||
public CommonResult<String> edit(@RequestBody @Valid SysSpaEditParam sysSpaEditParam) {
|
||||
sysSpaService.edit(sysSpaEditParam);
|
||||
return CommonResult.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除单页面
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2022/4/24 20:00
|
||||
*/
|
||||
@ApiOperationSupport(order = 4)
|
||||
@ApiOperation("删除单页面")
|
||||
@CommonLog("删除单页面")
|
||||
@PostMapping("/sys/spa/delete")
|
||||
public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
|
||||
CommonValidList<SysSpaIdParam> sysSpaIdParamList) {
|
||||
sysSpaService.delete(sysSpaIdParamList);
|
||||
return CommonResult.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单页面详情
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2022/4/24 20:00
|
||||
*/
|
||||
@ApiOperationSupport(order = 5)
|
||||
@ApiOperation("获取单页面详情")
|
||||
@GetMapping("/sys/spa/detail")
|
||||
public CommonResult<SysSpa> detail(@Valid SysSpaIdParam sysSpaIdParam) {
|
||||
return CommonResult.data(sysSpaService.detail(sysSpaIdParam));
|
||||
}
|
||||
}
|
||||
@@ -82,12 +82,16 @@ public class SysMenu extends CommonEntity {
|
||||
@ApiModelProperty(value = "颜色", position = 12)
|
||||
private String color;
|
||||
|
||||
/** 是否可见 */
|
||||
@ApiModelProperty(value = "是否可见", position = 13)
|
||||
private String visible;
|
||||
|
||||
/** 排序码 */
|
||||
@ApiModelProperty(value = "排序码", position = 13)
|
||||
@ApiModelProperty(value = "排序码", position = 14)
|
||||
private Integer sortCode;
|
||||
|
||||
/** 扩展信息 */
|
||||
@ApiModelProperty(value = "扩展信息", position = 14)
|
||||
@ApiModelProperty(value = "扩展信息", position = 15)
|
||||
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
|
||||
private String extJson;
|
||||
}
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
*
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
*
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
package vip.xiaonuo.sys.modular.resource.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import vip.xiaonuo.common.pojo.CommonEntity;
|
||||
|
||||
/**
|
||||
* 单页面实体
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2022/7/27 18:27
|
||||
**/
|
||||
@Getter
|
||||
@Setter
|
||||
@TableName("SYS_RESOURCE")
|
||||
public class SysSpa extends CommonEntity {
|
||||
|
||||
/** id */
|
||||
@ApiModelProperty(value = "id", position = 1)
|
||||
private String id;
|
||||
|
||||
/** 标题 */
|
||||
@ApiModelProperty(value = "标题", position = 2)
|
||||
private String title;
|
||||
|
||||
/** 别名 */
|
||||
@ApiModelProperty(value = "别名", position = 3)
|
||||
private String name;
|
||||
|
||||
/** 编码 */
|
||||
@ApiModelProperty(value = "编码", position = 4)
|
||||
private String code;
|
||||
|
||||
/** 分类 */
|
||||
@ApiModelProperty(value = "分类", position = 5)
|
||||
private String category;
|
||||
|
||||
/** 菜单类型 */
|
||||
@ApiModelProperty(value = "菜单类型", position = 6)
|
||||
private String menuType;
|
||||
|
||||
/** 路径 */
|
||||
@ApiModelProperty(value = "路径", position = 7)
|
||||
private String path;
|
||||
|
||||
/** 组件 */
|
||||
@ApiModelProperty(value = "组件", position = 8)
|
||||
private String component;
|
||||
|
||||
/** 图标 */
|
||||
@ApiModelProperty(value = "图标", position = 9)
|
||||
private String icon;
|
||||
|
||||
/** 排序码 */
|
||||
@ApiModelProperty(value = "排序码", position = 10)
|
||||
private Integer sortCode;
|
||||
|
||||
/** 扩展信息 */
|
||||
@ApiModelProperty(value = "扩展信息", position = 11)
|
||||
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
|
||||
private String extJson;
|
||||
}
|
||||
@@ -27,9 +27,6 @@ public enum SysResourceCategoryEnum {
|
||||
/** 模块 */
|
||||
MODULE("MODULE"),
|
||||
|
||||
/** 单页面 */
|
||||
SPA("SPA"),
|
||||
|
||||
/** 菜单 */
|
||||
MENU("MENU"),
|
||||
|
||||
@@ -43,7 +40,7 @@ public enum SysResourceCategoryEnum {
|
||||
}
|
||||
|
||||
public static void validate(String value) {
|
||||
boolean flag = MODULE.getValue().equals(value) || SPA.getValue().equals(value) || MENU.getValue().equals(value)
|
||||
boolean flag = MODULE.getValue().equals(value) || MENU.getValue().equals(value)
|
||||
|| BUTTON.getValue().equals(value);
|
||||
if(!flag) {
|
||||
throw new CommonException("不支持的资源分类:{}", value);
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
*
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
*
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
package vip.xiaonuo.sys.modular.resource.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import vip.xiaonuo.sys.modular.resource.entity.SysSpa;
|
||||
|
||||
/**
|
||||
* 单页面Mapper接口
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2022/4/21 18:37
|
||||
**/
|
||||
public interface SysSpaMapper extends BaseMapper<SysSpa> {
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="vip.xiaonuo.sys.modular.resource.mapper.SysSpaMapper">
|
||||
|
||||
|
||||
</mapper>
|
||||
@@ -71,7 +71,11 @@ public class SysMenuAddParam {
|
||||
@ApiModelProperty(value = "图标", position = 9)
|
||||
private String icon;
|
||||
|
||||
/** 是否可见 */
|
||||
@ApiModelProperty(value = "是否可见", position = 10)
|
||||
private String visible;
|
||||
|
||||
/** 扩展信息 */
|
||||
@ApiModelProperty(value = "扩展信息", position = 10)
|
||||
@ApiModelProperty(value = "扩展信息", position = 11)
|
||||
private String extJson;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,11 @@ public class SysMenuEditParam {
|
||||
@ApiModelProperty(value = "图标", position = 10)
|
||||
private String icon;
|
||||
|
||||
/** 是否可见 */
|
||||
@ApiModelProperty(value = "是否可见", position = 11)
|
||||
private String visible;
|
||||
|
||||
/** 扩展信息 */
|
||||
@ApiModelProperty(value = "扩展信息", position = 11)
|
||||
@ApiModelProperty(value = "扩展信息", position = 12)
|
||||
private String extJson;
|
||||
}
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
*
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
*
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
package vip.xiaonuo.sys.modular.resource.param.spa;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 单页面添加参数
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2022/7/27 18:40
|
||||
**/
|
||||
@Getter
|
||||
@Setter
|
||||
public class SysSpaAddParam {
|
||||
|
||||
/** 标题 */
|
||||
@ApiModelProperty(value = "标题", required = true, position = 1)
|
||||
@NotBlank(message = "title不能为空")
|
||||
private String title;
|
||||
|
||||
/** 菜单类型 */
|
||||
@ApiModelProperty(value = "菜单类型", required = true, position = 2)
|
||||
@NotBlank(message = "menuType不能为空")
|
||||
private String menuType;
|
||||
|
||||
/** 别名 */
|
||||
@ApiModelProperty(value = "别名", required = true, position = 3)
|
||||
private String name;
|
||||
|
||||
/** 路径 */
|
||||
@ApiModelProperty(value = "路径", required = true, position = 4)
|
||||
@NotBlank(message = "path不能为空")
|
||||
private String path;
|
||||
|
||||
/** 组件 */
|
||||
@ApiModelProperty(value = "组件", required = true, position = 5)
|
||||
private String component;
|
||||
|
||||
/** 图标 */
|
||||
@ApiModelProperty(value = "图标", required = true, position = 6)
|
||||
private String icon;
|
||||
|
||||
/** 排序码 */
|
||||
@ApiModelProperty(value = "排序码", required = true, position = 7)
|
||||
@NotNull(message = "sortCode不能为空")
|
||||
private Integer sortCode;
|
||||
|
||||
/** 扩展信息 */
|
||||
@ApiModelProperty(value = "扩展信息", position = 8)
|
||||
private String extJson;
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
*
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
*
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
package vip.xiaonuo.sys.modular.resource.param.spa;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 单页面编辑参数
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2022/7/27 18:40
|
||||
**/
|
||||
@Getter
|
||||
@Setter
|
||||
public class SysSpaEditParam {
|
||||
|
||||
/** id */
|
||||
@ApiModelProperty(value = "id", required = true, position = 1)
|
||||
@NotBlank(message = "id不能为空")
|
||||
private String id;
|
||||
|
||||
/** 标题 */
|
||||
@ApiModelProperty(value = "标题", required = true, position = 2)
|
||||
@NotBlank(message = "title不能为空")
|
||||
private String title;
|
||||
|
||||
/** 菜单类型 */
|
||||
@ApiModelProperty(value = "菜单类型", required = true, position = 3)
|
||||
@NotBlank(message = "menuType不能为空")
|
||||
private String menuType;
|
||||
|
||||
/** 别名 */
|
||||
@ApiModelProperty(value = "别名", required = true, position = 4)
|
||||
private String name;
|
||||
|
||||
/** 路径 */
|
||||
@ApiModelProperty(value = "路径", required = true, position = 5)
|
||||
@NotBlank(message = "path不能为空")
|
||||
private String path;
|
||||
|
||||
/** 组件 */
|
||||
@ApiModelProperty(value = "组件", required = true, position = 6)
|
||||
private String component;
|
||||
|
||||
/** 图标 */
|
||||
@ApiModelProperty(value = "图标", required = true, position = 7)
|
||||
private String icon;
|
||||
|
||||
/** 排序码 */
|
||||
@ApiModelProperty(value = "排序码", required = true, position = 8)
|
||||
@NotNull(message = "sortCode不能为空")
|
||||
private Integer sortCode;
|
||||
|
||||
/** 扩展信息 */
|
||||
@ApiModelProperty(value = "扩展信息", position = 9)
|
||||
private String extJson;
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
*
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
*
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
package vip.xiaonuo.sys.modular.resource.param.spa;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 单页面Id参数
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2022/7/27 18:40
|
||||
**/
|
||||
@Getter
|
||||
@Setter
|
||||
public class SysSpaIdParam {
|
||||
|
||||
/** id */
|
||||
@ApiModelProperty(value = "id", required = true)
|
||||
@NotBlank(message = "id不能为空")
|
||||
private String id;
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
*
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
*
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
package vip.xiaonuo.sys.modular.resource.param.spa;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 单页面查询参数
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2022/7/27 18:40
|
||||
**/
|
||||
@Getter
|
||||
@Setter
|
||||
public class SysSpaPageParam {
|
||||
|
||||
/** 当前页 */
|
||||
@ApiModelProperty(value = "当前页码")
|
||||
private Integer current;
|
||||
|
||||
/** 每页条数 */
|
||||
@ApiModelProperty(value = "每页条数")
|
||||
private Integer size;
|
||||
|
||||
/** 排序字段 */
|
||||
@ApiModelProperty(value = "排序字段,字段驼峰名称,如:userName")
|
||||
private String sortField;
|
||||
|
||||
/** 排序方式 */
|
||||
@ApiModelProperty(value = "排序方式,升序:ASCEND;降序:DESCEND")
|
||||
private String sortOrder;
|
||||
|
||||
/** 名称关键词 */
|
||||
@ApiModelProperty(value = "名称关键词")
|
||||
private String searchKey;
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
*
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
*
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
package vip.xiaonuo.sys.modular.resource.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import vip.xiaonuo.sys.modular.resource.entity.SysSpa;
|
||||
import vip.xiaonuo.sys.modular.resource.param.spa.SysSpaAddParam;
|
||||
import vip.xiaonuo.sys.modular.resource.param.spa.SysSpaEditParam;
|
||||
import vip.xiaonuo.sys.modular.resource.param.spa.SysSpaIdParam;
|
||||
import vip.xiaonuo.sys.modular.resource.param.spa.SysSpaPageParam;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 单页面Service接口
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2022/6/27 14:03
|
||||
**/
|
||||
public interface SysSpaService extends IService<SysSpa> {
|
||||
|
||||
/**
|
||||
* 获取单页面分页
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2022/4/24 20:08
|
||||
*/
|
||||
Page<SysSpa> page(SysSpaPageParam sysSpaPageParam);
|
||||
|
||||
/**
|
||||
* 添加单页面
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2022/4/24 20:48
|
||||
*/
|
||||
void add(SysSpaAddParam sysSpaAddParam);
|
||||
|
||||
/**
|
||||
* 编辑单页面
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2022/4/24 21:13
|
||||
*/
|
||||
void edit(SysSpaEditParam sysSpaEditParam);
|
||||
|
||||
/**
|
||||
* 删除单页面
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2022/4/24 21:18
|
||||
*/
|
||||
void delete(List<SysSpaIdParam> sysSpaIdParamList);
|
||||
|
||||
/**
|
||||
* 获取单页面详情
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2022/4/24 21:18
|
||||
*/
|
||||
SysSpa detail(SysSpaIdParam sysSpaIdParam);
|
||||
|
||||
/**
|
||||
* 获取单页面详情
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2022/4/24 21:18
|
||||
*/
|
||||
SysSpa queryEntity(String id);
|
||||
}
|
||||
@@ -239,6 +239,11 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
|
||||
boolean repeatTitle = this.count(new LambdaQueryWrapper<SysMenu>().eq(SysMenu::getParentId, sysMenu.getParentId())
|
||||
.eq(SysMenu::getCategory, SysResourceCategoryEnum.MENU.getValue()).eq(SysMenu::getTitle, sysMenu.getTitle())
|
||||
.ne(SysMenu::getId, sysMenu.getId())) > 0;
|
||||
// 不管是哪个改为菜单,设置组件为空
|
||||
if(sysMenuEditParam.getMenuType().equals(SysResourceMenuTypeEnum.MENU.getValue())) {
|
||||
sysMenuEditParam.setComponent(null);
|
||||
sysMenuEditParam.setName(null);
|
||||
}
|
||||
if(repeatTitle) {
|
||||
throw new CommonException("存在重复的菜单,名称为:{}", sysMenu.getTitle());
|
||||
}
|
||||
|
||||
@@ -1,178 +0,0 @@
|
||||
/*
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
*
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
*
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
package vip.xiaonuo.sys.modular.resource.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollStreamUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import vip.xiaonuo.common.enums.CommonSortOrderEnum;
|
||||
import vip.xiaonuo.common.exception.CommonException;
|
||||
import vip.xiaonuo.common.listener.CommonDataChangeEventCenter;
|
||||
import vip.xiaonuo.common.page.CommonPageRequest;
|
||||
import vip.xiaonuo.sys.core.enums.SysBuildInEnum;
|
||||
import vip.xiaonuo.sys.core.enums.SysDataTypeEnum;
|
||||
import vip.xiaonuo.sys.modular.resource.entity.SysSpa;
|
||||
import vip.xiaonuo.sys.modular.resource.enums.SysResourceCategoryEnum;
|
||||
import vip.xiaonuo.sys.modular.resource.enums.SysResourceMenuTypeEnum;
|
||||
import vip.xiaonuo.sys.modular.resource.mapper.SysSpaMapper;
|
||||
import vip.xiaonuo.sys.modular.resource.param.spa.SysSpaAddParam;
|
||||
import vip.xiaonuo.sys.modular.resource.param.spa.SysSpaEditParam;
|
||||
import vip.xiaonuo.sys.modular.resource.param.spa.SysSpaIdParam;
|
||||
import vip.xiaonuo.sys.modular.resource.param.spa.SysSpaPageParam;
|
||||
import vip.xiaonuo.sys.modular.resource.service.SysSpaService;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 单页面Service接口实现类
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2022/6/27 14:25
|
||||
**/
|
||||
@Service
|
||||
public class SysSpaServiceImpl extends ServiceImpl<SysSpaMapper, SysSpa> implements SysSpaService {
|
||||
|
||||
@Override
|
||||
public Page<SysSpa> page(SysSpaPageParam sysSpaPageParam) {
|
||||
QueryWrapper<SysSpa> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.lambda().eq(SysSpa::getCategory, SysResourceCategoryEnum.SPA.getValue());
|
||||
if(ObjectUtil.isNotEmpty(sysSpaPageParam.getSearchKey())) {
|
||||
queryWrapper.lambda().like(SysSpa::getTitle, sysSpaPageParam.getSearchKey());
|
||||
}
|
||||
if(ObjectUtil.isAllNotEmpty(sysSpaPageParam.getSortField(), sysSpaPageParam.getSortOrder())) {
|
||||
CommonSortOrderEnum.validate(sysSpaPageParam.getSortOrder());
|
||||
queryWrapper.orderBy(true, sysSpaPageParam.getSortOrder().equals(CommonSortOrderEnum.ASC.getValue()),
|
||||
StrUtil.toUnderlineCase(sysSpaPageParam.getSortField()));
|
||||
} else {
|
||||
queryWrapper.lambda().orderByAsc(SysSpa::getSortCode);
|
||||
}
|
||||
return this.page(CommonPageRequest.defaultPage(), queryWrapper);
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public void add(SysSpaAddParam sysSpaAddParam) {
|
||||
checkParam(sysSpaAddParam);
|
||||
SysSpa sysSpa = BeanUtil.toBean(sysSpaAddParam, SysSpa.class);
|
||||
boolean repeatTitle = this.count(new LambdaQueryWrapper<SysSpa>().eq(SysSpa::getCategory,
|
||||
SysResourceCategoryEnum.SPA.getValue()).eq(SysSpa::getTitle, sysSpa.getTitle())) > 0;
|
||||
if(repeatTitle) {
|
||||
throw new CommonException("存在重复的单页面,名称为:{}", sysSpa.getTitle());
|
||||
}
|
||||
sysSpa.setCode(RandomUtil.randomString(10));
|
||||
sysSpa.setCategory(SysResourceCategoryEnum.SPA.getValue());
|
||||
this.save(sysSpa);
|
||||
|
||||
// 发布增加事件
|
||||
CommonDataChangeEventCenter.doAddWithData(SysDataTypeEnum.RESOURCE.getValue(), JSONUtil.createArray().put(sysSpa));
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private void checkParam(SysSpaAddParam sysSpaAddParam) {
|
||||
SysResourceMenuTypeEnum.validate(sysSpaAddParam.getMenuType());
|
||||
if(SysResourceMenuTypeEnum.MENU.getValue().equals(sysSpaAddParam.getMenuType())) {
|
||||
if(ObjectUtil.isEmpty(sysSpaAddParam.getName())) {
|
||||
throw new CommonException("name不能为空");
|
||||
}
|
||||
if(ObjectUtil.isEmpty(sysSpaAddParam.getComponent())) {
|
||||
throw new CommonException("component不能为空");
|
||||
}
|
||||
} else if(SysResourceMenuTypeEnum.IFRAME.getValue().equals(sysSpaAddParam.getMenuType()) ||
|
||||
SysResourceMenuTypeEnum.LINK.getValue().equals(sysSpaAddParam.getMenuType())) {
|
||||
sysSpaAddParam.setName(RandomUtil.randomNumbers(10));
|
||||
sysSpaAddParam.setComponent(null);
|
||||
} else {
|
||||
sysSpaAddParam.setName(null);
|
||||
sysSpaAddParam.setComponent(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public void edit(SysSpaEditParam sysSpaEditParam) {
|
||||
SysSpa sysSpa = this.queryEntity(sysSpaEditParam.getId());
|
||||
checkParam(sysSpaEditParam);
|
||||
BeanUtil.copyProperties(sysSpaEditParam, sysSpa);
|
||||
boolean repeatTitle = this.count(new LambdaQueryWrapper<SysSpa>().eq(SysSpa::getCategory,
|
||||
SysResourceCategoryEnum.SPA.getValue()).eq(SysSpa::getTitle, sysSpa.getTitle())
|
||||
.ne(SysSpa::getId, sysSpa.getId())) > 0;
|
||||
if(repeatTitle) {
|
||||
throw new CommonException("存在重复的单页面,名称为:{}", sysSpa.getTitle());
|
||||
}
|
||||
this.updateById(sysSpa);
|
||||
|
||||
// 发布更新事件
|
||||
CommonDataChangeEventCenter.doUpdateWithData(SysDataTypeEnum.RESOURCE.getValue(), JSONUtil.createArray().put(sysSpa));
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private void checkParam(SysSpaEditParam sysSpaEditParam) {
|
||||
SysResourceMenuTypeEnum.validate(sysSpaEditParam.getMenuType());
|
||||
if(SysResourceMenuTypeEnum.MENU.getValue().equals(sysSpaEditParam.getMenuType())) {
|
||||
if(ObjectUtil.isEmpty(sysSpaEditParam.getName())) {
|
||||
throw new CommonException("name不能为空");
|
||||
}
|
||||
if(ObjectUtil.isEmpty(sysSpaEditParam.getComponent())) {
|
||||
throw new CommonException("component不能为空");
|
||||
}
|
||||
} else if(SysResourceMenuTypeEnum.IFRAME.getValue().equals(sysSpaEditParam.getMenuType()) ||
|
||||
SysResourceMenuTypeEnum.LINK.getValue().equals(sysSpaEditParam.getMenuType())) {
|
||||
sysSpaEditParam.setName(RandomUtil.randomNumbers(10));
|
||||
sysSpaEditParam.setComponent(null);
|
||||
} else {
|
||||
sysSpaEditParam.setName(null);
|
||||
sysSpaEditParam.setComponent(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(List<SysSpaIdParam> sysSpaIdParamList) {
|
||||
List<String> sysSpaIdList = CollStreamUtil.toList(sysSpaIdParamList, SysSpaIdParam::getId);
|
||||
if(ObjectUtil.isNotEmpty(sysSpaIdList)) {
|
||||
boolean containsSystemSpa = this.listByIds(sysSpaIdList).stream().map(SysSpa::getCode)
|
||||
.collect(Collectors.toSet()).contains(SysBuildInEnum.BUILD_IN_SPA_CODE.getValue());
|
||||
if(containsSystemSpa) {
|
||||
throw new CommonException("不可删除系统内置单页面");
|
||||
}
|
||||
// 删除
|
||||
this.removeByIds(sysSpaIdList);
|
||||
|
||||
// 发布删除事件
|
||||
CommonDataChangeEventCenter.doDeleteWithDataId(SysDataTypeEnum.RESOURCE.getValue(), sysSpaIdList);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SysSpa detail(SysSpaIdParam sysSpaIdParam) {
|
||||
return this.queryEntity(sysSpaIdParam.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SysSpa queryEntity(String id) {
|
||||
SysSpa sysSpa = this.getById(id);
|
||||
if(ObjectUtil.isEmpty(sysSpa)) {
|
||||
throw new CommonException("单页面不存在,id值为:{}", id);
|
||||
}
|
||||
return sysSpa;
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,7 @@ import vip.xiaonuo.sys.modular.relation.service.SysRelationService;
|
||||
import vip.xiaonuo.sys.modular.resource.entity.SysMenu;
|
||||
import vip.xiaonuo.sys.modular.resource.entity.SysModule;
|
||||
import vip.xiaonuo.sys.modular.resource.enums.SysResourceCategoryEnum;
|
||||
import vip.xiaonuo.sys.modular.resource.enums.SysResourceMenuTypeEnum;
|
||||
import vip.xiaonuo.sys.modular.resource.service.SysMenuService;
|
||||
import vip.xiaonuo.sys.modular.resource.service.SysModuleService;
|
||||
import vip.xiaonuo.sys.modular.role.entity.SysRole;
|
||||
@@ -356,8 +357,11 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
|
||||
new TreeNode<>(sysMenu.getId(), sysMenu.getParentId(),
|
||||
sysMenu.getTitle(), sysMenu.getSortCode())).collect(Collectors.toList());
|
||||
List<Tree<String>> treeList = TreeUtil.build(treeNodeList, "-1");
|
||||
|
||||
sysMenuList.forEach(sysMenu -> {
|
||||
boolean isLeafMenu = this.getChildListById(sysMenuList, sysMenu.getId(), false).size() == 0;
|
||||
// 排除菜单中的目录
|
||||
boolean isLeafMenu = !"0".equals(sysMenu.getId())
|
||||
&& !sysMenu.getMenuType().equals(SysResourceMenuTypeEnum.CATALOG.getValue());
|
||||
if(isLeafMenu) {
|
||||
SysRoleGrantResourceTreeResult.SysRoleGrantResourceMenuResult sysRoleGrantResourceMenuResult =
|
||||
new SysRoleGrantResourceTreeResult.SysRoleGrantResourceMenuResult();
|
||||
|
||||
@@ -108,6 +108,8 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -591,15 +593,13 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
SysRelationCategoryEnum.SYS_ROLE_HAS_RESOURCE.getValue()));
|
||||
}
|
||||
|
||||
// 获取所有的菜单和模块以及单页面列表,并按分类和排序码排序
|
||||
// 获取所有的菜单和模块列表,并按分类和排序码排序
|
||||
List<SysMenu> allModuleAndMenuAndSpaList = sysMenuService.list(new LambdaQueryWrapper<SysMenu>()
|
||||
.in(SysMenu::getCategory, SysResourceCategoryEnum.MODULE.getValue(), SysResourceCategoryEnum.MENU.getValue(),
|
||||
SysResourceCategoryEnum.SPA.getValue()).orderByAsc(CollectionUtil.newArrayList(SysMenu::getCategory,
|
||||
SysMenu::getSortCode)));
|
||||
.in(SysMenu::getCategory, SysResourceCategoryEnum.MODULE.getValue(), SysResourceCategoryEnum.MENU.getValue())
|
||||
.orderByAsc(CollectionUtil.newArrayList(SysMenu::getCategory,SysMenu::getSortCode)));
|
||||
// 全部以菜单承载
|
||||
List<SysMenu> allModuleList = CollectionUtil.newArrayList();
|
||||
List<SysMenu> allMenuList = CollectionUtil.newArrayList();
|
||||
List<SysMenu> allSpaList = CollectionUtil.newArrayList();
|
||||
// 根据类型抽取
|
||||
allModuleAndMenuAndSpaList.forEach(sysMenu -> {
|
||||
boolean isModule = sysMenu.getCategory().equals(SysResourceCategoryEnum.MODULE.getValue());
|
||||
@@ -612,11 +612,6 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
// 抽取所有的菜单列表
|
||||
allMenuList.add(sysMenu);
|
||||
}
|
||||
boolean isSpa = sysMenu.getCategory().equals(SysResourceCategoryEnum.SPA.getValue());
|
||||
if (isSpa) {
|
||||
// 抽取所有的单页面列表
|
||||
allSpaList.add(sysMenu);
|
||||
}
|
||||
});
|
||||
|
||||
// 定义结果
|
||||
@@ -643,8 +638,8 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
if (ObjectUtil.isEmpty(moduleList)) {
|
||||
// 如果系统中无模块(极端情况)
|
||||
if (ObjectUtil.isEmpty(allModuleList)) {
|
||||
// 如果系统中无单页面,则返回空列表
|
||||
if (ObjectUtil.isEmpty(allSpaList)) {
|
||||
// 如果系统中无菜单,则返回空列表
|
||||
if (ObjectUtil.isEmpty(allMenuList)) {
|
||||
return CollectionUtil.newArrayList();
|
||||
} else {
|
||||
// 否则构造一个模块,并添加到拥有模块
|
||||
@@ -667,17 +662,11 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
// 获取第一个模块
|
||||
SysMenu firstModule = moduleList.get(0);
|
||||
|
||||
// 将第一个模块作为所有单页面的所属模块,并添加
|
||||
List<SysMenu> spaList = allSpaList.stream().peek(sysMenu -> {
|
||||
sysMenu.setParentId(firstModule.getId());
|
||||
sysMenu.setModule(firstModule.getId());
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
// 获取第一个单页面的id
|
||||
String firstSpaId = spaList.get(0).getId();
|
||||
|
||||
// 将单页面放入集合
|
||||
resultList.addAll(spaList);
|
||||
// 获取第一个模块下的第一个菜单
|
||||
Optional<SysMenu> sysMenus = menuList.stream()
|
||||
.filter(sysMenu -> sysMenu.getModule().equals(firstModule.getId()))
|
||||
.findFirst()
|
||||
.filter(sysMenu -> !sysMenu.getMenuType().equals(SysResourceMenuTypeEnum.CATALOG.getValue()));
|
||||
|
||||
// 最终处理,构造meta
|
||||
List<JSONObject> resultJsonObjectList = resultList.stream().map(sysMenu -> {
|
||||
@@ -697,22 +686,20 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
JSONObject metaJsonObject = JSONUtil.createObj();
|
||||
metaJsonObject.set("icon", sysMenu.getIcon());
|
||||
metaJsonObject.set("title", sysMenu.getTitle());
|
||||
metaJsonObject.set("type", sysMenu.getCategory().toLowerCase());
|
||||
metaJsonObject.set("type", ObjectUtil.isEmpty(sysMenu.getMenuType())
|
||||
? sysMenu.getCategory().toLowerCase() : sysMenu.getMenuType().toLowerCase());
|
||||
// 如果是菜单,则设置type菜单类型为小写
|
||||
if (sysMenu.getCategory().equals(SysResourceCategoryEnum.MENU.getValue())) {
|
||||
if (!sysMenu.getMenuType().equals(SysResourceMenuTypeEnum.CATALOG.getValue())) {
|
||||
metaJsonObject.set("type", sysMenu.getMenuType().toLowerCase());
|
||||
// 如果设置了不可见,那么设置为false,为了兼容已有,所以只是false的为不显示
|
||||
if (ObjectUtil.isNotEmpty(sysMenu.getVisible()) && sysMenu.getVisible().equals("false")) {
|
||||
metaJsonObject.set("hidden", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 如果是单页面
|
||||
if (sysMenu.getCategory().equals(SysResourceCategoryEnum.SPA.getValue())) {
|
||||
metaJsonObject.set("type", SysResourceCategoryEnum.MENU.getValue().toLowerCase());
|
||||
if (sysMenu.getId().equals(firstSpaId)) {
|
||||
// 如果是首页(第一个单页面)则设置affix
|
||||
if (sysMenu.getId().equals(sysMenus.orElse(null).getId())) {
|
||||
// 如果是首页,则设置affix
|
||||
metaJsonObject.set("affix", true);
|
||||
} else {
|
||||
// 否则隐藏该单页面
|
||||
metaJsonObject.set("hidden", true);
|
||||
}
|
||||
}
|
||||
menuJsonObject.set("meta", metaJsonObject);
|
||||
|
||||
Reference in New Issue
Block a user