本质上是实现了一个eleUI select组件中的创建条目功能的组件,仅仅是将dropdown的选择框变成了label形式。支持eleUI的form表单校验,同时组件也提供了组件内自定义校验的方法。常规的用eleUI校验表单只需要在rules中正常定义:

  

rules: FormRules = {
labels: [
{ required: true, type: 'array', message: '请选择标签', trigger: 'change' },
{ required: true, type: 'array', min: 3, max: 10, message: '至少3个标签,最多添加10个标签', trigger: 'change' },
],
};

  eleUI表单校验的触发方式是组件内抛出一个emit事件(有change和on两种),在ElFormItem组件内会on监听这个事件,调用validate方法执行async-validator提供的校验。而form组件提供的this.$refs[formName].validate是通过遍历formItem调用每个ElFormItem组件内的validate方法。

  组件默认用了size=small时的大小。

<template>
<div>
<div
class="input-content el-input--small"
@click.stop="onFocus">
<div ref="inputLabels" class="input-labels el-select__tags">
<!-- disable-transitions 必须禁用动画效果 否则在计算高度时延迟动画时长触发 -->
<el-tag
v-for="(tag, i) in value"
:key="tag"
class="tag"
size="mini"
closable
type="info"
disable-transitions
@close="onCloseTag(i)">
{{ tag }}
</el-tag> <!-- 输入用 -->
<input
ref="input"
type="text"
class="input-label-show el-select__input"
v-model.trim="input"
@focus="onFocus"
@blur="isFocus = false"
@click.stop
@keydown.enter.prevent="onKeydownEnter" />
<div
v-if="max != 0"
class="limit-content">
{{ value.length }}/{{ max }}
</div>
</div> <!-- 展示用 -->
<input
type="text"
class="el-input__inner"
:class="[
{ 'is-focus': isFocus },
{ 'is-error': isError },
{ 'is-success': isSuccess },
]"
:style="{ 'height': `${height}px` }"
:placeholder="currentPlaceholder" />
<!-- <div v-show="isError" class="el-form-item__error">{{ validateMessage }}</div> -->
</div> <ul class="quick-selected-labels">
<li
v-for="tag in labelsToBeSelected"
:key="tag"
class="quick-label"
@click="onClickAddLabel(tag)">
<i class="quick-selected-icon el-icon-plus"></i>
{{ tag }}
</li>
</ul>
</div>
</template> <script lang="ts">
import { Component, Prop, Vue, Emit, Watch } from 'vue-property-decorator';
import axios from '@/api/index'; @Component
export default class Labels extends Vue {
@Prop({ type: Array, default: (): any => [] }) value: string[];
@Prop({ type: Number, default: 0 }) min: number;
@Prop({ type: Number, default: 0 }) max: number;
@Prop(String) fieldId: string; // 领域
@Prop() initValue: any; input: string = '';
currentPlaceholder: string = '回车添加标签 (最多添加10个)';
isFocus: boolean = false;
height: number = 32; // 展示用input标签的高度
quickSelectedLabels: string[] = []; // 快速添加提供的标签
isError: boolean = false;
isSuccess: boolean = false;
validateMessage: string = ''; // 校验不通过提示信息 $refs: {
input: HTMLInputElement,
inputLabels: HTMLElement,
} @Watch('value', { immediate: true, deep: true })
onWatchValue(val: string[]) {
if (val.length > 0 || this.input !== '') {
this.currentPlaceholder = '';
this.validate();
} else {
this.currentPlaceholder = '回车添加标签 (最多添加10个)';
}
this.resetInputHeight();
} @Watch('input')
onWatchInput(val: string) {
if (this.value.length > 0 || this.input !== '') {
this.currentPlaceholder = '';
} else {
this.currentPlaceholder = '回车添加标签 (最多添加10个)';
}
} @Watch('fieldId', { immediate: true })
onWatchField(val: string, oldVal: string) {
if (val === '' ||val === oldVal) return;
this.getQuickSelectedLabels(val);
this.$emit('input', []);
} created() {
// this.getQuickSelectedLabels();
} onFocus() {
this.isFocus = true;
this.$refs.input.focus();
} /* 查询快速添加提供的标签 */
getQuickSelectedLabels(fieldId: string = '') {
this.quickSelectedLabels = ['接口查询出的标签或者默认的标签'];
} /* 输入标签 */
onKeydownEnter(e: any) {
const val = this.input;
if (val === '') {
this.$message.warning('请勿输入空标签');
return;
}
const labels = [...this.value];
if (labels.includes(val)) {
this.$message.warning('重复的标签');
return;
}
this.input = '';
labels.push(val);
this.$emit('input', labels);
} /* 删除标签 */
@Emit('input')
onCloseTag(i: number) {
let labels = [...this.value];
labels.splice(i, 1);
return labels;
} /* 添加标签 */
@Emit('input')
onClickAddLabel(label: string) {
const labels = [...this.value];
labels.push(label);
return labels;
} /* 计算快速选择的标签是否展示 */
get labelsToBeSelected() {
const tags: string[] = [];
this.quickSelectedLabels.forEach((tag) => {
if (!this.value.includes(tag)) {
tags.push(tag);
}
});
return tags;
} /* 重置展示用input的高度 */
resetInputHeight() {
this.$nextTick(() => {
const initHeight = 32;
const dom = this.$refs.inputLabels;
this.height = this.value.length === 0
? initHeight
: Math.max(
dom ? (dom.clientHeight + (dom.clientHeight > initHeight ? 4 : 0)) : 0,
initHeight
);
});
} /* elementUI 的 dispatch */
dispatch(componentName: string, eventName: string, params: any[]) {
let parent: any = this.$parent || this.$root;
const options: any = parent.$options;
let name = options.componentName; while (parent && (!name || name !== componentName)) {
parent = parent.$parent; if (parent) {
name = parent.$options.componentName;
}
}
if (parent) {
parent.$emit.apply(parent, [eventName].concat(params));
}
} /* 检验 */
// @Emit('validate')
validate() {
this.dispatch('ElFormItem', 'el.form.change', [this.value]);
// const length = this.value.length;
// const min = this.min;
// const max = this.max;
// if (length === 0) {
// this.validateMessage = '请选择标签';
// this.isError = true;
// this.isSuccess = false;
// return false;
// } else if (min !== 0 && length < min) {
// this.validateMessage = `标签数量至少${min}个`;
// this.isError = true;
// this.isSuccess = false;
// return false;
// } else if (max !== 0 && length > max) {
// this.validateMessage = `标签数量至多${max}个`;
// this.isError = true;
// this.isSuccess = false;
// return false;
// }
// this.isError = false;
// this.isSuccess = true;
// return true;
}
}
</script> <style>
.el-form-item.is-error .input-content .el-input__inner {
border-color: #f56c6c !important;
}
</style> <style lang="css" scoped>
.input-content {
position: relative;
margin-bottom: 14px;
} .input-content:hover .el-input__inner {
border-color: #c0c4cc;
} .input-content:hover .is-focus {
border-color: #409eff;
} .input-labels {
padding-right: 45px;
width: 100%;
box-sizing: border-box;
} .input-label-show {
flex-grow: 1;
} .input-info {
font-size: 14px;
color: #bbb;
line-height: 14px;
} .input-content .is-focus {
border-color: #409eff;
} .input-content .is-error {
border-color: #f56c6c !important;
} .is-success {
border-color: #67c23a;
} .tag {
overflow: hidden;
position: relative;
margin-left: 4px;
margin-right: 0;
padding-right: 14px;
max-width: 146px;
min-width: 50px;
font-size: 12px;
color: #7e7e7e;
text-overflow: ellipsis;
white-space: nowrap;
background: rgba(239, 239, 239, .4);
border-radius: 2px;
box-sizing: border-box;
} .tag:last-child {
margin-right: 0;
} .quick-selected-labels {
overflow: hidden;
} .quick-label {
float: left;
overflow: hidden;
position: relative;
margin: 0 10px 10px 0;
padding: 8px 10px 8px 30px;
max-width: 146px;
min-width: 88px;
height: 28px;
font-size: 12px;
color: #7e7e7e;
line-height: 11px;
text-overflow: ellipsis;
white-space: nowrap;
border: 1px solid #e9e9e9;
border-radius: 2px;
background-color: #fff;
cursor: pointer;
box-sizing: border-box;
} .quick-label:hover {
background-color: rgba(144, 147, 153, .1);
} .quick-selected-icon {
position: absolute;
top: 8px;
left: 10px;
width: 12px;
height: 12px;
font-weight: 700;
color: #bbb;
} .limit-content {
position: absolute;
top: 8px;
right: 10px;
font-family: PingFangSC-Regular;
font-size: 14px;
color: #bbb;
text-align: right;
line-height: 14px;
}
</style> <style>
.tag .el-tag__close {
position: absolute;
top: 2px;
right: 0;
font-size: 14px;
}
</style>

