【vue填空题功能双版本】js简单实现填空题功能,可勾选并输入的填空功能,完形填空,可自由控制填空位置,段落中可加多个填空,可以保存回显数据【详细注释】

背景

今天遇到了这么一个需求,一个用户填的表。里面有很多的填空。
我一想这好像不是很难,写死就完事了。一个坑对一个就好了,这玩意应该也不怎么改。
结果领导来了一句,后面可能会改,需要做成活的,并且可以弄一个页面让别人配置这里面的信息,自由选择在哪里加一个填空。
于是我百度了一波没找到合适的能用的案例。经过自己琢磨了一下午终于弄出了一个版本可以使用。
保存一下,供大家参考,如果有更好用的方法可以评论说一下,学习一波

双版本

一个是纯填空的版本,另外一个是勾选的时候还要输入内容的填空版本

-----------【纯填空的版本】-----------

效果图

左边是填空,前面的,后面的,中间的,多个的都有。
右边是保存后拿到的数组数据,这里可以允许传入的数据是不需要填空的,那就会直接返回
在这里插入图片描述

逻辑简介

在配置的地方输入内容比如:啊啊{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>
  • 21
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

接口写好了吗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值