Commit 701804be authored by Damon's avatar Damon

refactor: 答题页面滚动组件重构

parent 1733381a
......@@ -55,12 +55,14 @@ export default {
type: Array,
default: [],
},
submitStatus: {
type: Boolean,
default: false,
},
},
data() {
return {
lists: [],
// 已提交答案
submitStatus: false,
// 答案对错
answer: '',
}
......@@ -69,15 +71,7 @@ export default {
answerList: {
handler: function(newValue, oldValue) {
if (newValue.length > 0) {
this.lists = newValue.map((v) => {
if (!v.checked) v.checked = false
if (!v.confirm) {
v.confirm = false
} else {
this.submitStatus = true
}
return v
})
this.lists = newValue
}
},
immediate: true,
......@@ -101,7 +95,9 @@ export default {
})
this.$set(this.lists[index], 'confirm', true)
// 判断所选答案是否正确
this.answer = this.rightKey.includes(this.lists[index].sort) ? 'pass' : 'fail'
this.answer = this.rightKey.includes(this.lists[index].sort)
? 'pass'
: 'fail'
}
this.$set(this.lists[index], 'checked', !this.lists[index].checked)
......@@ -124,7 +120,6 @@ export default {
}
},
submitAnswer() {
this.submitStatus = true
this.$emit('change', this.answer, this.lists)
},
},
......
......@@ -8,11 +8,12 @@
### Props
| 属性名 | 类型 | 默认值 | 可选值 | 说明 |
| :--------: | :-----: | :----: | :--------: | :---------------------: |
| multiple | Boolean | false | true/false | 是否支持多选 |
| answerList | Array | [] | - | 答案列表 **格式见下文** |
| rightKey | Array | [] | - | 正确答案 ['A'] |
| 属性名 | 类型 | 默认值 | 可选值 | 说明 |
| :----------: | :-----: | :----: | :--------: | :---------------------------------------------: |
| multiple | Boolean | false | true/false | 是否支持多选 |
| submitStatus | Boolean | false | true/false | 是否已选定答案, 选定后回到该页面将不能再重新选 |
| answerList | Array | [] | - | 答案列表 **格式见下文** |
| rightKey | Array | [] | - | 正确答案 ['A'] |
### answerList 格式
......
<template>
<view class="pageSwiperBox">
<view
class="pageSwiper"
:style="{
width: swiperWidth + 'px',
transform: 'translateX(' + (nowX + transX) + 'px)',
transition: !dragging ? duration + 'ms' : 'none',
}"
@touchstart="touchStart"
@touchmove="touchMove"
@touchend="touchEnd"
>
<block v-if="swiperType == 'lite'">
<view
class="pageSwiperItem"
:style="{ width: screenWidth + 'px', height: i != 1 ? 'auto' : '' }"
style="flex: 1;"
v-for="(item, i) in 3"
:key="i"
v-show="!waitShow"
>
<slot v-if="i == 1"></slot>
<view
class="pyh-pageSwiperMask"
v-show="i != 1"
@touchstart.stop="returnFalse"
@touchmove.stop.prevent="returnFalse"
@touchend.stop="returnFalse"
>
</view>
</view>
</block>
<block v-else-if="swiperType == 'static'">
<slot></slot>
</block>
</view>
</view>
</template>
<script>
export default {
name: 'pageSwiper',
props: {
swiperType: {
type: String,
default: 'lite',
},
dataLength: {
type: Number,
default: 1,
},
initIndex: {
type: Number,
default: 0,
},
duration: {
type: Number,
default: 200,
},
height: {
type: String,
default: '',
},
},
data() {
return {
swiperWidth: 3 * 375,
screenWidth: 375,
index: 0,
current: 1,
nowX: 0,
transX: 0,
dragging: true,
canMove: true,
showLoading: true,
waitShow: false,
pageTop: 0,
}
},
watch: {
dataLength(newValue, oldValue) {
if (newValue <= 1) this.canMove = false
},
},
created() {
this.index = this.initIndex
uni.getSystemInfo({
success: (res) => {
this.screenWidth = res.windowWidth
if (this.swiperType == 'lite') {
this.swiperWidth = 3 * res.windowWidth
this.nowX = -1 * this.current * this.screenWidth
} else if (this.swiperType == 'static') {
this.swiperWidth = this.dataLength * res.windowWidth
this.current = this.index
this.nowX = -1 * this.index * this.screenWidth
}
this.dragging = false
if (!this.height) {
var sQ = uni.createSelectorQuery().in(this)
var getTop = setInterval(() => {
sQ.select('.pageSwiperBox')
.boundingClientRect((data) => {
if (data) {
clearInterval(getTop)
this.pageTop = data.top
}
})
.exec()
}, 100)
}
},
})
},
methods: {
returnFalse() {},
swiperTap() {
this.$emit('swiperTap', this.current)
},
swiperTo(index) {
this.dragging = false
this.index = index
if (this.swiperType == 'lite') {
this.$emit('swiperChange', index)
} else if (this.swiperType == 'static') {
this.current = this.index
this.nowX = -1 * this.index * this.screenWidth
this.$emit('swiperChange', index)
}
if (!this.height) {
uni.pageScrollTo({
scrollTop: this.pageTop,
duration: 0,
})
}
},
touchStart(e) {
if (!this.canMove) return false
this.touchStartObj = e.mp.touches[0]
this.moveObj = null
},
touchMove(e) {
if (!this.canMove) return false
var moveObj = e.mp.touches[0]
if (!this.moveObj) {
this.moveObj = moveObj
if (Math.abs(this.moveObj.clientY - this.touchStartObj.clientY) <= 5) {
this.dragging = true
} else {
this.dragging = false
return false
}
}
if (!this.dragging) return false
var last = this.dataLength - 1
if (this.index == 0 && moveObj.clientX - this.touchStartObj.clientX > 0) {
this.dragging = false
this.$emit('endChange', 'left')
return false
} else if (
this.index == last &&
moveObj.clientX - this.touchStartObj.clientX < 0
) {
this.dragging = false
this.$emit('endChange', 'right')
return false
}
this.transX = parseInt(moveObj.clientX - this.touchStartObj.clientX)
if (moveObj.clientX < 0 || moveObj.clientX > this.screenWidth) {
this.touchEnd()
}
},
touchEnd() {
if (
Math.abs(this.transX) > this.screenWidth / 4 &&
this.canMove &&
this.dragging
) {
if (this.transX > 0) {
this.nowX += this.screenWidth
this.current--
} else {
this.nowX -= this.screenWidth
this.current++
}
var transAdd = this.transX > 0 ? false : true
var index = transAdd ? this.index + 1 : this.index - 1
this.transX = 0
this.dragging = false
this.canMove = false
setTimeout(() => {
this.canMove = true
this.$emit('swiperChange', index)
if (!this.height) {
uni.pageScrollTo({
scrollTop: this.pageTop,
duration: 0,
})
}
if (this.swiperType == 'lite') {
this.waitShow = true
this.dragging = true
this.index = index
this.current = 1
} else if (this.swiperType == 'static') {
if (transAdd) {
this.waitShow = true
this.dragging = true
}
this.index = index
}
this.nowX = -1 * this.current * this.screenWidth
setTimeout(() => {
this.waitShow = false
this.dragging = false
}, 200)
}, this.duration)
} else {
this.transX = 0
}
},
},
}
</script>
<style lang="scss">
.pageSwiperBox {
width: 100%;
overflow-x: hidden;
height: 100%;
flex: 1;
}
.pageSwiper {
width: 100%;
position: relative;
display: flex;
justify-content: flex-start;
align-items: flex-start;
flex: 1;
height: 100%;
}
.pageSwiper-wrap {
width: 100%;
display: flex;
align-items: flex-start;
min-height: 100%;
}
.pageSwiperItem {
overflow-x: hidden;
width: 750rpx;
position: relative;
}
.pyh-pageSwiperMask {
position: absolute;
top: 0;
width: 100%;
height: 100%;
}
.pyh-pageSwiperMask-text {
width: 750rpx;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
/* 旋转动画 */
.loadRotate {
margin-right: 10rpx;
display: inline-block;
width: 32upx;
height: 32upx;
border-radius: 50%;
border: 2upx solid #333;
border-bottom-color: transparent;
vertical-align: middle;
-webkit-animation: loadRotate 0.6s linear infinite;
animation: loadRotate 0.6s linear infinite;
}
@-webkit-keyframes loadRotate {
0% {
-webkit-transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
}
}
@keyframes loadRotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
......@@ -104,7 +104,7 @@ export default {
// 那么不会走swiperChange的change方法,需要我们手动去给它加一个current,然后传出去
if (current == this.swiperIndex) {
this.$emit("change", {source: "",current: index})
}
}
},
immediate: true
},
......@@ -132,9 +132,11 @@ export default {
swiperChange: function (e) {
let current = e.detail.current
let lastIndex = this.swiperIndex
console.log("本次:" + current + "上次:" + lastIndex)
let currentItem = this.swiperList[current]
let info = {}
info.source = e.detail.source
// 正向滑动,到下一个的时候
// 是正向衔接
let isLoopPositive = current == START && lastIndex == END
......@@ -146,8 +148,6 @@ export default {
this.swiperCurrent = lastIndex
return
}
//let swiperChangeItem = "swiperList[" + this.getNextSwiperChangeIndex(current) + "]"
//this[swiperChangeItem] = this.getNextSwiperNeedItem(currentItem, this.list)
this.$set(this.swiperList, this.getNextSwiperChangeIndex(current), this.getNextSwiperNeedItem(currentItem, this.list))
}
......@@ -162,17 +162,15 @@ export default {
this.swiperCurrent = lastIndex
return
}
// let swiperChangeItem = "swiperList[" + this.getLastSwiperChangeIndex(current) + "]"
// this[swiperChangeItem] = this.getLastSwiperNeedItem(currentItem, this.list)
this.$set(this.swiperList, this.getLastSwiperChangeIndex(current), this.getLastSwiperNeedItem(currentItem, this.list))
}
if (currentItem == null) return
info.current = currentItem.index
this.$emit("change", info)
// 记录滑过来的位置,此值对于下一次滑动的计算很重要
this.swiperIndex = current
},
/**
* 获取初始化的swiperList
*/
......
......@@ -10,26 +10,60 @@
></u-navbar>
<view class="head" v-if="type !== 3">
<view class="u-flex tab" v-if="type === 1">
<view :class="['tab-list', {'active': model === 1}]" @click="changeModel(1)">答题模式</view>
<view :class="['tab-list', {'active': model === 2}]" @click="changeModel(2)">学习模式</view>
<view
:class="['tab-list', { active: model === 1 }]"
@click="changeModel(1)"
>答题模式</view
>
<view
:class="['tab-list', { active: model === 2 }]"
@click="changeModel(2)"
>学习模式</view
>
</view>
<view class="u-flex count-down" v-if="type === 2">
<view>倒计时:<u-count-down :timestamp="timestamp" bg-color="" :show-hours="false"></u-count-down></view>
<view
>倒计时:<u-count-down
:timestamp="timestamp"
bg-color=""
:show-hours="false"
></u-count-down
></view>
</view>
</view>
<view class="swiper">
<swiper-limited-load
ref="swiper"
:swiperHeight="500"
:swiperDuration="swiperDuration"
:list="list"
:current="current"
:type="type"
:model="model"
@answerChange="answerChange"
@change="swiperChange"
<page-swiper
ref="pageSwiper"
:dataLength="dataLength"
:initIndex="index"
@swiperChange="swiperChange"
@endChange="endChange"
style="flex: 1;"
>
</swiper-limited-load>
<view class="u-flex subject">
<view class="title"
><text>{{ titleType[list[index].type - 1] }}</text
>{{ list[index].title }}</view
>
<view class="content">
<answer-sheet
ref="answerSheet"
:answerList="list[index].answerList"
:rightKey="list[index].rightKey"
:multiple="list[index].type === 3"
:submitStatus="list[index].submitStatus"
@change="answerChange"
></answer-sheet>
</view>
<view class="explain">
<answer-explanation
v-show="model === 2 || list[index].errShow"
:answer="list[index].answer"
:explain="list[index].explain"
></answer-explanation>
</view>
</view>
</page-swiper>
</view>
<view class="u-flex u-row-around bottom">
<view class="u-flex">
......@@ -39,7 +73,11 @@
</view>
</view>
<view class="u-flex">
<view class="c-list" v-if="type === 1 || type === 3" @click="collection">
<view
class="c-list"
v-if="type === 1 || type === 3"
@click="collection"
>
<u-icon name="star" color="#5A6073" size="40"></u-icon>
<text>收藏</text>
</view>
......@@ -50,7 +88,7 @@
</view>
</view>
<answer-operation-tips :show.sync="show" />
<!-- <answer-operation-tips :show.sync="show" />
<answer-complete-pop
:numberList="[1, 2, 3]"
type="fail"
......@@ -58,7 +96,7 @@
@cancel="visible = false"
@submit="submit"
>
</answer-complete-pop>
</answer-complete-pop> -->
</view>
</template>
......@@ -66,29 +104,33 @@
import swiperLimitedLoad from '@/components/swiper-limited-load/index.vue'
import answerOperationTips from '@/components/answer-operation-tips/index.vue'
import answerCompletePop from '@/components/answer-complete-pop/index.vue'
import pageSwiper from '@/components/page-swiper/index.vue'
import answerSheet from '@/components/answer-sheet/index.vue'
import answerExplanation from '@/components/answer-explanation/index.vue'
const NO_PREV_PAGE = -1
const NO_NEXT_PAGE = -2
const app = getApp()
export default {
components: {
swiperLimitedLoad,
answerOperationTips,
answerCompletePop,
pageSwiper,
answerSheet,
answerExplanation,
},
data() {
return {
visible: false,
show: false,
// 1 练习 2 考试 3 每日一练
type: 1,
// 1 练习 2 考试 3 每日一练 4 错题记录 5 收藏记录
type: 1,
// 1 答题模式 2 学习模式
model: 1,
timestamp: 60 * 60,
list: [],
current: 0,
currentIndex: 0,
swiperDuration: '0',
index: 0,
dataLength: 0,
titleType: ['单选题', '判断题', '多选题'],
}
},
computed: {},
......@@ -174,8 +216,7 @@ export default {
},
],
answer: 'B',
explain:
'测试内容测试内容测试内容测试内容测试内容测试内容',
explain: '测试内容测试内容测试内容测试内容测试内容测试内容',
index: 3,
rightKey: ['B'],
},
......@@ -184,67 +225,66 @@ export default {
this.requestQuestionInfo(lists)
},
methods: {
requestQuestionInfo(lists) {
// 初始化setData后,需要调用swiper组件的init方法
this.list = lists
this.$nextTick(() => {
// 比如上次答到了第20题
this.$refs.swiper.init(0)
// 初始化后再把动画弄出来,否则初始的current不是0,界面会自动跳动到当前位置,体验不太好
this.swiperDuration = '250'
// 全局记一下list, 答题卡页暂时就直接用了
app.globalData.questionList = lists
swiperChange(e) {
this.index = e
// 初始化checked confirm
this.list[e].answerList = this.list[e].answerList.map((v) => {
if (!v.checked) v.checked = false
if (!v.confirm) v.confirm = false
return v
})
},
swiperChange(e) {
let current = e.current
this.currentIndex = current
if (current == NO_PREV_PAGE) {
uni.showToast({
endChange(type) {
if (type === 'left') {
return uni.showToast({
title: '已经是第一题了',
icon: 'none',
})
return
}
if (current == NO_NEXT_PAGE) {
uni.showModal({
title: '提示',
content: '您已经答完所有题,是否退出?',
} else if (type === 'right') {
return uni.showToast({
title: '已经是最后一题了',
icon: 'none',
})
return
}
},
requestQuestionInfo(lists) {
this.list = lists
this.dataLength = lists.length
app.globalData.questionList = lists
},
changeModel(i) {
this.model = i
},
collection() {
console.log("this.list[this.currentIndex]", this.list[this.currentIndex])
console.log("收藏")
console.log('收藏')
},
// 作答完毕
answerChange(status, lists) {
// 保存答题状态
this.$set(this.list[this.currentIndex], 'answerList', lists)
this.$set(this.list[this.currentIndex], 'errShow', this.type === 2 ? true : status === 'fail')
this.$set(this.list[this.index], 'submitStatus', true)
this.$set(this.list[this.index], 'answerList', lists)
this.$set(
this.list[this.index],
'errShow',
this.type === 2 ? true : status === 'fail'
)
app.globalData.questionList = this.list
// 如果回答错误或者是考试模式,自动进入下一题
if (status === 'pass' || this.type === 2) {
if (this.currentIndex + 1 > this.list.length - 1) {
return
}
this.current = this.currentIndex + 1
if (this.index + 1 === this.list.length) return
this.index++
this.$refs.pageSwiper.swiperTo(this.index)
}
},
// 交卷
handPapers() {
this.visible = true
console.log("交卷")
console.log('交卷')
},
// 提前交卷
submit() {
console.log("提前交卷")
}
console.log('提前交卷')
},
},
}
</script>
......@@ -260,31 +300,64 @@ export default {
width: 100%;
height: 90rpx;
.tab {
margin:10rpx auto;
border: 2rpx solid #2953FF;
margin: 10rpx auto;
border: 2rpx solid #2953ff;
border-radius: 10rpx;
width: 300rpx;
.tab-list {
flex: 1;
padding: 10rpx 25rpx;
font-size: 24rpx;
color: #191C27;
color: #191c27;
}
.active {
background: #2953FF;
background: #2953ff;
color: #fff;
}
}
.count-down {
color: #5A6073;
color: #5a6073;
font-size: 32rpx;
margin:10rpx auto;
margin: 10rpx auto;
width: 230rpx;
}
}
.swiper {
width: 100%;
flex: 1;
flex: 1;
display: flex;
flex-direction: column;
.subject {
flex-flow: column;
padding: 20rpx;
.type {
width: 100%;
text-align: left;
padding: 20rpx 0;
}
.title {
color: #191c27;
font-size: 32rpx;
line-height: 50rpx;
font-weight: bold;
width: 100%;
text-align: left;
text {
background: #9ea5bb;
color: #fff;
padding: 8rpx 20rpx;
margin-right: 10rpx;
font-size: 20rpx;
border-radius: 50rpx;
}
}
.content,
.explain,
.btn {
width: 100%;
padding-top: 50rpx;
}
}
}
.bottom {
width: 100%;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment