|
|
@@ -1,5 +1,5 @@
|
|
|
-import React, { useEffect, useState } from 'react'
|
|
|
-import { View, Text, ScrollView, Video } from '@tarojs/components'
|
|
|
+import React, { useEffect } from 'react'
|
|
|
+import { View, Text, ScrollView } from '@tarojs/components'
|
|
|
import Taro from '@tarojs/taro'
|
|
|
import './TalentDetail.css'
|
|
|
import { useQuery } from '@tanstack/react-query'
|
|
|
@@ -12,9 +12,7 @@ import { useRequireAuth } from '@/hooks'
|
|
|
import type {
|
|
|
WorkHistoryItem,
|
|
|
SalaryHistoryItem,
|
|
|
- SalaryHistoryResponse,
|
|
|
- CreditInfoItem,
|
|
|
- PersonVideoItem
|
|
|
+ SalaryHistoryResponse
|
|
|
} from '@d8d/allin-disability-module/routes'
|
|
|
|
|
|
export interface TalentDetailProps {
|
|
|
@@ -27,10 +25,6 @@ const TalentDetail: React.FC<TalentDetailProps> = () => {
|
|
|
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],
|
|
|
@@ -184,71 +178,6 @@ const TalentDetail: React.FC<TalentDetailProps> = () => {
|
|
|
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({
|
|
|
@@ -256,7 +185,7 @@ const TalentDetail: React.FC<TalentDetailProps> = () => {
|
|
|
})
|
|
|
}, [])
|
|
|
|
|
|
- const isLoading = talentLoading || workLoading || salaryLoading || filesLoading || historyLoading || workHistoryLoading || videosLoading
|
|
|
+ const isLoading = talentLoading || workLoading || salaryLoading || historyLoading || workHistoryLoading
|
|
|
const hasError = talentError
|
|
|
|
|
|
// 格式化日期
|
|
|
@@ -271,109 +200,6 @@ const TalentDetail: React.FC<TalentDetailProps> = () => {
|
|
|
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
|
|
|
@@ -649,109 +475,6 @@ const TalentDetail: React.FC<TalentDetailProps> = () => {
|
|
|
)}
|
|
|
</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>
|
|
|
-
|
|
|
{/* TODO: 联系按钮暂时隐藏 - 2025-03-25 */}
|
|
|
{/* 操作按钮 - 原型第831-839行 */}
|
|
|
{/* <View className="mt-4"> */}
|
|
|
@@ -788,48 +511,6 @@ const TalentDetail: React.FC<TalentDetailProps> = () => {
|
|
|
)}
|
|
|
</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>
|
|
|
)
|
|
|
}
|