|
@@ -1,51 +1,70 @@
|
|
|
<script setup lang="ts">
|
|
|
/**
|
|
|
* @Name: index.vue
|
|
|
- * @Description: 关键词-TopSearchTerm Table
|
|
|
+ * @Description: 搜索词-TopSearchTerm Table
|
|
|
* @Author: Cheney
|
|
|
*/
|
|
|
|
|
|
-import { onMounted, ref, watch } from 'vue';
|
|
|
+import { nextTick, onBeforeMount, onMounted, ref, watch } from 'vue';
|
|
|
import { usePagination } from '/@/utils/usePagination';
|
|
|
-import { getTopSearchTermTable } from './api';
|
|
|
+import { getTopSearchTermTable, postDownload } from './api';
|
|
|
import { marketplaceIdEnum } from '/@/utils/marketplaceIdEnum';
|
|
|
-import {
|
|
|
- Download,
|
|
|
- Goods,
|
|
|
- Key,
|
|
|
- Medal,
|
|
|
- Memo,
|
|
|
- Pointer,
|
|
|
- Rank,
|
|
|
- Reading,
|
|
|
- Refresh,
|
|
|
- Search,
|
|
|
- Switch,
|
|
|
- TopRight,
|
|
|
-} from '@element-plus/icons-vue';
|
|
|
+import { Download, Goods, Key, Medal, Pointer, Rank, Refresh, Search, Switch, TopRight } from '@element-plus/icons-vue';
|
|
|
import { useRouter } from 'vue-router';
|
|
|
import { ElMessage } from 'element-plus';
|
|
|
import dayjs from 'dayjs';
|
|
|
+import enLocale from 'element-plus/es/locale/lang/en';
|
|
|
|
|
|
const router = useRouter();
|
|
|
|
|
|
const { tableData, total, currentPage, pageSize, handlePageChange } = usePagination(fetchTableData);
|
|
|
-const date = ref([dayjs().subtract(7, 'day').format('YYYY-MM-DD'), dayjs().subtract(1, 'day').format('YYYY-MM-DD')]);
|
|
|
+
|
|
|
const marketplaceSelect = ref(marketplaceIdEnum[0].value); // 当前只有美国区 默认第一个为美国
|
|
|
const marketplaceOptions = marketplaceIdEnum;
|
|
|
const reportTypeSelect = ref('weekly');
|
|
|
const searchTermInp = ref('');
|
|
|
const asinInp = ref('');
|
|
|
const tableLoading = ref(false);
|
|
|
+const downloadLoading = ref(false);
|
|
|
+const date = ref(calculateLastWeek()); // 设置默认日期为上周的周日到周六
|
|
|
+const dateDimension = ref(date.value[0]);
|
|
|
+
|
|
|
|
|
|
-onMounted(() => {
|
|
|
+onBeforeMount(() => {
|
|
|
fetchTableData();
|
|
|
});
|
|
|
|
|
|
-watch(date, () => {
|
|
|
+watch(dateDimension, () => {
|
|
|
+ calculateDate();
|
|
|
+ // console.log('==Date==', date.value[0], date.value[1]);
|
|
|
fetchTableData();
|
|
|
});
|
|
|
|
|
|
+function calculateDate() {
|
|
|
+ if (reportTypeSelect.value === 'weekly') {
|
|
|
+ date.value[0] = dateDimension.value;
|
|
|
+ date.value[1] = calculateEndDate(dateDimension.value);
|
|
|
+ } else if (reportTypeSelect.value === 'monthly') {
|
|
|
+ const selectedMonth = dayjs(dateDimension.value);
|
|
|
+ date.value[0] = selectedMonth.startOf('month').format('YYYY-MM-DD');
|
|
|
+ date.value[1] = selectedMonth.endOf('month').format('YYYY-MM-DD');
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 计算上周的周日到周六的日期范围
|
|
|
+ */
|
|
|
+function calculateLastWeek() {
|
|
|
+ const today = dayjs();
|
|
|
+ const lastSaturday = today.subtract(today.day() + 1, 'day'); // 上周六
|
|
|
+ const lastSunday = lastSaturday.subtract(6, 'day'); // 上周日
|
|
|
+ return [lastSunday.format('YYYY-MM-DD'), lastSaturday.format('YYYY-MM-DD')];
|
|
|
+}
|
|
|
+
|
|
|
+function calculateEndDate(startDate: string) {
|
|
|
+ return dayjs(startDate).add(6, 'day').format('YYYY-MM-DD');
|
|
|
+}
|
|
|
+
|
|
|
async function refreshTable() {
|
|
|
currentPage.value = 1;
|
|
|
pageSize.value = 10;
|
|
@@ -68,13 +87,22 @@ async function fetchTableData() {
|
|
|
date_start: date.value[0],
|
|
|
date_end: date.value[1],
|
|
|
};
|
|
|
- const response = await getTopSearchTermTable(query);
|
|
|
- total.value = response.total;
|
|
|
- tableData.value = response.data;
|
|
|
- tableLoading.value = false;
|
|
|
+ try {
|
|
|
+ const response = await getTopSearchTermTable(query);
|
|
|
+ total.value = response.total;
|
|
|
+ tableData.value = response.data;
|
|
|
+ } catch (error) {
|
|
|
+ console.error('==Error==:', error);
|
|
|
+ } finally {
|
|
|
+ tableLoading.value = false;
|
|
|
+ await nextTick();
|
|
|
+ // 触发窗口 resize 事件
|
|
|
+ window.dispatchEvent(new Event('resize'));
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
async function handleSelectChange() {
|
|
|
+ calculateDate();
|
|
|
await fetchTableData();
|
|
|
}
|
|
|
|
|
@@ -83,7 +111,7 @@ async function handleQueryChange() {
|
|
|
if (searchTermInp.value.length == 0) {
|
|
|
return;
|
|
|
} else {
|
|
|
- ElMessage.warning({ message: '关键词只能输入数字和英文字母', plain: true });
|
|
|
+ ElMessage.warning({ message: '搜索词只能输入数字和英文字母', plain: true });
|
|
|
return;
|
|
|
}
|
|
|
}
|
|
@@ -114,7 +142,53 @@ function validateAsinInput(input: string) {
|
|
|
|
|
|
function handleJump() {
|
|
|
// console.log('All defined routes:', router.getRoutes());
|
|
|
- router.push({ path: '/keyword/rootWordManage' });
|
|
|
+ router.push({ path: '/searchTerm/rootWordManage' });
|
|
|
+}
|
|
|
+
|
|
|
+async function handleDownload() {
|
|
|
+ downloadLoading.value = true;
|
|
|
+ try {
|
|
|
+ const body = {
|
|
|
+ asin: asinInp.value,
|
|
|
+ date_start: date.value[0],
|
|
|
+ date_end: date.value[1],
|
|
|
+ search_term: searchTermInp.value,
|
|
|
+ marketplace_Ids: marketplaceSelect.value,
|
|
|
+ report_type: reportTypeSelect.value,
|
|
|
+ };
|
|
|
+
|
|
|
+ const response = await postDownload(body);
|
|
|
+
|
|
|
+ const blob = new Blob([response.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
|
|
|
+
|
|
|
+ // 创建一个临时 URL
|
|
|
+ const url = window.URL.createObjectURL(blob);
|
|
|
+
|
|
|
+ // 创建一个临时的 <a> 元素并触发下载
|
|
|
+ const link = document.createElement('a');
|
|
|
+ link.href = url;
|
|
|
+
|
|
|
+ // 设置文件名
|
|
|
+ const currentTime = dayjs().format('YYYY-MM-DD_HH_mm_ss');
|
|
|
+ const filename = `TopSearchTerm_${currentTime}.xlsx`;
|
|
|
+
|
|
|
+ link.setAttribute('download', filename);
|
|
|
+
|
|
|
+ // 添加到 body, 触发点击, 然后移除
|
|
|
+ document.body.appendChild(link);
|
|
|
+ link.click();
|
|
|
+ document.body.removeChild(link);
|
|
|
+
|
|
|
+ // 释放 URL 对象
|
|
|
+ window.URL.revokeObjectURL(url);
|
|
|
+
|
|
|
+ ElMessage.success('文件下载成功');
|
|
|
+ } catch (error) {
|
|
|
+ console.error('==Error==:', error);
|
|
|
+ ElMessage.error('文件下载失败,请重试');
|
|
|
+ } finally {
|
|
|
+ downloadLoading.value = false;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
function getTagStyle(clickShareRank: number): Record<string, string> {
|
|
@@ -134,7 +208,7 @@ function getTagStyle(clickShareRank: number): Record<string, string> {
|
|
|
<template>
|
|
|
<div class="mx-3">
|
|
|
<el-divider>
|
|
|
- <div class="font-bold text-lg">
|
|
|
+ <div class="font-bold text-xl">
|
|
|
<el-icon style="top: 3px">
|
|
|
<DataAnalysis />
|
|
|
</el-icon>
|
|
@@ -148,7 +222,7 @@ function getTagStyle(clickShareRank: number): Record<string, string> {
|
|
|
<div class="flex gap-5 flex-wrap">
|
|
|
<div>
|
|
|
<span class="font-medium mr-0.5">市场 </span>
|
|
|
- <el-select v-model="marketplaceSelect" @change="handleSelectChange" style="width: 130px">
|
|
|
+ <el-select v-model="marketplaceSelect" @change="handleSelectChange" style="width: 90px">
|
|
|
<el-option
|
|
|
v-for="item in marketplaceOptions"
|
|
|
:disabled="item.disabled"
|
|
@@ -159,13 +233,13 @@ function getTagStyle(clickShareRank: number): Record<string, string> {
|
|
|
</div>
|
|
|
<div>
|
|
|
<span class="font-medium mr-0.5">报告类型 </span>
|
|
|
- <el-select v-model="reportTypeSelect" @change="handleSelectChange" style="width: 100px">
|
|
|
+ <el-select v-model="reportTypeSelect" @change="handleSelectChange" style="width: 90px">
|
|
|
<el-option label="周度" value="weekly" />
|
|
|
<el-option label="月度" value="monthly" />
|
|
|
</el-select>
|
|
|
</div>
|
|
|
<div>
|
|
|
- <span class="font-medium mr-0.5">关键词 </span>
|
|
|
+ <span class="font-medium mr-0.5">搜索词 </span>
|
|
|
<el-input
|
|
|
v-model="searchTermInp"
|
|
|
@keyup.enter="handleQueryChange"
|
|
@@ -173,7 +247,7 @@ function getTagStyle(clickShareRank: number): Record<string, string> {
|
|
|
placeholder="输入后回车查询"
|
|
|
clearable
|
|
|
@clear="handleSelectChange"
|
|
|
- style="width: 300px" />
|
|
|
+ style="width: 240px" />
|
|
|
</div>
|
|
|
<div>
|
|
|
<span class="font-medium mr-0.5">ASIN </span>
|
|
@@ -188,34 +262,58 @@ function getTagStyle(clickShareRank: number): Record<string, string> {
|
|
|
</div>
|
|
|
<div>
|
|
|
<span class="font-medium mr-0.5">报告日期 </span>
|
|
|
- <el-date-picker
|
|
|
- v-model="date"
|
|
|
- type="daterange"
|
|
|
- value-format="YYYY-MM-DD"
|
|
|
- :popper-options="{ placement: 'bottom-end' }"
|
|
|
- :clearable="false"
|
|
|
- :disabled-date="(time: Date) => time > new Date()"
|
|
|
- range-separator="至"
|
|
|
- start-placeholder="开始日期"
|
|
|
- end-placeholder="结束日期" />
|
|
|
+
|
|
|
+ <el-config-provider :locale="enLocale">
|
|
|
+ <el-date-picker
|
|
|
+ v-if="reportTypeSelect === 'weekly'"
|
|
|
+ v-model="dateDimension"
|
|
|
+ type="week"
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
+ :format="`${date[0]} To ${date[1]}`"
|
|
|
+ :popper-options="{ placement: 'bottom-end' }"
|
|
|
+ :disabled-date="(time: Date) => time > new Date()"
|
|
|
+ :clearable="false" />
|
|
|
+ <el-date-picker
|
|
|
+ v-else
|
|
|
+ v-model="dateDimension"
|
|
|
+ type="month"
|
|
|
+ value-format="YYYY-MM"
|
|
|
+ :format="`${date[0]} To ${date[1]}`"
|
|
|
+ :popper-options="{ placement: 'bottom-end' }"
|
|
|
+ :disabled-date="(time: Date) => time > new Date()"
|
|
|
+ :clearable="false">
|
|
|
+ <template #default>
|
|
|
+ 123
|
|
|
+ </template>
|
|
|
+ </el-date-picker>
|
|
|
+ </el-config-provider>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="flex">
|
|
|
- <el-button type="primary" plain @click="handleJump" :icon="TopRight">关键词管理</el-button>
|
|
|
- <el-button type="success" plain round :icon="Download">下载表格</el-button>
|
|
|
+ <el-button type="primary" plain @click="handleJump" :icon="TopRight">搜索词管理</el-button>
|
|
|
+ <el-button
|
|
|
+ type="success"
|
|
|
+ plain
|
|
|
+ @click="handleDownload"
|
|
|
+ :icon="Download"
|
|
|
+ round
|
|
|
+ :loading="downloadLoading"
|
|
|
+ :disabled="!tableData.length"
|
|
|
+ >下载表格
|
|
|
+ </el-button>
|
|
|
<el-button @click="refreshTable" :icon="Refresh" circle></el-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
<!-- table -->
|
|
|
<el-card shadow="never" class="mt-5">
|
|
|
<div style="height: 795px; overflow: auto">
|
|
|
- <el-table :data="tableData" stripe style="width: 100%">
|
|
|
- <el-table-column fixed prop="searchTerm" label="关键词" width="260">
|
|
|
+ <el-table :data="tableData" height="795" stripe style="width: 100%">
|
|
|
+ <el-table-column fixed prop="searchTerm" label="搜索词" width="260">
|
|
|
<template #header>
|
|
|
<el-icon style="top: 2px; margin-right: 3px">
|
|
|
<Key />
|
|
|
</el-icon>
|
|
|
- <span>关键词</span>
|
|
|
+ <span>搜索词</span>
|
|
|
</template>
|
|
|
<template #default="{ row }">
|
|
|
<el-link :underline="false" href="https://www.bilibili.com/" target="_blank" style="color: #0b3289"
|
|
@@ -223,18 +321,18 @@ function getTagStyle(clickShareRank: number): Record<string, string> {
|
|
|
</el-link>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column prop="searchFrequencyRank" label="关键词搜索排名" align="center" width="150">
|
|
|
+ <el-table-column prop="searchFrequencyRank" label="搜索词搜索排名" align="center" width="150">
|
|
|
<template #header>
|
|
|
<el-icon style="top: 2px; margin-right: 4px">
|
|
|
<Rank />
|
|
|
</el-icon>
|
|
|
- <span>关键词搜索排名</span>
|
|
|
+ <span>搜索词搜索排名</span>
|
|
|
</template>
|
|
|
<template #default="{ row }">
|
|
|
<span class="font-medium">{{ row.searchFrequencyRank }}</span>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column prop="clickedAsin" align="center" label="Asin">
|
|
|
+ <el-table-column prop="clickedAsin" label="Asin" align="center">
|
|
|
<template #header>
|
|
|
<el-icon style="top: 2px; margin-right: 5px">
|
|
|
<Goods />
|
|
@@ -242,17 +340,25 @@ function getTagStyle(clickShareRank: number): Record<string, string> {
|
|
|
<span>Asin</span>
|
|
|
</template>
|
|
|
<template #default="{ row }">
|
|
|
- <span class="font-medium">{{ row.clickedAsin }}</span>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column prop="clickedItemName" label="标题">
|
|
|
- <template #header>
|
|
|
- <el-icon style="top: 2px; margin-right: 5px">
|
|
|
- <Reading />
|
|
|
- </el-icon>
|
|
|
- <span>标题</span>
|
|
|
+ <div class="font-medium" style="color: black">{{ row.clickedAsin }}</div>
|
|
|
+ <div class="text-sm text-left">
|
|
|
+ <el-tooltip class="box-item" effect="dark" :content="row.clickedItemName" placement="top-start">
|
|
|
+ <div class="tooltip-text">
|
|
|
+ <span class="font-medium mr-1">Title:</span>
|
|
|
+ {{ row.clickedItemName }}
|
|
|
+ </div>
|
|
|
+ </el-tooltip>
|
|
|
+ </div>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
+ <!--<el-table-column prop="clickedItemName" label="标题">-->
|
|
|
+ <!-- <template #header>-->
|
|
|
+ <!-- <el-icon style="top: 2px; margin-right: 5px">-->
|
|
|
+ <!-- <Reading />-->
|
|
|
+ <!-- </el-icon>-->
|
|
|
+ <!-- <span>标题</span>-->
|
|
|
+ <!-- </template>-->
|
|
|
+ <!--</el-table-column>-->
|
|
|
<el-table-column prop="clickShareRank" label="点击分享率排名" align="center" width="150">
|
|
|
<template #header>
|
|
|
<el-icon style="top: 2px; margin-right: 4px">
|
|
@@ -308,4 +414,15 @@ function getTagStyle(clickShareRank: number): Record<string, string> {
|
|
|
:deep(.el-divider__text.is-center.el-divider__text) {
|
|
|
background-color: #f8f8f8;
|
|
|
}
|
|
|
+
|
|
|
+.tooltip-text {
|
|
|
+ display: -webkit-box;
|
|
|
+ -webkit-box-orient: vertical;
|
|
|
+ -webkit-line-clamp: 2;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: normal;
|
|
|
+ line-height: 1.2em;
|
|
|
+ max-height: 2.4em;
|
|
|
+}
|
|
|
</style>
|