Sfoglia il codice sorgente

✨ feat(ui): add image component

- 创建图片组件,支持基础图片展示功能
- 实现加载状态和错误状态的占位显示
- 添加圆角样式控制(rounded属性)
- 支持懒加载、加载/错误回调等功能
- 提供图片模式(mode)和替代文本(alt)配置选项
yourname 4 mesi fa
parent
commit
82250d2a09
1 ha cambiato i file con 127 aggiunte e 0 eliminazioni
  1. 127 0
      mini/src/components/ui/image.tsx

+ 127 - 0
mini/src/components/ui/image.tsx

@@ -0,0 +1,127 @@
+import { View, Image as TaroImage, ImageProps as TaroImageProps } from '@tarojs/components'
+import { cn } from '@/utils/cn'
+import { useState } from 'react'
+
+export interface ImageProps extends Omit<TaroImageProps, 'onError'> {
+  /**
+   * 图片地址
+   */
+  src: string
+  /**
+   * 替代文本
+   */
+  alt?: string
+  /**
+   * 图片模式
+   * @default "aspectFill"
+   */
+  mode?: TaroImageProps['mode']
+  /**
+   * 是否懒加载
+   * @default true
+   */
+  lazyLoad?: boolean
+  /**
+   * 是否显示加载占位
+   * @default true
+   */
+  showLoading?: boolean
+  /**
+   * 是否显示错误占位
+   * @default true
+   */
+  showError?: boolean
+  /**
+   * 圆角大小
+   */
+  rounded?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | 'full'
+  /**
+   * 自定义样式类
+   */
+  className?: string
+  /**
+   * 图片加载失败时的回调
+   */
+  onError?: () => void
+  /**
+   * 图片加载成功的回调
+   */
+  onLoad?: () => void
+}
+
+const roundedMap = {
+  none: '',
+  sm: 'rounded-sm',
+  md: 'rounded-md',
+  lg: 'rounded-lg',
+  xl: 'rounded-xl',
+  full: 'rounded-full'
+}
+
+export function Image({
+  src,
+  alt = '图片',
+  mode = 'aspectFill',
+  lazyLoad = true,
+  showLoading = true,
+  showError = true,
+  rounded = 'none',
+  className,
+  onError,
+  onLoad,
+  ...props
+}: ImageProps) {
+  const [loading, setLoading] = useState(true)
+  const [error, setError] = useState(false)
+
+  const handleLoad = () => {
+    setLoading(false)
+    setError(false)
+    onLoad?.()
+  }
+
+  const handleError = () => {
+    setLoading(false)
+    setError(true)
+    onError?.()
+  }
+
+  const renderPlaceholder = () => {
+    if (loading && showLoading) {
+      return (
+        <View className="absolute inset-0 flex items-center justify-center bg-gray-100">
+          <View className="i-heroicons-photo-20-solid w-8 h-8 text-gray-400 animate-pulse" />
+        </View>
+      )
+    }
+
+    if (error && showError) {
+      return (
+        <View className="absolute inset-0 flex items-center justify-center bg-gray-100">
+          <View className="i-heroicons-exclamation-triangle-20-solid w-8 h-8 text-gray-400" />
+        </View>
+      )
+    }
+
+    return null
+  }
+
+  return (
+    <View className={cn('relative overflow-hidden', roundedMap[rounded], className)}>
+      <TaroImage
+        src={src}
+        mode={mode}
+        lazyLoad={lazyLoad}
+        onLoad={handleLoad}
+        onError={handleError}
+        className={cn(
+          'w-full h-full',
+          loading && 'opacity-0',
+          !loading && !error && 'opacity-100 transition-opacity duration-300'
+        )}
+        {...props}
+      />
+      {renderPlaceholder()}
+    </View>
+  )
+}