2
0
Эх сурвалжийг харах

✨ feat(components): add tab-bar component

- 添加TabBar组件,支持自定义选项、颜色、固定定位等功能
- 实现徽章(badge)和红点(dot)提示功能
- 支持选中状态样式自定义和切换事件回调

📦 build(deps): add clsx dependency

- 添加clsx@^2.1.1依赖,用于条件类名组合
yourname 4 сар өмнө
parent
commit
00f87e95bd

+ 1 - 0
mini/package.json

@@ -63,6 +63,7 @@
     "@tarojs/shared": "4.1.4",
     "@tarojs/taro": "4.1.4",
     "abortcontroller-polyfill": "^1.7.8",
+    "clsx": "^2.1.1",
     "react": "^18.0.0",
     "react-dom": "^18.0.0"
   },

+ 9 - 0
mini/pnpm-lock.yaml

@@ -62,6 +62,9 @@ importers:
       abortcontroller-polyfill:
         specifier: ^1.7.8
         version: 1.7.8
+      clsx:
+        specifier: ^2.1.1
+        version: 2.1.1
       react:
         specifier: ^18.0.0
         version: 18.3.1
@@ -3072,6 +3075,10 @@ packages:
     resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==}
     engines: {node: '>=0.8'}
 
+  clsx@2.1.1:
+    resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
+    engines: {node: '>=6'}
+
   code-block-writer@13.0.3:
     resolution: {integrity: sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==}
 
@@ -10798,6 +10805,8 @@ snapshots:
 
   clone@1.0.4: {}
 
+  clsx@2.1.1: {}
+
   code-block-writer@13.0.3: {}
 
   color-convert@2.0.1:

+ 143 - 0
mini/src/components/ui/tab-bar.tsx

@@ -0,0 +1,143 @@
+import React from 'react'
+import { View, Text } from '@tarojs/components'
+import { useRouter } from '@tarojs/taro'
+import clsx from 'clsx'
+
+export interface TabBarItem {
+  key: string
+  title: string
+  icon?: string
+  selectedIcon?: string
+  badge?: number | string
+  dot?: boolean
+}
+
+export interface TabBarProps {
+  items: TabBarItem[]
+  activeKey?: string
+  onChange?: (key: string) => void
+  className?: string
+  style?: React.CSSProperties
+  fixed?: boolean
+  safeArea?: boolean
+  color?: string
+  selectedColor?: string
+  backgroundColor?: string
+}
+
+const TabBar = React.forwardRef<HTMLDivElement, TabBarProps>(({
+  items,
+  activeKey,
+  onChange,
+  className,
+  style,
+  fixed = true,
+  safeArea = true,
+  color = '#7f7f7f',
+  selectedColor = '#1890ff',
+  backgroundColor = '#ffffff',
+}, ref) => {
+  const router = useRouter()
+  
+  const currentActiveKey = activeKey || items[0]?.key
+
+  const handleTabChange = (key: string) => {
+    if (key !== currentActiveKey) {
+      onChange?.(key)
+    }
+  }
+
+  return (
+    <View
+      ref={ref}
+      className={clsx(
+        'tab-bar',
+        fixed && 'fixed bottom-0 left-0 right-0',
+        safeArea && 'pb-safe',
+        'z-50',
+        className
+      )}
+      style={{
+        backgroundColor,
+        ...style,
+      }}
+    >
+      <View className="flex h-16 border-t border-gray-200">
+        {items.map((item) => {
+          const isActive = item.key === currentActiveKey
+          
+          return (
+            <View
+              key={item.key}
+              className={clsx(
+                'flex-1 flex flex-col items-center justify-center',
+                'px-2 py-1',
+                'cursor-pointer',
+                'transition-colors duration-200',
+                'hover:opacity-80'
+              )}
+              onClick={() => handleTabChange(item.key)}
+            >
+              <View className="relative">
+                {item.icon && (
+                  <View
+                    className={clsx(
+                      'text-2xl',
+                      'mb-1',
+                      isActive ? 'text-blue-500' : 'text-gray-500'
+                    )}
+                    style={{
+                      color: isActive ? selectedColor : color,
+                    }}
+                  >
+                    {isActive && item.selectedIcon ? item.selectedIcon : item.icon}
+                  </View>
+                )}
+                
+                {item.badge && (
+                  <View
+                    className={clsx(
+                      'absolute -top-1 -right-2',
+                      'bg-red-500 text-white text-xs',
+                      'rounded-full px-1.5 py-0.5',
+                      'min-w-4 h-4 flex items-center justify-center'
+                    )}
+                  >
+                    {typeof item.badge === 'number' && item.badge > 99 ? '99+' : item.badge}
+                  </View>
+                )}
+                
+                {item.dot && (
+                  <View
+                    className={clsx(
+                      'absolute -top-1 -right-1',
+                      'w-2 h-2 bg-red-500 rounded-full'
+                    )}
+                  />
+                )}
+              </View>
+              
+              <Text
+                className={clsx(
+                  'text-xs',
+                  'leading-tight',
+                  isActive ? 'font-medium' : 'font-normal'
+                )}
+                style={{
+                  color: isActive ? selectedColor : color,
+                }}
+                numberOfLines={1}
+              >
+                {item.title}
+              </Text>
+            </View>
+          )
+        })}
+      </View>
+    </View>
+  )
+})
+
+TabBar.displayName = 'TabBar'
+
+export { TabBar }