逻辑优化

This commit is contained in:
18631081161 2026-01-18 18:13:01 +08:00
parent 4ab93debc7
commit 93f7b104c8
12 changed files with 187 additions and 20 deletions

View File

@ -14,7 +14,14 @@ import { getToken } from '../utils/storage'
*/ */
export async function getSessions() { export async function getSessions() {
const response = await get('/chat/sessions') const response = await get('/chat/sessions')
return response // 统一返回格式
if (response && response.code === 0) {
return {
success: true,
data: Array.isArray(response.data) ? response.data : (response.data?.list || [])
}
}
return { success: false, data: [] }
} }
/** /**
@ -105,8 +112,11 @@ export async function getUnreadCount() {
*/ */
export async function uploadVoice(filePath) { export async function uploadVoice(filePath) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// 简化 URL 拼接
const uploadUrl = `${config.API_BASE_URL}/upload/voice`
uni.uploadFile({ uni.uploadFile({
url: config.API_BASE_URL.replace('/api/app', '') + '/api/app/upload/voice', url: uploadUrl,
filePath: filePath, filePath: filePath,
name: 'file', name: 'file',
header: { header: {
@ -114,18 +124,22 @@ export async function uploadVoice(filePath) {
}, },
success: (res) => { success: (res) => {
if (res.statusCode === 200) { if (res.statusCode === 200) {
const data = JSON.parse(res.data) try {
if (data.code === 0) { const data = JSON.parse(res.data)
resolve(data) if (data.code === 0) {
} else { resolve(data)
reject(new Error(data.message || '上传失败')) } else {
reject(new Error(data.message || '上传失败'))
}
} catch (e) {
reject(new Error('解析响应失败'))
} }
} else { } else {
reject(new Error('上传失败')) reject(new Error(`上传失败: ${res.statusCode}`))
} }
}, },
fail: (err) => { fail: (err) => {
reject(err) reject(new Error(err.errMsg || '网络错误'))
} }
}) })
}) })

View File

@ -21,7 +21,7 @@ const ENV = {
} }
// 当前环境 - 开发时使用 development打包时改为 production // 当前环境 - 开发时使用 development打包时改为 production
const CURRENT_ENV = 'production' const CURRENT_ENV = 'development'
// 导出配置 // 导出配置
export const config = { export const config = {

View File

@ -229,7 +229,10 @@
<!-- 消息状态 --> <!-- 消息状态 -->
<view v-if="message.isMine && message.messageType === MessageType.TEXT" class="message-status"> <view v-if="message.isMine && message.messageType === MessageType.TEXT" class="message-status">
<text v-if="message.status === MessageStatus.SENDING" class="status sending">发送中</text> <text v-if="message.status === MessageStatus.SENDING" class="status sending">发送中</text>
<text v-else-if="message.status === MessageStatus.FAILED" class="status failed">发送失败</text> <view v-else-if="message.status === MessageStatus.FAILED" class="status-failed-wrapper">
<text class="status failed">发送失败</text>
<text class="retry-btn" @click="handleRetryMessage(message)">重试</text>
</view>
</view> </view>
</view> </view>
@ -530,6 +533,36 @@ const handleSendMessage = async () => {
} }
} }
//
const handleRetryMessage = async (message) => {
if (message.status !== MessageStatus.FAILED) return
message.status = MessageStatus.SENDING
try {
const res = await sendMessage({
sessionId: sessionId.value,
receiverId: targetUserId.value,
messageType: message.messageType,
content: message.content
})
if (res && res.code === 0) {
message.status = MessageStatus.SENT
if (res.data?.messageId) {
message.id = res.data.messageId
}
uni.showToast({ title: '发送成功', icon: 'success' })
} else {
message.status = MessageStatus.FAILED
uni.showToast({ title: '发送失败', icon: 'none' })
}
} catch (error) {
message.status = MessageStatus.FAILED
uni.showToast({ title: '发送失败', icon: 'none' })
}
}
// //
const handleSwitchInputMode = () => { const handleSwitchInputMode = () => {
inputMode.value = inputMode.value === 'text' ? 'voice' : 'text' inputMode.value = inputMode.value === 'text' ? 'voice' : 'text'
@ -1444,6 +1477,18 @@ onUnmounted(() => {
color: #ff5252; color: #ff5252;
} }
} }
.status-failed-wrapper {
display: flex;
align-items: center;
gap: 12rpx;
}
.retry-btn {
font-size: 22rpx;
color: #1890ff;
text-decoration: underline;
}
} }
} }

View File

