mirror of https://github.com/certd/certd
chore: account
parent
0451fa7573
commit
bdc0227c08
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ export interface SettingState {
|
|||
sysPublic?: SysPublicSetting;
|
||||
installInfo?: {
|
||||
siteId: string;
|
||||
installTime?: number;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
<template>
|
||||
<div class="cd-page-account">
|
||||
<iframe >
|
||||
|
||||
</iframe>
|
||||
|
||||
</div>
|
||||
</template>
|
|
@ -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>
|
||||
|
|
|
@ -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