Files
deerflow-code/web-code/pages/LightAppSquare.tsx
2026-05-27 19:43:15 +08:00

980 lines
30 KiB
TypeScript

import React, { useState, useMemo, useEffect, useRef } 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,
Loading,
Empty,
Pagination,
} from "tdesign-react";
import { get } from "../lib/api";
import type { UploadFile, UploadProps } from "tdesign-react";
import { requestCrudService } from "@/service/request-axios-service";
import { AppCard } from "@/components/square/AppCard";
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 {
id: number;
categoryName: string;
category: string;
appName: string;
appDesc: string;
appIcon: string;
jumpUrl: string;
appType: number;
}
interface analysisListData {
key: "info" | "account" | "event" | "topic";
list: 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 categoryConfig = {
info: {
title: "内部应用",
hoverColor: "group-hover:text-blue-500", // 内部应用 hover 蓝色
btnTheme: {
dark: "bg-blue-500/20 text-blue-400 hover:bg-blue-500/30 border-blue-500/30",
light: "bg-blue-50 text-blue-600 hover:bg-blue-100 border-blue-200",
},
},
account: {
title: "外部应用",
hoverColor: "group-hover:text-purple-500", // 外部应用 hover 紫色
btnTheme: {
dark: "bg-purple-500/20 text-purple-400 hover:bg-purple-500/30 border-purple-500/30",
light: "bg-red-50 text-red-600 hover:bg-red-100 border-red-200", // 保留你原本外部应用的红色风格
},
},
};
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 [historyApps, setHistoryApps] = useState([]);
const [visible, setVisible] = useState(false);
const [editDataShow, setEditDataShow] = useState(false);
const [analysisData, setAnalysisData] = useState<analysisDataItem>({
appName: "",
appDesc: "",
appIcon: "",
jumpUrl: "",
appType: 2,
});
const [analysisList, setAnalysisList] = useState<analysisListData[]>([
{ key: "info", list: [] },
{ key: "account", list: [] },
]);
const [fileFail, setFileFail] = useState<UploadFile[]>([
// 如果需要默认回显图片,可以在这里初始化
// { name: '默认图片.png', url: 'https://tdesign.gtimg.com/site/avatar.jpg', status: 'fail' }
]);
const [loading, setLoading] = useState<boolean>(false);
const [pageNum, setPageNum] = useState<number>(1);
const [pageSize, setPageSize] = useState<number>(10); // 每页显示 10 条
const [total, setTotal] = useState<number>(0);
const state = useRef({
pageNum: 1,
pageSize: 10,
});
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: searchQuery,
pageNum: state.current.pageNum,
pageSize: state.current.pageSize,
};
setLoading(true);
const res = await service.post(`${zncmUrl}/app/analysis/list`, parmy);
console.log(res);
setLoading(false);
const result = res.data;
const listData: analysisListData[] = [
{ key: "info", list: [] },
{ key: "account", list: [] },
];
result.records.map((item: analysisListItem) => {
if (item.appType === 1) {
item.category = "info";
item.categoryName = "内部应用";
listData[0].list.push(item);
} else if (item.appType === 2) {
item.category = "account";
item.categoryName = "外部应用";
listData[1].list.push(item);
}
setAnalysisList(listData);
});
setTotal(result.total);
};
// 添加应用
const analysieAddListChange = async () => {
const res = await service.post(`${zncmUrl}/app/analysis/add`, analysisData);
console.log(res);
MessagePlugin.success("添加成功");
await analysisListSearch();
};
// 回车处理
const handleKeyDown = async (e) => {
if ((e.metaKey || e.ctrlKey || e.shiftKey) && e.key === "Enter") {
e.preventDefault();
} else if (e.keyCode === 13) {
e.preventDefault();
await analysisListSearch();
} else if (e.key === "/") {
e.preventDefault();
}
};
// 添加应用
const showDialog = () => {
setAnalysisData({
appName: "",
appDesc: "",
appIcon: "",
jumpUrl: "",
appType: 2,
});
setFileFail([]);
setVisible(true);
};
const handleOk = async () => {
await analysieAddListChange();
setVisible(false);
};
const handleClose = () => {
setVisible(false);
};
// 编辑应用
const editShowChange = (item: analysisDataItem) => {
setAnalysisData(item);
if (item.appIcon) {
setFileFail([
{ name: "默认图片.png", url: item.appIcon, status: "fail" },
]);
}
setEditDataShow(true);
};
const handleOkEdit = () => {
editDataChange();
setEditDataShow(false);
};
const handleCloseEdit = () => {
setEditDataShow(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,
}));
};
// 删除应用
const delDataChange = async (item: analysisListItem) => {
console.log("删除", item);
setLoading(true);
const res = await service.get(`${zncmUrl}/app/analysis/delete/${item.id}`);
console.log(res);
MessagePlugin.success("操作成功");
await analysisListSearch();
};
const getHistoryApps = async () => {
const reqParam = {
limit: 5,
};
const res = await service.get(`${zncmUrl}/app/history/recent`, reqParam);
if (res && res.data) {
console.log({ resData: res.data });
setHistoryApps(res.data);
}
};
// 编辑应用
const editDataChange = async () => {
setLoading(true);
const res = await service.post(
`${zncmUrl}/app/analysis/update`,
analysisData
);
console.log(res);
MessagePlugin.success("操作成功");
await analysisListSearch();
};
const pageIndexChange = async (value: number) => {
state.current.pageNum = value;
setPageNum(value);
await analysisListSearch();
};
const pageSizeChange = async (value: number) => {
state.current.pageSize = value;
setPageSize(value);
await analysisListSearch();
};
// 发送请求
useEffect(() => {
analysisListSearch();
getHistoryApps();
}, []);
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)}
onKeyDown={(e) => handleKeyDown(e)}
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>
{/* 最近使用 */}
{historyApps.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">
{historyApps.map((app) => {
// const app = historyApps.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.appName}
</button>
);
})}
</div>
</div>
)}
{/* 应用分组区 */}
<Loading loading={loading} text="正在加载中...">
<div className="space-y-6">
{analysisList.map((section) => {
const config = categoryConfig[section.key];
const colorStyle = getCategoryColor(section.key);
return (
<div key={section.key} className="space-y-4">
{/* 标题区域 */}
<div className="flex items-center gap-2">
<h2
className={`text-lg font-semibold ${
isDark ? "text-white" : "text-gray-900"
}`}
>
{config.title}
</h2>
<span
className={`px-2 py-0.5 rounded text-xs font-medium ${colorStyle.bg} ${colorStyle.text}`}
>
{section.list.length} 个应用
</span>
</div>
{/* 卡片网格 */}
{section.list.length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{section.list.map((app) => (
<AppCard
key={app.id}
app={app}
isDark={isDark}
hoverColor={config.hoverColor}
btnClass={
isDark ? config.btnTheme.dark : config.btnTheme.light
}
delDataChange={delDataChange}
editShowChange={editShowChange}
/>
))}
</div>
) : (
<div
className={`text-center py-10 text-sm ${
isDark ? "text-slate-500" : "text-gray-400"
}`}
>
<Empty description="暂无数据" />
</div>
)}
</div>
);
})}
</div>
</Loading>
<div className="p-4 text-center">
<Pagination
total={total}
current={pageNum}
pageSize={pageSize}
onCurrentChange={(page) => pageIndexChange(Number(page))}
onPageSizeChange={(size) => pageSizeChange(Number(size))} // 每页数量变化时的回调
/>
</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>
<Dialog
header="编辑应用"
visible={editDataShow}
onClose={handleCloseEdit} // 监听关闭事件
onConfirm={handleOkEdit} // 点击确定按钮
onCancel={handleCloseEdit} // 点击取消按钮
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;