From 2a4a9511d62e27b03e751a658c8b10ce72a7d594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E9=B1=BC=E5=BC=80=E5=8F=91?= Date: Wed, 20 May 2026 22:19:39 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=87=8D=E6=96=B0=E7=94=9F=E6=88=90=20u?= =?UTF-8?q?pdater=20=E5=AF=86=E9=92=A5=E5=AF=B9=EF=BC=8C=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E8=BF=90=E8=A1=8C=E6=A8=A1=E5=BC=8F=E5=88=87=E6=8D=A2=EF=BC=8C?= =?UTF-8?q?=E5=90=AF=E7=94=A8=20updater=20=E4=BA=A7=E7=89=A9=E7=94=9F?= =?UTF-8?q?=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/release.yml | 4 +- .gitlab-ci.yml | 10 +- tauri-app/src-tauri/src/lib.rs | 25 ++-- tauri-app/src-tauri/src/storage/config.rs | 61 ++++------ tauri-app/src-tauri/tauri.conf.json | 4 +- tauri-app/src-tauri/tauri.key.pub | 2 +- tauri-app/src/api/modules/config.ts | 4 +- .../Modal/EnvironmentSwitchModal.tsx | 72 +---------- tauri-app/src/main.tsx | 114 +++++++++++------- tauri-app/src/pages/Settings/AboutUs.tsx | 4 +- 10 files changed, 126 insertions(+), 174 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0295b2d..e2131d1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -101,7 +101,8 @@ jobs: name: macos-universal path: | tauri-app/src-tauri/target/universal-apple-darwin/release/bundle/dmg/*.dmg - tauri-app/src-tauri/target/universal-apple-darwin/release/bundle/dmg/*.dmg.sig + tauri-app/src-tauri/target/universal-apple-darwin/release/bundle/macos/*.app.tar.gz + tauri-app/src-tauri/target/universal-apple-darwin/release/bundle/macos/*.app.tar.gz.sig build-windows: name: Build Windows (x64) @@ -177,4 +178,5 @@ jobs: path: | tauri-app/src-tauri/target/x86_64-pc-windows-msvc/release/bundle/nsis/*.exe tauri-app/src-tauri/target/x86_64-pc-windows-msvc/release/bundle/nsis/*.exe.sig + tauri-app/src-tauri/target/x86_64-pc-windows-msvc/release/bundle/nsis/*-setup.exe diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1f1ab58..42e15a0 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -80,8 +80,9 @@ build-frontend-macos: paths: # DMG 安装包 (推荐用户下载) - tauri-app/src-tauri/target/universal-apple-darwin/release/bundle/dmg/*.dmg - # .app bundle (供进一步分发或公证使用) - - tauri-app/src-tauri/target/universal-apple-darwin/release/bundle/macos/*.app + # Updater 专用包 + 签名 + - tauri-app/src-tauri/target/universal-apple-darwin/release/bundle/macos/*.app.tar.gz + - tauri-app/src-tauri/target/universal-apple-darwin/release/bundle/macos/*.app.tar.gz.sig expire_in: "${ARTIFACT_EXPIRE_DAYS} days" timeout: 45 minutes retry: @@ -114,8 +115,11 @@ build-frontend-windows: artifacts: name: "meijiaka-windows-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHORT_SHA}" paths: - # NSIS 安装包 (推荐用户下载) + # Updater 专用包 + 签名 - tauri-app/src-tauri/target/x86_64-pc-windows-msvc/release/bundle/nsis/*.exe + - tauri-app/src-tauri/target/x86_64-pc-windows-msvc/release/bundle/nsis/*.exe.sig + # NSIS 安装包 (推荐用户下载) + - tauri-app/src-tauri/target/x86_64-pc-windows-msvc/release/bundle/nsis/*-setup.exe # MSI 安装包 (企业部署场景) - tauri-app/src-tauri/target/x86_64-pc-windows-msvc/release/bundle/msi/*.msi expire_in: "${ARTIFACT_EXPIRE_DAYS} days" diff --git a/tauri-app/src-tauri/src/lib.rs b/tauri-app/src-tauri/src/lib.rs index 962c482..256d979 100644 --- a/tauri-app/src-tauri/src/lib.rs +++ b/tauri-app/src-tauri/src/lib.rs @@ -61,25 +61,18 @@ pub fn run() { crate::storage::init_app_data_dir(app_data_dir); } - // Release 构建也打开 DevTools(临时:排查 Windows 网络问题) - // 排查完成后可移除或改为快捷键触发 + // 窗口初始 visible=false,setup 阶段先显示窗口 if let Some(window) = app.get_webview_window("main") { - // 窗口默认 visible=false,必须先 show 再 open_devtools 才有效 - let _ = window.show(); - let _ = window.set_focus(); - // 延迟 1s 等 WebView 完全初始化 - let app_handle = app.handle().clone(); - std::thread::spawn(move || { - std::thread::sleep(std::time::Duration::from_secs(1)); - if let Some(w) = app_handle.get_webview_window("main") { - let _ = w.open_devtools(); - } - }); + if let Err(e) = window.show() { + eprintln!("[setup] window.show() failed: {}", e); + } + if let Err(e) = window.set_focus() { + eprintln!("[setup] window.set_focus() failed: {}", e); + } + } else { + eprintln!("[setup] main window not found"); } - // 全局快捷键备选:F12 / Ctrl+Shift+I 手动打开 DevTools - // (通过前端 keydown 事件 + invoke 调用,无需额外插件) - // macOS 自定义菜单栏(中文本地化) #[cfg(target_os = "macos")] { diff --git a/tauri-app/src-tauri/src/storage/config.rs b/tauri-app/src-tauri/src/storage/config.rs index 979267c..74108e3 100644 --- a/tauri-app/src-tauri/src/storage/config.rs +++ b/tauri-app/src-tauri/src/storage/config.rs @@ -2,19 +2,21 @@ //! //! 存储路径: {app_local_data_dir}/config.json //! -//! 所有环境预设和默认配置均在 Rust 层定义,前端不硬编码任何 API 地址。 +//! 运行模式(生产/调试)控制桌面端行为,API 地址固定为 dev 环境。 use std::path::PathBuf; use serde::{Deserialize, Serialize}; use crate::storage::engine::{read_json, atomic_write_json, StorageError}; +const API_BASE_URL: &str = "https://dev.tapi.meijiaka.cn/api/v1"; + #[derive(Serialize, Deserialize, Clone, Debug)] pub struct AppConfig { pub api_base_url: String, pub environment: String, } -/// 环境预设(唯一数据源,前端通过 IPC 获取) +/// 运行模式预设(唯一数据源,前端通过 IPC 获取) #[derive(Serialize, Clone, Debug)] pub struct EnvironmentPreset { pub label: String, @@ -25,27 +27,22 @@ pub struct EnvironmentPreset { fn presets() -> Vec { vec![ EnvironmentPreset { - label: "生产环境".to_string(), - url: "https://tapi.meijiaka.cn/api/v1".to_string(), + label: "生产模式".to_string(), + url: API_BASE_URL.to_string(), env: "production".to_string(), }, EnvironmentPreset { - label: "测试环境".to_string(), - url: "https://dev.tapi.meijiaka.cn/api/v1".to_string(), - env: "staging".to_string(), - }, - EnvironmentPreset { - label: "本地开发".to_string(), - url: "http://127.0.0.1:8081/api/v1".to_string(), - env: "development".to_string(), + label: "调试模式".to_string(), + url: API_BASE_URL.to_string(), + env: "debug".to_string(), }, ] } impl Default for AppConfig { fn default() -> Self { - // 默认指向测试环境(索引 1),避免首次安装时误连生产环境 - let preset = &presets()[1]; + // 默认生产模式 + let preset = &presets()[0]; Self { api_base_url: preset.url.clone(), environment: preset.env.clone(), @@ -59,22 +56,24 @@ fn get_config_path() -> Result { /// 同步读取 API Base URL(供 Rust 命令内部使用) pub fn get_api_base_url_sync() -> String { - let path = match get_config_path() { - Ok(p) => p, - // 默认指向测试环境(索引 1) - Err(_) => return presets()[1].url.clone(), - }; - match read_json::(&path) { - Ok(Some(config)) => config.api_base_url, - _ => AppConfig::default().api_base_url, - } + API_BASE_URL.to_string() } #[tauri::command] pub async fn load_app_config() -> Result { let path = get_config_path().map_err(|e| e.to_string())?; match read_json::(&path) { - Ok(Some(config)) => Ok(config), + Ok(Some(config)) => { + // 兼容旧环境值:旧值(staging/development/custom)统一映射为 debug + let environment = match config.environment.as_str() { + "production" => "production".to_string(), + _ => "debug".to_string(), + }; + Ok(AppConfig { + api_base_url: API_BASE_URL.to_string(), + environment, + }) + } Ok(None) => Ok(AppConfig::default()), Err(e) => Err(e.to_string()), } @@ -86,19 +85,9 @@ pub async fn get_environment_presets() -> Result, String> } #[tauri::command] -pub async fn save_app_config(environment: String, custom_url: Option) -> Result<(), String> { - let url = if environment == "custom" { - custom_url.ok_or("自定义地址不能为空")? - } else { - presets() - .iter() - .find(|p| p.env == environment) - .map(|p| p.url.clone()) - .unwrap_or_else(|| "https://dev.tapi.meijiaka.cn/api/v1".to_string()) - }; - +pub async fn save_app_config(environment: String, _custom_url: Option) -> Result<(), String> { let config = AppConfig { - api_base_url: url, + api_base_url: API_BASE_URL.to_string(), environment, }; diff --git a/tauri-app/src-tauri/tauri.conf.json b/tauri-app/src-tauri/tauri.conf.json index ed8ced4..5a5590e 100644 --- a/tauri-app/src-tauri/tauri.conf.json +++ b/tauri-app/src-tauri/tauri.conf.json @@ -41,7 +41,7 @@ "plugins": { "opener": {}, "updater": { - "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDkwRTdFOUI5NkZERkNFRDMKUldUVHp0OXZ1ZW5ua0pVN2M0ZGoyakxneFc5am5pR21UNFdCaThDN0p5S0JubUxUVUhLNnlkMWkK", + "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEM0MzBGMjFDM0Q2MzNGN0MKUldSOFAyTTlIUEl3eFBEUTBidXpiSEI5TVFrQnk4RnJyU0QzVWtvSlNheVY2SEhNNkU1ZzN5VHIK", "endpoints": [ "https://dev.tapi.meijiaka.cn/api/v1/update/check?version={{current_version}}&target={{target}}&arch={{arch}}" ] @@ -49,7 +49,7 @@ }, "bundle": { "active": true, - "createUpdaterArtifacts": false, + "createUpdaterArtifacts": true, "targets": [ "nsis", "dmg", diff --git a/tauri-app/src-tauri/tauri.key.pub b/tauri-app/src-tauri/tauri.key.pub index 9526389..c945f39 100644 --- a/tauri-app/src-tauri/tauri.key.pub +++ b/tauri-app/src-tauri/tauri.key.pub @@ -1 +1 @@ -dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEZCMUQyRjU1MzY3ODZDREIKUldUYmJIZzJWUzhkK3l4VTl0RGE3OFRhN2JXdjBjZnR5UC9aMmwxTUY5K2ZoeDVNOStjZlFwQWYK \ No newline at end of file +dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEM0MzBGMjFDM0Q2MzNGN0MKUldSOFAyTTlIUEl3eFBEUTBidXpiSEI5TVFrQnk4RnJyU0QzVWtvSlNheVY2SEhNNkU1ZzN5VHIK diff --git a/tauri-app/src/api/modules/config.ts b/tauri-app/src/api/modules/config.ts index 4de36b9..b1bb91c 100644 --- a/tauri-app/src/api/modules/config.ts +++ b/tauri-app/src/api/modules/config.ts @@ -40,8 +40,8 @@ export async function getEnvironmentPresets(): Promise { /** * 保存应用配置 - * @param environment 环境标识(production / staging / development / custom) - * @param customUrl 自定义地址(仅 custom 时必填) + * @param environment 运行模式(production / debug) + * @param customUrl 已废弃,保留参数仅为兼容旧调用 */ export async function saveAppConfig(environment: string, customUrl?: string): Promise { await invoke('save_app_config', { environment, customUrl }); diff --git a/tauri-app/src/components/Modal/EnvironmentSwitchModal.tsx b/tauri-app/src/components/Modal/EnvironmentSwitchModal.tsx index e810991..08c9c92 100644 --- a/tauri-app/src/components/Modal/EnvironmentSwitchModal.tsx +++ b/tauri-app/src/components/Modal/EnvironmentSwitchModal.tsx @@ -1,5 +1,5 @@ /** - * 环境切换弹窗 + * 运行模式切换弹窗 * ============= * * 通过「关于我们」页面连击版本号 5 次触发。 @@ -13,7 +13,7 @@ import { getEnvironmentPresets, type EnvironmentPreset } from '../../api/modules interface EnvironmentSwitchModalProps { open: boolean; currentEnv: string; - onSave: (env: string, customUrl?: string) => void; + onSave: (env: string) => void; onCancel: () => void; } @@ -25,7 +25,6 @@ export default function EnvironmentSwitchModal({ }: EnvironmentSwitchModalProps) { const [presets, setPresets] = useState([]); const [selected, setSelected] = useState(currentEnv); - const [customUrl, setCustomUrl] = useState(''); useEffect(() => { if (open) { @@ -36,12 +35,7 @@ export default function EnvironmentSwitchModal({ if (!open) {return null;} const handleSave = () => { - if (selected === 'custom') { - if (!customUrl.trim()) {return;} - onSave('custom', customUrl.trim()); - } else { - onSave(selected); - } + onSave(selected); }; return ( @@ -55,7 +49,7 @@ export default function EnvironmentSwitchModal({ -

切换服务器环境

+

切换运行模式

@@ -85,67 +79,11 @@ export default function EnvironmentSwitchModal({ style={{ margin: '2px 0 0' }} />
-
+
{preset.label}
-
- {preset.url} -
))} - - - - {selected === 'custom' && ( - setCustomUrl(e.target.value)} - style={{ - marginLeft: '32px', - padding: '8px 12px', - borderRadius: '8px', - border: '1px solid var(--border-light)', - fontSize: '14px', - background: 'var(--bg-primary)', - color: 'var(--text-primary)', - width: 'calc(100% - 56px)', - boxSizing: 'border-box', - }} - /> - )}
diff --git a/tauri-app/src/main.tsx b/tauri-app/src/main.tsx index 0950eda..465942e 100644 --- a/tauri-app/src/main.tsx +++ b/tauri-app/src/main.tsx @@ -3,54 +3,80 @@ import { BrowserRouter } from 'react-router-dom'; import App from './App'; import './styles/variables.css'; import './styles/global.css'; +import { loadAppConfig } from './api/modules/config'; -// 全局禁用浏览器默认右键菜单,提升桌面应用质感 -// 输入框/文本区自动放行;按住 Shift 时放行(方便 DevTools 检查) -document.addEventListener('contextmenu', (e) => { - const target = e.target as HTMLElement; - const tag = target.tagName.toLowerCase(); - const isInput = tag === 'input' || tag === 'textarea' || target.isContentEditable; - if (!isInput && !e.shiftKey) { - e.preventDefault(); +async function bootstrap() { + // 加载运行模式配置 + let appEnvironment = 'production'; + try { + const config = await loadAppConfig(); + appEnvironment = config.environment; + } catch { + // 加载失败时默认为生产模式 } -}); + (window as unknown as Record).__APP_ENV__ = appEnvironment; -// 全局快捷键:F12 / Ctrl+Shift+I 打开 DevTools(release 构建备用) -document.addEventListener('keydown', (e) => { - if (e.key === 'F12' || (e.ctrlKey && e.shiftKey && (e.key === 'I' || e.key === 'i'))) { - e.preventDefault(); - import('@tauri-apps/api/core') - .then(({ invoke }) => invoke('open_devtools')) + // 右键菜单:生产模式受限,调试模式开放 + // 生产模式:非输入框/文本区且未按 Shift 时阻止默认右键(不支持刷新) + // 调试模式:不拦截,WebView 默认右键菜单可用(含刷新/检查元素) + document.addEventListener('contextmenu', (e) => { + const env = (window as unknown as Record).__APP_ENV__ as string; + if (env === 'production') { + const target = e.target as HTMLElement; + const tag = target.tagName.toLowerCase(); + const isInput = tag === 'input' || tag === 'textarea' || target.isContentEditable; + if (!isInput && !e.shiftKey) { + e.preventDefault(); + } + } + // 调试模式:不拦截,放行默认右键菜单 + }); + + // 全局快捷键:仅 F12 打开 DevTools + // 生产模式禁用,调试模式开放(macOS 需 Fn+F12) + document.addEventListener('keydown', (e) => { + if (e.key === 'F12') { + const env = (window as unknown as Record).__APP_ENV__ as string; + if (env === 'production') { + e.preventDefault(); + return; + } + e.preventDefault(); + import('@tauri-apps/api/core') + .then(({ invoke }) => invoke('open_devtools')) + .catch(() => { + // 非 Tauri 环境忽略 + }); + } + }); + + const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); + + root.render( + + + + ); + + // 前端渲染完成后,通知 Tauri 显示主窗口 + // 使用 requestIdleCallback 确保首帧已绘制 + const showWindow = () => { + import('@tauri-apps/api/webviewWindow') + .then(({ getCurrentWebviewWindow }) => { + const win = getCurrentWebviewWindow(); + win.show(); + win.setFocus(); + }) .catch(() => { - // 非 Tauri 环境忽略 + // 非 Tauri 环境(如浏览器开发)忽略 }); + }; + + if ('requestIdleCallback' in window) { + requestIdleCallback(showWindow, { timeout: 500 }); + } else { + setTimeout(showWindow, 100); } -}); - -const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); - -root.render( - - - -); - -// 前端渲染完成后,通知 Tauri 显示主窗口 -// 使用 requestIdleCallback 确保首帧已绘制 -const showWindow = () => { - import('@tauri-apps/api/webviewWindow') - .then(({ getCurrentWebviewWindow }) => { - const win = getCurrentWebviewWindow(); - win.show(); - win.setFocus(); - }) - .catch(() => { - // 非 Tauri 环境(如浏览器开发)忽略 - }); -}; - -if ('requestIdleCallback' in window) { - requestIdleCallback(showWindow, { timeout: 500 }); -} else { - setTimeout(showWindow, 100); } + +bootstrap(); diff --git a/tauri-app/src/pages/Settings/AboutUs.tsx b/tauri-app/src/pages/Settings/AboutUs.tsx index e3e1a72..191e00b 100644 --- a/tauri-app/src/pages/Settings/AboutUs.tsx +++ b/tauri-app/src/pages/Settings/AboutUs.tsx @@ -32,9 +32,9 @@ export default function AboutUs() { }, 2000); }, []); - const handleSave = async (env: string, customUrl?: string) => { + const handleSave = async (env: string) => { try { - await saveAppConfig(env, customUrl); + await saveAppConfig(env); // 开发模式下 Vite dev server 重启后无法自动恢复,改用刷新页面 if (import.meta.env.DEV) {