2
0
yourname 3 долоо хоног өмнө
parent
commit
f310f9bbdc

+ 1 - 1
mini-talent/src/app.tsx

@@ -1,5 +1,5 @@
 import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only';
-import '@/utils/headers-polyfill.js'
+// import '@/utils/headers-polyfill.js'
 import { PropsWithChildren } from 'react'
 import { useLaunch } from '@tarojs/taro'
 import { QueryClientProvider } from '@tanstack/react-query'

+ 0 - 35
mini/src/api.ts

@@ -1,35 +0,0 @@
-import type {
-  AuthRoutes,
-  UserRoutes,
-  RoleRoutes,
-  FileRoutes,
-  ChannelRoutes,
-  CompanyRoutes,
-  DisabilityRoutes,
-  OrderRoutes,
-  PlatformRoutes,
-  SalaryRoutes,
-  EnterpriseAuthRoutes,
-  EnterpriseCompanyRoutes,
-  EnterpriseDisabilityRoutes
-} from '@d8d/server'
-import { rpcClient } from './utils/rpc-client'
-
-// 创建各个模块的RPC客户端
-export const authClient = rpcClient<AuthRoutes>().api.v1.auth
-export const userClient = rpcClient<UserRoutes>().api.v1.users
-export const roleClient = rpcClient<RoleRoutes>().api.v1.roles
-export const fileClient = rpcClient<FileRoutes>().api.v1.files
-
-// Allin系统移植模块客户端
-export const channelClient = rpcClient<ChannelRoutes>().api.v1.channel
-export const companyClient = rpcClient<CompanyRoutes>().api.v1.company
-export const disabilityClient = rpcClient<DisabilityRoutes>().api.v1.disability
-export const orderClient = rpcClient<OrderRoutes>().api.v1.order
-export const platformClient = rpcClient<PlatformRoutes>().api.v1.platform
-export const salaryClient = rpcClient<SalaryRoutes>().api.v1.salary
-
-// 用人方小程序专用客户端(史诗012补充API)
-export const enterpriseAuthClient = rpcClient<EnterpriseAuthRoutes>().api.v1.yongren.auth
-export const enterpriseCompanyClient = rpcClient<EnterpriseCompanyRoutes>().api.v1.yongren.company
-export const enterpriseDisabilityClient = rpcClient<EnterpriseDisabilityRoutes>().api.v1.yongren['disability-person']

+ 0 - 1
mini/src/app.config.ts

@@ -9,7 +9,6 @@ export default defineAppConfig({
     'pages/yongren/settings/index',
     'pages/yongren/video/index',
     // 原有小程序页面(企业用户专用)
-    'pages/profile/index',
     'pages/login/index'
   ],
   window: {

+ 1 - 1
mini/src/app.tsx

@@ -1,5 +1,5 @@
 import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only';
-import '@/utils/headers-polyfill.js'
+// import '@/utils/headers-polyfill.js'
 import { PropsWithChildren } from 'react'
 import { useLaunch } from '@tarojs/taro'
 import { QueryClientProvider } from '@tanstack/react-query'

+ 0 - 131
mini/src/components/ui/avatar-upload.tsx

@@ -1,131 +0,0 @@
-import { useState } from 'react'
-import { View, Image } from '@tarojs/components'
-import Taro from '@tarojs/taro'
-import { cn } from '@/utils/cn'
-import { uploadFromSelect, type UploadResult } from '@/utils/minio'
-
-interface AvatarUploadProps {
-  currentAvatar?: string
-  onUploadSuccess?: (result: UploadResult) => void
-  onUploadError?: (error: Error) => void
-  size?: number
-  editable?: boolean
-}
-
-export function AvatarUpload({ 
-  currentAvatar, 
-  onUploadSuccess, 
-  onUploadError,
-  size = 96,
-  editable = true 
-}: AvatarUploadProps) {
-  const [uploading, setUploading] = useState(false)
-  const [progress, setProgress] = useState(0)
-
-  const handleChooseImage = async () => {
-    if (!editable || uploading) return
-
-    try {
-      setUploading(true)
-      setProgress(0)
-
-      const result = await uploadFromSelect(
-        'avatars',
-        {
-          sourceType: ['album', 'camera'],
-          count: 1
-        },
-        {
-          onProgress: (event) => {
-            setProgress(event.progress)
-            if (event.stage === 'uploading') {
-              Taro.showLoading({
-                title: `上传中...${event.progress}%`
-              })
-            }
-          },
-          onComplete: () => {
-            Taro.hideLoading()
-            Taro.showToast({
-              title: '上传成功',
-              icon: 'success'
-            })
-          },
-          onError: (error) => {
-            Taro.hideLoading()
-            onUploadError?.(error)
-            Taro.showToast({
-              title: '上传失败',
-              icon: 'none'
-            })
-          }
-        }
-      )
-
-      onUploadSuccess?.(result)
-    } catch (error) {
-      console.error('头像上传失败:', error)
-      onUploadError?.(error as Error)
-    } finally {
-      setUploading(false)
-      setProgress(0)
-    }
-  }
-
-  const avatarSize = size
-
-  return (
-    <View 
-      className="relative inline-block"
-      onClick={handleChooseImage}
-    >
-      <View 
-        className={cn(
-          "relative overflow-hidden rounded-full",
-          "border-4 border-white shadow-lg",
-          editable && "cursor-pointer active:scale-95 transition-transform duration-150",
-          uploading && "opacity-75"
-        )}
-        style={{ width: avatarSize, height: avatarSize }}
-      >
-        <Image
-          src={currentAvatar || 'https://images.unsplash.com/photo-1494790108755-2616b612b786?w=160&h=160&fit=crop&crop=face'}
-          mode="aspectFill"
-          className="w-full h-full"
-        />
-        
-        {uploading && (
-          <View className="absolute inset-0 bg-black bg-opacity-50 flex items-center justify-center">
-            <View className="text-white text-xs">{progress}%</View>
-          </View>
-        )}
-      </View>
-
-      {editable && !uploading && (
-        <View 
-          className={cn(
-            "absolute -bottom-1 -right-1",
-            "w-8 h-8 bg-blue-500 rounded-full",
-            "flex items-center justify-center shadow-md",
-            "border-2 border-white"
-          )}
-        >
-          <View className="i-heroicons-camera-20-solid w-4 h-4 text-white" />
-        </View>
-      )}
-
-      {uploading && (
-        <View 
-          className={cn(
-            "absolute -bottom-1 -right-1",
-            "w-8 h-8 bg-gray-500 rounded-full",
-            "flex items-center justify-center shadow-md",
-            "border-2 border-white"
-          )}
-        >
-          <View className="i-heroicons-arrow-path-20-solid w-4 h-4 text-white animate-spin" />
-        </View>
-      )}
-    </View>
-  )
-}

+ 0 - 46
mini/src/components/ui/button.tsx

@@ -1,46 +0,0 @@
-import { Button as TaroButton, ButtonProps as TaroButtonProps } from '@tarojs/components'
-import { cn } from '@/utils/cn'
-import { cva, type VariantProps } from 'class-variance-authority'
-
-const buttonVariants = cva(
-  'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background',
-  {
-    variants: {
-      variant: {
-        default: 'bg-primary text-primary-foreground hover:bg-primary/90',
-        destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
-        outline: 'border border-input hover:bg-accent hover:text-accent-foreground',
-        secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
-        ghost: 'hover:bg-accent hover:text-accent-foreground',
-        link: 'underline-offset-4 hover:underline text-primary',
-      },
-      size: {
-        default: 'h-10 py-2 px-4',
-        sm: 'h-9 px-3 rounded-md text-xs',
-        lg: 'h-11 px-8 rounded-md',
-        icon: 'h-10 w-10',
-      },
-    },
-    defaultVariants: {
-      variant: 'default',
-      size: 'default',
-    },
-  }
-)
-
-interface ButtonProps extends Omit<TaroButtonProps, 'size'>, VariantProps<typeof buttonVariants> {
-  className?: string
-  children?: React.ReactNode
-}
-
-export function Button({ className, variant, size, ...props }: ButtonProps) {
-  return (
-    <TaroButton
-      className={cn(buttonVariants({ variant, size, className }))}
-      {...props}
-    />
-  )
-}
-
-// 预定义的按钮样式导出
-export { buttonVariants }

+ 0 - 54
mini/src/components/ui/card.tsx

@@ -1,54 +0,0 @@
-import { View } from '@tarojs/components'
-import { cn } from '@/utils/cn'
-
-interface CardProps {
-  className?: string
-  children: React.ReactNode
-}
-
-export function Card({ className, children }: CardProps) {
-  return (
-    <View className={cn("bg-white rounded-xl shadow-sm", className)}>
-      {children}
-    </View>
-  )
-}
-
-interface CardHeaderProps {
-  className?: string
-  children: React.ReactNode
-}
-
-export function CardHeader({ className, children }: CardHeaderProps) {
-  return (
-    <View className={cn("p-4 border-b border-gray-100", className)}>
-      {children}
-    </View>
-  )
-}
-
-interface CardContentProps {
-  className?: string
-  children: React.ReactNode
-}
-
-export function CardContent({ className, children }: CardContentProps) {
-  return (
-    <View className={cn("p-4", className)}>
-      {children}
-    </View>
-  )
-}
-
-interface CardFooterProps {
-  className?: string
-  children: React.ReactNode
-}
-
-export function CardFooter({ className, children }: CardFooterProps) {
-  return (
-    <View className={cn("p-4 border-t border-gray-100", className)}>
-      {children}
-    </View>
-  )
-}

+ 0 - 95
mini/src/components/ui/dialog.tsx

@@ -1,95 +0,0 @@
-import { useEffect } from 'react'
-import { View, Text } from '@tarojs/components'
-import { cn } from '@/utils/cn'
-
-interface DialogProps {
-  open: boolean
-  onOpenChange: (open: boolean) => void
-  children: React.ReactNode
-}
-
-export function Dialog({ open, onOpenChange, children }: DialogProps) {
-  useEffect(() => {
-    if (open) {
-      // 在 Taro 中,我们可以使用模态框或者自定义弹窗
-      // 这里使用自定义实现
-    }
-  }, [open])
-
-  const handleBackdropClick = () => {
-    onOpenChange(false)
-  }
-
-  const handleContentClick = (e: any) => {
-    // 阻止事件冒泡,避免点击内容区域时关闭弹窗
-    e.stopPropagation()
-  }
-
-  if (!open) return null
-
-  return (
-    <View
-      className="fixed inset-0 z-50 flex items-center justify-center bg-black/50"
-      onClick={handleBackdropClick}
-    >
-      <View
-        className="relative bg-white rounded-lg shadow-lg max-w-md w-full mx-4"
-        onClick={handleContentClick}
-      >
-        {children}
-      </View>
-    </View>
-  )
-}
-
-interface DialogContentProps {
-  className?: string
-  children: React.ReactNode
-}
-
-export function DialogContent({ className, children }: DialogContentProps) {
-  return (
-    <View className={cn("p-6", className)}>
-      {children}
-    </View>
-  )
-}
-
-interface DialogHeaderProps {
-  className?: string
-  children: React.ReactNode
-}
-
-export function DialogHeader({ className, children }: DialogHeaderProps) {
-  return (
-    <View className={cn("mb-4", className)}>
-      {children}
-    </View>
-  )
-}
-
-interface DialogTitleProps {
-  className?: string
-  children: React.ReactNode
-}
-
-export function DialogTitle({ className, children }: DialogTitleProps) {
-  return (
-    <Text className={cn("text-lg font-semibold text-gray-900", className)}>
-      {children}
-    </Text>
-  )
-}
-
-interface DialogFooterProps {
-  className?: string
-  children: React.ReactNode
-}
-
-export function DialogFooter({ className, children }: DialogFooterProps) {
-  return (
-    <View className={cn("flex justify-end space-x-2", className)}>
-      {children}
-    </View>
-  )
-}

+ 0 - 168
mini/src/components/ui/form.tsx

@@ -1,168 +0,0 @@
-import * as React from "react"
-import { View, Text } from "@tarojs/components"
-import { Slot } from "@radix-ui/react-slot"
-import {
-  Controller,
-  FormProvider,
-  useFormContext,
-  useFormState,
-  type ControllerProps,
-  type FieldPath,
-  type FieldValues,
-} from "react-hook-form"
-
-import { cn } from '@/utils/cn'
-import { Label } from '@/components/ui/label'
-
-const Form = FormProvider
-
-type FormFieldContextValue<
-  TFieldValues extends FieldValues = FieldValues,
-  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
-> = {
-  name: TName
-}
-
-const FormFieldContext = React.createContext<FormFieldContextValue>(
-  {} as FormFieldContextValue
-)
-
-const FormField = <
-  TFieldValues extends FieldValues = FieldValues,
-  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
->({
-  ...props
-}: ControllerProps<TFieldValues, TName, TFieldValues>) => {
-  const ControllerWrapper = (props: any) => (
-    // @ts-ignore
-    <Controller {...props} />
-  )
-  return (
-    <FormFieldContext.Provider value={{ name: props.name }}>
-      <ControllerWrapper {...props} />
-    </FormFieldContext.Provider>
-  )
-}
-
-const useFormField = () => {
-  const fieldContext = React.useContext(FormFieldContext)
-  const itemContext = React.useContext(FormItemContext)
-  const { getFieldState } = useFormContext()
-  const formState = useFormState({ name: fieldContext.name })
-  const fieldState = getFieldState(fieldContext.name, formState)
-
-  if (!fieldContext) {
-    throw new Error("useFormField should be used within <FormField>")
-  }
-
-  const { id } = itemContext
-
-  return {
-    id,
-    name: fieldContext.name,
-    formItemId: `${id}-form-item`,
-    formDescriptionId: `${id}-form-item-description`,
-    formMessageId: `${id}-form-item-message`,
-    ...fieldState,
-  }
-}
-
-type FormItemContextValue = {
-  id: string
-}
-
-const FormItemContext = React.createContext<FormItemContextValue>(
-  {} as FormItemContextValue
-)
-
-function FormItem({ className, ...props }: React.ComponentProps<typeof View>) {
-  const id = React.useId()
-
-  return (
-    <FormItemContext.Provider value={{ id }}>
-      <View
-        className={cn("grid gap-2", className)}
-        {...props}
-      />
-    </FormItemContext.Provider>
-  )
-}
-
-function FormLabel({
-  className,
-  ...props
-}: React.ComponentProps<typeof Label>) {
-  const { error, formItemId } = useFormField()
-
-  return (
-    <Label
-      data-slot="form-label"
-      data-error={!!error}
-      className={cn("data-[error=true]:text-destructive", className)}
-      htmlFor={formItemId}
-      {...props}
-    />
-  )
-}
-
-function FormControl({ ...props }: React.ComponentProps<typeof Slot>) {
-  const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
-
-  return (
-    <Slot
-      data-slot="form-control"
-      id={formItemId}
-      aria-describedby={
-        !error
-          ? `${formDescriptionId}`
-          : `${formDescriptionId} ${formMessageId}`
-      }
-      aria-invalid={!!error}
-      {...props}
-    />
-  )
-}
-
-function FormDescription({ className, ...props }: React.ComponentProps<typeof Text>) {
-  const { formDescriptionId } = useFormField()
-
-  return (
-    <Text
-      data-slot="form-description"
-      id={formDescriptionId}
-      className={cn("text-muted-foreground text-sm", className)}
-      {...props}
-    />
-  )
-}
-
-function FormMessage({ className, ...props }: React.ComponentProps<typeof Text>) {
-  const { error, formMessageId } = useFormField()
-  const body = error ? String(error?.message ?? "") : props.children
-
-  if (!body) {
-    return null
-  }
-
-  return (
-    <Text
-      data-slot="form-message"
-      id={formMessageId}
-      className={cn("text-destructive text-sm", className)}
-      {...props}
-    >
-      {body}
-    </Text>
-  )
-}
-
-export {
-  useFormField,
-  Form,
-  FormItem,
-  FormLabel,
-  FormControl,
-  FormDescription,
-  FormMessage,
-  FormField,
-}

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

@@ -1,127 +0,0 @@
-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>
-  )
-}

