@focus-teach/ui 1.0.54 → 1.0.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/api/index.js +48 -1
- package/lib/img/ico_xkw.d6be8fa4.png +0 -0
- package/lib/ui.common.js +30479 -18641
- package/lib/ui.css +1 -1
- package/lib/ui.umd.js +30479 -18641
- package/lib/ui.umd.min.js +32 -5
- package/package.json +4 -2
- package/utils/common.js +217 -13
- package/utils/index.js +1181 -0
- package/utils/xkw/util.js +588 -0
- package/utils/xkw/xkw-sdk.js +296 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xkw.com Inc. Copyright (c) 2022 All Rights Reserved.
|
|
3
|
+
*
|
|
4
|
+
* 试题html拆分服务
|
|
5
|
+
* 将题库服务拆分出的html格式的题干答案解析拆分出其中的内部结构
|
|
6
|
+
*
|
|
7
|
+
* 使用方法:
|
|
8
|
+
* var parser = new QuestionParserService();
|
|
9
|
+
* var stem= parser.splitStem(stemHTML)
|
|
10
|
+
* var answer=parser.splitAnswer(answerHtml)
|
|
11
|
+
* var explanation= parser.splitExplanation(explanationHtml);
|
|
12
|
+
*
|
|
13
|
+
* @author Luozl
|
|
14
|
+
* @version 1.0
|
|
15
|
+
* @date 2022年07月01日
|
|
16
|
+
*/
|
|
17
|
+
export function QuestionParserService() {
|
|
18
|
+
var SQ = "sq";
|
|
19
|
+
/**
|
|
20
|
+
* 题干
|
|
21
|
+
*/
|
|
22
|
+
var QML_STEM = "qml-stem";
|
|
23
|
+
/**
|
|
24
|
+
* 小题
|
|
25
|
+
*/
|
|
26
|
+
var QML_SQ = "qml-sq";
|
|
27
|
+
/**
|
|
28
|
+
* 答题空
|
|
29
|
+
*/
|
|
30
|
+
var QML_BK = "qml-bk";
|
|
31
|
+
/**
|
|
32
|
+
* 小问
|
|
33
|
+
*/
|
|
34
|
+
var QML_ID_CONTAINER = "id-container";
|
|
35
|
+
/**
|
|
36
|
+
* 小问属性
|
|
37
|
+
*/
|
|
38
|
+
var QML_ID_CONTAINER_VAL = "question";
|
|
39
|
+
/**
|
|
40
|
+
* 选项组
|
|
41
|
+
*/
|
|
42
|
+
var QML_OG = "qml-og";
|
|
43
|
+
/**
|
|
44
|
+
* 题号
|
|
45
|
+
*/
|
|
46
|
+
var QML_QUES_NO = "ques-no";
|
|
47
|
+
/**
|
|
48
|
+
* 选项
|
|
49
|
+
*/
|
|
50
|
+
var QML_OP = "qml-op";
|
|
51
|
+
/**
|
|
52
|
+
* 选择题
|
|
53
|
+
*/
|
|
54
|
+
var QML_ISOP = "qml-isop";
|
|
55
|
+
/**
|
|
56
|
+
* 答案
|
|
57
|
+
*/
|
|
58
|
+
var QML_ANSWER = "qml-answer";
|
|
59
|
+
/**
|
|
60
|
+
* 小题答案
|
|
61
|
+
*/
|
|
62
|
+
var QML_AN_SQ = "qml-an-sq";
|
|
63
|
+
/**
|
|
64
|
+
* 答题空
|
|
65
|
+
*/
|
|
66
|
+
var QML_AN = "qml-an";
|
|
67
|
+
/**
|
|
68
|
+
* 机阅
|
|
69
|
+
*/
|
|
70
|
+
var QML_EXACT = "qml-exact";
|
|
71
|
+
/**
|
|
72
|
+
* 解析
|
|
73
|
+
*/
|
|
74
|
+
var QML_EXPLANATION = "qml-explanation";
|
|
75
|
+
/**
|
|
76
|
+
* 解析详情
|
|
77
|
+
*/
|
|
78
|
+
var QML_SEG = "qml-seg";
|
|
79
|
+
/**
|
|
80
|
+
* 小题解析
|
|
81
|
+
*/
|
|
82
|
+
var QML_EXPS_SQ = "qml-exps-sq";
|
|
83
|
+
/**
|
|
84
|
+
* 解析名称
|
|
85
|
+
*/
|
|
86
|
+
var SEG_NAME = "seg-name";
|
|
87
|
+
/**
|
|
88
|
+
* 小题解析详情输出模板
|
|
89
|
+
*/
|
|
90
|
+
var SQ_DETAIL = "题详解";
|
|
91
|
+
/**
|
|
92
|
+
* 选项序号(A,B,C,D等)
|
|
93
|
+
*/
|
|
94
|
+
var OP_INDEXES = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"
|
|
95
|
+
, "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];
|
|
96
|
+
|
|
97
|
+
function htmlResolve(htmlStr) {
|
|
98
|
+
var parseHTML = "";
|
|
99
|
+
|
|
100
|
+
if (typeof DOMParser == "function") {
|
|
101
|
+
parseHTML = function (xmlStr) {
|
|
102
|
+
return (new DOMParser()).parseFromString(xmlStr, "text/html");
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
throw new Error("No html parser found");
|
|
106
|
+
}
|
|
107
|
+
if (htmlStr == null || htmlStr.trim() == "") {
|
|
108
|
+
throw new Error("需要转化的html不能为空");
|
|
109
|
+
}
|
|
110
|
+
//将文本中\"替换为"
|
|
111
|
+
htmlStr = htmlStr.replaceAll("\\\"", "\"");
|
|
112
|
+
return parseHTML(htmlStr);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function splitStem(stemElement) {
|
|
116
|
+
if (stemElement == null) {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
var retStem = {};
|
|
120
|
+
retStem.sqs = Object.values(stemElement.querySelectorAll("." + QML_SQ + ">." + QML_STEM))
|
|
121
|
+
.map(function (node) {
|
|
122
|
+
return splitStem(node);
|
|
123
|
+
});
|
|
124
|
+
var ogElement = Object.values(stemElement.childNodes).filter(function (element) {
|
|
125
|
+
return hasClass(element, QML_OG)
|
|
126
|
+
});
|
|
127
|
+
retStem.og = splitOg(ogElement.length > 0 ? ogElement[0] : null);
|
|
128
|
+
retStem.html = Object.values(stemElement.childNodes).filter(function (element) {
|
|
129
|
+
return !hasClass(element, QML_SQ) && !hasClass(element, QML_OG) && !hasClass(element, QML_QUES_NO);
|
|
130
|
+
}).map(function (element) {
|
|
131
|
+
return element.outerHTML;
|
|
132
|
+
}).join("");
|
|
133
|
+
retStem.type = getQuestionType(retStem);
|
|
134
|
+
//中小题空的个数
|
|
135
|
+
retStem.sqBlankCount = Object.values(stemElement.querySelectorAll("."+QML_BK)).filter(function (element) {
|
|
136
|
+
return element.hasAttribute(SQ)
|
|
137
|
+
}).length;
|
|
138
|
+
//0=小题,1=小问
|
|
139
|
+
retStem.sqIdMode = Object.values(stemElement.querySelectorAll("*")).filter(function (element) {
|
|
140
|
+
return element.hasAttribute(QML_ID_CONTAINER) && element.getAttribute(QML_ID_CONTAINER) === QML_ID_CONTAINER_VAL
|
|
141
|
+
}).length > 0 ? 1 : 0;
|
|
142
|
+
//修复下小题中的字段
|
|
143
|
+
retStem.sqs.forEach(function (element) {
|
|
144
|
+
element.sqIdMode = retStem.sqIdMode;
|
|
145
|
+
})
|
|
146
|
+
return retStem;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* 判断一个dom元素是否存在class属性
|
|
151
|
+
* @param element dom元素
|
|
152
|
+
* @param className class属性名称
|
|
153
|
+
*/
|
|
154
|
+
function hasClass(element, className) {
|
|
155
|
+
if (element == null || !element.classList) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
return element.classList.contains(className);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function getQuestionType(retStem) {
|
|
162
|
+
return retStem.sqs.length > 0
|
|
163
|
+
? "复合题" : retStem.og != null ? "选择题" : "填空题";
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function splitOg(ogElement) {
|
|
167
|
+
if (ogElement == null) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
var stemOg = {};
|
|
171
|
+
stemOg.ogOps = Object.values(ogElement.querySelectorAll("." + QML_OP)).map(function (element, index) {
|
|
172
|
+
return {"index": OP_INDEXES[index], "html": element.outerHTML};
|
|
173
|
+
})
|
|
174
|
+
// 设置一行显示选项个数
|
|
175
|
+
stemOg.cols = ogElement.children[0] instanceof HTMLTableElement
|
|
176
|
+
? ogElement.children[0].querySelector("tbody>tr").querySelectorAll("td").length
|
|
177
|
+
: ogElement.querySelectorAll(".qml-op").length;
|
|
178
|
+
return stemOg;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function splitAnswer(answerElement) {
|
|
182
|
+
if (answerElement == null) {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
var answerElements = Object.values(answerElement.body.childNodes)
|
|
186
|
+
.filter(function (node) {
|
|
187
|
+
return hasClass(node, QML_ANSWER)
|
|
188
|
+
});
|
|
189
|
+
if (answerElements == null || answerElements.length !== 1) {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
"anSqs": Object.values(answerElements[0].childNodes).filter(function (element) {
|
|
194
|
+
return hasClass(element, QML_AN_SQ);
|
|
195
|
+
}).map(function (element) {
|
|
196
|
+
return {
|
|
197
|
+
"ans": Object.values(element.childNodes).filter(function (element) {
|
|
198
|
+
return hasClass(element, QML_AN);
|
|
199
|
+
}).map(function (element) {
|
|
200
|
+
return {
|
|
201
|
+
"html": hasClass(element, QML_ISOP) ? element.innerHTML : element.outerHTML,
|
|
202
|
+
"op": hasClass(element, QML_ISOP),
|
|
203
|
+
"exact": hasClass(element, QML_EXACT) || hasClass(element, QML_ISOP)
|
|
204
|
+
};
|
|
205
|
+
})
|
|
206
|
+
};
|
|
207
|
+
})
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function splitExplanation(explanationElement) {
|
|
212
|
+
if (explanationElement == null) {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
var explanationElements = Object.values(explanationElement.body.childNodes)
|
|
216
|
+
.filter(function (node) {
|
|
217
|
+
return hasClass(node, QML_EXPLANATION)
|
|
218
|
+
});
|
|
219
|
+
if (explanationElements == null || explanationElements.length !== 1) {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
//小题编号
|
|
223
|
+
var sqIndex = 0;
|
|
224
|
+
return {
|
|
225
|
+
"explanationSegs": Object.values(explanationElements[0].childNodes).filter(function (element) {
|
|
226
|
+
return hasClass(element, QML_SEG) || hasClass(element, QML_EXPS_SQ);
|
|
227
|
+
}).map(function (element) {
|
|
228
|
+
return {
|
|
229
|
+
"name": element.hasAttribute(SEG_NAME) ? element.getAttribute(SEG_NAME) : "(" + (++sqIndex) + ")" + SQ_DETAIL,
|
|
230
|
+
"html": Object.values(element.children).map(function (element) {
|
|
231
|
+
return element.outerHTML
|
|
232
|
+
}).join("")
|
|
233
|
+
};
|
|
234
|
+
})
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
/**
|
|
240
|
+
* 拆分题干
|
|
241
|
+
* @param stem
|
|
242
|
+
*/
|
|
243
|
+
splitStem: function (stem) {
|
|
244
|
+
var doc = htmlResolve(stem);
|
|
245
|
+
if (doc == null) {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
var stemElements = Object.values(doc.body.childNodes)
|
|
249
|
+
.filter(function (node) {
|
|
250
|
+
return hasClass(node, QML_STEM)
|
|
251
|
+
});
|
|
252
|
+
if (stemElements == null || stemElements.length !== 1) {
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
return splitStem(stemElements[0]);
|
|
256
|
+
},
|
|
257
|
+
/**
|
|
258
|
+
* 拆分答案
|
|
259
|
+
* @param answer
|
|
260
|
+
*/
|
|
261
|
+
splitAnswer: function (answer) {
|
|
262
|
+
var doc = htmlResolve(answer);
|
|
263
|
+
if (doc == null) {
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
return splitAnswer(doc);
|
|
267
|
+
},
|
|
268
|
+
/**
|
|
269
|
+
* 拆分解析
|
|
270
|
+
* @param explanation
|
|
271
|
+
*/
|
|
272
|
+
splitExplanation: function (explanation) {
|
|
273
|
+
var doc = htmlResolve(explanation);
|
|
274
|
+
if (doc == null) {
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
return splitExplanation(doc);
|
|
278
|
+
},
|
|
279
|
+
/**
|
|
280
|
+
* 拆分试题
|
|
281
|
+
* @param stem
|
|
282
|
+
* @param answer
|
|
283
|
+
* @param explanation
|
|
284
|
+
*/
|
|
285
|
+
splitQuestion: function (stem, answer, explanation) {
|
|
286
|
+
var stemDocument = htmlResolve(stem);
|
|
287
|
+
var answerDocument = htmlResolve(answer);
|
|
288
|
+
var explanationDocument = htmlResolve(explanation);
|
|
289
|
+
var question = {};
|
|
290
|
+
question.stem = splitStem(stemDocument);
|
|
291
|
+
question.answer = splitAnswer(answerDocument);
|
|
292
|
+
question.explanation = splitExplanation(explanationDocument);
|
|
293
|
+
return question;
|
|
294
|
+
},
|
|
295
|
+
};
|
|
296
|
+
}
|