Vue项目Iconify的使用以及自定义图标,封装图标选择器

需要安装@iconify/vue

Antdsign的图标可以直接拿来使用,如下

javascript 复制代码
<Icon icon="ant-design:menu-outlined"></Icon>
import { Icon } from "@iconify/vue";

自定义图标需要获取图标的Svg的Path,然后自定义注册,记得在main.ts引入,如下

javascript 复制代码
import { addIcon } from '@iconify/vue';


addIcon('icon3-huanzheguanli', {
  width: 1024,
  height: 1024,
  body: `<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
  <path fill="#0070f3" d="
    m35,0 v35 h-35 v30 h35 v35 h30 v-35 h35 v-30 h-35 v-35 z
  "></path>
</svg>`
});

图标选择器效果如下

完整组件代码如下

javascript 复制代码
<template>
  <div class="icon-picker">
    <div class="icon-picker-trigger" @click="showDialog = true">
      <slot name="trigger">
        <div class="trigger-button">
          <Icon v-if="modelValue" :icon="modelValue" class="selected-icon" />
          <span v-else class="placeholder">
            <Icon icon="ant-design:appstore-outlined" class="placeholder-icon" />
            选择图标
          </span>
        </div>
      </slot>
    </div>

    <el-dialog
      v-model="showDialog"
      width="900px"
      :close-on-click-modal="true"
      :close-on-press-escape="true"
      :show-close="true"
      title="选择图标"
      class="icon-picker-dialog"
    >
      <div class="dialog-content">
        <!-- 搜索框 -->
        <div class="search-box">
          <Icon icon="ant-design:search-outlined" class="search-icon" />
          <input
            v-model="searchText"
            type="text"
            placeholder="搜索图标名称..."
            class="search-input"
            @input="handleSearch"
          />
          <button v-if="searchText" class="clear-btn" @click="clearSearch">
            <Icon icon="ant-design:close-circle-outlined" />
          </button>
        </div>

        <!-- 图标列表 -->
        <div class="icon-list-container">
          <div v-if="filteredIcons.length === 0" class="empty-state">
            <Icon icon="ant-design:inbox-outlined" class="empty-icon" />
            <p>未找到匹配的图标</p>
            <p class="empty-tip">试试其他关键词</p>
          </div>
          <div v-else class="icon-list">
            <div
              v-for="icon in filteredIcons"
              :key="icon.id"
              :class="['icon-item', { active: isSelected(icon) }]"
              @click="selectIcon(icon)"
              :title="icon.name"
            >
              <div class="icon-wrapper">
                <Icon :icon="icon.id" class="icon-display" />
              </div>
              <div class="icon-name">{{ icon.label }}</div>
            </div>
          </div>
        </div>
      </div>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue'
import { Icon } from '@iconify/vue'

const props = defineProps({
  modelValue: {
    type: String,
    default: ''
  }
})

const emit = defineEmits(['update:modelValue', 'change'])

const showDialog = ref(false)
const searchText = ref('')

