1+ use std:: { path:: Path , process:: Command } ;
2+
13use anyhow:: Result ;
24use rand:: { Rng , distributions:: Alphanumeric } ;
35use serde:: Deserialize ;
4- use tauri:: { AppHandle , State } ;
6+ use tauri:: { AppHandle , Manager , State } ;
57use tauri_plugin_opener:: OpenerExt ;
68use tracing:: info;
79
810use crate :: {
911 DatabaseManager ,
12+ api:: window_api,
1013 db:: {
1114 launcher,
1215 launcher_resource:: { self , CreateResourceParam , LauncherResource } ,
@@ -283,6 +286,17 @@ pub async fn modify_resource_name(
283286 Ok ( ( ) )
284287}
285288
289+ /// 修改资源路径
290+ #[ tauri:: command]
291+ pub async fn modify_resource_path (
292+ db : State < ' _ , DatabaseManager > ,
293+ resource_id : i64 ,
294+ path : & str ,
295+ ) -> Result < ( ) , OneClickLaunchError > {
296+ launcher_resource:: modify_path ( & db. pool , resource_id, path) . await ?;
297+ Ok ( ( ) )
298+ }
299+
286300/// 删除启动器中的资源
287301#[ tauri:: command]
288302pub async fn delete_resource (
@@ -295,12 +309,26 @@ pub async fn delete_resource(
295309
296310/// 启动启动器
297311#[ tauri:: command]
298- pub async fn launch (
299- app : AppHandle ,
300- db : State < ' _ , DatabaseManager > ,
301- launcher_id : i64 ,
302- ) -> Result < ( ) , OneClickLaunchError > {
303- let resources = launcher_resource:: query_by_launcher_id ( & db. pool , launcher_id) . await ?;
312+ pub async fn launch ( app : AppHandle , launcher_id : i64 ) -> Result < ( ) , OneClickLaunchError > {
313+ let db: State < ' _ , DatabaseManager > = app. try_state ( ) . ok_or (
314+ OneClickLaunchError :: ExecutionError ( "Unable to get DatabaseManager" . to_string ( ) ) ,
315+ ) ?;
316+
317+ let mut resources = launcher_resource:: query_by_launcher_id ( & db. pool , launcher_id) . await ?;
318+
319+ tracing:: debug!( "启动编组原始资源列表: {resources:?}" ) ;
320+
321+ // 必须从启动资源中排除自己,防止出现死循环
322+ let app_path = current_exe_path_str ( ) ?;
323+ resources. retain ( |e| {
324+ // 检查路径是否指向当前应用程序
325+ !e. path . starts_with ( & app_path)
326+ } ) ;
327+
328+ if resources. is_empty ( ) {
329+ tracing:: debug!( "资源列表为空" ) ;
330+ return Ok ( ( ) ) ;
331+ }
304332
305333 launch_resources ( & app, & resources) ;
306334
@@ -342,8 +370,78 @@ pub fn launch_resources(app: &AppHandle, resources: &[LauncherResource]) {
342370/// - `Ok(())` 表示操作成功。
343371/// - `Err(OneClickLaunchError)` 表示操作失败。
344372pub fn open_using_default_program ( app : & AppHandle , path : & str ) -> Result < ( ) , OneClickLaunchError > {
373+ match try_open_as_command ( path) {
374+ Ok ( true ) => return Ok ( ( ) ) , // 已经作为程序执行
375+ Ok ( false ) => {
376+ // 不是程序,交给 opener
377+ open_path_with_opener ( app, path) ?;
378+ }
379+ Err ( e) => {
380+ tracing:: debug!( "作为命令执行失败: {e:?},尝试默认打开" ) ;
381+ open_path_with_opener ( app, path) ?;
382+ }
383+ }
384+
385+ Ok ( ( ) )
386+ }
387+
388+ fn open_path_with_opener ( app : & AppHandle , path : & str ) -> Result < ( ) , OneClickLaunchError > {
345389 app. opener ( )
346390 . open_path ( path, None :: < & str > )
347391 . map_err ( |e| OneClickLaunchError :: ExecutionError ( e. to_string ( ) ) ) ?;
348392 Ok ( ( ) )
349393}
394+
395+ fn try_open_as_command ( path : & str ) -> Result < bool , OneClickLaunchError > {
396+ let parts = shlex:: split ( path)
397+ . ok_or_else ( || OneClickLaunchError :: ExecutionError ( "无法解析路径" . to_string ( ) ) ) ?;
398+
399+ if parts. is_empty ( ) {
400+ return Ok ( false ) ;
401+ }
402+
403+ let program = & parts[ 0 ] ;
404+ let args = & parts[ 1 ..] ;
405+
406+ // 如果第一个部分是存在的文件(.exe/.bat/.sh 等),才当成命令执行
407+ if Path :: new ( program) . exists ( ) {
408+ Command :: new ( program) . args ( args) . spawn ( ) ?;
409+ return Ok ( true ) ;
410+ }
411+
412+ Ok ( false )
413+ }
414+
415+ #[ tauri:: command]
416+ pub async fn create_handler_shortcut (
417+ launcher_id : i64 ,
418+ db : State < ' _ , DatabaseManager > ,
419+ ) -> Result < String , OneClickLaunchError > {
420+ let launcher = launcher:: find_by_id ( & db. pool , launcher_id) . await ?;
421+
422+ let app_path = current_exe_path_str ( ) ?;
423+
424+ // 构建参数
425+ let args = Some ( vec ! [ format!( "launch {}" , launcher_id) ] ) ;
426+
427+ window_api:: create_shortcut (
428+ & app_path,
429+ & launcher. name ,
430+ args,
431+ // None 表示保存到桌面
432+ None ,
433+ )
434+ . map ( |path| path. to_string_lossy ( ) . to_string ( ) )
435+ }
436+
437+ fn current_exe_path_str ( ) -> Result < String , OneClickLaunchError > {
438+ // 获取当前应用程序的绝对路径
439+ let exe_path = std:: env:: current_exe ( ) ?;
440+
441+ // 转换为 Windows 可识别的普通路径
442+ let mut app_path = exe_path. to_string_lossy ( ) . to_string ( ) ;
443+ if app_path. starts_with ( r"\\?\" ) {
444+ app_path = app_path. trim_start_matches ( r"\\?\" ) . to_string ( ) ;
445+ }
446+ Ok ( app_path)
447+ }
0 commit comments