From b607b246d6681c778aa57ebf990997d4119167a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?LeoYuan=20=E8=A2=81=E5=8A=9B=E7=9A=93?= Date: Wed, 29 Mar 2023 10:22:17 +0800 Subject: [PATCH] feat: add a new built-in solution icejs3 which is corresponding to the latest icejs framework --- modules/code-generator/package.json | 2 +- modules/code-generator/src/index.ts | 7 + .../src/plugins/common/esmodule.ts | 4 +- .../plugins/project/framework/icejs3/index.ts | 17 + .../framework/icejs3/plugins/appConfig.ts | 50 + .../framework/icejs3/plugins/buildConfig.ts | 165 ++ .../framework/icejs3/plugins/globalStyle.ts | 56 + .../framework/icejs3/plugins/layout.ts | 41 + .../framework/icejs3/plugins/packageJSON.ts | 119 ++ .../icejs3/template/files/README.md.ts | 12 + .../icejs3/template/files/browserslistrc.ts | 14 + .../icejs3/template/files/document.ts | 41 + .../icejs3/template/files/gitignore.ts | 36 + .../components/Footer/index.jsx.ts | 25 + .../components/Footer/index.style.ts | 26 + .../BasicLayout/components/Logo/index.jsx.ts | 27 + .../components/Logo/index.style.ts | 31 + .../components/PageNav/index.jsx.ts | 79 + .../src/layouts/BasicLayout/index.jsx.ts | 92 + .../src/layouts/BasicLayout/menuConfig.js.ts | 22 + .../icejs3/template/files/tsconfig.ts | 38 + .../icejs3/template/files/typings.ts | 20 + .../framework/icejs3/template/index.ts | 57 + .../framework/icejs3/template/static-files.ts | 33 + .../code-generator/src/solutions/icejs3.ts | 109 ++ modules/code-generator/src/utils/format.ts | 12 + modules/code-generator/src/utils/theme.ts | 20 + .../expected/demo-project/.browserslistrc | 3 + .../demo1/expected/demo-project/.gitignore | 25 + .../demo1/expected/demo-project/README.md | 1 + .../expected/demo-project/ice.config.mts | 90 + .../demo1/expected/demo-project/package.json | 44 + .../demo1/expected/demo-project/src/app.ts | 13 + .../expected/demo-project/src/constants.js | 3 + .../expected/demo-project/src/document.tsx | 29 + .../expected/demo-project/src/global.scss | 13 + .../demo1/expected/demo-project/src/i18n.js | 77 + .../BasicLayout/components/Footer/index.jsx | 14 + .../components/Footer/index.module.scss | 15 + .../BasicLayout/components/Logo/index.jsx | 16 + .../components/Logo/index.module.scss | 20 + .../BasicLayout/components/PageNav/index.jsx | 68 + .../src/layouts/BasicLayout/index.jsx | 81 + .../src/layouts/BasicLayout/menuConfig.js | 11 + .../demo-project/src/pages/Test/index.css | 0 .../demo-project/src/pages/Test/index.jsx | 195 ++ .../demo-project/src/pages/layout.jsx | 10 + .../expected/demo-project/src/typings.d.ts | 9 + .../demo1/expected/demo-project/src/utils.js | 47 + .../test-cases/icejs3-app/demo1/schema.json5 | 276 +++ .../expected/demo-project/.browserslistrc | 3 + .../expected/demo-project/.gitignore | 25 + .../expected/demo-project/README.md | 1 + .../expected/demo-project/ice.config.mts | 90 + .../expected/demo-project/package.json | 48 + .../expected/demo-project/src/app.ts | 13 + .../expected/demo-project/src/constants.js | 3 + .../expected/demo-project/src/document.tsx | 29 + .../expected/demo-project/src/global.scss | 6 + .../expected/demo-project/src/i18n.js | 77 + .../BasicLayout/components/Footer/index.jsx | 14 + .../components/Footer/index.module.scss | 15 + .../BasicLayout/components/Logo/index.jsx | 16 + .../components/Logo/index.module.scss | 20 + .../BasicLayout/components/PageNav/index.jsx | 68 + .../src/layouts/BasicLayout/index.jsx | 81 + .../src/layouts/BasicLayout/menuConfig.js | 11 + .../demo-project/src/pages/Aaaa/index.css | 0 .../demo-project/src/pages/Aaaa/index.jsx | 118 ++ .../demo-project/src/pages/layout.jsx | 10 + .../expected/demo-project/src/typings.d.ts | 9 + .../expected/demo-project/src/utils.js | 61 + .../demo2-utils-name-alias/schema.json5 | 123 ++ .../expected/demo-project/.browserslistrc | 3 + .../demo2/expected/demo-project/.gitignore | 25 + .../demo2/expected/demo-project/README.md | 1 + .../expected/demo-project/ice.config.mts | 90 + .../demo2/expected/demo-project/package.json | 42 + .../demo2/expected/demo-project/src/app.ts | 13 + .../expected/demo-project/src/constants.js | 3 + .../expected/demo-project/src/document.tsx | 29 + .../expected/demo-project/src/global.scss | 13 + .../demo2/expected/demo-project/src/i18n.js | 86 + .../BasicLayout/components/Footer/index.jsx | 14 + .../components/Footer/index.module.scss | 15 + .../BasicLayout/components/Logo/index.jsx | 16 + .../components/Logo/index.module.scss | 20 + .../BasicLayout/components/PageNav/index.jsx | 68 + .../src/layouts/BasicLayout/index.jsx | 81 + .../src/layouts/BasicLayout/menuConfig.js | 11 + .../demo-project/src/pages/Test/index.css | 0 .../demo-project/src/pages/Test/index.jsx | 119 ++ .../demo-project/src/pages/layout.jsx | 10 + .../expected/demo-project/src/typings.d.ts | 9 + .../demo2/expected/demo-project/src/utils.js | 47 + .../test-cases/icejs3-app/demo2/schema.json5 | 256 +++ .../expected/demo-project/.browserslistrc | 3 + .../demo3/expected/demo-project/.gitignore | 25 + .../demo3/expected/demo-project/README.md | 1 + .../expected/demo-project/ice.config.mts | 90 + .../demo3/expected/demo-project/package.json | 42 + .../demo3/expected/demo-project/src/app.ts | 13 + .../expected/demo-project/src/constants.js | 3 + .../expected/demo-project/src/document.tsx | 29 + .../expected/demo-project/src/global.scss | 13 + .../demo3/expected/demo-project/src/i18n.js | 86 + .../BasicLayout/components/Footer/index.jsx | 14 + .../components/Footer/index.module.scss | 15 + .../BasicLayout/components/Logo/index.jsx | 16 + .../components/Logo/index.module.scss | 20 + .../BasicLayout/components/PageNav/index.jsx | 68 + .../src/layouts/BasicLayout/index.jsx | 81 + .../src/layouts/BasicLayout/menuConfig.js | 11 + .../demo-project/src/pages/Test/index.css | 0 .../demo-project/src/pages/Test/index.jsx | 93 + .../demo-project/src/pages/layout.jsx | 10 + .../expected/demo-project/src/typings.d.ts | 9 + .../demo3/expected/demo-project/src/utils.js | 47 + .../test-cases/icejs3-app/demo3/schema.json5 | 159 ++ .../expected/demo-project/.browserslistrc | 3 + .../demo4/expected/demo-project/.gitignore | 25 + .../demo4/expected/demo-project/README.md | 1 + .../expected/demo-project/ice.config.mts | 90 + .../demo4/expected/demo-project/package.json | 45 + .../demo4/expected/demo-project/src/app.ts | 13 + .../expected/demo-project/src/constants.js | 3 + .../expected/demo-project/src/document.tsx | 29 + .../expected/demo-project/src/global.scss | 6 + .../demo4/expected/demo-project/src/i18n.js | 77 + .../BasicLayout/components/Footer/index.jsx | 14 + .../components/Footer/index.module.scss | 15 + .../BasicLayout/components/Logo/index.jsx | 16 + .../components/Logo/index.module.scss | 20 + .../BasicLayout/components/PageNav/index.jsx | 68 + .../src/layouts/BasicLayout/index.jsx | 81 + .../src/layouts/BasicLayout/menuConfig.js | 11 + .../demo-project/src/pages/Test/index.css | 8 + .../demo-project/src/pages/Test/index.jsx | 292 +++ .../demo-project/src/pages/layout.jsx | 10 + .../expected/demo-project/src/typings.d.ts | 9 + .../demo4/expected/demo-project/src/utils.js | 47 + .../test-cases/icejs3-app/demo4/schema.json5 | 353 ++++ .../expected/demo-project/.browserslistrc | 3 + .../demo5/expected/demo-project/.gitignore | 25 + .../demo5/expected/demo-project/README.md | 1 + .../expected/demo-project/ice.config.mts | 90 + .../demo5/expected/demo-project/package.json | 46 + .../demo5/expected/demo-project/src/app.ts | 13 + .../expected/demo-project/src/constants.js | 3 + .../expected/demo-project/src/document.tsx | 29 + .../expected/demo-project/src/global.scss | 6 + .../demo5/expected/demo-project/src/i18n.js | 77 + .../BasicLayout/components/Footer/index.jsx | 14 + .../components/Footer/index.module.scss | 15 + .../BasicLayout/components/Logo/index.jsx | 16 + .../components/Logo/index.module.scss | 20 + .../BasicLayout/components/PageNav/index.jsx | 68 + .../src/layouts/BasicLayout/index.jsx | 81 + .../src/layouts/BasicLayout/menuConfig.js | 11 + .../demo-project/src/pages/Test/index.css | 8 + .../demo-project/src/pages/Test/index.jsx | 389 ++++ .../demo-project/src/pages/layout.jsx | 10 + .../expected/demo-project/src/typings.d.ts | 9 + .../demo5/expected/demo-project/src/utils.js | 47 + .../test-cases/icejs3-app/demo5/schema.json5 | 677 +++++++ .../expected/demo-project/.browserslistrc | 3 + .../expected/demo-project/.gitignore | 25 + .../expected/demo-project/README.md | 1 + .../expected/demo-project/ice.config.mts | 90 + .../expected/demo-project/package.json | 44 + .../expected/demo-project/src/app.ts | 13 + .../expected/demo-project/src/constants.js | 3 + .../expected/demo-project/src/document.tsx | 29 + .../expected/demo-project/src/global.scss | 13 + .../expected/demo-project/src/i18n.js | 77 + .../BasicLayout/components/Footer/index.jsx | 14 + .../components/Footer/index.module.scss | 15 + .../BasicLayout/components/Logo/index.jsx | 16 + .../components/Logo/index.module.scss | 20 + .../BasicLayout/components/PageNav/index.jsx | 68 + .../src/layouts/BasicLayout/index.jsx | 81 + .../src/layouts/BasicLayout/menuConfig.js | 11 + .../demo-project/src/pages/Test/index.css | 0 .../demo-project/src/pages/Test/index.jsx | 195 ++ .../demo-project/src/pages/layout.jsx | 10 + .../expected/demo-project/src/typings.d.ts | 9 + .../expected/demo-project/src/utils.js | 47 + .../demo6-literal-condition/schema.json5 | 273 +++ .../expected/demo-project/.browserslistrc | 3 + .../expected/demo-project/.gitignore | 25 + .../expected/demo-project/README.md | 1 + .../expected/demo-project/ice.config.mts | 90 + .../expected/demo-project/package.json | 45 + .../expected/demo-project/src/app.ts | 13 + .../expected/demo-project/src/constants.js | 3 + .../expected/demo-project/src/document.tsx | 29 + .../expected/demo-project/src/global.scss | 6 + .../expected/demo-project/src/i18n.js | 77 + .../BasicLayout/components/Footer/index.jsx | 14 + .../components/Footer/index.module.scss | 15 + .../BasicLayout/components/Logo/index.jsx | 16 + .../components/Logo/index.module.scss | 20 + .../BasicLayout/components/PageNav/index.jsx | 68 + .../src/layouts/BasicLayout/index.jsx | 81 + .../src/layouts/BasicLayout/menuConfig.js | 11 + .../demo-project/src/pages/Test/index.css | 8 + .../demo-project/src/pages/Test/index.jsx | 1076 +++++++++++ .../demo-project/src/pages/layout.jsx | 10 + .../expected/demo-project/src/typings.d.ts | 9 + .../expected/demo-project/src/utils.js | 47 + .../demo7-literal-condition2/schema.json5 | 1703 +++++++++++++++++ .../expected/demo-project/.browserslistrc | 3 + .../expected/demo-project/.gitignore | 25 + .../expected/demo-project/README.md | 1 + .../expected/demo-project/ice.config.mts | 90 + .../expected/demo-project/package.json | 44 + .../expected/demo-project/src/app.ts | 13 + .../expected/demo-project/src/constants.js | 3 + .../expected/demo-project/src/document.tsx | 29 + .../expected/demo-project/src/global.scss | 6 + .../expected/demo-project/src/i18n.js | 77 + .../BasicLayout/components/Footer/index.jsx | 14 + .../components/Footer/index.module.scss | 15 + .../BasicLayout/components/Logo/index.jsx | 16 + .../components/Logo/index.module.scss | 20 + .../BasicLayout/components/PageNav/index.jsx | 68 + .../src/layouts/BasicLayout/index.jsx | 81 + .../src/layouts/BasicLayout/menuConfig.js | 11 + .../demo-project/src/pages/Example/index.css | 0 .../demo-project/src/pages/Example/index.jsx | 116 ++ .../demo-project/src/pages/layout.jsx | 10 + .../expected/demo-project/src/typings.d.ts | 9 + .../expected/demo-project/src/utils.js | 47 + .../demo8-datasource-prop/schema.json5 | 65 + .../expected/demo-project/.browserslistrc | 3 + .../expected/demo-project/.gitignore | 25 + .../expected/demo-project/README.md | 1 + .../expected/demo-project/ice.config.mts | 90 + .../expected/demo-project/package.json | 43 + .../expected/demo-project/src/app.ts | 13 + .../expected/demo-project/src/constants.js | 3 + .../expected/demo-project/src/document.tsx | 29 + .../expected/demo-project/src/global.scss | 6 + .../expected/demo-project/src/i18n.js | 77 + .../BasicLayout/components/Footer/index.jsx | 14 + .../components/Footer/index.module.scss | 15 + .../BasicLayout/components/Logo/index.jsx | 16 + .../components/Logo/index.module.scss | 20 + .../BasicLayout/components/PageNav/index.jsx | 68 + .../src/layouts/BasicLayout/index.jsx | 81 + .../src/layouts/BasicLayout/menuConfig.js | 11 + .../demo-project/src/pages/$/index.css | 0 .../demo-project/src/pages/$/index.jsx | 123 ++ .../demo-project/src/pages/layout.jsx | 10 + .../expected/demo-project/src/typings.d.ts | 9 + .../expected/demo-project/src/utils.js | 47 + .../demo9-datasource-engine/schema.json5 | 59 + .../expected/demo-project/.browserslistrc | 3 + .../expected/demo-project/.gitignore | 25 + .../expected/demo-project/README.md | 1 + .../expected/demo-project/ice.config.mts | 90 + .../expected/demo-project/package.json | 46 + .../expected/demo-project/src/app.ts | 13 + .../expected/demo-project/src/constants.js | 3 + .../expected/demo-project/src/document.tsx | 29 + .../expected/demo-project/src/global.scss | 6 + .../expected/demo-project/src/i18n.js | 77 + .../BasicLayout/components/Footer/index.jsx | 14 + .../components/Footer/index.module.scss | 15 + .../BasicLayout/components/Logo/index.jsx | 16 + .../components/Logo/index.module.scss | 20 + .../BasicLayout/components/PageNav/index.jsx | 68 + .../src/layouts/BasicLayout/index.jsx | 81 + .../src/layouts/BasicLayout/menuConfig.js | 11 + .../demo-project/src/pages/Test/index.css | 8 + .../demo-project/src/pages/Test/index.jsx | 822 ++++++++ .../demo-project/src/pages/layout.jsx | 10 + .../expected/demo-project/src/typings.d.ts | 9 + .../expected/demo-project/src/utils.js | 47 + .../icejs3-app/demo_10-jsslot/schema.json5 | 1206 ++++++++++++ .../expected/demo-project/.browserslistrc | 3 + .../expected/demo-project/.gitignore | 25 + .../expected/demo-project/README.md | 1 + .../expected/demo-project/ice.config.mts | 90 + .../expected/demo-project/package.json | 46 + .../expected/demo-project/src/app.ts | 13 + .../expected/demo-project/src/constants.js | 3 + .../expected/demo-project/src/document.tsx | 29 + .../expected/demo-project/src/global.scss | 6 + .../expected/demo-project/src/i18n.js | 77 + .../BasicLayout/components/Footer/index.jsx | 14 + .../components/Footer/index.module.scss | 15 + .../BasicLayout/components/Logo/index.jsx | 16 + .../components/Logo/index.module.scss | 20 + .../BasicLayout/components/PageNav/index.jsx | 68 + .../src/layouts/BasicLayout/index.jsx | 81 + .../src/layouts/BasicLayout/menuConfig.js | 11 + .../demo-project/src/pages/Test/index.css | 8 + .../demo-project/src/pages/Test/index.jsx | 976 ++++++++++ .../demo-project/src/pages/layout.jsx | 10 + .../expected/demo-project/src/typings.d.ts | 9 + .../expected/demo-project/src/utils.js | 47 + .../icejs3-app/demo_11-jsslot-2/schema.json5 | 1457 ++++++++++++++ .../tests/public/solutions/icejs3-app.test.ts | 63 + 304 files changed, 19427 insertions(+), 3 deletions(-) create mode 100644 modules/code-generator/src/plugins/project/framework/icejs3/index.ts create mode 100644 modules/code-generator/src/plugins/project/framework/icejs3/plugins/appConfig.ts create mode 100644 modules/code-generator/src/plugins/project/framework/icejs3/plugins/buildConfig.ts create mode 100644 modules/code-generator/src/plugins/project/framework/icejs3/plugins/globalStyle.ts create mode 100644 modules/code-generator/src/plugins/project/framework/icejs3/plugins/layout.ts create mode 100644 modules/code-generator/src/plugins/project/framework/icejs3/plugins/packageJSON.ts create mode 100644 modules/code-generator/src/plugins/project/framework/icejs3/template/files/README.md.ts create mode 100644 modules/code-generator/src/plugins/project/framework/icejs3/template/files/browserslistrc.ts create mode 100644 modules/code-generator/src/plugins/project/framework/icejs3/template/files/document.ts create mode 100644 modules/code-generator/src/plugins/project/framework/icejs3/template/files/gitignore.ts create mode 100644 modules/code-generator/src/plugins/project/framework/icejs3/template/files/src/layouts/BasicLayout/components/Footer/index.jsx.ts create mode 100644 modules/code-generator/src/plugins/project/framework/icejs3/template/files/src/layouts/BasicLayout/components/Footer/index.style.ts create mode 100644 modules/code-generator/src/plugins/project/framework/icejs3/template/files/src/layouts/BasicLayout/components/Logo/index.jsx.ts create mode 100644 modules/code-generator/src/plugins/project/framework/icejs3/template/files/src/layouts/BasicLayout/components/Logo/index.style.ts create mode 100644 modules/code-generator/src/plugins/project/framework/icejs3/template/files/src/layouts/BasicLayout/components/PageNav/index.jsx.ts create mode 100644 modules/code-generator/src/plugins/project/framework/icejs3/template/files/src/layouts/BasicLayout/index.jsx.ts create mode 100644 modules/code-generator/src/plugins/project/framework/icejs3/template/files/src/layouts/BasicLayout/menuConfig.js.ts create mode 100644 modules/code-generator/src/plugins/project/framework/icejs3/template/files/tsconfig.ts create mode 100644 modules/code-generator/src/plugins/project/framework/icejs3/template/files/typings.ts create mode 100644 modules/code-generator/src/plugins/project/framework/icejs3/template/index.ts create mode 100644 modules/code-generator/src/plugins/project/framework/icejs3/template/static-files.ts create mode 100644 modules/code-generator/src/solutions/icejs3.ts create mode 100644 modules/code-generator/src/utils/format.ts create mode 100644 modules/code-generator/src/utils/theme.ts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/.browserslistrc create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/.gitignore create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/README.md create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/ice.config.mts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/package.json create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/app.ts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/constants.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/document.tsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/global.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/i18n.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/layouts/BasicLayout/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/layouts/BasicLayout/menuConfig.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/pages/Test/index.css create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/pages/Test/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/pages/layout.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/typings.d.ts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/utils.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/schema.json5 create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/.browserslistrc create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/.gitignore create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/README.md create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/ice.config.mts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/package.json create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/app.ts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/constants.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/document.tsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/global.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/i18n.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/layouts/BasicLayout/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/layouts/BasicLayout/menuConfig.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/pages/Aaaa/index.css create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/pages/Aaaa/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/pages/layout.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/typings.d.ts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/utils.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/schema.json5 create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/.browserslistrc create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/.gitignore create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/README.md create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/ice.config.mts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/package.json create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/app.ts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/constants.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/document.tsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/global.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/i18n.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/layouts/BasicLayout/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/layouts/BasicLayout/menuConfig.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/pages/Test/index.css create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/pages/Test/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/pages/layout.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/typings.d.ts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/utils.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/schema.json5 create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/.browserslistrc create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/.gitignore create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/README.md create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/ice.config.mts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/package.json create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/app.ts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/constants.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/document.tsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/global.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/i18n.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/layouts/BasicLayout/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/layouts/BasicLayout/menuConfig.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/pages/Test/index.css create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/pages/Test/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/pages/layout.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/typings.d.ts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/utils.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/schema.json5 create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/.browserslistrc create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/.gitignore create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/README.md create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/ice.config.mts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/package.json create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/app.ts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/constants.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/document.tsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/global.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/i18n.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/layouts/BasicLayout/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/layouts/BasicLayout/menuConfig.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/pages/Test/index.css create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/pages/Test/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/pages/layout.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/typings.d.ts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/utils.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/schema.json5 create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/.browserslistrc create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/.gitignore create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/README.md create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/ice.config.mts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/package.json create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/app.ts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/constants.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/document.tsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/global.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/i18n.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/layouts/BasicLayout/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/layouts/BasicLayout/menuConfig.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/pages/Test/index.css create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/pages/Test/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/pages/layout.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/typings.d.ts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/utils.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/schema.json5 create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/.browserslistrc create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/.gitignore create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/README.md create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/ice.config.mts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/package.json create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/app.ts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/constants.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/document.tsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/global.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/i18n.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/layouts/BasicLayout/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/layouts/BasicLayout/menuConfig.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/pages/Test/index.css create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/pages/Test/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/pages/layout.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/typings.d.ts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/utils.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/schema.json5 create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/.browserslistrc create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/.gitignore create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/README.md create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/ice.config.mts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/package.json create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/app.ts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/constants.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/document.tsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/global.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/i18n.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/layouts/BasicLayout/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/layouts/BasicLayout/menuConfig.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/pages/Test/index.css create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/pages/Test/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/pages/layout.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/typings.d.ts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/utils.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/schema.json5 create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/.browserslistrc create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/.gitignore create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/README.md create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/ice.config.mts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/package.json create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/app.ts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/constants.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/document.tsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/global.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/i18n.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/layouts/BasicLayout/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/layouts/BasicLayout/menuConfig.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/pages/Example/index.css create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/pages/Example/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/pages/layout.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/typings.d.ts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/utils.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/schema.json5 create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/.browserslistrc create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/.gitignore create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/README.md create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/ice.config.mts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/package.json create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/app.ts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/constants.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/document.tsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/global.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/i18n.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/layouts/BasicLayout/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/layouts/BasicLayout/menuConfig.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/pages/$/index.css create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/pages/$/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/pages/layout.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/typings.d.ts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/utils.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/schema.json5 create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/.browserslistrc create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/.gitignore create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/README.md create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/ice.config.mts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/package.json create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/app.ts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/constants.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/document.tsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/global.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/i18n.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/layouts/BasicLayout/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/layouts/BasicLayout/menuConfig.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/pages/Test/index.css create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/pages/Test/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/pages/layout.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/typings.d.ts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/utils.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/schema.json5 create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/.browserslistrc create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/.gitignore create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/README.md create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/ice.config.mts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/package.json create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/app.ts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/constants.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/document.tsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/global.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/i18n.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/layouts/BasicLayout/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/layouts/BasicLayout/menuConfig.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/pages/Test/index.css create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/pages/Test/index.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/pages/layout.jsx create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/typings.d.ts create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/utils.js create mode 100644 modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/schema.json5 create mode 100644 modules/code-generator/tests/public/solutions/icejs3-app.test.ts diff --git a/modules/code-generator/package.json b/modules/code-generator/package.json index a7b795f38..97d945469 100644 --- a/modules/code-generator/package.json +++ b/modules/code-generator/package.json @@ -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", diff --git a/modules/code-generator/src/index.ts b/modules/code-generator/src/index.ts index 742b2e6a8..abe080557 100644 --- a/modules/code-generator/src/index.ts +++ b/modules/code-generator/src/index.ts @@ -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, diff --git a/modules/code-generator/src/plugins/common/esmodule.ts b/modules/code-generator/src/plugins/common/esmodule.ts index 09c6aec59..53f3f9418 100644 --- a/modules/code-generator/src/plugins/common/esmodule.ts +++ b/modules/code-generator/src/plugins/common/esmodule.ts @@ -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}]`); } }); diff --git a/modules/code-generator/src/plugins/project/framework/icejs3/index.ts b/modules/code-generator/src/plugins/project/framework/icejs3/index.ts new file mode 100644 index 000000000..37fa7a9e3 --- /dev/null +++ b/modules/code-generator/src/plugins/project/framework/icejs3/index.ts @@ -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, + }, +}; diff --git a/modules/code-generator/src/plugins/project/framework/icejs3/plugins/appConfig.ts b/modules/code-generator/src/plugins/project/framework/icejs3/plugins/appConfig.ts new file mode 100644 index 000000000..6ec5384a5 --- /dev/null +++ b/modules/code-generator/src/plugins/project/framework/icejs3/plugins/appConfig.ts @@ -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 = () => { + 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; diff --git a/modules/code-generator/src/plugins/project/framework/icejs3/plugins/buildConfig.ts b/modules/code-generator/src/plugins/project/framework/icejs3/plugins/buildConfig.ts new file mode 100644 index 000000000..e5eee83d5 --- /dev/null +++ b/modules/code-generator/src/plugins/project/framework/icejs3/plugins/buildConfig.ts @@ -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 = (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 = (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; diff --git a/modules/code-generator/src/plugins/project/framework/icejs3/plugins/globalStyle.ts b/modules/code-generator/src/plugins/project/framework/icejs3/plugins/globalStyle.ts new file mode 100644 index 000000000..3daaeecdc --- /dev/null +++ b/modules/code-generator/src/plugins/project/framework/icejs3/plugins/globalStyle.ts @@ -0,0 +1,56 @@ +import { COMMON_CHUNK_NAME } from '../../../../../const/generator'; + +import { + BuilderComponentPlugin, + BuilderComponentPluginFactory, + ChunkType, + FileType, + ICodeStruct, + IProjectInfo, +} from '../../../../../types'; + +const pluginFactory: BuilderComponentPluginFactory = () => { + 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; diff --git a/modules/code-generator/src/plugins/project/framework/icejs3/plugins/layout.ts b/modules/code-generator/src/plugins/project/framework/icejs3/plugins/layout.ts new file mode 100644 index 000000000..a67f22701 --- /dev/null +++ b/modules/code-generator/src/plugins/project/framework/icejs3/plugins/layout.ts @@ -0,0 +1,41 @@ +import { + BuilderComponentPlugin, + BuilderComponentPluginFactory, + ChunkType, + FileType, + ICodeStruct, +} from '../../../../../types'; +import { COMMON_CHUNK_NAME } from '../../../../../const/generator'; + +const pluginFactory: BuilderComponentPluginFactory = () => { + 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 ( + + + + );; + } + `, + linkAfter: [], + }); + + return next; + }; + + return plugin; +}; + +export default pluginFactory; diff --git a/modules/code-generator/src/plugins/project/framework/icejs3/plugins/packageJSON.ts b/modules/code-generator/src/plugins/project/framework/icejs3/plugins/packageJSON.ts new file mode 100644 index 000000000..11248a0b5 --- /dev/null +++ b/modules/code-generator/src/plugins/project/framework/icejs3/plugins/packageJSON.ts @@ -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 = (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; diff --git a/modules/code-generator/src/plugins/project/framework/icejs3/template/files/README.md.ts b/modules/code-generator/src/plugins/project/framework/icejs3/template/files/README.md.ts new file mode 100644 index 000000000..e73ab2b82 --- /dev/null +++ b/modules/code-generator/src/plugins/project/framework/icejs3/template/files/README.md.ts @@ -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]; +} diff --git a/modules/code-generator/src/plugins/project/framework/icejs3/template/files/browserslistrc.ts b/modules/code-generator/src/plugins/project/framework/icejs3/template/files/browserslistrc.ts new file mode 100644 index 000000000..a3a346e3f --- /dev/null +++ b/modules/code-generator/src/plugins/project/framework/icejs3/template/files/browserslistrc.ts @@ -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]; +} diff --git a/modules/code-generator/src/plugins/project/framework/icejs3/template/files/document.ts b/modules/code-generator/src/plugins/project/framework/icejs3/template/files/document.ts new file mode 100644 index 000000000..17978b20d --- /dev/null +++ b/modules/code-generator/src/plugins/project/framework/icejs3/template/files/document.ts @@ -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 ( + + + + + + + + + + <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]; +} diff --git a/modules/code-generator/src/plugins/project/framework/icejs3/template/files/gitignore.ts b/modules/code-generator/src/plugins/project/framework/icejs3/template/files/gitignore.ts new file mode 100644 index 000000000..5c6eb3f13 --- /dev/null +++ b/modules/code-generator/src/plugins/project/framework/icejs3/template/files/gitignore.ts @@ -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]; +} diff --git a/modules/code-generator/src/plugins/project/framework/icejs3/template/files/src/layouts/BasicLayout/components/Footer/index.jsx.ts b/modules/code-generator/src/plugins/project/framework/icejs3/template/files/src/layouts/BasicLayout/components/Footer/index.jsx.ts new file mode 100644 index 000000000..9647a7643 --- /dev/null +++ b/modules/code-generator/src/plugins/project/framework/icejs3/template/files/src/layouts/BasicLayout/components/Footer/index.jsx.ts @@ -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]; +} diff --git a/modules/code-generator/src/plugins/project/framework/icejs3/template/files/src/layouts/BasicLayout/components/Footer/index.style.ts b/modules/code-generator/src/plugins/project/framework/icejs3/template/files/src/layouts/BasicLayout/components/Footer/index.style.ts new file mode 100644 index 000000000..941be0d26 --- /dev/null +++ b/modules/code-generator/src/plugins/project/framework/icejs3/template/files/src/layouts/BasicLayout/components/Footer/index.style.ts @@ -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]; +} diff --git a/modules/code-generator/src/plugins/project/framework/icejs3/template/files/src/layouts/BasicLayout/components/Logo/index.jsx.ts b/modules/code-generator/src/plugins/project/framework/icejs3/template/files/src/layouts/BasicLayout/components/Logo/index.jsx.ts new file mode 100644 index 000000000..9c078c92c --- /dev/null +++ b/modules/code-generator/src/plugins/project/framework/icejs3/template/files/src/layouts/BasicLayout/components/Logo/index.jsx.ts @@ -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]; +} diff --git a/modules/code-generator/src/plugins/project/framework/icejs3/template/files/src/layouts/BasicLayout/components/Logo/index.style.ts b/modules/code-generator/src/plugins/project/framework/icejs3/template/files/src/layouts/BasicLayout/components/Logo/index.style.ts new file mode 100644 index 000000000..5ac92b550 --- /dev/null +++ b/modules/code-generator/src/plugins/project/framework/icejs3/template/files/src/layouts/BasicLayout/components/Logo/index.style.ts @@ -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]; +} diff --git a/modules/code-generator/src/plugins/project/framework/icejs3/template/files/src/layouts/BasicLayout/components/PageNav/index.jsx.ts b/modules/code-generator/src/plugins/project/framework/icejs3/template/files/src/layouts/BasicLayout/components/PageNav/index.jsx.ts new file mode 100644 index 000000000..171305756 --- /dev/null +++ b/modules/code-generator/src/plugins/project/framework/icejs3/template/files/src/layouts/BasicLayout/components/PageNav/index.jsx.ts @@ -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]; +} diff --git a/modules/code-generator/src/plugins/project/framework/icejs3/template/files/src/layouts/BasicLayout/index.jsx.ts b/modules/code-generator/src/plugins/project/framework/icejs3/template/files/src/layouts/BasicLayout/index.jsx.ts new file mode 100644 index 000000000..9c7318dab --- /dev/null +++ b/modules/code-generator/src/plugins/project/framework/icejs3/template/files/src/layouts/BasicLayout/index.jsx.ts @@ -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]; +} diff --git a/modules/code-generator/src/plugins/project/framework/icejs3/template/files/src/layouts/BasicLayout/menuConfig.js.ts b/modules/code-generator/src/plugins/project/framework/icejs3/template/files/src/layouts/BasicLayout/menuConfig.js.ts new file mode 100644 index 000000000..636539b65 --- /dev/null +++ b/modules/code-generator/src/plugins/project/framework/icejs3/template/files/src/layouts/BasicLayout/menuConfig.js.ts @@ -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]; +} diff --git a/modules/code-generator/src/plugins/project/framework/icejs3/template/files/tsconfig.ts b/modules/code-generator/src/plugins/project/framework/icejs3/template/files/tsconfig.ts new file mode 100644 index 000000000..f58639db2 --- /dev/null +++ b/modules/code-generator/src/plugins/project/framework/icejs3/template/files/tsconfig.ts @@ -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]; +} diff --git a/modules/code-generator/src/plugins/project/framework/icejs3/template/files/typings.ts b/modules/code-generator/src/plugins/project/framework/icejs3/template/files/typings.ts new file mode 100644 index 000000000..c8cbe9254 --- /dev/null +++ b/modules/code-generator/src/plugins/project/framework/icejs3/template/files/typings.ts @@ -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]; +} diff --git a/modules/code-generator/src/plugins/project/framework/icejs3/template/index.ts b/modules/code-generator/src/plugins/project/framework/icejs3/template/index.ts new file mode 100644 index 000000000..e29f93138 --- /dev/null +++ b/modules/code-generator/src/plugins/project/framework/icejs3/template/index.ts @@ -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; diff --git a/modules/code-generator/src/plugins/project/framework/icejs3/template/static-files.ts b/modules/code-generator/src/plugins/project/framework/icejs3/template/static-files.ts new file mode 100644 index 000000000..8f3794b61 --- /dev/null +++ b/modules/code-generator/src/plugins/project/framework/icejs3/template/static-files.ts @@ -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; +} diff --git a/modules/code-generator/src/solutions/icejs3.ts b/modules/code-generator/src/solutions/icejs3.ts new file mode 100644 index 000000000..26128a9eb --- /dev/null +++ b/modules/code-generator/src/solutions/icejs3.ts @@ -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, +}; diff --git a/modules/code-generator/src/utils/format.ts b/modules/code-generator/src/utils/format.ts new file mode 100644 index 000000000..7c865e8f3 --- /dev/null +++ b/modules/code-generator/src/utils/format.ts @@ -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, + }); +} diff --git a/modules/code-generator/src/utils/theme.ts b/modules/code-generator/src/utils/theme.ts new file mode 100644 index 000000000..590cf711c --- /dev/null +++ b/modules/code-generator/src/utils/theme.ts @@ -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), + }; +} \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/.browserslistrc b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/.browserslistrc new file mode 100644 index 000000000..55a130413 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/.browserslistrc @@ -0,0 +1,3 @@ +defaults +ios_saf 9 + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/.gitignore b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/.gitignore new file mode 100644 index 000000000..4ec178818 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/.gitignore @@ -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 + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/README.md b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/README.md new file mode 100644 index 000000000..6d9dd7521 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/README.md @@ -0,0 +1 @@ +This project is generated by lowcode-code-generator & lowcode-solution-icejs3. \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/ice.config.mts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/ice.config.mts new file mode 100644 index 000000000..fe77c9b6c --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/ice.config.mts @@ -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(), + ], +})); + diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/package.json b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/package.json new file mode 100644 index 000000000..54a1e4e12 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/package.json @@ -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" +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/app.ts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/app.ts new file mode 100644 index 000000000..6d5856292 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/app.ts @@ -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: '/', + }, +})); diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/constants.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/constants.js new file mode 100644 index 000000000..91198f904 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/constants.js @@ -0,0 +1,3 @@ +const __$$constants = { ENV: 'prod', DOMAIN: 'xxx.xxx.com' }; + +export default __$$constants; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/document.tsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/document.tsx new file mode 100644 index 000000000..286be9f8c --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/document.tsx @@ -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> + ); +} \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/global.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/global.scss new file mode 100644 index 000000000..ed7204b4a --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/global.scss @@ -0,0 +1,13 @@ +// 引入默认全局样式 +@import '@alifd/next/reset.scss'; + +body { + -webkit-font-smoothing: antialiased; +} + +body { + font-size: 12px; +} +.table { + width: 100px; +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/i18n.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/i18n.js new file mode 100644 index 000000000..adbbe673d --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/i18n.js @@ -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 }; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx new file mode 100644 index 000000000..cc70d53be --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss new file mode 100644 index 000000000..81e77fda5 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss @@ -0,0 +1,15 @@ + +.footer { + line-height: 20px; + text-align: center; +} + +.logo { + font-weight: bold; + font-size: 16px; +} + +.copyright { + font-size: 12px; +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx new file mode 100644 index 000000000..265bfdaa0 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss new file mode 100644 index 000000000..1ab56d394 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss @@ -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; + } +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx new file mode 100644 index 000000000..911998b0d --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx @@ -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; + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/layouts/BasicLayout/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/layouts/BasicLayout/index.jsx new file mode 100644 index 000000000..18db44df5 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/layouts/BasicLayout/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/layouts/BasicLayout/menuConfig.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/layouts/BasicLayout/menuConfig.js new file mode 100644 index 000000000..5332202be --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/layouts/BasicLayout/menuConfig.js @@ -0,0 +1,11 @@ + +const headerMenuConfig = []; +const asideMenuConfig = [ + { + name: 'Dashboard', + path: '/', + icon: 'smile', + }, +]; +export { headerMenuConfig, asideMenuConfig }; + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/pages/Test/index.css b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/pages/Test/index.css new file mode 100644 index 000000000..e69de29bb diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/pages/Test/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/pages/Test/index.jsx new file mode 100644 index 000000000..070020b74 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/pages/Test/index.jsx @@ -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; +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/pages/layout.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/pages/layout.jsx new file mode 100644 index 000000000..50fbb2d1f --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/pages/layout.jsx @@ -0,0 +1,10 @@ +import { Outlet } from 'ice'; +import BasicLayout from '@/layouts/BasicLayout'; + +export default function Layout() { + return ( + <BasicLayout> + <Outlet /> + </BasicLayout> + ); +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/typings.d.ts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/typings.d.ts new file mode 100644 index 000000000..a9f8de7ce --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/typings.d.ts @@ -0,0 +1,9 @@ +/// <reference types="@ice/app/types" /> + +export {}; +declare global { + interface Window { + g_config: Record<string, any>; + } +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/utils.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/utils.js new file mode 100644 index 000000000..119071792 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/expected/demo-project/src/utils.js @@ -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 {}; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/schema.json5 b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/schema.json5 new file mode 100644 index 000000000..76c52fb5e --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo1/schema.json5 @@ -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": "月飞" + } +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/.browserslistrc b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/.browserslistrc new file mode 100644 index 000000000..55a130413 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/.browserslistrc @@ -0,0 +1,3 @@ +defaults +ios_saf 9 + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/.gitignore b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/.gitignore new file mode 100644 index 000000000..4ec178818 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/.gitignore @@ -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 + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/README.md b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/README.md new file mode 100644 index 000000000..6d9dd7521 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/README.md @@ -0,0 +1 @@ +This project is generated by lowcode-code-generator & lowcode-solution-icejs3. \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/ice.config.mts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/ice.config.mts new file mode 100644 index 000000000..fe77c9b6c --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/ice.config.mts @@ -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(), + ], +})); + diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/package.json b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/package.json new file mode 100644 index 000000000..e234f0da0 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/package.json @@ -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" +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/app.ts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/app.ts new file mode 100644 index 000000000..6d5856292 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/app.ts @@ -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: '/', + }, +})); diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/constants.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/constants.js new file mode 100644 index 000000000..ea766c9da --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/constants.js @@ -0,0 +1,3 @@ +const __$$constants = {}; + +export default __$$constants; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/document.tsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/document.tsx new file mode 100644 index 000000000..286be9f8c --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/document.tsx @@ -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> + ); +} \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/global.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/global.scss new file mode 100644 index 000000000..82ca3eac7 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/global.scss @@ -0,0 +1,6 @@ +// 引入默认全局样式 +@import '@alifd/next/reset.scss'; + +body { + -webkit-font-smoothing: antialiased; +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/i18n.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/i18n.js new file mode 100644 index 000000000..adbbe673d --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/i18n.js @@ -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 }; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx new file mode 100644 index 000000000..cc70d53be --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss new file mode 100644 index 000000000..81e77fda5 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss @@ -0,0 +1,15 @@ + +.footer { + line-height: 20px; + text-align: center; +} + +.logo { + font-weight: bold; + font-size: 16px; +} + +.copyright { + font-size: 12px; +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx new file mode 100644 index 000000000..265bfdaa0 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss new file mode 100644 index 000000000..1ab56d394 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss @@ -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; + } +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx new file mode 100644 index 000000000..911998b0d --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx @@ -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; + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/layouts/BasicLayout/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/layouts/BasicLayout/index.jsx new file mode 100644 index 000000000..18db44df5 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/layouts/BasicLayout/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/layouts/BasicLayout/menuConfig.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/layouts/BasicLayout/menuConfig.js new file mode 100644 index 000000000..5332202be --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/layouts/BasicLayout/menuConfig.js @@ -0,0 +1,11 @@ + +const headerMenuConfig = []; +const asideMenuConfig = [ + { + name: 'Dashboard', + path: '/', + icon: 'smile', + }, +]; +export { headerMenuConfig, asideMenuConfig }; + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/pages/Aaaa/index.css b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/pages/Aaaa/index.css new file mode 100644 index 000000000..e69de29bb diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/pages/Aaaa/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/pages/Aaaa/index.jsx new file mode 100644 index 000000000..2945a9d8f --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/pages/Aaaa/index.jsx @@ -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; +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/pages/layout.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/pages/layout.jsx new file mode 100644 index 000000000..50fbb2d1f --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/pages/layout.jsx @@ -0,0 +1,10 @@ +import { Outlet } from 'ice'; +import BasicLayout from '@/layouts/BasicLayout'; + +export default function Layout() { + return ( + <BasicLayout> + <Outlet /> + </BasicLayout> + ); +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/typings.d.ts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/typings.d.ts new file mode 100644 index 000000000..a9f8de7ce --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/typings.d.ts @@ -0,0 +1,9 @@ +/// <reference types="@ice/app/types" /> + +export {}; +declare global { + interface Window { + g_config: Record<string, any>; + } +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/utils.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/utils.js new file mode 100644 index 000000000..868d47110 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/expected/demo-project/src/utils.js @@ -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, +}; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/schema.json5 b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/schema.json5 new file mode 100644 index 000000000..76e8248cb --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2-utils-name-alias/schema.json5 @@ -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: '张三', + }, +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/.browserslistrc b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/.browserslistrc new file mode 100644 index 000000000..55a130413 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/.browserslistrc @@ -0,0 +1,3 @@ +defaults +ios_saf 9 + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/.gitignore b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/.gitignore new file mode 100644 index 000000000..4ec178818 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/.gitignore @@ -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 + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/README.md b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/README.md new file mode 100644 index 000000000..6d9dd7521 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/README.md @@ -0,0 +1 @@ +This project is generated by lowcode-code-generator & lowcode-solution-icejs3. \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/ice.config.mts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/ice.config.mts new file mode 100644 index 000000000..fe77c9b6c --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/ice.config.mts @@ -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(), + ], +})); + diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/package.json b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/package.json new file mode 100644 index 000000000..63717e319 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/package.json @@ -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" +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/app.ts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/app.ts new file mode 100644 index 000000000..6d5856292 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/app.ts @@ -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: '/', + }, +})); diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/constants.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/constants.js new file mode 100644 index 000000000..91198f904 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/constants.js @@ -0,0 +1,3 @@ +const __$$constants = { ENV: 'prod', DOMAIN: 'xxx.xxx.com' }; + +export default __$$constants; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/document.tsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/document.tsx new file mode 100644 index 000000000..286be9f8c --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/document.tsx @@ -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> + ); +} \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/global.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/global.scss new file mode 100644 index 000000000..ed7204b4a --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/global.scss @@ -0,0 +1,13 @@ +// 引入默认全局样式 +@import '@alifd/next/reset.scss'; + +body { + -webkit-font-smoothing: antialiased; +} + +body { + font-size: 12px; +} +.table { + width: 100px; +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/i18n.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/i18n.js new file mode 100644 index 000000000..2fa87027f --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/i18n.js @@ -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 }; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx new file mode 100644 index 000000000..cc70d53be --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss new file mode 100644 index 000000000..81e77fda5 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss @@ -0,0 +1,15 @@ + +.footer { + line-height: 20px; + text-align: center; +} + +.logo { + font-weight: bold; + font-size: 16px; +} + +.copyright { + font-size: 12px; +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx new file mode 100644 index 000000000..265bfdaa0 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss new file mode 100644 index 000000000..1ab56d394 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss @@ -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; + } +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx new file mode 100644 index 000000000..911998b0d --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx @@ -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; + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/layouts/BasicLayout/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/layouts/BasicLayout/index.jsx new file mode 100644 index 000000000..18db44df5 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/layouts/BasicLayout/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/layouts/BasicLayout/menuConfig.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/layouts/BasicLayout/menuConfig.js new file mode 100644 index 000000000..5332202be --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/layouts/BasicLayout/menuConfig.js @@ -0,0 +1,11 @@ + +const headerMenuConfig = []; +const asideMenuConfig = [ + { + name: 'Dashboard', + path: '/', + icon: 'smile', + }, +]; +export { headerMenuConfig, asideMenuConfig }; + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/pages/Test/index.css b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/pages/Test/index.css new file mode 100644 index 000000000..e69de29bb diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/pages/Test/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/pages/Test/index.jsx new file mode 100644 index 000000000..6cb430b1b --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/pages/Test/index.jsx @@ -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; +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/pages/layout.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/pages/layout.jsx new file mode 100644 index 000000000..50fbb2d1f --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/pages/layout.jsx @@ -0,0 +1,10 @@ +import { Outlet } from 'ice'; +import BasicLayout from '@/layouts/BasicLayout'; + +export default function Layout() { + return ( + <BasicLayout> + <Outlet /> + </BasicLayout> + ); +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/typings.d.ts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/typings.d.ts new file mode 100644 index 000000000..a9f8de7ce --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/typings.d.ts @@ -0,0 +1,9 @@ +/// <reference types="@ice/app/types" /> + +export {}; +declare global { + interface Window { + g_config: Record<string, any>; + } +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/utils.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/utils.js new file mode 100644 index 000000000..119071792 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/expected/demo-project/src/utils.js @@ -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 {}; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/schema.json5 b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/schema.json5 new file mode 100644 index 000000000..222821206 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo2/schema.json5 @@ -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": "月飞" + } +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/.browserslistrc b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/.browserslistrc new file mode 100644 index 000000000..55a130413 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/.browserslistrc @@ -0,0 +1,3 @@ +defaults +ios_saf 9 + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/.gitignore b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/.gitignore new file mode 100644 index 000000000..4ec178818 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/.gitignore @@ -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 + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/README.md b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/README.md new file mode 100644 index 000000000..6d9dd7521 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/README.md @@ -0,0 +1 @@ +This project is generated by lowcode-code-generator & lowcode-solution-icejs3. \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/ice.config.mts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/ice.config.mts new file mode 100644 index 000000000..fe77c9b6c --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/ice.config.mts @@ -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(), + ], +})); + diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/package.json b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/package.json new file mode 100644 index 000000000..63717e319 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/package.json @@ -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" +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/app.ts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/app.ts new file mode 100644 index 000000000..6d5856292 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/app.ts @@ -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: '/', + }, +})); diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/constants.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/constants.js new file mode 100644 index 000000000..91198f904 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/constants.js @@ -0,0 +1,3 @@ +const __$$constants = { ENV: 'prod', DOMAIN: 'xxx.xxx.com' }; + +export default __$$constants; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/document.tsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/document.tsx new file mode 100644 index 000000000..286be9f8c --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/document.tsx @@ -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> + ); +} \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/global.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/global.scss new file mode 100644 index 000000000..ed7204b4a --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/global.scss @@ -0,0 +1,13 @@ +// 引入默认全局样式 +@import '@alifd/next/reset.scss'; + +body { + -webkit-font-smoothing: antialiased; +} + +body { + font-size: 12px; +} +.table { + width: 100px; +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/i18n.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/i18n.js new file mode 100644 index 000000000..2fa87027f --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/i18n.js @@ -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 }; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx new file mode 100644 index 000000000..cc70d53be --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss new file mode 100644 index 000000000..81e77fda5 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss @@ -0,0 +1,15 @@ + +.footer { + line-height: 20px; + text-align: center; +} + +.logo { + font-weight: bold; + font-size: 16px; +} + +.copyright { + font-size: 12px; +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx new file mode 100644 index 000000000..265bfdaa0 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss new file mode 100644 index 000000000..1ab56d394 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss @@ -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; + } +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx new file mode 100644 index 000000000..911998b0d --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx @@ -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; + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/layouts/BasicLayout/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/layouts/BasicLayout/index.jsx new file mode 100644 index 000000000..18db44df5 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/layouts/BasicLayout/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/layouts/BasicLayout/menuConfig.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/layouts/BasicLayout/menuConfig.js new file mode 100644 index 000000000..5332202be --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/layouts/BasicLayout/menuConfig.js @@ -0,0 +1,11 @@ + +const headerMenuConfig = []; +const asideMenuConfig = [ + { + name: 'Dashboard', + path: '/', + icon: 'smile', + }, +]; +export { headerMenuConfig, asideMenuConfig }; + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/pages/Test/index.css b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/pages/Test/index.css new file mode 100644 index 000000000..e69de29bb diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/pages/Test/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/pages/Test/index.jsx new file mode 100644 index 000000000..18c5e0a57 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/pages/Test/index.jsx @@ -0,0 +1,93 @@ +// 注意: 出码引擎注入的临时变量默认都以 "__$$" 开头,禁止在搭建的代码中直接访问。 +// 例外:react 框架的导出名和各种组件名除外。 +import React from 'react'; + +import Super, { + Button, + Input as CustomInput, + Form, + NumberPicker, + Select, + SearchTable as SearchTableExport, +} from '@alifd/next'; + +import SuperOther from '@alifd/next'; + +import utils from '../../utils'; + +import * as __$$i18n from '../../i18n'; + +import __$$constants from '../../constants'; + +import './index.css'; + +const SuperSub = Super.Sub; + +const SelectOption = Select.Option; + +const SearchTable = SearchTableExport.default; + +class Test$$Page extends React.Component { + _context = this; + + get constants() { + return __$$constants || {}; + } + + constructor(props, context) { + super(props); + + this.utils = utils; + + __$$i18n._inject2(this); + + this.state = {}; + } + + $ = () => null; + + $$ = () => []; + + componentDidMount() {} + + render() { + const __$$context = this._context || this; + const { state } = __$$context; + return ( + <div> + <Super title={__$$eval(() => this.state.title)} /> + <SuperSub /> + <SuperOther /> + <Button /> + <Button.Group /> + <CustomInput /> + <Form.Item /> + <NumberPicker /> + <SelectOption /> + <SearchTable /> + </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; +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/pages/layout.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/pages/layout.jsx new file mode 100644 index 000000000..50fbb2d1f --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/pages/layout.jsx @@ -0,0 +1,10 @@ +import { Outlet } from 'ice'; +import BasicLayout from '@/layouts/BasicLayout'; + +export default function Layout() { + return ( + <BasicLayout> + <Outlet /> + </BasicLayout> + ); +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/typings.d.ts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/typings.d.ts new file mode 100644 index 000000000..a9f8de7ce --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/typings.d.ts @@ -0,0 +1,9 @@ +/// <reference types="@ice/app/types" /> + +export {}; +declare global { + interface Window { + g_config: Record<string, any>; + } +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/utils.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/utils.js new file mode 100644 index 000000000..119071792 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/expected/demo-project/src/utils.js @@ -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 {}; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/schema.json5 b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/schema.json5 new file mode 100644 index 000000000..173b793e1 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo3/schema.json5 @@ -0,0 +1,159 @@ +{ + "version": "1.0.0", + "componentsMap": [ + { + "componentName": "Super", + "package": "@alifd/next", + "version": "1.19.18", + "destructuring": false, + "exportName": "Super" + }, + { + "componentName": "SuperOther", + "package": "@alifd/next", + "version": "1.19.18", + "destructuring": false, + "exportName": "Super" + }, + { + "componentName": "SuperSub", + "package": "@alifd/next", + "version": "1.19.18", + "destructuring": false, + "exportName": "Super", + "subName": "Sub", + }, + { + "componentName": "Button", + "package": "@alifd/next", + "version": "1.19.18", + "destructuring": true, + "exportName": "Button" + }, + { + "componentName": "SearchTable", + "package": "@alifd/next", + "version": "1.19.18", + "destructuring": true, + "exportName": "SearchTable", + "subName": "default", + }, + { + "componentName": "Button.Group", + "package": "@alifd/next", + "version": "1.19.18", + "destructuring": true, + "exportName": "Button", + "subName": "Group" + }, + { + "componentName": "CustomInput", + "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": "SelectOption", + "package": "@alifd/next", + "version": "1.19.18", + "destructuring": true, + "exportName": "Select", + "subName": "Option" + } + ], + "componentsTree": [ + { + "componentName": "Page", + "id": "node$1", + "meta": { + "title": "测试", + "router": "/" + }, + "fileName": "test", + "children": [ + { + "componentName": "Super", + "props": { + "title": { + "type":"variable", + "value":"标题", + "variable":"this.state.title" + } + } + }, + { "componentName": "SuperSub" }, + { "componentName": "SuperOther" }, + { "componentName": "Button" }, + { "componentName": "Button.Group" }, + { "componentName": "CustomInput" }, + { "componentName": "Form.Item" }, + { "componentName": "NumberPicker" }, + { "componentName": "SelectOption" }, + { "componentName": "SearchTable" }, + ] + } + ], + "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": "月飞" + } +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/.browserslistrc b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/.browserslistrc new file mode 100644 index 000000000..55a130413 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/.browserslistrc @@ -0,0 +1,3 @@ +defaults +ios_saf 9 + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/.gitignore b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/.gitignore new file mode 100644 index 000000000..4ec178818 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/.gitignore @@ -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 + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/README.md b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/README.md new file mode 100644 index 000000000..6d9dd7521 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/README.md @@ -0,0 +1 @@ +This project is generated by lowcode-code-generator & lowcode-solution-icejs3. \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/ice.config.mts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/ice.config.mts new file mode 100644 index 000000000..fe77c9b6c --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/ice.config.mts @@ -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(), + ], +})); + diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/package.json b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/package.json new file mode 100644 index 000000000..43f444085 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/package.json @@ -0,0 +1,45 @@ +{ + "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-fetch-handler": "^1.0.0", + "@alife/container": "^1.0.0", + "@alife/mc-assets-1935": "0.1.9" + }, + "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" +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/app.ts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/app.ts new file mode 100644 index 000000000..6d5856292 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/app.ts @@ -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: '/', + }, +})); diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/constants.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/constants.js new file mode 100644 index 000000000..ea766c9da --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/constants.js @@ -0,0 +1,3 @@ +const __$$constants = {}; + +export default __$$constants; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/document.tsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/document.tsx new file mode 100644 index 000000000..286be9f8c --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/document.tsx @@ -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> + ); +} \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/global.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/global.scss new file mode 100644 index 000000000..82ca3eac7 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/global.scss @@ -0,0 +1,6 @@ +// 引入默认全局样式 +@import '@alifd/next/reset.scss'; + +body { + -webkit-font-smoothing: antialiased; +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/i18n.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/i18n.js new file mode 100644 index 000000000..adbbe673d --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/i18n.js @@ -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 }; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx new file mode 100644 index 000000000..cc70d53be --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss new file mode 100644 index 000000000..81e77fda5 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss @@ -0,0 +1,15 @@ + +.footer { + line-height: 20px; + text-align: center; +} + +.logo { + font-weight: bold; + font-size: 16px; +} + +.copyright { + font-size: 12px; +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx new file mode 100644 index 000000000..265bfdaa0 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss new file mode 100644 index 000000000..1ab56d394 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss @@ -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; + } +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx new file mode 100644 index 000000000..911998b0d --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx @@ -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; + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/layouts/BasicLayout/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/layouts/BasicLayout/index.jsx new file mode 100644 index 000000000..18db44df5 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/layouts/BasicLayout/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/layouts/BasicLayout/menuConfig.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/layouts/BasicLayout/menuConfig.js new file mode 100644 index 000000000..5332202be --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/layouts/BasicLayout/menuConfig.js @@ -0,0 +1,11 @@ + +const headerMenuConfig = []; +const asideMenuConfig = [ + { + name: 'Dashboard', + path: '/', + icon: 'smile', + }, +]; +export { headerMenuConfig, asideMenuConfig }; + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/pages/Test/index.css b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/pages/Test/index.css new file mode 100644 index 000000000..066114aee --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/pages/Test/index.css @@ -0,0 +1,8 @@ +body { + font-size: 12px; +} + +.botton { + width: 100px; + color: #ff00ff; +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/pages/Test/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/pages/Test/index.jsx new file mode 100644 index 000000000..6400d7445 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/pages/Test/index.jsx @@ -0,0 +1,292 @@ +// 注意: 出码引擎注入的临时变量默认都以 "__$$" 开头,禁止在搭建的代码中直接访问。 +// 例外:react 框架的导出名和各种组件名除外。 +import React from 'react'; + +import { + Page as NextPage, + Block as NextBlock, + P as NextP, + Text as NextText, +} from '@alife/container/lib/index.js'; + +import { AliSearchTable as AliSearchTableExport } from '@alife/mc-assets-1935/build/lowcode/index.js'; + +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'; + +const NextBlockCell = NextBlock.Cell; + +const AliSearchTable = AliSearchTableExport.default; + +class Test$$Page extends React.Component { + _context = this; + + _dataSourceConfig = this._defineDataSourceConfig(); + _dataSourceEngine = __$$createDataSourceEngine(this._dataSourceConfig, this, { + runtimeConfig: true, + requestHandlersMap: { 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', isShowDialog: false }; + } + + $ = (refName) => { + return this._refsManager.get(refName); + }; + + $$ = (refName) => { + return this._refsManager.getAll(refName); + }; + + _defineDataSourceConfig() { + const _this = this; + return { + list: [ + { + type: 'fetch', + isInit: function () { + return true; + }.bind(_this), + options: function () { + return { + params: {}, + method: 'GET', + isCors: true, + timeout: 5000, + headers: {}, + uri: 'https://mocks.xxx.com/mock/jjpin/user/list', + }; + }.bind(_this), + id: 'users', + }, + ], + }; + } + + componentWillUnmount() { + console.log('will umount'); + } + + componentDidUpdate(prevProps, prevState, snapshot) { + console.log(this.state); + } + + testFunc() { + console.log('test func'); + } + + onClick() { + this.setState({ + isShowDialog: true, + }); + } + + closeDialog() { + this.setState({ + isShowDialog: false, + }); + } + + onSearch(values) { + console.log('search form:', values); + console.log(this.dataSourceMap); + this.dataSourceMap['users'].load(values); + } + + onClear() { + console.log('form reset'); + this.setState({ + isShowDialog: true, + }); + } + + onPageChange(page, pageSize) { + console.log(`page: ${page}, pageSize: ${pageSize}`); + } + + componentDidMount() { + this._dataSourceEngine.reloadDataSource(); + + console.log('did mount'); + } + + render() { + const __$$context = this._context || this; + const { state } = __$$context; + return ( + <div + ref={this._refsManager.linkRef('outterView')} + style={{ height: '100%' }} + > + <NextPage + columns={12} + placeholderStyle={{ gridRowEnd: 'span 1', gridColumnEnd: 'span 12' }} + placeholder="页面主体内容:拖拽Block布局组件到这里" + header={ + <NextP + wrap={true} + type="body2" + verAlign="middle" + textSpacing={true} + align="left" + flex={true} + > + <NextText type="h5">员工列表</NextText> + </NextP> + } + headerTest={[]} + headerProps={{ background: 'surface' }} + footer={null} + minHeight="100vh" + > + <NextBlock + prefix="next-" + placeholderStyle={{ height: '100%' }} + noPadding={false} + noBorder={false} + background="surface" + colSpan={12} + rowSpan={1} + childTotalColumns="1fr" + > + <NextBlockCell + title="" + primaryKey="732" + prefix="next-" + placeholderStyle={{ height: '100%' }} + colSpan={1} + rowSpan={1} + > + <NextP + wrap={true} + type="body2" + textSpacing={true} + verAlign="center" + align="flex-start" + flex={true} + > + <AliSearchTable + dataSource={__$$eval(() => this.state.users.data)} + rowKey="workid" + columns={[ + { title: '花名', dataIndex: 'cname' }, + { title: 'user_id', dataIndex: 'workid' }, + { title: '部门', dataIndex: 'dep' }, + ]} + searchItems={[ + { label: '姓名', name: 'cname' }, + { label: '部门', name: 'dep' }, + ]} + onSearch={function () { + return this.onSearch.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + onClear={function () { + return this.onClear.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + pagination={{ + defaultPageSize: '', + onPageChange: function () { + return this.onPageChange.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this), + showSizeChanger: true, + }} + /> + </NextP> + </NextBlockCell> + </NextBlock> + </NextPage> + <NextPage + columns={12} + headerDivider={true} + placeholderStyle={{ gridRowEnd: 'span 1', gridColumnEnd: 'span 12' }} + placeholder="页面主体内容:拖拽Block布局组件到这里" + header={null} + headerProps={{ background: 'surface' }} + footer={null} + minHeight="100vh" + > + <NextBlock + prefix="next-" + placeholderStyle={{ height: '100%' }} + noPadding={false} + noBorder={false} + background="surface" + colSpan={12} + rowSpan={1} + childTotalColumns={1} + > + <NextBlockCell + title="" + primaryKey="472" + prefix="next-" + placeholderStyle={{ height: '100%' }} + colSpan={1} + rowSpan={1} + /> + </NextBlock> + </NextPage> + </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; +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/pages/layout.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/pages/layout.jsx new file mode 100644 index 000000000..50fbb2d1f --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/pages/layout.jsx @@ -0,0 +1,10 @@ +import { Outlet } from 'ice'; +import BasicLayout from '@/layouts/BasicLayout'; + +export default function Layout() { + return ( + <BasicLayout> + <Outlet /> + </BasicLayout> + ); +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/typings.d.ts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/typings.d.ts new file mode 100644 index 000000000..a9f8de7ce --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/typings.d.ts @@ -0,0 +1,9 @@ +/// <reference types="@ice/app/types" /> + +export {}; +declare global { + interface Window { + g_config: Record<string, any>; + } +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/utils.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/utils.js new file mode 100644 index 000000000..119071792 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/expected/demo-project/src/utils.js @@ -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 {}; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/schema.json5 b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/schema.json5 new file mode 100644 index 000000000..ca97204e9 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo4/schema.json5 @@ -0,0 +1,353 @@ +{ + "version": "1.0.0", + "componentsMap": [ + { + "package": "@alife/mc-assets-1935", + "version": "0.1.9", + "exportName": "AliSearchTable", + "main": "build/lowcode/index.js", + "subName": "default", + "destructuring": true, + "componentName": "AliSearchTable" + }, + { + "package": "@alife/container", + "version": "^1.0.0", + "exportName": "P", + "main": "lib/index.js", + "destructuring": true, + "subName": "", + "componentName": "NextP" + }, + { + "package": "@alife/container", + "version": "^1.0.0", + "exportName": "Block", + "main": "lib/index.js", + "destructuring": true, + "subName": "Cell", + "componentName": "NextBlockCell" + }, + { + "package": "@alife/container", + "version": "^1.0.0", + "exportName": "Block", + "main": "lib/index.js", + "destructuring": true, + "subName": "", + "componentName": "NextBlock" + }, + { + "package": "@alife/container", + "version": "^1.0.0", + "exportName": "Text", + "main": "lib/index.js", + "destructuring": true, + "subName": "", + "componentName": "NextText" + }, + { + "package": "@alife/container", + "version": "^1.0.0", + "exportName": "Page", + "main": "lib/index.js", + "destructuring": true, + "subName": "", + "componentName": "NextPage" + } + ], + "componentsTree": [ + { + "componentName": "Page", + "id": "node_dockcviv8fo1", + "props": { + "ref": "outterView", + "style": { + "height": "100%" + } + }, + "fileName": "test", + "dataSource": { + "list": [ + { + "type": "fetch", + "isInit": true, + "options": { + "params": {}, + "method": "GET", + "isCors": true, + "timeout": 5000, + "headers": {}, + "uri": "https://mocks.xxx.com/mock/jjpin/user/list" + }, + "id": "users" + } + ] + }, + "css": "body {\n font-size: 12px;\n}\n\n.botton {\n width: 100px;\n color: #ff00ff\n}", + "lifeCycles": { + "componentDidMount": { + "type": "JSFunction", + "value": "function() {\n console.log('did mount');\n }" + }, + "componentWillUnmount": { + "type": "JSFunction", + "value": "function() {\n console.log('will umount');\n }" + }, + "componentDidUpdate": { + "type": "JSFunction", + "value": "function(prevProps, prevState, snapshot) {\n console.log(this.state);\n }" + } + }, + "methods": { + "testFunc": { + "type": "JSFunction", + "value": "function() {\n console.log('test func');\n }" + }, + "onClick": { + "type": "JSFunction", + "value": "function() {\n this.setState({\n isShowDialog: true\n })\n }" + }, + "closeDialog": { + "type": "JSFunction", + "value": "function() {\n this.setState({\n isShowDialog: false\n })\n }" + }, + "onSearch": { + "type": "JSFunction", + "value": "function(values) {\n console.log('search form:', values)\n console.log(this.dataSourceMap);\n this.dataSourceMap['users'].load(values)\n }" + }, + "onClear": { + "type": "JSFunction", + "value": "function() {\n console.log('form reset')\n this.setState({\n isShowDialog: true\n })\n }" + }, + "onPageChange": { + "type": "JSFunction", + "value": "function(page, pageSize) {\n console.log(`page: ${page}, pageSize: ${pageSize}`)\n }" + } + }, + "state": { + "text": "outter", + "isShowDialog": false + }, + "children": [ + { + "componentName": "NextPage", + "id": "node_ockkgjwi8z1", + "props": { + "columns": 12, + "placeholderStyle": { + "gridRowEnd": "span 1", + "gridColumnEnd": "span 12" + }, + "placeholder": "页面主体内容:拖拽Block布局组件到这里", + "header": { + "type": "JSSlot", + "value": [ + { + "componentName": "NextP", + "id": "node_ockkgjwi8zn", + "props": { + "wrap": true, + "type": "body2", + "verAlign": "middle", + "textSpacing": true, + "align": "left", + "flex": true + }, + "children": [ + { + "componentName": "NextText", + "id": "node_ockkgjwi8zo", + "props": { + "type": "h5", + "children": "员工列表" + } + } + ] + } + ], + "title": "header" + }, + "headerTest": { + "type": "JSSlot", + "value": [], + "title": "header" + }, + "headerProps": { + "background": "surface" + }, + "footer": { + "type": "JSSlot", + "title": "footer" + }, + "minHeight": "100vh" + }, + "children": [ + { + "componentName": "NextBlock", + "id": "node_ockkgjwi8z2", + "props": { + "prefix": "next-", + "placeholderStyle": { + "height": "100%" + }, + "noPadding": false, + "noBorder": false, + "background": "surface", + "colSpan": 12, + "rowSpan": 1, + "childTotalColumns": "1fr" + }, + "title": "分区", + "children": [ + { + "componentName": "NextBlockCell", + "id": "node_ockkgjwi8z3", + "props": { + "title": "", + "primaryKey": "732", + "prefix": "next-", + "placeholderStyle": { + "height": "100%" + }, + "colSpan": 1, + "rowSpan": 1 + }, + "children": [ + { + "componentName": "NextP", + "id": "node_ockkgjwi8zu", + "props": { + "wrap": true, + "type": "body2", + "textSpacing": true, + "verAlign": "center", + "align": "flex-start", + "flex": true + }, + "children": [ + { + "componentName": "AliSearchTable", + "id": "node_ockkgjwi8zv", + "props": { + "dataSource": { + "type": "JSExpression", + "value": "this.state.users.data" + }, + "rowKey": "workid", + "columns": [ + { + "title": "花名", + "dataIndex": "cname" + }, + { + "title": "user_id", + "dataIndex": "workid" + }, + { + "title": "部门", + "dataIndex": "dep" + } + ], + "searchItems": [ + { + "label": "姓名", + "name": "cname" + }, + { + "label": "部门", + "name": "dep" + } + ], + "onSearch": { + "type": "JSFunction", + "value": "function(){ return this.onSearch.apply(this,Array.prototype.slice.call(arguments).concat([])) }" + }, + "onClear": { + "type": "JSFunction", + "value": "function(){ return this.onClear.apply(this,Array.prototype.slice.call(arguments).concat([])) }" + }, + "pagination": { + "defaultPageSize": "", + "onPageChange": { + "type": "JSFunction", + "value": "function(){ return this.onPageChange.apply(this,Array.prototype.slice.call(arguments).concat([])) }" + }, + "showSizeChanger": true + } + } + } + ] + } + ] + } + ] + } + ] + }, + { + "componentName": "NextPage", + "id": "node_ockm4jxd6313", + "props": { + "columns": 12, + "headerDivider": true, + "placeholderStyle": { + "gridRowEnd": "span 1", + "gridColumnEnd": "span 12" + }, + "placeholder": "页面主体内容:拖拽Block布局组件到这里", + "header": { + "type": "JSSlot", + "title": "header" + }, + "headerProps": { + "background": "surface" + }, + "footer": { + "type": "JSSlot", + "title": "footer" + }, + "minHeight": "100vh" + }, + "title": "页面", + "children": [ + { + "componentName": "NextBlock", + "id": "node_ockm4jxd6314", + "props": { + "prefix": "next-", + "placeholderStyle": { + "height": "100%" + }, + "noPadding": false, + "noBorder": false, + "background": "surface", + "colSpan": 12, + "rowSpan": 1, + "childTotalColumns": 1 + }, + "title": "区块", + "children": [ + { + "componentName": "NextBlockCell", + "id": "node_ockm4jxd6315", + "props": { + "title": "", + "primaryKey": "472", + "prefix": "next-", + "placeholderStyle": { + "height": "100%" + }, + "colSpan": 1, + "rowSpan": 1 + } + } + ] + } + ] + } + ] + } + ], + "i18n": {} +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/.browserslistrc b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/.browserslistrc new file mode 100644 index 000000000..55a130413 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/.browserslistrc @@ -0,0 +1,3 @@ +defaults +ios_saf 9 + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/.gitignore b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/.gitignore new file mode 100644 index 000000000..4ec178818 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/.gitignore @@ -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 + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/README.md b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/README.md new file mode 100644 index 000000000..6d9dd7521 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/README.md @@ -0,0 +1 @@ +This project is generated by lowcode-code-generator & lowcode-solution-icejs3. \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/ice.config.mts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/ice.config.mts new file mode 100644 index 000000000..fe77c9b6c --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/ice.config.mts @@ -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(), + ], +})); + diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/package.json b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/package.json new file mode 100644 index 000000000..a62526e76 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/package.json @@ -0,0 +1,46 @@ +{ + "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", + "undefined": "*", + "@alife/container": "0.3.7", + "@alilc/antd-lowcode": "0.5.4", + "@alife/mc-assets-1935": "0.1.16" + }, + "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" +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/app.ts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/app.ts new file mode 100644 index 000000000..6d5856292 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/app.ts @@ -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: '/', + }, +})); diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/constants.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/constants.js new file mode 100644 index 000000000..ea766c9da --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/constants.js @@ -0,0 +1,3 @@ +const __$$constants = {}; + +export default __$$constants; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/document.tsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/document.tsx new file mode 100644 index 000000000..286be9f8c --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/document.tsx @@ -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> + ); +} \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/global.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/global.scss new file mode 100644 index 000000000..82ca3eac7 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/global.scss @@ -0,0 +1,6 @@ +// 引入默认全局样式 +@import '@alifd/next/reset.scss'; + +body { + -webkit-font-smoothing: antialiased; +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/i18n.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/i18n.js new file mode 100644 index 000000000..adbbe673d --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/i18n.js @@ -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 }; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx new file mode 100644 index 000000000..cc70d53be --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss new file mode 100644 index 000000000..81e77fda5 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss @@ -0,0 +1,15 @@ + +.footer { + line-height: 20px; + text-align: center; +} + +.logo { + font-weight: bold; + font-size: 16px; +} + +.copyright { + font-size: 12px; +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx new file mode 100644 index 000000000..265bfdaa0 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss new file mode 100644 index 000000000..1ab56d394 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss @@ -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; + } +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx new file mode 100644 index 000000000..911998b0d --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx @@ -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; + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/layouts/BasicLayout/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/layouts/BasicLayout/index.jsx new file mode 100644 index 000000000..18db44df5 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/layouts/BasicLayout/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/layouts/BasicLayout/menuConfig.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/layouts/BasicLayout/menuConfig.js new file mode 100644 index 000000000..5332202be --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/layouts/BasicLayout/menuConfig.js @@ -0,0 +1,11 @@ + +const headerMenuConfig = []; +const asideMenuConfig = [ + { + name: 'Dashboard', + path: '/', + icon: 'smile', + }, +]; +export { headerMenuConfig, asideMenuConfig }; + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/pages/Test/index.css b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/pages/Test/index.css new file mode 100644 index 000000000..066114aee --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/pages/Test/index.css @@ -0,0 +1,8 @@ +body { + font-size: 12px; +} + +.botton { + width: 100px; + color: #ff00ff; +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/pages/Test/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/pages/Test/index.jsx new file mode 100644 index 000000000..7427f164d --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/pages/Test/index.jsx @@ -0,0 +1,389 @@ +// 注意: 出码引擎注入的临时变量默认都以 "__$$" 开头,禁止在搭建的代码中直接访问。 +// 例外:react 框架的导出名和各种组件名除外。 +import React from 'react'; + +import { + Page as NextPage, + Block as NextBlock, + P as NextP, +} from '@alife/container/lib/index.js'; + +import { + Card, + Space, + Typography, + Select, + Button, + Modal, + Form, + InputNumber, + Input, +} from '@alilc/antd-lowcode/dist/antd-lowcode.esm.js'; + +import { AliAutoSearchTable } from '@alife/mc-assets-1935/build/lowcode/index.js'; + +import utils, { RefsManager } from '../../utils'; + +import * as __$$i18n from '../../i18n'; + +import __$$constants from '../../constants'; + +import './index.css'; + +const NextBlockCell = NextBlock.Cell; + +const AliAutoSearchTableDefault = AliAutoSearchTable.default; + +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 = { + name: 'nongzhou', + gateways: [], + selectedGateway: null, + records: [], + modalVisible: false, + }; + } + + $ = (refName) => { + return this._refsManager.get(refName); + }; + + $$ = (refName) => { + return this._refsManager.getAll(refName); + }; + + componentWillUnmount() { + /* ... */ + } + + componentDidUpdate() { + /* ... */ + } + + onChange() { + /* ... */ + } + + getActions() { + /* ... */ + } + + onCreateOrder() { + /* ... */ + } + + onCancelModal() { + /* ... */ + } + + onConfirmCreateOrder() { + /* ... */ + } + + componentDidMount() {} + + render() { + const __$$context = this._context || this; + const { state } = __$$context; + return ( + <div + ref={this._refsManager.linkRef('outterView')} + style={{ height: '100%' }} + > + <NextPage + columns={12} + headerDivider={true} + placeholderStyle={{ gridRowEnd: 'span 1', gridColumnEnd: 'span 12' }} + placeholder="页面主体内容:拖拽Block布局组件到这里" + header={null} + headerProps={{ background: 'surface' }} + footer={null} + minHeight="100vh" + style={{ cursor: 'pointer' }} + > + <NextBlock + prefix="next-" + placeholderStyle={{ height: '100%' }} + noPadding={false} + noBorder={false} + background="surface" + layoutmode="O" + colSpan={12} + rowSpan={1} + childTotalColumns={12} + > + <NextBlockCell + title="" + prefix="next-" + placeholderStyle={{ height: '100%' }} + layoutmode="O" + childTotalColumns={12} + isAutoContainer={true} + colSpan={12} + rowSpan={1} + > + <NextP + wrap={false} + type="body2" + verAlign="middle" + textSpacing={true} + align="left" + full={true} + flex={true} + > + <Card title=""> + <Space size={0} align="center" direction="horizontal"> + <Typography.Text>所在网关:</Typography.Text> + <Select + style={{ + marginTop: '16px', + marginRight: '16px', + marginBottom: '16px', + marginLeft: '16px', + width: '400px', + display: 'inline-block', + }} + options={__$$eval(() => this.state.gateways)} + mode="single" + defaultValue={['auto-edd-uniproxy']} + labelInValue={true} + showSearch={true} + allowClear={false} + placeholder="请选取网关" + showArrow={true} + loading={false} + tokenSeparators={[]} + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onChange', + relatedEventName: 'onChange', + }, + ], + eventList: [ + { name: 'onBlur', disabled: false }, + { name: 'onChange', disabled: true }, + { name: 'onDeselect', disabled: false }, + { name: 'onFocus', disabled: false }, + { name: 'onInputKeyDown', disabled: false }, + { name: 'onMouseEnter', disabled: false }, + { name: 'onMouseLeave', disabled: false }, + { name: 'onPopupScroll', disabled: false }, + { name: 'onSearch', disabled: false }, + { name: 'onSelect', disabled: false }, + { name: 'onDropdownVisibleChange', disabled: false }, + ], + }} + onChange={function () { + this.onChange.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + /> + </Space> + <Button + type="primary" + style={{ + display: 'block', + marginTop: '20px', + marginBottom: '20px', + }} + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'onCreateOrder', + }, + ], + eventList: [{ name: 'onClick', disabled: true }], + }} + onClick={function () { + this.onCreateOrder.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + > + 创建发布单 + </Button> + <Modal + title="创建发布单" + visible={__$$eval(() => this.state.modalVisible)} + footer="" + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onCancel', + relatedEventName: 'onCancelModal', + }, + ], + eventList: [ + { name: 'onCancel', disabled: true }, + { name: 'onOk', disabled: false }, + ], + }} + onCancel={function () { + this.onCancelModal.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + zIndex={2000} + > + <Form + labelCol={{ span: 6 }} + wrapperCol={{ span: 14 }} + onFinish={function () { + this.onConfirmCreateOrder.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + name="basic" + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onFinish', + relatedEventName: 'onConfirmCreateOrder', + }, + ], + eventList: [ + { name: 'onFinish', disabled: true }, + { name: 'onFinishFailed', disabled: false }, + { name: 'onFieldsChange', disabled: false }, + { name: 'onValuesChange', disabled: false }, + ], + }} + > + <Form.Item label="发布批次"> + <InputNumber value={3} min={1} /> + </Form.Item> + <Form.Item label="批次间隔时间"> + <InputNumber value={3} /> + </Form.Item> + <Form.Item label="备注 "> + <Input.TextArea rows={3} placeholder="请输入" /> + </Form.Item> + <Form.Item + wrapperCol={{ offset: 6 }} + style={{ + flexDirection: 'row', + alignItems: 'flex-end', + justifyContent: 'center', + display: 'flex', + }} + labelAlign="right" + > + <Button type="primary" htmlType="submit"> + 提交 + </Button> + <Button + style={{ marginLeft: 20 }} + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'onCancelModal', + }, + ], + eventList: [{ name: 'onClick', disabled: true }], + }} + onClick={function () { + this.onCancelModal.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + > + 取消 + </Button> + </Form.Item> + </Form> + </Modal> + <AliAutoSearchTableDefault + rowKey="key" + dataSource={__$$eval(() => this.state.records)} + columns={[ + { + title: '发布名称', + dataIndex: 'order_name', + key: 'name', + }, + { + title: '类型', + dataIndex: 'order_type_desc', + key: 'age', + }, + { + title: '发布状态', + dataIndex: 'order_status_desc', + key: 'address', + }, + { title: '发布人', dataIndex: 'creator_name' }, + { title: '当前批次/总批次', dataIndex: 'cur_batch_no' }, + { + title: '发布机器/总机器', + dataIndex: 'pubblish_ip_finish_num', + }, + { title: '发布时间', dataIndex: 'publish_id' }, + ]} + actions={__$$eval(() => this.actions || [])} + getActions={function () { + return this.getActions.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + /> + </Card> + </NextP> + </NextBlockCell> + </NextBlock> + </NextPage> + </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; +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/pages/layout.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/pages/layout.jsx new file mode 100644 index 000000000..50fbb2d1f --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/pages/layout.jsx @@ -0,0 +1,10 @@ +import { Outlet } from 'ice'; +import BasicLayout from '@/layouts/BasicLayout'; + +export default function Layout() { + return ( + <BasicLayout> + <Outlet /> + </BasicLayout> + ); +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/typings.d.ts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/typings.d.ts new file mode 100644 index 000000000..a9f8de7ce --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/typings.d.ts @@ -0,0 +1,9 @@ +/// <reference types="@ice/app/types" /> + +export {}; +declare global { + interface Window { + g_config: Record<string, any>; + } +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/utils.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/utils.js new file mode 100644 index 000000000..119071792 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/expected/demo-project/src/utils.js @@ -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 {}; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/schema.json5 b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/schema.json5 new file mode 100644 index 000000000..b59b97e76 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo5/schema.json5 @@ -0,0 +1,677 @@ +{ + version: '1.0.0', + componentsMap: [ + { + package: '@alilc/antd-lowcode', + version: '0.5.4', + exportName: 'Typography', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + subName: 'Text', + componentName: 'Typography.Text', + }, + { + package: '@alilc/antd-lowcode', + version: '0.5.4', + exportName: 'Select', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'Select', + }, + { + package: '@alilc/antd-lowcode', + version: '0.5.4', + exportName: 'Space', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'Space', + }, + { + package: '@alilc/antd-lowcode', + version: '0.5.4', + exportName: 'Button', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'Button', + }, + { + package: '@alilc/antd-lowcode', + version: '0.5.4', + exportName: 'InputNumber', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'InputNumber', + }, + { + package: '@alilc/antd-lowcode', + version: '0.5.4', + exportName: 'Form', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + subName: 'Item', + componentName: 'Form.Item', + }, + { + package: '@alilc/antd-lowcode', + version: '0.5.4', + exportName: 'Input', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + subName: 'TextArea', + componentName: 'Input.TextArea', + }, + { + package: '@alilc/antd-lowcode', + version: '0.5.4', + exportName: 'Form', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'Form', + }, + { + package: '@alilc/antd-lowcode', + version: '0.5.4', + exportName: 'Modal', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'Modal', + }, + { + package: '@alife/mc-assets-1935', + version: '0.1.16', + exportName: 'AliAutoSearchTable', + main: 'build/lowcode/index.js', + destructuring: true, + subName: 'default', + componentName: 'AliAutoSearchTableDefault', + }, + { + package: '@alilc/antd-lowcode', + version: '0.5.4', + exportName: 'Card', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'Card', + }, + { + package: '@alife/container', + version: '0.3.7', + exportName: 'P', + main: 'lib/index.js', + destructuring: true, + subName: '', + componentName: 'NextP', + }, + { + package: '@alife/container', + version: '0.3.7', + exportName: 'Block', + main: 'lib/index.js', + destructuring: true, + subName: 'Cell', + componentName: 'NextBlockCell', + }, + { + package: '@alife/container', + version: '0.3.7', + exportName: 'Block', + main: 'lib/index.js', + destructuring: true, + subName: '', + componentName: 'NextBlock', + }, + { + devMode: 'lowcode', + componentName: 'Slot', + }, + { + package: '@alife/container', + version: '0.3.7', + exportName: 'Page', + main: 'lib/index.js', + destructuring: true, + subName: '', + componentName: 'NextPage', + }, + { + devMode: 'lowcode', + componentName: 'Page', + }, + ], + componentsTree: [ + { + componentName: 'Page', + id: 'node_dockcviv8fo1', + props: { + ref: 'outterView', + style: { + height: '100%', + }, + }, + fileName: 'test', + dataSource: { + list: [], + }, + css: 'body {\n font-size: 12px;\n}\n\n.botton {\n width: 100px;\n color: #ff00ff\n}', + lifeCycles: { + componentDidMount: { + type: 'JSFunction', + value: 'function() { /* ... */ }', + }, + componentWillUnmount: { + type: 'JSFunction', + value: 'function() { /* ... */ }', + }, + componentDidUpdate: { + type: 'JSFunction', + value: 'function() { /* ... */ }', + }, + }, + methods: { + onChange: { + type: 'JSFunction', + value: 'function() { /* ... */ }', + }, + getActions: { + type: 'JSFunction', + value: 'function() { /* ... */ }', + }, + onCreateOrder: { + type: 'JSFunction', + value: 'function() { /* ... */ }', + }, + onCancelModal: { + type: 'JSFunction', + value: 'function() { /* ... */ }', + }, + onConfirmCreateOrder: { + type: 'JSFunction', + value: 'function() { /* ... */ }', + }, + }, + state: { + name: 'nongzhou', + gateways: [], + selectedGateway: null, + records: [], + modalVisible: false, + }, + children: [ + { + componentName: 'NextPage', + id: 'node_ocknqx3esma', + props: { + columns: 12, + headerDivider: true, + placeholderStyle: { + gridRowEnd: 'span 1', + gridColumnEnd: 'span 12', + }, + placeholder: '页面主体内容:拖拽Block布局组件到这里', + header: { + type: 'JSSlot', + title: 'header', + }, + headerProps: { + background: 'surface', + }, + footer: { + type: 'JSSlot', + title: 'footer', + }, + minHeight: '100vh', + style: { + cursor: 'pointer', + }, + }, + title: '页面', + children: [ + { + componentName: 'NextBlock', + id: 'node_ocknqx3esmb', + props: { + prefix: 'next-', + placeholderStyle: { + height: '100%', + }, + noPadding: false, + noBorder: false, + background: 'surface', + layoutmode: 'O', + colSpan: 12, + rowSpan: 1, + childTotalColumns: 12, + }, + title: '区块', + children: [ + { + componentName: 'NextBlockCell', + id: 'node_ocknqx3esmc', + props: { + title: '', + prefix: 'next-', + placeholderStyle: { + height: '100%', + }, + layoutmode: 'O', + childTotalColumns: 12, + isAutoContainer: true, + colSpan: 12, + rowSpan: 1, + }, + children: [ + { + componentName: 'NextP', + id: 'node_ocknqx3esm1j', + props: { + wrap: false, + type: 'body2', + verAlign: 'middle', + textSpacing: true, + align: 'left', + full: true, + flex: true, + }, + title: '段落', + children: [ + { + componentName: 'Card', + id: 'node_ocknqx3esm1k', + props: { + title: '', + }, + children: [ + { + componentName: 'Space', + id: 'node_ocknqx3esm1n', + props: { + size: 0, + align: 'center', + direction: 'horizontal', + }, + children: [ + { + componentName: 'Typography.Text', + id: 'node_ocknqx3esm1l', + props: { + children: '所在网关:', + }, + }, + { + componentName: 'Select', + id: 'node_ocknqx3esm1m', + props: { + style: { + marginTop: '16px', + marginRight: '16px', + marginBottom: '16px', + marginLeft: '16px', + width: '400px', + display: 'inline-block', + }, + options: { + type: 'JSExpression', + value: 'this.state.gateways', + }, + mode: 'single', + defaultValue: ['auto-edd-uniproxy'], + labelInValue: true, + showSearch: true, + allowClear: false, + placeholder: '请选取网关', + showArrow: true, + loading: false, + tokenSeparators: [], + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onChange', + relatedEventName: 'onChange', + }, + ], + eventList: [ + { + name: 'onBlur', + disabled: false, + }, + { + name: 'onChange', + disabled: true, + }, + { + name: 'onDeselect', + disabled: false, + }, + { + name: 'onFocus', + disabled: false, + }, + { + name: 'onInputKeyDown', + disabled: false, + }, + { + name: 'onMouseEnter', + disabled: false, + }, + { + name: 'onMouseLeave', + disabled: false, + }, + { + name: 'onPopupScroll', + disabled: false, + }, + { + name: 'onSearch', + disabled: false, + }, + { + name: 'onSelect', + disabled: false, + }, + { + name: 'onDropdownVisibleChange', + disabled: false, + }, + ], + }, + onChange: { + type: 'JSFunction', + value: 'function(){this.onChange.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + }, + }, + ], + }, + { + componentName: 'Button', + id: 'node_ockntwgdsn7', + props: { + type: 'primary', + children: '创建发布单', + style: { + display: 'block', + marginTop: '20px', + marginBottom: '20px', + }, + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'onCreateOrder', + }, + ], + eventList: [ + { + name: 'onClick', + disabled: true, + }, + ], + }, + onClick: { + type: 'JSFunction', + value: 'function(){this.onCreateOrder.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + }, + }, + { + componentName: 'Modal', + id: 'node_ockntx4eo9p', + props: { + title: '创建发布单', + visible: { + type: 'JSExpression', + value: 'this.state.modalVisible', + }, + footer: '', + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onCancel', + relatedEventName: 'onCancelModal', + }, + ], + eventList: [ + { + name: 'onCancel', + disabled: true, + }, + { + name: 'onOk', + disabled: false, + }, + ], + }, + onCancel: { + type: 'JSFunction', + value: 'function(){this.onCancelModal.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + zIndex: 2000, + }, + hidden: true, + children: [ + { + componentName: 'Form', + id: 'node_ockntx4eo9s', + props: { + labelCol: { + span: 6, + }, + wrapperCol: { + span: 14, + }, + onFinish: { + type: 'JSFunction', + value: 'function(){this.onConfirmCreateOrder.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + name: 'basic', + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onFinish', + relatedEventName: 'onConfirmCreateOrder', + }, + ], + eventList: [ + { + name: 'onFinish', + disabled: true, + }, + { + name: 'onFinishFailed', + disabled: false, + }, + { + name: 'onFieldsChange', + disabled: false, + }, + { + name: 'onValuesChange', + disabled: false, + }, + ], + }, + }, + children: [ + { + componentName: 'Form.Item', + id: 'node_ockntx4eo91k', + props: { + label: '发布批次', + }, + children: [ + { + componentName: 'InputNumber', + id: 'node_ockntx4eo91l', + props: { + value: 3, + min: 1, + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_ockntx4eo91r', + props: { + label: '批次间隔时间', + }, + children: [ + { + componentName: 'InputNumber', + id: 'node_ockntx4eo91s', + props: { + value: 3, + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_ockntx4eo91y', + props: { + label: '备注 ', + }, + children: [ + { + componentName: 'Input.TextArea', + id: 'node_ockntx4eo91z', + props: { + rows: 3, + placeholder: '请输入', + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_ockntx4eo9v', + props: { + wrapperCol: { + offset: 6, + }, + style: { + flexDirection: 'row', + alignItems: 'flex-end', + justifyContent: 'center', + display: 'flex', + }, + labelAlign: 'right', + }, + children: [ + { + componentName: 'Button', + id: 'node_ockntx4eo9w', + props: { + type: 'primary', + children: '提交', + htmlType: 'submit', + }, + }, + { + componentName: 'Button', + id: 'node_ockntx4eo9x', + props: { + style: { + marginLeft: 20, + }, + children: '取消', + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'onCancelModal', + }, + ], + eventList: [ + { + name: 'onClick', + disabled: true, + }, + ], + }, + onClick: { + type: 'JSFunction', + value: 'function(){this.onCancelModal.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + }, + }, + ], + }, + ], + }, + ], + }, + { + componentName: 'AliAutoSearchTableDefault', + id: 'node_ocknqx3esm1q', + props: { + rowKey: 'key', + dataSource: { + type: 'JSExpression', + value: 'this.state.records', + }, + columns: [ + { + title: '发布名称', + dataIndex: 'order_name', + key: 'name', + }, + { + title: '类型', + dataIndex: 'order_type_desc', + key: 'age', + }, + { + title: '发布状态', + dataIndex: 'order_status_desc', + key: 'address', + }, + { + title: '发布人', + dataIndex: 'creator_name', + }, + { + title: '当前批次/总批次', + dataIndex: 'cur_batch_no', + }, + { + title: '发布机器/总机器', + dataIndex: 'pubblish_ip_finish_num', + }, + { + title: '发布时间', + dataIndex: 'publish_id', + }, + ], + actions: { + type: 'JSExpression', + value: 'this.actions || []', + }, + getActions: { + type: 'JSFunction', + value: 'function(){ return this.getActions.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + }, + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + i18n: {}, +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/.browserslistrc b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/.browserslistrc new file mode 100644 index 000000000..55a130413 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/.browserslistrc @@ -0,0 +1,3 @@ +defaults +ios_saf 9 + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/.gitignore b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/.gitignore new file mode 100644 index 000000000..4ec178818 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/.gitignore @@ -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 + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/README.md b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/README.md new file mode 100644 index 000000000..6d9dd7521 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/README.md @@ -0,0 +1 @@ +This project is generated by lowcode-code-generator & lowcode-solution-icejs3. \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/ice.config.mts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/ice.config.mts new file mode 100644 index 000000000..fe77c9b6c --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/ice.config.mts @@ -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(), + ], +})); + diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/package.json b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/package.json new file mode 100644 index 000000000..54a1e4e12 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/package.json @@ -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" +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/app.ts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/app.ts new file mode 100644 index 000000000..6d5856292 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/app.ts @@ -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: '/', + }, +})); diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/constants.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/constants.js new file mode 100644 index 000000000..91198f904 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/constants.js @@ -0,0 +1,3 @@ +const __$$constants = { ENV: 'prod', DOMAIN: 'xxx.xxx.com' }; + +export default __$$constants; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/document.tsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/document.tsx new file mode 100644 index 000000000..286be9f8c --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/document.tsx @@ -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> + ); +} \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/global.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/global.scss new file mode 100644 index 000000000..ed7204b4a --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/global.scss @@ -0,0 +1,13 @@ +// 引入默认全局样式 +@import '@alifd/next/reset.scss'; + +body { + -webkit-font-smoothing: antialiased; +} + +body { + font-size: 12px; +} +.table { + width: 100px; +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/i18n.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/i18n.js new file mode 100644 index 000000000..adbbe673d --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/i18n.js @@ -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 }; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx new file mode 100644 index 000000000..cc70d53be --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss new file mode 100644 index 000000000..81e77fda5 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss @@ -0,0 +1,15 @@ + +.footer { + line-height: 20px; + text-align: center; +} + +.logo { + font-weight: bold; + font-size: 16px; +} + +.copyright { + font-size: 12px; +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx new file mode 100644 index 000000000..265bfdaa0 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss new file mode 100644 index 000000000..1ab56d394 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss @@ -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; + } +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx new file mode 100644 index 000000000..911998b0d --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx @@ -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; + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/layouts/BasicLayout/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/layouts/BasicLayout/index.jsx new file mode 100644 index 000000000..18db44df5 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/layouts/BasicLayout/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/layouts/BasicLayout/menuConfig.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/layouts/BasicLayout/menuConfig.js new file mode 100644 index 000000000..5332202be --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/layouts/BasicLayout/menuConfig.js @@ -0,0 +1,11 @@ + +const headerMenuConfig = []; +const asideMenuConfig = [ + { + name: 'Dashboard', + path: '/', + icon: 'smile', + }, +]; +export { headerMenuConfig, asideMenuConfig }; + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/pages/Test/index.css b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/pages/Test/index.css new file mode 100644 index 000000000..e69de29bb diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/pages/Test/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/pages/Test/index.jsx new file mode 100644 index 000000000..c27cce153 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/pages/Test/index.jsx @@ -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) => + !!false && ( + <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; +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/pages/layout.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/pages/layout.jsx new file mode 100644 index 000000000..50fbb2d1f --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/pages/layout.jsx @@ -0,0 +1,10 @@ +import { Outlet } from 'ice'; +import BasicLayout from '@/layouts/BasicLayout'; + +export default function Layout() { + return ( + <BasicLayout> + <Outlet /> + </BasicLayout> + ); +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/typings.d.ts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/typings.d.ts new file mode 100644 index 000000000..a9f8de7ce --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/typings.d.ts @@ -0,0 +1,9 @@ +/// <reference types="@ice/app/types" /> + +export {}; +declare global { + interface Window { + g_config: Record<string, any>; + } +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/utils.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/utils.js new file mode 100644 index 000000000..119071792 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/expected/demo-project/src/utils.js @@ -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 {}; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/schema.json5 b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/schema.json5 new file mode 100644 index 000000000..5b6776c1e --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo6-literal-condition/schema.json5 @@ -0,0 +1,273 @@ +{ + 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: 'JSFunction', + 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: 'JSFunction', + value: 'function (response) {\nif (!response.data.success){\n throw new Error(response.data.message);\n }\n return response.data.data.result;\n}', + }, + }, + ], + dataHandler: { + type: 'JSFunction', + 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: false, + 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: '月飞', + }, +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/.browserslistrc b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/.browserslistrc new file mode 100644 index 000000000..55a130413 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/.browserslistrc @@ -0,0 +1,3 @@ +defaults +ios_saf 9 + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/.gitignore b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/.gitignore new file mode 100644 index 000000000..4ec178818 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/.gitignore @@ -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 + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/README.md b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/README.md new file mode 100644 index 000000000..6d9dd7521 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/README.md @@ -0,0 +1 @@ +This project is generated by lowcode-code-generator & lowcode-solution-icejs3. \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/ice.config.mts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/ice.config.mts new file mode 100644 index 000000000..fe77c9b6c --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/ice.config.mts @@ -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(), + ], +})); + diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/package.json b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/package.json new file mode 100644 index 000000000..399ff4333 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/package.json @@ -0,0 +1,45 @@ +{ + "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", + "undefined": "*", + "@alilc/antd-lowcode": "0.8.0", + "@alife/container": "0.3.7" + }, + "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" +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/app.ts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/app.ts new file mode 100644 index 000000000..6d5856292 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/app.ts @@ -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: '/', + }, +})); diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/constants.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/constants.js new file mode 100644 index 000000000..ea766c9da --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/constants.js @@ -0,0 +1,3 @@ +const __$$constants = {}; + +export default __$$constants; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/document.tsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/document.tsx new file mode 100644 index 000000000..286be9f8c --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/document.tsx @@ -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> + ); +} \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/global.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/global.scss new file mode 100644 index 000000000..82ca3eac7 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/global.scss @@ -0,0 +1,6 @@ +// 引入默认全局样式 +@import '@alifd/next/reset.scss'; + +body { + -webkit-font-smoothing: antialiased; +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/i18n.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/i18n.js new file mode 100644 index 000000000..adbbe673d --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/i18n.js @@ -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 }; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx new file mode 100644 index 000000000..cc70d53be --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss new file mode 100644 index 000000000..81e77fda5 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss @@ -0,0 +1,15 @@ + +.footer { + line-height: 20px; + text-align: center; +} + +.logo { + font-weight: bold; + font-size: 16px; +} + +.copyright { + font-size: 12px; +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx new file mode 100644 index 000000000..265bfdaa0 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss new file mode 100644 index 000000000..1ab56d394 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss @@ -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; + } +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx new file mode 100644 index 000000000..911998b0d --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx @@ -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; + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/layouts/BasicLayout/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/layouts/BasicLayout/index.jsx new file mode 100644 index 000000000..18db44df5 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/layouts/BasicLayout/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/layouts/BasicLayout/menuConfig.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/layouts/BasicLayout/menuConfig.js new file mode 100644 index 000000000..5332202be --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/layouts/BasicLayout/menuConfig.js @@ -0,0 +1,11 @@ + +const headerMenuConfig = []; +const asideMenuConfig = [ + { + name: 'Dashboard', + path: '/', + icon: 'smile', + }, +]; +export { headerMenuConfig, asideMenuConfig }; + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/pages/Test/index.css b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/pages/Test/index.css new file mode 100644 index 000000000..066114aee --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/pages/Test/index.css @@ -0,0 +1,8 @@ +body { + font-size: 12px; +} + +.botton { + width: 100px; + color: #ff00ff; +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/pages/Test/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/pages/Test/index.jsx new file mode 100644 index 000000000..9e93a3ff6 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/pages/Test/index.jsx @@ -0,0 +1,1076 @@ +// 注意: 出码引擎注入的临时变量默认都以 "__$$" 开头,禁止在搭建的代码中直接访问。 +// 例外:react 框架的导出名和各种组件名除外。 +import React from 'react'; + +import { + Modal, + Steps, + Form, + Input, + Checkbox, + Select, + DatePicker, + InputNumber, + Button, +} from '@alilc/antd-lowcode/dist/antd-lowcode.esm.js'; + +import { + Text as NextText, + Page as NextPage, + Block as NextBlock, + P as NextP, +} from '@alife/container/lib/index.js'; + +import utils, { RefsManager } from '../../utils'; + +import * as __$$i18n from '../../i18n'; + +import __$$constants from '../../constants'; + +import './index.css'; + +const NextBlockCell = NextBlock.Cell; + +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 = { + books: [], + currentStep: 0, + isModifyDialogVisible: false, + isModifyStatus: false, + secondCommitText: '完成并提交', + thirdAuditText: '审核中', + thirdButtonText: '修改', + customerProjectInfo: { + id: null, + systemProjectName: null, + projectVersionTypeArray: null, + projectVersionType: null, + versionLine: 2, + expectedTime: null, + expectedNum: null, + projectModal: null, + displayWidth: null, + displayHeight: null, + displayInch: null, + displayDpi: null, + mainSoc: null, + cpuCoreNum: null, + instructions: null, + osVersion: null, + status: null, + }, + versionLinesArray: [ + { label: 'AmapAuto 485', value: 1 }, + { label: 'AmapAuto 505', value: 2 }, + ], + projectModalsArray: [ + { label: '车机', value: 1 }, + { label: '车镜', value: 2 }, + { label: '记录仪', value: 3 }, + { label: '其他', value: 4 }, + ], + osVersionsArray: [ + { label: '安卓5', value: 1 }, + { label: '安卓6', value: 2 }, + { label: '安卓7', value: 3 }, + { label: '安卓8', value: 4 }, + { label: '安卓9', value: 5 }, + { label: '安卓10', value: 6 }, + ], + instructionsArray: [ + { label: 'ARM64-V8', value: 'ARM64-V8' }, + { label: 'ARM32-V7', value: 'ARM32-V7' }, + { label: 'X86', value: 'X86' }, + { label: 'X64', value: 'X64' }, + ], + }; + } + + $ = (refName) => { + return this._refsManager.get(refName); + }; + + $$ = (refName) => { + return this._refsManager.getAll(refName); + }; + + componentDidUpdate(prevProps, prevState, snapshot) {} + + componentWillUnmount() {} + + __jp__init() { + /*...*/ + } + + __jp__initRouter() { + /*...*/ + } + + __jp__initDataSource() { + /*...*/ + } + + __jp__initEnv() { + /*...*/ + } + + __jp__initUtils() { + /*...*/ + } + + onFinishFirst() { + /*...*/ + } + + onClickPreSecond() { + /*...*/ + } + + onFinishSecond() { + /*...*/ + } + + onClickModifyThird() { + /*...*/ + } + + onOkModifyDialogThird() { + //第三步 修改 对话框 确定 + + this.setState({ + currentStep: 0, + isModifyDialogVisible: false, + }); + } + + onCancelModifyDialogThird() { + //第三步 修改 对话框 取消 + + this.setState({ + isModifyDialogVisible: false, + }); + } + + onFinishFailed() {} + + onClickPreThird() { + // 第三步 上一步 + this.setState({ + currentStep: 1, + }); + } + + onClickFirstBack() { + // 第一步 返回按钮 + this.$router.push('/myProjectList'); + } + + onClickSecondBack() { + // 第二步 返回按钮 + this.$router.push('/myProjectList'); + } + + onClickThirdBack() { + // 第三步 返回按钮 + this.$router.push('/myProjectList'); + } + + onValuesChange(_, values) { + this.setState({ + customerProjectInfo: { + ...this.state.customerProjectInfo, + ...values, + }, + }); + } + + componentDidMount() {} + + render() { + const __$$context = this._context || this; + const { state } = __$$context; + return ( + <div + ref={this._refsManager.linkRef('outterView')} + style={{ height: '100%' }} + > + <Modal + title="是否修改" + visible={__$$eval(() => this.state.isModifyDialogVisible)} + okText="确认" + okType="" + forceRender={false} + cancelText="取消" + zIndex={2000} + destroyOnClose={false} + confirmLoading={false} + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onOk', + relatedEventName: 'onOkModifyDialogThird', + }, + { + type: 'componentEvent', + name: 'onCancel', + relatedEventName: 'onCancelModifyDialogThird', + }, + ], + eventList: [ + { name: 'onCancel', disabled: true }, + { name: 'onOk', disabled: true }, + ], + }} + onOk={function () { + this.onOkModifyDialogThird.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + onCancel={function () { + this.onCancelModifyDialogThird.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + > + <NextText + type="inherit" + style={{ + fontStyle: 'normal', + textAlign: 'left', + display: 'block', + fontFamily: 'arial, helvetica, microsoft yahei', + fontWeight: 'normal', + }} + > + 修改将撤回此前填写的信息 + </NextText> + </Modal> + <NextPage + columns={12} + headerDivider={true} + placeholderStyle={{ gridRowEnd: 'span 1', gridColumnEnd: 'span 12' }} + placeholder="页面主体内容:拖拽Block布局组件到这里" + header={null} + headerProps={{ background: 'surface' }} + footer={null} + minHeight="100vh" + style={{}} + > + <NextBlock + prefix="next-" + placeholderStyle={{ height: '100%' }} + noPadding={false} + noBorder={false} + background="surface" + layoutmode="O" + colSpan={12} + rowSpan={1} + childTotalColumns={12} + > + <NextBlockCell + title="" + prefix="next-" + placeholderStyle={{ height: '100%' }} + layoutmode="O" + childTotalColumns={12} + isAutoContainer={true} + colSpan={12} + rowSpan={1} + > + <NextP + wrap={false} + type="body2" + verAlign="middle" + textSpacing={true} + align="left" + flex={true} + style={{ marginBottom: '24px' }} + > + <Steps current={__$$eval(() => this.state.currentStep)}> + <Steps.Step title="版本申请" description="" /> + <Steps.Step title="机器配置" subTitle="" description="" /> + <Steps.Step title="项目审批" description="" /> + </Steps> + </NextP> + {!!__$$eval(() => this.state.currentStep === 0) && ( + <NextP + wrap={false} + type="body2" + verAlign="middle" + textSpacing={true} + align="left" + full={true} + flex={true} + style={{ display: 'flex', justifyContent: 'center' }} + > + <Form + labelCol={{ span: 10 }} + wrapperCol={{ span: 10 }} + onFinish={function () { + this.onFinishFirst.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + name="basic" + style={{ + display: 'flex', + flexDirection: 'column', + width: '600px', + justifyContent: 'center', + }} + layout="vertical" + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onFinish', + relatedEventName: 'onFinishFirst', + }, + { + type: 'componentEvent', + name: 'onValuesChange', + relatedEventName: 'onValuesChange', + }, + ], + eventList: [ + { name: 'onFinish', disabled: true }, + { name: 'onFinishFailed', disabled: false }, + { name: 'onFieldsChange', disabled: false }, + { name: 'onValuesChange', disabled: true }, + ], + }} + initialValues={__$$eval( + () => this.state.customerProjectInfo + )} + onValuesChange={function () { + this.onValuesChange.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + > + {!!false && ( + <Form.Item + label="" + style={{ width: '600px' }} + colon={false} + name="id" + > + <Input + placeholder="" + style={{ width: '600px' }} + bordered={false} + disabled={true} + /> + </Form.Item> + )} + <Form.Item + label="版本类型选择" + name="projectVersionTypeArray" + initialValue="" + labelAlign="left" + colon={false} + required={true} + style={{ flexDirection: 'column', width: '600px' }} + requiredobj={{ + required: true, + message: '请选择版本类型', + }} + > + <Checkbox.Group + options={[ + { label: '基础版本', value: '3' }, + { label: 'AR导航', value: '1' }, + { label: '货车导航', value: '2' }, + { label: 'UI定制', value: '4', disabled: false }, + ]} + style={{ width: '600px' }} + disabled={__$$eval( + () => + this.state.customerProjectInfo.id > 0 && + !this.state.isModifyStatus + )} + /> + </Form.Item> + <Form.Item + label="版本线选择" + labelAlign="left" + colon={false} + required={true} + style={{ width: '600px' }} + name="versionLine" + requiredobj={{ required: true, message: '请选择版本线' }} + extra="" + > + <Select + style={{ width: '600px' }} + options={__$$eval(() => this.state.versionLinesArray)} + disabled={__$$eval( + () => + this.state.customerProjectInfo.id > 0 && + !this.state.isModifyStatus + )} + placeholder="请选择版本线" + /> + </Form.Item> + <Form.Item + label="项目名称" + colon={false} + required={true} + style={{ display: 'flex' }} + labelAlign="left" + extra="" + name="systemProjectName" + requiredobj={{ + required: true, + message: '请按格式填写项目名称', + }} + typeobj={{ + type: 'string', + message: + '请输入项目名称,格式:公司简称-产品名称-版本类型', + }} + lenobj={{ + max: 100, + message: '项目名称不能超过100个字符', + }} + > + <Input + placeholder="公司简称-产品名称-版本类型" + style={{ width: '600px' }} + disabled={__$$eval( + () => + this.state.customerProjectInfo.id > 0 && + !this.state.isModifyStatus + )} + /> + </Form.Item> + <Form.Item + label="预期交付时间" + style={{ width: '600px' }} + colon={false} + required={true} + name="expectedTime" + labelAlign="left" + requiredobj={{ + required: true, + message: '请填写预期交付时间', + }} + > + <DatePicker + style={{ width: '600px' }} + disabled={__$$eval( + () => + this.state.customerProjectInfo.id > 0 && + !this.state.isModifyStatus + )} + /> + </Form.Item> + <Form.Item + label="预期出货量" + style={{ width: '600px' }} + required={true} + requiredobj={{ + required: true, + message: '请填写预期出货量', + }} + name="expectedNum" + labelAlign="left" + colon={false} + > + <InputNumber + value={3} + style={{ width: '600px' }} + placeholder="单位(台)使用该版本的机器数量+预计出货量,请如实填写" + disabled={__$$eval( + () => + this.state.customerProjectInfo.id > 0 && + !this.state.isModifyStatus + )} + min={0} + size="middle" + /> + </Form.Item> + <Form.Item + wrapperCol={{ offset: '' }} + style={{ + flexDirection: 'row', + alignItems: 'baseline', + justifyContent: 'space-between', + width: '600px', + display: 'block', + }} + labelAlign="left" + colon={false} + > + <Button + style={{ margin: '0px' }} + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'onClickFirstBack', + }, + ], + eventList: [{ name: 'onClick', disabled: true }], + }} + onClick={function () { + this.onClickFirstBack.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + > + 返回 + </Button> + <Button + type="primary" + htmlType="submit" + style={{ + boxShadow: 'rgba(31, 56, 88, 0.2) 0px 0px 0px 0px', + float: 'right', + }} + __events={{ + eventDataList: [], + eventList: [{ name: 'onClick', disabled: false }], + }} + > + 下一步 + </Button> + </Form.Item> + </Form> + </NextP> + )} + {!!__$$eval(() => this.state.currentStep === 1) && ( + <NextP + wrap={false} + type="body2" + verAlign="middle" + textSpacing={true} + align="left" + full={true} + flex={true} + style={{ display: 'flex', justifyContent: 'center' }} + > + <Form + labelCol={{ span: 10 }} + wrapperCol={{ span: 10 }} + onFinish={function () { + this.onFinishSecond.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + name="basic" + style={{ + display: 'flex', + flexDirection: 'column', + width: '600px', + justifyContent: 'center', + height: '800px', + }} + layout="vertical" + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onFinish', + relatedEventName: 'onFinishSecond', + }, + { + type: 'componentEvent', + name: 'onValuesChange', + relatedEventName: 'onValuesChange', + }, + ], + eventList: [ + { name: 'onFinish', disabled: true }, + { name: 'onFinishFailed', disabled: false }, + { name: 'onFieldsChange', disabled: false }, + { name: 'onValuesChange', disabled: true }, + ], + }} + initialValues={__$$eval( + () => this.state.customerProjectInfo + )} + onValuesChange={function () { + this.onValuesChange.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + > + <Form.Item + label="设备类型选择" + labelAlign="left" + colon={false} + required={true} + style={{ width: '600px' }} + name="projectModal" + requiredobj={{ + required: true, + message: '请选择设备类型', + }} + > + <Select + style={{ width: '600px' }} + options={__$$eval(() => this.state.projectModalsArray)} + disabled={__$$eval( + () => + this.state.customerProjectInfo.id > 0 && + !this.state.isModifyStatus + )} + placeholder="请选择设备类型" + /> + </Form.Item> + <Form.Item + label="屏幕分辨率宽" + style={{ width: '600px' }} + name="displayWidth" + colon={false} + required={true} + requiredobj={{ + required: true, + message: '请输入屏幕分辨率宽', + }} + labelAlign="left" + > + <InputNumber + value={3} + style={{ width: '600px' }} + placeholder="例如1280" + disabled={__$$eval( + () => + this.state.customerProjectInfo.id > 0 && + !this.state.isModifyStatus + )} + min={0} + /> + </Form.Item> + <Form.Item + label="屏幕分辨率高" + style={{ width: '600px' }} + labelAlign="left" + colon={false} + name="displayHeight" + required={true} + requiredobj={{ + required: true, + message: '请输入屏幕分辨率高', + }} + > + <InputNumber + value={3} + style={{ width: '600px' }} + placeholder="例如720" + disabled={__$$eval( + () => + this.state.customerProjectInfo.id > 0 && + !this.state.isModifyStatus + )} + min={0} + /> + </Form.Item> + <Form.Item + label="屏幕尺寸(inch)" + style={{ width: '600px' }} + name="displayInch" + labelAlign="left" + required={true} + colon={false} + requiredobj={{ + required: true, + message: '请输入屏幕尺寸', + }} + > + <InputNumber + value={3} + style={{ width: '600px' }} + placeholder="请输入尺寸" + disabled={__$$eval( + () => + this.state.customerProjectInfo.id > 0 && + !this.state.isModifyStatus + )} + min={0} + /> + </Form.Item> + <Form.Item + label="屏幕DPI" + style={{ width: '600px' }} + labelAlign="left" + colon={false} + required={false} + name="displayDpi" + > + <InputNumber + value={3} + style={{ width: '600px' }} + placeholder="UI定制项目必填" + disabled={__$$eval( + () => + this.state.customerProjectInfo.id > 0 && + !this.state.isModifyStatus + )} + min={0} + /> + </Form.Item> + <Form.Item + label="芯片名称" + colon={false} + required={true} + style={{ display: 'flex' }} + labelAlign="left" + extra="" + name="mainSoc" + requiredobj={{ + required: true, + message: '请输入芯片名称', + }} + lenobj={{ max: 50, message: '芯片名称不能超过50个字符' }} + > + <Input + placeholder="请输入芯片名称" + style={{ width: '600px' }} + disabled={__$$eval( + () => + this.state.customerProjectInfo.id > 0 && + !this.state.isModifyStatus + )} + /> + </Form.Item> + <Form.Item + label="芯片核数" + style={{ width: '600px' }} + required={true} + requiredobj={{ + required: true, + message: '请输入芯片核数', + }} + name="cpuCoreNum" + labelAlign="left" + colon={false} + > + <InputNumber + value={3} + style={{ width: '600px' }} + placeholder="请输入芯片核数" + disabled={__$$eval( + () => + this.state.customerProjectInfo.id > 0 && + !this.state.isModifyStatus + )} + defaultValue="" + min={0} + /> + </Form.Item> + <Form.Item + label="指令集" + style={{ width: '600px' }} + required={true} + requiredobj={{ required: true, message: '请选择指令集' }} + name="instructions" + colon={false} + > + <Select + style={{ width: '600px' }} + options={__$$eval(() => this.state.instructionsArray)} + disabled={__$$eval( + () => + this.state.customerProjectInfo.id > 0 && + !this.state.isModifyStatus + )} + /> + </Form.Item> + <Form.Item + label="系统版本" + labelAlign="left" + colon={false} + required={true} + style={{ width: '600px' }} + name="osVersion" + requiredobj={{ + required: true, + message: '请选择系统版本', + }} + > + <Select + style={{ width: '600px' }} + options={__$$eval(() => this.state.osVersionsArray)} + disabled={__$$eval( + () => + this.state.customerProjectInfo.id > 0 && + !this.state.isModifyStatus + )} + placeholder="请选择系统版本" + /> + </Form.Item> + <Form.Item + wrapperCol={{ offset: '' }} + style={{ + flexDirection: 'row', + width: '600px', + display: 'flex', + }} + > + <Button + style={{ marginLeft: '0' }} + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'onClickSecondBack', + }, + ], + eventList: [{ name: 'onClick', disabled: true }], + }} + onClick={function () { + this.onClickSecondBack.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + > + 返回 + </Button> + <Button + type="primary" + htmlType="submit" + style={{ float: 'right', marginLeft: '20px' }} + loading={__$$eval( + () => + this.state.LOADING_ADD_OR_UPDATE_CUSTOMER_PROJECT + )} + > + {__$$eval(() => this.state.secondCommitText)} + </Button> + <Button + type="primary" + htmlType="submit" + style={{ marginLeft: '0px', float: 'right' }} + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'onClickPreSecond', + }, + ], + eventList: [{ name: 'onClick', disabled: true }], + }} + onClick={function () { + this.onClickPreSecond.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + > + 上一步 + </Button> + </Form.Item> + </Form> + </NextP> + )} + {!!__$$eval(() => this.state.currentStep === 2) && ( + <NextP + wrap={false} + type="body2" + verAlign="middle" + textSpacing={true} + align="left" + full={true} + flex={true} + style={{ display: 'flex', justifyContent: 'center' }} + > + <Form + labelCol={{ span: 10 }} + wrapperCol={{ span: 10 }} + onFinishFailed={function () { + this.onFinishFailed.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + name="basic" + style={{ + display: 'flex', + flexDirection: 'column', + width: '600px', + justifyContent: 'center', + }} + layout="vertical" + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onFinishFailed', + relatedEventName: 'onFinishFailed', + }, + ], + eventList: [ + { name: 'onFinish', disabled: false }, + { name: 'onFinishFailed', disabled: true }, + { name: 'onFieldsChange', disabled: false }, + { name: 'onValuesChange', disabled: false }, + ], + }} + > + <Form.Item label=""> + <Steps + current={1} + style={{ + width: '600px', + display: 'flex', + justifyContent: 'space-around', + alignItems: 'center', + height: '300px', + }} + labelPlacement="horizontal" + direction="vertical" + > + <Steps.Step + title="提交完成" + description="" + style={{ width: '200px' }} + /> + <Steps.Step + title={__$$eval(() => this.state.thirdAuditText)} + subTitle="" + description="" + style={{ width: '200px' }} + /> + </Steps> + </Form.Item> + <Form.Item + wrapperCol={{ offset: '' }} + style={{ + flexDirection: 'row', + width: '600px', + display: 'flex', + }} + > + <Button + style={{ marginLeft: '0' }} + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'onClickThirdBack', + }, + ], + eventList: [{ name: 'onClick', disabled: true }], + }} + onClick={function () { + this.onClickThirdBack.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + > + 返回 + </Button> + <Button + type="primary" + htmlType="submit" + style={{ float: 'right', marginLeft: '20px' }} + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'onClickModifyThird', + }, + ], + eventList: [{ name: 'onClick', disabled: true }], + }} + onClick={function () { + this.onClickModifyThird.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + > + {__$$eval(() => this.state.thirdButtonText)} + </Button> + {!!__$$eval( + () => this.state.customerProjectInfo.status > 2 + ) && ( + <Button + type="primary" + htmlType="submit" + style={{ marginLeft: '0px', float: 'right' }} + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'onClickPreThird', + }, + ], + eventList: [{ name: 'onClick', disabled: true }], + }} + onClick={function () { + this.onClickPreThird.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + > + 上一步 + </Button> + )} + </Form.Item> + </Form> + </NextP> + )} + </NextBlockCell> + </NextBlock> + </NextPage> + </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; +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/pages/layout.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/pages/layout.jsx new file mode 100644 index 000000000..50fbb2d1f --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/pages/layout.jsx @@ -0,0 +1,10 @@ +import { Outlet } from 'ice'; +import BasicLayout from '@/layouts/BasicLayout'; + +export default function Layout() { + return ( + <BasicLayout> + <Outlet /> + </BasicLayout> + ); +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/typings.d.ts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/typings.d.ts new file mode 100644 index 000000000..a9f8de7ce --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/typings.d.ts @@ -0,0 +1,9 @@ +/// <reference types="@ice/app/types" /> + +export {}; +declare global { + interface Window { + g_config: Record<string, any>; + } +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/utils.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/utils.js new file mode 100644 index 000000000..119071792 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/expected/demo-project/src/utils.js @@ -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 {}; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/schema.json5 b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/schema.json5 new file mode 100644 index 000000000..4c829cfab --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo7-literal-condition2/schema.json5 @@ -0,0 +1,1703 @@ +{ + version: '1.0.0', + componentsMap: [ + { + package: '@alife/container', + version: '0.3.7', + exportName: 'Text', + main: 'lib/index.js', + destructuring: true, + subName: '', + componentName: 'NextText', + }, + { + package: '@alilc/antd-lowcode', + version: '0.8.0', + exportName: 'Modal', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'Modal', + }, + { + package: '@alilc/antd-lowcode', + version: '0.8.0', + exportName: 'Steps', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + subName: 'Step', + componentName: 'Steps.Step', + }, + { + package: '@alilc/antd-lowcode', + version: '0.8.0', + exportName: 'Steps', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'Steps', + }, + { + package: '@alife/container', + version: '0.3.7', + exportName: 'P', + main: 'lib/index.js', + destructuring: true, + subName: '', + componentName: 'NextP', + }, + { + package: '@alilc/antd-lowcode', + version: '0.8.0', + exportName: 'Input', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'Input', + }, + { + package: '@alilc/antd-lowcode', + version: '0.8.0', + exportName: 'Form', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + subName: 'Item', + componentName: 'Form.Item', + }, + { + package: '@alilc/antd-lowcode', + version: '0.8.0', + exportName: 'Checkbox', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + subName: 'Group', + componentName: 'Checkbox.Group', + }, + { + package: '@alilc/antd-lowcode', + version: '0.8.0', + exportName: 'Select', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'Select', + }, + { + package: '@alilc/antd-lowcode', + version: '0.8.0', + exportName: 'DatePicker', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'DatePicker', + }, + { + package: '@alilc/antd-lowcode', + version: '0.8.0', + exportName: 'InputNumber', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'InputNumber', + }, + { + package: '@alilc/antd-lowcode', + version: '0.8.0', + exportName: 'Button', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'Button', + }, + { + package: '@alilc/antd-lowcode', + version: '0.8.0', + exportName: 'Form', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'Form', + }, + { + package: '@alife/container', + version: '0.3.7', + exportName: 'Block', + main: 'lib/index.js', + destructuring: true, + subName: 'Cell', + componentName: 'NextBlockCell', + }, + { + package: '@alife/container', + version: '0.3.7', + exportName: 'Block', + main: 'lib/index.js', + destructuring: true, + subName: '', + componentName: 'NextBlock', + }, + { + devMode: 'lowcode', + componentName: 'Slot', + }, + { + package: '@alife/container', + version: '0.3.7', + exportName: 'Page', + main: 'lib/index.js', + destructuring: true, + subName: '', + componentName: 'NextPage', + }, + { + devMode: 'lowcode', + componentName: 'Page', + }, + ], + componentsTree: [ + { + componentName: 'Page', + id: 'node_dockcviv8fo1', + props: { + ref: 'outterView', + style: { + height: '100%', + }, + }, + fileName: 'test', + dataSource: { + list: [], + }, + css: 'body {\n font-size: 12px;\n}\n\n.botton {\n width: 100px;\n color: #ff00ff\n}', + lifeCycles: { + constructor: { + type: 'JSFunction', + value: "function() { /*...*/ }", + }, + componentDidMount: { + type: 'JSFunction', + value: 'function() {}', + }, + componentDidUpdate: { + type: 'JSFunction', + value: 'function(prevProps, prevState, snapshot) {}', + }, + componentWillUnmount: { + type: 'JSFunction', + value: 'function() {}', + }, + }, + methods: { + __jp__init: { + type: 'JSFunction', + value: "function() { /*...*/ }", + }, + __jp__initRouter: { + type: 'JSFunction', + value: "function() { /*...*/ }", + }, + __jp__initDataSource: { + type: 'JSFunction', + value: "function() { /*...*/ }", + }, + __jp__initEnv: { + type: 'JSFunction', + value: "function() { /*...*/ }", + }, + __jp__initUtils: { + type: 'JSFunction', + value: "function() { /*...*/ }", + }, + onFinishFirst: { + type: 'JSFunction', + value: "function() { /*...*/ }", + }, + onClickPreSecond: { + type: 'JSFunction', + value: "function() { /*...*/ }", + }, + onFinishSecond: { + type: 'JSFunction', + value: "function() { /*...*/ }", + }, + onClickModifyThird: { + type: 'JSFunction', + value: "function() { /*...*/ }", + }, + onOkModifyDialogThird: { + type: 'JSFunction', + value: 'function() {\n //第三步 修改 对话框 确定\n\n this.setState({\n currentStep: 0,\n isModifyDialogVisible: false,\n });\n }', + }, + onCancelModifyDialogThird: { + type: 'JSFunction', + value: 'function() {\n //第三步 修改 对话框 取消\n\n this.setState({\n isModifyDialogVisible: false,\n });\n }', + }, + onFinishFailed: { + type: 'JSFunction', + value: 'function() {}', + }, + onClickPreThird: { + type: 'JSFunction', + value: 'function() {\n // 第三步 上一步\n this.setState({\n currentStep: 1,\n });\n }', + }, + onClickFirstBack: { + type: 'JSFunction', + value: "function() {\n // 第一步 返回按钮\n this.$router.push('/myProjectList');\n }", + }, + onClickSecondBack: { + type: 'JSFunction', + value: "function() {\n // 第二步 返回按钮\n this.$router.push('/myProjectList');\n }", + }, + onClickThirdBack: { + type: 'JSFunction', + value: "function() {\n // 第三步 返回按钮\n this.$router.push('/myProjectList');\n }", + }, + onValuesChange: { + type: 'JSFunction', + value: 'function(_, values) {\n this.setState({\n customerProjectInfo: {\n ...this.state.customerProjectInfo,\n ...values,\n },\n });\n }', + }, + }, + state: { + books: [], + currentStep: 0, + isModifyDialogVisible: false, + isModifyStatus: false, + secondCommitText: '完成并提交', + thirdAuditText: '审核中', + thirdButtonText: '修改', + customerProjectInfo: { + id: null, + systemProjectName: null, + projectVersionTypeArray: null, + projectVersionType: null, + versionLine: 2, + expectedTime: null, + expectedNum: null, + projectModal: null, + displayWidth: null, + displayHeight: null, + displayInch: null, + displayDpi: null, + mainSoc: null, + cpuCoreNum: null, + instructions: null, + osVersion: null, + status: null, + }, + versionLinesArray: [ + { + label: 'AmapAuto 485', + value: 1, + }, + { + label: 'AmapAuto 505', + value: 2, + }, + ], + projectModalsArray: [ + { + label: '车机', + value: 1, + }, + { + label: '车镜', + value: 2, + }, + { + label: '记录仪', + value: 3, + }, + { + label: '其他', + value: 4, + }, + ], + osVersionsArray: [ + { + label: '安卓5', + value: 1, + }, + { + label: '安卓6', + value: 2, + }, + { + label: '安卓7', + value: 3, + }, + { + label: '安卓8', + value: 4, + }, + { + label: '安卓9', + value: 5, + }, + { + label: '安卓10', + value: 6, + }, + ], + instructionsArray: [ + { + label: 'ARM64-V8', + value: 'ARM64-V8', + }, + { + label: 'ARM32-V7', + value: 'ARM32-V7', + }, + { + label: 'X86', + value: 'X86', + }, + { + label: 'X64', + value: 'X64', + }, + ], + }, + children: [ + { + componentName: 'Modal', + id: 'node_ockodngwu940', + props: { + title: '是否修改', + visible: { + type: 'JSExpression', + value: 'this.state.isModifyDialogVisible', + }, + okText: '确认', + okType: '', + forceRender: false, + cancelText: '取消', + zIndex: 2000, + destroyOnClose: false, + confirmLoading: false, + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onOk', + relatedEventName: 'onOkModifyDialogThird', + }, + { + type: 'componentEvent', + name: 'onCancel', + relatedEventName: 'onCancelModifyDialogThird', + }, + ], + eventList: [ + { + name: 'onCancel', + disabled: true, + }, + { + name: 'onOk', + disabled: true, + }, + ], + }, + onOk: { + type: 'JSFunction', + value: 'function(){this.onOkModifyDialogThird.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + onCancel: { + type: 'JSFunction', + value: 'function(){this.onCancelModifyDialogThird.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + }, + hidden: true, + children: [ + { + componentName: 'NextText', + id: 'node_ockodngwu946', + props: { + type: 'inherit', + children: '修改将撤回此前填写的信息', + style: { + fontStyle: 'normal', + textAlign: 'left', + display: 'block', + fontFamily: 'arial, helvetica, microsoft yahei', + fontWeight: 'normal', + }, + }, + }, + ], + }, + { + componentName: 'NextPage', + id: 'node_ocko19zplh1', + props: { + columns: 12, + headerDivider: true, + placeholderStyle: { + gridRowEnd: 'span 1', + gridColumnEnd: 'span 12', + }, + placeholder: '页面主体内容:拖拽Block布局组件到这里', + header: { + type: 'JSSlot', + title: 'header', + }, + headerProps: { + background: 'surface', + }, + footer: { + type: 'JSSlot', + title: 'footer', + }, + minHeight: '100vh', + style: {}, + }, + title: '页面', + children: [ + { + componentName: 'NextBlock', + id: 'node_ocko19zplh2', + props: { + prefix: 'next-', + placeholderStyle: { + height: '100%', + }, + noPadding: false, + noBorder: false, + background: 'surface', + layoutmode: 'O', + colSpan: 12, + rowSpan: 1, + childTotalColumns: 12, + }, + title: '区块', + children: [ + { + componentName: 'NextBlockCell', + id: 'node_ocko19zplh3', + props: { + title: '', + prefix: 'next-', + placeholderStyle: { + height: '100%', + }, + layoutmode: 'O', + childTotalColumns: 12, + isAutoContainer: true, + colSpan: 12, + rowSpan: 1, + }, + children: [ + { + componentName: 'NextP', + id: 'node_ockoco6icv1w', + props: { + wrap: false, + type: 'body2', + verAlign: 'middle', + textSpacing: true, + align: 'left', + flex: true, + style: { + marginBottom: '24px', + }, + }, + title: '段落', + children: [ + { + componentName: 'Steps', + id: 'node_ockoco6icv1x', + props: { + current: { + type: 'JSExpression', + value: 'this.state.currentStep', + }, + }, + children: [ + { + componentName: 'Steps.Step', + id: 'node_ockoco6icv1y', + props: { + title: '版本申请', + description: '', + }, + }, + { + componentName: 'Steps.Step', + id: 'node_ockoco6icv1z', + props: { + title: '机器配置', + subTitle: '', + description: '', + }, + }, + { + componentName: 'Steps.Step', + id: 'node_ockoco6icv20', + props: { + title: '项目审批', + description: '', + }, + }, + ], + }, + ], + }, + { + componentName: 'NextP', + id: 'node_ockoco6icv12w', + props: { + wrap: false, + type: 'body2', + verAlign: 'middle', + textSpacing: true, + align: 'left', + full: true, + flex: true, + style: { + display: 'flex', + justifyContent: 'center', + }, + }, + title: '段落', + condition: { + type: 'JSExpression', + value: 'this.state.currentStep === 0', + }, + children: [ + { + componentName: 'Form', + id: 'node_ockoco6icv12x', + props: { + labelCol: { + span: 10, + }, + wrapperCol: { + span: 10, + }, + onFinish: { + type: 'JSFunction', + value: 'function(){this.onFinishFirst.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + name: 'basic', + style: { + display: 'flex', + flexDirection: 'column', + width: '600px', + justifyContent: 'center', + }, + layout: 'vertical', + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onFinish', + relatedEventName: 'onFinishFirst', + }, + { + type: 'componentEvent', + name: 'onValuesChange', + relatedEventName: 'onValuesChange', + }, + ], + eventList: [ + { + name: 'onFinish', + disabled: true, + }, + { + name: 'onFinishFailed', + disabled: false, + }, + { + name: 'onFieldsChange', + disabled: false, + }, + { + name: 'onValuesChange', + disabled: true, + }, + ], + }, + initialValues: { + type: 'JSExpression', + value: 'this.state.customerProjectInfo', + }, + onValuesChange: { + type: 'JSFunction', + value: 'function(){this.onValuesChange.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + }, + children: [ + { + componentName: 'Form.Item', + id: 'node_ockojhvrkn2u', + props: { + label: '', + style: { + width: '600px', + }, + colon: false, + name: 'id', + }, + condition: false, + children: [ + { + componentName: 'Input', + id: 'node_ockojhvrkn2v', + props: { + placeholder: '', + style: { + width: '600px', + }, + bordered: false, + disabled: true, + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_ockoco6icv12y', + props: { + label: '版本类型选择', + name: 'projectVersionTypeArray', + initialValue: '', + labelAlign: 'left', + colon: false, + required: true, + style: { + flexDirection: 'column', + width: '600px', + }, + requiredobj: { + required: true, + message: '请选择版本类型', + }, + }, + children: [ + { + componentName: 'Checkbox.Group', + id: 'node_ockoco6icv12z', + props: { + options: [ + { + label: '基础版本', + value: '3', + }, + { + label: 'AR导航', + value: '1', + }, + { + label: '货车导航', + value: '2', + }, + { + label: 'UI定制', + value: '4', + disabled: false, + }, + ], + style: { + width: '600px', + }, + disabled: { + type: 'JSExpression', + value: 'this.state.customerProjectInfo.id > 0 && ! this.state.isModifyStatus', + }, + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_ockoco6icv13a', + props: { + label: '版本线选择', + labelAlign: 'left', + colon: false, + required: true, + style: { + width: '600px', + }, + name: 'versionLine', + requiredobj: { + required: true, + message: '请选择版本线', + }, + extra: '', + }, + children: [ + { + componentName: 'Select', + id: 'node_ockoco6icv13b', + props: { + style: { + width: '600px', + }, + options: { + type: 'JSExpression', + value: 'this.state.versionLinesArray', + }, + disabled: { + type: 'JSExpression', + value: 'this.state.customerProjectInfo.id > 0 && ! this.state.isModifyStatus', + }, + placeholder: '请选择版本线', + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_ockoco6icv13o', + props: { + label: '项目名称', + colon: false, + required: true, + style: { + display: 'flex', + }, + labelAlign: 'left', + extra: '', + name: 'systemProjectName', + requiredobj: { + required: true, + message: '请按格式填写项目名称', + }, + typeobj: { + type: 'string', + message: '请输入项目名称,格式:公司简称-产品名称-版本类型', + }, + lenobj: { + max: 100, + message: '项目名称不能超过100个字符', + }, + }, + children: [ + { + componentName: 'Input', + id: 'node_ockoco6icv13p', + props: { + placeholder: '公司简称-产品名称-版本类型', + style: { + width: '600px', + }, + disabled: { + type: 'JSExpression', + value: 'this.state.customerProjectInfo.id > 0 && ! this.state.isModifyStatus', + }, + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_ockoco6icv13v', + props: { + label: '预期交付时间', + style: { + width: '600px', + }, + colon: false, + required: true, + name: 'expectedTime', + labelAlign: 'left', + requiredobj: { + required: true, + message: '请填写预期交付时间', + }, + }, + children: [ + { + componentName: 'DatePicker', + id: 'node_ockoco6icv13w', + props: { + style: { + width: '600px', + }, + disabled: { + type: 'JSExpression', + value: 'this.state.customerProjectInfo.id > 0 && ! this.state.isModifyStatus', + }, + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_ockpmbs0bv8', + props: { + label: '预期出货量', + style: { + width: '600px', + }, + required: true, + requiredobj: { + required: true, + message: '请填写预期出货量', + }, + name: 'expectedNum', + labelAlign: 'left', + colon: false, + }, + children: [ + { + componentName: 'InputNumber', + id: 'node_ockpmbs0bv9', + props: { + value: 3, + style: { + width: '600px', + }, + placeholder: '单位(台)使用该版本的机器数量+预计出货量,请如实填写', + disabled: { + type: 'JSExpression', + value: 'this.state.customerProjectInfo.id > 0 && ! this.state.isModifyStatus', + }, + min: 0, + size: 'middle', + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_ockoco6icv130', + props: { + wrapperCol: { + offset: '', + }, + style: { + flexDirection: 'row', + alignItems: 'baseline', + justifyContent: 'space-between', + width: '600px', + display: 'block', + }, + labelAlign: 'left', + colon: false, + }, + children: [ + { + componentName: 'Button', + id: 'node_ockoco6icv132', + props: { + style: { + margin: '0px', + }, + children: '返回', + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'onClickFirstBack', + }, + ], + eventList: [ + { + name: 'onClick', + disabled: true, + }, + ], + }, + onClick: { + type: 'JSFunction', + value: 'function(){this.onClickFirstBack.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + }, + }, + { + componentName: 'Button', + id: 'node_ockoco6icv131', + props: { + type: 'primary', + children: '下一步', + htmlType: 'submit', + style: { + boxShadow: 'rgba(31, 56, 88, 0.2) 0px 0px 0px 0px', + float: 'right', + }, + __events: { + eventDataList: [], + eventList: [ + { + name: 'onClick', + disabled: false, + }, + ], + }, + }, + }, + ], + }, + ], + }, + ], + }, + { + componentName: 'NextP', + id: 'node_ockoco6icv1ue', + props: { + wrap: false, + type: 'body2', + verAlign: 'middle', + textSpacing: true, + align: 'left', + full: true, + flex: true, + style: { + display: 'flex', + justifyContent: 'center', + }, + }, + title: '段落', + condition: { + type: 'JSExpression', + value: 'this.state.currentStep === 1', + }, + children: [ + { + componentName: 'Form', + id: 'node_ockoco6icv1uf', + props: { + labelCol: { + span: 10, + }, + wrapperCol: { + span: 10, + }, + onFinish: { + type: 'JSFunction', + value: 'function(){this.onFinishSecond.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + name: 'basic', + style: { + display: 'flex', + flexDirection: 'column', + width: '600px', + justifyContent: 'center', + height: '800px', + }, + layout: 'vertical', + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onFinish', + relatedEventName: 'onFinishSecond', + }, + { + type: 'componentEvent', + name: 'onValuesChange', + relatedEventName: 'onValuesChange', + }, + ], + eventList: [ + { + name: 'onFinish', + disabled: true, + }, + { + name: 'onFinishFailed', + disabled: false, + }, + { + name: 'onFieldsChange', + disabled: false, + }, + { + name: 'onValuesChange', + disabled: true, + }, + ], + }, + initialValues: { + type: 'JSExpression', + value: 'this.state.customerProjectInfo', + }, + onValuesChange: { + type: 'JSFunction', + value: 'function(){this.onValuesChange.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + }, + children: [ + { + componentName: 'Form.Item', + id: 'node_ockoco6icv1ui', + props: { + label: '设备类型选择', + labelAlign: 'left', + colon: false, + required: true, + style: { + width: '600px', + }, + name: 'projectModal', + requiredobj: { + required: true, + message: '请选择设备类型', + }, + }, + children: [ + { + componentName: 'Select', + id: 'node_ockoco6icv1uj', + props: { + style: { + width: '600px', + }, + options: { + type: 'JSExpression', + value: 'this.state.projectModalsArray', + }, + disabled: { + type: 'JSExpression', + value: 'this.state.customerProjectInfo.id > 0 && ! this.state.isModifyStatus', + }, + placeholder: '请选择设备类型', + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_ockpmbs0bv17', + props: { + label: '屏幕分辨率宽', + style: { + width: '600px', + }, + name: 'displayWidth', + colon: false, + required: true, + requiredobj: { + required: true, + message: '请输入屏幕分辨率宽', + }, + labelAlign: 'left', + }, + children: [ + { + componentName: 'InputNumber', + id: 'node_ockpmbs0bv18', + props: { + value: 3, + style: { + width: '600px', + }, + placeholder: '例如1280', + disabled: { + type: 'JSExpression', + value: 'this.state.customerProjectInfo.id > 0 && ! this.state.isModifyStatus', + }, + min: 0, + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_ockpmbs0bv10', + props: { + label: '屏幕分辨率高', + style: { + width: '600px', + }, + labelAlign: 'left', + colon: false, + name: 'displayHeight', + required: true, + requiredobj: { + required: true, + message: '请输入屏幕分辨率高', + }, + }, + children: [ + { + componentName: 'InputNumber', + id: 'node_ockpmbs0bv11', + props: { + value: 3, + style: { + width: '600px', + }, + placeholder: '例如720', + disabled: { + type: 'JSExpression', + value: 'this.state.customerProjectInfo.id > 0 && ! this.state.isModifyStatus', + }, + min: 0, + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_ockpmbs0bvt', + props: { + label: '屏幕尺寸(inch)', + style: { + width: '600px', + }, + name: 'displayInch', + labelAlign: 'left', + required: true, + colon: false, + requiredobj: { + required: true, + message: '请输入屏幕尺寸', + }, + }, + children: [ + { + componentName: 'InputNumber', + id: 'node_ockpmbs0bvu', + props: { + value: 3, + style: { + width: '600px', + }, + placeholder: '请输入尺寸', + disabled: { + type: 'JSExpression', + value: 'this.state.customerProjectInfo.id > 0 && ! this.state.isModifyStatus', + }, + min: 0, + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_ockpmbs0bvm', + props: { + label: '屏幕DPI', + style: { + width: '600px', + }, + labelAlign: 'left', + colon: false, + required: false, + name: 'displayDpi', + }, + children: [ + { + componentName: 'InputNumber', + id: 'node_ockpmbs0bvn', + props: { + value: 3, + style: { + width: '600px', + }, + placeholder: 'UI定制项目必填', + disabled: { + type: 'JSExpression', + value: 'this.state.customerProjectInfo.id > 0 && ! this.state.isModifyStatus', + }, + min: 0, + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_ockoco6icv1v3', + props: { + label: '芯片名称', + colon: false, + required: true, + style: { + display: 'flex', + }, + labelAlign: 'left', + extra: '', + name: 'mainSoc', + requiredobj: { + required: true, + message: '请输入芯片名称', + }, + lenobj: { + max: 50, + message: '芯片名称不能超过50个字符', + }, + }, + children: [ + { + componentName: 'Input', + id: 'node_ockoco6icv1v4', + props: { + placeholder: '请输入芯片名称', + style: { + width: '600px', + }, + disabled: { + type: 'JSExpression', + value: 'this.state.customerProjectInfo.id > 0 && ! this.state.isModifyStatus', + }, + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_ockpmbs0bvf', + props: { + label: '芯片核数', + style: { + width: '600px', + }, + required: true, + requiredobj: { + required: true, + message: '请输入芯片核数', + }, + name: 'cpuCoreNum', + labelAlign: 'left', + colon: false, + }, + children: [ + { + componentName: 'InputNumber', + id: 'node_ockpmbs0bvg', + props: { + value: 3, + style: { + width: '600px', + }, + placeholder: '请输入芯片核数', + disabled: { + type: 'JSExpression', + value: 'this.state.customerProjectInfo.id > 0 && ! this.state.isModifyStatus', + }, + defaultValue: '', + min: 0, + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_ockpxba11aa', + props: { + label: '指令集', + style: { + width: '600px', + }, + required: true, + requiredobj: { + required: true, + message: '请选择指令集', + }, + name: 'instructions', + colon: false, + }, + children: [ + { + componentName: 'Select', + id: 'node_ockpxba11ab', + props: { + style: { + width: '600px', + }, + options: { + type: 'JSExpression', + value: 'this.state.instructionsArray', + }, + disabled: { + type: 'JSExpression', + value: 'this.state.customerProjectInfo.id > 0 && ! this.state.isModifyStatus', + }, + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_ockodz1kiqh', + props: { + label: '系统版本', + labelAlign: 'left', + colon: false, + required: true, + style: { + width: '600px', + }, + name: 'osVersion', + requiredobj: { + required: true, + message: '请选择系统版本', + }, + }, + children: [ + { + componentName: 'Select', + id: 'node_ockodz1kiqi', + props: { + style: { + width: '600px', + }, + options: { + type: 'JSExpression', + value: 'this.state.osVersionsArray', + }, + disabled: { + type: 'JSExpression', + value: 'this.state.customerProjectInfo.id > 0 && ! this.state.isModifyStatus', + }, + placeholder: '请选择系统版本', + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_ockoco6icv1uq', + props: { + wrapperCol: { + offset: '', + }, + style: { + flexDirection: 'row', + width: '600px', + display: 'flex', + }, + }, + children: [ + { + componentName: 'Button', + id: 'node_ockoco6icv1ur', + props: { + style: { + marginLeft: '0', + }, + children: '返回', + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'onClickSecondBack', + }, + ], + eventList: [ + { + name: 'onClick', + disabled: true, + }, + ], + }, + onClick: { + type: 'JSFunction', + value: 'function(){this.onClickSecondBack.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + }, + }, + { + componentName: 'Button', + id: 'node_ockoco6icv1vb', + props: { + type: 'primary', + children: { + type: 'JSExpression', + value: 'this.state.secondCommitText', + }, + htmlType: 'submit', + style: { + float: 'right', + marginLeft: '20px', + }, + loading: { + type: 'JSExpression', + value: 'this.state.LOADING_ADD_OR_UPDATE_CUSTOMER_PROJECT', + }, + }, + }, + { + componentName: 'Button', + id: 'node_ockoco6icv1us', + props: { + type: 'primary', + children: '上一步', + htmlType: 'submit', + style: { + marginLeft: '0px', + float: 'right', + }, + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'onClickPreSecond', + }, + ], + eventList: [ + { + name: 'onClick', + disabled: true, + }, + ], + }, + onClick: { + type: 'JSFunction', + value: 'function(){this.onClickPreSecond.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + }, + }, + ], + }, + ], + }, + ], + }, + { + componentName: 'NextP', + id: 'node_ockodngwu9m', + props: { + wrap: false, + type: 'body2', + verAlign: 'middle', + textSpacing: true, + align: 'left', + full: true, + flex: true, + style: { + display: 'flex', + justifyContent: 'center', + }, + }, + title: '段落', + condition: { + type: 'JSExpression', + value: 'this.state.currentStep === 2', + }, + children: [ + { + componentName: 'Form', + id: 'node_ockodngwu9n', + props: { + labelCol: { + span: 10, + }, + wrapperCol: { + span: 10, + }, + onFinishFailed: { + type: 'JSFunction', + value: 'function(){this.onFinishFailed.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + name: 'basic', + style: { + display: 'flex', + flexDirection: 'column', + width: '600px', + justifyContent: 'center', + }, + layout: 'vertical', + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onFinishFailed', + relatedEventName: 'onFinishFailed', + }, + ], + eventList: [ + { + name: 'onFinish', + disabled: false, + }, + { + name: 'onFinishFailed', + disabled: true, + }, + { + name: 'onFieldsChange', + disabled: false, + }, + { + name: 'onValuesChange', + disabled: false, + }, + ], + }, + }, + children: [ + { + componentName: 'Form.Item', + id: 'node_ockodngwu91m', + props: { + label: '', + }, + children: [ + { + componentName: 'Steps', + id: 'node_ockodngwu91n', + props: { + current: 1, + style: { + width: '600px', + display: 'flex', + justifyContent: 'space-around', + alignItems: 'center', + height: '300px', + }, + labelPlacement: 'horizontal', + direction: 'vertical', + }, + children: [ + { + componentName: 'Steps.Step', + id: 'node_ockodngwu91o', + props: { + title: '提交完成', + description: '', + style: { + width: '200px', + }, + }, + }, + { + componentName: 'Steps.Step', + id: 'node_ockodngwu91p', + props: { + title: { + type: 'JSExpression', + value: 'this.state.thirdAuditText', + }, + subTitle: '', + description: '', + style: { + width: '200px', + }, + }, + }, + ], + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_ockodngwu914', + props: { + wrapperCol: { + offset: '', + }, + style: { + flexDirection: 'row', + width: '600px', + display: 'flex', + }, + }, + children: [ + { + componentName: 'Button', + id: 'node_ockodngwu915', + props: { + style: { + marginLeft: '0', + }, + children: '返回', + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'onClickThirdBack', + }, + ], + eventList: [ + { + name: 'onClick', + disabled: true, + }, + ], + }, + onClick: { + type: 'JSFunction', + value: 'function(){this.onClickThirdBack.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + }, + }, + { + componentName: 'Button', + id: 'node_ockodngwu916', + props: { + type: 'primary', + children: { + type: 'JSExpression', + value: 'this.state.thirdButtonText', + }, + htmlType: 'submit', + style: { + float: 'right', + marginLeft: '20px', + }, + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'onClickModifyThird', + }, + ], + eventList: [ + { + name: 'onClick', + disabled: true, + }, + ], + }, + onClick: { + type: 'JSFunction', + value: 'function(){this.onClickModifyThird.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + }, + }, + { + componentName: 'Button', + id: 'node_ockosjrkvr1d', + props: { + type: 'primary', + children: '上一步', + htmlType: 'submit', + style: { + marginLeft: '0px', + float: 'right', + }, + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'onClickPreThird', + }, + ], + eventList: [ + { + name: 'onClick', + disabled: true, + }, + ], + }, + onClick: { + type: 'JSFunction', + value: 'function(){this.onClickPreThird.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + }, + condition: { + type: 'JSExpression', + value: 'this.state.customerProjectInfo.status > 2', + }, + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + i18n: {}, +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/.browserslistrc b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/.browserslistrc new file mode 100644 index 000000000..55a130413 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/.browserslistrc @@ -0,0 +1,3 @@ +defaults +ios_saf 9 + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/.gitignore b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/.gitignore new file mode 100644 index 000000000..4ec178818 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/.gitignore @@ -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 + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/README.md b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/README.md new file mode 100644 index 000000000..6d9dd7521 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/README.md @@ -0,0 +1 @@ +This project is generated by lowcode-code-generator & lowcode-solution-icejs3. \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/ice.config.mts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/ice.config.mts new file mode 100644 index 000000000..fe77c9b6c --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/ice.config.mts @@ -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(), + ], +})); + diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/package.json b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/package.json new file mode 100644 index 000000000..45c8e304a --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/package.json @@ -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.26.15", + "@ice/runtime": "^1.0.0", + "@alilc/lowcode-datasource-engine": "^1.0.0", + "@alilc/lowcode-datasource-http-handler": "^1.0.0", + "@alilc/lowcode-components": "^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" +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/app.ts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/app.ts new file mode 100644 index 000000000..6d5856292 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/app.ts @@ -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: '/', + }, +})); diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/constants.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/constants.js new file mode 100644 index 000000000..ea766c9da --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/constants.js @@ -0,0 +1,3 @@ +const __$$constants = {}; + +export default __$$constants; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/document.tsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/document.tsx new file mode 100644 index 000000000..286be9f8c --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/document.tsx @@ -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> + ); +} \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/global.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/global.scss new file mode 100644 index 000000000..82ca3eac7 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/global.scss @@ -0,0 +1,6 @@ +// 引入默认全局样式 +@import '@alifd/next/reset.scss'; + +body { + -webkit-font-smoothing: antialiased; +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/i18n.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/i18n.js new file mode 100644 index 000000000..adbbe673d --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/i18n.js @@ -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 }; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx new file mode 100644 index 000000000..cc70d53be --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss new file mode 100644 index 000000000..81e77fda5 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss @@ -0,0 +1,15 @@ + +.footer { + line-height: 20px; + text-align: center; +} + +.logo { + font-weight: bold; + font-size: 16px; +} + +.copyright { + font-size: 12px; +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx new file mode 100644 index 000000000..265bfdaa0 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss new file mode 100644 index 000000000..1ab56d394 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss @@ -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; + } +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx new file mode 100644 index 000000000..911998b0d --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx @@ -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; + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/layouts/BasicLayout/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/layouts/BasicLayout/index.jsx new file mode 100644 index 000000000..18db44df5 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/layouts/BasicLayout/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/layouts/BasicLayout/menuConfig.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/layouts/BasicLayout/menuConfig.js new file mode 100644 index 000000000..5332202be --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/layouts/BasicLayout/menuConfig.js @@ -0,0 +1,11 @@ + +const headerMenuConfig = []; +const asideMenuConfig = [ + { + name: 'Dashboard', + path: '/', + icon: 'smile', + }, +]; +export { headerMenuConfig, asideMenuConfig }; + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/pages/Example/index.css b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/pages/Example/index.css new file mode 100644 index 000000000..e69de29bb diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/pages/Example/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/pages/Example/index.jsx new file mode 100644 index 000000000..9a661ad75 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/pages/Example/index.jsx @@ -0,0 +1,116 @@ +// 注意: 出码引擎注入的临时变量默认都以 "__$$" 开头,禁止在搭建的代码中直接访问。 +// 例外:react 框架的导出名和各种组件名除外。 +import React from 'react'; + +import { Page, Table } from '@alilc/lowcode-components'; + +import { createHttpHandler as __$$createHttpRequestHandler } from '@alilc/lowcode-datasource-http-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 Example$$Page extends React.Component { + _context = this; + + _dataSourceConfig = this._defineDataSourceConfig(); + _dataSourceEngine = __$$createDataSourceEngine(this._dataSourceConfig, this, { + runtimeConfig: true, + requestHandlersMap: { http: __$$createHttpRequestHandler() }, + }); + + 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: 'userList', + type: 'http', + description: '用户列表', + options: function () { + return { + uri: 'https://api.example.com/user/list', + }; + }.bind(_this), + isInit: function () { + return undefined; + }.bind(_this), + }, + ], + }; + } + + componentDidMount() { + this._dataSourceEngine.reloadDataSource(); + } + + render() { + const __$$context = this._context || this; + const { state } = __$$context; + return ( + <div> + <Table + dataSource={__$$eval(() => this.dataSourceMap['userList'])} + columns={[ + { dataIndex: 'name', title: '姓名' }, + { dataIndex: 'age', title: '年龄' }, + ]} + /> + </div> + ); + } +} + +export default Example$$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; +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/pages/layout.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/pages/layout.jsx new file mode 100644 index 000000000..50fbb2d1f --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/pages/layout.jsx @@ -0,0 +1,10 @@ +import { Outlet } from 'ice'; +import BasicLayout from '@/layouts/BasicLayout'; + +export default function Layout() { + return ( + <BasicLayout> + <Outlet /> + </BasicLayout> + ); +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/typings.d.ts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/typings.d.ts new file mode 100644 index 000000000..a9f8de7ce --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/typings.d.ts @@ -0,0 +1,9 @@ +/// <reference types="@ice/app/types" /> + +export {}; +declare global { + interface Window { + g_config: Record<string, any>; + } +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/utils.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/utils.js new file mode 100644 index 000000000..119071792 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/expected/demo-project/src/utils.js @@ -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 {}; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/schema.json5 b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/schema.json5 new file mode 100644 index 000000000..1e61996cf --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo8-datasource-prop/schema.json5 @@ -0,0 +1,65 @@ +{ + version: '1.0.0', + componentsMap: [ + { + package: '@alilc/lowcode-components', + version: '^1.0.0', + componentName: 'Page', + destructuring: true, + exportName: 'Page', + }, + { + package: '@alilc/lowcode-components', + version: '^1.0.0', + componentName: 'Table', + destructuring: true, + exportName: 'Table', + }, + ], + componentsTree: [ + { + componentName: 'Page', + id: 'node_ockp6ci0hm1', + props: {}, + fileName: 'example', + dataSource: { + list: [ + { + id: 'userList', + type: 'http', + description: '用户列表', + options: { + uri: 'https://api.example.com/user/list', + }, + }, + ], + }, + children: [ + { + componentName: 'Table', + id: 'node_ockp6ci0hm22', + props: { + dataSource: { + type: 'DataSource', + id: 'userList', + }, + columns: [ + { + dataIndex: 'name', + title: '姓名', + }, + { + dataIndex: 'age', + title: '年龄', + }, + ], + }, + }, + ], + }, + ], + meta: { + name: 'example', + description: 'Example', + }, +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/.browserslistrc b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/.browserslistrc new file mode 100644 index 000000000..55a130413 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/.browserslistrc @@ -0,0 +1,3 @@ +defaults +ios_saf 9 + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/.gitignore b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/.gitignore new file mode 100644 index 000000000..4ec178818 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/.gitignore @@ -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 + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/README.md b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/README.md new file mode 100644 index 000000000..6d9dd7521 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/README.md @@ -0,0 +1 @@ +This project is generated by lowcode-code-generator & lowcode-solution-icejs3. \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/ice.config.mts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/ice.config.mts new file mode 100644 index 000000000..fe77c9b6c --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/ice.config.mts @@ -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(), + ], +})); + diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/package.json b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/package.json new file mode 100644 index 000000000..87ad38514 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/package.json @@ -0,0 +1,43 @@ +{ + "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-jsonp-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" +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/app.ts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/app.ts new file mode 100644 index 000000000..6d5856292 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/app.ts @@ -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: '/', + }, +})); diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/constants.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/constants.js new file mode 100644 index 000000000..ea766c9da --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/constants.js @@ -0,0 +1,3 @@ +const __$$constants = {}; + +export default __$$constants; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/document.tsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/document.tsx new file mode 100644 index 000000000..286be9f8c --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/document.tsx @@ -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> + ); +} \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/global.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/global.scss new file mode 100644 index 000000000..82ca3eac7 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/global.scss @@ -0,0 +1,6 @@ +// 引入默认全局样式 +@import '@alifd/next/reset.scss'; + +body { + -webkit-font-smoothing: antialiased; +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/i18n.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/i18n.js new file mode 100644 index 000000000..adbbe673d --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/i18n.js @@ -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 }; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx new file mode 100644 index 000000000..cc70d53be --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss new file mode 100644 index 000000000..81e77fda5 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss @@ -0,0 +1,15 @@ + +.footer { + line-height: 20px; + text-align: center; +} + +.logo { + font-weight: bold; + font-size: 16px; +} + +.copyright { + font-size: 12px; +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx new file mode 100644 index 000000000..265bfdaa0 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss new file mode 100644 index 000000000..1ab56d394 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss @@ -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; + } +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx new file mode 100644 index 000000000..911998b0d --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx @@ -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; + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/layouts/BasicLayout/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/layouts/BasicLayout/index.jsx new file mode 100644 index 000000000..18db44df5 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/layouts/BasicLayout/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/layouts/BasicLayout/menuConfig.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/layouts/BasicLayout/menuConfig.js new file mode 100644 index 000000000..5332202be --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/layouts/BasicLayout/menuConfig.js @@ -0,0 +1,11 @@ + +const headerMenuConfig = []; +const asideMenuConfig = [ + { + name: 'Dashboard', + path: '/', + icon: 'smile', + }, +]; +export { headerMenuConfig, asideMenuConfig }; + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/pages/$/index.css b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/pages/$/index.css new file mode 100644 index 000000000..e69de29bb diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/pages/$/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/pages/$/index.jsx new file mode 100644 index 000000000..1f2bf9041 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/pages/$/index.jsx @@ -0,0 +1,123 @@ +// 注意: 出码引擎注入的临时变量默认都以 "__$$" 开头,禁止在搭建的代码中直接访问。 +// 例外:react 框架的导出名和各种组件名除外。 +import React from 'react'; + +import { Switch } from '@alifd/next'; + +import { createJsonpHandler as __$$createJsonpRequestHandler } from '@alilc/lowcode-datasource-jsonp-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 $$Page extends React.Component { + _context = this; + + _dataSourceConfig = this._defineDataSourceConfig(); + _dataSourceEngine = __$$createDataSourceEngine(this._dataSourceConfig, this, { + runtimeConfig: true, + requestHandlersMap: { jsonp: __$$createJsonpRequestHandler() }, + }); + + 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: 'todos', + isInit: function () { + return true; + }.bind(_this), + type: 'jsonp', + options: function () { + return { + method: 'GET', + uri: 'https://a0ee9135-6a7f-4c0f-a215-f0f247ad907d.mock.pstmn.io', + }; + }.bind(_this), + dataHandler: function dataHandler(data) { + return data.data; + }, + }, + ], + }; + } + + componentDidMount() { + this._dataSourceEngine.reloadDataSource(); + } + + render() { + const __$$context = this._context || this; + const { state } = __$$context; + return ( + <div> + {__$$evalArray(() => this.dataSourceMap.todos.data).map((item, index) => + ((__$$context) => ( + <div> + <Switch + checkedChildren="开" + unCheckedChildren="关" + checked={__$$eval(() => item.done)} + /> + </div> + ))(__$$createChildContext(__$$context, { item, index })) + )} + </div> + ); + } +} + +export default $$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; +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/pages/layout.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/pages/layout.jsx new file mode 100644 index 000000000..50fbb2d1f --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/pages/layout.jsx @@ -0,0 +1,10 @@ +import { Outlet } from 'ice'; +import BasicLayout from '@/layouts/BasicLayout'; + +export default function Layout() { + return ( + <BasicLayout> + <Outlet /> + </BasicLayout> + ); +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/typings.d.ts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/typings.d.ts new file mode 100644 index 000000000..a9f8de7ce --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/typings.d.ts @@ -0,0 +1,9 @@ +/// <reference types="@ice/app/types" /> + +export {}; +declare global { + interface Window { + g_config: Record<string, any>; + } +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/utils.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/utils.js new file mode 100644 index 000000000..119071792 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/expected/demo-project/src/utils.js @@ -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 {}; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/schema.json5 b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/schema.json5 new file mode 100644 index 000000000..f91f132ad --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo9-datasource-engine/schema.json5 @@ -0,0 +1,59 @@ +{ + version: '1.0.0', + componentsMap: [ + { + componentName: 'Switch', + package: '@alifd/next', + version: '1.19.18', + exportName: 'Switch', + destructuring: true, + subName: '', + }, + ], + componentsTree: [ + { + componentName: 'Page', + props: {}, + children: [ + { + componentName: 'Div', + props: {}, + children: [ + { + componentName: 'Switch', + props: { + checkedChildren: '开', + unCheckedChildren: '关', + checked: { + type: 'JSExpression', + value: 'this.item.done', + }, + }, + }, + ], + loop: { + type: 'JSExpression', + value: 'this.dataSourceMap.todos.data', + }, + }, + ], + dataSource: { + list: [ + { + id: 'todos', + isInit: true, + type: 'jsonp', + options: { + method: 'GET', + uri: 'https://a0ee9135-6a7f-4c0f-a215-f0f247ad907d.mock.pstmn.io', + }, + dataHandler: { + type: 'JSFunction', + value: 'function dataHandler(data) {return data.data;}', + }, + }, + ], + }, + }, + ], +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/.browserslistrc b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/.browserslistrc new file mode 100644 index 000000000..55a130413 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/.browserslistrc @@ -0,0 +1,3 @@ +defaults +ios_saf 9 + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/.gitignore b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/.gitignore new file mode 100644 index 000000000..4ec178818 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/.gitignore @@ -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 + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/README.md b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/README.md new file mode 100644 index 000000000..6d9dd7521 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/README.md @@ -0,0 +1 @@ +This project is generated by lowcode-code-generator & lowcode-solution-icejs3. \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/ice.config.mts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/ice.config.mts new file mode 100644 index 000000000..fe77c9b6c --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/ice.config.mts @@ -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(), + ], +})); + diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/package.json b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/package.json new file mode 100644 index 000000000..cad3038d9 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/package.json @@ -0,0 +1,46 @@ +{ + "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", + "undefined": "*", + "@alilc/antd-lowcode-materials": "0.9.4", + "@alife/mc-assets-1935": "0.1.42", + "@alife/container": "0.3.7" + }, + "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" +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/app.ts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/app.ts new file mode 100644 index 000000000..6d5856292 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/app.ts @@ -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: '/', + }, +})); diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/constants.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/constants.js new file mode 100644 index 000000000..ea766c9da --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/constants.js @@ -0,0 +1,3 @@ +const __$$constants = {}; + +export default __$$constants; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/document.tsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/document.tsx new file mode 100644 index 000000000..286be9f8c --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/document.tsx @@ -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> + ); +} \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/global.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/global.scss new file mode 100644 index 000000000..82ca3eac7 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/global.scss @@ -0,0 +1,6 @@ +// 引入默认全局样式 +@import '@alifd/next/reset.scss'; + +body { + -webkit-font-smoothing: antialiased; +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/i18n.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/i18n.js new file mode 100644 index 000000000..adbbe673d --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/i18n.js @@ -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 }; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx new file mode 100644 index 000000000..cc70d53be --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss new file mode 100644 index 000000000..81e77fda5 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss @@ -0,0 +1,15 @@ + +.footer { + line-height: 20px; + text-align: center; +} + +.logo { + font-weight: bold; + font-size: 16px; +} + +.copyright { + font-size: 12px; +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx new file mode 100644 index 000000000..265bfdaa0 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss new file mode 100644 index 000000000..1ab56d394 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss @@ -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; + } +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx new file mode 100644 index 000000000..911998b0d --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx @@ -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; + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/layouts/BasicLayout/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/layouts/BasicLayout/index.jsx new file mode 100644 index 000000000..18db44df5 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/layouts/BasicLayout/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/layouts/BasicLayout/menuConfig.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/layouts/BasicLayout/menuConfig.js new file mode 100644 index 000000000..5332202be --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/layouts/BasicLayout/menuConfig.js @@ -0,0 +1,11 @@ + +const headerMenuConfig = []; +const asideMenuConfig = [ + { + name: 'Dashboard', + path: '/', + icon: 'smile', + }, +]; +export { headerMenuConfig, asideMenuConfig }; + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/pages/Test/index.css b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/pages/Test/index.css new file mode 100644 index 000000000..066114aee --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/pages/Test/index.css @@ -0,0 +1,8 @@ +body { + font-size: 12px; +} + +.botton { + width: 100px; + color: #ff00ff; +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/pages/Test/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/pages/Test/index.jsx new file mode 100644 index 000000000..922ad47ad --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/pages/Test/index.jsx @@ -0,0 +1,822 @@ +// 注意: 出码引擎注入的临时变量默认都以 "__$$" 开头,禁止在搭建的代码中直接访问。 +// 例外:react 框架的导出名和各种组件名除外。 +import React from 'react'; + +import { + Modal, + Button, + Typography, + Form, + Select, + Input, + ConfigProvider, + Tooltip, + Empty, +} from '@alilc/antd-lowcode-materials/dist/antd-lowcode.esm.js'; + +import { + AliAutoDiv, + AliAutoSearchTable, +} from '@alife/mc-assets-1935/build/lowcode/index.js'; + +import { + Page as NextPage, + Block as NextBlock, + P as NextP, +} from '@alife/container/lib/index.js'; + +import utils, { RefsManager } from '../../utils'; + +import * as __$$i18n from '../../i18n'; + +import __$$constants from '../../constants'; + +import './index.css'; + +const AliAutoDivDefault = AliAutoDiv.default; + +const AliAutoSearchTableDefault = AliAutoSearchTable.default; + +const NextBlockCell = NextBlock.Cell; + +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 = { + pkgs: [], + total: 0, + isSearch: false, + projects: [], + results: [], + resultVisible: false, + }; + + this.__jp__init(); + this.statusDesc = { + 0: '失败', + 1: '成功', + 2: '构建中', + 3: '构建超时', + }; + this.pageParams = {}; + } + + $ = (refName) => { + return this._refsManager.get(refName); + }; + + $$ = (refName) => { + return this._refsManager.getAll(refName); + }; + + componentDidUpdate(prevProps, prevState, snapshot) {} + + componentWillUnmount() {} + + __jp__init() { + /*...*/ + } + + __jp__initRouter() { + if (window.arsenal) { + this.$router = new window.jianpin.ArsenalRouter({ + app: this.props.microApp, + }); + } else { + this.$router = new window.jianpin.ArsenalRouter(); + } + } + + __jp__initDataSource() { + /*...*/ + } + + __jp__initEnv() { + /*...*/ + } + + __jp__initConfig() { + /*...*/ + } + + __jp__initUtils() { + this.$utils = { + message: window.jianpin.utils.message, + axios: window.jianpin.utils.axios, + moment: window.jianpin.utils.moment, + }; + } + + fetchPkgs() { + /*...*/ + } + + onPageChange(pageIndex, pageSize) { + this.pageParams = { + pageIndex, + pageSize, + }; + this.fetchPkgs(); + } + + renderTime(time) { + return this.$utils.moment(time).format('YYYY-MM-DD HH:mm'); + } + + renderUserName(user) { + return user.user_name; + } + + reload() { + /*...*/ + } + + handleResult() { + /*...*/ + } + + handleDetail() { + // 跳转详情页面 TODO + } + + onResultCancel() { + this.setState({ + resultVisible: false, + }); + } + + formatResult(item) { + if (!item) { + return '暂无结果'; + } + const { channel, plat, version, status } = item; + return [channel, plat, version, status].join('-'); + } + + handleDownload() { + /*...*/ + } + + onFinish() { + /*...*/ + } + + componentDidMount() { + this.$ds.resolve('PROJECTS', { + params: { + size: 5000, + }, + }); + // if (this.state.init === false) { + // this.setState({ + // init: true, + // }); + // } + } + + render() { + const __$$context = this._context || this; + const { state } = __$$context; + return ( + <div + ref={this._refsManager.linkRef('outterView')} + style={{ height: '100%' }} + > + <Modal + title="查看结果" + visible={__$$eval(() => this.state.resultVisible)} + footer={ + <Button + type="primary" + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'onResultCancel', + }, + ], + eventList: [{ name: 'onClick', disabled: true }], + }} + onClick={function () { + this.onResultCancel.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + > + 确定 + </Button> + } + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onCancel', + relatedEventName: 'onResultCancel', + }, + ], + eventList: [ + { name: 'onCancel', disabled: true }, + { name: 'onOk', disabled: false }, + ], + }} + onCancel={function () { + this.onResultCancel.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + width="720px" + centered={true} + > + {__$$evalArray(() => this.state.results).map((item, index) => + ((__$$context) => ( + <AliAutoDivDefault style={{ width: '100%' }}> + {!!__$$eval( + () => + __$$context.state.results && + __$$context.state.results.length > 0 + ) && ( + <AliAutoDivDefault + style={{ + width: '100%', + textAlign: 'left', + marginBottom: '10px', + }} + > + <Button + type="primary" + size="small" + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'handleDownload', + }, + ], + eventList: [{ name: 'onClick', disabled: true }], + }} + onClick={function () { + this.handleDownload.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(__$$context)} + > + 下载全部 + </Button> + </AliAutoDivDefault> + )} + <Typography.Text> + {__$$eval(() => __$$context.formatResult(item))} + </Typography.Text> + {!!__$$eval(() => item.download_link) && ( + <Typography.Link + href={__$$eval(() => item.download_link)} + target="_blank" + > + {' '} + - 点击下载 + </Typography.Link> + )} + {!!__$$eval(() => item.release_notes) && ( + <Typography.Link + href={__$$eval(() => item.release_notes)} + target="_blank" + > + {' '} + - 跳转发布节点 + </Typography.Link> + )} + </AliAutoDivDefault> + ))(__$$createChildContext(__$$context, { item, index })) + )} + </Modal> + <NextPage + columns={12} + headerDivider={true} + placeholderStyle={{ gridRowEnd: 'span 1', gridColumnEnd: 'span 12' }} + placeholder="页面主体内容:拖拽Block布局组件到这里" + header={null} + headerProps={{ background: 'surface' }} + footer={null} + minHeight="100vh" + > + <NextBlock + prefix="next-" + placeholderStyle={{ height: '100%' }} + noPadding={false} + noBorder={false} + background="surface" + layoutmode="O" + colSpan={12} + rowSpan={1} + childTotalColumns={12} + > + <NextBlockCell + title="" + prefix="next-" + placeholderStyle={{ height: '100%' }} + layoutmode="O" + childTotalColumns={12} + isAutoContainer={true} + colSpan={12} + rowSpan={1} + > + <NextP + wrap={false} + type="body2" + verAlign="middle" + textSpacing={true} + align="left" + full={true} + flex={true} + > + <Form + labelCol={{ span: 10 }} + wrapperCol={{ span: 14 }} + onFinish={function () { + this.onFinish.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + name="basic" + layout="inline" + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onFinish', + relatedEventName: 'onFinish', + }, + ], + eventList: [ + { name: 'onFinish', disabled: true }, + { name: 'onFinishFailed', disabled: false }, + { name: 'onFieldsChange', disabled: false }, + { name: 'onValuesChange', disabled: false }, + ], + }} + > + <Form.Item label="项目名称/渠道号" name="channel_id"> + <Select + style={{ width: '280px' }} + options={__$$eval(() => this.state.projects)} + showArrow={true} + tokenSeparators={[]} + showSearch={true} + /> + </Form.Item> + <Form.Item label="版本号" name="buildId"> + <Input + placeholder="请输入" + style={{ width: '280px' }} + size="middle" + /> + </Form.Item> + <Form.Item label="构建人" name="user_id"> + <Select + style={{ width: 200 }} + options={[ + { label: 'A', value: 'A' }, + { label: 'B', value: 'B' }, + { label: 'C', value: 'C' }, + ]} + showSearch={true} + /> + </Form.Item> + <Form.Item label="ID" name="id"> + <Input placeholder="请输入" style={{ width: '160px' }} /> + </Form.Item> + <Form.Item wrapperCol={{ offset: 6 }}> + <Button type="primary" htmlType="submit"> + 查询 + </Button> + </Form.Item> + </Form> + </NextP> + </NextBlockCell> + </NextBlock> + <NextBlock childTotalColumns={12}> + <NextBlockCell isAutoContainer={true} colSpan={12} rowSpan={1}> + <NextP + wrap={false} + type="body2" + verAlign="middle" + textSpacing={true} + align="left" + flex={true} + > + <ConfigProvider locale="zh-CN"> + {!!__$$eval( + () => + !this.state.isSearch || + (this.state.isSearch && this.state.pkgs.length > 0) + ) && ( + <AliAutoSearchTableDefault + rowKey="key" + dataSource={__$$eval(() => this.state.pkgs)} + columns={[ + { + title: 'ID', + dataIndex: 'id', + key: 'name', + width: 80, + }, + { + title: '渠道号', + dataIndex: 'channels', + key: 'age', + width: 142, + render: (text, record, index) => + ((__$$context) => + __$$evalArray(() => text.split(',')).map( + (item, index) => + ((__$$context) => ( + <Typography.Text + style={{ display: 'block' }} + > + {__$$eval(() => item)} + </Typography.Text> + ))( + __$$createChildContext(__$$context, { + item, + index, + }) + ) + ))( + __$$createChildContext(__$$context, { + text, + record, + index, + }) + ), + }, + { + title: '版本号', + dataIndex: 'dic_version', + key: 'address', + render: (text, record, index) => + ((__$$context) => ( + <Tooltip + title={__$$evalArray(() => text || []).map( + (item, index) => + ((__$$context) => ( + <Typography.Text + style={{ + display: 'block', + color: '#FFFFFF', + }} + > + {__$$eval( + () => + item.channelId + + ' / ' + + item.version + )} + </Typography.Text> + ))( + __$$createChildContext(__$$context, { + item, + index, + }) + ) + )} + > + <Typography.Text> + {__$$eval(() => text[0].version)} + </Typography.Text> + </Tooltip> + ))( + __$$createChildContext(__$$context, { + text, + record, + index, + }) + ), + width: 120, + }, + { title: '构建Job', dataIndex: 'job_name', width: 180 }, + { + title: '构建类型', + dataIndex: 'packaging_type', + width: 94, + }, + { + title: '构建状态', + dataIndex: 'status', + render: (text, record, index) => + ((__$$context) => [ + <Typography.Text> + {__$$eval(() => __$$context.statusDesc[text])} + </Typography.Text>, + !!__$$eval(() => text === 2) && ( + <Icon + type="SyncOutlined" + size={16} + spin={true} + style={{ marginLeft: '10px' }} + /> + ), + ])( + __$$createChildContext(__$$context, { + text, + record, + index, + }) + ), + width: 100, + }, + { + title: '构建时间', + dataIndex: 'start_time', + render: function () { + return this.renderTime.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this), + width: 148, + }, + { + title: '构建人', + dataIndex: 'user', + render: function () { + return this.renderUserName.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this), + width: 80, + }, + { + title: 'Jenkins 链接', + dataIndex: 'jenkins_link', + render: (text, record, index) => + ((__$$context) => [ + !!__$$eval(() => text) && ( + <Typography.Link + href={__$$eval(() => text)} + target="_blank" + > + 查看 + </Typography.Link> + ), + !!__$$eval(() => !text) && ( + <Typography.Text>暂无</Typography.Text> + ), + ])( + __$$createChildContext(__$$context, { + text, + record, + index, + }) + ), + width: 120, + }, + { + title: '测试平台链接', + dataIndex: 'is_run_testing', + width: 120, + render: (text, record, index) => + ((__$$context) => [ + !!__$$eval(() => text) && ( + <Typography.Link + href="http://rivermap.alibaba.net/dashboard/testExecute" + target="_blank" + > + 查看 + </Typography.Link> + ), + !!__$$eval(() => !text) && ( + <Typography.Text>暂无</Typography.Text> + ), + ])( + __$$createChildContext(__$$context, { + text, + record, + index, + }) + ), + }, + { title: '触发源', dataIndex: 'source', width: 120 }, + { + title: '详情', + dataIndex: 'id', + render: (text, record, index) => + ((__$$context) => ( + <Button + type="link" + size="small" + style={{ padding: '0px' }} + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'handleDetail', + }, + ], + eventList: [ + { name: 'onClick', disabled: true }, + ], + }} + onClick={function () { + this.handleDetail.apply( + this, + Array.prototype.slice + .call(arguments) + .concat([]) + ); + }.bind(__$$context)} + > + 查看 + </Button> + ))( + __$$createChildContext(__$$context, { + text, + record, + index, + }) + ), + width: 80, + fixed: 'right', + }, + { + title: '结果', + dataIndex: 'id', + render: (text, record, index) => + ((__$$context) => ( + <Button + type="link" + size="small" + style={{ padding: '0px' }} + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'handleResult', + paramStr: 'this.text', + }, + ], + eventList: [ + { name: 'onClick', disabled: true }, + ], + }} + onClick={function () { + this.handleResult.apply( + this, + Array.prototype.slice + .call(arguments) + .concat([]) + ); + }.bind(__$$context)} + ghost={false} + href={__$$eval(() => text)} + > + 查看 + </Button> + ))( + __$$createChildContext(__$$context, { + text, + record, + index, + }) + ), + width: 80, + fixed: 'right', + }, + { + title: '重新执行', + dataIndex: 'id', + width: 92, + render: (text, record, index) => + ((__$$context) => ( + <Button + type="text" + children="" + icon={ + <Icon + type="ReloadOutlined" + size={14} + color="#0593d3" + style={{ + padding: '3px', + border: '1px solid #0593d3', + borderRadius: '14px', + cursor: 'pointer', + height: '22px', + }} + spin={false} + /> + } + shape="circle" + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'reload', + }, + ], + eventList: [ + { name: 'onClick', disabled: true }, + ], + }} + onClick={function () { + this.reload.apply( + this, + Array.prototype.slice + .call(arguments) + .concat([]) + ); + }.bind(__$$context)} + /> + ))( + __$$createChildContext(__$$context, { + text, + record, + index, + }) + ), + fixed: 'right', + }, + ]} + actions={[]} + pagination={{ + total: __$$eval(() => this.state.total), + defaultPageSize: 8, + onPageChange: function () { + return this.onPageChange.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this), + }} + scrollX={1200} + /> + )} + </ConfigProvider> + </NextP> + </NextBlockCell> + </NextBlock> + <NextBlock childTotalColumns={12}> + <NextBlockCell isAutoContainer={true} colSpan={12} rowSpan={1}> + <NextP + wrap={false} + type="body2" + verAlign="middle" + textSpacing={true} + align="left" + flex={true} + > + {!!__$$eval( + () => this.state.pkgs.length < 1 && this.state.isSearch + ) && <Empty description="暂无数据" />} + </NextP> + </NextBlockCell> + </NextBlock> + </NextPage> + </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; +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/pages/layout.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/pages/layout.jsx new file mode 100644 index 000000000..50fbb2d1f --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/pages/layout.jsx @@ -0,0 +1,10 @@ +import { Outlet } from 'ice'; +import BasicLayout from '@/layouts/BasicLayout'; + +export default function Layout() { + return ( + <BasicLayout> + <Outlet /> + </BasicLayout> + ); +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/typings.d.ts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/typings.d.ts new file mode 100644 index 000000000..a9f8de7ce --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/typings.d.ts @@ -0,0 +1,9 @@ +/// <reference types="@ice/app/types" /> + +export {}; +declare global { + interface Window { + g_config: Record<string, any>; + } +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/utils.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/utils.js new file mode 100644 index 000000000..119071792 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/expected/demo-project/src/utils.js @@ -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 {}; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/schema.json5 b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/schema.json5 new file mode 100644 index 000000000..a499dfc65 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_10-jsslot/schema.json5 @@ -0,0 +1,1206 @@ +{ + version: '1.0.0', + componentsMap: [ + { + devMode: 'lowcode', + componentName: 'Slot', + }, + { + package: '@alilc/antd-lowcode-materials', + version: '0.9.4', + exportName: 'Button', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'Button', + }, + { + package: '@alife/mc-assets-1935', + version: '0.1.42', + exportName: 'AliAutoDiv', + main: 'build/lowcode/index.js', + destructuring: true, + subName: 'default', + componentName: 'AliAutoDivDefault', + }, + { + package: '@alilc/antd-lowcode-materials', + version: '0.9.4', + exportName: 'Typography', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + subName: 'Text', + componentName: 'Typography.Text', + }, + { + package: '@alilc/antd-lowcode-materials', + version: '0.9.4', + exportName: 'Typography', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + subName: 'Link', + componentName: 'Typography.Link', + }, + { + package: '@alilc/antd-lowcode-materials', + version: '0.9.4', + exportName: 'Modal', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'Modal', + }, + { + package: '@alilc/antd-lowcode-materials', + version: '0.9.4', + exportName: 'Select', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'Select', + }, + { + package: '@alilc/antd-lowcode-materials', + version: '0.9.4', + exportName: 'Form', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + subName: 'Item', + componentName: 'Form.Item', + }, + { + package: '@alilc/antd-lowcode-materials', + version: '0.9.4', + exportName: 'Input', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'Input', + }, + { + package: '@alilc/antd-lowcode-materials', + version: '0.9.4', + exportName: 'Form', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'Form', + }, + { + package: '@alife/container', + version: '0.3.7', + exportName: 'P', + main: 'lib/index.js', + destructuring: true, + subName: '', + componentName: 'NextP', + }, + { + package: '@alife/container', + version: '0.3.7', + exportName: 'Block', + main: 'lib/index.js', + destructuring: true, + subName: 'Cell', + componentName: 'NextBlockCell', + }, + { + package: '@alife/container', + version: '0.3.7', + exportName: 'Block', + main: 'lib/index.js', + destructuring: true, + subName: '', + componentName: 'NextBlock', + }, + { + package: '@alife/mc-assets-1935', + version: '0.1.42', + exportName: 'AliAutoSearchTable', + main: 'build/lowcode/index.js', + destructuring: true, + subName: 'default', + componentName: 'AliAutoSearchTableDefault', + }, + { + package: '@alilc/antd-lowcode-materials', + version: '0.9.4', + exportName: 'ConfigProvider', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'ConfigProvider', + }, + { + package: '@alilc/antd-lowcode-materials', + version: '0.9.4', + exportName: 'Empty', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'Empty', + }, + { + package: '@alife/container', + version: '0.3.7', + exportName: 'Page', + main: 'lib/index.js', + destructuring: true, + subName: '', + componentName: 'NextPage', + }, + { + devMode: 'lowcode', + componentName: 'Page', + }, + { + package: '@alilc/antd-lowcode-materials', + version: '0.9.4', + exportName: 'Tooltip', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'Tooltip', + }, + ], + componentsTree: [ + { + componentName: 'Page', + id: 'node_dockcviv8fo1', + props: { + ref: 'outterView', + style: { + height: '100%', + }, + }, + fileName: 'test', + dataSource: { + list: [], + }, + css: 'body {\n font-size: 12px;\n}\n\n.botton {\n width: 100px;\n color: #ff00ff\n}', + lifeCycles: { + constructor: { + type: 'JSFunction', + value: "function() {\n this.__jp__init();\n this.statusDesc = {\n 0: '失败',\n 1: '成功',\n 2: '构建中',\n 3: '构建超时',\n };\n this.pageParams = {};\n }", + }, + componentDidMount: { + type: 'JSFunction', + value: "function() {\n this.$ds.resolve('PROJECTS', {\n params: {\n size: 5000,\n },\n });\n // if (this.state.init === false) {\n // this.setState({\n // init: true,\n // });\n // }\n }", + }, + componentDidUpdate: { + type: 'JSFunction', + value: 'function(prevProps, prevState, snapshot) {}', + }, + componentWillUnmount: { + type: 'JSFunction', + value: 'function() {}', + }, + }, + methods: { + __jp__init: { + type: 'JSFunction', + value: 'function() { /*...*/ }', + }, + __jp__initRouter: { + type: 'JSFunction', + value: 'function() {\n if (window.arsenal) {\n this.$router = new window.jianpin.ArsenalRouter({\n app: this.props.microApp,\n });\n } else {\n this.$router = new window.jianpin.ArsenalRouter();\n }\n}', + }, + __jp__initDataSource: { + type: 'JSFunction', + value: 'function() { /*...*/ }', + }, + __jp__initEnv: { + type: 'JSFunction', + value: 'function() { /*...*/ }', + }, + __jp__initConfig: { + type: 'JSFunction', + value: 'function() { /*...*/ }', + }, + __jp__initUtils: { + type: 'JSFunction', + value: 'function() {\n this.$utils = {\n message: window.jianpin.utils.message,\n axios: window.jianpin.utils.axios,\n moment: window.jianpin.utils.moment,\n };\n}', + }, + fetchPkgs: { + type: 'JSFunction', + value: 'function() { /*...*/ }', + }, + onPageChange: { + type: 'JSFunction', + value: 'function(pageIndex, pageSize) {\n this.pageParams = {\n pageIndex,\n pageSize,\n };\n this.fetchPkgs();\n }', + }, + renderTime: { + type: 'JSFunction', + value: "function(time) {\n return this.$utils.moment(time).format('YYYY-MM-DD HH:mm');\n }", + }, + renderUserName: { + type: 'JSFunction', + value: 'function(user) {\n return user.user_name;\n }', + }, + reload: { + type: 'JSFunction', + value: 'function() { /*...*/ }', + }, + handleResult: { + type: 'JSFunction', + value: 'function() { /*...*/ }', + }, + handleDetail: { + type: 'JSFunction', + value: 'function() {\n // 跳转详情页面 TODO\n }', + }, + onResultCancel: { + type: 'JSFunction', + value: 'function() {\n this.setState({\n resultVisible: false,\n });\n }', + }, + formatResult: { + type: 'JSFunction', + value: "function(item) {\n if (!item) {\n return '暂无结果';\n }\n const { channel, plat, version, status } = item;\n return [channel, plat, version, status].join('-');\n }", + }, + handleDownload: { + type: 'JSFunction', + value: 'function() { /*...*/ }', + }, + onFinish: { + type: 'JSFunction', + value: 'function() { /*...*/ }', + }, + }, + state: { + pkgs: [], + total: 0, + isSearch: false, + projects: [], + results: [], + resultVisible: false, + }, + children: [ + { + componentName: 'Modal', + id: 'node_ocksh9yppxb', + props: { + title: '查看结果', + visible: { + type: 'JSExpression', + value: 'this.state.resultVisible', + }, + footer: { + type: 'JSSlot', + value: [ + { + componentName: 'Button', + id: 'node_ocksh9yppxf', + props: { + type: 'primary', + children: '确定', + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'onResultCancel', + }, + ], + eventList: [ + { + name: 'onClick', + disabled: true, + }, + ], + }, + onClick: { + type: 'JSFunction', + value: 'function(){this.onResultCancel.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + }, + }, + ], + }, + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onCancel', + relatedEventName: 'onResultCancel', + }, + ], + eventList: [ + { + name: 'onCancel', + disabled: true, + }, + { + name: 'onOk', + disabled: false, + }, + ], + }, + onCancel: { + type: 'JSFunction', + value: 'function(){this.onResultCancel.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + width: '720px', + centered: true, + }, + hidden: true, + children: [ + { + componentName: 'AliAutoDivDefault', + id: 'node_ockshazuxa4', + props: { + style: { + width: '100%', + }, + }, + loop: { + type: 'JSExpression', + value: 'this.state.results', + }, + children: [ + { + componentName: 'AliAutoDivDefault', + id: 'node_ockshazuxai', + props: { + style: { + width: '100%', + textAlign: 'left', + marginBottom: '10px', + }, + }, + condition: { + type: 'JSExpression', + value: 'this.state.results && this.state.results.length > 0', + }, + children: [ + { + componentName: 'Button', + id: 'node_ockshazuxah', + props: { + type: 'primary', + children: '下载全部', + size: 'small', + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'handleDownload', + }, + ], + eventList: [ + { + name: 'onClick', + disabled: true, + }, + ], + }, + onClick: { + type: 'JSFunction', + value: 'function(){this.handleDownload.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + }, + }, + ], + }, + { + componentName: 'Typography.Text', + id: 'node_ockshazuxa5', + props: { + children: { + type: 'JSExpression', + value: 'this.formatResult(this.item)', + }, + }, + }, + { + componentName: 'Typography.Link', + id: 'node_ockshazuxa6', + props: { + href: { + type: 'JSExpression', + value: 'this.item.download_link', + }, + target: '_blank', + children: ' - 点击下载', + }, + condition: { + type: 'JSExpression', + value: 'this.item.download_link', + }, + }, + { + componentName: 'Typography.Link', + id: 'node_ockshazuxa7', + props: { + href: { + type: 'JSExpression', + value: 'this.item.release_notes', + }, + target: '_blank', + children: ' - 跳转发布节点', + }, + condition: { + type: 'JSExpression', + value: 'this.item.release_notes', + }, + }, + ], + }, + ], + }, + { + componentName: 'NextPage', + id: 'node_ocko19zplh1', + props: { + columns: 12, + headerDivider: true, + placeholderStyle: { + gridRowEnd: 'span 1', + gridColumnEnd: 'span 12', + }, + placeholder: '页面主体内容:拖拽Block布局组件到这里', + header: { + type: 'JSSlot', + title: 'header', + }, + headerProps: { + background: 'surface', + }, + footer: { + type: 'JSSlot', + title: 'footer', + }, + minHeight: '100vh', + }, + title: '页面', + children: [ + { + componentName: 'NextBlock', + id: 'node_ocko19zplh2', + props: { + prefix: 'next-', + placeholderStyle: { + height: '100%', + }, + noPadding: false, + noBorder: false, + background: 'surface', + layoutmode: 'O', + colSpan: 12, + rowSpan: 1, + childTotalColumns: 12, + }, + title: '区块', + children: [ + { + componentName: 'NextBlockCell', + id: 'node_ocko19zplh3', + props: { + title: '', + prefix: 'next-', + placeholderStyle: { + height: '100%', + }, + layoutmode: 'O', + childTotalColumns: 12, + isAutoContainer: true, + colSpan: 12, + rowSpan: 1, + }, + children: [ + { + componentName: 'NextP', + id: 'node_ocks8dtt1ms', + props: { + wrap: false, + type: 'body2', + verAlign: 'middle', + textSpacing: true, + align: 'left', + full: true, + flex: true, + }, + title: '段落', + children: [ + { + componentName: 'Form', + id: 'node_ocks8dtt1mt', + props: { + labelCol: { + span: 10, + }, + wrapperCol: { + span: 14, + }, + onFinish: { + type: 'JSFunction', + value: 'function(){this.onFinish.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + name: 'basic', + layout: 'inline', + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onFinish', + relatedEventName: 'onFinish', + }, + ], + eventList: [ + { + name: 'onFinish', + disabled: true, + }, + { + name: 'onFinishFailed', + disabled: false, + }, + { + name: 'onFieldsChange', + disabled: false, + }, + { + name: 'onValuesChange', + disabled: false, + }, + ], + }, + }, + children: [ + { + componentName: 'Form.Item', + id: 'node_ocks8dtt1mz', + props: { + label: '项目名称/渠道号', + name: 'channel_id', + }, + children: [ + { + componentName: 'Select', + id: 'node_ocksfuhwhsd', + props: { + style: { + width: '280px', + }, + options: { + type: 'JSExpression', + value: 'this.state.projects', + }, + showArrow: true, + tokenSeparators: [], + showSearch: true, + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_ocks8dtt1m12', + props: { + label: '版本号', + name: 'buildId', + }, + children: [ + { + componentName: 'Input', + id: 'node_ocksfuhwhs3', + props: { + placeholder: '请输入', + style: { + width: '280px', + }, + size: 'middle', + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_ocks8dtt1m18', + props: { + label: '构建人', + name: 'user_id', + }, + children: [ + { + componentName: 'Select', + id: 'node_ocksfuhwhsi', + props: { + style: { + width: 200, + }, + options: [ + { + label: 'A', + value: 'A', + }, + { + label: 'B', + value: 'B', + }, + { + label: 'C', + value: 'C', + }, + ], + showSearch: true, + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_ocks8dtt1m19', + props: { + label: 'ID', + name: 'id', + }, + children: [ + { + componentName: 'Input', + id: 'node_ocksfuhwhs8', + props: { + placeholder: '请输入', + style: { + width: '160px', + }, + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_ocks8dtt1mw', + props: { + wrapperCol: { + offset: 6, + }, + }, + children: [ + { + componentName: 'Button', + id: 'node_ocks8dtt1mx', + props: { + type: 'primary', + children: '查询', + htmlType: 'submit', + }, + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + { + componentName: 'NextBlock', + id: 'node_ockshc4ifn1b', + props: { + childTotalColumns: 12, + }, + title: '区块', + children: [ + { + componentName: 'NextBlockCell', + id: 'node_ockshc4ifn1c', + props: { + isAutoContainer: true, + colSpan: 12, + rowSpan: 1, + }, + title: '子区块', + children: [ + { + componentName: 'NextP', + id: 'node_ockshc4ifn1d', + props: { + wrap: false, + type: 'body2', + verAlign: 'middle', + textSpacing: true, + align: 'left', + flex: true, + }, + title: '段落', + children: [ + { + componentName: 'ConfigProvider', + id: 'node_ockshc4ifn1e', + props: { + locale: 'zh-CN', + }, + children: [ + { + componentName: 'AliAutoSearchTableDefault', + id: 'node_ocksfuhwhsx', + props: { + rowKey: 'key', + dataSource: { + type: 'JSExpression', + value: 'this.state.pkgs', + }, + columns: [ + { + title: 'ID', + dataIndex: 'id', + key: 'name', + width: 80, + }, + { + title: '渠道号', + dataIndex: 'channels', + key: 'age', + width: 142, + render: { + type: 'JSSlot', + params: ['text', 'record', 'index'], + value: [ + { + componentName: 'Typography.Text', + id: 'node_ocksh2bq0428', + props: { + children: { + type: 'JSExpression', + value: 'this.item', + }, + style: { + display: 'block', + }, + }, + loop: { + type: 'JSExpression', + value: "this.text.split(',')", + }, + }, + ], + }, + }, + { + title: '版本号', + dataIndex: 'dic_version', + key: 'address', + render: { + type: 'JSSlot', + params: ['text', 'record', 'index'], + value: [ + { + componentName: 'Tooltip', + id: 'node_ocksso4xavj', + props: { + title: { + type: 'JSSlot', + value: [ + { + componentName: 'Typography.Text', + id: 'node_ocksso4xavn', + props: { + children: { + type: 'JSExpression', + value: "this.item. channelId + ' / ' + this.item.version", + }, + style: { + display: 'block', + color: '#FFFFFF', + }, + }, + loop: { + type: 'JSExpression', + value: 'this.text || []', + }, + }, + ], + }, + }, + children: [ + { + componentName: 'Typography.Text', + id: 'node_ocksso4xavm', + props: { + children: { + type: 'JSExpression', + value: 'this.text[0].version', + }, + }, + }, + ], + }, + ], + }, + width: 120, + }, + { + title: '构建Job', + dataIndex: 'job_name', + width: 180, + }, + { + title: '构建类型', + dataIndex: 'packaging_type', + width: 94, + }, + { + title: '构建状态', + dataIndex: 'status', + render: { + type: 'JSSlot', + params: ['text', 'record', 'index'], + value: [ + { + componentName: 'Typography.Text', + id: 'node_ocksh3jkxzw', + props: { + children: { + type: 'JSExpression', + value: 'this.statusDesc[this.text]', + }, + }, + }, + { + componentName: 'Icon', + id: 'node_ocksh3jkxzx', + props: { + type: 'SyncOutlined', + size: 16, + spin: true, + style: { + marginLeft: '10px', + }, + }, + condition: { + type: 'JSExpression', + value: 'this.text === 2', + }, + }, + ], + }, + width: 100, + }, + { + title: '构建时间', + dataIndex: 'start_time', + render: { + type: 'JSFunction', + value: 'function(){ return this.renderTime.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + width: 148, + }, + { + title: '构建人', + dataIndex: 'user', + render: { + type: 'JSFunction', + value: 'function(){ return this.renderUserName.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + width: 80, + }, + { + title: 'Jenkins 链接', + dataIndex: 'jenkins_link', + render: { + type: 'JSSlot', + params: ['text', 'record', 'index'], + value: [ + { + componentName: 'Typography.Link', + id: 'node_ocksh64kbx21', + props: { + href: { + type: 'JSExpression', + value: 'this.text', + }, + target: '_blank', + children: '查看', + }, + condition: { + type: 'JSExpression', + value: 'this.text', + }, + }, + { + componentName: 'Typography.Text', + id: 'node_ocksh64kbx22', + props: { + children: '暂无', + }, + condition: { + type: 'JSExpression', + value: '!this.text', + }, + }, + ], + }, + width: 120, + }, + { + title: '测试平台链接', + dataIndex: 'is_run_testing', + width: 120, + render: { + type: 'JSSlot', + params: ['text', 'record', 'index'], + value: [ + { + componentName: 'Typography.Link', + id: 'node_ocksh3jkxz3e', + props: { + href: 'http://rivermap.alibaba.net/dashboard/testExecute', + target: '_blank', + children: '查看', + }, + condition: { + type: 'JSExpression', + value: 'this.text', + }, + }, + { + componentName: 'Typography.Text', + id: 'node_ocksh3jkxz3f', + props: { + children: '暂无', + }, + condition: { + type: 'JSExpression', + value: '!this.text', + }, + }, + ], + }, + }, + { + title: '触发源', + dataIndex: 'source', + width: 120, + }, + { + title: '详情', + dataIndex: 'id', + render: { + type: 'JSSlot', + params: ['text', 'record', 'index'], + value: [ + { + componentName: 'Button', + id: 'node_ocksh8yryw7', + props: { + type: 'link', + children: '查看', + size: 'small', + style: { + padding: '0px', + }, + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'handleDetail', + }, + ], + eventList: [ + { + name: 'onClick', + disabled: true, + }, + ], + }, + onClick: { + type: 'JSFunction', + value: 'function(){this.handleDetail.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + }, + }, + ], + }, + width: 80, + fixed: 'right', + }, + { + title: '结果', + dataIndex: 'id', + render: { + type: 'JSSlot', + params: ['text', 'record', 'index'], + value: [ + { + componentName: 'Button', + id: 'node_ocksh9v6jw7', + props: { + type: 'link', + children: '查看', + size: 'small', + style: { + padding: '0px', + }, + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'handleResult', + paramStr: 'this.text', + }, + ], + eventList: [ + { + name: 'onClick', + disabled: true, + }, + ], + }, + onClick: { + type: 'JSFunction', + value: 'function(){this.handleResult.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + ghost: false, + href: { + type: 'JSExpression', + value: 'this.text', + }, + }, + }, + ], + }, + width: 80, + fixed: 'right', + }, + { + title: '重新执行', + dataIndex: 'id', + width: 92, + render: { + type: 'JSSlot', + params: ['text', 'record', 'index'], + value: [ + { + componentName: 'Button', + id: 'node_ocksh96rad1g', + props: { + type: 'text', + children: '', + icon: { + type: 'JSSlot', + value: [ + { + componentName: 'Icon', + id: 'node_ocksh96rad1j', + props: { + type: 'ReloadOutlined', + size: 14, + color: '#0593d3', + style: { + padding: '3px', + border: '1px solid #0593d3', + borderRadius: '14px', + cursor: 'pointer', + height: '22px', + }, + spin: false, + }, + }, + ], + }, + shape: 'circle', + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'reload', + }, + ], + eventList: [ + { + name: 'onClick', + disabled: true, + }, + ], + }, + onClick: { + type: 'JSFunction', + value: 'function(){this.reload.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + }, + }, + ], + }, + fixed: 'right', + }, + ], + actions: [], + pagination: { + total: { + type: 'JSExpression', + value: 'this.state.total', + }, + defaultPageSize: 8, + onPageChange: { + type: 'JSFunction', + value: 'function(){ return this.onPageChange.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + }, + scrollX: 1200, + }, + condition: { + type: 'JSExpression', + value: '!this.state.isSearch || (this.state.isSearch && this.state.pkgs.length > 0)', + }, + }, + ], + }, + ], + }, + ], + }, + ], + }, + { + componentName: 'NextBlock', + id: 'node_ocksk6f8fa3b', + props: { + childTotalColumns: 12, + }, + title: '区块', + children: [ + { + componentName: 'NextBlockCell', + id: 'node_ocksk6f8fa3c', + props: { + isAutoContainer: true, + colSpan: 12, + rowSpan: 1, + }, + title: '子区块', + children: [ + { + componentName: 'NextP', + id: 'node_ocksk6f8fa3d', + props: { + wrap: false, + type: 'body2', + verAlign: 'middle', + textSpacing: true, + align: 'left', + flex: true, + }, + title: '段落', + children: [ + { + componentName: 'Empty', + id: 'node_ocksk6f8fa3e', + props: { + description: '暂无数据', + }, + condition: { + type: 'JSExpression', + value: 'this.state.pkgs.length < 1 && this.state.isSearch', + }, + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + i18n: {}, +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/.browserslistrc b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/.browserslistrc new file mode 100644 index 000000000..55a130413 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/.browserslistrc @@ -0,0 +1,3 @@ +defaults +ios_saf 9 + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/.gitignore b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/.gitignore new file mode 100644 index 000000000..4ec178818 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/.gitignore @@ -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 + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/README.md b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/README.md new file mode 100644 index 000000000..6d9dd7521 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/README.md @@ -0,0 +1 @@ +This project is generated by lowcode-code-generator & lowcode-solution-icejs3. \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/ice.config.mts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/ice.config.mts new file mode 100644 index 000000000..fe77c9b6c --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/ice.config.mts @@ -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(), + ], +})); + diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/package.json b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/package.json new file mode 100644 index 000000000..32b4ea877 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/package.json @@ -0,0 +1,46 @@ +{ + "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", + "undefined": "*", + "@alilc/antd-lowcode-materials": "0.11.0", + "@alife/mc-assets-1935": "0.1.43", + "@alife/container": "0.3.7" + }, + "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" +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/app.ts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/app.ts new file mode 100644 index 000000000..6d5856292 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/app.ts @@ -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: '/', + }, +})); diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/constants.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/constants.js new file mode 100644 index 000000000..ea766c9da --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/constants.js @@ -0,0 +1,3 @@ +const __$$constants = {}; + +export default __$$constants; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/document.tsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/document.tsx new file mode 100644 index 000000000..286be9f8c --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/document.tsx @@ -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> + ); +} \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/global.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/global.scss new file mode 100644 index 000000000..82ca3eac7 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/global.scss @@ -0,0 +1,6 @@ +// 引入默认全局样式 +@import '@alifd/next/reset.scss'; + +body { + -webkit-font-smoothing: antialiased; +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/i18n.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/i18n.js new file mode 100644 index 000000000..adbbe673d --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/i18n.js @@ -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 }; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx new file mode 100644 index 000000000..cc70d53be --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss new file mode 100644 index 000000000..81e77fda5 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/layouts/BasicLayout/components/Footer/index.module.scss @@ -0,0 +1,15 @@ + +.footer { + line-height: 20px; + text-align: center; +} + +.logo { + font-weight: bold; + font-size: 16px; +} + +.copyright { + font-size: 12px; +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx new file mode 100644 index 000000000..265bfdaa0 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss new file mode 100644 index 000000000..1ab56d394 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/layouts/BasicLayout/components/Logo/index.module.scss @@ -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; + } +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx new file mode 100644 index 000000000..911998b0d --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/layouts/BasicLayout/components/PageNav/index.jsx @@ -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; + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/layouts/BasicLayout/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/layouts/BasicLayout/index.jsx new file mode 100644 index 000000000..18db44df5 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/layouts/BasicLayout/index.jsx @@ -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> + ); +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/layouts/BasicLayout/menuConfig.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/layouts/BasicLayout/menuConfig.js new file mode 100644 index 000000000..5332202be --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/layouts/BasicLayout/menuConfig.js @@ -0,0 +1,11 @@ + +const headerMenuConfig = []; +const asideMenuConfig = [ + { + name: 'Dashboard', + path: '/', + icon: 'smile', + }, +]; +export { headerMenuConfig, asideMenuConfig }; + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/pages/Test/index.css b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/pages/Test/index.css new file mode 100644 index 000000000..066114aee --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/pages/Test/index.css @@ -0,0 +1,8 @@ +body { + font-size: 12px; +} + +.botton { + width: 100px; + color: #ff00ff; +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/pages/Test/index.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/pages/Test/index.jsx new file mode 100644 index 000000000..5630342f3 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/pages/Test/index.jsx @@ -0,0 +1,976 @@ +// 注意: 出码引擎注入的临时变量默认都以 "__$$" 开头,禁止在搭建的代码中直接访问。 +// 例外:react 框架的导出名和各种组件名除外。 +import React from 'react'; + +import { + Modal, + Button, + Typography, + Form, + Select, + Input, + Tooltip, + Icon, + Empty, +} from '@alilc/antd-lowcode-materials/dist/antd-lowcode.esm.js'; + +import { + AliAutoDiv, + AliAutoSearchTable, +} from '@alife/mc-assets-1935/build/lowcode/index.js'; + +import { + Page as NextPage, + Block as NextBlock, + P as NextP, +} from '@alife/container/lib/index.js'; + +import utils, { RefsManager } from '../../utils'; + +import * as __$$i18n from '../../i18n'; + +import __$$constants from '../../constants'; + +import './index.css'; + +const AliAutoDivDefault = AliAutoDiv.default; + +const AliAutoSearchTableDefault = AliAutoSearchTable.default; + +const NextBlockCell = NextBlock.Cell; + +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 = { + pkgs: [], + total: 0, + isSearch: false, + projects: [], + results: [], + resultVisible: false, + userOptions: [], + searchValues: { user_id: '', channel_id: '' }, + }; + + this.__jp__init(); + this.statusDesc = { + 0: '失败', + 1: '成功', + 2: '构建中', + 3: '构建超时', + }; + this.pageParams = {}; + this.searchParams = {}; + this.userTimeout = null; + this.currentUser = null; + this.notFoundContent = null; + this.projectTimeout = null; + this.currentProject = null; + } + + $ = (refName) => { + return this._refsManager.get(refName); + }; + + $$ = (refName) => { + return this._refsManager.getAll(refName); + }; + + componentDidUpdate(prevProps, prevState, snapshot) {} + + componentWillUnmount() {} + + __jp__init() { + /*...*/ + } + + __jp__initRouter() { + /*...*/ + } + + __jp__initDataSource() { + /*...*/ + } + + __jp__initEnv() { + /*...*/ + } + + __jp__initConfig() { + /*...*/ + } + + __jp__initUtils() { + /*...*/ + } + + setSearchItem() { + /*...*/ + } + + fetchProject() { + /*...*/ + } + + handleProjectSearch() { + /*...*/ + } + + handleProjectChange(id) { + this.setSearchItem({ + channel_id: id, + }); + } + + fetchUser() { + /*...*/ + } + + handleUserSearch() { + /*...*/ + } + + handleUserChange(user) { + console.log('debug user', user); + this.setSearchItem({ + user_id: user, + }); + } + + fetchPkgs() { + /*...*/ + } + + onPageChange(pageIndex, pageSize) { + this.pageParams = { + pageIndex, + pageSize, + }; + this.fetchPkgs(); + } + + renderTime(time) { + return this.$utils.moment(time).format('YYYY-MM-DD HH:mm'); + } + + renderUserName(user) { + return user.user_name; + } + + reload() { + /*...*/ + } + + handleResult() { + /*...*/ + } + + handleDetail() { + /*...*/ + } + + onResultCancel() { + /*...*/ + } + + formatResult() { + /*...*/ + } + + handleDownload() { + /*...*/ + } + + onFinish() { + /*...*/ + } + + componentDidMount() { + this.$ds.resolve('PROJECTS'); + if (this.userTimeout) { + clearTimeout(this.userTimeout); + this.userTimeout = null; + } + if (this.projectTimeout) { + clearTimeout(this.projectTimeout); + this.projectTimeout = null; + } + } + + render() { + const __$$context = this._context || this; + const { state } = __$$context; + return ( + <div + ref={this._refsManager.linkRef('outterView')} + style={{ height: '100%' }} + > + <Modal + title="查看结果" + visible={__$$eval(() => this.state.resultVisible)} + footer={ + <Button + type="primary" + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'onResultCancel', + }, + ], + eventList: [{ name: 'onClick', disabled: true }], + }} + onClick={function () { + this.onResultCancel.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + > + 确定 + </Button> + } + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onCancel', + relatedEventName: 'onResultCancel', + }, + ], + eventList: [ + { name: 'onCancel', disabled: true }, + { name: 'onOk', disabled: false }, + ], + }} + onCancel={function () { + this.onResultCancel.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + width="720px" + centered={true} + closable={true} + keyboard={true} + mask={true} + maskClosable={true} + > + <AliAutoDivDefault style={{ width: '100%' }}> + {!!__$$eval( + () => this.state.results && this.state.results.length > 0 + ) && ( + <AliAutoDivDefault + style={{ + width: '100%', + textAlign: 'left', + marginBottom: '16px', + }} + > + <Button + type="primary" + size="small" + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'handleDownload', + }, + ], + eventList: [{ name: 'onClick', disabled: true }], + }} + onClick={function () { + this.handleDownload.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + > + 下载全部 + </Button> + </AliAutoDivDefault> + )} + {__$$evalArray(() => this.state.results).map((item, index) => + ((__$$context) => ( + <AliAutoDivDefault style={{ width: '100%', marginTop: '10px' }}> + <Typography.Text> + {__$$eval(() => __$$context.formatResult(item))} + </Typography.Text> + {!!__$$eval(() => item.download_link) && ( + <Typography.Link + href={__$$eval(() => item.download_link)} + target="_blank" + > + {' '} + - 点击下载 + </Typography.Link> + )} + {!!__$$eval(() => item.release_notes) && ( + <Typography.Link + href={__$$eval(() => item.release_notes)} + target="_blank" + > + {' '} + - 跳转发布节点 + </Typography.Link> + )} + </AliAutoDivDefault> + ))(__$$createChildContext(__$$context, { item, index })) + )} + </AliAutoDivDefault> + </Modal> + <NextPage + columns={12} + headerDivider={true} + placeholderStyle={{ gridRowEnd: 'span 1', gridColumnEnd: 'span 12' }} + placeholder="页面主体内容:拖拽Block布局组件到这里" + header={null} + headerProps={{ background: 'surface', style: { padding: '' } }} + footer={null} + minHeight="100vh" + contentProps={{ noPadding: false, background: 'transparent' }} + > + <NextBlock childTotalColumns={12}> + <NextBlockCell isAutoContainer={true} colSpan={12} rowSpan={1}> + <NextP + wrap={false} + type="body2" + verAlign="middle" + textSpacing={true} + align="left" + flex={true} + > + <AliAutoDivDefault style={{ width: '100%', display: 'flex' }}> + <AliAutoDivDefault style={{ flex: '1' }}> + <Form + labelCol={{ span: 10 }} + wrapperCol={{ span: 14 }} + onFinish={function () { + this.onFinish.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + name="basic" + layout="inline" + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onFinish', + relatedEventName: 'onFinish', + }, + ], + eventList: [ + { name: 'onFinish', disabled: true }, + { name: 'onFinishFailed', disabled: false }, + { name: 'onFieldsChange', disabled: false }, + { name: 'onValuesChange', disabled: false }, + ], + }} + colon={true} + labelAlign="right" + preserve={true} + scrollToFirstError={true} + size="middle" + values={__$$eval(() => this.state.searchValues)} + > + <Form.Item + label="项目名称/渠道号" + name="channel_id" + labelAlign="right" + colon={true} + > + <Select + style={{ width: '320px' }} + options={__$$eval(() => this.state.projects)} + showArrow={false} + tokenSeparators={[]} + showSearch={true} + defaultActiveFirstOption={true} + size="middle" + bordered={true} + filterOption={true} + optionFilterProp="label" + allowClear={true} + placeholder="请输入项目名称/渠道号" + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onChange', + relatedEventName: 'handleProjectChange', + }, + { + type: 'componentEvent', + name: 'onSearch', + relatedEventName: 'handleProjectSearch', + }, + ], + eventList: [ + { name: 'onBlur', disabled: false }, + { name: 'onChange', disabled: true }, + { name: 'onDeselect', disabled: false }, + { name: 'onFocus', disabled: false }, + { name: 'onInputKeyDown', disabled: false }, + { name: 'onMouseEnter', disabled: false }, + { name: 'onMouseLeave', disabled: false }, + { name: 'onPopupScroll', disabled: false }, + { name: 'onSearch', disabled: true }, + { name: 'onSelect', disabled: false }, + { + name: 'onDropdownVisibleChange', + disabled: false, + }, + ], + }} + onChange={function () { + this.handleProjectChange.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + onSearch={function () { + this.handleProjectSearch.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + /> + </Form.Item> + <Form.Item label="版本号" name="buildId"> + <Input + placeholder="请输入版本号" + style={{ width: '180px' }} + size="middle" + bordered={true} + /> + </Form.Item> + <Form.Item label="构建人" name="user_id"> + <Select + style={{ width: '210px' }} + options={__$$eval(() => this.state.userOptions)} + showSearch={true} + defaultActiveFirstOption={false} + size="middle" + bordered={true} + filterOption={true} + optionFilterProp="label" + notFoundContent={__$$eval( + () => this.userNotFoundContent + )} + showArrow={false} + placeholder="请输入构建人" + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onChange', + relatedEventName: 'handleUserChange', + }, + { + type: 'componentEvent', + name: 'onSearch', + relatedEventName: 'handleUserSearch', + }, + ], + eventList: [ + { name: 'onBlur', disabled: false }, + { name: 'onChange', disabled: true }, + { name: 'onDeselect', disabled: false }, + { name: 'onFocus', disabled: false }, + { name: 'onInputKeyDown', disabled: false }, + { name: 'onMouseEnter', disabled: false }, + { name: 'onMouseLeave', disabled: false }, + { name: 'onPopupScroll', disabled: false }, + { name: 'onSearch', disabled: true }, + { name: 'onSelect', disabled: false }, + { + name: 'onDropdownVisibleChange', + disabled: false, + }, + ], + }} + onChange={function () { + this.handleUserChange.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + onSearch={function () { + this.handleUserSearch.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this)} + allowClear={true} + /> + </Form.Item> + <Form.Item + label="ID" + name="id" + labelAlign="right" + colon={true} + > + <Input + placeholder="请输入ID" + style={{ width: '180px' }} + bordered={true} + size="middle" + /> + </Form.Item> + <Form.Item + wrapperCol={{ offset: 6 }} + labelAlign="right" + colon={true} + style={{ flex: '1', textAlign: 'right' }} + > + <Button + type="primary" + htmlType="submit" + shape="default" + size="middle" + > + 查询 + </Button> + </Form.Item> + </Form> + </AliAutoDivDefault> + <AliAutoDivDefault style={{}}> + <Button + type="link" + htmlType="button" + shape="default" + size="middle" + > + 新增打包 + </Button> + </AliAutoDivDefault> + </AliAutoDivDefault> + </NextP> + </NextBlockCell> + </NextBlock> + <NextBlock + childTotalColumns={12} + mode="inset" + layoutmode="O" + autolayout="(12|1)" + > + <NextBlockCell isAutoContainer={true} colSpan={12} rowSpan={1}> + <NextP + wrap={false} + type="body2" + verAlign="middle" + textSpacing={true} + align="left" + flex={true} + > + {!!__$$eval( + () => + !this.state.isSearch || + (this.state.isSearch && this.state.pkgs.length > 0) + ) && ( + <AliAutoSearchTableDefault + rowKey="key" + dataSource={__$$eval(() => this.state.pkgs)} + columns={[ + { title: 'ID', dataIndex: 'id', key: 'name', width: 80 }, + { + title: '渠道号', + dataIndex: 'channels', + key: 'age', + width: 142, + render: (text, record, index) => + ((__$$context) => + __$$evalArray(() => text.split(',')).map( + (item, index) => + ((__$$context) => ( + <Typography.Text style={{ display: 'block' }}> + {__$$eval(() => item)} + </Typography.Text> + ))( + __$$createChildContext(__$$context, { + item, + index, + }) + ) + ))( + __$$createChildContext(__$$context, { + text, + record, + index, + }) + ), + }, + { + title: '版本号', + dataIndex: 'dic_version', + key: 'address', + render: (text, record, index) => + ((__$$context) => ( + <Tooltip + title={__$$evalArray(() => text || []).map( + (item, index) => + ((__$$context) => ( + <Typography.Text + style={{ + display: 'block', + color: '#FFFFFF', + }} + > + {__$$eval( + () => + item.channelId + ' / ' + item.version + )} + </Typography.Text> + ))( + __$$createChildContext(__$$context, { + item, + index, + }) + ) + )} + > + <Typography.Text> + {__$$eval(() => text[0].version)} + </Typography.Text> + </Tooltip> + ))( + __$$createChildContext(__$$context, { + text, + record, + index, + }) + ), + width: 120, + }, + { title: '构建Job', dataIndex: 'job_name', width: 180 }, + { + title: '构建类型', + dataIndex: 'packaging_type', + width: 94, + }, + { + title: '构建状态', + dataIndex: 'status', + render: (text, record, index) => + ((__$$context) => [ + <Typography.Text> + {__$$eval(() => __$$context.statusDesc[text])} + </Typography.Text>, + !!__$$eval(() => text === 2) && ( + <Icon + type="SyncOutlined" + size={16} + spin={true} + style={{ marginLeft: '10px' }} + /> + ), + ])( + __$$createChildContext(__$$context, { + text, + record, + index, + }) + ), + width: 100, + }, + { + title: '构建时间', + dataIndex: 'start_time', + render: function () { + return this.renderTime.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this), + width: 148, + }, + { + title: '构建人', + dataIndex: 'user', + render: function () { + return this.renderUserName.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this), + width: 80, + }, + { + title: 'Jenkins 链接', + dataIndex: 'jenkins_link', + render: (text, record, index) => + ((__$$context) => [ + !!__$$eval(() => text) && ( + <Typography.Link + href={__$$eval(() => text)} + target="_blank" + > + 查看 + </Typography.Link> + ), + !!__$$eval(() => !text) && ( + <Typography.Text>暂无</Typography.Text> + ), + ])( + __$$createChildContext(__$$context, { + text, + record, + index, + }) + ), + width: 120, + }, + { + title: '测试平台链接', + dataIndex: 'is_run_testing', + width: 120, + render: (text, record, index) => + ((__$$context) => [ + !!__$$eval(() => text) && ( + <Typography.Link + href="http://rivermap.alibaba.net/dashboard/testExecute" + target="_blank" + > + 查看 + </Typography.Link> + ), + !!__$$eval(() => !text) && ( + <Typography.Text>暂无</Typography.Text> + ), + ])( + __$$createChildContext(__$$context, { + text, + record, + index, + }) + ), + }, + { title: '触发源', dataIndex: 'source', width: 120 }, + { + title: '详情', + dataIndex: 'id', + render: (text, record, index) => + ((__$$context) => ( + <Button + type="link" + size="small" + style={{ padding: '0px' }} + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'handleDetail', + }, + ], + eventList: [ + { name: 'onClick', disabled: true }, + ], + }} + onClick={function () { + this.handleDetail.apply( + this, + Array.prototype.slice + .call(arguments) + .concat([]) + ); + }.bind(__$$context)} + > + 查看 + </Button> + ))( + __$$createChildContext(__$$context, { + text, + record, + index, + }) + ), + width: 80, + fixed: 'right', + }, + { + title: '结果', + dataIndex: 'id', + render: (text, record, index) => + ((__$$context) => ( + <Button + type="link" + size="small" + style={{ padding: '0px' }} + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'handleResult', + paramStr: 'this.text', + }, + ], + eventList: [ + { name: 'onClick', disabled: true }, + ], + }} + onClick={function () { + this.handleResult.apply( + this, + Array.prototype.slice + .call(arguments) + .concat([]) + ); + }.bind(__$$context)} + ghost={false} + href={__$$eval(() => text)} + > + 查看 + </Button> + ))( + __$$createChildContext(__$$context, { + text, + record, + index, + }) + ), + width: 80, + fixed: 'right', + }, + { + title: '重新执行', + dataIndex: 'id', + width: 92, + render: (text, record, index) => + ((__$$context) => ( + <Button + type="text" + children="" + icon={ + <Icon + type="ReloadOutlined" + size={14} + color="#0593d3" + style={{ + padding: '3px', + border: '1px solid #0593d3', + borderRadius: '14px', + cursor: 'pointer', + height: '22px', + }} + spin={false} + /> + } + shape="circle" + __events={{ + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'reload', + }, + ], + eventList: [ + { name: 'onClick', disabled: true }, + ], + }} + onClick={function () { + this.reload.apply( + this, + Array.prototype.slice + .call(arguments) + .concat([]) + ); + }.bind(__$$context)} + /> + ))( + __$$createChildContext(__$$context, { + text, + record, + index, + }) + ), + fixed: 'right', + }, + ]} + actions={[]} + pagination={{ + total: __$$eval(() => this.state.total), + defaultPageSize: 10, + onPageChange: function () { + return this.onPageChange.apply( + this, + Array.prototype.slice.call(arguments).concat([]) + ); + }.bind(this), + defaultPageIndex: 1, + }} + scrollX={1200} + isPagination={true} + /> + )} + </NextP> + </NextBlockCell> + </NextBlock> + <NextBlock + childTotalColumns={12} + mode="inset" + layoutmode="O" + autolayout="(12|1)" + > + <NextBlockCell isAutoContainer={true} colSpan={12} rowSpan={1}> + <NextP + wrap={false} + type="body2" + verAlign="middle" + textSpacing={true} + align="left" + flex={true} + > + {!!__$$eval( + () => this.state.pkgs.length < 1 && this.state.isSearch + ) && <Empty description="暂无数据" />} + </NextP> + </NextBlockCell> + </NextBlock> + </NextPage> + </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; +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/pages/layout.jsx b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/pages/layout.jsx new file mode 100644 index 000000000..50fbb2d1f --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/pages/layout.jsx @@ -0,0 +1,10 @@ +import { Outlet } from 'ice'; +import BasicLayout from '@/layouts/BasicLayout'; + +export default function Layout() { + return ( + <BasicLayout> + <Outlet /> + </BasicLayout> + ); +} diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/typings.d.ts b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/typings.d.ts new file mode 100644 index 000000000..a9f8de7ce --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/typings.d.ts @@ -0,0 +1,9 @@ +/// <reference types="@ice/app/types" /> + +export {}; +declare global { + interface Window { + g_config: Record<string, any>; + } +} + \ No newline at end of file diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/utils.js b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/utils.js new file mode 100644 index 000000000..119071792 --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/expected/demo-project/src/utils.js @@ -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 {}; diff --git a/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/schema.json5 b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/schema.json5 new file mode 100644 index 000000000..2bd00adda --- /dev/null +++ b/modules/code-generator/tests/fixtures/test-cases/icejs3-app/demo_11-jsslot-2/schema.json5 @@ -0,0 +1,1457 @@ +{ + version: '1.0.0', + componentsMap: [ + { + devMode: 'lowcode', + componentName: 'Slot', + }, + { + package: '@alilc/antd-lowcode-materials', + version: '0.11.0', + exportName: 'Button', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'Button', + }, + { + package: '@alife/mc-assets-1935', + version: '0.1.43', + exportName: 'AliAutoDiv', + main: 'build/lowcode/index.js', + destructuring: true, + subName: 'default', + componentName: 'AliAutoDivDefault', + }, + { + package: '@alilc/antd-lowcode-materials', + version: '0.11.0', + exportName: 'Typography', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + subName: 'Text', + componentName: 'Typography.Text', + }, + { + package: '@alilc/antd-lowcode-materials', + version: '0.11.0', + exportName: 'Typography', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + subName: 'Link', + componentName: 'Typography.Link', + }, + { + package: '@alilc/antd-lowcode-materials', + version: '0.11.0', + exportName: 'Modal', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'Modal', + }, + { + package: '@alilc/antd-lowcode-materials', + version: '0.11.0', + exportName: 'Select', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'Select', + }, + { + package: '@alilc/antd-lowcode-materials', + version: '0.11.0', + exportName: 'Form', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + subName: 'Item', + componentName: 'Form.Item', + }, + { + package: '@alilc/antd-lowcode-materials', + version: '0.11.0', + exportName: 'Input', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'Input', + }, + { + package: '@alilc/antd-lowcode-materials', + version: '0.11.0', + exportName: 'Form', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'Form', + }, + { + package: '@alife/container', + version: '0.3.7', + exportName: 'P', + main: 'lib/index.js', + destructuring: true, + subName: '', + componentName: 'NextP', + }, + { + package: '@alife/container', + version: '0.3.7', + exportName: 'Block', + main: 'lib/index.js', + destructuring: true, + subName: 'Cell', + componentName: 'NextBlockCell', + }, + { + package: '@alife/container', + version: '0.3.7', + exportName: 'Block', + main: 'lib/index.js', + destructuring: true, + subName: '', + componentName: 'NextBlock', + }, + { + package: '@alilc/antd-lowcode-materials', + version: '0.11.0', + exportName: 'Tooltip', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'Tooltip', + }, + { + package: '@alilc/antd-lowcode-materials', + version: '0.11.0', + exportName: 'Icon', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'Icon', + }, + { + package: '@alife/mc-assets-1935', + version: '0.1.43', + exportName: 'AliAutoSearchTable', + main: 'build/lowcode/index.js', + destructuring: true, + subName: 'default', + componentName: 'AliAutoSearchTableDefault', + }, + { + package: '@alilc/antd-lowcode-materials', + version: '0.11.0', + exportName: 'Empty', + main: 'dist/antd-lowcode.esm.js', + destructuring: true, + componentName: 'Empty', + }, + { + package: '@alife/container', + version: '0.3.7', + exportName: 'Page', + main: 'lib/index.js', + destructuring: true, + subName: '', + componentName: 'NextPage', + }, + { + devMode: 'lowcode', + componentName: 'Page', + }, + ], + componentsTree: [ + { + componentName: 'Page', + id: 'node_dockcviv8fo1', + props: { + ref: 'outterView', + style: { + height: '100%', + }, + }, + fileName: 'test', + dataSource: { + list: [], + }, + css: 'body {\n font-size: 12px;\n}\n\n.botton {\n width: 100px;\n color: #ff00ff\n}', + lifeCycles: { + constructor: { + type: 'JSFunction', + value: "function() {\n this.__jp__init();\n this.statusDesc = {\n 0: '失败',\n 1: '成功',\n 2: '构建中',\n 3: '构建超时',\n };\n this.pageParams = {};\n this.searchParams = {};\n this.userTimeout = null;\n this.currentUser = null;\n this.notFoundContent = null;\n this.projectTimeout = null;\n this.currentProject = null;\n }", + }, + componentDidMount: { + type: 'JSFunction', + value: "function() {\n this.$ds.resolve('PROJECTS');\n if (this.userTimeout) {\n clearTimeout(this.userTimeout);\n this.userTimeout = null;\n }\n if (this.projectTimeout) {\n clearTimeout(this.projectTimeout);\n this.projectTimeout = null;\n }\n }", + }, + componentDidUpdate: { + type: 'JSFunction', + value: 'function(prevProps, prevState, snapshot) {}', + }, + componentWillUnmount: { + type: 'JSFunction', + value: 'function() {}', + }, + }, + methods: { + __jp__init: { + type: 'JSFunction', + value: 'function() { /*...*/ }', + }, + __jp__initRouter: { + type: 'JSFunction', + value: 'function() { /*...*/ }', + }, + __jp__initDataSource: { + type: 'JSFunction', + value: 'function() { /*...*/ }', + }, + __jp__initEnv: { + type: 'JSFunction', + value: 'function() { /*...*/ }', + }, + __jp__initConfig: { + type: 'JSFunction', + value: 'function() { /*...*/ }', + }, + __jp__initUtils: { + type: 'JSFunction', + value: 'function() { /*...*/ }', + }, + setSearchItem: { + type: 'JSFunction', + value: 'function() { /*...*/ }', + }, + fetchProject: { + type: 'JSFunction', + value: 'function() { /*...*/ }', + }, + handleProjectSearch: { + type: 'JSFunction', + value: 'function() { /*...*/ }', + }, + handleProjectChange: { + type: 'JSFunction', + value: 'function(id) {\n this.setSearchItem({\n channel_id: id,\n });\n }', + }, + fetchUser: { + type: 'JSFunction', + value: 'function() { /*...*/ }', + }, + handleUserSearch: { + type: 'JSFunction', + value: 'function() { /*...*/ }', + }, + handleUserChange: { + type: 'JSFunction', + value: "function(user) {\n console.log('debug user', user);\n this.setSearchItem({\n user_id: user,\n });\n }", + }, + fetchPkgs: { + type: 'JSFunction', + value: 'function() { /*...*/ }', + }, + onPageChange: { + type: 'JSFunction', + value: 'function(pageIndex, pageSize) {\n this.pageParams = {\n pageIndex,\n pageSize,\n };\n this.fetchPkgs();\n }', + }, + renderTime: { + type: 'JSFunction', + value: "function(time) {\n return this.$utils.moment(time).format('YYYY-MM-DD HH:mm');\n }", + }, + renderUserName: { + type: 'JSFunction', + value: 'function(user) {\n return user.user_name;\n }', + }, + reload: { + type: 'JSFunction', + value: 'function() { /*...*/ }', + }, + handleResult: { + type: 'JSFunction', + value: 'function() { /*...*/ }', + }, + handleDetail: { + type: 'JSFunction', + value: 'function() { /*...*/ }', + }, + onResultCancel: { + type: 'JSFunction', + value: 'function() { /*...*/ }', + }, + formatResult: { + type: 'JSFunction', + value: 'function() { /*...*/ }', + }, + handleDownload: { + type: 'JSFunction', + value: 'function() { /*...*/ }', + }, + onFinish: { + type: 'JSFunction', + value: 'function() { /*...*/ }', + }, + }, + state: { + pkgs: [], + total: 0, + isSearch: false, + projects: [], + results: [], + resultVisible: false, + userOptions: [], + searchValues: { + user_id: '', + channel_id: '', + }, + }, + children: [ + { + componentName: 'Modal', + id: 'node_ocksh9yppxb', + props: { + title: '查看结果', + visible: { + type: 'JSExpression', + value: 'this.state.resultVisible', + }, + footer: { + type: 'JSSlot', + value: [ + { + componentName: 'Button', + id: 'node_ocksh9yppxf', + props: { + type: 'primary', + children: '确定', + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'onResultCancel', + }, + ], + eventList: [ + { + name: 'onClick', + disabled: true, + }, + ], + }, + onClick: { + type: 'JSFunction', + value: 'function(){this.onResultCancel.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + }, + }, + ], + }, + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onCancel', + relatedEventName: 'onResultCancel', + }, + ], + eventList: [ + { + name: 'onCancel', + disabled: true, + }, + { + name: 'onOk', + disabled: false, + }, + ], + }, + onCancel: { + type: 'JSFunction', + value: 'function(){this.onResultCancel.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + width: '720px', + centered: true, + closable: true, + keyboard: true, + mask: true, + maskClosable: true, + }, + hidden: true, + children: [ + { + componentName: 'AliAutoDivDefault', + id: 'node_ockshazuxa4', + props: { + style: { + width: '100%', + }, + }, + children: [ + { + componentName: 'AliAutoDivDefault', + id: 'node_ockshazuxai', + props: { + style: { + width: '100%', + textAlign: 'left', + marginBottom: '16px', + }, + }, + condition: { + type: 'JSExpression', + value: 'this.state.results && this.state.results.length > 0', + }, + children: [ + { + componentName: 'Button', + id: 'node_ockshazuxah', + props: { + type: 'primary', + children: '下载全部', + size: 'small', + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'handleDownload', + }, + ], + eventList: [ + { + name: 'onClick', + disabled: true, + }, + ], + }, + onClick: { + type: 'JSFunction', + value: 'function(){this.handleDownload.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + }, + }, + ], + }, + { + componentName: 'AliAutoDivDefault', + id: 'node_ockt2muyfi4', + props: { + style: { + width: '100%', + marginTop: '10px', + }, + }, + loop: { + type: 'JSExpression', + value: 'this.state.results', + }, + children: [ + { + componentName: 'Typography.Text', + id: 'node_ockshazuxa5', + props: { + children: { + type: 'JSExpression', + value: 'this.formatResult(this.item)', + }, + }, + }, + { + componentName: 'Typography.Link', + id: 'node_ockshazuxa6', + props: { + href: { + type: 'JSExpression', + value: 'this.item.download_link', + }, + target: '_blank', + children: ' - 点击下载', + }, + condition: { + type: 'JSExpression', + value: 'this.item.download_link', + }, + }, + { + componentName: 'Typography.Link', + id: 'node_ockshazuxa7', + props: { + href: { + type: 'JSExpression', + value: 'this.item.release_notes', + }, + target: '_blank', + children: ' - 跳转发布节点', + }, + condition: { + type: 'JSExpression', + value: 'this.item.release_notes', + }, + }, + ], + }, + ], + }, + ], + }, + { + componentName: 'NextPage', + id: 'node_ocko19zplh1', + props: { + columns: 12, + headerDivider: true, + placeholderStyle: { + gridRowEnd: 'span 1', + gridColumnEnd: 'span 12', + }, + placeholder: '页面主体内容:拖拽Block布局组件到这里', + header: { + type: 'JSSlot', + title: 'header', + }, + headerProps: { + background: 'surface', + style: { + padding: '', + }, + }, + footer: { + type: 'JSSlot', + title: 'footer', + }, + minHeight: '100vh', + contentProps: { + noPadding: false, + background: 'transparent', + }, + }, + title: '页面', + children: [ + { + componentName: 'NextBlock', + id: 'node_ockt3t4q8565', + props: { + childTotalColumns: 12, + }, + title: '区块', + children: [ + { + componentName: 'NextBlockCell', + id: 'node_ockt3t4q8566', + props: { + isAutoContainer: true, + colSpan: 12, + rowSpan: 1, + }, + title: '子区块', + children: [ + { + componentName: 'NextP', + id: 'node_ockt3t4q8567', + props: { + wrap: false, + type: 'body2', + verAlign: 'middle', + textSpacing: true, + align: 'left', + flex: true, + }, + title: '段落', + children: [ + { + componentName: 'AliAutoDivDefault', + id: 'node_ockt3t4q8568', + props: { + style: { + width: '100%', + display: 'flex', + }, + }, + children: [ + { + componentName: 'AliAutoDivDefault', + id: 'node_ockt3t4q857a', + props: { + style: { + flex: '1', + }, + }, + children: [ + { + componentName: 'Form', + id: 'node_ocks8dtt1mt', + props: { + labelCol: { + span: 10, + }, + wrapperCol: { + span: 14, + }, + onFinish: { + type: 'JSFunction', + value: 'function(){this.onFinish.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + name: 'basic', + layout: 'inline', + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onFinish', + relatedEventName: 'onFinish', + }, + ], + eventList: [ + { + name: 'onFinish', + disabled: true, + }, + { + name: 'onFinishFailed', + disabled: false, + }, + { + name: 'onFieldsChange', + disabled: false, + }, + { + name: 'onValuesChange', + disabled: false, + }, + ], + }, + colon: true, + labelAlign: 'right', + preserve: true, + scrollToFirstError: true, + size: 'middle', + values: { + type: 'JSExpression', + value: 'this.state.searchValues', + }, + }, + children: [ + { + componentName: 'Form.Item', + id: 'node_ocks8dtt1mz', + props: { + label: '项目名称/渠道号', + name: 'channel_id', + labelAlign: 'right', + colon: true, + }, + children: [ + { + componentName: 'Select', + id: 'node_ocksfuhwhsd', + props: { + style: { + width: '320px', + }, + options: { + type: 'JSExpression', + value: 'this.state.projects', + }, + showArrow: false, + tokenSeparators: [], + showSearch: true, + defaultActiveFirstOption: true, + size: 'middle', + bordered: true, + filterOption: true, + optionFilterProp: 'label', + allowClear: true, + placeholder: '请输入项目名称/渠道号', + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onChange', + relatedEventName: 'handleProjectChange', + }, + { + type: 'componentEvent', + name: 'onSearch', + relatedEventName: 'handleProjectSearch', + }, + ], + eventList: [ + { + name: 'onBlur', + disabled: false, + }, + { + name: 'onChange', + disabled: true, + }, + { + name: 'onDeselect', + disabled: false, + }, + { + name: 'onFocus', + disabled: false, + }, + { + name: 'onInputKeyDown', + disabled: false, + }, + { + name: 'onMouseEnter', + disabled: false, + }, + { + name: 'onMouseLeave', + disabled: false, + }, + { + name: 'onPopupScroll', + disabled: false, + }, + { + name: 'onSearch', + disabled: true, + }, + { + name: 'onSelect', + disabled: false, + }, + { + name: 'onDropdownVisibleChange', + disabled: false, + }, + ], + }, + onChange: { + type: 'JSFunction', + value: 'function(){this.handleProjectChange.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + onSearch: { + type: 'JSFunction', + value: 'function(){this.handleProjectSearch.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_ocks8dtt1m12', + props: { + label: '版本号', + name: 'buildId', + }, + children: [ + { + componentName: 'Input', + id: 'node_ocksfuhwhs3', + props: { + placeholder: '请输入版本号', + style: { + width: '180px', + }, + size: 'middle', + bordered: true, + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_ocks8dtt1m18', + props: { + label: '构建人', + name: 'user_id', + }, + children: [ + { + componentName: 'Select', + id: 'node_ocksfuhwhsi', + props: { + style: { + width: '210px', + }, + options: { + type: 'JSExpression', + value: 'this.state.userOptions', + }, + showSearch: true, + defaultActiveFirstOption: false, + size: 'middle', + bordered: true, + filterOption: true, + optionFilterProp: 'label', + notFoundContent: { + type: 'JSExpression', + value: 'this.userNotFoundContent', + }, + showArrow: false, + placeholder: '请输入构建人', + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onChange', + relatedEventName: 'handleUserChange', + }, + { + type: 'componentEvent', + name: 'onSearch', + relatedEventName: 'handleUserSearch', + }, + ], + eventList: [ + { + name: 'onBlur', + disabled: false, + }, + { + name: 'onChange', + disabled: true, + }, + { + name: 'onDeselect', + disabled: false, + }, + { + name: 'onFocus', + disabled: false, + }, + { + name: 'onInputKeyDown', + disabled: false, + }, + { + name: 'onMouseEnter', + disabled: false, + }, + { + name: 'onMouseLeave', + disabled: false, + }, + { + name: 'onPopupScroll', + disabled: false, + }, + { + name: 'onSearch', + disabled: true, + }, + { + name: 'onSelect', + disabled: false, + }, + { + name: 'onDropdownVisibleChange', + disabled: false, + }, + ], + }, + onChange: { + type: 'JSFunction', + value: 'function(){this.handleUserChange.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + onSearch: { + type: 'JSFunction', + value: 'function(){this.handleUserSearch.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + allowClear: true, + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_ocks8dtt1m19', + props: { + label: 'ID', + name: 'id', + labelAlign: 'right', + colon: true, + }, + children: [ + { + componentName: 'Input', + id: 'node_ocksfuhwhs8', + props: { + placeholder: '请输入ID', + style: { + width: '180px', + }, + bordered: true, + size: 'middle', + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_ocks8dtt1mw', + props: { + wrapperCol: { + offset: 6, + }, + labelAlign: 'right', + colon: true, + style: { + flex: '1', + textAlign: 'right', + }, + }, + children: [ + { + componentName: 'Button', + id: 'node_ocks8dtt1mx', + props: { + type: 'primary', + children: '查询', + htmlType: 'submit', + shape: 'default', + size: 'middle', + }, + }, + ], + }, + ], + }, + ], + }, + { + componentName: 'AliAutoDivDefault', + id: 'node_ockt3t4q856b', + props: { + style: {}, + }, + children: [ + { + componentName: 'Button', + id: 'node_ockt3t4q85y', + props: { + type: 'link', + children: '新增打包', + htmlType: 'button', + shape: 'default', + size: 'middle', + }, + condition: true, + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + { + componentName: 'NextBlock', + id: 'node_ockshc4ifn1b', + props: { + childTotalColumns: 12, + mode: 'inset', + layoutmode: 'O', + autolayout: '(12|1)', + }, + title: '区块', + children: [ + { + componentName: 'NextBlockCell', + id: 'node_ockshc4ifn1c', + props: { + isAutoContainer: true, + colSpan: 12, + rowSpan: 1, + }, + title: '子区块', + children: [ + { + componentName: 'NextP', + id: 'node_ockshc4ifn1d', + props: { + wrap: false, + type: 'body2', + verAlign: 'middle', + textSpacing: true, + align: 'left', + flex: true, + }, + title: '段落', + children: [ + { + componentName: 'AliAutoSearchTableDefault', + id: 'node_ocksfuhwhsx', + props: { + rowKey: 'key', + dataSource: { + type: 'JSExpression', + value: 'this.state.pkgs', + }, + columns: [ + { + title: 'ID', + dataIndex: 'id', + key: 'name', + width: 80, + }, + { + title: '渠道号', + dataIndex: 'channels', + key: 'age', + width: 142, + render: { + type: 'JSSlot', + params: ['text', 'record', 'index'], + value: [ + { + componentName: 'Typography.Text', + id: 'node_ocksh2bq0428', + props: { + children: { + type: 'JSExpression', + value: 'this.item', + }, + style: { + display: 'block', + }, + }, + loop: { + type: 'JSExpression', + value: "this.text.split(',')", + }, + }, + ], + }, + }, + { + title: '版本号', + dataIndex: 'dic_version', + key: 'address', + render: { + type: 'JSSlot', + params: ['text', 'record', 'index'], + value: [ + { + componentName: 'Tooltip', + id: 'node_ocksts0jqgj', + props: { + title: { + type: 'JSSlot', + value: [ + { + componentName: 'Typography.Text', + id: 'node_ocksts0jqgn', + props: { + children: { + type: 'JSExpression', + value: "this.item. channelId + ' / ' + this.item.version", + }, + style: { + display: 'block', + color: '#FFFFFF', + }, + }, + loop: { + type: 'JSExpression', + value: 'this.text || []', + }, + }, + ], + }, + }, + children: [ + { + componentName: 'Typography.Text', + id: 'node_ocksts0jqgm', + props: { + children: { + type: 'JSExpression', + value: 'this.text[0].version', + }, + }, + }, + ], + }, + ], + }, + width: 120, + }, + { + title: '构建Job', + dataIndex: 'job_name', + width: 180, + }, + { + title: '构建类型', + dataIndex: 'packaging_type', + width: 94, + }, + { + title: '构建状态', + dataIndex: 'status', + render: { + type: 'JSSlot', + params: ['text', 'record', 'index'], + value: [ + { + componentName: 'Typography.Text', + id: 'node_ocksh3jkxzw', + props: { + children: { + type: 'JSExpression', + value: 'this.statusDesc[this.text]', + }, + }, + }, + { + componentName: 'Icon', + id: 'node_ocksh3jkxzx', + props: { + type: 'SyncOutlined', + size: 16, + spin: true, + style: { + marginLeft: '10px', + }, + }, + condition: { + type: 'JSExpression', + value: 'this.text === 2', + }, + }, + ], + }, + width: 100, + }, + { + title: '构建时间', + dataIndex: 'start_time', + render: { + type: 'JSFunction', + value: 'function(){ return this.renderTime.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + width: 148, + }, + { + title: '构建人', + dataIndex: 'user', + render: { + type: 'JSFunction', + value: 'function(){ return this.renderUserName.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + width: 80, + }, + { + title: 'Jenkins 链接', + dataIndex: 'jenkins_link', + render: { + type: 'JSSlot', + params: ['text', 'record', 'index'], + value: [ + { + componentName: 'Typography.Link', + id: 'node_ocksh64kbx21', + props: { + href: { + type: 'JSExpression', + value: 'this.text', + }, + target: '_blank', + children: '查看', + }, + condition: { + type: 'JSExpression', + value: 'this.text', + }, + }, + { + componentName: 'Typography.Text', + id: 'node_ocksh64kbx22', + props: { + children: '暂无', + }, + condition: { + type: 'JSExpression', + value: '!this.text', + }, + }, + ], + }, + width: 120, + }, + { + title: '测试平台链接', + dataIndex: 'is_run_testing', + width: 120, + render: { + type: 'JSSlot', + params: ['text', 'record', 'index'], + value: [ + { + componentName: 'Typography.Link', + id: 'node_ocksh3jkxz3e', + props: { + href: 'http://rivermap.alibaba.net/dashboard/testExecute', + target: '_blank', + children: '查看', + }, + condition: { + type: 'JSExpression', + value: 'this.text', + }, + }, + { + componentName: 'Typography.Text', + id: 'node_ocksh3jkxz3f', + props: { + children: '暂无', + }, + condition: { + type: 'JSExpression', + value: '!this.text', + }, + }, + ], + }, + }, + { + title: '触发源', + dataIndex: 'source', + width: 120, + }, + { + title: '详情', + dataIndex: 'id', + render: { + type: 'JSSlot', + params: ['text', 'record', 'index'], + value: [ + { + componentName: 'Button', + id: 'node_ocksh8yryw7', + props: { + type: 'link', + children: '查看', + size: 'small', + style: { + padding: '0px', + }, + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'handleDetail', + }, + ], + eventList: [ + { + name: 'onClick', + disabled: true, + }, + ], + }, + onClick: { + type: 'JSFunction', + value: 'function(){this.handleDetail.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + }, + }, + ], + }, + width: 80, + fixed: 'right', + }, + { + title: '结果', + dataIndex: 'id', + render: { + type: 'JSSlot', + params: ['text', 'record', 'index'], + value: [ + { + componentName: 'Button', + id: 'node_ocksh9v6jw7', + props: { + type: 'link', + children: '查看', + size: 'small', + style: { + padding: '0px', + }, + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'handleResult', + paramStr: 'this.text', + }, + ], + eventList: [ + { + name: 'onClick', + disabled: true, + }, + ], + }, + onClick: { + type: 'JSFunction', + value: 'function(){this.handleResult.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + ghost: false, + href: { + type: 'JSExpression', + value: 'this.text', + }, + }, + }, + ], + }, + width: 80, + fixed: 'right', + }, + { + title: '重新执行', + dataIndex: 'id', + width: 92, + render: { + type: 'JSSlot', + params: ['text', 'record', 'index'], + value: [ + { + componentName: 'Button', + id: 'node_ocksh96rad1g', + props: { + type: 'text', + children: '', + icon: { + type: 'JSSlot', + value: [ + { + componentName: 'Icon', + id: 'node_ocksh96rad1j', + props: { + type: 'ReloadOutlined', + size: 14, + color: '#0593d3', + style: { + padding: '3px', + border: '1px solid #0593d3', + borderRadius: '14px', + cursor: 'pointer', + height: '22px', + }, + spin: false, + }, + }, + ], + }, + shape: 'circle', + __events: { + eventDataList: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'reload', + }, + ], + eventList: [ + { + name: 'onClick', + disabled: true, + }, + ], + }, + onClick: { + type: 'JSFunction', + value: 'function(){this.reload.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + }, + }, + ], + }, + fixed: 'right', + }, + ], + actions: [], + pagination: { + total: { + type: 'JSExpression', + value: 'this.state.total', + }, + defaultPageSize: 10, + onPageChange: { + type: 'JSFunction', + value: 'function(){ return this.onPageChange.apply(this,Array.prototype.slice.call(arguments).concat([])) }', + }, + defaultPageIndex: 1, + }, + scrollX: 1200, + isPagination: true, + }, + condition: { + type: 'JSExpression', + value: '!this.state.isSearch || (this.state.isSearch && this.state.pkgs.length > 0)', + }, + }, + ], + }, + ], + }, + ], + }, + { + componentName: 'NextBlock', + id: 'node_ocksk6f8fa3b', + props: { + childTotalColumns: 12, + mode: 'inset', + layoutmode: 'O', + autolayout: '(12|1)', + }, + title: '区块', + children: [ + { + componentName: 'NextBlockCell', + id: 'node_ocksk6f8fa3c', + props: { + isAutoContainer: true, + colSpan: 12, + rowSpan: 1, + }, + title: '子区块', + children: [ + { + componentName: 'NextP', + id: 'node_ocksk6f8fa3d', + props: { + wrap: false, + type: 'body2', + verAlign: 'middle', + textSpacing: true, + align: 'left', + flex: true, + }, + title: '段落', + children: [ + { + componentName: 'Empty', + id: 'node_ocksk6f8fa3e', + props: { + description: '暂无数据', + }, + condition: { + type: 'JSExpression', + value: 'this.state.pkgs.length < 1 && this.state.isSearch', + }, + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + i18n: {}, +} diff --git a/modules/code-generator/tests/public/solutions/icejs3-app.test.ts b/modules/code-generator/tests/public/solutions/icejs3-app.test.ts new file mode 100644 index 000000000..3d3ec4eac --- /dev/null +++ b/modules/code-generator/tests/public/solutions/icejs3-app.test.ts @@ -0,0 +1,63 @@ +import 'jest'; +import fs from 'fs'; +import glob from 'glob'; +import JSON from 'json5'; +import path from 'path'; + +import { + getSubDirectoriesSync, + removeActualDirRecursiveSync, + createDiskPublisher, +} from '../../helpers/solutionHelper'; + +import CodeGenerator from '../../../src'; + +import type { IPublicTypeProjectSchema } from '@alilc/lowcode-types'; + +jest.setTimeout(15 * 1000); + +const TEST_CASES_DIR = path.join(__dirname, '../../fixtures/test-cases/icejs3-app'); +const SHOULD_UPDATE_EXPECTED = process.env.UPDATE_EXPECTED === 'true'; + +getSubDirectoriesSync(TEST_CASES_DIR).forEach(defineTest); + +function defineTest(caseDirName: string) { + test(`react-app (icejs 3)/${caseDirName} should works`, async () => { + try { + const caseFullDir = path.join(TEST_CASES_DIR, caseDirName); + const schema = JSON.parse(fs.readFileSync(path.join(caseFullDir, 'schema.json5'), 'utf-8')); + const actualDir = path.join(caseFullDir, SHOULD_UPDATE_EXPECTED ? 'expected' : 'actual'); + + removeActualDirRecursiveSync(actualDir, caseFullDir); + + await exportProject(schema, actualDir, 'demo-project'); + + const actualFiles = glob.sync('**/*.{js,jsx,json,ts,tsx,less,css,scss,sass}', { + cwd: actualDir, + }); + + expect(actualFiles.length > 0).toBeTruthy(); + + // runPrettierSync(actualFiles, actualDir); + + if (!SHOULD_UPDATE_EXPECTED) { + expect(caseFullDir).toBeSameFileContents(); + } + } catch (e) { + throw e; // just for debugger + } + }); +} + +async function exportProject(schemaJson: IPublicTypeProjectSchema, targetPath: string, projectName: string) { + const icejs3AppBuilder = CodeGenerator.solutions.icejs3(); + const result = await icejs3AppBuilder.generateProject(schemaJson); + + const publisher = createDiskPublisher(); + await publisher.publish({ + project: result, + outputPath: targetPath, + projectSlug: projectName, + createProjectFolder: true, + }); +}