shuisheng 2 лет назад
Родитель
Сommit
43acb69457

+ 27 - 0
src/api/user.ts

@@ -1,6 +1,33 @@
 import { IPaging, IUser } from '@/interface';
 import request from '@/utils/request';
 
+export function fetchQrcodeLogin({ platform, exp }) {
+  return request.post<{
+    login_id: string;
+    exp: any;
+    platform: any;
+    isLogin: boolean;
+    token: string;
+  }>('/user/qrcode_login', {
+    platform,
+    exp,
+  });
+}
+
+// eslint-disable-next-line
+export function fetchQrcodeLoginStatus({ platform, login_id }) {
+  return request.get<{
+    login_id: string;
+    exp: any;
+    platform: any;
+    isLogin: boolean;
+    token: string;
+  }>('/user/qrcode_login', {
+    // eslint-disable-next-line
+    params: { platform, login_id },
+  });
+}
+
 export function fetchLogin({ id, password }) {
   return request.instance({
     url: '/user/login',

+ 10 - 0
src/api/wechatUser.ts

@@ -0,0 +1,10 @@
+import request from '@/utils/request';
+
+// qq登录
+export function fetchWechatLogin(code: any) {
+  return request.instance({
+    url: `/wechat_user/login`,
+    method: 'post',
+    data: { code },
+  });
+}

BIN
src/assets/img/qq-logo.webp


BIN
src/assets/img/wechat_logo.webp


+ 70 - 13
src/components/LoginModal/index.vue

@@ -74,6 +74,17 @@
                 登录
               </n-button>
             </n-tab-pane>
+            <n-tab-pane
+              name="qrcodelogin"
+              tab="微信登录"
+            >
+              <div
+                class="qrcode"
+                :style="{
+                  backgroundImage: `url(${base64})`,
+                }"
+              ></div>
+            </n-tab-pane>
           </n-tabs>
         </n-card>
         <div class="other-login">
@@ -83,16 +94,25 @@
             @click="handleQQLogin()"
           >
             <img
-              class="qq-logo"
+              class="logo"
               src="@/assets/img/qq_logo.webp"
             />
           </div>
+          <!-- <div
+            class="logo-wrap"
+            @click="handleWechatLogin()"
+          >
+            <img
+              class="logo"
+              src="@/assets/img/wechat_logo.webp"
+            />
+          </div> -->
           <div
             class="logo-wrap"
             @click="handleGithubLogin"
           >
             <img
-              class="qq-logo"
+              class="logo"
               src="@/assets/img/github_logo.webp"
             />
           </div>
@@ -104,8 +124,11 @@
 
 <script lang="ts" setup>
 import { LockClosedOutline, PersonOutline } from '@vicons/ionicons5';
+import QRCode from 'qrcode';
 import { ref } from 'vue';
 
+import { fetchQrcodeLogin } from '@/api/user';
+import { QRCODE_LOGIN_URI } from '@/constant';
 import { useQQLogin } from '@/hooks/use-login';
 import { useAppStore } from '@/store/app';
 import { useUserStore } from '@/store/user';
@@ -122,22 +145,47 @@ const loginForm = ref({
   id: '',
   password: '',
 });
-const registerForm = ref({
-  email: '',
-  code: '',
-});
+const base64 = ref('');
 const loginFormRef = ref(null);
-const currentTab = ref('pwdlogin');
+const currentTab = ref('qrcodelogin'); // qrcodelogin,pwdlogin
 
 const emits = defineEmits(['close']);
 
+async function generateQR(text) {
+  let base64 = '';
+  try {
+    base64 = await QRCode.toDataURL(text, {
+      margin: 1,
+    });
+  } catch (err) {
+    console.error('生成二维码失败!', err);
+  }
+  return base64;
+}
+
 function handleGithubLogin() {
   window.$message.warning('敬请期待!');
 }
+
 function handleQQLogin() {
   useQQLogin();
 }
 
+async function handleWechatLogin() {
+  const params = {
+    platform: 'wechat',
+    exp: 24,
+  };
+  const res = await fetchQrcodeLogin(params);
+  if (res.code === 200) {
+    base64.value = await generateQR(
+      `${QRCODE_LOGIN_URI}?platform=${params.platform}&exp=${params.exp}&login_id=${res.data.login_id}`
+    );
+  } else {
+    window.$message.error(res.message);
+  }
+}
+
 function handleClose() {
   appStore.showLoginModal = false;
   emits('close');
@@ -145,11 +193,8 @@ function handleClose() {
 
 const handleLogin = async () => {
   let token = null;
-  if (currentTab.value === 'codelogin') {
-    token = await userStore.codeLogin({
-      email: registerForm.value.email,
-      code: registerForm.value.code,
-    });
+  if (currentTab.value === 'qrcodelogin') {
+    handleWechatLogin();
   } else {
     token = await userStore.pwdLogin({
       id: +loginForm.value.id,
@@ -173,6 +218,9 @@ const handleLoginSubmit = (e) => {
 };
 const tabChange = (v) => {
   currentTab.value = v;
+  if (currentTab.value === 'qrcodelogin') {
+    handleWechatLogin();
+  }
 };
 const focus = ref(false);
 const onFocus = () => {
@@ -196,6 +244,15 @@ const onBlur = () => {
     border-radius: 5px;
     background-color: #fff;
     transform: translate(-50%, -50%);
+    .qrcode {
+      width: 166px;
+      height: 166px;
+      margin: 0 auto;
+      background-color: gray;
+      background-size: contain;
+      background-repeat: no-repeat;
+      background-position: center center;
+    }
     .close {
       position: absolute;
       top: 20px;
@@ -240,7 +297,7 @@ const onBlur = () => {
         border-radius: 50%;
         background-color: #f4f8fb;
         cursor: pointer;
-        .qq-logo {
+        .logo {
           width: 26px;
           height: 26px;
         }

+ 8 - 0
src/constant.ts

@@ -4,6 +4,14 @@ export const QQ_CLIENT_ID = '101958191';
 export const QQ_OAUTH_URL = 'https://graph.qq.com/oauth2.0';
 export const QQ_REDIRECT_URI = 'https://live.hsslive.cn/oauth/qq_login';
 
+export const WECHAT_GZH_APPID = 'wxbd243c01ac5ad1b7'; // 公众号
+export const WECHAT_GZH_OAUTH_URL =
+  'https://open.weixin.qq.com/connect/oauth2/authorize?';
+
+export const WECHAT_REDIRECT_URI = `http://www.hfgmupw.cn/oauth/wechat_login`;
+
+export const QRCODE_LOGIN_URI = `http://www.hfgmupw.cn/qrcodeLogin`;
+
 export const AUTHOR_GITHUB = 'https://github.com/galaxy-s10';
 
 // wss://srs-pull.hsslive.cn

+ 34 - 4
src/hooks/use-login.ts

@@ -1,9 +1,16 @@
-import { hrefToTarget, isMobile } from 'billd-utils';
+import { hrefToTarget, isMobile, isWechat } from 'billd-utils';
 import { createApp } from 'vue';
 
 import { fetchQQLogin } from '@/api/qqUser';
 import { fullLoading } from '@/components/FullLoading';
-import { QQ_CLIENT_ID, QQ_OAUTH_URL, QQ_REDIRECT_URI } from '@/constant';
+import {
+  QQ_CLIENT_ID,
+  QQ_OAUTH_URL,
+  QQ_REDIRECT_URI,
+  WECHAT_GZH_APPID,
+  WECHAT_GZH_OAUTH_URL,
+  WECHAT_REDIRECT_URI,
+} from '@/constant';
 import LoginModalCpt from '@/hooks/loginModal/index.vue';
 import { PlatformEnum } from '@/interface';
 import { useAppStore } from '@/store/app';
@@ -21,7 +28,7 @@ document.body.appendChild(container);
 
 const POSTMESSAGE_TYPE = [PlatformEnum.qqLogin];
 
-export async function handleLogin(e) {
+export async function handleQQLogin(e) {
   const { type, data } = e.data;
   if (!POSTMESSAGE_TYPE.includes(type)) return;
   console.log('收到消息', type, data);
@@ -65,7 +72,7 @@ export function loginTip(show = false) {
 }
 
 export function loginMessage() {
-  window.addEventListener('message', handleLogin);
+  window.addEventListener('message', handleQQLogin);
 }
 
 export function useQQLogin() {
@@ -96,3 +103,26 @@ export function useQQLogin() {
     );
   }
 }
+
+export const useWechatLogin = (qrData: { platform; exp; login_id }) => {
+  const redirectUri = encodeURIComponent(WECHAT_REDIRECT_URI);
+  const flag = isWechat();
+  if (flag) {
+    window.$message.error('请在微信打开!');
+    return;
+  }
+  const loginInfo = JSON.stringify({
+    isMobile: false,
+    createTime: +new Date(),
+    env: 'wechat',
+    dev: process.env.NODE_ENV === 'development',
+    qr_platform: qrData.platform,
+    qr_exp: qrData.exp,
+    qr_login_id: qrData.login_id,
+  });
+  setLoginInfo(loginInfo);
+  const stateRes = window.btoa(loginInfo);
+  // https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html
+  const url = `${WECHAT_GZH_OAUTH_URL}appid=${WECHAT_GZH_APPID}&redirect_uri=${redirectUri}&scope=snsapi_userinfo&response_type=code&state=${stateRes}`;
+  hrefToTarget(url);
+};

+ 1 - 0
src/interface.ts

@@ -280,6 +280,7 @@ export interface BilldHtmlWebpackPluginLog {
 
 export enum PlatformEnum {
   qqLogin = 'qq_login',
+  wechatLogin = 'wechat_login',
 }
 
 export interface IAuth {

+ 15 - 1
src/router/index.ts

@@ -6,12 +6,17 @@ import PcLayout from '@/layout/pc/index.vue';
 
 import type { RouteRecordRaw } from 'vue-router';
 
+export const commonRouterName = {
+  qrcodeLogin: 'qrcodeLogin',
+};
+
 export const mobileRouterName = {
   h5: 'h5',
   h5Room: 'h5Room',
   h5Area: 'h5Area',
   h5Rank: 'h5Rank',
   h5Profile: 'h5Profile',
+  ...commonRouterName,
 };
 
 export const routerName = {
@@ -186,7 +191,13 @@ export const defaultRoutes: RouteRecordRaw[] = [
     path: '/h5/:roomId',
     component: () => import('@/views/h5/room/index.vue'),
   },
+  {
+    name: commonRouterName.qrcodeLogin,
+    path: '/qrcodeLogin',
+    component: () => import('@/views/qrcodeLogin/index.vue'),
+  },
 ];
+
 const router = createRouter({
   routes: [
     ...defaultRoutes,
@@ -203,7 +214,10 @@ router.beforeEach((to, from, next) => {
   if (to.name === routerName.oauth) {
     return next();
   }
-  if (isMobile() && !isIPad()) {
+  if (Object.keys(commonRouterName).includes(to.name as string)) {
+    // 跳转通用路由
+    return next();
+  } else if (isMobile() && !isIPad()) {
     if (!Object.keys(mobileRouterName).includes(to.name as string)) {
       // 当前移动端,但是跳转了非移动端路由
       console.log('当前移动端,但是跳转了非移动端路由', to, from);

+ 15 - 7
src/views/h5/room/index.vue

@@ -24,7 +24,10 @@
     <div
       v-loading="videoLoading"
       class="video-wrap"
-      :style="{ height: videoWrapHeight + 'px' }"
+      :style="{
+        height: videoWrapHeight + 'px',
+        '--max-height': videoWrapHeight + 'px',
+      }"
     >
       <div
         class="cover"
@@ -262,10 +265,10 @@ onMounted(() => {
   .video-wrap {
     position: relative;
     overflow: hidden;
-    flex: 1;
     background-color: rgba($color: #000000, $alpha: 0.5);
     .cover {
       position: absolute;
+      z-index: -1;
       background-position: center center;
       background-size: cover;
       filter: blur(10px);
@@ -283,16 +286,21 @@ onMounted(() => {
     }
     .media-list {
       position: relative;
-      overflow-y: scroll;
       :deep(video) {
+        position: absolute;
+        left: 50%;
         display: block;
-        width: 100%;
-        height: 100%;
+        max-width: 100vw;
+        max-height: var(--max-height);
+        transform: translateX(-50%);
       }
       :deep(canvas) {
+        position: absolute;
+        left: 50%;
         display: block;
-        width: 100%;
-        height: 100%;
+        max-width: 100vw;
+        max-height: var(--max-height);
+        transform: translateX(-50%);
       }
     }
     .tip-btn {

+ 17 - 3
src/views/oauth/index.vue

@@ -7,7 +7,8 @@
 import { onMounted, ref } from 'vue';
 import { useRoute } from 'vue-router';
 
-import { handleLogin } from '@/hooks/use-login';
+import { fetchQrcodeLoginStatus } from '@/api/user';
+import { handleQQLogin } from '@/hooks/use-login';
 import { PlatformEnum } from '@/interface';
 import { clearLoginInfo, getLoginInfo } from '@/utils/cookie';
 
@@ -57,16 +58,19 @@ onMounted(async () => {
     case PlatformEnum.qqLogin:
       currentOauth.value = 'QQ';
       break;
+    case PlatformEnum.wechatLogin:
+      currentOauth.value = 'Wechat';
+      break;
   }
 
   try {
     const { isMobile, env } = JSON.parse(loginInfo);
-    const info = { type: PlatformEnum.qqLogin, data: code };
 
     if (env === 'qq') {
+      const info = { type: PlatformEnum.qqLogin, data: code };
       if (isMobile) {
         try {
-          await handleLogin({
+          await handleQQLogin({
             data: info,
           });
         } catch (error) {
@@ -76,6 +80,16 @@ onMounted(async () => {
         window.opener.postMessage(info, '*');
         window.close();
       }
+    } else if (env === 'wechat') {
+      // eslint-disable-next-line
+      const { qr_platform, qr_login_id } = JSON.parse(loginInfo);
+      console.log(qr_platform, qr_login_id, '====');
+      await fetchQrcodeLoginStatus({
+        // eslint-disable-next-line
+        platform: qr_platform,
+        // eslint-disable-next-line
+        login_id: qr_login_id,
+      });
     }
   } catch (error) {
     console.log(error);

+ 4 - 0
src/views/pull/index.vue

@@ -408,6 +408,9 @@ async function uploadChange() {
     } finally {
       msgIsFile.value = false;
       msgLoading.value = false;
+      if (uploadRef.value) {
+        uploadRef.value.value = '';
+      }
     }
   }
 }
@@ -788,6 +791,7 @@ function handleScrollTop() {
       }
     }
     .send-msg {
+      position: relative;
       box-sizing: border-box;
       padding: 4px 10px;
       width: 100%;

+ 55 - 0
src/views/qrcodeLogin/index.vue

@@ -0,0 +1,55 @@
+<template>
+  <div class="qrcodeLogin-wrap">
+    <div
+      class="btn"
+      @click="handleClick"
+    >
+      确认登录
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { useRoute } from 'vue-router';
+
+import { useWechatLogin } from '@/hooks/use-login';
+
+const route = useRoute();
+
+function handleClick() {
+  // eslint-disable-next-line
+  const { platform, exp, login_id } = route.query;
+  // eslint-disable-next-line
+  if (!platform || !exp || !login_id) {
+    window.$message.error('参数缺失!');
+    return;
+  }
+  if (platform === 'wechat') {
+    // eslint-disable-next-line
+    useWechatLogin({ platform, exp, login_id });
+  } else {
+    window.$message.error('platform错误!');
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.qrcodeLogin-wrap {
+  .btn {
+    position: fixed;
+    top: 50%;
+    left: 50%;
+    padding: 10px;
+    width: 100px;
+    border-radius: 10px;
+    background-color: $theme-color-gold;
+    color: white;
+    text-align: center;
+    font-size: 16px;
+    cursor: pointer;
+    transform: translate(-50%, -50%);
+
+    user-select: none;
+  }
+}
+</style>

+ 5 - 0
test/test.js

@@ -0,0 +1,5 @@
+let a = {
+  aaa: 1,
+  gg: '2',
+};
+console.log(Object.keys(a));