直播间需要添加签到功能

This commit is contained in:
cbb
2026-01-13 17:56:19 +08:00
parent 06e026c8b8
commit c139fcf501
16 changed files with 313 additions and 171 deletions

View File

@@ -17,7 +17,8 @@
const silentLogin = async () => {
if (token.value) {
loginTencentIM()
reLaunch('/TUIKit/components/TUIConversation/index')
// reLaunch('/TUIKit/components/TUIConversation/index')
return
}

View File

@@ -627,7 +627,6 @@
item.modifyMessage({
cloudCustomData: JSON.stringify(newMessage)
})
return
receiveRedEnvelope({
redPacketId: data.id
})

View File

@@ -889,7 +889,7 @@
reason: ''
}
console.log(options, '===')
return
await TUIGroupService.deleteGroupMember(options)
}

View File

@@ -212,6 +212,7 @@ export const getUserMomentsList = data => {
return http({
url: '/api/service/userMoments/list',
method: 'get',
loading: false,
data
})
}

View File

@@ -99,9 +99,9 @@ export const imDataStartLive = (roomId) => {
}
/** 结束直播 */
export const imDataEndLive = (roomId) => {
export const imDataEndLive = (roomId, viewers) => {
return http({
url: `/api/service/imLiveRoom/${roomId}`,
url: `/api/service/imLiveRoom/${roomId}/${viewers}`,
method: 'delete'
})
}

View File

@@ -12,6 +12,8 @@
type: String,
default: ''
})
const emit = defineEmits(['search'])
</script>
<template>
@@ -27,7 +29,7 @@
:placeholder="props.placeholder"
class="search-box"
/>
<button class="search-btn">搜索</button>
<button class="search-btn" @click="emit('search')">搜索</button>
</view>
</template>

View File

