|
@@ -1,24 +1,25 @@
|
|
|
<template>
|
|
|
<div style="width: 100%; margin-top: 20px">
|
|
|
<el-divider content-position="left">
|
|
|
- <span style="font-size: 18px;font-weight: 700;">关键词定向</span>
|
|
|
+ <span style="font-size: 18px; font-weight: 700">关键词定向</span>
|
|
|
</el-divider>
|
|
|
- <div style="width: 100%; height: 600px; margin-top: 10px; display: flex; border: 1px solid #e5e7eb; border-radius: 6px">
|
|
|
+ <div style="width: 100%; height: 600px; display: flex; border: 1px solid #e5e7eb; border-radius: 6px" v-loading="keywordsLoading">
|
|
|
<div style="width: 50%; border-right: 1px solid #e5e7eb">
|
|
|
<el-tabs v-model="keyWordsTabs" class="demo-tabs">
|
|
|
- <div style="margin: 8px 10px 10px 10px">
|
|
|
- <p style=" margin-bottom: -5px; font-weight: 500; color: #616266">竞价:</p>
|
|
|
- <div style="display: flex; align-items: center; margin-left:-7px">
|
|
|
+ <div style="margin: 8px">
|
|
|
+
|
|
|
+ <div style="display: flex; align-items: center">
|
|
|
+ <div style="min-width: 40px; margin-left: 8px; font-weight: 500; color: #616266">竞价:</div>
|
|
|
<el-select v-model="bidType" class="m-2" placeholder="Select" style="width: 450px">
|
|
|
- <el-option v-for="item in bidTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
|
|
|
+ <el-option v-for="item in bidTypeOptions" :key="item.value" :label="item.label" :value="item.value" :disabled="item.disabled" />
|
|
|
</el-select>
|
|
|
- <el-input v-model="bidInput" placeholder="Please input">
|
|
|
+ <el-input v-model="bidInput" :disabled="!(bidType == 'customBid')" placeholder="Please input">
|
|
|
<template #prepend>$</template>
|
|
|
</el-input>
|
|
|
</div>
|
|
|
<div style="display: flex; align-items: center;">
|
|
|
- <span style="margin: 0 10px 0 0; font-weight: 500; color: #616266">匹配类型: </span>
|
|
|
- <el-checkbox v-model="widelyType" label="广泛" />
|
|
|
+ <span style="margin: 0 10px 0 8px; font-weight: 500; color: #616266;">匹配类型: </span>
|
|
|
+ <el-checkbox v-model="broadType" label="广泛" />
|
|
|
<el-checkbox v-model="phraseType" label="词组" />
|
|
|
<el-checkbox v-model="exactType" label="精确" />
|
|
|
</div>
|
|
@@ -26,18 +27,20 @@
|
|
|
<el-tab-pane label="建议" name="first">
|
|
|
<el-table
|
|
|
height="425"
|
|
|
- v-loading="loading"
|
|
|
- :data="keywordsTableData"
|
|
|
+ style="width: 100%; padding-left: 5px"
|
|
|
+ :data="keyWordsTableData"
|
|
|
:header-cell-style="headerCellStyle"
|
|
|
:header-row-style="changeKeyWordsTableHeader">
|
|
|
<el-table-column prop="asin" label="关键词"> </el-table-column>
|
|
|
- <el-table-column prop="name" label="匹配类型"> </el-table-column>
|
|
|
- <el-table-column prop="name" label="建议出价" width="120"> </el-table-column>
|
|
|
+ <el-table-column prop="matchType" label="匹配类型"> </el-table-column>
|
|
|
+ <el-table-column prop="adviceBid" label="建议出价" width="120"> </el-table-column>
|
|
|
</el-table>
|
|
|
</el-tab-pane>
|
|
|
<el-tab-pane label="输入" name="second">
|
|
|
- <el-input v-model="keyWordsTextarea" :rows="10" type="textarea" disabled="true" style="padding-left: 5px" />
|
|
|
- <!-- <el-button type="primary" size="small" text bg @click="addKeyWords">添加</el-button> -->
|
|
|
+ <el-input v-model="keyWordsTextarea" :rows="10" type="textarea" style="padding-left: 5px;" />
|
|
|
+ <div style="display: flex; flex-direction: row-reverse; margin-top: 10px;">
|
|
|
+ <el-button type="primary" text bg @click="addKeyWords">添加</el-button>
|
|
|
+ </div>
|
|
|
</el-tab-pane>
|
|
|
</el-tabs>
|
|
|
</div>
|
|
@@ -45,8 +48,10 @@
|
|
|
<el-card class="box-card" shadow="never" style="border: none">
|
|
|
<template #header>
|
|
|
<div class="card-header">
|
|
|
- <span style="font-weight: 550; font-size: 15px; color: #1f2128">已添加: {{ }}</span>
|
|
|
- <el-button class="button" text bg @click="delAllKeyWords">全部删除</el-button>
|
|
|
+ <span style="font-weight: 550; font-size: 15px; color: #1f2128">已添加: {{ addedKeyWordsTableData.length }}</span>
|
|
|
+ <span style="color: #529b2e">成功: {{ successCount }}</span>
|
|
|
+ <span style="color: #c45656">失败: {{ errorCount }}</span>
|
|
|
+ <el-button class="button" type="danger" text bg @click="delAllKeyWords">全部删除</el-button>
|
|
|
</div>
|
|
|
</template>
|
|
|
<div class="card-body" body-style="padding-bottom: -20px;">
|
|
@@ -56,14 +61,23 @@
|
|
|
:header-row-style="changeKeyWordsTableHeader"
|
|
|
:header-cell-style="headerCellStyle">
|
|
|
<el-table-column prop="keyword" label="关键词" width="auto" />
|
|
|
- <el-table-column prop="bid" label="出价" />
|
|
|
+ <el-table-column prop="matchType" label="匹配类型" />
|
|
|
+ <el-table-column prop="bid" label="出价">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-input v-model="scope.row.bid" placeholder="Please input bid" />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
<el-table-column prop="suggestBid" label="建议出价" />
|
|
|
- <el-table-column prop="operate" label="操作" width="60" align="right" />
|
|
|
+ <el-table-column prop="operate" label="操作" width="60" align="right">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-button type="danger" size="small" link @click="delSingleKeyWord(scope)">删除</el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
</el-table>
|
|
|
</div>
|
|
|
</el-card>
|
|
|
<div style="display: flex; justify-content: space-around; padding-top: 0px">
|
|
|
- <el-button type="primary" plain :disabled="!addedKeyWordsTableData.length">保存</el-button>
|
|
|
+ <el-button type="primary" plain @click="keyWordsSave" :disabled="!addedKeyWordsTableData.length">保存</el-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
@@ -71,26 +85,27 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
-import { ref } from 'vue';
|
|
|
-
|
|
|
+import { Ref, inject, ref, watch } from 'vue'
|
|
|
+import { ElMessage } from 'element-plus'
|
|
|
+import { useShopInfo } from '/@/stores/shopInfo'
|
|
|
+import { usePublicData } from '/@/stores/publicData'
|
|
|
+import { storeToRefs } from 'pinia'
|
|
|
+import { useRouter } from 'vue-router'
|
|
|
+import { request } from '/@/utils/service'
|
|
|
|
|
|
+const respCampaignId = inject<Ref>('respCampaignId')
|
|
|
+const respAdGroupId = inject<Ref>('respAdGroupId')
|
|
|
+const shopInfo = useShopInfo()
|
|
|
+const { profile } = storeToRefs(shopInfo)
|
|
|
|
|
|
const keyWordsTabs = ref('first')
|
|
|
-const bidInput = ref('0.75')
|
|
|
-const keywordsTableData = ref([]) // 关键词定向左侧表格数据
|
|
|
-const addedKeyWordsTableData = ref([]) // 关键词定向右侧表格数据
|
|
|
-const keyWordsTextarea = ref('')
|
|
|
-
|
|
|
-let widelyType = ref(true)
|
|
|
-let phraseType = ref(true)
|
|
|
-let exactType = ref(true)
|
|
|
-
|
|
|
-const loading = ref(false)
|
|
|
const bidType = ref('customBid')
|
|
|
+const keywordsLoading = ref(false)
|
|
|
const bidTypeOptions = [
|
|
|
{
|
|
|
value: 'suggestBid',
|
|
|
label: '建议出价',
|
|
|
+ disabled: true
|
|
|
},
|
|
|
{
|
|
|
value: 'customBid',
|
|
@@ -99,23 +114,136 @@ const bidTypeOptions = [
|
|
|
{
|
|
|
value: 'defaultBid',
|
|
|
label: '默认出价',
|
|
|
+ disabled: true
|
|
|
},
|
|
|
]
|
|
|
|
|
|
+const bidInput = ref('0.75')
|
|
|
+const keyWordsTableData = ref([]) // 关键词定向左侧表格数据
|
|
|
+const addedKeyWordsTableData = ref([]) // 关键词定向右侧表格数据
|
|
|
+const keyWordsTextarea = ref('')
|
|
|
+let broadType = ref(true)
|
|
|
+let phraseType = ref(true)
|
|
|
+let exactType = ref(true)
|
|
|
+const MATCH_TYPE = {
|
|
|
+ BROAD: '广泛',
|
|
|
+ PHRASE: '词组',
|
|
|
+ EXACT: '精确',
|
|
|
+}
|
|
|
+const MATCH_TYPE_MAP = {
|
|
|
+ 广泛: 'BROAD',
|
|
|
+ 词组: 'PHRASE',
|
|
|
+ 精确: 'EXACT',
|
|
|
+}
|
|
|
+const successCount = ref('')
|
|
|
+const errorCount = ref('')
|
|
|
+
|
|
|
+function addKeyWords() {
|
|
|
+ const trimmedText = keyWordsTextarea.value.trim()
|
|
|
+ const items = trimmedText.split(/,|\n/)
|
|
|
|
|
|
+ items.forEach((item) => {
|
|
|
+ const trimmedItem = item.trim()
|
|
|
+ if (trimmedItem) {
|
|
|
+ if (broadType.value) {
|
|
|
+ addKeyWordEntry(trimmedItem, MATCH_TYPE.BROAD)
|
|
|
+ }
|
|
|
+ if (phraseType.value) {
|
|
|
+ addKeyWordEntry(trimmedItem, MATCH_TYPE.PHRASE)
|
|
|
+ }
|
|
|
+ if (exactType.value) {
|
|
|
+ addKeyWordEntry(trimmedItem, MATCH_TYPE.EXACT)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ ElMessage({
|
|
|
+ message: '有空项目,未被添加到列表中',
|
|
|
+ type: 'warning',
|
|
|
+ })
|
|
|
+ }
|
|
|
+ })
|
|
|
+ keyWordsTextarea.value = ''
|
|
|
+}
|
|
|
+
|
|
|
+function addKeyWordEntry(keyword, matchType) {
|
|
|
+ // let bidValue
|
|
|
+ // switch (bidType.value) {
|
|
|
+ // case 'customBid':
|
|
|
+ // bidValue = bidInput.value
|
|
|
+ // break
|
|
|
+ // case 'defaultBid':
|
|
|
+ // // bidValue = adGroupRuleForm.defaultBidInp
|
|
|
+ // break
|
|
|
+ // default:
|
|
|
+ // bidValue = ''
|
|
|
+ // }
|
|
|
+
|
|
|
+ let keyWordEntry = {
|
|
|
+ keyword: keyword,
|
|
|
+ matchType: matchType,
|
|
|
+ bid: bidInput.value,
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!addedKeyWordsTableData.value.some((n) => n.keyword === keyWordEntry.keyword && n.matchType === keyWordEntry.matchType)) {
|
|
|
+ addedKeyWordsTableData.value.push(keyWordEntry)
|
|
|
+ } else {
|
|
|
+ ElMessage({
|
|
|
+ message: `关键词 ${keyword} (${matchType}) 已存在,未被添加到列表中`,
|
|
|
+ type: 'warning',
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function delSingleKeyWord(scope) {
|
|
|
+ const index = addedKeyWordsTableData.value.findIndex((item) => item.keyword === scope.row.keyword && item.matchType === scope.row.matchType)
|
|
|
+ if (index !== -1) {
|
|
|
+ addedKeyWordsTableData.value.splice(index, 1)
|
|
|
+ } else {
|
|
|
+ console.log('无效的索引,无法删除条目')
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
function delAllKeyWords() {
|
|
|
- // 删除已经添加的关键词
|
|
|
addedKeyWordsTableData.value = []
|
|
|
}
|
|
|
|
|
|
-function addKeyWords() {
|
|
|
- // 添加关键词
|
|
|
- if (keyWordsTextarea.value) {
|
|
|
- addedKeyWordsTableData.value.push({
|
|
|
- keyWords: keyWordsTextarea.value,
|
|
|
+async function keyWordsSave() {
|
|
|
+ keywordsLoading.value = true
|
|
|
+ successCount.value = ''
|
|
|
+ errorCount.value = ''
|
|
|
+ const keywordList = addedKeyWordsTableData.value.map((kw) => ({
|
|
|
+ keywordText: kw.keyword,
|
|
|
+ bid: kw.bid,
|
|
|
+ matchType: MATCH_TYPE_MAP[kw.matchType],
|
|
|
+ }))
|
|
|
+
|
|
|
+ const requestData = {
|
|
|
+ profile_id: profile.value.profile_id,
|
|
|
+ campaignId: respCampaignId.value,
|
|
|
+ adGroupId: respAdGroupId.value,
|
|
|
+ keywordlist: keywordList,
|
|
|
+ }
|
|
|
+ const filteredRequestData = Object.fromEntries(Object.entries(requestData).filter(([_, v]) => v != null))
|
|
|
+ try {
|
|
|
+ const resp = await request({
|
|
|
+ url: '/api/ad_manage/sptargets/add/keywords/',
|
|
|
+ method: 'POST',
|
|
|
+ data: filteredRequestData,
|
|
|
})
|
|
|
- keyWordsTextarea.value = ''
|
|
|
+ if (resp.data.success.length !== 0) {
|
|
|
+ ElMessage({
|
|
|
+ message: '关键词创建成功',
|
|
|
+ type: 'success',
|
|
|
+ })
|
|
|
+ successCount.value = resp.data.success.length
|
|
|
+ errorCount.value = resp.data.error.length
|
|
|
+ delAllKeyWords()
|
|
|
+ } else {
|
|
|
+ ElMessage.error('关键词创建失败!')
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('请求失败:', error)
|
|
|
+ } finally {
|
|
|
+ keywordsLoading.value = false
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -135,7 +263,6 @@ function changeKeyWordsTableHeader(args) {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|