+ 0 - 102
mini/src/components/ui/input.tsx

@@ -1,102 +0,0 @@
-import { Input as TaroInput, InputProps as TaroInputProps, View, Text } from '@tarojs/components'
-import { cn } from '@/utils/cn'
-import { cva, type VariantProps } from 'class-variance-authority'
-import { forwardRef } from 'react'
-
-const inputVariants = cva(
-  'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
-  {
-    variants: {
-      variant: {
-        default: 'border-gray-300 focus:border-blue-500 focus:ring-blue-500',
-        outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
-        filled: 'border-none bg-gray-50 hover:bg-gray-100',
-      },
-      size: {
-        default: 'h-10 px-3 py-2',
-        sm: 'h-9 px-2 text-sm',
-        lg: 'h-11 px-4 text-lg',
-        icon: 'h-10 w-10',
-      },
-    },
-    defaultVariants: {
-      variant: 'default',
-      size: 'default',
-    },
-  }
-)
-
-export interface InputProps extends Omit<TaroInputProps, 'className' | 'onChange'>, VariantProps<typeof inputVariants> {
-  className?: string
-  leftIcon?: string
-  rightIcon?: string
-  error?: boolean
-  errorMessage?: string
-  onLeftIconClick?: () => void
-  onRightIconClick?: () => void
-  onChange?: (value: string, event: any) => void
-}
-
-const Input = forwardRef<any, InputProps>(
-  ({ className, variant, size, leftIcon, rightIcon, error, errorMessage, onLeftIconClick, onRightIconClick, onChange, ...props }, ref) => {
-    const handleInput = (event: any) => {
-      const value = event.detail.value
-      onChange?.(value, event)
-      
-      // 同时调用原始的onInput(如果提供了)
-      if (props.onInput) {
-        props.onInput(event)
-      }
-    }
-
-    return (
-      <View className="w-full">
-        <View className="relative">
-          {leftIcon && (
-            <View
-              className={cn(
-                "absolute left-3 top-1/2 -translate-y-1/2",
-                onLeftIconClick ? "cursor-pointer" : "pointer-events-none"
-              )}
-              onClick={onLeftIconClick}
-            >
-              <View className={cn('w-5 h-5 text-gray-400', leftIcon)} />
-            </View>
-          )}
-          
-          <TaroInput
-            ref={ref}
-            className={cn(
-              inputVariants({ variant, size, className }),
-              error && 'border-red-500 focus:border-red-500 focus:ring-red-500',
-              leftIcon && 'pl-10',
-              rightIcon && 'pr-10',
-            )}
-            onInput={handleInput}
-            {...props}
-          />
-          
-          {rightIcon && (
-            <View
-              className={cn(
-                "absolute right-3 top-1/2 -translate-y-1/2",
-                onRightIconClick ? "cursor-pointer" : "pointer-events-none"
-              )}
-              onClick={onRightIconClick}
-            >
-              <View className={cn('w-5 h-5 text-gray-400', rightIcon)} />
-            </View>
-          )}
-        </View>
-        
-        {error && errorMessage && (
-          <Text className="mt-1 text-sm text-red-600">{errorMessage}</Text>
-        )}
-      </View>
-    )
-  }
-)
-
-Input.displayName = 'Input'
-
-export { Input, inputVariants }

