修改练习的问答题答题

This commit is contained in:
田岩
2025-08-28 23:15:08 +08:00
parent ad2fece331
commit d8da850d92
13 changed files with 604 additions and 366 deletions
+131
View File
@@ -0,0 +1,131 @@
<template>
<view class="analysis_div" @click.stop="click_option()" :class="status">
<view class="line">
<text class="tr">维度评分</text>
<text class="td">{{ dimension_all_score }}</text>
</view>
<view class="line" v-for="item in evalSettingVos">
<text class="tr">{{ item.gradeDimens }}</text>
<text class="td">{{ item.anlyGrade }}</text>
<view class="your_answer" v-if="item.dimensCode==='01'">
<rich-text :nodes="highlightedText" class="content" />
</view>
</view>
</view>
</template>
<script setup>
import { ref, reactive, onMounted, nextTick, computed, toRaw, watch } from 'vue';
const emit = defineEmits(['click_option']);
const props = defineProps({
value: {
type: Object,
default: () => {},
required: false
},
judgeResult: {
type: Boolean,
default: false,
required: false
},
answerText: {
type: String,
default: '',
required: false
}
});
const evalSettingVos = computed(() => props.value.evalSettingVos);
const status = computed(() => (props.judgeResult ? 'success' : 'error'));
const keywords = computed(() => {
if (!props.value.relKeyword) return [];
return props.value.relKeyword.split(',').map(key => key.trim());
});
const highlightedText = computed(() => {
// 如果没有关键词,直接返回原文本
if (!keywords.value.length) return props.answerText;
// 构建正则表达式,匹配所有关键词(不区分大小写)
// 对关键词进行转义,避免特殊字符影响正则
const escapedKeywords = keywords.value.map(keyword =>
keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
);
// 创建正则表达式,g表示全局匹配,i表示不区分大小写
const regex = new RegExp(`(${escapedKeywords.join('|')})`, 'gi');
// 替换匹配到的关键词,用span包裹并添加高亮样式
return props.answerText.replace(regex, (match) =>
`<span class="highlight">${match}</span>`
);
});
const dimension_all_score = computed(() => {
return (
evalSettingVos.value
// 先通过map提取每个item的anlyGrade,不存在或非数字则取0
.map((item) => (item?.anlyGrade && typeof item.anlyGrade === 'number' ? item.anlyGrade : 0))
// 再通过reduce累加所有数值
.reduce((total, current) => total + current, 0)
);
});
watch(evalSettingVos.value, (newVal) => {
console.log('newVal2', newVal);
});
const click_option = () => {
emit('click_option', props.analysisVo);
};
</script>
<style scoped lang="scss">
.success {
background-color: #e7f8ed;
.td {
color: #15b859;
}
:deep(.highlight) {
color: #15b859;
}
}
.error {
background-color: #fce8e9;
.td {
color: #f5212d;
}
:deep(.highlight) {
color: #f5212d;
}
}
.your_answer{
:deep(.highlight) {
text-decoration: underline;
}
}
.analysis_div {
margin-top: 30rpx;
border-radius: 16rpx;
padding: 22rpx 15rpx 15rpx 34rpx;
.line {
margin: 12rpx 0;
}
.tr {
font-weight: 600;
font-size: 30rpx;
color: #333333;
}
.td {
font-weight: 600;
font-size: 30rpx;
}
.your_answer{
font-weight: 500;
font-size: 30rpx;
color: #333333;
line-height: 40rpx;
text-align: left;
margin-top: 12rpx;
}
}
</style>
+80 -60
View File
@@ -1,57 +1,63 @@
<template>
<view class="talking-info" v-if="status === '02'">
<view :class="['talking-gif', isPlaying ? 'is-play' : '']">
<!-- <CommonLoading color="#fff" /> -->
<view class="talking-line">
<view class="talking-info" v-if="status === '02'">
<view :class="['talking-gif', isPlaying ? 'is-play' : '']">
<!-- <CommonLoading color="#fff" /> -->
</view>
<view class="right-time">{{ voiceTime }}</view>
<view class="talking-cont">
<text :class="['talking-text translate-text loading']" v-show="textShow">
{{ showContent }}
</text>
<view class="talking-text translate-text" v-if="textLoading">
<CommonLoading color="#fff" />
</view>
</view>
<view class="btns-cont">
<view class="play-btn">
<image
v-show="!isPlaying"
class="icon"
@click="playVoice"
src="@/static/images/course/play-blue.png"
style="width: 100%; height: 100%"
></image>
<image
v-show="isPlaying"
class="icon"
@click.stop="playVoice"
src="@/static/images/course/stop-blue.png"
style="width: 100%; height: 100%"
></image>
</view>
<view class="text-btn" v-if="!textShow" @click.stop="translateVoice()"></view>
<view class="fl1"></view>
<view v-show="!props.isPreview && props.button_again_show" class="btns-cont-button-text" @click.stop="buutton_click('again')">
重答
</view>
<view v-show="!props.isPreview && props.button_complete_show" class="btns-cont-button-text" @click.stop="buutton_click('complete')">
完成
</view>
</view>
</view>
<view class="right-time">{{ voiceTime }}</view>
<view class="talking-cont">
<text :class="['talking-text translate-text loading']" v-show="textShow">
{{ showContent }}
</text>
<view class="talking-text translate-text" v-if="textLoading">
<!-- 01纯文字 -->
<view class="talking-info talking-info-text" v-else-if="status === '01'">
<view class="talking-cont">
<text :class="['talking-text translate-text loading']">
{{ showContent }}
</text>
</view>
<view class="btns-cont" v-show="!props.isPreview">
<view class="fl1"></view>
<view v-if="props.button_again_show" class="btns-cont-button-text" @click.stop="buutton_click('again')">重答</view>
<view v-if="props.button_complete_show" class="btns-cont-button-text" @click.stop="buutton_click('complete')">完成</view>
</view>
</view>
<view class="talking-info talking-info-loading" v-else-if="status === '00'">
<view :class="['talking-gif']">
<CommonLoading color="#fff" />
</view>
</view>
<view class="btns-cont">
<view class="play-btn">
<image
v-show="!isPlaying"
class="icon"
@click="playVoice"
src="@/static/images/course/play-blue.png"
style="width: 100%; height: 100%"
></image>
<image
v-show="isPlaying"
class="icon"
@click.stop="playVoice"
src="@/static/images/course/stop-blue.png"
style="width: 100%; height: 100%"
></image>
</view>
<view class="text-btn" v-if="!textShow" @click.stop="translateVoice()"></view>
<view class="fl1"></view>
<view class="btns-cont-button-text" @click.stop="buutton_click('again')">重答</view>
<view class="btns-cont-button-text" @click.stop="buutton_click('complete')">完成</view>
</view>
</view>
<!-- 01纯文字 -->
<view class="talking-info talking-info-text" v-else-if="status === '01'">
<view class="talking-cont">
<text :class="['talking-text translate-text loading']">
{{ showContent }}
</text>
</view>
<view class="btns-cont">
<view class="fl1"></view>
<view class="btns-cont-button-text" @click.stop="buutton_click('again')">重答</view>
<view class="btns-cont-button-text" @click.stop="buutton_click('complete')">完成</view>
</view>
</view>
<view class="talking-info talking-info-loading" v-else-if="status === '00'">
<view :class="['talking-gif']">
<CommonLoading color="#fff" />
</view>
</view>
</template>
@@ -85,12 +91,23 @@
default: '',
type: String
},
isPreview: { // 是否是预览语音
isPreview: {
// 是否是预览语音
default: false,
type: Boolean
}
},
button_again_show: {
// 是否显示重答按钮
default: true,
type: Boolean
},
button_complete_show: {
// 是否显示重答按钮
default: true,
type: Boolean
},
});
console.log('props.isPreview', props.isPreview);
const audioStore = useAudioStore();
const { dataInfo } = toRefs(props);
const status = computed(() => props.status);
@@ -139,7 +156,7 @@
console.log('点击播放');
audioStore.playAudio();
};
let unsubscribe = () =>{};
let unsubscribe = () => {};
onMounted(() => {
// 2. 监听 Store 中 isPlaying 的变化,实时同步
unsubscribe = audioStore.$subscribe((mutation, state) => {
@@ -174,12 +191,16 @@
audioStore.stopAudio();
// 2. 移除当前组件注册的回调(避免内存泄漏)
audioStore.removeCallbacks();
unsubscribe();
});
</script>
<style scoped lang="scss">
.talking-line {
display: flex;
justify-content: flex-end;
}
.talking-info {
padding: 20rpx;
border-radius: 15rpx;
@@ -188,7 +209,6 @@
position: relative;
color: #fff;
width: 100%;
float: right;
.talking-gif {
width: 200rpx;
@@ -220,14 +240,14 @@
padding-bottom: 20rpx;
width: auto;
min-width: calc(100vw * 0.45);
float: right;
// float: right;
.talking-cont {
margin-top: 0rpx;
// margin-top: 0rpx;
}
}
.talking-cont {
margin-top: 16rpx;
// margin-top: 16rpx;
overflow: hidden;
.talking-text {
@@ -251,7 +271,7 @@
justify-content: space-between;
align-items: center;
padding-top: 20rpx;
margin-top: 22rpx;
margin-top: 14rpx;
.replay-btn {
float: left;
width: 46rpx;
@@ -321,7 +341,7 @@
line-height: 30rpx;
text-align: left;
font-style: normal;
margin-left: 10rpx;
margin-left: 18rpx;
}
}
}
+36 -11
View File
@@ -35,6 +35,7 @@
:isPreview="props.isPreview"
:activeVoiceId="subject_data.qnsId"
@buutton_click="questionButtonClick"
:button_complete_show="props.mode === 'practice'"
></questionVue>
</template>
<template v-else>
@@ -62,17 +63,31 @@
确定
</uv-button>
</view>
<view
class="analysis_div"
v-if="!clearResult && ['study', 'mistake', 'settlement', 'practice'].includes(props.mode)"
>
<view class="analysis_title">
<view class="analysis_title" v-if="qnsTyp !== 'ES'">
<view class="success_answer">正确答案{{ correctResultLabel.join('') }}</view>
<view class="your_answer">
你的答案
<text :class="right_or_wrong_class ? 'success' : 'error'">{{ myResultLabel.join('') }}</text>
</view>
</view>
<esAnalysis
v-if="qnsTyp === 'ES'"
:value="subject_data.itemVos[0]"
:judgeResult="judgeResultStatus"
:answerText="subject_data.anserResult"
></esAnalysis>
<!-- 问答题专属参考答案 -->
<view class="reference_answer" v-if="qnsTyp === 'ES'">
<view class="reference_answer_title">参考答案</view>
{{ subject_data.itemVos[0]?.itemAnswer ?? '' }}
</view>
<view class="analysis_note">题目解析{{ subject_data?.analyContent }}</view>
</view>
</view>
@@ -82,6 +97,7 @@
<script setup>
import { ref, reactive, onMounted, nextTick, computed, getCurrentInstance } from 'vue';
import radioSubjectItem from './radio-subject-item.vue';
import esAnalysis from './es-analysis.vue';
import questionVue from './question.vue';
import { markIcon, feedbackIcon } from '@/common/imgSvg';
import { SUBJECT_TYPE } from '@/enum.js';
@@ -132,7 +148,7 @@
const isExpanded = ref(false);
const wrapperRef = ref(null);
const contentRef = ref(null);
const judgeResultStatus = computed(() => props.item.judgeResult === 'P');
const animationStyle = ref({
height: '0px',
overflow: 'hidden',
@@ -271,14 +287,7 @@
};
// 初始化动画
onMounted(() => {
// // 创建动画实例
// animation = uni.createAnimation({
// duration: 300,
// timingFunction: 'ease-in-out',
// delay: 0
// });
});
onMounted(() => {});
</script>
<style scoped lang="scss">
@@ -318,7 +327,7 @@
display: flex;
justify-content: space-between;
align-items: center;
margin: 32rpx 0 28rpx 0;
margin: 32rpx 0 0 0;
.success_answer {
}
@@ -341,6 +350,22 @@
line-height: 48rpx;
text-align: left;
font-style: normal;
margin-top: 28rpx;
}
.reference_answer {
.reference_answer_title {
font-weight: 700;
font-size: 30rpx;
color: #333333;
text-align: left;
line-height: 30rpx;
margin: 30rpx 0;
}
font-weight: 400;
font-size: 30rpx;
color: #666666;
line-height: 48rpx;
text-align: left;
}
}
.item {