11<template >
22 <n-popover
33 :show =" userMenuShow"
4- style =" padding : 12px ; max-width : 160 px "
4+ style =" padding : 12px ; max-width : 240 px "
55 trigger =" manual"
66 @clickoutside =" userMenuShow = false"
77 >
2222 <SvgIcon name="Person" :depth =" 3 " size="26" />
2323 </n-avatar >
2424 </div >
25- <n-flex v-if =" isDesktop" class =" user-data" size =" small" >
26- <n-text class =" name" >
25+ <n-flex v-if =" isDesktop" :wrap = " false " class =" user-data" size =" small" >
26+ <n-text class =" name text-hidden " >
2727 {{ dataStore.userLoginStatus ? dataStore.userData.name || "未知用户名" : "未登录" }}
2828 </n-text >
2929 <!-- VIP -->
7070 <n-text :depth =" 3" >部分功能暂不可用</n-text >
7171 </n-flex >
7272 <n-divider />
73+ <!-- 多账号 -->
74+ <div class =" account-list" v-if =" dataStore.userLoginStatus && dataStore.loginType !== 'uid'" >
75+ <n-text class =" subtitle" :depth =" 3" >切换账号</n-text >
76+ <div
77+ v-for =" account in otherAccounts"
78+ :key =" account.userId"
79+ class =" account-item"
80+ @click =" handleSwitchAccount(account.userId)"
81+ >
82+ <n-avatar :src =" account.avatarUrl" round size =" small" />
83+ <div class =" account-name text-hidden" >{{ account.name }}</div >
84+ <div class =" delete-btn" @click.stop =" handleRemoveAccount(account.userId)" >
85+ <SvgIcon name="Close" />
86+ </div >
87+ </div >
88+ <n-button class =" add-account" ghost block @click =" handleAddAccount" >
89+ <template #icon >
90+ <SvgIcon name="Add" />
91+ </template >
92+ 添加账号
93+ </n-button >
94+ </div >
95+ <n-divider v-if =" dataStore.userLoginStatus" />
7396 <!-- 退出登录 -->
7497 <n-button :focusable =" false" class =" logout" strong secondary round @click =" isLogout" >
7598 <template #icon >
@@ -86,11 +109,14 @@ import { useDataStore } from "@/stores";
86109import { openUserLogin } from " @/utils/modal" ;
87110import { getLoginState } from " @/api/login" ;
88111import {
112+ updateUserData ,
113+ updateSpecialUserData ,
89114 toLogout ,
90115 isLogin ,
91116 refreshLoginData ,
92- updateUserData ,
93- updateSpecialUserData ,
117+ saveCurrentAccount ,
118+ switchAccount ,
119+ removeAccount ,
94120} from " @/utils/auth" ;
95121import { useMobile } from " @/composables/useMobile" ;
96122
@@ -158,6 +184,55 @@ const checkLoginStatus = async () => {
158184 }
159185};
160186
187+ // 其他账号列表(排除当前登录的)
188+ const otherAccounts = computed (() => {
189+ return dataStore .userList .filter ((u ) => u .userId !== dataStore .userData .userId );
190+ });
191+
192+ // 切换账号
193+ const handleSwitchAccount = async (userId : number ) => {
194+ userMenuShow .value = false ;
195+ await switchAccount (userId );
196+ };
197+
198+ // 移除账号
199+ const handleRemoveAccount = (userId : number ) => {
200+ removeAccount (userId );
201+ };
202+
203+ // 添加新账号 (保存当前 -> 开启强制登录 -> 成功后自动切换)
204+ const handleAddAccount = async () => {
205+ // 限制账号数量
206+ if (dataStore .userList .length >= 3 ) {
207+ window .$message .warning (" 最多只能保留 3 个账号" );
208+ return ;
209+ }
210+
211+ // 1先保存当前账号状态 (快照)
212+ saveCurrentAccount ();
213+
214+ userMenuShow .value = false ;
215+
216+ // 打开登录框 (强制模式, 不登出当前用户以保持 cookies 直到新登录成功, 禁用 UID 登录)
217+ openUserLogin (
218+ false ,
219+ true ,
220+ async () => {
221+ // 登录成功回调
222+ // 此时新 cookies 已设置,store 已更新
223+ window .$message .loading (" 正在更新数据..." );
224+ try {
225+ await updateUserData ();
226+ window .$message .success (" 登录成功" );
227+ // router.push("/");
228+ } catch (error ) {
229+ console .error (" Login update failed" , error );
230+ }
231+ },
232+ true ,
233+ );
234+ };
235+
161236// 退出登录
162237const isLogout = () => {
163238 if (! isLogin ()) {
@@ -169,7 +244,11 @@ const isLogout = () => {
169244 content: " 确认退出当前用户登录?" ,
170245 positiveText: " 确认登出" ,
171246 negativeText: " 取消" ,
172- onPositiveClick : () => toLogout (),
247+ onPositiveClick : () => {
248+ // 退出时保存当前账号,方便下次登录
249+ saveCurrentAccount ();
250+ toLogout ();
251+ },
173252 });
174253};
175254
@@ -233,6 +312,7 @@ onBeforeMount(() => {
233312 .user-info {
234313 .nickname {
235314 font-weight : bold ;
315+ max-width : 220px ;
236316 }
237317 .n-tag {
238318 height : 18px ;
@@ -253,9 +333,56 @@ onBeforeMount(() => {
253333 .n-text {
254334 font-size : 12px ;
255335 font-weight : normal ;
336+ margin-top : 4px ;
256337 }
257338 }
258339 }
340+ .account-list {
341+ .subtitle {
342+ font-size : 12px ;
343+ text-align : center ;
344+ margin-bottom : 8px ;
345+ display : block ;
346+ }
347+ .account-item {
348+ display : flex ;
349+ align-items : center ;
350+ padding : 6px 8px ;
351+ margin-bottom : 8px ;
352+ border-radius : 8px ;
353+ cursor : pointer ;
354+ transition : background-color 0.2s ;
355+ position : relative ;
356+ .account-name {
357+ margin-left : 8px ;
358+ font-size : 13px ;
359+ flex : 1 ;
360+ }
361+ .delete-btn {
362+ opacity : 0 ;
363+ padding : 4px ;
364+ border-radius : 4px ;
365+ display : flex ;
366+ align-items : center ;
367+ transition :
368+ background-color 0.2s ,
369+ color 0.2s ;
370+ & :hover {
371+ background-color : rgba (var (--primary ), 0.1 );
372+ color : var (--primary-color );
373+ }
374+ }
375+ & :hover {
376+ background-color : rgba (var (--primary ), 0.08 );
377+ .delete-btn {
378+ opacity : 1 ;
379+ }
380+ }
381+ }
382+ .add-account {
383+ border-radius : 8px ;
384+ }
385+ }
259386 .n-divider {
260387 margin : 12px 0 ;
261388 }
0 commit comments