代码修改
This commit is contained in:
1 parent
8f5af46ec2
commit
8f75e12871
168 files changed
+76575
No files matched your search
@@ -0,0 +1,979 @@
|
||||
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;
|
||||
Reference in new issue
Block a user