perf: replace shell icon bundle
This commit is contained in:
+15
-30
@@ -1,19 +1,3 @@
|
||||
import {
|
||||
BarChartOutlined,
|
||||
BranchesOutlined,
|
||||
CustomerServiceOutlined,
|
||||
DeleteOutlined,
|
||||
FolderOpenOutlined,
|
||||
GlobalOutlined,
|
||||
HeartOutlined,
|
||||
HomeOutlined,
|
||||
LayoutOutlined,
|
||||
RobotOutlined,
|
||||
ShoppingOutlined,
|
||||
SwapOutlined,
|
||||
ToolOutlined,
|
||||
WalletOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useShallow } from "zustand/react/shallow";
|
||||
import ErrorBoundary from "./components/ErrorBoundary";
|
||||
@@ -37,6 +21,7 @@ import { webGenerationGateway, type CreatePreviewTaskInput } from "./api/webGene
|
||||
import { translateTaskError } from "./utils/translateTaskError";
|
||||
import { recoverAndResumeTasks } from "./services/backgroundTaskRunner";
|
||||
import AppShell from "./components/AppShell";
|
||||
import { ShellIcon } from "./components/ShellIcon";
|
||||
const NotFoundPage = lazy(() => import("./components/NotFoundPage"));
|
||||
const CompliancePage = lazy(() => import("./features/compliance/CompliancePage"));
|
||||
import { cloneWorkflow, createBlankWorkflow } from "./data/workflows";
|
||||
@@ -441,24 +426,24 @@ function App() {
|
||||
|
||||
const navItems = useMemo<WebNavItem[]>(
|
||||
() => [
|
||||
{ key: "home", label: "首页", hint: "项目入口", icon: <HomeOutlined /> },
|
||||
{ key: "workbench", label: "生成", hint: "对话生成页面", icon: <RobotOutlined /> },
|
||||
{ key: "home", label: "首页", hint: "项目入口", icon: <ShellIcon name="home" /> },
|
||||
{ key: "workbench", label: "生成", hint: "对话生成页面", icon: <ShellIcon name="robot" /> },
|
||||
{
|
||||
key: "ecommerce",
|
||||
label: "电商生成",
|
||||
hint: "AI创作与海报生成",
|
||||
icon: <ShoppingOutlined />,
|
||||
icon: <ShellIcon name="shopping" />,
|
||||
},
|
||||
{ key: "canvas", label: "画布", hint: "进入自由画布编排", icon: <BranchesOutlined /> },
|
||||
{ key: "community", label: "社区", hint: "案例分享与导入", icon: <GlobalOutlined /> },
|
||||
{ key: "scriptTokens", label: "剧本评分", hint: "剧本评分系统", icon: <BarChartOutlined /> },
|
||||
{ key: "tokenUsage", label: "Token消耗", hint: "成员、服务与调用记录", icon: <WalletOutlined /> },
|
||||
{ key: "providerHealth", label: "服务商健康", hint: "AI 服务商状态与监控", icon: <HeartOutlined /> },
|
||||
{ key: "assets", label: "资产库", hint: "角色、场景、道具", icon: <FolderOpenOutlined /> },
|
||||
{ key: "agent", label: "Agent", hint: "拆解与规划", icon: <RobotOutlined /> },
|
||||
{ key: "digitalHuman", label: "数字人", hint: "口播与人像生成", icon: <CustomerServiceOutlined /> },
|
||||
{ key: "characterMix", label: "角色迁移", hint: "人物视频迁移", icon: <SwapOutlined /> },
|
||||
{ key: "more", label: "工具盒", hint: "图像与镜头工具", icon: <ToolOutlined /> },
|
||||
{ key: "canvas", label: "画布", hint: "进入自由画布编排", icon: <ShellIcon name="branches" /> },
|
||||
{ key: "community", label: "社区", hint: "案例分享与导入", icon: <ShellIcon name="global" /> },
|
||||
{ key: "scriptTokens", label: "剧本评分", hint: "剧本评分系统", icon: <ShellIcon name="bar-chart" /> },
|
||||
{ key: "tokenUsage", label: "Token消耗", hint: "成员、服务与调用记录", icon: <ShellIcon name="wallet" /> },
|
||||
{ key: "providerHealth", label: "服务商健康", hint: "AI 服务商状态与监控", icon: <ShellIcon name="heart" /> },
|
||||
{ key: "assets", label: "资产库", hint: "角色、场景、道具", icon: <ShellIcon name="folder" /> },
|
||||
{ key: "agent", label: "Agent", hint: "拆解与规划", icon: <ShellIcon name="robot" /> },
|
||||
{ key: "digitalHuman", label: "数字人", hint: "口播与人像生成", icon: <ShellIcon name="customer-service" /> },
|
||||
{ key: "characterMix", label: "角色迁移", hint: "人物视频迁移", icon: <ShellIcon name="swap" /> },
|
||||
{ key: "more", label: "工具盒", hint: "图像与镜头工具", icon: <ShellIcon name="tool" /> },
|
||||
],
|
||||
[],
|
||||
);
|
||||
@@ -1455,7 +1440,7 @@ function App() {
|
||||
/>
|
||||
<section className="project-delete-modal__panel">
|
||||
<span className="project-delete-modal__icon">
|
||||
<DeleteOutlined />
|
||||
<ShellIcon name="delete" />
|
||||
</span>
|
||||
<h2 id="project-delete-title">删除项目</h2>
|
||||
<p>确认删除项目「{pendingDeleteProject.name}」?删除后将从服务器项目列表移除。</p>
|
||||
|
||||
+11
-22
@@ -1,15 +1,3 @@
|
||||
import {
|
||||
ArrowDownOutlined,
|
||||
ArrowUpOutlined,
|
||||
CheckCircleOutlined,
|
||||
FlagOutlined,
|
||||
InfoCircleOutlined,
|
||||
LoginOutlined,
|
||||
LogoutOutlined,
|
||||
PlusCircleOutlined,
|
||||
UserOutlined,
|
||||
WalletOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import type { ReactNode } from "react";
|
||||
import { publicConfigClient, type WebPublicConfig } from "../api/publicConfigClient";
|
||||
@@ -23,6 +11,7 @@ import { AnimatedPanel } from "./AnimatedPanel";
|
||||
import AdminMonitor from "./AdminMonitor";
|
||||
import CookieConsentBanner from "./CookieConsentBanner";
|
||||
import { loadRechargeModal, type RechargeModalComponent } from "./RechargeModal/loadRechargeModal";
|
||||
import { ShellIcon } from "./ShellIcon";
|
||||
import { loadDarkGreenTheme } from "../styles/loadDarkGreenTheme";
|
||||
|
||||
interface AppShellProps {
|
||||
@@ -330,7 +319,7 @@ function AppShell({
|
||||
aria-label="返回页面顶部"
|
||||
onClick={() => scrollActivePage("top")}
|
||||
>
|
||||
<ArrowUpOutlined />
|
||||
<ShellIcon name="arrow-up" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@@ -339,7 +328,7 @@ function AppShell({
|
||||
aria-label="到达页面底部"
|
||||
onClick={() => scrollActivePage("bottom")}
|
||||
>
|
||||
<ArrowDownOutlined />
|
||||
<ShellIcon name="arrow-down" />
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
@@ -369,7 +358,7 @@ function AppShell({
|
||||
aria-label="网站信息"
|
||||
onClick={() => setInfoOpen((c) => !c)}
|
||||
>
|
||||
<InfoCircleOutlined />
|
||||
<ShellIcon name="info-circle" />
|
||||
</button>
|
||||
<AnimatedPanel open={infoOpen} className="info-popover panel-surface">
|
||||
<dl>
|
||||
@@ -392,7 +381,7 @@ function AppShell({
|
||||
aria-label={`积分余额 ${displayedBalanceLabel}`}
|
||||
onClick={() => toast.info("充值功能即将开放,敬请期待")}
|
||||
>
|
||||
<WalletOutlined />
|
||||
<ShellIcon name="wallet" />
|
||||
<span className="member-button__label">{displayedBalanceLabel}</span>
|
||||
</button>
|
||||
<div className="profile-popover-anchor" ref={profileRef}>
|
||||
@@ -416,7 +405,7 @@ function AppShell({
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<LoginOutlined />
|
||||
<ShellIcon name="login" />
|
||||
<span>登录 / 注册</span>
|
||||
</>
|
||||
)}
|
||||
@@ -444,7 +433,7 @@ function AppShell({
|
||||
<div className="profile-popover__footer">
|
||||
<span>{session?.source === "server" ? "服务器会话" : "预览会话"}</span>
|
||||
<button type="button" onClick={onLogout}>
|
||||
<LogoutOutlined />
|
||||
<ShellIcon name="logout" />
|
||||
退出
|
||||
</button>
|
||||
</div>
|
||||
@@ -456,7 +445,7 @@ function AppShell({
|
||||
onSelectView("login");
|
||||
}}
|
||||
>
|
||||
<UserOutlined />
|
||||
<ShellIcon name="user" />
|
||||
个人中心
|
||||
</button>
|
||||
<button
|
||||
@@ -467,7 +456,7 @@ function AppShell({
|
||||
onSelectView("report");
|
||||
}}
|
||||
>
|
||||
<FlagOutlined />
|
||||
<ShellIcon name="flag" />
|
||||
投诉举报
|
||||
</button>
|
||||
{showCommunityReview ? (
|
||||
@@ -480,7 +469,7 @@ function AppShell({
|
||||
onSelectView("communityReview");
|
||||
}}
|
||||
>
|
||||
<CheckCircleOutlined />
|
||||
<ShellIcon name="check-circle" />
|
||||
社区审核
|
||||
</button>
|
||||
</>
|
||||
@@ -495,7 +484,7 @@ function AppShell({
|
||||
onSelectView("communityCaseAdd");
|
||||
}}
|
||||
>
|
||||
<PlusCircleOutlined />
|
||||
<ShellIcon name="plus-circle" />
|
||||
添加案例
|
||||
</button>
|
||||
</>
|
||||
|
||||
@@ -1,26 +1,17 @@
|
||||
import {
|
||||
BellOutlined,
|
||||
CheckCircleOutlined,
|
||||
CloseCircleOutlined,
|
||||
DeleteOutlined,
|
||||
DislikeOutlined,
|
||||
ExclamationCircleOutlined,
|
||||
LikeOutlined,
|
||||
LockOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import type { WebNotification, WebNotificationType, WebViewKey } from "../types";
|
||||
import { AnimatedPanel } from "./AnimatedPanel";
|
||||
import { ShellIcon } from "./ShellIcon";
|
||||
|
||||
const NOTIFICATION_ICONS: Record<WebNotificationType, React.ReactNode> = {
|
||||
task_completed: <CheckCircleOutlined style={{ color: "#10b981" }} />,
|
||||
task_failed: <CloseCircleOutlined style={{ color: "#ef4444" }} />,
|
||||
review_pending: <ExclamationCircleOutlined style={{ color: "#f59e0b" }} />,
|
||||
review_passed: <LikeOutlined style={{ color: "#10b981" }} />,
|
||||
review_rejected: <DislikeOutlined style={{ color: "#f59e0b" }} />,
|
||||
credits_low: <ExclamationCircleOutlined style={{ color: "#f59e0b" }} />,
|
||||
session_expired: <LockOutlined style={{ color: "#ef4444" }} />,
|
||||
info: <BellOutlined style={{ color: "#2563eb" }} />,
|
||||
task_completed: <ShellIcon name="check-circle" style={{ color: "#10b981" }} />,
|
||||
task_failed: <ShellIcon name="close-circle" style={{ color: "#ef4444" }} />,
|
||||
review_pending: <ShellIcon name="exclamation-circle" style={{ color: "#f59e0b" }} />,
|
||||
review_passed: <ShellIcon name="like" style={{ color: "#10b981" }} />,
|
||||
review_rejected: <ShellIcon name="dislike" style={{ color: "#f59e0b" }} />,
|
||||
credits_low: <ShellIcon name="exclamation-circle" style={{ color: "#f59e0b" }} />,
|
||||
session_expired: <ShellIcon name="lock" style={{ color: "#ef4444" }} />,
|
||||
info: <ShellIcon name="bell" style={{ color: "#2563eb" }} />,
|
||||
};
|
||||
|
||||
function parseTimestamp(dateStr: string): number {
|
||||
@@ -111,7 +102,7 @@ function NotificationCenter({ items, onNavigate, onMarkRead, onMarkAllRead, onCl
|
||||
aria-label={`通知中心${unreadCount > 0 ? `,${unreadCount}条未读` : ""}`}
|
||||
onClick={() => { setOpen((v) => !v); setNow(Date.now()); }}
|
||||
>
|
||||
<BellOutlined />
|
||||
<ShellIcon name="bell" />
|
||||
{unreadCount > 0 && (
|
||||
<span className="notification-center__badge">{unreadCount > 99 ? "99+" : unreadCount}</span>
|
||||
)}
|
||||
@@ -127,7 +118,7 @@ function NotificationCenter({ items, onNavigate, onMarkRead, onMarkAllRead, onCl
|
||||
)}
|
||||
{notifications.length > 0 && onClear && (
|
||||
<button className="notification-center__clear" type="button" onClick={() => { onClear(); setOpen(false); }}>
|
||||
<DeleteOutlined /> 清空
|
||||
<ShellIcon name="delete" /> 清空
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
@@ -135,7 +126,7 @@ function NotificationCenter({ items, onNavigate, onMarkRead, onMarkAllRead, onCl
|
||||
<div className="notification-center__list">
|
||||
{notifications.length === 0 ? (
|
||||
<div className="notification-center__empty">
|
||||
<BellOutlined style={{ fontSize: 28, opacity: 0.3 }} />
|
||||
<ShellIcon name="bell" style={{ fontSize: 28, opacity: 0.3 }} />
|
||||
<span>暂无通知</span>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -0,0 +1,344 @@
|
||||
import type { CSSProperties } from "react";
|
||||
|
||||
export type ShellIconName =
|
||||
| "arrow-down"
|
||||
| "arrow-left"
|
||||
| "arrow-up"
|
||||
| "bar-chart"
|
||||
| "bell"
|
||||
| "branches"
|
||||
| "check-circle"
|
||||
| "chevron-left"
|
||||
| "chevron-right"
|
||||
| "close-circle"
|
||||
| "copy"
|
||||
| "customer-service"
|
||||
| "delete"
|
||||
| "dislike"
|
||||
| "download"
|
||||
| "exclamation-circle"
|
||||
| "flag"
|
||||
| "file-text"
|
||||
| "folder"
|
||||
| "global"
|
||||
| "heart"
|
||||
| "home"
|
||||
| "info-circle"
|
||||
| "like"
|
||||
| "line-chart"
|
||||
| "lock"
|
||||
| "login"
|
||||
| "logout"
|
||||
| "loading"
|
||||
| "plus-circle"
|
||||
| "reload"
|
||||
| "robot"
|
||||
| "shopping"
|
||||
| "swap"
|
||||
| "team"
|
||||
| "thunderbolt"
|
||||
| "tool"
|
||||
| "upload"
|
||||
| "user"
|
||||
| "wallet"
|
||||
| "warning";
|
||||
|
||||
interface ShellIconProps {
|
||||
name: ShellIconName;
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
}
|
||||
|
||||
function renderIcon(name: ShellIconName) {
|
||||
switch (name) {
|
||||
case "arrow-down":
|
||||
return <path d="M12 5v14m0 0 6-6m-6 6-6-6" />;
|
||||
case "arrow-left":
|
||||
return <path d="M19 12H5m0 0 6-6m-6 6 6 6" />;
|
||||
case "arrow-up":
|
||||
return <path d="M12 19V5m0 0 6 6m-6-6-6 6" />;
|
||||
case "bar-chart":
|
||||
return (
|
||||
<>
|
||||
<path d="M4 19V5" />
|
||||
<path d="M4 19h16" />
|
||||
<path d="M8 16v-5" />
|
||||
<path d="M12 16V8" />
|
||||
<path d="M16 16v-9" />
|
||||
</>
|
||||
);
|
||||
case "bell":
|
||||
return (
|
||||
<>
|
||||
<path d="M18 9a6 6 0 0 0-12 0c0 7-3 7-3 9h18c0-2-3-2-3-9" />
|
||||
<path d="M10 21h4" />
|
||||
</>
|
||||
);
|
||||
case "branches":
|
||||
return (
|
||||
<>
|
||||
<circle cx="6" cy="6" r="2" />
|
||||
<circle cx="18" cy="6" r="2" />
|
||||
<circle cx="12" cy="18" r="2" />
|
||||
<path d="M8 7.5 12 12l4-4.5" />
|
||||
<path d="M12 12v4" />
|
||||
</>
|
||||
);
|
||||
case "check-circle":
|
||||
return (
|
||||
<>
|
||||
<circle cx="12" cy="12" r="9" />
|
||||
<path d="m8 12 2.5 2.5L16 9" />
|
||||
</>
|
||||
);
|
||||
case "chevron-left":
|
||||
return <path d="m15 18-6-6 6-6" />;
|
||||
case "chevron-right":
|
||||
return <path d="m9 18 6-6-6-6" />;
|
||||
case "close-circle":
|
||||
return (
|
||||
<>
|
||||
<circle cx="12" cy="12" r="9" />
|
||||
<path d="m9 9 6 6m0-6-6 6" />
|
||||
</>
|
||||
);
|
||||
case "copy":
|
||||
return (
|
||||
<>
|
||||
<rect x="8" y="8" width="11" height="11" rx="2" />
|
||||
<path d="M5 15H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v1" />
|
||||
</>
|
||||
);
|
||||
case "customer-service":
|
||||
return (
|
||||
<>
|
||||
<path d="M4 13a8 8 0 0 1 16 0" />
|
||||
<path d="M5 13h3v5H5a2 2 0 0 1-2-2v-1a2 2 0 0 1 2-2Z" />
|
||||
<path d="M16 13h3a2 2 0 0 1 2 2v1a2 2 0 0 1-2 2h-3v-5Z" />
|
||||
<path d="M18 18c0 2-2 3-6 3" />
|
||||
</>
|
||||
);
|
||||
case "delete":
|
||||
return (
|
||||
<>
|
||||
<path d="M4 7h16" />
|
||||
<path d="M10 11v6" />
|
||||
<path d="M14 11v6" />
|
||||
<path d="M6 7l1 14h10l1-14" />
|
||||
<path d="M9 7V4h6v3" />
|
||||
</>
|
||||
);
|
||||
case "download":
|
||||
return (
|
||||
<>
|
||||
<path d="M12 4v11" />
|
||||
<path d="m7 10 5 5 5-5" />
|
||||
<path d="M5 20h14" />
|
||||
</>
|
||||
);
|
||||
case "dislike":
|
||||
return (
|
||||
<>
|
||||
<path d="M7 3v12" />
|
||||
<path d="M7 15h9l-1 5a2 2 0 0 1-3 1l-3-6H5a2 2 0 0 1-2-2V6a3 3 0 0 1 3-3h1" />
|
||||
<path d="M17 3h2a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-3" />
|
||||
</>
|
||||
);
|
||||
case "exclamation-circle":
|
||||
return (
|
||||
<>
|
||||
<circle cx="12" cy="12" r="9" />
|
||||
<path d="M12 7v6" />
|
||||
<path d="M12 17h.01" />
|
||||
</>
|
||||
);
|
||||
case "flag":
|
||||
return (
|
||||
<>
|
||||
<path d="M5 21V4" />
|
||||
<path d="M5 5h11l-1.5 4L16 13H5" />
|
||||
</>
|
||||
);
|
||||
case "file-text":
|
||||
return (
|
||||
<>
|
||||
<path d="M14 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9Z" />
|
||||
<path d="M14 3v6h6" />
|
||||
<path d="M8 13h8" />
|
||||
<path d="M8 17h6" />
|
||||
</>
|
||||
);
|
||||
case "folder":
|
||||
return <path d="M3 7h7l2 2h9v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7Z" />;
|
||||
case "global":
|
||||
return (
|
||||
<>
|
||||
<circle cx="12" cy="12" r="9" />
|
||||
<path d="M3 12h18" />
|
||||
<path d="M12 3c3 3 3 15 0 18" />
|
||||
<path d="M12 3c-3 3-3 15 0 18" />
|
||||
</>
|
||||
);
|
||||
case "heart":
|
||||
return <path d="M20 8.5c0 5-8 10.5-8 10.5S4 13.5 4 8.5A4.5 4.5 0 0 1 12 6a4.5 4.5 0 0 1 8 2.5Z" />;
|
||||
case "home":
|
||||
return (
|
||||
<>
|
||||
<path d="M3 11 12 4l9 7" />
|
||||
<path d="M5 10v10h14V10" />
|
||||
<path d="M10 20v-6h4v6" />
|
||||
</>
|
||||
);
|
||||
case "info-circle":
|
||||
return (
|
||||
<>
|
||||
<circle cx="12" cy="12" r="9" />
|
||||
<path d="M12 11v6" />
|
||||
<path d="M12 7h.01" />
|
||||
</>
|
||||
);
|
||||
case "like":
|
||||
return (
|
||||
<>
|
||||
<path d="M7 21V9" />
|
||||
<path d="M7 9h3l3-6a2 2 0 0 1 3 1l-1 5h4a2 2 0 0 1 2 2l-2 8a3 3 0 0 1-3 2H7" />
|
||||
<path d="M3 10h4v10H3z" />
|
||||
</>
|
||||
);
|
||||
case "line-chart":
|
||||
return (
|
||||
<>
|
||||
<path d="M4 19V5" />
|
||||
<path d="M4 19h16" />
|
||||
<path d="m7 15 4-4 3 3 5-7" />
|
||||
</>
|
||||
);
|
||||
case "lock":
|
||||
return (
|
||||
<>
|
||||
<rect x="5" y="10" width="14" height="10" rx="2" />
|
||||
<path d="M8 10V7a4 4 0 0 1 8 0v3" />
|
||||
</>
|
||||
);
|
||||
case "login":
|
||||
return (
|
||||
<>
|
||||
<path d="M14 4h5v16h-5" />
|
||||
<path d="M4 12h10" />
|
||||
<path d="m10 8 4 4-4 4" />
|
||||
</>
|
||||
);
|
||||
case "logout":
|
||||
return (
|
||||
<>
|
||||
<path d="M10 4H5v16h5" />
|
||||
<path d="M20 12H10" />
|
||||
<path d="m14 8-4 4 4 4" />
|
||||
</>
|
||||
);
|
||||
case "loading":
|
||||
return (
|
||||
<>
|
||||
<path d="M12 3a9 9 0 1 1-8 5" />
|
||||
<path d="M4 3v5h5" />
|
||||
</>
|
||||
);
|
||||
case "plus-circle":
|
||||
return (
|
||||
<>
|
||||
<circle cx="12" cy="12" r="9" />
|
||||
<path d="M12 8v8" />
|
||||
<path d="M8 12h8" />
|
||||
</>
|
||||
);
|
||||
case "reload":
|
||||
return (
|
||||
<>
|
||||
<path d="M20 12a8 8 0 1 1-2.3-5.7" />
|
||||
<path d="M20 4v6h-6" />
|
||||
</>
|
||||
);
|
||||
case "robot":
|
||||
return (
|
||||
<>
|
||||
<rect x="5" y="8" width="14" height="11" rx="3" />
|
||||
<path d="M12 8V4" />
|
||||
<path d="M8 13h.01" />
|
||||
<path d="M16 13h.01" />
|
||||
<path d="M9 17h6" />
|
||||
</>
|
||||
);
|
||||
case "shopping":
|
||||
return (
|
||||
<>
|
||||
<path d="M6 7h15l-2 8H8L6 7Z" />
|
||||
<path d="M6 7 5 4H2" />
|
||||
<circle cx="9" cy="20" r="1.5" />
|
||||
<circle cx="18" cy="20" r="1.5" />
|
||||
</>
|
||||
);
|
||||
case "swap":
|
||||
return (
|
||||
<>
|
||||
<path d="M7 7h13m0 0-4-4m4 4-4 4" />
|
||||
<path d="M17 17H4m0 0 4-4m-4 4 4 4" />
|
||||
</>
|
||||
);
|
||||
case "team":
|
||||
return (
|
||||
<>
|
||||
<circle cx="9" cy="8" r="3" />
|
||||
<path d="M3 20a6 6 0 0 1 12 0" />
|
||||
<path d="M16 11a3 3 0 1 0-1-5.8" />
|
||||
<path d="M17 20a5 5 0 0 0-3-4.6" />
|
||||
</>
|
||||
);
|
||||
case "thunderbolt":
|
||||
return <path d="M13 2 4 14h7l-1 8 10-13h-7l1-7Z" />;
|
||||
case "tool":
|
||||
return <path d="M14.5 5.5a5 5 0 0 0 4 6.5L9 21l-6-6 9-9.5a5 5 0 0 0 2.5 0Z" />;
|
||||
case "upload":
|
||||
return (
|
||||
<>
|
||||
<path d="M12 20V9" />
|
||||
<path d="m7 14 5-5 5 5" />
|
||||
<path d="M5 4h14" />
|
||||
</>
|
||||
);
|
||||
case "user":
|
||||
return (
|
||||
<>
|
||||
<circle cx="12" cy="8" r="4" />
|
||||
<path d="M4 21a8 8 0 0 1 16 0" />
|
||||
</>
|
||||
);
|
||||
case "wallet":
|
||||
return (
|
||||
<>
|
||||
<path d="M4 7h15a2 2 0 0 1 2 2v10H5a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h12" />
|
||||
<path d="M16 13h5" />
|
||||
<path d="M17 16h.01" />
|
||||
</>
|
||||
);
|
||||
case "warning":
|
||||
return (
|
||||
<>
|
||||
<path d="M12 3 2 20h20L12 3Z" />
|
||||
<path d="M12 9v5" />
|
||||
<path d="M12 17h.01" />
|
||||
</>
|
||||
);
|
||||
default:
|
||||
return <circle cx="12" cy="12" r="8" />;
|
||||
}
|
||||
}
|
||||
|
||||
export function ShellIcon({ name, className, style }: ShellIconProps) {
|
||||
return (
|
||||
<span className={["anticon", "shell-icon", className].filter(Boolean).join(" ")} style={style} aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
|
||||
{renderIcon(name)}
|
||||
</svg>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@@ -1,18 +1,9 @@
|
||||
import {
|
||||
BarChartOutlined,
|
||||
CheckCircleFilled,
|
||||
CopyOutlined,
|
||||
DownloadOutlined,
|
||||
FileTextOutlined,
|
||||
LoadingOutlined,
|
||||
ThunderboltOutlined,
|
||||
UploadOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { useEffect, useRef, useState, type ChangeEvent, type KeyboardEvent } from "react";
|
||||
import "../../styles/pages/script-tokens-v5.css";
|
||||
import "../../styles/pages/script-tokens.css";
|
||||
import { evaluateScript } from "../../api/scriptEvalClient";
|
||||
import { buildApiUrl, getStoredToken } from "../../api/serverConnection";
|
||||
import { ShellIcon } from "../../components/ShellIcon";
|
||||
import { useSessionStore } from "../../stores";
|
||||
|
||||
interface ScoreDimension {
|
||||
@@ -494,7 +485,7 @@ function ScriptTokensPage() {
|
||||
>
|
||||
{uploadedFile ? (
|
||||
<div className="script-eval-v5-upload-done is-show">
|
||||
<CheckCircleFilled />
|
||||
<ShellIcon name="check-circle" />
|
||||
<span className="script-eval-v5-uf-meta">
|
||||
<span className="script-eval-v5-uf-name">{uploadedFile.name}</span>
|
||||
<span className="script-eval-v5-uf-size">{formatFileSize(uploadedFile.size)}</span>
|
||||
@@ -505,10 +496,10 @@ function ScriptTokensPage() {
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="script-eval-v5-upload-icon"><UploadOutlined /></div>
|
||||
<div className="script-eval-v5-upload-icon"><ShellIcon name="upload" /></div>
|
||||
<div className="script-eval-v5-upload-text">拖拽或点击上传</div>
|
||||
<button type="button" className="script-eval-v5-upload-btn" onClick={(e) => { e.stopPropagation(); fileInputRef.current?.click(); }}>
|
||||
<UploadOutlined /> 选择剧本
|
||||
<ShellIcon name="upload" /> 选择剧本
|
||||
</button>
|
||||
<div className="script-eval-v5-upload-hint">{TEXT_FILE_HINT}</div>
|
||||
</>
|
||||
@@ -581,11 +572,11 @@ function ScriptTokensPage() {
|
||||
disabled={loading || !hasContent}
|
||||
onClick={() => void handleEvaluate()}
|
||||
>
|
||||
{loading ? <LoadingOutlined /> : <ThunderboltOutlined />}
|
||||
{loading ? <ShellIcon name="loading" /> : <ShellIcon name="thunderbolt" />}
|
||||
<span>{loading ? "评测中..." : "开始评测"}</span>
|
||||
</button>
|
||||
<button type="button" className="script-eval-v5-export-btn" disabled={!result} onClick={handleExportMarkdown}>
|
||||
<DownloadOutlined />
|
||||
<ShellIcon name="download" />
|
||||
<span>导出评测报告</span>
|
||||
</button>
|
||||
</div>
|
||||
@@ -603,10 +594,10 @@ function ScriptTokensPage() {
|
||||
{result && (
|
||||
<>
|
||||
<button type="button" className="script-eval-v5-action-btn" onClick={() => void handleCopyReport()}>
|
||||
<CopyOutlined />{copied ? "已复制" : "复制"}
|
||||
<ShellIcon name="copy" />{copied ? "已复制" : "复制"}
|
||||
</button>
|
||||
<button type="button" className="script-eval-v5-action-btn" onClick={handleExportMarkdown}>
|
||||
<DownloadOutlined />导出
|
||||
<ShellIcon name="download" />导出
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
@@ -640,7 +631,7 @@ function ScriptTokensPage() {
|
||||
onKeyDown={uploadKeyDown}
|
||||
>
|
||||
<div className="script-eval-v5-upload-card-icon">
|
||||
<FileTextOutlined />
|
||||
<ShellIcon name="file-text" />
|
||||
</div>
|
||||
<div className="script-eval-v5-upload-card-title">
|
||||
{uploadedFile ? "剧本已导入" : "上传剧本文件"}
|
||||
@@ -744,7 +735,7 @@ function ScriptTokensPage() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="script-eval-report__chart-note">
|
||||
<BarChartOutlined />
|
||||
<ShellIcon name="bar-chart" />
|
||||
<span>
|
||||
{activeDim === null
|
||||
? "悬停维度可查看当前分项表现,优先从低分项制定改稿计划。"
|
||||
|
||||
@@ -1,16 +1,5 @@
|
||||
import {
|
||||
ArrowLeftOutlined,
|
||||
BarChartOutlined,
|
||||
CheckCircleOutlined,
|
||||
LeftOutlined,
|
||||
LineChartOutlined,
|
||||
ReloadOutlined,
|
||||
RightOutlined,
|
||||
TeamOutlined,
|
||||
UserOutlined,
|
||||
WarningOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { ShellIcon } from "../../components/ShellIcon";
|
||||
import "../../styles/pages/more-tools.css";
|
||||
import "../../styles/pages/script-tokens-v5.css";
|
||||
import "../../styles/pages/script-tokens.css";
|
||||
@@ -246,7 +235,7 @@ function TokenUsagePage({
|
||||
<header className="management-center-toolbar" aria-label="管理中心操作">
|
||||
<div className="management-center-toolbar__title">
|
||||
<button type="button" className="management-center-toolbar__back" aria-label="返回工具盒" onClick={onOpenMore}>
|
||||
<ArrowLeftOutlined />
|
||||
<ShellIcon name="arrow-left" />
|
||||
</button>
|
||||
<span>
|
||||
<strong>管理中心</strong>
|
||||
@@ -257,18 +246,18 @@ function TokenUsagePage({
|
||||
{enterpriseUsageLoading ? "正在同步企业用量" : enterpriseUsageError || "服务器已连接"}
|
||||
</span>
|
||||
<button type="button" onClick={refreshEnterpriseUsage} disabled={enterpriseUsageLoading}>
|
||||
<ReloadOutlined />
|
||||
<ShellIcon name="reload" />
|
||||
刷新数据
|
||||
</button>
|
||||
<button type="button" className="is-muted-action">
|
||||
<UserOutlined />
|
||||
<ShellIcon name="user" />
|
||||
成员管理
|
||||
</button>
|
||||
</header>
|
||||
|
||||
{isLowBalance ? (
|
||||
<div className="management-balance-alert" role="alert">
|
||||
<WarningOutlined />
|
||||
<ShellIcon name="warning" />
|
||||
<span>当前余额 {formatCredits(availableBalanceCents)},可能不足以完成下一次生成,请及时充值。</span>
|
||||
</div>
|
||||
) : null}
|
||||
@@ -287,7 +276,7 @@ function TokenUsagePage({
|
||||
<article className="management-card management-card--chart">
|
||||
<div className="management-card__head">
|
||||
<h2>
|
||||
<BarChartOutlined />
|
||||
<ShellIcon name="bar-chart" />
|
||||
模型消耗分布
|
||||
</h2>
|
||||
<span>{enterpriseUsageLoading ? "SYNC" : modelBreakdown.length ? `${modelBreakdown.length} 个模型` : "LIVE"}</span>
|
||||
@@ -309,7 +298,7 @@ function TokenUsagePage({
|
||||
</div>
|
||||
) : (
|
||||
<div className="management-empty-chart">
|
||||
<BarChartOutlined />
|
||||
<ShellIcon name="bar-chart" />
|
||||
<span>暂无模型用量数据</span>
|
||||
</div>
|
||||
)}
|
||||
@@ -318,7 +307,7 @@ function TokenUsagePage({
|
||||
<article className="management-card management-status-card">
|
||||
<div className="management-card__head">
|
||||
<h2>
|
||||
<LineChartOutlined />
|
||||
<ShellIcon name="line-chart" />
|
||||
系统状态
|
||||
</h2>
|
||||
</div>
|
||||
@@ -347,7 +336,7 @@ function TokenUsagePage({
|
||||
<section className="management-card management-members">
|
||||
<div className="management-card__head">
|
||||
<h2>
|
||||
<TeamOutlined />
|
||||
<ShellIcon name="team" />
|
||||
团队成员 ({members.length})
|
||||
</h2>
|
||||
<button type="button">{isEnterpriseAdmin ? "企业管理员" : "当前账号"}</button>
|
||||
@@ -366,7 +355,7 @@ function TokenUsagePage({
|
||||
<b>{member.taskCount} 调用</b>
|
||||
<b>{formatDateTime(member.lastUsedAt)}</b>
|
||||
</span>
|
||||
<CheckCircleOutlined />
|
||||
<ShellIcon name="check-circle" />
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
@@ -375,7 +364,7 @@ function TokenUsagePage({
|
||||
<section className="management-card management-records">
|
||||
<div className="management-card__head">
|
||||
<h2>
|
||||
<BarChartOutlined />
|
||||
<ShellIcon name="bar-chart" />
|
||||
调用记录
|
||||
</h2>
|
||||
<span>{records.length} 条记录</span>
|
||||
@@ -411,11 +400,11 @@ function TokenUsagePage({
|
||||
{records.length > pageSize && (
|
||||
<div className="management-record-pagination">
|
||||
<button type="button" disabled={recordPage === 0} onClick={() => setRecordPage((p) => p - 1)}>
|
||||
<LeftOutlined />
|
||||
<ShellIcon name="chevron-left" />
|
||||
</button>
|
||||
<span>{recordPage + 1} / {totalPages}</span>
|
||||
<button type="button" disabled={recordPage >= totalPages - 1} onClick={() => setRecordPage((p) => p + 1)}>
|
||||
<RightOutlined />
|
||||
<ShellIcon name="chevron-right" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -189,6 +189,18 @@
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.shell-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
.shell-icon svg {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
.creator-button,
|
||||
.member-button,
|
||||
.profile-button,
|
||||
|
||||
Reference in New Issue
Block a user