本章主要内容是完善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选项32.题目2[多选题]选项4选项5选项63.单行文本题</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"}]}
]