This commit is contained in:
2025-11-07 16:10:23 +08:00
parent 4f6403d08c
commit 5b607c8031

View File

@@ -4,312 +4,119 @@
<div slot="header">
<span style="font-weight: bold; font-size: 18px;">批量创建礼金并替换URL</span>
<el-divider direction="vertical"></el-divider>
<span style="color: #909399; font-size: 14px;">支持批量创建20张1元面额礼金券并自动将文本中的URL替换为包含礼金的推广链接</span>
<span style="color: #909399; font-size: 14px;">一键操作粘贴文案 自动创建礼金 输出替换后的文案</span>
</div>
<!-- 商品信息配置区域 -->
<el-form :model="form" :rules="rules" ref="form" label-width="120px" style="margin-bottom: 20px;">
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="商品链接/SKU" prop="materialUrl">
<el-input
type="textarea"
:rows="3"
v-model="form.materialUrl"
placeholder="请输入商品链接或SKU ID每行一个支持多个点击查询按钮获取商品信息"
/>
<div style="margin-top: 10px; text-align: right;">
<el-button type="primary" icon="el-icon-search" @click="queryProductInfo" :loading="queryLoading">查询商品</el-button>
</div>
<div style="color: #E6A23C; font-size: 12px; margin-top: 5px;">
<i class="el-icon-warning"></i>
<strong>重要</strong>请输入商品链接或SKU ID支持多行输入多个商品点击查询后会显示商品列表供选择
</div>
</el-form-item>
</el-col>
</el-row>
<!-- 配置区域 -->
<el-form :model="form" :rules="rules" ref="form" inline style="margin-bottom: 15px;">
<el-form-item label="礼金金额" prop="amount">
<el-input-number v-model="form.amount" :min="1" :max="50" :precision="2" :step="0.01" style="width: 120px" />
<span style="margin-left: 5px; color: #909399;"></span>
</el-form-item>
<el-form-item label="每张数量" prop="quantity">
<el-input-number v-model="form.quantity" :min="1" :max="100" style="width: 120px" />
<span style="margin-left: 5px; color: #909399;"></span>
</el-form-item>
<el-form-item label="商品类型" prop="owner">
<el-radio-group v-model="form.owner">
<el-radio label="g">自营</el-radio>
<el-radio label="pop">POP</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item style="margin-left: 20px;">
<el-tag v-if="detectedUrls.length > 0" type="success" size="medium">
<i class="el-icon-link"></i> 已识别 {{ detectedUrls.length }} 个URL
</el-tag>
</el-form-item>
</el-form>
<!-- 商品列表展示 -->
<el-card v-if="productList.length > 0" shadow="never" style="margin-bottom: 20px;">
<div slot="header">
<span style="font-weight: bold;">查询结果 {{ productList.length }} 个商品</span>
<span style="color: #909399; font-size: 12px; margin-left: 10px;">已选择 {{ selectedProducts.length }} 个商品</span>
<div style="float: right;">
<el-button size="mini" @click="selectAllProducts">全选</el-button>
<el-button size="mini" @click="clearSelection">清空</el-button>
<!-- 左右两个文本框 -->
<el-row :gutter="20">
<!-- 左侧输入文案 -->
<el-col :span="12">
<div class="text-panel">
<div class="panel-header">
<span style="font-weight: bold; font-size: 16px;">
<i class="el-icon-edit-outline"></i> 输入原始文案
</span>
<el-button
type="primary"
size="small"
@click="handleProcess"
:loading="processing"
:disabled="detectedUrls.length === 0"
>
<i class="el-icon-magic-stick"></i> 一键生成 ({{ detectedUrls.length }})
</el-button>
</div>
<el-input
type="textarea"
:rows="25"
v-model="content"
placeholder="💡 将包含京东链接的完整推广文案粘贴到这里&#10;&#10;示例:&#10;🔴【海尔电热水器】11月9号晚8好价&#10;✅9折券商品页面直接领取&#10;&#10;1⃣海尔无镁棒BK5PLUS60升&#10;下单https://u.jd.com/T1G7978&#10;👉300券+388红包&#10;&#10;2⃣海尔无镁棒BK5PLUS80升&#10;下单https://u.jd.com/TrG7lCN&#10;👉300券+388红包&#10;&#10;✨ 系统会自动识别所有京东链接并替换!"
class="textarea-input"
/>
</div>
</el-col>
<!-- 右侧输出结果 -->
<el-col :span="12">
<div class="text-panel">
<div class="panel-header">
<span style="font-weight: bold; font-size: 16px;">
<i class="el-icon-document-checked"></i> 替换后的文案
</span>
<el-button
type="success"
size="small"
@click="copyResult"
:disabled="!result || !result.replacedContent"
>
<i class="el-icon-document-copy"></i> 复制结果
</el-button>
</div>
<el-input
v-if="!processing && result && result.replacedContent"
type="textarea"
:rows="25"
v-model="result.replacedContent"
readonly
class="textarea-output"
/>
<div v-else-if="processing" class="loading-container">
<el-progress
:percentage="progress"
:status="progressStatus"
:stroke-width="15"
style="width: 80%;"
>
<template slot="format">
{{ progressText }}
</template>
</el-progress>
<div style="margin-top: 15px; color: #909399; font-size: 14px;">
{{ progressDetail }}
</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>
</div>
</div>
<el-table
ref="productTable"
:data="productList"
border
@selection-change="handleSelectionChange"
style="width: 100%"
>
<el-table-column type="selection" width="55" :reserve-selection="true" />
<el-table-column type="index" label="序号" width="60" />
<el-table-column label="商品名称" prop="skuName" min-width="250" show-overflow-tooltip>
<template slot-scope="scope">
<div>
<div style="font-weight: bold; margin-bottom: 5px;">{{ scope.row.skuName || scope.row.title || scope.row.productName || scope.row.cleanSkuName || '-' }}</div>
<div v-if="scope.row.cleanSkuName && scope.row.cleanSkuName !== scope.row.skuName" style="color: #909399; font-size: 12px;">
清理后名称{{ scope.row.cleanSkuName }}
</div>
</div>
</template>
</el-table-column>
<el-table-column label="商品类型" width="80">
<template slot-scope="scope">
<el-tag :type="(scope.row.owner === 'g' || (!scope.row.popId && scope.row.owner !== 'pop')) ? 'success' : 'warning'">
{{ (scope.row.owner === 'g' || (!scope.row.popId && scope.row.owner !== 'pop')) ? '自营' : 'POP' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="SPU ID" prop="spuid" width="150" show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.spuid || scope.row.spuId || '-' }}
</template>
</el-table-column>
<el-table-column label="SKU ID" prop="skuId" width="150" show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.skuId || scope.row.skuid || '-' }}
</template>
</el-table-column>
<el-table-column label="oriItemId" prop="oriItemId" width="150" show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.oriItemId || scope.row.ori_item_id || '-' }}
</template>
</el-table-column>
<el-table-column label="价格" width="120">
<template slot-scope="scope">
<span v-if="scope.row.price">¥{{ formatPrice(scope.row.price) }}</span>
<span v-else style="color: #909399;">-</span>
</template>
</el-table-column>
<el-table-column label="佣金比例" width="100">
<template slot-scope="scope">
<span v-if="scope.row.commissionShare || scope.row.commission">{{ (scope.row.commissionShare || scope.row.commission) }}%</span>
<span v-else style="color: #909399;">-</span>
</template>
</el-table-column>
<el-table-column label="佣金金额" width="120">
<template slot-scope="scope">
<span v-if="scope.row.commission">
¥{{ formatCommissionAmount(scope.row.commission, scope.row.price) }}
</span>
<span v-else style="color: #909399;">-</span>
</template>
</el-table-column>
</el-table>
</el-card>
<!-- 已选商品信息展示 -->
<el-card v-if="selectedProducts.length > 0" shadow="never" style="margin-bottom: 20px;">
<div slot="header">
<span style="font-weight: bold;">已选商品信息{{ selectedProducts.length }} </span>
<el-button type="primary" size="small" style="float: right;" @click="submitSelectedProducts" :loading="processing">
<i class="el-icon-check"></i> 提交并批量创建
</el-button>
</div>
<el-table :data="selectedProducts" border style="margin-top: 10px;" max-height="300">
<el-table-column type="index" label="序号" width="60" />
<el-table-column label="商品名称" prop="skuName" min-width="250" show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.skuName || scope.row.title || scope.row.productName || scope.row.cleanSkuName || '-' }}
</template>
</el-table-column>
<el-table-column label="SPU ID" width="120">
<template slot-scope="scope">{{ scope.row.spuid || scope.row.spuId || '-' }}</template>
</el-table-column>
<el-table-column label="SKU ID" width="120">
<template slot-scope="scope">{{ scope.row.skuId || scope.row.skuid || '-' }}</template>
</el-table-column>
<el-table-column label="类型" width="80">
<template slot-scope="scope">
<el-tag :type="(scope.row.owner === 'g' || (!scope.row.popId && scope.row.owner !== 'pop')) ? 'success' : 'warning'">
{{ (scope.row.owner === 'g' || (!scope.row.popId && scope.row.owner !== 'pop')) ? '自营' : 'POP' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="价格" width="100">
<template slot-scope="scope">
<span v-if="scope.row.price">¥{{ formatPrice(scope.row.price) }}</span>
<span v-else>-</span>
</template>
</el-table-column>
</el-table>
</el-card>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="礼金金额(元)" prop="amount">
<el-input-number v-model="form.amount" :min="1" :max="50" :precision="2" :step="0.01" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="每张礼金数量" prop="quantity">
<el-input-number v-model="form.quantity" :min="1" :max="100" style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-alert
:type="detectedUrls.length > 0 ? 'success' : 'info'"
:closable="false"
style="margin-bottom: 10px;"
>
<!-- 统计信息 -->
<div v-if="result && result.replacedContent" style="margin-top: 15px;">
<el-alert type="success" :closable="false">
<template slot="title">
<span style="font-weight: bold; font-size: 16px;">
<i :class="detectedUrls.length > 0 ? 'el-icon-success' : 'el-icon-info'"></i>
批量创建说明
</span>
<span v-if="detectedUrls.length > 0" style="color: #67C23A; font-weight: bold; font-size: 18px;">
已识别到 <strong style="font-size: 22px; color: #E6A23C;">{{ detectedUrls.length }}</strong> 个URL将批量创建 <strong style="font-size: 22px; color: #E6A23C;">{{ detectedUrls.length }}</strong> 张礼金券
</span>
<span v-else style="color: #909399; font-size: 14px;">
系统会自动解析文本中的京东商品URL根据解析出的URL数量自动批量创建对应数量的礼金券并替换URL请在下方文本框中输入包含京东商品链接的文本内容
<span style="font-weight: bold;">
<i class="el-icon-success"></i> 处理完成
成功替换 {{ result.replacedCount || 0 }} / {{ result.totalUrls || 0 }} 个URL
</span>
</template>
</el-alert>
</el-col>
</el-row>
</el-form>
<!-- 文本输入区域 -->
<el-divider content-position="left">
<span style="font-size: 16px; font-weight: bold;">
<i class="el-icon-edit"></i> 文本内容
<el-tag v-if="detectedUrls.length > 0" type="success" size="medium" style="margin-left: 10px;">
已检测到 {{ detectedUrls.length }} 个URL
</el-tag>
</span>
</el-divider>
<div style="margin-bottom: 10px;">
<div v-if="selectedProducts.length > 0" style="margin-bottom: 10px; padding: 10px; background: #f0f9ff; border: 1px solid #409EFF; border-radius: 4px;">
<i class="el-icon-success" style="color: #67C23A;"></i>
<span style="color: #409EFF; font-weight: bold;">已选择 {{ selectedProducts.length }} 个商品现在请粘贴您的推广文案包含京东链接</span>
</div>
<el-input
type="textarea"
:rows="12"
v-model="content"
placeholder="请将您的完整推广文案粘贴到这里包含emoji、商品描述、京东链接等所有内容&#10;&#10;示例:&#10;🔴【商品名称】活动时间~&#10;✅优惠券信息&#10;1⃣商品1&#10;下单https://u.jd.com/xxxxx&#10;👉优惠详情&#10;&#10;系统会自动识别所有京东链接并替换为包含礼金的推广链接"
style="margin-bottom: 10px;"
@focus="handleTextareaFocus"
/>
<div style="color: #E6A23C; font-size: 13px; margin-bottom: 10px; font-weight: bold;">
<i class="el-icon-warning"></i>
直接粘贴完整文案即可支持识别 https://item.jd.com、https://u.jd.com、u.jd.com 等所有京东链接格式
</div>
<div style="text-align: right; padding: 10px; background: #f5f7fa; border-radius: 4px;">
<span style="font-size: 16px; font-weight: bold; margin-right: 20px;">
已识别URL数量: <strong style="color: #E6A23C; font-size: 20px;">{{ detectedUrls.length }}</strong>
</span>
<el-button
type="primary"
size="medium"
@click="handleReplace"
:loading="processing"
:disabled="detectedUrls.length === 0 || selectedProducts.length === 0"
style="font-size: 16px; padding: 12px 24px;"
>
<i class="el-icon-magic-stick"></i> 批量创建并替换 ({{ detectedUrls.length }})
</el-button>
</div>
</div>
<!-- 进度显示 -->
<el-card v-if="processing" shadow="never" style="margin-top: 20px;">
<el-progress :percentage="progress" :status="progressStatus" :stroke-width="20">
<template slot="format">
{{ progressText }}
</template>
</el-progress>
<div style="margin-top: 10px; text-align: center; color: #909399;">
{{ progressDetail }}
</div>
</el-card>
<!-- 结果展示区域 -->
<el-card v-if="result && result.replacedContent" shadow="never" style="margin-top: 20px;">
<div slot="header">
<span style="font-weight: bold;">替换结果</span>
<el-button style="float: right; padding: 3px 0;" type="text" @click="copyResult">
<i class="el-icon-document-copy"></i> 复制结果
</el-button>
</div>
<!-- 统计信息 -->
<el-row :gutter="20" style="margin-bottom: 20px;">
<el-col :span="6">
<el-statistic title="总URL数" :value="result.totalUrls || 0" />
</el-col>
<el-col :span="6">
<el-statistic title="成功替换" :value="result.replacedCount || 0">
<template slot="suffix">
<span style="color: #67C23A;"></span>
</template>
</el-statistic>
</el-col>
<el-col :span="6">
<el-statistic title="失败数量" :value="(result.totalUrls || 0) - (result.replacedCount || 0)">
<template slot="suffix">
<span style="color: #F56C6C;"></span>
</template>
</el-statistic>
</el-col>
<el-col :span="6">
<el-statistic title="成功率" :value="result.totalUrls > 0 ? Math.round((result.replacedCount / result.totalUrls) * 100) : 0">
<template slot="suffix">
<span style="color: #409EFF;">%</span>
</template>
</el-statistic>
</el-col>
</el-row>
<!-- 替换后的文本 -->
<el-form-item label="替换后的文本">
<el-input
type="textarea"
:rows="10"
v-model="result.replacedContent"
readonly
/>
</el-form-item>
<!-- 替换详情表格 -->
<el-table :data="result.replacements || []" border style="margin-top: 20px;" max-height="300">
<el-table-column type="index" label="序号" width="60" />
<el-table-column label="原始URL" prop="originalUrl" min-width="250" show-overflow-tooltip />
<el-table-column label="新URL" prop="newUrl" min-width="250" show-overflow-tooltip />
<el-table-column label="状态" width="100">
<template slot-scope="scope">
<el-tag :type="scope.row.success ? 'success' : 'danger'">
{{ scope.row.success ? '成功' : '失败' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="礼金Key" prop="giftCouponKey" width="200" show-overflow-tooltip>
<template slot-scope="scope">
<span v-if="scope.row.giftCouponKey">{{ scope.row.giftCouponKey }}</span>
<span v-else style="color: #909399;">-</span>
</template>
</el-table-column>
<el-table-column label="操作" width="120" fixed="right">
<template slot-scope="scope">
<el-button
type="text"
size="mini"
@click="copyToClipboard(scope.row.newUrl || scope.row.originalUrl)"
:disabled="!scope.row.success"
>
复制新URL
</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
</div>
</el-col>
</el-row>
</el-card>
</div>
</template>
@@ -323,22 +130,14 @@ export default {
return {
content: '',
form: {
materialUrl: '',
skuName: '',
owner: 'g',
amount: 1.8, // 默认1.8元
quantity: 12, // 默认每个礼金数量为12
queryResult: null, // 保存查询到的商品信息(保留兼容性)
selectedProduct: null // 用户选中的商品(保留兼容性,用于单个商品)
amount: 1.8,
quantity: 12,
owner: 'g'
},
productList: [], // 查询到的商品列表
selectedProducts: [], // 用户选中的商品列表(多选)
rules: {
materialUrl: [{ required: true, message: '请输入商品链接或SKU', trigger: 'blur' }],
amount: [{ required: true, message: '请输入礼金金额', trigger: 'blur' }],
quantity: [{ required: true, message: '请输入礼金数量', trigger: 'blur' }]
},
queryLoading: false,
processing: false,
progress: 0,
progressText: '',
@@ -354,216 +153,6 @@ export default {
}
},
methods: {
/** 查询商品信息 */
async queryProductInfo() {
const materialUrl = this.form.materialUrl.trim()
if (!materialUrl) {
this.$modal.msgWarning('请输入商品链接或SKU ID')
return
}
this.queryLoading = true
this.productList = []
this.form.selectedProduct = null
this.form.queryResult = null
try {
const res = await generatePromotionContent({ promotionContent: materialUrl })
if (res && res.code === 200) {
try {
let resultStr = res.data || res.msg
let parsed = null
if (typeof resultStr === 'string') {
parsed = JSON.parse(resultStr)
} else {
parsed = resultStr
}
console.log('解析后的数据:', parsed)
// 解析商品数组
let products = []
if (Array.isArray(parsed) && parsed.length > 0) {
// 格式1直接是数组
products = parsed
} else if (parsed && typeof parsed === 'object') {
// 格式2对象中包含list数组
if (parsed.list && Array.isArray(parsed.list) && parsed.list.length > 0) {
products = parsed.list
}
// 格式3对象中包含data数组
else if (parsed.data && Array.isArray(parsed.data) && parsed.data.length > 0) {
products = parsed.data
}
// 格式4对象本身包含商品信息单个商品
else if (parsed.materialUrl || parsed.owner || parsed.skuName) {
products = [parsed]
}
}
console.log('解析到的商品列表:', products)
if (products.length > 0) {
this.productList = products
// 默认全选所有商品
this.$nextTick(() => {
this.selectAllProducts()
// 如果只有一个商品,自动滚动到文本输入区域
if (products.length === 1) {
setTimeout(() => {
window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' })
}, 300)
}
})
this.$modal.msgSuccess({
message: `✅ 查询成功!找到 ${products.length} 个商品,已默认全选。现在请在下方"输入文本"框中粘贴您的推广文案。`,
duration: 5000
})
} else {
this.$modal.msgWarning('未找到商品信息,请检查链接是否正确')
}
} catch (e) {
console.error('解析商品信息失败', e)
this.$modal.msgWarning('返回数据格式异常:' + e.message)
}
} else {
this.$modal.msgError('查询商品信息失败:' + (res.msg || '未知错误'))
}
} catch (e) {
console.error('查询商品信息异常', e)
this.$modal.msgError('查询失败:' + (e.message || '未知错误'))
} finally {
this.queryLoading = false
}
},
/** 选择商品(单选,保留兼容性) */
selectProduct(product) {
this.form.selectedProduct = product
this.form.queryResult = product
this.form.skuName = product.skuName || product.title || product.productName || product.cleanSkuName || ''
const ownerValue = product.owner || (product.popId ? 'pop' : 'g') || 'g'
this.form.owner = ownerValue === 'p' ? 'pop' : (ownerValue === 'pop' ? 'pop' : 'g')
this.$modal.msgSuccess('已选择商品:' + this.form.skuName + '' + (this.form.owner === 'g' ? '自营' : 'POP') + '')
},
/** 表格多选变化 */
handleSelectionChange(selection) {
this.selectedProducts = selection
// 如果只选中一个同步到selectedProduct兼容性
if (selection.length === 1) {
this.form.selectedProduct = selection[0]
this.form.queryResult = selection[0]
} else {
this.form.selectedProduct = null
this.form.queryResult = null
}
},
/** 全选商品 */
selectAllProducts() {
this.$nextTick(() => {
this.productList.forEach(row => {
this.$refs.productTable && this.$refs.productTable.toggleRowSelection(row, true)
})
})
},
/** 清空选择 */
clearSelection() {
this.$refs.productTable && this.$refs.productTable.clearSelection()
},
/** 提交选中的商品并批量创建 */
async submitSelectedProducts() {
if (this.selectedProducts.length === 0) {
this.$modal.msgWarning('请至少选择一个商品')
return
}
if (!this.$refs.form) {
return
}
this.$refs.form.validate(async (valid) => {
if (!valid) return
if (this.detectedUrls.length === 0) {
this.$modal.msgWarning('文本中未找到URL无需替换。请在文本框中输入包含京东商品链接的内容。')
return
}
if (this.detectedUrls.length > 100) {
this.$modal.msgError('检测到的URL数量超过100个请分批处理')
return
}
// 确认操作
try {
await this.$confirm(
`已选择 ${this.selectedProducts.length} 个商品,文本中检测到 ${this.detectedUrls.length} 个URL将批量创建 ${this.detectedUrls.length}${this.form.amount} 元礼金券并自动替换文本中的URL为包含礼金的推广链接。是否继续`,
'确认批量创建',
{
confirmButtonText: '确定创建',
cancelButtonText: '取消',
type: 'warning'
}
)
} catch {
return
}
// 为每个选中的商品分别处理(这里简化处理,使用第一个选中的商品)
// 实际业务中可能需要为每个商品创建对应的礼金券
const selectedProduct = this.selectedProducts[0]
this.form.selectedProduct = selectedProduct
this.form.queryResult = selectedProduct
this.form.skuName = selectedProduct.skuName || selectedProduct.title || selectedProduct.productName || selectedProduct.cleanSkuName || ''
const ownerValue = selectedProduct.owner || (selectedProduct.popId ? 'pop' : 'g') || 'g'
this.form.owner = ownerValue === 'p' ? 'pop' : (ownerValue === 'pop' ? 'pop' : 'g')
// 调用批量创建和替换
await this.handleReplace()
})
},
/** 格式化价格 */
formatPrice(price) {
if (!price) return '0.00'
// 如果价格是分为单位,转换为元
if (typeof price === 'number' && price > 1000) {
return (price / 100).toFixed(2)
}
return Number(price).toFixed(2)
},
/** 格式化佣金金额 */
formatCommissionAmount(commission, price) {
if (!commission || !price) return '0.00'
// commission可能是百分比字符串或数值
let commissionPercent = 0
if (typeof commission === 'string') {
commissionPercent = parseFloat(commission.replace('%', '')) || 0
} else {
commissionPercent = commission
}
// price可能是分为单位需要转换
let priceValue = price
if (typeof price === 'number' && price > 1000) {
priceValue = price / 100
} else {
priceValue = parseFloat(price) || 0
}
const commissionAmount = (priceValue * commissionPercent / 100)
return commissionAmount.toFixed(2)
},
/** 检测文本中的URL */
detectUrls(text) {
if (!text || text.trim().length === 0) {
@@ -585,8 +174,8 @@ export default {
this.detectedUrls = urls
},
/** 批量创建并替换 */
async handleReplace() {
/** 一键处理 */
async handleProcess() {
if (!this.$refs.form) {
return
}
@@ -594,13 +183,8 @@ export default {
this.$refs.form.validate(async (valid) => {
if (!valid) return
if (!this.form.selectedProduct || !this.form.queryResult) {
this.$modal.msgWarning('请先查询商品信息并选择一个商品')
return
}
if (this.detectedUrls.length === 0) {
this.$modal.msgWarning('文本中未找到URL无需替换。请在文本框中输入包含京东商品链接的内容。')
this.$modal.msgWarning('文本中未找到URL输入包含京东商品链接的文案')
return
}
@@ -612,7 +196,7 @@ export default {
// 确认操作
try {
await this.$confirm(
`检测到 ${this.detectedUrls.length} 个商品URL批量创建 ${this.detectedUrls.length}${this.form.amount} 元礼金券并自动替换文中的URL为包含礼金的推广链接。是否继续?`,
`检测到 ${this.detectedUrls.length} 个商品链接,将自动批量创建 ${this.detectedUrls.length}${this.form.amount} 元礼金券并替换文中的URL。是否继续`,
'确认批量创建',
{
confirmButtonText: '确定创建',
@@ -627,81 +211,105 @@ export default {
this.processing = true
this.progress = 0
this.progressText = '准备中...'
this.progressDetail = ''
this.progressDetail = '正在从URL提取商品信息...'
this.progressStatus = ''
this.result = null
try {
// 构建请求参数
// 注意batchSize 会自动根据文本中解析出的URL数量设置
const params = {
content: this.content,
amount: this.form.amount,
quantity: this.form.quantity,
// batchSize 会在后端根据content中解析出的URL数量自动计算这里不需要传
owner: this.form.owner || 'g',
skuName: this.form.skuName || ''
}
// 使用第一个URL查询商品信息
const firstUrl = this.detectedUrls[0]
this.progress = 10
this.progressText = '正在查询商品信息...'
this.progressDetail = `正在查询商品: ${firstUrl.substring(0, 50)}...`
// 根据查询结果设置skuId或materialUrl
if (this.form.queryResult) {
const queryResult = this.form.queryResult
const isPop = this.form.owner === 'pop'
const queryRes = await generatePromotionContent({ promotionContent: firstUrl })
if (queryRes && queryRes.code === 200) {
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) {
this.$modal.msgError('无法从URL中提取商品信息请检查链接是否正确')
this.processing = false
return
}
this.progress = 30
this.progressText = '正在批量创建礼金券...'
this.progressDetail = `准备创建 ${this.detectedUrls.length} 张礼金券,请耐心等待...`
// 构建请求参数
const params = {
content: this.content,
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 (queryResult.materialUrl) {
params.materialUrl = queryResult.materialUrl
} else if ((queryResult.skuId || queryResult.skuid) && /^\d+$/.test(String(queryResult.skuId || queryResult.skuid))) {
params.skuId = String(queryResult.skuId || queryResult.skuid)
if (product.materialUrl) {
params.materialUrl = product.materialUrl
} else if ((product.skuId || product.skuid) && /^\d+$/.test(String(product.skuId || product.skuid))) {
params.skuId = String(product.skuId || product.skuid)
}
} else {
const skuIdValue = queryResult.skuId || queryResult.skuid
const skuIdValue = product.skuId || product.skuid
if (skuIdValue && /^\d+$/.test(String(skuIdValue))) {
params.skuId = String(skuIdValue)
} else if (queryResult.materialUrl) {
params.materialUrl = queryResult.materialUrl
} else if (product.materialUrl) {
params.materialUrl = product.materialUrl
}
}
}
// 如果没有设置skuId或materialUrl使用用户输入的
if (!params.skuId && !params.materialUrl) {
const materialUrl = this.form.materialUrl.trim()
if (materialUrl) {
if (/^\d+$/.test(materialUrl)) {
params.skuId = materialUrl
} else {
params.materialUrl = materialUrl
}
}
}
console.log('批量替换请求参数:', params)
// 调用替换接口
this.progress = 30
this.progressText = '正在批量创建礼金券...'
this.progressDetail = '这可能需要几分钟时间,请耐心等待'
const res = await replaceUrlsWithGiftCoupons(params)
this.progress = 80
this.progressText = '正在处理结果...'
if (res && res.code === 200 && res.data) {
this.result = res.data
this.progress = 100
this.progressStatus = 'success'
this.progressText = '完成!'
this.progressDetail = `成功替换 ${this.result.replacedCount || 0} / ${this.result.totalUrls || 0} 个URL`
this.$modal.msgSuccess(`批量替换完成!成功 ${this.result.replacedCount || 0} / ${this.result.totalUrls || 0}`)
console.log('批量替换请求参数:', params)
// 调用替换接口
this.progress = 40
const res = await replaceUrlsWithGiftCoupons(params)
this.progress = 80
this.progressText = '正在处理结果...'
if (res && res.code === 200 && res.data) {
this.result = res.data
this.progress = 100
this.progressStatus = 'success'
this.progressText = '完成!'
this.progressDetail = `成功替换 ${this.result.replacedCount || 0} / ${this.result.totalUrls || 0} 个URL`
this.$modal.msgSuccess(`✅ 批量替换完成!成功 ${this.result.replacedCount || 0} / ${this.result.totalUrls || 0}`)
} else {
this.progress = 100
this.progressStatus = 'exception'
this.progressText = '失败'
this.progressDetail = res.msg || '未知错误'
this.$modal.msgError('批量替换失败:' + (res.msg || '未知错误'))
}
} else {
this.progress = 100
this.progressStatus = 'exception'
this.progressText = '失败'
this.progressDetail = res.msg || '未知错误'
this.$modal.msgError('批量替换失败:' + (res.msg || '未知错误'))
this.$modal.msgError('查询商品信息失败:' + (queryRes.msg || '未知错误'))
}
} catch (e) {
console.error('批量替换异常', e)
@@ -717,7 +325,7 @@ export default {
errorMsg = e.message
}
this.$modal.msgError('批量替换失败:' + errorMsg)
this.$modal.msgError('操作失败:' + errorMsg)
} finally {
this.processing = false
}
@@ -731,13 +339,6 @@ export default {
}
},
/** 文本框获得焦点 */
handleTextareaFocus() {
if (this.selectedProducts.length === 0) {
this.$modal.msgWarning('请先查询商品并选择要推广的商品')
}
},
/** 复制到剪贴板 */
copyToClipboard(text) {
if (navigator.clipboard && navigator.clipboard.writeText) {
@@ -761,7 +362,7 @@ export default {
textArea.select()
try {
document.execCommand('copy')
this.$modal.msgSuccess('复制成功')
this.$modal.msgSuccess('复制成功')
} catch (err) {
this.$modal.msgError('复制失败')
}
@@ -776,12 +377,59 @@ export default {
padding: 20px;
}
.el-divider {
margin: 10px 0;
.text-panel {
border: 2px solid #DCDFE6;
border-radius: 8px;
overflow: hidden;
}
.el-statistic {
text-align: center;
.panel-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.textarea-input, .textarea-output {
border: none !important;
border-radius: 0 !important;
}
.textarea-input >>> textarea {
border: none !important;
border-radius: 0 !important;
font-size: 14px;
line-height: 1.8;
padding: 20px;
}
.textarea-output >>> textarea {
border: none !important;
border-radius: 0 !important;
font-size: 14px;
line-height: 1.8;
padding: 20px;
background: #f5f7fa;
}
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 550px;
padding: 40px;
background: #f5f7fa;
}
.empty-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 550px;
background: #fafafa;
}
</style>