// Ant Design 图标集合(常用图标,去重)
const antDesignIcons = [
  // 基础图标
  { id: 'ant-design:home-outlined', name: 'home', label: '首页' },
  { id: 'ant-design:user-outlined', name: 'user', label: '用户' },
  { id: 'ant-design:team-outlined', name: 'team', label: '团队' },
  { id: 'ant-design:setting-outlined', name: 'setting', label: '设置' },
  { id: 'ant-design:search-outlined', name: 'search', label: '搜索' },
  { id: 'ant-design:menu-outlined', name: 'menu', label: '菜单' },
  { id: 'ant-design:appstore-outlined', name: 'appstore', label: '应用' },
  { id: 'ant-design:close-outlined', name: 'close', label: '关闭' },
  { id: 'ant-design:check-outlined', name: 'check', label: '确认' },
  { id: 'ant-design:plus-outlined', name: 'plus', label: '添加' },
  { id: 'ant-design:minus-outlined', name: 'minus', label: '减少' },
  { id: 'ant-design:delete-outlined', name: 'delete', label: '删除' },
  { id: 'ant-design:edit-outlined', name: 'edit', label: '编辑' },
  { id: 'ant-design:save-outlined', name: 'save', label: '保存' },
  { id: 'ant-design:download-outlined', name: 'download', label: '下载' },
  { id: 'ant-design:upload-outlined', name: 'upload', label: '上传' },
  { id: 'ant-design:folder-outlined', name: 'folder', label: '文件夹' },
  { id: 'ant-design:folder-open-outlined', name: 'folder-open', label: '打开文件夹' },
  { id: 'ant-design:file-outlined', name: 'file', label: '文件' },
  { id: 'ant-design:file-text-outlined', name: 'file-text', label: '文本文件' },
  { id: 'ant-design:file-image-outlined', name: 'file-image', label: '图片文件' },
  { id: 'ant-design:picture-outlined', name: 'picture', label: '照片' },
  { id: 'ant-design:video-camera-outlined', name: 'video-camera', label: '视频' },
  { id: 'ant-design:mail-outlined', name: 'mail', label: '邮件' },
  { id: 'ant-design:phone-outlined', name: 'phone', label: '电话' },
  { id: 'ant-design:calendar-outlined', name: 'calendar', label: '日历' },
  { id: 'ant-design:clock-circle-outlined', name: 'clock-circle', label: '时钟' },
  { id: 'ant-design:bell-outlined', name: 'bell', label: '通知' },
  { id: 'ant-design:star-outlined', name: 'star', label: '收藏' },
  { id: 'ant-design:heart-outlined', name: 'heart', label: '喜欢' },
  { id: 'ant-design:like-outlined', name: 'like', label: '点赞' },
  { id: 'ant-design:share-alt-outlined', name: 'share-alt', label: '分享' },
  { id: 'ant-design:printer-outlined', name: 'printer', label: '打印' },
  { id: 'ant-design:copy-outlined', name: 'copy', label: '复制' },
  { id: 'ant-design:scissor-outlined', name: 'scissor', label: '剪切' },
  { id: 'ant-design:redo-outlined', name: 'redo', label: '重做' },
  { id: 'ant-design:undo-outlined', name: 'undo', label: '撤销' },
  
  // 方向图标
  { id: 'ant-design:arrow-up-outlined', name: 'arrow-up', label: '向上' },
  { id: 'ant-design:arrow-down-outlined', name: 'arrow-down', label: '向下' },
  { id: 'ant-design:arrow-left-outlined', name: 'arrow-left', label: '向左' },
  { id: 'ant-design:arrow-right-outlined', name: 'arrow-right', label: '向右' },
  { id: 'ant-design:up-outlined', name: 'up', label: '上' },
  { id: 'ant-design:down-outlined', name: 'down', label: '下' },
  { id: 'ant-design:left-outlined', name: 'left', label: '左' },
  { id: 'ant-design:right-outlined', name: 'right', label: '右' },
  { id: 'ant-design:double-left-outlined', name: 'double-left', label: '双左' },
  { id: 'ant-design:double-right-outlined', name: 'double-right', label: '双右' },
  
  // 状态图标
  { id: 'ant-design:check-circle-outlined', name: 'check-circle', label: '成功' },
  { id: 'ant-design:close-circle-outlined', name: 'close-circle', label: '错误' },
  { id: 'ant-design:exclamation-circle-outlined', name: 'exclamation-circle', label: '警告' },
  { id: 'ant-design:info-circle-outlined', name: 'info-circle', label: '信息' },
  { id: 'ant-design:question-circle-outlined', name: 'question-circle', label: '疑问' },
  { id: 'ant-design:stop-outlined', name: 'stop', label: '停止' },
  { id: 'ant-design:warning-outlined', name: 'warning', label: '警告' },
  
  // 数据图标
  { id: 'ant-design:bar-chart-outlined', name: 'bar-chart', label: '柱状图' },
  { id: 'ant-design:line-chart-outlined', name: 'line-chart', label: '折线图' },
  { id: 'ant-design:pie-chart-outlined', name: 'pie-chart', label: '饼图' },
  { id: 'ant-design:area-chart-outlined', name: 'area-chart', label: '面积图' },
  { id: 'ant-design:table-outlined', name: 'table', label: '表格' },
  { id: 'ant-design:database-outlined', name: 'database', label: '数据库' },
  { id: 'ant-design:cloud-outlined', name: 'cloud', label: '云' },
  { id: 'ant-design:cloud-upload-outlined', name: 'cloud-upload', label: '云上传' },
  { id: 'ant-design:cloud-download-outlined', name: 'cloud-download', label: '云下载' },
  
  // 操作图标
  { id: 'ant-design:reload-outlined', name: 'reload', label: '刷新' },
  { id: 'ant-design:sync-outlined', name: 'sync', label: '同步' },
  { id: 'ant-design:poweroff-outlined', name: 'poweroff', label: '电源' },
  { id: 'ant-design:lock-outlined', name: 'lock', label: '锁定' },
  { id: 'ant-design:unlock-outlined', name: 'unlock', label: '解锁' },
  { id: 'ant-design:eye-outlined', name: 'eye', label: '查看' },
  { id: 'ant-design:eye-invisible-outlined', name: 'eye-invisible', label: '隐藏' },
  { id: 'ant-design:filter-outlined', name: 'filter', label: '筛选' },
  { id: 'ant-design:sort-ascending-outlined', name: 'sort-ascending', label: '升序' },
  { id: 'ant-design:sort-descending-outlined', name: 'sort-descending', label: '降序' },
  
  // 其他常用图标
  { id: 'ant-design:shopping-cart-outlined', name: 'shopping-cart', label: '购物车' },
  { id: 'ant-design:shopping-outlined', name: 'shopping', label: '购物' },
  { id: 'ant-design:gift-outlined', name: 'gift', label: '礼物' },
  { id: 'ant-design:tag-outlined', name: 'tag', label: '标签' },
  { id: 'ant-design:tags-outlined', name: 'tags', label: '标签组' },
  { id: 'ant-design:book-outlined', name: 'book', label: '书籍' },
  { id: 'ant-design:read-outlined', name: 'read', label: '阅读' },
  { id: 'ant-design:file-search-outlined', name: 'file-search', label: '文件搜索' },
  { id: 'ant-design:folder-add-outlined', name: 'folder-add', label: '新建文件夹' },
  { id: 'ant-design:inbox-outlined', name: 'inbox', label: '收件箱' },
  { id: 'ant-design:message-outlined', name: 'message', label: '消息' },
  { id: 'ant-design:notification-outlined', name: 'notification', label: '通知' },
  { id: 'ant-design:customer-service-outlined', name: 'customer-service', label: '客服' },
  { id: 'ant-design:question-outlined', name: 'question', label: '帮助' },
  { id: 'ant-design:tool-outlined', name: 'tool', label: '工具' },
  { id: 'ant-design:api-outlined', name: 'api', label: 'API' },
  { id: 'ant-design:code-outlined', name: 'code', label: '代码' },
  { id: 'ant-design:bug-outlined', name: 'bug', label: 'Bug' },
  { id: 'ant-design:experiment-outlined', name: 'experiment', label: '实验' },
  { id: 'ant-design:thunderbolt-outlined', name: 'thunderbolt', label: '闪电' },
  { id: 'ant-design:fire-outlined', name: 'fire', label: '火焰' },
  { id: 'ant-design:rocket-outlined', name: 'rocket', label: '火箭' },
  { id: 'ant-design:global-outlined', name: 'global', label: '全球' },
  { id: 'ant-design:compass-outlined', name: 'compass', label: '指南针' },
  { id: 'ant-design:environment-outlined', name: 'environment', label: '位置' },
  { id: 'ant-design:car-outlined', name: 'car', label: '汽车' },
  { id: 'ant-design:medicine-box-outlined', name: 'medicine-box', label: '医疗' },
  { id: 'ant-design:bank-outlined', name: 'bank', label: '银行' },
  { id: 'ant-design:shop-outlined', name: 'shop', label: '商店' },
  { id: 'ant-design:build-outlined', name: 'build', label: '建筑' },
  { id: 'ant-design:audit-outlined', name: 'audit', label: '审核' },
  { id: 'ant-design:security-scan-outlined', name: 'security-scan', label: '安全扫描' },
  { id: 'ant-design:safety-outlined', name: 'safety', label: '安全' },
  { id: 'ant-design:key-outlined', name: 'key', label: '钥匙' },
  { id: 'ant-design:wallet-outlined', name: 'wallet', label: '钱包' },
  { id: 'ant-design:credit-card-outlined', name: 'credit-card', label: '信用卡' },
  { id: 'ant-design:pay-circle-outlined', name: 'pay-circle', label: '支付' },
  { id: 'ant-design:transaction-outlined', name: 'transaction', label: '交易' },
  { id: 'ant-design:account-book-outlined', name: 'account-book', label: '账本' },
  { id: 'ant-design:calculator-outlined', name: 'calculator', label: '计算器' },
  { id: 'ant-design:fund-outlined', name: 'fund', label: '基金' },
  { id: 'ant-design:stock-outlined', name: 'stock', label: '股票' },
  { id: 'ant-design:gold-outlined', name: 'gold', label: '黄金' },
  { id: 'ant-design:trophy-outlined', name: 'trophy', label: '奖杯' },
  { id: 'ant-design:crown-outlined', name: 'crown', label: '皇冠' },
  { id: 'ant-design:bulb-outlined', name: 'bulb', label: '灯泡' },
  { id: 'ant-design:highlight-outlined', name: 'highlight', label: '高亮' },
  { id: 'ant-design:pushpin-outlined', name: 'pushpin', label: '图钉' },
  { id: 'ant-design:flag-outlined', name: 'flag', label: '旗帜' },
  { id: 'ant-design:link-outlined', name: 'link', label: '链接' },
  { id: 'ant-design:disconnect-outlined', name: 'disconnect', label: '断开' },
  { id: 'ant-design:wifi-outlined', name: 'wifi', label: 'WiFi' },
  { id: 'ant-design:usb-outlined', name: 'usb', label: 'USB' },
  { id: 'ant-design:desktop-outlined', name: 'desktop', label: '桌面' },
  { id: 'ant-design:laptop-outlined', name: 'laptop', label: '笔记本' },
  { id: 'ant-design:tablet-outlined', name: 'tablet', label: '平板' },
  { id: 'ant-design:mobile-outlined', name: 'mobile', label: '手机' },
  { id: 'ant-design:scan-outlined', name: 'scan', label: '扫描' },
  { id: 'ant-design:qrcode-outlined', name: 'qrcode', label: '二维码' },
  { id: 'ant-design:barcode-outlined', name: 'barcode', label: '条形码' },
  { id: 'ant-design:camera-outlined', name: 'camera', label: '相机' },
  { id: 'ant-design:video-camera-add-outlined', name: 'video-camera-add', label: '添加视频' },
  { id: 'ant-design:audio-outlined', name: 'audio', label: '音频' },
  { id: 'ant-design:contacts-outlined', name: 'contacts', label: '联系人' },
  { id: 'ant-design:usergroup-add-outlined', name: 'usergroup-add', label: '添加用户组' },
  { id: 'ant-design:user-add-outlined', name: 'user-add', label: '添加用户' },
  { id: 'ant-design:user-delete-outlined', name: 'user-delete', label: '删除用户' },
  { id: 'ant-design:user-switch-outlined', name: 'user-switch', label: '切换用户' },
  { id: 'ant-design:logout-outlined', name: 'logout', label: '退出' },
  { id: 'ant-design:login-outlined', name: 'login', label: '登录' },
  { id: 'ant-design:safety-certificate-outlined', name: 'safety-certificate', label: '安全证书' },
  { id: 'ant-design:idcard-outlined', name: 'idcard', label: '身份证' },
  { id: 'ant-design:verified-outlined', name: 'verified', label: '已验证' },
  { id: 'ant-design:file-protect-outlined', name: 'file-protect', label: '文件保护' },
  { id: 'ant-design:file-excel-outlined', name: 'file-excel', label: 'Excel' },
  { id: 'ant-design:file-pdf-outlined', name: 'file-pdf', label: 'PDF' },
  { id: 'ant-design:file-word-outlined', name: 'file-word', label: 'Word' },
  { id: 'ant-design:file-ppt-outlined', name: 'file-ppt', label: 'PPT' },
  { id: 'ant-design:file-zip-outlined', name: 'file-zip', label: 'ZIP' },
  { id: 'ant-design:file-markdown-outlined', name: 'file-markdown', label: 'Markdown' },
  { id: 'ant-design:file-unknown-outlined', name: 'file-unknown', label: '未知文件' },
  { id: 'ant-design:file-add-outlined', name: 'file-add', label: '新建文件' },
  { id: 'ant-design:file-done-outlined', name: 'file-done', label: '完成文件' },
  { id: 'ant-design:file-exclamation-outlined', name: 'file-exclamation', label: '文件警告' },
  { id: 'ant-design:file-sync-outlined', name: 'file-sync', label: '文件同步' },
  { id: 'ant-design:file-search-outlined', name: 'file-search', label: '文件搜索' },
  { id: 'ant-design:folder-view-outlined', name: 'folder-view', label: '查看文件夹' },
  { id: 'ant-design:play-circle-outlined', name: 'play-circle', label: '播放' },
  { id: 'ant-design:pause-circle-outlined', name: 'pause-circle', label: '暂停' },
  { id: 'ant-design:step-backward-outlined', name: 'step-backward', label: '上一步' },
  { id: 'ant-design:step-forward-outlined', name: 'step-forward', label: '下一步' },
  { id: 'ant-design:fast-backward-outlined', name: 'fast-backward', label: '快退' },
  { id: 'ant-design:fast-forward-outlined', name: 'fast-forward', label: '快进' },
  { id: 'ant-design:shrink-outlined', name: 'shrink', label: '收缩' },
  { id: 'ant-design:arrows-alt-outlined', name: 'arrows-alt', label: '全屏' },
  { id: 'ant-design:fullscreen-outlined', name: 'fullscreen', label: '全屏' },
  { id: 'ant-design:fullscreen-exit-outlined', name: 'fullscreen-exit', label: '退出全屏' },
  { id: 'ant-design:zoom-in-outlined', name: 'zoom-in', label: '放大' },
  { id: 'ant-design:zoom-out-outlined', name: 'zoom-out', label: '缩小' },
  { id: 'ant-design:expand-outlined', name: 'expand', label: '展开' },
  { id: 'ant-design:compress-outlined', name: 'compress', label: '压缩' },
  { id: 'ant-design:enter-outlined', name: 'enter', label: '进入' },
  { id: 'ant-design:export-outlined', name: 'export', label: '导出' },
  { id: 'ant-design:import-outlined', name: 'import', label: '导入' },
  { id: 'ant-design:swap-outlined', name: 'swap', label: '交换' },
  { id: 'ant-design:swap-left-outlined', name: 'swap-left', label: '左交换' },
  { id: 'ant-design:swap-right-outlined', name: 'swap-right', label: '右交换' },
  { id: 'ant-design:retweet-outlined', name: 'retweet', label: '转发' },
  { id: 'ant-design:rollback-outlined', name: 'rollback', label: '回滚' },
  { id: 'ant-design:history-outlined', name: 'history', label: '历史' },
  { id: 'ant-design:schedule-outlined', name: 'schedule', label: '日程' },
  { id: 'ant-design:number-outlined', name: 'number', label: '数字' },
  { id: 'ant-design:font-size-outlined', name: 'font-size', label: '字体大小' },
  { id: 'ant-design:bold-outlined', name: 'bold', label: '粗体' },
  { id: 'ant-design:italic-outlined', name: 'italic', label: '斜体' },
  { id: 'ant-design:underline-outlined', name: 'underline', label: '下划线' },
  { id: 'ant-design:strikethrough-outlined', name: 'strikethrough', label: '删除线' },
  { id: 'ant-design:font-colors-outlined', name: 'font-colors', label: '字体颜色' },
  { id: 'ant-design:bg-colors-outlined', name: 'bg-colors', label: '背景颜色' },
  { id: 'ant-design:align-left-outlined', name: 'align-left', label: '左对齐' },
  { id: 'ant-design:align-center-outlined', name: 'align-center', label: '居中' },
  { id: 'ant-design:align-right-outlined', name: 'align-right', label: '右对齐' },
  { id: 'ant-design:ordered-list-outlined', name: 'ordered-list', label: '有序列表' },
  { id: 'ant-design:unordered-list-outlined', name: 'unordered-list', label: '无序列表' },
  { id: 'ant-design:line-height-outlined', name: 'line-height', label: '行高' },
  { id: 'ant-design:column-width-outlined', name: 'column-width', label: '列宽' },
  { id: 'ant-design:border-outlined', name: 'border', label: '边框' },
  { id: 'ant-design:border-inner-outlined', name: 'border-inner', label: '内边框' },
  { id: 'ant-design:border-outer-outlined', name: 'border-outer', label: '外边框' },
  { id: 'ant-design:border-left-outlined', name: 'border-left', label: '左边框' },
  { id: 'ant-design:border-right-outlined', name: 'border-right', label: '右边框' },
  { id: 'ant-design:border-top-outlined', name: 'border-top', label: '上边框' },
  { id: 'ant-design:border-bottom-outlined', name: 'border-bottom', label: '下边框' },
  { id: 'ant-design:radius-setting-outlined', name: 'radius-setting', label: '圆角设置' },
  { id: 'ant-design:radius-bottomleft-outlined', name: 'radius-bottomleft', label: '左下圆角' },
  { id: 'ant-design:radius-bottomright-outlined', name: 'radius-bottomright', label: '右下圆角' },
  { id: 'ant-design:radius-upleft-outlined', name: 'radius-upleft', label: '左上圆角' },
  { id: 'ant-design:radius-upright-outlined', name: 'radius-upright', label: '右上圆角' },
  { id: 'icon3-shoushuguanli', name: 'shoushuguanli', label: '手术管理' },
]