+ 0 - 55
mini/src/components/ui/label.tsx

@@ -1,55 +0,0 @@
-import { View, Text } from '@tarojs/components'
-import { cn } from '@/utils/cn'
-import { cva, type VariantProps } from 'class-variance-authority'
-import { forwardRef } from 'react'
-
-const labelVariants = cva(
-  'text-sm font-medium',
-  {
-    variants: {
-      variant: {
-        default: 'text-gray-900',
-        secondary: 'text-gray-600',
-        destructive: 'text-red-600',
-      },
-      size: {
-        default: 'text-sm',
-        sm: 'text-xs',
-        lg: 'text-base',
-      },
-    },
-    defaultVariants: {
-      variant: 'default',
-      size: 'default',
-    },
-  }
-)
-
-export interface LabelProps {
-  className?: string
-  variant?: VariantProps<typeof labelVariants>['variant']
-  size?: VariantProps<typeof labelVariants>['size']
-  children: React.ReactNode
-  required?: boolean
-  htmlFor?: string
-}
-
-const Label = forwardRef<HTMLLabelElement, LabelProps>(
-  ({ className, variant, size, children, required, htmlFor, ...props }, _ref) => {
-    return (
-      <View className="mb-2">
-        <Text
-          className={cn(labelVariants({ variant, size, className }))}
-          {...props}
-        >
-          {children}
-          {required && <Text className="text-red-500 ml-1">*</Text>}
-        </Text>
-      </View>
-    )
-  }
-)
-
-Label.displayName = 'Label'
-
-export { Label, labelVariants }

+ 0 - 230
mini/src/components/ui/navbar.tsx

@@ -1,230 +0,0 @@
-import React from 'react'
-import { View, Text } from '@tarojs/components'
-import { cn } from '@/utils/cn'
-import Taro from '@tarojs/taro'
-import { isWeapp } from '@/utils/platform'
-
-export interface NavbarProps {
-  title?: string
-  leftText?: string
-  leftIcon?: string
-  rightText?: string
-  rightIcon?: string
-  backgroundColor?: string
-  textColor?: string
-  border?: boolean
-  fixed?: boolean
-  placeholder?: boolean
-  onClickLeft?: () => void
-  onClickRight?: () => void
-  children?: React.ReactNode
-  className?: string
-  /** 是否在小程序环境下隐藏右侧按钮(默认false,会自动避让) */
-  hideRightInWeapp?: boolean
-}
-
-const systemInfo = Taro.getSystemInfoSync()
-const menuButtonInfo = isWeapp() ? Taro.getMenuButtonBoundingClientRect() : undefined
-
-// 计算导航栏高度
-const NAVBAR_HEIGHT = 44
-const STATUS_BAR_HEIGHT = systemInfo.statusBarHeight || 0
-const TOTAL_HEIGHT = STATUS_BAR_HEIGHT + NAVBAR_HEIGHT
-
-export const Navbar: React.FC<NavbarProps> = ({
-  title,
-  leftText,
-  leftIcon = 'i-heroicons-chevron-left-20-solid',
-  rightText,
-  rightIcon,
-  backgroundColor = 'bg-white',
-  textColor = 'text-gray-900',
-  border = true,
-  fixed = true,
-  placeholder = true,
-  onClickLeft,
-  onClickRight,
-  children,
-  className,
-  hideRightInWeapp,
-}) => {
-  // 处理左侧点击
-  const handleLeftClick = () => {
-    if (onClickLeft) {
-      onClickLeft()
-    } else {
-      // 默认返回上一页
-      Taro.navigateBack()
-    }
-  }
-
-  // 渲染左侧内容
-  const renderLeft = () => {
-    if (children) return null
-    
-    return (
-      <View 
-        className="absolute left-3 top-0 bottom-0 flex items-center z-10"
-        style={{ height: NAVBAR_HEIGHT }}
-        onClick={handleLeftClick}
-      >
-        <View className="flex items-center">
-          {leftIcon && (
-            <View className={cn(leftIcon, 'w-5 h-5', textColor)} />
-          )}
-          {leftText && (
-            <Text className={cn('ml-1 text-sm', textColor)}>{leftText}</Text>
-          )}
-        </View>
-      </View>
-    )
-  }
-
-  // 渲染右侧内容
-  const renderRight = () => {
-    if (!rightText && !rightIcon || (hideRightInWeapp && isWeapp())) return null
-        
-    if (isWeapp() && menuButtonInfo) {
-      // 小程序环境下,调整右侧按钮位置
-      return (
-        <View
-          className="absolute top-0 bottom-0 flex items-center z-10"
-          style={{
-            height: NAVBAR_HEIGHT,
-            right: `${systemInfo.screenWidth - menuButtonInfo.left + 10}px`,
-          }}
-          onClick={onClickRight}
-        >
-          <View className="flex items-center">
-            {rightText && (
-              <Text className={cn('mr-1 text-sm', textColor)}>{rightText}</Text>
-            )}
-            {rightIcon && (
-              <View className={cn(rightIcon, 'w-5 h-5', textColor)} />
-            )}
-          </View>
-        </View>
-      )
-    }
-    
-    // H5或其他平台,保持原有样式
-    return (
-      <View
-        className="absolute right-3 top-0 bottom-0 flex items-center z-10"
-        style={{ height: NAVBAR_HEIGHT }}
-        onClick={onClickRight}
-      >
-        <View className="flex items-center">
-          {rightText && (
-            <Text className={cn('mr-1 text-sm', textColor)}>{rightText}</Text>
-          )}
-          {rightIcon && (
-            <View className={cn(rightIcon, 'w-5 h-5', textColor)} />
-          )}
-        </View>
-      </View>
-    )
-  }
-
-  // 渲染标题
-  const renderTitle = () => {
-    if (children) return children
-    
-    if (isWeapp() && menuButtonInfo) {
-      // 小程序环境下,调整标题位置
-      return (
-        <View className="flex-1 flex items-center justify-center">
-          <Text
-            className={cn('text-base font-semibold truncate', textColor)}
-            style={{
-              maxWidth: `calc(100% - ${systemInfo.screenWidth - menuButtonInfo.right + 10}px - 60px - 60px)`
-            }}
-          >
-            {title}
-          </Text>
-        </View>
-      )
-    }
-    
-    // H5或其他平台,保持原有样式
-    return (
-      <Text className={cn('text-base font-semibold', textColor)}>
-        {title}
-      </Text>
-    )
-  }
-
-  // 导航栏样式
-  const navbarStyle = {
-    height: TOTAL_HEIGHT,
-    paddingTop: STATUS_BAR_HEIGHT,
-  }
-
-  return (
-    <>
-      <View
-        className={cn(
-          'relative w-full',
-          backgroundColor,
-          border && 'border-b border-gray-200',
-          fixed && 'fixed top-0 left-0 right-0 z-50',
-          className
-        )}
-        style={navbarStyle}
-      >
-        {/* 导航栏内容 */}
-        <View
-          className="relative flex items-center justify-center"
-          style={{ height: NAVBAR_HEIGHT }}
-        >
-          {renderLeft()}
-          {renderTitle()}
-          {renderRight()}
-        </View>
-      </View>
-      
-      {/* 占位元素 */}
-      {fixed && placeholder && (
-        <View style={{ height: TOTAL_HEIGHT }} />
-      )}
-    </>
-  )
-}
-
-// 预设样式
-export const NavbarPresets = {
-  // 默认白色导航栏
-  default: {
-    backgroundColor: 'bg-white',
-    textColor: 'text-gray-900',
-    border: true,
-  },
-  
-  // 深色导航栏
-  dark: {
-    backgroundColor: 'bg-gray-900',
-    textColor: 'text-white',
-    border: true,
-  },
-  
-  // 透明导航栏
-  transparent: {
-    backgroundColor: 'bg-transparent',
-    textColor: 'text-white',
-    border: false,
-  },
-  
-  // 主色调导航栏
-  primary: {
-    backgroundColor: 'bg-blue-500',
-    textColor: 'text-white',
-    border: false,
-  },
-}
-
-// 快捷创建函数
-export const createNavbar = (preset: keyof typeof NavbarPresets) => {
-  return NavbarPresets[preset]
-}
-
-export default Navbar

+ 0 - 37
mini/src/components/ui/page-container.tsx

@@ -1,37 +0,0 @@
-import React, { ReactNode } from 'react'
-import { View } from '@tarojs/components'
-import { cn } from '@/utils/cn'
-
-export interface PageContainerProps {
-  children: ReactNode
-  className?: string
-  padding?: boolean
-  background?: string
-  safeArea?: boolean
-}
-
-export const PageContainer: React.FC<PageContainerProps> = ({
-  children,
-  className,
-  padding = true,
-  background = 'bg-gray-50',
-  safeArea = true,
-}) => {
-  return (
-    <View className={cn(
-      'min-h-screen w-full',
-      background,
-      safeArea && 'pb-safe',
-      className
-    )}>
-      <View className={cn(
-        padding && 'px-4 py-4',
-        'max-w-screen-md mx-auto'
-      )}>
-        {children}
-      </View>
-    </View>
-  )
-}
-
-export default PageContainer

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

