From 1bfa40c841889e2ec5397bf1612fb97892acf154 Mon Sep 17 00:00:00 2001 From: 87owo <85057800+87owo@users.noreply.github.com> Date: Sun, 26 Apr 2026 21:13:42 +0800 Subject: [PATCH] Add files via upload --- Interface/static/js/main.js | 112 +++++++++++++++++++++--------------- PYAS.py | 29 ++++++++-- 2 files changed, 90 insertions(+), 51 deletions(-) diff --git a/Interface/static/js/main.js b/Interface/static/js/main.js index bf5a6e4..581c570 100644 --- a/Interface/static/js/main.js +++ b/Interface/static/js/main.js @@ -1362,15 +1362,6 @@ document.addEventListener('DOMContentLoaded', () => { } else if (oldActive.id === 'repair_window') { const list = document.querySelector('#repair_window .manage-list'); if (list) list.innerHTML = ''; - } else if (oldActive.id === 'scan_window' && !isScanning) { - virusList.innerHTML = ''; - scanMethodSelect.value = 'none'; - scanMethodSelect.classList.remove('hidden'); - stopBtn.classList.add('hidden'); - solveBtn.classList.add('hidden'); - if (progressTitle) progressTitle.textContent = getMsg("病毒掃描"); - progressText.textContent = getMsg("此選項可以選擇掃描方式"); - if (window.pywebview) window.pywebview.api.stop_scan(); } } @@ -1437,50 +1428,83 @@ document.addEventListener('DOMContentLoaded', () => { const progressTitle = document.querySelector('#scan_window .section-title'); const virusList = document.getElementById('virus_list'); + const virusState = new Map(); + window.addVirusResult = (label, path) => { - const li = document.createElement('li'); - li.className = 'manage-list-item'; - li.innerHTML = ` - -
${label} | ${path}
- `; - li.addEventListener('click', (e) => { - if (e.target.tagName !== 'INPUT') { - const cb = li.querySelector('input'); - cb.checked = !cb.checked; - } - }); + if (!virusState.has(path)) { + virusState.set(path, true); + const li = document.createElement('li'); + li.className = 'manage-list-item'; + li.dataset.path = path; + li.innerHTML = ` + +
${label} | ${path}
+ `; + virusList.appendChild(li); + } + }; + + virusList.addEventListener('click', (e) => { + const li = e.target.closest('.manage-list-item'); + if (!li) return; - li.addEventListener('contextmenu', (e) => { - e.preventDefault(); - if (window.pywebview) { - window.pywebview.api.prompt_virus_action(path).then(action => { - if (action === '1') { - window.pywebview.api.manage_named_list('white_list', [path], 'add').then(() => li.remove()); - } else if (action === '2') { - window.pywebview.api.manage_named_list('quarantine', [path], 'add').then(() => li.remove()); - } + const cb = li.querySelector('input'); + if (e.target.tagName !== 'INPUT') { + cb.checked = !cb.checked; + } + virusState.set(li.dataset.path, cb.checked); + }); + + virusList.addEventListener('contextmenu', (e) => { + e.preventDefault(); + const li = e.target.closest('.manage-list-item'); + if (!li || !window.pywebview) return; + + const path = li.dataset.path; + window.pywebview.api.prompt_virus_action(path).then(action => { + if (action === '1') { + window.pywebview.api.manage_named_list('white_list', [path], 'add').then(() => { + window.pywebview.api.remove_virus_result([path]).then(() => { + virusState.delete(path); + li.remove(); + if (virusState.size === 0) solveBtn.classList.add('hidden'); + }); + }); + } else if (action === '2') { + window.pywebview.api.manage_named_list('quarantine', [path], 'add').then(() => { + window.pywebview.api.remove_virus_result([path]).then(() => { + virusState.delete(path); + li.remove(); + if (virusState.size === 0) solveBtn.classList.add('hidden'); + }); }); } }); - virusList.appendChild(li); - }; + }); solveBtn?.addEventListener('click', () => { if (!window.pywebview) return; - const cbs = Array.from(virusList.querySelectorAll('input[type="checkbox"]:checked')); - const paths = cbs.map(cb => cb.value); + + const paths = Array.from(virusState.entries()) + .filter(([_, isChecked]) => isChecked) + .map(([path, _]) => path); + if (paths.length === 0) return; solveBtn.disabled = true; window.pywebview.api.solve_scan(paths).then((deletedPaths) => { solveBtn.disabled = false; - cbs.forEach(cb => { - if (deletedPaths.includes(cb.value)) { - cb.parentElement.remove(); + + const deletedSet = new Set(deletedPaths); + deletedPaths.forEach(p => virusState.delete(p)); + + Array.from(virusList.children).forEach(li => { + if (deletedSet.has(li.dataset.path)) { + li.remove(); } }); - if (virusList.children.length === 0) solveBtn.classList.add('hidden'); + + if (virusState.size === 0) solveBtn.classList.add('hidden'); }); }); @@ -1500,16 +1524,18 @@ document.addEventListener('DOMContentLoaded', () => { progressText.textContent = msg; stopBtn.classList.add('hidden'); scanMethodSelect.classList.remove('hidden'); - scanMethodSelect.value = 'none'; + updateCustomSelectUI('scan_method_select', 'none'); if (count > 0) solveBtn.classList.remove('hidden'); }; function triggerScan(method) { if (!window.pywebview) return; isScanning = true; + virusState.clear(); virusList.innerHTML = ''; scanMethodSelect.classList.add('hidden'); stopBtn.classList.remove('hidden'); + stopBtn.disabled = false; solveBtn.classList.add('hidden'); if (progressTitle) progressTitle.textContent = getMsg("正在掃描"); progressText.textContent = getMsg("正在初始化中"); @@ -1524,12 +1550,7 @@ document.addEventListener('DOMContentLoaded', () => { stopBtn?.addEventListener('click', () => { if (window.pywebview) window.pywebview.api.stop_scan(); - isScanning = false; - stopBtn.classList.add('hidden'); - scanMethodSelect.classList.remove('hidden'); - scanMethodSelect.value = 'none'; - if (progressTitle) progressTitle.textContent = getMsg("病毒掃描"); - progressText.textContent = getMsg("停止"); + stopBtn.disabled = true; }); document.getElementById('theme_select').addEventListener('change', (e) => { @@ -1567,6 +1588,7 @@ document.addEventListener('DOMContentLoaded', () => { switchPage('scan_window'); if (window.pywebview) { isScanning = true; + virusState.clear(); virusList.innerHTML = ''; scanMethodSelect.classList.add('hidden'); stopBtn.classList.remove('hidden'); diff --git a/PYAS.py b/PYAS.py index a075596..d9caa7d 100644 --- a/PYAS.py +++ b/PYAS.py @@ -1002,7 +1002,15 @@ class WindowAPI: deleted_paths = [] running_procs = self.get_process_list() + proc_map = {} + for proc in running_procs: + if proc["path"] and proc["path"] != "None": + norm_p = self.norm_path(proc["path"], must_exist=False) + if norm_p: + proc_map.setdefault(os.path.normcase(norm_p), []).append(proc["pid"]) + with self.lock_virus: + deleted_set = set() for raw_path in file_paths: path = self.norm_path(raw_path, must_exist=False) if not path: @@ -1015,9 +1023,10 @@ class WindowAPI: if path in self.virus_lock: self.lock_file(path, False) - for proc in running_procs: - if self.path_equal(proc["path"], path): - self.kill_process(proc["pid"]) + path_key = os.path.normcase(path) + if path_key in proc_map: + for pid in proc_map[path_key]: + self.kill_process(pid) try: os.chmod(path, stat.S_IWRITE) @@ -1026,16 +1035,18 @@ class WindowAPI: self.remove_list_items("quarantine", [path]) os.remove(path) - deleted_paths.append(raw_path) - if path in self.virus_results: - self.virus_results.remove(path) + deleted_paths.append(raw_path) + deleted_set.add(path) self.write_log("INFO", "Virus Delete", source=path, file_hash=self.calc_file_hash(path), operate=True, success=True) except Exception as e: self.write_log("SCAN", "Virus Delete", source=path, file_hash=self.calc_file_hash(path), detail=str(e), operate=True, success=False) + if deleted_set: + self.virus_results = [p for p in self.virus_results if p not in deleted_set] + remaining = len(self.virus_results) with self.lock_config: @@ -1060,6 +1071,12 @@ class WindowAPI: self._window.evaluate_js(f"if(window.finishScan) window.finishScan({json.dumps(result_msg)}, {remaining});") return deleted_paths + def remove_virus_result(self, paths): + with self.lock_virus: + norm_paths = set(self.norm_path(p, must_exist=False) for p in paths) + self.virus_results = [p for p in self.virus_results if p not in norm_paths] + return len(self.virus_results) + #################################################################################################### def trigger_scan(self, method):