chore: account

pull/189/head
xiaojunnuo 2024-09-22 02:06:34 +08:00
parent 0451fa7573
commit bdc0227c08
9 changed files with 163 additions and 73 deletions

View File

@ -1,51 +1,90 @@
import { nanoid } from 'nanoid';
export type IframeMessageData = {
export type IframeMessageData<T> = {
action: string;
id: string;
data: any;
data?: T;
replyId?: string;
errorCode?: number; //0为成功
message?: string;
};
export type IframeMessageReq = {
req: IframeMessageData;
onReply: (data: IframeMessageData) => void;
export type IframeMessageReq<T = any, R = any> = {
req: IframeMessageData<T>;
onReply: (data: IframeMessageData<R>) => void;
};
export class IframeException extends Error {
code?: number = 0;
constructor(data: IframeMessageData<any>) {
super(data.message);
this.code = data.errorCode;
}
}
export class IframeClient {
messageBucket: Record<string, IframeMessageReq> = {};
requestQueue: Record<string, IframeMessageReq> = {};
//当前客户端是否是父级页面
iframe?: HTMLIFrameElement;
handlers: Record<string, (data: IframeMessageData<any>) => Promise<void>> = {};
constructor(iframe?: HTMLIFrameElement) {
this.iframe = iframe;
window.addEventListener('message', (event: MessageEvent<IframeMessageData>) => {
window.addEventListener('message', async (event: MessageEvent<IframeMessageData<any>>) => {
const data = event.data;
if (data.replyId) {
const req = this.messageBucket[data.replyId];
if (req) {
req.onReply(data);
delete this.messageBucket[data.replyId];
if (data.action) {
console.log('收到消息', data);
try {
const handler = this.handlers[data.action];
if (handler) {
const res = await handler(data);
if (data.id && data.action !== 'reply') {
await this.send('reply', res, data.id);
}
} else {
throw new Error(`action:${data.action} 未注册处理器`);
}
} catch (e: any) {
await this.send('reply', {}, data.id, 500, e.message);
}
}
});
this.register('reply', async data => {
const req = this.requestQueue[data.replyId!];
if (req) {
req.onReply(data);
delete this.requestQueue[data.replyId!];
}
});
}
isInFrame() {
return window.self !== window.top;
}
async send(action: string, data?: any, replyId?: string) {
const reqMessageData: IframeMessageData = {
register<T = any>(action: string, handler: (data: IframeMessageData<T>) => Promise<void>) {
this.handlers[action] = handler;
}
async send<R = any, T = any>(action: string, data?: T, replyId?: string, errorCode?: number, message?: string): Promise<IframeMessageData<R>> {
const reqMessageData: IframeMessageData<T> = {
id: nanoid(),
action,
data,
replyId,
errorCode,
message,
};
return new Promise((resolve, reject) => {
const onReply = async (reply: IframeMessageData) => {
const onReply = async (reply: IframeMessageData<R>) => {
if (reply.errorCode && reply.errorCode > 0) {
reject(new IframeException(reply));
return;
}
resolve(reply);
};
this.messageBucket[reqMessageData.id] = {
this.requestQueue[reqMessageData.id] = {
req: reqMessageData,
onReply,
};

View File

@ -58,6 +58,7 @@
"vuedraggable": "^4.1.0"
},
"devDependencies": {
"@certd/lib-iframe": "^1.24.4",
"@certd/pipeline": "^1.24.4",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",

View File

@ -143,8 +143,8 @@ function openUpgrade() {
<div class="flex-o w-100">
<span>站点ID</span>
<fs-copyable class="flex-1" v-model={computedSiteId.value}></fs-copyable>
<div>注意保存好数据库暂不支持换绑默认数据库路径/data/certd/db.sqlite</div>
</div>
<div class="mt-10">注意保存好数据库暂不支持换绑默认数据库路径/data/certd/db.sqlite</div>
<a-input class="mt-10" v-model:value={formState.code} placeholder={placeholder} />
</div>

View File

@ -57,7 +57,7 @@ export default {
.container {
.main {
max-width: 368px;
width: 98%;
width: 96%;
}
}
}
@ -137,8 +137,6 @@ export default {
.main {
min-width: 260px;
width: 368px;
margin: 0 auto;
}
.footer {
@ -163,6 +161,7 @@ export default {
color: rgba(0, 0, 0, 0.45);
font-size: 14px;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
span {

View File

@ -65,6 +65,16 @@ export const sysResources = [
},
path: "/sys/settings",
component: "/sys/settings/index.vue"
},
{
title: "账号绑定",
name: "account",
meta: {
icon: "ion:settings-outline",
permission: "sys:settings:view"
},
path: "/sys/account",
component: "/sys/account/index.vue"
}
]
}

View File

@ -23,6 +23,7 @@ export interface SettingState {
sysPublic?: SysPublicSetting;
installInfo?: {
siteId: string;
installTime?: number;
};
}

View File

@ -1,8 +0,0 @@
<template>
<div class="cd-page-account">
<iframe >
</iframe>
</div>
</template>

View File

@ -1,5 +1,5 @@
<template>
<div class="main">
<div class="main login-page">
<a-form
ref="formRef"
class="user-layout-login"
@ -207,59 +207,62 @@ export default defineComponent({
<style lang="less">
@import "../../../style/theme/index.less";
.user-layout-login {
label {
font-size: 14px;
}
.login-page.main {
margin: 20px !important;
.user-layout-login {
label {
font-size: 14px;
}
.login-title {
color: @primary-color;
font-size: 18px;
text-align: center;
margin: 20px;
}
.getCaptcha {
display: block;
width: 100%;
height: 40px;
}
.login-title {
color: @primary-color;
font-size: 18px;
text-align: center;
margin: 20px;
}
.getCaptcha {
display: block;
width: 100%;
height: 40px;
}
.forge-password {
font-size: 14px;
}
.forge-password {
font-size: 14px;
}
button.login-button {
padding: 0 15px;
font-size: 16px;
height: 40px;
width: 100%;
}
button.login-button {
padding: 0 15px;
font-size: 16px;
height: 40px;
width: 100%;
}
.user-login-other {
text-align: left;
margin-top: 30px;
margin-bottom: 30px;
line-height: 22px;
.user-login-other {
text-align: left;
margin-top: 30px;
margin-bottom: 30px;
line-height: 22px;
.item-icon {
font-size: 24px;
color: rgba(0, 0, 0, 0.2);
margin-left: 16px;
vertical-align: middle;
cursor: pointer;
transition: color 0.3s;
.item-icon {
font-size: 24px;
color: rgba(0, 0, 0, 0.2);
margin-left: 16px;
vertical-align: middle;
cursor: pointer;
transition: color 0.3s;
&:hover {
color: @primary-color;
&:hover {
color: @primary-color;
}
}
.register {
float: right;
}
}
.register {
float: right;
.iconify {
color: rgba(0, 0, 0, 0.45);
}
}
.iconify {
color: rgba(0, 0, 0, 0.45);
}
}
</style>

View File

@ -0,0 +1,45 @@
<template>
<fs-page class="cd-page-account">
<iframe ref="iframeRef" class="account-iframe" src="http://localhost:1017/#/home?appKey=z4nXOeTeSnnpUpnmsV"> </iframe>
</fs-page>
</template>
<script setup lang="tsx">
import { IframeClient } from "@certd/lib-iframe";
import { onMounted, ref } from "vue";
import { useUserStore } from "/@/store/modules/user";
import { useSettingStore } from "/@/store/modules/settings";
const iframeRef = ref();
const userStore = useUserStore();
const settingStore = useSettingStore();
type SubjectInfo = {
subjectId: string;
installTime?: number;
vipType?: string;
expiresTime?: number;
};
onMounted(() => {
const iframeClient = new IframeClient(iframeRef.value);
iframeClient.register("getSubjectInfo", (req) => {
const subjectInfo: SubjectInfo = {
subjectId: settingStore.installInfo.siteId,
installTime: settingStore.installInfo.installTime,
vipType: userStore.plusInfo.vipType || "free",
expiresTime: userStore.plusInfo.expireTime
};
return subjectInfo;
});
});
</script>
<style lang="less">
.cd-page-account {
.account-iframe {
width: 100%;
height: 100%;
border: none;
}
}
</style>