提交签活动功能

This commit is contained in:
bobobobo
2026-01-13 23:43:41 +08:00
parent c139fcf501
commit 07f9501c0f
9 changed files with 577 additions and 88 deletions

View File

@@ -0,0 +1,449 @@
<script setup>
import { reactive } from 'vue'
import { useUI } from '../../../utils/use-ui'
import { TUIGroupService } from '@tencentcloud/chat-uikit-engine-lite'
import { useAuthUser } from '../../../composables/useAuthUser'
const { showToast } = useUI()
const { tencentUserSig } = useAuthUser()
const props = defineProps({
modelValue: {
type: Boolean,
default: false
},
/**
* 创建人类型1-主播 2-管理员 3-系统
*/
creatorType: {
type: String,
default: ''
},
/**
* 直播间ID
*/
roomId: {
type: String,
default: ''
},
/**
* 群 id
*/
groupID: {
type: String,
default: ''
}
})
const emit = defineEmits(['update:modelValue'])
/**
* 根据用户输入的结束时间(分钟)计算开始时间和结束时间,并格式化为 yyyy-MM-dd HH:mm:ss
*/
const calculateTimeRange = endMinutes => {
// 获取当前时间(毫秒)
const now = new Date().getTime()
// 计算结束时间(毫秒)
const endTimeMs = now + endMinutes * 60 * 1000
// 创建 Date 对象
const startDate = new Date(now)
const endDate = new Date(endTimeMs)
// 格式化函数:将 Date 转为 'yyyy-MM-dd HH:mm:ss'
const formatDateTime = date => {
const pad = num => num.toString().padStart(2, '0')
const year = date.getFullYear()
const month = pad(date.getMonth() + 1) // getMonth() 返回 0-11
const day = pad(date.getDate())
const hours = pad(date.getHours())
const minutes = pad(date.getMinutes())
const seconds = pad(date.getSeconds())
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
}
return {
startTime: formatDateTime(startDate),
endTime: formatDateTime(endDate)
}
}
const close = () => {
emit('update:modelValue', false)
}
const formData = reactive({
title: '',
// startTime: '',
// endTime: '',
// 结束时间
endTime: '',
roomId: '',
rewardValue: '',
maxParticipants: '',
isRepeat: 0
})
const submitForm = async () => {
const res = await TUIGroupService.getGroupMemberProfile({
groupID: props.groupID,
userIDList: [tencentUserSig.value.userId]
})
console.log(res)
if (!formData.title) {
showToast('请填写标题')
return
}
if (!formData.endTime) {
showToast('请填写结束时间')
return
}
if (!formData.rewardValue) {
showToast('请填写奖励值')
return
}
if (!formData.maxParticipants) {
showToast('请填写参与人数')
return
}
// 参与人数不能为0
if (Number(formData.maxParticipants) === 0) {
showToast('参与人数不能为0')
return
}
// 参与人数必须是整数
if (!Number.isInteger(Number(formData.maxParticipants))) {
showToast('参与人数必须是整数')
return
}
// 参与人数不能大于1000
if (Number(formData.maxParticipants) > 1000) {
showToast('参与人数不能大于1000')
return
}
const data = {
title: formData.title,
startTime: calculateTimeRange(Number(formData.endTime)).startTime,
endTime: calculateTimeRange(Number(formData.endTime)).endTime,
rewardValue: Number(formData.rewardValue),
maxParticipants: Number(formData.maxParticipants),
isRepeat: formData.isRepeat,
creatorType: props.creatorType,
roomId: props.roomId
}
console.log(data)
}
</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">添加活动</text>
<text class="drawer-done" @tap="submitForm">完成</text>
</view>
<view class="setting-item">
<text class="setting-label">标题</text>
<view class="live-list-quick-join">
<input
v-model="formData.title"
placeholder="请输入标题"
class="quick-join-input"
/>
</view>
</view>
<view class="setting-item">
<text class="setting-label">结束时间(分钟)</text>
<view class="live-list-quick-join">
<input
v-model="formData.endTime"
type="number"
placeholder="请输入结束时间(分钟)"
class="quick-join-input"
/>
</view>
</view>
<view class="setting-item">
<text class="setting-label">奖励值(积分)</text>
<view class="live-list-quick-join">
<input
v-model="formData.rewardValue"
type="number"
placeholder="请输入奖励值"
class="quick-join-input"
/>
</view>
</view>
<view class="setting-item">
<text class="setting-label">参与人数</text>
<view class="live-list-quick-join">
<input
v-model="formData.maxParticipants"
type="number"
placeholder="请输入参与人数"
class="quick-join-input"
/>
</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;
margin: 0 32rpx 24rpx 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>

View File

