| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342 |
- <template>
- <div class="work-tab-container">
- <div class="work-tabs">
- <a-tabs v-model:activeKey="activeKey" type="editable-card" :hide-add="true" @edit="onEdit" @change="onChange">
- <a-tab-pane v-for="tab in tabs" :key="tab.key" :tab="tab.title" :closable="tab.closable" />
- </a-tabs>
- </div>
- </div>
- </template>
- <script setup>
- import { ref, watch, onMounted, nextTick, defineExpose, defineProps } from 'vue';
- import { useRouter, useRoute } from 'vue-router';
- import CurdWindowResourceV2 from '../api/dic/CurdWindowResourceV2.js';
- import { menuClickedEvent } from '../common/eventBus.js';
- import { Uuid } from 'pc-component-v3';
- const props = defineProps({
- menuWidth: {
- type: Number,
- default: 230,
- },
- });
- const router = useRouter();
- const route = useRoute();
- // 标签页数组
- const tabs = ref([
- {
- key: '/desktop/dashboard',
- title: '首页',
- path: '/desktop/dashboard',
- closable: false, // 首页不可关闭
- },
- ]);
- // 当前激活的标签页
- const activeKey = ref('/desktop/dashboard');
- // 菜单节点映射表 - 存储路径与菜单节点的对应关系
- const pathMenuMap = ref({});
- // 初始化标签页
- onMounted(() => {
- // 尝试从本地存储中恢复标签页
- const savedTabs = localStorage.getItem('workTabs');
- if (savedTabs) {
- try {
- const parsedTabs = JSON.parse(savedTabs);
- if (Array.isArray(parsedTabs) && parsedTabs.length > 0) {
- // 确保首页总是存在
- if (!parsedTabs.some(tab => tab.path === '/desktop/dashboard')) {
- parsedTabs.unshift({
- key: '/desktop/dashboard',
- title: '首页',
- path: '/desktop/dashboard',
- closable: false,
- });
- }
- tabs.value = parsedTabs;
- }
- } catch (e) {
- console.error('恢复标签页失败', e);
- }
- }
- // 如果当前路由不是首页,添加当前路由的标签页
- if (route.path !== '/desktop/dashboard') {
- setTimeout(() => {
- addTab(route.path);
- }, 100);
- }
- });
- // 添加标签页方法
- const addTab = async path => {
- // 如果标签页已存在,则激活该标签页
- const existTab = tabs.value.find(tab => tab.path === path);
- if (existTab) {
- activeKey.value = existTab.key;
- return;
- }
- // 尝试获取菜单标题
- let title = await getMenuTitle(path);
- // if (!title) {
- // // 如果没有找到菜单标题,则从路由元数据中获取
- // const matchedRoute = router.getRoutes().find(route => route.path === path);
- // if (matchedRoute && matchedRoute.meta && matchedRoute.meta.title) {
- // title = matchedRoute.meta.title;
- // } else {
- // // 如果路由也没有定义标题,则从路径中提取
- // const pathSegments = path.split('/');
- // title = pathSegments[pathSegments.length - 1] || '未命名页面';
- // }
- // }
- if (title) {
- // 创建新标签页
- const newTab = {
- key: path,
- title: title,
- path: path,
- closable: true, // 非首页标签可以关闭
- };
- tabs.value.push(newTab);
- activeKey.value = path;
- // 保存标签页到本地存储
- saveTabsToLocalStorage();
- }
- };
- // 获取菜单标题
- const getMenuTitle = async path => {
- // 如果有缓存的菜单信息,直接使用
- if (pathMenuMap.value[path]) {
- return pathMenuMap.value[path].title;
- }
- // 处理不同类型的路由
- if (path.includes('/desktop/window/') || path.includes('/desktop/window1/')) {
- const windowNo = extractWindowNo(path);
- if (windowNo) {
- try {
- const res = await CurdWindowResourceV2.uniqueByNoAccessControl(windowNo);
- if (res.errorCode === 0 && res.data) {
- return res.data.name;
- }
- } catch (error) {
- console.error('获取窗口标题失败', error);
- }
- }
- } else if (path.includes('/desktop/sheetWindow/')) {
- const windowNo = extractWindowNo(path);
- if (windowNo) {
- // 这里可以添加获取SheetWindow标题的逻辑
- return `表格窗口-${windowNo}`;
- }
- } else if (path.includes('/desktop/info/')) {
- const infoWindowNo = extractWindowNo(path);
- if (infoWindowNo) {
- // 这里可以添加获取Info窗口标题的逻辑
- return `信息窗口-${infoWindowNo}`;
- }
- }
- return '';
- };
- // 从路径中提取窗口编号
- const extractWindowNo = path => {
- const segments = path.split('/');
- for (let i = 0; i < segments.length; i++) {
- if (segments[i] === 'window' || segments[i] === 'window1' || segments[i] === 'sheetWindow' || segments[i] === 'info') {
- return segments[i + 1]?.split('?')[0];
- }
- }
- return null;
- };
- // 编辑标签页(关闭)
- const onEdit = (targetKey, action) => {
- if (action === 'remove') {
- removeTab(targetKey);
- }
- };
- // 移除标签页
- const removeTab = targetKey => {
- // 找到当前关闭的标签页索引
- const targetIndex = tabs.value.findIndex(tab => tab.key === targetKey);
- // 从数组中移除该标签页
- tabs.value = tabs.value.filter(tab => tab.key !== targetKey);
- // 如果关闭的是当前激活的标签页,则需要激活其他标签页
- if (targetKey === activeKey.value) {
- // 如果关闭的是最后一个标签页,则激活前一个标签页
- if (targetIndex === tabs.value.length) {
- activeKey.value = tabs.value[tabs.value.length - 1].key;
- } else {
- // 否则激活后一个标签页
- activeKey.value = tabs.value[targetIndex].key;
- }
- console.log(activeKey.value, 'activeKey.value');
- // 跳转到新激活标签页对应的路由
- if (activeKey.value.includes('/desktop/window1/')) {
- router.push(activeKey.value + '?uuid=' + Uuid.createUUID());
- } else {
- router.push(activeKey.value);
- }
- }
- // 保存标签页到本地存储
- saveTabsToLocalStorage();
- };
- // 切换标签页
- const onChange = newActiveKey => {
- let uidWindow = newActiveKey;
- if (newActiveKey.includes('/desktop/window1/')) {
- uidWindow = newActiveKey + '?uuid=' + Uuid.createUUID();
- }
- activeKey.value = newActiveKey;
- router.push(uidWindow);
- };
- // 保存标签页到本地存储
- const saveTabsToLocalStorage = () => {
- try {
- localStorage.setItem('workTabs', JSON.stringify(tabs.value));
- } catch (e) {
- console.error('保存标签页失败', e);
- }
- };
- // 监听菜单点击事件,添加菜单信息到pathMenuMap
- watch(menuClickedEvent, event => {
- if (event && event.path && event.menuInfo) {
- pathMenuMap.value[event.path] = event.menuInfo;
- }
- }, { deep: true, immediate: true });
- // 监听路由变化,添加新标签页
- watch(
- () => route.path,
- newPath => {
- // 有一些特殊路由可能不需要添加标签页,可以在这里过滤
- const ignorePaths = ['/login', '/loginNode', '/loginGraphic'];
- if (!ignorePaths.includes(newPath)) {
- nextTick(() => {
- setTimeout(() => {
- addTab(newPath);
- }, 100);
- });
- }
- },
- );
- watch(
- () => props.menuWidth,
- newWidth => {
- nextTick(() => {
- const workTabContainer = document.querySelector('.work-tab-container');
- if (workTabContainer) {
- setTimeout(() => {
- workTabContainer.style.width = `calc(100% - 20px - ${newWidth}px)`;
- });
- }
- });
- },
- { immediate: true },
- );
- // 供外部组件调用,用于添加带有菜单信息的标签页
- const addTabWithMenuInfo = (path, menuInfo) => {
- // 缓存菜单信息
- pathMenuMap.value[path] = {
- title: menuInfo.title || menuInfo.name || '',
- menuNodeType: menuInfo.menuNodeType,
- no: menuInfo.no,
- };
- // 添加标签页
- addTab(path);
- };
- // 暴露方法给其他组件使用
- defineExpose({
- addTabWithMenuInfo,
- });
- </script>
- <style scoped>
- .work-tab-container {
- width: calc(100% - 20px - 230px);
- background-color: #fff;
- padding: 0;
- position: fixed;
- top: 50px;
- right: 0;
- z-index: 10;
- }
- .work-tabs {
- overflow-x: auto;
- overflow-y: hidden;
- scrollbar-width: none;
- /* Firefox */
- -ms-overflow-style: none;
- /* IE and Edge */
- }
- .work-tabs::-webkit-scrollbar {
- display: none;
- /* Chrome, Safari, Opera */
- }
- /* 添加额外的样式来确保内容区域不被遮挡 */
- :deep(.ant-tabs) {
- height: 40px;
- /* 设置固定高度 */
- }
- :deep(.ant-tabs-nav) {
- margin-bottom: 0;
- }
- :deep(.ant-tabs-nav-wrap) {
- flex: 1;
- overflow-x: auto;
- scrollbar-width: none;
- -ms-overflow-style: none;
- }
- :deep(.ant-tabs-nav-wrap::-webkit-scrollbar) {
- display: none;
- }
- :deep(.ant-tabs-tab) {
- padding: 6px 16px !important;
- }
- :deep(.ant-tabs-tab-btn) {
- transition: all 0.3s;
- }
- :deep(.ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn) {
- color: #1890ff;
- font-weight: 500;
- }
- :deep(.ant-tabs-extra-content) {
- line-height: 44px;
- }
- </style>
|