support context menu

This commit is contained in:
Julian Freeman
2026-03-03 13:19:18 -04:00
parent e7030f0be1
commit dbec37cd3d
3 changed files with 128 additions and 0 deletions

View File

@@ -155,6 +155,18 @@ pub fn get_children(parent_path: String, state: &DiskState) -> Vec<FileTreeNode>
results results
} }
pub async fn open_explorer(path: String) -> Result<(), String> {
const CREATE_NO_WINDOW: u32 = 0x08000000;
// 使用 /select, 参数可以在打开目录的同时选中目标
Command::new("explorer.exe")
.arg("/select,")
.arg(&path)
.creation_flags(CREATE_NO_WINDOW)
.spawn()
.map_err(|e| e.to_string())?;
Ok(())
}
// --- 快速模式配置与逻辑 --- // --- 快速模式配置与逻辑 ---
#[derive(Clone)] #[derive(Clone)]

View File

@@ -24,6 +24,11 @@ async fn get_tree_children(path: String, state: State<'_, cleaner::DiskState>) -
Ok(cleaner::get_children(path, &state)) Ok(cleaner::get_children(path, &state))
} }
#[tauri::command]
async fn open_in_explorer(path: String) -> Result<(), String> {
cleaner::open_explorer(path).await
}
// --- 高级清理命令 --- // --- 高级清理命令 ---
#[tauri::command] #[tauri::command]
@@ -54,6 +59,7 @@ pub fn run() {
start_fast_clean, start_fast_clean,
start_full_disk_scan, start_full_disk_scan,
get_tree_children, get_tree_children,
open_in_explorer,
clean_system_components, clean_system_components,
clean_thumbnails, clean_thumbnails,
disable_hibernation disable_hibernation

View File

@@ -1,6 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from "vue"; import { ref } from "vue";
import { invoke } from "@tauri-apps/api/core"; import { invoke } from "@tauri-apps/api/core";
import { openUrl } from "@tauri-apps/plugin-opener";
import pkg from "../package.json"; import pkg from "../package.json";
// --- 导航状态 --- // --- 导航状态 ---
@@ -41,6 +42,55 @@ function showAlert(title: string, message: string, type: 'info' | 'success' | 'e
showModal.value = true; showModal.value = true;
} }
// --- 右键菜单状态 ---
const contextMenu = ref({
show: false,
x: 0,
y: 0,
node: null as FileNode | null
});
function handleContextMenu(e: MouseEvent, node: FileNode) {
e.preventDefault();
contextMenu.value = {
show: true,
x: e.clientX,
y: e.clientY,
node: node
};
// 监听一次性点击以关闭菜单
const close = () => {
contextMenu.value.show = false;
window.removeEventListener('click', close);
};
window.addEventListener('click', close);
}
async function openNodeInExplorer() {
if (contextMenu.value.node) {
try {
await invoke("open_in_explorer", { path: contextMenu.value.node.path });
} catch (err) {
console.error(err);
}
}
}
async function searchNode(type: 'google' | 'perplexity') {
if (contextMenu.value.node) {
const name = contextMenu.value.node.name;
const query = encodeURIComponent(`Windows 文件或目录 ${name} 是做什么用的,我可以删除吗`);
const url = type === 'google'
? `https://www.google.com/search?q=${query}`
: `https://www.perplexity.ai/?q=${query}`;
try {
await openUrl(url);
} catch (err) {
console.error(err);
}
}
}
// 高级模式特有 // 高级模式特有
const expandedAdvanced = ref<string | null>(null); const expandedAdvanced = ref<string | null>(null);
const advLoading = ref<Record<string, boolean>>({}); const advLoading = ref<Record<string, boolean>>({});
@@ -431,6 +481,7 @@ function splitSize(sizeStr: string | number) {
class="tree-row" class="tree-row"
:class="{ 'is-file': !node.is_dir }" :class="{ 'is-file': !node.is_dir }"
:style="{ paddingLeft: (node.level * 20 + 16) + 'px' }" :style="{ paddingLeft: (node.level * 20 + 16) + 'px' }"
@contextmenu="handleContextMenu($event, node)"
> >
<div class="col-name" @click="toggleNode(index)"> <div class="col-name" @click="toggleNode(index)">
<span v-if="node.is_dir" class="node-toggle"> <span v-if="node.is_dir" class="node-toggle">
@@ -462,6 +513,28 @@ function splitSize(sizeStr: string | number) {
</section> </section>
</main> </main>
<!-- 右键菜单 -->
<div
v-if="contextMenu.show"
class="context-menu shadow-card"
:style="{ top: contextMenu.y + 'px', left: contextMenu.x + 'px' }"
@click.stop
>
<div class="menu-item" @click="openNodeInExplorer">
<span class="menu-icon">📂</span>
<span>在文件夹中打开</span>
</div>
<div class="menu-divider"></div>
<div class="menu-item" @click="searchNode('google')">
<span class="menu-icon">🌐</span>
<span> Google 搜索</span>
</div>
<div class="menu-item" @click="searchNode('perplexity')">
<span class="menu-icon">🤖</span>
<span>询问 Perplexity</span>
</div>
</div>
<!-- 自定义弹窗 --> <!-- 自定义弹窗 -->
<div class="modal-overlay" v-if="showModal" @click.self="showModal = false"> <div class="modal-overlay" v-if="showModal" @click.self="showModal = false">
<div class="modal-card" :class="modalType"> <div class="modal-card" :class="modalType">
@@ -952,4 +1025,41 @@ body {
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes modalIn { from { opacity: 0; transform: scale(0.9) translateY(20px); } to { opacity: 1; transform: scale(1) translateY(0); } } @keyframes modalIn { from { opacity: 0; transform: scale(0.9) translateY(20px); } to { opacity: 1; transform: scale(1) translateY(0); } }
/* --- 右键菜单样式 --- */
.context-menu {
position: fixed;
background: white;
min-width: 180px;
border-radius: 12px;
padding: 6px;
z-index: 2000;
border: 1px solid rgba(0,0,0,0.08);
animation: fadeIn 0.1s ease;
}
.menu-item {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 14px;
font-size: 13px;
color: var(--text-main);
cursor: pointer;
border-radius: 8px;
transition: background 0.15s;
}
.menu-item:hover {
background-color: #F2F2F7;
color: var(--primary-color);
}
.menu-icon { font-size: 16px; }
.menu-divider {
height: 1px;
background: #F5F5F7;
margin: 4px 0;
}
</style> </style>