944 lines
22 KiB
Vue
944 lines
22 KiB
Vue
<template>
|
||
<view class="container">
|
||
<view class="status_bar"></view>
|
||
<!-- 顶部导航栏 -->
|
||
<view class="nav-bar">
|
||
<view class="nav-left" @tap="goBack">
|
||
<uni-icons type="left" size="24" color="#000"></uni-icons>
|
||
</view>
|
||
<view class="nav-title">{{navTitle}}</view>
|
||
<view class="nav-right"></view>
|
||
</view>
|
||
|
||
|
||
<!-- 内容区域 -->
|
||
<scroll-view class="scroll-content" scroll-y="true">
|
||
<!-- 选项卡 -->
|
||
<view class="tabs">
|
||
<view
|
||
class="tab-item"
|
||
:class="{ 'active': activeTab === 'detail' }"
|
||
@tap="switchTab('detail')"
|
||
>
|
||
活动详情
|
||
</view>
|
||
<view
|
||
class="tab-item"
|
||
:class="{ 'active': activeTab === 'status' }"
|
||
@tap="switchTab('status')"
|
||
>
|
||
活动实况
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 活动详情内容 -->
|
||
<view v-if="activeTab === 'detail'" class="detail-content">
|
||
<!-- 基本信息 -->
|
||
<view class="info-section">
|
||
<view class="info-item">
|
||
<view class="info-label">报名截止时间</view>
|
||
<view class="info-value">{{formatTime(activityConent.applyDeadline)}}</view>
|
||
</view>
|
||
|
||
<view class="info-item">
|
||
<view class="info-label">活动时间</view>
|
||
<view class="info-value">
|
||
<text>{{formatTime(activityConent.startTime)}} 至 {{formatTime(activityConent.endTime)}}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="info-item">
|
||
<view class="info-label">签到时间</view>
|
||
<view class="info-value">
|
||
<text>{{formatTime(activityConent.signinStartTime)}} 至 {{formatTime(activityConent.signinEndTime)}}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="info-item">
|
||
<view class="info-label">活动地点</view>
|
||
<view class="info-value">{{activityConent.address}}</view>
|
||
</view>
|
||
|
||
<view class="info-item">
|
||
<view class="info-label">活动名额</view>
|
||
<view class="info-value">{{ activityConent.quota == 0 ? '无限名额' : activityConent.quota + '人' }}</view>
|
||
</view>
|
||
|
||
<view class="info-item">
|
||
<view class="info-label">主办方</view>
|
||
<view class="info-value">{{activityConent.organizer}}</view>
|
||
</view>
|
||
|
||
<view class="info-item">
|
||
<view class="info-label">联系人</view>
|
||
<view class="info-value">{{activityConent.contactName}}</view>
|
||
</view>
|
||
|
||
<view class="info-item">
|
||
<view class="info-label">联系电话</view>
|
||
<view class="info-value">{{activityConent.contactPhone}}</view>
|
||
</view>
|
||
|
||
<!-- <view class="info-item">
|
||
<view class="info-label">积分奖励</view>
|
||
<view class="info-value">{{activityConent.pointsReward}}</view>
|
||
</view> -->
|
||
</view>
|
||
|
||
<!-- 招募要求 -->
|
||
<view class="section">
|
||
<view class="section-title">招募要求</view>
|
||
<view class="section-content">
|
||
{{activityConent.requirements}}
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 活动详情 -->
|
||
<view class="section" style="margin-bottom: 40rpx;">
|
||
<view class="section-title">活动详情</view>
|
||
<view class="section-content rich-content">
|
||
<view v-html="activityConent.content"></view>
|
||
<view></view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 活动实况内容 -->
|
||
<view v-else class="status-content">
|
||
<view class="status-header">
|
||
<view class="status-title">
|
||
<image src="http://47.104.199.163:9090/images/activity/liveScene.png" style="width: 38rpx;height: 23rpx;"></image>
|
||
现场实况
|
||
</view>
|
||
<view class="status-count">{{ liveRecords.length }}次</view>
|
||
</view>
|
||
|
||
<!-- 实况列表 -->
|
||
<view class="record-list">
|
||
<view v-for="(record, index) in liveRecords" :key="index" class="record-item">
|
||
<view class="record-user">
|
||
<view class="user-avatar">
|
||
<uni-icons type="person" size="20" color="#999"></uni-icons>
|
||
<!-- <image :src="record.userAvatar" mode="aspectFill"></image> -->
|
||
</view>
|
||
<view class="user-info">
|
||
<view class="user-name">{{record.userNickname || '微信用户'}}</view>
|
||
<view class="record-time">{{ record.createTime }}</view>
|
||
<view class="record-content">{{ record.content }}</view>
|
||
<!-- 图片展示 -->
|
||
<view v-if="record.images && record.images.length > 0" class="record-images">
|
||
<view
|
||
v-for="(img, imgIndex) in record.images"
|
||
:key="imgIndex"
|
||
class="image-item"
|
||
:style="{ backgroundImage: `url(${img})` }"
|
||
>
|
||
<image :src="img" style="width: 100%;height: 100%;"></image>
|
||
</view>
|
||
|
||
</view>
|
||
</view>
|
||
|
||
</view>
|
||
|
||
|
||
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
|
||
<!-- 底部操作栏 -->
|
||
<view class="bottom-bar">
|
||
<!-- 活动已结束,显示不可点击的结束按钮 -->
|
||
<button
|
||
v-if="isActivityEnded && activeTab === 'detail'"
|
||
class="action-btn ended-btn"
|
||
disabled
|
||
>
|
||
报名已结束
|
||
</button>
|
||
|
||
<!-- 未报名状态显示报名按钮 -->
|
||
<button
|
||
v-if="!isActivityEnded && !hasSigned && activeTab === 'detail'"
|
||
class="action-btn signup-btn"
|
||
@tap="handleSignup"
|
||
>
|
||
报名
|
||
</button>
|
||
|
||
<!-- 已报名状态显示取消报名按钮(活动已结束则隐藏) -->
|
||
<button
|
||
v-if="hasSigned && !isActivityEnded && activeTab === 'detail'"
|
||
class="action-btn cancel-btn"
|
||
@tap="handleCancelSignup"
|
||
>
|
||
取消报名
|
||
</button>
|
||
|
||
<!-- 活动实况页显示添加实况按钮 -->
|
||
<button class="action-btn add-status-btn" v-if="hasSigned && isActivityStarted && !isActivityEnded && activeTab === 'status'" @click="liveClick">添加实况</button>
|
||
</view>
|
||
|
||
<!-- 报名确认模态框 -->
|
||
<view class="modal" :class="{ 'show': showSignupModal }">
|
||
<view class="modal-content">
|
||
<view class="modal-header">报名确认</view>
|
||
<view class="modal-body">
|
||
<view>{{navTitle}}</view>
|
||
<view class="modal-tip">报名后请按时参加活动</view>
|
||
</view>
|
||
<view class="modal-footer">
|
||
<view class="modal-btn cancel" @tap="hideSignupModal">取消</view>
|
||
<view class="modal-btn confirm" @tap="confirmSignup">确定</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 取消报名确认模态框 -->
|
||
<view class="modal" :class="{ 'show': showCancelModal }">
|
||
<view class="modal-content">
|
||
<view class="modal-header">取消报名</view>
|
||
<view class="modal-body">
|
||
<view>{{navTitle}}</view>
|
||
<view class="modal-tip">取消后如需参加需重新报名</view>
|
||
</view>
|
||
<view class="modal-footer">
|
||
<view class="modal-btn cancel" @tap="hideCancelModal">再想想</view>
|
||
<view class="modal-btn confirm" @tap="confirmCancel">确定取消</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 提示信息 -->
|
||
<view class="toast" :class="{ 'show': showToast }">
|
||
{{ toastMessage }}
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, onMounted, computed } from 'vue'
|
||
import {onLoad,onUnload} from '@dcloudio/uni-app'
|
||
import moment from 'moment'
|
||
import {getActivityDetails,confirmResident,cancelActivity,getActivityLive} from '../api/api.js'
|
||
|
||
// 响应式数据
|
||
const activeTab = ref('detail') // 'detail' 或 'status'
|
||
const hasSigned = ref(false) // 是否已报名
|
||
const showSignupModal = ref(false)
|
||
const showCancelModal = ref(false)
|
||
const showToast = ref(false)
|
||
const toastMessage = ref('')
|
||
const newStatusContent = ref('')
|
||
const navTitle = ref('')
|
||
const curActivityId = ref('') // 当前活动的id
|
||
const activityConent = ref({}) // 获取详情数据
|
||
// 实况记录数据
|
||
const liveRecords = ref([])
|
||
|
||
// 判断活动是否已结束
|
||
const isActivityEnded = computed(() => {
|
||
// 优先使用列表页传入的statusText
|
||
if (statusTextFromParam.value === '已结束') return true
|
||
// 其次使用后端详情接口返回的状态字段
|
||
// if (activityConent.value.statusText === '已结束') return true
|
||
// 报名截止时间已过
|
||
if (activityConent.value.applyDeadline && moment().isAfter(moment(activityConent.value.applyDeadline))) return true
|
||
// 活动结束时间已过
|
||
if (activityConent.value.endTime && moment().isAfter(moment(activityConent.value.endTime))) return true
|
||
return false
|
||
})
|
||
|
||
// 判断活动是否已开始(开始时间已到)
|
||
const isActivityStarted = computed(() => {
|
||
if (!activityConent.value.startTime) return false
|
||
return moment().isSameOrAfter(moment(activityConent.value.startTime))
|
||
})
|
||
|
||
|
||
|
||
|
||
// ========方法=====
|
||
// 时间格式化函数
|
||
const formatTime = (timeStr) => {
|
||
if (!timeStr) return ''
|
||
return moment(timeStr).format('YYYY-MM-DD HH:mm')
|
||
}
|
||
|
||
// ---接收参数---
|
||
const statusTextFromParam = ref('') // 从列表页传入的活动状态文本
|
||
onLoad((option) =>{
|
||
navTitle.value = option?.title
|
||
curActivityId.value = option?.id
|
||
statusTextFromParam.value = option?.statusText || ''
|
||
// 处理applied参数
|
||
hasSigned.value = option?.applied === 'true'
|
||
// console.log('hasSigned.value',hasSigned.value)
|
||
// 2. 初始化数据
|
||
if (curActivityId.value) {
|
||
getActivityDetailsData(curActivityId.value) // 获取活动详情
|
||
// getLiveActivityList() // 获取实况列表
|
||
}
|
||
|
||
// 3. 监听实况列表刷新事件
|
||
uni.$on('refreshLiveList', () => {
|
||
getLiveActivityList()
|
||
})
|
||
})
|
||
|
||
// 获取列表详情
|
||
const getActivityDetailsData = async (id) =>{
|
||
try{
|
||
let params = {villageId:uni.getStorageSync('currentVillageId')}
|
||
const res = await getActivityDetails(id,params)
|
||
if(res.code === 200){
|
||
activityConent.value = res.data || {}
|
||
}else{
|
||
activityConent.value = {}
|
||
uni.showToast({
|
||
title: res.msg ||"详情获取失败",
|
||
icon:"error"
|
||
})
|
||
}
|
||
}catch(err){
|
||
uni.showToast({
|
||
title: "网络异常",
|
||
icon:"none"
|
||
})
|
||
// console.error('获取活动详情失败:', err)
|
||
}
|
||
}
|
||
|
||
|
||
// 切换选项卡
|
||
const switchTab = (tab) => {
|
||
activeTab.value = tab
|
||
if (tab === 'status') {
|
||
getLiveActivityList()
|
||
}
|
||
}
|
||
|
||
// 返回上一页
|
||
const goBack = () => {
|
||
uni.navigateBack()
|
||
}
|
||
|
||
// 处理报名
|
||
const handleSignup = () => {
|
||
showSignupModal.value = true
|
||
}
|
||
|
||
// 隐藏报名模态框
|
||
const hideSignupModal = () => {
|
||
showSignupModal.value = false
|
||
}
|
||
|
||
// 确认报名
|
||
const confirmSignup = () => {
|
||
confirmRegist()
|
||
hideSignupModal()
|
||
}
|
||
|
||
// 确认报名接口
|
||
const confirmRegist = async () =>{
|
||
try{
|
||
let params = {villageId:uni.getStorageSync('currentVillageId')}
|
||
const res = await confirmResident(curActivityId.value,params)
|
||
if(res.code === 200){
|
||
hasSigned.value = true // 报名成功后更新状态
|
||
uni.showToast({
|
||
title: '报名成功',
|
||
icon:"success"
|
||
})
|
||
setTimeout(() =>{
|
||
uni.$emit('refreshActivityList', {
|
||
activityId: curActivityId.value,
|
||
isSigned: true
|
||
})
|
||
uni.navigateBack()
|
||
},1500)
|
||
}else{
|
||
uni.showToast({
|
||
title: res.msg || "报名失败",
|
||
icon:"error"
|
||
})
|
||
}
|
||
}catch(err){
|
||
uni.showToast({
|
||
title: err.msg ||"网络异常",
|
||
icon:"none"
|
||
})
|
||
// console.error('报名失败:', err)
|
||
}
|
||
}
|
||
|
||
// 处理取消报名
|
||
const handleCancelSignup = () => {
|
||
showCancelModal.value = true
|
||
}
|
||
|
||
// 隐藏取消报名模态框
|
||
const hideCancelModal = () => {
|
||
showCancelModal.value = false
|
||
}
|
||
|
||
// 确认取消报名
|
||
const confirmCancel = () => {
|
||
cancelActivityData()
|
||
hideCancelModal()
|
||
}
|
||
// 确认取消报名接口
|
||
const cancelActivityData = async () =>{
|
||
try{
|
||
let params = {villageId:uni.getStorageSync('currentVillageId')}
|
||
const res = await cancelActivity(curActivityId.value,params)
|
||
if(res.code === 200){
|
||
hasSigned.value = false // 报名成功后更新状态
|
||
uni.showToast({
|
||
title: '取消报名成功',
|
||
icon:"success"
|
||
})
|
||
setTimeout(() =>{
|
||
uni.$emit('refreshActivityList', {
|
||
activityId: curActivityId.value,
|
||
isSigned: false
|
||
})
|
||
uni.navigateBack()
|
||
},1500)
|
||
}else{
|
||
uni.showToast({
|
||
title: res.msg || "取消报名失败",
|
||
icon:"error"
|
||
})
|
||
}
|
||
}catch(err){
|
||
uni.showToast({
|
||
title: err.msg ||"网络异常",
|
||
icon:"none"
|
||
})
|
||
}
|
||
}
|
||
|
||
|
||
// -----获取活动实况列表----
|
||
const getLiveActivityList = async () =>{
|
||
try{
|
||
let params = {villageId:uni.getStorageSync('currentVillageId')}
|
||
const res = await getActivityLive(curActivityId.value,params)
|
||
if(res.code === 200){
|
||
liveRecords.value = res.rows || []
|
||
}else{
|
||
liveRecords.value = []
|
||
uni.showToast({
|
||
title: res.msg ||"实况列表获取失败",
|
||
icon:"error"
|
||
})
|
||
}
|
||
}catch(err){
|
||
uni.showToast({
|
||
title: err.msg ||"网络异常",
|
||
icon:"none"
|
||
})
|
||
}
|
||
}
|
||
|
||
// 添加实况跳转
|
||
const liveClick = () =>{
|
||
uni.navigateTo({
|
||
url:`/pageSubPack/activity-list/live-add?id=${curActivityId.value}`
|
||
})
|
||
}
|
||
|
||
// 监听实况列表刷新
|
||
|
||
// ---------
|
||
onUnload(() => {
|
||
uni.$off('refreshLiveList')
|
||
})
|
||
|
||
// 显示提示信息
|
||
const showToastMessage = (message) => {
|
||
toastMessage.value = message
|
||
showToast.value = true
|
||
|
||
setTimeout(() => {
|
||
showToast.value = false
|
||
}, 2000)
|
||
}
|
||
|
||
|
||
|
||
</script>
|
||
|
||
<style scoped>
|
||
/* 页面容器 */
|
||
.container {
|
||
height: 100vh;
|
||
display: flex;
|
||
flex-direction: column;
|
||
background: linear-gradient(to bottom left, #E2F0FF 0%, #ffffff 50%);
|
||
overflow: hidden;
|
||
}
|
||
/* 状态栏 */
|
||
.status_bar {
|
||
height: 46px;
|
||
width: 100%;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* 顶部导航栏 */
|
||
.nav-bar {
|
||
height: 90rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 0 30rpx;
|
||
color: #fff;
|
||
position: sticky;
|
||
top: 44px;
|
||
z-index: 100;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.nav-left {
|
||
width: 60rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.nav-title {
|
||
flex: 1;
|
||
text-align: left;
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
padding: 0 20rpx;
|
||
color: #000;
|
||
}
|
||
|
||
.nav-right {
|
||
width: 60rpx;
|
||
}
|
||
|
||
/* 滚动内容区域 */
|
||
.scroll-content {
|
||
flex: 1;
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
/* 选项卡 */
|
||
.tabs {
|
||
display: flex;
|
||
border-bottom: 1rpx solid #e9ecef;
|
||
}
|
||
|
||
.tab-item {
|
||
text-align: center;
|
||
padding: 30rpx 35rpx;
|
||
font-size: 32rpx;
|
||
color: #666;
|
||
position: relative;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.tab-item.active {
|
||
color: #007AFF;
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* 活动详情内容 */
|
||
.detail-content {
|
||
padding: 0;
|
||
}
|
||
|
||
/* 基本信息区域 */
|
||
.info-section {
|
||
border-radius: 16rpx;
|
||
padding: 30rpx 40rpx;
|
||
}
|
||
|
||
.info-item {
|
||
display: flex;
|
||
margin-bottom: 30rpx;
|
||
padding-bottom: 30rpx;
|
||
border-bottom: 1rpx solid #f0f0f0;
|
||
}
|
||
|
||
.info-item:last-child {
|
||
margin-bottom: 0;
|
||
padding-bottom: 0;
|
||
border-bottom: none;
|
||
}
|
||
|
||
.info-label {
|
||
width: 220rpx;
|
||
font-size: 28rpx;
|
||
color: #222;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.info-value {
|
||
flex: 1;
|
||
font-size: 28rpx;
|
||
color: #222;
|
||
font-weight: 500;
|
||
line-height: 1.4;
|
||
text-align: right;
|
||
white-space: pre-wrap;
|
||
word-break: break-all;
|
||
}
|
||
|
||
/* 区块样式 */
|
||
.section {
|
||
border-radius: 16rpx;
|
||
padding: 30rpx 40rpx;
|
||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 32rpx;
|
||
color: #333;
|
||
font-weight: 600;
|
||
margin-bottom: 20rpx;
|
||
padding-bottom: 20rpx;
|
||
border-bottom: 1rpx solid #f0f0f0;
|
||
}
|
||
|
||
.section-content {
|
||
font-size: 28rpx;
|
||
white-space: pre-wrap;
|
||
word-break: break-all;
|
||
color: #666;
|
||
line-height: 1.6;
|
||
overflow-x: hidden;
|
||
}
|
||
|
||
.section-content view {
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.section-content view:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
/* 活动实况内容 */
|
||
.status-content {
|
||
padding: 30rpx;
|
||
}
|
||
|
||
/* 实况头部 */
|
||
.status-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
border-radius: 16rpx;
|
||
padding: 30rpx;
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.status-title {
|
||
font-size: 32rpx;
|
||
color: #333;
|
||
font-weight: 600;
|
||
border-bottom: 1rpx solid #f0f0f0;
|
||
}
|
||
|
||
.status-count {
|
||
padding: 8rpx 20rpx;
|
||
color: #666;
|
||
font-size: 28rpx;
|
||
border-radius: 20rpx;
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* 实况列表 */
|
||
.record-list {
|
||
border-radius: 16rpx;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.record-item {
|
||
padding: 30rpx;
|
||
border-bottom: 1rpx solid #f0f0f0;
|
||
}
|
||
|
||
.record-user {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
justify-content: flex-start;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.user-avatar {
|
||
width: 80rpx;
|
||
height: 80rpx;
|
||
border-radius: 50%;
|
||
background-color: #f8f9fa;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-right: 20rpx;
|
||
border: 1rpx solid #e9ecef;
|
||
}
|
||
|
||
.user-info {
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: flex-start;
|
||
}
|
||
|
||
.user-name {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
font-weight: 500;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.record-time {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
}
|
||
|
||
.record-content {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
line-height: 1.5;
|
||
margin: 25rpx 0rpx;
|
||
}
|
||
|
||
/* 实况图片 */
|
||
.record-images {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 20rpx;
|
||
}
|
||
|
||
.image-item {
|
||
width: 150rpx;
|
||
height: 150rpx;
|
||
border-radius: 12rpx;
|
||
background-color: #f8f9fa;
|
||
background-size: cover;
|
||
background-position: center;
|
||
background-repeat: no-repeat;
|
||
border: 1rpx solid #e9ecef;
|
||
}
|
||
|
||
/* 底部操作栏 */
|
||
.bottom-bar {
|
||
padding: 20rpx 30rpx 40rpx;
|
||
background-color: #fff;
|
||
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.05);
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.action-btn {
|
||
height: 100rpx;
|
||
line-height: 100rpx;
|
||
border-radius: 12rpx;
|
||
font-size: 34rpx;
|
||
font-weight: 600;
|
||
width: 100%;
|
||
border: none;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.signup-btn {
|
||
background: linear-gradient(135deg, #007AFF 0%, #0056cc 100%);
|
||
color: #fff;
|
||
box-shadow: 0 8rpx 30rpx rgba(0, 122, 255, 0.3);
|
||
}
|
||
|
||
.signup-btn:active {
|
||
background: linear-gradient(135deg, #0056cc 0%, #004199 100%);
|
||
transform: translateY(2rpx);
|
||
box-shadow: 0 4rpx 20rpx rgba(0, 122, 255, 0.2);
|
||
}
|
||
|
||
.cancel-btn {
|
||
background: linear-gradient(135deg, #fff 0%, #f8f9fa 100%);
|
||
color: #666;
|
||
border: 1rpx solid #e9ecef;
|
||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
|
||
}
|
||
|
||
.cancel-btn:active {
|
||
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
||
transform: translateY(2rpx);
|
||
}
|
||
|
||
.add-status-btn {
|
||
background: linear-gradient(135deg, #007AFF 0%, #0056cc 100%);
|
||
color: #fff;
|
||
box-shadow: 0 8rpx 30rpx rgba(0, 122, 255, 0.3);
|
||
}
|
||
|
||
.add-status-btn:active {
|
||
background: linear-gradient(135deg, #0056cc 0%, #004199 100%);
|
||
transform: translateY(2rpx);
|
||
box-shadow: 0 4rpx 20rpx rgba(0, 122, 255, 0.2);
|
||
}
|
||
|
||
.ended-btn {
|
||
background: linear-gradient(135deg, #ccc 0%, #999 100%);
|
||
color: #fff;
|
||
opacity: 0.7;
|
||
}
|
||
|
||
/* 模态框 */
|
||
.modal {
|
||
display: none;
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background-color: rgba(0, 0, 0, 0.5);
|
||
z-index: 1000;
|
||
align-items: center;
|
||
justify-content: center;
|
||
backdrop-filter: blur(5rpx);
|
||
}
|
||
|
||
.modal.show {
|
||
display: flex;
|
||
animation: fadeIn 0.3s;
|
||
}
|
||
|
||
@keyframes fadeIn {
|
||
from { opacity: 0; }
|
||
to { opacity: 1; }
|
||
}
|
||
|
||
.modal-content {
|
||
background-color: #fff;
|
||
border-radius: 20rpx;
|
||
width: 600rpx;
|
||
overflow: hidden;
|
||
box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.2);
|
||
animation: slideUp 0.3s;
|
||
}
|
||
|
||
@keyframes slideUp {
|
||
from { transform: translateY(100rpx); opacity: 0; }
|
||
to { transform: translateY(0); opacity: 1; }
|
||
}
|
||
|
||
.modal-header {
|
||
padding: 40rpx 30rpx 20rpx;
|
||
text-align: center;
|
||
font-size: 36rpx;
|
||
font-weight: 600;
|
||
color: #333;
|
||
border-bottom: 1rpx solid #f0f0f0;
|
||
}
|
||
|
||
.modal-body {
|
||
padding: 40rpx 30rpx;
|
||
text-align: center;
|
||
font-size: 30rpx;
|
||
color: #666;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.modal-tip {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
margin-top: 20rpx;
|
||
}
|
||
|
||
.modal-footer {
|
||
display: flex;
|
||
border-top: 1rpx solid #f0f0f0;
|
||
}
|
||
|
||
.modal-btn {
|
||
flex: 1;
|
||
padding: 30rpx 0;
|
||
text-align: center;
|
||
font-size: 32rpx;
|
||
font-weight: 500;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.modal-btn.cancel {
|
||
color: #666;
|
||
border-right: 1rpx solid #f0f0f0;
|
||
}
|
||
|
||
.modal-btn.cancel:active {
|
||
background-color: #f8f9fa;
|
||
}
|
||
|
||
.modal-btn.confirm {
|
||
color: #007AFF;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.modal-btn.confirm:active {
|
||
background-color: #f0f7ff;
|
||
}
|
||
|
||
/* 提示信息 */
|
||
.toast {
|
||
position: fixed;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
background-color: rgba(0, 0, 0, 0.8);
|
||
color: #fff;
|
||
padding: 20rpx 40rpx;
|
||
border-radius: 10rpx;
|
||
font-size: 28rpx;
|
||
z-index: 1100;
|
||
display: none;
|
||
backdrop-filter: blur(10rpx);
|
||
}
|
||
|
||
.toast.show {
|
||
display: block;
|
||
animation: toastFade 2s;
|
||
}
|
||
|
||
@keyframes toastFade {
|
||
0% { opacity: 0; transform: translate(-50%, -40%); }
|
||
20% { opacity: 1; transform: translate(-50%, -50%); }
|
||
80% { opacity: 1; transform: translate(-50%, -50%); }
|
||
100% { opacity: 0; transform: translate(-50%, -60%); }
|
||
}
|
||
|
||
/* 适配不同屏幕 */
|
||
@media (min-width: 768px) {
|
||
.container {
|
||
max-width: 500px;
|
||
margin: 0 auto;
|
||
}
|
||
}
|
||
</style>
|
||
|
||
<style lang="scss">
|
||
/* 富文本(v-html)内图片自适应 — 非 scoped,否则穿透不到 v-html 内容 */
|
||
.section-content {
|
||
img {
|
||
max-width: 100% !important;
|
||
height: auto !important;
|
||
display: block;
|
||
}
|
||
}
|
||
.rich-content {
|
||
white-space: normal !important;
|
||
word-break: normal !important;
|
||
overflow: hidden;
|
||
/* 富文本内嵌的所有元素不超出容器 */
|
||
* {
|
||
max-width: 100% !important;
|
||
box-sizing: border-box !important;
|
||
}
|
||
}
|
||
</style> |