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