实现一个兼容eleUI form表单的多选组件的更多相关文章

  1. asp.net使用post方式action到另一个页面,在另一个页面接受form表单的值!(报错,已解决!)

    原文:asp.net使用post方式action到另一个页面,在另一个页面接受form表单的值!(报错,已解决!) 我想用post的方式把一个页面表单的值,传到另一个页面.当我点击Default.as ...

  2. 仿 ELEMENTUI 实现一个简单的 Form 表单

    原文:仿 ElmentUI 实现一个 Form 表单 一.目标 ElementUI 中 Form 组件主要有以下 功能 / 模块: Form FormItem Input 表单验证 在这套组件中,有 ...

  3. form表单获取多选的值

    flask 中 form 表单直接获取多选框的值时 language = request.values.getlist('values')或 language=request.from.getlist ...

  4. Form表单之复选框checkbox操作

    input复选(checkbox): <label>input复选1组:</label> <input type="checkbox" name=&q ...

  5. 写一个简单的form表单,当光标离开表单的时候表单的值发送给后台

    <body> <form action="index.php"> <input type="text" name="tx ...

  6. 及时从数据库中取得数据填放进Form表单的多选框中

    #写上以下代码就不用担心数据库添加了数据而不能及时获取了 def __init__(self, *args, **kwargs): #每次创建Form1对象时执行init方法 super(Form1, ...

  7. 直接提交一个form表单后台返回一个新页面通过target属性可以放到iframe当中

    问题描述: 我想提交一个form表单后台直接返回一个新页面,但是当前页面还不想被替换掉: 解决方案: 在页面中添加一个iframe将form表单的target属性设置为iframe的id这样的话返回的 ...

  8. form表单那点事儿(下) 进阶篇

    form表单那点事儿(下) 进阶篇 上一篇主要温习了一下form表单的属性和表单元素,这一片主要讲解用JavaScript如何操作form. 目录: 表单操作 取值 赋值 重置 校验 提交 技巧 不提 ...

  9. Unit 2.前端之html--table(表格),form(表单)标签

    一.table标签 作用:定义html表格.一个table标签元素至少包含 thead(表头),tbody(表主题),还可以有tfoot(表底部) html表格游table元素及一个或者多个tr,th ...

