feat: 节点/服务器批量编辑 Sheet、Server 表单协议合并修复、设备类型解析
Build and Release / Build (push) Has been cancelled

- 新增 NodeBatchSheet 与 ServerBatchSheet,支持批量选中后一键编辑公共字段
- Server 表单:引入 mergeProtocol / buildSanitizedProtocols,过滤空字符串避免 Zod 枚举校验失败;renderGroupCard 改为具名导出供批量 Sheet 复用;优化嵌套错误提示递归取第一条
- 新增 parseDeviceType 工具函数,从 User-Agent 解析设备平台(iPhone/Android/Mac 等)
- 用户列表:登录标识旁展示设备平台 Badge
- 注册安全设置表单:引入 Textarea 组件并完善字段布局
- 类型定义补充及 i18n 本地化更新

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-01 05:17:01 -07:00
parent de87133061
commit 7400137b3c
19 changed files with 1422 additions and 142 deletions
+1
View File
@@ -55,6 +55,7 @@ function SheetContent({
<SheetPortal>
<SheetOverlay />
<SheetPrimitive.Content
aria-describedby={undefined}
className={cn(
"fixed z-50 flex flex-col gap-4 bg-background shadow-lg transition ease-in-out data-[state=closed]:animate-out data-[state=open]:animate-in data-[state=closed]:duration-300 data-[state=open]:duration-500",
side === "right" &&
+3
View File
@@ -1791,6 +1791,8 @@ declare namespace API {
trial_subscribe: number;
trial_time: number;
trial_time_unit: string;
enable_trial_email_whitelist: boolean;
trial_email_domain_whitelist: string;
enable_ip_register_limit: boolean;
ip_register_limit: number;
ip_register_limit_duration: number;
@@ -2076,6 +2078,7 @@ declare namespace API {
type SubscribeDiscount = {
quantity: number;
discount: number;
map_apple?: string;
};
type SubscribeGroup = {
+1
View File
@@ -909,6 +909,7 @@ declare namespace API {
type SubscribeDiscount = {
quantity: number;
discount: number;
map_apple?: string;
};
type SubscribeGroup = {
+1
View File
@@ -1003,6 +1003,7 @@ declare namespace API {
type SubscribeDiscount = {
quantity: number;
discount: number;
map_apple?: string;
};
type SubscribeGroup = {
+18
View File
@@ -31,3 +31,21 @@ export function shortenDeviceIdentifier(identifier: string): string {
.slice(0, 8)
.toUpperCase();
}
/**
* 从 User-Agent 字符串解析设备平台类型
* 与后端 parseDeviceType 逻辑保持一致
* @param userAgent - 设备的 User-Agent 字符串
* @returns 设备平台类型字符串,如 "iPhone"、"Android" 等,未识别时返回空字符串
*/
export function parseDeviceType(userAgent: string): string {
if (!userAgent) return "";
const ua = userAgent.toLowerCase();
if (ua.includes("iphone")) return "iPhone";
if (ua.includes("ipad")) return "iPad";
if (ua.includes("android")) return "Android";
if (ua.includes("windows")) return "Windows";
if (ua.includes("macintosh") || ua.includes("mac os")) return "Mac";
if (ua.includes("linux")) return "Linux";
return "";
}