application 多个web配置
This commit is contained in:
parent
89a7761722
commit
ca7f15829f
|
|
@ -7,7 +7,7 @@ import { File } from 'formidable';
|
|||
export declare function getApplication<ED extends EntityDict, Cxt extends BackendRuntimeContext<ED>>(params: {
|
||||
type: AppType;
|
||||
domain: string;
|
||||
}, context: Cxt): Promise<string>;
|
||||
}, context: Cxt): Promise<string | undefined>;
|
||||
export declare function signatureJsSDK<ED extends EntityDict, Cxt extends BackendRuntimeContext<ED>>({ url, env }: {
|
||||
url: string;
|
||||
env: WebEnv;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,64 @@ export async function getApplication(params, context) {
|
|||
const { type, domain } = params;
|
||||
const url = context.getHeader('host');
|
||||
console.log('url is', url);
|
||||
const [application] = await context.select('application', {
|
||||
|
||||
// const [application] = await context.select(
|
||||
// 'application',
|
||||
// {
|
||||
// data: cloneDeep(applicationProjection),
|
||||
// filter: {
|
||||
// type,
|
||||
// system: {
|
||||
// domain$system: {
|
||||
// url: domain,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// {}
|
||||
// );
|
||||
// //微信小程序环境下 没有就报错
|
||||
// if (type === 'wechatMp') {
|
||||
// assert(
|
||||
// application,
|
||||
// '微信小程序环境下 application必须存在小程序相关配置'
|
||||
// );
|
||||
// } else if (type === 'native') {
|
||||
// assert(application, 'APP环境下 application必须存在APP相关配置');
|
||||
// } else {
|
||||
// //web 或 wechatPublic
|
||||
// if (type === 'wechatPublic') {
|
||||
// // 如果微信公众号环境下 application不存在公众号配置,但又在公众号访问,这时可以使用web的application
|
||||
// if (!application) {
|
||||
// const [application2] = await context.select(
|
||||
// 'application',
|
||||
// {
|
||||
// data: cloneDeep(applicationProjection),
|
||||
// filter: {
|
||||
// type: 'web',
|
||||
// system: {
|
||||
// domain$system: {
|
||||
// url: domain,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// {}
|
||||
// );
|
||||
// assert(
|
||||
// application2,
|
||||
// '微信公众号环境下 application不存在公众号配置,但必须存在web相关配置'
|
||||
// );
|
||||
// return application2.id as string;
|
||||
// }
|
||||
// } else {
|
||||
// assert(application, 'web环境下 application必须存在web相关配置');
|
||||
// }
|
||||
// }
|
||||
// return application.id as string;
|
||||
// 先找指定domain的应用,如果不存在再找系统下面的domain, 但无论怎么样都必须一项
|
||||
//
|
||||
let applications = await context.select('application', {
|
||||
data: cloneDeep(applicationProjection),
|
||||
filter: {
|
||||
type,
|
||||
|
|
@ -17,21 +74,47 @@ export async function getApplication(params, context) {
|
|||
url: domain,
|
||||
},
|
||||
},
|
||||
domain: {
|
||||
url: domain,
|
||||
},
|
||||
},
|
||||
}, {});
|
||||
//微信小程序环境下 没有就报错
|
||||
if (type === 'wechatMp') {
|
||||
assert(application, '微信小程序环境下 application必须存在小程序相关配置');
|
||||
assert(applications.length <= 1, `指定域名的应用 只能存在一项或未指定`);
|
||||
if (applications.length === 0) {
|
||||
applications = await context.select(
|
||||
'application',
|
||||
{
|
||||
data: cloneDeep(applicationProjection),
|
||||
filter: {
|
||||
type,
|
||||
system: {
|
||||
domain$system: {
|
||||
url: domain,
|
||||
},
|
||||
},
|
||||
domainId: {
|
||||
$exists: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{}
|
||||
);
|
||||
}
|
||||
else if (type === 'native') {
|
||||
assert(application, 'APP环境下 application必须存在APP相关配置');
|
||||
}
|
||||
else {
|
||||
//web 或 wechatPublic
|
||||
if (type === 'wechatPublic') {
|
||||
// 如果微信公众号环境下 application不存在公众号配置,但又在公众号访问,这时可以使用web的application
|
||||
if (!application) {
|
||||
const [application2] = await context.select('application', {
|
||||
switch (type) {
|
||||
case 'wechatMp': {
|
||||
assert(applications.length === 1, `微信小程序环境下,同一个系统必须存在唯一的【${type}】应用`);
|
||||
const application = applications[0];
|
||||
return application.id;
|
||||
}
|
||||
case 'native': {
|
||||
assert(applications.length === 1, `APP环境下,同一个系统必须存在唯一的【${type}】应用`);
|
||||
const application = applications[0];
|
||||
return application.id;
|
||||
}
|
||||
case 'wechatPublic': {
|
||||
// 微信公众号环境下,未配置公众号,可以使用web的application
|
||||
if (applications.length === 0) {
|
||||
let applications2 = await context.select('application', {
|
||||
data: cloneDeep(applicationProjection),
|
||||
filter: {
|
||||
type: 'web',
|
||||
|
|
@ -40,17 +123,50 @@ export async function getApplication(params, context) {
|
|||
url: domain,
|
||||
},
|
||||
},
|
||||
domain: {
|
||||
url: domain,
|
||||
},
|
||||
},
|
||||
}, {});
|
||||
assert(application2, '微信公众号环境下 application不存在公众号配置,但必须存在web相关配置');
|
||||
return application2.id;
|
||||
assert(applications2.length <= 1, `指定域名的应用 只能存在一项或未指定`);
|
||||
if (applications2.length === 0) {
|
||||
applications2 = await context.select(
|
||||
'application',
|
||||
{
|
||||
data: cloneDeep(applicationProjection),
|
||||
filter: {
|
||||
type,
|
||||
system: {
|
||||
domain$system: {
|
||||
url: domain,
|
||||
},
|
||||
},
|
||||
domainId: {
|
||||
$exists: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{}
|
||||
);
|
||||
}
|
||||
assert(applications2.length === 1, '微信公众号环境下, 可以未配置公众号,但必须存在web的application');
|
||||
const application = applications2[0];
|
||||
return application.id;
|
||||
}
|
||||
assert(applications.length === 1, `微信公众号环境下,同一个系统必须存在唯一的【${type}】应用 或 多个${type}应用必须配置域名`);
|
||||
const application = applications[0];
|
||||
return application.id;
|
||||
}
|
||||
else {
|
||||
assert(application, 'web环境下 application必须存在web相关配置');
|
||||
case 'web': {
|
||||
assert(applications.length === 1, `web环境下,同一个系统必须存在唯一的【${type}】应用 或 多个${type}应用必须配置域名`);
|
||||
const application = applications[0];
|
||||
return application.id;
|
||||
}
|
||||
default: {
|
||||
assert(false, `不支持的类型【${type}】`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
return application.id;
|
||||
}
|
||||
export async function signatureJsSDK({ url, env }, context) {
|
||||
const application = context.getApplication();
|
||||
|
|
@ -64,7 +180,7 @@ export async function signatureJsSDK({ url, env }, context) {
|
|||
}
|
||||
export async function uploadWechatMedia(params, // FormData表单提交 isPermanent 变成 'true' | 'false'
|
||||
context) {
|
||||
const { applicationId, file, type: mediaType, description, extraFileId } = params;
|
||||
const { applicationId, file, type: mediaType, description, extraFileId, } = params;
|
||||
assert(applicationId);
|
||||
const isPermanent = params.isPermanent === 'true';
|
||||
const filename = file.originalFilename;
|
||||
|
|
@ -98,14 +214,14 @@ context) {
|
|||
if (isPermanent) {
|
||||
// 只有公众号才能上传永久素材
|
||||
assert(type === 'wechatPublic');
|
||||
const result = await wechatInstance.createMaterial({
|
||||
const result = (await wechatInstance.createMaterial({
|
||||
type: mediaType,
|
||||
media: fileStream,
|
||||
filename,
|
||||
filetype,
|
||||
fileLength,
|
||||
description: description ? JSON.parse(description) : null,
|
||||
});
|
||||
}));
|
||||
mediaId = result.media_id;
|
||||
}
|
||||
else {
|
||||
|
|
|
|||
|
|
@ -1062,135 +1062,118 @@ export async function sendCaptcha({ mobile, env, type: type2, }, context) {
|
|||
const closeRootMode = context.openRootMode();
|
||||
if (process.env.NODE_ENV !== 'development' && !mockSend) {
|
||||
const [count1, count2] = await Promise.all([
|
||||
context.count(
|
||||
'captcha',
|
||||
{
|
||||
filter: {
|
||||
visitorId,
|
||||
$$createAt$$: {
|
||||
$gt: now - 3600 * 1000,
|
||||
},
|
||||
type: type2,
|
||||
context.count('captcha', {
|
||||
filter: {
|
||||
visitorId,
|
||||
$$createAt$$: {
|
||||
$gt: now - 3600 * 1000,
|
||||
},
|
||||
type: type2,
|
||||
},
|
||||
{
|
||||
dontCollect: true,
|
||||
}
|
||||
),
|
||||
context.count(
|
||||
'captcha',
|
||||
{
|
||||
filter: {
|
||||
mobile,
|
||||
$$createAt$$: {
|
||||
$gt: now - 3600 * 1000,
|
||||
},
|
||||
type: type2,
|
||||
}, {
|
||||
dontCollect: true,
|
||||
}),
|
||||
context.count('captcha', {
|
||||
filter: {
|
||||
mobile,
|
||||
$$createAt$$: {
|
||||
$gt: now - 3600 * 1000,
|
||||
},
|
||||
type: type2,
|
||||
},
|
||||
{
|
||||
dontCollect: true,
|
||||
}
|
||||
),
|
||||
}, {
|
||||
dontCollect: true,
|
||||
}),
|
||||
]);
|
||||
if (count1 > 5 || count2 > 5) {
|
||||
closeRootMode();
|
||||
throw new OakUserException('您已发送很多次短信,请休息会再发吧');
|
||||
}
|
||||
}
|
||||
const [captcha] = await context.select(
|
||||
'captcha',
|
||||
{
|
||||
data: {
|
||||
id: 1,
|
||||
code: 1,
|
||||
$$createAt$$: 1,
|
||||
},
|
||||
filter: {
|
||||
mobile,
|
||||
$$createAt$$: {
|
||||
$gt: now - duration * 60 * 1000,
|
||||
},
|
||||
expired: false,
|
||||
type: type2,
|
||||
},
|
||||
const [captcha] = await context.select('captcha', {
|
||||
data: {
|
||||
id: 1,
|
||||
code: 1,
|
||||
$$createAt$$: 1,
|
||||
},
|
||||
{
|
||||
dontCollect: true,
|
||||
}
|
||||
);
|
||||
filter: {
|
||||
mobile,
|
||||
$$createAt$$: {
|
||||
$gt: now - duration * 60 * 1000,
|
||||
},
|
||||
expired: false,
|
||||
type: type2,
|
||||
},
|
||||
}, {
|
||||
dontCollect: true,
|
||||
});
|
||||
if (captcha) {
|
||||
const code = captcha.code;
|
||||
if (process.env.NODE_ENV === 'development' || mockSend) {
|
||||
closeRootMode();
|
||||
return `验证码[${code}]已创建`;
|
||||
} else if (captcha.$$createAt$$ - now < 60000) {
|
||||
}
|
||||
else if (captcha.$$createAt$$ - now < 60000) {
|
||||
closeRootMode();
|
||||
throw new OakUserException('您的操作太迅捷啦,请稍等再点吧');
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
assert(origin, '必须设置短信渠道');
|
||||
// todo 再次发送
|
||||
const result = await sendSms(
|
||||
{
|
||||
origin: origin,
|
||||
templateName: codeTemplateName,
|
||||
mobile,
|
||||
templateParam: { code, duration: duration.toString() },
|
||||
},
|
||||
context
|
||||
);
|
||||
const result = await sendSms({
|
||||
origin: origin,
|
||||
templateName: codeTemplateName,
|
||||
mobile,
|
||||
templateParam: { code, duration: duration.toString() },
|
||||
}, context);
|
||||
closeRootMode();
|
||||
if (result.success) {
|
||||
return '验证码已发送';
|
||||
}
|
||||
return '验证码发送失败';
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
let code;
|
||||
if (process.env.NODE_ENV === 'development' || mockSend) {
|
||||
code = mobile.substring(7);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
code = Math.floor(Math.random() * 10000).toString();
|
||||
while (code.length < 4) {
|
||||
code += '0';
|
||||
}
|
||||
}
|
||||
const id = await generateNewIdAsync();
|
||||
await context.operate(
|
||||
'captcha',
|
||||
{
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'create',
|
||||
data: {
|
||||
id,
|
||||
mobile,
|
||||
code,
|
||||
visitorId,
|
||||
env,
|
||||
expired: false,
|
||||
expiresAt: now + duration * 60 * 1000,
|
||||
type: type2,
|
||||
},
|
||||
await context.operate('captcha', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'create',
|
||||
data: {
|
||||
id,
|
||||
mobile,
|
||||
code,
|
||||
visitorId,
|
||||
env,
|
||||
expired: false,
|
||||
expiresAt: now + duration * 60 * 1000,
|
||||
type: type2,
|
||||
},
|
||||
{
|
||||
dontCollect: true,
|
||||
}
|
||||
);
|
||||
}, {
|
||||
dontCollect: true,
|
||||
});
|
||||
if (process.env.NODE_ENV === 'development' || mockSend) {
|
||||
closeRootMode();
|
||||
return `验证码[${code}]已创建`;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
assert(origin, '必须设置短信渠道');
|
||||
//发送短信
|
||||
const result = await sendSms(
|
||||
{
|
||||
origin: origin,
|
||||
templateName: codeTemplateName,
|
||||
mobile,
|
||||
templateParam: { code, duration: duration.toString() },
|
||||
},
|
||||
context
|
||||
);
|
||||
const result = await sendSms({
|
||||
origin: origin,
|
||||
templateName: codeTemplateName,
|
||||
mobile,
|
||||
templateParam: { code, duration: duration.toString() },
|
||||
}, context);
|
||||
closeRootMode();
|
||||
if (result.success) {
|
||||
return '验证码已发送';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Row, Descriptions, Typography, Button, Modal } from 'antd';
|
||||
import { Row, Descriptions, Typography, Button, Modal, Space } from 'antd';
|
||||
import ApplicationUpsert from '../upsert';
|
||||
export default function Render(props) {
|
||||
const { id, name, description, type, oakFullpath, oakExecutable, oakExecuting } = props.data;
|
||||
|
|
@ -7,15 +7,23 @@ export default function Render(props) {
|
|||
const [open, setOpen] = useState(false);
|
||||
if (id && oakFullpath) {
|
||||
return (<>
|
||||
<Modal open={open} width={800} onCancel={() => {
|
||||
<Modal open={open} width={500} onCancel={() => {
|
||||
clean();
|
||||
setOpen(false);
|
||||
}} footer={<Button type="primary" onClick={async () => {
|
||||
}} footer={<Space>
|
||||
<Button onClick={async () => {
|
||||
clean();
|
||||
setOpen(false);
|
||||
}} disabled={oakExecuting}>
|
||||
{t('common::action.cancel')}
|
||||
</Button>
|
||||
<Button type="primary" onClick={async () => {
|
||||
await execute();
|
||||
setOpen(false);
|
||||
}} disabled={oakExecutable !== true || oakExecuting}>
|
||||
{t('common::action.confirm')}
|
||||
</Button>}>
|
||||
{t('common::action.confirm')}
|
||||
</Button>
|
||||
</Space>}>
|
||||
<ApplicationUpsert oakPath={oakFullpath} oakId={id}/>
|
||||
</Modal>
|
||||
<Descriptions column={2} bordered>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ export default OakComponent({
|
|||
config: 1,
|
||||
description: 1,
|
||||
type: 1,
|
||||
systemId: 1,
|
||||
domainId: 1,
|
||||
},
|
||||
formData({ data }) {
|
||||
return data || {};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
/// <reference types="wechat-miniprogram" />
|
||||
/// <reference types="react" />
|
||||
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<import("../../../oak-app-domain").EntityDict, "application", false, WechatMiniprogram.Component.DataOption>) => import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>>;
|
||||
import { EntityDict } from '../../../oak-app-domain';
|
||||
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<EntityDict, "application", false, WechatMiniprogram.Component.DataOption>) => import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>>;
|
||||
export default _default;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,15 @@
|
|||
export default OakComponent({
|
||||
isList: false,
|
||||
entity: 'application',
|
||||
projection: {
|
||||
id: 1,
|
||||
name: 1,
|
||||
config: 1,
|
||||
description: 1,
|
||||
type: 1,
|
||||
systemId: 1,
|
||||
domainId: 1,
|
||||
},
|
||||
formData({ data }) {
|
||||
return data || {};
|
||||
},
|
||||
|
|
@ -16,18 +25,29 @@ export default OakComponent({
|
|||
value: 'wechatPublic',
|
||||
},
|
||||
],
|
||||
domains: [],
|
||||
},
|
||||
/* lifetimes: {
|
||||
ready() {
|
||||
const { systemId, oakId } = this.props;
|
||||
|
||||
if (!oakId) {
|
||||
if (systemId) {
|
||||
this.update({
|
||||
methods: {
|
||||
async getDomains(systemId) {
|
||||
const { data: domains } = await this.features.cache.refresh(
|
||||
'domain',
|
||||
{
|
||||
data: {
|
||||
id: 1,
|
||||
systemId: 1,
|
||||
url: 1,
|
||||
apiPath: 1,
|
||||
port: 1,
|
||||
protocol: 1,
|
||||
},
|
||||
filter: {
|
||||
systemId,
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
);
|
||||
this.setState({
|
||||
domains,
|
||||
});
|
||||
},
|
||||
}, */
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,8 +4,6 @@ import { WebComponentProps } from 'oak-frontend-base';
|
|||
export default function Render(props: WebComponentProps<EntityDict, 'application', false, {
|
||||
name: string;
|
||||
description: string;
|
||||
variant: 'inline' | 'alone' | 'dialog';
|
||||
showBack: boolean;
|
||||
type: EntityDict['application']['Schema']['type'];
|
||||
typeArr: Array<{
|
||||
label: string;
|
||||
|
|
@ -15,6 +13,9 @@ export default function Render(props: WebComponentProps<EntityDict, 'application
|
|||
oakId: string;
|
||||
style: EntityDict['system']['Schema']['style'];
|
||||
$$createAt$$: number;
|
||||
domainId: string;
|
||||
domains: EntityDict['domain']['Schema'][];
|
||||
}, {
|
||||
confirm: () => void;
|
||||
getDomains: (systemId: string) => Promise<void>;
|
||||
}>): React.JSX.Element;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import React from 'react';
|
||||
import { Form, Select, Input } from 'antd';
|
||||
export default function Render(props) {
|
||||
const { name, description, type, typeArr, $$createAt$$, } = props.data;
|
||||
const { t, update, navigateBack, confirm } = props.methods;
|
||||
const { systemId, name, description, type, typeArr, $$createAt$$, domainId, domains, } = props.data;
|
||||
const { t, update, confirm, getDomains } = props.methods;
|
||||
return (<Form colon={true} labelCol={{ span: 6 }} wrapperCol={{ span: 16 }}>
|
||||
<Form.Item label="名称" required>
|
||||
<>
|
||||
|
|
@ -31,6 +31,26 @@ export default function Render(props) {
|
|||
update({
|
||||
type: value,
|
||||
});
|
||||
}}/>
|
||||
</>
|
||||
</Form.Item>
|
||||
<Form.Item label="域名">
|
||||
<>
|
||||
<Select allowClear value={domainId} style={{ width: 120 }} options={domains?.map((ele) => ({
|
||||
label: ele.url,
|
||||
value: ele.id,
|
||||
}))} onChange={(value) => {
|
||||
if (!value) {
|
||||
update({
|
||||
domainId: undefined,
|
||||
});
|
||||
return;
|
||||
}
|
||||
update({
|
||||
domainId: value,
|
||||
});
|
||||
}} onClick={() => {
|
||||
getDomains(systemId);
|
||||
}}/>
|
||||
</>
|
||||
</Form.Item>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ const Colors = ['primary', 'success', 'error', 'warning', 'info'];
|
|||
function Color(props) {
|
||||
const { value = {}, setValue } = props;
|
||||
;
|
||||
return (<Form>
|
||||
return (<Form labelCol={{ span: 4 }} wrapperCol={{ span: 20 }} style={{ maxWidth: 600 }}>
|
||||
{Colors.map((ele) => (<Form.Item key={ele} label={ele}
|
||||
// required
|
||||
tooltip={`设置系统【${ele}】颜色`}>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
import React from 'react';
|
||||
import { Config } from '../../../../types/Config';
|
||||
export default function Basic(props: {
|
||||
app: Required<Config>['App'];
|
||||
setValue: (path: string, value: any) => void;
|
||||
}): React.JSX.Element;
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
import React from 'react';
|
||||
import { Row, Col, Card, Divider, Input, Form, Space, Switch, } from 'antd';
|
||||
import Styles from './web.module.less';
|
||||
// qrCodeType?: QrCodeType; // 生成二维码时,优先生成的类型
|
||||
// qrCodeApplicationId?: string; // 生成二维码时,优先使用的appId
|
||||
// qrCodePublicForMpId?: string; // 如果qrCodeType是wechatPublicForMp,在此指明关联的小程序appId
|
||||
// mpShareImageUrl?: string; // 小程序分享时的imageUrl(使用网络图片,5:4)
|
||||
export default function Basic(props) {
|
||||
const { app, setValue } = props;
|
||||
return (<Space direction="vertical" size="middle" style={{ display: 'flex' }}>
|
||||
<Row>
|
||||
<Card className={Styles.tips}>
|
||||
每种均可配置一个,相应的服务所使用的帐号请准确对应
|
||||
</Card>
|
||||
</Row>
|
||||
<Col flex="auto">
|
||||
<Divider orientation="left" className={Styles.title}>
|
||||
基础设置
|
||||
</Divider>
|
||||
<Form labelCol={{ span: 8 }} wrapperCol={{ span: 16 }} style={{ maxWidth: 600 }}>
|
||||
<Form.Item label="小程序分享时图片地址" tooltip="小程序分享时图片地址,使用网络图片">
|
||||
<Input placeholder="请输入图片地址" type="text" value={app?.mpShareImageUrl} onChange={(e) => setValue(`mpShareImageUrl`, e.target.value)}/>
|
||||
</Form.Item>
|
||||
<Form.Item label="用户合并"
|
||||
//name="mergeUserDirectly"
|
||||
tooltip="当发现用户具有相同的特征时是否合并">
|
||||
<>
|
||||
<Switch checkedChildren="是" unCheckedChildren="否" checked={app?.mergeUserDirectly} onChange={(checked) => setValue(`mergeUserDirectly`, checked)}/>
|
||||
</>
|
||||
</Form.Item>
|
||||
<Form.Item label="Token刷新时间" tooltip="设置Token刷新时间,默认使用系统定义">
|
||||
<Input placeholder="请输入Token刷新时间" type="number" value={app?.tokenRefreshTime} onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
if (val) {
|
||||
setValue(`tokenRefreshTime`, Number(val));
|
||||
}
|
||||
else {
|
||||
setValue(`tokenRefreshTime`, val);
|
||||
}
|
||||
}} suffix="毫秒"/>
|
||||
</Form.Item>
|
||||
<Form.Item label="Token过期时间" tooltip="设置Token过期时间,默认使用系统定义的">
|
||||
<Input placeholder="Token过期时间" type="number" value={app?.tokenExpireTime} onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
if (val) {
|
||||
setValue(`tokenExpireTime`, Number(val));
|
||||
}
|
||||
else {
|
||||
setValue(`tokenExpireTime`, val);
|
||||
}
|
||||
}} suffix="毫秒"/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Col>
|
||||
</Space>);
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
|
||||
.label {
|
||||
color: var(--oak-text-color-primary);
|
||||
font-size: 28px;
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
.tips {
|
||||
color: var(--oak-text-color-placeholder);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-bottom: 0px;
|
||||
margin-top:36px;
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Tabs, Row, Col, Card, Divider, Input, Form, Space, Modal, message, Switch, Select } from 'antd';
|
||||
import { Tabs, Row, Col, Card, Divider, Input, Form, Space, Select, Modal, message, Switch, } from 'antd';
|
||||
import { get } from 'oak-domain/lib/utils/lodash';
|
||||
import Styles from './web.module.less';
|
||||
function Ali(props) {
|
||||
|
|
@ -25,27 +25,27 @@ function Ali(props) {
|
|||
key: `${idx}`,
|
||||
label: `短信${idx + 1}`,
|
||||
children: (<Form colon={false} labelAlign="left" layout="vertical" style={{ marginTop: 10 }}>
|
||||
<Form.Item label="accessKeyId" >
|
||||
<Form.Item label="accessKeyId">
|
||||
<>
|
||||
<Input placeholder="请输入accessKeyId" type="text" value={ele.accessKeyId} onChange={(e) => setValue(`${idx}.accessKeyId`, e.target.value)}/>
|
||||
</>
|
||||
</Form.Item>
|
||||
<Form.Item label="accessKeySecret" >
|
||||
<Form.Item label="accessKeySecret">
|
||||
<>
|
||||
<Input placeholder="请输入accessKeySecret" type="text" value={ele.accessKeySecret} onChange={(e) => setValue(`${idx}.accessKeySecret`, e.target.value)}/>
|
||||
</>
|
||||
</Form.Item>
|
||||
<Form.Item label="endpoint" >
|
||||
<Form.Item label="endpoint">
|
||||
<>
|
||||
<Input placeholder="请输入endpoint" type="text" value={ele.endpoint} onChange={(e) => setValue(`${idx}.endpoint`, e.target.value)}/>
|
||||
</>
|
||||
</Form.Item>
|
||||
<Form.Item label="apiVersion" >
|
||||
<Form.Item label="apiVersion">
|
||||
<>
|
||||
<Input placeholder="请输入apiVersion" type="text" value={ele.apiVersion} onChange={(e) => setValue(`${idx}.defaultSignName`, e.target.value)}/>
|
||||
</>
|
||||
</Form.Item>
|
||||
<Form.Item label="defaultSignName" >
|
||||
<Form.Item label="defaultSignName">
|
||||
<>
|
||||
<Input placeholder="请输入defaultSignName" type="text" value={ele.defaultSignName} onChange={(e) => setValue(`${idx}.defaultSignName`, e.target.value)}/>
|
||||
</>
|
||||
|
|
@ -343,22 +343,22 @@ function CTYun(props) {
|
|||
key: `${idx}`,
|
||||
label: `短信${idx + 1}`,
|
||||
children: (<Form colon={false} labelAlign="left" layout="vertical" style={{ marginTop: 10 }}>
|
||||
<Form.Item label="accessKey" >
|
||||
<Form.Item label="accessKey">
|
||||
<>
|
||||
<Input placeholder="请输入accessKey" type="text" value={ele.accessKey} onChange={(e) => setValue(`${idx}.accessKey`, e.target.value)}/>
|
||||
</>
|
||||
</Form.Item>
|
||||
<Form.Item label="securityKey" >
|
||||
<Form.Item label="securityKey">
|
||||
<>
|
||||
<Input placeholder="请输入securityKey" type="text" value={ele.securityKey} onChange={(e) => setValue(`${idx}.securityKey`, e.target.value)}/>
|
||||
</>
|
||||
</Form.Item>
|
||||
<Form.Item label="endpoint" >
|
||||
<Form.Item label="endpoint">
|
||||
<>
|
||||
<Input placeholder="请输入endpoint" type="text" value={ele.endpoint} onChange={(e) => setValue(`${idx}.endpoint`, e.target.value)}/>
|
||||
</>
|
||||
</Form.Item>
|
||||
<Form.Item label="defaultSignName" >
|
||||
<Form.Item label="defaultSignName">
|
||||
<>
|
||||
<Input placeholder="请输入defaultSignName" type="text" value={ele.defaultSignName} onChange={(e) => setValue(`${idx}.defaultSignName`, e.target.value)}/>
|
||||
</>
|
||||
|
|
@ -506,9 +506,8 @@ function CTYun(props) {
|
|||
}
|
||||
export default function Sms(props) {
|
||||
const { sms, setValue, removeItem, cleanKey } = props;
|
||||
const { ali, tencent, mockSend, ctyun } = sms;
|
||||
return (
|
||||
<Space direction="vertical" size="middle" style={{ display: 'flex' }}>
|
||||
const { ali, tencent, ctyun } = sms;
|
||||
return (<Space direction="vertical" size="middle" style={{ display: 'flex' }}>
|
||||
<Row>
|
||||
<Card className={Styles.tips}>
|
||||
每种均可配置一个,相应的服务所使用的帐号请准确对应
|
||||
|
|
@ -518,97 +517,43 @@ export default function Sms(props) {
|
|||
<Divider orientation="left" className={Styles.title}>
|
||||
短信配置
|
||||
</Divider>
|
||||
<Form>
|
||||
<Form.Item
|
||||
label="模拟发送"
|
||||
//name="mockSend"
|
||||
tooltip="开启模拟发送短信,发短信不会调用api"
|
||||
>
|
||||
<Form labelCol={{ span: 8 }} wrapperCol={{ span: 16 }} style={{ maxWidth: 600 }}>
|
||||
<Form.Item label="模拟发送"
|
||||
//name="mockSend"
|
||||
tooltip="开启模拟发送短信,发短信不会调用api">
|
||||
<>
|
||||
<Switch
|
||||
checkedChildren="是"
|
||||
unCheckedChildren="否"
|
||||
checked={mockSend}
|
||||
onChange={(checked) =>
|
||||
setValue(`mockSend`, checked)
|
||||
}
|
||||
/>
|
||||
<Switch checkedChildren="是" unCheckedChildren="否" checked={sms?.mockSend} onChange={(checked) => setValue(`mockSend`, checked)}/>
|
||||
</>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="默认渠道"
|
||||
tooltip="发送短信渠道,如阿里云、腾讯云、天翼云"
|
||||
>
|
||||
<Form.Item label="默认渠道" tooltip="发送短信渠道,如阿里云、腾讯云、天翼云">
|
||||
<>
|
||||
<Select
|
||||
value={sms?.defaultOrigin}
|
||||
style={{ width: 120 }}
|
||||
onChange={(value) => {
|
||||
setValue(`defaultOrigin`, value);
|
||||
}}
|
||||
options={[
|
||||
{ value: 'ali', label: '阿里云' },
|
||||
{ value: 'tencent', label: '腾讯云' },
|
||||
{ value: 'ctyun', label: '天翼云' },
|
||||
]}
|
||||
/>
|
||||
<Select placeholder="请选择渠道" value={sms?.defaultOrigin} style={{ width: 120 }} onChange={(value) => {
|
||||
setValue(`defaultOrigin`, value);
|
||||
}} options={[
|
||||
{ value: 'ali', label: '阿里云' },
|
||||
{ value: 'tencent', label: '腾讯云' },
|
||||
{ value: 'ctyun', label: '天翼云' },
|
||||
]}/>
|
||||
</>
|
||||
</Form.Item>
|
||||
<Form.Item label="验证码模版名" tooltip="">
|
||||
<Input
|
||||
placeholder="请输入defaultCodeTemplateName"
|
||||
type="text"
|
||||
value={sms?.defaultCodeTemplateName}
|
||||
onChange={(e) =>
|
||||
setValue(
|
||||
`defaultCodeTemplateName`,
|
||||
e.target.value
|
||||
)
|
||||
}
|
||||
/>
|
||||
<Form.Item label="验证码模版名" tooltip="短信验证码模版名">
|
||||
<Input placeholder="请输入验证码模版名" type="text" value={sms?.defaultCodeTemplateName} onChange={(e) => setValue(`defaultCodeTemplateName`, e.target.value)}/>
|
||||
</Form.Item>
|
||||
<Form.Item label="验证码有效时间" tooltip="">
|
||||
<Input
|
||||
placeholder="请输入defaultCodeDuration"
|
||||
type="number"
|
||||
value={sms?.defaultCodeDuration}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
if (val) {
|
||||
setValue(
|
||||
`defaultCodeDuration`,
|
||||
Number(val)
|
||||
);
|
||||
} else {
|
||||
setValue(`defaultCodeDuration`, val);
|
||||
}
|
||||
}}
|
||||
suffix="分钟"
|
||||
/>
|
||||
<Form.Item label="验证码有效时间" tooltip="短信验证码发送有效时间">
|
||||
<Input placeholder="请输入验证码有效时间" type="number" value={sms?.defaultCodeDuration} onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
if (val) {
|
||||
setValue(`defaultCodeDuration`, Number(val));
|
||||
}
|
||||
else {
|
||||
setValue(`defaultCodeDuration`, val);
|
||||
}
|
||||
}} suffix="分钟"/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Col>
|
||||
<Tencent
|
||||
sms={tencent || []}
|
||||
setValue={(path, value) => setValue(`tencent.${path}`, value)}
|
||||
removeItem={(path, index) => removeItem(`tencent`, index)}
|
||||
addItem={(path, index) => setValue(`tencent.${index}`, {})}
|
||||
cleanKey={(path, key) => cleanKey(`tencent.${path}`, key)}
|
||||
/>
|
||||
<Ali
|
||||
sms={ali || []}
|
||||
setValue={(path, value) => setValue(`ali.${path}`, value)}
|
||||
removeItem={(path, index) => removeItem(`ali`, index)}
|
||||
addItem={(path, index) => setValue(`ali.${index}`, {})}
|
||||
cleanKey={(path, key) => cleanKey(`ali.${path}`, key)}
|
||||
/>
|
||||
<CTYun
|
||||
sms={ctyun || []}
|
||||
setValue={(path, value) => setValue(`ctyun.${path}`, value)}
|
||||
removeItem={(path, index) => removeItem(`ctyun`, index)}
|
||||
addItem={(path, index) => setValue(`ctyun.${index}`, {})}
|
||||
cleanKey={(path, key) => cleanKey(`ctyun.${path}`, key)}
|
||||
/>
|
||||
</Space>
|
||||
);
|
||||
<Tencent sms={tencent || []} setValue={(path, value) => setValue(`tencent.${path}`, value)} removeItem={(path, index) => removeItem(`tencent`, index)} addItem={(path, index) => setValue(`tencent.${index}`, {})} cleanKey={(path, key) => cleanKey(`tencent.${path}`, key)}/>
|
||||
<Ali sms={ali || []} setValue={(path, value) => setValue(`ali.${path}`, value)} removeItem={(path, index) => removeItem(`ali`, index)} addItem={(path, index) => setValue(`ali.${index}`, {})} cleanKey={(path, key) => cleanKey(`ali.${path}`, key)}/>
|
||||
<CTYun sms={ctyun || []} setValue={(path, value) => setValue(`ctyun.${path}`, value)} removeItem={(path, index) => removeItem(`ctyun`, index)} addItem={(path, index) => setValue(`ctyun.${index}`, {})} cleanKey={(path, key) => cleanKey(`ctyun.${path}`, key)}/>
|
||||
</Space>);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@ import Cos from './cos/index';
|
|||
import Map from './map/index';
|
||||
import Live from './live/index';
|
||||
import Sms from './sms/index';
|
||||
import Basic from './basic/index';
|
||||
export default function Render(props) {
|
||||
const { entity, name, currentConfig, dirty } = props.data;
|
||||
const { resetConfig, updateConfig, setValue, removeItem, cleanKey, t } = props.methods;
|
||||
const { Account: account, Cos: cos, Map: map, Live: live, Sms: sms, } = currentConfig || {};
|
||||
const { Account: account, Cos: cos, Map: map, Live: live, Sms: sms, App: app } = currentConfig || {};
|
||||
return (<>
|
||||
<Affix offsetTop={64}>
|
||||
<Alert message={<div>
|
||||
|
|
@ -62,6 +63,11 @@ export default function Render(props) {
|
|||
label: '短信设置',
|
||||
children: (<Sms sms={sms || {}} setValue={(path, value) => setValue(`Sms.${path}`, value)} removeItem={(path, index) => removeItem(`Sms.${path}`, index)} cleanKey={(path, key) => cleanKey(`Sms.${path}`, key)}/>),
|
||||
},
|
||||
{
|
||||
key: '基础设置',
|
||||
label: '基础设置',
|
||||
children: (<Basic app={app || {}} setValue={(path, value) => setValue(`App.${path}`, value)}/>),
|
||||
},
|
||||
]}></Tabs>
|
||||
</div>
|
||||
</>);
|
||||
|
|
|
|||
|
|
@ -13,19 +13,19 @@ declare const _default: <ED2 extends EntityDict & BaseEntityDict, T2 extends key
|
|||
type?: ButtonProps['type'] | AmButtonProps['type'];
|
||||
executeText?: string | undefined;
|
||||
buttonProps?: (ButtonProps & {
|
||||
color?: "success" | "default" | "primary" | "danger" | "warning" | undefined;
|
||||
color?: "default" | "success" | "warning" | "primary" | "danger" | undefined;
|
||||
fill?: "none" | "solid" | "outline" | undefined;
|
||||
size?: "small" | "large" | "middle" | "mini" | undefined;
|
||||
size?: "small" | "middle" | "large" | "mini" | undefined;
|
||||
block?: boolean | undefined;
|
||||
loading?: boolean | "auto" | undefined;
|
||||
loadingText?: string | undefined;
|
||||
loadingIcon?: import("react").ReactNode;
|
||||
disabled?: boolean | undefined;
|
||||
onClick?: ((event: import("react").MouseEvent<HTMLButtonElement, MouseEvent>) => unknown) | undefined;
|
||||
type?: "button" | "reset" | "submit" | undefined;
|
||||
type?: "button" | "submit" | "reset" | undefined;
|
||||
shape?: "default" | "rounded" | "rectangular" | undefined;
|
||||
children?: import("react").ReactNode;
|
||||
} & Pick<import("react").ClassAttributes<HTMLButtonElement> & import("react").ButtonHTMLAttributes<HTMLButtonElement>, "id" | "onMouseDown" | "onMouseUp" | "onTouchEnd" | "onTouchStart"> & {
|
||||
} & Pick<import("react").ClassAttributes<HTMLButtonElement> & import("react").ButtonHTMLAttributes<HTMLButtonElement>, "id" | "onMouseDown" | "onMouseUp" | "onTouchStart" | "onTouchEnd"> & {
|
||||
className?: string | undefined;
|
||||
style?: (import("react").CSSProperties & Partial<Record<"--text-color" | "--background-color" | "--border-radius" | "--border-width" | "--border-style" | "--border-color", string>>) | undefined;
|
||||
tabIndex?: number | undefined;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ export default OakComponent({
|
|||
config: 1,
|
||||
description: 1,
|
||||
type: 1,
|
||||
systemId: 1,
|
||||
domainId: 1,
|
||||
},
|
||||
properties: {
|
||||
systemId: '',
|
||||
|
|
@ -15,5 +17,5 @@ export default OakComponent({
|
|||
return {
|
||||
applications: data || [],
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Tabs, Modal, Button } from 'antd';
|
||||
import { Tabs, Modal, Button, Space } from 'antd';
|
||||
import ApplicationPanel from '../../application/panel';
|
||||
import ApplicationUpsert from '../../application/upsert';
|
||||
export default function render(props) {
|
||||
|
|
@ -9,21 +9,29 @@ export default function render(props) {
|
|||
const [removeId, setRemoveId] = useState('');
|
||||
if (oakFullpath && applications?.length > 0) {
|
||||
return (<>
|
||||
<Modal open={!!createId} width={800} onCancel={() => {
|
||||
<Modal open={!!createId} width={500} onCancel={() => {
|
||||
clean();
|
||||
setCreateId('');
|
||||
}} footer={<Button type='primary' onClick={async () => {
|
||||
}} footer={<Space>
|
||||
<Button onClick={async () => {
|
||||
clean();
|
||||
setCreateId('');
|
||||
}} disabled={oakExecuting}>
|
||||
{t('common::action.cancel')}
|
||||
</Button>
|
||||
<Button type="primary" onClick={async () => {
|
||||
await execute();
|
||||
setCreateId('');
|
||||
}} disabled={oakExecutable !== true || oakExecuting}>
|
||||
{t('common::action.confirm')}
|
||||
</Button>}>
|
||||
{t('common::action.confirm')}
|
||||
</Button>
|
||||
</Space>}>
|
||||
<ApplicationUpsert oakId={createId} oakPath={`${oakFullpath}.${createId}`}/>
|
||||
</Modal>
|
||||
<Modal open={!!removeId} onCancel={() => {
|
||||
clean();
|
||||
setRemoveId('');
|
||||
}} footer={<Button type='primary' onClick={async () => {
|
||||
}} footer={<Button type="primary" onClick={async () => {
|
||||
removeItem(removeId);
|
||||
await execute();
|
||||
setRemoveId('');
|
||||
|
|
@ -41,12 +49,12 @@ export default function render(props) {
|
|||
const appId = applications[Number(key)].id;
|
||||
setRemoveId(appId);
|
||||
}
|
||||
}} items={applications?.length > 0 ?
|
||||
applications.map((item, idx) => {
|
||||
}} items={applications?.length > 0
|
||||
? applications.map((item, idx) => {
|
||||
return {
|
||||
label: item.name,
|
||||
key: `${idx}`,
|
||||
children: (<ApplicationPanel oakPath={`${oakFullpath}.${item.id}`} oakId={item.id}/>)
|
||||
children: (<ApplicationPanel oakPath={`${oakFullpath}.${item.id}`} oakId={item.id}/>),
|
||||
};
|
||||
})
|
||||
: []}/>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Row, Modal, Descriptions, Typography, Button } from 'antd';
|
||||
import { Row, Modal, Descriptions, Typography, Button, Space } from 'antd';
|
||||
import SystemUpsert from '../upsert';
|
||||
import Styles from './web.pc.module.less';
|
||||
export default function Render(props) {
|
||||
|
|
@ -11,12 +11,22 @@ export default function Render(props) {
|
|||
<Modal open={open} onCancel={() => {
|
||||
clean();
|
||||
setOpen(false);
|
||||
}} width={800} footer={<Button type='primary' onClick={async () => {
|
||||
}} width={500} footer={<Space>
|
||||
<Button
|
||||
// type='primary'
|
||||
onClick={async () => {
|
||||
clean();
|
||||
setOpen(false);
|
||||
}} disabled={oakExecuting}>
|
||||
{t('common::action.cancel')}
|
||||
</Button>
|
||||
<Button type="primary" onClick={async () => {
|
||||
await execute();
|
||||
setOpen(false);
|
||||
}} disabled={oakExecutable !== true || oakExecuting}>
|
||||
{t('common::action.confirm')}
|
||||
</Button>}>
|
||||
{t('common::action.confirm')}
|
||||
</Button>
|
||||
</Space>}>
|
||||
<div className={Styles.upsert}>
|
||||
<SystemUpsert oakId={oakId} oakPath={oakFullpath}/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ export default OakComponent({
|
|||
config: 1,
|
||||
description: 1,
|
||||
type: 1,
|
||||
systemId: 1,
|
||||
domainId: 1,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import SmsTemplateList from '../../messageTypeSmsTemplate/tab';
|
|||
import ApplicationList from '../application';
|
||||
import Styles from './web.pc.module.less';
|
||||
export default function Render(props) {
|
||||
const { id, config, oakFullpath, name, style, application$system: applications } = props.data;
|
||||
const { t, update, addItem, removeItem } = props.methods;
|
||||
const { id, config, oakFullpath, name, style } = props.data;
|
||||
const { t, } = props.methods;
|
||||
if (id && oakFullpath) {
|
||||
return (<div className={Styles.container}>
|
||||
<Tabs tabPosition='left' items={[
|
||||
|
|
|
|||
|
|
@ -1,6 +1,14 @@
|
|||
export default OakComponent({
|
||||
isList: false,
|
||||
entity: 'system',
|
||||
projection: {
|
||||
id: 1,
|
||||
name: 1,
|
||||
config: 1,
|
||||
description: 1,
|
||||
super: 1,
|
||||
folder: 1,
|
||||
},
|
||||
formData({ data }) {
|
||||
return data || {};
|
||||
},
|
||||
|
|
|
|||
|
|
@ -15,11 +15,7 @@ export default function Render(props) {
|
|||
</Form.Item>
|
||||
<Form.Item label="目录" required
|
||||
// name="folder"
|
||||
tooltip="目录属性应和开发目录下的对应目录名匹配,请谨慎修改" rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}>
|
||||
tooltip="目录属性应和开发目录下的对应目录名匹配,请谨慎修改">
|
||||
<>
|
||||
<Input onChange={(e) => {
|
||||
update({
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ declare const _default: (props: import("oak-frontend-base").ReactComponentProps<
|
|||
entity: keyof EntityDict;
|
||||
entityFilter: any;
|
||||
relationIds: string[];
|
||||
rule: "single" | "all" | "free";
|
||||
ruleOnRow: "single" | "all" | "free";
|
||||
rule: "all" | "single" | "free";
|
||||
ruleOnRow: "all" | "single" | "free";
|
||||
onPickRelations: (ids: string[]) => void;
|
||||
onPickRows: (ids: string[]) => void;
|
||||
pickedRowIds: string[] | undefined;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ declare const _default: (props: import("oak-frontend-base").ReactComponentProps<
|
|||
claimUrl: string;
|
||||
qrCodeType: QrCodeType;
|
||||
multiple: boolean;
|
||||
rule: "single" | "all" | "free";
|
||||
ruleOnRow: "single" | "all" | "free";
|
||||
rule: "all" | "single" | "free";
|
||||
ruleOnRow: "all" | "single" | "free";
|
||||
}>) => import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>>;
|
||||
export default _default;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/// <reference types="react" />
|
||||
import { EntityDict } from '../../../oak-app-domain';
|
||||
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<EntityDict, keyof EntityDict, false, {
|
||||
type: "bind" | "login";
|
||||
type: "login" | "bind";
|
||||
url: string;
|
||||
}>) => import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>>;
|
||||
export default _default;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { String, Text } from 'oak-domain/lib/types/DataType';
|
|||
import { EntityShape } from 'oak-domain/lib/types/Entity';
|
||||
import { Schema as System } from './System';
|
||||
import { Schema as Session } from './Session';
|
||||
import { Schema as Domain } from './Domain';
|
||||
import { Style } from '../types/Style';
|
||||
export type Passport = 'email' | 'mobile' | 'wechat' | 'wechatPublic';
|
||||
export type AppType = 'web' | 'wechatMp' | 'wechatPublic' | 'native';
|
||||
|
|
@ -63,4 +64,5 @@ export interface Schema extends EntityShape {
|
|||
config: WebConfig | WechatMpConfig | WechatPublicConfig | NativeConfig;
|
||||
style?: Style;
|
||||
sessions?: Session[];
|
||||
domain?: Domain;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ const entityDesc = {
|
|||
config: '设置',
|
||||
style: '样式',
|
||||
sessions: '会话',
|
||||
domain: '域名',
|
||||
},
|
||||
v: {
|
||||
type: {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,6 @@ export default class Theme<ED extends EntityDict, Cxt extends BackendRuntimeCont
|
|||
switchThemeMode(finalThemeMode: ETheme): void;
|
||||
openSystemTheme(): void;
|
||||
getColor(): string;
|
||||
switchColor(color: string): void;
|
||||
insertThemeStylesheet(theme: string, color: string, mode: ETheme): void;
|
||||
switchColor(color: string, theme?: string): void;
|
||||
insertThemeStylesheet(color: string, mode: ETheme, theme?: string): void;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,11 +6,7 @@ const initialThemeState = {
|
|||
setting: false,
|
||||
themeMode: defaultTheme,
|
||||
systemTheme: false,
|
||||
isFullPage: false,
|
||||
color: '#0052d9',
|
||||
showHeader: true,
|
||||
showBreadcrumbs: true,
|
||||
showFooter: true,
|
||||
};
|
||||
export default class Theme extends Feature {
|
||||
cache;
|
||||
|
|
@ -82,15 +78,17 @@ export default class Theme extends Feature {
|
|||
const state = this.themeState;
|
||||
return state.color;
|
||||
}
|
||||
switchColor(color) {
|
||||
switchColor(color, theme) {
|
||||
const state = this.themeState;
|
||||
if (color) {
|
||||
state.color = color; // 某主题 主题色
|
||||
const themeColor = 'blue'; // 主题颜色类型
|
||||
// const themeColor = 'blue'; // 主题颜色类型
|
||||
switch (process.env.OAK_PLATFORM) {
|
||||
case 'web': {
|
||||
this.insertThemeStylesheet(themeColor, color, state.themeMode);
|
||||
document.documentElement.setAttribute('theme-color', themeColor);
|
||||
this.insertThemeStylesheet(color, state.themeMode, theme);
|
||||
if (theme) {
|
||||
document.documentElement.setAttribute('theme-color', theme);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
|
@ -100,11 +98,13 @@ export default class Theme extends Feature {
|
|||
this.set(state);
|
||||
}
|
||||
}
|
||||
insertThemeStylesheet(theme, color, mode) {
|
||||
insertThemeStylesheet(color, mode, theme) {
|
||||
const isDarkMode = mode === 'dark';
|
||||
const root = !isDarkMode
|
||||
? `:root[theme-color='${theme}']`
|
||||
: `:root[theme-color='${theme}'][theme-mode='dark']`;
|
||||
? (theme
|
||||
? `:root[theme-color='${theme}']`
|
||||
: `:root`)
|
||||
: (theme ? `:root[theme-color='${theme}'][theme-mode='dark']` : `:root[theme-mode='dark']`);
|
||||
const styleSheet = document.createElement('style');
|
||||
styleSheet.type = 'text/css';
|
||||
styleSheet.innerText = `${root}{
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { GenericAction } from "oak-domain/lib/actions/action";
|
|||
import { String, Text } from "oak-domain/lib/types/DataType";
|
||||
import { Style } from "../../types/Style";
|
||||
import * as System from "../System/Schema";
|
||||
import * as Domain from "../Domain/Schema";
|
||||
import * as ExtraFile from "../ExtraFile/Schema";
|
||||
import * as Notification from "../Notification/Schema";
|
||||
import * as SessionMessage from "../SessionMessage/Schema";
|
||||
|
|
@ -76,6 +77,7 @@ export type OpSchema = EntityShape & {
|
|||
systemId: ForeignKey<"system">;
|
||||
config: WebConfig | WechatMpConfig | WechatPublicConfig | NativeConfig;
|
||||
style?: Style | null;
|
||||
domainId?: ForeignKey<"domain"> | null;
|
||||
};
|
||||
export type OpAttr = keyof OpSchema;
|
||||
export type Schema = EntityShape & {
|
||||
|
|
@ -85,7 +87,9 @@ export type Schema = EntityShape & {
|
|||
systemId: ForeignKey<"system">;
|
||||
config: WebConfig | WechatMpConfig | WechatPublicConfig | NativeConfig;
|
||||
style?: Style | null;
|
||||
domainId?: ForeignKey<"domain"> | null;
|
||||
system: System.Schema;
|
||||
domain?: Domain.Schema | null;
|
||||
extraFile$application?: Array<ExtraFile.Schema>;
|
||||
extraFile$application$$aggr?: AggregationResult<ExtraFile.Schema>;
|
||||
notification$application?: Array<Notification.Schema>;
|
||||
|
|
@ -123,6 +127,8 @@ type AttrFilter = {
|
|||
system: System.Filter;
|
||||
config: JsonFilter<WebConfig | WechatMpConfig | WechatPublicConfig | NativeConfig>;
|
||||
style: JsonFilter<Style>;
|
||||
domainId: Q_StringValue;
|
||||
domain: Domain.Filter;
|
||||
extraFile$application: ExtraFile.Filter & SubQueryPredicateMetadata;
|
||||
notification$application: Notification.Filter & SubQueryPredicateMetadata;
|
||||
sessionMessage$application: SessionMessage.Filter & SubQueryPredicateMetadata;
|
||||
|
|
@ -150,6 +156,8 @@ export type Projection = {
|
|||
system?: System.Projection;
|
||||
config?: number | JsonProjection<WebConfig | WechatMpConfig | WechatPublicConfig | NativeConfig>;
|
||||
style?: number | JsonProjection<Style>;
|
||||
domainId?: number;
|
||||
domain?: Domain.Projection;
|
||||
extraFile$application?: ExtraFile.Selection & {
|
||||
$entity: "extraFile";
|
||||
};
|
||||
|
|
@ -223,6 +231,9 @@ type ApplicationIdProjection = OneOf<{
|
|||
type SystemIdProjection = OneOf<{
|
||||
systemId: number;
|
||||
}>;
|
||||
type DomainIdProjection = OneOf<{
|
||||
domainId: number;
|
||||
}>;
|
||||
export type SortAttr = {
|
||||
id: number;
|
||||
} | {
|
||||
|
|
@ -243,6 +254,10 @@ export type SortAttr = {
|
|||
system: System.SortAttr;
|
||||
} | {
|
||||
style: number;
|
||||
} | {
|
||||
domainId: number;
|
||||
} | {
|
||||
domain: Domain.SortAttr;
|
||||
} | {
|
||||
[k: string]: any;
|
||||
} | OneOf<ExprOp<OpAttr | string>>;
|
||||
|
|
@ -254,7 +269,7 @@ export type Sorter = SortNode[];
|
|||
export type SelectOperation<P extends Object = Projection> = OakSelection<"select", P, Filter, Sorter>;
|
||||
export type Selection<P extends Object = Projection> = SelectOperation<P>;
|
||||
export type Aggregation = DeduceAggregation<Projection, Filter, Sorter>;
|
||||
export type CreateOperationData = FormCreateData<Omit<OpSchema, "systemId">> & (({
|
||||
export type CreateOperationData = FormCreateData<Omit<OpSchema, "systemId" | "domainId">> & (({
|
||||
systemId?: never;
|
||||
system: System.CreateSingleOperation;
|
||||
} | {
|
||||
|
|
@ -263,6 +278,15 @@ export type CreateOperationData = FormCreateData<Omit<OpSchema, "systemId">> & (
|
|||
} | {
|
||||
system?: never;
|
||||
systemId: ForeignKey<"system">;
|
||||
}) & ({
|
||||
domainId?: never;
|
||||
domain?: Domain.CreateSingleOperation;
|
||||
} | {
|
||||
domainId: ForeignKey<"domain">;
|
||||
domain?: Domain.UpdateOperation;
|
||||
} | {
|
||||
domain?: never;
|
||||
domainId?: ForeignKey<"domain">;
|
||||
})) & {
|
||||
extraFile$application?: OakOperation<ExtraFile.UpdateOperation["action"], Omit<ExtraFile.UpdateOperationData, "application" | "applicationId">, Omit<ExtraFile.Filter, "application" | "applicationId">> | OakOperation<"create", Omit<ExtraFile.CreateOperationData, "application" | "applicationId">[]> | Array<OakOperation<"create", Omit<ExtraFile.CreateOperationData, "application" | "applicationId">> | OakOperation<ExtraFile.UpdateOperation["action"], Omit<ExtraFile.UpdateOperationData, "application" | "applicationId">, Omit<ExtraFile.Filter, "application" | "applicationId">>>;
|
||||
notification$application?: OakOperation<Notification.UpdateOperation["action"], Omit<Notification.UpdateOperationData, "application" | "applicationId">, Omit<Notification.Filter, "application" | "applicationId">> | OakOperation<"create", Omit<Notification.CreateOperationData, "application" | "applicationId">[]> | Array<OakOperation<"create", Omit<Notification.CreateOperationData, "application" | "applicationId">> | OakOperation<Notification.UpdateOperation["action"], Omit<Notification.UpdateOperationData, "application" | "applicationId">, Omit<Notification.Filter, "application" | "applicationId">>>;
|
||||
|
|
@ -279,7 +303,7 @@ export type CreateOperationData = FormCreateData<Omit<OpSchema, "systemId">> & (
|
|||
export type CreateSingleOperation = OakOperation<"create", CreateOperationData>;
|
||||
export type CreateMultipleOperation = OakOperation<"create", Array<CreateOperationData>>;
|
||||
export type CreateOperation = CreateSingleOperation | CreateMultipleOperation;
|
||||
export type UpdateOperationData = FormUpdateData<Omit<OpSchema, "systemId">> & (({
|
||||
export type UpdateOperationData = FormUpdateData<Omit<OpSchema, "systemId" | "domainId">> & (({
|
||||
system?: System.CreateSingleOperation;
|
||||
systemId?: never;
|
||||
} | {
|
||||
|
|
@ -291,6 +315,18 @@ export type UpdateOperationData = FormUpdateData<Omit<OpSchema, "systemId">> & (
|
|||
} | {
|
||||
system?: never;
|
||||
systemId?: ForeignKey<"system">;
|
||||
}) & ({
|
||||
domain?: Domain.CreateSingleOperation;
|
||||
domainId?: never;
|
||||
} | {
|
||||
domain?: Domain.UpdateOperation;
|
||||
domainId?: never;
|
||||
} | {
|
||||
domain?: Domain.RemoveOperation;
|
||||
domainId?: never;
|
||||
} | {
|
||||
domain?: never;
|
||||
domainId?: ForeignKey<"domain"> | null;
|
||||
})) & {
|
||||
[k: string]: any;
|
||||
extraFile$application?: OakOperation<ExtraFile.UpdateOperation["action"], Omit<ExtraFile.UpdateOperationData, "application" | "applicationId">, Omit<ExtraFile.Filter, "application" | "applicationId">> | OakOperation<ExtraFile.RemoveOperation["action"], Omit<ExtraFile.RemoveOperationData, "application" | "applicationId">, Omit<ExtraFile.Filter, "application" | "applicationId">> | OakOperation<"create", Omit<ExtraFile.CreateOperationData, "application" | "applicationId">[]> | Array<OakOperation<"create", Omit<ExtraFile.CreateOperationData, "application" | "applicationId">> | OakOperation<ExtraFile.UpdateOperation["action"], Omit<ExtraFile.UpdateOperationData, "application" | "applicationId">, Omit<ExtraFile.Filter, "application" | "applicationId">> | OakOperation<ExtraFile.RemoveOperation["action"], Omit<ExtraFile.RemoveOperationData, "application" | "applicationId">, Omit<ExtraFile.Filter, "application" | "applicationId">>>;
|
||||
|
|
@ -308,10 +344,13 @@ export type UpdateOperationData = FormUpdateData<Omit<OpSchema, "systemId">> & (
|
|||
export type UpdateOperation = OakOperation<"update" | string, UpdateOperationData, Filter, Sorter>;
|
||||
export type RemoveOperationData = {} & (({
|
||||
system?: System.UpdateOperation | System.RemoveOperation;
|
||||
}) & ({
|
||||
domain?: Domain.UpdateOperation | Domain.RemoveOperation;
|
||||
}));
|
||||
export type RemoveOperation = OakOperation<"remove", RemoveOperationData, Filter, Sorter>;
|
||||
export type Operation = CreateOperation | UpdateOperation | RemoveOperation;
|
||||
export type SystemIdSubQuery = Selection<SystemIdProjection>;
|
||||
export type DomainIdSubQuery = Selection<DomainIdProjection>;
|
||||
export type ApplicationIdSubQuery = Selection<ApplicationIdProjection>;
|
||||
export type EntityDef = {
|
||||
Schema: Schema;
|
||||
|
|
|
|||
|
|
@ -28,6 +28,10 @@ export const desc = {
|
|||
},
|
||||
style: {
|
||||
type: "object"
|
||||
},
|
||||
domainId: {
|
||||
type: "ref",
|
||||
ref: "domain"
|
||||
}
|
||||
},
|
||||
actionType: "crud",
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{ "name": "应用", "attr": { "description": "描述", "type": "类型", "system": "系统", "name": "名称", "config": "设置", "style": "样式", "sessions": "会话" }, "v": { "type": { "web": "网站", "wechatPublic": "微信公众号", "wechatMp": "微信小程序", "native": "App" } } }
|
||||
{ "name": "应用", "attr": { "description": "描述", "type": "类型", "system": "系统", "name": "名称", "config": "设置", "style": "样式", "sessions": "会话", "domain": "域名" }, "v": { "type": { "web": "网站", "wechatPublic": "微信公众号", "wechatMp": "微信小程序", "native": "App" } } }
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import { ForeignKey } from "oak-domain/lib/types/DataType";
|
||||
import { Q_DateValue, Q_NumberValue, Q_StringValue, Q_EnumValue, NodeId, MakeFilter, ExprOp, ExpressionKey } from "oak-domain/lib/types/Demand";
|
||||
import { Q_DateValue, Q_NumberValue, Q_StringValue, Q_EnumValue, NodeId, MakeFilter, ExprOp, ExpressionKey, SubQueryPredicateMetadata } from "oak-domain/lib/types/Demand";
|
||||
import { OneOf } from "oak-domain/lib/types/Polyfill";
|
||||
import { FormCreateData, FormUpdateData, DeduceAggregation, Operation as OakOperation, Selection as OakSelection, MakeAction as OakMakeAction, EntityShape } from "oak-domain/lib/types/Entity";
|
||||
import { FormCreateData, FormUpdateData, DeduceAggregation, Operation as OakOperation, Selection as OakSelection, MakeAction as OakMakeAction, AggregationResult, EntityShape } from "oak-domain/lib/types/Entity";
|
||||
import { GenericAction } from "oak-domain/lib/actions/action";
|
||||
import { String, Int } from "oak-domain/lib/types/DataType";
|
||||
import * as System from "../System/Schema";
|
||||
import * as Application from "../Application/Schema";
|
||||
export type OpSchema = EntityShape & {
|
||||
url: String<64>;
|
||||
apiPath?: String<32> | null;
|
||||
|
|
@ -20,6 +21,8 @@ export type Schema = EntityShape & {
|
|||
port: Int<2>;
|
||||
systemId: ForeignKey<"system">;
|
||||
system: System.Schema;
|
||||
application$domain?: Array<Application.Schema>;
|
||||
application$domain$$aggr?: AggregationResult<Application.Schema>;
|
||||
} & {
|
||||
[A in ExpressionKey]?: any;
|
||||
};
|
||||
|
|
@ -34,6 +37,7 @@ type AttrFilter = {
|
|||
port: Q_NumberValue;
|
||||
systemId: Q_StringValue;
|
||||
system: System.Filter;
|
||||
application$domain: Application.Filter & SubQueryPredicateMetadata;
|
||||
};
|
||||
export type Filter = MakeFilter<AttrFilter & ExprOp<OpAttr | string>>;
|
||||
export type Projection = {
|
||||
|
|
@ -49,6 +53,12 @@ export type Projection = {
|
|||
port?: number;
|
||||
systemId?: number;
|
||||
system?: System.Projection;
|
||||
application$domain?: Application.Selection & {
|
||||
$entity: "application";
|
||||
};
|
||||
application$domain$$aggr?: Application.Aggregation & {
|
||||
$entity: "application";
|
||||
};
|
||||
} & Partial<ExprOp<OpAttr | string>>;
|
||||
type DomainIdProjection = OneOf<{
|
||||
id: number;
|
||||
|
|
@ -96,7 +106,9 @@ export type CreateOperationData = FormCreateData<Omit<OpSchema, "systemId">> & (
|
|||
} | {
|
||||
system?: never;
|
||||
systemId: ForeignKey<"system">;
|
||||
}));
|
||||
})) & {
|
||||
application$domain?: OakOperation<Application.UpdateOperation["action"], Omit<Application.UpdateOperationData, "domain" | "domainId">, Omit<Application.Filter, "domain" | "domainId">> | OakOperation<"create", Omit<Application.CreateOperationData, "domain" | "domainId">[]> | Array<OakOperation<"create", Omit<Application.CreateOperationData, "domain" | "domainId">> | OakOperation<Application.UpdateOperation["action"], Omit<Application.UpdateOperationData, "domain" | "domainId">, Omit<Application.Filter, "domain" | "domainId">>>;
|
||||
};
|
||||
export type CreateSingleOperation = OakOperation<"create", CreateOperationData>;
|
||||
export type CreateMultipleOperation = OakOperation<"create", Array<CreateOperationData>>;
|
||||
export type CreateOperation = CreateSingleOperation | CreateMultipleOperation;
|
||||
|
|
@ -114,6 +126,7 @@ export type UpdateOperationData = FormUpdateData<Omit<OpSchema, "systemId">> & (
|
|||
systemId?: ForeignKey<"system">;
|
||||
})) & {
|
||||
[k: string]: any;
|
||||
application$domain?: OakOperation<Application.UpdateOperation["action"], Omit<Application.UpdateOperationData, "domain" | "domainId">, Omit<Application.Filter, "domain" | "domainId">> | OakOperation<Application.RemoveOperation["action"], Omit<Application.RemoveOperationData, "domain" | "domainId">, Omit<Application.Filter, "domain" | "domainId">> | OakOperation<"create", Omit<Application.CreateOperationData, "domain" | "domainId">[]> | Array<OakOperation<"create", Omit<Application.CreateOperationData, "domain" | "domainId">> | OakOperation<Application.UpdateOperation["action"], Omit<Application.UpdateOperationData, "domain" | "domainId">, Omit<Application.Filter, "domain" | "domainId">> | OakOperation<Application.RemoveOperation["action"], Omit<Application.RemoveOperationData, "domain" | "domainId">, Omit<Application.Filter, "domain" | "domainId">>>;
|
||||
};
|
||||
export type UpdateOperation = OakOperation<"update" | string, UpdateOperationData, Filter, Sorter>;
|
||||
export type RemoveOperationData = {} & (({
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ export const desc = {
|
|||
type: "object"
|
||||
}
|
||||
},
|
||||
static: true,
|
||||
actionType: "crud",
|
||||
actions,
|
||||
indexes: [
|
||||
|
|
|
|||
|
|
@ -273,7 +273,9 @@ export type ChangePasswordTempIdSubQuery = {
|
|||
}) | any;
|
||||
};
|
||||
export type DomainIdSubQuery = {
|
||||
[K in "$in" | "$nin"]?: (Domain.DomainIdSubQuery & {
|
||||
[K in "$in" | "$nin"]?: (Application.DomainIdSubQuery & {
|
||||
entity: "application";
|
||||
}) | (Domain.DomainIdSubQuery & {
|
||||
entity: "domain";
|
||||
}) | any;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export declare function createToDo<ED extends EntityDict & BaseEntityDict, T ext
|
|||
redirectTo: EntityDict['toDo']['OpSchema']['redirectTo'];
|
||||
entity: any;
|
||||
entityId: string;
|
||||
}, userIds?: string[]): Promise<0 | 1>;
|
||||
}, userIds?: string[]): Promise<1 | 0>;
|
||||
/**
|
||||
* 完成todo例程,当在entity对象上进行action操作时(操作条件是filter),将对应的todo完成
|
||||
* 必须在entity的action的后trigger中调用
|
||||
|
|
|
|||
|
|
@ -105,9 +105,9 @@ export type Config = {
|
|||
};
|
||||
Sms?: {
|
||||
mockSend?: boolean;
|
||||
defaultOrigin?: 'ali' | 'tencent' | 'ctyun'; //默认渠道
|
||||
defaultCodeTemplateName?: string; //验证码模版名
|
||||
defaultCodeDuration?: number; //验证码有效时间 单位分钟, 不填1分钟
|
||||
defaultOrigin?: 'ali' | 'tencent' | 'ctyun';
|
||||
defaultCodeTemplateName?: string;
|
||||
defaultCodeDuration?: number;
|
||||
ali?: AliSmsConfig[];
|
||||
tencent?: TencentSmsConfig[];
|
||||
ctyun?: CTYunSmsConfig[];
|
||||
|
|
@ -118,6 +118,8 @@ export type Config = {
|
|||
qrCodePublicForMpId?: string;
|
||||
mpShareImageUrl?: string;
|
||||
mergeUserDirectly?: boolean;
|
||||
tokenRefreshTime?: number;
|
||||
tokenExpireTime?: number;
|
||||
};
|
||||
};
|
||||
export type Origin = 'ali' | 'tencent' | 'qiniu' | 'amap' | 'ctyun';
|
||||
|
|
|
|||
|
|
@ -129,8 +129,17 @@ export const applicationProjection = {
|
|||
apiPath: 1,
|
||||
protocol: 1,
|
||||
port: 1,
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
domainId: 1,
|
||||
domain: {
|
||||
id: 1,
|
||||
systemId: 1,
|
||||
url: 1,
|
||||
apiPath: 1,
|
||||
protocol: 1,
|
||||
port: 1,
|
||||
},
|
||||
};
|
||||
export const extraFileProjection = {
|
||||
|
|
|
|||
|
|
@ -13,8 +13,4 @@ export interface IThemeState {
|
|||
* 是否开启跟随系统主题
|
||||
*/
|
||||
systemTheme: boolean;
|
||||
isFullPage: boolean;
|
||||
showHeader: boolean;
|
||||
showBreadcrumbs: boolean;
|
||||
showFooter: boolean;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { File } from 'formidable';
|
|||
export declare function getApplication<ED extends EntityDict, Cxt extends BackendRuntimeContext<ED>>(params: {
|
||||
type: AppType;
|
||||
domain: string;
|
||||
}, context: Cxt): Promise<string>;
|
||||
}, context: Cxt): Promise<string | undefined>;
|
||||
export declare function signatureJsSDK<ED extends EntityDict, Cxt extends BackendRuntimeContext<ED>>({ url, env }: {
|
||||
url: string;
|
||||
env: WebEnv;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,64 @@ async function getApplication(params, context) {
|
|||
const { type, domain } = params;
|
||||
const url = context.getHeader('host');
|
||||
console.log('url is', url);
|
||||
const [application] = await context.select('application', {
|
||||
|
||||
// const [application] = await context.select(
|
||||
// 'application',
|
||||
// {
|
||||
// data: cloneDeep(applicationProjection),
|
||||
// filter: {
|
||||
// type,
|
||||
// system: {
|
||||
// domain$system: {
|
||||
// url: domain,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// {}
|
||||
// );
|
||||
// //微信小程序环境下 没有就报错
|
||||
// if (type === 'wechatMp') {
|
||||
// assert(
|
||||
// application,
|
||||
// '微信小程序环境下 application必须存在小程序相关配置'
|
||||
// );
|
||||
// } else if (type === 'native') {
|
||||
// assert(application, 'APP环境下 application必须存在APP相关配置');
|
||||
// } else {
|
||||
// //web 或 wechatPublic
|
||||
// if (type === 'wechatPublic') {
|
||||
// // 如果微信公众号环境下 application不存在公众号配置,但又在公众号访问,这时可以使用web的application
|
||||
// if (!application) {
|
||||
// const [application2] = await context.select(
|
||||
// 'application',
|
||||
// {
|
||||
// data: cloneDeep(applicationProjection),
|
||||
// filter: {
|
||||
// type: 'web',
|
||||
// system: {
|
||||
// domain$system: {
|
||||
// url: domain,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// {}
|
||||
// );
|
||||
// assert(
|
||||
// application2,
|
||||
// '微信公众号环境下 application不存在公众号配置,但必须存在web相关配置'
|
||||
// );
|
||||
// return application2.id as string;
|
||||
// }
|
||||
// } else {
|
||||
// assert(application, 'web环境下 application必须存在web相关配置');
|
||||
// }
|
||||
// }
|
||||
// return application.id as string;
|
||||
// 先找指定domain的应用,如果不存在再找系统下面的domain, 但无论怎么样都必须一项
|
||||
//
|
||||
let applications = await context.select('application', {
|
||||
data: (0, lodash_1.cloneDeep)(Projection_1.applicationProjection),
|
||||
filter: {
|
||||
type,
|
||||
|
|
@ -21,21 +78,49 @@ async function getApplication(params, context) {
|
|||
url: domain,
|
||||
},
|
||||
},
|
||||
domain: {
|
||||
url: domain,
|
||||
},
|
||||
},
|
||||
}, {});
|
||||
//微信小程序环境下 没有就报错
|
||||
if (type === 'wechatMp') {
|
||||
(0, assert_1.assert)(application, '微信小程序环境下 application必须存在小程序相关配置');
|
||||
(0, assert_1.assert)(applications.length <= 1, `指定域名的应用 只能存在一项或未指定`);
|
||||
if (applications.length === 0) {
|
||||
applications = await context.select(
|
||||
'application',
|
||||
{
|
||||
data: (0, lodash_1.cloneDeep)(
|
||||
Projection_1.applicationProjection
|
||||
),
|
||||
filter: {
|
||||
type,
|
||||
system: {
|
||||
domain$system: {
|
||||
url: domain,
|
||||
},
|
||||
},
|
||||
domainId: {
|
||||
$exists: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{}
|
||||
);
|
||||
}
|
||||
else if (type === 'native') {
|
||||
(0, assert_1.assert)(application, 'APP环境下 application必须存在APP相关配置');
|
||||
}
|
||||
else {
|
||||
//web 或 wechatPublic
|
||||
if (type === 'wechatPublic') {
|
||||
// 如果微信公众号环境下 application不存在公众号配置,但又在公众号访问,这时可以使用web的application
|
||||
if (!application) {
|
||||
const [application2] = await context.select('application', {
|
||||
switch (type) {
|
||||
case 'wechatMp': {
|
||||
(0, assert_1.assert)(applications.length === 1, `微信小程序环境下,同一个系统必须存在唯一的【${type}】应用`);
|
||||
const application = applications[0];
|
||||
return application.id;
|
||||
}
|
||||
case 'native': {
|
||||
(0, assert_1.assert)(applications.length === 1, `APP环境下,同一个系统必须存在唯一的【${type}】应用`);
|
||||
const application = applications[0];
|
||||
return application.id;
|
||||
}
|
||||
case 'wechatPublic': {
|
||||
// 微信公众号环境下,未配置公众号,可以使用web的application
|
||||
if (applications.length === 0) {
|
||||
let applications2 = await context.select('application', {
|
||||
data: (0, lodash_1.cloneDeep)(Projection_1.applicationProjection),
|
||||
filter: {
|
||||
type: 'web',
|
||||
|
|
@ -44,17 +129,52 @@ async function getApplication(params, context) {
|
|||
url: domain,
|
||||
},
|
||||
},
|
||||
domain: {
|
||||
url: domain,
|
||||
},
|
||||
},
|
||||
}, {});
|
||||
(0, assert_1.assert)(application2, '微信公众号环境下 application不存在公众号配置,但必须存在web相关配置');
|
||||
return application2.id;
|
||||
(0, assert_1.assert)(applications2.length <= 1, `指定域名的应用 只能存在一项或未指定`);
|
||||
if (applications2.length === 0) {
|
||||
applications2 = await context.select(
|
||||
'application',
|
||||
{
|
||||
data: (0, lodash_1.cloneDeep)(
|
||||
Projection_1.applicationProjection
|
||||
),
|
||||
filter: {
|
||||
type,
|
||||
system: {
|
||||
domain$system: {
|
||||
url: domain,
|
||||
},
|
||||
},
|
||||
domainId: {
|
||||
$exists: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{}
|
||||
);
|
||||
}
|
||||
(0, assert_1.assert)(applications2.length === 1, '微信公众号环境下, 可以未配置公众号,但必须存在web的application');
|
||||
const application = applications2[0];
|
||||
return application.id;
|
||||
}
|
||||
(0, assert_1.assert)(applications.length === 1, `微信公众号环境下,同一个系统必须存在唯一的【${type}】应用 或 多个${type}应用必须配置域名`);
|
||||
const application = applications[0];
|
||||
return application.id;
|
||||
}
|
||||
else {
|
||||
(0, assert_1.assert)(application, 'web环境下 application必须存在web相关配置');
|
||||
case 'web': {
|
||||
(0, assert_1.assert)(applications.length === 1, `web环境下,同一个系统必须存在唯一的【${type}】应用 或 多个${type}应用必须配置域名`);
|
||||
const application = applications[0];
|
||||
return application.id;
|
||||
}
|
||||
default: {
|
||||
(0, assert_1.assert)(false, `不支持的类型【${type}】`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
return application.id;
|
||||
}
|
||||
exports.getApplication = getApplication;
|
||||
async function signatureJsSDK({ url, env }, context) {
|
||||
|
|
@ -70,7 +190,7 @@ async function signatureJsSDK({ url, env }, context) {
|
|||
exports.signatureJsSDK = signatureJsSDK;
|
||||
async function uploadWechatMedia(params, // FormData表单提交 isPermanent 变成 'true' | 'false'
|
||||
context) {
|
||||
const { applicationId, file, type: mediaType, description, extraFileId } = params;
|
||||
const { applicationId, file, type: mediaType, description, extraFileId, } = params;
|
||||
(0, assert_1.assert)(applicationId);
|
||||
const isPermanent = params.isPermanent === 'true';
|
||||
const filename = file.originalFilename;
|
||||
|
|
@ -104,14 +224,14 @@ context) {
|
|||
if (isPermanent) {
|
||||
// 只有公众号才能上传永久素材
|
||||
(0, assert_1.assert)(type === 'wechatPublic');
|
||||
const result = await wechatInstance.createMaterial({
|
||||
const result = (await wechatInstance.createMaterial({
|
||||
type: mediaType,
|
||||
media: fileStream,
|
||||
filename,
|
||||
filetype,
|
||||
fileLength,
|
||||
description: description ? JSON.parse(description) : null,
|
||||
});
|
||||
}));
|
||||
mediaId = result.media_id;
|
||||
}
|
||||
else {
|
||||
|
|
|
|||
|
|
@ -1072,139 +1072,118 @@ async function sendCaptcha({ mobile, env, type: type2, }, context) {
|
|||
const closeRootMode = context.openRootMode();
|
||||
if (process.env.NODE_ENV !== 'development' && !mockSend) {
|
||||
const [count1, count2] = await Promise.all([
|
||||
context.count(
|
||||
'captcha',
|
||||
{
|
||||
filter: {
|
||||
visitorId,
|
||||
$$createAt$$: {
|
||||
$gt: now - 3600 * 1000,
|
||||
},
|
||||
type: type2,
|
||||
context.count('captcha', {
|
||||
filter: {
|
||||
visitorId,
|
||||
$$createAt$$: {
|
||||
$gt: now - 3600 * 1000,
|
||||
},
|
||||
type: type2,
|
||||
},
|
||||
{
|
||||
dontCollect: true,
|
||||
}
|
||||
),
|
||||
context.count(
|
||||
'captcha',
|
||||
{
|
||||
filter: {
|
||||
mobile,
|
||||
$$createAt$$: {
|
||||
$gt: now - 3600 * 1000,
|
||||
},
|
||||
type: type2,
|
||||
}, {
|
||||
dontCollect: true,
|
||||
}),
|
||||
context.count('captcha', {
|
||||
filter: {
|
||||
mobile,
|
||||
$$createAt$$: {
|
||||
$gt: now - 3600 * 1000,
|
||||
},
|
||||
type: type2,
|
||||
},
|
||||
{
|
||||
dontCollect: true,
|
||||
}
|
||||
),
|
||||
}, {
|
||||
dontCollect: true,
|
||||
}),
|
||||
]);
|
||||
if (count1 > 5 || count2 > 5) {
|
||||
closeRootMode();
|
||||
throw new types_1.OakUserException(
|
||||
'您已发送很多次短信,请休息会再发吧'
|
||||
);
|
||||
throw new types_1.OakUserException('您已发送很多次短信,请休息会再发吧');
|
||||
}
|
||||
}
|
||||
const [captcha] = await context.select(
|
||||
'captcha',
|
||||
{
|
||||
data: {
|
||||
id: 1,
|
||||
code: 1,
|
||||
$$createAt$$: 1,
|
||||
},
|
||||
filter: {
|
||||
mobile,
|
||||
$$createAt$$: {
|
||||
$gt: now - duration * 60 * 1000,
|
||||
},
|
||||
expired: false,
|
||||
type: type2,
|
||||
},
|
||||
const [captcha] = await context.select('captcha', {
|
||||
data: {
|
||||
id: 1,
|
||||
code: 1,
|
||||
$$createAt$$: 1,
|
||||
},
|
||||
{
|
||||
dontCollect: true,
|
||||
}
|
||||
);
|
||||
filter: {
|
||||
mobile,
|
||||
$$createAt$$: {
|
||||
$gt: now - duration * 60 * 1000,
|
||||
},
|
||||
expired: false,
|
||||
type: type2,
|
||||
},
|
||||
}, {
|
||||
dontCollect: true,
|
||||
});
|
||||
if (captcha) {
|
||||
const code = captcha.code;
|
||||
if (process.env.NODE_ENV === 'development' || mockSend) {
|
||||
closeRootMode();
|
||||
return `验证码[${code}]已创建`;
|
||||
} else if (captcha.$$createAt$$ - now < 60000) {
|
||||
}
|
||||
else if (captcha.$$createAt$$ - now < 60000) {
|
||||
closeRootMode();
|
||||
throw new types_1.OakUserException(
|
||||
'您的操作太迅捷啦,请稍等再点吧'
|
||||
);
|
||||
} else {
|
||||
throw new types_1.OakUserException('您的操作太迅捷啦,请稍等再点吧');
|
||||
}
|
||||
else {
|
||||
(0, assert_1.assert)(origin, '必须设置短信渠道');
|
||||
// todo 再次发送
|
||||
const result = await (0, sms_1.sendSms)(
|
||||
{
|
||||
origin: origin,
|
||||
templateName: codeTemplateName,
|
||||
mobile,
|
||||
templateParam: { code, duration: duration.toString() },
|
||||
},
|
||||
context
|
||||
);
|
||||
const result = await (0, sms_1.sendSms)({
|
||||
origin: origin,
|
||||
templateName: codeTemplateName,
|
||||
mobile,
|
||||
templateParam: { code, duration: duration.toString() },
|
||||
}, context);
|
||||
closeRootMode();
|
||||
if (result.success) {
|
||||
return '验证码已发送';
|
||||
}
|
||||
return '验证码发送失败';
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
let code;
|
||||
if (process.env.NODE_ENV === 'development' || mockSend) {
|
||||
code = mobile.substring(7);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
code = Math.floor(Math.random() * 10000).toString();
|
||||
while (code.length < 4) {
|
||||
code += '0';
|
||||
}
|
||||
}
|
||||
const id = await (0, uuid_1.generateNewIdAsync)();
|
||||
await context.operate(
|
||||
'captcha',
|
||||
{
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'create',
|
||||
data: {
|
||||
id,
|
||||
mobile,
|
||||
code,
|
||||
visitorId,
|
||||
env,
|
||||
expired: false,
|
||||
expiresAt: now + duration * 60 * 1000,
|
||||
type: type2,
|
||||
},
|
||||
await context.operate('captcha', {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'create',
|
||||
data: {
|
||||
id,
|
||||
mobile,
|
||||
code,
|
||||
visitorId,
|
||||
env,
|
||||
expired: false,
|
||||
expiresAt: now + duration * 60 * 1000,
|
||||
type: type2,
|
||||
},
|
||||
{
|
||||
dontCollect: true,
|
||||
}
|
||||
);
|
||||
}, {
|
||||
dontCollect: true,
|
||||
});
|
||||
if (process.env.NODE_ENV === 'development' || mockSend) {
|
||||
closeRootMode();
|
||||
return `验证码[${code}]已创建`;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
(0, assert_1.assert)(origin, '必须设置短信渠道');
|
||||
//发送短信
|
||||
const result = await (0, sms_1.sendSms)(
|
||||
{
|
||||
origin: origin,
|
||||
templateName: codeTemplateName,
|
||||
mobile,
|
||||
templateParam: { code, duration: duration.toString() },
|
||||
},
|
||||
context
|
||||
);
|
||||
const result = await (0, sms_1.sendSms)({
|
||||
origin: origin,
|
||||
templateName: codeTemplateName,
|
||||
mobile,
|
||||
templateParam: { code, duration: duration.toString() },
|
||||
}, context);
|
||||
closeRootMode();
|
||||
if (result.success) {
|
||||
return '验证码已发送';
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { String, Text } from 'oak-domain/lib/types/DataType';
|
|||
import { EntityShape } from 'oak-domain/lib/types/Entity';
|
||||
import { Schema as System } from './System';
|
||||
import { Schema as Session } from './Session';
|
||||
import { Schema as Domain } from './Domain';
|
||||
import { Style } from '../types/Style';
|
||||
export type Passport = 'email' | 'mobile' | 'wechat' | 'wechatPublic';
|
||||
export type AppType = 'web' | 'wechatMp' | 'wechatPublic' | 'native';
|
||||
|
|
@ -63,4 +64,5 @@ export interface Schema extends EntityShape {
|
|||
config: WebConfig | WechatMpConfig | WechatPublicConfig | NativeConfig;
|
||||
style?: Style;
|
||||
sessions?: Session[];
|
||||
domain?: Domain;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ const entityDesc = {
|
|||
config: '设置',
|
||||
style: '样式',
|
||||
sessions: '会话',
|
||||
domain: '域名',
|
||||
},
|
||||
v: {
|
||||
type: {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,6 @@ export default class Theme<ED extends EntityDict, Cxt extends BackendRuntimeCont
|
|||
switchThemeMode(finalThemeMode: ETheme): void;
|
||||
openSystemTheme(): void;
|
||||
getColor(): string;
|
||||
switchColor(color: string): void;
|
||||
insertThemeStylesheet(theme: string, color: string, mode: ETheme): void;
|
||||
switchColor(color: string, theme?: string): void;
|
||||
insertThemeStylesheet(color: string, mode: ETheme, theme?: string): void;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,11 +8,7 @@ const initialThemeState = {
|
|||
setting: false,
|
||||
themeMode: defaultTheme,
|
||||
systemTheme: false,
|
||||
isFullPage: false,
|
||||
color: '#0052d9',
|
||||
showHeader: true,
|
||||
showBreadcrumbs: true,
|
||||
showFooter: true,
|
||||
};
|
||||
class Theme extends Feature_1.Feature {
|
||||
cache;
|
||||
|
|
@ -84,15 +80,17 @@ class Theme extends Feature_1.Feature {
|
|||
const state = this.themeState;
|
||||
return state.color;
|
||||
}
|
||||
switchColor(color) {
|
||||
switchColor(color, theme) {
|
||||
const state = this.themeState;
|
||||
if (color) {
|
||||
state.color = color; // 某主题 主题色
|
||||
const themeColor = 'blue'; // 主题颜色类型
|
||||
// const themeColor = 'blue'; // 主题颜色类型
|
||||
switch (process.env.OAK_PLATFORM) {
|
||||
case 'web': {
|
||||
this.insertThemeStylesheet(themeColor, color, state.themeMode);
|
||||
document.documentElement.setAttribute('theme-color', themeColor);
|
||||
this.insertThemeStylesheet(color, state.themeMode, theme);
|
||||
if (theme) {
|
||||
document.documentElement.setAttribute('theme-color', theme);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
|
@ -102,11 +100,13 @@ class Theme extends Feature_1.Feature {
|
|||
this.set(state);
|
||||
}
|
||||
}
|
||||
insertThemeStylesheet(theme, color, mode) {
|
||||
insertThemeStylesheet(color, mode, theme) {
|
||||
const isDarkMode = mode === 'dark';
|
||||
const root = !isDarkMode
|
||||
? `:root[theme-color='${theme}']`
|
||||
: `:root[theme-color='${theme}'][theme-mode='dark']`;
|
||||
? (theme
|
||||
? `:root[theme-color='${theme}']`
|
||||
: `:root`)
|
||||
: (theme ? `:root[theme-color='${theme}'][theme-mode='dark']` : `:root[theme-mode='dark']`);
|
||||
const styleSheet = document.createElement('style');
|
||||
styleSheet.type = 'text/css';
|
||||
styleSheet.innerText = `${root}{
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { GenericAction } from "oak-domain/lib/actions/action";
|
|||
import { String, Text } from "oak-domain/lib/types/DataType";
|
||||
import { Style } from "../../types/Style";
|
||||
import * as System from "../System/Schema";
|
||||
import * as Domain from "../Domain/Schema";
|
||||
import * as ExtraFile from "../ExtraFile/Schema";
|
||||
import * as Notification from "../Notification/Schema";
|
||||
import * as SessionMessage from "../SessionMessage/Schema";
|
||||
|
|
@ -76,6 +77,7 @@ export type OpSchema = EntityShape & {
|
|||
systemId: ForeignKey<"system">;
|
||||
config: WebConfig | WechatMpConfig | WechatPublicConfig | NativeConfig;
|
||||
style?: Style | null;
|
||||
domainId?: ForeignKey<"domain"> | null;
|
||||
};
|
||||
export type OpAttr = keyof OpSchema;
|
||||
export type Schema = EntityShape & {
|
||||
|
|
@ -85,7 +87,9 @@ export type Schema = EntityShape & {
|
|||
systemId: ForeignKey<"system">;
|
||||
config: WebConfig | WechatMpConfig | WechatPublicConfig | NativeConfig;
|
||||
style?: Style | null;
|
||||
domainId?: ForeignKey<"domain"> | null;
|
||||
system: System.Schema;
|
||||
domain?: Domain.Schema | null;
|
||||
extraFile$application?: Array<ExtraFile.Schema>;
|
||||
extraFile$application$$aggr?: AggregationResult<ExtraFile.Schema>;
|
||||
notification$application?: Array<Notification.Schema>;
|
||||
|
|
@ -123,6 +127,8 @@ type AttrFilter = {
|
|||
system: System.Filter;
|
||||
config: JsonFilter<WebConfig | WechatMpConfig | WechatPublicConfig | NativeConfig>;
|
||||
style: JsonFilter<Style>;
|
||||
domainId: Q_StringValue;
|
||||
domain: Domain.Filter;
|
||||
extraFile$application: ExtraFile.Filter & SubQueryPredicateMetadata;
|
||||
notification$application: Notification.Filter & SubQueryPredicateMetadata;
|
||||
sessionMessage$application: SessionMessage.Filter & SubQueryPredicateMetadata;
|
||||
|
|
@ -150,6 +156,8 @@ export type Projection = {
|
|||
system?: System.Projection;
|
||||
config?: number | JsonProjection<WebConfig | WechatMpConfig | WechatPublicConfig | NativeConfig>;
|
||||
style?: number | JsonProjection<Style>;
|
||||
domainId?: number;
|
||||
domain?: Domain.Projection;
|
||||
extraFile$application?: ExtraFile.Selection & {
|
||||
$entity: "extraFile";
|
||||
};
|
||||
|
|
@ -223,6 +231,9 @@ type ApplicationIdProjection = OneOf<{
|
|||
type SystemIdProjection = OneOf<{
|
||||
systemId: number;
|
||||
}>;
|
||||
type DomainIdProjection = OneOf<{
|
||||
domainId: number;
|
||||
}>;
|
||||
export type SortAttr = {
|
||||
id: number;
|
||||
} | {
|
||||
|
|
@ -243,6 +254,10 @@ export type SortAttr = {
|
|||
system: System.SortAttr;
|
||||
} | {
|
||||
style: number;
|
||||
} | {
|
||||
domainId: number;
|
||||
} | {
|
||||
domain: Domain.SortAttr;
|
||||
} | {
|
||||
[k: string]: any;
|
||||
} | OneOf<ExprOp<OpAttr | string>>;
|
||||
|
|
@ -254,7 +269,7 @@ export type Sorter = SortNode[];
|
|||
export type SelectOperation<P extends Object = Projection> = OakSelection<"select", P, Filter, Sorter>;
|
||||
export type Selection<P extends Object = Projection> = SelectOperation<P>;
|
||||
export type Aggregation = DeduceAggregation<Projection, Filter, Sorter>;
|
||||
export type CreateOperationData = FormCreateData<Omit<OpSchema, "systemId">> & (({
|
||||
export type CreateOperationData = FormCreateData<Omit<OpSchema, "systemId" | "domainId">> & (({
|
||||
systemId?: never;
|
||||
system: System.CreateSingleOperation;
|
||||
} | {
|
||||
|
|
@ -263,6 +278,15 @@ export type CreateOperationData = FormCreateData<Omit<OpSchema, "systemId">> & (
|
|||
} | {
|
||||
system?: never;
|
||||
systemId: ForeignKey<"system">;
|
||||
}) & ({
|
||||
domainId?: never;
|
||||
domain?: Domain.CreateSingleOperation;
|
||||
} | {
|
||||
domainId: ForeignKey<"domain">;
|
||||
domain?: Domain.UpdateOperation;
|
||||
} | {
|
||||
domain?: never;
|
||||
domainId?: ForeignKey<"domain">;
|
||||
})) & {
|
||||
extraFile$application?: OakOperation<ExtraFile.UpdateOperation["action"], Omit<ExtraFile.UpdateOperationData, "application" | "applicationId">, Omit<ExtraFile.Filter, "application" | "applicationId">> | OakOperation<"create", Omit<ExtraFile.CreateOperationData, "application" | "applicationId">[]> | Array<OakOperation<"create", Omit<ExtraFile.CreateOperationData, "application" | "applicationId">> | OakOperation<ExtraFile.UpdateOperation["action"], Omit<ExtraFile.UpdateOperationData, "application" | "applicationId">, Omit<ExtraFile.Filter, "application" | "applicationId">>>;
|
||||
notification$application?: OakOperation<Notification.UpdateOperation["action"], Omit<Notification.UpdateOperationData, "application" | "applicationId">, Omit<Notification.Filter, "application" | "applicationId">> | OakOperation<"create", Omit<Notification.CreateOperationData, "application" | "applicationId">[]> | Array<OakOperation<"create", Omit<Notification.CreateOperationData, "application" | "applicationId">> | OakOperation<Notification.UpdateOperation["action"], Omit<Notification.UpdateOperationData, "application" | "applicationId">, Omit<Notification.Filter, "application" | "applicationId">>>;
|
||||
|
|
@ -279,7 +303,7 @@ export type CreateOperationData = FormCreateData<Omit<OpSchema, "systemId">> & (
|
|||
export type CreateSingleOperation = OakOperation<"create", CreateOperationData>;
|
||||
export type CreateMultipleOperation = OakOperation<"create", Array<CreateOperationData>>;
|
||||
export type CreateOperation = CreateSingleOperation | CreateMultipleOperation;
|
||||
export type UpdateOperationData = FormUpdateData<Omit<OpSchema, "systemId">> & (({
|
||||
export type UpdateOperationData = FormUpdateData<Omit<OpSchema, "systemId" | "domainId">> & (({
|
||||
system?: System.CreateSingleOperation;
|
||||
systemId?: never;
|
||||
} | {
|
||||
|
|
@ -291,6 +315,18 @@ export type UpdateOperationData = FormUpdateData<Omit<OpSchema, "systemId">> & (
|
|||
} | {
|
||||
system?: never;
|
||||
systemId?: ForeignKey<"system">;
|
||||
}) & ({
|
||||
domain?: Domain.CreateSingleOperation;
|
||||
domainId?: never;
|
||||
} | {
|
||||
domain?: Domain.UpdateOperation;
|
||||
domainId?: never;
|
||||
} | {
|
||||
domain?: Domain.RemoveOperation;
|
||||
domainId?: never;
|
||||
} | {
|
||||
domain?: never;
|
||||
domainId?: ForeignKey<"domain"> | null;
|
||||
})) & {
|
||||
[k: string]: any;
|
||||
extraFile$application?: OakOperation<ExtraFile.UpdateOperation["action"], Omit<ExtraFile.UpdateOperationData, "application" | "applicationId">, Omit<ExtraFile.Filter, "application" | "applicationId">> | OakOperation<ExtraFile.RemoveOperation["action"], Omit<ExtraFile.RemoveOperationData, "application" | "applicationId">, Omit<ExtraFile.Filter, "application" | "applicationId">> | OakOperation<"create", Omit<ExtraFile.CreateOperationData, "application" | "applicationId">[]> | Array<OakOperation<"create", Omit<ExtraFile.CreateOperationData, "application" | "applicationId">> | OakOperation<ExtraFile.UpdateOperation["action"], Omit<ExtraFile.UpdateOperationData, "application" | "applicationId">, Omit<ExtraFile.Filter, "application" | "applicationId">> | OakOperation<ExtraFile.RemoveOperation["action"], Omit<ExtraFile.RemoveOperationData, "application" | "applicationId">, Omit<ExtraFile.Filter, "application" | "applicationId">>>;
|
||||
|
|
@ -308,10 +344,13 @@ export type UpdateOperationData = FormUpdateData<Omit<OpSchema, "systemId">> & (
|
|||
export type UpdateOperation = OakOperation<"update" | string, UpdateOperationData, Filter, Sorter>;
|
||||
export type RemoveOperationData = {} & (({
|
||||
system?: System.UpdateOperation | System.RemoveOperation;
|
||||
}) & ({
|
||||
domain?: Domain.UpdateOperation | Domain.RemoveOperation;
|
||||
}));
|
||||
export type RemoveOperation = OakOperation<"remove", RemoveOperationData, Filter, Sorter>;
|
||||
export type Operation = CreateOperation | UpdateOperation | RemoveOperation;
|
||||
export type SystemIdSubQuery = Selection<SystemIdProjection>;
|
||||
export type DomainIdSubQuery = Selection<DomainIdProjection>;
|
||||
export type ApplicationIdSubQuery = Selection<ApplicationIdProjection>;
|
||||
export type EntityDef = {
|
||||
Schema: Schema;
|
||||
|
|
|
|||
|
|
@ -31,6 +31,10 @@ exports.desc = {
|
|||
},
|
||||
style: {
|
||||
type: "object"
|
||||
},
|
||||
domainId: {
|
||||
type: "ref",
|
||||
ref: "domain"
|
||||
}
|
||||
},
|
||||
actionType: "crud",
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{ "name": "应用", "attr": { "description": "描述", "type": "类型", "system": "系统", "name": "名称", "config": "设置", "style": "样式", "sessions": "会话" }, "v": { "type": { "web": "网站", "wechatPublic": "微信公众号", "wechatMp": "微信小程序", "native": "App" } } }
|
||||
{ "name": "应用", "attr": { "description": "描述", "type": "类型", "system": "系统", "name": "名称", "config": "设置", "style": "样式", "sessions": "会话", "domain": "域名" }, "v": { "type": { "web": "网站", "wechatPublic": "微信公众号", "wechatMp": "微信小程序", "native": "App" } } }
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import { ForeignKey } from "oak-domain/lib/types/DataType";
|
||||
import { Q_DateValue, Q_NumberValue, Q_StringValue, Q_EnumValue, NodeId, MakeFilter, ExprOp, ExpressionKey } from "oak-domain/lib/types/Demand";
|
||||
import { Q_DateValue, Q_NumberValue, Q_StringValue, Q_EnumValue, NodeId, MakeFilter, ExprOp, ExpressionKey, SubQueryPredicateMetadata } from "oak-domain/lib/types/Demand";
|
||||
import { OneOf } from "oak-domain/lib/types/Polyfill";
|
||||
import { FormCreateData, FormUpdateData, DeduceAggregation, Operation as OakOperation, Selection as OakSelection, MakeAction as OakMakeAction, EntityShape } from "oak-domain/lib/types/Entity";
|
||||
import { FormCreateData, FormUpdateData, DeduceAggregation, Operation as OakOperation, Selection as OakSelection, MakeAction as OakMakeAction, AggregationResult, EntityShape } from "oak-domain/lib/types/Entity";
|
||||
import { GenericAction } from "oak-domain/lib/actions/action";
|
||||
import { String, Int } from "oak-domain/lib/types/DataType";
|
||||
import * as System from "../System/Schema";
|
||||
import * as Application from "../Application/Schema";
|
||||
export type OpSchema = EntityShape & {
|
||||
url: String<64>;
|
||||
apiPath?: String<32> | null;
|
||||
|
|
@ -20,6 +21,8 @@ export type Schema = EntityShape & {
|
|||
port: Int<2>;
|
||||
systemId: ForeignKey<"system">;
|
||||
system: System.Schema;
|
||||
application$domain?: Array<Application.Schema>;
|
||||
application$domain$$aggr?: AggregationResult<Application.Schema>;
|
||||
} & {
|
||||
[A in ExpressionKey]?: any;
|
||||
};
|
||||
|
|
@ -34,6 +37,7 @@ type AttrFilter = {
|
|||
port: Q_NumberValue;
|
||||
systemId: Q_StringValue;
|
||||
system: System.Filter;
|
||||
application$domain: Application.Filter & SubQueryPredicateMetadata;
|
||||
};
|
||||
export type Filter = MakeFilter<AttrFilter & ExprOp<OpAttr | string>>;
|
||||
export type Projection = {
|
||||
|
|
@ -49,6 +53,12 @@ export type Projection = {
|
|||
port?: number;
|
||||
systemId?: number;
|
||||
system?: System.Projection;
|
||||
application$domain?: Application.Selection & {
|
||||
$entity: "application";
|
||||
};
|
||||
application$domain$$aggr?: Application.Aggregation & {
|
||||
$entity: "application";
|
||||
};
|
||||
} & Partial<ExprOp<OpAttr | string>>;
|
||||
type DomainIdProjection = OneOf<{
|
||||
id: number;
|
||||
|
|
@ -96,7 +106,9 @@ export type CreateOperationData = FormCreateData<Omit<OpSchema, "systemId">> & (
|
|||
} | {
|
||||
system?: never;
|
||||
systemId: ForeignKey<"system">;
|
||||
}));
|
||||
})) & {
|
||||
application$domain?: OakOperation<Application.UpdateOperation["action"], Omit<Application.UpdateOperationData, "domain" | "domainId">, Omit<Application.Filter, "domain" | "domainId">> | OakOperation<"create", Omit<Application.CreateOperationData, "domain" | "domainId">[]> | Array<OakOperation<"create", Omit<Application.CreateOperationData, "domain" | "domainId">> | OakOperation<Application.UpdateOperation["action"], Omit<Application.UpdateOperationData, "domain" | "domainId">, Omit<Application.Filter, "domain" | "domainId">>>;
|
||||
};
|
||||
export type CreateSingleOperation = OakOperation<"create", CreateOperationData>;
|
||||
export type CreateMultipleOperation = OakOperation<"create", Array<CreateOperationData>>;
|
||||
export type CreateOperation = CreateSingleOperation | CreateMultipleOperation;
|
||||
|
|
@ -114,6 +126,7 @@ export type UpdateOperationData = FormUpdateData<Omit<OpSchema, "systemId">> & (
|
|||
systemId?: ForeignKey<"system">;
|
||||
})) & {
|
||||
[k: string]: any;
|
||||
application$domain?: OakOperation<Application.UpdateOperation["action"], Omit<Application.UpdateOperationData, "domain" | "domainId">, Omit<Application.Filter, "domain" | "domainId">> | OakOperation<Application.RemoveOperation["action"], Omit<Application.RemoveOperationData, "domain" | "domainId">, Omit<Application.Filter, "domain" | "domainId">> | OakOperation<"create", Omit<Application.CreateOperationData, "domain" | "domainId">[]> | Array<OakOperation<"create", Omit<Application.CreateOperationData, "domain" | "domainId">> | OakOperation<Application.UpdateOperation["action"], Omit<Application.UpdateOperationData, "domain" | "domainId">, Omit<Application.Filter, "domain" | "domainId">> | OakOperation<Application.RemoveOperation["action"], Omit<Application.RemoveOperationData, "domain" | "domainId">, Omit<Application.Filter, "domain" | "domainId">>>;
|
||||
};
|
||||
export type UpdateOperation = OakOperation<"update" | string, UpdateOperationData, Filter, Sorter>;
|
||||
export type RemoveOperationData = {} & (({
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ exports.desc = {
|
|||
type: "object"
|
||||
}
|
||||
},
|
||||
static: true,
|
||||
actionType: "crud",
|
||||
actions: action_1.genericActions,
|
||||
indexes: [
|
||||
|
|
|
|||
|
|
@ -273,7 +273,9 @@ export type ChangePasswordTempIdSubQuery = {
|
|||
}) | any;
|
||||
};
|
||||
export type DomainIdSubQuery = {
|
||||
[K in "$in" | "$nin"]?: (Domain.DomainIdSubQuery & {
|
||||
[K in "$in" | "$nin"]?: (Application.DomainIdSubQuery & {
|
||||
entity: "application";
|
||||
}) | (Domain.DomainIdSubQuery & {
|
||||
entity: "domain";
|
||||
}) | any;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -105,9 +105,9 @@ export type Config = {
|
|||
};
|
||||
Sms?: {
|
||||
mockSend?: boolean;
|
||||
defaultOrigin?: 'ali' | 'tencent' | 'ctyun'; //默认渠道
|
||||
defaultCodeTemplateName?: string; //验证码模版名
|
||||
defaultCodeDuration?: number; //验证码有效时间 单位分钟, 不填1分钟
|
||||
defaultOrigin?: 'ali' | 'tencent' | 'ctyun';
|
||||
defaultCodeTemplateName?: string;
|
||||
defaultCodeDuration?: number;
|
||||
ali?: AliSmsConfig[];
|
||||
tencent?: TencentSmsConfig[];
|
||||
ctyun?: CTYunSmsConfig[];
|
||||
|
|
@ -118,6 +118,8 @@ export type Config = {
|
|||
qrCodePublicForMpId?: string;
|
||||
mpShareImageUrl?: string;
|
||||
mergeUserDirectly?: boolean;
|
||||
tokenRefreshTime?: number;
|
||||
tokenExpireTime?: number;
|
||||
};
|
||||
};
|
||||
export type Origin = 'ali' | 'tencent' | 'qiniu' | 'amap' | 'ctyun';
|
||||
|
|
|
|||
|
|
@ -132,8 +132,17 @@ exports.applicationProjection = {
|
|||
apiPath: 1,
|
||||
protocol: 1,
|
||||
port: 1,
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
domainId: 1,
|
||||
domain: {
|
||||
id: 1,
|
||||
systemId: 1,
|
||||
url: 1,
|
||||
apiPath: 1,
|
||||
protocol: 1,
|
||||
port: 1,
|
||||
},
|
||||
};
|
||||
exports.extraFileProjection = {
|
||||
|
|
|
|||
|
|
@ -13,8 +13,4 @@ export interface IThemeState {
|
|||
* 是否开启跟随系统主题
|
||||
*/
|
||||
systemTheme: boolean;
|
||||
isFullPage: boolean;
|
||||
showHeader: boolean;
|
||||
showBreadcrumbs: boolean;
|
||||
showFooter: boolean;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,13 +11,14 @@ import { MediaType, MaterialType } from '../types/WeChat';
|
|||
import { WebEnv } from 'oak-domain/lib/types/Environment';
|
||||
import WechatSDK, {
|
||||
WechatPublicInstance,
|
||||
WechatMpInstance
|
||||
WechatMpInstance,
|
||||
} from 'oak-external-sdk/lib/WechatSDK';
|
||||
import fs from 'fs';
|
||||
import { File } from 'formidable';
|
||||
import { cloneDeep } from 'oak-domain/lib/utils/lodash';
|
||||
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
|
||||
|
||||
|
||||
export async function getApplication<
|
||||
ED extends EntityDict,
|
||||
Cxt extends BackendRuntimeContext<ED>
|
||||
|
|
@ -32,7 +33,64 @@ export async function getApplication<
|
|||
const url = context.getHeader('host');
|
||||
console.log('url is', url);
|
||||
|
||||
const [application] = await context.select(
|
||||
// const [application] = await context.select(
|
||||
// 'application',
|
||||
// {
|
||||
// data: cloneDeep(applicationProjection),
|
||||
// filter: {
|
||||
// type,
|
||||
// system: {
|
||||
// domain$system: {
|
||||
// url: domain,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// {}
|
||||
// );
|
||||
// //微信小程序环境下 没有就报错
|
||||
// if (type === 'wechatMp') {
|
||||
// assert(
|
||||
// application,
|
||||
// '微信小程序环境下 application必须存在小程序相关配置'
|
||||
// );
|
||||
// } else if (type === 'native') {
|
||||
// assert(application, 'APP环境下 application必须存在APP相关配置');
|
||||
// } else {
|
||||
// //web 或 wechatPublic
|
||||
// if (type === 'wechatPublic') {
|
||||
// // 如果微信公众号环境下 application不存在公众号配置,但又在公众号访问,这时可以使用web的application
|
||||
// if (!application) {
|
||||
// const [application2] = await context.select(
|
||||
// 'application',
|
||||
// {
|
||||
// data: cloneDeep(applicationProjection),
|
||||
// filter: {
|
||||
// type: 'web',
|
||||
// system: {
|
||||
// domain$system: {
|
||||
// url: domain,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// {}
|
||||
// );
|
||||
// assert(
|
||||
// application2,
|
||||
// '微信公众号环境下 application不存在公众号配置,但必须存在web相关配置'
|
||||
// );
|
||||
// return application2.id as string;
|
||||
// }
|
||||
// } else {
|
||||
// assert(application, 'web环境下 application必须存在web相关配置');
|
||||
// }
|
||||
// }
|
||||
// return application.id as string;
|
||||
|
||||
// 先找指定domain的应用,如果不存在再找系统下面的domain, 但无论怎么样都必须一项
|
||||
//
|
||||
let applications = await context.select(
|
||||
'application',
|
||||
{
|
||||
data: cloneDeep(applicationProjection),
|
||||
|
|
@ -43,26 +101,56 @@ export async function getApplication<
|
|||
url: domain,
|
||||
},
|
||||
},
|
||||
domain: {
|
||||
url: domain,
|
||||
},
|
||||
},
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
//微信小程序环境下 没有就报错
|
||||
if (type === 'wechatMp') {
|
||||
assert(
|
||||
application,
|
||||
'微信小程序环境下 application必须存在小程序相关配置'
|
||||
assert(applications.length <= 1, `指定域名的应用 只能存在一项或未指定`);
|
||||
if (applications.length === 0) {
|
||||
applications = await context.select(
|
||||
'application',
|
||||
{
|
||||
data: cloneDeep(applicationProjection),
|
||||
filter: {
|
||||
type,
|
||||
system: {
|
||||
domain$system: {
|
||||
url: domain,
|
||||
},
|
||||
},
|
||||
domainId: {
|
||||
$exists: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{}
|
||||
);
|
||||
}
|
||||
else if (type === 'native') {
|
||||
assert(application, 'APP环境下 application必须存在APP相关配置');
|
||||
} else {
|
||||
//web 或 wechatPublic
|
||||
if (type === 'wechatPublic') {
|
||||
// 如果微信公众号环境下 application不存在公众号配置,但又在公众号访问,这时可以使用web的application
|
||||
if (!application) {
|
||||
const [application2] = await context.select(
|
||||
|
||||
switch (type) {
|
||||
case 'wechatMp': {
|
||||
assert(
|
||||
applications.length === 1,
|
||||
`微信小程序环境下,同一个系统必须存在唯一的【${type}】应用`
|
||||
);
|
||||
const application = applications[0];
|
||||
return application.id as string;
|
||||
}
|
||||
case 'native': {
|
||||
assert(
|
||||
applications.length === 1,
|
||||
`APP环境下,同一个系统必须存在唯一的【${type}】应用`
|
||||
);
|
||||
const application = applications[0];
|
||||
return application.id as string;
|
||||
}
|
||||
case 'wechatPublic': {
|
||||
// 微信公众号环境下,未配置公众号,可以使用web的application
|
||||
if (applications.length === 0) {
|
||||
let applications2 = await context.select(
|
||||
'application',
|
||||
{
|
||||
data: cloneDeep(applicationProjection),
|
||||
|
|
@ -73,24 +161,66 @@ export async function getApplication<
|
|||
url: domain,
|
||||
},
|
||||
},
|
||||
domain: {
|
||||
url: domain,
|
||||
},
|
||||
},
|
||||
},
|
||||
{}
|
||||
);
|
||||
assert(
|
||||
applications2.length <= 1,
|
||||
`指定域名的应用 只能存在一项或未指定`
|
||||
);
|
||||
if (applications2.length === 0) {
|
||||
applications2 = await context.select(
|
||||
'application',
|
||||
{
|
||||
data: cloneDeep(applicationProjection),
|
||||
filter: {
|
||||
type,
|
||||
system: {
|
||||
domain$system: {
|
||||
url: domain,
|
||||
},
|
||||
},
|
||||
domainId: {
|
||||
$exists: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
assert(
|
||||
application2,
|
||||
'微信公众号环境下 application不存在公众号配置,但必须存在web相关配置'
|
||||
applications2.length === 1,
|
||||
'微信公众号环境下, 可以未配置公众号,但必须存在web的application'
|
||||
);
|
||||
|
||||
return application2.id as string;
|
||||
const application = applications2[0];
|
||||
return application.id as string;
|
||||
}
|
||||
} else {
|
||||
assert(application, 'web环境下 application必须存在web相关配置');
|
||||
assert(
|
||||
applications.length === 1,
|
||||
`微信公众号环境下,同一个系统必须存在唯一的【${type}】应用 或 多个${type}应用必须配置域名`
|
||||
);
|
||||
const application = applications[0];
|
||||
return application.id as string;
|
||||
}
|
||||
case 'web': {
|
||||
assert(
|
||||
applications.length === 1,
|
||||
`web环境下,同一个系统必须存在唯一的【${type}】应用 或 多个${type}应用必须配置域名`
|
||||
);
|
||||
|
||||
const application = applications[0];
|
||||
return application.id as string;
|
||||
}
|
||||
default: {
|
||||
assert(false, `不支持的类型【${type}】`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return application.id as string;
|
||||
}
|
||||
|
||||
export async function signatureJsSDK<
|
||||
|
|
@ -127,7 +257,13 @@ export async function uploadWechatMedia<
|
|||
}, // FormData表单提交 isPermanent 变成 'true' | 'false'
|
||||
context: Cxt
|
||||
): Promise<{ mediaId: string }> {
|
||||
const { applicationId, file, type: mediaType, description, extraFileId } = params;
|
||||
const {
|
||||
applicationId,
|
||||
file,
|
||||
type: mediaType,
|
||||
description,
|
||||
extraFileId,
|
||||
} = params;
|
||||
assert(applicationId);
|
||||
const isPermanent = params.isPermanent === 'true';
|
||||
const filename = file.originalFilename!;
|
||||
|
|
@ -175,7 +311,7 @@ export async function uploadWechatMedia<
|
|||
if (isPermanent) {
|
||||
// 只有公众号才能上传永久素材
|
||||
assert(type === 'wechatPublic');
|
||||
const result = await(
|
||||
const result = (await (
|
||||
wechatInstance as WechatPublicInstance
|
||||
).createMaterial({
|
||||
type: mediaType,
|
||||
|
|
@ -184,7 +320,7 @@ export async function uploadWechatMedia<
|
|||
filetype,
|
||||
fileLength,
|
||||
description: description ? JSON.parse(description) : null,
|
||||
}) as {
|
||||
})) as {
|
||||
media_id: string;
|
||||
url: string;
|
||||
};
|
||||
|
|
@ -231,7 +367,6 @@ export async function uploadWechatMedia<
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
mediaId,
|
||||
};
|
||||
|
|
@ -292,7 +427,7 @@ export async function getMaterial<
|
|||
if (isPermanent) {
|
||||
// 只有公众号才能获取永久素材
|
||||
assert(type === 'wechatPublic');
|
||||
result = await(wechatInstance as WechatPublicInstance).getMaterial({
|
||||
result = await (wechatInstance as WechatPublicInstance).getMaterial({
|
||||
mediaId,
|
||||
});
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { ReactNode, useState } from 'react';
|
||||
import { Tabs, Row, Descriptions, Typography, Button, Modal } from 'antd';
|
||||
import { Tabs, Row, Descriptions, Typography, Button, Modal, Space } from 'antd';
|
||||
|
||||
import {
|
||||
AppType,
|
||||
|
|
@ -37,22 +37,35 @@ export default function Render(
|
|||
<>
|
||||
<Modal
|
||||
open={open}
|
||||
width={800}
|
||||
width={500}
|
||||
onCancel={() => {
|
||||
clean();
|
||||
setOpen(false);
|
||||
}}
|
||||
footer={
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={async () => {
|
||||
await execute();
|
||||
setOpen(false);
|
||||
}}
|
||||
disabled={oakExecutable !== true || oakExecuting}
|
||||
>
|
||||
{t('common::action.confirm')}
|
||||
</Button>
|
||||
<Space>
|
||||
<Button
|
||||
onClick={async () => {
|
||||
clean();
|
||||
setOpen(false);
|
||||
}}
|
||||
disabled={oakExecuting}
|
||||
>
|
||||
{t('common::action.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={async () => {
|
||||
await execute();
|
||||
setOpen(false);
|
||||
}}
|
||||
disabled={
|
||||
oakExecutable !== true || oakExecuting
|
||||
}
|
||||
>
|
||||
{t('common::action.confirm')}
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<ApplicationUpsert oakPath={oakFullpath} oakId={id} />
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ export default OakComponent({
|
|||
config: 1,
|
||||
description: 1,
|
||||
type: 1,
|
||||
systemId: 1,
|
||||
domainId: 1,
|
||||
},
|
||||
formData({ data }) {
|
||||
return data || {};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { AppType } from '../../../oak-app-domain/Application/Schema';
|
||||
import { EntityDict } from '../../../oak-app-domain';
|
||||
|
||||
type typeOption = {
|
||||
value: AppType
|
||||
|
|
@ -7,6 +8,15 @@ type typeOption = {
|
|||
export default OakComponent({
|
||||
isList: false,
|
||||
entity: 'application',
|
||||
projection: {
|
||||
id: 1,
|
||||
name: 1,
|
||||
config: 1,
|
||||
description: 1,
|
||||
type: 1,
|
||||
systemId: 1,
|
||||
domainId: 1,
|
||||
},
|
||||
formData({ data }) {
|
||||
return data || {};
|
||||
},
|
||||
|
|
@ -22,18 +32,29 @@ export default OakComponent({
|
|||
value: 'wechatPublic',
|
||||
},
|
||||
] as typeOption[],
|
||||
domains: [] as Partial<EntityDict['domain']['Schema']>[],
|
||||
},
|
||||
/* lifetimes: {
|
||||
ready() {
|
||||
const { systemId, oakId } = this.props;
|
||||
|
||||
if (!oakId) {
|
||||
if (systemId) {
|
||||
this.update({
|
||||
methods: {
|
||||
async getDomains(systemId: string) {
|
||||
const { data: domains } = await this.features.cache.refresh(
|
||||
'domain',
|
||||
{
|
||||
data: {
|
||||
id: 1,
|
||||
systemId: 1,
|
||||
url: 1,
|
||||
apiPath: 1,
|
||||
port: 1,
|
||||
protocol: 1,
|
||||
},
|
||||
filter: {
|
||||
systemId,
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
);
|
||||
this.setState({
|
||||
domains,
|
||||
});
|
||||
},
|
||||
}, */
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -12,8 +12,6 @@ export default function Render(
|
|||
{
|
||||
name: string;
|
||||
description: string;
|
||||
variant: 'inline' | 'alone' | 'dialog';
|
||||
showBack: boolean;
|
||||
type: EntityDict['application']['Schema']['type'];
|
||||
typeArr: Array<{
|
||||
label: string;
|
||||
|
|
@ -23,35 +21,32 @@ export default function Render(
|
|||
oakId: string;
|
||||
style: EntityDict['system']['Schema']['style'];
|
||||
$$createAt$$: number;
|
||||
domainId: string;
|
||||
domains: EntityDict['domain']['Schema'][];
|
||||
},
|
||||
{
|
||||
confirm: () => void;
|
||||
getDomains: (systemId: string) => Promise<void>;
|
||||
}
|
||||
>
|
||||
) {
|
||||
const {
|
||||
systemId,
|
||||
name,
|
||||
description,
|
||||
type,
|
||||
typeArr,
|
||||
$$createAt$$,
|
||||
domainId,
|
||||
domains,
|
||||
} = props.data;
|
||||
const { t, update, navigateBack, confirm } = props.methods;
|
||||
const { t, update, confirm, getDomains } = props.methods;
|
||||
return (
|
||||
<Form
|
||||
colon={true}
|
||||
labelCol={{ span: 6 }}
|
||||
wrapperCol={{ span: 16 }}
|
||||
>
|
||||
<Form colon={true} labelCol={{ span: 6 }} wrapperCol={{ span: 16 }}>
|
||||
<Form.Item
|
||||
label="名称"
|
||||
required
|
||||
// name="name"
|
||||
// rules={[
|
||||
// {
|
||||
// required: true,
|
||||
// },
|
||||
// ]}
|
||||
// name="name"
|
||||
>
|
||||
<>
|
||||
<Input
|
||||
|
|
@ -66,7 +61,7 @@ export default function Render(
|
|||
</Form.Item>
|
||||
<Form.Item
|
||||
label="描述"
|
||||
//name="description"
|
||||
//name="description"
|
||||
>
|
||||
<>
|
||||
<Input.TextArea
|
||||
|
|
@ -82,26 +77,17 @@ export default function Render(
|
|||
<Form.Item
|
||||
label="应用类型"
|
||||
required
|
||||
// name="type"
|
||||
// rules={[
|
||||
// {
|
||||
// required: true,
|
||||
// },
|
||||
// ]}
|
||||
// name="type"
|
||||
>
|
||||
<>
|
||||
<Select
|
||||
value={type}
|
||||
style={{ width: 120 }}
|
||||
disabled={$$createAt$$ > 1}
|
||||
options={typeArr.map(
|
||||
(ele: { value: string }) => ({
|
||||
label: t(
|
||||
`application:v.type.${ele.value}`
|
||||
),
|
||||
value: ele.value,
|
||||
})
|
||||
)}
|
||||
options={typeArr.map((ele: { value: string }) => ({
|
||||
label: t(`application:v.type.${ele.value}`),
|
||||
value: ele.value,
|
||||
}))}
|
||||
onChange={(value) => {
|
||||
update({
|
||||
type: value,
|
||||
|
|
@ -110,6 +96,36 @@ export default function Render(
|
|||
/>
|
||||
</>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="域名"
|
||||
// name="domain"
|
||||
>
|
||||
<>
|
||||
<Select
|
||||
allowClear
|
||||
value={domainId}
|
||||
style={{ width: 120 }}
|
||||
options={domains?.map((ele) => ({
|
||||
label: ele.url,
|
||||
value: ele.id,
|
||||
}))}
|
||||
onChange={(value) => {
|
||||
if (!value) {
|
||||
update({
|
||||
domainId: undefined,
|
||||
});
|
||||
return;
|
||||
}
|
||||
update({
|
||||
domainId: value,
|
||||
});
|
||||
}}
|
||||
onClick={() => {
|
||||
getDomains(systemId!);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,11 @@ const Colors: ColorType[] = ['primary', 'success', 'error', 'warning', 'info'];
|
|||
function Color(props: { value: StyleType['color'], setValue: (path: string, value: string) => void }) {
|
||||
const { value = {}, setValue } = props;;
|
||||
return (
|
||||
<Form>
|
||||
<Form
|
||||
labelCol={{ span: 4 }}
|
||||
wrapperCol={{ span: 20 }}
|
||||
style={{ maxWidth: 600 }}
|
||||
>
|
||||
{Colors.map((ele) => (
|
||||
<Form.Item
|
||||
key={ele}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,114 @@
|
|||
import React, { useState } from 'react';
|
||||
import {
|
||||
Tabs,
|
||||
Row,
|
||||
Col,
|
||||
Card,
|
||||
Divider,
|
||||
Input,
|
||||
Form,
|
||||
Space,
|
||||
Select,
|
||||
Switch,
|
||||
} from 'antd';
|
||||
import Styles from './web.module.less';
|
||||
import { Config } from '../../../../types/Config';
|
||||
|
||||
// qrCodeType?: QrCodeType; // 生成二维码时,优先生成的类型
|
||||
// qrCodeApplicationId?: string; // 生成二维码时,优先使用的appId
|
||||
// qrCodePublicForMpId?: string; // 如果qrCodeType是wechatPublicForMp,在此指明关联的小程序appId
|
||||
// mpShareImageUrl?: string; // 小程序分享时的imageUrl(使用网络图片,5:4)
|
||||
|
||||
export default function Basic(props: {
|
||||
app: Required<Config>['App'];
|
||||
setValue: (path: string, value: any) => void;
|
||||
}) {
|
||||
const { app, setValue } = props;
|
||||
return (
|
||||
<Space direction="vertical" size="middle" style={{ display: 'flex' }}>
|
||||
<Row>
|
||||
<Card className={Styles.tips}>
|
||||
每种均可配置一个,相应的服务所使用的帐号请准确对应
|
||||
</Card>
|
||||
</Row>
|
||||
<Col flex="auto">
|
||||
<Divider orientation="left" className={Styles.title}>
|
||||
基础设置
|
||||
</Divider>
|
||||
<Form
|
||||
labelCol={{ span: 8 }}
|
||||
wrapperCol={{ span: 16 }}
|
||||
style={{ maxWidth: 600 }}
|
||||
>
|
||||
<Form.Item
|
||||
label="小程序分享时图片地址"
|
||||
tooltip="小程序分享时图片地址,使用网络图片"
|
||||
>
|
||||
<Input
|
||||
placeholder="请输入图片地址"
|
||||
type="text"
|
||||
value={app?.mpShareImageUrl}
|
||||
onChange={(e) =>
|
||||
setValue(`mpShareImageUrl`, e.target.value)
|
||||
}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="用户合并"
|
||||
//name="mergeUserDirectly"
|
||||
tooltip="当发现用户具有相同的特征时是否合并"
|
||||
>
|
||||
<>
|
||||
<Switch
|
||||
checkedChildren="是"
|
||||
unCheckedChildren="否"
|
||||
checked={app?.mergeUserDirectly}
|
||||
onChange={(checked) =>
|
||||
setValue(`mergeUserDirectly`, checked)
|
||||
}
|
||||
/>
|
||||
</>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Token刷新时间"
|
||||
tooltip="设置Token刷新时间,默认使用系统定义"
|
||||
>
|
||||
<Input
|
||||
placeholder="请输入Token刷新时间"
|
||||
type="number"
|
||||
value={app?.tokenRefreshTime}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
if (val) {
|
||||
setValue(`tokenRefreshTime`, Number(val));
|
||||
} else {
|
||||
setValue(`tokenRefreshTime`, val);
|
||||
}
|
||||
}}
|
||||
suffix="毫秒"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Token过期时间"
|
||||
tooltip="设置Token过期时间,默认使用系统定义的"
|
||||
>
|
||||
<Input
|
||||
placeholder="Token过期时间"
|
||||
type="number"
|
||||
value={app?.tokenExpireTime}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
if (val) {
|
||||
setValue(`tokenExpireTime`, Number(val));
|
||||
} else {
|
||||
setValue(`tokenExpireTime`, val);
|
||||
}
|
||||
}}
|
||||
suffix="毫秒"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Col>
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
|
||||
.label {
|
||||
color: var(--oak-text-color-primary);
|
||||
font-size: 28px;
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
.tips {
|
||||
color: var(--oak-text-color-placeholder);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-bottom: 0px;
|
||||
margin-top:36px;
|
||||
}
|
||||
|
|
@ -940,7 +940,11 @@ export default function Sms(props: {
|
|||
<Divider orientation="left" className={Styles.title}>
|
||||
短信配置
|
||||
</Divider>
|
||||
<Form>
|
||||
<Form
|
||||
labelCol={{ span: 8 }}
|
||||
wrapperCol={{ span: 16 }}
|
||||
style={{ maxWidth: 600 }}
|
||||
>
|
||||
<Form.Item
|
||||
label="模拟发送"
|
||||
//name="mockSend"
|
||||
|
|
@ -963,6 +967,7 @@ export default function Sms(props: {
|
|||
>
|
||||
<>
|
||||
<Select
|
||||
placeholder="请选择渠道"
|
||||
value={sms?.defaultOrigin}
|
||||
style={{ width: 120 }}
|
||||
onChange={(value) => {
|
||||
|
|
@ -978,7 +983,7 @@ export default function Sms(props: {
|
|||
</Form.Item>
|
||||
<Form.Item label="验证码模版名" tooltip="短信验证码模版名">
|
||||
<Input
|
||||
placeholder="请输入defaultCodeTemplateName"
|
||||
placeholder="请输入验证码模版名"
|
||||
type="text"
|
||||
value={sms?.defaultCodeTemplateName}
|
||||
onChange={(e) =>
|
||||
|
|
@ -994,7 +999,7 @@ export default function Sms(props: {
|
|||
tooltip="短信验证码发送有效时间"
|
||||
>
|
||||
<Input
|
||||
placeholder="请输入defaultCodeDuration"
|
||||
placeholder="请输入验证码有效时间"
|
||||
type="number"
|
||||
value={sms?.defaultCodeDuration}
|
||||
onChange={(e) => {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import Cos from './cos/index';
|
|||
import Map from './map/index';
|
||||
import Live from './live/index';
|
||||
import Sms from './sms/index';
|
||||
|
||||
import Basic from './basic/index';
|
||||
import { Config } from '../../../types/Config';
|
||||
|
||||
import { EntityDict } from '../../../oak-app-domain';
|
||||
|
|
@ -41,6 +41,7 @@ export default function Render(
|
|||
Map: map,
|
||||
Live: live,
|
||||
Sms: sms,
|
||||
App: app
|
||||
} = currentConfig || {};
|
||||
return (
|
||||
<>
|
||||
|
|
@ -169,6 +170,18 @@ export default function Render(
|
|||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: '基础设置',
|
||||
label: '基础设置',
|
||||
children: (
|
||||
<Basic
|
||||
app={app || {}}
|
||||
setValue={(path, value) =>
|
||||
setValue(`App.${path}`, value)
|
||||
}
|
||||
/>
|
||||
),
|
||||
},
|
||||
]}
|
||||
></Tabs>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ export default OakComponent({
|
|||
config: 1,
|
||||
description: 1,
|
||||
type: 1,
|
||||
|
||||
systemId: 1,
|
||||
domainId: 1,
|
||||
},
|
||||
properties: {
|
||||
systemId: '',
|
||||
|
|
@ -16,5 +17,5 @@ export default OakComponent({
|
|||
return {
|
||||
applications: data || [],
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Tabs, Modal, Button } from 'antd';
|
||||
import { Tabs, Modal, Button, Space } from 'antd';
|
||||
import { WebComponentProps } from 'oak-frontend-base';
|
||||
import { EntityDict } from '../../../oak-app-domain';
|
||||
import ApplicationPanel from '../../application/panel';
|
||||
|
|
@ -19,22 +19,35 @@ export default function render(props: WebComponentProps<EntityDict, 'application
|
|||
<>
|
||||
<Modal
|
||||
open={!!createId}
|
||||
width={800}
|
||||
width={500}
|
||||
onCancel={() => {
|
||||
clean();
|
||||
setCreateId('');
|
||||
}}
|
||||
footer={
|
||||
<Button
|
||||
type='primary'
|
||||
onClick={async () => {
|
||||
await execute();
|
||||
setCreateId('');
|
||||
}}
|
||||
disabled={oakExecutable !== true || oakExecuting}
|
||||
>
|
||||
{t('common::action.confirm')}
|
||||
</Button>
|
||||
<Space>
|
||||
<Button
|
||||
onClick={async () => {
|
||||
clean();
|
||||
setCreateId('');
|
||||
}}
|
||||
disabled={oakExecuting}
|
||||
>
|
||||
{t('common::action.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={async () => {
|
||||
await execute();
|
||||
setCreateId('');
|
||||
}}
|
||||
disabled={
|
||||
oakExecutable !== true || oakExecuting
|
||||
}
|
||||
>
|
||||
{t('common::action.confirm')}
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<ApplicationUpsert
|
||||
|
|
@ -50,7 +63,7 @@ export default function render(props: WebComponentProps<EntityDict, 'application
|
|||
}}
|
||||
footer={
|
||||
<Button
|
||||
type='primary'
|
||||
type="primary"
|
||||
onClick={async () => {
|
||||
removeItem(removeId);
|
||||
await execute();
|
||||
|
|
@ -69,32 +82,29 @@ export default function render(props: WebComponentProps<EntityDict, 'application
|
|||
if (action === 'add') {
|
||||
const id = addItem({ systemId });
|
||||
setCreateId(id);
|
||||
}
|
||||
else if (action === 'remove') {
|
||||
} else if (action === 'remove') {
|
||||
const appId = applications![Number(key)].id;
|
||||
setRemoveId(appId);
|
||||
}
|
||||
}}
|
||||
items={
|
||||
applications?.length > 0 ?
|
||||
applications.map(
|
||||
(item, idx) => {
|
||||
return {
|
||||
label: item.name,
|
||||
key: `${idx}`,
|
||||
children: (
|
||||
<ApplicationPanel
|
||||
oakPath={`${oakFullpath}.${item.id}`}
|
||||
oakId={item.id}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
applications?.length > 0
|
||||
? applications.map((item, idx) => {
|
||||
return {
|
||||
label: item.name,
|
||||
key: `${idx}`,
|
||||
children: (
|
||||
<ApplicationPanel
|
||||
oakPath={`${oakFullpath}.${item.id}`}
|
||||
oakId={item.id}
|
||||
/>
|
||||
),
|
||||
};
|
||||
})
|
||||
: []
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Row, Modal, Descriptions, Typography, Button } from 'antd';
|
||||
import { Row, Modal, Descriptions, Typography, Button, Space } from 'antd';
|
||||
import { WebComponentProps } from 'oak-frontend-base';
|
||||
|
||||
import { EntityDict } from '../../../oak-app-domain';
|
||||
|
|
@ -33,25 +33,36 @@ export default function Render(
|
|||
clean();
|
||||
setOpen(false);
|
||||
}}
|
||||
width={800}
|
||||
width={500}
|
||||
footer={
|
||||
<Button
|
||||
type='primary'
|
||||
onClick={async () => {
|
||||
await execute();
|
||||
setOpen(false);
|
||||
}}
|
||||
disabled={oakExecutable !== true || oakExecuting}
|
||||
>
|
||||
{t('common::action.confirm')}
|
||||
</Button>
|
||||
<Space>
|
||||
<Button
|
||||
// type='primary'
|
||||
onClick={async () => {
|
||||
clean();
|
||||
setOpen(false);
|
||||
}}
|
||||
disabled={oakExecuting}
|
||||
>
|
||||
{t('common::action.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={async () => {
|
||||
await execute();
|
||||
setOpen(false);
|
||||
}}
|
||||
disabled={
|
||||
oakExecutable !== true || oakExecuting
|
||||
}
|
||||
>
|
||||
{t('common::action.confirm')}
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<div className={Styles.upsert}>
|
||||
<SystemUpsert
|
||||
oakId={oakId}
|
||||
oakPath={oakFullpath}
|
||||
/>
|
||||
<SystemUpsert oakId={oakId} oakPath={oakFullpath} />
|
||||
</div>
|
||||
</Modal>
|
||||
<Descriptions column={2} bordered>
|
||||
|
|
@ -60,30 +71,20 @@ export default function Render(
|
|||
{oakId}
|
||||
</Typography.Paragraph>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item
|
||||
label={t('system:attr.name')}
|
||||
>
|
||||
<Descriptions.Item label={t('system:attr.name')}>
|
||||
{name}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item
|
||||
label={t('system:attr.description')}
|
||||
>
|
||||
<Descriptions.Item label={t('system:attr.description')}>
|
||||
{description}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item
|
||||
label={t('system:attr.super')}
|
||||
>
|
||||
<Descriptions.Item label={t('system:attr.super')}>
|
||||
{isSuper ? '是' : '否'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item
|
||||
label={t('system:attr.folder')}
|
||||
>
|
||||
<Descriptions.Item label={t('system:attr.folder')}>
|
||||
{folder}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item>
|
||||
<Row
|
||||
justify="end"
|
||||
>
|
||||
<Row justify="end">
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => setOpen(true)}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ export default OakComponent({
|
|||
config: 1,
|
||||
description: 1,
|
||||
type: 1,
|
||||
systemId: 1,
|
||||
domainId: 1,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -19,17 +19,21 @@ export default function Render(props: WebComponentProps<EntityDict, 'system', fa
|
|||
style: Style;
|
||||
application$system: EntityDict['application']['OpSchema'][];
|
||||
}>) {
|
||||
const { id, config, oakFullpath, name, style, application$system: applications } = props.data;
|
||||
const { t, update, addItem, removeItem } = props.methods;
|
||||
const { id, config, oakFullpath, name, style } = props.data;
|
||||
const { t, } = props.methods;
|
||||
|
||||
if (id && oakFullpath) {
|
||||
return (
|
||||
<div className={Styles.container}>
|
||||
<Tabs
|
||||
tabPosition='left'
|
||||
tabPosition="left"
|
||||
items={[
|
||||
{
|
||||
label: <div className={Styles.tabLabel}>{t('detail')}</div>,
|
||||
label: (
|
||||
<div className={Styles.tabLabel}>
|
||||
{t('detail')}
|
||||
</div>
|
||||
),
|
||||
key: 'detail',
|
||||
children: (
|
||||
<SystemDetail
|
||||
|
|
@ -39,7 +43,11 @@ export default function Render(props: WebComponentProps<EntityDict, 'system', fa
|
|||
),
|
||||
},
|
||||
{
|
||||
label: <div className={Styles.tabLabel}>{t('config')}</div>,
|
||||
label: (
|
||||
<div className={Styles.tabLabel}>
|
||||
{t('config')}
|
||||
</div>
|
||||
),
|
||||
key: 'config',
|
||||
children: (
|
||||
<ConfigUpsert
|
||||
|
|
@ -51,7 +59,11 @@ export default function Render(props: WebComponentProps<EntityDict, 'system', fa
|
|||
),
|
||||
},
|
||||
{
|
||||
label: <div className={Styles.tabLabel}>{t('style')}</div>,
|
||||
label: (
|
||||
<div className={Styles.tabLabel}>
|
||||
{t('style')}
|
||||
</div>
|
||||
),
|
||||
key: 'style',
|
||||
children: (
|
||||
<StyleUpsert
|
||||
|
|
@ -63,7 +75,11 @@ export default function Render(props: WebComponentProps<EntityDict, 'system', fa
|
|||
),
|
||||
},
|
||||
{
|
||||
label: <div className={Styles.tabLabel}>{t('application-list')}</div>,
|
||||
label: (
|
||||
<div className={Styles.tabLabel}>
|
||||
{t('application-list')}
|
||||
</div>
|
||||
),
|
||||
key: 'application',
|
||||
children: (
|
||||
<ApplicationList
|
||||
|
|
@ -73,7 +89,11 @@ export default function Render(props: WebComponentProps<EntityDict, 'system', fa
|
|||
),
|
||||
},
|
||||
{
|
||||
label: <div className={Styles.tabLabel}>{t('domain-list')}</div>,
|
||||
label: (
|
||||
<div className={Styles.tabLabel}>
|
||||
{t('domain-list')}
|
||||
</div>
|
||||
),
|
||||
key: 'domain_list',
|
||||
children: (
|
||||
<DomainList
|
||||
|
|
@ -83,7 +103,11 @@ export default function Render(props: WebComponentProps<EntityDict, 'system', fa
|
|||
),
|
||||
},
|
||||
{
|
||||
label: <div className={Styles.tabLabel}>{t('smsTemplate-list')}</div>,
|
||||
label: (
|
||||
<div className={Styles.tabLabel}>
|
||||
{t('smsTemplate-list')}
|
||||
</div>
|
||||
),
|
||||
key: 'smsTemplate-list',
|
||||
children: (
|
||||
<SmsTemplateList
|
||||
|
|
|
|||
|
|
@ -1,6 +1,14 @@
|
|||
export default OakComponent({
|
||||
isList: false,
|
||||
entity: 'system',
|
||||
projection: {
|
||||
id: 1,
|
||||
name: 1,
|
||||
config: 1,
|
||||
description: 1,
|
||||
super: 1,
|
||||
folder: 1,
|
||||
},
|
||||
formData({ data }) {
|
||||
return data || {};
|
||||
},
|
||||
|
|
|
|||
|
|
@ -54,11 +54,6 @@ export default function Render(
|
|||
required
|
||||
// name="folder"
|
||||
tooltip="目录属性应和开发目录下的对应目录名匹配,请谨慎修改"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<>
|
||||
<Input
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { String, Int, Datetime, Image, Boolean, Text } from 'oak-domain/lib/type
|
|||
import { EntityShape } from 'oak-domain/lib/types/Entity';
|
||||
import { Schema as System } from './System';
|
||||
import { Schema as Session } from './Session';
|
||||
import { Schema as Domain } from './Domain';
|
||||
|
||||
import { Style } from '../types/Style';
|
||||
import { EntityDesc } from 'oak-domain/lib/types/EntityDesc';
|
||||
|
|
@ -72,6 +73,7 @@ export interface Schema extends EntityShape {
|
|||
config: WebConfig | WechatMpConfig | WechatPublicConfig | NativeConfig;
|
||||
style?: Style;
|
||||
sessions?: Session[];
|
||||
domain?: Domain;
|
||||
};
|
||||
|
||||
const entityDesc: EntityDesc<
|
||||
|
|
@ -93,6 +95,7 @@ const entityDesc: EntityDesc<
|
|||
config: '设置',
|
||||
style: '样式',
|
||||
sessions: '会话',
|
||||
domain: '域名',
|
||||
},
|
||||
v: {
|
||||
type: {
|
||||
|
|
|
|||
|
|
@ -15,11 +15,7 @@ const initialThemeState: IThemeState = {
|
|||
setting: false,
|
||||
themeMode: defaultTheme,
|
||||
systemTheme: false,
|
||||
isFullPage: false,
|
||||
color: '#0052d9',
|
||||
showHeader: true,
|
||||
showBreadcrumbs: true,
|
||||
showFooter: true,
|
||||
};
|
||||
|
||||
export default class Theme<
|
||||
|
|
@ -32,8 +28,10 @@ export default class Theme<
|
|||
private themeState: IThemeState;
|
||||
private storage: LocalStorage;
|
||||
|
||||
private async loadSavedState() {
|
||||
const themeState = await this.storage.load(LOCAL_STORAGE_KEYS.themeState);
|
||||
private async loadSavedState() {
|
||||
const themeState = await this.storage.load(
|
||||
LOCAL_STORAGE_KEYS.themeState
|
||||
);
|
||||
this.themeState = themeState;
|
||||
}
|
||||
|
||||
|
|
@ -115,22 +113,21 @@ export default class Theme<
|
|||
return state.color;
|
||||
}
|
||||
|
||||
switchColor(color: string) {
|
||||
switchColor(color: string, theme?: string) {
|
||||
const state = this.themeState;
|
||||
if (color) {
|
||||
state.color = color; // 某主题 主题色
|
||||
const themeColor = 'blue'; // 主题颜色类型
|
||||
// const themeColor = 'blue'; // 主题颜色类型
|
||||
switch (process.env.OAK_PLATFORM) {
|
||||
case 'web': {
|
||||
this.insertThemeStylesheet(
|
||||
themeColor,
|
||||
color,
|
||||
state.themeMode
|
||||
);
|
||||
document.documentElement.setAttribute(
|
||||
'theme-color',
|
||||
themeColor
|
||||
);
|
||||
this.insertThemeStylesheet(color, state.themeMode, theme);
|
||||
if (theme) {
|
||||
document.documentElement.setAttribute(
|
||||
'theme-color',
|
||||
theme
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
|
@ -141,11 +138,13 @@ export default class Theme<
|
|||
}
|
||||
}
|
||||
|
||||
insertThemeStylesheet(theme: string, color: string, mode: ETheme) {
|
||||
insertThemeStylesheet(color: string, mode: ETheme, theme?: string) {
|
||||
const isDarkMode = mode === 'dark';
|
||||
const root = !isDarkMode
|
||||
? `:root[theme-color='${theme}']`
|
||||
: `:root[theme-color='${theme}'][theme-mode='dark']`;
|
||||
? (theme
|
||||
? `:root[theme-color='${theme}']`
|
||||
: `:root`)
|
||||
: (theme ? `:root[theme-color='${theme}'][theme-mode='dark']` : `:root[theme-mode='dark']`);
|
||||
|
||||
const styleSheet = document.createElement('style');
|
||||
styleSheet.type = 'text/css';
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ export type Config = {
|
|||
mockSend?: boolean;
|
||||
defaultOrigin?: 'ali' | 'tencent' | 'ctyun'; //默认渠道
|
||||
defaultCodeTemplateName?: string; //验证码模版名
|
||||
defaultCodeDuration?: number; //验证码有效时间 单位分钟, 不填1分钟
|
||||
defaultCodeDuration?: number; //验证码有效时间 单位分钟, 不填1分钟
|
||||
ali?: AliSmsConfig[];
|
||||
tencent?: TencentSmsConfig[];
|
||||
ctyun?: CTYunSmsConfig[];
|
||||
|
|
@ -137,6 +137,8 @@ export type Config = {
|
|||
qrCodePublicForMpId?: string; // 如果qrCodeType是wechatPublicForMp,在此指明关联的小程序appId
|
||||
mpShareImageUrl?: string; // 小程序分享时的imageUrl(使用网络图片,5:4)
|
||||
mergeUserDirectly?: boolean; // 当发现用户具有相同的特征时直接合并
|
||||
tokenRefreshTime?: number; //毫秒
|
||||
tokenExpireTime?: number; //毫秒
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -135,8 +135,17 @@ export const applicationProjection: EntityDict['application']['Selection']['data
|
|||
apiPath: 1,
|
||||
protocol: 1,
|
||||
port: 1,
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
domainId: 1,
|
||||
domain: {
|
||||
id: 1,
|
||||
systemId: 1,
|
||||
url: 1,
|
||||
apiPath: 1,
|
||||
protocol: 1,
|
||||
port: 1,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -15,9 +15,5 @@ export interface IThemeState {
|
|||
* 是否开启跟随系统主题
|
||||
*/
|
||||
systemTheme: boolean;
|
||||
isFullPage: boolean;
|
||||
showHeader: boolean;
|
||||
showBreadcrumbs: boolean;
|
||||
showFooter: boolean;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue