473 lines
11 KiB
Vue
473 lines
11 KiB
Vue
<!-- 小区活动详情页 -->
|
||
<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>
|