|  | @@ -1,85 +1,132 @@
 | 
	
		
			
				|  |  |  <script setup lang="ts">
 | 
	
		
			
				|  |  | +import _ from 'lodash'
 | 
	
		
			
				|  |  | +import type { Ref } from 'vue'
 | 
	
		
			
				|  |  |  import StdTable from '@/components/StdDesign/StdDataDisplay/StdTable.vue'
 | 
	
		
			
				|  |  |  import type Curd from '@/api/curd'
 | 
	
		
			
				|  |  |  import type { Column } from '@/components/StdDesign/types'
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  const props = defineProps<{
 | 
	
		
			
				|  |  | -  selectedKey: string | number
 | 
	
		
			
				|  |  | -  value?: string | number
 | 
	
		
			
				|  |  | -  recordValueIndex: string
 | 
	
		
			
				|  |  | +  label?: string
 | 
	
		
			
				|  |  | +  selectedKey: number | number[] | undefined | null
 | 
	
		
			
				|  |  |    selectionType: 'radio' | 'checkbox'
 | 
	
		
			
				|  |  | +  recordValueIndex: string // to index the value of the record
 | 
	
		
			
				|  |  |    // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | 
	
		
			
				|  |  |    api: Curd<any>
 | 
	
		
			
				|  |  |    columns: Column[]
 | 
	
		
			
				|  |  |    disableSearch?: boolean
 | 
	
		
			
				|  |  |    // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | 
	
		
			
				|  |  | -  getParams: any
 | 
	
		
			
				|  |  | +  getParams?: any
 | 
	
		
			
				|  |  |    description?: string
 | 
	
		
			
				|  |  | +  errorMessages?: string
 | 
	
		
			
				|  |  | +  itemKey?: string // default: id
 | 
	
		
			
				|  |  | +  // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | 
	
		
			
				|  |  | +  value?: any | any[]
 | 
	
		
			
				|  |  | +  disabled?: boolean
 | 
	
		
			
				|  |  | +  // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | 
	
		
			
				|  |  | +  valueApi?: Curd<any>
 | 
	
		
			
				|  |  |  }>()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -const emit = defineEmits(['update:selectedKey', 'changeSelect'])
 | 
	
		
			
				|  |  | +const emit = defineEmits(['update:selectedKey'])
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +const getParams = computed(() => {
 | 
	
		
			
				|  |  | +  return props.getParams
 | 
	
		
			
				|  |  | +})
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  const visible = ref(false)
 | 
	
		
			
				|  |  | -const M_value = ref('')
 | 
	
		
			
				|  |  | +// eslint-disable-next-line @typescript-eslint/no-explicit-any
 | 
	
		
			
				|  |  | +const M_values = ref([]) as any
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +const init = _.debounce(_init, 500, {
 | 
	
		
			
				|  |  | +  leading: true,
 | 
	
		
			
				|  |  | +  trailing: false,
 | 
	
		
			
				|  |  | +})
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  onMounted(() => {
 | 
	
		
			
				|  |  |    init()
 | 
	
		
			
				|  |  |  })
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -const selected = ref([])
 | 
	
		
			
				|  |  |  // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | 
	
		
			
				|  |  | -const record: any = reactive({})
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -function init() {
 | 
	
		
			
				|  |  | -  if (props.selectedKey && !props.value && props.selectionType === 'radio') {
 | 
	
		
			
				|  |  | -    // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | 
	
		
			
				|  |  | -    props.api.get(props.selectedKey).then((r: any) => {
 | 
	
		
			
				|  |  | -      Object.assign(record, r)
 | 
	
		
			
				|  |  | -      M_value.value = r[props.recordValueIndex]
 | 
	
		
			
				|  |  | -    })
 | 
	
		
			
				|  |  | +const records = ref([]) as Ref<any[]>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +async function _init() {
 | 
	
		
			
				|  |  | +  // valueApi is used to fetch items that are using itemKey as index value
 | 
	
		
			
				|  |  | +  const api = props.valueApi || props.api
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  M_values.value = []
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (props.selectionType === 'radio') {
 | 
	
		
			
				|  |  | +    // M_values.value = [props.value] // not init value, we need to fetch them from api
 | 
	
		
			
				|  |  | +    if (!props.value && props.selectedKey) {
 | 
	
		
			
				|  |  | +      api.get(props.selectedKey, props.getParams).then(r => {
 | 
	
		
			
				|  |  | +        M_values.value = [r]
 | 
	
		
			
				|  |  | +        records.value = [r]
 | 
	
		
			
				|  |  | +      })
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  else if (typeof props.selectedKey === 'object') {
 | 
	
		
			
				|  |  | +    M_values.value = props.value || []
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // not init value, we need to fetch them from api
 | 
	
		
			
				|  |  | +    if (!props.value && (props.selectedKey?.length || 0) > 0) {
 | 
	
		
			
				|  |  | +      api.get_list({
 | 
	
		
			
				|  |  | +        ...props.getParams,
 | 
	
		
			
				|  |  | +        id: props.selectedKey,
 | 
	
		
			
				|  |  | +      }).then(r => {
 | 
	
		
			
				|  |  | +        M_values.value = r.data
 | 
	
		
			
				|  |  | +        records.value = r.data
 | 
	
		
			
				|  |  | +      })
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  function show() {
 | 
	
		
			
				|  |  | -  visible.value = true
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -// eslint-disable-next-line @typescript-eslint/no-explicit-any
 | 
	
		
			
				|  |  | -function onSelect(_selected: any) {
 | 
	
		
			
				|  |  | -  selected.value = _selected
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -// eslint-disable-next-line @typescript-eslint/no-explicit-any
 | 
	
		
			
				|  |  | -function onSelectedRecord(r: any) {
 | 
	
		
			
				|  |  | -  Object.assign(record, r)
 | 
	
		
			
				|  |  | +  if (!props.disabled)
 | 
	
		
			
				|  |  | +    visible.value = true
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -function ok() {
 | 
	
		
			
				|  |  | -  visible.value = false
 | 
	
		
			
				|  |  | -  if (props.selectionType === 'radio')
 | 
	
		
			
				|  |  | -    emit('update:selectedKey', selected.value[0])
 | 
	
		
			
				|  |  | -  else
 | 
	
		
			
				|  |  | -    emit('update:selectedKey', selected.value)
 | 
	
		
			
				|  |  | +const selectedKeyBuffer = ref()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  M_value.value = record[props.recordValueIndex]
 | 
	
		
			
				|  |  | -  emit('changeSelect', record)
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | +if (props.selectionType === 'radio')
 | 
	
		
			
				|  |  | +  selectedKeyBuffer.value = [props.selectedKey]
 | 
	
		
			
				|  |  | +else
 | 
	
		
			
				|  |  | +  selectedKeyBuffer.value = props.selectedKey
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -watch(props, () => {
 | 
	
		
			
				|  |  | -  if (!props?.selectedKey)
 | 
	
		
			
				|  |  | -    M_value.value = ''
 | 
	
		
			
				|  |  | -  else if (props.value)
 | 
	
		
			
				|  |  | -    M_value.value = props.value as string
 | 
	
		
			
				|  |  | -  else
 | 
	
		
			
				|  |  | -    init()
 | 
	
		
			
				|  |  | -})
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -const _selectedKey = computed({
 | 
	
		
			
				|  |  | +const computedSelectedKeys = computed({
 | 
	
		
			
				|  |  |    get() {
 | 
	
		
			
				|  |  | -    return props.selectedKey
 | 
	
		
			
				|  |  | +    if (props.selectionType === 'radio')
 | 
	
		
			
				|  |  | +      return [selectedKeyBuffer.value]
 | 
	
		
			
				|  |  | +    else
 | 
	
		
			
				|  |  | +      return selectedKeyBuffer.value
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  |    set(v) {
 | 
	
		
			
				|  |  | -    emit('update:selectedKey', v)
 | 
	
		
			
				|  |  | +    selectedKeyBuffer.value = v
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  |  })
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +onMounted(() => {
 | 
	
		
			
				|  |  | +  if (props.selectedKey === undefined || props.selectedKey === null) {
 | 
	
		
			
				|  |  | +    if (props.selectionType === 'radio')
 | 
	
		
			
				|  |  | +      emit('update:selectedKey', '')
 | 
	
		
			
				|  |  | +    else
 | 
	
		
			
				|  |  | +      emit('update:selectedKey', [])
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +})
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +async function ok() {
 | 
	
		
			
				|  |  | +  visible.value = false
 | 
	
		
			
				|  |  | +  emit('update:selectedKey', selectedKeyBuffer.value)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  M_values.value = _.clone(records.value)
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +watchEffect(() => {
 | 
	
		
			
				|  |  | +  init()
 | 
	
		
			
				|  |  | +})
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// function clear() {
 | 
	
		
			
				|  |  | +//   M_values.value = []
 | 
	
		
			
				|  |  | +//   emit('update:selectedKey', '')
 | 
	
		
			
				|  |  | +// }
 | 
	
		
			
				|  |  |  </script>
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  <template>
 | 
	
	
		
			
				|  | @@ -88,19 +135,23 @@ const _selectedKey = computed({
 | 
	
		
			
				|  |  |        class="std-selector"
 | 
	
		
			
				|  |  |        @click="show"
 | 
	
		
			
				|  |  |      >
 | 
	
		
			
				|  |  | -      <AInput
 | 
	
		
			
				|  |  | -        v-model="_selectedKey"
 | 
	
		
			
				|  |  | -        disabled
 | 
	
		
			
				|  |  | -        hidden
 | 
	
		
			
				|  |  | -      />
 | 
	
		
			
				|  |  | -      <div class="value">
 | 
	
		
			
				|  |  | -        {{ M_value }}
 | 
	
		
			
				|  |  | +      <div class="chips-container">
 | 
	
		
			
				|  |  | +        <ATag
 | 
	
		
			
				|  |  | +          v-for="(chipText, index) in M_values"
 | 
	
		
			
				|  |  | +          :key="index"
 | 
	
		
			
				|  |  | +          class="mr-1"
 | 
	
		
			
				|  |  | +          color="orange"
 | 
	
		
			
				|  |  | +          :bordered="false"
 | 
	
		
			
				|  |  | +          @click="show"
 | 
	
		
			
				|  |  | +        >
 | 
	
		
			
				|  |  | +          {{ chipText?.[recordValueIndex] }}
 | 
	
		
			
				|  |  | +        </ATag>
 | 
	
		
			
				|  |  |        </div>
 | 
	
		
			
				|  |  |        <AModal
 | 
	
		
			
				|  |  |          :mask="false"
 | 
	
		
			
				|  |  |          :open="visible"
 | 
	
		
			
				|  |  |          :cancel-text="$gettext('Cancel')"
 | 
	
		
			
				|  |  | -        :ok-text="$gettext('OK')"
 | 
	
		
			
				|  |  | +        :ok-text="$gettext('Ok')"
 | 
	
		
			
				|  |  |          :title="$gettext('Selector')"
 | 
	
		
			
				|  |  |          :width="800"
 | 
	
		
			
				|  |  |          destroy-on-close
 | 
	
	
		
			
				|  | @@ -109,15 +160,16 @@ const _selectedKey = computed({
 | 
	
		
			
				|  |  |        >
 | 
	
		
			
				|  |  |          {{ description }}
 | 
	
		
			
				|  |  |          <StdTable
 | 
	
		
			
				|  |  | +          v-model:selected-row-keys="computedSelectedKeys"
 | 
	
		
			
				|  |  | +          v-model:selected-rows="records"
 | 
	
		
			
				|  |  |            :api="api"
 | 
	
		
			
				|  |  |            :columns="columns"
 | 
	
		
			
				|  |  |            :disable-search="disableSearch"
 | 
	
		
			
				|  |  |            pithy
 | 
	
		
			
				|  |  | +          :row-key="itemKey"
 | 
	
		
			
				|  |  |            :get-params="getParams"
 | 
	
		
			
				|  |  |            :selection-type="selectionType"
 | 
	
		
			
				|  |  |            disable-query-params
 | 
	
		
			
				|  |  | -          @on-selected="onSelect"
 | 
	
		
			
				|  |  | -          @on-selected-record="onSelectedRecord"
 | 
	
		
			
				|  |  |          />
 | 
	
		
			
				|  |  |        </AModal>
 | 
	
		
			
				|  |  |      </div>
 | 
	
	
		
			
				|  | @@ -126,16 +178,18 @@ const _selectedKey = computed({
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  <style lang="less" scoped>
 | 
	
		
			
				|  |  |  .std-selector-container {
 | 
	
		
			
				|  |  | -  height: 39.9px;
 | 
	
		
			
				|  |  | +  min-height: 39.9px;
 | 
	
		
			
				|  |  |    display: flex;
 | 
	
		
			
				|  |  |    align-items: flex-start;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    .std-selector {
 | 
	
		
			
				|  |  | +    overflow-y: auto;
 | 
	
		
			
				|  |  |      box-sizing: border-box;
 | 
	
		
			
				|  |  |      font-variant: tabular-nums;
 | 
	
		
			
				|  |  |      list-style: none;
 | 
	
		
			
				|  |  |      font-feature-settings: 'tnum';
 | 
	
		
			
				|  |  | -    height: 32px;
 | 
	
		
			
				|  |  | +    min-height: 32px;
 | 
	
		
			
				|  |  | +    max-height: 100px;
 | 
	
		
			
				|  |  |      padding: 4px 11px;
 | 
	
		
			
				|  |  |      font-size: 14px;
 | 
	
		
			
				|  |  |      line-height: 1.5;
 | 
	
	
		
			
				|  | @@ -143,9 +197,15 @@ const _selectedKey = computed({
 | 
	
		
			
				|  |  |      border: 1px solid #d9d9d9;
 | 
	
		
			
				|  |  |      border-radius: 4px;
 | 
	
		
			
				|  |  |      transition: all 0.3s;
 | 
	
		
			
				|  |  | -    margin: 0 10px 0 0;
 | 
	
		
			
				|  |  | +    //margin: 0 10px 0 0;
 | 
	
		
			
				|  |  |      cursor: pointer;
 | 
	
		
			
				|  |  |      min-width: 180px;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +.chips-container {
 | 
	
		
			
				|  |  | +  span {
 | 
	
		
			
				|  |  | +    margin: 2px;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  </style>
 |