修改了初始化的逻辑和模板
This commit is contained in:
parent
f076a0422f
commit
975154d82d
|
|
@ -25,16 +25,22 @@ const prompt = [
|
||||||
message: 'version',
|
message: 'version',
|
||||||
default: '1.0.0',
|
default: '1.0.0',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
type: 'input',
|
|
||||||
name: 'title',
|
|
||||||
message: 'title of the project shown in App/Html'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
type: 'input',
|
type: 'input',
|
||||||
name: 'description',
|
name: 'description',
|
||||||
message: 'description',
|
message: 'description',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'useOgb',
|
||||||
|
type: 'confirm',
|
||||||
|
message: 'add oak-general-business into dependency?',
|
||||||
|
default: true,
|
||||||
|
}, {
|
||||||
|
name: 'moreDeps',
|
||||||
|
type: 'input',
|
||||||
|
message: 'do you have more dependent oak-family libraries? type their names, use comma as separator.',
|
||||||
|
default: '',
|
||||||
|
}
|
||||||
];
|
];
|
||||||
/**
|
/**
|
||||||
* 将项目的依赖关系加入
|
* 将项目的依赖关系加入
|
||||||
|
|
@ -137,10 +143,16 @@ async function create(dirName, cmd) {
|
||||||
message: `name`,
|
message: `name`,
|
||||||
default: dirName,
|
default: dirName,
|
||||||
};
|
};
|
||||||
prompt.unshift(nameOption);
|
const titleOption = {
|
||||||
|
type: 'input',
|
||||||
|
name: 'title',
|
||||||
|
message: 'title of the project shown in App/Html',
|
||||||
|
default: dirName,
|
||||||
|
};
|
||||||
|
prompt.unshift(nameOption, titleOption);
|
||||||
const isDev = cmd.dev ? true : false;
|
const isDev = cmd.dev ? true : false;
|
||||||
const isModule = cmd.module ? true : false;
|
const isModule = cmd.module ? true : false;
|
||||||
const { name, version, title, description } = await inquirer_1.default.prompt(prompt);
|
const { name, version, title, description, useOgb, moreDeps } = await inquirer_1.default.prompt(prompt);
|
||||||
// 获取tsconfig.json内容
|
// 获取tsconfig.json内容
|
||||||
const tsconfigJson = (0, template_1.tsConfigJsonContent)();
|
const tsconfigJson = (0, template_1.tsConfigJsonContent)();
|
||||||
const tsConfigBuildJson = (0, template_1.tsConfigBuildJsonContent)();
|
const tsConfigBuildJson = (0, template_1.tsConfigBuildJsonContent)();
|
||||||
|
|
@ -197,27 +209,16 @@ async function create(dirName, cmd) {
|
||||||
(0, file_handle_1.copyFolder)(currentPath, rootPath);
|
(0, file_handle_1.copyFolder)(currentPath, rootPath);
|
||||||
await createWechatMpBoilplate(weChatMpRootPath, isDev);
|
await createWechatMpBoilplate(weChatMpRootPath, isDev);
|
||||||
await createWebBoilplate(webRootPath, isDev);
|
await createWebBoilplate(webRootPath, isDev);
|
||||||
if (!shelljs_1.default.which('npm')) {
|
/* if (!shell.which('npm')) {
|
||||||
(0, tip_style_1.Warn)((0, tip_style_1.warn)('Sorry, this script requires npm! Please install npm!'));
|
Warn(warn('Sorry, this script requires npm! Please install npm!'));
|
||||||
shelljs_1.default.exit(1);
|
shell.exit(1);
|
||||||
}
|
}
|
||||||
/* Success(`${success(`Waiting...`)}`);
|
|
||||||
|
Success(`${success(`Waiting...`)}`);
|
||||||
Success(`${success(`Dependencies are now being installed`)}`);
|
Success(`${success(`Dependencies are now being installed`)}`);
|
||||||
shell.cd(dirName).exec('npm install'); */
|
shell.cd(dirName).exec('npm install'); */
|
||||||
(0, rename_1.renameProject)(rootPath, name, title, DEFAULT_PROJECT_NAME, DEFAULT_PROJECT_TITLE);
|
|
||||||
}
|
}
|
||||||
(0, tip_style_1.Success)(`${(0, tip_style_1.success)(`Successfully created project ${(0, tip_style_1.primary)(name)}, directory name is ${(0, tip_style_1.primary)(dirName)}`)}`);
|
(0, tip_style_1.Success)(`${(0, tip_style_1.success)(`Successfully created project ${(0, tip_style_1.primary)(name)}, directory name is ${(0, tip_style_1.primary)(dirName)}`)}`);
|
||||||
const { useOgb, moreDeps } = await inquirer_1.default.prompt([{
|
|
||||||
name: 'useOgb',
|
|
||||||
type: 'confirm',
|
|
||||||
message: 'add oak-general-business into dependency?',
|
|
||||||
default: true,
|
|
||||||
}, {
|
|
||||||
name: 'moreDeps',
|
|
||||||
type: 'input',
|
|
||||||
message: 'do you have more dependent oak-family libraries? type their names, use comma as separator.',
|
|
||||||
default: '',
|
|
||||||
}]);
|
|
||||||
const deps = [];
|
const deps = [];
|
||||||
if (useOgb) {
|
if (useOgb) {
|
||||||
deps.push('oak-general-business');
|
deps.push('oak-general-business');
|
||||||
|
|
@ -242,6 +243,7 @@ async function create(dirName, cmd) {
|
||||||
});
|
});
|
||||||
// 创建package.json
|
// 创建package.json
|
||||||
(0, file_handle_1.checkFileExistsAndCreate)(packageJsonPath, packageJson, enum_1.checkFileExistsAndCreateType.FILE);
|
(0, file_handle_1.checkFileExistsAndCreate)(packageJsonPath, packageJson, enum_1.checkFileExistsAndCreateType.FILE);
|
||||||
|
(0, rename_1.renameProject)(rootPath, name, title, DEFAULT_PROJECT_NAME, DEFAULT_PROJECT_TITLE);
|
||||||
(0, tip_style_1.Success)(`${(0, tip_style_1.success)(`Ok, type 'npm install' to install libs, then start!`)}`);
|
(0, tip_style_1.Success)(`${(0, tip_style_1.success)(`Ok, type 'npm install' to install libs, then start!`)}`);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ export interface PromptInput {
|
||||||
version: string;
|
version: string;
|
||||||
description: string;
|
description: string;
|
||||||
title: string;
|
title: string;
|
||||||
|
useOgb: boolean;
|
||||||
|
moreDeps: string;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @name project.config.json
|
* @name project.config.json
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,11 @@ async function renameProject(dir, name, title, placeholderName, placeholderTitle
|
||||||
const htmlContent = (0, fs_1.readFileSync)(htmlFilePath, 'utf-8');
|
const htmlContent = (0, fs_1.readFileSync)(htmlFilePath, 'utf-8');
|
||||||
const newHtmlContent = htmlContent.replace(new RegExp(placeholderTitle, 'g'), title).replace(new RegExp(placeholderTitle.toLowerCase(), 'g'), title.toLowerCase());
|
const newHtmlContent = htmlContent.replace(new RegExp(placeholderTitle, 'g'), title).replace(new RegExp(placeholderTitle.toLowerCase(), 'g'), title.toLowerCase());
|
||||||
(0, fs_1.writeFileSync)(htmlFilePath, newHtmlContent, 'utf-8');
|
(0, fs_1.writeFileSync)(htmlFilePath, newHtmlContent, 'utf-8');
|
||||||
|
// index.tsx下的title
|
||||||
|
const indexTsxPath = (0, path_1.join)(dir, 'web', 'src/index.tsx');
|
||||||
|
const tsxContent = (0, fs_1.readFileSync)(indexTsxPath, 'utf-8');
|
||||||
|
const newTsxlContent = tsxContent.replace(new RegExp(placeholderName, 'g'), name).replace(new RegExp(placeholderName.toLowerCase(), 'g'), name.toLowerCase());
|
||||||
|
(0, fs_1.writeFileSync)(indexTsxPath, newTsxlContent, 'utf-8');
|
||||||
// replace wechatMp下project.config.json中的projectname
|
// replace wechatMp下project.config.json中的projectname
|
||||||
// todo,现在这个是在wechatMp/src目录下的,可能是搞错了,待修正
|
// todo,现在这个是在wechatMp/src目录下的,可能是搞错了,待修正
|
||||||
const pcjFilePath = (0, path_1.join)(dir, 'wechatMp', 'src', 'project.config.json');
|
const pcjFilePath = (0, path_1.join)(dir, 'wechatMp', 'src', 'project.config.json');
|
||||||
|
|
|
||||||
|
|
@ -56,16 +56,22 @@ const prompt = [
|
||||||
message: 'version',
|
message: 'version',
|
||||||
default: '1.0.0',
|
default: '1.0.0',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
type: 'input',
|
|
||||||
name: 'title',
|
|
||||||
message: 'title of the project shown in App/Html'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
type: 'input',
|
type: 'input',
|
||||||
name: 'description',
|
name: 'description',
|
||||||
message: 'description',
|
message: 'description',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'useOgb',
|
||||||
|
type: 'confirm',
|
||||||
|
message: 'add oak-general-business into dependency?',
|
||||||
|
default: true,
|
||||||
|
}, {
|
||||||
|
name: 'moreDeps',
|
||||||
|
type: 'input',
|
||||||
|
message: 'do you have more dependent oak-family libraries? type their names, use comma as separator.',
|
||||||
|
default: '',
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -220,11 +226,17 @@ export async function create(dirName: string, cmd: any) {
|
||||||
message: `name`,
|
message: `name`,
|
||||||
default: dirName,
|
default: dirName,
|
||||||
};
|
};
|
||||||
prompt.unshift(nameOption);
|
const titleOption = {
|
||||||
|
type: 'input',
|
||||||
|
name: 'title',
|
||||||
|
message: 'title of the project shown in App/Html',
|
||||||
|
default: dirName,
|
||||||
|
};
|
||||||
|
prompt.unshift(nameOption, titleOption);
|
||||||
const isDev = cmd.dev ? true : false;
|
const isDev = cmd.dev ? true : false;
|
||||||
const isModule = cmd.module ? true : false;
|
const isModule = cmd.module ? true : false;
|
||||||
|
|
||||||
const { name, version, title, description }: PromptInput = await inquirer.prompt(
|
const { name, version, title, description, useOgb, moreDeps }: PromptInput = await inquirer.prompt(
|
||||||
prompt
|
prompt
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -311,19 +323,16 @@ export async function create(dirName: string, cmd: any) {
|
||||||
|
|
||||||
await createWechatMpBoilplate(weChatMpRootPath, isDev);
|
await createWechatMpBoilplate(weChatMpRootPath, isDev);
|
||||||
await createWebBoilplate(webRootPath, isDev);
|
await createWebBoilplate(webRootPath, isDev);
|
||||||
if (!shell.which('npm')) {
|
/* if (!shell.which('npm')) {
|
||||||
Warn(warn('Sorry, this script requires npm! Please install npm!'));
|
Warn(warn('Sorry, this script requires npm! Please install npm!'));
|
||||||
shell.exit(1);
|
shell.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Success(`${success(`Waiting...`)}`);
|
Success(`${success(`Waiting...`)}`);
|
||||||
Success(`${success(`Dependencies are now being installed`)}`);
|
Success(`${success(`Dependencies are now being installed`)}`);
|
||||||
shell.cd(dirName).exec('npm install'); */
|
shell.cd(dirName).exec('npm install'); */
|
||||||
|
|
||||||
renameProject(rootPath, name, title, DEFAULT_PROJECT_NAME, DEFAULT_PROJECT_TITLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Success(
|
Success(
|
||||||
`${success(
|
`${success(
|
||||||
`Successfully created project ${primary(
|
`Successfully created project ${primary(
|
||||||
|
|
@ -332,17 +341,6 @@ export async function create(dirName: string, cmd: any) {
|
||||||
)}`
|
)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
const { useOgb, moreDeps } = await inquirer.prompt([{
|
|
||||||
name: 'useOgb',
|
|
||||||
type: 'confirm',
|
|
||||||
message: 'add oak-general-business into dependency?',
|
|
||||||
default: true,
|
|
||||||
}, {
|
|
||||||
name: 'moreDeps',
|
|
||||||
type: 'input',
|
|
||||||
message: 'do you have more dependent oak-family libraries? type their names, use comma as separator.',
|
|
||||||
default: '',
|
|
||||||
}]);
|
|
||||||
const deps = [] as string[];
|
const deps = [] as string[];
|
||||||
if (useOgb) {
|
if (useOgb) {
|
||||||
deps.push('oak-general-business');
|
deps.push('oak-general-business');
|
||||||
|
|
@ -381,6 +379,7 @@ export async function create(dirName: string, cmd: any) {
|
||||||
checkFileExistsAndCreateType.FILE
|
checkFileExistsAndCreateType.FILE
|
||||||
);
|
);
|
||||||
|
|
||||||
|
renameProject(rootPath, name, title, DEFAULT_PROJECT_NAME, DEFAULT_PROJECT_TITLE);
|
||||||
Success(
|
Success(
|
||||||
`${success(
|
`${success(
|
||||||
`Ok, type 'npm install' to install libs, then start!`
|
`Ok, type 'npm install' to install libs, then start!`
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,8 @@ export interface PromptInput {
|
||||||
version: string,
|
version: string,
|
||||||
description: string,
|
description: string,
|
||||||
title: string,
|
title: string,
|
||||||
|
useOgb: boolean;
|
||||||
|
moreDeps: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,12 @@ export async function renameProject(dir: string, name: string, title: string, pl
|
||||||
const newHtmlContent = htmlContent.replace(new RegExp(placeholderTitle, 'g'), title).replace(new RegExp(placeholderTitle.toLowerCase(), 'g'), title.toLowerCase());
|
const newHtmlContent = htmlContent.replace(new RegExp(placeholderTitle, 'g'), title).replace(new RegExp(placeholderTitle.toLowerCase(), 'g'), title.toLowerCase());
|
||||||
writeFileSync(htmlFilePath, newHtmlContent, 'utf-8');
|
writeFileSync(htmlFilePath, newHtmlContent, 'utf-8');
|
||||||
|
|
||||||
|
// index.tsx下的title
|
||||||
|
const indexTsxPath = join(dir, 'web', 'src/index.tsx');
|
||||||
|
const tsxContent = readFileSync(indexTsxPath, 'utf-8');
|
||||||
|
const newTsxlContent = tsxContent.replace(new RegExp(placeholderName, 'g'), name).replace(new RegExp(placeholderName.toLowerCase(), 'g'), name.toLowerCase());
|
||||||
|
writeFileSync(indexTsxPath, newTsxlContent, 'utf-8');
|
||||||
|
|
||||||
// replace wechatMp下project.config.json中的projectname
|
// replace wechatMp下project.config.json中的projectname
|
||||||
// todo,现在这个是在wechatMp/src目录下的,可能是搞错了,待修正
|
// todo,现在这个是在wechatMp/src目录下的,可能是搞错了,待修正
|
||||||
const pcjFilePath = join(dir, 'wechatMp', 'src', 'project.config.json');
|
const pcjFilePath = join(dir, 'wechatMp', 'src', 'project.config.json');
|
||||||
|
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
import { EntityDict } from '@project/oak-app-domain';
|
|
||||||
import { RuntimeContext } from './RuntimeContext';
|
|
||||||
import { BackendRuntimeContext as DependentBackendRuntimeContext } from './DependentContext';
|
|
||||||
|
|
||||||
export class BackendRuntimeContext extends DependentBackendRuntimeContext<EntityDict> implements RuntimeContext {
|
|
||||||
async toString() {
|
|
||||||
const data = await this.getSerializedData();
|
|
||||||
return JSON.stringify(data);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default BackendRuntimeContext;
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
import { EntityDict } from '@project/oak-app-domain';
|
|
||||||
import {
|
|
||||||
FrontendRuntimeContext as DependentFrontendRuntimeContext,
|
|
||||||
} from './DependentContext';
|
|
||||||
import { RuntimeContext } from './RuntimeContext';
|
|
||||||
|
|
||||||
export class FrontendRuntimeContext
|
|
||||||
extends DependentFrontendRuntimeContext<EntityDict>
|
|
||||||
implements RuntimeContext
|
|
||||||
{
|
|
||||||
async toString() {
|
|
||||||
const data = await this.getSerializedData();
|
|
||||||
return JSON.stringify(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default FrontendRuntimeContext;
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import initialize from './initialize.dev';
|
import initialize from './initialize.frontend';
|
||||||
export default initialize;
|
export default initialize;
|
||||||
|
|
||||||
console.log('不应该走到这里');
|
console.log('不应该走到这里');
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@
|
||||||
work correctly both with client-side routing and a non-root public URL.
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
Learn how to configure a non-root public URL by running `npm run build`.
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
-->
|
-->
|
||||||
<title></title>
|
<title>oak template project</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
@import '@project/config/styles/web/index.less'; // 少量公共样式
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import './App.less';
|
|
||||||
import AppContainer from './AppContainer';
|
|
||||||
import AppRouter from './AppRouter';
|
|
||||||
import AppError from './AppError';
|
|
||||||
|
|
||||||
function App(props: { error?: any }) {
|
|
||||||
if (props.error) {
|
|
||||||
return <AppError error={props.error} />;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<AppContainer>
|
|
||||||
<AppRouter />
|
|
||||||
</AppContainer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default App;
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
import React, { lazy } from 'react';
|
|
||||||
const Message = lazy(() => import('oak-frontend-base/es/components/message'));
|
|
||||||
const DebugPanel = lazy(() => import('oak-frontend-base/es/components/func/debugPanel'));
|
|
||||||
|
|
||||||
type AppContainerProps = {
|
|
||||||
children?: React.ReactNode;
|
|
||||||
};
|
|
||||||
|
|
||||||
const AppContainer = (props: AppContainerProps) => {
|
|
||||||
const { children } = props;
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
<React.Suspense fallback={<></>}>
|
|
||||||
<Message />
|
|
||||||
</React.Suspense>
|
|
||||||
{children}
|
|
||||||
<React.Suspense fallback={<></>}>
|
|
||||||
{process.env.NODE_ENV === 'development' && <DebugPanel />}
|
|
||||||
</React.Suspense>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AppContainer;
|
|
||||||
|
|
@ -1,121 +0,0 @@
|
||||||
import React, { lazy } from 'react';
|
|
||||||
import { Button, Space } from 'antd';
|
|
||||||
|
|
||||||
import {
|
|
||||||
OakException,
|
|
||||||
OakExternalException,
|
|
||||||
OakNetworkException,
|
|
||||||
OakServerProxyException,
|
|
||||||
} from 'oak-domain/lib/types/Exception';
|
|
||||||
import { ECode } from 'oak-frontend-base/es/types/ErrorPage';
|
|
||||||
const ErrorPage = lazy(() => import('oak-frontend-base/es/components/errorPage'));
|
|
||||||
|
|
||||||
interface ErrorProps {
|
|
||||||
error: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Error(props: ErrorProps) {
|
|
||||||
const { error } = props;
|
|
||||||
|
|
||||||
if (error instanceof OakException) {
|
|
||||||
if (error instanceof OakNetworkException) {
|
|
||||||
// 网络中断出现的异常
|
|
||||||
return (
|
|
||||||
<ErrorPage
|
|
||||||
code={ECode.error}
|
|
||||||
title="网络异常"
|
|
||||||
desc="抱歉,网络访问失败!"
|
|
||||||
>
|
|
||||||
<ErrorMessage error={error} />
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
onClick={async () => {
|
|
||||||
window.location.reload();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
检查网络
|
|
||||||
</Button>
|
|
||||||
</ErrorPage>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (error instanceof OakServerProxyException) {
|
|
||||||
// 服务器代理异常
|
|
||||||
return (
|
|
||||||
<ErrorPage
|
|
||||||
code={ECode.error}
|
|
||||||
title="服务器代理异常"
|
|
||||||
desc="抱歉,服务器代理出现错误!"
|
|
||||||
>
|
|
||||||
<ErrorMessage error={error} />
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
onClick={async () => {
|
|
||||||
window.location.reload();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
检查服务器代理
|
|
||||||
</Button>
|
|
||||||
</ErrorPage>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ErrorPage
|
|
||||||
code={ECode.error}
|
|
||||||
title="系统内部错误"
|
|
||||||
desc="抱歉,系统内部错误,我们的技术人员正在快马加鞭的修复"
|
|
||||||
>
|
|
||||||
<ErrorMessage error={error} />
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
onClick={async () => {
|
|
||||||
window.location.reload();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
联系我们
|
|
||||||
</Button>
|
|
||||||
</ErrorPage>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ErrorPage
|
|
||||||
code={ECode.error}
|
|
||||||
title="数据缓存失效"
|
|
||||||
desc="抱歉,数据缓存失效,需要清除缓存,请点击【清除缓存】"
|
|
||||||
>
|
|
||||||
<ErrorMessage error={error} />
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
onClick={async () => {
|
|
||||||
(global as any).features.localStorage.clear();
|
|
||||||
window.location.reload();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
清除缓存
|
|
||||||
</Button>
|
|
||||||
</ErrorPage>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ErrorMessage(props: { error: any }) {
|
|
||||||
const { error } = props;
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
|
||||||
return (
|
|
||||||
<span
|
|
||||||
style={{
|
|
||||||
marginBottom: 24,
|
|
||||||
color: 'red',
|
|
||||||
fontSize: 14,
|
|
||||||
marginLeft: 24,
|
|
||||||
marginRight: 24,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{typeof error === 'object' ? error.message : error}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Error;
|
|
||||||
|
|
@ -1,99 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { Routes, Route, Outlet, useLocation } from 'react-router-dom';
|
|
||||||
import { IRouter, IBrowserRouterProps, IMeta } from './types/router';
|
|
||||||
import routers from './app/routers';
|
|
||||||
import Loading from './app/components/Loading';
|
|
||||||
import useFeatures from '@project/hooks/useFeatures';
|
|
||||||
|
|
||||||
type TRenderRoutes = (
|
|
||||||
routes?: IRouter[],
|
|
||||||
breadcrumbs?: string[]
|
|
||||||
) => React.ReactNode[] | null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 渲染应用路由
|
|
||||||
* @param routes
|
|
||||||
* @param breadcrumb
|
|
||||||
*/
|
|
||||||
const renderRoutes: TRenderRoutes = (routes = [], breadcrumb = []) => {
|
|
||||||
if (routes.length === 0) return null;
|
|
||||||
return routes.map((route, index: number) => {
|
|
||||||
const { Component, children, namespace, meta, isFirst, customRouter } =
|
|
||||||
route;
|
|
||||||
let currentBreadcrumb = breadcrumb;
|
|
||||||
let props = {};
|
|
||||||
if (isFirst) {
|
|
||||||
props = {
|
|
||||||
index: true,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
props = {
|
|
||||||
path: route.path,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Route
|
|
||||||
{...props}
|
|
||||||
key={index}
|
|
||||||
element={
|
|
||||||
<Guard
|
|
||||||
Component={Component}
|
|
||||||
meta={meta}
|
|
||||||
breadcrumb={currentBreadcrumb}
|
|
||||||
namespace={namespace}
|
|
||||||
customRouter={customRouter}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{renderRoutes(children, currentBreadcrumb)}
|
|
||||||
</Route>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const Guard = React.memo(
|
|
||||||
(props: {
|
|
||||||
Component?: React.FC<IBrowserRouterProps> | (() => any);
|
|
||||||
namespace?: string;
|
|
||||||
meta?: IMeta;
|
|
||||||
breadcrumb: string[];
|
|
||||||
customRouter?: boolean;
|
|
||||||
}) => {
|
|
||||||
const { Component, namespace, meta, breadcrumb, customRouter } = props;
|
|
||||||
const features = useFeatures();
|
|
||||||
const location = useLocation();
|
|
||||||
const { pathname } = location;
|
|
||||||
const path = namespace ? pathname.slice(namespace.length) : pathname;
|
|
||||||
const i18nNs = `taicang-p-${
|
|
||||||
path.startsWith('/')
|
|
||||||
? path.slice(1).replaceAll(/\//g, '-')
|
|
||||||
: path.replaceAll(/\//g, '-')
|
|
||||||
}`;
|
|
||||||
|
|
||||||
if (i18nNs) {
|
|
||||||
const windowTitle = features.locales.hasKey(`${i18nNs}.pageTitle`);
|
|
||||||
if (windowTitle) {
|
|
||||||
breadcrumb.push(windowTitle);
|
|
||||||
window.document.title = windowTitle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Component ? (
|
|
||||||
<React.Suspense fallback={<Loading />}>
|
|
||||||
<Component
|
|
||||||
oakDisablePulldownRefresh={meta?.oakDisablePulldownRefresh}
|
|
||||||
namespace={namespace || ''}
|
|
||||||
customRouter={customRouter}
|
|
||||||
/>
|
|
||||||
</React.Suspense>
|
|
||||||
) : (
|
|
||||||
<Outlet />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const AppRouter = React.memo(() => (
|
|
||||||
<Routes>{renderRoutes(routers)}</Routes>
|
|
||||||
));
|
|
||||||
|
|
||||||
export default AppRouter;
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
@import '@project/config/styles/web/index.less'; // 少量公共样式
|
||||||
|
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,12 @@
|
||||||
import './utils/polyfill';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom/client';
|
import { EntityDict } from '@project/oak-app-domain';
|
||||||
import { createBrowserHistory } from 'history';
|
|
||||||
import { unstable_HistoryRouter as HistoryRouter } from 'react-router-dom';
|
|
||||||
import { ConfigProvider } from 'antd';
|
|
||||||
import { StyleProvider, legacyLogicalPropertiesTransformer } from '@ant-design/cssinjs';
|
|
||||||
import dayjs from 'dayjs';
|
|
||||||
import 'dayjs/locale/zh-cn';
|
|
||||||
import zhCN from 'antd/locale/zh_CN';
|
|
||||||
|
|
||||||
import {
|
|
||||||
ResponsiveProvider,
|
|
||||||
FeaturesProvider,
|
|
||||||
} from 'oak-frontend-base/es/platforms/web';
|
|
||||||
import './index.less';
|
import './index.less';
|
||||||
import App from './App';
|
|
||||||
import reportWebVitals from './reportWebVitals';
|
|
||||||
import { handler as exceptionHandler } from '@project/exceptionHandler';
|
import { handler as exceptionHandler } from '@project/exceptionHandler';
|
||||||
import initializeFeatures from '@project/initializeFeatures';
|
import initializeFeatures from '@project/initializeFeatures';
|
||||||
|
import initialize from 'oak-frontend-base/es/platforms/web/initialize';
|
||||||
|
import routers from './app/routers';
|
||||||
|
|
||||||
import { features } from './initialize';
|
import { features } from './initialize';
|
||||||
dayjs.locale('zh-cn');
|
|
||||||
|
|
||||||
window.addEventListener('unhandledrejection', async (event) => {
|
window.addEventListener('unhandledrejection', async (event) => {
|
||||||
// 全局捕获异常处理
|
// 全局捕获异常处理
|
||||||
|
|
@ -31,13 +17,6 @@ window.addEventListener('unhandledrejection', async (event) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(
|
|
||||||
document.getElementById('root') as HTMLElement
|
|
||||||
);
|
|
||||||
|
|
||||||
const history = createBrowserHistory();
|
|
||||||
features.navigator.setHistory(history);
|
|
||||||
|
|
||||||
const init = async () => {
|
const init = async () => {
|
||||||
let error;
|
let error;
|
||||||
try {
|
try {
|
||||||
|
|
@ -45,33 +24,8 @@ const init = async () => {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error = err;
|
error = err;
|
||||||
}
|
}
|
||||||
// 抓到异常处理 1、token过期 2、网络断了 3、接口请求失败
|
|
||||||
root.render(
|
|
||||||
<HistoryRouter history={history as any}>
|
|
||||||
<ResponsiveProvider>
|
|
||||||
<FeaturesProvider features={features as any}>
|
|
||||||
<ConfigProvider
|
|
||||||
locale={zhCN}
|
|
||||||
>
|
|
||||||
|
|
||||||
<StyleProvider
|
initialize<EntityDict>(features, 'oak_template', routers, undefined, error);
|
||||||
hashPriority="high"
|
|
||||||
transformers={[
|
|
||||||
legacyLogicalPropertiesTransformer,
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<App error={error} />
|
|
||||||
</StyleProvider>
|
|
||||||
</ConfigProvider>
|
|
||||||
</FeaturesProvider>
|
|
||||||
</ResponsiveProvider>
|
|
||||||
</HistoryRouter>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
init();
|
init();
|
||||||
|
|
||||||
// If you want to start measuring performance in your app, pass a function
|
|
||||||
// to log results (for example: reportWebVitals(console.log))
|
|
||||||
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
|
||||||
reportWebVitals();
|
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>
|
|
||||||
|
Before Width: | Height: | Size: 2.6 KiB |
|
|
@ -1,15 +0,0 @@
|
||||||
import { ReportHandler } from 'web-vitals';
|
|
||||||
|
|
||||||
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
|
|
||||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
|
||||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
|
||||||
getCLS(onPerfEntry);
|
|
||||||
getFID(onPerfEntry);
|
|
||||||
getFCP(onPerfEntry);
|
|
||||||
getLCP(onPerfEntry);
|
|
||||||
getTTFB(onPerfEntry);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default reportWebVitals;
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
|
||||||
// allows you to do things like:
|
|
||||||
// expect(element).toHaveTextContent(/react/i)
|
|
||||||
// learn more: https://github.com/testing-library/jest-dom
|
|
||||||
import '@testing-library/jest-dom';
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
import { BrowserRouterProps } from 'react-router-dom';
|
|
||||||
|
|
||||||
export interface IBrowserRouterProps extends BrowserRouterProps {
|
|
||||||
namespace: string;
|
|
||||||
oakDisablePulldownRefresh?: boolean;
|
|
||||||
navigationBarTitleText?: string;
|
|
||||||
customRouter?: boolean; //表示自定义路由
|
|
||||||
}
|
|
||||||
|
|
||||||
export type IMeta = {
|
|
||||||
navigationBarTitleText?: string;
|
|
||||||
Icon?: React.FC;
|
|
||||||
oakDisablePulldownRefresh?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IRouter {
|
|
||||||
path: string;
|
|
||||||
/**
|
|
||||||
* 是否为根路由
|
|
||||||
*/
|
|
||||||
isFirst?: boolean;
|
|
||||||
namespace?: string;
|
|
||||||
customRouter?: boolean; //表示自定义路由
|
|
||||||
Component?: React.FC<IBrowserRouterProps> | (() => any);
|
|
||||||
/**
|
|
||||||
* 当前路由是否全屏显示
|
|
||||||
*/
|
|
||||||
isFullPage?: boolean;
|
|
||||||
/**
|
|
||||||
* meta
|
|
||||||
*/
|
|
||||||
meta?: IMeta;
|
|
||||||
children?: IRouter[];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 文件path
|
|
||||||
type FilePath = string;
|
|
||||||
|
|
||||||
// 路由path 如果不传 就直接 文件Path当作路由path
|
|
||||||
type RoutePath = string | undefined | null;
|
|
||||||
|
|
||||||
// 设置根路由
|
|
||||||
type IsFirst = boolean;
|
|
||||||
|
|
||||||
// 项目路径别名
|
|
||||||
export type Project = '@project';
|
|
||||||
|
|
||||||
|
|
||||||
// [项目别名, 文件path,[嵌套路由顶层path],设置根路由, 路由path]
|
|
||||||
export type Page<T> = [Project, FilePath, Array<T>?, IsFirst?, RoutePath?];
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
export function getAppType() {
|
|
||||||
if (/MicroMessenger/i.test(window.navigator.userAgent)) {
|
|
||||||
return 'wechatPublic';
|
|
||||||
}
|
|
||||||
return 'web';
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
|
|
||||||
Object.assign(global, {
|
|
||||||
fetch: fetch,
|
|
||||||
getRandomValues: async function getRandomValues(length: number) {
|
|
||||||
if (length > 65536) {
|
|
||||||
throw new Error('Can only request a maximum of 65536 bytes');
|
|
||||||
}
|
|
||||||
const randomValues = window.crypto.getRandomValues(
|
|
||||||
new Uint8Array(length)
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
return new Uint8Array(randomValues);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { EntityDict } from '@project/oak-app-domain';
|
||||||
|
import { RuntimeContext } from './RuntimeContext';
|
||||||
|
|
||||||
|
export class BackendRuntimeContext extends BaseBackendRuntimeContext<EntityDict> implements RuntimeContext {
|
||||||
|
async toString() {
|
||||||
|
const data = await this.getSerializedData();
|
||||||
|
return JSON.stringify(data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BackendRuntimeContext;
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { EntityDict } from '@project/oak-app-domain';
|
||||||
|
import { RuntimeContext } from './RuntimeContext';
|
||||||
|
|
||||||
|
export class FrontendRuntimeContext extends BaseFrontendRuntimeContext<EntityDict> implements RuntimeContext {
|
||||||
|
async toString() {
|
||||||
|
const data = await this.getSerializedData();
|
||||||
|
return JSON.stringify(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FrontendRuntimeContext;
|
||||||
|
|
@ -29,8 +29,7 @@ export default function initialize() {
|
||||||
totalData = mergeConcatMany([data] as Array<typeof data>)!,
|
totalData = mergeConcatMany([data] as Array<typeof data>)!,
|
||||||
totalImportations = mergeConcatMany([importations] as Array<typeof importations>)!,
|
totalImportations = mergeConcatMany([importations] as Array<typeof importations>)!,
|
||||||
totalExportations = mergeConcatMany([exportations] as Array<typeof exportations>)!,
|
totalExportations = mergeConcatMany([exportations] as Array<typeof exportations>)!,
|
||||||
totalCommon = mergeConcatMany([common] as Array<typeof common>)!,
|
totalCommon = mergeConcatMany([common] as Array<typeof common>)!;
|
||||||
totalRender = mergeConcatMany([render] as Array<typeof render>)!;
|
|
||||||
|
|
||||||
const debugConnector = new DebugConnector<EntityDict, BackendRuntimeContext, FrontendRuntimeContext>(
|
const debugConnector = new DebugConnector<EntityDict, BackendRuntimeContext, FrontendRuntimeContext>(
|
||||||
storageSchema,
|
storageSchema,
|
||||||
|
|
@ -48,13 +47,14 @@ export default function initialize() {
|
||||||
);
|
);
|
||||||
|
|
||||||
const totalFeatures = {} as FeatureDict & BasicFeatures<EntityDict>;
|
const totalFeatures = {} as FeatureDict & BasicFeatures<EntityDict>;
|
||||||
|
// @ts-ignore
|
||||||
const { features } = initFrontend<EntityDict, BackendRuntimeContext, FrontendRuntimeContext>(
|
const { features } = initFrontend<EntityDict, BackendRuntimeContext, FrontendRuntimeContext>(
|
||||||
storageSchema,
|
storageSchema,
|
||||||
(store) => new FrontendRuntimeContext(store, totalFeatures),
|
(store) => new FrontendRuntimeContext(store, totalFeatures),
|
||||||
debugConnector,
|
debugConnector,
|
||||||
totalCheckers,
|
totalCheckers,
|
||||||
totalCommon,
|
totalCommon,
|
||||||
totalRender
|
render
|
||||||
);
|
);
|
||||||
|
|
||||||
Object.assign(totalFeatures, features);
|
Object.assign(totalFeatures, features);
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ export default function initialize() {
|
||||||
totalRender = mergeConcatMany([render] as Array<typeof render>)!;
|
totalRender = mergeConcatMany([render] as Array<typeof render>)!;
|
||||||
|
|
||||||
const totalFeatures = {} as FeatureDict & BasicFeatures<EntityDict>;
|
const totalFeatures = {} as FeatureDict & BasicFeatures<EntityDict>;
|
||||||
|
// @ts-ignore
|
||||||
const { features } = initFrontend<EntityDict, BackendRuntimeContext, FrontendRuntimeContext>(
|
const { features } = initFrontend<EntityDict, BackendRuntimeContext, FrontendRuntimeContext>(
|
||||||
storageSchema,
|
storageSchema,
|
||||||
(store) => new FrontendRuntimeContext(store, totalFeatures),
|
(store) => new FrontendRuntimeContext(store, totalFeatures),
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,11 @@ import { FeatureDict } from '@project/features';
|
||||||
import { BasicFeatures } from 'oak-frontend-base/es/features';
|
import { BasicFeatures } from 'oak-frontend-base/es/features';
|
||||||
import { EntityDict } from '@project/oak-app-domain';
|
import { EntityDict } from '@project/oak-app-domain';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize your features when app loads.
|
||||||
|
* If you use the oak-general-business, then remember to call the initial function in "oak-general-business/es/features/index".
|
||||||
|
* @param features
|
||||||
|
*/
|
||||||
export default async function initialize(features: FeatureDict & BasicFeatures<EntityDict>) {
|
export default async function initialize(features: FeatureDict & BasicFeatures<EntityDict>) {
|
||||||
// 项目自己的features如果有初始化写在这里
|
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue