fjrcloud-community-app/pages/sub/activity/detail.vue

473 lines
11 KiB
Vue
Raw Normal View History

<!-- 小区活动详情页 -->
<template>
<s-layout title="小区活动">
<view class="activity-detail-page">
<!-- 顶部Banner轮播 -->
<swiper class="banner-swiper" :autoplay="true" :interval="3000" :circular="true" indicator-dots indicator-color="rgba(255,255,255,0.4)" active-color="#FFFFFF">
<swiper-item v-for="(img, index) in activityInfo.banners" :key="index">
<image class="banner-img" :src="img" mode="aspectFill" />
</swiper-item>
</swiper>
<!-- Tab 切换栏 -->
<view class="tab-bar">
<view
class="tab-item"
:class="{ active: currentTab === 'basic' }"
@tap="currentTab = 'basic'"
>
<text class="tab-text">基本信息</text>
</view>
<view
class="tab-item"
:class="{ active: currentTab === 'intro' }"
@tap="currentTab = 'intro'"
>
<text class="tab-text">活动简介</text>
</view>
</view>
<!-- 内容区域 - 独立滚动 -->
<scroll-view class="detail-scroll" scroll-y :style="{ height: scrollViewHeight + 'px' }">
<view class="detail-content">
<!-- 基本信息 -->
<template v-if="currentTab === 'basic'">
<!-- 活动标题 -->
<view class="activity-title">{{ activityInfo.title }}</view>
<!-- 地址 -->
<view class="info-row location-row">
<text class="sicon-location"></text>
<text class="location-text">{{ activityInfo.location }}</text>
<text class="sicon-right navigation-icon" @tap="openNavigation"></text>
</view>
<!-- 服务类别 -->
<view class="info-row tag-row">
<text class="label-text">服务类别</text>
<view class="tag-list">
<text
class="tag-item"
v-for="(tag, index) in activityInfo.serviceTypes"
:key="index"
>{{ tag }}</text>
</view>
</view>
<!-- 服务对象 -->
<view class="info-row tag-row">
<text class="label-text">服务对象</text>
<view class="tag-list">
<text
class="tag-item target-tag"
v-for="(tag, index) in activityInfo.targetAudience"
:key="index"
>{{ tag }}</text>
</view>
</view>
<!-- 报名日期 -->
<view class="info-row text-row">
<text class="label-text">报名日期</text>
<text class="value-text">{{ activityInfo.registerDate }}</text>
</view>
<!-- 活动日期 -->
<view class="info-row text-row">
<text class="label-text">活动日期</text>
<text class="value-text">{{ activityInfo.activityDate }}</text>
</view>
<!-- 人数上限 -->
<view class="info-row text-row">
<text class="sicon-person"></text>
<text class="label-text">人数上限</text>
<text class="value-text">{{ activityInfo.maxPeople }}</text>
</view>
<!-- 联系人 -->
<view class="info-row contact-row" @tap="callPhone">
<text class="sicon-phone"></text>
<text class="contact-name">{{ activityInfo.contactName }}</text>
<text class="contact-phone">{{ activityInfo.contactPhone }}</text>
</view>
</template>
<!-- 活动简介 -->
<template v-if="currentTab === 'intro'">
<view class="intro-content">
<mp-html :content="activityInfo.introduction"></mp-html>
</view>
</template>
<!-- 底部占位防止内容被底部按钮遮挡 -->
<view class="bottom-placeholder"></view>
</view>
</scroll-view>
<!-- 底部固定按钮栏 -->
<view class="bottom-bar">
<view class="registered-btn" @tap="showRegisteredList">
<text class="registered-text">已有{{ activityInfo.registeredCount }}人报名</text>
</view>
<view class="back-btn" @tap="goBack">
<text class="back-text">返回</text>
</view>
</view>
</view>
</s-layout>
</template>
<script setup>
import { ref, onMounted, nextTick, getCurrentInstance } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
// 当前Tab
const currentTab = ref('basic');
// 列表滚动区域高度单位px
const scrollViewHeight = ref(0);
// 活动详情数据
const activityInfo = ref({
id: null,
title: '',
banners: [],
location: '',
serviceTypes: [],
targetAudience: [],
registerDate: '',
activityDate: '',
maxPeople: 0,
contactName: '',
contactPhone: '',
registeredCount: 0,
introduction: ''
});
// 页面加载
onLoad((options) => {
if (options.id) {
loadActivityDetail(options.id);
}
});
// 渲染完成后计算scroll-view高度
onMounted(() => {
const instance = getCurrentInstance();
nextTick(() => {
const sysInfo = uni.getSystemInfoSync();
uni.createSelectorQuery()
.in(instance)
.select('.tab-bar')
.boundingClientRect((rect) => {
if (rect) {
// scroll-view高度 = 屏幕可用高度 - tab栏高度 - tab栏距顶部距离 - 底部按钮高度(120rpx)
scrollViewHeight.value = sysInfo.windowHeight - rect.height - rect.top - 60;
}
})
.exec();
});
});
// 加载活动详情
async function loadActivityDetail(id) {
// TODO: 调用API获取活动详情
// 模拟数据
activityInfo.value = {
id: id,
title: 'xxxxxxxxxxxxxxxxx活动',
banners: [
'/static/img/guest.png',
'/static/img/login_img.png'
],
location: '福清市音西街道',
serviceTypes: ['社区服务', '敬老服务', '助残服务', '关爱儿童'],
targetAudience: ['儿童', '孤寡老人', '残障人士', '优抚对象'],
registerDate: '2025/06/30-2025/07/01',
activityDate: '2025/07/05 12:00-2025/07/05 18:00',
maxPeople: 100,
contactName: '王德福',
contactPhone: '13322471131',
registeredCount: 37,
introduction: `<p>本次活动旨在丰富社区居民文化生活,增进邻里关系,共建和谐社区。</p><p>欢迎各位居民踊跃参与!</p>`
};
}
// 打开导航
function openNavigation() {
// TODO: 调用地图导航
}
// 拨打电话
function callPhone() {
if (activityInfo.value.contactPhone) {
uni.makePhoneCall({
phoneNumber: activityInfo.value.contactPhone
});
}
}
// 显示已报名列表
function showRegisteredList() {
// TODO: 弹出已报名人员列表或跳转页面
uni.showToast({
title: '查看报名名单',
icon: 'none'
});
}
// 返回上一页
function goBack() {
uni.navigateBack();
}
</script>
<style lang="scss" scoped>
/* 页面容器 */
.activity-detail-page {
position: relative;
min-height: 100vh;
}
/* 顶部Banner轮播 */
.banner-swiper {
width: 100%;
height: 400rpx;
}
.banner-img {
width: 100%;
height: 400rpx;
display: block;
}
/* Tab 切换栏 */
.tab-bar {
display: flex;
align-items: center;
background-color: #FFFFFF;
padding: 0 32rpx;
border-bottom: 1rpx solid #F0F0F0;
.tab-item {
flex: 1;
text-align: center;
padding: 28rpx 0;
position: relative;
.tab-text {
font-size: 30rpx;
color: #999999;
transition: all 0.3s;
}
&.active {
&::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 120rpx;
height: 4rpx;
background: #FA7E49;
border-radius: 2rpx;
}
}
&.active .tab-text {
font-size: 30rpx;
font-weight: 600;
color: #333333;
}
}
}
/* 内容滚动区域 */
.detail-scroll {
/* 高度由JS动态绑定 */
}
/* 详情内容 */
.detail-content {
background-color: #FFFFFF;
}
/* 活动标题 */
.activity-title {
padding: 32rpx 32rpx 16rpx;
font-size: 34rpx;
font-weight: 600;
color: #FA7E49;
line-height: 1.5;
}
/* 信息行 */
.info-row {
display: flex;
align-items: flex-start;
padding: 20rpx 32rpx;
.sicon-location,
.sicon-phone,
.sicon-person {
font-size: 32rpx;
color: #666666;
margin-right: 8rpx;
margin-top: 2rpx;
}
}
/* 地址行 */
.location-row {
align-items: center;
.location-text {
font-size: 28rpx;
color: #333333;
flex: 1;
}
.navigation-icon {
font-size: 36rpx;
color: #FA7E49;
flex-shrink: 0;
}
}
/* 标签行 */
.tag-row {
align-items: flex-start;
.label-text {
font-size: 28rpx;
color: #333333;
flex-shrink: 0;
line-height: 1.8;
}
}
.tag-list {
display: flex;
flex-wrap: wrap;
gap: 12rpx;
flex: 1;
}
.tag-item {
font-size: 24rpx;
color: #FA7E49;
border: 1rpx solid #FA7E49;
border-radius: 8rpx;
padding: 6rpx 16rpx;
background-color: rgba(250, 126, 73, 0.04);
line-height: 1.4;
}
.target-tag {
color: #FA7E49;
border: 1rpx solid #FA7E49;
}
/* 文本行 */
.text-row {
.label-text {
font-size: 28rpx;
color: #333333;
flex-shrink: 0;
}
.value-text {
font-size: 28rpx;
color: #333333;
}
}
/* 联系人行 */
.contact-row {
align-items: center;
&:active {
opacity: 0.7;
}
.contact-name {
font-size: 28rpx;
color: #333333;
margin-left: 8rpx;
margin-right: 24rpx;
}
.contact-phone {
font-size: 28rpx;
color: #333333;
}
}
/* 活动简介内容 */
.intro-content {
padding: 32rpx;
}
/* 底部占位 */
.bottom-placeholder {
height: 140rpx;
}
/* 底部固定按钮栏 */
.bottom-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
display: flex;
align-items: center;
gap: 24rpx;
padding: 20rpx 32rpx;
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
background-color: #FFFFFF;
box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.06);
.registered-btn {
flex: 1;
height: 88rpx;
border: 2rpx solid #FA7E49;
border-radius: 44rpx;
display: flex;
align-items: center;
justify-content: center;
&:active {
opacity: 0.85;
}
.registered-text {
font-size: 30rpx;
color: #FA7E49;
font-weight: 500;
}
}
.back-btn {
width: 240rpx;
height: 88rpx;
background-color: #FA7E49;
border-radius: 44rpx;
display: flex;
align-items: center;
justify-content: center;
&:active {
opacity: 0.85;
}
.back-text {
font-size: 30rpx;
color: #FFFFFF;
font-weight: 500;
}
}
}
</style>