staff/pagesB/questionnaire/detail.vue

441 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>