26
26
</n-tooltip >
27
27
</div >
28
28
<div class =" user-info-stats" >
29
- <div class =" user-info-stat-item" >
29
+ <div class =" user-info-stat-item" @click = " showFollowerList " >
30
30
<div class =" label" >{{ userDetail.profile.followeds }}</div >
31
31
<div >{{ t('user.profile.followers') }}</div >
32
32
</div >
33
- <div class =" user-info-stat-item" >
33
+ <div class =" user-info-stat-item" @click = " showFollowList " >
34
34
<div class =" label" >{{ userDetail.profile.follows }}</div >
35
35
<div >{{ t('user.profile.following') }}</div >
36
36
</div >
50
50
<n-tabs type =" line" animated >
51
51
<!-- 歌单列表 -->
52
52
<n-tab-pane name =" playlists" :tab =" t('user.detail.playlists')" >
53
- <div v-if =" loading" class =" loading-container" >
54
- <n-spin size =" medium" />
55
- </div >
56
- <div v-else-if =" playList.length === 0" class =" empty-message" >
53
+ <div v-if =" playList.length === 0" class =" empty-message" >
57
54
{{ t('user.detail.noPlaylists') }}
58
55
</div >
59
56
<div v-else class =" playlist-grid" :class =" setAnimationClass('animate__fadeInUp')" >
89
86
90
87
<!-- 听歌排行 -->
91
88
<n-tab-pane name =" records" :tab =" t('user.detail.records')" >
92
- <div v-if =" loading" class =" loading-container" >
93
- <n-spin size =" medium" />
89
+ <div v-if =" !hasRecordPermission" class =" empty-message" >
90
+ <div class =" no-permission" >
91
+ <i class =" ri-lock-line text-2xl mr-2" ></i >
92
+ {{ t('user.detail.noRecordPermission', { name: userDetail.profile.nickname }) }}
93
+ </div >
94
94
</div >
95
95
<div v-else-if =" !recordList || recordList.length === 0" class =" empty-message" >
96
96
{{ t('user.detail.noRecords') }}
103
103
:class =" setAnimationClass('animate__bounceInUp')"
104
104
:style =" setAnimationDelay(index, 25)"
105
105
>
106
- <div class =" play-score" >
107
- {{ index + 1 }}
108
- </div >
109
- <song-item class =" song-item" :item =" item" mini @play =" handlePlay" />
106
+ <song-item class =" song-item" :index =" index" :item =" item" compact @play =" handlePlay" />
110
107
</div >
111
108
</div >
112
109
</n-tab-pane >
125
122
126
123
<script lang="ts" setup>
127
124
import { useMessage } from ' naive-ui' ;
128
- import { onMounted , ref } from ' vue' ;
125
+ import { onMounted , ref , watch } from ' vue' ;
129
126
import { useI18n } from ' vue-i18n' ;
130
127
import { useRoute , useRouter } from ' vue-router' ;
131
128
@@ -150,12 +147,12 @@ const playerStore = usePlayerStore();
150
147
151
148
// 获取路由参数中的用户ID
152
149
const userId = ref <number >(Number (route .params .uid ));
153
-
154
150
// 用户数据
155
151
const userDetail = ref <IUserDetail >();
156
152
const playList = ref <any []>([]);
157
153
const recordList = ref <any []>([]);
158
154
const loading = ref (true );
155
+ const hasRecordPermission = ref (true ); // 是否有权限查看听歌记录
159
156
160
157
// 歌单详情相关
161
158
const currentList = ref <Playlist >();
@@ -171,34 +168,68 @@ const loadUserData = async () => {
171
168
172
169
try {
173
170
loading .value = true ;
171
+ recordList .value = []; // 清空之前的记录
172
+ hasRecordPermission .value = true ; // 重置权限状态
173
+
174
+ // 分开处理请求,处理可能的错误
175
+ // 1. 获取用户详情和歌单列表
176
+ try {
177
+ const [userDetailRes, playlistRes] = await Promise .all ([
178
+ getUserDetail (userId .value ),
179
+ getUserPlaylist (userId .value )
180
+ ]);
181
+
182
+ userDetail .value = userDetailRes .data ;
183
+ playList .value = playlistRes .data .playlist ;
184
+ } catch (error ) {
185
+ console .error (' 加载用户基本信息失败:' , error );
186
+ message .error (t (' user.message.loadBasicInfoFailed' ));
187
+ return ; // 如果基本信息加载失败,直接返回
188
+ }
174
189
175
- // 使用 Promise.all 并行请求提高效率
176
- const [userDetailRes, playlistRes, recordRes] = await Promise . all ([
177
- getUserDetail (userId .value ),
178
- getUserPlaylist ( userId . value ),
179
- getUserRecord ( userId . value )
180
- ]);
181
-
182
- userDetail . value = userDetailRes . data ;
183
- playList . value = playlistRes . data . playlist ;
184
-
185
- if ( recordRes . data && recordRes . data . allData ) {
186
- recordList . value = recordRes . data . allData . map (( item : any ) => ( {
187
- ... item ,
188
- ... item . song ,
189
- picUrl: item . song . al . picUrl
190
- })) ;
191
- } else {
192
- recordList . value = [];
190
+ // 2. 单独处理听歌记录请求,这个请求可能会无权限
191
+ try {
192
+ const recordRes = await getUserRecord (userId .value );
193
+
194
+ if ( recordRes . data && recordRes . data . allData ) {
195
+ recordList . value = recordRes . data . allData . map (( item : any ) => ({
196
+ ... item ,
197
+ ... item . song ,
198
+ picUrl: item . song . al . picUrl
199
+ }));
200
+ }
201
+ } catch ( error : any ) {
202
+ console . error ( ' 加载听歌记录失败: ' , error );
203
+ // 判断是否是无权限错误
204
+ if ( error . response ?. data ?. code === - 2 || error . data ?. code === - 2 ) {
205
+ hasRecordPermission . value = false ;
206
+ }
207
+ // 不显示错误消息,因为这是预期的情况
193
208
}
194
209
} catch (error ) {
195
210
console .error (' 加载用户数据失败:' , error );
196
- message .error (' 加载用户数据失败 ' );
211
+ message .error (t ( ' user.message.loadFailed ' ) );
197
212
} finally {
198
213
loading .value = false ;
199
214
}
200
215
};
201
216
217
+ // 使用onMounted和watch结合的方式解决路由变化问题
218
+ onMounted (() => {
219
+ loadUserData ();
220
+ });
221
+
222
+ // 监听路由参数变化
223
+ watch (
224
+ () => route .params .uid ,
225
+ (newUid ) => {
226
+ if (newUid && Number (newUid ) !== userId .value ) {
227
+ userId .value = Number (newUid );
228
+ loadUserData ();
229
+ }
230
+ }
231
+ );
232
+
202
233
// 替换显示歌单的方法
203
234
const openPlaylist = (item : any ) => {
204
235
listLoading .value = true ;
@@ -226,15 +257,36 @@ const handlePlay = () => {
226
257
playerStore .setPlayList (tracks );
227
258
};
228
259
260
+ // 显示关注列表
261
+ const showFollowList = () => {
262
+ if (! userDetail .value ) return ;
263
+
264
+ router .push ({
265
+ path: ` /user/follows ` ,
266
+ query: {
267
+ uid: userId .value .toString (),
268
+ name: userDetail .value .profile .nickname
269
+ }
270
+ });
271
+ };
272
+
273
+ // 显示粉丝列表
274
+ const showFollowerList = () => {
275
+ if (! userDetail .value ) return ;
276
+
277
+ router .push ({
278
+ path: ` /user/followers ` ,
279
+ query: {
280
+ uid: userId .value .toString (),
281
+ name: userDetail .value .profile .nickname
282
+ }
283
+ });
284
+ };
285
+
229
286
// 判断是否为歌手
230
287
const isArtist = (profile : any ) => {
231
288
return profile .userType === 4 || profile .userType === 2 || profile .accountType === 2 ;
232
289
};
233
-
234
- // 页面挂载时加载数据
235
- onMounted (() => {
236
- loadUserData ();
237
- });
238
290
</script >
239
291
240
292
<style lang="scss" scoped>
@@ -288,6 +340,11 @@ onMounted(() => {
288
340
.label {
289
341
@apply text-lg font-bold ;
290
342
}
343
+
344
+ & :nth-child (1 ), & :nth-child (2 ) {
345
+ @apply cursor-pointer transition-all duration- 200;
346
+ @apply hover :bg- black hover:bg- opacity- 20 rounded- lg px- 2;
347
+ }
291
348
}
292
349
}
293
350
@@ -365,5 +422,14 @@ onMounted(() => {
365
422
366
423
.empty-message {
367
424
@apply flex justify-center items-center p- 8;
425
+
426
+ .no-permission {
427
+ @apply flex flex-col items-center justify-center text-gray-500 dark :text- gray- 400;
428
+ @apply p-4 rounded-lg ;
429
+
430
+ i {
431
+ @apply text-3xl mb- 2;
432
+ }
433
+ }
368
434
}
369
435
</style >
0 commit comments