本章主要内容是完善index.js逻辑功能。
1,修改index.html,直接copy
html和css文件直接从源码中拷贝:
html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>问卷设计工具</title> <link rel="stylesheet" href="./index.css" /> </head> <body class="wrapper"> <div class="row"> <div class="form"> <div class="row"> 问卷ID:<br /> <input id="qid" type="text" value="1" /> </div> <div class="row"> 问卷名称:<br /> <input id="qname" type="text" value="测试问卷" /> </div> <div class="row"> 问卷题目:<br /> <textarea name="" id="text" cols="60" rows="20"> 1. 题目1 选项1 选项2 选项3 2.题目2[多选题] 选项4 选项5 选项6 3.单行文本题 </textarea> </div> <div class="row"> <button id="btn">生成问卷</button> </div> <div class="row"> JSON结果:<br /> <pre class="json-preview" id="json-preview"></pre> </div> <div class="row"> <button id="copy">拷贝JSON内容</button> </div> </div> <div class="result"> <div class="row"> 问卷预览:<br /> <div class="html-preview" id="html-preview"></div> </div> </div> </div> <div class="footer"> 「Powered by <a href="https://webify.cloudbase.net/">CloudBase Webify</a>」 </div> <script type="module" src="./index.js"></script> </body> </html>
css
.form { float: left; min-height: 300px; padding: 20px; } .row { clear: both; margin: 5px 0; } .row::after { content: ""; display: table; clear: both; } .result { float: left; margin-left: 20px; } .question-wrap { padding: 20px; } .question-wrap:hover { background-color: #f5f5f5; } .label { display: block; margin: 5px; } .html-preview { width: 500px; box-sizing: border-box; padding: 20px; border: solid 1px #ccc; } .json-preview { width: 500px; height: 100px; border: solid 1px #ccc; overflow: scroll; }
html结构说明:
从上到下:问卷id(输入框),问卷名称(输入框),问卷题目(文本框 存放对象数据),生成问卷(button按钮),JSON结果(文本框),拷贝JSON内容(button按钮)。
其中,用户点击 生成问卷(button按钮),右边生成一个问卷游览;并且把这套问卷的json题目展示到JSON结果文本框中,用户点击 拷贝JSON内容(button按钮),可以将JSON结果文本框中的内容拷贝到剪切板,方便粘贴到其他使用。
2,运行index后,界面如下图
3,在index.js中添加“生成问卷” button按钮的点击事件:
从text富文本中拿到内容并把用户输入的内容打印输出
document.getElementById("btn").addEventListener("click", function () { // 用户输入的字符串 let text = document.getElementById("text").value; console.log(text); })
打印输出结果如下:
对打印输出的富文本内容进行修改
4,按空行分割题目,生成数组并打印输出:
利用正则表达式,使用string.split方法,里面传一个正则表达式,将字符串进行空行分割。
没有文字内容,只有空内容。
//正则表达式 除了空白和换行,其他什么都没有,就是一个空行 const regexQuestionSplit = /\n\s*\n/gm; document.getElementById("btn").addEventListener("click", function () { // 用户输入的字符串 let text = document.getElementById("text").value; console.log(text); // 按空行分割题目 let questionArr = text.split(regexQuestionSplit); console.log(questionArr); })
分割的输出数组结果:
开始对每个题目进行处理,分割每一行
let questions = []; // 开始对每个题目进行处理 questionArr.forEach((q) => { // 分割每一行 let rows = q.split(regexQuestionRowSplit); //console.log(JSON.stringify(rows)); // 去掉空行 rows = rows.filter((item) => item.trim() !== ""); // console.log(JSON.stringify(rows)); // 全部去掉前后空格 rows = rows.reduce((acc, cur) => { return [...acc, cur.trim()]; }, []); console.log(JSON.stringify(rows)); })
输出结果如下:
5,对填空题,单选题和多选题分别利用正则表达式来处理:
import { prettyPrintJson } from "pretty-print-json"; import copy from "copy-to-clipboard"; import gotpl from "gotpl"; const regexQuestionSplit = /\n\s*\n/gm; const regexQuestionRowSplit = /\n/gm; const regexTitle1 = /(?<index>\d+)[.、][\s]*(?<title>[^\n]*)$/; const retexTitle2 = /(?<index>\d+)[.、][\s]*(?<title>[^\[【]*)[\[【](?<type>\W+)[\]】]$/; let resultObj = { id: 0, name: "", questions: [], }; let resultJson = ""; const tpl = ` <div class="question"> <div class="row"> 问卷ID:<%= id %> </div> <div class="row"> 问卷名称:<%= name %> </div> <% for(var i=0, l=questions.length; i<l; ++i){ %> <% var item = questions[i]; %> <div class="question-wrap"> <div class="question-title"><%= item.id %>. <%= item.title %>【<%=item.question_type_text %>】</div> <% if(item.question_type === 'input'){ %> <div class="input"> <input type="text" name="<%= item.id %>" /> </div> <% }else if(item.question_type === 'radio'){ %> <div class="radio"> <% for(var j=0, k=item.options.length; j<k; ++j){ %> <label class="label"> <input type="radio" name="<%= item.id %>" value="<%= item.options[j].option_id %>" /> <%= item.options[j].option_id %>. <%= item.options[j].option_value %> </label> <% } %> </div> <% }else if(item.question_type === 'checkbox'){ %> <div class="checkbox"> <% for(var j=0, k=item.options.length; j<k; ++j){ %> <label class="label"> <input type="checkbox" name="<%= item.id %>" value="<%= item.options[j].option_id %>" /> <%= item.options[j].option_id %>. <%= item.options[j].option_value %> </label> <% } %> </div> <% } %> </div> <% } %> </div> `; const alphabet = [ "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", ]; const type_map = { 填空题: "input", 单选题: "radio", 多选题: "checkbox", }; function getQuestionType(questionType) { return type_map[questionType] || "类型错误"; } function formatQuestion(index, title, typeText, options = null) { // console.log(index, title, questionType); const questionType = getQuestionType(typeText); let question = { id: +index, title: title, question_type: questionType, question_type_text: typeText, }; // console.log(options); if (options && options.length) { let tmpOptions = []; options.forEach((item, index) => { tmpOptions.push({ option_id: alphabet[index], option_value: item, }); }); question.options = tmpOptions; } return question; } document.getElementById("btn").addEventListener("click", function () { resultObj.id = +document.getElementById("qid").value.trim(); resultObj.name = document.getElementById("qname").value.trim(); // 用户输入的字符串 let text = document.getElementById("text").value; // console.log(text); // 按空行分割题目 let questionArr = text.split(regexQuestionSplit); // console.log(questionArr); let questions = []; // 开始对每个题目进行处理 questionArr.forEach((q) => { // 分割每一行 let rows = q.split(regexQuestionRowSplit); // console.log(JSON.stringify(rows)); // 去掉空行 rows = rows.filter((item) => item.trim() !== ""); // console.log(JSON.stringify(rows)); // 全部去掉前后空格 rows = rows.reduce((acc, cur) => { return [...acc, cur.trim()]; }, []); // console.log(JSON.stringify(rows)); // 如果是单行,是填空题 if (rows.length === 1) { if (regexTitle1.test(rows[0])) { let matches = rows[0].match(regexTitle1); // console.log(matches); let { index, title } = matches.groups; questions.push(formatQuestion(index, title, "填空题")); } } // console.log(questions); // 多行,可能是单选或多选 if (rows.length > 1) { // 第一行是标题,其他行是选项 let [titleRow, ...options] = rows; // console.log("titleRow", titleRow); // console.log("options", options); // 先验证带题目类型的格式 if (retexTitle2.test(titleRow)) { let matches = titleRow.match(retexTitle2); // console.log(matches) let { index, title, type } = matches.groups; questions.push(formatQuestion(index, title, type, options)); } else if (regexTitle1.test(titleRow)) { // 没有设置类型的,当成单选题 let matches = titleRow.match(regexTitle1); let { index, title } = matches.groups; console.log(index, title, options); questions.push(formatQuestion(index, title, "单选题", options)); } // console.log(questions); } }); resultObj.questions = questions; resultJson = JSON.stringify(resultObj); console.log(resultObj); document.getElementById("json-preview").innerHTML = prettyPrintJson.toHtml(resultObj); document.getElementById("html-preview").innerHTML = gotpl.render( tpl, resultObj ); }); document.getElementById("copy").onclick = () => { copy(resultJson); alert("已复制到剪贴板"); };
6,输出最终结果:
输出的json数据如下:
[ { "id": 1, "title": "题目1", "question_type": "radio", "question_type_text": "单选题", "options": [ { "option_id": "A", "option_value": "选项1" }, { "option_id": "B", "option_value": "选项2" }, { "option_id": "C", "option_value": "选项3" } ] }, { "id": 2, "title": "题目2", "question_type": "checkbox", "question_type_text": "多选题", "options": [ { "option_id": "A", "option_value": "选项4" }, { "option_id": "B", "option_value": "选项5" }, { "option_id": "C", "option_value": "选项6" } ] }, { "id": 3, "title": "单行文本题", "question_type": "input", "question_type_text": "填空题" }, { "id": 4, "title": "题目2", "question_type": "checkbox", "question_type_text": "多选题", "options": [ { "option_id": "A", "option_value": "选项4" }, { "option_id": "B", "option_value": "选项5" }, { "option_id": "C", "option_value": "选项6" } ] } ]