diff --git a/app/(admin)/settings/_components/admin-settings-navigation.tsx b/app/(admin)/settings/_components/admin-settings-navigation.tsx new file mode 100644 index 000000000..5fc9010c0 --- /dev/null +++ b/app/(admin)/settings/_components/admin-settings-navigation.tsx @@ -0,0 +1,91 @@ +"use client"; + +import Link from "next/link"; +import { TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { ADMIN_SETTINGS_NAV_GROUPS } from "../_lib/settings-navigation"; +import { SettingStatusBadge } from "./setting-status-badge"; + +export function AdminSettingsNavigation({ activeTab }: { activeTab: string }) { + return ( +
+
+
+
+ Admin Console +
+

+ 系统设置中心 +

+

+ 按后台人员的操作路径重新组织入口:先处理运营数据,再调整站点体验,最后管理模型与基础设施能力。 +

+
+
+
+ {ADMIN_SETTINGS_NAV_GROUPS.map((group) => { + const hasActiveTab = group.items.some( + (item) => item.value === activeTab + ); + return ( +
+
+
+

{group.title}

+

+ {group.description} +

+
+ {hasActiveTab ? ( + 当前 + ) : null} +
+ + {group.items.map((item) => { + const Icon = item.icon; + const triggerClassName = + "h-auto justify-start gap-2 rounded-md border border-transparent px-3 py-2 text-left data-[state=active]:border-border data-[state=active]:bg-muted/50"; + const content = ( + <> + + + + {item.label} + + + {item.description} + + + + ); + + if (item.href) { + return ( + + {content} + + ); + } + + return ( + + {content} + + ); + })} + +
+ ); + })} +
+
+ ); +} diff --git a/app/(admin)/settings/_components/local-ai-command-center.tsx b/app/(admin)/settings/_components/local-ai-command-center.tsx new file mode 100644 index 000000000..8fa8e23c4 --- /dev/null +++ b/app/(admin)/settings/_components/local-ai-command-center.tsx @@ -0,0 +1,148 @@ +"use client"; + +import { Button } from "@/components/ui/button"; +import { cn } from "@/lib/utils"; +import { LOCAL_AI_QUICK_LINK_GROUPS } from "../_lib/local-ai-sections"; +import type { LocalAiOverviewItem, LocalAiQuickLink } from "../_lib/types"; +import { SettingStatusBadge } from "./setting-status-badge"; + +export function LocalAiCommandCenter({ + openSectionCount, + providerCount, + modelCount, + overviewItems, + quickLinks, + isSectionExpanded, + onFocusSection, + onSetAllExpanded, +}: { + openSectionCount: number; + providerCount: number; + modelCount: number; + overviewItems: LocalAiOverviewItem[]; + quickLinks: LocalAiQuickLink[]; + isSectionExpanded: (sectionKey: string) => boolean; + onFocusSection: (sectionKey: string) => void; + onSetAllExpanded: (expanded: boolean) => void; +}) { + return ( +
+
+
+
+
+ SYSTEM INTEGRATIONS +
+

+ 高级能力与外部服务 +

+

+ 把商品策划、图像处理、3D/CAD、实时画布和开放 API + 按运维任务重新归类。折叠状态会保留,常用模块可以保持展开。 +

+
+
+ + 已展开 {openSectionCount} 个模块 + + + 供应商 {providerCount} + + + 模型 {modelCount} + + + +
+
+
+
+ {overviewItems.map((item) => { + const Icon = item.icon; + return ( + + ); + })} +
+
+
+ {LOCAL_AI_QUICK_LINK_GROUPS.map((group) => ( +
+
+ {group} +
+
+ {quickLinks + .filter((item) => item.group === group) + .map((item) => ( + + ))} +
+
+ ))} +
+
+
+ ); +} diff --git a/app/(admin)/settings/_components/local-ai-section-shell.tsx b/app/(admin)/settings/_components/local-ai-section-shell.tsx new file mode 100644 index 000000000..5a7268368 --- /dev/null +++ b/app/(admin)/settings/_components/local-ai-section-shell.tsx @@ -0,0 +1,121 @@ +"use client"; + +import { ArrowDown, ArrowUp } from "lucide-react"; +import type { ReactNode } from "react"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { cn } from "@/lib/utils"; +import type { SettingStatusTone } from "../_lib/types"; +import { SettingStatusBadge } from "./setting-status-badge"; + +export function LocalAiGroupHeading({ + eyebrow, + title, + description, +}: { + eyebrow: string; + title: string; + description: string; +}) { + return ( +
+
+ {eyebrow} +
+
+

{title}

+

{description}

+
+
+ ); +} + +export function LocalAiSectionSummary({ + items, +}: { + items: Array<{ + label: string; + tone?: SettingStatusTone; + }>; +}) { + return ( +
+ {items.map((item) => ( + + {item.label} + + ))} +
+ ); +} + +export function LocalAiSectionToggleButton({ + expanded, + onClick, +}: { + expanded: boolean; + onClick: () => void; +}) { + return ( + + ); +} + +export function LocalAiSectionShell({ + id, + title, + description, + summary, + expanded, + onToggle, + children, +}: { + id: string; + title: ReactNode; + description: ReactNode; + summary?: ReactNode; + expanded: boolean; + onToggle: () => void; + children: ReactNode; +}) { + return ( + + +
+ {title} + {description} + {summary} +
+ +
+ + {children} + +
+ ); +} diff --git a/app/(admin)/settings/_components/local-ai-sections/image-pipeline-sections.tsx b/app/(admin)/settings/_components/local-ai-sections/image-pipeline-sections.tsx new file mode 100644 index 000000000..c2ef656c8 --- /dev/null +++ b/app/(admin)/settings/_components/local-ai-sections/image-pipeline-sections.tsx @@ -0,0 +1,2068 @@ +"use client"; + +import { + ArrowDown, + ArrowUp, + Check, + Layers, + Menu, + Move3D, + Plus, + RefreshCw, + Save, + Trash2, +} from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Separator } from "@/components/ui/separator"; +import { Switch } from "@/components/ui/switch"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Textarea } from "@/components/ui/textarea"; +import { + ENHANCE_PRO_KREA_DEFAULT_BASE_URL, + ENHANCE_PRO_KREA_DEFAULT_MODEL, + ENHANCE_PRO_KREA_MODELS, + type EnhanceProKreaModel, + type EnhanceProPreset, +} from "@/lib/enhance-pro/config"; +import { cn } from "@/lib/utils"; +import type { + EnhanceProKeyRow, + LocalAiImagePipelineSectionsProps, +} from "../../_lib/types"; +import { + LocalAiGroupHeading, + LocalAiSectionShell, + LocalAiSectionSummary, +} from "../local-ai-section-shell"; + +export function LocalAiImagePipelineSections({ + rmbgConfig, + setRmbgConfig, + enhanceConfig, + setEnhanceConfig, + enhanceProConfig, + setEnhanceProConfig, + enhanceProApiKeysDraft, + setEnhanceProApiKeysDraft, + enhanceProApiKeysDraftCount, + draftEnhanceProKeyRows, + savedEnhanceProKeyRows, + enhanceProPresetEditorMode, + setEnhanceProPresetEditorMode, + selectedEnhanceProPresetId, + setSelectedEnhanceProPresetId, + selectedEnhanceProPreset, + selectedEnhanceProPresetIndex, + draggingEnhanceProPresetId, + extendConfig, + setExtendConfig, + watermarkRemoveConfig, + setWatermarkRemoveConfig, + angleEditConfig, + setAngleEditConfig, + layerSplitConfig, + setLayerSplitConfig, + isLocalAiSectionExpanded, + toggleLocalAiSection, + renderEnhanceProPresetEditor, + getEnhanceProPresetSummary, + handleSaveRmbgConfig, + handleSaveEnhanceConfig, + handleSaveEnhanceProConfig, + handleAddEnhanceProPoolKey, + handleDeleteEnhanceProPoolKey, + handleToggleEnhanceProModelVisible, + handleAddEnhanceProPreset, + handleResetEnhanceProPresets, + handleEnhanceProPresetDragStart, + handleEnhanceProPresetDragEnd, + handleEnhanceProPresetDrop, + handleMoveEnhanceProPreset, + handleSaveExtendConfig, + handleSaveWatermarkRemoveConfig, + handleSaveAngleEditConfig, + handleSaveLayerSplitConfig, +}: LocalAiImagePipelineSectionsProps) { + return ( + <> + {/* 抠图API配置 */} + + + + 配置背景移除API,用于画图标注功能中的抠图处理。支持自部署RMBG、PhotoRoom和AOS云服务。 + + } + expanded={isLocalAiSectionExpanded("rmbg")} + id="local-ai-section-rmbg" + onToggle={() => toggleLocalAiSection("rmbg")} + summary={ + + } + title="抠图API配置" + > + {/* 服务提供商选择 */} +
+ +
+
setRmbgConfig({ ...rmbgConfig, provider: "rmbg" })} + > +
+ {rmbgConfig.provider === "rmbg" && ( + + )} +
+
+

