From 1ffc6d7e1d17043614bf6530cb90a646d84291a6 Mon Sep 17 00:00:00 2001 From: Gloridust Date: Sun, 14 Jun 2026 19:01:03 +0800 Subject: [PATCH] =?UTF-8?q?feat(v1.2.0):=20=E5=AE=9E=E4=BE=8B=E5=A4=B4?= =?UTF-8?q?=E5=83=8F=E7=94=B1=E9=A6=96=E5=AD=97=E6=AF=8D=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=E6=8C=89=E5=BA=94=E7=94=A8=E7=B1=BB=E5=9E=8B=E7=9A=84=E5=9B=BE?= =?UTF-8?q?=E6=A0=87=EF=BC=88=E8=87=AA=E5=AE=9A=E4=B9=89=E5=9B=BE=E6=A0=87?= =?UTF-8?q?=E9=93=BA=E5=BA=95=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 AppIcon.tsx(InstanceIcon + 内置 SVG 图标:微信/Chromium/Telegram/通用),侧栏与主页 卡片头像改用它(按 appType 出默认图标;data: 图片 / builtin: 优先)。 新增 Instance.icon 字段 + publicInstance 下发,为「自定义图标(内置选择 + 上传裁剪)」铺底。 侧栏/主页标题「微信实例」→「实例」,主页副文案按应用名泛化。 Co-Authored-By: Claude Opus 4.8 --- panel/server/src/store.ts | 2 ++ panel/web/src/AppIcon.tsx | 74 ++++++++++++++++++++++++++++++++++++++ panel/web/src/AppShell.tsx | 24 +++++++------ panel/web/src/api.ts | 1 + panel/web/src/styles.css | 18 ---------- 5 files changed, 91 insertions(+), 28 deletions(-) create mode 100644 panel/web/src/AppIcon.tsx diff --git a/panel/server/src/store.ts b/panel/server/src/store.ts index 9d78238..d69d809 100644 --- a/panel/server/src/store.ts +++ b/panel/server/src/store.ts @@ -43,6 +43,7 @@ export interface Instance { id: string; // 短 id,用于容器/卷命名 name: string; // 显示名 appType?: AppType; // 承载的应用类型;缺省(老实例)= wechat(见 instanceAppType) + icon?: string; // 自定义图标:data: 图片(base64) 或 builtin:;缺省按 appType 取默认图标 containerName: string; // woc-wx- volumeName: string; // woc-data- kasmUser: string; // 随机生成,服务端注入反代,永不下发前端 @@ -216,6 +217,7 @@ export function publicInstance(i: Instance) { id: i.id, name: i.name, appType: instanceAppType(i), // 老实例无字段时回退 wechat + icon: i.icon, createdAt: i.createdAt, createdBy: i.createdBy, memSoftLimitMB: i.memSoftLimitMB, diff --git a/panel/web/src/AppIcon.tsx b/panel/web/src/AppIcon.tsx new file mode 100644 index 0000000..f84d667 --- /dev/null +++ b/panel/web/src/AppIcon.tsx @@ -0,0 +1,74 @@ +import type { AppType } from './api'; + +// 实例图标。支持三种来源(优先级从高到低): +// 1) 自定义上传/裁剪的图片 → inst.icon = "data:image/...;base64,..." +// 2) 内置图标 → inst.icon = "builtin:"(如 builtin:xiaohongshu) +// 3) 缺省:按 appType 给默认图标(微信 / Chromium / Telegram / 通用) +// 内置图标用简洁 SVG(彩色圆角块 + 白色字形),风格统一、无需联网抓取。后续可往 BUILTIN 里加更多平台。 + +type Glyph = { bg: string; el: JSX.Element }; +const G = (bg: string, el: JSX.Element): Glyph => ({ bg, el }); + +// 白色字形(viewBox 0 0 48 48,置于彩色圆角块上) +const chat = ( + +); +const globe = ( + + + + + +); +const plane = ; +const dots = ( + + + + + +); + +// key → 字形。default-by-appType 与「内置图标选择器」共用同一张表。 +export const BUILTIN_ICONS: Record = { + wechat: G('#07c160', chat), + chromium: G('#4285f4', globe), + telegram: G('#2aabee', plane), + globe: G('#5b8def', globe), + app: G('#8a9099', dots), +}; +const DEFAULT_BY_APP: Record = { + wechat: 'wechat', + chromium: 'chromium', + telegram: 'telegram', + custom: 'app', +}; + +export function InstanceIcon({ + icon, + appType, + size = 36, + radius = 12, +}: { + icon?: string; + appType?: AppType; + size?: number; + radius?: number; +}) { + // 1) 自定义图片 + if (icon && icon.startsWith('data:')) { + return ; + } + // 2) 内置 / 3) 默认 + const key = icon && icon.startsWith('builtin:') ? icon.slice(8) : DEFAULT_BY_APP[appType ?? 'wechat'] ?? 'app'; + const g = BUILTIN_ICONS[key] ?? BUILTIN_ICONS.app; + return ( + + ); +} diff --git a/panel/web/src/AppShell.tsx b/panel/web/src/AppShell.tsx index 15b1bf5..48a66cf 100644 --- a/panel/web/src/AppShell.tsx +++ b/panel/web/src/AppShell.tsx @@ -2,7 +2,8 @@ import { createContext, useContext, useEffect, useRef, useState, type ReactNode import { Navigate, Route, Routes, useLocation, useNavigate } from 'react-router-dom'; import { useAuth } from './auth'; import { useUI, PasswordInput } from './ui'; -import { api, type InstanceWithStatus } from './api'; +import { api, appProfile, type InstanceWithStatus } from './api'; +import { InstanceIcon } from './AppIcon'; import InstanceView from './pages/Desktop'; import Admin from './pages/Admin'; @@ -175,7 +176,7 @@ function Sidebar({ collapsed, onToggleCollapsed }: { collapsed: boolean; onToggl - {!collapsed &&
微信实例
} + {!collapsed &&
实例
}
{instances.length === 0 && !collapsed &&
暂无可用实例
} {instances.map((inst) => { @@ -184,7 +185,7 @@ function Sidebar({ collapsed, onToggleCollapsed }: { collapsed: boolean; onToggl return (
) : (
{instances.map((inst) => { const st = statusOf(inst); + const prof = appProfile(inst.appType); const meta = inst.wechat.installed - ? `微信 ${inst.wechat.version || ''}`.trim() - : inst.runtime === 'running' - ? '待下载安装微信' + ? `${prof.label} ${inst.wechat.version || ''}`.trim() + : inst.runtime === 'running' && prof.needsInstall + ? `待下载安装${prof.label}` : ''; return (