Ver Fonte

feat: 复制页面组件到 mini/src/pages/ (UI包整合阶段2)

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
yourname há 2 dias atrás
pai
commit
632cfe2e08

+ 212 - 3
mini/src/pages/login/index.tsx

@@ -1,3 +1,212 @@
-// 桥接文件:从 @d8d/mini-enterprise-auth-ui 包导入Login页面
-import Login from '@d8d/mini-enterprise-auth-ui/pages/login/Login'
-export default Login
+import { View, Text } from '@tarojs/components'
+import { useState, useEffect } from 'react'
+import Taro from '@tarojs/taro'
+import { useAuth } from '../../hooks'
+import { cn } from '@d8d/mini-shared-ui-components/utils/cn'
+import { Button } from '@d8d/mini-shared-ui-components/components/button'
+import { Input } from '@d8d/mini-shared-ui-components/components/input'
+import { Form, FormField, FormItem, FormControl, FormMessage } from '@d8d/mini-shared-ui-components/components/form'
+import { Navbar } from '@d8d/mini-shared-ui-components/components/navbar'
+import { z } from 'zod'
+import { zodResolver } from '@hookform/resolvers/zod'
+import { useForm } from 'react-hook-form'
+
+const loginSchema = z.object({
+  phone: z
+    .string()
+    .regex(/^1[3-9]\d{9}$/, '请输入有效的手机号码'),
+  password: z
+    .string()
+    .min(6, '密码至少6位')
+    .max(20, '密码最多20位'),
+})
+
+type LoginFormData = z.infer<typeof loginSchema>
+
+export default function Login() {
+  const [showPassword, setShowPassword] = useState(false)
+  const { login, isLoading, isLoggedIn } = useAuth()
+
+  // 如果已登录,直接跳转到 dashboard
+  useEffect(() => {
+    if (isLoggedIn) {
+      Taro.switchTab({ url: '/pages/yongren/dashboard/index' })
+    }
+  }, [isLoggedIn])
+
+  const form = useForm<LoginFormData>({
+    resolver: zodResolver(loginSchema as any),
+    defaultValues: {
+      phone: '',
+      password: '',
+    },
+  })
+
+  // 设置导航栏标题
+  useEffect(() => {
+    Taro.setNavigationBarTitle({
+      title: '企业用户登录'
+    })
+  }, [])
+
+  const onSubmit = async (data: LoginFormData) => {
+    try {
+      Taro.showLoading({
+        title: '登录中...',
+        mask: true
+      })
+
+      await login({
+        phone: data.phone.trim(),
+        password: data.password.trim()
+      })
+
+      Taro.hideLoading()
+
+      Taro.showToast({
+        title: '登录成功',
+        icon: 'success',
+        duration: 1500
+      })
+
+      setTimeout(() => {
+        Taro.switchTab({ url: '/pages/yongren/dashboard/index' })
+      }, 1500)
+    } catch (error: any) {
+      Taro.hideLoading()
+
+      const errorMessage = error.message || '登录失败'
+
+      if (errorMessage.includes('用户名或密码错误')) {
+        Taro.showToast({
+          title: '用户名或密码错误',
+          icon: 'none',
+          duration: 3000
+        })
+      } else if (errorMessage.includes('网络')) {
+        Taro.showModal({
+          title: '网络错误',
+          content: '请检查网络连接后重试',
+          showCancel: false,
+          confirmText: '确定'
+        })
+      } else {
+        Taro.showToast({
+          title: errorMessage,
+          icon: 'none',
+          duration: 3000
+        })
+      }
+    }
+  }
+
+
+  return (
+    <View className="min-h-screen bg-white" data-testid="mini-login-page">
+      {/* 导航栏 */}
+      <Navbar title="企业用户登录" leftIcon="" testId="mini-page-title" />
+
+      <View className="h-[calc(100%-44px)] flex flex-col justify-center p-8">
+        {/* Logo区域 - 对照原型第232-235行 */}
+        <View className="text-center mb-10 flex flex-col">
+          <Text className="text-2xl font-bold text-gray-800 mb-2">残疾人就业平台</Text>
+          <Text className="text-gray-600">为人力资源公司提供专业服务</Text>
+        </View>
+
+        {/* 登录表单 - 对照原型第237-246行 */}
+        <View className="mb-6">
+          <Form {...form}>
+            {/* 手机号输入框 */}
+            <FormField
+              control={form.control}
+              name="phone"
+              render={({ field }) => (
+                <FormItem>
+                  <FormControl>
+                    <View className="flex items-center border border-gray-300 rounded-lg px-4 py-3 mb-4">
+                      <View className="i-heroicons-phone-20-solid text-gray-400 mr-3 w-5 h-5" />
+                      <Input
+                        data-testid="mini-phone-input"
+                        placeholder="请输入手机号"
+                        maxlength={11}
+                        type="number"
+                        confirmType="next"
+                        className="w-full outline-none border-none bg-transparent"
+                        {...field}
+                      />
+                    </View>
+                  </FormControl>
+                  <FormMessage />
+                </FormItem>
+              )}
+            />
+
+            {/* 密码输入框 */}
+            <FormField
+              control={form.control}
+              name="password"
+              render={({ field }) => (
+                <FormItem>
+                  <FormControl>
+                    <View className="flex items-center border border-gray-300 rounded-lg px-4 py-3">
+                      <View className="i-heroicons-lock-closed-20-solid text-gray-400 mr-3 w-5 h-5" />
+                      <Input
+                        data-testid="mini-password-input"
+                        placeholder="请输入密码"
+                        password={!showPassword}
+                        maxlength={20}
+                        confirmType="done"
+                        className="w-full outline-none border-none bg-transparent flex-1"
+                        {...field}
+                      />
+                      <View
+                        className={`i-heroicons-${showPassword ? 'eye-20-solid' : 'eye-slash-20-solid'} text-gray-400 ml-3 w-5 h-5`}
+                        onClick={() => setShowPassword(!showPassword)}
+                      />
+                    </View>
+                  </FormControl>
+                  <FormMessage />
+                </FormItem>
+              )}
+            />
+          </Form>
+        </View>
+
+        {/* 登录按钮 - 对照原型第248行 */}
+        <Button
+          data-testid="mini-login-button"
+          className={cn(
+            "bg-gradient-to-r from-blue-500 to-purple-600 text-white w-full py-3 rounded-lg font-medium mb-4",
+            !form.formState.isValid || isLoading
+              ? "opacity-50 cursor-not-allowed"
+              : ""
+          )}
+          onClick={form.handleSubmit(onSubmit) as any}
+          disabled={!form.formState.isValid || isLoading}
+        >
+          {isLoading ? (
+            <View className="flex items-center justify-center">
+              <View className="i-heroicons-arrow-path-20-solid animate-spin w-5 h-5 mr-2" />
+              登录中...
+            </View>
+          ) : (
+            '登录'
+          )}
+        </Button>
+
+        {/* 忘记密码链接 - 对照原型第250-252行 */}
+        <View className="text-center mb-8">
+          <Text className="text-sm text-blue-500">忘记密码?</Text>
+        </View>
+
+        {/* 协议声明 - 对照原型第254-256行 - 暂时注释掉以通过小程序审核 */}
+        {/* <View className="mt-12 text-center text-gray-500 text-sm">
+          <Text>登录即表示同意</Text>
+          <Text className="text-blue-500">《用户协议》</Text>
+          <Text>和</Text>
+          <Text className="text-blue-500">《隐私政策》</Text>
+        </View> */}
+      </View>
+    </View>
+  )
+}

+ 0 - 1
mini/src/pages/profile/index.css

@@ -1 +0,0 @@
-/* 样式已迁移到 @d8d/mini-enterprise-auth-ui 包中的 Profile.css 文件 */

+ 278 - 3
mini/src/pages/profile/index.tsx

@@ -1,3 +1,278 @@
-// 桥接文件:从 @d8d/mini-enterprise-auth-ui 包导入Profile页面
-import Profile from '@d8d/mini-enterprise-auth-ui/pages/profile/Profile'
-export default Profile
+import { useState } from 'react'
+import { View, Text, ScrollView } from '@tarojs/components'
+import Taro from '@tarojs/taro'
+import { YongrenTabBarLayout } from '../../components/YongrenTabBarLayout'
+import { useAuth } from '../hooks'
+import { cn } from '@d8d/mini-shared-ui-components/utils/cn'
+import { Button } from '@d8d/mini-shared-ui-components/components/button'
+import { Navbar } from '@d8d/mini-shared-ui-components/components/navbar'
+import { AvatarUpload } from '@d8d/mini-shared-ui-components/components/avatar-upload'
+import { type UploadResult } from '../../types/minio'
+
+const ProfilePage: React.FC = () => {
+  const { user: userProfile, logout, isLoading: loading, updateUser } = useAuth()
+  const [updatingAvatar, setUpdatingAvatar] = useState(false)
+
+  const handleLogout = async () => {
+    try {
+      Taro.showModal({
+        title: '退出登录',
+        content: '确定要退出登录吗?',
+        success: async (res) => {
+          if (res.confirm) {
+            Taro.showLoading({ title: '退出中...' })
+            await logout()
+            Taro.hideLoading()
+            Taro.showToast({
+              title: '已退出登录',
+              icon: 'success',
+              duration: 1500
+            })
+            // 注意:不需要额外的跳转逻辑,useAuth.logout 已经处理了跳转
+          }
+        }
+      })
+    } catch (_error) {
+      Taro.hideLoading()
+      Taro.showToast({
+        title: '退出失败,请重试',
+        icon: 'none'
+      })
+    }
+  }
+
+  const handleAvatarUpload = async (result: UploadResult) => {
+    try {
+      setUpdatingAvatar(true)
+      Taro.showLoading({ title: '更新头像...' })
+
+      // 这里应该调用更新用户头像的API
+      // 假设有一个更新用户信息的API
+      console.log('头像上传成功:', result)
+
+      // 更新本地用户数据
+      if (userProfile) {
+        const updatedUser = {
+          ...userProfile,
+          avatarFileId: result.fileId
+        }
+        updateUser(updatedUser)
+      }
+
+      Taro.hideLoading()
+      Taro.showToast({
+        title: '头像更新成功',
+        icon: 'success'
+      })
+    } catch (_error) {
+      console.error('更新头像失败:', _error)
+      Taro.hideLoading()
+      Taro.showToast({
+        title: '更新头像失败',
+        icon: 'none'
+      })
+    } finally {
+      setUpdatingAvatar(false)
+    }
+  }
+
+  const handleAvatarUploadError = (error: Error) => {
+    console.error('头像上传失败:', error)
+    Taro.showToast({
+      title: '上传失败,请重试',
+      icon: 'none'
+    })
+  }
+
+  const handleEditProfile = () => {
+    Taro.showToast({
+      title: '功能开发中...',
+      icon: 'none'
+    })
+  }
+
+  const handleSettings = () => {
+    Taro.showToast({
+      title: '功能开发中...',
+      icon: 'none'
+    })
+  }
+
+  const menuItems = [
+    {
+      icon: 'i-heroicons-user-circle-20-solid',
+      title: '企业信息',
+      onClick: handleEditProfile,
+      color: 'text-blue-500'
+    },
+    {
+      icon: 'i-heroicons-cog-6-tooth-20-solid',
+      title: '账户设置',
+      onClick: handleSettings,
+      color: 'text-gray-500'
+    },
+    {
+      icon: 'i-heroicons-shield-check-20-solid',
+      title: '服务协议',
+      onClick: () => Taro.showToast({ title: '功能开发中...', icon: 'none' }),
+      color: 'text-green-500'
+    },
+    {
+      icon: 'i-heroicons-question-mark-circle-20-solid',
+      title: '联系客服',
+      onClick: () => Taro.showToast({ title: '功能开发中...', icon: 'none' }),
+      color: 'text-purple-500'
+    }
+  ]
+
+  if (loading) {
+    return (
+      <YongrenTabBarLayout activeKey="settings">
+        <View className="flex-1 flex items-center justify-center">
+          <View className="i-heroicons-arrow-path-20-solid animate-spin w-8 h-8 text-blue-500" />
+        </View>
+      </YongrenTabBarLayout>
+    )
+  }
+
+  if (!userProfile) {
+    return (
+      <YongrenTabBarLayout activeKey="settings">
+        <Navbar
+          title="企业账户"
+          leftIcon=""
+        />
+        <View className="flex-1 flex flex-col items-center justify-center">
+          <View className="flex flex-col items-center">
+            <View className="i-heroicons-exclamation-circle-20-solid w-12 h-12 text-gray-400 mx-auto mb-4" />
+            <Text className="text-gray-600 mb-4">请先登录企业账户</Text>
+            <Button
+              variant="default"
+              size="lg"
+              onClick={() => Taro.navigateTo({ url: '/pages/login/index' })}
+            >
+              去登录
+            </Button>
+          </View>
+        </View>
+      </YongrenTabBarLayout>
+    )
+  }
+
+  return (
+    <YongrenTabBarLayout activeKey="settings">
+      <Navbar
+        title="企业账户"
+        rightIcon="i-heroicons-cog-6-tooth-20-solid"
+        onClickRight={handleSettings}
+        leftIcon=""
+      />
+      <ScrollView className="flex-1 bg-gray-50">
+        {/* 用户信息卡片 */}
+        <View className="bg-white rounded-b-3xl shadow-sm pb-8">
+          <View className="flex flex-col items-center pt-8 pb-6">
+            <View className="relative">
+              <AvatarUpload
+                currentAvatar={userProfile.avatarFile?.fullUrl}
+                onUploadSuccess={handleAvatarUpload}
+                onUploadError={handleAvatarUploadError}
+                size={96}
+                editable={!updatingAvatar}
+              />
+            </View>
+            <Text className="text-xl font-bold text-gray-900 mt-4">{userProfile.username}</Text>
+            {userProfile.email && (
+              <Text className="text-sm text-gray-600 mt-1">{userProfile.email}</Text>
+            )}
+            <View className="flex items-center mt-2">
+              <View className="i-heroicons-calendar-20-solid w-4 h-4 text-gray-400 mr-1" />
+              <Text className="text-xs text-gray-500">
+                注册于 {new Date(userProfile.createdAt).toLocaleDateString('zh-CN')}
+              </Text>
+            </View>
+          </View>
+
+          {/* 统计信息 */}
+          <View className="px-6">
+            <View className="grid grid-cols-3 gap-4 text-center">
+              <View className="bg-gray-50 rounded-xl p-4">
+                <Text className="text-2xl font-bold text-blue-500">0</Text>
+                <Text className="text-xs text-gray-600 mt-1">人才数</Text>
+              </View>
+              <View className="bg-gray-50 rounded-xl p-4">
+                <Text className="text-2xl font-bold text-green-500">0</Text>
+                <Text className="text-xs text-gray-600 mt-1">订单数</Text>
+              </View>
+              <View className="bg-gray-50 rounded-xl p-4">
+                <Text className="text-2xl font-bold text-purple-500">0</Text>
+                <Text className="text-xs text-gray-600 mt-1">消息数</Text>
+              </View>
+            </View>
+          </View>
+        </View>
+
+        {/* 功能菜单 */}
+        <View className="px-4 pt-6">
+          <View className="bg-white rounded-2xl shadow-sm overflow-hidden">
+            {menuItems.map((item, index) => (
+              <View
+                key={index}
+                className="flex items-center px-4 py-4 active:bg-gray-50 transition-colors duration-150"
+                onClick={item.onClick}
+              >
+                <View className={cn("w-6 h-6 mr-3", item.color, item.icon)} />
+                <Text className="flex-1 text-gray-800">{item.title}</Text>
+                <View className="i-heroicons-chevron-right-20-solid w-5 h-5 text-gray-400" />
+              </View>
+            ))}
+          </View>
+        </View>
+
+        {/* 账号信息 */}
+        <View className="px-4 pt-6">
+          <View className="bg-white rounded-2xl shadow-sm p-4">
+            <Text className="text-sm font-medium text-gray-700 mb-3">企业账户信息</Text>
+            <View className="space-y-3">
+              <View className="flex justify-between items-center">
+                <Text className="text-sm text-gray-600">企业账号ID</Text>
+                <Text className="text-sm text-gray-900 font-mono">{userProfile.id}</Text>
+              </View>
+              {userProfile.updatedAt && (
+                <View className="flex justify-between items-center">
+                  <Text className="text-sm text-gray-600">最近登录时间</Text>
+                  <Text className="text-sm text-gray-900">
+                    {new Date(userProfile.updatedAt).toLocaleString('zh-CN')}
+                  </Text>
+                </View>
+              )}
+            </View>
+          </View>
+        </View>
+
+        {/* 退出登录按钮 */}
+        <View className="px-4 pt-6 pb-8">
+          <Button
+            variant="destructive"
+            size="lg"
+            className="w-full"
+            onClick={handleLogout}
+          >
+            <View className="flex items-center justify-center">
+              <View className="i-heroicons-arrow-left-on-rectangle-20-solid w-5 h-5 mr-2" />
+              退出登录
+            </View>
+          </Button>
+        </View>
+
+        {/* 版本信息 */}
+        <View className="pb-8">
+          <Text className="text-center text-xs text-gray-400">
+            v1.0.0 - 小程序版
+          </Text>
+        </View>
+      </ScrollView>
+    </YongrenTabBarLayout>
+  )
+}
+
+export default ProfilePage

+ 92 - 0
mini/src/pages/yongren/dashboard/Dashboard.css