+ RMBG-2.0 (自部署) +

+
+
+ +
+ setRmbgConfig({ ...rmbgConfig, provider: "photoroom" }) + } + > +
+ {rmbgConfig.provider === "photoroom" && ( + + )} +
+
+

+ PhotoRoom (云服务) +

+
+
+ +
+ setRmbgConfig({ ...rmbgConfig, provider: "aoscdn" }) + } + > +
+ {rmbgConfig.provider === "aoscdn" && ( + + )} +
+
+

+ AOS Segmentation +

+
+
+
+
+ + + + {/* RMBG 配置 */} + {rmbgConfig.provider === "rmbg" && ( + <> +
+ RMBG-2.0 配置 +
+
+ + + setRmbgConfig({ ...rmbgConfig, apiUrl: e.target.value }) + } + placeholder="http://156.225.27.52:8000" + value={rmbgConfig.apiUrl} + /> +
+ 请输入完整的API地址,例如: http://156.225.27.52:8000 +
+
+
+ + + setRmbgConfig({ ...rmbgConfig, apiKey: e.target.value }) + } + placeholder="如果API需要密钥,请在此输入" + type="password" + value={rmbgConfig.apiKey} + /> +
+
+
+ RMBG-2.0 说明 +
+
+

• 需要自行部署RMBG-2.0服务

