// ==UserScript==
// @name Temu商品批量上架工具(菲律宾男装)自动发布
// @namespace http://tampermonkey.net/
// @version 2.1
// @description 批量生成Temu商品草稿、自动上传图片、自动保存草稿、库存200正常生效
// @author 定制工具
// @match https://agentseller.temu.com/*
// @require https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js
// @require https://cdn.jsdelivr.net/npm/axios@1.6.0/dist/axios.min.js
// @grant none
// @run-at document-end
// ==/UserScript==
(function () {
'use strict';
// ==============================================
// 1. 自动获取当前页面的 cookie、anti-content、mallId
// ====================== 【新增】中文标题 → 自动翻译英文标题 ======================
async function translateZhToEn(zhText) {
try {
const { data } = await axios.post(
"https://agentseller.temu.com/anniston-agent-seller/translation/batchQuery",
{
"translateScene": 4,
"textDesc": "商品标题",
"translateReqs": [
{
"key": "21",
"baseLang": "zh",
"baseText": zhText
}
],
"targetLangList": [
"en"
]
},
{
headers: {
"Content-Type": "application/json",
"anti-content": currentAnti,
"mallid": currentMallId,
},
withCredentials: true,
}
);
if (data.success && data.result.translateRespMap["21"]) {
const enResult = data.result.translateRespMap["21"].find(t => t.lang === "en");
return enResult ? enResult.translation : "";
}
} catch (err) {
console.error("中文翻译英文失败", err);
}
return "";
}
// 调用Temu官方接口裁剪 1:1 正方形
async function temuCropImageToSquare(originalUrl) {
try {
const res = await fetch('https://agentseller.temu.com/temu-avi/image-crop', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
'anti-content': getAntiContent(),
'mallid': getMallId()
},
body: JSON.stringify({
url: originalUrl,
cutConfig: { width: 1340, height: 1340, dx: 0, dy: 222.5 },
extendConfig: { maxSize: '2m' }
})
});
const data = await res.json();
if (data.success && data.result?.url) {
return data.result.url;
}
} catch (e) {
console.error('裁剪失败', e);
}
return originalUrl;
}
// ==============================================
// 全局图片压缩函数(1340x1785)
function resizeImageFile(file, targetWidth, targetHeight) {
return new Promise((resolve) => {
const img = new Image();
img.src = URL.createObjectURL(file);
img.onload = function () {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
const scale = Math.max(targetWidth / img.width, targetHeight / img.height);
const w = img.width * scale;
const h = img.height * scale;
const x = (targetWidth - w) / 2;
const y = (targetHeight - h) / 2;
canvas.width = targetWidth;
canvas.height = targetHeight;
ctx.drawImage(img, x, y, w, h);
canvas.toBlob((blob) => {
const newFile = new File([blob], file.name, { type: "image/jpeg", quality: 0.85 });
resolve(newFile);
}, "image/jpeg", 0.85);
};
});
}
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
}
function getAntiContent() {
return `0aqAfxnYgiSY89d2J2goDWND5PwGbLPP2y1UqwKU51ttJhQyIMeD_jr_nAU8zin-1anOjLM6cEWa0ipLKxpcwj-P9VcXGxPbHBtPHC-sTmsTAUcScZVP45RRzybN45hNP68AQ4RDxundpczuyR9p7wJCkkAyj89hvjFLq_y2FACTFx6py9FFcsfquTHp7jgHNoyEVvz-nhgrJ4Xz_7RV67-tWg8FfU_XvipE_IDW-B1jJ3CpORC1vio6VzP8j9BGwgJoafkf7L9evTUhSz61gwN9dvdPpL4FLVt7Ps7kB1lKcr9BZSTsR9yzZGlowcY_Kxs0vTUqU9eFECsAPmjZvWVQkS_GYXspujU1dHUkEsbUpVJZLElDgdJHYVpLMKoIaSAuVormbq5SCUi3zwYoi0pDUm8HRLEe7PfxiPxMt3V3rZuOIwHqBFlwRnlVRk7egzT2vgwKgQaOB7LJay8bEa0pAi7zahqArLZi51Qv3wU7cziQFFzAPiLvs9dVwud6xJ3vtUM6bCYySe1SanbEKptzdsIgVOBFQlLAuEvaZMnBHRo87jHoGh32KEYet-2qmF_QwcoDdXQLYlujlBNScPzeuDvjub7r0I4B5ZKHjyLGBMvHY803g4ugxpsqzNvBbIBSjMNuxvJc4YNkP74l29rmuPpgCAOKPMv8SXOEoXiSVgkab-2qcLHZWH0T5foqVzaqw605CaswFOdoRbD_pzuYStbhqdHKkp-74DKA4eiXugtpIhRbr6kyImziqzecz_hfjvMlxxWYtOLzIy5lf0oRRJt0R53ab7cb2Iy2jolL1ZWGOGBJ`
}
function getMallId() {
return "634418227088667"
}
const currentCookie = document.cookie;
const currentAnti = getAntiContent();
const currentMallId = getMallId();
// ==============================================
// 官方Temu图片上传函数 + 自动压缩成 1340x1785
// ==============================================
async function temuUploadImage(imageFile) {
const TARGET_WIDTH = 1340;
const TARGET_HEIGHT = 1785;
function resizeImage(file, maxWidth, maxHeight) {
return new Promise((resolve) => {
const img = new Image();
img.src = URL.createObjectURL(file);
img.onload = function () {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
const scale = Math.max(maxWidth / img.width, maxHeight / img.height);
const w = img.width * scale;
const h = img.height * scale;
const x = (maxWidth - w) / 2;
const y = (maxHeight - h) / 2;
canvas.width = maxWidth;
canvas.height = maxHeight;
ctx.drawImage(img, x, y, w, h);
canvas.toBlob(
(blob) => {
const newFile = new File([blob], file.name, {
type: "image/jpeg",
quality: 0.85,
});
resolve(newFile);
},
"image/jpeg",
0.85
);
};
});
}
const compressedFile = await resizeImage(imageFile, TARGET_WIDTH, TARGET_HEIGHT);
const signRes = await fetch('https://agentseller.temu.com/general_auth/get_signature?sdk_version=js-0.0.40&tag_name=product-material-tag&scene_id=agent-seller', {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ bucket_tag: 'product-material-tag' })
});
const signData = await signRes.json();
const signature = signData.signature;
const formData = new FormData();
formData.append('url_width_height', 'true');
formData.append('upload_sign', signature);
formData.append('image', compressedFile);
const upRes = await fetch('https://agentseller.temu.com/api/galerie/v3/store_image', {
method: 'POST',
credentials: 'include',
body: formData
});
const res = await upRes.json();
return res.image_url || res.url || res.data?.image_url;
}
// ==============================================
// 2. 创建浮动按钮
// ==============================================
const floatBtn = document.createElement('div');
floatBtn.innerHTML = '打开上架工具';
floatBtn.style.cssText = `
position: fixed; bottom: 50px; right: 110px; z-index: 9999999;
padding: 12px 20px; background: #409eff; color: #fff; border-radius: 50px;
cursor: pointer; box-shadow: 0 4px 12px rgba(0,0,0,0.2); font-size: 14px;
`;
document.body.appendChild(floatBtn);
// ==============================================
// 3. 样式
// ==============================================
const style = document.createElement('style');
style.textContent = `
html,body { width: 100%; height: 100%; margin: 0; padding: 0; }
#temu-tool-app {
position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;
background: rgba(0,0,0,0.5); z-index: 9999999; display: none;
}
#app { width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; }
.content { width: 95%; height: 98%; background: #fff; padding: 40px 20px 20px; box-sizing: border-box; display: flex; flex-direction: column; gap: 10px; position: relative; }
.content .close-panel-btn {
position: absolute; right: 20px; top: 10px; width: 36px; height: 36px;
background: #ff4d4f; color: #fff; border: none; border-radius: 50%;
font-size: 18px; cursor: pointer; display: flex; align-items: center; justify-content: center;
}
.content .close-panel-btn:hover { background: #e64547; }
.upload-progress-container { width: 100%; height: 8px; background: #e5e7eb; border-radius: 4px; margin: 8px 0; overflow: hidden; }
.upload-progress-bar { height: 100%; background: #409eff; border-radius: 4px; transition: width 0.3s ease; }
.defalut { display: flex; gap: 25px; align-items: center; flex-wrap: wrap; }
.setClothing { display: flex; align-items: center; gap: 10px; background: #f7f8fa; padding: 10px 14px; border-radius: 10px; box-shadow: 0 2px 6px #0001; }
.selectBox { border: 1px solid #e1e5e9; border-radius: 6px; padding: 6px 10px; outline: none; background: #fff; cursor: pointer; }
.selectBox:focus { border-color: #409eff; box-shadow: 0 0 0 2px #409eff1a; }
.tools { display: flex; gap: 30px; align-items: center; flex-wrap: wrap; }
.tool { display: flex; align-items: center; gap: 12px; background: #f7f8fa; padding: 10px 16px; border-radius: 10px; box-shadow: 0 2px 6px #0001; }
.tool input[type="text"],.tool input[type="number"] { border: 1px solid #e1e5e9; border-radius: 6px; padding: 6px 10px; outline: none; width: 100px; }
.tool input:focus { border-color: #409eff; box-shadow: 0 0 0 2px #409eff1a; }
.tool button { padding: 6px 14px; background: #409eff; color: #fff; border: none; border-radius: 6px; cursor: pointer; }
.tool button:hover { background: #338eef; }
table { width: 100%; border-collapse: collapse; background: #fff; border-radius: 10px; overflow: hidden; box-shadow: 0 2px 10px #0001; table-layout: fixed; }
td { height: 90px; }
thead td { height: 30px; }
thead td:nth-child(1) { width: 120px; }
thead td:nth-child(2) { width: 220px; }
thead td:nth-child(3) { width: 240px; }
thead td:nth-child(4) { width: 240px; }
thead td:nth-child(5) { width: 280px; }
thead td:nth-child(6) { width: 420px; }
thead td:nth-child(7) { width: 100px; }
thead { color: white; background: #5a709d; height: 40px; }
thead td { padding: 14px 10px; font-weight: 600; text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
tbody tr { border-bottom: 1px solid #f2f2f2; }
tbody tr:hover { background: #fafbfc; }
table td { padding: 12px 8px; vertical-align: top; text-align: center; overflow: hidden; }
table input[type="text"] { width: 100% !important; box-sizing: border-box; padding: 6px 10px; border: 1px solid #b8bcc0; border-radius: 6px; outline: none; height: 40px; }
table input:focus { border-color: #409eff; box-shadow: 0 0 0 2px #409eff1a; }
.color-checkbox-group { display: grid; grid-template-columns: repeat(4,1fr); gap: 6px 4px; text-align: left; width: 100%; box-sizing: border-box; }
.color-checkbox-group label { display: flex; align-items: center; gap: 4px; padding: 4px 6px; border-radius: 4px; cursor: pointer; white-space: nowrap; overflow: hidden; }
.color-checkbox-group label:has(input:checked[value="黑色"]) { background: #383838; color: #fff; }
.color-checkbox-group label:has(input:checked[value="白色"]) { background: #fafafa; }
.RowImageList { display: flex; flex-direction: column; gap: 10px; max-height: 300px; overflow-y: auto; width: 100%; box-sizing: border-box; }
.ImageByColor { background: #fafbfc; border-radius: 8px; padding: 10px; border: 1px solid #c6c6c0; width: 100%; box-sizing: border-box; }
.ImageByColor>div:first-child { font-size: 13px; font-weight: 500; margin-bottom: 8px; display: flex; justify-content: space-between; }
.ImageByColor>div:first-child div:last-child { padding: 3px 8px; border: 1px solid #409eff; border-radius: 4px; color: #409eff; cursor: pointer; }
.ImageByColor>div:first-child div:last-child:hover { background: #409eff; color: #fff; }
.colorImages { display: flex; justify-content: flex-start; gap: 6px; flex-wrap: wrap; width: 100%; }
.imgs { width: 62px; height: 62px; border: 1px solid #e1e5e9; border-radius: 6px; background: #fff; display: flex; align-items: center; justify-content: center; cursor: move; position: relative; flex-shrink: 0; }
.imgs img { width: 100%; height: 100%; object-fit: cover; }
.del-img { position: absolute; top: -4px; right: -4px; width: 18px; height: 18px; background: #ff4d4f; color: #fff; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 12px; cursor: pointer; line-height: 1; }
.del-img:hover { background: #ff3333; }
.delete-btn { cursor: pointer; padding: 5px 10px; border-radius: 5px; background-color: #ff4d4f; color: white; }
.delete-btn:hover { background-color: rgb(130,7,7); }
.batch-color-tool { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.batch-color-label { padding: 2px 6px; cursor: pointer; display: flex; align-items: center; gap: 4px; }
.batch-color-tool { display: flex; align-items: center; gap: 10px; flex-wrap: nowrap; overflow-x: auto; padding: 8px 12px; }
.batch-color-label { display: flex; align-items: center; gap: 4px; padding: 4px 8px; border-radius: 4px; cursor: pointer; white-space: nowrap; transition: all 0.2s; }
.batch-color-label input { cursor: pointer; }
.batch-color-label.checked { background: #383838; color: #fff; }
.batch-color-label.checked:has([value="白色"]) { background: #fafafa; color: #000; }
.style-manager-btn { margin-left: 10px; padding: 6px 12px; background: #677ab7; color: #fff; border: none; border-radius: 6px; cursor: pointer; }
.style-manager-btn:hover { background: #5a6ca8; }
.style-manager-modal { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 9999; }
.modal-content { background: #fff; width: 90%; height: 90vh; border-radius: 12px; display: flex; flex-direction: column; overflow: hidden; }
.modal-header { padding: 16px 20px; background: #5a709d; color: #fff; display: flex; justify-content: space-between; align-items: center; }
.modal-header h2 { margin: 0; font-size: 18px; }
.close-btn { background: none; border: none; color: #fff; font-size: 24px; cursor: pointer; }
.modal-body { padding: 20px; overflow-y: auto; flex: 1; }
.modal-footer { padding: 16px 20px; background: #f7f8fa; display: flex; justify-content: flex-end; gap: 12px; }
.form-section { margin-bottom: 24px; padding-bottom: 16px; border-bottom: 1px solid #eee; }
.form-section h3 { margin: 0 0 12px; font-size: 16px; color: #333; }
.form-row { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; flex-wrap: wrap; }
.form-row label { width: 90px; font-weight: 500; }
.form-row input { padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; width: 120px; }
.add-btn { padding: 6px 12px; background: #409eff; color: #fff; border: none; border-radius: 4px; cursor: pointer; margin-bottom: 8px; }
.del-btn { padding: 4px 8px; background: #ff4d4f; color: #fff; border: none; border-radius: 4px; cursor: pointer; }
.inner-table { width: 100%; border-collapse: collapse; margin-top: 8px; }
.inner-table th,.inner-table td { border: 1px solid #ddd; padding: 8px; text-align: center; }
.inner-table th { background: #f7f8fa; }
.inner-table input { width: 100%; box-sizing: border-box; padding: 4px; border: 1px solid #ddd; border-radius: 4px; }
.key-value-list { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px; }
.kv-item { display: flex; align-items: center; gap: 6px; padding: 6px; border: 1px solid #eee; border-radius: 4px; }
.kv-item input { width: 120px; padding: 4px; border: 1px solid #ddd; border-radius: 4px; }
.del-col { color: #ff4d4f; cursor: pointer; margin-left: 4px; }
.save-btn { padding: 8px 16px; background: #409eff; color: #fff; border: none; border-radius: 6px; cursor: pointer; }
.cancel-btn { padding: 8px 16px; background: #999; color: #fff; border: none; border-radius: 6px; cursor: pointer; }
.custom-alert { position: fixed; top: 30px; left: 50%; transform: translateX(-50%); z-index: 999999; padding: 12px 28px; background: #fff; color: #f56c6c; border-radius: 8px; font-size: 15px; font-weight: 500; box-shadow: 0 4px 15px rgba(0,0,0,0.15); border-left: 4px solid #f56c6c; animation: alertPopup 0.3s ease forwards; }
@keyframes alertPopup { 0% { opacity: 0; top: 0; } 100% { opacity: 1; top: 30px; } }
`;
document.head.appendChild(style);
// ==============================================
// 4. Vue 容器
// ==============================================
const appContainer = document.createElement('div');
appContainer.id = 'temu-tool-app';
appContainer.innerHTML = `
`;
document.body.appendChild(appContainer);
// ==============================================
// 5. Vue 实例(已完整修复)
// ==============================================
const vm = new Vue({
el: '#app',
data() {
return {
showAlert: false, alertText: "", alertTimer: null, computerId: "",
batchSelectedColors: [], selectClthingData: "", currentColors: [],
currentStyleColorImg: {}, prefix: "", startNum: 1, addRowCount: 1,
showStyleManager: false, editStyleKey: "",
styleForm: { code: "", colors: [{ name: "", img8: "", img9: "", img10: "" }] },
defaultClothings: { XS001model: {} },
Clothings: {},
goods: [{ id: "", title: "", titleEn: "", titlePh: "", selectedColors: [], Color: {} }],
dragData: null, uploadProgress: 0, showProgress: false
}
},
created() {
let cid = localStorage.getItem('my_computer_id');
if (!cid) {
cid = 'PC-' + crypto.randomUUID().split('-')[0];
localStorage.setItem('my_computer_id', cid);
}
this.computerId = cid;
this.loadLocalData();
},
methods: {
// 中文标题自动翻译为英文
async translateZhToEn(zhText) {
if (!zhText) return "";
try {
const { data } = await axios.post(
"https://agentseller.temu.com/anniston-agent-seller/translation/batchQuery",
{
"translateScene": 4,
"textDesc": "商品标题",
"translateReqs": [
{
"key": "21",
"baseLang": "zh",
"baseText": zhText
}
],
"targetLangList": ["en"]
},
{
headers: {
"Content-Type": "application/json",
"anti-content": currentAnti,
"mallid": currentMallId,
},
withCredentials: true,
}
);
if (data.success && data.result?.translateRespMap?.["21"]) {
const enItem = data.result.translateRespMap["21"].find(x => x.lang === "en");
return enItem?.translation || "";
}
} catch (err) {
console.error("中文译英文失败:", err);
}
return "";
},
async translateTitleBatch(enText) {
try {
const { data } = await axios.post(
"https://agentseller.temu.com/anniston-agent-seller/translation/batchQuery",
{
translateScene: 4,
textDesc: "商品标题",
translateReqs: [
{
key: "22",
baseLang: "en",
baseText: enText,
},
],
targetLangList: [
"fil"
],
},
{
headers: {
"Content-Type": "application/json",
"anti-content": currentAnti,
"mallid": currentMallId,
},
withCredentials: true,
}
);
if (data.success && data.result.translateRespMap["22"]) {
const map = {};
data.result.translateRespMap["22"].forEach((item) => {
map[item.lang] = item.translation;
});
return map;
}
return {};
} catch (err) {
console.error("翻译失败", err);
return {};
}
},
async preCheckProductStock(row) {
const warehouseId = "WH-01805626921113355";
const siteIdList = [127];
const sizeArr = ["S", "M", "L", "XL", "XXL"];
let skuStockList = [];
row.selectedColors.forEach(color => {
sizeArr.forEach(size => {
skuStockList.push({
productSkuSpecName: `${color}-${size}`,
productSkcSpecName: color,
productSkuId: null,
stockType: 2,
skuStockChangeList: [{
targetStockAvailable: 200,
warehouseId: warehouseId, productSkuId: null
}],
productSkuShippingMode: 1,
latestShippingModeEditStatus: 1
});
});
});
const reqBody = {
targetRouteList: [{ warehouseId, siteIdList }],
goodsBindSiteList: siteIdList,
skuStockList: skuStockList, operateSource: 1
};
try {
const { data } = await axios.post(
'https://agentseller.temu.com/darwin-mms/api/kiana/ghost/btg/sales/stock/preCheckProductStock',
reqBody, {
headers: {
"accept": "*/*", "accept-language": "zh-CN,zh;q=0.9,en;q=0.8",
"anti-content": currentAnti, "cache-control": "no-cache",
"content-type": "application/json", "mallid": currentMallId, "pragma": "no-cache"
},
withCredentials: true
}
);
console.log("✅ 库存预校验完成", data);
return true;
} catch (e) {
console.error("❌ 库存预校验失败", e);
return false;
}
},
loadLocalData() {
const localClothings = localStorage.getItem('app_clothings');
if (localClothings) {
const parsed = JSON.parse(localClothings);
this.Clothings = (parsed && Object.keys(parsed).length > 0) ? parsed : JSON.parse(JSON.stringify(this.defaultClothings));
} else {
this.Clothings = JSON.parse(JSON.stringify(this.defaultClothings));
}
},
saveClothingsToLocal() {
localStorage.setItem('app_clothings', JSON.stringify(this.Clothings));
},
openStyleManager() {
this.showStyleManager = true;
this.editStyleKey = "";
this.resetStyleForm();
this.loadLocalData();
},
closeStyleManager() { this.showStyleManager = false; },
resetStyleForm() { this.styleForm = { code: "", colors: [] }; },
addColorRow() { this.styleForm.colors.push({ name: "", img8: "", img9: "", img10: "" }); },
delColorRow(idx) { this.styleForm.colors.splice(idx, 1); },
saveStyle() {
const { code, colors } = this.styleForm;
if (!code) { alert("请输入款式编码!"); return; }
const colorObj = {};
colors.forEach(c => { if (c.name) colorObj[c.name] = { img8: c.img8, img9: c.img9, img10: c.img10 }; });
this.$set(this.Clothings, code, { color: colorObj });
this.saveClothingsToLocal();
this.closeStyleManager();
alert("保存款式成功!");
},
deleteStyle() {
const key = this.editStyleKey;
if (!key) { alert("请选择要删除的款式!"); return; }
if (!confirm(`确定要删除款式【${key}】吗?`)) return;
this.$delete(this.Clothings, key);
this.saveClothingsToLocal();
this.resetStyleForm();
},
clearAllRows() {
this.goods = [{ id: "", title: "", titleEn: "", titlePh: "", selectedColors: [], Color: {} }];
},
getFullColorImgList(userImgList, color) {
const detailUrls = [];
const colorCfg = this.currentStyleColorImg[color] || {};
if (colorCfg.img8) detailUrls.push(colorCfg.img8);
if (colorCfg.img9) detailUrls.push(colorCfg.img9);
if (colorCfg.img10) detailUrls.push(colorCfg.img10);
return [...userImgList, ...detailUrls];
},
hidePanel() {
appContainer.style.display = 'none';
floatBtn.textContent = '打开上架工具';
show = false;
},
async upload() {
this.showCustomAlert("⏳ 正在上传所有图片,请稍候...");
console.log("开始统一上传所有图片");
let totalFiles = 0; let uploaded = 0;
this.uploadProgress = 0; this.showProgress = true;
for (let item of this.goods) {
for (let color in item.Color) {
const fileList = item.Color[color] || [];
fileList.forEach(f => { if (f.file) totalFiles++; });
}
}
if (totalFiles === 0) { this.showProgress = false; }
for (let item of this.goods) {
item.squareImageUrl = "";
for (let color in item.Color) {
const fileList = item.Color[color] || [];
const urlList = [];
let colorFirstSquareUrl = "";
for (let [index, f] of fileList.entries()) {
if (f.file) {
try {
let uploadFile = f.file;
if (index === 0) {
const originalUploadedUrl = await temuUploadImage(uploadFile);
const squareImageUrl = await temuCropImageToSquare(originalUploadedUrl);
urlList.push(originalUploadedUrl);
uploaded++;
this.uploadProgress = Math.round((uploaded / totalFiles) * 100);
colorFirstSquareUrl = squareImageUrl;
continue;
}
const url = await temuUploadImage(uploadFile);
urlList.push(url);
uploaded++;
this.uploadProgress = Math.round((uploaded / totalFiles) * 100);
} catch (err) {
console.error("上传失败", err);
}
} else if (f.url) {
urlList.push(f.url);
if (index === 0 && !colorFirstSquareUrl) {
const squareImageUrl = await temuCropImageToSquare(f.url);
colorFirstSquareUrl = squareImageUrl;
}
}
}
if (colorFirstSquareUrl) {
item.squareImageUrl = colorFirstSquareUrl;
}
item.Color[color] = urlList;
}
}
this.showCustomAlert("✅ 图片上传完成,开始自动翻译中文→英文");
setTimeout(() => { this.showProgress = false; }, 800);
for (let item of this.goods) {
if (item.title?.trim() && !item.titleEn?.trim()) {
item.titleEn = await this.translateZhToEn(item.title.trim());
await new Promise(r => setTimeout(r, 200));
}
}
this.showCustomAlert("✅ 英文标题翻译完成,开始生成商品草稿");
const exportData = this.goods.map(item => {
const newColorData = {};
for (const color in item.Color) {
newColorData[color] = this.getFullColorImgList(item.Color[color] || [], color);
}
return {
id: item.id,
title: item.title,
titleEn: item.titleEn,
titlePh: item.titlePh,
selectedColors: item.selectedColors,
Color: newColorData,
squareImageUrl: item.squareImageUrl
};
});
console.log("【完整商品数据】", exportData);
const templateData = [];
for (const rows of exportData) {
// 自动翻译:英文 → 菲律宾语
if (rows.titleEn && !rows.titlePh) {
const transMap = await this.translateTitleBatch(rows.titleEn);
rows.titlePh = transMap.fil || "";
await new Promise(r => setTimeout(r, 200));
}
const rowData = await this.buildProductPayload(rows);
const spuid = await this.createTemuProductDraft();
rowData.productDraftId = spuid;
console.log("草稿ID:", spuid);
if (spuid) {
console.log("创建草稿成功:", spuid);
await this.preCheckProductStock(rows);
await this.saveTemuProductDraft(rowData);
await this.publishTemuProductDirect(rowData);
}
templateData.push(rowData);
}
console.log("【最终提交模板】", templateData);
this.showCustomAlert("🎉 全部处理完成!");
},
onColorChange(item, color) {
if (!item.Color[color]) this.$set(item.Color, color, []);
},
selectClothing(code) {
const data = this.Clothings[code];
if (!data) return;
this.currentStyleColorImg = data.color || {};
this.currentColors = Object.keys(data.color || {});
this.goods.forEach(item => { item.selectedColors = []; item.Color = {}; });
},
uploadImgs(item, color) {
const input = document.createElement("input");
input.type = "file"; input.multiple = true; input.accept = "image/*";
input.onchange = async () => {
const files = Array.from(input.files);
if (!item.Color[color]) this.$set(item.Color, color, []);
for (let file of files) {
const compressedFile = await resizeImageFile(file, 1340, 1785);
const preview = URL.createObjectURL(compressedFile);
item.Color[color].push({ file: compressedFile, preview: preview });
}
this.$forceUpdate();
};
input.click();
},
delSingleImg(item, color, imgIdx) {
item.Color[color].splice(imgIdx, 1);
this.$forceUpdate();
},
dragStart(e, idx, color, imgIdx) {
this.dragData = { idx, color, imgIdx };
e.dataTransfer.effectAllowed = "move";
},
drop(e, idx, color, targetIdx) {
if (!this.dragData) return;
const { idx: sIdx, color: sColor, imgIdx: sImgIdx } = this.dragData;
if (idx !== sIdx || color !== sColor) return;
const arr = this.goods[idx].Color[color];
const movedItem = arr[sImgIdx];
arr.splice(sImgIdx, 1);
arr.splice(targetIdx, 0, movedItem);
this.$forceUpdate();
this.dragData = null;
},
fillIds() {
let num = parseInt(this.startNum);
this.goods.forEach(item => { item.id = this.prefix + "-" + num; num++; });
},
addRows() {
const count = parseInt(this.addRowCount) || 1;
for (let i = 0; i < count; i++) {
this.goods.push({ id: "", title: "", titleEn: "", titlePh: "", selectedColors: [], Color: {} });
}
},
delRow(idx) { this.goods.splice(idx, 1); },
batchApplyColors() {
if (this.currentColors.length === 0) { alert("请先选择款式!"); return; }
this.goods.forEach(item => {
item.selectedColors = [...this.batchSelectedColors];
item.selectedColors.forEach(color => { if (!item.Color[color]) this.$set(item.Color, color, []); });
});
},
batchCheckAllColor() {
if (this.currentColors.length === 0) { alert("请先选择款式!"); return; }
this.goods.forEach(item => {
item.selectedColors = [...this.currentColors];
this.currentColors.forEach(color => { if (!item.Color[color]) this.$set(item.Color, color, []); });
});
},
batchClearColor() {
this.goods.forEach(item => { item.selectedColors = []; item.Color = {}; });
},
pasteColumn(e, startIndex, field) {
const text = e.clipboardData.getData('text').trim();
if (!text) return;
const lines = text.split(/\r\n|\n|\r|\t/).map(s => s.trim()).filter(Boolean);
const lastNeedIndex = startIndex + lines.length - 1;
const needAddCount = lastNeedIndex - this.goods.length + 1;
if (needAddCount > 0) {
for (let i = 0; i < needAddCount; i++) {
this.goods.push({ id: "", title: "", titleEn: "", titlePh: "", selectedColors: [], Color: {} });
}
}
lines.forEach((val, i) => {
const rowIndex = startIndex + i;
if (rowIndex >= this.goods.length) return;
this.goods[rowIndex][field] = val;
});
},
loadEditStyle() {
const key = this.editStyleKey;
if (!key) { this.resetStyleForm(); return; }
const data = this.Clothings[key];
if (!data) return;
this.styleForm.code = key;
const colorArr = [];
const colorObj = data.color || {};
for (const name in colorObj) {
const img = colorObj[name];
colorArr.push({ name, img8: img.img8 || "", img9: img.img9 || "", img10: img.img10 || "" });
}
this.styleForm.colors = colorArr;
},
showCustomAlert(text) {
this.alertText = text;
this.showAlert = true;
clearTimeout(this.alertTimer);
this.alertTimer = setTimeout(() => { this.showAlert = false; }, 2800);
},
async uploadSingleImage(url) {
return url;
},
// ====================== 【核心】全新菲律宾店铺模板 ======================
async buildProductPayload(row) {
let materialImgUrl = '';
if (row.squareImageUrl) {
materialImgUrl = row.squareImageUrl;
} else if (row.selectedColors && row.selectedColors.length > 0) {
const firstColor = row.selectedColors[0];
const colorImages = row.Color[firstColor] || [];
materialImgUrl = colorImages[0] || "";
}
const originalPayload = {
"copyFromProductId": 8816007682,
"cat1Id": 27011,
"cat2Id": 30327,
"cat3Id": 30328,
"cat4Id": 30477,
"cat5Id": 30478,
"cat6Id": 0,
"cat7Id": 0,
"cat8Id": 0,
"cat9Id": 0,
"cat10Id": 0,
"materialMultiLanguages": [],
"productName": (row.title).slice(0, 250),
"productI18nReqs": [
{
"productName": (row.titleEn || "").slice(0, 250),
"language": "en"
}
],
"goodsI18nReqs": [
{
"language": "fil",
"productName": (row.titlePh || "").slice(0, 500)
}
],
"productPropertyReqs": [
{ "templatePid": 1140677, "pid": 1, "refPid": 12, "propName": "材质", "vid": 49, "propValue": "棉", "valueUnit": "", "valueExtendInfo": "", "numberInputValue": "" },
{ "templatePid": 1140678, "pid": 2, "refPid": 15, "propName": "成分", "vid": 78, "propValue": "棉", "valueUnit": "%", "valueExtendInfo": "", "numberInputValue": "100.00" },
{ "templatePid": 1140683, "pid": 3, "refPid": 19, "propName": "风格", "vid": 145, "propValue": "休闲", "valueUnit": "", "valueExtendInfo": "", "numberInputValue": "" },
{ "templatePid": 1140684, "pid": 4, "refPid": 20, "propName": "护理说明", "vid": 26001, "propValue": "可机洗且不可干洗", "valueUnit": "", "valueExtendInfo": "", "numberInputValue": "" },
{ "templatePid": 1140682, "pid": 5, "refPid": 21, "propName": "领型", "vid": 182, "propValue": "小圆领", "valueUnit": "", "valueExtendInfo": "", "numberInputValue": "" },
{ "templatePid": 1140685, "pid": 6, "refPid": 22, "propName": "面料", "vid": 207, "propValue": "微弹", "valueUnit": "", "valueExtendInfo": "", "numberInputValue": "" },
{ "templatePid": 1140688, "pid": 7, "refPid": 24, "propName": "是否透明", "vid": 210, "propValue": "否", "valueUnit": "", "valueExtendInfo": "", "numberInputValue": "" },
{ "templatePid": 1140680, "pid": 10, "refPid": 26, "propName": "图案", "vid": 230, "propValue": "卡通", "valueUnit": "", "valueExtendInfo": "", "numberInputValue": "" },
{ "templatePid": 1140689, "pid": 11, "refPid": 27, "propName": "袖型", "vid": 289, "propValue": "落肩袖", "valueUnit": "", "valueExtendInfo": "", "numberInputValue": "" },
{ "templatePid": 1140691, "pid": 12, "refPid": 28, "propName": "长度", "vid": 293, "propValue": "常规款", "valueUnit": "", "valueExtendInfo": "", "numberInputValue": "" },
{ "templatePid": 1140690, "pid": 16, "refPid": 29, "propName": "袖长", "vid": 296, "propValue": "短袖", "valueUnit": "", "valueExtendInfo": "", "numberInputValue": "" },
{ "templatePid": 1140699, "pid": 19, "refPid": 74, "propName": "腰带", "vid": 551, "propValue": "否", "valueUnit": "", "valueExtendInfo": "", "numberInputValue": "" },
{ "templatePid": 1140687, "pid": 24, "refPid": 76, "propName": "季节", "vid": 25850, "propValue": "夏天", "valueUnit": "", "valueExtendInfo": "", "numberInputValue": "" },
{ "templatePid": 1140679, "pid": 22, "refPid": 77, "propName": "次要材质", "vid": 653, "propValue": "棉", "valueUnit": "", "valueExtendInfo": "", "numberInputValue": "" },
{ "templatePid": 1140681, "pid": 21, "refPid": 83, "propName": "细节", "vid": 29210, "propValue": "无", "valueUnit": "", "valueExtendInfo": "", "numberInputValue": "" },
{ "templatePid": 1140697, "pid": 39, "refPid": 86, "propName": "门襟类型", "vid": 873, "propValue": "套头衫", "valueUnit": "", "valueExtendInfo": "", "numberInputValue": "" },
{ "templatePid": 1140694, "pid": 37, "refPid": 90, "propName": "场景", "vid": 893, "propValue": "日常和休闲", "valueUnit": "", "valueExtendInfo": "", "numberInputValue": "" },
{ "templatePid": 1140693, "pid": 26, "refPid": 96, "propName": "场合", "vid": 724, "propValue": "休闲外出", "valueUnit": "", "valueExtendInfo": "", "numberInputValue": "" },
{ "templatePid": 1140695, "pid": 20, "refPid": 113, "propName": "廓形", "vid": 1829, "propValue": "常规款", "valueUnit": "", "valueExtendInfo": "", "numberInputValue": "" },
{ "templatePid": 1140692, "pid": 36, "refPid": 114, "propName": "版型", "vid": 35189, "propValue": "常规", "valueUnit": "", "valueExtendInfo": "", "numberInputValue": "" },
{ "templatePid": 1140686, "pid": 67, "refPid": 115, "propName": "适用人群", "vid": 30062, "propValue": "男士", "valueUnit": "", "valueExtendInfo": "", "numberInputValue": "" },
{ "templatePid": 1140703, "pid": 1224, "refPid": 1192, "propName": "织造方式", "vid": 54654, "propValue": "针织(含钩织、毛织面料)", "valueUnit": "", "valueExtendInfo": "", "numberInputValue": "" },
{ "templatePid": 1196179, "pid": 1437, "refPid": 1919, "propName": "印花类型", "vid": 36893, "propValue": "定位印花", "valueUnit": "", "valueExtendInfo": "", "numberInputValue": "" },
{ "valueUnit": "", "propValue": "DISNEY", "propName": "品牌名", "refPid": 1960, "vid": 71208, "numberInputValue": "", "controlType": 1, "pid": 1467, "templatePid": 1140702, "valueExtendInfo": "" },
{ "templatePid": 1333378, "pid": 1514, "refPid": 2103, "propName": "款式来源", "vid": 70453, "propValue": "现货款", "valueUnit": "", "valueExtendInfo": "", "numberInputValue": "" },
{ "templatePid": 1225371, "pid": 1926, "refPid": 6491, "propName": "面料克重", "vid": 91475, "propValue": "180", "valueUnit": "", "valueExtendInfo": "", "numberInputValue": "" },
{ "templatePid": 1301462, "pid": 2054, "refPid": 6926, "propName": "面料纹理1", "vid": 161198, "propValue": "光面", "valueUnit": "", "valueExtendInfo": "", "numberInputValue": "" },
{ "templatePid": 1301882, "pid": 2054, "refPid": 6927, "propName": "面料纹理2", "vid": 161198, "propValue": "光面", "valueUnit": "", "valueExtendInfo": "", "numberInputValue": "" },
{ "templatePid": 1337001, "pid": 2050, "refPid": 6928, "propName": "里料纹理", "vid": 161112, "propValue": "无里料/无内衬", "valueUnit": "", "valueExtendInfo": "", "numberInputValue": "" },
{ "templatePid": 1302302, "pid": 2052, "refPid": 6930, "propName": "面料克重1(g/m²)", "vid": 0, "propValue": "180", "valueUnit": "g/㎡", "valueExtendInfo": "", "numberInputValue": "" },
{ "templatePid": 1302724, "pid": 2052, "refPid": 6931, "propName": "面料克重2(g/m²)", "vid": 0, "propValue": "180", "valueUnit": "g/㎡", "valueExtendInfo": "", "numberInputValue": "" }
],
"productSkcReqs": [],
"productSpecPropertyReqs": [],
"carouselImageUrls": [],
"carouselImageI18nReqs": [],
materialImgUrl: materialImgUrl,
"goodsLayerDecorationReqs": [],
"goodsLayerDecorationCustomizeI18nReqs": [],
"sizeTemplateIds": [17595378557742],
"showSizeTemplateIds": [17595378557742],
"goodsModelReqs": [{
"modelId": 2604220096493,
"modelName": "Malik",
"modelType": 1,
"modelFeature": 1,
"modelProfileUrl": "https://img.kwcdn.com/product/76942db695580e3e1f56feb1ed9665b4.jpg",
"modelHeight": "177.5",
"modelBust": "98",
"modelWaist": "82",
"modelHip": "98",
"modelFootLength": "",
"modelFootWidth": "",
"sizeSpecId": 9005,
"sizeSpecName": "M",
"tryOnResult": 1
}],
"productWhExtAttrReq": {
"outerGoodsUrl": "",
"productOrigin": { "countryShortName": "CN", "region2Id": 43000000000006 }
},
"productCarouseVideoReqList": [],
"goodsAdvantageLabelTypes": [],
"productDetailVideoReqList": [],
"productOuterPackageImageReqs": [],
"productOuterPackageReq": {},
"sensitiveTransNormalFileReqs": [],
"productGuideFileNewReqList": [],
"productGuideFileI18nReqs": [],
"productSaleExtAttrReq": { "bodyShape": null },
"productNonAuditExtAttrReq": {
"california65WarningInfoReq": { "california65WarningType": null, "california65ChemicalNames": null },
"cosmeticInfoReq": {}
},
"productShipmentReq": {
"freightTemplateId": "HFT-18140051757711563355",
"shipmentLimitSecond": 172800
},
"productWarehouseRouteReq": {
"targetRouteList": [{
"warehouseId": "WH-01805626921113355",
"siteIdList": [127]
}]
},
"productSemiManagedReq": {
"bindSiteIds": [127],
"semiLanguageStrategy": 2
},
"personalizationSwitch": 0,
"productOriginCertFileReqs": [],
"productDraftId": 0
};
const payload = JSON.parse(JSON.stringify(originalPayload));
// ====================== 颜色规格 ======================
const colorMap = {
白色: { specId: 2001, vid: 376, groupId: 1 },
黑色: { specId: 3002, vid: 378, groupId: 3 }
};
const sizeList = [
{ specId: 10004, name: "S", vid: 315, weight: 170000 },
{ specId: 9005, name: "M", vid: 317, weight: 180000 },
{ specId: 11002, name: "L", vid: 319, weight: 190000 },
{ specId: 12003, name: "XL", vid: 320, weight: 200000 },
{ specId: 8002, name: "XXL", vid: 321, weight: 210000 }
];
row.selectedColors.forEach(colorName => {
const cfg = colorMap[colorName];
if (!cfg) return;
payload.productSpecPropertyReqs.push({
"parentSpecId": 1001, "parentSpecName": "颜色",
"specId": cfg.specId, "specName": colorName,
"groupId": cfg.groupId, "vid": cfg.vid,
"refPid": 63, "pid": 13, "templatePid": 1140675,
"propName": "颜色", "propValue": colorName, "valueUnit": "",
"valueGroupId": cfg.groupId,
"valueGroupName": colorName === "白色" ? "白色系" : "黑色系",
"valueExtendInfo": colorName === "白色" ? "(255,255,255,1)" : "(0,0,0,1)"
});
});
sizeList.forEach(size => {
payload.productSpecPropertyReqs.push({
"parentSpecId": 3001, "parentSpecName": "尺码",
"specId": size.specId, "specName": size.name,
"groupId": 2, "vid": size.vid,
"refPid": 65, "pid": 14, "templatePid": 1140676,
"propName": "尺码", "propValue": size.name, "valueUnit": "",
"valueGroupId": 2, "valueGroupName": "中国码", "valueExtendInfo": ""
});
});
// 生成SKC
row.selectedColors.forEach(colorName => {
const cfg = colorMap[colorName];
if (!cfg) return;
const imgs = row.Color[colorName] || [];
const firstImg = imgs[0] || "";
const skuList = sizeList.map(size => {
return {
"thumbUrl": firstImg,
"productSkuThumbUrlI18nReqs": [],
"extCode": "",
"siteSupplierPrices": [
{ "siteId": 127, "supplierPrice": 4200 }
],
"currencyType": "CNY",
"productSkuSpecReqs": [
{ "parentSpecId": 1001, "parentSpecName": "颜色", "specId": cfg.specId, "specName": colorName, "groupId": cfg.groupId, "vid": cfg.vid },
{ "parentSpecId": 3001, "parentSpecName": "尺码", "specId": size.specId, "specName": size.name, "groupId": 2, "vid": size.vid }
],
"productSkuId": 0,
"productSkuSuggestedPriceReq": {
"suggestedPrice": 8000,
"suggestedPriceCurrencyType": "CNY"
},
"productSkuWhExtAttrReq": {
"productSkuVolumeReq": { "len": 300, "width": 250, "height": 10 },
"productSkuWeightReq": { "value": size.weight },
"productSkuBarCodeReqs": [],
"productSkuSensitiveAttrReq": { "isSensitive": 0, "sensitiveList": [] },
"productSkuSensitiveLimitReq": {}
},
"productSkuMultiPackReq": {
"skuClassification": 1, "numberOfPieces": 1, "pieceUnitCode": 1,
"productSkuNetContentReq": {}, "totalNetContent": {},
"numberOfPiecesNew": 1, "pieceNewUnitCode": 1
},
"productSkuStockQuantityReq": {
"warehouseStockQuantityReqs": [{
"warehouseId": "WH-01805626921113355",
"targetStockAvailable": 200
}]
},
"productSkuAccessoriesReq": { "productSkuAccessories": [] },
"productSkuNonAuditExtAttrReq": {}
};
});
const skc = {
"previewImgUrls": imgs,
"productSkcCarouselImageI18nReqs": [],
"extCode": row.id,
"mainProductSkuSpecReqs": [{
"parentSpecId": 1001, "parentSpecName": "颜色",
"specId": cfg.specId, "specName": colorName,
"groupId": cfg.groupId, "vid": cfg.vid
}],
"productSkuReqs": skuList,
"productSkcId": 0,
"colorImageUrl": colorName === "黑色"
? "https://img.kwcdn.com/product/fancy/cb516eae-2176-4fa4-a1e0-f548b81f848f.jpg"
: colorName === "白色"
? "https://img.kwcdn.com/product/fancy/38db2fa3-997c-40db-8b6c-58f398730a71.jpg"
: firstImg,
"isBasePlate": 0
};
payload.productSkcReqs.push(skc);
});
// ====================== 最终版:主图全部在前 + 细节图按颜色交替 ======================
let mainImages = []; // 主图:颜色A主图 → 颜色B主图
let colorDetails = []; // 每个颜色的细节图数组 [[A细节1,A细节2...], [B细节1,B细节2...]]
// 1. 先收集:所有颜色主图 + 所有颜色细节图
row.selectedColors.forEach(color => {
const imgs = row.Color[color] || [];
if (imgs.length >= 3) {
mainImages.push(...imgs.slice(0, 3)); // 主图(前3张)
}
const details = imgs.slice(3); // 该颜色的所有细节图
if (details.length > 0) {
colorDetails.push(details);
}
});
// 2. 核心:按颜色交替生成细节图(A1→B1→A2→B2)
let alternateDetails = [];
let maxDetailLen = Math.max(...colorDetails.map(d => d.length), 0);
for (let i = 0; i < maxDetailLen; i++) {
colorDetails.forEach(details => {
if (details[i]) alternateDetails.push(details[i]);
});
}
// 3. 最终轮播图:主图 + 交替细节图,最多10张
payload.carouselImageUrls = [...mainImages, ...alternateDetails].slice(0, 10);
return payload;
},
async createTemuProductDraft() {
try {
const { data } = await axios.post('https://agentseller.temu.com/visage-agent-seller/product/draft/add', {
catId: 30478,
productSemiManagedReq: { bindSiteIds: [127] },
productWarehouseRouteReq: {
targetRouteList: [{
warehouseId: "WH-01805626921113355",
siteIdList: [127]
}]
}
}, {
headers: {
"accept": "*/*", "accept-language": "zh-CN,zh;q=0.9,en;q=0.8",
"anti-content": currentAnti, "cache-control": "no-cache",
"content-type": "application/json", "mallid": currentMallId, "pragma": "no-cache"
},
withCredentials: true
});
if (data.success) return data.result.productDraftId;
console.error('创建草稿失败', data);
return null;
} catch (e) {
console.error('创建草稿异常', e);
return null;
}
},
async saveTemuProductDraft(payload) {
try {
const { data: firstData } = await axios.post(
'https://agentseller.temu.com/visage-agent-seller/product/draft/save',
payload, {
headers: {
"accept": "*/*", "accept-language": "zh-CN,zh;q=0.9,en;q=0.8",
"anti-content": currentAnti, "cache-control": "no-cache",
"content-type": "application/json",
"mallid": currentMallId,
"pragma": "no-cache"
},
withCredentials: true
});
if (firstData && firstData.success) {
console.log("✅ 草稿保存成功!", firstData);
this.showCustomAlert("✅ 商品草稿保存成功");
return true;
} else {
console.error("❌ 草稿保存失败", firstData);
this.showCustomAlert("❌ 草稿保存失败");
return false;
}
} catch (err) {
console.error("❌ 保存草稿接口异常", err);
this.showCustomAlert("❌ 保存草稿异常");
return false;
}
},
async publishTemuProductDirect(payload) {
try {
const { data } = await axios.post(
"https://agentseller.temu.com/visage-agent-seller/product/add",
payload,
{
headers: {
"accept": "*/*",
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8",
"anti-content": currentAnti,
"content-type": "application/json",
"mallid": currentMallId,
"pragma": "no-cache"
},
withCredentials: true
}
);
if (data.success === true) {
const productId = data.result?.productId || "未知ID";
console.log("✅ 商品直接发布成功!商品ID:", productId, data);
this.showCustomAlert("✅ 商品发布成功!ID:" + productId);
return true;
} else {
console.error("❌ 商品发布失败", data);
this.showCustomAlert("❌ 发布失败:" + (data.errorMsg || "未知错误"));
return false;
}
} catch (err) {
console.error("❌ 发布接口异常", err);
this.showCustomAlert("❌ 发布异常");
return false;
}
},
}
});
// ==============================================
// 6. 打开/关闭面板
// ==============================================
let show = false;
floatBtn.addEventListener('click', () => {
show = !show;
if (show) {
appContainer.style.display = 'flex';
floatBtn.textContent = '关闭上架工具';
} else {
appContainer.style.display = 'none';
floatBtn.textContent = '打开上架工具';
}
});
})();