2021-03-04 21:57:25 +08:00
|
|
|
|
<template>
|
2022-04-19 20:31:05 +08:00
|
|
|
|
<div class="icon-selector w100 h100">
|
2022-04-18 19:14:38 +08:00
|
|
|
|
<el-popover placement="bottom" :width="fontIconWidth" trigger="click" transition="el-zoom-in-top" popper-class="icon-selector-popper">
|
2021-03-15 12:44:58 +08:00
|
|
|
|
<template #reference>
|
|
|
|
|
<el-input
|
2021-06-29 17:20:40 +08:00
|
|
|
|
v-model="fontIconSearch"
|
2021-07-04 01:09:37 +08:00
|
|
|
|
:placeholder="fontIconPlaceholder"
|
2021-06-29 17:20:40 +08:00
|
|
|
|
:clearable="clearable"
|
|
|
|
|
:disabled="disabled"
|
|
|
|
|
:size="size"
|
2021-03-15 12:44:58 +08:00
|
|
|
|
ref="inputWidthRef"
|
|
|
|
|
@clear="onClearFontIcon"
|
2021-07-04 01:09:37 +08:00
|
|
|
|
@focus="onIconFocus"
|
|
|
|
|
@blur="onIconBlur"
|
2021-06-29 17:20:40 +08:00
|
|
|
|
>
|
|
|
|
|
<template #prepend>
|
2021-12-04 13:32:14 +08:00
|
|
|
|
<SvgIcon
|
|
|
|
|
:name="fontIconPrefix === '' ? prepend : fontIconPrefix"
|
2021-07-02 21:52:32 +08:00
|
|
|
|
class="font14"
|
2022-02-21 23:52:59 +08:00
|
|
|
|
v-if="fontIconPrefix === '' ? prepend?.indexOf('ele-') > -1 : fontIconPrefix?.indexOf('ele-') > -1"
|
2021-12-04 13:32:14 +08:00
|
|
|
|
/>
|
|
|
|
|
<i v-else :class="fontIconPrefix === '' ? prepend : fontIconPrefix" class="font14"></i>
|
2021-06-29 17:20:40 +08:00
|
|
|
|
</template>
|
|
|
|
|
</el-input>
|
2021-03-15 12:44:58 +08:00
|
|
|
|
</template>
|
2022-04-18 19:14:38 +08:00
|
|
|
|
<template #default>
|
|
|
|
|
<div class="icon-selector-warp">
|
2021-12-04 13:32:14 +08:00
|
|
|
|
<div class="icon-selector-warp-title flex">
|
|
|
|
|
<div class="flex-auto">{{ title }}</div>
|
|
|
|
|
<div class="icon-selector-warp-title-tab" v-if="type === 'all'">
|
|
|
|
|
<span :class="{ 'span-active': fontIconType === 'ali' }" @click="onIconChange('ali')" class="ml10" title="iconfont 图标">ali</span>
|
|
|
|
|
<span :class="{ 'span-active': fontIconType === 'ele' }" @click="onIconChange('ele')" class="ml10" title="elementPlus 图标">ele</span>
|
|
|
|
|
<span :class="{ 'span-active': fontIconType === 'awe' }" @click="onIconChange('awe')" class="ml10" title="fontawesome 图标">awe</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2021-03-15 12:44:58 +08:00
|
|
|
|
<div class="icon-selector-warp-row">
|
2021-12-04 13:32:14 +08:00
|
|
|
|
<el-scrollbar ref="selectorScrollbarRef">
|
2021-07-02 21:52:32 +08:00
|
|
|
|
<el-row :gutter="10" v-if="fontIconSheetsFilterList.length > 0">
|
2021-07-04 01:09:37 +08:00
|
|
|
|
<el-col :xs="6" :sm="4" :md="4" :lg="4" :xl="4" @click="onColClick(v)" v-for="(v, k) in fontIconSheetsFilterList" :key="k">
|
|
|
|
|
<div class="icon-selector-warp-item" :class="{ 'icon-selector-active': fontIconPrefix === v }">
|
2021-06-29 17:20:40 +08:00
|
|
|
|
<div class="flex-margin">
|
|
|
|
|
<div class="icon-selector-warp-item-value">
|
2021-12-04 13:32:14 +08:00
|
|
|
|
<SvgIcon :name="v" />
|
2021-06-29 17:20:40 +08:00
|
|
|
|
</div>
|
2021-03-15 12:44:58 +08:00
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2021-06-29 17:20:40 +08:00
|
|
|
|
</el-col>
|
|
|
|
|
</el-row>
|
|
|
|
|
<el-empty :image-size="100" v-if="fontIconSheetsFilterList.length <= 0" :description="emptyDescription"></el-empty>
|
|
|
|
|
</el-scrollbar>
|
2021-03-15 12:44:58 +08:00
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2022-04-18 19:14:38 +08:00
|
|
|
|
</template>
|
2021-03-15 12:44:58 +08:00
|
|
|
|
</el-popover>
|
|
|
|
|
</div>
|
2021-03-04 21:57:25 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script lang="ts">
|
2022-02-21 23:52:59 +08:00
|
|
|
|
import { ref, toRefs, reactive, onMounted, nextTick, computed, watch, defineComponent } from 'vue';
|
2021-06-19 17:49:42 +08:00
|
|
|
|
import initIconfont from '/@/utils/getStyleSheets';
|
2022-04-18 19:14:38 +08:00
|
|
|
|
|
2022-02-21 23:52:59 +08:00
|
|
|
|
export default defineComponent({
|
2021-03-15 12:44:58 +08:00
|
|
|
|
name: 'iconSelector',
|
2021-07-02 21:52:32 +08:00
|
|
|
|
emits: ['update:modelValue', 'get', 'clear'],
|
2021-03-15 12:44:58 +08:00
|
|
|
|
props: {
|
2021-06-29 17:20:40 +08:00
|
|
|
|
// 输入框前置内容
|
|
|
|
|
prepend: {
|
|
|
|
|
type: String,
|
2022-02-21 23:52:59 +08:00
|
|
|
|
default: () => 'ele-Pointer',
|
2021-06-29 17:20:40 +08:00
|
|
|
|
},
|
|
|
|
|
// 输入框占位文本
|
|
|
|
|
placeholder: {
|
|
|
|
|
type: String,
|
|
|
|
|
default: () => '请输入内容搜索图标或者选择图标',
|
|
|
|
|
},
|
|
|
|
|
// 输入框占位文本
|
|
|
|
|
size: {
|
|
|
|
|
type: String,
|
2022-02-21 23:52:59 +08:00
|
|
|
|
default: () => 'default',
|
2021-06-29 17:20:40 +08:00
|
|
|
|
},
|
|
|
|
|
// 弹窗标题
|
|
|
|
|
title: {
|
|
|
|
|
type: String,
|
|
|
|
|
default: () => '请选择图标',
|
|
|
|
|
},
|
|
|
|
|
// icon 图标类型
|
|
|
|
|
type: {
|
|
|
|
|
type: String,
|
|
|
|
|
default: () => 'ele',
|
|
|
|
|
},
|
|
|
|
|
// 禁用
|
|
|
|
|
disabled: {
|
2021-03-15 12:44:58 +08:00
|
|
|
|
type: Boolean,
|
|
|
|
|
default: () => false,
|
|
|
|
|
},
|
2021-06-29 17:20:40 +08:00
|
|
|
|
// 是否可清空
|
|
|
|
|
clearable: {
|
|
|
|
|
type: Boolean,
|
|
|
|
|
default: () => true,
|
|
|
|
|
},
|
|
|
|
|
// 自定义空状态描述文字
|
|
|
|
|
emptyDescription: {
|
2021-03-15 12:44:58 +08:00
|
|
|
|
type: String,
|
2021-06-29 17:20:40 +08:00
|
|
|
|
default: () => '无相关图标',
|
2021-03-15 12:44:58 +08:00
|
|
|
|
},
|
2021-07-02 21:52:32 +08:00
|
|
|
|
// 双向绑定值,字段名为固定,改了之后将不生效
|
|
|
|
|
// 参考:https://v3.cn.vuejs.org/guide/migration/v-model.html#%E8%BF%81%E7%A7%BB%E7%AD%96%E7%95%A5
|
|
|
|
|
modelValue: String,
|
2021-03-15 12:44:58 +08:00
|
|
|
|
},
|
|
|
|
|
setup(props, { emit }) {
|
|
|
|
|
const inputWidthRef = ref();
|
2021-12-04 13:32:14 +08:00
|
|
|
|
const selectorScrollbarRef = ref();
|
2022-02-21 23:52:59 +08:00
|
|
|
|
const state = reactive({
|
2021-03-15 12:44:58 +08:00
|
|
|
|
fontIconPrefix: '',
|
|
|
|
|
fontIconWidth: 0,
|
|
|
|
|
fontIconSearch: '',
|
|
|
|
|
fontIconTabsIndex: 0,
|
|
|
|
|
fontIconSheetsList: [],
|
2021-07-04 01:09:37 +08:00
|
|
|
|
fontIconPlaceholder: '',
|
2021-12-04 13:32:14 +08:00
|
|
|
|
fontIconType: 'ali',
|
|
|
|
|
fontIconShow: true,
|
2021-03-15 12:44:58 +08:00
|
|
|
|
});
|
2021-07-04 01:09:37 +08:00
|
|
|
|
// 处理 input 获取焦点时,modelValue 有值时,改变 input 的 placeholder 值
|
|
|
|
|
const onIconFocus = () => {
|
|
|
|
|
if (!props.modelValue) return false;
|
|
|
|
|
state.fontIconSearch = '';
|
|
|
|
|
state.fontIconPlaceholder = props.modelValue;
|
2022-04-26 19:21:48 +08:00
|
|
|
|
initFontIconTypeEcho();
|
2021-07-04 01:09:37 +08:00
|
|
|
|
};
|
|
|
|
|
// 处理 input 失去焦点时,为空将清空 input 值,为点击选中图标时,将取原先值
|
|
|
|
|
const onIconBlur = () => {
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
const icon = state.fontIconSheetsList.filter((icon: string) => icon === state.fontIconSearch);
|
|
|
|
|
if (icon.length <= 0) state.fontIconSearch = '';
|
|
|
|
|
}, 300);
|
2021-07-02 21:52:32 +08:00
|
|
|
|
};
|
|
|
|
|
// 处理 icon 双向绑定数值回显
|
|
|
|
|
const initModeValueEcho = () => {
|
|
|
|
|
if (props.modelValue === '') return false;
|
2022-02-21 23:52:59 +08:00
|
|
|
|
(<string | undefined>state.fontIconPlaceholder) = props.modelValue;
|
|
|
|
|
(<string | undefined>state.fontIconPrefix) = props.modelValue;
|
2021-07-02 21:52:32 +08:00
|
|
|
|
};
|
2022-04-26 19:17:00 +08:00
|
|
|
|
// 处理 icon type 类型为 all 时,类型 ali、ele、awe 回显问题
|
2022-04-26 19:21:48 +08:00
|
|
|
|
const initFontIconTypeEcho = () => {
|
2022-04-26 19:17:00 +08:00
|
|
|
|
if ((<any>props.modelValue)?.indexOf('iconfont') > -1) onIconChange('ali');
|
|
|
|
|
else if ((<any>props.modelValue)?.indexOf('ele-') > -1) onIconChange('ele');
|
|
|
|
|
else if ((<any>props.modelValue)?.indexOf('fa') > -1) onIconChange('awe');
|
|
|
|
|
else onIconChange('ali');
|
|
|
|
|
};
|
2021-07-04 01:09:37 +08:00
|
|
|
|
// 图标搜索及图标数据显示
|
2021-03-15 12:44:58 +08:00
|
|
|
|
const fontIconSheetsFilterList = computed(() => {
|
|
|
|
|
if (!state.fontIconSearch) return state.fontIconSheetsList;
|
|
|
|
|
let search = state.fontIconSearch.trim().toLowerCase();
|
|
|
|
|
return state.fontIconSheetsList.filter((item: any) => {
|
|
|
|
|
if (item.toLowerCase().indexOf(search) !== -1) return item;
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
// 获取 input 的宽度
|
|
|
|
|
const getInputWidth = () => {
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
state.fontIconWidth = inputWidthRef.value.$el.offsetWidth;
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
// 监听页面宽度改变
|
|
|
|
|
const initResize = () => {
|
|
|
|
|
window.addEventListener('resize', () => {
|
|
|
|
|
getInputWidth();
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
// 初始化数据
|
2021-12-04 13:32:14 +08:00
|
|
|
|
const initFontIconData = async (type: string) => {
|
|
|
|
|
state.fontIconSheetsList = [];
|
|
|
|
|
if (type === 'ali') {
|
2021-07-02 21:52:32 +08:00
|
|
|
|
await initIconfont.ali().then((res: any) => {
|
2021-07-03 20:29:26 +08:00
|
|
|
|
// 阿里字体图标使用 `iconfont xxx`
|
2022-02-21 23:52:59 +08:00
|
|
|
|
state.fontIconSheetsList = res.map((i: string) => `iconfont ${i}`);
|
2021-06-29 17:20:40 +08:00
|
|
|
|
});
|
2021-12-04 13:32:14 +08:00
|
|
|
|
} else if (type === 'ele') {
|
2021-07-02 21:52:32 +08:00
|
|
|
|
await initIconfont.ele().then((res: any) => {
|
2021-06-29 17:20:40 +08:00
|
|
|
|
state.fontIconSheetsList = res;
|
|
|
|
|
});
|
2021-12-04 13:32:14 +08:00
|
|
|
|
} else if (type === 'awe') {
|
2021-07-02 21:52:32 +08:00
|
|
|
|
await initIconfont.awe().then((res: any) => {
|
2021-07-03 20:29:26 +08:00
|
|
|
|
// fontawesome字体图标使用 `fa xxx`
|
2022-02-21 23:52:59 +08:00
|
|
|
|
state.fontIconSheetsList = res.map((i: string) => `fa ${i}`);
|
2021-06-29 17:20:40 +08:00
|
|
|
|
});
|
|
|
|
|
}
|
2021-07-04 01:09:37 +08:00
|
|
|
|
// 初始化 input 的 placeholder
|
|
|
|
|
// 参考(单项数据流):https://cn.vuejs.org/v2/guide/components-props.html?#%E5%8D%95%E5%90%91%E6%95%B0%E6%8D%AE%E6%B5%81
|
|
|
|
|
state.fontIconPlaceholder = props.placeholder;
|
|
|
|
|
// 初始化双向绑定回显
|
2021-07-02 21:52:32 +08:00
|
|
|
|
initModeValueEcho();
|
2021-12-04 13:32:14 +08:00
|
|
|
|
// 切换时,滚动条置顶。感兴趣可以使用 keep-alive <component :is="xxx"/> 进行缓存
|
|
|
|
|
selectorScrollbarRef.value.wrap$.scrollTop = 0;
|
|
|
|
|
};
|
|
|
|
|
// 图标点击切换
|
|
|
|
|
const onIconChange = (type: string) => {
|
|
|
|
|
state.fontIconType = type;
|
|
|
|
|
initFontIconData(type);
|
2021-03-15 12:44:58 +08:00
|
|
|
|
};
|
2021-06-29 17:20:40 +08:00
|
|
|
|
// 获取当前点击的 icon 图标
|
2021-07-04 01:09:37 +08:00
|
|
|
|
const onColClick = (v: any) => {
|
|
|
|
|
state.fontIconPlaceholder = v;
|
2021-12-04 13:32:14 +08:00
|
|
|
|
state.fontIconPrefix = v;
|
2021-03-15 12:44:58 +08:00
|
|
|
|
emit('get', state.fontIconPrefix);
|
2021-07-04 01:09:37 +08:00
|
|
|
|
emit('update:modelValue', state.fontIconPrefix);
|
2021-03-15 12:44:58 +08:00
|
|
|
|
};
|
2021-06-29 17:20:40 +08:00
|
|
|
|
// 清空当前点击的 icon 图标
|
2021-03-15 12:44:58 +08:00
|
|
|
|
const onClearFontIcon = () => {
|
|
|
|
|
state.fontIconPrefix = '';
|
2021-06-29 17:20:40 +08:00
|
|
|
|
emit('clear', state.fontIconPrefix);
|
2021-07-04 01:09:37 +08:00
|
|
|
|
emit('update:modelValue', state.fontIconPrefix);
|
2021-03-15 12:44:58 +08:00
|
|
|
|
};
|
|
|
|
|
// 页面加载时
|
|
|
|
|
onMounted(() => {
|
2021-12-04 13:32:14 +08:00
|
|
|
|
// 判断默认进来是什么类型图标,进行 tab 回显
|
2022-04-26 19:21:48 +08:00
|
|
|
|
if (props.type === 'all') initFontIconTypeEcho();
|
2022-04-26 19:17:00 +08:00
|
|
|
|
else onIconChange(props.type);
|
2021-03-15 12:44:58 +08:00
|
|
|
|
initResize();
|
|
|
|
|
getInputWidth();
|
|
|
|
|
});
|
2021-07-03 20:29:26 +08:00
|
|
|
|
// 监听双向绑定 modelValue 的变化
|
|
|
|
|
watch(
|
|
|
|
|
() => props.modelValue,
|
|
|
|
|
() => {
|
|
|
|
|
initModeValueEcho();
|
|
|
|
|
}
|
|
|
|
|
);
|
2021-03-15 12:44:58 +08:00
|
|
|
|
return {
|
|
|
|
|
inputWidthRef,
|
2021-12-04 13:32:14 +08:00
|
|
|
|
selectorScrollbarRef,
|
2021-03-15 12:44:58 +08:00
|
|
|
|
fontIconSheetsFilterList,
|
|
|
|
|
onColClick,
|
2021-12-04 13:32:14 +08:00
|
|
|
|
onIconChange,
|
2021-03-15 12:44:58 +08:00
|
|
|
|
onClearFontIcon,
|
2021-07-04 01:09:37 +08:00
|
|
|
|
onIconFocus,
|
|
|
|
|
onIconBlur,
|
2021-03-15 12:44:58 +08:00
|
|
|
|
...toRefs(state),
|
|
|
|
|
};
|
|
|
|
|
},
|
2022-02-21 23:52:59 +08:00
|
|
|
|
});
|
2021-03-15 12:44:58 +08:00
|
|
|
|
</script>
|