transfer-panel.vue 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. <template>
  2. <div :class="ns.b('panel')">
  3. <p :class="ns.be('panel', 'header')">
  4. <el-checkbox
  5. v-model="allChecked"
  6. :indeterminate="isIndeterminate"
  7. :validate-event="false"
  8. @change="handleAllCheckedChange"
  9. >
  10. {{ title }}
  11. <span>{{ checkedSummary }}</span>
  12. </el-checkbox>
  13. </p>
  14. <div :class="[ns.be('panel', 'body'), ns.is('with-footer', hasFooter)]">
  15. <el-input
  16. v-if="filterable"
  17. v-model="query"
  18. :class="ns.be('panel', 'filter')"
  19. size="default"
  20. :placeholder="placeholder"
  21. :prefix-icon="Search"
  22. clearable
  23. :validate-event="false"
  24. />
  25. <el-checkbox-group
  26. v-show="!hasNoMatch && !isEmpty(data)"
  27. v-model="checked"
  28. :validate-event="false"
  29. :class="[ns.is('filterable', filterable), ns.be('panel', 'list')]"
  30. >
  31. <el-checkbox
  32. v-for="item in filteredData"
  33. :key="item[propsAlias.key]"
  34. :class="ns.be('panel', 'item')"
  35. :label="item[propsAlias.key]"
  36. :disabled="item[propsAlias.disabled]"
  37. :validate-event="false"
  38. >
  39. <option-content :option="optionRender?.(item)" />
  40. </el-checkbox>
  41. </el-checkbox-group>
  42. <p v-show="hasNoMatch || isEmpty(data)" :class="ns.be('panel', 'empty')">
  43. {{ hasNoMatch ? t('el.transfer.noMatch') : t('el.transfer.noData') }}
  44. </p>
  45. </div>
  46. <p v-if="hasFooter" :class="ns.be('panel', 'footer')">
  47. <slot />
  48. </p>
  49. </div>
  50. </template>
  51. <script lang="ts" setup>
  52. import { computed, reactive, toRefs, useSlots } from 'vue'
  53. import { isEmpty } from '@element-plus/utils'
  54. import { useLocale, useNamespace } from '@element-plus/hooks'
  55. import { ElCheckbox, ElCheckboxGroup } from '@element-plus/components/checkbox'
  56. import { ElInput } from '@element-plus/components/input'
  57. import { Search } from '@element-plus/icons-vue'
  58. import { transferPanelEmits, transferPanelProps } from './transfer-panel'
  59. import { useCheck, usePropsAlias } from './composables'
  60. import type { VNode } from 'vue'
  61. import type { TransferPanelState } from './transfer-panel'
  62. defineOptions({
  63. name: 'ElTransferPanel',
  64. })
  65. const props = defineProps(transferPanelProps)
  66. const emit = defineEmits(transferPanelEmits)
  67. const slots = useSlots()
  68. const OptionContent = ({ option }: { option: VNode | VNode[] }) => option
  69. const { t } = useLocale()
  70. const ns = useNamespace('transfer')
  71. const panelState = reactive<TransferPanelState>({
  72. checked: [],
  73. allChecked: false,
  74. query: '',
  75. checkChangeByUser: true,
  76. })
  77. const propsAlias = usePropsAlias(props)
  78. const {
  79. filteredData,
  80. checkedSummary,
  81. isIndeterminate,
  82. handleAllCheckedChange,
  83. } = useCheck(props, panelState, emit)
  84. const hasNoMatch = computed(
  85. () => !isEmpty(panelState.query) && isEmpty(filteredData.value)
  86. )
  87. const hasFooter = computed(() => !isEmpty(slots.default!()[0].children))
  88. const { checked, allChecked, query } = toRefs(panelState)
  89. defineExpose({
  90. /** @description filter keyword */
  91. query,
  92. })
  93. </script>