注释搜索:主播发送消息

观看列表只能主播跟管理员查看
This commit is contained in:
cbb
2026-02-10 17:49:33 +08:00
parent 2cc252dce0
commit 20ccbf1f14
19 changed files with 190 additions and 67 deletions

2
.env
View File

@@ -1,5 +1,5 @@
# API # API
# VITE_SYSTEM_URL = "http://h69b336a.natappfree.cc" # VITE_SYSTEM_URL = "http://s8e84fdb.natappfree.cc"
VITE_SYSTEM_URL = "https://dev.cqjcteach.cn/prod-api" VITE_SYSTEM_URL = "https://dev.cqjcteach.cn/prod-api"
# 第三方客户 channelId # 第三方客户 channelId

View File

@@ -851,6 +851,7 @@
userIDList userIDList
} }
try { try {
// await updateProfile({ key: 'inviteOption', value: TUIChatEngine.TYPES.INVITE_OPTIONS_FREE_ACCESS })
await TUIGroupService.addGroupMember(options) await TUIGroupService.addGroupMember(options)
} catch (error) { } catch (error) {
console.log(error) console.log(error)

View File

@@ -53,6 +53,15 @@ export const addOrder = data => {
}) })
} }
/** 加入订单 */
export const addOrderItem = data => {
return http({
url: '/api/service/order/join',
method: 'post',
data
})
}
/** 创建普通订单接口 */ /** 创建普通订单接口 */
export const createOrder = data => { export const createOrder = data => {
return http({ return http({

View File

@@ -164,10 +164,11 @@ export const getUserWithdrawConfig = id => {
} }
/** 获取用户积分详细信息 */ /** 获取用户积分详细信息 */
export const getUserIntegral = () => { export const getUserIntegral = (loading = true) => {
return http({ return http({
url: `/api/system/userPoints/details`, url: `/api/system/userPoints/details`,
method: 'get' method: 'get',
loading
}) })
} }
@@ -308,3 +309,12 @@ export const getUserBuyRecordDetail = id => {
method: 'get' method: 'get'
}) })
} }
/** 修改订单 */
export const updateUserBuyRecord = data => {
return http({
url: `/api/service/order`,
method: 'put',
data
})
}

View File

@@ -1,7 +1,9 @@
/** 直播间 businessID 唯一值 */ /** 直播间 businessID 唯一值 */
export const LIVE_BUSINESS = { export const LIVE_BUSINESS = {
// 管理员 // 系统管理员发送
ADMIN: 'admin', ADMIN: 'admin',
// 签到 // 签到
SIGN: 'sign', SIGN: 'sign',
/** 房间管理员 */
ANCHOR: 'anchor',
} }

View File

