feat: 图标选择器

This commit is contained in:
WANGFAN\wangf 2025-04-07 14:49:27 +08:00
parent 2a9591320f
commit 9e90d3d410
3 changed files with 123 additions and 96 deletions

33
src/components.d.ts vendored
View File

@ -5,22 +5,23 @@
// Read more: https://github.com/vuejs/core/pull/3399 // Read more: https://github.com/vuejs/core/pull/3399
export {} export {}
declare module 'vue' { declare module "vue" {
export interface GlobalComponents { export interface GlobalComponents {
BarcodeDraw: typeof import('./components/barcode-draw/index.vue')['default'] BarcodeDraw: (typeof import("./components/barcode-draw/index.vue"))["default"];
CodeView: typeof import('./components/code-view/index.vue')['default'] CodeView: (typeof import("./components/code-view/index.vue"))["default"];
ExternalLinkPage: typeof import('./components/external-link-page/index.vue')['default'] ExternalLinkPage: (typeof import("./components/external-link-page/index.vue"))["default"];
FillPage: typeof import('./components/fill-page/index.vue')['default'] FillPage: (typeof import("./components/fill-page/index.vue"))["default"];
InternalLinkPage: typeof import('./components/internal-link-page/index.vue')['default'] InternalLinkPage: (typeof import("./components/internal-link-page/index.vue"))["default"];
LangProvider: typeof import('./components/lang-provider/index.vue')['default'] LangProvider: (typeof import("./components/lang-provider/index.vue"))["default"];
MainTransition: typeof import('./components/main-transition/index.vue')['default'] MainTransition: (typeof import("./components/main-transition/index.vue"))["default"];
PinyinPro: typeof import('./components/pinyin-pro/index.vue')['default'] PinyinPro: (typeof import("./components/pinyin-pro/index.vue"))["default"];
QrcodeDraw: typeof import('./components/qrcode-draw/index.vue')['default'] QrcodeDraw: (typeof import("./components/qrcode-draw/index.vue"))["default"];
RouterLink: typeof import('vue-router')['RouterLink'] RouterLink: (typeof import("vue-router"))["RouterLink"];
RouterView: typeof import('vue-router')['RouterView'] RouterView: (typeof import("vue-router"))["RouterView"];
SelectIcon: typeof import('./components/select-icon/index.vue')['default'] SelectIcon: (typeof import("./components/select-icon/index.vue"))["default"];
SvgAndIcon: typeof import('./components/svg-and-icon/index.vue')['default'] SelectSvg: (typeof import("./components/select-svg/index.vue"))["default"];
SvgIcon: typeof import('./components/svg-icon/index.vue')['default'] SvgAndIcon: (typeof import("./components/svg-and-icon/index.vue"))["default"];
VerifyCode: typeof import('./components/verify-code/index.vue')['default'] SvgIcon: (typeof import("./components/svg-icon/index.vue"))["default"];
VerifyCode: (typeof import("./components/verify-code/index.vue"))["default"];
} }
} }

View File

