682 lines
24 KiB
TypeScript
682 lines
24 KiB
TypeScript
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<LightAppSquareProps> = ({ onNavigate }) => {
|
|
const { theme } = useTheme();
|
|
const isDark = theme === 'dark';
|
|
const [searchQuery, setSearchQuery] = useState('');
|
|
const [categoryFilter, setCategoryFilter] = useState<AppCategory | 'all'>('all');
|
|
const [recentApps, setRecentApps] = useState<LightAppType[]>(['sentiment', 'event', 'account-pg']);
|
|
const [visible, setVisible] = useState(false);
|
|
const [analysisData, setAnalysisData] = useState<analysisDataItem>({
|
|
appName: '',
|
|
appDesc: '',
|
|
appIcon: '',
|
|
jumpUrl: '',
|
|
appType: 2,
|
|
});
|
|
const [analysisList, setAnalysisList] = useState<analysisListData>(
|
|
{
|
|
internalList: [], // 内部应用
|
|
externalList: [], // 外部应用
|
|
})
|
|
const [fileFail, setFileFail] = useState<UploadFile[]>([
|
|
// 如果需要默认回显图片,可以在这里初始化
|
|
// { 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<AppCategory, LightApp[]> = {
|
|
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<AppCategory, { text: string; bg: string }> = {
|
|
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<string> => {
|
|
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 (
|
|
<div className="space-y-6">
|
|
{/* 顶部工具栏 */}
|
|
<div className={`rounded-xl border p-4 ${isDark ? 'bg-slate-800/50 border-slate-700' : 'bg-white border-gray-200'
|
|
}`}>
|
|
<div className="flex flex-col lg:flex-row lg:items-center gap-4">
|
|
{/* 面包屑和标题 */}
|
|
<div className="flex items-center gap-2 flex-1">
|
|
<Layout className={`w-5 h-5 ${isDark ? 'text-blue-400' : 'text-red-500'}`} />
|
|
<span className={`text-sm ${isDark ? 'text-slate-400' : 'text-gray-500'}`}>轻应用中心 /</span>
|
|
<span className={`text-sm font-medium ${isDark ? 'text-white' : 'text-gray-900'}`}>应用广场</span>
|
|
</div>
|
|
|
|
{/* 搜索框 */}
|
|
<div className="flex-1 max-w-md">
|
|
<div className="relative">
|
|
<Search className={`absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 ${isDark ? 'text-slate-500' : 'text-gray-400'}`} />
|
|
<input
|
|
type="text"
|
|
value={searchQuery}
|
|
onChange={(e) => 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'
|
|
}`}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 筛选和操作 */}
|
|
<div className="flex items-center gap-3">
|
|
<select
|
|
value={categoryFilter}
|
|
onChange={(e) => setCategoryFilter(e.target.value as AppCategory | 'all')}
|
|
className={`px-3 py-2 rounded-lg border text-sm transition-colors focus:outline-none focus:ring-2 ${isDark
|
|
? 'bg-slate-900 border-slate-600 text-slate-200 focus:border-blue-500'
|
|
: 'bg-gray-50 border-gray-300 text-gray-700 focus:border-red-500'
|
|
}`}
|
|
>
|
|
<option value="all">全部分类</option>
|
|
<option value="info">信息分析类</option>
|
|
<option value="account">账号分析类</option>
|
|
<option value="event">事件分析类</option>
|
|
<option value="topic">专题分析类</option>
|
|
</select>
|
|
<button
|
|
onClick={() => onNavigate('light-app-records')}
|
|
className={`px-4 py-2 rounded-lg text-sm font-medium flex items-center gap-2 transition-colors ${isDark
|
|
? 'bg-slate-700 text-slate-300 hover:bg-slate-600'
|
|
: 'bg-gray-100 text-gray-600 hover:bg-gray-200'
|
|
}`}
|
|
>
|
|
<Clock className="w-4 h-4" />
|
|
运行记录
|
|
</button>
|
|
<Button theme="primary" onClick={showDialog}>
|
|
<span className="flex items-center">
|
|
<Plus className="w-4 h-4 mr-[10px]" />
|
|
添加
|
|
</span>
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 最近使用 */}
|
|
{recentApps.length > 0 && searchQuery === '' && categoryFilter === 'all' && (
|
|
<div className={`rounded-xl border p-4 ${isDark ? 'bg-slate-800/50 border-slate-700' : 'bg-white border-gray-200'
|
|
}`}>
|
|
<div className="flex items-center gap-2 mb-4">
|
|
<Clock className={`w-4 h-4 ${isDark ? 'text-blue-400' : 'text-red-500'}`} />
|
|
<h3 className={`text-sm font-medium ${isDark ? 'text-slate-300' : 'text-gray-700'}`}>最近使用</h3>
|
|
</div>
|
|
<div className="flex flex-wrap gap-2">
|
|
{recentApps.map(appId => {
|
|
const app = lightApps.find(a => a.id === appId);
|
|
if (!app) return null;
|
|
const Icon = app.icon;
|
|
return (
|
|
<button
|
|
key={app.id}
|
|
onClick={() => handleLaunchApp(app)}
|
|
className={`px-4 py-2 rounded-lg border text-sm font-medium flex items-center gap-2 transition-all hover:scale-105 ${isDark
|
|
? `bg-slate-700/50 border-slate-600 text-slate-300 hover:bg-slate-700`
|
|
: `bg-gray-50 border-gray-200 text-gray-700 hover:bg-gray-100`
|
|
}`}
|
|
>
|
|
<Icon className={`w-4 h-4 ${app.iconColor}`} />
|
|
{app.name}
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* 应用分组区 */}
|
|
<div className="space-y-6">
|
|
{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 (
|
|
<div key={category} className="space-y-4">
|
|
{/* 分类标题 */}
|
|
<div className="flex items-center gap-2">
|
|
<h2 className={`text-lg font-semibold ${isDark ? 'text-white' : 'text-gray-900'}`}>
|
|
{categoryInfo?.categoryName}
|
|
</h2>
|
|
<span className={`px-2 py-0.5 rounded text-xs font-medium ${catColor.bg} ${catColor.text}`}>
|
|
{apps.length} 个应用
|
|
</span>
|
|
</div>
|
|
|
|
{/* 应用卡片网格 */}
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
{apps.map(app => {
|
|
const Icon = app.icon;
|
|
return (
|
|
<div
|
|
key={app.id}
|
|
className={`rounded-xl border p-5 transition-all duration-200 hover:shadow-lg group ${isDark
|
|
? 'bg-slate-800/50 border-slate-700 hover:border-slate-600'
|
|
: 'bg-white border-gray-200 hover:border-gray-300'
|
|
}`}
|
|
>
|
|
{/* 应用头部 */}
|
|
<div className="flex items-start gap-4 mb-4">
|
|
<div className={`p-3 rounded-lg ${app.bgColor}`}>
|
|
<Icon className={`w-6 h-6 ${app.iconColor}`} />
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<h3 className={`font-semibold group-hover:${app.iconColor} ${isDark ? 'text-white' : 'text-gray-900'}`}>
|
|
{app.name}
|
|
</h3>
|
|
<p className={`mt-1 text-sm line-clamp-2 ${isDark ? 'text-slate-400' : 'text-gray-500'}`}>
|
|
{app.description}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 输入输出 */}
|
|
<div className={`text-xs space-y-1 mb-4 ${isDark ? 'text-slate-500' : 'text-gray-400'}`}>
|
|
<div className="flex items-start gap-2">
|
|
<span className="font-medium">输入:</span>
|
|
<span>{app.input}</span>
|
|
</div>
|
|
<div className="flex items-start gap-2">
|
|
<span className="font-medium">输出:</span>
|
|
<span>{app.output}</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 立即使用按钮 */}
|
|
<button
|
|
onClick={() => handleLaunchApp(app)}
|
|
className={`w-full py-2.5 rounded-lg font-medium flex items-center justify-center gap-2 transition-all ${isDark
|
|
? 'bg-blue-500/20 text-blue-400 hover:bg-blue-500/30 border border-blue-500/30'
|
|
: 'bg-red-50 text-red-600 hover:bg-red-100 border border-red-200'
|
|
}`}
|
|
>
|
|
<PlayCircle className="w-4 h-4" />
|
|
立即使用
|
|
</button>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
{/* 空状态 */}
|
|
{filteredApps.length === 0 && (
|
|
<div className={`text-center py-12 rounded-xl border ${isDark ? 'bg-slate-800/50 border-slate-700' : 'bg-white border-gray-200'
|
|
}`}>
|
|
<Search className={`w-12 h-12 mx-auto mb-4 ${isDark ? 'text-slate-600' : 'text-gray-300'}`} />
|
|
<p className={`text-sm ${isDark ? 'text-slate-400' : 'text-gray-500'}`}>
|
|
未找到匹配的应用,请尝试其他搜索词或筛选条件
|
|
</p>
|
|
</div>
|
|
)}
|
|
|
|
{/* 底部提示 */}
|
|
<div className={`p-4 rounded-xl border ${isDark ? 'bg-blue-500/10 border-blue-500/30' : 'bg-blue-50 border-blue-200'
|
|
}`}>
|
|
<div className="flex items-start gap-3">
|
|
<Sparkles className={`w-5 h-5 mt-0.5 ${isDark ? 'text-blue-400' : 'text-blue-500'}`} />
|
|
<div>
|
|
<p className={`text-sm font-medium ${isDark ? 'text-blue-300' : 'text-blue-800'}`}>
|
|
关于轻应用中心
|
|
</p>
|
|
<p className={`mt-1 text-sm ${isDark ? 'text-blue-400/80' : 'text-blue-700'}`}>
|
|
轻应用中心提供了一系列快速分析工具,适合处理临时性的分析需求。相比完整的任务级应用,轻应用更加轻量便捷,可以快速调用多种工具完成分析工作。运行结果可保存快照、导出或转为正式任务。
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<Dialog
|
|
header="添加应用"
|
|
visible={visible}
|
|
onClose={handleClose} // 监听关闭事件
|
|
onConfirm={handleOk} // 点击确定按钮
|
|
onCancel={handleClose} // 点击取消按钮
|
|
closeOnOverlayClick={true} // 点击遮罩层是否关闭
|
|
>
|
|
<div className='mb-[10px]'>
|
|
<Upload
|
|
// 对应 v-model
|
|
files={fileFail}
|
|
onChange={handleChange}
|
|
// 对应 theme="image"
|
|
theme="image"
|
|
requestMethod={imgUpload}
|
|
// 对应 accept
|
|
accept="image/*"
|
|
tips="系统logo"
|
|
// 对应 :show-image-file-name
|
|
showImageFileName={showImageFileName}
|
|
// 对应 :format-response
|
|
formatResponse={formatResponse}
|
|
// 可选:限制上传数量(演示单张)
|
|
multiple={false}
|
|
// 可选:上传失败时的提示(如果接口直接返回 4xx/5xx)
|
|
onFail={({ e, file }) => {
|
|
MessagePlugin.error(`文件 ${file.name} 上传失败`);
|
|
}} />
|
|
</div>
|
|
<div className='mb-[10px] overflow-hidden'>
|
|
<Row>
|
|
<Col span={2}>
|
|
<div>名称:</div>
|
|
</Col>
|
|
<Col span={9}>
|
|
<Input placeholder="请输入应用名称" value={analysisData.appName} onChange={(val) => handleDataChange('appName', val)} clearable />
|
|
</Col>
|
|
</Row>
|
|
</div>
|
|
<div className='mb-[10px] overflow-hidden'>
|
|
<Row>
|
|
<Col span={2}>
|
|
<div>详情:</div>
|
|
</Col>
|
|
<Col span={9}>
|
|
<Textarea placeholder="请输入应用详情" value={analysisData.appDesc} onChange={(val) => handleDataChange('appDesc', val)} autosize />
|
|
</Col>
|
|
</Row>
|
|
</div>
|
|
<div className='mb-[10px] overflow-hidden'>
|
|
<Row>
|
|
<Col span={2}>
|
|
<div>类型:</div>
|
|
</Col>
|
|
<Col span={9}>
|
|
<RadioGroup
|
|
variant="primary-filled" // 对应 variant="primary-filled"
|
|
value={analysisData.appType} // 绑定状态值
|
|
onChange={(value) => handleDataChange('appType', Number(value))}
|
|
>
|
|
{/* 3. 使用 RadioButton */}
|
|
<RadioButton value={1}>内部应用</RadioButton>
|
|
<RadioButton value={2}>外部应用</RadioButton>
|
|
<RadioButton value={3}>第三方集成</RadioButton>
|
|
</RadioGroup>
|
|
</Col>
|
|
</Row>
|
|
</div>
|
|
<div className='mb-[10px] overflow-hidden'>
|
|
<Row>
|
|
<Col span={2}>
|
|
<div>链接:</div>
|
|
</Col>
|
|
<Col span={9}>
|
|
<Input placeholder="请输入链接" value={analysisData.jumpUrl} onChange={(val) => handleDataChange('jumpUrl', val)} clearable />
|
|
</Col>
|
|
</Row>
|
|
</div>
|
|
</Dialog>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default LightAppSquare;
|