index.vue 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. <script setup lang="ts">
  2. /**
  3. * @Name: index.vue
  4. * @Description: asinView
  5. * @Author: Cheney
  6. */
  7. import { Refresh, Search } from '@element-plus/icons-vue';
  8. import { reactive, ref, watch, onBeforeMount } from 'vue';
  9. import dayjs from 'dayjs';
  10. import { getTableData } from './api';
  11. import { ElMessage } from 'element-plus';
  12. import { asinColumns } from './useColumns';
  13. import WeekRangePicker from '/@/components/WeekRangePicker/index.vue';
  14. import MonthRangePicker from '/@/components/MonthRangePicker/index.vue';
  15. const weekDate = ref([
  16. dayjs().subtract(2, 'week').day(0).format('YYYY-MM-DD'),
  17. dayjs().subtract(1, 'week').day(6).format('YYYY-MM-DD'),
  18. ]);
  19. const monthDate = ref([
  20. dayjs().subtract(2, 'month').startOf('month').format('YYYY-MM-DD'),
  21. dayjs().subtract(0, 'month').startOf('month').format('YYYY-MM-DD'),
  22. ]);
  23. const reportTypeSelect = ref('weekly');
  24. const searchTermInp = ref('');
  25. const asinInp = ref('B0');
  26. const tableLoading = ref(false);
  27. const gridOptions: any = reactive({
  28. height: '100%',
  29. border: false,
  30. round: true,
  31. columnConfig: {
  32. resizable: true,
  33. },
  34. toolbarConfig: {
  35. custom: true,
  36. slots: {
  37. buttons: 'toolbar_buttons',
  38. },
  39. },
  40. columns: asinColumns,
  41. data: [],
  42. });
  43. const tablePage = reactive({
  44. total: 0,
  45. currentPage: 1,
  46. pageSize: 20,
  47. });
  48. onBeforeMount(() => {
  49. fetchTableData();
  50. });
  51. watch([weekDate, monthDate], () => {
  52. fetchTableData();
  53. });
  54. async function handlePageChange({ currentPage, pageSize }) {
  55. tablePage.currentPage = currentPage;
  56. tablePage.pageSize = pageSize;
  57. await fetchTableData();
  58. }
  59. async function handleSelectChange() {
  60. if (!asinInp.value) {
  61. ElMessage.warning({ message: '请输入ASIN', plain: true });
  62. return;
  63. } else {
  64. await fetchTableData();
  65. }
  66. }
  67. async function refreshTable() {
  68. tablePage.currentPage = 1;
  69. tablePage.pageSize = 20;
  70. asinInp.value = '';
  71. searchTermInp.value = '';
  72. reportTypeSelect.value = 'weekly';
  73. // marketplaceSelect.value = marketplaceIdEnum[0].value;
  74. await fetchTableData();
  75. }
  76. async function fetchTableData() {
  77. tableLoading.value = true;
  78. const query = {
  79. page: tablePage.currentPage,
  80. limit: tablePage.pageSize,
  81. asin: asinInp.value,
  82. search_term: searchTermInp.value,
  83. report_type: reportTypeSelect.value,
  84. date_start: reportTypeSelect.value == 'weekly' ? weekDate.value[0] : monthDate.value[0],
  85. date_end: reportTypeSelect.value == 'weekly' ? weekDate.value[1] : monthDate.value[1],
  86. };
  87. try {
  88. const response = await getTableData(query);
  89. tablePage.total = response.total;
  90. gridOptions.data = response.data;
  91. } catch (error) {
  92. console.error('==Error==:', error);
  93. } finally {
  94. tableLoading.value = false;
  95. }
  96. }
  97. /**
  98. * 输入框按下回车后触发
  99. */
  100. async function handleQueryChange() {
  101. if (!validateSearchTermInput(searchTermInp.value)) {
  102. if (searchTermInp.value.length == 0) {
  103. return;
  104. } else {
  105. ElMessage.warning({ message: '搜索词只能输入数字和英文字母', plain: true });
  106. return;
  107. }
  108. }
  109. if (asinInp.value.length > 0 && !validateAsinInput(asinInp.value)) {
  110. ElMessage.warning({ message: '不符合匹配规范', plain: true });
  111. return;
  112. }
  113. await fetchTableData();
  114. }
  115. /**
  116. * 校验SearchTerm输入是否合法
  117. * @param input 输入的字符串
  118. */
  119. function validateSearchTermInput(input: string) {
  120. const regex = /^[a-zA-Z0-9\s]*$/;
  121. return regex.test(input);
  122. }
  123. /**
  124. * 校验Asin输入是否合法
  125. * @param input 输入的字符串
  126. */
  127. function validateAsinInput(input: string) {
  128. const regex = /^[Bb]0[A-Za-z0-9\s]*$/i;
  129. return regex.test(input);
  130. }
  131. </script>
  132. <template>
  133. <div class="py-2 px-2.5 flex" style="background-color: #f7f7f7">
  134. <el-card shadow="hover" class="mb-2.5" style="border: none; margin-bottom: 10px">
  135. <div ref="queryContainer" class="flex justify-between">
  136. <div class="flex gap-5 flex-wrap">
  137. <div>
  138. <span class="font-medium mr-0.5">报告类型 </span>
  139. <el-select v-model="reportTypeSelect" @change="handleSelectChange" style="width: 90px">
  140. <el-option label="周度" value="weekly" />
  141. <el-option label="月度" value="monthly" />
  142. </el-select>
  143. </div>
  144. <div>
  145. <span class="font-medium mr-0.5">搜索词 </span>
  146. <el-input
  147. v-model="searchTermInp"
  148. @keyup.enter="handleQueryChange"
  149. :prefix-icon="Search"
  150. placeholder="输入后回车查询"
  151. clearable
  152. @clear="handleSelectChange"
  153. style="width: 240px" />
  154. </div>
  155. <div>
  156. <span class="font-medium mr-0.5">ASIN </span>
  157. <el-input
  158. v-model="asinInp"
  159. @keyup.enter="handleQueryChange"
  160. :prefix-icon="Search"
  161. placeholder="输入后回车查询"
  162. clearable
  163. @clear="handleSelectChange"
  164. style="width: 180px" />
  165. </div>
  166. <div>
  167. <span class="font-medium mr-0.5">报告日期 </span>
  168. <MonthRangePicker v-model="monthDate" v-if="reportTypeSelect === 'monthly'" />
  169. <WeekRangePicker v-model="weekDate" v-else />
  170. </div>
  171. </div>
  172. <div class="flex">
  173. <el-button @click="refreshTable" :icon="Refresh" circle></el-button>
  174. </div>
  175. </div>
  176. </el-card>
  177. <el-card shadow="hover" style="border: none; flex: 1 1 auto; height: calc(100vh - 154px)">
  178. <div style="overflow: hidden; width: 100%; height: calc(100vh - 174px)" v-loading="tableLoading">
  179. <vxe-grid v-bind="gridOptions">
  180. <template #toolbar_buttons></template>
  181. <template v-for="col in asinColumns" #[`${col.field}_default`]="{ row }">
  182. <div v-if="col.field === 'clickedItemName'">
  183. <el-tooltip effect="dark" :content="row.clickedItemName" placement="top" :show-after="300">
  184. <div class="line-text font-medium">
  185. {{ row.clickedItemName }}
  186. </div>
  187. </el-tooltip>
  188. </div>
  189. <div v-else class="font-medium">
  190. {{ row[col.field] ? row[col.field] : '-' }}
  191. </div>
  192. </template>
  193. <template #pager>
  194. <vxe-pager
  195. :layouts="['Sizes', 'PrevJump', 'PrevPage', 'Number', 'NextPage', 'NextJump', 'FullJump', 'Total']"
  196. v-model:current-page="tablePage.currentPage"
  197. v-model:page-size="tablePage.pageSize"
  198. :total="tablePage.total"
  199. @page-change="handlePageChange">
  200. </vxe-pager>
  201. </template>
  202. </vxe-grid>
  203. </div>
  204. </el-card>
  205. </div>
  206. </template>
  207. <style scoped>
  208. .line-text {
  209. white-space: nowrap;
  210. overflow: hidden;
  211. text-overflow: ellipsis;
  212. }
  213. </style>