From f2873ee122a6502e94dd7740454ce9c076c2dcfe Mon Sep 17 00:00:00 2001 From: vaebe <18137693952@163.com> Date: Mon, 16 Sep 2024 22:09:15 +0800 Subject: [PATCH] =?UTF-8?q?feat(tabs):=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E9=A3=8E=E6=A0=BC,=E4=BF=AE=E5=A4=8D=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/ccui/ui/tabs/index.ts | 22 +++---- .../ui/tabs/src/components/tab/tab-types.ts | 18 ++--- .../ccui/ui/tabs/src/components/tab/tab.tsx | 40 ++++++----- .../ui/tabs/src/components/tabs-nav/index.tsx | 66 ++++++++++--------- .../src/components/tabs-nav/tabs-nav.scss | 6 +- packages/ccui/ui/tabs/src/tabs-types.ts | 50 +++++++------- packages/ccui/ui/tabs/src/tabs.tsx | 57 ++++++++-------- packages/ccui/ui/tabs/test/tabs.test.ts | 2 +- 8 files changed, 137 insertions(+), 124 deletions(-) diff --git a/packages/ccui/ui/tabs/index.ts b/packages/ccui/ui/tabs/index.ts index c503ff3..970144b 100644 --- a/packages/ccui/ui/tabs/index.ts +++ b/packages/ccui/ui/tabs/index.ts @@ -1,20 +1,20 @@ -import type { App } from 'vue'; -import Tabs from './src/tabs'; -import Tab from './src/components/tab/tab'; +import type { App } from 'vue' +import Tab from './src/components/tab/tab' +import Tabs from './src/tabs' Tabs.install = function (app: App): void { - app.component(Tabs.name, Tabs); - app.component(Tab.name, Tab); -}; + app.component(Tabs.name, Tabs) + app.component(Tab.name, Tab) +} -export { Tabs, Tab }; +export { Tab, Tabs } export default { title: 'Tabs 选项卡', category: '导航', status: undefined, // TODO: 组件若开发完成则填入"100%",并删除该注释 install(app: App): void { - app.component(Tabs.name, Tabs); - app.component(Tab.name, Tab); - } -}; + app.component(Tabs.name, Tabs) + app.component(Tab.name, Tab) + }, +} diff --git a/packages/ccui/ui/tabs/src/components/tab/tab-types.ts b/packages/ccui/ui/tabs/src/components/tab/tab-types.ts index 831cc96..bf3be92 100644 --- a/packages/ccui/ui/tabs/src/components/tab/tab-types.ts +++ b/packages/ccui/ui/tabs/src/components/tab/tab-types.ts @@ -1,21 +1,21 @@ -import { ExtractPropTypes, PropType } from 'vue'; +import type { ExtractPropTypes, PropType } from 'vue' -export type LabelType = string | number; -export type NameType = string | number; +export type LabelType = string | number +export type NameType = string | number export const tabProps = { label: { type: [String, Number] as PropType, - default: null + default: null, }, name: { type: [String, Number] as PropType, - default: null + default: null, }, disabled: { type: Boolean, - default: false - } -} as const; + default: false, + }, +} as const -export type TabProps = ExtractPropTypes; +export type TabProps = ExtractPropTypes diff --git a/packages/ccui/ui/tabs/src/components/tab/tab.tsx b/packages/ccui/ui/tabs/src/components/tab/tab.tsx index 99da8f5..5b71ba5 100644 --- a/packages/ccui/ui/tabs/src/components/tab/tab.tsx +++ b/packages/ccui/ui/tabs/src/components/tab/tab.tsx @@ -1,33 +1,37 @@ -import { defineComponent, inject, onUnmounted } from 'vue'; -import { tabsInjectionKey, TabsState } from '../../tabs-types'; -import { tabProps, TabProps } from './tab-types'; -import './tab.scss'; -import { useNamespace } from '../../../../shared/hooks/use-namespace'; +import { defineComponent, inject, onUnmounted } from 'vue' +import type { TabsState } from '../../tabs-types' +import { useNamespace } from '../../../../shared/hooks/use-namespace' +import { tabsInjectionKey } from '../../tabs-types' +import type { TabProps } from './tab-types' +import { tabProps } from './tab-types' +import './tab.scss' export default defineComponent({ name: 'CTab', props: tabProps, emits: [], setup(props: TabProps, { slots }) { - const ns = useNamespace('tab'); + const ns = useNamespace('tab') - const tabsState = inject(tabsInjectionKey); - tabsState?.data?.push(props); - tabsState?.slots?.push(slots); + const tabsState = inject(tabsInjectionKey) + tabsState?.data?.push(props) + tabsState?.slots?.push(slots) // 组件卸载移除 组件props数据缓存 onUnmounted(() => { if (tabsState) { tabsState.data = tabsState.data?.filter( - (item) => item.name !== props.name - ); + item => item.name !== props.name, + ) } - }); + }) return () => { - return props.name === tabsState?.active ? ( -
{slots.default && slots.default()}
- ) : null; - }; - } -}); + return props.name === tabsState?.active + ? ( +
{slots.default && slots.default()}
+ ) + : null + } + }, +}) diff --git a/packages/ccui/ui/tabs/src/components/tabs-nav/index.tsx b/packages/ccui/ui/tabs/src/components/tabs-nav/index.tsx index 58012e7..8f664c0 100644 --- a/packages/ccui/ui/tabs/src/components/tabs-nav/index.tsx +++ b/packages/ccui/ui/tabs/src/components/tabs-nav/index.tsx @@ -1,72 +1,74 @@ -import { defineComponent, inject, computed } from 'vue'; +import type { + TabsProps, + TabsState, +} from '../../tabs-types' +import type { TabProps } from '../tab/tab-types' +import { computed, defineComponent, inject } from 'vue' +import { useNamespace } from '../../../../shared/hooks/use-namespace' import { tabsInjectionKey, tabsProps, - TabsProps, - TabsState -} from '../../tabs-types'; -import { TabProps } from '../tab/tab-types'; -import './tabs-nav.scss'; -import { useNamespace } from '../../../../shared/hooks/use-namespace'; +} from '../../tabs-types' +import './tabs-nav.scss' export default defineComponent({ name: 'CTabs-nav', props: tabsProps, emits: ['active-tab-change'], setup(props: TabsProps, { emit }) { - const ns = useNamespace('tabs-nav'); + const ns = useNamespace('tabs-nav') - const tabsState = inject(tabsInjectionKey); + const tabsState = inject(tabsInjectionKey) - const navList = computed(() => tabsState?.data || []); - const slotsList = computed(() => tabsState?.slots || []); + const navList = computed(() => tabsState?.data || []) + const slotsList = computed(() => tabsState?.slots || []) const containerClass = computed(() => { - let cls = ns.b(); + let cls = ns.b() if (props.type) { - cls += `-${props.type}`; + cls += `-${props.type}` } - return `${cls}--${props.tabPosition}`; - }); + return `${cls}--${props.tabPosition}` + }) const getNavItemClass = (item: TabProps) => { - const itemClass = `${containerClass.value}-item`; - const itemActiveClass = - tabsState?.active === item.name ? `${itemClass}-active` : ''; + const itemClass = `${containerClass.value}-item` + const itemActiveClass + = tabsState?.active === item.name ? `${itemClass}-active` : '' - return `${itemClass} ${itemActiveClass}`; - }; + return `${itemClass} ${itemActiveClass}` + } const navItemClick = (item: TabProps) => { if (tabsState) { // 相同不进行切换 if (tabsState.active === item.name) { - return; + return } - emit('active-tab-change', item.name); + emit('active-tab-change', item.name) } - }; + } const navItemDom = computed(() => { return navList.value.map((item, index) => { - const curSlotTitle = slotsList.value[index]; + const curSlotTitle = slotsList.value[index] return (

{ - navItemClick(item); + navItemClick(item) }} > {curSlotTitle.title ? curSlotTitle.title() : item.label}

- ); - }); - }); + ) + }) + }) return () => { - return
{navItemDom.value}
; - }; - } -}); + return
{navItemDom.value}
+ } + }, +}) diff --git a/packages/ccui/ui/tabs/src/components/tabs-nav/tabs-nav.scss b/packages/ccui/ui/tabs/src/components/tabs-nav/tabs-nav.scss index c82c6a2..2934333 100644 --- a/packages/ccui/ui/tabs/src/components/tabs-nav/tabs-nav.scss +++ b/packages/ccui/ui/tabs/src/components/tabs-nav/tabs-nav.scss @@ -122,7 +122,7 @@ border-bottom: 2px solid transparent; &:first-of-type { - border: 1px solid $ccui-line; + border-left: 1px solid $ccui-line; border-top-left-radius: $ccui-border-radius; } @@ -237,6 +237,10 @@ background: $ccui-base-bg; border-right: 1px solid $ccui-line; border-left: 1px solid $ccui-line; + + &:first-of-type { + border-left: unset; + } } } diff --git a/packages/ccui/ui/tabs/src/tabs-types.ts b/packages/ccui/ui/tabs/src/tabs-types.ts index 8bc4fdd..e9992ae 100644 --- a/packages/ccui/ui/tabs/src/tabs-types.ts +++ b/packages/ccui/ui/tabs/src/tabs-types.ts @@ -1,64 +1,64 @@ import type { ComputedRef, ExtractPropTypes, + InjectionKey, PropType, - InjectionKey -} from 'vue'; -import { TabProps } from './components/tab/tab-types'; +} from 'vue' +import type { TabProps } from './components/tab/tab-types' -export type ModelValueType = string | number; +export type ModelValueType = string | number -export type ITabsType = '' | 'card' | 'border-card'; +export type ITabsType = '' | 'card' | 'border-card' -export type ITabPositionType = 'top' | 'right' | 'bottom' | 'left'; +export type ITabPositionType = 'top' | 'right' | 'bottom' | 'left' -export type Active = string | number | null; -export type BeforeChangeType = (id: Active) => boolean; +export type Active = string | number | null +export type BeforeChangeType = (id: Active) => boolean export interface TabsState { - data?: TabProps[]; - active: string | number; - slots: any[]; + data?: TabProps[] + active: string | number + slots: any[] } export const tabsProps = { modelValue: { type: [String, Number] as PropType, - default: null + default: null, }, type: { type: String as () => ITabsType, - default: '' + default: '', }, customWidth: { type: String, - default: '' + default: '', }, cssClass: { type: String, - default: '' + default: '', }, beforeChange: { type: Function as PropType, - default: null + default: null, }, tabPosition: { type: String as () => ITabPositionType, - default: 'top' - } -} as const; + default: 'top', + }, +} as const -export type TabsProps = ExtractPropTypes; +export type TabsProps = ExtractPropTypes export interface UseTabsEvent { - onUpdateModelValue: (value: string | number) => void; - onActiveTabChange: (value: string) => void; - onTabChange: (id: string | undefined, type: string) => void; + onUpdateModelValue: (value: string | number) => void + onActiveTabChange: (value: string) => void + onTabChange: (id: string | undefined, type: string) => void } /** KTabs 注入 tab 的 key 值 */ -export const tabsInjectionKey: InjectionKey = Symbol('CTabs'); +export const tabsInjectionKey: InjectionKey = Symbol('CTabs') export interface UseTabsRender { - tabsClasses: ComputedRef>; + tabsClasses: ComputedRef> } diff --git a/packages/ccui/ui/tabs/src/tabs.tsx b/packages/ccui/ui/tabs/src/tabs.tsx index bb7173a..cbeb7e5 100644 --- a/packages/ccui/ui/tabs/src/tabs.tsx +++ b/packages/ccui/ui/tabs/src/tabs.tsx @@ -1,55 +1,58 @@ -import { defineComponent, reactive, provide, defineAsyncComponent } from 'vue'; -import { - tabsProps, +import { defineAsyncComponent, defineComponent, provide, reactive } from 'vue' +import { useNamespace } from '../../shared/hooks/use-namespace' +import type { TabsProps, TabsState, - tabsInjectionKey -} from './tabs-types'; -import './tabs.scss'; -import TabsNav from './components/tabs-nav'; -import { useNamespace } from '../../shared/hooks/use-namespace'; +} from './tabs-types' +import TabsNav from './components/tabs-nav' +import { + tabsInjectionKey, + tabsProps, +} from './tabs-types' +import './tabs.scss' export default defineComponent({ name: 'CTabs', props: tabsProps, emits: ['change', 'update:modelValue'], components: { - TabsNav: defineAsyncComponent(() => import('./components/tabs-nav')) + TabsNav: defineAsyncComponent(() => import('./components/tabs-nav')), }, setup(props: TabsProps, { slots, emit }) { - const ns = useNamespace('tabs'); + const ns = useNamespace('tabs') const state: TabsState = reactive({ data: [], active: props.modelValue, - slots: [] - }); + slots: [], + }) - provide(tabsInjectionKey, state); + provide(tabsInjectionKey, state) const setActiveTab = (name: string | number) => { - state.active = name; + state.active = name // 更新 v-model 触发change事件 - emit('update:modelValue', name); - emit('change', name); - }; + emit('update:modelValue', name) + emit('change', name) + } const tabsContent = () => { const tabsNav = ( - ); - const content = slots.default && slots.default(); + ) + const content = slots.default && slots.default() if (['bottom'].includes(props.tabPosition)) { - return [content, tabsNav]; - } else { - return [tabsNav, content]; + return [content, tabsNav] + } + else { + return [tabsNav, content] } - }; + } return () => { - return
{tabsContent()}
; - }; - } -}); + return
{tabsContent()}
+ } + }, +}) diff --git a/packages/ccui/ui/tabs/test/tabs.test.ts b/packages/ccui/ui/tabs/test/tabs.test.ts index d007a6b..390f4c1 100644 --- a/packages/ccui/ui/tabs/test/tabs.test.ts +++ b/packages/ccui/ui/tabs/test/tabs.test.ts @@ -1,7 +1,7 @@ import { shallowMount } from '@vue/test-utils' import { describe, expect, it } from 'vitest' -import { Tabs } from '../index' import { useNamespace } from '../../shared/hooks/use-namespace' +import { Tabs } from '../index' // todo tabs-tab 测试用例待补充 const ns = useNamespace('tabs', true)