+

• 系统会自动调用 /rmbg/base64 接口进行抠图处理

+

• 支持的图片格式:JPG, PNG, JPEG等常见格式

+
+
+ + )} + + {/* PhotoRoom 配置 */} + {rmbgConfig.provider === "photoroom" && ( + <> +
+ PhotoRoom 配置 +
+
+ + + setRmbgConfig({ + ...rmbgConfig, + photoroomApiKey: e.target.value, + }) + } + placeholder="输入PhotoRoom API密钥" + type="password" + value={rmbgConfig.photoroomApiKey} + /> +
+ 在 PhotoRoom 开发者平台获取API密钥 +
+
+
+
+ PhotoRoom 说明 +
+
+

• 使用 PhotoRoom 云服务进行高清抠图

+

• 支持 HD 高清背景移除,效果更好

+

• API文档:https://www.photoroom.com/api

+

• 按调用次数计费,请注意用量

+
+
+ + )} + + {/* AOS 配置 */} + {rmbgConfig.provider === "aoscdn" && ( + <> +
+ AOS Segmentation 配置 +
+
+ + + setRmbgConfig({ + ...rmbgConfig, + aosApiKey: e.target.value, + }) + } + placeholder="输入 AOS X-API-KEY" + type="password" + value={rmbgConfig.aosApiKey} + /> +
+
+ + + setRmbgConfig({ + ...rmbgConfig, + aosApiBaseUrl: e.target.value, + }) + } + placeholder="https://techsz.aoscdn.com" + value={rmbgConfig.aosApiBaseUrl} + /> +
+ 默认使用官方地址,可按需替换。 +
+
+
+
+ + +
+
+ + +
+
+
+
+ +
+ 关闭时保持原图尺寸,开启时按主体区域裁剪。 +
+
+
+ + setRmbgConfig({ ...rmbgConfig, aosCrop: checked }) + } + /> + + {rmbgConfig.aosCrop ? "已开启" : "已关闭"} + +
+
+
+
+ AOS 说明 +
+
+

• 使用异步任务模式(1秒轮询,最多30秒)

+

• 接口返回链接有效期1小时,系统会自动转存到你的存储桶

+

• 官方限制:图片最大20MB,分辨率最大4096x4096

+
+
+ + )} + + +
+ + {/* 变清晰API配置 */} + + 配置图片变清晰API,用于画图标注功能中的图片增强处理。支持主API和备用API切换。 + + } + expanded={isLocalAiSectionExpanded("enhance")} + id="local-ai-section-enhance" + onToggle={() => toggleLocalAiSection("enhance")} + summary={ + + } + title="变清晰API配置" + > + {/* API切换开关 */} +
+
+ +
+ 启用后将使用Real-ESRGAN本地模型进行图片增强,效果更好 +
+
+
+ + setEnhanceConfig({ + ...enhanceConfig, + useBackupApi: checked, + }) + } + /> + + {enhanceConfig.useBackupApi ? "已开启" : "已关闭"} + +
+
+ + {/* 主API配置 */} + {!enhanceConfig.useBackupApi && ( + <> + +
+ 主API配置 (第三方云服务) +
+
+ + + setEnhanceConfig({ + ...enhanceConfig, + apiUrl: e.target.value, + }) + } + placeholder="https://techsz.aoscdn.com" + value={enhanceConfig.apiUrl} + /> +
+ 请输入完整的API地址,例如: https://techsz.aoscdn.com +
+
+
+ + + setEnhanceConfig({ + ...enhanceConfig, + apiKey: e.target.value, + }) + } + placeholder="请输入API密钥" + type="password" + value={enhanceConfig.apiKey} + /> +
+ API密钥是必需的,请从服务提供商获取 +
+
+
+ + +
+ 选择图片类型:通用或人像(人像模式最多同时处理10个人) +
+
+
+ + +
+ 选择放大倍数:自动(根据图片尺寸智能选择)、不放大、2倍或4倍 +
+
+
+
+ 主API文档说明 +
+
+

