feat(installer): 实现游戏安装与快捷方式创建功能- 添加禁用右键菜单和开发者工具的保护机制
- 引入GAME_CONFIG配置文件统一管理游戏信息 - 安装页面支持记录安装路径到localStorage - 成功页面增加创建桌面快捷方式选项 - 实现调用系统资源管理器打开游戏目录功能 - 集成Windows COM组件创建快捷方式的能力 - 更新UI界面展示多语言标题和版本信息 - 升级daisyui依赖至5.5.2版本- 调整窗口标题和产品名称为"游戏安装工具 - NoPJ"
This commit is contained in:
18
package-lock.json
generated
18
package-lock.json
generated
@@ -14,7 +14,7 @@
|
|||||||
"@tauri-apps/plugin-opener": "^2.5.2",
|
"@tauri-apps/plugin-opener": "^2.5.2",
|
||||||
"@tauri-apps/plugin-shell": "^2.3.3",
|
"@tauri-apps/plugin-shell": "^2.3.3",
|
||||||
"@tauri-apps/plugin-store": "^2.4.1",
|
"@tauri-apps/plugin-store": "^2.4.1",
|
||||||
"daisyui": "^5.4.7"
|
"daisyui": "^5.5.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/adapter-static": "^3.0.10",
|
"@sveltejs/adapter-static": "^3.0.10",
|
||||||
@@ -1204,7 +1204,6 @@
|
|||||||
"integrity": "sha512-TGFX1pZUt9qqY20Cv5NyYvy0iLWHf2jXi8s+eCGsig7jQMdwZWKUFMR6TbvFNhfDSUpc1sH/Y5EHv20g3HHA3g==",
|
"integrity": "sha512-TGFX1pZUt9qqY20Cv5NyYvy0iLWHf2jXi8s+eCGsig7jQMdwZWKUFMR6TbvFNhfDSUpc1sH/Y5EHv20g3HHA3g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@standard-schema/spec": "^1.0.0",
|
"@standard-schema/spec": "^1.0.0",
|
||||||
"@sveltejs/acorn-typescript": "^1.0.5",
|
"@sveltejs/acorn-typescript": "^1.0.5",
|
||||||
@@ -1244,7 +1243,6 @@
|
|||||||
"integrity": "sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ==",
|
"integrity": "sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sveltejs/vite-plugin-svelte-inspector": "^5.0.0",
|
"@sveltejs/vite-plugin-svelte-inspector": "^5.0.0",
|
||||||
"debug": "^4.4.1",
|
"debug": "^4.4.1",
|
||||||
@@ -1842,7 +1840,6 @@
|
|||||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
@@ -1935,9 +1932,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/daisyui": {
|
"node_modules/daisyui": {
|
||||||
"version": "5.4.7",
|
"version": "5.5.2",
|
||||||
"resolved": "https://registry.npmmirror.com/daisyui/-/daisyui-5.4.7.tgz",
|
"resolved": "https://registry.npmmirror.com/daisyui/-/daisyui-5.5.2.tgz",
|
||||||
"integrity": "sha512-2wYO61vTPCXk7xEBgnzLZAYoE0xS5IRLu/GSq0vORpB+cTrtubdx69NnA0loc0exvCY1s2fYL4lGZtFHe2ohNQ==",
|
"integrity": "sha512-uhEz8X1urrEhHhK51/vIQAL1fWPEJP8mTqCTEhfHfC8Dgur9XAz9SOWwl41LZgNs/fjWGvWzFcGM5WCw5ovp8g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/saadeghi/daisyui?sponsor=1"
|
"url": "https://github.com/saadeghi/daisyui?sponsor=1"
|
||||||
@@ -2572,7 +2569,6 @@
|
|||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -2615,7 +2611,6 @@
|
|||||||
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
|
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"prettier": "bin/prettier.cjs"
|
"prettier": "bin/prettier.cjs"
|
||||||
},
|
},
|
||||||
@@ -2632,7 +2627,6 @@
|
|||||||
"integrity": "sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ==",
|
"integrity": "sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"prettier": "^3.0.0",
|
"prettier": "^3.0.0",
|
||||||
"svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0"
|
"svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0"
|
||||||
@@ -2824,7 +2818,6 @@
|
|||||||
"integrity": "sha512-+VUy01yfDqNmIVMd/LLKl2TTtY0ovZN0rTonh+FhKr65mFwIYgU9WzgIZKS7U9/SPCQvWTsTGx9jyt+qRm/XFw==",
|
"integrity": "sha512-+VUy01yfDqNmIVMd/LLKl2TTtY0ovZN0rTonh+FhKr65mFwIYgU9WzgIZKS7U9/SPCQvWTsTGx9jyt+qRm/XFw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bufbuild/protobuf": "^2.5.0",
|
"@bufbuild/protobuf": "^2.5.0",
|
||||||
"buffer-builder": "^0.2.0",
|
"buffer-builder": "^0.2.0",
|
||||||
@@ -3222,7 +3215,6 @@
|
|||||||
"integrity": "sha512-kjkAjCk41mJfvJZG56XcJNOdJSke94JxtcX8zFzzz2vrt47E0LnoBzU6azIZ1aBxJgUep8qegAkguSf1GjxLXQ==",
|
"integrity": "sha512-kjkAjCk41mJfvJZG56XcJNOdJSke94JxtcX8zFzzz2vrt47E0LnoBzU6azIZ1aBxJgUep8qegAkguSf1GjxLXQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/remapping": "^2.3.4",
|
"@jridgewell/remapping": "^2.3.4",
|
||||||
"@jridgewell/sourcemap-codec": "^1.5.0",
|
"@jridgewell/sourcemap-codec": "^1.5.0",
|
||||||
@@ -3365,7 +3357,6 @@
|
|||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@@ -3387,7 +3378,6 @@
|
|||||||
"integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==",
|
"integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.25.0",
|
"esbuild": "^0.25.0",
|
||||||
"fdir": "^6.5.0",
|
"fdir": "^6.5.0",
|
||||||
|
|||||||
@@ -37,6 +37,6 @@
|
|||||||
"@tauri-apps/plugin-opener": "^2.5.2",
|
"@tauri-apps/plugin-opener": "^2.5.2",
|
||||||
"@tauri-apps/plugin-shell": "^2.3.3",
|
"@tauri-apps/plugin-shell": "^2.3.3",
|
||||||
"@tauri-apps/plugin-store": "^2.4.1",
|
"@tauri-apps/plugin-store": "^2.4.1",
|
||||||
"daisyui": "^5.4.7"
|
"daisyui": "^5.5.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
src-tauri/Cargo.lock
generated
1
src-tauri/Cargo.lock
generated
@@ -104,6 +104,7 @@ dependencies = [
|
|||||||
"tauri-plugin-shell",
|
"tauri-plugin-shell",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.17",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"windows",
|
||||||
"zip",
|
"zip",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -32,3 +32,4 @@ async-runtime = "0.0.0"
|
|||||||
tokio = { version = "1.48.0", features = ["time"] }
|
tokio = { version = "1.48.0", features = ["time"] }
|
||||||
tauri-plugin-shell = "2.3.3"
|
tauri-plugin-shell = "2.3.3"
|
||||||
tauri-plugin-opener = "2.5.2"
|
tauri-plugin-opener = "2.5.2"
|
||||||
|
windows = "0.61.3"
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ use thiserror::Error;
|
|||||||
use zip::ZipArchive;
|
use zip::ZipArchive;
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::PathBuf;
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::process::Command;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use tauri::Emitter;
|
use tauri::Emitter;
|
||||||
|
|
||||||
@@ -145,3 +146,20 @@ pub async fn install_game(window: tauri::Window, target_dir: String) -> Result<S
|
|||||||
Ok(format!("成功解压 {} 个文件到指定目录!", total_files as u32))
|
Ok(format!("成功解压 {} 个文件到指定目录!", total_files as u32))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//调用资源管理器打开游戏目录
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn open_game_dir(install_path:String) -> Result<String,String> {
|
||||||
|
let game_path = Path::new(&install_path);
|
||||||
|
if !game_path.exists() {
|
||||||
|
return Err("游戏目录不存在".to_string());
|
||||||
|
}
|
||||||
|
let result = Command::new("explorer")
|
||||||
|
.arg(game_path)
|
||||||
|
.spawn()
|
||||||
|
.map_err(|e| format!("打开游戏目录失败: {}", e))?;
|
||||||
|
|
||||||
|
println!("资源管理器进程ID: {}", result.id());
|
||||||
|
Ok("已打开游戏目录".to_string())
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
mod network;
|
mod network;
|
||||||
mod gameins;
|
mod gameins;
|
||||||
|
mod shortcut;
|
||||||
|
|
||||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||||
pub fn run() {
|
pub fn run() {
|
||||||
@@ -19,7 +20,9 @@ pub fn run() {
|
|||||||
})
|
})
|
||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
network::get_network_interfaces,
|
network::get_network_interfaces,
|
||||||
gameins::install_game
|
gameins::install_game,
|
||||||
|
gameins::open_game_dir,
|
||||||
|
shortcut::create_shortcut,
|
||||||
])
|
])
|
||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
|
|||||||
132
src-tauri/src/shortcut.rs
Normal file
132
src-tauri/src/shortcut.rs
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
use std::path::Path;
|
||||||
|
use windows::{
|
||||||
|
core::{Interface, PCWSTR, HRESULT},
|
||||||
|
Win32::{
|
||||||
|
System::{
|
||||||
|
Com::{
|
||||||
|
CoCreateInstance, CoInitializeEx, CoUninitialize,
|
||||||
|
IPersistFile, CLSCTX_INPROC_SERVER, COINIT_APARTMENTTHREADED
|
||||||
|
},
|
||||||
|
},
|
||||||
|
UI::Shell::{IShellLinkW, ShellLink},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn create_shortcut(
|
||||||
|
install_path: String,
|
||||||
|
shortcut_name: String,
|
||||||
|
exe_name: String
|
||||||
|
) -> Result<String, String> {
|
||||||
|
unsafe {
|
||||||
|
// 初始化COM库
|
||||||
|
let com_result = CoInitializeEx(None, COINIT_APARTMENTTHREADED);
|
||||||
|
let needs_uninit = com_result.is_ok() || com_result == HRESULT(0x80010106u32 as i32); // S_FALSE or RPC_E_CHANGED_MODE
|
||||||
|
|
||||||
|
let result = create_shortcut_internal(&install_path, &shortcut_name, &exe_name);
|
||||||
|
|
||||||
|
if needs_uninit {
|
||||||
|
CoUninitialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn create_shortcut_internal(
|
||||||
|
install_path: &str,
|
||||||
|
shortcut_name: &str,
|
||||||
|
exe_name: &str
|
||||||
|
) -> Result<String, String> {
|
||||||
|
// 获取当前用户桌面路径
|
||||||
|
let desktop_path = get_desktop_path()?;
|
||||||
|
let shortcut_file = format!("{}.lnk", shortcut_name);
|
||||||
|
let shortcut_full_path = Path::new(&desktop_path).join(&shortcut_file);
|
||||||
|
|
||||||
|
// 构建目标程序路径
|
||||||
|
let target_path = Path::new(install_path).join(exe_name);
|
||||||
|
if !target_path.exists() {
|
||||||
|
return Err(format!("目标程序不存在: {}", target_path.display()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建IShellLink对象
|
||||||
|
let shell_link: IShellLinkW = CoCreateInstance(
|
||||||
|
&ShellLink,
|
||||||
|
None,
|
||||||
|
CLSCTX_INPROC_SERVER
|
||||||
|
).map_err(|e| format!("创建ShellLink失败: {:?}", e))?;
|
||||||
|
|
||||||
|
// 设置快捷方式属性
|
||||||
|
set_shell_link_properties(&shell_link, &target_path, install_path)?;
|
||||||
|
|
||||||
|
// 保存快捷方式
|
||||||
|
save_shortcut(&shell_link, &shortcut_full_path)?;
|
||||||
|
|
||||||
|
Ok(format!("快捷方式创建成功: {}", shortcut_full_path.display()))
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn get_desktop_path() -> Result<String, String> {
|
||||||
|
std::env::var("USERPROFILE")
|
||||||
|
.map(|profile| format!("{}\\Desktop", profile))
|
||||||
|
.or_else(|_| {
|
||||||
|
std::env::var("USERNAME")
|
||||||
|
.map(|username| format!("C:\\Users\\{}\\Desktop", username))
|
||||||
|
})
|
||||||
|
.map_err(|_| "无法获取用户桌面路径".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn set_shell_link_properties(
|
||||||
|
shell_link: &IShellLinkW,
|
||||||
|
target_path: &Path,
|
||||||
|
working_dir: &str
|
||||||
|
) -> Result<(), String> {
|
||||||
|
// 设置目标路径
|
||||||
|
let target_path_str = target_path.to_string_lossy();
|
||||||
|
let wide_target: Vec<u16> = to_wide_string(&target_path_str);
|
||||||
|
|
||||||
|
// 用 map_err + ? 处理错误并转换为 String
|
||||||
|
shell_link
|
||||||
|
.SetPath(PCWSTR(wide_target.as_ptr()))
|
||||||
|
.map_err(|e| format!("设置目标路径失败: {:?}", e))?;
|
||||||
|
|
||||||
|
// 设置工作目录
|
||||||
|
let wide_working_dir: Vec<u16> = to_wide_string(working_dir);
|
||||||
|
shell_link
|
||||||
|
.SetWorkingDirectory(PCWSTR(wide_working_dir.as_ptr()))
|
||||||
|
.map_err(|e| format!("设置工作目录失败: {:?}", e))?;
|
||||||
|
|
||||||
|
// 设置描述
|
||||||
|
let description = "NoPJ创建的快捷方式 - nopj.cn";
|
||||||
|
let wide_desc: Vec<u16> = to_wide_string(description);
|
||||||
|
shell_link
|
||||||
|
.SetDescription(PCWSTR(wide_desc.as_ptr()))
|
||||||
|
.map_err(|e| format!("设置描述失败: {:?}", e))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsafe fn save_shortcut(
|
||||||
|
shell_link: &IShellLinkW,
|
||||||
|
shortcut_path: &Path
|
||||||
|
) -> Result<(), String> {
|
||||||
|
// 转换为 IPersistFile 接口
|
||||||
|
let persist_file: IPersistFile = shell_link
|
||||||
|
.cast()
|
||||||
|
.map_err(|e| format!("接口转换失败: {:?}", e))?;
|
||||||
|
|
||||||
|
// 保存文件
|
||||||
|
let path_str = shortcut_path.to_string_lossy();
|
||||||
|
let wide_path: Vec<u16> = to_wide_string(&path_str);
|
||||||
|
|
||||||
|
// Save 可能返回 windows::core::Result<()>,同样用 map_err ? 处理
|
||||||
|
persist_file
|
||||||
|
.Save(PCWSTR(wide_path.as_ptr()), true)
|
||||||
|
.map_err(|e| format!("保存快捷方式失败: {:?}", e))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_wide_string(s: &str) -> Vec<u16> {
|
||||||
|
s.encode_utf16().chain(std::iter::once(0)).collect()
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
|
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
|
||||||
"productName": "NoPJ安装器",
|
"productName": "游戏安装工具 - NoPJ",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"identifier": "cn.nopj.gametools",
|
"identifier": "cn.nopj.gametools",
|
||||||
"build": {
|
"build": {
|
||||||
@@ -12,13 +12,13 @@
|
|||||||
"app": {
|
"app": {
|
||||||
"windows": [
|
"windows": [
|
||||||
{
|
{
|
||||||
"title": "Game Install",
|
"title": "游戏安装工具 - NoPJ",
|
||||||
"width": 1280,
|
"width": 1280,
|
||||||
"height": 720,
|
"height": 720,
|
||||||
"resizable": false,
|
"resizable": false,
|
||||||
"fullscreen": false,
|
"fullscreen": false,
|
||||||
"decorations": false
|
"decorations": false,
|
||||||
|
"devtools": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"security": {
|
"security": {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
%sveltekit.head%
|
%sveltekit.head%
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body data-sveltekit-preload-data="hover">
|
<body data-sveltekit-preload-data="hover">
|
||||||
<div style="display: contents">%sveltekit.body%</div>
|
<div style="display: contents">%sveltekit.body%</div>
|
||||||
|
|||||||
10
src/lib/config.ts
Normal file
10
src/lib/config.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
export const GAME_CONFIG = {
|
||||||
|
title: '逃离鸭科夫',
|
||||||
|
title_rus: 'Побег из Дакова',
|
||||||
|
title_en: 'Escape From Duckov',
|
||||||
|
version: '1.1.6',
|
||||||
|
description: "「逃离鸭科夫」是一款鸭子题材PVE俯视角撤离射击游戏。你要在鸭科夫的世界里搜寻物资,建造藏身处,升级装备,从一无所有到做大做强;面对虎视眈眈的敌鸭,想方设法生存下去——或者逃离。",
|
||||||
|
pathname: '\\Duckov',
|
||||||
|
launch: 'Duckov.exe',
|
||||||
|
shortcut_name: "逃离鸭科夫",
|
||||||
|
};
|
||||||
@@ -12,6 +12,20 @@
|
|||||||
<svelte:head>
|
<svelte:head>
|
||||||
<link rel="icon" href={favicon} />
|
<link rel="icon" href={favicon} />
|
||||||
<Sprite />
|
<Sprite />
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('contextmenu', (e) => {
|
||||||
|
e.preventDefault(); // 禁用右键菜单
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('keydown', (event) => {
|
||||||
|
// 禁用 F12 和 Ctrl+Shift+I
|
||||||
|
if (event.key === 'F12' || (event.ctrlKey && event.shiftKey && event.key === 'I')) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<Header/>
|
<Header/>
|
||||||
|
|||||||
@@ -1,61 +1,40 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
|
import { GAME_CONFIG } from '$lib/config';
|
||||||
|
|
||||||
let isHover = false;
|
|
||||||
|
|
||||||
// 提取函数:处理“激活”状态
|
|
||||||
function handleFocus() {
|
|
||||||
isHover = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 提取函数:处理“失活”状态
|
|
||||||
function handleBlur() {
|
|
||||||
isHover = false;
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<!--<div class={'relative pt-10 px-4 h-screen w-screen flex justify-center items-center transition-all ' }}-->
|
<div class="hero min-h-screen transition-all duration-300 "
|
||||||
<!-- class:backdrop-blur-xs={isHover}-->
|
|
||||||
<!-->-->
|
|
||||||
<!-- <button class="btn btn-wide" on:mouseenter={()=>{isHover = true}} on:mouseout={()=>{isHover = false}}>安装游戏</button>-->
|
|
||||||
<!--</div>-->
|
|
||||||
|
|
||||||
|
|
||||||
<div class="hero min-h-screen transition-all {isHover ? 'bg-neutral-700/30' : ''} "
|
|
||||||
class:backdrop-blur-xs={isHover}
|
|
||||||
|
|
||||||
>
|
>
|
||||||
<div class="hero-content text-center select-none">
|
<div class="hero-content text-center select-none p-12 group hover:backdrop-blur hover:bg-base-100/70">
|
||||||
<div class="max-w-md" >
|
<div class="w-xl group-hover:w-2xl transition-all duration-1000 ease-in-out ">
|
||||||
<h1 class="text-5xl font-bold"
|
<p class="text-5xl font-bold ">
|
||||||
on:mouseenter={()=>{isHover = true}}
|
<span class="text-rotate">
|
||||||
on:mouseout={()=>{isHover = false}}
|
<span class="justify-items-center">
|
||||||
on:blur={handleBlur}
|
<span>{GAME_CONFIG.title}</span>
|
||||||
on:focus={handleFocus}
|
<span>{GAME_CONFIG.title_en}</span>
|
||||||
>逃离鸭科夫</h1>
|
<span>{GAME_CONFIG.title_rus}</span>
|
||||||
<p class="py-6"
|
</span>
|
||||||
on:mouseenter={()=>{isHover = true}}
|
</span>
|
||||||
on:mouseout={()=>{isHover = false}}
|
|
||||||
on:blur={handleBlur}
|
|
||||||
on:focus={handleFocus}
|
|
||||||
>
|
|
||||||
<span class="font-bold">《逃离鸭科夫》</span>是一款鸭子题材PVE俯视角撤离射击游戏。你要在鸭科夫的世界里搜寻物资,建造藏身处,升级装备,从一无所有到做大做强;面对虎视眈眈的敌鸭,想方设法生存下去——或者逃离。
|
|
||||||
</p>
|
</p>
|
||||||
<button class="btn btn-primary btn-wide "
|
<p class="pt-2">versin {GAME_CONFIG.version}</p>
|
||||||
on:mouseenter={()=>{isHover = true}}
|
<p class="py-6 text-left">
|
||||||
on:mouseout={()=>{isHover = false}}
|
{GAME_CONFIG.description}
|
||||||
on:blur={handleBlur}
|
</p>
|
||||||
on:focus={handleFocus}
|
<!-- 事件会冒泡到父元素hero-content,因此这些按钮可以不用显式绑定事件 -->
|
||||||
on:click={()=>{goto('/install')}}
|
<div class="w-full">
|
||||||
>安装游戏</button>
|
<button class="btn btn-ghost btn-wide group-hover:btn-primary "
|
||||||
<button class="btn btn-primary btn-wide "
|
on:click="{() => goto('/install')}"
|
||||||
on:mouseenter={()=>{isHover = true}}
|
>
|
||||||
on:mouseout={()=>{isHover = false}}
|
安装游戏
|
||||||
on:blur={handleBlur}
|
</button>
|
||||||
on:focus={handleFocus}
|
</div>
|
||||||
on:click={()=>{goto('/success')}}
|
|
||||||
>完成</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { listen } from '@tauri-apps/api/event';
|
import { listen } from '@tauri-apps/api/event';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
|
import { GAME_CONFIG } from '$lib/config';
|
||||||
|
|
||||||
let installPath = '';
|
let installPath = '';
|
||||||
let isInstalling = false;
|
let isInstalling = false;
|
||||||
@@ -30,10 +31,6 @@
|
|||||||
progressPercent = event.payload.percentage;
|
progressPercent = event.payload.percentage;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 组件销毁时停止监听
|
|
||||||
return () => {
|
|
||||||
unlisten();
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -60,7 +57,7 @@
|
|||||||
installPath = '';
|
installPath = '';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
installPath = selected + '\\DK'; // 您的逻辑
|
installPath = selected +GAME_CONFIG.pathname; // 您的逻辑
|
||||||
log('已选择安装目录:'+ installPath +",点击开始安装即可开始自动安装");
|
log('已选择安装目录:'+ installPath +",点击开始安装即可开始自动安装");
|
||||||
}else if (selected === null) {
|
}else if (selected === null) {
|
||||||
log("未选择安装目录");
|
log("未选择安装目录");
|
||||||
@@ -82,7 +79,7 @@
|
|||||||
progressPercent = 0; // 重置进度条
|
progressPercent = 0; // 重置进度条
|
||||||
log("安装程序启动...");
|
log("安装程序启动...");
|
||||||
|
|
||||||
|
localStorage.setItem('installPath', installPath)
|
||||||
try {
|
try {
|
||||||
// 我们仍然调用 invoke,但现在它只负责启动
|
// 我们仍然调用 invoke,但现在它只负责启动
|
||||||
// 真正的日志将通过 'install_progress' 事件传来
|
// 真正的日志将通过 'install_progress' 事件传来
|
||||||
|
|||||||
@@ -2,10 +2,39 @@
|
|||||||
import tl from '$lib/assets/images/tl.png'
|
import tl from '$lib/assets/images/tl.png'
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { openUrl } from '@tauri-apps/plugin-opener';
|
import { openUrl } from '@tauri-apps/plugin-opener';
|
||||||
|
import { getCurrentWindow } from '@tauri-apps/api/window';
|
||||||
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
|
import { GAME_CONFIG } from '$lib/config';
|
||||||
|
|
||||||
let data:[{attributes:{title:string, slug:string, id:string}}] = [];
|
|
||||||
|
const win = getCurrentWindow();
|
||||||
|
|
||||||
|
|
||||||
|
const closeWindow = async () => {
|
||||||
|
if (openGameDir) {
|
||||||
|
try {
|
||||||
|
const result = await invoke('open_game_dir', {
|
||||||
|
installPath: installPath
|
||||||
|
});
|
||||||
|
console.log(result);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('启动游戏失败:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await create_shortcut()
|
||||||
|
await win.close()
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
let data:[{attributes:{title:string, slug:string, id:string}}];
|
||||||
let loading = true;
|
let loading = true;
|
||||||
let error;
|
let error;
|
||||||
|
//是否创建快捷方式变量
|
||||||
|
let createShortcut = true;
|
||||||
|
//是否退出后启动变量
|
||||||
|
let openGameDir = true;
|
||||||
|
|
||||||
|
let installPath = localStorage.getItem('installPath');
|
||||||
|
|
||||||
const url = "https://nopj.cn/api/discussions?include=tags.parent&filter[tag]=resources&sort=-createdAt&page[offset]=0&page[limit]=5"
|
const url = "https://nopj.cn/api/discussions?include=tags.parent&filter[tag]=resources&sort=-createdAt&page[offset]=0&page[limit]=5"
|
||||||
|
|
||||||
@@ -44,11 +73,68 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const create_shortcut = async () => {
|
||||||
|
if (createShortcut){
|
||||||
|
|
||||||
|
try{
|
||||||
|
const result = await invoke('create_shortcut', {
|
||||||
|
installPath: localStorage.getItem('installPath'),
|
||||||
|
shortcutName: GAME_CONFIG.shortcut_name,
|
||||||
|
exeName: GAME_CONFIG.launch,
|
||||||
|
})
|
||||||
|
console.log(result);
|
||||||
|
}catch (e){
|
||||||
|
console.error("创建快捷方式失败:", e);
|
||||||
|
alert(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="h-screen w-screen flex z-10 pt-8">
|
<div class="h-screen w-screen flex z-10 pt-8 select-none relative">
|
||||||
<div class="flex-1">
|
<div class="flex-1 flex items-center justify-center">
|
||||||
123
|
<div class="w-2xl p-8 backdrop-blur-xl shadow-xl shadow-base/30">
|
||||||
|
<div class=" text-center">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="120" viewBox="0 0 32 32" class="m-auto">
|
||||||
|
<g fill="none">
|
||||||
|
<path fill="url(#SVGFnVq5bNm)" d="M30 16c0 7.732-6.268 14-14 14S2 23.732 2 16S8.268 2 16 2s14 6.268 14 14" />
|
||||||
|
<path fill="url(#SVGcpquMdXZ)" d="M22.707 12.707a1 1 0 0 0-1.414-1.414L14.5 18.086l-3.293-3.293a1 1 0 0 0-1.414 1.414l4 4a1 1 0 0 0 1.414 0z" />
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="SVGFnVq5bNm" x1="3" x2="22.323" y1="7.25" y2="27.326" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#52d17c" />
|
||||||
|
<stop offset="1" stop-color="#22918b" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="SVGcpquMdXZ" x1="12.031" x2="14.162" y1="11.969" y2="22.66" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#fff" />
|
||||||
|
<stop offset="1" stop-color="#e3ffd9" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<div class="font-bold text-3xl pt-2 pb-4">
|
||||||
|
安装完成
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<fieldset class="fieldset bg-base-100/30 border-base-300/10 rounded-box w-full border p-4">
|
||||||
|
<legend class="fieldset-legend">启动设置</legend>
|
||||||
|
<label class="label">
|
||||||
|
<input type="checkbox" checked={createShortcut} class="checkbox" />
|
||||||
|
设置桌面快捷方式
|
||||||
|
</label>
|
||||||
|
<label class="label pt-4">
|
||||||
|
<input type="checkbox" checked={openGameDir} class="checkbox" />
|
||||||
|
打开游戏目录
|
||||||
|
</label>
|
||||||
|
</fieldset>
|
||||||
|
<div class="w-full text-center pt-12">
|
||||||
|
<button class="btn btn-primary btn-wide "
|
||||||
|
on:click={closeWindow}
|
||||||
|
>退出</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class=" w-96 bg-base-200 bg-cover bg-center text-base-300 overflow-hidden relative" style="background-image: url({tl})">
|
<div class=" w-96 bg-base-200 bg-cover bg-center text-base-300 overflow-hidden relative" style="background-image: url({tl})">
|
||||||
<div class="h-full w-full absolute top-0 z-0">
|
<div class="h-full w-full absolute top-0 z-0">
|
||||||
@@ -56,9 +142,16 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="z-10 relative p-4 w-full ">
|
<div class="z-10 relative p-4 w-full ">
|
||||||
<h1 class="font-bold text-4xl text-right">NoPJ</h1>
|
<h1 class="font-bold text-4xl text-right">NoPJ</h1>
|
||||||
<p class="font-bold text-right pt-2 ">最新资源</p>
|
<p class="pt-2 ">
|
||||||
|
<span class="text-rotate font-bold text-right ">
|
||||||
|
<span>
|
||||||
|
<span>最新资源</span>
|
||||||
|
<span>Latest Resources</span>
|
||||||
|
<span>Последний ресурс</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
<div class="pt-4">
|
<div class="pt-4">
|
||||||
|
|
||||||
<ul class="list">
|
<ul class="list">
|
||||||
{#each data as item}
|
{#each data as item}
|
||||||
<li class=" list-row backdrop-blur-xs hover:backdrop-blur-xl hover:font-bold transition-all mt-4 font-misans">
|
<li class=" list-row backdrop-blur-xs hover:backdrop-blur-xl hover:font-bold transition-all mt-4 font-misans">
|
||||||
@@ -74,5 +167,11 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="absolute bottom-3 text-center z-10 left-0 w-full">
|
||||||
|
<p class="font-bold">Designed by Chaos & <a href="https://nopj.cn"
|
||||||
|
on:click={(e)=>handleExternalLink(e, 'https://nopj.cn')}
|
||||||
|
>Nopj</a></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
Reference in New Issue
Block a user