index.vue 7.1 KB

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