mirror of
				https://github.com/scinote-eln/scinote-web.git
				synced 2025-10-31 16:49:40 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			142 lines
		
	
	
	
		
			4.3 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			142 lines
		
	
	
	
		
			4.3 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <template>
 | |
|   <checklistSelect
 | |
|     :class="{
 | |
|       ['sn-select sn-select--search hover:border-sn-sleepy-grey']: true,
 | |
|       'sn-select--open': shouldOpen,
 | |
|       'sn-select--blank': !valueLabel,
 | |
|       'disabled': disabled
 | |
|     }"
 | |
|     :className="className"
 | |
|     :optionsClassName="optionsClassName"
 | |
|     :withButtons="withButtons"
 | |
|     :withEditCursor="withEditCursor"
 | |
|     :initialSelectedValues="initialSelectedValues"
 | |
|     :options="currentOptions"
 | |
|     :placeholder="placeholder"
 | |
|     :noOptionsPlaceholder="isLoading || isLazyLoading ? i18n.t('general.loading') : noOptionsPlaceholder"
 | |
|     :disabled="disabled"
 | |
|     :shouldOpen="shouldOpen"
 | |
|     :shouldUpdateWithoutValues="shouldUpdateWithoutValues"
 | |
|     :shouldUpdateOnToggleClose="shouldUpdateOnToggleClose"
 | |
|     @reached-end="handleReachedEnd"
 | |
|     @close="shouldOpen = false"
 | |
|     @update-selected-values="selectedValues = $event"
 | |
|   >
 | |
|     <div class="flex w-full" @click="shouldOpen = !shouldOpen">
 | |
|       <input
 | |
|         ref="focusElement"
 | |
|         v-model="query"
 | |
|         type="text"
 | |
|         class="sn-select__search-input"
 | |
|         :placeholder="placeholder"
 | |
|         @blur="query = ''" />
 | |
|       <span class="sn-select__value">{{ valueLabel || (placeholder || i18n.t('general.select')) }}</span>
 | |
|       <i class="sn-icon" :class="{ 'sn-icon-down': !shouldOpen, 'sn-icon-up': shouldOpen}"></i>
 | |
|     </div>
 | |
|   </checklistSelect>
 | |
| </template>
 | |
| 
 | |
| <script>
 | |
| import checklistSelect from './checklist_select.vue';
 | |
| import { debounce } from '../debounce';
 | |
| 
 | |
| export default {
 | |
|   name: 'ChecklistSearch',
 | |
|   props: {
 | |
|     withButtons: { type: Boolean, default: false },
 | |
|     withEditCursor: { type: Boolean, default: false },
 | |
|     initialSelectedValues: { type: Array, default: () => [] },
 | |
|     options: { type: Array, default: () => [] },
 | |
|     placeholder: { type: String },
 | |
|     noOptionsPlaceholder: { type: String },
 | |
|     disabled: { type: Boolean, default: false },
 | |
|     className: { type: String, default: '' },
 | |
|     optionsClassName: { type: String, default: '' },
 | |
|     shouldUpdateWithoutValues: { type: Boolean, default: false },
 | |
|     shouldUpdateOnToggleClose: { type: Boolean, default: false },
 | |
|     isLoading: { type: Boolean, default: false },
 | |
|     params: { type: Array, default: () => [] },
 | |
|     optionsUrl: { type: String, required: true },
 | |
|     lazyLoadEnabled: { type: Boolean, default: false }
 | |
|   },
 | |
|   components: { checklistSelect },
 | |
|   emits: ['reached-end', 'update-options'],
 | |
|   data() {
 | |
|     return {
 | |
|       query: null,
 | |
|       currentOptions: null,
 | |
|       shouldOpen: false,
 | |
|       nextPage: 1,
 | |
|       isLazyLoading: false,
 | |
|       selectedValues: []
 | |
|     };
 | |
|   },
 | |
|   created() {
 | |
|     this.currentOptions = this.options;
 | |
|   },
 | |
|   watch: {
 | |
|     query() {
 | |
|       if (!this.query) {
 | |
|         this.currentOptions = this.options;
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       if (this.optionsUrl) {
 | |
|         // reset current options and next page when lazy loading is enabled
 | |
|         if (this.lazyLoadEnabled) {
 | |
|           this.currentOptions = [];
 | |
|           this.isLazyLoading = true;
 | |
|           this.nextPage = 1;
 | |
|         }
 | |
|         this.$nextTick(() => {
 | |
|           debounce(this.fetchOptions(), 10);
 | |
|         });
 | |
|       } else {
 | |
|         this.currentOptions = this.options.filter((o) => o[1].toLowerCase().includes(this.query.toLowerCase()));
 | |
|       }
 | |
|     },
 | |
|     options() {
 | |
|       this.currentOptions = this.options;
 | |
|     }
 | |
|   },
 | |
|   computed: {
 | |
|     valueLabel() {
 | |
|       if (!this.selectedValues.length) return '';
 | |
|       if (this.selectedValues.length === 1) {
 | |
|         return this.currentOptions.find(({ id }) => id === this.selectedValues[0])?.label;
 | |
|       }
 | |
|       return `${this.selectedValues.length} ${this.i18n.t('general.options_selected')}`;
 | |
|     }
 | |
|   },
 | |
|   methods: {
 | |
|     handleReachedEnd() {
 | |
|       if (this.query) {
 | |
|         this.fetchOptions();
 | |
|       } else {
 | |
|         this.$emit('reached-end');
 | |
|       }
 | |
|     },
 | |
|     fetchOptions() {
 | |
|       if (!this.nextPage || !this.optionsUrl) return;
 | |
| 
 | |
|       $.ajax({
 | |
|         url: this.optionsUrl,
 | |
|         data: {
 | |
|           ...(this.query && { query: this.query }),
 | |
|           ...(this.lazyLoadEnabled && this.nextPage && { page: this.nextPage }),
 | |
|           ...this.params
 | |
|         },
 | |
|         success: (result) => {
 | |
|           if (this.lazyLoadEnabled) {
 | |
|             this.nextPage = result.next_page;
 | |
|             this.$emit('update-options', this.currentOptions, result);
 | |
|             this.isLazyLoading = false;
 | |
|             return;
 | |
|           }
 | |
|           this.currentOptions = result;
 | |
|         }
 | |
|       });
 | |
|     }
 | |
|   }
 | |
| };
 | |
| </script>
 |