1
This commit is contained in:
45
src/api/jarvis/batchPublish.js
Normal file
45
src/api/jarvis/batchPublish.js
Normal file
@@ -0,0 +1,45 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 解析线报消息
|
||||
export function parseLineReport(data) {
|
||||
return request({
|
||||
url: '/jarvis/batchPublish/parse',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 批量发品
|
||||
export function batchPublish(data) {
|
||||
return request({
|
||||
url: '/jarvis/batchPublish/publish',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 查询批量发品任务列表
|
||||
export function listTasks(query) {
|
||||
return request({
|
||||
url: '/jarvis/batchPublish/task/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询批量发品任务详情
|
||||
export function getTask(taskId) {
|
||||
return request({
|
||||
url: '/jarvis/batchPublish/task/' + taskId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询批量发品明细列表
|
||||
export function listItems(taskId) {
|
||||
return request({
|
||||
url: '/jarvis/batchPublish/item/list/' + taskId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -204,6 +204,22 @@ export const dynamicRoutes = [
|
||||
}
|
||||
]
|
||||
},
|
||||
// 批量发品
|
||||
{
|
||||
path: '/batchPublish',
|
||||
component: Layout,
|
||||
redirect: 'noredirect',
|
||||
name: 'BatchPublish',
|
||||
meta: { title: '批量发品', icon: 'shopping' },
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
component: () => import('@/views/jarvis/batchPublish/index'),
|
||||
name: 'BatchPublishIndex',
|
||||
meta: { title: '批量发品', icon: 'upload' }
|
||||
}
|
||||
]
|
||||
},
|
||||
// 线报群管理
|
||||
{
|
||||
path: '/xbgroup',
|
||||
|
||||
756
src/views/jarvis/batchPublish/index.vue
Normal file
756
src/views/jarvis/batchPublish/index.vue
Normal file
@@ -0,0 +1,756 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-card class="box-card" shadow="hover">
|
||||
<div slot="header" class="clearfix">
|
||||
<span style="font-weight: bold; font-size: 16px;">📦 线报批量发品</span>
|
||||
<el-button style="float: right; padding: 3px 10px" type="text" @click="showHistory">历史记录</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 步骤条 -->
|
||||
<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-steps>
|
||||
|
||||
<!-- 第一步:输入线报消息 -->
|
||||
<div v-show="activeStep === 0" class="step-content">
|
||||
<el-form label-width="100px">
|
||||
<el-form-item label="线报消息">
|
||||
<el-input
|
||||
type="textarea"
|
||||
v-model="lineReportMessage"
|
||||
:rows="12"
|
||||
placeholder="请粘贴线报消息内容,支持多个商品链接..."
|
||||
@input="onMessageInput"
|
||||
/>
|
||||
<div style="margin-top: 10px; color: #909399; font-size: 12px;">
|
||||
<i class="el-icon-info"></i> 支持京东商品链接、SKUID等多种格式,系统会自动识别并提取商品信息
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="parsing" @click="parseMessage" :disabled="!lineReportMessage.trim()">
|
||||
解析商品 <i class="el-icon-arrow-right"></i>
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 第二步:选择商品 -->
|
||||
<div v-show="activeStep === 1" class="step-content">
|
||||
<div style="margin-bottom: 15px; display: flex; justify-content: space-between; align-items: center;">
|
||||
<div>
|
||||
<el-button type="primary" size="small" @click="selectAll">全选</el-button>
|
||||
<el-button size="small" @click="selectNone">取消全选</el-button>
|
||||
<el-tag type="info" size="small" style="margin-left: 10px;">
|
||||
已选择: {{ selectedProducts.length }} / {{ parsedProducts.length }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<el-button type="success" @click="nextStep" :disabled="selectedProducts.length === 0">
|
||||
下一步 <i class="el-icon-arrow-right"></i>
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
:data="parsedProducts"
|
||||
border
|
||||
style="width: 100%"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center"></el-table-column>
|
||||
<el-table-column label="商品图片" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-image
|
||||
v-if="scope.row.productImage"
|
||||
:src="scope.row.productImage"
|
||||
:preview-src-list="[scope.row.productImage]"
|
||||
style="width: 60px; height: 60px; border-radius: 4px;"
|
||||
fit="cover"
|
||||
/>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="商品信息" min-width="300">
|
||||
<template slot-scope="scope">
|
||||
<div style="font-weight: bold; margin-bottom: 5px;">{{ scope.row.productName }}</div>
|
||||
<div style="color: #666; font-size: 12px;">
|
||||
<div>SKUID: {{ scope.row.skuid }}</div>
|
||||
<div v-if="scope.row.shopName">店铺: {{ scope.row.shopName }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="价格" width="120" align="center">
|
||||
<template slot-scope="scope">
|
||||
<div style="color: #f56c6c; font-weight: bold; font-size: 16px;">¥{{ scope.row.price }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="佣金" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.commissionInfo" type="success" size="small">
|
||||
{{ scope.row.commissionInfo }}
|
||||
</el-tag>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div style="margin-top: 15px;">
|
||||
<el-button @click="prevStep">
|
||||
<i class="el-icon-arrow-left"></i> 上一步
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第三步:设置参数 -->
|
||||
<div v-show="activeStep === 2" class="step-content">
|
||||
<el-form :model="publishForm" :rules="publishRules" ref="publishForm" label-width="120px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="任务名称" prop="taskName">
|
||||
<el-input v-model="publishForm.taskName" placeholder="请输入任务名称(选填)"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="延迟上架" prop="delaySeconds">
|
||||
<el-input-number v-model="publishForm.delaySeconds" :min="1" :max="60" placeholder="秒"/>
|
||||
<span style="margin-left: 10px; color: #909399;">发品成功后延迟上架</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="目标账号" prop="targetAccounts" required>
|
||||
<el-select v-model="publishForm.targetAccounts" multiple placeholder="请选择目标ERP账号" style="width: 100%;">
|
||||
<el-option
|
||||
v-for="account in erpAccounts"
|
||||
:key="account.value"
|
||||
:label="account.label"
|
||||
:value="account.value"
|
||||
/>
|
||||
</el-select>
|
||||
<div style="margin-top: 5px; color: #909399; font-size: 12px;">
|
||||
<i class="el-icon-info"></i> 支持选择多个账号,每个商品将发送到所有选中的账号
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-divider content-position="left">通用参数</el-divider>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="会员名" prop="userName" required>
|
||||
<el-input v-model="publishForm.userName" placeholder="请输入会员名"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="商品类型" prop="itemBizType" required>
|
||||
<el-select v-model="publishForm.itemBizType" placeholder="请选择" style="width: 100%;">
|
||||
<el-option label="普通商品" :value="2"/>
|
||||
<el-option label="已验货" :value="0"/>
|
||||
<el-option label="验货宝" :value="10"/>
|
||||
<el-option label="闲鱼优品" :value="19"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="行业类型" prop="spBizType" required>
|
||||
<el-select v-model="publishForm.spBizType" placeholder="请选择" style="width: 100%;">
|
||||
<el-option label="手机" :value="1"/>
|
||||
<el-option label="时尚" :value="2"/>
|
||||
<el-option label="家电" :value="3"/>
|
||||
<el-option label="数码3C" :value="9"/>
|
||||
<el-option label="母婴" :value="17"/>
|
||||
<el-option label="美妆" :value="18"/>
|
||||
<el-option label="家居" :value="21"/>
|
||||
<el-option label="其他" :value="99"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="类目ID" prop="channelCatId" required>
|
||||
<el-input v-model="publishForm.channelCatId" placeholder="请输入类目ID"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="省" prop="province" required>
|
||||
<el-input-number v-model="publishForm.province" placeholder="省代码" style="width: 100%;"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="市" prop="city" required>
|
||||
<el-input-number v-model="publishForm.city" placeholder="市代码" style="width: 100%;"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="区" prop="district" required>
|
||||
<el-input-number v-model="publishForm.district" placeholder="区代码" style="width: 100%;"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="邮费" prop="expressFee" required>
|
||||
<el-input-number v-model="publishForm.expressFee" :min="0" :precision="2" placeholder="元" style="width: 100%;"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="库存" prop="stock" required>
|
||||
<el-input-number v-model="publishForm.stock" :min="1" placeholder="库存数量" style="width: 100%;"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="成色" prop="stuffStatus">
|
||||
<el-select v-model="publishForm.stuffStatus" placeholder="请选择" clearable style="width: 100%;">
|
||||
<el-option label="全新" :value="100"/>
|
||||
<el-option label="99新" :value="99"/>
|
||||
<el-option label="95新" :value="95"/>
|
||||
<el-option label="9成新" :value="90"/>
|
||||
<el-option label="8成新" :value="80"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="服务支持" prop="serviceSupport">
|
||||
<el-select v-model="publishForm.serviceSupport" multiple placeholder="请选择服务支持" style="width: 100%;">
|
||||
<el-option label="七天无理由退货" value="SDR"/>
|
||||
<el-option label="描述不符包邮退" value="NFR"/>
|
||||
<el-option label="24小时极速发货" value="FD_24HS"/>
|
||||
<el-option label="48小时极速发货" value="FD_48HS"/>
|
||||
<el-option label="正品保障" value="FD_GPA"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<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>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 第四步:发品进度 -->
|
||||
<div v-show="activeStep === 3" class="step-content">
|
||||
<el-alert
|
||||
v-if="publishResult.taskId"
|
||||
title="批量发品任务已创建"
|
||||
type="success"
|
||||
:description="`任务ID: ${publishResult.taskId}`"
|
||||
show-icon
|
||||
:closable="false"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
|
||||
<el-progress
|
||||
:percentage="publishProgress"
|
||||
:status="publishProgress === 100 ? 'success' : null"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
|
||||
<el-table :data="publishItems" border style="width: 100%" v-if="publishItems.length > 0">
|
||||
<el-table-column label="商品" prop="productName" min-width="200"/>
|
||||
<el-table-column label="账号" prop="accountRemark" width="120"/>
|
||||
<el-table-column label="状态" width="120" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="getStatusType(scope.row.status)" size="small">
|
||||
{{ getStatusText(scope.row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="商品ID" prop="productId" width="150"/>
|
||||
<el-table-column label="错误信息" prop="errorMessage" min-width="200" :show-overflow-tooltip="true"/>
|
||||
</el-table>
|
||||
|
||||
<div style="margin-top: 20px; text-align: center;">
|
||||
<el-button type="primary" @click="reset">创建新任务</el-button>
|
||||
<el-button @click="showHistory">查看历史记录</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 历史记录对话框 -->
|
||||
<el-dialog title="批量发品历史记录" :visible.sync="historyVisible" width="80%" append-to-body>
|
||||
<el-table :data="historyList" border style="width: 100%">
|
||||
<el-table-column label="任务ID" prop="id" width="80"/>
|
||||
<el-table-column label="任务名称" prop="taskName" min-width="150"/>
|
||||
<el-table-column label="商品数" prop="selectedProducts" width="80" align="center"/>
|
||||
<el-table-column label="目标账号数" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
{{ parseTargetAccounts(scope.row.targetAccounts).length }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="getTaskStatusType(scope.row.status)">
|
||||
{{ getTaskStatusText(scope.row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="成功/失败" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span style="color: #67c23a;">{{ scope.row.successCount }}</span> /
|
||||
<span style="color: #f56c6c;">{{ scope.row.failCount }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" prop="createTime" width="160"/>
|
||||
<el-table-column label="操作" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="small" @click="viewTaskDetail(scope.row)">查看详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="historyTotal>0"
|
||||
:total="historyTotal"
|
||||
:page.sync="historyQuery.pageNum"
|
||||
:limit.sync="historyQuery.pageSize"
|
||||
@pagination="loadHistory"
|
||||
/>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 任务详情对话框 -->
|
||||
<el-dialog title="任务详情" :visible.sync="detailVisible" width="80%" append-to-body>
|
||||
<el-descriptions v-if="currentTask" :column="3" border>
|
||||
<el-descriptions-item label="任务ID">{{ currentTask.id }}</el-descriptions-item>
|
||||
<el-descriptions-item label="任务名称">{{ currentTask.taskName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="状态">
|
||||
<el-tag :type="getTaskStatusType(currentTask.status)">
|
||||
{{ getTaskStatusText(currentTask.status) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="商品数">{{ currentTask.selectedProducts }}</el-descriptions-item>
|
||||
<el-descriptions-item label="成功数">{{ currentTask.successCount }}</el-descriptions-item>
|
||||
<el-descriptions-item label="失败数">{{ currentTask.failCount }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建人">{{ currentTask.createUserName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间" :span="2">{{ currentTask.createTime }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<el-divider>发品明细</el-divider>
|
||||
|
||||
<el-table :data="taskItems" border style="width: 100%">
|
||||
<el-table-column label="商品" prop="productName" min-width="200"/>
|
||||
<el-table-column label="SKUID" prop="skuid" width="120"/>
|
||||
<el-table-column label="账号" prop="accountRemark" width="120"/>
|
||||
<el-table-column label="状态" width="120" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="getStatusType(scope.row.status)" size="small">
|
||||
{{ getStatusText(scope.row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="商品ID" prop="productId" width="150"/>
|
||||
<el-table-column label="价格" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.publishPrice">¥{{ (scope.row.publishPrice / 100).toFixed(2) }}</span>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="错误信息" prop="errorMessage" min-width="200" :show-overflow-tooltip="true"/>
|
||||
</el-table>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { parseLineReport, batchPublish, listTasks, getTask, listItems } from "@/api/jarvis/batchPublish";
|
||||
import { getERPAccounts } from "@/api/system/jdorder";
|
||||
import Pagination from "@/components/Pagination";
|
||||
|
||||
export default {
|
||||
name: "BatchPublish",
|
||||
components: { Pagination },
|
||||
data() {
|
||||
return {
|
||||
// 步骤
|
||||
activeStep: 0,
|
||||
|
||||
// 输入
|
||||
lineReportMessage: "",
|
||||
parsing: false,
|
||||
|
||||
// 商品列表
|
||||
parsedProducts: [],
|
||||
selectedProducts: [],
|
||||
|
||||
// 发品表单
|
||||
publishForm: {
|
||||
taskName: "",
|
||||
targetAccounts: [],
|
||||
delaySeconds: 3,
|
||||
userName: "",
|
||||
province: null,
|
||||
city: null,
|
||||
district: null,
|
||||
itemBizType: 2,
|
||||
spBizType: 3,
|
||||
channelCatId: "",
|
||||
expressFee: 0,
|
||||
stock: 1,
|
||||
stuffStatus: 100,
|
||||
serviceSupport: [],
|
||||
channelPv: ""
|
||||
},
|
||||
publishRules: {
|
||||
targetAccounts: [{ required: true, message: "请选择目标账号", trigger: "change" }],
|
||||
userName: [{ required: true, message: "请输入会员名", trigger: "blur" }],
|
||||
province: [{ required: true, message: "请输入省代码", trigger: "blur" }],
|
||||
city: [{ required: true, message: "请输入市代码", trigger: "blur" }],
|
||||
district: [{ required: true, message: "请输入区代码", trigger: "blur" }],
|
||||
itemBizType: [{ required: true, message: "请选择商品类型", trigger: "change" }],
|
||||
spBizType: [{ required: true, message: "请选择行业类型", trigger: "change" }],
|
||||
channelCatId: [{ required: true, message: "请输入类目ID", trigger: "blur" }],
|
||||
expressFee: [{ required: true, message: "请输入邮费", trigger: "blur" }],
|
||||
stock: [{ required: true, message: "请输入库存", trigger: "blur" }]
|
||||
},
|
||||
publishing: false,
|
||||
|
||||
// ERP账号
|
||||
erpAccounts: [],
|
||||
|
||||
// 发品结果
|
||||
publishResult: {
|
||||
taskId: null
|
||||
},
|
||||
publishProgress: 0,
|
||||
publishItems: [],
|
||||
refreshTimer: null,
|
||||
|
||||
// 历史记录
|
||||
historyVisible: false,
|
||||
historyList: [],
|
||||
historyTotal: 0,
|
||||
historyQuery: {
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
},
|
||||
|
||||
// 任务详情
|
||||
detailVisible: false,
|
||||
currentTask: null,
|
||||
taskItems: []
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.loadERPAccounts();
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.refreshTimer) {
|
||||
clearInterval(this.refreshTimer);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 加载ERP账号
|
||||
async loadERPAccounts() {
|
||||
try {
|
||||
const res = await getERPAccounts();
|
||||
if (res.code === 200) {
|
||||
this.erpAccounts = res.data || [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("加载ERP账号失败", error);
|
||||
}
|
||||
},
|
||||
|
||||
// 输入变化
|
||||
onMessageInput() {
|
||||
// 可以添加实时提示
|
||||
},
|
||||
|
||||
// 解析消息
|
||||
async parseMessage() {
|
||||
if (!this.lineReportMessage.trim()) {
|
||||
this.$modal.msgWarning("请输入线报消息");
|
||||
return;
|
||||
}
|
||||
|
||||
this.parsing = true;
|
||||
try {
|
||||
const res = await parseLineReport({ message: this.lineReportMessage });
|
||||
if (res.code === 200) {
|
||||
this.parsedProducts = res.data || [];
|
||||
if (this.parsedProducts.length === 0) {
|
||||
this.$modal.msgWarning("未能识别到商品信息,请检查消息内容");
|
||||
} else {
|
||||
this.$modal.msgSuccess(`成功解析 ${this.parsedProducts.length} 个商品`);
|
||||
this.activeStep = 1;
|
||||
}
|
||||
} else {
|
||||
this.$modal.msgError(res.msg || "解析失败");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("解析失败", error);
|
||||
this.$modal.msgError("解析失败,请稍后重试");
|
||||
} finally {
|
||||
this.parsing = false;
|
||||
}
|
||||
},
|
||||
|
||||
// 选择变化
|
||||
handleSelectionChange(selection) {
|
||||
this.selectedProducts = selection;
|
||||
},
|
||||
|
||||
// 全选
|
||||
selectAll() {
|
||||
this.$refs.productTable && this.$refs.productTable.toggleAllSelection();
|
||||
},
|
||||
|
||||
// 取消全选
|
||||
selectNone() {
|
||||
this.$refs.productTable && this.$refs.productTable.clearSelection();
|
||||
},
|
||||
|
||||
// 下一步
|
||||
nextStep() {
|
||||
if (this.activeStep === 1 && this.selectedProducts.length === 0) {
|
||||
this.$modal.msgWarning("请至少选择一个商品");
|
||||
return;
|
||||
}
|
||||
this.activeStep++;
|
||||
},
|
||||
|
||||
// 上一步
|
||||
prevStep() {
|
||||
this.activeStep--;
|
||||
},
|
||||
|
||||
// 提交发品
|
||||
submitPublish() {
|
||||
this.$refs.publishForm.validate(async (valid) => {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.publishForm.targetAccounts.length === 0) {
|
||||
this.$modal.msgWarning("请至少选择一个目标账号");
|
||||
return;
|
||||
}
|
||||
|
||||
this.publishing = true;
|
||||
try {
|
||||
const request = {
|
||||
taskName: this.publishForm.taskName || `批量发品-${new Date().toLocaleString()}`,
|
||||
originalMessage: this.lineReportMessage,
|
||||
products: this.selectedProducts.map(p => ({
|
||||
skuid: p.skuid,
|
||||
productName: p.productName,
|
||||
price: p.price,
|
||||
productImage: p.productImage,
|
||||
shopName: p.shopName,
|
||||
shopId: p.shopId,
|
||||
commissionInfo: p.commissionInfo
|
||||
})),
|
||||
targetAccounts: this.publishForm.targetAccounts,
|
||||
delaySeconds: this.publishForm.delaySeconds,
|
||||
commonParams: {
|
||||
userName: this.publishForm.userName,
|
||||
province: this.publishForm.province,
|
||||
city: this.publishForm.city,
|
||||
district: this.publishForm.district,
|
||||
itemBizType: this.publishForm.itemBizType,
|
||||
spBizType: this.publishForm.spBizType,
|
||||
channelCatId: this.publishForm.channelCatId,
|
||||
expressFee: this.publishForm.expressFee,
|
||||
stock: this.publishForm.stock,
|
||||
stuffStatus: this.publishForm.stuffStatus,
|
||||
serviceSupport: this.publishForm.serviceSupport.join(","),
|
||||
channelPv: this.publishForm.channelPv
|
||||
}
|
||||
};
|
||||
|
||||
const res = await batchPublish(request);
|
||||
if (res.code === 200) {
|
||||
this.publishResult.taskId = res.data;
|
||||
this.$modal.msgSuccess("批量发品任务已创建");
|
||||
this.activeStep = 3;
|
||||
this.startRefreshProgress();
|
||||
} else {
|
||||
this.$modal.msgError(res.msg || "提交失败");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("提交失败", error);
|
||||
this.$modal.msgError("提交失败,请稍后重试");
|
||||
} finally {
|
||||
this.publishing = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 开始刷新进度
|
||||
startRefreshProgress() {
|
||||
this.refreshProgress();
|
||||
this.refreshTimer = setInterval(() => {
|
||||
this.refreshProgress();
|
||||
}, 2000);
|
||||
},
|
||||
|
||||
// 刷新进度
|
||||
async refreshProgress() {
|
||||
if (!this.publishResult.taskId) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await listItems(this.publishResult.taskId);
|
||||
if (res.code === 200) {
|
||||
this.publishItems = res.data || [];
|
||||
|
||||
// 计算进度
|
||||
const total = this.publishItems.length;
|
||||
const finished = this.publishItems.filter(item => item.status >= 2).length;
|
||||
this.publishProgress = total > 0 ? Math.round((finished / total) * 100) : 0;
|
||||
|
||||
// 如果全部完成,停止刷新
|
||||
if (finished === total) {
|
||||
if (this.refreshTimer) {
|
||||
clearInterval(this.refreshTimer);
|
||||
this.refreshTimer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("刷新进度失败", error);
|
||||
}
|
||||
},
|
||||
|
||||
// 重置
|
||||
reset() {
|
||||
this.activeStep = 0;
|
||||
this.lineReportMessage = "";
|
||||
this.parsedProducts = [];
|
||||
this.selectedProducts = [];
|
||||
this.publishResult = { taskId: null };
|
||||
this.publishProgress = 0;
|
||||
this.publishItems = [];
|
||||
if (this.refreshTimer) {
|
||||
clearInterval(this.refreshTimer);
|
||||
this.refreshTimer = null;
|
||||
}
|
||||
},
|
||||
|
||||
// 显示历史记录
|
||||
async showHistory() {
|
||||
this.historyVisible = true;
|
||||
await this.loadHistory();
|
||||
},
|
||||
|
||||
// 加载历史记录
|
||||
async loadHistory() {
|
||||
try {
|
||||
const res = await listTasks(this.historyQuery);
|
||||
if (res.code === 200) {
|
||||
this.historyList = res.rows || [];
|
||||
this.historyTotal = res.total || 0;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("加载历史记录失败", error);
|
||||
}
|
||||
},
|
||||
|
||||
// 查看任务详情
|
||||
async viewTaskDetail(task) {
|
||||
try {
|
||||
const res1 = await getTask(task.id);
|
||||
if (res1.code === 200) {
|
||||
this.currentTask = res1.data;
|
||||
}
|
||||
|
||||
const res2 = await listItems(task.id);
|
||||
if (res2.code === 200) {
|
||||
this.taskItems = res2.data || [];
|
||||
}
|
||||
|
||||
this.detailVisible = true;
|
||||
} catch (error) {
|
||||
console.error("加载任务详情失败", error);
|
||||
this.$modal.msgError("加载任务详情失败");
|
||||
}
|
||||
},
|
||||
|
||||
// 解析目标账号
|
||||
parseTargetAccounts(jsonStr) {
|
||||
try {
|
||||
return JSON.parse(jsonStr) || [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
|
||||
// 状态相关
|
||||
getStatusType(status) {
|
||||
const map = {
|
||||
0: "info",
|
||||
1: "warning",
|
||||
2: "success",
|
||||
3: "danger",
|
||||
4: "warning",
|
||||
5: "success",
|
||||
6: "danger"
|
||||
};
|
||||
return map[status] || "info";
|
||||
},
|
||||
|
||||
getStatusText(status) {
|
||||
const map = {
|
||||
0: "待发布",
|
||||
1: "发布中",
|
||||
2: "发布成功",
|
||||
3: "发布失败",
|
||||
4: "上架中",
|
||||
5: "已上架",
|
||||
6: "上架失败"
|
||||
};
|
||||
return map[status] || "未知";
|
||||
},
|
||||
|
||||
getTaskStatusType(status) {
|
||||
const map = {
|
||||
0: "info",
|
||||
1: "warning",
|
||||
2: "success",
|
||||
3: "danger"
|
||||
};
|
||||
return map[status] || "info";
|
||||
},
|
||||
|
||||
getTaskStatusText(status) {
|
||||
const map = {
|
||||
0: "待处理",
|
||||
1: "处理中",
|
||||
2: "已完成",
|
||||
3: "失败"
|
||||
};
|
||||
return map[status] || "未知";
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.step-content {
|
||||
min-height: 400px;
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.clearfix:before,
|
||||
.clearfix:after {
|
||||
display: table;
|
||||
content: "";
|
||||
}
|
||||
|
||||
.clearfix:after {
|
||||
clear: both;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -156,6 +156,10 @@ export default {
|
||||
if (Array.isArray(data)) this.resultList = data
|
||||
else if (typeof data === 'string') this.resultList = data ? [data] : []
|
||||
else this.resultList = []
|
||||
|
||||
// 检查是否有以[炸弹]开头的消息
|
||||
this.checkBombAlert(this.resultList)
|
||||
|
||||
// 执行成功后刷新历史记录
|
||||
this.loadHistory()
|
||||
} else {
|
||||
@@ -251,6 +255,9 @@ export default {
|
||||
this.$message.success('已查询到今天的慢单数据')
|
||||
}
|
||||
|
||||
// 检查是否有以[炸弹]开头的消息
|
||||
this.checkBombAlert(this.resultList)
|
||||
|
||||
// 执行成功后刷新历史记录
|
||||
this.loadHistory()
|
||||
} else {
|
||||
@@ -266,6 +273,39 @@ export default {
|
||||
clearAll() {
|
||||
this.form.command = ''
|
||||
this.resultList = []
|
||||
},
|
||||
checkBombAlert(resultList) {
|
||||
if (!resultList || resultList.length === 0) return
|
||||
|
||||
// 检查是否有以[炸弹]开头的消息
|
||||
const bombMessages = resultList
|
||||
.filter(msg => {
|
||||
return msg && typeof msg === 'string' && msg.trim().startsWith('[炸弹]')
|
||||
})
|
||||
.map(msg => {
|
||||
// 移除所有的[炸弹]标记
|
||||
return msg.trim().replace(/\[炸弹\]\s*/g, '').trim()
|
||||
})
|
||||
|
||||
if (bombMessages.length > 0) {
|
||||
// 显示全屏警告弹窗
|
||||
this.$alert(bombMessages.join('\n\n'), '⚠️ 警告提示', {
|
||||
confirmButtonText: '我已知晓',
|
||||
type: 'warning',
|
||||
center: true,
|
||||
customClass: 'bomb-alert-dialog',
|
||||
showClose: false,
|
||||
closeOnClickModal: false,
|
||||
closeOnPressEscape: false,
|
||||
dangerouslyUseHTMLString: false
|
||||
}).catch(() => {})
|
||||
}
|
||||
},
|
||||
copyHistoryItem(item) {
|
||||
const message = this.extractMessage(item)
|
||||
if (message) {
|
||||
this.doCopy(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -363,6 +403,80 @@ export default {
|
||||
.history-content::-webkit-scrollbar-thumb:hover {
|
||||
background: #C0C4CC;
|
||||
}
|
||||
|
||||
.history-item-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* 炸弹警告弹窗样式 */
|
||||
::v-deep .bomb-alert-dialog {
|
||||
width: 80% !important;
|
||||
max-width: 1920px !important;
|
||||
}
|
||||
|
||||
::v-deep .bomb-alert-dialog .el-message-box__message {
|
||||
font-size: 16px !important;
|
||||
font-weight: 600 !important;
|
||||
color: #E6A23C !important;
|
||||
white-space: pre-wrap !important;
|
||||
word-break: break-word !important;
|
||||
line-height: 1.8 !important;
|
||||
max-height: 60vh !important;
|
||||
overflow-y: auto !important;
|
||||
}
|
||||
|
||||
::v-deep .bomb-alert-dialog .el-message-box__btns {
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
::v-deep .bomb-alert-dialog .el-message-box__btns .el-button {
|
||||
padding: 12px 40px !important;
|
||||
font-size: 16px !important;
|
||||
font-weight: 600 !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
/* 全局样式:炸弹警告弹窗(不使用scoped,因为弹窗挂载在body下) */
|
||||
.bomb-alert-dialog {
|
||||
width: 80vw !important;
|
||||
max-width: 1920px !important;
|
||||
min-width: 500px !important;
|
||||
}
|
||||
|
||||
.bomb-alert-dialog .el-message-box__header {
|
||||
padding: 20px 20px 15px !important;
|
||||
}
|
||||
|
||||
.bomb-alert-dialog .el-message-box__title {
|
||||
font-size: 20px !important;
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
|
||||
.bomb-alert-dialog .el-message-box__message {
|
||||
font-size: 16px !important;
|
||||
font-weight: 600 !important;
|
||||
color: #E6A23C !important;
|
||||
white-space: pre-wrap !important;
|
||||
word-break: break-word !important;
|
||||
line-height: 1.8 !important;
|
||||
max-height: 60vh !important;
|
||||
overflow-y: auto !important;
|
||||
padding: 15px 20px !important;
|
||||
}
|
||||
|
||||
.bomb-alert-dialog .el-message-box__btns {
|
||||
text-align: center !important;
|
||||
padding: 15px 20px 20px !important;
|
||||
}
|
||||
|
||||
.bomb-alert-dialog .el-message-box__btns .el-button {
|
||||
padding: 12px 40px !important;
|
||||
font-size: 16px !important;
|
||||
font-weight: 600 !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user