仿造问卷星--开发一套调查问卷设计工具(2/3)--完整流程

深藏阁楼爱情的钟 2023-09-25 21:46 109阅读 0赞

本章主要内容是完善index.js逻辑功能。

1,修改index.html,直接copy

html和css文件直接从源码中拷贝:

html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  7. <title>问卷设计工具</title>
  8. <link rel="stylesheet" href="./index.css" />
  9. </head>
  10. <body class="wrapper">
  11. <div class="row">
  12. <div class="form">
  13. <div class="row">
  14. 问卷ID:<br />
  15. <input id="qid" type="text" value="1" />
  16. </div>
  17. <div class="row">
  18. 问卷名称:<br />
  19. <input id="qname" type="text" value="测试问卷" />
  20. </div>
  21. <div class="row">
  22. 问卷题目:<br />
  23. <textarea name="" id="text" cols="60" rows="20">
  24. 1. 题目1
  25. 选项1
  26. 选项2
  27. 选项3
  28. 2.题目2[多选题]
  29. 选项4
  30. 选项5
  31. 选项6
  32. 3.单行文本题
  33. </textarea>
  34. </div>
  35. <div class="row">
  36. <button id="btn">生成问卷</button>
  37. </div>
  38. <div class="row">
  39. JSON结果:<br />
  40. <pre class="json-preview" id="json-preview"></pre>
  41. </div>
  42. <div class="row">
  43. <button id="copy">拷贝JSON内容</button>
  44. </div>
  45. </div>
  46. <div class="result">
  47. <div class="row">
  48. 问卷预览:<br />
  49. <div class="html-preview" id="html-preview"></div>
  50. </div>
  51. </div>
  52. </div>
  53. <div class="footer">
  54. 「Powered by
  55. <a href="https://webify.cloudbase.net/">CloudBase Webify</a>
  56. </div>
  57. <script type="module" src="./index.js"></script>
  58. </body>
  59. </html>

css

  1. .form {
  2. float: left;
  3. min-height: 300px;
  4. padding: 20px;
  5. }
  6. .row {
  7. clear: both;
  8. margin: 5px 0;
  9. }
  10. .row::after {
  11. content: "";
  12. display: table;
  13. clear: both;
  14. }
  15. .result {
  16. float: left;
  17. margin-left: 20px;
  18. }
  19. .question-wrap {
  20. padding: 20px;
  21. }
  22. .question-wrap:hover {
  23. background-color: #f5f5f5;
  24. }
  25. .label {
  26. display: block;
  27. margin: 5px;
  28. }
  29. .html-preview {
  30. width: 500px;
  31. box-sizing: border-box;
  32. padding: 20px;
  33. border: solid 1px #ccc;
  34. }
  35. .json-preview {
  36. width: 500px;
  37. height: 100px;
  38. border: solid 1px #ccc;
  39. overflow: scroll;
  40. }

html结构说明:

从上到下:问卷id(输入框),问卷名称(输入框),问卷题目(文本框 存放对象数据),生成问卷(button按钮),JSON结果(文本框),拷贝JSON内容(button按钮)。

其中,用户点击 生成问卷(button按钮),右边生成一个问卷游览;并且把这套问卷的json题目展示到JSON结果文本框中,用户点击 拷贝JSON内容(button按钮),可以将JSON结果文本框中的内容拷贝到剪切板,方便粘贴到其他使用。

2,运行index后,界面如下图

在这里插入图片描述

3,在index.js中添加“生成问卷” button按钮的点击事件:

从text富文本中拿到内容并把用户输入的内容打印输出

  1. document.getElementById("btn").addEventListener("click", function () {
  2. // 用户输入的字符串
  3. let text = document.getElementById("text").value;
  4. console.log(text);
  5. })

打印输出结果如下:
在这里插入图片描述

对打印输出的富文本内容进行修改

4,按空行分割题目,生成数组并打印输出:

利用正则表达式,使用string.split方法,里面传一个正则表达式,将字符串进行空行分割。
在这里插入图片描述
没有文字内容,只有空内容。

  1. //正则表达式 除了空白和换行,其他什么都没有,就是一个空行
  2. const regexQuestionSplit = /\n\s*\n/gm;
  3. document.getElementById("btn").addEventListener("click", function () {
  4. // 用户输入的字符串
  5. let text = document.getElementById("text").value;
  6. console.log(text);
  7. // 按空行分割题目
  8. let questionArr = text.split(regexQuestionSplit);
  9. console.log(questionArr);
  10. })

分割的输出数组结果:
在这里插入图片描述

