新功能:

1.部门显示组件
This commit is contained in:
猿小天
2023-12-27 19:08:55 +08:00
parent 6146e6edb6
commit 7f184b2a9a
9 changed files with 322 additions and 56 deletions

View File

@@ -0,0 +1,28 @@
<template>
<div>
{{ data }}
</div>
</template>
<script setup lang="ts">
import {defineProps,ref,watch} from 'vue'
import {useDeptInfoStore} from '/@/stores/modules/dept'
const props = defineProps({
modelValue:{
type: Number || String
}
})
const data = ref()
watch(()=>{
return props.modelValue
},async (newVal)=>{
const deptInfoStore = useDeptInfoStore()
const result = await deptInfoStore.getParentDeptById(newVal)
if(result?.nodes){
let name = ""
result.nodes.forEach((item:any,index:number)=>{
name += index>0?`/${item.name}`:item.name
})
data.value = name
}
},{immediate: true} )
</script>

View File

@@ -13,7 +13,8 @@ import { useMenuApi } from '/@/api/menu/index';
import { handleMenu } from '../utils/menu';
import { BtnPermissionStore } from '/@/plugin/permission/store.permission';
import {SystemConfigStore} from "/@/stores/systemConfig";
import {useDeptInfoStore} from "/@/stores/modules/dept";
import {DictionaryStore} from "/@/stores/dictionary";
const menuApi = useMenuApi();
const layouModules: any = import.meta.glob('../layout/routerView/*.{vue,tsx}');
@@ -112,6 +113,10 @@ export function getBackEndControlRoutes() {
BtnPermissionStore().getBtnPermissionStore();
// 获取系统配置
SystemConfigStore().getSystemConfigs()
// 获取所有部门信息
useDeptInfoStore().requestDeptInfo()
// 获取字典信息
DictionaryStore().getSystemDictionarys()
return menuApi.getSystemMenu();
}

View File

