better fps testing ui + more info + comment + users minor update
This commit is contained in:
@@ -45,6 +45,15 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
|||||||
|
|
||||||
addToast({ title: `电源计划已切换 → ${PowerPlans[current].title}` })
|
addToast({ title: `电源计划已切换 → ${PowerPlans[current].title}` })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
void listen<number>("tray://set_launch_index", async (event) => {
|
||||||
|
const index = event.payload
|
||||||
|
if (typeof index === "number" && index >= 0 && index < toolStore.state.launchOptions.length) {
|
||||||
|
tool.setLaunchIndex(index)
|
||||||
|
const optionName = toolStore.state.launchOptions[index].name || `启动项 ${index + 1}`
|
||||||
|
addToast({ title: `启动项已切换 → ${optionName}` })
|
||||||
|
}
|
||||||
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
// 检测steam路径和游戏路径是否有效
|
// 检测steam路径和游戏路径是否有效
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export function AuthButton() {
|
|||||||
isIconOnly
|
isIconOnly
|
||||||
variant="light"
|
variant="light"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="px-2 py-0 rounded transition duration-150 hover:bg-black/10 dark:hover:bg-zinc-100/10 active:scale-95"
|
className="px-2 py-0 rounded transition duration-150 hover:bg-black/10 dark:hover:bg-zinc-100/10 active:scale-95 cursor-pointer"
|
||||||
>
|
>
|
||||||
<Spinner size="sm" />
|
<Spinner size="sm" />
|
||||||
</Button>
|
</Button>
|
||||||
@@ -46,13 +46,13 @@ export function AuthButton() {
|
|||||||
isIconOnly
|
isIconOnly
|
||||||
variant="light"
|
variant="light"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="px-2 py-0 rounded transition duration-150 hover:bg-black/10 dark:hover:bg-zinc-100/10 active:scale-95"
|
className="px-2 py-0 rounded transition duration-150 hover:bg-black/10 dark:hover:bg-zinc-100/10 active:scale-95 cursor-pointer [&>*]:cursor-pointer"
|
||||||
>
|
>
|
||||||
<Avatar
|
<Avatar
|
||||||
src={state.user.user_metadata?.avatar_url}
|
src={state.user.user_metadata?.avatar_url}
|
||||||
name={state.user.email || state.user.id}
|
name={state.user.email || state.user.id}
|
||||||
size="sm"
|
size="sm"
|
||||||
className="w-6 h-6"
|
className="w-6 h-6 cursor-pointer"
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownTrigger>
|
</DropdownTrigger>
|
||||||
@@ -115,9 +115,9 @@ export function AuthButton() {
|
|||||||
isIconOnly
|
isIconOnly
|
||||||
variant="light"
|
variant="light"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="px-2 py-0 rounded transition duration-150 hover:bg-black/10 dark:hover:bg-zinc-100/10 active:scale-95"
|
className="px-2 py-0 rounded transition duration-150 hover:bg-black/10 dark:hover:bg-zinc-100/10 active:scale-95 cursor-pointer [&>*]:cursor-pointer"
|
||||||
>
|
>
|
||||||
<User size={16} />
|
<User size={16} className="cursor-pointer" />
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownTrigger>
|
</DropdownTrigger>
|
||||||
<DropdownMenu aria-label="登录菜单">
|
<DropdownMenu aria-label="登录菜单">
|
||||||
|
|||||||
@@ -1,13 +1,36 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useSteamStore } from "@/store/steam"
|
import { useSteamStore } from "@/store/steam"
|
||||||
import { useToolStore } from "@/store/tool"
|
import { useToolStore } from "@/store/tool"
|
||||||
import { useFpsTestStore } from "@/store/fpsTest"
|
import { useFpsTestStore } from "@/store/fps_test"
|
||||||
import { invoke } from "@tauri-apps/api/core"
|
import { invoke } from "@tauri-apps/api/core"
|
||||||
import { Card, CardBody, CardHeader, CardIcon, CardTool } from "../window/Card"
|
import { Card, CardBody, CardHeader, CardIcon, CardTool } from "../window/Card"
|
||||||
import { addToast, Button, Chip, Spinner, Switch, Table, TableHeader, TableColumn, TableBody, TableRow, TableCell } from "@heroui/react"
|
import {
|
||||||
|
addToast,
|
||||||
|
Button,
|
||||||
|
Chip,
|
||||||
|
Spinner,
|
||||||
|
Table,
|
||||||
|
TableHeader,
|
||||||
|
TableColumn,
|
||||||
|
TableBody,
|
||||||
|
TableRow,
|
||||||
|
TableCell,
|
||||||
|
Tabs,
|
||||||
|
Tab,
|
||||||
|
Tooltip,
|
||||||
|
Input,
|
||||||
|
Modal,
|
||||||
|
ModalContent,
|
||||||
|
ModalHeader,
|
||||||
|
ModalBody,
|
||||||
|
ModalFooter,
|
||||||
|
Textarea,
|
||||||
|
useDisclosure,
|
||||||
|
} from "@heroui/react"
|
||||||
import { useState, useEffect, useRef, useCallback } from "react"
|
import { useState, useEffect, useRef, useCallback } from "react"
|
||||||
import { TestTube, Power, List, Delete } from "@icon-park/react"
|
import { TestTube, Power, List, Delete, Play, Edit, Check, Close, Square } from "@icon-park/react"
|
||||||
import { allSysInfo, type AllSystemInfo } from "tauri-plugin-system-info-api"
|
import { allSysInfo, type AllSystemInfo } from "tauri-plugin-system-info-api"
|
||||||
|
import { ToolButton } from "../window/ToolButton"
|
||||||
|
|
||||||
const BENCHMARK_MAPS = [
|
const BENCHMARK_MAPS = [
|
||||||
{
|
{
|
||||||
@@ -24,6 +47,8 @@ const BENCHMARK_MAPS = [
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const TEST_TIMEOUT = 200000 // 200秒超时时间(毫秒)
|
||||||
|
|
||||||
// 解析性能报告,提取时间戳和性能数据
|
// 解析性能报告,提取时间戳和性能数据
|
||||||
function parseVProfReport(rawReport: string): { timestamp: string; data: string } | null {
|
function parseVProfReport(rawReport: string): { timestamp: string; data: string } | null {
|
||||||
if (!rawReport) return null
|
if (!rawReport) return null
|
||||||
@@ -156,18 +181,47 @@ export function FpsTest() {
|
|||||||
const steam = useSteamStore()
|
const steam = useSteamStore()
|
||||||
const tool = useToolStore()
|
const tool = useToolStore()
|
||||||
const fpsTest = useFpsTestStore()
|
const fpsTest = useFpsTestStore()
|
||||||
const [testing, setTesting] = useState(false)
|
|
||||||
const [testResult, setTestResult] = useState<string | null>(null)
|
const [testResult, setTestResult] = useState<string | null>(null)
|
||||||
const [testTimestamp, setTestTimestamp] = useState<string | null>(null)
|
const [testTimestamp, setTestTimestamp] = useState<string | null>(null)
|
||||||
const [selectedMap, setSelectedMap] = useState<string | null>(null)
|
const [selectedMapIndex, setSelectedMapIndex] = useState(0)
|
||||||
const [selectedMapLabel, setSelectedMapLabel] = useState<string | null>(null)
|
|
||||||
const [autoCloseGame, setAutoCloseGame] = useState(false)
|
|
||||||
const [isMonitoring, setIsMonitoring] = useState(false)
|
const [isMonitoring, setIsMonitoring] = useState(false)
|
||||||
const [showResultsTable, setShowResultsTable] = useState(false)
|
const [showResultsTable, setShowResultsTable] = useState(false)
|
||||||
const [hardwareInfo, setHardwareInfo] = useState<AllSystemInfo | null>(null)
|
const [hardwareInfo, setHardwareInfo] = useState<AllSystemInfo | null>(null)
|
||||||
|
const [isGameRunning, setIsGameRunning] = useState(false)
|
||||||
|
const [testNote, setTestNote] = useState<string>("") // 测试备注
|
||||||
|
const [editingNoteId, setEditingNoteId] = useState<string | null>(null) // 正在编辑的备注ID
|
||||||
|
const [editingNoteValue, setEditingNoteValue] = useState<string>("") // 正在编辑的备注内容
|
||||||
|
const { isOpen: isNoteModalOpen, onOpen: onNoteModalOpen, onClose: onNoteModalClose } = useDisclosure()
|
||||||
const monitoringIntervalRef = useRef<NodeJS.Timeout | null>(null)
|
const monitoringIntervalRef = useRef<NodeJS.Timeout | null>(null)
|
||||||
|
const timeoutRef = useRef<NodeJS.Timeout | null>(null)
|
||||||
// 记录测试开始的时间戳(用于过滤旧数据)
|
// 记录测试开始的时间戳(用于过滤旧数据)
|
||||||
const testStartTimestampRef = useRef<string | null>(null)
|
const testStartTimestampRef = useRef<string | null>(null)
|
||||||
|
const testStartTimeRef = useRef<number | null>(null)
|
||||||
|
// 记录测试开始时的视频设置
|
||||||
|
const testStartVideoSettingRef = useRef<typeof tool.state.videoSetting | null>(null)
|
||||||
|
|
||||||
|
// 检测游戏是否运行
|
||||||
|
const checkGameRunning = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
const result = await invoke<boolean>("check_process_running", {
|
||||||
|
processName: "cs2.exe",
|
||||||
|
}).catch(() => false)
|
||||||
|
setIsGameRunning(result)
|
||||||
|
return result
|
||||||
|
} catch {
|
||||||
|
setIsGameRunning(false)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
// 定期检测游戏运行状态
|
||||||
|
useEffect(() => {
|
||||||
|
void checkGameRunning()
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
void checkGameRunning()
|
||||||
|
}, 3000)
|
||||||
|
return () => clearInterval(interval)
|
||||||
|
}, [checkGameRunning])
|
||||||
|
|
||||||
// 获取硬件信息
|
// 获取硬件信息
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -182,6 +236,34 @@ export function FpsTest() {
|
|||||||
void fetchHardwareInfo()
|
void fetchHardwareInfo()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
// 停止测试
|
||||||
|
const stopTest = useCallback(async () => {
|
||||||
|
setIsMonitoring(false)
|
||||||
|
if (monitoringIntervalRef.current) {
|
||||||
|
clearInterval(monitoringIntervalRef.current)
|
||||||
|
monitoringIntervalRef.current = null
|
||||||
|
}
|
||||||
|
if (timeoutRef.current) {
|
||||||
|
clearTimeout(timeoutRef.current)
|
||||||
|
timeoutRef.current = null
|
||||||
|
}
|
||||||
|
testStartTimestampRef.current = null
|
||||||
|
testStartTimeRef.current = null
|
||||||
|
testStartVideoSettingRef.current = null
|
||||||
|
|
||||||
|
// 如果启用了自动关闭游戏,则关闭游戏
|
||||||
|
if (tool.state.autoCloseGame) {
|
||||||
|
try {
|
||||||
|
await invoke("kill_game")
|
||||||
|
addToast({ title: "已停止测试并关闭游戏" })
|
||||||
|
} catch (error) {
|
||||||
|
console.error("关闭游戏失败:", error)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addToast({ title: "已停止测试" })
|
||||||
|
}
|
||||||
|
}, [tool.state.autoCloseGame])
|
||||||
|
|
||||||
// 读取结果函数
|
// 读取结果函数
|
||||||
const readResult = useCallback(
|
const readResult = useCallback(
|
||||||
async (silent = false): Promise<boolean> => {
|
async (silent = false): Promise<boolean> => {
|
||||||
@@ -220,49 +302,80 @@ export function FpsTest() {
|
|||||||
setTestTimestamp(parsed.timestamp)
|
setTestTimestamp(parsed.timestamp)
|
||||||
// 成功读取后,清除测试开始时间戳(测试已完成)
|
// 成功读取后,清除测试开始时间戳(测试已完成)
|
||||||
testStartTimestampRef.current = null
|
testStartTimestampRef.current = null
|
||||||
|
testStartTimeRef.current = null
|
||||||
|
|
||||||
|
// 停止监控和超时
|
||||||
|
setIsMonitoring(false)
|
||||||
|
if (monitoringIntervalRef.current) {
|
||||||
|
clearInterval(monitoringIntervalRef.current)
|
||||||
|
monitoringIntervalRef.current = null
|
||||||
|
}
|
||||||
|
if (timeoutRef.current) {
|
||||||
|
clearTimeout(timeoutRef.current)
|
||||||
|
timeoutRef.current = null
|
||||||
|
}
|
||||||
|
|
||||||
// 提取 avg 和 p1 值
|
// 提取 avg 和 p1 值
|
||||||
const { avg, p1 } = extractFpsMetrics(parsed.data)
|
const { avg, p1 } = extractFpsMetrics(parsed.data)
|
||||||
|
|
||||||
// 保存测试结果(即使没有 selectedMap 也保存,使用 "未知地图" 作为默认值)
|
// 保存测试结果
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
const testDate = now.toISOString()
|
const testDate = now.toISOString()
|
||||||
const mapConfig = selectedMap ? BENCHMARK_MAPS.find(m => m.name === selectedMap) : null
|
const mapConfig = BENCHMARK_MAPS[selectedMapIndex]
|
||||||
|
|
||||||
// 从 store 直接获取最新的 videoSetting,避免依赖项问题
|
// 使用测试开始时的视频设置(如果有的话),否则使用当前的
|
||||||
const currentVideoSetting = tool.store.state.videoSetting
|
const currentVideoSetting = testStartVideoSettingRef.current || tool.store.state.videoSetting
|
||||||
|
|
||||||
fpsTest.addResult({
|
fpsTest.addResult({
|
||||||
id: `${now.getTime()}-${Math.random().toString(36).slice(2, 11)}`,
|
id: `${now.getTime()}-${Math.random().toString(36).slice(2, 11)}`,
|
||||||
testTime: parsed.timestamp,
|
testTime: parsed.timestamp,
|
||||||
testDate,
|
testDate,
|
||||||
mapName: selectedMap || "unknown",
|
mapName: mapConfig?.name || "unknown",
|
||||||
mapLabel: mapConfig?.label || selectedMapLabel || "未知地图",
|
mapLabel: mapConfig?.label || "未知地图",
|
||||||
avg,
|
avg,
|
||||||
p1,
|
p1,
|
||||||
rawResult: parsed.data,
|
rawResult: parsed.data,
|
||||||
videoSetting: currentVideoSetting,
|
videoSetting: currentVideoSetting,
|
||||||
hardwareInfo: hardwareInfo ? {
|
hardwareInfo: hardwareInfo
|
||||||
|
? {
|
||||||
cpu: hardwareInfo.cpus[0]?.brand || null,
|
cpu: hardwareInfo.cpus[0]?.brand || null,
|
||||||
cpuCount: hardwareInfo.cpu_count || null,
|
cpuCount: hardwareInfo.cpu_count || null,
|
||||||
os: hardwareInfo.name && hardwareInfo.os_version
|
os:
|
||||||
|
hardwareInfo.name && hardwareInfo.os_version
|
||||||
? `${hardwareInfo.name} ${hardwareInfo.os_version}`
|
? `${hardwareInfo.name} ${hardwareInfo.os_version}`
|
||||||
: null,
|
: null,
|
||||||
memory: hardwareInfo.total_memory
|
memory: hardwareInfo.total_memory
|
||||||
? Math.round(hardwareInfo.total_memory / 1024 / 1024 / 1024)
|
? Math.round(hardwareInfo.total_memory / 1024 / 1024 / 1024)
|
||||||
: null,
|
: null,
|
||||||
} : null,
|
gpu: null,
|
||||||
|
monitor: null,
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
note: testNote, // 保存备注
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 清除保存的启动时视频设置
|
||||||
|
testStartVideoSettingRef.current = null
|
||||||
|
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
if (avg !== null || p1 !== null) {
|
if (avg !== null || p1 !== null) {
|
||||||
addToast({
|
addToast({
|
||||||
title: `已读取并保存测试结果${avg !== null ? ` (avg: ${avg.toFixed(1)} FPS)` : ""}${p1 !== null ? ` (p1: ${p1.toFixed(1)} FPS)` : ""}`
|
title: `已读取并保存测试结果${
|
||||||
|
avg !== null ? ` (avg: ${avg.toFixed(1)} FPS)` : ""
|
||||||
|
}${p1 !== null ? ` (p1: ${p1.toFixed(1)} FPS)` : ""}`,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
addToast({ title: "已读取并保存测试结果(未能提取帧数数据)" })
|
addToast({ title: "已读取并保存测试结果(未能提取帧数数据)" })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果启用了自动关闭游戏,则关闭游戏
|
||||||
|
if (tool.state.autoCloseGame) {
|
||||||
|
setTimeout(() => {
|
||||||
|
void invoke("kill_game").catch(() => {})
|
||||||
|
}, 2000) // 延迟2秒关闭,让用户看到结果
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
} else if (!silent) {
|
} else if (!silent) {
|
||||||
addToast({
|
addToast({
|
||||||
@@ -288,28 +401,9 @@ export function FpsTest() {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[steam.state.cs2Dir, selectedMap, selectedMapLabel, fpsTest, tool.store, hardwareInfo]
|
[steam.state.cs2Dir, selectedMapIndex, fpsTest, tool.store, hardwareInfo, tool.state.autoCloseGame]
|
||||||
)
|
)
|
||||||
|
|
||||||
// 关闭游戏
|
|
||||||
const closeGame = useCallback(async () => {
|
|
||||||
try {
|
|
||||||
await invoke("kill_game")
|
|
||||||
addToast({ title: "已关闭CS2" })
|
|
||||||
setIsMonitoring(false)
|
|
||||||
if (monitoringIntervalRef.current) {
|
|
||||||
clearInterval(monitoringIntervalRef.current)
|
|
||||||
monitoringIntervalRef.current = null
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("关闭游戏失败:", error)
|
|
||||||
addToast({
|
|
||||||
title: `关闭游戏失败: ${error instanceof Error ? error.message : String(error)}`,
|
|
||||||
variant: "flat",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
// 开始监控文件更新
|
// 开始监控文件更新
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isMonitoring && steam.state.cs2Dir) {
|
if (isMonitoring && steam.state.cs2Dir) {
|
||||||
@@ -317,27 +411,46 @@ export function FpsTest() {
|
|||||||
monitoringIntervalRef.current = setInterval(async () => {
|
monitoringIntervalRef.current = setInterval(async () => {
|
||||||
const success = await readResult(true) // 静默读取
|
const success = await readResult(true) // 静默读取
|
||||||
if (success) {
|
if (success) {
|
||||||
// 读取成功,停止监控
|
// 读取成功,监控会在readResult中停止
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}, 2000) // 每2秒检查一次
|
||||||
|
|
||||||
|
// 设置超时
|
||||||
|
if (testStartTimeRef.current) {
|
||||||
|
timeoutRef.current = setTimeout(() => {
|
||||||
|
// 超时,认为测试失败
|
||||||
setIsMonitoring(false)
|
setIsMonitoring(false)
|
||||||
if (monitoringIntervalRef.current) {
|
if (monitoringIntervalRef.current) {
|
||||||
clearInterval(monitoringIntervalRef.current)
|
clearInterval(monitoringIntervalRef.current)
|
||||||
monitoringIntervalRef.current = null
|
monitoringIntervalRef.current = null
|
||||||
}
|
}
|
||||||
|
testStartTimestampRef.current = null
|
||||||
|
testStartTimeRef.current = null
|
||||||
|
testStartVideoSettingRef.current = null
|
||||||
|
addToast({
|
||||||
|
title: "测试超时(200秒),测试失败",
|
||||||
|
variant: "flat",
|
||||||
|
color: "warning",
|
||||||
|
})
|
||||||
// 如果启用了自动关闭游戏,则关闭游戏
|
// 如果启用了自动关闭游戏,则关闭游戏
|
||||||
if (autoCloseGame) {
|
if (tool.state.autoCloseGame) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
void closeGame()
|
void invoke("kill_game").catch(() => {})
|
||||||
}, 2000) // 延迟2秒关闭,让用户看到结果
|
}, 1000)
|
||||||
}
|
}
|
||||||
|
}, TEST_TIMEOUT)
|
||||||
}
|
}
|
||||||
}, 2000) // 每2秒检查一次
|
|
||||||
} else {
|
} else {
|
||||||
// 停止监控
|
// 停止监控
|
||||||
if (monitoringIntervalRef.current) {
|
if (monitoringIntervalRef.current) {
|
||||||
clearInterval(monitoringIntervalRef.current)
|
clearInterval(monitoringIntervalRef.current)
|
||||||
monitoringIntervalRef.current = null
|
monitoringIntervalRef.current = null
|
||||||
}
|
}
|
||||||
|
if (timeoutRef.current) {
|
||||||
|
clearTimeout(timeoutRef.current)
|
||||||
|
timeoutRef.current = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清理函数
|
// 清理函数
|
||||||
@@ -346,20 +459,48 @@ export function FpsTest() {
|
|||||||
clearInterval(monitoringIntervalRef.current)
|
clearInterval(monitoringIntervalRef.current)
|
||||||
monitoringIntervalRef.current = null
|
monitoringIntervalRef.current = null
|
||||||
}
|
}
|
||||||
|
if (timeoutRef.current) {
|
||||||
|
clearTimeout(timeoutRef.current)
|
||||||
|
timeoutRef.current = null
|
||||||
}
|
}
|
||||||
}, [isMonitoring, steam.state.cs2Dir, autoCloseGame, readResult, closeGame])
|
}
|
||||||
|
}, [isMonitoring, steam.state.cs2Dir, readResult, tool.state.autoCloseGame])
|
||||||
|
|
||||||
const startTest = async (mapConfig: (typeof BENCHMARK_MAPS)[0]) => {
|
const startTest = async () => {
|
||||||
if (!steam.state.steamDir || !steam.state.cs2Dir) {
|
if (!steam.state.steamDir || !steam.state.cs2Dir) {
|
||||||
addToast({ title: "请先配置 Steam 和 CS2 路径", variant: "flat", color: "warning" })
|
addToast({ title: "请先配置 Steam 和 CS2 路径", variant: "flat", color: "warning" })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
setTesting(true)
|
const mapConfig = BENCHMARK_MAPS[selectedMapIndex]
|
||||||
setSelectedMap(mapConfig.name)
|
if (!mapConfig) {
|
||||||
setSelectedMapLabel(mapConfig.label)
|
addToast({ title: "请选择测试地图", variant: "flat", color: "warning" })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果启用了自动关闭游戏,检测并关闭正在运行的游戏
|
||||||
|
if (tool.state.autoCloseGame) {
|
||||||
|
const gameRunning = await checkGameRunning()
|
||||||
|
if (gameRunning) {
|
||||||
|
try {
|
||||||
|
await invoke("kill_game")
|
||||||
|
addToast({ title: "检测到游戏正在运行,已关闭" })
|
||||||
|
// 等待一下确保游戏关闭
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 2000))
|
||||||
|
} catch (error) {
|
||||||
|
console.error("关闭游戏失败:", error)
|
||||||
|
addToast({
|
||||||
|
title: `关闭游戏失败: ${error instanceof Error ? error.message : String(error)}`,
|
||||||
|
variant: "flat",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setTestResult(null)
|
setTestResult(null)
|
||||||
setTestTimestamp(null)
|
setTestTimestamp(null)
|
||||||
|
// 注意:不清空备注,让用户可以在测试过程中记住备注内容
|
||||||
|
|
||||||
// 记录测试开始时间戳(格式:MM/DD HH:mm:ss)
|
// 记录测试开始时间戳(格式:MM/DD HH:mm:ss)
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
@@ -369,6 +510,9 @@ export function FpsTest() {
|
|||||||
const minute = String(now.getMinutes()).padStart(2, "0")
|
const minute = String(now.getMinutes()).padStart(2, "0")
|
||||||
const second = String(now.getSeconds()).padStart(2, "0")
|
const second = String(now.getSeconds()).padStart(2, "0")
|
||||||
testStartTimestampRef.current = `${month}/${day} ${hour}:${minute}:${second}`
|
testStartTimestampRef.current = `${month}/${day} ${hour}:${minute}:${second}`
|
||||||
|
testStartTimeRef.current = now.getTime()
|
||||||
|
// 保存启动时的视频设置
|
||||||
|
testStartVideoSettingRef.current = { ...tool.store.state.videoSetting }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const launchOption = `-allow_third_party_software -condebug -conclearlog +map_workshop ${mapConfig.workshopId} ${mapConfig.map}`
|
const launchOption = `-allow_third_party_software -condebug -conclearlog +map_workshop ${mapConfig.workshopId} ${mapConfig.map}`
|
||||||
@@ -381,7 +525,6 @@ export function FpsTest() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
addToast({ title: `已启动 ${mapConfig.label} 测试,正在自动监听结果...` })
|
addToast({ title: `已启动 ${mapConfig.label} 测试,正在自动监听结果...` })
|
||||||
setTesting(false)
|
|
||||||
|
|
||||||
// 开始自动监听文件更新
|
// 开始自动监听文件更新
|
||||||
setIsMonitoring(true)
|
setIsMonitoring(true)
|
||||||
@@ -391,10 +534,43 @@ export function FpsTest() {
|
|||||||
title: `启动测试失败: ${error instanceof Error ? error.message : String(error)}`,
|
title: `启动测试失败: ${error instanceof Error ? error.message : String(error)}`,
|
||||||
variant: "flat",
|
variant: "flat",
|
||||||
})
|
})
|
||||||
setTesting(false)
|
|
||||||
setIsMonitoring(false)
|
setIsMonitoring(false)
|
||||||
// 启动失败,清除测试开始时间戳
|
// 启动失败,清除测试开始时间戳和视频设置
|
||||||
testStartTimestampRef.current = null
|
testStartTimestampRef.current = null
|
||||||
|
testStartTimeRef.current = null
|
||||||
|
testStartVideoSettingRef.current = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否可以开始测试
|
||||||
|
const canStartTest = !tool.state.autoCloseGame ? !isGameRunning : true
|
||||||
|
|
||||||
|
// 格式化视频设置摘要
|
||||||
|
const formatVideoSettingSummary = (videoSetting: typeof tool.state.videoSetting | null): string => {
|
||||||
|
if (!videoSetting) return "N/A"
|
||||||
|
const resolution = `${videoSetting.defaultres}x${videoSetting.defaultresheight}`
|
||||||
|
const refreshRate = videoSetting.refreshrate_denominator === "1"
|
||||||
|
? videoSetting.refreshrate_numerator
|
||||||
|
: `${videoSetting.refreshrate_numerator}/${videoSetting.refreshrate_denominator}`
|
||||||
|
const msaa = videoSetting.msaa_samples === "0" ? "无" : `${videoSetting.msaa_samples}x`
|
||||||
|
return `${resolution}@${refreshRate}Hz, MSAA:${msaa}`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开备注编辑对话框
|
||||||
|
const handleEditNote = (resultId: string, currentNote: string) => {
|
||||||
|
setEditingNoteId(resultId)
|
||||||
|
setEditingNoteValue(currentNote)
|
||||||
|
onNoteModalOpen()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存备注
|
||||||
|
const handleSaveNote = () => {
|
||||||
|
if (editingNoteId) {
|
||||||
|
fpsTest.updateNote(editingNoteId, editingNoteValue)
|
||||||
|
addToast({ title: "备注已更新" })
|
||||||
|
onNoteModalClose()
|
||||||
|
setEditingNoteId(null)
|
||||||
|
setEditingNoteValue("")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -403,47 +579,129 @@ export function FpsTest() {
|
|||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardIcon>
|
<CardIcon>
|
||||||
<TestTube size={16} /> 帧数测试
|
<TestTube size={16} /> 帧数测试
|
||||||
|
{isGameRunning && !tool.state.autoCloseGame && (
|
||||||
|
<Chip size="sm" color="warning" variant="flat" className="ml-2 cursor-help">
|
||||||
|
游戏运行中 关闭游戏后从这里启动
|
||||||
|
</Chip>
|
||||||
|
)}
|
||||||
</CardIcon>
|
</CardIcon>
|
||||||
<CardTool>
|
<CardTool className="justify-end">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant={showResultsTable ? "solid" : "flat"}
|
variant={showResultsTable ? "solid" : "flat"}
|
||||||
|
color={showResultsTable ? "secondary" : "default"}
|
||||||
onPress={() => setShowResultsTable(!showResultsTable)}
|
onPress={() => setShowResultsTable(!showResultsTable)}
|
||||||
className="px-3"
|
className="font-medium"
|
||||||
>
|
>
|
||||||
<List size={14} className="mr-1" />
|
<List size={14} />
|
||||||
测试结果
|
测试结果
|
||||||
</Button>
|
</Button>
|
||||||
|
{isMonitoring ? (
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
color="danger"
|
||||||
|
onPress={() => {
|
||||||
|
void stopTest()
|
||||||
|
}}
|
||||||
|
className="font-medium"
|
||||||
|
>
|
||||||
|
<Square size={14} />
|
||||||
|
停止测试
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
color="primary"
|
||||||
|
isDisabled={!canStartTest}
|
||||||
|
onPress={() => {
|
||||||
|
void startTest()
|
||||||
|
}}
|
||||||
|
className="font-medium"
|
||||||
|
>
|
||||||
|
<Play size={14} />
|
||||||
|
开始测试
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</CardTool>
|
</CardTool>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardBody>
|
<CardBody>
|
||||||
{showResultsTable ? (
|
{showResultsTable ? (
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="max-h-[500px] overflow-auto">
|
<div className="max-h-[500px] overflow-auto">
|
||||||
<Table aria-label="测试结果表格" selectionMode="none">
|
<Table
|
||||||
|
aria-label="测试结果表格"
|
||||||
|
selectionMode="none"
|
||||||
|
removeWrapper
|
||||||
|
classNames={{
|
||||||
|
base: "min-h-[222px]",
|
||||||
|
th: "px-2 py-1.5 text-xs",
|
||||||
|
td: "px-2 py-1.5 text-xs",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableColumn>测试时间</TableColumn>
|
<TableColumn>测试时间</TableColumn>
|
||||||
<TableColumn>测试地图</TableColumn>
|
<TableColumn>测试地图</TableColumn>
|
||||||
<TableColumn>平均帧数</TableColumn>
|
<TableColumn>AVG平均帧</TableColumn>
|
||||||
<TableColumn>P1帧数</TableColumn>
|
<TableColumn>P1低帧</TableColumn>
|
||||||
<TableColumn>CPU</TableColumn>
|
<TableColumn>系统版本</TableColumn>
|
||||||
<TableColumn width={80}>操作</TableColumn>
|
<TableColumn>GPU</TableColumn>
|
||||||
|
<TableColumn>内存</TableColumn>
|
||||||
|
<TableColumn>视频设置</TableColumn>
|
||||||
|
<TableColumn>备注</TableColumn>
|
||||||
|
<TableColumn width={100}>操作</TableColumn>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody emptyContent="暂无测试记录">
|
<TableBody emptyContent="暂无测试记录">
|
||||||
{fpsTest.state.results.map((result) => (
|
{fpsTest.state.results.map((result) => (
|
||||||
<TableRow key={result.id}>
|
<TableRow key={result.id}>
|
||||||
<TableCell>{result.testTime}</TableCell>
|
<TableCell className="text-xs">{result.testTime}</TableCell>
|
||||||
<TableCell>{result.mapLabel}</TableCell>
|
<TableCell className="text-xs">{result.mapLabel}</TableCell>
|
||||||
<TableCell>
|
<TableCell className="text-xs">
|
||||||
{result.avg !== null ? `${result.avg.toFixed(1)}` : "N/A"}
|
{result.avg !== null ? `${result.avg.toFixed(1)}` : "N/A"}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell className="text-xs">
|
||||||
{result.p1 !== null ? `${result.p1.toFixed(1)}` : "N/A"}
|
{result.p1 !== null ? `${result.p1.toFixed(1)}` : "N/A"}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell className="text-xs" title={result.hardwareInfo?.os || undefined}>
|
||||||
{result.hardwareInfo?.cpu || "N/A"}
|
{result.hardwareInfo?.os || "N/A"}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="text-xs" title={result.hardwareInfo?.gpu || undefined}>
|
||||||
|
{result.hardwareInfo?.gpu || "N/A"}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="text-xs">
|
||||||
|
{result.hardwareInfo?.memory ? `${result.hardwareInfo.memory}GB` : "N/A"}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell
|
||||||
|
className="text-xs"
|
||||||
|
title={formatVideoSettingSummary(result.videoSetting)}
|
||||||
|
>
|
||||||
|
<Tooltip content={formatVideoSettingSummary(result.videoSetting)}>
|
||||||
|
<span className="cursor-help">
|
||||||
|
{result.videoSetting
|
||||||
|
? `${result.videoSetting.defaultres}x${result.videoSetting.defaultresheight}`
|
||||||
|
: "N/A"}
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="text-xs max-w-[150px]">
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<span className="truncate" title={result.note || "无备注"}>
|
||||||
|
{result.note || "无备注"}
|
||||||
|
</span>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
isIconOnly
|
||||||
|
variant="light"
|
||||||
|
onPress={() => handleEditNote(result.id, result.note || "")}
|
||||||
|
className="h-5 min-w-5 shrink-0"
|
||||||
|
>
|
||||||
|
<Edit size={12} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
isIconOnly
|
isIconOnly
|
||||||
@@ -452,13 +710,14 @@ export function FpsTest() {
|
|||||||
fpsTest.removeResult(result.id)
|
fpsTest.removeResult(result.id)
|
||||||
addToast({
|
addToast({
|
||||||
title: "已删除测试记录",
|
title: "已删除测试记录",
|
||||||
variant: "flat"
|
variant: "flat",
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
className="min-w-8"
|
className="h-6 min-w-6"
|
||||||
>
|
>
|
||||||
<Delete size={16} />
|
<Delete size={14} />
|
||||||
</Button>
|
</Button>
|
||||||
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))}
|
))}
|
||||||
@@ -467,63 +726,127 @@ export function FpsTest() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-5">
|
||||||
<div className="flex flex-wrap items-center gap-2">
|
{/* 表单区域:地图和备注同一行 */}
|
||||||
{BENCHMARK_MAPS.map((mapConfig) => (
|
<div className="flex items-start gap-4">
|
||||||
<Button
|
{/* 测试地图 */}
|
||||||
key={mapConfig.name}
|
<div className="flex flex-col gap-1.5">
|
||||||
size="sm"
|
<label className="text-xs text-default-500">测试地图</label>
|
||||||
isDisabled={testing || isMonitoring}
|
<Tabs
|
||||||
onPress={() => {
|
selectedKey={String(selectedMapIndex)}
|
||||||
void startTest(mapConfig)
|
onSelectionChange={(key) => {
|
||||||
|
if (!isMonitoring) {
|
||||||
|
setSelectedMapIndex(Number(key))
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
className="font-medium transition bg-blue-200 rounded-full select-none dark:bg-blue-900/60"
|
aria-label="测试地图选择"
|
||||||
|
size="md"
|
||||||
|
radius="lg"
|
||||||
>
|
>
|
||||||
{testing && selectedMap === mapConfig.name ? (
|
{BENCHMARK_MAPS.map((map, index) => (
|
||||||
<Spinner size="sm" className="mr-2" />
|
<Tab key={String(index)} title={map.label} />
|
||||||
) : null}
|
|
||||||
{mapConfig.label}
|
|
||||||
</Button>
|
|
||||||
))}
|
))}
|
||||||
|
</Tabs>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 备注 */}
|
||||||
|
<div className="flex flex-col gap-1.5 grow">
|
||||||
|
<label className="text-xs text-default-500">备注</label>
|
||||||
|
<Input
|
||||||
|
size="md"
|
||||||
|
placeholder="输入测试备注"
|
||||||
|
value={testNote}
|
||||||
|
onValueChange={setTestNote}
|
||||||
|
isDisabled={isMonitoring}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 工具栏:按钮靠右对齐 */}
|
||||||
|
<div className="flex items-center justify-start gap-2">
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
isDisabled={isMonitoring}
|
variant={tool.state.autoCloseGame ? "solid" : "flat"}
|
||||||
|
color={tool.state.autoCloseGame ? "primary" : "default"}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
void readResult()
|
tool.setAutoCloseGame(!tool.state.autoCloseGame)
|
||||||
}}
|
}}
|
||||||
className="font-medium transition bg-green-200 rounded-full select-none dark:bg-green-900/60"
|
className="font-medium"
|
||||||
>
|
>
|
||||||
手动读取结果
|
{tool.state.autoCloseGame ? <Check size={14} /> : <Close size={14} />}
|
||||||
|
自动关闭游戏
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
|
variant="flat"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
void closeGame()
|
void invoke("kill_game")
|
||||||
|
.then(() => {
|
||||||
|
addToast({ title: "已关闭CS2" })
|
||||||
|
void checkGameRunning()
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error("关闭游戏失败:", error)
|
||||||
|
addToast({
|
||||||
|
title: `关闭游戏失败: ${
|
||||||
|
error instanceof Error ? error.message : String(error)
|
||||||
|
}`,
|
||||||
|
variant: "flat",
|
||||||
|
})
|
||||||
|
})
|
||||||
}}
|
}}
|
||||||
className="font-medium transition bg-orange-200 rounded-full select-none dark:bg-orange-900/60"
|
className="font-medium"
|
||||||
>
|
>
|
||||||
<Power size={14} />
|
<Power size={14} />
|
||||||
关闭游戏
|
关闭游戏
|
||||||
</Button>
|
</Button>
|
||||||
<Switch size="sm" isSelected={autoCloseGame} onValueChange={setAutoCloseGame} className="ml-4">
|
|
||||||
测试完成自动关闭游戏
|
|
||||||
</Switch>
|
|
||||||
{isMonitoring && (
|
{isMonitoring && (
|
||||||
<Chip size="sm" color="primary" variant="flat">
|
<Chip size="sm" color="primary" variant="flat">
|
||||||
正在监听中...
|
正在监听中...
|
||||||
</Chip>
|
</Chip>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="flat"
|
||||||
|
isDisabled={isMonitoring}
|
||||||
|
onPress={() => {
|
||||||
|
void readResult()
|
||||||
|
}}
|
||||||
|
className="font-medium"
|
||||||
|
>
|
||||||
|
手动读取结果
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{/* 测试结果显示:测试时间、平均帧、P1低帧 */}
|
||||||
|
{testResult && testTimestamp && (() => {
|
||||||
|
const { avg, p1 } = extractFpsMetrics(testResult)
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="px-3 py-1.5 h-12 rounded-md bg-default-100 dark:bg-default-50 text-xs flex flex-col justify-center">
|
||||||
|
<div className="text-default-500">测试时间</div>
|
||||||
|
<div className="font-medium">{testTimestamp}</div>
|
||||||
|
</div>
|
||||||
|
<div className="px-3 py-1.5 h-12 rounded-md bg-default-100 dark:bg-default-50 text-xs flex items-center">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div>
|
||||||
|
<div className="text-default-500">平均帧</div>
|
||||||
|
<div className="font-medium">{avg !== null ? `${avg.toFixed(1)}` : "N/A"}</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="text-default-500">P1低帧</div>
|
||||||
|
<div className="font-medium">{p1 !== null ? `${p1.toFixed(1)}` : "N/A"}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
})()}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{testResult && (
|
{testResult && (
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
<div className="flex items-center gap-2 mb-2">
|
|
||||||
{testTimestamp && (
|
|
||||||
<Chip size="sm" variant="flat" color="default">
|
|
||||||
测试时间: {testTimestamp}
|
|
||||||
</Chip>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<pre className="p-3 overflow-auto font-mono text-xs rounded-md bg-black/5 dark:bg-white/5 hide-scrollbar">
|
<pre className="p-3 overflow-auto font-mono text-xs rounded-md bg-black/5 dark:bg-white/5 hide-scrollbar">
|
||||||
{testResult}
|
{testResult}
|
||||||
</pre>
|
</pre>
|
||||||
@@ -532,6 +855,34 @@ export function FpsTest() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</CardBody>
|
</CardBody>
|
||||||
|
|
||||||
|
{/* 备注编辑对话框 */}
|
||||||
|
<Modal isOpen={isNoteModalOpen} onClose={onNoteModalClose} size="md">
|
||||||
|
<ModalContent>
|
||||||
|
{(onClose) => (
|
||||||
|
<>
|
||||||
|
<ModalHeader className="flex flex-col gap-1">编辑备注</ModalHeader>
|
||||||
|
<ModalBody>
|
||||||
|
<Textarea
|
||||||
|
placeholder="输入备注内容"
|
||||||
|
value={editingNoteValue}
|
||||||
|
onValueChange={setEditingNoteValue}
|
||||||
|
minRows={3}
|
||||||
|
maxRows={5}
|
||||||
|
/>
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter>
|
||||||
|
<Button color="danger" variant="light" onPress={onClose}>
|
||||||
|
取消
|
||||||
|
</Button>
|
||||||
|
<Button color="primary" onPress={handleSaveNote}>
|
||||||
|
保存
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { Refresh, User } from "@icon-park/react"
|
import { Refresh, User, FolderFocusOne, Login, Check } from "@icon-park/react"
|
||||||
import { Card, CardBody, CardHeader, CardIcon, CardTool } from "../window/Card"
|
import { Card, CardBody, CardHeader, CardIcon, CardTool } from "../window/Card"
|
||||||
import { addToast, Button, Chip } from "@heroui/react"
|
import { addToast, Button, Chip } from "@heroui/react"
|
||||||
import { useSteamStore } from "@/store/steam"
|
import { useSteamStore } from "@/store/steam"
|
||||||
import { ToolButton } from "../window/ToolButton"
|
import { ToolButton } from "../window/ToolButton"
|
||||||
import { useAutoAnimate } from "@formkit/auto-animate/react"
|
import { useAutoAnimate } from "@formkit/auto-animate/react"
|
||||||
|
import { invoke } from "@tauri-apps/api/core"
|
||||||
|
import path from "path"
|
||||||
|
|
||||||
const SteamUsers = ({ className }: { className?: string }) => {
|
const SteamUsers = ({ className }: { className?: string }) => {
|
||||||
const steam = useSteamStore()
|
const steam = useSteamStore()
|
||||||
@@ -68,10 +70,26 @@ const SteamUsers = ({ className }: { className?: string }) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-end gap-2 p-2">
|
<div className="flex items-end gap-2 p-2">
|
||||||
<Button size="sm" onPress={() => steam.switchLoginUser(id)}>
|
<Button
|
||||||
|
size="sm"
|
||||||
|
onPress={async () => {
|
||||||
|
if (!steam.state.steamDirValid) {
|
||||||
|
addToast({ title: "Steam路径不可用", color: "warning" })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await invoke("open_path", {
|
||||||
|
path: path.resolve(steam.state.steamDir, "userdata", user.steam_id32.toString(), "730", "local", "cfg"),
|
||||||
|
})
|
||||||
|
addToast({ title: "个人CFG" })
|
||||||
|
}} className="gap-1"
|
||||||
|
>
|
||||||
|
个人CFG
|
||||||
|
</Button>
|
||||||
|
<Button size="sm" onPress={() => steam.switchLoginUser(id)} className="gap-1">
|
||||||
切换登录
|
切换登录
|
||||||
</Button>
|
</Button>
|
||||||
<Button size="sm" onPress={() => steam.selectUser(id)}>
|
<Button size="sm" onPress={() => steam.selectUser(id)} className="gap-1">
|
||||||
|
<Check size={14} />
|
||||||
选择
|
选择
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import { CloseSmall, Down, Edit, Plus, SettingConfig, Up } from "@icon-park/react"
|
import { CloseSmall, Down, Edit, Plus, SettingConfig, Up } from "@icon-park/react"
|
||||||
import { useEffect, useState, useCallback } from "react"
|
import { useEffect, useState, useCallback, useRef } from "react"
|
||||||
import { Card, CardBody, CardHeader, CardIcon, CardTool } from "../window/Card"
|
import { Card, CardBody, CardHeader, CardIcon, CardTool } from "../window/Card"
|
||||||
import { ToolButton } from "../window/ToolButton"
|
import { ToolButton } from "../window/ToolButton"
|
||||||
import { addToast, NumberInput, Tab, Tabs, Tooltip, Chip } from "@heroui/react"
|
import { addToast, NumberInput, Tab, Tabs, Tooltip, Chip } from "@heroui/react"
|
||||||
import { motion } from "framer-motion"
|
import { motion } from "framer-motion"
|
||||||
import { useToolStore, VideoSetting as VideoConfig, VideoSettingTemplate } from "@/store/tool"
|
import { useToolStore, VideoSetting as VideoConfig, VideoSettingTemplate } from "@/store/tool"
|
||||||
import { useSteamStore } from "@/store/steam"
|
import { useSteamStore } from "@/store/steam"
|
||||||
import { useDebounce, useDebounceFn } from "ahooks"
|
import { useDebounce, useDebounceFn, useThrottleFn } from "ahooks"
|
||||||
import { invoke } from "@tauri-apps/api/core"
|
import { invoke } from "@tauri-apps/api/core"
|
||||||
|
import { listen } from "@tauri-apps/api/event"
|
||||||
|
|
||||||
const VideoSetting = () => {
|
const VideoSetting = () => {
|
||||||
const [hide, setHide] = useState(false)
|
const [hide, setHide] = useState(false)
|
||||||
@@ -15,6 +16,11 @@ const VideoSetting = () => {
|
|||||||
const [isGameRunning, setIsGameRunning] = useState(false)
|
const [isGameRunning, setIsGameRunning] = useState(false)
|
||||||
const tool = useToolStore()
|
const tool = useToolStore()
|
||||||
const steam = useSteamStore()
|
const steam = useSteamStore()
|
||||||
|
// 使用 ref 存储 edit 的最新值,供 throttle 回调使用
|
||||||
|
const editRef = useRef(edit)
|
||||||
|
useEffect(() => {
|
||||||
|
editRef.current = edit
|
||||||
|
}, [edit])
|
||||||
|
|
||||||
// 检测游戏是否运行
|
// 检测游戏是否运行
|
||||||
const checkGameRunning = useCallback(async () => {
|
const checkGameRunning = useCallback(async () => {
|
||||||
@@ -42,7 +48,7 @@ const VideoSetting = () => {
|
|||||||
addToast({ title: "请先选择用户", color: "danger" })
|
addToast({ title: "请先选择用户", color: "danger" })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ wait: 500, leading: true, trailing: false }
|
{ wait: 500, leading: false, trailing: true }
|
||||||
)
|
)
|
||||||
const videoSettings = (video: VideoConfig) => {
|
const videoSettings = (video: VideoConfig) => {
|
||||||
return [
|
return [
|
||||||
@@ -286,7 +292,7 @@ const VideoSetting = () => {
|
|||||||
// 定期检测游戏运行状态
|
// 定期检测游戏运行状态
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
void checkGameRunning()
|
void checkGameRunning()
|
||||||
}, 2000)
|
}, 4000)
|
||||||
return () => clearInterval(interval)
|
return () => clearInterval(interval)
|
||||||
}, [checkGameRunning])
|
}, [checkGameRunning])
|
||||||
|
|
||||||
@@ -301,10 +307,55 @@ const VideoSetting = () => {
|
|||||||
trailing: true,
|
trailing: true,
|
||||||
maxWait: 2500,
|
maxWait: 2500,
|
||||||
})
|
})
|
||||||
|
// 节流重新读取配置函数,2秒间隔,trailing模式
|
||||||
|
const { run: throttledRefreshVideoConfig } = useThrottleFn(
|
||||||
|
async () => {
|
||||||
|
if (steam.state.steamDirValid && steam.currentUser()) {
|
||||||
|
await tool.getVideoConfig(
|
||||||
|
steam.state.steamDir,
|
||||||
|
steam.currentUser()?.steam_id32 || 0
|
||||||
|
)
|
||||||
|
// 如果不在编辑状态,更新本地状态(使用 ref 获取最新的 edit 值)
|
||||||
|
if (!editRef.current) {
|
||||||
|
setVconfig(tool.state.videoSetting)
|
||||||
|
}
|
||||||
|
addToast({ title: "检测到视频设置文件变动,已自动刷新", color: "success" })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
wait: 2000,
|
||||||
|
leading: false,
|
||||||
|
trailing: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (steam.state.steamDirValid && steam.currentUser())
|
if (steam.state.steamDirValid && steam.currentUser()) {
|
||||||
void tool.getVideoConfig(steam.state.steamDir, steam.currentUser()?.steam_id32 || 0)
|
void tool.getVideoConfig(steam.state.steamDir, steam.currentUser()?.steam_id32 || 0)
|
||||||
}, [debounceCurrentUserId])
|
// 启动文件监听
|
||||||
|
void invoke("start_watch_cs2_video", {
|
||||||
|
steamDir: steam.state.steamDir,
|
||||||
|
steamId32: steam.currentUser()?.steam_id32 || 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 清理函数:停止后端监听
|
||||||
|
return () => {
|
||||||
|
void invoke("stop_watch_cs2_video").catch(() => {
|
||||||
|
// 忽略错误,可能监听器已经不存在
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [debounceCurrentUserId, steam.state.steamDirValid, steam.state.steamDir, tool])
|
||||||
|
|
||||||
|
// 监听 cs2_video.txt 文件变动事件
|
||||||
|
useEffect(() => {
|
||||||
|
const unlisten = listen("steam://cs2_video_changed", () => {
|
||||||
|
// 文件变化时使用节流函数重新读取配置
|
||||||
|
throttledRefreshVideoConfig()
|
||||||
|
})
|
||||||
|
return () => {
|
||||||
|
void unlisten.then((fn) => fn())
|
||||||
|
}
|
||||||
|
}, [throttledRefreshVideoConfig])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
@@ -346,6 +397,11 @@ const VideoSetting = () => {
|
|||||||
>
|
>
|
||||||
推荐
|
推荐
|
||||||
</ToolButton>
|
</ToolButton>
|
||||||
|
<Tooltip
|
||||||
|
content={isGameRunning ? "游戏运行中,无法修改视频设置" : ""}
|
||||||
|
isDisabled={!isGameRunning}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
<ToolButton
|
<ToolButton
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
// 检查游戏是否运行
|
// 检查游戏是否运行
|
||||||
@@ -375,6 +431,8 @@ const VideoSetting = () => {
|
|||||||
<Plus />
|
<Plus />
|
||||||
应用
|
应用
|
||||||
</ToolButton>
|
</ToolButton>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<ToolButton
|
<ToolButton
|
||||||
|
|||||||
@@ -61,38 +61,38 @@ const Nav = () => {
|
|||||||
{pathname !== "/" && (
|
{pathname !== "/" && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="px-2 py-0 rounded transition duration-150 hover:bg-black/10 dark:hover:bg-zinc-100/10 active:scale-95"
|
className="px-2 py-0 transition duration-150 rounded cursor-pointer hover:bg-black/10 dark:hover:bg-zinc-100/10 active:scale-95"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
app.setInited(false)
|
app.setInited(false)
|
||||||
if (pathname !== "/") router.push("/")
|
if (pathname !== "/") router.push("/")
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<RocketOne size={16} />
|
<RocketOne size={16} className="cursor-pointer" />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip content="深色模式" showArrow={true} delay={300}>
|
<Tooltip content="深色模式" showArrow={true} delay={300}>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="px-2 py-0 rounded transition duration-150 hover:bg-black/10 dark:hover:bg-zinc-100/10 active:scale-95"
|
className="px-2 py-0 transition duration-150 rounded cursor-pointer hover:bg-black/10 dark:hover:bg-zinc-100/10 active:scale-95"
|
||||||
onClick={() => (theme === "light" ? setAppTheme("dark") : setAppTheme("light"))}
|
onClick={() => (theme === "light" ? setAppTheme("dark") : setAppTheme("light"))}
|
||||||
>
|
>
|
||||||
{theme === "light" ? <SunOne size={16} /> : <Moon size={16} />}
|
{theme === "light" ? <SunOne size={16} className="cursor-pointer" /> : <Moon size={16} className="cursor-pointer" />}
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip content="反馈" showArrow={true} delay={300}>
|
<Tooltip content="反馈" showArrow={true} delay={300}>
|
||||||
<Link
|
<Link
|
||||||
href="https://docs.qq.com/form/page/DZU1ieW9SQkxWU1RF"
|
href="https://docs.qq.com/form/page/DZU1ieW9SQkxWU1RF"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
className="px-2 py-0 text-black rounded transition duration-150 dark:text-white hover:bg-black/10 dark:hover:bg-zinc-100/10 active:scale-95"
|
className="px-2 py-0 text-black transition duration-150 rounded cursor-pointer dark:text-white hover:bg-black/10 dark:hover:bg-zinc-100/10 active:scale-95"
|
||||||
>
|
>
|
||||||
<button type="button">
|
<button type="button" className="cursor-pointer">
|
||||||
<Communication size={16} />
|
<Communication size={16} className="cursor-pointer" />
|
||||||
</button>
|
</button>
|
||||||
</Link>
|
</Link>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
<AuthButtonWrapper />
|
{/* <AuthButtonWrapper /> */}
|
||||||
|
|
||||||
<ResetModal />
|
<ResetModal />
|
||||||
|
|
||||||
@@ -100,24 +100,24 @@ const Nav = () => {
|
|||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="px-2 py-0 rounded transition duration-150 hover:bg-black/10 dark:hover:bg-zinc-100/10 active:scale-95"
|
className="px-2 py-0 transition duration-150 rounded cursor-pointer hover:bg-black/10 dark:hover:bg-zinc-100/10 active:scale-95"
|
||||||
onClick={minimize}
|
onClick={minimize}
|
||||||
>
|
>
|
||||||
<Minus size={16} />
|
<Minus size={16} className="cursor-pointer" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="px-2 py-0 rounded transition duration-150 hover:bg-black/10 dark:hover:bg-zinc-100/10 active:scale-95"
|
className="px-2 py-0 transition duration-150 rounded cursor-pointer hover:bg-black/10 dark:hover:bg-zinc-100/10 active:scale-95"
|
||||||
onClick={toggleMaximize}
|
onClick={toggleMaximize}
|
||||||
>
|
>
|
||||||
<Square size={16} />
|
<Square size={16} className="cursor-pointer" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="px-2 py-0 rounded transition duration-150 hover:bg-black/10 dark:hover:bg-zinc-100/10 active:scale-95"
|
className="px-2 py-0 transition duration-150 rounded cursor-pointer hover:bg-black/10 dark:hover:bg-zinc-100/10 active:scale-95"
|
||||||
onClick={close}
|
onClick={close}
|
||||||
>
|
>
|
||||||
<Close size={16} />
|
<Close size={16} className="cursor-pointer" />
|
||||||
</button>
|
</button>
|
||||||
</>
|
</>
|
||||||
{/* )} */}
|
{/* )} */}
|
||||||
@@ -149,10 +149,10 @@ function ResetModal() {
|
|||||||
<Tooltip content="重置设置" showArrow={true} delay={300}>
|
<Tooltip content="重置设置" showArrow={true} delay={300}>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="px-2 py-0 rounded transition duration-150 hover:bg-black/10 dark:hover:bg-zinc-100/10 active:scale-95"
|
className="px-2 py-0 transition duration-150 rounded cursor-pointer hover:bg-black/10 dark:hover:bg-zinc-100/10 active:scale-95"
|
||||||
onClick={onOpen}
|
onClick={onOpen}
|
||||||
>
|
>
|
||||||
<Refresh size={16} />
|
<Refresh size={16} className="cursor-pointer" />
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
|
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ const SideButton = ({
|
|||||||
onClick={() => router.push(route || "/")}
|
onClick={() => router.push(route || "/")}
|
||||||
className={cn(
|
className={cn(
|
||||||
className,
|
className,
|
||||||
"p-2.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg transition relative active:scale-90 cursor-pointer",
|
"p-2.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg transition relative active:scale-90 cursor-pointer [&>*]:cursor-pointer",
|
||||||
path.startsWith(route) && "bg-black/5 dark:bg-white/5"
|
path.startsWith(route) && "bg-black/5 dark:bg-white/5"
|
||||||
)}
|
)}
|
||||||
{...rest}
|
{...rest}
|
||||||
|
|||||||
@@ -6,12 +6,17 @@ interface ToolButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>
|
|||||||
className?: string
|
className?: string
|
||||||
selected?: boolean
|
selected?: boolean
|
||||||
}
|
}
|
||||||
export const ToolButton = ({ children, className, selected, ...rest }: ToolButtonProps) => {
|
export const ToolButton = ({ children, className, selected, disabled, ...rest }: ToolButtonProps) => {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
disabled={disabled}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex shrink-0 gap-0.5 active:scale-95 cursor-pointer items-center min-w-7 justify-center px-2 py-1.5 bg-black/5 transition hover:bg-black/10 dark:bg-white/5 dark:hover:bg-white/10 rounded-md text-sm leading-none",
|
"flex shrink-0 gap-0.5 items-center min-w-7 justify-center px-2 py-1.5 bg-black/5 transition rounded-md text-sm leading-none",
|
||||||
|
disabled
|
||||||
|
? "opacity-50 cursor-not-allowed"
|
||||||
|
: "active:scale-95 cursor-pointer hover:bg-black/10 dark:hover:bg-white/10",
|
||||||
|
"dark:bg-white/5",
|
||||||
className,
|
className,
|
||||||
selected &&
|
selected &&
|
||||||
"bg-purple-500/40 hover:bg-purple-500/20 text-purple-900 dark:text-purple-100 drop-shadow-sm dark:bg-purple-500/40 dark:hover:bg-purple-500/20"
|
"bg-purple-500/40 hover:bg-purple-500/20 text-purple-900 dark:text-purple-100 drop-shadow-sm dark:bg-purple-500/40 dark:hover:bg-purple-500/20"
|
||||||
|
|||||||
@@ -19,7 +19,10 @@ export interface FpsTestResult {
|
|||||||
cpuCount: number | null
|
cpuCount: number | null
|
||||||
os: string | null
|
os: string | null
|
||||||
memory: number | null // GB
|
memory: number | null // GB
|
||||||
|
gpu: string | null
|
||||||
|
monitor: string | null
|
||||||
} | null // 硬件信息
|
} | null // 硬件信息
|
||||||
|
note?: string // 备注(可选,用于向后兼容)
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultValue = {
|
const defaultValue = {
|
||||||
@@ -27,7 +30,7 @@ const defaultValue = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const fpsTestStore = store(
|
export const fpsTestStore = store(
|
||||||
"fpsTest",
|
"fps_test",
|
||||||
{ ...defaultValue },
|
{ ...defaultValue },
|
||||||
DEFAULT_STORE_CONFIG,
|
DEFAULT_STORE_CONFIG,
|
||||||
)
|
)
|
||||||
@@ -42,6 +45,7 @@ export const useFpsTestStore = () => {
|
|||||||
addResult,
|
addResult,
|
||||||
removeResult,
|
removeResult,
|
||||||
clearResults,
|
clearResults,
|
||||||
|
updateNote,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,3 +67,10 @@ const clearResults = () => {
|
|||||||
fpsTestStore.state.results = []
|
fpsTestStore.state.results = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateNote = (id: string, note: string) => {
|
||||||
|
const result = fpsTestStore.state.results.find((r) => r.id === id)
|
||||||
|
if (result) {
|
||||||
|
result.note = note
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -123,6 +123,7 @@ const defaultValue = {
|
|||||||
] as LaunchOption[],
|
] as LaunchOption[],
|
||||||
launchIndex: 0,
|
launchIndex: 0,
|
||||||
powerPlan: 0,
|
powerPlan: 0,
|
||||||
|
autoCloseGame: true, // 帧数测试自动关闭游戏
|
||||||
videoSetting: {
|
videoSetting: {
|
||||||
version: "15",
|
version: "15",
|
||||||
vendor_id: "0",
|
vendor_id: "0",
|
||||||
@@ -184,6 +185,7 @@ export const useToolStore = () => {
|
|||||||
setLaunchIndex,
|
setLaunchIndex,
|
||||||
removeLaunchOption,
|
removeLaunchOption,
|
||||||
setPowerPlan,
|
setPowerPlan,
|
||||||
|
setAutoCloseGame,
|
||||||
setVideoSetting,
|
setVideoSetting,
|
||||||
getVideoConfig,
|
getVideoConfig,
|
||||||
setVideoConfig,
|
setVideoConfig,
|
||||||
@@ -198,10 +200,17 @@ const setLaunchOption = (option: LaunchOption, index: number) => {
|
|||||||
option,
|
option,
|
||||||
...toolStore.state.launchOptions.slice(index + 1),
|
...toolStore.state.launchOptions.slice(index + 1),
|
||||||
]
|
]
|
||||||
|
// 同步更新托盘
|
||||||
|
sendCurrentLaunchOptionToTray(toolStore.state.launchIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
const setLaunchOptions = (options: LaunchOption[]) => {
|
const setLaunchOptions = (options: LaunchOption[]) => {
|
||||||
toolStore.state.launchOptions = options
|
toolStore.state.launchOptions = options
|
||||||
|
// 确保索引在有效范围内
|
||||||
|
if (toolStore.state.launchIndex >= options.length) {
|
||||||
|
toolStore.state.launchIndex = options.length > 0 ? options.length - 1 : 0
|
||||||
|
}
|
||||||
|
sendCurrentLaunchOptionToTray(toolStore.state.launchIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
const setLaunchIndex = (index: number) => {
|
const setLaunchIndex = (index: number) => {
|
||||||
@@ -214,10 +223,27 @@ const removeLaunchOption = (index: number) => {
|
|||||||
...toolStore.state.launchOptions.slice(0, index),
|
...toolStore.state.launchOptions.slice(0, index),
|
||||||
...toolStore.state.launchOptions.slice(index + 1),
|
...toolStore.state.launchOptions.slice(index + 1),
|
||||||
]
|
]
|
||||||
|
// 如果删除的是当前项或当前项在删除项之后,需要调整索引
|
||||||
|
if (index <= toolStore.state.launchIndex) {
|
||||||
|
if (toolStore.state.launchIndex > 0) {
|
||||||
|
toolStore.state.launchIndex -= 1
|
||||||
|
} else {
|
||||||
|
toolStore.state.launchIndex = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 确保索引在有效范围内
|
||||||
|
if (toolStore.state.launchIndex >= toolStore.state.launchOptions.length) {
|
||||||
|
toolStore.state.launchIndex = toolStore.state.launchOptions.length > 0 ? toolStore.state.launchOptions.length - 1 : 0
|
||||||
|
}
|
||||||
|
sendCurrentLaunchOptionToTray(toolStore.state.launchIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
const sendCurrentLaunchOptionToTray = (index: number) => {
|
const sendCurrentLaunchOptionToTray = (index: number) => {
|
||||||
void emit("tray://get_current_launch_option", toolStore.state.launchOptions[index].name || index + 1)
|
// 发送完整的启动项列表和当前索引到托盘
|
||||||
|
void emit("tray://update_launch_options", {
|
||||||
|
options: toolStore.state.launchOptions,
|
||||||
|
currentIndex: index,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
const setPowerPlan = (plan: number) => {
|
const setPowerPlan = (plan: number) => {
|
||||||
toolStore.state.powerPlan = plan
|
toolStore.state.powerPlan = plan
|
||||||
@@ -227,6 +253,10 @@ const sendPowerPlanToTray = (plan: number) => {
|
|||||||
void emit("tray://get_powerplan", plan)
|
void emit("tray://get_powerplan", plan)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const setAutoCloseGame = (enabled: boolean) => {
|
||||||
|
toolStore.state.autoCloseGame = enabled
|
||||||
|
}
|
||||||
|
|
||||||
const setVideoSetting = (setting: VideoSetting) => {
|
const setVideoSetting = (setting: VideoSetting) => {
|
||||||
toolStore.state.videoSetting = setting
|
toolStore.state.videoSetting = setting
|
||||||
}
|
}
|
||||||
@@ -248,11 +278,14 @@ const addLaunchOption = (option: LaunchOption) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
toolStore.state.launchOptions = [...toolStore.state.launchOptions, option]
|
toolStore.state.launchOptions = [...toolStore.state.launchOptions, option]
|
||||||
|
// 同步更新托盘
|
||||||
|
sendCurrentLaunchOptionToTray(toolStore.state.launchIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
const resetToolStore = () => {
|
const resetToolStore = () => {
|
||||||
setLaunchOptions(defaultValue.launchOptions)
|
setLaunchOptions(defaultValue.launchOptions)
|
||||||
setLaunchIndex(defaultValue.launchIndex)
|
setLaunchIndex(defaultValue.launchIndex)
|
||||||
setPowerPlan(defaultValue.powerPlan)
|
setPowerPlan(defaultValue.powerPlan)
|
||||||
|
setAutoCloseGame(defaultValue.autoCloseGame)
|
||||||
setVideoSetting(defaultValue.videoSetting)
|
setVideoSetting(defaultValue.videoSetting)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user