fix: replace input component in `IconPicker` (#5047)
* fix: replace input component in `IconPicker` * chore: fixed IconPicker demopull/58/MERGE
							parent
							
								
									f0db3d6b79
								
							
						
					
					
						commit
						d1862fba27
					
				| 
						 | 
				
			
			@ -102,7 +102,13 @@ async function initComponentAdapter() {
 | 
			
		|||
      return h(Button, { ...props, attrs, type: 'default' }, slots);
 | 
			
		||||
    },
 | 
			
		||||
    Divider,
 | 
			
		||||
    IconPicker,
 | 
			
		||||
    IconPicker: (props, { attrs, slots }) => {
 | 
			
		||||
      return h(
 | 
			
		||||
        IconPicker,
 | 
			
		||||
        { iconSlot: 'addonAfter', inputComponent: Input, ...props, ...attrs },
 | 
			
		||||
        slots,
 | 
			
		||||
      );
 | 
			
		||||
    },
 | 
			
		||||
    Input: withDefaultPlaceholder(Input, 'input'),
 | 
			
		||||
    InputNumber: withDefaultPlaceholder(InputNumber, 'input'),
 | 
			
		||||
    InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -88,7 +88,19 @@ async function initComponentAdapter() {
 | 
			
		|||
      return h(ElButton, { ...props, attrs, type: 'primary' }, slots);
 | 
			
		||||
    },
 | 
			
		||||
    Divider: ElDivider,
 | 
			
		||||
    IconPicker,
 | 
			
		||||
    IconPicker: (props, { attrs, slots }) => {
 | 
			
		||||
      return h(
 | 
			
		||||
        IconPicker,
 | 
			
		||||
        {
 | 
			
		||||
          iconSlot: 'append',
 | 
			
		||||
          modelValueProp: 'model-value',
 | 
			
		||||
          inputComponent: ElInput,
 | 
			
		||||
          ...props,
 | 
			
		||||
          ...attrs,
 | 
			
		||||
        },
 | 
			
		||||
        slots,
 | 
			
		||||
      );
 | 
			
		||||
    },
 | 
			
		||||
    Input: withDefaultPlaceholder(ElInput, 'input'),
 | 
			
		||||
    InputNumber: withDefaultPlaceholder(ElInputNumber, 'input'),
 | 
			
		||||
    RadioGroup: ElRadioGroup,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -89,7 +89,13 @@ async function initComponentAdapter() {
 | 
			
		|||
      return h(NButton, { ...props, attrs, type: 'primary' }, slots);
 | 
			
		||||
    },
 | 
			
		||||
    Divider: NDivider,
 | 
			
		||||
    IconPicker,
 | 
			
		||||
    IconPicker: (props, { attrs, slots }) => {
 | 
			
		||||
      return h(
 | 
			
		||||
        IconPicker,
 | 
			
		||||
        { iconSlot: 'suffix', inputComponent: NInput, ...props, ...attrs },
 | 
			
		||||
        slots,
 | 
			
		||||
      );
 | 
			
		||||
    },
 | 
			
		||||
    Input: withDefaultPlaceholder(NInput, 'input'),
 | 
			
		||||
    InputNumber: withDefaultPlaceholder(NInputNumber, 'input'),
 | 
			
		||||
    RadioGroup: NRadioGroup,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,12 +1,11 @@
 | 
			
		|||
<script setup lang="ts">
 | 
			
		||||
import { computed, ref, watch, watchEffect } from 'vue';
 | 
			
		||||
import { computed, h, ref, type VNode, watch, watchEffect } from 'vue';
 | 
			
		||||
 | 
			
		||||
import { usePagination } from '@vben/hooks';
 | 
			
		||||
import { EmptyIcon, Grip, listIcons } from '@vben/icons';
 | 
			
		||||
import { $t } from '@vben/locales';
 | 
			
		||||
import {
 | 
			
		||||
  Button,
 | 
			
		||||
  Input,
 | 
			
		||||
  Pagination,
 | 
			
		||||
  PaginationEllipsis,
 | 
			
		||||
  PaginationFirst,
 | 
			
		||||
| 
						 | 
				
			
			@ -29,12 +28,24 @@ interface Props {
 | 
			
		|||
   * 图标列表
 | 
			
		||||
   */
 | 
			
		||||
  icons?: string[];
 | 
			
		||||
  /** Input组件 */
 | 
			
		||||
  inputComponent?: VNode;
 | 
			
		||||
  /** 图标插槽名,预览图标将被渲染到此插槽中 */
 | 
			
		||||
  iconSlot?: string;
 | 
			
		||||
  /** input组件的值属性名称 */
 | 
			
		||||
  modelValueProp?: string;
 | 
			
		||||
  /** 图标样式 */
 | 
			
		||||
  iconClass?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const props = withDefaults(defineProps<Props>(), {
 | 
			
		||||
  prefix: 'ant-design',
 | 
			
		||||
  pageSize: 36,
 | 
			
		||||
  icons: () => [],
 | 
			
		||||
  inputComponent: () => h('div'),
 | 
			
		||||
  iconSlot: 'default',
 | 
			
		||||
  iconClass: 'size-4',
 | 
			
		||||
  modelValueProp: 'value',
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const emit = defineEmits<{
 | 
			
		||||
| 
						 | 
				
			
			@ -110,6 +121,19 @@ function close() {
 | 
			
		|||
  visible.value = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function onKeywordChange(v: string) {
 | 
			
		||||
  keyword.value = v;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const searchInputProps = computed(() => {
 | 
			
		||||
  return {
 | 
			
		||||
    placeholder: $t('ui.iconPicker.search'),
 | 
			
		||||
    [props.modelValueProp]: keyword.value,
 | 
			
		||||
    [`onUpdate:${props.modelValueProp}`]: onKeywordChange,
 | 
			
		||||
    class: 'mx-2',
 | 
			
		||||
  };
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
defineExpose({ toggleOpenState, open, close });
 | 
			
		||||
</script>
 | 
			
		||||
<template>
 | 
			
		||||
| 
						 | 
				
			
			@ -119,24 +143,18 @@ defineExpose({ toggleOpenState, open, close });
 | 
			
		|||
    content-class="p-0 pt-3"
 | 
			
		||||
  >
 | 
			
		||||
    <template #trigger>
 | 
			
		||||
      <slot :close="close" :icon="currentSelect" :open="open" name="trigger">
 | 
			
		||||
        <div class="flex items-center gap-2">
 | 
			
		||||
          <Input
 | 
			
		||||
            :value="currentSelect"
 | 
			
		||||
            class="flex-1 cursor-pointer"
 | 
			
		||||
            v-bind="$attrs"
 | 
			
		||||
            :placeholder="$t('ui.iconPicker.placeholder')"
 | 
			
		||||
          />
 | 
			
		||||
          <VbenIcon :icon="currentSelect || Grip" class="size-8" />
 | 
			
		||||
        </div>
 | 
			
		||||
      </slot>
 | 
			
		||||
      <component
 | 
			
		||||
        :is="inputComponent"
 | 
			
		||||
        :[modelValueProp]="currentSelect"
 | 
			
		||||
        :placeholder="$t('ui.iconPicker.placeholder')"
 | 
			
		||||
      >
 | 
			
		||||
        <template #[iconSlot]>
 | 
			
		||||
          <VbenIcon :icon="currentSelect || Grip" class="size-4" />
 | 
			
		||||
        </template>
 | 
			
		||||
      </component>
 | 
			
		||||
    </template>
 | 
			
		||||
    <div class="mb-2 flex w-full">
 | 
			
		||||
      <Input
 | 
			
		||||
        v-model="keyword"
 | 
			
		||||
        :placeholder="$t('ui.iconPicker.search')"
 | 
			
		||||
        class="mx-2"
 | 
			
		||||
      />
 | 
			
		||||
      <component :is="inputComponent" v-bind="searchInputProps" />
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <template v-if="paginationList.length > 0">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -103,7 +103,13 @@ async function initComponentAdapter() {
 | 
			
		|||
      return h(Button, { ...props, attrs, type: 'default' }, slots);
 | 
			
		||||
    },
 | 
			
		||||
    Divider,
 | 
			
		||||
    IconPicker,
 | 
			
		||||
    IconPicker: (props, { attrs, slots }) => {
 | 
			
		||||
      return h(
 | 
			
		||||
        IconPicker,
 | 
			
		||||
        { iconSlot: 'addonAfter', inputComponent: Input, ...props, ...attrs },
 | 
			
		||||
        slots,
 | 
			
		||||
      );
 | 
			
		||||
    },
 | 
			
		||||
    Input: withDefaultPlaceholder(Input, 'input'),
 | 
			
		||||
    InputNumber: withDefaultPlaceholder(InputNumber, 'input'),
 | 
			
		||||
    InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,9 +1,8 @@
 | 
			
		|||
<script lang="ts" setup>
 | 
			
		||||
import { ref } from 'vue';
 | 
			
		||||
import { h, ref } from 'vue';
 | 
			
		||||
 | 
			
		||||
import { IconPicker, Page } from '@vben/common-ui';
 | 
			
		||||
import {
 | 
			
		||||
  IconifyIcon,
 | 
			
		||||
  MdiGithub,
 | 
			
		||||
  MdiGoogle,
 | 
			
		||||
  MdiKeyboardEsc,
 | 
			
		||||
| 
						 | 
				
			
			@ -22,6 +21,8 @@ import {
 | 
			
		|||
import { Card, Input } from 'ant-design-vue';
 | 
			
		||||
 | 
			
		||||
const iconValue = ref('ant-design:trademark-outlined');
 | 
			
		||||
 | 
			
		||||
const inputComponent = h(Input);
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
| 
						 | 
				
			
			@ -84,23 +85,8 @@ const iconValue = ref('ant-design:trademark-outlined');
 | 
			
		|||
        <IconPicker class="w-[200px]" prefix="svg" />
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="mb-5 flex items-center gap-5">
 | 
			
		||||
        <span>完整替换触发组件:</span>
 | 
			
		||||
        <IconPicker class="w-[200px]">
 | 
			
		||||
          <template #trigger="{ icon }">
 | 
			
		||||
            <Input
 | 
			
		||||
              :value="icon"
 | 
			
		||||
              placeholder="点击这里选择图标"
 | 
			
		||||
              style="width: 300px"
 | 
			
		||||
            >
 | 
			
		||||
              <template #addonAfter>
 | 
			
		||||
                <IconifyIcon
 | 
			
		||||
                  :icon="icon || 'ant-design:appstore-filled'"
 | 
			
		||||
                  class="text-2xl"
 | 
			
		||||
                />
 | 
			
		||||
              </template>
 | 
			
		||||
            </Input>
 | 
			
		||||
          </template>
 | 
			
		||||
        </IconPicker>
 | 
			
		||||
        <span>使用Input:</span>
 | 
			
		||||
        <IconPicker :input-component="inputComponent" icon-slot="addonAfter" />
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="flex items-center gap-5">
 | 
			
		||||
        <span>可手动输入,只能点击图标打开弹窗:</span>
 | 
			
		||||
| 
						 | 
				
			
			@ -111,14 +97,7 @@ const iconValue = ref('ant-design:trademark-outlined');
 | 
			
		|||
          style="width: 300px"
 | 
			
		||||
        >
 | 
			
		||||
          <template #addonAfter>
 | 
			
		||||
            <IconPicker v-model="iconValue" class="w-[200px]">
 | 
			
		||||
              <template #trigger="{ icon }">
 | 
			
		||||
                <IconifyIcon
 | 
			
		||||
                  :icon="icon || 'ant-design:appstore-filled'"
 | 
			
		||||
                  class="text-2xl"
 | 
			
		||||
                />
 | 
			
		||||
              </template>
 | 
			
		||||
            </IconPicker>
 | 
			
		||||
            <IconPicker v-model="iconValue" class="w-[200px]" />
 | 
			
		||||
          </template>
 | 
			
		||||
        </Input>
 | 
			
		||||
      </div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue