更改了模板的文件结构,增加了polyfill.d.ts

This commit is contained in:
Xu Chang 2024-04-12 18:01:05 +08:00
parent bbc845b01c
commit 68e9e30cac
31 changed files with 352 additions and 243 deletions

View File

@ -68,6 +68,7 @@ async function build(cmd) {
} }
} }
else if (target === 'web') { else if (target === 'web') {
const port = cmd.port;
const webFileMap = { const webFileMap = {
production: 'build-web.js', production: 'build-web.js',
staging: 'build-staging-web.js', staging: 'build-staging-web.js',
@ -81,6 +82,7 @@ async function build(cmd) {
`COMPILE_ANALYZE=${!!cmd.analyze}`, `COMPILE_ANALYZE=${!!cmd.analyze}`,
`GENERATE_SOURCEMAP=${!!cmd.sourcemap}`, `GENERATE_SOURCEMAP=${!!cmd.sourcemap}`,
`PROD=${!!cmd.prod}`, `PROD=${!!cmd.prod}`,
`PORT=${port || 3000}`,
!!cmd.memoryLimit && `MEMORY_LIMIT=${cmd.memoryLimit}`, !!cmd.memoryLimit && `MEMORY_LIMIT=${cmd.memoryLimit}`,
`node`, `node`,
cmd.memoryLimit && `--max_old_space_size=${cmd.memoryLimit}`, cmd.memoryLimit && `--max_old_space_size=${cmd.memoryLimit}`,

View File

@ -66,6 +66,7 @@ commander_1.default
.option('-m, --mode <mode>', 'mode') .option('-m, --mode <mode>', 'mode')
.option('-d, --subDir <subDirName>', 'subDirName') .option('-d, --subDir <subDirName>', 'subDirName')
.option('-c, --check <level>', 'level') .option('-c, --check <level>', 'level')
.option('-p, --port <port>', 'port')
.description('build project of start on demand') .description('build project of start on demand')
.action(build_1.default); .action(build_1.default);
commander_1.default commander_1.default

View File

@ -6,20 +6,18 @@ const {
buildSchema, buildSchema,
analyzeEntities, analyzeEntities,
} = require(`${process.cwd()}/node_modules/oak-domain/lib/compiler/schemalBuilder`); } = require(`${process.cwd()}/node_modules/oak-domain/lib/compiler/schemalBuilder`);
const { analyzeDepedency } = require(`${process.cwd()}/node_modules/oak-domain/lib/compiler/dependencyBuilder`);
// todo这里还是很奇怪要把src/entites的依赖给去掉 // todo这里还是很奇怪要把src/entites的依赖给去掉
analyzeEntities(`${process.cwd()}/node_modules/oak-domain/src/entities`, 'oak-domain/lib/entities'); analyzeEntities(`${process.cwd()}/node_modules/oak-domain/src/entities`, 'oak-domain/lib/entities');
// 从config中读出相应依赖 // 分析项目依赖并从底向上构建所有的entity
const depConfigTsFile = join(process.cwd(), 'src', 'configuration', 'dependency.ts'); const depGraph = analyzeDepedency(process.cwd());
if (existsSync(depConfigTsFile)) { const dependencies = depGraph.ascOrder;
const result = ts.transpileModule(readFileSync(depConfigTsFile, 'utf-8'), { compilerOptions: { module: ts.ModuleKind.CommonJS } }); dependencies.forEach(
const dependencies = eval(result.outputText); (ele) => {
dependencies.forEach( analyzeEntities(`${process.cwd()}/node_modules/${ele}/src/entities`, `${ele}/lib/entities`);
(ele) => { }
analyzeEntities(`${process.cwd()}/node_modules/${ele}/src/entities`, `${ele}/lib/entities`); );
}
);
}
analyzeEntities(join(process.cwd(), 'src', 'entities')); analyzeEntities(join(process.cwd(), 'src', 'entities'));
removeSync(join(process.cwd(), 'src', 'oak-app-domain')); removeSync(join(process.cwd(), 'src', 'oak-app-domain'));

View File

@ -91,6 +91,7 @@ export default async function build(cmd: any) {
Error(`${error(`执行失败`)}`); Error(`${error(`执行失败`)}`);
} }
} else if (target === 'web') { } else if (target === 'web') {
const port = cmd.port;
const webFileMap = { const webFileMap = {
production: 'build-web.js', production: 'build-web.js',
staging: 'build-staging-web.js', staging: 'build-staging-web.js',
@ -106,6 +107,7 @@ export default async function build(cmd: any) {
`COMPILE_ANALYZE=${!!cmd.analyze}`, `COMPILE_ANALYZE=${!!cmd.analyze}`,
`GENERATE_SOURCEMAP=${!!cmd.sourcemap}`, `GENERATE_SOURCEMAP=${!!cmd.sourcemap}`,
`PROD=${!!cmd.prod}`, `PROD=${!!cmd.prod}`,
`PORT=${port || 3000}`,
!!cmd.memoryLimit && `MEMORY_LIMIT=${cmd.memoryLimit}`, !!cmd.memoryLimit && `MEMORY_LIMIT=${cmd.memoryLimit}`,
`node`, `node`,
cmd.memoryLimit && `--max_old_space_size=${cmd.memoryLimit}`, cmd.memoryLimit && `--max_old_space_size=${cmd.memoryLimit}`,

View File

@ -75,6 +75,7 @@ program
.option('-m, --mode <mode>', 'mode') .option('-m, --mode <mode>', 'mode')
.option('-d, --subDir <subDirName>', 'subDirName') .option('-d, --subDir <subDirName>', 'subDirName')
.option('-c, --check <level>', 'level') .option('-c, --check <level>', 'level')
.option('-p, --port <port>', 'port')
.description('build project of start on demand') .description('build project of start on demand')
.action(build); .action(build);
program program

2
template/.gitignore vendored
View File

@ -87,7 +87,7 @@ build
.pnp.* .pnp.*
package-lock.json package-lock.json
oak-app-domain src/oak-app-domain
*/public/locales */public/locales
# 如果不自动生成allRouters这里要注释掉 # 如果不自动生成allRouters这里要注释掉
web/src/**/allRouters.ts web/src/**/allRouters.ts

View File

@ -1,177 +0,0 @@
import { CreateOperationData as Application } from '@oak-app-domain/Application/Schema';
import { CreateOperationData as System } from '@oak-app-domain/System/Schema';
import { CreateOperationData as Platform } from '@oak-app-domain/Platform/Schema';
import { CreateOperationData as Domain } from '@oak-app-domain/Domain/Schema';
export const DEV_WECHATMP_APPLICATION_ID = 'MY_DEV_WECHATMP_APPLICATION_ID';
export const DEV_WEB_APPLICATION_ID = 'MY_DEV_WEB_APPLICATION_ID';
export const DEV_PUBLIC_APPLICATION_ID = 'MY_DEV_PUBLIC_APPLICATION_ID';
export const DEV_WECHATPUPLIC_APPLICATION_ID = 'MY_DEV_WECHATPUPLIC_APPLICATION_ID';
export const DEV_DOMAIN_ID = 'MY_DEV_DOMAIN_ID';
export const DEV_SYSTEM_ID = 'MY_DEV_SYSTEM_ID';
const SUPER_SYSTEM_ID = 'SUPER_SYSTEM_ID';
const SUPER_WEB_APPLICATION_ID = 'SUPER_WEB_APPLICATION_ID';
const SUPER_DOMAIN_ID = 'SUPER_DOMAIN_ID';
/**
*
*/
export const application: Application[] = [
{
id: DEV_WECHATMP_APPLICATION_ID,
name: 'wechatMp',
type: 'wechatMp',
systemId: DEV_SYSTEM_ID,
config: {
type: 'wechatMp',
appId: '',
appSecret: '',
},
description: '小程序应用指向dev_system',
},
{
id: DEV_WEB_APPLICATION_ID,
name: '测试web',
type: 'web',
systemId: DEV_SYSTEM_ID,
config: {
type: 'web',
passport: ['email', 'mobile', 'wechat'],
},
description: 'web应用指向dev_system',
},
{
id: DEV_PUBLIC_APPLICATION_ID,
name: '测试public',
type: 'wechatPublic',
systemId: DEV_SYSTEM_ID,
config: {
type: 'wechatPublic',
isService: true,
appId: 'wx850874243dbcb34a',
appSecret: '6fae672615730c0c1ea58e83621ad7c9',
},
description: 'public应用指向dev_system',
},
{
/** *
* 线web应用
* */
id: SUPER_WEB_APPLICATION_ID,
name: '线上',
type: 'web',
systemId: SUPER_SYSTEM_ID,
config: {
type: 'web',
passport: ['mobile', 'wechat'],
},
description: '线上网站',
},
];
export const system: System[] = [
{
// 测试用系统,可将自己申请相应的服务帐号填在这里,用于开发过程
id: DEV_SYSTEM_ID,
name: '测试系统',
description: '测试系统',
config: {
Map: {
amap: {
webApiKey: '',
},
},
Cos: {
qiniu: {
accessKey: '',
buckets: [
{
zone: 'z0',
name: '',
domain: '',
protocol: 'http',
},
],
defaultBucket: '',
},
},
Live: {
qiniu: {
accessKey: '',
liveHost: '', // 七牛直播云接口
publishDomain: '', // rtmp
playDomain: '', // rtmp
hub: '',
publishKey: '',
playKey: '',
playBackDomain: '',
},
},
Account: {
qiniu: [
{
accessKey: '',
secretKey: '',
},
],
},
App: {},
},
super: true,
folder: 'test',
style: {
color: {
primary: 'red',
},
},
},
{
/**
* 线线
* */
id: SUPER_SYSTEM_ID,
name: '线上系统',
description: '线上系统',
config: {
App: {},
},
super: true,
folder: 'test',
style: {
color: {
primary: 'red',
},
},
},
];
export const domain: Domain[] = [
{
id: DEV_DOMAIN_ID,
protocol: 'http',
url: 'localhost',
port: 3001,
systemId: DEV_SYSTEM_ID,
},
{
/**
* 线访
* */
id: SUPER_DOMAIN_ID,
protocol: 'https',
url: 'test.com',
port: 443,
apiPath: '/rest/aspect',
systemId: SUPER_SYSTEM_ID,
},
];
export const data = {
application,
system,
domain,
};

View File

@ -2,7 +2,7 @@ import { EntityDict } from '@project/oak-app-domain';
import { RuntimeContext } from './RuntimeContext'; import { RuntimeContext } from './RuntimeContext';
import { BackendRuntimeContext as DependentBackendRuntimeContext } from './DependentContext'; import { BackendRuntimeContext as DependentBackendRuntimeContext } from './DependentContext';
export class BackendRuntimeContext extends DependentFrontendRuntimeContext<EntityDict> implements RuntimeContext { export class BackendRuntimeContext extends DependentBackendRuntimeContext<EntityDict> implements RuntimeContext {
async toString() { async toString() {
const data = await this.getSerializedData(); const data = await this.getSerializedData();
return JSON.stringify(data); return JSON.stringify(data);

View File

@ -1,19 +1,13 @@
import { application, system, domain } from '../config/data';
import { relations } from '@project/oak-app-domain/Relation'; import { relations } from '@project/oak-app-domain/Relation';
import actionAuth from './actionAuth'; import actionAuth from './actionAuth';
import relationAuth from './relationAuth'; import relationAuth from './relationAuth';
import path from './path'; import path from './path';
import i18n from './i18n'; import i18n from './i18n';
const data = { export default {
application,
system,
domain,
relation: relations, relation: relations,
actionAuth, actionAuth,
relationAuth, relationAuth,
path, path,
i18n, i18n,
}; };
export default data;

View File

@ -1,6 +1,5 @@
/// <reference path="./polyfill.d.ts" /> /// <reference path="../../templateFiles/polyfill.d.ts" />
declare global { declare global {
const XC: number;
} }

View File

@ -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>oak template project</title> <title></title>
</head> </head>
<body> <body>

View File

@ -4,15 +4,14 @@ import './App.less';
import AppContainer from './AppContainer'; import AppContainer from './AppContainer';
import AppRouter from './AppRouter'; import AppRouter from './AppRouter';
import AppError from './AppError'; import AppError from './AppError';
import { Folder } from './routers';
function App(props: { folder: Folder; error?: any }) { function App(props: { error?: any }) {
if (props.error) { if (props.error) {
return <AppError error={props.error} />; return <AppError error={props.error} />;
} }
return ( return (
<AppContainer> <AppContainer>
<AppRouter folder={props.folder} /> <AppRouter />
</AppContainer> </AppContainer>
); );
} }

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { Routes, Route, Outlet, useLocation } from 'react-router-dom'; import { Routes, Route, Outlet, useLocation } from 'react-router-dom';
import { IRouter, IBrowserRouterProps, IMeta } from './types/router'; import { IRouter, IBrowserRouterProps, IMeta } from './types/router';
import routers, { Folder } from './routers'; import routers from './app/routers';
import Loading from './components/Loading'; import Loading from './components/Loading';
import useFeatures from '@project/hooks/useFeatures'; import useFeatures from '@project/hooks/useFeatures';
@ -92,8 +92,8 @@ const Guard = React.memo(
} }
); );
const AppRouter = React.memo((props: { folder: Folder }) => ( const AppRouter = React.memo(() => (
<Routes>{renderRoutes(routers[props.folder])}</Routes> <Routes>{renderRoutes(routers)}</Routes>
)); ));
export default AppRouter; export default AppRouter;

View File

@ -0,0 +1,51 @@
export default OakComponent({
isList: false,
formData({ features }) {
const loggedIn = !!features.token.getUserId(true);
const user = loggedIn && features.token.getUserInfo()!;
const { extraFile$entity } = user || {};
const [extraFile] = extraFile$entity || [];
const avatar = extraFile && features.extraFile.getUrl(extraFile);
const mode = features.console.getMode();
let modeTitle = '' as string | undefined;
if (mode === 'supplier') {
const supplierId = features.console.getSupplierId();
const [supplier] = features.cache.get('supplier', {
data: {
id: 1,
name: 1,
},
filter: {
id: supplierId,
}
});
modeTitle = supplier?.name;
}
else if (mode === 'systemProvider') {
const spId = features.console.getSystemProviderId();
const [systemProvider] = features.cache.get('systemProvider', {
data: {
id: 1,
name: 1,
},
filter: {
id: spId,
}
});
modeTitle = systemProvider?.name;
}
return {
loggedIn,
avatar,
modeTitle,
};
},
features: ['token', 'console'],
methods: {
logout() {
return this.features.token.logout();
}
},
})

View File

@ -0,0 +1 @@
{"name":"Oak模板项目", "close": "关闭"}

View File

@ -0,0 +1,18 @@
.header {
height: 64px;
padding: 4px;
width: 100%;
background-color: white;
display: flex;
justify-content: space-between;
align-items: center;
.home {
font-size: 22px;
font-weight: bold;
}
.right{
margin-right: 16px;
}
}

View File

@ -0,0 +1,60 @@
import React, { useState } from 'react';
import { EntityDict } from '@project/oak-app-domain';
import { WebComponentProps } from 'oak-frontend-base';
import { useLocation } from 'react-router-dom';
import { Row, Modal, Button } from 'antd';
import { DownOutlined, UserOutlined } from '@ant-design/icons';
import Styles from './web.pc.module.less';
export default function Render(props: WebComponentProps<
EntityDict,
'user',
false,
{
loggedIn: boolean;
avatar?: string;
modeTitle?: string;
},
{
}
>) {
const { modeTitle, avatar } = props.data;
const { t, navigateTo } = props.methods;
const location = useLocation();
const [open, setOpen] = useState(false);
return (
<Row className={Styles.header}>
<Button
type="text"
size="large"
onClick={() => {
navigateTo(
{
url: '/',
},
undefined,
true
);
}}
>
<div className={Styles.home}>
{t('name')}
</div>
</Button>
<div className={Styles.right}>
<Button
icon={<DownOutlined />}
onClick={() => {
setOpen(true);
}}
style={{
marginRight: 8,
}}
>
{modeTitle || t('modeUnset')}
</Button>
</div>
</Row>
);
}

View File

@ -0,0 +1,5 @@
import React from 'react';
export default function Render() {
return null;
}

View File

@ -0,0 +1,108 @@
import React, { useEffect, useState } from 'react';
import { Routes, Route, Outlet, useLocation } from 'react-router-dom';
import Header from '../../components/consoleHeader';
import Styles from './web.pc.module.less';
import useFeatures from '@project/hooks/useFeatures';
import Menu from '@project/components/console/menu';
import { useWidth } from 'oak-frontend-base/es/platforms/web';
function Console(props: { namespace: string }) {
const { namespace } = props;
const features = useFeatures();
const [currentMenuPath, setCmp] = useState('');
const [title, setTitle] = useState('');
const width = useWidth();
const location = useLocation();
const { pathname } = location;
const path = pathname.slice(namespace.length);
const showBack = currentMenuPath && currentMenuPath !== path;
const isRoot = features.token.isRoot();
// const mode = features.console.getMode(true);
const [mode, setMode] = useState(features.console.getMode(true));
const getTitleFromLocale = () => {
const location = window.location;
const { pathname } = location;
const path = pathname.slice(namespace.length);
const i18nNs = `taicang-p-console-${path.startsWith('/') ? path.slice(1).replaceAll(/\//g, '-') : path.replaceAll(/\//g, '-')}`;
const pageTitle = features.locales.t('pageTitle', {
'#oakNamespace': i18nNs,
});
return pageTitle === 'pageTitle' ? '' : pageTitle;
}
useEffect(() => {
features.navigator.setNamespace(namespace);
features.console.initialize();
const location = window.location;
const { pathname } = location;
const path2 = pathname.slice(namespace.length);
setCmp(path2);
const tt = getTitleFromLocale();
setTitle(tt);
const unsubscribe = features.locales.subscribe(
() => {
const tt = getTitleFromLocale();
setTitle(tt);
}
);
const unModeSubscribe = features.console.subscribe(
() => {
const newMode = features.console.getMode();
setMode(newMode);
}
)
return () => {
unsubscribe();
unModeSubscribe();
features.console.destroy();
};
}, []);
useEffect(() => {
const tt = getTitleFromLocale();
setTitle(tt);
}, [path]);
// const showOpenStoreBtn = !isRoot && !mode && pathname === '/console';
const [showOpenStore, setShowOpenStore] = useState(true);
useEffect(() => {
if (mode || isRoot) {
setShowOpenStore(false);
} else {
setShowOpenStore(true);
}
}, [mode, isRoot])
return (
<div className={Styles.container}>
<div className={Styles.header}>
<Header />
</div>
<div className={Styles.body}>
<Menu
pathname={path}
onPathClicked={(path) => setCmp(path)}
/>
<div className={Styles.content}>
{/* <PageHeader contentMargin={width !== 'xs'} showBack={!!showBack} title={title}> */}
{showOpenStore ? (
<div className={Styles.nullRelContainer}>
<span className={Styles.openStore} onClick={() => {
features.navigator.navigateTo({
url: '/spContractApplyment/upsert'
}, {}, true)
}}>
</span>
</div>
) : <Outlet />}
{/* </PageHeader> */}
</div>
</div>
</div>
);
}
export default Console;

View File

@ -0,0 +1,40 @@
.container {
width: 100%;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: stretch;
background-color: var(--oak-bg-color-page);
.header {
margin-bottom: 2px;
position: sticky;
z-index: 1000;
top: 0px;
}
.body {
flex: 1;
display: flex;
flex-direction: row;
// align-items: flex-start;
.content {
// align-self: stretch;
margin-left: 1px;
flex: 1;
}
}
}
.nullRelContainer {
display: flex;
flex: 1;
align-items: center;
justify-content: center;
}
.openStore {
cursor: pointer;
color: var(--oak-color-primary-light);
}

View File

@ -1,5 +1,5 @@
"use oak router builder"; // 这行备注说明是用oak的router builder来生成allRouters "use oak router builder"; // 这行备注说明是用oak的router builder来生成allRouters
import React from 'react'; import React from 'react';
import { IRouter } from '../../../types/router'; import { IRouter } from '../../types/router';
let allRouters: IRouter[] = []; let allRouters: IRouter[] = [];
export default allRouters; export default allRouters;

View File

@ -1,13 +0,0 @@
import React from 'react';
import { useNavigate, useParams, useSearchParams, useLocation } from 'react-router-dom';
import { Routes, Route, Outlet } from 'react-router-dom';
function Console() {
return (
<Outlet />
);
}
export default Console;

View File

@ -45,10 +45,6 @@ const init = async () => {
} catch (err) { } catch (err) {
error = err; error = err;
} }
const application = features.application.getApplication();
const folder = application?.system?.folder;
// 抓到异常处理 1、token过期 2、网络断了 3、接口请求失败 // 抓到异常处理 1、token过期 2、网络断了 3、接口请求失败
root.render( root.render(
<HistoryRouter history={history as any}> <HistoryRouter history={history as any}>
@ -64,7 +60,7 @@ const init = async () => {
legacyLogicalPropertiesTransformer, legacyLogicalPropertiesTransformer,
]} ]}
> >
<App folder={folder as any} error={error} /> <App error={error} />
</StyleProvider> </StyleProvider>
</ConfigProvider> </ConfigProvider>
</FeaturesProvider> </FeaturesProvider>

View File

@ -0,0 +1,4 @@
{
"theme": {
}
}

View File

@ -1,12 +0,0 @@
import { IRouter } from './types/router';
export type Folder = 'test';
export type Routers = Record<Folder, IRouter[]>;
import testAllRouters from './app/test/router';
export const routers: Routers = {
test: testAllRouters,
};
export default routers;

View File

@ -0,0 +1,36 @@
import {
String,
Text,
Price
} from 'oak-domain/lib/types/DataType';
import { EntityShape } from 'oak-domain/lib/types/Entity';
import { Schema as System } from 'oak-general-business/lib/entities/System';
import { Schema as ArticleMenu } from 'oak-general-business/lib/entities/ArticleMenu';
import { EntityDesc } from 'oak-domain/lib/types';
export interface Schema extends EntityShape {
name: String<32>;
system: System;
systems?: System[];
articleMenus?: ArticleMenu[];
};
export type Relation = 'owner' | 'manager';
const entityDesc: EntityDesc<Schema, '', Relation> = {
locales: {
zh_CN: {
name: '系统供应商',
attr: {
name: '名称',
system: '系统',
systems: '系统',
articleMenus: '文章分类',
},
r: {
owner: '所有者',
manager: '管理员',
},
},
}
}

View File

@ -1,5 +1,5 @@
"use oak router builder"; // 这行备注说明是用oak的router builder来生成allRouters "use oak router builder"; // 这行备注说明是用oak的router builder来生成allRouters
import React from 'react'; import React from 'react';
import { IRouter } from '../../../types/router'; import { IRouter } from '../../types/router';
let allRouters: IRouter[] = []; let allRouters: IRouter[] = [];
export default allRouters; export default allRouters;

View File

@ -1,7 +1,4 @@
import { GenerateIdOption } from '@project/utils/polyfill'; import { MakeOakComponent } from 'oak-frontend-base';
import {
MakeOakComponent,
} from 'oak-general-business';
import { EntityDict } from '@oak-app-domain'; import { EntityDict } from '@oak-app-domain';
import { AspectDict } from '@project/aspects/AspectDict'; import { AspectDict } from '@project/aspects/AspectDict';
import { FeatureDict } from '@project/features'; import { FeatureDict } from '@project/features';
@ -16,7 +13,6 @@ declare global {
AspectDict, AspectDict,
FeatureDict FeatureDict
>; >;
const Aliplayer: any;
const features: FeatureDict; const features: FeatureDict;
} }
export {}; export {};