| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 |
- import { pxCheck } from '@/packages/utils/pxCheck';
- import { onMounted, provide, VNode, ref, Ref, computed, onActivated, watch, PropType } from 'vue';
- export class Title {
- title: string = '';
- titleSlot?: VNode[];
- paneKey: string = '';
- disabled: boolean = false;
- constructor() {}
- }
- export type TabsSize = 'large' | 'normal' | 'small';
- export const component = {
- props: {
- modelValue: {
- type: [String, Number],
- default: 0
- },
- color: {
- type: String,
- default: ''
- },
- direction: {
- type: String,
- default: 'horizontal' //vertical
- },
- size: {
- type: String as PropType<TabsSize>,
- default: 'normal'
- },
- type: {
- type: String,
- default: 'line' //card、line、smile
- },
- titleScroll: {
- type: Boolean,
- default: false
- },
- ellipsis: {
- type: Boolean,
- default: true
- },
- background: {
- type: String,
- default: ''
- },
- animatedTime: {
- type: [Number, String],
- default: 300
- },
- titleGutter: {
- type: [Number, String],
- default: 0
- }
- },
- components: {},
- emits: ['update:modelValue', 'click', 'change'],
- setup(props: any, { emit, slots }: any) {
- provide('activeKey', { activeKey: computed(() => props.modelValue) });
- const titles: Ref<Title[]> = ref([]);
- const renderTitles = (vnodes: VNode[]) => {
- vnodes.forEach((vnode: VNode, index: number) => {
- let type = vnode.type;
- type = (type as any).name || type;
- if (type == 'nut-tabpane') {
- let title = new Title();
- if (vnode.props?.title || vnode.props?.['pane-key']) {
- title.title = vnode.props?.title;
- title.paneKey = vnode.props?.['pane-key'] || index;
- title.disabled = vnode.props?.disabled;
- } else {
- // title.titleSlot = vnode.children?.title() as VNode[];
- }
- titles.value.push(title);
- } else {
- renderTitles(vnode.children as VNode[]);
- }
- });
- };
- const currentIndex = ref((props.modelValue as number) || 0);
- const findTabsIndex = (value: string | number) => {
- let index = titles.value.findIndex((item) => item.paneKey == value);
- if (index == -1) {
- console.error('[NutUI] <Tabs> 请检查 v-model 值是否为 paneKey ,如 paneKey 未设置,请采用下标控制 .');
- } else {
- currentIndex.value = index;
- }
- };
- const init = (vnodes: VNode[] = slots.default?.()) => {
- titles.value = [];
- if (vnodes && vnodes.length) {
- renderTitles(vnodes);
- }
- findTabsIndex(props.modelValue);
- };
- watch(
- () => slots.default?.(),
- (vnodes: VNode[]) => {
- init(vnodes);
- }
- );
- watch(
- () => props.modelValue,
- (value: string | number) => {
- findTabsIndex(value);
- }
- );
- onMounted(init);
- onActivated(init);
- const contentStyle = computed(() => {
- return {
- transform:
- props.direction == 'horizontal'
- ? `translate3d(-${currentIndex.value * 100}%, 0, 0)`
- : `translate3d( 0,-${currentIndex.value * 100}%, 0)`,
- transitionDuration: `${props.animatedTime}ms`
- };
- });
- const tabsNavStyle = computed(() => {
- return {
- background: props.background
- };
- });
- const tabsActiveStyle = computed(() => {
- return {
- color: props.type == 'smile' ? props.color : '',
- background: props.type == 'line' ? props.color : ''
- };
- });
- const titleStyle = computed(() => {
- return {
- marginLeft: pxCheck(props.titleGutter),
- marginRight: pxCheck(props.titleGutter)
- };
- });
- const methods = {
- tabChange: (item: Title, index: number) => {
- emit('click', item);
- if (item.disabled) {
- return;
- }
- currentIndex.value = index;
- emit('update:modelValue', item.paneKey);
- emit('change', item);
- }
- };
- return {
- titles,
- contentStyle,
- tabsNavStyle,
- titleStyle,
- tabsActiveStyle,
- ...methods
- };
- }
- };
|