// 过滤后的图标
const filteredIcons = computed(() => {
  if (!searchText.value.trim()) {
    return antDesignIcons
  }
  
  const keyword = searchText.value.toLowerCase()
  return antDesignIcons.filter(icon => 
    icon.name.toLowerCase().includes(keyword) ||
    icon.label.toLowerCase().includes(keyword) ||
    icon.id.toLowerCase().includes(keyword)
  )
})

// 判断是否选中
const isSelected = (icon: typeof antDesignIcons[0]) => {
  return props.modelValue === icon.id
}

// 选择图标
const selectIcon = (icon: typeof antDesignIcons[0]) => {
  emit('update:modelValue', icon.id)
  emit('change', icon)
  showDialog.value = false
  searchText.value = ''
}

// 清除搜索
const clearSearch = () => {
  searchText.value = ''
}

// 处理搜索
const handleSearch = () => {
  // 搜索逻辑已在 computed 中处理
}
</script>

<style scoped>
.icon-picker {
  display: inline-block;
}

.trigger-button {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px;
  border: 1px solid #dcdfe6;
  border-radius: 6px;
  background: #fff;
  cursor: pointer;
  transition: all 0.3s ease;
  font-size: 14px;
  color: #606266;
}

.trigger-button:hover {
  border-color: #409eff;
  color: #409eff;
  box-shadow: 0 2px 4px rgba(64, 158, 255, 0.1);
}

