index.vue 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. <script setup lang="ts">
  2. /**
  3. * @Name: index.vue
  4. * @Description: 搜索词-TopSearchTerm Rank
  5. * @Author: Cheney
  6. */
  7. import { Key, PictureRounded, Refresh, Search, TopRight } from '@element-plus/icons-vue';
  8. import { nextTick, onBeforeMount, ref, watch } from 'vue';
  9. import { useRouter } from 'vue-router';
  10. import { marketplaceIdEnum } from '/@/utils/marketplaceIdEnum';
  11. import { usePagination } from '/@/utils/usePagination';
  12. import { getTopSearchTermTable } from './api';
  13. import { ElMessage } from 'element-plus';
  14. import ColumnChart from '/src/views/searchTerm/topSearchTermRank/column-chart.vue';
  15. import dayjs from 'dayjs';
  16. import WeekRangePicker from '/@/components/WeekRangePicker/index.vue';
  17. import MonthRangePicker from '/@/components/MonthRangePicker/index.vue';
  18. const weekDate = ref([
  19. dayjs().subtract(2, 'week').day(0).format('YYYY-MM-DD'),
  20. dayjs().subtract(1, 'week').day(6).format('YYYY-MM-DD'),
  21. ]);
  22. const monthDate = ref([
  23. dayjs().subtract(2, 'month').startOf('month').format('YYYY-MM-DD'),
  24. dayjs().subtract(0, 'month').startOf('month').format('YYYY-MM-DD'),
  25. ]);
  26. const router = useRouter();
  27. const { tableData, total, currentPage, pageSize, handlePageChange } = usePagination(fetchTableData);
  28. const marketplaceSelect = ref(marketplaceIdEnum[0].value); // 当前只有美国区 默认第一个为美国
  29. const marketplaceOptions = marketplaceIdEnum;
  30. const reportTypeSelect = ref('weekly');
  31. const searchTermInp = ref('');
  32. const asinInp = ref('');
  33. const tableLoading = ref(false);
  34. onBeforeMount(() => {
  35. fetchTableData();
  36. });
  37. watch([weekDate, monthDate], () => {
  38. fetchTableData();
  39. });
  40. async function refreshTable() {
  41. currentPage.value = 1;
  42. pageSize.value = 10;
  43. asinInp.value = '';
  44. searchTermInp.value = '';
  45. reportTypeSelect.value = 'weekly';
  46. marketplaceSelect.value = marketplaceIdEnum[0].value;
  47. await fetchTableData();
  48. }
  49. async function fetchTableData() {
  50. tableLoading.value = true;
  51. const query = {
  52. page: currentPage.value,
  53. limit: pageSize.value,
  54. asin: asinInp.value,
  55. search_term: searchTermInp.value,
  56. report_type: reportTypeSelect.value,
  57. marketplace_Ids: marketplaceSelect.value,
  58. date_start: reportTypeSelect.value == 'weekly' ? weekDate.value[0] : monthDate.value[0],
  59. date_end: reportTypeSelect.value == 'weekly' ? weekDate.value[1] : monthDate.value[1],
  60. };
  61. try {
  62. const response = await getTopSearchTermTable(query);
  63. total.value = response.total;
  64. tableData.value = response.data;
  65. } catch (error) {
  66. console.error('==Error==:', error);
  67. } finally {
  68. tableLoading.value = false;
  69. await nextTick();
  70. // 触发窗口 resize 事件
  71. window.dispatchEvent(new Event('resize'));
  72. }
  73. }
  74. async function handleSelectChange() {
  75. await fetchTableData();
  76. }
  77. async function handleQueryChange() {
  78. if (!validateSearchTermInput(searchTermInp.value)) {
  79. if (searchTermInp.value.length == 0) {
  80. return;
  81. } else {
  82. ElMessage.warning({ message: '搜索词只能输入数字和英文字母', plain: true });
  83. return;
  84. }
  85. }
  86. if (asinInp.value.length > 0 && !validateAsinInput(asinInp.value)) {
  87. ElMessage.warning({ message: '不符合匹配规范', plain: true });
  88. return;
  89. }
  90. await fetchTableData();
  91. }
  92. /**
  93. * 校验SearchTerm输入是否合法
  94. * @param input 输入的字符串
  95. */
  96. function validateSearchTermInput(input: string) {
  97. const regex = /^[a-zA-Z0-9\s]*$/;
  98. return regex.test(input);
  99. }
  100. /**
  101. * 校验Asin输入是否合法
  102. * @param input 输入的字符串
  103. */
  104. function validateAsinInput(input: string) {
  105. const regex = /^[Bb]0[A-Za-z0-9\s]*$/i;
  106. return regex.test(input);
  107. }
  108. function handleJump() {
  109. router.push({ path: '/searchTerm/rootWordManage' });
  110. }
  111. </script>
  112. <template>
  113. <div class="mx-3">
  114. <el-divider>
  115. <div class="font-bold text-xl">
  116. <el-icon style="top: 3px">
  117. <DataLine />
  118. </el-icon>
  119. Top Search Term - Rank
  120. </div>
  121. </el-divider>
  122. </div>
  123. <el-card class="mx-3 mb-2.5" v-loading="tableLoading" style="border: none">
  124. <!-- table筛选栏 -->
  125. <div class="flex justify-between">
  126. <div class="flex gap-5 flex-wrap">
  127. <div>
  128. <span class="font-medium mr-0.5">市场 </span>
  129. <el-select v-model="marketplaceSelect" @change="handleSelectChange" style="width: 90px">
  130. <el-option
  131. v-for="item in marketplaceOptions"
  132. :disabled="item.disabled"
  133. :key="item.value"
  134. :value="item.value"
  135. :label="item.label" />
  136. </el-select>
  137. </div>
  138. <div>
  139. <span class="font-medium mr-0.5">报告类型 </span>
  140. <el-select v-model="reportTypeSelect" @change="handleSelectChange" style="width: 90px">
  141. <el-option label="周度" value="weekly" />
  142. <el-option label="月度" value="monthly" />
  143. </el-select>
  144. </div>
  145. <div>
  146. <span class="font-medium mr-0.5">搜索词 </span>
  147. <el-input
  148. v-model="searchTermInp"
  149. @keyup.enter="handleQueryChange"
  150. :prefix-icon="Search"
  151. placeholder="输入后回车查询"
  152. clearable
  153. @clear="handleSelectChange"
  154. style="width: 240px" />
  155. </div>
  156. <div>
  157. <span class="font-medium mr-0.5">ASIN </span>
  158. <el-input
  159. v-model="asinInp"
  160. @keyup.enter="handleQueryChange"
  161. :prefix-icon="Search"
  162. placeholder="输入后回车查询"
  163. clearable
  164. @clear="handleSelectChange"
  165. style="width: 180px" />
  166. </div>
  167. <div>
  168. <span class="font-medium mr-0.5">报告日期 </span>
  169. <MonthRangePicker v-model="monthDate" v-if="reportTypeSelect === 'monthly'" />
  170. <WeekRangePicker v-model="weekDate" v-else />
  171. </div>
  172. </div>
  173. <div class="flex">
  174. <el-button type="primary" plain @click="handleJump" :icon="TopRight">搜索词管理</el-button>
  175. <el-button @click="refreshTable" :icon="Refresh" circle></el-button>
  176. </div>
  177. </div>
  178. <!-- table -->
  179. <el-card shadow="never" class="mt-5">
  180. <div style="height: 100%; overflow: auto">
  181. <el-table :data="tableData" height="920" stripe style="width: 100%">
  182. <el-table-column fixed prop="searchTerm" label="搜索词" width="260">
  183. <template #header>
  184. <el-icon style="top: 2px; margin-right: 3px">
  185. <Key />
  186. </el-icon>
  187. <span>搜索词</span>
  188. </template>
  189. <template #default="{ row }">
  190. <el-link :underline="false" target="_blank" style="color: #5a6fc0">
  191. {{ row.searchTerm }}
  192. </el-link>
  193. </template>
  194. </el-table-column>
  195. <el-table-column prop="rank" label="搜索词搜索排名" align="center">
  196. <template #header>
  197. <el-icon style="top: 2px; margin-right: 4px">
  198. <PictureRounded />
  199. </el-icon>
  200. <span>Tendency</span>
  201. </template>
  202. <template #default="{ row }">
  203. <ColumnChart :rowData="row" />
  204. </template>
  205. </el-table-column>
  206. </el-table>
  207. </div>
  208. <div class="mt-3.5 flex justify-end">
  209. <el-pagination
  210. v-model:current-page="currentPage"
  211. v-model:page-size="pageSize"
  212. :page-sizes="[10, 20, 30, 50, 100, 200]"
  213. layout="sizes, prev, pager, next, total"
  214. :total="total"
  215. @change="handlePageChange" />
  216. </div>
  217. </el-card>
  218. </el-card>
  219. </template>
  220. <style scoped>
  221. :deep(.el-divider__text.is-center.el-divider__text) {
  222. background-color: #f8f8f8;
  223. }
  224. </style>