聊天搜索修改
This commit is contained in:
parent
185343822c
commit
82acdee645
|
|
@ -76,3 +76,14 @@ export function deleteUser(id: number): Promise<void> {
|
|||
export function updateContactCount(id: number, contactCount: number): Promise<void> {
|
||||
return request.put(`/admin/users/${id}/contact-count`, { contactCount })
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户会员等级
|
||||
* @param id 用户ID
|
||||
* @param memberLevel 会员等级:0非会员 1不限时会员 2诚意会员 3家庭版会员
|
||||
* @param memberExpireTime 会员到期时间(等级2、3需要)
|
||||
* @returns 操作结果
|
||||
*/
|
||||
export function updateMemberLevel(id: number, memberLevel: number, memberExpireTime?: string): Promise<void> {
|
||||
return request.put(`/admin/users/${id}/member-level`, { memberLevel, memberExpireTime })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { useRoute, useRouter } from 'vue-router'
|
|||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { ArrowLeft } from '@element-plus/icons-vue'
|
||||
import StatusTag from '@/components/StatusTag/index.vue'
|
||||
import { getUserDetail, updateUserStatus, updateContactCount } from '@/api/user'
|
||||
import { getUserDetail, updateUserStatus, updateContactCount, updateMemberLevel } from '@/api/user'
|
||||
import { getFullImageUrl } from '@/utils/image'
|
||||
import type { UserDetail } from '@/types/user.d'
|
||||
|
||||
|
|
@ -106,6 +106,54 @@ const handleEditContactCount = async () => {
|
|||
}
|
||||
}
|
||||
|
||||
// 会员等级选项
|
||||
const memberLevelOptions = [
|
||||
{ value: 0, label: '非会员' },
|
||||
{ value: 1, label: '不限时会员' },
|
||||
{ value: 2, label: '诚意会员' },
|
||||
{ value: 3, label: '家庭版会员' }
|
||||
]
|
||||
|
||||
// 修改会员等级对话框
|
||||
const memberLevelDialogVisible = ref(false)
|
||||
const memberLevelForm = ref({
|
||||
memberLevel: 0,
|
||||
memberExpireTime: ''
|
||||
})
|
||||
|
||||
// 打开修改会员等级对话框
|
||||
const handleEditMemberLevel = () => {
|
||||
if (!userDetail.value) return
|
||||
memberLevelForm.value = {
|
||||
memberLevel: userDetail.value.memberLevel,
|
||||
memberExpireTime: userDetail.value.memberExpireTime ? userDetail.value.memberExpireTime.substring(0, 10) : ''
|
||||
}
|
||||
memberLevelDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 确认修改会员等级
|
||||
const handleConfirmMemberLevel = async () => {
|
||||
try {
|
||||
const { memberLevel, memberExpireTime } = memberLevelForm.value
|
||||
// 等级2、3需要到期时间
|
||||
if (memberLevel > 1 && !memberExpireTime) {
|
||||
ElMessage.warning('请选择会员到期时间')
|
||||
return
|
||||
}
|
||||
await updateMemberLevel(
|
||||
userId.value,
|
||||
memberLevel,
|
||||
memberLevel > 1 ? memberExpireTime : undefined
|
||||
)
|
||||
ElMessage.success('修改成功')
|
||||
memberLevelDialogVisible.value = false
|
||||
fetchUserDetail()
|
||||
} catch (error) {
|
||||
console.error('修改会员等级失败:', error)
|
||||
ElMessage.error('修改失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化时间
|
||||
const formatTime = (time: string) => {
|
||||
if (!time) return '-'
|
||||
|
|
@ -303,6 +351,15 @@ onMounted(() => {
|
|||
{{ userDetail.memberLevelText }}
|
||||
</el-tag>
|
||||
<span v-else>非会员</span>
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
size="small"
|
||||
style="margin-left: 8px;"
|
||||
@click="handleEditMemberLevel"
|
||||
>
|
||||
修改
|
||||
</el-button>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="会员到期时间">
|
||||
{{ formatTime(userDetail.memberExpireTime) }}
|
||||
|
|
@ -591,6 +648,58 @@ onMounted(() => {
|
|||
v-else-if="!loading"
|
||||
description="用户不存在"
|
||||
/>
|
||||
|
||||
<!-- 修改会员等级对话框 -->
|
||||
<el-dialog
|
||||
v-model="memberLevelDialogVisible"
|
||||
title="修改会员等级"
|
||||
width="400px"
|
||||
>
|
||||
<el-form label-width="100px">
|
||||
<el-form-item label="会员等级">
|
||||
<el-select
|
||||
v-model="memberLevelForm.memberLevel"
|
||||
placeholder="请选择会员等级"
|
||||
style="width: 100%;"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in memberLevelOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="memberLevelForm.memberLevel > 1"
|
||||
label="到期时间"
|
||||
>
|
||||
<el-date-picker
|
||||
v-model="memberLevelForm.memberExpireTime"
|
||||
type="date"
|
||||
placeholder="选择到期日期"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 100%;"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="memberLevelForm.memberLevel === 1">
|
||||
<el-text type="info">
|
||||
不限时会员无需设置到期时间
|
||||
</el-text>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="memberLevelDialogVisible = false">
|
||||
取消
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="handleConfirmMemberLevel"
|
||||
>
|
||||
确定
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
|
|
@ -341,16 +341,19 @@ onMounted(() => {
|
|||
stripe
|
||||
border
|
||||
style="width: 100%"
|
||||
table-layout="auto"
|
||||
>
|
||||
<el-table-column
|
||||
prop="xiangQinNo"
|
||||
label="相亲编号"
|
||||
width="120"
|
||||
min-width="120"
|
||||
fixed="left"
|
||||
align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
label="用户信息"
|
||||
width="200"
|
||||
min-width="180"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<div class="user-info">
|
||||
|
|
@ -372,17 +375,18 @@ onMounted(() => {
|
|||
<el-table-column
|
||||
prop="genderText"
|
||||
label="性别"
|
||||
width="80"
|
||||
min-width="70"
|
||||
align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="city"
|
||||
label="城市"
|
||||
width="100"
|
||||
min-width="90"
|
||||
align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
label="状态"
|
||||
width="80"
|
||||
min-width="70"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
|
|
@ -397,7 +401,7 @@ onMounted(() => {
|
|||
</el-table-column>
|
||||
<el-table-column
|
||||
label="会员等级"
|
||||
width="100"
|
||||
min-width="90"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
|
|
@ -412,7 +416,7 @@ onMounted(() => {
|
|||
</el-table-column>
|
||||
<el-table-column
|
||||
label="实名认证"
|
||||
width="100"
|
||||
min-width="90"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
|
|
@ -425,7 +429,7 @@ onMounted(() => {
|
|||
</el-table-column>
|
||||
<el-table-column
|
||||
label="资料状态"
|
||||
width="100"
|
||||
min-width="90"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
|
|
@ -440,12 +444,13 @@ onMounted(() => {
|
|||
<el-table-column
|
||||
prop="contactCount"
|
||||
label="联系次数"
|
||||
width="100"
|
||||
min-width="90"
|
||||
align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
label="注册时间"
|
||||
width="170"
|
||||
min-width="160"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
{{ formatTime(row.createTime) }}
|
||||
|
|
@ -453,7 +458,8 @@ onMounted(() => {
|
|||
</el-table-column>
|
||||
<el-table-column
|
||||
label="最后登录"
|
||||
width="170"
|
||||
min-width="160"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
{{ formatTime(row.lastLoginTime) }}
|
||||
|
|
@ -461,35 +467,37 @@ onMounted(() => {
|
|||
</el-table-column>
|
||||
<el-table-column
|
||||
label="操作"
|
||||
width="240"
|
||||
width="200"
|
||||
fixed="right"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
:icon="View"
|
||||
@click="handleViewDetail(row)"
|
||||
>
|
||||
详情
|
||||
</el-button>
|
||||
<el-button
|
||||
:type="row.status === 1 ? 'danger' : 'success'"
|
||||
link
|
||||
:icon="Edit"
|
||||
@click="handleToggleStatus(row)"
|
||||
>
|
||||
{{ row.status === 1 ? '禁用' : '启用' }}
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
link
|
||||
:icon="Delete"
|
||||
@click="handleDeleteUser(row)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
<div style="display: flex; justify-content: center; gap: 8px; flex-wrap: nowrap;">
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
:icon="View"
|
||||
@click="handleViewDetail(row)"
|
||||
>
|
||||
详情
|
||||
</el-button>
|
||||
<el-button
|
||||
:type="row.status === 1 ? 'danger' : 'success'"
|
||||
link
|
||||
:icon="Edit"
|
||||
@click="handleToggleStatus(row)"
|
||||
>
|
||||
{{ row.status === 1 ? '禁用' : '启用' }}
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
link
|
||||
:icon="Delete"
|
||||
@click="handleDeleteUser(row)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ export async function exchangePhoto(sessionId, receiverId) {
|
|||
* @returns {Promise<Object>} 响应结果
|
||||
*/
|
||||
export async function respondExchange(requestMessageId, accept) {
|
||||
const response = await post('/chat/respondExchange', { requestMessageId, accept })
|
||||
const response = await post('/chat/respondExchange', { requestMessageId, isAgreed: accept })
|
||||
return response
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,15 +8,16 @@
|
|||
|
||||
<!-- 性别选择弹窗 -->
|
||||
<template v-if="type === 'gender'">
|
||||
<view class="popup-title">请选择您想看的性别</view>
|
||||
<view class="popup-title">您想找儿媳?还是找女婿?</view>
|
||||
<view class="popup-subtitle">登录资料后,推荐更精准</view>
|
||||
<view class="gender-options">
|
||||
<view class="gender-option male" @click="handleGenderSelect(1)">
|
||||
<view class="gender-icon">♂</view>
|
||||
<text>看男生</text>
|
||||
</view>
|
||||
<view class="gender-option female" @click="handleGenderSelect(2)">
|
||||
<view class="gender-option male" @click="handleGenderSelect(2)">
|
||||
<view class="gender-icon">♀</view>
|
||||
<text>看女生</text>
|
||||
<text>找儿媳</text>
|
||||
</view>
|
||||
<view class="gender-option female" @click="handleGenderSelect(1)">
|
||||
<view class="gender-icon">♂</view>
|
||||
<text>找女婿</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
|
@ -244,6 +245,13 @@ export default {
|
|||
font-weight: 600;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.popup-subtitle {
|
||||
font-size: 26rpx;
|
||||
color: #FF5F5F;
|
||||
text-align: center;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
|
|
@ -327,20 +335,20 @@ export default {
|
|||
}
|
||||
|
||||
&.male {
|
||||
background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);
|
||||
|
||||
.gender-icon {
|
||||
color: #2196f3;
|
||||
}
|
||||
}
|
||||
|
||||
&.female {
|
||||
background: linear-gradient(135deg, #fce4ec 0%, #f8bbd9 100%);
|
||||
|
||||
.gender-icon {
|
||||
color: #e91e63;
|
||||
}
|
||||
}
|
||||
|
||||
&.female {
|
||||
background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);
|
||||
|
||||
.gender-icon {
|
||||
color: #2196f3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@
|
|||
<view class="title-row">
|
||||
<text class="gender-year">{{ genderText }} · {{ birthYear }}年</text>
|
||||
<view class="title-tags">
|
||||
<text v-if="isRealName" class="tag tag-realname">已实名</text>
|
||||
<text v-if="isMember" class="tag tag-member">会员</text>
|
||||
<text v-if="isRealName" class="tag tag-realname">已实名</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
|
@ -70,8 +70,8 @@
|
|||
<view class="title-row">
|
||||
<text class="gender-year">{{ genderText }} · {{ birthYear }}年</text>
|
||||
<view class="title-tags">
|
||||
<text v-if="isRealName" class="tag tag-realname">已实名</text>
|
||||
<text v-if="isMember" class="tag tag-member">会员</text>
|
||||
<text v-if="isRealName" class="tag tag-realname">已实名</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@
|
|||
{
|
||||
"path": "pages/interact/viewedMe",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "看过我"
|
||||
}
|
||||
},
|
||||
|
|
@ -107,6 +108,7 @@
|
|||
{
|
||||
"path": "pages/interact/favoritedMe",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "收藏我"
|
||||
}
|
||||
},
|
||||
|
|
@ -119,6 +121,7 @@
|
|||
{
|
||||
"path": "pages/interact/unlockedMe",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "解锁我"
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -121,15 +121,80 @@
|
|||
:key="message.id"
|
||||
:id="'msg-' + message.id"
|
||||
class="message-item"
|
||||
:class="{ 'mine': message.isMine }"
|
||||
:class="{ 'mine': message.isMine, 'exchange-item': message.messageType === MessageType.EXCHANGE_WECHAT || message.messageType === MessageType.EXCHANGE_PHOTO }"
|
||||
>
|
||||
<!-- 时间分隔 -->
|
||||
<view v-if="shouldShowTime(message, index)" class="time-divider">
|
||||
<text>{{ formatMessageTime(message.createTime) }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 消息内容 -->
|
||||
<view class="message-content">
|
||||
<!-- 交换微信请求卡片 - 独立全宽显示 -->
|
||||
<view
|
||||
v-if="message.messageType === MessageType.EXCHANGE_WECHAT"
|
||||
class="exchange-card"
|
||||
>
|
||||
<view class="exchange-card-header">
|
||||
<text>{{ getExchangeTitle(message) }}</text>
|
||||
</view>
|
||||
<!-- 待处理状态 - 显示操作按钮 -->
|
||||
<view v-if="showExchangeActions(message)" class="exchange-card-actions">
|
||||
<button class="action-btn reject-btn" @click="handleRespondExchange(message.id, false)">拒绝</button>
|
||||
<button class="action-btn accept-btn" @click="handleRespondExchange(message.id, true)">同意</button>
|
||||
</view>
|
||||
<!-- 已接受 - 显示微信号 -->
|
||||
<view v-else-if="message.status === ExchangeStatus.ACCEPTED" class="exchange-card-result accepted">
|
||||
<text class="result-label">{{ targetNickname }}({{ targetRelationship || '本人' }})的微信号</text>
|
||||
<text class="wechat-no">{{ message.exchangedContent || 'abcv123123' }}</text>
|
||||
<button class="copy-btn" @click="handleCopyWeChat(message.exchangedContent)">点击复制微信号</button>
|
||||
</view>
|
||||
<!-- 已拒绝 -->
|
||||
<view v-else-if="message.status === ExchangeStatus.REJECTED" class="exchange-card-result rejected">
|
||||
<text>{{ getExchangeRejectText(message, 'wechat') }}</text>
|
||||
</view>
|
||||
<!-- 等待中 -->
|
||||
<view v-else class="exchange-card-result pending">
|
||||
<text>等待对方回应...</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 交换照片请求卡片 - 独立全宽显示 -->
|
||||
<view
|
||||
v-else-if="message.messageType === MessageType.EXCHANGE_PHOTO"
|
||||
class="exchange-card"
|
||||
>
|
||||
<view class="exchange-card-header">
|
||||
<text>{{ getExchangePhotoTitle(message) }}</text>
|
||||
</view>
|
||||
<!-- 待处理状态 - 显示操作按钮 -->
|
||||
<view v-if="showExchangeActions(message)" class="exchange-card-actions">
|
||||
<button class="action-btn reject-btn" @click="handleRespondExchange(message.id, false)">拒绝</button>
|
||||
<button class="action-btn accept-btn" @click="handleRespondExchange(message.id, true)">同意</button>
|
||||
</view>
|
||||
<!-- 已接受 - 显示照片 -->
|
||||
<view v-else-if="message.status === ExchangeStatus.ACCEPTED" class="exchange-card-result accepted">
|
||||
<text class="result-label">{{ targetNickname }}({{ targetRelationship || '本人' }})希望与您发送孩子的照片共{{ message.photoCount || 5 }}张</text>
|
||||
<view class="photo-preview" v-if="message.photos && message.photos.length > 0">
|
||||
<image
|
||||
v-for="(photo, idx) in message.photos.slice(0, 3)"
|
||||
:key="idx"
|
||||
:src="photo"
|
||||
mode="aspectFill"
|
||||
@click="previewPhotos(message.photos, idx)"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 已拒绝 -->
|
||||
<view v-else-if="message.status === ExchangeStatus.REJECTED" class="exchange-card-result rejected">
|
||||
<text>{{ getExchangeRejectText(message, 'photo') }}</text>
|
||||
</view>
|
||||
<!-- 等待中 -->
|
||||
<view v-else class="exchange-card-result pending">
|
||||
<text>等待对方回应...</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 普通消息内容 -->
|
||||
<view v-else class="message-content">
|
||||
<!-- 对方头像 -->
|
||||
<image
|
||||
v-if="!message.isMine"
|
||||
|
|
@ -178,71 +243,6 @@
|
|||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 交换微信请求卡片 -->
|
||||
<view
|
||||
v-else-if="message.messageType === MessageType.EXCHANGE_WECHAT"
|
||||
class="exchange-card"
|
||||
>
|
||||
<view class="exchange-card-header">
|
||||
<text>{{ getExchangeTitle(message) }}</text>
|
||||
</view>
|
||||
<!-- 待处理状态 - 显示操作按钮 -->
|
||||
<view v-if="showExchangeActions(message)" class="exchange-card-actions">
|
||||
<button class="action-btn reject-btn" @click="handleRespondExchange(message.id, false)">拒绝</button>
|
||||
<button class="action-btn accept-btn" @click="handleRespondExchange(message.id, true)">同意</button>
|
||||
</view>
|
||||
<!-- 已接受 - 显示微信号 -->
|
||||
<view v-else-if="message.status === ExchangeStatus.ACCEPTED" class="exchange-card-result accepted">
|
||||
<text class="result-label">{{ targetNickname }}({{ targetRelationship || '本人' }})的微信号</text>
|
||||
<text class="wechat-no">{{ message.exchangedContent || 'abcv123123' }}</text>
|
||||
<button class="copy-btn" @click="handleCopyWeChat(message.exchangedContent)">点击复制微信号</button>
|
||||
</view>
|
||||
<!-- 已拒绝 -->
|
||||
<view v-else-if="message.status === ExchangeStatus.REJECTED" class="exchange-card-result rejected">
|
||||
<text>{{ message.isMine ? targetNickname : '您' }}拒绝了交换微信号</text>
|
||||
</view>
|
||||
<!-- 等待中 -->
|
||||
<view v-else class="exchange-card-result pending">
|
||||
<text>等待对方回应...</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 交换照片请求卡片 -->
|
||||
<view
|
||||
v-else-if="message.messageType === MessageType.EXCHANGE_PHOTO"
|
||||
class="exchange-card"
|
||||
>
|
||||
<view class="exchange-card-header">
|
||||
<text>{{ getExchangePhotoTitle(message) }}</text>
|
||||
</view>
|
||||
<!-- 待处理状态 - 显示操作按钮 -->
|
||||
<view v-if="showExchangeActions(message)" class="exchange-card-actions">
|
||||
<button class="action-btn reject-btn" @click="handleRespondExchange(message.id, false)">拒绝</button>
|
||||
<button class="action-btn accept-btn" @click="handleRespondExchange(message.id, true)">同意</button>
|
||||
</view>
|
||||
<!-- 已接受 - 显示照片 -->
|
||||
<view v-else-if="message.status === ExchangeStatus.ACCEPTED" class="exchange-card-result accepted">
|
||||
<text class="result-label">{{ targetNickname }}({{ targetRelationship || '本人' }})的照片共{{ message.photoCount || 5 }}张</text>
|
||||
<view class="photo-preview" v-if="message.photos && message.photos.length > 0">
|
||||
<image
|
||||
v-for="(photo, idx) in message.photos.slice(0, 3)"
|
||||
:key="idx"
|
||||
:src="photo"
|
||||
mode="aspectFill"
|
||||
@click="previewPhotos(message.photos, idx)"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 已拒绝 -->
|
||||
<view v-else-if="message.status === ExchangeStatus.REJECTED" class="exchange-card-result rejected">
|
||||
<text>{{ message.isMine ? targetNickname : '您' }}拒绝了交换孩子照片</text>
|
||||
</view>
|
||||
<!-- 等待中 -->
|
||||
<view v-else class="exchange-card-result pending">
|
||||
<text>等待对方回应...</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 消息状态 -->
|
||||
<view v-if="message.isMine && message.messageType === MessageType.TEXT" class="message-status">
|
||||
<text v-if="message.status === MessageStatus.SENDING" class="status sending">发送中</text>
|
||||
|
|
@ -470,7 +470,10 @@ const loadMessages = async (isLoadMore = false) => {
|
|||
if (res && res.code === 0 && res.data) {
|
||||
const serverMessages = res.data.items || []
|
||||
|
||||
const newMessages = serverMessages.map((msg) => {
|
||||
const newMessages = serverMessages
|
||||
// 过滤掉结果类型的消息(5=交换微信结果,7=交换照片结果),这些结果已在原请求消息中更新
|
||||
.filter((msg) => msg.messageType !== MessageType.EXCHANGE_WECHAT_RESULT && msg.messageType !== MessageType.EXCHANGE_PHOTO_RESULT)
|
||||
.map((msg) => {
|
||||
// 后端字段 MessageId / IsSelf 转为前端使用的 id / isMine
|
||||
const mapped = {
|
||||
...msg,
|
||||
|
|
@ -854,11 +857,37 @@ const showExchangeActions = (message) => {
|
|||
}
|
||||
|
||||
const getExchangeTitle = (message) => {
|
||||
return message.isMine ? '您发起了交换微信请求' : '对方想和您交换微信'
|
||||
if (message.isMine) {
|
||||
return '您发起了交换微信请求'
|
||||
}
|
||||
// 如果昵称已包含关系信息(括号),则不再添加
|
||||
const name = targetNickname.value
|
||||
if (name && name.includes('(')) {
|
||||
return `${name}希望与您交换微信号`
|
||||
}
|
||||
return `${name}(${targetRelationship.value || '本人'})希望与您交换微信号`
|
||||
}
|
||||
|
||||
const getExchangePhotoTitle = (message) => {
|
||||
return message.isMine ? '您发起了交换照片请求' : '对方想和您交换照片'
|
||||
if (message.isMine) {
|
||||
return '您发起了交换照片请求'
|
||||
}
|
||||
const name = targetNickname.value
|
||||
if (name && name.includes('(')) {
|
||||
return `${name}希望与您交换孩子照片`
|
||||
}
|
||||
return `${name}(${targetRelationship.value || '本人'})希望与您交换孩子照片`
|
||||
}
|
||||
|
||||
const getExchangeRejectText = (message, type) => {
|
||||
let name = targetNickname.value
|
||||
if (name && !name.includes('(')) {
|
||||
name = `${name}(${targetRelationship.value || '本人'})`
|
||||
}
|
||||
if (type === 'wechat') {
|
||||
return message.isMine ? `${name}拒绝了交换微信号` : '您拒绝了交换微信号'
|
||||
}
|
||||
return message.isMine ? `${name}拒绝了交换孩子照片` : '您拒绝了交换孩子照片'
|
||||
}
|
||||
|
||||
const getExchangeStatusClass = (message) => {
|
||||
|
|
@ -1073,6 +1102,11 @@ onMounted(async () => {
|
|||
const handleReceiveMessage = (message) => {
|
||||
console.log('[Chat] 收到新消息:', message)
|
||||
|
||||
// 过滤掉结果类型的消息(5=交换微信结果,7=交换照片结果)
|
||||
if (message.messageType === MessageType.EXCHANGE_WECHAT_RESULT || message.messageType === MessageType.EXCHANGE_PHOTO_RESULT) {
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否是当前会话的消息
|
||||
if (message.sessionId !== sessionId.value) {
|
||||
return
|
||||
|
|
@ -1491,80 +1525,100 @@ onUnmounted(() => {
|
|||
}
|
||||
}
|
||||
|
||||
// 交换消息项样式 - 居中显示
|
||||
&.exchange-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 0 40rpx;
|
||||
|
||||
.time-divider {
|
||||
width: 100%;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 交换卡片样式
|
||||
.exchange-card {
|
||||
background: #fff;
|
||||
background: #FFFFFF;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
min-width: 400rpx;
|
||||
width: 100%;
|
||||
max-width: 600rpx;
|
||||
|
||||
.exchange-card-header {
|
||||
padding: 24rpx;
|
||||
background: #f8f8f8;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
padding: 30rpx 24rpx;
|
||||
text-align: center;
|
||||
|
||||
text {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.exchange-card-actions {
|
||||
display: flex;
|
||||
padding: 24rpx;
|
||||
gap: 24rpx;
|
||||
padding: 20rpx 60rpx 30rpx;
|
||||
gap: 40rpx;
|
||||
justify-content: center;
|
||||
|
||||
.action-btn {
|
||||
flex: 1;
|
||||
height: 72rpx;
|
||||
line-height: 72rpx;
|
||||
width: 180rpx;
|
||||
height: 64rpx;
|
||||
line-height: 64rpx;
|
||||
font-size: 28rpx;
|
||||
border-radius: 36rpx;
|
||||
border: none;
|
||||
border-radius: 32rpx;
|
||||
padding: 0;
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
&.reject-btn {
|
||||
background: #f5f5f5;
|
||||
background: #fff;
|
||||
color: #666;
|
||||
border: 2rpx solid #CCCCCC;
|
||||
}
|
||||
|
||||
&.accept-btn {
|
||||
background: linear-gradient(135deg, #ff9a9a 0%, #ff6b6b 100%);
|
||||
background: linear-gradient(135deg, #FFB5B5 0%, #FF9A9A 100%);
|
||||
color: #fff;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.exchange-card-result {
|
||||
padding: 24rpx;
|
||||
padding: 20rpx 24rpx 30rpx;
|
||||
text-align: center;
|
||||
|
||||
.result-label {
|
||||
display: block;
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
margin-bottom: 16rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
margin-bottom: 20rpx;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.wechat-no {
|
||||
display: block;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 20rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
width: 100%;
|
||||
height: 72rpx;
|
||||
line-height: 72rpx;
|
||||
background: linear-gradient(135deg, #4cd964 0%, #34c759 100%);
|
||||
color: #fff;
|
||||
font-size: 28rpx;
|
||||
border-radius: 36rpx;
|
||||
border: none;
|
||||
width: 280rpx;
|
||||
height: 64rpx;
|
||||
line-height: 64rpx;
|
||||
background: #fff;
|
||||
color: #666;
|
||||
font-size: 26rpx;
|
||||
border-radius: 32rpx;
|
||||
border: 2rpx solid #CCCCCC;
|
||||
margin: 0 auto;
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
|
|
@ -1573,20 +1627,21 @@ onUnmounted(() => {
|
|||
|
||||
.photo-preview {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
margin-top: 16rpx;
|
||||
gap: 16rpx;
|
||||
margin-top: 20rpx;
|
||||
justify-content: center;
|
||||
|
||||
image {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 8rpx;
|
||||
width: 180rpx;
|
||||
height: 180rpx;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
}
|
||||
|
||||
&.rejected {
|
||||
text {
|
||||
color: #999;
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,8 +3,24 @@
|
|||
<!-- 页面加载状态 -->
|
||||
<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">{{ activeTab === 'favoritedMe' ? '收藏我' : '我收藏的' }}</text>
|
||||
<view class="navbar-placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Tab切换 -->
|
||||
<view class="tab-header">
|
||||
<view class="tab-header" :style="{ top: (statusBarHeight + 44) + 'px' }">
|
||||
<view
|
||||
class="tab-item"
|
||||
:class="{ active: activeTab === 'favoritedMe' }"
|
||||
|
|
@ -21,72 +37,107 @@
|
|||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 用户列表 -->
|
||||
<view class="user-list" v-if="list.length > 0">
|
||||
<view
|
||||
class="user-item"
|
||||
v-for="item in list"
|
||||
:key="item.id"
|
||||
@click="handleUserClick(item.userId)"
|
||||
>
|
||||
<!-- 头像 -->
|
||||
<view class="user-avatar">
|
||||
<image
|
||||
class="avatar-image"
|
||||
:src="item.avatar || defaultAvatar"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 用户信息 -->
|
||||
<view class="user-info">
|
||||
<view class="user-header">
|
||||
<text class="user-nickname">{{ item.nickname }}</text>
|
||||
<view class="user-badges">
|
||||
<view v-if="item.isMember" class="badge member-badge">会员</view>
|
||||
<view v-if="item.isRealName" class="badge realname-badge">已实名</view>
|
||||
<!-- 可滚动内容区域 -->
|
||||
<scroll-view
|
||||
class="content-scroll"
|
||||
scroll-y
|
||||
:style="{
|
||||
top: (statusBarHeight + 44 + 80) + 'px',
|
||||
height: 'calc(100vh - ' + (statusBarHeight + 44 + 80) + 'px)'
|
||||
}"
|
||||
@scrolltolower="handleScrollToLower"
|
||||
>
|
||||
<!-- 用户列表 -->
|
||||
<view class="user-list" v-if="list.length > 0">
|
||||
<view
|
||||
class="user-card"
|
||||
v-for="item in list"
|
||||
:key="item.id || item.userId"
|
||||
@click="handleUserClick(item.userId)"
|
||||
>
|
||||
<!-- 时间信息 -->
|
||||
<view class="time-row">
|
||||
<text class="time-text">{{ formatViewTime(item.createTime) }}{{ activeTab === 'favoritedMe' ? '收藏了我' : '收藏' }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 性别年份 -->
|
||||
<view class="title-row">
|
||||
<text class="gender-year">{{ item.gender === 1 ? '男' : '女' }} · {{ item.birthYear || getYearFromAge(item.age) }}年</text>
|
||||
</view>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<view class="content-row">
|
||||
<!-- 左侧信息 -->
|
||||
<view class="info-section" :class="{ 'full-width': !item.isPhotoPublic || !item.firstPhoto }">
|
||||
<view class="info-grid">
|
||||
<view class="info-item">
|
||||
<text class="label">现居</text>
|
||||
<text class="value">{{ item.workCity || '未填写' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">身高</text>
|
||||
<text class="value">{{ item.height ? item.height + 'cm' : '未填写' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">学历</text>
|
||||
<text class="value">{{ item.educationName || '未填写' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">体重</text>
|
||||
<text class="value">{{ item.weight ? item.weight + 'kg' : '未填写' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">职业</text>
|
||||
<text class="value">{{ item.occupation || '未填写' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">家乡</text>
|
||||
<text class="value">{{ item.hometown || '未填写' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 右侧照片 -->
|
||||
<view class="photo-section" v-if="item.isPhotoPublic && item.firstPhoto">
|
||||
<image class="user-photo" :src="getFullUrl(item.firstPhoto)" mode="aspectFill" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="user-detail">
|
||||
<text>{{ item.age }}岁</text>
|
||||
<text class="divider">|</text>
|
||||
<text>{{ item.workCity }}</text>
|
||||
<text class="divider">|</text>
|
||||
<text>{{ item.height }}cm</text>
|
||||
|
||||
<!-- 简介 -->
|
||||
<view class="intro-section" v-if="item.intro">
|
||||
<text class="intro-text">{{ item.intro }}</text>
|
||||
</view>
|
||||
<view class="user-time">
|
||||
<text>{{ formatTime(item.createTime) }}</text>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="action-buttons" @click.stop>
|
||||
<button class="btn-detail" @click="handleUserClick(item.userId)">查看详细资料</button>
|
||||
<button class="btn-contact" @click="handleContact(item.userId)">联系对方</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="user-action" @click.stop="handleContact(item.userId)">
|
||||
<text>联系TA</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<Empty
|
||||
v-else-if="!listLoading"
|
||||
:text="activeTab === 'favoritedMe' ? '暂无人收藏你' : '你还没有收藏别人'"
|
||||
buttonText="去相亲"
|
||||
buttonUrl="/pages/index/index"
|
||||
/>
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<Loading
|
||||
type="more"
|
||||
:loading="listLoading"
|
||||
:noMore="noMoreData && list.length > 0"
|
||||
/>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<Empty
|
||||
v-else-if="!listLoading"
|
||||
:text="activeTab === 'favoritedMe' ? '暂无人收藏你' : '你还没有收藏别人'"
|
||||
buttonText="去相亲"
|
||||
buttonUrl="/pages/index/index"
|
||||
/>
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<Loading
|
||||
type="more"
|
||||
:loading="listLoading"
|
||||
:noMore="noMoreData && list.length > 0"
|
||||
/>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { getFavoritedMe, getMyFavorite } from '@/api/interact.js'
|
||||
import { formatTimestamp } from '@/utils/format.js'
|
||||
import { getFullImageUrl } from '@/utils/image.js'
|
||||
import { useUserStore } from '@/store/user.js'
|
||||
import { useConfigStore } from '@/store/config.js'
|
||||
import Loading from '@/components/Loading/index.vue'
|
||||
|
|
@ -102,6 +153,9 @@ export default {
|
|||
const userStore = useUserStore()
|
||||
const configStore = useConfigStore()
|
||||
|
||||
// 状态栏高度
|
||||
const statusBarHeight = ref(20)
|
||||
|
||||
// 页面状态
|
||||
const pageLoading = ref(true)
|
||||
const listLoading = ref(false)
|
||||
|
|
@ -116,6 +170,56 @@ export default {
|
|||
// 从 configStore 获取默认头像
|
||||
const defaultAvatar = computed(() => configStore.defaultAvatar || '/static/logo.png')
|
||||
|
||||
// 获取系统信息
|
||||
const getSystemInfo = () => {
|
||||
uni.getSystemInfo({
|
||||
success: (res) => {
|
||||
statusBarHeight.value = res.statusBarHeight || 20
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 返回上一页
|
||||
const handleBack = () => {
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
// 获取完整图片URL
|
||||
const getFullUrl = (url) => {
|
||||
return getFullImageUrl(url)
|
||||
}
|
||||
|
||||
// 从年龄计算出生年份
|
||||
const getYearFromAge = (age) => {
|
||||
if (!age) return ''
|
||||
return new Date().getFullYear() - age
|
||||
}
|
||||
|
||||
// 格式化时间 - 显示为"今天15:21"或"昨天15:21"或"01-20 15:21"
|
||||
const formatViewTime = (timeStr) => {
|
||||
if (!timeStr) return ''
|
||||
|
||||
const date = new Date(timeStr)
|
||||
const now = new Date()
|
||||
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
|
||||
const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000)
|
||||
const targetDate = new Date(date.getFullYear(), date.getMonth(), date.getDate())
|
||||
|
||||
const hours = String(date.getHours()).padStart(2, '0')
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||
const timeStr2 = `${hours}:${minutes}`
|
||||
|
||||
if (targetDate.getTime() === today.getTime()) {
|
||||
return `今天${timeStr2}`
|
||||
} else if (targetDate.getTime() === yesterday.getTime()) {
|
||||
return `昨天${timeStr2}`
|
||||
} else {
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
return `${month}-${day} ${timeStr2}`
|
||||
}
|
||||
}
|
||||
|
||||
// 加载列表
|
||||
const loadList = async (refresh = false) => {
|
||||
if (refresh) {
|
||||
|
|
@ -157,6 +261,7 @@ export default {
|
|||
const initPage = async () => {
|
||||
pageLoading.value = true
|
||||
try {
|
||||
getSystemInfo()
|
||||
await loadList(true)
|
||||
} finally {
|
||||
pageLoading.value = false
|
||||
|
|
@ -168,18 +273,15 @@ export default {
|
|||
if (activeTab.value === tab) return
|
||||
activeTab.value = tab
|
||||
|
||||
// 更新导航栏标题
|
||||
uni.setNavigationBarTitle({
|
||||
title: tab === 'favoritedMe' ? '收藏我' : '我收藏的'
|
||||
})
|
||||
|
||||
// 重新加载数据
|
||||
loadList(true)
|
||||
}
|
||||
|
||||
// 格式化时间
|
||||
const formatTime = (timestamp) => {
|
||||
return formatTimestamp(timestamp)
|
||||
// 滚动到底部加载更多
|
||||
const handleScrollToLower = () => {
|
||||
if (!noMoreData.value && !listLoading.value) {
|
||||
loadList()
|
||||
}
|
||||
}
|
||||
|
||||
// 点击用户
|
||||
|
|
@ -212,16 +314,21 @@ export default {
|
|||
})
|
||||
|
||||
return {
|
||||
statusBarHeight,
|
||||
pageLoading,
|
||||
listLoading,
|
||||
noMoreData,
|
||||
activeTab,
|
||||
list,
|
||||
defaultAvatar,
|
||||
handleBack,
|
||||
switchTab,
|
||||
formatTime,
|
||||
formatViewTime,
|
||||
getFullUrl,
|
||||
getYearFromAge,
|
||||
handleUserClick,
|
||||
handleContact,
|
||||
handleScrollToLower,
|
||||
loadList
|
||||
}
|
||||
},
|
||||
|
|
@ -230,156 +337,270 @@ export default {
|
|||
this.loadList && this.loadList(true).finally(() => {
|
||||
uni.stopPullDownRefresh()
|
||||
})
|
||||
},
|
||||
// 触底加载更多
|
||||
onReachBottom() {
|
||||
if (!this.noMoreData && !this.listLoading) {
|
||||
this.loadList && this.loadList()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.interact-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f8f8f8;
|
||||
height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// 顶部背景图
|
||||
.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: 200;
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tab切换
|
||||
.tab-header {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
background-color: #fff;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
padding: 20rpx 40rpx;
|
||||
gap: 24rpx;
|
||||
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 28rpx 0;
|
||||
position: relative;
|
||||
padding: 20rpx 40rpx;
|
||||
border-radius: 40rpx;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
|
||||
|
||||
text {
|
||||
font-size: 30rpx;
|
||||
color: #666;
|
||||
color: #2F2F2F;
|
||||
}
|
||||
|
||||
&.active {
|
||||
text {
|
||||
color: #ff6b6b;
|
||||
font-weight: 600;
|
||||
}
|
||||
background-color: #FFF0F0;
|
||||
position: relative;
|
||||
box-shadow: 0 4rpx 16rpx rgba(255, 107, 107, 0.2);
|
||||
|
||||
&::after {
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 60rpx;
|
||||
height: 4rpx;
|
||||
background-color: #ff6b6b;
|
||||
border-radius: 2rpx;
|
||||
border-radius: 40rpx;
|
||||
padding: 2rpx;
|
||||
background: linear-gradient(to right, #FFCBCB, #FF7B7B);
|
||||
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||
-webkit-mask-composite: xor;
|
||||
mask-composite: exclude;
|
||||
}
|
||||
|
||||
text {
|
||||
color: #FF5F5F;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 可滚动内容区域
|
||||
.content-scroll {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
// 用户列表
|
||||
.user-list {
|
||||
padding: 0 20rpx;
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
// 用户卡片
|
||||
.user-card {
|
||||
background: linear-gradient(to bottom, #FFEBEA 0%, #FFFFFF 100%);
|
||||
border-radius: 24rpx;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
// 时间行
|
||||
.time-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.user-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 24rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 16rpx;
|
||||
margin-top: 20rpx;
|
||||
|
||||
&:active {
|
||||
background-color: #f8f8f8;
|
||||
.time-text {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
// 标题行
|
||||
.title-row {
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.gender-year {
|
||||
font-size: 40rpx;
|
||||
font-weight: 600;
|
||||
color: #ff6b6b;
|
||||
}
|
||||
}
|
||||
|
||||
// 内容区域
|
||||
.content-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
// 信息区域
|
||||
.info-section {
|
||||
flex: 1;
|
||||
|
||||
&.full-width {
|
||||
.info-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
margin-right: 24rpx;
|
||||
flex-shrink: 0;
|
||||
|
||||
.avatar-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.user-info {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
|
||||
.user-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8rpx;
|
||||
|
||||
.user-nickname {
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.user-badges {
|
||||
display: flex;
|
||||
gap: 8rpx;
|
||||
|
||||
.badge {
|
||||
padding: 2rpx 8rpx;
|
||||
border-radius: 6rpx;
|
||||
font-size: 20rpx;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.member-badge {
|
||||
background: linear-gradient(135deg, #ffd700 0%, #ffb800 100%);
|
||||
}
|
||||
|
||||
.realname-badge {
|
||||
background: linear-gradient(135deg, #4cd964 0%, #34c759 100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-detail {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
margin-bottom: 8rpx;
|
||||
|
||||
.divider {
|
||||
margin: 0 8rpx;
|
||||
color: #ddd;
|
||||
}
|
||||
}
|
||||
|
||||
.user-time {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.user-action {
|
||||
padding: 12rpx 24rpx;
|
||||
background: linear-gradient(135deg, #ff6b6b 0%, #ff5252 100%);
|
||||
border-radius: 30rpx;
|
||||
flex-shrink: 0;
|
||||
|
||||
text {
|
||||
font-size: 26rpx;
|
||||
color: #fff;
|
||||
}
|
||||
.info-item {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
.info-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
.label {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
width: 80rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 照片区域
|
||||
.photo-section {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
margin-left: 24rpx;
|
||||
flex-shrink: 0;
|
||||
|
||||
.user-photo {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 简介区域
|
||||
.intro-section {
|
||||
margin-top: 24rpx;
|
||||
padding-top: 24rpx;
|
||||
border-top: 1rpx solid #f5f5f5;
|
||||
|
||||
.intro-text {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
// 操作按钮
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 24rpx;
|
||||
margin-top: 32rpx;
|
||||
|
||||
button {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
line-height: 80rpx;
|
||||
font-size: 30rpx;
|
||||
border-radius: 40rpx;
|
||||
border: none;
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-detail {
|
||||
background: #fff;
|
||||
color: #333;
|
||||
border: 2rpx solid #FFCBCB;
|
||||
}
|
||||
|
||||
.btn-contact {
|
||||
background: linear-gradient(to right, #FFBDC2, #FF8A93);
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -3,8 +3,24 @@
|
|||
<!-- 页面加载状态 -->
|
||||
<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">{{ activeTab === 'unlockedMe' ? '解锁我' : '我解锁的' }}</text>
|
||||
<view class="navbar-placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Tab切换 -->
|
||||
<view class="tab-header">
|
||||
<view class="tab-header" :style="{ top: (statusBarHeight + 44) + 'px' }">
|
||||
<view
|
||||
class="tab-item"
|
||||
:class="{ active: activeTab === 'unlockedMe' }"
|
||||
|
|
@ -21,72 +37,107 @@
|
|||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 用户列表 -->
|
||||
<view class="user-list" v-if="list.length > 0">
|
||||
<view
|
||||
class="user-item"
|
||||
v-for="item in list"
|
||||
:key="item.id"
|
||||
@click="handleUserClick(item.userId)"
|
||||
>
|
||||
<!-- 头像 -->
|
||||
<view class="user-avatar">
|
||||
<image
|
||||
class="avatar-image"
|
||||
:src="item.avatar || defaultAvatar"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 用户信息 -->
|
||||
<view class="user-info">
|
||||
<view class="user-header">
|
||||
<text class="user-nickname">{{ item.nickname }}</text>
|
||||
<view class="user-badges">
|
||||
<view v-if="item.isMember" class="badge member-badge">会员</view>
|
||||
<view v-if="item.isRealName" class="badge realname-badge">已实名</view>
|
||||
<!-- 可滚动内容区域 -->
|
||||
<scroll-view
|
||||
class="content-scroll"
|
||||
scroll-y
|
||||
:style="{
|
||||
top: (statusBarHeight + 44 + 80) + 'px',
|
||||
height: 'calc(100vh - ' + (statusBarHeight + 44 + 80) + 'px)'
|
||||
}"
|
||||
@scrolltolower="handleScrollToLower"
|
||||
>
|
||||
<!-- 用户列表 -->
|
||||
<view class="user-list" v-if="list.length > 0">
|
||||
<view
|
||||
class="user-card"
|
||||
v-for="item in list"
|
||||
:key="item.id || item.userId"
|
||||
@click="handleUserClick(item.userId)"
|
||||
>
|
||||
<!-- 时间信息 -->
|
||||
<view class="time-row">
|
||||
<text class="time-text">{{ formatViewTime(item.createTime) }}{{ activeTab === 'unlockedMe' ? '解锁了我' : '解锁' }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 性别年份 -->
|
||||
<view class="title-row">
|
||||
<text class="gender-year">{{ item.gender === 1 ? '男' : '女' }} · {{ item.birthYear || getYearFromAge(item.age) }}年</text>
|
||||
</view>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<view class="content-row">
|
||||
<!-- 左侧信息 -->
|
||||
<view class="info-section" :class="{ 'full-width': !item.isPhotoPublic || !item.firstPhoto }">
|
||||
<view class="info-grid">
|
||||
<view class="info-item">
|
||||
<text class="label">现居</text>
|
||||
<text class="value">{{ item.workCity || '未填写' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">身高</text>
|
||||
<text class="value">{{ item.height ? item.height + 'cm' : '未填写' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">学历</text>
|
||||
<text class="value">{{ item.educationName || '未填写' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">体重</text>
|
||||
<text class="value">{{ item.weight ? item.weight + 'kg' : '未填写' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">职业</text>
|
||||
<text class="value">{{ item.occupation || '未填写' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">家乡</text>
|
||||
<text class="value">{{ item.hometown || '未填写' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 右侧照片 -->
|
||||
<view class="photo-section" v-if="item.isPhotoPublic && item.firstPhoto">
|
||||
<image class="user-photo" :src="getFullUrl(item.firstPhoto)" mode="aspectFill" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="user-detail">
|
||||
<text>{{ item.age }}岁</text>
|
||||
<text class="divider">|</text>
|
||||
<text>{{ item.workCity }}</text>
|
||||
<text class="divider">|</text>
|
||||
<text>{{ item.height }}cm</text>
|
||||
|
||||
<!-- 简介 -->
|
||||
<view class="intro-section" v-if="item.intro">
|
||||
<text class="intro-text">{{ item.intro }}</text>
|
||||
</view>
|
||||
<view class="user-time">
|
||||
<text>{{ formatTime(item.createTime) }}</text>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="action-buttons" @click.stop>
|
||||
<button class="btn-detail" @click="handleUserClick(item.userId)">查看详细资料</button>
|
||||
<button class="btn-contact" @click="handleContact(item.userId)">联系对方</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="user-action" @click.stop="handleContact(item.userId)">
|
||||
<text>联系TA</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<Empty
|
||||
v-else-if="!listLoading"
|
||||
:text="activeTab === 'unlockedMe' ? '暂无人解锁你' : '你还没有解锁别人'"
|
||||
buttonText="去相亲"
|
||||
buttonUrl="/pages/index/index"
|
||||
/>
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<Loading
|
||||
type="more"
|
||||
:loading="listLoading"
|
||||
:noMore="noMoreData && list.length > 0"
|
||||
/>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<Empty
|
||||
v-else-if="!listLoading"
|
||||
:text="activeTab === 'unlockedMe' ? '暂无人解锁你' : '你还没有解锁别人'"
|
||||
buttonText="去相亲"
|
||||
buttonUrl="/pages/index/index"
|
||||
/>
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<Loading
|
||||
type="more"
|
||||
:loading="listLoading"
|
||||
:noMore="noMoreData && list.length > 0"
|
||||
/>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { getUnlockedMe, getMyUnlocked } from '@/api/interact.js'
|
||||
import { formatTimestamp } from '@/utils/format.js'
|
||||
import { getFullImageUrl } from '@/utils/image.js'
|
||||
import { useUserStore } from '@/store/user.js'
|
||||
import { useConfigStore } from '@/store/config.js'
|
||||
import Loading from '@/components/Loading/index.vue'
|
||||
|
|
@ -102,6 +153,9 @@ export default {
|
|||
const userStore = useUserStore()
|
||||
const configStore = useConfigStore()
|
||||
|
||||
// 状态栏高度
|
||||
const statusBarHeight = ref(20)
|
||||
|
||||
// 页面状态
|
||||
const pageLoading = ref(true)
|
||||
const listLoading = ref(false)
|
||||
|
|
@ -116,6 +170,56 @@ export default {
|
|||
// 从 configStore 获取默认头像
|
||||
const defaultAvatar = computed(() => configStore.defaultAvatar || '/static/logo.png')
|
||||
|
||||
// 获取系统信息
|
||||
const getSystemInfo = () => {
|
||||
uni.getSystemInfo({
|
||||
success: (res) => {
|
||||
statusBarHeight.value = res.statusBarHeight || 20
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 返回上一页
|
||||
const handleBack = () => {
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
// 获取完整图片URL
|
||||
const getFullUrl = (url) => {
|
||||
return getFullImageUrl(url)
|
||||
}
|
||||
|
||||
// 从年龄计算出生年份
|
||||
const getYearFromAge = (age) => {
|
||||
if (!age) return ''
|
||||
return new Date().getFullYear() - age
|
||||
}
|
||||
|
||||
// 格式化时间 - 显示为"今天15:21"或"昨天15:21"或"01-20 15:21"
|
||||
const formatViewTime = (timeStr) => {
|
||||
if (!timeStr) return ''
|
||||
|
||||
const date = new Date(timeStr)
|
||||
const now = new Date()
|
||||
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
|
||||
const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000)
|
||||
const targetDate = new Date(date.getFullYear(), date.getMonth(), date.getDate())
|
||||
|
||||
const hours = String(date.getHours()).padStart(2, '0')
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||
const timeStr2 = `${hours}:${minutes}`
|
||||
|
||||
if (targetDate.getTime() === today.getTime()) {
|
||||
return `今天${timeStr2}`
|
||||
} else if (targetDate.getTime() === yesterday.getTime()) {
|
||||
return `昨天${timeStr2}`
|
||||
} else {
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
return `${month}-${day} ${timeStr2}`
|
||||
}
|
||||
}
|
||||
|
||||
// 加载列表
|
||||
const loadList = async (refresh = false) => {
|
||||
if (refresh) {
|
||||
|
|
@ -157,6 +261,7 @@ export default {
|
|||
const initPage = async () => {
|
||||
pageLoading.value = true
|
||||
try {
|
||||
getSystemInfo()
|
||||
await loadList(true)
|
||||
} finally {
|
||||
pageLoading.value = false
|
||||
|
|
@ -168,18 +273,15 @@ export default {
|
|||
if (activeTab.value === tab) return
|
||||
activeTab.value = tab
|
||||
|
||||
// 更新导航栏标题
|
||||
uni.setNavigationBarTitle({
|
||||
title: tab === 'unlockedMe' ? '解锁我' : '我解锁的'
|
||||
})
|
||||
|
||||
// 重新加载数据
|
||||
loadList(true)
|
||||
}
|
||||
|
||||
// 格式化时间
|
||||
const formatTime = (timestamp) => {
|
||||
return formatTimestamp(timestamp)
|
||||
// 滚动到底部加载更多
|
||||
const handleScrollToLower = () => {
|
||||
if (!noMoreData.value && !listLoading.value) {
|
||||
loadList()
|
||||
}
|
||||
}
|
||||
|
||||
// 点击用户
|
||||
|
|
@ -212,16 +314,21 @@ export default {
|
|||
})
|
||||
|
||||
return {
|
||||
statusBarHeight,
|
||||
pageLoading,
|
||||
listLoading,
|
||||
noMoreData,
|
||||
activeTab,
|
||||
list,
|
||||
defaultAvatar,
|
||||
handleBack,
|
||||
switchTab,
|
||||
formatTime,
|
||||
formatViewTime,
|
||||
getFullUrl,
|
||||
getYearFromAge,
|
||||
handleUserClick,
|
||||
handleContact,
|
||||
handleScrollToLower,
|
||||
loadList
|
||||
}
|
||||
},
|
||||
|
|
@ -230,156 +337,270 @@ export default {
|
|||
this.loadList && this.loadList(true).finally(() => {
|
||||
uni.stopPullDownRefresh()
|
||||
})
|
||||
},
|
||||
// 触底加载更多
|
||||
onReachBottom() {
|
||||
if (!this.noMoreData && !this.listLoading) {
|
||||
this.loadList && this.loadList()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.interact-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f8f8f8;
|
||||
height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// 顶部背景图
|
||||
.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: 200;
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tab切换
|
||||
.tab-header {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
background-color: #fff;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
padding: 20rpx 40rpx;
|
||||
gap: 24rpx;
|
||||
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 28rpx 0;
|
||||
position: relative;
|
||||
padding: 20rpx 40rpx;
|
||||
border-radius: 40rpx;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
|
||||
|
||||
text {
|
||||
font-size: 30rpx;
|
||||
color: #666;
|
||||
color: #2F2F2F;
|
||||
}
|
||||
|
||||
&.active {
|
||||
text {
|
||||
color: #ff6b6b;
|
||||
font-weight: 600;
|
||||
}
|
||||
background-color: #FFF0F0;
|
||||
position: relative;
|
||||
box-shadow: 0 4rpx 16rpx rgba(255, 107, 107, 0.2);
|
||||
|
||||
&::after {
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 60rpx;
|
||||
height: 4rpx;
|
||||
background-color: #ff6b6b;
|
||||
border-radius: 2rpx;
|
||||
border-radius: 40rpx;
|
||||
padding: 2rpx;
|
||||
background: linear-gradient(to right, #FFCBCB, #FF7B7B);
|
||||
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||
-webkit-mask-composite: xor;
|
||||
mask-composite: exclude;
|
||||
}
|
||||
|
||||
text {
|
||||
color: #FF5F5F;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 可滚动内容区域
|
||||
.content-scroll {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
// 用户列表
|
||||
.user-list {
|
||||
padding: 0 20rpx;
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
// 用户卡片
|
||||
.user-card {
|
||||
background: linear-gradient(to bottom, #FFEBEA 0%, #FFFFFF 100%);
|
||||
border-radius: 24rpx;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
// 时间行
|
||||
.time-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.user-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 24rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 16rpx;
|
||||
margin-top: 20rpx;
|
||||
|
||||
&:active {
|
||||
background-color: #f8f8f8;
|
||||
.time-text {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
// 标题行
|
||||
.title-row {
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.gender-year {
|
||||
font-size: 40rpx;
|
||||
font-weight: 600;
|
||||
color: #ff6b6b;
|
||||
}
|
||||
}
|
||||
|
||||
// 内容区域
|
||||
.content-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
// 信息区域
|
||||
.info-section {
|
||||
flex: 1;
|
||||
|
||||
&.full-width {
|
||||
.info-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
margin-right: 24rpx;
|
||||
flex-shrink: 0;
|
||||
|
||||
.avatar-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.user-info {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
|
||||
.user-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8rpx;
|
||||
|
||||
.user-nickname {
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.user-badges {
|
||||
display: flex;
|
||||
gap: 8rpx;
|
||||
|
||||
.badge {
|
||||
padding: 2rpx 8rpx;
|
||||
border-radius: 6rpx;
|
||||
font-size: 20rpx;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.member-badge {
|
||||
background: linear-gradient(135deg, #ffd700 0%, #ffb800 100%);
|
||||
}
|
||||
|
||||
.realname-badge {
|
||||
background: linear-gradient(135deg, #4cd964 0%, #34c759 100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-detail {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
margin-bottom: 8rpx;
|
||||
|
||||
.divider {
|
||||
margin: 0 8rpx;
|
||||
color: #ddd;
|
||||
}
|
||||
}
|
||||
|
||||
.user-time {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.user-action {
|
||||
padding: 12rpx 24rpx;
|
||||
background: linear-gradient(135deg, #ff6b6b 0%, #ff5252 100%);
|
||||
border-radius: 30rpx;
|
||||
flex-shrink: 0;
|
||||
|
||||
text {
|
||||
font-size: 26rpx;
|
||||
color: #fff;
|
||||
}
|
||||
.info-item {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
.info-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
.label {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
width: 80rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 照片区域
|
||||
.photo-section {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
margin-left: 24rpx;
|
||||
flex-shrink: 0;
|
||||
|
||||
.user-photo {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 简介区域
|
||||
.intro-section {
|
||||
margin-top: 24rpx;
|
||||
padding-top: 24rpx;
|
||||
border-top: 1rpx solid #f5f5f5;
|
||||
|
||||
.intro-text {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
// 操作按钮
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 24rpx;
|
||||
margin-top: 32rpx;
|
||||
|
||||
button {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
line-height: 80rpx;
|
||||
font-size: 30rpx;
|
||||
border-radius: 40rpx;
|
||||
border: none;
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-detail {
|
||||
background: #fff;
|
||||
color: #333;
|
||||
border: 2rpx solid #FFCBCB;
|
||||
}
|
||||
|
||||
.btn-contact {
|
||||
background: linear-gradient(to right, #FFBDC2, #FF8A93);
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -3,8 +3,24 @@
|
|||
<!-- 页面加载状态 -->
|
||||
<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">{{ activeTab === 'viewedMe' ? '看过我' : '我看过的' }}</text>
|
||||
<view class="navbar-placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Tab切换 -->
|
||||
<view class="tab-header">
|
||||
<view class="tab-header" :style="{ top: (statusBarHeight + 44) + 'px' }">
|
||||
<view
|
||||
class="tab-item"
|
||||
:class="{ active: activeTab === 'viewedMe' }"
|
||||
|
|
@ -21,72 +37,108 @@
|
|||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 用户列表 -->
|
||||
<view class="user-list" v-if="list.length > 0">
|
||||
<view
|
||||
class="user-item"
|
||||
v-for="item in list"
|
||||
:key="item.id"
|
||||
@click="handleUserClick(item.userId)"
|
||||
>
|
||||
<!-- 头像 -->
|
||||
<view class="user-avatar">
|
||||
<image
|
||||
class="avatar-image"
|
||||
:src="item.avatar || defaultAvatar"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 用户信息 -->
|
||||
<view class="user-info">
|
||||
<view class="user-header">
|
||||
<text class="user-nickname">{{ item.nickname }}</text>
|
||||
<view class="user-badges">
|
||||
<view v-if="item.isMember" class="badge member-badge">会员</view>
|
||||
<view v-if="item.isRealName" class="badge realname-badge">已实名</view>
|
||||
<!-- 可滚动内容区域 -->
|
||||
<scroll-view
|
||||
class="content-scroll"
|
||||
scroll-y
|
||||
:style="{
|
||||
top: (statusBarHeight + 44 + 80) + 'px',
|
||||
height: 'calc(100vh - ' + (statusBarHeight + 44 + 80) + 'px)'
|
||||
}"
|
||||
@scrolltolower="handleScrollToLower"
|
||||
>
|
||||
<!-- 用户列表 -->
|
||||
<view class="user-list" v-if="list.length > 0">
|
||||
<view
|
||||
class="user-card"
|
||||
v-for="item in list"
|
||||
:key="item.id || item.userId"
|
||||
@click="handleUserClick(item.userId)"
|
||||
>
|
||||
<!-- 时间信息 -->
|
||||
<view class="time-row">
|
||||
<text class="time-text">{{ formatViewTime(item.lastViewTime) }}{{ activeTab === 'viewedMe' ? '看过我' : '我看过' }}</text>
|
||||
<text class="view-count" v-if="activeTab === 'viewedMe' && item.viewCount > 1"> 共看过我{{ item.viewCount }}次</text>
|
||||
</view>
|
||||
|
||||
<!-- 性别年份 -->
|
||||
<view class="title-row">
|
||||
<text class="gender-year">{{ item.gender === 1 ? '男' : '女' }} · {{ item.birthYear || getYearFromAge(item.age) }}年</text>
|
||||
</view>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<view class="content-row">
|
||||
<!-- 左侧信息 -->
|
||||
<view class="info-section" :class="{ 'full-width': !item.isPhotoPublic || !item.firstPhoto }">
|
||||
<view class="info-grid">
|
||||
<view class="info-item">
|
||||
<text class="label">现居</text>
|
||||
<text class="value">{{ item.workCity || '未填写' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">身高</text>
|
||||
<text class="value">{{ item.height ? item.height + 'cm' : '未填写' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">学历</text>
|
||||
<text class="value">{{ item.educationName || '未填写' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">体重</text>
|
||||
<text class="value">{{ item.weight ? item.weight + 'kg' : '未填写' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">职业</text>
|
||||
<text class="value">{{ item.occupation || '未填写' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">家乡</text>
|
||||
<text class="value">{{ item.hometown || '未填写' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 右侧照片 -->
|
||||
<view class="photo-section" v-if="item.isPhotoPublic && item.firstPhoto">
|
||||
<image class="user-photo" :src="getFullUrl(item.firstPhoto)" mode="aspectFill" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="user-detail">
|
||||
<text>{{ item.age }}岁</text>
|
||||
<text class="divider">|</text>
|
||||
<text>{{ item.workCity }}</text>
|
||||
<text class="divider">|</text>
|
||||
<text>{{ item.height }}cm</text>
|
||||
|
||||
<!-- 简介 -->
|
||||
<view class="intro-section" v-if="item.intro">
|
||||
<text class="intro-text">{{ item.intro }}</text>
|
||||
</view>
|
||||
<view class="user-time">
|
||||
<text>{{ formatTime(item.createTime) }}</text>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="action-buttons" @click.stop>
|
||||
<button class="btn-detail" @click="handleUserClick(item.userId)">查看详细资料</button>
|
||||
<button class="btn-contact" @click="handleContact(item.userId)">联系对方</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="user-action" @click.stop="handleContact(item.userId)">
|
||||
<text>联系TA</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<Empty
|
||||
v-else-if="!listLoading"
|
||||
:text="activeTab === 'viewedMe' ? '暂无人看过你' : '你还没有看过别人'"
|
||||
buttonText="去相亲"
|
||||
buttonUrl="/pages/index/index"
|
||||
/>
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<Loading
|
||||
type="more"
|
||||
:loading="listLoading"
|
||||
:noMore="noMoreData && list.length > 0"
|
||||
/>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<Empty
|
||||
v-else-if="!listLoading"
|
||||
:text="activeTab === 'viewedMe' ? '暂无人看过你' : '你还没有看过别人'"
|
||||
buttonText="去相亲"
|
||||
buttonUrl="/pages/index/index"
|
||||
/>
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<Loading
|
||||
type="more"
|
||||
:loading="listLoading"
|
||||
:noMore="noMoreData && list.length > 0"
|
||||
/>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { getViewedMe, getMyViewed } from '@/api/interact.js'
|
||||
import { formatTimestamp } from '@/utils/format.js'
|
||||
import { getFullImageUrl } from '@/utils/image.js'
|
||||
import { useUserStore } from '@/store/user.js'
|
||||
import { useConfigStore } from '@/store/config.js'
|
||||
import Loading from '@/components/Loading/index.vue'
|
||||
|
|
@ -102,6 +154,9 @@ export default {
|
|||
const userStore = useUserStore()
|
||||
const configStore = useConfigStore()
|
||||
|
||||
// 状态栏高度
|
||||
const statusBarHeight = ref(20)
|
||||
|
||||
// 页面状态
|
||||
const pageLoading = ref(true)
|
||||
const listLoading = ref(false)
|
||||
|
|
@ -116,6 +171,56 @@ export default {
|
|||
// 从 configStore 获取默认头像
|
||||
const defaultAvatar = computed(() => configStore.defaultAvatar || '/static/logo.png')
|
||||
|
||||
// 获取系统信息
|
||||
const getSystemInfo = () => {
|
||||
uni.getSystemInfo({
|
||||
success: (res) => {
|
||||
statusBarHeight.value = res.statusBarHeight || 20
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 返回上一页
|
||||
const handleBack = () => {
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
// 获取完整图片URL
|
||||
const getFullUrl = (url) => {
|
||||
return getFullImageUrl(url)
|
||||
}
|
||||
|
||||
// 从年龄计算出生年份
|
||||
const getYearFromAge = (age) => {
|
||||
if (!age) return ''
|
||||
return new Date().getFullYear() - age
|
||||
}
|
||||
|
||||
// 格式化浏览时间 - 显示为"今天15:21"或"昨天15:21"或"01-20 15:21"
|
||||
const formatViewTime = (timeStr) => {
|
||||
if (!timeStr) return ''
|
||||
|
||||
const date = new Date(timeStr)
|
||||
const now = new Date()
|
||||
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
|
||||
const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000)
|
||||
const targetDate = new Date(date.getFullYear(), date.getMonth(), date.getDate())
|
||||
|
||||
const hours = String(date.getHours()).padStart(2, '0')
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||
const timeStr2 = `${hours}:${minutes}`
|
||||
|
||||
if (targetDate.getTime() === today.getTime()) {
|
||||
return `今天${timeStr2}`
|
||||
} else if (targetDate.getTime() === yesterday.getTime()) {
|
||||
return `昨天${timeStr2}`
|
||||
} else {
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
return `${month}-${day} ${timeStr2}`
|
||||
}
|
||||
}
|
||||
|
||||
// 加载列表
|
||||
const loadList = async (refresh = false) => {
|
||||
if (refresh) {
|
||||
|
|
@ -157,6 +262,7 @@ export default {
|
|||
const initPage = async () => {
|
||||
pageLoading.value = true
|
||||
try {
|
||||
getSystemInfo()
|
||||
await loadList(true)
|
||||
} finally {
|
||||
pageLoading.value = false
|
||||
|
|
@ -168,18 +274,15 @@ export default {
|
|||
if (activeTab.value === tab) return
|
||||
activeTab.value = tab
|
||||
|
||||
// 更新导航栏标题
|
||||
uni.setNavigationBarTitle({
|
||||
title: tab === 'viewedMe' ? '看过我' : '我看过的'
|
||||
})
|
||||
|
||||
// 重新加载数据
|
||||
loadList(true)
|
||||
}
|
||||
|
||||
// 格式化时间
|
||||
const formatTime = (timestamp) => {
|
||||
return formatTimestamp(timestamp)
|
||||
// 滚动到底部加载更多
|
||||
const handleScrollToLower = () => {
|
||||
if (!noMoreData.value && !listLoading.value) {
|
||||
loadList()
|
||||
}
|
||||
}
|
||||
|
||||
// 点击用户
|
||||
|
|
@ -212,16 +315,21 @@ export default {
|
|||
})
|
||||
|
||||
return {
|
||||
statusBarHeight,
|
||||
pageLoading,
|
||||
listLoading,
|
||||
noMoreData,
|
||||
activeTab,
|
||||
list,
|
||||
defaultAvatar,
|
||||
handleBack,
|
||||
switchTab,
|
||||
formatTime,
|
||||
formatViewTime,
|
||||
getFullUrl,
|
||||
getYearFromAge,
|
||||
handleUserClick,
|
||||
handleContact,
|
||||
handleScrollToLower,
|
||||
loadList
|
||||
}
|
||||
},
|
||||
|
|
@ -230,156 +338,276 @@ export default {
|
|||
this.loadList && this.loadList(true).finally(() => {
|
||||
uni.stopPullDownRefresh()
|
||||
})
|
||||
},
|
||||
// 触底加载更多
|
||||
onReachBottom() {
|
||||
if (!this.noMoreData && !this.listLoading) {
|
||||
this.loadList && this.loadList()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.interact-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f8f8f8;
|
||||
height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// 顶部背景图
|
||||
.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: 200;
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tab切换
|
||||
.tab-header {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
background-color: #fff;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
padding: 20rpx 40rpx;
|
||||
gap: 24rpx;
|
||||
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 28rpx 0;
|
||||
position: relative;
|
||||
padding: 20rpx 40rpx;
|
||||
border-radius: 40rpx;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
|
||||
|
||||
text {
|
||||
font-size: 30rpx;
|
||||
color: #666;
|
||||
color: #2F2F2F;
|
||||
}
|
||||
|
||||
&.active {
|
||||
text {
|
||||
color: #ff6b6b;
|
||||
font-weight: 600;
|
||||
}
|
||||
background-color: #FFF0F0;
|
||||
position: relative;
|
||||
box-shadow: 0 4rpx 16rpx rgba(255, 107, 107, 0.2);
|
||||
|
||||
&::after {
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 60rpx;
|
||||
height: 4rpx;
|
||||
background-color: #ff6b6b;
|
||||
border-radius: 2rpx;
|
||||
border-radius: 40rpx;
|
||||
padding: 2rpx;
|
||||
background: linear-gradient(to right, #FFCBCB, #FF7B7B);
|
||||
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||
-webkit-mask-composite: xor;
|
||||
mask-composite: exclude;
|
||||
}
|
||||
|
||||
text {
|
||||
color: #FF5F5F;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 可滚动内容区域
|
||||
.content-scroll {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
// 用户列表
|
||||
.user-list {
|
||||
padding: 0 20rpx;
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
// 用户卡片
|
||||
.user-card {
|
||||
background: linear-gradient(to bottom, #FFEBEA 0%, #FFFFFF 100%);
|
||||
border-radius: 24rpx;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
// 时间行
|
||||
.time-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.user-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 24rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 16rpx;
|
||||
margin-top: 20rpx;
|
||||
|
||||
&:active {
|
||||
background-color: #f8f8f8;
|
||||
.time-text {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.view-count {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 标题行
|
||||
.title-row {
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.gender-year {
|
||||
font-size: 40rpx;
|
||||
font-weight: 600;
|
||||
color: #ff6b6b;
|
||||
}
|
||||
}
|
||||
|
||||
// 内容区域
|
||||
.content-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
// 信息区域
|
||||
.info-section {
|
||||
flex: 1;
|
||||
|
||||
&.full-width {
|
||||
.info-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
margin-right: 24rpx;
|
||||
flex-shrink: 0;
|
||||
|
||||
.avatar-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.user-info {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
|
||||
.user-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8rpx;
|
||||
|
||||
.user-nickname {
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.user-badges {
|
||||
display: flex;
|
||||
gap: 8rpx;
|
||||
|
||||
.badge {
|
||||
padding: 2rpx 8rpx;
|
||||
border-radius: 6rpx;
|
||||
font-size: 20rpx;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.member-badge {
|
||||
background: linear-gradient(135deg, #ffd700 0%, #ffb800 100%);
|
||||
}
|
||||
|
||||
.realname-badge {
|
||||
background: linear-gradient(135deg, #4cd964 0%, #34c759 100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-detail {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
margin-bottom: 8rpx;
|
||||
|
||||
.divider {
|
||||
margin: 0 8rpx;
|
||||
color: #ddd;
|
||||
}
|
||||
}
|
||||
|
||||
.user-time {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.user-action {
|
||||
padding: 12rpx 24rpx;
|
||||
background: linear-gradient(135deg, #ff6b6b 0%, #ff5252 100%);
|
||||
border-radius: 30rpx;
|
||||
flex-shrink: 0;
|
||||
|
||||
text {
|
||||
font-size: 26rpx;
|
||||
color: #fff;
|
||||
}
|
||||
.info-item {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
.info-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
.label {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
width: 80rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 照片区域
|
||||
.photo-section {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
margin-left: 24rpx;
|
||||
flex-shrink: 0;
|
||||
|
||||
.user-photo {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 简介区域
|
||||
.intro-section {
|
||||
margin-top: 24rpx;
|
||||
padding-top: 24rpx;
|
||||
border-top: 1rpx solid #f5f5f5;
|
||||
|
||||
.intro-text {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
// 操作按钮
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 24rpx;
|
||||
margin-top: 32rpx;
|
||||
|
||||
button {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
line-height: 80rpx;
|
||||
font-size: 30rpx;
|
||||
border-radius: 40rpx;
|
||||
border: none;
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-detail {
|
||||
background: #fff;
|
||||
color: #333;
|
||||
border: 2rpx solid #FFCBCB;
|
||||
}
|
||||
|
||||
.btn-contact {
|
||||
background: linear-gradient(to right, #FFBDC2, #FF8A93);
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -6,23 +6,60 @@
|
|||
<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="header-section" :style="{ paddingTop: (statusBarHeight + 44) + 'px' }">
|
||||
<view class="search-date">搜索日期 {{ searchDate }}</view>
|
||||
<view class="search-title">
|
||||
<text>根据您的要求</text>
|
||||
<text class="highlight">精准搜索出</text>
|
||||
</view>
|
||||
<view class="search-count">
|
||||
<text class="count-number">{{ totalCount }}+</text>
|
||||
<text class="count-text">位合适的亲家</text>
|
||||
</view>
|
||||
|
||||
<!-- 搜索条件标签 -->
|
||||
<view class="search-tags">
|
||||
<view class="tag" v-if="searchParams.ageMin || searchParams.ageMax">
|
||||
年龄{{ searchParams.ageMin || 18 }}-{{ searchParams.ageMax || 60 }}岁
|
||||
</view>
|
||||
<view class="tag" v-if="searchParams.heightMin || searchParams.heightMax">
|
||||
身高{{ searchParams.heightMin || 150 }}cm及以上
|
||||
</view>
|
||||
<view class="tag" v-if="searchParams.cities && searchParams.cities.length > 0">
|
||||
地区{{ searchParams.cities[0] }}
|
||||
</view>
|
||||
<view class="tag" v-if="searchParams.carStatus">
|
||||
{{ searchParams.carStatus === 1 ? '有车' : '车产不限' }}
|
||||
</view>
|
||||
<view class="tag" v-if="searchParams.houseStatus">
|
||||
{{ searchParams.houseStatus === 1 ? '有房' : '房产不限' }}
|
||||
</view>
|
||||
<view class="tag" v-if="searchParams.marriageStatus">
|
||||
{{ getMarriageText(searchParams.marriageStatus) }}
|
||||
</view>
|
||||
<view class="tag" v-if="searchParams.monthlyIncome">
|
||||
{{ getIncomeText(searchParams.monthlyIncome) }}
|
||||
</view>
|
||||
<view class="tag" v-if="searchParams.education && searchParams.education.length > 0">
|
||||
{{ getEducationText(searchParams.education[0]) }}
|
||||
</view>
|
||||
<view class="tag more" v-if="hasMoreTags" @click="showAllTags = !showAllTags">
|
||||
{{ showAllTags ? '收起' : '更多...' }} ∨
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<scroll-view
|
||||
class="content-scroll"
|
||||
scroll-y
|
||||
:style="{ paddingTop: (statusBarHeight + 44) + 'px' }"
|
||||
@scrolltolower="loadMore"
|
||||
>
|
||||
<!-- 结果统计 -->
|
||||
<view class="result-header" v-if="!loading">
|
||||
<text class="result-count">共找到 {{ totalCount }} 位符合条件的用户</text>
|
||||
</view>
|
||||
|
||||
<!-- 加载中 -->
|
||||
<Loading v-if="loading" type="page" :loading="loading" />
|
||||
|
||||
|
|
@ -34,46 +71,92 @@
|
|||
<view
|
||||
v-for="user in results"
|
||||
:key="user.userId"
|
||||
class="result-item-wrapper"
|
||||
class="result-card"
|
||||
@click="handleCardClick(user.userId)"
|
||||
>
|
||||
<!-- 非会员模糊遮罩 -->
|
||||
<view v-if="!isMember" class="blur-mask">
|
||||
<view class="blur-content">
|
||||
<text class="blur-text">开通会员查看完整信息</text>
|
||||
<button class="btn-member" @click="goToMember">立即开通</button>
|
||||
<!-- 性别年份和标签 -->
|
||||
<view class="card-header">
|
||||
<view class="gender-year">
|
||||
<text :class="{ male: user.gender === 1 }">{{ user.gender === 1 ? '男' : '女' }}·{{ user.birthYear || '99' }}年</text>
|
||||
</view>
|
||||
<view class="user-tags">
|
||||
<view class="tag vip" v-if="user.isMember">
|
||||
<image src="/static/vip-icon.png" mode="aspectFit" class="vip-icon" />
|
||||
<text>xx会员</text>
|
||||
</view>
|
||||
<view class="tag realname" v-if="user.isRealName">已实名</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<UserCard
|
||||
:userId="user.userId"
|
||||
:nickname="user.nickname"
|
||||
:avatar="user.avatar"
|
||||
:gender="user.gender"
|
||||
:age="user.age"
|
||||
:birthYear="user.birthYear"
|
||||
:workCity="user.workCity"
|
||||
:hometown="user.hometown"
|
||||
:height="user.height"
|
||||
:weight="user.weight"
|
||||
:education="user.education"
|
||||
:educationName="user.educationName"
|
||||
:occupation="user.occupation"
|
||||
:monthlyIncome="user.monthlyIncome"
|
||||
:intro="user.intro"
|
||||
:isMember="user.isMember"
|
||||
:isRealName="user.isRealName"
|
||||
:isPhotoPublic="user.isPhotoPublic"
|
||||
:firstPhoto="user.firstPhoto"
|
||||
:viewedToday="user.viewedToday"
|
||||
@click="handleCardClick"
|
||||
@contact="handleContact"
|
||||
/>
|
||||
<!-- 详细信息网格 -->
|
||||
<view class="info-grid">
|
||||
<view class="info-row">
|
||||
<view class="info-item">
|
||||
<text class="label">年龄</text>
|
||||
<text class="value">{{ user.age }}岁</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">身高</text>
|
||||
<text class="value">{{ user.height || '-' }}cm</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<view class="info-item">
|
||||
<text class="label">学历</text>
|
||||
<text class="value">{{ user.educationName || getEducationText(user.education) }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">体重</text>
|
||||
<text class="value">{{ user.weight || '-' }}kg</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<view class="info-item">
|
||||
<text class="label">收入</text>
|
||||
<text class="value">{{ getIncomeText(user.monthlyIncome) }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">职业</text>
|
||||
<text class="value">{{ user.occupation || '未填写' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<view class="info-item">
|
||||
<text class="label">现居</text>
|
||||
<text class="value">{{ user.workCity || '未填写' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">家乡</text>
|
||||
<text class="value">{{ user.hometown || '未填写' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 简介 -->
|
||||
<view class="intro-section" v-if="user.intro">
|
||||
<text class="intro-text">{{ user.intro }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 联系按钮 -->
|
||||
<view class="contact-btn" @click.stop="handleContact(user.userId)">
|
||||
<text class="lock-icon">🔒</text>
|
||||
<text>开通诚意会员 联系对方</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<Loading v-if="results.length > 0" type="more" :loading="loadingMore" :noMore="!hasMore" />
|
||||
|
||||
<!-- 底部占位 -->
|
||||
<view class="bottom-placeholder"></view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 底部会员按钮 -->
|
||||
<view class="bottom-bar" v-if="!isMember">
|
||||
<button class="member-btn" @click="goToMember">开通诚意会员</button>
|
||||
<text class="member-tip" @click="goToMember">查看诚意会员其他特权</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
|
|
@ -81,7 +164,6 @@
|
|||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useUserStore } from '@/store/user.js'
|
||||
import { search } from '@/api/user.js'
|
||||
import UserCard from '@/components/UserCard/index.vue'
|
||||
import Empty from '@/components/Empty/index.vue'
|
||||
import Loading from '@/components/Loading/index.vue'
|
||||
|
||||
|
|
@ -96,6 +178,24 @@ const isMember = computed(() => userStore.isMember)
|
|||
// 搜索参数
|
||||
const searchParams = ref({})
|
||||
|
||||
// 搜索日期
|
||||
const searchDate = ref('')
|
||||
|
||||
// 显示所有标签
|
||||
const showAllTags = ref(false)
|
||||
const hasMoreTags = computed(() => {
|
||||
let count = 0
|
||||
if (searchParams.value.ageMin || searchParams.value.ageMax) count++
|
||||
if (searchParams.value.heightMin || searchParams.value.heightMax) count++
|
||||
if (searchParams.value.cities?.length > 0) count++
|
||||
if (searchParams.value.carStatus) count++
|
||||
if (searchParams.value.houseStatus) count++
|
||||
if (searchParams.value.marriageStatus) count++
|
||||
if (searchParams.value.monthlyIncome) count++
|
||||
if (searchParams.value.education?.length > 0) count++
|
||||
return count > 6
|
||||
})
|
||||
|
||||
// 搜索状态
|
||||
const loading = ref(true)
|
||||
const loadingMore = ref(false)
|
||||
|
|
@ -105,6 +205,34 @@ const pageIndex = ref(1)
|
|||
const pageSize = ref(10)
|
||||
const hasMore = ref(true)
|
||||
|
||||
// 选项映射
|
||||
const educationMap = {
|
||||
1: '高中',
|
||||
2: '中专',
|
||||
3: '大专',
|
||||
4: '本科',
|
||||
5: '研究生',
|
||||
6: '博士及以上'
|
||||
}
|
||||
|
||||
const incomeMap = {
|
||||
1: '5千以下',
|
||||
2: '5千-8千/月',
|
||||
3: '8千-1万/月',
|
||||
4: '1万-2万/月',
|
||||
5: '2万以上'
|
||||
}
|
||||
|
||||
const marriageMap = {
|
||||
1: '未婚',
|
||||
2: '离异',
|
||||
3: '丧偶'
|
||||
}
|
||||
|
||||
const getEducationText = (value) => educationMap[value] || '学历不限'
|
||||
const getIncomeText = (value) => incomeMap[value] || '收入不限'
|
||||
const getMarriageText = (value) => marriageMap[value] || '婚史不限'
|
||||
|
||||
// 获取系统信息
|
||||
const getSystemInfo = () => {
|
||||
uni.getSystemInfo({
|
||||
|
|
@ -187,11 +315,6 @@ const loadMore = () => {
|
|||
|
||||
// 点击用户卡片
|
||||
const handleCardClick = (userId) => {
|
||||
if (!isMember.value) {
|
||||
uni.showToast({ title: '开通会员查看完整信息', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
uni.navigateTo({
|
||||
url: `/pages/profile/detail?userId=${userId}`
|
||||
})
|
||||
|
|
@ -200,7 +323,7 @@ const handleCardClick = (userId) => {
|
|||
// 联系用户
|
||||
const handleContact = (userId) => {
|
||||
if (!isMember.value) {
|
||||
uni.showToast({ title: '开通会员联系TA', icon: 'none' })
|
||||
goToMember()
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -231,8 +354,18 @@ const goToMember = () => {
|
|||
})
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
const formatDate = () => {
|
||||
const now = new Date()
|
||||
const year = now.getFullYear()
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(now.getDate()).padStart(2, '0')
|
||||
return `${year}-${month}-${day}`
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getSystemInfo()
|
||||
searchDate.value = formatDate()
|
||||
|
||||
// 获取页面参数
|
||||
const pages = getCurrentPages()
|
||||
|
|
@ -252,6 +385,7 @@ onMounted(() => {
|
|||
})
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.result-page {
|
||||
min-height: 100vh;
|
||||
|
|
@ -265,7 +399,7 @@ onMounted(() => {
|
|||
left: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
background: linear-gradient(135deg, #ffb5b5 0%, #ff9a9a 100%);
|
||||
background: transparent;
|
||||
|
||||
.navbar-content {
|
||||
height: 44px;
|
||||
|
|
@ -283,37 +417,82 @@ onMounted(() => {
|
|||
|
||||
.back-icon {
|
||||
font-size: 64rpx;
|
||||
color: #fff;
|
||||
color: #333;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.navbar-placeholder {
|
||||
width: 80rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 内容滚动区域
|
||||
.content-scroll {
|
||||
min-height: 100vh;
|
||||
padding-bottom: 40rpx;
|
||||
// 顶部背景区域
|
||||
.header-section {
|
||||
background: linear-gradient(180deg, #FFD4D4 0%, #FFE8E8 50%, #f5f6fa 100%);
|
||||
padding: 30rpx 32rpx 40rpx;
|
||||
text-align: center;
|
||||
|
||||
.search-date {
|
||||
font-size: 24rpx;
|
||||
color: #FF6B6B;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.search-title {
|
||||
font-size: 40rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 12rpx;
|
||||
|
||||
.highlight {
|
||||
color: #FF5F5F;
|
||||
}
|
||||
}
|
||||
|
||||
.search-count {
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
.count-number {
|
||||
font-size: 48rpx;
|
||||
font-weight: 700;
|
||||
color: #FF5F5F;
|
||||
}
|
||||
|
||||
.count-text {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #FF5F5F;
|
||||
}
|
||||
}
|
||||
|
||||
.search-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16rpx;
|
||||
justify-content: center;
|
||||
|
||||
.tag {
|
||||
padding: 12rpx 24rpx;
|
||||
background: #FFF0F0;
|
||||
border-radius: 30rpx;
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
border: 1rpx solid #FFD4D4;
|
||||
|
||||
&.more {
|
||||
color: #FF5F5F;
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 结果统计
|
||||
.result-header {
|
||||
padding: 24rpx 32rpx;
|
||||
|
||||
.result-count {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
// 内容滚动区域
|
||||
.content-scroll {
|
||||
min-height: calc(100vh - 400rpx);
|
||||
padding-bottom: 200rpx;
|
||||
}
|
||||
|
||||
// 结果列表
|
||||
|
|
@ -321,49 +500,168 @@ onMounted(() => {
|
|||
padding: 0 24rpx;
|
||||
}
|
||||
|
||||
.result-item-wrapper {
|
||||
position: relative;
|
||||
// 结果卡片
|
||||
.result-card {
|
||||
background: #fff;
|
||||
border-radius: 24rpx;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.blur-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
backdrop-filter: blur(10px);
|
||||
z-index: 10;
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 24rpx;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.blur-content {
|
||||
text-align: center;
|
||||
|
||||
.blur-text {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.btn-member {
|
||||
width: 240rpx;
|
||||
height: 72rpx;
|
||||
line-height: 72rpx;
|
||||
background: linear-gradient(135deg, #ffd700 0%, #ffb800 100%);
|
||||
color: #fff;
|
||||
font-size: 28rpx;
|
||||
border-radius: 36rpx;
|
||||
border: none;
|
||||
.gender-year {
|
||||
text {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #FF5F5F;
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
&.male {
|
||||
color: #4A90D9;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-tags {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
|
||||
.tag {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 6rpx 16rpx;
|
||||
border-radius: 20rpx;
|
||||
font-size: 22rpx;
|
||||
|
||||
&.vip {
|
||||
background: linear-gradient(135deg, #FFE4B5 0%, #FFD700 100%);
|
||||
color: #8B4513;
|
||||
|
||||
.vip-icon {
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
margin-right: 6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
&.realname {
|
||||
background: #E8F5E9;
|
||||
color: #4CAF50;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
background: #FFF9F9;
|
||||
border-radius: 16rpx;
|
||||
padding: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
|
||||
.label {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
width: 80rpx;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.intro-section {
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.intro-text {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.contact-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 80rpx;
|
||||
background: linear-gradient(90deg, #F6C47A 0%, #FFE7C3 50%, #F6C47A 100%);
|
||||
border: none;
|
||||
border-radius: 40rpx;
|
||||
|
||||
.lock-icon {
|
||||
font-size: 28rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
text {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 底部占位
|
||||
.bottom-placeholder {
|
||||
height: 40rpx;
|
||||
}
|
||||
|
||||
// 底部会员按钮
|
||||
.bottom-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #fff;
|
||||
padding: 20rpx 32rpx;
|
||||
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||||
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.05);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.member-btn {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
background: linear-gradient(135deg, #FFB5B5 0%, #FF9A9A 100%);
|
||||
color: #fff;
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
border-radius: 44rpx;
|
||||
border: none;
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.member-tip {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -125,6 +125,20 @@ public class AdminUserController : ControllerBase
|
|||
var result = await _adminUserService.UpdateContactCountAsync(id, request.ContactCount, adminId);
|
||||
return result ? ApiResponse.Success("更新成功") : ApiResponse.Error(40001, "更新失败");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新用户会员等级
|
||||
/// </summary>
|
||||
/// <param name="id">用户ID</param>
|
||||
/// <param name="request">会员等级更新请求</param>
|
||||
/// <returns>操作结果</returns>
|
||||
[HttpPut("{id}/member-level")]
|
||||
public async Task<ApiResponse> UpdateMemberLevel(long id, [FromBody] UpdateMemberLevelRequest request)
|
||||
{
|
||||
var adminId = GetCurrentAdminId();
|
||||
var result = await _adminUserService.UpdateMemberLevelAsync(id, request.MemberLevel, request.MemberExpireTime, adminId);
|
||||
return result ? ApiResponse.Success("更新成功") : ApiResponse.Error(40001, "更新失败");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -153,3 +167,19 @@ public class UpdateContactCountRequest
|
|||
/// </summary>
|
||||
public int ContactCount { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新会员等级请求
|
||||
/// </summary>
|
||||
public class UpdateMemberLevelRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// 会员等级:0非会员 1不限时会员 2诚意会员 3家庭版会员
|
||||
/// </summary>
|
||||
public int MemberLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 会员到期时间(等级2、3需要)
|
||||
/// </summary>
|
||||
public DateTime? MemberExpireTime { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,16 +20,81 @@ public class ViewRecordResponse
|
|||
/// </summary>
|
||||
public string? Avatar { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 性别 1男 2女
|
||||
/// </summary>
|
||||
public int? Gender { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 年龄
|
||||
/// </summary>
|
||||
public int? Age { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 出生年份
|
||||
/// </summary>
|
||||
public int? BirthYear { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 工作城市
|
||||
/// </summary>
|
||||
public string? WorkCity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 家乡
|
||||
/// </summary>
|
||||
public string? Hometown { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 身高
|
||||
/// </summary>
|
||||
public int? Height { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 体重
|
||||
/// </summary>
|
||||
public int? Weight { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 学历
|
||||
/// </summary>
|
||||
public int? Education { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 学历名称
|
||||
/// </summary>
|
||||
public string? EducationName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 职业
|
||||
/// </summary>
|
||||
public string? Occupation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 简介
|
||||
/// </summary>
|
||||
public string? Intro { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否会员
|
||||
/// </summary>
|
||||
public bool IsMember { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否实名
|
||||
/// </summary>
|
||||
public bool IsRealName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否公开照片
|
||||
/// </summary>
|
||||
public bool IsPhotoPublic { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 首张照片
|
||||
/// </summary>
|
||||
public string? FirstPhoto { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 浏览次数
|
||||
/// </summary>
|
||||
|
|
@ -61,16 +126,81 @@ public class FavoriteRecordResponse
|
|||
/// </summary>
|
||||
public string? Avatar { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 性别 1男 2女
|
||||
/// </summary>
|
||||
public int? Gender { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 年龄
|
||||
/// </summary>
|
||||
public int? Age { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 出生年份
|
||||
/// </summary>
|
||||
public int? BirthYear { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 工作城市
|
||||
/// </summary>
|
||||
public string? WorkCity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 家乡
|
||||
/// </summary>
|
||||
public string? Hometown { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 身高
|
||||
/// </summary>
|
||||
public int? Height { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 体重
|
||||
/// </summary>
|
||||
public int? Weight { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 学历
|
||||
/// </summary>
|
||||
public int? Education { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 学历名称
|
||||
/// </summary>
|
||||
public string? EducationName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 职业
|
||||
/// </summary>
|
||||
public string? Occupation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 简介
|
||||
/// </summary>
|
||||
public string? Intro { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否会员
|
||||
/// </summary>
|
||||
public bool IsMember { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否实名
|
||||
/// </summary>
|
||||
public bool IsRealName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否公开照片
|
||||
/// </summary>
|
||||
public bool IsPhotoPublic { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 首张照片
|
||||
/// </summary>
|
||||
public string? FirstPhoto { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 收藏时间
|
||||
/// </summary>
|
||||
|
|
@ -118,16 +248,81 @@ public class UnlockRecordResponse
|
|||
/// </summary>
|
||||
public string? Avatar { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 性别 1男 2女
|
||||
/// </summary>
|
||||
public int? Gender { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 年龄
|
||||
/// </summary>
|
||||
public int? Age { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 出生年份
|
||||
/// </summary>
|
||||
public int? BirthYear { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 工作城市
|
||||
/// </summary>
|
||||
public string? WorkCity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 家乡
|
||||
/// </summary>
|
||||
public string? Hometown { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 身高
|
||||
/// </summary>
|
||||
public int? Height { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 体重
|
||||
/// </summary>
|
||||
public int? Weight { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 学历
|
||||
/// </summary>
|
||||
public int? Education { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 学历名称
|
||||
/// </summary>
|
||||
public string? EducationName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 职业
|
||||
/// </summary>
|
||||
public string? Occupation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 简介
|
||||
/// </summary>
|
||||
public string? Intro { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否会员
|
||||
/// </summary>
|
||||
public bool IsMember { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否实名
|
||||
/// </summary>
|
||||
public bool IsRealName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否公开照片
|
||||
/// </summary>
|
||||
public bool IsPhotoPublic { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 首张照片
|
||||
/// </summary>
|
||||
public string? FirstPhoto { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 解锁时间
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -61,4 +61,14 @@ public interface IAdminUserService
|
|||
/// <param name="adminId">操作管理员ID</param>
|
||||
/// <returns>是否成功</returns>
|
||||
Task<bool> UpdateContactCountAsync(long userId, int contactCount, long adminId);
|
||||
|
||||
/// <summary>
|
||||
/// 更新用户会员等级
|
||||
/// </summary>
|
||||
/// <param name="userId">用户ID</param>
|
||||
/// <param name="memberLevel">会员等级:0非会员 1不限时会员 2诚意会员 3家庭版会员</param>
|
||||
/// <param name="memberExpireTime">会员到期时间(等级2、3需要)</param>
|
||||
/// <param name="adminId">操作管理员ID</param>
|
||||
/// <returns>是否成功</returns>
|
||||
Task<bool> UpdateMemberLevelAsync(long userId, int memberLevel, DateTime? memberExpireTime, long adminId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -465,6 +465,51 @@ public class AdminUserService : IAdminUserService
|
|||
return result > 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> UpdateMemberLevelAsync(long userId, int memberLevel, DateTime? memberExpireTime, long adminId)
|
||||
{
|
||||
var user = await _userRepository.GetByIdAsync(userId);
|
||||
if (user == null || user.IsDeleted)
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.UserNotFound, "用户不存在");
|
||||
}
|
||||
|
||||
// 验证会员等级
|
||||
if (memberLevel < 0 || memberLevel > 3)
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.InvalidParameter, "无效的会员等级");
|
||||
}
|
||||
|
||||
var oldLevel = user.MemberLevel;
|
||||
var oldExpireTime = user.MemberExpireTime;
|
||||
|
||||
user.MemberLevel = memberLevel;
|
||||
user.IsMember = memberLevel > 0;
|
||||
|
||||
// 不限时会员不需要到期时间
|
||||
if (memberLevel == 1)
|
||||
{
|
||||
user.MemberExpireTime = null;
|
||||
}
|
||||
else if (memberLevel > 1)
|
||||
{
|
||||
user.MemberExpireTime = memberExpireTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
user.MemberExpireTime = null;
|
||||
}
|
||||
|
||||
user.UpdateTime = DateTime.Now;
|
||||
|
||||
var result = await _userRepository.UpdateAsync(user);
|
||||
|
||||
_logger.LogInformation("管理员更新用户会员等级: AdminId={AdminId}, UserId={UserId}, OldLevel={OldLevel}, NewLevel={NewLevel}, ExpireTime={ExpireTime}",
|
||||
adminId, userId, oldLevel, memberLevel, memberExpireTime);
|
||||
|
||||
return result > 0;
|
||||
}
|
||||
|
||||
#region Private Helper Methods
|
||||
|
||||
private static AdminUserListDto MapToUserListDto(User user, UserProfile? profile)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ public class InteractService : IInteractService
|
|||
private readonly IRepository<UserUnlock> _unlockRepository;
|
||||
private readonly IRepository<ChatSession> _sessionRepository;
|
||||
private readonly IRepository<UserProfile> _profileRepository;
|
||||
private readonly IRepository<UserPhoto> _photoRepository;
|
||||
private readonly ILogger<InteractService> _logger;
|
||||
|
||||
public InteractService(
|
||||
|
|
@ -28,6 +29,7 @@ public class InteractService : IInteractService
|
|||
IRepository<UserUnlock> unlockRepository,
|
||||
IRepository<ChatSession> sessionRepository,
|
||||
IRepository<UserProfile> profileRepository,
|
||||
IRepository<UserPhoto> photoRepository,
|
||||
ILogger<InteractService> logger)
|
||||
{
|
||||
_userRepository = userRepository;
|
||||
|
|
@ -36,6 +38,7 @@ public class InteractService : IInteractService
|
|||
_unlockRepository = unlockRepository;
|
||||
_sessionRepository = sessionRepository;
|
||||
_profileRepository = profileRepository;
|
||||
_photoRepository = photoRepository;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
|
@ -115,14 +118,29 @@ public class InteractService : IInteractService
|
|||
{
|
||||
var user = await _userRepository.GetByIdAsync(item.UserId);
|
||||
var profile = (await _profileRepository.GetListAsync(p => p.UserId == item.UserId)).FirstOrDefault();
|
||||
var photos = await _photoRepository.GetListAsync(p => p.UserId == item.UserId);
|
||||
var firstPhoto = photos.OrderBy(p => p.Sort).FirstOrDefault();
|
||||
|
||||
responses.Add(new ViewRecordResponse
|
||||
{
|
||||
UserId = item.UserId,
|
||||
Nickname = user?.Nickname,
|
||||
Avatar = user?.Avatar,
|
||||
Gender = profile?.ChildGender ?? user?.Gender,
|
||||
Age = profile?.BirthYear != null ? DateTime.Now.Year - profile.BirthYear : null,
|
||||
BirthYear = profile?.BirthYear,
|
||||
WorkCity = profile?.WorkCity,
|
||||
Hometown = profile?.HomeCity,
|
||||
Height = profile?.Height,
|
||||
Weight = profile?.Weight,
|
||||
Education = profile?.Education,
|
||||
EducationName = GetEducationName(profile?.Education),
|
||||
Occupation = profile?.Occupation,
|
||||
Intro = profile?.Introduction,
|
||||
IsMember = user?.IsMember ?? false,
|
||||
IsRealName = user?.IsRealName ?? false,
|
||||
IsPhotoPublic = profile?.IsPhotoPublic ?? false,
|
||||
FirstPhoto = firstPhoto?.PhotoUrl,
|
||||
ViewCount = item.ViewCount,
|
||||
LastViewTime = item.LastViewTime
|
||||
});
|
||||
|
|
@ -152,14 +170,29 @@ public class InteractService : IInteractService
|
|||
{
|
||||
var user = await _userRepository.GetByIdAsync(item.TargetUserId);
|
||||
var profile = (await _profileRepository.GetListAsync(p => p.UserId == item.TargetUserId)).FirstOrDefault();
|
||||
var photos = await _photoRepository.GetListAsync(p => p.UserId == item.TargetUserId);
|
||||
var firstPhoto = photos.OrderBy(p => p.Sort).FirstOrDefault();
|
||||
|
||||
responses.Add(new ViewRecordResponse
|
||||
{
|
||||
UserId = item.TargetUserId,
|
||||
Nickname = user?.Nickname,
|
||||
Avatar = user?.Avatar,
|
||||
Gender = profile?.ChildGender ?? user?.Gender,
|
||||
Age = profile?.BirthYear != null ? DateTime.Now.Year - profile.BirthYear : null,
|
||||
BirthYear = profile?.BirthYear,
|
||||
WorkCity = profile?.WorkCity,
|
||||
Hometown = profile?.HomeCity,
|
||||
Height = profile?.Height,
|
||||
Weight = profile?.Weight,
|
||||
Education = profile?.Education,
|
||||
EducationName = GetEducationName(profile?.Education),
|
||||
Occupation = profile?.Occupation,
|
||||
Intro = profile?.Introduction,
|
||||
IsMember = user?.IsMember ?? false,
|
||||
IsRealName = user?.IsRealName ?? false,
|
||||
IsPhotoPublic = profile?.IsPhotoPublic ?? false,
|
||||
FirstPhoto = firstPhoto?.PhotoUrl,
|
||||
ViewCount = item.ViewCount,
|
||||
LastViewTime = item.LastViewTime
|
||||
});
|
||||
|
|
@ -174,6 +207,23 @@ public class InteractService : IInteractService
|
|||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取学历名称
|
||||
/// </summary>
|
||||
private static string? GetEducationName(int? education)
|
||||
{
|
||||
return education switch
|
||||
{
|
||||
1 => "高中",
|
||||
2 => "中专",
|
||||
3 => "大专",
|
||||
4 => "本科",
|
||||
5 => "研究生",
|
||||
6 => "博士及以上",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 收藏功能
|
||||
|
|
@ -254,14 +304,29 @@ public class InteractService : IInteractService
|
|||
{
|
||||
var user = await _userRepository.GetByIdAsync(item.UserId);
|
||||
var profile = (await _profileRepository.GetListAsync(p => p.UserId == item.UserId)).FirstOrDefault();
|
||||
var photos = await _photoRepository.GetListAsync(p => p.UserId == item.UserId);
|
||||
var firstPhoto = photos.OrderBy(p => p.Sort).FirstOrDefault();
|
||||
|
||||
responses.Add(new FavoriteRecordResponse
|
||||
{
|
||||
UserId = item.UserId,
|
||||
Nickname = user?.Nickname,
|
||||
Avatar = user?.Avatar,
|
||||
Gender = profile?.ChildGender ?? user?.Gender,
|
||||
Age = profile?.BirthYear != null ? DateTime.Now.Year - profile.BirthYear : null,
|
||||
BirthYear = profile?.BirthYear,
|
||||
WorkCity = profile?.WorkCity,
|
||||
Hometown = profile?.HomeCity,
|
||||
Height = profile?.Height,
|
||||
Weight = profile?.Weight,
|
||||
Education = profile?.Education,
|
||||
EducationName = GetEducationName(profile?.Education),
|
||||
Occupation = profile?.Occupation,
|
||||
Intro = profile?.Introduction,
|
||||
IsMember = user?.IsMember ?? false,
|
||||
IsRealName = user?.IsRealName ?? false,
|
||||
IsPhotoPublic = profile?.IsPhotoPublic ?? false,
|
||||
FirstPhoto = firstPhoto?.PhotoUrl,
|
||||
CreateTime = item.CreateTime
|
||||
});
|
||||
}
|
||||
|
|
@ -290,14 +355,29 @@ public class InteractService : IInteractService
|
|||
{
|
||||
var user = await _userRepository.GetByIdAsync(item.TargetUserId);
|
||||
var profile = (await _profileRepository.GetListAsync(p => p.UserId == item.TargetUserId)).FirstOrDefault();
|
||||
var photos = await _photoRepository.GetListAsync(p => p.UserId == item.TargetUserId);
|
||||
var firstPhoto = photos.OrderBy(p => p.Sort).FirstOrDefault();
|
||||
|
||||
responses.Add(new FavoriteRecordResponse
|
||||
{
|
||||
UserId = item.TargetUserId,
|
||||
Nickname = user?.Nickname,
|
||||
Avatar = user?.Avatar,
|
||||
Gender = profile?.ChildGender ?? user?.Gender,
|
||||
Age = profile?.BirthYear != null ? DateTime.Now.Year - profile.BirthYear : null,
|
||||
BirthYear = profile?.BirthYear,
|
||||
WorkCity = profile?.WorkCity,
|
||||
Hometown = profile?.HomeCity,
|
||||
Height = profile?.Height,
|
||||
Weight = profile?.Weight,
|
||||
Education = profile?.Education,
|
||||
EducationName = GetEducationName(profile?.Education),
|
||||
Occupation = profile?.Occupation,
|
||||
Intro = profile?.Introduction,
|
||||
IsMember = user?.IsMember ?? false,
|
||||
IsRealName = user?.IsRealName ?? false,
|
||||
IsPhotoPublic = profile?.IsPhotoPublic ?? false,
|
||||
FirstPhoto = firstPhoto?.PhotoUrl,
|
||||
CreateTime = item.CreateTime
|
||||
});
|
||||
}
|
||||
|
|
@ -413,14 +493,29 @@ public class InteractService : IInteractService
|
|||
{
|
||||
var user = await _userRepository.GetByIdAsync(item.UserId);
|
||||
var profile = (await _profileRepository.GetListAsync(p => p.UserId == item.UserId)).FirstOrDefault();
|
||||
var photos = await _photoRepository.GetListAsync(p => p.UserId == item.UserId);
|
||||
var firstPhoto = photos.OrderBy(p => p.Sort).FirstOrDefault();
|
||||
|
||||
responses.Add(new UnlockRecordResponse
|
||||
{
|
||||
UserId = item.UserId,
|
||||
Nickname = user?.Nickname,
|
||||
Avatar = user?.Avatar,
|
||||
Gender = profile?.ChildGender ?? user?.Gender,
|
||||
Age = profile?.BirthYear != null ? DateTime.Now.Year - profile.BirthYear : null,
|
||||
BirthYear = profile?.BirthYear,
|
||||
WorkCity = profile?.WorkCity,
|
||||
Hometown = profile?.HomeCity,
|
||||
Height = profile?.Height,
|
||||
Weight = profile?.Weight,
|
||||
Education = profile?.Education,
|
||||
EducationName = GetEducationName(profile?.Education),
|
||||
Occupation = profile?.Occupation,
|
||||
Intro = profile?.Introduction,
|
||||
IsMember = user?.IsMember ?? false,
|
||||
IsRealName = user?.IsRealName ?? false,
|
||||
IsPhotoPublic = profile?.IsPhotoPublic ?? false,
|
||||
FirstPhoto = firstPhoto?.PhotoUrl,
|
||||
CreateTime = item.CreateTime
|
||||
});
|
||||
}
|
||||
|
|
@ -449,14 +544,29 @@ public class InteractService : IInteractService
|
|||
{
|
||||
var user = await _userRepository.GetByIdAsync(item.TargetUserId);
|
||||
var profile = (await _profileRepository.GetListAsync(p => p.UserId == item.TargetUserId)).FirstOrDefault();
|
||||
var photos = await _photoRepository.GetListAsync(p => p.UserId == item.TargetUserId);
|
||||
var firstPhoto = photos.OrderBy(p => p.Sort).FirstOrDefault();
|
||||
|
||||
responses.Add(new UnlockRecordResponse
|
||||
{
|
||||
UserId = item.TargetUserId,
|
||||
Nickname = user?.Nickname,
|
||||
Avatar = user?.Avatar,
|
||||
Gender = profile?.ChildGender ?? user?.Gender,
|
||||
Age = profile?.BirthYear != null ? DateTime.Now.Year - profile.BirthYear : null,
|
||||
BirthYear = profile?.BirthYear,
|
||||
WorkCity = profile?.WorkCity,
|
||||
Hometown = profile?.HomeCity,
|
||||
Height = profile?.Height,
|
||||
Weight = profile?.Weight,
|
||||
Education = profile?.Education,
|
||||
EducationName = GetEducationName(profile?.Education),
|
||||
Occupation = profile?.Occupation,
|
||||
Intro = profile?.Introduction,
|
||||
IsMember = user?.IsMember ?? false,
|
||||
IsRealName = user?.IsRealName ?? false,
|
||||
IsPhotoPublic = profile?.IsPhotoPublic ?? false,
|
||||
FirstPhoto = firstPhoto?.PhotoUrl,
|
||||
CreateTime = item.CreateTime
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user