1
This commit is contained in:
@@ -31,7 +31,7 @@
|
||||
</el-form>
|
||||
|
||||
<!-- 左右两个文本框 -->
|
||||
<el-row :gutter="20">
|
||||
<el-row :gutter="20">
|
||||
<!-- 左侧:输入文案 -->
|
||||
<el-col :span="12">
|
||||
<div class="text-panel">
|
||||
@@ -49,15 +49,15 @@
|
||||
<i class="el-icon-magic-stick"></i> 一键生成 ({{ detectedUrls.length }}个)
|
||||
</el-button>
|
||||
</div>
|
||||
<el-input
|
||||
type="textarea"
|
||||
<el-input
|
||||
type="textarea"
|
||||
:rows="25"
|
||||
v-model="content"
|
||||
placeholder="💡 将包含京东链接的完整推广文案粘贴到这里 示例: 🔴【海尔电热水器】11月9号晚8好价~ ✅9折券:商品页面直接领取 1️⃣海尔无镁棒BK5PLUS(60升) 下单:https://u.jd.com/T1G7978 👉300券+388红包 2️⃣海尔无镁棒BK5PLUS(80升) 下单:https://u.jd.com/TrG7lCN 👉300券+388红包 ✨ 系统会自动识别所有京东链接并替换!"
|
||||
class="textarea-input"
|
||||
/>
|
||||
</div>
|
||||
</el-col>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- 右侧:输出结果 -->
|
||||
<el-col :span="12">
|
||||
@@ -96,8 +96,8 @@
|
||||
</el-progress>
|
||||
<div style="margin-top: 15px; color: #909399; font-size: 14px;">
|
||||
{{ progressDetail }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="empty-container">
|
||||
<i class="el-icon-document" style="font-size: 64px; color: #DCDFE6; margin-bottom: 10px;"></i>
|
||||
<div style="color: #909399; font-size: 14px;">点击左侧"一键生成"按钮后,替换结果将显示在这里</div>
|
||||
@@ -114,15 +114,15 @@
|
||||
</span>
|
||||
</template>
|
||||
</el-alert>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { generatePromotionContent, createGiftCoupon, transferWithGift } from '@/api/system/jdorder'
|
||||
import { replaceUrlsWithGiftCoupons } from '@/api/system/jdorder'
|
||||
|
||||
export default {
|
||||
name: 'BatchGiftCoupon',
|
||||
@@ -209,186 +209,48 @@ export default {
|
||||
}
|
||||
|
||||
this.processing = true
|
||||
this.progress = 0
|
||||
this.progressText = '准备中...'
|
||||
this.progressDetail = '正在从URL提取商品信息...'
|
||||
this.progress = 20
|
||||
this.progressText = '正在处理...'
|
||||
this.progressDetail = '后端正在为每个URL单独创建礼金券,请耐心等待...'
|
||||
this.progressStatus = ''
|
||||
this.result = null
|
||||
|
||||
try {
|
||||
// 为每个URL单独处理:查询商品 → 创建礼金 → 转链
|
||||
let replacedContent = this.content
|
||||
const replacements = []
|
||||
let successCount = 0
|
||||
// 调用后端接口,后端会为每个URL单独处理
|
||||
const params = {
|
||||
content: this.content,
|
||||
amount: this.form.amount,
|
||||
quantity: this.form.quantity,
|
||||
owner: this.form.owner || 'g'
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.detectedUrls.length; i++) {
|
||||
const originalUrl = this.detectedUrls[i]
|
||||
this.progress = Math.floor(10 + (i / this.detectedUrls.length) * 80)
|
||||
this.progressText = `正在处理第 ${i + 1}/${this.detectedUrls.length} 个商品...`
|
||||
this.progressDetail = `URL: ${originalUrl.substring(0, 50)}...`
|
||||
this.progress = 40
|
||||
const res = await replaceUrlsWithGiftCoupons(params)
|
||||
|
||||
this.progress = 80
|
||||
|
||||
if (res && res.code === 200 && res.data) {
|
||||
this.result = res.data
|
||||
|
||||
try {
|
||||
// 1. 查询商品信息
|
||||
const queryRes = await generatePromotionContent({ promotionContent: originalUrl })
|
||||
|
||||
if (!queryRes || queryRes.code !== 200) {
|
||||
replacements.push({
|
||||
originalUrl,
|
||||
newUrl: originalUrl,
|
||||
success: false,
|
||||
error: '查询商品信息失败'
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
let parsed = null
|
||||
let resultStr = queryRes.data || queryRes.msg
|
||||
|
||||
if (typeof resultStr === 'string') {
|
||||
parsed = JSON.parse(resultStr)
|
||||
} else {
|
||||
parsed = resultStr
|
||||
}
|
||||
|
||||
// 提取商品信息
|
||||
let product = null
|
||||
if (Array.isArray(parsed) && parsed.length > 0) {
|
||||
product = parsed[0]
|
||||
} else if (parsed && typeof parsed === 'object') {
|
||||
if (parsed.list && Array.isArray(parsed.list) && parsed.list.length > 0) {
|
||||
product = parsed.list[0]
|
||||
} else if (parsed.data && Array.isArray(parsed.data) && parsed.data.length > 0) {
|
||||
product = parsed.data[0]
|
||||
} else if (parsed.materialUrl || parsed.skuName) {
|
||||
product = parsed
|
||||
}
|
||||
}
|
||||
|
||||
if (!product) {
|
||||
replacements.push({
|
||||
originalUrl,
|
||||
newUrl: originalUrl,
|
||||
success: false,
|
||||
error: '无法提取商品信息'
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
// 2. 创建礼金券
|
||||
const createParams = {
|
||||
amount: this.form.amount,
|
||||
quantity: this.form.quantity,
|
||||
owner: this.form.owner || 'g',
|
||||
skuName: product.skuName || product.title || product.productName || product.cleanSkuName || ''
|
||||
}
|
||||
|
||||
// 设置skuId或materialUrl
|
||||
const isPop = this.form.owner === 'pop'
|
||||
if (isPop) {
|
||||
if (product.materialUrl) {
|
||||
createParams.materialUrl = product.materialUrl
|
||||
} else if ((product.skuId || product.skuid) && /^\d+$/.test(String(product.skuId || product.skuid))) {
|
||||
createParams.skuId = String(product.skuId || product.skuid)
|
||||
}
|
||||
} else {
|
||||
const skuIdValue = product.skuId || product.skuid
|
||||
if (skuIdValue && /^\d+$/.test(String(skuIdValue))) {
|
||||
createParams.skuId = String(skuIdValue)
|
||||
} else if (product.materialUrl) {
|
||||
createParams.materialUrl = product.materialUrl
|
||||
}
|
||||
}
|
||||
|
||||
const createRes = await createGiftCoupon(createParams)
|
||||
|
||||
if (!createRes || createRes.code !== 200 || !createRes.data) {
|
||||
replacements.push({
|
||||
originalUrl,
|
||||
newUrl: originalUrl,
|
||||
success: false,
|
||||
error: '创建礼金失败'
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
const giftCouponKey = typeof createRes.data === 'object' ? createRes.data.giftCouponKey : null
|
||||
|
||||
if (!giftCouponKey) {
|
||||
replacements.push({
|
||||
originalUrl,
|
||||
newUrl: originalUrl,
|
||||
success: false,
|
||||
error: '礼金Key为空'
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
// 3. 转链(带礼金)
|
||||
const transferParams = {
|
||||
materialUrl: originalUrl,
|
||||
giftCouponKey: giftCouponKey
|
||||
}
|
||||
|
||||
const transferRes = await transferWithGift(transferParams)
|
||||
|
||||
if (transferRes && transferRes.code === 200 && transferRes.data) {
|
||||
const shortUrl = typeof transferRes.data === 'object' ? transferRes.data.shortURL : transferRes.data
|
||||
|
||||
if (shortUrl) {
|
||||
// 替换文本中的URL
|
||||
replacedContent = replacedContent.replace(originalUrl, shortUrl)
|
||||
replacements.push({
|
||||
originalUrl,
|
||||
newUrl: shortUrl,
|
||||
success: true,
|
||||
giftCouponKey: giftCouponKey
|
||||
})
|
||||
successCount++
|
||||
} else {
|
||||
replacements.push({
|
||||
originalUrl,
|
||||
newUrl: originalUrl,
|
||||
success: false,
|
||||
error: '转链失败'
|
||||
})
|
||||
}
|
||||
} else {
|
||||
replacements.push({
|
||||
originalUrl,
|
||||
newUrl: originalUrl,
|
||||
success: false,
|
||||
error: '转链接口调用失败'
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`处理URL失败: ${originalUrl}`, e)
|
||||
replacements.push({
|
||||
originalUrl,
|
||||
newUrl: originalUrl,
|
||||
success: false,
|
||||
error: e.message || '未知错误'
|
||||
})
|
||||
const successCount = this.result.replacedCount || 0
|
||||
const totalCount = this.result.totalUrls || 0
|
||||
|
||||
this.progress = 100
|
||||
this.progressStatus = successCount === totalCount ? 'success' : (successCount > 0 ? 'warning' : 'exception')
|
||||
this.progressText = successCount === totalCount ? '完成!' : '部分成功'
|
||||
this.progressDetail = `成功替换 ${successCount} / ${totalCount} 个URL`
|
||||
|
||||
if (successCount > 0) {
|
||||
this.$modal.msgSuccess(`✅ 批量替换完成!成功 ${successCount} / ${totalCount} 个`)
|
||||
} else {
|
||||
this.$modal.msgError('批量替换失败,所有URL处理均失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 构建结果
|
||||
this.result = {
|
||||
replacedContent: replacedContent,
|
||||
originalContent: this.content,
|
||||
replacements: replacements,
|
||||
totalUrls: this.detectedUrls.length,
|
||||
replacedCount: successCount
|
||||
}
|
||||
|
||||
this.progress = 100
|
||||
this.progressStatus = successCount === this.detectedUrls.length ? 'success' : (successCount > 0 ? 'warning' : 'exception')
|
||||
this.progressText = successCount === this.detectedUrls.length ? '完成!' : '部分成功'
|
||||
this.progressDetail = `成功替换 ${successCount} / ${this.detectedUrls.length} 个URL`
|
||||
|
||||
if (successCount > 0) {
|
||||
this.$modal.msgSuccess(`✅ 批量替换完成!成功 ${successCount} / ${this.detectedUrls.length} 个`)
|
||||
} else {
|
||||
this.$modal.msgError('批量替换失败,所有URL处理均失败')
|
||||
this.progress = 100
|
||||
this.progressStatus = 'exception'
|
||||
this.progressText = '失败'
|
||||
this.progressDetail = res.msg || '未知错误'
|
||||
this.$modal.msgError('批量替换失败:' + (res.msg || '未知错误'))
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('批量替换异常', e)
|
||||
|
||||
@@ -132,18 +132,24 @@
|
||||
<div v-if="expandedRecords.includes(record.batchId)" class="record-detail">
|
||||
<el-divider></el-divider>
|
||||
|
||||
<div v-if="record.resultMessage" class="detail-message">
|
||||
<div class="message-label">结果消息:</div>
|
||||
<div class="message-content">{{ record.resultMessage }}</div>
|
||||
<!-- 加载状态 -->
|
||||
<div v-if="record.loadingDetail" class="loading-detail" v-loading="true" element-loading-text="正在加载详情...">
|
||||
<div style="height: 100px;"></div>
|
||||
</div>
|
||||
|
||||
<div v-if="record.errorMessage" class="detail-error">
|
||||
<div class="error-label">错误信息:</div>
|
||||
<div class="error-content">{{ record.errorMessage }}</div>
|
||||
</div>
|
||||
<template v-else>
|
||||
<div v-if="record.resultMessage" class="detail-message">
|
||||
<div class="message-label">结果消息:</div>
|
||||
<div class="message-content">{{ record.resultMessage }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作日志列表 -->
|
||||
<div v-if="record.operationLogs && record.operationLogs.length > 0" class="operation-logs">
|
||||
<div v-if="record.errorMessage" class="detail-error">
|
||||
<div class="error-label">错误信息:</div>
|
||||
<div class="error-content">{{ record.errorMessage }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作日志列表 -->
|
||||
<div v-if="record.operationLogs && record.operationLogs.length > 0" class="operation-logs">
|
||||
<div class="logs-header">
|
||||
<i class="el-icon-document"></i>
|
||||
<span>操作日志({{ record.operationLogs.length }} 条)</span>
|
||||
@@ -168,6 +174,13 @@
|
||||
<el-table-column prop="errorMessage" label="错误信息" min-width="150" show-overflow-tooltip />
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<!-- 暂无操作日志 -->
|
||||
<div v-else class="no-logs">
|
||||
<i class="el-icon-info"></i>
|
||||
<span>暂无操作日志</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</el-collapse-transition>
|
||||
</el-card>
|
||||
@@ -330,12 +343,48 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
toggleRecordDetail(batchId) {
|
||||
async toggleRecordDetail(batchId) {
|
||||
const index = this.expandedRecords.indexOf(batchId)
|
||||
if (index > -1) {
|
||||
// 收起
|
||||
this.expandedRecords.splice(index, 1)
|
||||
} else {
|
||||
// 展开 - 加载详情
|
||||
this.expandedRecords.push(batchId)
|
||||
await this.loadRecordDetail(batchId)
|
||||
}
|
||||
},
|
||||
|
||||
async loadRecordDetail(batchId) {
|
||||
try {
|
||||
const record = this.batchRecords.find(r => r.batchId === batchId)
|
||||
if (!record) return
|
||||
|
||||
// 如果已经加载过详情,则不再重复加载
|
||||
if (record.operationLogs && record.operationLogs.length > 0) {
|
||||
return
|
||||
}
|
||||
|
||||
// 显示加载状态
|
||||
this.$set(record, 'loadingDetail', true)
|
||||
|
||||
const res = await getBatchPushRecordDetail(batchId)
|
||||
if (res.code === 200 && res.data) {
|
||||
// 更新记录的详细信息
|
||||
this.$set(record, 'operationLogs', res.data.operationLogs || [])
|
||||
this.$set(record, 'errorMessage', res.data.errorMessage)
|
||||
this.$set(record, 'resultMessage', res.data.remark)
|
||||
} else {
|
||||
this.$message.warning('加载详情失败: ' + (res.msg || '未知错误'))
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('加载推送详情失败', e)
|
||||
this.$message.error('加载详情失败: ' + (e.message || '未知错误'))
|
||||
} finally {
|
||||
const record = this.batchRecords.find(r => r.batchId === batchId)
|
||||
if (record) {
|
||||
this.$set(record, 'loadingDetail', false)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -711,5 +760,25 @@ export default {
|
||||
.logs-header i {
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.loading-detail {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.no-logs {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
padding: 30px;
|
||||
color: #909399;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.no-logs i {
|
||||
font-size: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user