@@ -550,14 +550,16 @@
clearAudioEffectSet()
clearBeautyPanelSet()
uni.$summaryData = summaryData.value
console.warn(` 退出直播 imDataEndLive`);
endLive({
success: () => {
console.warn(` 退出直播 imDataEndLive`);
uni.redirectTo({ url: '/pages/liveend/index' });
},
});
console.warn(` 退出直播===直播间人数`, audienceCount.value);
imDataEndLive(roomDataId.value, audienceCount.value).then(() => {
uni.redirectTo({ url: '/pages/liveend/index' });
})
// endLive({
// success: () => {
// console.warn(` 退出直播 imDataEndLive`);
// uni.redirectTo({ url: '/pages/liveend/index' });
// },
// });
}
}
@@ -569,60 +571,67 @@
isShowLiveMoreActionsPanel.value = true;
};
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 res = await imDataStartLive(roomData.data.roomId)
// console.log(res)
// const params = {
// cursor: "", // 首次拉起传空不能是null),然后根据回调数据的cursor确认是否拉完
// count: 20, // 分页拉取的个数
// };
// 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);
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')
}
openLocalCamera({ isFront: isFrontCamera.value });
openLocalMicrophone();
setLocalVideoMuteImage();
},
fail: (errCode, errMsg) => {
uni.showToast({
title: '创建直播间失败',
});
},
});
isStartLive.value = true;
// 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;
};
const ShowAnchorViewClickPanel = (userInfo) => {

View File

@@ -427,16 +427,17 @@
disconnect({
liveID: uni?.$liveID,
})
exitSheetItems.value = ['退出直播间']
exitSheetItems.value = ['退2出直播间']
exitSheetTitle.value = ''
uni.$localGuestStatus = 'IDLE'
return
}
if ((uni.$localGuestStatus === 'CONNECTED' && index === 1) || (uni.$localGuestStatus !== 'CONNECTED' && index === 0)) {
// imDataEndLive(liveID.value, 0)
leaveLive({
success: () => {
// imDataEndLive(liveID.value)
uni.$liveID = ''
uni.redirectTo({
url: `/pages/discover/livelist/index`,

View File

@@ -25,6 +25,7 @@ text-transform: none;`
const MAX_SCROLL = 446
const paging = ref(null)
const listLoading = ref(true)
const cbNavBar = ref({})
const dataList = ref([])
const topIcon = reactive({
@@ -54,24 +55,36 @@ text-transform: none;`
try {
const res = await getUserMomentsList({
pageNum,
pageSize
pageSize,
targetUserId: userInfo.value.userId
})
paging.value.complete(res.rows)
const list = res.rows.map(item => {
return {
...item,
commentList: item.comments
}
})
paging.value.complete(list)
listLoading.value = false
} catch (error) {
paging.value.complete(false)
}
}
const onLike = async item => {
await likeUserMoments(item.id)
// item.likeCount += 1
const res = await likeUserMoments(item.id)
if (res.data) {
item.likeCount += 1
} else {
item.likeCount -= 1
}
}
/** 点击查看大图 */
const onImage = current => {
const onImage = (urls, current = 0) => {
uni.previewImage({
urls: [current], // 图片路径数组(本地或网络)
current: current // 当前显示的图片(可选,默认为第一张)
urls, // 图片路径数组(本地或网络)
current // 当前显示的图片(可选,默认为第一张)
})
closeComment()
}
@@ -87,9 +100,11 @@ text-transform: none;`
console.log('发布评论')
const data = {
content: contentData.value,
id: item.id
id: item.id,
momentId: item.id
}
const res = await addUserMomentsComment(data)
item.commentList.push(res.data)
closeComment()
}
@@ -117,6 +132,7 @@ text-transform: none;`
safe-area-inset-bottom
use-safe-area-placeholder
:show-loading-more-no-more-view="false"
:auto="false"
@query="getData"
@scroll="onScroll"
>
@@ -146,39 +162,51 @@ text-transform: none;`
</template>
<view class="top-bg-img">
<image
:src="userInfo.avatar"
:src="userInfo?.avatar"
mode="aspectFill"
class="img"
@click="onImage(userInfo.avatar)"
@click="onImage([userInfo?.avatar])"
></image>
<!-- 用户信息 -->
<view class="user-info">
<text>{{ userInfo.userName }}</text>
<text>{{ userInfo?.userName }}</text>
<image
:src="userInfo.avatar"
:src="userInfo?.avatar"
mode="aspectFill"
class="avatar"
@click="onImage(userInfo.avatar)"
@click="onImage([userInfo?.avatar])"
></image>
</view>
</view>
<!-- 动态列表 -->
<view class="dynamic-list" @click="closeComment">
<view v-if="!listLoading" class="dynamic-list" @click="closeComment">
<view v-for="item in dataList" :key="item.id" class="list">
<image
src="https://img1.baidu.com/it/u=2645961124,1296922095&fm=253&app=138&f=JPEG?w=800&h=1530"
:src="item.avatar"
mode="aspectFill"
class="avatar"
></image>
<view class="content">
<text class="name">名字</text>
<text class="name">{{ item.userName }}</text>
<text class="text">{{ item.content }}</text>
<view class="img-list">
<view
v-if="item.images.length > 0"
:class="item.images.length === 1 ? 'one-img' : 'img-list'"
>
<image
src="https://p4.itc.cn/images01/20220619/46660ed163164c14be90e605a73ee5e8.jpeg"
v-for="(img, index) in item.images"
:key="index"
:src="img.imageUrl"
lazy-load
mode="aspectFill"
class="item-img"
@click="
onImage(
item.images.map(v => v.imageUrl),
index
)
"
></image>
</view>
<!-- 地址 -->
@@ -223,10 +251,14 @@ text-transform: none;`
<button @click.stop="onComment(item)">发布</button>
</view>
<!-- 评论内容 -->
<view class="comment">
<view v-for="item in 3" :key="item" class="comment-item">
<text>Admin:</text>
<text>确实的很好值得推荐的很好</text>
<view v-if="item.commentList.length > 0" class="comment">
<view
v-for="(c, i) in item.commentList"
:key="i"
class="comment-item"
>
<text>{{ c.userName }}:</text>
<text>{{ c.content }}</text>
</view>
</view>
</view>

View File

@@ -29,9 +29,14 @@
/** 连续天数 */
const continuousDays = ref(0)
/** 给数子补零 */
const padZero = num => {
return num < 10 ? `0${num}` : num
}
const getData = async msg => {
const res = await getSignList({
signMonth: `${currentYear.value}-${currentMonth.value}`
signMonth: `${currentYear.value}-${padZero(currentMonth.value)}`
})
dateList.value = generateCalendar(
@@ -199,7 +204,9 @@
</view>
<!-- 切换 -->
<view class="switch-box">
<text class="date">{{ currentYear }}{{ currentMonth }}</text>
<text class="date">
{{ currentYear }}{{ padZero(currentMonth) }}
</text>
<view class="btn">
<uni-icons
type="left"

View File

@@ -64,6 +64,16 @@
margin: 16rpx 0;
}
.img-list {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 14rpx;
.item-img {
width: 180rpx;
height: 180rpx;
border-radius: 8rpx;
}
}
.one-img {
.item-img {
width: 410rpx;
height: 250rpx;

View File

@@ -8,7 +8,7 @@
onLoad(() => {
// 3秒后跳转
indexGo.value = setTimeout(() => {
reLaunch('/pages/news-list/news-list')
reLaunch('/TUIKit/components/TUIConversation/index')
}, 3000)
})

View File

@@ -4,11 +4,14 @@
import { getCategory, getProductList } from '@/api/mall'
import { navigateTo } from '@/utils/router'
const paging = ref(null)
/** 顶部分类选项 */
const topNavOptions = ref([])
const formData = reactive({
name: '',
type: '0'
type: '',
pageNum: 1,
pageSize: 15
})
/** 商品列表 */
const cardList = ref([])
@@ -18,14 +21,24 @@
topNavOptions.value = res.data
}
const getListData = async () => {
const res = await getProductList()
cardList.value = res.rows
console.log(res.rows)
const getListData = async (pageNum, pageSize) => {
try {
const res = await getProductList({
pageNum,
pageSize,
categoryId: formData.type,
productName: formData.name
})
paging.value.complete(res.rows)
} catch (error) {
paging.value.complete(false)
}
}
const onTop = value => {
formData.type = value
formData.name = ''
getListData(1, formData.pageSize)
}
const onGo = item => {
@@ -34,53 +47,77 @@
onLoad(async () => {
await categoryList()
await getListData()
// await getListData(1, formData.pageSize)
})
</script>
<template>
<view class="mall-list">
<view class="top-box">
<cb-search v-model="formData.name"></cb-search>
<view class="top-options">
<view
v-for="(item, index) in topNavOptions"
:key="index"
:class="{ active: item.id === formData.type }"
class="text"
@click="onTop(item.id)"
>
{{ item.categoryName }}
</view>
</view>
</view>
<!-- 商品卡片 -->
<view class="card-list">
<view
v-for="item in cardList"
:key="item.id"
class="card-item"
@click="onGo(item)"
>
<image
:src="item.mainImage"
mode="scaleToFill"
class="imghead"
></image>
<text class="title">{{ item.productName }}</text>
<view class="price">
<view class="num-box">
<text class="num"></text>
<text class="num">{{ item.minPrice }}</text>
<z-paging
ref="paging"
v-model="cardList"
:default-page-no="formData.pageNum"
:default-page-size="formData.pageSize"
safe-area-inset-bottom
use-safe-area-placeholder
:show-loading-more-no-more-view="false"
:paging-style="{ 'background-color': '#f9f9f9' }"
@query="getListData"
>
<view class="mall-list">
<view class="top-box">
<cb-search
v-model="formData.name"
@search="getListData()"
></cb-search>
<view class="top-options">
<view
:class="{ active: formData.type === '' }"
class="text"
@click="onTop('')"
>
全部
</view>
<view
v-for="(item, index) in topNavOptions"
:key="index"
:class="{ active: item.id === formData.type }"
class="text"
@click="onTop(item.id)"
>
{{ item.categoryName }}
</view>
<!-- <text class="buy">好评率99%</text> -->
</view>
<!-- 拼单数量 -->
<text class="bottom-name">拼单数量:{{ item.salesCount }}</text>
</view>
<!-- 商品卡片 -->
<view class="card-list">
<view
v-for="item in cardList"
:key="item.id"
class="card-item"
@click="onGo(item)"
>
<image
:src="item.mainImage"
mode="scaleToFill"
class="imghead"
></image>
<text class="title">{{ item.productName }}</text>
<view class="price">
<view class="num-box">
<text class="num"></text>
<text class="num">{{ item.minPrice }}</text>
</view>
<!-- <text class="buy">好评率99%</text> -->
</view>
<!-- 拼单数量 -->
<text class="bottom-name">
拼单数量:{{ item.salesCount }}
</text>
</view>
</view>
</view>
</view>
</z-paging>
</template>
<style lang="scss" scoped>

View File

@@ -17,11 +17,11 @@
icon: 'meeting',
url: '/pages/my-index/meeting-record/index'
},
{
name: '我的朋友圈',
icon: 'circle',
url: '/pages/discover/dynamic/dynamic'
},
// {
// name: '我的朋友圈',
// icon: 'circle',
// url: '/pages/discover/dynamic/dynamic'
// },
{
name: '我的收藏',
icon: 'collection',

View File

@@ -87,19 +87,25 @@ export const useUserStore = defineStore('user', () => {
const show = await showDialog('提示', '确定要退出登录吗?')
if (show) {
await userLogout()
userInfo.value = null
clearToken()
removeUserInfoData()
removeSig()
await TUILogin.logout().then(() => {
reLaunch('/pages/login/login')
})
// #ifdef APP-PLUS
await useLoginState().logout()
// #endif
await logout()
}
}
/**
* 退出登录(不带提示)
*/
const logout = async () => {
userInfo.value = null
clearToken()
removeUserInfoData()
removeSig()
await TUILogin.logout().then(() => {
reLaunch('/pages/login/login')
})
// #ifdef APP-PLUS
await useLoginState().logout()
// #endif
}
/** 刷新用户信息(如用户信息被修改) */
const refreshUserInfo = async () => {
const res = await getUserData()
@@ -119,6 +125,7 @@ export const useUserStore = defineStore('user', () => {
return {
userInfo,
tencentUserSig,
logout,
refreshUserInfo,
fetchUserInfo,
loginTencentIM,

View File

@@ -1,7 +1,11 @@
import { getToken, removeToken } from './storage'
import { useUserStore } from '../stores/user'
import { getToken } from './storage'
const BASE_URL = import.meta.env.VITE_SYSTEM_URL
// 防止多个 401 同时触发登出和跳转
let isHandling401 = false
/**
* 网络请求封装
* @param {Object} options 请求参数
@@ -50,12 +54,17 @@ const request = options => {
if (response.data.code === 200) {
resolve(response.data)
} else {
handleError(response.data.code, response.data)
// 注意:这里也要 reject否则调用方无法感知失败
const err = handleError(response.data.code, response.data)
reject(err || response.data)
// handleError(response.data.code, response.data)
}
} else {
// 状态码错误处理
handleError(response.statusCode, response.data)
reject(response)
// handleError(response.statusCode, response.data)
// reject(response)
const err = handleError(response.statusCode, response.data)
reject(err || response)
}
},
fail: error => {
@@ -84,21 +93,46 @@ const request = options => {
* @param {Object} data 响应数据
*/
const handleError = (statusCode, data) => {
// 如果是 401 且正在处理中,直接返回(避免重复处理)
if (statusCode === 401) {
if (isHandling401) {
return new Error('Unauthorized')
}
isHandling401 = true
uni.showModal({
title: '提示',
content: '登录已过期,请重新登录',
showCancel: false,
success: async () => {
await useUserStore().logout()
// 可选:跳转登录页
uni.redirectTo({ url: '/pages/login/index' })
console.log('登录已过期,====')
},
complete: () => {
// 重置标志,允许下次 401 处理(比如用户重新登录后再次过期)
isHandling401 = false
}
})
return new Error('Unauthorized')
}
switch (statusCode) {
case 401:
uni.showModal({
title: '提示',
content: '登录已过期,请重新登录',
showCancel: false,
success: () => {
// 清除本地存储的token并跳转到登录页
removeToken()
uni.navigateTo({
url: '/pages/login/index'
})
}
})
break
// case 401:
// console.log('登录已过期,====')
// uni.showModal({
// title: '提示',
// content: '登录已过期,请重新登录',
// showCancel: false,
// success: () => {
// useUserStore().logout()
// // uni.navigateTo({
// // url: '/pages/login/index'
// // })
// }
// })
// break
case 403:
uni.showToast({
title: '没有权限访问',
@@ -131,6 +165,8 @@ const handleError = (statusCode, data) => {
mask: true
})
}
return new Error(`Request failed with code: ${statusCode}`)
}
export default request