多标签页很多公司的后台管理系统都会有这个需求,之前用vue一般架子也是带的,现在公司用了antd pro ,看了下官方不支持,确实会影响性能,但是架不住需求。
先看下远古截图:
https://github.com/ant-design/ant-design-pro/issues/220
17年提出的需求现在还没有实现,看样官方也是铁了心了。在看看提供的其他解决方案:
另外我还找到了一个插件也可以实现:
https://github.com/fangzhengjin/umi-plugin-panel-tabs
这些不是因为这版本不对,就是细节不满意决定自己实现一个版本。
自己实现的好处:
先看最后实现的版本:
核心问题:
核心实现思路:
具体逻辑就是,写一个TabsView 组件,在Layout chlidren的时候嵌套上Tabs 多页签卡这一层。
Layout 文件夹Index.ts 文件:
<TabsView
activeKey={getActiveKey(props.tagsModel)}
tags={props.tagsModel}
route={props.route}
dispatch={props.dispatch}
/>
利用dva 来实现tags 数组的增删改查,具体代码如下:
/*
* @Author: ZY
* @Date: 2021-10-25 13:42:43
* @LastEditors: ZY
* @LastEditTime: 2022-05-01 10:18:48
* @Description: dva tags
* tabs 整理设计思路:
* 需求:两种逻辑,一种是菜单功能,功能、路由、tag是一对一的关系,另一种单据类可以开多个
* 设计:
* 利用tabs 进行页面布局,来实现缓存的目的。key是path和query的合集,这样能满足需求
* 利用dva组织数据
* 动态加载组件,组件利用key关联
*/
import type { Reducer } from 'umi';
import _remove from 'lodash/remove';
import _cloneDeep from 'lodash/cloneDeep';
import _findIndex from 'lodash/findIndex';
export interface Tag {
key: string;
title: string;
path?: string;
active: boolean;
query?: any;
}
export type TagsStateType = Tag[];
export interface TagsModelType {
namespace: 'tagsModel';
state: TagsStateType;
reducers: {
addTag: Reducer<TagsStateType>;
updateActive: Reducer<TagsStateType>;
removeTag: Reducer<TagsStateType>;
removeAllTags: Reducer<TagsStateType>;
};
}
/**
* @description: 初始化tab
* @param {*}
* @return {*}
*/
const homeTag: Tag = {
key: `/dashboard`,
title: '首页',
active: true,
path: '/dashboard',
};
/**
* @description:
* @param {*}
* @return {*}
*/
const addNewTag = (tags: Tag[], newTag: Tag) => {
if (_findIndex(tags, (t) => t.key === newTag.key) === -1) {
// tags数组里面有没有新增的tag
const cTags = tags.map((t) => {
const ct = _cloneDeep(t);
ct.active = false;
return ct;
});
return [...cTags, newTag];
}
// 新增tag 在数组中,选中即可。
const cTags = tags.map((t) => {
const ct = _cloneDeep(t);
ct.active = ct.key === newTag.key;
return ct;
});
return cTags;
};
const TagsModel: TagsModelType = {
namespace: 'tagsModel',
state: [homeTag] as TagsStateType,
reducers: {
addTag: (state, action) => {
if (state) {
return addNewTag(state, action.payload);
}
return [];
},
updateActive: (state, action) => {
if (!state) {
return [];
}
const cTags = state.map((t) => {
const ct = _cloneDeep(t);
ct.active = ct.key === action.payload;
return ct;
});
return [...cTags];
},
removeTag: (state, action) => {
if (!state) {
return [];
}
const ct = _cloneDeep(state);
if (ct.filter((t) => t.active)[0].key === action.payload) {
_remove(ct, (tag: Tag) => tag.key === action.payload);
// 如果关闭的是当前选中的标签,默认选中最后一个的策略
ct[ct.length - 1].active = true;
} else {
_remove(ct, (tag: Tag) => tag.key === action.payload);
}
return [...ct];
},
removeAllTags: () => {
return [];
},
},
};
export default TagsModel;
这里会遍历tags 数组,然后创建tab, 每一个tab 都用Route 标签缓存,通过路径匹配的组件。
<Tabs
activeKey={activeKey}
type="editable-card"
hideAdd={true}
onEdit={tabOnEdit}
onChange={tabOnChange}
onTabClick={onTabClick}
>
{tags.length &&
tags.map((tag) => {
return (
<TabPane tab={tag.title} key={tag.key} closable={tag.key !== '/dashboard'}>
<Route key={tag.key} component={getPathComponent(tag.path!)} exact />
</TabPane>
);
})}
</Tabs>
这样核心内容基本讲完,边缘代码就不多赘述了,代码再项目里还没来得及抽取,如有需要以后找时间发出。