Files
YwxAppThink/addon/wxchat/YwxMakeFriends/pages/index/index.uvue
T

623 lines
14 KiB
Plaintext

<template>
<!-- #ifdef APP -->
<scroll-view style="flex: 1;" enable-back-to-top="true">
<!-- #endif -->
<view class="page">
<view class="-status-bar"></view>
<!-- 功能模块入口(彩色区块) -->
<scroll-view class="function-modules" direction="horizontal" :show-scrollbar="false">
<view v-for="(module, index) in functionModules" :key="index" class="module-item"
:style="{ backgroundColor: module.bgColor }" @click="navigateToModule(module.path)">
<image class="module-icon" :src="module.icon" mode="aspectFit"></image>
<text class="module-name">{{ module.name }}</text>
<text class="module-desc">{{ module.desc }}</text>
</view>
</scroll-view>
<!-- 同城/附近切换栏 -->
<view class="tabsbox" style="justify-content: space-between;">
<text v-for="(tab, index) in tabs" :key="index" class="tabsbox-txt" style="flex:1"
:class="{ ' tabsbox-active-txt': activeTab === index }" @click="activeTab = index">
{{ tab }}
</text>
</view>
<list-view style="flex: 1;">
<list-item v-for="(user, index) in filteredUsers" :key="user.id" @click="viewUserProfile(user.id)">
<view class="chat-item">
<view class="chat-item-left">
<image class="avatar-square" :src="user.avatar" mode="aspectFill"></image>
</view>
<view class="chat-item-center" style="flex-direction: column; align-items: flex-start;">
<view style="flex-flow: row;">
<text class="user-name">{{ user.nickname }}</text>
<view class="user-badges">
<text v-if="user.vip" class="user-badge" style="background-color: #FFCC00;">V</text>
<text v-if="user.verified" class="user-badge" style="background-color: #09b400;">
R</text>
<text v-if="user.live" class="user-badge"
style="background-color: #FF3B30;">L</text>
</view>
</view>
<view class="user-info">
<text v-if="user.location" class="user-info-i"> {{ user.location }} </text>
<text v-if="user.age" class="user-info-i"> {{ user.age }}岁 </text>
<text v-if="user.height" class="user-info-i"> {{ user.height }}cm </text>
<text v-if="user.profession" class="user-info-i"> {{ user.profession }} </text>
</view>
<text v-if="user.signature" class="user-signature">{{ user.signature }}</text>
</view>
<view class="chat-item-right">
<button class="user-chat-btn" @click.stop="startChat(user.id)">搭讪</button>
</view>
</view>
</list-item>
</list-view>
<!-- 你可能感兴趣的人(右下角提示框) -->
<view class="interest-tip" @click="showInterestUsers">
<text class="tip-text">你可能感兴趣的人</text>
<uni-icons type="arrowright" size="16" color="#fff"></uni-icons>
</view>
</view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
</template>
<script setup lang="uts">
import { ref, reactive, computed, onMounted } from 'vue'
import { IGrid, IUser } from '@/types/index.uts'
import { Api } from "@/common/api-service.uts"
// 功能模块数据(参考第1张图彩色模块)
const functionModules = reactive<IGrid[]>([
{
name: '心动聊天',
desc: '玩游戏交朋友',
bgColor: '#FF6B6B',
icon: '/static/icons/chatroom.png',
path: '/pages/chatroom/index'
},
{
name: '免费群聊',
desc: '多人实时聊天',
bgColor: '#4CD964',
icon: '/static/icons/group.png',
path: '/pages/group-chat/index'
},
{
name: '神兽宠物',
desc: '养宠物交朋友',
bgColor: '#5AC8FA',
icon: '/static/icons/pet.png',
path: '/pages/pet-social/index'
},
{
name: '真人匹配',
desc: '真人线上奔现',
bgColor: '#FFCC00',
icon: '/static/icons/match.png',
path: '/pages/real-match/index'
},
{
name: '我的好友',
desc: '好友与请求',
bgColor: '#9B59B6',
icon: '/static/logo.png',
path: '/pages/friend/index'
}
] as IGrid[])
// 同城/附近切换标签
const tabs = ref(['同城', '附近'])
const activeTab = ref(0) // 0:同城, 1:附近
// 在线匹配数据
const matchInfo = reactive({
distance: '25.05km',
onlineCount: 473
})
// 用户卡片数据(整合三张图用户信息)
const users = reactive<IUser[]>([
{
id: 1,
nickname: '萌小依',
realname: true,
location: '南充',
age: 21,
height: 170,
profession: '研发',
signature: '我是个坚定的缘分信徒。相信在茫茫人海中...',
avatar: 'https://randomuser.me/api/portraits/women/44.jpg',
vip: false,
verified: true,
live: false
},
{
id: 2,
nickname: '董小姐',
realname: true,
location: '德阳',
age: 26,
height: 163,
profession: '',
signature: '人生苦短,三万天而已,烂事就得...',
avatar: 'https://randomuser.me/api/portraits/women/68.jpg',
vip: false,
verified: true,
live: false
},
{
id: 3,
nickname: '贝儿妹妹',
realname: true,
location: '沈阳',
age: 27,
height: 163,
profession: '',
signature: '',
avatar: 'https://randomuser.me/api/portraits/women/23.jpg',
vip: false,
verified: true,
live: true // 直播中
},
{
id: 4,
nickname: '积极的红薯',
realname: true,
location: '南充',
age: 31,
height: 167,
profession: '',
signature: '',
avatar: 'https://randomuser.me/api/portraits/men/32.jpg',
vip: true,
verified: true,
live: false
},
{
id: 5,
nickname: '萌小依',
realname: true,
location: '南充',
age: 21,
height: 170,
profession: '研发',
signature: '我是个坚定的缘分信徒。相信在茫茫人海中...',
avatar: 'https://randomuser.me/api/portraits/women/44.jpg',
vip: false,
verified: true,
live: false
},
{
id: 6,
nickname: '董小姐',
realname: true,
location: '德阳',
age: 26,
height: 163,
profession: '',
signature: '人生苦短,三万天而已,烂事就得...',
avatar: 'https://randomuser.me/api/portraits/women/68.jpg',
vip: false,
verified: true,
live: false
},
{
id: 7,
nickname: '贝儿妹妹',
realname: true,
location: '沈阳',
age: 27,
height: 163,
profession: '',
signature: '',
avatar: 'https://randomuser.me/api/portraits/women/23.jpg',
vip: false,
verified: true,
live: true // 直播中
},
{
id: 8,
nickname: '积极的红薯',
realname: true,
location: '南充',
age: 31,
height: 167,
profession: '',
signature: '',
avatar: 'https://randomuser.me/api/portraits/men/32.jpg',
vip: true,
verified: true,
live: false
},
{
id: 9,
nickname: '萌小依',
realname: true,
location: '南充',
age: 21,
height: 170,
profession: '研发',
signature: '我是个坚定的缘分信徒。相信在茫茫人海中...',
avatar: 'https://randomuser.me/api/portraits/women/44.jpg',
vip: false,
verified: true,
live: false
},
{
id: 10,
nickname: '董小姐',
realname: true,
location: '德阳',
age: 26,
height: 163,
profession: '',
signature: '人生苦短,三万天而已,烂事就得...',
avatar: 'https://randomuser.me/api/portraits/women/68.jpg',
vip: false,
verified: true,
live: false
},
{
id: 3,
nickname: '贝儿妹妹',
realname: true,
location: '沈阳',
age: 27,
height: 163,
profession: '',
signature: '',
avatar: 'https://randomuser.me/api/portraits/women/23.jpg',
vip: false,
verified: true,
live: true // 直播中
},
{
id: 4,
nickname: '积极的红薯',
realname: true,
location: '南充',
age: 31,
height: 167,
profession: '',
signature: '',
avatar: 'https://randomuser.me/api/portraits/men/32.jpg',
vip: true,
verified: true,
live: false
},
{
id: 4,
nickname: '积极的红薯',
realname: true,
location: '南充',
age: 31,
height: 167,
profession: '',
signature: '',
avatar: 'https://randomuser.me/api/portraits/men/32.jpg',
vip: true,
verified: true,
live: false
},
{
id: 4,
nickname: '积极的红薯',
realname: true,
location: '南充',
age: 31,
height: 167,
profession: '',
signature: '',
avatar: 'https://randomuser.me/api/portraits/men/32.jpg',
vip: true,
verified: true,
live: false
},
{
id: 4,
nickname: '积极的红薯',
realname: true,
location: '南充',
age: 31,
height: 167,
profession: '',
signature: '',
avatar: 'https://randomuser.me/api/portraits/men/32.jpg',
vip: true,
verified: true,
live: false
},
{
id: 4,
nickname: '积极的红薯',
realname: true,
location: '南充',
age: 31,
height: 167,
profession: '',
signature: '',
avatar: 'https://randomuser.me/api/portraits/men/32.jpg',
vip: true,
verified: true,
live: false
},
{
id: 4,
nickname: '积极的红薯',
realname: true,
location: '南充',
age: 31,
height: 167,
profession: '',
signature: '',
avatar: 'https://randomuser.me/api/portraits/men/32.jpg',
vip: true,
verified: true,
live: false
},
{
id: 4,
nickname: '积极的红薯',
realname: true,
location: '南充',
age: 31,
height: 167,
profession: '',
signature: '',
avatar: 'https://randomuser.me/api/portraits/men/32.jpg',
vip: true,
verified: true,
live: false
},
{
id: 4,
nickname: '积极的红薯',
realname: true,
location: '南充',
age: 31,
height: 167,
profession: '',
signature: '',
avatar: 'https://randomuser.me/api/portraits/men/32.jpg',
vip: true,
verified: true,
live: false
}
] as IUser[])
// 默认选中"交友"
const activeNav = ref(0)
// 计算属性:根据切换标签过滤用户(简化示例)
const filteredUsers = computed<IUser[]>(() => {
return users
})
// 将后端 user_profile 列表项映射为前端展示用的 IUser
const mapUser = (item : UTSJSONObject) : IUser => {
const city = item.getString('residecity') ?? ''
const province = item.getString('resideprovince') ?? ''
const location = city.length > 0 ? city : province
const age = item.getNumber('age') ?? 0
const height = item.getNumber('height') ?? 0
const isOnline = item.getBoolean('is_online') ?? false
return {
id: item.getNumber('uid') ?? 0,
nickname: item.getString('nickname') ?? '',
realname: false,
location: location,
age: age,
height: height,
profession: '',
signature: item.getString('bio') ?? '',
avatar: item.getString('avatar') ?? '',
vip: false,
verified: false,
live: isOnline
} as IUser
}
// 拉取附近/同城真实用户,成功则替换列表,失败保留默认假数据
const loadNearbyUsers = async () => {
try {
let data : UTSJSONObject | null = null
try {
data = await Api.user.local({})
} catch (e) {
console.warn('本地同城加载失败,尝试在线列表', e)
}
// 本地同城为空(如未设置城市)时,回退到在线活跃用户
const localList = data == null ? null : data.getJSONArray('list')
if (localList == null || localList.size() == 0) {
try {
data = await Api.user.online({})
} catch (e) {
console.warn('在线列表加载失败', e)
}
}
const list = data == null ? null : data.getJSONArray('list')
if (list != null && list.size() > 0) {
const mapped : IUser[] = []
for (let i = 0; i < list.size(); i++) {
const item = list.get(i) as UTSJSONObject
if (item != null) {
mapped.push(mapUser(item))
}
}
if (mapped.length > 0) {
users.splice(0, users.length, ...mapped)
}
}
} catch (error) {
console.warn('加载附近用户失败,使用默认数据', error)
}
}
// 方法:导航到功能模块
const navigateToModule = (path : string) => {
uni.navigateTo({
"url": path
})
}
// 方法:开始匹配
const startMatching = () => {
uni.showToast({ title: '开始匹配附近的人...', icon: 'loading' })
// 实际项目中调用匹配API
}
// 方法:发起聊天
const startChat = (userId : number) => {
uni.navigateTo({ url: `/pages/chat/chat?userId=${userId}` })
}
// 方法:查看用户资料
const viewUserProfile = (userId : number) => {
uni.navigateTo({ url: `/pages/user/profile?id=${userId}` })
}
// 方法:显示感兴趣的人
const showInterestUsers = () => {
uni.showToast({ title: '显示你可能感兴趣的人', icon: 'none' })
}
onMounted(() => {
console.log('社交交友页面加载完成')
loadNearbyUsers()
})
onLoad(() => {
loadNearbyUsers()
})
</script>
<style>
/* 功能模块区(彩色区块) */
.function-modules {
display: flex;
flex-flow: row nowrap;
}
.module-item {
display: flex;
flex-direction: column;
align-items: center;
box-shadow: 0 5rpx 15rpx rgba(0, 0, 0, 0.1);
border-radius: 20rpx;
margin: 20rpx 10rpx;
padding: 15rpx;
}
.module-icon {
width: 64rpx;
height: 64rpx;
margin-bottom: 20rpx;
}
.module-name {
font-size: 32rpx;
font-weight: bold;
margin-bottom: 10rpx;
}
.module-desc {
font-size: 24rpx;
opacity: 0.9;
}
/* 同城/附近切换栏 */
.tab-switch {
display: flex;
flex-flow: row nowrap;
justify-content: center;
align-items: center;
margin: 0 10rpx;
margin-bottom: 10rpx;
border-radius: 50rpx;
padding: 10rpx;
}
.tab-item {
flex: 1;
text-align: center;
padding: 10rpx 0;
border-radius: 40rpx;
font-size: 28rpx;
color: #666;
}
.tabactive {
background-color: #FF6B6B;
color: #fff;
font-weight: bold;
}
/* 用户列表 */
.user-name {
font-size: 28rpx;
font-weight: bold;
margin-right: 15rpx;
color: #5f5f5f;
}
.user-badges {
display: flex;
flex-flow: row nowrap;
}
.user-badge {
padding: 3rpx 9rpx;
margin: 3rpx;
border-radius: 15rpx;
font-size: 14rpx;
color: #fff;
}
.user-info {
flex-flow: row nowrap;
}
.user-info-i {
margin: 5rpx 3rpx;
padding: 3rpx 6rpx;
border-radius: 25rpx;
background: rgba(255, 255, 255, 0.3);
font-size: 18rpx;
}
.user-signature {
font-size: 16rpx;
color: #333;
overflow: hidden;
}
.user-chat-btn {
background-color: #FF6B6B;
color: #fff;
border: none;
border-radius: 50rpx;
padding: 5rpx 15rpx;
font-size: 16rpx;
}
/* 你可能感兴趣的人提示框 */
.interest-tip {
position: fixed;
right: 30rpx;
bottom: 150rpx;
background-color: #FF6B6B;
padding: 15rpx 25rpx;
border-radius: 50rpx;
display: flex;
align-items: center;
box-shadow: 0 5rpx 15rpx rgba(255, 107, 107, 0.3);
}
.tip-text {
font-size: 26rpx;
}
</style>