From 4d26310666dd749b78a8a362caacc039a3916044 Mon Sep 17 00:00:00 2001 From: zhanglei <383094403@qq.com> Date: Sun, 6 Sep 2026 09:54:35 +0800 Subject: [PATCH 1/2] =?UTF-8?q?Update=20plugin=20=E6=9D=80=E8=BF=9B?= =?UTF-8?q?=E7=A8=8B/=E8=BF=9B=E7=A8=8B=E7=AE=A1=E7=90=86=20v1.0.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - init - 添加端口查询 - 修改版本号 --- plugins/ztools-kill-process/app.js | 137 +++++++++++++++++++---- plugins/ztools-kill-process/index.css | 47 +++++++- plugins/ztools-kill-process/index.html | 6 +- plugins/ztools-kill-process/plugin.json | 2 +- plugins/ztools-kill-process/preload.js | 140 +++++++++++++++++++++++- 5 files changed, 304 insertions(+), 28 deletions(-) diff --git a/plugins/ztools-kill-process/app.js b/plugins/ztools-kill-process/app.js index 8dbe3ead7..d873ee7fe 100644 --- a/plugins/ztools-kill-process/app.js +++ b/plugins/ztools-kill-process/app.js @@ -15,6 +15,7 @@ document.addEventListener('DOMContentLoaded', () => { // Header click sort handlers const thName = document.getElementById('thName'); const thPID = document.getElementById('thPID'); + const thPort = document.getElementById('thPort'); const thMem = document.getElementById('thMem'); // Modal Elements @@ -88,11 +89,11 @@ document.addEventListener('DOMContentLoaded', () => { // Resolution of process service from preload const processService = window.services || window.processService || { getProcesses: async () => [ - { name: 'chrome.exe', pid: 12100, memoryStr: '132.3 MB', memoryKB: 135480 }, - { name: 'Antigravity.exe', pid: 65176, memoryStr: '252.4 MB', memoryKB: 258488 }, - { name: 'ZTools.exe', pid: 56044, memoryStr: '91.9 MB', memoryKB: 94092 }, - { name: 'WeChatAppEx.exe', pid: 54964, memoryStr: '133.1 MB', memoryKB: 136308 }, - { name: 'Unity.exe', pid: 28976, memoryStr: '0.96 GB', memoryKB: 1002752 } + { name: 'chrome.exe', pid: 12100, memoryStr: '132.3 MB', memoryKB: 135480, listeningPorts: [8080, 8081], allPorts: [8080, 8081], primaryPort: 8080, portsStr: ':8080, :8081' }, + { name: 'Antigravity.exe', pid: 65176, memoryStr: '252.4 MB', memoryKB: 258488, listeningPorts: [], allPorts: [54321], primaryPort: 54321, portsStr: ':54321' }, + { name: 'ZTools.exe', pid: 56044, memoryStr: '91.9 MB', memoryKB: 94092, listeningPorts: [], allPorts: [], primaryPort: Infinity, portsStr: '' }, + { name: 'WeChatAppEx.exe', pid: 54964, memoryStr: '133.1 MB', memoryKB: 136308, listeningPorts: [], allPorts: [], primaryPort: Infinity, portsStr: '' }, + { name: 'Unity.exe', pid: 28976, memoryStr: '0.96 GB', memoryKB: 1002752, listeningPorts: [3000], allPorts: [3000], primaryPort: 3000, portsStr: ':3000' } ], killProcess: async (pid) => `Mock killed process PID ${pid}` }; @@ -120,12 +121,20 @@ document.addEventListener('DOMContentLoaded', () => { }, 3000); } + let isFetchingProcesses = false; + let searchDebounceTimer = null; + // Load and refresh process list - async function loadProcesses(keepIndex = true) { + async function loadProcesses(keepIndex = true, silent = false) { + if (isFetchingProcesses) return; + isFetchingProcesses = true; + adjustPluginHeight(580); - loadingState.style.display = 'flex'; - emptyState.style.display = 'none'; - processListEl.style.display = 'none'; + if (!silent) { + loadingState.style.display = 'flex'; + emptyState.style.display = 'none'; + processListEl.style.display = 'none'; + } try { allProcesses = await processService.getProcesses(); @@ -141,21 +150,44 @@ document.addEventListener('DOMContentLoaded', () => { applyFilterAndSort(keepIndex); } catch (err) { console.error('Failed to load processes:', err); - showToast('获取进程列表失败: ' + (err.message || '未知错误'), 'error'); + if (!silent) { + showToast('获取进程列表失败: ' + (err.message || '未知错误'), 'error'); + } } finally { - loadingState.style.display = 'none'; - processListEl.style.display = 'block'; - if (searchInput && !isModalOpen) { + isFetchingProcesses = false; + if (!silent) { + loadingState.style.display = 'none'; + processListEl.style.display = 'block'; + } + if (searchInput && !isModalOpen && document.activeElement !== searchInput) { searchInput.focus(); } } } + // Window Focus & Plugin Enter Lifecycle Events for Auto-Refresh + window.onPluginEnter = function(action) { + loadProcesses(true, allProcesses.length > 0); + }; + + window.addEventListener('focus', () => { + if (!isModalOpen && !isFetchingProcesses) { + loadProcesses(true, true); + } + }); + + document.addEventListener('visibilitychange', () => { + if (!document.hidden && !isModalOpen && !isFetchingProcesses) { + loadProcesses(true, true); + } + }); + // Update header sort direction icons function updateHeaderSortIcons() { const headers = [ { el: thName, field: 'name' }, { el: thPID, field: 'pid' }, + { el: thPort, field: 'primaryPort' }, { el: thMem, field: 'memoryKB' } ]; @@ -209,18 +241,36 @@ document.addEventListener('DOMContentLoaded', () => { // Filter and Sort logic function applyFilterAndSort(keepIndex = false) { - const query = searchInput.value.trim().toLowerCase(); + const rawQuery = searchInput.value.trim().toLowerCase(); // Filter - if (!query) { + if (!rawQuery) { filteredProcesses = [...allProcesses]; searchClear.style.display = 'none'; } else { searchClear.style.display = 'flex'; + + // Clean query for port matching (e.g. ":8080", "port:8080", "port=8080" -> "8080") + const cleanPortQuery = rawQuery.replace(/^port\s*[:=]?\s*/i, '').replace(/^[:]/, '').trim(); + filteredProcesses = allProcesses.filter(p => { - const nameMatch = p.name.toLowerCase().includes(query); - const pidMatch = p.pid.toString().includes(query); - return nameMatch || pidMatch; + const nameMatch = p.name.toLowerCase().includes(rawQuery); + const pidMatch = p.pid.toString().includes(rawQuery); + + let portMatch = false; + if (cleanPortQuery) { + const listeningPorts = p.listeningPorts || []; + const allPorts = p.allPorts || []; + const portsStr = p.portsStr || ''; + + const hasListeningMatch = listeningPorts.some(port => port.toString().includes(cleanPortQuery)); + const hasAllMatch = allPorts.some(port => port.toString().includes(cleanPortQuery)); + const hasStrMatch = portsStr.toLowerCase().includes(rawQuery) || portsStr.toLowerCase().includes(cleanPortQuery); + + portMatch = hasListeningMatch || hasAllMatch || hasStrMatch; + } + + return nameMatch || pidMatch || portMatch; }); } @@ -229,6 +279,9 @@ document.addEventListener('DOMContentLoaded', () => { let valA = a[currentSort.field]; let valB = b[currentSort.field]; + if (valA === undefined || valA === null) valA = currentSort.field === 'primaryPort' ? Infinity : ''; + if (valB === undefined || valB === null) valB = currentSort.field === 'primaryPort' ? Infinity : ''; + if (typeof valA === 'string') valA = valA.toLowerCase(); if (typeof valB === 'string') valB = valB.toLowerCase(); @@ -251,6 +304,36 @@ document.addEventListener('DOMContentLoaded', () => { updateBatchKillState(); } + // Render Port Badges Helper + function renderPortBadges(proc) { + const listening = proc.listeningPorts || []; + const all = proc.allPorts || []; + + if (listening.length > 0) { + const showPorts = listening.slice(0, 2); + const extraCount = listening.length - showPorts.length; + const fullTooltip = `监听端口: ${listening.map(p => ':' + p).join(', ')}`; + + let html = showPorts.map(p => `:${p}`).join(' '); + if (extraCount > 0) { + html += ` +${extraCount}`; + } + return html; + } else if (all.length > 0) { + const showPorts = all.slice(0, 2); + const extraCount = all.length - showPorts.length; + const fullTooltip = `活跃连接端口: ${all.map(p => ':' + p).join(', ')}`; + + let html = showPorts.map(p => `:${p}`).join(' '); + if (extraCount > 0) { + html += ` +${extraCount}`; + } + return html; + } + + return `-`; + } + // Render Process Items function renderList() { processListEl.innerHTML = ''; @@ -268,6 +351,8 @@ document.addEventListener('DOMContentLoaded', () => { item.className = `process-item ${index === selectedIndex ? 'selected' : ''} ${isChecked ? 'checked-row' : ''}`; item.setAttribute('data-index', index); + const portHtml = renderPortBadges(proc); + item.innerHTML = `
@@ -288,6 +373,7 @@ document.addEventListener('DOMContentLoaded', () => { ${escapeHtml(proc.name)}
${proc.pid}
+
${portHtml}
${escapeHtml(proc.memoryStr)}
+
+ 占用端口: + ${escapeHtml(proc.portsStr || '无')} +
内存占用: ${escapeHtml(proc.memoryStr)} @@ -459,7 +549,7 @@ document.addEventListener('DOMContentLoaded', () => { let html = selectedProcs.map(p => ` `).join(''); @@ -647,12 +737,20 @@ document.addEventListener('DOMContentLoaded', () => { // Search Input Event searchInput.addEventListener('input', () => { + // 1. Instantly filter existing cached processes for fast response applyFilterAndSort(false); + + // 2. Debounce (300ms) background refresh from OS to catch newly launched processes/ports + if (searchDebounceTimer) clearTimeout(searchDebounceTimer); + searchDebounceTimer = setTimeout(() => { + loadProcesses(true, true); + }, 300); }); searchClear.addEventListener('click', () => { searchInput.value = ''; applyFilterAndSort(false); + loadProcesses(true, true); searchInput.focus(); }); @@ -674,6 +772,7 @@ document.addEventListener('DOMContentLoaded', () => { thName.addEventListener('click', () => toggleSort('name', false)); thPID.addEventListener('click', () => toggleSort('pid', false)); + if (thPort) thPort.addEventListener('click', () => toggleSort('primaryPort', false)); thMem.addEventListener('click', () => toggleSort('memoryKB', true)); // Modal Mouse Click Handlers diff --git a/plugins/ztools-kill-process/index.css b/plugins/ztools-kill-process/index.css index 831dab583..2119824d1 100644 --- a/plugins/ztools-kill-process/index.css +++ b/plugins/ztools-kill-process/index.css @@ -434,7 +434,7 @@ input[type="checkbox"]:indeterminate::before { /* List Table Header */ .table-header { display: grid; - grid-template-columns: 36px minmax(180px, 3fr) 100px 140px 90px; + grid-template-columns: 36px minmax(140px, 3fr) 75px 130px 90px 65px; padding: 10px 16px; background-color: var(--bg-header); border-bottom: 1px solid var(--border-color); @@ -504,7 +504,7 @@ input[type="checkbox"]:indeterminate::before { .process-item { display: grid; - grid-template-columns: 36px minmax(180px, 3fr) 100px 140px 90px; + grid-template-columns: 36px minmax(140px, 3fr) 75px 130px 90px 65px; align-items: center; padding: 9px 12px; margin-bottom: 4px; @@ -556,6 +556,49 @@ input[type="checkbox"]:indeterminate::before { font-size: 12px; } +.port-cell { + display: flex; + align-items: center; + gap: 4px; + flex-wrap: nowrap; + overflow: hidden; +} + +.port-badge { + background-color: rgba(16, 185, 129, 0.12); + color: var(--accent-green); + border: 1px solid rgba(16, 185, 129, 0.25); + border-radius: var(--radius-sm); + padding: 1px 5px; + font-family: var(--font-mono); + font-size: 11px; + font-weight: 600; + white-space: nowrap; +} + +.port-badge-active { + background-color: rgba(59, 130, 246, 0.12); + color: var(--accent-blue); + border-color: rgba(59, 130, 246, 0.25); +} + +.port-badge-more { + background-color: var(--bg-surface-hover); + color: var(--text-muted); + border: 1px solid var(--border-color); + border-radius: var(--radius-sm); + padding: 1px 4px; + font-family: var(--font-mono); + font-size: 10px; +} + +.no-port { + color: var(--text-muted); + font-family: var(--font-mono); + font-size: 12px; + opacity: 0.5; +} + .mem-cell { font-family: var(--font-mono); font-weight: 600; diff --git a/plugins/ztools-kill-process/index.html b/plugins/ztools-kill-process/index.html index 75b42e3e3..186b66df3 100644 --- a/plugins/ztools-kill-process/index.html +++ b/plugins/ztools-kill-process/index.html @@ -29,7 +29,7 @@

进程管理与结束工具

- +
@@ -70,6 +70,10 @@

进程管理与结束工具

PID
+
+ 占用端口 + +
内存占用 diff --git a/plugins/ztools-kill-process/plugin.json b/plugins/ztools-kill-process/plugin.json index 0e22555b6..9e41d938a 100644 --- a/plugins/ztools-kill-process/plugin.json +++ b/plugins/ztools-kill-process/plugin.json @@ -3,7 +3,7 @@ "title": "杀进程/进程管理", "pluginName": "杀进程/进程管理", "description": "显示当前系统的进程列表,快速搜索与一键结束指定进程", - "version": "1.0.0", + "version": "1.0.1", "author": "zhanglei", "main": "index.html", "preload": "preload.js", diff --git a/plugins/ztools-kill-process/preload.js b/plugins/ztools-kill-process/preload.js index e4494ddcc..e761108b4 100644 --- a/plugins/ztools-kill-process/preload.js +++ b/plugins/ztools-kill-process/preload.js @@ -2,20 +2,37 @@ console.log('ztools-kill-process preload.js loaded!'); const ztools = window.ztools || window.utools || {}; +function notifyPluginEnter(action) { + if (typeof window.onPluginEnter === 'function') { + try { + window.onPluginEnter(action); + } catch (e) { + console.error('Error in onPluginEnter:', e); + } + } +} + window.exports = { 'kill-process': { mode: 'none', args: { - enter() { + enter(action) { if (ztools.setExpendHeight) ztools.setExpendHeight(580); if (ztools.setExploresHeight) ztools.setExploresHeight(580); if (ztools.showMainWindow) ztools.showMainWindow(); + notifyPluginEnter(action); }, leave() {} } } }; +if (ztools && typeof ztools.onPluginEnter === 'function') { + ztools.onPluginEnter((action) => { + notifyPluginEnter(action); + }); +} + // Memory formatter helper function formatMemory(memKB) { if (!memKB || isNaN(memKB) || memKB <= 0) return '0 KB'; @@ -28,6 +45,93 @@ function formatMemory(memKB) { } } +// Port scanner helper for Windows using netstat -ano +function getPortsWin32() { + return new Promise((resolve) => { + const { exec } = require('child_process'); + exec('netstat -ano', { encoding: 'utf8', maxBuffer: 10 * 1024 * 1024, timeout: 5000 }, (err, stdout) => { + if (err || !stdout) return resolve({}); + const lines = stdout.split('\r\n'); + const portMap = {}; // pid -> { listening: Set, all: Set } + for (const line of lines) { + const parts = line.trim().split(/\s+/); + if (parts.length >= 4 && (parts[0] === 'TCP' || parts[0] === 'UDP')) { + const proto = parts[0]; + const localAddr = parts[1]; + const pidStr = parts[parts.length - 1]; + const pid = parseInt(pidStr, 10); + if (!isNaN(pid) && pid > 0) { + const lastColon = localAddr.lastIndexOf(':'); + if (lastColon !== -1) { + const port = parseInt(localAddr.substring(lastColon + 1), 10); + if (!isNaN(port) && port > 0) { + if (!portMap[pid]) { + portMap[pid] = { listening: new Set(), all: new Set() }; + } + portMap[pid].all.add(port); + const isListening = (proto === 'TCP' && parts.includes('LISTENING')) || proto === 'UDP'; + if (isListening) { + portMap[pid].listening.add(port); + } + } + } + } + } + } + const result = {}; + for (const pid in portMap) { + result[pid] = { + listening: Array.from(portMap[pid].listening).sort((a, b) => a - b), + all: Array.from(portMap[pid].all).sort((a, b) => a - b) + }; + } + resolve(result); + }); + }); +} + +// Port scanner helper for macOS / Linux using lsof +function getPortsPosix() { + return new Promise((resolve) => { + const { exec } = require('child_process'); + exec('lsof -i -P -n', { encoding: 'utf8', maxBuffer: 10 * 1024 * 1024, timeout: 5000 }, (err, stdout) => { + if (err || !stdout) return resolve({}); + const lines = stdout.split('\n'); + const portMap = {}; + for (let i = 1; i < lines.length; i++) { + const parts = lines[i].trim().split(/\s+/); + if (parts.length >= 9) { + const pid = parseInt(parts[1], 10); + const nameField = parts[8] || parts[parts.length - 1]; + if (!isNaN(pid) && nameField) { + const match = nameField.match(/:(\d+)(?:->|$)/); + if (match) { + const port = parseInt(match[1], 10); + if (!isNaN(port) && port > 0) { + if (!portMap[pid]) { + portMap[pid] = { listening: new Set(), all: new Set() }; + } + portMap[pid].all.add(port); + if (lines[i].includes('LISTEN') || lines[i].includes('(LISTEN)')) { + portMap[pid].listening.add(port); + } + } + } + } + } + } + const result = {}; + for (const pid in portMap) { + result[pid] = { + listening: Array.from(portMap[pid].listening).sort((a, b) => a - b), + all: Array.from(portMap[pid].all).sort((a, b) => a - b) + }; + } + resolve(result); + }); + }); +} + window.services = { getProcesses() { return new Promise((resolve, reject) => { @@ -35,6 +139,8 @@ window.services = { const os = require('os'); const platform = os.platform(); + const portsPromise = platform === 'win32' ? getPortsWin32() : getPortsPosix(); + if (platform === 'win32') { const psScript = ` Get-Process | ForEach-Object { @@ -48,12 +154,13 @@ Get-Process | ForEach-Object { const encoded = Buffer.from(psScript, 'utf16le').toString('base64'); - execFile('powershell.exe', ['-NoProfile', '-EncodedCommand', encoded], { encoding: 'utf8', timeout: 5000, maxBuffer: 20 * 1024 * 1024 }, (err, stdout) => { + execFile('powershell.exe', ['-NoProfile', '-EncodedCommand', encoded], { encoding: 'utf8', timeout: 5000, maxBuffer: 20 * 1024 * 1024 }, async (err, stdout) => { if (err) return reject(err); try { const rawItems = JSON.parse(stdout || '[]'); const items = Array.isArray(rawItems) ? rawItems : [rawItems]; + const portMap = await portsPromise.catch(() => ({})); const list = []; for (const item of items) { @@ -70,11 +177,22 @@ Get-Process | ForEach-Object { const name = rawName ? `${rawName}.exe` : `PID-${pid}`; const memoryKB = Math.round((item.WorkingSet64 || 0) / 1024); + const portsInfo = portMap[pid] || { listening: [], all: [] }; + const listeningPorts = portsInfo.listening || []; + const allPorts = portsInfo.all || []; + const displayPorts = listeningPorts.length > 0 ? listeningPorts : allPorts; + const primaryPort = displayPorts.length > 0 ? displayPorts[0] : 999999; + const portsStr = displayPorts.map(p => `:${p}`).join(', '); + list.push({ name, pid, memoryStr: formatMemory(memoryKB), - memoryKB + memoryKB, + listeningPorts, + allPorts, + primaryPort, + portsStr }); } @@ -85,9 +203,10 @@ Get-Process | ForEach-Object { }); } else { // macOS / Linux - execFile('ps', ['-ax', '-o', 'pid,rss,comm'], { encoding: 'utf8', maxBuffer: 20 * 1024 * 1024, timeout: 5000 }, (err, stdout) => { + execFile('ps', ['-ax', '-o', 'pid,rss,comm'], { encoding: 'utf8', maxBuffer: 20 * 1024 * 1024, timeout: 5000 }, async (err, stdout) => { if (err) return reject(err); const lines = stdout.split('\n').slice(1); + const portMap = await portsPromise.catch(() => ({})); const list = []; for (const line of lines) { const parts = line.trim().split(/\s+/); @@ -98,11 +217,22 @@ Get-Process | ForEach-Object { if (!isNaN(pid) && comm) { const name = comm.split('/').pop(); if (name.toLowerCase().includes('memory compression')) continue; + const portsInfo = portMap[pid] || { listening: [], all: [] }; + const listeningPorts = portsInfo.listening || []; + const allPorts = portsInfo.all || []; + const displayPorts = listeningPorts.length > 0 ? listeningPorts : allPorts; + const primaryPort = displayPorts.length > 0 ? displayPorts[0] : 999999; + const portsStr = displayPorts.map(p => `:${p}`).join(', '); + list.push({ name, pid, memoryStr: formatMemory(rssKB), - memoryKB: rssKB + memoryKB: rssKB, + listeningPorts, + allPorts, + primaryPort, + portsStr }); } } From c6f4386f94d093595c85bf103f184b39a7ca45e9 Mon Sep 17 00:00:00 2001 From: zhanglei <383094403@qq.com> Date: Sun, 6 Sep 2026 10:35:17 +0800 Subject: [PATCH 2/2] =?UTF-8?q?Update=20plugin=20=E6=9D=80=E8=BF=9B?= =?UTF-8?q?=E7=A8=8B/=E8=BF=9B=E7=A8=8B=E7=AE=A1=E7=90=86=20v1.0.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - init - 添加端口查询 - 修改版本号 - Add ReadME --- plugins/ztools-kill-process/README.md | 63 +++++++++++++++++---------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/plugins/ztools-kill-process/README.md b/plugins/ztools-kill-process/README.md index 5b480b7fc..430c6ef28 100644 --- a/plugins/ztools-kill-process/README.md +++ b/plugins/ztools-kill-process/README.md @@ -1,6 +1,5 @@ -# ⚡ 进程管理与结束工具 (Ztools / uTools 插件) - -一款轻量、极速、高交互性的 Windows 进程管理与一键结束插件。专门为 Ztools / uTools 开发者及高效办公用户打造,提供秒级进程检索、内存监控、键盘优先操作与批量进程杀死功能。 +# ⚡ 进程管理与结束工具 +一款轻量、极速、高交互性的 Windows / 跨平台进程管理与一键结束插件,提供秒级进程检索、网络端口占用定位、内存监控、键盘优先操作与批量进程强制结束功能。 ![插件界面预览](logo.png) @@ -8,38 +7,58 @@ ## ✨ 核心特性 -- ⚡ **毫秒级极速响应**:基于底层 PowerShell 结构化指令优化,单次枚举进程仅需不到 100ms,绝不卡顿。 -- 🎨 **主题自动跟随系统**:原生支持 Windows / OS 系统深色(Dark)与浅色(Light)模式,支持宿主软件主题无缝自动切换。 -- 📦 **自动内存格式化**:智能换算进程内存占用(KB / MB / GB),数据直观易读。 -- 🎯 **智能过滤与安全防护**:自动过滤 `Memory Compression` 及 `Idle` 等系统内核级别进程,防止误杀导致系统蓝屏。 -- 🔍 **实时搜索与点击排序**:支持按进程名称(如 `chrome`)或 `PID` 实时搜索;点击表头可自由按名称、PID、内存大小进行升序/降序排列。 -- 📑 **多选与批量结束**: +- ⚡ **毫秒级极速响应**:基于底层 PowerShell 结构化指令与系统原生 API 优化,单次枚举数百个进程仅需不到 100ms,卡顿全无。 +- 🌐 **网络端口占用检索**: + - 自动抓取并关联进程占用的 TCP/UDP 端口(支持区分**监听端口**与**活跃连接**)。 + - 支持直接搜索端口号(如 `8080`、`:3000` 或 `port:5432`),秒级找出占用端口的后端或前端开发服务。 + - 悬浮可查看完整端口列表,点击表头可按「占用端口」进行升降序排列。 +- 🎨 **主题自动跟随系统**:原生支持 Windows / macOS 系统深色(Dark)与浅色(Light)模式,无缝匹配宿主软件主题。 +- 📦 **自动内存格式化**:智能换算进程内存占用(KB / MB / GB),实时可视化对比高占用进程。 +- 🎯 **智能过滤与安全防护**:自动过滤 `Memory Compression` 及 `Idle` 等内核级系统进程,防止误杀导致系统蓝屏崩溃。 +- 🔍 **多维实时搜索与表头排序**: + - 支持按 **进程名称**(如 `chrome`)、**PID** 或 **占用端口** 实时模糊匹配。 + - 点击表头自由按进程名称、PID、占用端口、内存大小进行升序/降序排列。 +- 📑 **灵活多选与批量结束**: - 支持复选框勾选、`Shift` 范围框选、`Ctrl` 点选及 `Ctrl+A` 全选。 - - 提供安全的批量确认弹窗与清单明细展示,支持一键批量强制结束(Taskkill /F)。 + - 提供安全确认弹窗与清单明细展示(包含 PID、端口及内存信息),支持一键批量强制结束(Taskkill /F)。 - ⌨️ **键盘优先高效交互**: - - 打开即自动聚焦搜索框,输入随查随选。 - - 支持键盘上下方向键选择、`Enter` 触发杀死弹窗、`Esc` 快速撤销/清空。 + - 打开插件自动聚焦搜索框,随输随查。 + - 支持全键盘方向键导航、空格键勾选、回车确认结束、Esc 清空/取消。 --- -## ⌨️ 快捷键指南 +## ⌨️ 快捷键与交互指南 -| 快捷键 | 功能说明 | +| 操作 / 快捷键 | 功能说明 | | :--- | :--- | | `↑` / `↓` | 在进程列表中上下切换当前选中行 | -| `Space` (空格) | 快速勾选/取消勾选当前选中的进程 | +| `Space` (空格) | 快速勾选 / 取消勾选当前选中的进程 | | `Shift` + 鼠标点击 | 快速进行范围多选 | -| `Ctrl` / `Cmd` + 鼠标点击 | 自由多选/取消多选指定的进程行 | -| `Ctrl + A` | 一键全选当前搜索过滤结果中的所有进程 | -| `Enter` | 触发单进程结束或批量结束确认弹窗(弹窗内按 `Enter` 确认杀死) | -| `Esc` | 弹窗中取消关闭 / 输入框中清空搜索 / 列表中取消全选 | -| `F5` / `Ctrl + R` | 强制刷新系统进程列表 | +| `Ctrl` / `Cmd` + 鼠标点击 | 自由多选 / 取消多选指定的进程行 | +| `Ctrl + A` | 一键全选当前过滤结果中的所有进程 | +| `鼠标双击` / `Enter` | 触发单进程结束或批量结束确认弹窗(弹窗内按 `Enter` 确认杀死) | +| `Esc` | 弹窗中取消关闭 / 输入框中清空搜索 / 列表中取消勾选全选 | +| `F5` / `Ctrl + R` | 强制刷新系统进程列表与端口占用 | + +--- + +## 💡 典型使用场景 + +### 1. 解决开发端口冲突 (`EADDRINUSE`) +当启动 Node.js, Spring Boot, Vue / React 等项目提示 `Port 8080 is already in use` 时: +1. 输入 `kill` 或 `8080` 唤起插件; +2. 输入 `:8080` 或 `8080`,瞬间定位占用该端口的进程; +3. 按 `Enter` 键直接强制杀死进程,快速释放端口。 + +### 2. 批量清理高内存占用进程 +1. 点击表头 **内存占用** 排序,找到高资源占用进程; +2. 按 `Ctrl` 依次选中多个无需使用的子进程或浏览器进程; +3. 点击 **批量结束** 或按 `Enter` 一键批量释放系统内存。 --- -## 🚀 安装与使用 +## 🚀 唤起关键词 -### 触发关键词 在 Ztools / uTools 搜索框中输入以下任意关键词即可唤起: - `kill` - `进程`