@@ -355,12 +355,6 @@
"navigationStyle": "custom" "navigationStyle": "custom"
} }
}, },
{
"path": "pages/shop-together/user",
"style": {
"navigationStyle": "custom"
}
},
{ {
"path": "pages/discover/ranking-list", "path": "pages/discover/ranking-list",
"style": { "style": {
@@ -404,6 +398,12 @@
} }
}, },
// #ifdef APP-PLUS // #ifdef APP-PLUS
{
"path": "pages/shop-together/user",
"style": {
"navigationStyle": "custom"
}
},
{ {
"path": "pages/discover/livelist/index", "path": "pages/discover/livelist/index",
"style": { "style": {

View File

@@ -1,4 +1,5 @@
<template> <template>
<!-- 开始直播页面 -->
<view <view
class="live-container" class="live-container"
@click="handleHideInput" @click="handleHideInput"
@@ -193,12 +194,15 @@
:groupID="groupId" :groupID="groupId"
:creatorType="creatorType" :creatorType="creatorType"
></Activity> ></Activity>
<LiveAudienceList v-model="isShowAudienceList"></LiveAudienceList>
<LiveAudienceList v-model="isShowAudienceList" @adminBack="handleAdminBack"></LiveAudienceList>
<AudienceActionPanel <AudienceActionPanel
v-if="liveID" v-if="liveID && isShowAudienceActionPanel"
v-model="isShowAudienceActionPanel" v-model="isShowAudienceActionPanel"
:userInfo="selectedAudience" :userInfo="selectedAudience"
:liveID="liveID" :liveID="liveID"
@adminBack="handleAdminBack"
></AudienceActionPanel> ></AudienceActionPanel>
<CoGuestPanel <CoGuestPanel
@@ -476,6 +480,19 @@
templateLayout.value = data templateLayout.value = data
} }
/** 设置管理员回调(发送自定义消息) */
const handleAdminBack = (e) => {
const data = {
liveID: liveID.value,
businessID: LIVE_BUSINESS.ANCHOR,
data: JSON.stringify({
...e,
count: `${e.userName}${e.show ? '成为' : '撤销'}管理员`
})
}
sendCustomMessage(data)
}
// 自定义Modal相关方法 // 自定义Modal相关方法
const showCustomModalDialog = userInfo => { const showCustomModalDialog = userInfo => {
currentModalUserInfo.value = userInfo currentModalUserInfo.value = userInfo

View File

@@ -1,4 +1,5 @@
<template> <template>
<!-- 观众端页面 -->
<view <view
class="live-container" class="live-container"
@click="handleHideInput" @click="handleHideInput"

View File

@@ -240,7 +240,9 @@
</span> </span>
</div> </div>
<div class="top-right"> <div class="top-right">
<div class="audience-list-header" @click="showAudienceList"> <!-- 不能点击查看观众列表 -->
<!-- @click="showAudienceList" -->
<div class="audience-list-header">
<Avatar <Avatar
v-for="item in audienceList.slice(0, 3)" v-for="item in audienceList.slice(0, 3)"
:key="item.userId" :key="item.userId"

View File

@@ -97,8 +97,6 @@
} }
} }
// 订单列表 // 订单列表
.order-list { .order-list {
padding: 24rpx 0; padding: 24rpx 0;
@@ -111,7 +109,9 @@
margin-bottom: 24rpx; margin-bottom: 24rpx;
padding: 32rpx; padding: 32rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.03); box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.03);
transition: transform 0.2s ease, box-shadow 0.2s ease; transition:
transform 0.2s ease,
box-shadow 0.2s ease;
&:active { &:active {
transform: translateY(-2rpx); transform: translateY(-2rpx);
@@ -314,4 +314,24 @@
} }
} }
// 底部按钮
.bottom-btn {
margin-top: 16rpx;
display: flex;
justify-content: end;
button {
margin: 0;
border-radius: 80rpx;
width: 180rpx;
height: 60rpx;
line-height: 60rpx;
font-size: 26rpx;
color: #333333;
background: #fff;
border: 2rpx solid #d4dae0;
box-sizing: border-box;
&:after {
display: none;
}
}
}

View File

