|
|
@@ -1,7 +1,7 @@
|
|
|
import React, { useState } from 'react';
|
|
|
import { useParams, useNavigate } from 'react-router-dom';
|
|
|
import { useQuery } from '@tanstack/react-query';
|
|
|
-import { useSilverWisdomDetail } from '@/client/mobile/hooks/useSilverWisdomData';
|
|
|
+import { useSilverWisdomDetail, wisdomCategories } from '@/client/mobile/hooks/useSilverWisdomData';
|
|
|
import { silverKnowledgeClient } from '@/client/api';
|
|
|
import { ChevronLeftIcon, HeartIcon, BookmarkIcon, ShareIcon, ArrowDownTrayIcon, EyeIcon } from '@heroicons/react/24/outline';
|
|
|
import { HeartIcon as HeartSolidIcon, BookmarkIcon as BookmarkSolidIcon } from '@heroicons/react/24/solid';
|
|
|
@@ -47,6 +47,36 @@ const SilverWisdomDetailPage: React.FC = () => {
|
|
|
|
|
|
const { data, isLoading, error } = useSilverWisdomDetail(Number(id));
|
|
|
|
|
|
+ // 检查点赞和收藏状态
|
|
|
+ const checkInteractionStatus = async () => {
|
|
|
+ try {
|
|
|
+ // 这里可以添加检查用户是否已点赞/收藏的API调用
|
|
|
+ // 暂时使用本地状态
|
|
|
+ } catch (error) {
|
|
|
+ console.error('检查交互状态失败:', error);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleLike = async () => {
|
|
|
+ try {
|
|
|
+ // 实际项目中这里应该调用点赞API
|
|
|
+ setIsLiked(!isLiked);
|
|
|
+ console.log(isLiked ? '取消点赞' : '点赞成功');
|
|
|
+ } catch (error) {
|
|
|
+ console.error('点赞失败:', error);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleBookmark = async () => {
|
|
|
+ try {
|
|
|
+ // 实际项目中这里应该调用收藏API
|
|
|
+ setIsBookmarked(!isBookmarked);
|
|
|
+ console.log(isBookmarked ? '取消收藏' : '收藏成功');
|
|
|
+ } catch (error) {
|
|
|
+ console.error('收藏失败:', error);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
if (isLoading) {
|
|
|
return (
|
|
|
<div className="min-h-screen flex items-center justify-center" style={{ backgroundColor: COLORS.ink.light }}>
|
|
|
@@ -77,19 +107,66 @@ const SilverWisdomDetailPage: React.FC = () => {
|
|
|
|
|
|
const knowledge = data.data;
|
|
|
|
|
|
- const handleShare = () => {
|
|
|
- if (navigator.share) {
|
|
|
- navigator.share({
|
|
|
- title: knowledge.title,
|
|
|
- text: knowledge.content.substring(0, 100) + '...',
|
|
|
- url: window.location.href,
|
|
|
- });
|
|
|
+ // 解析标签
|
|
|
+ const tags = knowledge.tags ? knowledge.tags.split(',').map(tag => tag.trim()).filter(tag => tag) : [];
|
|
|
+
|
|
|
+ const handleShare = async () => {
|
|
|
+ try {
|
|
|
+ if (navigator.share) {
|
|
|
+ await navigator.share({
|
|
|
+ title: knowledge.title,
|
|
|
+ text: knowledge.content.substring(0, 100) + '...',
|
|
|
+ url: window.location.href,
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ // 降级处理:复制到剪贴板
|
|
|
+ await navigator.clipboard.writeText(window.location.href);
|
|
|
+ alert('链接已复制到剪贴板');
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('分享失败:', error);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
const handleDownload = () => {
|
|
|
if (knowledge.attachment) {
|
|
|
- window.open(knowledge.attachment, '_blank');
|
|
|
+ // 创建临时链接下载,避免直接打开可能的安全问题
|
|
|
+ const link = document.createElement('a');
|
|
|
+ link.href = knowledge.attachment;
|
|
|
+ link.download = knowledge.attachmentName || '附件';
|
|
|
+ link.target = '_blank';
|
|
|
+ link.rel = 'noopener noreferrer';
|
|
|
+ document.body.appendChild(link);
|
|
|
+ link.click();
|
|
|
+ document.body.removeChild(link);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const getFileExtension = (filename: string) => {
|
|
|
+ return filename.split('.').pop()?.toLowerCase() || '';
|
|
|
+ };
|
|
|
+
|
|
|
+ const getFileIcon = (filename: string) => {
|
|
|
+ const ext = getFileExtension(filename);
|
|
|
+ switch (ext) {
|
|
|
+ case 'pdf':
|
|
|
+ return '📄';
|
|
|
+ case 'doc':
|
|
|
+ case 'docx':
|
|
|
+ return '📝';
|
|
|
+ case 'xls':
|
|
|
+ case 'xlsx':
|
|
|
+ return '📊';
|
|
|
+ case 'ppt':
|
|
|
+ case 'pptx':
|
|
|
+ return '📊';
|
|
|
+ case 'jpg':
|
|
|
+ case 'jpeg':
|
|
|
+ case 'png':
|
|
|
+ case 'gif':
|
|
|
+ return '🖼️';
|
|
|
+ default:
|
|
|
+ return '📎';
|
|
|
}
|
|
|
};
|
|
|
|
|
|
@@ -163,28 +240,41 @@ const SilverWisdomDetailPage: React.FC = () => {
|
|
|
{getReadTime(knowledge.content)}分钟阅读
|
|
|
</span>
|
|
|
</div>
|
|
|
- <div className="flex items-center mt-2 space-x-2">
|
|
|
- <span className="px-2 py-1 rounded-full text-xs" style={{
|
|
|
- backgroundColor: COLORS.accent.blue,
|
|
|
- color: 'white'
|
|
|
- }}>
|
|
|
- {knowledge.category?.name || '未分类'}
|
|
|
- </span>
|
|
|
- {knowledge.tags && (
|
|
|
- <span className="px-2 py-1 rounded-full text-xs" style={{
|
|
|
- backgroundColor: COLORS.ink.medium,
|
|
|
- color: COLORS.text.primary
|
|
|
+ <div className="flex items-center mt-2 space-x-2 flex-wrap">
|
|
|
+ {knowledge.categoryId && (
|
|
|
+ <span className="px-2 py-1 rounded-full text-xs" style={{
|
|
|
+ backgroundColor: COLORS.accent.blue,
|
|
|
+ color: 'white'
|
|
|
}}>
|
|
|
- {knowledge.tags}
|
|
|
+ {wisdomCategories.find(c => c.value === knowledge.categoryId)?.label || '未分类'}
|
|
|
</span>
|
|
|
)}
|
|
|
+ {tags.map((tag, index) => (
|
|
|
+ <span key={index} className="px-2 py-1 rounded-full text-xs" style={{
|
|
|
+ backgroundColor: COLORS.ink.medium,
|
|
|
+ color: COLORS.text.primary
|
|
|
+ }}>
|
|
|
+ {tag}
|
|
|
+ </span>
|
|
|
+ ))}
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
{/* 内容正文 */}
|
|
|
<div className="mb-8">
|
|
|
- <div className={`${FONT_STYLES.body} whitespace-pre-wrap`} style={{ color: COLORS.text.primary }}>
|
|
|
- {knowledge.content}
|
|
|
+ <div
|
|
|
+ className={`${FONT_STYLES.body} whitespace-pre-wrap leading-relaxed`}
|
|
|
+ style={{
|
|
|
+ color: COLORS.text.primary,
|
|
|
+ wordBreak: 'break-word',
|
|
|
+ lineHeight: '1.8'
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ {knowledge.content.split('\n').map((paragraph, index) => (
|
|
|
+ <p key={index} className="mb-4">
|
|
|
+ {paragraph.trim() || <br />}
|
|
|
+ </p>
|
|
|
+ ))}
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
@@ -194,50 +284,53 @@ const SilverWisdomDetailPage: React.FC = () => {
|
|
|
<h3 className={FONT_STYLES.sectionTitle} style={{ color: COLORS.text.primary }}>
|
|
|
附件资料
|
|
|
</h3>
|
|
|
- <div className="mt-4 rounded-xl border p-4"
|
|
|
- style={{
|
|
|
+ <div className="mt-4 rounded-xl border p-4"
|
|
|
+ style={{
|
|
|
borderColor: COLORS.ink.medium,
|
|
|
backgroundColor: 'rgba(255,255,255,0.8)'
|
|
|
}}
|
|
|
>
|
|
|
<div className="flex items-center justify-between">
|
|
|
- <div className="flex items-center">
|
|
|
- <ArrowDownTrayIcon className="w-5 h-5 mr-2" style={{ color: COLORS.ink.dark }} />
|
|
|
- <span style={{ color: COLORS.text.primary }}>{knowledge.attachmentName || '附件'}</span>
|
|
|
+ <div className="flex items-center flex-1 min-w-0">
|
|
|
+ <span className="text-2xl mr-2">{getFileIcon(knowledge.attachmentName || '')}</span>
|
|
|
+ <div className="min-w-0">
|
|
|
+ <p className="font-medium truncate" style={{ color: COLORS.text.primary }}>
|
|
|
+ {knowledge.attachmentName || '附件'}
|
|
|
+ </p>
|
|
|
+ <p className="text-xs" style={{ color: COLORS.text.secondary }}>
|
|
|
+ {getFileExtension(knowledge.attachmentName || '').toUpperCase()} 文件
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div className="flex space-x-2">
|
|
|
- <button
|
|
|
+ <div className="flex space-x-2 ml-2">
|
|
|
+ <button
|
|
|
onClick={handleDownload}
|
|
|
- className="px-3 py-1 rounded-full text-sm transition-colors"
|
|
|
- style={{
|
|
|
- backgroundColor: COLORS.ink.dark,
|
|
|
- color: 'white'
|
|
|
+ className="px-3 py-1 rounded-full text-sm transition-colors flex items-center"
|
|
|
+ style={{
|
|
|
+ backgroundColor: COLORS.ink.dark,
|
|
|
+ color: 'white'
|
|
|
}}
|
|
|
>
|
|
|
+ <ArrowDownTrayIcon className="w-4 h-4 mr-1" />
|
|
|
下载
|
|
|
</button>
|
|
|
- <button
|
|
|
- onClick={() => window.open(knowledge.attachment, '_blank')}
|
|
|
- className="px-3 py-1 rounded-full text-sm border transition-colors"
|
|
|
- style={{
|
|
|
- borderColor: COLORS.ink.medium,
|
|
|
- color: COLORS.ink.dark
|
|
|
- }}
|
|
|
- >
|
|
|
- 预览
|
|
|
- </button>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- {/* PDF预览 */}
|
|
|
- <div className="mt-4">
|
|
|
- <iframe
|
|
|
- src={knowledge.attachment}
|
|
|
- className="w-full h-64 rounded-lg border"
|
|
|
- style={{ borderColor: COLORS.ink.medium }}
|
|
|
- title={knowledge.attachmentName || 'PDF预览'}
|
|
|
- />
|
|
|
- </div>
|
|
|
+ {/* 文件预览 - 仅对支持的格式显示 */}
|
|
|
+ {getFileExtension(knowledge.attachmentName || '') === 'pdf' && (
|
|
|
+ <div className="mt-4 border-t pt-4" style={{ borderColor: COLORS.ink.medium }}>
|
|
|
+ <p className="text-sm mb-2" style={{ color: COLORS.text.secondary }}>
|
|
|
+ 文件预览:
|
|
|
+ </p>
|
|
|
+ <iframe
|
|
|
+ src={knowledge.attachment}
|
|
|
+ className="w-full h-64 rounded-lg border"
|
|
|
+ style={{ borderColor: COLORS.ink.medium }}
|
|
|
+ title={knowledge.attachmentName || 'PDF预览'}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
</div>
|
|
|
</div>
|
|
|
)}
|
|
|
@@ -255,18 +348,43 @@ const SilverWisdomDetailPage: React.FC = () => {
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
+
|
|
|
+ {/* 相关推荐 */}
|
|
|
+ <div className="mb-8">
|
|
|
+ <h3 className={FONT_STYLES.sectionTitle} style={{ color: COLORS.text.primary }}>
|
|
|
+ 相关推荐
|
|
|
+ </h3>
|
|
|
+ <div className="mt-4 space-y-3">
|
|
|
+ {[1, 2, 3].map((i) => (
|
|
|
+ <div key={i} className="p-3 rounded-lg border transition-colors cursor-pointer"
|
|
|
+ style={{
|
|
|
+ borderColor: COLORS.ink.medium,
|
|
|
+ backgroundColor: 'rgba(255,255,255,0.6)'
|
|
|
+ }}
|
|
|
+ onClick={() => navigate(`/silver-wisdom/${Number(id) + i}`)}
|
|
|
+ >
|
|
|
+ <h4 className="font-medium text-sm mb-1" style={{ color: COLORS.text.primary }}>
|
|
|
+ 相关文章 {i}
|
|
|
+ </h4>
|
|
|
+ <p className="text-xs" style={{ color: COLORS.text.secondary }}>
|
|
|
+ 基于您的浏览历史推荐
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+ ))}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
|
|
|
{/* 底部互动栏 */}
|
|
|
<div className="fixed bottom-0 left-0 right-0 border-t p-4"
|
|
|
- style={{
|
|
|
+ style={{
|
|
|
backgroundColor: COLORS.ink.light,
|
|
|
- borderColor: COLORS.ink.medium
|
|
|
+ borderColor: COLORS.ink.medium
|
|
|
}}
|
|
|
>
|
|
|
<div className="flex items-center justify-around max-w-md mx-auto">
|
|
|
- <button
|
|
|
- onClick={() => setIsLiked(!isLiked)}
|
|
|
+ <button
|
|
|
+ onClick={handleLike}
|
|
|
className="flex flex-col items-center p-2 rounded-lg transition-colors"
|
|
|
style={{ color: isLiked ? COLORS.accent.red : COLORS.text.secondary }}
|
|
|
>
|
|
|
@@ -275,11 +393,13 @@ const SilverWisdomDetailPage: React.FC = () => {
|
|
|
) : (
|
|
|
<HeartIcon className="w-6 h-6" />
|
|
|
)}
|
|
|
- <span className="text-xs mt-1">点赞</span>
|
|
|
+ <span className="text-xs mt-1">
|
|
|
+ {isLiked ? '已点赞' : '点赞'}
|
|
|
+ </span>
|
|
|
</button>
|
|
|
|
|
|
- <button
|
|
|
- onClick={() => setIsBookmarked(!isBookmarked)}
|
|
|
+ <button
|
|
|
+ onClick={handleBookmark}
|
|
|
className="flex flex-col items-center p-2 rounded-lg transition-colors"
|
|
|
style={{ color: isBookmarked ? COLORS.accent.blue : COLORS.text.secondary }}
|
|
|
>
|
|
|
@@ -288,10 +408,12 @@ const SilverWisdomDetailPage: React.FC = () => {
|
|
|
) : (
|
|
|
<BookmarkIcon className="w-6 h-6" />
|
|
|
)}
|
|
|
- <span className="text-xs mt-1">收藏</span>
|
|
|
+ <span className="text-xs mt-1">
|
|
|
+ {isBookmarked ? '已收藏' : '收藏'}
|
|
|
+ </span>
|
|
|
</button>
|
|
|
|
|
|
- <button
|
|
|
+ <button
|
|
|
onClick={handleShare}
|
|
|
className="flex flex-col items-center p-2 rounded-lg transition-colors"
|
|
|
style={{ color: COLORS.text.secondary }}
|