添加账户渠道显示

This commit is contained in:
SunWuyuan 2025-10-07 14:45:09 +08:00
parent 934cdb7040
commit 2363bb3c32
No known key found for this signature in database
GPG Key ID: A6A54CF66F56BB64
4 changed files with 66 additions and 14 deletions

View File

@ -20,14 +20,16 @@
v-for="provider in providers" v-for="provider in providers"
:key="provider.id" :key="provider.id"
@click="handleLogin(provider)" @click="handleLogin(provider)"
class="w-full flex items-center gap-3 px-4 py-3 border rounded-lg hover:bg-accent transition-colors" class="w-full flex items-center gap-3 px-4 py-3 border rounded-lg transition-colors hover:bg-accent"
:style="{ borderColor: provider.color + '20' }" :style="{
borderColor: (provider.color || '#666')
}"
> >
<div class="w-10 h-10 flex items-center justify-center rounded-lg" :style="{ backgroundColor: provider.color + '10' }"> <div class="w-10 h-10 flex items-center justify-center rounded-lg bg-muted">
<component :is="getProviderIcon(provider.icon)" class="w-6 h-6" :style="{ color: provider.color }" /> <component :is="getProviderIcon(provider.icon)" class="w-6 h-6" />
</div> </div>
<div class="flex-1 text-left"> <div class="flex-1 text-left">
<div class="font-medium">{{ provider.name }}</div> <div class="font-medium">{{ provider.displayName || provider.name }}</div>
<div class="text-sm text-muted-foreground">{{ provider.description }}</div> <div class="text-sm text-muted-foreground">{{ provider.description }}</div>
</div> </div>
<ChevronRight class="w-5 h-5 text-muted-foreground" /> <ChevronRight class="w-5 h-5 text-muted-foreground" />
@ -96,7 +98,9 @@ const getProviderIcon = (icon) => {
const loadProviders = async () => { const loadProviders = async () => {
try { try {
const response = await apiClient.getOAuthProviders() const response = await apiClient.getOAuthProviders()
providers.value = response.data || [] const list = response.data || []
// order
providers.value = list
} catch (error) { } catch (error) {
console.error('Failed to load OAuth providers:', error) console.error('Failed to load OAuth providers:', error)
toast.error('无法加载登录方式', { toast.error('无法加载登录方式', {
@ -142,8 +146,11 @@ const handleLogin = (provider) => {
authWindow.close() authWindow.close()
} }
const display = event.data.providerName || event.data.provider
const color = event.data.providerColor
toast.success('登录成功', { toast.success('登录成功', {
description: `已通过 ${event.data.provider} 登录` description: `已通过 ${display} 登录`,
style: color ? { borderLeft: `4px solid ${color}` } : undefined
}) })
// //
@ -180,11 +187,13 @@ const handleLogin = (provider) => {
// localStoragetoken // localStoragetoken
const token = localStorage.getItem('auth_token') const token = localStorage.getItem('auth_token')
const authProvider = localStorage.getItem('auth_provider') const authProvider = localStorage.getItem('auth_provider_name') || localStorage.getItem('auth_provider')
const providerColor = localStorage.getItem('auth_provider_color')
if (token) { if (token) {
toast.success('登录成功', { toast.success('登录成功', {
description: `已通过 ${authProvider} 登录` description: `已通过 ${authProvider || '账户'} 登录`,
style: providerColor ? { borderLeft: `4px solid ${providerColor}` } : undefined
}) })
// //
@ -211,7 +220,7 @@ const handleLogin = (provider) => {
description: '请重试' description: '请重试'
}) })
} }
}, 30000) }, 300000)
} }
onMounted(() => { onMounted(() => {

View File

@ -13,7 +13,7 @@ export function useOAuthCallback() {
const accountStore = useAccountStore() const accountStore = useAccountStore()
const handleOAuthCallback = async () => { const handleOAuthCallback = async () => {
const { token, provider, success, error } = route.query const { token, provider, color, success, error } = route.query
// 检查是否是OAuth回调 // 检查是否是OAuth回调
if (!success && !error) { if (!success && !error) {
@ -25,14 +25,15 @@ export function useOAuthCallback() {
try { try {
// 保存token到localStorage // 保存token到localStorage
localStorage.setItem('auth_token', token) localStorage.setItem('auth_token', token)
localStorage.setItem('auth_provider', provider) localStorage.setItem('auth_provider', provider)
if (color) localStorage.setItem('auth_provider_color', color)
// 登录到store // 登录到store
await accountStore.login(token) await accountStore.login(token)
// 显示成功提示 // 显示成功提示
toast.success('登录成功', { toast.success('登录成功', {
description: `已通过 ${provider} 登录` description: `已通过 ${provider} 登录`,
}) })
// 清除URL参数 // 清除URL参数
@ -51,7 +52,7 @@ export function useOAuthCallback() {
window.opener.postMessage({ window.opener.postMessage({
type: 'oauth_success', type: 'oauth_success',
token, token,
provider provider,
}, window.location.origin) }, window.location.origin)
// 延迟关闭窗口,确保消息已发送 // 延迟关闭窗口,确保消息已发送

View File

@ -418,9 +418,27 @@ onMounted(async () => {
> >
<User v-else class="h-4 w-4" /> <User v-else class="h-4 w-4" />
<span class="hidden sm:inline">{{ accountStore.userName }}</span> <span class="hidden sm:inline">{{ accountStore.userName }}</span>
<span v-if="accountStore.profile?.providerInfo"
class="hidden md:inline px-1.5 py-0.5 rounded text-[10px]"
:style="{
backgroundColor: (accountStore.profile.providerInfo.color || '#999') + '22',
color: accountStore.profile.providerInfo.color || 'inherit',
border: `1px solid ${(accountStore.profile.providerInfo.color || '#999')}55`
}"
>
{{ accountStore.profile.providerInfo.displayName }}
</span>
<ChevronDown class="h-3.5 w-3.5" :class="{ 'rotate-180': open }" /> <ChevronDown class="h-3.5 w-3.5" :class="{ 'rotate-180': open }" />
</Button> </Button>
</template> </template>
<DropdownItem @click="$router.push('/device-management')" :style="{
backgroundColor: (accountStore.profile.providerInfo.color || '#999') + '22',
color: accountStore.profile.providerInfo.color || 'inherit',
border: `1px solid ${(accountStore.profile.providerInfo.color || '#999')}55`
}">
<Layers class="h-4 w-4" />
账户渠道{{ accountStore.profile.providerInfo.displayName }}
</DropdownItem>
<DropdownItem @click="$router.push('/device-management')"> <DropdownItem @click="$router.push('/device-management')">
<Layers class="h-4 w-4" /> <Layers class="h-4 w-4" />
@ -447,6 +465,8 @@ onMounted(async () => {
</Button> </Button>
<Button <Button
variant="outline" variant="outline"
size="icon" size="icon"

View File

@ -8,11 +8,15 @@ export const useAccountStore = defineStore('account', () => {
const profile = ref(null) const profile = ref(null)
const devices = ref([]) const devices = ref([])
const loading = ref(false) const loading = ref(false)
const providerName = ref(localStorage.getItem('auth_provider') || '')
const providerColor = ref(localStorage.getItem('auth_provider_color') || '')
// 计算属性 // 计算属性
const isAuthenticated = computed(() => !!token.value) const isAuthenticated = computed(() => !!token.value)
const userName = computed(() => profile.value?.name || '') const userName = computed(() => profile.value?.name || '')
const userAvatar = computed(() => profile.value?.avatarUrl || '') const userAvatar = computed(() => profile.value?.avatarUrl || '')
const userProviderDisplay = computed(() => profile.value?.providerInfo?.displayName || profile.value?.providerInfo?.name || providerName.value || profile.value?.provider || '')
const userProviderColor = computed(() => profile.value?.providerInfo?.color || providerColor.value || '')
// 方法 // 方法
const setToken = (newToken) => { const setToken = (newToken) => {
@ -22,6 +26,9 @@ export const useAccountStore = defineStore('account', () => {
} else { } else {
localStorage.removeItem('auth_token') localStorage.removeItem('auth_token')
localStorage.removeItem('auth_provider') localStorage.removeItem('auth_provider')
localStorage.removeItem('auth_provider_color')
providerName.value = ''
providerColor.value = ''
} }
} }
@ -32,6 +39,14 @@ export const useAccountStore = defineStore('account', () => {
try { try {
const response = await apiClient.getAccountProfile(token.value) const response = await apiClient.getAccountProfile(token.value)
profile.value = response.data profile.value = response.data
// 若后端返回 providerInfo则回填前端展示字段
const p = profile.value?.providerInfo
if (p) {
providerName.value = p.displayName || p.name || profile.value?.provider || providerName.value
providerColor.value = p.color || providerColor.value
localStorage.setItem('auth_provider', providerName.value)
if (providerColor.value) localStorage.setItem('auth_provider_color', providerColor.value)
}
} catch (error) { } catch (error) {
console.error('Failed to load profile:', error) console.error('Failed to load profile:', error)
// Token可能无效清除 // Token可能无效清除
@ -86,6 +101,9 @@ export const useAccountStore = defineStore('account', () => {
devices.value = [] devices.value = []
localStorage.removeItem('auth_token') localStorage.removeItem('auth_token')
localStorage.removeItem('auth_provider') localStorage.removeItem('auth_provider')
localStorage.removeItem('auth_provider_color')
providerName.value = ''
providerColor.value = ''
} }
// 初始化时加载用户信息 // 初始化时加载用户信息
@ -100,10 +118,14 @@ export const useAccountStore = defineStore('account', () => {
profile, profile,
devices, devices,
loading, loading,
providerName,
providerColor,
// 计算属性 // 计算属性
isAuthenticated, isAuthenticated,
userName, userName,
userAvatar, userAvatar,
userProviderDisplay,
userProviderColor,
// 方法 // 方法
setToken, setToken,
loadProfile, loadProfile,