278 lines
6.7 KiB
Vue
278 lines
6.7 KiB
Vue
<template>
|
||
<button
|
||
class="google-oauth-button"
|
||
:disabled="loading"
|
||
@click="handleGoogleLogin"
|
||
>
|
||
<!-- Google Logo -->
|
||
<div class="google-logo">
|
||
<svg width="18" height="18" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
||
<path
|
||
fill="#4285F4"
|
||
d="M16.51 8.44c0-.62-.05-1.22-.15-1.8H8.98v3.4h4.05c-.17 1-.75 1.85-1.6 2.42v2.01h2.59c1.52-1.4 2.39-3.46 2.39-5.89z"
|
||
/>
|
||
<path
|
||
fill="#34A853"
|
||
d="M8.98 17.5c2.43 0 4.47-.8 5.96-2.17l-2.59-2.01c-.8.54-1.83.86-2.99.86-2.3 0-4.24-1.55-4.93-3.64H2.18v2.07C3.99 15.95 6.26 17.5 8.98 17.5z"
|
||
/>
|
||
<path
|
||
fill="#FBBC05"
|
||
d="M4.05 10.91c-.15-.54-.23-1.12-.23-1.91s.08-1.37.23-1.91V4.94H2.18C1.55 6.16 1.2 7.55 1.2 9s.35 2.84.98 4.06l2.87-2.15z"
|
||
/>
|
||
<path
|
||
fill="#EA4335"
|
||
d="M8.98 4.18c1.17 0 2.22.4 3.06 1.18l2.3-2.3C13.46 1.8 11.41 1.2 8.98 1.2 6.26 1.2 3.99 2.75 2.18 4.94l2.87 2.15c.69-2.09 2.63-3.64 4.93-3.64z"
|
||
/>
|
||
</svg>
|
||
</div>
|
||
|
||
<!-- 按钮文字 -->
|
||
<span class="button-text">
|
||
<span v-if="!loading">{{ t('login.google_login') }}</span>
|
||
<span v-else>{{ t('login.google_logging') }}</span>
|
||
</span>
|
||
|
||
<!-- 加载状态指示器 -->
|
||
<div v-if="loading" class="loading-spinner"></div>
|
||
</button>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref } from 'vue'
|
||
import { useAuthStore } from '@/stores/auth'
|
||
import { useI18n } from 'vue-i18n'
|
||
import { requestUtils,clientApi } from '@deotaland/utils'
|
||
const authStore = useAuthStore()
|
||
const { t } = useI18n()
|
||
const emit = defineEmits(['success', 'error'])
|
||
const props = defineProps({
|
||
loading: {
|
||
type: Boolean,
|
||
default: false
|
||
}
|
||
})
|
||
|
||
const isProcessing = ref(false)
|
||
// 动态加载 Google Identity Services 脚本
|
||
const loadGoogleScript = () => {
|
||
return new Promise((resolve, reject) => {
|
||
if (window.google && window.google.accounts && window.google.accounts.id) {
|
||
return resolve()
|
||
}
|
||
const script = document.createElement('script')
|
||
script.src = 'https://accounts.google.com/gsi/client'
|
||
script.async = true
|
||
script.defer = true
|
||
script.onload = () => resolve()
|
||
script.onerror = (e) => reject(new Error('Google Identity Services 脚本加载失败'))
|
||
document.head.appendChild(script)
|
||
})
|
||
}
|
||
// 处理 Google 登录
|
||
const handleGoogleLogin = async () => {
|
||
if (isProcessing.value ) return
|
||
isProcessing.value = true
|
||
await loadGoogleScript()
|
||
// const clientId = '680509991778-f5qgqbampabs1atblvm1jkoi4itl1nni.apps.googleusercontent.com'
|
||
const clientId = '1087356879940-kcdvstc2pgoim27gffl67qrs297avdmb.apps.googleusercontent.com'
|
||
const callback = async (response) => {
|
||
console.log(response,'responseresponseresponseresponse');
|
||
const idToken = response && response.credential
|
||
if (!idToken) {
|
||
errorMessage.value = '未获取到 Google 身份凭证'
|
||
return
|
||
}
|
||
await loginWithidToken(idToken)
|
||
}
|
||
// 初始化并触发 One Tap 或弹出
|
||
window.google.accounts.id.initialize({ client_id: clientId, callback })
|
||
// 尝试弹出一键登录(如果浏览器允许)
|
||
window.google.accounts.id.prompt()
|
||
}
|
||
const loginWithidToken = async (idToken) => {
|
||
try {clientApi
|
||
const res = await requestUtils.common(clientApi.default.OAUTH_GOOGLE,{
|
||
googleIdToken:idToken
|
||
})
|
||
if(res.code === 200){
|
||
// 登录成功,保存token和用户信息
|
||
let data = res.data;
|
||
authStore.loginSuccess(data,()=>{
|
||
emit('success', res.data.user)
|
||
})
|
||
return
|
||
emit('success', res.data.user)
|
||
return res
|
||
}
|
||
return res
|
||
} catch (error) {
|
||
console.error('登录失败:', error)
|
||
emit('error', error)
|
||
throw error
|
||
} finally {
|
||
isProcessing.value = false
|
||
}
|
||
}
|
||
onMounted(() => {
|
||
loadGoogleScript()
|
||
})
|
||
// 注意:在实际项目中,这里需要集成真实的 Google OAuth SDK
|
||
// 例如使用 @google-cloud/local-auth 或类似库
|
||
// 真实的实现需要:
|
||
// 1. 配置 Google OAuth 客户端 ID
|
||
// 2. 处理 OAuth 授权流程
|
||
// 3. 验证 Google ID Token
|
||
// 4. 调用后端 API 创建用户会话
|
||
</script>
|
||
|
||
<style scoped>
|
||
/* Google 登录按钮样式 */
|
||
.google-oauth-button {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 12px;
|
||
width: 100%;
|
||
height: 48px;
|
||
background: white;
|
||
border: 1px solid #D1D5DB;
|
||
border-radius: 12px;
|
||
color: #374151;
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
position: relative;
|
||
overflow: hidden;
|
||
box-shadow:
|
||
0 1px 2px 0 rgba(0, 0, 0, 0.05),
|
||
0 1px 3px 0 rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.google-oauth-button:hover:not(:disabled) {
|
||
background: #F9FAFB;
|
||
border-color: #9CA3AF;
|
||
box-shadow:
|
||
0 4px 6px -1px rgba(0, 0, 0, 0.1),
|
||
0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||
transform: translateY(-1px);
|
||
}
|
||
|
||
.google-oauth-button:active:not(:disabled) {
|
||
transform: translateY(0);
|
||
box-shadow:
|
||
0 2px 4px -1px rgba(0, 0, 0, 0.1),
|
||
0 1px 2px -1px rgba(0, 0, 0, 0.06);
|
||
}
|
||
|
||
.google-oauth-button:focus {
|
||
outline: none;
|
||
ring: 2px solid #6B46C1;
|
||
ring-offset: 2px;
|
||
ring-offset-color: white;
|
||
}
|
||
|
||
.google-oauth-button:disabled {
|
||
background: #F3F4F6;
|
||
border-color: #E5E7EB;
|
||
color: #9CA3AF;
|
||
cursor: not-allowed;
|
||
transform: none;
|
||
}
|
||
|
||
/* Google Logo */
|
||
.google-logo {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 18px;
|
||
height: 18px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* 按钮文字 */
|
||
.button-text {
|
||
flex: 1;
|
||
text-align: center;
|
||
font-weight: 500;
|
||
letter-spacing: 0.025em;
|
||
}
|
||
|
||
/* 加载状态指示器 */
|
||
.loading-spinner {
|
||
position: absolute;
|
||
right: 16px;
|
||
width: 18px;
|
||
height: 18px;
|
||
border: 2px solid transparent;
|
||
border-top: 2px solid #6B7280;
|
||
border-radius: 50%;
|
||
animation: spin 1s linear infinite;
|
||
}
|
||
|
||
@keyframes spin {
|
||
to {
|
||
transform: rotate(360deg);
|
||
}
|
||
}
|
||
|
||
/* 按钮激活状态动画 */
|
||
.google-oauth-button::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: -100%;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: linear-gradient(90deg, transparent, rgba(139, 92, 246, 0.1), transparent);
|
||
transition: left 0.5s ease;
|
||
}
|
||
|
||
.google-oauth-button:hover::before {
|
||
left: 100%;
|
||
}
|
||
|
||
/* 响应式设计 */
|
||
@media (max-width: 768px) {
|
||
.google-oauth-button {
|
||
height: 44px;
|
||
font-size: 15px;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 480px) {
|
||
.google-oauth-button {
|
||
height: 42px;
|
||
font-size: 14px;
|
||
}
|
||
}
|
||
|
||
.google-oauth-button:hover::before {
|
||
left: 100%;
|
||
}
|
||
|
||
/* 移动端优化 */
|
||
@media (max-width: 768px) {
|
||
.google-oauth-button {
|
||
height: 44px;
|
||
font-size: 15px;
|
||
}
|
||
|
||
.button-text {
|
||
font-size: 15px;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 480px) {
|
||
.google-oauth-button {
|
||
height: 42px;
|
||
font-size: 14px;
|
||
gap: 10px;
|
||
}
|
||
|
||
.google-logo {
|
||
width: 16px;
|
||
height: 16px;
|
||
}
|
||
}
|
||
</style> |