在assets的icons下面建文件夹svg,svg下载地址如下:xicons - Icons for Vue & React
这里下载的是ionicons5的svg
requireIcons.ts
import type { Ref } from 'vue';
import { ref } from 'vue';
const icons: Ref<string[]> = ref([]);
const modules = import.meta.glob('./../../../../../assets/icons/svg/*.svg');
// eslint-disable-next-line guard-for-in
for (const path in modules) {
const p = path.split('assets/icons/svg/')[1].split('.svg')[0];
icons.value.push(p);
}
export default icons;
iconSelect.vue
<template>
<div class="icon-body">
<div class="icon-list">
<div class="list-container">
<div v-for="(item, index) in iconList" :key="index" class="icon-item-wrapper" @click="selectedIcon(item)">
<div :class="['icon-item', { active: activeIcon === item }]">
<svg-icon :icon-name="item" class-name="icon" style="height: 28px; width: 16px" />
<span>{{ item }}</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import svgIcon from '../SvgIcon/index.vue';
import icons from './requireIcons';
defineProps({
activeIcon: {
type: String,
default: ''
}
});
const iconList = ref(icons);
const emit = defineEmits(['selected']);
function selectedIcon(name: string) {
emit('selected', name);
document.body.click();
}
</script>
<style lang="scss" scoped>
.icon-body {
width: 100%;
padding: 10px;
.icon-search {
position: relative;
margin-bottom: 5px;
}
.icon-list {
height: 200px;
overflow: auto;
.list-container {
display: flex;
flex-wrap: wrap;
.icon-item-wrapper {
width: calc(100% / 3);
height: 25px;
line-height: 25px;
cursor: pointer;
display: flex;
.icon-item {
display: flex;
max-width: 100%;
height: 100%;
padding: 0 5px;
&:hover {
background: #ececec;
border-radius: 5px;
}
.icon {
flex-shrink: 0;
}
span {
display: inline-block;
vertical-align: -0.15em;
fill: currentColor;
padding-left: 2px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.icon-item.active {
background: #ececec;
border-radius: 5px;
}
}
}
}
}
</style>
svgIcon.vue
<template>
<n-icon class="svg-icon" :component="icon" style="margin-top: 4px" />
</template>
<script setup lang="ts">
import type { Component } from 'vue';
import { shallowRef, defineProps, onMounted } from 'vue';
import * as icons from '@vicons/ionicons5';
const props = defineProps({
iconName: {
type: String,
required: true
},
className: {
type: String,
default: ''
},
color: {
type: String,
default: ''
}
});
const icon = shallowRef<Component>();
function getIconInfo() {
icon.value = icons[props.iconName];
}
onMounted(() => {
getIconInfo();
});
</script>
<style scope lang="scss">
.sub-el-icon,
.nav-icon {
display: inline-block;
font-size: 15px;
margin-right: 12px;
position: relative;
}
.svg-icon {
width: 1em;
height: 1em;
position: relative;
fill: currentColor;
vertical-align: -2px;
}
</style>
展示界面
<n-form-item label="菜单图标" path="icon">
<n-popover trigger="click" :show="showPopover" width="trigger" placement="bottom-start">
<template #trigger>
<n-input
v-model:value="menuForm.icon"
placeholder="点击选择图标"
style="margin-bottom: 2px"
@blur="showSelectIcon"
@click="showSelectIcon"
>
<template #prefix>
<!--如果是回显,则回显图标-->
<svg-icon v-if="menuForm.icon" :icon-name="menuForm.icon" style="height: 22px; width: 16px" />
<!--如果是新增,则显示下面图标-->
<icon-uil-search v-else class="text-20px text-#c2c2c2" />
</template>
</n-input>
</template>
<icon-select ref="iconSelectRef" :active-icon="menuForm.icon" @selected="selected"></icon-select>
</n-popover>
</n-form-item>
1.width="trigger"
使 popover 的宽度等于触发元素。
2.placement="bottom-start",弹出信息展示的方向。
3.<template #trigger<标签>/>,触发的方式
const showPopover = ref(false);
/** 展示下拉图标 */
function showSelectIcon() {
showPopover.value = !showPopover.value;
}
/** 选择图标 */
function selected(name: string) {
menuForm.icon = name;
showPopover.value = false;
}