Files
property-resident-side/property-uniapp-project/pageSubPack/service-list/questionnaireDetail.vue

222 lines
4.3 KiB
Vue

<template>
<view class="contentStyle">
<view class="status-bar"></view>
<uni-nav-bar title="问卷调查" backgroundColor="transparent" :border="false" left-icon="left" @clickLeft="goBack">
</uni-nav-bar>
<scroll-view class="page" scroll-y="true">
<view class="survey-title">{{ detailItem.title || '' }}</view>
<view class="survey-desc">{{ detailItem.description || '' }}</view>
<view v-if="questionList.length === 0" class="empty-tip">
<text>暂无问卷内容</text>
</view>
<view v-for="(q, idx) in questionList" :key="q.cid" class="question-item">
<view class="question-title">
<text v-if="q.required" class="required">*</text>
<text>{{ idx + 1 }}.{{ q.title || '未命名问题' }}</text>
</view>
<!-- radio / checkbox: 展示选项及选中状态 -->
<view v-if="q.type === 'radio' || q.type === 'checkbox'" class="option-list">
<view
v-for="(opt, oIdx) in q.options"
:key="oIdx"
class="option-item"
:class="{ active: isOptionSelected(q, opt.label) }"
>
<text class="option-icon">{{ q.type === 'radio' ? '●' : '■' }}</text>
<text class="option-label">{{ opt.label }}</text>
</view>
</view>
<!-- input / textarea: 展示文本答案 -->
<view v-if="q.type === 'input' || q.type === 'textarea'" class="answer-text">
{{ getAnswerText(q.cid) || '未填写' }}
</view>
</view>
</scroll-view>
</view>
</template>
<script setup>
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getQuestionnaireDetail } from '../api/apiSub.js'
const currentId = ref('')
const detailItem = ref({})
const questionList = computed(() => {
const content = detailItem.value?.content
if (!content) return []
try {
const list = JSON.parse(content)
return Array.isArray(list) ? list : []
} catch (e) {
return []
}
})
const answerMap = computed(() => {
const answerContent = detailItem.value?.myAnswer
if (!answerContent) return {}
try {
const list = JSON.parse(answerContent)
if (!Array.isArray(list)) return {}
const map = {}
for (const item of list) {
map[item.cid] = item.value
}
return map
} catch (e) {
return {}
}
})
const getDetailData = async () => {
try {
const params = { villageId: uni.getStorageSync('currentVillageId') }
const res = await getQuestionnaireDetail(currentId.value, params)
if (res.code === 200) {
const data = Array.isArray(res.data) ? res.data[0] : res.data
detailItem.value = data || {}
} else {
detailItem.value = {}
uni.showToast({ title: res.msg || '获取失败', icon: 'none' })
}
} catch (err) {
uni.showToast({ title: err.msg || '网络异常', icon: 'none' })
}
}
onLoad((option) => {
if (option.id) {
currentId.value = option.id
getDetailData()
}
})
function isOptionSelected(question, label) {
const val = answerMap.value[question.cid]
if (val === undefined || val === null) return false
if (Array.isArray(val)) {
return val.includes(label)
}
return val === label
}
function getAnswerText(cid) {
return answerMap.value[cid]
}
function goBack() {
uni.navigateBack()
}
</script>
<style lang="scss">
page {
background: #fff;
}
</style>
<style scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 40rpx;
box-sizing: border-box;
margin-bottom: 48rpx;
}
.survey-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
text-align: center;
margin: 0px 0 20rpx 0;
}
.survey-desc {
font-size: 24rpx;
color: #4A4A4A;
line-height: 1.6;
margin-bottom: 20rpx;
text-align: justify;
}
.empty-tip {
text-align: center;
padding: 100rpx 0;
font-size: 28rpx;
color: #999;
}
.question-item {
margin-bottom: 30rpx;
}
.question-title {
font-size: 32rpx;
color: #000;
margin-bottom: 10rpx;
font-weight: 600;
}
.required {
color: #ff0000;
font-size: 34rpx;
margin-right: 8rpx;
}
.answer-text {
font-size: 32rpx;
color: #333;
line-height: 1.5;
padding-left: 30rpx;
}
.option-list {
padding-left: 30rpx;
}
.option-item {
display: flex;
align-items: center;
gap: 16rpx;
padding: 16rpx 0;
font-size: 32rpx;
color: #333;
}
.option-item.active {
color: #409eff;
}
.option-icon {
font-size: 24rpx;
color: #ccc;
}
.option-item.active .option-icon {
color: #409eff;
}
.option-label {
font-size: 32rpx;
}
.status-bar {
width: 100vw;
height: 46px;
}
</style>