// ==UserScript==
// @name 浩毅工具箱(组合+绑定SKU)左侧固定版
// @namespace http://tampermonkey.net/
// @version 3.2
// @description 左侧垂直排列:双合成编号组合 + 批量绑定SKU | 全局统一样式 | 默认隐藏
// @author helper
// @match https://www2.haoyipod.com/workbenchcustommade
// @match https://www2.haoyipod.com/workbenchgroupcustommade
// @grant none
// @run-at document-end
// ==/UserScript==
(function() {
'use strict';
// ====================== 全局统一配置 ======================
const TOKEN = '6eba73fc-77da-4d26-8887-a791acc3b2b4';
const WIDTH = '280px';
const PADDING = '16px';
const RADIUS = '12px';
const SHADOW = '0 6px 20px rgba(0, 0, 0, 0.08)';
const BORDER = '1px solid #e8e8e8';
const BTN_BG = '#3b82f6';
const BTN_SUCCESS = '#10b981';
let checkInterval = null;
let isVisible = false; // 默认隐藏
// ====================== 主容器(统一包裹两个功能) ======================
function createToolPanel() {
const root = document.createElement('div');
root.style.cssText = `
position: fixed;
left: 20px;
top: 50%;
transform: translateY(-50%);
z-index: 99999;
width: ${WIDTH};
display: flex;
flex-direction: column;
gap: 12px;
`;
// 功能1:双合成编号组合
const panel1 = createCombinePanel();
// 功能2:批量绑定SKU
const panel2 = createBindSkuPanel();
// 开关按钮
const toggleBtn = createToggleBtn();
root.append(panel1, panel2, toggleBtn);
document.body.appendChild(root);
return root;
}
// ====================== 开关按钮:显示/隐藏全部 ======================
function createToggleBtn() {
const btn = document.createElement('button');
btn.innerText = '浩毅工具';
btn.style.cssText = `
padding: 10px;
background: ${BTN_BG};
color: #fff;
border: none;
border-radius: ${RADIUS};
font-size: 14px;
cursor: pointer;
box-shadow: ${SHADOW};
`;
btn.onclick = () => {
isVisible = !isVisible;
const panels = document.querySelectorAll('.tool-panel');
panels.forEach(p => p.style.display = isVisible ? 'block' : 'none');
};
return btn;
}
// ====================== 功能1:双合成编号组合 ======================
function createCombinePanel() {
const box = document.createElement('div');
box.className = 'tool-panel';
box.style.cssText = `
background: #fff;
border: ${BORDER};
border-radius: ${RADIUS};
padding: ${PADDING};
box-shadow: ${SHADOW};
display: none;
`;
box.innerHTML = `
双合成编号组合
`;
// 注入通用样式
addGlobalStyle();
setTimeout(() => {
const startBtn = document.getElementById('startCombine');
const taskId1 = document.getElementById('taskId1');
const taskId2 = document.getElementById('taskId2');
const logBox = document.getElementById('combineLog');
const headers = { token: TOKEN, 'Content-Type': 'application/json' };
async function getSizeMap(taskId) {
const listUrl = `https://crossdiy2.haoyipod.com/api/finished/getlist?task_id=${taskId}&pageNumber=1&pageSize=10&is_online=0`;
const res = await fetch(listUrl, { headers });
const data = await res.json();
const fid = data.data?.list?.[0]?.id;
if (!fid) throw new Error('未获取到成品ID');
const sizeRes = await fetch(`https://crossdiy2.haoyipod.com/api/finished/addToCartsPage?id=${fid}`, { headers });
const sizeData = await sizeRes.json();
const map = {};
sizeData.data?.forEach(i => map[i.size] = i.id);
return map;
}
async function createCombine(f1, f2, size) {
return fetch('https://crossdiy2.haoyipod.com/api/finished_combination/add', {
method: 'POST', headers,
body: JSON.stringify({ finished_ids: [f1, f2], finished_nums: [1,1], combine_name: size })
});
}
startBtn.onclick = async () => {
const t1 = taskId1.value.trim();
const t2 = taskId2.value.trim();
if (!t1 || !t2) return alert('请输入两个编号');
startBtn.disabled = true;
startBtn.innerText = '处理中...';
logBox.style.display = 'none';
let log = '';
try {
const map1 = await getSizeMap(t1);
const map2 = await getSizeMap(t2);
const sizes = Object.keys(map1).filter(s => map2[s]);
if (!sizes.length) throw new Error('无共同尺码');
log += `找到 ${sizes.length} 个共同尺码\n`;
let ok = 0;
for (let s of sizes) {
log += `[${s}] `;
await createCombine(map1[s], map2[s], s);
ok++;
log += '✅ 成功\n';
await new Promise(r => setTimeout(r, 300));
}
log += `\n完成:${ok}/${sizes.length}`;
} catch (e) {
log += '❌ 错误:' + e.message;
alert(e.message);
}
logBox.innerText = log;
logBox.style.display = 'block';
startBtn.disabled = false;
startBtn.innerText = '开始组合生成';
};
}, 100);
return box;
}
// ====================== 功能2:批量绑定SKU(新版:先获取→预览→反向→统一提交) ======================
function createBindSkuPanel() {
const box = document.createElement('div');
box.className = 'tool-panel';
box.style.cssText = `
background: #fff;
border: ${BORDER};
border-radius: ${RADIUS};
padding: ${PADDING};
box-shadow: ${SHADOW};
display: none;
`;
box.innerHTML = `
批量绑定第三方SKU
`;
setTimeout(() => {
const skuInput = document.getElementById('skuList');
const loadMatchBtn = document.getElementById('loadMatch');
const reverseMatchBtn = document.getElementById('reverseMatch');
const startBindBtn = document.getElementById('startBind');
const previewBox = document.getElementById('matchPreview');
// 全局存储:尺码列表 + SKU列表
let sizeList = []; // [{ id, name }]
let skuList = [];
let isReversed = false;
// 自动加复选框
function addCheckboxes() {
const wrapper = document.querySelector(".el-table__body-wrapper");
if (!wrapper) return;
const tbody = wrapper.querySelector("tbody");
if (!tbody) return;
tbody.querySelectorAll("tr").forEach(tr => {
if (tr.querySelector('input[type="checkbox"]')) return;
const td = tr.querySelector(".el-table_1_column_6 .cell");
if (!td) return;
const btn = td.querySelector("button");
if (!btn) return;
const ck = document.createElement("input");
ck.type = "checkbox";
ck.style.marginRight = "8px";
ck.style.verticalAlign = "middle";
td.insertBefore(ck, btn);
});
}
checkInterval = setInterval(addCheckboxes, 1000);
// 1)加载:先获取所有勾选的尺码ID+名称
loadMatchBtn.onclick = async () => {
skuList = skuInput.value.trim().split(/\s+/).filter(Boolean);
if (skuList.length === 0) return alert('请输入SKU');
// 获取勾选行
const tbody = document.querySelector(".el-table__body-wrapper tbody");
const rows = Array.from(tbody.querySelectorAll("input[type='checkbox']:checked")).map(c => c.closest("tr"));
if (rows.length === 0) return alert('请勾选要绑定的尺码');
sizeList = [];
for (const tr of rows) {
const no = tr.querySelector("td:first-child")?.textContent?.trim();
if (!no) continue;
try {
const res = await fetch(`https://crossdiy2.haoyipod.com/api/finished_combination/index?combine_no=${no}`, {
headers: { token: TOKEN }
});
const data = await res.json();
const item = data.data?.list?.[0];
if (item) sizeList.push({ id: item.id, name: item.combine_name });
} catch (e) {}
}
if (sizeList.length !== skuList.length) {
return alert(`勾选尺码数(${sizeList.length}) ≠ SKU数(${skuList.length})`);
}
isReversed = false;
renderPreview();
reverseMatchBtn.style.display = 'inline-block';
startBindBtn.style.display = 'block';
};
// 2)渲染预览
function renderPreview() {
const sList = isReversed ? [...sizeList].reverse() : sizeList;
let html = '📋 配对预览(尺码 ↔ SKU)\n';
html += '-----------------------------------\n';
sList.forEach((s, i) => {
html += `${s.name} ↔ ${skuList[i]}\n`;
});
previewBox.innerText = html;
previewBox.style.display = 'block';
}
// 3)反向
reverseMatchBtn.onclick = () => {
isReversed = !isReversed;
renderPreview();
};
// 4)统一提交绑定
startBindBtn.onclick = async () => {
if (!sizeList.length || !skuList.length) return;
startBindBtn.disabled = true;
startBindBtn.innerText = '绑定中...';
const sList = isReversed ? [...sizeList].reverse() : sizeList;
const headers = { token: TOKEN, 'Content-Type': 'application/json' };
for (let i = 0; i < sList.length; i++) {
try {
await fetch("https://crossdiy2.haoyipod.com/api/finished_combination/addBindThirdSku", {
method: "POST", headers,
body: JSON.stringify({
combine_id: sList[i].id,
params: [{ id: 0, import_sku: skuList[i] }]
})
});
} catch (e) {}
}
alert('✅ 全部绑定完成!');
startBindBtn.disabled = false;
startBindBtn.innerText = '🚀 确认绑定';
};
}, 100);
return box;
}
// ====================== 全局统一样式 ======================
function addGlobalStyle() {
if (document.querySelector('#tool-global-style')) return;
const style = document.createElement('style');
style.id = 'tool-global-style';
style.textContent = `
.tool-input {
width: 100%;
box-sizing: border-box;
padding: 10px 12px;
margin-bottom: 10px;
border: 1px solid #dcdfe6;
border-radius: 8px;
font-size: 14px;
outline: none;
transition: border-color 0.2s;
}
.tool-input:focus {
border-color: ${BTN_BG};
}
.tool-textarea {
width: 100%;
box-sizing: border-box;
height: 90px;
padding: 10px 12px;
border: 1px solid #dcdfe6;
border-radius: 8px;
font-size: 14px;
resize: none;
outline: none;
margin-bottom: 12px;
}
.tool-btn {
width: 100%;
padding: 10px;
border: none;
border-radius: 8px;
color: #fff;
font-size: 14px;
cursor: pointer;
transition: opacity 0.2s;
}
.tool-btn.primary {
background: ${BTN_BG};
}
.tool-btn.success {
background: ${BTN_SUCCESS};
}
.tool-btn:hover {
opacity: 0.9;
}
.tool-log {
margin-top: 12px;
padding: 10px;
background: #f8f9fa;
border-radius: 8px;
font-size: 12px;
max-height: 260px;
overflow-y: auto;
white-space: pre-wrap;
display: none;
}
`;
document.head.appendChild(style);
}
// 启动
window.addEventListener('load', createToolPanel);
})();