This commit is contained in:
2025-10-29 19:43:03 +08:00
parent 9387722860
commit 98ae13db7a

View File

@@ -10,7 +10,9 @@
<el-steps :active="activeStep" finish-status="success" align-center style="margin-bottom: 30px">
<el-step title="输入线报消息"></el-step>
<el-step title="选择商品"></el-step>
<el-step title="编辑商品信息"></el-step>
<el-step title="设置参数"></el-step>
<el-step title="预览确认"></el-step>
<el-step title="批量发品"></el-step>
</el-steps>
@@ -103,8 +105,112 @@
</div>
</div>
<!-- 第三步设置参数 -->
<!-- 第三步编辑商品信息 -->
<div v-show="activeStep === 2" class="step-content">
<div style="margin-bottom: 15px;">
<el-alert type="info" :closable="false">
为每个商品设置发布价格和文案版本固定属性成色库存等将在下一步统一设置
</el-alert>
</div>
<el-table :data="selectedProducts" border style="width: 100%">
<!-- 商品图片 -->
<el-table-column label="商品图片" width="100" align="center">
<template slot-scope="scope">
<el-image
v-if="scope.row.productImage"
:src="scope.row.productImage"
style="width: 60px; height: 60px; border-radius: 4px;"
fit="cover"
/>
</template>
</el-table-column>
<!-- 商品名称 -->
<el-table-column label="商品名称" min-width="250">
<template slot-scope="scope">
<div style="font-weight: bold; margin-bottom: 5px;">{{ scope.row.productName }}</div>
<div style="color: #666; font-size: 12px;">SKUID: {{ scope.row.skuid }}</div>
</template>
</el-table-column>
<!-- 京东原价 -->
<el-table-column label="京东原价" width="100" align="center">
<template slot-scope="scope">
<span style="color: #909399;">¥{{ scope.row.price }}</span>
</template>
</el-table-column>
<!-- 发布价格可编辑 -->
<el-table-column label="发布价格" width="140" align="center">
<template slot-scope="scope">
<el-input-number
v-model="scope.row.publishPrice"
:min="0"
:precision="2"
:step="10"
size="small"
controls-position="right"
style="width: 120px;"
/>
</template>
</el-table-column>
<!-- 文案版本选择 -->
<el-table-column label="文案版本" width="200" align="center">
<template slot-scope="scope">
<el-select
v-model="scope.row.selectedWenanIndex"
size="small"
style="width: 100%;"
:disabled="!scope.row.wenan || scope.row.wenan.length === 0"
>
<el-option
v-for="(wenan, index) in scope.row.wenan"
:key="index"
:label="wenan.type"
:value="index"
/>
</el-select>
<div v-if="!scope.row.wenan || scope.row.wenan.length === 0" style="color: #999; font-size: 12px;">
无可用文案
</div>
</template>
</el-table-column>
<!-- 预览文案 -->
<el-table-column label="操作" width="100" align="center">
<template slot-scope="scope">
<el-button
type="text"
size="small"
@click="previewWenan(scope.row)"
:disabled="!scope.row.wenan || scope.row.wenan.length === 0"
>
预览文案
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 批量操作 -->
<div style="margin-top: 15px;">
<el-button size="small" @click="batchSetPrice">批量设置价格</el-button>
<el-button size="small" @click="batchSetWenan">批量选择文案版本</el-button>
</div>
<div style="margin-top: 20px;">
<el-button @click="prevStep">
<i class="el-icon-arrow-left"></i> 上一步
</el-button>
<el-button type="primary" @click="nextStep">
下一步 <i class="el-icon-arrow-right"></i>
</el-button>
</div>
</div>
<!-- 第四步设置参数 -->
<div v-show="activeStep === 3" class="step-content">
<el-form :model="publishForm" :rules="publishRules" ref="publishForm" label-width="120px">
<el-row :gutter="20">
<el-col :span="12">
@@ -291,15 +397,93 @@
<el-button @click="prevStep">
<i class="el-icon-arrow-left"></i> 上一步
</el-button>
<el-button type="success" @click="submitPublish" :loading="publishing">
开始批量发品 <i class="el-icon-upload"></i>
<el-button type="primary" @click="nextStep">
下一步:预览确认 <i class="el-icon-arrow-right"></i>
</el-button>
</el-form-item>
</el-form>
</div>
<!-- 第步:发品进度 -->
<div v-show="activeStep === 3" class="step-content">
<!-- 第步:预览确认 -->
<div v-show="activeStep === 4" class="step-content">
<el-alert type="warning" :closable="false" style="margin-bottom: 20px;">
<template slot="title">
<strong>即将发布 {{ totalPublishCount }} 条商品</strong>
{{ selectedProducts.length }} 个商品 × {{ totalSubAccountCount }} 个子账号)
</template>
请仔细核对发布信息,确认无误后点击"确认发布"按钮。
</el-alert>
<!-- 汇总信息 -->
<el-descriptions :column="4" border style="margin-bottom: 20px;">
<el-descriptions-item label="商品数量">{{ selectedProducts.length }}</el-descriptions-item>
<el-descriptions-item label="目标账号">{{ totalSubAccountCount }}</el-descriptions-item>
<el-descriptions-item label="总发布数">{{ totalPublishCount }}</el-descriptions-item>
<el-descriptions-item label="延迟上架">{{ publishForm.delaySeconds }}秒</el-descriptions-item>
</el-descriptions>
<!-- 详细预览表格 -->
<el-table :data="previewList" border style="width: 100%" max-height="500">
<el-table-column type="index" label="#" width="50" align="center"/>
<el-table-column label="商品图片" width="80" align="center">
<template slot-scope="scope">
<el-image :src="scope.row.productImage" style="width: 50px; height: 50px;" fit="cover"/>
</template>
</el-table-column>
<el-table-column label="商品名称" min-width="200" show-overflow-tooltip>
<template slot-scope="scope">
<div style="font-weight: bold;">{{ scope.row.productName }}</div>
<div style="color: #666; font-size: 12px;">SKUID: {{ scope.row.skuid }}</div>
</template>
</el-table-column>
<el-table-column label="发布价格" width="100" align="center">
<template slot-scope="scope">
<span style="color: #f56c6c; font-weight: bold;">¥{{ scope.row.publishPrice }}</span>
</template>
</el-table-column>
<el-table-column label="目标账号" width="150" align="center">
<template slot-scope="scope">
<el-tag size="small">{{ scope.row.accountName }}</el-tag>
</template>
</el-table-column>
<el-table-column label="子账号" width="120" align="center">
<template slot-scope="scope">
{{ scope.row.subAccount }}
</template>
</el-table-column>
<el-table-column label="文案版本" width="150" align="center">
<template slot-scope="scope">
<el-tag type="success" size="small">{{ scope.row.wenanType }}</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="100" align="center" fixed="right">
<template slot-scope="scope">
<el-button type="text" size="small" @click="viewFullWenan(scope.row)">
查看文案
</el-button>
</template>
</el-table-column>
</el-table>
<div style="margin-top: 20px;">
<el-button @click="prevStep">
<i class="el-icon-arrow-left"></i> 上一步
</el-button>
<el-button type="success" @click="submitPublish" :loading="publishing">
确认发布 <i class="el-icon-check"></i>
</el-button>
</div>
</div>
<!-- 第六步:发品进度 -->
<div v-show="activeStep === 5" class="step-content">
<el-alert
v-if="publishResult.taskId"
title="批量发品任务已创建"
@@ -418,6 +602,55 @@
<el-table-column label="错误信息" prop="errorMessage" min-width="200" :show-overflow-tooltip="true"/>
</el-table>
</el-dialog>
<!-- 文案预览对话框 -->
<el-dialog title="文案预览" :visible.sync="wenanPreviewVisible" width="60%" append-to-body>
<el-descriptions v-if="currentPreviewProduct" :column="1" border>
<el-descriptions-item label="商品名称">{{ currentPreviewProduct.productName }}</el-descriptions-item>
<el-descriptions-item label="文案类型">
{{ currentPreviewProduct.wenan && currentPreviewProduct.wenan[currentPreviewProduct.selectedWenanIndex] ?
currentPreviewProduct.wenan[currentPreviewProduct.selectedWenanIndex].type : '无' }}
</el-descriptions-item>
</el-descriptions>
<el-divider>文案内容</el-divider>
<div v-if="currentPreviewProduct && currentPreviewProduct.wenan && currentPreviewProduct.wenan[currentPreviewProduct.selectedWenanIndex]"
style="white-space: pre-wrap; line-height: 1.8; padding: 15px; background: #f5f5f5; border-radius: 4px; max-height: 400px; overflow-y: auto;">
{{ currentPreviewProduct.wenan[currentPreviewProduct.selectedWenanIndex].content }}
</div>
<div v-else style="text-align: center; padding: 30px; color: #999;">
暂无文案内容
</div>
<div slot="footer">
<el-button @click="wenanPreviewVisible = false">关闭</el-button>
<el-button type="primary" @click="copyWenan"
:disabled="!currentPreviewProduct || !currentPreviewProduct.wenan || !currentPreviewProduct.wenan[currentPreviewProduct.selectedWenanIndex]">
复制文案
</el-button>
</div>
</el-dialog>
<!-- 完整文案查看对话框(预览页面使用) -->
<el-dialog title="完整文案" :visible.sync="fullWenanVisible" width="60%" append-to-body>
<el-descriptions v-if="currentFullWenan" :column="2" border>
<el-descriptions-item label="商品名称" :span="2">{{ currentFullWenan.productName }}</el-descriptions-item>
<el-descriptions-item label="发布价格">¥{{ currentFullWenan.publishPrice }}</el-descriptions-item>
<el-descriptions-item label="文案类型">{{ currentFullWenan.wenanType }}</el-descriptions-item>
</el-descriptions>
<el-divider>文案内容</el-divider>
<div style="white-space: pre-wrap; line-height: 1.8; padding: 15px; background: #f5f5f5; border-radius: 4px; max-height: 400px; overflow-y: auto;">
{{ currentFullWenan ? currentFullWenan.wenanContent : '' }}
</div>
<div slot="footer">
<el-button @click="fullWenanVisible = false">关闭</el-button>
<el-button type="primary" @click="copyFullWenan">复制文案</el-button>
</div>
</el-dialog>
</div>
</template>
@@ -511,9 +744,61 @@ export default {
// 任务详情
detailVisible: false,
currentTask: null,
taskItems: []
taskItems: [],
// 文案预览
wenanPreviewVisible: false,
currentPreviewProduct: null,
// 完整文案查看
fullWenanVisible: false,
currentFullWenan: null
};
},
computed: {
// 计算总子账号数
totalSubAccountCount() {
return Object.values(this.publishForm.accountSubAccounts)
.reduce((sum, subAccounts) => sum + subAccounts.length, 0);
},
// 计算总发布数
totalPublishCount() {
return this.selectedProducts.length * this.totalSubAccountCount;
},
// 生成预览列表
previewList() {
const list = [];
for (const product of this.selectedProducts) {
for (const appid of this.publishForm.selectedMainAccounts) {
const accountName = this.erpAccounts.find(a => a.value === appid)?.label || appid;
const subAccounts = this.publishForm.accountSubAccounts[appid] || [];
for (const subAccount of subAccounts) {
const wenanType = product.wenan && product.wenan[product.selectedWenanIndex] ?
product.wenan[product.selectedWenanIndex].type : '无';
const wenanContent = product.wenan && product.wenan[product.selectedWenanIndex] ?
product.wenan[product.selectedWenanIndex].content : '';
list.push({
skuid: product.skuid,
productName: product.productName,
productImage: product.productImage,
publishPrice: product.publishPrice || product.price,
accountName: accountName,
subAccount: subAccount,
wenanType: wenanType,
wenanContent: wenanContent
});
}
}
}
return list;
}
},
created() {
this.loadERPAccounts();
this.loadProvinces();
@@ -748,7 +1033,17 @@ export default {
try {
const res = await parseLineReport({ message: this.lineReportMessage });
if (res.code === 200) {
this.parsedProducts = res.data || [];
this.parsedProducts = (res.data || []).map(product => {
// 初始化商品数据
return {
...product,
publishPrice: product.price || 0, // 默认发布价格=京东价格
wenan: product.wenan || [], // 文案数组
selectedWenanIndex: 0, // 默认选择第一个文案
images: product.images || [] // 图片数组
};
});
if (this.parsedProducts.length === 0) {
this.$modal.msgWarning("未能识别到商品信息请检查消息内容");
} else {
@@ -833,10 +1128,17 @@ export default {
skuid: p.skuid,
productName: p.productName,
price: p.price,
publishPrice: p.publishPrice, // 【新增】发布价格
productImage: p.productImage,
images: p.images || [], // 【新增】图片数组
shopName: p.shopName,
shopId: p.shopId,
commissionInfo: p.commissionInfo
commission: p.commission,
commissionShare: p.commissionShare,
commissionInfo: p.commissionInfo,
// 【新增】文案信息
wenan: p.wenan || [],
selectedWenanIndex: p.selectedWenanIndex || 0
})),
accountConfigs: accountConfigs,
delaySeconds: this.publishForm.delaySeconds,
@@ -860,7 +1162,7 @@ export default {
if (res.code === 200) {
this.publishResult.taskId = res.data;
this.$modal.msgSuccess("批量发品任务已创建");
this.activeStep = 3;
this.activeStep = 5; // 跳转到第六步(发品进度)
this.startRefreshProgress();
} else {
this.$modal.msgError(res.msg || "提交失败");
@@ -1019,6 +1321,142 @@ export default {
3: "失败"
};
return map[status] || "未知";
},
// 预览文案
previewWenan(product) {
this.currentPreviewProduct = product;
this.wenanPreviewVisible = true;
},
// 复制文案
copyWenan() {
if (!this.currentPreviewProduct || !this.currentPreviewProduct.wenan ||
!this.currentPreviewProduct.wenan[this.currentPreviewProduct.selectedWenanIndex]) {
this.$modal.msgWarning('没有可复制的文案');
return;
}
const content = this.currentPreviewProduct.wenan[this.currentPreviewProduct.selectedWenanIndex].content;
// 使用现代浏览器的 Clipboard API
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(content).then(() => {
this.$modal.msgSuccess('文案已复制到剪贴板');
}).catch(() => {
this.fallbackCopyToClipboard(content);
});
} else {
this.fallbackCopyToClipboard(content);
}
},
// 降级复制方案
fallbackCopyToClipboard(text) {
const textArea = document.createElement("textarea");
textArea.value = text;
textArea.style.position = "fixed";
textArea.style.left = "-999999px";
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand('copy');
this.$modal.msgSuccess('文案已复制到剪贴板');
} catch (err) {
this.$modal.msgError('复制失败,请手动复制');
}
document.body.removeChild(textArea);
},
// 批量设置价格
batchSetPrice() {
if (this.selectedProducts.length === 0) {
this.$modal.msgWarning('请先选择商品');
return;
}
this.$prompt('请输入统一的发布价格', '批量设置', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputPattern: /^\d+(\.\d{1,2})?$/,
inputErrorMessage: '请输入有效的价格(最多两位小数)'
}).then(({ value }) => {
this.selectedProducts.forEach(p => {
p.publishPrice = parseFloat(value);
});
this.$modal.msgSuccess('批量设置成功');
}).catch(() => {});
},
// 批量选择文案版本
batchSetWenan() {
if (this.selectedProducts.length === 0) {
this.$modal.msgWarning('请先选择商品');
return;
}
// 找到第一个有文案的商品作为参考
const firstProductWithWenan = this.selectedProducts.find(p => p.wenan && p.wenan.length > 0);
if (!firstProductWithWenan) {
this.$modal.msgWarning('所有商品都没有可用的文案');
return;
}
// 构建选项
const options = firstProductWithWenan.wenan.map((w, index) => ({
label: w.type,
value: index
}));
this.$prompt('请选择文案版本(索引)', '批量设置', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputType: 'number',
inputPlaceholder: `请输入 0 到 ${options.length - 1} 之间的数字`
}).then(({ value }) => {
const index = parseInt(value);
if (isNaN(index) || index < 0 || index >= options.length) {
this.$modal.msgError(`请输入 0 到 ${options.length - 1} 之间的数字`);
return;
}
let successCount = 0;
this.selectedProducts.forEach(p => {
if (p.wenan && p.wenan.length > index) {
p.selectedWenanIndex = index;
successCount++;
}
});
this.$modal.msgSuccess(`成功设置 ${successCount} 个商品的文案版本为:${options[index].label}`);
}).catch(() => {});
},
// 查看完整文案(预览页面使用)
viewFullWenan(row) {
this.currentFullWenan = row;
this.fullWenanVisible = true;
},
// 复制完整文案
copyFullWenan() {
if (!this.currentFullWenan || !this.currentFullWenan.wenanContent) {
this.$modal.msgWarning('没有可复制的文案');
return;
}
const content = this.currentFullWenan.wenanContent;
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(content).then(() => {
this.$modal.msgSuccess('文案已复制到剪贴板');
}).catch(() => {
this.fallbackCopyToClipboard(content);
});
} else {
this.fallbackCopyToClipboard(content);
}
}
}
};