0.0.10-beta03 • Published 12 months ago
vue3-vanilla-tab v0.0.10-beta03
安装
npm install vue3-vanilla-tab
使用
在 view.ts
:
import { IViewList, ViewTypeEnum } from 'vue3-vanilla-tab';
const views: IViewList = [
{
id: '001',
name: 'Home',
title: '首页',
type: ViewTypeEnum.MENU,
path: '/src/views/page-home.vue',
},
{
id: '002',
name: 'Test',
title: '测试',
type: ViewTypeEnum.MENU,
path: '/src/views/page-test.vue'
},
{
id: '003',
title: '测试',
type: ViewTypeEnum.DIR,
children: [
{
id: '004',
name: 'Test2',
title: '测试2',
type: ViewTypeEnum.MENU,
path: '/src/views/page-test2.vue'
},
]
},
{
id: '005',
title: '内联框架测试',
type: ViewTypeEnum.IFRAME,
link: 'http://139.186.198.135/'
}
];
export default views;
在 main.ts
:
import { createApp } from 'vue';
import App from './App.vue';
import views from './views';
import 'vue3-vanilla-tab/style.css';
import { createDynamicTabs } from 'vue3-vanilla-tab';
const vanillaTab = createVanillaTab({
modules: import.meta.glob('/src/views/page-*.vue'),
cache: {
encryption: {
enable: false,
key: '_11111020002111@',
iv: '@11111000302111_'
}
},
homeView: (viewTree) => viewTree[0]
});
createApp(App).use(vanillaTab).mount('#app');
// 登录成功时获取后端菜单或常量菜单调用此方法初始化
vanillaTab.initDynamicViews(views);
// 退出系统时记得释放缓存
// vanillaTab.destory();
在dynamic-crumbs.vue
面包屑组件(参考):
<template>
<a-breadcrumb>
<a-breadcrumb-item v-for="crumb in getCrumbs">
<a>{{ crumb.title }}</a>
<template #overlay v-if="crumb.children && crumb.children.length > 0">
<a-menu @click="onContextMenuClick">
<template v-for="item in crumb?.children">
<a-menu-item v-if="item.type !== ViewTypeEnum.DIR" :key="item.id">
<a>{{ item.title }}</a>
</a-menu-item>
</template>
</a-menu>
</template>
</a-breadcrumb-item>
</a-breadcrumb>
</template>
<script setup lang="ts" name="DynamicCrumbs">
import { message } from 'ant-design-vue';
import { useAttribute, useTab, ViewTypeEnum } from 'vue3-vanilla-tab';
const {
getCrumbs,
getViewList,
} = useAttribute();
const { openTab } = useTab();
const onContextMenuClick = ({ key }) => {
try {
const findView = getViewList.value.find(item => item.id == key);
if (findView) {
const { id, props, title } = findView;
openTab(id, props as any, { title });
}
} catch (error) {
if (error) {
message.error(error.message);
}
}
}
</script>
在dynamic-tabs.vue
动态标签页组件(参考):
<template>
<a-tabs class="dynamic-tabs" type="editable-card" hide-add :activeKey="getActiveTab?.id" @tabClick="onTabSelect"
@edit="onTabClose">
<template #rightExtra>
<div :style="{ height: '100%', display: 'flex', alignItems: 'center' }">
<a-space :style="{ marginRight: '8px' }" :size="4">
<a-button shape="circle" @click="refreshTabAll()">
<template #icon>
<RetweetOutlined />
</template>
</a-button>
</a-space>
</div>
</template>
<a-tab-pane v-for="(tab, index) in getTabList" :key="tab.id" :closable="index > 0">
<template #tab>
<a-dropdown :trigger="['contextmenu']">
<div>{{ tab.title }}</div>
<template #overlay>
<a-menu @click="({ key: menuKey }) => onContextMenuClick(tab.id, menuKey)">
<a-menu-item key="refresh">
<ReloadOutlined :style="{ marginRight: '8px' }" />
<span>刷新</span>
</a-menu-item>
<a-menu-item key="close-left">
<ArrowLeftOutlined :style="{ marginRight: '8px' }" />
<span>关闭左侧</span>
</a-menu-item>
<a-menu-item key="close-right">
<ArrowRightOutlined :style="{ marginRight: '8px' }" />
<span>关闭右侧</span>
</a-menu-item>
<a-menu-item key="close-other">
<CloseOutlined :style="{ marginRight: '8px' }" />
<span>关闭其它</span>
</a-menu-item>
<a-menu-divider />
<a-menu-item key="close-all">
<RollbackOutlined :style="{ marginRight: '8px' }" />
<span>全部关闭</span>
</a-menu-item>
<a-menu-item key="refresh-all">
<RetweetOutlined :style="{ marginRight: '8px' }" />
<span>全部刷新</span>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</template>
</a-tab-pane>
</a-tabs>
</template>
<script setup lang="ts" name="DynamicTabs">
import { message } from 'ant-design-vue';
import { useAttribute, useTab } from 'vue3-vanilla-tab';
const {
getTabList,
getActiveTab,
} = useAttribute();
const {
openTab,
closeTab,
refreshTab,
refreshTabAll,
closeTabsByLeft,
closeTabsByRight,
closeTabsByOther,
closeTabByAll,
} = useTab();
/**
* 事件 - tab页右键菜单使用
* @param tabKey tab页id(uuid)
* @param menuEvent 自定义菜单事件
*/
const onContextMenuClick = async (tabKey: string, menuEvent: string) => {
try {
switch (menuEvent) {
case 'refresh':
refreshTab(tabKey);
break;
case 'close-left':
closeTabsByLeft(tabKey);
break;
case 'close-right':
closeTabsByRight(tabKey);
break;
case 'close-other':
closeTabsByOther(tabKey);
break;
case 'close-all':
closeTabByAll();
break;
case 'refresh-all':
refreshTabAll();
break;
}
} catch (error) {
if (error) {
message.error(error.message);
}
}
}
/**
* 事件 - tab页被点击调用
* @param tabKey tab页id(uuid)
*/
const onTabSelect = (tabKey: string) => {
try {
const findTab = getTabList.value.find(tab => tab.id === tabKey);
if (findTab) {
const { viewId, props, title } = findTab;
openTab(viewId, props, { title });
}
} catch (error) {
if (error) {
message.error(error.message);
}
}
}
/**
* 事件 - tab页被关闭时调用
* @param tabKey tab页id(uuid)
*/
const onTabClose = (tabKey: string) => {
try {
closeTab(tabKey);
} catch (error) {
if (error) {
message.error(error.message);
}
}
}
</script>
在dynamic-menu.vue
动态菜单组件(参考):
<template>
<a-menu mode="inline" :openKeys="getOpenMenuKeys" :selectedKeys="[getActiveTab?.viewId]" @select="onMenuItemSelect">
<template v-for="view in getViewTree">
<!--menu-->
<template v-if="view.children.length <= 0">
<a-menu-item :key="view?.id" v-if="!view.hidden">
<template #icon>
<component v-if="view.icon" :is="view.icon" />
</template>
<span>{{ view.title }}</span>
</a-menu-item>
</template>
<!--subMenu-->
<template v-else>
<DynamicMenuItem :view="view" />
</template>
</template>
</a-menu>
</template>
<script lang="ts" setup name="DynamicMenu">
import { message } from 'ant-design-vue';
import DynamicMenuItem from './item.vue';
import { useTab, useAttribute } from 'vue3-vanilla-tab';
const {
getOpenMenuKeys,
getActiveTab,
getViewTree,
getViewList,
} = useAttribute();
const { openTab } = useTab();
const onMenuItemSelect = ({ key }) => {
try {
const findView = getViewList.value.find(item => item.id == key);
if (findView) {
const { id, props, title } = findView;
openTab(id, props as any, { title });
}
} catch (error) {
message.error(error.message);
}
}
</script>
<!-- DynamicMenuItem -->
<template>
<a-sub-menu :key="view.id">
<template #icon>
<component v-if="view.icon" :is="view.icon" />
</template>
<template #title>{{ view.title }}</template>
<template v-for="item in view.children">
<template v-if="item.children.length <= 0">
<a-menu-item :key="item.id" v-if="!item.hidden">
<template #icon>
<component v-if="item.icon" :is="item.icon" />
</template>
{{ item.title }}
</a-menu-item>
</template>
<template v-else>
<DynamicMenuItem :view="item" />
</template>
</template>
</a-sub-menu>
</template>
<script lang="ts" setup>
import DynamicMenuItem from './item.vue';
import { toRefs } from 'vue';
import { View } from 'vue3-vanilla-tab';
const props = defineProps<{
view: View
}>();
const { view } = toRefs(props);
</script>
在container.vue
容器组件(参考):
<template>
<article>
<header>
<DynamicCrumbs />
</header>
<aside>
<DynamicMenu />
</aside>
<nav>
<DynamicTabs />
</nav>
<main>
<DynamicContent />
</main>
</article>
</template>
<script setup lang="ts">
import DynamicCrumbs from './dynamic-crumbs.vue';
import DynamicTabs from './dynamic-tabs.vue';
import DynamicMenu from './dynamic-menu/index.vue';
import { DynamicContent } from 'vue3-vanilla-tab';
</script>
选项
export interface ITabConfig {
/**
* 缓存配置
*/
cache?: ICacheConfig,
/**
* 最大缓存tab页数量
*/
maxKeepTab?: number;
/**
* 组件模块
*/
modules: Record<string, () => Promise<unknown>>;
/**
* 组件资源配置
*/
source?: Partial<AsyncComponentOptions>,
/**
* 过渡动画配置
*/
animate?: TransitionEnum | string;
/**
* 默认加载首页
*/
homeView: (viewTree: ViewList) => View;
}
export interface ICacheConfig {
/**
* 缓存方式
*/
storage?: Storage;
/**
* 缓存前缀
*/
prefixKey?: string;
/**
* 缓存超时时间
*/
timeout?: number;
/**
* 缓存加密配置
*/
encryption?: {
/**
* 是否启用
*/
enable: boolean;
/**
* 密钥(16位)
*/
key: string;
/**
* 偏移量(16位)
*/
iv: string;
};
}
export enum TransitionEnum {
/**
* 渐变
*/
ZOOM_FADE = 'zoom-fade',
/**
* 闪现
*/
ZOOM_OUT = 'zoom-out',
/**
* 滑动
*/
FADE_SLIDE = 'fade-slide',
/**
* 消退
*/
FADE = 'fade',
/**
* 底部消退
*/
FADE_BOTTOM = 'fade-bottom',
/**
* 缩放消退
*/
FADE_SCALE = 'fade-scale'
}
菜单设计参考
export class View {
/**
* 主键
*/
id?: string;
/**
* 菜单名称
*/
title: string;
/**
* 菜单类型
*/
type: ViewTypeEnum;
/**
* 组件名称
*/
name?: string;
/**
* 组件路径
*/
path?: string;
/**
* 是否显示在菜单栏
*/
hidden?: boolean;
/**
* 是否进行缓存
*/
cache?: boolean;
/**
* 是否打开多标签
*/
single?: boolean;
/**
* 菜单图标
*/
icon?: string;
/**
* 组件参数
*/
props?: string;
/**
* 超链接
*/
link?: string;
}
export enum ViewTypeEnum {
/**
* 目录
*/
DIR,
/**
* 菜单
*/
MENU,
/**
* 内链
*/
IFRAME,
/**
* 外链
*/
LINK
}
示例
AntDesignVue组件库Demo 点我访问
小技巧
- 怎么在vue3中获取当前标签页的Id值?
const tabId = getCurrentInstance().vnode.key as string
;
更新日志
0.0.10-beta03
- 修复Dropdown,Menu组件动画失效问题。
0.0.10-beta02
- 修复打开内联或外联时报错组件未注册问题。
- DynamicTabs实例中导出hasCacheViews, hasCacheOpenTabs属性分别用于判断是否已经缓存视图列表和打开的标签页。
0.0.10-beta01
- 将antd组件库中以下组件集成,用法与组件库保持一致。
- Tabs,
- TabPane,
- Dropdown,
- Menu,
- MenuItem,
- SubMenu,
- MenuDivider
- Breadcrumb,
- BreadcrumbItem,
- BreadcrumbSeparator
0.0.10
- useTab中新增setTabProps方法用于设置当前标签页的参数。
0.0.9
- useTab中新增getTabById, getParentTabById, refreshParentTab方法分别用于通过tab页的Id查到当前Tab页,当前Tab页的父级Tab页和刷新指定标签页的父级标签页。
- useTab中新增redirectTab方法用于关闭指定/当前标签页后重定向到指定标签页。
- useTab中新增closeTabAndRefreshParentTab方法用于关闭指定/当前标签页后同时刷新父级标签页
0.0.8
- closeTab不能关闭首页。
- IMenuType类型中name改为非必填,未填入时自动将id作为name。
0.0.7
- 优化DynamicTabs实例中initDynamicViews方法,自动清空之前打开的标签页和视图树后重新装载。
- 移除utils中getComponentName方法。
- 移除错误前缀vue3-vanilla-tab error,
待做成配置项
。- useAttribute中新增isCacheViews, isCacheOpenTabs, getShowViewList三个属性分别用于判断是否已经缓存视图列表,已经打开的标签页和hidden属性为false的视图列表。
- DynamicTabs实例中新增findViewByViewId, openTab方法用于通过视图ID找到对应的视图和打开标签页(拦截openTab方法可以做打开之前的校验)。
- 导出eachTree, findTreeNode方法分别用于遍历树和查找树节点。
0.0.6
- DynamicTabs实例中新增getTabById, getComponentNameByViewId, getComponentByName, getComponentByViewId方法。
- useAttribute中新增getVueApp, getVueComponents分别用于获取vue实例和全局组件。
0.0.5
- 将openTab方法返回值改为tabId,用于closeTab方法关闭标签页。
- 修复openTab方法传入props字符串不是标准的json格式报错问题。
0.0.4
- 将getCacheItem, setCacheItem, removeCacheItem, clearCache从useTab中抽离组成新的useCache导出。
- 导出decodeByBase64, encryptByBase64, encryptByMd5三个方法。
0.0.10-beta03
12 months ago
0.0.11
12 months ago
0.0.10-beta02
12 months ago
0.0.10-beta01
12 months ago
0.0.10
1 year ago
0.0.9
1 year ago
0.0.8
1 year ago
0.0.7
1 year ago
0.0.6
1 year ago
0.0.5
1 year ago
0.0.4
1 year ago
0.0.3
1 year ago
0.0.2
1 year ago
0.0.1
1 year ago