diff --git a/package.json b/package.json
index 5905f04f..e45c4835 100644
--- a/package.json
+++ b/package.json
@@ -55,7 +55,9 @@
"js-md5": "^0.7.3",
"pinia": "^2.1.6",
"pinia-plugin-persistedstate": "^3.2.0",
+ "qrcode.vue": "^3.4.1",
"tauri-plugin-sql-api": "github:tauri-apps/tauri-plugin-sql#v1",
+ "uuid": "^9.0.0",
"vue": "^3.3.4",
"vue-echarts": "^6.6.1",
"vue-json-viewer": "^3.0.4",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ed523b05..dba3ad3b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -29,9 +29,15 @@ dependencies:
pinia-plugin-persistedstate:
specifier: ^3.2.0
version: 3.2.0(pinia@2.1.6)
+ qrcode.vue:
+ specifier: ^3.4.1
+ version: 3.4.1(vue@3.3.4)
tauri-plugin-sql-api:
specifier: github:tauri-apps/tauri-plugin-sql#v1
version: git@github.com+tauri-apps/tauri-plugin-sql/b8fd19dac907cc8c3d78681cd4803a326b8b861e
+ uuid:
+ specifier: ^9.0.0
+ version: 9.0.0
vue:
specifier: ^3.3.4
version: 3.3.4
@@ -3756,6 +3762,14 @@ packages:
engines: {node: '>=6'}
dev: true
+ /qrcode.vue@3.4.1(vue@3.3.4):
+ resolution: {integrity: sha512-wq/zHsifH4FJ1GXQi8/wNxD1KfQkckIpjK1KPTc/qwYU5/Bkd4me0w4xZSg6EXk6xLBkVDE0zxVagewv5EMAVA==}
+ peerDependencies:
+ vue: ^3.0.0
+ dependencies:
+ vue: 3.3.4
+ dev: false
+
/qs@6.11.0:
resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==}
engines: {node: '>=0.6'}
@@ -4684,6 +4698,11 @@ packages:
base64-arraybuffer: 1.0.2
dev: false
+ /uuid@9.0.0:
+ resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==}
+ hasBin: true
+ dev: false
+
/validate-npm-package-license@3.0.4:
resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
dependencies:
diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json
index 85075cf4..6db1ef04 100644
--- a/src-tauri/tauri.conf.json
+++ b/src-tauri/tauri.conf.json
@@ -29,6 +29,7 @@
"https://bbs.mihoyo.com/*",
"https://hk4e-api.mihoyo.com/*",
"https://passport-api.mihoyo.com/*",
+ "https://passport-api.miyoushe.com/*",
"https://passport-api-v4.mihoyo.com/*",
"https://act-webstatic.mihoyo.com/*",
"https://sdk-webstatic.mihoyo.com/*",
diff --git a/src/pages/common/Test.vue b/src/pages/common/Test.vue
index 0da2b42c..41d15fdb 100644
--- a/src/pages/common/Test.vue
+++ b/src/pages/common/Test.vue
@@ -1,6 +1,57 @@
Test
+
+ 渲染二维码
+ 获取数据
+
+
+
+
-
+
+
diff --git a/src/plugins/Mys/types/WebLogin.d.ts b/src/plugins/Mys/types/WebLogin.d.ts
new file mode 100644
index 00000000..d87e7738
--- /dev/null
+++ b/src/plugins/Mys/types/WebLogin.d.ts
@@ -0,0 +1,134 @@
+/**
+ * @file plugins Mys types WebLogin.d.ts
+ * @description Mys 插件 Web 登录类型定义文件
+ * @author BTMuli
+ * @since Beta v0.3.0
+ */
+
+/**
+ * @description Mys 插件 Web 登录类型
+ * @since Beta v0.3.0
+ * @namespace WebLogin
+ * @return WebLogin
+ */
+declare namespace TGApp.Plugins.Mys.WebLogin {
+ /**
+ * @description 获取登录二维码返回数据
+ * @since Beta v0.3.0
+ * @interface GetLoginQrResponse
+ * @extends TGApp.Plugins.Mys.Base.Response
+ * @property {GetLoginQrData} data 数据
+ * @return GetLoginQrResponse
+ */
+ export interface GetLoginQrResponse extends TGApp.Plugins.Mys.Base.Response {
+ data: GetLoginQrData;
+ }
+
+ /**
+ * @description 获取登录二维码返回数据
+ * @since Beta v0.3.0
+ * @interface GetLoginQrData
+ * @property {string} ticket 二维码票据
+ * @property {string} url 二维码链接
+ * @return GetLoginQrData
+ */
+ export interface GetLoginQrData {
+ ticket: string;
+ url: string;
+ }
+
+ /**
+ * @description 获取登录状态返回数据
+ * @since Beta v0.3.0
+ * @interface GetLoginStatusResponse
+ * @extends TGApp.Plugins.Mys.Base.Response
+ * @property {GetLoginStatusData} data 数据
+ * @return GetLoginStatusResponse
+ */
+ export interface GetLoginStatusResponse extends TGApp.Plugins.Mys.Base.Response {
+ data: GetLoginStatusData;
+ }
+
+ /**
+ * @description 获取登录状态返回数据
+ * @since Beta v0.3.0
+ * @interface GetLoginStatusData
+ * @property {string} app_id 应用 ID
+ * @property {number} client_type 客户端类型
+ * @property {string} created_at 创建时间
+ * @property {boolean} need_realperson 是否需要人机验证
+ * @property {RealNameInfo} realname_info 实名信息
+ * @property {string} scanned_at 扫码时间
+ * @property {string} status 状态
+ * @property {unknown[]} tokens 令牌
+ * @property {UserInfo} user_info 用户信息
+ * @return GetLoginStatusData
+ */
+ export interface GetLoginStatusData {
+ app_id: string;
+ client_type: number;
+ created_at: string;
+ need_realperson: boolean;
+ realname_info: RealNameInfo;
+ scanned_at: string;
+ status: string;
+ tokens: unknown[];
+ user_info: UserInfo;
+ }
+
+ /**
+ * @description 实名信息
+ * @since Beta v0.3.0
+ * @interface RealNameInfo
+ * @property {string} action_ticket 动作票据
+ * @property {string} action_type 动作类型
+ * @property {boolean} required 是否必须
+ * @return RealNameInfo
+ */
+ interface RealNameInfo {
+ action_ticket: string;
+ action_type: string;
+ required: boolean;
+ }
+
+ /**
+ * @description 用户信息
+ * @since Beta v0.3.0
+ * @interface UserInfo
+ * @property {string} account_name 账户名
+ * @property {string} aid 账户 ID
+ * @property {string} area_code 区域代码
+ * @property {string} country 国家
+ * @property {string} email 邮箱 // 为加密后的邮箱包含 * 号
+ * @property {string} identity_code 身份证号 // 为加密后的身份证号包含 * 号
+ * @property {number} is_email_verify 是否邮箱验证 // 1 为已验证
+ * @property {unknown[]} links 链接
+ * @property {string} mid 用户 ID
+ * @property {string} mobile 手机号 // 为加密后的手机号包含 * 号
+ * @property {string} realname 真实姓名 // 为加密后的真实姓名包含 * 号
+ * @property {string} rebind_area_code 重新绑定区域代码
+ * @property {string} rebind_mobile 重新绑定手机号
+ * @property {string} rebind_mobile_time 重新绑定手机号时间
+ * @property {string} safe_area_code 安全区域代码
+ * @property {string} safe_mobile 安全手机号
+ * @return UserInfo
+ */
+ interface UserInfo {
+ account_name: string;
+ aid: string;
+ area_code: string;
+ country: string;
+ email: string;
+ identity_code: string;
+ is_email_verify: number;
+ links: unknown[];
+ mid: string;
+ mobile: string;
+ realname: string;
+ rebind_area_code: string;
+ rebind_mobile: string;
+ rebind_mobile_time: string;
+ safe_area_code: string;
+ safe_mobile: string;
+ }
+}
diff --git a/src/plugins/Mys/utils/doWebLogin.ts b/src/plugins/Mys/utils/doWebLogin.ts
new file mode 100644
index 00000000..30aba6d1
--- /dev/null
+++ b/src/plugins/Mys/utils/doWebLogin.ts
@@ -0,0 +1,90 @@
+/**
+ * @file plugins Mys utils getLoginQr
+ * @description 获取登录二维码
+ * @author BTMuli
+ * @since Beta v0.3.0
+ */
+
+// tauri
+import { http } from "@tauri-apps/api";
+
+const uuid = crypto.randomUUID();
+
+const headers: Record = {
+ Accept: "application/json, text/plain, */*",
+ "Accept-Encoding": "gzip, deflate, br",
+ "Accept-Language": "zh-CN,zh;q=0.9",
+ Connection: "keep-alive",
+ Origin: "https://user.miyoushe.com",
+ Referer: "https://user.miyoushe.com/",
+ "User-Agent":
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36",
+ "x-rpc-app_id": "bll8iq97cem8",
+ "x-rpc-client_type": "4",
+ "x-rpc-device_model": "Chrome 116.0.0.0",
+ "x-rpc-device_name": "Chrome",
+ "x-rpc-device_os": "Windows 10 64-bit",
+ "x-rpc-game_biz": "bbs_cn",
+ "x-rpc-mi_referrer":
+ "https://user.miyoushe.com/login-platform/index.html?app_id=bll8iq97cem8&app_version=2.58.0&theme=&token_type=4&game_biz=bbs_cn&message_origin=https%253A%252F%252Fwww.miyoushe.com&succ_back_type=message%253Alogin-platform%253Alogin-success&fail_back_type=message%253Alogin-platform%253Alogin-fail&sync_login_status=popup&ux_mode=popup&iframe_level=1&ap_mps=1#/login/qr",
+ "x-rpc-sdk_version": "2.17.0",
+ "X-Rpc-Device_fp": "",
+ "X-Rpc-Device_id": uuid,
+};
+
+/**
+ * @description 获取登录二维码
+ * @since Beta v0.3.0
+ * @returns {Promise}
+ */
+export async function getLoginQr(): Promise<
+ TGApp.Plugins.Mys.WebLogin.GetLoginQrData | TGApp.Plugins.Mys.Base.Response
+> {
+ const url = "https://passport-api.miyoushe.com/account/ma-cn-passport/web/createQRLogin";
+ return await http
+ .fetch(url, {
+ method: "POST",
+ headers,
+ body: http.Body.json({}),
+ })
+ .then((res) => {
+ if (res.data.retcode === 0) {
+ return res.data.data;
+ } else {
+ return res.data;
+ }
+ });
+}
+
+/**
+ * @description 获取登录状态
+ * @since Beta v0.3.0
+ * @param {string} ticket 二维码票据
+ * @returns {Promise}
+ */
+export async function getLoginStatus(
+ ticket: string,
+): Promise> {
+ const url = "https://passport-api.miyoushe.com/account/ma-cn-passport/web/queryQRLoginStatus";
+ const resp = await http.fetch(url, {
+ method: "POST",
+ headers,
+ body: http.Body.json({ ticket }),
+ });
+ const data = resp.data;
+ if (data.retcode !== 0) return resp.data;
+ if (data.data.status === "Created" || data.data.status === "Scanned") {
+ await new Promise((resolve) => setTimeout(resolve, 5000));
+ return await getLoginStatus(ticket);
+ }
+ if (data.data.status === "Confirmed") {
+ const cookieList = resp.rawHeaders["set-cookie"].map((cookie) => cookie.split(";")[0]);
+ const cookieRecord: Record = {};
+ cookieList.forEach((cookie) => {
+ const [key, value] = cookie.split("=");
+ cookieRecord[key] = value;
+ });
+ return cookieRecord;
+ }
+ return resp.data;
+}