添加直播间功能,直播间右上角人数需要完善
This commit is contained in:
4
.env
4
.env
@@ -1,5 +1,5 @@
|
|||||||
# API
|
# API
|
||||||
VITE_SYSTEM_URL = "http://jb96776a.natappfree.cc"
|
VITE_SYSTEM_URL = "http://w6972efb.natappfree.cc"
|
||||||
|
|
||||||
#文档地址
|
#文档地址
|
||||||
VITE_DOC_URL = "http://k7986286.natappfree.cc"
|
VITE_DOC_URL = "http://f69dbab6.natappfree.cc"
|
||||||
@@ -91,7 +91,7 @@ export const imAddLive = (data, method = 'post') => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 开始直播 */
|
/** 开始直播 */
|
||||||
export const imDataStartLive = (roomId) => {
|
export const imDataStartLive = roomId => {
|
||||||
return http({
|
return http({
|
||||||
url: `/api/service/imLiveRoom/start/${roomId}`,
|
url: `/api/service/imLiveRoom/start/${roomId}`,
|
||||||
method: 'post'
|
method: 'post'
|
||||||
@@ -105,3 +105,37 @@ export const imDataEndLive = (roomId, viewers) => {
|
|||||||
method: 'delete'
|
method: 'delete'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 新增直播间活动 */
|
||||||
|
export const addLiveActivity = (data, method = 'post') => {
|
||||||
|
return http({
|
||||||
|
url: '/api/service/imLiveActivity',
|
||||||
|
method,
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取直播间活动详情 */
|
||||||
|
export const getLiveActivityDetail = id => {
|
||||||
|
return http({
|
||||||
|
url: `/api/service/imLiveActivity/${id}`,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 确认参加活动 */
|
||||||
|
export const confirmLiveActivity = data => {
|
||||||
|
return http({
|
||||||
|
url: `/api/service/imLiveActivityRecord`,
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询活动参与状态 */
|
||||||
|
export const getLiveActivityRecord = id => {
|
||||||
|
return http({
|
||||||
|
url: `/api/service/imLiveActivityRecord/partic/ipated/${id}`,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
7
constants/live-keys.js
Normal file
7
constants/live-keys.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
/** 直播间 businessID 唯一值 */
|
||||||
|
export const LIVE_BUSINESS = {
|
||||||
|
// 管理员
|
||||||
|
ADMIN: 'admin',
|
||||||
|
// 签到
|
||||||
|
SIGN: 'sign',
|
||||||
|
}
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { reactive } from 'vue'
|
import { reactive } from 'vue'
|
||||||
import { useUI } from '../../../utils/use-ui'
|
import { useUI } from '../../../utils/use-ui'
|
||||||
import { TUIGroupService } from '@tencentcloud/chat-uikit-engine-lite'
|
import { addLiveActivity } from '@/api/tui-kit'
|
||||||
import { useAuthUser } from '../../../composables/useAuthUser'
|
import { useBarrageState } from '@/uni_modules/tuikit-atomic-x/state/BarrageState';
|
||||||
|
import { LIVE_BUSINESS } from '@/constants/live-keys'
|
||||||
|
|
||||||
|
|
||||||
const { showToast } = useUI()
|
const { showToast } = useUI()
|
||||||
const { tencentUserSig } = useAuthUser()
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
@@ -35,6 +36,9 @@
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const { sendCustomMessage } = useBarrageState(props.roomId);
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -86,12 +90,6 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
const submitForm = async () => {
|
const submitForm = async () => {
|
||||||
const res = await TUIGroupService.getGroupMemberProfile({
|
|
||||||
groupID: props.groupID,
|
|
||||||
userIDList: [tencentUserSig.value.userId]
|
|
||||||
})
|
|
||||||
|
|
||||||
console.log(res)
|
|
||||||
if (!formData.title) {
|
if (!formData.title) {
|
||||||
showToast('请填写标题')
|
showToast('请填写标题')
|
||||||
return
|
return
|
||||||
@@ -134,7 +132,18 @@
|
|||||||
creatorType: props.creatorType,
|
creatorType: props.creatorType,
|
||||||
roomId: props.roomId
|
roomId: props.roomId
|
||||||
}
|
}
|
||||||
console.log(data)
|
|
||||||
|
const res = await addLiveActivity(data)
|
||||||
|
sendCustomMessage({
|
||||||
|
liveID: props.roomId,
|
||||||
|
businessID: LIVE_BUSINESS.SIGN,
|
||||||
|
data: JSON.stringify(res.data)
|
||||||
|
});
|
||||||
|
formData.title = ''
|
||||||
|
formData.endTime = ''
|
||||||
|
formData.rewardValue = ''
|
||||||
|
formData.maxParticipants = ''
|
||||||
|
close()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,7 @@
|
|||||||
<image class="action-button-icon" src="/static/images/host-pk.png" mode="aspectFit" />
|
<image class="action-button-icon" src="/static/images/host-pk.png" mode="aspectFit" />
|
||||||
<text class="action-button-text">主播pk</text>
|
<text class="action-button-text">主播pk</text>
|
||||||
</view> -->
|
</view> -->
|
||||||
<view class="action-button-item" @tap="isShowActivity = true">
|
<view class="action-button-item" @tap="userAddActivity">
|
||||||
<image class="action-button-icon" src="/static/images/activity.png" mode="aspectFit" />
|
<image class="action-button-icon" src="/static/images/activity.png" mode="aspectFit" />
|
||||||
<text class="action-button-text">活动</text>
|
<text class="action-button-text">活动</text>
|
||||||
</view>
|
</view>
|
||||||
@@ -91,7 +91,7 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<!-- 活动弹框 -->
|
<!-- 活动弹框 -->
|
||||||
<Activity v-model="isShowActivity" :roomId="roomDataId" :groupID="groupId"></Activity>
|
<Activity v-model="isShowActivity" :roomId="roomDataId" :groupID="groupId" :creatorType="creatorType"></Activity>
|
||||||
<LiveAudienceList v-model="isShowAudienceList"></LiveAudienceList>
|
<LiveAudienceList v-model="isShowAudienceList"></LiveAudienceList>
|
||||||
<AudienceActionPanel v-if="liveID" v-model="isShowAudienceActionPanel" :userInfo="selectedAudience"
|
<AudienceActionPanel v-if="liveID" v-model="isShowAudienceActionPanel" :userInfo="selectedAudience"
|
||||||
:liveID="liveID"></AudienceActionPanel>
|
:liveID="liveID"></AudienceActionPanel>
|
||||||
@@ -130,7 +130,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onLoad } from '@dcloudio/uni-app';
|
import { onLoad } from '@dcloudio/uni-app';
|
||||||
import { ref, onMounted, onUnmounted, computed, watch } from 'vue';
|
import { ref, onMounted, onUnmounted, computed, watch } from 'vue';
|
||||||
import { imAddLive, imDataStartLive, imDataEndLive } from '@/api/tui-kit'
|
import { imAddLive, imDataStartLive, getLiveActivityDetail, imDataEndLive } from '@/api/tui-kit'
|
||||||
import BeforeLivePanel from '../../components/BeforeLivePanel.nvue';
|
import BeforeLivePanel from '../../components/BeforeLivePanel.nvue';
|
||||||
import LiveStatusInfoCard from '@/uni_modules/tuikit-atomic-x/components/LiveStatusInfoCard.nvue';
|
import LiveStatusInfoCard from '@/uni_modules/tuikit-atomic-x/components/LiveStatusInfoCard.nvue';
|
||||||
import UserInfoPanel from '@/uni_modules/tuikit-atomic-x/components/LiveStreamView/UserInfoPanel.nvue';
|
import UserInfoPanel from '@/uni_modules/tuikit-atomic-x/components/LiveStreamView/UserInfoPanel.nvue';
|
||||||
@@ -376,8 +376,10 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
const groupId = ref('')
|
const groupId = ref('')
|
||||||
|
const creatorType = ref('')
|
||||||
// 页面加载
|
// 页面加载
|
||||||
onLoad((options) => {
|
onLoad((options) => {
|
||||||
|
creatorType.value = options?.creatorType
|
||||||
groupId.value = decodeURIComponent(options?.groupId)
|
groupId.value = decodeURIComponent(options?.groupId)
|
||||||
// 禁用右滑返回(仅 iOS 有效)
|
// 禁用右滑返回(仅 iOS 有效)
|
||||||
if (uni.getSystemInfoSync().platform === 'ios') {
|
if (uni.getSystemInfoSync().platform === 'ios') {
|
||||||
@@ -475,6 +477,23 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加活动
|
||||||
|
const userAddActivity = () => {
|
||||||
|
getLiveActivityDetail(roomDataId.value).then(res => {
|
||||||
|
if (res?.data && res.data.status === 1) {
|
||||||
|
// status: 0-未开始 1-进行中 2-已结束 3-已取消
|
||||||
|
uni.showModal({
|
||||||
|
title: `提示`,
|
||||||
|
content: '您有一个活动正在进行中,请勿重复添加活动',
|
||||||
|
showCancel: false,
|
||||||
|
confirmText: '确定'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
isShowActivity.value = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
const handleCoHostRequestReceived = {
|
const handleCoHostRequestReceived = {
|
||||||
callback: (event) => {
|
callback: (event) => {
|
||||||
if (isShowCoGuestPanelAvatar.value && applicants.value.length === 0) {
|
if (isShowCoGuestPanelAvatar.value && applicants.value.length === 0) {
|
||||||
@@ -581,65 +600,65 @@
|
|||||||
|
|
||||||
const roomDataId = ref('')
|
const roomDataId = ref('')
|
||||||
const startLive = async () => {
|
const startLive = async () => {
|
||||||
// try {
|
try {
|
||||||
// console.log('点击开始直播')
|
console.log('点击开始直播')
|
||||||
// const data = {
|
const data = {
|
||||||
// coverUrl: coverURL.value,
|
coverUrl: coverURL.value,
|
||||||
// roomName: liveTitle.value,
|
roomName: liveTitle.value,
|
||||||
// groupId: groupId.value
|
groupId: groupId.value
|
||||||
// }
|
}
|
||||||
// const roomData = await imAddLive(data)
|
const roomData = await imAddLive(data)
|
||||||
// const roomId = roomData.data.roomId
|
const roomId = roomData.data.roomId
|
||||||
// uni.$liveID = roomId
|
uni.$liveID = roomId
|
||||||
// liveID.value = roomId
|
liveID.value = roomId
|
||||||
// const res = await imDataStartLive(roomId)
|
const res = await imDataStartLive(roomId)
|
||||||
// console.log(roomData, '========11111')
|
console.log(roomData, '========11111')
|
||||||
// console.log(res, '========22222')
|
console.log(res, '========22222')
|
||||||
// roomDataId.value = roomId
|
roomDataId.value = roomId
|
||||||
// const params = {
|
const params = {
|
||||||
// cursor: "", // 首次拉起传空(不能是null),然后根据回调数据的cursor确认是否拉完
|
cursor: "", // 首次拉起传空(不能是null),然后根据回调数据的cursor确认是否拉完
|
||||||
// count: 20, // 分页拉取的个数
|
count: 20, // 分页拉取的个数
|
||||||
// };
|
};
|
||||||
// joinLive({ liveID: roomId })
|
joinLive({ liveID: roomId })
|
||||||
// fetchLiveList(params);
|
fetchLiveList(params);
|
||||||
// openLocalCamera({ isFront: isFrontCamera.value });
|
openLocalCamera({ isFront: isFrontCamera.value });
|
||||||
// openLocalMicrophone();
|
openLocalMicrophone();
|
||||||
// setLocalVideoMuteImage();
|
setLocalVideoMuteImage();
|
||||||
// isStartLive.value = true;
|
isStartLive.value = true;
|
||||||
// } catch (err) {
|
} catch (err) {
|
||||||
// console.log(err, '====22')
|
console.log(err, '====22')
|
||||||
// }
|
}
|
||||||
// ======================原本代码
|
// ======================原本代码
|
||||||
createLive({
|
// createLive({
|
||||||
liveInfo: {
|
// liveInfo: {
|
||||||
liveID: uni?.$liveID,
|
// liveID: uni?.$liveID,
|
||||||
liveName: liveTitle.value,
|
// liveName: liveTitle.value,
|
||||||
coverURL: coverURL.value,
|
// coverURL: coverURL.value,
|
||||||
isSeatEnabled: true,
|
// isSeatEnabled: true,
|
||||||
seatMode: 'APPLY',
|
// seatMode: 'APPLY',
|
||||||
maxSeatCount: 0,
|
// maxSeatCount: 0,
|
||||||
isPublicVisible: liveMode.value === '公开',
|
// isPublicVisible: liveMode.value === '公开',
|
||||||
keepOwnerOnSeat: true,
|
// keepOwnerOnSeat: true,
|
||||||
seatLayoutTemplateID: templateLayout.value,
|
// seatLayoutTemplateID: templateLayout.value,
|
||||||
},
|
// },
|
||||||
success: () => {
|
// success: () => {
|
||||||
const params = {
|
// const params = {
|
||||||
cursor: "", // 首次拉起传空(不能是null),然后根据回调数据的cursor确认是否拉完
|
// cursor: "", // 首次拉起传空(不能是null),然后根据回调数据的cursor确认是否拉完
|
||||||
count: 20, // 分页拉取的个数
|
// count: 20, // 分页拉取的个数
|
||||||
};
|
// };
|
||||||
fetchLiveList(params);
|
// fetchLiveList(params);
|
||||||
|
|
||||||
openLocalCamera({ isFront: isFrontCamera.value });
|
// openLocalCamera({ isFront: isFrontCamera.value });
|
||||||
openLocalMicrophone();
|
// openLocalMicrophone();
|
||||||
setLocalVideoMuteImage();
|
// setLocalVideoMuteImage();
|
||||||
},
|
// },
|
||||||
fail: (errCode, errMsg) => {
|
// fail: (errCode, errMsg) => {
|
||||||
uni.showToast({
|
// uni.showToast({
|
||||||
title: '创建直播间失败',
|
// title: '创建直播间失败',
|
||||||
});
|
// });
|
||||||
},
|
// },
|
||||||
});
|
// });
|
||||||
isStartLive.value = true;
|
// isStartLive.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ShowAnchorViewClickPanel = (userInfo) => {
|
const ShowAnchorViewClickPanel = (userInfo) => {
|
||||||
|
|||||||
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>
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
<view class="stream-info">
|
<view class="stream-info">
|
||||||
<image class="avatar" :src="currentLive?.liveOwner?.avatarURL || defaultAvatarURL" mode="aspectFill" />
|
<image class="avatar" :src="currentLive?.liveOwner?.avatarURL || defaultAvatarURL" mode="aspectFill" />
|
||||||
<view class="stream-details">
|
<view class="stream-details">
|
||||||
<text class="stream-title"
|
<text class="stream-title"
|
||||||
:numberOfLines="1">{{ currentLive?.liveOwner?.userName || currentLive?.liveOwner?.userID}}</text>
|
:numberOfLines="1">{{ currentLive?.liveOwner?.userName || currentLive?.liveOwner?.userID}}</text>
|
||||||
</view>
|
</view>
|
||||||
<!-- <view
|
<!-- <view
|
||||||
@@ -36,7 +36,8 @@
|
|||||||
<image class="participant-avatar" :src="user?.avatarURL || defaultAvatarURL" mode="aspectFill" />
|
<image class="participant-avatar" :src="user?.avatarURL || defaultAvatarURL" mode="aspectFill" />
|
||||||
</view>
|
</view>
|
||||||
<view class="participant-count">
|
<view class="participant-count">
|
||||||
<text class="count-text">{{ audienceList.length }}</text>
|
<text v-if="topNUmber" class="count-text">{{ audienceList.length }}</text>
|
||||||
|
<text v-else class="count-text">{{ audienceList.length }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="control-icons" @click.stop="navigateBack()">
|
<view class="control-icons" @click.stop="navigateBack()">
|
||||||
@@ -58,6 +59,7 @@
|
|||||||
<view class="footer">
|
<view class="footer">
|
||||||
<BarrageInput></BarrageInput>
|
<BarrageInput></BarrageInput>
|
||||||
<view class="action-buttons">
|
<view class="action-buttons">
|
||||||
|
<image v-if="activityData?.id" class="action-btn" @click="isShowActivity = true" src="/static/images/activity.png" />
|
||||||
<image class="action-btn" @click="showNetworkQualityPanel()" src="/static/images/dashboard.png" />
|
<image class="action-btn" @click="showNetworkQualityPanel()" src="/static/images/dashboard.png" />
|
||||||
<image class="action-btn" @click="showGiftPicker()" src="/static/images/live-gift.png" />
|
<image class="action-btn" @click="showGiftPicker()" src="/static/images/live-gift.png" />
|
||||||
<image class="action-btn" :class="{ 'disabled': shouldDisableCoGuestButton }"
|
<image class="action-btn" :class="{ 'disabled': shouldDisableCoGuestButton }"
|
||||||
@@ -71,6 +73,8 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 活动信息 :showData="activityData" -->
|
||||||
|
<ActivityInfo v-model="isShowActivity" v-model:info="activityData" :roomId="liveID" ></ActivityInfo>
|
||||||
<UserInfoPanel v-model="isShowUserInfoPanel" :userInfo="clickUserInfo" :isShowAnchor="isShowAnchorInfo">
|
<UserInfoPanel v-model="isShowUserInfoPanel" :userInfo="clickUserInfo" :isShowAnchor="isShowAnchorInfo">
|
||||||
</UserInfoPanel>
|
</UserInfoPanel>
|
||||||
<LiveAudienceList v-model="isShowAudienceList"></LiveAudienceList>
|
<LiveAudienceList v-model="isShowAudienceList"></LiveAudienceList>
|
||||||
@@ -94,7 +98,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { imDataEndLive } from '@/api/tui-kit'
|
import { imDataEndLive, getLiveActivityDetail, getLiveActivityRecord } from '@/api/tui-kit'
|
||||||
import { onLoad } from '@dcloudio/uni-app';
|
import { onLoad } from '@dcloudio/uni-app';
|
||||||
import { ref, onMounted, computed, onUnmounted, watch, nextTick } from 'vue';
|
import { ref, onMounted, computed, onUnmounted, watch, nextTick } from 'vue';
|
||||||
import UserInfoPanel from '@/uni_modules/tuikit-atomic-x/components/LiveStreamView/UserInfoPanel.nvue';
|
import UserInfoPanel from '@/uni_modules/tuikit-atomic-x/components/LiveStreamView/UserInfoPanel.nvue';
|
||||||
@@ -127,8 +131,13 @@
|
|||||||
const { disconnect, connected, cancelApplication } = useCoGuestState(uni?.$liveID)
|
const { disconnect, connected, cancelApplication } = useCoGuestState(uni?.$liveID)
|
||||||
const { addGiftListener, removeGiftListener } = useGiftState(uni?.$liveID);
|
const { addGiftListener, removeGiftListener } = useGiftState(uni?.$liveID);
|
||||||
const { connected: hostConnected } = useCoHostState(uni?.$liveID)
|
const { connected: hostConnected } = useCoHostState(uni?.$liveID)
|
||||||
|
import ActivityInfo from './components/activity-info.vue'
|
||||||
|
import { LIVE_BUSINESS } from '@/constants/live-keys'
|
||||||
|
|
||||||
|
|
||||||
const dom = uni.requireNativePlugin('dom')
|
const dom = uni.requireNativePlugin('dom')
|
||||||
|
const isShowActivity = ref(false)
|
||||||
|
const activityData = ref({})
|
||||||
const systemInfo = ref({});
|
const systemInfo = ref({});
|
||||||
const safeArea = ref({
|
const safeArea = ref({
|
||||||
left: 0,
|
left: 0,
|
||||||
@@ -185,6 +194,7 @@
|
|||||||
const exitSheetItems = ref(['退出直播间'])
|
const exitSheetItems = ref(['退出直播间'])
|
||||||
const coGuestSheetItems = ref(['取消连麦申请'])
|
const coGuestSheetItems = ref(['取消连麦申请'])
|
||||||
const coGuestSheetTitle = ref('')
|
const coGuestSheetTitle = ref('')
|
||||||
|
const topNUmber = ref('')
|
||||||
|
|
||||||
|
|
||||||
// 监听座位变化:当自身不在 seatList 时,将本地连麦状态重置为 IDLE
|
// 监听座位变化:当自身不在 seatList 时,将本地连麦状态重置为 IDLE
|
||||||
@@ -201,11 +211,45 @@
|
|||||||
}
|
}
|
||||||
}, { immediate: true, deep: true });
|
}, { immediate: true, deep: true });
|
||||||
|
|
||||||
|
// 监听自定义消息列表更新
|
||||||
|
watch(messageList, (newMessages) => {
|
||||||
|
if (newMessages && newMessages.length > 0) {
|
||||||
|
console.log('弹幕消息列表更新:', newMessages);
|
||||||
|
if (newMessages.some(v => v.businessID === LIVE_BUSINESS.ADMIN)) {
|
||||||
|
console.log('管理员消息====================', newMessages.find(v => v.businessID === LIVE_BUSINESS.ADMIN))
|
||||||
|
topNUmber.value = newMessages.find(v => v.businessID === LIVE_BUSINESS.ADMIN).data;
|
||||||
|
}
|
||||||
|
if (newMessages.some(v => v.businessID === LIVE_BUSINESS.SIGN)) {
|
||||||
|
activityData.value = {
|
||||||
|
...JSON.parse(newMessages.find(v => v.businessID === LIVE_BUSINESS.SIGN)?.data),
|
||||||
|
isParticipated: false
|
||||||
|
};
|
||||||
|
isShowActivity.value = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 页面加载
|
// 页面加载
|
||||||
onLoad((options) => {
|
onLoad((options) => {
|
||||||
console.warn('Live page onLoad = ', options);
|
console.warn('Live page onLoad = ', options);
|
||||||
liveID.value = options?.liveID;
|
liveID.value = options?.liveID;
|
||||||
|
getLiveActivityDetail(liveID.value).then(res => {
|
||||||
|
if (res?.data && res.data.status === 1) {
|
||||||
|
// status: 0-未开始 1-进行中 2-已结束 3-已取消
|
||||||
|
console.log('活动数据============= ', res.data);
|
||||||
|
|
||||||
|
getLiveActivityRecord(res.data.activityId).then(show => {
|
||||||
|
activityData.value = {
|
||||||
|
...res.data,
|
||||||
|
isParticipated: show.data
|
||||||
|
};
|
||||||
|
console.log('是否参加============= ', show);
|
||||||
|
isShowActivity.value = !show.data;
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
isShowActivity.value = false;
|
||||||
|
}
|
||||||
|
})
|
||||||
if (liveID.value) {
|
if (liveID.value) {
|
||||||
joinLive({
|
joinLive({
|
||||||
liveID: liveID.value,
|
liveID: liveID.value,
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { useUserStore } from '@/stores/user'
|
import { useUserStore } from '@/stores/user'
|
||||||
|
import { navigateTo } from '../../../utils/router'
|
||||||
|
|
||||||
// 基础设置
|
// 基础设置
|
||||||
const basicSetting = [
|
const basicSetting = [
|
||||||
{ name: '字体大小', value: '', url: '' },
|
{ name: '字体大小', value: '', url: '' },
|
||||||
{ name: '聊天背景', value: '', url: '' },
|
// { name: '聊天背景', value: '', url: '' },
|
||||||
{ name: '朋友圈设置', value: '', url: '' },
|
{ name: '朋友圈设置', value: '', url: '' },
|
||||||
{ name: '消息通知', value: '', url: '' },
|
{ name: '消息通知', value: '', url: '' },
|
||||||
{ name: '安全设置', value: '', url: '' },
|
// { name: '安全设置', value: '', url: '' },
|
||||||
{ name: '群发消息', value: '', url: '' },
|
// { name: '群发消息', value: '', url: '' },
|
||||||
{ name: '登录设备', value: '', url: '' }
|
{ name: '登录设备', value: '', url: '' }
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -17,10 +18,14 @@
|
|||||||
{ name: '隐私设置', value: '', url: '' },
|
{ name: '隐私设置', value: '', url: '' },
|
||||||
{ name: '清除缓存', value: '', url: '' },
|
{ name: '清除缓存', value: '', url: '' },
|
||||||
{ name: '意见反馈', value: '', url: '' },
|
{ name: '意见反馈', value: '', url: '' },
|
||||||
{ name: '关于我们', value: '', url: '' }
|
{ name: '关于我们', value: '', url: '/pages/discover/company' }
|
||||||
]
|
]
|
||||||
|
|
||||||
const { clearUserInfo } = useUserStore()
|
const { clearUserInfo } = useUserStore()
|
||||||
|
|
||||||
|
const onItem = item => {
|
||||||
|
item.url && navigateTo(item.url)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -30,6 +35,7 @@
|
|||||||
v-for="(item, index) in basicSetting"
|
v-for="(item, index) in basicSetting"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="item"
|
class="item"
|
||||||
|
@click="onItem(item)"
|
||||||
>
|
>
|
||||||
<text class="left-title">{{ item.name }}</text>
|
<text class="left-title">{{ item.name }}</text>
|
||||||
<view class="right-box">
|
<view class="right-box">
|
||||||
@@ -44,6 +50,7 @@
|
|||||||
v-for="(item, index) in systemSetting"
|
v-for="(item, index) in systemSetting"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="item"
|
class="item"
|
||||||
|
@click="onItem(item)"
|
||||||
>
|
>
|
||||||
<text class="left-title">{{ item.name }}</text>
|
<text class="left-title">{{ item.name }}</text>
|
||||||
<view class="right-box">
|
<view class="right-box">
|
||||||
|
|||||||
@@ -21,9 +21,13 @@
|
|||||||
<text class="message-role">主播</text>
|
<text class="message-role">主播</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="nickname-content-wrapper">
|
<view class="nickname-content-wrapper">
|
||||||
<text class="chat-nickname"
|
<text v-if="message?.businessID === LIVE_BUSINESS.ADMIN" class="chat-nickname"
|
||||||
|
numberOfLines="1">游客:</text>
|
||||||
|
<text v-else class="chat-nickname"
|
||||||
numberOfLines="1">{{ message?.sender?.userName || message?.sender?.userID }}:</text>
|
numberOfLines="1">{{ message?.sender?.userName || message?.sender?.userID }}:</text>
|
||||||
<text class="chat-content">{{ message?.textContent || '' }}</text>
|
<text v-if="message?.businessID === LIVE_BUSINESS.SIGN" class="chat-content">{{ JSON.parse(message?.data).title }}</text>
|
||||||
|
<text v-else-if="message?.businessID === LIVE_BUSINESS.ADMIN" class="chat-content">1</text>
|
||||||
|
<text v-else class="chat-content">{{ message?.textContent || '' }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -53,6 +57,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { LIVE_BUSINESS } from '@/constants/live-keys'
|
||||||
import { ref, watch, computed, onMounted, onUnmounted } from 'vue';
|
import { ref, watch, computed, onMounted, onUnmounted } from 'vue';
|
||||||
import { useBarrageState } from "@/uni_modules/tuikit-atomic-x/state/BarrageState";
|
import { useBarrageState } from "@/uni_modules/tuikit-atomic-x/state/BarrageState";
|
||||||
import { useGiftState } from "@/uni_modules/tuikit-atomic-x/state/GiftState";
|
import { useGiftState } from "@/uni_modules/tuikit-atomic-x/state/GiftState";
|
||||||
|
|||||||
@@ -150,12 +150,18 @@ const handleError = (statusCode, data) => {
|
|||||||
})
|
})
|
||||||
break
|
break
|
||||||
case 500:
|
case 500:
|
||||||
uni.showToast({
|
uni.showModal({
|
||||||
title: data.msg || '服务器内部错误',
|
title: `${statusCode}提示`,
|
||||||
icon: 'none',
|
content: data.msg || '服务器内部错误',
|
||||||
duration: 2000,
|
showCancel: false,
|
||||||
mask: true
|
confirmText: '确定'
|
||||||
})
|
})
|
||||||
|
// uni.showToast({
|
||||||
|
// title: data.msg || '服务器内部错误',
|
||||||
|
// icon: 'none',
|
||||||
|
// duration: 2000,
|
||||||
|
// mask: true
|
||||||
|
// })
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
|
|||||||
Reference in New Issue
Block a user