.selected-icon {
  width: 18px;
  height: 18px;
  color: #409eff;
}

.placeholder {
  display: flex;
  align-items: center;
  gap: 6px;
  color: #909399;
}

.placeholder-icon {
  width: 16px;
  height: 16px;
}

.dialog-content {
  display: flex;
  flex-direction: column;
  height: 600px;
}

.search-box {
  position: relative;
  padding: 20px;
  border-bottom: 1px solid #ebeef5;
  flex-shrink: 0;
}

.search-icon {
  position: absolute;
  left: 32px;
  top: 50%;
  transform: translateY(-50%);
  width: 18px;
  height: 18px;
  color: #909399;
  pointer-events: none;
}

.search-input {
  width: 100%;
  padding: 10px 40px 10px 40px;
  border: 1px solid #dcdfe6;
  border-radius: 6px;
  font-size: 14px;
  outline: none;
  transition: all 0.3s ease;
  background: #fafafa;
}

.search-input:focus {
  border-color: #409eff;
  background: #fff;
  box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1);
}

.clear-btn {
  position: absolute;
  right: 32px;
  top: 50%;
  transform: translateY(-50%);
  width: 20px;
  height: 20px;
  border: none;
  background: transparent;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #c0c4cc;
  transition: color 0.3s ease;
}

