动态标签组件
Ant Design of Vue现在已经支持vue3,虽然动态添加删除标签组件,在Ant Design of Vue2.0.0官网的文档,标签Tag中是有的,但是不是使用vue3的composition API实现的。而本文的目标就是通过vue3的composition API 实现封装动态添加删除标签组件。
一、安装Ant Design of Vue 2.0
首先新建vue3.0的步骤就不赘述,网上有很多相关文章,直接开始安装依赖,安装好后请注意引入项目,具体可参考Ant Design of Vue 2.0官方文档。
//默认安装命令 v2可安不上
npm install ant-design-vue --save
//检查版本如果不是v2 可执行升级
npm i --save ant-design-vue@next
//main.ts
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
//ant-design-vue 组件库
import Antd from 'ant-design-vue';
import 'ant-design-vue/dist/antd.css';
const app = createApp(App);
app.use(router);
app.use(Antd);
app.mount('#app');
二、新建文件夹和文件
在components新建DynamicTags目录
//路径:src/components/DynamicTags
在DynamicTags新建js目录
//路径:src/components/DynamicTags/js
在DynamicTags下js目录新建index.ts
//路径:src/components/DynamicTags/js/index.ts
在DynamicTags新建index.vue文件
//路径:src/components/DynamicTags/index.vue
DynamicTags下的index.vue基础结构
//src/components/DynamicTags/index.vue
<template>
<a-row>
</a-row>
</template>
<script lang="ts">
import {defineComponent} from 'vue'
export default defineComponent({
components:{},
props:{},
emits: [],
setup(){
return {
}
}
})
</script>
<style scoped>
</style>
新建src/view/DynamicTagsPage.vue
//src/vie/DynamicTagsPage.vue
<template>
<div>
<DynamicTags/>
</div>
</template>
<script>
import DynamicTags from '@/components/DynamicTags/index.vue'
export default {
components:{
DynamicTags,
}
}
</script>
<style scoped>
</style>
三、功能实现
(1)实现tag遍历显示
<template>
<a-row type="flex" align="middle">
<a-tag
v-for="(item, index) in tags"
closable
color="blue"
:key="index"
>
{{ item }}
</a-tag>
</a-row>
</template>
<script lang="ts">
import {defineComponent,ref} from 'vue'
export default defineComponent({
components:{},
props:{},
emits: [],
setup(){
//标签数据
const tags = ref<string[]>(['111','222']);
return {
tags
}
}
})
</script>
<style scoped>
</style>
(2)实现添加按钮和输入框显示
<template>
<a-row type="flex" align="middle">
<a-tag
v-for="(item, index) in tags"
closable
color="blue"
:key="index"
>
{{ item }}
</a-tag>
<a-input
type="text"
size="small"
v-if="inputVisible"
:style="{ width: '110px' }"
/>
<a-tag v-else-if="btnVisible">
<plus-outlined/> 添加标签
</a-tag>
</a-row>
</template>
<script lang="ts">
import {defineComponent,ref} from 'vue'
import {PlusOutlined} from '@ant-design/icons-vue';
export default defineComponent({
components:{
PlusOutlined
},
props:{},
emits: [],
setup(){
//标签数据
const tags = ref<string[]>(['111','222']);
//按钮显隐
const btnVisible = ref(true);
//输入框显隐
const inputVisible = ref(false);
return {
tags,
btnVisible,
inputVisible
}
}
})
</script>
<style scoped>
</style>
(3)实现点击添加按钮显示输入框
<template>
<a-row type="flex" align="middle">
<a-tag
v-for="(item, index) in tags"
closable
color="blue"
:key="index"
>
{{ item }}
</a-tag>
<a-input
ref="inputDom"
type="text"
size="small"
v-if="inputVisible"
:style="{ width: '110px' }"
/>
<a-tag v-else-if="btnVisible" @click="showInput">
<plus-outlined/> 添加标签
</a-tag>
</a-row>
</template>
<script lang="ts">
import {defineComponent,ref,nextTick} from 'vue'
import {PlusOutlined} from '@ant-design/icons-vue';
export default defineComponent({
components:{
PlusOutlined
},
props:{},
emits: [],
setup(){
//输入框Dom
const inputDom: any = ref(null);
//标签数据
const tags = ref<string[]>(['111','222']);
//按钮显隐
const btnVisible = ref(true);
//输入框显隐
const inputVisible = ref(false);
//显示输入框
const showInput = () => {
inputVisible.value = true;
btnVisible.value = true;
nextTick(() => {
inputDom.value.focus();
})
};
return {
inputDom,
tags,
btnVisible,
inputVisible,
showInput
}
}
})
</script>
<style scoped>
</style>
(4)实现输入文字新增标签
<template>
<a-row type="flex" align="middle">
<a-tag
v-for="(item, index) in tags"
closable
color="blue"
:key="index"
>
{{ item }}
</a-tag>
<a-input
ref="inputDom"
type="text"
size="small"
v-if="inputVisible"
v-model:value="inputValue"
:style="{ width: '110px' }"
@blur="handleInputConfirm"
@keyup.enter="handleInputConfirm"
/>
<a-tag v-else-if="btnVisible" @click="showInput">
<plus-outlined/> 添加标签
</a-tag>
</a-row>
</template>
<script lang="ts">
import {defineComponent,ref,nextTick} from 'vue'
import {PlusOutlined} from '@ant-design/icons-vue';
export default defineComponent({
components:{
PlusOutlined
},
props:{},
emits: [],
setup(){
//输入框Dom
const inputDom: any = ref(null);
//标签数据
const tags = ref<string[]>(['111','222']);
//按钮显隐
const btnVisible = ref(true);
//输入框显隐
const inputVisible = ref(false);
//当前输入框的值
const inputValue = ref('');
//显示输入框
const showInput = () => {
inputVisible.value = true;
btnVisible.value = true;
nextTick(() => {
inputDom.value.focus();
})
};
//完成输入
const handleInputConfirm = () => {
if (inputValue.value.trim()) {
tags.value.push(inputValue.value);
}
inputVisible.value = false;
inputValue.value = '';
};
return {
inputDom,
tags,
btnVisible,
inputVisible,
inputValue,
showInput,
handleInputConfirm
}
}
})
</script>
<style scoped>
</style>
(5)实现点击删除当前标签
<template>
<a-row type="flex" align="middle">
<a-tag
v-for="(item, index) in tags"
closable
color="blue"
:visible="true"
:key="index"
@close="handleClose(index)"
>
{{ item }}
</a-tag>
<a-input
ref="inputDom"
type="text"
size="small"
v-if="inputVisible"
v-model:value="inputValue"
:style="{ width: '110px' }"
@blur="handleInputConfirm"
@keyup.enter="handleInputConfirm"
/>
<a-tag v-else-if="btnVisible" @click="showInput">
<plus-outlined/> 添加标签
</a-tag>
</a-row>
</template>
<script lang="ts">
import {defineComponent,ref,nextTick} from 'vue'
import {PlusOutlined} from '@ant-design/icons-vue';
export default defineComponent({
components:{
PlusOutlined
},
props:{},
emits: [],
setup(){
//输入框Dom
const inputDom: any = ref(null);
//标签数据
const tags = ref<string[]>(['111','222']);
//按钮显隐
const btnVisible = ref(true);
//输入框显隐
const inputVisible = ref(false);
//当前输入框的值
const inputValue = ref('');
//删除某一个
const handleClose = (index: number) => {
tags.value.splice(index, 1);
btnVisible.value = true;
};
//显示输入框
const showInput = () => {
inputVisible.value = true;
btnVisible.value = true;
nextTick(() => {
inputDom.value.focus();
})
};
//完成输入
const handleInputConfirm = () => {
if (inputValue.value.trim()) {
tags.value.push(inputValue.value);
}
inputVisible.value = false;
inputValue.value = '';
};
return {
inputDom,
tags,
btnVisible,
inputVisible,
inputValue,
handleClose,
showInput,
handleInputConfirm,
}
}
})
</script>
<style scoped>
</style>
(6)实现监听tags数据变化通过change事件返回给父组件
//src/vie/DynamicTagsPage.vue
<template>
<div>
<DynamicTags @change="tagsChange"/>
</div>
</template>
<script>
import DynamicTags from '@/components/DynamicTags/index.vue'
export default {
components:{
DynamicTags,
},
setup(){
const tagsChange = e=>{
console.log(e)
};
return{
tagsChange
}
}
}
</script>
<style scoped>
</style>
<template>
<a-row type="flex" align="middle">
<a-tag
v-for="(item, index) in tags"
closable
color="blue"
:visible="true"
:key="index"
@close="handleClose(index)"
>
{{ item }}
</a-tag>
<a-input
ref="inputDom"
type="text"
size="small"
v-if="inputVisible"
v-model:value="inputValue"
:style="{ width: '110px' }"
@blur="handleInputConfirm"
@keyup.enter="handleInputConfirm"
/>
<a-tag v-else-if="btnVisible" @click="showInput">
<plus-outlined/> 添加标签
</a-tag>
</a-row>
</template>
<script lang="ts">
import {defineComponent,ref,toRaw,nextTick,watch} from 'vue'
import {PlusOutlined} from '@ant-design/icons-vue';
export default defineComponent({
components:{
PlusOutlined
},
props:{},
emits: ["change"],
setup(props,context){
//输入框Dom
const inputDom: any = ref(null);
//标签数据
const tags = ref<string[]>(['111','222']);
//按钮显隐
const btnVisible = ref(true);
//输入框显隐
const inputVisible = ref(false);
//当前输入框的值
const inputValue = ref('');
//删除某一个
const handleClose = (index: number) => {
tags.value.splice(index, 1);
btnVisible.value = true;
};
//显示输入框
const showInput = () => {
inputVisible.value = true;
btnVisible.value = true;
nextTick(() => {
inputDom.value.focus();
})
};
//完成输入
const handleInputConfirm = () => {
if (inputValue.value.trim()) {
tags.value.push(inputValue.value);
}
inputVisible.value = false;
inputValue.value = '';
};
watch(tags.value, (newValue, oldValue) => {
context.emit("change", toRaw(tags.value));
});
return {
inputDom,
tags,
btnVisible,
inputVisible,
inputValue,
handleClose,
showInput,
handleInputConfirm,
}
}
})
</script>
<style scoped>
</style>
(7)增加组件props参数,方便在不同场景使用
//src/view/DynamicTagsPage.vue
<template>
<div>
<DynamicTags
:maxLength="3"
:inputMaxLenght="6"
@change="tagsChange"
/>
</div>
</template>
<script>
import DynamicTags from '@/components/DynamicTags/index.vue'
export default {
components:{
DynamicTags,
},
setup(){
const tagsChange = e=>{
console.log(e)
};
return{
tagsChange
}
}
}
</script>
<style scoped>
</style>
<template>
<a-row type="flex" align="middle">
<a-tag
v-for="(item, index) in tags"
closable
color="blue"
:visible="true"
:key="index"
@close="handleClose(index)"
>
{{ item }}
</a-tag>
<a-input
ref="inputDom"
type="text"
size="small"
v-if="inputVisible"
v-model:value="inputValue"
:style="{ width: '110px' }"
@blur="handleInputConfirm"
@keyup.enter="handleInputConfirm"
/>
<a-tag v-else-if="btnVisible" @click="showInput">
<plus-outlined/> 添加标签
</a-tag>
</a-row>
</template>
<script lang="ts">
import {defineComponent,ref,toRaw,nextTick,watch} from 'vue'
import {PlusOutlined} from '@ant-design/icons-vue';
export default defineComponent({
components:{
PlusOutlined
},
props:{
maxLength: Number,
inputMaxLenght: Number
},
emits: ["change"],
setup(props: any,context: any){
//输入框Dom
const inputDom: any = ref(null);
//标签数据
const tags = ref<string[]>([]);
//按钮显隐
const btnVisible = ref(true);
//输入框显隐
const inputVisible = ref(false);
//当前输入框的值
const inputValue = ref('');
//输入框最大长度
const itemMaxLenght = ref(props.inputMaxLenght);
//删除某一个
const handleClose = (index: number) => {
tags.value.splice(index, 1);
if (tags.value.length + 1 > props.maxLength) {
btnVisible.value = false;
}
btnVisible.value = true;
};
//显示输入框
const showInput = () => {
if (tags.value.length + 1 > props.maxLength) {
inputVisible.value = false;
btnVisible.value = false;
return false;
}
inputVisible.value = true;
btnVisible.value = true;
nextTick(() => {
inputDom.value.focus();
})
};
//完成输入
const handleInputConfirm = () => {
if (inputValue.value.trim()) {
tags.value.push(inputValue.value);
}
if (tags.value.length + 1 > props.maxLength) {
btnVisible.value = false;
}
inputVisible.value = false;
inputValue.value = '';
};
watch(tags.value, (newValue, oldValue) => {
context.emit("change",toRaw(tags.value));
});
return {
inputDom,//输入框Dom
tags,
inputVisible,
inputValue,
itemMaxLenght,
btnVisible,
handleClose,
showInput,
handleInputConfirm,
}
}
})
</script>
<style scoped>
</style>
四、完整代码
DynamicTags/index.vue
<template>
<a-row type="flex" align="middle" class="addTags">
<a-tag
:key="index"
v-for="(item, index) in tags"
closable
color="blue"
:visible="true"
@close="handleClose(index)">
{{ item }}
</a-tag>
<a-input
ref="inputDom"
type="text"
size="small"
v-if="inputVisible"
v-model:value="inputValue"
:maxlength="itemMaxLenght"
:style="{ width: '110px' }"
@blur="handleInputConfirm"
@keyup.enter="handleInputConfirm"
/>
<a-tag v-else-if="btnVisible" @click="showInput">
<plus-outlined /> 添加标签
</a-tag>
</a-row>
</template>
<script lang="ts">
import {defineComponent,ref,toRaw,nextTick,watch} from 'vue'
import {PlusOutlined} from '@ant-design/icons-vue';
export default defineComponent({
components:{
PlusOutlined
},
props:{
maxLength: Number,
inputMaxLenght: Number
},
emits: ["change"],
setup(props: any,context: any){
//输入框Dom
const inputDom: any = ref(null);
//标签数据
const tags = ref<string[]>([]);
//按钮显隐
const btnVisible = ref(true);
//输入框显隐
const inputVisible = ref(false);
//当前输入框的值
const inputValue = ref('');
//输入框最大长度
const itemMaxLenght = ref(props.inputMaxLenght);
//删除某一个
const handleClose = (index: number) => {
tags.value.splice(index, 1);
if (tags.value.length + 1 > props.maxLength) {
btnVisible.value = false;
}
btnVisible.value = true;
};
//显示输入框
const showInput = () => {
if (tags.value.length + 1 > props.maxLength) {
inputVisible.value = false;
btnVisible.value = false;
return false;
}
inputVisible.value = true;
btnVisible.value = true;
nextTick(() => {
inputDom.value.focus();
})
};
//完成输入
const handleInputConfirm = () => {
if (inputValue.value.trim()) {
tags.value.push(inputValue.value);
}
if (tags.value.length + 1 > props.maxLength) {
btnVisible.value = false;
}
inputVisible.value = false;
inputValue.value = '';
};
watch(tags.value, (newValue, oldValue) => {
context.emit("change", toRaw(tags.value));
});
return {
inputDom,//输入框Dom
tags,
inputVisible,
inputValue,
itemMaxLenght,
btnVisible,
handleClose,
showInput,
handleInputConfirm,
}
}
})
</script>
<style scoped>
</style>
五、抽离逻辑代码到单独文件
DynamicTags/js/index.ts
import {ref,toRaw,nextTick,watch} from 'vue'
export default function dynamicTags(props: any,context: any) {
//输入框Dom
const inputDom: any = ref(null);
//标签数据
const tags = ref<string[]>([]);
//按钮显隐
const btnVisible = ref(true);
//输入框显隐
const inputVisible = ref(false);
//当前输入框的值
const inputValue = ref('');
//输入框最大长度
const itemMaxLenght = ref(props.inputMaxLenght);
//删除某一个
const handleClose = (index: number) => {
tags.value.splice(index, 1);
if (tags.value.length + 1 > props.maxLength) {
btnVisible.value = false;
}
btnVisible.value = true;
};
//显示输入框
const showInput = () => {
if (tags.value.length + 1 > props.maxLength) {
inputVisible.value = false;
btnVisible.value = false;
return false;
}
inputVisible.value = true;
btnVisible.value = true;
nextTick(() => {
inputDom.value.focus();
})
};
//完成输入
const handleInputConfirm = () => {
if (inputValue.value.trim()) {
tags.value.push(inputValue.value);
}
if (tags.value.length + 1 > props.maxLength) {
btnVisible.value = false;
}
inputVisible.value = false;
inputValue.value = '';
};
watch(tags.value, (newValue, oldValue) => {
context.emit("change", toRaw(tags.value));
});
return {
inputDom,//输入框Dom
tags,
inputVisible,
inputValue,
itemMaxLenght,
btnVisible,
handleClose,
showInput,
handleInputConfirm,
}
}
DynamicTags/index.vue
<template>
<a-row type="flex" align="middle" class="addTags">
<a-tag
:key="index"
v-for="(item, index) in tags"
closable color="blue"
:visible="true"
@close="handleClose(index)"
>
{{ item }}
</a-tag>
<a-input
ref="inputDom"
type="text"
size="small"
v-if="inputVisible"
v-model:value="inputValue"
:maxlength="itemMaxLenght"
:style="{ width: '110px' }"
@blur="handleInputConfirm"
@keyup.enter="handleInputConfirm"
/>
<a-tag v-else-if="btnVisible" @click="showInput">
<plus-outlined /> 添加标签
</a-tag>
</a-row>
</template>
<script lang="ts">
import {defineComponent} from 'vue';
import {PlusOutlined} from '@ant-design/icons-vue';
import dynamicTags from './js/index';
export default defineComponent({
components:{
PlusOutlined
},
props:{
maxLength: Number,
inputMaxLenght: Number
},
emits: ["change"],
setup(props,context){
const {
inputDom,
tags,
inputVisible,
inputValue,
itemMaxLenght,
btnVisible,
handleClose,
showInput,
handleInputConfirm,
} = dynamicTags(props,context);
return {
inputDom,
tags,
inputVisible,
inputValue,
itemMaxLenght,
btnVisible,
handleClose,
showInput,
handleInputConfirm,
}
}
})
</script>
<style scoped>
</style>
本文演示视频:点击浏览
项目源代码:点击浏览
更多前端内容欢迎关注公众号:天小天个人网