mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2025-12-13 09:28:14 +08:00
✨ 扫码登录
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
<template>
|
||||
<ToGameLogin v-model="showLoginQr" @success="tryGetTokens" />
|
||||
<v-card class="tcu-box">
|
||||
<template #prepend>
|
||||
<v-avatar :image="userInfo.avatar" />
|
||||
@@ -36,12 +37,18 @@
|
||||
</template>
|
||||
<template #actions>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
variant="outlined"
|
||||
@click="tryCaptchaLogin()"
|
||||
icon="mdi-cellphone"
|
||||
title="验证码登录"
|
||||
/>
|
||||
<!-- <v-btn-->
|
||||
<!-- variant="outlined"-->
|
||||
<!-- @click="showLoginQr = true"-->
|
||||
<!-- icon="mdi-qrcode-scan"-->
|
||||
<!-- title="扫码登录"-->
|
||||
<!-- />-->
|
||||
<!-- <v-btn-->
|
||||
<!-- variant="outlined"-->
|
||||
<!-- @click="tryCaptchaLogin()"-->
|
||||
<!-- icon="mdi-cellphone"-->
|
||||
<!-- title="验证码登录"-->
|
||||
<!-- />-->
|
||||
<v-btn
|
||||
variant="outlined"
|
||||
@click="confirmRefreshUser(uid!)"
|
||||
@@ -62,6 +69,7 @@
|
||||
variant="outlined"
|
||||
icon="mdi-account-switch"
|
||||
title="切换账户"
|
||||
:disabled="accounts.length === 0"
|
||||
@click="showMenu"
|
||||
v-bind="props"
|
||||
/>
|
||||
@@ -90,10 +98,25 @@
|
||||
/>
|
||||
</template>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
<v-menu location="start">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn variant="outlined" icon="mdi-login" title="登录" v-bind="props" />
|
||||
</template>
|
||||
<v-list>
|
||||
<v-list-item @click="addByCookie()" append-icon="mdi-account-plus">
|
||||
<v-list-item-title>手动添加</v-list-item-title>
|
||||
<v-list-item-subtitle>手动输入Cookie</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
<v-list-item @click="tryCaptchaLogin()" append-icon="mdi-cellphone">
|
||||
<v-list-item-title>验证码登录</v-list-item-title>
|
||||
<v-list-item-subtitle>使用手机号登录</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
<v-list-item @click="showLoginQr = true" append-icon="mdi-qrcode-scan">
|
||||
<v-list-item-title>扫码登录</v-list-item-title>
|
||||
<v-list-item-subtitle>使用米游社扫码登录</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</template>
|
||||
@@ -104,10 +127,11 @@ import showDialog from "@comp/func/dialog.js";
|
||||
import showGeetest from "@comp/func/geetest.js";
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import ToGameLogin from "@comp/pageConfig/tco-gameLogin.vue";
|
||||
import Mys from "@Mys/index.js";
|
||||
import TSUserAccount from "@Sqlite/modules/userAccount.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed, shallowRef } from "vue";
|
||||
import { computed, ref, shallowRef } from "vue";
|
||||
|
||||
import { useAppStore } from "@/store/modules/app.js";
|
||||
import { useUserStore } from "@/store/modules/user.js";
|
||||
@@ -119,6 +143,7 @@ import TakumiApi from "@/web/request/takumiReq.js";
|
||||
const { isLogin } = storeToRefs(useAppStore());
|
||||
const { uid, briefInfo, cookie, account } = storeToRefs(useUserStore());
|
||||
|
||||
const showLoginQr = ref<boolean>(false);
|
||||
const accounts = shallowRef<Array<TGApp.App.Account.User>>([]);
|
||||
const gameAccounts = shallowRef<Array<TGApp.Sqlite.Account.Game>>([]);
|
||||
const userInfo = computed<TGApp.App.Account.BriefInfo>(() => {
|
||||
@@ -131,37 +156,7 @@ const userInfo = computed<TGApp.App.Account.BriefInfo>(() => {
|
||||
};
|
||||
});
|
||||
|
||||
async function tryCaptchaLogin(): Promise<void> {
|
||||
const phone = await showDialog.input("请输入手机号", "+86");
|
||||
if (!phone) {
|
||||
showSnackbar.cancel("已取消验证码登录");
|
||||
return;
|
||||
}
|
||||
const phoneReg = /^1[3-9]\d{9}$/;
|
||||
if (!phoneReg.test(phone)) {
|
||||
showSnackbar.warn("请输入正确的手机号");
|
||||
return;
|
||||
}
|
||||
const actionType = await tryGetCaptcha(phone);
|
||||
if (!actionType) return;
|
||||
showSnackbar.success(`已发送验证码到 ${phone}`);
|
||||
const captcha = await showDialog.input("请输入验证码", "验证码:", undefined, false);
|
||||
if (!captcha) {
|
||||
showSnackbar.warn("输入验证码为空");
|
||||
return;
|
||||
}
|
||||
const loginResp = await tryLoginByCaptcha(phone, captcha, actionType);
|
||||
if (!loginResp) return;
|
||||
await showLoading.start("正在尝试登录...");
|
||||
const ck: TGApp.App.Account.Cookie = {
|
||||
account_id: loginResp.user_info.aid,
|
||||
ltuid: loginResp.user_info.aid,
|
||||
stuid: loginResp.user_info.aid,
|
||||
mid: loginResp.user_info.mid,
|
||||
cookie_token: "",
|
||||
stoken: loginResp.token.token,
|
||||
ltoken: "",
|
||||
};
|
||||
async function tryGetTokens(ck: TGApp.App.Account.Cookie): Promise<void> {
|
||||
await showLoading.update("正在获取 LToken");
|
||||
const ltokenRes = await PassportApi.lToken.get(ck);
|
||||
if (typeof ltokenRes !== "string") {
|
||||
@@ -231,6 +226,40 @@ async function tryCaptchaLogin(): Promise<void> {
|
||||
showSnackbar.success("成功登录!");
|
||||
}
|
||||
|
||||
async function tryCaptchaLogin(): Promise<void> {
|
||||
const phone = await showDialog.input("请输入手机号", "+86");
|
||||
if (!phone) {
|
||||
showSnackbar.cancel("已取消验证码登录");
|
||||
return;
|
||||
}
|
||||
const phoneReg = /^1[3-9]\d{9}$/;
|
||||
if (!phoneReg.test(phone)) {
|
||||
showSnackbar.warn("请输入正确的手机号");
|
||||
return;
|
||||
}
|
||||
const actionType = await tryGetCaptcha(phone);
|
||||
if (!actionType) return;
|
||||
showSnackbar.success(`已发送验证码到 ${phone}`);
|
||||
const captcha = await showDialog.input("请输入验证码", "验证码:", undefined, false);
|
||||
if (!captcha) {
|
||||
showSnackbar.warn("输入验证码为空");
|
||||
return;
|
||||
}
|
||||
const loginResp = await tryLoginByCaptcha(phone, captcha, actionType);
|
||||
if (!loginResp) return;
|
||||
await showLoading.start("正在尝试登录...");
|
||||
const ck: TGApp.App.Account.Cookie = {
|
||||
account_id: loginResp.user_info.aid,
|
||||
ltuid: loginResp.user_info.aid,
|
||||
stuid: loginResp.user_info.aid,
|
||||
mid: loginResp.user_info.mid,
|
||||
cookie_token: "",
|
||||
stoken: loginResp.token.token,
|
||||
ltoken: "",
|
||||
};
|
||||
await tryGetTokens(ck);
|
||||
}
|
||||
|
||||
async function refreshUser(uid: string) {
|
||||
let account = await TSUserAccount.account.getAccount(uid);
|
||||
if (!account) {
|
||||
@@ -375,6 +404,7 @@ async function tryGetCaptcha(phone: string, aigis?: string): Promise<string | fa
|
||||
showSnackbar.error(`[${captchaResp.retcode}] ${captchaResp.message}`);
|
||||
return false;
|
||||
}
|
||||
// @ts-expect-error type {} is not assignable to type string
|
||||
const aigisResp: TGApp.Plugins.Mys.CaptchaLogin.CaptchaAigis = JSON.parse(captchaResp.data);
|
||||
const resp = await showGeetest(JSON.parse(aigisResp.data));
|
||||
const aigisStr = `${aigisResp.session_id};${btoa(JSON.stringify(resp))}`;
|
||||
|
||||
129
src/components/pageConfig/tco-gameLogin.vue
Normal file
129
src/components/pageConfig/tco-gameLogin.vue
Normal file
@@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<TOverlay v-model="model" hide blur-val="20px">
|
||||
<div class="tog-box">
|
||||
<div class="tog-top">
|
||||
<div class="tog-title">请使用米游社进行扫码操作</div>
|
||||
</div>
|
||||
<div class="tog-mid">
|
||||
<qrcode-vue
|
||||
v-if="codeData"
|
||||
class="tog-qr"
|
||||
:value="codeData.url"
|
||||
render-as="svg"
|
||||
:background="'var(--box-bg-1)'"
|
||||
foreground="var(--box-text-1)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</TOverlay>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import TOverlay from "@comp/app/t-overlay.vue";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import QrcodeVue from "qrcode.vue";
|
||||
import { onUnmounted, shallowRef, watch } from "vue";
|
||||
|
||||
import PassportReq from "@/web/request/passportReq.js";
|
||||
|
||||
type ToGameLoginEmits = (e: "success", data: TGApp.App.Account.Cookie) => void;
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
let cycleTimer: NodeJS.Timeout | null = null;
|
||||
|
||||
const model = defineModel<boolean>({ default: false });
|
||||
const emits = defineEmits<ToGameLoginEmits>();
|
||||
const codeData = shallowRef<TGApp.BBS.GameLogin.GetLoginQrData>();
|
||||
|
||||
watch(model, async (value) => {
|
||||
if (value) {
|
||||
await freshQr();
|
||||
cycleTimer = setInterval(cycleGetData, 1000);
|
||||
}
|
||||
});
|
||||
|
||||
async function freshQr(): Promise<void> {
|
||||
const res = await PassportReq.qrLogin.create();
|
||||
if ("retcode" in res) {
|
||||
showSnackbar.error(`[${res.retcode}] ${res.message}`);
|
||||
return;
|
||||
}
|
||||
codeData.value = res;
|
||||
}
|
||||
|
||||
async function cycleGetData() {
|
||||
if (cycleTimer === null || !codeData.value) return;
|
||||
const res = await PassportReq.qrLogin.query(codeData.value.ticket);
|
||||
console.log(res);
|
||||
if ("retcode" in res) {
|
||||
showSnackbar.error(`[${res.retcode}] ${res.message}`);
|
||||
if (res.retcode === -106) {
|
||||
await freshQr();
|
||||
} else {
|
||||
clearInterval(cycleTimer);
|
||||
cycleTimer = null;
|
||||
model.value = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (res.status === "Created" || res.status === "Scanned") return;
|
||||
if (res.status === "Confirmed") {
|
||||
clearInterval(cycleTimer);
|
||||
cycleTimer = null;
|
||||
const ck: TGApp.App.Account.Cookie = {
|
||||
mid: res.user_info.mid,
|
||||
stoken: res.tokens[0].token,
|
||||
stuid: res.user_info.aid,
|
||||
account_id: res.user_info.aid,
|
||||
cookie_token: "",
|
||||
ltoken: "",
|
||||
ltuid: "",
|
||||
};
|
||||
emits("success", ck);
|
||||
model.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
if (cycleTimer !== null) clearInterval(cycleTimer);
|
||||
cycleTimer = null;
|
||||
});
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.tog-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
background-color: var(--box-bg-1);
|
||||
color: var(--app-page-content);
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.tog-top {
|
||||
border-bottom: 1px solid var(--common-shadow-4);
|
||||
font-family: var(--font-title);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tog-title {
|
||||
color: var(--common-text-title);
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.tog-mid {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 10px;
|
||||
border: 1px solid var(--common-shadow-2);
|
||||
border-radius: 5px;
|
||||
aspect-ratio: 1;
|
||||
background: var(--box-bg-2);
|
||||
}
|
||||
|
||||
.tog-qr {
|
||||
width: 256px;
|
||||
height: 256px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user