support context menu
This commit is contained in:
@@ -155,6 +155,18 @@ pub fn get_children(parent_path: String, state: &DiskState) -> Vec<FileTreeNode>
|
||||
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)]
|
||||
|
||||
@@ -24,6 +24,11 @@ async fn get_tree_children(path: String, state: State<'_, cleaner::DiskState>) -
|
||||
Ok(cleaner::get_children(path, &state))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn open_in_explorer(path: String) -> Result<(), String> {
|
||||
cleaner::open_explorer(path).await
|
||||
}
|
||||
|
||||
// --- 高级清理命令 ---
|
||||
|
||||
#[tauri::command]
|
||||
@@ -54,6 +59,7 @@ pub fn run() {
|
||||
start_fast_clean,
|
||||
start_full_disk_scan,
|
||||
get_tree_children,
|
||||
open_in_explorer,
|
||||
clean_system_components,
|
||||
clean_thumbnails,
|
||||
disable_hibernation
|
||||
|
||||
110
src/App.vue
110
src/App.vue
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { openUrl } from "@tauri-apps/plugin-opener";
|
||||
import pkg from "../package.json";
|
||||
|
||||
// --- 导航状态 ---
|
||||
@@ -41,6 +42,55 @@ function showAlert(title: string, message: string, type: 'info' | 'success' | 'e
|
||||
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 advLoading = ref<Record<string, boolean>>({});
|
||||
@@ -431,6 +481,7 @@ function splitSize(sizeStr: string | number) {
|
||||
class="tree-row"
|
||||
:class="{ 'is-file': !node.is_dir }"
|
||||
:style="{ paddingLeft: (node.level * 20 + 16) + 'px' }"
|
||||
@contextmenu="handleContextMenu($event, node)"
|
||||
>
|
||||
<div class="col-name" @click="toggleNode(index)">
|
||||
<span v-if="node.is_dir" class="node-toggle">
|
||||
@@ -462,6 +513,28 @@ function splitSize(sizeStr: string | number) {
|
||||
</section>
|
||||
</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-card" :class="modalType">
|
||||
@@ -952,4 +1025,41 @@ body {
|
||||
|
||||
@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); } }
|
||||
|
||||
/* --- 右键菜单样式 --- */
|
||||
.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>
|
||||
Reference in New Issue
Block a user