1
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user