.clear-btn:hover {
  color: #909399;
}

.icon-list-container {
  flex: 1;
  overflow-y: auto;
  padding: 20px;
  background: #fafafa;
}

.icon-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
  gap: 12px;
}

.icon-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 20px 12px;
  border: 1px solid #ebeef5;
  border-radius: 8px;
  cursor: pointer;
  transition: all 0.3s ease;
  background: #fff;
  position: relative;
  overflow: hidden;
}

.icon-item::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: linear-gradient(135deg, rgba(64, 158, 255, 0.05) 0%, rgba(64, 158, 255, 0.02) 100%);
  opacity: 0;
  transition: opacity 0.3s ease;
}

.icon-item:hover {
  border-color: #409eff;
  background: #ecf5ff;
  transform: translateY(-4px);
  box-shadow: 0 4px 12px rgba(64, 158, 255, 0.15);
}

.icon-item:hover::before {
  opacity: 1;
}

.icon-item.active {
  border-color: #409eff;
  background: linear-gradient(135deg, #ecf5ff 0%, #d9ecff 100%);
  box-shadow: 0 2px 8px rgba(64, 158, 255, 0.2);
}

.icon-item.active::before {
  opacity: 1;
}

.icon-wrapper {
  width: 48px;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 8px;
  position: relative;
  z-index: 1;
}

.icon-display {
  width: 28px;
  height: 28px;
  color: #606266;
  transition: all 0.3s ease;
}

.icon-item:hover .icon-display {
  color: #409eff;
  transform: scale(1.1);
}

.icon-item.active .icon-display {
  color: #409eff;
}

.icon-name {
  font-size: 12px;
  color: #606266;
  text-align: center;
  word-break: break-all;
  line-height: 1.4;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  position: relative;
  z-index: 1;
  transition: color 0.3s ease;
}

.icon-item:hover .icon-name {
  color: #409eff;
  font-weight: 500;
}

.icon-item.active .icon-name {
  color: #409eff;
  font-weight: 500;
}

.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 80px 20px;
  color: #909399;
}

