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

473 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!-- 小区活动详情页 -->
<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>