开始对每个题目进行处理,分割每一行

  1. let questions = [];
  2. // 开始对每个题目进行处理
  3. questionArr.forEach((q) => {
  4. // 分割每一行
  5. let rows = q.split(regexQuestionRowSplit);
  6. //console.log(JSON.stringify(rows));
  7. // 去掉空行
  8. rows = rows.filter((item) => item.trim() !== "");
  9. // console.log(JSON.stringify(rows));
  10. // 全部去掉前后空格
  11. rows = rows.reduce((acc, cur) => {
  12. return [...acc, cur.trim()];
  13. }, []);
  14. console.log(JSON.stringify(rows));
  15. })

输出结果如下:
在这里插入图片描述

5,对填空题,单选题和多选题分别利用正则表达式来处理:

  1. import {
  2. prettyPrintJson } from "pretty-print-json";
  3. import copy from "copy-to-clipboard";
  4. import gotpl from "gotpl";
  5. const regexQuestionSplit = /\n\s*\n/gm;
  6. const regexQuestionRowSplit = /\n/gm;
  7. const regexTitle1 = /(?<index>\d+)[.、][\s]*(?<title>[^\n]*)$/;
  8. const retexTitle2 =
  9. /(?<index>\d+)[.、][\s]*(?<title>[^\[【]*)[\[【](?<type>\W+)[\]】]$/;
  10. let resultObj = {
  11. id: 0,
  12. name: "",
  13. questions: [],
  14. };
  15. let resultJson = "";
  16. const tpl = `
  17. <div class="question">
  18. <div class="row">
  19. 问卷ID:<%= id %>
  20. </div>
  21. <div class="row">
  22. 问卷名称:<%= name %>
  23. </div>
  24. <% for(var i=0, l=questions.length; i<l; ++i){
  25. %>
  26. <% var item = questions[i]; %>
  27. <div class="question-wrap">
  28. <div class="question-title"><%= item.id %>. <%= item.title %>【<%=item.question_type_text %>】</div>
  29. <% if(item.question_type === 'input'){
  30. %>
  31. <div class="input">
  32. <input type="text" name="<%= item.id %>" />
  33. </div>
  34. <% }else if(item.question_type === 'radio'){
  35. %>
  36. <div class="radio">
  37. <% for(var j=0, k=item.options.length; j<k; ++j){
  38. %>
  39. <label class="label">
  40. <input type="radio" name="<%= item.id %>" value="<%= item.options[j].option_id %>" />
  41. <%= item.options[j].option_id %>.
  42. <%= item.options[j].option_value %>
  43. </label>
  44. <% } %>
  45. </div>
  46. <% }else if(item.question_type === 'checkbox'){
  47. %>
  48. <div class="checkbox">
  49. <% for(var j=0, k=item.options.length; j<k; ++j){
  50. %>
  51. <label class="label">
  52. <input type="checkbox" name="<%= item.id %>" value="<%= item.options[j].option_id %>" />
  53. <%= item.options[j].option_id %>.
  54. <%= item.options[j].option_value %>
  55. </label>
  56. <% } %>
  57. </div>
  58. <% } %>
  59. </div>
  60. <% } %>
  61. </div>
  62. `;
  63. const alphabet = [
  64. "A",
  65. "B",
  66. "C",
  67. "D",
  68. "E",
  69. "F",
  70. "G",
  71. "H",
  72. "I",
  73. "J",
  74. "K",
  75. "L",
  76. "M",
  77. "N",
  78. "O",
  79. "P",
  80. "Q",
  81. "R",
  82. "S",
  83. "T",
  84. "U",
  85. "V",
  86. "W",
  87. "X",
  88. "Y",
  89. "Z",
  90. ];
  91. const type_map = {
  92. 填空题: "input",
  93. 单选题: "radio",
  94. 多选题: "checkbox",
  95. };
  96. function getQuestionType(questionType) {
  97. return type_map[questionType] || "类型错误";
  98. }
  99. function formatQuestion(index, title, typeText, options = null) {
  100. // console.log(index, title, questionType);
  101. const questionType = getQuestionType(typeText);
  102. let question = {
  103. id: +index,
  104. title: title,
  105. question_type: questionType,
  106. question_type_text: typeText,
  107. };
  108. // console.log(options);
  109. if (options && options.length) {
  110. let tmpOptions = [];
  111. options.forEach((item, index) => {
  112. tmpOptions.push({
  113. option_id: alphabet[index],
  114. option_value: item,
  115. });
  116. });
  117. question.options = tmpOptions;
  118. }
  119. return question;
  120. }
  121. document.getElementById("btn").addEventListener("click", function () {
  122. resultObj.id = +document.getElementById("qid").value.trim();
  123. resultObj.name = document.getElementById("qname").value.trim();
  124. // 用户输入的字符串
  125. let text = document.getElementById("text").value;
  126. // console.log(text);
  127. // 按空行分割题目
  128. let questionArr = text.split(regexQuestionSplit);
  129. // console.log(questionArr);
  130. let questions = [];
  131. // 开始对每个题目进行处理
  132. questionArr.forEach((q) => {
  133. // 分割每一行
  134. let rows = q.split(regexQuestionRowSplit);
  135. // console.log(JSON.stringify(rows));
  136. // 去掉空行
  137. rows = rows.filter((item) => item.trim() !== "");
  138. // console.log(JSON.stringify(rows));
  139. // 全部去掉前后空格
  140. rows = rows.reduce((acc, cur) => {
  141. return [...acc, cur.trim()];
  142. }, []);
  143. // console.log(JSON.stringify(rows));
  144. // 如果是单行,是填空题
  145. if (rows.length === 1) {
  146. if (regexTitle1.test(rows[0])) {
  147. let matches = rows[0].match(regexTitle1);
  148. // console.log(matches);
  149. let {
  150. index, title } = matches.groups;
  151. questions.push(formatQuestion(index, title, "填空题"));
  152. }
  153. }
  154. // console.log(questions);
  155. // 多行,可能是单选或多选
  156. if (rows.length > 1) {
  157. // 第一行是标题,其他行是选项
  158. let [titleRow, ...options] = rows;
  159. // console.log("titleRow", titleRow);
  160. // console.log("options", options);
  161. // 先验证带题目类型的格式
  162. if (retexTitle2.test(titleRow)) {
  163. let matches = titleRow.match(retexTitle2);
  164. // console.log(matches)
  165. let {
  166. index, title, type } = matches.groups;
  167. questions.push(formatQuestion(index, title, type, options));
  168. } else if (regexTitle1.test(titleRow)) {
  169. // 没有设置类型的,当成单选题
  170. let matches = titleRow.match(regexTitle1);
  171. let {
  172. index, title } = matches.groups;
  173. console.log(index, title, options);
  174. questions.push(formatQuestion(index, title, "单选题", options));
  175. }
  176. // console.log(questions);
  177. }
  178. });
  179. resultObj.questions = questions;
  180. resultJson = JSON.stringify(resultObj);
  181. console.log(resultObj);
  182. document.getElementById("json-preview").innerHTML =
  183. prettyPrintJson.toHtml(resultObj);
  184. document.getElementById("html-preview").innerHTML = gotpl.render(
  185. tpl,
  186. resultObj
  187. );
  188. });
  189. document.getElementById("copy").onclick = () => {
  190. copy(resultJson);
  191. alert("已复制到剪贴板");
  192. };

6,输出最终结果:

在这里插入图片描述

输出的json数据如下:

  1. [
  2. {
  3. "id": 1,
  4. "title": "题目1",
  5. "question_type": "radio",
  6. "question_type_text": "单选题",
  7. "options": [
  8. {
  9. "option_id": "A",
  10. "option_value": "选项1"
  11. },
  12. {
  13. "option_id": "B",
  14. "option_value": "选项2"
  15. },
  16. {
  17. "option_id": "C",
  18. "option_value": "选项3"
  19. }
  20. ]
  21. },
  22. {
  23. "id": 2,
  24. "title": "题目2",
  25. "question_type": "checkbox",
  26. "question_type_text": "多选题",
  27. "options": [
  28. {
  29. "option_id": "A",
  30. "option_value": "选项4"
  31. },
  32. {
  33. "option_id": "B",
  34. "option_value": "选项5"
  35. },
  36. {
  37. "option_id": "C",
  38. "option_value": "选项6"
  39. }
  40. ]
  41. },
  42. {
  43. "id": 3,
  44. "title": "单行文本题",
  45. "question_type": "input",
  46. "question_type_text": "填空题"
  47. },
  48. {
  49. "id": 4,
  50. "title": "题目2",
  51. "question_type": "checkbox",
  52. "question_type_text": "多选题",
  53. "options": [
  54. {
  55. "option_id": "A",
  56. "option_value": "选项4"
  57. },
  58. {
  59. "option_id": "B",
  60. "option_value": "选项5"
  61. },
  62. {
  63. "option_id": "C",
  64. "option_value": "选项6"
  65. }
  66. ]
  67. }
  68. ]

发表评论

表情:
评论列表 (有 0 条评论,109人围观)

还没有评论,来说两句吧...

相关阅读

    相关 HDU6344 调查问卷

    状态压缩 + 模拟 把AB串压缩成二进制,A用1表示,B用0表示。 枚举所有问题的子集,选中的问题用1表示,其余的用0表示。对于每个子集,我们去和所有问题按位与,这样对