|
@@ -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
|
|
export default TalentDetail
|