|
@@ -0,0 +1,444 @@
|
|
|
|
+<template>
|
|
|
|
+ <div prop="commodity" style="width: 100%" v-loading="productLoading">
|
|
|
|
+ <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="productTabs" class="demo-tabs">
|
|
|
|
+ <el-tab-pane label="搜索" name="first">
|
|
|
|
+ <div style="margin-bottom: 10px">
|
|
|
|
+ <el-input v-model="searchInp" placeholder="Please input" class="input-with-select" @change="inpChange" clearable>
|
|
|
|
+ <template #prepend>
|
|
|
|
+ <el-select v-model="leftSelect" style="width: 100px" @change="selChange">
|
|
|
|
+ <el-option label="名称" value="name" />
|
|
|
|
+ <el-option label="ASIN" value="asin" />
|
|
|
|
+ <el-option label="SKU" value="sku" />
|
|
|
|
+ </el-select>
|
|
|
|
+ </template>
|
|
|
|
+ <template #append>
|
|
|
|
+ <el-select v-model="rightSelect" style="width: 100px">
|
|
|
|
+ <el-option label="最新优先" value="latest" />
|
|
|
|
+ <el-option label="最早优先" value="earliest" />
|
|
|
|
+ <el-option label="优选广告" value="optimal" />
|
|
|
|
+ </el-select>
|
|
|
|
+ </template>
|
|
|
|
+ </el-input>
|
|
|
|
+ </div>
|
|
|
|
+ <el-table
|
|
|
|
+ height="490"
|
|
|
|
+ style="width: 100%"
|
|
|
|
+ v-loading="loading"
|
|
|
|
+ :data="productTableData"
|
|
|
|
+ :header-cell-style="headerCellStyle"
|
|
|
|
+ @selection-change="handleSelectionChange">
|
|
|
|
+ <el-table-column type="selection" width="50" />
|
|
|
|
+ <el-table-column prop="asin" label="商品">
|
|
|
|
+ <template #default="scope">
|
|
|
|
+ <div style="display: flex; align-items: center">
|
|
|
|
+ <div style="margin-right: 8px; line-height: normal">
|
|
|
|
+ <el-image class="img-box" :src="scope.row.image_link" />
|
|
|
|
+ </div>
|
|
|
|
+ <div>
|
|
|
|
+ <el-tooltip class="box-item" effect="dark" :content="scope.row.title" placement="top">
|
|
|
|
+ <div class="single-line">{{ scope.row.title ? scope.row.title : '--' }}</div>
|
|
|
|
+ </el-tooltip>
|
|
|
|
+ <div class="data-color">
|
|
|
|
+ <span style="font-weight: 500; color: rgb(30, 33, 41)">${{ scope.row.price ? scope.row.price : '--' }}</span>
|
|
|
|
+ <span style="margin: 0 5px; color: #cacdd4">|</span>
|
|
|
|
+ <span style="color: #6d7784">{{ scope.row.quantity }}</span>
|
|
|
|
+ </div>
|
|
|
|
+ <span>
|
|
|
|
+ ASIN: <span class="data-color" style="margin-right: 8px">{{ scope.row.asin ? scope.row.asin : '--' }}</span>
|
|
|
|
+ </span>
|
|
|
|
+ <span>
|
|
|
|
+ SKU: <span class="data-color">{{ scope.row.sku ? scope.row.sku : '--' }}</span>
|
|
|
|
+ </span>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </template>
|
|
|
|
+ </el-table-column>
|
|
|
|
+ <el-table-column prop="name" label="Name" width="120" align="right">
|
|
|
|
+ <template #header>
|
|
|
|
+ <el-button type="primary" size="normal" link @click="handleGoodsAdd">添加已选中</el-button>
|
|
|
|
+ </template>
|
|
|
|
+ <template #default="scope">
|
|
|
|
+ <el-button type="primary" size="small" @click="addSingleGoods(scope)" text>添加</el-button>
|
|
|
|
+ </template>
|
|
|
|
+ </el-table-column>
|
|
|
|
+ </el-table>
|
|
|
|
+ <el-pagination
|
|
|
|
+ @current-change="handleCurrentChange"
|
|
|
|
+ @size-change="handleSizeChange"
|
|
|
|
+ :current-page="currentPage"
|
|
|
|
+ :page-size="pageSize"
|
|
|
|
+ :total="totalItems"
|
|
|
|
+ layout="prev, pager, next" />
|
|
|
|
+ </el-tab-pane>
|
|
|
|
+ <el-tab-pane label="输入" name="second">
|
|
|
|
+ <el-input
|
|
|
|
+ style="padding: 10px"
|
|
|
|
+ v-model="productTextarea"
|
|
|
|
+ :rows="20"
|
|
|
|
+ type="textarea"
|
|
|
|
+ placeholder="请输入ASIN,多个ASIN使用逗号、空格或换行符分隔。(未完成)"
|
|
|
|
+ maxlength="11000" />
|
|
|
|
+ <div style="display: flex; flex-direction: row-reverse; margin-top: 10px">
|
|
|
|
+ <el-button v-for="button in buttons" :key="button.text" :type="button.type" link @click="addGods">{{ button.text }}</el-button>
|
|
|
|
+ </div>
|
|
|
|
+ </el-tab-pane>
|
|
|
|
+ </el-tabs>
|
|
|
|
+ </div>
|
|
|
|
+ <div style="width: 50%">
|
|
|
|
+ <el-card class="box-card" shadow="never" style="border: 0">
|
|
|
|
+ <template #header>
|
|
|
|
+ <div class="card-header">
|
|
|
|
+ <span style="font-weight: 550; font-size: 15px; color: #1f2128">已添加: {{ addedTableData.length }}</span>
|
|
|
|
+ <el-tooltip content="添加最少3件商品。我们建议添加至少5件商品,以降低在商品缺货时出现广告活动暂停的可能性。" placement="top">
|
|
|
|
+ <el-text type="warning" truncated style="width: 350px;">添加最少3件商品。我们建议添加至少5件商品,以降低在商品缺货时出现广告活动暂停的可能性</el-text>
|
|
|
|
+ </el-tooltip>
|
|
|
|
+ <el-button class="button" text bg @click="delAllGoods">全部删除</el-button>
|
|
|
|
+ </div>
|
|
|
|
+ </template>
|
|
|
|
+ <div class="card-body"></div>
|
|
|
|
+ </el-card>
|
|
|
|
+ <div style="padding: 0 10px 0 10px; margin-top: -12px">
|
|
|
|
+ <el-table
|
|
|
|
+ :data="addedTableData"
|
|
|
|
+ height="475"
|
|
|
|
+ style="width: 100%"
|
|
|
|
+ :header-cell-style="headerCellStyle"
|
|
|
|
+ @selection-change="handleAddedGoodsChange">
|
|
|
|
+ <el-table-column type="selection" width="50" />
|
|
|
|
+ <el-table-column prop="asin" label="ASIN">
|
|
|
|
+ <template #default="scope">
|
|
|
|
+ <div style="display: flex; align-items: center">
|
|
|
|
+ <div style="margin-right: 8px; line-height: normal">
|
|
|
|
+ <el-image class="img-box" :src="scope.row.image_link" />
|
|
|
|
+ </div>
|
|
|
|
+ <div>
|
|
|
|
+ <el-tooltip class="box-item" effect="dark" :content="scope.row.title" placement="top">
|
|
|
|
+ <div class="single-line">{{ scope.row.title ? scope.row.title : '--' }}</div>
|
|
|
|
+ </el-tooltip>
|
|
|
|
+ <div class="data-color">
|
|
|
|
+ <span style="font-weight: 500; color: rgb(30, 33, 41)">${{ scope.row.price ? scope.row.price : '--' }}</span>
|
|
|
|
+ <span style="margin: 0 5px; color: #cacdd4">|</span>
|
|
|
|
+ <span style="color: #6d7784">{{ scope.row.quantity }}</span>
|
|
|
|
+ </div>
|
|
|
|
+ <span
|
|
|
|
+ >ASIN:
|
|
|
|
+ <span class="data-color" style="margin-right: 8px">{{ scope.row.asin ? scope.row.asin : '--' }}</span>
|
|
|
|
+ </span>
|
|
|
|
+ <span
|
|
|
|
+ >SKU:
|
|
|
|
+ <span class="data-color">{{ scope.row.sku ? scope.row.sku : '--' }}</span>
|
|
|
|
+ </span>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </template>
|
|
|
|
+ </el-table-column>
|
|
|
|
+ <el-table-column prop="name" label="Name" width="120" align="right">
|
|
|
|
+ <template #header>
|
|
|
|
+ <el-button type="primary" size="normal" link @click="delSelectedGoods">删除已选中</el-button>
|
|
|
|
+ </template>
|
|
|
|
+ <template #default="scope">
|
|
|
|
+ <el-button type="primary" size="small" @click="delSingleGoods(scope)" text>删除</el-button>
|
|
|
|
+ </template>
|
|
|
|
+ </el-table-column>
|
|
|
|
+ </el-table>
|
|
|
|
+ </div>
|
|
|
|
+ <div style="display: flex; justify-content: space-around; padding-top: 5px">
|
|
|
|
+ <el-button type="primary" plain :disabled="productSave" @click="submitProductForm">保存</el-button>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+</template>
|
|
|
|
+
|
|
|
|
+<script setup lang="ts">
|
|
|
|
+import type { TabsPaneContext } from 'element-plus'
|
|
|
|
+import { ElMessage } from 'element-plus'
|
|
|
|
+import { storeToRefs } from 'pinia'
|
|
|
|
+import type { Ref } from 'vue'
|
|
|
|
+import { inject, onMounted, ref, watch } from 'vue'
|
|
|
|
+import { useShopInfo } from '/@/stores/shopInfo'
|
|
|
|
+import { request } from '/@/utils/service'
|
|
|
|
+
|
|
|
|
+const shopInfo = useShopInfo()
|
|
|
|
+const { profile } = storeToRefs(shopInfo)
|
|
|
|
+const productTextarea = ref('')
|
|
|
|
+const productLoading = ref(false)
|
|
|
|
+let addedAdsTableItems = ref([])
|
|
|
|
+const currentPage = ref() // 当前页
|
|
|
|
+const pageSize = ref(20) // 每页显示条目数
|
|
|
|
+const totalItems = ref() // 数据总量
|
|
|
|
+const productTableData = ref([]) // 左侧表格数据
|
|
|
|
+const loading = ref(false)
|
|
|
|
+let addedTableData = ref([])
|
|
|
|
+let selections = []
|
|
|
|
+let addedSels = []
|
|
|
|
+const searchInp = ref('')
|
|
|
|
+const leftSelect = ref('name')
|
|
|
|
+let productSave = ref(true)
|
|
|
|
+const buttons = [{ type: 'primary', text: '添加' }] as const
|
|
|
|
+const productTabs = ref('first')
|
|
|
|
+const rightSelect = ref('latest')
|
|
|
|
+const respCampaignId = inject<Ref>('respCampaignId')
|
|
|
|
+const respCampaignName = inject<Ref>('respCampaignName')
|
|
|
|
+const respAdGroupId = inject<Ref>('respAdGroupId')
|
|
|
|
+
|
|
|
|
+function setTableData(asin = '', sku = '') {
|
|
|
|
+ return request({
|
|
|
|
+ url: '/api/sellers/listings/our/',
|
|
|
|
+ method: 'GET',
|
|
|
|
+ params: {
|
|
|
|
+ page: currentPage.value,
|
|
|
|
+ limit: pageSize.value,
|
|
|
|
+ profile_id: profile.value.profile_id,
|
|
|
|
+ asin,
|
|
|
|
+ sku,
|
|
|
|
+ },
|
|
|
|
+ })
|
|
|
|
+ .then((resp) => {
|
|
|
|
+ productTableData.value = resp.data
|
|
|
|
+ totalItems.value = resp.total
|
|
|
|
+ currentPage.value = resp.page
|
|
|
|
+ loading.value = false
|
|
|
|
+ })
|
|
|
|
+ .catch((error) => {
|
|
|
|
+ console.error('Error fetching data:', error)
|
|
|
|
+ loading.value = false
|
|
|
|
+ })
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function addSingleGoods(scope) {
|
|
|
|
+ // console.log('scope', scope.row)
|
|
|
|
+ const isAlreadyAdded = addedTableData.value.some((item) => item.sku === scope.row.sku)
|
|
|
|
+ if (!isAlreadyAdded) {
|
|
|
|
+ addedTableData.value.push(scope.row)
|
|
|
|
+ } else {
|
|
|
|
+ console.log('Item is already added.')
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function addGods() {
|
|
|
|
+ const inputData = productTextarea.value
|
|
|
|
+ const asins = inputData.split(/[\n,]+/)
|
|
|
|
+
|
|
|
|
+ asins.forEach((asin) => {
|
|
|
|
+ if (asin.trim()) {
|
|
|
|
+ setTableData(asin.trim())
|
|
|
|
+ .then((response) => {
|
|
|
|
+ console.log(`Data for ASIN ${asin}:`, response) // 更新这里来正确地访问数据
|
|
|
|
+ })
|
|
|
|
+ .catch((error) => {
|
|
|
|
+ console.error(`Error fetching data for ASIN ${asin}:`, error)
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function delSingleGoods(scope) {
|
|
|
|
+ const index = addedTableData.value.findIndex((item) => item.sku === scope.row.sku)
|
|
|
|
+ if (index !== -1) {
|
|
|
|
+ addedTableData.value.splice(index, 1)
|
|
|
|
+ console.log('Item removed successfully.')
|
|
|
|
+ } else {
|
|
|
|
+ console.log('Item not found.')
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function delAllGoods() {
|
|
|
|
+ addedTableData.value = []
|
|
|
|
+ // addedTableData.value.splice(0, addedTableData.value.length)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// 删除第二个table中已经选中的项
|
|
|
|
+function delSelectedGoods() {
|
|
|
|
+ addedTableData.value = addedTableData.value.filter((item) => !addedSels.includes(item))
|
|
|
|
+ addedSels = []
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function inpChange(e) {
|
|
|
|
+ const value = e
|
|
|
|
+ if (leftSelect.value === 'asin') {
|
|
|
|
+ loading.value = true
|
|
|
|
+ setTableData(value)
|
|
|
|
+ } else if (leftSelect.value === 'sku') {
|
|
|
|
+ loading.value = true
|
|
|
|
+ setTableData('', value)
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function selChange(e) {
|
|
|
|
+ console.log('e', e)
|
|
|
|
+ const value = e
|
|
|
|
+ if (leftSelect.value === 'asin' && searchInp.value) {
|
|
|
|
+ loading.value = true
|
|
|
|
+ setTableData(value)
|
|
|
|
+ } else if (leftSelect.value === 'sku' && searchInp.value) {
|
|
|
|
+ loading.value = true
|
|
|
|
+ setTableData('', value)
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+// 点击表格选项触发事件
|
|
|
|
+function handleSelectionChange(selection) {
|
|
|
|
+ selections = selection
|
|
|
|
+}
|
|
|
|
+// 获取addedTable中已选中的项
|
|
|
|
+function handleAddedGoodsChange(selection) {
|
|
|
|
+ addedSels = selection
|
|
|
|
+}
|
|
|
|
+// 添加已选中的项
|
|
|
|
+function handleGoodsAdd() {
|
|
|
|
+ // 过滤掉已经存在于addedData.value中的项
|
|
|
|
+ const newSelections = selections.filter(
|
|
|
|
+ (sel) => !addedTableData.value.some((added) => added.sku === sel.sku) // 使用sku作为唯一标识
|
|
|
|
+ )
|
|
|
|
+ // 如果有新的不重复项,加入到addedData.value中
|
|
|
|
+ if (newSelections.length > 0) {
|
|
|
|
+ addedTableData.value.push(...newSelections)
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+// 点击Tab
|
|
|
|
+const handleGoodsTabs = (tab: TabsPaneContext, event: Event) => {
|
|
|
|
+ console.log(tab, event)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function isItemInList(item, list) {
|
|
|
|
+ return list.some((listItem) => listItem.sku === item.sku && listItem.asin === item.asin)
|
|
|
|
+}
|
|
|
|
+// 监听商品右侧表格已添加的数据并转化数据格式
|
|
|
|
+watch(
|
|
|
|
+ addedTableData,
|
|
|
|
+ (newValue, oldValue) => {
|
|
|
|
+ newValue.forEach((item) => {
|
|
|
|
+ if (!isItemInList(item, addedAdsTableItems.value)) {
|
|
|
|
+ addedAdsTableItems.value.push({ sku: item.sku, asin: item.asin })
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ if (addedTableData.value.length !== 0) {
|
|
|
|
+ productSave.value = false
|
|
|
|
+ } else {
|
|
|
|
+ productSave.value = true
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ { deep: true }
|
|
|
|
+)
|
|
|
|
+
|
|
|
|
+async function createAds() {
|
|
|
|
+ try {
|
|
|
|
+ const requestData = {
|
|
|
|
+ profile_id: profile.value.profile_id,
|
|
|
|
+ campaignId: respCampaignId.value,
|
|
|
|
+ adGroupId: respAdGroupId.value,
|
|
|
|
+ asinsku: addedAdsTableItems.value,
|
|
|
|
+ state: 'PAUSED',
|
|
|
|
+ }
|
|
|
|
+ const filteredRequestData = Object.fromEntries(Object.entries(requestData).filter(([_, v]) => v != null))
|
|
|
|
+ const resp = await request({
|
|
|
|
+ url: '/api/ad_manage/spads/create/',
|
|
|
|
+ method: 'POST',
|
|
|
|
+ data: filteredRequestData,
|
|
|
|
+ })
|
|
|
|
+ console.log('🚀 ~ createAds ~ resp-->>', resp)
|
|
|
|
+ productLoading.value = false
|
|
|
|
+ if (resp.data.success.length > 0) {
|
|
|
|
+ productSave.value = false
|
|
|
|
+ addedTableData.value = []
|
|
|
|
+ ElMessage({
|
|
|
|
+ message: '商品创建成功',
|
|
|
|
+ type: 'success',
|
|
|
|
+ })
|
|
|
|
+ } else {
|
|
|
|
+ ElMessage.error('商品创建失败!')
|
|
|
|
+ }
|
|
|
|
+ } catch (error) {
|
|
|
|
+ console.error('请求失败:', error)
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function submitProductForm() {
|
|
|
|
+ productLoading.value = true
|
|
|
|
+ createAds()
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// 处理分页器当前页变化
|
|
|
|
+function handleCurrentChange(newPage) {
|
|
|
|
+ currentPage.value = newPage
|
|
|
|
+ loading.value = true
|
|
|
|
+ setTableData()
|
|
|
|
+}
|
|
|
|
+// 处理分页器每页显示条目数变化
|
|
|
|
+function handleSizeChange(newSize) {
|
|
|
|
+ pageSize.value = newSize
|
|
|
|
+ currentPage.value = 1 // 重置到第一页
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const headerCellStyle = (args) => {
|
|
|
|
+ if (args.rowIndex === 0) {
|
|
|
|
+ return {
|
|
|
|
+ backgroundColor: 'rgba(245, 245, 245, 0.9)',
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+onMounted(() => {
|
|
|
|
+ setTableData()
|
|
|
|
+})
|
|
|
|
+</script>
|
|
|
|
+
|
|
|
|
+<style lang="scss" scoped>
|
|
|
|
+::v-deep(.el-form--default.el-form--label-top .el-form-item .el-form-item__label) {
|
|
|
|
+ font-weight: 500;
|
|
|
|
+}
|
|
|
|
+.demo-tabs > .el-tabs__content {
|
|
|
|
+ padding: 52px;
|
|
|
|
+ color: #6b778c;
|
|
|
|
+ font-size: 32px;
|
|
|
|
+ font-weight: 600;
|
|
|
|
+}
|
|
|
|
+/* 广告组商品Tab栏 */
|
|
|
|
+::v-deep(.el-tabs__nav-scroll) {
|
|
|
|
+ overflow: hidden;
|
|
|
|
+ margin-left: 20px;
|
|
|
|
+}
|
|
|
|
+::v-deep(.el-table__inner-wrapper::before) {
|
|
|
|
+ background-color: white;
|
|
|
|
+}
|
|
|
|
+// 表格内容边距
|
|
|
|
+div {
|
|
|
|
+ & #pane-first,
|
|
|
|
+ & #pane-second {
|
|
|
|
+ margin: 10px;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+.card-header {
|
|
|
|
+ display: flex;
|
|
|
|
+ justify-content: space-between;
|
|
|
|
+ align-items: center;
|
|
|
|
+}
|
|
|
|
+.box-card {
|
|
|
|
+ width: 100%;
|
|
|
|
+ margin-right: 10px;
|
|
|
|
+}
|
|
|
|
+.single-line {
|
|
|
|
+ color: rgb(30, 33, 41);
|
|
|
|
+ overflow: hidden;
|
|
|
|
+ display: -webkit-box;
|
|
|
|
+ -webkit-box-orient: vertical;
|
|
|
|
+ -webkit-line-clamp: 1;
|
|
|
|
+ white-space: pre-wrap;
|
|
|
|
+ word-break: break-word;
|
|
|
|
+}
|
|
|
|
+.data-color {
|
|
|
|
+ color: rgb(30, 33, 41);
|
|
|
|
+}
|
|
|
|
+.img-box {
|
|
|
|
+ width: 60px;
|
|
|
|
+ height: 60px;
|
|
|
|
+ margin-top: 5px;
|
|
|
|
+ border: 1px solid rgb(194, 199, 207);
|
|
|
|
+ border-radius: 4px;
|
|
|
|
+}
|
|
|
|
+/* 商品定向Tab栏 */
|
|
|
|
+::v-deep(.goods-orientation-tabs #tab-1) {
|
|
|
|
+ border-right: 0;
|
|
|
|
+}
|
|
|
|
+</style>
|