风险测评小程序前端代码仓库
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.
 
 
 

497 lines
12 KiB

<template>
<view class="page__view">
<navbar title="答题测评" leftClick @leftClick="$utils.navigateBack" bgColor="transparent" />
<!-- 答题完成 -->
<template v-if="current === total">
<view class="flex main is-finish">
<view class="card">
<image class="card-bg" src="@/pages_order/static/test/bg-test-finsih.png" mode="widthFix"></image>
<view class="flex flex-column card-content">
<view class="text">恭喜你~</view>
<view class="text">您已完成所有测评题目!</view>
<button class="btn" @click="onCreateReport">生成报告</button>
</view>
</view>
</view>
</template>
<!-- 答题中 -->
<template v-else-if="currentQuestion">
<view class="bar">
<view class="flex info">
<view>答题进度</view>
<view>
<text class="highlight">{{ current + 1 }}</text>
<text>{{ `/${total}` }}</text>
</view>
</view>
<view class="progress">
<view class="progress-bar" :style="{ width: `${progress}%` }"></view>
</view>
</view>
<view class="main">
<view class="card">
<view class="card-header">
<view class="flex tips">
<image class="icon" src="@/pages_order/static/test/icon-warning.png" mode="widthFix"></image>
<view>请根据真实情况谨慎作答,点击下一题提交答案无法更改</view>
</view>
<view class="question">{{ currentQuestion.question }}</view>
</view>
<view class="card-content">
<template v-if="currentQuestion.component === 'select'">
<view class="select">
<view
v-for="item in currentQuestion.options"
:key="item.id"
:class="['select-option', item.id === value ? 'is-active' : '']"
@click="onSelect(item.id)"
>
{{ item.content }}
</view>
</view>
</template>
<template v-else-if="currentQuestion.component === 'select-multiple'">
<view class="select">
<view
v-for="item in currentQuestion.options"
:key="item.id"
:class="['select-option', value.includes(item.id) ? 'is-active' : '']"
@click="onSelectMulitple(item.id)"
>
{{ item.content }}
</view>
</view>
</template>
</view>
</view>
</view>
<view class="bottom">
<button v-if="isLast" class="btn" @click="finish">提交</button>
<button v-else class="btn" @click="next">下一题</button>
</view>
</template>
</view>
</template>
<script>
import { mapState } from 'vuex'
export default {
data() {
return {
current: null,
tabs: [],
value: null,
answers: [],
paperTags: [],
batchNo: null,
questionsList: [],
total: 0,
}
},
computed: {
preIdx() {
let index = this.tabs.findIndex(index => index === this.current)
return index - 1
},
progress() {
return 100 * (this.current + 1) / this.total
},
isLast() {
return this.progress === 100
},
currentQuestion() {
return this.questionsList[this.current]
},
},
onLoad(arg) {
this.fetchQuestionList()
this.switchQuestion(parseInt(arg.current || 0))
this.value = this.answers[this.current]
console.log('paperTags', this.paperTags)
console.log('currentQuestion', this.currentQuestion)
},
methods: {
async fetchQuestionList(categories) {
const result = await this.$fetch('queryQuestionList', { categories })
// todo: set batchNo
// todo: transfer
const questionsList = new Array(23).fill(1).map((item, index) => {
const id = `00${index}`
return {
id,
text: '问题内容文字问题内容文字问题内容文字问题内容文字问题内容文字?',
type: '0',
options: new Array(4).fill(1).map((option, oIdx) => {
return {
id: `${id}${oIdx}`,
content: '答案内容',
}
}),
}
})
this.questionsList = questionsList.map((item, index) => {
const { id, text, type, options, needTag, required, multiple: _multiple, content } = item
let component = 'select'
const multiple = _multiple == 'Y'
switch(type) { // 0-单选题 1-填空题 2-图片单选题
case '1':
component = options?.length > 1 ? 'input-group' : 'input'
break
case '2':
component = multiple ? 'select-box-multiple' : 'select-box'
break
default:
component = multiple ? 'select-multiple' : 'select'
break
}
return {
id,
question: `${index + 1}、${text}`,
type,
component,
options: options.map((option, oIdx) => ({ id: option.id, content: `${String.fromCharCode(oIdx+65)}、${option.content}` })),
needTag: needTag ? JSON.parse(needTag).map(config => config.tags).filter(tags => tags.length) : null,
required: required == 'Y',
multiple,
desc: content,
}
})
this.total = this.questionsList.length
this.answers = this.questionsList.map(() => null)
console.log('questionsList', this.questionsList)
console.log('answers', this.answers)
},
switchQuestion(current) {
console.log('current', this.current, 'target', current)
let { needTag } = this.questionsList[current]
console.log('needTag length', needTag?.length)
if (!needTag?.length) {
this.current = current
this.tabs.push(this.current)
return
}
const selectTags = this.paperTags.flat(1)
console.log('selectTags', selectTags)
console.log('needTag', needTag)
let valid = needTag.some(tags => {
return tags.every(tag => {
const { value, exclude } = tag
let include = selectTags.includes(value)
return exclude ? !include : include
})
})
console.log('valid', valid)
if (valid) {
this.current = current
this.tabs.push(this.current)
return
}
if (current + 1 < this.questionsList.length) {
current += 1
this.switchQuestion(current)
return
}
this.fetchFinish()
},
async fetchAnswer() {
try {
const { id: questionsId, type, required, multiple, options } = this.currentQuestion
console.log('currentQuestion', this.currentQuestion)
console.log('value', this.value)
if (!this.value) {
console.log('未答题')
uni.showToast({
title: '请答题',
icon:'none'
})
return false
}
let params = {
batchNo: this.batchNo,
questionsId,
answerId: this.value,
}
await this.$fetch('updateAnswer', params)
this.answers[this.current] = this.value
return true
} catch (err) {
console.log('fetchAnswer', err)
return false
}
},
async fetchFinish() {
// todo: delete
// todo
// await this.$fetch('submitPaper', { id: this.paperInfo.reportId })
this.current = this.total
console.log('fetchFinish', this.current, this.currentQuestion)
// todo
// uni.reLaunch({
// url: '/pages/index/report'
// })
},
async next() {
let succ = await this.fetchAnswer()
if (!succ) {
return
}
this.switchQuestion(this.current + 1)
this.value = this.answers[this.current]
},
async finish() {
let succ = await this.fetchAnswer()
if (!succ) {
return
}
this.fetchFinish()
},
async onSelect(id) {
this.value = id
},
async onSelectMulitple(id) {
this.value = this.value.includes(id) ? this.value.filter(item => item !== id) : this.value.concat(id)
},
onCreateReport() {
uni.navigateTo({
url: `/pages_order/report/pay?batchNo=${this.batchNo}`
})
},
},
}
</script>
<style scoped lang="scss">
.page__view {
width: 100vw;
min-height: 100vh;
background: linear-gradient(164deg, #014FA2, #014FA2, #2E8AED);
position: relative;
}
.bar {
margin-top: 42rpx;
width: 100%;
padding: 0 47rpx;
box-sizing: border-box;
.info {
justify-content: flex-start;
column-gap: 13rpx;
font-size: 28rpx;
color: #A1D6FF;
.highlight {
color: #FFFFFF;
margin-right: 8rpx;
}
}
.progress {
margin-top: 20rpx;
width: 100%;
height: 16rpx;
background: rgba($color: #FFFFFF, $alpha: 0.35);
border-radius: 8rpx;
&-bar {
height: 100%;
background: #FFFFFF;
border-radius: 8rpx;
}
}
}
.main {
width: 100%;
padding: 73rpx 33rpx;
box-sizing: border-box;
}
.card {
position: relative;
width: 100%;
min-height: 876rpx;
padding: 27rpx 0;
box-sizing: border-box;
background: #FFFFFF;
border-radius: 16rpx;
&:after {
content: ' ';
position: absolute;
bottom: 0;
left: 50%;
transform: translate(-50%, 100%);
width: calc(100% - 20rpx * 2);
height: 28rpx;
background: rgba($color: #E9EFF2, $alpha: 0.29);
border-bottom-left-radius: 16rpx;
border-bottom-right-radius: 16rpx;
}
&-header {
padding: 0 33rpx;
.tips {
column-gap: 3rpx;
padding: 12rpx 5rpx;
font-size: 22rpx;
color: #DB5742;
border: 3rpx solid #DB5742;
border-radius: 7rpx;
margin-bottom: 26rpx;
.icon {
width: 31rpx;
height: auto;
}
}
}
&-content {
padding: 0 24rpx;
}
}
.question {
font-family: PingFang SC;
font-weight: 400;
font-size: 28rpx;
line-height: 50rpx;
color: #000000;
margin-bottom: 64rpx;
}
.select {
&-option {
margin-top: 44rpx;
padding: 22rpx 28rpx;
line-height: 1.3;
font-size: 28rpx;
color: #707070;
background: #F3F3F3;
border-radius: 28rpx;
&.is-active {
color: #014FA2;
background: rgba($color: #014FA2, $alpha: 0.22);
}
}
}
.bottom {
position: fixed;
left: 0;
bottom: 0;
width: 100%;
padding: 17rpx 72rpx;
padding-bottom: calc(env(safe-area-inset-bottom) + 17rpx);
background: #FFFFFF;
box-sizing: border-box;
.btn {
width: 100%;
padding: 26rpx 0;
font-size: 30rpx;
line-height: 1.4;
color: #FFFFFF;
background: #014FA2;
border-radius: 42rpx;
}
}
.desc {
margin-top: 220rpx;
font-family: PingFang SC;
font-weight: 400;
line-height: 1.4;
font-size: 26rpx;
color: #989898;
}
.main.is-finish {
height: calc(100vh - (var(--status-bar-height) + 120rpx));
padding: 0 33rpx;
padding-bottom: calc(var(--status-bar-height) + 120rpx);
.card {
min-height: 745rpx;
&-content {
justify-content: flex-end;
width: 100%;
padding: 69rpx 94rpx;
box-sizing: border-box;
.text {
font-size: 32rpx;
font-weight: 600;
color: #000000;
}
.text + .text {
margin-top: 45rpx;
}
.btn {
margin-top: 139rpx;
padding: 29rpx 183rpx;
box-sizing: border-box;
line-height: 1.4;
white-space: nowrap;
color: #FFFFFF;
background: #014FA2;
border-radius: 50rpx;
}
}
}
}
</style>