@ -215,7 +215,8 @@
if (res && res.code === 0) { if (res && res.code === 0) {
configStore.setHomeConfig({ configStore.setHomeConfig({
banners: res.data?.banners || [], banners: res.data?.banners || [],
kingKongs: res.data?.kingKongs || [] kingKongs: res.data?.kingKongs || [],
defaultAvatar: res.data?.defaultAvatar || ''
}) })
} }
} catch (error) { } catch (error) {

View File

@ -122,12 +122,12 @@ export default {
// //
const handleUserAgreement = () => { const handleUserAgreement = () => {
uni.showToast({ title: '功能开发中', icon: 'none' }) uni.navigateTo({ url: '/pages/agreement/index?type=user' })
} }
// //
const handlePrivacyPolicy = () => { const handlePrivacyPolicy = () => {
uni.showToast({ title: '功能开发中', icon: 'none' }) uni.navigateTo({ url: '/pages/agreement/index?type=privacy' })
} }
onMounted(() => { onMounted(() => {

View File

@ -93,6 +93,7 @@ import { useUserStore } from '@/store/user.js'
import { useConfigStore } from '@/store/config.js' import { useConfigStore } from '@/store/config.js'
import { getMemberInfo } from '@/api/member.js' import { getMemberInfo } from '@/api/member.js'
import { createOrder } from '@/api/order.js' import { createOrder } from '@/api/order.js'
import { getFullImageUrl } from '@/utils/image.js'
import Loading from '@/components/Loading/index.vue' import Loading from '@/components/Loading/index.vue'
export default { export default {
@ -181,7 +182,7 @@ export default {
const userInfo = computed(() => ({ const userInfo = computed(() => ({
userId: userStore.userId, userId: userStore.userId,
nickname: userStore.nickname, nickname: userStore.nickname,
avatar: userStore.avatar, avatar: getFullImageUrl(userStore.avatar),
isMember: userStore.isMember, isMember: userStore.isMember,
memberLevel: userStore.memberLevel memberLevel: userStore.memberLevel
})) }))

View File

@ -108,7 +108,19 @@
</view> </view>
</view> </view>
<!-- 空状态 -->
<view class="empty-state" v-else-if="!pageLoading && !userStore.isLoggedIn">
<image src="/static/ic_empty.png" mode="aspectFit" class="empty-icon" />
<text class="empty-text">登录后查看消息</text>
<button class="login-btn" @click="handleLogin">立即登录</button>
</view>
<!-- 无聊天记录 -->
<view class="empty-state" v-else-if="!pageLoading && sessions.length === 0">
<image src="/static/ic_empty.png" mode="aspectFit" class="empty-icon" />
<text class="empty-text">暂无聊天记录</text>
<text class="empty-tip">去首页看看心仪的对象吧</text>
</view>
</view> </view>
</scroll-view> </scroll-view>
</view> </view>
@ -273,6 +285,11 @@ export default {
}) })
} }
//
const handleLogin = () => {
uni.navigateTo({ url: '/pages/login/index' })
}
onMounted(() => { onMounted(() => {
getSystemInfo() getSystemInfo()
initPage() initPage()
@ -289,10 +306,12 @@ export default {
sessions, sessions,
interactCounts, interactCounts,
defaultAvatar, defaultAvatar,
userStore,
formatTime, formatTime,
navigateTo, navigateTo,
handleSessionClick, handleSessionClick,
handleRefresh, handleRefresh,
handleLogin,
initPage, initPage,
loadInteractCounts, loadInteractCounts,
loadSessions loadSessions
@ -591,4 +610,47 @@ export default {
} }
} }
} }
//
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 120rpx 40rpx;
.empty-icon {
width: 200rpx;
height: 200rpx;
margin-bottom: 32rpx;
opacity: 0.6;
}
.empty-text {
font-size: 32rpx;
color: #666;
margin-bottom: 16rpx;
}
.empty-tip {
font-size: 26rpx;
color: #999;
}
.login-btn {
margin-top: 32rpx;
width: 240rpx;
height: 80rpx;
line-height: 80rpx;
background: linear-gradient(135deg, #FFBDC2 0%, #FF8A93 100%);
border-radius: 40rpx;
font-size: 30rpx;
color: #fff;
border: none;
&::after {
border: none;
}
}
}
</style> </style>

View File

@ -596,9 +596,19 @@ const getSystemInfo = () => {
}) })
} }
// //
const handleBack = () => { const handleBack = () => {
uni.navigateBack() uni.showModal({
title: '提示',
content: '确定要离开吗?已填写的内容将不会保存',
confirmText: '离开',
cancelText: '继续填写',
success: (res) => {
if (res.confirm) {
uni.navigateBack()
}
}
})
} }
// //
@ -1332,6 +1342,26 @@ onMounted(() => {
}) })
</script> </script>
<script>
// script
export default {
onBackPress() {
uni.showModal({
title: '提示',
content: '确定要离开吗?已填写的内容将不会保存',
confirmText: '离开',
cancelText: '继续填写',
success: (res) => {
if (res.confirm) {
uni.navigateBack()
}
}
})
return true // true
}
}
</script>
<style lang="scss" scoped> <style lang="scss" scoped>
.profile-edit-page { .profile-edit-page {
min-height: 100vh; min-height: 100vh;

View File

@ -133,7 +133,7 @@ export default {
// 使 // 使
const uploadRes = await new Promise((resolve, reject) => { const uploadRes = await new Promise((resolve, reject) => {
uni.uploadFile({ uni.uploadFile({
url: `${API_BASE_URL}/upload`, url: `${API_BASE_URL}/api/app/upload/image`,
filePath: tempFilePath, filePath: tempFilePath,
name: 'file', name: 'file',
header: { header: {

View File

@ -54,9 +54,13 @@ class SignalRClient {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
try { try {
// 构建 WebSocket URL // 构建 WebSocket URL
// API_BASE_URL: http://localhost:5000/api/app 或 https://xxx.com/api/app
// Hub URL: ws://localhost:5000/hubs/chat 或 wss://xxx.com/hubs/chat
let baseUrl = config.API_BASE_URL let baseUrl = config.API_BASE_URL
// 移除 /api/app 后缀,获取服务器根地址
const serverUrl = baseUrl.replace(/\/api\/app$/, '')
// 将 http/https 替换为 ws/wss // 将 http/https 替换为 ws/wss
const wsUrl = baseUrl.replace(/^http/, 'ws').replace('/api/app', '') + '/hubs/chat' const wsUrl = serverUrl.replace(/^http/, 'ws') + '/hubs/chat'
console.log('[SignalR] 正在连接:', wsUrl) console.log('[SignalR] 正在连接:', wsUrl)

View File

@ -51,6 +51,11 @@ public class HomeConfigResponse
/// 金刚位列表 /// 金刚位列表
/// </summary> /// </summary>
public List<KingKongResponse> KingKongs { get; set; } = new(); public List<KingKongResponse> KingKongs { get; set; } = new();
/// <summary>
/// 默认头像URL
/// </summary>
public string? DefaultAvatar { get; set; }
} }
/// <summary> /// <summary>

View File

@ -13,6 +13,7 @@ public class ConfigService : IConfigService
private readonly IRepository<Banner> _bannerRepository; private readonly IRepository<Banner> _bannerRepository;
private readonly IRepository<KingKong> _kingKongRepository; private readonly IRepository<KingKong> _kingKongRepository;
private readonly IRepository<PopupConfig> _popupConfigRepository; private readonly IRepository<PopupConfig> _popupConfigRepository;
private readonly ISystemConfigService _systemConfigService;
private readonly ILogger<ConfigService> _logger; private readonly ILogger<ConfigService> _logger;
/// <summary> /// <summary>
@ -29,11 +30,13 @@ public class ConfigService : IConfigService
IRepository<Banner> bannerRepository, IRepository<Banner> bannerRepository,
IRepository<KingKong> kingKongRepository, IRepository<KingKong> kingKongRepository,
IRepository<PopupConfig> popupConfigRepository, IRepository<PopupConfig> popupConfigRepository,
ISystemConfigService systemConfigService,
ILogger<ConfigService> logger) ILogger<ConfigService> logger)
{ {
_bannerRepository = bannerRepository; _bannerRepository = bannerRepository;
_kingKongRepository = kingKongRepository; _kingKongRepository = kingKongRepository;
_popupConfigRepository = popupConfigRepository; _popupConfigRepository = popupConfigRepository;
_systemConfigService = systemConfigService;
_logger = logger; _logger = logger;
} }
@ -42,11 +45,13 @@ public class ConfigService : IConfigService
{ {
var banners = await GetBannersAsync(); var banners = await GetBannersAsync();
var kingKongs = await GetKingKongsAsync(); var kingKongs = await GetKingKongsAsync();
var defaultAvatar = await _systemConfigService.GetDefaultAvatarAsync();
return new HomeConfigResponse return new HomeConfigResponse
{ {
Banners = banners, Banners = banners,
KingKongs = kingKongs KingKongs = kingKongs,
DefaultAvatar = defaultAvatar
}; };
} }