Files
tra-app/src/components/examination/es-analysis.vue
T
2025-10-29 14:14:00 +08:00

149 lines
3.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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 class="td_success_error_img">
<image :src="select_status_icon" class="img"></image>
</view>
</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, watch } from 'vue';
import { optionErrorIcon, optionSuccessIcon } from '@/common/imgSvg';
const emit = defineEmits(['click_option']);
const props = defineProps({
value: {
type: Object,
default: () => {},
required: false
},
judgeResult: {
type: Boolean,
default: false,
required: false
},
anserResult: {
type: String,
required: true
}
});
const evalSettingVos = computed(() => props.value.evalSettingVos);
const status = computed(() => (props.judgeResult ? 'success' : 'error'));
const select_status_icon = computed(() => {
if (props.judgeResult) {
return optionSuccessIcon('#15B859');
} else {
return optionErrorIcon();
}
});
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.anserResult;
// 构建正则表达式,匹配所有关键词(不区分大小写)
// 对关键词进行转义,避免特殊字符影响正则
const escapedKeywords = keywords.value.map((keyword) => keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
// 创建正则表达式,g表示全局匹配,i表示不区分大小写
const regex = new RegExp(`(${escapedKeywords.join('|')})`, 'gi');
// 替换匹配到的关键词,用span包裹并添加高亮样式
return '你的答案:' + props.anserResult.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)
);
});
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;
position: relative;
}
.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: 46rpx;
text-align: left;
margin-top: 12rpx;
}
.td_success_error_img {
position: absolute;
right: 0;
top: 0;
.img {
width: 30rpx;
height: 30rpx;
padding-right: 20rpx;
}
}
}
</style>