细节优化.
This commit is contained in:
parent
6f1892b0e7
commit
b833b83d02
|
|
@ -42,8 +42,19 @@ export async function getAllPopupConfigs() {
|
|||
return response
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认头像配置
|
||||
*
|
||||
* @returns {Promise<Object>} 默认头像URL
|
||||
*/
|
||||
export async function getDefaultAvatarConfig() {
|
||||
const response = await get('/config/defaultAvatar', {}, { needAuth: false })
|
||||
return response
|
||||
}
|
||||
|
||||
export default {
|
||||
getHomeConfig,
|
||||
getPopupConfig,
|
||||
getAllPopupConfigs
|
||||
getAllPopupConfigs,
|
||||
getDefaultAvatarConfig
|
||||
}
|
||||
|
|
|
|||
44
miniapp/api/message.js
Normal file
44
miniapp/api/message.js
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
* 消息相关 API
|
||||
*/
|
||||
|
||||
import { get, post } from './request.js'
|
||||
|
||||
/**
|
||||
* 获取系统消息列表
|
||||
* @param {Object} params - 查询参数
|
||||
* @param {number} params.pageIndex - 页码
|
||||
* @param {number} params.pageSize - 每页数量
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export const getSystemMessages = (params) => {
|
||||
return get('/notification/list', {
|
||||
pageIndex: params.pageIndex,
|
||||
pageSize: params.pageSize
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记消息已读
|
||||
* @param {number} messageId - 消息ID
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export const markMessageRead = (messageId) => {
|
||||
return post('/notification/read', { notificationId: messageId })
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记所有系统消息已读
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export const markAllSystemMessagesRead = () => {
|
||||
return post('/notification/read', { markAll: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取未读消息数量
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export const getUnreadCount = () => {
|
||||
return get('/notification/unreadCount')
|
||||
}
|
||||
|
|
@ -15,6 +15,13 @@
|
|||
"navigationBarTitleText": "消息"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/message/system",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "系统消息"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/mine/index",
|
||||
"style": {
|
||||
|
|
|
|||
|
|
@ -121,7 +121,8 @@
|
|||
} from '@/store/config.js'
|
||||
import {
|
||||
getHomeConfig,
|
||||
getPopupConfig
|
||||
getPopupConfig,
|
||||
getDefaultAvatarConfig
|
||||
} from '@/api/config.js'
|
||||
import {
|
||||
getRecommend
|
||||
|
|
@ -247,6 +248,18 @@
|
|||
}
|
||||
}
|
||||
|
||||
// 加载默认头像配置
|
||||
const loadDefaultAvatarConfig = async () => {
|
||||
try {
|
||||
const res = await getDefaultAvatarConfig()
|
||||
if (res && res.code === 0 && res.data?.avatarUrl) {
|
||||
configStore.setDefaultAvatarConfig(res.data.avatarUrl)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载默认头像配置失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 加载推荐用户列表
|
||||
const loadRecommendList = async (isLoadMore = false) => {
|
||||
if (listLoading.value) return
|
||||
|
|
@ -309,7 +322,8 @@
|
|||
await Promise.all([
|
||||
loadHomeConfig(),
|
||||
loadDailyPopup(),
|
||||
loadMemberAdConfig()
|
||||
loadMemberAdConfig(),
|
||||
loadDefaultAvatarConfig()
|
||||
])
|
||||
|
||||
// 加载推荐列表(无论是否登录都加载)
|
||||
|
|
|
|||
|
|
@ -84,10 +84,11 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { getFavoritedMe, getMyFavorite } from '@/api/interact.js'
|
||||
import { formatTimestamp } from '@/utils/format.js'
|
||||
import { useUserStore } from '@/store/user.js'
|
||||
import { useConfigStore } from '@/store/config.js'
|
||||
import Loading from '@/components/Loading/index.vue'
|
||||
import Empty from '@/components/Empty/index.vue'
|
||||
|
||||
|
|
@ -99,6 +100,7 @@ export default {
|
|||
},
|
||||
setup() {
|
||||
const userStore = useUserStore()
|
||||
const configStore = useConfigStore()
|
||||
|
||||
// 页面状态
|
||||
const pageLoading = ref(true)
|
||||
|
|
@ -111,8 +113,8 @@ export default {
|
|||
const pageIndex = ref(1)
|
||||
const pageSize = 20
|
||||
|
||||
// 默认头像
|
||||
const defaultAvatar = '/static/logo.png'
|
||||
// 从 configStore 获取默认头像
|
||||
const defaultAvatar = computed(() => configStore.defaultAvatar || '/static/logo.png')
|
||||
|
||||
// 加载列表
|
||||
const loadList = async (refresh = false) => {
|
||||
|
|
|
|||
|
|
@ -84,10 +84,11 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { getFavoritedMe, getMyFavorite } from '@/api/interact.js'
|
||||
import { formatTimestamp } from '@/utils/format.js'
|
||||
import { useUserStore } from '@/store/user.js'
|
||||
import { useConfigStore } from '@/store/config.js'
|
||||
import Loading from '@/components/Loading/index.vue'
|
||||
import Empty from '@/components/Empty/index.vue'
|
||||
|
||||
|
|
@ -99,6 +100,7 @@ export default {
|
|||
},
|
||||
setup() {
|
||||
const userStore = useUserStore()
|
||||
const configStore = useConfigStore()
|
||||
|
||||
// 页面状态
|
||||
const pageLoading = ref(true)
|
||||
|
|
@ -111,8 +113,8 @@ export default {
|
|||
const pageIndex = ref(1)
|
||||
const pageSize = 20
|
||||
|
||||
// 默认头像
|
||||
const defaultAvatar = '/static/logo.png'
|
||||
// 从 configStore 获取默认头像
|
||||
const defaultAvatar = computed(() => configStore.defaultAvatar || '/static/logo.png')
|
||||
|
||||
// 加载列表
|
||||
const loadList = async (refresh = false) => {
|
||||
|
|
|
|||
|
|
@ -84,10 +84,11 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { getUnlockedMe, getMyUnlocked } from '@/api/interact.js'
|
||||
import { formatTimestamp } from '@/utils/format.js'
|
||||
import { useUserStore } from '@/store/user.js'
|
||||
import { useConfigStore } from '@/store/config.js'
|
||||
import Loading from '@/components/Loading/index.vue'
|
||||
import Empty from '@/components/Empty/index.vue'
|
||||
|
||||
|
|
@ -99,6 +100,7 @@ export default {
|
|||
},
|
||||
setup() {
|
||||
const userStore = useUserStore()
|
||||
const configStore = useConfigStore()
|
||||
|
||||
// 页面状态
|
||||
const pageLoading = ref(true)
|
||||
|
|
@ -111,8 +113,8 @@ export default {
|
|||
const pageIndex = ref(1)
|
||||
const pageSize = 20
|
||||
|
||||
// 默认头像
|
||||
const defaultAvatar = '/static/logo.png'
|
||||
// 从 configStore 获取默认头像
|
||||
const defaultAvatar = computed(() => configStore.defaultAvatar || '/static/logo.png')
|
||||
|
||||
// 加载列表
|
||||
const loadList = async (refresh = false) => {
|
||||
|
|
|
|||
|
|
@ -84,10 +84,11 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { getViewedMe, getMyViewed } from '@/api/interact.js'
|
||||
import { formatTimestamp } from '@/utils/format.js'
|
||||
import { useUserStore } from '@/store/user.js'
|
||||
import { useConfigStore } from '@/store/config.js'
|
||||
import Loading from '@/components/Loading/index.vue'
|
||||
import Empty from '@/components/Empty/index.vue'
|
||||
|
||||
|
|
@ -99,6 +100,7 @@ export default {
|
|||
},
|
||||
setup() {
|
||||
const userStore = useUserStore()
|
||||
const configStore = useConfigStore()
|
||||
|
||||
// 页面状态
|
||||
const pageLoading = ref(true)
|
||||
|
|
@ -111,8 +113,8 @@ export default {
|
|||
const pageIndex = ref(1)
|
||||
const pageSize = 20
|
||||
|
||||
// 默认头像
|
||||
const defaultAvatar = '/static/logo.png'
|
||||
// 从 configStore 获取默认头像
|
||||
const defaultAvatar = computed(() => configStore.defaultAvatar || '/static/logo.png')
|
||||
|
||||
// 加载列表
|
||||
const loadList = async (refresh = false) => {
|
||||
|
|
|
|||
|
|
@ -84,10 +84,11 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { getUnlockedMe, getMyUnlocked } from '@/api/interact.js'
|
||||
import { formatTimestamp } from '@/utils/format.js'
|
||||
import { useUserStore } from '@/store/user.js'
|
||||
import { useConfigStore } from '@/store/config.js'
|
||||
import Loading from '@/components/Loading/index.vue'
|
||||
import Empty from '@/components/Empty/index.vue'
|
||||
|
||||
|
|
@ -99,6 +100,7 @@ export default {
|
|||
},
|
||||
setup() {
|
||||
const userStore = useUserStore()
|
||||
const configStore = useConfigStore()
|
||||
|
||||
// 页面状态
|
||||
const pageLoading = ref(true)
|
||||
|
|
@ -111,8 +113,8 @@ export default {
|
|||
const pageIndex = ref(1)
|
||||
const pageSize = 20
|
||||
|
||||
// 默认头像
|
||||
const defaultAvatar = '/static/logo.png'
|
||||
// 从 configStore 获取默认头像
|
||||
const defaultAvatar = computed(() => configStore.defaultAvatar || '/static/logo.png')
|
||||
|
||||
// 加载列表
|
||||
const loadList = async (refresh = false) => {
|
||||
|
|
|
|||
|
|
@ -84,10 +84,11 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { getViewedMe, getMyViewed } from '@/api/interact.js'
|
||||
import { formatTimestamp } from '@/utils/format.js'
|
||||
import { useUserStore } from '@/store/user.js'
|
||||
import { useConfigStore } from '@/store/config.js'
|
||||
import Loading from '@/components/Loading/index.vue'
|
||||
import Empty from '@/components/Empty/index.vue'
|
||||
|
||||
|
|
@ -99,6 +100,7 @@ export default {
|
|||
},
|
||||
setup() {
|
||||
const userStore = useUserStore()
|
||||
const configStore = useConfigStore()
|
||||
|
||||
// 页面状态
|
||||
const pageLoading = ref(true)
|
||||
|
|
@ -111,8 +113,8 @@ export default {
|
|||
const pageIndex = ref(1)
|
||||
const pageSize = 20
|
||||
|
||||
// 默认头像
|
||||
const defaultAvatar = '/static/logo.png'
|
||||
// 从 configStore 获取默认头像
|
||||
const defaultAvatar = computed(() => configStore.defaultAvatar || '/static/logo.png')
|
||||
|
||||
// 加载列表
|
||||
const loadList = async (refresh = false) => {
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@
|
|||
<script>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useUserStore } from '@/store/user.js'
|
||||
import { useConfigStore } from '@/store/config.js'
|
||||
import { getMemberInfo } from '@/api/member.js'
|
||||
import { createOrder } from '@/api/order.js'
|
||||
import Loading from '@/components/Loading/index.vue'
|
||||
|
|
@ -101,14 +102,15 @@ export default {
|
|||
},
|
||||
setup() {
|
||||
const userStore = useUserStore()
|
||||
const configStore = useConfigStore()
|
||||
|
||||
// 页面状态
|
||||
const pageLoading = ref(true)
|
||||
const purchasing = ref(false)
|
||||
const selectedTier = ref(1) // 默认选中第一个
|
||||
|
||||
// 默认头像
|
||||
const defaultAvatar = '/static/logo.png'
|
||||
// 从 configStore 获取默认头像
|
||||
const defaultAvatar = computed(() => configStore.defaultAvatar || '/static/logo.png')
|
||||
|
||||
// 会员等级映射
|
||||
const memberLevelMap = {
|
||||
|
|
|
|||
|
|
@ -115,9 +115,10 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useChatStore } from '@/store/chat.js'
|
||||
import { useUserStore } from '@/store/user.js'
|
||||
import { useConfigStore } from '@/store/config.js'
|
||||
import { getSessions } from '@/api/chat.js'
|
||||
import { getViewedMe, getFavoritedMe, getUnlockedMe } from '@/api/interact.js'
|
||||
import { formatTimestamp } from '@/utils/format.js'
|
||||
|
|
@ -131,6 +132,7 @@ export default {
|
|||
setup() {
|
||||
const chatStore = useChatStore()
|
||||
const userStore = useUserStore()
|
||||
const configStore = useConfigStore()
|
||||
|
||||
// 页面状态
|
||||
const pageLoading = ref(true)
|
||||
|
|
@ -151,8 +153,8 @@ export default {
|
|||
unlockedMe: 0
|
||||
})
|
||||
|
||||
// 默认头像
|
||||
const defaultAvatar = '/static/logo.png'
|
||||
// 从 configStore 获取默认头像
|
||||
const defaultAvatar = computed(() => configStore.defaultAvatar || '/static/logo.png')
|
||||
|
||||
// 获取系统信息
|
||||
const getSystemInfo = () => {
|
||||
|
|
|
|||
377
miniapp/pages/message/system.vue
Normal file
377
miniapp/pages/message/system.vue
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
<template>
|
||||
<view class="system-message-page">
|
||||
<!-- 页面加载状态 -->
|
||||
<Loading type="page" :loading="pageLoading" />
|
||||
|
||||
<!-- 顶部背景图 -->
|
||||
<view class="top-bg">
|
||||
<image src="/static/title_bg.png" mode="aspectFill" class="bg-img" />
|
||||
</view>
|
||||
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="navbar-content">
|
||||
<view class="navbar-back" @click="handleBack">
|
||||
<text class="back-icon">‹</text>
|
||||
</view>
|
||||
<text class="navbar-title">系统消息</text>
|
||||
<view class="navbar-placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 筛选标签 -->
|
||||
<view class="filter-section" :style="{ top: (statusBarHeight + 44) + 'px' }">
|
||||
<view
|
||||
class="filter-tag"
|
||||
:class="{ active: currentFilter === 'all' }"
|
||||
@click="handleFilterChange('all')"
|
||||
>
|
||||
全部
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 消息列表 -->
|
||||
<scroll-view
|
||||
class="message-scroll"
|
||||
scroll-y
|
||||
:style="{
|
||||
top: (statusBarHeight + 44 + 60) + 'px',
|
||||
height: scrollHeight + 'px'
|
||||
}"
|
||||
refresher-enabled
|
||||
:refresher-triggered="isRefreshing"
|
||||
@refresherrefresh="handleRefresh"
|
||||
@scrolltolower="handleLoadMore"
|
||||
>
|
||||
<view class="message-list" v-if="messageList.length > 0">
|
||||
<view
|
||||
class="message-item"
|
||||
v-for="item in messageList"
|
||||
:key="item.id"
|
||||
@click="handleMessageClick(item)"
|
||||
>
|
||||
<view class="message-title">{{ item.title }}</view>
|
||||
<view class="message-content">{{ item.content }}</view>
|
||||
<view class="message-time">{{ formatTime(item.createTime) }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view class="empty-wrapper" v-else-if="!pageLoading">
|
||||
<Empty text="暂无系统消息" :showButton="false" />
|
||||
</view>
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<Loading type="more" :loading="listLoading" :noMore="noMoreData" />
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useUserStore } from '@/store/user.js'
|
||||
import { getSystemMessages } from '@/api/message.js'
|
||||
import { formatTimestamp } from '@/utils/format.js'
|
||||
import Loading from '@/components/Loading/index.vue'
|
||||
import Empty from '@/components/Empty/index.vue'
|
||||
|
||||
export default {
|
||||
name: 'SystemMessagePage',
|
||||
components: {
|
||||
Loading,
|
||||
Empty
|
||||
},
|
||||
setup() {
|
||||
const userStore = useUserStore()
|
||||
|
||||
// 状态栏高度
|
||||
const statusBarHeight = ref(20)
|
||||
const scrollHeight = ref(500)
|
||||
|
||||
// 页面状态
|
||||
const pageLoading = ref(true)
|
||||
const listLoading = ref(false)
|
||||
const noMoreData = ref(false)
|
||||
const isRefreshing = ref(false)
|
||||
|
||||
// 筛选
|
||||
const currentFilter = ref('all')
|
||||
|
||||
// 分页
|
||||
const pageIndex = ref(1)
|
||||
const pageSize = 20
|
||||
|
||||
// 数据
|
||||
const messageList = ref([])
|
||||
|
||||
// 获取系统信息
|
||||
const getSystemInfo = () => {
|
||||
uni.getSystemInfo({
|
||||
success: (res) => {
|
||||
statusBarHeight.value = res.statusBarHeight || 20
|
||||
// 计算滚动区域高度:窗口高度 - 导航栏高度 - 筛选区域高度
|
||||
const navbarHeight = statusBarHeight.value + 44
|
||||
const filterHeight = 60
|
||||
scrollHeight.value = res.windowHeight - navbarHeight - filterHeight
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 返回
|
||||
const handleBack = () => {
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
// 加载消息列表
|
||||
const loadMessages = async (isLoadMore = false) => {
|
||||
if (listLoading.value) return
|
||||
if (isLoadMore && noMoreData.value) return
|
||||
|
||||
listLoading.value = true
|
||||
try {
|
||||
if (!isLoadMore) {
|
||||
pageIndex.value = 1
|
||||
messageList.value = []
|
||||
noMoreData.value = false
|
||||
}
|
||||
|
||||
const res = await getSystemMessages({
|
||||
pageIndex: pageIndex.value,
|
||||
pageSize: pageSize
|
||||
})
|
||||
|
||||
if (res && (res.code === 0 || res.success) && res.data) {
|
||||
const items = res.data.items || res.data.list || []
|
||||
const total = res.data.total || 0
|
||||
|
||||
if (isLoadMore) {
|
||||
messageList.value = [...messageList.value, ...items]
|
||||
} else {
|
||||
messageList.value = items
|
||||
}
|
||||
|
||||
noMoreData.value = messageList.value.length >= total || items.length < pageSize
|
||||
pageIndex.value++
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载系统消息失败:', error)
|
||||
} finally {
|
||||
listLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化页面
|
||||
const initPage = async () => {
|
||||
pageLoading.value = true
|
||||
try {
|
||||
getSystemInfo()
|
||||
userStore.restoreFromStorage()
|
||||
await loadMessages()
|
||||
} catch (error) {
|
||||
console.error('初始化页面失败:', error)
|
||||
} finally {
|
||||
pageLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 筛选切换
|
||||
const handleFilterChange = (filter) => {
|
||||
if (currentFilter.value === filter) return
|
||||
currentFilter.value = filter
|
||||
loadMessages()
|
||||
}
|
||||
|
||||
// 下拉刷新
|
||||
const handleRefresh = async () => {
|
||||
isRefreshing.value = true
|
||||
try {
|
||||
await loadMessages()
|
||||
} finally {
|
||||
isRefreshing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 加载更多
|
||||
const handleLoadMore = () => {
|
||||
if (!noMoreData.value && !listLoading.value) {
|
||||
loadMessages(true)
|
||||
}
|
||||
}
|
||||
|
||||
// 点击消息
|
||||
const handleMessageClick = (item) => {
|
||||
// 如果有跳转链接
|
||||
if (item.linkUrl) {
|
||||
uni.navigateTo({ url: item.linkUrl })
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化时间
|
||||
const formatTime = (timestamp) => {
|
||||
return formatTimestamp(timestamp)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initPage()
|
||||
})
|
||||
|
||||
return {
|
||||
statusBarHeight,
|
||||
scrollHeight,
|
||||
pageLoading,
|
||||
listLoading,
|
||||
noMoreData,
|
||||
isRefreshing,
|
||||
currentFilter,
|
||||
messageList,
|
||||
handleBack,
|
||||
handleFilterChange,
|
||||
handleRefresh,
|
||||
handleLoadMore,
|
||||
handleMessageClick,
|
||||
formatTime
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.system-message-page {
|
||||
height: 100vh;
|
||||
background-color: #f5f6fa;
|
||||
}
|
||||
|
||||
// 顶部背景图
|
||||
.top-bg {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 400rpx;
|
||||
z-index: 0;
|
||||
|
||||
.bg-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
// 自定义导航栏
|
||||
.custom-navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
|
||||
.navbar-content {
|
||||
position: relative;
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 24rpx;
|
||||
|
||||
.navbar-back {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.back-icon {
|
||||
font-size: 56rpx;
|
||||
color: #333;
|
||||
font-weight: 300;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.navbar-placeholder {
|
||||
width: 80rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 筛选标签
|
||||
.filter-section {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 10;
|
||||
padding: 16rpx 24rpx;
|
||||
|
||||
.filter-tag {
|
||||
display: inline-block;
|
||||
padding: 12rpx 32rpx;
|
||||
font-size: 28rpx;
|
||||
color: #ff6b6b;
|
||||
background: #fff;
|
||||
border: 2rpx solid #ff6b6b;
|
||||
border-radius: 32rpx;
|
||||
|
||||
&.active {
|
||||
color: #ff6b6b;
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 消息滚动区域
|
||||
.message-scroll {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: #f5f6fa;
|
||||
}
|
||||
|
||||
// 消息列表
|
||||
.message-list {
|
||||
padding: 24rpx;
|
||||
min-height: 100%;
|
||||
|
||||
.message-item {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
&:active {
|
||||
background: #f8f8f8;
|
||||
}
|
||||
|
||||
.message-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 16rpx;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.message-content {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
line-height: 1.8;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.message-time {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 空状态
|
||||
.empty-wrapper {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -20,8 +20,9 @@
|
|||
<view class="user-info-row" @click="handlePersonalProfile">
|
||||
<image
|
||||
class="user-avatar"
|
||||
:src="userInfo.avatar || defaultAvatar"
|
||||
:src="avatarLoadError ? defaultAvatar : (userInfo.avatar || defaultAvatar)"
|
||||
mode="aspectFill"
|
||||
@error="onAvatarError"
|
||||
/>
|
||||
<view class="user-detail">
|
||||
<view class="user-name-row">
|
||||
|
|
@ -130,9 +131,11 @@
|
|||
<script>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useUserStore } from '@/store/user.js'
|
||||
import { useConfigStore } from '@/store/config.js'
|
||||
import { getMyProfile } from '@/api/profile.js'
|
||||
import { login } from '@/api/auth.js'
|
||||
import { getViewedMe, getFavoritedMe, getUnlockedMe } from '@/api/interact.js'
|
||||
import { getFullImageUrl } from '@/utils/image.js'
|
||||
import Loading from '@/components/Loading/index.vue'
|
||||
|
||||
export default {
|
||||
|
|
@ -142,13 +145,14 @@ export default {
|
|||
},
|
||||
setup() {
|
||||
const userStore = useUserStore()
|
||||
const configStore = useConfigStore()
|
||||
|
||||
// 页面状态
|
||||
const pageLoading = ref(true)
|
||||
const statusBarHeight = ref(20)
|
||||
|
||||
// 默认头像
|
||||
const defaultAvatar = '/static/logo.png'
|
||||
// 从 configStore 获取默认头像
|
||||
const defaultAvatar = computed(() => configStore.defaultAvatar || '/static/logo.png')
|
||||
|
||||
// 互动统计
|
||||
const interactCounts = ref({
|
||||
|
|
@ -172,7 +176,7 @@ export default {
|
|||
const userInfo = computed(() => ({
|
||||
userId: userStore.userId,
|
||||
nickname: userStore.nickname,
|
||||
avatar: userStore.avatar,
|
||||
avatar: getFullImageUrl(userStore.avatar),
|
||||
xiangQinNo: userStore.xiangQinNo,
|
||||
isProfileCompleted: userStore.isProfileCompleted,
|
||||
isMember: userStore.isMember,
|
||||
|
|
@ -289,6 +293,12 @@ export default {
|
|||
uni.navigateTo({ url })
|
||||
}
|
||||
|
||||
// 头像加载失败时的处理
|
||||
const avatarLoadError = ref(false)
|
||||
const onAvatarError = () => {
|
||||
avatarLoadError.value = true
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getSystemInfo()
|
||||
initPage()
|
||||
|
|
@ -301,6 +311,8 @@ export default {
|
|||
userInfo,
|
||||
interactCounts,
|
||||
defaultAvatar,
|
||||
avatarLoadError,
|
||||
onAvatarError,
|
||||
handleLogin,
|
||||
handlePersonalProfile,
|
||||
handlePreviewProfile,
|
||||
|
|
@ -318,6 +330,10 @@ export default {
|
|||
onShow() {
|
||||
const userStore = useUserStore()
|
||||
userStore.restoreFromStorage()
|
||||
// 重置头像加载错误状态,以便重新尝试加载
|
||||
if (this.avatarLoadError !== undefined) {
|
||||
this.avatarLoadError = false
|
||||
}
|
||||
if (userStore.isLoggedIn && this.loadInteractCounts) {
|
||||
this.loadInteractCounts()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,7 +68,8 @@
|
|||
<text class="gender-year" :class="{ male: userDetail.childGender === 1 }">
|
||||
{{ genderText }} · {{ userDetail.birthYear }}年
|
||||
</text>
|
||||
<button class="share-btn" @click="handleShare">分享</button>
|
||||
<view class="flex-spacer"></view>
|
||||
<button class="share-btn" open-type="share">分享</button>
|
||||
</view>
|
||||
|
||||
<view class="info-grid">
|
||||
|
|
@ -257,6 +258,7 @@
|
|||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { onShareAppMessage } from '@dcloudio/uni-app'
|
||||
import { useUserStore } from '@/store/user.js'
|
||||
import { getUserDetail } from '@/api/user.js'
|
||||
import { favorite, unlock } from '@/api/interact.js'
|
||||
|
|
@ -292,7 +294,16 @@ const getSystemInfo = () => {
|
|||
|
||||
// 返回
|
||||
const handleBack = () => {
|
||||
uni.navigateBack()
|
||||
const pages = getCurrentPages()
|
||||
if (pages.length > 1) {
|
||||
// 有上一页,正常返回
|
||||
uni.navigateBack()
|
||||
} else {
|
||||
// 没有上一页(从分享链接进入),跳转到首页
|
||||
uni.switchTab({
|
||||
url: '/pages/index/index'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 选项映射
|
||||
|
|
@ -521,11 +532,6 @@ const handleFavorite = async () => {
|
|||
}
|
||||
}
|
||||
|
||||
// 分享处理
|
||||
const handleShare = () => {
|
||||
uni.showToast({ title: '请点击右上角分享', icon: 'none' })
|
||||
}
|
||||
|
||||
// 联系处理
|
||||
const handleContact = () => {
|
||||
// 检查是否登录
|
||||
|
|
@ -653,12 +659,11 @@ onMounted(() => {
|
|||
})
|
||||
|
||||
// 分享配置
|
||||
defineExpose({
|
||||
onShareAppMessage() {
|
||||
return {
|
||||
title: userDetail.value?.nickname ? `${userDetail.value.nickname}的相亲资料` : '相宜相亲',
|
||||
path: `/pages/profile/detail?userId=${userId.value}`
|
||||
}
|
||||
onShareAppMessage(() => {
|
||||
return {
|
||||
title: userDetail.value?.nickname ? `${userDetail.value.nickname}的相亲资料` : '相宜相亲',
|
||||
path: `/pages/profile/detail?userId=${userId.value}`,
|
||||
imageUrl: '/static/logo.png'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
|
@ -826,7 +831,6 @@ defineExpose({
|
|||
.gender-year-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.gender-year {
|
||||
|
|
@ -839,13 +843,17 @@ defineExpose({
|
|||
}
|
||||
}
|
||||
|
||||
.flex-spacer {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.share-btn {
|
||||
padding: 12rpx 32rpx;
|
||||
font-size: 26rpx;
|
||||
color: #ff6b6b;
|
||||
background: #fff;
|
||||
border: 2rpx solid #ff6b6b;
|
||||
border-radius: 30rpx;
|
||||
padding: 16rpx 40rpx;
|
||||
font-size: 28rpx;
|
||||
color: #fff;
|
||||
background: #4cd964;
|
||||
border: none;
|
||||
border-radius: 12rpx;
|
||||
line-height: 1;
|
||||
|
||||
&::after {
|
||||
|
|
|
|||
|
|
@ -21,8 +21,9 @@
|
|||
<view class="avatar-wrapper" @click="handleChangeAvatar">
|
||||
<image
|
||||
class="avatar-img"
|
||||
:src="userInfo.avatar || defaultAvatar"
|
||||
:src="avatarLoadError ? defaultAvatar : (userInfo.avatar || defaultAvatar)"
|
||||
mode="aspectFill"
|
||||
@error="onAvatarError"
|
||||
/>
|
||||
<view class="avatar-edit-icon">
|
||||
<text>✎</text>
|
||||
|
|
@ -64,27 +65,38 @@
|
|||
<script>
|
||||
import { ref, computed, onMounted, watch } from 'vue'
|
||||
import { useUserStore } from '@/store/user.js'
|
||||
import { useConfigStore } from '@/store/config.js'
|
||||
import { updateAvatar, updateNickname } from '@/api/user.js'
|
||||
import { getMyProfile } from '@/api/profile.js'
|
||||
import { getFullImageUrl } from '@/utils/image.js'
|
||||
import config from '@/config/index.js'
|
||||
|
||||
export default {
|
||||
name: 'PersonalPage',
|
||||
setup() {
|
||||
const userStore = useUserStore()
|
||||
const defaultAvatar = '/static/logo.png'
|
||||
const configStore = useConfigStore()
|
||||
const statusBarHeight = ref(20)
|
||||
const nickname = ref('')
|
||||
|
||||
// 从统一配置获取 API 地址
|
||||
const API_BASE_URL = config.API_BASE_URL
|
||||
|
||||
// 从 configStore 获取默认头像
|
||||
const defaultAvatar = computed(() => configStore.defaultAvatar || '/static/logo.png')
|
||||
|
||||
const userInfo = computed(() => ({
|
||||
avatar: userStore.avatar,
|
||||
avatar: getFullImageUrl(userStore.avatar),
|
||||
xiangQinNo: userStore.xiangQinNo,
|
||||
nickname: userStore.nickname
|
||||
}))
|
||||
|
||||
// 头像加载失败处理
|
||||
const avatarLoadError = ref(false)
|
||||
const onAvatarError = () => {
|
||||
avatarLoadError.value = true
|
||||
}
|
||||
|
||||
// 监听 store 中的 nickname 变化,同步到本地
|
||||
watch(() => userStore.nickname, (newVal) => {
|
||||
if (newVal && !nickname.value) {
|
||||
|
|
@ -142,7 +154,9 @@ export default {
|
|||
// 调用后端API保存头像到数据库
|
||||
const saveRes = await updateAvatar(uploadRes.data.url)
|
||||
if (saveRes && saveRes.code === 0) {
|
||||
// 更新 store 并重置头像加载错误状态
|
||||
userStore.updateUserInfo({ avatar: uploadRes.data.url })
|
||||
avatarLoadError.value = false
|
||||
uni.showToast({ title: '头像更新成功', icon: 'success' })
|
||||
} else {
|
||||
uni.showToast({ title: saveRes?.message || '保存失败', icon: 'none' })
|
||||
|
|
@ -240,6 +254,8 @@ export default {
|
|||
defaultAvatar,
|
||||
statusBarHeight,
|
||||
nickname,
|
||||
avatarLoadError,
|
||||
onAvatarError,
|
||||
handleBack,
|
||||
handleChangeAvatar,
|
||||
handleCopyXiangQinNo,
|
||||
|
|
@ -249,6 +265,10 @@ export default {
|
|||
},
|
||||
// 页面显示时重新初始化
|
||||
onShow() {
|
||||
// 重置头像加载错误状态
|
||||
if (this.avatarLoadError !== undefined) {
|
||||
this.avatarLoadError = false
|
||||
}
|
||||
if (this.initNickname) {
|
||||
this.initNickname()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ import {
|
|||
getLastPopupDate, setLastPopupDate,
|
||||
getMemberAdClosedDate, setMemberAdClosedDate,
|
||||
getMemberAdClosedForever, setMemberAdClosedForever,
|
||||
getGenderPreference
|
||||
getGenderPreference,
|
||||
getDefaultAvatar, setDefaultAvatar
|
||||
} from '../utils/storage.js'
|
||||
|
||||
/**
|
||||
|
|
@ -46,6 +47,9 @@ export const useConfigStore = defineStore('config', {
|
|||
memberAdClosedDate: getMemberAdClosedDate() || '',
|
||||
memberAdClosedForever: getMemberAdClosedForever() || false,
|
||||
|
||||
// 默认头像(从后台配置获取)
|
||||
defaultAvatar: getDefaultAvatar() || '/static/logo.png',
|
||||
|
||||
// 弹窗显示状态
|
||||
showGenderPopup: false,
|
||||
showDailyPopup: false,
|
||||
|
|
@ -71,7 +75,12 @@ export const useConfigStore = defineStore('config', {
|
|||
/**
|
||||
* 是否有会员广告配置
|
||||
*/
|
||||
hasMemberAdConfig: (state) => state.memberAdConfig !== null && state.memberAdConfig.status === 1
|
||||
hasMemberAdConfig: (state) => state.memberAdConfig !== null && state.memberAdConfig.status === 1,
|
||||
|
||||
/**
|
||||
* 获取默认头像URL
|
||||
*/
|
||||
getDefaultAvatar: (state) => state.defaultAvatar || '/static/logo.png'
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
|
@ -80,6 +89,7 @@ export const useConfigStore = defineStore('config', {
|
|||
* @param {Object} config - 首页配置
|
||||
* @param {Array} config.banners - Banner列表
|
||||
* @param {Array} config.kingKongs - 金刚位列表
|
||||
* @param {string} config.defaultAvatar - 默认头像URL
|
||||
*/
|
||||
setHomeConfig(config) {
|
||||
if (config.banners) {
|
||||
|
|
@ -88,6 +98,10 @@ export const useConfigStore = defineStore('config', {
|
|||
if (config.kingKongs) {
|
||||
this.kingKongs = config.kingKongs
|
||||
}
|
||||
if (config.defaultAvatar) {
|
||||
this.defaultAvatar = config.defaultAvatar
|
||||
setDefaultAvatar(config.defaultAvatar)
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -247,6 +261,18 @@ export const useConfigStore = defineStore('config', {
|
|||
this.showGenderPopup = false
|
||||
this.showDailyPopup = false
|
||||
this.showMemberAd = false
|
||||
// 不重置 defaultAvatar,保留后台配置
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置默认头像
|
||||
* @param {string} avatar - 默认头像URL
|
||||
*/
|
||||
setDefaultAvatarConfig(avatar) {
|
||||
if (avatar) {
|
||||
this.defaultAvatar = avatar
|
||||
setDefaultAvatar(avatar)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ const STORAGE_KEYS = {
|
|||
LAST_POPUP_DATE: 'lastPopupDate',
|
||||
MEMBER_AD_CLOSED_DATE: 'memberAdClosedDate',
|
||||
MEMBER_AD_CLOSED_FOREVER: 'memberAdClosedForever',
|
||||
GENDER_PREFERENCE: 'genderPreference'
|
||||
GENDER_PREFERENCE: 'genderPreference',
|
||||
DEFAULT_AVATAR: 'defaultAvatar'
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -143,4 +144,13 @@ export function getGenderPreference() {
|
|||
return getStorage(STORAGE_KEYS.GENDER_PREFERENCE, 0)
|
||||
}
|
||||
|
||||
// 默认头像
|
||||
export function setDefaultAvatar(avatar) {
|
||||
return setStorage(STORAGE_KEYS.DEFAULT_AVATAR, avatar)
|
||||
}
|
||||
|
||||
export function getDefaultAvatar() {
|
||||
return getStorage(STORAGE_KEYS.DEFAULT_AVATAR, '/static/logo.png')
|
||||
}
|
||||
|
||||
export { STORAGE_KEYS }
|
||||
|
|
|
|||
|
|
@ -92,6 +92,21 @@ public class ConfigController : ControllerBase
|
|||
Content = content ?? "暂无隐私协议内容"
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取默认头像配置
|
||||
/// </summary>
|
||||
/// <returns>默认头像URL</returns>
|
||||
[HttpGet("defaultAvatar")]
|
||||
[AllowAnonymous]
|
||||
public async Task<ApiResponse<DefaultAvatarConfigResponse>> GetDefaultAvatar()
|
||||
{
|
||||
var avatarUrl = await _systemConfigService.GetDefaultAvatarAsync();
|
||||
return ApiResponse<DefaultAvatarConfigResponse>.Success(new DefaultAvatarConfigResponse
|
||||
{
|
||||
AvatarUrl = avatarUrl
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -104,3 +119,14 @@ public class AgreementContentResponse
|
|||
/// </summary>
|
||||
public string Content { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 默认头像配置响应
|
||||
/// </summary>
|
||||
public class DefaultAvatarConfigResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// 默认头像URL
|
||||
/// </summary>
|
||||
public string? AvatarUrl { get; set; }
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user