You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

943 lines
22 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view class='wrap'>
<view class="top">
<view class='logintitle'>
<image src="@/static/login_logo.png" mode="aspectFit"></image>
</view>
</view>
<view class="answerwrap">
<!-- 倒计时圆形进度条 -->
<view class="timer-circle-wrapper">
<view class="timer-circle">
<svg class="timer-svg" viewBox="0 0 100 100">
<circle class="timer-circle-bg" cx="50" cy="50" r="43" fill="none" stroke="#c3c8ca" stroke-width="7"></circle>
<circle
class="timer-circle-progress"
cx="50"
cy="50"
r="43"
fill="none"
stroke="#4f79f1"
stroke-width="7"
:stroke-dasharray="`${2 * Math.PI * 43 * (45 - seconds) / 45}, ${2 * Math.PI * 43}`"
stroke-linecap="round"
transform="rotate(-90 50 50)"
></circle>
</svg>
<view class="timer-inner">
<view class="timer-text">{{seconds}}</view>
</view>
</view>
</view>
<view>
<view class='answercenter'>
<!-- 连续答对提示 -->
<view v-if="showStreakTip" class="streak-tip" :class="{ 'show': streakTipShow }" :key="streakCount">
<text class="streak-text">连续答对 <text class="streak-num">X{{streakCount}}</text></text>
</view>
<view class='answertitle'>
<view class="title-line">
<view class="question-number">{{questionIndex+1}}/5</view>
<view class="hasspan">
<!-- eslint-disable-next-line vue/no-v-text-v-html-on-component -->
<text v-html="question_list[questionIndex]?replaceAtSymbolsWithSpan(question_list[questionIndex]['title']):''"></text>
</view>
</view>
</view>
<view class='answercheck'
v-for="(ans,ansindex) in question_list[questionIndex]?question_list[questionIndex].options:[]"
:key="`${questionIndex}-${ans.id || ansindex}`">
<view
@click="chooseAnswer(ans,ansindex)"
:class="['answeritem-wrapper', {'active':ans.flag}, {'correct':ans.isanswer}, {'wrong':ans.iswrong}]">
<view class="answeritem">
<view class="answer-letter">{{answerNum[ansindex]}}</view>
<text class="answer-text">{{ans.title}}</text>
</view>
</view>
</view>
</view>
</view>
<view class='submit'>
<u-button v-if="!answerSubmit" size="mini" :throttle-time="1000" shape="circle" type="default" :custom-style="parStyle"
@click="nextQue">下一题</u-button>
<u-button v-if="answerSubmit" size="mini" :throttle-time="1000" shape="circle" type="default" :custom-style="parStyle"
@click="nextQue">提交</u-button>
</view>
</view>
<!-- 答题结果弹窗 -->
<view v-show="showResult" class="result-popup" @click.stop>
<view class="result-content">
<image
:src="isAllCorrect ? require('@/static/answer_right.png') : require('@/static/answer_error.png')"
class="result-image"
mode="aspectFit"
></image>
<view class="result-text">
<text v-if="isAllCorrect">恭喜你!全部答对了!</text>
<text v-else>答对 <text class="correct-num">{{correctNum}}</text> 题!继续努力</text>
</view>
<view class="result-buttons">
<u-button
v-if="isAllCorrect"
size="mini"
:throttle-time="1000"
shape="circle"
type="default"
:custom-style="parStyle"
@click="goToPrize"
>立即抽奖</u-button>
<u-button
v-else
size="mini"
:throttle-time="1000"
shape="circle"
type="default"
:custom-style="parStyle"
@click="continueAnswer"
>继续答题</u-button>
<u-button
size="mini"
:throttle-time="1000"
shape="circle"
type="default"
:custom-style="parStyle"
@click="goBack"
>返回</u-button>
</view>
</view>
</view>
</view>
</template>
<script>
import {
isNull,
toast
} from "@/common/util.js"
export default {
data() {
return {
parStyle: {
'background': 'linear-gradient(to right, #58b3fe, #446efd)',
'color': '#fff',
'font-size': '32rpx',
'padding': '30rpx',
'width': '100%',
'height': '90rpx'
},
seconds: 45,
timer: null,
question_list: [],
questionIndex: 0,
answercount: 0, //第几次答题
maxCount: 2,
answerNum: ["A", "B", "C", "D", "E", "F", "G", "H"],
myAnswer: [],
correctNum: 0, // 答对的题目数量
correctScore: 0, // 得分
correctAnswer: '', // 正确答案
answerSubmit: false, // 最后一题
hasFlag: false, // 是否选中
showAnswer: false,
timeicon: '',
logintitle: '',
isanswer: false, // 控制答案结束前不能点击到下一题
islock: false,
showResult: false, // 控制结果弹窗显示
isAllCorrect: false, // 是否全部答对
streakCount: 0, // 连续答对数量
showStreakTip: false, // 是否显示连续答对提示
streakTipShow: false, // 控制动画触发
}
},
onShow() {
this.getUserInfo()
},
onLoad() {
this.getQuestion()
this.startTimer()
},
onUnload() {
clearTimeout(this.timer)
},
methods: {
async getUserInfo() {
const res = await this.$u.api.user()
this.$u.vuex('vuex_user', res);
this.userInfo = res;
if (isNull(res.mobile)) {
uni.redirectTo({
url: '/pages/login/index'
})
}
},
async getQuestion() {
let that = this
const res = await this.$u.api.questions()
let data = res.questions
data.map(item => {
let type = 0
item.correctAnswer = []
item.options.map(i => {
i.flag = false
i.isanswer = false
i.iswrong = false
if (i.is_correct === 1) {
type++
item.correctAnswer.push(that.answerNum[i.myindex - 1])
}
})
item.type_name = type > 1 ? '多选题' : '单选题'
console.log("item.type_name",item.type_name)
})
console.log("data",data)
this.question_list = data
},
replaceAtSymbolsWithSpan(str) {
return str.replace(/@/g,
'<span style="display: inline-block;width:40px;border-bottom:1px solid #666;margin: 0 3px;margin-bottom: -2px;"></span>'
);
},
chooseAnswer(ans, ansindex) {
// 如果已经显示了正确答案,不允许用户再点击
if (this.isanswer) {
return
}
this.hasFlag = false
if (this.question_list[this.questionIndex]['type_name'] === '单选题') {
this.question_list[this.questionIndex]['options'].map(item => {
item.flag = false
})
ans.flag = !ans.flag
this.hasFlag = true
// ans.flag = !ans.flag
} else {
ans.flag = !ans.flag
this.question_list[this.questionIndex]['options'].map(item => {
if (item.flag == true) {
this.hasFlag = true
}
})
}
console.log("hasFlag", this.hasFlag)
},
submitQue() {
let that = this
if (this.myAnswer.length > 0) {
let count = 0
this.myAnswer.map(item => {
if (item.isCorrect === true) {
count++
}
})
this.correctNum = count
this.correctScore = (count / 5) * 100 // 总共5题
} else {
this.correctScore = 0
this.correctNum = 0
}
this.islock = true
clearInterval(this.timer)
// 判断是否全部答对
this.isAllCorrect = this.correctNum === 5
// 提交答案
this.$u.api.saveQuestion({
score: this.correctScore,
answers: this.myAnswer
}).then(res=>{
// 显示结果弹窗
this.showResult = true
}).catch(err => {
console.error('提交答案失败:', err)
// 即使提交失败也显示结果
this.showResult = true
})
},
goToPrize() {
uni.redirectTo({
url: '/pages/prize/index'
})
},
goBack() {
uni.redirectTo({
url: '/pages/me/me'
})
},
continueAnswer() {
// 重新加载当前页面
uni.reLaunch({
url: '/pages/answer/index'
})
},
nextQue() {
let that = this
// this.isanswer = false
if (this.isanswer) {
return
}
if (!this.hasFlag) {
toast("请先选择答案")
return
}
this.hasFlag = false
if (this.questionIndex > 4) {
this.answerSubmit = true
return
}
let ansid = []
let count = 0
this.question_list[this.questionIndex]['options'].map(item => {
if (item.flag === true) {
ansid.push(item.id)
}
if (!item.flag && item.is_correct === 1) {
count++
}
})
let isCurrentCorrect = count === 0 // 当前题目是否答对
this.myAnswer.push({
question_id: this.question_list[this.questionIndex]['id'],
answer_ids: ansid.join("|"),
isCorrect: isCurrentCorrect
})
this.correctAnswer = this.question_list[this.questionIndex]['correctAnswer'].join(',')
// 判断选择的是否正确 加样式
this.question_list[this.questionIndex]['options'].map(item => {
if (item.is_correct === 1) {
item.isanswer = true
} else if (item.flag == true && item.is_correct === 0) {
item.iswrong = true
}
})
this.isanswer = true
if (that.answerSubmit) {
that.submitQue()
return
}
setTimeout(function() {
that.correctAnswer = ''
that.isanswer = false
// 判断是否答对,更新连续答对数
if (isCurrentCorrect) {
that.streakCount++
// 如果连续答对1题以上显示提示
if (that.streakCount > 0) {
// 先重置动画状态
that.streakTipShow = false
that.showStreakTip = true
// 等待 DOM 更新后触发动画
that.$nextTick(() => {
setTimeout(() => {
that.streakTipShow = true
// 2秒后自动隐藏
setTimeout(() => {
that.streakTipShow = false
setTimeout(() => {
that.showStreakTip = false
}, 300) // 等待动画完成
}, 2000)
}, 10)
})
}
} else {
// 答错了,重置连续答对数
that.streakCount = 0
that.streakTipShow = false
that.showStreakTip = false
}
that.questionIndex++
// 重置选项状态
if (that.question_list[that.questionIndex]) {
that.question_list[that.questionIndex]['options'].map(item => {
item.flag = false
item.isanswer = false
item.iswrong = false
})
}
// 强制更新视图,确保选项正确渲染
that.$nextTick(() => {
that.$forceUpdate()
})
// 重置倒计时
that.resetTimer()
if (that.questionIndex >= 4) {
that.answerSubmit = true
// return
}
}, 1500)
},
startTimer() {
// 每题45秒倒计时
this.seconds = 45
this.timer = setInterval(() => {
if (this.seconds > 0) {
this.seconds--
} else {
clearInterval(this.timer)
if (!this.islock) {
// 倒计时结束,检查是否已作答
if (!this.hasFlag) {
// 未作答,自动显示正确答案并记录为错误
this.autoNextQuestion()
} else {
// 已作答,正常提交
this.submitQue()
}
}
}
}, 1000)
},
autoNextQuestion() {
// 倒计时结束未作答,自动显示正确答案并跳转
let that = this
// 显示正确答案
this.question_list[this.questionIndex]['options'].forEach((item, index) => {
if (item.is_correct === 1) {
// 使用 $set 确保响应式更新
this.$set(this.question_list[this.questionIndex]['options'][index], 'isanswer', true)
}
})
this.isanswer = true
// 强制更新视图,确保样式生效
this.$forceUpdate()
// 记录为错误(未作答)
this.myAnswer.push({
question_id: this.question_list[this.questionIndex]['id'],
answer_ids: "",
isCorrect: false
})
// 未作答,重置连续答对数
this.streakCount = 0
this.streakTipShow = false
this.showStreakTip = false
// 1.5秒后自动跳转到下一题
setTimeout(function() {
that.isanswer = false
// 检查是否是最后一题
if (that.questionIndex >= 4) {
// 最后一题,直接提交
that.submitQue()
} else {
// 不是最后一题,跳转到下一题
that.questionIndex++
// 重置选项状态
if (that.question_list[that.questionIndex]) {
that.question_list[that.questionIndex]['options'].map(item => {
item.flag = false
item.isanswer = false
item.iswrong = false
})
}
// 强制更新视图,确保选项正确渲染
that.$nextTick(() => {
that.$forceUpdate()
})
// 重置倒计时
that.resetTimer()
if (that.questionIndex >= 4) {
that.answerSubmit = true
}
}
}, 1500)
},
resetTimer() {
// 重置倒计时为45秒
clearInterval(this.timer)
this.seconds = 45
this.startTimer()
},
countdown(duration, onTick, onEnd) {
let remainingTime = duration * 60;
const tick = () => {
if (remainingTime > -1) {
const minutes = Math.floor(remainingTime / 60);
const seconds = remainingTime % 60;
onTick(minutes, seconds);
remainingTime--;
this.timer = setTimeout(tick, 1000);
} else {
console.log("remainingTime", remainingTime)
console.log("2--", this.islock)
onEnd();
}
};
tick();
}
}
}
</script>
<style scoped lang="scss">
.wrap {
/* height: 100%; */
width: 100%;
height: 100vh;
width: 100vw;
background-image: url('../../static/login_bg.png');
background-size: 100% 100%;
background-repeat: no-repeat;
overflow: scroll;
}
.top {
width: 100%;
// height: 440rpx;
/* background-image: url('../../static/bgtop1.png'); */
background-size: 100% 100%;
background-repeat: no-repeat;
margin-bottom:30rpx;
// position: relative;
// top: 0;
// left: 0
}
.logintitle {
padding-top: 100rpx;
padding-left:60rpx;
&>image{
width: 489rpx;
height: 244rpx;
}
}
.bottom {
width: 100%;
height: 533rpx;
/* background-image: url('../../static/bgbottom.png'); */
background-size: 100% 100%;
background-repeat: no-repeat;
position: relative;
bottom: 0;
left: 0
}
.answerwrap {
position: relative;
z-index: 2;
width: 90%;
background-color: #eff8fd;
border-radius: 40rpx;
margin: 0 auto;
box-shadow: 0px 0px 20px -10px rgba(0, 0, 0, 0.1);
padding-bottom: 40rpx;
margin-top: 50rpx;
}
.timer-circle-wrapper {
position: absolute;
top: -50rpx;
left: 50%;
transform: translateX(-50%);
z-index: 10;
.timer-circle {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
background: #fff;
display: flex;
align-items: center;
justify-content: center;
position: relative;
.timer-svg {
position: absolute;
top: 0;
left: 0;
width: 100rpx;
height: 100rpx;
transform: rotate(0deg);
}
.timer-inner {
position: relative;
z-index: 2;
width: 86rpx;
height: 86rpx;
border-radius: 50%;
background: #fff;
display: flex;
align-items: center;
justify-content: center;
}
.timer-text {
font-size: 36rpx;
font-weight: bold;
color: #4f79f1;
}
}
}
.answercenter {
position: relative;
.streak-tip {
position: absolute;
right: 30rpx;
top: 40rpx;
display: flex;
align-items: center;
gap: 20rpx;
z-index: 11;
.streak-icon {
width: 48rpx;
height: 48rpx;
border-radius: 50%;
background: #4cd964;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.streak-text {
font-size: 28rpx;
color: #333;
white-space: nowrap;
opacity: 0;
transform: translateX(-30rpx);
transition: opacity 0.3s ease-out, transform 0.3s ease-out;
}
.streak-num {
color: #4f79f1;
font-weight: bold;
}
&.show .streak-text {
opacity: 1;
transform: translateX(0);
}
}
}
.answertitle {
padding: 100rpx 30rpx 30rpx;
line-height: 1.8;
font-family: "宋体";
color: rgba(0, 0, 0, 0.8);
.title-line {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
font-size: 32rpx;
.question-number {
font-size: 32rpx;
color: #4f79f1;
font-weight: bold;
margin-right: 20rpx;
flex-shrink: 0;
}
}
}
.answertitle .hasspan {
flex: 1;
min-width: 0;
}
.answertitle .question-type {
color: #4f79f1;
font-size: 32rpx;
flex-shrink: 0;
margin-left: 10rpx;
}
.borderb {
display: inline-block;
width: 10rpx;
border-bottom: 1px solid #666;
}
.answercheck {
padding: 30rpx;
padding-top: 0rpx;
&:last-child .answeritem-wrapper {
margin-bottom: 0;
}
}
.answeritem-wrapper {
margin-bottom: 20rpx;
border-radius: 100rpx;
padding: 4rpx 20rpx;
background-color: #fff;
border: 4rpx solid transparent;
transition: border-color 0.3s, background-color 0.3s;
box-sizing: border-box;
}
.answeritem-wrapper.active {
border: 4rpx solid #4f79f1;
background-color: #fff;
}
.answeritem-wrapper.wrong {
border: 4rpx solid #ff0000;
background-color: #fff;
}
.answeritem-wrapper.correct {
border: 4rpx solid #4f79f1;
background-color: #fff;
}
.answeritem {
padding: 30rpx;
background-color: #fff;
border-radius: 46rpx;
color: #333;
display: flex;
align-items: center;
.answer-letter {
display: inline-flex;
align-items: center;
justify-content: center;
width: 50rpx;
height: 50rpx;
background-color: #4f79f1;
color: #fff;
text-align: center;
line-height: 50rpx;
border-radius: 50%;
margin-right: 20rpx;
font-size: 28rpx;
font-weight: bold;
flex-shrink: 0;
}
.answer-text {
flex: 1;
font-size: 28rpx;
color: #333;
}
}
.submit {
width: 50%;
margin: 0rpx auto;
position: relative;
z-index: 9;
padding-bottom: 40rpx;
margin-top:40rpx;
}
.answertip {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
/* padding-top:397rpx;
padding-bottom:300rpx; */
z-index: 9;
}
.answertipitem {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 77%;
}
.answrap {
/* background: url(../../static/answertipbg1.png); */
width: 483rpx;
height: 563rpx;
background-size: 100% 100%;
font-size: 28rpx;
width: 100%;
}
.answrap100 {
/* background: url(../../static/answertipbg.png); */
height: 612rpx;
background-size: 100% 100%;
width: 100%;
}
.ansscore {
padding-top: 120rpx;
text-align: center;
}
.ansflag {
/* background: url(../../static/answer100flag.png); */
width: 119%;
height: 164rpx;
background-size: 100% 100%;
position: absolute;
top: 240rpx;
left: -56rpx;
}
.answrap100 .ansflag {
top: 290rpx;
}
.ansicon {
position: absolute;
left: 235rpx;
top: -20rpx;
}
.ansicon80 {
position: absolute;
left: 165rpx;
top: -80rpx;
}
/* .answer100>view:first-child{
margin-top: 236rpx;
margin-left: 210rpx;
} */
.answerBtn {
/* margin-top: 236rpx;
margin-left: 210rpx; */
/* display:flex;
margin-top:100rpx;
margin-left:80rpx */
text-align: center;
/* margin-top: 120rpx; */
position: absolute;
top: 355rpx;
left: 0rpx;
width: 100%;
}
.answrap100 .answerBtn {
top: 410rpx;
}
.answerBtn view {
box-shadow: 1rpx 7rpx 18rpx 0rpx #2754a5;
color: #333;
padding: 16rpx 60rpx;
margin: 10rpx;
font-size: 28rpx;
border-radius: 10rpx;
display: inline-block;
}
.answerBtn view:last-child {
color: #fff;
background: linear-gradient(90deg, to right, #2754a5, #2b83bb);
}
.answertip span.ansfont {
color: #0095e5;
font-size: 90rpx;
}
.answer80 {
/* background: url(../../static/answer80.png); */
width: 483rpx;
height: 641rpx;
background-size: 100% 100%;
}
// 答题结果弹窗
.result-popup {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
}
.result-content {
position: relative;
display: flex;
align-items: center;
justify-content: center;
.result-image {
width: 725rpx;
height: 956rpx;
}
.result-text {
position: absolute;
top: 35%;
left: 50%;
transform: translateX(-50%);
font-size: 40rpx;
color: #2f6cf6;
font-weight: bold;
text-align: center;
white-space: nowrap;
.correct-num {
color: #fd9d0c;
font-weight: bold;
}
}
.result-buttons {
position: absolute;
top:60%;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 30rpx;
justify-content: center;
align-items: center;
width: 100%;
padding: 0 50rpx;
box-sizing: border-box;
::v-deep .u-btn {
flex: 0 0 auto;
width: 45%!important;
}
}
}
</style>