@@ -10,6 +10,7 @@ import {Session} from '/@/utils/storage';
import {notFoundAndNoPower,staticRoutes} from '/@/router/route';
import {initFrontEndControlRoutes} from '/@/router/frontEnd';
import {initBackEndControlRoutes} from '/@/router/backEnd';
import {useFrontendMenuStore} from "/@/stores/frontendMenu";
/**
* 1、前端控制路由时isRequestRoutes 为 false需要写 roles需要走 setFilterRoute 方法。
@@ -107,6 +108,7 @@ router.beforeEach(async (to, from, next) => {
next('/home');
NProgress.done();
} else {
const storesRoutesList = useRoutesList(pinia);
const {routesList} = storeToRefs(storesRoutesList);
if (routesList.value.length === 0) {
@@ -115,6 +117,8 @@ router.beforeEach(async (to, from, next) => {
await initBackEndControlRoutes();
// 解决刷新时,一直跳 404 页面问题,关联问题 No match found for location with path 'xxx'
// to.query 防止页面刷新时普通路由带参数时参数丢失。动态路由xxx/:id/:name"isDynamic 无需处理
console.log("缓存的路由",routesList.value)
console.log("所有路由",router.getRoutes())
next({ path: to.path, query: to.query });
} else {
// https://gitee.com/lyt-top/vue-next-admin/issues/I5F1HP

View File

@@ -89,19 +89,11 @@ export const staticRoutes: Array<RouteRecordRaw> = [
},
},
{
path: '/operationLog',
name: 'operationLog',
component: () => import('/@/views/system/personal/index.vue'),
path: '/demo',
name: 'demo',
component: () => import('/@/views/system/demo/index.vue'),
meta: {
title: 'message.router.personal'
},
},
// {
// path: '/demo',
// name: 'demo',
// component: () => import('/@/views/system/demo/index.vue'),
// meta: {
// title: 'message.router.personal'
// },
// }
}
];

View File

@@ -0,0 +1,159 @@
import {defineStore} from 'pinia';
import {FrontendMenu} from './interface';
import {Session} from '/@/utils/storage';
import {request} from '../utils/service';
import XEUtils from "xe-utils";
import {RouteRecordRaw} from "vue-router";
const layouModules: any = import.meta.glob('../layout/routerView/*.{vue,tsx}');
const viewsModules: any = import.meta.glob('../views/**/*.{vue,tsx}');
/**
* 获取目录下的 .vue、.tsx 全部文件
* @method import.meta.glob
* @link 参考https://cn.vitejs.dev/guide/features.html#json
*/
const dynamicViewsModules: Record<string, Function> = Object.assign({}, { ...layouModules }, { ...viewsModules });
/**
* 后端路由 component 转换函数
* @param dynamicViewsModules 获取目录下的 .vue、.tsx 全部文件
* @param component 当前要处理项 component
* @returns 返回处理成函数后的 component
*/
export function dynamicImport(dynamicViewsModules: Record<string, Function>, component: string) {
const keys = Object.keys(dynamicViewsModules);
const matchKeys = keys.filter((key) => {
const k = key.replace(/..\/views|../, '');
return k.startsWith(`${component}`) || k.startsWith(`/${component}`);
});
if (matchKeys?.length === 1) {
const matchKey = matchKeys[0];
return dynamicViewsModules[matchKey];
}
if (matchKeys?.length > 1) {
return false;
}
}
/**
* @description: 处理后端菜单数据格式
* @param {Array} menuData
* @return {*}
*/
export const handleMenu = (menuData: Array<any>) => {
// 框架内路由
const frameInRoutes:Array<any> = []
// 框架外路由
const frameOutRoutes:Array<any> = []
// 先处理menu meta数据转换
const handleMeta = (item: any) => {
item.path = item.web_path
item.meta = {
title: item.title,
isLink: item.link_url,
isHide: !item.visible,
isKeepAlive: item.cache,
isAffix: item.is_affix,
isIframe: item.is_iframe,
roles: ['admin'],
icon: item.icon
}
item.component = dynamicImport(dynamicViewsModules, item.component as string)
if(item.is_catalog){
// 对目录的处理
item.component = dynamicImport(dynamicViewsModules, 'layout/routerView/parent')
}
if(item.is_link){
// 对外链接的处理
item.meta.isIframe = !item.is_iframe
if(item.is_iframe){
item.component = dynamicImport(dynamicViewsModules, 'layout/routerView/link')
}else {
item.component = dynamicImport(dynamicViewsModules, 'layout/routerView/iframes')
}
}else{
if(item.is_iframe){
const route = JSON.parse(JSON.stringify(item))
route.meta.isLink = ''
route.path = `${item.web_path}`
route.name = `${item.name}`
route.meta.isIframe = true
route.meta.isKeepAlive = false
route.meta.isIframeOpen = true
route.component = item.component
frameOutRoutes.push(route)
item.path = `${item.web_path}FrameOut`
item.name = `frameOut_${item.name}`
item.meta.isLink = item.web_path
item.meta.isIframe = !item.is_iframe
item.component = dynamicImport(dynamicViewsModules, 'layout/routerView/link.vue')
}
}
item.children && handleMeta(item.children);
return item
}
menuData.forEach((val) => {
frameInRoutes.push(handleMeta(val))
})
const data = XEUtils.toArrayTree(frameInRoutes, {
parentKey: 'parent',
strict: true,
})
const dynamicRoutes = [
{
path: '/home', name: 'home',
component: '/system/home/index',
meta: {
title: 'message.router.home',
isLink: '',
isHide: false,
isKeepAlive: true,
isAffix: true,
isIframe: false,
roles: ['admin'],
icon: 'iconfont icon-shouye'
}
},
...data
]
return {frameIn:dynamicRoutes,frameOut:frameOutRoutes}
}
export const useFrontendMenuStore = defineStore('frontendMenu',{
state: (): FrontendMenu => ({
arrayRouter: [],
treeRouter: [],
frameInRoutes:[],
frameOutRoutes:[]
}),
actions:{
async requestMenu(){
return request({
url: '/api/system/menu/web_router/',
method: 'get',
params:{},
}).then((res:any)=>{
return res.data
});
},
async handleRouter(){
const menuData = await this.requestMenu();
this.arrayRouter = menuData
const {frameIn,frameOut} = handleMenu(menuData);
this.treeRouter = [...frameIn,...frameOut]
this.frameInRoutes=frameIn
this.frameOutRoutes=frameOut
},
async getRouter(){
await this.handleRouter()
return {
frameInRoutes:this.frameInRoutes,
frameOutRoutes:this.frameOutRoutes,
treeRouter:this.treeRouter
}
}
}
})

View File

