441 lines
13 KiB
Vue
441 lines
13 KiB
Vue
<template>
|
||
<view class="questionnaire-detail">
|
||
<!-- 自定义导航栏 -->
|
||
<wd-navbar :bordered="false"
|
||
custom-style="background: transparent !important; backdrop-filter: blur(10px) !important; -webkit-backdrop-filter: blur(10px) !important;"
|
||
safeAreaInsetTop fixed placeholder>
|
||
<template #left>
|
||
<view class="li-ml-15 li-mt-10 li-flex li-items-center">
|
||
<text class="ri-arrow-left-s-line li-text-70" @click="navBack"></text>
|
||
<text class="li-text-42">问卷详情</text>
|
||
</view>
|
||
</template>
|
||
</wd-navbar>
|
||
<!-- 导航栏背景 -->
|
||
<view class="nav-bg-layer"></view>
|
||
|
||
<!-- 整页背景 -->
|
||
<view class="page-bg"></view>
|
||
|
||
<!-- 问卷基本信息卡片 -->
|
||
<view class="li-w-92% li-mx-auto li-mt-30">
|
||
<view class="info-card li-bg-white li-rd-20 li-p-30 li-shadow-sm">
|
||
<!-- 问卷标题 -->
|
||
<view class="li-flex li-items-center li-justify-between li-pb-20 li-bottom-border2">
|
||
<view class="li-flex li-items-center">
|
||
<text class="ri-questionnaire-line li-text-40 li-mr-15 li-text-#0070F0"></text>
|
||
<text class="li-text-34 li-font-bold">{{questionnaireDetail.title}}</text>
|
||
</view>
|
||
<wd-tag :color="getTypeColor(questionnaireDetail.type)" :bg-color="getTypeBgColor(questionnaireDetail.type)">
|
||
{{questionnaireDetail.type}}
|
||
</wd-tag>
|
||
</view>
|
||
|
||
<!-- 提交信息 -->
|
||
<view class="li-mt-20">
|
||
<view class="li-flex li-items-center li-mb-15">
|
||
<text class="content-label li-text-28 li-text-#9a9a9a li-w-160">提交人</text>
|
||
<view class="li-flex li-items-center">
|
||
<text class="content-text li-text-28 li-text-#333">{{questionnaireDetail.user_name}}</text>
|
||
<view v-if="questionnaireDetail.is_anonymous" class="user-badge">匿名</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="li-flex li-items-center li-mb-15">
|
||
<text class="content-label li-text-28 li-text-#9a9a9a li-w-160">联系电话</text>
|
||
<text class="content-text li-text-28 li-text-#333">{{questionnaireDetail.contact_phone || '未提供'}}</text>
|
||
</view>
|
||
|
||
<view class="li-flex li-items-center li-mb-15">
|
||
<text class="content-label li-text-28 li-text-#9a9a9a li-w-160">所属小区</text>
|
||
<text class="content-text li-text-28 li-text-#333">{{questionnaireDetail.village_name}}</text>
|
||
</view>
|
||
|
||
<view class="li-flex li-items-center li-mb-15">
|
||
<text class="content-label li-text-28 li-text-#9a9a9a li-w-160">提交时间</text>
|
||
<text class="content-text li-text-28 li-text-#333">{{questionnaireDetail.submit_time}}</text>
|
||
</view>
|
||
|
||
<view class="li-flex li-items-center li-mb-15">
|
||
<text class="content-label li-text-28 li-text-#9a9a9a li-w-160">问题数量</text>
|
||
<text class="content-text li-text-28 li-text-#333">{{questionnaireDetail.questions?.length || 0}}题</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 问答内容区域 -->
|
||
<view class="li-w-92% li-mx-auto li-mt-30 li-pb-30">
|
||
<view class="content-card li-bg-white li-rd-20 li-p-30 li-shadow-sm">
|
||
<view class="li-flex li-items-center li-mb-20">
|
||
<text class="ri-question-answer-line li-text-40 li-mr-15 li-text-#0070F0"></text>
|
||
<text class="li-text-34 li-font-bold">问答内容</text>
|
||
</view>
|
||
|
||
<!-- 问答列表 -->
|
||
<view v-if="questionnaireDetail.questions && questionnaireDetail.questions.length > 0">
|
||
<view v-for="(question, index) in questionnaireDetail.questions" :key="index" class="question-item li-mb-30">
|
||
<view class="question-title li-mb-10">
|
||
<text class="question-index li-text-26 li-text-white bg-#0070F0">{{index + 1}}</text>
|
||
<text class="li-text-30 li-font-bold li-ml-15">{{question.title}}</text>
|
||
<text v-if="question.required" class="required-mark">*</text>
|
||
</view>
|
||
|
||
<!-- 问题类型标签 -->
|
||
<view class="question-type li-my-10">
|
||
<wd-tag size="small" color="#666" bg-color="#f5f5f5">{{getQuestionTypeName(question.type)}}</wd-tag>
|
||
</view>
|
||
|
||
<!-- 根据问题类型展示不同的答案内容 -->
|
||
<view class="answer-content li-p-20 li-bg-#f9f9f9 li-rd-10">
|
||
<!-- 单选题 -->
|
||
<view v-if="question.type === 'radio'" class="radio-answer">
|
||
<view v-for="(option, optionIndex) in question.options" :key="optionIndex"
|
||
class="option-item li-mb-10 li-py-10 li-px-20 li-rd-8"
|
||
:class="{'selected-option': question.answer === option.value}">
|
||
<text class="li-text-28">{{option.label}}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 多选题 -->
|
||
<view v-else-if="question.type === 'checkbox'" class="checkbox-answer">
|
||
<view v-for="(option, optionIndex) in question.options" :key="optionIndex"
|
||
class="option-item li-mb-10 li-py-10 li-px-20 li-rd-8"
|
||
:class="{'selected-option': question.answer && question.answer.includes(option.value)}">
|
||
<text class="li-text-28">{{option.label}}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 填空题 -->
|
||
<view v-else-if="question.type === 'text'" class="text-answer">
|
||
<text class="li-text-28">{{question.answer || '未填写'}}</text>
|
||
</view>
|
||
|
||
<!-- 评分题 -->
|
||
<view v-else-if="question.type === 'rating'" class="rating-answer">
|
||
<view class="rating-stars li-flex">
|
||
<text v-for="i in 5" :key="i"
|
||
:class="[
|
||
'ri-star-fill li-text-40 li-mr-15',
|
||
i <= question.answer ? 'li-text-#ff9900' : 'li-text-#dddddd'
|
||
]">
|
||
</text>
|
||
</view>
|
||
<text class="li-text-28 li-text-#666 li-mt-10">{{question.answer || 0}}分</text>
|
||
</view>
|
||
|
||
<!-- 图片上传题 -->
|
||
<view v-else-if="question.type === 'image'" class="image-answer">
|
||
<view v-if="question.answer && question.answer.length" class="image-list li-flex li-flex-wrap">
|
||
<view v-for="(img, imgIndex) in question.answer" :key="imgIndex" class="image-item">
|
||
<image :src="img" mode="aspectFill" class="uploaded-image" @click="previewImage(question.answer, imgIndex)"></image>
|
||
</view>
|
||
</view>
|
||
<view v-else class="li-text-28 li-text-#999">未上传图片</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 空状态 -->
|
||
<view v-else class="empty-state li-py-60 li-flex-center li-flex-col">
|
||
<image src="https://img.yzcdn.cn/vant/empty-image-default.png" mode="aspectFit" class="empty-image"></image>
|
||
<text class="li-text-28 li-text-#999 li-mt-20">暂无问卷内容</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 加载状态 -->
|
||
<view v-if="loading" class="loading-mask">
|
||
<wd-loading color="#0070F0" />
|
||
</view>
|
||
|
||
<wd-toast />
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, onMounted } from 'vue';
|
||
import { onLoad } from '@dcloudio/uni-app';
|
||
import { useToast } from '@/uni_modules/wot-design-uni';
|
||
|
||
const Toast = useToast();
|
||
|
||
// 状态管理
|
||
const loading = ref<boolean>(false);
|
||
const questionnaireId = ref<string | number>('');
|
||
|
||
// 模拟问卷详情数据
|
||
const questionnaireDetail = ref({
|
||
id: 2,
|
||
title: '小区环境改造意见征集',
|
||
village_name: '翠湖庭院',
|
||
type: '意见征集',
|
||
user_name: '李四',
|
||
contact_phone: '136****5678',
|
||
is_anonymous: true,
|
||
submit_time: '2024-06-12 09:45',
|
||
questions: [
|
||
{
|
||
id: 1,
|
||
title: '您对小区目前的环境满意度如何?',
|
||
type: 'radio',
|
||
required: true,
|
||
options: [
|
||
{ label: '非常满意', value: '5' },
|
||
{ label: '满意', value: '4' },
|
||
{ label: '一般', value: '3' },
|
||
{ label: '不满意', value: '2' },
|
||
{ label: '非常不满意', value: '1' }
|
||
],
|
||
answer: '3'
|
||
},
|
||
{
|
||
id: 2,
|
||
title: '您认为小区环境需要改进的地方有哪些?(可多选)',
|
||
type: 'checkbox',
|
||
required: true,
|
||
options: [
|
||
{ label: '绿化维护', value: '1' },
|
||
{ label: '健身设施', value: '2' },
|
||
{ label: '儿童游乐设施', value: '3' },
|
||
{ label: '休闲座椅', value: '4' },
|
||
{ label: '照明系统', value: '5' },
|
||
{ label: '停车位', value: '6' }
|
||
],
|
||
answer: ['1', '3', '5']
|
||
},
|
||
{
|
||
id: 3,
|
||
title: '请对小区环境改造提出您的具体建议',
|
||
type: 'text',
|
||
required: false,
|
||
answer: '希望能增加更多的绿植,并加强对现有绿化的养护。小区内的照明系统也需要升级,部分区域晚上太暗影响出行安全。'
|
||
},
|
||
{
|
||
id: 4,
|
||
title: '请对小区物业服务进行评分',
|
||
type: 'rating',
|
||
required: true,
|
||
answer: 4
|
||
},
|
||
{
|
||
id: 5,
|
||
title: '如有小区环境问题,请上传照片',
|
||
type: 'image',
|
||
required: false,
|
||
answer: [
|
||
'https://img.yzcdn.cn/vant/cat.jpeg',
|
||
'https://img.yzcdn.cn/vant/leaf.jpg'
|
||
]
|
||
}
|
||
]
|
||
});
|
||
|
||
// 获取问卷类型颜色
|
||
const getTypeColor = (type) => {
|
||
const colorMap = {
|
||
'满意度调查': '#0070F0',
|
||
'意见征集': '#ff9900',
|
||
'投票选举': '#07c160',
|
||
'需求调研': '#9254de',
|
||
'活动报名': '#ff4d4f'
|
||
};
|
||
return colorMap[type] || '#666666';
|
||
};
|
||
|
||
// 获取问卷类型背景色
|
||
const getTypeBgColor = (type) => {
|
||
const bgColorMap = {
|
||
'满意度调查': '#e8f4ff',
|
||
'意见征集': '#fff6e9',
|
||
'投票选举': '#e8fff1',
|
||
'需求调研': '#f5f0ff',
|
||
'活动报名': '#ffeded'
|
||
};
|
||
return bgColorMap[type] || '#f5f5f5';
|
||
};
|
||
|
||
// 获取问题类型名称
|
||
const getQuestionTypeName = (type) => {
|
||
const typeMap = {
|
||
'radio': '单选题',
|
||
'checkbox': '多选题',
|
||
'text': '填空题',
|
||
'rating': '评分题',
|
||
'image': '图片上传'
|
||
};
|
||
return typeMap[type] || '未知类型';
|
||
};
|
||
|
||
// 图片预览
|
||
const previewImage = (images, index) => {
|
||
uni.previewImage({
|
||
urls: images,
|
||
current: index
|
||
});
|
||
};
|
||
|
||
// 返回上一页
|
||
const navBack = () => {
|
||
uni.navigateBack();
|
||
};
|
||
|
||
// 加载问卷详情
|
||
const loadQuestionnaireDetail = async () => {
|
||
if (!questionnaireId.value) return;
|
||
|
||
loading.value = true;
|
||
try {
|
||
// 实际项目中这里应该调用API获取问卷详情
|
||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||
// 模拟获取数据,实际项目中应该替换为API调用
|
||
// questionnaireDetail.value = res.data;
|
||
loading.value = false;
|
||
} catch (error) {
|
||
console.error('加载问卷详情失败', error);
|
||
Toast.fail('加载失败,请重试');
|
||
loading.value = false;
|
||
}
|
||
};
|
||
|
||
onLoad((options) => {
|
||
if (options.id) {
|
||
questionnaireId.value = options.id;
|
||
loadQuestionnaireDetail();
|
||
}
|
||
});
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
page {
|
||
background-color: #F7F8FA;
|
||
}
|
||
|
||
.questionnaire-detail {
|
||
min-height: 100vh;
|
||
position: relative;
|
||
z-index: 0;
|
||
padding-bottom: 40rpx;
|
||
}
|
||
|
||
.nav-bg-layer {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: calc(var(--status-bar-height) + 88rpx);
|
||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.95) 0%, rgba(255, 255, 255, 0.9) 100%);
|
||
z-index: 10;
|
||
backdrop-filter: blur(10px);
|
||
-webkit-backdrop-filter: blur(10px);
|
||
}
|
||
|
||
.page-bg {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: linear-gradient(180deg, rgba(247, 248, 250, 0.9) 0%, rgba(247, 248, 250, 1) 100%);
|
||
z-index: -2;
|
||
}
|
||
|
||
.info-card, .content-card {
|
||
transition: all 0.3s;
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.li-bottom-border2 {
|
||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||
}
|
||
|
||
.user-badge {
|
||
display: inline-block;
|
||
font-size: 22rpx;
|
||
background-color: #f5f5f5;
|
||
color: #999;
|
||
padding: 2rpx 12rpx;
|
||
border-radius: 16rpx;
|
||
margin-left: 16rpx;
|
||
}
|
||
|
||
.question-item {
|
||
.question-title {
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.question-index {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 36rpx;
|
||
height: 36rpx;
|
||
border-radius: 18rpx;
|
||
font-size: 22rpx;
|
||
}
|
||
|
||
.required-mark {
|
||
color: #ff4d4f;
|
||
margin-left: 10rpx;
|
||
}
|
||
}
|
||
|
||
.answer-content {
|
||
.option-item {
|
||
transition: all 0.2s;
|
||
background-color: #f2f2f2;
|
||
|
||
&.selected-option {
|
||
background-color: #e8f4ff;
|
||
color: #0070F0;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
|
||
.image-list {
|
||
margin-top: 16rpx;
|
||
|
||
.image-item {
|
||
width: 180rpx;
|
||
height: 180rpx;
|
||
margin-right: 16rpx;
|
||
margin-bottom: 16rpx;
|
||
|
||
.uploaded-image {
|
||
width: 100%;
|
||
height: 100%;
|
||
border-radius: 8rpx;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.empty-state {
|
||
.empty-image {
|
||
width: 200rpx;
|
||
height: 200rpx;
|
||
}
|
||
}
|
||
|
||
.loading-mask {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background-color: rgba(255, 255, 255, 0.7);
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
z-index: 100;
|
||
}
|
||
|
||
::v-deep .wd-navbar__placeholder {
|
||
height: calc(var(--status-bar-height) + 88rpx) !important;
|
||
padding-top: constant(safe-area-inset-top) !important;
|
||
padding-top: env(safe-area-inset-top) !important;
|
||
}
|
||
</style> |