This commit is contained in:
158
miniprogram/src/pages/competition/project-detail.tsx
Normal file
158
miniprogram/src/pages/competition/project-detail.tsx
Normal file
@@ -0,0 +1,158 @@
|
||||
import { View, Text, Image, Button, ScrollView } from '@tarojs/components'
|
||||
import Taro, { useLoad } from '@tarojs/taro'
|
||||
import { useState } from 'react'
|
||||
import { getProjectDetail, getComments } from '../../api'
|
||||
import MarkdownReader from '../../components/MarkdownReader'
|
||||
import './project-detail.scss'
|
||||
|
||||
export default function ProjectDetail() {
|
||||
const [project, setProject] = useState<any>(null)
|
||||
const [comments, setComments] = useState<any[]>([])
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
useLoad((options) => {
|
||||
const { id } = options
|
||||
if (id) {
|
||||
fetchProject(id)
|
||||
fetchComments(id)
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 获取项目详情
|
||||
* @param id 项目ID
|
||||
*/
|
||||
const fetchProject = async (id) => {
|
||||
setLoading(true)
|
||||
try {
|
||||
const res = await getProjectDetail(id)
|
||||
setProject(res)
|
||||
} catch (e) {
|
||||
Taro.showToast({ title: '加载项目详情失败', icon: 'none' })
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取项目评语
|
||||
* @param id 项目ID
|
||||
*/
|
||||
const fetchComments = async (id) => {
|
||||
try {
|
||||
const res = await getComments({ project: id })
|
||||
const list = res.results || res.data || res || []
|
||||
setComments(Array.isArray(list) ? list : [])
|
||||
} catch (e) {
|
||||
console.error('获取评语失败', e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开/下载附件
|
||||
* @param file 文件对象
|
||||
*/
|
||||
const handleOpenFile = (file) => {
|
||||
if (!file.file) return
|
||||
|
||||
// 如果是图片,预览
|
||||
if (file.file.match(/\.(jpg|jpeg|png|gif)$/i)) {
|
||||
Taro.previewImage({ urls: [file.file] })
|
||||
return
|
||||
}
|
||||
|
||||
// 其他文件尝试下载打开
|
||||
Taro.showLoading({ title: '下载中...' })
|
||||
Taro.downloadFile({
|
||||
url: file.file,
|
||||
success: (res) => {
|
||||
const filePath = res.tempFilePath
|
||||
Taro.openDocument({
|
||||
filePath,
|
||||
success: () => console.log('打开文档成功'),
|
||||
fail: (err) => {
|
||||
console.error(err)
|
||||
Taro.showToast({ title: '打开文件失败', icon: 'none' })
|
||||
}
|
||||
})
|
||||
},
|
||||
fail: () => {
|
||||
Taro.showToast({ title: '下载文件失败', icon: 'none' })
|
||||
},
|
||||
complete: () => {
|
||||
Taro.hideLoading()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (loading || !project) return <View className='loading'>加载中...</View>
|
||||
|
||||
return (
|
||||
<ScrollView scrollY className='project-detail'>
|
||||
<Image
|
||||
className='cover'
|
||||
mode='aspectFill'
|
||||
src={project.display_cover_image || project.cover_image_url || 'https://via.placeholder.com/400x200'}
|
||||
/>
|
||||
|
||||
<View className='content'>
|
||||
<View className='header'>
|
||||
<Text className='title'>{project.title}</Text>
|
||||
<View className='author'>
|
||||
<Image className='avatar' src={project.contestant_info?.avatar_url || ''} />
|
||||
<Text className='name'>{project.contestant_info?.nickname || '参赛者'}</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View className='section'>
|
||||
<Text className='section-title'>项目介绍</Text>
|
||||
<View className='text-content'>
|
||||
{project.description ? <MarkdownReader content={project.description} /> : <Text className='empty'>暂无介绍</Text>}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View className='section'>
|
||||
<Text className='section-title'>团队介绍</Text>
|
||||
<View className='text-content'>
|
||||
<Text>{project.team_info || '暂无团队信息'}</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View className='section'>
|
||||
<Text className='section-title'>项目附件</Text>
|
||||
{project.files && project.files.length > 0 ? (
|
||||
<View className='file-list'>
|
||||
{project.files.map((file, index) => (
|
||||
<View key={index} className='file-item' onClick={() => handleOpenFile(file)}>
|
||||
<Text className='file-name'>{file.name || '附件 ' + (index + 1)}</Text>
|
||||
<Text className='file-action'>查看</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
) : (
|
||||
<Text className='empty'>暂无附件</Text>
|
||||
)}
|
||||
</View>
|
||||
|
||||
<View className='section comments-section'>
|
||||
<Text className='section-title'>评委评语</Text>
|
||||
{comments.length > 0 ? (
|
||||
<View className='comment-list'>
|
||||
{comments.map((c) => (
|
||||
<View key={c.id} className='comment-item'>
|
||||
<View className='comment-header'>
|
||||
<Text className='judge-name'>{c.judge_name || '评委'}</Text>
|
||||
<Text className='comment-time'>{c.created_at?.substring(0, 16)}</Text>
|
||||
</View>
|
||||
<Text className='comment-content'>{c.content}</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
) : (
|
||||
<Text className='empty'>暂无评语</Text>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user