修改UI.
This commit is contained in:
parent
e56f3738c2
commit
181ffdae66
|
|
@ -60,9 +60,8 @@ import { ElMessage } from 'element-plus'
|
|||
import type { UploadFile, UploadFiles, UploadRawFile, UploadUserFile } from 'element-plus'
|
||||
import { getToken } from '@/utils/auth'
|
||||
|
||||
// 获取后端基础地址(去掉 /api 后缀)
|
||||
const API_BASE = import.meta.env.VITE_API_BASE_URL || ''
|
||||
const SERVER_BASE = API_BASE.replace(/\/api$/, '')
|
||||
// 获取静态资源服务器地址(AppApi,用于图片等静态资源)
|
||||
const STATIC_BASE = import.meta.env.VITE_STATIC_BASE_URL || 'http://localhost:5001'
|
||||
|
||||
// 处理图片URL,将相对路径转换为完整URL
|
||||
function getFullImageUrl(url: string): string {
|
||||
|
|
@ -71,8 +70,8 @@ function getFullImageUrl(url: string): string {
|
|||
if (url.startsWith('http://') || url.startsWith('https://')) {
|
||||
return url
|
||||
}
|
||||
// 相对路径,拼接服务器地址
|
||||
return `${SERVER_BASE}${url.startsWith('/') ? '' : '/'}${url}`
|
||||
// 相对路径,拼接静态资源服务器地址
|
||||
return `${STATIC_BASE}${url.startsWith('/') ? '' : '/'}${url}`
|
||||
}
|
||||
|
||||
interface Props {
|
||||
|
|
|
|||
|
|
@ -49,8 +49,8 @@ export default {
|
|||
emits: ['click'],
|
||||
data() {
|
||||
return {
|
||||
defaultImage: '/static/empty.png',
|
||||
hasDefaultImage: false // 默认不使用图片,使用emoji
|
||||
defaultImage: '/static/ic_empty.png',
|
||||
hasDefaultImage: true // 使用空占位图
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ export default {
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.user-card {
|
||||
background-color: #fff;
|
||||
background: linear-gradient(to bottom, #FFEBEA 0%, #FFFFFF 100%);
|
||||
border-radius: 24rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06);
|
||||
|
|
@ -412,11 +412,37 @@ export default {
|
|||
.btn-detail {
|
||||
background: #fff;
|
||||
color: #333;
|
||||
border: 2rpx solid #ddd;
|
||||
border: 2rpx solid #FFCBCB;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -2rpx;
|
||||
left: -2rpx;
|
||||
right: -2rpx;
|
||||
bottom: -2rpx;
|
||||
background: linear-gradient(to right, #FFCBCB, #FF7B7B);
|
||||
z-index: -1;
|
||||
border-radius: 40rpx;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: #fff;
|
||||
z-index: -1;
|
||||
border-radius: 38rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-contact {
|
||||
background: linear-gradient(135deg, #ff8a8a 0%, #ff6b6b 100%);
|
||||
background: linear-gradient(to right, #FFBDC2, #FF8A93);
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "相宜相亲",
|
||||
"enablePullDownRefresh": true
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -53,15 +53,24 @@
|
|||
{
|
||||
"path": "pages/chat/index",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "聊天"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/search/index",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "搜索"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/search/result",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "搜索结果"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/member/index",
|
||||
"style": {
|
||||
|
|
@ -71,6 +80,7 @@
|
|||
{
|
||||
"path": "pages/realname/index",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "实名认证"
|
||||
}
|
||||
},
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -14,28 +14,36 @@
|
|||
|
||||
<!-- 固定头部区域 -->
|
||||
<view class="fixed-header">
|
||||
<!-- Banner 轮播图区域 -->
|
||||
<!-- Banner区域(导航栏和搜索框浮在Banner上) -->
|
||||
<view class="banner-section">
|
||||
<!-- 悬浮标题和搜索框 -->
|
||||
<view class="banner-overlay">
|
||||
<view class="header-title">相宜相亲</view>
|
||||
<view class="search-bar" @click="handleSearchClick">
|
||||
<text class="search-icon">🔍</text>
|
||||
<text class="search-placeholder">搜索你心目中的TA</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Banner 轮播图 -->
|
||||
<swiper v-if="banners.length > 0" class="banner-swiper" :indicator-dots="banners.length > 1"
|
||||
:autoplay="true" :interval="3000" :duration="500" indicator-color="#F3F3F3"
|
||||
:autoplay="true" :interval="3000" :duration="500" indicator-color="rgba(255,255,255,0.5)"
|
||||
indicator-active-color="#FF6A6A" circular>
|
||||
<swiper-item v-for="banner in banners" :key="banner.id" @click="handleBannerClick(banner)">
|
||||
<image class="banner-image" :src="banner.imageUrl" mode="aspectFill" />
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
|
||||
<!-- 无banner时的默认背景 -->
|
||||
<view v-else class="banner-placeholder"></view>
|
||||
|
||||
<!-- 浮层:导航栏 + 搜索框 -->
|
||||
<view class="banner-overlay">
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="navbar-content">
|
||||
<view class="header-title">相宜相亲</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 搜索框 -->
|
||||
<view class="search-section">
|
||||
<view class="search-bar" @click="handleSearchClick">
|
||||
<text class="search-icon">🔍</text>
|
||||
<text class="search-placeholder">搜索你心目中的TA</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 金刚位导航 -->
|
||||
|
|
@ -51,7 +59,11 @@
|
|||
|
||||
<!-- 推荐标题栏 -->
|
||||
<view class="section-header">
|
||||
<text class="section-title">今日推荐</text>
|
||||
<view class="section-title-wrapper">
|
||||
<text class="section-title-main">今日优质推荐</text>
|
||||
<text class="section-title-highlight">已更新</text>
|
||||
</view>
|
||||
<text class="section-subtitle">每天早上五点准时更新</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
|
@ -70,15 +82,8 @@
|
|||
</view>
|
||||
|
||||
<!-- 可滚动的用户列表区域 -->
|
||||
<scroll-view
|
||||
class="user-scroll-area"
|
||||
scroll-y
|
||||
:style="{ height: scrollHeight }"
|
||||
refresher-enabled
|
||||
:refresher-triggered="isRefreshing"
|
||||
@refresherrefresh="handleRefresh"
|
||||
@scrolltolower="handleScrollToLower"
|
||||
>
|
||||
<scroll-view class="user-scroll-area" scroll-y :style="{ height: scrollHeight }" refresher-enabled
|
||||
:refresher-triggered="isRefreshing" @refresherrefresh="handleRefresh" @scrolltolower="handleScrollToLower">
|
||||
<!-- 用户列表 -->
|
||||
<view class="user-list" v-if="recommendList.length > 0">
|
||||
<UserCard v-for="user in recommendList" :key="user.userId" :userId="user.userId"
|
||||
|
|
@ -141,6 +146,9 @@
|
|||
const userStore = useUserStore()
|
||||
const configStore = useConfigStore()
|
||||
|
||||
// 状态栏高度
|
||||
const statusBarHeight = ref(20)
|
||||
|
||||
// 页面状态
|
||||
const pageLoading = ref(true)
|
||||
const listLoading = ref(false)
|
||||
|
|
@ -287,6 +295,13 @@
|
|||
const initPage = async () => {
|
||||
pageLoading.value = true
|
||||
try {
|
||||
// 获取状态栏高度
|
||||
uni.getSystemInfo({
|
||||
success: (res) => {
|
||||
statusBarHeight.value = res.statusBarHeight || 20
|
||||
}
|
||||
})
|
||||
|
||||
// 恢复用户状态
|
||||
userStore.restoreFromStorage()
|
||||
|
||||
|
|
@ -423,7 +438,24 @@
|
|||
|
||||
// 联系用户
|
||||
const handleUserContact = (userId) => {
|
||||
// 检查是否完善资料
|
||||
// 1. 首先检查是否已登录
|
||||
if (!userStore.isLoggedIn) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '请先登录后再联系对方',
|
||||
confirmText: '去登录',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.navigateTo({
|
||||
url: '/pages/auth/login'
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 2. 检查是否完善资料
|
||||
if (!userStore.isProfileCompleted) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
|
|
@ -440,7 +472,7 @@
|
|||
return
|
||||
}
|
||||
|
||||
// 跳转到聊天页面(后续会实现解锁逻辑)
|
||||
// 3. 跳转到聊天页面(后续会实现解锁逻辑)
|
||||
uni.navigateTo({
|
||||
url: `/pages/chat/index?targetUserId=${userId}`
|
||||
})
|
||||
|
|
@ -487,6 +519,7 @@
|
|||
})
|
||||
|
||||
return {
|
||||
statusBarHeight,
|
||||
pageLoading,
|
||||
listLoading,
|
||||
noMoreData,
|
||||
|
|
@ -518,13 +551,6 @@
|
|||
handleRefresh
|
||||
}
|
||||
},
|
||||
// 下拉刷新
|
||||
async onPullDownRefresh() {
|
||||
if (this.loadRecommendList) {
|
||||
await this.loadRecommendList()
|
||||
}
|
||||
uni.stopPullDownRefresh()
|
||||
},
|
||||
// 页面显示
|
||||
onShow() {
|
||||
// 每次显示页面时检查弹窗
|
||||
|
|
@ -554,51 +580,13 @@
|
|||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
// Banner 轮播图区域
|
||||
// Banner区域
|
||||
.banner-section {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
.banner-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 10;
|
||||
padding: 20rpx 24rpx;
|
||||
padding-top: calc(20rpx + env(safe-area-inset-top));
|
||||
|
||||
.header-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
margin-top: 96rpx;
|
||||
text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.2);
|
||||
margin-bottom: 28rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 40rpx;
|
||||
padding: 20rpx 24rpx;
|
||||
|
||||
.search-icon {
|
||||
font-size: 28rpx;
|
||||
margin-right: 12rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.search-placeholder {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
// Banner 轮播图
|
||||
.banner-swiper {
|
||||
width: 100%;
|
||||
height: 420rpx;
|
||||
|
|
@ -614,12 +602,62 @@
|
|||
height: 420rpx;
|
||||
background: linear-gradient(135deg, #FFB6C1 0%, #FFC0CB 100%);
|
||||
}
|
||||
|
||||
// 浮层(导航栏+搜索框)
|
||||
.banner-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
// 自定义导航栏
|
||||
.custom-navbar {
|
||||
.navbar-content {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.header-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索框
|
||||
.search-section {
|
||||
padding: 16rpx 24rpx;
|
||||
|
||||
.search-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 40rpx;
|
||||
padding: 20rpx 24rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
|
||||
|
||||
.search-icon {
|
||||
font-size: 28rpx;
|
||||
margin-right: 12rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.search-placeholder {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 金刚位导航
|
||||
.kingkong-section {
|
||||
padding: 20rpx;
|
||||
background-color: #fff;
|
||||
|
||||
.kingkong-grid {
|
||||
display: flex;
|
||||
|
|
@ -650,17 +688,40 @@
|
|||
// 推荐标题栏
|
||||
.section-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx;
|
||||
justify-content: center;
|
||||
padding: 5rpx 20rpx 24rpx;
|
||||
background-color: #f8f8f8;
|
||||
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
.section-title-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8rpx;
|
||||
|
||||
.section-title-main {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.section-title-highlight {
|
||||
font-size: 42rpx;
|
||||
color: #FF6A6A;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
border-radius: 0;
|
||||
font-weight: 600;
|
||||
font-style: italic;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.section-subtitle {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.section-more {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
|
|||
|
|
@ -128,8 +128,8 @@ export default {
|
|||
const api = activeTab.value === 'favoritedMe' ? getFavoritedMe : getMyFavorite
|
||||
const res = await api(pageIndex.value, pageSize)
|
||||
|
||||
if (res && res.success) {
|
||||
const newList = res.data?.list || []
|
||||
if (res && (res.success || res.code === 0)) {
|
||||
const newList = res.data?.items || []
|
||||
|
||||
if (refresh) {
|
||||
list.value = newList
|
||||
|
|
|
|||
|
|
@ -128,8 +128,8 @@ export default {
|
|||
const api = activeTab.value === 'favoritedMe' ? getFavoritedMe : getMyFavorite
|
||||
const res = await api(pageIndex.value, pageSize)
|
||||
|
||||
if (res && res.success) {
|
||||
const newList = res.data?.list || []
|
||||
if (res && (res.success || res.code === 0)) {
|
||||
const newList = res.data?.items || []
|
||||
|
||||
if (refresh) {
|
||||
list.value = newList
|
||||
|
|
|
|||
|
|
@ -128,8 +128,8 @@ export default {
|
|||
const api = activeTab.value === 'unlockedMe' ? getUnlockedMe : getMyUnlocked
|
||||
const res = await api(pageIndex.value, pageSize)
|
||||
|
||||
if (res && res.success) {
|
||||
const newList = res.data?.list || []
|
||||
if (res && (res.success || res.code === 0)) {
|
||||
const newList = res.data?.items || []
|
||||
|
||||
if (refresh) {
|
||||
list.value = newList
|
||||
|
|
|
|||
|
|
@ -128,8 +128,8 @@ export default {
|
|||
const api = activeTab.value === 'viewedMe' ? getViewedMe : getMyViewed
|
||||
const res = await api(pageIndex.value, pageSize)
|
||||
|
||||
if (res && res.success) {
|
||||
const newList = res.data?.list || []
|
||||
if (res && (res.success || res.code === 0)) {
|
||||
const newList = res.data?.items || []
|
||||
|
||||
if (refresh) {
|
||||
list.value = newList
|
||||
|
|
|
|||
|
|
@ -128,8 +128,8 @@ export default {
|
|||
const api = activeTab.value === 'unlockedMe' ? getUnlockedMe : getMyUnlocked
|
||||
const res = await api(pageIndex.value, pageSize)
|
||||
|
||||
if (res && res.success) {
|
||||
const newList = res.data?.list || []
|
||||
if (res && (res.success || res.code === 0)) {
|
||||
const newList = res.data?.items || []
|
||||
|
||||
if (refresh) {
|
||||
list.value = newList
|
||||
|
|
|
|||
|
|
@ -128,8 +128,8 @@ export default {
|
|||
const api = activeTab.value === 'viewedMe' ? getViewedMe : getMyViewed
|
||||
const res = await api(pageIndex.value, pageSize)
|
||||
|
||||
if (res && res.success) {
|
||||
const newList = res.data?.list || []
|
||||
if (res && (res.success || res.code === 0)) {
|
||||
const newList = res.data?.items || []
|
||||
|
||||
if (refresh) {
|
||||
list.value = newList
|
||||
|
|
|
|||
|
|
@ -52,6 +52,10 @@
|
|||
<view class="name-row">
|
||||
<text class="nickname">{{ userDetail.nickname }}</text>
|
||||
<text class="relationship">({{ relationshipText }})</text>
|
||||
<view class="header-tags">
|
||||
<text v-if="userDetail.isRealName" class="tag tag-realname">已实名</text>
|
||||
<text v-if="userDetail.isMember" class="tag tag-member">会员</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="xiangqin-no">相亲ID: {{ userDetail.xiangQinNo }}</text>
|
||||
</view>
|
||||
|
|
@ -64,10 +68,6 @@
|
|||
<text class="gender-year" :class="{ male: userDetail.childGender === 1 }">
|
||||
{{ genderText }} · {{ userDetail.birthYear }}年
|
||||
</text>
|
||||
<view class="tags-row">
|
||||
<text v-if="userDetail.isRealName" class="tag tag-realname">已实名</text>
|
||||
<text v-if="userDetail.isMember" class="tag tag-member">会员</text>
|
||||
</view>
|
||||
<button class="share-btn" @click="handleShare">分享</button>
|
||||
</view>
|
||||
|
||||
|
|
@ -259,7 +259,7 @@
|
|||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useUserStore } from '@/store/user.js'
|
||||
import { getUserDetail } from '@/api/user.js'
|
||||
import { favorite, unlock, recordView } from '@/api/interact.js'
|
||||
import { favorite, unlock } from '@/api/interact.js'
|
||||
import { getFullImageUrl } from '@/utils/image.js'
|
||||
import Loading from '@/components/Loading/index.vue'
|
||||
import Popup from '@/components/Popup/index.vue'
|
||||
|
|
@ -459,14 +459,14 @@ const loadUserDetail = async () => {
|
|||
loading.value = true
|
||||
try {
|
||||
const res = await getUserDetail(userId.value)
|
||||
if (res && res.code === 0 && res.data) {
|
||||
// 后端返回 code: 0 表示成功
|
||||
if (res && (res.success || res.code === 0) && res.data) {
|
||||
userDetail.value = res.data
|
||||
isFavorited.value = res.data.isFavorited || false
|
||||
isUnlocked.value = res.data.isUnlocked || false
|
||||
remainingUnlockQuota.value = res.data.remainingUnlockQuota || 0
|
||||
|
||||
// 记录浏览
|
||||
await recordView(userId.value)
|
||||
// 已登录用户记录浏览(后端已处理,这里不需要再调用)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载用户详情失败:', error)
|
||||
|
|
@ -492,9 +492,24 @@ const previewPhoto = (index) => {
|
|||
|
||||
// 收藏处理
|
||||
const handleFavorite = async () => {
|
||||
// 检查是否登录
|
||||
if (!userStore.isLoggedIn) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '请先登录后再收藏',
|
||||
confirmText: '去登录',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.navigateTo({ url: '/pages/login/index' })
|
||||
}
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await favorite(userId.value)
|
||||
if (res && res.code === 0) {
|
||||
if (res && (res.success || res.code === 0)) {
|
||||
isFavorited.value = res.data?.isFavorited ?? !isFavorited.value
|
||||
uni.showToast({
|
||||
title: isFavorited.value ? '收藏成功' : '已取消收藏',
|
||||
|
|
@ -513,6 +528,21 @@ const handleShare = () => {
|
|||
|
||||
// 联系处理
|
||||
const handleContact = () => {
|
||||
// 检查是否登录
|
||||
if (!userStore.isLoggedIn) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '请先登录后再联系对方',
|
||||
confirmText: '去登录',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.navigateTo({ url: '/pages/login/index' })
|
||||
}
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (!userStore.isProfileCompleted) {
|
||||
showProfilePopup.value = true
|
||||
return
|
||||
|
|
@ -550,7 +580,7 @@ const handleConfirmUnlock = async () => {
|
|||
|
||||
try {
|
||||
const res = await unlock(userId.value)
|
||||
if (res && res.code === 0) {
|
||||
if (res && (res.success || res.code === 0)) {
|
||||
isUnlocked.value = true
|
||||
showUnlockPopup.value = false
|
||||
remainingUnlockQuota.value = Math.max(0, remainingUnlockQuota.value - 1)
|
||||
|
|
@ -569,6 +599,21 @@ const handleGoMember = () => {
|
|||
|
||||
// 拨打电话
|
||||
const handleCall = () => {
|
||||
// 检查是否登录
|
||||
if (!userStore.isLoggedIn) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '请先登录后再联系对方',
|
||||
confirmText: '去登录',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.navigateTo({ url: '/pages/login/index' })
|
||||
}
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (!isUnlocked.value) {
|
||||
showUnlockPopup.value = true
|
||||
return
|
||||
|
|
@ -641,16 +686,16 @@ defineExpose({
|
|||
padding: 0 24rpx;
|
||||
|
||||
.navbar-back {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.back-icon {
|
||||
font-size: 48rpx;
|
||||
font-size: 64rpx;
|
||||
color: #fff;
|
||||
font-weight: 300;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -702,6 +747,8 @@ defineExpose({
|
|||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8rpx;
|
||||
flex-wrap: wrap;
|
||||
gap: 8rpx;
|
||||
|
||||
.nickname {
|
||||
font-size: 32rpx;
|
||||
|
|
@ -712,7 +759,28 @@ defineExpose({
|
|||
.relationship {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.header-tags {
|
||||
display: flex;
|
||||
gap: 8rpx;
|
||||
margin-left: 8rpx;
|
||||
|
||||
.tag {
|
||||
font-size: 20rpx;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 16rpx;
|
||||
|
||||
&.tag-realname {
|
||||
background: #e8f5e9;
|
||||
color: #4caf50;
|
||||
}
|
||||
|
||||
&.tag-member {
|
||||
background: linear-gradient(135deg, #fff3e0 0%, #ffe0b2 100%);
|
||||
color: #ff9800;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -758,9 +826,8 @@ defineExpose({
|
|||
.gender-year-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 24rpx;
|
||||
flex-wrap: wrap;
|
||||
gap: 16rpx;
|
||||
|
||||
.gender-year {
|
||||
font-size: 40rpx;
|
||||
|
|
@ -772,29 +839,7 @@ defineExpose({
|
|||
}
|
||||
}
|
||||
|
||||
.tags-row {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
|
||||
.tag {
|
||||
font-size: 22rpx;
|
||||
padding: 6rpx 16rpx;
|
||||
border-radius: 20rpx;
|
||||
|
||||
&.tag-realname {
|
||||
background: #e8f5e9;
|
||||
color: #4caf50;
|
||||
}
|
||||
|
||||
&.tag-member {
|
||||
background: linear-gradient(135deg, #fff3e0 0%, #ffe0b2 100%);
|
||||
color: #ff9800;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.share-btn {
|
||||
margin-left: auto;
|
||||
padding: 12rpx 32rpx;
|
||||
font-size: 26rpx;
|
||||
color: #ff6b6b;
|
||||
|
|
@ -1034,7 +1079,7 @@ defineExpose({
|
|||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
font-size: 30rpx;
|
||||
border-radius: 44rpx;
|
||||
border-radius: 16rpx;
|
||||
border: none;
|
||||
|
||||
&::after {
|
||||
|
|
@ -1043,23 +1088,21 @@ defineExpose({
|
|||
}
|
||||
|
||||
.btn-greet {
|
||||
background: linear-gradient(135deg, #ff8a8a 0%, #ff6b6b 100%);
|
||||
background: #4cd964;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-favorite {
|
||||
background: #fff;
|
||||
color: #666;
|
||||
border: 2rpx solid #ddd;
|
||||
background: #f5d742;
|
||||
color: #fff;
|
||||
|
||||
&.active {
|
||||
color: #ff6b6b;
|
||||
border-color: #ff6b6b;
|
||||
background: #e5c732;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-call {
|
||||
background: linear-gradient(135deg, #ffb5b5 0%, #ff9a9a 100%);
|
||||
background: #ff9a9a;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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">实名认证</text>
|
||||
<view class="navbar-placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 已认证状态 (Requirements 12.4) -->
|
||||
<view v-if="isVerified" class="verified-section">
|
||||
<view v-if="isVerified" class="verified-section" :style="{ paddingTop: (statusBarHeight + 44) + 'px' }">
|
||||
<view class="verified-icon">✓</view>
|
||||
<view class="verified-title">实名认证已完成</view>
|
||||
<view class="verified-info">
|
||||
|
|
@ -25,27 +41,37 @@
|
|||
|
||||
<!-- 未认证状态 - 分步骤显示 -->
|
||||
<view v-else class="unverified-section">
|
||||
<!-- 步骤1: 支付页面 (Requirements 12.1) -->
|
||||
<view v-if="currentStep === 1" class="step-payment">
|
||||
<view class="step-header">
|
||||
<!-- 固定的步骤指示器 -->
|
||||
<view class="step-header-fixed" :style="{ top: (statusBarHeight + 44) + 'px' }">
|
||||
<view class="step-indicator">
|
||||
<view class="step-item active">
|
||||
<view class="step-num">1</view>
|
||||
<view class="step-item" :class="{ active: currentStep === 1, completed: currentStep > 1 }" @click="currentStep = 1">
|
||||
<view class="step-num">{{ currentStep > 1 ? '✓' : '1' }}</view>
|
||||
<text class="step-text">支付费用</text>
|
||||
</view>
|
||||
<view class="step-line"></view>
|
||||
<view class="step-item">
|
||||
<view class="step-num">2</view>
|
||||
<view class="step-line" :class="{ completed: currentStep > 1 }"></view>
|
||||
<view class="step-item" :class="{ active: currentStep === 2, completed: currentStep > 2 }" @click="currentStep = 2">
|
||||
<view class="step-num">{{ currentStep > 2 ? '✓' : '2' }}</view>
|
||||
<text class="step-text">上传证件</text>
|
||||
</view>
|
||||
<view class="step-line"></view>
|
||||
<view class="step-item">
|
||||
<view class="step-line" :class="{ completed: currentStep > 2 }"></view>
|
||||
<view class="step-item" :class="{ active: currentStep === 3 }" @click="currentStep = 3">
|
||||
<view class="step-num">3</view>
|
||||
<text class="step-text">认证完成</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 可滚动内容区域 -->
|
||||
<scroll-view
|
||||
class="content-scroll"
|
||||
scroll-y
|
||||
:style="{
|
||||
top: (statusBarHeight + 44 + 100) + 'px',
|
||||
height: 'calc(100vh - ' + (statusBarHeight + 44 + 100 + 120) + 'px)'
|
||||
}"
|
||||
>
|
||||
<!-- 步骤1: 支付页面 (Requirements 12.1) -->
|
||||
<view v-if="currentStep === 1" class="step-payment">
|
||||
<view class="payment-content">
|
||||
<view class="payment-icon">🛡️</view>
|
||||
<view class="payment-title">实名认证</view>
|
||||
|
|
@ -74,40 +100,10 @@
|
|||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="bottom-action">
|
||||
<button
|
||||
class="btn-pay"
|
||||
:disabled="paying"
|
||||
@click="handlePayment"
|
||||
>
|
||||
<text v-if="paying">支付中...</text>
|
||||
<text v-else>立即支付 ¥{{ verificationFee }}</text>
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 步骤2: 身份证上传页面 (Requirements 12.2, 12.3) -->
|
||||
<view v-if="currentStep === 2" class="step-upload">
|
||||
<view class="step-header">
|
||||
<view class="step-indicator">
|
||||
<view class="step-item completed">
|
||||
<view class="step-num">✓</view>
|
||||
<text class="step-text">支付费用</text>
|
||||
</view>
|
||||
<view class="step-line completed"></view>
|
||||
<view class="step-item active">
|
||||
<view class="step-num">2</view>
|
||||
<text class="step-text">上传证件</text>
|
||||
</view>
|
||||
<view class="step-line"></view>
|
||||
<view class="step-item">
|
||||
<view class="step-num">3</view>
|
||||
<text class="step-text">认证完成</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="upload-content">
|
||||
<view class="upload-title">请上传身份证照片</view>
|
||||
<view class="upload-desc">请确保照片清晰、完整,信息可辨认</view>
|
||||
|
|
@ -161,40 +157,10 @@
|
|||
<view class="tips-item">• 您的信息将被严格保密</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="bottom-action">
|
||||
<button
|
||||
class="btn-submit"
|
||||
:disabled="!canSubmit || submitting"
|
||||
@click="handleSubmitVerification"
|
||||
>
|
||||
<text v-if="submitting">提交中...</text>
|
||||
<text v-else>提交认证</text>
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 步骤3: 认证结果页面 (Requirements 12.4) -->
|
||||
<view v-if="currentStep === 3" class="step-result">
|
||||
<view class="step-header">
|
||||
<view class="step-indicator">
|
||||
<view class="step-item completed">
|
||||
<view class="step-num">✓</view>
|
||||
<text class="step-text">支付费用</text>
|
||||
</view>
|
||||
<view class="step-line completed"></view>
|
||||
<view class="step-item completed">
|
||||
<view class="step-num">✓</view>
|
||||
<text class="step-text">上传证件</text>
|
||||
</view>
|
||||
<view class="step-line completed"></view>
|
||||
<view class="step-item active">
|
||||
<view class="step-num">3</view>
|
||||
<text class="step-text">认证完成</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="result-content">
|
||||
<view class="result-icon success">✓</view>
|
||||
<view class="result-title">实名认证成功</view>
|
||||
|
|
@ -211,15 +177,41 @@
|
|||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 底部操作按钮 -->
|
||||
<view class="bottom-action">
|
||||
<button class="btn-done" @click="handleDone">
|
||||
<button
|
||||
v-if="currentStep === 1"
|
||||
class="btn-pay"
|
||||
:disabled="paying"
|
||||
@click="handlePayment"
|
||||
>
|
||||
<text v-if="paying">支付中...</text>
|
||||
<text v-else>立即支付 ¥{{ verificationFee }}</text>
|
||||
</button>
|
||||
|
||||
<button
|
||||
v-if="currentStep === 2"
|
||||
class="btn-submit"
|
||||
:disabled="!canSubmit || submitting"
|
||||
@click="handleSubmitVerification"
|
||||
>
|
||||
<text v-if="submitting">提交中...</text>
|
||||
<text v-else>提交认证</text>
|
||||
</button>
|
||||
|
||||
<button
|
||||
v-if="currentStep === 3"
|
||||
class="btn-done"
|
||||
@click="handleDone"
|
||||
>
|
||||
完成
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
@ -227,7 +219,7 @@ import { ref, computed, onMounted } from 'vue'
|
|||
import { useUserStore } from '@/store/user.js'
|
||||
import { createOrder } from '@/api/order.js'
|
||||
import { maskName, maskIdNumber } from '@/utils/format.js'
|
||||
import { get, post } from '@/api/request.js'
|
||||
import { get } from '@/api/request.js'
|
||||
import { getToken } from '@/utils/storage.js'
|
||||
import Loading from '@/components/Loading/index.vue'
|
||||
|
||||
|
|
@ -242,6 +234,9 @@ export default {
|
|||
setup() {
|
||||
const userStore = useUserStore()
|
||||
|
||||
// 状态栏高度
|
||||
const statusBarHeight = ref(20)
|
||||
|
||||
// 页面状态
|
||||
const pageLoading = ref(true)
|
||||
const paying = ref(false)
|
||||
|
|
@ -258,6 +253,20 @@ export default {
|
|||
// 认证结果
|
||||
const verificationResult = ref(null)
|
||||
|
||||
// 获取系统信息
|
||||
const getSystemInfo = () => {
|
||||
uni.getSystemInfo({
|
||||
success: (res) => {
|
||||
statusBarHeight.value = res.statusBarHeight || 20
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 返回上一页
|
||||
const handleBack = () => {
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
// 计算属性
|
||||
const isVerified = computed(() => userStore.isRealName)
|
||||
|
||||
|
|
@ -308,6 +317,7 @@ export default {
|
|||
const initPage = async () => {
|
||||
pageLoading.value = true
|
||||
try {
|
||||
getSystemInfo()
|
||||
userStore.restoreFromStorage()
|
||||
await getRealNameStatus()
|
||||
} catch (error) {
|
||||
|
|
@ -552,6 +562,7 @@ export default {
|
|||
})
|
||||
|
||||
return {
|
||||
statusBarHeight,
|
||||
pageLoading,
|
||||
paying,
|
||||
submitting,
|
||||
|
|
@ -563,6 +574,7 @@ export default {
|
|||
canSubmit,
|
||||
maskedName,
|
||||
maskedIdNumber,
|
||||
handleBack,
|
||||
handlePayment,
|
||||
chooseIdCardFront,
|
||||
chooseIdCardBack,
|
||||
|
|
@ -580,13 +592,71 @@ export default {
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.realname-page {
|
||||
min-height: 100vh;
|
||||
height: 100vh;
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
// 顶部背景图
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 已认证状态
|
||||
.verified-section {
|
||||
padding: 60rpx 30rpx;
|
||||
padding-top: 60rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
|
@ -664,19 +734,44 @@ export default {
|
|||
|
||||
// 未认证状态
|
||||
.unverified-section {
|
||||
min-height: 100vh;
|
||||
padding-bottom: 160rpx;
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// 步骤指示器
|
||||
.step-header {
|
||||
background: #fff;
|
||||
padding: 40rpx 30rpx;
|
||||
// 可滚动内容区域
|
||||
.content-scroll {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
// 步骤内容区域
|
||||
.step-payment,
|
||||
.step-upload,
|
||||
.step-result {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
// 固定的步骤指示器
|
||||
.step-header-fixed {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 99;
|
||||
padding: 20rpx 30rpx;
|
||||
background: transparent;
|
||||
|
||||
.step-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #fff;
|
||||
border-radius: 20rpx;
|
||||
padding: 24rpx 20rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
|
||||
|
||||
.step-item {
|
||||
display: flex;
|
||||
|
|
@ -739,6 +834,69 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
// 步骤指示器(保留旧样式兼容)
|
||||
.step-header {
|
||||
padding: 30rpx;
|
||||
padding-top: 20rpx;
|
||||
|
||||
.step-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #fff;
|
||||
border-radius: 20rpx;
|
||||
padding: 24rpx 20rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
|
||||
|
||||
.step-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.step-num {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
border-radius: 50%;
|
||||
background: #e0e0e0;
|
||||
color: #999;
|
||||
font-size: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.step-text {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
&.active {
|
||||
.step-num {
|
||||
background: linear-gradient(135deg, #52c41a 0%, #389e0d 100%);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.step-text {
|
||||
color: #52c41a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.step-line {
|
||||
width: 80rpx;
|
||||
height: 4rpx;
|
||||
background: #e0e0e0;
|
||||
margin: 0 16rpx;
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
&.completed {
|
||||
background: linear-gradient(135deg, #52c41a 0%, #389e0d 100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 步骤1: 支付页面
|
||||
.step-payment {
|
||||
.payment-content {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
369
miniapp/pages/search/result.vue
Normal file
369
miniapp/pages/search/result.vue
Normal file
|
|
@ -0,0 +1,369 @@
|
|||
<template>
|
||||
<view class="result-page">
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="navbar-content">
|
||||
<view class="navbar-back" @click="handleBack">
|
||||
<text class="back-icon">‹</text>
|
||||
</view>
|
||||
<text class="navbar-title">搜索结果</text>
|
||||
<view class="navbar-placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<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" />
|
||||
|
||||
<!-- 空状态 -->
|
||||
<Empty v-else-if="!loading && results.length === 0" text="暂无符合条件的用户" />
|
||||
|
||||
<!-- 结果列表 -->
|
||||
<view v-else class="results-list">
|
||||
<view
|
||||
v-for="user in results"
|
||||
:key="user.userId"
|
||||
class="result-item-wrapper"
|
||||
>
|
||||
<!-- 非会员模糊遮罩 -->
|
||||
<view v-if="!isMember" class="blur-mask">
|
||||
<view class="blur-content">
|
||||
<text class="blur-text">开通会员查看完整信息</text>
|
||||
<button class="btn-member" @click="goToMember">立即开通</button>
|
||||
</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>
|
||||
</view>
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<Loading v-if="results.length > 0" type="more" :loading="loadingMore" :noMore="!hasMore" />
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
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'
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
// 状态栏高度
|
||||
const statusBarHeight = ref(20)
|
||||
|
||||
// 会员状态
|
||||
const isMember = computed(() => userStore.isMember)
|
||||
|
||||
// 搜索参数
|
||||
const searchParams = ref({})
|
||||
|
||||
// 搜索状态
|
||||
const loading = ref(true)
|
||||
const loadingMore = ref(false)
|
||||
const results = ref([])
|
||||
const totalCount = ref(0)
|
||||
const pageIndex = ref(1)
|
||||
const pageSize = ref(10)
|
||||
const hasMore = ref(true)
|
||||
|
||||
// 获取系统信息
|
||||
const getSystemInfo = () => {
|
||||
uni.getSystemInfo({
|
||||
success: (res) => {
|
||||
statusBarHeight.value = res.statusBarHeight || 20
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 返回
|
||||
const handleBack = () => {
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
// 构建API参数
|
||||
const buildApiParams = () => {
|
||||
const params = {
|
||||
pageIndex: pageIndex.value,
|
||||
pageSize: pageSize.value
|
||||
}
|
||||
|
||||
const sp = searchParams.value
|
||||
|
||||
if (sp.ageMin > 0) params.ageMin = sp.ageMin
|
||||
if (sp.ageMax > 0) params.ageMax = sp.ageMax
|
||||
if (sp.heightMin > 0) params.heightMin = sp.heightMin
|
||||
if (sp.heightMax > 0) params.heightMax = sp.heightMax
|
||||
if (sp.education && sp.education.length > 0) params.education = sp.education
|
||||
if (sp.cities && sp.cities.length > 0) params.cities = sp.cities
|
||||
if (sp.monthlyIncome > 0) params.monthlyIncomeMin = sp.monthlyIncome
|
||||
if (sp.houseStatus > 0) params.houseStatus = [sp.houseStatus]
|
||||
if (sp.carStatus > 0) params.carStatus = [sp.carStatus]
|
||||
if (sp.marriageStatus > 0) params.marriageStatus = [sp.marriageStatus]
|
||||
|
||||
return params
|
||||
}
|
||||
|
||||
// 搜索
|
||||
const doSearch = async (isLoadMore = false) => {
|
||||
if (isLoadMore) {
|
||||
if (loadingMore.value || !hasMore.value) return
|
||||
loadingMore.value = true
|
||||
pageIndex.value++
|
||||
} else {
|
||||
loading.value = true
|
||||
pageIndex.value = 1
|
||||
results.value = []
|
||||
}
|
||||
|
||||
try {
|
||||
const params = buildApiParams()
|
||||
const res = await search(params)
|
||||
|
||||
if (res && res.code === 0 && res.data) {
|
||||
const items = res.data.items || []
|
||||
totalCount.value = res.data.total || 0
|
||||
|
||||
if (isLoadMore) {
|
||||
results.value = [...results.value, ...items]
|
||||
} else {
|
||||
results.value = items
|
||||
}
|
||||
|
||||
hasMore.value = results.value.length < totalCount.value
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('搜索失败:', error)
|
||||
uni.showToast({ title: '搜索失败', icon: 'none' })
|
||||
if (isLoadMore) pageIndex.value--
|
||||
} finally {
|
||||
loading.value = false
|
||||
loadingMore.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 加载更多
|
||||
const loadMore = () => {
|
||||
doSearch(true)
|
||||
}
|
||||
|
||||
// 点击用户卡片
|
||||
const handleCardClick = (userId) => {
|
||||
if (!isMember.value) {
|
||||
uni.showToast({ title: '开通会员查看完整信息', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
uni.navigateTo({
|
||||
url: `/pages/profile/detail?userId=${userId}`
|
||||
})
|
||||
}
|
||||
|
||||
// 联系用户
|
||||
const handleContact = (userId) => {
|
||||
if (!isMember.value) {
|
||||
uni.showToast({ title: '开通会员联系TA', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否完善资料
|
||||
if (!userStore.isProfileCompleted) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '请先完善您的资料,才能联系对方',
|
||||
confirmText: '去完善',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.navigateTo({ url: '/pages/profile/edit' })
|
||||
}
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
uni.navigateTo({
|
||||
url: `/pages/chat/index?targetUserId=${userId}`
|
||||
})
|
||||
}
|
||||
|
||||
// 跳转会员页
|
||||
const goToMember = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/member/index'
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getSystemInfo()
|
||||
|
||||
// 获取页面参数
|
||||
const pages = getCurrentPages()
|
||||
const currentPage = pages[pages.length - 1]
|
||||
const options = currentPage?.options || {}
|
||||
|
||||
if (options.params) {
|
||||
try {
|
||||
searchParams.value = JSON.parse(decodeURIComponent(options.params))
|
||||
} catch (e) {
|
||||
console.error('解析搜索参数失败:', e)
|
||||
}
|
||||
}
|
||||
|
||||
// 执行搜索
|
||||
doSearch()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.result-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f6fa;
|
||||
}
|
||||
|
||||
// 自定义导航栏
|
||||
.custom-navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
background: linear-gradient(135deg, #ffb5b5 0%, #ff9a9a 100%);
|
||||
|
||||
.navbar-content {
|
||||
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: 64rpx;
|
||||
color: #fff;
|
||||
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;
|
||||
}
|
||||
|
||||
// 结果统计
|
||||
.result-header {
|
||||
padding: 24rpx 32rpx;
|
||||
|
||||
.result-count {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
// 结果列表
|
||||
.results-list {
|
||||
padding: 0 24rpx;
|
||||
}
|
||||
|
||||
.result-item-wrapper {
|
||||
position: relative;
|
||||
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;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 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;
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 749 B After Width: | Height: | Size: 731 B |
BIN
miniapp/static/ic_empty.png
Normal file
BIN
miniapp/static/ic_empty.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
147
miniapp/utils/cityData.js
Normal file
147
miniapp/utils/cityData.js
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
// 中国省市数据
|
||||
export const provinceData = [
|
||||
{
|
||||
name: '北京市',
|
||||
cities: ['北京']
|
||||
},
|
||||
{
|
||||
name: '上海市',
|
||||
cities: ['上海']
|
||||
},
|
||||
{
|
||||
name: '天津市',
|
||||
cities: ['天津']
|
||||
},
|
||||
{
|
||||
name: '重庆市',
|
||||
cities: ['重庆']
|
||||
},
|
||||
{
|
||||
name: '广东省',
|
||||
cities: ['广州', '深圳', '东莞', '佛山', '珠海', '惠州', '中山', '汕头', '江门', '湛江', '肇庆', '茂名', '揭阳', '梅州', '清远', '阳江', '韶关', '河源', '云浮', '汕尾', '潮州']
|
||||
},
|
||||
{
|
||||
name: '江苏省',
|
||||
cities: ['南京', '苏州', '无锡', '常州', '南通', '徐州', '扬州', '盐城', '泰州', '镇江', '淮安', '连云港', '宿迁']
|
||||
},
|
||||
{
|
||||
name: '浙江省',
|
||||
cities: ['杭州', '宁波', '温州', '绍兴', '嘉兴', '金华', '台州', '湖州', '丽水', '衢州', '舟山']
|
||||
},
|
||||
{
|
||||
name: '山东省',
|
||||
cities: ['济南', '青岛', '烟台', '潍坊', '临沂', '淄博', '济宁', '泰安', '威海', '德州', '聊城', '菏泽', '滨州', '枣庄', '日照', '东营']
|
||||
},
|
||||
{
|
||||
name: '河南省',
|
||||
cities: ['郑州', '洛阳', '南阳', '许昌', '周口', '新乡', '商丘', '信阳', '平顶山', '驻马店', '安阳', '焦作', '开封', '濮阳', '漯河', '三门峡', '鹤壁']
|
||||
},
|
||||
{
|
||||
name: '四川省',
|
||||
cities: ['成都', '绵阳', '德阳', '宜宾', '南充', '泸州', '达州', '乐山', '内江', '自贡', '眉山', '广安', '遂宁', '资阳', '攀枝花', '广元', '雅安', '巴中']
|
||||
},
|
||||
{
|
||||
name: '湖北省',
|
||||
cities: ['武汉', '宜昌', '襄阳', '荆州', '黄冈', '十堰', '孝感', '荆门', '咸宁', '鄂州', '黄石', '随州']
|
||||
},
|
||||
{
|
||||
name: '湖南省',
|
||||
cities: ['长沙', '株洲', '湘潭', '衡阳', '岳阳', '常德', '郴州', '邵阳', '怀化', '永州', '益阳', '娄底', '张家界']
|
||||
},
|
||||
{
|
||||
name: '福建省',
|
||||
cities: ['福州', '厦门', '泉州', '漳州', '莆田', '宁德', '三明', '南平', '龙岩']
|
||||
},
|
||||
{
|
||||
name: '安徽省',
|
||||
cities: ['合肥', '芜湖', '蚌埠', '阜阳', '淮南', '安庆', '马鞍山', '宿州', '滁州', '六安', '淮北', '铜陵', '亳州', '宣城', '池州', '黄山']
|
||||
},
|
||||
{
|
||||
name: '河北省',
|
||||
cities: ['石家庄', '唐山', '保定', '邯郸', '廊坊', '沧州', '邢台', '衡水', '秦皇岛', '张家口', '承德']
|
||||
},
|
||||
{
|
||||
name: '陕西省',
|
||||
cities: ['西安', '咸阳', '宝鸡', '渭南', '汉中', '榆林', '延安', '安康', '商洛', '铜川']
|
||||
},
|
||||
{
|
||||
name: '辽宁省',
|
||||
cities: ['沈阳', '大连', '鞍山', '锦州', '抚顺', '营口', '盘锦', '朝阳', '丹东', '辽阳', '本溪', '葫芦岛', '铁岭', '阜新']
|
||||
},
|
||||
{
|
||||
name: '江西省',
|
||||
cities: ['南昌', '赣州', '九江', '宜春', '吉安', '上饶', '抚州', '景德镇', '萍乡', '新余', '鹰潭']
|
||||
},
|
||||
{
|
||||
name: '山西省',
|
||||
cities: ['太原', '大同', '运城', '临汾', '晋中', '长治', '晋城', '忻州', '吕梁', '朔州', '阳泉']
|
||||
},
|
||||
{
|
||||
name: '云南省',
|
||||
cities: ['昆明', '曲靖', '大理', '玉溪', '红河', '楚雄', '文山', '昭通', '普洱', '保山', '丽江', '临沧']
|
||||
},
|
||||
{
|
||||
name: '广西',
|
||||
cities: ['南宁', '柳州', '桂林', '玉林', '梧州', '北海', '贵港', '钦州', '百色', '河池', '来宾', '崇左', '防城港', '贺州']
|
||||
},
|
||||
{
|
||||
name: '贵州省',
|
||||
cities: ['贵阳', '遵义', '毕节', '黔南', '黔东南', '六盘水', '铜仁', '安顺', '黔西南']
|
||||
},
|
||||
{
|
||||
name: '黑龙江省',
|
||||
cities: ['哈尔滨', '大庆', '齐齐哈尔', '牡丹江', '绥化', '佳木斯', '鸡西', '双鸭山', '鹤岗', '黑河', '伊春', '七台河']
|
||||
},
|
||||
{
|
||||
name: '吉林省',
|
||||
cities: ['长春', '吉林', '四平', '延边', '松原', '白城', '通化', '白山', '辽源']
|
||||
},
|
||||
{
|
||||
name: '内蒙古',
|
||||
cities: ['呼和浩特', '包头', '鄂尔多斯', '赤峰', '通辽', '呼伦贝尔', '巴彦淖尔', '乌兰察布', '锡林郭勒', '乌海', '兴安盟', '阿拉善盟']
|
||||
},
|
||||
{
|
||||
name: '新疆',
|
||||
cities: ['乌鲁木齐', '昌吉', '巴音郭楞', '伊犁', '阿克苏', '喀什', '哈密', '克拉玛依', '博尔塔拉', '吐鲁番', '和田', '石河子', '塔城', '阿勒泰']
|
||||
},
|
||||
{
|
||||
name: '甘肃省',
|
||||
cities: ['兰州', '天水', '白银', '庆阳', '平凉', '酒泉', '张掖', '武威', '定西', '金昌', '陇南', '临夏', '嘉峪关', '甘南']
|
||||
},
|
||||
{
|
||||
name: '海南省',
|
||||
cities: ['海口', '三亚', '儋州', '琼海', '文昌', '万宁', '东方', '五指山']
|
||||
},
|
||||
{
|
||||
name: '宁夏',
|
||||
cities: ['银川', '吴忠', '石嘴山', '中卫', '固原']
|
||||
},
|
||||
{
|
||||
name: '青海省',
|
||||
cities: ['西宁', '海东', '海西', '海北', '海南', '黄南', '果洛', '玉树']
|
||||
},
|
||||
{
|
||||
name: '西藏',
|
||||
cities: ['拉萨', '日喀则', '昌都', '林芝', '山南', '那曲', '阿里']
|
||||
},
|
||||
{
|
||||
name: '香港',
|
||||
cities: ['香港']
|
||||
},
|
||||
{
|
||||
name: '澳门',
|
||||
cities: ['澳门']
|
||||
},
|
||||
{
|
||||
name: '台湾',
|
||||
cities: ['台北', '高雄', '台中', '台南', '新北', '桃园']
|
||||
}
|
||||
]
|
||||
|
||||
// 热门城市
|
||||
export const hotCities = [
|
||||
'北京', '上海', '广州', '深圳',
|
||||
'杭州', '成都', '武汉', '南京',
|
||||
'苏州', '西安', '重庆', '天津',
|
||||
'长沙', '郑州', '青岛', '厦门'
|
||||
]
|
||||
|
|
@ -3,8 +3,8 @@
|
|||
* 将相对路径转换为完整的后端URL
|
||||
*/
|
||||
|
||||
// 后端服务器基础地址(图片资源 - 使用AdminApi,因为图片存储在AdminApi的wwwroot下)
|
||||
const IMAGE_BASE_URL = 'http://localhost:5000'
|
||||
// 后端服务器基础地址(图片资源 - 使用AppApi,因为图片存储在AppApi的wwwroot下)
|
||||
const IMAGE_BASE_URL = 'http://localhost:5001'
|
||||
|
||||
/**
|
||||
* 获取完整的图片URL
|
||||
|
|
|
|||
|
|
@ -98,7 +98,17 @@ public class AdminUploadController : ControllerBase
|
|||
/// </summary>
|
||||
private async Task<string> SaveToLocalAsync(IFormFile file, string fileName, string folder)
|
||||
{
|
||||
var uploadPath = Path.Combine(_environment.WebRootPath ?? "wwwroot", "uploads", folder);
|
||||
// 使用 AppApi 的 wwwroot 目录作为共享存储
|
||||
var appApiBasePath = Path.GetFullPath(Path.Combine(_environment.ContentRootPath, "..", "XiangYi.AppApi", "wwwroot"));
|
||||
var uploadPath = Path.Combine(appApiBasePath, "uploads", folder);
|
||||
|
||||
// 如果 AppApi 路径不存在,回退到当前项目的 wwwroot
|
||||
if (!Directory.Exists(appApiBasePath))
|
||||
{
|
||||
_logger.LogWarning("AppApi wwwroot 目录不存在,使用本地存储: {Path}", appApiBasePath);
|
||||
uploadPath = Path.Combine(_environment.WebRootPath ?? "wwwroot", "uploads", folder);
|
||||
}
|
||||
|
||||
if (!Directory.Exists(uploadPath))
|
||||
{
|
||||
Directory.CreateDirectory(uploadPath);
|
||||
|
|
@ -108,6 +118,8 @@ public class AdminUploadController : ControllerBase
|
|||
using var stream = new FileStream(filePath, FileMode.Create);
|
||||
await file.CopyToAsync(stream);
|
||||
|
||||
_logger.LogInformation("文件保存到: {FilePath}", filePath);
|
||||
|
||||
// 返回相对URL
|
||||
return $"/uploads/{folder}/{fileName}";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ public class UserController : ControllerBase
|
|||
/// <param name="request">用户详情请求</param>
|
||||
/// <returns>用户详情</returns>
|
||||
[HttpPost("detail")]
|
||||
[AllowAnonymous]
|
||||
public async Task<ApiResponse<ProfileResponse>> GetUserDetail([FromBody] UserDetailRequest request)
|
||||
{
|
||||
if (request.UserId <= 0)
|
||||
|
|
@ -70,8 +71,11 @@ public class UserController : ControllerBase
|
|||
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
// 记录浏览
|
||||
// 已登录用户记录浏览(排除查看自己)
|
||||
if (currentUserId > 0 && currentUserId != request.UserId)
|
||||
{
|
||||
await _interactService.RecordViewAsync(currentUserId, request.UserId);
|
||||
}
|
||||
|
||||
// 获取用户资料
|
||||
var profile = await _profileService.GetByUserIdAsync(request.UserId);
|
||||
|
|
@ -81,6 +85,14 @@ public class UserController : ControllerBase
|
|||
return ApiResponse<ProfileResponse>.Error(ErrorCodes.UserNotFound, "用户不存在");
|
||||
}
|
||||
|
||||
// 已登录用户查询互动状态(排除查看自己)
|
||||
if (currentUserId > 0 && currentUserId != request.UserId)
|
||||
{
|
||||
profile.IsFavorited = await _interactService.IsFavoritedAsync(currentUserId, request.UserId);
|
||||
profile.IsUnlocked = await _interactService.IsUnlockedAsync(currentUserId, request.UserId);
|
||||
profile.RemainingUnlockQuota = await _interactService.GetRemainingUnlockQuotaAsync(currentUserId);
|
||||
}
|
||||
|
||||
return ApiResponse<ProfileResponse>.Success(profile);
|
||||
}
|
||||
|
||||
|
|
|
|||
24
server/src/XiangYi.AppApi/Properties/launchSettings.json
Normal file
24
server/src/XiangYi.AppApi/Properties/launchSettings.json
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "http://localhost:5001",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "https://localhost:7001;http://localhost:5001",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -159,6 +159,51 @@ public class ProfileResponse
|
|||
/// 是否实名认证
|
||||
/// </summary>
|
||||
public bool IsRealName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否已收藏(当前用户对该用户)
|
||||
/// </summary>
|
||||
public bool IsFavorited { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否已解锁(当前用户对该用户)
|
||||
/// </summary>
|
||||
public bool IsUnlocked { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 剩余解锁次数
|
||||
/// </summary>
|
||||
public int RemainingUnlockQuota { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 头像URL
|
||||
/// </summary>
|
||||
public string? Avatar { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否独居
|
||||
/// </summary>
|
||||
public bool IsLivingAlone { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 父母状况
|
||||
/// </summary>
|
||||
public string? ParentStatus { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 父母是否退休
|
||||
/// </summary>
|
||||
public bool IsParentRetired { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 父母现居城市
|
||||
/// </summary>
|
||||
public string? ParentCity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 父母是否有房
|
||||
/// </summary>
|
||||
public bool ParentHasHouse { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -79,4 +79,11 @@ public interface IInteractService
|
|||
/// 检查是否已解锁
|
||||
/// </summary>
|
||||
Task<bool> IsUnlockedAsync(long userId, long targetUserId);
|
||||
|
||||
/// <summary>
|
||||
/// 获取剩余解锁次数
|
||||
/// </summary>
|
||||
/// <param name="userId">用户ID</param>
|
||||
/// <returns>剩余解锁次数</returns>
|
||||
Task<int> GetRemainingUnlockQuotaAsync(long userId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -452,7 +452,7 @@ public class AdminUserService : IAdminUserService
|
|||
Phone = MaskPhone(user.Phone),
|
||||
Gender = user.Gender,
|
||||
GenderText = GetGenderText(user.Gender),
|
||||
City = user.City,
|
||||
City = profile?.WorkCity ?? user.City,
|
||||
Status = user.Status,
|
||||
StatusText = GetStatusText(user.Status),
|
||||
IsProfileCompleted = user.IsProfileCompleted,
|
||||
|
|
|
|||
|
|
@ -477,6 +477,24 @@ public class InteractService : IInteractService
|
|||
u.UserId == userId && u.TargetUserId == targetUserId);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<int> GetRemainingUnlockQuotaAsync(long userId)
|
||||
{
|
||||
var user = await _userRepository.GetByIdAsync(userId);
|
||||
if (user == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 会员有无限次数,返回一个大数
|
||||
if (user.IsMember)
|
||||
{
|
||||
return 999;
|
||||
}
|
||||
|
||||
return user.ContactCount;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 私有方法
|
||||
|
|
|
|||
|
|
@ -180,6 +180,7 @@ public class ProfileService : IProfileService
|
|||
UserId = userId,
|
||||
Nickname = user.Nickname,
|
||||
XiangQinNo = user.XiangQinNo,
|
||||
Avatar = user.Avatar,
|
||||
Photos = photos.OrderBy(p => p.Sort).Select(p => new PhotoResponse
|
||||
{
|
||||
Id = p.Id,
|
||||
|
|
@ -200,6 +201,7 @@ public class ProfileService : IProfileService
|
|||
UserId = userId,
|
||||
Nickname = user.Nickname,
|
||||
XiangQinNo = user.XiangQinNo,
|
||||
Avatar = user.Avatar,
|
||||
Relationship = profile.Relationship,
|
||||
Surname = profile.Surname,
|
||||
ChildGender = profile.ChildGender,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user