更新最新代码
This commit is contained in:
21
packages/@core/ui-kit/tabs-ui/build.config.ts
Normal file
21
packages/@core/ui-kit/tabs-ui/build.config.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { defineBuildConfig } from 'unbuild';
|
||||
|
||||
export default defineBuildConfig({
|
||||
clean: true,
|
||||
declaration: true,
|
||||
entries: [
|
||||
{
|
||||
builder: 'mkdist',
|
||||
input: './src',
|
||||
loaders: ['vue'],
|
||||
pattern: ['**/*.vue'],
|
||||
},
|
||||
{
|
||||
builder: 'mkdist',
|
||||
format: 'esm',
|
||||
input: './src',
|
||||
loaders: ['js'],
|
||||
pattern: ['**/*.ts'],
|
||||
},
|
||||
],
|
||||
});
|
||||
1
packages/@core/ui-kit/tabs-ui/postcss.config.mjs
Normal file
1
packages/@core/ui-kit/tabs-ui/postcss.config.mjs
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from '@vben/tailwind-config/postcss';
|
||||
2
packages/@core/ui-kit/tabs-ui/src/components/index.ts
Normal file
2
packages/@core/ui-kit/tabs-ui/src/components/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { default as TabsChrome } from './tabs-chrome/tabs.vue';
|
||||
export { default as Tabs } from './tabs/tabs.vue';
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default as TabsToolMore } from './tool-more.vue';
|
||||
export { default as TabsToolScreen } from './tool-screen.vue';
|
||||
3
packages/@core/ui-kit/tabs-ui/src/index.ts
Normal file
3
packages/@core/ui-kit/tabs-ui/src/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './components/widgets';
|
||||
export { default as TabsView } from './tabs-view.vue';
|
||||
export type { IContextMenuItem } from '@vben-core/shadcn-ui';
|
||||
73
packages/@core/ui-kit/tabs-ui/src/types.ts
Normal file
73
packages/@core/ui-kit/tabs-ui/src/types.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import type { IContextMenuItem } from '@vben-core/shadcn-ui';
|
||||
import type { TabDefinition, TabsStyleType } from '@vben-core/typings';
|
||||
|
||||
export type TabsEmits = {
|
||||
close: [string];
|
||||
sortTabs: [number, number];
|
||||
unpin: [TabDefinition];
|
||||
};
|
||||
|
||||
export interface TabsProps {
|
||||
active?: string;
|
||||
/**
|
||||
* @zh_CN content class
|
||||
* @default tabs-chrome
|
||||
*/
|
||||
contentClass?: string;
|
||||
/**
|
||||
* @zh_CN 右键菜单
|
||||
*/
|
||||
contextMenus?: (data: any) => IContextMenuItem[];
|
||||
/**
|
||||
* @zh_CN 是否可以拖拽
|
||||
*/
|
||||
draggable?: boolean;
|
||||
/**
|
||||
* @zh_CN 间隙
|
||||
* @default 7
|
||||
* 仅限 tabs-chrome
|
||||
*/
|
||||
gap?: number;
|
||||
/**
|
||||
* @zh_CN tab 最大宽度
|
||||
* 仅限 tabs-chrome
|
||||
*/
|
||||
maxWidth?: number;
|
||||
/**
|
||||
* @zh_CN 点击中键时关闭Tab
|
||||
*/
|
||||
middleClickToClose?: boolean;
|
||||
|
||||
/**
|
||||
* @zh_CN tab最小宽度
|
||||
* 仅限 tabs-chrome
|
||||
*/
|
||||
minWidth?: number;
|
||||
|
||||
/**
|
||||
* @zh_CN 是否显示图标
|
||||
*/
|
||||
showIcon?: boolean;
|
||||
/**
|
||||
* @zh_CN 标签页风格
|
||||
*/
|
||||
styleType?: TabsStyleType;
|
||||
|
||||
/**
|
||||
* @zh_CN 选项卡数据
|
||||
*/
|
||||
tabs?: TabDefinition[];
|
||||
|
||||
/**
|
||||
* @zh_CN 是否响应滚轮事件
|
||||
*/
|
||||
wheelable?: boolean;
|
||||
}
|
||||
|
||||
export interface TabConfig extends TabDefinition {
|
||||
affixTab: boolean;
|
||||
closable: boolean;
|
||||
icon: string;
|
||||
key: string;
|
||||
title: string;
|
||||
}
|
||||
124
packages/@core/ui-kit/tabs-ui/src/use-tabs-drag.ts
Normal file
124
packages/@core/ui-kit/tabs-ui/src/use-tabs-drag.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
import type { Sortable } from '@vben-core/composables';
|
||||
import type { EmitType } from '@vben-core/typings';
|
||||
|
||||
import type { TabsProps } from './types';
|
||||
|
||||
import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
|
||||
|
||||
import { useIsMobile, useSortable } from '@vben-core/composables';
|
||||
|
||||
// 可能会找到拖拽的子元素,这里需要确保拖拽的dom时tab元素
|
||||
function findParentElement(element: HTMLElement) {
|
||||
const parentCls = 'group';
|
||||
return element.classList.contains(parentCls)
|
||||
? element
|
||||
: element.closest(`.${parentCls}`);
|
||||
}
|
||||
|
||||
export function useTabsDrag(props: TabsProps, emit: EmitType) {
|
||||
const sortableInstance = ref<null | Sortable>(null);
|
||||
|
||||
async function initTabsSortable() {
|
||||
await nextTick();
|
||||
|
||||
const el = document.querySelectorAll(
|
||||
`.${props.contentClass}`,
|
||||
)?.[0] as HTMLElement;
|
||||
|
||||
if (!el) {
|
||||
console.warn('Element not found for sortable initialization');
|
||||
return;
|
||||
}
|
||||
|
||||
const resetElState = async () => {
|
||||
el.style.cursor = 'default';
|
||||
// el.classList.remove('dragging');
|
||||
el.querySelector('.draggable')?.classList.remove('dragging');
|
||||
};
|
||||
|
||||
const { initializeSortable } = useSortable(el, {
|
||||
filter: (_evt, target: HTMLElement) => {
|
||||
const parent = findParentElement(target);
|
||||
const draggable = parent?.classList.contains('draggable');
|
||||
return !draggable || !props.draggable;
|
||||
},
|
||||
onEnd(evt) {
|
||||
const { newIndex, oldIndex } = evt;
|
||||
// const fromElement = evt.item;
|
||||
const { srcElement } = (evt as any).originalEvent;
|
||||
|
||||
if (!srcElement) {
|
||||
resetElState();
|
||||
return;
|
||||
}
|
||||
|
||||
const srcParent = findParentElement(srcElement);
|
||||
|
||||
if (!srcParent) {
|
||||
resetElState();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!srcParent.classList.contains('draggable')) {
|
||||
resetElState();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
oldIndex !== undefined &&
|
||||
newIndex !== undefined &&
|
||||
!Number.isNaN(oldIndex) &&
|
||||
!Number.isNaN(newIndex) &&
|
||||
oldIndex !== newIndex
|
||||
) {
|
||||
emit('sortTabs', oldIndex, newIndex);
|
||||
}
|
||||
resetElState();
|
||||
},
|
||||
onMove(evt) {
|
||||
const parent = findParentElement(evt.related);
|
||||
if (parent?.classList.contains('draggable') && props.draggable) {
|
||||
const isCurrentAffix = evt.dragged.classList.contains('affix-tab');
|
||||
const isRelatedAffix = evt.related.classList.contains('affix-tab');
|
||||
// 不允许在固定的tab和非固定的tab之间互相拖拽
|
||||
return isCurrentAffix === isRelatedAffix;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
onStart: () => {
|
||||
el.style.cursor = 'grabbing';
|
||||
el.querySelector('.draggable')?.classList.add('dragging');
|
||||
// el.classList.add('dragging');
|
||||
},
|
||||
});
|
||||
|
||||
sortableInstance.value = await initializeSortable();
|
||||
}
|
||||
|
||||
async function init() {
|
||||
const { isMobile } = useIsMobile();
|
||||
|
||||
// 移动端下tab不需要拖拽
|
||||
if (isMobile.value) {
|
||||
return;
|
||||
}
|
||||
await nextTick();
|
||||
initTabsSortable();
|
||||
}
|
||||
|
||||
onMounted(init);
|
||||
|
||||
watch(
|
||||
() => props.styleType,
|
||||
() => {
|
||||
sortableInstance.value?.destroy();
|
||||
init();
|
||||
},
|
||||
);
|
||||
|
||||
onUnmounted(() => {
|
||||
sortableInstance.value?.destroy();
|
||||
});
|
||||
}
|
||||
1
packages/@core/ui-kit/tabs-ui/tailwind.config.mjs
Normal file
1
packages/@core/ui-kit/tabs-ui/tailwind.config.mjs
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from '@vben/tailwind-config';
|
||||
6
packages/@core/ui-kit/tabs-ui/tsconfig.json
Normal file
6
packages/@core/ui-kit/tabs-ui/tsconfig.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "@vben/tsconfig/web.json",
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
Reference in New Issue
Block a user