.empty-icon {
  width: 64px;
  height: 64px;
  color: #c0c4cc;
  margin-bottom: 16px;
}

.empty-state p {
  margin: 8px 0;
  font-size: 14px;
}

.empty-tip {
  font-size: 12px;
  color: #c0c4cc;
}

/* 滚动条样式 */
.icon-list-container::-webkit-scrollbar {
  width: 8px;
}

.icon-list-container::-webkit-scrollbar-track {
  background: #f5f7fa;
  border-radius: 4px;
}

.icon-list-container::-webkit-scrollbar-thumb {
  background: #dcdfe6;
  border-radius: 4px;
  transition: background 0.3s ease;
}

.icon-list-container::-webkit-scrollbar-thumb:hover {
  background: #c0c4cc;
}

/* Dialog 样式优化 */
:deep(.el-dialog) {
  border-radius: 12px;
  overflow: hidden;
}

:deep(.el-dialog__header) {
  padding: 20px 24px;
  border-bottom: 1px solid #ebeef5;
  background: #fff;
}

:deep(.el-dialog__title) {
  font-size: 18px;
  font-weight: 600;
  color: #303133;
}

:deep(.el-dialog__body) {
  padding: 0;
}
</style>
相关推荐
前端大卫1 小时前
CSS 属性值 initial、unset 和 revert 的解析
前端
shimh_凉茶1 小时前
webpack+vue2打包分析视图插件 webpack-bundle-analyzer
前端·webpack·node.js
P***25391 小时前
JavaScript部署
开发语言·前端·javascript
一只小阿乐1 小时前
react 状态管理mobx中的行为模式
前端·javascript·react.js·mobx·vue开发·react开发
l***O5201 小时前
前端路由历史监听,React与Vue实现
前端·vue.js·react.js
超级战斗鸡1 小时前
React 性能优化教程:useMemo 和 useCallback 的正确使用方式
前端·react.js·性能优化
bemyrunningdog1 小时前
创建 React 项目指南:Vite 与 Create React App 详
前端·react.js·前端框架
大雷神2 小时前
DevUI 实战教程:从零构建电商后台管理系统(完整版)
前端·javascript·华为·angular.js
come112342 小时前
现代前端技术栈关系详解 (PHP 开发者特供版)
开发语言·前端·php