diff --git a/app/amazon/__pycache__/approve.cpython-39.pyc b/app/amazon/__pycache__/approve.cpython-39.pyc index fb67c01..694a2d5 100644 Binary files a/app/amazon/__pycache__/approve.cpython-39.pyc and b/app/amazon/__pycache__/approve.cpython-39.pyc differ diff --git a/app/amazon/__pycache__/del_brand.cpython-39.pyc b/app/amazon/__pycache__/del_brand.cpython-39.pyc index 7f3c49b..1e620db 100644 Binary files a/app/amazon/__pycache__/del_brand.cpython-39.pyc and b/app/amazon/__pycache__/del_brand.cpython-39.pyc differ diff --git a/app/amazon/__pycache__/main.cpython-39.pyc b/app/amazon/__pycache__/main.cpython-39.pyc index 7e85b18..a10a71e 100644 Binary files a/app/amazon/__pycache__/main.cpython-39.pyc and b/app/amazon/__pycache__/main.cpython-39.pyc differ diff --git a/app/amazon/__pycache__/match_action.cpython-39.pyc b/app/amazon/__pycache__/match_action.cpython-39.pyc index a9709a9..cfa3167 100644 Binary files a/app/amazon/__pycache__/match_action.cpython-39.pyc and b/app/amazon/__pycache__/match_action.cpython-39.pyc differ diff --git a/app/amazon/__pycache__/tool.cpython-39.pyc b/app/amazon/__pycache__/tool.cpython-39.pyc index c74884a..08dd859 100644 Binary files a/app/amazon/__pycache__/tool.cpython-39.pyc and b/app/amazon/__pycache__/tool.cpython-39.pyc differ diff --git a/app/amazon/approve.py b/app/amazon/approve.py index 7615a80..5577327 100644 --- a/app/amazon/approve.py +++ b/app/amazon/approve.py @@ -7,6 +7,8 @@ from config import runing_task, runing_shop from datetime import datetime from amazon.del_brand import AmamzonBase, kill_process +from amazon.tool import get_shop_info,show_notification + # 导入 webview 用于前端通知 try: import webview @@ -92,17 +94,18 @@ class AmzoneApprove(AmamzonBase): print("等待加载中消失出错", e) def clear_tab(self): - all_tab = self.browser.get_tabs(title="亚马逊") - close_tab = [] - for tab in all_tab: - tab_id = tab if isinstance(tab,str) else tab.tab_id - if self.tab.tab_id == tab: - continue - close_tab.append(tab) - print("需要关闭的标签页",close_tab) - print("当前操作的tab_id",self.tab.tab_id) - self.browser.close_tabs(close_tab) - + for i in ["亚马逊","您的账户是否符合条件?"]: + all_tab = self.browser.get_tabs(title=i) + close_tab = [] + for tab in all_tab: + tab_id = tab if isinstance(tab,str) else tab.tab_id + if self.tab.tab_id == tab_id: + continue + close_tab.append(tab) + print("需要关闭的标签页",close_tab) + print("当前操作的tab_id",self.tab.tab_id) + self.browser.close_tabs(close_tab) + def handle_repair_product(self,tab:ChromiumTab): def wait_loaded(): # 等待加载完成 @@ -118,7 +121,7 @@ class AmzoneApprove(AmamzonBase): submit_btn_ls = tab.eles('xpath://div[@id="ahd-product-policies-table"]/div//div[@data-testid="nextStepsMetricWrapper"]//kat-button[@label="提交"]',timeout=10) if len(submit_btn_ls) == 0: print("未找到提交按钮,可能页面未加载完成或者页面结构发生变化") - return + # return for _ in range(3): try: # todo 遍历 @@ -265,6 +268,7 @@ class AmzoneApprove(AmamzonBase): action_panel.wait.displayed(timeout=5, raise_err=False) self.wait_loaded() + time.sleep(1) try: # 如果存在“请求批准”按钮,则直接返回跳过 @@ -342,7 +346,7 @@ class AmzoneApprove(AmamzonBase): kat_box_ls = action_panel.eles('xpath:.//kat-box[@variant="white"]', timeout=5) # 修复商品信息 - repair_product = action_panel.eles('xpath:.//kat-button[@class="action-button"]',timeout=5) + repair_product = action_panel.eles('xpath:.//kat-button[@class="action-button" and not(@variant="secondary")]',timeout=5) if len(repair_product) > 0: print(f"ASIN {asin} 存在修复商品信息按钮,正在点击...") self.clear_tab() @@ -556,11 +560,9 @@ class ApproveTask: if not task_id: self.log("任务ID为空,跳过", "WARNING") return - if not items: self.log("店铺列表为空,跳过", "WARNING") return - if not country_codes: self.log("国家列表为空,跳过", "WARNING") return @@ -673,16 +675,28 @@ class ApproveTask: if browser and browser != "店铺不存在": self.log(f"成功打开店铺 {shop_name}") - break + # break else: self.log(f"打开店铺失败: {browser}", "WARNING") driver = None + continue # 判断是否需要登录 need_login = driver.need_login() + print("【是否需要登录】:",need_login) if need_login: self.log(f"店铺 {shop_name} 需要登录,正在登录...") - password = "" + # 获取店铺凭证 + response = get_shop_info(shop_name) + print("【获取店铺凭证返回】:",response.text) + shop_data = response.json() + if not shop_data: + mes = f"获取店铺凭证失败,响应数据: {shop_data.get('message', '未知错误')}" + self.log(mes, "ERROR") + show_notification(mes, "ERROR") + continue + + password = shop_data["data"]["password"] login_success = driver.login(password) if login_success: @@ -697,7 +711,8 @@ class ApproveTask: else: self.log(f"店铺 {shop_name} 登录失败", "WARNING") driver = None - + else: + break except Exception as e: import traceback self.log(f"打开店铺异常: {traceback.format_exc()}", "INFO") diff --git a/app/amazon/del_brand.py b/app/amazon/del_brand.py index d0f60f2..4c92467 100644 --- a/app/amazon/del_brand.py +++ b/app/amazon/del_brand.py @@ -489,7 +489,9 @@ class AmamzonBase(ZiniaoDriver): 处理流程: """ - need_login_ele = self.tab.eles('xpath://h1[@class="a-spacing-small"]',timeout=5) + time.sleep(3) # 等待页面可能的登录元素加载,避免跳转等等 + self.tab.wait.doc_loaded(timeout=30, raise_err=False) + need_login_ele = self.tab.eles('xpath://h1[@class="a-spacing-small"]|//span[contains(text(),"登录")]',timeout=5) if len(need_login_ele) >0: print("检测到需要登录元素") return True @@ -497,13 +499,15 @@ class AmamzonBase(ZiniaoDriver): def login(self,password,username=""): try: - pwd_input = self.tab.ele('xpath://input[@type="password"]',timeout=10) - pwd_input.input(password) - submit_btn = self.tab.ele('xpath://input[@id="signInSubmit"]',timeout=10) - submit_btn.click() + pwd_input = self.tab.eles('xpath://input[@type="password"]',timeout=10) + if len(pwd_input) > 0: + pwd_input[0].input(password) + submit_btn = self.tab.eles('xpath://input[@id="signInSubmit"]',timeout=10) + if len(submit_btn) > 0: + submit_btn[0].click() self.tab.wait.doc_loaded() - opt_code_input = self.tab.ele('xpath://input[@name="otpCode"]',timeout=10) + opt_code_input = self.tab.ele('xpath://input[@name="otpCode"]',timeout=60) opt_code_input.wait.displayed(timeout=10,raise_err=False) for _ in range(30): if opt_code_input.value is not None and opt_code_input.value.strip() != "": @@ -512,6 +516,9 @@ class AmamzonBase(ZiniaoDriver): submit_btn.click() self.tab.wait.doc_loaded() return True + time.sleep(1) + submit_btn = self.tab.ele('xpath://input[@id="auth-signin-button"]',timeout=10) + submit_btn.click() except Exception as e: print("登录过程中发生异常:" + traceback.format_exc()) return False diff --git a/app/amazon/main.py b/app/amazon/main.py index 1f7d6f7..0e072bc 100644 --- a/app/amazon/main.py +++ b/app/amazon/main.py @@ -10,6 +10,9 @@ from amazon.approve import ApproveTask from amazon.match_action import MatchTak +from amazon.tool import get_shop_info,show_notification + + class TaskMonitor: """任务监控器:负责监控队列并执行品牌删除任务""" @@ -64,13 +67,13 @@ class TaskMonitor: if task_type == "delete-brand-run": # 提交删除品牌任务到线程池 - self.log(f"接收到删除品牌任务,提交到线程池处理...") + self.log(f"接收到【删除品牌】任务,提交到线程池处理...") future = self.executor.submit(self._process_task_wrapper, task_data) futures.append(future) elif task_type in task_type_info: # 提交产品风险审批任务到线程池 - self.log(f"接收到产品风险审批任务,提交到线程池处理...") + self.log(f"接收到任务,提交到线程池处理...") future = self.executor.submit(self._process_approve_task_wrapper, task_data, task_type_info[task_type]) futures.append(future) @@ -242,10 +245,22 @@ class TaskMonitor: driver = None # 判断是否需要登录 + driver.tab.wait.doc_loaded(timeout=30, raise_err=False) # 等待页面加载,避免过早判断登录状态 need_login = driver.need_login() + print("【是否需要登录】:",need_login) if need_login: self.log(f"店铺 {shop_name} 需要登录,正在登录...") - password = "" + # 获取店铺凭证 + response = get_shop_info(shop_name) + print("【获取店铺凭证返回】:",response.text) + shop_data = response.json() + if not shop_data: + mes = f"获取店铺凭证失败,响应数据: {shop_data.get('message', '未知错误')}" + self.log(mes, "ERROR") + show_notification(mes, "ERROR") + continue + + password = shop_data["data"]["password"] login_success = driver.login(password) if login_success: @@ -611,6 +626,7 @@ class TaskMonitor: verify=False # 忽略SSL证书验证 ) print("【结果提交】:",payload) + print("【结果提交返回】:",response.text) if response.status_code == 200: self.log(f"结果回传成功: {url}") diff --git a/app/amazon/match_action.py b/app/amazon/match_action.py index e0ad574..1841aa8 100644 --- a/app/amazon/match_action.py +++ b/app/amazon/match_action.py @@ -7,7 +7,7 @@ from config import runing_task, runing_shop from datetime import datetime from amazon.del_brand import AmamzonBase, kill_process -from amazon.tool import show_notification +from amazon.tool import show_notification,get_shop_info class AmzoneMatchAction(AmamzonBase): @@ -273,6 +273,8 @@ class MatchTak: try: self.process_shop(shop_item, country_codes, task_id, risk_listing_filter, user_id, stage_index, final_stage) + + # self.process_shop(shop_item, country_codes, task_id,risk_listing_filter) # 更新已处理店铺数 if task_id in runing_task: runing_task[task_id]["processed_shops"] += 1 @@ -299,6 +301,7 @@ class MatchTak: runing_task[task_id]["status"] = "failed" runing_task[task_id]["error"] = str(e) + # def process_shop(self, shop_item: dict, country_codes: list, task_id: int, risk_listing_filter: str): def process_shop(self, shop_item: dict, country_codes: list, task_id: int, risk_listing_filter: str, user_id=None, stage_index=None, final_stage: bool = True): """处理单个店铺 @@ -350,16 +353,27 @@ class MatchTak: if browser and browser != "店铺不存在": self.log(f"成功打开店铺 {shop_name}") - break else: self.log(f"打开店铺失败: {browser}", "WARNING") driver = None + continue # 判断是否需要登录 need_login = driver.need_login() + print("【是否需要登录】:",need_login) if need_login: self.log(f"店铺 {shop_name} 需要登录,正在登录...") - password = "" + # 获取店铺凭证 + response = get_shop_info(shop_name) + print("【获取店铺凭证返回】:",response.text) + shop_data = response.json() + if not shop_data: + mes = f"获取店铺凭证失败,响应数据: {shop_data.get('message', '未知错误')}" + self.log(mes, "ERROR") + show_notification(mes, "ERROR") + continue + + password = shop_data["data"]["password"] login_success = driver.login(password) if login_success: @@ -374,6 +388,8 @@ class MatchTak: else: self.log(f"店铺 {shop_name} 登录失败", "WARNING") driver = None + else: + break except Exception as e: import traceback @@ -409,7 +425,9 @@ class MatchTak: import traceback self.log(f"处理国家 {country_code} 失败: {str(e)}", "ERROR") self.log(traceback.format_exc(), "ERROR") + # 最后回传,标记完成 try: + # self.post_result(task_id, shop_name, country_code, "", "", is_done=True) if final_stage: self.post_result(task_id, shop_name, country_code, "", "", is_done=True) else: @@ -564,7 +582,6 @@ class MatchTak: runing_task[task_id]["processed_countries"] += 1 self.log(f"国家 {country_name} 处理完成") - def post_stage_finished(self, task_id: int, user_id, stage_index): import requests from config import DELETE_BRAND_API_BASE @@ -639,8 +656,8 @@ class MatchTak: max_retries = 3 for retry in range(max_retries): try: - print("================回传处理结果====================") - self.log(f"尝试回传结果 (第{retry + 1}/{max_retries} 次)") + print("================【匹配】=====================") + self.log(f"尝试回传结果 (第 {retry + 1}/{max_retries} 次)") self.log(f"回传URL: {url}") self.log(f"回传数据: {payload}") response = requests.post( @@ -653,9 +670,10 @@ class MatchTak: self.log(f"回传结果: {response.text}") data = response.json() if response.text else {} if response.status_code == 200 and isinstance(data, dict) and data.get("success"): - self.log(f"回传结果成功: {asin} - {status}") + self.log(f"结果回传成功: {asin} - {status}") return - self.log(f"回传结果失败,状态码: {response.status_code}", "WARNING") + else: + self.log(f"结果回传失败,状态码: {response.status_code}", "WARNING") print("=====================================") except Exception as e: @@ -666,7 +684,10 @@ class MatchTak: if retry < max_retries - 1: time.sleep(2) + self.log(f"已达到最大重试次数,结果回传最终失败", "ERROR") raise RuntimeError("已达到最大重试次数,结果回传最终失败") + + if __name__ == "__main__": user_info = { diff --git a/app/amazon/tool.py b/app/amazon/tool.py index 4f688cc..af48ded 100644 --- a/app/amazon/tool.py +++ b/app/amazon/tool.py @@ -5,6 +5,9 @@ except ImportError: webview = None +import requests +from urllib.parse import quote + def show_notification(message: str, message_type: str = "error"): """显示 pywebview 顶层通知(5秒后自动消失) @@ -75,3 +78,39 @@ def show_notification(message: str, message_type: str = "error"): print(f"显示通知失败: {str(e)}") + + +def get_shop_info(shop_name: str, base_url: str = "http://8.136.19.173:18080") -> requests.Response: + """ + 调用商铺凭证接口 + :param shop_name: 商铺名称,将自动进行 URL 编码 + :param base_url: API 基础地址,默认从 curl 中提取 + :return: requests.Response 对象 + """ + url = f"{base_url}/api/admin/shop-manages/credential" + params = {"shopName": shop_name} + + headers = { + "Accept": "*/*", + "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", + "Cache-Control": "no-cache", + "Connection": "keep-alive", + "Pragma": "no-cache", + "Referer": "http://8.136.19.173:18080/doc.html", + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36", + "X-Internal-Token": "59c6691199917a2095827b5188502029", + } + + response = requests.get( + url, + params=params, + headers=headers, + verify=False, + timeout=30 + ) + return response + + + + + diff --git a/app/assets/delete-brand.js b/app/assets/delete-brand.js index 8b54b1e..3872715 100644 --- a/app/assets/delete-brand.js +++ b/app/assets/delete-brand.js @@ -1 +1 @@ -import{d as Oe,o as He,I as Je,a as u,c,b as Ge,B as je,e as r,F as K,r as re,t as f,f as p,J as Ve,K as Fe,n as Ne,g as m,h as W,L as ze,i as O,E as h,M as Qe,N as Xe,O as We,P as Ye,_ as Ze,p as es,q as ss}from"./pywebview-D7_PdvyM.js";import{e as ts}from"./brand-DysFlQbZ.js";const as={class:"page-shell module-page"},ns={class:"main-content"},ls={class:"left-panel"},rs={class:"upload-zone"},os={class:"selected-files clean-placeholder split-selected-files"},is={key:0,class:"more-line"},us={key:1},cs={class:"run-row"},ds=["disabled"],fs={class:"loading-msg"},vs={key:0,class:"queue-debug-card"},ps={key:0,class:"queue-debug-line"},hs={key:1,class:"queue-debug-payload"},_s={class:"right-panel"},gs={class:"task-list-wrap"},ys={class:"clean-result-summary"},ks={class:"summary-card"},ms={class:"summary-card"},ws={class:"summary-card"},Is={class:"result-list-wrap"},Ss={key:0,class:"empty-tasks"},bs={key:0,class:"result-subsection"},Cs={class:"task-list clean-result-list"},Ts={class:"left split-result-main"},Fs=["title"],Ns={class:"files"},Es={class:"files"},Rs={key:0,class:"files"},xs={key:1,class:"files"},$s={key:2,class:"files"},Ps={key:3,class:"time"},As={key:4,class:"delete-brand-progress-block"},Ds={class:"delete-brand-progress-header"},Ms={class:"delete-brand-progress-bar"},Bs={class:"files delete-brand-progress"},Us={key:5,class:"files split-entry-list delete-brand-preview"},qs={key:6,class:"files"},Ls={class:"task-right split-result-actions"},Ks=["onClick"],Os=["onClick"],Hs=["onClick"],Js={key:1,class:"result-subsection"},Gs={class:"task-list clean-result-list"},js={class:"left split-result-main"},Vs=["title"],zs={class:"files"},Qs={class:"files"},Xs={key:0,class:"files"},Ws={key:1,class:"files"},Ys={key:2,class:"time"},Zs={key:3,class:"delete-brand-progress-block"},et={class:"delete-brand-progress-header"},st={class:"delete-brand-progress-bar"},tt={class:"files delete-brand-progress"},at={key:4,class:"files split-entry-list delete-brand-preview"},nt={key:5,class:"files"},lt={class:"task-right split-result-actions"},rt=["onClick"],ot=["onClick"],it=Oe({__name:"BrandDeleteBrandTab",setup(ct){function M(){return`delete-brand:current-tasks:${typeof window<"u"&&window.localStorage.getItem("uid")||"0"}`}const $=m([]),H=m([]),B=m(!1),oe=m([]),v=m([]),J=m([]),G=m({total:0,successCount:0,failedCount:0,items:[]}),T=m(""),C=m(""),g=m({}),S=m(null),Y=m(!1),P=m(!1),A=m(""),j=m(!1),Z=W(()=>$.value.slice(0,10)),F=W(()=>{const e=[];return v.value.forEach(s=>{e.push(...s.items)}),e});function Ee(){try{const e=typeof window<"u"?window.localStorage.getItem(M()):null;if(e){if(v.value=JSON.parse(e),v.value.length>0){const s=v.value[v.value.length-1];T.value=s.queuePushResult||"",C.value=s.queuePayloadText||""}}else v.value=[]}catch{v.value=[]}}function ie(e,s,t,a){if(!s?.length)return;const n=v.value.findIndex(d=>d.taskId===e),o=n>=0?v.value[n]:null,l=s.map(d=>{const _=o?.items.find(Ke=>D(Ke,d)),k=Array.isArray(d.countries)?d.countries:void 0,y=Array.isArray(_?.countries)?_?.countries:void 0,w=k&&k.length>0?k:y&&y.length>0?y:k??y,I=Array.isArray(d.previewRows)?d.previewRows:void 0,b=Array.isArray(_?.previewRows)?_?.previewRows:void 0,le=I&&I.length>0?I:b&&b.length>0?b:I??b;return{...d,countries:w,previewRows:le,_pushed:_?._pushed??d._pushed??!1,_completed:_?._completed??d._completed??!1}}),i={taskId:e,items:l,createdAt:o?.createdAt||Date.now(),queuePushResult:t??o?.queuePushResult,queuePayloadText:a??o?.queuePayloadText};n>=0?v.value[n]=i:v.value.push(i),typeof window<"u"&&window.localStorage.setItem(M(),JSON.stringify(v.value))}function ue(e){const s=v.value.length;v.value=v.value.filter(t=>t.taskId!==e),v.value.length!==s&&typeof window<"u"&&window.localStorage.setItem(M(),JSON.stringify(v.value))}const V=W(()=>J.value.filter(e=>!F.value.some(s=>D(s,e)))),Re=W(()=>F.value.length>0||V.value.length>0);function ce(){typeof window<"u"&&window.localStorage.setItem(M(),JSON.stringify(v.value))}function N(e){return e?(v.value.find(t=>t.taskId===e)?.items.length||0)>1:!1}function ee(e){return e&&v.value.find(s=>s.taskId===e)||null}function D(e,s){return!e||!s?!1:e.resultId!=null&&s.resultId!=null?e.resultId===s.resultId:e.fileKey&&s.fileKey?e.fileKey===s.fileKey:e.sourceFilename===s.sourceFilename}function E(e,s){const t=ee(e);return t&&t.items.find(a=>D(a,s))||null}function de(e){if(!e)return!1;const s=ee(e);if(!s)return!1;let t=!1;const a=g.value[e]?.line_progress?.info,n=N(e);if(a?.file_name&&a.current_line!=null&&a.total_lines!=null&&a.total_lines>0&&a.current_line>=a.total_lines){const o=s.items.find(l=>l.sourceFilename===a.file_name);o&&!o._completed&&(o._completed=!0,t=!0)}if(!n){const o=g.value[e]?.task?.status;if((o==="SUCCESS"||o==="FAILED")&&s.items.length===1){const l=s.items[0];l&&!l._completed&&(l._completed=!0,t=!0)}}return t&&ce(),t}function fe(e){return e&&g.value[e]?.task?.status||""}function z(e){if(e.error)return e.error;if(e.matchStatus&&e.matchStatus!=="MATCHED")return e.matchMessage||"";const s=e.taskId;return s&&g.value[s]?.task?.errorMessage||""}function ve(e){const s=e.taskId;if(!s)return!1;const t=g.value[s]?.line_progress,a=fe(s),n=N(s),o=x(e);if(n){const l=E(s,e),i=L(e)===A.value,d=!!(t?.has_progress&&t?.info?.file_name===e.sourceFilename);return!!(i||d||l?._pushed||(a==="RUNNING"||a==="PENDING"||!a)&&l?._completed)}return o==="本文件解析完毕"||o==="已被全局判定为终态"?!1:t?.has_progress||t?.info?!0:o==="已入队"||o==="处理中"}function pe(e){return e.map(s=>s.matchStatus==="MATCHED"?{...s,_pushed:!1,_completed:!1}:{...s,matched:!1,openStoreUrl:void 0,error:s.error||void 0,_pushed:!1,_completed:!1})}function he(e){return e.matchStatus==="MATCHED"?`已匹配 ${e.shopId||""}`.trim():e.matchMessage?e.matchMessage:e.matchStatus==="CONFLICT"?"存在多个同名店铺,请人工确认":e.matchStatus==="PENDING"||e.matchStatus==="INDEX_STALE"?"店铺索引暂未就绪":e.matchStatus?e.matchStatus:e.matched?`已匹配 ${e.shopId||""}`.trim():"待匹配"}function xe(){oe.value=[...F.value,...V.value]}function R(){xe(),$e(oe.value)}function $e(e){G.value={total:e.length,successCount:e.filter(s=>s.success).length,failedCount:e.filter(s=>!s.success).length,items:e}}function se(e){const s=e.taskId;return s&&(g.value[s]?.fileProgress||[]).find(n=>n.fileKey&&e.fileKey?n.fileKey===e.fileKey:n.sourceFilename&&e.sourceFilename?n.sourceFilename===e.sourceFilename:!1)||null}function _e(e){const s=e.taskId;if(!s)return"";const t=g.value[s],a=t?.line_progress?.info,n=se(e);if(N(s)){const i=E(s,e),d=t?.task?.status,_=!!(a?.file_name&&a.file_name===e.sourceFilename);let k=i?._completed?"COMPLETED":(i?._pushed,"PENDING"),y=0,w=Math.max(0,e.totalRows??n?.totalRows??0);d==="SUCCESS"||d==="FAILED"&&i?._completed?(k="COMPLETED",y=w):_?(k="RUNNING",y=Math.max(0,Math.min(w||Number.MAX_SAFE_INTEGER,a?.current_line??0)),!w&&a?.total_lines&&a.total_lines>0&&(w=a.total_lines)):i?._completed?(k="COMPLETED",y=w):i?._pushed&&(k="PENDING",y=0);const I=[];if(I.push(`文件状态:${k}`),I.push(`文件进度:${y}/${w}`),a?.finished_files!=null){const b=a.file_total&&a.file_total>0?`/${a.file_total}`:"";I.push(`已完成文件:${a.finished_files}${b}`)}return a?.phase&&I.push(`阶段:${a.phase}`),I.join("|")}if(n){const i=[],d=n.processedRows??0,_=n.totalRows??0;if(n.status&&i.push(`文件状态:${n.status}`),i.push(`文件进度:${d}/${_}`),a?.finished_files!=null){const k=a.file_total&&a.file_total>0?`/${a.file_total}`:"";i.push(`已完成文件:${a.finished_files}${k}`)}return a?.phase&&i.push(`阶段:${a.phase}`),i.join("|")}if(!t?.line_progress?.has_progress||!a){const i=t?.task?.status;return i?`任务状态:${i}`:""}const l=[];if(a.phase&&l.push(`阶段:${a.phase}`),a.file_index&&a.file_total&&l.push(`文件:${a.file_index}/${a.file_total}`),a.file_name&&l.push(`当前文件:${a.file_name}`),a.current_line!=null&&a.total_lines!=null&&l.push(`${N(s)?"当前文件进度":"进度"}:${a.current_line}/${a.total_lines}`),a.current_country&&l.push(`国家:${a.current_country}`),a.current_asin&&l.push(`ASIN:${a.current_asin}`),a.finished_files!=null){const i=a.file_total&&a.file_total>0?`/${a.file_total}`:"";l.push(`已完成文件:${a.finished_files}${i}`)}return l.join("|")}function Q(e){const s=e.taskId;if(!s)return 0;const t=g.value[s]?.line_progress?.info;if(N(s)){const l=E(s,e);if(g.value[s]?.task?.status==="SUCCESS"||l?._completed)return 100;const d=Math.max(0,e.totalRows??se(e)?.totalRows??0);if(!!(t?.file_name&&t.file_name===e.sourceFilename)){if(t?.current_line!=null&&t?.total_lines&&t.total_lines>0)return Math.max(0,Math.min(100,Math.round(t.current_line/t.total_lines*100)));if(t?.current_line!=null&&d>0)return Math.max(0,Math.min(100,Math.round(t.current_line/d*100)))}return 0}const n=se(e);return n?Math.max(0,Math.min(100,n.percent??0)):t?t.current_line!=null&&t.total_lines&&t.total_lines>0?Math.max(0,Math.min(100,Math.round(t.current_line/t.total_lines*100))):t.finished_files!=null&&t.file_total&&t.file_total>0?Math.max(0,Math.min(100,Math.round(t.finished_files/t.file_total*100))):g.value[s]?.task?.status==="SUCCESS"?100:0:0}function ge(e){const s=g.value[e]?.task?.status;return s==="SUCCESS"||s==="FAILED"}function U(){const e=new Set;return v.value.forEach(s=>{s.items.some(t=>t._pushed)&&e.add(s.taskId)}),Array.from(e)}function x(e){const s=e.taskId;if(!s||!ee(s))return"";const a=E(s,e),n=fe(s),o=g.value[s]?.line_progress,l=N(s);if(!l&&(n==="SUCCESS"||n==="FAILED"||e.taskStatus==="SUCCESS"||e.taskStatus==="FAILED"))return"已被全局判定为终态";if(a?._completed)return"本文件解析完毕";if(n==="RUNNING"&&o?.has_progress&&o?.info?.file_name===e.sourceFilename)return"处理中";if(l&&!a?._pushed&&!a?._completed)return"未入队";if(l&&n==="SUCCESS")return"本文件解析完毕";if(!l){const i=g.value[s];if(i?.items&&i.items.find(_=>D(_,e))&&n==="SUCCESS")return"本文件解析完毕"}return a?._pushed?"已入队":"未入队"}function ye(e){if(e.downloadUrl)return!0;const s=e.taskId;return s?g.value[s]?.task?.status==="SUCCESS":!1}function Pe(e){const s=e?.task?.id;if(!s)return!1;const t=v.value.find(l=>l.taskId===s);if(!t)return!1;const a=e?.items||[];let n=!1;const o=t.items.map(l=>{const i=a.find(le=>D(le,l));if(!i)return l;const d=Array.isArray(i.countries)?i.countries:void 0,_=Array.isArray(l.countries)?l.countries:void 0,k=d&&d.length>0?d:_&&_.length>0?_:d??_,y=Array.isArray(i.previewRows)?i.previewRows:void 0,w=Array.isArray(l.previewRows)?l.previewRows:void 0,I=y&&y.length>0?y:w&&w.length>0?w:y??w,b={...l,...i,countries:k,previewRows:I,_pushed:l._pushed,_completed:l._completed};return JSON.stringify(b)!==JSON.stringify(l)&&(n=!0),b});for(const l of a)o.some(i=>D(i,l))||(o.push({...l,_pushed:!1,_completed:!1}),n=!0);return JSON.stringify(o)!==JSON.stringify(t.items)&&(t.items=o,n=!0),n&&ce(),n}async function te(e){const s=e||U();if(s.length)try{const t=await ze(s);let a=!1;for(const n of t.items||[]){const o=n?.task?.id;typeof o=="number"&&o>0&&(g.value[o]=n,Pe(n)&&(a=!0),de(o)&&(a=!0))}a&&R()}catch{}}function Ae(){return document.visibilityState==="visible"?4500:1e4}function ae(e=!1){if(S.value){if(!e)return;clearTimeout(S.value),S.value=null}const s=async()=>{if(S.value=null,Y.value){ae();return}const t=U();if(!t.length)return;Y.value=!0;let a=!1,n=!1;try{await te(t),await qe();for(const o of t)ge(o)&&(ue(o),a=!0,n=!0)}finally{Y.value=!1}n?await q():a&&R(),U().length>0&&ae()};e?s():S.value=window.setTimeout(s,Ae())}function ne(e=!1){S.value&&!e||ae(e)}function X(e){const s=e.taskId?g.value[e.taskId]?.task?.status:null;return s==="SUCCESS"?{className:"success",text:"已完成"}:s==="FAILED"?{className:"failed",text:"失败"}:s==="RUNNING"?{className:"running",text:"执行中"}:e.taskStatus==="SUCCESS"?{className:"success",text:"已完成"}:e.taskStatus==="FAILED"?{className:"failed",text:"失败"}:e.taskStatus==="RUNNING"?{className:"running",text:"执行中"}:e.success?{className:"success",text:"已完成"}:{className:"failed",text:"失败"}}function ke(){S.value&&(window.clearTimeout(S.value),S.value=null)}async function me(e){const s=O();if(!s?.upload_file_to_java)throw new Error("当前桌面端未提供文件上传桥接能力");const t=[];for(const a of e){const n=typeof a=="string"?a:a.absolutePath,o=typeof a=="string"?void 0:a.relativePath,l=await s.upload_file_to_java(n,o);if(!l?.success||!l.data)throw new Error(l?.error||l?.message||`上传失败:${n}`);t.push(l.data)}return t}async function De(){const e=O();if(!e?.select_brand_xlsx_files){h.warning("当前环境不支持文件选择,请在本机客户端中打开");return}try{const s=await e.select_brand_xlsx_files();if(!s?.length)return;$.value=s,H.value=await me(s),h.success(`已选择 ${s.length} 个删除品牌文件`)}catch(s){h.error(s instanceof Error?s.message:"选择失败")}}async function Me(){const e=O();if(!e?.select_brand_folder){h.warning("当前环境不支持文件夹选择,请在本机客户端中打开");return}try{const s=await e.select_brand_folder();if(!s)return;const t=await ts(s);if(!t.success||!t.items?.length){h.warning(t.error||"该文件夹下没有 xlsx 文件");return}$.value=t.items.map(a=>a.relativePath||a.absolutePath),H.value=await me(t.items),h.success(`已选择文件夹内 ${t.items.length} 个 xlsx 文件`)}catch(s){h.error(s instanceof Error?s.message:"选择失败")}}async function Be(){if(!H.value.length){h.warning("请先选择待处理文件");return}try{B.value=!0;const e=await Qe({files:H.value.map(n=>({fileKey:n.fileKey,originalFilename:n.originalFilename,relativePath:n.relativePath}))}),s=pe(e.items||[]),t=s.some(n=>n.matchStatus==="MATCHED"),a=s.some(n=>n.matchStatus&&n.matchStatus!=="MATCHED");T.value="解析完成,等待手动点击打开紫鸟进行单任务处理",C.value="",s.length&&s[0].taskId&&ie(s[0].taskId,s,T.value,C.value),R(),a?h.warning("部分文件尚未命中可用店铺索引,已保留状态信息,请等待后台刷新后重试。"):t?h.success("删除品牌解析完成,请手动推送"):h.warning("当前没有可推送的已匹配文件,请先等待店铺索引刷新。"),await q()}catch(e){h.error(e instanceof Error?e.message:"执行失败")}finally{B.value=!1}}async function we(e){const s=e.taskId;if(!s){h.warning("未找到任务ID");return}const t=O(),a=Xe(s),n=e.outputFilename||e.sourceFilename||`delete-brand_${s}.xlsx`;if(t?.save_file_from_url_new){const o=await t.save_file_from_url_new(a,n);o.success?h.success(`已保存:${o.path||n}`):o.error&&o.error!=="用户取消"&&h.error(o.error);return}}async function q(){try{const e=await We();J.value=pe(e.items||[]),R(),await te(),ne(!0)}catch(e){console.error("loadHistory error:",e)}}function Ie(e){v.value.forEach(t=>{t.items=t.items.filter(a=>a.resultId!==e)});const s=v.value.length;v.value=v.value.filter(t=>t.items.length>0),v.value.length!==s&&typeof window<"u"&&window.localStorage.setItem(M(),JSON.stringify(v.value)),J.value=J.value.filter(t=>t.resultId!==e),R(),U().length===0&&ke()}async function Se(e){try{await Ye(e),Ie(e),await q(),h.success("已删除")}catch(s){const t=s instanceof Error?s.message:"删除失败";if(typeof t=="string"&&t.includes("记录不存在")){Ie(e),h.success("已删除");return}h.error(t)}}function be(e){const s=e.slice(0,5).map(a=>`${a.country}:${a.asin}${a.status?`(${a.status})`:""}`);return e.length-s.length>0?`${s.join("、")} 等 ${e.length} 条`:s.join("、")}function dt(e,s){}function Ce(){for(const e of v.value)for(const s of e.items){const t=x(s);if(t==="已入队"||t==="处理中")return!0}return!1}function L(e){return e.resultId!=null?`result:${e.resultId}`:`task:${e.taskId||0}:${e.sourceFilename||""}`}function Ue(){return F.value.find(e=>!e.openStoreUrl||L(e)===A.value||E(e.taskId,e)?._completed?!1:x(e)==="未入队")}async function Te(e,s){if(s?.auto&&Ce())return e.taskId,e.sourceFilename,!1;const t=O(),a=e.taskId;if(!a)return!1;const n=v.value.find(d=>d.taskId===a);if(!n)return!1;if(!t?.enqueue_json)return h.error("当前环境未启用 pywebview enqueue_json(浏览器环境不会推送)"),!1;const o={type:"delete-brand-run",ts:Date.now(),data:{taskId:a,items:[e]}};C.value=JSON.stringify(o,null,2);const l=await t.enqueue_json(o);let i="";if(l?.success){i=`已推送文件 ${e.sourceFilename},当前队列长度:${l.queue_size??"-"}`;const d=E(a,e);d&&(d._pushed=!0,d._completed=!1),A.value=L(e),ie(a,n.items,i,C.value),ne(!0)}else i=`推送到 Python 队列失败:${l?.error||"未知错误"}`;return T.value=i,!!l?.success}async function qe(){if(!P.value||j.value){P.value,j.value;return}if(Ce())return;const e=A.value;if(e){const t=F.value.find(a=>L(a)===e);if(t){const a=t.taskId,n=N(a);n&&(de(a),x(t),void 0);const o=x(t);if(o==="已入队"||o==="处理中"){t.sourceFilename;return}if(n){const l=E(a,t);if(l&&!l._completed){t.sourceFilename,l._pushed,l._completed;return}}}}const s=Ue();if(!s){A.value="",P.value=!1;return}j.value=!0;try{s.taskId,s.sourceFilename,L(s),await Te(s,{auto:!0})||(s.taskId,s.sourceFilename,P.value=!1)}finally{j.value=!1}}async function Le(e){P.value=!0,await Te(e)||(P.value=!1,A.value="")}return He(()=>{Ee();const e=U();e.length>0?te(e).then(async()=>{let s=!1,t=!1;for(const a of e)ge(a)&&(ue(a),s=!0,t=!0);t?await q():s&&R(),ne(!0)}):R(),q().catch(()=>{})}),Je(()=>{ke()}),(e,s)=>(u(),c("div",as,[Ge(je,{active:"delete-brand"}),r("div",ns,[r("aside",ls,[s[2]||(s[2]=r("div",{class:"section-title"},"选择文件",-1)),r("div",rs,[s[0]||(s[0]=r("div",{class:"hint"},"上传删除品牌 Excel;无论是直接上传文件还是上传文件夹批量导入,都会按每个 Excel 文件名作为店铺名。",-1)),r("div",{class:"btns"},[r("button",{type:"button",class:"opt-btn",onClick:De},"选择 Excel 文件"),r("button",{type:"button",class:"opt-btn",onClick:Me},"选择文件夹")]),r("div",os,[$.value.length?(u(),c(K,{key:0},[(u(!0),c(K,null,re(Z.value,t=>(u(),c("span",{key:t},f(t),1))),128)),$.value.length>Z.value.length?(u(),c("span",is," 还有 "+f($.value.length-Z.value.length)+" 个文件未展开显示 ",1)):p("",!0)],64)):(u(),c("span",us,"暂未选择删除品牌文件"))])]),s[3]||(s[3]=Ve('
执行说明
',2)),r("div",cs,[r("button",{type:"button",class:"btn-run",disabled:B.value,onClick:Be},f(B.value?"解析中...":"开始解析"),9,ds),r("span",fs,f(B.value?"正在读取删除品牌数据,请稍候…":"解析后会在右侧展示按国家分组的去重结果"),1)]),T.value||C.value?(u(),c("div",vs,[s[1]||(s[1]=r("div",{class:"section-title queue-debug-title"},"Python 队列推送结果",-1)),T.value?(u(),c("div",ps,f(T.value),1)):p("",!0),C.value?(u(),c("pre",hs,f(C.value),1)):p("",!0)])):p("",!0)]),r("section",_s,[s[12]||(s[12]=r("div",{class:"panel-header"},"删除品牌结果",-1)),r("div",gs,[r("div",ys,[r("div",ks,[s[4]||(s[4]=r("span",{class:"summary-label"},"已处理文件",-1)),r("strong",null,f(G.value.total),1)]),r("div",ms,[s[5]||(s[5]=r("span",{class:"summary-label"},"成功结果",-1)),r("strong",null,f(G.value.successCount),1)]),r("div",ws,[s[6]||(s[6]=r("span",{class:"summary-label"},"失败文件",-1)),r("strong",null,f(G.value.failedCount),1)])]),r("div",Is,[s[11]||(s[11]=r("div",{class:"result-list-header"},[r("span",null,"删除品牌结果列表")],-1)),Re.value?(u(),c(K,{key:1},[F.value.length?(u(),c("div",bs,[s[8]||(s[8]=r("div",{class:"result-subsection-title"},"当前任务",-1)),r("ul",Cs,[(u(!0),c(K,null,re(F.value,t=>(u(),c("li",{key:`current-${t.taskId||t.resultId||t.sourceFilename}`,class:"task-item split-result-item"},[r("div",Ts,[r("span",{class:"id",title:t.sourceFilename},f(t.sourceFilename||"-"),9,Fs),r("div",Ns,"店铺名:"+f(t.shopName||"-"),1),r("div",Es,"匹配结果:"+f(he(t)),1),t.platform?(u(),c("div",Rs,"平台:"+f(t.platform),1)):p("",!0),x(t)?(u(),c("div",xs,"队列状态:"+f(x(t)),1)):p("",!0),t.countryCount!==void 0&&t.matched?(u(),c("div",$s,"国家数:"+f(t.countryCount),1)):p("",!0),t.totalRows!==void 0&&t.matched?(u(),c("div",Ps,"去重后 "+f(t.totalRows)+" 条 ",1)):p("",!0),ve(t)?(u(),c("div",As,[r("div",Ds,[s[7]||(s[7]=r("span",null,"任务进度",-1)),r("span",null,f(Q(t))+"%",1)]),r("div",Ms,[r("div",{class:"delete-brand-progress-bar-fill",style:Fe({width:`${Q(t)}%`})},null,4)]),r("div",Bs,f(_e(t)),1)])):p("",!0),t.previewRows?.length?(u(),c("div",Us,f(be(t.previewRows)),1)):p("",!0),z(t)?(u(),c("div",qs,"错误信息:"+f(z(t)),1)):p("",!0)]),r("div",Ls,[r("span",{class:Ne(["status",X(t).className])},f(X(t).text),3),t.openStoreUrl?(u(),c("button",{key:0,type:"button",class:"download",onClick:a=>Le(t)}," 打开紫鸟 ",8,Ks)):p("",!0),ye(t)?(u(),c("button",{key:1,type:"button",class:"download",onClick:a=>we(t)}," 下载 ",8,Os)):p("",!0),t.resultId?(u(),c("button",{key:2,type:"button",class:"btn-delete",onClick:a=>Se(t.resultId)}," 删除 ",8,Hs)):p("",!0)])]))),128))])])):p("",!0),V.value.length?(u(),c("div",Js,[s[10]||(s[10]=r("div",{class:"result-subsection-title"},"历史记录",-1)),r("ul",Gs,[(u(!0),c(K,null,re(V.value,t=>(u(),c("li",{key:`history-${t.taskId||t.resultId||t.sourceFilename}`,class:"task-item split-result-item"},[r("div",js,[r("span",{class:"id",title:t.sourceFilename},f(t.sourceFilename||"-"),9,Vs),r("div",zs,"店铺名:"+f(t.shopName||"-"),1),r("div",Qs,"匹配结果:"+f(he(t)),1),t.platform?(u(),c("div",Xs,"平台:"+f(t.platform),1)):p("",!0),t.countryCount!==void 0&&t.matched?(u(),c("div",Ws,"国家数:"+f(t.countryCount),1)):p("",!0),t.totalRows!==void 0&&t.matched?(u(),c("div",Ys,"去重后 "+f(t.totalRows)+" 条 ",1)):p("",!0),ve(t)?(u(),c("div",Zs,[r("div",et,[s[9]||(s[9]=r("span",null,"任务进度",-1)),r("span",null,f(Q(t))+"%",1)]),r("div",st,[r("div",{class:"delete-brand-progress-bar-fill",style:Fe({width:`${Q(t)}%`})},null,4)]),r("div",tt,f(_e(t)),1)])):p("",!0),t.previewRows?.length?(u(),c("div",at,f(be(t.previewRows)),1)):p("",!0),z(t)?(u(),c("div",nt,"错误信息:"+f(z(t)),1)):p("",!0)]),r("div",lt,[r("span",{class:Ne(["status",X(t).className])},f(X(t).text),3),ye(t)?(u(),c("button",{key:0,type:"button",class:"download",onClick:a=>we(t)}," 下载 ",8,rt)):p("",!0),t.resultId?(u(),c("button",{key:1,type:"button",class:"btn-delete",onClick:a=>Se(t.resultId)}," 删除 ",8,ot)):p("",!0)])]))),128))])])):p("",!0)],64)):(u(),c("div",Ss," 暂无删除品牌结果,完成解析后会在这里展示按国家分组的去重结果 "))])])])])]))}}),ut=Ze(it,[["__scopeId","data-v-468f2aa7"]]);es(ut).use(ss).mount("#app"); +import{d as Je,o as Ge,I as je,a as u,c,b as ze,B as Ve,e as r,F as K,r as oe,t as v,f as h,J as Qe,K as Ee,n as Ne,g as m,h as W,L as Xe,i as H,E as p,M as We,N as Ye,O as Ze,P as es,_ as ss,p as ts,q as ns}from"./pywebview-D7_PdvyM.js";import{e as as}from"./brand-DysFlQbZ.js";const ls={class:"page-shell module-page"},rs={class:"main-content"},os={class:"left-panel"},is={class:"upload-zone"},us={class:"selected-files clean-placeholder split-selected-files"},cs={key:0,class:"more-line"},ds={key:1},fs={class:"run-row"},vs=["disabled"],hs={class:"loading-msg"},ps={key:0,class:"queue-debug-card"},_s={key:0,class:"queue-debug-line"},gs={key:1,class:"queue-debug-payload"},ys={class:"right-panel"},ks={class:"task-list-wrap"},ms={class:"clean-result-summary"},ws={class:"summary-card"},Is={class:"summary-card"},bs={class:"summary-card"},Ss={class:"result-list-wrap"},Cs={key:0,class:"empty-tasks"},Ts={key:0,class:"result-subsection"},Fs={class:"task-list clean-result-list"},Es={class:"left split-result-main"},Ns=["title"],Rs={class:"files"},xs={class:"files"},Ps={key:0,class:"files"},$s={key:1,class:"files"},As={key:2,class:"files"},Ds={key:3,class:"time"},Ms={key:4,class:"delete-brand-progress-block"},Bs={class:"delete-brand-progress-header"},Us={class:"delete-brand-progress-bar"},qs={class:"files delete-brand-progress"},Ls={key:5,class:"files split-entry-list delete-brand-preview"},Os={key:6,class:"files"},Ks={class:"task-right split-result-actions"},Hs=["onClick"],Js=["onClick"],Gs=["onClick"],js={key:1,class:"result-subsection"},zs={class:"task-list clean-result-list"},Vs={class:"left split-result-main"},Qs=["title"],Xs={class:"files"},Ws={class:"files"},Ys={key:0,class:"files"},Zs={key:1,class:"files"},et={key:2,class:"time"},st={key:3,class:"delete-brand-progress-block"},tt={class:"delete-brand-progress-header"},nt={class:"delete-brand-progress-bar"},at={class:"files delete-brand-progress"},lt={key:4,class:"files split-entry-list delete-brand-preview"},rt={key:5,class:"files"},ot={class:"task-right split-result-actions"},it=["onClick"],ut=["onClick"],ct=Je({__name:"BrandDeleteBrandTab",setup(ft){function M(){return`delete-brand:current-tasks:${typeof window<"u"&&window.localStorage.getItem("uid")||"0"}`}const P=m([]),J=m([]),B=m(!1),ie=m([]),d=m([]),U=m([]),G=m({total:0,successCount:0,failedCount:0,items:[]}),T=m(""),C=m(""),g=m({}),b=m(null),Y=m(!1),$=m(!1),A=m(""),j=m(!1),Z=W(()=>P.value.slice(0,10)),F=W(()=>{const e=[];return d.value.forEach(s=>{e.push(...s.items)}),e});function Re(){try{const e=typeof window<"u"?window.localStorage.getItem(M()):null;if(e){if(d.value=JSON.parse(e),d.value.length>0){const s=d.value[d.value.length-1];T.value=s.queuePushResult||"",C.value=s.queuePayloadText||""}}else d.value=[]}catch{d.value=[]}}function ue(e,s,t,n){if(!s?.length)return;const a=d.value.findIndex(f=>f.taskId===e),o=a>=0?d.value[a]:null,l=s.map(f=>{const _=o?.items.find(He=>D(He,f)),k=Array.isArray(f.countries)?f.countries:void 0,y=Array.isArray(_?.countries)?_?.countries:void 0,w=k&&k.length>0?k:y&&y.length>0?y:k??y,I=Array.isArray(f.previewRows)?f.previewRows:void 0,S=Array.isArray(_?.previewRows)?_?.previewRows:void 0,re=I&&I.length>0?I:S&&S.length>0?S:I??S;return{...f,countries:w,previewRows:re,_pushed:_?._pushed??f._pushed??!1,_completed:_?._completed??f._completed??!1}}),i={taskId:e,items:l,createdAt:o?.createdAt||Date.now(),queuePushResult:t??o?.queuePushResult,queuePayloadText:n??o?.queuePayloadText};a>=0?d.value[a]=i:d.value.push(i),typeof window<"u"&&window.localStorage.setItem(M(),JSON.stringify(d.value))}function ce(e){const s=d.value.length;d.value=d.value.filter(t=>t.taskId!==e),d.value.length!==s&&typeof window<"u"&&window.localStorage.setItem(M(),JSON.stringify(d.value))}function de(e){return e==="SUCCESS"||e==="FAILED"||e==="COMPLETED"}function xe(e){if(!e?.length)return!1;const s=new Set;if(e.forEach(n=>{n.taskId&&de(n.taskStatus)&&s.add(n.taskId)}),!s.size)return!1;const t=d.value.length;return d.value=d.value.filter(n=>!s.has(n.taskId)),d.value.length!==t?(ee(),!0):!1}const z=W(()=>U.value.filter(e=>!F.value.some(s=>D(s,e)))),Pe=W(()=>F.value.length>0||z.value.length>0);function ee(){typeof window<"u"&&window.localStorage.setItem(M(),JSON.stringify(d.value))}function E(e){return e?(d.value.find(t=>t.taskId===e)?.items.length||0)>1:!1}function se(e){return e&&d.value.find(s=>s.taskId===e)||null}function D(e,s){return!e||!s?!1:e.resultId!=null&&s.resultId!=null?e.resultId===s.resultId:e.fileKey&&s.fileKey?e.fileKey===s.fileKey:e.sourceFilename===s.sourceFilename}function N(e,s){const t=se(e);return t&&t.items.find(n=>D(n,s))||null}function fe(e){if(!e)return!1;const s=se(e);if(!s)return!1;let t=!1;const n=g.value[e]?.line_progress?.info,a=E(e);if(n?.file_name&&n.current_line!=null&&n.total_lines!=null&&n.total_lines>0&&n.current_line>=n.total_lines){const o=s.items.find(l=>l.sourceFilename===n.file_name);o&&!o._completed&&(o._completed=!0,t=!0)}if(!a){const o=g.value[e]?.task?.status;if((o==="SUCCESS"||o==="FAILED")&&s.items.length===1){const l=s.items[0];l&&!l._completed&&(l._completed=!0,t=!0)}}return t&&ee(),t}function ve(e){return e&&g.value[e]?.task?.status||""}function V(e){if(e.error)return e.error;if(e.matchStatus&&e.matchStatus!=="MATCHED")return e.matchMessage||"";const s=e.taskId;return s&&g.value[s]?.task?.errorMessage||""}function he(e){const s=e.taskId;if(!s)return!1;const t=g.value[s]?.line_progress,n=ve(s),a=E(s),o=x(e);if(a){const l=N(s,e),i=O(e)===A.value,f=!!(t?.has_progress&&t?.info?.file_name===e.sourceFilename);return!!(i||f||l?._pushed||(n==="RUNNING"||n==="PENDING"||!n)&&l?._completed)}return o==="本文件解析完毕"||o==="已被全局判定为终态"?!1:t?.has_progress||t?.info?!0:o==="已入队"||o==="处理中"}function pe(e){return e.map(s=>s.matchStatus==="MATCHED"?{...s,_pushed:!1,_completed:!1}:{...s,matched:!1,openStoreUrl:void 0,error:s.error||void 0,_pushed:!1,_completed:!1})}function _e(e){return e.matchStatus==="MATCHED"?`已匹配 ${e.shopId||""}`.trim():e.matchMessage?e.matchMessage:e.matchStatus==="CONFLICT"?"存在多个同名店铺,请人工确认":e.matchStatus==="PENDING"||e.matchStatus==="INDEX_STALE"?"店铺索引暂未就绪":e.matchStatus?e.matchStatus:e.matched?`已匹配 ${e.shopId||""}`.trim():"待匹配"}function $e(){ie.value=[...F.value,...z.value]}function R(){$e(),Ae(ie.value)}function Ae(e){G.value={total:e.length,successCount:e.filter(s=>s.success).length,failedCount:e.filter(s=>!s.success).length,items:e}}function te(e){const s=e.taskId;return s&&(g.value[s]?.fileProgress||[]).find(a=>a.fileKey&&e.fileKey?a.fileKey===e.fileKey:a.sourceFilename&&e.sourceFilename?a.sourceFilename===e.sourceFilename:!1)||null}function ge(e){const s=e.taskId;if(!s)return"";const t=g.value[s],n=t?.line_progress?.info,a=te(e);if(E(s)){const i=N(s,e),f=t?.task?.status,_=!!(n?.file_name&&n.file_name===e.sourceFilename);let k=i?._completed?"COMPLETED":(i?._pushed,"PENDING"),y=0,w=Math.max(0,e.totalRows??a?.totalRows??0);f==="SUCCESS"||f==="FAILED"&&i?._completed?(k="COMPLETED",y=w):_?(k="RUNNING",y=Math.max(0,Math.min(w||Number.MAX_SAFE_INTEGER,n?.current_line??0)),!w&&n?.total_lines&&n.total_lines>0&&(w=n.total_lines)):i?._completed?(k="COMPLETED",y=w):i?._pushed&&(k="PENDING",y=0);const I=[];if(I.push(`文件状态:${k}`),I.push(`文件进度:${y}/${w}`),n?.finished_files!=null){const S=n.file_total&&n.file_total>0?`/${n.file_total}`:"";I.push(`已完成文件:${n.finished_files}${S}`)}return n?.phase&&I.push(`阶段:${n.phase}`),I.join("|")}if(a){const i=[],f=a.processedRows??0,_=a.totalRows??0;if(a.status&&i.push(`文件状态:${a.status}`),i.push(`文件进度:${f}/${_}`),n?.finished_files!=null){const k=n.file_total&&n.file_total>0?`/${n.file_total}`:"";i.push(`已完成文件:${n.finished_files}${k}`)}return n?.phase&&i.push(`阶段:${n.phase}`),i.join("|")}if(!t?.line_progress?.has_progress||!n){const i=t?.task?.status;return i?`任务状态:${i}`:""}const l=[];if(n.phase&&l.push(`阶段:${n.phase}`),n.file_index&&n.file_total&&l.push(`文件:${n.file_index}/${n.file_total}`),n.file_name&&l.push(`当前文件:${n.file_name}`),n.current_line!=null&&n.total_lines!=null&&l.push(`${E(s)?"当前文件进度":"进度"}:${n.current_line}/${n.total_lines}`),n.current_country&&l.push(`国家:${n.current_country}`),n.current_asin&&l.push(`ASIN:${n.current_asin}`),n.finished_files!=null){const i=n.file_total&&n.file_total>0?`/${n.file_total}`:"";l.push(`已完成文件:${n.finished_files}${i}`)}return l.join("|")}function Q(e){const s=e.taskId;if(!s)return 0;const t=g.value[s]?.line_progress?.info;if(E(s)){const l=N(s,e);if(g.value[s]?.task?.status==="SUCCESS"||l?._completed)return 100;const f=Math.max(0,e.totalRows??te(e)?.totalRows??0);if(!!(t?.file_name&&t.file_name===e.sourceFilename)){if(t?.current_line!=null&&t?.total_lines&&t.total_lines>0)return Math.max(0,Math.min(100,Math.round(t.current_line/t.total_lines*100)));if(t?.current_line!=null&&f>0)return Math.max(0,Math.min(100,Math.round(t.current_line/f*100)))}return 0}const a=te(e);return a?Math.max(0,Math.min(100,a.percent??0)):t?t.current_line!=null&&t.total_lines&&t.total_lines>0?Math.max(0,Math.min(100,Math.round(t.current_line/t.total_lines*100))):t.finished_files!=null&&t.file_total&&t.file_total>0?Math.max(0,Math.min(100,Math.round(t.finished_files/t.file_total*100))):g.value[s]?.task?.status==="SUCCESS"?100:0:0}function ye(e){const s=g.value[e]?.task?.status;return de(s)}function q(){const e=new Set;return d.value.forEach(s=>{s.items.some(t=>t._pushed)&&e.add(s.taskId)}),Array.from(e)}function x(e){const s=e.taskId;if(!s||!se(s))return"";const n=N(s,e),a=ve(s),o=g.value[s]?.line_progress,l=E(s);if(!l&&(a==="SUCCESS"||a==="FAILED"||e.taskStatus==="SUCCESS"||e.taskStatus==="FAILED"))return"已被全局判定为终态";if(n?._completed)return"本文件解析完毕";if(a==="RUNNING"&&o?.has_progress&&o?.info?.file_name===e.sourceFilename)return"处理中";if(l&&!n?._pushed&&!n?._completed)return"未入队";if(l&&a==="SUCCESS")return"本文件解析完毕";if(!l){const i=g.value[s];if(i?.items&&i.items.find(_=>D(_,e))&&a==="SUCCESS")return"本文件解析完毕"}return n?._pushed?"已入队":"未入队"}function ke(e){if(e.downloadUrl)return!0;const s=e.taskId;return s?g.value[s]?.task?.status==="SUCCESS":!1}function De(e){const s=e?.task?.id;if(!s)return!1;const t=d.value.find(l=>l.taskId===s);if(!t)return!1;const n=e?.items||[];let a=!1;const o=t.items.map(l=>{const i=n.find(re=>D(re,l));if(!i)return l;const f=Array.isArray(i.countries)?i.countries:void 0,_=Array.isArray(l.countries)?l.countries:void 0,k=f&&f.length>0?f:_&&_.length>0?_:f??_,y=Array.isArray(i.previewRows)?i.previewRows:void 0,w=Array.isArray(l.previewRows)?l.previewRows:void 0,I=y&&y.length>0?y:w&&w.length>0?w:y??w,S={...l,...i,countries:k,previewRows:I,_pushed:l._pushed,_completed:l._completed};return JSON.stringify(S)!==JSON.stringify(l)&&(a=!0),S});for(const l of n)o.some(i=>D(i,l))||(o.push({...l,_pushed:!1,_completed:!1}),a=!0);return JSON.stringify(o)!==JSON.stringify(t.items)&&(t.items=o,a=!0),a&&ee(),a}async function ne(e){const s=e||q();if(s.length)try{const t=await Xe(s);let n=!1;for(const a of t.items||[]){const o=a?.task?.id;typeof o=="number"&&o>0&&(g.value[o]=a,De(a)&&(n=!0),fe(o)&&(n=!0))}n&&R()}catch{}}function Me(){return document.visibilityState==="visible"?4500:1e4}function ae(e=!1){if(b.value){if(!e)return;clearTimeout(b.value),b.value=null}const s=async()=>{if(b.value=null,Y.value){ae();return}const t=q();if(!t.length)return;Y.value=!0;let n=!1,a=!1;try{await ne(t),await Oe();for(const o of t)ye(o)&&(ce(o),n=!0,a=!0)}finally{Y.value=!1}a?await L():n&&R(),q().length>0&&ae()};e?s():b.value=window.setTimeout(s,Me())}function le(e=!1){b.value&&!e||ae(e)}function X(e){const s=e.taskId?g.value[e.taskId]?.task?.status:null;return s==="SUCCESS"?{className:"success",text:"已完成"}:s==="FAILED"?{className:"failed",text:"失败"}:s==="RUNNING"?{className:"running",text:"执行中"}:e.taskStatus==="SUCCESS"?{className:"success",text:"已完成"}:e.taskStatus==="FAILED"?{className:"failed",text:"失败"}:e.taskStatus==="RUNNING"?{className:"running",text:"执行中"}:e.success?{className:"success",text:"已完成"}:{className:"failed",text:"失败"}}function me(){b.value&&(window.clearTimeout(b.value),b.value=null)}async function we(e){const s=H();if(!s?.upload_file_to_java)throw new Error("当前桌面端未提供文件上传桥接能力");const t=[];for(const n of e){const a=typeof n=="string"?n:n.absolutePath,o=typeof n=="string"?void 0:n.relativePath,l=await s.upload_file_to_java(a,o);if(!l?.success||!l.data)throw new Error(l?.error||l?.message||`上传失败:${a}`);t.push(l.data)}return t}async function Be(){const e=H();if(!e?.select_brand_xlsx_files){p.warning("当前环境不支持文件选择,请在本机客户端中打开");return}try{const s=await e.select_brand_xlsx_files();if(!s?.length)return;P.value=s,J.value=await we(s),p.success(`已选择 ${s.length} 个删除品牌文件`)}catch(s){p.error(s instanceof Error?s.message:"选择失败")}}async function Ue(){const e=H();if(!e?.select_brand_folder){p.warning("当前环境不支持文件夹选择,请在本机客户端中打开");return}try{const s=await e.select_brand_folder();if(!s)return;const t=await as(s);if(!t.success||!t.items?.length){p.warning(t.error||"该文件夹下没有 xlsx 文件");return}P.value=t.items.map(n=>n.relativePath||n.absolutePath),J.value=await we(t.items),p.success(`已选择文件夹内 ${t.items.length} 个 xlsx 文件`)}catch(s){p.error(s instanceof Error?s.message:"选择失败")}}async function qe(){if(!J.value.length){p.warning("请先选择待处理文件");return}try{B.value=!0;const e=await We({files:J.value.map(a=>({fileKey:a.fileKey,originalFilename:a.originalFilename,relativePath:a.relativePath}))}),s=pe(e.items||[]),t=s.some(a=>a.matchStatus==="MATCHED"),n=s.some(a=>a.matchStatus&&a.matchStatus!=="MATCHED");T.value="解析完成,等待手动点击打开紫鸟进行单任务处理",C.value="",s.length&&s[0].taskId&&ue(s[0].taskId,s,T.value,C.value),R(),n?p.warning("部分文件尚未命中可用店铺索引,已保留状态信息,请等待后台刷新后重试。"):t?p.success("删除品牌解析完成,请手动推送"):p.warning("当前没有可推送的已匹配文件,请先等待店铺索引刷新。"),await L()}catch(e){p.error(e instanceof Error?e.message:"执行失败")}finally{B.value=!1}}async function Ie(e){const s=e.taskId;if(!s){p.warning("未找到任务ID");return}const t=H(),n=Ye(s),a=e.outputFilename||e.sourceFilename||`delete-brand_${s}.xlsx`;if(t?.save_file_from_url_new){const o=await t.save_file_from_url_new(n,a);o.success?p.success(`已保存:${o.path||a}`):o.error&&o.error!=="用户取消"&&p.error(o.error);return}}async function L(){try{const e=await Ze();U.value=pe(e.items||[]),xe(U.value),R(),await ne(),le(!0)}catch(e){console.error("loadHistory error:",e)}}function be(e){d.value.forEach(t=>{t.items=t.items.filter(n=>n.resultId!==e)});const s=d.value.length;d.value=d.value.filter(t=>t.items.length>0),d.value.length!==s&&typeof window<"u"&&window.localStorage.setItem(M(),JSON.stringify(d.value)),U.value=U.value.filter(t=>t.resultId!==e),R(),q().length===0&&me()}async function Se(e){try{await es(e),be(e),await L(),p.success("已删除")}catch(s){const t=s instanceof Error?s.message:"删除失败";if(typeof t=="string"&&t.includes("记录不存在")){be(e),p.success("已删除");return}p.error(t)}}function Ce(e){const s=e.slice(0,5).map(n=>`${n.country}:${n.asin}${n.status?`(${n.status})`:""}`);return e.length-s.length>0?`${s.join("、")} 等 ${e.length} 条`:s.join("、")}function vt(e,s){}function Te(){for(const e of d.value)for(const s of e.items){const t=x(s);if(t==="已入队"||t==="处理中")return!0}return!1}function O(e){return e.resultId!=null?`result:${e.resultId}`:`task:${e.taskId||0}:${e.sourceFilename||""}`}function Le(){return F.value.find(e=>!e.openStoreUrl||O(e)===A.value||N(e.taskId,e)?._completed?!1:x(e)==="未入队")}async function Fe(e,s){if(s?.auto&&Te())return e.taskId,e.sourceFilename,!1;const t=H(),n=e.taskId;if(!n)return!1;const a=d.value.find(f=>f.taskId===n);if(!a)return!1;if(!t?.enqueue_json)return p.error("当前环境未启用 pywebview enqueue_json(浏览器环境不会推送)"),!1;const o={type:"delete-brand-run",ts:Date.now(),data:{taskId:n,items:[e]}};C.value=JSON.stringify(o,null,2);const l=await t.enqueue_json(o);let i="";if(l?.success){i=`已推送文件 ${e.sourceFilename},当前队列长度:${l.queue_size??"-"}`;const f=N(n,e);f&&(f._pushed=!0,f._completed=!1),A.value=O(e),ue(n,a.items,i,C.value),le(!0)}else i=`推送到 Python 队列失败:${l?.error||"未知错误"}`;return T.value=i,!!l?.success}async function Oe(){if(!$.value||j.value){$.value,j.value;return}if(Te())return;const e=A.value;if(e){const t=F.value.find(n=>O(n)===e);if(t){const n=t.taskId,a=E(n);a&&(fe(n),x(t),void 0);const o=x(t);if(o==="已入队"||o==="处理中"){t.sourceFilename;return}if(a){const l=N(n,t);if(l&&!l._completed){t.sourceFilename,l._pushed,l._completed;return}}}}const s=Le();if(!s){A.value="",$.value=!1;return}j.value=!0;try{s.taskId,s.sourceFilename,O(s),await Fe(s,{auto:!0})||(s.taskId,s.sourceFilename,$.value=!1)}finally{j.value=!1}}async function Ke(e){$.value=!0,await Fe(e)||($.value=!1,A.value="")}return Ge(()=>{Re();const e=q();e.length>0?ne(e).then(async()=>{let s=!1,t=!1;for(const n of e)ye(n)&&(ce(n),s=!0,t=!0);t?await L():s&&R(),le(!0)}):R(),L().catch(()=>{})}),je(()=>{me()}),(e,s)=>(u(),c("div",ls,[ze(Ve,{active:"delete-brand"}),r("div",rs,[r("aside",os,[s[2]||(s[2]=r("div",{class:"section-title"},"选择文件",-1)),r("div",is,[s[0]||(s[0]=r("div",{class:"hint"},"上传删除品牌 Excel;无论是直接上传文件还是上传文件夹批量导入,都会按每个 Excel 文件名作为店铺名。",-1)),r("div",{class:"btns"},[r("button",{type:"button",class:"opt-btn",onClick:Be},"选择 Excel 文件"),r("button",{type:"button",class:"opt-btn",onClick:Ue},"选择文件夹")]),r("div",us,[P.value.length?(u(),c(K,{key:0},[(u(!0),c(K,null,oe(Z.value,t=>(u(),c("span",{key:t},v(t),1))),128)),P.value.length>Z.value.length?(u(),c("span",cs," 还有 "+v(P.value.length-Z.value.length)+" 个文件未展开显示 ",1)):h("",!0)],64)):(u(),c("span",ds,"暂未选择删除品牌文件"))])]),s[3]||(s[3]=Qe('
执行说明
',2)),r("div",fs,[r("button",{type:"button",class:"btn-run",disabled:B.value,onClick:qe},v(B.value?"解析中...":"开始解析"),9,vs),r("span",hs,v(B.value?"正在读取删除品牌数据,请稍候…":"解析后会在右侧展示按国家分组的去重结果"),1)]),T.value||C.value?(u(),c("div",ps,[s[1]||(s[1]=r("div",{class:"section-title queue-debug-title"},"Python 队列推送结果",-1)),T.value?(u(),c("div",_s,v(T.value),1)):h("",!0),C.value?(u(),c("pre",gs,v(C.value),1)):h("",!0)])):h("",!0)]),r("section",ys,[s[12]||(s[12]=r("div",{class:"panel-header"},"删除品牌结果",-1)),r("div",ks,[r("div",ms,[r("div",ws,[s[4]||(s[4]=r("span",{class:"summary-label"},"已处理文件",-1)),r("strong",null,v(G.value.total),1)]),r("div",Is,[s[5]||(s[5]=r("span",{class:"summary-label"},"成功结果",-1)),r("strong",null,v(G.value.successCount),1)]),r("div",bs,[s[6]||(s[6]=r("span",{class:"summary-label"},"失败文件",-1)),r("strong",null,v(G.value.failedCount),1)])]),r("div",Ss,[s[11]||(s[11]=r("div",{class:"result-list-header"},[r("span",null,"删除品牌结果列表")],-1)),Pe.value?(u(),c(K,{key:1},[F.value.length?(u(),c("div",Ts,[s[8]||(s[8]=r("div",{class:"result-subsection-title"},"当前任务",-1)),r("ul",Fs,[(u(!0),c(K,null,oe(F.value,t=>(u(),c("li",{key:`current-${t.taskId||t.resultId||t.sourceFilename}`,class:"task-item split-result-item"},[r("div",Es,[r("span",{class:"id",title:t.sourceFilename},v(t.sourceFilename||"-"),9,Ns),r("div",Rs,"店铺名:"+v(t.shopName||"-"),1),r("div",xs,"匹配结果:"+v(_e(t)),1),t.platform?(u(),c("div",Ps,"平台:"+v(t.platform),1)):h("",!0),x(t)?(u(),c("div",$s,"队列状态:"+v(x(t)),1)):h("",!0),t.countryCount!==void 0&&t.matched?(u(),c("div",As,"国家数:"+v(t.countryCount),1)):h("",!0),t.totalRows!==void 0&&t.matched?(u(),c("div",Ds,"去重后 "+v(t.totalRows)+" 条 ",1)):h("",!0),he(t)?(u(),c("div",Ms,[r("div",Bs,[s[7]||(s[7]=r("span",null,"任务进度",-1)),r("span",null,v(Q(t))+"%",1)]),r("div",Us,[r("div",{class:"delete-brand-progress-bar-fill",style:Ee({width:`${Q(t)}%`})},null,4)]),r("div",qs,v(ge(t)),1)])):h("",!0),t.previewRows?.length?(u(),c("div",Ls,v(Ce(t.previewRows)),1)):h("",!0),V(t)?(u(),c("div",Os,"错误信息:"+v(V(t)),1)):h("",!0)]),r("div",Ks,[r("span",{class:Ne(["status",X(t).className])},v(X(t).text),3),t.openStoreUrl?(u(),c("button",{key:0,type:"button",class:"download",onClick:n=>Ke(t)}," 打开紫鸟 ",8,Hs)):h("",!0),ke(t)?(u(),c("button",{key:1,type:"button",class:"download",onClick:n=>Ie(t)}," 下载 ",8,Js)):h("",!0),t.resultId?(u(),c("button",{key:2,type:"button",class:"btn-delete",onClick:n=>Se(t.resultId)}," 删除 ",8,Gs)):h("",!0)])]))),128))])])):h("",!0),z.value.length?(u(),c("div",js,[s[10]||(s[10]=r("div",{class:"result-subsection-title"},"历史记录",-1)),r("ul",zs,[(u(!0),c(K,null,oe(z.value,t=>(u(),c("li",{key:`history-${t.taskId||t.resultId||t.sourceFilename}`,class:"task-item split-result-item"},[r("div",Vs,[r("span",{class:"id",title:t.sourceFilename},v(t.sourceFilename||"-"),9,Qs),r("div",Xs,"店铺名:"+v(t.shopName||"-"),1),r("div",Ws,"匹配结果:"+v(_e(t)),1),t.platform?(u(),c("div",Ys,"平台:"+v(t.platform),1)):h("",!0),t.countryCount!==void 0&&t.matched?(u(),c("div",Zs,"国家数:"+v(t.countryCount),1)):h("",!0),t.totalRows!==void 0&&t.matched?(u(),c("div",et,"去重后 "+v(t.totalRows)+" 条 ",1)):h("",!0),he(t)?(u(),c("div",st,[r("div",tt,[s[9]||(s[9]=r("span",null,"任务进度",-1)),r("span",null,v(Q(t))+"%",1)]),r("div",nt,[r("div",{class:"delete-brand-progress-bar-fill",style:Ee({width:`${Q(t)}%`})},null,4)]),r("div",at,v(ge(t)),1)])):h("",!0),t.previewRows?.length?(u(),c("div",lt,v(Ce(t.previewRows)),1)):h("",!0),V(t)?(u(),c("div",rt,"错误信息:"+v(V(t)),1)):h("",!0)]),r("div",ot,[r("span",{class:Ne(["status",X(t).className])},v(X(t).text),3),ke(t)?(u(),c("button",{key:0,type:"button",class:"download",onClick:n=>Ie(t)}," 下载 ",8,it)):h("",!0),t.resultId?(u(),c("button",{key:1,type:"button",class:"btn-delete",onClick:n=>Se(t.resultId)}," 删除 ",8,ut)):h("",!0)])]))),128))])])):h("",!0)],64)):(u(),c("div",Cs," 暂无删除品牌结果,完成解析后会在这里展示按国家分组的去重结果 "))])])])])]))}}),dt=ss(ct,[["__scopeId","data-v-beec339e"]]);ts(dt).use(ns).mount("#app"); diff --git a/app/new_web_source/delete-brand.html b/app/new_web_source/delete-brand.html index 9f4323e..f56cd3d 100644 --- a/app/new_web_source/delete-brand.html +++ b/app/new_web_source/delete-brand.html @@ -8,7 +8,7 @@ - +