@@ -2,6 +2,7 @@
* 定义接口来定义对象的类型
* `stores` 全部类型定义在这里
*/
import {useFrontendMenuStore} from "/@/stores/frontendMenu";
// 用户信息
export interface UserInfosState {
@@ -102,3 +103,12 @@ export interface DictionaryStates {
export interface ConfigStates {
systemConfig: any;
}
export interface FrontendMenu {
arrayRouter: Array<any>;
treeRouter:Array<any>;
frameOutRoutes:Array<any>;
frameInRoutes:Array<any>;
}

View File

@@ -0,0 +1,30 @@
import {defineStore} from "pinia";
import {request} from "/@/utils/service";
import XEUtils from "xe-utils";
import {toRaw} from 'vue'
export const useDeptInfoStore = defineStore('deptInfo', {
state:()=>(
{
list:[],
tree:[],
}
),
actions:{
async requestDeptInfo() {
// 请求部门信息
const ret = await request({
url: '/api/system/dept/all_dept/'
})
this.list = ret.data
this.tree = XEUtils.toArrayTree(ret.data,{parentKey:'parent',strict:true})
},
async getDeptById(id:any){
},
async getParentDeptById(id: any){
const tree = toRaw(this.tree)
const obj = XEUtils.findTree(tree, item => item.id == id)
return obj
}
}
})

View File

@@ -1,4 +1,6 @@
import { dict } from "@fast-crud/fast-crud";
import {shallowRef} from 'vue'
import deptFormat from "/@/components/dept-format/index.vue";
export const commonCrudConfig = (options = {
create_datetime: {
form: false,
@@ -32,6 +34,48 @@ export const commonCrudConfig = (options = {
},
}) => {
return {
dept_belong_id: {
title: '所属部门',
type: 'dict-cascader',
search: {
show: false
},
dict: dict({
url: '/api/system/dept/all_dept/',
isTree: true,
value: 'id',
label: 'name',
children: 'children',
}),
column: {
align: 'center',
width: 200,
show: options.dept_belong_id?.table || false,
component:{
name: shallowRef(deptFormat),
vModel: "modelValue",
}
},
form: {
show: options.dept_belong_id?.form || false,
component: {
multiple: false,
clearable: true,
props: {
showAllLevels:false,
props: {
// 为什么这里要写两层props
// 因为props属性名与fs的动态渲染的props命名冲突所以要多写一层
label: "name",
value: "id",
checkStrictly: true,
emitPath:false
}
}
},
helper: "默认不填则为当前创建用户的部门ID"
}
},
description: {
title: '备注',
search: {
@@ -39,15 +83,19 @@ export const commonCrudConfig = (options = {
},
type: 'textarea',
column: {
width: 100,
show: options.description?.table || false,
},
form: {
show: options.description?.form || false,
component: {
show: options.description?.form || false,
placeholder: '请输入内容',
showWordLimit: true,
maxlength: '200',
}
},
viewForm: {
show: true
}
},
modifier_name: {
@@ -58,6 +106,28 @@ export const commonCrudConfig = (options = {
column: {
width: 100,
show: options.modifier_name?.table || false,
},
form: {
show: false,
},
viewForm: {
show: true
}
},
creator_name: {
title: '创建人',
search: {
show: options.creator_name?.search || false
},
column: {
width: 100,
show: options.creator_name?.table || false,
},
form: {
show: false,
},
viewForm: {
show: true
}
},
update_datetime: {
@@ -69,16 +139,12 @@ export const commonCrudConfig = (options = {
column: {
width: 160,
show: options.update_datetime?.table || false,
}
},
creator_name: {
title: '创建人',
search: {
show: options.creator_name?.search || false
},
column: {
width: 100,
show: options.creator_name?.table || false,
form: {
show: false,
},
viewForm: {
show: true
}
},
create_datetime: {
@@ -90,40 +156,12 @@ export const commonCrudConfig = (options = {
column: {
width: 160,
show: options.create_datetime?.table || false,
}
},
dept_belong_id: {
title: '所属部门',
type: 'dict-tree',
search: {
show: false
},
dict: dict({
url: '/api/system/dept/all_dept/',
isTree: true,
value: 'id',
label: 'name',
children: 'children' // 数据字典中children字段的属性名
}),
column: {
width: 150,
show: options.dept_belong_id?.table || false,
},
form: {
component: {
show: options.dept_belong_id?.form || false,
multiple: false,
clearable: true,
props: {
props: {
// 为什么这里要写两层props
// 因为props属性名与fs的动态渲染的props命名冲突所以要多写一层
label: "name",
value: "id",
}
}
},
helper: "默认不填则为当前创建用户的部门ID"
show: false,
},
viewForm: {
show: true
}
}
}