Advertisement
IndomitablePlatypus

Untitled

Jun 6th, 2023
612
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. <template>
  2.   <div>
  3.     <heading class="mb-6">Clc Sync</heading>
  4.     <ClcBar
  5.         :isImportAllowed="isImportAllowed"
  6.         :totalJobs="totalJobs"
  7.         @searchCars="searchCars()"
  8.         @importCars="importCars()"
  9.         @clearFilters="clearFilters()"
  10.     ></ClcBar>
  11.  
  12.     <div class="flex flex-row items-start p-4">
  13.       <form class="flex flex-col items-start justify-center filters" v-on:keyup.enter="searchCars">
  14.  
  15.         <ClcRangedFilter header="Цена" filter-name="cost"></ClcRangedFilter>
  16.  
  17.         <ClcRangedFilter header="Год" filter-name="year"></ClcRangedFilter>
  18.  
  19.         <ClcRangedFilter header="Пробег" filter-name="mileage"></ClcRangedFilter>
  20.  
  21.         <ClcArrayFilter header="Марки" filter-name="mark" :original-values="marks"></ClcArrayFilter>
  22.  
  23.         <ClcArrayFilter header="Модели" filter-name="model" :original-values="models"></ClcArrayFilter>
  24.  
  25.         <ClcArrayFilter header="Источники" filter-name="import_source" :original-values="sources"></ClcArrayFilter>
  26.  
  27.         <ClcArrayFilter header="Регионы" filter-name="seller_region" :original-values="regions"></ClcArrayFilter>
  28.  
  29.         <div class="header">Параметры поиска</div>
  30.         <div class="filter-box m-2 p-2 flex flex-col items-start justify-start">
  31.           <div class="filter-field flex flex-row items-center justify-start m-1">
  32.             <label class="m-1" for="limit">Выбрать машин:</label>
  33.             <input
  34.                 type="number"
  35.                 class="filter-textbox m-1"
  36.                 v-model="limit"
  37.                 id="limit"
  38.             >
  39.           </div>
  40.  
  41.           <div class="m-1 randomize">
  42.             <input
  43.                 type="checkbox"
  44.                 v-model="randomizeSelection"
  45.                 id="randomizeSelection"
  46.             >
  47.             <label for="randomizeSelection">Рандомизировать выборку</label>
  48.           </div>
  49.         </div>
  50.  
  51.       </form>
  52.       <div class="search-results" v-if="searchResult.length > 0">
  53.         <div class="left flex flex-row center justify-start m-1">
  54.           <input
  55.               type="checkbox"
  56.               id="checkAll"
  57.               class="m-1"
  58.               @change="selectAll"
  59.               v-model="selectedAll"
  60.               :class="{inactive: selectedCustom}"
  61.           />
  62.           <label
  63.               for="checkAll"
  64.               class="m-1"
  65.               :class="{inactive: selectedCustom}"
  66.           >
  67.             Все на всех страницах
  68.           </label>
  69.         </div>
  70.         <ClcCar
  71.             v-for="car in pagedCars"
  72.             :car="car"
  73.             :key="car.id"
  74.             :excluded="excludedCars[car.id]"
  75.             @carChecked="onCarChecked"
  76.         ></ClcCar>
  77.  
  78.         <ClcPaging
  79.             :current-page="currentPage"
  80.             :items-per-page="itemsPerPage"
  81.             :total-items="searchResult.length"
  82.             @pageChanged="onPageChanged"
  83.         ></ClcPaging>
  84.  
  85.       </div>
  86.       <div class="search-results flex justify-center fixed" v-if="searchResult.length === 0 && !searchInProgress">
  87.         <h3>{{ emptySearchMessage }}</h3>
  88.       </div>
  89.  
  90.       <div class="search-results flex justify-center fixed" v-if="searchInProgress">
  91.         <div class="loader">
  92.           <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
  93.             <path fill="currentColor"
  94.                   d="M10 4.5a5.5 5.5 0 1 0 5.5 5.5A5.5 5.5 0 0 0 10 4.5zm4 3.6l-2.3.8a2 2 0 0 0-1.2-.8V5.6A4.5 4.5 0 0 1 14.1 8zM9 10a1 1 0 1 1 1 1 1 1 0 0 1-1-1zm.5-4.5v2.6a2 2 0 0 0-1.2.8L6 8.2a4.5 4.5 0 0 1 3.6-2.7zm-4 4.5a4.5 4.5 0 0 1 0-.9l2.5.8v.1a2 2 0 0 0 .5 1.3l-1.5 2A4.5 4.5 0 0 1 5.5 10zm2.3 4l1.5-2.1a2 2 0 0 0 1.5 0l1.4 2a4.3 4.3 0 0 1-4.4 0zm5.3-.7l-1.5-2A2 2 0 0 0 12 10v-.1l2.4-.8a4.5 4.5 0 0 1-1.3 4.2zM10 0a10 10 0 1 0 10 10A10 10 0 0 0 10 0zm0 18.5a8.5 8.5 0 1 1 8.5-8.5 8.5 8.5 0 0 1-8.5 8.5z"
  95.                   fill-rule="evenodd"/>
  96.           </svg>
  97.         </div>
  98.       </div>
  99.  
  100.     </div>
  101.   </div>
  102. </template>
  103.  
  104. <script>
  105.  
  106. import api from "../api";
  107. import ClcCar from "./ClcCar.vue";
  108. import ClcBar from "./ClcBar.vue";
  109. import ClcPaging from "./ClcPaging.vue";
  110. import ClcRangedFilter from "./ClcRangedFilter.vue";
  111. import SelectedFilters from "../shared/selectedFilters";
  112. import ClcArrayFilter from "./ClcArrayFilter.vue";
  113.  
  114. export default {
  115.   components: {ClcArrayFilter, ClcRangedFilter, ClcPaging, ClcBar, ClcCar},
  116.  
  117.   data() {
  118.     return {
  119.       searchResult: [],
  120.       excludedCars: {},
  121.       itemsPerPage: 20,
  122.       currentPage: 1,
  123.       marks: [],
  124.       models: [],
  125.       sources: [],
  126.       regions: [],
  127.       emptySearchMessage: "Выберите фильтры для поиска",
  128.       limit: 1000,
  129.       randomizeSelection: true,
  130.       selectedAll: true,
  131.       selectedCustom: false,
  132.       isImporting: true,
  133.       totalJobs: 0,
  134.       totalJobsIntervalHandle: null,
  135.       totalJobsIntervalPeriod: 1000,
  136.       searchInProgress: false,
  137.     };
  138.   },
  139.  
  140.   async mounted() {
  141.     this.startTotalJobsMonitoring();
  142.  
  143.     await this.prepareFilters();
  144.   },
  145.  
  146.   beforeDestroy() {
  147.     if (this.totalJobsIntervalHandle) {
  148.       clearInterval(this.totalJobsIntervalHandle);
  149.     }
  150.   },
  151.  
  152.   computed: {
  153.     pagedCars() {
  154.       const start = (this.currentPage - 1) * this.itemsPerPage;
  155.       const end = this.currentPage * this.itemsPerPage;
  156.       return this.searchResult.slice(start, end);
  157.     },
  158.  
  159.     isImportAllowed() {
  160.       return !this.isImporting
  161.           && this.totalJobs === 0
  162.           && this.searchResult.length > 0;
  163.     }
  164.   },
  165.  
  166.   methods: {
  167.     async prepareFilters() {
  168.       const filters = await api.getFilters();
  169.       filters.marks.forEach((item, index) => {
  170.         this.marks.push({
  171.           id: index,
  172.           filterKey: item.mark,
  173.           name: item.mark,
  174.           value: item.mark,
  175.           textId: `mark${item.mark}`
  176.         });
  177.       });
  178.  
  179.       filters.models.forEach((item, index) => {
  180.         this.models.push({
  181.           id: index,
  182.           filterKey: item.model,
  183.           name: `${item.mark} ${item.model}`,
  184.           value: item.model,
  185.           textId: `model${item.mark}${item.model}`
  186.         });
  187.       });
  188.  
  189.       filters.sources.forEach((item, index) => {
  190.         this.sources.push({
  191.           id: item.id,
  192.           filterKey: item.id,
  193.           name: item.name,
  194.           value: item.id,
  195.           textId: `source${item.id}`
  196.         });
  197.       });
  198.  
  199.       filters.regions.forEach((item, index) => {
  200.         this.regions.push({
  201.           id: item.id,
  202.           filterKey: item.id,
  203.           name: item.name,
  204.           value: item.id,
  205.           textId: `region${item.id}`
  206.         });
  207.       });
  208.     },
  209.  
  210.     async searchCars() {
  211.       if (this.searchInProgress) {
  212.         return;
  213.       }
  214.  
  215.       if (SelectedFilters.isEmpty() && !confirm("Вы уверены, что хотите запустить поиск с пустыми фильтрами?")) {
  216.         return;
  217.       }
  218.  
  219.       const filters = SelectedFilters.getFilters();
  220.  
  221.       this.searchInProgress = true;
  222.       filters.limit = this.limit;
  223.       filters.randomize = this.randomizeSelection;
  224.  
  225.       this.clearSearch();
  226.       this.searchResult = await api.search(filters);
  227.  
  228.       this.searchResult.forEach(car => this.$set(this.excludedCars, car.id, false));
  229.       this.searchInProgress = false;
  230.       if (window) {
  231.         window.scrollTo(0, 0);
  232.       }
  233.     },
  234.  
  235.     async importCars() {
  236.       if (this.isImporting) {
  237.         return;
  238.       }
  239.  
  240.       this.isImporting = true;
  241.  
  242.       const ids = this
  243.           .searchResult
  244.           .map(car => car.id)
  245.           .filter(id => this.excludedCars[id] === false);
  246.  
  247.       await api
  248.           .import(ids)
  249.           .then(Nova.success('Синхронизация запущена'));
  250.     },
  251.  
  252.     async getTotalJobs() {
  253.       const status = await api.getJobStatus();
  254.       this.totalJobs = status.total;
  255.       this.isImporting = this.totalJobs !== 0;
  256.     },
  257.  
  258.     startTotalJobsMonitoring() {
  259.       if (this.totalJobsIntervalHandle) {
  260.         return;
  261.       }
  262.       this.totalJobsIntervalHandle = setInterval(
  263.           () => this.getTotalJobs(),
  264.           this.totalJobsIntervalPeriod
  265.       );
  266.     },
  267.  
  268.     clearSearch() {
  269.       this.emptySearchMessage = "Ничего не найдено";
  270.       this.currentPage = 1;
  271.       this.excludedCars = {};
  272.       this.selectedCustom = false;
  273.       this.selectedAll = true;
  274.       this.searchResult = [];
  275.     },
  276.  
  277.     selectAll(value) {
  278.       this.selectedCustom = false;
  279.       this.searchResult.forEach(car => this.$set(this.excludedCars, car.id, !this.selectedAll));
  280.     },
  281.  
  282.     onPageChanged(pageNumber) {
  283.       this.currentPage = pageNumber;
  284.     },
  285.  
  286.     onCarChecked(carId, excluded) {
  287.       this.selectedCustom = true;
  288.       this.$set(this.excludedCars, carId, excluded);
  289.     },
  290.  
  291.     clearFilters() {
  292.       SelectedFilters.clearFilters();
  293.     },
  294.  
  295.     probablyTooMuchToSearch() {
  296.       return
  297.     }
  298.   }
  299. }
  300. </script>
  301.  
  302. <style scoped lang="scss">
  303.  
  304. .header {
  305.   font-weight: bold;
  306. }
  307.  
  308. .filters {
  309.   .filter-box {
  310.     border: 1px solid #f1f3f6;
  311.     width: 360px;
  312.  
  313.     .filter-textbox {
  314.       width: 160px;
  315.       outline: 1px solid #ddd;
  316.       margin-top: 2px;
  317.       height: 22px;
  318.  
  319.       &:focus {
  320.         outline: 1px solid #aaa;
  321.       }
  322.     }
  323.   }
  324.  
  325.   .randomize {
  326.     margin-top: 20px;
  327.   }
  328. }
  329.  
  330. .search-results {
  331.   width: 100%;
  332.   margin-left: 20px;
  333. }
  334.  
  335. .inactive {
  336.   opacity: 50%;
  337. }
  338.  
  339. .loader {
  340.   width: 100px;
  341.   height: 100px;
  342.   animation-name: spin;
  343.   animation-duration: 5000ms;
  344.   animation-iteration-count: infinite;
  345.   animation-timing-function: linear;
  346. }
  347. .px-view {
  348.   >div {
  349.     margin-left: -30px;
  350.     margin-right: -30px;
  351.   }
  352. }
  353. </style>
  354.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement