feat(ui): 自研 Celadon 组件族入库与 AntD 消费全量替换 - base(Kbd/StatusBadge/IconButton/ToolButton)+product(EmptyState/Callout/SegmentedControl/Checkbox/Select/Tooltip/Switch), composio 视觉基底, globals.css 覆盖清理 (ADR-0008)
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { App, Button, Form, Input, Select, Skeleton, Switch } from "antd";
|
||||
import { App, Button, Form, Input, Select, Skeleton } from "antd";
|
||||
import { Switch } from "@/components/ui/base/switch";
|
||||
import { AlertTriangle, BadgeCheck, ChevronDown, Globe2, KeyRound, LockKeyhole, RefreshCw, RotateCcw, Save, ShieldCheck, UserPlus, UsersRound } from "lucide-react";
|
||||
import { useCallback, useEffect, useRef, useState, type ReactNode } from "react";
|
||||
import { useBlocker } from "react-router";
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useEffect, useRef, useState, type ChangeEvent } from "react";
|
||||
import { App, Button, Form, Input, Modal, Select, Switch } from "antd";
|
||||
import { App, Button, Form, Input, Modal, Select } from "antd";
|
||||
import { Switch } from "@/components/ui/base/switch";
|
||||
import type { InputRef } from "antd";
|
||||
import type { ColumnsType } from "antd/es/table";
|
||||
import { PencilLine, Pin, Plus, RefreshCw, Search, Send, Upload, X } from "lucide-react";
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ConfigProvider, Dropdown, Tooltip } from "antd";
|
||||
import { ConfigProvider, Dropdown } from "antd";
|
||||
import { Tooltip } from "@/components/ui/base/tooltip";
|
||||
import type { MenuProps } from "antd";
|
||||
import {
|
||||
ArrowLeft,
|
||||
@@ -131,7 +132,7 @@ export function AdminShell() {
|
||||
<main className="admin-shell app-user-workspace flex h-full min-h-0 overflow-hidden text-foreground">
|
||||
<aside className={cn("app-workspace-sidebar admin-sidebar hidden shrink-0 flex-col overflow-hidden lg:flex", collapsed && "is-collapsed")}>
|
||||
<div className="admin-sidebar-identity shrink-0">
|
||||
<Tooltip mouseEnterDelay={0.1} title={collapsed ? "查看更新日志" : undefined} placement="right" rootClassName="app-workspace-sidebar-tooltip">
|
||||
<Tooltip delay={100} title={collapsed ? "查看更新日志" : undefined} placement="right">
|
||||
<AppChangelogButton
|
||||
className={cn("admin-sidebar-brand-button", collapsed && "is-collapsed")}
|
||||
icon={<BrandLogoFrame className="admin-sidebar-brand-mark grid shrink-0 place-items-center bg-foreground text-background" logoClassName="size-5 object-contain" alt="" fallback={<InfinityIcon className="size-4" />} />}
|
||||
@@ -145,7 +146,7 @@ export function AdminShell() {
|
||||
</div>
|
||||
<AdminNavigation collapsed={collapsed} />
|
||||
<div className="admin-sidebar-footer shrink-0">
|
||||
<Tooltip mouseEnterDelay={0.1} title={collapsed ? "返回创作台" : undefined} placement="right" rootClassName="app-workspace-sidebar-tooltip">
|
||||
<Tooltip delay={100} title={collapsed ? "返回创作台" : undefined} placement="right">
|
||||
<NavLink
|
||||
to="/"
|
||||
aria-label={collapsed ? "返回创作台" : undefined}
|
||||
@@ -157,7 +158,7 @@ export function AdminShell() {
|
||||
</Tooltip>
|
||||
</div>
|
||||
</aside>
|
||||
<Tooltip mouseEnterDelay={0.1} title={collapsed ? "展开侧栏" : "收起侧栏"} placement="right" rootClassName="app-workspace-sidebar-tooltip">
|
||||
<Tooltip delay={100} title={collapsed ? "展开侧栏" : "收起侧栏"} placement="right">
|
||||
<button type="button" className={cn("admin-sidebar-edge-toggle hidden lg:grid", collapsed && "is-collapsed")} onClick={toggleCollapsed} aria-label={collapsed ? "展开侧栏" : "收起侧栏"} aria-expanded={!collapsed}>
|
||||
{collapsed ? <ChevronRight className="size-3.5" aria-hidden="true" /> : <ChevronLeft className="size-3.5" aria-hidden="true" />}
|
||||
</button>
|
||||
@@ -293,7 +294,7 @@ function AdminNavigation({ collapsed }: { collapsed: boolean }) {
|
||||
return (
|
||||
<div key={group.label} className="admin-nav-group">
|
||||
{!collapsed ? (
|
||||
<div className="admin-nav-group-label mb-1 px-2.5 text-[var(--fs-tiny)] font-medium text-foreground/38">
|
||||
<div className="admin-nav-group-label">
|
||||
<span>{group.label}</span>
|
||||
</div>
|
||||
) : (
|
||||
@@ -301,16 +302,16 @@ function AdminNavigation({ collapsed }: { collapsed: boolean }) {
|
||||
)}
|
||||
<div className={cn("admin-nav-group-items space-y-0.5", collapsed && "is-collapsed")}>
|
||||
{visibleItems.map((item) => (
|
||||
<Tooltip key={item.path} mouseEnterDelay={0.1} title={collapsed ? item.label : undefined} placement="right" rootClassName="app-workspace-sidebar-tooltip">
|
||||
<Tooltip key={item.path} delay={100} title={collapsed ? item.label : undefined} placement="right">
|
||||
<NavLink
|
||||
to={item.path}
|
||||
end={item.path === "/admin"}
|
||||
aria-label={collapsed ? item.label : undefined}
|
||||
className={({ isActive }) =>
|
||||
cn(
|
||||
"app-workspace-nav-link flex h-8 items-center rounded-md text-[var(--fs-body)] transition-colors",
|
||||
"app-workspace-nav-link flex h-8 items-center rounded-md transition-colors",
|
||||
collapsed ? "justify-center px-0" : "gap-2.5 px-2.5",
|
||||
isActive ? "is-active font-medium" : "text-foreground/62 hover:bg-surface-hover hover:text-foreground",
|
||||
isActive ? "is-active" : "text-foreground/62 hover:bg-surface-hover hover:text-foreground",
|
||||
)
|
||||
}
|
||||
>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { StatusBadge } from "@/components/ui/base/badges";
|
||||
import { EmptyState } from "@/components/ui/product/empty-state";
|
||||
import { App, Button, Dropdown, Table } from "antd";
|
||||
import type { ButtonProps, MenuProps, TableProps } from "antd";
|
||||
import { saveAs } from "file-saver";
|
||||
@@ -12,12 +14,18 @@ export const configuredSecretText = "已配置 · 留空不改";
|
||||
|
||||
export type AdminStatusTone = "neutral" | "success" | "warning" | "error" | "info";
|
||||
|
||||
/** admin 状态徽章:归并到自研 StatusBadge(outline 圆点胶囊),admin 全部页面零改动统一视觉。
|
||||
* tone 归一:info 在 admin 中本就是前景中性(--admin-status-info: var(--foreground))→ neutral。 */
|
||||
const adminToneToBadgeTone: Record<AdminStatusTone, "neutral" | "success" | "warning" | "error"> = {
|
||||
neutral: "neutral",
|
||||
success: "success",
|
||||
warning: "warning",
|
||||
error: "error",
|
||||
info: "neutral",
|
||||
};
|
||||
|
||||
export function AdminStatusBadge({ label, tone = "neutral", title }: { label: string; tone?: AdminStatusTone; title?: string }) {
|
||||
return (
|
||||
<span className="admin-status-badge" data-tone={tone} title={title}>
|
||||
{label}
|
||||
</span>
|
||||
);
|
||||
return <StatusBadge tone={adminToneToBadgeTone[tone]} title={title} label={label} />;
|
||||
}
|
||||
|
||||
export function AdminStatTile({ label, value, detail, trend }: { label: string; value: string | number; detail?: string; trend?: { value: string; tone?: AdminStatusTone } }) {
|
||||
@@ -133,14 +141,14 @@ export function AdminExportButton({
|
||||
|
||||
export function AdminTableEmpty({ filtered = false, title, description, action }: { filtered?: boolean; title?: string; description?: string; action?: ReactNode }) {
|
||||
return (
|
||||
<div className="flex min-h-40 flex-col items-center justify-center px-6 py-8 text-center">
|
||||
<span className="grid size-9 place-items-center rounded-md bg-muted/35 text-foreground/45">
|
||||
<SearchX className="size-4" />
|
||||
</span>
|
||||
<div className="mt-3 text-sm font-medium">{title || (filtered ? "没有符合筛选条件的数据" : "暂无数据")}</div>
|
||||
{description ? <p className="mt-1 max-w-sm text-xs leading-5 text-foreground/50">{description}</p> : null}
|
||||
{action ? <div className="mt-4">{action}</div> : null}
|
||||
</div>
|
||||
<EmptyState
|
||||
size="compact"
|
||||
icon={SearchX}
|
||||
title={title || (filtered ? "没有符合筛选条件的数据" : "暂无数据")}
|
||||
description={description}
|
||||
action={action}
|
||||
className="min-h-40 w-full justify-center"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { App, Button, Descriptions, Drawer, Empty, Progress, Skeleton, Tabs } from "antd";
|
||||
import { App, Button, Descriptions, Drawer, Progress, Skeleton, Tabs } from "antd";
|
||||
import { IconButton } from "@/components/ui/base/buttons";
|
||||
import { EmptyState } from "@/components/ui/product/empty-state";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
|
||||
import { PaginationBar } from "@/components/layout/workspace-page";
|
||||
@@ -97,8 +99,8 @@ export function AdminUserDetailDrawer({ userId, onClose, previousUserId, nextUse
|
||||
rootClassName="admin-drawer"
|
||||
extra={onNavigate ? (
|
||||
<div className="flex items-center gap-1">
|
||||
<Button type="text" size="small" aria-label="上一条用户" disabled={!previousUserId} icon={<ChevronLeft className="size-4" />} onClick={() => previousUserId && onNavigate(previousUserId)} />
|
||||
<Button type="text" size="small" aria-label="下一条用户" disabled={!nextUserId} icon={<ChevronRight className="size-4" />} onClick={() => nextUserId && onNavigate(nextUserId)} />
|
||||
<IconButton size="sm" variant="ghost" aria-label="上一条用户" disabled={!previousUserId} icon={ChevronLeft} onClick={() => previousUserId && onNavigate(previousUserId)} />
|
||||
<IconButton size="sm" variant="ghost" aria-label="下一条用户" disabled={!nextUserId} icon={ChevronRight} onClick={() => nextUserId && onNavigate(nextUserId)} />
|
||||
</div>
|
||||
) : null}
|
||||
>
|
||||
@@ -225,7 +227,7 @@ export function AdminUserDetailDrawer({ userId, onClose, previousUserId, nextUse
|
||||
]}
|
||||
/>
|
||||
) : (
|
||||
<Empty description="没有用户详情" />
|
||||
<EmptyState size="compact" title="没有用户详情" />
|
||||
)}
|
||||
</Drawer>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { App, Button, DatePicker, Drawer, Form, Input, Modal, Select, Tabs, Tag } from "antd";
|
||||
import { Tooltip } from "@/components/ui/base/tooltip";
|
||||
import { useCallback, useEffect, useMemo, useState, type ReactNode } from "react";
|
||||
import { App, Button, DatePicker, Drawer, Form, Input, Modal, Select, Tabs, Tag, Tooltip } from "antd";
|
||||
|
||||
import type { ColumnsType } from "antd/es/table";
|
||||
import dayjs, { type Dayjs } from "dayjs";
|
||||
import { AlertTriangle, BarChart3, CircleDollarSign, Clock3, Gauge, Pencil, Plus, RefreshCw, Settings2, Trash2, UsersRound, Workflow } from "lucide-react";
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { App, Button, Descriptions, Drawer, Empty, Skeleton, Tabs, Typography } from "antd";
|
||||
import { App, Button, Descriptions, Drawer, Skeleton, Tabs, Typography } from "antd";
|
||||
import { EmptyState } from "@/components/ui/product/empty-state";
|
||||
import { RefreshCw } from "lucide-react";
|
||||
|
||||
import { formatCredits } from "@/constant/credits";
|
||||
@@ -49,7 +50,7 @@ export function ApiLogDetailDrawer({ logId, onClose, onLogUpdated }: { logId: st
|
||||
|
||||
return (
|
||||
<Drawer title="请求详情" open={Boolean(logId)} onClose={onClose} size="min(920px, 100vw)" destroyOnHidden rootClassName="admin-drawer">
|
||||
{loading ? <Skeleton active paragraph={{ rows: 12 }} /> : log ? <LogDetail log={log} querying={querying} onQueryProviderTask={queryProviderTask} /> : <Empty description="没有请求详情" />}
|
||||
{loading ? <Skeleton active paragraph={{ rows: 12 }} /> : log ? <LogDetail log={log} querying={querying} onQueryProviderTask={queryProviderTask} /> : <EmptyState size="compact" title="没有请求详情" />}
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
@@ -125,7 +126,7 @@ function requestKindText(value: ApiCallLog["requestKind"]) {
|
||||
}
|
||||
|
||||
function PayloadPanel({ value, empty }: { value?: string; empty: string }) {
|
||||
if (!value) return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={empty} />;
|
||||
if (!value) return <EmptyState size="compact" title={empty} />;
|
||||
return (
|
||||
<div className="relative">
|
||||
<div className="absolute right-3 top-2 z-10">
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { App, Button, Form, Input, InputNumber, Select, Skeleton, Switch } from "antd";
|
||||
import { App, Button, Form, Input, InputNumber, Skeleton } from "antd";
|
||||
import { Select } from "@/components/ui/base/select";
|
||||
import { Switch } from "@/components/ui/base/switch";
|
||||
import { AlertTriangle, AtSign, BadgeCheck, KeyRound, MailCheck, RefreshCw, RotateCcw, Save, Send, Server } from "lucide-react";
|
||||
import { useCallback, useEffect, useRef, useState, type ReactNode } from "react";
|
||||
import { useBlocker } from "react-router";
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useCallback, useEffect, useRef, useState, type ReactNode } from "react";
|
||||
import { App, Button, Skeleton, Switch } from "antd";
|
||||
import { App, Button, Skeleton } from "antd";
|
||||
import { Switch } from "@/components/ui/base/switch";
|
||||
import { AlertTriangle, Clapperboard, Coins, ListChecks, LockKeyhole, MonitorCog, PlugZap, RadioTower, RefreshCw, ShieldCheck, Sparkles } from "lucide-react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
Reference in New Issue
Block a user