540 lines
13 KiB
Vue
540 lines
13 KiB
Vue
<template>
|
|
<view class="questionnaire-page">
|
|
<!-- 自定义导航栏 -->
|
|
<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 v-if="hasMultiplePages" class="ri-arrow-left-s-line li-text-70"
|
|
@click="toPages({type:'nav'})"></text>
|
|
<text v-if="!hasMultiplePages" class="ri-home-5-line li-text-55 li-mb-8 li-mr-10"
|
|
@click="toPages({type:'home'})"></text>
|
|
<text class="li-text-42">问卷管理</text>
|
|
</view>
|
|
</template>
|
|
<!-- #ifdef MP-WEIXIN -->
|
|
<template #right>
|
|
<view class="li-flex-center li-mr-200" @click="createQuestionnaire">
|
|
<text class="ri-add-line li-text-52"></text>
|
|
</view>
|
|
</template>
|
|
|
|
<!-- #endif -->
|
|
<!-- #ifndef MP-WEIXIN -->
|
|
<template #right>
|
|
<view class="li-flex-center li-mr-25" @click="createQuestionnaire">
|
|
<text class="ri-add-line li-text-52"></text>
|
|
</view>
|
|
</template>
|
|
<!-- #endif -->
|
|
</wd-navbar>
|
|
<!-- 导航栏背景 -->
|
|
<view class="nav-bg-layer"></view>
|
|
|
|
<!-- 整页背景 -->
|
|
<view class="page-bg"></view>
|
|
|
|
<!-- 筛选区域 -->
|
|
<view class="filter-section li-w-92% li-mx-auto li-mt-30">
|
|
|
|
<!-- 问卷状态筛选 -->
|
|
<view class="li-flex li-items-center li-flex-wrap">
|
|
<text v-for="(item, index) in statusList" :key="index"
|
|
:class="activeStatus === item.value ? 'status-tag active' : 'status-tag'"
|
|
@click="handleStatusChange(item.value)">
|
|
{{item.label}}
|
|
</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 小区选择器 -->
|
|
<view class="li-px-15">
|
|
<wd-picker size="large" v-model="showVillagePicker" :columns="pickerVillageList" title="选择小区"
|
|
v-model:value="selectedVillage" @confirm="handleVillageConfirm" label-key="label" value-key="value" />
|
|
</view>
|
|
|
|
<!-- 问卷列表 -->
|
|
<view class="li-w-92% li-mx-auto li-mt-30 li-pb-30">
|
|
<view v-if="questionnaireList.length > 0">
|
|
<view v-for="(item, index) in questionnaireList" :key="index"
|
|
class="questionnaire-card li-bg-white li-rd-20 li-p-30 li-mb-20 li-shadow-sm"
|
|
@click="toPages({type:'detail', value:item})">
|
|
<!-- 问卷标题 -->
|
|
<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-32 li-font-bold">{{item.title}}</text>
|
|
</view>
|
|
<view class="status-badge" :style="{
|
|
backgroundColor: getStatusBgColor(item.status),
|
|
color: getStatusColor(item.status)
|
|
}">
|
|
{{getStatusText(item.status)}}
|
|
</view>
|
|
</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>
|
|
<text class="content-text li-text-28 li-text-#333">{{item.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>
|
|
<wd-tag color="#0083ff" bg-color="#d0e8ff">{{item.type}}</wd-tag>
|
|
</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">{{item.participant_count}}人</text>
|
|
</view>
|
|
|
|
<!-- 修改有效期和操作按钮的布局 -->
|
|
<view class="li-flex li-flex-col">
|
|
<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">{{item.start_time}} ~
|
|
{{item.end_time}}</text>
|
|
</view>
|
|
<view class="li-flex li-items-center li-justify-between" @click.stop>
|
|
<view></view>
|
|
<view class="li-flex li-items-center ">
|
|
<wd-button type="primary" size="small" plain custom-class="action-btn"
|
|
@click="handleEdit(item)">编辑</wd-button>
|
|
<wd-button type="danger" size="small" plain custom-class="action-btn"
|
|
@click="handleDelete(item)">删除</wd-button>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 空状态 -->
|
|
<view v-else class="empty-state li-mt-100">
|
|
<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 v-if="loading" class="li-flex-center li-mt-30">
|
|
<wd-loading color="#0070F0" />
|
|
</view>
|
|
|
|
<!-- 底部提示 -->
|
|
<view v-if="!loading && finished && questionnaireList.length > 0"
|
|
class="li-text-center li-text-26 li-text-#999 li-py-20">
|
|
-- 没有更多数据了 --
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, reactive, onMounted, watch } from 'vue';
|
|
import { onLoad, onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app';
|
|
import { useNavigation } from '@/hooks/useNavigation';
|
|
import { useToast } from '@/uni_modules/wot-design-uni';
|
|
|
|
const Toast = useToast();
|
|
|
|
// 使用导航composable
|
|
const {
|
|
hasMultiplePages,
|
|
isTabBarPage,
|
|
checkRouteStack
|
|
} = useNavigation();
|
|
|
|
// 权限控制
|
|
const hasPermission = ref(true); // 实际项目中应该从用户信息或权限系统中获取
|
|
|
|
// 状态管理
|
|
const selectedVillage = ref('ALL'); // 设置默认为全部小区
|
|
const activeStatus = ref('ALL');
|
|
const loading = ref(false);
|
|
const finished = ref(false);
|
|
const page = ref(1);
|
|
const pageSize = ref(10);
|
|
|
|
// 小区列表
|
|
const villageList = ref([
|
|
{ value: '1', label: '阳光花园小区' },
|
|
{ value: '2', label: '翠湖庭院' },
|
|
{ value: '3', label: '金色家园' }
|
|
]);
|
|
|
|
// 状态筛选选项
|
|
const statusList = ref([
|
|
{ label: '全部', value: 'ALL' },
|
|
{ label: '未开始', value: 0 },
|
|
{ label: '进行中', value: 1 },
|
|
{ label: '已结束', value: 2 }
|
|
]);
|
|
|
|
// 模拟问卷列表数据
|
|
const questionnaireList = ref([
|
|
{
|
|
id: 1,
|
|
title: '2024年度物业服务满意度调查',
|
|
village_name: '阳光花园小区',
|
|
type: '满意度调查',
|
|
participant_count: 156,
|
|
start_time: '2024-06-01',
|
|
end_time: '2024-06-30',
|
|
status: 1
|
|
},
|
|
{
|
|
id: 2,
|
|
title: '小区环境改造意见征集',
|
|
village_name: '阳光花园小区',
|
|
type: '意见征集',
|
|
participant_count: 89,
|
|
start_time: '2024-07-01',
|
|
end_time: '2024-07-15',
|
|
status: 0
|
|
},
|
|
{
|
|
id: 3,
|
|
title: '业主委员会换届选举投票',
|
|
village_name: '阳光花园小区',
|
|
type: '投票选举',
|
|
participant_count: 245,
|
|
start_time: '2024-05-01',
|
|
end_time: '2024-05-15',
|
|
status: 2
|
|
},
|
|
{
|
|
id: 3,
|
|
title: '业主委员会换届选举投票',
|
|
village_name: '阳光花园小区',
|
|
type: '投票选举',
|
|
participant_count: 245,
|
|
start_time: '2024-05-01',
|
|
end_time: '2024-05-15',
|
|
status: 2
|
|
},
|
|
{
|
|
id: 3,
|
|
title: '业主委员会换届选举投票',
|
|
village_name: '阳光花园小区',
|
|
type: '投票选举',
|
|
participant_count: 245,
|
|
start_time: '2024-05-01',
|
|
end_time: '2024-05-15',
|
|
status: 2
|
|
}
|
|
]);
|
|
|
|
// 状态颜色配置
|
|
const getStatusColor = (status) => {
|
|
const colorMap = {
|
|
0: '#ff9d00', // 未开始
|
|
1: '#0070F0', // 进行中
|
|
2: '#999999' // 已结束
|
|
};
|
|
return colorMap[status] || '#666666';
|
|
};
|
|
|
|
// 状态背景色配置
|
|
const getStatusBgColor = (status) => {
|
|
const bgColorMap = {
|
|
0: '#fff6e9', // 未开始
|
|
1: '#e8f4ff', // 进行中
|
|
2: '#f5f5f5' // 已结束
|
|
};
|
|
return bgColorMap[status] || '#f5f5f5';
|
|
};
|
|
|
|
// 状态文字配置
|
|
const getStatusText = (status) => {
|
|
const textMap = {
|
|
0: '未开始',
|
|
1: '进行中',
|
|
2: '已结束'
|
|
};
|
|
return textMap[status] || '未知状态';
|
|
};
|
|
|
|
// 处理状态切换
|
|
const handleStatusChange = (status) => {
|
|
activeStatus.value = status;
|
|
resetList();
|
|
loadQuestionnaireList();
|
|
};
|
|
|
|
// 重置列表
|
|
const resetList = () => {
|
|
page.value = 1;
|
|
finished.value = false;
|
|
questionnaireList.value = [];
|
|
};
|
|
|
|
// 加载问卷列表数据
|
|
const loadQuestionnaireList = async () => {
|
|
if (loading.value || finished.value) return;
|
|
|
|
try {
|
|
loading.value = true;
|
|
// 实际项目中这里应该调用API
|
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
if (page.value > 1) {
|
|
finished.value = true;
|
|
} else {
|
|
page.value++;
|
|
}
|
|
loading.value = false;
|
|
} catch (error) {
|
|
console.error('加载问卷列表失败', error);
|
|
Toast.fail('加载失败,请重试');
|
|
loading.value = false;
|
|
}
|
|
};
|
|
|
|
// 创建问卷
|
|
const createQuestionnaire = () => {
|
|
uni.navigateTo({
|
|
url: '/pagesB/questionnaire/edit'
|
|
});
|
|
};
|
|
|
|
// 编辑问卷
|
|
const handleEdit = (item) => {
|
|
uni.navigateTo({
|
|
url: `/pagesB/questionnaire/edit?id=${item.id}`
|
|
});
|
|
};
|
|
|
|
// 删除问卷
|
|
const handleDelete = (item) => {
|
|
uni.showModal({
|
|
title: '提示',
|
|
content: '确定要删除该问卷吗?',
|
|
success: async (res) => {
|
|
if (res.confirm) {
|
|
Toast.loading('删除中...');
|
|
// 实际项目中这里应该调用API
|
|
await new Promise(resolve => setTimeout(resolve, 800));
|
|
Toast.success('删除成功');
|
|
loadQuestionnaireList();
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
// 页面跳转
|
|
const toPages = (item) => {
|
|
switch (item.type) {
|
|
case 'nav':
|
|
uni.navigateBack({
|
|
delta: 1
|
|
});
|
|
break;
|
|
case 'home':
|
|
uni.switchTab({
|
|
url: '/pages/index/index'
|
|
});
|
|
break;
|
|
case 'detail':
|
|
uni.navigateTo({
|
|
url: `/pagesB/questionnaire/detail?id=${item.value.id}`
|
|
});
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
};
|
|
|
|
// 生命周期钩子
|
|
onLoad(() => {
|
|
checkRouteStack();
|
|
loadQuestionnaireList();
|
|
});
|
|
|
|
// 下拉刷新
|
|
onPullDownRefresh(() => {
|
|
resetList();
|
|
loadQuestionnaireList().then(() => {
|
|
uni.stopPullDownRefresh();
|
|
});
|
|
});
|
|
|
|
// 上拉加载
|
|
onReachBottom(() => {
|
|
loadQuestionnaireList();
|
|
});
|
|
|
|
// 监听小区选择变化
|
|
watch(selectedVillage, (newVal) => {
|
|
resetList();
|
|
loadQuestionnaireList();
|
|
});
|
|
|
|
// 新增变量
|
|
const showVillagePicker = ref(false);
|
|
const pickerVillageList = ref([
|
|
[
|
|
{ value: 'ALL', label: '全部小区' },
|
|
{ value: '1', label: '阳光花园小区' },
|
|
{ value: '2', label: '翠湖庭院' },
|
|
{ value: '3', label: '金色家园' }
|
|
]
|
|
]);
|
|
|
|
// 新增方法
|
|
const getVillageName = (value) => {
|
|
const village = villageList.value.find(item => item.value === value);
|
|
return village ? village.label : '未知小区';
|
|
};
|
|
|
|
const handleVillageConfirm = (selectedItem) => {
|
|
selectedVillage.value = selectedItem[0].value;
|
|
resetList();
|
|
loadQuestionnaireList();
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
page {
|
|
background-color: #F7F8FA;
|
|
}
|
|
|
|
.questionnaire-page {
|
|
min-height: 100vh;
|
|
position: relative;
|
|
z-index: 0;
|
|
}
|
|
|
|
.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;
|
|
}
|
|
|
|
.filter-section {
|
|
padding: 20rpx 0 0;
|
|
position: sticky;
|
|
top: calc(var(--status-bar-height) + 88rpx);
|
|
z-index: 9;
|
|
background: rgba(255, 255, 255, 0.85);
|
|
backdrop-filter: blur(10px);
|
|
-webkit-backdrop-filter: blur(10px);
|
|
}
|
|
|
|
.status-tag {
|
|
height: 60rpx;
|
|
padding: 0 30rpx;
|
|
border-radius: 30rpx;
|
|
font-size: 28rpx;
|
|
color: #666;
|
|
background: rgba(255, 255, 255, 0.8);
|
|
margin-right: 20rpx;
|
|
margin-bottom: 16rpx;
|
|
transition: all 0.3s;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border: 2rpx solid transparent;
|
|
|
|
&:active {
|
|
transform: scale(0.95);
|
|
opacity: 0.8;
|
|
}
|
|
}
|
|
|
|
.status-tag.active {
|
|
color: #0070F0;
|
|
background: rgba(255, 255, 255, 0.95);
|
|
font-weight: 500;
|
|
border-color: #0070F0;
|
|
box-shadow: 0 2rpx 12rpx rgba(0, 112, 240, 0.15);
|
|
}
|
|
|
|
.questionnaire-card {
|
|
transition: all 0.3s;
|
|
position: relative;
|
|
z-index: 1;
|
|
|
|
&:active {
|
|
transform: scale(0.98);
|
|
opacity: 0.9;
|
|
}
|
|
|
|
.status-badge {
|
|
padding: 4rpx 20rpx;
|
|
border-radius: 20rpx;
|
|
font-size: 24rpx;
|
|
}
|
|
}
|
|
|
|
.li-bottom-border2 {
|
|
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
|
}
|
|
|
|
.empty-state {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
|
|
.empty-image {
|
|
width: 240rpx;
|
|
height: 240rpx;
|
|
}
|
|
|
|
}
|
|
|
|
.action-btn {
|
|
width: 100rpx !important;
|
|
margin-left: 15rpx !important;
|
|
}
|
|
|
|
|
|
::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;
|
|
}
|
|
|
|
::v-deep .wd-select {
|
|
background-color: transparent !important;
|
|
margin-bottom: 0 !important;
|
|
|
|
.wd-select__value {
|
|
color: #333 !important;
|
|
}
|
|
|
|
.wd-select__label {
|
|
margin-right: 20rpx !important;
|
|
}
|
|
}
|
|
|
|
::v-deep .wd-select__right-icon {
|
|
color: #666 !important;
|
|
}
|
|
|
|
.village-filter {
|
|
.select-box {
|
|
background: rgba(255, 255, 255, 0.9);
|
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
|
|
}
|
|
}
|
|
</style> |