mirror of https://github.com/certd/certd
chore: account
parent
0451fa7573
commit
bdc0227c08
|
@ -1,31 +1,60 @@
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
|
|
||||||
export type IframeMessageData = {
|
export type IframeMessageData<T> = {
|
||||||
action: string;
|
action: string;
|
||||||
id: string;
|
id: string;
|
||||||
data: any;
|
data?: T;
|
||||||
replyId?: string;
|
replyId?: string;
|
||||||
|
errorCode?: number; //0为成功
|
||||||
|
message?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type IframeMessageReq = {
|
export type IframeMessageReq<T = any, R = any> = {
|
||||||
req: IframeMessageData;
|
req: IframeMessageData<T>;
|
||||||
onReply: (data: IframeMessageData) => void;
|
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 {
|
export class IframeClient {
|
||||||
messageBucket: Record<string, IframeMessageReq> = {};
|
requestQueue: Record<string, IframeMessageReq> = {};
|
||||||
//当前客户端是否是父级页面
|
//当前客户端是否是父级页面
|
||||||
iframe?: HTMLIFrameElement;
|
iframe?: HTMLIFrameElement;
|
||||||
|
|
||||||
|
handlers: Record<string, (data: IframeMessageData<any>) => Promise<void>> = {};
|
||||||
constructor(iframe?: HTMLIFrameElement) {
|
constructor(iframe?: HTMLIFrameElement) {
|
||||||
this.iframe = iframe;
|
this.iframe = iframe;
|
||||||
window.addEventListener('message', (event: MessageEvent<IframeMessageData>) => {
|
window.addEventListener('message', async (event: MessageEvent<IframeMessageData<any>>) => {
|
||||||
const data = event.data;
|
const data = event.data;
|
||||||
if (data.replyId) {
|
if (data.action) {
|
||||||
const req = this.messageBucket[data.replyId];
|
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) {
|
if (req) {
|
||||||
req.onReply(data);
|
req.onReply(data);
|
||||||
delete this.messageBucket[data.replyId];
|
delete this.requestQueue[data.replyId!];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -33,19 +62,29 @@ export class IframeClient {
|
||||||
return window.self !== window.top;
|
return window.self !== window.top;
|
||||||
}
|
}
|
||||||
|
|
||||||
async send(action: string, data?: any, replyId?: string) {
|
register<T = any>(action: string, handler: (data: IframeMessageData<T>) => Promise<void>) {
|
||||||
const reqMessageData: IframeMessageData = {
|
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(),
|
id: nanoid(),
|
||||||
action,
|
action,
|
||||||
data,
|
data,
|
||||||
replyId,
|
replyId,
|
||||||
|
errorCode,
|
||||||
|
message,
|
||||||
};
|
};
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
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);
|
resolve(reply);
|
||||||
};
|
};
|
||||||
this.messageBucket[reqMessageData.id] = {
|
this.requestQueue[reqMessageData.id] = {
|
||||||
req: reqMessageData,
|
req: reqMessageData,
|
||||||
onReply,
|
onReply,
|
||||||
};
|
};
|
||||||
|
|
|
@ -58,6 +58,7 @@
|
||||||
"vuedraggable": "^4.1.0"
|
"vuedraggable": "^4.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@certd/lib-iframe": "^1.24.4",
|
||||||
"@certd/pipeline": "^1.24.4",
|
"@certd/pipeline": "^1.24.4",
|
||||||
"@rollup/plugin-commonjs": "^25.0.7",
|
"@rollup/plugin-commonjs": "^25.0.7",
|
||||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||||
|
|
|
@ -143,8 +143,8 @@ function openUpgrade() {
|
||||||
<div class="flex-o w-100">
|
<div class="flex-o w-100">
|
||||||
<span>站点ID:</span>
|
<span>站点ID:</span>
|
||||||
<fs-copyable class="flex-1" v-model={computedSiteId.value}></fs-copyable>
|
<fs-copyable class="flex-1" v-model={computedSiteId.value}></fs-copyable>
|
||||||
<div>注意保存好数据库,暂不支持换绑(默认数据库路径/data/certd/db.sqlite)</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mt-10">注意保存好数据库,暂不支持换绑(默认数据库路径/data/certd/db.sqlite)</div>
|
||||||
<a-input class="mt-10" v-model:value={formState.code} placeholder={placeholder} />
|
<a-input class="mt-10" v-model:value={formState.code} placeholder={placeholder} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@ export default {
|
||||||
.container {
|
.container {
|
||||||
.main {
|
.main {
|
||||||
max-width: 368px;
|
max-width: 368px;
|
||||||
width: 98%;
|
width: 96%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,8 +137,6 @@ export default {
|
||||||
|
|
||||||
.main {
|
.main {
|
||||||
min-width: 260px;
|
min-width: 260px;
|
||||||
width: 368px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
|
@ -163,6 +161,7 @@ export default {
|
||||||
color: rgba(0, 0, 0, 0.45);
|
color: rgba(0, 0, 0, 0.45);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
span {
|
span {
|
||||||
|
|
|
@ -65,6 +65,16 @@ export const sysResources = [
|
||||||
},
|
},
|
||||||
path: "/sys/settings",
|
path: "/sys/settings",
|
||||||
component: "/sys/settings/index.vue"
|
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"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ export interface SettingState {
|
||||||
sysPublic?: SysPublicSetting;
|
sysPublic?: SysPublicSetting;
|
||||||
installInfo?: {
|
installInfo?: {
|
||||||
siteId: string;
|
siteId: string;
|
||||||
|
installTime?: number;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="cd-page-account">
|
|
||||||
<iframe >
|
|
||||||
|
|
||||||
</iframe>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="main">
|
<div class="main login-page">
|
||||||
<a-form
|
<a-form
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
class="user-layout-login"
|
class="user-layout-login"
|
||||||
|
@ -207,6 +207,8 @@ export default defineComponent({
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
@import "../../../style/theme/index.less";
|
@import "../../../style/theme/index.less";
|
||||||
|
.login-page.main {
|
||||||
|
margin: 20px !important;
|
||||||
.user-layout-login {
|
.user-layout-login {
|
||||||
label {
|
label {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
@ -262,4 +264,5 @@ export default defineComponent({
|
||||||
color: rgba(0, 0, 0, 0.45);
|
color: rgba(0, 0, 0, 0.45);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -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>
|
Loading…
Reference in New Issue