@@ -1,8 +1,13 @@
<script setup> <script setup>
import { ref, computed, onMounted, reactive } from 'vue' import { ref, computed, onMounted, reactive } from 'vue'
import { navigateTo } from '@/utils/router' import { navigateTo } from '@/utils/router'
import { getUserBuyRecordList } from '../../../api/my-index' import {
getUserBuyRecordList,
updateUserBuyRecord
} from '../../../api/my-index'
import { useUI } from '../../../utils/use-ui'
const { showDialog, showToast } = useUI()
// 响应式数据 // 响应式数据
const paging = ref(null) const paging = ref(null)
const activeFilter = ref('') // -1表示全部 const activeFilter = ref('') // -1表示全部
@@ -36,6 +41,7 @@
const getList = async () => { const getList = async () => {
try { try {
const res = await getUserBuyRecordList({ const res = await getUserBuyRecordList({
// orderType: 1,
orderStatus: activeFilter.value, orderStatus: activeFilter.value,
pageNum: formData.pageNum, pageNum: formData.pageNum,
pageSize: formData.pageSize pageSize: formData.pageSize
@@ -74,6 +80,19 @@
const getStatusClass = status => { const getStatusClass = status => {
return statusMap[status]?.color || 'status-default' return statusMap[status]?.color || 'status-default'
} }
/** 确认收货按钮回调 */
const confirmReceipt = async item => {
const show = await showDialog('提示', '确认已收到商品吗?')
if (!show) return
const data = {
orderStatus: 8,
id: item.id
}
await updateUserBuyRecord(data)
item.orderStatus = 8
showToast('收货成功', 'success')
}
</script> </script>
<template> <template>
@@ -187,7 +206,6 @@
<view class="goods-info"> <view class="goods-info">
<text class="goods-name">{{ item.userName }}</text> <text class="goods-name">{{ item.userName }}</text>
<text class="goods-spec"> <text class="goods-spec">
<!-- {{ item.orderType == 1 ? '普通订单' : '拼单订单' }} -->
{{ item.specText }} {{ item.specText }}
</text> </text>
<view class="goods-price-info"> <view class="goods-price-info">
@@ -206,6 +224,11 @@
</view> </view>
</view> </view>
<!-- 底部按钮 -->
<view v-if="item.orderStatus == 7" class="bottom-btn">
<button @click.stop="confirmReceipt(item)">确认收货</button>
</view>
<!-- 底部装饰线 --> <!-- 底部装饰线 -->
<view class="card-footer"> <view class="card-footer">
<view class="decorative-line"></view> <view class="decorative-line"></view>

View File

@@ -1,6 +1,11 @@
<script setup> <script setup>
import { onLoad, onShow } from '@dcloudio/uni-app' import { onLoad, onShow } from '@dcloudio/uni-app'
import { getProductDetail, addOrder, createOrder } from '@/api/mall' import {
getProductDetail,
addOrder,
addOrderItem,
createOrder
} from '@/api/mall'
import { reactive, ref } from 'vue' import { reactive, ref } from 'vue'
import { formatRMB } from '@/utils' import { formatRMB } from '@/utils'
import { getUserAddress } from '@/api' import { getUserAddress } from '@/api'
@@ -86,13 +91,13 @@
const getData = async productId => { const getData = async productId => {
const res = await getProductDetail(productId) const res = await getProductDetail(productId)
viewData.value = res.data viewData.value = res.data
const { const {
id, id,
price, price,
stockQuantity, stockQuantity,
originalPrice: nub originalPrice: nub
} = res.data.skuList[0] } = res.data.skuList[0]
originalPrice.value = nub originalPrice.value = nub
formData.maxNum = stockQuantity formData.maxNum = stockQuantity
formData.spec = id formData.spec = id
@@ -142,15 +147,19 @@
skuId: formData.spec skuId: formData.spec
} }
tixian.value.close() tixian.value.close()
if (formData.startGroup) { const isApiShow = formData.startGroup
const res = await addOrder(data) let api = null
await refreshUserInfo() if (isApiShow) {
await showToast('订单提交成功', 'success') api = data.groupId ? addOrderItem : addOrder
} else {
api = createOrder
}
const res = await api(data)
await refreshUserInfo()
await showToast('订单提交成功', 'success')
if (isApiShow) {
navigateTo('/pages/shop-together/detail', { id: res.data.groupId }) navigateTo('/pages/shop-together/detail', { id: res.data.groupId })
} else { } else {
const res = await createOrder(data)
await refreshUserInfo()
await showToast('订单提交成功', 'success')
navigateBack() navigateBack()
} }
} }
@@ -163,7 +172,6 @@
// groupId // groupId
groupId.value = e?.groupId || '' groupId.value = e?.groupId || ''
console.log(e?.groupId, '===') console.log(e?.groupId, '===')
formData.startGroup = !!e?.groupId
await getData(e.productId) await getData(e.productId)
}) })
</script> </script>
@@ -268,9 +276,10 @@
></image> ></image>
</view> </view>
</view> </view>
<!--
v-if="groupId" -->
<view <view
v-if="groupId" v-if="!groupId"
class="pay-way-item" class="pay-way-item"
@click="formData.startGroup = !formData.startGroup" @click="formData.startGroup = !formData.startGroup"
> >

View File

@@ -1,10 +1,11 @@
<script setup> <script setup>
import { useAuthUser } from '@/composables/useAuthUser' import { useAuthUser } from '@/composables/useAuthUser'
import { onLoad } from '@dcloudio/uni-app' import { onLoad, onShow } from '@dcloudio/uni-app'
import { navigateTo } from '@/utils/router' import { navigateTo } from '@/utils/router'
import { useUI } from '@/utils/use-ui' import { useUI } from '@/utils/use-ui'
import { formatNumberWithWan } from '../../utils' import { formatNumberWithWan } from '../../utils'
import { getUserPayPwd } from '@/api/my-index' import { getUserPayPwd } from '@/api/my-index'
import { useUserStore } from '../../stores/user'
const bottomList = [ const bottomList = [
{ {
@@ -17,7 +18,11 @@
icon: 'user-code', icon: 'user-code',
url: '/pages/my-index/wallet/invite' url: '/pages/my-index/wallet/invite'
}, },
{ name: '我的拼团伙伴', icon: 'team', url: '/pages/my-index/my-team' }, {
name: '我的拼团伙伴',
icon: 'team',
url: '/pages/my-index/my-team'
},
// { name: '群创建直播', icon: 'videocam', url: '' }, // { name: '群创建直播', icon: 'videocam', url: '' },
{ {
name: '直播记录', name: '直播记录',
@@ -46,6 +51,7 @@
} }
] ]
const { getIntegral } = useUserStore()
const { showDialog } = useUI() const { showDialog } = useUI()
const { userInfo, integralData } = useAuthUser() const { userInfo, integralData } = useAuthUser()
@@ -70,6 +76,9 @@
// 获取用户信息 // 获取用户信息
console.log(userInfo.value, '===获取用户信息') console.log(userInfo.value, '===获取用户信息')
}) })
onShow(() => {
getIntegral(false)
})
</script> </script>
<template> <template>
@@ -179,7 +188,9 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.name { .name {
font-family: PingFang SC, PingFang SC; font-family:
PingFang SC,
PingFang SC;
font-weight: bold; font-weight: bold;
font-size: 32rpx; font-size: 32rpx;
color: #333333; color: #333333;
@@ -227,7 +238,9 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
text { text {
font-family: PingFang SC, PingFang SC; font-family:
PingFang SC,
PingFang SC;
font-weight: 500; font-weight: 500;
font-size: 28rpx; font-size: 28rpx;
color: #ffffff; color: #ffffff;
@@ -248,7 +261,9 @@
height: 64rpx; height: 64rpx;
line-height: 64rpx; line-height: 64rpx;
border-radius: 100rpx 100rpx 100rpx 100rpx; border-radius: 100rpx 100rpx 100rpx 100rpx;
font-family: PingFang SC, PingFang SC; font-family:
PingFang SC,
PingFang SC;
font-weight: 500; font-weight: 500;
font-size: 28rpx; font-size: 28rpx;
color: #ffffff; color: #ffffff;
@@ -283,7 +298,9 @@
} }
.text-box { .text-box {
margin-left: 16rpx; margin-left: 16rpx;
font-family: PingFang SC, PingFang SC; font-family:
PingFang SC,
PingFang SC;
font-weight: 500; font-weight: 500;
font-size: 32rpx; font-size: 32rpx;
color: #333333; color: #333333;

View File

@@ -103,10 +103,16 @@
> >
<view class="left-name"> <view class="left-name">
<image <image
src="https://wx1.sinaimg.cn/mw690/92eeb099gy1i29hl0ne80j21jk2bcash.jpg" v-if="item.avatar"
:src="item.avatar"
mode="scaleToFill" mode="scaleToFill"
class="avatar" class="avatar"
></image> ></image>
<uni-icons
v-else
type="contact-filled"
size="120rpx"
></uni-icons>
<text>{{ item.userName }}</text> <text>{{ item.userName }}</text>
</view> </view>
<text class="date">加入时间{{ item.paidTime }}</text> <text class="date">加入时间{{ item.paidTime }}</text>

View File

@@ -1,22 +0,0 @@
module.exports = {
printWidth: 74,
tabWidth: 2,
semi: false,
arrowParens: 'avoid',
singleQuote: true,
trailingComma: 'none',
bracketSpacing: true,
htmlWhitespaceSensitivity: 'ignore',
endOfLine: 'auto',
insertPragma: false,
proseWrap: 'preserve',
'objectCurly-newline': [
'error',
{
multiline: true
}
],
'array-bracket-newline': ['error', 'consistent'],
vueIndentScriptAndStyle: true
}

View File

@@ -126,8 +126,8 @@ export const useUserStore = defineStore('user', () => {
} }
/** 获取用户积分 */ /** 获取用户积分 */
const getIntegral = async () => { const getIntegral = async (loading = true) => {
const res = await getUserIntegral() const res = await getUserIntegral(loading)
integralData.value = res.data.availablePoints integralData.value = res.data.availablePoints
} }

View File

@@ -23,7 +23,13 @@
<view class="nickname-content-wrapper"> <view class="nickname-content-wrapper">
<text class="chat-nickname" <text 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 == 'anchor'" class="chat-content">
{{ getAnchorMessageText(message) }}
</text> -->
<text class="chat-content">
{{ message?.textContent || '' }}
</text>
</view> </view>
</view> </view>
</view> </view>
@@ -177,9 +183,16 @@
emit('itemTap', message); emit('itemTap', message);
}; };
/** 主播发送自定义消息显示文本 */
const getAnchorMessageText = (message: any) => {
const data = JSON.parse(message.data)
return data.count
}
const handleReceiveGift = { const handleReceiveGift = {
callback: (event) => { callback: (event) => {
const res = JSON.parse(event) const res = JSON.parse(event)
console.log('===消息接收===', res)
const value = { const value = {
...res, ...res,
textContent: `${res.gift?.name || ''}`, textContent: `${res.gift?.name || ''}`,

View File

@@ -1,5 +1,5 @@
<template> <template>
<view class="bottom-drawer-container" v-if="modelValue"> <view class="bottom-drawer-container" v-if="!loading && modelValue">
<view class="drawer-overlay" @tap="close"></view> <view class="drawer-overlay" @tap="close"></view>
<view class="bottom-drawer" :class="{ 'drawer-open': modelValue }"> <view class="bottom-drawer" :class="{ 'drawer-open': modelValue }">
<view class="drawer-header"> <view class="drawer-header">
@@ -126,7 +126,10 @@
} }
}) })
const emit = defineEmits(['update:modelValue', 'update:userInfo']) const emit = defineEmits(['update:modelValue', 'update:userInfo', 'adminBack'])
/** 加载状态 */
const loading = ref(true)
/** 禁言,管理员列表 */ /** 禁言,管理员列表 */
const listData = reactive({ const listData = reactive({
@@ -168,12 +171,16 @@
Promise.all(apiList).then((res) => { Promise.all(apiList).then((res) => {
listData.tabooList = res[0].data.Response?.MutedAccountList || [] listData.tabooList = res[0].data.Response?.MutedAccountList || []
listData.adminList = res[1].data.Response?.Admin_Account || [] listData.adminList = res[1].data.Response?.Admin_Account || []
console.log('===请求接口了!!!')
if (state === 1) { if (state === 1) {
showToast(`${isMessageDisabled.value ? '禁止' : '恢复'}成功`, 'success') showToast(`${isMessageDisabled.value ? '禁止' : '恢复'}成功`, 'success')
} }
if (state === 2) { if (state === 2) {
showToast(`${isAdminShow.value ? '设置' : '撤销'}成功`, 'success') showToast(`${isAdminShow.value ? '设置' : '撤销'}成功`, 'success')
} }
}).finally(() => {
loading.value = false
}) })
} }
@@ -200,6 +207,10 @@
userID: userData.value.userID, userID: userData.value.userID,
success: () =>{ success: () =>{
getListData(2) getListData(2)
emit('adminBack', {
...userData.value,
show: !isAdminShow.value
})
close() close()
} }
}) })

View File

@@ -40,7 +40,7 @@
</view> </view>
</view> </view>
<AudienceActionPanel v-model="isShowAudienceActionPanel" :userInfo="selectedAudience" :liveID="liveID" :adminType="isAdminState"> <AudienceActionPanel v-model="isShowAudienceActionPanel" :userInfo="selectedAudience" :liveID="liveID" :adminType="isAdminState" @adminBack="onAdminBack">
</AudienceActionPanel> </AudienceActionPanel>
</view> </view>
@@ -108,7 +108,7 @@ import { useAuthUser } from '../../../../composables/useAuthUser';
} }
}); });
const emit = defineEmits(['update:modelValue']); const emit = defineEmits(['update:modelValue', 'adminBack']);
const close = () => { const close = () => {
emit('update:modelValue', false); emit('update:modelValue', false);
}; };
@@ -123,6 +123,10 @@ import { useAuthUser } from '../../../../composables/useAuthUser';
}) })
} }
}) })
const onAdminBack = (e) => {
emit('adminBack', e)
}
// 初始化加载 // 初始化加载
onMounted(() => { onMounted(() => {