chore: 重写初始提交(清空历史,整理后全量提交)
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<!-- #ifdef APP -->
|
||||
<scroll-view style="flex:1">
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- #ifdef APP -->
|
||||
</scroll-view>
|
||||
<!-- #endif -->
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,613 @@
|
||||
<template>
|
||||
<scroll-view class="page" :show-scrollbar="false" style="padding-bottom: 140rpx;">
|
||||
<view class="-status-bar"></view>
|
||||
|
||||
<!-- 照片墙 -->
|
||||
<swiper class="photo-swiper" :indicator-dots="true" :circular="true" v-if="userInfo.albumThumbs.length > 0">
|
||||
<swiper-item v-for="(img, i) in userInfo.albumThumbs" :key="i">
|
||||
<image :src="img" mode="aspectFill" class="photo"></image>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
<view v-else class="photo-swiper photo-empty">
|
||||
<uni-icons type="person" size="80" color="#ccc"></uni-icons>
|
||||
</view>
|
||||
|
||||
<!-- 昵称 / 年龄 / 在线 -->
|
||||
<view class="head">
|
||||
<text class="nickname">{{ userInfo.nickname }}</text>
|
||||
<text class="age" v-if="userInfo.age > 0">{{ userInfo.age }}岁</text>
|
||||
<text class="online" v-if="userInfo.isOnline">在线</text>
|
||||
</view>
|
||||
|
||||
<!-- 标签切换 -->
|
||||
<view class="tabsbox" style="justify-content: space-around;">
|
||||
<text class="tabsbox-item" style="flex:1"
|
||||
:class="currentTab == 'about' ? 'tabsbox-active tabsbox-active-txt' : ''"
|
||||
@click="switchTab('about')">关于TA</text>
|
||||
<text class="tabsbox-item" style="flex:1"
|
||||
:class="currentTab == 'moments' ? 'tabsbox-active tabsbox-active-txt' : ''"
|
||||
@click="switchTab('moments')">动态 ({{ userInfo.profile.momentCount }})</text>
|
||||
</view>
|
||||
|
||||
<!-- 关于TA -->
|
||||
<view v-if="currentTab == 'about'">
|
||||
<view class="panel">
|
||||
<text class="panel-title">个人资料</text>
|
||||
<view class="panel-content">
|
||||
<text class="panel-text" v-if="userInfo.profile.age > 0">{{ userInfo.profile.age }}岁</text>
|
||||
<text class="panel-text" v-if="userInfo.profile.height > 0">{{ userInfo.profile.height }}cm</text>
|
||||
<text class="panel-text" v-if="userInfo.profile.weight > 0">{{ userInfo.profile.weight }}kg</text>
|
||||
<text class="panel-text" v-if="userInfo.profile.live">{{ userInfo.profile.live }}</text>
|
||||
<text class="panel-text" v-if="userInfo.profile.hometown">{{ userInfo.profile.hometown }}</text>
|
||||
<text class="panel-text" v-if="userInfo.profile.education">{{ userInfo.profile.education }}</text>
|
||||
<text class="panel-text" v-if="userInfo.profile.profession">{{ userInfo.profile.profession }}</text>
|
||||
<text class="panel-text" v-if="userInfo.profile.constellation">{{ userInfo.profile.constellation }}</text>
|
||||
<text class="panel-text" v-if="userInfo.profile.annualIncome">{{ userInfo.profile.annualIncome }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="panel" v-if="userInfo.profile.bio">
|
||||
<text class="panel-title">个性签名</text>
|
||||
<view class="panel-content" style="flex-flow: column; align-items: flex-start;">
|
||||
<text class="bio">{{ userInfo.profile.bio }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="panel">
|
||||
<text class="panel-title">收到的礼物</text>
|
||||
<view class="panel-content" style="flex-flow: column; align-items: flex-start;">
|
||||
<text class="bio tip">在聊天中赠送礼物,表达你的心意~</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 动态 -->
|
||||
<view v-if="currentTab == 'moments'">
|
||||
<view class="panel">
|
||||
<view class="panel-content" style="flex-flow: column; align-items: center;">
|
||||
<text class="bio tip">该用户暂未公开动态</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<view class="tabbar-op">
|
||||
<view class="tabbar-item" @click="onFollowClick">
|
||||
<uni-icons :type="isFollowed ? 'star-filled' : 'star'" size="40"
|
||||
:color="isFollowed ? '#FFB400' : '#666'"></uni-icons>
|
||||
<text class="tabbar-text">{{ isFollowed ? '已关注' : '关注' }}</text>
|
||||
</view>
|
||||
<view class="tabbar-item" @click="onChatClick">
|
||||
<uni-icons type="chat" size="40" color="#666"></uni-icons>
|
||||
<text class="tabbar-text">私聊</text>
|
||||
</view>
|
||||
<view class="tabbar-item" @click="onGiftClick">
|
||||
<uni-icons type="gift" size="40" color="#666"></uni-icons>
|
||||
<text class="tabbar-text">礼物</text>
|
||||
</view>
|
||||
<view class="tabbar-item tabbar-hion" @click="onAccostClick">
|
||||
<uni-icons type="heart" size="40" color="#FF5C8A"></uni-icons>
|
||||
<text class="tabbar-text">搭讪</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 送礼弹窗 -->
|
||||
<view v-if="showGift" class="gift-mask" @click="closeGift">
|
||||
<view class="gift-sheet" @click.stop>
|
||||
<view class="gift-sheet-head">
|
||||
<text class="gift-sheet-title">赠送礼物</text>
|
||||
<uni-icons type="close" size="22" color="#999" @click="closeGift"></uni-icons>
|
||||
</view>
|
||||
<scroll-view class="gift-sheet-list" direction="vertical" :show-scrollbar="false">
|
||||
<view v-for="(g, i) in gifts" :key="g.id" class="gift-sheet-cell"
|
||||
:class="{ active: selectedGift != null && selectedGift.id == g.id }" @click="selectGift(g)">
|
||||
<text class="gift-sheet-name">{{ g.name }}</text>
|
||||
<text class="gift-sheet-price">{{ g.price }}金币</text>
|
||||
</view>
|
||||
<view v-if="gifts.length == 0" class="gift-sheet-empty">
|
||||
<text class="empty-text">暂无可赠送的礼物</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="gift-sheet-count">
|
||||
<text class="count-label">数量</text>
|
||||
<view class="count-ctrl">
|
||||
<text class="count-btn" @click="giftCount > 1 ? giftCount = giftCount - 1 : null">-</text>
|
||||
<text class="count-num">{{ giftCount }}</text>
|
||||
<text class="count-btn" @click="giftCount = giftCount + 1">+</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="gift-sheet-send" @click="sendGift">
|
||||
<text class="send-text">赠送({{ giftTotal }}金币)</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref, reactive, computed, onLoad } from 'vue'
|
||||
import { Api } from '@/common/api-service.uts'
|
||||
import { getUserInfo } from '@/stores/user.uts'
|
||||
|
||||
type IViewProfile = {
|
||||
age: number
|
||||
height: number
|
||||
weight: number
|
||||
live: string
|
||||
hometown: string
|
||||
education: string
|
||||
profession: string
|
||||
constellation: string
|
||||
annualIncome: string
|
||||
bio: string
|
||||
momentCount: number
|
||||
uid: number
|
||||
}
|
||||
type IViewUser = {
|
||||
uid: number
|
||||
nickname: string
|
||||
avatar: string
|
||||
albumThumbs: string[]
|
||||
age: number
|
||||
isOnline: boolean
|
||||
profile: IViewProfile
|
||||
}
|
||||
|
||||
const userInfo = reactive<IViewUser>({
|
||||
uid: 0,
|
||||
nickname: '',
|
||||
avatar: '',
|
||||
albumThumbs: [],
|
||||
age: 0,
|
||||
isOnline: false,
|
||||
profile: {
|
||||
age: 0, height: 0, weight: 0, live: '', hometown: '', education: '',
|
||||
profession: '', constellation: '', annualIncome: '', bio: '', momentCount: 0, uid: 0
|
||||
}
|
||||
})
|
||||
|
||||
const currentTab = ref('about')
|
||||
const isFollowed = ref(false)
|
||||
const targetUid = ref(0)
|
||||
|
||||
const switchTab = (tab : string) => {
|
||||
currentTab.value = tab
|
||||
}
|
||||
|
||||
const loadDetail = async (uid : number) => {
|
||||
try {
|
||||
const data = await Api.user.detail(uid)
|
||||
const p = data
|
||||
const city = p.getString('residecity') ?? ''
|
||||
const province = p.getString('resideprovince') ?? ''
|
||||
const age = p.getNumber('age') ?? 0
|
||||
const photos = p.getJSONArray('photos')
|
||||
const album : string[] = []
|
||||
if (photos != null && photos.size() > 0) {
|
||||
for (let i = 0; i < photos.size(); i++) {
|
||||
const s = photos.get(i) as string
|
||||
if (s != null && s.length > 0) album.push(s)
|
||||
}
|
||||
}
|
||||
const avatar = p.getString('avatar') ?? ''
|
||||
if (album.length == 0 && avatar.length > 0) album.push(avatar)
|
||||
|
||||
userInfo.uid = uid
|
||||
userInfo.nickname = p.getString('nickname') ?? '匿名用户'
|
||||
userInfo.avatar = avatar
|
||||
userInfo.albumThumbs = album
|
||||
userInfo.age = age
|
||||
userInfo.isOnline = p.getBoolean('is_online') ?? false
|
||||
userInfo.profile = {
|
||||
age: age,
|
||||
height: p.getNumber('height') ?? 0,
|
||||
weight: p.getNumber('weight') ?? 0,
|
||||
live: city.length > 0 ? city : province,
|
||||
hometown: province,
|
||||
education: p.getString('education') ?? '',
|
||||
profession: p.getString('occupation') ?? '',
|
||||
constellation: p.getString('constellation') ?? '',
|
||||
annualIncome: p.getString('revenue') ?? '',
|
||||
bio: p.getString('bio') ?? '',
|
||||
momentCount: p.getNumber('moment_count') ?? 0,
|
||||
uid: uid
|
||||
}
|
||||
isFollowed.value = (p.getNumber('is_followed') ?? 0) == 1
|
||||
} catch (e : any) {
|
||||
uni.showToast({ title: e?.message ?? '加载失败', icon: 'none' })
|
||||
}
|
||||
}
|
||||
|
||||
const meUid = () : number => {
|
||||
return getUserInfo()?.uid ?? 0
|
||||
}
|
||||
|
||||
const onFollowClick = () => {
|
||||
if (targetUid.value <= 0) return
|
||||
const action = isFollowed.value ? 0 : 1
|
||||
Api.follow.toggle(meUid(), targetUid.value, action).then(() => {
|
||||
isFollowed.value = ! isFollowed.value
|
||||
uni.showToast({ title: isFollowed.value ? '已关注' : '已取消关注', icon: 'none' })
|
||||
}).catch((err : any) => {
|
||||
uni.showToast({ title: err?.message ?? '操作失败', icon: 'none' })
|
||||
})
|
||||
}
|
||||
|
||||
const onChatClick = () => {
|
||||
if (targetUid.value <= 0) return
|
||||
uni.navigateTo({ url: `/pages/chat/chat?uid=${targetUid.value}` })
|
||||
}
|
||||
|
||||
const onGiftClick = () => {
|
||||
showGift.value = true
|
||||
loadGifts()
|
||||
}
|
||||
|
||||
// ===== 送礼弹窗 =====
|
||||
type IGift = {
|
||||
id : number
|
||||
name : string
|
||||
icon : string
|
||||
price : number
|
||||
}
|
||||
const showGift = ref<boolean>(false)
|
||||
const gifts = reactive<IGift[]>([])
|
||||
const selectedGift = ref<IGift | null>(null)
|
||||
const giftCount = ref<number>(1)
|
||||
const giftLoading = ref<boolean>(false)
|
||||
|
||||
const giftTotal = computed<number>(() => {
|
||||
const p = selectedGift.value?.price ?? 0
|
||||
return p * giftCount.value
|
||||
})
|
||||
|
||||
const loadGifts = () => {
|
||||
if (giftLoading.value) return
|
||||
giftLoading.value = true
|
||||
Api.gift.list()
|
||||
.then((res : any) => {
|
||||
const list = (res as Array<IGift>) ?? []
|
||||
gifts.splice(0, gifts.length)
|
||||
list.forEach((g : IGift) => gifts.push(g))
|
||||
if (gifts.length > 0 && selectedGift.value == null) {
|
||||
selectedGift.value = gifts[0]
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
uni.showToast({ title: '礼物加载失败', icon: 'none' })
|
||||
})
|
||||
.finally(() => { giftLoading.value = false })
|
||||
}
|
||||
|
||||
const selectGift = (g : IGift) => {
|
||||
selectedGift.value = g
|
||||
}
|
||||
|
||||
const sendGift = () => {
|
||||
if (selectedGift.value == null) {
|
||||
uni.showToast({ title: '请选择礼物', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (targetUid.value <= 0) return
|
||||
uni.showLoading({ title: '赠送中...', mask: true })
|
||||
Api.gift.send({
|
||||
to_uid: targetUid.value,
|
||||
gift_id: selectedGift.value.id,
|
||||
count: giftCount.value,
|
||||
message: ''
|
||||
})
|
||||
.then(() => {
|
||||
uni.hideLoading()
|
||||
showGift.value = false
|
||||
uni.showToast({ title: '赠送成功', icon: 'success' })
|
||||
})
|
||||
.catch((e : any) => {
|
||||
uni.hideLoading()
|
||||
const msg = (e as UTSJSONObject)?.get('message') as string
|
||||
uni.showToast({ title: msg ?? '赠送失败', icon: 'none' })
|
||||
})
|
||||
}
|
||||
|
||||
const closeGift = () => {
|
||||
showGift.value = false
|
||||
}
|
||||
|
||||
const onAccostClick = () => {
|
||||
if (targetUid.value <= 0) return
|
||||
Api.message.send({
|
||||
receiver_id: targetUid.value,
|
||||
receiver_type: 0,
|
||||
content: '你好,很高兴认识你~'
|
||||
}).then(() => {
|
||||
uni.showToast({ title: '搭讪已送达', icon: 'success' })
|
||||
}).catch((err : any) => {
|
||||
uni.showToast({ title: err?.message ?? '搭讪失败', icon: 'none' })
|
||||
})
|
||||
}
|
||||
|
||||
onLoad((options : any) => {
|
||||
const uid = Number(options?.uid ?? options?.id ?? 0)
|
||||
targetUid.value = uid
|
||||
if (uid > 0) {
|
||||
loadDetail(uid)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #f6f6f6;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.photo-swiper {
|
||||
width: 100%;
|
||||
height: 760rpx;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.photo-empty {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.photo {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.head {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
padding: 24rpx 30rpx;
|
||||
}
|
||||
|
||||
.nickname {
|
||||
font-size: 38rpx;
|
||||
font-weight: bold;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.age {
|
||||
font-size: 30rpx;
|
||||
color: #666;
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
|
||||
.online {
|
||||
font-size: 22rpx;
|
||||
color: #fff;
|
||||
background: #4CD964;
|
||||
border-radius: 20rpx;
|
||||
padding: 4rpx 14rpx;
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
|
||||
.tabsbox {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
background: #fff;
|
||||
margin-top: 2rpx;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
|
||||
.tabsbox-item {
|
||||
text-align: center;
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.tabsbox-active {
|
||||
font-weight: bold;
|
||||
color: #FF5C8A;
|
||||
}
|
||||
|
||||
.panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #fff;
|
||||
margin-top: 16rpx;
|
||||
padding: 20rpx 30rpx;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
padding: 10rpx 0;
|
||||
border-bottom: 1rpx solid #eee;
|
||||
}
|
||||
|
||||
.panel-content {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding: 24rpx 0;
|
||||
}
|
||||
|
||||
.panel-text {
|
||||
font-size: 24rpx;
|
||||
border-radius: 25rpx;
|
||||
padding: 6rpx 18rpx;
|
||||
margin: 6rpx;
|
||||
background: #f3f3f3;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.bio {
|
||||
font-size: 26rpx;
|
||||
color: #444;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
|
||||
.tip {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.tabbar-op {
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
bottom: var(--window-bottom);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-flow: row nowrap;
|
||||
align-items: center;
|
||||
padding: 16rpx 20rpx;
|
||||
background-color: #fff;
|
||||
border-top: 1rpx solid #e2e2e2;
|
||||
}
|
||||
|
||||
.tabbar-item {
|
||||
width: 16%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-flow: column nowrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tabbar-text {
|
||||
font-size: 22rpx;
|
||||
color: #666;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
.tabbar-hion {
|
||||
width: 40%;
|
||||
flex-flow: row nowrap;
|
||||
background-image: linear-gradient(to bottom, #ffd2fb, #ffaaff);
|
||||
border-radius: 40rpx;
|
||||
padding: 16rpx 0;
|
||||
}
|
||||
|
||||
.gift-mask {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.gift-sheet {
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
border-top-left-radius: 28rpx;
|
||||
border-top-right-radius: 28rpx;
|
||||
padding: 24rpx 30rpx calc(30rpx + var(--uni-safe-area-inset-bottom));
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.gift-sheet-head {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.gift-sheet-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.gift-sheet-list {
|
||||
max-height: 420rpx;
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
|
||||
.gift-sheet-cell {
|
||||
width: 22%;
|
||||
margin: 10rpx 1.5%;
|
||||
padding: 20rpx 0;
|
||||
border-radius: 16rpx;
|
||||
background-color: #f7f7f7;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.gift-sheet-cell.active {
|
||||
background-color: #fff0ec;
|
||||
border: 2rpx solid #FF6B6B;
|
||||
}
|
||||
|
||||
.gift-sheet-name {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.gift-sheet-price {
|
||||
font-size: 22rpx;
|
||||
color: #FF6B6B;
|
||||
margin-top: 6rpx;
|
||||
}
|
||||
|
||||
.gift-sheet-empty {
|
||||
width: 100%;
|
||||
padding: 60rpx 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.gift-sheet-count {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: 20rpx 0;
|
||||
}
|
||||
|
||||
.count-label {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.count-ctrl {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.count-btn {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border-radius: 30rpx;
|
||||
background-color: #f0f0f0;
|
||||
text-align: center;
|
||||
line-height: 60rpx;
|
||||
font-size: 36rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.count-num {
|
||||
min-width: 60rpx;
|
||||
text-align: center;
|
||||
font-size: 30rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.gift-sheet-send {
|
||||
height: 92rpx;
|
||||
border-radius: 46rpx;
|
||||
background: linear-gradient(135deg, #FF6B6B 0%, #FF8E53 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.send-text {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user