|
@@ -3,65 +3,80 @@
|
|
<el-card>
|
|
<el-card>
|
|
<div style="padding-left: 5px">
|
|
<div style="padding-left: 5px">
|
|
<!-- 广告活动 -->
|
|
<!-- 广告活动 -->
|
|
- <div style="font-size: 20px; font-weight: bold">广告活动</div>
|
|
|
|
- <hr />
|
|
|
|
- <br />
|
|
|
|
<el-form
|
|
<el-form
|
|
:label-position="labelPosition"
|
|
:label-position="labelPosition"
|
|
- ref="ruleFormRef"
|
|
|
|
- :model="ruleForm"
|
|
|
|
- :rules="rules"
|
|
|
|
|
|
+ ref="campaignFormRef"
|
|
|
|
+ :model="campaignRuleForm"
|
|
|
|
+ :rules="campaignRules"
|
|
label-width="120px"
|
|
label-width="120px"
|
|
class="demo-ruleForm"
|
|
class="demo-ruleForm"
|
|
:size="formSize"
|
|
:size="formSize"
|
|
status-icon>
|
|
status-icon>
|
|
|
|
+ <el-card shadow="never" body-style="padding-bottom: 0 !important;" v-loading="campaignLoading">
|
|
|
|
+ <div style="font-size: 24px; font-weight: bold;">广告活动</div>
|
|
|
|
+ <hr>
|
|
|
|
+ <br>
|
|
<el-form-item label="广告活动名称" prop="name" style="width: 350px">
|
|
<el-form-item label="广告活动名称" prop="name" style="width: 350px">
|
|
- <el-input v-model="ruleForm.name" />
|
|
|
|
|
|
+ <el-input v-model="campaignRuleForm.name" />
|
|
</el-form-item>
|
|
</el-form-item>
|
|
<el-form-item label="广告组合" prop="adMix">
|
|
<el-form-item label="广告组合" prop="adMix">
|
|
- <el-select v-model="ruleForm.adMix" placeholder="请选择">
|
|
|
|
|
|
+ <el-select v-model="campaignRuleForm.adMix" placeholder="请选择">
|
|
<el-option label="Zone one" value="shanghai" />
|
|
<el-option label="Zone one" value="shanghai" />
|
|
<el-option label="Zone two" value="beijing" />
|
|
<el-option label="Zone two" value="beijing" />
|
|
</el-select>
|
|
</el-select>
|
|
</el-form-item>
|
|
</el-form-item>
|
|
- <el-form-item prop="date1" label="开始日期" style="width: 350px">
|
|
|
|
- <el-date-picker v-model="ruleForm.date1" type="date" label="开始日期" placeholder="开始日期" style="width: 100%" />
|
|
|
|
|
|
+ <el-form-item prop="startDate" label="开始日期" style="width: 350px">
|
|
|
|
+ <el-date-picker
|
|
|
|
+ v-model="campaignRuleForm.startDate"
|
|
|
|
+ type="date"
|
|
|
|
+ format="YYYY-MM-DD"
|
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
|
+ label="开始日期"
|
|
|
|
+ placeholder="开始日期"
|
|
|
|
+ style="width: 100%"/>
|
|
</el-form-item>
|
|
</el-form-item>
|
|
<el-form-item prop="date2" label="结束日期" style="width: 350px">
|
|
<el-form-item prop="date2" label="结束日期" style="width: 350px">
|
|
- <el-date-picker v-model="ruleForm.date2" label="结束日期" placeholder="结束日期" style="width: 100%" />
|
|
|
|
|
|
+ <el-date-picker
|
|
|
|
+ v-model="campaignRuleForm.date2"
|
|
|
|
+ type="date"
|
|
|
|
+ format="YYYY-MM-DD"
|
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
|
+ label="结束日期"
|
|
|
|
+ placeholder="结束日期"
|
|
|
|
+ style="width: 100%"/>
|
|
</el-form-item>
|
|
</el-form-item>
|
|
<el-form-item prop="budget" label="每日预算" style="width: 350px">
|
|
<el-form-item prop="budget" label="每日预算" style="width: 350px">
|
|
- <el-input v-model="ruleForm.budget" maxlength="7" oninput="value=value.indexOf('.') > -1?value.slice(0, value.indexOf('.') + 3):value" />
|
|
|
|
|
|
+ <el-input v-model="campaignRuleForm.budget" maxlength="7" oninput="value=value.indexOf('.') > -1?value.slice(0, value.indexOf('.') + 3):value" />
|
|
</el-form-item>
|
|
</el-form-item>
|
|
<el-form-item label="投放类型" prop="type" class="column-item">
|
|
<el-form-item label="投放类型" prop="type" class="column-item">
|
|
- <el-radio-group v-model="ruleForm.type" @click="changeType">
|
|
|
|
|
|
+ <el-radio-group v-model="campaignRuleForm.type" @click="changeType">
|
|
<div>
|
|
<div>
|
|
- <el-radio label="auto">自动</el-radio>
|
|
|
|
|
|
+ <el-radio label="AUTO">自动</el-radio>
|
|
<div class="radio-description">定向与您推广商品相似的关键词和商品</div>
|
|
<div class="radio-description">定向与您推广商品相似的关键词和商品</div>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<div>
|
|
- <el-radio label="manual">手动</el-radio>
|
|
|
|
|
|
+ <el-radio label="MANUAL">手动</el-radio>
|
|
<div class="radio-description">选择关键词或商品以定向购物者搜索并设置自定义出价</div>
|
|
<div class="radio-description">选择关键词或商品以定向购物者搜索并设置自定义出价</div>
|
|
</div>
|
|
</div>
|
|
</el-radio-group>
|
|
</el-radio-group>
|
|
</el-form-item>
|
|
</el-form-item>
|
|
<el-form-item label="竞价策略" prop="bidStrategy" class="column-item column-margin-bottom">
|
|
<el-form-item label="竞价策略" prop="bidStrategy" class="column-item column-margin-bottom">
|
|
- <el-radio-group v-model="ruleForm.bidStrategy">
|
|
|
|
|
|
+ <el-radio-group v-model="campaignRuleForm.bidStrategy">
|
|
<div>
|
|
<div>
|
|
- <el-radio label="dynamicBid_Low" border>
|
|
|
|
|
|
+ <el-radio label="LEGACY_FOR_SALES" border>
|
|
动态竞价-仅降低
|
|
动态竞价-仅降低
|
|
<div class="radio-description-2">当您的广告不太可能带来销售时,我们将实时降低您的竞价</div>
|
|
<div class="radio-description-2">当您的广告不太可能带来销售时,我们将实时降低您的竞价</div>
|
|
</el-radio>
|
|
</el-radio>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<div>
|
|
- <el-radio label="dynamicBid_HighAndLow" border>
|
|
|
|
|
|
+ <el-radio label="AUTO_FOR_SALES" border>
|
|
动态竞价-提高和降低
|
|
动态竞价-提高和降低
|
|
<div class="radio-description-2">
|
|
<div class="radio-description-2">
|
|
当您的广告很有可能带来销售时,我们将实时提高您的竞价(最高可达 100%),并在您的广告不太可能带来销售时降低您的竞价
|
|
当您的广告很有可能带来销售时,我们将实时提高您的竞价(最高可达 100%),并在您的广告不太可能带来销售时降低您的竞价
|
|
</div>
|
|
</div>
|
|
</el-radio>
|
|
</el-radio>
|
|
</div>
|
|
</div>
|
|
- <el-radio label="staticBid" border>
|
|
|
|
|
|
+ <el-radio label="MANUAL" border>
|
|
固定竞价
|
|
固定竞价
|
|
<div class="radio-description-2">我们将使用您的确切竞价和您设置的任何手动调整,而不会根据售出可能性对您的竞价进行更改</div>
|
|
<div class="radio-description-2">我们将使用您的确切竞价和您设置的任何手动调整,而不会根据售出可能性对您的竞价进行更改</div>
|
|
</el-radio>
|
|
</el-radio>
|
|
@@ -71,33 +86,55 @@
|
|
<p style="color: #8e9196">除了出价策略外,您还可以将出价提高多达900%</p>
|
|
<p style="color: #8e9196">除了出价策略外,您还可以将出价提高多达900%</p>
|
|
<div class="gap-items">
|
|
<div class="gap-items">
|
|
<div class="gap-item">搜索结果顶部(首页)</div>
|
|
<div class="gap-item">搜索结果顶部(首页)</div>
|
|
- <el-input v-model="ruleForm.placeBid" class="gap-item">
|
|
|
|
|
|
+ <el-input v-model="campaignRuleForm.placeBid" class="gap-item">
|
|
<template #append>%</template>
|
|
<template #append>%</template>
|
|
</el-input>
|
|
</el-input>
|
|
</div>
|
|
</div>
|
|
<div class="gap-items">
|
|
<div class="gap-items">
|
|
<div class="gap-item">商品首页</div>
|
|
<div class="gap-item">商品首页</div>
|
|
- <el-input v-model="ruleForm.firstPage" class="gap-item">
|
|
|
|
|
|
+ <el-input v-model="campaignRuleForm.firstPage" class="gap-item">
|
|
<template #append>%</template>
|
|
<template #append>%</template>
|
|
</el-input>
|
|
</el-input>
|
|
</div>
|
|
</div>
|
|
- <div class="gap-items">
|
|
|
|
|
|
+ <div class="gap-items" style="margin-bottom: 0">
|
|
<div class="gap-item">搜索结果的其余位置</div>
|
|
<div class="gap-item">搜索结果的其余位置</div>
|
|
- <el-input v-model="ruleForm.other" class="gap-item">
|
|
|
|
|
|
+ <el-input v-model="campaignRuleForm.other" class="gap-item">
|
|
<template #append>%</template>
|
|
<template #append>%</template>
|
|
</el-input>
|
|
</el-input>
|
|
</div>
|
|
</div>
|
|
</el-form-item>
|
|
</el-form-item>
|
|
- <!-- 广告组 -->
|
|
|
|
- <div style="font-size: 20px; font-weight: bold; margin-top: 30px">广告组</div>
|
|
|
|
- <hr />
|
|
|
|
- <el-form-item label="广告组名称" prop="adGroupName" style="width: 350px; margin-top: 20px">
|
|
|
|
- <el-input v-model="ruleForm.adGroupName" />
|
|
|
|
|
|
+ <el-form-item style="margin-left: 48%;">
|
|
|
|
+ <el-button type="primary" plain @click="submitCampaignForm(campaignFormRef)">保存</el-button>
|
|
</el-form-item>
|
|
</el-form-item>
|
|
- <!-- 广告组商品表格 -->
|
|
|
|
- <el-form-item label="商品" prop="commodity" style="width: 100%; margin-top: 20px">
|
|
|
|
- <div style="width: 100%; height: 620px; display: flex; border: 1px solid #c2c7cf; border-radius: 6px">
|
|
|
|
- <div style="width: 50%; border-right: 1px solid #c2c7cf">
|
|
|
|
|
|
+ </el-card>
|
|
|
|
+ <br>
|
|
|
|
+ <!-- 广告组 -->
|
|
|
|
+ <el-card shadow="never" body-style="padding-bottom: 0 !important;">
|
|
|
|
+ <div style="font-size: 20px; font-weight: bold;">广告组</div>
|
|
|
|
+ <hr>
|
|
|
|
+ <br>
|
|
|
|
+ <el-form ref="adGroupRuleFormRef" :model="adGroupRuleForm" :rules="adGroupRules">
|
|
|
|
+ <el-form-item required label="广告组名称" prop="adGroupName" style="width: 350px; margin-top: 20px">
|
|
|
|
+ <el-input v-model="adGroupRuleForm.adGroupName" />
|
|
|
|
+ </el-form-item>
|
|
|
|
+ <el-form-item required label="默认竞价" prop="defaultBidInp">
|
|
|
|
+ <el-input v-model="adGroupRuleForm.defaultBidInp" style="width: 200px">
|
|
|
|
+ <template #prepend>$</template>
|
|
|
|
+ </el-input>
|
|
|
|
+ </el-form-item>
|
|
|
|
+ <el-form-item style="margin-left: 48%;">
|
|
|
|
+ <el-button type="primary" plain @click="submitGroupsForm(adGroupRuleFormRef)" :disabled="campaignSave">保存</el-button>
|
|
|
|
+ </el-form-item>
|
|
|
|
+ </el-form>
|
|
|
|
+ </el-card>
|
|
|
|
+
|
|
|
|
+ <!-- 商品表格 -->
|
|
|
|
+ <div style=" margin-top:20px; font-size: 24px; font-weight: bold;">商品</div>
|
|
|
|
+ <hr>
|
|
|
|
+ <br>
|
|
|
|
+ <el-form-item prop="commodity" style="width: 100%;">
|
|
|
|
+ <div style="width: 100%; height: 620px; display: flex; border: 1px solid #e5e7ec; border-radius: 6px">
|
|
|
|
+ <div style="width: 50%; border-right: 1px solid #e5e7ec">
|
|
<el-tabs v-model="activeName" class="demo-tabs">
|
|
<el-tabs v-model="activeName" class="demo-tabs">
|
|
<el-tab-pane label="搜索" name="first">
|
|
<el-tab-pane label="搜索" name="first">
|
|
<div style="margin-bottom: 10px">
|
|
<div style="margin-bottom: 10px">
|
|
@@ -201,7 +238,7 @@
|
|
</el-tabs>
|
|
</el-tabs>
|
|
</div>
|
|
</div>
|
|
<div style="width: 50%">
|
|
<div style="width: 50%">
|
|
- <el-card class="box-card" shadow="never">
|
|
|
|
|
|
+ <el-card class="box-card" shadow="never" style="border: 0">
|
|
<template #header>
|
|
<template #header>
|
|
<div class="card-header">
|
|
<div class="card-header">
|
|
<span style="font-weight: 550; font-size: 15px; color: #1f2128">已添加: {{ addedData.length }}</span>
|
|
<span style="font-weight: 550; font-size: 15px; color: #1f2128">已添加: {{ addedData.length }}</span>
|
|
@@ -260,25 +297,12 @@
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</el-form-item>
|
|
</el-form-item>
|
|
- <!-- 自动定向 -->
|
|
|
|
- <div class="column-item" v-if="ruleForm.type == 'auto'">
|
|
|
|
- <p style="color: #606266; font-weight: 450"><span style="color: #e47470">*</span> 自动定向</p>
|
|
|
|
- <el-radio-group v-model="ruleForm.autoRedirect" @change="changeBid">
|
|
|
|
- <div style="display: flex">
|
|
|
|
- <el-radio label="defaultBid">设置默认出价</el-radio>
|
|
|
|
- <el-form-item prop="defaultBidInp">
|
|
|
|
- <el-input v-model="ruleForm.defaultBidInp" label="ruleForm.defaultBidInp" style="width: 200px">
|
|
|
|
- <template #prepend>$</template>
|
|
|
|
- </el-input>
|
|
|
|
- </el-form-item>
|
|
|
|
- </div>
|
|
|
|
- <div>
|
|
|
|
- <el-radio label="targetBid">按目标组设置出价</el-radio>
|
|
|
|
- </div>
|
|
|
|
- </el-radio-group>
|
|
|
|
- </div>
|
|
|
|
- <!-- 自动定向---按目标组设置出价 -->
|
|
|
|
- <el-card v-if="showCard && ruleForm.type == 'auto'" class="box-card">
|
|
|
|
|
|
+ <!-- 按目标组设置出价 -->
|
|
|
|
+ <div class="column-item" v-if="campaignRuleForm.type == 'AUTO'">
|
|
|
|
+ <div style=" margin-top:20px; font-size: 24px; font-weight: bold;">按目标组设置出价</div>
|
|
|
|
+ <hr>
|
|
|
|
+ <br>
|
|
|
|
+ <el-card shadow="never" v-if="campaignRuleForm.type == 'AUTO'" class="box-card">
|
|
<div>
|
|
<div>
|
|
<div style="color: #8e9095">
|
|
<div style="color: #8e9095">
|
|
<span>目标群体</span>
|
|
<span>目标群体</span>
|
|
@@ -337,9 +361,12 @@
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</el-card>
|
|
</el-card>
|
|
|
|
+
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
|
|
<!-- 投放类型 -->
|
|
<!-- 投放类型 -->
|
|
- <div class="column-item" v-if="ruleForm.type == 'manual'">
|
|
|
|
|
|
+ <div class="column-item" v-if="campaignRuleForm.type == 'MANUAL'">
|
|
<p style="color: #606266; font-weight: 450"><span style="color: #e47470">*</span> 投放类型</p>
|
|
<p style="color: #606266; font-weight: 450"><span style="color: #e47470">*</span> 投放类型</p>
|
|
<el-radio-group v-model="ruleForm.targetType" @change="changeTargetType">
|
|
<el-radio-group v-model="ruleForm.targetType" @change="changeTargetType">
|
|
<div style="display: flex">
|
|
<div style="display: flex">
|
|
@@ -357,11 +384,11 @@
|
|
</el-radio-group>
|
|
</el-radio-group>
|
|
</div>
|
|
</div>
|
|
<!-- 关键词定向 -->
|
|
<!-- 关键词定向 -->
|
|
- <div style="font-size: 20px; font-weight: bold; margin-top: 30px" v-if="ruleForm.targetType == 'keyWords' && ruleForm.type == 'manual'">
|
|
|
|
|
|
+ <div style="font-size: 20px; font-weight: bold; margin-top: 30px" v-if="ruleForm.targetType == 'keyWords' && campaignRuleForm.type == 'MANUAL'">
|
|
关键词定向
|
|
关键词定向
|
|
</div>
|
|
</div>
|
|
- <hr v-if="ruleForm.targetType == 'keyWords' && ruleForm.type == 'manual'" />
|
|
|
|
- <el-form-item style="width: 100%; margin-top: 20px" v-if="ruleForm.targetType == 'keyWords' && ruleForm.type == 'manual'">
|
|
|
|
|
|
+ <hr v-if="ruleForm.targetType == 'keyWords' && campaignRuleForm.type == 'MANUAL'" />
|
|
|
|
+ <el-form-item style="width: 100%; margin-top: 20px" v-if="ruleForm.targetType == 'keyWords' && campaignRuleForm.type == 'MANUAL'">
|
|
<div style="width: 100%; height: 520px; display: flex; border: 1px solid #c2c7cf; border-radius: 6px">
|
|
<div style="width: 100%; height: 520px; display: flex; border: 1px solid #c2c7cf; border-radius: 6px">
|
|
<div style="width: 50%; border-right: 1px solid #c2c7cf">
|
|
<div style="width: 50%; border-right: 1px solid #c2c7cf">
|
|
<el-tabs v-model="keyWordsTabs" class="demo-tabs" @tab-click="handleGoodsTabs">
|
|
<el-tabs v-model="keyWordsTabs" class="demo-tabs" @tab-click="handleGoodsTabs">
|
|
@@ -429,16 +456,16 @@
|
|
|
|
|
|
<div
|
|
<div
|
|
style="font-size: 20px; font-weight: bold; margin-top: 30px"
|
|
style="font-size: 20px; font-weight: bold; margin-top: 30px"
|
|
- v-if="ruleForm.type === 'auto' || (ruleForm.targetType === 'keyWords' && ruleForm.type === 'manual')">
|
|
|
|
|
|
+ v-if="campaignRuleForm.type === 'AUTO' || (ruleForm.targetType === 'keyWords' && campaignRuleForm.type === 'MANUAL')">
|
|
否定词
|
|
否定词
|
|
</div>
|
|
</div>
|
|
- <hr v-if="ruleForm.type === 'auto' || (ruleForm.targetType === 'keyWords' && ruleForm.type === 'manual')" />
|
|
|
|
|
|
+ <hr v-if="campaignRuleForm.type === 'AUTO' || (ruleForm.targetType === 'keyWords' && campaignRuleForm.type === 'MANUAL')" />
|
|
<!-- 否定词表格 -->
|
|
<!-- 否定词表格 -->
|
|
<el-form-item
|
|
<el-form-item
|
|
style="width: 100%; margin-top: 20px"
|
|
style="width: 100%; margin-top: 20px"
|
|
- v-if="ruleForm.type === 'auto' || (ruleForm.targetType === 'keyWords' && ruleForm.type === 'manual')">
|
|
|
|
- <div style="width: 100%; height: 520px; display: flex; border: 1px solid #c2c7cf; border-radius: 6px">
|
|
|
|
- <div style="width: 50%; border-right: 1px solid #c2c7cf">
|
|
|
|
|
|
+ v-if="campaignRuleForm.type === 'AUTO' || (ruleForm.targetType === 'keyWords' && campaignRuleForm.type === 'MANUAL')">
|
|
|
|
+ <div style="width: 100%; height: 520px; display: flex; border: 1px solid #e5e7ec; border-radius: 6px">
|
|
|
|
+ <div style="width: 50%; border-right: 1px solid #e5e7ec">
|
|
<div style="margin: 10px 0">
|
|
<div style="margin: 10px 0">
|
|
<span style="margin-left: 25px; color: #e47470">*</span>
|
|
<span style="margin-left: 25px; color: #e47470">*</span>
|
|
<span style="color: #666666; margin-right: 10px">匹配类型: </span>
|
|
<span style="color: #666666; margin-right: 10px">匹配类型: </span>
|
|
@@ -457,7 +484,7 @@
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div style="width: 50%">
|
|
<div style="width: 50%">
|
|
- <el-card class="box-card" shadow="never">
|
|
|
|
|
|
+ <el-card class="box-card" shadow="never" style="border: none">
|
|
<template #header>
|
|
<template #header>
|
|
<div class="card-header">
|
|
<div class="card-header">
|
|
<span style="font-weight: 550; font-size: 15px; color: #1f2128">已添加: {{ addedData.length }}</span>
|
|
<span style="font-weight: 550; font-size: 15px; color: #1f2128">已添加: {{ addedData.length }}</span>
|
|
@@ -487,7 +514,11 @@
|
|
style="width: 100%; height: 520px; display: flex; border: 1px solid #c2c7cf; border-radius: 6px"
|
|
style="width: 100%; height: 520px; display: flex; border: 1px solid #c2c7cf; border-radius: 6px"
|
|
v-loading="productOrientationLoading">
|
|
v-loading="productOrientationLoading">
|
|
<div style="width: 50%; border-right: 1px solid #c2c7cf">
|
|
<div style="width: 50%; border-right: 1px solid #c2c7cf">
|
|
- <el-tabs type="border-card" stretch class="goods-orientation-tabs" style="border-bottom-left-radius: 6px">
|
|
|
|
|
|
+ <el-tabs
|
|
|
|
+ type="border-card"
|
|
|
|
+ stretch
|
|
|
|
+ class="goods-orientation-tabs"
|
|
|
|
+ style="border: 0; border-right: 0; border-bottom-left-radius: 6px; border-top-left-radius: 5px; overflow: hidden">
|
|
<el-tab-pane label="品类" style="border-top-left-radius: 6px">
|
|
<el-tab-pane label="品类" style="border-top-left-radius: 6px">
|
|
<div style="display: flex; align-items: center">
|
|
<div style="display: flex; align-items: center">
|
|
<span style="width: 40px">竞价:</span>
|
|
<span style="width: 40px">竞价:</span>
|
|
@@ -529,42 +560,45 @@
|
|
</template>
|
|
</template>
|
|
</el-tree>
|
|
</el-tree>
|
|
</el-scrollbar>
|
|
</el-scrollbar>
|
|
- <el-dialog v-model="visible" :title="`细化分类: ${dialogTitle}`" >
|
|
|
|
- <div style="display: flex; justify-content: space-between;">
|
|
|
|
|
|
+ <el-dialog v-model="visible" :title="`细化分类: ${dialogTitle}`" @close="dialogClose" destroy-on-close>
|
|
|
|
+ <div style="display: flex; justify-content: space-between">
|
|
<span>根据特定品牌、价格范围、星级和Prime配送资格,细化分类</span>
|
|
<span>根据特定品牌、价格范围、星级和Prime配送资格,细化分类</span>
|
|
<span>
|
|
<span>
|
|
- <el-checkbox v-model="dialogForm.isCount" label="显示商品数量" />
|
|
|
|
|
|
+ <el-checkbox v-model="dialogForm.isCount" label="显示商品数量" @change="isCountChanged" />
|
|
</span>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
- <el-form :model="dialogForm" :rules="dialogRules" ref="dialogFormRef" style="margin-top:20px">
|
|
|
|
- <el-form-item style="padding-left: 140px;">
|
|
|
|
- <span style="margin-right: 10px; color:#616266; font-weight: 500;">品牌</span>
|
|
|
|
- <el-select v-model="dialogForm.dialogselectValue" placeholder="请选择">
|
|
|
|
|
|
+ <el-form :model="dialogForm" :rules="dialogRules" ref="dialogFormRef" style="margin-top: 20px">
|
|
|
|
+ <el-form-item style="padding-left: 140px">
|
|
|
|
+ <span style="margin-right: 10px; color: #616266; font-weight: 500">品牌</span>
|
|
|
|
+ <el-select v-model="dialogForm.dialogselectValue" placeholder="请选择" v-loading="dialogSelectLoading">
|
|
<el-option v-for="item in dialogForm.dialogOptions" :key="item.value" :label="item.label" :value="item.value" />
|
|
<el-option v-for="item in dialogForm.dialogOptions" :key="item.value" :label="item.label" :value="item.value" />
|
|
</el-select>
|
|
</el-select>
|
|
</el-form-item>
|
|
</el-form-item>
|
|
- <el-form-item prop="prices" style="padding-left: 112px; margin-top: 10px;">
|
|
|
|
- <span style="margin-right: 10px; color:#616266; font-weight: 500;">价格范围</span>
|
|
|
|
|
|
+ <el-form-item prop="prices" style="padding-left: 112px; margin-top: 10px">
|
|
|
|
+ <span style="margin-right: 10px; color: #616266; font-weight: 500">价格范围</span>
|
|
<el-input-number v-model="dialogForm.prices.lowest" :min="1" :controls="false" placeholder="无最低商品价格" />
|
|
<el-input-number v-model="dialogForm.prices.lowest" :min="1" :controls="false" placeholder="无最低商品价格" />
|
|
--
|
|
--
|
|
<el-input-number v-model="dialogForm.prices.highest" :min="1" :controls="false" placeholder="无最高商品价格" />
|
|
<el-input-number v-model="dialogForm.prices.highest" :min="1" :controls="false" placeholder="无最高商品价格" />
|
|
</el-form-item>
|
|
</el-form-item>
|
|
- <el-form-item prop="starRating" style="padding-left: 85px; margin-top: 10px;">
|
|
|
|
- <span style="margin-right: 15px; color:#616266; font-weight: 500;">查看星级评定</span>
|
|
|
|
- <el-slider v-model="dialogForm.starRating" range show-stops :max="5" :marks="marks" style="width: 70%;"/>
|
|
|
|
|
|
+ <el-form-item prop="starRating" style="padding-left: 85px; margin-top: 10px">
|
|
|
|
+ <span style="margin-right: 15px; color: #616266; font-weight: 500">查看星级评定</span>
|
|
|
|
+ <el-slider v-model="dialogForm.starRating" range show-stops :max="5" :marks="marks" style="width: 70%" />
|
|
</el-form-item>
|
|
</el-form-item>
|
|
- <el-form-item prop="delivery" style="padding-left: 140px; margin-top: 30px;">
|
|
|
|
- <span style="margin-right: 10px; color:#616266; font-weight: 500;">配送</span>
|
|
|
|
|
|
+ <el-form-item prop="delivery" style="padding-left: 140px; margin-top: 30px">
|
|
|
|
+ <span style="margin-right: 10px; color: #616266; font-weight: 500">配送</span>
|
|
<el-radio-group v-model="dialogForm.delivery">
|
|
<el-radio-group v-model="dialogForm.delivery">
|
|
- <el-radio label="all" style="font-weight: 400;">具备</el-radio>
|
|
|
|
- <el-radio label="eligible" style="font-weight: 400;">具备Prime资格</el-radio>
|
|
|
|
- <el-radio label="diseligible" style="font-weight: 400;">不具备Prime资格</el-radio>
|
|
|
|
|
|
+ <el-radio label="all" style="font-weight: 400">具备</el-radio>
|
|
|
|
+ <el-radio label="eligible" style="font-weight: 400">具备Prime资格</el-radio>
|
|
|
|
+ <el-radio label="diseligible" style="font-weight: 400">不具备Prime资格</el-radio>
|
|
</el-radio-group>
|
|
</el-radio-group>
|
|
</el-form-item>
|
|
</el-form-item>
|
|
</el-form>
|
|
</el-form>
|
|
<template #footer>
|
|
<template #footer>
|
|
- <div style="display: flex; justify-content: space-between;">
|
|
|
|
- <span v-loading="countLoadig">定位到的商品数量: {{ commodityCount[0]?.min }} - {{ commodityCount[0]?.max }}</span>
|
|
|
|
|
|
+ <div style="display: flex; justify-content: space-between">
|
|
|
|
+ <span v-loading="countLoadig"
|
|
|
|
+ >定位到的商品数量:
|
|
|
|
+ <span v-if="dialogForm.isCount == true">{{ commodityCount[0]?.min }} - {{ commodityCount[0]?.max }}</span></span
|
|
|
|
+ >
|
|
<span class="dialog-footer">
|
|
<span class="dialog-footer">
|
|
<el-button @click="visible = false">取消</el-button>
|
|
<el-button @click="visible = false">取消</el-button>
|
|
<el-button type="primary" @click="dialogFormSubmit">确定</el-button>
|
|
<el-button type="primary" @click="dialogFormSubmit">确定</el-button>
|
|
@@ -578,15 +612,19 @@
|
|
<el-tab-pane label="单个商品">
|
|
<el-tab-pane label="单个商品">
|
|
<div style="display: flex; align-items: center">
|
|
<div style="display: flex; align-items: center">
|
|
<span style="width: 40px">竞价:</span>
|
|
<span style="width: 40px">竞价:</span>
|
|
- <el-select v-model="categoryBiddingType" class="m-2" placeholder="Select">
|
|
|
|
- <el-option v-for="item in categoryBiddingTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
|
|
|
|
|
|
+ <el-select class="m-2" v-model="singleGoodsBidSelect" @change="singleGoodsBidSelectChanged">
|
|
|
|
+ <el-option v-for="item in singleGoodsBidTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
|
|
</el-select>
|
|
</el-select>
|
|
- <el-input v-model="categoryBidInput" placeholder="Please input" style="width: 200px">
|
|
|
|
|
|
+ <el-input
|
|
|
|
+ v-model="singleGoodsBidInput"
|
|
|
|
+ :disabled="singleGoodsBidSelect == 'defaultBid'"
|
|
|
|
+ placeholder="Please input"
|
|
|
|
+ style="width: 200px">
|
|
<template #prepend>$</template>
|
|
<template #prepend>$</template>
|
|
</el-input>
|
|
</el-input>
|
|
<div style="margin-left: 20px">
|
|
<div style="margin-left: 20px">
|
|
<span style="margin-right: 10px">类型:</span>
|
|
<span style="margin-right: 10px">类型:</span>
|
|
- <el-checkbox v-model="expand" label="拓展" />
|
|
|
|
|
|
+ <el-checkbox v-model="expand" label="扩展" />
|
|
<el-checkbox v-model="accurate" label="精准" />
|
|
<el-checkbox v-model="accurate" label="精准" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@@ -600,7 +638,7 @@
|
|
</el-tab-pane>
|
|
</el-tab-pane>
|
|
<el-tab-pane label="搜索" name="second">
|
|
<el-tab-pane label="搜索" name="second">
|
|
<el-input placeholder="按ASIN搜索"></el-input>
|
|
<el-input placeholder="按ASIN搜索"></el-input>
|
|
- <el-table :data="proposalTableData" style="width: 100%" height="342">
|
|
|
|
|
|
+ <el-table :data="proposalTableData" style="width: 100%" height="309">
|
|
<el-table-column prop="proposal" label="商品" width="520" />
|
|
<el-table-column prop="proposal" label="商品" width="520" />
|
|
<el-table-column prop="address" label="类型" />
|
|
<el-table-column prop="address" label="类型" />
|
|
<el-table-column prop="operational" label="操作" />
|
|
<el-table-column prop="operational" label="操作" />
|
|
@@ -616,20 +654,34 @@
|
|
<el-card class="box-card" shadow="never">
|
|
<el-card class="box-card" shadow="never">
|
|
<template #header>
|
|
<template #header>
|
|
<div class="card-header">
|
|
<div class="card-header">
|
|
- <span style="font-weight: 550; font-size: 15px; color: #1f2128">已添加: {{ addedData.length }}</span>
|
|
|
|
- <el-button class="button" text bg @click="delAllKeyWords">全部删除</el-button>
|
|
|
|
|
|
+ <span style="font-weight: 550; font-size: 15px; color: #1f2128">已添加: {{ productOrientationTableData.length }}</span>
|
|
|
|
+ <el-button class="button" text bg @click="delAllCna">全部删除</el-button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</template>
|
|
<div class="card-body">
|
|
<div class="card-body">
|
|
<el-table
|
|
<el-table
|
|
- :data="addedKeyWordsTableData"
|
|
|
|
|
|
+ :data="productOrientationTableData"
|
|
style="width: 100%"
|
|
style="width: 100%"
|
|
:header-row-style="changeKeyWordsTableHeader"
|
|
:header-row-style="changeKeyWordsTableHeader"
|
|
:header-cell-style="headerCellStyle">
|
|
:header-cell-style="headerCellStyle">
|
|
- <el-table-column prop="keyword" label="关键词" width="auto" />
|
|
|
|
- <el-table-column prop="bid" label="出价" />
|
|
|
|
- <el-table-column prop="suggestBid" label="建议出价" />
|
|
|
|
- <el-table-column prop="operate" label="操作" width="60" align="right" />
|
|
|
|
|
|
+ <el-table-column prop="cna" label="分类 & 商品" width="300">
|
|
|
|
+ <template #default="scope"> 分类: {{ scope.row.cna }} </template>
|
|
|
|
+ </el-table-column>
|
|
|
|
+ <el-table-column prop="type" label="类型">
|
|
|
|
+ <template #default="scope">
|
|
|
|
+ {{ scope.row.type ? 'scope.row.type' : '--' }}
|
|
|
|
+ </template>
|
|
|
|
+ </el-table-column>
|
|
|
|
+ <el-table-column prop="bid" label="竞价">
|
|
|
|
+ <template #default="scope">
|
|
|
|
+ <el-input-number v-model="scope.row.bid" :min="0" :max="1000000" :controls="false" size="small" />
|
|
|
|
+ </template>
|
|
|
|
+ </el-table-column>
|
|
|
|
+ <el-table-column prop="operate" label="操作" width="60" align="right">
|
|
|
|
+ <template #default="scope">
|
|
|
|
+ <el-button text size="small" @click="delCna(scope.$index)">删除</el-button>
|
|
|
|
+ </template>
|
|
|
|
+ </el-table-column>
|
|
</el-table>
|
|
</el-table>
|
|
</div>
|
|
</div>
|
|
</el-card>
|
|
</el-card>
|
|
@@ -640,17 +692,17 @@
|
|
|
|
|
|
<div
|
|
<div
|
|
style="font-size: 20px; font-weight: bold; margin-top: 30px"
|
|
style="font-size: 20px; font-weight: bold; margin-top: 30px"
|
|
- v-if="ruleForm.type == 'auto' || (ruleForm.targetType == 'Goods' && ruleForm.type === 'manual')">
|
|
|
|
|
|
+ v-if="campaignRuleForm.type == 'AUTO' || (ruleForm.targetType == 'Goods' && campaignRuleForm.type === 'manual')">
|
|
否定商品
|
|
否定商品
|
|
</div>
|
|
</div>
|
|
- <hr v-if="ruleForm.type == 'auto' || (ruleForm.targetType == 'Goods' && ruleForm.type === 'manual')" />
|
|
|
|
|
|
+ <hr v-if="campaignRuleForm.type == 'AUTO' || (ruleForm.targetType == 'Goods' && campaignRuleForm.type === 'MANUAL')" />
|
|
<!-- 否定商品表格 -->
|
|
<!-- 否定商品表格 -->
|
|
<el-form-item
|
|
<el-form-item
|
|
prop="matchType"
|
|
prop="matchType"
|
|
style="width: 100%; margin-top: 20px"
|
|
style="width: 100%; margin-top: 20px"
|
|
- v-if="ruleForm.type == 'auto' || (ruleForm.targetType == 'Goods' && ruleForm.type === 'manual')">
|
|
|
|
- <div style="width: 100%; height: 520px; display: flex; border: 1px solid #c2c7cf; border-radius: 6px">
|
|
|
|
- <div style="width: 50%; border-right: 1px solid #c2c7cf">
|
|
|
|
|
|
+ v-if="campaignRuleForm.type == 'AUTO' || (ruleForm.targetType == 'Goods' && campaignRuleForm.type === 'MANUAL')">
|
|
|
|
+ <div style="width: 100%; height: 520px; display: flex; border: 1px solid #e5e7ec; border-radius: 6px">
|
|
|
|
+ <div style="width: 50%; border-right: 1px solid #e5e7ec">
|
|
<el-tabs v-model="negativeTabs" class="demo-tabs" @tab-click="handleNegGoodsTabs">
|
|
<el-tabs v-model="negativeTabs" class="demo-tabs" @tab-click="handleNegGoodsTabs">
|
|
<el-tab-pane label="搜索" name="first">
|
|
<el-tab-pane label="搜索" name="first">
|
|
<div style="margin-bottom: 10px">
|
|
<div style="margin-bottom: 10px">
|
|
@@ -703,7 +755,7 @@
|
|
</el-tabs>
|
|
</el-tabs>
|
|
</div>
|
|
</div>
|
|
<div style="width: 50%">
|
|
<div style="width: 50%">
|
|
- <el-card class="box-card" shadow="never">
|
|
|
|
|
|
+ <el-card class="box-card" shadow="never" style="border: none">
|
|
<template #header>
|
|
<template #header>
|
|
<div class="card-header">
|
|
<div class="card-header">
|
|
<span style="font-weight: 550; font-size: 15px; color: #1f2128">已添加: {{ addedNegetiveTableData.length }}</span>
|
|
<span style="font-weight: 550; font-size: 15px; color: #1f2128">已添加: {{ addedNegetiveTableData.length }}</span>
|
|
@@ -750,8 +802,8 @@
|
|
|
|
|
|
<br />
|
|
<br />
|
|
<el-form-item>
|
|
<el-form-item>
|
|
- <el-button type="negativeGoods" @click="submitForm(ruleFormRef)">Create</el-button>
|
|
|
|
- <el-button @click="resetForm(ruleFormRef)">Reset</el-button>
|
|
|
|
|
|
+ <el-button size="large" @click="resetForm(ruleFormRef)">取消</el-button>
|
|
|
|
+ <el-button size="large" type="primary" plain @click="submitForm(ruleFormRef)">保存</el-button>
|
|
</el-form-item>
|
|
</el-form-item>
|
|
</el-form>
|
|
</el-form>
|
|
</div>
|
|
</div>
|
|
@@ -764,11 +816,14 @@ import { onMounted, reactive, ref, computed, watch } from 'vue'
|
|
import type { CSSProperties } from 'vue'
|
|
import type { CSSProperties } from 'vue'
|
|
import { useRoute } from 'vue-router'
|
|
import { useRoute } from 'vue-router'
|
|
import type { FormInstance, FormRules, TabsPaneContext } from 'element-plus'
|
|
import type { FormInstance, FormRules, TabsPaneContext } from 'element-plus'
|
|
|
|
+import { ElMessage } from 'element-plus'
|
|
import { useShopInfo } from '/@/stores/shopInfo'
|
|
import { useShopInfo } from '/@/stores/shopInfo'
|
|
import { usePublicData } from '/@/stores/publicData'
|
|
import { usePublicData } from '/@/stores/publicData'
|
|
import { storeToRefs } from 'pinia'
|
|
import { storeToRefs } from 'pinia'
|
|
import { useRouter } from 'vue-router'
|
|
import { useRouter } from 'vue-router'
|
|
import { request } from '/@/utils/service'
|
|
import { request } from '/@/utils/service'
|
|
|
|
+import { number } from 'echarts'
|
|
|
|
+
|
|
|
|
|
|
const negativeTableData = ref([])
|
|
const negativeTableData = ref([])
|
|
const addedNegetiveTableData = ref([])
|
|
const addedNegetiveTableData = ref([])
|
|
@@ -801,14 +856,15 @@ let phraseType = ref(true)
|
|
let exactType = ref(true)
|
|
let exactType = ref(true)
|
|
|
|
|
|
// 表单相关数据
|
|
// 表单相关数据
|
|
|
|
+const campaignLoading = ref(false)
|
|
const formSize = ref('default')
|
|
const formSize = ref('default')
|
|
const labelPosition = ref('top')
|
|
const labelPosition = ref('top')
|
|
-const ruleFormRef = ref<FormInstance>()
|
|
|
|
-interface RuleForm {
|
|
|
|
|
|
+const campaignFormRef = ref<FormInstance>()
|
|
|
|
+interface CampaignForm {
|
|
name: string
|
|
name: string
|
|
adMix: string
|
|
adMix: string
|
|
count: string
|
|
count: string
|
|
- date1: string
|
|
|
|
|
|
+ startDate: string
|
|
date2: string
|
|
date2: string
|
|
budget: string
|
|
budget: string
|
|
delivery: boolean
|
|
delivery: boolean
|
|
@@ -817,9 +873,79 @@ interface RuleForm {
|
|
placeBid: string
|
|
placeBid: string
|
|
firstPage: string
|
|
firstPage: string
|
|
other: string
|
|
other: string
|
|
|
|
+}
|
|
|
|
+const campaignRuleForm = reactive<CampaignForm>({
|
|
|
|
+ name: 'aitest02campaign_wxc',
|
|
|
|
+ adMix: '',
|
|
|
|
+ count: '',
|
|
|
|
+ startDate: '',
|
|
|
|
+ date2: '',
|
|
|
|
+ budget: '',
|
|
|
|
+ delivery: false,
|
|
|
|
+ type: 'AUTO',
|
|
|
|
+ bidStrategy: 'LEGACY_FOR_SALES',
|
|
|
|
+ placeBid: '',
|
|
|
|
+ firstPage: '',
|
|
|
|
+ other: ''
|
|
|
|
+})
|
|
|
|
+const campaignRules = computed(() => ({
|
|
|
|
+ name: [{ required: true, message: 'Please input Activity name', trigger: 'blur' }],
|
|
|
|
+ adMix: [{ required: false, message: 'Please select Activity zone', trigger: 'change' }],
|
|
|
|
+ count: [{ required: true, message: 'Please select Activity count', trigger: 'change' }],
|
|
|
|
+ startDate: [{ required: true, type: 'date', message: 'Please pick a date', trigger: 'change' }],
|
|
|
|
+ date2: [{ required: false, type: 'date', message: 'Please pick a time', trigger: 'change' }],
|
|
|
|
+ budget: [
|
|
|
|
+ { required: true, message: '请输入预算', trigger: 'blur' },
|
|
|
|
+ { pattern: /^(?:[1-9]\d{0,5}|1000000)(?:\.\d{1,2})?$/, message: '预算必须是1到1000000之间的数字,小数点后最多两位', trigger: 'blur' },
|
|
|
|
+ ],
|
|
|
|
+ type: [{ required: false, trigger: 'change' }],
|
|
|
|
+ bidStrategy: [{ required: true, message: 'Please select activity resource', trigger: 'change' }],
|
|
|
|
+ placeBid: [{ required: false, pattern: /^[0-9]{1,3}$/, message: '必须是0~900之间的整数百分比', trigger: 'change' }],
|
|
|
|
+ firstPage: [{ required: false, pattern: /^[0-9]{1,3}$/, message: '必须是0~900之间的整数百分比', trigger: 'change' }],
|
|
|
|
+ other: [{ required: false, pattern: /^[0-9]{1,3}$/, message: '必须是0~900之间的整数百分比', trigger: 'change' }],
|
|
|
|
+}))
|
|
|
|
+
|
|
|
|
+const adGroupRuleFormRef = ref<FormInstance>()
|
|
|
|
+interface AdGroupForm {
|
|
adGroupName: string
|
|
adGroupName: string
|
|
- autoRedirect: string
|
|
|
|
defaultBidInp: string
|
|
defaultBidInp: string
|
|
|
|
+}
|
|
|
|
+const adGroupRuleForm = reactive<AdGroupForm>({
|
|
|
|
+ adGroupName: '',
|
|
|
|
+ defaultBidInp: '',
|
|
|
|
+})
|
|
|
|
+const adGroupRules = computed(() => ({
|
|
|
|
+ adGroupName: [{ required: true, message: '请输入广告组名称', trigger: 'blur' }],
|
|
|
|
+ defaultBidInp: [
|
|
|
|
+ { required: true, message: '请输入默认出价', trigger: 'blur' },
|
|
|
|
+ { validator: validateDefaultBidInp, trigger: 'blur' },
|
|
|
|
+ ],
|
|
|
|
+}))
|
|
|
|
+
|
|
|
|
+function validateDefaultBidInp(rule, value, callback) {
|
|
|
|
+ if (value === '') {
|
|
|
|
+ callback(new Error('请输入默认出价'))
|
|
|
|
+ } else if (!/^(0\.0[2-9]|0\.[1-9]\d|1?\d{1,2}(\.\d{1,2})?|1000(\.0{1,2})?)$/.test(value)) {
|
|
|
|
+ callback(new Error('最小不低于0.02,最大不超过1000,并且只能有两位小数'))
|
|
|
|
+ } else {
|
|
|
|
+ const numericValue = parseFloat(value)
|
|
|
|
+ const numericBudget = parseFloat(campaignRuleForm.budget)
|
|
|
|
+ if (numericValue > numericBudget) {
|
|
|
|
+ callback(new Error('默认出价不能大于当前预算'))
|
|
|
|
+ } else {
|
|
|
|
+ callback()
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+const ruleFormRef = ref<FormInstance>()
|
|
|
|
+interface RuleForm {
|
|
|
|
+
|
|
|
|
+ autoRedirect: string
|
|
|
|
+
|
|
closeMatch: boolean
|
|
closeMatch: boolean
|
|
broadMatch: boolean
|
|
broadMatch: boolean
|
|
similarProducts: boolean
|
|
similarProducts: boolean
|
|
@@ -835,21 +961,7 @@ interface RuleForm {
|
|
targetType: string
|
|
targetType: string
|
|
}
|
|
}
|
|
const ruleForm = reactive<RuleForm>({
|
|
const ruleForm = reactive<RuleForm>({
|
|
- name: 'Hello',
|
|
|
|
- adMix: '',
|
|
|
|
- count: '',
|
|
|
|
- date1: '',
|
|
|
|
- date2: '',
|
|
|
|
- budget: '',
|
|
|
|
- delivery: false,
|
|
|
|
- type: 'auto',
|
|
|
|
- bidStrategy: 'dynamicBid_Low',
|
|
|
|
- placeBid: '',
|
|
|
|
- firstPage: '',
|
|
|
|
- other: '',
|
|
|
|
- adGroupName: '',
|
|
|
|
autoRedirect: 'defaultBid',
|
|
autoRedirect: 'defaultBid',
|
|
- defaultBidInp: '',
|
|
|
|
closeMatch: true,
|
|
closeMatch: true,
|
|
broadMatch: true,
|
|
broadMatch: true,
|
|
similarProducts: true,
|
|
similarProducts: true,
|
|
@@ -865,23 +977,7 @@ const ruleForm = reactive<RuleForm>({
|
|
targetType: 'keyWords',
|
|
targetType: 'keyWords',
|
|
})
|
|
})
|
|
const rules = computed(() => ({
|
|
const rules = computed(() => ({
|
|
- name: [{ required: true, message: 'Please input Activity name', trigger: 'blur' }],
|
|
|
|
- adMix: [{ required: false, message: 'Please select Activity zone', trigger: 'change' }],
|
|
|
|
- count: [{ required: true, message: 'Please select Activity count', trigger: 'change' }],
|
|
|
|
- date1: [{ required: true, type: 'date', message: 'Please pick a date', trigger: 'change' }],
|
|
|
|
- date2: [{ required: false, type: 'date', message: 'Please pick a time', trigger: 'change' }],
|
|
|
|
- budget: [
|
|
|
|
- { required: true, message: '请输入预算', trigger: 'blur' },
|
|
|
|
- { pattern: /^(?:[1-9]\d{0,5}|1000000)(?:\.\d{1,2})?$/, message: '预算必须是1到1000000之间的数字,小数点后最多两位', trigger: 'blur' },
|
|
|
|
- ],
|
|
|
|
- type: [{ required: false, trigger: 'change' }],
|
|
|
|
- bidStrategy: [{ required: true, message: 'Please select activity resource', trigger: 'change' }],
|
|
|
|
- placeBid: [{ required: false, pattern: /^[0-9]{1,3}$/, message: '必须是0~900之间的整数百分比', trigger: 'change' }],
|
|
|
|
- firstPage: [{ required: false, pattern: /^[0-9]{1,3}$/, message: '必须是0~900之间的整数百分比', trigger: 'change' }],
|
|
|
|
- other: [{ required: false, pattern: /^[0-9]{1,3}$/, message: '必须是0~900之间的整数百分比', trigger: 'change' }],
|
|
|
|
- adGroupName: [{ required: true, message: 'Please input Activity name', trigger: 'blur' }],
|
|
|
|
autoRedirect: [{ required: true, trigger: 'change' }],
|
|
autoRedirect: [{ required: true, trigger: 'change' }],
|
|
- defaultBidInp: getValidationRules('defaultBidInp'),
|
|
|
|
relatedProductsInp: getValidationRules('relatedProductsInp'),
|
|
relatedProductsInp: getValidationRules('relatedProductsInp'),
|
|
similarProductsInp: getValidationRules('similarProductsInp'),
|
|
similarProductsInp: getValidationRules('similarProductsInp'),
|
|
broadMatchInp: getValidationRules('broadMatchInp'),
|
|
broadMatchInp: getValidationRules('broadMatchInp'),
|
|
@@ -933,11 +1029,24 @@ const categoryBiddingTypeOptions = [
|
|
},
|
|
},
|
|
]
|
|
]
|
|
const categoryBidInput = ref('0.75')
|
|
const categoryBidInput = ref('0.75')
|
|
|
|
+const singleGoodsBidSelect = ref('customBid')
|
|
|
|
+const singleGoodsBidTypeOptions = [
|
|
|
|
+ {
|
|
|
|
+ value: 'defaultBid',
|
|
|
|
+ label: '默认竞价',
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ value: 'customBid',
|
|
|
|
+ label: '自定义竞价',
|
|
|
|
+ },
|
|
|
|
+]
|
|
|
|
+const singleGoodsBidInput = ref('0.75')
|
|
const expand = ref(true)
|
|
const expand = ref(true)
|
|
const accurate = ref(false)
|
|
const accurate = ref(false)
|
|
const proposalTableData = ref([])
|
|
const proposalTableData = ref([])
|
|
const searchClassifyTableData = ref([])
|
|
const searchClassifyTableData = ref([])
|
|
const productOrientationLoading = ref(false)
|
|
const productOrientationLoading = ref(false)
|
|
|
|
+const dialogSelectLoading = ref(false)
|
|
const defaultProps = {
|
|
const defaultProps = {
|
|
children: 'ch',
|
|
children: 'ch',
|
|
label: 'cna',
|
|
label: 'cna',
|
|
@@ -950,19 +1059,17 @@ const dialogselectValue = ref('')
|
|
let dialogOptions: any = ref([])
|
|
let dialogOptions: any = ref([])
|
|
const dialogForm: any = reactive({
|
|
const dialogForm: any = reactive({
|
|
prices: {
|
|
prices: {
|
|
- lowest: undefined,
|
|
|
|
- highest: undefined
|
|
|
|
|
|
+ lowest: undefined,
|
|
|
|
+ highest: undefined,
|
|
},
|
|
},
|
|
- starRating: [0,5],
|
|
|
|
|
|
+ starRating: [0, 5],
|
|
dialogselectValue: [],
|
|
dialogselectValue: [],
|
|
delivery: 'all',
|
|
delivery: 'all',
|
|
isCount: false,
|
|
isCount: false,
|
|
})
|
|
})
|
|
const dialogFormRef = ref()
|
|
const dialogFormRef = ref()
|
|
const dialogRules = reactive({
|
|
const dialogRules = reactive({
|
|
- prices: [
|
|
|
|
- { validator: validatePrices, trigger: 'blur' }
|
|
|
|
- ]
|
|
|
|
|
|
+ prices: [{ validator: validatePrices, trigger: 'blur' }],
|
|
})
|
|
})
|
|
|
|
|
|
interface Mark {
|
|
interface Mark {
|
|
@@ -979,7 +1086,8 @@ const marks = reactive<Marks>({
|
|
5: '5',
|
|
5: '5',
|
|
})
|
|
})
|
|
let commodityCount = ref([])
|
|
let commodityCount = ref([])
|
|
-
|
|
|
|
|
|
+let currentDialogIndex = ref(0)
|
|
|
|
+let productOrientationTableData = ref([])
|
|
|
|
|
|
async function validatePrices(rule, value) {
|
|
async function validatePrices(rule, value) {
|
|
if (value.highest !== '' && value.lowest !== '' && value.highest <= value.lowest) {
|
|
if (value.highest !== '' && value.lowest !== '' && value.highest <= value.lowest) {
|
|
@@ -999,7 +1107,6 @@ function dialogFormSubmit() {
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
async function setProductOrientationData() {
|
|
async function setProductOrientationData() {
|
|
try {
|
|
try {
|
|
const resp = await request({
|
|
const resp = await request({
|
|
@@ -1027,20 +1134,19 @@ async function setDialogOption() {
|
|
},
|
|
},
|
|
})
|
|
})
|
|
const options = resp.data
|
|
const options = resp.data
|
|
- dialogForm.dialogOptions = options.brands.map(brand => {
|
|
|
|
|
|
+ dialogForm.dialogOptions = options.brands.map((brand) => {
|
|
return {
|
|
return {
|
|
label: brand.name,
|
|
label: brand.name,
|
|
- value: brand.id
|
|
|
|
|
|
+ value: brand.id,
|
|
}
|
|
}
|
|
})
|
|
})
|
|
- console.log('🚀 ~ setDialogOption ~ dialogOptions-->>', dialogOptions)
|
|
|
|
|
|
+ dialogSelectLoading.value = false
|
|
} catch (error) {
|
|
} catch (error) {
|
|
console.error('请求失败:', error)
|
|
console.error('请求失败:', error)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
-async function getCount() {
|
|
|
|
|
|
+async function getCount(instanceId) {
|
|
try {
|
|
try {
|
|
const resp = await request({
|
|
const resp = await request({
|
|
url: '/api/ad_manage/products/count/',
|
|
url: '/api/ad_manage/products/count/',
|
|
@@ -1050,32 +1156,78 @@ async function getCount() {
|
|
category_id: categoryId.value,
|
|
category_id: categoryId.value,
|
|
},
|
|
},
|
|
})
|
|
})
|
|
- commodityCount.value = resp.data.AsinCounts
|
|
|
|
- countLoadig.value = false
|
|
|
|
- } catch(error) {
|
|
|
|
|
|
+ if (instanceId === currentDialogIndex.value) {
|
|
|
|
+ commodityCount.value = resp.data.AsinCounts
|
|
|
|
+ }
|
|
|
|
+ } catch (error) {
|
|
console.error('请求失败:', error)
|
|
console.error('请求失败:', error)
|
|
|
|
+ } finally {
|
|
|
|
+ if (instanceId === currentDialogIndex.value) {
|
|
|
|
+ countLoadig.value = false
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function dialogClose() {
|
|
|
|
+ currentDialogIndex.value++
|
|
|
|
+ resetDialogForm()
|
|
|
|
+ dialogForm.isCount = false
|
|
|
|
+ commodityCount.value = []
|
|
|
|
+ countLoadig.value = false
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function resetDialogForm() {
|
|
|
|
+ dialogForm.prices.lowest = undefined
|
|
|
|
+ dialogForm.prices.highest = undefined
|
|
|
|
+ dialogForm.starRating = [0, 5]
|
|
|
|
+ dialogForm.dialogselectValue = []
|
|
|
|
+ dialogForm.delivery = 'all'
|
|
|
|
+ dialogForm.isCount = false
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function isCountChanged() {
|
|
|
|
+ if (dialogForm.isCount) {
|
|
|
|
+ const instanceId = currentDialogIndex.value
|
|
|
|
+ countLoadig.value = true
|
|
|
|
+ getCount(instanceId)
|
|
|
|
+ } else {
|
|
|
|
+ countLoadig.value = false
|
|
|
|
+ commodityCount.value = []
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function delCna(index) {
|
|
|
|
+ productOrientationTableData.value.splice(index, 1)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function delAllCna() {
|
|
|
|
+ productOrientationTableData.value = []
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function singleGoodsBidSelectChanged() {
|
|
|
|
+ if (singleGoodsBidSelect.value === 'defaultBid') {
|
|
|
|
+ singleGoodsBidInput.value = ''
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
function refine(data) {
|
|
function refine(data) {
|
|
console.log('🚀 ~ refine ~ data-->>', data)
|
|
console.log('🚀 ~ refine ~ data-->>', data)
|
|
|
|
+ commodityCount.value = []
|
|
dialogTitle.value = data.cna
|
|
dialogTitle.value = data.cna
|
|
categoryId.value = data.cid
|
|
categoryId.value = data.cid
|
|
visible.value = true
|
|
visible.value = true
|
|
|
|
+ dialogSelectLoading.value = true
|
|
setDialogOption()
|
|
setDialogOption()
|
|
}
|
|
}
|
|
|
|
|
|
function orientate(node, data) {
|
|
function orientate(node, data) {
|
|
-}
|
|
|
|
|
|
+ console.log('🚀 ~ orientate ~ data-->>', data)
|
|
|
|
+ const exists = productOrientationTableData.value.some((item) => item.cid === data.cid)
|
|
|
|
|
|
-watch(() => dialogForm.isCount, (newValue, oldValue) => {
|
|
|
|
- if (newValue === true) {
|
|
|
|
- countLoadig.value = true
|
|
|
|
- getCount()
|
|
|
|
- } else {
|
|
|
|
- commodityCount.value = []
|
|
|
|
- }
|
|
|
|
- })
|
|
|
|
|
|
+ if (!exists) {
|
|
|
|
+ productOrientationTableData.value.push(data)
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
|
|
// ------------------------------------------关键词定向模块------------------------------------------
|
|
// ------------------------------------------关键词定向模块------------------------------------------
|
|
const bidType = ref('customBid')
|
|
const bidType = ref('customBid')
|
|
@@ -1373,7 +1525,7 @@ function handleNegGoodsTabs(tab: TabsPaneContext, event: Event) {
|
|
// ------------------------------------------自定义校验模块------------------------------------------
|
|
// ------------------------------------------自定义校验模块------------------------------------------
|
|
function checkBid(value, callback, bidField) {
|
|
function checkBid(value, callback, bidField) {
|
|
const bid = parseFloat(value)
|
|
const bid = parseFloat(value)
|
|
- const budget = parseFloat(ruleForm.budget)
|
|
|
|
|
|
+ const budget = parseFloat(campaignRuleForm.budget)
|
|
// 检查值是否为最多两位小数的普通数字格式
|
|
// 检查值是否为最多两位小数的普通数字格式
|
|
const isNormalNumberWithTwoDecimals = /^-?\d+(\.\d{1,2})?$/.test(value)
|
|
const isNormalNumberWithTwoDecimals = /^-?\d+(\.\d{1,2})?$/.test(value)
|
|
|
|
|
|
@@ -1451,12 +1603,13 @@ watch(
|
|
}
|
|
}
|
|
)
|
|
)
|
|
|
|
|
|
-// 表单提交
|
|
|
|
|
|
+// ------------------------------------------表单提交------------------------------------------
|
|
async function submitForm(formEl: FormInstance | undefined) {
|
|
async function submitForm(formEl: FormInstance | undefined) {
|
|
if (!formEl) return
|
|
if (!formEl) return
|
|
await formEl.validate((valid, fields) => {
|
|
await formEl.validate((valid, fields) => {
|
|
if (valid) {
|
|
if (valid) {
|
|
console.log('submit!')
|
|
console.log('submit!')
|
|
|
|
+ createCampaigns()
|
|
} else {
|
|
} else {
|
|
console.log('error submit!', fields)
|
|
console.log('error submit!', fields)
|
|
}
|
|
}
|
|
@@ -1468,6 +1621,118 @@ function resetForm(formEl: FormInstance | undefined) {
|
|
formEl.resetFields()
|
|
formEl.resetFields()
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+
|
|
|
|
+let respCampaignId = ref('')
|
|
|
|
+let campaignSave = ref(true)
|
|
|
|
+async function createCampaigns() {
|
|
|
|
+ try {
|
|
|
|
+ // 必需字段列表
|
|
|
|
+ const requiredFields = [
|
|
|
|
+ { key: 'profile_id', value: profile.value.profile_id },
|
|
|
|
+ { key: 'name', value: campaignRuleForm.name },
|
|
|
|
+ { key: 'startDate', value: campaignRuleForm.startDate },
|
|
|
|
+ { key: 'targetingType', value: campaignRuleForm.type },
|
|
|
|
+ { key: 'strategy', value: campaignRuleForm.bidStrategy },
|
|
|
|
+ { key: 'budget', value: campaignRuleForm.budget },
|
|
|
|
+ ]
|
|
|
|
+
|
|
|
|
+ // 检查每个必需字段
|
|
|
|
+ requiredFields.forEach((field) => {
|
|
|
|
+ if (!field.value) {
|
|
|
|
+ throw new Error(`缺少必需的字段: ${field.key}`)
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ // 构建请求数据
|
|
|
|
+ const requestData = {
|
|
|
|
+ profile_id: profile.value.profile_id,
|
|
|
|
+ name: campaignRuleForm.name,
|
|
|
|
+ startDate: campaignRuleForm.startDate,
|
|
|
|
+ budget: campaignRuleForm.budget,
|
|
|
|
+ targetingType: campaignRuleForm.type,
|
|
|
|
+ strategy: campaignRuleForm.bidStrategy,
|
|
|
|
+ state: 'PAUSED',
|
|
|
|
+ // 可选字段
|
|
|
|
+ endDate: campaignRuleForm.date2,
|
|
|
|
+ t_percentage: campaignRuleForm.placeBid,
|
|
|
|
+ p_percentage: campaignRuleForm.firstPage,
|
|
|
|
+ r_percentage: campaignRuleForm.other,
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 过滤掉 undefined 或空的可选字段
|
|
|
|
+ const filteredRequestData = Object.fromEntries(Object.entries(requestData).filter(([_, v]) => v != null))
|
|
|
|
+
|
|
|
|
+ const resp = await request({
|
|
|
|
+ url: '/api/ad_manage/spcampaigns/create/',
|
|
|
|
+ method: 'POST',
|
|
|
|
+ data: filteredRequestData,
|
|
|
|
+ })
|
|
|
|
+ console.log('🚀 ~ createCampaigns ~ resp-->>', resp)
|
|
|
|
+ respCampaignId.value = resp.data.campaignId
|
|
|
|
+ if (respCampaignId.value) {
|
|
|
|
+ campaignSave.value = false
|
|
|
|
+ campaignLoading.value = false
|
|
|
|
+ } else {
|
|
|
|
+ campaignLoading.value = false
|
|
|
|
+ ElMessage.error('广告活动创建失败!')
|
|
|
|
+ }
|
|
|
|
+ } catch (error) {
|
|
|
|
+ console.error('请求失败:', error)
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+async function submitCampaignForm(formEl: FormInstance | undefined) {
|
|
|
|
+ if (!formEl) return
|
|
|
|
+ await formEl.validate((valid, fields) => {
|
|
|
|
+ if (valid) {
|
|
|
|
+ console.log('submit!')
|
|
|
|
+ campaignLoading.value = true
|
|
|
|
+ createCampaigns()
|
|
|
|
+ } else {
|
|
|
|
+ console.log('error submit!', fields)
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+async function createGroups() {
|
|
|
|
+ try {
|
|
|
|
+ // 构建请求数据
|
|
|
|
+ const requestData = {
|
|
|
|
+ profile_id: profile.value.profile_id,
|
|
|
|
+ campaignId: respCampaignId,
|
|
|
|
+ name: adGroupRuleForm.adGroupName,
|
|
|
|
+ defaultBid: adGroupRuleForm.defaultBidInp,
|
|
|
|
+ state: 'PAUSED',
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 过滤掉 undefined 或空的可选字段
|
|
|
|
+ const filteredRequestData = Object.fromEntries(Object.entries(requestData).filter(([_, v]) => v != null))
|
|
|
|
+
|
|
|
|
+ const resp = await request({
|
|
|
|
+ url: '/api/ad_manage/spgroups/create/',
|
|
|
|
+ method: 'POST',
|
|
|
|
+ data: filteredRequestData,
|
|
|
|
+ })
|
|
|
|
+ console.log('🚀 ~ createCampaigns ~ resp-->>', resp)
|
|
|
|
+
|
|
|
|
+ } catch (error) {
|
|
|
|
+ console.error('请求失败:', error)
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+async function submitGroupsForm(formEl: FormInstance | undefined) {
|
|
|
|
+ if (!formEl) return
|
|
|
|
+ await formEl.validate((valid, fields) => {
|
|
|
|
+ if (valid) {
|
|
|
|
+ console.log('submit!')
|
|
|
|
+ createGroups()
|
|
|
|
+ } else {
|
|
|
|
+ console.log('error submit!', fields)
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
// 修改表头样式
|
|
// 修改表头样式
|
|
const headerCellStyle = (args) => {
|
|
const headerCellStyle = (args) => {
|
|
if (args.rowIndex === 0) {
|
|
if (args.rowIndex === 0) {
|
|
@@ -1585,7 +1850,6 @@ div {
|
|
width: 100%;
|
|
width: 100%;
|
|
// margin: 10px 0 10px 10px;
|
|
// margin: 10px 0 10px 10px;
|
|
margin-right: 10px;
|
|
margin-right: 10px;
|
|
- border: none;
|
|
|
|
}
|
|
}
|
|
.single-line {
|
|
.single-line {
|
|
color: rgb(30, 33, 41);
|
|
color: rgb(30, 33, 41);
|
|
@@ -1624,8 +1888,12 @@ div {
|
|
::v-deep(.category-tabs .el-tabs__nav) {
|
|
::v-deep(.category-tabs .el-tabs__nav) {
|
|
margin-left: 20px;
|
|
margin-left: 20px;
|
|
}
|
|
}
|
|
-// el-tree自定义样式
|
|
|
|
|
|
+::v-deep(.goods-orientation-tabs #tab-1) {
|
|
|
|
+ /* 商品定向Tab栏 */
|
|
|
|
+ border-right: 0;
|
|
|
|
+}
|
|
.custom-tree-node {
|
|
.custom-tree-node {
|
|
|
|
+ /* el-tree自定义样式 */
|
|
flex: 1;
|
|
flex: 1;
|
|
display: flex;
|
|
display: flex;
|
|
align-items: center;
|
|
align-items: center;
|
|
@@ -1633,8 +1901,8 @@ div {
|
|
font-size: 14px;
|
|
font-size: 14px;
|
|
padding-right: 8px;
|
|
padding-right: 8px;
|
|
}
|
|
}
|
|
-/* 弹窗样式 */
|
|
|
|
.dialog-head {
|
|
.dialog-head {
|
|
|
|
+ /* 弹窗样式 */
|
|
display: flex;
|
|
display: flex;
|
|
flex-direction: row;
|
|
flex-direction: row;
|
|
justify-content: space-between;
|
|
justify-content: space-between;
|