2 lines
29 KiB
JavaScript
2 lines
29 KiB
JavaScript
import{d as os,g as p,o as ls,I as rs,a as i,c,b,B as us,e as l,R as is,S as cs,t as v,T as N,U as ds,V as fs,F as L,r as H,W as vt,n as ie,f as T,X as hs,Y as mt,Z as vs,$ as ms,w as ps,v as gs,ae as ys,u as ce,h as de,af as ws,ag as Ss,ah as ks,ai as bs,E as m,aj as _s,ak as Ts,al as Is,i as Pe,am as Cs,an as Es,ao as $s,ap as Ds,aq as Ns,ar as pt,as as Ms,Q as Ps,_ as xs,at as As,p as Ls,q as Fs}from"./pywebview-CeWJDVeG.js";import{z as Us}from"./zh-cn-DyKmB71i.js";/* empty css */import{L as Rs}from"./listingFilters-BpGOU_pJ.js";const Os={class:"page-shell module-page"},Vs={class:"main-content"},Hs={class:"left-panel"},Bs={class:"input-zone"},qs={class:"input-row"},js=["disabled"],Ks={key:0,class:"empty-candidates"},zs={key:1,class:"candidate-table-scroll"},Ys=["onClick"],Gs={class:"country-pref-checks"},Js=["checked","disabled","onChange"],Qs={class:"country-check-text"},Ws={key:2,class:"country-order-panel"},Xs={class:"country-order-list"},Zs=["onDragstart","onDrop"],ea={class:"country-drag-label"},ta={key:3,class:"country-pref-status"},sa={class:"listing-filter-row"},aa={class:"schedule-switch"},na=["disabled"],oa={key:4,class:"schedule-config"},la=["disabled","onClick"],ra=["disabled"],ua={class:"run-row"},ia=["disabled"],ca=["disabled"],da={key:5,class:"queue-debug-card"},fa={class:"queue-debug-line"},ha={key:0,class:"queue-debug-payload"},va={class:"right-panel"},ma={class:"task-list-wrap"},pa={class:"clean-result-summary"},ga={class:"summary-card"},ya={class:"summary-card"},wa={class:"summary-card"},Sa={class:"summary-card"},ka={key:0,class:"empty-tasks narrow"},ba=["onClick"],_a={class:"result-list-wrap"},Ta={key:0,class:"empty-tasks"},Ia={key:0,class:"result-subsection"},Ca={class:"task-list"},Ea={class:"left split-result-main"},$a=["title"],Da={class:"files"},Na={key:0,class:"files"},Ma={key:1,class:"files"},Pa={key:2,class:"files"},xa={key:3,class:"files"},Aa={class:"task-right"},La=["onClick"],Fa=["onClick"],Ua={key:1,class:"result-subsection"},Ra={class:"task-list"},Oa={class:"left split-result-main"},Va=["title"],Ha={class:"files"},Ba={key:0,class:"files"},qa={key:1,class:"files"},ja={class:"task-right"},Ka=["onClick"],za=["onClick"],Ya=os({__name:"BrandShopMatchTab",setup(Ja){const fe=[{code:"DE",label:"德国"},{code:"UK",label:"英国"},{code:"FR",label:"法国"},{code:"IT",label:"意大利"},{code:"ES",label:"西班牙"}],X=p(""),he=p([]),xe=p([]),_=p([]),ve=p("Active"),Z=p(!1),ee=p(!1),$=p(!1),me=p(!1),pe=p(!1),w=p(""),B=p(""),g=p(["DE","UK","FR","IT","ES"]),q=p(null),ge=p(!1),ye=p(!1);let F=null;const D=p(!0),S=p([_e()]),j=p({candidateCount:0,processedTaskCount:0,successTaskCount:0,failedTaskCount:0}),C=p([]),y=p([]),k=p({}),h=p({}),E=p(null),we=p(!1),U=new Map,Se=p(!1);let K=null,Ae=0,Le="";const ke=de(()=>qe(C.value.filter(e=>!!I(e.taskId)&&!ze(e)),h.value,"current")),be=de(()=>qe(C.value.filter(e=>!I(e.taskId)||ze(e)),h.value,"history")),gt=de(()=>ke.value.length>0||be.value.length>0),yt=de(()=>{const e=new Set(g.value);return[...g.value.map(t=>({code:t,label:He(t)})),...fe.filter(t=>!e.has(t.code)).map(t=>({code:t.code,label:t.label}))]});function z(){return typeof window<"u"&&window.localStorage.getItem("uid")||"0"}function wt(){const e=Number(z());return Number.isFinite(e)&&e>0?e:0}function I(e){const t=Number(e);return Number.isFinite(t)&&t>0?t:0}function Fe(){return`shop-match:tasks:${z()}`}function Ue(){return`shop-match:task-status:${z()}`}function Re(){return`shop-match:task-snapshots:${z()}`}function Oe(){return`shop-match:matched-items:${z()}`}function te(e,t,s){s?window.localStorage.removeItem(e):window.localStorage.setItem(e,JSON.stringify(t))}function M(e){return`${(e.shopName||"").trim()}::${e.shopId||""}`}function Ve(e){return`${I(e.taskId)}-${e.resultId||0}-${e.shopName||""}`}function He(e){return fe.find(t=>t.code===e)?.label||e}function St(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 se(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 _e(){const e=new Date;return e.setSeconds(0,0),e.setMinutes(e.getMinutes()+5),se(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]),d=Number(s[5]),o=Number(s[6]||"0"),f=new Date(a,n-1,u,r,d,o,0);return Number.isNaN(f.getTime())||f.getFullYear()!==a||f.getMonth()!==n-1||f.getDate()!==u?null:f}function kt(e,t){const s=Y(e)||new Date;return s.setSeconds(0,0),s.setMinutes(s.getMinutes()+t),se(s)}function Be(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 qe(e,t,s){const a=new Map;for(const n of e)a.set(Ve(n),n);for(const[n,u]of Object.entries(t)){const r=I(n),d=u.items?.[0];if(!d||!r)continue;const o=Ke(r);if(s==="current"&&o||s==="history"&&!o)continue;const f=Ve({...d,taskId:r});a.has(f)||a.set(f,{...d,taskId:r})}return Array.from(a.values())}function bt(){try{const e=window.localStorage.getItem(Fe()),t=e?JSON.parse(e):[];y.value=Array.isArray(t)?t.filter(s=>typeof s=="number"&&s>0):[]}catch{y.value=[]}}function ae(){te(Fe(),y.value,y.value.length===0)}function _t(){try{const e=window.localStorage.getItem(Ue());k.value=e?JSON.parse(e):{}}catch{k.value={}}}function R(){te(Ue(),k.value,Object.keys(k.value).length===0)}function Tt(){try{const e=window.localStorage.getItem(Re());h.value=e?JSON.parse(e):{}}catch{h.value={}}}function G(){te(Re(),h.value,Object.keys(h.value).length===0)}function It(){try{const e=window.localStorage.getItem(Oe());_.value=e?JSON.parse(e):[]}catch{_.value=[]}}function Te(){te(Oe(),_.value,_.value.length===0)}function Ct(e){const t=e instanceof Error?e.message:String(e||"");return/记录不存在|任务不存在|不存在|已删除|not\s*found|404/i.test(t)}function Et(){const e=y.value.filter(t=>(k.value[t]||h.value[t]?.task?.status)==="RUNNING");e.length!==y.value.length&&(y.value=e,ae())}function je(e){y.value.includes(e)||(y.value=[...y.value,e],ae())}function ne(e){const t=U.get(e);t&&(window.clearTimeout(t),U.delete(e))}function Ie(e){if(ne(e),y.value=y.value.filter(t=>t!==e),delete k.value[e],h.value[e]){const t={...h.value};delete t[e],h.value=t,G()}ae(),R()}function oe(e){Ie(e),C.value=C.value.filter(t=>I(t.taskId)!==e)}function $t(e,t=!0){y.value=y.value.filter(s=>s!==e),t||delete k.value[e],ae(),R()}function le(e){return e==="SUCCESS"||e==="FAILED"||e==="COMPLETED"}function Ke(e){if(!e)return!1;const t=k.value[e]||h.value[e]?.task?.status;return le(t)}function ze(e){const t=I(e.taskId);return t&&Ke(t)||le(e.taskStatus)?!0:!!e.success}async function J(){he.value=await ws()}async function P(){j.value=await Ss()}async function O(){const e=await ks();C.value=e.items||[]}async function Dt(){try{const e=await bs();if(ye.value)return;const t=(e.country_codes||[]).filter(s=>fe.some(a=>a.code===s));t.length&&(g.value=t)}catch{}}async function Ye(){await Promise.allSettled([O(),P()])}function Ge(){F&&clearTimeout(F),F=setTimeout(()=>{F=null,Nt()},450)}async function Nt(){if(g.value.length){ge.value=!0;try{await Ns(g.value)}catch(e){m.error(e instanceof Error?e.message:"保存国家配置失败")}finally{ge.value=!1}}}function Mt(e){xe.value=e}async function Je(){const e=X.value.trim();if(!e){m.warning("请输入店铺名");return}Z.value=!0;try{await _s(e),X.value="",await J(),await P()}catch(t){m.error(t instanceof Error?t.message:"添加失败")}finally{Z.value=!1}}async function Pt(e){try{await Ts(e.id),await J(),await P()}catch(t){m.error(t instanceof Error?t.message:"删除失败")}}function xt(e){return g.value.includes(e)}function Qe(e){return g.value.length===1&&g.value[0]===e}function At(e,t){const s=t.target;if(s){if(!s.checked&&Qe(e)){s.checked=!0,m.warning("至少保留 1 个国家");return}Lt(e,s.checked)}}function Lt(e,t){if(ye.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)}Ge()}function Ft(e){q.value=e}function Ut(){q.value=null}function Rt(e){const t=q.value;if(q.value=null,t==null||t===e)return;ye.value=!0;const s=[...g.value],[a]=s.splice(t,1);s.splice(e,0,a),g.value=s,Ge()}function Ot(){D.value&&(S.value=Ce(S.value,!0)),D.value||(S.value=[])}function Vt(){const e=S.value[S.value.length-1],t=e?kt(e,60):_e();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=Ce(s,!1)}function qt(e,t){const s=new Map;for(const a of e)s.set(M(a),a);for(const a of t)s.set(M(a),a);return Array.from(s.values())}async function jt(){const e=xe.value.map(t=>t.shop_name).filter(Boolean);if(!e.length){m.warning("请先选择候选店铺");return}ee.value=!0;try{const t=await Is(e);_.value=qt(_.value,t.items||[]),Te(),pe.value&&dt(),m.success(`本次返回 ${t.items?.length||0} 条匹配结果`)}catch(t){m.error(t instanceof Error?t.message:"匹配失败")}finally{ee.value=!1}}function Kt(e){_.value=_.value.filter(t=>M(t)!==M(e)),Te()}function x(e){const t=new Set(e.map(s=>M(s)));_.value=_.value.filter(s=>!t.has(M(s))),Te()}function zt(){if(!D.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:St(u),picker:se(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 Ce(e,t){const s=Date.now(),a=e.map(r=>r.trim()).filter(Boolean).map(r=>{const d=Y(r);return d?{value:se(d),time:d.getTime()}:null}).filter(r=>!!r).filter(r=>r.time>s).sort((r,d)=>r.time-d.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?[_e()]:n}function We(e,t,s,a,n=!0){return{type:"shop-match-run",ts:Date.now(),data:{taskId:e,user_id:wt(),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:ve.value,stage_index:a,final_stage:n}}}function Ee(e){const t=h.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 Yt(e,t){const s=Pe();if(!s?.enqueue_json)throw new Error("当前客户端未提供 enqueue_json");const a=Ee(e),n=h.value[e],u=n?.task?.countryCodes||g.value;if(!a)throw new Error(`任务 ${e} 缺少待执行阶段`);ne(e),await et(()=>Ms(e,a.stageIndex),(o,f)=>{w.value=`任务 ${e} 等待后端恢复后再激活执行(${o}/${f})...`}),k.value={...k.value,[e]:"RUNNING"},n?.task&&(h.value={...h.value,[e]:{...n,task:{...n.task,status:"RUNNING",activeStageIndex:a.stageIndex}}}),G(),R();const r=We(e,t,u,a.stageIndex,a.finalStage);B.value=JSON.stringify(r,null,2);const d=await s.enqueue_json(r);if(!d?.success)throw new Error(d?.error||`任务 ${e} 推送失败`);je(e),w.value=`任务 ${e} 已推送第 ${a.stageIndex+1} 轮`,W(!0)}function Xe(e){const t=Y(e);return t?t.getTime():Number.NaN}function Gt(){const e=new Set(y.value);for(const s of Object.keys(h.value)){const a=I(s);a>0&&e.add(a)}for(const s of C.value){const a=I(s.taskId);a>0&&e.add(a)}const t=Array.from(e).map(s=>{const a=h.value[s],n=k.value[s]||a?.task?.status,u=a?.items?.[0]||C.value.find(o=>I(o.taskId)===s),r=Ee(s);if(n!=="SCHEDULED"||!u||!r?.scheduledAt)return null;const d=Xe(r.scheduledAt);return!Number.isFinite(d)||d>Date.now()?null:{taskId:s,item:u,scheduledTime:d}}).filter(s=>!!s);return t.sort((s,a)=>s.scheduledTime-a.scheduledTime||s.taskId-a.taskId),t}async function $e(){if(Se.value)return;const e=Gt();if(e.length){Se.value=!0;try{for(const t of e)try{await Yt(t.taskId,t.item)}catch(s){const a=s instanceof Error?s.message:`任务 ${t.taskId} 调度失败`,n=`${t.taskId}:${a}`,u=Date.now();w.value=a,(Le!==n||u-Ae>1e4)&&(Le=n,Ae=u,m.error(a)),await Promise.allSettled([O(),tt()])}}finally{Se.value=!1}}}function Jt(e,t){if(!t)return;ne(e);const s=Xe(t);if(!Number.isFinite(s))return;const a=Math.max(0,s-Date.now()),n=window.setTimeout(()=>{U.delete(e),$e()},a);U.set(e,n)}function A(){const e=new Set(y.value);for(const t of Object.keys(h.value)){const s=I(t);s>0&&e.add(s)}for(const t of C.value){const s=I(t.taskId);s>0&&e.add(s)}for(const t of e){const a=h.value[t]?.task?.status||k.value[t],n=Ee(t);a==="SCHEDULED"&&n?.scheduledAt?Jt(t,n.scheduledAt):ne(t)}$e()}function Qt(){K||(K=window.setInterval(()=>{$e()},1e3))}function Wt(){K&&(window.clearInterval(K),K=null)}function re(){A(),y.value.length&&W(!0)}const Xt=["无法连接到后端服务","bad gateway","gateway timeout","network error","timeout","502","503","504"];function De(e){return new Promise(t=>{window.setTimeout(()=>t(),e)})}function Ze(e){const t=(e instanceof Error?e.message:String(e||"")).toLowerCase();return Xt.some(s=>t.includes(s.toLowerCase()))}async function et(e,t,s=10){let a=0;for(;;)try{return await e()}catch(n){if(a+=1,!Ze(n)||a>=s)throw n;t(a,s),await De(Q())}}async function tt(){if(!y.value.length)return{settledTaskIds:[]};try{const e=await pt(y.value),t={...h.value},s=new Set;for(const a of e.missingTaskIds||[])oe(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,le(r)){Ie(n),s.add(n);continue}r==="SCHEDULED"&&($t(n),s.add(n))}}return h.value=t,G(),R(),A(),{settledTaskIds:Array.from(s)}}catch{return{settledTaskIds:[]}}}function Q(){return Ps()}function st(e=!1){if(E.value){if(!e)return;window.clearTimeout(E.value),E.value=null}const t=async()=>{if(E.value=null,we.value||!y.value.length){y.value.length&&st();return}we.value=!0;try{const{settledTaskIds:s}=await tt();s.length&&await Promise.allSettled([O(),P()])}finally{we.value=!1}y.value.length&&(E.value=window.setTimeout(t,Q()))};e?t():E.value=window.setTimeout(t,Q())}function W(e=!1){st(e)}function Zt(){E.value&&(window.clearTimeout(E.value),E.value=null)}async function es(e){let t=0;const s=30;for(;;){try{const a=await pt([e]);if(t>0&&(w.value=`任务 ${e} 后端已恢复,继续等待执行结果...`),t=0,(a.missingTaskIds||[]).includes(e))return oe(e),await Ye(),A(),"FAILED";const n=(a.items||[]).find(r=>r.task?.id===e),u=n?.task?.status||"";if(n){const r=h.value[e];h.value={...h.value,[e]:r?{...r,task:{...r.task||{},...n.task||{}}}:n},G()}if(u&&(k.value[e]=u,R()),le(u))return Ie(e),await Ye(),A(),u}catch(a){if(!Ze(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 De(Q());continue}await De(Q())}}function Ne(e){const t=I(e.taskId);return t?k.value[t]||h.value[t]?.task?.status||e.taskStatus||"":e.taskStatus||""}function at(e){const t=I(e.taskId);return t?h.value[t]:void 0}function nt(e){const s=at(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 ot(e){const s=at(e)?.task,a=s?.scheduleStages||[];if(typeof s?.currentStageIndex=="number"){const n=a.find(u=>u.stageIndex===s.currentStageIndex);if(n?.scheduledAt)return Be(n.scheduledAt)}return e.scheduledAt?Be(e.scheduledAt):""}function lt(e){const t=Ne(e);return t==="SCHEDULED"?"待执行":t==="RUNNING"?"执行中":t==="SUCCESS"||t==="COMPLETED"?"已完成":t==="FAILED"?"失败":e.success?"已完成":"未知"}function rt(e){const t=Ne(e);return t==="SUCCESS"||t==="COMPLETED"?"success":t==="FAILED"?"failed":"running"}function ut(e){const t=Ne(e);return!!e.resultId&&!!e.downloadUrl&&(t==="SUCCESS"||t==="COMPLETED")}async function it(e){if(!e.resultId)return;const t=Pe();if(!t?.save_file_from_url_new){m.error("当前客户端未提供下载能力");return}const s=Cs(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 ct(e){const t=I(e.taskId),s=Number(e.resultId||0);try{if(t>0)await Es(t),oe(t);else if(s>0)await $s(s),C.value=C.value.filter(a=>Number(a.resultId||0)!==s),x([e]);else throw new Error("缺少可删除的任务标识");await Promise.allSettled([O(),J(),P()]),m.success("删除成功")}catch(a){if(Ct(a)){t>0?oe(t):s>0&&(C.value=C.value.filter(n=>Number(n.resultId||0)!==s)),x([e]),await Promise.allSettled([O(),J(),P()]),m.success("后端记录已不存在,本地记录已同步清理");return}m.error(a instanceof Error?a.message:"删除失败")}}function ts(e){const t=(e||"").trim();return{MATCHED:"已匹配",PENDING:"待匹配",CONFLICT:"需人工确认",INDEX_STALE:"索引过期"}[t]||t||"—"}function ss(e){const t=(e.matchMessage||"").trim();return t||(e.matched&&e.matchStatus==="MATCHED"?"紫鸟索引已命中,可推送队列":e.matched?"已关联索引,请结合状态列查看是否可推送":"未命中或未就绪,请检查店铺名与索引刷新")}async function as(){pe.value=!0,await dt()}function ns(){return _.value.find(e=>e.matched)}async function dt(){if(me.value)return;const e=Pe();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}me.value=!0,$.value=!0,B.value="";let a=0,n=0,u=0;try{for(;pe.value;){const r=ns();if(!r)break;u+=1;try{const d=await et(()=>Ds([r],g.value,s),(Me,ue)=>{w.value=`后端服务暂时不可用,正在重试创建任务(${Me}/${ue})...`}),o=d.taskId,f=s?.length?"SCHEDULED":"RUNNING",V={task:{id:o,status:f,scheduledAt:s?.[0],countryCodes:[...g.value],currentStageIndex:s?.length?0:void 0,activeStageIndex:void 0,scheduleStages:(s||[]).map((Me,ue)=>({stageIndex:ue,scheduledAt:Me,status:ue===0?"SCHEDULED":"PENDING"}))},items:d.items};if(h.value={...h.value,[o]:V},k.value={...k.value,[o]:f},G(),R(),!d.items?.[0]){x([r]);continue}if(s?.length){a+=1,A(),w.value=`任务 ${o} 已创建,共 ${s.length} 个执行时间点`,x([r]);continue}const ft=We(o,r,g.value);B.value=JSON.stringify(ft,null,2);const ht=await e.enqueue_json(ft);if(!ht?.success){n+=1,w.value=`任务 ${o} 推送失败,已自动继续下一条:${ht?.error||"未知错误"}`,m.error(w.value),x([r]);continue}if(je(o),W(!0),x([r]),w.value=`任务 ${o} 已入队,等待执行完成`,await es(o)!=="SUCCESS"){n+=1,w.value=`任务 ${o} 执行失败,已自动继续下一条`,m.error(w.value);continue}a+=1,w.value=`任务 ${o} 已完成`}catch(d){n+=1,w.value=`第 ${u} 条任务处理失败,已自动继续下一条:${d instanceof Error?d.message:"未知错误"}`,m.error(w.value),x([r]);continue}}A(),s?.length||W(!0),(a>0||n>0)&&m.success(s?.length?`定时任务已创建:成功 ${a} 条,失败 ${n} 条`:`店铺推送已完成:成功 ${a} 条,失败 ${n} 条`)}catch(r){const d=r instanceof Error?r.message:"推送失败";w.value=d,m.error(d)}finally{me.value=!1,$.value=!1}}return ls(async()=>{bt(),_t(),Tt(),It(),S.value=Ce(S.value,D.value),Et(),await Promise.allSettled([J(),Dt(),O(),P()]),Qt(),window.addEventListener("focus",re),document.addEventListener("visibilitychange",re),A(),y.value.length&&W(!0)}),rs(()=>{Zt(),Wt(),window.removeEventListener("focus",re),document.removeEventListener("visibilitychange",re),F&&clearTimeout(F);for(const e of U.values())window.clearTimeout(e);U.clear()}),(e,t)=>{const s=cs,a=ds,n=fs,u=vs,r=ms,d=ys;return i(),c("div",Os,[b(us,{active:"shop-match"}),l("div",Vs,[l("aside",Hs,[t[10]||(t[10]=l("div",{class:"section-title"},"店铺候选",-1)),l("div",Bs,[t[4]||(t[4]=l("div",{class:"hint"},"输入店铺名后点“添加”加入下方备选区。勾选备选店铺后先做“匹配店铺”,确认命中紫鸟索引后再推送到 Python 队列。",-1)),l("div",qs,[b(s,{modelValue:X.value,"onUpdate:modelValue":t[0]||(t[0]=o=>X.value=o),clearable:"",placeholder:"请输入店铺名后回车或点击添加",onKeyup:is(Je,["enter"])},null,8,["modelValue"]),l("button",{type:"button",class:"opt-btn",disabled:Z.value,onClick:Je},v(Z.value?"添加中...":"添加"),9,js)])]),t[11]||(t[11]=l("div",{class:"section-title"},"备选店铺",-1)),he.value.length?(i(),c("div",zs,[b(n,{data:he.value,"row-key":"id",height:"240",class:"candidate-table",onSelectionChange:Mt},{default:N(()=>[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:N(({row:o})=>[l("button",{type:"button",class:"link-danger",onClick:f=>Pt(o)},"删除",8,Ys)]),_:1})]),_:1},8,["data"])])):(i(),c("div",Ks,"暂无备选店铺,请先输入并添加")),t[12]||(t[12]=l("div",{class:"section-title"},"国家配置",-1)),t[13]||(t[13]=l("p",{class:"hint country-pref-hint"},"勾选的国家会在每一个执行时间点都执行一遍。勾选区会按照已选顺序排在前面,下方支持拖拽排序,和商品风险页保持一致。",-1)),l("div",Gs,[(i(!0),c(L,null,H(yt.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:Qe(o.code),onChange:f=>At(o.code,f)},null,40,Js),l("span",Qs,v(o.label)+" ("+v(o.code)+")",1)]))),128))]),g.value.length?(i(),c("div",Ws,[t[6]||(t[6]=l("div",{class:"country-order-caption"},"已选顺序(拖拽 ⋮⋮ 调整)",-1)),l("div",Xs,[(i(!0),c(L,null,H(g.value,(o,f)=>(i(),c("div",{key:o,class:ie(["country-drag-row",{dragging:q.value===f}]),draggable:"true",onDragstart:V=>Ft(f),onDragend:Ut,onDragover:t[1]||(t[1]=vt(()=>{},["prevent"])),onDrop:vt(V=>Rt(f),["prevent"])},[t[5]||(t[5]=l("span",{class:"drag-handle",title:"拖动排序"},"⋮⋮",-1)),l("span",ea,v(He(o))+" ("+v(o)+")",1)],42,Zs))),128))])])):T("",!0),ge.value?(i(),c("div",ta,"保存中...")):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",sa,[b(r,{modelValue:ve.value,"onUpdate:modelValue":t[2]||(t[2]=o=>ve.value=o),class:"listing-filter-select",teleported:"",placeholder:"选择筛选类型"},{default:N(()=>[(i(!0),c(L,null,H(hs(Rs),o=>(i(),mt(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",aa,[ps(l("input",{"onUpdate:modelValue":t[3]||(t[3]=o=>D.value=o),type:"checkbox",disabled:$.value,onChange:Ot},null,40,na),[[gs,D.value]]),t[7]||(t[7]=l("span",null,"启用多时间点调度",-1))]),D.value?(i(),c("div",oa,[(i(!0),c(L,null,H(S.value,(o,f)=>(i(),c("div",{key:`schedule-${f}`,class:"schedule-row"},[b(d,{"model-value":o,type:"datetime","value-format":"YYYY-MM-DD HH:mm:ss",format:"MM-DD HH:mm",placeholder:"选择执行时间",disabled:$.value,class:"schedule-picker","onUpdate:modelValue":V=>Bt(f,V)},null,8,["model-value","disabled","onUpdate:modelValue"]),l("button",{type:"button",class:"schedule-remove",disabled:$.value||S.value.length<=1,onClick:V=>Ht(f)},"删除",8,la)]))),128)),l("button",{type:"button",class:"schedule-add",disabled:$.value,onClick:Vt},"添加时间点",8,ra),t[8]||(t[8]=l("p",{class:"hint schedule-hint"},"默认开启定时;支持按 `MM-DD HH:mm` 配置多个时间点,前端不再限制选择更早时间,实际是否可执行以后端校验为准。",-1))])):T("",!0),l("div",ua,[l("button",{type:"button",class:"btn-run",disabled:ee.value,onClick:jt},v(ee.value?"匹配中...":"匹配店铺"),9,ia),l("button",{type:"button",class:"btn-run btn-queue",disabled:$.value||!_.value.length,onClick:as},v($.value?"推送中...":D.value?"创建定时任务":"推送到 Python 队列"),9,ca)]),t[17]||(t[17]=l("p",{class:"loading-msg"},[ce("已支持多阶段定时执行;Python 每处理完一店可回传 "),l("code",null,"/api/shop-match/tasks/{taskId}/result"),ce(",后端会按阶段汇总并在超时场景自动收尾。")],-1)),w.value?(i(),c("div",da,[t[9]||(t[9]=l("div",{class:"section-title queue-debug-title"},"推送结果",-1)),l("div",fa,v(w.value),1),B.value?(i(),c("pre",ha,v(B.value),1)):T("",!0)])):T("",!0)]),l("section",va,[t[26]||(t[26]=l("div",{class:"panel-header"},"匹配与任务",-1)),l("div",ma,[l("div",pa,[l("div",ga,[t[18]||(t[18]=l("span",{class:"summary-label"},"候选店铺",-1)),l("strong",null,v(j.value.candidateCount),1)]),l("div",ya,[t[19]||(t[19]=l("span",{class:"summary-label"},"已结束任务",-1)),l("strong",null,v(j.value.processedTaskCount),1)]),l("div",wa,[t[20]||(t[20]=l("span",{class:"summary-label"},"成功",-1)),l("strong",null,v(j.value.successTaskCount),1)]),l("div",Sa,[t[21]||(t[21]=l("span",{class:"summary-label"},"失败",-1)),l("strong",null,v(j.value.failedTaskCount),1)])]),t[25]||(t[25]=l("div",{class:"subsection-title"},"匹配结果",-1)),_.value.length?(i(),mt(n,{key:1,data:_.value,"row-key":M,"highlight-current-row":!1,class:"result-table match-table"},{default:N(()=>[b(a,{prop:"shopName",label:"店铺名","min-width":"100"}),b(a,{label:"匹配",width:"72",align:"center"},{default:N(({row:o})=>[l("span",{class:ie(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:N(({row:o})=>[ce(v(ts(o.matchStatus)),1)]),_:1}),b(a,{label:"说明","min-width":"140","show-overflow-tooltip":""},{default:N(({row:o})=>[ce(v(ss(o)),1)]),_:1}),b(a,{label:"操作",width:"72",align:"center"},{default:N(({row:o})=>[l("button",{type:"button",class:"link-danger",onClick:f=>Kt(o)},"删除",8,ba)]),_:1})]),_:1},8,["data"])):(i(),c("div",ka,"完成“匹配店铺”后,在此展示紫鸟索引匹配结果,供核对后再推送队列。")),l("div",_a,[t[24]||(t[24]=l("div",{class:"result-list-header"},[l("span",null,"处理记录")],-1)),gt.value?(i(),c(L,{key:1},[ke.value.length?(i(),c("div",Ia,[t[22]||(t[22]=l("div",{class:"result-subsection-title"},"当前任务",-1)),l("ul",Ca,[(i(!0),c(L,null,H(ke.value,o=>(i(),c("li",{key:`cur-${o.resultId}-${o.taskId}`,class:"task-item"},[l("div",Ea,[l("span",{class:"id",title:o.shopName||""},v(o.shopName||"-"),9,$a),l("div",Da,"任务 ID: "+v(o.taskId??"-"),1),o.platform?(i(),c("div",Na,"平台: "+v(o.platform),1)):T("",!0),nt(o)?(i(),c("div",Ma,v(nt(o)),1)):T("",!0),ot(o)?(i(),c("div",Pa,"下一次: "+v(ot(o)),1)):T("",!0),o.error?(i(),c("div",xa,"错误: "+v(o.error),1)):T("",!0)]),l("div",Aa,[l("span",{class:ie(["status",rt(o)])},v(lt(o)),3),ut(o)?(i(),c("button",{key:0,type:"button",class:"download",onClick:f=>it(o)},"下载",8,La)):T("",!0),l("button",{type:"button",class:"btn-delete",onClick:f=>ct(o)},"删除",8,Fa)])]))),128))])])):T("",!0),be.value.length?(i(),c("div",Ua,[t[23]||(t[23]=l("div",{class:"result-subsection-title"},"历史任务",-1)),l("ul",Ra,[(i(!0),c(L,null,H(be.value,o=>(i(),c("li",{key:`his-${o.resultId}-${o.taskId}`,class:"task-item"},[l("div",Oa,[l("span",{class:"id",title:o.shopName||""},v(o.shopName||"-"),9,Va),l("div",Ha,"任务 ID: "+v(o.taskId??"-"),1),o.outputFilename?(i(),c("div",Ba,"文件: "+v(o.outputFilename),1)):T("",!0),o.error?(i(),c("div",qa,"错误: "+v(o.error),1)):T("",!0)]),l("div",ja,[l("span",{class:ie(["status",rt(o)])},v(lt(o)),3),ut(o)?(i(),c("button",{key:0,type:"button",class:"download",onClick:f=>it(o)},"下载",8,Ka)):T("",!0),l("button",{type:"button",class:"btn-delete",onClick:f=>ct(o)},"删除",8,za)])]))),128))])])):T("",!0)],64)):(i(),c("div",Ta,"暂无处理记录。推送队列并等待 Python 回传后,将在此显示下载链接。"))])])])])])}}}),Ga=xs(Ya,[["__scopeId","data-v-800944cb"]]);As.locale("zh-cn");Ls(Ga).use(Fs,{locale:Us}).mount("#app");
|