1.如何动态添加/删除某个表单项
如上图,点击加号就会生成一行表单,第一行不可以删除,不显示删除按钮,从第二行开始显示删除按钮。(需要注意的是目前实现的功能暂不支持从中间插入一行,比如你在第一行和第二行之间想插入一行,点击第一行的添加按钮,新的一行永远是加在最后的)
- 解决方案:
<div v-for="(item, index) in temp.applyItem" :key="'applyItem'+index">
<el-card class="box-card ml-10 mt-7 mr-10">
<el-row>
<el-form-item
label="检查部位"
:prop="'applyItem.' + index + '.examPartCode'"
>
<el-select v-model.trim="item.examPartCode" class="w-160" filterable clearable placeholder="请选择">
<el-option
v-for="(item,index) in examPartOptions"
:key="'examPartCode'+index"
:label="item.label"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item
label="检查方法"
:prop="'applyItem.' + index + '.examMethodCode'"
>
<el-select v-model.trim="item.examMethodCode" class="w-160" filterable clearable placeholder="请选择">
<el-option
v-for="(item,index) in examMethodOptions"
:key="'examMethodCode'+index"
:label="item.label"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item
label="检查项目"
:prop="'applyItem.' + index + '.itemCode'"
>
<el-select v-model.trim="item.itemCode" class="w-160" filterable clearable placeholder="请选择">
<el-option
v-for="(item,index) in examItemOptions"
:key="'itemCode'+index"
:label="item.label"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<i class="el-icon-circle-plus"
style="font-size: 20px; color: #0a76a4; vertical-align: middle; margin-top: 5px; margin-left: 5px"
@click="addItem">
</i>
<i class="el-icon-delete"
style="font-size: 20px; color: #CB0526; vertical-align: middle; margin-top: 5px; margin-left: 5px"
@click="deleteItem(item, index)"
v-if="index > 0">
</i>
</el-row>
</el-card>
</div>
删除按钮的显示使用v-if判断就好了,使用 v-if="index > 0" 判断它不是第一行数据就好了
注意:prop的写法, :prop="'applyItem.' + index + '.examPartCode'" applyItem是一个数组,具体定义如下代码片段。prop是以数组.下标.属性的形式拼接的 这两个 “点” 一定不能忽略,v-model直接绑定当前遍历对象的对应属性
data() {
return {
temp: {
/* 其他字段 */
applyItem: [{ //数组
examMethodCode: "",
examMethodName: "",
examPartCode: "",
examPartName: "",
itemCode: "",
itemName: ""
}], //检查项目数组
},
};
},
下面是删除和添加一行表单的方法
methods: {
//新增表单的检查项目
addItem() {
this.temp.applyItem.push({
examPartCode: "",
examPartName: "",
itemCode: "",
itemName: "",
examMethodCode: "",
examMethodName: "",
});
},
//删除表单项目
deleteItem(item, index) {
this.temp.applyItem.splice(index, 1);
},
}
添加只需要把这几个push进去就可以了,删除使用splice删除一个就好了,要注意如果你也是使用下拉形式,在提交时,把对应的name做赋值。 如果你用的input,则可以忽略下面的处理,其中examPartOptions等几个Options的定义为
examPartOptions: [ {id: 1, label: "选项1"}, {id: 2, label: "选项2"}, {id: 3, label: "选项3"}, // ...... ]
methods: {
createData() {
this.$refs["dataForm"].validate((valid) => {
if (valid) {
let arr = this.temp.applyItem;
// 遍历数组,对应赋值
for (let i = 0; i < arr.length; i++) {
let examPart = this.examPartOptions.filter((item) => {
return item.id == arr[i].examPartCode;
});
let examItem = this.examItemOptions.filter((item) => {
return item.id == arr[i].itemCode;
});
let examMethod = this.examMethodOptions.filter((item) => {
return item.id == arr[i].examMethodCode;
});
arr[i].examPartName = examPart.length > 0 ? examPart[0].label : null;
arr[i].itemName = examItem.length > 0 ? examItem[0].label : null;
arr[i].examMethodName = examMethod.length > 0 ? examMethod[0].label : null;
}
// 提交表单的一些代码.......
},
}
}
}
- 如果需要表单检验怎么办
只需添加 :rules="rules.examPartCode" 然后 rules: {} 正常写就能分别对每一行的数据进行校验。
代码如下,注意与之前的不同,多了:rules
<el-form-item
label="检查部位"
:prop="'applyItem.' + index + '.examPartCode'"
:rules="rules.examPartCode"
>
<el-select v-model.trim="item.examPartCode" class="w-160" filterable clearable placeholder="请选择">
<el-option
v-for="(item,index) in examPartOptions"
:key="'examPartCode'+index"
:label="item.label"
:value="item.id"
>
</el-option>
</el-select>
</el-form-item>
data() {
return {
temp: {
/* 其他字段 */
applyItem: [{ //数组
examMethodCode: "",
examMethodName: "",
examPartCode: "",
examPartName: "",
itemCode: "",
itemName: ""
}], //检查项目数组
},
// 表单规则
rules: {
/* 其他需要检验的表单规则 */
examPartCode: [
{
required: true,
message: "请选择检查部位",
trigger: "blur, change",
},
],
itemCode: [
{
required: true,
message: "请选择检查项目",
trigger: "blur, change",
},
],
examMethodCode: [
{
required: true,
message: "请选择检查方法",
trigger: "blur, change",
},
],
},
};
},
2.el-select多选时回显,赋值等问题
某些表单需要使用select多选时,在编辑和回显时会出现问题
我们v-model绑定的是temp.appoiMethods
temp中
上面那个仅用于接收后台传回的数据 格式为 1,2,3..... 以逗号分割的字符串
下面带s的是我们v-model绑定的 是一个数组 准确来说应该是number类型的(必须)可以参考vue官方文档关于 el-select 多选的描述
options如下
添加时都是没问题的,编辑回显会有回显问题和选中时没反应的问题,处理方式为
// 弹出编辑框
handleUpdate(row) {
this.temp = Object.assign({}, row); // copy obj
// 分割成字符数组
let splitStr = (row.appoiMethod || "").split(',');
// 定义一个新数组接收
let appoiMethodArr = [];
for (let item in splitStr) {
// 解析成int,因为el-select 多选时回显需要number类型的数组(可以查阅官方文档)
appoiMethodArr.push(parseInt(splitStr[item]));
}
this.temp.appoiMethods = appoiMethodArr;
if (splitStr == "" || splitStr == null){ // 避免显示NaN
row.appoiMethods = "";
this.temp.appoiMethods = "";
}
// 其他操作
},
在我们做新增和修改的时候还需要把appoiMethods的值重新拼接成以“逗号”分割的串赋值给appoiMethod
//新增修改前给某些字段赋值
beforeAddOrUpdate() {
let arr = [];
for (let i = 0; i<this.temp.appoiMethods.length; i++) {
arr.push(this.temp.appoiMethods[i])
}
// arr数组拼接“逗号”形成字符串,并赋值给appoiMethod 传给后端
this.temp.appoiMethod = arr.join();
},
然后再新增和修改数据提交之前调用beforeAddOrUpdate方法,处理select多选需要的值
//新增
createData() {
this.beforeAddOrUpdate();
this.$refs["dataForm"].validate((valid) => {
if (valid) {
xxxxxx.add(this.temp).then((response) => {
this.list.unshift(response.result);
this.dialogFormVisible = false;
this.$notify({
title: "成功",
message: "创建成功",
type: "success",
duration: 2000,
});
});
}
});
},
修改同上调用一下 this.beforeAddOrUpdate(); 即可
最后解决编辑时回显的问题,前面的虽然已经做到了数据的一致,没有差错,也可以正常编辑新增,但是在编辑时回显,虽然数据已经被选择了,但是页面却没动态响应,点了之后没反应,这时候就需要一个@change调用方法强制更新
<el-col :span="12">
<el-form-item size="small" label="预约方式" prop="appoiMethod">
<el-select class="filter-item"
v-model="temp.appoiMethods"
placeholder="请选择预约方式"
@change="appoiMethodChange"
clearable
filterable
multiple
>
<el-option v-for="(item,index) in appoiMethodOptions"
:key="index"
:label="item.label"
:value="item.key"></el-option>
</el-select>
</el-form-item>
</el-col>
appoiMethodChange(item) {
this.$forceUpdate();
}
3.input表单校验时,规定保留n位小数点,输入多余的自动删除
<el-form-item label="患者身高" prop="patHeight">
<el-input
class="w-160"
@keyup.native="temp.patHeight = oninput(temp.patHeight,2)"
v-model="temp.patHeight"
placeholder="请输入"
></el-input>
</el-form-item>
//oninput 限制输入框小数点位数,多出的过滤掉
oninput(num, limit) {
let str = num
let len1 = str.substr(0, 1)
let len2 = str.substr(1, 1)
//如果第一位是0,第二位不是点,就用数字把点替换掉
if (str.length > 1 && len1 === 0 && len2 !== ".") {
str = str.substr(1, 1)
}
//第一位不能是.
if (len1 === ".") {
str = ""
}
//限制只能输入一个小数点
if (str.indexOf(".") !== -1) {
var str_ = str.substr(str.indexOf(".") + 1)
if (str_.indexOf(".") !== -1) {
str = str.substr(0, str.indexOf(".") + str_.indexOf(".") + 1)
}
}
//正则替换
//str = str.replace(/[^\d^\.]+/g, '') // 保留数字和小数点
str = str.replace(/[^\d^.]+/g, '') // 保留数字和小数点
if (limit / 1 === 1) {
str = str.replace(/^\D*([0-9]\d*\.?\d{0,1})?.*$/,'$1') // 小数点后只能输 1 位
} else {
str = str.replace(/^\D*([0-9]\d*\.?\d{0,2})?.*$/,'$1') // 小数点后只能输 2 位
}
return str
},
4.VUE2分页父子组件传值的问题(props父子组件 实现数据双向绑定)
在vue2中使用element的分页,数据都在父组件中,引用子组件table表格,且分页组件也在子组件,如果直接传分页参数到子组件,控制台会报错。注意(vue2中props属性只能父->子 而不能子->父 也就是说不能双向传值,这也造成无论你点那一页都不会切换,因为子组件的分页参数的值传不回来,父在请求时传的永远是第一次的参数,所以分页失败)
父组件代码(只考虑本功能,为了避免其他无关数据干预,删除掉其他无关的数据,保证代码看起来清爽) MyTable即为子组件,通过props将分页参数传递给子组件
<template>
<MyTable
:listTotal="listTotal"
:page="temp.page"
:limit="temp.limit"
@handleChildFun="handleChildFun"
></MyTable>
</template>
data() {
return {
list: null, // 页面数据
listTotal: 0, // 页面数据行数
loading: true,
temp: {
page: 1, // 第几页
limit: 10, //每页显示几个
},
};
},
methods: {
// 获取列表
getList() {
this.loading = true;
xxxxx.getList(this.temp).then((res) => {
this.listTotal = res.result.count; //返回的当前列表数据个数赋值给listTotal
});
},
// 处理子组件的方法
handleChildFun(type, data) {
// type为方法名, data为操作数据
this[type](data);
},
// 分页事件
handleCurrentChange(val) {
this.temp.page = val.page;
this.temp.limit = val.limit;
this.getList(); //加载
},
// 子组件中watch监听子组件中的listTotal,一旦listToal值变化会立刻调用该方法将值回传,实现子->父传值
onListTotalChange(val) {
this.listTotal = val;
},
// 子组件中watch监听子组件中的page,一旦page值变化会立刻调用该方法将值回传,实现子->父传值
onPageChange(val) {
this.temp.page = val;
},
// 子组件中watch监听子组件中的limit,一旦limit值变化会立刻调用该方法将值回传,实现子->父传值
onLimitChange(val) {
this.temp.limit = val;
},
},
子组件代码
<template>
<div class="app-container">
<el-table>
</el-table>
<pagination
v-show="curListTotal > 0"
:total="curListTotal"
:page.sync="curPage"
:limit.sync="curLimit"
@pagination="handleCurrentChange"
/>
</div>
</template>
export default {
props: [
"listTotal",
"page",
"limit",
],
data() {
return { // 把上面props传来的父组件的值保存到子组件的数据中
curListTotal: this.listTotal,
curPage: this.page,
curLimit: this.limit,
};
},
watch: {
// 一旦listToal值表动调用curListTotal curListTotal通过emit 最终调用父组件中onListTotalChange方法,并把改变的值传回父组件
listTotal(val) {
this.curListTotal = val;
},
curListTotal(val) {
this.$emit("handleChildFun", "onListTotalChange", val);
},
page(val) {
this.curPage = val;
},
curPage(val) {
this.$emit("handleChildFun", "onPageChange", val);
},
limit(val) {
this.curLimit = val;
},
curLimit(val) {
this.$emit("handleChildFun", "onLimitChange", val);
},
},
methods: {
// 分页事件,调用父组件中的handleCurrentChange方法
handleCurrentChange(val) {
this.$emit("handleChildFun", "handleCurrentChange", val);
},
},
};