|
@@ -1,3 +1,4 @@
|
|
|
|
|
+import { lessonDateLabel } from '../appointment/lesson-display';
|
|
|
import { TeacherTodayReviewService } from '../../core/teacher-today-review.service';
|
|
import { TeacherTodayReviewService } from '../../core/teacher-today-review.service';
|
|
|
import { lessonStudySources } from '../appointment/lesson-study-sources';
|
|
import { lessonStudySources } from '../appointment/lesson-study-sources';
|
|
|
import { DropdownDirective } from '../../shared/dropdown.directive';
|
|
import { DropdownDirective } from '../../shared/dropdown.directive';
|
|
@@ -173,6 +174,20 @@ export class LearningPageComponent implements OnInit, OnDestroy {
|
|
|
readonly examStarted = signal(false);
|
|
readonly examStarted = signal(false);
|
|
|
readonly examSubmitted = signal(false);
|
|
readonly examSubmitted = signal(false);
|
|
|
readonly askInfo = signal<SourceItem>({});
|
|
readonly askInfo = signal<SourceItem>({});
|
|
|
|
|
+ readonly examHistory = signal<SourceItem[]>([]);
|
|
|
|
|
+ readonly examHistoryLoading = signal(false);
|
|
|
|
|
+ readonly examHistoryError = signal('');
|
|
|
|
|
+ readonly examHistoryMore = signal(false);
|
|
|
|
|
+ readonly examCompareIds = signal<string[]>([]);
|
|
|
|
|
+ readonly comparedExams = computed(() => this.examHistory().filter(r => this.examCompareIds().includes(String(r['GeneralID']))));
|
|
|
|
|
+ readonly examSaveError = signal('');
|
|
|
|
|
+ readonly examDateLabel = lessonDateLabel;
|
|
|
|
|
+ private examHistoryPage = 0;
|
|
|
|
|
+ private examHistoryRequest?: Subscription;
|
|
|
|
|
+ private examLoadRequest?: Subscription;
|
|
|
|
|
+ private examAttemptStamp = '';
|
|
|
|
|
+ private examLogKey = '';
|
|
|
|
|
+ private examAnswerId: string | number | null = null;
|
|
|
readonly previousExam = signal<SourceItem>({});
|
|
readonly previousExam = signal<SourceItem>({});
|
|
|
readonly questions = signal<ExamQuestion[]>([]);
|
|
readonly questions = signal<ExamQuestion[]>([]);
|
|
|
readonly answers = signal<ExamAnswer[]>([]);
|
|
readonly answers = signal<ExamAnswer[]>([]);
|
|
@@ -321,6 +336,7 @@ export class LearningPageComponent implements OnInit, OnDestroy {
|
|
|
this.choiceOptionsVersion++;
|
|
this.choiceOptionsVersion++;
|
|
|
this.choiceOptionsRequest?.unsubscribe();
|
|
this.choiceOptionsRequest?.unsubscribe();
|
|
|
this.stopExamTimer();
|
|
this.stopExamTimer();
|
|
|
|
|
+ this.examHistoryRequest?.unsubscribe();this.examLoadRequest?.unsubscribe();
|
|
|
this.platform.stopAudio();
|
|
this.platform.stopAudio();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1115,7 +1131,9 @@ export class LearningPageComponent implements OnInit, OnDestroy {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
startExam(): void {
|
|
startExam(): void {
|
|
|
- if (!this.questions().length) return;
|
|
|
|
|
|
|
+ if (!this.questions().length || this.loading() || this.submitting()) return;
|
|
|
|
|
+ this.examAttemptStamp=new Date().toISOString();this.examLogKey=crypto.randomUUID();this.examAnswerId=null;this.examSaveError.set('');
|
|
|
|
|
+ this.answers.set(this.questions().map(q=>({qid:q.ID,qtype:String(q.QType??''),answer:'',result:2})));
|
|
|
this.examStarted.set(true);
|
|
this.examStarted.set(true);
|
|
|
this.examSubmitted.set(false);
|
|
this.examSubmitted.set(false);
|
|
|
this.currentIndex.set(0);
|
|
this.currentIndex.set(0);
|
|
@@ -1123,6 +1141,7 @@ export class LearningPageComponent implements OnInit, OnDestroy {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
answerExam(option: ExamOption, optionIndex: number): void {
|
|
answerExam(option: ExamOption, optionIndex: number): void {
|
|
|
|
|
+ if(this.submitting()||this.examSaveError()||this.examSubmitted())return;
|
|
|
const question = this.currentQuestion();
|
|
const question = this.currentQuestion();
|
|
|
if (!question) return;
|
|
if (!question) return;
|
|
|
const options = question.QOption.map((item, index) => ({ ...item, checked: index === optionIndex }));
|
|
const options = question.QOption.map((item, index) => ({ ...item, checked: index === optionIndex }));
|
|
@@ -1133,6 +1152,7 @@ export class LearningPageComponent implements OnInit, OnDestroy {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
skipExamQuestion(): void {
|
|
skipExamQuestion(): void {
|
|
|
|
|
+ if(this.submitting()||this.examSaveError()||this.examSubmitted())return;
|
|
|
const question = this.currentQuestion();
|
|
const question = this.currentQuestion();
|
|
|
if (!question) return;
|
|
if (!question) return;
|
|
|
this.setExamAnswer({ qid: question.ID, qtype: String(question.QType ?? ''), answer: '', result: 2 });
|
|
this.setExamAnswer({ qid: question.ID, qtype: String(question.QType ?? ''), answer: '', result: 2 });
|
|
@@ -1140,7 +1160,9 @@ export class LearningPageComponent implements OnInit, OnDestroy {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
submitExam(): void {
|
|
submitExam(): void {
|
|
|
- if(this.submitting())return;
|
|
|
|
|
|
|
+ if(this.submitting()||this.examSubmitted())return;
|
|
|
|
|
+ this.examSaveError.set('');
|
|
|
|
|
+ if(this.examAnswerId!==null){this.submitting.set(true);this.saveExamLog(this.examAnswerId);return;}
|
|
|
this.stopExamTimer();
|
|
this.stopExamTimer();
|
|
|
const ask = this.askInfo();
|
|
const ask = this.askInfo();
|
|
|
const user = this.session.user();
|
|
const user = this.session.user();
|
|
@@ -1153,14 +1175,13 @@ export class LearningPageComponent implements OnInit, OnDestroy {
|
|
|
Zstatus: 0,
|
|
Zstatus: 0,
|
|
|
Source: '',
|
|
Source: '',
|
|
|
UserID: user?.userId,
|
|
UserID: user?.userId,
|
|
|
- CDate: this.dateTime(true),
|
|
|
|
|
|
|
+ CDate: this.examAttemptStamp,
|
|
|
};
|
|
};
|
|
|
this.submitting.set(true);
|
|
this.submitting.set(true);
|
|
|
this.api.post<string | number>('vote_add', { mu: JSON.stringify(mu) }).subscribe({
|
|
this.api.post<string | number>('vote_add', { mu: JSON.stringify(mu) }).subscribe({
|
|
|
- next: ({ result }) => this.saveExamLog(result),
|
|
|
|
|
|
|
+ next: ({ result }) => {this.examAnswerId=result;this.saveExamLog(result);},
|
|
|
error: (error: ApiError) => {
|
|
error: (error: ApiError) => {
|
|
|
- this.submitting.set(false);
|
|
|
|
|
- this.platform.toast(error.message, 'error');
|
|
|
|
|
|
|
+ this.submitting.set(false);this.examSaveError.set(error.message);
|
|
|
},
|
|
},
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
@@ -1862,25 +1883,31 @@ export class LearningPageComponent implements OnInit, OnDestroy {
|
|
|
}).subscribe({ error: () => undefined });
|
|
}).subscribe({ error: () => undefined });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ loadExamHistory(more = false): void {
|
|
|
|
|
+ if(more && this.examHistoryLoading())return;
|
|
|
|
|
+ this.examHistoryRequest?.unsubscribe();this.examHistoryLoading.set(true);this.examHistoryError.set('');
|
|
|
|
|
+ const page=more?this.examHistoryPage+1:1;
|
|
|
|
|
+ this.examHistoryRequest=this.api.get<{items:SourceItem[];hasMore:boolean}>('app_exam_history',{page}).pipe(finalize(()=>this.examHistoryLoading.set(false))).subscribe({
|
|
|
|
|
+ next:({result})=>{
|
|
|
|
|
+ if(!result || !Array.isArray(result.items)){this.examHistoryError.set('测试记录返回异常,请重试');return;}
|
|
|
|
|
+ this.examHistoryPage=page;this.examHistoryMore.set(result.hasMore);
|
|
|
|
|
+ this.examHistory.set(more?[...this.examHistory(),...result.items]:result.items);
|
|
|
|
|
+ if(!more){this.previousExam.set(result.items.find(r=>r['resultAvailable'])||{});this.examCompareIds.set(result.items.filter(r=>r['resultAvailable']).slice(0,2).map(r=>String(r['GeneralID'])));}
|
|
|
|
|
+ },error:(e:ApiError)=>this.examHistoryError.set(e.message),
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ toggleExamComparison(item:SourceItem):void {
|
|
|
|
|
+ const id=String(item['GeneralID']);if(!item['resultAvailable'])return;
|
|
|
|
|
+ this.examCompareIds.update(ids=>ids.includes(id)?ids.filter(x=>x!==id):[...ids.slice(-1),id]);
|
|
|
|
|
+ }
|
|
|
private loadExam(): void {
|
|
private loadExam(): void {
|
|
|
- this.turnAskId();
|
|
|
|
|
|
|
+ this.stopExamTimer();this.examLoadRequest?.unsubscribe();this.examStarted.set(false);this.examSubmitted.set(false);this.examSaveError.set('');
|
|
|
|
|
+ this.loading.set(true);this.error.set('');this.questions.set([]);this.turnAskId();
|
|
|
const askId = Number(this.storageGet('askId') ?? 1);
|
|
const askId = Number(this.storageGet('askId') ?? 1);
|
|
|
- const user = this.session.user();
|
|
|
|
|
- this.api.get<unknown>('vote_ask', { id: askId }).subscribe({
|
|
|
|
|
- next: ({ result }) => this.askInfo.set(this.findList(result)[0] ?? {}),
|
|
|
|
|
- error: () => undefined,
|
|
|
|
|
- });
|
|
|
|
|
- this.api.get<unknown>('content_list', { nid: 389, modelId: 61, cpage: 1, psize: 2, myfield2: `UserID=${user?.userId}`, orders: 'CreateTime=desc' }).subscribe({
|
|
|
|
|
- next: ({ result }) => this.previousExam.set(this.findList(result)[0] ?? {}),
|
|
|
|
|
- error: () => undefined,
|
|
|
|
|
- });
|
|
|
|
|
- this.api.get<unknown>('vote_question', { askid: askId }).pipe(finalize(() => this.loading.set(false))).subscribe({
|
|
|
|
|
- next: ({ result }) => {
|
|
|
|
|
- const questions = this.shuffle(this.findList(result).map((item) => this.toQuestion(item)));
|
|
|
|
|
- this.questions.set(questions);
|
|
|
|
|
- this.answers.set(questions.map((question) => ({ qid: question.ID, qtype: String(question.QType ?? ''), answer: '', result: 2 })));
|
|
|
|
|
- },
|
|
|
|
|
- error: (error: ApiError) => this.error.set(error.message),
|
|
|
|
|
|
|
+ this.loadExamHistory();
|
|
|
|
|
+ this.examLoadRequest=forkJoin({ask:this.api.get<unknown>('vote_ask',{id:askId}),questions:this.api.get<unknown>('vote_question',{askid:askId})}).pipe(finalize(()=>this.loading.set(false))).subscribe({
|
|
|
|
|
+ next:({ask,questions})=>{this.askInfo.set(this.findList(ask.result)[0]??{});const items=this.shuffle(this.findList(questions.result).map(item=>this.toQuestion(item)));this.questions.set(items);this.answers.set(items.map(q=>({qid:q.ID,qtype:String(q.QType??''),answer:'',result:2})));},
|
|
|
|
|
+ error:(e:ApiError)=>this.error.set(e.message),
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1964,18 +1991,13 @@ export class LearningPageComponent implements OnInit, OnDestroy {
|
|
|
df: score,
|
|
df: score,
|
|
|
UserID: user?.userId,
|
|
UserID: user?.userId,
|
|
|
};
|
|
};
|
|
|
- let action = 'content_add';
|
|
|
|
|
- if (previous['GeneralID']) {
|
|
|
|
|
- content['GeneralID'] = previous['GeneralID'];
|
|
|
|
|
- action = 'content_update';
|
|
|
|
|
- }
|
|
|
|
|
- this.api.post(action, { content: JSON.stringify(content), addon: JSON.stringify(addon) }).pipe(finalize(() => this.submitting.set(false))).subscribe({
|
|
|
|
|
|
|
+ this.api.post('content_add', { requestKey:this.examLogKey,content: JSON.stringify(content), addon: JSON.stringify(addon) }).pipe(finalize(() => this.submitting.set(false))).subscribe({
|
|
|
next: () => {
|
|
next: () => {
|
|
|
this.examStarted.set(false);
|
|
this.examStarted.set(false);
|
|
|
this.examSubmitted.set(true);
|
|
this.examSubmitted.set(true);
|
|
|
- this.platform.toast('答题结果已保存', 'success');
|
|
|
|
|
|
|
+ this.platform.toast('答题结果已保存', 'success');this.loadExamHistory();
|
|
|
},
|
|
},
|
|
|
- error: (error: ApiError) => this.platform.toast(error.message, 'error'),
|
|
|
|
|
|
|
+ error: (error: ApiError) => this.examSaveError.set(error.message),
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
|