diff --git a/panel/web/src/api.ts b/panel/web/src/api.ts index d8b0b3c..1e553cc 100644 --- a/panel/web/src/api.ts +++ b/panel/web/src/api.ts @@ -26,6 +26,24 @@ export const APP_LABELS: Record = { chromium: 'Chromium', custom: '自定义应用', }; + +// 各应用的 UI 画像,供卡片/桌面页按类型显示正确文案(避免到处写死「微信」)。 +// needsInstall: 是否需要运行时下载安装(微信/Telegram 是;Chromium 已烤进镜像、即创建即就绪)。 +// enterHint: 首次进入实例的提示。 +// updateLabel: 「管理」菜单里的更新按钮文案(needsInstall=false 时不显示)。 +export interface AppProfile { + label: string; + needsInstall: boolean; + enterHint: string; + updateLabel: string; +} +export const APP_PROFILES: Record = { + wechat: { label: '微信', needsInstall: true, enterHint: '首次进入请扫码登录微信', updateLabel: '更新微信' }, + telegram: { label: 'Telegram', needsInstall: true, enterHint: '首次进入请登录 Telegram', updateLabel: '更新 Telegram' }, + chromium: { label: 'Chromium', needsInstall: false, enterHint: '浏览器已就绪,直接使用即可', updateLabel: '' }, + custom: { label: '自定义应用', needsInstall: true, enterHint: '', updateLabel: '更新' }, +}; +export const appProfile = (t?: AppType): AppProfile => APP_PROFILES[t ?? 'wechat'] ?? APP_PROFILES.wechat; export interface PanelInstance { id: string; name: string; diff --git a/panel/web/src/pages/Admin.tsx b/panel/web/src/pages/Admin.tsx index 6247984..5d21c1b 100644 --- a/panel/web/src/pages/Admin.tsx +++ b/panel/web/src/pages/Admin.tsx @@ -1,6 +1,6 @@ import { useEffect, useRef, useState } from 'react'; import { useNavigate } from 'react-router-dom'; -import { api, APP_LABELS, type PanelUser, type InstanceWithStatus, type VolEntry, type AppType } from '../api'; +import { api, APP_LABELS, appProfile, type PanelUser, type InstanceWithStatus, type VolEntry, type AppType } from '../api'; import { useUI, PasswordInput } from '../ui'; import { useAuth } from '../auth'; @@ -271,7 +271,7 @@ export default function Admin({ onOpenMenu, onChangePassword }: { onOpenMenu: () {isAdmin && ( <>
- 微信实例 + 实例 @@ -279,11 +279,11 @@ export default function Admin({ onOpenMenu, onChangePassword }: { onOpenMenu: () {instances.length === 0 ? ( setCreatingInst(true)}> - + 新建微信实例 + + 新建实例 } /> @@ -899,6 +899,8 @@ function InstanceAdminCard({ return () => document.removeEventListener('mousedown', onDocDown); }, [menuOpen]); + const profile = appProfile(inst.appType); + let badge: { text: string; cls: string }; if (acting) badge = { text: '处理中', cls: 'tag-busy' }; else if (offline) badge = { text: inst.runtime === 'missing' ? '未创建' : '已停止', cls: 'tag-off' }; @@ -911,8 +913,8 @@ function InstanceAdminCard({ else if (busy) sub = wx.percent >= 0 ? `${wx.message || '处理中'} ${wx.percent}%` : wx.message || '请稍候…'; else if (wx.phase === 'error') sub = wx.message || '操作失败,可重试'; else if (offline) sub = inst.runtime === 'missing' ? '容器尚未创建' : '容器已停止'; - else if (installed) sub = wx.version ? `微信 ${wx.version}` : '微信已安装'; - else sub = '微信尚未安装'; + else if (installed) sub = wx.version ? `${profile.label} ${wx.version}` : `${profile.label}已就绪`; + else sub = `${profile.label}尚未安装`; return (
@@ -943,7 +945,7 @@ function InstanceAdminCard({ {inst.runtime === 'missing' ? '创建并启动' : '启动实例'} ) : ( - )} @@ -960,9 +962,9 @@ function InstanceAdminCard({
运维
- {!offline && ( + {!offline && profile.needsInstall && ( )} ) : ( -
请联系管理员在「管理」中下载安装微信
+
请联系管理员在「管理」中下载安装{appLabel}
)} {isAdmin && ( -
也可直接把文件拖进来。下方为桌面(~/Desktop)里的文件,微信收到的文件另存到桌面即可在此下载。
+
也可直接把文件拖进来。下方为桌面(~/Desktop)里的文件,应用收到的文件另存到桌面即可在此下载。
{files.length === 0 && (
@@ -776,17 +778,17 @@ export default function InstanceView({ onOpenMenu }: { onOpenMenu: () => void }) className="clip-area" value={clipText} onChange={(e) => setClipText(e.target.value)} - placeholder="在此输入或粘贴文本,点「发送到微信」后到微信输入框按 Ctrl+V 粘贴" + placeholder="在此输入或粘贴文本,点「发送到剪贴板」后到应用输入框按 Ctrl+V 粘贴" rows={5} />
- 局域网 http 访问时浏览器会禁用系统级剪贴板同步,故用此框中转:文本→容器剪贴板,再在微信里 Ctrl+V。 + 局域网 http 访问时浏览器会禁用系统级剪贴板同步,故用此框中转:文本→容器剪贴板,再在应用里 Ctrl+V。
)} @@ -804,7 +806,7 @@ export default function InstanceView({ onOpenMenu }: { onOpenMenu: () => void }) sendImeText(); } }} - placeholder="中文输入这里 → 回车送进微信(先点好微信的输入框)。Shift+回车换行。" + placeholder="中文输入这里 → 回车送进应用(先点好应用的输入框)。Shift+回车换行。" rows={1} />