修正 aliChoice 失败根因:类目弹窗没关(遮罩挡住所有必填属性)+ 新增文本型必填兜底 fillRequiredTextInputs
This commit is contained in:
1 parent
de9d988f4c
commit
14214862e6
3 files changed
+145
-5
No files matched your search
+59
-4
@@ -75,17 +75,21 @@ export async function selectCategory(c, kw, preferName) {
|
||||
await wait(2000);
|
||||
}
|
||||
await wait(1800);
|
||||
// 3) 弹窗必须关掉,否则后面的点击全被挡(曾出现「类目已选但弹窗还开着 → 预览图挂不上」)
|
||||
// 3) 弹窗必须关掉,否则后面的点击全被挡(SHEIN/Temu 上是「预览图挂不上」,
|
||||
// aliChoice 上更狠:弹窗不关 → 所有必填属性都填不上 → 保存报一串牛头不对马嘴的错)
|
||||
let left = Number(await c.evalJS(`[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).length`));
|
||||
for (let k = 0; k < 3 && left; k++) {
|
||||
for (let k = 0; k < 6 && left; k++) {
|
||||
await c.evalJS(`(()=>{const m=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).pop();
|
||||
if(!m) return 'closed';
|
||||
const b=[...m.querySelectorAll('button')].find(x=>/^\\s*(确定|选择)\\s*$/.test((x.innerText||'').trim()));
|
||||
const bar=[...m.querySelectorAll('button')];
|
||||
const b=bar.find(x=>/^\\s*(确定|选择)\\s*$/.test((x.innerText||'').trim()))||bar.find(x=>/^\\s*(关闭|取消)\\s*$/.test((x.innerText||'').trim()));
|
||||
if(b) for(const ev of ['mousedown','mouseup','click']) b.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));
|
||||
const c2=[...m.querySelectorAll('.ant-modal-close')][0]; if(c2) c2.click();
|
||||
return b?'ok':'still-open';})()`);
|
||||
await wait(1500);
|
||||
if (k >= 1) await c.send('Input.dispatchKeyEvent', {type:'keyDown', key:'Escape', code:'Escape', windowsVirtualKeyCode:27}).catch(()=>{});
|
||||
await c.send('Input.dispatchKeyEvent', {type:'keyDown', key:'Escape', code:'Escape', windowsVirtualKeyCode:27}).catch(()=>{});
|
||||
await wait(600);
|
||||
if (k >= 3) await c.evalJS(`(()=>{const w=[...document.querySelectorAll('.ant-modal-wrap')].filter(e=>e.offsetParent!==null).pop(); if(w) w.click(); return 1;})()`);
|
||||
left = Number(await c.evalJS(`[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).length`));
|
||||
}
|
||||
await wait(3000);
|
||||
@@ -271,3 +275,54 @@ export async function fillRequiredSelects(c, skip = ['店铺账号', '产品分
|
||||
}
|
||||
return [filled.join(' | '), ticked].filter(x=>x&&x!=='none').join(' | ') || 'none';
|
||||
}
|
||||
|
||||
/** 兜底填「必填但空着」的**文本输入**型类目属性(aliChoice 的「材质(Material)」就是 input,不是 select)。
|
||||
* values:候选值,按顺序给每个空文本项发一个(不够就重复最后一个)。 */
|
||||
export async function fillRequiredTextInputs(c, values = ['Other'], skip = ['店铺账号', '产品分类', '主规格', '产品标题', '店小秘分类', '尺码表', 'ParentSKU']) {
|
||||
const filled = [];
|
||||
for (let round = 0; round < 6; round++) {
|
||||
const tgt = JSON.parse(String(await c.evalJS(`(()=>{
|
||||
const items=[...document.querySelectorAll('.ant-form-item')].filter(e=>{
|
||||
if(e.offsetParent===null) return false;
|
||||
const l=e.querySelector('label');
|
||||
const reqOk = l && (/required/.test(String(l.className)) || (l.innerText||'').trim().startsWith('*') || e.querySelector('.attr-label.required'));
|
||||
if(!reqOk) return false;
|
||||
if(e.querySelector('.ant-form-item')) return false;
|
||||
const name=(l.innerText||'').replace(/\\s+/g,' ').trim();
|
||||
if(${JSON.stringify(skip)}.some(s=>name.includes(s))) return false;
|
||||
if(e.querySelector('.ant-select,.ant-checkbox-group,.ant-radio-group,.ant-cascader')) return false;
|
||||
const inp=e.querySelector('input.ant-input, textarea.ant-input');
|
||||
if(!inp || inp.offsetParent===null) return false;
|
||||
if(String(inp.value||'').trim()) return false;
|
||||
return true;});
|
||||
const it=items[0]; if(!it) return '[]';
|
||||
const l=it.querySelector('label');
|
||||
return JSON.stringify({name:(l.innerText||'').replace(/\\s+/g,' ').trim().slice(0,30)});})()`)));
|
||||
if (!tgt.name) break;
|
||||
const v = values[Math.min(filled.length, values.length - 1)] || 'Other';
|
||||
const ok = await c.evalJS(`(()=>{
|
||||
const items=[...document.querySelectorAll('.ant-form-item')].filter(e=>{
|
||||
if(e.offsetParent===null) return false;
|
||||
const l=e.querySelector('label');
|
||||
const reqOk = l && (/required/.test(String(l.className)) || (l.innerText||'').trim().startsWith('*') || e.querySelector('.attr-label.required'));
|
||||
if(!reqOk) return false;
|
||||
if(e.querySelector('.ant-form-item')) return false;
|
||||
const name=(l.innerText||'').replace(/\\s+/g,' ').trim();
|
||||
if(${JSON.stringify(skip)}.some(s=>name.includes(s))) return false;
|
||||
if(e.querySelector('.ant-select,.ant-checkbox-group,.ant-radio-group,.ant-cascader')) return false;
|
||||
const inp=e.querySelector('input.ant-input, textarea.ant-input');
|
||||
return inp && inp.offsetParent!==null && !String(inp.value||'').trim();});
|
||||
const it=items.find(e=>((e.querySelector('label')||{}).innerText||'').replace(/\\s+/g,' ').trim().slice(0,30)===${JSON.stringify(tgt.name)});
|
||||
if(!it) return 'gone';
|
||||
const inp=it.querySelector('input.ant-input, textarea.ant-input');
|
||||
const proto = inp.tagName==='TEXTAREA' ? window.HTMLTextAreaElement.prototype : window.HTMLInputElement.prototype;
|
||||
Object.getOwnPropertyDescriptor(proto,'value').set.call(inp, ${JSON.stringify(v)});
|
||||
inp.dispatchEvent(new Event('input',{bubbles:true})); inp.dispatchEvent(new Event('change',{bubbles:true}));
|
||||
inp.dispatchEvent(new Event('blur',{bubbles:true}));
|
||||
return 'ok';})()`);
|
||||
filled.push(tgt.name + '=' + v);
|
||||
await wait(1200);
|
||||
if (ok === 'gone') break;
|
||||
}
|
||||
return filled.join(' | ') || 'none';
|
||||
}
|
||||
Reference in new issue
Block a user