Files
hejiu/pages/friends/friends.vue
T
cg 99723497fa rename(app): 将应用名称从"干杯日记"更改为"碰盏日记"
- 替换所有文件中的品牌名称引用,包括App.vue、pages.json、uni.scss等
- 更新全局样式、导航栏标题和组件中的文案显示
- 修改分享功能中的应用名称显示
- 调整聊天页面的输入栏键盘适配逻辑
- 在多个页面组件中添加事件发射声明以支持交互功能
2026-07-21 22:20:08 +08:00

379 lines
8.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view :class="themeClass" class="page-container friends-page">
<!-- 顶部导航 -->
<view class="friends-nav" :style="{ paddingTop: navPaddingTop }">
<view class="friends-nav-inner">
<view class="friends-back" @click="goBack">
<text class="friends-back-icon"></text>
</view>
<text class="friends-nav-title">我的酒友</text>
<view class="friends-nav-ph"></view>
</view>
</view>
<!-- 搜索栏 -->
<view class="friends-search">
<view class="search-box">
<text class="search-icon">🔍</text>
<input
class="search-input"
v-model="keyword"
placeholder="搜索酒友"
placeholder-class="search-placeholder"
confirm-type="search"
@confirm="doSearch"
@input="onSearchInput"
/>
<view v-if="keyword" class="search-clear" @click="clearSearch">
<text class="search-clear-icon">×</text>
</view>
</view>
</view>
<scroll-view class="friends-scroll" scroll-y>
<view class="friends-body">
<!-- 好友请求区 -->
<view v-if="requests.length && !keyword" class="requests-section">
<text class="section-label">新的好友请求</text>
<view class="request-item" v-for="req in requests" :key="req.id">
<view class="request-avatar">
<text class="request-avatar-text">{{ req.nickname[0] }}</text>
</view>
<view class="request-info">
<text class="request-name">{{ req.nickname }}</text>
<text class="request-msg">{{ req.message }}</text>
</view>
<view class="request-accept" @click="acceptRequest(req)">
<text class="request-accept-text">接受</text>
</view>
</view>
</view>
<!-- 酒友列表 -->
<view v-if="friends.length" class="friends-list">
<text class="section-label" v-if="!keyword">全部酒友 ({{ friends.length }})</text>
<text class="section-label" v-else>搜索结果 ({{ friends.length }})</text>
<FriendItem
v-for="f in friends"
:key="f.id"
:friend="f"
>
<template #action>
<view class="friend-del" @click.stop="confirmRemove(f)">
<text class="friend-del-text">删除</text>
</view>
</template>
</FriendItem>
</view>
<!-- 空状态 -->
<EmptyState
v-if="!friends.length && !requests.length"
icon="👥"
title="还没有酒友"
desc="分享小程序给好友,邀请他们加入"
actionText="邀请好友"
@action="inviteFriends"
/>
<EmptyState
v-if="keyword && !friends.length"
icon="🔍"
title="未找到相关酒友"
desc="换个关键词试试"
/>
</view>
</scroll-view>
</view>
</template>
<script>
import FriendItem from '../../components/FriendItem.vue'
import EmptyState from '../../components/EmptyState.vue'
import { GetFriends, GetFriendRequests, AcceptFriendRequest, RemoveFriend } from '../../common/api'
import themeMixin from '../../common/theme-mixin'
export default {
mixins: [themeMixin],
components: { FriendItem, EmptyState },
data() {
const menuBtn = uni.getMenuButtonBoundingClientRect()
return {
navPaddingTop: menuBtn.top + 'px',
keyword: '',
friends: [],
requests: [],
searchTimer: null
}
},
onLoad() {
this.loadData()
},
methods: {
goBack() {
uni.navigateBack()
},
async loadData() {
try {
const [friendsRes, reqRes] = await Promise.all([
GetFriends({}),
GetFriendRequests()
])
this.friends = friendsRes.data.list
this.requests = reqRes.data.list
} catch (e) {
console.warn('加载酒友失败', e)
}
},
onSearchInput() {
clearTimeout(this.searchTimer)
this.searchTimer = setTimeout(() => this.doSearch(), 300)
},
async doSearch() {
try {
const res = await GetFriends({ keyword: this.keyword.trim() })
this.friends = res.data.list
} catch (e) { /* ignore */ }
},
clearSearch() {
this.keyword = ''
this.doSearch()
},
async acceptRequest(req) {
try {
await AcceptFriendRequest({ requestId: req.id })
this.requests = this.requests.filter(r => r.id !== req.id)
uni.showToast({ title: '已添加酒友 🍻', icon: 'none' })
this.loadData()
} catch (e) {
uni.showToast({ title: '操作失败', icon: 'none' })
}
},
confirmRemove(friend) {
uni.showModal({
title: '删除酒友',
content: `确定要删除「${friend.nickname}」吗?`,
confirmText: '删除',
confirmColor: '#FF6B6B',
success: async (res) => {
if (res.confirm) {
try {
await RemoveFriend({ userId: friend.id })
this.friends = this.friends.filter(f => f.id !== friend.id)
uni.showToast({ title: '已删除', icon: 'none' })
} catch (e) {
uni.showToast({ title: '操作失败', icon: 'none' })
}
}
}
})
},
inviteFriends() {
// 触发分享
uni.showToast({ title: '请点击右上角分享', icon: 'none' })
}
},
onShareAppMessage() {
return {
title: '来「碰盏日记」一起记录饮酒生活吧!',
path: '/pages/index/index'
}
}
}
</script>
<style lang="scss" scoped>
.friends-page {
display: flex;
flex-direction: column;
height: 100vh;
overflow: hidden;
}
.friends-nav {
background: $bg-base;
padding-left: $sp-lg;
padding-right: $sp-lg;
}
.friends-nav-inner {
display: flex;
align-items: center;
justify-content: space-between;
height: 88rpx;
}
.friends-back {
width: 64rpx;
height: 64rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: $bg-card;
&:active { background: $bg-card-alt; }
}
.friends-back-icon {
font-size: $fs-xl;
color: $text-primary;
font-weight: $fw-bold;
}
.friends-nav-title {
font-size: $fs-base;
font-weight: $fw-bold;
color: $text-primary;
}
.friends-nav-ph {
width: 64rpx;
}
/* 搜索 */
.friends-search {
padding: $sp-md $sp-lg;
}
.search-box {
display: flex;
align-items: center;
gap: $sp-sm;
padding: 0 $sp-lg;
height: 76rpx;
background: $bg-card;
border-radius: $radius-full;
border: 1rpx solid var(--border-faint, rgba(255,255,255,0.08));
}
.search-icon {
font-size: $fs-sm;
}
.search-input {
flex: 1;
font-size: $fs-base;
color: $text-primary;
}
.search-placeholder {
color: $text-tertiary;
}
.search-clear {
width: 40rpx;
height: 40rpx;
display: flex;
align-items: center;
justify-content: center;
}
.search-clear-icon {
font-size: $fs-lg;
color: $text-tertiary;
}
.friends-scroll {
flex: 1;
height: 0;
}
.friends-body {
padding: 0 $sp-lg;
padding-bottom: calc(env(safe-area-inset-bottom) + #{$sp-2xl});
}
.section-label {
display: block;
font-size: $fs-sm;
color: $text-tertiary;
font-weight: $fw-medium;
margin-bottom: $sp-md;
margin-top: $sp-lg;
}
/* 好友请求 */
.requests-section {
margin-bottom: $sp-lg;
}
.request-item {
display: flex;
align-items: center;
gap: $sp-md;
padding: $sp-lg;
background: rgba(232,168,56,0.05);
border: 1rpx solid rgba(232,168,56,0.12);
border-radius: $radius-lg;
margin-bottom: $sp-sm;
}
.request-avatar {
width: 72rpx;
height: 72rpx;
border-radius: 50%;
background: $bg-elevated;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.request-avatar-text {
font-size: $fs-base;
font-weight: $fw-bold;
color: $amber;
}
.request-info {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 4rpx;
}
.request-name {
font-size: $fs-base;
font-weight: $fw-bold;
color: $text-primary;
}
.request-msg {
font-size: $fs-xs;
color: $text-tertiary;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.request-accept {
padding: 8rpx 24rpx;
background: linear-gradient(135deg, $amber, $amber-deep);
border-radius: $radius-full;
flex-shrink: 0;
&:active { transform: scale(0.95); }
}
.request-accept-text {
font-size: $fs-sm;
font-weight: $fw-bold;
color: $text-on-amber;
}
/* 删除按钮 */
.friend-del {
padding: 8rpx 20rpx;
border-radius: $radius-full;
background: rgba(255,107,107,0.1);
&:active { background: rgba(255,107,107,0.2); }
}
.friend-del-text {
font-size: $fs-xs;
color: $coral;
}
</style>