今天遇到了这么一个需求,一个用户填的表。里面有很多的填空。
我一想这好像不是很难,写死就完事了。一个坑对一个就好了,这玩意应该也不怎么改。
结果领导来了一句,后面可能会改,需要做成活的,并且可以弄一个页面让别人配置这里面的信息,自由选择在哪里加一个填空。
于是我百度了一波没找到合适的能用的案例。经过自己琢磨了一下午终于弄出了一个版本可以使用。
保存一下,供大家参考,如果有更好用的方法可以评论说一下,学习一波
一个是纯填空的版本,另外一个是勾选的时候还要输入内容的填空版本
左边是填空,前面的,后面的,中间的,多个的都有。
右边是保存后拿到的数组数据,这里可以允许传入的数据是不需要填空的,那就会直接返回
在配置的地方输入内容比如:啊啊{1}啊啊{2}
注意上面这个文字后面的{},这就代表了输入框。
我们拿到这个数据后,通过正则匹配到对应花括号的位置,然后把他替换成一个输入框
然后输入框输入的内容在通过匹配花括号的位置来替换成答案,最终合并出了一个我们需要的数据
回显也是根据括号内的数字来匹配输入框的dom实现的。具体看下面代码的注释
这里看一下注释哦,注意点和解释的比较详细了。如果看不懂的可以评论提问
然后我这里是html页面写的测试案例。可以直接复制到自己的编辑器种查看效果。
我没有使用外部js和css都写在这里面的,直接复制应该就可以用
两个注意点:
配置填写的数据:list里面
title必填!!里面是通过{}代表输入框的,里面的数字必填,且不能重复!!!
fill也是必填!!里面的值就是上面title括号内的数字,有几个写几个。且不能重复!!!
当然:如果你某一条数据不需要添加填空,那么可以不输入fill字段,也可以不加{} 只需要给个title内容就行了
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
<!-- 公共css文件 -->
<link rel="stylesheet" href="/statics/css/common/common.css">
<!-- 公共js -->
<script src="/statics/vue_element/common.js"></script>
<!-- vue部分依赖 -->
<link rel="stylesheet" href="/statics/vue_element/element.css">
<script src="/statics/vue_element/vue.js"></script>
<script src="/statics/vue_element/element.js"></script>
<script src="/statics/vue_element/axios.js"></script>
<!-- 引入vue类型组件 -->
<script src="/statics/vue_element/httpVueLoader.js"></script>
<title>测试</title>
</head>
<body>
<div id="app">
<el-card>
<div v-html="processString(item.title)" v-for="(item,index) in list" :key="index" :id="item.title"></div>
<button @click="save">保存</button>
</el-card>
</div>
</body>
<script>
let v = new Vue({
el: '#app',
data() {
return {
// 内容保存
from: {},
// 模拟后台返回的数据:其中必须有的是title字段,包含里面{}内必须有数字id,且不能重复。
//然后括号内有几个id,对应的在fill属性内添加一次。这两个属性必须有。且按要求填写,否则不生效。
//其他的属性answer和value什么的都可以不写,后续保存的时候会自动添加上
//如果你需要的是某一个数据不需要填空,只要显示就行了。那就可以不输入花括号一级fill字段,只给一个title
list: [{
"title": "测试{1}是测试{2}是测试{3}是测试{4}是",
"answer": "测试10是测试123是测试22是测试44是",
"fill": "1,2,3,4",
"value1": "10",
"value2": "123",
"value3": "22",
"value4": "44"
},{
"title": "这是没有填空的题目",
},{
"title": "{6}测试测试{7}",
"fill": "6,7",
}]
};
},
mounted() {
// 回显:注意一点!!一定是要输入框全部加载完毕后再调用这个方法,否则拿不到dom就无法赋值输入框。
//如果是后端返回数据,建议在后端接口里面生成完毕所有html后再调用这个方法
this.defaultInfo()
},
methods: {
// 回显输入框内容
defaultInfo() {
// 逻辑:根据每一条数据中保存的时候存入的value值来判断,存在就赋值到输入框中,不存在为空
this.list.forEach(item => {
if(item.fill){
let arr = item.fill.split(',')
arr.forEach(num => {
document.getElementById('p' + num).value = (item['value' + num] ? item[
'value' + num] : '')
})
}
});
},
// 保存
save() {
this.list.forEach(item => {
if(item.fill){
let arr = item.fill.split(',')//根据每一条数据中的id拆分成数组
arr.forEach(num => {
this.from['value' + num] = document.getElementById('p' + num).value//循环每一个id然后拼接成变量名然后把当前对应id的输入框的内容赋值进去
item['value' + num] = document.getElementById('p' + num).value //在每一条数据中也创建一个相同的变量保存一下
})
let answer = this.merge(item.title)//这里把每一条数据和输入框的内容合并成一段完整的话
item.answer = answer //把完整的答案保存到每一条数据的字段中,以后备用,可能会在某些地方需要显示完整的内容就用这个字段
}else{
item.answer = item.title
}
});
// 这是保存的内容,可以把这个数组直接保存到后端,下次拿到这个数组渲染就行
console.log('保存内容',this.list);
},
// 生成:根据{}替换成输入框
processString(str) {
let regex = /\{(.?)\}/g; //匹配{*} 大括号里面任意内容的正则
let arr = str.match(regex); //字符串匹配出来的数组
if (!arr) return str;
let arrNum = arr.map(item => {
str = str.replace(
new RegExp('\\{' + item.substr(1, item.length - 2) + '\\}', 'g'),
`<input id='p${item.substr(1, item.length - 2)}' style='width: 50px;height:24px;margin-left:10px;outline:none;border: none;border-bottom: 1px solid #000;text-align:center' ></input>`
);
}); //循环遍历取出所有正则匹配值并转换为input
return str;
},
// 合并:根据{}替换成输入的内容,返回一句完整的内容包含其他文字以及输入框输入的内容
merge(str) {
let regex = /\{(.?)\}/g;
let arr = str.match(regex);
if (!arr) return str;
let arrNum = arr.map(item => {
str = str.replace(
new RegExp('\\{' + item.substr(1, item.length - 2) + '\\}', 'g'), this.from[
'value' + item.substr(1, item.length - 2)]
);
});
return str;
},
}
})
</script>
<style scoped>
</style>
</html>
勾选的才会在保存的时候获取到的数据,取消勾选会删除数据
因为这里代码是上面的代码更改了一部分,所以有些注释我就不加了,可以看上面的,免得混乱,我这里只是加了关于勾选的注释,逻辑比较简单的,就不详细描述了。功能实现了,可以复制直接使用。
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
<!-- 公共css文件 -->
<link rel="stylesheet" href="/statics/css/common/common.css">
<!-- 公共js -->
<script src="/statics/vue_element/common.js"></script>
<!-- vue部分依赖 -->
<link rel="stylesheet" href="/statics/vue_element/element.css">
<script src="/statics/vue_element/vue.js"></script>
<script src="/statics/vue_element/element.js"></script>
<script src="/statics/vue_element/axios.js"></script>
<!-- 引入vue类型组件 -->
<script src="/statics/vue_element/httpVueLoader.js"></script>
<title>测试</title>
</head>
<body>
<div id="app">
<el-card>
<div v-for="(item,index) in list" :key="index" style="display:flex;align-items: center;border-bottom: 1px solid #999;padding: 10px 0;">
<input type="checkbox" :id="item.title" @click="getCheckbox(item)" style="margin-right:10px;"/>
<div v-html="processString(item.title)" :id="item.title" ></div>
</div>
<button @click="save">保存</button>
</el-card>
</div>
</body>
<script>
let v = new Vue({
el: '#app',
data() {
return {
// 内容保存
from: {},
// 汇总:保存勾选项
summary:[],
// 数据列表
list: [{
"title": "测试{1}是测试{2}是测试{3}是测试{4}是",
"answer": "测试10是测试123是测试22是测试44是",
"fill": "1,2,3,4",
"value1": "10",
"value2": "123",
"value3": "22",
"value4": "44"
},{
"title": "这是没有填空的题目",
},{
"title": "{6}测试测试{7}",
"fill": "6,7",
}]
};
},
mounted() {
this.defaultInfo()
},
methods: {
// 勾选
getCheckbox(row){
let ck=document.getElementById(row.title).checked //获取到对应多选框dom的勾选状态
if(ck==true){
this.summary.push(row.title)//当状态是勾选的时候保存这一条数据
}else{
// 否则删除这条数据
this.summary=this.summary.filter(f=>{
return f!==row.title
})
}
},
// 回显输入框内容
defaultInfo() {
this.list.forEach(item => {
if(item.fill){
let arr = item.fill.split(',')
arr.forEach(num => {
document.getElementById('p' + num).value = (item['value' + num] ? item[
'value' + num] : '')
})
}
});
},
// 保存
save() {
this.list.forEach(item => {
if(item.fill){
let arr = item.fill.split(',')
arr.forEach(num => {
this.from['value' + num] = document.getElementById('p' + num).value
item['value' + num] = document.getElementById('p' + num).value
})
let answer = this.merge(item.title)
item.answer = answer
}else{
item.answer = item.title
}
});
// 去重
let newArr = this.summary.filter(function(value,index,self){
return self.indexOf(value) === index;
});
// 保存勾选项内容
let data=[]
// 根据勾选项匹配对应的数据保存
this.list.forEach(i=>{
if(newArr.indexOf(i.title)!==-1){
data.push(i)
}
})
console.log('勾选的内容:',data);
},
// 生成:根据{}替换成输入框
processString(str) {
let regex = /\{(.?)\}/g; //匹配{*} 大括号里面任意内容的正则
let arr = str.match(regex); //字符串匹配出来的数组
if (!arr) return str;
let arrNum = arr.map(item => {
str = str.replace(
new RegExp('\\{' + item.substr(1, item.length - 2) + '\\}', 'g'),
`<input id='p${item.substr(1, item.length - 2)}' style='width: 50px;height:20px;margin-left:10px;outline:none;border: none;border-bottom: 1px solid #000;text-align:center' ></input>`
);
}); //循环遍历取出所有正则匹配值并转换为input
return str;
},
// 合并:根据{}替换成输入的内容,返回一句完整的内容包含其他文字以及输入框输入的内容
merge(str) {
let regex = /\{(.?)\}/g;
let arr = str.match(regex);
if (!arr) return str;
let arrNum = arr.map(item => {
str = str.replace(
new RegExp('\\{' + item.substr(1, item.length - 2) + '\\}', 'g'), this.from[
'value' + item.substr(1, item.length - 2)]
);
});
return str;
},
}
})
</script>
<style scoped>
</style>
</html>