feat(device,feedback): 新增设备识别码与意见反馈功能

重构意见反馈设置页面,替换原有占位内容,新增FeedbackButton组件
更新DEV.md与README.md文档,补充对应功能的相关说明
This commit is contained in:
2026-09-04 20:16:51 +08:00
parent 9eaa482a4d
commit 8b7c90b730
9 changed files with 804 additions and 167 deletions
+10
View File
@@ -11,6 +11,16 @@ export async function getSystemInfo(): Promise<SystemInfo> {
return ipcInvoke<SystemInfo>('system:info');
}
/** 设备唯一标识(组合指纹 + 回退 MachineGuid */
export interface DeviceIdentity {
deviceId: string | null;
source: 'board' | 'disk' | 'bios' | 'machine' | 'none';
}
export async function getDeviceId(): Promise<DeviceIdentity> {
return ipcInvoke<DeviceIdentity>('system:deviceId');
}
// ---- 进程内存快照(资源/内存调试面板用)----
/** app.getAppMetrics() 的进程项;workingSetSize 单位为 KB */
@@ -0,0 +1,30 @@
// 定制反馈按钮(HeroUI):点击后用系统浏览器打开 YouTrack 反馈表单直链。
import { ComponentProps } from "react";
import { Button } from "@heroui/react";
import { MessageSquareHeart, ExternalLink } from "lucide-react";
import { toast } from "sonner";
const FORM_DIRECT_URL = "https://lingke.youtrack.cloud/form/aa6a005d-2559-4e92-a0f5-d3898f377ef8";
type FeedbackButtonProps = {
/** 按钮文案,默认「意见反馈」 */
label?: string;
} & Omit<ComponentProps<typeof Button>, "children">;
export function FeedbackButton({ label = "意见反馈", ...buttonProps }: FeedbackButtonProps) {
const openFeedback = async () => {
try {
await window.electronAPI?.openExternal(FORM_DIRECT_URL);
} catch {
toast.error("无法打开反馈页面,请稍后重试");
}
};
return (
<Button variant="primary" size="md" onPress={openFeedback} {...buttonProps}>
<MessageSquareHeart className="w-4 h-4" />
{label}
<ExternalLink className="w-3 h-3 opacity-70" />
</Button>
);
}
+24
View File
@@ -14,6 +14,7 @@ import {
type UpdateChannelDef,
} from "@/api/update";
import { toast } from "sonner";
import { getDeviceId, type DeviceIdentity } from "@/api/system";
import {
AlertDialog,
AlertDialogTrigger,
@@ -46,6 +47,22 @@ export function AboutSetting() {
const [countdown, setCountdown] = useState(5);
const canConfirm = countdown <= 0;
// 设备识别码(组合指纹:主板/硬盘/BIOS → 回退系统安装标识)
const [device, setDevice] = useState<DeviceIdentity | null>(null);
useEffect(() => {
let cancelled = false;
getDeviceId()
.then((d) => {
if (!cancelled) setDevice(d);
})
.catch(() => {
if (!cancelled) setDevice(null);
});
return () => {
cancelled = true;
};
}, []);
// 更新通道(下拉框,选项来自主进程通道注册表)
const [channels, setChannels] = useState<UpdateChannelDef[]>([]);
const [activeChannel, setActiveChannel] = useState("woker");
@@ -132,6 +149,13 @@ export function AboutSetting() {
<span className="text-[13px] text-muted-foreground">Node.js</span>
</SettingRow>
</SettingCard>
<SettingCard>
<SettingRow label="设备识别码" desc="设备追踪ID">
<span className="text-[13px] text-muted-foreground font-mono">
{device?.deviceId ?? "—"}
</span>
</SettingRow>
</SettingCard>
</div>
</div>
+24 -5
View File
@@ -1,15 +1,34 @@
import { EmptyState } from "@heroui/react";
import { MessageSquareHeart } from "lucide-react";
import { PageHeader } from "@/components/setting";
import { FeedbackButton } from "@/components/feedback/FeedbackButton";
export function FeedbackSetting() {
return (
<div>
<PageHeader title="服务与反馈" desc="提交问题反馈、功能建议与联系开发团队" />
<EmptyState className="py-16">
<MessageSquareHeart className="w-10 h-10 text-muted-foreground/30" />
<p className="text-sm text-muted-foreground mt-3"></p>
</EmptyState>
<div className="space-y-6">
<div className="rounded-xl border border-black/[0.06] dark:border-white/[0.07] bg-white/85 dark:bg-black/45 backdrop-blur-[12px] px-5 py-8 flex flex-col items-center text-center gap-3">
<div className="w-12 h-12 rounded-full bg-foreground/[0.05] dark:bg-white/[0.05] flex items-center justify-center">
<MessageSquareHeart className="w-6 h-6 text-muted-foreground/60" />
</div>
<div className="space-y-1">
<p className="text-sm font-medium text-foreground"></p>
<p className="text-[12.5px] text-muted-foreground max-w-sm leading-relaxed">
</p>
</div>
<FeedbackButton label="填写反馈表单" className="mt-1" />
</div>
<div className="rounded-xl border border-black/[0.06] dark:border-white/[0.07] bg-white/85 dark:bg-black/45 backdrop-blur-[12px] px-5 py-4">
<p className="text-sm font-medium text-foreground"></p>
<ul className="mt-2 space-y-1.5 text-[12.5px] text-muted-foreground leading-relaxed list-disc pl-4">
<li> YouTrack 线</li>
<li></li>
</ul>
</div>
</div>
</div>
);
}