|
|
@@ -11,7 +11,7 @@ import { Question } from "@/types/study";
|
|
|
export const decodeHtmlEntities = (str: string): string => {
|
|
|
if (!str) return str;
|
|
|
|
|
|
- // 音标和常用 HTML 实体映射表
|
|
|
+ // 标准 HTML 实体映射表
|
|
|
const entityMap: Record<string, string> = {
|
|
|
// 音标相关 - 锐音符 (acute)
|
|
|
'aacute': 'á',
|
|
|
@@ -60,23 +60,45 @@ export const decodeHtmlEntities = (str: string): string => {
|
|
|
'plusmn': '±',
|
|
|
};
|
|
|
|
|
|
- // 处理命名实体(如 í)
|
|
|
- // 使用 [a-z0-9] 以支持包含数字的实体名称
|
|
|
- let result = str.replace(/&([a-z0-9]+);/gi, (match, entity) => {
|
|
|
+ let result = str;
|
|
|
+
|
|
|
+ // 1. 处理非标准的上标实体:&sup数字; → <sup>数字</sup>
|
|
|
+ // 例如:² → <sup>2</sup>, ¹23; → <sup>123</sup>
|
|
|
+ result = result.replace(/&sup(\d+);/gi, (match, digits) => {
|
|
|
+ return `<sup>${digits}</sup>`;
|
|
|
+ });
|
|
|
+
|
|
|
+ // 2. 处理非标准的下标实体:&sub数字; → <sub>数字</sub>
|
|
|
+ // 例如:&sub2; → <sub>2</sub>, &sub123; → <sub>123</sub>
|
|
|
+ result = result.replace(/&sub(\d+);/gi, (match, digits) => {
|
|
|
+ return `<sub>${digits}</sub>`;
|
|
|
+ });
|
|
|
+
|
|
|
+ // 3. 处理标准命名实体(如 í)
|
|
|
+ result = result.replace(/&([a-z]+);/gi, (match, entity) => {
|
|
|
const lowerEntity = entity.toLowerCase();
|
|
|
if (entityMap[lowerEntity]) {
|
|
|
return entityMap[lowerEntity];
|
|
|
}
|
|
|
- return match; // 如果找不到映射,保持原样
|
|
|
+ return match;
|
|
|
});
|
|
|
|
|
|
- // 处理数字实体(如 í 或 í)
|
|
|
+ // 4. 处理十进制数字实体(如 ² → ²)
|
|
|
result = result.replace(/&#(\d+);/g, (match, num) => {
|
|
|
- return String.fromCharCode(parseInt(num, 10));
|
|
|
+ const code = parseInt(num, 10);
|
|
|
+ if (!isNaN(code)) {
|
|
|
+ return String.fromCharCode(code);
|
|
|
+ }
|
|
|
+ return match;
|
|
|
});
|
|
|
|
|
|
+ // 5. 处理十六进制数字实体(如 ² → ²)
|
|
|
result = result.replace(/&#x([0-9a-f]+);/gi, (match, hex) => {
|
|
|
- return String.fromCharCode(parseInt(hex, 16));
|
|
|
+ const code = parseInt(hex, 16);
|
|
|
+ if (!isNaN(code)) {
|
|
|
+ return String.fromCharCode(code);
|
|
|
+ }
|
|
|
+ return match;
|
|
|
});
|
|
|
|
|
|
return result;
|
|
|
@@ -569,7 +591,8 @@ export const useExam = () => {
|
|
|
answers: item.answers || [],
|
|
|
subQuestions: item.subQuestions?.map(transerQuestion) || [],
|
|
|
options: item.options?.map((option, index) => {
|
|
|
- // 移除选项编号(如 A.)并解码 HTML 实体(如 í → í)
|
|
|
+ // 移除选项编号(如 A.)并解码 HTML 实体为 Unicode 字符
|
|
|
+ // 必须在这里解码,因为 Vue/uni-app 会对动态属性中的 & 进行转义
|
|
|
const cleanedOption = option.replace(/[A-Z]\./g, '').replace(/\s/g, ' ');
|
|
|
return {
|
|
|
name: decodeHtmlEntities(cleanedOption),
|