new file: assets/_plugin-vue_export-helper-xvHHTGU_.css new file: assets/appearance-patent-BNTrbLJh.css new file: assets/appearance-patent-BPCrG75P.css new file: assets/appearance-patent.js new file: assets/brand-1nUVsWrm.js new file: assets/brand-5-W0Wonr.js new file: assets/brand-BATjYfEL.js new file: assets/brand-BZije8D7.js new file: assets/brand-C0Q5LZ0P.js new file: assets/brand-CFZZtTv8.js new file: assets/brand-CKgrwtni.js new file: assets/brand-CRmRvyGD.js new file: assets/brand-CXHemZwJ.js new file: assets/brand-DBpLCgAF.js new file: assets/brand-DRn3N4FA.js new file: assets/brand-DZ5_PGnw.js new file: assets/brand-DysFlQbZ.js new file: assets/brand-UU-ckLq6.js new file: assets/convert-1dkFPbKa.css new file: assets/convert-6b0yq-M0.css new file: assets/convert-7wWJ02Tw.css new file: assets/convert-BKSNvX8i.css new file: assets/convert-BxkpMHO9.css new file: assets/convert-CQ22EBpm.css new file: assets/convert-Cfr3mUjs.css new file: assets/convert-D4Yv2oRy.css new file: assets/convert-DwbQug2y.css new file: assets/dedupe-B0eqpVpj.css new file: assets/dedupe-BHE_BCAJ.css new file: assets/dedupe-BpNHwt51.css new file: assets/dedupe-CS18ls2z.css new file: assets/dedupe-DNlVfFj-.css new file: assets/dedupe-Day_nGxq.css new file: assets/dedupe-DyzjKbsT.css new file: assets/dedupe-DzFvTfoj.css new file: assets/dedupe-rhGu1E8E.css new file: assets/delete-brand-BNvh4ru4.css new file: assets/delete-brand-BP3XWKAC.css new file: assets/delete-brand-BRIDBaoM.css new file: assets/delete-brand-BcZomrN3.css new file: assets/delete-brand-BgPrpPsj.css new file: assets/delete-brand-ByeMdpEk.css new file: assets/delete-brand-C-Rj3NMH.css new file: assets/delete-brand-CDZTH98q.css new file: assets/delete-brand-CS44Bk-y.css new file: assets/delete-brand-CWLpe7lu.css new file: assets/delete-brand-CdxGWtvK.css new file: assets/delete-brand-Chu8awlt.css new file: assets/delete-brand-CndjrFFS.css new file: assets/delete-brand-CpNTe9WM.css new file: assets/delete-brand-D7t16Ukk.css new file: assets/delete-brand-DE0hLEak.css new file: assets/delete-brand-DLyg-H8J.css new file: assets/delete-brand-DO6pdlC3.css new file: assets/delete-brand-DXSeQxXc.css new file: assets/delete-brand-DZ6zYt8o.css new file: assets/delete-brand-DfRLfrtm.css new file: assets/delete-brand-Dl7T0pmm.css new file: assets/delete-brand-Dt_MS9as.css new file: assets/delete-brand-DvND7vBL.css new file: assets/delete-brand-_jGFWTAn.css new file: assets/el-alert-Ct0RpqUD.css new file: assets/el-input-CezJelDw.css new file: assets/el-input-xaztPnzw.css new file: assets/el-table-column-Cy4YJvw0.css new file: assets/listingFilters-B6-M_FNM.js new file: assets/listingFilters-BmlAzw7M.css new file: assets/listingFilters-BpGOU_pJ.js new file: assets/listingFilters-CK58rX4v.css new file: assets/patrol-delete-BPu1AAPM.css new file: assets/patrol-delete-BctopIU4.css new file: assets/patrol-delete-CSekhzSS.css new file: assets/patrol-delete-DIvFZUTh.css new file: assets/patrol-delete.js new file: assets/price-track-1SjcUfP4.css new file: assets/price-track-85NbckiJ.css new file: assets/price-track-B5FVfJb_.css new file: assets/price-track-BO5PtL0W.css new file: assets/price-track-BccJT4Qr.css new file: assets/price-track-BlSD-0un.css new file: assets/price-track-BlVVl2eV.css new file: assets/price-track-BwgEuRxe.css new file: assets/price-track-CWQD_A7D.css new file: assets/price-track-DNTcdRH8.css new file: assets/price-track-Dv4EZPhf.css new file: assets/price-track-Ji5JAoeR.css new file: assets/price-track-RLJUgMl_.css new file: assets/price-track-z8RU_FlW.css new file: assets/price-track.js new file: assets/product-risk-1G_ztqNY.css new file: assets/product-risk-A5bhfaTH.css new file: assets/product-risk-BBMcuGAw.css new file: assets/product-risk-C3Yn2wAL.css new file: assets/product-risk-CG-1Iq4P.css new file: assets/product-risk-COeM91v5.css new file: assets/product-risk-ClWs7d57.css new file: assets/product-risk-CtpNfape.css new file: assets/product-risk-DBsBE_mg.css new file: assets/product-risk-DRYaWmI2.css new file: assets/product-risk-Db49F4J5.css new file: assets/product-risk-DoO8ZHM0.css new file: assets/product-risk-DpSMKBCA.css new file: assets/product-risk-DqFPFWEv.css new file: assets/product-risk-cfL3Ch5-.css new file: assets/product-risk-eXMv239F.css new file: assets/product-risk-un2cxu--.css new file: assets/product-risk.js new file: assets/pywebview-3PePjYSu.js new file: assets/pywebview-8G_9KMXd.css new file: assets/pywebview-9YBa--7x.js new file: assets/pywebview-B6ZNXusn.css new file: assets/pywebview-BCPdlDdb.js new file: assets/pywebview-BLoeiUcF.css new file: assets/pywebview-BRrok0pS.js new file: assets/pywebview-BT0f6KST.css new file: assets/pywebview-BbIq-QPS.css new file: assets/pywebview-Bc09DixN.js new file: assets/pywebview-Bl6M97uH.js new file: assets/pywebview-BnpJyyGZ.js new file: assets/pywebview-Bt854mYs.js new file: assets/pywebview-C5RGB8QW.css new file: assets/pywebview-C66x_2Dh.js new file: assets/pywebview-C8Bik-Sc.js new file: assets/pywebview-CGwF8TuM.js new file: assets/pywebview-CJLylbJu.js new file: assets/pywebview-CeWJDVeG.js new file: assets/pywebview-Chz98E9c.css new file: assets/pywebview-ClWy2SbE.js new file: assets/pywebview-Cq_E2BnJ.js new file: assets/pywebview-Cs1Kot1q.js new file: assets/pywebview-D-PKWjUg.js new file: assets/pywebview-D-mMH8F6.css new file: assets/pywebview-D4gpiFjY.js new file: assets/pywebview-D74jYk6P.css new file: assets/pywebview-D7_PdvyM.js new file: assets/pywebview-D808cNhy.js new file: assets/pywebview-DVgP2-kW.js new file: assets/pywebview-Dee3nQjE.js new file: assets/pywebview-DiP0HdY6.js new file: assets/pywebview-Dp5dN8OO.css new file: assets/pywebview-DuyK2jB1.js new file: assets/pywebview-IqgMfeBe.css new file: assets/pywebview-MkjZQlBu.css new file: assets/pywebview-Q4glvYu5.css new file: assets/pywebview-Rm5Hhqpf.js new file: assets/pywebview-V_UIajJm.js new file: assets/pywebview-hj_EXwAP.css new file: assets/pywebview-ij4pgMq8.css new file: assets/pywebview-leZCedEt.js new file: assets/query-asin-B0yMJ9Or.css new file: assets/query-asin-B5e5m-vs.css new file: assets/query-asin-Bv7Jtgqa.css new file: assets/query-asin-DZwJfwHU.css new file: assets/query-asin.js new file: assets/shop-match-Banz7DWL.css new file: assets/shop-match-BiZP8IGM.css new file: assets/shop-match-BrrGkYV7.css new file: assets/shop-match-CVFsXUvd.css new file: assets/shop-match-CfGJEHz0.css new file: assets/shop-match-CfGWwDtw.css new file: assets/shop-match-D0DGETCs.css new file: assets/shop-match-DFEKOpvV.css new file: assets/shop-match-DNFA8ceR.css new file: assets/shop-match-DhT1CvMP.css new file: assets/shop-match-L0xHTaQ2.css new file: assets/shop-match-MsxXMG_G.css new file: assets/shop-match-g3eRKhTP.css new file: assets/shop-match.js new file: assets/split-BoBVrLdC.css new file: assets/split-BsJfbBqn.css new file: assets/split-CRUIYKS6.css new file: assets/split-DAa_Usoh.css new file: assets/split-DidKJ84l.css new file: assets/split-RNZYJ-zZ.css new file: assets/split-R_8V5xgh.css new file: assets/split-_O6NHqDj.css new file: assets/split-mwLUJT1K.css new file: assets/zh-cn-BjWx61Lu.js new file: assets/zh-cn-C0iS6aoJ.js new file: assets/zh-cn-CuE9Zzgz.js new file: assets/zh-cn-DyKmB71i.js new file: assets/zh-cn-Z9PBnl-n.js
2 lines
30 KiB
JavaScript
2 lines
30 KiB
JavaScript
import{d as ls,g as p,o as rs,L as us,a as i,c,b,B as is,e as l,a5 as cs,a6 as ds,t as v,V as M,W as fs,X as hs,F as U,r as q,a7 as pt,n as fe,f as T,a8 as vs,U as gt,a9 as ms,aa as ps,w as gs,v as ys,ap as ws,x as he,h as ve,aq as Ss,ar as ks,as as bs,at as _s,E as m,au as Ts,av as Cs,aw as Es,i as Ae,ax as Is,ay as $s,az as Ds,aA as Ns,aB as Ms,aC as Le,aD as Ps,T as xs,_ as As,aE as Ls,q as Fs,s as Us}from"./pywebview-C66x_2Dh.js";import{z as Rs}from"./zh-cn-CuE9Zzgz.js";/* empty css *//* empty css */import{L as Os}from"./listingFilters-BpGOU_pJ.js";const Vs={class:"page-shell module-page"},Hs={class:"main-content"},Bs={class:"left-panel"},qs={class:"input-zone"},js={class:"input-row"},Ks=["disabled"],zs={key:0,class:"empty-candidates"},Gs={key:1,class:"candidate-table-scroll"},Js=["onClick"],Ys={class:"country-pref-checks"},Qs=["checked","disabled","onChange"],Ws={class:"country-check-text"},Xs={key:2,class:"country-order-panel"},Zs={class:"country-order-list"},ea=["onDragstart","onDrop"],ta={class:"country-drag-label"},sa={key:3,class:"country-pref-status"},aa={class:"listing-filter-row"},na={class:"schedule-switch"},oa=["disabled"],la={key:4,class:"schedule-config"},ra=["disabled","onClick"],ua=["disabled"],ia={class:"run-row"},ca=["disabled"],da=["disabled"],fa={key:5,class:"queue-debug-card"},ha={class:"queue-debug-line"},va={key:0,class:"queue-debug-payload"},ma={class:"right-panel"},pa={class:"task-list-wrap"},ga={class:"clean-result-summary"},ya={class:"summary-card"},wa={class:"summary-card"},Sa={class:"summary-card"},ka={class:"summary-card"},ba={key:0,class:"empty-tasks narrow"},_a=["onClick"],Ta={class:"result-list-wrap"},Ca={key:0,class:"empty-tasks"},Ea={key:0,class:"result-subsection"},Ia={class:"task-list"},$a={class:"left split-result-main"},Da=["title"],Na={class:"files"},Ma={key:0,class:"files"},Pa={key:1,class:"files"},xa={key:2,class:"files"},Aa={key:3,class:"files"},La={class:"task-right"},Fa=["onClick"],Ua=["onClick"],Ra={key:1,class:"result-subsection"},Oa={class:"task-list"},Va={class:"left split-result-main"},Ha=["title"],Ba={class:"files"},qa={key:0,class:"files"},ja={key:1,class:"files"},Ka={class:"task-right"},za=["onClick"],Ga=["onClick"],Ja=ls({__name:"BrandShopMatchTab",setup(Qa){const me=[{code:"DE",label:"德国"},{code:"UK",label:"英国"},{code:"FR",label:"法国"},{code:"IT",label:"意大利"},{code:"ES",label:"西班牙"}],te=p(""),pe=p([]),Fe=p([]),_=p([]),ge=p("Active"),se=p(!1),ae=p(!1),D=p(!1),ye=p(!1),we=p(!1),w=p(""),j=p(""),g=p(["DE","UK","FR","IT","ES"]),K=p(null),Se=p(!1),ke=p(!1);let R=null;const N=p(!0),S=p([Ee()]),z=p({candidateCount:0,processedTaskCount:0,successTaskCount:0,failedTaskCount:0}),E=p([]),y=p([]),k=p({}),d=p({}),$=p(null),be=p(!1),O=new Map,_e=p(!1);let G=null,Ue=0,Re="";const Te=ve(()=>ze(E.value.filter(e=>!!C(e.taskId)&&!Qe(e)),d.value,"current")),Ce=ve(()=>ze(E.value.filter(e=>!C(e.taskId)||Qe(e)),d.value,"history")),yt=ve(()=>Te.value.length>0||Ce.value.length>0),wt=ve(()=>{const e=new Set(g.value);return[...g.value.map(t=>({code:t,label:je(t)})),...me.filter(t=>!e.has(t.code)).map(t=>({code:t.code,label:t.label}))]});function J(){return typeof window<"u"&&window.localStorage.getItem("uid")||"0"}function St(){const e=Number(J());return Number.isFinite(e)&&e>0?e:0}function C(e){const t=Number(e);return Number.isFinite(t)&&t>0?t:0}function Oe(){return`shop-match:tasks:${J()}`}function Ve(){return`shop-match:task-status:${J()}`}function He(){return`shop-match:task-snapshots:${J()}`}function Be(){return`shop-match:matched-items:${J()}`}function ne(e,t,s){s?window.localStorage.removeItem(e):window.localStorage.setItem(e,JSON.stringify(t))}function P(e){return`${(e.shopName||"").trim()}::${e.shopId||""}`}function qe(e){return`${C(e.taskId)}-${e.resultId||0}-${e.shopName||""}`}function je(e){return me.find(t=>t.code===e)?.label||e}function kt(e){const t=e.getFullYear(),s=String(e.getMonth()+1).padStart(2,"0"),a=String(e.getDate()).padStart(2,"0"),n=String(e.getHours()).padStart(2,"0"),u=String(e.getMinutes()).padStart(2,"0"),r=String(e.getSeconds()).padStart(2,"0");return`${t}-${s}-${a}T${n}:${u}:${r}`}function oe(e){const t=e.getFullYear(),s=String(e.getMonth()+1).padStart(2,"0"),a=String(e.getDate()).padStart(2,"0"),n=String(e.getHours()).padStart(2,"0"),u=String(e.getMinutes()).padStart(2,"0");return`${t}-${s}-${a} ${n}:${u}:00`}function Ee(){const e=new Date;return e.setSeconds(0,0),e.setMinutes(e.getMinutes()+5),oe(e)}function Y(e){if(!e)return null;const s=e.trim().replace("T"," ").match(/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2})(?::(\d{2}))?$/);if(!s)return null;const a=Number(s[1]),n=Number(s[2]),u=Number(s[3]),r=Number(s[4]),f=Number(s[5]),o=Number(s[6]||"0"),h=new Date(a,n-1,u,r,f,o,0);return Number.isNaN(h.getTime())||h.getFullYear()!==a||h.getMonth()!==n-1||h.getDate()!==u?null:h}function bt(e,t){const s=Y(e)||new Date;return s.setSeconds(0,0),s.setMinutes(s.getMinutes()+t),oe(s)}function Ke(e){const t=Y(e);if(!t)return"-";const s=String(t.getMonth()+1).padStart(2,"0"),a=String(t.getDate()).padStart(2,"0"),n=String(t.getHours()).padStart(2,"0"),u=String(t.getMinutes()).padStart(2,"0");return`${s}-${a} ${n}:${u}`}function ze(e,t,s){const a=new Map;for(const n of e)a.set(qe(n),n);for(const[n,u]of Object.entries(t)){const r=C(n),f=u.items?.[0];if(!f||!r)continue;const o=Ye(r);if(s==="current"&&o||s==="history"&&!o)continue;const h=qe({...f,taskId:r});a.has(h)||a.set(h,{...f,taskId:r})}return Array.from(a.values())}function _t(){try{const e=window.localStorage.getItem(Oe()),t=e?JSON.parse(e):[];y.value=Array.isArray(t)?t.filter(s=>typeof s=="number"&&s>0):[]}catch{y.value=[]}}function le(){ne(Oe(),y.value,y.value.length===0)}function Tt(){try{const e=window.localStorage.getItem(Ve());k.value=e?JSON.parse(e):{}}catch{k.value={}}}function x(){ne(Ve(),k.value,Object.keys(k.value).length===0)}function Ct(){try{const e=window.localStorage.getItem(He());d.value=e?JSON.parse(e):{}}catch{d.value={}}}function V(){ne(He(),d.value,Object.keys(d.value).length===0)}function Et(){try{const e=window.localStorage.getItem(Be());_.value=e?JSON.parse(e):[]}catch{_.value=[]}}function Ie(){ne(Be(),_.value,_.value.length===0)}function It(e){const t=e instanceof Error?e.message:String(e||"");return/记录不存在|任务不存在|不存在|已删除|not\s*found|404/i.test(t)}function $t(){const e=y.value.filter(t=>(k.value[t]||d.value[t]?.task?.status)==="RUNNING");e.length!==y.value.length&&(y.value=e,le())}function Ge(e){y.value.includes(e)||(y.value=[...y.value,e],le())}function re(e){const t=O.get(e);t&&(window.clearTimeout(t),O.delete(e))}function ue(e){if(re(e),y.value=y.value.filter(t=>t!==e),delete k.value[e],d.value[e]){const t={...d.value};delete t[e],d.value=t,V()}le(),x()}function Q(e){ue(e),E.value=E.value.filter(t=>C(t.taskId)!==e)}function Je(e,t=!0){y.value=y.value.filter(s=>s!==e),t||delete k.value[e],le(),x()}function W(e){return e==="SUCCESS"||e==="FAILED"||e==="COMPLETED"}function Ye(e){if(!e)return!1;const t=k.value[e]||d.value[e]?.task?.status;return W(t)}function Qe(e){const t=C(e.taskId);return t&&Ye(t)||W(e.taskStatus)?!0:!!e.success}async function X(){pe.value=await Ss()}async function A(){z.value=await ks()}async function H(){const e=await bs();E.value=e.items||[]}async function Dt(){try{const e=await _s();if(ke.value)return;const t=(e.country_codes||[]).filter(s=>me.some(a=>a.code===s));t.length&&(g.value=t)}catch{}}async function ie(){await Promise.allSettled([H(),A()])}function We(){R&&clearTimeout(R),R=setTimeout(()=>{R=null,Nt()},450)}async function Nt(){if(g.value.length){Se.value=!0;try{await Ms(g.value)}catch(e){m.error(e instanceof Error?e.message:"保存国家配置失败")}finally{Se.value=!1}}}function Mt(e){Fe.value=e}async function Xe(){const e=te.value.trim();if(!e){m.warning("请输入店铺名");return}se.value=!0;try{await Ts(e),te.value="",await X(),await A()}catch(t){m.error(t instanceof Error?t.message:"添加失败")}finally{se.value=!1}}async function Pt(e){try{await Cs(e.id),await X(),await A()}catch(t){m.error(t instanceof Error?t.message:"删除失败")}}function xt(e){return g.value.includes(e)}function Ze(e){return g.value.length===1&&g.value[0]===e}function At(e,t){const s=t.target;if(s){if(!s.checked&&Ze(e)){s.checked=!0,m.warning("至少保留 1 个国家");return}Lt(e,s.checked)}}function Lt(e,t){if(ke.value=!0,t)g.value.includes(e)||(g.value=[...g.value,e]);else{if(g.value.length<=1){m.warning("至少保留 1 个国家");return}g.value=g.value.filter(s=>s!==e)}We()}function Ft(e){K.value=e}function Ut(){K.value=null}function Rt(e){const t=K.value;if(K.value=null,t==null||t===e)return;ke.value=!0;const s=[...g.value],[a]=s.splice(t,1);s.splice(e,0,a),g.value=s,We()}function Ot(){N.value&&(S.value=$e(S.value,!0)),N.value||(S.value=[])}function Vt(){const e=S.value[S.value.length-1],t=e?bt(e,60):Ee();S.value=[...S.value,t]}function Ht(e){S.value.length<=1||(S.value=S.value.filter((t,s)=>s!==e))}function Bt(e,t){const s=[...S.value];s[e]=t||"",S.value=$e(s,!1)}function qt(e,t){const s=new Map;for(const a of e)s.set(P(a),a);for(const a of t)s.set(P(a),a);return Array.from(s.values())}async function jt(){const e=Fe.value.map(t=>t.shop_name).filter(Boolean);if(!e.length){m.warning("请先选择候选店铺");return}ae.value=!0;try{const t=await Es(e);_.value=qt(_.value,t.items||[]),Ie(),we.value&&ht(),m.success(`本次返回 ${t.items?.length||0} 条匹配结果`)}catch(t){m.error(t instanceof Error?t.message:"匹配失败")}finally{ae.value=!1}}function Kt(e){_.value=_.value.filter(t=>P(t)!==P(e)),Ie()}function L(e){const t=new Set(e.map(s=>P(s)));_.value=_.value.filter(s=>!t.has(P(s))),Ie()}function zt(){if(!N.value)return;const e=Date.now(),t=S.value.map(n=>n.trim()).filter(Boolean);if(!t.length)throw new Error("请至少选择一个执行时间");const s=t.map(n=>{const u=Y(n);if(!u)throw new Error(`时间格式无效: ${n}`);return{raw:kt(u),picker:oe(u),time:u.getTime()}}).sort((n,u)=>n.time-u.time);for(let n=1;n<s.length;n+=1)if(s[n].time===s[n-1].time)throw new Error("执行时间不能重复");const a=s.filter(n=>n.time>e);if(!a.length)throw new Error("执行时间必须晚于当前时间,请重新选择");return a.length!==s.length&&(S.value=a.map(n=>n.picker),m.warning("已自动移除早于当前时间的执行时间")),a.map(n=>n.raw)}function $e(e,t){const s=Date.now(),a=e.map(r=>r.trim()).filter(Boolean).map(r=>{const f=Y(r);return f?{value:oe(f),time:f.getTime()}:null}).filter(r=>!!r).filter(r=>r.time>s).sort((r,f)=>r.time-f.time),n=[],u=new Set;for(const r of a)u.has(r.value)||(u.add(r.value),n.push(r.value));return!n.length&&t?[Ee()]:n}function et(e,t,s,a,n=!0){return{type:"shop-match-run",ts:Date.now(),data:{taskId:e,user_id:St(),items:[{shopName:t.shopName,shopId:t.shopId,platform:t.platform,companyName:t.companyName,matched:!0,matchStatus:t.matchStatus,matchMessage:t.matchMessage}],country_codes:[...s],risk_listing_filter:ge.value,stage_index:a,final_stage:n}}}function De(e){const t=d.value[e]?.task,s=t?.scheduleStages||[],a=t?.currentStageIndex;if(typeof a!="number")return null;const n=s.find(u=>u.stageIndex===a);return n?.scheduledAt?{stageIndex:a,scheduledAt:n.scheduledAt,finalStage:a===s.length-1}:null}async function Gt(e,t){const s=Ae();if(!s?.enqueue_json)throw new Error("当前客户端未提供 enqueue_json");const a=De(e),n=d.value[e],u=n?.task?.countryCodes||g.value;if(!a)throw new Error(`任务 ${e} 缺少待执行阶段`);re(e),await st(()=>Ps(e,a.stageIndex),(o,h)=>{w.value=`任务 ${e} 等待后端恢复后再激活执行(${o}/${h})...`}),k.value={...k.value,[e]:"RUNNING"},n?.task&&(d.value={...d.value,[e]:{...n,task:{...n.task,status:"RUNNING",activeStageIndex:a.stageIndex}}}),V(),x();const r=et(e,t,u,a.stageIndex,a.finalStage);j.value=JSON.stringify(r,null,2);const f=await s.enqueue_json(r);if(!f?.success)throw new Error(f?.error||`任务 ${e} 推送失败`);Ge(e),w.value=`任务 ${e} 已推送第 ${a.stageIndex+1} 轮`,ee(!0)}function tt(e){const t=Y(e);return t?t.getTime():Number.NaN}function Jt(){const e=new Set(y.value);for(const s of Object.keys(d.value)){const a=C(s);a>0&&e.add(a)}for(const s of E.value){const a=C(s.taskId);a>0&&e.add(a)}const t=Array.from(e).map(s=>{const a=d.value[s],n=k.value[s]||a?.task?.status,u=a?.items?.[0]||E.value.find(o=>C(o.taskId)===s),r=De(s);if(n!=="SCHEDULED"||!u||!r?.scheduledAt)return null;const f=tt(r.scheduledAt);return!Number.isFinite(f)||f>Date.now()?null:{taskId:s,item:u,scheduledTime:f}}).filter(s=>!!s);return t.sort((s,a)=>s.scheduledTime-a.scheduledTime||s.taskId-a.taskId),t}async function Ne(){if(_e.value)return;const e=Jt();if(e.length){_e.value=!0;try{const t=e[0];try{await Gt(t.taskId,t.item),await ts(t.taskId)}catch(s){const a=s instanceof Error?s.message:`任务 ${t.taskId} 调度失败`,n=`${t.taskId}:${a}`,u=Date.now();w.value=a,(Re!==n||u-Ue>1e4)&&(Re=n,Ue=u,m.error(a)),await Promise.allSettled([H(),at()])}}finally{_e.value=!1,I()}}}function Yt(e,t){if(!t)return;re(e);const s=tt(t);if(!Number.isFinite(s))return;const a=Math.max(0,s-Date.now()),n=window.setTimeout(()=>{O.delete(e),Ne()},a);O.set(e,n)}function I(){const e=new Set(y.value);for(const t of Object.keys(d.value)){const s=C(t);s>0&&e.add(s)}for(const t of E.value){const s=C(t.taskId);s>0&&e.add(s)}for(const t of e){const a=d.value[t]?.task?.status||k.value[t],n=De(t);a==="SCHEDULED"&&n?.scheduledAt?Yt(t,n.scheduledAt):re(t)}Ne()}function Qt(){G||(G=window.setInterval(()=>{Ne()},1e3))}function Wt(){G&&(window.clearInterval(G),G=null)}function ce(){I(),y.value.length&&ee(!0)}const Xt=["无法连接到后端服务","bad gateway","gateway timeout","network error","timeout","502","503","504"];function Z(e){return new Promise(t=>{window.setTimeout(()=>t(),e)})}function Me(e){const t=(e instanceof Error?e.message:String(e||"")).toLowerCase();return Xt.some(s=>t.includes(s.toLowerCase()))}async function st(e,t,s=10){let a=0;for(;;)try{return await e()}catch(n){if(a+=1,!Me(n)||a>=s)throw n;t(a,s),await Z(F())}}async function at(){if(!y.value.length)return{settledTaskIds:[]};try{const e=await Le(y.value),t={...d.value},s=new Set;for(const a of e.missingTaskIds||[])Q(a),delete t[a],s.add(a);for(const a of e.items||[]){const n=a.task?.id;if(!n)continue;const u=t[n];t[n]=u?{...u,task:{...u.task||{},...a.task||{}}}:a;const r=a.task?.status||"";if(r){if(k.value[n]=r,W(r)){ue(n),s.add(n);continue}r==="SCHEDULED"&&(Je(n),s.add(n))}}return d.value=t,V(),x(),I(),{settledTaskIds:Array.from(s)}}catch{return{settledTaskIds:[]}}}function F(){return xs()}function nt(e=!1){if($.value){if(!e)return;window.clearTimeout($.value),$.value=null}const t=async()=>{if($.value=null,be.value||!y.value.length){y.value.length&&nt();return}be.value=!0;try{const{settledTaskIds:s}=await at();s.length&&await Promise.allSettled([H(),A()])}finally{be.value=!1}y.value.length&&($.value=window.setTimeout(t,F()))};e?t():$.value=window.setTimeout(t,F())}function ee(e=!1){nt(e)}function Zt(){$.value&&(window.clearTimeout($.value),$.value=null)}async function es(e){let t=0;const s=30;for(;;){try{const a=await Le([e]);if(t>0&&(w.value=`任务 ${e} 后端已恢复,继续等待执行结果...`),t=0,(a.missingTaskIds||[]).includes(e))return Q(e),await ie(),I(),"FAILED";const n=(a.items||[]).find(r=>r.task?.id===e),u=n?.task?.status||"";if(n){const r=d.value[e];d.value={...d.value,[e]:r?{...r,task:{...r.task||{},...n.task||{}}}:n},V()}if(u&&(k.value[e]=u,x()),W(u))return ue(e),await ie(),I(),u}catch(a){if(!Me(a))throw a;if(t+=1,t>=s)throw new Error(`任务 ${e} 等待后端恢复超时,请稍后手动刷新查看状态`);w.value=`任务 ${e} 运行中,正在等待后端服务恢复(${t}/${s})...`,(t===1||t%5===0)&&m.warning(`任务 ${e} 运行中,后端服务暂时不可用,正在自动重试`),await Z(F());continue}await Z(F())}}async function ts(e){let t=0;const s=30;for(;;)try{const a=await Le([e]);if(t>0&&(w.value=`任务 ${e} 后端已恢复,继续等待当前轮次结束...`),t=0,(a.missingTaskIds||[]).includes(e))return Q(e),await ie(),I(),"FAILED";const n=(a.items||[]).find(r=>r.task?.id===e),u=n?.task?.status||"";if(n){const r=d.value[e];d.value={...d.value,[e]:r?{...r,task:{...r.task||{},...n.task||{}}}:n},V()}if(u&&(k.value[e]=u,x()),!u||u==="RUNNING"){await Z(F());continue}return W(u)?(ue(e),await ie(),I(),u):(u==="SCHEDULED"&&(Je(e),I()),u)}catch(a){if(!Me(a))throw a;if(t+=1,t>=s)throw new Error(`任务 ${e} 等待当前轮次结束超时,请稍后手动刷新查看状态`);w.value=`任务 ${e} 运行中,正在等待当前轮次结束(${t}/${s})...`,(t===1||t%5===0)&&m.warning(`任务 ${e} 运行中,后端服务暂时不可用,正在自动重试`),await Z(F())}}function Pe(e){const t=C(e.taskId);return t?k.value[t]||d.value[t]?.task?.status||e.taskStatus||"":e.taskStatus||""}function ot(e){const t=C(e.taskId);return t?d.value[t]:void 0}function lt(e){const s=ot(e)?.task,a=s?.scheduleStages||[];if(!a.length)return"";const n=a.length,u=typeof s?.activeStageIndex=="number"?s.activeStageIndex:void 0,r=typeof s?.currentStageIndex=="number"?s.currentStageIndex:void 0;return typeof u=="number"?`执行进度: 第 ${u+1}/${n} 次执行中`:typeof r=="number"?`执行进度: 等待第 ${r+1}/${n} 次`:`执行进度: 共 ${n} 次`}function rt(e){const s=ot(e)?.task,a=s?.scheduleStages||[];if(typeof s?.currentStageIndex=="number"){const n=a.find(u=>u.stageIndex===s.currentStageIndex);if(n?.scheduledAt)return Ke(n.scheduledAt)}return e.scheduledAt?Ke(e.scheduledAt):""}function ut(e){const t=Pe(e);return t==="SCHEDULED"?"待执行":t==="RUNNING"?"执行中":t==="SUCCESS"||t==="COMPLETED"?"已完成":t==="FAILED"?"失败":e.success?"已完成":"未知"}function it(e){const t=Pe(e);return t==="SUCCESS"||t==="COMPLETED"?"success":t==="FAILED"?"failed":"running"}function ct(e){const t=Pe(e);return!!e.resultId&&(!!e.fileReady||!!e.downloadUrl)&&(t==="SUCCESS"||t==="COMPLETED")}async function dt(e){if(!e.resultId)return;const t=Ae();if(!t?.save_file_from_url_new){m.error("当前客户端未提供下载能力");return}const s=Is(e.resultId),a=e.outputFilename||`${e.shopName||"result"}.xlsx`,n=await t.save_file_from_url_new(s,a);n.success?m.success(`已保存: ${n.path||a}`):n.error&&n.error!=="用户取消"&&m.error(n.error)}async function ft(e){const t=C(e.taskId),s=Number(e.resultId||0);try{if(t>0)await $s(t),Q(t);else if(s>0)await Ds(s),E.value=E.value.filter(a=>Number(a.resultId||0)!==s),L([e]);else throw new Error("缺少可删除的任务标识");await Promise.allSettled([H(),X(),A()]),m.success("删除成功")}catch(a){if(It(a)){t>0?Q(t):s>0&&(E.value=E.value.filter(n=>Number(n.resultId||0)!==s)),L([e]),await Promise.allSettled([H(),X(),A()]),m.success("后端记录已不存在,本地记录已同步清理");return}m.error(a instanceof Error?a.message:"删除失败")}}function ss(e){const t=(e||"").trim();return{MATCHED:"已匹配",PENDING:"待匹配",CONFLICT:"需人工确认",INDEX_STALE:"索引过期"}[t]||t||"—"}function as(e){const t=(e.matchMessage||"").trim();return t||(e.matched&&e.matchStatus==="MATCHED"?"紫鸟索引已命中,可推送队列":e.matched?"已关联索引,请结合状态列查看是否可推送":"未命中或未就绪,请检查店铺名与索引刷新")}async function ns(){we.value=!0,await ht()}function os(){return _.value.find(e=>e.matched)}async function ht(){if(ye.value)return;const e=Ae();if(!e?.enqueue_json){m.error("当前客户端未提供 enqueue_json");return}if(!_.value.filter(r=>r.matched).length){m.warning("请先匹配可用店铺");return}if(!g.value.length){m.warning("请至少勾选一个国家");return}let s;try{s=zt()}catch(r){m.error(r instanceof Error?r.message:"定时配置无效");return}ye.value=!0,D.value=!0,j.value="";let a=0,n=0,u=0;try{for(;we.value;){const r=os();if(!r)break;u+=1;try{const f=await st(()=>Ns([r],g.value,s),(xe,de)=>{w.value=`后端服务暂时不可用,正在重试创建任务(${xe}/${de})...`}),o=f.taskId,h=s?.length?"SCHEDULED":"RUNNING",B={task:{id:o,status:h,scheduledAt:s?.[0],countryCodes:[...g.value],currentStageIndex:s?.length?0:void 0,activeStageIndex:void 0,scheduleStages:(s||[]).map((xe,de)=>({stageIndex:de,scheduledAt:xe,status:de===0?"SCHEDULED":"PENDING"}))},items:f.items};if(d.value={...d.value,[o]:B},k.value={...k.value,[o]:h},V(),x(),!f.items?.[0]){L([r]);continue}if(s?.length){a+=1,I(),w.value=`任务 ${o} 已创建,共 ${s.length} 个执行时间点`,L([r]);continue}const vt=et(o,r,g.value);j.value=JSON.stringify(vt,null,2);const mt=await e.enqueue_json(vt);if(!mt?.success){n+=1,w.value=`任务 ${o} 推送失败,已自动继续下一条:${mt?.error||"未知错误"}`,m.error(w.value),L([r]);continue}if(Ge(o),ee(!0),L([r]),w.value=`任务 ${o} 已入队,等待执行完成`,await es(o)!=="SUCCESS"){n+=1,w.value=`任务 ${o} 执行失败,已自动继续下一条`,m.error(w.value);continue}a+=1,w.value=`任务 ${o} 已完成`}catch(f){n+=1,w.value=`第 ${u} 条任务处理失败,已自动继续下一条:${f instanceof Error?f.message:"未知错误"}`,m.error(w.value),L([r]);continue}}I(),s?.length||ee(!0),(a>0||n>0)&&m.success(s?.length?`定时任务已创建:成功 ${a} 条,失败 ${n} 条`:`店铺推送已完成:成功 ${a} 条,失败 ${n} 条`)}catch(r){const f=r instanceof Error?r.message:"推送失败";w.value=f,m.error(f)}finally{ye.value=!1,D.value=!1}}return rs(async()=>{_t(),Tt(),Ct(),Et(),S.value=$e(S.value,N.value),$t(),await Promise.allSettled([X(),Dt(),H(),A()]),Qt(),window.addEventListener("focus",ce),document.addEventListener("visibilitychange",ce),I(),y.value.length&&ee(!0)}),us(()=>{Zt(),Wt(),window.removeEventListener("focus",ce),document.removeEventListener("visibilitychange",ce),R&&clearTimeout(R);for(const e of O.values())window.clearTimeout(e);O.clear()}),(e,t)=>{const s=ds,a=fs,n=hs,u=ms,r=ps,f=ws;return i(),c("div",Vs,[b(is,{active:"shop-match"}),l("div",Hs,[l("aside",Bs,[t[10]||(t[10]=l("div",{class:"section-title"},"店铺候选",-1)),l("div",qs,[t[4]||(t[4]=l("div",{class:"hint"},"输入店铺名后点“添加”加入下方备选区。勾选备选店铺后先做“匹配店铺”,确认命中紫鸟索引后再推送到 Python 队列。",-1)),l("div",js,[b(s,{modelValue:te.value,"onUpdate:modelValue":t[0]||(t[0]=o=>te.value=o),clearable:"",placeholder:"请输入店铺名后回车或点击添加",onKeyup:cs(Xe,["enter"])},null,8,["modelValue"]),l("button",{type:"button",class:"opt-btn",disabled:se.value,onClick:Xe},v(se.value?"添加中...":"添加"),9,Ks)])]),t[11]||(t[11]=l("div",{class:"section-title"},"备选店铺",-1)),pe.value.length?(i(),c("div",Gs,[b(n,{data:pe.value,"row-key":"id",height:"240",class:"candidate-table",onSelectionChange:Mt},{default:M(()=>[b(a,{type:"selection",width:"42"}),b(a,{prop:"shop_name",label:"店铺名","min-width":"120","show-overflow-tooltip":""}),b(a,{label:"操作",width:"72",align:"center"},{default:M(({row:o})=>[l("button",{type:"button",class:"link-danger",onClick:h=>Pt(o)},"删除",8,Js)]),_:1})]),_:1},8,["data"])])):(i(),c("div",zs,"暂无备选店铺,请先输入并添加")),t[12]||(t[12]=l("div",{class:"section-title"},"国家配置",-1)),t[13]||(t[13]=l("p",{class:"hint country-pref-hint"},"勾选的国家会在每一个执行时间点都执行一遍。勾选区会按照已选顺序排在前面,下方支持拖拽排序,和商品风险页保持一致。",-1)),l("div",Ys,[(i(!0),c(U,null,q(wt.value,o=>(i(),c("label",{key:o.code,class:"country-check-row"},[l("input",{type:"checkbox",class:"country-check-input",checked:xt(o.code),disabled:Ze(o.code),onChange:h=>At(o.code,h)},null,40,Qs),l("span",Ws,v(o.label)+" ("+v(o.code)+")",1)]))),128))]),g.value.length?(i(),c("div",Xs,[t[6]||(t[6]=l("div",{class:"country-order-caption"},"已选顺序(拖拽 ⋮⋮ 调整)",-1)),l("div",Zs,[(i(!0),c(U,null,q(g.value,(o,h)=>(i(),c("div",{key:o,class:fe(["country-drag-row",{dragging:K.value===h}]),draggable:"true",onDragstart:B=>Ft(h),onDragend:Ut,onDragover:t[1]||(t[1]=pt(()=>{},["prevent"])),onDrop:pt(B=>Rt(h),["prevent"])},[t[5]||(t[5]=l("span",{class:"drag-handle",title:"拖动排序"},"⋮⋮",-1)),l("span",ta,v(je(o))+" ("+v(o)+")",1)],42,ea))),128))])])):T("",!0),Se.value?(i(),c("div",sa,"保存中...")):T("",!0),t[14]||(t[14]=l("div",{class:"section-title"},"商品列表筛选",-1)),t[15]||(t[15]=l("p",{class:"hint listing-filter-hint"},"与“推送到 Python 队列”一并下发,供 Python 区分处理场景。",-1)),l("div",aa,[b(r,{modelValue:ge.value,"onUpdate:modelValue":t[2]||(t[2]=o=>ge.value=o),class:"listing-filter-select",teleported:"",placeholder:"选择筛选类型"},{default:M(()=>[(i(!0),c(U,null,q(vs(Os),o=>(i(),gt(u,{key:o.value,label:o.label,value:o.value},null,8,["label","value"]))),128))]),_:1},8,["modelValue"])]),t[16]||(t[16]=l("div",{class:"section-title"},"定时执行",-1)),l("label",na,[gs(l("input",{"onUpdate:modelValue":t[3]||(t[3]=o=>N.value=o),type:"checkbox",disabled:D.value,onChange:Ot},null,40,oa),[[ys,N.value]]),t[7]||(t[7]=l("span",null,"启用多时间点调度",-1))]),N.value?(i(),c("div",la,[(i(!0),c(U,null,q(S.value,(o,h)=>(i(),c("div",{key:`schedule-${h}`,class:"schedule-row"},[b(f,{"model-value":o,type:"datetime","value-format":"YYYY-MM-DD HH:mm:ss",format:"MM-DD HH:mm",placeholder:"选择执行时间",disabled:D.value,class:"schedule-picker","onUpdate:modelValue":B=>Bt(h,B)},null,8,["model-value","disabled","onUpdate:modelValue"]),l("button",{type:"button",class:"schedule-remove",disabled:D.value||S.value.length<=1,onClick:B=>Ht(h)},"删除",8,ra)]))),128)),l("button",{type:"button",class:"schedule-add",disabled:D.value,onClick:Vt},"添加时间点",8,ua),t[8]||(t[8]=l("p",{class:"hint schedule-hint"},"默认开启定时;支持按 `MM-DD HH:mm` 配置多个时间点,前端不再限制选择更早时间,实际是否可执行以后端校验为准。",-1))])):T("",!0),l("div",ia,[l("button",{type:"button",class:"btn-run",disabled:ae.value,onClick:jt},v(ae.value?"匹配中...":"匹配店铺"),9,ca),l("button",{type:"button",class:"btn-run btn-queue",disabled:D.value||!_.value.length,onClick:ns},v(D.value?"推送中...":N.value?"创建定时任务":"推送到 Python 队列"),9,da)]),t[17]||(t[17]=l("p",{class:"loading-msg"},[he("已支持多阶段定时执行;Python 每处理完一店可回传 "),l("code",null,"/api/shop-match/tasks/{taskId}/result"),he(",后端会按阶段汇总并在超时场景自动收尾。")],-1)),w.value?(i(),c("div",fa,[t[9]||(t[9]=l("div",{class:"section-title queue-debug-title"},"推送结果",-1)),l("div",ha,v(w.value),1),j.value?(i(),c("pre",va,v(j.value),1)):T("",!0)])):T("",!0)]),l("section",ma,[t[26]||(t[26]=l("div",{class:"panel-header"},"匹配与任务",-1)),l("div",pa,[l("div",ga,[l("div",ya,[t[18]||(t[18]=l("span",{class:"summary-label"},"候选店铺",-1)),l("strong",null,v(z.value.candidateCount),1)]),l("div",wa,[t[19]||(t[19]=l("span",{class:"summary-label"},"已结束任务",-1)),l("strong",null,v(z.value.processedTaskCount),1)]),l("div",Sa,[t[20]||(t[20]=l("span",{class:"summary-label"},"成功",-1)),l("strong",null,v(z.value.successTaskCount),1)]),l("div",ka,[t[21]||(t[21]=l("span",{class:"summary-label"},"失败",-1)),l("strong",null,v(z.value.failedTaskCount),1)])]),t[25]||(t[25]=l("div",{class:"subsection-title"},"匹配结果",-1)),_.value.length?(i(),gt(n,{key:1,data:_.value,"row-key":P,"highlight-current-row":!1,class:"result-table match-table"},{default:M(()=>[b(a,{prop:"shopName",label:"店铺名","min-width":"100"}),b(a,{label:"匹配",width:"72",align:"center"},{default:M(({row:o})=>[l("span",{class:fe(o.matched?"ok":"fail")},v(o.matched?"是":"否"),3)]),_:1}),b(a,{prop:"shopId",label:"店铺ID","min-width":"120","show-overflow-tooltip":""}),b(a,{prop:"platform",label:"平台",width:"88","show-overflow-tooltip":""}),b(a,{prop:"companyName",label:"公司","min-width":"100","show-overflow-tooltip":""}),b(a,{label:"状态",width:"100","show-overflow-tooltip":""},{default:M(({row:o})=>[he(v(ss(o.matchStatus)),1)]),_:1}),b(a,{label:"说明","min-width":"140","show-overflow-tooltip":""},{default:M(({row:o})=>[he(v(as(o)),1)]),_:1}),b(a,{label:"操作",width:"72",align:"center"},{default:M(({row:o})=>[l("button",{type:"button",class:"link-danger",onClick:h=>Kt(o)},"删除",8,_a)]),_:1})]),_:1},8,["data"])):(i(),c("div",ba,"完成“匹配店铺”后,在此展示紫鸟索引匹配结果,供核对后再推送队列。")),l("div",Ta,[t[24]||(t[24]=l("div",{class:"result-list-header"},[l("span",null,"处理记录")],-1)),yt.value?(i(),c(U,{key:1},[Te.value.length?(i(),c("div",Ea,[t[22]||(t[22]=l("div",{class:"result-subsection-title"},"当前任务",-1)),l("ul",Ia,[(i(!0),c(U,null,q(Te.value,o=>(i(),c("li",{key:`cur-${o.resultId}-${o.taskId}`,class:"task-item"},[l("div",$a,[l("span",{class:"id",title:o.shopName||""},v(o.shopName||"-"),9,Da),l("div",Na,"任务 ID: "+v(o.taskId??"-"),1),o.platform?(i(),c("div",Ma,"平台: "+v(o.platform),1)):T("",!0),lt(o)?(i(),c("div",Pa,v(lt(o)),1)):T("",!0),rt(o)?(i(),c("div",xa,"下一次: "+v(rt(o)),1)):T("",!0),o.error?(i(),c("div",Aa,"错误: "+v(o.error),1)):T("",!0)]),l("div",La,[l("span",{class:fe(["status",it(o)])},v(ut(o)),3),ct(o)?(i(),c("button",{key:0,type:"button",class:"download",onClick:h=>dt(o)},"下载",8,Fa)):T("",!0),l("button",{type:"button",class:"btn-delete",onClick:h=>ft(o)},"删除",8,Ua)])]))),128))])])):T("",!0),Ce.value.length?(i(),c("div",Ra,[t[23]||(t[23]=l("div",{class:"result-subsection-title"},"历史任务",-1)),l("ul",Oa,[(i(!0),c(U,null,q(Ce.value,o=>(i(),c("li",{key:`his-${o.resultId}-${o.taskId}`,class:"task-item"},[l("div",Va,[l("span",{class:"id",title:o.shopName||""},v(o.shopName||"-"),9,Ha),l("div",Ba,"任务 ID: "+v(o.taskId??"-"),1),o.outputFilename?(i(),c("div",qa,"文件: "+v(o.outputFilename),1)):T("",!0),o.error?(i(),c("div",ja,"错误: "+v(o.error),1)):T("",!0)]),l("div",Ka,[l("span",{class:fe(["status",it(o)])},v(ut(o)),3),ct(o)?(i(),c("button",{key:0,type:"button",class:"download",onClick:h=>dt(o)},"下载",8,za)):T("",!0),l("button",{type:"button",class:"btn-delete",onClick:h=>ft(o)},"删除",8,Ga)])]))),128))])])):T("",!0)],64)):(i(),c("div",Ca,"暂无处理记录。推送队列并等待 Python 回传后,将在此显示下载链接。"))])])])])])}}}),Ya=As(Ja,[["__scopeId","data-v-064c8132"]]);Ls.locale("zh-cn");Fs(Ya).use(Us,{locale:Rs}).mount("#app");
|