点击 input 框显示弹窗,关闭弹窗给 input 赋值并进行必填校验
点击输入框(input)时显示一个弹窗,当用户关闭弹窗时,将弹窗中的输入值赋给对应的输入框,并对输入内容进行必填项校验
背景
在现代Web应用开发中,实现用户友好的输入交互是提升用户体验的关键之一。例如,在表单设计中,通过点击输入框触发弹窗来辅助用户输入,并在关闭弹窗时自动填充输入框并进行必要的校验,可以显著提高表单填写的便捷性和准确性。
功能描述
点击输入框(input)时显示一个弹窗,当用户关闭弹窗时,将弹窗中的输入值赋给对应的输入框,并对输入内容进行必填项校验。
实现思路
- 绑定事件:为
input
元素绑定focus
事件。 - 显示弹窗:点击事件触发后显示弹窗。
- 关闭弹窗:使用
emit
传递参数并关闭弹窗。 - 赋值与校验:获取传递的参数,赋值给input元素,最后表单提交时校验必填。
代码示例
安装 element-ui
npm install element-ui
# 或者
yarn add element-ui
main.js 引入 element-ui
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import "element-ui/lib/theme-chalk/icon.css";
import Element from "element-ui";
Vue.use(Element);
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
显示弹窗
使用 Element-UI 的 dialog
组件, 给 input
输入框绑定 focus
事件, 当 input
输入框获得焦点时调用 selectUser
组件里定义的 show
方法, 显示 selectUser
组件
// index.vue
<template>
<div>
<el-form :model="ruleForm" ref="ruleForm" label-position="left" label-width="86px">
<el-row>
<el-col>
<el-form-item label="项目成员" prop="oneName">
<el-input size="medium" v-model="ruleForm.oneName" clearable @focus="showDialog"
placeholder="请选择"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<SDialog ref="SDialog"></SDialog>
</div>
</template>
<script>
import SDialog from '@/views/popupAndSelect/SDialog.vue';
export default {
name: 'show',
data() {
return {
ruleForm: {
oneName: "",
}
}
},
components: { SDialog },
methods: {
showDialog() {
this.$refs.SDialog.show()
},
}
}
</script>
selectUser.vue 文件
// selectUser.vue
<template>
<div>
<el-dialog title="请选择" :visible.sync="isShow" width="600px" :before-close="handleClose" center :show-close="false"
:close-on-click-modal="false" append-to-body :close-on-press-escape="false" class="dialog_class">
<h2>dialog</h2>
<span slot="footer" class="dialog-footer">
<el-button class="btn submit" type="primary" @click="ok">确 定</el-button>
<el-button class="btn cancel" @click="cancel">取 消</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
export default {
name: "SDialog",
data() {
return {
isShow: false,
}
},
methods: {
show() {
this.isShow = true;
},
ok() {
this.cancel();
},
cancel() {
this.isShow = false;
},
handleClose(done) {
done();
},
}
}
</script>
传递参数并关闭弹窗
选择好人员后, selectUser
组件使用 emit
自定义事件 selectUser
将选择的人员数据传递给 index.vue
, index.vue
在 select-user
标签定义事件 @selectUser="selectUser"
接受数据
// selectUser.vue
<template>
<div>
<el-dialog title="请选择项目人员" :visible.sync="isShow" width="1050px" :before-close="handleClose" center :show-close="false"
:close-on-click-modal="false" append-to-body :close-on-press-escape="false" class="dialog_class">
<div class="content">
<el-checkbox-group v-model="userIds" class="checkbox-con" @change="handleCheckedCitiesChange">
<el-checkbox v-for="item in userList" :key="Math.random()" border :label="item.userId">{{ `${item.userName} -
${item.phone}` }}</el-checkbox>
</el-checkbox-group>
</div>
<span slot="footer" class="dialog-footer">
<el-button class="btn submit" type="primary" @click="ok">确 定</el-button>
<el-button class="btn cancel" @click="cancel">取 消</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { getUser } from '@/api/prokect.js';
export default {
name: "SDialog",
data() {
return {
userList: [], // 人员列表
userIds: [], // 选中的用户id
isShow: false,
selectioneds: [], // 选中的项
}
},
methods: {
show() {
this.isShow = true;
this.query();
},
ok() {
this.$emit("selectUser", this.selectioneds);
this.cancel();
},
cancel() {
this.isShow = false;
},
// 查询人员列表
query() {
getUser().then(({ data: { userList } }) => {
this.userList = userList;
})
},
// 多选
handleCheckedCitiesChange(value) {
console.log(value); // value勾选的所有项
this.selectioneds = []; // 先初始化
this.userIds = [];
value && value.forEach(v => {
// 存储用户id
this.userIds.push(v);
this.userList.forEach(w => {
if (v === w.userId) {
// 选中的项
this.selectioneds.push(w);
}
})
});
},
handleClose(done) {
done();
},
}
}
</script>
index.vue
// index.vue
<template>
<div>
<el-form :model="ruleForm" ref="ruleForm" label-position="left" label-width="86px">
<el-row>
<el-col>
<el-form-item label="项目成员" prop="oneName">
<el-input size="medium" v-model="ruleForm.oneName" clearable @focus="showDialog"
placeholder="请选择"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<SDialog ref="SDialog" @selectUser="selectUser"></SDialog>
</div>
</template>
<script>
import SDialog from '@/views/popupAndSelect/SDialog.vue';
export default {
name: 'show',
data() {
return {
ruleForm: {
oneName: "",
}
}
},
components: { SDialog },
methods: {
showDialog() {
this.$refs.SDialog.show()
},
selectUser(data){
console.log('data::: ', data);
}
}
}
</script>
校验表单
Element-UI 表单校验规则:
- 定义表单模型:通常使用
v-model
指令将表单项与 Vue 实例中的数据绑定。 - 定义校验规则:通过
el-form
的:rules
属性定义校验规则,这是一个对象,其键是表单域的名字,值是一个对象或校验规则数组。 - 触发校验:可以通过调用
el-form
的validate
方法来手动触发校验,也可以通过表单控件的trigger
属性来指定自动触发校验的时机。 - 下面是
rule
规则的属性介绍:required
: 布尔值,表示是否必须填写该项,默认为false
。如果设为true
,则该项不能为空。message
: 字符串,当验证失败时显示的消息。trigger
: 字符串或字符串数组,表示在什么情况下触发验证。可以是'blur'
(失焦时触发)、'change'
(值改变时触发)或'submit'
(提交时触发)。也可以同时设置多个触发器。type
: 字符串,验证类型,可以是'string'
,'number'
,'integer'
,'email'
,'url'
,'date'
,'datetime'
,'time'
。默认为'string'
。min
: 数字,对于字符串来说是最小长度,对于数字来说是最小值。max
: 数字,对于字符串来说是最大长度,对于数字来说是最大值。pattern
: 正则表达式对象,用于验证字段值是否符合正则表达式的模式。validator
: 函数,自定义验证方法,接收(rule, value, callback)
作为参数,其中rule
是当前的验证规则对象,value
是当前字段的值,callback
是验证完毕后的回调函数。
注意:
el-form-item
标签里的表单元素的v-model
需注意以下几点:
- 绑定的属性名要与
rules
里定义的规则名字一致,否则不会触发校验。- 一定要是
el-form
的model
绑定的对象里的属性,否则会一直校验不通过。el-form
的model
绑定的对象在data
里定义, 且el-form-item
下的v-model
绑定的属性要在该对象里定义。
校验表单示例
<template>
<div>
<el-form :model="ruleForm" ref="ruleForm" :rules="rules" label-position="left" label-width="86px">
<el-row class="row-from">
<el-col class="text-left col-item">
<el-form-item label="项目成员" prop="oneName">
<el-tooltip class="item" size="medium" effect="dark" :disabled="selectNum == 0" placement="top"
v-model="isShowTooltip">
<div slot="content" class="tooltip-con">
<div v-for="item in selectUserList" :key="item.userId" class="size16 team-text">{{ `${item.userName} -
${item.phone}` }}</div>
<i class="el-icon-close close" @click="closeTooltip"></i>
</div>
<el-badge :value="selectNum" class="item" type="primary">
<el-input size="medium" v-model="ruleForm.oneName" clearable @focus="showDialog"
placeholder="请选择"></el-input>
</el-badge>
</el-tooltip>
</el-form-item>
</el-col>
</el-row>
<el-row class="row-from">
<el-col class="text-left">
<el-button class="btn select" size="medium" @click="submit" type="primary">提交</el-button>
</el-col>
</el-row>
</el-form>
<SDialog ref="SDialog" @selectUser="selectUser"></SDialog>
</div>
</template>
<script>
import SDialog from '@/views/popupAndSelect/SDialog.vue';
export default {
name: 'show',
data() {
const checkOneName = (rule, value, callback) => {
console.log('checkTeam::: ', value);
if (!value) {
callback(new Error('请选择和作社项目组名称'))
} else {
callback()
}
};
return {
isShowTooltip: false,
selectNum: 0,
rules: {
oneName: [
{ required: true, message: '请选择名字', trigger: 'blur,change' },
{ validator: checkOneName, trigger: 'change' }
],
},
ruleForm: {
nameList: [],
userIds: [],
oneName: "",
},
selectUserList: [],
}
},
components: { SDialog },
methods: {
showDialog() {
this.$refs.SDialog.show()
},
selectUser(data) {
console.log('data::: ', data);
// 拿到所有用户的 userName
let nameList = data.map(item => item.userName);
this.ruleForm.oneName = `${data[0].userName} - ${data[0].phone}`;
this.selectNum = nameList.length;
this.ruleForm.nameList = nameList;
// 拿到所有用户的 userId
this.ruleForm.userIds = data.map(item => item.userId);
this.selectUserList = data;
},
// 关闭 Tooltip
closeTooltip() {
this.isShowTooltip = false;
},
// 提交表单
submit() {
this.$refs.ruleForm.validate(valid => {
if (valid) {
this.$message({
message: '提交成功',
type: 'success'
});
} else {
console.log('error submit!!');
return false;
}
});
},
}
}
</script>
完整代码
selectUser.vue
// selectUser.vue
<template>
<div>
<el-dialog title="请选择项目人员" :visible.sync="isShow" width="1050px" :before-close="handleClose" center
:show-close="false" :close-on-click-modal="false" append-to-body :close-on-press-escape="false"
class="dialog_class">
<div class="content">
<el-checkbox-group v-model="userIds" class="checkbox-con" @change="handleCheckedCitiesChange">
<el-checkbox v-for="item in userList" :key="Math.random()" border :label="item.userId">{{ `${item.userName} -
${item.phone}` }}</el-checkbox>
</el-checkbox-group>
</div>
<span slot="footer" class="dialog-footer">
<el-button class="btn submit" type="primary" @click="ok">确 定</el-button>
<el-button class="btn cancel" @click="cancel">取 消</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { getUser } from '@/api/prokect.js';
export default {
name: "selectUser",
data() {
return {
userList: [], // 人员列表
userIds: [], // 选中的用户id
isShow: false,
selectioneds: [], // 选中的项
}
},
methods: {
show({ selectioneds = [] }) {
this.isShow = true;
this.query();
if (selectioneds.length > 0) {
this.userIds = selectioneds.map(item => item.userId);
this.selectioneds = selectioneds;
}
},
// 查询人员列表
query() {
getUser().then(({ data: { userList } }) => {
this.userList = userList;
})
},
// 多选
handleCheckedCitiesChange(value) {
console.log(value); // value勾选的所有项
this.selectioneds = []; // 先初始化
this.userIds = [];
value && value.forEach(v => {
// 存储用户id
this.userIds.push(v);
this.userList.forEach(w => {
if (v === w.userId) {
// 选中的项
this.selectioneds.push(w);
}
})
});
},
handleClose(done) {
done();
},
ok() {
this.$emit("selectUser", this.selectioneds);
this.cancel();
},
cancel() {
this.selectioneds = [];
this.userIds = [];
this.userList = [];
this.isShow = false;
},
}
}
</script>
<style lang="scss" scoped>
::v-deep .text-left .el-form-item .el-form-item__label {
font-size: 15px;
color: #333;
font-weight: 400;
}
.pagination {
float: right;
padding-top: 18px;
}
.radio-con .el-radio,
.checkbox-con .el-checkbox {
margin: 20px 20px 0 10px;
width: 216px;
}
::v-deep .el-dialog {
margin-top: 5vh !important;
padding-bottom: 78px;
max-height: 90vh !important;
min-height: 300px;
overflow: hidden;
}
.content {
overflow-y: scroll;
max-height: 72vh;
min-height: 56px;
&::-webkit-scrollbar {
width: 7px;
height: 7px;
background-color: #fff;
}
&::-webkit-scrollbar-thumb {
border-radius: 5px;
background-color: rgba(144, 146, 152, 0.3);
}
}
::v-deep.el-dialog__footer {
padding: 20px;
width: 100%;
position: absolute;
background-color: #fff;
bottom: 0;
z-index: 200;
}
</style>
index.vue 文件
// index.vue
<template>
<div>
<el-form :model="ruleForm" ref="ruleForm" :rules="rules" label-position="left" label-width="86px" class="ruleForm">
<el-row class="row-from">
<el-col class="text-left col-item">
<el-form-item label="项目名称" prop="projectName">
<el-input size="medium" v-model="ruleForm.projectName" placeholder="请输入项目名称">
</el-input>
</el-form-item>
</el-col>
</el-row>
<el-row class="row-from">
<el-col class="text-left col-item">
<el-form-item label="项目年份" prop="createYear">
<el-date-picker size="medium" v-model="ruleForm.createYear" type="year" value-format="yyyy"
placeholder="请选择年份" id="input">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row class="row-from">
<el-col class="text-left col-item">
<el-form-item label="项目成员" class="typeface" prop="oneName">
<el-tooltip class="item" size="medium" effect="dark" :disabled="selectNum == 0" placement="top"
v-model="isShowTooltip">
<div slot="content" class="tooltip-con">
<div v-for="item in selectUserList" :key="item.userId" class="size16 team-text">{{ `${item.userName} -
${item.phone}` }}</div>
<i class="el-icon-close close" @click="closeTooltip"></i>
</div>
<el-badge :value="selectNum" class="item" type="primary">
<el-input size="medium" v-model="ruleForm.oneName" clearable @focus="showDialog" @clear="clearName"
placeholder="请选择"></el-input>
</el-badge>
</el-tooltip>
</el-form-item>
</el-col>
</el-row>
<el-row class="row-from">
<el-col class="text-left">
<el-button class="btn select" size="medium" @click="submit" type="primary">提交</el-button>
<el-button class="btn select" size="medium" @click="reset" type="primary">重置</el-button>
</el-col>
</el-row>
</el-form>
<select-user ref="selectUser" @selectUser="selectUser"></select-user>
</div>
</template>
<script>
import selectUser from '@/views/popupAndSelect/selectUser.vue';
export default {
name: 'popupAndSelect',
data() {
const checkOneName = (rule, value, callback) => {
console.log('checkTeam::: ', value);
if (!value) {
callback(new Error('请选择和作社项目组名称'))
} else {
callback()
}
};
return {
isShowTooltip: false,
selectNum: 0,
rules: {
oneName: [
{ required: true, message: '请选择名字', trigger: 'blur,change' },
{ validator: checkOneName, trigger: 'change' }
],
projectName: [
{ required: true, message: '请输入项目名称', trigger: 'blur' },
],
createYear: [
{ required: true, message: '请选择年份', trigger: 'change' },
],
},
ruleForm: {
projectName: "",
createYear: "",
nameList: [],
userIds: [],
oneName: "",
},
selectUserList: [],
}
},
components: { selectUser },
methods: {
showDialog() {
this.$refs.selectUser.show(this.selectUserList)
},
selectUser(data) {
console.log('data::: ', data);
// 拿到所有用户的 userName
let nameList = data.map(item => item.userName);
this.ruleForm.oneName = `${data[0].userName} - ${data[0].phone}`;
this.selectNum = nameList.length;
this.ruleForm.nameList = nameList;
// 拿到所有用户的 userId
this.ruleForm.userIds = data.map(item => item.userId);
this.selectUserList = data;
},
clearName() {
this.ruleForm.oneName = '';
this.selectNum = 0;
this.ruleForm.nameList = [];
this.ruleForm.userIds = [];
this.selectUserList = [];
},
// 关闭 Tooltip
closeTooltip() {
this.isShowTooltip = false;
},
// 提交表单
submit(){
this.$refs.ruleForm.validate(valid => {
if (valid) {
this.$message({
message: '提交成功',
type: 'success'
});
} else {
console.log('error submit!!');
return false;
}
});
},
// 重置表单
reset(){
this.$refs.ruleForm.resetFields();
this.ruleForm.nameList = [];
this.ruleForm.userIds = [];
this.selectNum = 0;
this.selectUserList = [];
}
}
}
</script>
<style lang="scss" scoped>
.ruleForm {
display: flex;
flex-direction: column;
.row-from {
flex-wrap: wrap;
&::before {
display: none;
}
&::after {
display: none;
}
.col-item {
width: 520px;
}
}
}
.text-left {
text-align: left;
}
::v-deep .el-input__inner {
width: 434px;
}
.tooltip-con {
position: relative;
padding: 24px 36px;
.close {
position: absolute;
top: 0;
right: 0;
cursor: pointer;
font-size: 18px;
}
}
</style>
表单字段 oneName
的验证规则:
- 配置必填项验证,点击提交按钮时触发
blur
和change
事件。 - 配置自定义函数
checkOneName
,在oneName
值发生改变时触发change
事件。
还有一点得注意,如果需要回显表单数据,通过后端接口获取数据后,千万不要直接赋值给
ruleForm
对象(例:this.ruleForm = res.data
),因为v-model
绑定的是oneName
, 而oneName
是前端自己定义的属性,后端接口返回的对象里面没有oneName
属性,直接赋值会覆盖原有的ruleForm
对象,导致提交的时候就会一直校验不通过。正确用法:
this.ruleForm= Object.assign(this.ruleForm, res.data);
实现效果
总结
通过上述步骤,我们实现了一个功能完整的表单校验,其中项目成员字段通过点击输入框显示弹窗,并在关闭弹窗时自动给输入框赋值。同时,通过定义验证规则确保了必填项的正确性和完整性。这种设计不仅提高了用户体验,也保证了数据的有效性和一致性。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)