| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703 |
- import { EnumQuestionType, EnumReviewMode } from "@/common/enum";
- import { getPaper } from '@/api/modules/study';
- import { Study } from "@/types";
- import { Question } from "@/types/study";
- /**
- * @description 解码 HTML 实体
- * 由于 uv-parse 的 decodeEntity 只支持有限的实体,需要手动解码音标等特殊实体
- * 使用手动映射表,兼容所有 uni-app 平台(包括小程序)
- */
- export const decodeHtmlEntities = (str: string): string => {
- if (!str) return str;
- // 音标和常用 HTML 实体映射表
- const entityMap: Record<string, string> = {
- // 音标相关 - 锐音符 (acute)
- 'aacute': 'á',
- 'eacute': 'é',
- 'iacute': 'í',
- 'oacute': 'ó',
- 'uacute': 'ú',
- // 音标相关 - 重音符 (grave)
- 'agrave': 'à',
- 'egrave': 'è',
- 'igrave': 'ì',
- 'ograve': 'ò',
- 'ugrave': 'ù',
- // 音标相关 - 扬抑符 (circumflex)
- 'acirc': 'â',
- 'ecirc': 'ê',
- 'icirc': 'î',
- 'ocirc': 'ô',
- 'ucirc': 'û',
- // 音标相关 - 分音符 (umlaut/diaeresis)
- 'auml': 'ä',
- 'euml': 'ë',
- 'iuml': 'ï',
- 'ouml': 'ö',
- 'uuml': 'ü',
- // 音标相关 - 波浪符 (tilde)
- 'ntilde': 'ñ',
- 'atilde': 'ã',
- 'otilde': 'õ',
- // 其他常用实体
- 'amp': '&',
- 'lt': '<',
- 'gt': '>',
- 'quot': '"',
- 'apos': "'",
- 'nbsp': '\u00A0',
- 'copy': '©',
- 'reg': '®',
- 'trade': '™',
- 'mdash': '—',
- 'ndash': '–',
- 'hellip': '…',
- // 数学符号
- 'times': '×',
- 'divide': '÷',
- 'plusmn': '±',
- };
- // 处理命名实体(如 í)
- // 使用 [a-z0-9] 以支持包含数字的实体名称
- let result = str.replace(/&([a-z0-9]+);/gi, (match, entity) => {
- const lowerEntity = entity.toLowerCase();
- if (entityMap[lowerEntity]) {
- return entityMap[lowerEntity];
- }
- return match; // 如果找不到映射,保持原样
- });
- // 处理数字实体(如 í 或 í)
- result = result.replace(/&#(\d+);/g, (match, num) => {
- return String.fromCharCode(parseInt(num, 10));
- });
- result = result.replace(/&#x([0-9a-f]+);/gi, (match, hex) => {
- return String.fromCharCode(parseInt(hex, 16));
- });
- return result;
- }
- export const useExam = () => {
- const questionTypeDesc: Record<EnumQuestionType, string> = {
- [EnumQuestionType.SINGLE_CHOICE]: '单选题',
- [EnumQuestionType.MULTIPLE_CHOICE]: '多选题',
- [EnumQuestionType.JUDGMENT]: '判断题',
- [EnumQuestionType.FILL_IN_THE_BLANK]: '填空题',
- [EnumQuestionType.SUBJECTIVE]: '主观题',
- [EnumQuestionType.SHORT_ANSWER]: '简答题',
- [EnumQuestionType.ESSAY]: '问答题',
- [EnumQuestionType.ANALYSIS]: '分析题',
- [EnumQuestionType.OTHER]: '阅读题'
- }
- // 题型顺序
- const questionTypeOrder = [
- EnumQuestionType.SINGLE_CHOICE,
- EnumQuestionType.MULTIPLE_CHOICE,
- EnumQuestionType.JUDGMENT,
- EnumQuestionType.FILL_IN_THE_BLANK,
- EnumQuestionType.SUBJECTIVE,
- EnumQuestionType.SHORT_ANSWER,
- EnumQuestionType.ESSAY,
- EnumQuestionType.ANALYSIS,
- EnumQuestionType.OTHER
- ];
- let interval: NodeJS.Timeout | null = null;
- let animationFrameId: number | null = null; // requestAnimationFrame 的 ID
- let countStart: number = 0;
- let countTime: number = 0;
- const countDownCallback = ref<() => void>(() => { });
- // 练习计时相关变量
- let practiceStartTime: number = 0; // 练习开始时间戳(毫秒)
- let practiceAccumulatedTime: number = 0; // 累计的练习时间(秒)
- // 练习时长
- const practiceDuration = ref<number>(0);
- const formatPracticeDuration = computed(() => {
- const hours = Math.floor(practiceDuration.value / 3600);
- const minutes = Math.floor((practiceDuration.value % 3600) / 60);
- const seconds = practiceDuration.value % 60;
- if (hours > 0) {
- return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
- } else {
- return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
- }
- });
- // 考试时长
- const examDuration = ref<number>(0);
- const formatExamDuration = computed(() => {
- const hours = Math.floor(examDuration.value / 3600);
- const minutes = Math.floor((examDuration.value % 3600) / 60);
- const seconds = examDuration.value % 60;
- return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
- });
- const swiperDuration = ref<number>(300);
- const questionList = ref<Study.Question[]>([]);
- // 练习设置
- const practiceSettings = ref<Study.PracticeSettings>({
- reviewMode: EnumReviewMode.AFTER_SUBMIT,
- autoNext: false
- });
- // 收藏列表
- const favoriteList = ref<Study.Question[]>([]);
- // 不会列表
- const notKnowList = ref<Study.Question[]>([]);
- // 重点标记列表
- const markList = ref<Study.Question[]>([]);
- /// 虚拟题目索引,包含子题
- const virtualCurrentIndex = ref<number>(0);
- /// 虚拟总题量,包含子题
- const virtualTotalCount = computed(() => {
- return questionList.value.reduce((acc, item) => {
- if (item.subQuestions && item.subQuestions.length > 0) {
- return acc + item.subQuestions.length;
- }
- return acc + 1;
- }, 0);
- });
- // 包含状态的问题列表
- const stateQuestionList = computed(() => {
- const parseQuestion = (qs: Study.Question) => {
- if (qs.subQuestions && qs.subQuestions.length > 0) {
- qs.isLeaf = false;
- qs.subQuestions.forEach((subQs, index) => {
- subQs.isDone = isDone(subQs);
- subQs.isCorrect = isQuestionCorrect(subQs);
- subQs.isNotAnswer = isQuestionNotAnswer(subQs);
- subQs.isLeaf = true;
- subQs.options.forEach(option => {
- option.isCorrect = isOptionCorrect(subQs, option);
- option.isSelected = isOptionSelected(subQs, option);
- option.isMissed = !option.isSelected && option.isCorrect;
- option.isIncorrect = !option.isCorrect && option.isSelected;
- });
- });
- } else {
- qs.isSubQuestion = false;
- qs.isDone = isDone(qs);
- qs.isCorrect = isQuestionCorrect(qs);
- qs.isNotAnswer = isQuestionNotAnswer(qs);
- qs.isLeaf = true;
- qs.options.forEach(option => {
- option.isCorrect = isOptionCorrect(qs, option);
- option.isSelected = isOptionSelected(qs, option);
- option.isMissed = !option.isSelected && option.isCorrect;
- option.isIncorrect = !option.isCorrect && option.isSelected;
- });
- }
- return qs;
- }
- return questionList.value.map((item, index) => {
- return parseQuestion(item)
- });
- });
- /// 扁平化题目列表,用于答题卡
- const flatQuestionList = computed(() => {
- return stateQuestionList.value.flatMap(item => {
- if (item.subQuestions && item.subQuestions.length > 0) {
- return item.subQuestions.flat();
- }
- return item;
- });
- });
- /// 按照题型分组,用于答题卡
- const groupedQuestionList = computed(() => {
- const state = questionTypeOrder.map(type => {
- return {
- type,
- list: [] as {
- question: Study.Question;
- index: number
- }[]
- }
- });
- flatQuestionList.value.forEach((qs, index) => {
- let group;
- if (qs.isSubQuestion) {
- group = state.find(item => item.type === qs.parentTypeId);
- } else {
- group = state.find(item => item.type === qs.typeId);
- }
- if (group) {
- group.list.push({
- question: qs,
- index
- });
- } else {
- state.push({
- type: qs.typeId,
- list: [{
- question: qs,
- index
- }]
- });
- }
- });
- return state;
- });
- /// 是否全部做完
- const isAllDone = computed(() => {
- return doneCount.value === virtualTotalCount.value;
- });
- // 当前下标
- const currentIndex = ref<number>(0);
- // 子题下标
- const subQuestionIndex = ref<number>(0);
- // 当前题目
- const currentQuestion = computed(() => {
- return questionList.value[currentIndex.value];
- });
- // 当前子题
- const currentSubQuestion = computed(() => {
- if (currentQuestion.value.subQuestions.length === 0) {
- return null;
- }
- if (subQuestionIndex.value >= currentQuestion.value.subQuestions.length) {
- return null;
- }
- // return currentQuestion.value.subQuestions[subQuestionIndex.value];
- return currentQuestion.value.subQuestions[currentQuestion.value.activeSubIndex];
- });
- /// 总题量,不区分子题,等同于接口返回的题目列表
- const totalCount = computed(() => {
- return questionList.value.length;
- });
- /// 已做题的数量 --> stateQuestionList
- const doneCount = computed(() => {
- // 有答案的或者不会做的,都认为是做了
- let count = 0;
- for (let i = 0; i <= stateQuestionList.value.length - 1; i++) {
- const qs = stateQuestionList.value[i];
- if (qs.subQuestions && qs.subQuestions.length > 0) {
- qs.subQuestions.forEach(subQs => {
- if (subQs.isDone) {
- count++;
- }
- });
- } else {
- if (qs.isDone) {
- count++;
- }
- }
- }
- return count;
- });
- /// 未做题的数量
- const notDoneCount = computed(() => {
- return virtualTotalCount.value - doneCount.value;
- });
- watch(() => virtualCurrentIndex.value, (newVal, oldVal) => {
- updateQuestionDuration(oldVal);
- });
- // 更新单个题目做题时间
- const updateQuestionDuration = (index: number, continueCount = true) => {
- const question = flatQuestionList.value[index];
- const time = stopCount();
- question.duration += time;
- // 每次结算后都清空累计时长,避免多次提交或多次 stop 导致重复累加
- clearCount();
- if (continueCount) {
- startCount();
- }
- }
- /// 包含子题的题目计算整体做题进度
- const calcProgress = (qs: Study.Question): number => {
- if (qs.subQuestions && qs.subQuestions.length > 0) {
- return qs.subQuestions.reduce((acc, q) => acc + calcProgress(q), 0) / qs.subQuestions.length;
- }
- return qs.isDone ? 100 : 0;
- }
- /// 题目是否做完
- const isDone = (qs: Study.Question): boolean => {
- if (qs.subQuestions && qs.subQuestions.length > 0) {
- return qs.subQuestions.every(q => isDone(q));
- }
- return (qs.answers && qs.answers.filter(item => !!item).length > 0) || !!qs.isNotKnow;
- }
- /// 题目是否正确
- const isQuestionCorrect = (qs: Study.Question): boolean => {
- let { answers, answer1, answer2, typeId } = qs;
- answers = answers?.filter(item => !!item) || [];
- answer1 = answer1 || '';
- answer2 = answer2 || '';
- if ([EnumQuestionType.SINGLE_CHOICE, EnumQuestionType.JUDGMENT].includes(typeId)) {
- return answer1.includes(answers[0]);
- } else if ([EnumQuestionType.MULTIPLE_CHOICE].includes(typeId)) {
- return answers.length === answer1.length && answers.every(item => answer1.includes(item));
- } else {
- // 主观题 A 对 B 错
- return answers.includes('A') && !answers.includes('B');
- }
- };
- /// 题目是否未作答
- const isQuestionNotAnswer = (qs: Study.Question): boolean => {
- if (qs.subQuestions && qs.subQuestions.length > 0) {
- return qs.subQuestions.every(q => isQuestionNotAnswer(q));
- }
- return !qs.answers || qs.answers.filter(item => !!item).length === 0;
- }
- /// 选项是否正确
- const isOptionCorrect = (question: Study.Question, option: Study.QuestionOption) => {
- const { answers, answer1, typeId } = question;
- if ([EnumQuestionType.SINGLE_CHOICE, EnumQuestionType.JUDGMENT].includes(typeId)) {
- return answer1?.includes(option.no);
- } else if ([EnumQuestionType.MULTIPLE_CHOICE].includes(typeId)) {
- return answer1?.includes(option.no);
- } else {
- return answers?.includes(option.no) && option.no === 'A';
- }
- }
- /// 选项是否选中
- const isOptionSelected = (question: Study.Question, option: Study.QuestionOption) => {
- return question.answers.includes(option.no);
- }
- // 是否可以切换上一题
- const prevEnable = computed(() => {
- return currentIndex.value > 0 || currentQuestion.value.activeSubIndex > 0;
- });
- // 是否可以切换下一题
- const nextEnable = computed(() => {
- return currentIndex.value < questionList.value.length - 1 || currentQuestion.value.activeSubIndex < currentQuestion.value.subQuestions.length - 1;
- });
- // 下一题
- const nextQuestion = () => {
- if (!nextEnable.value) {
- return;
- }
- if (currentQuestion.value.activeSubIndex < currentQuestion.value.subQuestions.length - 1) {
- currentQuestion.value.activeSubIndex++;
- } else {
- currentIndex.value++;
- }
- }
- // 上一题
- const prevQuestion = () => {
- if (!prevEnable.value) {
- return;
- }
- if (currentQuestion.value.subQuestions && currentQuestion.value.subQuestions.length > 0) {
- if (currentQuestion.value.activeSubIndex > 0) {
- currentQuestion.value.activeSubIndex--;
- } else {
- currentIndex.value--;
- }
- } else {
- if (currentIndex.value > 0) {
- currentIndex.value--;
- }
- }
- }
- // 快速下一题
- const nextQuestionQuickly = () => {
- if (!nextEnable) {
- return;
- }
- swiperDuration.value = 0;
- setTimeout(() => {
- nextQuestion();
- setTimeout(() => {
- swiperDuration.value = 300;
- }, 0);
- }, 0);
- }
- // 快速上一题
- const prevQuestionQuickly = () => {
- if (!prevEnable.value) {
- return;
- }
- swiperDuration.value = 0;
- setTimeout(() => {
- prevQuestion();
- setTimeout(() => {
- swiperDuration.value = 300;
- }, 0);
- }, 0);
- }
- // 通过下标切换题目
- const changeIndex = (index: number, subIndex?: number) => {
- swiperDuration.value = 0;
- setTimeout(() => {
- currentIndex.value = index;
- if (!subIndex !== undefined) {
- if (subIndex !== undefined) {
- questionList.value[index].activeSubIndex = subIndex || 0;
- }
- }
- setTimeout(() => {
- swiperDuration.value = 300;
- }, 0);
- }, 0);
- }
- // 开始计时
- const startTiming = () => {
- startCount();
-
- // 记录开始时间戳(毫秒)
- if (practiceStartTime === 0) {
- practiceStartTime = performance.now();
- }
-
- // 使用 requestAnimationFrame 更新显示,更流畅且性能更好
- const updatePracticeDuration = () => {
- if (practiceStartTime > 0) {
- // 计算实际经过的时间(秒)
- const elapsed = (performance.now() - practiceStartTime) / 1000;
- practiceDuration.value = Math.floor(practiceAccumulatedTime + elapsed);
- // 继续下一帧更新
- animationFrameId = requestAnimationFrame(updatePracticeDuration);
- }
- };
-
- // 开始动画帧循环
- animationFrameId = requestAnimationFrame(updatePracticeDuration);
- }
- // 停止计时
- const stopTiming = () => {
- stopCount();
-
- // 取消动画帧
- if (animationFrameId !== null) {
- cancelAnimationFrame(animationFrameId);
- animationFrameId = null;
- }
-
- // 如果正在计时,累加经过的时间
- if (practiceStartTime > 0) {
- const elapsed = (performance.now() - practiceStartTime) / 1000;
- practiceAccumulatedTime += elapsed;
- practiceDuration.value = Math.floor(practiceAccumulatedTime);
- practiceStartTime = 0;
- }
- }
- const startCount = () => {
- if (countStart === 0) {
- countStart = performance.now();
- }
- }
- const stopCount = () => {
- // 如果当前没有在计时(countStart 为 0),说明已经 stop 过了,直接返回累计时长,避免重复累加
- if (countStart === 0) {
- return countTime;
- }
- countTime += (performance.now() - countStart);
- countStart = 0;
- return countTime;
- }
- const clearCount = () => {
- countTime = 0;
- }
- // 开始倒计时
- const startCountdown = () => {
- interval = setInterval(() => {
- if (examDuration.value <= 0) {
- console.log('停止倒计时')
- stopExamDuration();
- return;
- }
- examDuration.value -= 1;
- }, 1000);
- }
- // 停止倒计时
- const stopExamDuration = () => {
- interval && clearInterval(interval);
- interval = null;
- countDownCallback.value && countDownCallback.value();
- }
- const setExamDuration = (duration: number) => {
- examDuration.value = duration;
- }
- const setPracticeDuration = (duration: number) => {
- practiceDuration.value = duration;
- practiceAccumulatedTime = duration;
- practiceStartTime = 0;
- }
- const setCountDownCallback = (callback: () => void) => {
- countDownCallback.value = callback;
- }
- const setDuration = (duration: number) => {
- examDuration.value = duration;
- practiceDuration.value = duration;
- practiceAccumulatedTime = duration;
- practiceStartTime = 0;
- }
- /// 整理题目结构
- const transerQuestions = (arr: Study.Question[]) => {
- let offset = 0;
- return arr.map((item: Study.Question, index: number) => {
- const result = {
- ...item,
- index: index
- };
- // 如果有子节点,处理子节点并计算subIndex
- if (item.subQuestions && Array.isArray(item.subQuestions) && item.subQuestions.length > 0) {
- // 为当前节点设置offset
- result.offset = offset;
- result.subQuestions = item.subQuestions.map((child, childIndex) => ({
- ...child,
- subIndex: childIndex,
- isSubQuestion: true,
- parentId: item.id,
- parentTypeId: item.typeId,
- parentIndex: index,
- index: index,
- virtualIndex: index + result.offset + childIndex
- }));
- // 更新offset,累加当前节点的子节点数量
- offset += (item.subQuestions.length - 1);
- } else {
- // 如果没有子节点,设置offset为当前累计值
- result.offset = offset;
- result.isSubQuestion = false;
- result.virtualIndex = result.index + offset;
- }
- return result;
- });
- }
- // 将ExamineeQuestion转为Question
- const setQuestionList = (list: Study.ExamineeQuestion[]) => {
- const orders = ['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'];
- // 数据预处理
- // 1、给每个项目补充额外字段
- const transerQuestion = (item: Study.ExamineeQuestion, index: number): Study.Question => {
- return {
- ...item,
- // 处理没有题型的大题,统一作为阅读题
- typeId: (item.typeId === null || item.typeId === undefined) ? EnumQuestionType.OTHER : item.typeId,
- answers: item.answers || [],
- subQuestions: item.subQuestions?.map(transerQuestion) || [],
- options: item.options?.map((option, index) => {
- // 移除选项编号(如 A.)并解码 HTML 实体(如 í → í)
- const cleanedOption = option.replace(/[A-Z]\./g, '').replace(/\s/g, ' ');
- return {
- name: decodeHtmlEntities(cleanedOption),
- no: orders[index],
- id: index,
- isAnswer: false,
- isCorrect: false,
- isSelected: false
- } as Study.QuestionOption
- }) || [],
- totalScore: item.totalScore || 0,
- offset: 0,
- index: index,
- virtualIndex: 0,
- duration: 0,
- activeSubIndex: 0,
- hasSubQuestions: item.subQuestions?.length > 0
- } as Study.Question
- }
- questionList.value = transerQuestions(list.map((item, index) => transerQuestion(item, index)));
- }
- /// 重置题目状态
- const reset = () => {
- questionList.value = questionList.value.map(item => {
- return {
- ...item,
- answers: [],
- isMark: false,
- isNotKnow: false,
- hasParsed: false,
- showParse: false,
- subQuestions: item.subQuestions.map(subItem => {
- return {
- ...subItem,
- answers: [],
- isMark: false,
- isNotKnow: false,
- hasParsed: false,
- showParse: false
- }
- }),
- options: item.options.map(option => {
- return {
- ...option,
- isAnswer: false,
- isCorrect: false,
- isSelected: false,
- isMissed: false,
- isIncorrect: false
- }
- })
- }
- });
- changeIndex(0);
- practiceDuration.value = 0;
- practiceAccumulatedTime = 0;
- practiceStartTime = 0;
- examDuration.value = 0;
- interval && clearInterval(interval);
- interval = null;
- if (animationFrameId !== null) {
- cancelAnimationFrame(animationFrameId);
- animationFrameId = null;
- }
- }
- /// 设置子题下标
- const setSubQuestionIndex = (index: number) => {
- currentQuestion.value.activeSubIndex = index;
- }
- // 切换阅卷模式
- const setPracticeSettings = (settings: Study.PracticeSettings) => {
- practiceSettings.value = settings;
- }
- const submit = () => {
- updateQuestionDuration(virtualCurrentIndex.value, false);
- }
- watch([() => currentIndex.value, () => currentQuestion.value?.activeSubIndex], (val) => {
- const qs = questionList.value[val[0]];
- virtualCurrentIndex.value = qs.index + qs.offset + val[1];
- console.log(virtualCurrentIndex.value, 777)
- }, {
- immediate: false
- });
- return {
- practiceSettings,
- questionList,
- groupedQuestionList,
- stateQuestionList,
- flatQuestionList,
- favoriteList,
- notKnowList,
- markList,
- currentIndex,
- isAllDone,
- totalCount,
- virtualCurrentIndex,
- virtualTotalCount,
- subQuestionIndex,
- setSubQuestionIndex,
- doneCount,
- notDoneCount,
- questionTypeDesc,
- currentSubQuestion,
- prevEnable,
- nextEnable,
- nextQuestion,
- prevQuestion,
- nextQuestionQuickly,
- prevQuestionQuickly,
- swiperDuration,
- practiceDuration,
- examDuration,
- formatExamDuration,
- formatPracticeDuration,
- startTiming,
- stopTiming,
- setDuration,
- startCountdown,
- stopExamDuration,
- setExamDuration,
- setPracticeDuration,
- setCountDownCallback,
- setQuestionList,
- changeIndex,
- reset,
- submit,
- isQuestionCorrect,
- isOptionCorrect,
- setPracticeSettings,
- }
- }
|