Ben Lin
2025-03-08 745815f637e5385b2cbc23a6ae02401bb8b6c675
src/components/Icon/src/IconPicker.vue
@@ -1,13 +1,15 @@
<template>
  <a-input
    disabled
  <Input
    :style="{ width }"
    :placeholder="t('component.icon.placeholder')"
    :class="prefixCls"
    v-model:value="currentSelect"
    @click="triggerPopover"
    :allowClear="props.allowClear"
    :readonly="props.readonly"
  >
    <template #addonAfter>
      <a-popover
      <Popover
        placement="bottomLeft"
        trigger="click"
        v-model="visible"
@@ -15,7 +17,7 @@
      >
        <template #title>
          <div class="flex justify-between">
            <a-input
            <Input
              :placeholder="t('component.icon.search')"
              @change="debounceHandleSearchChange"
              allowClear
@@ -35,14 +37,13 @@
                  @click="handleClick(icon)"
                  :title="icon"
                >
                  <!-- <Icon :icon="icon" :prefix="prefix" /> -->
                  <SvgIcon v-if="isSvgMode" :name="icon" />
                  <Icon :icon="icon" v-else />
                </li>
              </ul>
            </ScrollContainer>
            <div class="flex py-2 items-center justify-center" v-if="getTotal >= pageSize">
              <a-pagination
              <Pagination
                showLessItems
                size="small"
                :pageSize="pageSize"
@@ -51,64 +52,75 @@
              />
            </div>
          </div>
          <template v-else
            ><div class="p-5"><a-empty /></div>
          <template v-else>
            <div class="p-5"><Empty /> </div>
          </template>
        </template>
        <span class="cursor-pointer px-2 py-1 flex items-center" v-if="isSvgMode && currentSelect">
          <SvgIcon :name="currentSelect" />
        </span>
        <Icon :icon="currentSelect || 'ion:apps-outline'" class="cursor-pointer px-2 py-1" v-else />
      </a-popover>
        <div ref="trigger">
          <span
            class="cursor-pointer px-2 py-1 flex items-center"
            v-if="isSvgMode && currentSelect"
          >
            <SvgIcon :name="currentSelect" />
          </span>
          <Icon
            :icon="currentSelect || 'ion:apps-outline'"
            class="cursor-pointer px-2 py-1"
            v-else
          />
        </div>
      </Popover>
    </template>
  </a-input>
  </Input>
</template>
<script lang="ts" setup>
  import { ref, watchEffect, watch, unref } from 'vue';
  import { useDesign } from '/@/hooks/web/useDesign';
  import { ScrollContainer } from '/@/components/Container';
  import { ref, watchEffect, watch } from 'vue';
  import { useDesign } from '@/hooks/web/useDesign';
  import { ScrollContainer } from '@/components/Container';
  import { Input, Popover, Pagination, Empty } from 'ant-design-vue';
  import Icon from '../Icon.vue';
  import SvgIcon from './SvgIcon.vue';
  import iconsData from '../data/icons.data';
  import { propTypes } from '/@/utils/propTypes';
  import { usePagination } from '/@/hooks/web/usePagination';
  import { usePagination } from '@/hooks/web/usePagination';
  import { useDebounceFn } from '@vueuse/core';
  import { useI18n } from '/@/hooks/web/useI18n';
  import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard';
  import { useMessage } from '/@/hooks/web/useMessage';
  import { useI18n } from '@/hooks/web/useI18n';
  import svgIcons from 'virtual:svg-icons-names';
  // 没有使用别名引入,是因为WebStorm当前版本还不能正确识别,会报unused警告
  const AInput = Input;
  const APopover = Popover;
  const APagination = Pagination;
  const AEmpty = Empty;
  import { copyText } from '@/utils/copyTextToClipboard';
  function getIcons() {
    const data = iconsData as any;
    const prefix: string = data?.prefix ?? '';
    let result: string[] = [];
    if (prefix) {
      result = (data?.icons ?? []).map((item) => `${prefix}:${item}`);
    } else if (Array.isArray(iconsData)) {
      result = iconsData as string[];
    }
    return result;
    const prefix = iconsData.prefix;
    return iconsData.icons.map((icon) => `${prefix}:${icon}`);
  }
  function getSvgIcons() {
    return svgIcons.map((icon) => icon.replace('icon-', ''));
    return svgIcons.map((icon: string) => icon.replace('icon-', ''));
  }
  const props = defineProps({
    value: propTypes.string,
    width: propTypes.string.def('100%'),
    pageSize: propTypes.number.def(140),
    copy: propTypes.bool.def(false),
    mode: propTypes.oneOf<('svg' | 'iconify')[]>(['svg', 'iconify']).def('iconify'),
  export interface Props {
    value?: string;
    width?: string;
    pageSize?: number;
    copy?: boolean;
    mode?: 'svg' | 'iconify';
    allowClear?: boolean;
    readonly?: boolean;
  }
  const props = withDefaults(defineProps<Props>(), {
    value: '',
    width: '100%',
    pageSize: 140,
    copy: false,
    mode: 'iconify',
    allowClear: true,
    readonly: false,
  });
  // Don't inherit FormItem disabled、placeholder...
  defineOptions({
    inheritAttrs: false,
  });
  const emit = defineEmits(['change', 'update:value']);
@@ -119,22 +131,18 @@
  const currentSelect = ref('');
  const visible = ref(false);
  const currentList = ref(icons);
  const trigger = ref<HTMLDivElement>();
  const triggerPopover = () => {
    if (trigger.value) {
      trigger.value.click();
    }
  };
  const { t } = useI18n();
  const { prefixCls } = useDesign('icon-picker');
  const debounceHandleSearchChange = useDebounceFn(handleSearchChange, 100);
  let clipboardRef;
  let isSuccessRef;
  if (props.copy) {
    const clipboard = useCopyToClipboard(props.value);
    clipboardRef = clipboard?.clipboardRef;
    isSuccessRef = clipboard?.isSuccessRef;
  }
  const { createMessage } = useMessage();
  const { getPaginationList, getTotal, setCurrentPage } = usePagination(
    currentList,
@@ -149,10 +157,9 @@
    () => currentSelect.value,
    (v) => {
      emit('update:value', v);
      return emit('change', v);
      emit('change', v);
    },
  );
  function handlePageChange(page: number) {
    setCurrentPage(page);
  }
@@ -160,15 +167,13 @@
  function handleClick(icon: string) {
    currentSelect.value = icon;
    if (props.copy) {
      clipboardRef.value = icon;
      if (unref(isSuccessRef)) {
        createMessage.success(t('component.icon.copy'));
      }
      copyText(icon, t('component.icon.copy'));
    }
  }
  function handleSearchChange(e: Event) {
    const value = e.target.value;
    const value = (e.target as HTMLInputElement).value;
    if (!value) {
      setCurrentPage(1);
      currentList.value = icons;
@@ -185,6 +190,10 @@
      padding: 0;
    }
    .ant-input {
      cursor: pointer;
    }
    &-popover {
      width: 300px;