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

<template>
<view class='wrap'>
<view class="top">
5 months ago
<view class='logintitle'>
<image src="@/static/login_logo.png" mode="aspectFit"></image>
</view>
</view>
<view class="answerwrap">
5 months ago
<!-- 倒计时圆形进度条 -->
<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>
5 months ago
<view>
<view class='answercenter'>
5 months ago
<!-- 连续答对提示 -->
<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'>
5 months ago
<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'
5 months ago
v-for="(ans,ansindex) in question_list[questionIndex]?question_list[questionIndex].options:[]"
5 months ago
:key="`${questionIndex}-${ans.id || ansindex}`">
5 months ago
<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>
5 months ago
<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>
5 months ago
<!-- 答题结果弹窗 -->
<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>
5 months ago
<script>
import {
isNull,
toast
} from "@/common/util.js"
export default {
data() {
5 months ago
return {
parStyle: {
'background': 'linear-gradient(to right, #58b3fe, #446efd)',
'color': '#fff',
'font-size': '32rpx',
'padding': '30rpx',
'width': '100%',
'height': '90rpx'
},
5 months ago
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,
5 months ago
timeicon: '',
logintitle: '',
isanswer: false, // 控制答案结束前不能点击到下一题
islock: false,
5 months ago
showResult: false, // 控制结果弹窗显示
isAllCorrect: false, // 是否全部答对
streakCount: 0, // 连续答对数量
showStreakTip: false, // 是否显示连续答对提示
streakTipShow: false, // 控制动画触发
}
},
onShow() {
this.getUserInfo()
},
onLoad() {
this.getQuestion()
5 months ago
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'
})
}
},
5 months ago
async getQuestion() {
let that = this
const res = await this.$u.api.questions()
5 months ago
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
5 months ago
},
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) {
5 months ago
// 如果已经显示了正确答案,不允许用户再点击
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
5 months ago
this.correctScore = (count / 5) * 100 // 总共5题
} else {
this.correctScore = 0
5 months ago
this.correctNum = 0
}
this.islock = true
5 months ago
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
})
},
5 months ago
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++
}
})
5 months ago
let isCurrentCorrect = count === 0 // 当前题目是否答对
this.myAnswer.push({
question_id: this.question_list[this.questionIndex]['id'],
answer_ids: ansid.join("|"),
5 months ago
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
5 months ago
// 判断是否答对,更新连续答对数
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++
5 months ago
// 重置选项状态
if (that.question_list[that.questionIndex]) {
that.question_list[that.questionIndex]['options'].map(item => {
item.flag = false
item.isanswer = false
item.iswrong = false
})
}
5 months ago
// 强制更新视图,确保选项正确渲染
that.$nextTick(() => {
that.$forceUpdate()
})
5 months ago
// 重置倒计时
that.resetTimer()
if (that.questionIndex >= 4) {
that.answerSubmit = true
// return
}
}, 1500)
},
5 months ago
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
})
}
5 months ago
// 强制更新视图,确保选项正确渲染
that.$nextTick(() => {
that.$forceUpdate()
})
5 months ago
// 重置倒计时
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;
5 months ago
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%;
5 months ago
background-repeat: no-repeat;
margin-bottom:30rpx;
// position: relative;
// top: 0;
// left: 0
}
.logintitle {
5 months ago
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%;
5 months ago
background-color: #eff8fd;
border-radius: 40rpx;
margin: 0 auto;
5 months ago
box-shadow: 0px 0px 20px -10px rgba(0, 0, 0, 0.1);
padding-bottom: 40rpx;
margin-top: 50rpx;
}
5 months ago
.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;
}
}
}
5 months ago
.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 {
5 months ago
padding: 100rpx 30rpx 30rpx;
line-height: 1.8;
font-family: "宋体";
color: rgba(0, 0, 0, 0.8);
5 months ago
.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;
}
}
}
5 months ago
.answertitle .hasspan {
flex: 1;
min-width: 0;
}
5 months ago
.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;
5 months ago
&:last-child .answeritem-wrapper {
margin-bottom: 0;
}
}
5 months ago
.answeritem-wrapper {
5 months ago
margin-bottom: 20rpx;
5 months ago
border-radius: 100rpx;
padding: 4rpx 20rpx;
background-color: #fff;
5 months ago
border: 4rpx solid transparent;
5 months ago
transition: border-color 0.3s, background-color 0.3s;
box-sizing: border-box;
}
5 months ago
.answeritem-wrapper.active {
border: 4rpx solid #4f79f1;
background-color: #fff;
}
5 months ago
.answeritem-wrapper.wrong {
border: 4rpx solid #ff0000;
background-color: #fff;
}
5 months ago
.answeritem-wrapper.correct {
border: 4rpx solid #4f79f1;
background-color: #fff;
}
5 months ago
.answeritem {
padding: 30rpx;
5 months ago
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%;
5 months ago
margin: 0rpx auto;
position: relative;
5 months ago
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%;
}
5 months ago
// 答题结果弹窗
.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>