2 lines
18 KiB
JavaScript
2 lines
18 KiB
JavaScript
import{d as Vt,o as Kt,L as zt,a as u,c as i,b as p,B as Jt,e as n,a5 as jt,a6 as Wt,t as l,V as b,W as Xt,X as Yt,f as m,U as Gt,n as Z,x as mt,F as tt,r as yt,g as v,h as O,a_ as Zt,a$ as te,b0 as ee,E as c,b1 as se,b2 as ae,b3 as ne,i as wt,b4 as oe,b5 as le,b6 as re,b7 as ue,b8 as ie,T as U,b9 as ce,_ as de,aE as fe,q as he,s as ve}from"./pywebview-C66x_2Dh.js";import{z as pe}from"./zh-cn-CuE9Zzgz.js";/* empty css *//* empty css */const me={class:"page-shell module-page"},ye={class:"main-content"},we={class:"left-panel"},_e={class:"input-zone"},ge={class:"input-row"},ke=["disabled"],be={key:0,class:"empty-candidates"},Ie={key:1,class:"candidate-table-scroll"},Se=["onClick"],Te={class:"run-row"},Ce=["disabled"],$e=["disabled"],Ee={key:2,class:"queue-debug-card"},De={class:"queue-debug-line"},Ne={key:0,class:"queue-debug-payload"},Pe={class:"right-panel"},Me={class:"task-list-wrap"},Re={class:"clean-result-summary"},Ae={class:"summary-card"},Qe={class:"summary-card"},qe={class:"summary-card"},Le={class:"summary-card"},Be={key:0,class:"empty-tasks narrow"},Fe=["onClick"],Oe={class:"result-list-wrap"},Ue={key:0,class:"empty-tasks"},He={key:0,class:"result-subsection"},xe={class:"task-list"},Ve={class:"left split-result-main"},Ke=["title"],ze={class:"files"},Je={key:0,class:"files"},je={key:1,class:"files"},We={key:2,class:"files"},Xe={class:"files"},Ye={class:"files"},Ge={key:3,class:"files"},Ze={class:"task-right"},ts=["onClick"],es={key:1,class:"result-subsection"},ss={class:"task-list"},as={class:"left split-result-main"},ns=["title"],os={class:"files"},ls={key:0,class:"files"},rs={key:1,class:"files"},us={key:2,class:"files"},is={class:"files"},cs={key:3,class:"files"},ds={class:"files"},fs={key:4,class:"files"},hs={class:"task-right"},vs=["onClick"],ps=["onClick"],_t=30,ms=Vt({__name:"BrandPatrolDeleteTab",setup(ws){const et=["德国","英国","法国","意大利","西班牙"],N=v(""),H=v([]),P=v([]),y=v([]),g=v([]),T=v({candidateCount:0,processedTaskCount:0,successTaskCount:0,failedTaskCount:0}),M=v(!1),R=v(!1),A=v(!1),w=v(""),x=v(""),f=v([]),h=v(null),C=v(null),Q=v(!1),V=v(!1);let I=null;const K=O(()=>y.value.filter(t=>t.matched)),q=O(()=>g.value.filter(t=>!ut(t.taskStatus))),z=O(()=>g.value.filter(t=>ut(t.taskStatus))),$=O(()=>Q.value||!!h.value||f.value.length>0);function J(){return typeof window<"u"&&window.localStorage.getItem("uid")||"0"}function st(){return`patrol-delete:matched:${J()}`}function at(){return`patrol-delete:queue-state:${J()}`}function _(t){return`${(t.shopName||"").trim()}::${t.shopId||""}`}function L(t){return`${t.taskId??0}:${t.resultId??0}`}function nt(t){return new Promise(e=>window.setTimeout(e,t))}function ot(t){const e=t instanceof Error?t.message.toLowerCase():String(t||"").toLowerCase();return e.includes("network error")||e.includes("failed to fetch")||e.includes("timeout")||e.includes("503")||e.includes("502")||e.includes("504")||e.includes("500")||e.includes("load failed")}async function gt(t,e,o=20){let a=0;for(;;)try{return await t()}catch(r){if(!ot(r)||a>=o-1)throw r;a+=1,e?.(a,o),await nt(U())}}function kt(t,e){return`patrol-delete:${t}:${e||"shop"}:${Date.now()}`}function j(t){if(!t)return"-";const e=new Date(t);if(Number.isNaN(e.getTime()))return t;const o=e.getFullYear(),a=String(e.getMonth()+1).padStart(2,"0"),r=String(e.getDate()).padStart(2,"0"),s=String(e.getHours()).padStart(2,"0"),d=String(e.getMinutes()).padStart(2,"0"),k=String(e.getSeconds()).padStart(2,"0");return`${o}-${a}-${r} ${s}:${d}:${k}`}function bt(t){const e=(t||"").trim();return{MATCHED:"已匹配",PENDING:"待匹配",CONFLICT:"需人工确认",INDEX_STALE:"索引过期"}[e]||e||"-"}function It(t){const e=(t.matchMessage||"").trim();return e||(t.matched&&t.matchStatus==="MATCHED"?"索引已命中,可推送到 Python 队列":t.matched?"已命中索引,请结合状态列确认":"未命中或未就绪,请检查店铺名")}function St(){return et.map(t=>({country:t,rows:[{status:"全部",quantity:"",deleteQuantity:"",processStatus:""}]}))}function Tt(){return et.map(t=>({country:t,ratio:""}))}function lt(t){const e=t.countrySections||[],o=t.cartRatios||[],a=Math.max(...e.map(r=>r.rows.length),1);return Array.from({length:a},(r,s)=>({shopName:s===0?t.shopName:"",countries:e.map(d=>{const k=d.rows[s];return{country:d.country,status:k?.status||"",quantity:k?.quantity||"",deleteQuantity:k?.deleteQuantity||"",processStatus:k?.processStatus||""}}),cartRatios:s===0?o.map(d=>({country:d.country,ratio:d.ratio})):[]}))}function rt(t){return`${lt(t).length} 行状态数据,首行含 ${t.cartRatios?.length||0} 个购物车比例列`}function ut(t){return t==="SUCCESS"||t==="FAILED"||t==="COMPLETED"}function it(t){return t==="SUCCESS"||t==="COMPLETED"?"已完成":t==="FAILED"?"失败":"执行中"}function ct(t){return t==="SUCCESS"||t==="COMPLETED"?"success":t==="FAILED"?"failed":"running"}function Ct(t){return!!(t.resultId&&(t.fileReady||t.downloadUrl))}function W(){typeof window>"u"||window.localStorage.setItem(st(),JSON.stringify(y.value))}function $t(){try{const t=typeof window<"u"?window.localStorage.getItem(st()):null;y.value=t?JSON.parse(t):[]}catch{y.value=[]}}function S(){if(typeof window>"u")return;const t={pendingQueue:f.value,activeTaskId:h.value,activeQueueItem:C.value};window.localStorage.setItem(at(),JSON.stringify(t))}function Et(){try{const t=typeof window<"u"?window.localStorage.getItem(at()):null;if(!t)return;const e=JSON.parse(t);f.value=e.pendingQueue||[],h.value=e.activeTaskId??null,C.value=e.activeQueueItem??null}catch{f.value=[],h.value=null,C.value=null}}function B(){h.value=null,C.value=null,S()}function Dt(t){const e=t instanceof Error?t.message:String(t||"");return/记录不存在|任务不存在|不存在|已删除|not\s*found|404/i.test(e)}function dt(t){y.value=y.value.filter(e=>_(e)!==_(t)),W()}function Nt(t){g.value=g.value.filter(e=>!(t.resultId!=null&&e.resultId===t.resultId||t.taskId!=null&&e.taskId===t.taskId))}function ft(t,e){const o=new Map;for(const a of t)o.set(_(a),a);for(const a of e)o.set(_(a),a);return Array.from(o.values())}async function E(){H.value=await Zt()}async function F(){T.value=await te()}async function X(){const t=await ee();g.value=t.items||[]}async function D(){await Promise.all([F(),X()])}function Pt(t){if(!t.length)return;const e=new Map(t.map(a=>[L(a),a])),o=g.value.map(a=>{const r=e.get(L(a));return r?{...a,...r,countrySections:r.countrySections||a.countrySections||[],cartRatios:r.cartRatios||a.cartRatios||[]}:a});for(const a of t){const r=L(a);o.some(s=>L(s)===r)||o.push(a)}g.value=o}async function ht(t){const e=Array.from(new Set((t&&t.length?t:q.value.map(a=>a.taskId??0)).filter(a=>Number.isFinite(a)&&a>0)));if(!e.length)return;const o=await ce(e);Pt(o.items||[]),(o.missingTaskIds||[]).length&&await X()}function Mt(){if(Y(),!$.value)return;const t=async()=>{I=null,$.value&&(await ht(),$.value&&(I=window.setTimeout(t,U()*2)))};I=window.setTimeout(t,U()*2)}function Y(){I&&(window.clearTimeout(I),I=null)}function Rt(t){P.value=t}async function vt(){const t=N.value.trim();if(!t){c.warning("请输入店铺名");return}M.value=!0;try{await se(t),N.value="",await Promise.all([E(),F()]),c.success("已加入备选区")}catch(e){c.error(e instanceof Error?e.message:"添加失败")}finally{M.value=!1}}async function At(t){try{await ae(t),P.value=P.value.filter(e=>e.id!==t),await Promise.all([E(),F()]),c.success("已删除")}catch(e){c.error(e instanceof Error?e.message:"删除失败")}}function Qt(t,e){const o=new Map;for(const a of t)o.set(_(a),a);for(const a of e)o.set(_(a),a);return Array.from(o.values())}async function qt(){const t=P.value.map(e=>e.shop_name).filter(Boolean);if(!t.length){c.warning("请先勾选备选区店铺");return}R.value=!0;try{const e=await ne(t);y.value=Qt(y.value,e.items||[]),W(),V.value&&(f.value=ft(f.value,K.value),S(),G()),c.success(`本次返回 ${e.items?.length||0} 条匹配结果`)}catch(e){c.error(e instanceof Error?e.message:"匹配失败")}finally{R.value=!1}}function Lt(t){if($.value){c.warning("队列执行中,请等待当前串行任务结束后再调整");return}y.value=y.value.filter(e=>_(e)!==_(t)),W()}function Bt(t){return{shopName:t.shopName,matched:t.matched,shopId:t.shopId,platform:t.platform,companyName:t.companyName,matchStatus:t.matchStatus,matchMessage:t.matchMessage,countrySections:St(),cartRatios:Tt()}}function Ft(t,e){return{type:"patrol-delete-run",ts:Date.now(),data:{taskId:t,user_id:Number(J())||0,source:"frontend-vue-patrol-delete",items:[{shopName:e.shopName,shopId:e.shopId,platform:e.platform,companyName:e.companyName,matched:e.matched,matchStatus:e.matchStatus,matchMessage:e.matchMessage}],template_rows:lt(e),country_sections:e.countrySections||[],cart_ratios:e.cartRatios||[]}}}function Ot(t){return g.value.find(e=>e.taskId===t)}async function Ut(t){let e=0;for(;;){try{await ht([t]),e>0&&(w.value=`任务 ${t} 后端已恢复,继续等待执行结果...`),e=0;const a=Ot(t)?.taskStatus||"";if(a==="SUCCESS"||a==="FAILED"||a==="COMPLETED")return a}catch(o){if(!ot(o))throw o;if(e+=1,e>=_t)throw new Error(`任务 ${t} 等待后端恢复超时,请稍后刷新查看状态`);w.value=`任务 ${t} 执行中,正在等待后端服务恢复(${e}/${_t})...`}await nt(U())}}async function G(){if(!Q.value){Q.value=!0,A.value=!0,Mt();try{const t=wt();if(!t?.enqueue_json)throw new Error("当前客户端未提供 enqueue_json");for(;h.value||f.value.length;){if(h.value){const d=await Ut(h.value);w.value=f.value.length>0?`任务 ${h.value} ${d==="SUCCESS"?"已完成":"执行失败"},自动继续下一个(剩余 ${f.value.length} 条)`:`任务 ${h.value} ${d==="SUCCESS"?"已完成":"执行失败"}`,B();continue}const e=f.value.shift();if(S(),!e)break;const o=await gt(()=>oe([Bt(e)]),(d,k)=>{w.value=`后端服务暂时不可用,正在重试创建任务(${d}/${k})...`}),a=o.items?.[0];if(!a?.taskId||!a.resultId)throw new Error("后端未返回有效任务标识");h.value=o.taskId,C.value=e,S(),await D();const r=Ft(o.taskId,a);x.value=JSON.stringify(r,null,2);const s=await t.enqueue_json(r);if(!s?.success){await le(o.taskId,{shops:[{shopName:a.shopName||e.shopName||"",error:s?.error||`任务 ${o.taskId} 推送失败`,countrySections:a.countrySections||[],cartRatios:a.cartRatios||[],shopDone:!0,submissionId:kt(o.taskId,a.shopName||e.shopName),chunkIndex:1,chunkTotal:1}]}),dt(e),await D(),w.value=`任务 ${o.taskId} 推送失败,已自动继续下一个任务`,B();continue}dt(e),w.value=f.value.length>0?`任务 ${o.taskId} 已入队,等待完成后自动继续下一个(剩余 ${f.value.length} 条)`:`任务 ${o.taskId} 已入队,等待执行完成`}w.value="串行队列已执行完成",await D(),c.success("巡店删除队列已按顺序执行完成")}catch(t){const e=t instanceof Error?t.message:"队列执行失败";w.value=e,c.error(e),await D()}finally{Q.value=!1,A.value=!1,Y(),S()}}}async function Ht(){V.value=!0;const t=K.value;if(!t.length){c.warning("请先匹配可用店铺");return}f.value=ft(f.value,t),S(),w.value=`已加入 ${t.length} 条店铺,开始串行执行`,await G()}async function xt(t){if(!t.resultId)return;const e=wt();if(!e?.save_file_from_url_new){c.error("当前客户端未提供下载能力");return}const o=re(t.resultId),a=t.outputFilename||`${t.shopName||"result"}.xlsx`,r=await e.save_file_from_url_new(o,a);r.success?c.success(`已保存: ${r.path||a}`):r.error&&r.error!=="用户取消"&&c.error(r.error)}async function pt(t){try{if(t.taskId)await ue(t.taskId),h.value===t.taskId&&B();else if(t.resultId)await ie(t.resultId);else throw new Error("缺少可删除的任务标识");await Promise.all([E(),D()]),c.success("已删除")}catch(e){if(Dt(e)){t.taskId&&h.value===t.taskId&&B(),Nt(t),await E(),c.success("后端记录已不存在,已同步移除本地记录");return}c.error(e instanceof Error?e.message:"删除失败")}}return Kt(async()=>{$t(),Et(),await Promise.all([E(),F(),X()]),(h.value||f.value.length)&&(V.value=!0,w.value=h.value!=null?`检测到未完成队列,继续等待任务 ${h.value} 完成并自动接续后续店铺`:`检测到未完成队列,继续执行剩余 ${f.value.length} 条店铺`,G())}),zt(()=>{Y()}),(t,e)=>{const o=Wt,a=Xt,r=Yt;return u(),i("div",me,[p(Jt,{active:"patrol-delete"}),n("div",ye,[n("aside",we,[e[3]||(e[3]=n("div",{class:"section-title"},"店铺输入",-1)),n("div",_e,[e[1]||(e[1]=n("div",{class:"hint"}," 左侧负责录入店铺并加入备选区,确认命中后推送到 Python 队列。巡店删除按店铺串行执行:上一个任务完成后会自动开始下一个,失败也会继续下一个。 ",-1)),n("div",ge,[p(o,{modelValue:N.value,"onUpdate:modelValue":e[0]||(e[0]=s=>N.value=s),clearable:"",placeholder:"请输入店铺名后回车或点击添加",onKeyup:jt(vt,["enter"])},null,8,["modelValue"]),n("button",{type:"button",class:"opt-btn",disabled:M.value,onClick:vt},l(M.value?"添加中...":"添加"),9,ke)])]),e[4]||(e[4]=n("div",{class:"section-title"},"备选区",-1)),H.value.length?(u(),i("div",Ie,[p(r,{data:H.value,"row-key":"id",height:"260",class:"candidate-table",onSelectionChange:Rt},{default:b(()=>[p(a,{type:"selection",width:"42"}),p(a,{prop:"shop_name",label:"店铺名","min-width":"140","show-overflow-tooltip":""}),p(a,{label:"操作",width:"72",align:"center"},{default:b(({row:s})=>[n("button",{type:"button",class:"link-danger",onClick:d=>At(s.id)}," 删除 ",8,Se)]),_:1})]),_:1},8,["data"])])):(u(),i("div",be," 暂无备选店铺,请先输入并添加 ")),n("div",Te,[n("button",{type:"button",class:"btn-run",disabled:R.value||$.value,onClick:qt},l(R.value?"匹配中...":"匹配店铺"),9,Ce),n("button",{type:"button",class:"btn-run btn-queue",disabled:A.value||!K.value.length,onClick:Ht},l(A.value?"串行执行中...":"推送到 Python 队列"),9,$e)]),e[5]||(e[5]=n("p",{class:"loading-msg"}," 当前页面会按店铺串行推送任务,并在任务成功或失败后自动接续下一个任务。服务端临时重启时会自动重试查询状态,恢复后继续后续店铺。 ",-1)),w.value?(u(),i("div",Ee,[e[2]||(e[2]=n("div",{class:"section-title queue-debug-title"},"队列状态",-1)),n("div",De,l(w.value),1),x.value?(u(),i("pre",Ne,l(x.value),1)):m("",!0)])):m("",!0)]),n("section",Pe,[e[14]||(e[14]=n("div",{class:"panel-header"},"匹配与任务",-1)),n("div",Me,[n("div",Re,[n("div",Ae,[e[6]||(e[6]=n("span",{class:"summary-label"},"备选店铺",-1)),n("strong",null,l(T.value.candidateCount),1)]),n("div",Qe,[e[7]||(e[7]=n("span",{class:"summary-label"},"已处理任务",-1)),n("strong",null,l(T.value.processedTaskCount),1)]),n("div",qe,[e[8]||(e[8]=n("span",{class:"summary-label"},"成功任务",-1)),n("strong",null,l(T.value.successTaskCount),1)]),n("div",Le,[e[9]||(e[9]=n("span",{class:"summary-label"},"失败任务",-1)),n("strong",null,l(T.value.failedTaskCount),1)])]),e[13]||(e[13]=n("div",{class:"subsection-title"},"匹配结果",-1)),y.value.length?(u(),Gt(r,{key:1,data:y.value,"row-key":_,"highlight-current-row":!1,class:"result-table match-table"},{default:b(()=>[p(a,{prop:"shopName",label:"店铺名","min-width":"120"}),p(a,{label:"匹配",width:"72",align:"center"},{default:b(({row:s})=>[n("span",{class:Z(s.matched?"ok":"fail")},l(s.matched?"是":"否"),3)]),_:1}),p(a,{prop:"shopId",label:"店铺 ID","min-width":"120","show-overflow-tooltip":""}),p(a,{prop:"platform",label:"平台",width:"88","show-overflow-tooltip":""}),p(a,{prop:"companyName",label:"公司","min-width":"120","show-overflow-tooltip":""}),p(a,{label:"状态",width:"110","show-overflow-tooltip":""},{default:b(({row:s})=>[mt(l(bt(s.matchStatus)),1)]),_:1}),p(a,{label:"说明","min-width":"180","show-overflow-tooltip":""},{default:b(({row:s})=>[mt(l(It(s)),1)]),_:1}),p(a,{label:"操作",width:"72",align:"center"},{default:b(({row:s})=>[n("button",{type:"button",class:"link-danger",onClick:d=>Lt(s)}," 删除 ",8,Fe)]),_:1})]),_:1},8,["data"])):(u(),i("div",Be," 完成“匹配店铺”后,这里会展示匹配结果。 ")),n("div",Oe,[e[12]||(e[12]=n("div",{class:"result-list-header"},[n("span",null,"任务记录")],-1)),!q.value.length&&!z.value.length?(u(),i("div",Ue," 暂无任务记录。推送到 Python 队列后,这里会展示当前任务和历史任务。 ")):(u(),i(tt,{key:1},[q.value.length?(u(),i("div",He,[e[10]||(e[10]=n("div",{class:"result-subsection-title"},"当前任务",-1)),n("ul",xe,[(u(!0),i(tt,null,yt(q.value,s=>(u(),i("li",{key:`${s.resultId}-${s.taskId}`,class:"task-item"},[n("div",Ve,[n("span",{class:"id",title:s.shopName||""},l(s.shopName||"-"),9,Ke),n("div",ze,"任务 ID: "+l(s.taskId??"-"),1),s.resultId?(u(),i("div",Je," 结果 ID: "+l(s.resultId),1)):m("",!0),s.shopId?(u(),i("div",je," 店铺 ID: "+l(s.shopId),1)):m("",!0),s.platform?(u(),i("div",We," 平台: "+l(s.platform),1)):m("",!0),n("div",Xe," 创建时间: "+l(j(s.createdAt)),1),n("div",Ye," 模板结构: "+l(rt(s)),1),s.error?(u(),i("div",Ge," 错误: "+l(s.error),1)):m("",!0)]),n("div",Ze,[n("span",{class:Z(["status",ct(s.taskStatus)])},l(it(s.taskStatus)),3),n("button",{type:"button",class:"btn-delete",onClick:d=>pt(s)}," 删除 ",8,ts)])]))),128))])])):m("",!0),z.value.length?(u(),i("div",es,[e[11]||(e[11]=n("div",{class:"result-subsection-title"},"历史任务",-1)),n("ul",ss,[(u(!0),i(tt,null,yt(z.value,s=>(u(),i("li",{key:`${s.resultId}-${s.taskId}`,class:"task-item"},[n("div",as,[n("span",{class:"id",title:s.shopName||""},l(s.shopName||"-"),9,ns),n("div",os,"任务 ID: "+l(s.taskId??"-"),1),s.resultId?(u(),i("div",ls," 结果 ID: "+l(s.resultId),1)):m("",!0),s.shopId?(u(),i("div",rs," 店铺 ID: "+l(s.shopId),1)):m("",!0),s.platform?(u(),i("div",us," 平台: "+l(s.platform),1)):m("",!0),n("div",is," 创建时间: "+l(j(s.createdAt)),1),s.finishedAt?(u(),i("div",cs," 完成时间: "+l(j(s.finishedAt)),1)):m("",!0),n("div",ds," 模板结构: "+l(rt(s)),1),s.error?(u(),i("div",fs," 错误: "+l(s.error),1)):m("",!0)]),n("div",hs,[n("span",{class:Z(["status",ct(s.taskStatus)])},l(it(s.taskStatus)),3),Ct(s)?(u(),i("button",{key:0,type:"button",class:"archive",onClick:d=>xt(s)}," 下载结果 ",8,vs)):m("",!0),n("button",{type:"button",class:"btn-delete",onClick:d=>pt(s)}," 删除 ",8,ps)])]))),128))])])):m("",!0)],64))])])])])])}}}),ys=de(ms,[["__scopeId","data-v-52605d65"]]);fe.locale("zh-cn");he(ys).use(ve,{locale:pe}).mount("#app");
|