index.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. <script setup lang="ts">
  2. /**
  3. * @Name: index.vue
  4. * @Description: 关键词-TopSearchTerm Table
  5. * @Author: Cheney
  6. */
  7. import { onMounted, ref, watch } from 'vue';
  8. import { usePagination } from '/@/utils/usePagination';
  9. import { getTopSearchTermTable } from './api';
  10. import { marketplaceIdEnum } from '/@/utils/marketplaceIdEnum';
  11. import {
  12. Download,
  13. Goods,
  14. Key,
  15. Medal,
  16. Memo,
  17. Pointer,
  18. Rank,
  19. Reading,
  20. RefreshRight,
  21. Search,
  22. Switch,
  23. TopRight,
  24. } from '@element-plus/icons-vue';
  25. import { useRouter } from 'vue-router';
  26. import { ElMessage } from 'element-plus';
  27. import dayjs from 'dayjs';
  28. const router = useRouter();
  29. const { tableData, total, currentPage, pageSize, handlePageChange } = usePagination(fetchTableData);
  30. const date = ref([dayjs().subtract(7, 'day').format('YYYY-MM-DD'), dayjs().subtract(1, 'day').format('YYYY-MM-DD')]);
  31. const searchTermInp = ref('');
  32. const asinInp = ref('');
  33. const marketplaceSelect = ref(marketplaceIdEnum[0].value); // 当前只有美国区 默认第一个为美国
  34. const marketplaceOptions = marketplaceIdEnum;
  35. const reportTypeSelect = ref('weekly');
  36. const tableLoading = ref(false);
  37. onMounted(() => {
  38. fetchTableData();
  39. });
  40. watch(date, () => {
  41. fetchTableData();
  42. });
  43. async function refreshTable() {
  44. currentPage.value = 1;
  45. pageSize.value = 10;
  46. asinInp.value = '';
  47. searchTermInp.value = '';
  48. reportTypeSelect.value = 'weekly';
  49. marketplaceSelect.value = marketplaceIdEnum[0].value;
  50. await fetchTableData();
  51. }
  52. async function fetchTableData() {
  53. tableLoading.value = true;
  54. const query = {
  55. page: currentPage.value,
  56. limit: pageSize.value,
  57. asin: asinInp.value,
  58. search_term: searchTermInp.value,
  59. report_type: reportTypeSelect.value,
  60. marketplace_Ids: marketplaceSelect.value,
  61. date_start: date.value[0],
  62. date_end: date.value[1],
  63. };
  64. const response = await getTopSearchTermTable(query);
  65. total.value = response.total;
  66. tableData.value = response.data;
  67. tableLoading.value = false;
  68. }
  69. async function handleSelectChange() {
  70. await fetchTableData();
  71. }
  72. async function handleQueryChange() {
  73. if (!validateSearchTermInput(searchTermInp.value)) {
  74. if (searchTermInp.value.length == 0) {
  75. return;
  76. } else {
  77. ElMessage.warning({ message: '关键词只能输入数字和英文字母', plain: true });
  78. return;
  79. }
  80. }
  81. if (asinInp.value.length > 0 && !validateAsinInput(asinInp.value)) {
  82. ElMessage.warning({ message: '不符合匹配规范', plain: true });
  83. return;
  84. }
  85. await fetchTableData();
  86. }
  87. /**
  88. * 校验SearchTerm输入是否合法
  89. * @param input 输入的字符串
  90. */
  91. function validateSearchTermInput(input: string) {
  92. const regex = /^[a-zA-Z0-9\s]*$/;
  93. return regex.test(input);
  94. }
  95. /**
  96. * 校验Asin输入是否合法
  97. * @param input 输入的字符串
  98. */
  99. function validateAsinInput(input: string) {
  100. const regex = /^[Bb]0[A-Za-z0-9\s]*$/i;
  101. return regex.test(input);
  102. }
  103. function handleJump() {
  104. // console.log('All defined routes:', router.getRoutes());
  105. router.push({ path: '/keyword/rootWordManage' });
  106. }
  107. function getTagStyle(clickShareRank: number): Record<string, string> {
  108. switch (clickShareRank) {
  109. case 1:
  110. return { backgroundColor: '#fbbf24', color: '#fff', border: '1px solid #fbbf24' }; // 金色
  111. case 2:
  112. return { backgroundColor: '#C0C0C0', color: '#fff', border: '1px solid #C0C0C0' }; // 银色
  113. case 3:
  114. return { backgroundColor: '#CD7F32', color: '#fff', border: '1px solid #CD7F32' }; // 铜色
  115. default:
  116. return { backgroundColor: '#e0e0e0', color: '#000', border: '1px solid #e0e0e0' };
  117. }
  118. }
  119. </script>
  120. <template>
  121. <!--<div class="mt-3 mx-1.5" style="background-color: #f7f7f7">-->
  122. <!-- <div class="flex justify-between mt-1.5 mx-2">-->
  123. <!-- <div class="font-bold text-lg">-->
  124. <!-- <el-icon style="top: 3px">-->
  125. <!-- <Memo />-->
  126. <!-- </el-icon>-->
  127. <!-- Top Search Term - Table-->
  128. <!-- </div>-->
  129. <!-- <div>-->
  130. <!-- <el-button type="primary" plain @click="handleJump" :icon="TopRight">关键词管理</el-button>-->
  131. <!-- <el-button type="success" plain round :icon="Download">下载表格</el-button>-->
  132. <!-- </div>-->
  133. <!-- </div>-->
  134. <!--</div>-->
  135. <div class="mx-3" style="margin-top: -8px">
  136. <el-divider>
  137. <!--<el-icon>-->
  138. <!-- <star-filled />-->
  139. <!--</el-icon>-->
  140. <div class="font-bold text-lg">
  141. <el-icon style="top: 3px">
  142. <Memo />
  143. </el-icon>
  144. Top Search Term - Table
  145. </div>
  146. </el-divider>
  147. </div>
  148. <el-card class="mx-3" v-loading="tableLoading" style="border: none">
  149. <!-- table筛选栏 -->
  150. <div class="flex justify-between">
  151. <div class="flex gap-5 flex-wrap">
  152. <div>
  153. <span class="font-medium mr-0.5">市场 </span>
  154. <el-select v-model="marketplaceSelect" @change="handleSelectChange" style="width: 130px">
  155. <el-option
  156. v-for="item in marketplaceOptions"
  157. :disabled="item.disabled"
  158. :key="item.value"
  159. :value="item.value"
  160. :label="item.label" />
  161. </el-select>
  162. </div>
  163. <div>
  164. <span class="font-medium mr-0.5">报告类型 </span>
  165. <el-select v-model="reportTypeSelect" @change="handleSelectChange" style="width: 100px">
  166. <el-option label="周度" value="weekly" />
  167. <el-option label="月度" value="monthly" />
  168. </el-select>
  169. </div>
  170. <div>
  171. <span class="font-medium mr-0.5">关键词 </span>
  172. <el-input
  173. v-model="searchTermInp"
  174. @keyup.enter="handleQueryChange"
  175. :prefix-icon="Search"
  176. placeholder="输入后回车查询"
  177. clearable
  178. style="width: 300px"></el-input>
  179. </div>
  180. <div>
  181. <span class="font-medium mr-0.5">ASIN </span>
  182. <el-input
  183. v-model="asinInp"
  184. @keyup.enter="handleQueryChange"
  185. :prefix-icon="Search"
  186. placeholder="输入后回车查询"
  187. clearable
  188. style="width: 180px"></el-input>
  189. </div>
  190. <div>
  191. <span class="font-medium mr-0.5">报告日期 </span>
  192. <el-date-picker
  193. v-model="date"
  194. type="daterange"
  195. value-format="YYYY-MM-DD"
  196. :popper-options="{ placement: 'bottom-end' }"
  197. :clearable="false"
  198. :disabled-date="(time: Date) => time > new Date()"
  199. range-separator="至"
  200. start-placeholder="开始日期"
  201. end-placeholder="结束日期" />
  202. </div>
  203. </div>
  204. <div>
  205. <el-button type="primary" plain @click="handleJump" :icon="TopRight">关键词管理</el-button>
  206. <el-button type="success" plain round :icon="Download">下载表格</el-button>
  207. <el-button @click="refreshTable" :icon="RefreshRight" circle></el-button>
  208. </div>
  209. </div>
  210. <!-- table -->
  211. <el-card shadow="never" class="mt-5">
  212. <div style="height: 795px; overflow: auto">
  213. <el-table :data="tableData" stripe style="width: 100%">
  214. <el-table-column fixed prop="searchTerm" label="关键词" width="260">
  215. <template #header>
  216. <el-icon style="top: 2px; margin-right: 3px">
  217. <Key />
  218. </el-icon>
  219. <span>关键词</span>
  220. </template>
  221. <template #default="{ row }">
  222. <el-link :underline="false" href="https://www.bilibili.com/" target="_blank" style="color: #0b3289">{{
  223. row.searchTerm
  224. }}</el-link>
  225. </template>
  226. </el-table-column>
  227. <el-table-column prop="searchFrequencyRank" label="关键词搜索排名" align="center" width="150">
  228. <template #header>
  229. <el-icon style="top: 2px; margin-right: 4px">
  230. <Rank />
  231. </el-icon>
  232. <span>关键词搜索排名</span>
  233. </template>
  234. <template #default="{ row }">
  235. <span class="font-medium">{{ row.searchFrequencyRank }}</span>
  236. </template>
  237. </el-table-column>
  238. <el-table-column prop="clickedAsin" align="center" label="Asin">
  239. <template #header>
  240. <el-icon style="top: 2px; margin-right: 5px">
  241. <Goods />
  242. </el-icon>
  243. <span>Asin</span>
  244. </template>
  245. <template #default="{ row }">
  246. <span class="font-medium">{{ row.clickedAsin }}</span>
  247. </template>
  248. </el-table-column>
  249. <el-table-column prop="clickedItemName" label="标题">
  250. <template #header>
  251. <el-icon style="top: 2px; margin-right: 5px">
  252. <Reading />
  253. </el-icon>
  254. <span>标题</span>
  255. </template>
  256. </el-table-column>
  257. <el-table-column prop="clickShareRank" label="点击分享率排名" align="center" width="150">
  258. <template #header>
  259. <el-icon style="top: 2px; margin-right: 4px">
  260. <Medal />
  261. </el-icon>
  262. <span>点击分享率排名</span>
  263. </template>
  264. <template #default="{ row }">
  265. <!--<span class="font-semibold">{{ row.clickShareRank }}</span>-->
  266. <el-tag :style="getTagStyle(row.clickShareRank)">
  267. {{ row.clickShareRank }}
  268. </el-tag>
  269. </template>
  270. </el-table-column>
  271. <el-table-column prop="clickShare" align="center" label="点击分享率">
  272. <template #header>
  273. <el-icon style="top: 2px; margin-right: 4px">
  274. <Pointer />
  275. </el-icon>
  276. <span>点击分享率</span>
  277. </template>
  278. <template #default="{ row }">
  279. <span class="font-semibold">{{ row.clickShare }}</span>
  280. </template>
  281. </el-table-column>
  282. <el-table-column prop="conversionShare" align="center" label="转化分享率">
  283. <template #header>
  284. <el-icon style="top: 2px; margin-right: 5px">
  285. <Switch />
  286. </el-icon>
  287. <span>转化分享率</span>
  288. </template>
  289. <template #default="{ row }">
  290. <span class="font-semibold">{{ row.conversionShare }}</span>
  291. </template>
  292. </el-table-column>
  293. </el-table>
  294. </div>
  295. <div class="mt-3.5 flex justify-end">
  296. <el-pagination
  297. v-model:current-page="currentPage"
  298. v-model:page-size="pageSize"
  299. :page-sizes="[10, 20, 30, 50, 100, 200]"
  300. layout="sizes, prev, pager, next"
  301. :total="total"
  302. @change="handlePageChange" />
  303. </div>
  304. </el-card>
  305. </el-card>
  306. </template>
  307. <style scoped>
  308. :deep(.el-divider__text.is-center.el-divider__text) {
  309. background-color: #f8f8f8;
  310. }
  311. </style>