Files
uniapp-im-shop/pages/audience/components/activity-info.vue

415 lines
8.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup>
import { ref, watch, reactive } from 'vue'
import { useUI } from '../../../utils/use-ui'
import { confirmLiveActivity } from '@/api/tui-kit'
const { showToast } = useUI()
const showData = defineModel('info', {
type: Object,
default: () => ({})
})
const props = defineProps({
modelValue: {
type: Boolean,
default: false
},
/**
* 直播间ID
*/
roomId: {
type: String,
default: ''
},
// // 显示数据
// showData: {
// type: Object,
// default: () => ({})
// }
})
const loading = ref(true)
// 响应式倒计时状态
const countdown = ref({ type: 'second', value: 0 })
const formData = reactive({
title: '',
rewardValue: '',
endTime: '',
activityId: '',
/** 用户是否参与活动 */
isParticipated: false
})
// 定时器引用
let timer = null
/**
* 根据结束时间返回倒计时(>=1分钟时按分钟倒计时<1分钟时按秒倒计时
*/
const getSmartCountdown = endTime => {
// 兼容 Safari将空格替换为 'T' 以符合 ISO 8601
const end = new Date(endTime.replace(' ', 'T'))
const now = new Date()
const diffMs = end - now
if (diffMs <= 0) {
return { type: 'second', value: 0 }
}
const totalSeconds = Math.floor(diffMs / 1000)
return { type: 'second', value: totalSeconds }
}
/** 启动倒计时 */
const startCountdown = () => {
// 先清除可能存在的旧定时器
stopCountdown()
const update = () => {
countdown.value = getSmartCountdown(formData.endTime)
// 如果已结束可选择是否继续更新这里仍每秒更新但值为0
if (countdown.value.value <= 0) {
showData.value = {}
close()
}
}
update() // 立即更新一次
timer = setInterval(update, 1000)
}
/** 停止倒计时 */
const stopCountdown = () => {
if (timer) {
clearInterval(timer)
timer = null
}
}
const getData = async () => {
loading.value = true
formData.isParticipated = showData.value.isParticipated
formData.endTime = showData.value.endTime
formData.title = showData.value.title
formData.rewardValue = showData.value.rewardValue
formData.activityId = showData.value.activityId
startCountdown()
loading.value = false
}
watch(
() => props.modelValue,
newVal => {
if (newVal) {
getData()
} else {
stopCountdown()
}
}
)
const emit = defineEmits(['update:modelValue'])
const close = () => {
emit('update:modelValue', false)
}
const submitForm = async () => {
const data = {
activityId: formData.activityId,
roomId: showData.value.roomId
}
console.log('确认活动:', data)
await confirmLiveActivity(data)
await showToast('参与活动成功', 'success')
showData.value.isParticipated = true
close()
}
</script>
<template>
<view class="bottom-drawer-container" v-if="modelValue">
<view class="drawer-overlay" @tap="close"></view>
<view
class="bottom-drawer"
:class="{ 'drawer-open': modelValue }"
@click.stop
>
<view class="drawer-header">
<text class="drawer-title">{{ formData.title }}</text>
<text v-if="formData.isParticipated" class="drawer-done" @tap="close">关闭</text>
<text v-if="!formData.isParticipated" class="drawer-done" @tap="submitForm">确定参与</text>
</view>
<view class="setting-item">
<text class="setting-label">结束时间</text>
<view class="live-list-quick-join">
<text class="text">{{ countdown.value }} </text>
</view>
</view>
<view class="setting-item">
<text class="setting-label">奖励值积分</text>
<view class="live-list-quick-join">
<text class="text" style="color: #e6431a">
{{ formData.rewardValue }} 积分
</text>
</view>
</view>
</view>
</view>
</template>
<style lang="scss" scoped>
.bottom-drawer-container {
position: fixed;
bottom: 0;
left: 0;
right: 0;
top: 0;
z-index: 1000;
}
.drawer-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.4);
}
.bottom-drawer {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: rgba(34, 38, 46, 1);
border-top-left-radius: 32rpx;
border-top-right-radius: 32rpx;
transform: translateY(100%);
transition-property: transform;
transition-duration: 0.3s;
transition-timing-function: ease;
padding: 32rpx;
}
.drawer-open {
transform: translateY(0);
}
.drawer-header {
flex-direction: row;
justify-content: space-between;
align-items: center;
margin-bottom: 32rpx;
}
.drawer-title {
font-size: 32rpx;
color: #ffffff;
font-weight: 500;
}
.drawer-done {
font-size: 32rpx;
color: #2b65fb;
}
.setting-item {
flex-direction: row;
justify-content: space-between;
align-items: center;
height: 88rpx;
border-bottom-width: 1px;
border-bottom-color: rgba(255, 255, 255, 0.1);
.live-list-quick-join {
flex-direction: row;
align-items: center;
.text {
color: #ffffff;
font-size: 32rpx;
}
}
.quick-join-input {
flex: 1;
width: 360rpx;
height: 64rpx;
border-radius: 999rpx;
padding: 10rpx 20rpx;
margin-top: 20rpx;
font-size: 28rpx;
text-align: right;
}
}
.setting-label {
font-size: 28rpx;
color: #ffffff;
}
.setting-value {
flex-direction: row;
align-items: center;
}
.setting-text {
font-size: 28rpx;
color: rgba(255, 255, 255, 0.5);
margin-right: 8rpx;
}
.setting-arrow {
width: 24rpx;
height: 24rpx;
}
.volume-settings {
margin-top: 32rpx;
}
.section-title {
font-size: 28rpx;
color: rgba(255, 255, 255, 0.5);
margin-bottom: 24rpx;
}
.slider-item {
margin-bottom: 24rpx;
display: flex;
flex-direction: column;
}
.slider-label {
font-size: 28rpx;
color: #ffffff;
margin-bottom: 16rpx;
}
/* 自定义控制区域样式 */
.custom-slider {
flex: 1;
flex-direction: row;
align-items: center;
margin: 0 20rpx;
}
.control-btn {
width: 60rpx;
height: 60rpx;
border-radius: 30rpx;
display: flex;
justify-content: center;
align-items: center;
border: 2rpx solid #2b6ad6;
}
.minus-btn {
display: flex;
justify-content: center;
align-items: center;
background-color: rgba(43, 106, 214, 0.1);
}
.plus-btn {
display: flex;
align-items: center;
justify-content: center;
background-color: #2b6ad6;
}
.btn-text {
font-size: 32rpx;
font-weight: bold;
color: #ffffff;
}
.plus-btn .btn-text {
color: #ffffff;
}
.progress-section {
flex: 1;
margin: 0 20rpx;
align-items: center;
flex-direction: row;
justify-content: center;
}
.progress-bar {
width: 400rpx;
height: 8rpx;
background-color: rgba(255, 255, 255, 0.3);
border-radius: 4rpx;
position: relative;
overflow: hidden;
margin-right: 16rpx;
}
.progress-fill {
height: 8rpx;
background-color: #2b6ad6;
border-radius: 4rpx;
}
.current-value {
font-size: 24rpx;
color: #ffffff;
font-weight: 600;
text-align: center;
z-index: 10;
}
.voice-effects,
.reverb-effects {
margin-top: 32rpx;
}
.effects-grid {
flex-direction: row;
flex-wrap: wrap;
margin: 0 -8rpx;
}
.effect-item {
margin: 8rpx;
/* background-color: rgba(255, 255, 255, 0.1); */
justify-content: center;
align-items: center;
}
.effect-active {
background-color: rgba(43, 101, 251, 0.2);
border-width: 2rpx;
border-color: #2b65fb;
}
.effect-icon-container {
width: 112rpx;
height: 112rpx;
display: flex;
justify-content: center;
align-items: center;
border-radius: 16rpx;
background-color: rgba(255, 255, 255, 0.1);
margin-bottom: 12rpx;
}
.effect-icon {
width: 48rpx;
height: 48rpx;
margin-bottom: 8rpx;
}
.effect-name {
font-size: 24rpx;
color: #ffffff;
}
</style>