index.vue 7.2 KB

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