405 lines
10 KiB
Vue
405 lines
10 KiB
Vue
<template>
|
||
<view class="item">
|
||
<view class="top">
|
||
<view class="type">{{ subject_type_text }}</view>
|
||
<view class="score" v-if="['settlement', 'examination'].includes(props.mode)">{{ item.tgtGrade }}分</view>
|
||
<view class="fl1"></view>
|
||
<view class="right">
|
||
<!-- 反馈中心 -->
|
||
<view class="mark_and_feedback_div" v-if="['examination', 'practice'].includes(props.mode)">
|
||
<image
|
||
v-if="props.mode === 'examination'"
|
||
:src="markIcon(props.item.mark ? '#0066FF' : '#ABABAB')"
|
||
class="img"
|
||
@click="mark_click()"
|
||
></image>
|
||
<image :src="feedbackIcon()" class="img" @click="feedback_click()"></image>
|
||
</view>
|
||
<template v-if="['mistake'].includes(props.mode)">
|
||
<view class="right_text" @click="toggleExpand()">展开</view>
|
||
<uv-icon class="arrow-icon" name="arrow-right" color="#0066FF"></uv-icon>
|
||
</template>
|
||
</view>
|
||
</view>
|
||
<view class="question_text">
|
||
{{ subject_data.qnsContent }}
|
||
</view>
|
||
|
||
<view :style="props.mode === 'mistake' ? animationStyle : {}" class="collapse-wrapper" ref="wrapperRef">
|
||
<view class="collapse-content" ref="contentRef">
|
||
<view class="option_div">
|
||
<template v-if="qnsTyp === 'ES'">
|
||
<questionVue
|
||
:status="subject_data.es_status"
|
||
:dataInfo="props.item?.my_answer"
|
||
:activeVoiceId="subject_data.qnsId"
|
||
@buutton_click="questionBuuttonClick"
|
||
></questionVue>
|
||
</template>
|
||
<template v-else>
|
||
<radioSubjectItem
|
||
v-for="(analysisVo, index) in subject_data.itemVos"
|
||
@click_option="click_option"
|
||
:key="index"
|
||
:analysisVo="analysisVo"
|
||
:my_answer="item_answer"
|
||
></radioSubjectItem>
|
||
</template>
|
||
</view>
|
||
<view
|
||
class="option_div_button"
|
||
v-if="qnsTyp === 'MU' && ['practice', 'study'].includes(props.mode) && !props.isPreview && clearResult"
|
||
>
|
||
<uv-button
|
||
type="primary"
|
||
class="send_evaluate"
|
||
:custom-style="customStyle"
|
||
:throttleTime="300"
|
||
@click="muCheckbox('answer')"
|
||
:hairline="false"
|
||
>
|
||
确定
|
||
</uv-button>
|
||
</view>
|
||
<view
|
||
class="analysis_div"
|
||
v-if="!clearResult && ['study', 'mistake', 'settlement', 'practice'].includes(props.mode)"
|
||
>
|
||
<view class="analysis_title">
|
||
<view class="success_answer">正确答案:{{ correctResultLabel.join('') }}</view>
|
||
<view class="your_answer">
|
||
你的答案:
|
||
<text :class="right_or_wrong_class ? 'success' : 'error'">{{ myResultLabel.join('') }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="analysis_note">题目解析:{{ subject_data?.analyContent }}</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
<script setup>
|
||
import { ref, reactive, onMounted, nextTick, computed, getCurrentInstance } from 'vue';
|
||
import radioSubjectItem from './radio-subject-item.vue';
|
||
import questionVue from './question.vue';
|
||
import { markIcon, feedbackIcon } from '@/common/imgSvg';
|
||
import { SUBJECT_TYPE } from '@/enum.js';
|
||
|
||
// 多选的确定按钮的样式
|
||
const customStyle = {
|
||
borderRadius: '16rpx',
|
||
width: '292rpx',
|
||
backgroundColor: '#0066FF',
|
||
border: '1rpx solid rgb(12, 123, 245)'
|
||
};
|
||
|
||
const emit = defineEmits(['subject_click', 'update:modelValue']);
|
||
const props = defineProps({
|
||
modelValue: {
|
||
type: [Array, Object],
|
||
default: () => []
|
||
},
|
||
item: {
|
||
type: Object,
|
||
required: true
|
||
},
|
||
mode: {
|
||
type: String,
|
||
required: true,
|
||
validator: (value) => {
|
||
// 考试: examination
|
||
// 练习: practice
|
||
// 错题本: mistake
|
||
// 学习: study
|
||
// 考试结算页 settlement
|
||
return ['examination', 'practice', 'mistake', 'study', 'settlement'].includes(value);
|
||
}
|
||
},
|
||
// 是否预览模式
|
||
isPreview: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
// 是否清除正确答案
|
||
clearResult: {
|
||
type: Boolean,
|
||
default: true
|
||
}
|
||
});
|
||
|
||
// 状态管理
|
||
const isExpanded = ref(false);
|
||
const wrapperRef = ref(null);
|
||
const contentRef = ref(null);
|
||
|
||
const animationStyle = ref({
|
||
height: '0px',
|
||
overflow: 'hidden',
|
||
transition: 'height 0.3s ease-in-out'
|
||
});
|
||
|
||
const modelValue = computed({
|
||
get: () => props.modelValue,
|
||
set: (val) => emit('update:modelValue', val)
|
||
});
|
||
// result.anserResult
|
||
|
||
let answerText = props.item?.my_answer || props.item.anserResult || [];
|
||
// item_answer 是答题结果,可以是对象 数组 字符串类型
|
||
const item_answer = ref(typeof answerText === 'string' ? answerText.split(',') : answerText);
|
||
|
||
const feedback_click = () => {
|
||
emit('subject_click', 'feedback', props.item);
|
||
};
|
||
const instance = getCurrentInstance(); // 获取当前组件实例
|
||
|
||
// 切换展开/折叠状态(兼容H5和App)
|
||
const toggleExpand = async () => {
|
||
console.log('切换展开/折叠状态');
|
||
const query = uni.createSelectorQuery().in(instance);
|
||
query
|
||
.select('.collapse-content')
|
||
.boundingClientRect((rect) => {
|
||
console.log('rect', rect);
|
||
})
|
||
.exec();
|
||
};
|
||
const queryRect = () => {
|
||
// 组件内部一般用this.$uvGetRect,对外的为getRect,二者功能一致,名称不同
|
||
return new Promise((resolve) => {
|
||
this.$uvGetRect(`#${this.elId}`).then((size) => {
|
||
resolve(size);
|
||
});
|
||
});
|
||
};
|
||
const mark_click = () => {
|
||
emit('subject_click', 'mark', props.item);
|
||
};
|
||
// 单选 SN, 多选 MU,判断题 JD,简答 ES
|
||
const qnsTyp = computed(() => props.item.qnsTyp);
|
||
// 正确答案选项
|
||
const correctResultLabel = computed(() =>
|
||
subject_data.value.itemVos.filter((res) => res.correctFlag === 'R').map((res) => res.itemNo)
|
||
);
|
||
// 我的答案选项
|
||
const myResultLabel = computed(() => {
|
||
if (!props.item.anserResult) return ['无'];
|
||
const anserResultArray = props.item.anserResult.split(',');
|
||
return subject_data.value.itemVos.filter((res) => anserResultArray.includes(res.itemId)).map((res) => res.itemNo);
|
||
});
|
||
// 判断选项是否完全正确
|
||
const right_or_wrong_class = computed(
|
||
() =>
|
||
correctResultLabel.value.length === myResultLabel.value.length &&
|
||
myResultLabel.value.every((res) => correctResultLabel.value.includes(res))
|
||
);
|
||
|
||
const subject_data = computed({
|
||
get: () => {
|
||
return {
|
||
qnsId: props.item.qnsId,
|
||
qnsContent: props.item.qnsContent,
|
||
qnsTyp: props.item.qnsTyp,
|
||
es_status: props.item.es_status,
|
||
analyContent: props.item.analyContent,
|
||
anserResult: props.item.anserResult,
|
||
itemVos: (props.item.itemVos ?? []).map((res) => ({
|
||
...res,
|
||
correctFlag: props.clearResult ? '' : res.correctFlag
|
||
}))
|
||
};
|
||
},
|
||
set: (val) => {}
|
||
});
|
||
|
||
// 按钮
|
||
const questionBuuttonClick = (type, submitObj) => {
|
||
console.log('type', type, submitObj);
|
||
// 判断点击的是什么
|
||
if (type === 'complete') {
|
||
// 完成
|
||
} else if (type === 'again') {
|
||
// 重答
|
||
}
|
||
emit('subject_click', type, {
|
||
qnsTyp: props.item.qnsTyp,
|
||
qnsId: props.item.qnsId,
|
||
answer: item_answer.value,
|
||
answerItems: []
|
||
});
|
||
};
|
||
|
||
// 题类型
|
||
const subject_type_text = computed(() => SUBJECT_TYPE.find((res) => qnsTyp.value === res.value)?.label || '');
|
||
// 点击答题
|
||
const click_option = (analysisVo) => {
|
||
if (props.isPreview || !props.clearResult) return;
|
||
// 无论什么类型如果表里已经有了,再次点击就是弹出
|
||
if (item_answer.value.includes(analysisVo.itemId)) {
|
||
item_answer.value = item_answer.value.filter((id) => id !== analysisVo.itemId);
|
||
if (!['study'].includes(props.mode)) {
|
||
muCheckbox('choose');
|
||
}
|
||
} else if (props.item.qnsTyp === 'SN') {
|
||
// 单选
|
||
// 单选插入,并且移除其他
|
||
item_answer.value[0] = analysisVo.itemId;
|
||
muCheckbox('choose');
|
||
} else if (props.item.qnsTyp === 'MU') {
|
||
// 多选题
|
||
// 多选直接插入
|
||
item_answer.value.push(analysisVo.itemId);
|
||
if (!['study'].includes(props.mode)) {
|
||
muCheckbox('choose');
|
||
}
|
||
} else if (props.item.qnsTyp === 'JD') {
|
||
// 判断题
|
||
// 判断插入,并且移除其他
|
||
item_answer.value = [analysisVo.itemId];
|
||
muCheckbox('choose');
|
||
}
|
||
modelValue.value = item_answer.value;
|
||
};
|
||
const muCheckbox = (type = 'answer') => {
|
||
emit('subject_click', type, {
|
||
qnsTyp: props.item.qnsTyp,
|
||
qnsId: props.item.qnsId,
|
||
answer: item_answer.value,
|
||
answerItems: subject_data.value.itemVos.filter((t) => item_answer.value.includes(t.itemId))
|
||
});
|
||
};
|
||
|
||
// 初始化动画
|
||
onMounted(() => {
|
||
// // 创建动画实例
|
||
// animation = uni.createAnimation({
|
||
// duration: 300,
|
||
// timingFunction: 'ease-in-out',
|
||
// delay: 0
|
||
// });
|
||
});
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.collapse-wrapper {
|
||
// overflow: hidden; /* 关键:隐藏超出部分 */
|
||
// height: 0; /* 初始高度为0 */
|
||
// transition: height 0.3s ease-in-out;
|
||
}
|
||
|
||
.mark_and_feedback_div {
|
||
display: flex;
|
||
height: 100%;
|
||
justify-content: end;
|
||
align-items: center;
|
||
|
||
.img {
|
||
width: 38rpx;
|
||
height: 38rpx;
|
||
margin-left: 4rpx;
|
||
padding: 4rpx;
|
||
}
|
||
}
|
||
.option_div {
|
||
padding-top: 20rpx;
|
||
}
|
||
.option_div_button {
|
||
width: 100%;
|
||
height: 84rpx;
|
||
padding-top: 30rpx;
|
||
// background-color: red;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
.analysis_div {
|
||
.analysis_title {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin: 32rpx 0 28rpx 0;
|
||
|
||
.success_answer {
|
||
}
|
||
|
||
.your_answer {
|
||
.success {
|
||
color: #15b859;
|
||
}
|
||
.error {
|
||
color: #f5212d;
|
||
}
|
||
}
|
||
}
|
||
|
||
.analysis_note {
|
||
font-family: PingFangSC;
|
||
font-weight: 500;
|
||
font-size: 30rpx;
|
||
color: #666666;
|
||
line-height: 48rpx;
|
||
text-align: left;
|
||
font-style: normal;
|
||
}
|
||
}
|
||
.item {
|
||
// height: 170rpx;
|
||
overflow: hidden;
|
||
display: flex;
|
||
flex-direction: column;
|
||
padding-bottom: 40rpx;
|
||
|
||
.top {
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.type {
|
||
width: 108rpx;
|
||
height: 46rpx;
|
||
background: #267eff;
|
||
border-radius: 8rpx;
|
||
font-family: PingFangSC;
|
||
font-weight: 500;
|
||
font-size: 25rpx;
|
||
color: #ffffff;
|
||
text-align: center;
|
||
line-height: 46rpx;
|
||
}
|
||
|
||
.score {
|
||
margin-left: 24rpx;
|
||
font-family: ArialMT;
|
||
font-size: 30rpx;
|
||
color: #333333;
|
||
text-align: left;
|
||
}
|
||
|
||
.right {
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.right_text {
|
||
font-family: PingFangSC;
|
||
font-weight: 400;
|
||
font-size: 28rpx;
|
||
color: #333333;
|
||
line-height: 40px;
|
||
text-align: left;
|
||
font-style: normal;
|
||
}
|
||
}
|
||
}
|
||
|
||
.question_text {
|
||
font-family: PingFangSC;
|
||
font-weight: 500;
|
||
font-size: 30rpx;
|
||
color: #333333;
|
||
line-height: 48rpx;
|
||
text-align: left;
|
||
font-style: normal;
|
||
margin-top: 20rpx;
|
||
}
|
||
}
|
||
</style>
|