@@ -0,0 +1,92 @@
+/* 首页样式 */
+
+/* 头像颜色类 */
+.name-avatar.blue {
+  background: linear-gradient(135deg, #3b82f6, #1d4ed8);
+}
+.name-avatar.green {
+  background: linear-gradient(135deg, #10b981, #059669);
+}
+.name-avatar.purple {
+  background: linear-gradient(135deg, #8b5cf6, #7c3aed);
+}
+.name-avatar.orange {
+  background: linear-gradient(135deg, #f59e0b, #d97706);
+}
+
+/* 进度条样式 */
+.progress-bar {
+  height: 6px;
+  background-color: #e5e7eb;
+  border-radius: 3px;
+  overflow: hidden;
+}
+
+.progress-fill {
+  height: 100%;
+  background: linear-gradient(90deg, #3b82f6, #8b5cf6);
+  border-radius: 3px;
+  transition: width 0.3s ease;
+}
+
+/* 脉冲点样式 */
+.pulse-dot {
+  width: 8px;
+  height: 8px;
+  background-color: #3b82f6;
+  border-radius: 50%;
+  position: relative;
+}
+
+.pulse-dot::before {
+  content: '';
+  position: absolute;
+  top: -4px;
+  left: -4px;
+  right: -4px;
+  bottom: -4px;
+  background-color: #3b82f6;
+  border-radius: 50%;
+  opacity: 0.4;
+  animation: pulse 2s infinite;
+}
+
+@keyframes pulse {
+  0% {
+    transform: scale(1);
+    opacity: 0.4;
+  }
+  70% {
+    transform: scale(1.5);
+    opacity: 0;
+  }
+  100% {
+    transform: scale(1.5);
+    opacity: 0;
+  }
+}
+
+/* 统计卡片样式 */
+.stat-card {
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+  transition: transform 0.2s ease, box-shadow 0.2s ease;
+}
+
+.stat-card:hover {
+  transform: translateY(-2px);
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
+}
+
+/* 加载动画 */
+@keyframes pulse-bg {
+  0%, 100% {
+    opacity: 1;
+  }
+  50% {
+    opacity: 0.5;
+  }
+}
+
+.animate-pulse {
+  animation: pulse-bg 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+}

+ 354 - 2
mini/src/pages/yongren/dashboard/index.tsx

@@ -1,3 +1,355 @@
-// 桥接文件:从 @d8d/yongren-dashboard-ui 包导入Dashboard页面
-import Dashboard from '@d8d/yongren-dashboard-ui/pages/Dashboard/Dashboard'
+import React, { useEffect } from 'react'
+import { View, Text, ScrollView } from '@tarojs/components'
+import Taro, { usePullDownRefresh } from '@tarojs/taro'
+import { useQuery, useQueryClient } from '@tanstack/react-query'
+import dayjs from 'dayjs'
+import { YongrenTabBarLayout } from '../../../components/YongrenTabBarLayout'
+import { Navbar } from '@d8d/mini-shared-ui-components/components/navbar'
+import { enterpriseCompanyClient } from '../../../api'
+import { useAuth, useRequireAuth } from '../../../hooks'
+// import './Dashboard.css'
+
+// 类型定义
+interface OverviewData {
+  totalEmployees: number
+  pendingAssignments: number
+  monthlyOrders: number
+  companyName: string
+}
+
+interface AllocationData {
+  id: string
+  name: string
+  avatarColor: 'blue' | 'green' | 'purple' | 'orange'
+  disabilityType: string
+  disabilityLevel: string
+  status: '在职' | '待入职' | '离职'
+  joinDate: string
+  salary: number
+  progress: number
+}
+
+const Dashboard: React.FC = () => {
+  const { user } = useAuth()
+  const queryClient = useQueryClient()
+
+  // 检查登录状态,未登录则重定向
+  useRequireAuth()
+
+  // 获取企业概览数据
+  const { data: overview, isLoading: _overviewLoading } = useQuery({
+    queryKey: ['enterpriseOverview'],
+    queryFn: async () => {
+      const response = await enterpriseCompanyClient.overview.$get()
+      if (response.status !== 200) {
+        throw new Error('获取企业概览数据失败')
+      }
+      const data = await response.json()
+      // 转换为OverviewData接口
+      return {
+        totalEmployees: data['在职人员数'],
+        pendingAssignments: data['待入职数'],
+        monthlyOrders: data['已完成订单数'],
+        companyName: data['企业名称'] || user?.name || '企业名称'
+      } as OverviewData
+    },
+    refetchOnWindowFocus: false
+  })
+
+  // 将API人才数据转换为前端AllocationData接口
+  const convertTalentToAllocation = (talent: any): AllocationData => {
+    // 根据personId生成稳定的颜色
+    const colors: Array<'blue' | 'green' | 'purple' | 'orange'> = ['blue', 'green', 'purple', 'orange']
+    const colorIndex = talent.personId ? talent.personId % colors.length : 0
+    const avatarColor = colors[colorIndex]
+
+    // 工作状态映射:working -> 在职, pre_working -> 待入职, resigned -> 离职
+    const statusMap: Record<string, '在职' | '待入职' | '离职'> = {
+      'working': '在职',
+      'pre_working': '待入职',
+      'resigned': '离职'
+    }
+    const status = statusMap[talent.workStatus] || '在职'
+
+    // 格式化日期
+    const joinDate = talent.joinDate ? dayjs(talent.joinDate).format('YYYY-MM-DD') : '未知'
+
+    return {
+      id: talent.personId?.toString() || '0',
+      name: talent.personName || '未知姓名',
+      avatarColor,
+      disabilityType: talent.disabilityType || '未知类型',
+      disabilityLevel: talent.disabilityLevel || '未知等级',
+      status,
+      joinDate,
+      salary: 4500, // 暂时使用默认薪资
+      progress: 75   // 暂时使用默认进度
+    }
+  }
+
+  // 获取近期分配人才列表
+  const { data: allocations, isLoading: allocationsLoading } = useQuery({
+    queryKey: ['recentAllocations'],
+    queryFn: async () => {
+      const response = await enterpriseCompanyClient.allocations.recent.$get({
+        query: { limit: 5 } // 获取5条记录,dashboard只显示前2条
+      })
+      if (response.status !== 200) {
+        throw new Error('获取分配人才列表失败')
+      }
+      const data = await response.json()
+      // 转换数据格式
+      return data.人才列表.map(convertTalentToAllocation)
+    },
+    refetchOnWindowFocus: false
+  })
+
+  // 使用页面级下拉刷新
+  usePullDownRefresh(async () => {
+    try {
+      await Promise.all([
+        queryClient.invalidateQueries({ queryKey: ['enterpriseOverview'] }),
+        queryClient.invalidateQueries({ queryKey: ['recentAllocations'] })
+      ])
+    } finally {
+      Taro.stopPullDownRefresh()
+    }
+  })
+
+  // 页面加载时设置标题
+  useEffect(() => {
+    Taro.setNavigationBarTitle({
+      title: '企业仪表板'
+    })
+  }, [])
+
+  // 处理快捷操作按钮点击
+  const handleQuickActionClick = (action: string) => {
+    switch (action) {
+      case 'talentPool':
+        Taro.switchTab({
+          url: '/pages/yongren/talent/list/index'
+        })
+        break
+      case 'dataStats':
+        Taro.switchTab({
+          url: '/pages/yongren/statistics/index'
+        })
+        break
+      case 'orderManagement':
+        Taro.switchTab({
+          url: '/pages/yongren/order/list/index'
+        })
+        break
+      case 'settings':
+        Taro.switchTab({
+          url: '/pages/yongren/settings/index'
+        })
+        break
+      default:
+        break
+    }
+  }
+
+  // 处理"查看全部"链接点击
+  const handleViewAllClick = () => {
+    Taro.navigateTo({
+      url: '/pages/yongren/talent/list/index'
+    })
+  }
+
+  // 处理人才卡片点击
+  const handleTalentCardClick = (talentId: string) => {
+    Taro.navigateTo({
+      url: `/pages/yongren/talent/detail/index?id=${talentId}`
+    })
+  }
+
+  return (
+    <YongrenTabBarLayout activeKey="dashboard">
+      <ScrollView
+        className="h-[calc(100%-60px)] overflow-y-auto px-4 pb-4 pt-0"
+        scrollY
+      >
+        {/* 导航栏 */}
+        <Navbar
+          title="企业仪表板"
+          leftIcon=""
+          leftText=""
+          onClickLeft={() => {}}
+          backgroundColor="bg-white"
+          border={true}
+          fixed={true}
+          placeholder={true}
+        />
+
+        {/* 顶部信息栏 - 对照原型第276-300行 */}
+        <View className="bg-gradient-to-r from-blue-500 to-purple-600 text-white rounded-2xl p-5 mb-4">
+          <View className="flex justify-between items-center">
+            <View className="flex flex-col">
+              <Text className="text-sm opacity-80">欢迎回来</Text>
+              <Text className="text-xl font-bold">
+                {overview?.companyName || user?.name || '企业名称'}
+              </Text>
+            </View>
+            <View className="w-12 h-12 rounded-full bg-white/20 flex items-center justify-center">
+              <View className="i-heroicons-building-office-20-solid text-white text-xl" />
+            </View>
+          </View>
+          <View className="mt-4 flex justify-around">
+            <View className="flex flex-col items-center">
+              <Text className="text-2xl font-bold">{overview?.totalEmployees || 0}</Text>
+              <Text className="text-xs opacity-80">在职人员</Text>
+            </View>
+            <View className="flex flex-col items-center">
+              <Text className="text-2xl font-bold">{overview?.pendingAssignments || 0}</Text>
+              <Text className="text-xs opacity-80">待入职</Text>
+            </View>
+          </View>
+        </View>
+
+        {/* 快速操作网格 - 对照原型第303-320行 */}
+        <View className="grid grid-cols-4 gap-3 mb-4">
+          <View
+            className="bg-blue-50 rounded-xl p-3 flex flex-col items-center active:bg-blue-100"
+            onClick={() => handleQuickActionClick('talentPool')}
+          >
+            <View className="i-heroicons-user-group-20-solid text-blue-500 text-lg mb-1" />
+            <Text className="text-xs text-gray-700">人才库</Text>
+          </View>
+          <View
+            className="bg-green-50 rounded-xl p-3 flex flex-col items-center active:bg-green-100"
+            onClick={() => handleQuickActionClick('dataStats')}
+          >
+            <View className="i-heroicons-chart-bar-20-solid text-green-500 text-lg mb-1" />
+            <Text className="text-xs text-gray-700">数据统计</Text>
+          </View>
+          <View
+            className="bg-purple-50 rounded-xl p-3 flex flex-col items-center active:bg-purple-100"
+            onClick={() => handleQuickActionClick('orderManagement')}
+          >
+            <View className="i-heroicons-document-text-20-solid text-purple-500 text-lg mb-1" />
+            <Text className="text-xs text-gray-700">订单管理</Text>
+          </View>
+          <View
+            className="bg-yellow-50 rounded-xl p-3 flex flex-col items-center active:bg-yellow-100"
+            onClick={() => handleQuickActionClick('settings')}
+          >
+            <View className="i-heroicons-cog-6-tooth-20-solid text-yellow-500 text-lg mb-1" />
+            <Text className="text-xs text-gray-700">设置</Text>
+          </View>
+        </View>
+
+        {/* 人才列表区域 - 对照原型第323-376行 */}
+        <View className="mb-4">
+          <View className="flex justify-between items-center mb-3">
+            <Text className="font-semibold text-gray-700">分配人才</Text>
+            <Text
+              className="text-xs text-blue-500 active:text-blue-600"
+              onClick={handleViewAllClick}
+            >
+              查看全部
+            </Text>
+          </View>
+
+          {allocationsLoading ? (
+            <View className="space-y-3">
+              {[1, 2].map((i) => (
+                <View key={i} className="bg-white p-4 rounded-lg animate-pulse">
+                  <View className="flex items-center">
+                    <View className="w-10 h-10 bg-gray-200 rounded-full" />
+                    <View className="flex-1 ml-3">
+                      <View className="h-4 bg-gray-200 rounded w-1/3 mb-2" />
+                      <View className="h-3 bg-gray-200 rounded w-1/2" />
+                    </View>
+                  </View>
+                </View>
+              ))}
+            </View>
+          ) : allocations && allocations.length > 0 ? (
+            <View className="space-y-3">
+              {allocations.slice(0, 2).map((allocation: AllocationData) => (
+                <View
+                  key={allocation.id}
+                  className="bg-white p-4 rounded-lg flex items-center active:bg-gray-50"
+                  onClick={() => handleTalentCardClick(allocation.id)}
+                >
+                  {/* 头像区域 */}
+                  <View className={`name-avatar ${allocation.avatarColor} w-10 h-10 rounded-full flex items-center justify-center`}>
+                    <Text className="text-white font-semibold">
+                      {allocation.name.charAt(0)}
+                    </Text>
+                  </View>
+
+                  {/* 信息区域 */}
+                  <View className="flex-1 ml-3">
+                    <View className="flex justify-between items-start">
+                      <View className="flex flex-col">
+                        <Text className="font-semibold text-gray-800">{allocation.name}</Text>
+                        <Text className="text-xs text-gray-500">
+                          {allocation.disabilityType} · {allocation.disabilityLevel}
+                        </Text>
+                      </View>
+                      <Text className={`text-xs px-2 py-1 rounded-full ${
+                        allocation.status === '在职'
+                          ? 'bg-green-100 text-green-800'
+                          : allocation.status === '待入职'
+                          ? 'bg-yellow-100 text-yellow-800'
+                          : 'bg-gray-100 text-gray-800'
+                      }`}>
+                        {allocation.status}
+                      </Text>
+                    </View>
+
+                    <View className="mt-2">
+                      <View className="flex justify-between text-xs text-gray-500 mb-1">
+                        <Text>{allocation.status === '在职' ? '入职时间:' : '预计入职:'} {allocation.joinDate}</Text>
+                        <Text>薪资: ¥{allocation.salary.toLocaleString()}</Text>
+                      </View>
+                      <View className="progress-bar">
+                        <View
+                          className="progress-fill"
+                          style={{ width: `${allocation.progress}%` }}
+                        />
+                      </View>
+                    </View>
+                  </View>
+                </View>
+              ))}
+            </View>
+          ) : (
+            <View className="bg-white p-4 rounded-lg text-center">
+              <Text className="text-gray-500 text-sm">暂无分配人才</Text>
+            </View>
+          )}
+        </View>
+
+        {/* 数据统计卡片 - 对照原型第379-394行 */}
+        <View className="mb-4">
+          <Text className="font-semibold text-gray-700 mb-3">数据统计</Text>
+          <View className="grid grid-cols-2 gap-3">
+            <View className="stat-card bg-white p-4 rounded-lg flex flex-col">
+              <View className="flex items-center mb-2">
+                <View className="pulse-dot mr-2" />
+                <Text className="text-sm text-gray-600">在职率</Text>
+              </View>
+              <Text className="text-2xl font-bold text-gray-800">
+                {overview?.totalEmployees ? '92%' : '--'}
+              </Text>
+            </View>
+            <View className="stat-card bg-white p-4 rounded-lg flex flex-col items-center">
+              <Text className="text-sm text-gray-600 mb-2">平均薪资</Text>
+              <Text className="text-2xl font-bold text-gray-800">
+                {allocations && allocations.length > 0
+                  ? `¥${Math.round(allocations.reduce((sum: number, a: AllocationData) => sum + a.salary, 0) / allocations.length).toLocaleString()}`
+                  : '¥0'}
+              </Text>
+            </View>
+          </View>
+        </View>
+      </ScrollView>
+    </YongrenTabBarLayout>
+  )
+}
+
 export default Dashboard

+ 729 - 2
mini/src/pages/yongren/order/detail/index.tsx

@@ -1,3 +1,730 @@
-// 桥接文件:从 @d8d/yongren-order-management-ui 包导入OrderDetail页面
-import OrderDetail from '@d8d/yongren-order-management-ui/pages/OrderDetail/OrderDetail'
+import React, { useState, useEffect } from 'react'
+import { View, Text, ScrollView, Picker } from '@tarojs/components'
+import Taro from '@tarojs/taro'
+import { useQuery } from '@tanstack/react-query'
+import { Navbar } from '@d8d/mini-shared-ui-components/components/navbar'
+import { enterpriseOrderClient } from '../../../api'
+import type { CompanyVideosResponse } from '../../../api'
+
+
+interface OrderDetailData {
+  id: any
+  orderNumber: any
+  name: any
+  createdAt: string
+  updatedAt: string
+  status: any
+  statusLabel: string
+  statusClass: string
+  company: any
+  platform: any
+  channel: any
+  actualPeople: any
+  expectedStartDate: string
+  actualStartDate: string
+  actualEndDate: string | null
+  orderPersons?: Array<{
+    id: number
+    workStatus: string
+    joinDate: string
+    person?: {
+      name: string
+      gender: string
+      disabilityType: string
+      phone: string
+    }
+  }>
+}
+
+const OrderDetail: React.FC = () => {
+  const router = Taro.useRouter()
+  const orderIdParam = router.params.id ? parseInt(router.params.id) : null
+  const orderId = orderIdParam && !Number.isNaN(orderIdParam) ? orderIdParam : null
+  const [showCheckinCalendar, setShowCheckinCalendar] = useState(false)
+  const [filterStartDate, setFilterStartDate] = useState('')
+  const [filterEndDate, setFilterEndDate] = useState('')
+
+
+  // 获取订单详情查询函数
+  const fetchOrderDetailQuery = async (id: number) => {
+    const response = await enterpriseOrderClient.detail[':id'].$get({
+      param: { id: id }
+    })
+
+    if (response.status !== 200) {
+      throw new Error(`获取订单详情失败: ${response.status}`)
+    }
+
+    const data = await response.json()
+    if (!data) {
+      throw new Error('API返回数据为空')
+    }
+    // 转换API数据到UI格式
+    const orderPersons = data?.orderPersons || []
+    return {
+      id: data?.id ?? 0,
+      orderNumber: data?.orderName ? `ORDER-${data.id}` : `ORDER-${data?.id ?? 0}`,
+      name: data?.orderName || '未命名订单',
+      createdAt: data?.createTime ? new Date(data.createTime).toISOString().split('T')[0] : '未知日期',
+      updatedAt: data?.updateTime ? new Date(data.updateTime).toISOString().split('T')[0] : '未知日期',
+      status: data?.orderStatus || 'draft',
+      statusLabel: getStatusLabel(data?.orderStatus),
+      statusClass: getStatusClass(data?.orderStatus),
+      company: data?.companyId ? `公司${data.companyId}` : '未知公司',
+      platform: data?.platformId ? `平台${data.platformId}` : '未知平台',
+      channel: '未知渠道', // data?.channelName 不存在
+      actualPeople: orderPersons.length,
+      expectedStartDate: data?.expectedStartDate ? new Date(data.expectedStartDate).toISOString().split('T')[0] : '未设置',
+      actualStartDate: data?.actualStartDate ? new Date(data.actualStartDate).toISOString().split('T')[0] : '未设置',
+      actualEndDate: data?.actualEndDate ? new Date(data.actualEndDate).toISOString().split('T')[0] : null,
+      orderPersons: orderPersons.map((op: any) => ({
+        id: op.id,
+        workStatus: op.workStatus || 'unknown',
+        joinDate: op.joinDate ? new Date(op.joinDate).toISOString().split('T')[0] : '未知日期',
+        person: op.person ? {
+          name: op.person.name || '未知姓名',
+          gender: op.person.gender || '未知',
+          disabilityType: op.person.disabilityType || '未知类型',
+          phone: op.person.phone || '未知电话'
+        } : undefined
+      }))
+    }
+  }
+
+  // 获取企业用户信息
+  const getEnterpriseUserInfo = () => {
+    try {
+      const userInfoStr = Taro.getStorageSync('enterpriseUserInfo')
+      if (!userInfoStr) return null
+
+      // 尝试解析JSON字符串
+      let userInfo = userInfoStr
+      if (typeof userInfoStr === 'string') {
+        userInfo = JSON.parse(userInfoStr)
+      }
+
+      // 处理双重编码情况:{"data": "{\"id\":2,...}"}
+      if (userInfo && typeof userInfo === 'object' && userInfo.data) {
+        if (typeof userInfo.data === 'string') {
+          userInfo = JSON.parse(userInfo.data)
+        } else {
+          userInfo = userInfo.data
+        }
+      }
+
+      return userInfo || null
+    } catch (error) {
+      console.error('获取企业用户信息失败:', error)
+      return null
+    }
+  }
+
+  // 获取订单视频查询函数
+  const fetchOrderVideosQuery = async (_orderId: number) => {
+    try {
+      const userInfo = getEnterpriseUserInfo()
+      const companyId = userInfo?.companyId || 0
+
+      if (!companyId) {
+        console.warn('未找到企业ID,返回空视频列表')
+        return [] as VideoItem[]
+      }
+
+      // 获取企业视频列表
+      const response = await enterpriseOrderClient['company-videos'].$get({
+        query: {
+          companyId,
+          page: 1,
+          pageSize: 50 // 获取前50个视频
+        }
+      })
+
+      if (response.ok) {
+        const data = await response.json()
+        const videos = data?.data || []
+
+        // 转换API数据到UI格式
+        const transformedVideos = videos.map((video: CompanyVideosResponse['data'][0]) => {
+          // 根据assetType确定视频类型(后端返回值:job_result, salary, tax)
+          let videoType: 'checkin_video' | 'salary_video' | 'tax_video' = 'checkin_video'
+          if (video.assetType === 'salary') videoType = 'salary_video'
+          if (video.assetType === 'tax') videoType = 'tax_video'
+          // job_result 是打卡视频,使用默认值 checkin_video
+
+          return {
+            id: video.id || video.fileId || 0,
+            name: video.file?.name || `视频-${video.id}`,
+            type: videoType,
+            size: video.file?.size ? `${Math.round(video.file.size / 1024 / 1024)}MB` : '未知大小',
+            uploadTime: video.file?.uploadTime ? new Date(video.file.uploadTime).toISOString().split('T')[0] : '未知日期'
+          }
+        })
+
+        return transformedVideos
+      } else {
+        throw new Error('获取视频列表失败')
+      }
+    } catch (error) {
+      console.error('获取订单视频失败:', error)
+      // 返回空列表
+      return [] as VideoItem[]
+    }
+  }
+
+
+  // 获取订单统计数据查询函数
+  const fetchOrderStatisticsQuery = async (orderId: number) => {
+    try {
+      if (!orderId) {
+        console.warn('[fetchOrderStatisticsQuery] orderId 为空,返回空统计数据')
+        return {
+          actualPeople: 0,
+          checkinStats: { current: 0, total: 0, percentage: 0 },
+          salaryVideoStats: { current: 0, total: 0, percentage: 0 },
+          taxVideoStats: { current: 0, total: 0, percentage: 0 },
+        }
+      }
+
+      console.debug('[fetchOrderStatisticsQuery] 正在调用订单统计 API,orderId:', orderId)
+      // 使用与列表页相同的统计 API:/company-orders/:id/stats
+      const response = await enterpriseOrderClient['company-orders'][':id']['stats'].$get({
+        param: { id: orderId },
+        query: {}
+      })
+      console.debug('[fetchOrderStatisticsQuery] API 响应状态:', response.status, response.ok)
+
+      if (response.ok) {
+        const data = await response.json()
+        console.debug('[fetchOrderStatisticsQuery] API 返回数据:', data)
+        return data
+      } else {
+        throw new Error(`获取订单统计失败: ${response.status}`)
+      }
+    } catch (error) {
+      console.error('[fetchOrderStatisticsQuery] 获取订单统计数据失败:', error)
+      // 返回默认数据
+      return {
+        actualPeople: 0,
+        checkinStats: { current: 0, total: 0, percentage: 0 },
+        salaryVideoStats: { current: 0, total: 0, percentage: 0 },
+        taxVideoStats: { current: 0, total: 0, percentage: 0 },
+      }
+    }
+  }
+
+  // 使用React Query获取订单详情
+  const {
+    data: order,
+    isLoading,
+    error: queryError,
+  } = useQuery<OrderDetailData, Error>({
+    queryKey: ['order-detail', orderId],
+    queryFn: () => orderId ? fetchOrderDetailQuery(orderId) : Promise.reject(new Error('未找到订单ID')),
+    enabled: !!orderId,
+  })
+
+  // 视频查询接口
+  interface VideoItem {
+    id: number
+    name: string
+    type: 'checkin_video' | 'salary_video' | 'tax_video'
+    size: string
+    uploadTime: string
+  }
+
+  // 统计数据接口 - 与列表页保持一致
+  interface StatisticsData {
+    actualPeople: number
+    checkinStats: { current: number, total: number, percentage: number }
+    salaryVideoStats: { current: number, total: number, percentage: number }
+    taxVideoStats: { current: number, total: number, percentage: number }
+  }
+
+  // 使用React Query获取订单视频
+  const {
+    data: videos = [],
+    isLoading: videosLoading,
+    error: videosError,
+  } = useQuery<VideoItem[], Error>({
+    queryKey: ['order-videos', orderId],
+    queryFn: () => orderId ? fetchOrderVideosQuery(orderId) : Promise.reject(new Error('未找到订单ID')),
+    enabled: !!orderId,
+  })
+
+  // 使用React Query获取订单统计数据 - 与列表页保持相同的缓存策略
+  const {
+    data: statistics,
+    isLoading: statisticsLoading,
+    error: statisticsError,
+  } = useQuery<StatisticsData, Error>({
+    queryKey: ['order-stats', orderId], // 与列表页使用相同的 queryKey,确保缓存共享
+    queryFn: () => orderId ? fetchOrderStatisticsQuery(orderId) : Promise.reject(new Error('未找到订单ID')),
+    enabled: !!orderId,
+    staleTime: 5 * 60 * 1000, // 5 minutes cache - 与列表页保持一致
+    retry: 1
+  })
+
+  // 处理查询错误
+  useEffect(() => {
+    if (queryError) {
+      console.error('获取订单详情错误:', queryError)
+      Taro.showToast({
+        title: queryError.message.includes('未找到订单ID') ? '未找到订单ID' : '获取订单详情失败',
+        icon: 'none'
+      })
+    }
+    if (videosError) {
+      console.error('获取订单视频错误:', videosError)
+      Taro.showToast({
+        title: '获取视频数据失败',
+        icon: 'none'
+      })
+    }
+    if (statisticsError) {
+      console.error('获取订单统计错误:', statisticsError)
+      Taro.showToast({
+        title: '获取统计数据失败',
+        icon: 'none'
+      })
+    }
+  }, [queryError, videosError, statisticsError])
+
+  // 组合加载状态和错误状态
+  const isLoadingAll = isLoading || videosLoading || statisticsLoading
+  const hasError = queryError || videosError || statisticsError
+  const errorMessage = queryError?.message || videosError?.message || statisticsError?.message || '加载数据失败'
+
+  // 状态标签和样式辅助函数
+  const getStatusLabel = (status: string | undefined) => {
+    const statusValue = status || 'draft'
+    switch (statusValue) {
+      case 'draft': return '草稿'
+      case 'confirmed': return '已确认'
+      case 'in_progress': return '进行中'
+      case 'completed': return '已完成'
+      case 'cancelled': return '已取消'
+      default: return statusValue
+    }
+  }
+
+  const getStatusClass = (status: string | undefined) => {
+    const statusValue = status || 'draft'
+    switch (statusValue) {
+      case 'draft': return 'bg-gray-100 text-gray-800'
+      case 'confirmed': return 'bg-blue-100 text-blue-800'
+      case 'in_progress': return 'bg-green-100 text-green-800'
+      case 'completed': return 'bg-purple-100 text-purple-800'
+      case 'cancelled': return 'bg-red-100 text-red-800'
+      default: return 'bg-gray-100 text-gray-800'
+    }
+  }
+
+
+
+
+
+  const handleViewCheckinDetails = () => {
+    setShowCheckinCalendar(true)
+  }
+
+  // 获取打卡日历数据
+  const getCheckinCalendarData = () => {
+    if (!videos || videos.length === 0) {
+      return {
+        checkinDays: [],
+        totalDays: 31,
+        checkinCount: 0,
+        missedCount: 0,
+        attendanceRate: 0
+      }
+    }
+
+    // 筛选打卡视频
+    const checkinVideos = videos.filter(video => video.type === 'checkin_video')
+
+    // 提取日期(假设uploadTime格式为YYYY-MM-DD)
+    const checkinDates = checkinVideos.map(video => {
+      const dateStr = video.uploadTime
+      return dateStr.split(' ')[0] // 取日期部分
+    })
+
+    // 去重
+    const uniqueDates = [...new Set(checkinDates)]
+
+    // 计算本月数据(模拟)
+    const currentMonth = '2025-12'
+    const daysInMonth = 31
+
+    // 筛选本月日期
+    const monthDates = uniqueDates.filter(date => date.startsWith(currentMonth))
+
+    // 计算打卡日期
+    const checkinDays = monthDates.map(date => parseInt(date.split('-')[2]))
+
+    return {
+      checkinDays,
+      totalDays: daysInMonth,
+      checkinCount: checkinDays.length,
+      missedCount: daysInMonth - checkinDays.length,
+      attendanceRate: Math.round((checkinDays.length / daysInMonth) * 100)
+    }
+  }
+
+  // 导出打卡数据
+  const handleExportCheckinData = async () => {
+    try {
+      // 获取打卡数据
+      const checkinData = getCheckinCalendarData()
+      const checkinVideos = videos.filter(video => video.type === 'checkin_video')
+
+      // 创建CSV内容
+      const headers = ['日期', '视频名称', '视频类型', '大小', '上传时间']
+      const rows = checkinVideos.map(video => [
+        video.uploadTime.split(' ')[0],
+        video.name,
+        video.type === 'checkin_video' ? '打卡视频' : '其他',
+        video.size,
+        video.uploadTime
+      ])
+
+      // 添加统计信息
+      rows.push([])
+      rows.push(['统计信息', '', '', '', ''])
+      rows.push(['总打卡天数', checkinData.checkinCount.toString(), '', '', ''])
+      rows.push(['未打卡天数', checkinData.missedCount.toString(), '', '', ''])
+      rows.push(['出勤率', `${checkinData.attendanceRate}%`, '', '', ''])
+
+      const csvContent = [headers, ...rows].map(row => row.join(',')).join('\n')
+
+      // 在小程序中,可以使用Taro.saveFile或Taro.downloadFile
+      // 这里先显示提示,实际项目中需要实现文件保存逻辑
+      Taro.showToast({
+        title: `已生成${checkinVideos.length}条打卡记录`,
+        icon: 'success'
+      })
+
+      console.log('打卡数据CSV:', csvContent)
+      // TODO: 实际文件保存逻辑
+      // Taro.downloadFile({
+      //   url: 'data:text/csv;charset=utf-8,' + encodeURIComponent(csvContent),
+      //   success: function (res) {
+      //     Taro.saveFile({
+      //       tempFilePath: res.tempFilePath,
+      //       success: function (saveRes) {
+      //         Taro.showToast({ title: '导出成功', icon: 'success' })
+      //       }
+      //     })
+      //   }
+      // })
+
+    } catch (error) {
+      console.error('导出打卡数据失败:', error)
+      Taro.showToast({
+        title: '导出失败',
+        icon: 'none'
+      })
+    }
+  }
+
+  // 播放视频
+  const handlePlayVideo = (videoId: number, videoName: string) => {
+    console.log('播放视频:', videoId, videoName)
+    // TODO: 实现视频播放逻辑
+    // 在小程序中,可能需要使用Taro.openVideo或Taro.previewMedia
+    // 首先需要获取视频URL
+    Taro.showToast({
+      title: `播放视频: ${videoName}`,
+      icon: 'none'
+    })
+  }
+
+  return (
+    <>
+      {/* 导航栏 */}
+      <Navbar
+        title="订单详情"
+        leftIcon="i-heroicons-chevron-left-20-solid"
+        leftText="返回"
+        backgroundColor="bg-white"
+        border={true}
+        fixed={true}
+        placeholder={true}
+        onClickLeft={() => console.log('返回')}
+      />
+      <ScrollView
+        className="h-screen overflow-y-auto px-4 pb-4 pt-0"
+        scrollY
+      >
+        {isLoadingAll ? (
+          <View className="flex items-center justify-center h-64">
+            <Text className="text-gray-500">加载中...</Text>
+          </View>
+        ) : hasError ? (
+          <View className="flex items-center justify-center h-64">
+            <Text className="text-red-500">{errorMessage}</Text>
+          </View>
+        ) : !order ? (
+          <View className="flex items-center justify-center h-64">
+            <Text className="text-gray-500">未找到订单数据</Text>
+          </View>
+        ) : (
+          <>
+        {/* 顶部信息卡片 */}
+        <View className="card bg-white p-4 mb-4 mt-4">
+          <View className="flex justify-between items-start mb-3">
+            <View className="flex flex-col">
+              <Text className="text-xl font-bold text-gray-800">{order.name}</Text>
+              <Text className="text-sm text-gray-500 mt-1">订单编号: {order.orderNumber}</Text>
+            </View>
+            <Text className={`text-xs px-2 py-1 rounded-full ${order.statusClass}`}>
+              {order.statusLabel}
+            </Text>
+          </View>
+
+          <View className="grid grid-cols-2 gap-3 text-sm">
+            <View className="flex flex-col">
+              <Text className="text-gray-500">创建时间</Text>
+              <Text className="text-gray-800">{order.createdAt}</Text>
+            </View>
+            <View className="flex flex-col">
+              <Text className="text-gray-500">更新时间</Text>
+              <Text className="text-gray-800">{order.updatedAt}</Text>
+            </View>
+            <View className="flex flex-col">
+              <Text className="text-gray-500">企业名称</Text>
+              <Text className="text-gray-800">{order.company}</Text>
+            </View>
+            <View className="flex flex-col">
+              <Text className="text-gray-500">平台</Text>
+              <Text className="text-gray-800">{order.platform}</Text>
+            </View>
+          </View>
+        </View>
+
+        {/* 基本信息卡片 */}
+        <View className="card bg-white p-4 mb-4">
+          <Text className="font-semibold text-gray-700 mb-3">基本信息</Text>
+          <View className="grid grid-cols-2 gap-3 text-sm">
+            <View className="flex flex-col">
+              <Text className="text-gray-500">实际人数</Text>
+              <Text className="text-gray-800">{order.actualPeople}人</Text>
+            </View>
+            <View className="flex flex-col">
+              <Text className="text-gray-500">预计开始</Text>
+              <Text className="text-gray-800">{order.expectedStartDate}</Text>
+            </View>
+            <View className="flex flex-col">
+              <Text className="text-gray-500">实际开始</Text>
+              <Text className="text-gray-800">{order.actualStartDate}</Text>
+            </View>
+            <View className="flex flex-col">
+              <Text className="text-gray-500">实际结束</Text>
+              <Text className="text-gray-800">{order.actualEndDate || '未结束'}</Text>
+            </View>
+            <View className="flex flex-col">
+              <Text className="text-gray-500">渠道</Text>
+              <Text className="text-gray-800">{order.channel}</Text>
+            </View>
+          </View>
+        </View>
+
+        {/* 打卡统计卡片 */}
+        <View className="card bg-white p-4 mb-4">
+          <Text className="font-semibold text-gray-700 mb-3">打卡数据统计</Text>
+          <View className="grid grid-cols-3 gap-2 mb-3">
+            <View className="bg-blue-50 rounded-lg p-2 text-center flex flex-col">
+              <Text className="text-xs text-gray-600">本月打卡</Text>
+              <Text className="text-sm font-bold text-gray-800">
+                {statistics?.checkinStats.current || 0}/{statistics?.checkinStats.total || 0}
+              </Text>
+              <Text className="text-xs text-gray-500">100%</Text>
+            </View>
+            <View className="bg-green-50 rounded-lg p-2 text-center flex flex-col">
+              <Text className="text-xs text-gray-600">工资视频</Text>
+              <Text className="text-sm font-bold text-gray-800">
+                {statistics?.salaryVideoStats.current || 0}/{statistics?.salaryVideoStats.total || 0}
+              </Text>
+              <Text className="text-xs text-gray-500">100%</Text>
+            </View>
+            <View className="bg-purple-50 rounded-lg p-2 text-center flex flex-col">
+              <Text className="text-xs text-gray-600">个税视频</Text>
+              <Text className="text-sm font-bold text-gray-800">
+                {statistics?.taxVideoStats.current || 0}/{statistics?.taxVideoStats.total || 0}
+              </Text>
+              <Text className="text-xs text-gray-500">100%</Text>
+            </View>
+          </View>
+          <View className="flex items-center text-blue-500 text-sm" onClick={handleViewCheckinDetails}>
+            <Text>查看详细打卡记录</Text>
+          </View>
+        </View>
+
+        {/* 关联人才卡片 */}
+        <View className="card bg-white p-4 mb-4">
+          <View className="flex items-center mb-3">
+            <Text className="font-semibold text-gray-700">关联人才</Text>
+          </View>
+          <View className="space-y-3">
+            {order?.orderPersons?.map((orderPerson) => (
+              <View key={orderPerson.id} className="flex justify-between items-center border-b border-gray-100 pb-2">
+                <View className="flex flex-col">
+                  <Text className="font-medium text-gray-800">{orderPerson.person?.name || '未知姓名'}</Text>
+                  <Text className="text-xs text-gray-500">
+                    {orderPerson.person?.gender || '未知'} · {orderPerson.person?.disabilityType || '未知类型'} · 入职: {orderPerson.joinDate}
+                  </Text>
+                </View>
+                <Text className={`text-xs px-2 py-1 rounded-full ${
+                  orderPerson.workStatus === 'working' ? 'bg-green-100 text-green-800' :
+                  orderPerson.workStatus === 'pre_working' ? 'bg-yellow-100 text-yellow-800' :
+                  'bg-gray-100 text-gray-800'
+                }`}>
+                  {orderPerson.workStatus === 'working' ? '在职' :
+                   orderPerson.workStatus === 'pre_working' ? '待入职' : '离职'}
+                </Text>
+              </View>
+            ))}
+          </View>
+        </View>
+
+        {/* 视频资料卡片 */}
+        <View className="card bg-white p-4 mb-4">
+          <Text className="font-semibold text-gray-700 mb-3">视频资料</Text>
+          <View className="space-y-3">
+            {(videos || []).map((video: VideoItem) => (
+              <View key={video.id} className="flex justify-between items-center border-b border-gray-100 pb-2">
+                <View className="flex flex-col">
+                  <Text className="font-medium text-gray-800">{video.name}</Text>
+                  <Text className="text-xs text-gray-500">
+                    {video.type === 'checkin_video' ? '打卡视频' :
+                     video.type === 'salary_video' ? '工资视频' : '个税视频'} · {video.size} · {video.uploadTime}
+                  </Text>
+                </View>
+                <View className="flex items-center text-blue-500 text-xs" onClick={() => handlePlayVideo(video.id, video.name)}>
+                  <Text>播放</Text>
+                </View>
+              </View>
+            ))}
+          </View>
+        </View>
+        </>)}
+      </ScrollView>
+      {/* 打卡日历模态框 */}
+      {showCheckinCalendar && (
+        <View className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
+          <View className="bg-white rounded-lg w-11/12 max-w-md">
+            {/* 模态框头部 */}
+            <View className="flex justify-between items-center p-4 border-b border-gray-200">
+              <Text className="font-semibold text-gray-700">打卡日历</Text>
+              <View
+                className="text-gray-500 text-lg"
+                onClick={() => setShowCheckinCalendar(false)}
+              >
+                <Text>×</Text>
+              </View>
+            </View>
+            {/* 模态框内容 */}
+            <View className="p-4">
+              {/* 时间范围筛选 */}
+              <View className="flex space-x-2 mb-4">
+                <View className="flex-1">
+                  <Text className="text-xs text-gray-500 mb-1">开始日期</Text>
+                  <Picker
+                    mode="date"
+                    value={filterStartDate}
+                    onChange={(e) => setFilterStartDate(e.detail.value)}
+                  >
+                    <View className="border border-gray-300 rounded px-2 py-1 text-sm bg-white min-h-[2rem] flex items-center">
+                      <Text className={filterStartDate ? 'text-gray-800' : 'text-gray-400'}>
+                        {filterStartDate || '选择开始日期'}
+                      </Text>
+                    </View>
+                  </Picker>
+                </View>
+                <View className="flex-1">
+                  <Text className="text-xs text-gray-500 mb-1">结束日期</Text>
+                  <Picker
+                    mode="date"
+                    value={filterEndDate}
+                    onChange={(e) => setFilterEndDate(e.detail.value)}
+                  >
+                    <View className="border border-gray-300 rounded px-2 py-1 text-sm bg-white min-h-[2rem] flex items-center">
+                      <Text className={filterEndDate ? 'text-gray-800' : 'text-gray-400'}>
+                        {filterEndDate || '选择结束日期'}
+                      </Text>
+                    </View>
+                  </Picker>
+                </View>
+              </View>
+
+              {/* 简单的日历视图 */}
+              <View className="mb-4">
+                <Text className="font-medium text-gray-700 mb-2">2025年12月</Text>
+                <View className="grid grid-cols-7 gap-1 text-center">
+                  {/* 星期标题 */}
+                  {['日', '一', '二', '三', '四', '五', '六'].map((day) => (
+                    <Text key={day} className="text-xs text-gray-500 py-1">{day}</Text>
+                  ))}
+                  {/* 日期单元格 */}
+                  {Array.from({ length: 31 }, (_, i) => i + 1).map((day) => {
+                    const checkinData = getCheckinCalendarData()
+                    const isCheckedIn = checkinData.checkinDays.includes(day)
+                    return (
+                      <View
+                        key={day}
+                        className={`p-2 rounded ${isCheckedIn ? 'bg-green-100' : 'bg-gray-100'}`}
+                      >
+                        <Text className={`text-sm ${isCheckedIn ? 'text-green-800' : 'text-gray-500'}`}>
+                          {day}
+                        </Text>
+                        {isCheckedIn && (
+                          <Text className="text-xs text-green-600">已打卡</Text>
+                        )}
+                      </View>
+                    )
+                  })}
+                </View>
+              </View>
+
+              {/* 打卡统计 */}
+              <View className="bg-blue-50 rounded-lg p-3 mb-4">
+                <Text className="font-medium text-blue-700 mb-1">本月打卡统计</Text>
+                <View className="flex justify-between">
+                  <View className="flex flex-col">
+                    <Text className="text-xs text-blue-600">已打卡</Text>
+                    <Text className="text-lg font-bold text-blue-800">{getCheckinCalendarData().checkinCount}天</Text>
+                  </View>
+                  <View className="flex flex-col">
+                    <Text className="text-xs text-blue-600">未打卡</Text>
+                    <Text className="text-lg font-bold text-blue-800">{getCheckinCalendarData().missedCount}天</Text>
+                  </View>
+                  <View className="flex flex-col">
+                    <Text className="text-xs text-blue-600">出勤率</Text>
+                    <Text className="text-lg font-bold text-blue-800">100%</Text>
+                  </View>
+                </View>
+              </View>
+
+              {/* 操作按钮 */}
+              <View className="flex space-x-2">
+                <View
+                  className="flex-1 flex items-center justify-center bg-blue-500 text-white text-sm px-4 py-2 rounded-lg"
+                  onClick={handleExportCheckinData}
+                >
+                  <Text>导出打卡数据</Text>
+                </View>
+                <View
+                  className="flex-1 flex items-center justify-center border border-gray-300 text-gray-700 text-sm px-4 py-2 rounded-lg"
+                  onClick={() => setShowCheckinCalendar(false)}
+                >
+                  <Text>关闭</Text>
+                </View>
+              </View>
+            </View>
+          </View>
+        </View>
+      )}
+    </>
+  )
+} 
+
 export default OrderDetail

+ 457 - 2
mini/src/pages/yongren/order/list/index.tsx

@@ -1,3 +1,458 @@
-// 桥接文件:从 @d8d/yongren-order-management-ui 包导入OrderList页面
-import OrderList from '@d8d/yongren-order-management-ui/pages/OrderList/OrderList'
+import React, { useState } from 'react'
+import { View, Text, ScrollView, Input } from '@tarojs/components'
+import Taro, { useDidShow, usePullDownRefresh } from '@tarojs/taro'
+import { useInfiniteQuery, useQuery } from '@tanstack/react-query'
+import { YongrenTabBarLayout } from '../../../../components/YongrenTabBarLayout'
+import { Navbar } from '@d8d/mini-shared-ui-components/components/navbar'
+import { enterpriseOrderClient } from '../../../api'
+import type { OrderData } from '../../../api'
+
+type OrderStatus = 'all' | 'in_progress' | 'completed' | 'cancelled'
+
+/**
+ * Hook to fetch order statistics
+ * @param orderId Order ID
+ * @returns Query result with order stats
+ */
+const useOrderStats = (orderId: number | undefined) => {
+  return useQuery({
+    queryKey: ['order-stats', orderId],
+    queryFn: async () => {
+      if (!orderId) {
+        console.debug('[useOrderStats] orderId 为空,跳过 API 调用')
+        return null
+      }
+      console.debug('[useOrderStats] 正在调用统计 API,orderId:', orderId)
+      // 路径: /company-orders/:id/stats -> client key: 'company-orders' -> ':id' -> 'stats'
+      const response = await enterpriseOrderClient['company-orders'][':id']['stats'].$get({
+        param: { id: orderId },
+        query: {}
+      })
+      console.debug('[useOrderStats] API 响应状态:', response.status, response.ok)
+      if (response.ok) {
+        const data = await response.json()
+        console.debug('[useOrderStats] API 返回数据:', data)
+        return data
+      }
+      throw new Error('获取订单统计失败')
+    },
+    enabled: !!orderId,
+    staleTime: 5 * 60 * 1000, // 5 minutes cache
+    retry: 1
+  })
+}
+
+/**
+ * 订单卡片组件 - 负责显示单个订单和获取统计数据
+ */
+interface OrderCardProps {
+  order: {
+    id: number
+    orderNumber: string
+    name: string
+    createdAt: string
+    status: string
+    statusLabel: string
+    statusClass: string
+    actualPeople: number
+    startDate: string
+    endDate: string
+    talentName: string
+    position: string
+  }
+  onViewDetail: (orderId: number) => void
+}
+
+const OrderCard: React.FC<OrderCardProps> = ({ order, onViewDetail }) => {
+  // 获取订单统计数据
+  const { data: statsData, isLoading: statsLoading } = useOrderStats(order.id)
+
+  // 使用 API 返回的数据,或默认值(加载中或失败时)
+  const checkinStats = statsData?.checkinStats || { current: 0, total: order.actualPeople, percentage: 0 }
+  const salaryVideoStats = statsData?.salaryVideoStats || { current: 0, total: order.actualPeople, percentage: 0 }
+  const taxVideoStats = statsData?.taxVideoStats || { current: 0, total: order.actualPeople, percentage: 0 }
+
+  return (
+    <View className="card bg-white p-4">
+      {/* 订单头部 */}
+      <View className="flex justify-between items-start mb-3">
+        <View className="flex flex-col">
+          <Text className="font-semibold text-gray-800">{order.name}</Text>
+          <Text className="text-xs text-gray-500">{order.createdAt} 创建</Text>
+        </View>
+        <Text className={`text-xs px-2 py-1 rounded-full ${order.statusClass}`}>
+          {order.statusLabel}
+        </Text>
+      </View>
+
+      {/* 订单信息网格 */}
+      <View className="grid grid-cols-2 gap-3 text-sm mb-3">
+        <View className="flex flex-col">
+          <Text className="text-gray-500">实际人数</Text>
+          <Text className="text-gray-800">
+            {statsLoading ? '...' : `${statsData?.actualPeople ?? order.actualPeople}人`}
+          </Text>
+        </View>
+        <View className="flex flex-col">
+          <Text className="text-gray-500">开始日期</Text>
+          <Text className="text-gray-800">{order.startDate}</Text>
+        </View>
+        <View className="flex flex-col col-span-2">
+          <Text className="text-gray-500">实际结束</Text>
+          <Text className="text-gray-800">{order.endDate}</Text>
+        </View>
+      </View>
+
+      {/* 打卡数据统计网格 */}
+      <View className="grid grid-cols-3 gap-2 mb-3">
+        <View className="bg-blue-50 rounded-lg p-2 text-center flex flex-col">
+          <Text className="text-xs text-gray-600">本月打卡</Text>
+          {statsLoading ? (
+            <Text className="text-sm text-gray-400">...</Text>
+          ) : (
+            <>
+              <Text className="text-sm font-bold text-gray-800">
+                {checkinStats.current}/{checkinStats.total}
+              </Text>
+              <Text className="text-xs text-gray-500">{checkinStats.percentage}%</Text>
+            </>
+          )}
+        </View>
+        <View className="bg-green-50 rounded-lg p-2 text-center flex flex-col">
+          <Text className="text-xs text-gray-600">工资视频</Text>
+          {statsLoading ? (
+            <Text className="text-sm text-gray-400">...</Text>
+          ) : (
+            <>
+              <Text className="text-sm font-bold text-gray-800">
+                {salaryVideoStats.current}/{salaryVideoStats.total}
+              </Text>
+              <Text className="text-xs text-gray-500">{salaryVideoStats.percentage}%</Text>
+            </>
+          )}
+        </View>
+        <View className="bg-purple-50 rounded-lg p-2 text-center flex flex-col">
+          <Text className="text-xs text-gray-600">个税视频</Text>
+          {statsLoading ? (
+            <Text className="text-sm text-gray-400">...</Text>
+          ) : (
+            <>
+              <Text className="text-sm font-bold text-gray-800">
+                {taxVideoStats.current}/{taxVideoStats.total}
+              </Text>
+              <Text className="text-xs text-gray-500">{taxVideoStats.percentage}%</Text>
+            </>
+          )}
+        </View>
+      </View>
+
+      {/* 操作按钮区域 */}
+      <View className="flex justify-end text-sm">
+        <View className="flex items-center text-blue-500" onClick={() => onViewDetail(order.id)}>
+          <Text className="i-heroicons-eye-20-solid mr-1" />
+          <Text>查看详情</Text>
+        </View>
+      </View>
+    </View>
+  )
+}
+
+// 状态标签和样式辅助函数(移到组件外部以提高性能)
+const getStatusLabel = (status: string): string => {
+  switch (status) {
+    case 'draft': return '草稿'
+    case 'confirmed': return '已确认'
+    case 'in_progress': return '进行中'
+    case 'completed': return '已完成'
+    case 'cancelled': return '已取消'
+    default: return status
+  }
+}
+
+const getStatusClass = (status: string): string => {
+  switch (status) {
+    case 'draft': return 'bg-gray-100 text-gray-800'
+    case 'confirmed': return 'bg-blue-100 text-blue-800'
+    case 'in_progress': return 'bg-green-100 text-green-800'
+    case 'completed': return 'bg-purple-100 text-purple-800'
+    case 'cancelled': return 'bg-red-100 text-red-800'
+    default: return 'bg-gray-100 text-gray-800'
+  }
+}
+
+// 获取企业用户信息的辅助函数(移到组件外部以提高性能)
+const getEnterpriseUserInfo = () => {
+  try {
+    const userInfoStr = Taro.getStorageSync('enterpriseUserInfo')
+    if (!userInfoStr) return null
+
+    // 尝试解析JSON字符串
+    let userInfo = userInfoStr
+    if (typeof userInfoStr === 'string') {
+      userInfo = JSON.parse(userInfoStr)
+    }
+
+    // 处理双重编码情况:{"data": "{\"id\":2,...}"}
+    if (userInfo && typeof userInfo === 'object' && userInfo.data) {
+      if (typeof userInfo.data === 'string') {
+        userInfo = JSON.parse(userInfo.data)
+      } else {
+        userInfo = userInfo.data
+      }
+    }
+
+    return userInfo || null
+  } catch (error) {
+    console.debug('获取企业用户信息失败:', error)
+    return null
+  }
+}
+
+const OrderList: React.FC = () => {
+  const [activeStatus, setActiveStatus] = useState<OrderStatus>('all')
+  const [searchKeyword, setSearchKeyword] = useState('')
+  const [sortBy, _setSortBy] = useState('createTime')
+  const [sortOrder, _setSortOrder] = useState<'asc' | 'desc'>('desc')
+
+  // 使用useInfiniteQuery进行无限滚动分页
+  const {
+    data,
+    isLoading,
+    error,
+    isFetchingNextPage,
+    fetchNextPage,
+    hasNextPage,
+    refetch
+  } = useInfiniteQuery({
+    queryKey: ['company-orders', activeStatus, searchKeyword, sortBy, sortOrder],
+    queryFn: async ({ pageParam = 1 }) => {
+      const userInfo = getEnterpriseUserInfo()
+      const companyId = userInfo?.companyId || 0
+
+      // 构建查询参数
+      const queryParams: any = {
+        page: pageParam,
+        pageSize: 20,
+        sortBy,
+        sortOrder: sortOrder.toUpperCase()
+      }
+
+      // 添加公司ID
+      if (companyId) {
+        queryParams.companyId = companyId
+      }
+
+      // 搜索关键词
+      if (searchKeyword) {
+        queryParams.orderName = searchKeyword
+      }
+
+      // 状态筛选(排除'all'状态)
+      if (activeStatus !== 'all') {
+        queryParams.orderStatus = activeStatus
+      }
+
+      // 调用企业专用订单API
+      const response = await enterpriseOrderClient['company-orders'].$get({
+        query: queryParams
+      })
+
+      if (response.ok) {
+        const data = await response.json()
+        // 转换API数据到UI格式 - 使用RPC推断的OrderData类型
+        const transformedOrders = (data.data || []).map((order: OrderData) => {
+          // 统一使用 orderPersons.length 确保与详情页数据一致
+          const orderPersonsCount = order.orderPersons?.length ?? 0
+          const talentName = order.orderPersons && order.orderPersons.length > 0
+            ? order.orderPersons[0].person?.name || '关联人才'
+            : '待关联'
+
+          return {
+            id: order.id,
+            orderNumber: order.orderName ? `ORDER-${order.id}` : `ORDER-${order.id}`,
+            name: order.orderName || '未命名订单',
+            createdAt: order.createTime ? new Date(order.createTime).toISOString().split('T')[0] : '未知日期',
+            status: order.orderStatus || 'draft',
+            statusLabel: getStatusLabel(order.orderStatus),
+            statusClass: getStatusClass(order.orderStatus),
+            actualPeople: orderPersonsCount,
+            startDate: order.expectedStartDate ? new Date(order.expectedStartDate).toISOString().split('T')[0] : '未设置',
+            endDate: order.actualEndDate ? new Date(order.actualEndDate).toISOString().split('T')[0] : '未结束',
+            talentName: talentName,
+            position: '未设置' // 岗位字段不存在于API
+            // 注意:统计数据现在由 OrderCard 组件通过 useOrderStats hook 单独获取
+          }
+        })
+
+        return {
+          data: transformedOrders,
+          meta: { page: pageParam, pageSize: 20, total: data.total || 0, totalPages: Math.ceil((data.total || 0) / 20) }
+        }
+      } else {
+        throw new Error(`获取订单列表失败: ${response.status}`)
+      }
+    },
+    getNextPageParam: (lastPage, allPages) => {
+      const totalPages = lastPage.meta?.totalPages || 1
+      const currentPage = lastPage.meta?.page || allPages.length
+      return currentPage < totalPages ? currentPage + 1 : undefined
+    },
+    initialPageParam: 1,
+    staleTime: 30 * 1000, // 30秒 - 缩短缓存时间以更快获取最新数据
+  })
+
+  const statusFilters = [
+    { key: 'all', label: '全部订单', activeClass: 'bg-blue-100 text-blue-800', inactiveClass: 'bg-gray-100 text-gray-800' },
+    { key: 'in_progress', label: '进行中', activeClass: 'bg-green-100 text-green-800', inactiveClass: 'bg-gray-100 text-gray-800' },
+    { key: 'completed', label: '已完成', activeClass: 'bg-blue-100 text-blue-800', inactiveClass: 'bg-gray-100 text-gray-800' },
+    { key: 'cancelled', label: '已取消', activeClass: 'bg-gray-100 text-gray-800', inactiveClass: 'bg-gray-100 text-gray-800' },
+  ]
+
+  // 页面显示时自动刷新(从详情页返回时触发)
+  useDidShow(() => {
+    console.debug('订单列表页显示,自动刷新数据')
+    // 使用 invalidateQueries 触发重新获取数据
+    // refetch() 会使用缓存数据,而 invalidateQueries 会强制重新获取
+    refetch()
+  })
+
+  // 使用页面级下拉刷新
+  usePullDownRefresh(async () => {
+    try {
+      await refetch()
+    } finally {
+      Taro.stopPullDownRefresh()
+    }
+  })
+
+
+
+  const handleStatusFilter = (status: OrderStatus) => {
+    setActiveStatus(status)
+    // queryKey变化会自动触发重新查询
+  }
+
+  // 触底加载更多
+  const handleScrollToLower = () => {
+    if (hasNextPage && !isFetchingNextPage) {
+      fetchNextPage()
+    }
+  }
+
+  const handleViewDetail = (orderId: number) => {
+    console.debug('查看订单详情:', orderId)
+    // 跳转到订单详情页
+    Taro.navigateTo({
+      url: `/pages/yongren/order/detail/index?id=${orderId}`
+    })
+  }
+
+
+  // 合并所有分页数据
+  const allOrders = data?.pages.flatMap(page => page.data) || []
+
+  return (
+    <YongrenTabBarLayout activeKey="order">
+      <ScrollView
+        className="h-[calc(100%-60px)] overflow-y-auto px-4 pb-4 pt-0"
+        scrollY
+        onScrollToLower={handleScrollToLower}
+      >
+        {/* 导航栏 */}
+        <Navbar
+          title="订单列表"
+          leftIcon=""
+          leftText=""
+          onClickLeft={() => {}}
+          backgroundColor="bg-white"
+          border={true}
+          fixed={true}
+          placeholder={true}
+        />
+
+        {/* 订单筛选 */}
+        <View className="p-4 border-b border-gray-200">
+          <View className="flex space-x-2 overflow-x-auto pb-2">
+            {statusFilters.map((filter) => (
+              <Text
+                key={filter.key}
+                className={`text-xs px-3 py-1 rounded-full whitespace-nowrap ${
+                  activeStatus === filter.key ? filter.activeClass : filter.inactiveClass
+                }`}
+                onClick={() => handleStatusFilter(filter.key as OrderStatus)}
+              >
+                {filter.label}
+              </Text>
+            ))}
+          </View>
+        </View>
+
+        {/* 搜索栏 */}
+        <View className="px-4 py-3">
+          <View className="flex items-center bg-gray-100 rounded-lg px-4 py-2">
+            <Text className="i-heroicons-magnifying-glass-20-solid text-gray-400 mr-2" />
+            <Input
+              type="text"
+              placeholder="按订单号、人才姓名搜索"
+              className="w-full bg-transparent outline-none text-sm"
+              value={searchKeyword}
+              onInput={(e) => setSearchKeyword(e.detail.value)}
+            />
+          </View>
+        </View>
+
+        {/* 订单列表区域 */}
+        <View className="p-4">
+          {/* 标题 */}
+          <View className="mb-4">
+            <Text className="font-semibold text-gray-700">订单列表</Text>
+          </View>
+
+          {/* 订单卡片列表 */}
+          {isLoading && (
+            <View className="flex justify-center items-center py-8">
+              <Text className="text-gray-500">加载中...</Text>
+            </View>
+          )}
+          {error && (
+            <View className="bg-red-50 border border-red-200 rounded-lg p-4 mb-4">
+              <Text className="text-red-700">{error.message || '获取订单列表失败'}</Text>
+            </View>
+          )}
+          {!isLoading && !error && (
+            <View className="space-y-4">
+              {allOrders.length === 0 ? (
+                <View className="flex justify-center items-center py-8">
+                  <Text className="text-gray-500">暂无订单数据</Text>
+                </View>
+              ) : (
+                allOrders.map((order) => (
+                  <OrderCard
+                    key={order.id}
+                    order={order}
+                    onViewDetail={handleViewDetail}
+                  />
+                ))
+              )}
+
+              {isFetchingNextPage && (
+                <View className="flex justify-center py-4">
+                  <Text className="i-heroicons-arrow-path-20-solid animate-spin w-6 h-6 text-blue-500 mr-2" />
+                  <Text className="text-sm text-gray-500">加载更多...</Text>
+                </View>
+              )}
+
+              {!hasNextPage && allOrders.length > 0 && (
+                <View className="text-center py-4">
+                  <Text className="text-sm text-gray-400">没有更多了</Text>
+                </View>
+              )}
+            </View>
+          )}
+        </View>
+      </ScrollView>
+    </YongrenTabBarLayout>
+  )
+}
+
 export default OrderList

+ 272 - 3
mini/src/pages/yongren/settings/index.tsx

@@ -1,3 +1,272 @@
-// 桥接文件:从 @d8d/yongren-settings-ui 包导入Settings页面
-import Settings from '@d8d/yongren-settings-ui/pages/Settings/Settings'
-export default Settings
+import React from 'react'
+import { View, Text, ScrollView } from '@tarojs/components'
+import Taro from '@tarojs/taro'
+import { useQuery } from '@tanstack/react-query'
+import { YongrenTabBarLayout } from '../../../components/YongrenTabBarLayout'
+import { Navbar } from '@d8d/mini-shared-ui-components/components/navbar'
+import { useAuth, useRequireAuth } from '../../../hooks'
+import { enterpriseCompanyClient } from '../../../api/enterpriseCompanyClient'
+
+// 延期功能列表(因史诗012系统设置API延期)
+const DEFERRED_FEATURES = ['账号信息', '安全设置', '消息通知']
+
+const Settings: React.FC = () => {
+  const { user, logout } = useAuth()
+  // 检查登录状态,未登录则重定向
+  useRequireAuth()
+
+  // 获取企业概览信息
+  const { data: companyOverview, isLoading } = useQuery({
+    queryKey: ['company-overview'],
+    queryFn: async () => {
+      const res = await enterpriseCompanyClient.overview.$get({} as never)
+      if (!res.ok) {
+        throw new Error('获取企业信息失败')
+      }
+      return await res.json()
+    }
+  })
+
+  // 处理功能项点击
+  const handleFeatureClick = (featureName: string) => {
+    if (DEFERRED_FEATURES.includes(featureName)) {
+      Taro.showModal({
+        title: '功能开发中',
+        content: `${featureName}功能正在开发中,预计下一版本上线。如需使用此功能,请在管理后台执行。`,
+        showCancel: false,
+        confirmText: '我知道了'
+      })
+      return
+    }
+
+    // 处理其他功能
+    switch (featureName) {
+      case '帮助中心':
+        Taro.showToast({ title: '即将开放', icon: 'none' })
+        break
+      case '用户协议':
+        Taro.showToast({ title: '即将开放', icon: 'none' })
+        break
+      case '隐私政策':
+        Taro.showToast({ title: '即将开放', icon: 'none' })
+        break
+      case '退出登录':
+        handleLogout()
+        break
+      default:
+        break
+    }
+  }
+
+  // 退出登录
+  const handleLogout = () => {
+    Taro.showModal({
+      title: '退出登录',
+      content: '确定要退出登录吗?',
+      success: (res) => {
+        if (res.confirm) {
+          // 使用 useAuth 的 logout 方法
+          logout()
+        }
+      }
+    })
+  }
+
+  if (isLoading) {
+    return (
+      <YongrenTabBarLayout activeKey="settings">
+        <ScrollView
+          className="h-[calc(100%-60px)] overflow-y-auto px-4 pb-4 pt-0"
+          scrollY
+        >
+          <Navbar
+            title="企业设置"
+            leftIcon=""
+            leftText=""
+            onClickLeft={() => {}}
+            backgroundColor="bg-white"
+            border={true}
+            fixed={true}
+            placeholder={true}
+          />
+          <View className="p-4">
+            <Text>加载中...</Text>
+          </View>
+        </ScrollView>
+      </YongrenTabBarLayout>
+    )
+  }
+
+  const companyData: Record<string, unknown> = companyOverview || {}
+
+  return (
+    <YongrenTabBarLayout activeKey="settings">
+      <ScrollView
+        className="h-[calc(100%-60px)] overflow-y-auto px-4 pb-4 pt-0"
+        scrollY
+      >
+        {/* 导航栏 */}
+        <Navbar
+          title="企业设置"
+          leftIcon=""
+          leftText=""
+          onClickLeft={() => {}}
+          backgroundColor="bg-white"
+          border={true}
+          fixed={true}
+          placeholder={true}
+        />
+
+        {/* 企业信息区域 */}
+        <View className="p-4 border-b border-gray-200">
+          <View className="flex items-center mb-4">
+            <View className="w-16 h-16 rounded-full bg-blue-100 flex items-center justify-center mr-3">
+              <View className="i-heroicons-building-office-20-solid text-blue-500 text-2xl" />
+            </View>
+            <View className="flex flex-col">
+              <Text className="font-semibold text-gray-800">
+                {user?.company?.companyName || user?.nickname || user?.username || '企业名称'}
+              </Text>
+              <Text className="text-sm text-gray-500">企业账号</Text>
+            </View>
+          </View>
+
+          {/* 统计卡片 */}
+          <View className="grid grid-cols-3 gap-3 text-center">
+            <View className="flex flex-col">
+              <Text className="text-xl font-bold text-gray-800">
+                {(companyData?.['在职人员数'] as number | undefined) ?? 0}
+              </Text>
+              <Text className="text-xs text-gray-500">在职人员</Text>
+            </View>
+            <View className="flex flex-col">
+              <Text className="text-xl font-bold text-gray-800">
+                {(companyData?.['进行中订单数'] as number | undefined) ?? 0}
+              </Text>
+              <Text className="text-xs text-gray-500">进行中订单</Text>
+            </View>
+            <View className="flex flex-col">
+              <Text className="text-xl font-bold text-gray-800">
+                {(companyData?.['累计订单数'] as number | undefined) ?? 0}
+              </Text>
+              <Text className="text-xs text-gray-500">累计订单</Text>
+            </View>
+          </View>
+        </View>
+
+        {/* 功能列表区域 */}
+        <View className="p-4">
+          {/* 第一组功能列表 */}
+          <View className="mb-6">
+            <View
+              className="flex items-center justify-between p-3 rounded-lg hover:bg-gray-50"
+              onClick={() => handleFeatureClick('账号信息')}
+            >
+              <View className="flex items-center">
+                <View className="w-10 h-10 rounded-full bg-blue-100 flex items-center justify-center mr-3">
+                  <View className="i-heroicons-user-20-solid text-blue-500 text-lg" />
+                </View>
+                <View className="flex flex-col">
+                  <Text className="text-gray-700">账号信息</Text>
+                  <Text className="text-xs text-gray-400">(后期优化)</Text>
+                </View>
+              </View>
+              <Text className="text-gray-400">›</Text>
+            </View>
+
+            <View
+              className="flex items-center justify-between p-3 rounded-lg hover:bg-gray-50"
+              onClick={() => handleFeatureClick('安全设置')}
+            >
+              <View className="flex items-center">
+                <View className="w-10 h-10 rounded-full bg-green-100 flex items-center justify-center mr-3">
+                  <View className="i-heroicons-shield-check-20-solid text-green-500 text-lg" />
+                </View>
+                <View className="flex flex-col">
+                  <Text className="text-gray-700">安全设置</Text>
+                  <Text className="text-xs text-gray-400">(后期优化)</Text>
+                </View>
+              </View>
+              <Text className="text-gray-400">›</Text>
+            </View>
+
+            <View
+              className="flex items-center justify-between p-3 rounded-lg hover:bg-gray-50"
+              onClick={() => handleFeatureClick('消息通知')}
+            >
+              <View className="flex items-center">
+                <View className="w-10 h-10 rounded-full bg-purple-100 flex items-center justify-center mr-3">
+                  <View className="i-heroicons-bell-20-solid text-purple-500 text-lg" />
+                </View>
+                <View className="flex flex-col">
+                  <Text className="text-gray-700">消息通知</Text>
+                  <Text className="text-xs text-gray-400">(后期优化)</Text>
+                </View>
+              </View>
+              <Text className="text-gray-400">›</Text>
+            </View>
+          </View>
+
+          {/* 第二组功能列表 */}
+          <View className="mb-6">
+            <View
+              className="flex items-center justify-between p-3 rounded-lg hover:bg-gray-50"
+              onClick={() => handleFeatureClick('帮助中心')}
+            >
+              <View className="flex items-center">
+                <View className="w-10 h-10 rounded-full bg-yellow-100 flex items-center justify-center mr-3">
+                  <View className="i-heroicons-question-mark-circle-20-solid text-yellow-500 text-lg" />
+                </View>
+                <Text className="text-gray-700">帮助中心</Text>
+              </View>
+              <Text className="text-gray-400">›</Text>
+            </View>
+
+            <View
+              className="flex items-center justify-between p-3 rounded-lg hover:bg-gray-50"
+              onClick={() => handleFeatureClick('用户协议')}
+            >
+              <View className="flex items-center">
+                <View className="w-10 h-10 rounded-full bg-indigo-100 flex items-center justify-center mr-3">
+                  <View className="i-heroicons-document-text-20-solid text-indigo-500 text-lg" />
+                </View>
+                <Text className="text-gray-700">用户协议</Text>
+              </View>
+              <Text className="text-gray-400">›</Text>
+            </View>
+
+            <View
+              className="flex items-center justify-between p-3 rounded-lg hover:bg-gray-50"
+              onClick={() => handleFeatureClick('隐私政策')}
+            >
+              <View className="flex items-center">
+                <View className="w-10 h-10 rounded-full bg-pink-100 flex items-center justify-center mr-3">
+                  <View className="i-heroicons-lock-closed-20-solid text-pink-500 text-lg" />
+                </View>
+                <Text className="text-gray-700">隐私政策</Text>
+              </View>
+              <Text className="text-gray-400">›</Text>
+            </View>
+          </View>
+
+          {/* 退出登录 */}
+          <View>
+            <View
+              className="flex items-center justify-between p-3 rounded-lg hover:bg-gray-50"
+              onClick={() => handleFeatureClick('退出登录')}
+            >
+              <View className="flex items-center">
+                <View className="w-10 h-10 rounded-full bg-red-100 flex items-center justify-center mr-3">
+                  <View className="i-heroicons-arrow-left-on-rectangle-20-solid text-red-500 text-lg" />
+                </View>
+                <Text className="text-gray-700">退出登录</Text>
+              </View>
+            </View>
+          </View>
+        </View>
+      </ScrollView>
+    </YongrenTabBarLayout>
+  )
+}
+
+export default Settings

+ 533 - 3
mini/src/pages/yongren/statistics/index.tsx

@@ -1,3 +1,533 @@
-// 桥接文件:从 @d8d/yongren-statistics-ui 包导入Statistics页面
-import Statistics from '@d8d/yongren-statistics-ui/pages/Statistics/Statistics'
-export default Statistics
+import React, { useState, useEffect, memo } from 'react'
+import { View, Text, ScrollView } from '@tarojs/components'
+import { useQuery, useQueryClient } from '@tanstack/react-query'
+import { YongrenTabBarLayout } from '../../../components/YongrenTabBarLayout'
+import { Navbar } from '@d8d/mini-shared-ui-components/components/navbar'
+import { ColumnChart } from '@d8d/mini-charts/components/ColumnChart'
+import { BarChart } from '@d8d/mini-charts/components/BarChart'
+import { PieChart } from '@d8d/mini-charts/components/PieChart'
+import { RingChart } from '@d8d/mini-charts/components/RingChart'
+import { enterpriseStatisticsClient } from '../../../api/enterpriseStatisticsClient'
+import type {
+  EmploymentCountResponse,
+  AverageSalaryResponse,
+  EmploymentRateResponse
+} from '../../../types/statisticsTypes'
+
+/**
+ * 类型守卫:检查响应是否为成功的数据响应
+ * 注意:不能在 JSX 中直接使用泛型函数,需要具体类型
+ */
+// 具体类型的类型守卫(用于 JSX)
+const isEmploymentCountSuccess = (data: any): data is EmploymentCountResponse => {
+  return data && !('code' in data && 'message' in data)
+}
+const isAverageSalarySuccess = (data: any): data is AverageSalaryResponse => {
+  return data && !('code' in data && 'message' in data)
+}
+const isEmploymentRateSuccess = (data: any): data is EmploymentRateResponse => {
+  return data && !('code' in data && 'message' in data)
+}
+
+/**
+ * 检查字符串是否为纯数字(用于识别需要转换的省份ID)
+ */
+const isNumericString = (str: string): boolean => {
+  return /^\d+$/.test(str)
+}
+
+/**
+ * 从 key 中提取省份名称(格式:省份-城市)
+ */
+const extractProvinceFromKey = (key: string): string => {
+  const parts = key.split('-')
+  return parts[0] || key
+}
+
+/**
+ * 数据转换工具:将API统计数据转换为柱状图格式
+ * 优先使用中文名称的 province,如果是数字ID则从 key 提取
+ */
+const convertToColumnData = (stats: any[]) => {
+  console.debug('[convertToColumnData] Input stats:', JSON.stringify(stats))
+  const result = {
+    categories: stats.map(item => {
+      // 优先使用 province,但如果 province 是纯数字,则从 key 提取省份名称
+      if (item.province && !isNumericString(item.province)) {
+        return item.province
+      }
+      // 如果 province 是数字或不存在,从 key 提取
+      return item.key ? extractProvinceFromKey(item.key) : item.province || item.key
+    }),
+    series: [{
+      name: '人数',
+      data: stats.map(item => item.value || 0)
+    }]
+  }
+  console.debug('[convertToColumnData] Output categories:', result.categories)
+  console.debug('[convertToColumnData] Output series:', result.series)
+  return result
+}
+
+/**
+ * 数据转换工具:将API统计数据转换为饼图格式
+ */
+const convertToPieData = (stats: any[]) =>
+  stats.map(item => ({
+    name: item.key,
+    data: item.value || 0
+  }))
+
+export interface StatisticsProps {
+  // 组件属性定义(目前为空)
+}
+
+const Statistics: React.FC<StatisticsProps> = () => {
+  // Query Client 用于刷新数据
+  const queryClient = useQueryClient()
+
+  // 状态:图表懒加载控制 - 哪些图表已经加载
+  const [loadedCharts, setLoadedCharts] = useState<Set<string>>(new Set(['disability', 'gender'])) // 默认加载前两个关键图表
+
+  // 类型守卫:检查响应是否包含统计数据
+  const isSuccessfulResponse = (data: any): data is { companyId: number; stats: any[]; total: number } => {
+    return data && typeof data === 'object' && 'stats' in data && Array.isArray(data.stats)
+  }
+
+  // 获取统计数据数组,如果响应不成功则返回空数组
+  const getStats = (data: any): any[] => {
+    return isSuccessfulResponse(data) ? data.stats : []
+  }
+
+  // 状态:下拉刷新控制
+  const [refresherTriggered, setRefresherTriggered] = useState(false)
+
+  // 图表懒加载效果:逐步加载其他图表
+  useEffect(() => {
+    const timer1 = setTimeout(() => {
+      setLoadedCharts(prev => new Set([...prev, 'age', 'household']))
+    }, 500) // 500ms后加载年龄和户籍分布
+
+    const timer2 = setTimeout(() => {
+      setLoadedCharts(prev => new Set([...prev, 'jobStatus', 'salary']))
+    }, 1000) // 1000ms后加载在职状态和薪资分布
+
+    return () => {
+      clearTimeout(timer1)
+      clearTimeout(timer2)
+    }
+  }, [])
+
+  // 下拉刷新处理函数:刷新所有统计数据
+  const handleRefresh = async () => {
+    console.log('🔄 [下拉刷新] 开始刷新所有统计数据...')
+    try {
+      console.log('🔄 [下拉刷新] 开始执行 invalidateQueries Promise.all...')
+      await Promise.all([
+        queryClient.invalidateQueries({ queryKey: ['statistics', 'employment-count'] }),
+        queryClient.invalidateQueries({ queryKey: ['statistics', 'average-salary'] }),
+        queryClient.invalidateQueries({ queryKey: ['statistics', 'employment-rate'] }),
+        queryClient.invalidateQueries({ queryKey: ['statistics', 'disability-type-distribution'] }),
+        queryClient.invalidateQueries({ queryKey: ['statistics', 'gender-distribution'] }),
+        queryClient.invalidateQueries({ queryKey: ['statistics', 'age-distribution'] }),
+        queryClient.invalidateQueries({ queryKey: ['statistics', 'household-distribution'] }),
+        queryClient.invalidateQueries({ queryKey: ['statistics', 'job-status-distribution'] }),
+        queryClient.invalidateQueries({ queryKey: ['statistics', 'salary-distribution'] }),
+      ])
+      console.log('🔄 [下拉刷新] invalidateQueries Promise.all 完成!')
+    } catch (error) {
+      console.error('🔄 [下拉刷新] 发生错误:', error)
+    } finally {
+      console.log('🔄 [下拉刷新] 结束刷新状态')
+      setRefresherTriggered(false)
+    }
+  }
+
+  // 获取在职人数统计(简化版:无查询参数)
+  const { data: employmentCountData, isLoading: isLoadingEmploymentCount } = useQuery({
+    queryKey: ['statistics', 'employment-count'],
+    queryFn: async () => {
+      const response = await enterpriseStatisticsClient['employment-count'].$get()
+      return await response.json()
+    },
+    staleTime: 5 * 60 * 1000, // 数据过期时间5分钟
+    gcTime: 10 * 60 * 1000 // 缓存时间10分钟
+  })
+
+  // 获取平均薪资统计(简化版:无查询参数)
+  const { data: averageSalaryData, isLoading: isLoadingAverageSalary } = useQuery({
+    queryKey: ['statistics', 'average-salary'],
+    queryFn: async () => {
+      const response = await enterpriseStatisticsClient['average-salary'].$get()
+      return await response.json()
+    },
+    staleTime: 5 * 60 * 1000,
+    gcTime: 10 * 60 * 1000
+  })
+
+  // 获取在职率统计(简化版:无查询参数)
+  const { data: employmentRateData, isLoading: isLoadingEmploymentRate } = useQuery({
+    queryKey: ['statistics', 'employment-rate'],
+    queryFn: async () => {
+      const response = await enterpriseStatisticsClient['employment-rate'].$get()
+      return await response.json()
+    },
+    staleTime: 5 * 60 * 1000,
+    gcTime: 10 * 60 * 1000
+  })
+
+  // 获取残疾类型分布数据(无查询参数)
+  const { data: disabilityData, isLoading: isLoadingDisability } = useQuery({
+    queryKey: ['statistics', 'disability-type-distribution'],
+    queryFn: async () => {
+      const response = await enterpriseStatisticsClient['disability-type-distribution'].$get()
+      return await response.json()
+    },
+    enabled: loadedCharts.has('disability'),
+    staleTime: 5 * 60 * 1000, // 数据过期时间5分钟
+    gcTime: 10 * 60 * 1000 // 缓存时间10分钟
+  })
+
+  // 获取性别分布数据(无查询参数)
+  const { data: genderData, isLoading: isLoadingGender } = useQuery({
+    queryKey: ['statistics', 'gender-distribution'],
+    queryFn: async () => {
+      const response = await enterpriseStatisticsClient['gender-distribution'].$get()
+      return await response.json()
+    },
+    enabled: loadedCharts.has('gender'),
+    staleTime: 5 * 60 * 1000, // 数据过期时间5分钟
+    gcTime: 10 * 60 * 1000 // 缓存时间10分钟
+  })
+
+  // 获取年龄分布数据(无查询参数)
+  const { data: ageData, isLoading: isLoadingAge } = useQuery({
+    queryKey: ['statistics', 'age-distribution'],
+    queryFn: async () => {
+      const response = await enterpriseStatisticsClient['age-distribution'].$get()
+      return await response.json()
+    },
+    enabled: loadedCharts.has('age'),
+    staleTime: 5 * 60 * 1000, // 数据过期时间5分钟
+    gcTime: 10 * 60 * 1000 // 缓存时间10分钟
+  })
+
+  // 获取户籍分布数据(无查询参数)
+  const { data: householdData, isLoading: isLoadingHousehold } = useQuery({
+    queryKey: ['statistics', 'household-distribution'],
+    queryFn: async () => {
+      const response = await enterpriseStatisticsClient['household-distribution'].$get()
+      return await response.json()
+    },
+    enabled: loadedCharts.has('household'),
+    staleTime: 5 * 60 * 1000, // 数据过期时间5分钟
+    gcTime: 10 * 60 * 1000 // 缓存时间10分钟
+  })
+
+  // 获取在职状态分布数据(无查询参数)
+  const { data: jobStatusData, isLoading: isLoadingJobStatus } = useQuery({
+    queryKey: ['statistics', 'job-status-distribution'],
+    queryFn: async () => {
+      const response = await enterpriseStatisticsClient['job-status-distribution'].$get()
+      return await response.json()
+    },
+    enabled: loadedCharts.has('jobStatus'),
+    staleTime: 5 * 60 * 1000, // 数据过期时间5分钟
+    gcTime: 10 * 60 * 1000 // 缓存时间10分钟
+  })
+
+  // 获取薪资分布数据(无查询参数)
+  const { data: salaryData, isLoading: isLoadingSalary } = useQuery({
+    queryKey: ['statistics', 'salary-distribution'],
+    queryFn: async () => {
+      const response = await enterpriseStatisticsClient['salary-distribution'].$get()
+      return await response.json()
+    },
+    enabled: loadedCharts.has('salary'),
+    staleTime: 5 * 60 * 1000, // 数据过期时间5分钟
+    gcTime: 10 * 60 * 1000 // 缓存时间10分钟
+  })
+
+  return (
+    <YongrenTabBarLayout activeKey="statistics">
+      <ScrollView
+        className="h-[calc(100%-60px)] overflow-y-auto px-4 pb-16 pt-0"
+        scrollY
+        refresherEnabled
+        refresherTriggered={refresherTriggered}
+        onRefresherRefresh={handleRefresh}
+        refresherBackground="#f5f5f5"
+      >
+        {/* 导航栏 */}
+        <Navbar
+          title="数据统计"
+          leftIcon=""
+          leftText=""
+          onClickLeft={() => {}}
+          backgroundColor="bg-white"
+          border={true}
+          fixed={true}
+          placeholder={true}
+        />
+
+        {/* 数据筛选区域 - 简化版:只保留标题 */}
+        <View className="mb-4 pt-4">
+          <View className="flex justify-between items-center mb-3">
+            <Text className="font-semibold text-gray-700">数据统计</Text>
+          </View>
+        </View>
+
+
+        {/* 统计卡片 - 简化版:只显示当前数值,无对比数据 */}
+        <View className="grid grid-cols-2 gap-3 mb-4">
+          {/* 在职人数卡片 */}
+          <View className="stat-card bg-white rounded-lg p-3 shadow-sm flex flex-col">
+            <Text className="text-sm text-gray-600 mb-2">在职人数</Text>
+            {isLoadingEmploymentCount ? (
+              <Text className="text-2xl font-bold text-gray-400">加载中...</Text>
+            ) : !isEmploymentCountSuccess(employmentCountData) ? (
+              <Text className="text-2xl font-bold text-gray-400">--</Text>
+            ) : (
+              <Text className="text-2xl font-bold text-gray-800">{employmentCountData.count ?? 0}</Text>
+            )}
+          </View>
+
+          {/* 平均薪资卡片 */}
+          <View className="stat-card bg-white rounded-lg p-3 shadow-sm flex flex-col">
+            <Text className="text-sm text-gray-600 mb-2">平均薪资</Text>
+            {isLoadingAverageSalary ? (
+              <Text className="text-2xl font-bold text-gray-400">加载中...</Text>
+            ) : !isAverageSalarySuccess(averageSalaryData) ? (
+              <Text className="text-2xl font-bold text-gray-400">--</Text>
+            ) : (
+              <Text className="text-2xl font-bold text-gray-800">¥{(averageSalaryData.average ?? 0).toLocaleString()}</Text>
+            )}
+          </View>
+
+          {/* 在职率卡片 */}
+          <View className="stat-card bg-white rounded-lg p-3 shadow-sm flex flex-col">
+            <Text className="text-sm text-gray-600 mb-2">在职率</Text>
+            {isLoadingEmploymentRate ? (
+              <Text className="text-2xl font-bold text-gray-400">加载中...</Text>
+            ) : !isEmploymentRateSuccess(employmentRateData) ? (
+              <Text className="text-2xl font-bold text-gray-400">--</Text>
+            ) : (
+              <Text className="text-2xl font-bold text-gray-800">{employmentRateData.rate ?? 0}%</Text>
+            )}
+          </View>
+        </View>
+
+        {/* 残疾类型分布 */}
+        <View className="card bg-white p-4 mb-4 rounded-lg shadow-sm flex flex-col">
+          <Text className="font-semibold text-gray-700">残疾类型分布</Text>
+          {isLoadingDisability ? (
+            <Text className="text-gray-500 text-center py-4">加载中...</Text>
+          ) : (() => {
+            const stats = getStats(disabilityData)
+            if (stats.length > 0) {
+              const chartData = convertToColumnData(stats)
+              return (
+                <View className="mt-3">
+                  <ColumnChart
+                    canvasId="disability-type-chart"
+                    width={650}
+                    height={300}
+                    categories={chartData.categories}
+                    series={chartData.series}
+                    config={{ color: ['#3b82f6'] }}
+                  />
+                </View>
+              )
+            } else {
+              return <Text className="text-gray-500 text-center py-4 mt-3">暂无数据</Text>
+            }
+          })()}
+        </View>
+
+        {/* 性别分布 */}
+        <View className="card bg-white p-4 mb-4 rounded-lg shadow-sm flex flex-col">
+          <Text className="font-semibold text-gray-700">性别分布</Text>
+          {isLoadingGender ? (
+            <Text className="text-gray-500 text-center py-4">加载中...</Text>
+          ) : (() => {
+              const genderStats = getStats(genderData)
+              return genderStats.length > 0 ? (
+                <View className="mt-3">
+                  <ColumnChart
+                    canvasId="gender-chart"
+                    width={650}
+                    height={300}
+                    categories={genderStats.map(s => s.key)}
+                    series={[{
+                      name: '人数',
+                      data: genderStats.map(s => s.value || 0)
+                    }]}
+                    config={{
+                      color: ['#3b82f6', '#ec4899'],
+                    }}
+                  />
+                </View>
+              ) : (
+                <Text className="text-gray-500 text-center py-4">暂无数据</Text>
+              )
+            })()}
+        </View>
+
+        {/* 年龄分布 */}
+        <View className="card bg-white p-4 mb-4 rounded-lg shadow-sm flex flex-col">
+          <Text className="font-semibold text-gray-700">年龄分布</Text>
+          {isLoadingAge ? (
+            <Text className="text-gray-500 text-center py-4">加载中...</Text>
+          ) : (() => {
+              const ageStats = getStats(ageData)
+              return ageStats.length > 0 ? (
+                <View className="mt-3">
+                  <PieChart
+                    canvasId="age-chart"
+                    width={650}
+                    height={300}
+                    series={convertToPieData(ageStats)}
+                    config={{
+                      color: ['#3b82f6', '#10b981', '#f59e0b', '#8b5cf6', '#ef4444'],
+                    }}
+                  />
+                </View>
+              ) : (
+                <Text className="text-gray-500 text-center py-4">暂无数据</Text>
+              )
+            })()}
+        </View>
+
+        {/* 户籍省份分布 */}
+        <View className="card bg-white p-4 mb-4 rounded-lg shadow-sm flex flex-col">
+          <Text className="font-semibold text-gray-700">户籍省份分布</Text>
+          {isLoadingHousehold ? (
+            <Text className="text-gray-500 text-center py-4">加载中...</Text>
+          ) : (() => {
+              const stats = getStats(householdData)
+              // 详细调试信息
+              console.log('📊 [户籍省份分布] 原始 API 数据:', JSON.stringify(householdData, null, 2))
+              console.log('📊 [户籍省份分布] stats 数组:', JSON.stringify(stats, null, 2))
+              if (stats.length > 0) {
+                const chartData = convertToColumnData(stats.slice(0, 6))
+                console.log('📊 [户籍省份分布] 转换后的 categories:', chartData.categories)
+                console.log('📊 [户籍省份分布] 转换后的 series:', JSON.stringify(chartData.series, null, 2))
+                // 检查每个 stat 项的 province 值
+                stats.forEach((item, index) => {
+                  console.log(`📊 [户籍省份分布] stat[${index}]:`, {
+                    key: item.key,
+                    province: item.province,
+                    isNumericProvince: item.province ? /^\d+$/.test(item.province) : 'N/A'
+                  })
+                })
+                return (
+                  <View className="mt-3">
+                    <BarChart
+                      canvasId="household-chart"
+                      width={650}
+                      height={280}
+                      categories={chartData.categories}
+                      series={chartData.series}
+                      config={{
+                        color: ['#3b82f6', '#10b981', '#f59e0b', '#8b5cf6', '#ef4444', '#ec4899'],
+                      }}
+                    />
+                  </View>
+                )
+              } else {
+                return <Text className="text-gray-500 text-center py-4 mt-3">暂无数据</Text>
+              }
+            })()}
+        </View>
+
+        {/* 在职状态统计 */}
+        <View className="card bg-white p-4 mb-4 rounded-lg shadow-sm flex flex-col">
+          <Text className="font-semibold text-gray-700">在职状态统计</Text>
+          {isLoadingJobStatus ? (
+            <Text className="text-gray-500 text-center py-4">加载中...</Text>
+          ) : (() => {
+              const stats = getStats(jobStatusData)
+              if (stats.length > 0) {
+                return (
+                  <View className="mt-3">
+                    <RingChart
+                      canvasId="job-status-chart"
+                      width={650}
+                      height={300}
+                      series={convertToPieData(stats)}
+                      config={{
+                        color: ['#3b82f6', '#f59e0b', '#ef4444', '#10b981'],
+                        legend: {
+                          show: true,
+                          position: "right",
+                          lineHeight: 25
+                        },
+                      }}
+                    />
+                  </View>
+                )
+              } else {
+                return <Text className="text-gray-500 text-center py-4">暂无数据</Text>
+              }
+            })()}
+        </View>
+
+        {/* 薪资分布 */}
+        <View className="card bg-white p-4 rounded-lg shadow-sm flex flex-col">
+          <Text className="font-semibold text-gray-700">薪资分布</Text>
+          {isLoadingSalary ? (
+            <Text className="text-gray-500 text-center py-4">加载中...</Text>
+          ) : (() => {
+              const stats = getStats(salaryData)
+              if (stats.length > 0) {
+                const chartData = convertToColumnData(stats)
+                return (
+                  <View className="mt-3">
+                    <BarChart
+                      canvasId="salary-chart"
+                      width={650}
+                      height={280}
+                      categories={chartData.categories}
+                      series={chartData.series}
+                      config={{
+                        color: ['#3b82f6', '#10b981', '#f59e0b', '#8b5cf6', '#ef4444'],
+                      }}
+                    />
+                  </View>
+                )
+              } else {
+                return <Text className="text-gray-500 text-center py-4">暂无数据</Text>
+              }
+            })()}
+        </View>
+
+        {/* PieChartFCExample 测试组件 */}
+        {/* <View className="card bg-white p-4 mb-4 rounded-lg shadow-sm flex flex-col">
+          <Text className="font-semibold text-gray-700">饼图示例测试 (PieChartFCExample)</Text>
+          <View className="mt-3">
+            <PieChartFCExample
+              canvasId="pie-chart-fc-example-test"
+              width={650}
+              height={400}
+            />
+          </View>
+        </View> */}
+
+        {/* RingChartFCExample 测试组件 */}
+        {/* <View className="card bg-white p-4 mb-4 rounded-lg shadow-sm flex flex-col">
+          <Text className="font-semibold text-gray-700">环形图示例测试 (RingChartFCExample)</Text>
+          <View className="mt-3">
+            <RingChartFCExample
+              canvasId="ring-chart-fc-example-test"
+              width={650}
+              height={400}
+            />
+          </View>
+        </View> */}
+
+      </ScrollView>
+    </YongrenTabBarLayout>
+  )
+}
+
+export default memo(Statistics)

+ 78 - 0
mini/src/pages/yongren/talent/detail/TalentDetail.css

@@ -0,0 +1,78 @@
+/* 人才详情页样式 */
+
+/* 渐变背景 */
+.gradient-bg {
+  background: linear-gradient(135deg, #3b82f6, #8b5cf6);
+}
+
+/* 头像颜色类 */
+.name-avatar.blue {
+  background: linear-gradient(135deg, #3b82f6, #1d4ed8);
+}
+.name-avatar.green {
+  background: linear-gradient(135deg, #10b981, #059669);
+}
+.name-avatar.purple {
+  background: linear-gradient(135deg, #8b5cf6, #7c3aed);
+}
+.name-avatar.orange {
+  background: linear-gradient(135deg, #f59e0b, #d97706);
+}
+.name-avatar.red {
+  background: linear-gradient(135deg, #ef4444, #dc2626);
+}
+.name-avatar.teal {
+  background: linear-gradient(135deg, #14b8a6, #0d9488);
+}
+
+/* 卡片样式 */
+.card {
+  border-radius: 12px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+}
+
+/* 加载动画 */
+@keyframes pulse-bg {
+  0%, 100% {
+    opacity: 1;
+  }
+  50% {
+    opacity: 0.5;
+  }
+}
+
+.animate-pulse {
+  animation: pulse-bg 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+}
+
+/* 文件项样式 */
+.file-item {
+  transition: background-color 0.2s ease;
+}
+
+.file-item:active {
+  background-color: rgb(243 244 246);
+}
+
+/* 状态标签 */
+.status-badge {
+  font-size: 12px;
+  padding: 2px 8px;
+  border-radius: 9999px;
+  display: inline-block;
+}
+
+.status-badge.in-service {
+  background-color: rgb(220 252 231);
+  color: rgb(21 128 61);
+}
+
+.status-badge.pending {
+  background-color: rgb(254 249 195);
+  color: rgb(133 77 14);
+}
+
+.status-badge.left {
+  background-color: rgb(243 244 246);
+  color: rgb(55 65 81);
+}

+ 835 - 3
mini/src/pages/yongren/talent/detail/index.tsx

@@ -1,5 +1,837 @@
-// 桥接文件:从 @d8d/yongren-talent-management-ui 包导入TalentDetail页面
-import TalentDetail from '@d8d/yongren-talent-management-ui/pages/TalentDetail/TalentDetail'
-import '@d8d/yongren-talent-management-ui/pages/TalentDetail/TalentDetail.css'
+import React, { useEffect, useState } from 'react'
+import { View, Text, ScrollView, Video } from '@tarojs/components'
+import Taro from '@tarojs/taro'
+import { useQuery } from '@tanstack/react-query'
+import { PageContainer } from '@d8d/mini-shared-ui-components/components/page-container'
+import { Navbar } from '@d8d/mini-shared-ui-components/components/navbar'
+import { enterpriseDisabilityClient } from '../../../api'
+import { useRequireAuth } from '../../../../hooks'
+// 直接从后端模块导入类型定义
+import type {
+  WorkHistoryItem,
+  SalaryHistoryItem,
+  SalaryHistoryResponse,
+  CreditInfoItem,
+  PersonVideoItem
+} from '@d8d/allin-disability-module/routes'
+
+export interface TalentDetailProps {
+  // 组件属性定义(目前为空)
+}
+
+
+const TalentDetail: React.FC<TalentDetailProps> = () => {
+  const { isLoggedIn } = useRequireAuth()
+  const router = Taro.useRouter()
+  const talentId = router.params.id ? parseInt(router.params.id) : 0
+
+  // 视频播放器状态
+  const [showVideoPlayer, setShowVideoPlayer] = useState(false)
+  const [currentVideoUrl, setCurrentVideoUrl] = useState<string>('')
+
+  // 获取人才基本信息
+  const { data: talentDetail, isLoading: talentLoading, error: talentError } = useQuery({
+    queryKey: ['talentDetail', talentId],
+    queryFn: async () => {
+      if (!talentId) throw new Error('无效的人才ID')
+      const response = await enterpriseDisabilityClient[':id'].$get({
+        param: { id: talentId }
+      })
+      if (response.status !== 200) {
+        throw new Error('获取人才详情失败')
+      }
+      const data = await response.json()
+      // 添加兼容字段映射
+      return {
+        ...data,
+        id: data.personId,  // 映射id字段
+        status: data.jobStatus,  // 映射status字段
+        // 计算年龄
+        age: data.birthDate ? Math.floor((Date.now() - new Date(data.birthDate).getTime()) / (1000 * 60 * 60 * 24 * 365.25)) : undefined,
+        // 兼容字段映射 - 使用实际存在的字段
+        salary: 0, // 薪资字段不存在,使用默认值0
+        joinDate: undefined, // 入职日期字段不存在
+        disabilityId: data.disabilityId || '未提供', // 残疾证号
+        idAddress: data.idAddress || '未提供', // 身份证地址
+        phone: data.phone || '未提供'
+      }
+    },
+    enabled: isLoggedIn && talentId > 0
+  })
+
+  // 获取工作信息 - 使用企业专用工作历史API
+  const { data: workInfo, isLoading: workLoading } = useQuery({
+    queryKey: ['workInfo', talentId],
+    queryFn: async () => {
+      if (!talentId) throw new Error('无效的人才ID')
+      // 使用企业专用工作历史API:/api/v1/yongren/disability-person/{id}/work-history
+      const response = await enterpriseDisabilityClient[':id']['work-history'].$get({
+        param: { id: talentId }
+      })
+      if (response.status !== 200) {
+        // 可能没有工作信息,返回空对象
+        return {}
+      }
+      const data = await response.json()
+      // 企业专用工作历史API返回的是工作历史列表
+      const workHistory = data?.工作历史 || []
+      if (workHistory.length === 0) {
+        return {}
+      }
+      // 优先选择工作状态为"在职"的记录,如果没有则取最新的一条
+      const currentWork = workHistory.find((item: WorkHistoryItem) => item.工作状态 === '在职') || workHistory[0]
+      return {
+        id: currentWork.订单ID || talentId,
+        orderId: currentWork.订单ID,
+        position: undefined, // 企业专用API没有岗位类型字段
+        department: undefined, // 企业专用API没有部门字段
+        startDate: currentWork.入职日期 || undefined,
+        endDate: currentWork.离职日期 || undefined,
+        status: currentWork.工作状态,
+        companyId: undefined // 企业专用API没有公司ID字段
+      }
+    },
+    enabled: isLoggedIn && talentId > 0
+  })
+
+  // 获取完整工作历史 - 用于时间线展示
+  const { data: workHistoryFull, isLoading: workHistoryLoading } = useQuery({
+    queryKey: ['workHistoryFull', talentId],
+    queryFn: async () => {
+      if (!talentId) throw new Error('无效的人才ID')
+      // 使用企业专用工作历史API:/api/v1/yongren/disability-person/{id}/work-history
+      const response = await enterpriseDisabilityClient[':id']['work-history'].$get({
+        param: { id: talentId }
+      })
+      if (response.status !== 200) {
+        // 可能没有工作信息,返回空数组
+        return [] as WorkHistoryItem[]
+      }
+      const data = await response.json()
+      // 企业专用工作历史API返回的是工作历史列表
+      const workHistory = data?.工作历史 || []
+      // 按入职日期降序排序(最新的在前)
+      return workHistory.sort((a: WorkHistoryItem, b: WorkHistoryItem) => {
+        const dateA = a.入职日期 ? new Date(a.入职日期).getTime() : 0
+        const dateB = b.入职日期 ? new Date(b.入职日期).getTime() : 0
+        return dateB - dateA
+      })
+    },
+    enabled: isLoggedIn && talentId > 0
+  })
+
+  // 获取薪资信息 - 使用企业专用薪资历史API
+  const { data: salaryInfo, isLoading: salaryLoading } = useQuery({
+    queryKey: ['salaryInfo', talentId],
+    queryFn: async () => {
+      if (!talentId) throw new Error('无效的人才ID')
+      // 使用企业专用薪资历史API:/api/v1/yongren/disability-person/{id}/salary-history
+      const response = await enterpriseDisabilityClient[':id']['salary-history'].$get({
+        param: { id: talentId }
+      })
+      if (response.status !== 200) {
+        // 可能没有薪资信息,返回空对象
+        return {}
+      }
+      const data = await response.json()
+      // 企业专用薪资历史API返回结构:{ 薪资历史: [...] }
+      const salaryHistory = data?.薪资历史 || []
+      if (salaryHistory.length === 0) {
+        return {}
+      }
+      // 取最新的一条薪资记录(按月份降序)
+      const latestSalary = salaryHistory[0]
+      return {
+        id: talentId,
+        personId: talentId,
+        amount: latestSalary.实发工资 || latestSalary.基本工资,
+        paymentDate: latestSalary.月份 || undefined,
+        type: '月薪', // 默认类型
+        period: '月度' // 默认周期
+      }
+    },
+    enabled: isLoggedIn && talentId > 0
+  })
+
+  // 获取薪资历史记录 - 使用企业专用薪资历史API
+  const { data: salaryHistory, isLoading: historyLoading } = useQuery({
+    queryKey: ['salaryHistory', talentId],
+    queryFn: async () => {
+      if (!talentId) throw new Error('无效的人才ID')
+      // 使用企业专用薪资历史API:/api/v1/yongren/disability-person/{id}/salary-history
+      const response = await enterpriseDisabilityClient[':id']['salary-history'].$get({
+        param: { id: talentId }
+      })
+      if (response.status !== 200) {
+        return []
+      }
+      const data = await response.json() as SalaryHistoryResponse
+      // 企业专用薪资历史API返回结构:{ 薪资历史: [...] }
+      const salaryHistoryData = data?.薪资历史 || []
+      // 转换为SalaryData数组
+      return salaryHistoryData.map((item: SalaryHistoryItem, index: number) => ({
+        id: index + 1,
+        personId: talentId,
+        amount: item.实发工资 || item.基本工资,
+        paymentDate: item.月份 || undefined,
+        type: '月薪', // 默认类型
+        period: '月度' // 默认周期
+      }))
+    },
+    enabled: isLoggedIn && talentId > 0
+  })
+
+  // 获取个人征信文件 - 使用企业专用征信信息API
+  const { data: creditFiles, isLoading: filesLoading } = useQuery({
+    queryKey: ['creditFiles', talentId],
+    queryFn: async () => {
+      if (!talentId) throw new Error('无效的人才ID')
+      // 使用企业专用征信信息API:/api/v1/yongren/disability-person/{id}/credit-info
+      const response = await enterpriseDisabilityClient[':id']['credit-info'].$get({
+        param: { id: talentId }
+      })
+      if (response.status !== 200) {
+        return []
+      }
+      const data = await response.json()
+      // 企业专用征信信息API返回结构:{ 征信信息: [...] }
+      const creditInfoList = data?.征信信息 || []
+      // 转换为FileData数组
+      return creditInfoList.map((item: CreditInfoItem) => ({
+        id: item.文件ID || '',
+        name: item.银行卡号 ? `银行卡 ${item.银行卡号}` : item.持卡人姓名 ? `征信文件 - ${item.持卡人姓名}` : '征信文件',
+        url: item.文件URL || undefined,
+        size: undefined, // 征信信息API不返回文件大小
+        type: item.文件类型 || undefined,
+        createdAt: item.上传时间 || undefined
+      }))
+    },
+    enabled: isLoggedIn && talentId > 0
+  })
+
+  // 获取工作视频 - 使用企业专用视频API
+  const { data: videos, isLoading: videosLoading } = useQuery({
+    queryKey: ['videos', talentId],
+    queryFn: async () => {
+      if (!talentId) throw new Error('无效的人才ID')
+      // 使用企业专用视频API:/api/v1/yongren/disability-person/{id}/videos
+      const response = await enterpriseDisabilityClient[':id']['videos'].$get({
+        param: { id: talentId }
+      })
+      if (response.status !== 200) {
+        // 可能没有视频,返回空数组
+        return []
+      }
+      const data = await response.json()
+      // 企业专用视频API返回结构:{ 视频列表: [...] }
+      const videoList = data?.视频列表 || []
+      // 视频类型映射:英文 -> 中文
+      const videoCategoryMap: Record<string, '个税视频' | '工资视频' | '工作视频' | '其他'> = {
+        'tax_video': '个税视频',
+        'salary_video': '工资视频',
+        'work_video': '工作视频',
+        'checkin_video': '其他'
+      }
+      // 转换为VideoData数组
+      return videoList.map((item: PersonVideoItem) => ({
+        id: item.文件ID || '',
+        title: item.文件名 || item.视频类型 || '未命名视频',
+        url: item.文件URL || undefined,
+        type: item.文件类型 || undefined,
+        uploadTime: item.上传时间 || undefined,
+        size: undefined, // 文件大小字段不存在
+        category: videoCategoryMap[item.视频类型] || '其他'
+      }))
+    },
+    enabled: isLoggedIn && talentId > 0
+  })
+
+  // 页面加载时设置标题
+  useEffect(() => {
+    Taro.setNavigationBarTitle({
+      title: '人才详情'
+    })
+  }, [])
+
+  const isLoading = talentLoading || workLoading || salaryLoading || filesLoading || historyLoading || workHistoryLoading || videosLoading
+  const hasError = talentError
+
+  // 获取头像颜色
+  const getAvatarColor = (id: number) => {
+    const colors = ['blue', 'green', 'purple', 'orange', 'red', 'teal']
+    const index = id % colors.length
+    return colors[index]
+  }
+
+  // 格式化日期
+  const formatDate = (dateStr?: string) => {
+    if (!dateStr) return '未指定'
+    return dateStr.split('T')[0]
+  }
+
+  // 格式化金额
+  const formatCurrency = (amount?: number) => {
+    if (!amount) return '¥0'
+    return `¥${amount.toLocaleString()}`
+  }
+
+  // 判断文件类型的辅助函数
+  const getFileType = (url: string): 'image' | 'video' | 'other' => {
+    // 从 URL 中提取文件扩展名
+    const urlLower = url.toLowerCase()
+    const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.svg']
+    const videoExtensions = ['.mp4', '.mov', '.avi', '.mkv', '.wmv', '.flv', '.m4v']
+
+    for (const ext of imageExtensions) {
+      if (urlLower.includes(ext)) {
+        return 'image'
+      }
+    }
+
+    for (const ext of videoExtensions) {
+      if (urlLower.includes(ext)) {
+        return 'video'
+      }
+    }
+
+    return 'other'
+  }
+
+  // 打开文件URL的函数
+  const handleOpenFile = (url?: string, _fileName?: string) => {
+    if (!url) {
+      Taro.showToast({
+        title: '文件地址不存在',
+        icon: 'none'
+      })
+      return
+    }
+
+    try {
+      // 先判断文件类型,视频使用弹窗播放(所有环境统一)
+      const fileType = getFileType(url)
+
+      switch (fileType) {
+        case 'image':
+          // 图片文件使用 Taro.previewImage 预览
+          Taro.previewImage({
+            current: url,
+            urls: [url]
+          })
+          break
+
+        case 'video':
+          // 视频文件使用弹窗播放器直接播放
+          setCurrentVideoUrl(url)
+          setShowVideoPlayer(true)
+          break
+
+        case 'other':
+          // H5 环境直接打开文件
+          if (process.env.TARO_ENV === 'h5') {
+            window.open(url, '_blank')
+            break
+          }
+          // 小程序环境使用 Taro.openDocument
+          Taro.downloadFile({
+            url: url,
+            success: (res) => {
+              if (res.statusCode === 200) {
+                // 从 URL 或文件名中提取文件类型
+                const urlLower = url.toLowerCase()
+                let fileType = ''
+                const docTypes = ['.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.txt']
+                for (const type of docTypes) {
+                  if (urlLower.includes(type)) {
+                    fileType = type.replace('.', '')
+                    break
+                  }
+                }
+
+                Taro.openDocument({
+                  filePath: res.tempFilePath,
+                  fileType: (fileType || undefined) as Parameters<typeof Taro.openDocument>[0]['fileType'],
+                  showMenu: true,
+                  fail: (_err) => {
+                    Taro.showToast({
+                      title: '文件打开失败',
+                      icon: 'none'
+                    })
+                  }
+                })
+              }
+            },
+            fail: (_err) => {
+              Taro.showToast({
+                title: '文件下载失败',
+                icon: 'none'
+              })
+            }
+          })
+          break
+      }
+    } catch (_error) {
+      Taro.showToast({
+        title: '打开文件出错',
+        icon: 'none'
+      })
+    }
+  }
+
+  return (
+      <PageContainer padding={false} className="pb-0">
+        <ScrollView
+          className="h-[calc(100vh-120px)] overflow-y-auto px-4 pb-4 pt-0"
+          scrollY
+        >
+          {/* 导航栏 */}
+          <Navbar
+            title="人才详情"
+            leftIcon="i-heroicons-chevron-left-20-solid"
+            leftText="返回"
+            backgroundColor="bg-white"
+            border={true}
+            fixed={true}
+            placeholder={true}
+          />
+          {isLoading ? (
+            // 加载状态
+            <View className="p-4 space-y-4">
+              {[1, 2, 3].map((i) => (
+                <View key={i} className="bg-white p-4 rounded-lg animate-pulse">
+                  <View className="h-6 bg-gray-200 rounded w-1/3 mb-3" />
+                  <View className="space-y-2">
+                    <View className="h-4 bg-gray-200 rounded w-full" />
+                    <View className="h-4 bg-gray-200 rounded w-2/3" />
+                  </View>
+                </View>
+              ))}
+            </View>
+          ) : hasError ? (
+            // 错误状态
+            <View className="p-4">
+              <View className="bg-white p-4 rounded-lg text-center">
+                <Text className="text-red-500 text-sm">加载失败: {(talentError as Error).message}</Text>
+                <Text className="text-gray-400 text-xs mt-1">请返回重试</Text>
+              </View>
+            </View>
+          ) : talentDetail ? (
+            <>
+              {/* 顶部信息区域 - 对照原型第576-605行 */}
+              <View className="gradient-bg text-white p-5">
+                <View className="flex justify-between items-start">
+                  <View className="flex items-center">
+                    <View className={`name-avatar ${getAvatarColor(talentDetail.id)} w-16 h-16 rounded-full border-2 border-white mr-4 flex items-center justify-center`}>
+                      <Text className="text-white text-2xl font-bold">
+                        {talentDetail.name.charAt(0)}
+                      </Text>
+                    </View>
+                    <View>
+                      <Text className="text-xl font-bold">{talentDetail.name}</Text>
+                      <Text className="text-sm opacity-80">
+                        {talentDetail.disabilityType || '未指定'} · {talentDetail.disabilityLevel || '未分级'} · {talentDetail.status || '未知'}
+                      </Text>
+                    </View>
+                  </View>
+                  <View className="bg-white/20 rounded-full p-2">
+                    <Text className="i-heroicons-ellipsis-vertical-20-solid text-white" />
+                  </View>
+                </View>
+                <View className="mt-4 flex justify-between">
+                  <View className="text-center flex flex-col">
+                    <Text className="text-2xl font-bold">{formatCurrency(salaryInfo?.amount || 0)}</Text>
+                    <Text className="text-xs opacity-80">当前薪资</Text>
+                  </View>
+                  <View className="text-center flex flex-col">
+                    <Text className="text-2xl font-bold">
+                      {workInfo?.startDate ? Math.floor((Date.now() - new Date(workInfo.startDate).getTime()) / (1000 * 60 * 60 * 24)) : 0}
+                    </Text>
+                    <Text className="text-xs opacity-80">在职天数</Text>
+                  </View>
+                  <View className="text-center flex flex-col">
+                    <Text className="text-2xl font-bold">98%</Text>
+                    <Text className="text-xs opacity-80">出勤率</Text>
+                  </View>
+                </View>
+              </View>
+
+              {/* 详细信息区域 - 对照原型第608-864行 */}
+              <View className="p-4">
+                {/* 基本信息卡片 */}
+                <View className="card bg-white p-4 mb-4">
+                  <Text className="font-semibold text-gray-700 mb-3">基本信息</Text>
+                  <View className="grid grid-cols-2 gap-3 text-sm">
+                    <View className="flex flex-col">
+                      <Text className="text-gray-500">性别</Text>
+                      <Text className="text-gray-800">{talentDetail.gender || '未指定'}</Text>
+                    </View>
+                    <View className="flex flex-col">
+                      <Text className="text-gray-500">年龄</Text>
+                      <Text className="text-gray-800">{talentDetail.age || '未知'}岁</Text>
+                    </View>
+                    <View className="flex flex-col">
+                      <Text className="text-gray-500">身份证号</Text>
+                      <Text className="text-gray-800">{talentDetail.idCard || '未提供'}</Text>
+                    </View>
+                    <View className="flex flex-col">
+                      <Text className="text-gray-500">残疾证号</Text>
+                      <Text className="text-gray-800">{talentDetail.disabilityId || '未提供'}</Text>
+                    </View>
+                    <View className="col-span-2 flex flex-col">
+                      <Text className="text-gray-500">联系地址</Text>
+                      <Text className="text-gray-800">{talentDetail.idAddress || '未提供'}</Text>
+                    </View>
+                  </View>
+                </View>
+
+                {/* 工作信息卡片 */}
+                <View className="card bg-white p-4 mb-4">
+                  <Text className="font-semibold text-gray-700 mb-3">工作信息</Text>
+                  <View className="space-y-3 text-sm">
+                    <View className="flex justify-between">
+                      <Text className="text-gray-500">入职日期</Text>
+                      <Text className="text-gray-800">{formatDate(workInfo?.startDate)}</Text>
+                    </View>
+                    <View className="flex justify-between">
+                      <Text className="text-gray-500">工作状态</Text>
+                      <Text className={`text-xs px-2 py-1 rounded-full ${
+                        talentDetail.status === '在职'
+                          ? 'bg-green-100 text-green-800'
+                          : talentDetail.status === '待入职'
+                          ? 'bg-yellow-100 text-yellow-800'
+                          : talentDetail.status === '离职'
+                          ? 'bg-red-100 text-red-800'
+                          : 'bg-gray-100 text-gray-800'
+                      }`}>
+                        {talentDetail.status || '未知'}
+                      </Text>
+                    </View>
+                    <View className="flex justify-between">
+                      <Text className="text-gray-500">所属订单</Text>
+                      <Text className="text-gray-800">{workInfo?.orderId ? `订单 #${workInfo.orderId}` : '无'}</Text>
+                    </View>
+                    <View className="flex justify-between">
+                      <Text className="text-gray-500">岗位类型</Text>
+                      <Text className="text-gray-800">{workInfo?.position || '未指定'}</Text>
+                    </View>
+                  </View>
+                </View>
+
+                {/* 薪资信息卡片 - 按原型简化:当前月薪和薪资历史按钮 */}
+                <View className="card bg-white p-4 mb-4">
+                  <Text className="font-semibold text-gray-700 mb-3">薪资信息</Text>
+                  <View className="flex justify-between items-center">
+                    <View className="flex flex-col">
+                      <Text className="text-gray-500">当前月薪</Text>
+                      <Text className="text-2xl font-bold text-blue-600">{formatCurrency(salaryInfo?.amount || 0)}</Text>
+                    </View>
+                    <View
+                      className="text-blue-500 text-sm flex items-center"
+                      onClick={() => {
+                        // 滚动到薪资历史记录卡片
+                        // 可以在薪资历史记录卡片添加data-testid或id来定位
+                        Taro.showToast({
+                          title: '查看薪资历史',
+                          icon: 'none'
+                        });
+                      }}
+                    >
+                      <Text className="i-heroicons-clock-20-solid mr-1" />
+                      <Text>薪资历史</Text>
+                    </View>
+                  </View>
+                </View>
+
+                {/* 历史工作内容时间线卡片 - 原型第673-739行 */}
+                <View className="card bg-white p-4 mb-4">
+                  <Text className="font-semibold text-gray-700 mb-3">历史工作内容</Text>
+                  {workHistoryLoading ? (
+                    <View className="space-y-4">
+                      {[1, 2, 3].map((i) => (
+                        <View key={i} className="flex items-start animate-pulse">
+                          <View className="w-3 h-3 bg-gray-200 rounded-full mt-1 mr-3" />
+                          <View className="flex-1">
+                            <View className="h-4 bg-gray-200 rounded w-1/3 mb-2" />
+                            <View className="h-3 bg-gray-200 rounded w-2/3" />
+                          </View>
+                        </View>
+                      ))}
+                    </View>
+                  ) : workHistoryFull && workHistoryFull.length > 0 ? (
+                    <View className="space-y-6">
+                      {workHistoryFull.map((work: WorkHistoryItem, index: number) => {
+                        const isCurrent = work.工作状态 === '在职' || index === 0
+                        const startDate = work.入职日期 ? formatDate(work.入职日期) : '未指定'
+                        const endDate = work.离职日期 ? formatDate(work.离职日期) : '至今'
+                        const period = `${startDate} - ${endDate}`
+                        const salary = work.个人薪资 ? `¥${work.个人薪资.toLocaleString()}` : '未指定'
+
+                        return (
+                          <View key={work.订单ID || index} className="flex">
+                            <View className="flex flex-col items-center mr-3">
+                              <View className={`w-3 h-3 rounded-full ${isCurrent ? 'bg-blue-500' : 'bg-gray-400'}`} />
+                              {index !== workHistoryFull.length - 1 && (
+                                <View className={`w-0.5 h-full mt-1 ${isCurrent ? 'bg-blue-200' : 'bg-gray-200'}`} />
+                              )}
+                            </View>
+                            <View className="flex-1">
+                              {/* 左右分栏布局 - 原型第684-693行 */}
+                              <View className="flex justify-between items-start">
+                                {/* 左侧:公司名称和岗位 */}
+                                <View className="flex flex-col">
+                                  <Text className="font-medium text-gray-800">
+                                    {work.订单名称 || `订单 #${work.订单ID}` || '未命名工作'}
+                                  </Text>
+                                  <Text className="text-sm text-gray-600">
+                                    {work.工作状态 || '未指定'}
+                                  </Text>
+                                </View>
+                                {/* 右侧:薪资和时间段 */}
+                                <View className="flex flex-col items-end">
+                                  <Text className={`text-sm font-medium ${isCurrent ? 'text-blue-600' : 'text-gray-600'}`}>
+                                    {salary}
+                                  </Text>
+                                  <Text className="text-xs text-gray-500">{period}</Text>
+                                </View>
+                              </View>
+                              {/* 工作描述(API无单独字段,如果工作状态不是简单的状态值则显示为描述) - 原型第694行 */}
+                              {work.工作状态 && !['在职', '离职', '待入职'].includes(work.工作状态) && (
+                                <Text className="text-sm text-gray-600 mt-2">
+                                  {work.工作状态}
+                                </Text>
+                              )}
+                            </View>
+                          </View>
+                        )
+                      })}
+                    </View>
+                  ) : (
+                    <View className="text-center py-4">
+                      <Text className="text-gray-400 text-sm">暂无工作历史记录</Text>
+                    </View>
+                  )}
+                </View>
+
+                {/* 薪资历史记录卡片 */}
+                <View className="card bg-white p-4 mb-4">
+                  <Text className="font-semibold text-gray-700 mb-3">薪资历史记录</Text>
+                  {historyLoading ? (
+                    <View className="space-y-2">
+                      {[1, 2, 3].map((i) => (
+                        <View key={i} className="h-10 bg-gray-200 rounded animate-pulse" />
+                      ))}
+                    </View>
+                  ) : salaryHistory && salaryHistory.length > 0 ? (
+                    <View className="space-y-3">
+                      <View className="grid grid-cols-4 gap-2 text-xs text-gray-500 font-medium pb-2 border-b border-gray-200">
+                        <Text>日期</Text>
+                        <Text>薪资</Text>
+                        <Text>类型</Text>
+                        <Text>周期</Text>
+                      </View>
+                      {salaryHistory.slice(0, 5).map((record, index) => (
+                        <View key={index} className="grid grid-cols-4 gap-2 text-sm">
+                          <Text className="text-gray-800">{formatDate(record.paymentDate)}</Text>
+                          <Text className="text-gray-800 font-medium">{formatCurrency(record.amount)}</Text>
+                          <Text className="text-gray-800">{record.type || '月薪'}</Text>
+                          <Text className="text-gray-800">{record.period || '月度'}</Text>
+                        </View>
+                      ))}
+                      {salaryHistory.length > 5 && (
+                        <Text className="text-center text-xs text-blue-500 mt-2">
+                          查看更多 ({salaryHistory.length - 5} 条记录)
+                        </Text>
+                      )}
+                    </View>
+                  ) : (
+                    <View className="text-center py-4">
+                      <Text className="text-gray-400 text-sm">暂无薪资历史记录</Text>
+                    </View>
+                  )}
+                </View>
+
+                {/* 个人征信文件区域 */}
+                <View className="card bg-white p-4">
+                  <Text className="font-semibold text-gray-700 mb-3">个人征信文件</Text>
+                  {filesLoading ? (
+                    <View className="space-y-2">
+                      {[1, 2].map((i) => (
+                        <View key={i} className="h-10 bg-gray-200 rounded animate-pulse" />
+                      ))}
+                    </View>
+                  ) : creditFiles && creditFiles.length > 0 ? (
+                    <View className="space-y-3">
+                      {creditFiles.map((file: { id: string; name: string; url?: string; size?: number; createdAt?: string }) => (
+                        <View key={file.id} className="flex justify-between items-center p-3 bg-gray-50 rounded-lg">
+                          <View className="flex items-center flex-1">
+                            <Text className="i-heroicons-document-text-20-solid text-gray-400 mr-2" />
+                            <View className="flex flex-col">
+                              <Text className="text-sm text-gray-800">{file.name}</Text>
+                              <Text className="text-xs text-gray-500">
+                                {file.size ? `${(file.size / 1024).toFixed(1)} KB` : '大小未知'} · {formatDate(file.createdAt)}
+                              </Text>
+                            </View>
+                          </View>
+                          <View
+                            className="p-2 active:bg-blue-100 rounded"
+                            onClick={() => handleOpenFile(file.url, file.name)}
+                          >
+                            <Text className="i-heroicons-eye-20-solid text-blue-500 text-lg" />
+                          </View>
+                        </View>
+                      ))}
+                    </View>
+                  ) : (
+                    <View className="text-center py-4">
+                      <Text className="text-gray-400 text-sm">暂无征信文件</Text>
+                    </View>
+                  )}
+                </View>
+
+                {/* 工作视频管理区域 - 原型第765-829行 */}
+                <View className="card bg-white p-4 mb-4">
+                  <View className="flex justify-between items-center mb-3">
+                    <Text className="font-semibold text-gray-700">工作视频</Text>
+                    <Text className="text-sm text-blue-500">查看更多</Text>
+                  </View>
+                  {videosLoading ? (
+                    <View className="space-y-3">
+                      {[1, 2, 3].map((i) => (
+                        <View key={i} className="flex items-center p-3 bg-gray-50 rounded-lg animate-pulse">
+                          <View className="w-10 h-10 bg-gray-200 rounded mr-3" />
+                          <View className="flex-1">
+                            <View className="h-4 bg-gray-200 rounded w-1/2 mb-1" />
+                            <View className="h-3 bg-gray-200 rounded w-1/3" />
+                          </View>
+                          <View className="flex space-x-2">
+                            <View className="w-8 h-8 bg-gray-200 rounded" />
+                            <View className="w-8 h-8 bg-gray-200 rounded" />
+                          </View>
+                        </View>
+                      ))}
+                    </View>
+                  ) : videos && videos.length > 0 ? (
+                    <View className="space-y-3">
+                      {/* 按分类分组显示 */}
+                      {['个税视频', '工资视频', '工作视频', '其他'].map((category) => {
+                        const categoryVideos = videos.filter((v: { category: string }) => v.category === category)
+                        if (categoryVideos.length === 0) return null
+
+                        return (
+                          <View key={category} className="space-y-2">
+                            <Text className="text-sm font-medium text-gray-600 mb-1">{category}</Text>
+                            {categoryVideos.map((video: { id: string; title: string; url?: string; uploadTime?: string; size?: number }) => (
+                              <View key={video.id} className="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
+                                <View className="flex items-center flex-1">
+                                  <View className="w-10 h-10 bg-blue-100 rounded flex items-center justify-center mr-3">
+                                    <Text className="i-heroicons-play-20-solid text-blue-500 text-lg" />
+                                  </View>
+                                  <View className="flex flex-col">
+                                    <Text className="text-sm text-gray-800 font-medium">{video.title}</Text>
+                                    <Text className="text-xs text-gray-500">
+                                      {video.uploadTime ? formatDate(video.uploadTime) : '未知时间'} ·
+                                      {video.size ? ` ${(video.size / 1024 / 1024).toFixed(2)} MB` : ' 大小未知'}
+                                    </Text>
+                                  </View>
+                                </View>
+                                <View
+                                  className="p-2 active:bg-blue-100 rounded"
+                                  onClick={() => handleOpenFile(video.url, video.title)}
+                                >
+                                  <Text className="i-heroicons-eye-20-solid text-blue-500 text-lg" />
+                                </View>
+                              </View>
+                            ))}
+                          </View>
+                        )
+                      })}
+                    </View>
+                  ) : (
+                    <View className="text-center py-4">
+                      <Text className="text-gray-400 text-sm">暂无工作视频</Text>
+                    </View>
+                  )}
+                </View>
+
+                {/* 操作按钮 - 原型第831-839行 */}
+                <View className="mt-4">
+                  {/* 联系按钮 */}
+                  <View
+                    className="w-full bg-blue-500 text-white py-3 rounded-lg font-medium flex items-center justify-center active:bg-blue-600"
+                    onClick={() => {
+                      // 联系功能:拨打电话或跳转
+                      if (talentDetail.phone) {
+                        Taro.makePhoneCall({
+                          phoneNumber: talentDetail.phone
+                        })
+                      } else {
+                        Taro.showToast({
+                          title: '暂无联系电话',
+                          icon: 'none'
+                        })
+                      }
+                    }}
+                  >
+                    <Text className="i-heroicons-phone-20-solid mr-2" />
+                    <Text>联系</Text>
+                  </View>
+                </View>
+
+              </View>
+            </>
+          ) : (
+            <View className="p-4">
+              <View className="bg-white p-4 rounded-lg text-center">
+                <Text className="text-gray-500 text-sm">未找到人才信息</Text>
+              </View>
+            </View>
+          )}
+        </ScrollView>
+
+        {/* 视频播放弹窗 */}
+        {showVideoPlayer && (
+          <View className="fixed inset-0 z-50 bg-black bg-opacity-75 flex items-center justify-center">
+            <View className="relative w-full max-w-lg mx-4">
+              {/* 关闭按钮 */}
+              <View
+                className="absolute -top-10 right-0 z-10 w-8 h-8 bg-white rounded-full flex items-center justify-center"
+                onClick={() => {
+                  setShowVideoPlayer(false)
+                  setCurrentVideoUrl('')
+                }}
+              >
+                <Text className="i-heroicons-x-mark-20-solid text-gray-700 text-lg" />
+              </View>
+              {/* 视频播放器 */}
+              <Video
+                src={currentVideoUrl}
+                controls
+                autoplay
+                loop={false}
+                muted={false}
+                initialTime={0}
+                id="video-player"
+                className="w-full rounded-lg"
+                style={{ width: '100%', height: 'auto' }}
+                onEnded={() => {
+                  // 视频播放结束后的处理(可选)
+                }}
+                onError={() => {
+                  // 视频加载失败时关闭弹窗并显示提示
+                  setShowVideoPlayer(false)
+                  setCurrentVideoUrl('')
+                  Taro.showToast({
+                    title: '视频文件不存在或无法播放',
+                    icon: 'none',
+                    duration: 2000
+                  })
+                }}
+              />
+            </View>
+          </View>
+        )}
+      </PageContainer>
+  )
+}
 
 export default TalentDetail

+ 58 - 0
mini/src/pages/yongren/talent/list/TalentManagement.css

@@ -0,0 +1,58 @@
+/* 人才列表页样式 */
+
+/* 头像颜色类 */
+.name-avatar.blue {
+  background: linear-gradient(135deg, #3b82f6, #1d4ed8);
+}
+.name-avatar.green {
+  background: linear-gradient(135deg, #10b981, #059669);
+}
+.name-avatar.purple {
+  background: linear-gradient(135deg, #8b5cf6, #7c3aed);
+}
+.name-avatar.orange {
+  background: linear-gradient(135deg, #f59e0b, #d97706);
+}
+.name-avatar.red {
+  background: linear-gradient(135deg, #ef4444, #dc2626);
+}
+.name-avatar.teal {
+  background: linear-gradient(135deg, #14b8a6, #0d9488);
+}
+
+/* 卡片样式 */
+.card {
+  border-radius: 12px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+  transition: transform 0.2s ease, box-shadow 0.2s ease;
+}
+
+.card:active {
+  transform: translateY(-1px);
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
+}
+
+/* 筛选标签激活状态 */
+.filter-tag-active {
+  background-color: rgb(219 234 254);
+  color: rgb(30 64 175);
+}
+
+.filter-tag-inactive {
+  background-color: rgb(243 244 246);
+  color: rgb(55 65 81);
+}
+
+/* 加载动画 */
+@keyframes pulse-bg {
+  0%, 100% {
+    opacity: 1;
+  }
+  50% {
+    opacity: 0.5;
+  }
+}
+
+.animate-pulse {
+  animation: pulse-bg 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+}

+ 381 - 3
mini/src/pages/yongren/talent/list/index.tsx

@@ -1,4 +1,382 @@
-// 桥接文件:从 @d8d/yongren-talent-management-ui 包导入TalentManagement页面
-import TalentManagement from '@d8d/yongren-talent-management-ui/pages/TalentManagement/TalentManagement'
-import '@d8d/yongren-talent-management-ui/pages/TalentManagement/TalentManagement.css'
+import React, { useEffect, useState } from 'react'
+import { View, Text, Input, ScrollView } from '@tarojs/components'
+import Taro, { useDidShow, usePullDownRefresh } from '@tarojs/taro'
+import { useInfiniteQuery } from '@tanstack/react-query'
+import dayjs from 'dayjs'
+import { YongrenTabBarLayout } from '../../../../components/YongrenTabBarLayout'
+import { PageContainer } from '@d8d/mini-shared-ui-components/components/page-container'
+import { Navbar } from '@d8d/mini-shared-ui-components/components/navbar'
+import { enterpriseDisabilityClient } from '../../../api'
+import { useAuth, useRequireAuth } from '../../../hooks'
+import { WorkStatus } from '@d8d/allin-enums/enums/work-status.enum'
+
+export interface TalentManagementProps {
+  // 组件属性定义(目前为空)
+}
+
+// 企业专用人才列表项类型 - 匹配CompanyPersonListItemSchema
+interface _CompanyPersonListItem {
+  personId: number
+  name: string
+  gender: string
+  idCard: string
+  disabilityType: string
+  disabilityLevel: string
+  phone: string | null
+  birthDate: string | null
+  salaryDetail: number | null
+  jobStatus: string
+  workStatus?: WorkStatus  // 新增:工作状态枚举值
+  workStatusLabel?: string // 新增:工作状态中文标签
+  latestJoinDate: string | null
+  orderName: string | null
+}
+
+// 中文状态到WorkStatus枚举的映射
+const CHINESE_STATUS_TO_ENUM: Record<string, WorkStatus> = {
+  '在职': WorkStatus.WORKING,
+  '待入职': WorkStatus.PRE_WORKING,
+  '离职': WorkStatus.RESIGNED
+}
+
+const TalentManagement: React.FC<TalentManagementProps> = () => {
+  const { user: _user } = useAuth()
+  const { isLoggedIn } = useRequireAuth()
+
+  // 搜索和筛选状态
+  const [searchText, setSearchText] = useState('')
+  const [activeStatus, setActiveStatus] = useState<'全部' | '在职' | '待入职' | '离职'>('全部')
+  const [activeDisabilityType, setActiveDisabilityType] = useState<string>('')
+
+  // 搜索参数防抖
+  const [debouncedSearchText, setDebouncedSearchText] = useState('')
+  useEffect(() => {
+    const timer = setTimeout(() => {
+      setDebouncedSearchText(searchText)
+    }, 500)
+    return () => clearTimeout(timer)
+  }, [searchText])
+
+  // 使用 useInfiniteQuery 进行无限滚动分页
+  const {
+    data,
+    isLoading,
+    error,
+    isFetchingNextPage,
+    fetchNextPage,
+    hasNextPage,
+    refetch
+  } = useInfiniteQuery({
+    queryKey: ['talentList', { search: debouncedSearchText, jobStatus: activeStatus !== '全部' ? CHINESE_STATUS_TO_ENUM[activeStatus] : undefined, disabilityType: activeDisabilityType || undefined }],
+    queryFn: async ({ pageParam = 1 }) => {
+      // 构建查询参数(企业专用API使用camelCase参数名)
+      const queryParams = {
+        search: debouncedSearchText || undefined,
+        jobStatus: activeStatus !== '全部' ? CHINESE_STATUS_TO_ENUM[activeStatus] : undefined,
+        disabilityType: activeDisabilityType || undefined,
+        page: pageParam,
+        limit: 20
+      }
+
+      const response = await enterpriseDisabilityClient.index.$get({
+        query: queryParams
+      })
+      if (response.status !== 200) {
+        throw new Error('获取人才列表失败')
+      }
+      const result = await response.json()
+      // API返回结构:{ data: [...], pagination: { total, page, limit, totalPages } }
+      const data = result?.data || []
+      const pagination = result?.pagination || { total: 0, page: 1, limit: 20, totalPages: 0 }
+      // 转换为扁平结构以便使用
+      return {
+        data,
+        total: pagination.total,
+        page: pagination.page,
+        limit: pagination.limit,
+        totalPages: pagination.totalPages
+      }
+    },
+    getNextPageParam: (lastPage, allPages) => {
+      const totalPages = lastPage.totalPages || 1
+      const currentPage = lastPage.page || allPages.length
+      return currentPage < totalPages ? currentPage + 1 : undefined
+    },
+    initialPageParam: 1,
+    enabled: isLoggedIn, // 只有登录后才获取数据
+    refetchOnWindowFocus: false,
+    staleTime: 30 * 1000, // 30秒缓存
+  })
+
+  // 合并所有分页数据
+  const allTalents = data?.pages.flatMap(page => page.data) || []
+  // 安全访问 totalCount,处理 pages 为空的情况
+  const totalCount = data?.pages?.[0]?.total ?? 0
+
+  // 使用页面级下拉刷新
+  usePullDownRefresh(async () => {
+    try {
+      await refetch()
+    } catch (error) {
+      console.debug('刷新失败:', error)
+    } finally {
+      Taro.stopPullDownRefresh()
+    }
+  })
+
+  // 页面加载时设置标题
+  useEffect(() => {
+    Taro.setNavigationBarTitle({
+      title: '人才管理'
+    })
+  }, [])
+
+  // 页面显示时自动刷新(从详情页返回时触发)
+  useDidShow(() => {
+    console.debug('人才列表页显示,自动刷新数据')
+    refetch()
+  })
+
+  // 状态标签列表
+  const statusTags: Array<'全部' | '在职' | '待入职' | '离职'> = ['全部', '在职', '待入职', '离职']
+  const disabilityTypeTags = ['肢体残疾', '听力残疾', '视力残疾', '言语残疾', '智力残疾', '精神残疾']
+
+  // 处理状态筛选点击
+  const handleStatusClick = (status: '全部' | '在职' | '待入职' | '离职') => {
+    setActiveStatus(status)
+  }
+
+  // 处理残疾类型筛选点击
+  const handleDisabilityTypeClick = (type: string) => {
+    setActiveDisabilityType(activeDisabilityType === type ? '' : type)
+  }
+
+  // 处理搜索输入
+  const handleSearchChange = (e: { detail: { value: string } }) => {
+    setSearchText(e.detail.value)
+  }
+
+  // 触底加载更多
+  const handleScrollToLower = () => {
+    if (hasNextPage && !isFetchingNextPage) {
+      fetchNextPage()
+    }
+  }
+
+  // 处理人才卡片点击跳转
+  const handleTalentClick = (talentId: number) => {
+    Taro.navigateTo({
+      url: `/pages/yongren/talent/detail/index?id=${talentId}`
+    })
+  }
+
+  // 获取头像颜色
+  const getAvatarColor = (id: number) => {
+    const colors = ['blue', 'green', 'purple', 'orange', 'red', 'teal']
+    const index = id % colors.length
+    return colors[index]
+  }
+
+  // 计算年龄基于出生日期
+  const calculateAge = (birthDate: string | null): string => {
+    if (!birthDate) return '未知岁'
+    try {
+      const birth = new Date(birthDate)
+      const today = new Date()
+      let age = today.getFullYear() - birth.getFullYear()
+      const monthDiff = today.getMonth() - birth.getMonth()
+      if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())) {
+        age--
+      }
+      return `${age}岁`
+    } catch (_error) {
+      return '未知岁'
+    }
+  }
+
+  // 格式化薪资显示
+  const formatSalary = (salaryDetail: number | null): string => {
+    if (!salaryDetail || salaryDetail <= 0) return '待定'
+    return `¥${salaryDetail.toLocaleString()}`
+  }
+
+  return (
+    <YongrenTabBarLayout activeKey="talent">
+      <PageContainer padding={false} className="pb-0">
+        <ScrollView
+          className="h-[calc(100vh-120px)] overflow-y-auto px-4 pb-4 pt-0"
+          scrollY
+          onScrollToLower={handleScrollToLower}
+        >
+          {/* 导航栏 */}
+          <Navbar
+            title="人才管理"
+            leftIcon=""
+            leftText=""
+            onClickLeft={() => {}}
+            backgroundColor="bg-white"
+            border={true}
+            fixed={true}
+            placeholder={true}
+          />
+          {/* 搜索和筛选区域 - 对照原型第434-447行 */}
+          <View className="p-4 border-b border-gray-200">
+            <View className="flex items-center bg-gray-100 rounded-lg px-4 py-2 mb-3">
+              <Text className="i-heroicons-magnifying-glass-20-solid text-gray-400 mr-2" />
+              <Input
+                type="text"
+                placeholder="搜索姓名、残疾证号..."
+                className="w-full bg-transparent outline-none text-sm"
+                value={searchText}
+                onInput={handleSearchChange}
+              />
+            </View>
+            <View className="flex space-x-2 overflow-x-auto pb-2">
+              {statusTags.map((status) => (
+                <Text
+                  key={status}
+                  className={`text-xs px-3 py-1 rounded-full whitespace-nowrap ${
+                    activeStatus === status
+                      ? 'bg-blue-100 text-blue-800'
+                      : 'bg-gray-100 text-gray-800'
+                  }`}
+                  onClick={() => handleStatusClick(status)}
+                >
+                  {status}
+                </Text>
+              ))}
+            </View>
+            <View className="flex space-x-2 overflow-x-auto mt-2">
+              {disabilityTypeTags.map((type) => (
+                <Text
+                  key={type}
+                  className={`text-xs px-3 py-1 rounded-full whitespace-nowrap ${
+                    activeDisabilityType === type
+                      ? 'bg-blue-100 text-blue-800'
+                      : 'bg-gray-100 text-gray-800'
+                  }`}
+                  onClick={() => handleDisabilityTypeClick(type)}
+                >
+                  {type}
+                </Text>
+              ))}
+            </View>
+          </View>
+
+          {/* 人才列表区域 - 对照原型第451-560行 */}
+          <View className="p-4">
+            <View className="flex justify-between items-center mb-4">
+              <Text className="font-semibold text-gray-700">
+                全部人才 ({totalCount})
+              </Text>
+              <View className="flex space-x-2">
+                <View className="text-gray-500">
+                  <Text className="i-heroicons-funnel-20-solid" />
+                </View>
+                <View className="text-gray-500">
+                  <Text className="i-heroicons-arrows-up-down-20-solid" />
+                </View>
+              </View>
+            </View>
+
+            {isLoading ? (
+              // 加载状态
+              <View className="space-y-3">
+                {[1, 2, 3].map((i) => (
+                  <View key={i} className="bg-white p-4 rounded-lg animate-pulse flex items-center">
+                    <View className="w-10 h-10 bg-gray-200 rounded-full" />
+                    <View className="flex-1 ml-3">
+                      <View className="h-4 bg-gray-200 rounded w-1/3 mb-2" />
+                      <View className="h-3 bg-gray-200 rounded w-1/2" />
+                      <View className="flex justify-between mt-2">
+                        <View className="h-3 bg-gray-200 rounded w-1/4" />
+                        <View className="h-3 bg-gray-200 rounded w-1/4" />
+                      </View>
+                    </View>
+                  </View>
+                ))}
+              </View>
+            ) : error ? (
+              // 错误状态
+              <View className="bg-white p-4 rounded-lg text-center">
+                <Text className="text-red-500 text-sm">加载失败: {(error as Error).message}</Text>
+                <View
+                  className="mt-2 bg-blue-500 text-white text-xs px-3 py-1 rounded-full inline-block"
+                  onClick={() => refetch()}
+                >
+                  重试
+                </View>
+              </View>
+            ) : allTalents.length > 0 ? (
+              // 人才列表
+              <View className="space-y-3">
+                {allTalents.map((talent, index) => (
+                  <View
+                    key={`${talent.personId}-${index}`}
+                    className="card bg-white p-4 flex items-center cursor-pointer active:bg-gray-50"
+                    onClick={() => handleTalentClick(talent.personId)}
+                  >
+                    <View className={`name-avatar ${getAvatarColor(talent.personId)} w-10 h-10 rounded-full flex items-center justify-center`}>
+                      <Text className="text-white font-semibold">
+                        {talent.name.charAt(0)}
+                      </Text>
+                    </View>
+                    <View className="flex-1 ml-3">
+                      <View className="flex justify-between items-start">
+                        <View className="flex flex-col">
+                          <Text className="font-semibold text-gray-800">{talent.name}</Text>
+                          <Text className="text-xs text-gray-500">
+                            {talent.disabilityType || '未指定'} · {talent.disabilityLevel || '未分级'} · {talent.gender} · {calculateAge(talent.birthDate)}
+                          </Text>
+                        </View>
+                        <Text className={`text-xs px-2 py-1 rounded-full ${
+                          talent.jobStatus === '在职'
+                            ? 'bg-green-100 text-green-800'
+                            : talent.jobStatus === '待入职'
+                            ? 'bg-yellow-100 text-yellow-800'
+                            : 'bg-gray-100 text-gray-800'
+                        }`}>
+                          {talent.jobStatus}
+                        </Text>
+                      </View>
+                      <View className="mt-2 flex justify-between text-xs text-gray-500">
+                        <Text>入职: {talent.latestJoinDate ? dayjs(talent.latestJoinDate).format('YYYY-MM-DD') : '未入职'}</Text>
+                        <Text>薪资: {formatSalary(talent.salaryDetail)}</Text>
+                      </View>
+                    </View>
+                  </View>
+                ))}
+
+                {/* 无限滚动加载状态 */}
+                {isFetchingNextPage && (
+                  <View className="flex justify-center py-4">
+                    <Text className="i-heroicons-arrow-path-20-solid animate-spin w-6 h-6 text-blue-500 mr-2" />
+                    <Text className="text-sm text-gray-500">加载更多...</Text>
+                  </View>
+                )}
+
+                {!hasNextPage && allTalents.length > 0 && (
+                  <View className="text-center py-4">
+                    <Text className="text-sm text-gray-400">没有更多了</Text>
+                  </View>
+                )}
+              </View>
+            ) : (
+              // 空状态
+              <View className="bg-white p-4 rounded-lg text-center">
+                <Text className="text-gray-500 text-sm">暂无人才数据</Text>
+                {debouncedSearchText && (
+                  <Text className="text-gray-400 text-xs mt-1">
+                    尝试调整搜索条件
+                  </Text>
+                )}
+              </View>
+            )}
+          </View>
+        </ScrollView>
+      </PageContainer>
+    </YongrenTabBarLayout>
+  )
+}
+
 export default TalentManagement

+ 462 - 2
mini/src/pages/yongren/video/index.tsx

@@ -1,4 +1,464 @@
-// 桥接文件:从 @d8d/yongren-settings-ui 包导入VideoManagement页面
-import { VideoManagement } from '@d8d/yongren-settings-ui'
+import React, { useState } from 'react'
+import { View, Text, ScrollView } from '@tarojs/components'
+import Taro from '@tarojs/taro'
+import { useQuery } from '@tanstack/react-query'
+import { YongrenTabBarLayout } from '../../../components/YongrenTabBarLayout'
+import { Navbar } from '@d8d/mini-shared-ui-components/components/navbar'
+import { enterpriseOrderClient } from '../../../api/enterpriseOrderClient'
+
+// 视频类型枚举
+type VideoAssetType = 'salary_video' | 'tax_video' | 'checkin_video' | 'work_video'
+
+// 视频状态枚举
+type VideoStatus = 'pending' | 'verified' | 'rejected'
+
+// 视频分类标签
+interface VideoCategoryTab {
+  key: 'all' | VideoAssetType
+  label: string
+  count: number
+}
+
+// 视频卡片数据
+interface VideoCardData {
+  id: string
+  title: string
+  description: string
+  assetType: VideoAssetType
+  status: VideoStatus
+  fileSize: number
+  duration: number
+  uploadedAt: string
+  thumbnailUrl?: string
+  fileUrl: string
+  talentName?: string
+  orderId?: string
+}
+
+// 视频统计数据
+interface VideoStatisticsData {
+  totalCount: number
+  salaryVideoCount: number
+  taxVideoCount: number
+  checkinVideoCount: number
+  workVideoCount: number
+}
+
+// 视频分类标签配置
+const VIDEO_CATEGORIES: VideoCategoryTab[] = [
+  { key: 'all', label: '全部视频', count: 0 },
+  { key: 'salary_video', label: '工资视频', count: 0 },
+  { key: 'tax_video', label: '个税视频', count: 0 },
+  { key: 'checkin_video', label: '打卡视频', count: 0 }
+]
+
+// 视频状态标签配置
+const VIDEO_STATUS_LABELS = {
+  pending: { text: '待审核', className: 'bg-yellow-100 text-yellow-800' },
+  verified: { text: '已验证', className: 'bg-green-100 text-green-800' },
+  rejected: { text: '已拒绝', className: 'bg-red-100 text-red-800' }
+} as const
+
+// 视频类型标签配置
+const VIDEO_TYPE_LABELS: Record<VideoAssetType, string> = {
+  salary_video: '工资视频',
+  tax_video: '个税视频',
+  checkin_video: '打卡视频',
+  work_video: '工作视频'
+}
+
+// 格式化文件大小
+const formatFileSize = (bytes: number): string => {
+  if (bytes < 1024 * 1024) {
+    return `${(bytes / 1024).toFixed(1)}KB`
+  }
+  return `${(bytes / (1024 * 1024)).toFixed(1)}MB`
+}
+
+// 格式化时长
+const formatDuration = (seconds: number): string => {
+  const mins = Math.floor(seconds / 60)
+  const secs = seconds % 60
+  return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`
+}
+
+// 格式化日期
+const formatDate = (dateStr: string): string => {
+  const date = new Date(dateStr)
+  return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`
+}
+
+const VideoManagement: React.FC = () => {
+  const [activeCategory, setActiveCategory] = useState<'all' | VideoAssetType>('all')
+  const [selectedVideos, setSelectedVideos] = useState<Set<string>>(new Set())
+
+  // 获取视频统计数据
+  const { data: statisticsData, isLoading: statisticsLoading } = useQuery({
+    queryKey: ['video-statistics'],
+    queryFn: async () => {
+      const res = await enterpriseOrderClient['video-statistics'].$get({} as any)
+      if (!res.ok) {
+        throw new Error('获取视频统计数据失败')
+      }
+      return await res.json()
+    }
+  })
+
+  // 获取视频列表
+  const { data: videosData, isLoading: videosLoading } = useQuery({
+    queryKey: ['company-videos', activeCategory],
+    queryFn: async () => {
+      const query = activeCategory !== 'all' ? { assetType: activeCategory as any } : {}
+      const res = await enterpriseOrderClient['company-videos'].$get({ query } as any)
+      if (!res.ok) {
+        throw new Error('获取视频列表失败')
+      }
+      return await res.json()
+    }
+  })
+
+  // 计算统计数据 - 从API响应中提取
+  const statistics: VideoStatisticsData = statisticsData?.total !== undefined ? {
+    totalCount: statisticsData.total || 0,
+    salaryVideoCount: statisticsData.stats?.find((s: any) => s.assetType === 'salary_video')?.count || 0,
+    taxVideoCount: statisticsData.stats?.find((s: any) => s.assetType === 'tax_video')?.count || 0,
+    checkinVideoCount: statisticsData.stats?.find((s: any) => s.assetType === 'checkin_video')?.count || 0,
+    workVideoCount: statisticsData.stats?.find((s: any) => s.assetType === 'work_video')?.count || 0
+  } : {
+    totalCount: 0,
+    salaryVideoCount: 0,
+    taxVideoCount: 0,
+    checkinVideoCount: 0,
+    workVideoCount: 0
+  }
+
+  // 更新分类标签的计数
+  const categoryTabs: VideoCategoryTab[] = VIDEO_CATEGORIES.map(tab => {
+    if (tab.key === 'all') {
+      return { ...tab, count: statistics.totalCount }
+    }
+    const countMap: Record<string, number> = {
+      salary_video: statistics.salaryVideoCount,
+      tax_video: statistics.taxVideoCount,
+      checkin_video: statistics.checkinVideoCount,
+      work_video: statistics.workVideoCount
+    }
+    return { ...tab, count: countMap[tab.key] || 0 }
+  })
+
+  // 视频列表数据
+  const videos: VideoCardData[] = videosData?.data?.map((video: any) => {
+    const assetType: VideoAssetType = video.assetType || 'work_video'
+    const title = video.talentName
+      ? `${video.talentName} - ${VIDEO_TYPE_LABELS[assetType]}`
+      : VIDEO_TYPE_LABELS[assetType]
+
+    return {
+      id: video.id,
+      title,
+      description: video.description || '视频文件',
+      assetType,
+      status: video.status || 'pending',
+      fileSize: video.fileSize || 0,
+      duration: video.duration || 0,
+      uploadedAt: video.createdAt,
+      thumbnailUrl: video.thumbnailUrl,
+      fileUrl: video.fileUrl,
+      talentName: video.talentName,
+      orderId: video.orderId
+    }
+  }) || []
+
+  // 选择/取消选择视频
+  const toggleVideoSelection = (videoId: string) => {
+    const newSelection = new Set(selectedVideos)
+    if (newSelection.has(videoId)) {
+      newSelection.delete(videoId)
+    } else {
+      newSelection.add(videoId)
+    }
+    setSelectedVideos(newSelection)
+  }
+
+  // 批量下载视频
+  const handleBatchDownload = async () => {
+    if (selectedVideos.size === 0) {
+      Taro.showToast({
+        title: '请先选择要下载的视频',
+        icon: 'none'
+      })
+      return
+    }
+
+    try {
+      Taro.showLoading({ title: '准备下载...' })
+
+      const res = await enterpriseOrderClient['batch-download'].$post({
+        json: {
+          downloadScope: 'company' as any,
+          fileIds: Array.from(selectedVideos)
+        }
+      } as any)
+
+      if (!res.ok) {
+        throw new Error('批量下载失败')
+      }
+
+      const result = await res.json()
+
+      Taro.hideLoading()
+      Taro.showToast({
+        title: '下载任务已创建',
+        icon: 'success'
+      })
+
+      // 清空选择
+      setSelectedVideos(new Set())
+    } catch (error) {
+      Taro.hideLoading()
+      Taro.showToast({
+        title: '下载失败,请重试',
+        icon: 'none'
+      })
+    }
+  }
+
+  // 播放视频
+  const handlePlayVideo = (video: VideoCardData) => {
+    Taro.showModal({
+      title: '播放视频',
+      content: `即将播放:${video.title}`,
+      confirmText: '播放',
+      success: (res) => {
+        if (res.confirm && video.fileUrl) {
+          // 在小程序中打开视频播放器
+          Taro.openDocument({
+            filePath: video.fileUrl,
+            fileType: 'mp4' as any,
+            success: () => {
+              console.log('视频播放成功')
+            },
+            fail: (err) => {
+              console.error('视频播放失败:', err)
+              Taro.showToast({
+                title: '播放失败',
+                icon: 'none'
+              })
+            }
+          })
+        }
+      }
+    })
+  }
+
+  // 下载视频
+  const handleDownloadVideo = async (video: VideoCardData) => {
+    try {
+      Taro.showLoading({ title: '下载中...' })
+
+      const res = await enterpriseOrderClient['batch-download'].$post({
+        json: {
+          downloadScope: 'company' as any,
+          fileIds: [video.id]
+        }
+      } as any)
+
+      if (!res.ok) {
+        throw new Error('下载失败')
+      }
+
+      Taro.hideLoading()
+      Taro.showToast({
+        title: '下载成功',
+        icon: 'success'
+      })
+    } catch (error) {
+      Taro.hideLoading()
+      Taro.showToast({
+        title: '下载失败',
+        icon: 'none'
+      })
+    }
+  }
+
+  // 分享视频
+  const handleShareVideo = (video: VideoCardData) => {
+    Taro.showShareMenu({
+      withShareTicket: true
+    })
+  }
+
+  // 加载状态
+  if (videosLoading || statisticsLoading) {
+    return (
+      <YongrenTabBarLayout activeKey="settings">
+        <ScrollView
+          className="h-[calc(100%-60px)] overflow-y-auto px-4 pb-4 pt-0"
+          scrollY
+        >
+          <Navbar
+            title="视频管理"
+            leftIcon=""
+            leftText=""
+            onClickLeft={() => {}}
+            backgroundColor="bg-white"
+            border={true}
+            fixed={true}
+            placeholder={true}
+          />
+          <View className="p-4">
+            <Text>加载中...</Text>
+          </View>
+        </ScrollView>
+      </YongrenTabBarLayout>
+    )
+  }
+
+  return (
+    <YongrenTabBarLayout activeKey="settings">
+      <ScrollView
+        className="h-[calc(100%-60px)] overflow-y-auto px-4 pb-4 pt-0"
+        scrollY
+      >
+        {/* 导航栏 */}
+        <Navbar
+          title="视频管理"
+          leftIcon=""
+          leftText=""
+          onClickLeft={() => {}}
+          backgroundColor="bg-white"
+          border={true}
+          fixed={true}
+          placeholder={true}
+        />
+
+        {/* 顶部信息区域 */}
+        <View className="p-4 border-b border-gray-200">
+          <View className="flex flex-col">
+            <Text className="font-semibold text-gray-800">企业视频管理</Text>
+            <Text className="text-sm text-gray-500">企业维度 - 视频资料查看</Text>
+          </View>
+          <View
+            className="text-blue-500 text-sm mt-2"
+            onClick={handleBatchDownload}
+          >
+            <Text>批量下载 ({selectedVideos.size})</Text>
+          </View>
+        </View>
+
+        {/* 视频分类筛选区域 */}
+        <View className="p-4 border-b border-gray-200">
+          <View className="flex space-x-2 overflow-x-auto pb-2">
+            {categoryTabs.map(tab => (
+              <View
+                key={tab.key}
+                className={`text-xs px-3 py-1 rounded-full whitespace-nowrap ${
+                  activeCategory === tab.key
+                    ? 'bg-blue-100 text-blue-800'
+                    : 'bg-gray-100 text-gray-800'
+                }`}
+                onClick={() => setActiveCategory(tab.key)}
+              >
+                <Text>{tab.label} ({tab.count})</Text>
+              </View>
+            ))}
+          </View>
+        </View>
+
+        {/* 视频列表标题区域 */}
+        <View className="p-4">
+          <View className="flex justify-between items-center mb-4">
+            <Text className="font-semibold text-gray-700">
+              视频列表 ({videos.length})
+            </Text>
+            <View className="flex space-x-2">
+              <Text className="text-gray-500">排序</Text>
+              <Text className="text-gray-500">筛选</Text>
+            </View>
+          </View>
+
+          {/* 视频列表 */}
+          {videos.length === 0 ? (
+            <View className="p-8 text-center">
+              <Text className="text-gray-500">暂无视频数据</Text>
+            </View>
+          ) : (
+            <View className="space-y-4">
+              {videos.map(video => {
+                const statusLabel = VIDEO_STATUS_LABELS[video.status]
+                const isSelected = selectedVideos.has(video.id)
+
+                return (
+                  <View
+                    key={video.id}
+                    className="bg-white p-4 rounded-lg border border-gray-200"
+                  >
+                    {/* 视频卡片头部 */}
+                    <View className="flex justify-between items-start mb-3">
+                      <View className="flex flex-col">
+                        <Text className="font-semibold text-gray-800">
+                          {video.title}
+                        </Text>
+                        <Text className="text-xs text-gray-500">
+                          {formatDate(video.uploadedAt)} 上传
+                        </Text>
+                      </View>
+                      <View className={`text-xs px-2 py-1 rounded-full ${statusLabel.className}`}>
+                        <Text>{statusLabel.text}</Text>
+                      </View>
+                    </View>
+
+                    {/* 视频内容区域 */}
+                    <View className="flex items-center mb-3">
+                      <View className="w-16 h-16 bg-gray-200 rounded-lg flex items-center justify-center mr-3">
+                        <Text className="text-gray-500">▶</Text>
+                      </View>
+                      <View className="flex-1 flex flex-col">
+                        <Text className="text-sm text-gray-700">
+                          {video.description}
+                        </Text>
+                        <Text className="text-xs text-gray-500">
+                          时长: {formatDuration(video.duration)} · 大小: {formatFileSize(video.fileSize)}
+                        </Text>
+                      </View>
+                    </View>
+
+                    {/* 操作按钮区域 */}
+                    <View className="flex justify-between text-sm">
+                      <View
+                        className="text-blue-500"
+                        onClick={() => handlePlayVideo(video)}
+                      >
+                        <Text>播放</Text>
+                      </View>
+                      <View
+                        className="text-green-500"
+                        onClick={() => handleDownloadVideo(video)}
+                      >
+                        <Text>下载</Text>
+                      </View>
+                      <View
+                        className="text-gray-500"
+                        onClick={() => handleShareVideo(video)}
+                      >
+                        <Text>分享</Text>
+                      </View>
+                      <View
+                        className={`${isSelected ? 'text-blue-500' : 'text-gray-400'}`}
+                        onClick={() => toggleVideoSelection(video.id)}
+                      >
+                        <Text>{isSelected ? '已选择' : '选择'}</Text>
+                      </View>
+                    </View>
+                  </View>
+                )
+              })}
+            </View>
+          )}
+        </View>
+      </ScrollView>
+    </YongrenTabBarLayout>
+  )
+}
 
 export default VideoManagement