2 lines
33 KiB
JavaScript
2 lines
33 KiB
JavaScript
import{d as Wt,o as Qt,I as Yt,a as i,c as d,b as S,B as Xt,e as n,w as rt,s as ut,au as Zt,f as k,F as U,u as ce,r as de,t as v,R as es,S as ts,T as G,U as ss,V as as,W as it,n as ye,Y as ns,g as f,h as B,i as X,E as r,av as os,aw as ls,ax as rs,ay as us,az as is,aA as cs,aB as ds,aC as vs,aD as fs,aE as ct,aF as ps,aG as ms,aH as hs,aI as ys,aJ as ws,Q as ks,aK as gs,aL as _s,aM as Is,aN as dt,_ as bs,p as Ss,q as Cs}from"./pywebview-CeWJDVeG.js";/* empty css */import{e as Ts}from"./brand-UU-ckLq6.js";const Ns={class:"page-shell module-page"},Ps={class:"main-content"},Es={class:"left-panel"},As={class:"mode-zone"},Rs={class:"mode-check-row"},xs=["checked"],Ds={class:"mode-check-row"},Ls=["checked"],$s={class:"mode-zone"},Ms={class:"mode-check-row"},Fs={class:"mode-check-row"},Us={key:0,class:"loop-round-input"},Bs={class:"upload-zone"},Os={class:"selected-files clean-placeholder"},qs={key:1,class:"text-muted"},js={class:"input-zone"},Js={class:"input-row"},Ks=["disabled"],Vs={key:0,class:"empty-candidates"},Gs={key:1,class:"candidate-table-scroll"},Hs=["onClick"],zs={class:"country-pref-checks"},Ws=["checked","disabled","onChange"],Qs={class:"country-check-text"},Ys={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={key:4,class:"mode-status-bar"},aa={class:"run-row"},na=["disabled"],oa=["disabled"],la={key:5,class:"loop-status-card"},ra={class:"loop-status-line"},ua={class:"loop-status-line"},ia={class:"loop-status-line"},ca={class:"loop-status-line"},da={key:0,class:"loop-status-line loop-error"},va=["disabled"],fa={key:6,class:"queue-debug-card"},pa={class:"queue-debug-line"},ma={key:0,class:"queue-debug-payload"},ha={class:"right-panel"},ya={class:"task-list-wrap"},wa={class:"clean-result-summary pr-summary"},ka={class:"summary-card"},ga={class:"summary-card"},_a={class:"summary-card"},Ia={class:"summary-card"},ba={key:0,class:"empty-tasks narrow"},Sa=["onClick"],Ca={class:"result-list-wrap"},Ta={key:0,class:"empty-tasks"},Na={key:0,class:"result-subsection"},Pa={class:"task-list clean-result-list"},Ea={class:"left split-result-main"},Aa={key:0,class:"files"},Ra=["title"],xa={class:"files"},Da={key:1,class:"files"},La={key:2,class:"files"},$a={class:"task-right split-result-actions"},Ma=["onClick"],Fa=["onClick"],Ua={key:1,class:"result-subsection"},Ba={class:"task-list clean-result-list"},Oa={class:"left split-result-main"},qa={key:0,class:"files"},ja=["title"],Ja={class:"files"},Ka={key:1,class:"files"},Va={key:2,class:"files"},Ga={class:"task-right split-result-actions"},Ha=["onClick"],za=["onClick"],Wa=Wt({__name:"BrandPriceTrackTab",setup(Ya){const Fe=[{code:"DE",label:"德国"},{code:"UK",label:"英国"},{code:"FR",label:"法国"},{code:"IT",label:"意大利"},{code:"ES",label:"西班牙"}],Z=f(""),we=f([]),Ue=f([]),b=f([]),ve=f(!1),fe=f(!1),O=f(!1),ke=f(!1),Be=f(!1),p=f(""),H=f(""),ge=f({}),q=f("FINITE"),pe=f(1),m=f(null),w=f(null),_e=f(!1),ee=f(!1),A=f(!0),T=f(!1),E=f([]),te=f([]),Ie=B(()=>A.value||T.value),vt=B(()=>{const e=w.value?.status||"";return e==="SUCCESS"||e==="FAILED"||e==="STOPPED"}),be=B(()=>!!m.value&&!vt.value),ft=B(()=>{const e=w.value;if(!e)return"";const t=e.currentRound||1;if(e.executionMode==="INFINITE")return`第 ${t} 轮(无限)`;const s=Math.max(1,e.targetRounds||pe.value||1);return`第 ${t}/${s} 轮`}),h=f(["DE","UK","FR","IT","ES"]),se=f(null),Se=f(!1),Oe=f(!1);let z=null;const ae=f({candidateCount:0,processedTaskCount:0,successTaskCount:0,failedTaskCount:0}),j=f([]),g=f([]),N=f({}),_=f({}),R=f(null),Ce=f(!1);function ne(){return typeof window<"u"&&window.localStorage.getItem("uid")||"0"}function qe(){return`price-track:tasks:${ne()}`}function je(){return`price-track:task-status:${ne()}`}function Je(){return`price-track:tasks-batch-snapshot:${ne()}`}function Ke(){return`price-track:matched-items:${ne()}`}function Ve(){return`price-track:active-loop:${ne()}`}function oe(e,t,s){if(!(typeof window>"u"))try{s?window.localStorage.removeItem(e):window.localStorage.setItem(e,JSON.stringify(t))}catch{}}function pt(){try{const e=typeof window<"u"?window.localStorage.getItem(qe()):null;if(!e){g.value=[];return}const t=JSON.parse(e);g.value=Array.isArray(t)?t.filter(s=>typeof s=="number"&&s>0):[]}catch{g.value=[]}}function Ge(){oe(qe(),g.value,g.value.length===0)}function mt(){try{const e=typeof window<"u"?window.localStorage.getItem(je()):null;if(!e)return;const t=JSON.parse(e),s={};for(const[l,o]of Object.entries(t)){const u=Number(l);u>0&&typeof o=="string"&&(s[u]=o)}N.value=s}catch{}}function le(){oe(je(),N.value,Object.keys(N.value).length===0)}function ht(){try{const e=typeof window<"u"?window.localStorage.getItem(Je()):null;if(!e)return;const t=JSON.parse(e),s={};for(const[l,o]of Object.entries(t)){const u=Number(l);u>0&&o&&typeof o=="object"&&(s[u]=o)}_.value=s}catch{}}function re(){oe(Je(),_.value,Object.keys(_.value).length===0)}function yt(){try{const e=typeof window<"u"?window.localStorage.getItem(Ke()):null;if(!e)return;const t=JSON.parse(e);Array.isArray(t)&&(b.value=t)}catch{}}function me(){oe(Ke(),b.value,b.value.length===0)}function wt(){try{const e=typeof window<"u"?window.localStorage.getItem(Ve()):null;if(!e){m.value=null;return}const t=Number(JSON.parse(e));m.value=Number.isFinite(t)&&t>0?t:null}catch{m.value=null}}function W(){oe(Ve(),m.value,!m.value)}async function kt(){const e=X();if(e?.select_files){const t=await e.select_files({filters:[{name:"Excel/CSV",extensions:"xlsx,xls,csv"}],multiple:!0});t?.paths?.length&&(E.value=t.paths,te.value=await Te(t.paths),r.success(`已选择 ${t.paths.length} 个ASIN文件`));return}if(e?.select_brand_xlsx_files){const t=await e.select_brand_xlsx_files();if(!t?.length)return;E.value=t,te.value=await Te(t),r.success(`已选择 ${t.length} 个ASIN文件`);return}r.warning("当前环境不支持文件选择,请在本地客户端中打开")}async function gt(){const e=X();if(!e?.select_brand_folder){r.warning("当前环境不支持文件夹选择,请在本地客户端中打开");return}try{const t=await e.select_brand_folder();if(!t)return;const s=await Ts(t);if(!s.success||!s.items?.length){r.warning(s.error||"该文件夹下没有可用的 xlsx 文件");return}E.value=s.items.map(l=>l.relativePath||l.absolutePath),te.value=await Te(s.items),r.success(`已选择文件夹内 ${s.items.length} 个ASIN文件`)}catch(t){r.error(t instanceof Error?t.message:"选择失败")}}async function Te(e){const t=X();if(!t?.upload_file_to_java)return[];const s=[];for(const l of e){const o=typeof l=="string"?l:l.absolutePath,u=typeof l=="string"?void 0:l.relativePath,a=await t.upload_file_to_java(o,u);if(!a?.success||!a.data)throw new Error(a?.error||a?.message||`上传失败:${o}`);s.push(a.data)}return s}function Ne(){return te.value.length?te.value.map(e=>e.localPath).filter(e=>!!e):E.value}function Pe(e){A.value=e==="status",T.value=e==="asin"}function He(e){return Fe.find(s=>s.code===e)?.label??e}const _t=B(()=>{const e=h.value,t=new Set(e),s=e.map(o=>({code:o,label:He(o)})),l=Fe.filter(o=>!t.has(o.code)).map(o=>({code:o.code,label:o.label}));return[...s,...l]});function It(e){return h.value.includes(e)}function ze(e){return h.value.length===1&&h.value[0]===e}function bt(e,t){const s=t.target;if(s){if(!s.checked&&ze(e)){s.checked=!0,r.warning("至少保留 1 个国家");return}St(e,s.checked)}}function St(e,t){if(Oe.value=!0,t)h.value.includes(e)||(h.value=[...h.value,e]);else{if(h.value.length<=1){r.warning("至少保留 1 个国家");return}h.value=h.value.filter(s=>s!==e)}We()}function Ct(e){se.value=e}function Tt(){se.value=null}function Nt(e){const t=se.value;if(se.value=null,t==null||t===e)return;Oe.value=!0;const s=[...h.value],[l]=s.splice(t,1);s.splice(e,0,l),h.value=s,We()}function We(){z&&clearTimeout(z),z=setTimeout(()=>{z=null,Pt()},450)}async function Pt(){if(!(h.value.length<1)){Se.value=!0;try{await os(h.value)}catch(e){r.error(e instanceof Error?e.message:"保存国家顺序失败")}finally{Se.value=!1}}}async function Et(){try{const e=await ls();e?.countryCodes?.length&&(h.value=[...e.countryCodes])}catch{}}async function Ee(){try{we.value=await rs()}catch(e){r.error(e instanceof Error?e.message:"加载备选失败")}}async function x(){try{ae.value=await us()}catch{}}async function L(){try{const e=await is();j.value=e.items||[]}catch{j.value=[]}}function $(){for(const e of[...g.value])!j.value.some(s=>s.taskId===e)&&!_.value[e]&&K(e)}function At(e){Ue.value=e||[]}async function Qe(){const e=Z.value.trim();if(!e){r.warning("请输入店铺名称");return}ve.value=!0;try{await cs(e),Z.value="",await Ee(),r.success("已加入备选")}catch(t){r.error(t instanceof Error?t.message:"保存失败")}finally{ve.value=!1}}async function Rt(e){try{await ds(e.id),await Ee(),b.value=[],me(),r.success("已删除")}catch(t){r.error(t instanceof Error?t.message:"删除失败")}}function xt(){const e=new Set,t=Z.value.trim();t&&e.add(t);for(const s of Ue.value)s?.shopName?.trim()&&e.add(s.shopName.trim());if(T.value)for(const s of E.value){const l=(s||"").trim();if(!l)continue;const u=(l.split(/[/\\]/).filter(Boolean).pop()||"").replace(/\.[^.]+$/,"").trim();u&&e.add(u)}return[...e]}async function Dt(){const e=xt();if(!e.length){r.warning("请勾选备选店铺,或在输入框中填写店名");return}if(T.value&&!E.value.length){r.warning("请先选择 ASIN 文件");return}fe.value=!0;try{const t=await vs(e,{asinFiles:T.value?Ne():[],countryCodes:[...h.value]}),s=t.items||[];ge.value=t.asinRowsByCountry||{};const l=new Map(b.value.map(o=>[(o.shopName||"").trim(),o]));for(const o of s)l.set((o.shopName||"").trim(),o);b.value=[...l.values()],me(),Be.value&&Ot(),r.success(`匹配 ${s.length} 个店铺`)}catch(t){r.error(t instanceof Error?t.message:"匹配失败")}finally{fe.value=!1}}function Lt(e){const t=(e||"").trim();return{MATCHED:"已匹配",PENDING:"待匹配",CONFLICT:"需人工确认",INDEX_STALE:"索引过期"}[t]||t||"—"}function $t(e){const t=(e.matchMessage||"").trim();return t||(e.matched&&e.matchStatus==="MATCHED"?"紫鸟索引已命中,可推送队列":e.matched?"已关联索引,请结合状态列查看是否可推送":"未命中或未就绪,请检查店铺名与索引刷新")}function Ae(e){return`${(e.shopName||"").trim()}${e.shopId??""}`}function Ye(e){const t=e instanceof Error?e.message:String(e||"");return/记录不存在|不存在|已删除|not\s*found|404/i.test(t)}function Mt(){return{enabled:!1,mode:"NONE",deleteWhenPriceBelowMinimum:!1,compareField:"price",thresholdField:"minimumPrice",target:"skip_asin",source:"frontend-price-track"}}function J(e){if(!e.length)return;const t=new Set(e.map(l=>Ae(l))),s=new Set(e.map(l=>(l.shopName||"").trim()).filter(l=>!!l));b.value=b.value.filter(l=>{const o=Ae(l),u=(l.shopName||"").trim();return!t.has(o)&&(!u||!s.has(u))}),me()}function Ft(e){j.value=j.value.filter(t=>!(e.resultId!=null&&t.resultId===e.resultId||e.taskId!=null&&t.taskId===e.taskId))}async function Ut(e){const t=[...b.value];J([e]);const s=(e.shopName||"").trim();if(!s){r.success("已从匹配结果中移除");return}try{(await fs(s))?.removed&&(await L(),await x(),$()),r.success("已从匹配结果中移除")}catch(l){if(Ye(l)){await L(),await x(),$(),r.success("后端记录已不存在,已同步移除本地记录");return}b.value=t,me(),r.error(l instanceof Error?l.message:"同步后端失败")}}function Xe(e,t){const s=e.items?.[0],l=(t.shopName||"").trim(),o=t.skipAsins||e.skipAsinsByCountry||{},u=e.skipAsinDetailsByCountry||{},a=e.minimumPriceByCountryAndAsin||{},c=Mt(),C=s?.resultId??null,F=s?.loopRunId??null,Y=s?.roundIndex??null,y=s?.taskStatus||"RUNNING",D=s?.success??!1,P=s?.error||null,I=s?.outputFilename||null,V=s?.downloadUrl||null;return{type:"price-track-run",ts:Date.now(),data:{task_id:e.taskId,result_id:C,shop_name:l,shop_id:t.shopId??null,platform:t.platform||"",company_name:t.companyName||"",shop_mall_name:t.shopMallName||"",matched:!!t.matched,match_status:t.matchStatus||"",match_message:t.matchMessage||null,success:D,error:P,output_filename:I,download_url:V,task_status:y,loop_run_id:F,round_index:Y,country_codes:[...h.value],mode:A.value?"status":"asin",skip_asins:o,skip_asins_by_country:o,skip_asin_details_by_country:u,minimum_price_by_country_and_asin:a,skip_asin_delete_policy:c,delete_skip_asin_when_price_below_minimum:c.deleteWhenPriceBelowMinimum,deleteSkipAsinWhenPriceBelowMinimum:c.deleteWhenPriceBelowMinimum,asin_rows_by_country:Object.keys(ge.value).length?ge.value:e.asinRowsByCountry||{},taskId:e.taskId,resultId:C,shopName:l,shopId:t.shopId??null,platformName:t.platform||"",companyName:t.companyName||"",shopMallName:t.shopMallName||"",matchStatus:t.matchStatus||"",matchMessage:t.matchMessage||null,outputFilename:I,downloadUrl:V,taskStatus:y,loopRunId:F,roundIndex:Y}}}async function Ze(e){let t=0;const s=30;for(;;){try{const l=await dt([e]);if(t>0&&(p.value=`任务 ${e} 后端已恢复,继续等待执行结果...`),t=0,(l.missingTaskIds||[]).includes(e))return K(e),await L(),await x(),$(),"FAILED";const o=(l.items||[]).find(a=>a.task?.id===e),u=o?.task?.status||"";if(o){const a=_.value[e];_.value={..._.value,[e]:a?{...a,task:{...a.task||{},...o.task||{}}}:o},re()}if(u&&(N.value={...N.value,[e]:u},le()),u==="SUCCESS"||u==="FAILED")return K(e),await L(),await x(),$(),u}catch(l){const o=(l instanceof Error?l.message:String(l||"")).toLowerCase();if(!["无法连接到后端服务","bad gateway","gateway timeout","network error","timeout","502","503","504"].some(a=>o.includes(a.toLowerCase())))throw l;if(t+=1,t>=s)throw new Error(`任务 ${e} 等待后端恢复超时,请稍后手动刷新查看状态`);p.value=`任务 ${e} 运行中,正在等待后端服务恢复(${t}/${s})...`,(t===1||t%5===0)&&r.warning(`任务 ${e} 运行中,后端服务暂时不可用,正在自动重试`),await new Promise(a=>{window.setTimeout(()=>a(),M())});continue}await new Promise(l=>{window.setTimeout(()=>l(),M())})}}function Bt(){return b.value.find(e=>e.matched)}async function Ot(){if(ke.value)return;if(!Ie.value){r.warning("请至少选择一个跟价模式");return}if(T.value&&!E.value.length){r.warning("请先选择 ASIN 文件");return}const e=X();if(!e?.enqueue_json){r.error("当前环境未启用 pywebview enqueue_json");return}if(!b.value.filter(o=>o.matched).length){r.warning("无可用匹配店铺");return}ke.value=!0,O.value=!0,H.value="";let s=0,l=0;try{let o=0;for(;Be.value;){const u=Bt();if(!u)break;o+=1;try{let a=0;const c=await(async()=>{for(;;)try{return await ct({userId:0,statusMode:A.value,asinMode:T.value,items:[u],asinFiles:Ne(),countryCodes:[...h.value]})}catch(y){const D=(y instanceof Error?y.message:String(y||"")).toLowerCase(),P=["无法连接到后端服务","bad gateway","gateway timeout","network error","timeout","502","503","504"].some(I=>D.includes(I.toLowerCase()));if(a+=1,!P||a>=10)throw y;p.value="后端服务暂时不可用,正在重试创建任务("+a+"/10)...",await new Promise(I=>{window.setTimeout(()=>I(),M())})}})();_.value={..._.value,[c.taskId]:{task:{id:c.taskId,status:"RUNNING"},items:c.items}},N.value={...N.value,[c.taskId]:"RUNNING"},re(),le();const C=Xe(c,u);H.value=JSON.stringify(C,null,2);const F=await e.enqueue_json(C);if(!F?.success){l+=1,p.value="任务 "+c.taskId+" 推送失败,已自动继续下一条:"+(F?.error||"未知错误"),r.error(p.value),J([u]);continue}if(he(c.taskId),Q(!0),J([u]),p.value="任务 "+c.taskId+" 已入队,等待执行完成",await Ze(c.taskId)!=="SUCCESS"){l+=1,p.value="任务 "+c.taskId+" 执行失败,已自动继续下一条",r.error(p.value);continue}s+=1,p.value="任务 "+c.taskId+" 已完成"}catch(a){l+=1,p.value="第 "+o+" 条任务处理失败,已自动继续下一条:"+(a instanceof Error?a.message:"未知错误"),r.error(p.value),J([u]);continue}}(s>0||l>0)&&r.success("店铺任务推送已完成:成功 "+s+" 条,失败 "+l+" 条")}catch(o){p.value=o instanceof Error?o.message:"推送异常",r.error(p.value)}finally{ke.value=!1,O.value=!1}}function ue(e){w.value=e,m.value=e?.id??null,W()}function ie(){if(!w.value)return;const e=w.value.status||"";(e==="SUCCESS"||e==="FAILED"||e==="STOPPED")&&(w.value=null,m.value=null,W())}async function Re(){if(!m.value)return null;try{const e=await ps(m.value);return ue(e),ie(),w.value}catch(e){const t=(e instanceof Error?e.message:String(e||"")).toLowerCase();return["无法连接到后端服务","bad gateway","gateway timeout","network error","timeout","502","503","504"].some(l=>t.includes(l.toLowerCase()))?w.value:(w.value=null,m.value=null,W(),null)}}function qt(e){_.value={..._.value,[e.taskId]:{task:{id:e.taskId,status:"RUNNING"},items:e.items}},N.value={...N.value,[e.taskId]:"RUNNING"},re(),le()}async function jt(){if(!(!m.value||ee.value)){ee.value=!0;try{const e=await ms(m.value);ue(e),ie(),r.success(e.status==="STOPPED"?"循环已停止":"已请求停止,当前子任务完成后将收尾")}catch(e){r.error(e instanceof Error?e.message:"停止循环失败")}finally{ee.value=!1}}}async function xe(e){if(_e.value||(e&&m.value!==e&&(m.value=e,W()),!m.value))return;const t=X();if(!t?.enqueue_json)throw new Error("当前环境未启用 pywebview enqueue_json");_e.value=!0;try{for(;m.value;){const s=await Re();if(!s||s.status==="SUCCESS"||s.status==="FAILED"||s.status==="STOPPED")break;if(s.activeTaskId){const y=s.activeTaskId;he(y),Q(!0);const D=await Ze(y);let P=0;const I=await(async()=>{for(;;)try{return await hs(s.id,y)}catch(V){const Ht=(V instanceof Error?V.message:String(V||"")).toLowerCase(),zt=["无法连接到后端服务","bad gateway","gateway timeout","network error","timeout","502","503","504"].some(Me=>Ht.includes(Me.toLowerCase()));if(P+=1,!zt||P>=10)throw V;p.value=`循环任务 ${s.id} 等待后端恢复后确认子任务完成(${P}/10)...`,await new Promise(Me=>{window.setTimeout(()=>Me(),M())})}})();if(ue(I),await L(),await x(),$(),ie(),D!=="SUCCESS")throw new Error(I.errorMessage||`任务 ${s.activeTaskId} 执行失败`);continue}let l=0;const o=await(async()=>{for(;;)try{return await ys(s.id)}catch(y){const D=(y instanceof Error?y.message:String(y||"")).toLowerCase(),P=["无法连接到后端服务","bad gateway","gateway timeout","network error","timeout","502","503","504"].some(I=>D.includes(I.toLowerCase()));if(l+=1,!P||l>=10)throw y;p.value=`循环任务 ${s.id} 等待后端恢复后继续派发(${l}/10)...`,await new Promise(I=>{window.setTimeout(()=>I(),M())})}})();if(ue(o.loopRun||s),!o.childTaskRequest){ie();break}const u=o.childTaskRequest,a=u.items?.[0];if(!a)throw new Error("循环任务缺少待执行店铺");let c=0;const C=await(async()=>{for(;;)try{return await ct(u)}catch(y){const D=(y instanceof Error?y.message:String(y||"")).toLowerCase(),P=["无法连接到后端服务","bad gateway","gateway timeout","network error","timeout","502","503","504"].some(I=>D.includes(I.toLowerCase()));if(c+=1,!P||c>=10)throw y;p.value=`循环任务 ${s.id} 等待后端恢复后重试创建子任务(${c}/10)...`,await new Promise(I=>{window.setTimeout(()=>I(),M())})}})();qt(C);const F=Xe(C,a);H.value=JSON.stringify(F,null,2);const Y=await t.enqueue_json(F);if(!Y?.success)throw new Error(Y?.error||`任务 ${C.taskId} 推送失败`);he(C.taskId),Q(!0),J([a]),p.value=`循环任务 ${s.id} 第 ${o.childTaskRequest.roundIndex||1} 轮已推送:${a.shopName||C.taskId}`}}finally{_e.value=!1}}async function Jt(){if(be.value){try{await xe(m.value||void 0)}catch(t){t instanceof Error&&/子任务不存在|循环任务不存在|404/.test(t.message)&&(w.value=null,m.value=null,W());const s=t instanceof Error?t.message:"循环执行失败";p.value=s,r.error(s)}return}if(!Ie.value){r.warning("请至少选择一个跟价模式");return}if(T.value&&!E.value.length){r.warning("请先选择 ASIN 文件");return}const e=b.value.filter(t=>t.matched);if(!e.length){r.warning("请先匹配可用店铺");return}O.value=!0,H.value="";try{const t=await ws({userId:0,statusMode:A.value,asinMode:T.value,items:e,asinFiles:Ne(),countryCodes:[...h.value],executionMode:q.value,targetRounds:q.value==="FINITE"?Math.max(1,Number(pe.value)||1):void 0});ue(t),p.value=`循环任务 ${t.id} 已创建`,await xe(t.id),ie(),m.value||r.success("跟价循环已启动")}catch(t){const s=t instanceof Error?t.message:"启动循环失败";p.value=s,r.error(s)}finally{O.value=!1}}function M(){return ks()}async function Kt(){const e=g.value.filter(t=>t>0);if(e.length)try{const t=await dt(e);let s=!1;const l={..._.value};for(const o of t.missingTaskIds||[])K(o),delete l[o];for(const o of t.items||[]){const u=o.task?.id,a=o.task?.status;if(typeof u!="number"||u<=0)continue;const c=l[u];l[u]=c?{...c,task:{...c.task||{},...o.task||{}}}:o,a&&(N.value[u]=a,(a==="SUCCESS"||a==="FAILED")&&(K(u),s=!0))}_.value=l,re(),le(),await L(),$(),s&&await x()}catch{}}function Q(e=!1){if(R.value){if(!e)return;window.clearTimeout(R.value),R.value=null}const t=async()=>{if(R.value=null,Ce.value){Q();return}if(g.value.length){Ce.value=!0;try{await Kt()}finally{Ce.value=!1}g.value.length>0&&(R.value=window.setTimeout(t,M()))}};e?t():R.value=window.setTimeout(t,M())}function Vt(){R.value&&(window.clearTimeout(R.value),R.value=null)}function he(e){g.value.includes(e)||(g.value=[...g.value,e],Ge())}function K(e){if(g.value=g.value.filter(t=>t!==e),Ge(),delete N.value[e],le(),_.value[e]){const t={..._.value};delete t[e],_.value=t,re()}}const De=B(()=>{const e=j.value.filter(s=>s.taskId&&g.value.includes(s.taskId)&&!tt(s.taskId)),t=new Set(e.map(s=>s.taskId).filter(s=>typeof s=="number"));for(const s of g.value){if(t.has(s))continue;const l=_.value[s]?.items?.[0];l&&e.push(l)}return e}),Le=B(()=>j.value.filter(e=>!e.taskId||!g.value.includes(e.taskId)||tt(e.taskId))),Gt=B(()=>De.value.length>0||Le.value.length>0);function et(e){return e&&(N.value[e]||_.value[e]?.task?.status)||""}function tt(e){const t=et(e);return t==="SUCCESS"||t==="FAILED"}function $e(e){const t=e.taskId;return t?et(t)||e.taskStatus||"":e.taskStatus||""}function st(e){const t=$e(e);return t==="SUCCESS"?"已完成":t==="FAILED"?"失败":t==="RUNNING"?"执行中":e.success?"已完成":"等待中"}function at(e){const t=$e(e);return t==="SUCCESS"||e.success?"success":t==="FAILED"?"failed":"running"}function nt(e){return e.resultId?$e(e)==="SUCCESS"||e.success===!0:!1}async function ot(e){if(!e.resultId)return;const t=X(),s=e.outputFilename||`${e.shopName||"result"}.xlsx`;if(!t?.save_file_from_url_new){r.error("当前客户端未提供 save_file_from_url_new,无法下载文件");return}const l=gs(e.resultId),o=await t.save_file_from_url_new(l,s);o.success?r.success(`已保存:${o.path||s}`):o.error&&o.error!=="用户取消"&&r.error(o.error)}async function lt(e){try{e.resultId!=null?await _s(e.resultId):e.taskId!=null&&await Is(e.taskId),w.value&&(e.loopRunId!=null&&e.loopRunId===w.value.id||e.taskId!=null&&e.taskId===w.value.activeTaskId)&&await Re(),e.taskId!=null&&K(e.taskId),J([e]),await L(),await x(),$(),r.success("已删除")}catch(t){if(Ye(t)){e.taskId!=null&&K(e.taskId),J([e]),Ft(e),await x(),$(),r.success("后端记录已不存在,已同步移除本地记录");return}r.error(t instanceof Error?t.message:"删除失败")}}return Qt(()=>{!A.value&&!T.value&&Pe("status"),pt(),mt(),ht(),yt(),wt(),Ee().catch(()=>{}),Et().catch(()=>{}),L().catch(()=>{}),x().catch(()=>{}),g.value.length&&Q(!0),m.value&&Re().then(e=>{e?.activeTaskId&&(he(e.activeTaskId),Q(!0)),e&&e.status!=="SUCCESS"&&e.status!=="FAILED"&&e.status!=="STOPPED"&&xe(e.id)}).catch(()=>{m.value=null,W()})}),Yt(()=>{Vt(),z&&clearTimeout(z)}),(e,t)=>{const s=Zt,l=ts,o=ss,u=as;return i(),d("div",Ns,[S(Xt,{active:"pricing"}),n("div",Ps,[n("aside",Es,[t[25]||(t[25]=n("div",{class:"section-title"},"跟价模式",-1)),n("div",As,[n("label",Rs,[n("input",{type:"radio",name:"price-track-mode",class:"mode-check-input",checked:A.value,onChange:t[0]||(t[0]=a=>Pe("status"))},null,40,xs),t[7]||(t[7]=n("span",{class:"mode-check-text"},"按商品状态(全量)",-1)),t[8]||(t[8]=n("span",{class:"mode-hint-inline"},"— 获取店铺所有在售ASIN跑跟价",-1))]),n("label",Ds,[n("input",{type:"radio",name:"price-track-mode",class:"mode-check-input",checked:T.value,onChange:t[1]||(t[1]=a=>Pe("asin"))},null,40,Ls),t[9]||(t[9]=n("span",{class:"mode-check-text"},"按指定ASIN文档(自定义)",-1)),t[10]||(t[10]=n("span",{class:"mode-hint-inline"},"— 上传ASIN表格,按文档内ASIN跑跟价",-1))])]),t[26]||(t[26]=n("div",{class:"section-title"},"执行配置",-1)),n("div",$s,[n("label",Ms,[rt(n("input",{"onUpdate:modelValue":t[2]||(t[2]=a=>q.value=a),type:"radio",class:"mode-check-input",value:"FINITE"},null,512),[[ut,q.value]]),t[11]||(t[11]=n("span",{class:"mode-check-text"},"执行固定轮次",-1)),t[12]||(t[12]=n("span",{class:"mode-hint-inline"},"按整批店铺重复执行指定次数",-1))]),n("label",Fs,[rt(n("input",{"onUpdate:modelValue":t[3]||(t[3]=a=>q.value=a),type:"radio",class:"mode-check-input",value:"INFINITE"},null,512),[[ut,q.value]]),t[13]||(t[13]=n("span",{class:"mode-check-text"},"无限执行",-1)),t[14]||(t[14]=n("span",{class:"mode-hint-inline"},"上一轮全部成功后立即开始下一轮",-1))]),q.value==="FINITE"?(i(),d("div",Us,[t[15]||(t[15]=n("span",{class:"hint"},"轮次",-1)),S(s,{modelValue:pe.value,"onUpdate:modelValue":t[4]||(t[4]=a=>pe.value=a),min:1,max:9999,disabled:be.value||O.value},null,8,["modelValue","disabled"])])):k("",!0)]),T.value?(i(),d(U,{key:0},[t[17]||(t[17]=n("div",{class:"section-title"},[ce("ASIN文档 "),n("span",{class:"tag-badge"},"自定义模式")],-1)),n("div",Bs,[t[16]||(t[16]=n("div",{class:"hint"},"上传包含ASIN的表格文件,按文档内的ASIN进行跟价。",-1)),n("div",{class:"btns"},[n("button",{type:"button",class:"opt-btn",onClick:kt},"选择ASIN文件"),n("button",{type:"button",class:"opt-btn",onClick:gt},"选择文件夹")]),n("div",Os,[E.value.length?(i(!0),d(U,{key:0},de(E.value,(a,c)=>(i(),d("span",{key:c},v(a),1))),128)):(i(),d("span",qs,"暂未选择"))])])],64)):k("",!0),A.value?(i(),d(U,{key:1},[t[19]||(t[19]=n("div",{class:"section-title"},"店铺名称",-1)),n("div",js,[t[18]||(t[18]=n("div",{class:"hint"},"输入店铺名后点「确认」加入下方备选区。可同时配合上方模式使用。",-1)),n("div",Js,[S(l,{modelValue:Z.value,"onUpdate:modelValue":t[5]||(t[5]=a=>Z.value=a),clearable:"",placeholder:"请输入店铺名称",onKeyup:es(Qe,["enter"])},null,8,["modelValue"]),n("button",{type:"button",class:"opt-btn",disabled:ve.value,onClick:Qe},v(ve.value?"保存中…":"确认"),9,Ks)])]),t[20]||(t[20]=n("div",{class:"section-title"},"备选店铺",-1)),we.value.length?(i(),d("div",Gs,[S(u,{data:we.value,"row-key":"id",height:"160",class:"candidate-table",onSelectionChange:At},{default:G(()=>[S(o,{type:"selection",width:"42"}),S(o,{prop:"shopName",label:"店铺名","min-width":"120","show-overflow-tooltip":""}),S(o,{label:"操作",width:"72",align:"center"},{default:G(({row:a})=>[n("button",{type:"button",class:"link-danger",onClick:c=>Rt(a)},"删除",8,Hs)]),_:1})]),_:1},8,["data"])])):(i(),d("div",Vs,"暂无备选店铺,请先输入并确认"))],64)):k("",!0),t[27]||(t[27]=n("div",{class:"section-title"},"处理国家与顺序",-1)),t[28]||(t[28]=n("p",{class:"hint country-pref-hint"}," 勾选需要处理的站点;拖拽调整顺序。设置会按当前登录用户保存。 ",-1)),n("div",zs,[(i(!0),d(U,null,de(_t.value,a=>(i(),d("label",{key:a.code,class:"country-check-row"},[n("input",{type:"checkbox",class:"country-check-input",checked:It(a.code),disabled:ze(a.code),onChange:c=>bt(a.code,c)},null,40,Ws),n("span",Qs,v(a.label)+"("+v(a.code)+")",1)]))),128))]),h.value.length?(i(),d("div",Ys,[t[22]||(t[22]=n("div",{class:"country-order-caption"},"已选顺序(拖拽 ⋮⋮ 调整)",-1)),n("div",Xs,[(i(!0),d(U,null,de(h.value,(a,c)=>(i(),d("div",{key:a,class:ye(["country-drag-row",{dragging:se.value===c}]),draggable:"true",onDragstart:C=>Ct(c),onDragend:Tt,onDragover:t[6]||(t[6]=it(()=>{},["prevent"])),onDrop:it(C=>Nt(c),["prevent"])},[t[21]||(t[21]=n("span",{class:"drag-handle",title:"拖动排序"},"⋮⋮",-1)),n("span",ea,v(He(a))+"("+v(a)+")",1)],42,Zs))),128))])])):k("",!0),Se.value?(i(),d("div",ta,"保存中…")):k("",!0),A.value?(i(),d("div",sa,[...t[23]||(t[23]=[n("span",{class:"mode-status-dot active"},null,-1),n("span",{class:"mode-status-text"},"商品状态模式:将在售(Active)ASIN纳入跟价,跳过ASIN由后台配置的店铺维度数据提供",-1)])])):k("",!0),n("div",aa,[n("button",{type:"button",class:"btn-run",disabled:fe.value,onClick:Dt},v(fe.value?"匹配中…":"匹配店铺"),9,na),n("button",{type:"button",class:"btn-run btn-queue",disabled:O.value||!Ie.value,onClick:Jt},v(O.value?"推送中…":"推送到 Python 队列"),9,oa)]),t[29]||(t[29]=n("p",{class:"loading-msg"},[ce("上传ASIN文档和多选店铺"),n("strong",null,"可同时生效"),ce(",跟价结果合并输出。全量和自定义的区别仅在于ASIN来源不同。")],-1)),w.value?(i(),d("div",la,[n("div",ra,"循环任务 ID:"+v(w.value.id),1),n("div",ua,"状态:"+v(w.value.status||"-"),1),n("div",ia,"进度:"+v(ft.value),1),n("div",ca,"当前店铺:"+v((w.value.currentShopIndex||0)+1)+"/"+v(w.value.totalShopCount||0),1),w.value.errorMessage?(i(),d("div",da,v(w.value.errorMessage),1)):k("",!0),n("button",{type:"button",class:"opt-btn loop-stop-btn",disabled:!be.value||ee.value,onClick:jt},v(ee.value?"停止中...":"停止循环"),9,va)])):k("",!0),p.value?(i(),d("div",fa,[t[24]||(t[24]=n("div",{class:"section-title queue-debug-title"},"推送结果",-1)),n("div",pa,v(p.value),1),H.value?(i(),d("pre",ma,v(H.value),1)):k("",!0)])):k("",!0)]),n("section",ha,[t[38]||(t[38]=n("div",{class:"panel-header"},"匹配与任务",-1)),n("div",ya,[n("div",wa,[n("div",ka,[t[30]||(t[30]=n("span",{class:"summary-label"},"备选店铺数",-1)),n("strong",null,v(ae.value.candidateCount),1)]),n("div",ga,[t[31]||(t[31]=n("span",{class:"summary-label"},"已结束任务",-1)),n("strong",null,v(ae.value.processedTaskCount),1)]),n("div",_a,[t[32]||(t[32]=n("span",{class:"summary-label"},"成功任务",-1)),n("strong",null,v(ae.value.successTaskCount),1)]),n("div",Ia,[t[33]||(t[33]=n("span",{class:"summary-label"},"失败任务",-1)),n("strong",null,v(ae.value.failedTaskCount),1)])]),t[37]||(t[37]=n("div",{class:"subsection-title"},"匹配结果",-1)),b.value.length?(i(),ns(u,{key:1,data:b.value,"row-key":Ae,"highlight-current-row":!1,class:"result-table match-table"},{default:G(()=>[S(o,{prop:"shopName",label:"店铺名","min-width":"100"}),S(o,{label:"匹配",width:"72",align:"center"},{default:G(({row:a})=>[n("span",{class:ye(a.matched?"ok":"fail")},v(a.matched?"是":"否"),3)]),_:1}),S(o,{prop:"shopId",label:"店铺ID","min-width":"120","show-overflow-tooltip":""}),S(o,{prop:"platform",label:"平台",width:"88","show-overflow-tooltip":""}),S(o,{prop:"companyName",label:"公司","min-width":"100","show-overflow-tooltip":""}),S(o,{label:"状态",width:"100","show-overflow-tooltip":""},{default:G(({row:a})=>[ce(v(Lt(a.matchStatus)),1)]),_:1}),S(o,{label:"说明","min-width":"140","show-overflow-tooltip":""},{default:G(({row:a})=>[ce(v($t(a)),1)]),_:1}),S(o,{label:"操作",width:"72",align:"center"},{default:G(({row:a})=>[n("button",{type:"button",class:"link-danger",onClick:c=>Ut(a)},"删除",8,Sa)]),_:1})]),_:1},8,["data"])):(i(),d("div",ba,"完成「匹配店铺」后,在此展示紫鸟索引匹配结果,供核对后再推送队列。")),n("div",Ca,[t[36]||(t[36]=n("div",{class:"result-list-header"},[n("span",null,"处理记录")],-1)),Gt.value?(i(),d(U,{key:1},[De.value.length?(i(),d("div",Na,[t[34]||(t[34]=n("div",{class:"result-subsection-title"},"当前任务",-1)),n("ul",Pa,[(i(!0),d(U,null,de(De.value,a=>(i(),d("li",{key:`cur-${a.resultId}-${a.taskId}`,class:"task-item split-result-item"},[n("div",Ea,[a.roundIndex?(i(),d("div",Aa,"轮次:第 "+v(a.roundIndex)+" 轮",1)):k("",!0),n("span",{class:"id",title:a.shopName||""},v(a.shopName||"-"),9,Ra),n("div",xa,"任务 ID:"+v(a.taskId??"-"),1),a.platform?(i(),d("div",Da,"平台:"+v(a.platform),1)):k("",!0),a.error?(i(),d("div",La,"错误:"+v(a.error),1)):k("",!0)]),n("div",$a,[n("span",{class:ye(["status",at(a)])},v(st(a)),3),nt(a)?(i(),d("button",{key:0,type:"button",class:"download",onClick:c=>ot(a)}," 下载 ",8,Ma)):k("",!0),n("button",{type:"button",class:"btn-delete",onClick:c=>lt(a)},"删除",8,Fa)])]))),128))])])):k("",!0),Le.value.length?(i(),d("div",Ua,[t[35]||(t[35]=n("div",{class:"result-subsection-title"},"历史记录",-1)),n("ul",Ba,[(i(!0),d(U,null,de(Le.value,a=>(i(),d("li",{key:`his-${a.resultId}-${a.taskId}`,class:"task-item split-result-item"},[n("div",Oa,[a.roundIndex?(i(),d("div",qa,"轮次:第 "+v(a.roundIndex)+" 轮",1)):k("",!0),n("span",{class:"id",title:a.shopName||""},v(a.shopName||"-"),9,ja),n("div",Ja,"任务 ID:"+v(a.taskId??"-"),1),a.outputFilename?(i(),d("div",Ka,"文件:"+v(a.outputFilename),1)):k("",!0),a.error?(i(),d("div",Va,"错误:"+v(a.error),1)):k("",!0)]),n("div",Ga,[n("span",{class:ye(["status",at(a)])},v(st(a)),3),nt(a)?(i(),d("button",{key:0,type:"button",class:"download",onClick:c=>ot(a)}," 下载 ",8,Ha)):k("",!0),n("button",{type:"button",class:"btn-delete",onClick:c=>lt(a)},"删除",8,za)])]))),128))])])):k("",!0)],64)):(i(),d("div",Ta,"暂无处理记录。推送队列并等待 Python 回传后,将在此显示下载链接。"))])])])])])}}}),Qa=bs(Wa,[["__scopeId","data-v-f6430de9"]]);Ss(Qa).use(Cs).mount("#app");
|