@@ -75,6 +75,10 @@
<image class="action-button-icon" src="/static/images/host-pk.png" mode="aspectFit" />
<text class="action-button-text">主播pk</text>
</view> -->
<view class="action-button-item" @tap="isShowActivity = true">
<image class="action-button-icon" src="/static/images/activity.png" mode="aspectFit" />
<text class="action-button-text">活动</text>
</view>
<view class="action-button-item" @tap="showCoGuestPanel('requests')">
<image class="action-button-icon" src="/static/images/link-guest.png" mode="aspectFit" />
<text class="action-button-text">连观众</text>
@@ -86,7 +90,8 @@
<Like role="anchor" />
</view>
</view>
<!-- 活动弹框 -->
<Activity v-model="isShowActivity" :roomId="roomDataId" :groupID="groupId"></Activity>
<LiveAudienceList v-model="isShowAudienceList"></LiveAudienceList>
<AudienceActionPanel v-if="liveID" v-model="isShowAudienceActionPanel" :userInfo="selectedAudience"
:liveID="liveID"></AudienceActionPanel>
@@ -153,6 +158,8 @@
import { useGiftState } from "@/uni_modules/tuikit-atomic-x/state/GiftState";
import { useLiveSummaryState } from '@/uni_modules/tuikit-atomic-x/state/LiveSummaryState'
import ActionSheet from '@/components/ActionSheet.nvue'
import Activity from './components/activity.nvue'
const dom = uni.requireNativePlugin('dom')
const { loginUserInfo } = useLoginState();
uni.$liveID = `live_${uni.$userID}`
@@ -187,7 +194,8 @@
height: 750,
});
const beforeLivePanelRef = ref();
// 活动弹框状态
const isShowActivity = ref(false);
const isShowUserInfoPanel = ref(false);
const isShowAudienceList = ref(false);
const isShowCoHostPanel = ref(false);
@@ -573,65 +581,65 @@
const roomDataId = ref('')
const startLive = async () => {
try {
console.log('点击开始直播')
const data = {
coverUrl: coverURL.value,
roomName: liveTitle.value,
groupId: groupId.value
}
const roomData = await imAddLive(data)
const roomId = roomData.data.roomId
uni.$liveID = roomId
liveID.value = roomId
const res = await imDataStartLive(roomId)
console.log(roomData, '========11111')
console.log(res, '========22222')
roomDataId.value = roomId
const params = {
cursor: "", // 首次拉起传空不能是null),然后根据回调数据的cursor确认是否拉完
count: 20, // 分页拉取的个数
};
joinLive({ liveID: roomId })
fetchLiveList(params);
openLocalCamera({ isFront: isFrontCamera.value });
openLocalMicrophone();
setLocalVideoMuteImage();
isStartLive.value = true;
} catch (err) {
console.log(err, '====22')
}
// try {
// console.log('点击开始直播')
// const data = {
// coverUrl: coverURL.value,
// roomName: liveTitle.value,
// groupId: groupId.value
// }
// const roomData = await imAddLive(data)
// const roomId = roomData.data.roomId
// uni.$liveID = roomId
// liveID.value = roomId
// const res = await imDataStartLive(roomId)
// console.log(roomData, '========11111')
// console.log(res, '========22222')
// roomDataId.value = roomId
// const params = {
// cursor: "", // 首次拉起传空不能是null),然后根据回调数据的cursor确认是否拉完
// count: 20, // 分页拉取的个数
// };
// joinLive({ liveID: roomId })
// fetchLiveList(params);
// openLocalCamera({ isFront: isFrontCamera.value });
// openLocalMicrophone();
// setLocalVideoMuteImage();
// isStartLive.value = true;
// } catch (err) {
// console.log(err, '====22')
// }
// ======================原本代码
createLive({
liveInfo: {
liveID: uni?.$liveID,
liveName: liveTitle.value,
coverURL: coverURL.value,
isSeatEnabled: true,
seatMode: 'APPLY',
maxSeatCount: 0,
isPublicVisible: liveMode.value === '公开',
keepOwnerOnSeat: true,
seatLayoutTemplateID: templateLayout.value,
},
success: () => {
const params = {
cursor: "", // 首次拉起传空不能是null),然后根据回调数据的cursor确认是否拉完
count: 20, // 分页拉取的个数
};
fetchLiveList(params);
// createLive({
// liveInfo: {
// liveID: uni?.$liveID,
// liveName: liveTitle.value,
// coverURL: coverURL.value,
// isSeatEnabled: true,
// seatMode: 'APPLY',
// maxSeatCount: 0,
// isPublicVisible: liveMode.value === '公开',
// keepOwnerOnSeat: true,
// seatLayoutTemplateID: templateLayout.value,
// },
// success: () => {
// const params = {
// cursor: "", // 首次拉起传空不能是null),然后根据回调数据的cursor确认是否拉完
// count: 20, // 分页拉取的个数
// };
// fetchLiveList(params);
// openLocalCamera({ isFront: isFrontCamera.value });
// openLocalMicrophone();
// setLocalVideoMuteImage();
// },
// fail: (errCode, errMsg) => {
// uni.showToast({
// title: '创建直播间失败',
// });
// },
// });
// isStartLive.value = true;
openLocalCamera({ isFront: isFrontCamera.value });
openLocalMicrophone();
setLocalVideoMuteImage();
},
fail: (errCode, errMsg) => {
uni.showToast({
title: '创建直播间失败',
});
},
});
isStartLive.value = true;
};
const ShowAnchorViewClickPanel = (userInfo) => {