|
@@ -10,7 +10,9 @@
|
|
|
<SvgIcon icon-class="list" />
|
|
|
<span>{{ curQuestionIndex + 1 }} / {{ questionList.length }}</span>
|
|
|
</div>
|
|
|
- <div class="round primary"><SvgIcon icon-class="hourglass" />{{ secondFormatConversion(time) }}</div>
|
|
|
+ <div v-if="!isTeacherAnnotations" class="round primary">
|
|
|
+ <SvgIcon icon-class="hourglass" />{{ secondFormatConversion(time) }}
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</header>
|
|
|
|
|
@@ -39,27 +41,41 @@
|
|
|
|
|
|
<footer class="footer">
|
|
|
<el-popover v-model="isPopover" placement="top-start" trigger="click">
|
|
|
- <div class="annotations-container">
|
|
|
+ <div v-if="isEnable(remark.is_remarked) && !isTeacher" class="remark-container">
|
|
|
+ <div class="remark-info">
|
|
|
+ <el-avatar :size="24" :src="remark.remark_person_image_url" />
|
|
|
+ <span class="remark-name">{{ remark.remark_person_name }}</span>
|
|
|
+ <span class="remark-time">{{ remark.remark_time }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="remark">
|
|
|
+ {{ remark.remark }}
|
|
|
+ </div>
|
|
|
+ <div class="file">
|
|
|
+ <el-image v-for="{ file_url, file_id } in remark.file_list" :key="file_id" :src="file_url" fit="contain" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-else class="annotations-container">
|
|
|
<div class="title">增加批注</div>
|
|
|
<div class="score">
|
|
|
<span>分数</span>
|
|
|
- <el-input-number v-model="annotations.score" :min="0" :step="1" />
|
|
|
+ <el-input-number v-model="remark.score" :min="0" :step="1" />
|
|
|
</div>
|
|
|
- <el-input v-model="annotations.remark" type="textarea" rows="6" resize="none" class="remark" />
|
|
|
+ <el-input v-model="remark.remark" type="textarea" rows="6" resize="none" class="remark" />
|
|
|
<div>图片/视频</div>
|
|
|
|
|
|
- <el-upload
|
|
|
- action="no"
|
|
|
- accept="audio/*,video/*,image/*"
|
|
|
- :show-file-list="true"
|
|
|
- :http-request="upload"
|
|
|
- :on-remove="removeFile"
|
|
|
- >
|
|
|
+ <el-upload action="no" accept="video/*,image/*" :show-file-list="false" :http-request="upload">
|
|
|
<div class="upload">
|
|
|
<i class="el-icon-plus avatar-uploader-icon"></i>
|
|
|
<span>Upload</span>
|
|
|
</div>
|
|
|
</el-upload>
|
|
|
+ <ul class="file-list">
|
|
|
+ <li v-for="({ file_name, file_id }, i) in remark.file_list" :key="file_id" @click="removeFile(i)">
|
|
|
+ <span>{{ file_name }}</span>
|
|
|
+ <SvgIcon icon-class="delete" />
|
|
|
+ </li>
|
|
|
+ </ul>
|
|
|
|
|
|
<div class="popover-footer">
|
|
|
<el-button @click="isPopover = false">取消</el-button>
|
|
@@ -67,7 +83,16 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <div v-show="isAnnotations" slot="reference" class="annotations"><i class="el-icon-plus"></i>批注</div>
|
|
|
+ <div
|
|
|
+ v-show="isAnnotations"
|
|
|
+ slot="reference"
|
|
|
+ :class="['annotations', { has: isEnable(remark.is_remarked) && !isTeacher }]"
|
|
|
+ >
|
|
|
+ <template v-if="isEnable(remark.is_remarked) && !isTeacher">
|
|
|
+ <span>有一条教师批注</span>
|
|
|
+ </template>
|
|
|
+ <template v-else><i class="el-icon-plus"></i><span>批注</span></template>
|
|
|
+ </div>
|
|
|
</el-popover>
|
|
|
|
|
|
<div>
|
|
@@ -133,10 +158,11 @@ export default {
|
|
|
},
|
|
|
mixins: [PreviewQuestionTypeMixin],
|
|
|
data() {
|
|
|
- const { id, share_record_id, answer_record_id, question_index } = this.$route.query;
|
|
|
+ const { id, share_record_id, answer_record_id, exercise_id, question_index } = this.$route.query;
|
|
|
+ let questionIndex = Number(question_index);
|
|
|
|
|
|
return {
|
|
|
- exercise_id: id, // 练习题id
|
|
|
+ exercise_id: id || exercise_id, // 练习题id
|
|
|
share_record_id, // 分享记录id
|
|
|
answer_record_id: answer_record_id ?? '', // 答题记录id
|
|
|
secondFormatConversion,
|
|
@@ -150,7 +176,7 @@ export default {
|
|
|
currentQuestion: {},
|
|
|
// 当前问题索引
|
|
|
curQuestionIndex: -1,
|
|
|
- question_index: Number(question_index) || -1, // 跳转的问题索引
|
|
|
+ question_index: questionIndex >= 0 ? questionIndex : -1, // 跳转的问题索引
|
|
|
loading: false,
|
|
|
// 倒计时
|
|
|
countDownTimer: null,
|
|
@@ -162,13 +188,14 @@ export default {
|
|
|
curQuestionPage: '', // 当前问题页面
|
|
|
remark: {
|
|
|
is_remarked: 'false',
|
|
|
- }, // 教师批注
|
|
|
- isPopover: false,
|
|
|
- annotations: {
|
|
|
score: 0,
|
|
|
remark: '',
|
|
|
- file_id_list: [],
|
|
|
+ remark_person_image_url: '',
|
|
|
+ remark_person_name: '',
|
|
|
+ remark_time: '',
|
|
|
+ file_list: [],
|
|
|
}, // 批注
|
|
|
+ isPopover: false,
|
|
|
recordReport: {
|
|
|
answer_record: {
|
|
|
answer_duration: 0,
|
|
@@ -189,11 +216,20 @@ export default {
|
|
|
isAnnotations() {
|
|
|
return this.remark.is_remarked === 'true' || this.isTeacherAnnotations;
|
|
|
},
|
|
|
+ // 是否考试模式
|
|
|
+ isExamMode() {
|
|
|
+ return this.answer_mode === 2;
|
|
|
+ },
|
|
|
},
|
|
|
watch: {
|
|
|
curQuestionIndex() {
|
|
|
this.getQuestionInfo_AnswerRecord();
|
|
|
},
|
|
|
+ isSubmit(val) {
|
|
|
+ if (val) {
|
|
|
+ this.getAnswerRecordReport();
|
|
|
+ }
|
|
|
+ },
|
|
|
},
|
|
|
created() {
|
|
|
this.init();
|
|
@@ -208,7 +244,7 @@ export default {
|
|
|
this.getExerciseQuestionIndexList();
|
|
|
}
|
|
|
|
|
|
- if (this.share_record_id) {
|
|
|
+ if (this.share_record_id && !this.exercise_id) {
|
|
|
this.loading = true;
|
|
|
GetShareRecordInfo({ share_record_id: this.share_record_id }).then(
|
|
|
({ user_answer_record_info, share_record: { exercise_id, answer_mode, answer_time_limit_minute } }) => {
|
|
@@ -223,7 +259,7 @@ export default {
|
|
|
this.answer_record_id = this.user_answer_record_info.answer_record_id;
|
|
|
this.answer_mode = answer_mode;
|
|
|
// 如果是考试模式,且已经存在答题记录,则直接显示答题报告
|
|
|
- if (this.answer_mode === 2) this.isSubmit = true;
|
|
|
+ if (this.isExamMode) this.isSubmit = true;
|
|
|
}
|
|
|
if (!this.isTeacher) {
|
|
|
this.getAnswerRecordReport();
|
|
@@ -316,14 +352,13 @@ export default {
|
|
|
if (type === 'next') return this.nextQuestion();
|
|
|
}
|
|
|
|
|
|
- let answer = this.$refs.exercise[0].answer;
|
|
|
return FillQuestionAnswer({
|
|
|
answer_record_id: this.answer_record_id,
|
|
|
question_id: this.questionList[this.curQuestionIndex].id,
|
|
|
- answer: JSON.stringify(answer),
|
|
|
+ answer: JSON.stringify(this.$refs.exercise[0].answer),
|
|
|
}).then(() => {
|
|
|
this.questionList[this.curQuestionIndex].isFill = true;
|
|
|
- if (subjectiveQuestionList.includes(this.currentQuestion.type)) {
|
|
|
+ if (subjectiveQuestionList.includes(this.currentQuestion.type) || this.isExamMode) {
|
|
|
if (type === 'pre') return this.preQuestion();
|
|
|
if (type === 'next') return this.nextQuestion();
|
|
|
}
|
|
@@ -334,6 +369,7 @@ export default {
|
|
|
);
|
|
|
});
|
|
|
},
|
|
|
+ // 得到答题记录题目信息
|
|
|
getQuestionInfo_AnswerRecord() {
|
|
|
GetQuestionInfo_AnswerRecord({
|
|
|
answer_record_id: this.answer_record_id,
|
|
@@ -351,13 +387,16 @@ export default {
|
|
|
: this.previewComponents[this.questionList[this.curQuestionIndex].type];
|
|
|
}
|
|
|
|
|
|
- // 答案
|
|
|
- if (is_fill_answer === 'false') return;
|
|
|
- this.$refs.exercise?.[0].showAnswer(
|
|
|
- this.answer_mode === 1 && !this.isTeacherAnnotations,
|
|
|
- this.user_answer_record_info.correct_answer_show_mode === 1 && !this.isTeacherAnnotations,
|
|
|
- JSON.parse(content || '{}'),
|
|
|
- );
|
|
|
+ // 如果已经填写过答案,直接显示答案
|
|
|
+ if (is_fill_answer === 'true') {
|
|
|
+ this.$nextTick().then(() => {
|
|
|
+ this.$refs.exercise?.[0].showAnswer(
|
|
|
+ this.answer_mode === 1 && !this.isTeacherAnnotations,
|
|
|
+ this.user_answer_record_info.correct_answer_show_mode === 1 && !this.isTeacherAnnotations,
|
|
|
+ content.length > 0 ? JSON.parse(content) : null,
|
|
|
+ );
|
|
|
+ });
|
|
|
+ }
|
|
|
});
|
|
|
},
|
|
|
// 提交答题
|
|
@@ -399,23 +438,22 @@ export default {
|
|
|
upload(file) {
|
|
|
fileUpload('Mid', file).then(({ file_info_list }) => {
|
|
|
if (file_info_list.length > 0) {
|
|
|
- const { file_id } = file_info_list[0];
|
|
|
- this.annotations.file_id_list.push({ [file.file.uid]: file_id });
|
|
|
+ const { file_id, file_url, file_name } = file_info_list[0];
|
|
|
+ this.remark.file_list.push({ file_id, file_url, file_name });
|
|
|
}
|
|
|
});
|
|
|
},
|
|
|
- removeFile(file) {
|
|
|
- const index = this.annotations.file_id_list.findIndex((item) => Object.hasOwn(item, file.uid));
|
|
|
- this.annotations.file_id_list.splice(index, 1);
|
|
|
+ removeFile(i) {
|
|
|
+ this.remark.file_list.splice(i, 1);
|
|
|
},
|
|
|
// 填写批注
|
|
|
fillQuestionAnswerRemark() {
|
|
|
FillQuestionAnswerRemark({
|
|
|
answer_record_id: this.answer_record_id,
|
|
|
question_id: this.questionList[this.curQuestionIndex].id,
|
|
|
- file_id_list: this.annotations.file_id_list.map((item) => item[Object.keys(item)[0]]),
|
|
|
- score: this.annotations.score,
|
|
|
- remark: this.annotations.remark,
|
|
|
+ file_id_list: this.remark.file_list.map(({ file_id }) => file_id),
|
|
|
+ score: this.remark.score,
|
|
|
+ remark: this.remark.remark,
|
|
|
}).then(() => {
|
|
|
this.$message.success('批注成功');
|
|
|
this.isPopover = false;
|
|
@@ -473,6 +511,11 @@ export default {
|
|
|
cursor: pointer;
|
|
|
background-color: $fill-color;
|
|
|
border-radius: 20px;
|
|
|
+
|
|
|
+ &.has {
|
|
|
+ color: $danger-color;
|
|
|
+ background-color: #ffece8;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
.el-button {
|
|
@@ -490,6 +533,39 @@ export default {
|
|
|
padding: 8px 8px 14px;
|
|
|
font-size: 14px;
|
|
|
|
|
|
+ .remark-container {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ row-gap: 8px;
|
|
|
+
|
|
|
+ .remark-info {
|
|
|
+ display: flex;
|
|
|
+ column-gap: 8px;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ .remark-name {
|
|
|
+ flex: 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ .remark-time {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #999;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .file {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 8px;
|
|
|
+
|
|
|
+ .el-image {
|
|
|
+ width: 80px;
|
|
|
+ height: 80px;
|
|
|
+ background-color: #d9d9d9;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
.annotations-container {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
@@ -525,6 +601,25 @@ export default {
|
|
|
border: 1px solid $border-color;
|
|
|
}
|
|
|
|
|
|
+ .file-list {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ row-gap: 4px;
|
|
|
+
|
|
|
+ > li {
|
|
|
+ display: flex;
|
|
|
+ column-gap: 4px;
|
|
|
+
|
|
|
+ :first-child {
|
|
|
+ flex: 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ :last-child {
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
.popover-footer {
|
|
|
display: flex;
|
|
|
justify-content: flex-end;
|