效果图:

文件放置位置结构

1、定义选择按钮父组件 icon
html
<template>
<div>
<iconEdit :mode="'button'" />
</div>
</template>
<script lang="ts" setup>
import iconEdit from "../../components/IconEdit/indev.vue";
</script>
<style scoped lang="scss"></style>
2、IconEdit组件
html
<template>
<div>
<div v-if="mode === 'button'" class="button-container">
<IconComp v-if="icon" :icon="icon" />
<el-button link type="primary" @click="openIcon"> 选择图片 </el-button>
</div>
<el-dialog v-model="iconVisible" title="选择图标" width="900" draggable :close-on-click-modal="false">
<el-tabs v-model="activeName" class="icons-tabs">
<el-tab-pane label="内置图标" name="icon">
<div class="button-container">
<el-input
v-model="search"
:suffix-icon="Search"
placeholder="请输入关键字搜索图标"
style="width: 240px"
@input="searchAction(search)"
/>
<el-input v-model="colorInput" style="width: 120px" placeholder="颜色">
<template #suffix>
<input ref="iconPick" type="color" v-model="colorInput" class="w-[26px]" />
</template>
</el-input>
</div>
<div class="icons-container mt-3 h-[350px] overflow-y-auto">
<el-row :gutter="20">
<el-col
v-for="(item, index) in elementPlusIcons"
:key="index"
:span="2"
class="mb-1 cursor-pointer"
@click="selectAction(item.name)"
>
<div :class="{ 'selected-icon': item.name === selected }">
<component :is="item" v-bind="$attrs" :style="{ color: colorInput }" />
<p class="icons-text text-center">{{ item.name }}</p>
</div>
</el-col>
</el-row>
</div>
</el-tab-pane>
<el-tab-pane label="系统内置图片" name="icon-preset">
<el-input
v-model="search"
:suffix-icon="Search"
placeholder="请输入关键字搜索图片"
style="width: 240px"
@input="searchAction(search)"
/>
<div class="icons-container mt-3 h-[350px] overflow-y-auto">
<el-row :gutter="20">
<el-col
v-for="(item, index) in systemIcons"
:key="index"
:span="2"
class="mb-1 cursor-pointer"
@click="selectAction(item.code)"
>
<div
style="display: flex; align-items: center; flex-direction: column"
:class="{ 'selected-icon': item.code === selected }"
>
<img style="width: 50px; height: 50px" :src="item.icon" />
<p class="icons-text text-center">{{ item.name }}</p>
</div>
</el-col>
</el-row>
</div>
</el-tab-pane>
</el-tabs>
<template #footer>
<div class="dialog-footer">
<el-button @click="reset">重置</el-button>
<el-button @click="iconVisible = false">取消</el-button>
<el-button type="primary" @click="handleConfirm()"> 确定 </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import { type Component, ref, shallowRef, watch, nextTick } from "vue";
import IconComp from "../../components/IconEdit/iconComp.vue";
import { Icons, AiIcons } from "../../assets/index";
import { Search } from "@element-plus/icons-vue";
import * as ElementPlusIcons from "@element-plus/icons-vue";
const props = defineProps<{
mode?: string;
isReadonly?: boolean;
}>();
/** 数据源, 父组件不能为空,默认'' */
const icon = defineModel("icon", {
type: String,
default: "",
});
const emit = defineEmits(["confirm", "typeConfirm"]);
const activeName = ref("icon");
const iconComp = shallowRef<Component>();
const search = ref("");
const colorInput = ref("");
const selected = ref<string>("");
const systemIcons = ref<any[]>([...Icons, ...AiIcons]);
// 打开模态框
const iconVisible = ref(false);
const iconPick = ref();
// 打开弹窗
const openIcon = () => {
iconVisible.value = true;
search.value = "";
const iconArray = icon.value.split("#");
iconComp.value = ElementPlusIcons[iconArray[0] as keyof typeof ElementPlusIcons];
if (iconComp.value) {
selected.value = iconArray[0];
colorInput.value = iconArray[1] ? `#${iconArray[1]}` : "";
nextTick(() => {
iconPick.value.value = colorInput.value;
});
}
searchAction("");
};
// 关闭弹窗
const closeIcon = () => {
iconVisible.value = false;
};
const elementPlusIcons = shallowRef<any>(ElementPlusIcons);
const reset = () => {
selected.value = "";
iconComp.value = undefined;
colorInput.value = "";
iconPick.value.value = "";
};
// 搜索图标
const searchAction = (keyWord: string) => {
if (keyWord === "") {
elementPlusIcons.value = ElementPlusIcons;
return;
}
const filterIcons = Object.keys(ElementPlusIcons).filter((item) => {
return item.toLowerCase().includes(keyWord.toLowerCase());
});
elementPlusIcons.value = {};
filterIcons.forEach((item) => {
elementPlusIcons.value[item] = ElementPlusIcons[item as keyof typeof ElementPlusIcons];
});
};
const selectAction = (item: string) => {
selected.value = item;
};
// 确定图标
const handleConfirm = () => {
icon.value = activeName.value === "icon" && colorInput.value ? selected.value + colorInput.value : selected.value;
emit("confirm", icon.value);
emit("typeConfirm", {
type: activeName,
value: icon.value,
});
iconVisible.value = false;
};
watch(
() => icon.value,
(newValue) => {
if (newValue) {
const iconArray = newValue.split("#");
iconComp.value = ElementPlusIcons[iconArray[0] as keyof typeof ElementPlusIcons];
if (iconComp.value) {
selected.value = iconArray[0];
colorInput.value = iconArray[1];
nextTick(() => {
if (iconPick.value) iconPick.value.value = colorInput.value ? `#${colorInput.value}` : "";
});
}
} else {
selected.value = "";
iconComp.value = undefined;
colorInput.value = "";
}
},
{
immediate: true,
}
);
// 暴露方法·
defineExpose({
closeIcon,
openIcon,
});
</script>
<style scoped lang="scss">
.icons-tabs {
.icons-container {
padding-right: 15px;
margin-right: -10px;
}
}
.selected-icon {
background-color: #409eff;
border-radius: 6px;
}
.icons-text {
font-size: 12px;
}
.button-container {
display: flex;
gap: 0.5rem; /* 2对应大约0.5rem(8px) */
}
</style>
3、iconComp组件
html
<template>
<div>
<component v-if="iconComp" :is="iconComp" :style="{ color: icon?.split('#')[1], width: size + 'px', height: size + 'px' }" />
<img
v-else-if="systemIcons?.find((f: any) => f.code == icon)"
:style="{ width: size + 'px', height: size + 'px' }"
:src="systemIcons?.find((f: any) => f.code == icon)?.icon"
/>
<div v-else-if="icon" class="form-icon-item" v-html="icon"></div>
</div>
</template>
<script setup lang="ts">
import { watch, shallowRef, type Component } from "vue";
import * as ElementPlusIcons from "@element-plus/icons-vue";
import { AiIcons, Icons } from "../../assets/index";
const props = defineProps({
icon: {
type: String,
default: () => {},
},
size: {
type: Number,
default: 16,
},
});
const systemIcons = [...Icons, ...AiIcons];
const iconComp = shallowRef<Component>();
watch(
() => props.icon,
(newValue) => {
if (newValue) {
const iconArray = newValue.split("#");
iconComp.value = ElementPlusIcons[iconArray[0] as keyof typeof ElementPlusIcons];
}
},
{
immediate: true,
}
);
</script>
<style lang="less" scoped></style>
4、系统内置图片路径
index.ts
TypeScript
import bbmbSrc from "../assets/aiToolsIcons/baobiaomoban.png";
import aizy from "../assets/aiToolsIcons/ocr-yinshuawenzishibie.png";
import wbrs from "../assets/aiToolsIcons/wenbenbianjitianchong.png";
import dybgzx from "../assets/aiToolsIcons/baogao.png";
import dyjhzd from "../assets/aiToolsIcons/jihua.png";
import wzsb from "../assets/aiToolsIcons/ocr-yinshuawenzishibie.png";
import wdxxtq from "../assets/aiToolsIcons/wendang.png";
import pzsb from "../assets/aiToolsIcons/chuanpiaozhengfuwu.png";
import wdzh from "../assets/aiToolsIcons/zhuanhuan.png";
import wjcfhb from "../assets/aiToolsIcons/picichaifenhebing.png";
import yywbsc from "../assets/aiToolsIcons/zhihuiyuyinwenben.png";
import hgxjc from "../assets/aiToolsIcons/hefahegui.png";
import bgzdhsc from "../assets/aiToolsIcons/baobiaomoban.png";
import hyjysc from "../assets/aiToolsIcons/huiyi.png";
import wzzyy from "../assets/aiToolsIcons/zhihuiyuyinwenben.png";
import csznzs from "../assets/aiToolsIcons/测试智能助手.png";
import flfg from "../assets/aiToolsIcons/法律法规.png";
import fgk from "../assets/aiToolsIcons/法规库.png";
import flfgk from "../assets/aiToolsIcons/法律法规库@2x.png";
import nbzdk from "../assets/aiToolsIcons/内部制度库.png";
import rdjdzs from "../assets/aiToolsIcons/人大监督助手@2x.png";
import sjfxjmzs from "../assets/aiToolsIcons/审计分析建模助手.png";
import sjjdzs from "../assets/aiToolsIcons/审计监督助手@2x.png";
import sjwtdxzs from "../assets/aiToolsIcons/审计问题定性助手.png";
import sjywtjzs from "../assets/aiToolsIcons/审计业务推荐助手.png";
import sjzgzyzs from "../assets/aiToolsIcons/审计整改指引助手.png";
import xzyszt from "../assets/aiToolsIcons/新增预设主题.png";
import zbdbfxznzs from "../assets/aiToolsIcons/招标对比分析智能助手.png";
import zmzcfxzs from "../assets/aiToolsIcons/中煤招采分析助手.png";
import zdytsc from "../assets/aiToolsIcons/自定义提示词@2x.png";
import aiyyzs from "../assets/aiToolsIcons/AI应用助手@2x.png";
import bijcfxzs from "../assets/aiToolsIcons/BI集成分析助手.png";
export * from './aiIcons/index';
export const Icons = [
{
name: '标签模板',
code: 'system-bbmbSrc',
icon: bbmbSrc
},
{
name: '文字识别',
code: 'system-aizy',
icon: aizy
},
{
name: '文本编辑',
code: 'system-wbrs',
icon: wbrs
},
{
name: '报告',
code: 'system-dybgzx',
icon: dybgzx
},
{
name: '计划',
code: 'system-dyjhzd',
icon: dyjhzd
},
{
name: '识别',
code: 'system-wzsb',
icon: wzsb
},
{
name: '文档',
code: 'system-wdxxtq',
icon: wdxxtq
},
{
name: '传票',
code: 'system-pzsb',
icon: pzsb
},
{
name: '转换',
code: 'system-wdzh',
icon: wdzh
},
{
name: '拆分',
code: 'system-wjcfhb',
icon: wjcfhb
}, {
name: '语音',
code: 'system-yywbsc',
icon: yywbsc
}, {
name: '合法合规',
code: 'system-hgxjc',
icon: hgxjc
}, {
name: '报表模板',
code: 'system-bgzdhsc',
icon: bgzdhsc
}, {
name: '会议',
code: 'system-hyjysc',
icon: hyjysc
}, {
name: '语音文本',
code: 'system-wzzyy',
icon: wzzyy
},
{
name: '测试智能助手',
code: 'system-csznzs',
icon: csznzs
},
{
name: '法律法规',
code: 'system-flfg',
icon: flfg
},
{
name: '法规库',
code: 'system-fgk',
icon: fgk
},
{
name: '法律法规库',
code: 'system-flfgk',
icon: flfgk
},
{
name: '内部制度库',
code: 'system-nbzdk',
icon: nbzdk
},
{
name: '人大监督助手',
code: 'system-rdjdzs',
icon: rdjdzs
},
{
name: '审计分析建模助手',
code: 'system-sjfxjmzs',
icon: sjfxjmzs
},
{
name: '审计监督助手',
code: 'system-sjjdzs',
icon: sjjdzs
},
{
name: '审计问题定性助手',
code: 'system-sjwtdxzs',
icon: sjwtdxzs
},
{
name: '审计业务推荐助手',
code: 'system-sjywtjzs',
icon: sjywtjzs
},
{
name: '审计整改指引助手',
code: 'system-sjzgzyzs',
icon: sjzgzyzs
},
{
name: '新增预设主题',
code: 'system-xzyszt',
icon: xzyszt
},
{
name: '招标对比分析智能助手',
code: 'system-zbdbfxznzs',
icon: zbdbfxznzs
},
{
name: '中煤招采分析助手',
code: 'system-zmzcfxzs',
icon: zmzcfxzs
},
{
name: '自定义提示词',
code: 'system-zdytsc',
icon: zdytsc
},
{
name: 'AI应用助手',
code: 'system-aiyyzs',
icon: aiyyzs
},
{
name: 'BI集成分析助手',
code: 'system-bijcfxzs',
icon: bijcfxzs
},
]