需要添加直播接口
This commit is contained in:
418
uni_modules/tuikit-atomic-x/components/BarrageList.nvue
Normal file
418
uni_modules/tuikit-atomic-x/components/BarrageList.nvue
Normal file
@@ -0,0 +1,418 @@
|
||||
<template>
|
||||
<view class="barrage-container">
|
||||
<!-- 聊天消息列表 -->
|
||||
<list class="chat-list" scroll-y :show-scrollbar="false" :style="{ bottom: bottomPx + 'px' }">
|
||||
<cell class="chat-item" v-if="mixMessageList.length > 0" v-for="(message) in mixMessageList"
|
||||
:key="message?.sequence" @tap="onItemTap(message)">
|
||||
<view class="message-content-wrapper">
|
||||
<view class="nickname-content-gift" v-if="message?.gift">
|
||||
<text class="chat-nickname"
|
||||
numberOfLines="1">{{ message?.sender?.userName || message?.sender?.userID }}:</text>
|
||||
<view class="gift-row">
|
||||
<text class="gift-prefix">{{ giftPrefix }}</text>
|
||||
<text class="gift-recipient">{{ giftReceiverName }}</text>
|
||||
<text class="gift-name">{{ message?.textContent || '' }}</text>
|
||||
<image class="gift-icon" :src="message?.gift?.iconURL || ''" alt="" />
|
||||
<text class="gift-count" style="font-size: 26rpx;">x{{ message?.count || '1' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="nickname-content-wrapper" v-else>
|
||||
<view class="message-right" v-if="message?.sender?.userID === currentLive?.liveOwner.userID">
|
||||
<text class="message-role">主播</text>
|
||||
</view>
|
||||
<view class="nickname-content-wrapper">
|
||||
<text class="chat-nickname"
|
||||
numberOfLines="1">{{ message?.sender?.userName || message?.sender?.userID }}:</text>
|
||||
<text class="chat-content">{{ message?.textContent || '' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</cell>
|
||||
<cell ref="ListBottom" style="height: 50rpx;"></cell>
|
||||
</list>
|
||||
|
||||
<!-- GiftToast 提示 -->
|
||||
<view class="toast-container" v-for="toast in visibleToasts" :key="toast.id" :style="getToastStyle(toast)">
|
||||
<view class="toast-content">
|
||||
<!-- 左侧用户信息 -->
|
||||
<view class="user-info">
|
||||
<image class="user-avatar" :src="toast?.avatarURL || defaultAvatarURL" mode="aspectFill" />
|
||||
<view class="user-details">
|
||||
<text class="username">{{ toast?.name || '' }}</text>
|
||||
<text class="action-text">{{ toast?.desc || '' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 右侧图标 -->
|
||||
<view class="icon-container" v-if="toast.iconURL">
|
||||
<image class="icon" :src="toast?.iconURL || ''" mode="aspectFit" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, computed, onMounted, onUnmounted } from 'vue';
|
||||
import { useBarrageState } from "@/uni_modules/tuikit-atomic-x/state/BarrageState";
|
||||
import { useGiftState } from "@/uni_modules/tuikit-atomic-x/state/GiftState";
|
||||
import { useLiveListState } from "@/uni_modules/tuikit-atomic-x/state/LiveListState";
|
||||
|
||||
const props = defineProps<{
|
||||
mode ?: 'anchor' | 'audience',
|
||||
bottomPx ?: number,
|
||||
liveID ?: string,
|
||||
toast ?: any, // GiftToast 的 toast 属性
|
||||
}>();
|
||||
|
||||
const mode = computed(() => props.mode || 'audience');
|
||||
const bottomPx = computed(() => props.bottomPx ?? 0);
|
||||
|
||||
const liveID = computed(() => props.liveID || uni?.$liveID);
|
||||
|
||||
const { messageList } = useBarrageState(liveID.value);
|
||||
const { addGiftListener, removeGiftListener } = useGiftState(liveID.value);
|
||||
const { currentLive } = useLiveListState(liveID.value);
|
||||
|
||||
const mixMessageList = ref<any[]>(messageList.value || []);
|
||||
|
||||
const dom = uni.requireNativePlugin('dom');
|
||||
const ListBottom = ref('ListBottom');
|
||||
|
||||
const giftPrefix = computed(() => mode.value === 'anchor' ? '送给我' : '送给');
|
||||
const giftReceiverName = computed(() => {
|
||||
if (mode.value === 'anchor') return '';
|
||||
const owner = currentLive?.value?.liveOwner || {};
|
||||
return owner?.userName || owner?.userID || '';
|
||||
});
|
||||
|
||||
// GiftToast 相关状态和逻辑
|
||||
const defaultAvatarURL = 'https://web.sdk.qcloud.com/component/TUIKit/assets/avatar_01.png';
|
||||
const defaultPosition = {
|
||||
top: 'auto',
|
||||
bottom: 720,
|
||||
left: 32,
|
||||
right: 'auto',
|
||||
};
|
||||
const toastHeight = 105;
|
||||
|
||||
const visibleToasts = ref<any[]>([]);
|
||||
|
||||
const generateId = () => {
|
||||
return Date.now().toString(36) + Math.random().toString(36).substr(2);
|
||||
};
|
||||
|
||||
const showToast = (toastConfig : Partial<any>) => {
|
||||
const toast = {
|
||||
id: generateId(),
|
||||
duration: 3000,
|
||||
autoHide: true,
|
||||
position: {
|
||||
top: defaultPosition.top,
|
||||
bottom: defaultPosition.bottom + (visibleToasts.value || []).length * toastHeight,
|
||||
left: '32rpx',
|
||||
right: defaultPosition.bottom,
|
||||
},
|
||||
...toastConfig
|
||||
};
|
||||
|
||||
visibleToasts.value.push(toast);
|
||||
|
||||
if (toast.autoHide && toast.duration) {
|
||||
setTimeout(() => {
|
||||
hideToast(toast.id);
|
||||
}, toast.duration);
|
||||
}
|
||||
|
||||
return toast.id;
|
||||
};
|
||||
|
||||
const hideToast = (id : string) => {
|
||||
const index = visibleToasts.value.findIndex(toast => toast.id === id);
|
||||
if (index > -1) {
|
||||
const removedToast = visibleToasts.value.splice(index, 1)[0];
|
||||
emit('toastClosed', removedToast);
|
||||
}
|
||||
};
|
||||
|
||||
const hideAllToasts = () => {
|
||||
visibleToasts.value = [];
|
||||
};
|
||||
|
||||
const getToastStyle = (toast : any) => {
|
||||
const style : any = {
|
||||
position: 'fixed',
|
||||
zIndex: 999
|
||||
};
|
||||
|
||||
if (toast.position) {
|
||||
if (toast.position.top !== undefined) {
|
||||
style.top = typeof toast.position.top === 'number' ? `${toast.position.top}rpx` : toast.position.top;
|
||||
}
|
||||
if (toast.position.bottom !== undefined) {
|
||||
style.bottom = typeof toast.position.bottom === 'number' ? `${toast.position.bottom}rpx` : toast.position.bottom;
|
||||
}
|
||||
if (toast.position.left !== undefined) {
|
||||
style.left = typeof toast.position.left === 'number' ? `${toast.position.left}rpx` : toast.position.left;
|
||||
}
|
||||
if (toast.position.right !== undefined) {
|
||||
style.right = typeof toast.position.right === 'number' ? `${toast.position.right}rpx` : toast.position.right;
|
||||
}
|
||||
}
|
||||
|
||||
return style;
|
||||
};
|
||||
|
||||
watch(messageList, (newVal, oldVal) => {
|
||||
if (!newVal) return;
|
||||
const value = newVal.slice((oldVal || []).length, (newVal || []).length);
|
||||
if (value.length > 0) {
|
||||
mixMessageList.value = [...mixMessageList.value, ...value];
|
||||
dom.scrollToElement(ListBottom.value);
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['itemTap', 'toastClosed']);
|
||||
const onItemTap = (message : any) => {
|
||||
emit('itemTap', message);
|
||||
};
|
||||
|
||||
const handleReceiveGift = {
|
||||
callback: (event) => {
|
||||
const res = JSON.parse(event)
|
||||
const value = {
|
||||
...res,
|
||||
textContent: `${res.gift?.name || ''}`,
|
||||
};
|
||||
mixMessageList.value = [...mixMessageList.value, value];
|
||||
dom.scrollToElement(ListBottom.value);
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (props.toast && Object.keys(props.toast).length > 0) {
|
||||
showToast(props.toast);
|
||||
}
|
||||
addGiftListener(uni.$liveID, 'onReceiveGift', handleReceiveGift)
|
||||
});
|
||||
onUnmounted(() => {
|
||||
removeGiftListener(uni.$liveID, 'onReceiveGift', handleReceiveGift)
|
||||
});
|
||||
|
||||
// 暴露给父组件的方法
|
||||
defineExpose({
|
||||
showToast,
|
||||
hideToast,
|
||||
hideAllToasts
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.barrage-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.chat-list {
|
||||
position: fixed;
|
||||
left: 32rpx;
|
||||
right: 32rpx;
|
||||
bottom: 800rpx;
|
||||
height: 380rpx;
|
||||
width: 500rpx;
|
||||
}
|
||||
|
||||
/* GiftToast 样式 */
|
||||
.toast-container {
|
||||
/* 基础样式由getToastStyle动态设置 */
|
||||
}
|
||||
|
||||
.toast-content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: rgba(34, 38, 46, 0.4);
|
||||
padding: 20rpx 24rpx;
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.1);
|
||||
height: 100rpx;
|
||||
width: 320rpx;
|
||||
border-radius: 50rpx;
|
||||
backdrop-filter: blur(10rpx);
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
border-width: 2rpx;
|
||||
border-color: #8B5CF6;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.user-details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.username {
|
||||
color: #ffffff;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
margin-bottom: 4rpx;
|
||||
max-width: 120rpx;
|
||||
lines: 1;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.action-text {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
max-width: 120rpx;
|
||||
lines: 1;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.icon-container {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
}
|
||||
|
||||
.chat-item {
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
border-radius: 32rpx;
|
||||
padding: 6rpx;
|
||||
width: fit-content;
|
||||
max-width: 500rpx;
|
||||
}
|
||||
|
||||
.message-content-wrapper {
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
min-width: 0;
|
||||
max-width: 500rpx;
|
||||
width: fit-content;
|
||||
background-color: rgba(0, 0, 0, 0.25);
|
||||
border-radius: 999rpx;
|
||||
padding: 8rpx 12rpx;
|
||||
}
|
||||
|
||||
.nickname-content-wrapper {
|
||||
flex: 1;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.nickname-content-gift {
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
min-width: 0;
|
||||
max-width: 500rpx;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.message-role {
|
||||
background-color: #0468FC;
|
||||
border-radius: 999px;
|
||||
margin-right: 5rpx;
|
||||
color: #fff;
|
||||
padding: 5rpx 15rpx;
|
||||
font-size: 20rpx;
|
||||
}
|
||||
|
||||
.chat-nickname {
|
||||
color: #80BEF6;
|
||||
font-size: 24rpx;
|
||||
line-height: 24rpx;
|
||||
margin-right: 8rpx;
|
||||
padding: 5rpx 0;
|
||||
flex-shrink: 0;
|
||||
max-width: 200rpx;
|
||||
lines: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.gift-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
max-width: 300rpx;
|
||||
flex: 1;
|
||||
justify-content: flex-start;
|
||||
word-wrap: break-word;
|
||||
word-break: break-all;
|
||||
white-space: normal;
|
||||
flex-wrap: wrap;
|
||||
padding-top: 2rpx;
|
||||
}
|
||||
|
||||
.gift-icon {
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
flex-shrink: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.gift-recipient,
|
||||
.gift-name {
|
||||
color: #ffffff;
|
||||
font-size: 24rpx;
|
||||
line-height: 24rpx;
|
||||
font-weight: 500;
|
||||
z-index: 999;
|
||||
padding: 2rpx 0;
|
||||
max-width: 300rpx;
|
||||
word-wrap: break-word;
|
||||
word-break: break-all;
|
||||
white-space: normal;
|
||||
text-align: left;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.gift-prefix,
|
||||
.gift-count {
|
||||
color: #ffffff;
|
||||
font-size: 24rpx;
|
||||
line-height: 24rpx;
|
||||
font-weight: 500;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.chat-content {
|
||||
color: #ffffff;
|
||||
font-size: 24rpx;
|
||||
line-height: 24rpx;
|
||||
font-weight: 500;
|
||||
z-index: 999;
|
||||
padding: 5rpx 0;
|
||||
word-wrap: break-word;
|
||||
word-break: break-all;
|
||||
white-space: normal;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
text-indent: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user