@ -1,5 +1,15 @@
<template> <template>
<a-modal v-model:visible="visible" width="70vw" :top="'50px'" :align-center="false" :footer="false"> <div>
<a-input ref="inputRef" :style="{ width: '100%' }" placeholder="请选择图标" v-model="iconName" @focus="onFocus">
<template #suffix v-if="iconName">
<SvgIcon v-if="type == 'svg'" :name="iconName" :size="size" />
<component v-else :is="iconName" :size="size"></component>
</template>
<template #append>
<span class="icon-reset" @click="reset">重置</span>
</template>
</a-input>
<a-modal v-model:visible="visible" :unmount-on-close="true" width="70vw" :top="'50px'" :align-center="false" :footer="false">
<template #title> 请选择图标 </template> <template #title> 请选择图标 </template>
<a-input <a-input
:style="{ :style="{
@ -17,7 +27,8 @@
<div class="over-scroll"> <div class="over-scroll">
<div class="icon-grid"> <div class="icon-grid">
<div v-for="item in iconList" :key="item" class="grid-item" @click="onIcon(item)"> <div v-for="item in iconList" :key="item" class="grid-item" @click="onIcon(item)">
<component :is="item" :size="30"></component> <SvgIcon v-if="type == 'svg'" :name="item" :size="25" />
<component v-else :is="item" :size="25"></component>
<span>{{ item }}</span> <span>{{ item }}</span>
</div> </div>
</div> </div>
@ -31,19 +42,55 @@
</div> </div>
</div> </div>
</a-modal> </a-modal>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import * as ArcoIcons from "@arco-design/web-vue/es/icon"; import * as ArcoIcons from "@arco-design/web-vue/es/icon";
const emit = defineEmits(["select"]); interface Props {
type?: string;
size?: number;
}
const props = withDefaults(defineProps<Props>(), {
type: "arco", // arco svg
size: 25 // -25px
});
const { type, size } = toRefs(props);
const emit = defineEmits(["update:modelValue"]);
const visible = ref<boolean>(false);
const iconName = ref<string>("");
const reset = () => {
iconName.value = "";
emit("update:modelValue", "");
};
const onFocus = () => {
searchName.value = "";
visible.value = true;
};
// svg-assets/svgssvg
const SvgIconModules = import.meta.glob("@assets/svgs/*.svg");
//
const searchName = ref<string>(""); const searchName = ref<string>("");
// icon
const iconList = computed(() => { const iconList = computed(() => {
if (!ArcoIcons) return [];
let icons: string[] = []; let icons: string[] = [];
if (type.value === "arco") {
if (!ArcoIcons) return [];
for (let key in ArcoIcons) { for (let key in ArcoIcons) {
if (key != "default") icons.push(key); if (key != "default") icons.push(key);
} }
} else {
if (!SvgIconModules) return [];
for (let path in SvgIconModules) {
icons.push(path.replace("/src/assets/svgs/", "").split(".svg")[0]);
}
}
// //
if (searchName.value) { if (searchName.value) {
return icons.filter(item => item.toLowerCase().includes(searchName.value.toLowerCase())); return icons.filter(item => item.toLowerCase().includes(searchName.value.toLowerCase()));
@ -52,20 +99,12 @@ const iconList = computed(() => {
return icons; return icons;
}); });
const onIcon = (iconName: string) => { //
const onIcon = (name: string) => {
visible.value = false; visible.value = false;
emit("select", iconName); iconName.value = name;
emit("update:modelValue", name);
}; };
const visible = ref<boolean>(false);
const open = () => {
searchName.value = "";
visible.value = true;
};
defineExpose({
open
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -98,4 +137,7 @@ defineExpose({
width: 100%; width: 100%;
height: 300px; height: 300px;
} }
.icon-reset {
cursor: pointer;
}
</style> </style>

View File

@ -1,51 +1,35 @@
<template> <template>
<div class="snow-page"> <div class="snow-page">
<div class="snow-inner"> <div class="snow-inner">
<div class="title">图标选择器</div> <a-row class="grid-demo" :gutter="24">
<a-input ref="inputRef" :style="{ width: '400px' }" placeholder="请选择图标" v-model="iconName" @focus="onFocus"> <a-col :span="12">
<template #suffix v-if="iconName"> <a-card title="图标选择器" :style="{ width: '100%' }">
<component :is="iconName"></component> <SelectIcon type="arco" v-model="iconName" />
</template>
<template #append>
<span class="icon-reset" @click="reset">重置</span>
</template>
</a-input>
<a-divider />
<div class="target-title">当前选择的图标</div> <div class="target-title">当前选择的图标</div>
<component v-if="iconName" :is="iconName" size="50"></component> <component v-if="iconName" :is="iconName" :size="50"></component>
<a-empty v-else /> <a-empty v-else />
<SelectIcon ref="SelectIconRef" @select="select" /> </a-card>
</a-col>
<a-col :span="12">
<a-card title="SVG选择器" :style="{ width: '100%' }">
<SelectIcon type="svg" v-model="svgName" />
<div class="target-title">当前选择的图标</div>
<SvgIcon v-if="svgName" :name="svgName" :size="50" />
<a-empty v-else />
</a-card>
</a-col>
</a-row>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
const iconName = ref<string>(""); const iconName = ref<string>("");
const SelectIconRef = ref(); const svgName = ref<string>("");
const onFocus = () => {
SelectIconRef.value.open();
};
const reset = () => {
iconName.value = "";
};
const select = (icon: string) => {
iconName.value = icon;
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.title {
margin-bottom: $margin;
font-size: $font-size-title-1;
color: $color-text-1;
}
.target-title { .target-title {
margin-bottom: $margin; margin: $margin 0;
}
.icon-reset {
cursor: pointer;
} }
</style> </style>