+ • API地址应指向服务根地址,例如: https://techsz.aoscdn.com +

+

+ • 系统会自动调用 /api/tasks/visual/scale 接口进行变清晰处理 +

+

• 支持的图片格式:JPG, JPEG, PNG, BMP, WEBP, TIFF等

+

+ • 图片大小:不超过50MB,不限制分辨率(建议按业务需求控制尺寸) +

+

• 返回的链接有效期为1小时,系统会自动下载并存储到S3

+
+
+ + )} + + {/* 备用API配置 (Real-ESRGAN) */} + {enhanceConfig.useBackupApi && ( + <> + +
+ 备用API配置 (Real-ESRGAN 本地模型) +
+
+ + + setEnhanceConfig({ + ...enhanceConfig, + backupApiUrl: e.target.value, + }) + } + placeholder="http://156.225.27.52:5233" + value={enhanceConfig.backupApiUrl} + /> +
+ Real-ESRGAN API服务地址,默认: http://156.225.27.52:5233 +
+
+
+ + + setEnhanceConfig({ + ...enhanceConfig, + backupApiKey: e.target.value, + }) + } + placeholder="请输入Real-ESRGAN API密钥" + type="password" + value={enhanceConfig.backupApiKey} + /> +
+ API密钥用于认证,请从管理员处获取 +
+
+
+
+ + +
+ 推荐使用4倍放大,效果最佳 +
+
+
+ + +
+ 不同模型适用于不同场景 +
+
+
+
+
+ Real-ESRGAN API说明 +
+
+

• 基于Real-ESRGAN深度学习模型,图像超分辨率效果更好

+

• 使用URL方式上传图片,处理后自动保存到S3存储

+

• 支持2x、4x、8x放大倍数

+

• 处理时间:通常1-10秒,取决于图片大小

+

• 支持格式:JPG, PNG, JPEG等常见图片格式

+

• 文件限制:最大50MB

+
+
+ + )} + + +
+ + 配置 Krea Topaz Generative,用于无限画布中的“图像增强 Pro”能力。 + } + expanded={isLocalAiSectionExpanded("enhance-pro")} + id="local-ai-section-enhance-pro" + onToggle={() => toggleLocalAiSection("enhance-pro")} + summary={ + 0 + ? "enabled" + : "warning", + }, + { + label: + enhanceProConfig.defaultModel || + ENHANCE_PRO_KREA_DEFAULT_MODEL, + tone: "neutral", + }, + ]} + /> + } + title="图像增强 Pro 配置" + > +
+
+ +
+ 关闭后无限画布仍保留普通“高清增强”,但不再开放 Krea Topaz + Generative 参数面板。 +
+
+
+ + setEnhanceProConfig((prev) => ({ + ...prev, + enabled: checked, + })) + } + /> + + {enhanceProConfig.enabled ? "已开启" : "已关闭"} + +
+
+ +
+
+ + + setEnhanceProConfig((prev) => ({ + ...prev, + providerName: e.target.value, + })) + } + placeholder="Krea Topaz Generative" + value={enhanceProConfig.providerName} + /> +
+
+ + + setEnhanceProConfig((prev) => ({ + ...prev, + baseUrl: e.target.value, + })) + } + placeholder={ENHANCE_PRO_KREA_DEFAULT_BASE_URL} + value={enhanceProConfig.baseUrl} + /> +
+
+ +
+ +
+ + setEnhanceProConfig((prev) => ({ + ...prev, + apiKey: e.target.value, + })) + } + placeholder="请输入 Krea Bearer Token" + type="password" + value={enhanceProConfig.apiKey} + /> + +
+
+ 和 Tripo3D 一样,单个 Key + 可以直接加入池;批量导入仍支持一次粘贴多行。 +
+
+ +
+
+
+
Krea Key 池
+
+ 当前共 {enhanceProConfig.apiKeys.length} 个 Key + {enhanceProApiKeysDraft.trim() + ? `,本次保存会替换为 ${enhanceProApiKeysDraftCount} 个 Key` + : ",留空保存不会覆盖已保存池"} + 。增强 Pro 任务会轮询使用可用 Key。 +
+
+
+ + +
+
+ +
+ +