|
|
- <template>
- <view class="page__view">
- <navbar :title="`问题 ${current + 1}/${total}`" leftClick @leftClick="$utils.navigateBack" color="#191919" bgColor="transparent" />
-
- <template v-if="currentQuestion">
-
- <view class="bar">
- <view class="flex tools">
- <view class="left">
- <button class="flex btn" v-if="showPreBtn" @click="pre">
- <image class="btn-icon" src="@/pages_order/static/report/icon-wrapper.png" mode="widthFix"></image>
- <text>上一题</text>
- </button>
- </view>
- <view class="right">
- 选择你的答案进入下一题
- </view>
- </view>
- <view class="progress">
- <view class="progress-bar" :style="{ width: `${progress}%` }"></view>
- </view>
- </view>
-
- <view class="main">
- <view class="question">{{ currentQuestion.question }}</view>
- <template v-if="currentQuestion.component === 'select'">
- <view class="select">
- <view
- v-for="item in currentQuestion.options"
- :key="item.id"
- :class="['flex', '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="['flex', 'select-option', value.includes(item.id) ? 'is-active' : '']"
- @click="onSelectMulitple(item.id)"
- >
- {{ item.content }}
- </view>
- </view>
- </template>
- <template v-else-if="currentQuestion.component === 'select-box'">
- <view class="select-box">
- <view
- v-for="item in currentQuestion.options"
- :key="item.id"
- :class="['flex', 'flex-column', 'select-box-option', item.id === value ? 'is-active' : '']"
- @click="onSelect(item.id)"
- >
- <image class="img" :src="item.id === value ? item.imageAfter : item.image" mode="aspectFit"></image>
- <view class="text">{{ item.content }}</view>
- </view>
- </view>
- </template>
- <template v-else-if="currentQuestion.component === 'select-box-multiple'">
- <view class="select-box">
- <view
- v-for="item in currentQuestion.options"
- :key="item.id"
- :class="['flex', 'flex-column', 'select-box-option', value.includes(item.id) ? 'is-active' : '']"
- @click="onSelectMulitple(item.id)"
- >
- <image class="img" :src="value.includes(item.id) ? item.imageAfter : item.image" mode="aspectFit"></image>
- <view class="text">{{ item.content }}</view>
- </view>
- </view>
- </template>
- <template v-else-if="currentQuestion.component === 'input'">
- <view class="input">
- <view class="flex input-box">
- <view class="input-label" v-if="currentQuestion.options[0].prefix">{{ currentQuestion.options[0].prefix }}</view>
- <input class="input-doc" v-model="value" :type="currentQuestion.options[0].inputType || 'text'" placeholder="请输入内容" placeholder-style="font-family: PingFang SC; font-weight: 400; line-height: 1.4; font-size: 32rpx; color: #AAAACA;" />
- <view class="input-unit" v-if="currentQuestion.options[0].suffix">{{ currentQuestion.options[0].suffix }}</view>
- </view>
- </view>
- </template>
- <template v-else-if="currentQuestion.component === 'input-group'">
- <view class="input">
- <view class="flex input-box" v-for="(item, index) in currentQuestion.options" :key="item.id">
- <view class="input-label" v-if="item.prefix">{{ item.prefix }}</view>
- <input class="input-doc" v-model="value[index]" :type="currentQuestion.options[index].inputType || 'text'" placeholder="请输入内容" placeholder-style="font-family: PingFang SC; font-weight: 400; line-height: 1.4; font-size: 32rpx; color: #AAAACA;" />
- <view class="input-unit" v-if="item.suffix">{{ item.suffix }}</view>
- </view>
- </view>
- </template>
- <view class="flex btns">
- <button :class="['btn', 'btn-palin', noneFlag ? 'is-active' : '']" v-if="!currentQuestion.required" @click="onSelectNone">暂 无</button>
- <button class="btn" v-if="showConfirmBtn" @click="onConfirm">确 认</button>
- <button class="btn" v-if="showSubmitBtn" @click="onSubmit">提 交</button>
- </view>
- <view class="desc" v-if="currentQuestion.desc">
- <uv-parse :content="currentQuestion.desc"></uv-parse>
- </view>
- </view>
-
- </template>
-
- </view>
- </template>
-
- <script>
- import { mapState } from 'vuex'
-
- export default {
- data() {
- return {
- step: 0,
- current: null,
- tabs: [],
- value: null,
- noneFlag: false,
- answers: [],
- // tags: [],
- questionsList: [],
- total: 0,
- }
- },
- computed: {
- ...mapState(['paperInfo', 'paperTags']),
- preIdx() {
- let index = this.tabs.findIndex(index => index === this.current)
- return index - 1
- },
- showPreBtn() {
- return this.preIdx >= 0
- },
- progress() {
- return 100 * (this.current + 1) / this.total
- },
- showSubmitBtn() {
- return this.current + 1 === this.total
- },
- currentQuestion() {
- return this.questionsList[this.current]
- },
- showConfirmBtn() {
- if (this.showSubmitBtn) {
- return false
- }
-
- return ['select-box-multiple', 'select-multiple','input', 'input-group'].includes(this.currentQuestion?.component)
- },
- },
- onLoad(arg) {
- this.step = parseInt(arg.step)
- // this.current = parseInt(arg.current || 0)
- console.log('paperTags', this.paperTags)
- this.fetchQuestionList()
- this.switchQuestion(parseInt(arg.current || 0))
- this.value = this.answers[this.current]
-
- console.log('currentQuestion', this.currentQuestion)
- },
- methods: {
- fetchQuestionList() {
-
- const { category: categoryList } = this.paperInfo
- const category = categoryList[this.step]
-
- const { questionsList } = category
- this.questionsList = questionsList.map(item => {
- 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: text,
- type,
- component,
- options,
- 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(item => {
- let val = null
-
- switch (item.component) {
- case 'select-box-multiple':
- case 'select-multiple':
- val = []
- break
- case 'input-group':
- val = new Array(item.options.length).fill(0).map(() => '')
- break;
- default:
- val = null
- break;
- }
-
- return val
- })
- // this.tags = this.paperTags[this.step]
- // this.value = this.answers[this.current]
-
- console.log('questionsList', this.questionsList)
- console.log('answers', this.answers)
- },
- switchQuestion(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(2)
- 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.$utils.redirectTo(`/pages_order/report/test/step?step=${this.step + 1}`)
- },
- pre() {
- // this.current -= 1
- this.current = this.preIdx
- this.value = this.answers[this.current]
- this.noneFlag = false
- },
- next() {
- if (this.showSubmitBtn) {
- return
- }
- // this.current += 1
- this.switchQuestion(this.current + 1)
- this.value = this.answers[this.current]
- this.noneFlag = false
- },
- async fetchAnswer() {
- try {
- const { id: questionsId, type, required, multiple, options } = this.currentQuestion
-
- console.log('paperInfo', this.paperInfo)
- console.log('currentQuestion', this.currentQuestion)
- console.log('required', required, 'noneFlag', this.noneFlag, 'value', this.value)
- if (required && !this.noneFlag && (!this.value || !this.value?.length || this.value?.every?.(val => !val))) {
- console.log('未答题')
- uni.showToast({
- title: '请答题',
- icon:'none'
- })
-
- return false
- }
-
- let answer
-
- // 0-单选题 1-填空题 2-图片单选题
- if (type == 1) {
- answer = options.reduce((obj, option, oIdx) => {
- const { id: optionsId } = option
-
- obj[optionsId] = Array.isArray(this.value) ? this.value[oIdx] : this.value
-
- return obj
- }, {})
- answer = JSON.stringify(answer)
- } else if (multiple && Array.isArray(this.value)) { // 多选题
- answer = this.value.join(',')
- }
-
- let params = {
- id: this.paperInfo.reportId,
- questionsId,
- answer
- }
- console.log('params', params)
- await this.$fetch('answerPaper', params)
-
- this.answers[this.current] = this.value
-
- let paperTags = [...this.paperTags]
- if (type == 1) {
- paperTags[this.step][this.current] = []
- } else {
- let selectedIdArr = Array.isArray(this.value) ? this.value : [this.value]
- console.log('selectedIdArr', selectedIdArr, 'options', options)
-
- paperTags[this.step][this.current] = selectedIdArr.reduce((arr, id) => {
- const { settingTag } = options.find(item => item.id === id) || {}
-
- if (settingTag) {
- const tags = settingTag?.split?.(',')
- tags?.length && (arr = arr.concat(tags))
- }
-
- return arr
- }, [])
-
- }
- console.log('paperTags', paperTags)
- this.$store.commit('setPaperTags', paperTags)
-
- return true
- } catch (err) {
- console.log('answerPaper', err)
- return false
- }
- },
- async onSelect(id) {
- this.value = id
-
- if (this.showSubmitBtn) {
- return
- }
-
- let succ = await this.fetchAnswer()
-
- if (!succ) {
- return
- }
-
- this.$nextTick(() => {
- setTimeout(() => {
- this.next()
- }, 500)
- })
- },
- async onSelectMulitple(id) {
- this.value = this.value.includes(id) ? this.value.filter(item => item !== id) : this.value.concat(id)
- },
- async onConfirm() {
- let succ = await this.fetchAnswer()
-
- if (!succ) {
- return
- }
-
- this.next()
- },
- async onSelectNone() {
- this.noneFlag = true
-
- let val = null
-
- switch (this.currentQuestion.component) {
- case 'select-box-multiple':
- case 'select-multiple':
- val = []
- break
- case 'input-group':
- val = new Array(this.currentQuestion.options.length).fill(0).map(() => '')
- break;
- default:
- val = null
- break;
- }
-
- this.value = val
-
- let succ = await this.fetchAnswer()
-
- if (!succ) {
- return
- }
-
- this.next()
- },
- async onSubmit() {
- let succ = await this.fetchAnswer()
-
- if (!succ) {
- return
- }
-
- const { category } = this.paperInfo
-
- if (this.step == category.length - 1) {
-
- await this.$fetch('submitPaper', { id: this.paperInfo.reportId })
-
- uni.reLaunch({
- url: '/pages/index/report'
- })
- return
- }
-
- this.$utils.redirectTo(`/pages_order/report/test/step?step=${this.step + 1}`)
-
- },
- },
- }
- </script>
-
- <style scoped lang="scss">
- .page__view {
- width: 100vw;
- min-height: 100vh;
- background-color: $uni-bg-color;
- position: relative;
- }
-
- .bar {
- margin-top: 24rpx;
- width: 100%;
- padding: 0 32rpx;
- box-sizing: border-box;
-
- .left {
- text-align: left;
-
- .btn {
- display: inline-flex;
- font-family: PingFang SC;
- font-weight: 400;
- font-size: 30rpx;
- line-height: 1.4;
- color: #7451DE;
-
- &-icon {
- width: 32rpx;
- height: auto;
- margin-right: 8rpx;
- }
- }
- }
-
- .right {
- flex: 1;
- text-align: right;
- font-family: PingFang SC;
- font-weight: 400;
- font-size: 26rpx;
- line-height: 1.4;
- color: #989898;
- }
- }
-
- .progress {
- margin-top: 24rpx;
- width: 100%;
- height: 24rpx;
- background: #E5E5E5;
- border-radius: 12rpx;
-
- &-bar {
- height: 100%;
- background-image: linear-gradient(to right, #4B348F, #845CFA);
- border-radius: 12rpx;
- }
- }
-
- .main {
- width: 100%;
- padding: 70rpx 104rpx 0 104rpx;
- box-sizing: border-box;
- }
-
- .question {
- text-align: center;
- font-family: PingFang SC;
- font-weight: 400;
- font-size: 40rpx;
- line-height: 1.4;
- color: #000000;
- margin-bottom: 64rpx;
- }
-
- .select {
- &-option {
- padding: 30rpx 0;
- font-family: PingFang SC;
- font-weight: 400;
- line-height: 1.4;
- font-size: 32rpx;
- color: #252545;
- background-image: linear-gradient(to right, #FFFFFF, #F4F4F6, #F2F2F4);
- border-radius: 52rpx;
- box-shadow: -2rpx -2rpx 10rpx 0 #FFFFFFC4,
- 4rpx 4rpx 20rpx 0 #AAAACC1F,
- 2rpx 2rpx 4rpx 0 #AAAACC40,
- -1rpx -1rpx 2rpx 0 #FFFFFF;
-
- & + & {
- margin-top: 40rpx;
- }
-
- &.is-active {
- color: #FFFFFF;
- background: #7451DE;
- }
- }
- }
-
- .select-box {
- display: grid;
- grid-template-columns: repeat(3, 1fr);
- gap: 32rpx;
-
- &-option {
- padding: 24rpx 0;
- border-radius: 24rpx;
- background-image: linear-gradient(to right, #FFFFFF, #F2F2F4);
- box-shadow: -2rpx -2rpx 10rpx 0 #FFFFFFC4,
- 4rpx 4rpx 20rpx 0 #AAAACC1F,
- 2rpx 2rpx 4rpx 0 #AAAACC40,
- -1rpx -1rpx 2rpx 0 #FFFFFF;
-
- .img {
- width: 100%;
- height: 64rpx;
- }
-
- .text {
- margin-top: 8rpx;
- font-family: PingFang SC;
- font-weight: 400;
- font-size: 28rpx;
- line-height: 1.4;
- color: #969696;
- }
-
- &.is-active {
- background: #7451DE;
-
- .text {
- color: #FFFFFFC4;
- }
- }
- }
- }
-
- .input {
- &-box {
- width: 100%;
- height: 104rpx;
- padding: 0 40rpx;
- box-sizing: border-box;
- border-radius: 52rpx;
- background-image: linear-gradient(to right, #FFFFFF, #F4F4F6, #F2F2F4);
- box-shadow: -2rpx -2rpx 10rpx 0 #FFFFFFC4,
- 4rpx 4rpx 20rpx 0 #AAAACC1F,
- 2rpx 2rpx 4rpx 0 #AAAACC40,
- -1rpx -1rpx 2rpx 0 #FFFFFF;
-
- & + & {
- margin-top: 40rpx;
- }
- }
-
- &-doc {
- text-align: center;
- flex: 1;
- }
-
- &-label {
- padding: 21rpx 40rpx 21rpx 0;
- font-family: PingFang SC;
- font-weight: 600;
- font-size: 30rpx;
- line-height: 1.4;
- color: #252545;
- border-right: 2rpx solid #E0DFF0;
- }
-
- &-unit {
- padding: 21rpx 0 21rpx 40rpx;
- font-family: PingFang SC;
- font-weight: 600;
- font-size: 30rpx;
- line-height: 1.4;
- color: #252545;
- border-left: 2rpx solid #E0DFF0;
- }
- }
-
- .btns {
- margin-top: 64rpx;
- column-gap: 36rpx;
- .btn {
- flex: 1;
- padding: 24rpx 0;
- font-family: PingFang SC;
- font-weight: 600;
- font-size: 30rpx;
- line-height: 1.4;
- color: #FFFFFF;
- background-image: linear-gradient(to right, #4B348F, #845CFA);
- border-radius: 45rpx;
-
- &-palin {
- padding: 22rpx 0;
- font-weight: 400;
- color: #252545;
- background: transparent;
- border: 2rpx solid #252545;
-
- &.is-active {
- color: $uni-color;
- border-color: $uni-color;
- }
- }
- }
- }
-
- .desc {
- margin-top: 220rpx;
- font-family: PingFang SC;
- font-weight: 400;
- line-height: 1.4;
- font-size: 26rpx;
- color: #989898;
- }
-
- </style>
|