@@ -1,155 +0,0 @@
-import React from 'react'
-import { View, Text } from '@tarojs/components'
-import clsx from 'clsx'
-
-export interface TabBarItem {
-  key: string
-  title: string
-  icon?: string
-  selectedIcon?: string
-  iconClass?: string
-  selectedIconClass?: 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 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.iconClass || item.icon) && (
-                  <View
-                    className={clsx(
-                      'mb-1',
-                      'flex items-center justify-center',
-                      item.iconClass ? 'w-6 h-6' : 'text-2xl',
-                      isActive ? 'text-blue-500' : 'text-gray-500'
-                    )}
-                    style={{
-                      color: isActive ? selectedColor : color,
-                    }}
-                  >
-                    {item.iconClass ? (
-                      <View
-                        className={clsx(
-                          isActive && item.selectedIconClass
-                            ? item.selectedIconClass
-                            : item.iconClass,
-                          'w-full h-full'
-                        )}
-                      />
-                    ) : (
-                      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 }

+ 0 - 58
mini/src/components/ui/user-status-bar.tsx

@@ -1,58 +0,0 @@
-import React from 'react'
-import { View, Text, Image } from '@tarojs/components'
-import { cn } from '@/utils/cn'
-
-export interface UserStatusBarProps {
-  userName?: string
-  avatarUrl?: string
-  companyName?: string
-  notificationCount?: number
-  className?: string
-}
-
-export const UserStatusBar: React.FC<UserStatusBarProps> = ({
-  userName = '企业用户',
-  avatarUrl,
-  companyName = '企业名称',
-  notificationCount = 0,
-  className,
-}) => {
-  return (
-    <View className={cn(
-      'flex items-center justify-between px-4 py-3 bg-white border-b border-gray-200',
-      className
-    )}>
-      <View className="flex items-center">
-        {avatarUrl ? (
-          <Image
-            src={avatarUrl}
-            className="w-10 h-10 rounded-full mr-3"
-            mode="aspectFill"
-          />
-        ) : (
-          <View className="w-10 h-10 rounded-full bg-blue-500 flex items-center justify-center mr-3">
-            <Text className="text-white font-bold text-lg">
-              {userName.charAt(0).toUpperCase()}
-            </Text>
-          </View>
-        )}
-        <View>
-          <Text className="font-semibold text-gray-900">{userName}</Text>
-          <Text className="text-sm text-gray-600">{companyName}</Text>
-        </View>
-      </View>
-      <View className="relative">
-        <View className="i-heroicons-bell-20-solid w-6 h-6 text-gray-600" />
-        {notificationCount > 0 && (
-          <View className="absolute -top-1 -right-1 w-4 h-4 bg-red-500 rounded-full flex items-center justify-center">
-            <Text className="text-white text-xs font-bold">
-              {notificationCount > 99 ? '99+' : notificationCount}
-            </Text>
-          </View>
-        )}
-      </View>
-    </View>
-  )
-}
-
-export default UserStatusBar

+ 0 - 29
mini/src/hooks/useRequireAuth.ts

@@ -1,29 +0,0 @@
-import { useEffect } from 'react'
-import Taro from '@tarojs/taro'
-import { useAuth } from '@/utils/auth'
-
-/**
- * 要求认证的hook
- * 如果用户未登录,则重定向到登录页
- */
-export const useRequireAuth = () => {
-  const { isLoggedIn, isLoading } = useAuth()
-
-  useEffect(() => {
-    if (!isLoading && !isLoggedIn) {
-      Taro.showToast({
-        title: '请先登录',
-        icon: 'none',
-        duration: 1500
-      })
-
-      setTimeout(() => {
-        Taro.redirectTo({
-          url: '/pages/login/index'
-        })
-      }, 1500)
-    }
-  }, [isLoggedIn, isLoading])
-
-  return { isLoggedIn, isLoading }
-}

+ 0 - 26
mini/src/schemas/register.schema.ts

@@ -1,26 +0,0 @@
-import { z } from 'zod'
-
-export const registerSchema = z.object({
-  username: z.string()
-    .min(3, '用户名至少3个字符')
-    .max(20, '用户名最多20个字符')
-    .regex(/^[a-zA-Z0-9_]+$/, '用户名只能包含字母、数字和下划线'),
-  
-  email: z.string()
-    .optional()
-    .refine(
-      (val) => !val || /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val),
-      '请输入有效的邮箱地址'
-    ),
-  
-  password: z.string()
-    .min(6, '密码至少6位')
-    .max(20, '密码最多20位'),
-  
-  confirmPassword: z.string()
-}).refine((data) => data.password === data.confirmPassword, {
-  message: '两次输入的密码不一致',
-  path: ['confirmPassword']
-})
-
-export type RegisterFormData = z.infer<typeof registerSchema>

+ 0 - 172
mini/src/utils/auth.tsx

@@ -1,172 +0,0 @@
-import { createContext, useContext, PropsWithChildren } from 'react'
-import Taro from '@tarojs/taro'
-import { enterpriseAuthClient } from '../api'
-import { InferResponseType, InferRequestType } from 'hono'
-import { QueryClient, useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
-
-// 用户类型定义 - 使用企业用户认证
-export type User = InferResponseType<typeof enterpriseAuthClient.me.$get, 200>
-type LoginRequest = InferRequestType<typeof enterpriseAuthClient.login.$post>['json']
-// 企业用户注册可能由管理员创建,前端不提供注册接口
-type RegisterRequest = { username: string; password: string }
-
-interface AuthContextType {
-  user: User | null
-  login: (data: LoginRequest) => Promise<User>
-  logout: () => Promise<void>
-  register: (data: RegisterRequest) => Promise<User>
-  updateUser: (userData: Partial<User>) => void
-  isLoading: boolean
-  isLoggedIn: boolean
-}
-
-const AuthContext = createContext<AuthContextType | undefined>(undefined)
-
-const queryClient = new QueryClient()
-
-export const AuthProvider: React.FC<PropsWithChildren> = ({ children }) => {
-  const queryClient = useQueryClient()
-
-  const { data: user, isLoading } = useQuery<User | null, Error>({
-    queryKey: ['currentUser'],
-    queryFn: async () => {
-      const token = Taro.getStorageSync('enterprise_token')
-      if (!token) {
-        return null
-      }
-      try {
-        const response = await enterpriseAuthClient.me.$get({})
-        if (response.status !== 200) {
-          throw new Error('获取用户信息失败')
-        }
-        const user = await response.json()
-        Taro.setStorageSync('enterpriseUserInfo', JSON.stringify(user))
-        return user
-      } catch (error) {
-        Taro.removeStorageSync('enterprise_token')
-        Taro.removeStorageSync('enterpriseUserInfo')
-        return null
-      }
-    },
-    staleTime: Infinity, // 用户信息不常变动,设为无限期
-    refetchOnWindowFocus: false, // 失去焦点不重新获取
-    refetchOnReconnect: false, // 网络重连不重新获取
-  })
-
-  const loginMutation = useMutation<User, Error, LoginRequest>({
-    mutationFn: async (data) => {
-      const response = await enterpriseAuthClient.login.$post({ json: data })
-      if (response.status !== 200) {
-        throw new Error('登录失败')
-      }
-      const { token, user } = await response.json()
-      Taro.setStorageSync('enterprise_token', token)
-      Taro.setStorageSync('enterpriseUserInfo', JSON.stringify(user))
-      // if (refresh_token) {
-      //   Taro.setStorageSync('enterprise_refresh_token', refresh_token)
-      // }
-      return user
-    },
-    onSuccess: (newUser) => {
-      queryClient.setQueryData(['currentUser'], newUser)
-    },
-    onError: (error) => {
-      Taro.showToast({
-        title: error.message || '登录失败,请检查用户名和密码',
-        icon: 'none',
-        duration: 2000,
-      })
-    },
-  })
-
-  const registerMutation = useMutation<User, Error, RegisterRequest>({
-    mutationFn: async () => {
-      // 企业用户注册由管理员创建,前端不提供注册接口
-      throw new Error('企业用户注册请联系管理员创建账户')
-    },
-    onSuccess: (newUser) => {
-      queryClient.setQueryData(['currentUser'], newUser)
-    },
-    onError: (error) => {
-      Taro.showToast({
-        title: error.message || '企业用户注册请联系管理员',
-        icon: 'none',
-        duration: 3000,
-      })
-    },
-  })
-
-  const logoutMutation = useMutation<void, Error>({
-    mutationFn: async () => {
-      try {
-        const response = await enterpriseAuthClient.logout.$post({})
-        if (response.status !== 200) {
-          throw new Error('登出失败')
-        }
-      } catch (error) {
-        console.error('Logout error:', error)
-      } finally {
-        Taro.removeStorageSync('enterprise_token')
-        Taro.removeStorageSync('enterprise_refresh_token')
-        Taro.removeStorageSync('enterpriseUserInfo')
-      }
-    },
-    onSuccess: () => {
-      queryClient.setQueryData(['currentUser'], null)
-      Taro.redirectTo({ url: '/pages/login/index' })
-    },
-    onError: (error) => {
-      Taro.showToast({
-        title: error.message || '登出失败',
-        icon: 'none',
-        duration: 2000,
-      })
-    },
-  })
-
-  const updateUserMutation = useMutation<User, Error, Partial<User>>({
-    mutationFn: async () => {
-      // 企业用户信息更新可能由管理员管理,前端不提供更新接口
-      throw new Error('企业用户信息更新请联系管理员')
-    },
-    onSuccess: (updatedUser) => {
-      queryClient.setQueryData(['currentUser'], updatedUser)
-      Taro.showToast({
-        title: '更新成功',
-        icon: 'success',
-        duration: 2000,
-      })
-    },
-    onError: (error) => {
-      Taro.showToast({
-        title: error.message || '更新失败,请重试',
-        icon: 'none',
-        duration: 2000,
-      })
-    },
-  })
-
-  const updateUser = updateUserMutation.mutateAsync
-
-  const value = {
-    user: user || null,
-    login: loginMutation.mutateAsync,
-    logout: logoutMutation.mutateAsync,
-    register: registerMutation.mutateAsync,
-    updateUser,
-    isLoading: isLoading || loginMutation.isPending || registerMutation.isPending || logoutMutation.isPending,
-    isLoggedIn: !!user,
-  }
-
-  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
-}
-
-export const useAuth = () => {
-  const context = useContext(AuthContext)
-  if (context === undefined) {
-    throw new Error('useAuth must be used within an AuthProvider')
-  }
-  return context
-}
-
-export { queryClient }

+ 0 - 2
mini/src/utils/cn.ts

@@ -1,2 +0,0 @@
-// 从共享UI组件包重新导出cn函数
-export { cn } from '@d8d/mini-shared-ui-components'

+ 0 - 79
mini/src/utils/headers-polyfill.js

@@ -1,79 +0,0 @@
-class Headers {
-    constructor(init = {}) {
-      this._headers = {};
-  
-      if (init instanceof Headers) {
-        // 如果传入的是另一个 Headers 实例,复制其内容
-        init.forEach((value, name) => {
-          this.append(name, value);
-        });
-      } else if (init) {
-        // 处理普通对象或数组
-        Object.entries(init).forEach(([name, value]) => {
-          if (Array.isArray(value)) {
-            // 处理数组值(如 ['value1', 'value2'])
-            value.forEach(v => this.append(name, v));
-          } else {
-            this.set(name, value);
-          }
-        });
-      }
-    }
-  
-    // 添加头(可重复添加同名头)
-    append(name, value) {
-      const normalizedName = this._normalizeName(name);
-      if (this._headers[normalizedName]) {
-        this._headers[normalizedName] += `, ${value}`;
-      } else {
-        this._headers[normalizedName] = String(value);
-      }
-    }
-  
-    // 设置头(覆盖同名头)
-    set(name, value) {
-      this._headers[this._normalizeName(name)] = String(value);
-    }
-  
-    // 获取头
-    get(name) {
-      return this._headers[this._normalizeName(name)] || null;
-    }
-  
-    // 检查是否存在头
-    has(name) {
-      return this._normalizeName(name) in this._headers;
-    }
-  
-    // 删除头
-    delete(name) {
-      delete this._headers[this._normalizeName(name)];
-    }
-  
-    // 遍历头
-    forEach(callback) {
-      Object.entries(this._headers).forEach(([name, value]) => {
-        callback(value, name, this);
-      });
-    }
-  
-    // 获取所有头(原始对象)
-    raw() {
-      return { ...this._headers };
-    }
-  
-    // 规范化头名称(转为小写)
-    _normalizeName(name) {
-      if (typeof name !== 'string') {
-        throw new TypeError('Header name must be a string');
-      }
-      return name.toLowerCase();
-    }
-  }
-  
-  // 全局注册(如果需要)
-  if (typeof globalThis.Headers === 'undefined') {
-    globalThis.Headers = Headers;
-  }
-  
-  export default Headers;

+ 0 - 879
mini/src/utils/minio.ts

@@ -1,879 +0,0 @@
-import type { InferResponseType } from 'hono/client';
-import { fileClient } from "../api";
-import { isWeapp, isH5 } from './platform';
-import Taro from '@tarojs/taro';
-
-// 平台检测 - 使用统一的 platform.ts
-const isMiniProgram = isWeapp();
-const isBrowser = isH5();
-
-export interface MinioProgressEvent {
-  stage: 'uploading' | 'complete' | 'error';
-  message: string;
-  progress: number;
-  details?: {
-      loaded: number;
-      total: number;
-  };
-  timestamp: number;
-}
-
-export interface MinioProgressCallbacks {
-  onProgress?: (event: MinioProgressEvent) => void;
-  onComplete?: () => void;
-  onError?: (error: Error) => void;
-  signal?: AbortSignal | { aborted: boolean };
-}
-
-export interface UploadResult {
-  fileUrl: string;
-  fileKey: string;
-  bucketName: string;
-  fileId: number;
-}
-
-interface UploadPart {
-  ETag: string;
-  PartNumber: number;
-}
-
-interface UploadProgressDetails {
-  partNumber: number;
-  totalParts: number;
-  partSize: number;
-  totalSize: number;
-  partProgress?: number;
-}
-
-type MinioMultipartUploadPolicy = InferResponseType<typeof fileClient["multipart-policy"]['$post'], 200>
-type MinioUploadPolicy = InferResponseType<typeof fileClient["upload-policy"]['$post'], 200>
-
-const PART_SIZE = 5 * 1024 * 1024; // 每部分5MB
-
-// ==================== H5 实现(保留原有代码) ====================
-export class MinIOXHRMultipartUploader {
-  /**
-   * 使用XHR分段上传文件到MinIO(H5环境)
-   */
-  static async upload(
-    policy: MinioMultipartUploadPolicy,
-    file: File | Blob,
-    key: string,
-    callbacks?: MinioProgressCallbacks
-  ): Promise<UploadResult> {
-    const partSize = PART_SIZE;
-    const totalSize = file.size;
-    const totalParts = Math.ceil(totalSize / partSize);
-    const uploadedParts: UploadPart[] = [];
-    
-    callbacks?.onProgress?.({
-      stage: 'uploading',
-      message: '准备上传文件...',
-      progress: 0,
-      details: {
-        loaded: 0,
-        total: totalSize
-      },
-      timestamp: Date.now()
-    });
-    
-    // 分段上传
-    for (let i = 0; i < totalParts; i++) {
-      const start = i * partSize;
-      const end = Math.min(start + partSize, totalSize);
-      const partBlob = file.slice(start, end);
-      const partNumber = i + 1;
-      
-      try {
-        const etag = await this.uploadPart(
-          policy.partUrls[i],
-          partBlob,
-          callbacks,
-          {
-            partNumber,
-            totalParts,
-            partSize: partBlob.size,
-            totalSize
-          }
-        );
-        
-        uploadedParts.push({
-          ETag: etag,
-          PartNumber: partNumber
-        });
-        
-        // 更新进度
-        const progress = Math.round((end / totalSize) * 100);
-        callbacks?.onProgress?.({
-          stage: 'uploading',
-          message: `上传文件片段 ${partNumber}/${totalParts}`,
-          progress,
-          details: {
-            loaded: end,
-            total: totalSize,
-          },
-          timestamp: Date.now()
-        });
-      } catch (error) {
-        callbacks?.onError?.(error instanceof Error ? error : new Error(String(error)));
-        throw error;
-      }
-    }
-    
-    // 完成上传
-    try {
-      const result = await this.completeMultipartUpload(policy, key, uploadedParts);
-      
-      callbacks?.onProgress?.({
-        stage: 'complete',
-        message: '文件上传完成',
-        progress: 100,
-        timestamp: Date.now()
-      });
-      
-      callbacks?.onComplete?.();
-      return {
-        fileUrl: `${policy.host}/${key}`,
-        fileKey: key,
-        bucketName: policy.bucket,
-        fileId: result.fileId
-      };
-    } catch (error) {
-      callbacks?.onError?.(error instanceof Error ? error : new Error(String(error)));
-      throw error;
-    }
-  }
-  
-  // 上传单个片段
-  private static uploadPart(
-    uploadUrl: string,
-    partBlob: Blob,
-    callbacks?: MinioProgressCallbacks,
-    progressDetails?: UploadProgressDetails
-  ): Promise<string> {
-    return new Promise((resolve, reject) => {
-      const xhr = new XMLHttpRequest();
-      
-      xhr.upload.onprogress = (event) => {
-        if (event.lengthComputable && callbacks?.onProgress) {
-          const partProgress = Math.round((event.loaded / event.total) * 100);
-          callbacks.onProgress({
-            stage: 'uploading',
-            message: `上传文件片段 ${progressDetails?.partNumber}/${progressDetails?.totalParts} (${partProgress}%)`,
-            progress: Math.round((
-              (progressDetails?.partNumber ? (progressDetails.partNumber - 1) * (progressDetails.partSize || 0) : 0) + event.loaded
-            ) / (progressDetails?.totalSize || 1) * 100),
-            details: {
-              ...progressDetails,
-              loaded: event.loaded,
-              total: event.total
-            },
-            timestamp: Date.now()
-          });
-        }
-      };
-      
-      xhr.onload = () => {
-        if (xhr.status >= 200 && xhr.status < 300) {
-          const etag = xhr.getResponseHeader('ETag')?.replace(/"/g, '') || '';
-          resolve(etag);
-        } else {
-          reject(new Error(`上传片段失败: ${xhr.status} ${xhr.statusText}`));
-        }
-      };
-      
-      xhr.onerror = () => reject(new Error('上传片段失败'));
-      
-      xhr.open('PUT', uploadUrl);
-      xhr.send(partBlob);
-      
-      if (callbacks?.signal) {
-        if ('addEventListener' in callbacks.signal) {
-          callbacks.signal.addEventListener('abort', () => {
-            xhr.abort();
-            reject(new Error('上传已取消'));
-          });
-        }
-      }
-    });
-  }
-  
-  // 完成分段上传
-  private static async completeMultipartUpload(
-    policy: MinioMultipartUploadPolicy,
-    key: string,
-    uploadedParts: UploadPart[]
-  ): Promise<{ fileId: number }> {
-    const response = await fileClient["multipart-complete"].$post({
-      json: {
-        bucket: policy.bucket,
-        key,
-        uploadId: policy.uploadId,
-        parts: uploadedParts.map(part => ({ partNumber: part.PartNumber, etag: part.ETag }))
-      }
-    });
-    
-    if (!response.ok) {
-      throw new Error(`完成分段上传失败: ${response.status} ${response.statusText}`);
-    }
-    
-    return response.json();
-  }
-}
-
-export class MinIOXHRUploader {
-  /**
-   * 使用XHR上传文件到MinIO(H5环境)
-   */
-  static upload(
-    policy: MinioUploadPolicy,
-    file: File | Blob,
-    key: string,
-    callbacks?: MinioProgressCallbacks
-  ): Promise<UploadResult> {
-    const formData = new FormData();
-
-    // 添加 MinIO 需要的字段
-    Object.entries(policy.uploadPolicy).forEach(([k, value]) => {
-      if (k !== 'key' && k !== 'host' && k !== 'prefix' && k !== 'ossType' && typeof value === 'string') {
-        formData.append(k, value);
-      }
-    });
-    formData.append('key', key);
-    formData.append('file', file);
-
-    return new Promise((resolve, reject) => {
-      const xhr = new XMLHttpRequest();
-
-      // 上传进度处理
-      if (callbacks?.onProgress) {
-        xhr.upload.onprogress = (event) => {
-          if (event.lengthComputable) {
-            callbacks.onProgress?.({
-              stage: 'uploading',
-              message: '正在上传文件...',
-              progress: Math.round((event.loaded * 100) / event.total),
-              details: {
-                loaded: event.loaded,
-                total: event.total
-              },
-              timestamp: Date.now()
-            });
-          }
-        };
-      }
-
-      // 完成处理
-      xhr.onload = () => {
-        if (xhr.status >= 200 && xhr.status < 300) {
-          if (callbacks?.onProgress) {
-            callbacks.onProgress({
-              stage: 'complete',
-              message: '文件上传完成',
-              progress: 100,
-              timestamp: Date.now()
-            });
-          }
-          callbacks?.onComplete?.();
-          resolve({
-            fileUrl: `${policy.uploadPolicy.host}/${key}`,
-            fileKey: key,
-            bucketName: policy.uploadPolicy.bucket,
-            fileId: policy.file.id
-          });
-        } else {
-          const error = new Error(`上传失败: ${xhr.status} ${xhr.statusText}`);
-          callbacks?.onError?.(error);
-          reject(error);
-        }
-      };
-
-      // 错误处理
-      xhr.onerror = () => {
-        const error = new Error('上传失败');
-        if (callbacks?.onProgress) {
-          callbacks.onProgress({
-            stage: 'error',
-            message: '文件上传失败',
-            progress: 0,
-            timestamp: Date.now()
-          });
-        }
-        callbacks?.onError?.(error);
-        reject(error);
-      };
-
-      // 根据当前页面协议和 host 配置决定最终的上传地址
-      const currentProtocol = typeof window !== 'undefined' ? window.location.protocol : 'https:';
-      const host = policy.uploadPolicy.host?.startsWith('http')
-        ? policy.uploadPolicy.host
-        : `${currentProtocol}//${policy.uploadPolicy.host}`;
-      
-      xhr.open('POST', host);
-      xhr.send(formData);
-
-      // 处理取消
-      if (callbacks?.signal) {
-        if ('addEventListener' in callbacks.signal) {
-          callbacks.signal.addEventListener('abort', () => {
-            xhr.abort();
-            reject(new Error('上传已取消'));
-          });
-        }
-      }
-    });
-  }
-}
-
-// ==================== 小程序实现 ====================
-export class TaroMinIOMultipartUploader {
-  /**
-   * 使用 Taro 分段上传文件到 MinIO(小程序环境)
-   */
-  static async upload(
-    policy: MinioMultipartUploadPolicy,
-    filePath: string,
-    key: string,
-    callbacks?: MinioProgressCallbacks
-  ): Promise<UploadResult> {
-    const partSize = PART_SIZE;
-    
-    // 获取文件信息
-    const fileInfo = await getFileInfoPromise(filePath);
-    const totalSize = fileInfo.size;
-    const totalParts = Math.ceil(totalSize / partSize);
-    const uploadedParts: UploadPart[] = [];
-    
-    callbacks?.onProgress?.({
-      stage: 'uploading',
-      message: '准备上传文件...',
-      progress: 0,
-      details: {
-        loaded: 0,
-        total: totalSize
-      },
-      timestamp: Date.now()
-    });
-    
-    // 分段上传
-    for (let i = 0; i < totalParts; i++) {
-      if (callbacks?.signal && 'aborted' in callbacks.signal && callbacks.signal.aborted) {
-        throw new Error('上传已取消');
-      }
-      
-      const start = i * partSize;
-      const end = Math.min(start + partSize, totalSize);
-      const partNumber = i + 1;
-      
-      try {
-        // 读取文件片段
-        const partData = await this.readFileSlice(filePath, start, end);
-        
-        const etag = await this.uploadPart(
-          policy.partUrls[i],
-          partData,
-          callbacks,
-          {
-            partNumber,
-            totalParts,
-            partSize: end - start,
-            totalSize
-          }
-        );
-        
-        uploadedParts.push({
-          ETag: etag,
-          PartNumber: partNumber
-        });
-        
-        // 更新进度
-        const progress = Math.round((end / totalSize) * 100);
-        callbacks?.onProgress?.({
-          stage: 'uploading',
-          message: `上传文件片段 ${partNumber}/${totalParts}`,
-          progress,
-          details: {
-            loaded: end,
-            total: totalSize,
-          },
-          timestamp: Date.now()
-        });
-      } catch (error) {
-        callbacks?.onError?.(error instanceof Error ? error : new Error(String(error)));
-        throw error;
-      }
-    }
-    
-    // 完成上传
-    try {
-      const result = await this.completeMultipartUpload(policy, key, uploadedParts);
-      
-      callbacks?.onProgress?.({
-        stage: 'complete',
-        message: '文件上传完成',
-        progress: 100,
-        timestamp: Date.now()
-      });
-      
-      callbacks?.onComplete?.();
-      return {
-        fileUrl: `${policy.host}/${key}`,
-        fileKey: key,
-        bucketName: policy.bucket,
-        fileId: result.fileId
-      };
-    } catch (error) {
-      callbacks?.onError?.(error instanceof Error ? error : new Error(String(error)));
-      throw error;
-    }
-  }
-  
-  // 读取文件片段
-  private static async readFileSlice(filePath: string, start: number, end: number): Promise<ArrayBuffer> {
-    return new Promise((resolve, reject) => {
-      try {
-        const fs = Taro?.getFileSystemManager?.();
-        if (!fs) {
-          reject(new Error('小程序文件系统不可用'));
-          return;
-        }
-        
-        const fileData = fs.readFileSync(filePath, undefined, start, end - start);
-        
-        // 确保返回 ArrayBuffer 类型
-        if (typeof fileData === 'string') {
-          // 将字符串转换为 ArrayBuffer
-          const encoder = new TextEncoder();
-          resolve(encoder.encode(fileData).buffer);
-        } else if (fileData instanceof ArrayBuffer) {
-          resolve(fileData);
-        } else {
-          // 处理其他可能的数据类型
-          reject(new Error('文件数据类型不支持'));
-        }
-      } catch (error) {
-        reject(error);
-      }
-    });
-  }
-  
-  // 上传单个片段
-  private static async uploadPart(
-    uploadUrl: string,
-    partData: ArrayBuffer,
-    _callbacks?: MinioProgressCallbacks,
-    _progressDetails?: UploadProgressDetails
-  ): Promise<string> {
-    return new Promise((resolve, reject) => {
-      Taro?.request?.({
-        url: uploadUrl,
-        method: 'PUT',
-        data: partData,
-        header: {
-          'Content-Type': 'application/octet-stream'
-        },
-        success: (res: any) => {
-          if (res.statusCode >= 200 && res.statusCode < 300) {
-            const etag = res.header?.['ETag']?.replace(/"/g, '') || '';
-            resolve(etag);
-          } else {
-            reject(new Error(`上传片段失败: ${res.statusCode}`));
-          }
-        },
-        fail: (error: any) => {
-          reject(new Error(`上传片段失败: ${error.errMsg}`));
-        }
-      }) || reject(new Error('小程序环境不可用'));
-    });
-  }
-  
-  // 完成分段上传
-  private static async completeMultipartUpload(
-    policy: MinioMultipartUploadPolicy,
-    key: string,
-    uploadedParts: UploadPart[]
-  ): Promise<{ fileId: number }> {
-    const response = await fileClient["multipart-complete"].$post({
-      json: {
-        bucket: policy.bucket,
-        key,
-        uploadId: policy.uploadId,
-        parts: uploadedParts.map(part => ({ partNumber: part.PartNumber, etag: part.ETag }))
-      }
-    });
-    
-    if (!response.ok) {
-      throw new Error(`完成分段上传失败: ${response.status} ${response.statusText}`);
-    }
-    
-    return await response.json();
-  }
-}
-
-export class TaroMinIOUploader {
-  /**
-   * 使用 Taro 上传文件到 MinIO(小程序环境)
-   */
-  static async upload(
-    policy: MinioUploadPolicy,
-    filePath: string,
-    key: string,
-    callbacks?: MinioProgressCallbacks
-  ): Promise<UploadResult> {
-    // 获取文件信息
-    const fileInfo = await getFileInfoPromise(filePath);
-    const totalSize = fileInfo.size;
-    
-    callbacks?.onProgress?.({
-      stage: 'uploading',
-      message: '准备上传文件...',
-      progress: 0,
-      details: {
-        loaded: 0,
-        total: totalSize
-      },
-      timestamp: Date.now()
-    });
-    
-    return new Promise((resolve, reject) => {
-      // 准备表单数据 - 使用对象形式,Taro.uploadFile会自动处理
-      const formData: Record<string, string> = {};
-      
-      // 添加 MinIO 需要的字段
-      Object.entries(policy.uploadPolicy).forEach(([k, value]) => {
-        if (k !== 'key' && k !== 'host' && k !== 'prefix' && k !== 'ossType' && typeof value === 'string') {
-          formData[k] = value;
-        }
-      });
-      
-      formData['key'] = key;
-      
-      // 使用 Taro.uploadFile 替代 FormData
-      const uploadTask = Taro.uploadFile({
-        url: policy.uploadPolicy.host,
-        filePath: filePath,
-        name: 'file',
-        formData: formData,
-        header: {
-          'Content-Type': 'multipart/form-data'
-        },
-        success: (res) => {
-          if (res.statusCode >= 200 && res.statusCode < 300) {
-            callbacks?.onProgress?.({
-              stage: 'complete',
-              message: '文件上传完成',
-              progress: 100,
-              timestamp: Date.now()
-            });
-            callbacks?.onComplete?.();
-            resolve({
-              fileUrl: `${policy.uploadPolicy.host}/${key}`,
-              fileKey: key,
-              bucketName: policy.uploadPolicy.bucket,
-              fileId: policy.file.id
-            });
-          } else {
-            reject(new Error(`上传失败: ${res.statusCode}`));
-          }
-        },
-        fail: (error) => {
-          reject(new Error(`上传失败: ${error.errMsg}`));
-        }
-      });
-
-      // 监听上传进度
-      uploadTask.progress((res) => {
-        if (res.totalBytesExpectedToSend > 0) {
-          const currentProgress = Math.round((res.totalBytesSent / res.totalBytesExpectedToSend) * 100);
-          callbacks?.onProgress?.({
-            stage: 'uploading',
-            message: `上传中 ${currentProgress}%`,
-            progress: currentProgress,
-            details: {
-              loaded: res.totalBytesSent,
-              total: res.totalBytesExpectedToSend
-            },
-            timestamp: Date.now()
-          });
-        }
-      });
-
-      // 支持取消上传
-      if (callbacks?.signal && 'aborted' in callbacks.signal) {
-        if (callbacks.signal.aborted) {
-          uploadTask.abort();
-          reject(new Error('上传已取消'));
-        }
-        
-        // 监听取消信号
-        const checkAbort = () => {
-          if (callbacks.signal?.aborted) {
-            uploadTask.abort();
-            reject(new Error('上传已取消'));
-          }
-        };
-        
-        // 定期检查取消状态
-        const abortInterval = setInterval(checkAbort, 100);
-        
-        // 清理定时器
-        const cleanup = () => clearInterval(abortInterval);
-        uploadTask.onProgressUpdate = cleanup;
-        uploadTask.onHeadersReceived = cleanup;
-      }
-    });
-  }
-}
-
-// ==================== 统一 API ====================
-/**
- * 根据运行环境自动选择合适的上传器
- */
-export class UniversalMinIOMultipartUploader {
-  static async upload(
-    policy: MinioMultipartUploadPolicy,
-    file: File | Blob | string,
-    key: string,
-    callbacks?: MinioProgressCallbacks
-  ): Promise<UploadResult> {
-    if (isBrowser && (file instanceof File || file instanceof Blob)) {
-      return MinIOXHRMultipartUploader.upload(policy, file, key, callbacks);
-    } else if (isMiniProgram && typeof file === 'string') {
-      return TaroMinIOMultipartUploader.upload(policy, file, key, callbacks);
-    } else {
-      throw new Error('不支持的运行环境或文件类型');
-    }
-  }
-}
-
-export class UniversalMinIOUploader {
-  static async upload(
-    policy: MinioUploadPolicy,
-    file: File | Blob | string,
-    key: string,
-    callbacks?: MinioProgressCallbacks
-  ): Promise<UploadResult> {
-    if (isBrowser && (file instanceof File || file instanceof Blob)) {
-      return MinIOXHRUploader.upload(policy, file, key, callbacks);
-    } else if (isMiniProgram && typeof file === 'string') {
-      return TaroMinIOUploader.upload(policy, file, key, callbacks);
-    } else {
-      throw new Error('不支持的运行环境或文件类型');
-    }
-  }
-}
-
-// ==================== 通用函数 ====================
-export async function getUploadPolicy(key: string, fileName: string, fileType?: string, fileSize?: number): Promise<MinioUploadPolicy> {
-  const policyResponse = await fileClient["upload-policy"].$post({
-    json: {
-      path: key,
-      name: fileName,
-      type: fileType,
-      size: fileSize
-    }
-  });
-  if (!policyResponse.ok) {
-    throw new Error('获取上传策略失败');
-  }
-  return policyResponse.json();
-}
-
-export async function getMultipartUploadPolicy(totalSize: number, fileKey: string, fileType?: string, fileName: string = 'unnamed-file') {
-  const policyResponse = await fileClient["multipart-policy"].$post({
-    json: {
-      totalSize,
-      partSize: PART_SIZE,
-      fileKey,
-      type: fileType,
-      name: fileName
-    }
-  });
-  if (!policyResponse.ok) {
-    throw new Error('获取分段上传策略失败');
-  }
-  return await policyResponse.json();
-}
-
-/**
- * 统一的上传函数,自动适应运行环境
- */
-export async function uploadMinIOWithPolicy(
-  uploadPath: string,
-  file: File | Blob | string,
-  fileKey: string,
-  callbacks?: MinioProgressCallbacks
-): Promise<UploadResult> {
-  if(uploadPath === '/') uploadPath = '';
-  else{
-    if(!uploadPath.endsWith('/')) uploadPath = `${uploadPath}/`
-    if(uploadPath.startsWith('/')) uploadPath = uploadPath.replace(/^\//, '');
-  }
-  
-  let fileSize: number;
-  let fileType: string | undefined;
-  let fileName: string;
-  
-  if (isBrowser && (file instanceof File || file instanceof Blob)) {
-    fileSize = file.size;
-    fileType = (file as File).type || undefined;
-    fileName = (file as File).name || fileKey;
-  } else if (isMiniProgram && typeof file === 'string') {
-    try {
-      const fileInfo = await getFileInfoPromise(file);
-      fileSize = fileInfo.size;
-      fileType = undefined;
-      fileName = fileKey;
-    } catch {
-      fileSize = 0;
-      fileType = undefined;
-      fileName = fileKey;
-    }
-  } else {
-    throw new Error('不支持的文件类型');
-  }
-  
-  if (fileSize > PART_SIZE) {
-    if (isBrowser && !(file instanceof File)) {
-      throw new Error('不支持的文件类型,无法获取文件名');
-    }
-    
-    const policy = await getMultipartUploadPolicy(
-      fileSize,
-      `${uploadPath}${fileKey}`,
-      fileType,
-      fileName
-    );
-    
-    if (isBrowser) {
-      return MinIOXHRMultipartUploader.upload(policy, file as File | Blob, policy.key, callbacks);
-    } else {
-      return TaroMinIOMultipartUploader.upload(policy, file as string, policy.key, callbacks);
-    }
-  } else {
-    if (isBrowser && !(file instanceof File)) {
-      throw new Error('不支持的文件类型,无法获取文件名');
-    }
-    
-    const policy = await getUploadPolicy(`${uploadPath}${fileKey}`, fileName, fileType, fileSize);
-    
-    if (isBrowser) {
-      return MinIOXHRUploader.upload(policy, file as File | Blob, policy.uploadPolicy.key, callbacks);
-    } else {
-      return TaroMinIOUploader.upload(policy, file as string, policy.uploadPolicy.key, callbacks);
-    }
-  }
-}
-
-// ==================== 小程序工具函数 ====================
-/**
- * Promise封装的getFileInfo函数
- */
-async function getFileInfoPromise(filePath: string): Promise<{ size: number }> {
-  return new Promise((resolve, reject) => {
-    const fs = Taro?.getFileSystemManager?.();
-    if (!fs) {
-      reject(new Error('小程序文件系统不可用'));
-      return;
-    }
-    
-    fs.getFileInfo({
-      filePath,
-      success: (res) => {
-        resolve({ size: res.size });
-      },
-      fail: (error) => {
-        reject(new Error(`获取文件信息失败: ${error.errMsg}`));
-      }
-    });
-  });
-}
-
-// 新增:自动适应运行环境的文件选择并上传函数
-/**
- * 自动适应运行环境:选择文件并上传到 MinIO
- * 小程序:使用 Taro.chooseImage
- * H5:使用 input[type="file"]
- */
-export async function uploadFromSelect(
-  uploadPath: string = '',
-  options: {
-    sourceType?: ('album' | 'camera')[],
-    count?: number,
-    accept?: string,
-    maxSize?: number,
-  } = {},
-  callbacks?: MinioProgressCallbacks
-): Promise<UploadResult> {
-  const { sourceType = ['album', 'camera'], count = 1, accept = '*', maxSize = 10 * 1024 * 1024 } = options;
-
-  if (isMiniProgram) {
-    return new Promise((resolve, reject) => {
-      
-      Taro.chooseImage({
-        count,
-        sourceType: sourceType as any, // 确保类型兼容
-        success: async (res) => {
-          const tempFilePath = res.tempFilePaths[0];
-          const fileName = res.tempFiles[0]?.originalFileObj?.name || tempFilePath.split('/').pop() || 'unnamed-file';
-          
-          try {
-            const result = await uploadMinIOWithPolicy(uploadPath, tempFilePath, fileName, callbacks);
-            resolve(result);
-          } catch (error) {
-            reject(error);
-          }
-        },
-        fail: reject
-      });
-    });
-  } else if (isBrowser) {
-    return new Promise((resolve, reject) => {
-      const input = document.createElement('input');
-      input.type = 'file';
-      input.accept = accept;
-      input.multiple = count > 1;
-      
-      input.onchange = async (event) => {
-        const files = (event.target as HTMLInputElement).files;
-        if (!files || files.length === 0) {
-          reject(new Error('未选择文件'));
-          return;
-        }
-        
-        const file = files[0];
-        if (file.size > maxSize) {
-          reject(new Error(`文件大小超过限制: ${maxSize / 1024 / 1024}MB`));
-          return;
-        }
-        
-        const fileName = file.name || 'unnamed-file';
-        
-        try {
-          const result = await uploadMinIOWithPolicy(uploadPath, file, fileName, callbacks);
-          resolve(result);
-        } catch (error) {
-          reject(error);
-        }
-      };
-      
-      input.click();
-    });
-  } else {
-    throw new Error('不支持的运行环境');
-  }
-}
-
-// 默认导出
-export default {
-  MinIOXHRMultipartUploader,
-  MinIOXHRUploader,
-  TaroMinIOMultipartUploader,
-  TaroMinIOUploader,
-  UniversalMinIOMultipartUploader,
-  UniversalMinIOUploader,
-  getUploadPolicy,
-  getMultipartUploadPolicy,
-  uploadMinIOWithPolicy,
-  uploadFromSelect
-};

+ 0 - 2
mini/src/utils/platform.ts

@@ -1,2 +0,0 @@
-// 从共享UI组件包重新导出平台工具函数
-export { getPlatform, isWeapp, isH5 } from '@d8d/mini-shared-ui-components'

+ 0 - 88
mini/src/utils/response-polyfill.ts

@@ -1,88 +0,0 @@
-class ResponsePolyfill {
-    constructor(
-      public body: string | ArrayBuffer | null,
-      public init: {
-        status?: number
-        statusText?: string
-        headers?: Record<string, string>
-      } = {}
-    ) {}
-  
-    get ok(): boolean {
-      return this.status >= 200 && this.status < 300
-    }
-  
-    get status(): number {
-      return this.init.status || 200
-    }
-  
-    get statusText(): string {
-      return this.init.statusText || 'OK'
-    }
-  
-    get headers(): Headers {
-      return new Headers(this.init.headers || {})
-    }
-  
-    get bodyUsed(): boolean {
-      return false // 小程序环境简单实现
-    }
-  
-    async arrayBuffer(): Promise<ArrayBuffer> {
-      if (this.body instanceof ArrayBuffer) {
-        return this.body
-      }
-      throw new Error('Not implemented')
-    }
-  
-    async text(): Promise<string> {
-      if (typeof this.body === 'string') {
-        return this.body
-      }
-      throw new Error('Not implemented')
-    }
-  
-    async json<T = any>(): Promise<T> {
-      if (typeof this.body === 'string') {
-        try {
-          return JSON.parse(this.body)
-        } catch (e) {
-          throw new Error('Invalid JSON')
-        }
-      }
-      throw new Error('Not implemented')
-    }
-  
-    clone(): ResponsePolyfill {
-      return new ResponsePolyfill(this.body, { ...this.init })
-    }
-  
-    static json(data: any, init?: ResponseInit): ResponsePolyfill {
-      const headers = new Headers(init && 'headers' in init ? init.headers : undefined)
-      if (!headers.has('Content-Type')) {
-        headers.set('Content-Type', 'application/json')
-      }
-      return new ResponsePolyfill(JSON.stringify(data), {
-        ...init,
-        headers: Object.fromEntries(headers.entries())
-      })
-    }
-  
-    static error(): ResponsePolyfill {
-      return new ResponsePolyfill(null, { status: 0, statusText: 'Network Error' })
-    }
-  
-    static redirect(url: string, status: number): ResponsePolyfill {
-      return new ResponsePolyfill(null, {
-        status,
-        headers: { Location: url }
-      })
-    }
-  }
-  
-  // 全局注册(如果需要)
-  if (typeof globalThis.Response === 'undefined') {
-    globalThis.Response = ResponsePolyfill as any
-  }
-  
-  export default ResponsePolyfill

+ 0 - 182
mini/src/utils/rpc-client.ts

@@ -1,182 +0,0 @@
-import Taro from '@tarojs/taro'
-import { hc } from 'hono/client'
-import ResponsePolyfill from './response-polyfill'
-
-// 刷新token的函数
-let isRefreshing = false
-let refreshSubscribers: ((token: string) => void)[] = []
-
-// 执行token刷新
-const refreshToken = async (): Promise<string | null> => {
-  if (isRefreshing) {
-    // 如果已经在刷新,等待结果
-    return new Promise((resolve) => {
-      refreshSubscribers.push((token) => {
-        resolve(token)
-      })
-    })
-  }
-
-  isRefreshing = true
-  try {
-    const refreshToken = Taro.getStorageSync('enterprise_refresh_token')
-    if (!refreshToken) {
-      throw new Error('未找到刷新token')
-    }
-
-    // 调用刷新token接口
-    const response = await Taro.request({
-      url: `${process.env.TARO_APP_API_BASE_URL || 'http://localhost:3000'}/api/v1/yongren/auth/refresh-token`,
-      method: 'POST',
-      header: {
-        'Content-Type': 'application/json',
-        'Authorization': `Bearer ${refreshToken}`
-      }
-    })
-
-    if (response.statusCode === 200) {
-      const { token, refresh_token: newRefreshToken } = response.data
-      Taro.setStorageSync('enterprise_token', token)
-      if (newRefreshToken) {
-        Taro.setStorageSync('enterprise_refresh_token', newRefreshToken)
-      }
-
-      // 通知所有等待的请求
-      refreshSubscribers.forEach(callback => callback(token))
-      refreshSubscribers = []
-      return token
-    } else {
-      throw new Error('刷新token失败')
-    }
-  } catch (error) {
-    console.error('刷新token失败:', error)
-    // 清除token,跳转到登录页
-    Taro.removeStorageSync('enterprise_token')
-    Taro.removeStorageSync('enterprise_refresh_token')
-    Taro.removeStorageSync('enterpriseUserInfo')
-
-    // 跳转到登录页
-    Taro.showToast({
-      title: '登录已过期,请重新登录',
-      icon: 'none'
-    })
-    setTimeout(() => {
-      Taro.redirectTo({
-        url: '/pages/login/index'
-      })
-    }, 1500)
-
-    return null
-  } finally {
-    isRefreshing = false
-  }
-}
-
-// API配置
-const API_BASE_URL = process.env.TARO_APP_API_BASE_URL || 'http://localhost:3000'
-
-// 完整的API地址
-// const BASE_URL = `${API_BASE_URL}/api/${API_VERSION}`
-
-// 创建自定义fetch函数,适配Taro.request,支持token自动刷新
-const taroFetch: any = async (input, init) => {
-  const url = typeof input === 'string' ? input : input.url
-  const method = init.method || 'GET'
-
-  const requestHeaders: Record<string, string> = init.headers;
-
-  const keyOfContentType = Object.keys(requestHeaders).find(item => item.toLowerCase() === 'content-type')
-  if (!keyOfContentType) {
-    requestHeaders['content-type'] = 'application/json'
-  }
-
-  // 构建Taro请求选项
-  const options: Taro.request.Option = {
-    url,
-    method: method as any,
-    data: init.body,
-    header: requestHeaders
-  }
-
-  // 添加token - 优先使用企业token,兼容mini_token
-  let token = Taro.getStorageSync('enterprise_token')
-  if (!token) {
-    token = Taro.getStorageSync('mini_token')
-  }
-  if (token) {
-    options.header = {
-      ...options.header,
-      'Authorization': `Bearer ${token}`
-    }
-  }
-
-  // 发送请求
-  const sendRequest = async (): Promise<any> => {
-    try {
-      console.log('API请求:', options.url)
-      const response = await Taro.request(options)
-
-      const responseHeaders = response.header;
-
-      // 处理204 No Content响应,不设置body
-      const body = response.statusCode === 204
-        ? null
-        : responseHeaders['content-type']!.includes('application/json')
-          ? JSON.stringify(response.data)
-          : response.data;
-
-      return new ResponsePolyfill(
-        body,
-        {
-          status: response.statusCode,
-          statusText: response.errMsg || 'OK',
-          headers: responseHeaders
-        }
-      )
-    } catch (error) {
-      console.error('API Error:', error)
-      throw error
-    }
-  }
-
-  try {
-    let response = await sendRequest()
-
-    // 检查是否为401错误,尝试刷新token
-    if (response.status === 401 && token) {
-      console.log('检测到401错误,尝试刷新token...')
-      const newToken = await refreshToken()
-
-      if (newToken) {
-        // 更新请求header中的token
-        options.header = {
-          ...options.header,
-          'Authorization': `Bearer ${newToken}`
-        }
-
-        // 重试原始请求
-        response = await sendRequest()
-      } else {
-        // 刷新失败,返回原始401响应
-        return response
-      }
-    }
-
-    return response
-  } catch (error) {
-    console.error('API请求失败:', error)
-    Taro.showToast({
-      title: error.message || '网络错误',
-      icon: 'none'
-    })
-    throw error
-  }
-}
-
-// 创建Hono RPC客户端
-export const rpcClient = <T extends any>() => {
-  // @ts-ignore
-  return hc<T>(`${API_BASE_URL}`, {
-    fetch: taroFetch
-  })
-}