动态路由调整
This commit is contained in:
parent
5052ab0e56
commit
df639d040a
|
|
@ -1,87 +1,60 @@
|
||||||
// 模拟静态数据
|
import request from '@/utils/request'
|
||||||
const mockMenus = [
|
|
||||||
{ menuId: 1, menuName: '系统管理', parentId: 0, orderNum: 1, path: 'system', component: 'Layout', status: '0' },
|
|
||||||
{ menuId: 2, menuName: '用户管理', parentId: 1, orderNum: 1, path: 'user', component: 'system/user/index', status: '0' },
|
|
||||||
{ menuId: 3, menuName: '角色管理', parentId: 1, orderNum: 2, path: 'role', component: 'system/role/index', status: '0' },
|
|
||||||
{ menuId: 4, menuName: '菜单管理', parentId: 1, orderNum: 3, path: 'menu', component: 'system/menu/index', status: '0' }
|
|
||||||
];
|
|
||||||
|
|
||||||
// 查询菜单列表
|
// 查询菜单列表
|
||||||
export function listMenu(query) {
|
export function listMenu(query) {
|
||||||
const { menuName, status } = query;
|
return request({
|
||||||
let filteredMenus = [...mockMenus];
|
url: '/system/menu/list',
|
||||||
|
method: 'get',
|
||||||
if (menuName) {
|
params: query
|
||||||
filteredMenus = filteredMenus.filter(menu => menu.menuName.includes(menuName));
|
})
|
||||||
}
|
|
||||||
if (status) {
|
|
||||||
filteredMenus = filteredMenus.filter(menu => menu.status === status);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.resolve(filteredMenus);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询菜单详细
|
// 查询菜单详细
|
||||||
export function getMenu(menuId) {
|
export function getMenu(menuId) {
|
||||||
const menu = mockMenus.find(m => m.menuId === parseInt(menuId));
|
return request({
|
||||||
return Promise.resolve(menu || {});
|
url: '/system/menu/' + menuId,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询菜单下拉树结构
|
// 查询菜单下拉树结构
|
||||||
export function treeselect() {
|
export function treeselect() {
|
||||||
const buildTree = (parentId) => {
|
return request({
|
||||||
const children = mockMenus.filter(m => m.parentId === parentId);
|
url: '/system/menu/treeselect',
|
||||||
return children.map(child => ({
|
method: 'get'
|
||||||
id: child.menuId,
|
})
|
||||||
label: child.menuName,
|
|
||||||
children: buildTree(child.menuId)
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
return Promise.resolve(buildTree(0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据角色ID查询菜单下拉树结构
|
// 根据角色ID查询菜单下拉树结构
|
||||||
export function roleMenuTreeselect(roleId) {
|
export function roleMenuTreeselect(roleId) {
|
||||||
const buildTree = (parentId) => {
|
return request({
|
||||||
const children = mockMenus.filter(m => m.parentId === parentId);
|
url: '/system/menu/roleMenuTreeselect/' + roleId,
|
||||||
return children.map(child => ({
|
method: 'get'
|
||||||
id: child.menuId,
|
})
|
||||||
label: child.menuName,
|
|
||||||
children: buildTree(child.menuId)
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
return Promise.resolve({
|
|
||||||
menus: buildTree(0),
|
|
||||||
checkedKeys: [1, 2, 3, 4] // 模拟已选中的菜单
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增菜单
|
// 新增菜单
|
||||||
export function addMenu(data) {
|
export function addMenu(data) {
|
||||||
const newMenu = {
|
return request({
|
||||||
menuId: mockMenus.length + 1,
|
url: '/system/menu',
|
||||||
...data
|
method: 'post',
|
||||||
};
|
data: data
|
||||||
mockMenus.push(newMenu);
|
})
|
||||||
return Promise.resolve({ code: 200, msg: '新增成功' });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改菜单
|
// 修改菜单
|
||||||
export function updateMenu(data) {
|
export function updateMenu(data) {
|
||||||
const index = mockMenus.findIndex(m => m.menuId === data.menuId);
|
return request({
|
||||||
if (index !== -1) {
|
url: '/system/menu',
|
||||||
mockMenus[index] = { ...mockMenus[index], ...data };
|
method: 'put',
|
||||||
}
|
data: data
|
||||||
return Promise.resolve({ code: 200, msg: '修改成功' });
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除菜单
|
// 删除菜单
|
||||||
export function delMenu(menuId) {
|
export function delMenu(menuId) {
|
||||||
const index = mockMenus.findIndex(m => m.menuId === parseInt(menuId));
|
return request({
|
||||||
if (index !== -1) {
|
url: '/system/menu/' + menuId,
|
||||||
mockMenus.splice(index, 1);
|
method: 'delete'
|
||||||
}
|
})
|
||||||
return Promise.resolve({ code: 200, msg: '删除成功' });
|
|
||||||
}
|
}
|
||||||
|
|
@ -5,9 +5,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
const url = ref('http://doc.ruoyi.vip/ruoyi-vue');
|
const url = ref("http://doc.ruoyi.vip/ruoyi-vue");
|
||||||
|
|
||||||
function goto() {
|
function goto() {
|
||||||
window.open(url.value)
|
window.open(url.value);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -1,121 +1,128 @@
|
||||||
<template>
|
<template>
|
||||||
<div :class="classObj" class="app-wrapper" :style="{ '--current-color': theme }">
|
<div :class="classObj" class="app-wrapper" :style="{ '--current-color': theme }">
|
||||||
<div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside"/>
|
<div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside"/>
|
||||||
<sidebar v-if="!sidebar.hide" class="sidebar-container" />
|
<sidebar v-if="!sidebar.hide" class="sidebar-container" />
|
||||||
<div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }" class="main-container">
|
<div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }" class="main-container">
|
||||||
<div :class="{ 'fixed-header': fixedHeader }">
|
<div :class="{ 'fixed-header': fixedHeader }">
|
||||||
<navbar @setLayout="setLayout" />
|
<navbar @setLayout="setLayout" />
|
||||||
<tags-view v-if="needTagsView" />
|
<tags-view v-if="needTagsView" />
|
||||||
</div>
|
</div>
|
||||||
<app-main />
|
<app-main />
|
||||||
<settings ref="settingRef" />
|
<settings ref="settingRef" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useWindowSize } from '@vueuse/core'
|
import { useWindowSize } from '@vueuse/core'
|
||||||
import Sidebar from './components/Sidebar/index.vue'
|
import Sidebar from './components/Sidebar/index.vue'
|
||||||
import { AppMain, Navbar, Settings, TagsView } from './components'
|
import { AppMain, Navbar, Settings, TagsView } from './components'
|
||||||
import defaultSettings from '@/settings'
|
import defaultSettings from '@/settings'
|
||||||
|
|
||||||
import useAppStore from '@/store/modules/app'
|
import useAppStore from '@/store/modules/app'
|
||||||
import useSettingsStore from '@/store/modules/settings'
|
import useSettingsStore from '@/store/modules/settings'
|
||||||
import usePermissionStore from '@/store/modules/permission'
|
import usePermissionStore from '@/store/modules/permission'
|
||||||
|
import useUserStore from '@/store/modules/user'
|
||||||
const settingsStore = useSettingsStore()
|
|
||||||
const theme = computed(() => settingsStore.theme);
|
const settingsStore = useSettingsStore()
|
||||||
const sideTheme = computed(() => settingsStore.sideTheme);
|
const theme = computed(() => settingsStore.theme);
|
||||||
const sidebar = computed(() => useAppStore().sidebar);
|
const sideTheme = computed(() => settingsStore.sideTheme);
|
||||||
const device = computed(() => useAppStore().device);
|
const sidebar = computed(() => useAppStore().sidebar);
|
||||||
const needTagsView = computed(() => settingsStore.tagsView);
|
const device = computed(() => useAppStore().device);
|
||||||
const fixedHeader = computed(() => settingsStore.fixedHeader);
|
const needTagsView = computed(() => settingsStore.tagsView);
|
||||||
|
const fixedHeader = computed(() => settingsStore.fixedHeader);
|
||||||
const classObj = computed(() => ({
|
|
||||||
hideSidebar: !sidebar.value.opened,
|
const classObj = computed(() => ({
|
||||||
openSidebar: sidebar.value.opened,
|
hideSidebar: !sidebar.value.opened,
|
||||||
withoutAnimation: sidebar.value.withoutAnimation,
|
openSidebar: sidebar.value.opened,
|
||||||
mobile: device.value === 'mobile'
|
withoutAnimation: sidebar.value.withoutAnimation,
|
||||||
}))
|
mobile: device.value === 'mobile'
|
||||||
|
}))
|
||||||
const { width, height } = useWindowSize();
|
|
||||||
const WIDTH = 992; // refer to Bootstrap's responsive design
|
const { width, height } = useWindowSize();
|
||||||
|
const WIDTH = 992; // refer to Bootstrap's responsive design
|
||||||
const permissionStore = usePermissionStore()
|
|
||||||
|
const permissionStore = usePermissionStore()
|
||||||
watch(() => device.value, () => {
|
|
||||||
if (device.value === 'mobile' && sidebar.value.opened) {
|
watch(() => device.value, () => {
|
||||||
useAppStore().closeSideBar({ withoutAnimation: false })
|
if (device.value === 'mobile' && sidebar.value.opened) {
|
||||||
}
|
useAppStore().closeSideBar({ withoutAnimation: false })
|
||||||
})
|
}
|
||||||
|
})
|
||||||
watchEffect(() => {
|
|
||||||
if (width.value - 1 < WIDTH) {
|
watchEffect(() => {
|
||||||
useAppStore().toggleDevice('mobile')
|
if (width.value - 1 < WIDTH) {
|
||||||
useAppStore().closeSideBar({ withoutAnimation: true })
|
useAppStore().toggleDevice('mobile')
|
||||||
} else {
|
useAppStore().closeSideBar({ withoutAnimation: true })
|
||||||
useAppStore().toggleDevice('desktop')
|
} else {
|
||||||
}
|
useAppStore().toggleDevice('desktop')
|
||||||
})
|
}
|
||||||
|
})
|
||||||
function handleClickOutside() {
|
|
||||||
useAppStore().closeSideBar({ withoutAnimation: false })
|
function handleClickOutside() {
|
||||||
}
|
useAppStore().closeSideBar({ withoutAnimation: false })
|
||||||
|
}
|
||||||
const settingRef = ref(null);
|
|
||||||
function setLayout() {
|
const settingRef = ref(null);
|
||||||
settingRef.value.openSetting();
|
function setLayout() {
|
||||||
}
|
settingRef.value.openSetting();
|
||||||
|
}
|
||||||
onMounted(() => {
|
|
||||||
permissionStore.generateRoutes()
|
onMounted(() => {
|
||||||
})
|
// 如果路由未加载,则加载路由(避免与路由守卫中的调用冲突)
|
||||||
</script>
|
if (permissionStore.addRoutes.length === 0) {
|
||||||
|
const userStore = useUserStore()
|
||||||
<style lang="scss" scoped>
|
if (userStore.roles.length > 0) {
|
||||||
@import "@/assets/styles/mixin.scss";
|
permissionStore.generateRoutes(userStore.roles)
|
||||||
@import "@/assets/styles/variables.module.scss";
|
}
|
||||||
|
}
|
||||||
.app-wrapper {
|
})
|
||||||
@include clearfix;
|
</script>
|
||||||
position: relative;
|
|
||||||
height: 100%;
|
<style lang="scss" scoped>
|
||||||
width: 100%;
|
@import "@/assets/styles/mixin.scss";
|
||||||
|
@import "@/assets/styles/variables.module.scss";
|
||||||
&.mobile.openSidebar {
|
|
||||||
position: fixed;
|
.app-wrapper {
|
||||||
top: 0;
|
@include clearfix;
|
||||||
}
|
position: relative;
|
||||||
}
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
.drawer-bg {
|
|
||||||
background: #000;
|
&.mobile.openSidebar {
|
||||||
opacity: 0.3;
|
position: fixed;
|
||||||
width: 100%;
|
top: 0;
|
||||||
top: 0;
|
}
|
||||||
height: 100%;
|
}
|
||||||
position: absolute;
|
|
||||||
z-index: 999;
|
.drawer-bg {
|
||||||
}
|
background: #000;
|
||||||
|
opacity: 0.3;
|
||||||
.fixed-header {
|
width: 100%;
|
||||||
position: fixed;
|
top: 0;
|
||||||
top: 0;
|
height: 100%;
|
||||||
right: 0;
|
position: absolute;
|
||||||
z-index: 9;
|
z-index: 999;
|
||||||
width: calc(100% - #{$base-sidebar-width});
|
}
|
||||||
transition: width 0.28s;
|
|
||||||
}
|
.fixed-header {
|
||||||
|
position: fixed;
|
||||||
.hideSidebar .fixed-header {
|
top: 0;
|
||||||
width: calc(100% - 54px);
|
right: 0;
|
||||||
}
|
z-index: 9;
|
||||||
|
width: calc(100% - #{$base-sidebar-width});
|
||||||
.sidebarHide .fixed-header {
|
transition: width 0.28s;
|
||||||
width: 100%;
|
}
|
||||||
}
|
|
||||||
|
.hideSidebar .fixed-header {
|
||||||
.mobile .fixed-header {
|
width: calc(100% - 54px);
|
||||||
width: 100%;
|
}
|
||||||
}
|
|
||||||
|
.sidebarHide .fixed-header {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile .fixed-header {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
@ -1,61 +1,83 @@
|
||||||
import router from './router'
|
import router from './router'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import NProgress from 'nprogress'
|
import NProgress from 'nprogress'
|
||||||
import 'nprogress/nprogress.css'
|
import 'nprogress/nprogress.css'
|
||||||
import { getToken } from '@/utils/auth'
|
import { getToken } from '@/utils/auth'
|
||||||
import { isHttp, isPathMatch } from '@/utils/validate'
|
import { isHttp, isPathMatch } from '@/utils/validate'
|
||||||
import { isRelogin } from '@/utils/request'
|
import { isRelogin } from '@/utils/request'
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
import useSettingsStore from '@/store/modules/settings'
|
import useSettingsStore from '@/store/modules/settings'
|
||||||
|
import usePermissionStore from '@/store/modules/permission'
|
||||||
NProgress.configure({ showSpinner: false })
|
|
||||||
|
NProgress.configure({ showSpinner: false })
|
||||||
const whiteList = ['/login', '/register']
|
|
||||||
|
const whiteList = ['/login', '/register']
|
||||||
const isWhiteList = (path) => {
|
|
||||||
return whiteList.some(pattern => isPathMatch(pattern, path))
|
const isWhiteList = (path) => {
|
||||||
}
|
return whiteList.some(pattern => isPathMatch(pattern, path))
|
||||||
|
}
|
||||||
router.beforeEach((to, from, next) => {
|
|
||||||
NProgress.start()
|
router.beforeEach((to, from, next) => {
|
||||||
if (getToken()) {
|
NProgress.start()
|
||||||
to.meta.title && useSettingsStore().setTitle(to.meta.title)
|
if (getToken()) {
|
||||||
/* has token*/
|
to.meta.title && useSettingsStore().setTitle(to.meta.title)
|
||||||
if (to.path === '/login') {
|
/* has token*/
|
||||||
next({ path: '/' })
|
if (to.path === '/login') {
|
||||||
NProgress.done()
|
next({ path: '/' })
|
||||||
} else if (isWhiteList(to.path)) {
|
NProgress.done()
|
||||||
next()
|
} else if (isWhiteList(to.path)) {
|
||||||
} else {
|
next()
|
||||||
if (useUserStore().roles.length === 0) {
|
} else {
|
||||||
isRelogin.show = true
|
if (useUserStore().roles.length === 0) {
|
||||||
// 判断当前用户是否已拉取完user_info信息
|
isRelogin.show = true
|
||||||
useUserStore().getInfo().then(() => {
|
// 判断当前用户是否已拉取完user_info信息
|
||||||
isRelogin.show = false
|
useUserStore().getInfo().then(() => {
|
||||||
// 不再动态加载路由,直接使用静态路由
|
isRelogin.show = false
|
||||||
next({ ...to, replace: true })
|
// 动态加载路由
|
||||||
}).catch(err => {
|
const permissionStore = usePermissionStore()
|
||||||
useUserStore().logOut().then(() => {
|
permissionStore.generateRoutes(useUserStore().roles).then(() => {
|
||||||
ElMessage.error(err)
|
// 动态路由加载完成后,重新跳转到目标路由
|
||||||
next({ path: '/' })
|
next({ ...to, replace: true })
|
||||||
})
|
}).catch((error) => {
|
||||||
})
|
console.error('路由加载失败:', error)
|
||||||
} else {
|
ElMessage.error(error?.message || '路由加载失败,请刷新页面重试')
|
||||||
next()
|
next({ path: '/' })
|
||||||
}
|
})
|
||||||
}
|
}).catch(err => {
|
||||||
} else {
|
useUserStore().logOut().then(() => {
|
||||||
// 没有token
|
ElMessage.error(err)
|
||||||
if (isWhiteList(to.path)) {
|
next({ path: '/' })
|
||||||
// 在免登录白名单,直接进入
|
})
|
||||||
next()
|
})
|
||||||
} else {
|
} else {
|
||||||
next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页
|
// 已获取用户信息,检查路由是否已加载
|
||||||
NProgress.done()
|
const permissionStore = usePermissionStore()
|
||||||
}
|
if (permissionStore.addRoutes.length === 0) {
|
||||||
}
|
// 路由未加载,重新加载
|
||||||
})
|
permissionStore.generateRoutes(useUserStore().roles).then(() => {
|
||||||
|
next({ ...to, replace: true })
|
||||||
router.afterEach(() => {
|
}).catch((error) => {
|
||||||
NProgress.done()
|
console.error('路由加载失败:', error)
|
||||||
})
|
ElMessage.error(error?.message || '路由加载失败,请刷新页面重试')
|
||||||
|
next({ path: '/' })
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 没有token
|
||||||
|
if (isWhiteList(to.path)) {
|
||||||
|
// 在免登录白名单,直接进入
|
||||||
|
next()
|
||||||
|
} else {
|
||||||
|
next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页
|
||||||
|
NProgress.done()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
router.afterEach(() => {
|
||||||
|
NProgress.done()
|
||||||
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,203 +1,105 @@
|
||||||
import { createWebHistory, createRouter } from 'vue-router'
|
import { createWebHistory, createRouter } from 'vue-router'
|
||||||
/* Layout */
|
/* Layout */
|
||||||
import Layout from '@/layout/index.vue'
|
import Layout from '@/layout/index.vue'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Note: 路由配置项
|
* Note: 路由配置项
|
||||||
*
|
*
|
||||||
* hidden: true // 当设置 true 的时候该路由不会再侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1
|
* hidden: true // 当设置 true 的时候该路由不会再侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1
|
||||||
* alwaysShow: true // 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面
|
* alwaysShow: true // 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面
|
||||||
* // 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面
|
* // 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面
|
||||||
* // 若你想不管路由下面的 children 声明的个数都显示你的根路由
|
* // 若你想不管路由下面的 children 声明的个数都显示你的根路由
|
||||||
* // 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由
|
* // 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由
|
||||||
* redirect: noRedirect // 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击
|
* redirect: noRedirect // 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击
|
||||||
* name:'router-name' // 设定路由的名字,一定要填写不然使用<keep-alive>时会出现各种问题
|
* name:'router-name' // 设定路由的名字,一定要填写不然使用<keep-alive>时会出现各种问题
|
||||||
* query: '{"id": 1, "name": "ry"}' // 访问路由的默认传递参数
|
* query: '{"id": 1, "name": "ry"}' // 访问路由的默认传递参数
|
||||||
* roles: ['admin', 'common'] // 访问路由的角色权限
|
* roles: ['admin', 'common'] // 访问路由的角色权限
|
||||||
* permissions: ['a:a:a', 'b:b:b'] // 访问路由的菜单权限
|
* permissions: ['a:a:a', 'b:b:b'] // 访问路由的菜单权限
|
||||||
* meta : {
|
* meta : {
|
||||||
noCache: true // 如果设置为true,则不会被 <keep-alive> 缓存(默认 false)
|
noCache: true // 如果设置为true,则不会被 <keep-alive> 缓存(默认 false)
|
||||||
title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字
|
title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字
|
||||||
icon: 'svg-name' // 设置该路由的图标,对应路径src/assets/icons/svg
|
icon: 'svg-name' // 设置该路由的图标,对应路径src/assets/icons/svg
|
||||||
breadcrumb: false // 如果设置为false,则不会在breadcrumb面包屑中显示
|
breadcrumb: false // 如果设置为false,则不会在breadcrumb面包屑中显示
|
||||||
activeMenu: '/system/user' // 当路由设置了该属性,则会高亮相对应的侧边栏。
|
activeMenu: '/system/user' // 当路由设置了该属性,则会高亮相对应的侧边栏。
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// 公共路由
|
// 公共路由
|
||||||
export const constantRoutes = [
|
export const constantRoutes = [
|
||||||
{
|
{
|
||||||
path: '/redirect',
|
path: '/redirect',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
hidden: true,
|
hidden: true,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/redirect/:path(.*)',
|
path: '/redirect/:path(.*)',
|
||||||
component: () => import('@/views/redirect/index.vue')
|
component: () => import('@/views/redirect/index.vue')
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/login',
|
path: '/login',
|
||||||
component: () => import('@/views/login.vue'),
|
component: () => import('@/views/login.vue'),
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/register',
|
path: '/register',
|
||||||
component: () => import('@/views/register.vue'),
|
component: () => import('@/views/register.vue'),
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/:pathMatch(.*)*",
|
path: "/:pathMatch(.*)*",
|
||||||
component: () => import('@/views/error/404.vue'),
|
component: () => import('@/views/error/404.vue'),
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/401',
|
path: '/401',
|
||||||
component: () => import('@/views/error/401.vue'),
|
component: () => import('@/views/error/401.vue'),
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
redirect: '/order/intention',
|
redirect: 'index',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/index',
|
path: '/index',
|
||||||
component: () => import('@/views/index.vue'),
|
component: () => import('@/views/index.vue'),
|
||||||
name: 'Index',
|
name: 'Index',
|
||||||
meta: { title: '首页', icon: 'dashboard', affix: true }
|
meta: { title: '首页', icon: 'dashboard', affix: true }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/user',
|
path: '/user',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
hidden: true,
|
hidden: true,
|
||||||
redirect: 'noRedirect',
|
redirect: 'noRedirect',
|
||||||
name: 'UserCenter',
|
name: 'UserCenter',
|
||||||
meta: { title: '个人中心', icon: 'user' },
|
meta: { title: '个人中心', icon: 'user' },
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'profile',
|
path: 'profile',
|
||||||
component: () => import('@/views/user/profile/index.vue'),
|
component: () => import('@/views/user/profile/index.vue'),
|
||||||
name: 'Profile',
|
name: 'Profile',
|
||||||
meta: { title: '个人中心', icon: 'user' }
|
meta: { title: '个人中心', icon: 'user' }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
}
|
||||||
{
|
// 业务路由(订单管理、系统管理等)将从后端动态获取
|
||||||
path: '/order',
|
];
|
||||||
component: Layout,
|
|
||||||
hidden: false,
|
// 动态路由将从后端接口获取,不再在此处定义
|
||||||
name: 'Order',
|
|
||||||
meta: { title: '订单管理', icon: 'shopping' },
|
const router = createRouter({
|
||||||
children: [
|
history: createWebHistory(),
|
||||||
{
|
routes: constantRoutes,
|
||||||
path: 'intention',
|
scrollBehavior(to, from, savedPosition) {
|
||||||
component: () => import('@/views/order/intention/index.vue'),
|
if (savedPosition) {
|
||||||
name: 'Intention',
|
return savedPosition
|
||||||
meta: { title: '订单/意向单', icon: 'form' }
|
}
|
||||||
},
|
return { top: 0 }
|
||||||
{
|
},
|
||||||
path: 'create',
|
});
|
||||||
component: () => import('@/views/order/intention/create.vue'),
|
|
||||||
name: 'IntentionCreate',
|
export default router;
|
||||||
meta: { title: '创建订单/意向单', icon: 'edit', hidden: true }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'search',
|
|
||||||
component: () => import('@/views/order/intention/search.vue'),
|
|
||||||
name: 'IntentionSearch',
|
|
||||||
meta: { title: '品号查询', icon: 'search', hidden: true }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
// 以下为原动态路由,现在直接合并到静态路由中
|
|
||||||
{
|
|
||||||
path: '/system/user-auth',
|
|
||||||
component: Layout,
|
|
||||||
hidden: true,
|
|
||||||
permissions: ['system:user:edit'],
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'role/:userId(\\d+)',
|
|
||||||
component: () => import('@/views/system/user/authRole.vue'),
|
|
||||||
name: 'AuthRole',
|
|
||||||
meta: { title: '分配角色', activeMenu: '/system/user' }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/system/role-auth',
|
|
||||||
component: Layout,
|
|
||||||
hidden: true,
|
|
||||||
permissions: ['system:role:edit'],
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'user/:roleId(\\d+)',
|
|
||||||
component: () => import('@/views/system/role/authUser.vue'),
|
|
||||||
name: 'AuthUser',
|
|
||||||
meta: { title: '分配用户', activeMenu: '/system/role' }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/system/dict-data',
|
|
||||||
component: Layout,
|
|
||||||
hidden: true,
|
|
||||||
permissions: ['system:dict:list'],
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'index/:dictId(\\d+)',
|
|
||||||
component: () => import('@/views/system/dict/data.vue'),
|
|
||||||
name: 'Data',
|
|
||||||
meta: { title: '字典数据', activeMenu: '/system/dict' }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/monitor/job-log',
|
|
||||||
component: Layout,
|
|
||||||
hidden: true,
|
|
||||||
permissions: ['monitor:job:list'],
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'index/:jobId(\\d+)',
|
|
||||||
component: () => import('@/views/monitor/job/log.vue'),
|
|
||||||
name: 'JobLog',
|
|
||||||
meta: { title: '调度日志', activeMenu: '/monitor/job' }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/tool/gen-edit',
|
|
||||||
component: Layout,
|
|
||||||
hidden: true,
|
|
||||||
permissions: ['tool:gen:edit'],
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'index/:tableId(\\d+)',
|
|
||||||
component: () => import('@/views/tool/gen/editTable.vue'),
|
|
||||||
name: 'GenEdit',
|
|
||||||
meta: { title: '修改生成配置', activeMenu: '/tool/gen' }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
// 动态路由已合并到静态路由中,不再需要
|
|
||||||
// export const dynamicRoutes = [ ... ];
|
|
||||||
|
|
||||||
const router = createRouter({
|
|
||||||
history: createWebHistory(),
|
|
||||||
routes: constantRoutes,
|
|
||||||
scrollBehavior(to, from, savedPosition) {
|
|
||||||
if (savedPosition) {
|
|
||||||
return savedPosition
|
|
||||||
}
|
|
||||||
return { top: 0 }
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default router;
|
|
||||||
|
|
|
||||||
|
|
@ -1,119 +1,231 @@
|
||||||
import auth from '@/plugins/auth'
|
import auth from '@/plugins/auth'
|
||||||
import router, { constantRoutes } from '@/router'
|
import router, { constantRoutes } from '@/router'
|
||||||
// 不再需要动态路由,移除 dynamicRoutes 和 getRouters
|
import { getRouters } from '@/api/menu'
|
||||||
// import { getRouters } from '@/api/menu'
|
import Layout from '@/layout/index'
|
||||||
import Layout from '@/layout/index'
|
import ParentView from '@/components/ParentView'
|
||||||
import ParentView from '@/components/ParentView'
|
import InnerLink from '@/layout/components/InnerLink'
|
||||||
import InnerLink from '@/layout/components/InnerLink'
|
import { isHttp } from '@/utils/validate'
|
||||||
|
|
||||||
// 匹配views里面所有的.vue文件
|
// 匹配views里面所有的.vue文件
|
||||||
const modules = import.meta.glob('./../../views/**/*.vue')
|
const modules = import.meta.glob('./../../views/**/*.vue')
|
||||||
|
|
||||||
const usePermissionStore = defineStore(
|
const usePermissionStore = defineStore(
|
||||||
'permission',
|
'permission',
|
||||||
{
|
{
|
||||||
state: () => ({
|
state: () => ({
|
||||||
routes: [],
|
routes: [],
|
||||||
addRoutes: [],
|
addRoutes: [],
|
||||||
defaultRoutes: [],
|
defaultRoutes: [],
|
||||||
topbarRouters: [],
|
topbarRouters: [],
|
||||||
sidebarRouters: []
|
sidebarRouters: []
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
setRoutes(routes) {
|
setRoutes(routes) {
|
||||||
this.addRoutes = routes
|
this.addRoutes = routes
|
||||||
this.routes = constantRoutes.concat(routes)
|
this.routes = constantRoutes.concat(routes)
|
||||||
},
|
},
|
||||||
setDefaultRoutes(routes) {
|
setDefaultRoutes(routes) {
|
||||||
this.defaultRoutes = constantRoutes.concat(routes)
|
this.defaultRoutes = constantRoutes.concat(routes)
|
||||||
},
|
},
|
||||||
setTopbarRoutes(routes) {
|
setTopbarRoutes(routes) {
|
||||||
this.topbarRouters = routes
|
this.topbarRouters = routes
|
||||||
},
|
},
|
||||||
setSidebarRouters(routes) {
|
setSidebarRouters(routes) {
|
||||||
this.sidebarRouters = routes
|
this.sidebarRouters = routes
|
||||||
},
|
},
|
||||||
generateRoutes(roles) {
|
generateRoutes(roles) {
|
||||||
return new Promise(resolve => {
|
return new Promise((resolve, reject) => {
|
||||||
// 不再调用后端接口,直接使用静态路由
|
// 调用后端接口获取动态路由
|
||||||
const staticRoutes = constantRoutes
|
getRouters().then(res => {
|
||||||
this.setRoutes(staticRoutes)
|
// 确保 res.data 是数组
|
||||||
this.setSidebarRouters(staticRoutes)
|
if (!res.data || !Array.isArray(res.data)) {
|
||||||
this.setDefaultRoutes(staticRoutes)
|
console.error('路由数据格式错误:', res.data)
|
||||||
this.setTopbarRoutes(staticRoutes)
|
reject(new Error('路由数据格式错误,期望数组'))
|
||||||
resolve(staticRoutes)
|
return
|
||||||
})
|
}
|
||||||
}
|
|
||||||
}
|
try {
|
||||||
})
|
const sdata = JSON.parse(JSON.stringify(res.data))
|
||||||
|
const rdata = JSON.parse(JSON.stringify(res.data))
|
||||||
// 遍历后台传来的路由字符串,转换为组件对象
|
const defaultData = JSON.parse(JSON.stringify(res.data))
|
||||||
function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
|
const sidebarRoutes = filterAsyncRouter(sdata)
|
||||||
return asyncRouterMap.filter(route => {
|
const rewriteRoutes = filterAsyncRouter(rdata, false, true)
|
||||||
if (type && route.children) {
|
const defaultRoutes = filterAsyncRouter(defaultData)
|
||||||
route.children = filterChildren(route.children)
|
|
||||||
}
|
// 过滤权限路由
|
||||||
if (route.component) {
|
const accessedRoutes = filterDynamicRoutes(rewriteRoutes)
|
||||||
// Layout ParentView 组件特殊处理
|
|
||||||
if (route.component === 'Layout') {
|
// 调试:打印路由信息
|
||||||
route.component = Layout
|
console.log('动态路由加载成功,路由数量:', accessedRoutes.length)
|
||||||
} else if (route.component === 'ParentView') {
|
if (accessedRoutes.length > 0) {
|
||||||
route.component = ParentView
|
console.log('第一个路由示例:', accessedRoutes[0])
|
||||||
} else if (route.component === 'InnerLink') {
|
}
|
||||||
route.component = InnerLink
|
|
||||||
} else {
|
this.setRoutes(accessedRoutes)
|
||||||
route.component = loadView(route.component)
|
this.setSidebarRouters(constantRoutes.concat(sidebarRoutes))
|
||||||
}
|
this.setDefaultRoutes(constantRoutes.concat(defaultRoutes))
|
||||||
}
|
this.setTopbarRoutes(constantRoutes.concat(sidebarRoutes))
|
||||||
if (route.children != null && route.children && route.children.length) {
|
|
||||||
route.children = filterAsyncRouter(route.children, route, type)
|
// 动态添加路由到router
|
||||||
} else {
|
accessedRoutes.forEach(route => {
|
||||||
delete route['children']
|
router.addRoute(route)
|
||||||
delete route['redirect']
|
})
|
||||||
}
|
|
||||||
return true
|
resolve(accessedRoutes)
|
||||||
})
|
} catch (error) {
|
||||||
}
|
console.error('路由处理错误:', error)
|
||||||
|
reject(error)
|
||||||
function filterChildren(childrenMap, lastRouter = false) {
|
}
|
||||||
var children = []
|
}).catch(error => {
|
||||||
childrenMap.forEach(el => {
|
console.error('获取路由失败:', error)
|
||||||
el.path = lastRouter ? lastRouter.path + '/' + el.path : el.path
|
reject(error)
|
||||||
if (el.children && el.children.length && el.component === 'ParentView') {
|
})
|
||||||
children = children.concat(filterChildren(el.children, el))
|
})
|
||||||
} else {
|
}
|
||||||
children.push(el)
|
}
|
||||||
}
|
})
|
||||||
})
|
|
||||||
return children
|
// 遍历后台传来的路由字符串,转换为组件对象
|
||||||
}
|
function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
|
||||||
|
// 确保 asyncRouterMap 是数组
|
||||||
// 动态路由遍历,验证是否具备权限
|
if (!Array.isArray(asyncRouterMap)) {
|
||||||
export function filterDynamicRoutes(routes) {
|
console.error('filterAsyncRouter: 期望数组,但收到:', asyncRouterMap)
|
||||||
const res = []
|
return []
|
||||||
routes.forEach(route => {
|
}
|
||||||
if (route.permissions) {
|
|
||||||
if (auth.hasPermiOr(route.permissions)) {
|
return asyncRouterMap.filter(route => {
|
||||||
res.push(route)
|
// 处理外部链接路径:如果 path 是外部链接,需要转换为有效的内部路径
|
||||||
}
|
if (route.path && isHttp(route.path)) {
|
||||||
} else if (route.roles) {
|
// 确保 meta.link 保存实际的外部链接
|
||||||
if (auth.hasRoleOr(route.roles)) {
|
if (!route.meta) {
|
||||||
res.push(route)
|
route.meta = {}
|
||||||
}
|
}
|
||||||
}
|
// 保存原始的外部链接到 meta.link
|
||||||
})
|
const originalPath = route.path
|
||||||
return res
|
route.meta.link = originalPath
|
||||||
}
|
|
||||||
|
// 将外部链接路径转换为内部路径
|
||||||
export const loadView = (view) => {
|
// 使用简单的路径格式:/iframe/域名,避免路径冲突
|
||||||
let res;
|
const domain = originalPath.replace(/^https?:\/\//, '').replace(/\/.*$/, '')
|
||||||
for (const path in modules) {
|
const timestamp = Date.now()
|
||||||
const dir = path.split('views/')[1].split('.vue')[0];
|
const encodedPath = '/iframe/' + encodeURIComponent(domain) + '-' + timestamp
|
||||||
if (dir === view) {
|
|
||||||
res = () => modules[path]();
|
// 将 path 转换为有效的内部路径
|
||||||
}
|
route.path = encodedPath
|
||||||
}
|
} else if (!lastRouter && route.path && !route.path.startsWith('/')) {
|
||||||
return res
|
// 只对顶级路由(lastRouter 为 false)确保路径以 "/" 开头
|
||||||
}
|
// 子路由的路径会在 filterChildren 中处理
|
||||||
|
route.path = '/' + route.path
|
||||||
export default usePermissionStore
|
}
|
||||||
|
|
||||||
|
if (route.component) {
|
||||||
|
// 检查是否有外部链接
|
||||||
|
if (route.meta && route.meta.link && (route.meta.link.startsWith('http://') || route.meta.link.startsWith('https://'))) {
|
||||||
|
route.component = InnerLink
|
||||||
|
}
|
||||||
|
// Layout ParentView 组件特殊处理
|
||||||
|
else if (route.component === 'Layout') {
|
||||||
|
route.component = Layout
|
||||||
|
} else if (route.component === 'ParentView') {
|
||||||
|
route.component = ParentView
|
||||||
|
} else if (route.component === 'InnerLink') {
|
||||||
|
route.component = InnerLink
|
||||||
|
} else {
|
||||||
|
const component = loadView(route.component)
|
||||||
|
if (!component) {
|
||||||
|
console.warn('找不到组件:', route.component)
|
||||||
|
}
|
||||||
|
route.component = component || (() => import('@/views/error/404.vue'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (route.children != null && route.children && route.children.length) {
|
||||||
|
// 如果 type 为 true,先使用 filterChildren 处理子路由路径
|
||||||
|
if (type) {
|
||||||
|
route.children = filterChildren(route.children, route)
|
||||||
|
}
|
||||||
|
// 然后递归处理子路由
|
||||||
|
route.children = filterAsyncRouter(route.children, route, type)
|
||||||
|
} else {
|
||||||
|
delete route['children']
|
||||||
|
delete route['redirect']
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterChildren(childrenMap, lastRouter = false) {
|
||||||
|
var children = []
|
||||||
|
childrenMap.forEach(el => {
|
||||||
|
if (lastRouter) {
|
||||||
|
// 处理子路由路径:如果子路由路径以 / 开头,需要去掉,因为要拼接父路由路径
|
||||||
|
let childPath = el.path || ''
|
||||||
|
if (childPath.startsWith('/')) {
|
||||||
|
childPath = childPath.substring(1)
|
||||||
|
}
|
||||||
|
// 拼接父路由路径和子路由路径
|
||||||
|
const parentPath = lastRouter.path || ''
|
||||||
|
// 确保父路径以 / 结尾(如果父路径不是根路径)
|
||||||
|
const separator = parentPath.endsWith('/') ? '' : '/'
|
||||||
|
el.path = parentPath + separator + childPath
|
||||||
|
}
|
||||||
|
// 确保最终路径以 / 开头(如果不是空路径)
|
||||||
|
if (el.path && !el.path.startsWith('/')) {
|
||||||
|
el.path = '/' + el.path
|
||||||
|
}
|
||||||
|
if (el.children && el.children.length && el.component === 'ParentView') {
|
||||||
|
children = children.concat(filterChildren(el.children, el))
|
||||||
|
} else {
|
||||||
|
children.push(el)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return children
|
||||||
|
}
|
||||||
|
|
||||||
|
// 动态路由遍历,验证是否具备权限
|
||||||
|
export function filterDynamicRoutes(routes) {
|
||||||
|
const res = []
|
||||||
|
routes.forEach(route => {
|
||||||
|
if (route.permissions) {
|
||||||
|
if (auth.hasPermiOr(route.permissions)) {
|
||||||
|
res.push(route)
|
||||||
|
}
|
||||||
|
} else if (route.roles) {
|
||||||
|
if (auth.hasRoleOr(route.roles)) {
|
||||||
|
res.push(route)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 没有权限控制的路由,直接通过
|
||||||
|
res.push(route)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
export const loadView = (view) => {
|
||||||
|
if (!view) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理外部链接
|
||||||
|
if (view.startsWith('http://') || view.startsWith('https://')) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = null;
|
||||||
|
for (const path in modules) {
|
||||||
|
const dir = path.split('views/')[1]?.split('.vue')[0];
|
||||||
|
if (dir && dir === view) {
|
||||||
|
res = () => modules[path]();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
|
console.warn(`loadView: 找不到组件 "${view}",请检查路径是否正确`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
export default usePermissionStore
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,228 +1,234 @@
|
||||||
/**
|
/**
|
||||||
* 通用js方法封装处理
|
* 通用js方法封装处理
|
||||||
* Copyright (c) 2019 ruoyi
|
* Copyright (c) 2019 ruoyi
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// 日期格式化
|
// 日期格式化
|
||||||
export function parseTime(time, pattern) {
|
export function parseTime(time, pattern) {
|
||||||
if (arguments.length === 0 || !time) {
|
if (arguments.length === 0 || !time) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
|
const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
|
||||||
let date
|
let date
|
||||||
if (typeof time === 'object') {
|
if (typeof time === 'object') {
|
||||||
date = time
|
date = time
|
||||||
} else {
|
} else {
|
||||||
if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
|
if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
|
||||||
time = parseInt(time)
|
time = parseInt(time)
|
||||||
} else if (typeof time === 'string') {
|
} else if (typeof time === 'string') {
|
||||||
time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\.[\d]{3}/gm), '');
|
time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\.[\d]{3}/gm), '');
|
||||||
}
|
}
|
||||||
if ((typeof time === 'number') && (time.toString().length === 10)) {
|
if ((typeof time === 'number') && (time.toString().length === 10)) {
|
||||||
time = time * 1000
|
time = time * 1000
|
||||||
}
|
}
|
||||||
date = new Date(time)
|
date = new Date(time)
|
||||||
}
|
}
|
||||||
const formatObj = {
|
const formatObj = {
|
||||||
y: date.getFullYear(),
|
y: date.getFullYear(),
|
||||||
m: date.getMonth() + 1,
|
m: date.getMonth() + 1,
|
||||||
d: date.getDate(),
|
d: date.getDate(),
|
||||||
h: date.getHours(),
|
h: date.getHours(),
|
||||||
i: date.getMinutes(),
|
i: date.getMinutes(),
|
||||||
s: date.getSeconds(),
|
s: date.getSeconds(),
|
||||||
a: date.getDay()
|
a: date.getDay()
|
||||||
}
|
}
|
||||||
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
|
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
|
||||||
let value = formatObj[key]
|
let value = formatObj[key]
|
||||||
// Note: getDay() returns 0 on Sunday
|
// Note: getDay() returns 0 on Sunday
|
||||||
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] }
|
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] }
|
||||||
if (result.length > 0 && value < 10) {
|
if (result.length > 0 && value < 10) {
|
||||||
value = '0' + value
|
value = '0' + value
|
||||||
}
|
}
|
||||||
return value || 0
|
return value || 0
|
||||||
})
|
})
|
||||||
return time_str
|
return time_str
|
||||||
}
|
}
|
||||||
|
|
||||||
// 表单重置
|
// 表单重置
|
||||||
export function resetForm(refName) {
|
export function resetForm(refName) {
|
||||||
if (this.$refs[refName]) {
|
if (this.$refs[refName]) {
|
||||||
this.$refs[refName].resetFields();
|
this.$refs[refName].resetFields();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加日期范围
|
// 添加日期范围
|
||||||
export function addDateRange(params, dateRange, propName) {
|
export function addDateRange(params, dateRange, propName) {
|
||||||
let search = params;
|
let search = params;
|
||||||
search.params = typeof (search.params) === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {};
|
search.params = typeof (search.params) === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {};
|
||||||
dateRange = Array.isArray(dateRange) ? dateRange : [];
|
dateRange = Array.isArray(dateRange) ? dateRange : [];
|
||||||
if (typeof (propName) === 'undefined') {
|
if (typeof (propName) === 'undefined') {
|
||||||
search.params['beginTime'] = dateRange[0];
|
search.params['beginTime'] = dateRange[0];
|
||||||
search.params['endTime'] = dateRange[1];
|
search.params['endTime'] = dateRange[1];
|
||||||
} else {
|
} else {
|
||||||
search.params['begin' + propName] = dateRange[0];
|
search.params['begin' + propName] = dateRange[0];
|
||||||
search.params['end' + propName] = dateRange[1];
|
search.params['end' + propName] = dateRange[1];
|
||||||
}
|
}
|
||||||
return search;
|
return search;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 回显数据字典
|
// 回显数据字典
|
||||||
export function selectDictLabel(datas, value) {
|
export function selectDictLabel(datas, value) {
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
var actions = [];
|
var actions = [];
|
||||||
Object.keys(datas).some((key) => {
|
Object.keys(datas).some((key) => {
|
||||||
if (datas[key].value == ('' + value)) {
|
if (datas[key].value == ('' + value)) {
|
||||||
actions.push(datas[key].label);
|
actions.push(datas[key].label);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (actions.length === 0) {
|
if (actions.length === 0) {
|
||||||
actions.push(value);
|
actions.push(value);
|
||||||
}
|
}
|
||||||
return actions.join('');
|
return actions.join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 回显数据字典(字符串、数组)
|
// 回显数据字典(字符串、数组)
|
||||||
export function selectDictLabels(datas, value, separator) {
|
export function selectDictLabels(datas, value, separator) {
|
||||||
if (value === undefined || value.length ===0) {
|
if (value === undefined || value.length ===0) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
value = value.join(",");
|
value = value.join(",");
|
||||||
}
|
}
|
||||||
var actions = [];
|
var actions = [];
|
||||||
var currentSeparator = undefined === separator ? "," : separator;
|
var currentSeparator = undefined === separator ? "," : separator;
|
||||||
var temp = value.split(currentSeparator);
|
var temp = value.split(currentSeparator);
|
||||||
Object.keys(value.split(currentSeparator)).some((val) => {
|
Object.keys(value.split(currentSeparator)).some((val) => {
|
||||||
var match = false;
|
var match = false;
|
||||||
Object.keys(datas).some((key) => {
|
Object.keys(datas).some((key) => {
|
||||||
if (datas[key].value == ('' + temp[val])) {
|
if (datas[key].value == ('' + temp[val])) {
|
||||||
actions.push(datas[key].label + currentSeparator);
|
actions.push(datas[key].label + currentSeparator);
|
||||||
match = true;
|
match = true;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (!match) {
|
if (!match) {
|
||||||
actions.push(temp[val] + currentSeparator);
|
actions.push(temp[val] + currentSeparator);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return actions.join('').substring(0, actions.join('').length - 1);
|
return actions.join('').substring(0, actions.join('').length - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 字符串格式化(%s )
|
// 字符串格式化(%s )
|
||||||
export function sprintf(str) {
|
export function sprintf(str) {
|
||||||
var args = arguments, flag = true, i = 1;
|
var args = arguments, flag = true, i = 1;
|
||||||
str = str.replace(/%s/g, function () {
|
str = str.replace(/%s/g, function () {
|
||||||
var arg = args[i++];
|
var arg = args[i++];
|
||||||
if (typeof arg === 'undefined') {
|
if (typeof arg === 'undefined') {
|
||||||
flag = false;
|
flag = false;
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
return arg;
|
return arg;
|
||||||
});
|
});
|
||||||
return flag ? str : '';
|
return flag ? str : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// 转换字符串,undefined,null等转化为""
|
// 转换字符串,undefined,null等转化为""
|
||||||
export function parseStrEmpty(str) {
|
export function parseStrEmpty(str) {
|
||||||
if (!str || str == "undefined" || str == "null") {
|
if (!str || str == "undefined" || str == "null") {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 数据合并
|
// 数据合并
|
||||||
export function mergeRecursive(source, target) {
|
export function mergeRecursive(source, target) {
|
||||||
for (var p in target) {
|
for (var p in target) {
|
||||||
try {
|
try {
|
||||||
if (target[p].constructor == Object) {
|
if (target[p].constructor == Object) {
|
||||||
source[p] = mergeRecursive(source[p], target[p]);
|
source[p] = mergeRecursive(source[p], target[p]);
|
||||||
} else {
|
} else {
|
||||||
source[p] = target[p];
|
source[p] = target[p];
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
source[p] = target[p];
|
source[p] = target[p];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return source;
|
return source;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造树型结构数据
|
* 构造树型结构数据
|
||||||
* @param {*} data 数据源
|
* @param {*} data 数据源
|
||||||
* @param {*} id id字段 默认 'id'
|
* @param {*} id id字段 默认 'id'
|
||||||
* @param {*} parentId 父节点字段 默认 'parentId'
|
* @param {*} parentId 父节点字段 默认 'parentId'
|
||||||
* @param {*} children 孩子节点字段 默认 'children'
|
* @param {*} children 孩子节点字段 默认 'children'
|
||||||
*/
|
*/
|
||||||
export function handleTree(data, id, parentId, children) {
|
export function handleTree(data, id, parentId, children) {
|
||||||
let config = {
|
// 检查 data 是否为数组
|
||||||
id: id || 'id',
|
if (!data || !Array.isArray(data)) {
|
||||||
parentId: parentId || 'parentId',
|
console.warn('handleTree: 期望数组,但收到:', data)
|
||||||
childrenList: children || 'children'
|
return []
|
||||||
};
|
}
|
||||||
|
|
||||||
var childrenListMap = {};
|
let config = {
|
||||||
var tree = [];
|
id: id || 'id',
|
||||||
for (let d of data) {
|
parentId: parentId || 'parentId',
|
||||||
let id = d[config.id];
|
childrenList: children || 'children'
|
||||||
childrenListMap[id] = d;
|
};
|
||||||
if (!d[config.childrenList]) {
|
|
||||||
d[config.childrenList] = [];
|
var childrenListMap = {};
|
||||||
}
|
var tree = [];
|
||||||
}
|
for (let d of data) {
|
||||||
|
let id = d[config.id];
|
||||||
for (let d of data) {
|
childrenListMap[id] = d;
|
||||||
let parentId = d[config.parentId]
|
if (!d[config.childrenList]) {
|
||||||
let parentObj = childrenListMap[parentId]
|
d[config.childrenList] = [];
|
||||||
if (!parentObj) {
|
}
|
||||||
tree.push(d);
|
}
|
||||||
} else {
|
|
||||||
parentObj[config.childrenList].push(d)
|
for (let d of data) {
|
||||||
}
|
let parentId = d[config.parentId]
|
||||||
}
|
let parentObj = childrenListMap[parentId]
|
||||||
return tree;
|
if (!parentObj) {
|
||||||
}
|
tree.push(d);
|
||||||
|
} else {
|
||||||
/**
|
parentObj[config.childrenList].push(d)
|
||||||
* 参数处理
|
}
|
||||||
* @param {*} params 参数
|
}
|
||||||
*/
|
return tree;
|
||||||
export function tansParams(params) {
|
}
|
||||||
let result = ''
|
|
||||||
for (const propName of Object.keys(params)) {
|
/**
|
||||||
const value = params[propName];
|
* 参数处理
|
||||||
var part = encodeURIComponent(propName) + "=";
|
* @param {*} params 参数
|
||||||
if (value !== null && value !== "" && typeof (value) !== "undefined") {
|
*/
|
||||||
if (typeof value === 'object') {
|
export function tansParams(params) {
|
||||||
for (const key of Object.keys(value)) {
|
let result = ''
|
||||||
if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') {
|
for (const propName of Object.keys(params)) {
|
||||||
let params = propName + '[' + key + ']';
|
const value = params[propName];
|
||||||
var subPart = encodeURIComponent(params) + "=";
|
var part = encodeURIComponent(propName) + "=";
|
||||||
result += subPart + encodeURIComponent(value[key]) + "&";
|
if (value !== null && value !== "" && typeof (value) !== "undefined") {
|
||||||
}
|
if (typeof value === 'object') {
|
||||||
}
|
for (const key of Object.keys(value)) {
|
||||||
} else {
|
if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') {
|
||||||
result += part + encodeURIComponent(value) + "&";
|
let params = propName + '[' + key + ']';
|
||||||
}
|
var subPart = encodeURIComponent(params) + "=";
|
||||||
}
|
result += subPart + encodeURIComponent(value[key]) + "&";
|
||||||
}
|
}
|
||||||
return result
|
}
|
||||||
}
|
} else {
|
||||||
|
result += part + encodeURIComponent(value) + "&";
|
||||||
// 返回项目路径
|
}
|
||||||
export function getNormalPath(p) {
|
}
|
||||||
if (p.length === 0 || !p || p == 'undefined') {
|
}
|
||||||
return p
|
return result
|
||||||
};
|
}
|
||||||
let res = p.replace('//', '/')
|
|
||||||
if (res[res.length - 1] === '/') {
|
// 返回项目路径
|
||||||
return res.slice(0, res.length - 1)
|
export function getNormalPath(p) {
|
||||||
}
|
if (p.length === 0 || !p || p == 'undefined') {
|
||||||
return res
|
return p
|
||||||
}
|
};
|
||||||
|
let res = p.replace('//', '/')
|
||||||
// 验证是否为blob格式
|
if (res[res.length - 1] === '/') {
|
||||||
export function blobValidate(data) {
|
return res.slice(0, res.length - 1)
|
||||||
return data.type !== 'application/json'
|
}
|
||||||
}
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证是否为blob格式
|
||||||
|
export function blobValidate(data) {
|
||||||
|
return data.type !== 'application/json'
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -289,7 +289,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup name="Menu">
|
<script setup name="Menu">
|
||||||
import { addMenu, delMenu, getMenu, listMenu, updateMenu } from "@/api/system/menu";
|
import { addMenu, delMenu, getMenu, listMenu, treeselect, updateMenu } from "@/api/system/menu";
|
||||||
import SvgIcon from "@/components/SvgIcon";
|
import SvgIcon from "@/components/SvgIcon";
|
||||||
import IconSelect from "@/components/IconSelect";
|
import IconSelect from "@/components/IconSelect";
|
||||||
|
|
||||||
|
|
@ -333,7 +333,7 @@ function getList() {
|
||||||
/** 查询菜单下拉树结构 */
|
/** 查询菜单下拉树结构 */
|
||||||
function getTreeselect() {
|
function getTreeselect() {
|
||||||
menuOptions.value = [];
|
menuOptions.value = [];
|
||||||
listMenu().then(response => {
|
treeselect().then(response => {
|
||||||
const menu = { menuId: 0, menuName: "主类目", children: [] };
|
const menu = { menuId: 0, menuName: "主类目", children: [] };
|
||||||
menu.children = proxy.handleTree(response.data, "menuId");
|
menu.children = proxy.handleTree(response.data, "menuId");
|
||||||
menuOptions.value.push(menu);
|
menuOptions.value.push(menu);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue