import { useEffect, useState, useCallback } from 'react'; import { Card, Row, Col, Input, Tag, Button, Space, Typography, Modal, Rate, message, Empty, Tooltip, Form, Input as TextArea, Alert, Spin, } from 'antd'; import { SearchOutlined, DownloadOutlined, AppstoreOutlined, StarOutlined, } from '@ant-design/icons'; import { listMarketEntries, installFromMarket, listMarketReviews, submitMarketReview, listPlugins, type MarketEntry, type MarketReview, } from '../api/plugins'; const { Title, Text, Paragraph } = Typography; const CATEGORY_COLORS: Record = { '财务': '#059669', 'CRM': '#2563EB', '进销存': '#9333EA', '生产': '#dc2626', '人力资源': '#d97706', '基础': '#475569', }; export default function PluginMarket() { const [plugins, setPlugins] = useState([]); const [loading, setLoading] = useState(true); const [searchText, setSearchText] = useState(''); const [selectedCategory, setSelectedCategory] = useState(null); const [detailVisible, setDetailVisible] = useState(false); const [selectedPlugin, setSelectedPlugin] = useState(null); const [installing, setInstalling] = useState(null); // 当前已安装的插件列表(用于标识已安装状态) const [installedIds, setInstalledIds] = useState>(new Set()); // 评论区 const [reviews, setReviews] = useState([]); const [reviewForm] = Form.useForm(); const [submittingReview, setSubmittingReview] = useState(false); const fetchInstalled = useCallback(async () => { try { const result = await listPlugins(1); const ids = new Set(result.data.map((p) => p.id)); setInstalledIds(ids); } catch { // 静默失败 } }, []); const loadMarketPlugins = useCallback(async () => { setLoading(true); try { const result = await listMarketEntries({ search: searchText || undefined, category: selectedCategory || undefined }); setPlugins(result.data); } catch { message.error('加载插件市场失败'); } setLoading(false); }, [searchText, selectedCategory]); useEffect(() => { fetchInstalled(); }, [fetchInstalled]); useEffect(() => { loadMarketPlugins(); }, [loadMarketPlugins]); const categories = Array.from(new Set(plugins.map((p) => p.category).filter((c): c is string => Boolean(c)))); const showDetail = async (plugin: MarketEntry) => { setSelectedPlugin(plugin); setDetailVisible(true); try { const result = await listMarketReviews(plugin.id); setReviews(result); } catch { setReviews([]); } }; const handleInstall = async (plugin: MarketEntry) => { setInstalling(plugin.id); try { await installFromMarket(plugin.id); message.success(`${plugin.name} 安装成功`); fetchInstalled(); loadMarketPlugins(); } catch (e: unknown) { const msg = e instanceof Error ? e.message : '安装失败'; message.error(msg); } setInstalling(null); }; const handleSubmitReview = async () => { if (!selectedPlugin) return; try { const values = await reviewForm.validateFields(); setSubmittingReview(true); await submitMarketReview(selectedPlugin.id, values); message.success('评分提交成功'); reviewForm.resetFields(); // 刷新评论和列表 const result = await listMarketReviews(selectedPlugin.id); setReviews(result); loadMarketPlugins(); } catch { // 表单验证失败静默 } setSubmittingReview(false); }; return (
<AppstoreOutlined /> 插件市场 发现和安装行业插件,扩展 ERP 能力
{/* 搜索和分类 */}
} placeholder="搜索插件..." value={searchText} onChange={(e) => setSearchText(e.target.value)} style={{ width: 300 }} allowClear /> {categories.map((cat) => ( ))}
{/* 插件卡片网格 */} {plugins.length === 0 && !loading ? ( ) : ( {plugins.map((plugin) => ( showDetail(plugin)} style={{ height: '100%' }} >
{plugin.name} {plugin.category}
{plugin.description ?? '暂无描述'}
v{plugin.version} {plugin.author && {plugin.author}} {plugin.rating_count > 0 ? plugin.rating_avg.toFixed(1) : '-'}
{installedIds.has(plugin.plugin_id) && ( 已安装 )}
))}
)}
{/* 详情弹窗 */} { setDetailVisible(false); reviewForm.resetFields(); }} footer={null} width={640} > {selectedPlugin && (
{selectedPlugin.category} v{selectedPlugin.version} by {selectedPlugin.author ?? '未知'} {selectedPlugin.download_count} 次下载
{selectedPlugin.description ?? '暂无描述'} {selectedPlugin.changelog && (
更新日志 {selectedPlugin.changelog}
)} {selectedPlugin.tags && selectedPlugin.tags.length > 0 && (
{selectedPlugin.tags.map((tag) => ( {tag} ))}
)}
{selectedPlugin.rating_count} 评分
{/* 评论区 */}
用户评价 ({reviews.length}) {reviews.length > 0 && (
{reviews.map((review) => (
{review.created_at ? new Date(review.created_at).toLocaleDateString() : ''} {review.review_text && ( {review.review_text} )}
))}
)} {reviews.length === 0 && ( )} {installedIds.has(selectedPlugin.plugin_id) && (
)}
)}
); }