mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-01-26 12:18:21 +00:00
feat: add a new built-in solution icejs3 which is corresponding to the latest icejs framework
This commit is contained in:
parent
a6e768f8e9
commit
b607b246d6
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@alilc/lowcode-code-generator",
|
||||
"version": "1.0.8",
|
||||
"version": "1.1.0",
|
||||
"description": "出码引擎 for LowCode Engine",
|
||||
"license": "MIT",
|
||||
"main": "lib/index.js",
|
||||
|
||||
@ -9,6 +9,7 @@ import { createModuleBuilder } from './generator/ModuleBuilder';
|
||||
import { createDiskPublisher } from './publisher/disk';
|
||||
import { createZipPublisher } from './publisher/zip';
|
||||
import createIceJsProjectBuilder, { plugins as reactPlugins } from './solutions/icejs';
|
||||
import createIce3JsProjectBuilder, { plugins as icejs3Plugins } from './solutions/icejs3';
|
||||
import createRaxAppProjectBuilder, { plugins as raxPlugins } from './solutions/rax-app';
|
||||
|
||||
// 引入说明
|
||||
@ -32,6 +33,7 @@ import * as CONSTANTS from './const';
|
||||
|
||||
// 引入内置解决方案模块
|
||||
import icejs from './plugins/project/framework/icejs';
|
||||
import icejs3 from './plugins/project/framework/icejs3';
|
||||
import rax from './plugins/project/framework/rax';
|
||||
|
||||
export default {
|
||||
@ -39,10 +41,12 @@ export default {
|
||||
createModuleBuilder,
|
||||
solutions: {
|
||||
icejs: createIceJsProjectBuilder,
|
||||
icejs3: createIce3JsProjectBuilder,
|
||||
rax: createRaxAppProjectBuilder,
|
||||
},
|
||||
solutionParts: {
|
||||
icejs,
|
||||
icejs3,
|
||||
rax,
|
||||
},
|
||||
publishers: {
|
||||
@ -74,6 +78,9 @@ export default {
|
||||
i18n,
|
||||
utils,
|
||||
},
|
||||
icejs3: {
|
||||
...icejs3Plugins,
|
||||
},
|
||||
},
|
||||
postprocessor: {
|
||||
prettier,
|
||||
|
||||
@ -16,7 +16,7 @@ import {
|
||||
IWithDependency,
|
||||
} from '../../types';
|
||||
|
||||
import { isValidIdentifier, isValidComponentName } from '../../utils/validate';
|
||||
import { isValidIdentifier } from '../../utils/validate';
|
||||
|
||||
// TODO: main 这个信息到底怎么用,是不是外部包不需要使用?
|
||||
const DEP_MAIN_BLOCKLIST = ['lib', 'lib/index', 'es', 'es/index', 'main'];
|
||||
@ -261,7 +261,7 @@ function buildPackageImport(
|
||||
if (!isValidIdentifier(name)) {
|
||||
throw new CodeGeneratorError(`Invalid Identifier [${name}]`);
|
||||
}
|
||||
if (info.nodeIdentifier && !isValidComponentName(info.nodeIdentifier)) {
|
||||
if (info.nodeIdentifier && !isValidIdentifier(info.nodeIdentifier)) {
|
||||
throw new CodeGeneratorError(`Invalid Identifier [${info.nodeIdentifier}]`);
|
||||
}
|
||||
});
|
||||
|
||||
@ -0,0 +1,17 @@
|
||||
import template from './template';
|
||||
import globalStyle from './plugins/globalStyle';
|
||||
import packageJSON from './plugins/packageJSON';
|
||||
import layout from './plugins/layout';
|
||||
import appConfig from './plugins/appConfig';
|
||||
import buildConfig from './plugins/buildConfig';
|
||||
|
||||
export default {
|
||||
template,
|
||||
plugins: {
|
||||
appConfig,
|
||||
buildConfig,
|
||||
globalStyle,
|
||||
packageJSON,
|
||||
layout,
|
||||
},
|
||||
};
|
||||
@ -0,0 +1,50 @@
|
||||
import {
|
||||
BuilderComponentPlugin,
|
||||
BuilderComponentPluginFactory,
|
||||
ChunkType,
|
||||
FileType,
|
||||
ICodeStruct,
|
||||
} from '../../../../../types';
|
||||
import { COMMON_CHUNK_NAME } from '../../../../../const/generator';
|
||||
|
||||
export interface AppConfigPluginConfig {
|
||||
|
||||
}
|
||||
|
||||
function getContent() {
|
||||
return `import { defineAppConfig } from 'ice';
|
||||
|
||||
// App config, see https://v3.ice.work/docs/guide/basic/app
|
||||
export default defineAppConfig(() => ({
|
||||
// Set your configs here.
|
||||
app: {
|
||||
rootId: 'App',
|
||||
},
|
||||
router: {
|
||||
type: 'browser',
|
||||
basename: '/',
|
||||
},
|
||||
}));`;
|
||||
}
|
||||
|
||||
const pluginFactory: BuilderComponentPluginFactory<AppConfigPluginConfig> = () => {
|
||||
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
|
||||
const next: ICodeStruct = {
|
||||
...pre,
|
||||
};
|
||||
|
||||
next.chunks.push({
|
||||
type: ChunkType.STRING,
|
||||
fileType: FileType.TS,
|
||||
name: COMMON_CHUNK_NAME.FileMainContent,
|
||||
content: getContent(),
|
||||
linkAfter: [],
|
||||
});
|
||||
|
||||
return next;
|
||||
};
|
||||
|
||||
return plugin;
|
||||
};
|
||||
|
||||
export default pluginFactory;
|
||||
@ -0,0 +1,165 @@
|
||||
import {
|
||||
BuilderComponentPlugin,
|
||||
BuilderComponentPluginFactory,
|
||||
ChunkType,
|
||||
FileType,
|
||||
ICodeStruct,
|
||||
} from '../../../../../types';
|
||||
import { COMMON_CHUNK_NAME } from '../../../../../const/generator';
|
||||
import { format } from '../../../../../utils/format';
|
||||
import { getThemeInfo } from '../../../../../utils/theme';
|
||||
|
||||
export interface BuildConfigPluginConfig {
|
||||
|
||||
/** 包名 */
|
||||
themePackage?: string;
|
||||
}
|
||||
|
||||
function getContent(cfg?: BuildConfigPluginConfig, routesContent?: string) {
|
||||
return `
|
||||
import { join } from 'path';
|
||||
import { defineConfig } from '@ice/app';
|
||||
import _ from 'lodash';
|
||||
import fusion from '@ice/plugin-fusion';
|
||||
import locales from '@ice/plugin-moment-locales';
|
||||
import type { Plugin } from '@ice/app/esm/types';
|
||||
|
||||
interface PluginOptions {
|
||||
id: string;
|
||||
}
|
||||
|
||||
const plugin: Plugin<PluginOptions> = (options) => ({
|
||||
// name 可选,插件名称
|
||||
name: 'plugin-name',
|
||||
// setup 必选,用于定制工程构建配置
|
||||
setup: ({ onGetConfig, modifyUserConfig }) => {
|
||||
modifyUserConfig('codeSplitting', 'page');
|
||||
|
||||
onGetConfig((config) => {
|
||||
config.entry = {
|
||||
web: join(process.cwd(), '.ice/entry.client.tsx'),
|
||||
};
|
||||
|
||||
config.cssFilename = '[name].css';
|
||||
|
||||
config.configureWebpack = config.configureWebpack || [];
|
||||
config.configureWebpack?.push((webpackConfig) => {
|
||||
if (webpackConfig.output) {
|
||||
webpackConfig.output.filename = '[name].js';
|
||||
webpackConfig.output.chunkFilename = '[name].js';
|
||||
}
|
||||
return webpackConfig;
|
||||
});
|
||||
|
||||
config.swcOptions = _.merge(config.swcOptions, {
|
||||
compilationConfig: {
|
||||
jsc: {
|
||||
transform: {
|
||||
react: {
|
||||
runtime: 'classic',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
// 解决 webpack publicPath 问题
|
||||
config.transforms = config.transforms || [];
|
||||
config.transforms.push((source: string, id: string) => {
|
||||
if (id.includes('.ice/entry.client.tsx')) {
|
||||
let code = \`
|
||||
if (!__webpack_public_path__?.startsWith('http') && document.currentScript) {
|
||||
// @ts-ignore
|
||||
__webpack_public_path__ = document.currentScript.src.replace(/^(.*\\\\/)[^/]+$/, '$1');
|
||||
window.__ICE_ASSETS_MANIFEST__ = window.__ICE_ASSETS_MANIFEST__ || {};
|
||||
window.__ICE_ASSETS_MANIFEST__.publicPath = __webpack_public_path__;
|
||||
}
|
||||
\`;
|
||||
code += source;
|
||||
return { code };
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
// The project config, see https://v3.ice.work/docs/guide/basic/config
|
||||
const minify = process.env.NODE_ENV === 'production' ? 'swc' : false;
|
||||
export default defineConfig(() => ({
|
||||
ssr: false,
|
||||
ssg: false,
|
||||
minify,
|
||||
${routesContent}
|
||||
externals: {
|
||||
react: 'React',
|
||||
'react-dom': 'ReactDOM',
|
||||
'react-dom/client': 'ReactDOM',
|
||||
'@alifd/next': 'Next',
|
||||
lodash: 'var window._',
|
||||
'@alilc/lowcode-engine': 'var window.AliLowCodeEngine',
|
||||
},
|
||||
plugins: [
|
||||
fusion(${cfg?.themePackage ? `{
|
||||
importStyle: 'sass',
|
||||
themePackage: '${getThemeInfo(cfg.themePackage).name}',
|
||||
}` : `{
|
||||
importStyle: true,
|
||||
}`}),
|
||||
locales(),
|
||||
plugin(),
|
||||
]
|
||||
}));
|
||||
`;
|
||||
}
|
||||
|
||||
function getRoutesContent(navData: any, needShell = true) {
|
||||
const routes = [
|
||||
'routes: {',
|
||||
' defineRoutes: route => {',
|
||||
];
|
||||
function _getRoutes(nav: any, _routes: string[] = []) {
|
||||
const { slug, children } = nav;
|
||||
if (children && children.length > 0) {
|
||||
children.forEach((_nav: any) => _getRoutes(_nav, _routes));
|
||||
} else if (slug) {
|
||||
_routes.push(`route('/${slug}', '${slug}/index.jsx');`);
|
||||
}
|
||||
}
|
||||
if (needShell) {
|
||||
routes.push(" route('/', 'layout.jsx', () => {");
|
||||
}
|
||||
navData?.forEach((nav: any) => {
|
||||
_getRoutes(nav, routes);
|
||||
});
|
||||
if (needShell) {
|
||||
routes.push(' });');
|
||||
}
|
||||
routes.push(' }'); // end of defineRoutes
|
||||
routes.push(' },'); // end of routes
|
||||
return routes.join('\n');
|
||||
}
|
||||
|
||||
const pluginFactory: BuilderComponentPluginFactory<BuildConfigPluginConfig> = (cfg?) => {
|
||||
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
|
||||
const next: ICodeStruct = {
|
||||
...pre,
|
||||
};
|
||||
|
||||
const { navConfig } = next.contextData;
|
||||
const routesContent = navConfig?.data ? getRoutesContent(navConfig.data, true) : '';
|
||||
|
||||
next.chunks.push({
|
||||
type: ChunkType.STRING,
|
||||
fileType: FileType.MTS,
|
||||
name: COMMON_CHUNK_NAME.FileMainContent,
|
||||
content: format(getContent(cfg, routesContent)),
|
||||
linkAfter: [],
|
||||
});
|
||||
|
||||
return next;
|
||||
};
|
||||
|
||||
return plugin;
|
||||
};
|
||||
|
||||
export default pluginFactory;
|
||||
@ -0,0 +1,56 @@
|
||||
import { COMMON_CHUNK_NAME } from '../../../../../const/generator';
|
||||
|
||||
import {
|
||||
BuilderComponentPlugin,
|
||||
BuilderComponentPluginFactory,
|
||||
ChunkType,
|
||||
FileType,
|
||||
ICodeStruct,
|
||||
IProjectInfo,
|
||||
} from '../../../../../types';
|
||||
|
||||
const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
|
||||
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
|
||||
const next: ICodeStruct = {
|
||||
...pre,
|
||||
};
|
||||
|
||||
const ir = next.ir as IProjectInfo;
|
||||
|
||||
next.chunks.push({
|
||||
type: ChunkType.STRING,
|
||||
fileType: FileType.SCSS,
|
||||
name: COMMON_CHUNK_NAME.StyleDepsImport,
|
||||
content: `
|
||||
// 引入默认全局样式
|
||||
@import '@alifd/next/reset.scss';
|
||||
`,
|
||||
linkAfter: [],
|
||||
});
|
||||
|
||||
next.chunks.push({
|
||||
type: ChunkType.STRING,
|
||||
fileType: FileType.SCSS,
|
||||
name: COMMON_CHUNK_NAME.StyleCssContent,
|
||||
content: `
|
||||
body {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
`,
|
||||
linkAfter: [COMMON_CHUNK_NAME.StyleDepsImport],
|
||||
});
|
||||
|
||||
next.chunks.push({
|
||||
type: ChunkType.STRING,
|
||||
fileType: FileType.SCSS,
|
||||
name: COMMON_CHUNK_NAME.StyleCssContent,
|
||||
content: ir.css || '',
|
||||
linkAfter: [COMMON_CHUNK_NAME.StyleDepsImport],
|
||||
});
|
||||
|
||||
return next;
|
||||
};
|
||||
return plugin;
|
||||
};
|
||||
|
||||
export default pluginFactory;
|
||||
@ -0,0 +1,41 @@
|
||||
import {
|
||||
BuilderComponentPlugin,
|
||||
BuilderComponentPluginFactory,
|
||||
ChunkType,
|
||||
FileType,
|
||||
ICodeStruct,
|
||||
} from '../../../../../types';
|
||||
import { COMMON_CHUNK_NAME } from '../../../../../const/generator';
|
||||
|
||||
const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
|
||||
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
|
||||
const next: ICodeStruct = {
|
||||
...pre,
|
||||
};
|
||||
|
||||
next.chunks.push({
|
||||
type: ChunkType.STRING,
|
||||
fileType: FileType.JSX,
|
||||
name: COMMON_CHUNK_NAME.FileMainContent,
|
||||
content: `
|
||||
import { Outlet } from 'ice';
|
||||
import BasicLayout from '@/layouts/BasicLayout';
|
||||
|
||||
export default function Layout() {
|
||||
return (
|
||||
<BasicLayout>
|
||||
<Outlet />
|
||||
</BasicLayout>
|
||||
);;
|
||||
}
|
||||
`,
|
||||
linkAfter: [],
|
||||
});
|
||||
|
||||
return next;
|
||||
};
|
||||
|
||||
return plugin;
|
||||
};
|
||||
|
||||
export default pluginFactory;
|
||||
@ -0,0 +1,119 @@
|
||||
import { PackageJSON } from '@alilc/lowcode-types';
|
||||
|
||||
import { COMMON_CHUNK_NAME } from '../../../../../const/generator';
|
||||
|
||||
import {
|
||||
BuilderComponentPlugin,
|
||||
BuilderComponentPluginFactory,
|
||||
ChunkType,
|
||||
FileType,
|
||||
ICodeStruct,
|
||||
IProjectInfo,
|
||||
} from '../../../../../types';
|
||||
import { buildDataSourceDependencies } from '../../../../../utils/dataSource';
|
||||
|
||||
interface IIceJs3PackageJSON extends PackageJSON {
|
||||
originTemplate: string;
|
||||
}
|
||||
|
||||
export type IceJs3PackageJsonPluginConfig = {
|
||||
|
||||
/**
|
||||
* 数据源配置
|
||||
*/
|
||||
datasourceConfig?: {
|
||||
|
||||
/** 数据源引擎的版本 */
|
||||
engineVersion?: string;
|
||||
|
||||
/** 数据源引擎的包名 */
|
||||
enginePackage?: string;
|
||||
|
||||
/** 数据源 handlers 的版本 */
|
||||
handlersVersion?: {
|
||||
[key: string]: string;
|
||||
};
|
||||
|
||||
/** 数据源 handlers 的包名 */
|
||||
handlersPackages?: {
|
||||
[key: string]: string;
|
||||
};
|
||||
};
|
||||
|
||||
/** 包名 */
|
||||
packageName?: string;
|
||||
|
||||
/** 版本 */
|
||||
packageVersion?: string;
|
||||
};
|
||||
|
||||
const pluginFactory: BuilderComponentPluginFactory<IceJs3PackageJsonPluginConfig> = (cfg) => {
|
||||
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
|
||||
const next: ICodeStruct = {
|
||||
...pre,
|
||||
};
|
||||
|
||||
const ir = next.ir as IProjectInfo;
|
||||
|
||||
const packageJson: IIceJs3PackageJSON = {
|
||||
name: cfg?.packageName || 'icejs3-demo-app',
|
||||
version: cfg?.packageVersion || '0.1.5',
|
||||
description: 'icejs 3 轻量级模板,使用 JavaScript,仅包含基础的 Layout。',
|
||||
dependencies: {
|
||||
moment: '^2.24.0',
|
||||
react: '^18.2.0',
|
||||
'react-dom': '^18.2.0',
|
||||
'react-router': '^6.9.0',
|
||||
'react-router-dom': '^6.9.0',
|
||||
'intl-messageformat': '^9.3.6',
|
||||
'@alifd/next': '1.26.15',
|
||||
'@ice/runtime': '^1.0.0',
|
||||
// 数据源相关的依赖:
|
||||
...buildDataSourceDependencies(ir, cfg?.datasourceConfig),
|
||||
},
|
||||
devDependencies: {
|
||||
'@ice/app': '^3.0.0',
|
||||
'@types/react': '^18.0.0',
|
||||
'@types/react-dom': '^18.0.0',
|
||||
'@types/node': '^18.11.17',
|
||||
'@ice/plugin-fusion': '^1.0.1',
|
||||
'@ice/plugin-moment-locales': '^1.0.0',
|
||||
eslint: '^6.0.1',
|
||||
stylelint: '^13.2.0',
|
||||
},
|
||||
scripts: {
|
||||
start: 'ice start',
|
||||
build: 'ice build',
|
||||
lint: 'npm run eslint && npm run stylelint',
|
||||
eslint: 'eslint --cache --ext .js,.jsx ./',
|
||||
stylelint: 'stylelint ./**/*.scss',
|
||||
},
|
||||
engines: {
|
||||
node: '>=14.0.0',
|
||||
},
|
||||
repository: {
|
||||
type: 'git',
|
||||
url: 'http://gitlab.xxx.com/msd/leak-scan/tree/master',
|
||||
},
|
||||
private: true,
|
||||
originTemplate: '@alifd/scaffold-lite-js',
|
||||
};
|
||||
|
||||
ir.packages.forEach((packageInfo) => {
|
||||
packageJson.dependencies[packageInfo.package] = packageInfo.version;
|
||||
});
|
||||
|
||||
next.chunks.push({
|
||||
type: ChunkType.JSON,
|
||||
fileType: FileType.JSON,
|
||||
name: COMMON_CHUNK_NAME.FileMainContent,
|
||||
content: packageJson,
|
||||
linkAfter: [],
|
||||
});
|
||||
|
||||
return next;
|
||||
};
|
||||
return plugin;
|
||||
};
|
||||
|
||||
export default pluginFactory;
|
||||
@ -0,0 +1,12 @@
|
||||
import { ResultFile } from '@alilc/lowcode-types';
|
||||
import { createResultFile } from '../../../../../../utils/resultHelper';
|
||||
|
||||
export default function getFile(): [string[], ResultFile] {
|
||||
const file = createResultFile(
|
||||
'README',
|
||||
'md',
|
||||
'This project is generated by lowcode-code-generator & lowcode-solution-icejs3.',
|
||||
);
|
||||
|
||||
return [[], file];
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
import { ResultFile } from '@alilc/lowcode-types';
|
||||
import { createResultFile } from '../../../../../../utils/resultHelper';
|
||||
|
||||
export default function getFile(): [string[], ResultFile] {
|
||||
const file = createResultFile(
|
||||
'.browserslistrc',
|
||||
'',
|
||||
`defaults
|
||||
ios_saf 9
|
||||
`,
|
||||
);
|
||||
|
||||
return [[], file];
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
import { ResultFile } from '@alilc/lowcode-types';
|
||||
import { createResultFile } from '../../../../../../utils/resultHelper';
|
||||
|
||||
/* eslint-disable max-len */
|
||||
export default function getFile(): [string[], ResultFile] {
|
||||
const file = createResultFile(
|
||||
'document',
|
||||
'tsx',
|
||||
`import React from 'react';
|
||||
import { Meta, Title, Links, Main, Scripts } from 'ice';
|
||||
|
||||
export default function Document() {
|
||||
return (
|
||||
<html>
|
||||
<head>
|
||||
<meta charSet="utf-8" />
|
||||
<meta name="description" content="ice.js 3 lite scaffold" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<link rel="stylesheet" href="//alifd.alicdn.com/npm/@alifd/next/1.21.16/next.min.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
|
||||
<Meta />
|
||||
<Title />
|
||||
<Links />
|
||||
</head>
|
||||
<body>
|
||||
<Main />
|
||||
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/react/18.2.0/umd/react.development.js" />
|
||||
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/react-dom/18.2.0/umd/react-dom.development.js" />
|
||||
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/??react-router/6.9.0/react-router.production.min.js,react-router-dom/6.9.0/react-router-dom.production.min.js" />
|
||||
<script crossOrigin="anonymous" src="//alifd.alicdn.com/npm/@alifd/next/1.26.15/next.min.js" />
|
||||
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/prop-types/15.7.2/prop-types.js" />
|
||||
<script crossOrigin="anonymous" src="//g.alicdn.com/platform/c/??lodash/4.6.1/lodash.min.js,immutable/3.7.6/dist/immutable.min.js" />
|
||||
<Scripts />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}`,
|
||||
);
|
||||
|
||||
return [['src'], file];
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
import { ResultFile } from '@alilc/lowcode-types';
|
||||
import { createResultFile } from '../../../../../../utils/resultHelper';
|
||||
|
||||
export default function getFile(): [string[], ResultFile] {
|
||||
const file = createResultFile(
|
||||
'.gitignore',
|
||||
'',
|
||||
`
|
||||
# See https://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
# production
|
||||
build/
|
||||
dist/
|
||||
tmp/
|
||||
lib/
|
||||
|
||||
# misc
|
||||
.idea/
|
||||
.happypack
|
||||
.DS_Store
|
||||
*.swp
|
||||
*.dia~
|
||||
.ice
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
index.module.scss.d.ts
|
||||
`,
|
||||
);
|
||||
|
||||
return [[], file];
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
import { ResultFile } from '@alilc/lowcode-types';
|
||||
import { createResultFile } from '../../../../../../../../../../../utils/resultHelper';
|
||||
|
||||
export default function getFile(): [string[], ResultFile] {
|
||||
const file = createResultFile(
|
||||
'index',
|
||||
'jsx',
|
||||
`
|
||||
import React from 'react';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
export default function Footer() {
|
||||
return (
|
||||
<p className={styles.footer}>
|
||||
<span className={styles.logo}>Alibaba Fusion</span>
|
||||
<br />
|
||||
<span className={styles.copyright}>© 2019-现在 Alibaba Fusion & ICE</span>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
return [['src', 'layouts', 'BasicLayout', 'components', 'Footer'], file];
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
import { ResultFile } from '@alilc/lowcode-types';
|
||||
import { createResultFile } from '../../../../../../../../../../../utils/resultHelper';
|
||||
|
||||
export default function getFile(): [string[], ResultFile] {
|
||||
const file = createResultFile(
|
||||
'index',
|
||||
'module.scss',
|
||||
`
|
||||
.footer {
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.copyright {
|
||||
font-size: 12px;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
return [['src', 'layouts', 'BasicLayout', 'components', 'Footer'], file];
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
import { ResultFile } from '@alilc/lowcode-types';
|
||||
import { createResultFile } from '../../../../../../../../../../../utils/resultHelper';
|
||||
|
||||
export default function getFile(): [string[], ResultFile] {
|
||||
const file = createResultFile(
|
||||
'index',
|
||||
'jsx',
|
||||
`
|
||||
import React from 'react';
|
||||
import { Link } from 'ice';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
export default function Logo({ image, text, url }) {
|
||||
return (
|
||||
<div className="logo">
|
||||
<Link to={url || '/'} className={styles.logo}>
|
||||
{image && <img src={image} alt="logo" />}
|
||||
<span>{text}</span>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
return [['src', 'layouts', 'BasicLayout', 'components', 'Logo'], file];
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
import { ResultFile } from '@alilc/lowcode-types';
|
||||
import { createResultFile } from '../../../../../../../../../../../utils/resultHelper';
|
||||
|
||||
export default function getFile(): [string[], ResultFile] {
|
||||
const file = createResultFile(
|
||||
'index',
|
||||
'module.scss',
|
||||
`
|
||||
.logo{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: $color-text1-1;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
|
||||
&:visited, &:link {
|
||||
color: $color-text1-1;
|
||||
}
|
||||
|
||||
img {
|
||||
height: 24px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
return [['src', 'layouts', 'BasicLayout', 'components', 'Logo'], file];
|
||||
}
|
||||
@ -0,0 +1,79 @@
|
||||
import { ResultFile } from '@alilc/lowcode-types';
|
||||
import { createResultFile } from '../../../../../../../../../../../utils/resultHelper';
|
||||
|
||||
export default function getFile(): [string[], ResultFile] {
|
||||
const file = createResultFile(
|
||||
'index',
|
||||
'jsx',
|
||||
`import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Link, useLocation } from 'ice';
|
||||
import { Nav } from '@alifd/next';
|
||||
import { asideMenuConfig } from '../../menuConfig';
|
||||
|
||||
const { SubNav } = Nav;
|
||||
const NavItem = Nav.Item;
|
||||
|
||||
function getNavMenuItems(menusData) {
|
||||
if (!menusData) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return menusData
|
||||
.filter(item => item.name && !item.hideInMenu)
|
||||
.map((item, index) => getSubMenuOrItem(item, index));
|
||||
}
|
||||
|
||||
function getSubMenuOrItem(item, index) {
|
||||
if (item.children && item.children.some(child => child.name)) {
|
||||
const childrenItems = getNavMenuItems(item.children);
|
||||
|
||||
if (childrenItems && childrenItems.length > 0) {
|
||||
const subNav = (
|
||||
<SubNav key={index} icon={item.icon} label={item.name}>
|
||||
{childrenItems}
|
||||
</SubNav>
|
||||
);
|
||||
return subNav;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const navItem = (
|
||||
<NavItem key={item.path} icon={item.icon}>
|
||||
<Link to={item.path}>{item.name}</Link>
|
||||
</NavItem>
|
||||
);
|
||||
return navItem;
|
||||
}
|
||||
|
||||
const Navigation = (props, context) => {
|
||||
const location = useLocation();
|
||||
const { pathname } = location;
|
||||
const { isCollapse } = context;
|
||||
return (
|
||||
<Nav
|
||||
type="primary"
|
||||
selectedKeys={[pathname]}
|
||||
defaultSelectedKeys={[pathname]}
|
||||
embeddable
|
||||
openMode="single"
|
||||
iconOnly={isCollapse}
|
||||
hasArrow={false}
|
||||
mode={isCollapse ? 'popup' : 'inline'}
|
||||
>
|
||||
{getNavMenuItems(asideMenuConfig)}
|
||||
</Nav>
|
||||
);
|
||||
};
|
||||
|
||||
Navigation.contextTypes = {
|
||||
isCollapse: PropTypes.bool,
|
||||
};
|
||||
export default Navigation;
|
||||
`,
|
||||
);
|
||||
|
||||
return [['src', 'layouts', 'BasicLayout', 'components', 'PageNav'], file];
|
||||
}
|
||||
@ -0,0 +1,92 @@
|
||||
import { ResultFile } from '@alilc/lowcode-types';
|
||||
import { createResultFile } from '../../../../../../../../../utils/resultHelper';
|
||||
|
||||
export default function getFile(): [string[], ResultFile] {
|
||||
const file = createResultFile(
|
||||
'index',
|
||||
'jsx',
|
||||
`
|
||||
import React, { useState } from 'react';
|
||||
import { Shell, ConfigProvider } from '@alifd/next';
|
||||
import PageNav from './components/PageNav';
|
||||
import Logo from './components/Logo';
|
||||
import Footer from './components/Footer';
|
||||
|
||||
(function() {
|
||||
const throttle = function(type, name, obj = window) {
|
||||
let running = false;
|
||||
|
||||
const func = () => {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
|
||||
running = true;
|
||||
requestAnimationFrame(() => {
|
||||
obj.dispatchEvent(new CustomEvent(name));
|
||||
running = false;
|
||||
});
|
||||
};
|
||||
|
||||
obj.addEventListener(type, func);
|
||||
};
|
||||
|
||||
throttle('resize', 'optimizedResize');
|
||||
})();
|
||||
|
||||
export default function BasicLayout({ children }) {
|
||||
const getDevice = width => {
|
||||
const isPhone =
|
||||
typeof navigator !== 'undefined' && navigator && navigator.userAgent.match(/phone/gi);
|
||||
|
||||
if (width < 680 || isPhone) {
|
||||
return 'phone';
|
||||
}
|
||||
if (width < 1280 && width > 680) {
|
||||
return 'tablet';
|
||||
}
|
||||
return 'desktop';
|
||||
};
|
||||
|
||||
const [device, setDevice] = useState(getDevice(NaN));
|
||||
window.addEventListener('optimizedResize', e => {
|
||||
setDevice(getDevice(e && e.target && e.target.innerWidth));
|
||||
});
|
||||
return (
|
||||
<ConfigProvider device={device}>
|
||||
<Shell
|
||||
type="dark"
|
||||
style={{
|
||||
minHeight: '100vh',
|
||||
}}
|
||||
>
|
||||
<Shell.Branding>
|
||||
<Logo
|
||||
image="https://img.alicdn.com/tfs/TB1.ZBecq67gK0jSZFHXXa9jVXa-904-826.png"
|
||||
text="Logo"
|
||||
/>
|
||||
</Shell.Branding>
|
||||
<Shell.Navigation
|
||||
direction="hoz"
|
||||
style={{
|
||||
marginRight: 10,
|
||||
}}
|
||||
></Shell.Navigation>
|
||||
<Shell.Action></Shell.Action>
|
||||
<Shell.Navigation>
|
||||
<PageNav />
|
||||
</Shell.Navigation>
|
||||
|
||||
<Shell.Content>{children}</Shell.Content>
|
||||
<Shell.Footer>
|
||||
<Footer />
|
||||
</Shell.Footer>
|
||||
</Shell>
|
||||
</ConfigProvider>
|
||||
);
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
return [['src', 'layouts', 'BasicLayout'], file];
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
import { ResultFile } from '@alilc/lowcode-types';
|
||||
import { createResultFile } from '../../../../../../../../../utils/resultHelper';
|
||||
|
||||
export default function getFile(): [string[], ResultFile] {
|
||||
const file = createResultFile(
|
||||
'menuConfig',
|
||||
'js',
|
||||
`
|
||||
const headerMenuConfig = [];
|
||||
const asideMenuConfig = [
|
||||
{
|
||||
name: 'Dashboard',
|
||||
path: '/',
|
||||
icon: 'smile',
|
||||
},
|
||||
];
|
||||
export { headerMenuConfig, asideMenuConfig };
|
||||
`,
|
||||
);
|
||||
|
||||
return [['src', 'layouts', 'BasicLayout'], file];
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
import { ResultFile } from '@alilc/lowcode-types';
|
||||
import { createResultFile } from '../../../../../../utils/resultHelper';
|
||||
|
||||
export default function getFile(): [string[], ResultFile] {
|
||||
const file = createResultFile(
|
||||
'tsconfig',
|
||||
'json',
|
||||
`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"module": "ESNext",
|
||||
"target": "ESNext",
|
||||
"lib": ["DOM", "ESNext", "DOM.Iterable"],
|
||||
"jsx": "react-jsx",
|
||||
"moduleResolution": "node",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
"noImplicitAny": false,
|
||||
"importHelpers": true,
|
||||
"strictNullChecks": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"skipLibCheck": true,
|
||||
"paths": {
|
||||
"@/*": ["./src/*"],
|
||||
"ice": [".ice"]
|
||||
}
|
||||
},
|
||||
"include": ["src", ".ice"],
|
||||
"exclude": ["build"]
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
return [[], file];
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
import { ResultFile } from '@alilc/lowcode-types';
|
||||
import { createResultFile } from '../../../../../../utils/resultHelper';
|
||||
|
||||
export default function getFile(): [string[], ResultFile] {
|
||||
const file = createResultFile(
|
||||
'typings.d',
|
||||
'ts',
|
||||
`/// <reference types="@ice/app/types" />
|
||||
|
||||
export {};
|
||||
declare global {
|
||||
interface Window {
|
||||
g_config: Record<string, any>;
|
||||
}
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
return [['src'], file];
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
import { IProjectTemplate } from '../../../../../types';
|
||||
import { generateStaticFiles } from './static-files';
|
||||
|
||||
const icejs3Template: IProjectTemplate = {
|
||||
slots: {
|
||||
components: {
|
||||
path: ['src', 'components'],
|
||||
fileName: 'index',
|
||||
},
|
||||
pages: {
|
||||
path: ['src', 'pages'],
|
||||
fileName: 'index',
|
||||
},
|
||||
entry: {
|
||||
path: ['src'],
|
||||
fileName: 'app',
|
||||
},
|
||||
constants: {
|
||||
path: ['src'],
|
||||
fileName: 'constants',
|
||||
},
|
||||
utils: {
|
||||
path: ['src'],
|
||||
fileName: 'utils',
|
||||
},
|
||||
i18n: {
|
||||
path: ['src'],
|
||||
fileName: 'i18n',
|
||||
},
|
||||
globalStyle: {
|
||||
path: ['src'],
|
||||
fileName: 'global',
|
||||
},
|
||||
packageJSON: {
|
||||
path: [],
|
||||
fileName: 'package',
|
||||
},
|
||||
appConfig: {
|
||||
path: ['src'],
|
||||
fileName: 'app',
|
||||
},
|
||||
buildConfig: {
|
||||
path: [],
|
||||
fileName: 'ice.config',
|
||||
},
|
||||
layout: {
|
||||
path: ['src', 'pages'],
|
||||
fileName: 'layout',
|
||||
},
|
||||
},
|
||||
|
||||
generateTemplate() {
|
||||
return generateStaticFiles();
|
||||
},
|
||||
};
|
||||
|
||||
export default icejs3Template;
|
||||
@ -0,0 +1,33 @@
|
||||
import { ResultDir } from '@alilc/lowcode-types';
|
||||
import { createResultDir } from '../../../../../utils/resultHelper';
|
||||
import { runFileGenerator } from '../../../../../utils/templateHelper';
|
||||
|
||||
import file1 from './files/gitignore';
|
||||
import file2 from './files/README.md';
|
||||
import file3 from './files/browserslistrc';
|
||||
import file4 from './files/typings';
|
||||
import file5 from './files/document';
|
||||
import file6 from './files/src/layouts/BasicLayout/components/Footer/index.jsx';
|
||||
import file7 from './files/src/layouts/BasicLayout/components/Footer/index.style';
|
||||
import file8 from './files/src/layouts/BasicLayout/components/Logo/index.jsx';
|
||||
import file9 from './files/src/layouts/BasicLayout/components/Logo/index.style';
|
||||
import file10 from './files/src/layouts/BasicLayout/components/PageNav/index.jsx';
|
||||
import file11 from './files/src/layouts/BasicLayout/index.jsx';
|
||||
import file12 from './files/src/layouts/BasicLayout/menuConfig.js';
|
||||
|
||||
export function generateStaticFiles(root = createResultDir('.')): ResultDir {
|
||||
runFileGenerator(root, file1);
|
||||
runFileGenerator(root, file2);
|
||||
runFileGenerator(root, file3);
|
||||
runFileGenerator(root, file4);
|
||||
runFileGenerator(root, file5);
|
||||
runFileGenerator(root, file6);
|
||||
runFileGenerator(root, file7);
|
||||
runFileGenerator(root, file8);
|
||||
runFileGenerator(root, file9);
|
||||
runFileGenerator(root, file10);
|
||||
runFileGenerator(root, file11);
|
||||
runFileGenerator(root, file12);
|
||||
|
||||
return root;
|
||||
}
|
||||
109
modules/code-generator/src/solutions/icejs3.ts
Normal file
109
modules/code-generator/src/solutions/icejs3.ts
Normal file
@ -0,0 +1,109 @@
|
||||
import { IProjectBuilder, IProjectBuilderOptions } from '../types';
|
||||
|
||||
import { createProjectBuilder } from '../generator/ProjectBuilder';
|
||||
|
||||
import esmodule from '../plugins/common/esmodule';
|
||||
import containerClass from '../plugins/component/react/containerClass';
|
||||
import containerInitState from '../plugins/component/react/containerInitState';
|
||||
import containerInjectContext from '../plugins/component/react/containerInjectContext';
|
||||
import containerInjectUtils from '../plugins/component/react/containerInjectUtils';
|
||||
import containerInjectDataSourceEngine from '../plugins/component/react/containerInjectDataSourceEngine';
|
||||
import containerInjectConstants from '../plugins/component/react/containerInjectConstants';
|
||||
import containerInjectI18n from '../plugins/component/react/containerInjectI18n';
|
||||
import containerLifeCycle from '../plugins/component/react/containerLifeCycle';
|
||||
import containerMethod from '../plugins/component/react/containerMethod';
|
||||
import jsx from '../plugins/component/react/jsx';
|
||||
import reactCommonDeps from '../plugins/component/react/reactCommonDeps';
|
||||
import css from '../plugins/component/style/css';
|
||||
import constants from '../plugins/project/constants';
|
||||
import i18n from '../plugins/project/i18n';
|
||||
import utils from '../plugins/project/utils';
|
||||
|
||||
import icejs3 from '../plugins/project/framework/icejs3';
|
||||
|
||||
import { prettier } from '../postprocessor';
|
||||
|
||||
export type IceJs3ProjectBuilderOptions = IProjectBuilderOptions;
|
||||
|
||||
export default function createIceJsProjectBuilder(
|
||||
options?: IceJs3ProjectBuilderOptions,
|
||||
): IProjectBuilder {
|
||||
return createProjectBuilder({
|
||||
inStrictMode: options?.inStrictMode,
|
||||
extraContextData: { ...options },
|
||||
template: icejs3.template,
|
||||
plugins: {
|
||||
components: [
|
||||
reactCommonDeps(),
|
||||
esmodule({
|
||||
fileType: 'jsx',
|
||||
}),
|
||||
containerClass(),
|
||||
containerInjectContext(),
|
||||
containerInjectUtils(),
|
||||
containerInjectDataSourceEngine(),
|
||||
containerInjectI18n(),
|
||||
containerInitState(),
|
||||
containerLifeCycle(),
|
||||
containerMethod(),
|
||||
jsx({
|
||||
nodeTypeMapping: {
|
||||
Div: 'div',
|
||||
Component: 'div',
|
||||
Page: 'div',
|
||||
Block: 'div',
|
||||
},
|
||||
}),
|
||||
css(),
|
||||
],
|
||||
pages: [
|
||||
reactCommonDeps(),
|
||||
esmodule({
|
||||
fileType: 'jsx',
|
||||
}),
|
||||
containerClass(),
|
||||
containerInjectContext(),
|
||||
containerInjectUtils(),
|
||||
containerInjectDataSourceEngine(),
|
||||
containerInjectI18n(),
|
||||
containerInjectConstants(),
|
||||
containerInitState(),
|
||||
containerLifeCycle(),
|
||||
containerMethod(),
|
||||
jsx({
|
||||
nodeTypeMapping: {
|
||||
Div: 'div',
|
||||
Component: 'div',
|
||||
Page: 'div',
|
||||
Block: 'div',
|
||||
Box: 'div',
|
||||
},
|
||||
}),
|
||||
css(),
|
||||
],
|
||||
constants: [constants()],
|
||||
utils: [esmodule(), utils('react')],
|
||||
i18n: [i18n()],
|
||||
globalStyle: [icejs3.plugins.globalStyle()],
|
||||
packageJSON: [icejs3.plugins.packageJSON()],
|
||||
buildConfig: [icejs3.plugins.buildConfig()],
|
||||
appConfig: [icejs3.plugins.appConfig()],
|
||||
layout: [icejs3.plugins.layout()],
|
||||
},
|
||||
postProcessors: [prettier()],
|
||||
customizeBuilderOptions: options?.customizeBuilderOptions,
|
||||
});
|
||||
}
|
||||
|
||||
export const plugins = {
|
||||
containerClass,
|
||||
containerInitState,
|
||||
containerInjectContext,
|
||||
containerInjectUtils,
|
||||
containerInjectI18n,
|
||||
containerInjectDataSourceEngine,
|
||||
containerLifeCycle,
|
||||
containerMethod,
|
||||
jsx,
|
||||
commonDeps: reactCommonDeps,
|
||||
};
|
||||
12
modules/code-generator/src/utils/format.ts
Normal file
12
modules/code-generator/src/utils/format.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import prettier from 'prettier';
|
||||
import parserBabel from 'prettier/parser-babel';
|
||||
|
||||
export function format(content: string, options = {}) {
|
||||
return prettier.format(content, {
|
||||
parser: 'babel',
|
||||
plugins: [parserBabel],
|
||||
singleQuote: true,
|
||||
jsxSingleQuote: false,
|
||||
...options,
|
||||
});
|
||||
}
|
||||
20
modules/code-generator/src/utils/theme.ts
Normal file
20
modules/code-generator/src/utils/theme.ts
Normal file
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 获取主题信息
|
||||
* @param theme theme 形如 @alife/theme-97 或者 @alife/theme-97@^1.0.0
|
||||
*/
|
||||
|
||||
export interface ThemeInfo {
|
||||
name: string;
|
||||
version?: string;
|
||||
}
|
||||
|
||||
export function getThemeInfo(theme: string): ThemeInfo {
|
||||
const sepIdx = theme.indexOf('@', 1);
|
||||
if (sepIdx === -1) {
|
||||
return { name: theme };
|
||||
}
|
||||
return {
|
||||
name: theme.slice(0, sepIdx),
|
||||
version: theme.slice(sepIdx + 1),
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
defaults
|
||||
ios_saf 9
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
|
||||
# See https://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
# production
|
||||
build/
|
||||
dist/
|
||||
tmp/
|
||||
lib/
|
||||
|
||||
# misc
|
||||
.idea/
|
||||
.happypack
|
||||
.DS_Store
|
||||
*.swp
|
||||
*.dia~
|
||||
.ice
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
index.module.scss.d.ts
|
||||
|
||||
@ -0,0 +1 @@
|
||||
This project is generated by lowcode-code-generator & lowcode-solution-icejs3.
|
||||
@ -0,0 +1,90 @@
|
||||
import { join } from 'path';
|
||||
import { defineConfig } from '@ice/app';
|
||||
import _ from 'lodash';
|
||||
import fusion from '@ice/plugin-fusion';
|
||||
import locales from '@ice/plugin-moment-locales';
|
||||
import type { Plugin } from '@ice/app/esm/types';
|
||||
|
||||
interface PluginOptions {
|
||||
id: string;
|
||||
}
|
||||
|
||||
const plugin: Plugin<PluginOptions> = (options) => ({
|
||||
// name 可选,插件名称
|
||||
name: 'plugin-name',
|
||||
// setup 必选,用于定制工程构建配置
|
||||
setup: ({ onGetConfig, modifyUserConfig }) => {
|
||||
modifyUserConfig('codeSplitting', 'page');
|
||||
|
||||
onGetConfig((config) => {
|
||||
config.entry = {
|
||||
web: join(process.cwd(), '.ice/entry.client.tsx'),
|
||||
};
|
||||
|
||||
config.cssFilename = '[name].css';
|
||||
|
||||
config.configureWebpack = config.configureWebpack || [];
|
||||
config.configureWebpack?.push((webpackConfig) => {
|
||||
if (webpackConfig.output) {
|
||||
webpackConfig.output.filename = '[name].js';
|
||||
webpackConfig.output.chunkFilename = '[name].js';
|
||||
}
|
||||
return webpackConfig;
|
||||
});
|
||||
|
||||
config.swcOptions = _.merge(config.swcOptions, {
|
||||
compilationConfig: {
|
||||
jsc: {
|
||||
transform: {
|
||||
react: {
|
||||
runtime: 'classic',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// 解决 webpack publicPath 问题
|
||||
config.transforms = config.transforms || [];
|
||||
config.transforms.push((source: string, id: string) => {
|
||||
if (id.includes('.ice/entry.client.tsx')) {
|
||||
let code = `
|
||||
if (!__webpack_public_path__?.startsWith('http') && document.currentScript) {
|
||||
// @ts-ignore
|
||||
__webpack_public_path__ = document.currentScript.src.replace(/^(.*\\/)[^/]+$/, '$1');
|
||||
window.__ICE_ASSETS_MANIFEST__ = window.__ICE_ASSETS_MANIFEST__ || {};
|
||||
window.__ICE_ASSETS_MANIFEST__.publicPath = __webpack_public_path__;
|
||||
}
|
||||
`;
|
||||
code += source;
|
||||
return { code };
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
// The project config, see https://v3.ice.work/docs/guide/basic/config
|
||||
const minify = process.env.NODE_ENV === 'production' ? 'swc' : false;
|
||||
export default defineConfig(() => ({
|
||||
ssr: false,
|
||||
ssg: false,
|
||||
minify,
|
||||
|
||||
externals: {
|
||||
react: 'React',
|
||||
'react-dom': 'ReactDOM',
|
||||
'react-dom/client': 'ReactDOM',
|
||||
'@alifd/next': 'Next',
|
||||
lodash: 'var window._',
|
||||
'@alilc/lowcode-engine': 'var window.AliLowCodeEngine',
|
||||
},
|
||||
plugins: [
|
||||
fusion({
|
||||
importStyle: true,
|
||||
}),
|
||||
locales(),
|
||||
plugin(),
|
||||
],
|
||||
}));
|
||||
|
||||
@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "icejs3-demo-app",
|
||||
"version": "0.1.5",
|
||||
"description": "icejs 3 轻量级模板,使用 JavaScript,仅包含基础的 Layout。",
|
||||
"dependencies": {
|
||||
"moment": "^2.24.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router": "^6.9.0",
|
||||
"react-router-dom": "^6.9.0",
|
||||
"intl-messageformat": "^9.3.6",
|
||||
"@alifd/next": "1.19.18",
|
||||
"@ice/runtime": "^1.0.0",
|
||||
"@alilc/lowcode-datasource-engine": "^1.0.0",
|
||||
"@alilc/lowcode-datasource-url-params-handler": "^1.0.0",
|
||||
"@alilc/lowcode-datasource-fetch-handler": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ice/app": "^3.0.0",
|
||||
"@types/react": "^18.0.0",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
"@types/node": "^18.11.17",
|
||||
"@ice/plugin-fusion": "^1.0.1",
|
||||
"@ice/plugin-moment-locales": "^1.0.0",
|
||||
"eslint": "^6.0.1",
|
||||
"stylelint": "^13.2.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "ice start",
|
||||
"build": "ice build",
|
||||
"lint": "npm run eslint && npm run stylelint",
|
||||
"eslint": "eslint --cache --ext .js,.jsx ./",
|
||||
"stylelint": "stylelint ./**/*.scss"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://gitlab.xxx.com/msd/leak-scan/tree/master"
|
||||
},
|
||||
"private": true,
|
||||
"originTemplate": "@alifd/scaffold-lite-js"
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
import { defineAppConfig } from 'ice';
|
||||
|
||||
// App config, see https://v3.ice.work/docs/guide/basic/app
|
||||
export default defineAppConfig(() => ({
|
||||
// Set your configs here.
|
||||
app: {
|
||||
rootId: 'App',
|
||||
},
|
||||
router: {
|
||||
type: 'browser',
|
||||
basename: '/',
|
||||
},
|
||||
}));
|
||||
@ -0,0 +1,3 @@
|
||||
const __$$constants = { ENV: 'prod', DOMAIN: 'xxx.xxx.com' };
|
||||
|
||||
export default __$$constants;
|
||||
@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
import { Meta, Title, Links, Main, Scripts } from 'ice';
|
||||
|
||||
export default function Document() {
|
||||
return (
|
||||
<html>
|
||||
<head>
|
||||
<meta charSet="utf-8" />
|
||||
<meta name="description" content="ice.js 3 lite scaffold" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<link rel="stylesheet" href="//alifd.alicdn.com/npm/@alifd/next/1.21.16/next.min.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
|
||||
<Meta />
|
||||
<Title />
|
||||
<Links />
|
||||
</head>
|
||||
<body>
|
||||
<Main />
|
||||
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/react/18.2.0/umd/react.development.js" />
|
||||
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/react-dom/18.2.0/umd/react-dom.development.js" />
|
||||
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/??react-router/6.9.0/react-router.production.min.js,react-router-dom/6.9.0/react-router-dom.production.min.js" />
|
||||
<script crossOrigin="anonymous" src="//alifd.alicdn.com/npm/@alifd/next/1.26.15/next.min.js" />
|
||||
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/prop-types/15.7.2/prop-types.js" />
|
||||
<script crossOrigin="anonymous" src="//g.alicdn.com/platform/c/??lodash/4.6.1/lodash.min.js,immutable/3.7.6/dist/immutable.min.js" />
|
||||
<Scripts />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
// 引入默认全局样式
|
||||
@import '@alifd/next/reset.scss';
|
||||
|
||||
body {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
body {
|
||||
font-size: 12px;
|
||||
}
|
||||
.table {
|
||||
width: 100px;
|
||||
}
|
||||
@ -0,0 +1,77 @@
|
||||
const i18nConfig = {};
|
||||
|
||||
let locale =
|
||||
typeof navigator === 'object' && typeof navigator.language === 'string'
|
||||
? navigator.language
|
||||
: 'zh-CN';
|
||||
|
||||
const getLocale = () => locale;
|
||||
|
||||
const setLocale = (target) => {
|
||||
locale = target;
|
||||
};
|
||||
|
||||
const isEmptyVariables = (variables) =>
|
||||
(Array.isArray(variables) && variables.length === 0) ||
|
||||
(typeof variables === 'object' &&
|
||||
(!variables || Object.keys(variables).length === 0));
|
||||
|
||||
// 按低代码规范里面的要求进行变量替换
|
||||
const format = (msg, variables) =>
|
||||
typeof msg === 'string'
|
||||
? msg.replace(/\$\{(\w+)\}/g, (match, key) => variables?.[key] ?? '')
|
||||
: msg;
|
||||
|
||||
const i18nFormat = ({ id, defaultMessage, fallback }, variables) => {
|
||||
const msg =
|
||||
i18nConfig[locale]?.[id] ??
|
||||
i18nConfig[locale.replace('-', '_')]?.[id] ??
|
||||
defaultMessage;
|
||||
if (msg == null) {
|
||||
console.warn('[i18n]: unknown message id: %o (locale=%o)', id, locale);
|
||||
return fallback === undefined ? `${id}` : fallback;
|
||||
}
|
||||
|
||||
return format(msg, variables);
|
||||
};
|
||||
|
||||
const i18n = (id, params) => {
|
||||
return i18nFormat({ id }, params);
|
||||
};
|
||||
|
||||
// 将国际化的一些方法注入到目标对象&上下文中
|
||||
const _inject2 = (target) => {
|
||||
target.i18n = i18n;
|
||||
target.getLocale = getLocale;
|
||||
target.setLocale = (locale) => {
|
||||
setLocale(locale);
|
||||
target.forceUpdate();
|
||||
};
|
||||
target._i18nText = (t) => {
|
||||
// 优先取直接传过来的语料
|
||||
const localMsg = t[locale] ?? t[String(locale).replace('-', '_')];
|
||||
if (localMsg != null) {
|
||||
return format(localMsg, t.params);
|
||||
}
|
||||
|
||||
// 其次用项目级别的
|
||||
const projectMsg = i18nFormat({ id: t.key, fallback: null }, t.params);
|
||||
if (projectMsg != null) {
|
||||
return projectMsg;
|
||||
}
|
||||
|
||||
// 兜底用 use 指定的或默认语言的
|
||||
return format(t[t.use || 'zh-CN'] ?? t.en_US, t.params);
|
||||
};
|
||||
|
||||
// 注入到上下文中去
|
||||
if (target._context && target._context !== target) {
|
||||
Object.assign(target._context, {
|
||||
i18n,
|
||||
getLocale,
|
||||
setLocale: target.setLocale,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export { getLocale, setLocale, i18n, i18nFormat, _inject2 };
|
||||
@ -0,0 +1,14 @@
|
||||
|
||||
import React from 'react';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
export default function Footer() {
|
||||
return (
|
||||
<p className={styles.footer}>
|
||||
<span className={styles.logo}>Alibaba Fusion</span>
|
||||
<br />
|
||||
<span className={styles.copyright}>© 2019-现在 Alibaba Fusion & ICE</span>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
@ -0,0 +1,15 @@
|
||||
|
||||
.footer {
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.copyright {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
@ -0,0 +1,16 @@
|
||||
|
||||
import React from 'react';
|
||||
import { Link } from 'ice';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
export default function Logo({ image, text, url }) {
|
||||
return (
|
||||
<div className="logo">
|
||||
<Link to={url || '/'} className={styles.logo}>
|
||||
{image && <img src={image} alt="logo" />}
|
||||
<span>{text}</span>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
|
||||
.logo{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: $color-text1-1;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
|
||||
&:visited, &:link {
|
||||
color: $color-text1-1;
|
||||
}
|
||||
|
||||
img {
|
||||
height: 24px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,68 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Link, useLocation } from 'ice';
|
||||
import { Nav } from '@alifd/next';
|
||||
import { asideMenuConfig } from '../../menuConfig';
|
||||
|
||||
const { SubNav } = Nav;
|
||||
const NavItem = Nav.Item;
|
||||
|
||||
function getNavMenuItems(menusData) {
|
||||
if (!menusData) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return menusData
|
||||
.filter(item => item.name && !item.hideInMenu)
|
||||
.map((item, index) => getSubMenuOrItem(item, index));
|
||||
}
|
||||
|
||||
function getSubMenuOrItem(item, index) {
|
||||
if (item.children && item.children.some(child => child.name)) {
|
||||
const childrenItems = getNavMenuItems(item.children);
|
||||
|
||||
if (childrenItems && childrenItems.length > 0) {
|
||||
const subNav = (
|
||||
<SubNav key={index} icon={item.icon} label={item.name}>
|
||||
{childrenItems}
|
||||
</SubNav>
|
||||
);
|
||||
return subNav;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const navItem = (
|
||||
<NavItem key={item.path} icon={item.icon}>
|
||||
<Link to={item.path}>{item.name}</Link>
|
||||
</NavItem>
|
||||
);
|
||||
return navItem;
|
||||
}
|
||||
|
||||
const Navigation = (props, context) => {
|
||||
const location = useLocation();
|
||||
const { pathname } = location;
|
||||
const { isCollapse } = context;
|
||||
return (
|
||||
<Nav
|
||||
type="primary"
|
||||
selectedKeys={[pathname]}
|
||||
defaultSelectedKeys={[pathname]}
|
||||
embeddable
|
||||
openMode="single"
|
||||
iconOnly={isCollapse}
|
||||
hasArrow={false}
|
||||
mode={isCollapse ? 'popup' : 'inline'}
|
||||
>
|
||||
{getNavMenuItems(asideMenuConfig)}
|
||||
</Nav>
|
||||
);
|
||||
};
|
||||
|
||||
Navigation.contextTypes = {
|
||||
isCollapse: PropTypes.bool,
|
||||
};
|
||||
export default Navigation;
|
||||
|
||||
@ -0,0 +1,81 @@
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { Shell, ConfigProvider } from '@alifd/next';
|
||||
import PageNav from './components/PageNav';
|
||||
import Logo from './components/Logo';
|
||||
import Footer from './components/Footer';
|
||||
|
||||
(function() {
|
||||
const throttle = function(type, name, obj = window) {
|
||||
let running = false;
|
||||
|
||||
const func = () => {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
|
||||
running = true;
|
||||
requestAnimationFrame(() => {
|
||||
obj.dispatchEvent(new CustomEvent(name));
|
||||
running = false;
|
||||
});
|
||||
};
|
||||
|
||||
obj.addEventListener(type, func);
|
||||
};
|
||||
|
||||
throttle('resize', 'optimizedResize');
|
||||
})();
|
||||
|
||||
export default function BasicLayout({ children }) {
|
||||
const getDevice = width => {
|
||||
const isPhone =
|
||||
typeof navigator !== 'undefined' && navigator && navigator.userAgent.match(/phone/gi);
|
||||
|
||||
if (width < 680 || isPhone) {
|
||||
return 'phone';
|
||||
}
|
||||
if (width < 1280 && width > 680) {
|
||||
return 'tablet';
|
||||
}
|
||||
return 'desktop';
|
||||
};
|
||||
|
||||
const [device, setDevice] = useState(getDevice(NaN));
|
||||
window.addEventListener('optimizedResize', e => {
|
||||
setDevice(getDevice(e && e.target && e.target.innerWidth));
|
||||
});
|
||||
return (
|
||||
<ConfigProvider device={device}>
|
||||
<Shell
|
||||
type="dark"
|
||||
style={{
|
||||
minHeight: '100vh',
|
||||
}}
|
||||
>
|
||||
<Shell.Branding>
|
||||
<Logo
|
||||
image="https://img.alicdn.com/tfs/TB1.ZBecq67gK0jSZFHXXa9jVXa-904-826.png"
|
||||
text="Logo"
|
||||
/>
|
||||
</Shell.Branding>
|
||||
<Shell.Navigation
|
||||
direction="hoz"
|
||||
style={{
|
||||
marginRight: 10,
|
||||
}}
|
||||
></Shell.Navigation>
|
||||
<Shell.Action></Shell.Action>
|
||||
<Shell.Navigation>
|
||||
<PageNav />
|
||||
</Shell.Navigation>
|
||||
|
||||
<Shell.Content>{children}</Shell.Content>
|
||||
<Shell.Footer>
|
||||
<Footer />
|
||||
</Shell.Footer>
|
||||
</Shell>
|
||||
</ConfigProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
|
||||
const headerMenuConfig = [];
|
||||
const asideMenuConfig = [
|
||||
{
|
||||
name: 'Dashboard',
|
||||
path: '/',
|
||||
icon: 'smile',
|
||||
},
|
||||
];
|
||||
export { headerMenuConfig, asideMenuConfig };
|
||||
|
||||
@ -0,0 +1,195 @@
|
||||
// 注意: 出码引擎注入的临时变量默认都以 "__$$" 开头,禁止在搭建的代码中直接访问。
|
||||
// 例外:react 框架的导出名和各种组件名除外。
|
||||
import React from 'react';
|
||||
|
||||
import { Form, Input, NumberPicker, Select, Button } from '@alifd/next';
|
||||
|
||||
import { createUrlParamsHandler as __$$createUrlParamsRequestHandler } from '@alilc/lowcode-datasource-url-params-handler';
|
||||
|
||||
import { createFetchHandler as __$$createFetchRequestHandler } from '@alilc/lowcode-datasource-fetch-handler';
|
||||
|
||||
import { create as __$$createDataSourceEngine } from '@alilc/lowcode-datasource-engine/runtime';
|
||||
|
||||
import utils, { RefsManager } from '../../utils';
|
||||
|
||||
import * as __$$i18n from '../../i18n';
|
||||
|
||||
import __$$constants from '../../constants';
|
||||
|
||||
import './index.css';
|
||||
|
||||
class Test$$Page extends React.Component {
|
||||
_context = this;
|
||||
|
||||
_dataSourceConfig = this._defineDataSourceConfig();
|
||||
_dataSourceEngine = __$$createDataSourceEngine(this._dataSourceConfig, this, {
|
||||
runtimeConfig: true,
|
||||
requestHandlersMap: {
|
||||
urlParams: __$$createUrlParamsRequestHandler(window.location.search),
|
||||
fetch: __$$createFetchRequestHandler(),
|
||||
},
|
||||
});
|
||||
|
||||
get dataSourceMap() {
|
||||
return this._dataSourceEngine.dataSourceMap || {};
|
||||
}
|
||||
|
||||
reloadDataSource = async () => {
|
||||
await this._dataSourceEngine.reloadDataSource();
|
||||
};
|
||||
|
||||
get constants() {
|
||||
return __$$constants || {};
|
||||
}
|
||||
|
||||
constructor(props, context) {
|
||||
super(props);
|
||||
|
||||
this.utils = utils;
|
||||
|
||||
this._refsManager = new RefsManager();
|
||||
|
||||
__$$i18n._inject2(this);
|
||||
|
||||
this.state = { text: 'outter' };
|
||||
}
|
||||
|
||||
$ = (refName) => {
|
||||
return this._refsManager.get(refName);
|
||||
};
|
||||
|
||||
$$ = (refName) => {
|
||||
return this._refsManager.getAll(refName);
|
||||
};
|
||||
|
||||
_defineDataSourceConfig() {
|
||||
const _this = this;
|
||||
return {
|
||||
list: [
|
||||
{
|
||||
id: 'urlParams',
|
||||
type: 'urlParams',
|
||||
isInit: function () {
|
||||
return undefined;
|
||||
}.bind(_this),
|
||||
options: function () {
|
||||
return undefined;
|
||||
}.bind(_this),
|
||||
},
|
||||
{
|
||||
id: 'user',
|
||||
type: 'fetch',
|
||||
options: function () {
|
||||
return {
|
||||
method: 'GET',
|
||||
uri: 'https://shs.xxx.com/mock/1458/demo/user',
|
||||
isSync: true,
|
||||
};
|
||||
}.bind(_this),
|
||||
dataHandler: function (response) {
|
||||
if (!response.data.success) {
|
||||
throw new Error(response.data.message);
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
isInit: function () {
|
||||
return undefined;
|
||||
}.bind(_this),
|
||||
},
|
||||
{
|
||||
id: 'orders',
|
||||
type: 'fetch',
|
||||
options: function () {
|
||||
return {
|
||||
method: 'GET',
|
||||
uri: 'https://shs.xxx.com/mock/1458/demo/orders',
|
||||
isSync: true,
|
||||
};
|
||||
}.bind(_this),
|
||||
dataHandler: function (response) {
|
||||
if (!response.data.success) {
|
||||
throw new Error(response.data.message);
|
||||
}
|
||||
return response.data.data.result;
|
||||
},
|
||||
isInit: function () {
|
||||
return undefined;
|
||||
}.bind(_this),
|
||||
},
|
||||
],
|
||||
dataHandler: function (dataMap) {
|
||||
console.info('All datasources loaded:', dataMap);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._dataSourceEngine.reloadDataSource();
|
||||
|
||||
console.log('componentDidMount');
|
||||
}
|
||||
|
||||
render() {
|
||||
const __$$context = this._context || this;
|
||||
const { state } = __$$context;
|
||||
return (
|
||||
<div ref={this._refsManager.linkRef('outterView')} autoLoading={true}>
|
||||
<Form
|
||||
labelCol={__$$eval(() => this.state.colNum)}
|
||||
style={{}}
|
||||
ref={this._refsManager.linkRef('testForm')}
|
||||
>
|
||||
<Form.Item label="姓名:" name="name" initValue="李雷">
|
||||
<Input placeholder="请输入" size="medium" style={{ width: 320 }} />
|
||||
</Form.Item>
|
||||
<Form.Item label="年龄:" name="age" initValue="22">
|
||||
<NumberPicker size="medium" type="normal" />
|
||||
</Form.Item>
|
||||
<Form.Item label="职业:" name="profession">
|
||||
<Select
|
||||
dataSource={[
|
||||
{ label: '教师', value: 't' },
|
||||
{ label: '医生', value: 'd' },
|
||||
{ label: '歌手', value: 's' },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<Button.Group>
|
||||
{__$$evalArray(() => ['a', 'b', 'c']).map((item, index) =>
|
||||
((__$$context) =>
|
||||
!!__$$eval(() => index >= 1) && (
|
||||
<Button type="primary" style={{ margin: '0 5px 0 5px' }}>
|
||||
{__$$eval(() => item)}
|
||||
</Button>
|
||||
))(__$$createChildContext(__$$context, { item, index }))
|
||||
)}
|
||||
</Button.Group>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Test$$Page;
|
||||
|
||||
function __$$eval(expr) {
|
||||
try {
|
||||
return expr();
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
function __$$evalArray(expr) {
|
||||
const res = __$$eval(expr);
|
||||
return Array.isArray(res) ? res : [];
|
||||
}
|
||||
|
||||
function __$$createChildContext(oldContext, ext) {
|
||||
const childContext = {
|
||||
...oldContext,
|
||||
...ext,
|
||||
};
|
||||
childContext.__proto__ = oldContext;
|
||||
return childContext;
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
import { Outlet } from 'ice';
|
||||
import BasicLayout from '@/layouts/BasicLayout';
|
||||
|
||||
export default function Layout() {
|
||||
return (
|
||||
<BasicLayout>
|
||||
<Outlet />
|
||||
</BasicLayout>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
/// <reference types="@ice/app/types" />
|
||||
|
||||
export {};
|
||||
declare global {
|
||||
interface Window {
|
||||
g_config: Record<string, any>;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,47 @@
|
||||
import { createRef } from 'react';
|
||||
|
||||
export class RefsManager {
|
||||
constructor() {
|
||||
this.refInsStore = {};
|
||||
}
|
||||
|
||||
clearNullRefs() {
|
||||
Object.keys(this.refInsStore).forEach((refName) => {
|
||||
const filteredInsList = this.refInsStore[refName].filter(
|
||||
(insRef) => !!insRef.current
|
||||
);
|
||||
if (filteredInsList.length > 0) {
|
||||
this.refInsStore[refName] = filteredInsList;
|
||||
} else {
|
||||
delete this.refInsStore[refName];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
get(refName) {
|
||||
this.clearNullRefs();
|
||||
if (this.refInsStore[refName] && this.refInsStore[refName].length > 0) {
|
||||
return this.refInsStore[refName][0].current;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
getAll(refName) {
|
||||
this.clearNullRefs();
|
||||
if (this.refInsStore[refName] && this.refInsStore[refName].length > 0) {
|
||||
return this.refInsStore[refName].map((i) => i.current);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
linkRef(refName) {
|
||||
const refIns = createRef();
|
||||
this.refInsStore[refName] = this.refInsStore[refName] || [];
|
||||
this.refInsStore[refName].push(refIns);
|
||||
return refIns;
|
||||
}
|
||||
}
|
||||
|
||||
export default {};
|
||||
276
modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/schema.json5
vendored
Normal file
276
modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/schema.json5
vendored
Normal file
@ -0,0 +1,276 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"componentsMap": [
|
||||
{
|
||||
"componentName": "Button",
|
||||
"package": "@alifd/next",
|
||||
"version": "1.19.18",
|
||||
"destructuring": true,
|
||||
"exportName": "Button"
|
||||
},
|
||||
{
|
||||
"componentName": "Button.Group",
|
||||
"package": "@alifd/next",
|
||||
"version": "1.19.18",
|
||||
"destructuring": true,
|
||||
"exportName": "Button",
|
||||
"subName": "Group"
|
||||
},
|
||||
{
|
||||
"componentName": "Input",
|
||||
"package": "@alifd/next",
|
||||
"version": "1.19.18",
|
||||
"destructuring": true,
|
||||
"exportName": "Input"
|
||||
},
|
||||
{
|
||||
"componentName": "Form",
|
||||
"package": "@alifd/next",
|
||||
"version": "1.19.18",
|
||||
"destructuring": true,
|
||||
"exportName": "Form"
|
||||
},
|
||||
{
|
||||
"componentName": "Form.Item",
|
||||
"package": "@alifd/next",
|
||||
"version": "1.19.18",
|
||||
"destructuring": true,
|
||||
"exportName": "Form",
|
||||
"subName": "Item"
|
||||
},
|
||||
{
|
||||
"componentName": "NumberPicker",
|
||||
"package": "@alifd/next",
|
||||
"version": "1.19.18",
|
||||
"destructuring": true,
|
||||
"exportName": "NumberPicker"
|
||||
},
|
||||
{
|
||||
"componentName": "Select",
|
||||
"package": "@alifd/next",
|
||||
"version": "1.19.18",
|
||||
"destructuring": true,
|
||||
"exportName": "Select"
|
||||
}
|
||||
],
|
||||
"componentsTree": [
|
||||
{
|
||||
"componentName": "Page",
|
||||
"id": "node$1",
|
||||
"meta": {
|
||||
"title": "测试",
|
||||
"router": "/"
|
||||
},
|
||||
"props": {
|
||||
"ref": "outterView",
|
||||
"autoLoading": true
|
||||
},
|
||||
"fileName": "test",
|
||||
"state": {
|
||||
"text": "outter"
|
||||
},
|
||||
"lifeCycles": {
|
||||
"componentDidMount": {
|
||||
"type": "JSFunction",
|
||||
"value": "function() { console.log('componentDidMount'); }"
|
||||
}
|
||||
},
|
||||
dataSource: {
|
||||
list: [
|
||||
{
|
||||
id: 'urlParams',
|
||||
type: 'urlParams',
|
||||
},
|
||||
// 示例数据源:https://shs.xxx.com/mock/1458/demo/user
|
||||
{
|
||||
id: 'user',
|
||||
type: 'fetch',
|
||||
options: {
|
||||
method: 'GET',
|
||||
uri: 'https://shs.xxx.com/mock/1458/demo/user',
|
||||
isSync: true,
|
||||
},
|
||||
dataHandler: {
|
||||
type: 'JSExpression',
|
||||
value: 'function (response) {\nif (!response.data.success){\n throw new Error(response.data.message);\n }\n return response.data.data;\n}',
|
||||
},
|
||||
},
|
||||
// 示例数据源:https://shs.xxx.com/mock/1458/demo/orders
|
||||
{
|
||||
id: 'orders',
|
||||
type: 'fetch',
|
||||
options: {
|
||||
method: 'GET',
|
||||
uri: "https://shs.xxx.com/mock/1458/demo/orders",
|
||||
isSync: true,
|
||||
},
|
||||
dataHandler: {
|
||||
type: 'JSExpression',
|
||||
value: 'function (response) {\nif (!response.data.success){\n throw new Error(response.data.message);\n }\n return response.data.data.result;\n}',
|
||||
},
|
||||
},
|
||||
],
|
||||
dataHandler: {
|
||||
type: 'JSExpression',
|
||||
value: 'function (dataMap) {\n console.info("All datasources loaded:", dataMap);\n}',
|
||||
},
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"componentName": "Form",
|
||||
"id": "node$2",
|
||||
"props": {
|
||||
"labelCol": {
|
||||
"type": "JSExpression",
|
||||
"value": "this.state.colNum"
|
||||
},
|
||||
"style": {},
|
||||
"ref": "testForm"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"componentName": "Form.Item",
|
||||
"id": "node$3",
|
||||
"props": {
|
||||
"label": "姓名:",
|
||||
"name": "name",
|
||||
"initValue": "李雷"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"componentName": "Input",
|
||||
"id": "node$4",
|
||||
"props": {
|
||||
"placeholder": "请输入",
|
||||
"size": "medium",
|
||||
"style": {
|
||||
"width": 320
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"componentName": "Form.Item",
|
||||
"id": "node$5",
|
||||
"props": {
|
||||
"label": "年龄:",
|
||||
"name": "age",
|
||||
"initValue": "22"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"componentName": "NumberPicker",
|
||||
"id": "node$6",
|
||||
"props": {
|
||||
"size": "medium",
|
||||
"type": "normal"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"componentName": "Form.Item",
|
||||
"id": "node$7",
|
||||
"props": {
|
||||
"label": "职业:",
|
||||
"name": "profession"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"componentName": "Select",
|
||||
"id": "node$8",
|
||||
"props": {
|
||||
"dataSource": [
|
||||
{
|
||||
"label": "教师",
|
||||
"value": "t"
|
||||
},
|
||||
{
|
||||
"label": "医生",
|
||||
"value": "d"
|
||||
},
|
||||
{
|
||||
"label": "歌手",
|
||||
"value": "s"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"componentName": "Div",
|
||||
"id": "node$9",
|
||||
"props": {
|
||||
"style": {
|
||||
"textAlign": "center"
|
||||
}
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"componentName": "Button.Group",
|
||||
"id": "node$a",
|
||||
"props": {},
|
||||
"children": [
|
||||
{
|
||||
"componentName": "Button",
|
||||
"id": "node$b",
|
||||
"condition": {
|
||||
"type": "JSExpression",
|
||||
"value": "this.index >= 1"
|
||||
},
|
||||
"loop": ["a", "b", "c"],
|
||||
"props": {
|
||||
"type": "primary",
|
||||
"style": {
|
||||
"margin": "0 5px 0 5px"
|
||||
},
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "JSExpression",
|
||||
"value": "this.item"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"constants": {
|
||||
"ENV": "prod",
|
||||
"DOMAIN": "xxx.xxx.com"
|
||||
},
|
||||
"css": "body {font-size: 12px;} .table { width: 100px;}",
|
||||
"config": {
|
||||
"sdkVersion": "1.0.3",
|
||||
"historyMode": "hash",
|
||||
"targetRootID": "J_Container",
|
||||
"layout": {
|
||||
"componentName": "BasicLayout",
|
||||
"props": {
|
||||
"logo": "...",
|
||||
"name": "测试网站"
|
||||
}
|
||||
},
|
||||
"theme": {
|
||||
"package": "@alife/theme-fusion",
|
||||
"version": "^0.1.0",
|
||||
"primary": "#ff9966"
|
||||
}
|
||||
},
|
||||
"meta": {
|
||||
"name": "demo应用",
|
||||
"git_group": "appGroup",
|
||||
"project_name": "app_demo",
|
||||
"description": "这是一个测试应用",
|
||||
"spma": "spa23d",
|
||||
"creator": "月飞"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
defaults
|
||||
ios_saf 9
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
|
||||
# See https://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
# production
|
||||
build/
|
||||
dist/
|
||||
tmp/
|
||||
lib/
|
||||
|
||||
# misc
|
||||
.idea/
|
||||
.happypack
|
||||
.DS_Store
|
||||
*.swp
|
||||
*.dia~
|
||||
.ice
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
index.module.scss.d.ts
|
||||
|
||||
@ -0,0 +1 @@
|
||||
This project is generated by lowcode-code-generator & lowcode-solution-icejs3.
|
||||
@ -0,0 +1,90 @@
|
||||
import { join } from 'path';
|
||||
import { defineConfig } from '@ice/app';
|
||||
import _ from 'lodash';
|
||||
import fusion from '@ice/plugin-fusion';
|
||||
import locales from '@ice/plugin-moment-locales';
|
||||
import type { Plugin } from '@ice/app/esm/types';
|
||||
|
||||
interface PluginOptions {
|
||||
id: string;
|
||||
}
|
||||
|
||||
const plugin: Plugin<PluginOptions> = (options) => ({
|
||||
// name 可选,插件名称
|
||||
name: 'plugin-name',
|
||||
// setup 必选,用于定制工程构建配置
|
||||
setup: ({ onGetConfig, modifyUserConfig }) => {
|
||||
modifyUserConfig('codeSplitting', 'page');
|
||||
|
||||
onGetConfig((config) => {
|
||||
config.entry = {
|
||||
web: join(process.cwd(), '.ice/entry.client.tsx'),
|
||||
};
|
||||
|
||||
config.cssFilename = '[name].css';
|
||||
|
||||
config.configureWebpack = config.configureWebpack || [];
|
||||
config.configureWebpack?.push((webpackConfig) => {
|
||||
if (webpackConfig.output) {
|
||||
webpackConfig.output.filename = '[name].js';
|
||||
webpackConfig.output.chunkFilename = '[name].js';
|
||||
}
|
||||
return webpackConfig;
|
||||
});
|
||||
|
||||
config.swcOptions = _.merge(config.swcOptions, {
|
||||
compilationConfig: {
|
||||
jsc: {
|
||||
transform: {
|
||||
react: {
|
||||
runtime: 'classic',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// 解决 webpack publicPath 问题
|
||||
config.transforms = config.transforms || [];
|
||||
config.transforms.push((source: string, id: string) => {
|
||||
if (id.includes('.ice/entry.client.tsx')) {
|
||||
let code = `
|
||||
if (!__webpack_public_path__?.startsWith('http') && document.currentScript) {
|
||||
// @ts-ignore
|
||||
__webpack_public_path__ = document.currentScript.src.replace(/^(.*\\/)[^/]+$/, '$1');
|
||||
window.__ICE_ASSETS_MANIFEST__ = window.__ICE_ASSETS_MANIFEST__ || {};
|
||||
window.__ICE_ASSETS_MANIFEST__.publicPath = __webpack_public_path__;
|
||||
}
|
||||
`;
|
||||
code += source;
|
||||
return { code };
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
// The project config, see https://v3.ice.work/docs/guide/basic/config
|
||||
const minify = process.env.NODE_ENV === 'production' ? 'swc' : false;
|
||||
export default defineConfig(() => ({
|
||||
ssr: false,
|
||||
ssg: false,
|
||||
minify,
|
||||
|
||||
externals: {
|
||||
react: 'React',
|
||||
'react-dom': 'ReactDOM',
|
||||
'react-dom/client': 'ReactDOM',
|
||||
'@alifd/next': 'Next',
|
||||
lodash: 'var window._',
|
||||
'@alilc/lowcode-engine': 'var window.AliLowCodeEngine',
|
||||
},
|
||||
plugins: [
|
||||
fusion({
|
||||
importStyle: true,
|
||||
}),
|
||||
locales(),
|
||||
plugin(),
|
||||
],
|
||||
}));
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
{
|
||||
"name": "icejs3-demo-app",
|
||||
"version": "0.1.5",
|
||||
"description": "icejs 3 轻量级模板,使用 JavaScript,仅包含基础的 Layout。",
|
||||
"dependencies": {
|
||||
"moment": "^2.24.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router": "^6.9.0",
|
||||
"react-router-dom": "^6.9.0",
|
||||
"intl-messageformat": "^9.3.6",
|
||||
"@alifd/next": "1.26.15",
|
||||
"@ice/runtime": "^1.0.0",
|
||||
"@alilc/lowcode-datasource-engine": "^1.0.0",
|
||||
"@alilc/lowcode-datasource-url-params-handler": "^1.0.0",
|
||||
"@alilc/b6-page": "^0.1.0",
|
||||
"@alilc/b6-text": "^0.1.0",
|
||||
"@alilc/b6-compact-legao-builtin": "1.x",
|
||||
"antd": "3.x",
|
||||
"@alilc/b6-util-mocks": "1.x"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ice/app": "^3.0.0",
|
||||
"@types/react": "^18.0.0",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
"@types/node": "^18.11.17",
|
||||
"@ice/plugin-fusion": "^1.0.1",
|
||||
"@ice/plugin-moment-locales": "^1.0.0",
|
||||
"eslint": "^6.0.1",
|
||||
"stylelint": "^13.2.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "ice start",
|
||||
"build": "ice build",
|
||||
"lint": "npm run eslint && npm run stylelint",
|
||||
"eslint": "eslint --cache --ext .js,.jsx ./",
|
||||
"stylelint": "stylelint ./**/*.scss"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://gitlab.xxx.com/msd/leak-scan/tree/master"
|
||||
},
|
||||
"private": true,
|
||||
"originTemplate": "@alifd/scaffold-lite-js"
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
import { defineAppConfig } from 'ice';
|
||||
|
||||
// App config, see https://v3.ice.work/docs/guide/basic/app
|
||||
export default defineAppConfig(() => ({
|
||||
// Set your configs here.
|
||||
app: {
|
||||
rootId: 'App',
|
||||
},
|
||||
router: {
|
||||
type: 'browser',
|
||||
basename: '/',
|
||||
},
|
||||
}));
|
||||
@ -0,0 +1,3 @@
|
||||
const __$$constants = {};
|
||||
|
||||
export default __$$constants;
|
||||
@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
import { Meta, Title, Links, Main, Scripts } from 'ice';
|
||||
|
||||
export default function Document() {
|
||||
return (
|
||||
<html>
|
||||
<head>
|
||||
<meta charSet="utf-8" />
|
||||
<meta name="description" content="ice.js 3 lite scaffold" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<link rel="stylesheet" href="//alifd.alicdn.com/npm/@alifd/next/1.21.16/next.min.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
|
||||
<Meta />
|
||||
<Title />
|
||||
<Links />
|
||||
</head>
|
||||
<body>
|
||||
<Main />
|
||||
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/react/18.2.0/umd/react.development.js" />
|
||||
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/react-dom/18.2.0/umd/react-dom.development.js" />
|
||||
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/??react-router/6.9.0/react-router.production.min.js,react-router-dom/6.9.0/react-router-dom.production.min.js" />
|
||||
<script crossOrigin="anonymous" src="//alifd.alicdn.com/npm/@alifd/next/1.26.15/next.min.js" />
|
||||
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/prop-types/15.7.2/prop-types.js" />
|
||||
<script crossOrigin="anonymous" src="//g.alicdn.com/platform/c/??lodash/4.6.1/lodash.min.js,immutable/3.7.6/dist/immutable.min.js" />
|
||||
<Scripts />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
// 引入默认全局样式
|
||||
@import '@alifd/next/reset.scss';
|
||||
|
||||
body {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
@ -0,0 +1,77 @@
|
||||
const i18nConfig = {};
|
||||
|
||||
let locale =
|
||||
typeof navigator === 'object' && typeof navigator.language === 'string'
|
||||
? navigator.language
|
||||
: 'zh-CN';
|
||||
|
||||
const getLocale = () => locale;
|
||||
|
||||
const setLocale = (target) => {
|
||||
locale = target;
|
||||
};
|
||||
|
||||
const isEmptyVariables = (variables) =>
|
||||
(Array.isArray(variables) && variables.length === 0) ||
|
||||
(typeof variables === 'object' &&
|
||||
(!variables || Object.keys(variables).length === 0));
|
||||
|
||||
// 按低代码规范里面的要求进行变量替换
|
||||
const format = (msg, variables) =>
|
||||
typeof msg === 'string'
|
||||
? msg.replace(/\$\{(\w+)\}/g, (match, key) => variables?.[key] ?? '')
|
||||
: msg;
|
||||
|
||||
const i18nFormat = ({ id, defaultMessage, fallback }, variables) => {
|
||||
const msg =
|
||||
i18nConfig[locale]?.[id] ??
|
||||
i18nConfig[locale.replace('-', '_')]?.[id] ??
|
||||
defaultMessage;
|
||||
if (msg == null) {
|
||||
console.warn('[i18n]: unknown message id: %o (locale=%o)', id, locale);
|
||||
return fallback === undefined ? `${id}` : fallback;
|
||||
}
|
||||
|
||||
return format(msg, variables);
|
||||
};
|
||||
|
||||
const i18n = (id, params) => {
|
||||
return i18nFormat({ id }, params);
|
||||
};
|
||||
|
||||
// 将国际化的一些方法注入到目标对象&上下文中
|
||||
const _inject2 = (target) => {
|
||||
target.i18n = i18n;
|
||||
target.getLocale = getLocale;
|
||||
target.setLocale = (locale) => {
|
||||
setLocale(locale);
|
||||
target.forceUpdate();
|
||||
};
|
||||
target._i18nText = (t) => {
|
||||
// 优先取直接传过来的语料
|
||||
const localMsg = t[locale] ?? t[String(locale).replace('-', '_')];
|
||||
if (localMsg != null) {
|
||||
return format(localMsg, t.params);
|
||||
}
|
||||
|
||||
// 其次用项目级别的
|
||||
const projectMsg = i18nFormat({ id: t.key, fallback: null }, t.params);
|
||||
if (projectMsg != null) {
|
||||
return projectMsg;
|
||||
}
|
||||
|
||||
// 兜底用 use 指定的或默认语言的
|
||||
return format(t[t.use || 'zh-CN'] ?? t.en_US, t.params);
|
||||
};
|
||||
|
||||
// 注入到上下文中去
|
||||
if (target._context && target._context !== target) {
|
||||
Object.assign(target._context, {
|
||||
i18n,
|
||||
getLocale,
|
||||
setLocale: target.setLocale,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export { getLocale, setLocale, i18n, i18nFormat, _inject2 };
|
||||
@ -0,0 +1,14 @@
|
||||
|
||||
import React from 'react';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
export default function Footer() {
|
||||
return (
|
||||
<p className={styles.footer}>
|
||||
<span className={styles.logo}>Alibaba Fusion</span>
|
||||
<br />
|
||||
<span className={styles.copyright}>© 2019-现在 Alibaba Fusion & ICE</span>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
@ -0,0 +1,15 @@
|
||||
|
||||
.footer {
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.copyright {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
@ -0,0 +1,16 @@
|
||||
|
||||
import React from 'react';
|
||||
import { Link } from 'ice';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
export default function Logo({ image, text, url }) {
|
||||
return (
|
||||
<div className="logo">
|
||||
<Link to={url || '/'} className={styles.logo}>
|
||||
{image && <img src={image} alt="logo" />}
|
||||
<span>{text}</span>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
|
||||
.logo{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: $color-text1-1;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
|
||||
&:visited, &:link {
|
||||
color: $color-text1-1;
|
||||
}
|
||||
|
||||
img {
|
||||
height: 24px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,68 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Link, useLocation } from 'ice';
|
||||
import { Nav } from '@alifd/next';
|
||||
import { asideMenuConfig } from '../../menuConfig';
|
||||
|
||||
const { SubNav } = Nav;
|
||||
const NavItem = Nav.Item;
|
||||
|
||||
function getNavMenuItems(menusData) {
|
||||
if (!menusData) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return menusData
|
||||
.filter(item => item.name && !item.hideInMenu)
|
||||
.map((item, index) => getSubMenuOrItem(item, index));
|
||||
}
|
||||
|
||||
function getSubMenuOrItem(item, index) {
|
||||
if (item.children && item.children.some(child => child.name)) {
|
||||
const childrenItems = getNavMenuItems(item.children);
|
||||
|
||||
if (childrenItems && childrenItems.length > 0) {
|
||||
const subNav = (
|
||||
<SubNav key={index} icon={item.icon} label={item.name}>
|
||||
{childrenItems}
|
||||
</SubNav>
|
||||
);
|
||||
return subNav;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const navItem = (
|
||||
<NavItem key={item.path} icon={item.icon}>
|
||||
<Link to={item.path}>{item.name}</Link>
|
||||
</NavItem>
|
||||
);
|
||||
return navItem;
|
||||
}
|
||||
|
||||
const Navigation = (props, context) => {
|
||||
const location = useLocation();
|
||||
const { pathname } = location;
|
||||
const { isCollapse } = context;
|
||||
return (
|
||||
<Nav
|
||||
type="primary"
|
||||
selectedKeys={[pathname]}
|
||||
defaultSelectedKeys={[pathname]}
|
||||
embeddable
|
||||
openMode="single"
|
||||
iconOnly={isCollapse}
|
||||
hasArrow={false}
|
||||
mode={isCollapse ? 'popup' : 'inline'}
|
||||
>
|
||||
{getNavMenuItems(asideMenuConfig)}
|
||||
</Nav>
|
||||
);
|
||||
};
|
||||
|
||||
Navigation.contextTypes = {
|
||||
isCollapse: PropTypes.bool,
|
||||
};
|
||||
export default Navigation;
|
||||
|
||||
@ -0,0 +1,81 @@
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { Shell, ConfigProvider } from '@alifd/next';
|
||||
import PageNav from './components/PageNav';
|
||||
import Logo from './components/Logo';
|
||||
import Footer from './components/Footer';
|
||||
|
||||
(function() {
|
||||
const throttle = function(type, name, obj = window) {
|
||||
let running = false;
|
||||
|
||||
const func = () => {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
|
||||
running = true;
|
||||
requestAnimationFrame(() => {
|
||||
obj.dispatchEvent(new CustomEvent(name));
|
||||
running = false;
|
||||
});
|
||||
};
|
||||
|
||||
obj.addEventListener(type, func);
|
||||
};
|
||||
|
||||
throttle('resize', 'optimizedResize');
|
||||
})();
|
||||
|
||||
export default function BasicLayout({ children }) {
|
||||
const getDevice = width => {
|
||||
const isPhone =
|
||||
typeof navigator !== 'undefined' && navigator && navigator.userAgent.match(/phone/gi);
|
||||
|
||||
if (width < 680 || isPhone) {
|
||||
return 'phone';
|
||||
}
|
||||
if (width < 1280 && width > 680) {
|
||||
return 'tablet';
|
||||
}
|
||||
return 'desktop';
|
||||
};
|
||||
|
||||
const [device, setDevice] = useState(getDevice(NaN));
|
||||
window.addEventListener('optimizedResize', e => {
|
||||
setDevice(getDevice(e && e.target && e.target.innerWidth));
|
||||
});
|
||||
return (
|
||||
<ConfigProvider device={device}>
|
||||
<Shell
|
||||
type="dark"
|
||||
style={{
|
||||
minHeight: '100vh',
|
||||
}}
|
||||
>
|
||||
<Shell.Branding>
|
||||
<Logo
|
||||
image="https://img.alicdn.com/tfs/TB1.ZBecq67gK0jSZFHXXa9jVXa-904-826.png"
|
||||
text="Logo"
|
||||
/>
|
||||
</Shell.Branding>
|
||||
<Shell.Navigation
|
||||
direction="hoz"
|
||||
style={{
|
||||
marginRight: 10,
|
||||
}}
|
||||
></Shell.Navigation>
|
||||
<Shell.Action></Shell.Action>
|
||||
<Shell.Navigation>
|
||||
<PageNav />
|
||||
</Shell.Navigation>
|
||||
|
||||
<Shell.Content>{children}</Shell.Content>
|
||||
<Shell.Footer>
|
||||
<Footer />
|
||||
</Shell.Footer>
|
||||
</Shell>
|
||||
</ConfigProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
|
||||
const headerMenuConfig = [];
|
||||
const asideMenuConfig = [
|
||||
{
|
||||
name: 'Dashboard',
|
||||
path: '/',
|
||||
icon: 'smile',
|
||||
},
|
||||
];
|
||||
export { headerMenuConfig, asideMenuConfig };
|
||||
|
||||
@ -0,0 +1,118 @@
|
||||
// 注意: 出码引擎注入的临时变量默认都以 "__$$" 开头,禁止在搭建的代码中直接访问。
|
||||
// 例外:react 框架的导出名和各种组件名除外。
|
||||
import React from 'react';
|
||||
|
||||
import { Page } from '@alilc/b6-page';
|
||||
|
||||
import { Text } from '@alilc/b6-text';
|
||||
|
||||
import { createUrlParamsHandler as __$$createUrlParamsRequestHandler } from '@alilc/lowcode-datasource-url-params-handler';
|
||||
|
||||
import { create as __$$createDataSourceEngine } from '@alilc/lowcode-datasource-engine/runtime';
|
||||
|
||||
import utils from '../../utils';
|
||||
|
||||
import * as __$$i18n from '../../i18n';
|
||||
|
||||
import __$$constants from '../../constants';
|
||||
|
||||
import './index.css';
|
||||
|
||||
class Aaaa$$Page extends React.Component {
|
||||
_context = this;
|
||||
|
||||
_dataSourceConfig = this._defineDataSourceConfig();
|
||||
_dataSourceEngine = __$$createDataSourceEngine(this._dataSourceConfig, this, {
|
||||
runtimeConfig: true,
|
||||
requestHandlersMap: {
|
||||
urlParams: __$$createUrlParamsRequestHandler(window.location.search),
|
||||
},
|
||||
});
|
||||
|
||||
get dataSourceMap() {
|
||||
return this._dataSourceEngine.dataSourceMap || {};
|
||||
}
|
||||
|
||||
reloadDataSource = async () => {
|
||||
await this._dataSourceEngine.reloadDataSource();
|
||||
};
|
||||
|
||||
get constants() {
|
||||
return __$$constants || {};
|
||||
}
|
||||
|
||||
constructor(props, context) {
|
||||
super(props);
|
||||
|
||||
this.utils = utils;
|
||||
|
||||
__$$i18n._inject2(this);
|
||||
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
$ = () => null;
|
||||
|
||||
$$ = () => [];
|
||||
|
||||
_defineDataSourceConfig() {
|
||||
const _this = this;
|
||||
return {
|
||||
list: [
|
||||
{
|
||||
id: 'urlParams',
|
||||
type: 'urlParams',
|
||||
description: 'URL参数',
|
||||
options: function () {
|
||||
return {
|
||||
uri: '',
|
||||
};
|
||||
}.bind(_this),
|
||||
isInit: function () {
|
||||
return undefined;
|
||||
}.bind(_this),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._dataSourceEngine.reloadDataSource();
|
||||
}
|
||||
|
||||
render() {
|
||||
const __$$context = this._context || this;
|
||||
const { state } = __$$context;
|
||||
return (
|
||||
<div title="" backgroundColor="#fff" textColor="#333" style={{}}>
|
||||
<Text
|
||||
content="欢迎使用 BuildSuccess!sadsad"
|
||||
style={{}}
|
||||
fieldId="text_kp6ci11t"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Aaaa$$Page;
|
||||
|
||||
function __$$eval(expr) {
|
||||
try {
|
||||
return expr();
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
function __$$evalArray(expr) {
|
||||
const res = __$$eval(expr);
|
||||
return Array.isArray(res) ? res : [];
|
||||
}
|
||||
|
||||
function __$$createChildContext(oldContext, ext) {
|
||||
const childContext = {
|
||||
...oldContext,
|
||||
...ext,
|
||||
};
|
||||
childContext.__proto__ = oldContext;
|
||||
return childContext;
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
import { Outlet } from 'ice';
|
||||
import BasicLayout from '@/layouts/BasicLayout';
|
||||
|
||||
export default function Layout() {
|
||||
return (
|
||||
<BasicLayout>
|
||||
<Outlet />
|
||||
</BasicLayout>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
/// <reference types="@ice/app/types" />
|
||||
|
||||
export {};
|
||||
declare global {
|
||||
interface Window {
|
||||
g_config: Record<string, any>;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,61 @@
|
||||
import legaoBuiltin from '@alilc/b6-compact-legao-builtin';
|
||||
|
||||
import { message, Modal as modal } from 'antd';
|
||||
|
||||
import { mocks } from '@alilc/b6-util-mocks';
|
||||
|
||||
import { createRef } from 'react';
|
||||
|
||||
export class RefsManager {
|
||||
constructor() {
|
||||
this.refInsStore = {};
|
||||
}
|
||||
|
||||
clearNullRefs() {
|
||||
Object.keys(this.refInsStore).forEach((refName) => {
|
||||
const filteredInsList = this.refInsStore[refName].filter(
|
||||
(insRef) => !!insRef.current
|
||||
);
|
||||
if (filteredInsList.length > 0) {
|
||||
this.refInsStore[refName] = filteredInsList;
|
||||
} else {
|
||||
delete this.refInsStore[refName];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
get(refName) {
|
||||
this.clearNullRefs();
|
||||
if (this.refInsStore[refName] && this.refInsStore[refName].length > 0) {
|
||||
return this.refInsStore[refName][0].current;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
getAll(refName) {
|
||||
this.clearNullRefs();
|
||||
if (this.refInsStore[refName] && this.refInsStore[refName].length > 0) {
|
||||
return this.refInsStore[refName].map((i) => i.current);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
linkRef(refName) {
|
||||
const refIns = createRef();
|
||||
this.refInsStore[refName] = this.refInsStore[refName] || [];
|
||||
this.refInsStore[refName].push(refIns);
|
||||
return refIns;
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
legaoBuiltin,
|
||||
|
||||
message,
|
||||
|
||||
mocks,
|
||||
|
||||
modal,
|
||||
};
|
||||
123
modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/schema.json5
vendored
Normal file
123
modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/schema.json5
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
{
|
||||
version: '1.0.0',
|
||||
componentsMap: [
|
||||
{
|
||||
package: '@alilc/b6-page',
|
||||
version: '^0.1.0',
|
||||
componentName: 'Page',
|
||||
destructuring: true,
|
||||
exportName: 'Page',
|
||||
},
|
||||
{
|
||||
package: '@alilc/b6-text',
|
||||
version: '^0.1.0',
|
||||
componentName: 'Text',
|
||||
destructuring: true,
|
||||
exportName: 'Text',
|
||||
},
|
||||
],
|
||||
componentsTree: [
|
||||
{
|
||||
componentName: 'Page',
|
||||
id: 'node_ockp6ci0hm1',
|
||||
props: {
|
||||
title: '',
|
||||
backgroundColor: '#fff',
|
||||
textColor: '#333',
|
||||
style: {},
|
||||
},
|
||||
fileName: 'aaaa',
|
||||
dataSource: {
|
||||
list: [
|
||||
{
|
||||
id: 'urlParams',
|
||||
type: 'urlParams',
|
||||
description: 'URL参数',
|
||||
options: {
|
||||
uri: '',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Text',
|
||||
id: 'node_ockp6ci0hm2',
|
||||
props: {
|
||||
content: '欢迎使用 BuildSuccess!sadsad',
|
||||
style: {},
|
||||
fieldId: 'text_kp6ci11t',
|
||||
},
|
||||
},
|
||||
],
|
||||
meta: {
|
||||
router: '/aaaa',
|
||||
},
|
||||
methodsModule: {
|
||||
type: 'JSModule',
|
||||
compiled: '"use strict";\n\nObject.defineProperty(exports, "__esModule", {\n value: true\n});\nexports.helloPage = helloPage;\n\n/**\n * Private, and can be re-used functions\n * Actions panel help documentation:\n * @see https://yuque.antfin.com/docs/share/89ca7965-6387-4e3a-9964-81929ed48f1e\n */\nfunction printLog(obj) {\n console.info(obj);\n}\n/**\n * page function\n */\n\n\nfunction helloPage() {\n console.log(\'hello page\');\n}',
|
||||
source: "/**\n * Private, and can be re-used functions\n * Actions panel help documentation:\n * @see https://yuque.antfin.com/docs/share/89ca7965-6387-4e3a-9964-81929ed48f1e\n */\nfunction printLog(obj) {\n console.info(obj);\n}\n\n/**\n * page function\n */\nexport function helloPage() {\n console.log('hello page');\n}",
|
||||
},
|
||||
},
|
||||
],
|
||||
i18n: {},
|
||||
utils: [
|
||||
{
|
||||
name: 'legaoBuiltin',
|
||||
type: 'npm',
|
||||
content: {
|
||||
exportName: 'legaoBuiltin',
|
||||
package: '@alilc/b6-compact-legao-builtin',
|
||||
version: '1.x',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'message',
|
||||
type: 'npm',
|
||||
content: {
|
||||
package: 'antd',
|
||||
version: '3.x',
|
||||
destructuring: true,
|
||||
exportName: 'message',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'mocks',
|
||||
type: 'npm',
|
||||
content: {
|
||||
package: '@alilc/b6-util-mocks',
|
||||
version: '1.x',
|
||||
exportName: 'mocks',
|
||||
destructuring: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'modal',
|
||||
type: 'npm',
|
||||
content: {
|
||||
package: 'antd',
|
||||
version: '3.x',
|
||||
destructuring: true,
|
||||
exportName: 'Modal',
|
||||
},
|
||||
},
|
||||
],
|
||||
constants: {},
|
||||
dataSource: {
|
||||
list: [],
|
||||
},
|
||||
config: {
|
||||
sdkVersion: '1.0.3',
|
||||
historyMode: 'hash',
|
||||
targetRootID: 'root',
|
||||
miniAppBuildType: 'runtime',
|
||||
},
|
||||
meta: {
|
||||
name: 'jinyuan-test2',
|
||||
git_group: 'b6',
|
||||
project_name: 'jinyuan-test2',
|
||||
description: '瑾源测试',
|
||||
spma: 'spmademo',
|
||||
creator: '张三',
|
||||
},
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
defaults
|
||||
ios_saf 9
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
|
||||
# See https://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
# production
|
||||
build/
|
||||
dist/
|
||||
tmp/
|
||||
lib/
|
||||
|
||||
# misc
|
||||
.idea/
|
||||
.happypack
|
||||
.DS_Store
|
||||
*.swp
|
||||
*.dia~
|
||||
.ice
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
index.module.scss.d.ts
|
||||
|
||||
@ -0,0 +1 @@
|
||||
This project is generated by lowcode-code-generator & lowcode-solution-icejs3.
|
||||
@ -0,0 +1,90 @@
|
||||
import { join } from 'path';
|
||||
import { defineConfig } from '@ice/app';
|
||||
import _ from 'lodash';
|
||||
import fusion from '@ice/plugin-fusion';
|
||||
import locales from '@ice/plugin-moment-locales';
|
||||
import type { Plugin } from '@ice/app/esm/types';
|
||||
|
||||
interface PluginOptions {
|
||||
id: string;
|
||||
}
|
||||
|
||||
const plugin: Plugin<PluginOptions> = (options) => ({
|
||||
// name 可选,插件名称
|
||||
name: 'plugin-name',
|
||||
// setup 必选,用于定制工程构建配置
|
||||
setup: ({ onGetConfig, modifyUserConfig }) => {
|
||||
modifyUserConfig('codeSplitting', 'page');
|
||||
|
||||
onGetConfig((config) => {
|
||||
config.entry = {
|
||||
web: join(process.cwd(), '.ice/entry.client.tsx'),
|
||||
};
|
||||
|
||||
config.cssFilename = '[name].css';
|
||||
|
||||
config.configureWebpack = config.configureWebpack || [];
|
||||
config.configureWebpack?.push((webpackConfig) => {
|
||||
if (webpackConfig.output) {
|
||||
webpackConfig.output.filename = '[name].js';
|
||||
webpackConfig.output.chunkFilename = '[name].js';
|
||||
}
|
||||
return webpackConfig;
|
||||
});
|
||||
|
||||
config.swcOptions = _.merge(config.swcOptions, {
|
||||
compilationConfig: {
|
||||
jsc: {
|
||||
transform: {
|
||||
react: {
|
||||
runtime: 'classic',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// 解决 webpack publicPath 问题
|
||||
config.transforms = config.transforms || [];
|
||||
config.transforms.push((source: string, id: string) => {
|
||||
if (id.includes('.ice/entry.client.tsx')) {
|
||||
let code = `
|
||||
if (!__webpack_public_path__?.startsWith('http') && document.currentScript) {
|
||||
// @ts-ignore
|
||||
__webpack_public_path__ = document.currentScript.src.replace(/^(.*\\/)[^/]+$/, '$1');
|
||||
window.__ICE_ASSETS_MANIFEST__ = window.__ICE_ASSETS_MANIFEST__ || {};
|
||||
window.__ICE_ASSETS_MANIFEST__.publicPath = __webpack_public_path__;
|
||||
}
|
||||
`;
|
||||
code += source;
|
||||
return { code };
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
// The project config, see https://v3.ice.work/docs/guide/basic/config
|
||||
const minify = process.env.NODE_ENV === 'production' ? 'swc' : false;
|
||||
export default defineConfig(() => ({
|
||||
ssr: false,
|
||||
ssg: false,
|
||||
minify,
|
||||
|
||||
externals: {
|
||||
react: 'React',
|
||||
'react-dom': 'ReactDOM',
|
||||
'react-dom/client': 'ReactDOM',
|
||||
'@alifd/next': 'Next',
|
||||
lodash: 'var window._',
|
||||
'@alilc/lowcode-engine': 'var window.AliLowCodeEngine',
|
||||
},
|
||||
plugins: [
|
||||
fusion({
|
||||
importStyle: true,
|
||||
}),
|
||||
locales(),
|
||||
plugin(),
|
||||
],
|
||||
}));
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "icejs3-demo-app",
|
||||
"version": "0.1.5",
|
||||
"description": "icejs 3 轻量级模板,使用 JavaScript,仅包含基础的 Layout。",
|
||||
"dependencies": {
|
||||
"moment": "^2.24.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router": "^6.9.0",
|
||||
"react-router-dom": "^6.9.0",
|
||||
"intl-messageformat": "^9.3.6",
|
||||
"@alifd/next": "1.19.18",
|
||||
"@ice/runtime": "^1.0.0",
|
||||
"@alilc/lowcode-datasource-engine": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ice/app": "^3.0.0",
|
||||
"@types/react": "^18.0.0",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
"@types/node": "^18.11.17",
|
||||
"@ice/plugin-fusion": "^1.0.1",
|
||||
"@ice/plugin-moment-locales": "^1.0.0",
|
||||
"eslint": "^6.0.1",
|
||||
"stylelint": "^13.2.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "ice start",
|
||||
"build": "ice build",
|
||||
"lint": "npm run eslint && npm run stylelint",
|
||||
"eslint": "eslint --cache --ext .js,.jsx ./",
|
||||
"stylelint": "stylelint ./**/*.scss"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://gitlab.xxx.com/msd/leak-scan/tree/master"
|
||||
},
|
||||
"private": true,
|
||||
"originTemplate": "@alifd/scaffold-lite-js"
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
import { defineAppConfig } from 'ice';
|
||||
|
||||
// App config, see https://v3.ice.work/docs/guide/basic/app
|
||||
export default defineAppConfig(() => ({
|
||||
// Set your configs here.
|
||||
app: {
|
||||
rootId: 'App',
|
||||
},
|
||||
router: {
|
||||
type: 'browser',
|
||||
basename: '/',
|
||||
},
|
||||
}));
|
||||
@ -0,0 +1,3 @@
|
||||
const __$$constants = { ENV: 'prod', DOMAIN: 'xxx.xxx.com' };
|
||||
|
||||
export default __$$constants;
|
||||
@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
import { Meta, Title, Links, Main, Scripts } from 'ice';
|
||||
|
||||
export default function Document() {
|
||||
return (
|
||||
<html>
|
||||
<head>
|
||||
<meta charSet="utf-8" />
|
||||
<meta name="description" content="ice.js 3 lite scaffold" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<link rel="stylesheet" href="//alifd.alicdn.com/npm/@alifd/next/1.21.16/next.min.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
|
||||
<Meta />
|
||||
<Title />
|
||||
<Links />
|
||||
</head>
|
||||
<body>
|
||||
<Main />
|
||||
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/react/18.2.0/umd/react.development.js" />
|
||||
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/react-dom/18.2.0/umd/react-dom.development.js" />
|
||||
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/??react-router/6.9.0/react-router.production.min.js,react-router-dom/6.9.0/react-router-dom.production.min.js" />
|
||||
<script crossOrigin="anonymous" src="//alifd.alicdn.com/npm/@alifd/next/1.26.15/next.min.js" />
|
||||
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/prop-types/15.7.2/prop-types.js" />
|
||||
<script crossOrigin="anonymous" src="//g.alicdn.com/platform/c/??lodash/4.6.1/lodash.min.js,immutable/3.7.6/dist/immutable.min.js" />
|
||||
<Scripts />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
// 引入默认全局样式
|
||||
@import '@alifd/next/reset.scss';
|
||||
|
||||
body {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
body {
|
||||
font-size: 12px;
|
||||
}
|
||||
.table {
|
||||
width: 100px;
|
||||
}
|
||||
@ -0,0 +1,86 @@
|
||||
const i18nConfig = {
|
||||
'zh-CN': {
|
||||
'i18n-jwg27yo4': '你好',
|
||||
'i18n-jwg27yo3': '中国',
|
||||
},
|
||||
'en-US': {
|
||||
'i18n-jwg27yo4': 'Hello',
|
||||
'i18n-jwg27yo3': 'China',
|
||||
},
|
||||
};
|
||||
|
||||
let locale =
|
||||
typeof navigator === 'object' && typeof navigator.language === 'string'
|
||||
? navigator.language
|
||||
: 'zh-CN';
|
||||
|
||||
const getLocale = () => locale;
|
||||
|
||||
const setLocale = (target) => {
|
||||
locale = target;
|
||||
};
|
||||
|
||||
const isEmptyVariables = (variables) =>
|
||||
(Array.isArray(variables) && variables.length === 0) ||
|
||||
(typeof variables === 'object' &&
|
||||
(!variables || Object.keys(variables).length === 0));
|
||||
|
||||
// 按低代码规范里面的要求进行变量替换
|
||||
const format = (msg, variables) =>
|
||||
typeof msg === 'string'
|
||||
? msg.replace(/\$\{(\w+)\}/g, (match, key) => variables?.[key] ?? '')
|
||||
: msg;
|
||||
|
||||
const i18nFormat = ({ id, defaultMessage, fallback }, variables) => {
|
||||
const msg =
|
||||
i18nConfig[locale]?.[id] ??
|
||||
i18nConfig[locale.replace('-', '_')]?.[id] ??
|
||||
defaultMessage;
|
||||
if (msg == null) {
|
||||
console.warn('[i18n]: unknown message id: %o (locale=%o)', id, locale);
|
||||
return fallback === undefined ? `${id}` : fallback;
|
||||
}
|
||||
|
||||
return format(msg, variables);
|
||||
};
|
||||
|
||||
const i18n = (id, params) => {
|
||||
return i18nFormat({ id }, params);
|
||||
};
|
||||
|
||||
// 将国际化的一些方法注入到目标对象&上下文中
|
||||
const _inject2 = (target) => {
|
||||
target.i18n = i18n;
|
||||
target.getLocale = getLocale;
|
||||
target.setLocale = (locale) => {
|
||||
setLocale(locale);
|
||||
target.forceUpdate();
|
||||
};
|
||||
target._i18nText = (t) => {
|
||||
// 优先取直接传过来的语料
|
||||
const localMsg = t[locale] ?? t[String(locale).replace('-', '_')];
|
||||
if (localMsg != null) {
|
||||
return format(localMsg, t.params);
|
||||
}
|
||||
|
||||
// 其次用项目级别的
|
||||
const projectMsg = i18nFormat({ id: t.key, fallback: null }, t.params);
|
||||
if (projectMsg != null) {
|
||||
return projectMsg;
|
||||
}
|
||||
|
||||
// 兜底用 use 指定的或默认语言的
|
||||
return format(t[t.use || 'zh-CN'] ?? t.en_US, t.params);
|
||||
};
|
||||
|
||||
// 注入到上下文中去
|
||||
if (target._context && target._context !== target) {
|
||||
Object.assign(target._context, {
|
||||
i18n,
|
||||
getLocale,
|
||||
setLocale: target.setLocale,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export { getLocale, setLocale, i18n, i18nFormat, _inject2 };
|
||||
@ -0,0 +1,14 @@
|
||||
|
||||
import React from 'react';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
export default function Footer() {
|
||||
return (
|
||||
<p className={styles.footer}>
|
||||
<span className={styles.logo}>Alibaba Fusion</span>
|
||||
<br />
|
||||
<span className={styles.copyright}>© 2019-现在 Alibaba Fusion & ICE</span>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
@ -0,0 +1,15 @@
|
||||
|
||||
.footer {
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.copyright {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
@ -0,0 +1,16 @@
|
||||
|
||||
import React from 'react';
|
||||
import { Link } from 'ice';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
export default function Logo({ image, text, url }) {
|
||||
return (
|
||||
<div className="logo">
|
||||
<Link to={url || '/'} className={styles.logo}>
|
||||
{image && <img src={image} alt="logo" />}
|
||||
<span>{text}</span>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
|
||||
.logo{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: $color-text1-1;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
|
||||
&:visited, &:link {
|
||||
color: $color-text1-1;
|
||||
}
|
||||
|
||||
img {
|
||||
height: 24px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,68 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Link, useLocation } from 'ice';
|
||||
import { Nav } from '@alifd/next';
|
||||
import { asideMenuConfig } from '../../menuConfig';
|
||||
|
||||
const { SubNav } = Nav;
|
||||
const NavItem = Nav.Item;
|
||||
|
||||
function getNavMenuItems(menusData) {
|
||||
if (!menusData) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return menusData
|
||||
.filter(item => item.name && !item.hideInMenu)
|
||||
.map((item, index) => getSubMenuOrItem(item, index));
|
||||
}
|
||||
|
||||
function getSubMenuOrItem(item, index) {
|
||||
if (item.children && item.children.some(child => child.name)) {
|
||||
const childrenItems = getNavMenuItems(item.children);
|
||||
|
||||
if (childrenItems && childrenItems.length > 0) {
|
||||
const subNav = (
|
||||
<SubNav key={index} icon={item.icon} label={item.name}>
|
||||
{childrenItems}
|
||||
</SubNav>
|
||||
);
|
||||
return subNav;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const navItem = (
|
||||
<NavItem key={item.path} icon={item.icon}>
|
||||
<Link to={item.path}>{item.name}</Link>
|
||||
</NavItem>
|
||||
);
|
||||
return navItem;
|
||||
}
|
||||
|
||||
const Navigation = (props, context) => {
|
||||
const location = useLocation();
|
||||
const { pathname } = location;
|
||||
const { isCollapse } = context;
|
||||
return (
|
||||
<Nav
|
||||
type="primary"
|
||||
selectedKeys={[pathname]}
|
||||
defaultSelectedKeys={[pathname]}
|
||||
embeddable
|
||||
openMode="single"
|
||||
iconOnly={isCollapse}
|
||||
hasArrow={false}
|
||||
mode={isCollapse ? 'popup' : 'inline'}
|
||||
>
|
||||
{getNavMenuItems(asideMenuConfig)}
|
||||
</Nav>
|
||||
);
|
||||
};
|
||||
|
||||
Navigation.contextTypes = {
|
||||
isCollapse: PropTypes.bool,
|
||||
};
|
||||
export default Navigation;
|
||||
|
||||
@ -0,0 +1,81 @@
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { Shell, ConfigProvider } from '@alifd/next';
|
||||
import PageNav from './components/PageNav';
|
||||
import Logo from './components/Logo';
|
||||
import Footer from './components/Footer';
|
||||
|
||||
(function() {
|
||||
const throttle = function(type, name, obj = window) {
|
||||
let running = false;
|
||||
|
||||
const func = () => {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
|
||||
running = true;
|
||||
requestAnimationFrame(() => {
|
||||
obj.dispatchEvent(new CustomEvent(name));
|
||||
running = false;
|
||||
});
|
||||
};
|
||||
|
||||
obj.addEventListener(type, func);
|
||||
};
|
||||
|
||||
throttle('resize', 'optimizedResize');
|
||||
})();
|
||||
|
||||
export default function BasicLayout({ children }) {
|
||||
const getDevice = width => {
|
||||
const isPhone =
|
||||
typeof navigator !== 'undefined' && navigator && navigator.userAgent.match(/phone/gi);
|
||||
|
||||
if (width < 680 || isPhone) {
|
||||
return 'phone';
|
||||
}
|
||||
if (width < 1280 && width > 680) {
|
||||
return 'tablet';
|
||||
}
|
||||
return 'desktop';
|
||||
};
|
||||
|
||||
const [device, setDevice] = useState(getDevice(NaN));
|
||||
window.addEventListener('optimizedResize', e => {
|
||||
setDevice(getDevice(e && e.target && e.target.innerWidth));
|
||||
});
|
||||
return (
|
||||
<ConfigProvider device={device}>
|
||||
<Shell
|
||||
type="dark"
|
||||
style={{
|
||||
minHeight: '100vh',
|
||||
}}
|
||||
>
|
||||
<Shell.Branding>
|
||||
<Logo
|
||||
image="https://img.alicdn.com/tfs/TB1.ZBecq67gK0jSZFHXXa9jVXa-904-826.png"
|
||||
text="Logo"
|
||||
/>
|
||||
</Shell.Branding>
|
||||
<Shell.Navigation
|
||||
direction="hoz"
|
||||
style={{
|
||||
marginRight: 10,
|
||||
}}
|
||||
></Shell.Navigation>
|
||||
<Shell.Action></Shell.Action>
|
||||
<Shell.Navigation>
|
||||
<PageNav />
|
||||
</Shell.Navigation>
|
||||
|
||||
<Shell.Content>{children}</Shell.Content>
|
||||
<Shell.Footer>
|
||||
<Footer />
|
||||
</Shell.Footer>
|
||||
</Shell>
|
||||
</ConfigProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
|
||||
const headerMenuConfig = [];
|
||||
const asideMenuConfig = [
|
||||
{
|
||||
name: 'Dashboard',
|
||||
path: '/',
|
||||
icon: 'smile',
|
||||
},
|
||||
];
|
||||
export { headerMenuConfig, asideMenuConfig };
|
||||
|
||||
@ -0,0 +1,119 @@
|
||||
// 注意: 出码引擎注入的临时变量默认都以 "__$$" 开头,禁止在搭建的代码中直接访问。
|
||||
// 例外:react 框架的导出名和各种组件名除外。
|
||||
import React from 'react';
|
||||
|
||||
import { Form, Input, NumberPicker, Select, Button } from '@alifd/next';
|
||||
|
||||
import utils, { RefsManager } from '../../utils';
|
||||
|
||||
import * as __$$i18n from '../../i18n';
|
||||
|
||||
import __$$constants from '../../constants';
|
||||
|
||||
import './index.css';
|
||||
|
||||
class Test$$Page extends React.Component {
|
||||
_context = this;
|
||||
|
||||
get constants() {
|
||||
return __$$constants || {};
|
||||
}
|
||||
|
||||
constructor(props, context) {
|
||||
super(props);
|
||||
|
||||
this.utils = utils;
|
||||
|
||||
this._refsManager = new RefsManager();
|
||||
|
||||
__$$i18n._inject2(this);
|
||||
|
||||
this.state = { text: 'outter' };
|
||||
}
|
||||
|
||||
$ = (refName) => {
|
||||
return this._refsManager.get(refName);
|
||||
};
|
||||
|
||||
$$ = (refName) => {
|
||||
return this._refsManager.getAll(refName);
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
console.log('componentDidMount');
|
||||
}
|
||||
|
||||
render() {
|
||||
const __$$context = this._context || this;
|
||||
const { state } = __$$context;
|
||||
return (
|
||||
<div ref={this._refsManager.linkRef('outterView')} autoLoading={true}>
|
||||
<Form
|
||||
labelCol={__$$eval(() => this.state.colNum)}
|
||||
style={{}}
|
||||
ref={this._refsManager.linkRef('testForm')}
|
||||
>
|
||||
<Form.Item
|
||||
label={__$$eval(() => this.i18n('i18n-jwg27yo4'))}
|
||||
name="name"
|
||||
initValue="李雷"
|
||||
>
|
||||
<Input placeholder="请输入" size="medium" style={{ width: 320 }} />
|
||||
</Form.Item>
|
||||
<Form.Item label="年龄:" name="age" initValue="22">
|
||||
<NumberPicker size="medium" type="normal" />
|
||||
</Form.Item>
|
||||
<Form.Item label="职业:" name="profession">
|
||||
<Select
|
||||
dataSource={[
|
||||
{ label: '教师', value: 't' },
|
||||
{ label: '医生', value: 'd' },
|
||||
{ label: '歌手', value: 's' },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<Button.Group>
|
||||
<Button
|
||||
type="primary"
|
||||
style={{ margin: '0 5px 0 5px' }}
|
||||
htmlType="submit"
|
||||
>
|
||||
提交
|
||||
</Button>
|
||||
<Button
|
||||
type="normal"
|
||||
style={{ margin: '0 5px 0 5px' }}
|
||||
htmlType="reset"
|
||||
>
|
||||
重置
|
||||
</Button>
|
||||
</Button.Group>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Test$$Page;
|
||||
|
||||
function __$$eval(expr) {
|
||||
try {
|
||||
return expr();
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
function __$$evalArray(expr) {
|
||||
const res = __$$eval(expr);
|
||||
return Array.isArray(res) ? res : [];
|
||||
}
|
||||
|
||||
function __$$createChildContext(oldContext, ext) {
|
||||
const childContext = {
|
||||
...oldContext,
|
||||
...ext,
|
||||
};
|
||||
childContext.__proto__ = oldContext;
|
||||
return childContext;
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
import { Outlet } from 'ice';
|
||||
import BasicLayout from '@/layouts/BasicLayout';
|
||||
|
||||
export default function Layout() {
|
||||
return (
|
||||
<BasicLayout>
|
||||
<Outlet />
|
||||
</BasicLayout>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
/// <reference types="@ice/app/types" />
|
||||
|
||||
export {};
|
||||
declare global {
|
||||
interface Window {
|
||||
g_config: Record<string, any>;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,47 @@
|
||||
import { createRef } from 'react';
|
||||
|
||||
export class RefsManager {
|
||||
constructor() {
|
||||
this.refInsStore = {};
|
||||
}
|
||||
|
||||
clearNullRefs() {
|
||||
Object.keys(this.refInsStore).forEach((refName) => {
|
||||
const filteredInsList = this.refInsStore[refName].filter(
|
||||
(insRef) => !!insRef.current
|
||||
);
|
||||
if (filteredInsList.length > 0) {
|
||||
this.refInsStore[refName] = filteredInsList;
|
||||
} else {
|
||||
delete this.refInsStore[refName];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
get(refName) {
|
||||
this.clearNullRefs();
|
||||
if (this.refInsStore[refName] && this.refInsStore[refName].length > 0) {
|
||||
return this.refInsStore[refName][0].current;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
getAll(refName) {
|
||||
this.clearNullRefs();
|
||||
if (this.refInsStore[refName] && this.refInsStore[refName].length > 0) {
|
||||
return this.refInsStore[refName].map((i) => i.current);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
linkRef(refName) {
|
||||
const refIns = createRef();
|
||||
this.refInsStore[refName] = this.refInsStore[refName] || [];
|
||||
this.refInsStore[refName].push(refIns);
|
||||
return refIns;
|
||||
}
|
||||
}
|
||||
|
||||
export default {};
|
||||
256
modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/schema.json5
vendored
Normal file
256
modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/schema.json5
vendored
Normal file
@ -0,0 +1,256 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"componentsMap": [
|
||||
{
|
||||
"componentName": "Button",
|
||||
"package": "@alifd/next",
|
||||
"version": "1.19.18",
|
||||
"destructuring": true,
|
||||
"exportName": "Button"
|
||||
},
|
||||
{
|
||||
"componentName": "Button.Group",
|
||||
"package": "@alifd/next",
|
||||
"version": "1.19.18",
|
||||
"destructuring": true,
|
||||
"exportName": "Button",
|
||||
"subName": "Group"
|
||||
},
|
||||
{
|
||||
"componentName": "Input",
|
||||
"package": "@alifd/next",
|
||||
"version": "1.19.18",
|
||||
"destructuring": true,
|
||||
"exportName": "Input"
|
||||
},
|
||||
{
|
||||
"componentName": "Form",
|
||||
"package": "@alifd/next",
|
||||
"version": "1.19.18",
|
||||
"destructuring": true,
|
||||
"exportName": "Form"
|
||||
},
|
||||
{
|
||||
"componentName": "Form.Item",
|
||||
"package": "@alifd/next",
|
||||
"version": "1.19.18",
|
||||
"destructuring": true,
|
||||
"exportName": "Form",
|
||||
"subName": "Item"
|
||||
},
|
||||
{
|
||||
"componentName": "NumberPicker",
|
||||
"package": "@alifd/next",
|
||||
"version": "1.19.18",
|
||||
"destructuring": true,
|
||||
"exportName": "NumberPicker"
|
||||
},
|
||||
{
|
||||
"componentName": "Select",
|
||||
"package": "@alifd/next",
|
||||
"version": "1.19.18",
|
||||
"destructuring": true,
|
||||
"exportName": "Select"
|
||||
}
|
||||
],
|
||||
"componentsTree": [
|
||||
{
|
||||
"componentName": "Page",
|
||||
"id": "node$1",
|
||||
"meta": {
|
||||
"title": "测试",
|
||||
"router": "/"
|
||||
},
|
||||
"props": {
|
||||
"ref": "outterView",
|
||||
"autoLoading": true
|
||||
},
|
||||
"fileName": "test",
|
||||
"state": {
|
||||
"text": "outter"
|
||||
},
|
||||
"lifeCycles": {
|
||||
"componentDidMount": {
|
||||
"type": "JSFunction",
|
||||
"value": "function() { console.log('componentDidMount'); }"
|
||||
}
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"componentName": "Form",
|
||||
"id": "node$2",
|
||||
"props": {
|
||||
"labelCol": {
|
||||
"type": "JSExpression",
|
||||
"value": "this.state.colNum"
|
||||
},
|
||||
"style": {},
|
||||
"ref": "testForm"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"componentName": "Form.Item",
|
||||
"id": "node$3",
|
||||
"props": {
|
||||
"label": {
|
||||
type: 'JSExpression',
|
||||
value: 'this.i18n("i18n-jwg27yo4")',
|
||||
},
|
||||
"name": "name",
|
||||
"initValue": "李雷"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"componentName": "Input",
|
||||
"id": "node$4",
|
||||
"props": {
|
||||
"placeholder": "请输入",
|
||||
"size": "medium",
|
||||
"style": {
|
||||
"width": 320
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"componentName": "Form.Item",
|
||||
"id": "node$5",
|
||||
"props": {
|
||||
"label": "年龄:",
|
||||
"name": "age",
|
||||
"initValue": "22"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"componentName": "NumberPicker",
|
||||
"id": "node$6",
|
||||
"props": {
|
||||
"size": "medium",
|
||||
"type": "normal"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"componentName": "Form.Item",
|
||||
"id": "node$7",
|
||||
"props": {
|
||||
"label": "职业:",
|
||||
"name": "profession"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"componentName": "Select",
|
||||
"id": "node$8",
|
||||
"props": {
|
||||
"dataSource": [
|
||||
{
|
||||
"label": "教师",
|
||||
"value": "t"
|
||||
},
|
||||
{
|
||||
"label": "医生",
|
||||
"value": "d"
|
||||
},
|
||||
{
|
||||
"label": "歌手",
|
||||
"value": "s"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"componentName": "Div",
|
||||
"id": "node$9",
|
||||
"props": {
|
||||
"style": {
|
||||
"textAlign": "center"
|
||||
}
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"componentName": "Button.Group",
|
||||
"id": "node$a",
|
||||
"props": {},
|
||||
"children": [
|
||||
{
|
||||
"componentName": "Button",
|
||||
"id": "node$b",
|
||||
"props": {
|
||||
"type": "primary",
|
||||
"style": {
|
||||
"margin": "0 5px 0 5px"
|
||||
},
|
||||
"htmlType": "submit"
|
||||
},
|
||||
"children": [
|
||||
"提交"
|
||||
]
|
||||
},
|
||||
{
|
||||
"componentName": "Button",
|
||||
"id": "node$d",
|
||||
"props": {
|
||||
"type": "normal",
|
||||
"style": {
|
||||
"margin": "0 5px 0 5px"
|
||||
},
|
||||
"htmlType": "reset"
|
||||
},
|
||||
"children": [
|
||||
"重置"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"constants": {
|
||||
"ENV": "prod",
|
||||
"DOMAIN": "xxx.xxx.com"
|
||||
},
|
||||
"i18n": {
|
||||
"zh-CN": {
|
||||
"i18n-jwg27yo4": "你好",
|
||||
"i18n-jwg27yo3": "中国"
|
||||
},
|
||||
"en-US": {
|
||||
"i18n-jwg27yo4": "Hello",
|
||||
"i18n-jwg27yo3": "China"
|
||||
}
|
||||
},
|
||||
"css": "body {font-size: 12px;} .table { width: 100px;}",
|
||||
"config": {
|
||||
"sdkVersion": "1.0.3",
|
||||
"historyMode": "hash",
|
||||
"targetRootID": "J_Container",
|
||||
"layout": {
|
||||
"componentName": "BasicLayout",
|
||||
"props": {
|
||||
"logo": "...",
|
||||
"name": "测试网站"
|
||||
}
|
||||
},
|
||||
"theme": {
|
||||
"package": "@alife/theme-fusion",
|
||||
"version": "^0.1.0",
|
||||
"primary": "#ff9966"
|
||||
}
|
||||
},
|
||||
"meta": {
|
||||
"name": "demo应用",
|
||||
"git_group": "appGroup",
|
||||
"project_name": "app_demo",
|
||||
"description": "这是一个测试应用",
|
||||
"spma": "spa23d",
|
||||
"creator": "月飞"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
defaults
|
||||
ios_saf 9
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
|
||||
# See https://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
# production
|
||||
build/
|
||||
dist/
|
||||
tmp/
|
||||
lib/
|
||||
|
||||
# misc
|
||||
.idea/
|
||||
.happypack
|
||||
.DS_Store
|
||||
*.swp
|
||||
*.dia~
|
||||
.ice
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
index.module.scss.d.ts
|
||||
|
||||
@ -0,0 +1 @@
|
||||
This project is generated by lowcode-code-generator & lowcode-solution-icejs3.
|
||||
@ -0,0 +1,90 @@
|
||||
import { join } from 'path';
|
||||
import { defineConfig } from '@ice/app';
|
||||
import _ from 'lodash';
|
||||
import fusion from '@ice/plugin-fusion';
|
||||
import locales from '@ice/plugin-moment-locales';
|
||||
import type { Plugin } from '@ice/app/esm/types';
|
||||
|
||||
interface PluginOptions {
|
||||
id: string;
|
||||
}
|
||||
|
||||
const plugin: Plugin<PluginOptions> = (options) => ({
|
||||
// name 可选,插件名称
|
||||
name: 'plugin-name',
|
||||
// setup 必选,用于定制工程构建配置
|
||||
setup: ({ onGetConfig, modifyUserConfig }) => {
|
||||
modifyUserConfig('codeSplitting', 'page');
|
||||
|
||||
onGetConfig((config) => {
|
||||
config.entry = {
|
||||
web: join(process.cwd(), '.ice/entry.client.tsx'),
|
||||
};
|
||||
|
||||
config.cssFilename = '[name].css';
|
||||
|
||||
config.configureWebpack = config.configureWebpack || [];
|
||||
config.configureWebpack?.push((webpackConfig) => {
|
||||
if (webpackConfig.output) {
|
||||
webpackConfig.output.filename = '[name].js';
|
||||
webpackConfig.output.chunkFilename = '[name].js';
|
||||
}
|
||||
return webpackConfig;
|
||||
});
|
||||
|
||||
config.swcOptions = _.merge(config.swcOptions, {
|
||||
compilationConfig: {
|
||||
jsc: {
|
||||
transform: {
|
||||
react: {
|
||||
runtime: 'classic',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// 解决 webpack publicPath 问题
|
||||
config.transforms = config.transforms || [];
|
||||
config.transforms.push((source: string, id: string) => {
|
||||
if (id.includes('.ice/entry.client.tsx')) {
|
||||
let code = `
|
||||
if (!__webpack_public_path__?.startsWith('http') && document.currentScript) {
|
||||
// @ts-ignore
|
||||
__webpack_public_path__ = document.currentScript.src.replace(/^(.*\\/)[^/]+$/, '$1');
|
||||
window.__ICE_ASSETS_MANIFEST__ = window.__ICE_ASSETS_MANIFEST__ || {};
|
||||
window.__ICE_ASSETS_MANIFEST__.publicPath = __webpack_public_path__;
|
||||
}
|
||||
`;
|
||||
code += source;
|
||||
return { code };
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
// The project config, see https://v3.ice.work/docs/guide/basic/config
|
||||
const minify = process.env.NODE_ENV === 'production' ? 'swc' : false;
|
||||
export default defineConfig(() => ({
|
||||
ssr: false,
|
||||
ssg: false,
|
||||
minify,
|
||||
|
||||
externals: {
|
||||
react: 'React',
|
||||
'react-dom': 'ReactDOM',
|
||||
'react-dom/client': 'ReactDOM',
|
||||
'@alifd/next': 'Next',
|
||||
lodash: 'var window._',
|
||||
'@alilc/lowcode-engine': 'var window.AliLowCodeEngine',
|
||||
},
|
||||
plugins: [
|
||||
fusion({
|
||||
importStyle: true,
|
||||
}),
|
||||
locales(),
|
||||
plugin(),
|
||||
],
|
||||
}));
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user