Parcourir la source

facebook一键登录

urbanu il y a 1 mois
Parent
commit
ff2e3f9145

+ 2 - 2
src/api/auth.ts

@@ -99,7 +99,7 @@ export function requestGetAuthUrl(redirectUri: string): Promise<CommonResponse<{
 
 // OAuth登录参数
 interface OAuthLoginParams {
-  provider: 'google' | 'zalo' | 'telegram' | 'tiktok';
+  provider: 'google' | 'zalo' | 'telegram' | 'tiktok' | 'facebook';
   openId: string;
   nickname?: string;
   avatar?: string;
@@ -118,7 +118,7 @@ export function requestOAuthLogin(data: OAuthLoginParams): Promise<CommonRespons
 }
 
 // 获取OAuth配置
-export function requestOAuthConfig(): Promise<CommonResponse<{ googleClientId?: string; zaloAppId?: string; telegramBotName?: string; telegramBotId?: string; tiktokClientKey?: string }>> {
+export function requestOAuthConfig(): Promise<CommonResponse<{ googleClientId?: string; zaloAppId?: string; telegramBotName?: string; telegramBotId?: string; tiktokClientKey?: string; facebookAppId?: string }>> {
   return http.request({
     url: "/api/v1/dt/config/oauth",
     method: "get"

+ 3 - 0
src/locales/en.json

@@ -73,6 +73,9 @@
     "tiktokLogin": "Sign in with TikTok",
     "tiktokLoginFailed": "TikTok login failed",
     "tiktokNotConfigured": "TikTok login not configured",
+    "facebookLogin": "Sign in with Facebook",
+    "facebookLoginFailed": "Facebook login failed",
+    "facebookNotConfigured": "Facebook login not configured",
     "logoutConfirm": "Are you sure you want to log out?"
   },
   "terms": {

+ 3 - 0
src/locales/id.json

@@ -73,6 +73,9 @@
     "tiktokLogin": "Masuk dengan TikTok",
     "tiktokLoginFailed": "Gagal masuk TikTok",
     "tiktokNotConfigured": "Login TikTok tidak dikonfigurasi",
+    "facebookLogin": "Masuk dengan Facebook",
+    "facebookLoginFailed": "Gagal masuk Facebook",
+    "facebookNotConfigured": "Login Facebook tidak dikonfigurasi",
     "logoutConfirm": "Apakah Anda yakin ingin keluar?"
   },
   "terms": {

+ 3 - 0
src/locales/vi.json

@@ -73,6 +73,9 @@
     "tiktokLogin": "Đăng nhập bằng TikTok",
     "tiktokLoginFailed": "Đăng nhập TikTok thất bại",
     "tiktokNotConfigured": "Đăng nhập TikTok chưa được cấu hình",
+    "facebookLogin": "Đăng nhập bằng Facebook",
+    "facebookLoginFailed": "Đăng nhập Facebook thất bại",
+    "facebookNotConfigured": "Đăng nhập Facebook chưa được cấu hình",
     "logoutConfirm": "Bạn có chắc chắn muốn đăng xuất?"
   },
   "terms": {

+ 3 - 0
src/locales/zh.json

@@ -73,6 +73,9 @@
     "tiktokLogin": "TikTok 登录",
     "tiktokLoginFailed": "TikTok 登录失败",
     "tiktokNotConfigured": "TikTok 登录未配置",
+    "facebookLogin": "Facebook 登录",
+    "facebookLoginFailed": "Facebook 登录失败",
+    "facebookNotConfigured": "Facebook 登录未配置",
     "logoutConfirm": "确定要退出登录吗?"
   },
   "terms": {

+ 103 - 1
src/views/login/index.vue

@@ -156,6 +156,15 @@
           <span>{{ $t('auth.zaloLogin') || '使用 Zalo 登录' }}</span>
         </div>
 
+        <!-- Facebook 登录按钮 -->
+        <div class="oauth-btn facebook-btn" :class="{ loading: facebookLoading }" @click="handleFacebookLogin" v-if="facebookAppId">
+          <svg class="facebook-icon" viewBox="0 0 24 24" width="20" height="20">
+            <path fill="#fff" d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/>
+          </svg>
+          <span>{{ $t('auth.facebookLogin') || 'Facebook 登录' }}</span>
+          <van-loading v-if="facebookLoading" size="16px" color="#fff" />
+        </div>
+
         <!-- TikTok 登录按钮 -->
         <div class="oauth-btn tiktok-btn" :class="{ loading: tiktokLoading }" @click="handleTiktokLogin" v-if="tiktokClientKey">
           <svg class="tiktok-icon" viewBox="0 0 24 24" width="20" height="20">
@@ -205,6 +214,8 @@ const zaloLoading = ref(false);
 const zaloAppId = ref('');
 const tiktokLoading = ref(false);
 const tiktokClientKey = ref('');
+const facebookLoading = ref(false);
+const facebookAppId = ref('');
 
 const loginType = ref<'phone' | 'email'>('phone');
 const showPassword = ref(false);
@@ -339,6 +350,12 @@ const initOAuth = async () => {
         tiktokClientKey.value = configRes.data.tiktokClientKey;
         initTiktokLogin();
       }
+
+      // 初始化 Facebook Login
+      if (configRes.data.facebookAppId) {
+        facebookAppId.value = configRes.data.facebookAppId;
+        initFacebookSDK(configRes.data.facebookAppId);
+      }
     }
   } catch (error) {
     console.warn('Failed to load OAuth config:', error);
@@ -704,6 +721,82 @@ const handleTiktokLogin = () => {
   window.location.href = authUrl;
 };
 
+// 初始化 Facebook SDK
+const initFacebookSDK = (appId: string) => {
+  // 避免重复加载
+  if (document.getElementById('facebook-jssdk')) return;
+
+  window.fbAsyncInit = function() {
+    window.FB.init({
+      appId: appId,
+      cookie: true,
+      xfbml: false,
+      version: 'v19.0'
+    });
+  };
+
+  const script = document.createElement('script');
+  script.id = 'facebook-jssdk';
+  script.src = 'https://connect.facebook.net/en_US/sdk.js';
+  script.async = true;
+  script.defer = true;
+  document.head.appendChild(script);
+};
+
+// 触发 Facebook 登录
+const handleFacebookLogin = () => {
+  if (!facebookAppId.value) {
+    toast.error(t('auth.facebookNotConfigured') || 'Facebook 登录未配置');
+    return;
+  }
+
+  if (!window.FB) {
+    toast.error('Facebook SDK 加载中,请稍后再试');
+    return;
+  }
+
+  facebookLoading.value = true;
+  window.FB.login((response: any) => {
+    if (response.authResponse) {
+      const accessToken = response.authResponse.accessToken;
+      // 获取用户信息
+      window.FB.api('/me', { fields: 'id,name,email,picture.width(200)' }, (userInfo: any) => {
+        handleFacebookCallback(userInfo, accessToken);
+      });
+    } else {
+      facebookLoading.value = false;
+    }
+  }, { scope: 'public_profile,email' });
+};
+
+// Facebook 登录回调
+const handleFacebookCallback = async (userInfo: any, accessToken: string) => {
+  try {
+    const res = await requestOAuthLogin({
+      provider: 'facebook',
+      openId: userInfo.id,
+      nickname: userInfo.name || '',
+      avatar: userInfo.picture?.data?.url || '',
+      email: userInfo.email || '',
+      extra: accessToken
+    });
+
+    if (res.code === 200) {
+      localStorage.setItem('token', res.data.token);
+      userStore.setUserInfo(res.data.user);
+      toast.success(t('auth.loginSuccess'));
+      router.replace('/home');
+    } else {
+      toast.error(res.msg || t('auth.facebookLoginFailed') || 'Facebook 登录失败');
+    }
+  } catch (error) {
+    console.error('Facebook login error:', error);
+    toast.error(t('auth.facebookLoginFailed') || 'Facebook 登录失败');
+  } finally {
+    facebookLoading.value = false;
+  }
+};
+
 const goForgotPassword = () => {
   router.push('/forgot-password');
 };
@@ -882,10 +975,19 @@ const goRegister = () => {
       opacity: 0.7;
     }
 
-    .google-icon, .zalo-icon, .telegram-icon, .tiktok-icon {
+    .google-icon, .zalo-icon, .telegram-icon, .tiktok-icon, .facebook-icon {
       flex-shrink: 0;
     }
 
+    &.facebook-btn {
+      background: #1877F2;
+      color: #fff;
+
+      &:active {
+        background: #1565C0;
+      }
+    }
+
     &.google-btn {
       background: #fff;
       color: #333;

+ 103 - 1
src/views/register/index.vue

@@ -236,6 +236,15 @@
           <span>{{ $t('auth.telegramLogin') || 'Telegram 登录' }}</span>
         </div>
 
+        <!-- Facebook 登录按钮 -->
+        <div class="oauth-btn facebook-btn" :class="{ loading: facebookLoading }" @click="handleFacebookLogin" v-if="facebookAppId">
+          <svg class="facebook-icon" viewBox="0 0 24 24" width="20" height="20">
+            <path fill="#fff" d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/>
+          </svg>
+          <span>{{ $t('auth.facebookLogin') || 'Facebook 登录' }}</span>
+          <van-loading v-if="facebookLoading" size="16px" color="#fff" />
+        </div>
+
         <!-- TikTok 登录按钮 -->
         <div class="oauth-btn tiktok-btn" :class="{ loading: tiktokLoading }" @click="handleTiktokLogin" v-if="tiktokClientKey">
           <svg class="tiktok-icon" viewBox="0 0 24 24" width="20" height="20">
@@ -292,6 +301,8 @@ const telegramBotName = ref('');
 const telegramBotId = ref('');
 const tiktokLoading = ref(false);
 const tiktokClientKey = ref('');
+const facebookLoading = ref(false);
+const facebookAppId = ref('');
 
 const registerType = ref<'phone' | 'email'>('phone');
 const showPassword = ref(false);
@@ -486,6 +497,12 @@ const initGoogleSignIn = async () => {
       if (configRes.data.tiktokClientKey) {
         tiktokClientKey.value = configRes.data.tiktokClientKey;
       }
+
+      // 初始化 Facebook Login
+      if (configRes.data.facebookAppId) {
+        facebookAppId.value = configRes.data.facebookAppId;
+        initFacebookSDK(configRes.data.facebookAppId);
+      }
     }
   } catch (error) {
     console.warn('Failed to load OAuth config:', error);
@@ -620,6 +637,82 @@ const handleTelegramCallback = async (user: any) => {
 };
 
 
+// 初始化 Facebook SDK
+const initFacebookSDK = (appId: string) => {
+  if (document.getElementById('facebook-jssdk')) return;
+
+  window.fbAsyncInit = function() {
+    window.FB.init({
+      appId: appId,
+      cookie: true,
+      xfbml: false,
+      version: 'v19.0'
+    });
+  };
+
+  const script = document.createElement('script');
+  script.id = 'facebook-jssdk';
+  script.src = 'https://connect.facebook.net/en_US/sdk.js';
+  script.async = true;
+  script.defer = true;
+  document.head.appendChild(script);
+};
+
+// 触发 Facebook 登录
+const handleFacebookLogin = () => {
+  if (!facebookAppId.value) {
+    toast.error(t('auth.facebookNotConfigured') || 'Facebook 登录未配置');
+    return;
+  }
+
+  if (!window.FB) {
+    toast.error('Facebook SDK 加载中,请稍后再试');
+    return;
+  }
+
+  facebookLoading.value = true;
+  window.FB.login((response: any) => {
+    if (response.authResponse) {
+      const accessToken = response.authResponse.accessToken;
+      window.FB.api('/me', { fields: 'id,name,email,picture.width(200)' }, (userInfo: any) => {
+        handleFacebookCallback(userInfo, accessToken);
+      });
+    } else {
+      facebookLoading.value = false;
+    }
+  }, { scope: 'public_profile,email' });
+};
+
+// Facebook 登录回调
+const handleFacebookCallback = async (userInfo: any, accessToken: string) => {
+  try {
+    const inviteCode = phoneForm.inviteCode || emailForm.inviteCode || (route.query.smid as string) || '';
+    const res = await requestOAuthLogin({
+      provider: 'facebook',
+      openId: userInfo.id,
+      nickname: userInfo.name || '',
+      avatar: userInfo.picture?.data?.url || '',
+      email: userInfo.email || '',
+      extra: accessToken,
+      inviteCode
+    });
+
+    if (res.code === 200) {
+      localStorage.setItem('token', res.data.token);
+      userStore.setUserInfo(res.data.user);
+      toast.success(t('auth.loginSuccess'));
+      router.replace('/home');
+    } else {
+      toast.error(res.msg || t('auth.facebookLoginFailed') || 'Facebook 登录失败');
+    }
+  } catch (error) {
+    console.error('Facebook login error:', error);
+    toast.error(t('auth.facebookLoginFailed') || 'Facebook 登录失败');
+  } finally {
+    facebookLoading.value = false;
+  }
+};
+
 // 生成随机字符串
 const generateRandomString = (length: number): string => {
   const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
@@ -873,10 +966,19 @@ onMounted(() => {
       opacity: 0.7;
     }
 
-    .google-icon, .zalo-icon, .telegram-icon, .tiktok-icon {
+    .google-icon, .zalo-icon, .telegram-icon, .tiktok-icon, .facebook-icon {
       flex-shrink: 0;
     }
 
+    &.facebook-btn {
+      background: #1877F2;
+      color: #fff;
+
+      &:active {
+        background: #1565C0;
+      }
+    }
+
     &.google-btn {
       background: #fff;
       color: #333;