随机推荐

  1. 【开发工具】- Xshell工具的下载和安装

    下载地址:https://www.netsarang.com/zh/free-for-home-school/ Xshell 是一个强大的安全终端模拟软件,它支持SSH1, SSH2, 以及Micro ...

  2. SASS系列之:!global VS !deafult

    先脑补两组场景. 场景一: 同事们每天中午都会外出吃饭.通常情况下都会先问,去哪儿吃啊?不知道啊?下楼再说吧.到了楼下好不容易有个人站出来说,既然没人说我可就说了啊,咱们去吃香草香草吧.没人反对就去, ...

  3. 从零开始学虚拟DOM

    此文主要翻译自:Building a Simple Virtual DOM from Scratch,看原文的同学请直达! 此文是作者在一次现场编程演讲时现场所做的,有关演讲的相关资料我们也可以在原英 ...

  4. Java经典逻辑编程50题 (转)

    注: 转自https://blog.csdn.net/Alias_fa/article/details/52985112  (摘了题目部分和部分分析,想看原作代码的见原作链接) [程序1] 題目:古典 ...

  5. 0001-代码仓库-git 命令

    参考 https://www.cnblogs.com/NTWang/p/6213408.html https://www.cnblogs.com/Sungeek/p/6905102.html

  6. Redhat下Oracle 12c单节点安装

    操作系统:Redhat6.7 64位[root@Oracle12CDB ~]# more /etc/redhat-release Red Hat Enterprise Linux Server rel ...

  7. Linux中通过ssh将客户端与服务端的远程连接

    前提需要:1.在VMware中装上两台linux虚拟机,本博客使用的都是CentOS 7.2.两部虚拟机可以通过命令ping通.3.两部虚拟机中已经通过yum本地仓库安装了sshd服务. 首先  1. ...

  8. K3 Cloud的数据中心加载异常处理

    以前一直是财务维护的K3  Cloud突然说不能登录,用的SQL 2008的数据库,运维也搞不定,找帮忙,因为是部署在阿里云上,上去看看数据库,这个K3数据库占了600多G,想看看这个表结构,就是打不 ...

  9. docker 运行ELK

    docker network create somenetwork docker run -d --name elasticsearch --net somenetwork -p 9200:9200 ...

  10. raw镜像与qcw2镜像互相转换

    qcow2是最小使用,raw是置零使用 . raw格式是原始镜像,会直接当作一个块设备给虚拟机来使用,至于文件里面的空洞,则是由宿主机的文件系统来管理的,linux下的文件系统可以很好的支持空洞的特性 ...