添加直播间功能,直播间右上角人数需要完善
This commit is contained in:
414
pages/audience/components/activity-info.vue
Normal file
414
pages/audience/components/activity-info.vue
Normal file
@@ -0,0 +1,414 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user