import React, { useState, useMemo, useEffect } from 'react'; import { useTheme } from '../contexts/ThemeContext'; import { Search, ChevronRight, PlayCircle, Clock, Star, FileText, User, Zap, BookOpen, TrendingUp, MessageSquare, Network, Link2, Filter, Layout, Sparkles, BarChart3, Users, Target, PieChart, Send, Plus, } from 'lucide-react'; import { Button, Dialog, Radio, Upload, Input, MessagePlugin, Row, Col, Textarea } from 'tdesign-react'; import { get } from '../lib/api'; import type { UploadFile, UploadProps } from 'tdesign-react'; import { requestCrudService } from '@/service/request-axios-service'; interface LightAppSquareProps { onNavigate: (page: string, appId?: string) => void; } // 定义轻应用类型 export type LightAppType = | 'info-ty' // 信息TY | 'info-pg' // 信息PG | 'info-spread' // 信息传播分析 | 'account-pg' // 账号PG | 'sentiment' // 三情分析 | 'event' // 事件分析 | 'topic'; // 专题分析 // 应用分类 type AppCategory = 'info' | 'account' | 'event' | 'topic'; interface LightApp { id: LightAppType; name: string; description: string; category: AppCategory; categoryName: string; input: string; output: string; icon: React.ElementType; color: string; bgColor: string; borderColor: string; iconColor: string; dimensions?: string[]; // 可选的额外分析维度 } interface analysisDataItem { appName: string; appDesc: string; appIcon: string; jumpUrl: string; appType: number } interface analysisListItem { categoryName: string; category: string; appName: string; appDesc: string; appIcon: string; jumpUrl: string; appType: number } interface analysisListData { internalList: analysisListItem[]; externalList: analysisListItem[]; } // 应用数据 const lightApps: LightApp[] = [ // 信息分析类 { id: 'info-ty', name: '信息TY', description: '围绕单条或多条信息进行TY向分析', category: 'info', categoryName: '信息分析类', input: '信息文本/链接/素材', output: '信息特征、重点摘要、分析判断', icon: FileText, color: 'blue', bgColor: 'bg-blue-500/10', borderColor: 'border-blue-500/30', iconColor: 'text-blue-500', dimensions: ['基础分析', '风险提示', '摘要结论'], }, { id: 'info-pg', name: '信息PG', description: '围绕单条或多条信息进行PG向分析', category: 'info', categoryName: '信息分析类', input: '信息文本/链接/素材', output: '画像特征、相关判断', icon: User, color: 'cyan', bgColor: 'bg-cyan-500/10', borderColor: 'border-cyan-500/30', iconColor: 'text-cyan-500', dimensions: ['账号画像', '内容倾向', '行为特征'], }, { id: 'info-spread', name: '信息传播分析', description: '分析信息传播路径、传播范围与传播表现', category: 'info', categoryName: '信息分析类', input: '信息链接/信息集合', output: '传播过程、传播节点、传播影响', icon: Network, color: 'teal', bgColor: 'bg-teal-500/10', borderColor: 'border-teal-500/30', iconColor: 'text-teal-500', dimensions: ['传播路径', '传播节点', '传播表现', '影响摘要'], }, // 账号分析类 { id: 'account-pg', name: '账号PG', description: '围绕账号内容与行为特征进行PG分析', category: 'account', categoryName: '账号分析类', input: '账号ID/账号链接/内容集合', output: '账号画像、内容倾向、行为特征', icon: Users, color: 'purple', bgColor: 'bg-purple-500/10', borderColor: 'border-purple-500/30', iconColor: 'text-purple-500', dimensions: ['账号画像', '内容倾向', '行为特征', '影响判断'], }, // 事件分析类 { id: 'sentiment', name: '三情分析', description: '对事件进行舆情、社情、民情综合分析', category: 'event', categoryName: '事件分析类', input: '事件关键词/事件链接/素材', output: '情绪概览、关注焦点、风险提示', icon: MessageSquare, color: 'orange', bgColor: 'bg-orange-500/10', borderColor: 'border-orange-500/30', iconColor: 'text-orange-500', dimensions: ['舆情分析', '社情分析', '民情分析', '风险提示'], }, { id: 'event', name: '事件分析', description: '围绕某个事件进行整体分析', category: 'event', categoryName: '事件分析类', input: '事件名称/事件材料/事件链接', output: '事件脉络、关键节点、风险判断', icon: Target, color: 'red', bgColor: 'bg-red-500/10', borderColor: 'border-red-500/30', iconColor: 'text-red-500', dimensions: ['事件脉络', '关键节点', '风险判断', '初步建议'], }, // 专题分析类 { id: 'topic', name: '专题分析', description: '对某专题做集中分析', category: 'topic', categoryName: '专题分析类', input: '专题名称/专题材料/文本集合', output: '专题结构、核心问题、观点聚合、结论建议', icon: BookOpen, color: 'green', bgColor: 'bg-green-500/10', borderColor: 'border-green-500/30', iconColor: 'text-green-500', dimensions: ['专题结构', '核心问题', '观点聚合', '结论建议'], }, ]; const categoryOrder: AppCategory[] = ['info', 'account', 'event', 'topic']; const zncmUrl = import.meta.env.VITE_API_BASE_URL || ''; const LightAppSquare: React.FC = ({ onNavigate }) => { const { theme } = useTheme(); const isDark = theme === 'dark'; const [searchQuery, setSearchQuery] = useState(''); const [categoryFilter, setCategoryFilter] = useState('all'); const [recentApps, setRecentApps] = useState(['sentiment', 'event', 'account-pg']); const [visible, setVisible] = useState(false); const [analysisData, setAnalysisData] = useState({ appName: '', appDesc: '', appIcon: '', jumpUrl: '', appType: 2, }); const [analysisList, setAnalysisList] = useState( { internalList: [], // 内部应用 externalList: [], // 外部应用 }) const [fileFail, setFileFail] = useState([ // 如果需要默认回显图片,可以在这里初始化 // { name: '默认图片.png', url: 'https://tdesign.gtimg.com/site/avatar.jpg', status: 'fail' } ]); const showImageFileName = true; const service = requestCrudService; // 解构出 Group 和 Button,方便使用 const { Group: RadioGroup, Button: RadioButton } = Radio; // 用于格式化接口返回的数据,使其符合 TDesign 的要求 const formatResponse = (res: any) => { // 假设接口返回 { code: 0, data: { url: '...' } } // 如果接口报错,可以在这里处理,让组件显示失败状态 if (res.code !== 0) { return { error: res.message || '上传失败', url: '', name: '' }; } return { url: res.data.url, name: res.data.name, }; }; const handleChange: UploadProps['onChange'] = (files) => { setFileFail(files); console.log('当前文件列表:', files); }; // 过滤应用 const filteredApps = useMemo(() => { return lightApps.filter(app => { const matchesSearch = searchQuery === '' || app.name.toLowerCase().includes(searchQuery.toLowerCase()) || app.description.toLowerCase().includes(searchQuery.toLowerCase()) || app.input.toLowerCase().includes(searchQuery.toLowerCase()); const matchesCategory = categoryFilter === 'all' || app.category === categoryFilter; return matchesSearch && matchesCategory; }); }, [searchQuery, categoryFilter]); // 按分类分组 const groupedApps = useMemo(() => { const groups: Record = { info: [], account: [], event: [], topic: [], }; filteredApps.forEach(app => { groups[app.category].push(app); }); return groups; }, [filteredApps]); // 处理应用启动 const handleLaunchApp = (app: LightApp) => { console.log('启动应用:', app); // 更新最近使用 setRecentApps(prev => { const filtered = prev.filter(id => id !== app.id); return [app.id, ...filtered].slice(0, 5); }); onNavigate('light-app-run', app.id); }; // 获取分类颜色 const getCategoryColor = (category: AppCategory) => { const colors: Record = { info: { text: isDark ? 'text-blue-400' : 'text-blue-600', bg: isDark ? 'bg-blue-500/20' : 'bg-blue-50' }, account: { text: isDark ? 'text-purple-400' : 'text-purple-600', bg: isDark ? 'bg-purple-500/20' : 'bg-purple-50' }, event: { text: isDark ? 'text-orange-400' : 'text-orange-600', bg: isDark ? 'bg-orange-500/20' : 'bg-orange-50' }, topic: { text: isDark ? 'text-green-400' : 'text-green-600', bg: isDark ? 'bg-green-500/20' : 'bg-green-50' }, }; return colors[category]; }; // 获取应用列表 const analysisListSearch = async () => { const parmy = { appName: '', pageNum: 1, pageSize: 10, } const res = await service.post(`${zncmUrl}/app/analysis/list`, parmy) console.log(res); const result = res.data; const listData: analysisListData = { internalList: [], externalList: [], } result.records.map((item: analysisListItem) => { if (item.appType === 1) { item.category = 'info'; item.categoryName = '内部应用'; listData.internalList.push(item) } else if (item.appType === 2) { item.category = 'account'; item.categoryName = '外部应用'; listData.internalList.push(item) } }) } // 添加应用 const analysieAddListChange = async () => { const res = await service.post(`${zncmUrl}/app/analysis/add`, analysisData); console.log(res); MessagePlugin.success('添加成功'); } const showDialog = () => { setAnalysisData({ appName: '', appDesc: '', appIcon: '', jumpUrl: '', appType: 2, }) setVisible(true); }; const handleOk = async () => { await analysieAddListChange(); setVisible(false); }; const handleClose = () => { setVisible(false); }; const getBase64 = (file: File): Promise => { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = () => resolve(reader.result as string); reader.onerror = (error) => reject(error); }); }; const imgUpload: UploadProps['requestMethod'] = async (file: any) => { console.log('file', file); const base64 = await getBase64(file.raw) handleDataChange('appIcon', base64); return { status: 'success', // response 对象里的数据会传给 formatResponse response: { url: '', name: '', }, } } // 通用的处理函数 const handleDataChange = (key: keyof typeof analysisData, value: string | number) => { setAnalysisData((prev) => ({ ...prev, [key]: value, })); }; // 发送请求 useEffect(() => { analysisListSearch(); }, []); return (
{/* 顶部工具栏 */}
{/* 面包屑和标题 */}
轻应用中心 / 应用广场
{/* 搜索框 */}
setSearchQuery(e.target.value)} placeholder="搜索应用名称或分析对象..." className={`w-full pl-10 pr-4 py-2 rounded-lg border transition-colors focus:outline-none focus:ring-2 ${isDark ? 'bg-slate-900 border-slate-600 text-slate-200 placeholder-slate-500 focus:border-blue-500 focus:ring-blue-500/20' : 'bg-gray-50 border-gray-300 text-gray-900 placeholder-gray-400 focus:border-red-500 focus:ring-red-500/20' }`} />
{/* 筛选和操作 */}
{/* 最近使用 */} {recentApps.length > 0 && searchQuery === '' && categoryFilter === 'all' && (

最近使用

{recentApps.map(appId => { const app = lightApps.find(a => a.id === appId); if (!app) return null; const Icon = app.icon; return ( ); })}
)} {/* 应用分组区 */}
{categoryOrder.map(category => { const apps = groupedApps[category]; if (apps.length === 0) return null; const categoryInfo = lightApps.find(a => a.category === category); const catColor = getCategoryColor(category); return (
{/* 分类标题 */}

{categoryInfo?.categoryName}

{apps.length} 个应用
{/* 应用卡片网格 */}
{apps.map(app => { const Icon = app.icon; return (
{/* 应用头部 */}

{app.name}

{app.description}

{/* 输入输出 */}
输入: {app.input}
输出: {app.output}
{/* 立即使用按钮 */}
); })}
); })}
{/* 空状态 */} {filteredApps.length === 0 && (

未找到匹配的应用,请尝试其他搜索词或筛选条件

)} {/* 底部提示 */}

关于轻应用中心

轻应用中心提供了一系列快速分析工具,适合处理临时性的分析需求。相比完整的任务级应用,轻应用更加轻量便捷,可以快速调用多种工具完成分析工作。运行结果可保存快照、导出或转为正式任务。

{ MessagePlugin.error(`文件 ${file.name} 上传失败`); }} />
名称:
handleDataChange('appName', val)} clearable />
详情: