├── .jshintrc ├── .gitignore ├── src ├── template.xtpl ├── core │ ├── exportGlobalCss.ts │ ├── interface.ts │ ├── consts.ts │ ├── exportCreateApp.ts │ ├── entry.ts │ ├── exportBlock.ts │ └── utils.ts └── entry_back.js ├── schema.md ├── webpack.config.js ├── tsconfig.json ├── README.zh-CN.md ├── package.json ├── README.md ├── code ├── style.js ├── style.responsive.js ├── style.less ├── index.jsx ├── result.js ├── style.css └── style.responsive.css ├── test ├── index.js ├── react.test.js ├── data.js └── animation.js └── dist └── main.js /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esversion": 6 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | npm-debug.log 3 | .idea/ 4 | .vscode 5 | /test/template.* 6 | codeExample 7 | demo 8 | demo-ts 9 | dist/ 10 | coverage 11 | .nyc_output -------------------------------------------------------------------------------- /src/template.xtpl: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React, { Component } from 'react'; 4 | 5 | {{{imports}}} 6 | 7 | {{{utils}}} 8 | 9 | {{{style}}} 10 | 11 | {{{classes}}} 12 | 13 | {{{exports}}} 14 | -------------------------------------------------------------------------------- /schema.md: -------------------------------------------------------------------------------- 1 | ## D2C Schema 2 | 3 | * [D2C Schema 介绍](https://www.imgcook.com/docs?slug=d2c-json-info) 4 | * [D2C Schema TS 描述](https://www.imgcook.com/docs?slug=d2c-schema-ts) 5 | * [D2C Schema Demo](https://www.imgcook.com/docs?slug=d2c-schema-demo) -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | watch: false, 5 | // entry: './src/entry.js', 6 | entry: './src/core/entry.ts', 7 | resolve: { 8 | extensions: ['.tsx', '.ts', '.js'], 9 | }, 10 | module: { 11 | rules: [ 12 | { 13 | test: /\.tsx?$/, 14 | use:'ts-loader', 15 | exclude: /node_modules/, 16 | }, 17 | 18 | ] 19 | }, 20 | output: { 21 | filename: 'index.js', 22 | path: path.resolve(__dirname, 'src'), 23 | libraryTarget: 'commonjs2', 24 | } 25 | }; -------------------------------------------------------------------------------- /src/core/exportGlobalCss.ts: -------------------------------------------------------------------------------- 1 | import { IPanelDisplay } from './interface'; 2 | const { CSS_TYPE, prettierCssOpt } = require('./consts'); 3 | 4 | export default function exportGlobalCss(schema, option): IPanelDisplay[] { 5 | const { 6 | prettier, 7 | dslConfig, 8 | _, 9 | folder = '' 10 | } = option; 11 | 12 | // 只有一个模块时,生成到当前模块 13 | if (dslConfig.globalCss && dslConfig.inlineStyle !== CSS_TYPE.INLINE_CSS) { 14 | return [ 15 | { 16 | panelName: `global.css`, 17 | panelValue: prettier.format(schema.css || '', prettierCssOpt), 18 | panelType: 'css', 19 | folder: folder, 20 | }, 21 | ]; 22 | } else { 23 | return []; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "module": "commonjs", 5 | "jsx": "preserve", 6 | "declaration": true, 7 | "outDir": "./dist", 8 | "strict": true, 9 | "noImplicitAny": false, 10 | "moduleResolution": "node", 11 | "allowSyntheticDefaultImports": true, 12 | "esModuleInterop": true, 13 | "resolveJsonModule": true, 14 | "experimentalDecorators": true, 15 | "emitDecoratorMetadata": true, 16 | "noImplicitThis": true, 17 | "noUnusedLocals": false, 18 | "stripInternal": true, 19 | "pretty": true, 20 | "lib": [ 21 | "es2017", 22 | "dom" 23 | ] 24 | }, 25 | "include": [ 26 | "./src" 27 | ], 28 | "exclude": [ 29 | "node_modules" 30 | ] 31 | } -------------------------------------------------------------------------------- /src/core/interface.ts: -------------------------------------------------------------------------------- 1 | 2 | export interface IPanelDisplay { 3 | panelName: string; 4 | panelValue: string; 5 | panelType: string; 6 | folder?: string; 7 | panelDependencies?: IDependence[] 8 | } 9 | 10 | export interface IImport { 11 | _import: string; 12 | package: string; 13 | version: string; 14 | } 15 | 16 | export interface IDependence { 17 | package: string; 18 | version: string; 19 | } 20 | 21 | 22 | export interface IDslConfig { 23 | responseWidth: number; 24 | scale: number; 25 | globalCss: boolean; 26 | componentStyle: 'components' | 'hooks'; 27 | cssUnit: 'px' | 'vw' | 'rpx' | 'rem'; 28 | inlineStyle: 'import' | 'module' | 'inline' | 'module_style'; 29 | outputStyle: 'project' | 'component'; 30 | cssStyle: 'kebabCase' | 'camelCase' | 'snakeCase', 31 | htmlFontSize: number 32 | } -------------------------------------------------------------------------------- /README.zh-CN.md: -------------------------------------------------------------------------------- 1 | 简体中文 | [English](https://github.com/imgcook-dsl/react-standard/blob/master/README.md) 2 | 3 | ## 概述 4 | 5 | 为了让开发者能够在 imgcook 平台还原生成满足自己诉求的代码,我们提供了开放的 DSL 机制,在这套开放体系下,开发者可以根据 imgcook 提供的数据和能力生成自己所需的代码,不再局限于官方所提供的代码。 6 |
7 | 8 | ## 如何使用 9 | 10 | ### 选择 DSL 代码生成 11 | 12 | DSL 使用方式是在[编辑器](https://www.imgcook.com/editor#/)中点击「生成代码」按钮选择的 DSL 列表默认加载 DSL 官方提供的和自己定义的如图。 13 |

14 | 15 |

16 | 17 | ### 使用第三方 DSL 18 | 19 | 如果要使用其他开发者开发的 DSL 需要去 [DSL 广场](https://www.imgcook.com/dsl)收藏(如图),收藏后会在生成代码 DSL 选择列表中出现。 20 |

21 | 22 |

23 | 24 | ## 如何开发 25 | 26 | 参考开发文档:https://www.imgcook.com/docs?slug=dsl-dev 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-standard", 3 | "version": "1.0.0", 4 | "devDependencies": { 5 | "@babel/preset-env": "^7.7.7", 6 | "@babel/preset-stage-0": "^7.0.0", 7 | "@imgcook/dsl-helper": "0.0.1-alpha.0", 8 | "@types/node": "^16.11.7", 9 | "chai": "^4.3.4", 10 | "co": "^4.6.0", 11 | "css": "^3.0.0", 12 | "fs-extra": "^8.0.1", 13 | "lodash": "^4.17.11", 14 | "mocha": "^9.1.3", 15 | "nyc": "^15.1.0", 16 | "prettier": "^2.4.1", 17 | "thunkify": "^2.1.2", 18 | "ts-loader": "^8.2.0", 19 | "typescript": "^4.4.4", 20 | "vm2": "^3.5.2", 21 | "webpack": "^4.41.5", 22 | "webpack-cli": "^3.3.10", 23 | "xml-beautifier": "^0.4.0", 24 | "xtemplate": "^4.6.1", 25 | "xtpl": "^3.4.0" 26 | }, 27 | "scripts": { 28 | "debug": "npx webpack --config webpack.config.js --mode=development", 29 | "build": "npx webpack --config webpack.config.js --mode=production", 30 | "demo": "node ./test/index.js", 31 | "test": "mocha --colors test/**/*.test.js", 32 | "start": "node ./test/index.js", 33 | "test-with-coverage": "nyc --reporter=html mocha --exit --colors test/**/*.test.js " 34 | }, 35 | "dependencies": {} 36 | } 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | English | [简体中文](https://github.com/imgcook-dsl/react-standard/blob/master/README.zh-CN.md) 2 | 3 | ## Abstract 4 | 5 | To provide a better experience for users who use imgcook to generate codes on multiple platforms, we provide Open DSL System (open domain-specific language system) that transcodes imgcook schema to any programming languages you love. The developers can use default DSLs provided imgcook (React, React Hooks, Rax, etc.) or submit there own DSLs to generate customized codes. 6 | 7 | ## Usage 8 | 9 | ### Select a DSL for code generation 10 | 11 | In the imgcook editor, you can select a DSL for your project in the「code generation」panel. After that, we will generate your desired codes in the format of the language you chose. 12 |

13 | ![pic 1](https://gw.alicdn.com/imgextra/i3/O1CN01jYTHv81qNig5iq1cP_!!6000000005484-2-tps-2816-1592.png) 14 |

15 | 16 | ### Use a third-party DSL 17 | 18 | If you do not want to use the official DSL provided by imgcook. You can go to the [DSL Market](https://www.imgcook.com/dsl) to add the third-party DSLs to your own DSL list. 19 |

20 | ![pic 2](https://gw.alicdn.com/imgextra/i1/O1CN01QyHOTB1MK75O8LXOE_!!6000000001415-2-tps-2816-1596.png) 21 |

22 | 23 | ## How to make your own DSL 24 | 25 | Please refer to the DSL development part: https://www.imgcook.com/docs?slug=dsl-dev 26 | -------------------------------------------------------------------------------- /src/core/consts.ts: -------------------------------------------------------------------------------- 1 | import { IPanelDisplay, IDslConfig } from './interface'; 2 | export const prettierJsOpt = { 3 | parser: 'babel', 4 | printWidth: 120, 5 | singleQuote: true, 6 | }; 7 | export const prettierCssOpt = { 8 | parser: 'css', 9 | }; 10 | 11 | export const prettierJsonOpt = { 12 | parser: 'json', 13 | }; 14 | 15 | export const prettierScssOpt = { 16 | parser: 'scss', 17 | tabWidth: 2, 18 | printWidth: 120, 19 | singleQuote: true 20 | }; 21 | 22 | export const prettierLessOpt = { 23 | parser: 'less', 24 | tabWidth: 2, 25 | printWidth: 120, 26 | singleQuote: true 27 | }; 28 | 29 | export const prettierHtmlOpt = { 30 | parser: 'html', 31 | printWidth: 120, 32 | singleQuote: true 33 | }; 34 | 35 | 36 | export const CSS_TYPE = { 37 | MODULE_CLASS: 'module', 38 | MODULE_STYLE: 'module_style', 39 | IMPORT_CLASS: 'import', 40 | INLINE_CSS: 'inline', 41 | } 42 | 43 | export const COMPONENT_TYPE = { 44 | HOOKS: 'hooks', 45 | COMPONENT: 'component', 46 | } 47 | 48 | export const OUTPUT_TYPE = { 49 | PROJECT: 'project', 50 | COMPONENT: 'component', 51 | } 52 | 53 | 54 | // 记录全局参数配置,初始化时直接修改 55 | export let DSL_CONFIG: IDslConfig = { 56 | responseWidth: 750, 57 | scale: 1, 58 | globalCss: false, 59 | cssUnit: 'px', 60 | componentStyle: 'hooks', 61 | inlineStyle: 'module', 62 | outputStyle: 'component', 63 | cssStyle: 'camelCase', 64 | htmlFontSize: 16 65 | }; 66 | 67 | 68 | export const initConfig = (cfg)=>{ 69 | DSL_CONFIG = Object.assign(DSL_CONFIG, cfg) 70 | } 71 | 72 | 73 | 74 | 75 | export const defaultGlobalCss = `/** 76 | * 全局样式 global.css 77 | */ 78 | .flex-row { 79 | display: flex; 80 | flex-direction: row; 81 | } 82 | 83 | .flex-col { 84 | display: flex; 85 | flex-direction: column; 86 | } 87 | 88 | .justify-start { 89 | display: flex; 90 | justify-content: flex-start; 91 | } 92 | 93 | .justify-center { 94 | display: flex; 95 | justify-content: center; 96 | } 97 | 98 | .justify-end { 99 | display: flex; 100 | justify-content: flex-end; 101 | } 102 | 103 | .justify-between { 104 | display: flex; 105 | justify-content: space-between; 106 | } 107 | 108 | .items-start { 109 | display: flex; 110 | align-items: flex-start; 111 | } 112 | 113 | .items-center { 114 | display: flex; 115 | align-items: center; 116 | } 117 | 118 | .items-end { 119 | display: flex; 120 | align-items: flex-end; 121 | } 122 | 123 | ` -------------------------------------------------------------------------------- /code/style.js: -------------------------------------------------------------------------------- 1 | export default { 2 | box: { 3 | display: 'flex', 4 | flexDirection: 'row', 5 | justifyContent: 'space-around', 6 | alignItems: 'flex-start', 7 | height: '534px' 8 | }, 9 | bd: { 10 | display: 'flex', 11 | position: 'relative', 12 | alignItems: 'flex-start', 13 | flexDirection: 'row', 14 | opacity: '1.00', 15 | width: '342px', 16 | height: '342px' 17 | }, 18 | layer: { position: 'absolute', top: '0px', left: '0px', width: '342px', height: '342px', overflow: 'hidden' }, 19 | bg: { position: 'absolute', top: '0px', left: '0px', opacity: '1.00', width: '342px', height: '342px' }, 20 | wrap: { 21 | boxSizing: 'border-box', 22 | display: 'flex', 23 | position: 'relative', 24 | alignItems: 'center', 25 | flexDirection: 'row', 26 | marginTop: '18px', 27 | marginLeft: '18px', 28 | borderRadius: '15px', 29 | backgroundColor: 'rgba(0,0,0,0.40)', 30 | paddingRight: '9px', 31 | paddingLeft: '10px', 32 | height: '30px' 33 | }, 34 | riverdinwei: { opacity: '1.00', width: '14px', height: '18px' }, 35 | distance: { 36 | marginLeft: '4px', 37 | height: '22px', 38 | fontWeight: 400, 39 | fontSize: '18px', 40 | color: '#ffffff', 41 | lineHeight: '22px', 42 | whiteSpace: 'nowrap' 43 | }, 44 | main: { 45 | display: 'flex', 46 | alignItems: 'flex-start', 47 | flexDirection: 'row', 48 | justifyContent: 'center', 49 | backgroundColor: '#ffffff', 50 | width: '342px', 51 | height: '114px' 52 | }, 53 | title: { 54 | marginTop: '22px', 55 | width: '300px', 56 | height: '88px', 57 | fontWeight: 400, 58 | fontSize: '30px', 59 | color: '#333333', 60 | lineHeight: '44px', 61 | overflow: 'hidden', 62 | textOverflow: 'ellipsis' 63 | }, 64 | ft: { 65 | boxSizing: 'border-box', 66 | display: 'flex', 67 | alignItems: 'center', 68 | flexDirection: 'row', 69 | justifyContent: 'space-between', 70 | borderBottomLeftRadius: '12px', 71 | borderBottomRightRadius: '12px', 72 | backgroundColor: '#ffffff', 73 | paddingRight: '17px', 74 | paddingLeft: '18px', 75 | width: '342px', 76 | height: '78px', 77 | overflow: 'hidden' 78 | }, 79 | block: { display: 'flex', alignItems: 'center', flexDirection: 'row', height: '30px' }, 80 | xianjin: { width: '30px', height: '30px' }, 81 | fashionHome: { 82 | marginLeft: '6px', 83 | height: '28px', 84 | fontWeight: 300, 85 | fontSize: '24px', 86 | color: '#666666', 87 | lineHeight: '28px', 88 | whiteSpace: 'nowrap' 89 | }, 90 | group: { display: 'flex', alignItems: 'center', flexDirection: 'row', height: '30px' }, 91 | favorite: { width: '22px', height: '22px' }, 92 | num: { 93 | marginLeft: '5px', 94 | height: '26px', 95 | fontWeight: 400, 96 | fontSize: '22px', 97 | color: '#999999', 98 | lineHeight: '26px', 99 | whiteSpace: 'nowrap' 100 | } 101 | }; 102 | -------------------------------------------------------------------------------- /code/style.responsive.js: -------------------------------------------------------------------------------- 1 | export default { 2 | box: { 3 | display: 'flex', 4 | flexDirection: 'row', 5 | justifyContent: 'space-around', 6 | alignItems: 'flex-start', 7 | height: '71.20vw' 8 | }, 9 | bd: { 10 | display: 'flex', 11 | position: 'relative', 12 | alignItems: 'flex-start', 13 | flexDirection: 'row', 14 | opacity: '1.00', 15 | width: '45.60vw', 16 | height: '45.60vw' 17 | }, 18 | layer: { 19 | position: 'absolute', 20 | top: '0.00vw', 21 | left: '0.00vw', 22 | width: '45.60vw', 23 | height: '45.60vw', 24 | overflow: 'hidden' 25 | }, 26 | bg: { position: 'absolute', top: '0.00vw', left: '0.00vw', opacity: '1.00', width: '45.60vw', height: '45.60vw' }, 27 | wrap: { 28 | boxSizing: 'border-box', 29 | display: 'flex', 30 | position: 'relative', 31 | alignItems: 'center', 32 | flexDirection: 'row', 33 | marginTop: '2.40vw', 34 | marginLeft: '2.40vw', 35 | borderRadius: '2.00vw', 36 | backgroundColor: 'rgba(0,0,0,0.40)', 37 | paddingRight: '1.20vw', 38 | paddingLeft: '1.33vw', 39 | height: '4.00vw' 40 | }, 41 | riverdinwei: { opacity: '1.00', width: '1.87vw', height: '2.40vw' }, 42 | distance: { 43 | marginLeft: '0.53vw', 44 | height: '2.93vw', 45 | fontWeight: 400, 46 | fontSize: '2.40vw', 47 | color: '#ffffff', 48 | lineHeight: '2.93vw', 49 | whiteSpace: 'nowrap' 50 | }, 51 | main: { 52 | display: 'flex', 53 | alignItems: 'flex-start', 54 | flexDirection: 'row', 55 | justifyContent: 'center', 56 | backgroundColor: '#ffffff', 57 | width: '45.60vw', 58 | height: '15.20vw' 59 | }, 60 | title: { 61 | marginTop: '2.93vw', 62 | width: '40.00vw', 63 | height: '11.73vw', 64 | fontWeight: 400, 65 | fontSize: '4.00vw', 66 | color: '#333333', 67 | lineHeight: '5.87vw', 68 | overflow: 'hidden', 69 | textOverflow: 'ellipsis' 70 | }, 71 | ft: { 72 | boxSizing: 'border-box', 73 | display: 'flex', 74 | alignItems: 'center', 75 | flexDirection: 'row', 76 | justifyContent: 'space-between', 77 | borderBottomLeftRadius: '1.60vw', 78 | borderBottomRightRadius: '1.60vw', 79 | backgroundColor: '#ffffff', 80 | paddingRight: '2.27vw', 81 | paddingLeft: '2.40vw', 82 | width: '45.60vw', 83 | height: '10.40vw', 84 | overflow: 'hidden' 85 | }, 86 | block: { display: 'flex', alignItems: 'center', flexDirection: 'row', height: '4.00vw' }, 87 | xianjin: { width: '4.00vw', height: '4.00vw' }, 88 | fashionHome: { 89 | marginLeft: '0.80vw', 90 | height: '3.73vw', 91 | fontWeight: 300, 92 | fontSize: '3.20vw', 93 | color: '#666666', 94 | lineHeight: '3.73vw', 95 | whiteSpace: 'nowrap' 96 | }, 97 | group: { display: 'flex', alignItems: 'center', flexDirection: 'row', height: '4.00vw' }, 98 | favorite: { width: '2.93vw', height: '2.93vw' }, 99 | num: { 100 | marginLeft: '0.67vw', 101 | height: '3.47vw', 102 | fontWeight: 400, 103 | fontSize: '2.93vw', 104 | color: '#999999', 105 | lineHeight: '3.47vw', 106 | whiteSpace: 'nowrap' 107 | } 108 | }; 109 | -------------------------------------------------------------------------------- /code/style.less: -------------------------------------------------------------------------------- 1 | .box { 2 | display: flex; 3 | flex-direction: row; 4 | justify-content: space-around; 5 | align-items: flex-start; 6 | height: 534px; 7 | .bd { 8 | display: flex; 9 | position: relative; 10 | align-items: flex-start; 11 | flex-direction: row; 12 | opacity: 1; 13 | width: 342px; 14 | height: 342px; 15 | .layer { 16 | position: absolute; 17 | top: 0px; 18 | left: 0px; 19 | width: 342px; 20 | height: 342px; 21 | overflow: hidden; 22 | } 23 | .bg { 24 | position: absolute; 25 | top: 0px; 26 | left: 0px; 27 | opacity: 1; 28 | width: 342px; 29 | height: 342px; 30 | } 31 | .wrap { 32 | box-sizing: border-box; 33 | display: flex; 34 | position: relative; 35 | align-items: center; 36 | flex-direction: row; 37 | margin-top: 18px; 38 | margin-left: 18px; 39 | border-radius: 15px; 40 | background-color: rgba(0, 0, 0, 0.4); 41 | padding-right: 9px; 42 | padding-left: 10px; 43 | height: 30px; 44 | .riverdinwei { 45 | opacity: 1; 46 | width: 14px; 47 | height: 18px; 48 | } 49 | .distance { 50 | margin-left: 4px; 51 | height: 22px; 52 | font-weight: 400; 53 | font-size: 18px; 54 | color: #ffffff; 55 | line-height: 22px; 56 | white-space: nowrap; 57 | } 58 | } 59 | } 60 | .main { 61 | display: flex; 62 | align-items: flex-start; 63 | flex-direction: row; 64 | justify-content: center; 65 | background-color: #ffffff; 66 | width: 342px; 67 | height: 114px; 68 | .title { 69 | margin-top: 22px; 70 | width: 300px; 71 | height: 88px; 72 | font-weight: 400; 73 | font-size: 30px; 74 | color: #333333; 75 | line-height: 44px; 76 | overflow: hidden; 77 | text-overflow: ellipsis; 78 | } 79 | } 80 | .ft { 81 | box-sizing: border-box; 82 | display: flex; 83 | align-items: center; 84 | flex-direction: row; 85 | justify-content: space-between; 86 | border-bottom-left-radius: 12px; 87 | border-bottom-right-radius: 12px; 88 | background-color: #ffffff; 89 | padding-right: 17px; 90 | padding-left: 18px; 91 | width: 342px; 92 | height: 78px; 93 | overflow: hidden; 94 | .block { 95 | display: flex; 96 | align-items: center; 97 | flex-direction: row; 98 | height: 30px; 99 | .xianjin { 100 | width: 30px; 101 | height: 30px; 102 | } 103 | .fashionHome { 104 | margin-left: 6px; 105 | height: 28px; 106 | font-weight: 300; 107 | font-size: 24px; 108 | color: #666666; 109 | line-height: 28px; 110 | white-space: nowrap; 111 | } 112 | } 113 | .group { 114 | display: flex; 115 | align-items: center; 116 | flex-direction: row; 117 | height: 30px; 118 | .favorite { 119 | width: 22px; 120 | height: 22px; 121 | } 122 | .num { 123 | margin-left: 5px; 124 | height: 26px; 125 | font-weight: 400; 126 | font-size: 22px; 127 | color: #999999; 128 | line-height: 26px; 129 | white-space: nowrap; 130 | } 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | const co = require('co'); 2 | const xtpl = require('xtpl'); 3 | const fs = require('fs'); 4 | const thunkify = require('thunkify'); 5 | const path = require('path'); 6 | const { NodeVM } = require('vm2'); 7 | const _ = require('lodash'); 8 | const data = require('./data'); 9 | const componentsMap = require('./componentsMap'); 10 | const helper = require('@imgcook/dsl-helper'); 11 | 12 | const prettier = require('prettier/standalone'); 13 | 14 | const parserHtml =require('prettier/parser-html'); 15 | const parserBabel= require('prettier/parser-babel'); 16 | const parserCss =require('prettier/parser-postcss'); 17 | const parserMarkDown=require('prettier/parser-markdown'); 18 | 19 | const entry = require('../src/index') 20 | const browerParser = { 21 | babel: parserBabel, 22 | json: parserBabel, 23 | vue: parserHtml, 24 | css: parserCss, 25 | scss: parserCss, 26 | less: parserCss, 27 | html: parserHtml, 28 | md: parserMarkDown 29 | } 30 | 31 | const vm = new NodeVM({ 32 | console: 'inherit', 33 | sandbox: {}, 34 | }); 35 | 36 | const runCode = (data, dslConfig) => { 37 | data = _.cloneDeep(data); 38 | const config = _.get(data, 'imgcook.dslConfig', {}); 39 | _.set(data, 'imgcook.dslConfig', Object.assign(config, dslConfig)); 40 | 41 | const code = fs.readFileSync( 42 | path.resolve(__dirname, '../src/index.js'), 43 | 'utf8' 44 | ); 45 | const options = { 46 | prettier: { 47 | format: (str, opt) => { 48 | if (opt && browerParser[opt.parser]) { 49 | opt.plugins = [browerParser[opt.parser]] 50 | } else { 51 | return str 52 | } 53 | try{ 54 | return prettier.format(str, opt) 55 | }catch(e){ 56 | console.error('format error', e) 57 | return str 58 | } 59 | 60 | } 61 | }, 62 | _: _, 63 | responsive: { 64 | width: 750, 65 | viewportWidth: 375, 66 | }, 67 | helper, 68 | componentsMap, 69 | } 70 | 71 | // const files = vm.run(code)(data,options); 72 | 73 | const files = entry(data, options); 74 | return files.panelDisplay; 75 | }; 76 | 77 | co(function*() { 78 | const panelDisplay = runCode(data, { 79 | componentStyle: "hooks", 80 | cssUnit: "rpx", 81 | dsl: "rax", 82 | globalCss: true, 83 | htmlFontSize: "16", 84 | inlineStyle: "module", 85 | responseHeight: 1334, 86 | responseWidth: 750, 87 | useHooks: true, 88 | useTypescript: false 89 | }); 90 | 91 | // console.log('panelDisplay', panelDisplay) 92 | 93 | const baseDir = '../demo/src/dist'; 94 | 95 | if (fs.existsSync(path.join(__dirname, baseDir))) { 96 | fs.rmdirSync(path.join(__dirname, baseDir), { recursive: true }); 97 | console.log('删除文件夹') 98 | } 99 | mkDirsSync(path.join(__dirname, baseDir)); 100 | console.log('创建文件夹', path.join(__dirname, baseDir)) 101 | // const baseDir = '../code'; 102 | // 生成到目标目录运行 103 | 104 | panelDisplay.forEach((file) => { 105 | if (file.folder) { 106 | let fileFolder = path.join(__dirname, `${baseDir}/${file.folder}`); 107 | if (!fs.existsSync(fileFolder)) { 108 | mkDirsSync(fileFolder); 109 | } 110 | fs.writeFileSync( 111 | path.join(__dirname, `${baseDir}/${file.folder}/${file.panelName}`), 112 | file.panelValue 113 | ); 114 | } else { 115 | fs.writeFileSync( 116 | path.join(__dirname, `${baseDir}/${file.panelName}`), 117 | file.panelValue 118 | ); 119 | } 120 | }); 121 | }); 122 | 123 | function mkDirsSync(dirname) { 124 | if (fs.existsSync(dirname)) { 125 | return true; 126 | } else { 127 | if (mkDirsSync(path.dirname(dirname))) { 128 | fs.mkdirSync(dirname); 129 | return true; 130 | } 131 | } 132 | } 133 | 134 | module.exports = { 135 | runCode, 136 | }; 137 | -------------------------------------------------------------------------------- /src/core/exportCreateApp.ts: -------------------------------------------------------------------------------- 1 | import { IPanelDisplay } from './interface'; 2 | 3 | import { CSS_TYPE, OUTPUT_TYPE, prettierJsOpt, prettierCssOpt, prettierJsonOpt, prettierHtmlOpt } from './consts'; 4 | 5 | 6 | export default function exportCreateApp(schema, option): IPanelDisplay[] { 7 | const folderName = schema.folderName; 8 | const { 9 | dependencies, 10 | dslConfig, 11 | _, 12 | prettier 13 | } = option; 14 | 15 | let panelValue = ''; 16 | const panelDisplay: IPanelDisplay[] = []; 17 | 18 | panelValue = ` 19 | 20 | 21 | 22 | 23 | 24 | 25 | 29 | React App 30 | 31 | 32 |
33 | 34 | 35 | ` 36 | panelDisplay.push({ 37 | panelName: `index.html`, 38 | panelType: 'html', 39 | panelValue, 40 | folder: 'public', 41 | }); 42 | 43 | // index.js 44 | const isGlobal = schema.css && dslConfig.globalCss && dslConfig.inlineStyle !== CSS_TYPE.INLINE_CSS; 45 | panelValue = `'use strict'; 46 | import React from 'react'; 47 | import ReactDOM from 'react-dom/client'; 48 | ${isGlobal ? " import './global.css';" : ''} 49 | import './index.css'; 50 | import App from './App'; 51 | 52 | const root = ReactDOM.createRoot(document.getElementById('root')); 53 | root.render( 54 | 55 | 56 | 57 | ); 58 | 59 | ` 60 | panelDisplay.push({ 61 | panelName: `index.${ dslConfig.useTypescript?'tsx': 'jsx'}`, 62 | panelType: dslConfig.useTypescript?'tsx': 'jsx', 63 | panelValue: prettier.format(panelValue, prettierJsOpt), 64 | folder: 'src', 65 | }); 66 | 67 | // index.css 68 | panelValue = `` 69 | panelDisplay.push({ 70 | panelName: `index.css`, 71 | panelType: 'css', 72 | panelValue: prettier.format(panelValue, prettierCssOpt), 73 | folder: 'src', 74 | }); 75 | 76 | 77 | 78 | // dependencies 79 | let packDependencies = dependencies; 80 | 81 | // if (schema.imgcook && schema.imgcook.dependencies) { 82 | // schema.imgcook.dependencies.forEach(({packageRax1, versionRax1}) => { 83 | // packDependencies[packageRax1] = versionRax1 84 | // }) 85 | // } 86 | 87 | 88 | // package.json 89 | const packageJson = { 90 | "title": "imgcook demo", 91 | "scripts": { 92 | "start": "react-scripts start", 93 | "build": "react-scripts build", 94 | "test": "react-scripts test", 95 | "eject": "react-scripts eject" 96 | }, 97 | "main": "index.js", 98 | "devDependencies": { 99 | "typescript": "^4.0.5" 100 | }, 101 | "dependencies": { 102 | "react": "^18.0.0", 103 | "react-dom": "^18.0.0", 104 | "react-scripts": "5.0.1", 105 | ...packDependencies 106 | }, 107 | "browserslist": [ 108 | ">0.2%", 109 | "not dead", 110 | "not ie <= 11", 111 | "not op_mini all" 112 | ] 113 | } 114 | panelValue = JSON.stringify(packageJson, null, 4) 115 | panelDisplay.push({ 116 | panelName: `package.json`, 117 | panelType: 'json', 118 | panelValue: prettier.format(panelValue, prettierJsonOpt), 119 | folder: option.folder || '', 120 | }); 121 | 122 | 123 | if (dslConfig.useTypescript) { 124 | panelValue = `{ 125 | "compilerOptions": { 126 | "target": "es5", 127 | "lib": [ 128 | "dom", 129 | "dom.iterable", 130 | "esnext" 131 | ], 132 | "allowJs": true, 133 | "skipLibCheck": true, 134 | "esModuleInterop": true, 135 | "allowSyntheticDefaultImports": true, 136 | "strict": true, 137 | "forceConsistentCasingInFileNames": true, 138 | "noFallthroughCasesInSwitch": true, 139 | "module": "esnext", 140 | "moduleResolution": "node", 141 | "resolveJsonModule": true, 142 | "isolatedModules": true, 143 | "noEmit": true, 144 | "jsx": "react-jsx" 145 | }, 146 | "include": [ 147 | "src" 148 | ] 149 | } 150 | ` 151 | panelDisplay.push({ 152 | panelName: `tsconfig.json`, 153 | panelType: 'json', 154 | panelValue: prettier.format(panelValue, prettierJsonOpt), 155 | folder: option.folder || '', 156 | }); 157 | } 158 | 159 | 160 | return panelDisplay; 161 | } 162 | -------------------------------------------------------------------------------- /src/core/entry.ts: -------------------------------------------------------------------------------- 1 | import { IPanelDisplay, IDslConfig } from './interface'; 2 | import { 3 | line2Hump, 4 | transComponentsMap, 5 | initSchema, 6 | traverse, 7 | genStyleClass, 8 | getGlobalClassNames, 9 | genStyleCode, 10 | simpleStyle, 11 | commonStyle 12 | } from './utils'; 13 | import { CSS_TYPE, COMPONENT_TYPE, OUTPUT_TYPE, initConfig, defaultGlobalCss } from './consts'; 14 | 15 | 16 | 17 | import exportBlock from './exportBlock'; 18 | // const exportPage from './exportPage'; 19 | import exportCreateApp from './exportCreateApp'; 20 | import exportGlobalCss from './exportGlobalCss'; 21 | 22 | module.exports = function (schema, option) { 23 | // get blocks json 24 | const blocks: any[] = []; 25 | const pages: any[] = [] 26 | 27 | // 参数设置 28 | option.scale = 750 / ((option.responsive && option.responsive.width) || 750); 29 | option.componentsMap = transComponentsMap(option.componentsMap); 30 | option.blockInPage = schema.componentName === 'Page'; 31 | option.pageGlobalCss = schema.css || ''; 32 | 33 | const dslConfig = Object.assign( 34 | { 35 | scale: option.scale, 36 | globalCss: true, 37 | cssUnit: 'px', 38 | inlineStyle: CSS_TYPE.MODULE_CLASS, 39 | componentStyle: COMPONENT_TYPE.HOOKS, 40 | htmlFontSize: 16 41 | }, 42 | option._.get(schema, 'imgcook.dslConfig') 43 | ); 44 | 45 | dslConfig.useHooks = dslConfig.componentStyle === COMPONENT_TYPE.HOOKS; 46 | dslConfig.useTypescript = dslConfig.jsx === 'typescript' 47 | option.dslConfig = dslConfig; 48 | 49 | schema.css = schema.css || defaultGlobalCss; 50 | 51 | // 初始化全局参数 52 | initConfig(dslConfig); 53 | 54 | // 可选 className name style 55 | // inlineStyle = inlineStyle !== 'className'; 56 | 57 | 58 | const { inlineStyle } = dslConfig 59 | // clear schema 60 | initSchema(schema); 61 | 62 | const isProject = dslConfig.outputStyle == OUTPUT_TYPE.PROJECT; 63 | if(isProject){ 64 | // 导出完整项目时,使根节点为Page 65 | schema.componentName = 'Page'; 66 | } 67 | // 记录所有blocks 68 | traverse(schema, (json) => { 69 | switch (json.componentName.toLowerCase()) { 70 | case 'block': 71 | blocks.push(json); 72 | break; 73 | case 'page': 74 | pages.push(json); 75 | break; 76 | } 77 | }); 78 | 79 | 80 | // 提取全局样式 81 | if([CSS_TYPE.IMPORT_CLASS].includes(inlineStyle)){ 82 | traverse(schema, (json) => { 83 | let classnames: string[] = json.classnames || []; 84 | let style = json.props.style; 85 | const enableGlobalCss = dslConfig.globalCss && schema.css 86 | 87 | // 计算全局样式类名 88 | if (enableGlobalCss) { 89 | const cssResults = getGlobalClassNames(style, schema.css); 90 | if (cssResults.names.length > 0) { 91 | classnames = [...classnames, ...cssResults.names] 92 | } 93 | json.props.style = cssResults.style; 94 | json.classnames = classnames || [] 95 | } 96 | }); 97 | } 98 | 99 | 100 | // 提取公用样式 101 | if([CSS_TYPE.IMPORT_CLASS, CSS_TYPE.MODULE_CLASS].includes(inlineStyle)){ 102 | blocks.forEach((block) => { 103 | commonStyle(block); 104 | }); 105 | 106 | } 107 | 108 | // 精简默认样式 109 | simpleStyle(schema) 110 | 111 | 112 | // 提取全局样式,类名数组存于 json.classString , 剩余样式覆盖 style 113 | traverse(schema, (json) => { 114 | let classnames: string[] = json.classnames || []; 115 | const className = json.props && json.props.className || ''; 116 | 117 | let classString = ''; 118 | const style = json.props.style; 119 | 120 | // inline 内联 (不需要提取同名) 121 | if (inlineStyle === CSS_TYPE.INLINE_CSS) { 122 | className && (classString = `className="${className}"`); 123 | json.props.codeStyle = style; // 内联样式 124 | } else if (inlineStyle === CSS_TYPE.MODULE_STYLE) { 125 | className && (classString = ` style={${genStyleCode('styles', className)}}`); 126 | } else if(inlineStyle == CSS_TYPE.MODULE_CLASS) { 127 | classnames.push(className); 128 | classnames = classnames.filter(name=>name!==''); 129 | classnames = classnames.map(name=>genStyleCode('styles', name)); 130 | 131 | if (classnames.length > 1) { 132 | classString = ` className={\`${classnames.map(name=>`\$\{${name}\}`).join(' ').trim()}\`}`; 133 | } else if(classnames.length == 1) { 134 | classString = ` className={${classnames[0].trim()}}`; 135 | } 136 | }else if(inlineStyle == CSS_TYPE.IMPORT_CLASS){ 137 | classnames.push(className); 138 | classnames = classnames.filter(name=>name!==''); 139 | if (classnames.length >= 1) { 140 | classString = ` className="${classnames.join(' ')}"`; 141 | } 142 | } 143 | 144 | json.props.style = style; 145 | json.classString = classString; 146 | }); 147 | 148 | 149 | 150 | 151 | option.blocksCount = blocks.length; 152 | option.pagesCount = pages.length; 153 | 154 | // export module code 155 | let panelDisplay: IPanelDisplay[] = []; 156 | 157 | 158 | blocks.length > 0 && 159 | blocks.forEach((block) => { 160 | const result = exportBlock(block, option); 161 | panelDisplay = panelDisplay.concat(result); 162 | }); 163 | // export Page code 164 | if (schema.componentName === 'Page') { 165 | const result = exportBlock(schema, option); 166 | panelDisplay = panelDisplay.concat(result); 167 | } 168 | 169 | 170 | 171 | if (isProject) { 172 | // 依赖 package.json 173 | const dependencies = {}; 174 | for (let item of panelDisplay) { 175 | if (item.panelDependencies && item.panelDependencies.length > 0) { 176 | for (let pack of item.panelDependencies) { 177 | dependencies[pack.package] = pack.version || '*' 178 | } 179 | } 180 | } 181 | 182 | // 项目模式生成文件放到src中 183 | panelDisplay = panelDisplay.map(item => { 184 | item.folder = 'src/' + item.folder 185 | return item; 186 | }); 187 | 188 | // 项目文件 189 | panelDisplay = panelDisplay.concat(exportCreateApp(schema, { ...option, dependencies })); 190 | } 191 | 192 | 193 | // 全局样式 194 | panelDisplay = panelDisplay.concat(exportGlobalCss(schema, { ...option, folder: isProject ? 'src' : '' })); 195 | 196 | 197 | return { 198 | panelDisplay, 199 | noTemplate: true, 200 | }; 201 | }; 202 | -------------------------------------------------------------------------------- /code/index.jsx: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React, { Component } from 'react'; 4 | 5 | import './style.css'; 6 | 7 | const print = function(value) { 8 | console.log(value); 9 | }; 10 | class Block_0 extends Component { 11 | render() { 12 | return ( 13 |
14 | 15 | 23 | 31 | 39 | 40 | {true && } 41 | 49 | 57 | 65 | 73 | 81 | 89 | 90 | 98 | 106 | 107 | 113 | 121 | 129 | 130 | 131 | 132 | 140 | 141 | 142 | 3 143 | 次机会 144 | 145 | 153 | 154 | 155 | 163 | 164 | 165 | 173 | 181 | 189 | 190 | 191 | 192 |
193 | ); 194 | } 195 | } 196 | export default Block_0; 197 | -------------------------------------------------------------------------------- /dist/main.js: -------------------------------------------------------------------------------- 1 | !function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t){e.exports=function(e,t){const{_:n,prettier:o}=t;componentsMap=((e={})=>{if(!e||!Array.isArray(e.list))return[];return e.list.reduce((e,t)=>{const n=t.name;if(!e[n]){if(t.dependence)try{let e="string"==typeof t.dependence?JSON.parse(t.dependence):t.dependence;e&&(t.packageName=e.package,t.dependence=e),t.dependenceVersion||(t.dependenceVersion="*"),t.exportName=e.export_name,t.subName=e.sub_name,/^\d/.test(t.dependenceVersion)&&(t.dependenceVersion="^"+t.dependenceVersion)}catch(e){console.log(e)}e[n]=t}return e},{})})(t.componentsMap);const r=new Map,s=[],a={},i=[],c=[],p=t.responsive.width/100||750;let l=!1;const d=e=>/^\{\{.*\}\}$/.test(e),f=e=>"[object Function]"==={}.toString.call(e)?e.toString():"string"==typeof e?e:"object"==typeof e?JSON.stringify(e,(e,t)=>"function"==typeof t?t.toString():t):String(e),u=(e,t)=>{let n="";return Object.keys(e).map(o=>{n+=`.${o}{${h(e[o],t)}}`}),n},m=["fontSize","marginTop","marginBottom","paddingTop","paddingBottom","height","top","bottom","width","maxWidth","left","right","paddingRight","paddingLeft","marginLeft","marginRight","lineHeight","borderBottomRightRadius","borderBottomLeftRadius","borderTopRightRadius","borderTopLeftRadius","borderRadius"],$=["opacity","fontWeight"],h=(e,t)=>{const o=[];for(let r in e){let s=e[r];-1!=m.indexOf(r)?(t?(s=(parseInt(s)/p).toFixed(2),s=0==s?s:s+"vw"):(s=parseInt(s).toFixed(2),s=0==s?s:s+"px"),o.push(`${n.kebabCase(r)}: ${s}`)):-1!=$.indexOf(r)?o.push(`${n.kebabCase(r)}: ${parseFloat(s)}`):o.push(`${n.kebabCase(r)}: ${s}`)}return o.join(";")},y=e=>{const t=e.toString();return{params:t.match(/\([^\(\)]*\)/)[0].slice(1,-1),content:t.slice(t.indexOf("{")+1,t.lastIndexOf("}"))}},g=(e,t)=>{if("string"==typeof e)return d(e)?t?e.slice(1,-1):e.slice(2,-2):t?e:`"${e}"`;if("function"==typeof e){const{params:t,content:n}=y(e);return`(${t}) => {${n}}`}return JSON.stringify(e)},b=e=>{const t=e.componentName.toLowerCase(),n=e.props&&e.props.className||"",o=n?` className="${n}"`:"";let s;n&&(a[n]=e.props.style);let i="";switch(Object.keys(e.props).forEach(t=>{-1===["className","style","text","src","lines"].indexOf(t)&&(i+=` ${t}={${g(e.props[t])}}`)}),t){case"text":const t=g(e.props.text,!0);s=`${t}`;break;case"image":const n=g(e.props.src);s=``;break;case"div":case"page":case"block":case"component":s=e.children&&e.children.length?`${N(e.children)}`:``;break;default:s=e.children&&e.children.length?`<${e.componentName}${o}${i}>${N(e.children)}`:`<${e.componentName}${o}${i} />`;const a=(e=>{if(!e)return;const t=componentsMap[e];if(t){const e=r.get(t.packageName);if(e)e.add(t);else{const e=new Set;e.add(t),r.set(t.packageName,e)}}else;})(e.componentName);a&&r.push(a)}var c,p;return e.loop&&(s=((e,t,n)=>{let o,r=t&&t[0]||"item",s=t&&t[1]||"index";Array.isArray(e)?o=f(e):d(e)&&(o=e.slice(2,-2));const a=n.match(/^<.+?\s/)[0].length;n=`${n.slice(0,a)} key={${s}}${n.slice(a)}`;const i=new RegExp("this."+r,"g");return`${o}.map((${r}, ${s}) => {\n return (${n=n.replace(i,r)});\n })`})(e.loop,e.loopArgs,s)),e.condition&&(c=e.condition,p=s,s="boolean"==typeof c?`${c} && ${p}`:"string"==typeof c?`${c.slice(2,-2)} && ${p}`:void 0),!e.loop&&!e.condition||e.isRoot||(s=`{${s}}`),s};let x=function(e,t){if(t.hasOwnProperty("children"))for(let n of t.children)e.push(n),x(e,n)};const j=function(e){let t="";for(let n of e.keyframes)t+=`\n ${(1e4*n.offset/100).toFixed(0)+"%"} {\n ${n.opacity?"opacity: ".concat(n.opacity)+";":""}\n ${n.transform?"transform: ".concat(n.transform)+";":""}\n }\n `;return`\n@keyframes ${e.name} {\n ${t}\n}\n`},N=e=>{let t="";if(Array.isArray(e))e.forEach(e=>{t+=N(e)});else if("string"==typeof e)t+=e;else if("object"==typeof e&&"string"==typeof e.componentName){let n=e.componentName.toLowerCase(),o=-1!==["block","component"].indexOf(n);if(l&&o&&(n="div",o=!1),"page"===n&&(l?n="div":(l=!0,o=!0)),o){const t=[],n=[],o=[],r=[],a=["render(){ return ("];let i=[`class ${e.componentName}_${c.length} extends Component {`];if(e.state&&t.push("state = "+f(e.state)),e.methods&&Object.keys(e.methods).forEach(t=>{const{params:n,content:r}=y(e.methods[t]);o.push(`${t}(${n}) {${r}}`)}),e.dataSource&&Array.isArray(e.dataSource.list)&&(e.dataSource.list.forEach(e=>{"boolean"==typeof e.isInit&&e.isInit?r.push(`this.${e.id}();`):"string"==typeof e.isInit&&r.push(`if (${g(e.isInit)}) { this.${e.id}(); }`),o.push((e=>{const t=e.id,{uri:n,method:o,params:r}=e.options,a=e.type;let i={};switch(a){case"fetch":-1===s.indexOf("import {fetch} from whatwg-fetch")&&s.push("import {fetch} from 'whatwg-fetch'"),i={method:o};break;case"jsonp":-1===s.indexOf("import {fetchJsonp} from fetch-jsonp")&&s.push("import jsonp from 'fetch-jsonp'")}Object.keys(e.options).forEach(t=>{-1===["uri","method","params"].indexOf(t)&&(i[t]=f(e.options[t]))}),i=r?`${f(i).slice(0,-1)} ,body: ${d(r)?g(r):f(r)}}`:f(i);let c=`{\n ${a}(${g(n)}, ${f(i)})\n .then((response) => response.json())\n `;if(e.dataHandler){const{params:t,content:n}=y(e.dataHandler);c+=`.then((${t}) => {${n}})\n .catch((e) => {\n console.log('error', e);\n })\n `}return c+="}",`${t}() ${c}`})(e))}),e.dataSource.dataHandler)){const{params:t,content:n}=y(e.dataSource.dataHandler);o.push(`dataHandler(${t}) {${n}}`),r.push("this.dataHandler()")}e.lifeCycles&&(e.lifeCycles._constructor||n.push(`constructor(props, context) { super(); ${r.join("\n")}}`),Object.keys(e.lifeCycles).forEach(t=>{const{params:o,content:s}=y(e.lifeCycles[t]);"_constructor"===t?n.push(`constructor(${o}) { super(); ${s} ${r.join("\n")}}`):n.push(`${t}(${o}) {${s}}`)})),a.push(b(e)),a.push(");}"),i=i.concat(t).concat(n).concat(o).concat(a),i.push("}"),c.push(i.join("\n"))}else t+=b(e)}return t};t.utils&&Object.keys(t.utils).forEach(e=>{i.push(`const ${e} = ${t.utils[e]}`)}),e.isRoot=!0;N(e);const O=function(e){let t="";e.animation&&(t+=j(e.animation));let n=[];x(n,e);for(let e of n)e.animation&&(t+=j(e.animation));return t}(e);let k=(()=>{const e=[],t=[];for(const[n,o]of r){const r=new Set,s=new Set;for(const e of o){let n=e.exportName,o=e.subName,a=e.name;e.subName&&t.push(`const ${a} = ${n}.${o};`),a===n||e.subName||(n=`${n} as ${a}`),e.dependence.destructuring?s.add(n):r.add(n)}const a=[...r].join(",");let i=[...s].join(",");const c=a&&i?",":"";i&&(i=`{${i}}`),e.push(`import ${a} ${c} ${i} from '${n}'`)}return e.concat(t)})();return k=k.concat(s),{panelDisplay:[{panelName:"index.jsx",panelValue:o.format(`\n 'use strict';\n\n import React, { Component } from 'react';\n ${k.join("\n")}\n import './style.css';\n\n ${i.join("\n")}\n ${c.join("\n")}\n export default ${e.componentName}_0;\n `,{parser:"babel",printWidth:120,singleQuote:!0}),panelType:"js"},{panelName:"style.css",panelValue:o.format(u(a),{parser:"css"})+O,panelType:"css"},{panelName:"style.responsive.css",panelValue:o.format(""+u(a,!0),{parser:"css"})+O,panelType:"css"}],noTemplate:!0}}}]); -------------------------------------------------------------------------------- /code/result.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React, { Component } from 'react'; 4 | 5 | import { fetch } from 'whatwg-fetch'; 6 | import jsonp from 'fetch-jsonp'; 7 | 8 | const print = function(value) { 9 | console.log(value); 10 | }; 11 | 12 | const styles = { 13 | box: { 14 | display: 'flex', 15 | flexDirection: 'row', 16 | justifyContent: 'space-around', 17 | alignItems: 'flex-start', 18 | height: '71.20vw' 19 | }, 20 | bd: { 21 | display: 'flex', 22 | position: 'relative', 23 | alignItems: 'flex-start', 24 | flexDirection: 'row', 25 | opacity: '1.00', 26 | width: '45.60vw', 27 | height: '45.60vw' 28 | }, 29 | layer: { 30 | position: 'absolute', 31 | top: '0.00vw', 32 | left: '0.00vw', 33 | width: '45.60vw', 34 | height: '45.60vw', 35 | overflow: 'hidden' 36 | }, 37 | bg: { position: 'absolute', top: '0.00vw', left: '0.00vw', opacity: '1.00', width: '45.60vw', height: '45.60vw' }, 38 | wrap: { 39 | boxSizing: 'border-box', 40 | display: 'flex', 41 | position: 'relative', 42 | alignItems: 'center', 43 | flexDirection: 'row', 44 | marginTop: '2.40vw', 45 | marginLeft: '2.40vw', 46 | borderRadius: '2.00vw', 47 | backgroundColor: 'rgba(0,0,0,0.40)', 48 | paddingRight: '1.20vw', 49 | paddingLeft: '1.33vw', 50 | height: '4.00vw' 51 | }, 52 | riverdinwei: { opacity: '1.00', width: '1.87vw', height: '2.40vw' }, 53 | distance: { 54 | marginLeft: '0.53vw', 55 | width: '11.20vw', 56 | height: '2.93vw', 57 | lineHeight: '2.93vw', 58 | whiteSpace: 'nowrap', 59 | color: '#ffffff', 60 | fontSize: '2.40vw', 61 | fontWeight: 400, 62 | lines: 1 63 | }, 64 | main: { 65 | display: 'flex', 66 | alignItems: 'flex-start', 67 | flexDirection: 'row', 68 | justifyContent: 'center', 69 | backgroundColor: '#ffffff', 70 | width: '45.60vw', 71 | height: '15.20vw' 72 | }, 73 | title: { 74 | marginTop: '2.93vw', 75 | width: '40.00vw', 76 | height: '11.73vw', 77 | overflow: 'hidden', 78 | textOverflow: 'ellipsis', 79 | lineHeight: '5.87vw', 80 | color: '#333333', 81 | fontSize: '4.00vw', 82 | fontWeight: 400, 83 | lines: 2 84 | }, 85 | ft: { 86 | boxSizing: 'border-box', 87 | display: 'flex', 88 | alignItems: 'center', 89 | flexDirection: 'row', 90 | justifyContent: 'space-between', 91 | borderBottomLeftRadius: '1.60vw', 92 | borderBottomRightRadius: '1.60vw', 93 | backgroundColor: '#ffffff', 94 | paddingRight: '2.27vw', 95 | paddingLeft: '2.40vw', 96 | width: '45.60vw', 97 | height: '10.40vw', 98 | overflow: 'hidden' 99 | }, 100 | block: { display: 'flex', alignItems: 'center', flexDirection: 'row', height: '4.00vw' }, 101 | xianjin: { width: '4.00vw', height: '4.00vw' }, 102 | fashionHome: { 103 | marginLeft: '0.80vw', 104 | width: '12.80vw', 105 | height: '3.73vw', 106 | lineHeight: '3.73vw', 107 | whiteSpace: 'nowrap', 108 | color: '#666666', 109 | fontSize: '3.20vw', 110 | fontWeight: 300, 111 | lines: 1 112 | }, 113 | group: { display: 'flex', alignItems: 'center', flexDirection: 'row', height: '4.00vw' }, 114 | favorite: { width: '2.93vw', height: '2.93vw' }, 115 | num: { 116 | marginLeft: '0.67vw', 117 | width: '4.80vw', 118 | height: '3.47vw', 119 | lineHeight: '3.47vw', 120 | whiteSpace: 'nowrap', 121 | color: '#999999', 122 | fontSize: '2.93vw', 123 | fontWeight: 400, 124 | lines: 1 125 | } 126 | }; 127 | 128 | class Page_0 extends Component { 129 | state = { 130 | data: [ 131 | { 132 | title: '小户型卫浴怎样才能装得高大上?', 133 | coverImage: 'https://img.alicdn.com/tfs/TB1Txq6o7T2gK0jSZFkXXcIQFXa-684-684.png', 134 | readCount: 200, 135 | user: { userImage: 'https://img.alicdn.com/tfs/TB1DWe6oYj1gK0jSZFOXXc7GpXa-60-60.png', userName: '时尚家居' }, 136 | url: 'https://www.imgcook.com' 137 | }, 138 | { 139 | title: '拥有超多功能的40平米简约小公寓了解一下', 140 | coverImage: 'https://img.alicdn.com/tfs/TB1XRQTo7P2gK0jSZPxXXacQpXa-684-648.png', 141 | readCount: 500, 142 | user: { 143 | userImage: 'https://img.alicdn.com/tfs/TB1DWe6oYj1gK0jSZFOXXc7GpXa-60-60.png', 144 | userName: '花花设计工作' 145 | }, 146 | url: 'https://www.imgcook.com/docs' 147 | } 148 | ] 149 | }; 150 | constructor(props, context) { 151 | console.log('super props'); 152 | this.fetch_example(); 153 | this.jsonp_example(); 154 | } 155 | componentDidUpdate(prevProps, prevState, snapshot) {} 156 | isReadCountShow(readCount) { 157 | return readCount > 300; 158 | } 159 | fetch_example() { 160 | fetch('https://jsonplaceholder.typicode.com/todos/1', { method: 'GET', headers: '{"Content-Type":"json"}' }) 161 | .then(response => response.json()) 162 | .then((data, error) => { 163 | console.log('fetch example: ', data, error); 164 | return data; 165 | }) 166 | .catch(e => { 167 | console.log('error', e); 168 | }); 169 | } 170 | jsonp_example() { 171 | jsonp('https://assets.airbnb.com/frontend/search_results.js', { jsonpCallbackFunction: 'search_results', body: {} }) 172 | .then(response => response.json()) 173 | .then((data, error) => { 174 | console.log('jsonp example: ', data, error); 175 | return data; 176 | }) 177 | .catch(e => { 178 | console.log('error', e); 179 | }); 180 | } 181 | render() { 182 | return ( 183 |
184 | {this.state.data.map((item, index) => { 185 | return ( 186 |
{ 189 | window.open(item.url, '_blank'); 190 | }} 191 | data-url={item.url} 192 | key={item.index} 193 | > 194 |
195 | 196 | 197 |
198 | 202 | 距离500m 203 |
204 |
205 |
206 | {item.title} 207 |
208 |
209 |
210 | 214 | {item.user.userName} 215 |
216 | {this.isReadCountShow(item.readCount) && ( 217 |
218 | 222 | {item.readCount} 223 |
224 | )} 225 |
226 |
227 | ); 228 | })} 229 |
230 | ); 231 | } 232 | } 233 | 234 | export default Page_0; 235 | -------------------------------------------------------------------------------- /test/react.test.js: -------------------------------------------------------------------------------- 1 | const { expect, assert } = require('chai'); 2 | const { runCode } = require('./index'); 3 | const data = require('./data'); 4 | const _ = require('lodash'); 5 | 6 | 7 | 8 | describe('componentStyle 组件格式', () => { 9 | 10 | 11 | it('componentStyle = hooks 时,使用hooks出码方式', async () => { 12 | const result = runCode(data, { globalCss: true, componentStyle: 'hooks'}); 13 | const jsFile = _.find(result, { panelName:'index.jsx'}); 14 | expect(jsFile).to.not.be.equal(undefined); 15 | expect(jsFile.panelValue.includes('useState')).to.be.equal(true); 16 | expect(jsFile.panelValue.includes('memo')).to.be.equal(true); 17 | expect(jsFile.panelValue.includes('Component')).to.be.equal(false); 18 | }); 19 | 20 | it('componentStyle = component 时,使用 component 出码方式', async () => { 21 | const result = runCode(data, { globalCss: true, componentStyle: 'component'}); 22 | const jsFile = _.find(result, { panelName:'index.jsx'}); 23 | expect(jsFile).to.not.be.equal(undefined); 24 | expect(jsFile.panelValue.includes('useState')).to.be.equal(false); 25 | expect(jsFile.panelValue.includes('memo')).to.be.equal(false); 26 | expect(jsFile.panelValue.includes('Component')).to.be.equal(true); 27 | }); 28 | 29 | }); 30 | 31 | describe('globalCss 全局样式', () => { 32 | it('globalCss = true 时,有 global.css 文件', async () => { 33 | const result = runCode(data, { globalCss: true, outputStyle: 'project'}); 34 | expect(_.find(result, { panelName:'global.css'})).to.not.be.equal(undefined); 35 | }); 36 | 37 | it('globalCss = false 时,无 global.css 文件', async () => { 38 | const result = runCode(data, { globalCss: false, outputStyle: 'project'}); 39 | expect(_.find(result, { panelName:'global.css'})).to.be.equal(undefined); 40 | }); 41 | }); 42 | 43 | 44 | describe('outputStyle 风格参数', () => { 45 | it('outputStyle = project 时, 有 package.json文件', async () => { 46 | const result = runCode(data, { globalCss: true, jsx: 'javascript', outputStyle: 'project'}); 47 | expect(_.find(result, { panelName:'package.json'})).to.not.be.equal(undefined); 48 | expect(_.find(result, { panelName:'index.html'})).to.not.be.equal(undefined); 49 | expect(_.find(result, { panelName:'index.jsx'})).to.not.be.equal(undefined); 50 | expect(_.find(result, { panelName:'index.css'})).to.not.be.equal(undefined); 51 | }); 52 | 53 | it('outputStyle = component 时, 无 package.json文件', async () => { 54 | const result = runCode(data, { globalCss: true, outputStyle: 'component'}); 55 | expect(_.find(result, { panelName:'package.json'})).to.be.equal(undefined); 56 | }); 57 | }); 58 | 59 | describe('jsx 支持TS', () => { 60 | it(`jsx = javascript 时`, async () => { 61 | const schema = _.cloneDeep(data); 62 | const result = runCode(schema, { inlineStyle: 'import', jsx: 'javascript'}); 63 | const file = _.find(result, { panelType:'tsx'}); 64 | expect(file).to.be.equal(undefined); 65 | }); 66 | 67 | it(`jsx = typescript 时`, async () => { 68 | const schema = _.cloneDeep(data); 69 | const result = runCode(schema, { inlineStyle: 'import', jsx: 'typescript'}); 70 | const file = _.find(result, { panelType:'jsx'}); 71 | expect(file).to.be.equal(undefined); 72 | }); 73 | }); 74 | describe('cssStyle 样式名风格', () => { 75 | it(`cssStyle = kebabCase 时,中划线命名`, async () => { 76 | const schema = _.cloneDeep(data); 77 | schema.componentName = 'Block'; 78 | schema.fileName = 'BlockDemo'; 79 | schema.props.className = "class-name-01" 80 | const result = runCode(schema, { inlineStyle: 'import', cssStyle: 'kebabCase'}); 81 | const file = _.find(result, { panelName:'index.css', folder: 'components/BlockDemo'}); 82 | expect(file.panelValue.includes(`.class-name-01`)).to.be.equal(true); 83 | }); 84 | 85 | it(`cssStyle = snakeCase 时,下划线命名`, async () => { 86 | const schema = _.cloneDeep(data); 87 | schema.componentName = 'Block'; 88 | schema.fileName = 'BlockDemo'; 89 | schema.props.className = "class-name-01" 90 | const result = runCode(schema, { inlineStyle: 'import', cssStyle: 'snakeCase'}); 91 | const file = _.find(result, { panelName:'index.css', folder: 'components/BlockDemo'}); 92 | 93 | expect(file.panelValue.includes(`.class_name_01`)).to.be.equal(true); 94 | }); 95 | 96 | it(`cssStyle = camelCase 时,驼峰式命名`, async () => { 97 | const schema = _.cloneDeep(data); 98 | schema.componentName = 'Block'; 99 | schema.fileName = 'BlockDemo'; 100 | schema.props.className = "class-name-01" 101 | const result = runCode(schema, { inlineStyle: 'import', cssStyle: 'camelCase'}); 102 | const file = _.find(result, { panelName:'index.css', folder: 'components/BlockDemo'}); 103 | expect(file.panelValue.includes(`.className01`)).to.be.equal(true); 104 | }); 105 | 106 | 107 | }) 108 | 109 | describe('inlineStyle 样式引入方式', () => { 110 | it(`inlineStyle = import 时,包含 import './index.css';`, async () => { 111 | const schema = _.cloneDeep(data); 112 | schema.componentName = 'Block'; 113 | schema.fileName = 'BlockDemo'; 114 | const result = runCode(schema, { inlineStyle: 'import',}); 115 | const file = _.find(result, { panelName:'index.jsx', folder: 'components/BlockDemo'}); 116 | expect(file.panelValue.includes(`import './index.css';`)).to.be.equal(true); 117 | }); 118 | 119 | it(`inlineStyle = module 时,包含 import styles from './index.module.css'`, async () => { 120 | const schema = _.cloneDeep(data); 121 | schema.componentName = 'Block'; 122 | schema.fileName = 'BlockDemo'; 123 | const result = runCode(schema, { inlineStyle: 'module',}); 124 | const file = _.find(result, { panelName:'index.jsx', folder: 'components/BlockDemo'}); 125 | expect(file.panelValue.includes(`import styles from './index.module.css';`)).to.be.equal(true); 126 | }); 127 | 128 | it(`inlineStyle = inline 时,不引入css文件`, async () => { 129 | const schema = _.cloneDeep(data); 130 | schema.componentName = 'Block'; 131 | schema.fileName = 'BlockDemo'; 132 | const result = runCode(schema, { inlineStyle: 'inline',}); 133 | const file = _.find(result, { panelName:'index.jsx', folder: 'components/BlockDemo'}); 134 | expect(file.panelValue.includes(`'./index.css';`)).to.be.equal(false); 135 | }); 136 | 137 | }); 138 | 139 | describe('cssUnit 单位设置', () => { 140 | it('cssUnit = px', async () => { 141 | const schema = _.cloneDeep(data); 142 | schema.componentName = 'Block'; 143 | schema.fileName = 'BlockDemo'; 144 | const result = runCode(schema, { inlineStyle: 'import', cssUnit: 'px'}); 145 | const cssFile = _.find(result, { panelName:'index.css', folder: 'components/BlockDemo'}); 146 | expect(cssFile.panelValue.includes('px')).to.be.equal(true); 147 | expect(cssFile.panelValue.includes('vw')).to.be.equal(false); 148 | expect(cssFile.panelValue.includes('rem')).to.be.equal(false); 149 | }); 150 | 151 | it('cssUnit = vw', async () => { 152 | const schema = _.cloneDeep(data); 153 | schema.componentName = 'Block'; 154 | schema.fileName = 'BlockDemo'; 155 | const result = runCode(schema, { inlineStyle: 'import', cssUnit: 'vw'}); 156 | const cssFile = _.find(result, { panelName:'index.css', folder: 'components/BlockDemo'}); 157 | expect(cssFile.panelValue.includes('px')).to.be.equal(false); 158 | expect(cssFile.panelValue.includes('vw')).to.be.equal(true); 159 | expect(cssFile.panelValue.includes('rem')).to.be.equal(false); 160 | }); 161 | 162 | it('cssUnit = rem', async () => { 163 | const schema = _.cloneDeep(data); 164 | schema.componentName = 'Block'; 165 | schema.fileName = 'BlockDemo'; 166 | const result = runCode(schema, { inlineStyle: 'import', cssUnit: 'rem'}); 167 | const cssFile = _.find(result, { panelName:'index.css', folder: 'components/BlockDemo'}); 168 | expect(cssFile.panelValue.includes('px')).to.be.equal(false); 169 | expect(cssFile.panelValue.includes('vw')).to.be.equal(false); 170 | expect(cssFile.panelValue.includes('rem')).to.be.equal(true); 171 | }); 172 | 173 | 174 | 175 | 176 | }); 177 | 178 | 179 | -------------------------------------------------------------------------------- /code/style.css: -------------------------------------------------------------------------------- 1 | .mod { 2 | display: flex; 3 | flex-direction: row; 4 | align-items: flex-start; 5 | width: 565px; 6 | height: 623px; 7 | position: relative; 8 | animation-iteration-count: 1; 9 | animation-timing-function: linear; 10 | animation-duration: 1280ms; 11 | animation-name: swing; 12 | } 13 | .primary { 14 | display: flex; 15 | position: relative; 16 | align-items: flex-start; 17 | flex-direction: row; 18 | justify-content: center; 19 | margin-left: 94px; 20 | border-top-left-radius: 18px; 21 | border-top-right-radius: 18px; 22 | background-image: linear-gradient(148deg, #efeded 0%, #b4b4b4 76%); 23 | width: 438px; 24 | height: 461px; 25 | } 26 | .entry-pic { 27 | position: absolute; 28 | bottom: 12px; 29 | left: -14px; 30 | width: 405px; 31 | height: 310px; 32 | } 33 | .action-bg { 34 | position: relative; 35 | margin-top: 13px; 36 | margin-right: 87px; 37 | width: 201px; 38 | height: 76px; 39 | } 40 | .shop-logo { 41 | position: relative; 42 | margin-top: 20px; 43 | width: 106px; 44 | height: 52px; 45 | } 46 | .empty { 47 | position: absolute; 48 | top: 140px; 49 | right: 28px; 50 | width: 437px; 51 | height: 347px; 52 | overflow: hidden; 53 | } 54 | .item-long { 55 | position: absolute; 56 | top: 194px; 57 | left: 113px; 58 | animation-iteration-count: infinite; 59 | animation-timing-function: linear; 60 | animation-duration: 1280ms; 61 | animation-name: swing; 62 | width: 197px; 63 | height: 340px; 64 | } 65 | .product-long { 66 | position: absolute; 67 | top: 157px; 68 | left: 172px; 69 | animation-iteration-count: infinite; 70 | animation-timing-function: ease; 71 | animation-duration: 1280ms; 72 | animation-name: swing; 73 | width: 182px; 74 | height: 354px; 75 | } 76 | .item-long-1 { 77 | position: absolute; 78 | top: 154px; 79 | right: 173px; 80 | animation-iteration-count: infinite; 81 | animation-timing-function: ease; 82 | animation-duration: 1280ms; 83 | animation-name: swing; 84 | width: 179px; 85 | height: 388px; 86 | } 87 | .product-long-1 { 88 | position: absolute; 89 | right: 122px; 90 | bottom: 61px; 91 | animation-iteration-count: infinite; 92 | animation-timing-function: linear; 93 | animation-duration: 1280ms; 94 | animation-name: swing; 95 | width: 230px; 96 | height: 406px; 97 | } 98 | .item-long-2 { 99 | position: absolute; 100 | right: 56px; 101 | bottom: 37px; 102 | animation-iteration-count: infinite; 103 | animation-timing-function: ease; 104 | animation-duration: 1280ms; 105 | animation-name: swing; 106 | width: 302px; 107 | height: 417px; 108 | } 109 | .entry-pic-1 { 110 | position: absolute; 111 | right: 0; 112 | bottom: 20px; 113 | animation-iteration-count: infinite; 114 | animation-timing-function: linear; 115 | animation-duration: 1280ms; 116 | animation-name: swing; 117 | width: 392px; 118 | height: 251px; 119 | } 120 | .layer-wrapper { 121 | display: flex; 122 | position: absolute; 123 | top: 139px; 124 | right: 18px; 125 | align-items: center; 126 | flex-direction: row; 127 | width: 466px; 128 | height: 338px; 129 | } 130 | .layer { 131 | position: absolute; 132 | top: 0; 133 | left: 0; 134 | width: 466px; 135 | height: 338px; 136 | overflow: hidden; 137 | } 138 | .vertical-line { 139 | position: absolute; 140 | top: 1px; 141 | right: -2px; 142 | width: 3px; 143 | height: 309px; 144 | } 145 | .bg { 146 | position: absolute; 147 | top: 138px; 148 | left: 0; 149 | width: 319px; 150 | height: 3px; 151 | } 152 | .product-long-2 { 153 | position: absolute; 154 | top: 139px; 155 | left: 81px; 156 | width: 268px; 157 | height: 378px; 158 | overflow: hidden; 159 | } 160 | .background { 161 | position: absolute; 162 | top: 102px; 163 | right: 18px; 164 | width: 466px; 165 | height: 39px; 166 | } 167 | .wrapper { 168 | display: flex; 169 | position: absolute; 170 | right: 11px; 171 | bottom: 0; 172 | align-items: flex-start; 173 | flex-direction: row; 174 | } 175 | .wrapper-inner { 176 | display: flex; 177 | position: relative; 178 | align-items: center; 179 | flex-direction: row; 180 | margin-top: 17px; 181 | margin-left: 46px; 182 | background-size: contain; 183 | background-image: url(https://img.alicdn.com/imgextra/i2/O1CN01ofNLoD1YmIuIHhBl1_!!6000000003101-2-tps-964-380.png); 184 | height: 190px; 185 | } 186 | .group { 187 | display: flex; 188 | position: relative; 189 | align-items: flex-start; 190 | flex-direction: row; 191 | justify-content: center; 192 | background-color: #141414; 193 | width: 482px; 194 | height: 176px; 195 | overflow: hidden; 196 | } 197 | .entry-pic-2 { 198 | position: absolute; 199 | right: 12px; 200 | bottom: 0; 201 | width: 174px; 202 | height: 119px; 203 | } 204 | .view { 205 | display: flex; 206 | flex-direction: row; 207 | justify-content: center; 208 | align-items: center; 209 | margin-top: 2px; 210 | width: 159px; 211 | height: 40px; 212 | line-height: 26px; 213 | position: absolute; 214 | top: 110px; 215 | left: 29px; 216 | z-index: 10; 217 | background-image: url(https://img.alicdn.com/imgextra/i2/O1CN01A6AUU31FroypE1Fw8_!!6000000000541-2-tps-318-80.png); 218 | background-size: contain; 219 | white-space: nowrap; 220 | } 221 | .title { 222 | margin-top: -2px; 223 | color: #141414; 224 | font-family: PingFang SC; 225 | font-size: 26px; 226 | font-weight: 400; 227 | } 228 | .num { 229 | margin-top: -2px; 230 | color: #141414; 231 | font-family: PingFang SC; 232 | font-size: 26px; 233 | font-weight: 600; 234 | } 235 | .caption { 236 | margin-top: -2px; 237 | color: #141414; 238 | font-family: PingFang SC; 239 | font-size: 26px; 240 | font-weight: 400; 241 | } 242 | .bg-1 { 243 | position: relative; 244 | margin-top: 17px; 245 | width: 367px; 246 | height: 32px; 247 | } 248 | .entry-pic-3 { 249 | position: absolute; 250 | top: 0; 251 | left: 0; 252 | width: 174px; 253 | height: 119px; 254 | } 255 | .wrapper-inner-1 { 256 | display: flex; 257 | position: absolute; 258 | top: 90px; 259 | right: 39px; 260 | align-items: flex-start; 261 | flex-direction: column; 262 | border-width: 2px; 263 | border-style: solid; 264 | border-radius: 47px; 265 | border-color: rgba(0, 0, 0, 0.34); 266 | background-color: rgba(255, 255, 255, 0.1); 267 | width: 405px; 268 | height: 94px; 269 | } 270 | .button-bg-wrapper { 271 | display: flex; 272 | position: absolute; 273 | top: 10px; 274 | align-items: center; 275 | align-self: center; 276 | flex-direction: row; 277 | width: 382px; 278 | height: 74px; 279 | } 280 | .button-bg { 281 | position: absolute; 282 | top: 0; 283 | left: 0; 284 | width: 382px; 285 | height: 74px; 286 | overflow: hidden; 287 | } 288 | .background-1 { 289 | position: absolute; 290 | top: 0; 291 | left: 0; 292 | width: 382px; 293 | height: 49px; 294 | overflow: hidden; 295 | } 296 | .large-icon { 297 | position: absolute; 298 | top: 16px; 299 | right: 150px; 300 | width: 79px; 301 | height: 38px; 302 | } 303 | 304 | @keyframes swing { 305 | 306 | 20% { 307 | 308 | transform: rotate3d(0, 0, 1, 15deg); 309 | } 310 | 311 | 40% { 312 | 313 | transform: rotate3d(0, 0, 1, -10deg); 314 | } 315 | 316 | 60% { 317 | 318 | transform: rotate3d(0, 0, 1, 5deg); 319 | } 320 | 321 | 80% { 322 | 323 | transform: rotate3d(0, 0, 1, -5deg); 324 | } 325 | 326 | 100% { 327 | 328 | transform: rotate3d(0, 0, 1, 0deg); 329 | } 330 | 331 | } 332 | 333 | @keyframes swing { 334 | 335 | 20% { 336 | 337 | transform: rotate3d(0, 0, 1, 15deg); 338 | } 339 | 340 | 40% { 341 | 342 | transform: rotate3d(0, 0, 1, -10deg); 343 | } 344 | 345 | 60% { 346 | 347 | transform: rotate3d(0, 0, 1, 5deg); 348 | } 349 | 350 | 80% { 351 | 352 | transform: rotate3d(0, 0, 1, -5deg); 353 | } 354 | 355 | 100% { 356 | 357 | transform: rotate3d(0, 0, 1, 0deg); 358 | } 359 | 360 | } 361 | 362 | @keyframes swing { 363 | 364 | 20% { 365 | 366 | transform: rotate3d(0, 0, 1, 15deg); 367 | } 368 | 369 | 40% { 370 | 371 | transform: rotate3d(0, 0, 1, -10deg); 372 | } 373 | 374 | 60% { 375 | 376 | transform: rotate3d(0, 0, 1, 5deg); 377 | } 378 | 379 | 80% { 380 | 381 | transform: rotate3d(0, 0, 1, -5deg); 382 | } 383 | 384 | 100% { 385 | 386 | transform: rotate3d(0, 0, 1, 0deg); 387 | } 388 | 389 | } 390 | 391 | @keyframes swing { 392 | 393 | 20% { 394 | 395 | transform: rotate3d(0, 0, 1, 15deg); 396 | } 397 | 398 | 40% { 399 | 400 | transform: rotate3d(0, 0, 1, -10deg); 401 | } 402 | 403 | 60% { 404 | 405 | transform: rotate3d(0, 0, 1, 5deg); 406 | } 407 | 408 | 80% { 409 | 410 | transform: rotate3d(0, 0, 1, -5deg); 411 | } 412 | 413 | 100% { 414 | 415 | transform: rotate3d(0, 0, 1, 0deg); 416 | } 417 | 418 | } 419 | 420 | @keyframes swing { 421 | 422 | 20% { 423 | 424 | transform: rotate3d(0, 0, 1, 15deg); 425 | } 426 | 427 | 40% { 428 | 429 | transform: rotate3d(0, 0, 1, -10deg); 430 | } 431 | 432 | 60% { 433 | 434 | transform: rotate3d(0, 0, 1, 5deg); 435 | } 436 | 437 | 80% { 438 | 439 | transform: rotate3d(0, 0, 1, -5deg); 440 | } 441 | 442 | 100% { 443 | 444 | transform: rotate3d(0, 0, 1, 0deg); 445 | } 446 | 447 | } 448 | 449 | @keyframes swing { 450 | 451 | 20% { 452 | 453 | transform: rotate3d(0, 0, 1, 15deg); 454 | } 455 | 456 | 40% { 457 | 458 | transform: rotate3d(0, 0, 1, -10deg); 459 | } 460 | 461 | 60% { 462 | 463 | transform: rotate3d(0, 0, 1, 5deg); 464 | } 465 | 466 | 80% { 467 | 468 | transform: rotate3d(0, 0, 1, -5deg); 469 | } 470 | 471 | 100% { 472 | 473 | transform: rotate3d(0, 0, 1, 0deg); 474 | } 475 | 476 | } 477 | 478 | @keyframes swing { 479 | 480 | 20% { 481 | 482 | transform: rotate3d(0, 0, 1, 15deg); 483 | } 484 | 485 | 40% { 486 | 487 | transform: rotate3d(0, 0, 1, -10deg); 488 | } 489 | 490 | 60% { 491 | 492 | transform: rotate3d(0, 0, 1, 5deg); 493 | } 494 | 495 | 80% { 496 | 497 | transform: rotate3d(0, 0, 1, -5deg); 498 | } 499 | 500 | 100% { 501 | 502 | transform: rotate3d(0, 0, 1, 0deg); 503 | } 504 | 505 | } 506 | -------------------------------------------------------------------------------- /code/style.responsive.css: -------------------------------------------------------------------------------- 1 | .mod { 2 | display: flex; 3 | flex-direction: row; 4 | align-items: flex-start; 5 | width: 75.33vw; 6 | height: 83.07vw; 7 | position: relative; 8 | animation-iteration-count: 1; 9 | animation-timing-function: linear; 10 | animation-duration: 1280ms; 11 | animation-name: swing; 12 | } 13 | .primary { 14 | display: flex; 15 | position: relative; 16 | align-items: flex-start; 17 | flex-direction: row; 18 | justify-content: center; 19 | margin-left: 12.53vw; 20 | border-top-left-radius: 2.4vw; 21 | border-top-right-radius: 2.4vw; 22 | background-image: linear-gradient(148deg, #efeded 0%, #b4b4b4 76%); 23 | width: 58.4vw; 24 | height: 61.47vw; 25 | } 26 | .entry-pic { 27 | position: absolute; 28 | bottom: 1.6vw; 29 | left: -1.87vw; 30 | width: 54vw; 31 | height: 41.33vw; 32 | } 33 | .action-bg { 34 | position: relative; 35 | margin-top: 1.73vw; 36 | margin-right: 11.6vw; 37 | width: 26.8vw; 38 | height: 10.13vw; 39 | } 40 | .shop-logo { 41 | position: relative; 42 | margin-top: 2.67vw; 43 | width: 14.13vw; 44 | height: 6.93vw; 45 | } 46 | .empty { 47 | position: absolute; 48 | top: 18.67vw; 49 | right: 3.73vw; 50 | width: 58.27vw; 51 | height: 46.27vw; 52 | overflow: hidden; 53 | } 54 | .item-long { 55 | position: absolute; 56 | top: 25.87vw; 57 | left: 15.07vw; 58 | animation-iteration-count: infinite; 59 | animation-timing-function: linear; 60 | animation-duration: 1280ms; 61 | animation-name: swing; 62 | width: 26.27vw; 63 | height: 45.33vw; 64 | } 65 | .product-long { 66 | position: absolute; 67 | top: 20.93vw; 68 | left: 22.93vw; 69 | animation-iteration-count: infinite; 70 | animation-timing-function: ease; 71 | animation-duration: 1280ms; 72 | animation-name: swing; 73 | width: 24.27vw; 74 | height: 47.2vw; 75 | } 76 | .item-long-1 { 77 | position: absolute; 78 | top: 20.53vw; 79 | right: 23.07vw; 80 | animation-iteration-count: infinite; 81 | animation-timing-function: ease; 82 | animation-duration: 1280ms; 83 | animation-name: swing; 84 | width: 23.87vw; 85 | height: 51.73vw; 86 | } 87 | .product-long-1 { 88 | position: absolute; 89 | right: 16.27vw; 90 | bottom: 8.13vw; 91 | animation-iteration-count: infinite; 92 | animation-timing-function: linear; 93 | animation-duration: 1280ms; 94 | animation-name: swing; 95 | width: 30.67vw; 96 | height: 54.13vw; 97 | } 98 | .item-long-2 { 99 | position: absolute; 100 | right: 7.47vw; 101 | bottom: 4.93vw; 102 | animation-iteration-count: infinite; 103 | animation-timing-function: ease; 104 | animation-duration: 1280ms; 105 | animation-name: swing; 106 | width: 40.27vw; 107 | height: 55.6vw; 108 | } 109 | .entry-pic-1 { 110 | position: absolute; 111 | right: 0; 112 | bottom: 2.67vw; 113 | animation-iteration-count: infinite; 114 | animation-timing-function: linear; 115 | animation-duration: 1280ms; 116 | animation-name: swing; 117 | width: 52.27vw; 118 | height: 33.47vw; 119 | } 120 | .layer-wrapper { 121 | display: flex; 122 | position: absolute; 123 | top: 18.53vw; 124 | right: 2.4vw; 125 | align-items: center; 126 | flex-direction: row; 127 | width: 62.13vw; 128 | height: 45.07vw; 129 | } 130 | .layer { 131 | position: absolute; 132 | top: 0; 133 | left: 0; 134 | width: 62.13vw; 135 | height: 45.07vw; 136 | overflow: hidden; 137 | } 138 | .vertical-line { 139 | position: absolute; 140 | top: 0.13vw; 141 | right: -0.27vw; 142 | width: 0.4vw; 143 | height: 41.2vw; 144 | } 145 | .bg { 146 | position: absolute; 147 | top: 18.4vw; 148 | left: 0; 149 | width: 42.53vw; 150 | height: 0.4vw; 151 | } 152 | .product-long-2 { 153 | position: absolute; 154 | top: 18.53vw; 155 | left: 10.8vw; 156 | width: 35.73vw; 157 | height: 50.4vw; 158 | overflow: hidden; 159 | } 160 | .background { 161 | position: absolute; 162 | top: 13.6vw; 163 | right: 2.4vw; 164 | width: 62.13vw; 165 | height: 5.2vw; 166 | } 167 | .wrapper { 168 | display: flex; 169 | position: absolute; 170 | right: 1.47vw; 171 | bottom: 0; 172 | align-items: flex-start; 173 | flex-direction: row; 174 | } 175 | .wrapper-inner { 176 | display: flex; 177 | position: relative; 178 | align-items: center; 179 | flex-direction: row; 180 | margin-top: 2.27vw; 181 | margin-left: 6.13vw; 182 | background-size: contain; 183 | background-image: url(https://img.alicdn.com/imgextra/i2/O1CN01ofNLoD1YmIuIHhBl1_!!6000000003101-2-tps-964-380.png); 184 | height: 25.33vw; 185 | } 186 | .group { 187 | display: flex; 188 | position: relative; 189 | align-items: flex-start; 190 | flex-direction: row; 191 | justify-content: center; 192 | background-color: #141414; 193 | width: 64.27vw; 194 | height: 23.47vw; 195 | overflow: hidden; 196 | } 197 | .entry-pic-2 { 198 | position: absolute; 199 | right: 1.6vw; 200 | bottom: 0; 201 | width: 23.2vw; 202 | height: 15.87vw; 203 | } 204 | .view { 205 | display: flex; 206 | flex-direction: row; 207 | justify-content: center; 208 | align-items: center; 209 | margin-top: 0.27vw; 210 | width: 21.2vw; 211 | height: 5.33vw; 212 | line-height: 3.47vw; 213 | position: absolute; 214 | top: 14.67vw; 215 | left: 3.87vw; 216 | z-index: 10; 217 | background-image: url(https://img.alicdn.com/imgextra/i2/O1CN01A6AUU31FroypE1Fw8_!!6000000000541-2-tps-318-80.png); 218 | background-size: contain; 219 | white-space: nowrap; 220 | } 221 | .title { 222 | margin-top: -0.27vw; 223 | color: #141414; 224 | font-family: PingFang SC; 225 | font-size: 3.47vw; 226 | font-weight: 400; 227 | } 228 | .num { 229 | margin-top: -0.27vw; 230 | color: #141414; 231 | font-family: PingFang SC; 232 | font-size: 3.47vw; 233 | font-weight: 600; 234 | } 235 | .caption { 236 | margin-top: -0.27vw; 237 | color: #141414; 238 | font-family: PingFang SC; 239 | font-size: 3.47vw; 240 | font-weight: 400; 241 | } 242 | .bg-1 { 243 | position: relative; 244 | margin-top: 2.27vw; 245 | width: 48.93vw; 246 | height: 4.27vw; 247 | } 248 | .entry-pic-3 { 249 | position: absolute; 250 | top: 0; 251 | left: 0; 252 | width: 23.2vw; 253 | height: 15.87vw; 254 | } 255 | .wrapper-inner-1 { 256 | display: flex; 257 | position: absolute; 258 | top: 12vw; 259 | right: 5.2vw; 260 | align-items: flex-start; 261 | flex-direction: column; 262 | border-width: 2px; 263 | border-style: solid; 264 | border-radius: 6.27vw; 265 | border-color: rgba(0, 0, 0, 0.34); 266 | background-color: rgba(255, 255, 255, 0.1); 267 | width: 54vw; 268 | height: 12.53vw; 269 | } 270 | .button-bg-wrapper { 271 | display: flex; 272 | position: absolute; 273 | top: 1.33vw; 274 | align-items: center; 275 | align-self: center; 276 | flex-direction: row; 277 | width: 50.93vw; 278 | height: 9.87vw; 279 | } 280 | .button-bg { 281 | position: absolute; 282 | top: 0; 283 | left: 0; 284 | width: 50.93vw; 285 | height: 9.87vw; 286 | overflow: hidden; 287 | } 288 | .background-1 { 289 | position: absolute; 290 | top: 0; 291 | left: 0; 292 | width: 50.93vw; 293 | height: 6.53vw; 294 | overflow: hidden; 295 | } 296 | .large-icon { 297 | position: absolute; 298 | top: 2.13vw; 299 | right: 20vw; 300 | width: 10.53vw; 301 | height: 5.07vw; 302 | } 303 | 304 | @keyframes swing { 305 | 306 | 20% { 307 | 308 | transform: rotate3d(0, 0, 1, 15deg); 309 | } 310 | 311 | 40% { 312 | 313 | transform: rotate3d(0, 0, 1, -10deg); 314 | } 315 | 316 | 60% { 317 | 318 | transform: rotate3d(0, 0, 1, 5deg); 319 | } 320 | 321 | 80% { 322 | 323 | transform: rotate3d(0, 0, 1, -5deg); 324 | } 325 | 326 | 100% { 327 | 328 | transform: rotate3d(0, 0, 1, 0deg); 329 | } 330 | 331 | } 332 | 333 | @keyframes swing { 334 | 335 | 20% { 336 | 337 | transform: rotate3d(0, 0, 1, 15deg); 338 | } 339 | 340 | 40% { 341 | 342 | transform: rotate3d(0, 0, 1, -10deg); 343 | } 344 | 345 | 60% { 346 | 347 | transform: rotate3d(0, 0, 1, 5deg); 348 | } 349 | 350 | 80% { 351 | 352 | transform: rotate3d(0, 0, 1, -5deg); 353 | } 354 | 355 | 100% { 356 | 357 | transform: rotate3d(0, 0, 1, 0deg); 358 | } 359 | 360 | } 361 | 362 | @keyframes swing { 363 | 364 | 20% { 365 | 366 | transform: rotate3d(0, 0, 1, 15deg); 367 | } 368 | 369 | 40% { 370 | 371 | transform: rotate3d(0, 0, 1, -10deg); 372 | } 373 | 374 | 60% { 375 | 376 | transform: rotate3d(0, 0, 1, 5deg); 377 | } 378 | 379 | 80% { 380 | 381 | transform: rotate3d(0, 0, 1, -5deg); 382 | } 383 | 384 | 100% { 385 | 386 | transform: rotate3d(0, 0, 1, 0deg); 387 | } 388 | 389 | } 390 | 391 | @keyframes swing { 392 | 393 | 20% { 394 | 395 | transform: rotate3d(0, 0, 1, 15deg); 396 | } 397 | 398 | 40% { 399 | 400 | transform: rotate3d(0, 0, 1, -10deg); 401 | } 402 | 403 | 60% { 404 | 405 | transform: rotate3d(0, 0, 1, 5deg); 406 | } 407 | 408 | 80% { 409 | 410 | transform: rotate3d(0, 0, 1, -5deg); 411 | } 412 | 413 | 100% { 414 | 415 | transform: rotate3d(0, 0, 1, 0deg); 416 | } 417 | 418 | } 419 | 420 | @keyframes swing { 421 | 422 | 20% { 423 | 424 | transform: rotate3d(0, 0, 1, 15deg); 425 | } 426 | 427 | 40% { 428 | 429 | transform: rotate3d(0, 0, 1, -10deg); 430 | } 431 | 432 | 60% { 433 | 434 | transform: rotate3d(0, 0, 1, 5deg); 435 | } 436 | 437 | 80% { 438 | 439 | transform: rotate3d(0, 0, 1, -5deg); 440 | } 441 | 442 | 100% { 443 | 444 | transform: rotate3d(0, 0, 1, 0deg); 445 | } 446 | 447 | } 448 | 449 | @keyframes swing { 450 | 451 | 20% { 452 | 453 | transform: rotate3d(0, 0, 1, 15deg); 454 | } 455 | 456 | 40% { 457 | 458 | transform: rotate3d(0, 0, 1, -10deg); 459 | } 460 | 461 | 60% { 462 | 463 | transform: rotate3d(0, 0, 1, 5deg); 464 | } 465 | 466 | 80% { 467 | 468 | transform: rotate3d(0, 0, 1, -5deg); 469 | } 470 | 471 | 100% { 472 | 473 | transform: rotate3d(0, 0, 1, 0deg); 474 | } 475 | 476 | } 477 | 478 | @keyframes swing { 479 | 480 | 20% { 481 | 482 | transform: rotate3d(0, 0, 1, 15deg); 483 | } 484 | 485 | 40% { 486 | 487 | transform: rotate3d(0, 0, 1, -10deg); 488 | } 489 | 490 | 60% { 491 | 492 | transform: rotate3d(0, 0, 1, 5deg); 493 | } 494 | 495 | 80% { 496 | 497 | transform: rotate3d(0, 0, 1, -5deg); 498 | } 499 | 500 | 100% { 501 | 502 | transform: rotate3d(0, 0, 1, 0deg); 503 | } 504 | 505 | } 506 | -------------------------------------------------------------------------------- /src/core/exportBlock.ts: -------------------------------------------------------------------------------- 1 | import { IPanelDisplay, IImport, IDependence } from './interface'; 2 | import { 3 | toString, 4 | existImport, 5 | importString, 6 | parseLoop, 7 | parseStyle, 8 | parseFunction, 9 | parseProps, 10 | parseState, 11 | parseLifeCycles, 12 | replaceState, 13 | parseCondition, 14 | generateCSS, 15 | generateScss, 16 | parseDataSource, 17 | line2Hump, 18 | addAnimation, 19 | traverse, 20 | isObject, 21 | isJSSlot, 22 | } from './utils'; 23 | 24 | import { CSS_TYPE, OUTPUT_TYPE, prettierJsOpt, prettierCssOpt, prettierLessOpt, prettierScssOpt, DSL_CONFIG } from './consts'; 25 | 26 | 27 | export default function exportMod(schema, option):IPanelDisplay[] { 28 | const { 29 | prettier, 30 | scale, 31 | componentsMap, 32 | folder, 33 | blocksCount, 34 | pagesCount, 35 | blockInPage, 36 | dslConfig = {}, 37 | pageGlobalCss, 38 | _, 39 | } = option; 40 | 41 | const isExportGlobalFile = dslConfig.globalCss && blocksCount == 1 && !blockInPage; 42 | const fileName = schema.fileName; 43 | const { cssUnit } = dslConfig; 44 | const rootSchema = schema; 45 | 46 | let folderName; 47 | let filePathName = 'index'; 48 | if(schema.componentName == 'Page'){ 49 | // 单页面 50 | // if(pagesCount == 1){ 51 | // folderName = ''; 52 | // }else{ 53 | // folderName = 'pages/' + schema.fileName; 54 | // } 55 | // folderName = 'pages/' + schema.fileName; 56 | folderName = ''; 57 | if(dslConfig.outputStyle == OUTPUT_TYPE.PROJECT){ 58 | filePathName = 'App'; 59 | } 60 | 61 | // filePathName = schema.fileName 62 | }else{ 63 | folderName = pagesCount == 0 && blocksCount == 1 && dslConfig.outputStyle !== OUTPUT_TYPE.PROJECT? '' : ('components/' + schema.fileName); 64 | } 65 | schema.folderName = folderName; 66 | 67 | const globalCss = pageGlobalCss + '\n' + (schema.css || ''); 68 | 69 | // imports 70 | const dependenceList: IDependence[] = [] 71 | 72 | // imports mods 73 | const importMods: string[] = []; 74 | 75 | // import css 76 | const importStyles: string[] = []; 77 | 78 | const importsMap = new Map(); 79 | 80 | // inline style 81 | const style = {}; 82 | 83 | // Global Public Functions 84 | const utils: string[] = []; 85 | 86 | // states 87 | let statesData = null; 88 | 89 | // useState 90 | let useState: string[] = []; 91 | 92 | // calsses 93 | let classes: string[] = []; 94 | 95 | // methods 96 | const methods: string[] = []; 97 | 98 | // life cycles 99 | let lifeCycles: string[] = []; 100 | 101 | // init 102 | const init: string[] = []; 103 | 104 | const cssFileName = `${filePathName}${dslConfig.inlineStyle == CSS_TYPE.MODULE_CLASS ? '.module' : ''}.${dslConfig.cssType || 'css'}` 105 | 106 | 107 | if (dslConfig.inlineStyle !== CSS_TYPE.INLINE_CSS) { 108 | if (isExportGlobalFile) { 109 | importStyles.push(`import './global.css';`); 110 | } 111 | if (dslConfig.inlineStyle == CSS_TYPE.IMPORT_CLASS) { 112 | importStyles.push(`import './${cssFileName}';`); 113 | } else { 114 | importStyles.push(`import styles from './${cssFileName}';`); 115 | } 116 | } 117 | 118 | const collectImports = (componentName) => { 119 | // ignore the empty string 120 | if (!componentName) { 121 | return; 122 | } 123 | 124 | const component = componentsMap[componentName]; 125 | if(!component){ 126 | return 127 | } 128 | const objSets = importsMap.get(component.packageName); 129 | 130 | if (!objSets) { 131 | const set = new Set(); 132 | set.add(component); 133 | importsMap.set(component.packageName, set); 134 | } else { 135 | objSets.add(component); 136 | } 137 | 138 | if(!dependenceList.find(i=>i.package == component.packageName)){ 139 | dependenceList.push({ 140 | package: component.packageName, 141 | version: component.dependenceVersion || '*', 142 | }); 143 | } 144 | 145 | }; 146 | 147 | 148 | // generate render xml 149 | /** 150 | * 151 | * @param {*} json 152 | * @param {*} isReplace 是否提取 block 153 | * @returns 154 | */ 155 | const generateRender = (json, isReplace = false): string => { 156 | if(typeof json == 'string'){ 157 | return json 158 | } 159 | if(Array.isArray(json)){ 160 | return (json.map(item=>{ 161 | return generateRender(item, isReplace) 162 | })).join('') 163 | } 164 | const componentName = json.componentName; 165 | const type = json.componentName.toLowerCase(); 166 | let className = json.props && json.props.className; 167 | let classString = json.classString || ''; 168 | 169 | if (className) { 170 | style[className] = parseStyle(json.props.style); 171 | } 172 | 173 | let xml; 174 | let props = ''; 175 | 176 | Object.keys(json.props).forEach((key) => { 177 | if (key === 'codeStyle') { 178 | if (json.props[key] && JSON.stringify(json.props[key]) !== '{}') { 179 | props += ` style={${parseProps(json.props[key])}}`; 180 | } 181 | } 182 | 183 | if ( 184 | ['className', 'style', 'text', 'src', 'key', 'codeStyle'].indexOf( 185 | key 186 | ) === -1 187 | ) { 188 | props += ` ${key}={${parseProps(json.props[key])}}`; 189 | } 190 | 191 | // fix attr when type is not text 192 | if (type !== 'text' && ['text'].includes(key)) { 193 | props += ` ${key}={${parseProps(json.props[key])}}`; 194 | } 195 | 196 | }); 197 | 198 | switch (type) { 199 | case 'text': 200 | let innerText = 201 | parseProps(json.props.text || json.text, true) || ''; 202 | if (innerText.match(/this\.props/)) { 203 | innerText = innerText.replace(/this\./, ''); 204 | } 205 | xml = `${innerText || ''}`; 206 | break; 207 | case 'image': 208 | let source = parseProps(json.props.src); 209 | source = (source && `src={${source}}`) || ''; 210 | xml = ``; 211 | break; 212 | 213 | case 'page': 214 | case 'block': 215 | case 'component': 216 | if (isReplace) { 217 | const compName = json.fileName; 218 | xml = `<${compName} />`; 219 | // 当前是 Page 模块 220 | const compPath = rootSchema.componentName == 'Page' ? './components' : '..'; 221 | if(compName){ 222 | importMods.push(`import ${compName} from '${compPath}/${compName}';`); 223 | } 224 | delete style[className] 225 | } else if (json.children && json.children.length) { 226 | xml = `
${json.children 227 | .map((node) => { 228 | return generateRender(node, true); 229 | }) 230 | .join('')}
`; 231 | } else { 232 | xml = `
`; 233 | } 234 | break; 235 | case 'div': 236 | case 'view': 237 | if (json.children && json.children.length) { 238 | xml = `
${json.children 239 | .map((node) => { 240 | return generateRender(node, true); 241 | }) 242 | .join('')}
`; 243 | } else { 244 | xml = `
`; 245 | } 246 | break; 247 | default: 248 | if(componentName){ 249 | collectImports(componentName); 250 | if ( 251 | json.children && 252 | json.children.length && 253 | Array.isArray(json.children) 254 | ) { 255 | 256 | xml = `<${componentName} ${classString} ${props}>${json.children 257 | .map((node) => { 258 | return generateRender(node, true); 259 | }) 260 | .join('')}`; 261 | 262 | } else if (typeof json.children === 'string') { 263 | xml = `<${componentName} ${classString} ${props} >${json.children}`; 264 | } else { 265 | xml = `<${componentName} ${classString} ${props} />`; 266 | } 267 | }else{ 268 | xml = '' 269 | } 270 | } 271 | 272 | if (json.loop) { 273 | const parseLoopData = parseLoop( 274 | json.loop, 275 | json.loopArgs, 276 | xml, 277 | {} 278 | ); 279 | xml = parseLoopData.value; 280 | 281 | useState = useState.concat(parseLoopData.hookState); 282 | } 283 | 284 | xml = replaceState(xml); 285 | 286 | if (json.condition) { 287 | xml = parseCondition(json.condition, xml); 288 | } 289 | if (json.loop || json.condition) { 290 | xml = `{${xml}}`; 291 | } 292 | return xml; 293 | }; 294 | 295 | 296 | // parse schema 297 | const transformHooks = (json) => { 298 | if(typeof json == 'string'){ 299 | return json 300 | } 301 | let result = ''; 302 | const blockName = json.fileName || json.id; 303 | const type = json.componentName.toLowerCase(); 304 | 305 | // 容器组件处理: state/method/dataSource/lifeCycle 306 | const states: string[] = []; 307 | 308 | if (json.state) { 309 | states.push(`state = ${toString(json.state)};`); 310 | statesData = toString(json.state); 311 | } 312 | 313 | if (json.methods) { 314 | Object.keys(json.methods).forEach((name) => { 315 | const { params, content } = parseFunction(json.methods[name]); 316 | methods.push(`function ${name}(${params}) {${content}}`); 317 | }); 318 | } 319 | 320 | if (json.dataSource && Array.isArray(json.dataSource.list)) { 321 | json.dataSource.list.forEach((item) => { 322 | if (typeof item.isInit === 'boolean' && item.isInit) { 323 | init.push(`${item.id}();`); 324 | } else if (typeof item.isInit === 'string') { 325 | init.push(`if (${parseProps(item.isInit)}) { ${item.id}(); }`); 326 | } 327 | const parseDataSourceData = parseDataSource(item); 328 | methods.push( 329 | `const ${parseDataSourceData.functionName} = ()=> ${parseDataSourceData.functionBody}` 330 | ); 331 | }); 332 | 333 | if (json.dataSource.dataHandler) { 334 | const { params, content } = parseFunction( 335 | json.dataSource.dataHandler 336 | ); 337 | methods.push(`const dataHandler = (${params}) => {${content}}`); 338 | init.push(`dataHandler()`); 339 | } 340 | } 341 | 342 | if (json.lifeCycles) { 343 | lifeCycles = parseLifeCycles(json, init); 344 | } 345 | 346 | if (statesData) { 347 | useState.push(parseState(statesData)); 348 | } 349 | 350 | const hooksView = generateRender(json, false); 351 | const hasDispatch = hooksView.match('dispatch'); 352 | 353 | const classData = ` 354 | export default memo((props) => { 355 | ${useState.join('\n')} 356 | ${ 357 | hasDispatch 358 | ? 'const { state: { txt }, dispatch} = useContext(IndexContext);' 359 | : '' 360 | } 361 | 362 | ${methods.join('\n')} 363 | ${lifeCycles.join('\n')} 364 | ${ 365 | hooksView.match(/^\{true\ \&\& /) 366 | ? `return (${hooksView})` 367 | : `return (${hooksView})` 368 | } 369 | }); 370 | `; 371 | classes.push(classData); 372 | 373 | 374 | 375 | return result; 376 | }; 377 | 378 | const transformComponent = (json) => { 379 | if(typeof json == 'string'){ 380 | return json 381 | } 382 | let result: string = ''; 383 | const type = json.componentName.toLowerCase(); 384 | 385 | if (['page', 'block', 'component'].includes(type)) { 386 | // 容器组件处理: state/method/dataSource/lifeCycle/render 387 | const states: string[] = []; 388 | const lifeCycles: string[] = []; 389 | const methods: string[] = []; 390 | const init: string[] = []; 391 | 392 | let render = ''; 393 | let classData: string = ''; 394 | 395 | if (json.state) { 396 | states.push(`this.state = ${toString(json.state)};`); 397 | } 398 | 399 | if (json.methods) { 400 | Object.keys(json.methods).forEach((name) => { 401 | const { params, content } = parseFunction(json.methods[name]); 402 | methods.push(`${name}(${params}) {${content}}`); 403 | }); 404 | } 405 | 406 | if (json.dataSource && Array.isArray(json.dataSource.list)) { 407 | json.dataSource.list.forEach((item) => { 408 | if (typeof item.isInit === 'boolean' && item.isInit) { 409 | init.push(`this.${item.id}();`); 410 | } else if (typeof item.isInit === 'string') { 411 | init.push(`if (${parseProps(item.isInit)}) { this.${item.id}(); }`); 412 | } 413 | const parseDataSourceData = parseDataSource(item); 414 | methods.push( 415 | `${parseDataSourceData.functionName}()${parseDataSourceData.functionBody}` 416 | ); 417 | }); 418 | 419 | if (json.dataSource.dataHandler) { 420 | const { params, content } = parseFunction( 421 | json.dataSource.dataHandler 422 | ); 423 | methods.push(`dataHandler(${params}) {${content}}`); 424 | init.push(`this.dataHandler()`); 425 | } 426 | } 427 | 428 | 429 | if (!json.lifeCycles) { 430 | json.lifeCycles = {}; 431 | } 432 | 433 | if (!json.lifeCycles['_constructor']) { 434 | lifeCycles.push( 435 | `constructor(props, context) { super(); ${states.join('\n')} ${init.join('\n')}}` 436 | ); 437 | } 438 | 439 | Object.keys(json.lifeCycles).forEach((name) => { 440 | const { params, content } = parseFunction(json.lifeCycles[name]); 441 | 442 | if (name === '_constructor') { 443 | lifeCycles.push( 444 | `constructor(${params}) { super(); ${content} ${states.join('\n')} ${init.join('\n')}}` 445 | ); 446 | } else { 447 | lifeCycles.push(`${name}(${params}) {${content}}`); 448 | } 449 | }); 450 | 451 | render = generateRender(json, false); 452 | 453 | classData = ` 454 | export default class ${json.fileName} extends Component { 455 | ${lifeCycles.join('\n')} 456 | ${methods.join('\n')} 457 | render(){ 458 | const state = this.state; 459 | return (${render});} 460 | 461 | } 462 | `; 463 | 464 | classes.push(classData); 465 | } else { 466 | result += generateRender(json); 467 | } 468 | 469 | return result; 470 | }; 471 | 472 | 473 | const transform = dslConfig.useHooks 474 | ? transformHooks 475 | : transformComponent; 476 | 477 | // option.utils 478 | if (option.utils) { 479 | Object.keys(option.utils).forEach((name) => { 480 | utils.push(`const ${name} = ${option.utils[name]}`); 481 | }); 482 | } 483 | 484 | // parse schema 485 | 486 | 487 | traverse(schema, (node) => { 488 | const traverseProps = (value: any) => { 489 | console.log('traverseProps', value) 490 | if (value === null) { 491 | return null 492 | } 493 | if (Array.isArray(value)) { 494 | return value.map(item => traverseProps(item)) 495 | } 496 | if (!isObject(value)) return value 497 | if (isJSSlot(value)) { 498 | value.jsx = generateRender(value.value as unknown) 499 | } else { 500 | Object.keys(value).forEach(key => { 501 | value[key] = traverseProps(value[key]) 502 | }) 503 | } 504 | return value 505 | } 506 | 507 | traverseProps(node) 508 | }); 509 | 510 | // start parse schema 511 | transform(schema); 512 | let indexValue = ''; 513 | 514 | const imports: string[] = importString(importsMap); 515 | 516 | if (dslConfig.useHooks) { 517 | // const hooksView = generateRender(schema); 518 | // const hasDispatch = hooksView.match('dispatch'); 519 | indexValue = ` 520 | 'use strict'; 521 | import React, { useState, useEffect, memo } from 'react'; 522 | 523 | ${imports.join('\n')} 524 | ${importMods.join('\n')} 525 | 526 | ${importStyles.map((i) => i).join('\n')} 527 | ${utils.join('\n')} 528 | 529 | ${classes.join('\n')} 530 | 531 | `; 532 | } else { 533 | indexValue = ` 534 | 'use strict'; 535 | import React, { Component} from 'react'; 536 | 537 | ${imports.join('\n')} 538 | ${importMods.join('\n')} 539 | ${importStyles.map((i) => i).join('\n')} 540 | 541 | ${utils.join('\n')} 542 | ${classes.join('\n')} 543 | `; 544 | } 545 | 546 | const prefix = dslConfig.inlineStyle 547 | ? '' 548 | : schema.props && schema.props.className; 549 | 550 | // 获取当前 节点 所有 动画参数 551 | const animationKeyframes = addAnimation(schema); 552 | 553 | const panelDisplay: IPanelDisplay[] = [ 554 | { 555 | panelName: `${filePathName}.${dslConfig.useTypescript?'tsx': 'jsx'}`, 556 | panelValue: prettier.format(indexValue, prettierJsOpt), 557 | panelType: dslConfig.useTypescript?'tsx': 'jsx', 558 | folder: folderName, 559 | panelDependencies: dependenceList, 560 | }, 561 | ]; 562 | 563 | // 非内联模式 才引入 index.module.css 564 | if (dslConfig.inlineStyle !== CSS_TYPE.INLINE_CSS) { 565 | 566 | let cssPanelValue = generateCSS(schema.commonStyles, '') 567 | switch (dslConfig.cssType) { 568 | case 'less': 569 | cssPanelValue = prettier.format( 570 | `${cssPanelValue}${generateScss(schema)} ${animationKeyframes}`, 571 | prettierLessOpt 572 | ); 573 | break; 574 | case 'scss': 575 | cssPanelValue = prettier.format( 576 | `${cssPanelValue}${generateScss(schema)} ${animationKeyframes}`, 577 | prettierScssOpt 578 | ); 579 | break; 580 | default: 581 | cssPanelValue = prettier.format( 582 | `${cssPanelValue}${generateCSS(style, prefix)} ${animationKeyframes}`, 583 | prettierCssOpt 584 | ) 585 | } 586 | panelDisplay.push({ 587 | panelName: cssFileName, 588 | panelValue: cssPanelValue, 589 | panelType: dslConfig.cssType || 'css', 590 | folder: folderName, 591 | }); 592 | 593 | } 594 | 595 | // 只有一个模块时,生成到当前模块 596 | if (isExportGlobalFile && schema.css) { 597 | panelDisplay.push({ 598 | panelName: `global.css`, 599 | panelValue: prettier.format(schema.css || '', prettierCssOpt), 600 | panelType: 'css', 601 | folder: folderName, 602 | }); 603 | } 604 | 605 | return panelDisplay; 606 | } 607 | 608 | -------------------------------------------------------------------------------- /src/entry_back.js: -------------------------------------------------------------------------------- 1 | /** 2 | * transform the componentsMap to real Map from compsMap.list as array 3 | * @param {*} compsMap 4 | */ 5 | const transComponentsMap = (compsMap = {}) => { 6 | if (!compsMap || !Array.isArray(compsMap.list)) { 7 | return []; 8 | } 9 | const list = compsMap.list; 10 | return list.reduce((obj, comp) => { 11 | const componentName = comp.name; 12 | if (!obj[componentName]) { 13 | if (comp.dependence) { 14 | try { 15 | let dependence = typeof comp.dependence === 'string' ? JSON.parse(comp.dependence) : comp.dependence; 16 | if (dependence) { 17 | comp.packageName = dependence.package; 18 | comp.dependence = dependence; 19 | } 20 | if (!comp.dependenceVersion) { 21 | comp.dependenceVersion = '*'; 22 | } 23 | comp.exportName = dependence.export_name; 24 | comp.subName = dependence.sub_name; 25 | if (/^\d/.test(comp.dependenceVersion)) { 26 | comp.dependenceVersion = '^' + comp.dependenceVersion; 27 | } 28 | } catch (e) { 29 | console.log(e); 30 | } 31 | } 32 | obj[componentName] = comp; 33 | } 34 | return obj; 35 | }, {}); 36 | }; 37 | 38 | module.exports = function(schema, option) { 39 | const { _, prettier } = option; 40 | componentsMap = transComponentsMap(option.componentsMap); 41 | // imports, the key is the package name, the value is a set includes the component objects 42 | const imports = new Map(); 43 | const importsExt = []; 44 | 45 | // inline style 46 | const style = {}; 47 | 48 | // Global Public Functions 49 | const utils = []; 50 | 51 | // Classes 52 | const classes = []; 53 | 54 | // 1vw = width / 100 55 | const _w = (option.responsive.width / 100) || 750; 56 | 57 | let pageLock = false; 58 | 59 | const isExpression = (value) => { 60 | return /^\{\{.*\}\}$/.test(value); 61 | } 62 | 63 | const toString = (value) => { 64 | if ({}.toString.call(value) === '[object Function]') { 65 | return value.toString(); 66 | } 67 | if (typeof value === 'string') { 68 | return value; 69 | } 70 | if (typeof value === 'object') { 71 | return JSON.stringify(value, (key, value) => { 72 | if (typeof value === 'function') { 73 | return value.toString(); 74 | } else { 75 | return value; 76 | } 77 | }) 78 | } 79 | 80 | return String(value); 81 | }; 82 | 83 | // flexDirection -> flex-direction 84 | const parseCamelToLine = (string) => { 85 | return string.split(/(?=[A-Z])/).join('-').toLowerCase(); 86 | } 87 | 88 | /** 89 | * constrcut the import string 90 | */ 91 | const importString = () => { 92 | const importStrings = []; 93 | const subImports = []; 94 | for (const [ packageName, pkgSet ] of imports) { 95 | const set1 = new Set(), set2 = new Set(); 96 | for (const pkg of pkgSet) { 97 | let exportName = pkg.exportName; 98 | let subName = pkg.subName; 99 | let componentName = pkg.name; 100 | 101 | if (pkg.subName) { 102 | subImports.push(`const ${componentName} = ${exportName}.${subName};`); 103 | } 104 | if (componentName !== exportName && !pkg.subName) { 105 | exportName = `${exportName} as ${componentName}`; 106 | } 107 | if (!pkg.dependence.destructuring) { 108 | set1.add(exportName); 109 | } else { 110 | set2.add(exportName); 111 | } 112 | } 113 | const set1Str = [ ...set1 ].join(','); 114 | let set2Str = [ ...set2 ].join(','); 115 | const dot = set1Str && set2Str ? ',' : ''; 116 | if (set2Str) { 117 | set2Str = `{${set2Str}}`; 118 | } 119 | importStrings.push(`import ${set1Str} ${dot} ${set2Str} from '${packageName}'`); 120 | } 121 | return importStrings.concat(subImports); 122 | } 123 | 124 | /** 125 | * store the components to the 'imports' map which was used 126 | * 127 | * @param {*} componentName component name like 'Button' 128 | */ 129 | const generateImport = (componentName) => { 130 | // ignore the empty string 131 | if (!componentName) { 132 | return; 133 | } 134 | const component = componentsMap[componentName]; 135 | if (component) { 136 | const objSets = imports.get(component.packageName); 137 | if (!objSets) { 138 | const set = new Set(); 139 | set.add(component); 140 | imports.set(component.packageName, set); 141 | } else { 142 | objSets.add(component); 143 | } 144 | return; 145 | } 146 | }; 147 | 148 | // className structure support 149 | const generateLess = (schema, style) => { 150 | let less = ''; 151 | 152 | function walk(json) { 153 | if (json.props && json.props.className) { 154 | let className = json.props.className; 155 | less += `.${className} {`; 156 | 157 | for (let key in style[className]) { 158 | less += `${parseCamelToLine(key)}: ${style[className][key]};\n` 159 | } 160 | } 161 | if (json.children && json.children.length > 0 && Array.isArray(json.children)) { 162 | json.children.forEach(child => walk(child)); 163 | } 164 | 165 | if (json.props && json.props.className) { 166 | less += '}'; 167 | } 168 | } 169 | 170 | walk(schema); 171 | 172 | return less; 173 | }; 174 | 175 | const generateCss = (style, toVW) => { 176 | let css = ''; 177 | Object.keys(style).map((key) => { 178 | css +=`.${key}{${formatStyle(style[key], toVW)}}` 179 | }); 180 | return css; 181 | } 182 | 183 | // box relative style 184 | const boxStyleList = ['fontSize', 'marginTop', 'marginBottom', 'paddingTop', 'paddingBottom', 'height', 'top', 'bottom', 'width', 'maxWidth', 'left', 'right', 'paddingRight', 'paddingLeft', 'marginLeft', 'marginRight', 'lineHeight', 'borderBottomRightRadius', 'borderBottomLeftRadius', 'borderTopRightRadius', 'borderTopLeftRadius', 'borderRadius']; 185 | // no unit style 186 | const noUnitStyles = ['opacity', 'fontWeight']; 187 | 188 | const formatStyle = (style, toVW) => { 189 | const styleData = []; 190 | for (let key in style) { 191 | let value = style[key]; 192 | if (boxStyleList.indexOf(key) != -1) { 193 | if (toVW) { 194 | value = (parseInt(value) / _w).toFixed(2); 195 | value = value == 0 ? value : value + 'vw'; 196 | } else { 197 | value = (parseInt(value)).toFixed(2); 198 | value = value == 0 ? value : value + 'px'; 199 | } 200 | styleData.push(`${_.kebabCase(key)}: ${value}`); 201 | } else if (noUnitStyles.indexOf(key) != -1) { 202 | styleData.push(`${_.kebabCase(key)}: ${parseFloat(value)}`); 203 | } else { 204 | styleData.push(`${_.kebabCase(key)}: ${value}`); 205 | } 206 | } 207 | return styleData.join(';'); 208 | } 209 | 210 | 211 | // convert to responsive unit, such as vw 212 | const parseStyle = (styles) => { 213 | for (let style in styles) { 214 | for (let key in styles[style]) { 215 | if (boxStyleList.indexOf(key) > 0) { 216 | styles[style][key] = (parseInt(styles[style][key]) / _w).toFixed(2) + 'vw'; 217 | } 218 | } 219 | } 220 | 221 | return styles; 222 | } 223 | 224 | // parse function, return params and content 225 | const parseFunction = (func) => { 226 | const funcString = func.toString(); 227 | const params = funcString.match(/\([^\(\)]*\)/)[0].slice(1, -1); 228 | const content = funcString.slice(funcString.indexOf('{') + 1, funcString.lastIndexOf('}')); 229 | return { 230 | params, 231 | content 232 | }; 233 | } 234 | 235 | // parse layer props(static values or expression) 236 | const parseProps = (value, isReactNode) => { 237 | if (typeof value === 'string') { 238 | if (isExpression(value)) { 239 | if (isReactNode) { 240 | return value.slice(1, -1); 241 | } else { 242 | return value.slice(2, -2); 243 | } 244 | } 245 | 246 | if (isReactNode) { 247 | return value; 248 | } else { 249 | return `"${value}"`; 250 | } 251 | } else if (typeof value === 'function') { 252 | const {params, content} = parseFunction(value); 253 | return `(${params}) => {${content}}`; 254 | } else { 255 | return JSON.stringify(value); 256 | } 257 | } 258 | 259 | // parse async dataSource 260 | const parseDataSource = (data) => { 261 | const name = data.id; 262 | const {uri, method, params} = data.options; 263 | const action = data.type; 264 | let payload = {}; 265 | 266 | switch (action) { 267 | case 'fetch': 268 | if (importsExt.indexOf(`import {fetch} from whatwg-fetch`) === -1) { 269 | importsExt.push(`import {fetch} from 'whatwg-fetch'`); 270 | } 271 | payload = { 272 | method: method 273 | }; 274 | 275 | break; 276 | case 'jsonp': 277 | if (importsExt.indexOf(`import {fetchJsonp} from fetch-jsonp`) === -1) { 278 | importsExt.push(`import jsonp from 'fetch-jsonp'`); 279 | } 280 | break; 281 | } 282 | 283 | Object.keys(data.options).forEach((key) => { 284 | if (['uri', 'method', 'params'].indexOf(key) === -1) { 285 | payload[key] = toString(data.options[key]); 286 | } 287 | }); 288 | 289 | // params parse should in string template 290 | if (params) { 291 | payload = `${toString(payload).slice(0, -1)} ,body: ${isExpression(params) ? parseProps(params) : toString(params)}}`; 292 | } else { 293 | payload = toString(payload); 294 | } 295 | 296 | let result = `{ 297 | ${action}(${parseProps(uri)}, ${toString(payload)}) 298 | .then((response) => response.json()) 299 | `; 300 | 301 | if (data.dataHandler) { 302 | const { params, content } = parseFunction(data.dataHandler); 303 | result += `.then((${params}) => {${content}}) 304 | .catch((e) => { 305 | console.log('error', e); 306 | }) 307 | ` 308 | } 309 | 310 | result += '}'; 311 | 312 | return `${name}() ${result}`; 313 | } 314 | 315 | // parse condition: whether render the layer 316 | const parseCondition = (condition, render) => { 317 | if (typeof condition === 'boolean') { 318 | return `${condition} && ${render}` 319 | } else if (typeof condition === 'string') { 320 | return `${condition.slice(2, -2)} && ${render}` 321 | } 322 | } 323 | 324 | // parse loop render 325 | const parseLoop = (loop, loopArg, render) => { 326 | let data; 327 | let loopArgItem = (loopArg && loopArg[0]) || 'item'; 328 | let loopArgIndex = (loopArg && loopArg[1]) || 'index'; 329 | 330 | if (Array.isArray(loop)) { 331 | data = toString(loop); 332 | } else if (isExpression(loop)) { 333 | data = loop.slice(2, -2); 334 | } 335 | 336 | // add loop key 337 | const tagEnd = render.match(/^<.+?\s/)[0].length; 338 | render = `${render.slice(0, tagEnd)} key={${loopArgIndex}}${render.slice(tagEnd)}`; 339 | 340 | // remove `this` 341 | const re = new RegExp(`this.${loopArgItem}`, 'g') 342 | render = render.replace(re, loopArgItem); 343 | 344 | return `${data}.map((${loopArgItem}, ${loopArgIndex}) => { 345 | return (${render}); 346 | })`; 347 | } 348 | 349 | // generate render xml 350 | const generateRender = (schema) => { 351 | const type = schema.componentName.toLowerCase(); 352 | const className = schema.props && schema.props.className || ''; 353 | const classString = className ? ` className="${className}"` : ''; 354 | if (className) { 355 | style[className] = schema.props.style; 356 | } 357 | 358 | let xml; 359 | let props = ''; 360 | 361 | Object.keys(schema.props).forEach((key) => { 362 | if (['className', 'style', 'text', 'src', 'lines'].indexOf(key) === -1) { 363 | props += ` ${key}={${parseProps(schema.props[key])}}`; 364 | } 365 | }) 366 | switch(type) { 367 | case 'text': 368 | const innerText = parseProps(schema.props.text, true); 369 | xml = `${innerText}`; 370 | break; 371 | case 'image': 372 | const source = parseProps(schema.props.src); 373 | xml = ``; 374 | break; 375 | case 'div': 376 | case 'page': 377 | case 'block': 378 | case 'component': 379 | if (schema.children && schema.children.length) { 380 | xml = `${transform(schema.children)}
`; 381 | } else { 382 | xml = ``; 383 | } 384 | break; 385 | default: 386 | if (schema.children && schema.children.length) { 387 | xml = `<${schema.componentName}${classString}${props}>${transform(schema.children)}`; 388 | } else { 389 | xml = `<${schema.componentName}${classString}${props} />`; 390 | } 391 | 392 | const importString = generateImport(schema.componentName); 393 | 394 | if (importString) { 395 | imports.push(importString); 396 | } 397 | break; 398 | } 399 | 400 | if (schema.loop) { 401 | xml = parseLoop(schema.loop, schema.loopArgs, xml) 402 | } 403 | if (schema.condition) { 404 | xml = parseCondition(schema.condition, xml); 405 | } 406 | if ((schema.loop || schema.condition )&& !schema.isRoot) { 407 | xml = `{${xml}}`; 408 | } 409 | 410 | return xml; 411 | } 412 | // get all layers information 413 | let getAllLayers = function(layers, schema) { 414 | if (schema.hasOwnProperty('children')) { 415 | for (let i of schema.children) { 416 | layers.push(i); 417 | getAllLayers(layers, i); 418 | } 419 | } 420 | }; 421 | // trans animation JS Web Animation Api to css 422 | const transAnimation = function(animation) { 423 | let keyFrames = ``; 424 | for (let i of animation.keyframes) { 425 | keyFrames += ` 426 | ${((i.offset * 10000) / 100.0).toFixed(0) + '%'} { 427 | ${i.opacity ? 'opacity: '.concat(i.opacity) + ';' : ''} 428 | ${i.transform ? 'transform: '.concat(i.transform) + ';' : ''} 429 | } 430 | `; 431 | } 432 | let keyframes = ` 433 | @keyframes ${animation.name} { 434 | ${keyFrames} 435 | } 436 | `; 437 | return keyframes; 438 | }; 439 | // get keyframes from all layers 440 | const addAnimation = function (schema) { 441 | let animationRes = ``; 442 | if (schema.animation) { 443 | animationRes += transAnimation(schema.animation); 444 | } 445 | let layers = []; 446 | getAllLayers(layers, schema); 447 | for (let i of layers) { 448 | if (i.animation) { 449 | animationRes += transAnimation(i.animation); 450 | } 451 | } 452 | return animationRes; 453 | } 454 | 455 | // parse schema 456 | const transform = (schema) => { 457 | let result = ''; 458 | if (Array.isArray(schema)) { 459 | schema.forEach((layer) => { 460 | result += transform(layer); 461 | }); 462 | } else if (typeof schema === 'string') { 463 | result += schema; 464 | } else if (typeof schema === 'object' && typeof schema.componentName === 'string') { 465 | // fix the problem of multiple page tags 466 | let type = schema.componentName.toLowerCase(); 467 | let cycleMark = ['block', 'component'].indexOf(type) !== -1; 468 | if (pageLock && cycleMark) { 469 | type = 'div'; 470 | cycleMark = false; 471 | } 472 | if (type === 'page') { 473 | if (!pageLock) { 474 | pageLock = true; 475 | cycleMark = true; 476 | } else { 477 | type = 'div'; 478 | } 479 | } 480 | if (cycleMark) { 481 | // 容器组件处理: state/method/dataSource/lifeCycle/render 482 | const states = []; 483 | const lifeCycles = []; 484 | const methods = []; 485 | const init = []; 486 | const render = [`render(){ return (`]; 487 | let classData = [`class ${schema.componentName}_${classes.length} extends Component {`]; 488 | 489 | if (schema.state) { 490 | states.push(`state = ${toString(schema.state)}`); 491 | } 492 | 493 | if (schema.methods) { 494 | Object.keys(schema.methods).forEach((name) => { 495 | const { params, content } = parseFunction(schema.methods[name]); 496 | methods.push(`${name}(${params}) {${content}}`); 497 | }); 498 | } 499 | 500 | if (schema.dataSource && Array.isArray(schema.dataSource.list)) { 501 | schema.dataSource.list.forEach((item) => { 502 | if (typeof item.isInit === 'boolean' && item.isInit) { 503 | init.push(`this.${item.id}();`) 504 | } else if (typeof item.isInit === 'string') { 505 | init.push(`if (${parseProps(item.isInit)}) { this.${item.id}(); }`) 506 | } 507 | methods.push(parseDataSource(item)); 508 | }); 509 | 510 | if (schema.dataSource.dataHandler) { 511 | const { params, content } = parseFunction(schema.dataSource.dataHandler); 512 | methods.push(`dataHandler(${params}) {${content}}`); 513 | init.push(`this.dataHandler()`); 514 | } 515 | } 516 | 517 | if (schema.lifeCycles) { 518 | if (!schema.lifeCycles['_constructor']) { 519 | lifeCycles.push(`constructor(props, context) { super(); ${init.join('\n')}}`); 520 | } 521 | 522 | Object.keys(schema.lifeCycles).forEach((name) => { 523 | const { params, content } = parseFunction(schema.lifeCycles[name]); 524 | 525 | if (name === '_constructor') { 526 | lifeCycles.push(`constructor(${params}) { super(); ${content} ${init.join('\n')}}`); 527 | } else { 528 | lifeCycles.push(`${name}(${params}) {${content}}`); 529 | } 530 | }); 531 | } 532 | 533 | render.push(generateRender(schema)) 534 | render.push(`);}`); 535 | 536 | classData = classData.concat(states).concat(lifeCycles).concat(methods).concat(render); 537 | classData.push('}'); 538 | 539 | classes.push(classData.join('\n')); 540 | } else { 541 | result += generateRender(schema); 542 | } 543 | } 544 | 545 | return result; 546 | }; 547 | 548 | if (option.utils) { 549 | Object.keys(option.utils).forEach((name) => { 550 | utils.push(`const ${name} = ${option.utils[name]}`); 551 | }); 552 | } 553 | 554 | schema.isRoot = true 555 | 556 | const prettierCssOpt = { 557 | parser: 'css' 558 | } 559 | 560 | // start parse schema 561 | transform(schema); 562 | const animationKeyframes = addAnimation(schema); 563 | 564 | const prettierOpt = { 565 | parser: 'babel', 566 | printWidth: 120, 567 | singleQuote: true 568 | }; 569 | let importStrings = importString(); 570 | importStrings = importStrings.concat(importsExt); 571 | 572 | return { 573 | panelDisplay: [ 574 | { 575 | panelName: `index.jsx`, 576 | panelValue: prettier.format(` 577 | 'use strict'; 578 | 579 | import React, { Component } from 'react'; 580 | ${importStrings.join('\n')} 581 | import './style.css'; 582 | 583 | ${utils.join('\n')} 584 | ${classes.join('\n')} 585 | export default ${schema.componentName}_0; 586 | `, prettierOpt), 587 | panelType: 'js', 588 | }, 589 | { 590 | panelName: `style.css`, 591 | panelValue: prettier.format(generateCss(style), { parser: 'css' }) + animationKeyframes, 592 | panelType: 'css' 593 | }, 594 | { 595 | panelName: `style.responsive.css`, 596 | panelValue: prettier.format(`${generateCss(style, true)}`, { parser: 'css' }) + animationKeyframes, 597 | panelType: 'css' 598 | } 599 | ], 600 | noTemplate: true 601 | }; 602 | } 603 | -------------------------------------------------------------------------------- /src/core/utils.ts: -------------------------------------------------------------------------------- 1 | import { IImport } from './interface'; 2 | const find = require('lodash/find'); 3 | const unset = require('lodash/unset'); 4 | const set = require('lodash/set'); 5 | const get = require('lodash/get'); 6 | const camelCase = require('lodash/camelCase'); 7 | const kebabCase = require('lodash/kebabCase'); 8 | const snakeCase = require('lodash/snakeCase'); 9 | const cssParser = require('css/lib/parse'); 10 | import { DSL_CONFIG } from './consts' 11 | 12 | 13 | // 从 css 解析样式规则饿 14 | export const getCssRules = (text: string): { 15 | selectors: string, 16 | style: any 17 | }[] => { 18 | if (!cssParser) { 19 | return []; 20 | } 21 | const globalCssResult = cssParser(text, { source: 'global.css' }); 22 | 23 | let rules = globalCssResult.stylesheet.rules; 24 | rules = rules.filter((item) => item.type === 'rule'); 25 | rules = rules.map((item) => { 26 | let style = {}; 27 | for (let dec of item.declarations) { 28 | const property = camelCase(dec.property); 29 | style[property] = dec.value; 30 | } 31 | 32 | return { 33 | selectors: item.selectors[0], 34 | style: style, 35 | }; 36 | }); 37 | 38 | return rules; 39 | }; 40 | 41 | // 提取全局样式 42 | export const getGlobalClassNames = (cssObject, globalCssString) => { 43 | let names: string[] = []; 44 | if (!(globalCssString && cssParser)) { 45 | // 没有全局样式名 46 | return { 47 | names, 48 | style: cssObject, 49 | }; 50 | } 51 | 52 | // 解析全局 css 规则 53 | const rules = getCssRules(globalCssString); 54 | 55 | for (let rule of rules) { 56 | // 按顺序提取样式 57 | // 仅提取 . 选择符 58 | const isMatch = find([cssObject], rule.style) && rule.selectors.startsWith('.'); 59 | if (isMatch) { 60 | for (let key in rule.style) { 61 | unset(cssObject, key); 62 | } 63 | names.push(rule.selectors.replace('.', '')); 64 | } 65 | } 66 | 67 | return { 68 | names, 69 | style: cssObject, 70 | }; 71 | }; 72 | 73 | export const isExpression = (value) => { 74 | return /^\{\{.*\}\}$/.test(value); 75 | }; 76 | 77 | // eg: hello_world => HelloWorld 78 | export const line2Hump = (str) => { 79 | str = str.replace(/[_|-](\w)/g, (all, letter) => { 80 | return letter.toUpperCase(); 81 | }); 82 | str = str.charAt(0).toUpperCase() + str.slice(1); 83 | return str; 84 | }; 85 | 86 | export const isEmptyObj = (o) => { 87 | if (o !== null && Object.prototype.toString.call(o) === '[object Object]') { 88 | return !Object.keys(o).length; 89 | } 90 | return false; 91 | }; 92 | 93 | interface IComp { 94 | list?: { 95 | name: string; packageName: string; dependenceVersion: string; dependence: string, exportName: string; 96 | subName: string; 97 | }[] 98 | }; 99 | export const transComponentsMap = (compsMap: IComp = {}) => { 100 | if (!compsMap || !Array.isArray(compsMap.list)) { 101 | return []; 102 | } 103 | const list = compsMap.list; 104 | return list.reduce((obj, comp) => { 105 | const componentName = comp.name; 106 | if (!obj[componentName]) { 107 | if (comp.dependence) { 108 | try { 109 | let dependence = typeof comp.dependence === 'string' ? JSON.parse(comp.dependence) : comp.dependence; 110 | if (dependence) { 111 | comp.packageName = dependence.package; 112 | } 113 | if (!comp.dependenceVersion) { 114 | comp.dependenceVersion = '*'; 115 | } 116 | comp.exportName = dependence.export_name; 117 | comp.subName = dependence.sub_name; 118 | if (/^\d/.test(comp.dependenceVersion)) { 119 | comp.dependenceVersion = '^' + comp.dependenceVersion; 120 | } 121 | } catch (e) { 122 | console.log(e); 123 | } 124 | } 125 | 126 | obj[componentName] = comp; 127 | } 128 | return obj; 129 | }, {}); 130 | }; 131 | 132 | export const toString = (value) => { 133 | if ({}.toString.call(value) === '[object Function]') { 134 | return value.toString(); 135 | } 136 | if (typeof value === 'string') { 137 | return value; 138 | } 139 | if (typeof value === 'object') { 140 | return JSON.stringify(value, (key, value) => { 141 | if (typeof value === 'function') { 142 | return value.toString(); 143 | } else { 144 | return value; 145 | } 146 | }); 147 | } 148 | 149 | return String(value); 150 | }; 151 | 152 | export const toUpperCaseStart = (value) => { 153 | return value.charAt(0).toUpperCase() + value.slice(1); 154 | }; 155 | 156 | 157 | // 计数器 158 | let counter = {}; 159 | const getCounter = (key) => { 160 | counter[key] = (counter[key] || 0) + 1 161 | return counter[key]; 162 | } 163 | 164 | export const resetCounter = (key) => { 165 | counter[key] = 0; 166 | } 167 | 168 | 169 | // 是否所有样式相同 170 | function isAllEqual(array) { 171 | if (array.length > 0) { 172 | return !array.some(function (value, index) { 173 | return value !== array[0]; 174 | }); 175 | } else { 176 | return true; 177 | } 178 | } 179 | 180 | // 获得相同样式 181 | function getMaxSameStyles(array) { 182 | if (array.length < 2) { 183 | return {} 184 | } 185 | let maxStyle = {} 186 | const keys = Object.keys(array[0]) 187 | for (let key of keys) { 188 | if (isAllEqual(array.map(item => item[key]))) { 189 | maxStyle[key] = array[0][key] 190 | } 191 | } 192 | 193 | return maxStyle 194 | } 195 | 196 | export const commonStyle = (schema) => { 197 | traverseBrother(schema, function (nodes) { 198 | const sameStyle = getMaxSameStyles(nodes.filter(item => item.props && item.props.style).map(item => item.props.style)); 199 | if (Object.keys(sameStyle).length > 3) { 200 | const commonClassName = genStyleClass( 201 | 'common_'+nodes[0].props.className, 202 | DSL_CONFIG.cssStyle 203 | ); 204 | 205 | set(schema, `commonStyles.${commonClassName}`, parseStyle(sameStyle)) 206 | for (let node of nodes) { 207 | for (let key of Object.keys(sameStyle)) { 208 | unset(node, `props.style.${key}`) 209 | } 210 | node.classnames = node.classnames || [] 211 | node.classnames.push(commonClassName) 212 | } 213 | } 214 | }) 215 | } 216 | 217 | // 精简样式 218 | export const simpleStyle = (schema) => { 219 | 220 | function getMaxRepeatItem(array) { 221 | let a = {} 222 | let max = 0; 223 | let maxele = null; 224 | for (let i = 0; i < array.length; i++) { 225 | a[array[i]] == undefined ? a[array[i]] = 1 : a[array[i]]++; 226 | if (a[array[i]] > max) { 227 | maxele = array[i]; 228 | max = a[array[i]]; 229 | } 230 | } 231 | return maxele; 232 | } 233 | 234 | // 统计出现字体最多的,放到根节点 235 | let fontFamilys: string[] = [] 236 | traverse(schema, (node) => { 237 | const ft = get(node, 'props.style.fontFamily'); 238 | if (ft) { 239 | fontFamilys.push(ft) 240 | } 241 | }); 242 | 243 | const rootFont = get(schema, 'props.style.fontFamily') || getMaxRepeatItem(fontFamilys); 244 | if (rootFont) { 245 | traverse(schema, (node) => { 246 | const ft = get(node, 'props.style.fontFamily'); 247 | if (ft == rootFont) { 248 | unset(node, 'props.style.fontFamily'); 249 | } 250 | }); 251 | set(schema, 'props.style.fontFamily', rootFont); 252 | } 253 | 254 | // 删除 font-weight 400 或者 normal 255 | traverse(schema, (node) => { 256 | const removeStyle = (node, styleName, values) => { 257 | const fw = get(node, `props.style.${styleName}`); 258 | if (values.includes(String(fw) || '')) { 259 | unset(node, `props.style.${styleName}`); 260 | } 261 | } 262 | removeStyle(node, 'fontWeight', ['400', 400, 'normal']); 263 | removeStyle(node, 'flexDirection', ['row']); 264 | removeStyle(node, 'textDecoration', ['none']); 265 | }); 266 | 267 | 268 | 269 | return schema; 270 | } 271 | 272 | /** 273 | * 处理schema一些常见问题 274 | * @param schema 275 | * 1. 清理 class 空格 276 | * 2. 关键节点命名兜底 277 | */ 278 | export const initSchema = (schema) => { 279 | // 重置计数器 280 | resetCounter('page'); 281 | resetCounter('block'); 282 | resetCounter('component'); 283 | 284 | // 清理 class 空格 285 | traverse(schema, (node) => { 286 | if (node && node.props && node.props.className) { 287 | node.props.className = String(node.props.className).trim(); 288 | } 289 | node.componentName = node.componentName || 'div' 290 | }); 291 | 292 | // 样式名处理:指定命名风格 293 | traverse(schema, (json) => { 294 | if (json.props && json.props.className) { 295 | json.props.className = genStyleClass( 296 | json.props.className, 297 | DSL_CONFIG.cssStyle 298 | ); 299 | } 300 | }); 301 | 302 | // 关键节点命名兜底 303 | traverse(schema, (json) => { 304 | json.componentName = json.componentName || '' 305 | switch (json.componentName.toLowerCase()) { 306 | case 'page': 307 | json.fileName = line2Hump(json.fileName || `page_${getCounter('page')}`); 308 | break; 309 | case 'block': 310 | json.fileName = line2Hump(json.fileName || `block_${getCounter('block')}`); 311 | break; 312 | case 'component': 313 | json.fileName = line2Hump(json.fileName || `component_${getCounter('component')}`); 314 | break; 315 | default: 316 | break; 317 | } 318 | }); 319 | 320 | traverse(schema, (json) => { 321 | if(json.componentName == 'Text'){ 322 | delete json.props.lines 323 | } 324 | }); 325 | }; 326 | 327 | // 遍历节点 328 | export const traverse = (json, callback) => { 329 | if (Array.isArray(json)) { 330 | json.forEach((node) => { 331 | traverse(node, callback) 332 | }); 333 | return 334 | } 335 | 336 | // 去除 class 空格 337 | if (json && callback) { 338 | callback(json) 339 | } 340 | 341 | if ( 342 | json.children && 343 | json.children.length > 0 && 344 | Array.isArray(json.children) 345 | ) { 346 | json.children.forEach((child) => { 347 | traverse(child, callback); 348 | }); 349 | } 350 | }; 351 | 352 | 353 | // 遍历兄弟节点 354 | export const traverseBrother = (json, callback) => { 355 | if (Array.isArray(json)) { 356 | json.forEach((node) => { 357 | traverseBrother(node, callback) 358 | }); 359 | return 360 | } 361 | 362 | if (json && Array.isArray(json.children) && callback) { 363 | callback(json.children) 364 | } 365 | 366 | if ( 367 | json.children && 368 | json.children.length > 0 && 369 | Array.isArray(json.children) 370 | ) { 371 | json.children.forEach((child) => { 372 | traverseBrother(child, callback); 373 | }); 374 | } 375 | }; 376 | 377 | 378 | export const genStyleClass = (string, type) => { 379 | let classArray = string.split(' '); 380 | classArray = classArray.filter(name => !!name); 381 | classArray = classArray.map(name => { 382 | switch (type) { 383 | case 'camelCase': return camelCase(name); 384 | case 'kebabCase': return kebabCase(name); 385 | case 'snakeCase': return snakeCase(name); 386 | default: 387 | return camelCase(name); 388 | } 389 | }); 390 | return classArray.join(' ') 391 | } 392 | 393 | export const genStyleCode = (styles, key = '') => { 394 | return !/-/.test(key) && key.trim() 395 | ? `${styles}.${key}` 396 | : `${styles}['${key}']`; 397 | }; 398 | 399 | export const parseNumberValue = (value) => { 400 | const { cssUnit = 'px', scale, responseWidth } = DSL_CONFIG 401 | value = String(value).replace(/\b[\d\.]+(px|rem|rpx|vw)?\b/, (v) => { 402 | const nv = parseFloat(v); 403 | if (!isNaN(nv) && nv !== 0) { 404 | return toString(nv); 405 | } else { 406 | return 0; 407 | } 408 | }); 409 | 410 | if (/^\-?[\d\.]+$/.test(value)) { 411 | value = parseFloat(value); 412 | if (cssUnit == 'rpx') { 413 | value = 750 * value / Number(responseWidth); 414 | value = value == 0 ? value : value + 'rpx'; 415 | } else if (cssUnit == 'rem') { 416 | const htmlFontSize = DSL_CONFIG.htmlFontSize || 16; 417 | value = parseFloat((value / htmlFontSize).toFixed(2)); 418 | value = value ? `${value}rem` : value; 419 | } else if (cssUnit == 'vw') { 420 | const _w = 750 / scale 421 | value = (100 * parseInt(value) / _w).toFixed(2); 422 | value = value == 0 ? value : value + 'vw'; 423 | } else { 424 | value += cssUnit; 425 | } 426 | } 427 | return value; 428 | }; 429 | 430 | // convert to responsive unit, such as vw 431 | export const parseStyle = (style) => { 432 | const { scale, cssUnit } = DSL_CONFIG 433 | const resultStyle = {} 434 | for (let key in style) { 435 | switch (key) { 436 | case 'fontSize': 437 | case 'marginTop': 438 | case 'marginBottom': 439 | case 'paddingTop': 440 | case 'paddingBottom': 441 | case 'height': 442 | case 'top': 443 | case 'bottom': 444 | case 'width': 445 | case 'maxWidth': 446 | case 'left': 447 | case 'right': 448 | case 'paddingRight': 449 | case 'paddingLeft': 450 | case 'marginLeft': 451 | case 'marginRight': 452 | case 'lineHeight': 453 | case 'borderBottomRightRadius': 454 | case 'borderBottomLeftRadius': 455 | case 'borderTopRightRadius': 456 | case 'borderTopLeftRadius': 457 | case 'borderRadius': 458 | 459 | if (style[key]) { 460 | const values = String(style[key]).split(' '); 461 | resultStyle[key] = values.map(val => { 462 | if(String(val).endsWith('%')){ 463 | return val 464 | } 465 | return parseNumberValue(parseInt(val, 10) * scale) 466 | }).join(' ') 467 | } 468 | 469 | break; 470 | default: 471 | if (style[key] && String(style[key]).includes('px')) { 472 | resultStyle[key] = String(style[key]).replace(/[\d\.]+px/g, (v) => { 473 | return /^[\d\.]+px$/.test(v) ? parseNumberValue(v) : v; 474 | }) 475 | } 476 | resultStyle[key] = resultStyle[key] || style[key] 477 | } 478 | } 479 | 480 | return resultStyle; 481 | }; 482 | 483 | // parse function, return params and content 484 | export const parseFunction = (func) => { 485 | const funcString = func.toString(); 486 | const params = funcString.match(/\([^\(\)]*\)/)[0].slice(1, -1); 487 | const content = funcString.slice( 488 | funcString.indexOf('{') + 1, 489 | funcString.lastIndexOf('}') 490 | ); 491 | return { 492 | params, 493 | content, 494 | }; 495 | }; 496 | 497 | export const toJsString = (str)=>{ 498 | return typeof str === 'string' && str.includes("'") ? `\`${str}\`` : `'${str}'` 499 | } 500 | // parse layer props(static values or expression) 501 | export const parseProps = (value, isReactNode = false) => { 502 | if(Array.isArray(value)){ 503 | return `[${value.map(item => parseProps(item, isReactNode)).join(',')}]` 504 | } 505 | if (typeof value === 'string') { 506 | if (isExpression(value)) { 507 | if (isReactNode) { 508 | return value.slice(1, -1); 509 | } else { 510 | return value.slice(2, -2); 511 | } 512 | } 513 | 514 | if (isReactNode) { 515 | return value; 516 | } else { 517 | return `${toJsString(value)}`; 518 | } 519 | } else if (typeof value === 'function') { 520 | const { params, content } = parseFunction(value); 521 | return `(${params}) => {${content}}`; 522 | } else if (typeof value === 'object') { 523 | if(isJSSlot(value)){ 524 | if(value.params){ 525 | return `(${value.params.join(',')}) => { return ${value.jsx}}` 526 | } 527 | return value.jsx 528 | }else{ 529 | return `{${ Object.keys(value).map(key => `${key}: ${parseProps(value[key])}`).join(',')}}` 530 | } 531 | } else { 532 | return value; 533 | } 534 | }; 535 | 536 | // parse condition: whether render the layer 537 | export const parseCondition = (condition, render) => { 538 | if (typeof condition === 'boolean') { 539 | return `${condition} && ${render}`; 540 | } else if (typeof condition === 'string') { 541 | condition = condition.replace(/this\./, ''); 542 | return `${condition.slice(2, -2)} && ${render}`; 543 | } 544 | }; 545 | 546 | // flexDirection -> flex-direction 547 | export const parseCamelToLine = (string) => { 548 | return string 549 | .split(/(?=[A-Z])/) 550 | .join('-') 551 | .toLowerCase(); 552 | }; 553 | 554 | // style obj -> css 555 | export const generateCSS = (style, prefix) => { 556 | let css = ''; 557 | 558 | for (let layer in style) { 559 | css += `${prefix && prefix !== layer ? '.' + prefix + ' ' : ''}.${layer} {`; 560 | css += generateCssString(style[layer]) 561 | css += '}' 562 | } 563 | 564 | return css; 565 | }; 566 | 567 | /** 568 | * (1)定位属性:position display float left top right bottom overflow clear z-index 569 | (2)自身属性:width height padding border margin background 570 | (3)文字样式:font-family font-size font-style font-weight font-varient color 571 | (4)文本属性:text-align vertical-align text-wrap text-transform text-indent text-decoration letter-spacing word-spacing white-space text-overflow 572 | (5)css3中新增属性:content box-shadow border-radius transform…… 573 | */ 574 | const orderMap = [ 575 | "position", "display", "float", "left", "top", "right", "bottom", 576 | "flex-direction", "justify-content", "align-items", "align-self", "overflow", "clear", "z-index", 577 | "width", "height", "max-width", "max-height", "padding", "padding-bottom", "padding-left", "padding-right", "padding-left", "border", "margin", "margin-top", "margin-bottom", "margin-left", "margin-right", "background", 578 | "background-color", "background-image", "background-size", 579 | "font-family", "font-size", "font-style", "font-weight", "font-varient", "line-height", "color", "text-align", "vertical-align", "text-wrap", "text-transform", "text-indent", "text-decoration", 580 | "letter-spacing", "word-spacing", "white-space", "text-overflow", 581 | "content", "box-shadow", "border-radius", "transform" 582 | ] 583 | // genrate css object string 584 | export const generateCssString = (style) => { 585 | let css = ''; 586 | let array: any[] = []; 587 | 588 | // 缩写margin 589 | const margin = Object.keys(style).filter(item => item.startsWith("margin")); 590 | if (!style['margin'] && margin.length > 2) { 591 | style["margin"] = `${style["marginTop"] || 0} ${style["marginRight"] || 0} ${style["marginBottom"] || 0} ${style["marginLeft"] || 0}` 592 | delete style["marginTop"]; 593 | delete style["marginLeft"]; 594 | delete style["marginBottom"]; 595 | delete style["marginRight"]; 596 | } 597 | 598 | // 缩写 padding 599 | const padding = Object.keys(style).filter(item => item.startsWith("padding")); 600 | if (!style['padding'] && padding.length > 2) { 601 | style["padding"] = `${style["paddingTop"] || 0} ${style["paddingRight"] || 0} ${style["paddingBottom"] || 0} ${style["paddingLeft"] || 0}` 602 | delete style["paddingTop"]; 603 | delete style["paddingLeft"]; 604 | delete style["paddingBottom"]; 605 | delete style["paddingRight"]; 606 | } 607 | 608 | for (let key in style) { 609 | const cssKey = parseCamelToLine(key); 610 | const orderIndex = orderMap.indexOf(cssKey); 611 | array.push({ 612 | key: cssKey, 613 | value: style[key], 614 | index: orderIndex == -1 ? 100 : orderIndex 615 | }) 616 | } 617 | 618 | 619 | array.sort((a, b) => { 620 | return a.index - b.index 621 | }) 622 | 623 | css = array.map(item => { 624 | return `${item.key}: ${item.value};` 625 | }).join(''); 626 | 627 | return css 628 | } 629 | 630 | // 根据 schema 生成 scss 或者 less 631 | export const generateScss = (schema) => { 632 | let scss = ''; 633 | 634 | function walk(json) { 635 | if (json.props.className) { 636 | let className = json.props.className; 637 | scss += `.${className}{`; 638 | scss += `${generateCssString(parseStyle(json.props.style))};`; 639 | } 640 | 641 | if (json.children && json.children.length > 0) { 642 | json.children.forEach((child) => { 643 | if (!['block', 'component', 'page'].includes(child.componentName.toLowerCase())) { 644 | walk(child) 645 | } 646 | }); 647 | } 648 | 649 | if (json.props.className) { 650 | scss += '}'; 651 | } 652 | } 653 | 654 | walk(schema); 655 | 656 | return scss; 657 | }; 658 | 659 | 660 | // parse loop render 661 | export const parseLoop = (loop, loopArg, render, params = {}) => { 662 | let data; 663 | let loopArgItem = (loopArg && loopArg[0]) || 'item'; 664 | let loopArgIndex = (loopArg && loopArg[1]) || 'index'; 665 | 666 | if (Array.isArray(loop)) { 667 | data = toString(loop); 668 | } else if (isExpression(loop)) { 669 | data = loop.slice(2, -2); 670 | } 671 | 672 | // add loop key 673 | const tagEnd = render.match(/^<.+?\s/)[0].length; 674 | render = `${render.slice(0, tagEnd)} key={${loopArgIndex}}${render.slice( 675 | tagEnd 676 | )}`; 677 | 678 | // remove `this` 679 | const re = new RegExp(`this.${loopArgItem}`, 'g'); 680 | render = render.replace(re, loopArgItem); 681 | let stateValue = data; 682 | if (data.match(/this\.state\./)) { 683 | stateValue = `state.${data.split('.').pop()}`; 684 | } 685 | 686 | const formatRender = params['formatRender'] || function (str) { return str }; 687 | return { 688 | hookState: [], 689 | value: `${stateValue}.map((${loopArgItem}, ${loopArgIndex}) => { 690 | return (${formatRender(render)}); 691 | })`, 692 | }; 693 | }; 694 | 695 | // parse state 696 | export const parseState = (states) => { 697 | let stateName = 'state'; 698 | // hooks state 699 | return `const [${stateName}, set${toUpperCaseStart( 700 | stateName 701 | )}] = useState(${toString(JSON.parse(states)) || null});`; 702 | }; 703 | 704 | // replace state 705 | export const replaceState = (render) => { 706 | // remove `this` 707 | let stateName = 'state'; 708 | const re = new RegExp(`this.state`, 'g'); 709 | return render.replace(re, stateName); 710 | }; 711 | 712 | // replace state 713 | export const parseLifeCycles = (schema, init) => { 714 | let lifeCycles: string[] = []; 715 | if (!schema.lifeCycles['_constructor'] && init) { 716 | schema.lifeCycles['_constructor'] = `function _constructor() {}`; 717 | } 718 | 719 | Object.keys(schema.lifeCycles).forEach((name) => { 720 | let { params, content } = parseFunction(schema.lifeCycles[name]); 721 | content = replaceState(content); 722 | switch (name) { 723 | case '_constructor': { 724 | init.push(content); 725 | lifeCycles.unshift(` 726 | // constructor 727 | useState(()=>{ 728 | ${init.join('\n')} 729 | }) 730 | `); 731 | break; 732 | } 733 | case 'componentDidMount': { 734 | lifeCycles.push(` 735 | // componentDidMount 736 | useEffect(()=>{ 737 | ${content} 738 | }, []) 739 | `); 740 | break; 741 | } 742 | case 'componentDidUpdate': { 743 | lifeCycles.push(` 744 | // componentDidUpdate 745 | useEffect(()=>{ 746 | ${content} 747 | }) 748 | `); 749 | break; 750 | } 751 | case 'componentWillUnMount': { 752 | lifeCycles.push(` 753 | // componentWillUnMount 754 | useEffect(()=>{ 755 | return ()=>{ 756 | ${content} 757 | } 758 | }, []) 759 | `); 760 | break; 761 | } 762 | } 763 | }); 764 | return lifeCycles; 765 | }; 766 | 767 | export const existImport = (imports, singleImport) => { 768 | let exist = false; 769 | imports.forEach((item) => { 770 | if (item._import === singleImport) { 771 | exist = true; 772 | } 773 | }); 774 | return exist; 775 | }; 776 | 777 | // parse async dataSource 778 | export const parseDataSource = (data) => { 779 | const name = data.id; 780 | const { uri, method, params } = data.options; 781 | const action = data.type; 782 | let payload = {}; 783 | 784 | 785 | Object.keys(data.options).forEach((key) => { 786 | if (['uri', 'method', 'params'].indexOf(key) === -1) { 787 | payload[key] = toString(data.options[key]); 788 | } 789 | }); 790 | 791 | let comma = isEmptyObj(payload) ? '' : ','; 792 | // params parse should in string template 793 | if (params) { 794 | if (method !== 'GET') { 795 | payload = `${toString(payload).slice(0, -1)} ${comma} body: ${isExpression(params) ? parseProps(params) : toString(params) 796 | }}`; 797 | } else { 798 | payload = `${toString(payload).slice(0, -1)}}`; 799 | } 800 | } else { 801 | payload = toString(payload); 802 | } 803 | 804 | 805 | let result = `{ 806 | return ${action}(${parseProps(uri)}, ${toString(payload)}) 807 | .then((response) => response.json()) 808 | `; 809 | 810 | if (data.dataHandler) { 811 | const { params, content } = parseFunction(data.dataHandler); 812 | result += `.then((${params}) => {${content}}) 813 | .catch((e) => { 814 | console.log('error', e); 815 | }) 816 | `; 817 | } 818 | 819 | result += '}'; 820 | 821 | return { 822 | value: `${name}() ${result}`, 823 | functionName: name, 824 | functionBody: result 825 | }; 826 | }; 827 | 828 | // get children text 829 | export const getText = (schema) => { 830 | let text = ''; 831 | 832 | const getChildrenText = (schema) => { 833 | const type = schema.componentName.toLowerCase(); 834 | if (type === 'text') { 835 | text += parseProps(schema.props.text || schema.text, true).replace( 836 | /\{/g, 837 | '${' 838 | ); 839 | } 840 | 841 | schema.children && 842 | Array.isArray(schema.children) && 843 | schema.children.map((item) => { 844 | getChildrenText(item); 845 | }); 846 | }; 847 | 848 | getChildrenText(schema); 849 | 850 | return text; 851 | }; 852 | 853 | 854 | export const transAnimation = function (animation) { 855 | let keyFrames = ``; 856 | for (let i of animation.keyframes) { 857 | keyFrames += `${((i.offset * 10000) / 100.0).toFixed(0) + '%'} { 858 | ${i.opacity ? 'opacity: '.concat(i.opacity) + ';' : ''} 859 | ${i.transform ? 'transform: '.concat(i.transform) + ';' : ''} 860 | } 861 | `; 862 | } 863 | let keyframes = ` 864 | @keyframes ${animation.name} { 865 | ${keyFrames} 866 | } 867 | `; 868 | return keyframes; 869 | }; 870 | 871 | export const addAnimation = function (schema) { 872 | let animationRes = ``; 873 | traverse(schema, (json) => { 874 | if (json.animation) { 875 | animationRes += transAnimation(json.animation); 876 | } 877 | }) 878 | return animationRes; 879 | }; 880 | 881 | 882 | /** 883 | * constrcut the import string 884 | */ 885 | export const importString = (importsMap) => { 886 | const importStrings: string[] = []; 887 | const subImports: string[] = []; 888 | for (const [packageName, pkgSet] of importsMap) { 889 | const set1 = new Set(), set2 = new Set(); 890 | for (const pkg of pkgSet) { 891 | let exportName = pkg.exportName; 892 | let subName = pkg.subName; 893 | let componentName = pkg.name; 894 | 895 | if (pkg.subName) { 896 | subImports.push(`const ${componentName} = ${exportName}.${subName};`); 897 | } 898 | if (!exportName) { 899 | exportName = componentName 900 | } 901 | if (componentName !== exportName && !pkg.subName) { 902 | exportName = `${exportName} as ${componentName}`; 903 | } 904 | if (pkg.dependence && pkg.dependence.destructuring) { 905 | set2.add(exportName); 906 | } else { 907 | set1.add(exportName); 908 | } 909 | } 910 | const set1Str = [...set1].join(','); 911 | let set2Str = [...set2].join(','); 912 | const dot = set1Str && set2Str ? ',' : ''; 913 | if (set2Str) { 914 | set2Str = `{${set2Str}}`; 915 | } 916 | importStrings.push(`import ${set1Str} ${dot} ${set2Str} from '${packageName}'`); 917 | } 918 | return importStrings.concat(subImports); 919 | } 920 | 921 | export const isObject = (value: any): value is Record => { 922 | return typeof value == 'object' && Array.isArray(value) === false 923 | } 924 | 925 | 926 | export const isJSSlot = (value: unknown): boolean => { 927 | return isObject(value) && value?.type === 'JSSlot' 928 | } 929 | 930 | 931 | const isRecord = (data: unknown): data is Record => { 932 | return typeof data === 'object' && data !== null; 933 | }; 934 | 935 | -------------------------------------------------------------------------------- /test/data.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "taskId": "0D283120-E934-4A60-8E98-460AF583C288", 3 | "fileName": "Card", 4 | "artboardImg": "https://img.alicdn.com/imgextra/i2/O1CN01ugQAbP1JWWz3cTbfc_!!6000000001036-2-tps-714-390.png", 5 | "rect": { 6 | "x": 0, 7 | "y": 0, 8 | "width": 714, 9 | "height": 390 10 | }, 11 | "pluginVersion": "2.3.2", 12 | "componentName": "Page", 13 | "layerProtocols": [ 14 | "group" 15 | ], 16 | "name": "编组 9备份 7", 17 | "reference": "sketch", 18 | "restore_id": "48e826d2-dddd-4cf2-bb5d-c4ee106bbc4b", 19 | "props": { 20 | "style": { 21 | "width": "714px", 22 | "height": "390px" 23 | }, 24 | "className": "mod" 25 | }, 26 | "id": "01b30d10-2d5e-11ec-9614-f1bdff654829", 27 | "children": [ 28 | { 29 | "componentName": "Block", 30 | "props": { 31 | "style": { 32 | "display": "flex", 33 | "position": "relative", 34 | "alignItems": "flex-start", 35 | "flexDirection": "row" 36 | }, 37 | "className": "block1" 38 | }, 39 | "rect": { 40 | "x": 0, 41 | "y": 0, 42 | "width": 714, 43 | "height": 390 44 | }, 45 | "children": [ 46 | { 47 | "componentName": "div", 48 | "props": { 49 | "style": { 50 | "display": "flex", 51 | "position": "relative", 52 | "alignItems": "flex-start", 53 | "flexDirection": "row" 54 | }, 55 | "className": "div2" 56 | }, 57 | "rect": { 58 | "x": 0, 59 | "y": 0, 60 | "width": 714, 61 | "height": 390 62 | }, 63 | "children": [] 64 | } 65 | ] 66 | }, 67 | { 68 | "componentName": "Div", 69 | "id": "1657f48a-9b91-4d99-9d4e-d2b6e4309709", 70 | "props": { 71 | "style": { 72 | "display": "flex", 73 | "position": "relative", 74 | "alignItems": "flex-start", 75 | "flexDirection": "row" 76 | }, 77 | "className": "container-inner" 78 | }, 79 | "rect": { 80 | "x": 0, 81 | "y": 0, 82 | "width": 714, 83 | "height": 390 84 | }, 85 | "children": [ 86 | { 87 | "componentName": "Block", 88 | "fileName": "Header", 89 | "id": "e1d84884-da62-4696-809b-408bf4ae93e4", 90 | "props": { 91 | "style": { 92 | "display": "flex", 93 | "position": "relative", 94 | "alignItems": "flex-start", 95 | "flexDirection": "row", 96 | "borderRadius": "24px", 97 | "backgroundColor": "#EDEDED" 98 | }, 99 | "className": "primary" 100 | }, 101 | "rect": { 102 | "x": 0, 103 | "y": 0, 104 | "width": 714, 105 | "height": 390 106 | }, 107 | "smart": { 108 | "layerProtocol": { 109 | "group": { 110 | "type": "group" 111 | } 112 | } 113 | }, 114 | "selfId": "1D57C450-558D-4B85-877C-ED61FACF8DEA", 115 | "nodeLayerName": "#group#品牌福利", 116 | "children": [ 117 | { 118 | "componentName": "Div", 119 | "id": "0039563a-e78f-44b5-8264-8416a4d2baeb", 120 | "props": { 121 | "style": { 122 | "display": "flex", 123 | "alignItems": "center", 124 | "flexDirection": "row", 125 | "marginTop": "80px" 126 | }, 127 | "className": "wrapper-inner" 128 | }, 129 | "rect": { 130 | "x": 0, 131 | "y": 80, 132 | "width": 714, 133 | "height": 310 134 | }, 135 | "smart": { 136 | "layerProtocol": { 137 | "group": { 138 | "type": "group" 139 | }, 140 | "layout": { 141 | "type": "list", 142 | "row": 3, 143 | "col": 1 144 | }, 145 | "component": { 146 | "type": "slider" 147 | } 148 | }, 149 | "nodeIdentification": { 150 | "baseComponent": [ 151 | "slider" 152 | ] 153 | }, 154 | "nodeCustom": { 155 | "baseComponent": { 156 | "isConfident": true, 157 | "label": "slider", 158 | "score": 0.9958979487419128, 159 | "type": "baseComponent", 160 | "meta": { 161 | "threshold": 0.99 162 | }, 163 | "rect": { 164 | "x": 0, 165 | "y": 80, 166 | "width": 714, 167 | "height": 310 168 | }, 169 | "id": "0039563a-e78f-44b5-8264-8416a4d2baeb", 170 | "selfId": "BF41D7C0-17A7-4B33-88F6-16132CEEE9C4" 171 | } 172 | } 173 | }, 174 | "selfId": "BF41D7C0-17A7-4B33-88F6-16132CEEE9C4", 175 | "nodeLayerName": "#group#编组 3", 176 | "children": [ 177 | { 178 | "componentName": "Div", 179 | "id": "e41c0f6e-72ba-4534-873b-2db3b6a133e8", 180 | "props": { 181 | "style": { 182 | "display": "flex", 183 | "position": "relative", 184 | "alignItems": "center", 185 | "flexDirection": "column", 186 | "marginRight": "2px", 187 | "borderBottomLeftRadius": "24px", 188 | "backgroundColor": "#FFFFFF", 189 | "width": "237px", 190 | "height": "310px", 191 | "overflow": "hidden" 192 | }, 193 | "className": "item-wrapper-i0" 194 | }, 195 | "rect": { 196 | "x": 0, 197 | "y": 80, 198 | "width": 237, 199 | "height": 310 200 | }, 201 | "smart": { 202 | "layerProtocol": { 203 | "group": { 204 | "type": "group" 205 | }, 206 | "repeat": { 207 | "type": "repeat", 208 | "repeatId": "1634264238383575", 209 | "repeatIndex": "0" 210 | }, 211 | "multiStatus": { 212 | "type": "multiStatus", 213 | "protocol": { 214 | "autoDetected": true, 215 | "statusId": "16342642383925341", 216 | "statusIndex": 0, 217 | "statusCount": 3 218 | } 219 | }, 220 | "layout": { 221 | "type": "layout", 222 | "position": "left" 223 | } 224 | } 225 | }, 226 | "selfId": "62599A25-DD15-4012-9DD6-FF47F667D849", 227 | "nodeLayerName": "#group#1", 228 | "children": [ 229 | { 230 | "componentName": "Image", 231 | "id": "01b33425-2d5e-11ec-9614-f1bdff654829", 232 | "props": { 233 | "style": { 234 | "position": "relative", 235 | "marginTop": "12px", 236 | "width": "170px", 237 | "height": "170px" 238 | }, 239 | "src": "https://img.alicdn.com/imgextra/i2/O1CN012avgI31PWQaaou5AA_!!6000000001848-2-tps-340-340.png", 240 | "className": "item" 241 | }, 242 | "rect": { 243 | "x": 34, 244 | "y": 92, 245 | "width": 170, 246 | "height": 170 247 | }, 248 | "selfId": "7E391F47-8B27-49CC-9EC9-682B41BFB396", 249 | "nodeLayerName": "#merge#图片+蒙版" 250 | }, 251 | { 252 | "componentName": "Block", 253 | "id": "e8048804-8111-4b2a-8bc7-141410187b19", 254 | "props": { 255 | "style": { 256 | "display": "flex", 257 | "position": "absolute", 258 | "top": "172px", 259 | "alignItems": "center", 260 | "alignSelf": "center", 261 | "flexDirection": "row", 262 | "justifyContent": "center", 263 | "borderWidth": "2px", 264 | "borderStyle": "solid", 265 | "borderRadius": "24px", 266 | "borderColor": "#E1E1E1", 267 | "backgroundColor": "#FFFFFF", 268 | "width": "130px", 269 | "height": "48px" 270 | }, 271 | "className": "view" 272 | }, 273 | "rect": { 274 | "x": 54, 275 | "y": 252, 276 | "width": 130, 277 | "height": 48 278 | }, 279 | "smart": { 280 | "layerProtocol": { 281 | "group": { 282 | "type": "group" 283 | }, 284 | "multiStatus": { 285 | "type": "multiStatus", 286 | "protocol": { 287 | "autoDetected": true, 288 | "statusId": "16342642383933936", 289 | "statusIndex": 0, 290 | "statusCount": 1 291 | } 292 | } 293 | } 294 | }, 295 | "selfId": "33360FF6-93AA-4F56-8BE3-3F07506769AC", 296 | "nodeLayerName": "#group#编组 3", 297 | "children": [ 298 | { 299 | "componentName": "Image", 300 | "id": "01b33423-2d5e-11ec-9614-f1bdff654829", 301 | "props": { 302 | "style": { 303 | "width": "84px", 304 | "height": "42px" 305 | }, 306 | "src": "https://img.alicdn.com/imgextra/i3/O1CN01doUzCW22CWODauZhZ_!!6000000007084-2-tps-168-84.png", 307 | "className": "shop-logo" 308 | }, 309 | "rect": { 310 | "x": 77, 311 | "y": 255, 312 | "width": 84, 313 | "height": 42 314 | }, 315 | "selfId": "4462C8BF-C89F-4060-ADD2-E974A174821B", 316 | "nodeLayerName": "#merge#Bitmap备份" 317 | } 318 | ] 319 | }, 320 | { 321 | "componentName": "Text", 322 | "id": "01b35b33-2d5e-11ec-9614-f1bdff654829", 323 | "props": { 324 | "style": { 325 | "position": "relative", 326 | "marginTop": "48px", 327 | "maxWidth": "225px", 328 | "height": "28px", 329 | "overflow": "hidden", 330 | "textOverflow": "ellipsis", 331 | "lineHeight": "28px", 332 | "whiteSpace": "nowrap", 333 | "color": "#111111", 334 | "fontFamily": "PingFang SC", 335 | "fontSize": "24px", 336 | "fontWeight": 500 337 | }, 338 | "lines": 1, 339 | "className": "location", 340 | "text": "{{item.title}}" 341 | }, 342 | "rect": { 343 | "x": 83, 344 | "y": 310, 345 | "width": 72, 346 | "height": 28 347 | }, 348 | "selfId": "638858BB-612F-43BE-A2E2-2C2B8599E1670", 349 | "nodeLayerName": "马歇尔" 350 | }, 351 | { 352 | "componentName": "Div", 353 | "id": "128c8f8c-4897-436e-8be3-8f247b2f0efb", 354 | "props": { 355 | "style": { 356 | "display": "flex", 357 | "position": "relative", 358 | "alignItems": "center", 359 | "flexDirection": "row", 360 | "marginTop": "8px", 361 | "height": "30px" 362 | }, 363 | "className": "view-1" 364 | }, 365 | "rect": { 366 | "x": 39, 367 | "y": 346, 368 | "width": 160, 369 | "height": 30 370 | }, 371 | "smart": { 372 | "layerProtocol": { 373 | "group": { 374 | "type": "group" 375 | } 376 | } 377 | }, 378 | "selfId": "9BF237F6-0F7F-4C57-864A-1B889307DE90", 379 | "nodeLayerName": "#group#编组 3", 380 | "children": [ 381 | { 382 | "componentName": "Text", 383 | "id": "01b35b32-2d5e-11ec-9614-f1bdff654829", 384 | "props": { 385 | "style": { 386 | "marginRight": "6px", 387 | "lineHeight": "26px", 388 | "whiteSpace": "nowrap", 389 | "color": "#ff0036", 390 | "fontFamily": "PingFang SC", 391 | "fontSize": "26px", 392 | "fontWeight": 500 393 | }, 394 | "text": "满300送40", 395 | "lines": 1, 396 | "className": "title" 397 | }, 398 | "rect": { 399 | "x": 39, 400 | "y": 346, 401 | "width": 130, 402 | "height": 30 403 | }, 404 | "selfId": "F84B65C6-9B6C-4929-8420-B670A165FF820", 405 | "nodeLayerName": "满300送40", 406 | "smart": { 407 | "layerProtocol": { 408 | "layout": { 409 | "type": "layout", 410 | "position": "left" 411 | } 412 | } 413 | } 414 | }, 415 | { 416 | "componentName": "Image", 417 | "id": "01b35b31-2d5e-11ec-9614-f1bdff654829", 418 | "props": { 419 | "style": { 420 | "width": "24px", 421 | "height": "24px" 422 | }, 423 | "src": "https://img.alicdn.com/imgextra/i4/O1CN01KOYI9a1lud45JqE22_!!6000000004879-2-tps-48-48.png", 424 | "className": "icon-right" 425 | }, 426 | "rect": { 427 | "x": 175, 428 | "y": 349, 429 | "width": 24, 430 | "height": 24 431 | }, 432 | "selfId": "DB023BD4-828C-47DC-A4C0-B9E9C9C4D1D8", 433 | "nodeLayerName": "#merge#编组 3", 434 | "smart": { 435 | "layerProtocol": { 436 | "field": { 437 | "type": "right" 438 | }, 439 | "layout": { 440 | "type": "layout", 441 | "position": "right" 442 | } 443 | }, 444 | "nodeIdentification": { 445 | "fieldBind": [ 446 | "right" 447 | ] 448 | }, 449 | "nodeCustom": { 450 | "fieldBind": { 451 | "confidential": 0.793341338634491, 452 | "isConfident": true, 453 | "label": "right", 454 | "type": "fieldBind" 455 | } 456 | } 457 | } 458 | } 459 | ] 460 | } 461 | ], 462 | "loop": "{{this.state.items}}", 463 | "loopArgs": [ 464 | "item", 465 | "" 466 | ] 467 | } 468 | ] 469 | } 470 | ], 471 | "state": { 472 | "items": [ 473 | { 474 | "title": "标题1" 475 | }, 476 | { 477 | "title": "标题2" 478 | }, 479 | { 480 | "title": "标题3" 481 | } 482 | ] 483 | } 484 | }, 485 | { 486 | "componentName": "Image", 487 | "id": "01b30d11-2d5e-11ec-9614-f1bdff654829", 488 | "props": { 489 | "style": { 490 | "position": "absolute", 491 | "top": "0px", 492 | "left": "0px", 493 | "width": "714px", 494 | "height": "80px" 495 | }, 496 | "src": "https://img.alicdn.com/imgextra/i4/O1CN015bxNgd1PWszCyuBBr_!!6000000001849-2-tps-1428-160.png", 497 | "className": "floor-bg" 498 | }, 499 | "rect": { 500 | "x": 0, 501 | "y": 0, 502 | "width": 714, 503 | "height": 80 504 | }, 505 | "selfId": "317BE8EE-A5A9-46A8-8FFE-99CE20CB221D", 506 | "nodeLayerName": "#merge#编组 4" 507 | } 508 | ], 509 | "condition": "{{this.state.enable}}" 510 | } 511 | ], 512 | "imgcook": { 513 | "restore_id": "48e826d2-dddd-4cf2-bb5d-c4ee106bbc4b", 514 | "dslConfig": { 515 | "globalCss": false, 516 | "inlineStyle": "import", 517 | "componentStyle": "component", 518 | "outputStyle": "project", 519 | "cssUnit": "px", 520 | "cssStyle": "kebabCase", 521 | "dsl": "react-standard", 522 | "dslName": "React 开发规范", 523 | "htmlType": "html静态", 524 | "htmlFontSize": 16 525 | } 526 | }, 527 | "dataSource": { 528 | "list": [ 529 | { 530 | "id": "id1", 531 | "isInit": true, 532 | "type": "fetch", 533 | "options": { 534 | "method": "GET", 535 | "params": {}, 536 | "uri": "https://pre-apistudio.alibaba-inc.com/openapi/mock_api?projectId=5fc611b47f32555337dc5540&api=mtop.alsc.pos.bill.customer.batchqueryanalysis&errorCode=date¶m={%22analysisIds%22:[%22OVERALL_FLOW_OVERVIEW%22,%22INSIGHT_OVERVIEW%22,%22MEMBER_CHANNEL%22,%22CUSTOMER_CHANNEL%22],%22intervalType%22:%22LAST_SEVEN_DAYS%22,%22intervalTime%22:%222021-05-16%22,%22bizChannel%22:%22%E5%85%A8%E9%83%A8%22}" 537 | } 538 | } 539 | ] 540 | }, 541 | "css": "/************************************************************\n** 组件全局样式 **\n** 将按照从前到后按顺序提取类名,出码生成多类名 **\n************************************************************/\n\nhtml {\n font-size: 24px;\n}\n\nbody {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans',\n 'Droid Sans', 'Helvetica Neue', 'Microsoft Yahei', sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n* {\n box-sizing: border-box;\n flex-shrink: 0;\n}\n\n#root {\n width: 100vw;\n height: 100vh;\n}\n\n\n.flex-row {\n display: flex;\n flex-direction: row;\n}\n\n.flex-col {\n display: flex;\n flex-direction: column;\n}\n\n.justify-start {\n display: flex;\n justify-content: flex-start;\n}\n\n.justify-center {\n display: flex;\n justify-content: center;\n}\n\n.justify-end {\n display: flex;\n justify-content: flex-end;\n}\n\n.justify-evenly {\n display: flex;\n justify-content: space-evenly;\n}\n\n.justify-around {\n display: flex;\n justify-content: space-around;\n}\n\n.justify-between {\n display: flex;\n justify-content: space-between;\n}\n\n.items-start {\n display: flex;\n align-items: flex-start;\n}\n\n.items-center {\n display: flex;\n align-items: center;\n}\n\n.items-end {\n display: flex;\n align-items: flex-end;\n}\n\n.height-78{\n height: 78px;\n}\n", 542 | "state": { 543 | "enable": true 544 | }, 545 | "lifeCycles": {} 546 | } -------------------------------------------------------------------------------- /test/animation.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "componentName": "Block", 3 | "id": "7adeb880-2028-11ec-82f6-bbadaeddfccc", 4 | "props": { 5 | "style": { 6 | "display": "flex", 7 | "flexDirection": "row", 8 | "alignItems": "flex-start", 9 | "width": "565px", 10 | "height": "623px", 11 | "position": "relative", 12 | "animationIterationCount": 1, 13 | "animationTimingFunction": "linear", 14 | "animationDuration": "1280ms", 15 | "animationName": "swing" 16 | }, 17 | "className": "mod" 18 | }, 19 | "taskId": "16AF9B5E-69BB-4C18-B31C-1F46A3D1ED7D", 20 | "artboardImg": "https://img.alicdn.com/imgextra/i3/O1CN01gGC3JF1cyRsHOktYN_!!6000000003669-2-tps-490-624.png", 21 | "rect": { 22 | "x": -72, 23 | "y": 0, 24 | "width": 565, 25 | "height": 623 26 | }, 27 | "animation": { 28 | "layerid": "8544227E-2107-4EEE-AF40-864F20F9662D", 29 | "name": "swing", 30 | "keyframes": [{ 31 | "WebkitTransform": "rotate3d(0, 0, 1, 15deg)", 32 | "transform": "rotate3d(0, 0, 1, 15deg)", 33 | "offset": "0.2", 34 | "easing": "ease" 35 | }, { 36 | "WebkitTransform": "rotate3d(0, 0, 1, -10deg)", 37 | "transform": "rotate3d(0, 0, 1, -10deg)", 38 | "offset": "0.4", 39 | "easing": "ease" 40 | }, { 41 | "WebkitTransform": "rotate3d(0, 0, 1, 5deg)", 42 | "transform": "rotate3d(0, 0, 1, 5deg)", 43 | "offset": "0.6", 44 | "easing": "ease" 45 | }, { 46 | "WebkitTransform": "rotate3d(0, 0, 1, -5deg)", 47 | "transform": "rotate3d(0, 0, 1, -5deg)", 48 | "offset": "0.8", 49 | "easing": "ease" 50 | }, { 51 | "WebkitTransform": "rotate3d(0, 0, 1, 0deg)", 52 | "transform": "rotate3d(0, 0, 1, 0deg)", 53 | "offset": "1", 54 | "easing": "ease" 55 | }], 56 | "timing": { 57 | "duration": 1280, 58 | "iterations": "infinite", 59 | "easing": "linear" 60 | } 61 | }, 62 | "pluginVersion": "4.0.22", 63 | "smart": { 64 | "layerProtocol": { 65 | "group": { 66 | "type": "group" 67 | } 68 | } 69 | }, 70 | "layerProtocols": ["group"], 71 | "name": "#group#animation-swing#编组 21", 72 | "reference": "sketch", 73 | "restore_id": "5e0484c8-2b4a-497d-afef-00d4cfafcc8b", 74 | "children": [{ 75 | "componentName": "View", 76 | "id": "7adf54c7-2028-11ec-82f6-bbadaeddfccc", 77 | "props": { 78 | "style": { 79 | "display": "flex", 80 | "position": "relative", 81 | "alignItems": "flex-start", 82 | "flexDirection": "row", 83 | "justifyContent": "center", 84 | "marginLeft": "94px", 85 | "borderTopLeftRadius": "18px", 86 | "borderTopRightRadius": "18px", 87 | "backgroundImage": "linear-gradient(148deg, #EFEDED 0%, #B4B4B4 76%)", 88 | "width": "438px", 89 | "height": "461px" 90 | }, 91 | "className": "primary" 92 | }, 93 | "rect": { 94 | "x": 22, 95 | "y": 0, 96 | "width": 438, 97 | "height": 461 98 | }, 99 | "selfId": "C3104608-D699-44F8-9C96-5172EFA24BA1", 100 | "nodeLayerName": "矩形", 101 | "children": [{ 102 | "componentName": "Picture", 103 | "id": "7adf2db7-2028-11ec-82f6-bbadaeddfccc", 104 | "props": { 105 | "style": { 106 | "position": "absolute", 107 | "bottom": "12px", 108 | "left": "-14px", 109 | "width": "405px", 110 | "height": "310px" 111 | }, 112 | "className": "entry-pic", 113 | "source": { 114 | "uri": "https://img.alicdn.com/imgextra/i1/O1CN01iA6RUt1giCHh4uLfX_!!6000000004175-2-tps-810-620.png" 115 | }, 116 | "autoScaling": false, 117 | "autoWebp": false 118 | }, 119 | "rect": { 120 | "x": 8, 121 | "y": 139, 122 | "width": 405, 123 | "height": 310 124 | }, 125 | "selfId": "53BB11EC-E160-4D2D-9691-F801197D986A", 126 | "nodeLayerName": "auto-#merge#" 127 | }, { 128 | "componentName": "Picture", 129 | "id": "7adeb881-2028-11ec-82f6-bbadaeddfccc", 130 | "props": { 131 | "style": { 132 | "position": "relative", 133 | "marginTop": "13px", 134 | "marginRight": "87px", 135 | "width": "201px", 136 | "height": "76px" 137 | }, 138 | "className": "action-bg", 139 | "source": { 140 | "uri": "https://img.alicdn.com/imgextra/i4/O1CN01xCglio1fb2Tj4dGI7_!!6000000004024-2-tps-404-152.png" 141 | }, 142 | "autoScaling": false, 143 | "autoWebp": false 144 | }, 145 | "rect": { 146 | "x": 46, 147 | "y": 13, 148 | "width": 201, 149 | "height": 76 150 | }, 151 | "selfId": "A8A1D526-0456-4D5D-A82E-1EFAE0DBAE5F", 152 | "nodeLayerName": "auto-#merge#-autogroup" 153 | }, { 154 | "componentName": "Picture", 155 | "id": "7adf2db5-2028-11ec-82f6-bbadaeddfccc", 156 | "props": { 157 | "style": { 158 | "position": "relative", 159 | "marginTop": "20px", 160 | "width": "106px", 161 | "height": "52px" 162 | }, 163 | "className": "shop-logo", 164 | "source": { 165 | "uri": "https://img.alicdn.com/imgextra/i2/O1CN01fRLVpN1koqUi7VnM2_!!6000000004731-2-tps-214-104.png" 166 | }, 167 | "autoScaling": false, 168 | "autoWebp": false 169 | }, 170 | "rect": { 171 | "x": 334, 172 | "y": 20, 173 | "width": 106, 174 | "height": 52 175 | }, 176 | "selfId": "B4121643-E37C-4158-A6FF-9EE461B9E1CE", 177 | "nodeLayerName": "auto-#merge#" 178 | }] 179 | }, { 180 | "componentName": "View", 181 | "id": "7adf54c6-2028-11ec-82f6-bbadaeddfccc", 182 | "props": { 183 | "style": { 184 | "position": "absolute", 185 | "top": "140px", 186 | "right": "28px", 187 | "width": "437px", 188 | "height": "347px", 189 | "overflow": "hidden" 190 | }, 191 | "className": "empty" 192 | }, 193 | "rect": { 194 | "x": 28, 195 | "y": 140, 196 | "width": 437, 197 | "height": 347 198 | }, 199 | "selfId": "053213F4-9555-49FF-BED4-FE1E7E063C3B", 200 | "nodeLayerName": "蒙版", 201 | "condition": true 202 | }, { 203 | "componentName": "Picture", 204 | "id": "7adf54c5-2028-11ec-82f6-bbadaeddfccc", 205 | "props": { 206 | "style": { 207 | "position": "absolute", 208 | "top": "194px", 209 | "left": "113px", 210 | "animationIterationCount": "infinite", 211 | "animationTimingFunction": "linear", 212 | "animationDuration": "1280ms", 213 | "animationName": "swing", 214 | "width": "197px", 215 | "height": "340px" 216 | }, 217 | "className": "item-long", 218 | "source": { 219 | "uri": "https://img.alicdn.com/imgextra/i2/O1CN01IcS9wC1I6azO02qbt_!!6000000000844-2-tps-396-682.png" 220 | }, 221 | "autoScaling": false, 222 | "autoWebp": false 223 | }, 224 | "rect": { 225 | "x": 41, 226 | "y": 194, 227 | "width": 197, 228 | "height": 340 229 | }, 230 | "selfId": "4EF0C111-5F9D-4943-B7E7-262BA25239EF", 231 | "nodeLayerName": "#group#animation-swing#位图", 232 | "animation": { 233 | "layerid": "912CEF68-7509-438C-8927-D2A52DD5F9C9", 234 | "name": "swing", 235 | "keyframes": [{ 236 | "WebkitTransform": "rotate3d(0, 0, 1, 15deg)", 237 | "transform": "rotate3d(0, 0, 1, 15deg)", 238 | "offset": "0.2", 239 | "easing": "ease" 240 | }, { 241 | "WebkitTransform": "rotate3d(0, 0, 1, -10deg)", 242 | "transform": "rotate3d(0, 0, 1, -10deg)", 243 | "offset": "0.4", 244 | "easing": "ease" 245 | }, { 246 | "WebkitTransform": "rotate3d(0, 0, 1, 5deg)", 247 | "transform": "rotate3d(0, 0, 1, 5deg)", 248 | "offset": "0.6", 249 | "easing": "ease" 250 | }, { 251 | "WebkitTransform": "rotate3d(0, 0, 1, -5deg)", 252 | "transform": "rotate3d(0, 0, 1, -5deg)", 253 | "offset": "0.8", 254 | "easing": "ease" 255 | }, { 256 | "WebkitTransform": "rotate3d(0, 0, 1, 0deg)", 257 | "transform": "rotate3d(0, 0, 1, 0deg)", 258 | "offset": "1", 259 | "easing": "ease" 260 | }], 261 | "timing": { 262 | "duration": 1280, 263 | "iterations": "infinite", 264 | "easing": "linear" 265 | } 266 | } 267 | }, { 268 | "componentName": "Picture", 269 | "id": "7adf54c4-2028-11ec-82f6-bbadaeddfccc", 270 | "props": { 271 | "style": { 272 | "position": "absolute", 273 | "top": "157px", 274 | "left": "172px", 275 | "animationIterationCount": "infinite", 276 | "animationTimingFunction": "ease", 277 | "animationDuration": "1280ms", 278 | "animationName": "swing", 279 | "width": "182px", 280 | "height": "354px" 281 | }, 282 | "className": "product-long", 283 | "source": { 284 | "uri": "https://img.alicdn.com/imgextra/i4/O1CN01TC595Q1RWXy7mXO57_!!6000000002119-2-tps-368-712.png" 285 | }, 286 | "autoScaling": false, 287 | "autoWebp": false 288 | }, 289 | "rect": { 290 | "x": 100, 291 | "y": 157, 292 | "width": 182, 293 | "height": 354 294 | }, 295 | "selfId": "9B804B77-22E5-4129-BE58-8DF4EE7F2BE7", 296 | "nodeLayerName": "#group#animation-swing#位图", 297 | "animation": { 298 | "layerid": "39E1B106-2975-4EFA-BF54-B5F06BF5144E", 299 | "name": "swing", 300 | "keyframes": [{ 301 | "WebkitTransform": "rotate3d(0, 0, 1, 15deg)", 302 | "transform": "rotate3d(0, 0, 1, 15deg)", 303 | "offset": "0.2", 304 | "easing": "ease" 305 | }, { 306 | "WebkitTransform": "rotate3d(0, 0, 1, -10deg)", 307 | "transform": "rotate3d(0, 0, 1, -10deg)", 308 | "offset": "0.4", 309 | "easing": "ease" 310 | }, { 311 | "WebkitTransform": "rotate3d(0, 0, 1, 5deg)", 312 | "transform": "rotate3d(0, 0, 1, 5deg)", 313 | "offset": "0.6", 314 | "easing": "ease" 315 | }, { 316 | "WebkitTransform": "rotate3d(0, 0, 1, -5deg)", 317 | "transform": "rotate3d(0, 0, 1, -5deg)", 318 | "offset": "0.8", 319 | "easing": "ease" 320 | }, { 321 | "WebkitTransform": "rotate3d(0, 0, 1, 0deg)", 322 | "transform": "rotate3d(0, 0, 1, 0deg)", 323 | "offset": "1", 324 | "easing": "ease" 325 | }], 326 | "timing": { 327 | "duration": 1280, 328 | "iterations": "infinite", 329 | "easing": "ease" 330 | } 331 | } 332 | }, { 333 | "componentName": "Picture", 334 | "id": "7adf54c3-2028-11ec-82f6-bbadaeddfccc", 335 | "props": { 336 | "style": { 337 | "position": "absolute", 338 | "top": "154px", 339 | "right": "173px", 340 | "animationIterationCount": "infinite", 341 | "animationTimingFunction": "ease", 342 | "animationDuration": "1280ms", 343 | "animationName": "swing", 344 | "width": "179px", 345 | "height": "388px" 346 | }, 347 | "className": "item-long-1", 348 | "source": { 349 | "uri": "https://img.alicdn.com/imgextra/i3/O1CN01sNPqa11h9DeSojhL8_!!6000000004234-2-tps-362-780.png" 350 | }, 351 | "autoScaling": false, 352 | "autoWebp": false 353 | }, 354 | "rect": { 355 | "x": 141, 356 | "y": 154, 357 | "width": 179, 358 | "height": 388 359 | }, 360 | "selfId": "5689F4E9-F4F3-44F6-8CFB-79E981CF76D9", 361 | "nodeLayerName": "#group#animation-swing#位图", 362 | "animation": { 363 | "layerid": "806CA3E2-2C4C-48CB-8656-341680E278B0", 364 | "name": "swing", 365 | "keyframes": [{ 366 | "WebkitTransform": "rotate3d(0, 0, 1, 15deg)", 367 | "transform": "rotate3d(0, 0, 1, 15deg)", 368 | "offset": "0.2", 369 | "easing": "ease" 370 | }, { 371 | "WebkitTransform": "rotate3d(0, 0, 1, -10deg)", 372 | "transform": "rotate3d(0, 0, 1, -10deg)", 373 | "offset": "0.4", 374 | "easing": "ease" 375 | }, { 376 | "WebkitTransform": "rotate3d(0, 0, 1, 5deg)", 377 | "transform": "rotate3d(0, 0, 1, 5deg)", 378 | "offset": "0.6", 379 | "easing": "ease" 380 | }, { 381 | "WebkitTransform": "rotate3d(0, 0, 1, -5deg)", 382 | "transform": "rotate3d(0, 0, 1, -5deg)", 383 | "offset": "0.8", 384 | "easing": "ease" 385 | }, { 386 | "WebkitTransform": "rotate3d(0, 0, 1, 0deg)", 387 | "transform": "rotate3d(0, 0, 1, 0deg)", 388 | "offset": "1", 389 | "easing": "ease" 390 | }], 391 | "timing": { 392 | "duration": 1280, 393 | "iterations": "infinite", 394 | "easing": "ease" 395 | } 396 | } 397 | }, { 398 | "componentName": "Picture", 399 | "id": "7adf54c2-2028-11ec-82f6-bbadaeddfccc", 400 | "props": { 401 | "style": { 402 | "position": "absolute", 403 | "right": "122px", 404 | "bottom": "61px", 405 | "animationIterationCount": "infinite", 406 | "animationTimingFunction": "linear", 407 | "animationDuration": "1280ms", 408 | "animationName": "swing", 409 | "width": "230px", 410 | "height": "406px" 411 | }, 412 | "className": "product-long-1", 413 | "source": { 414 | "uri": "https://img.alicdn.com/imgextra/i1/O1CN01weu6uV1gD3Lkir9e0_!!6000000004107-2-tps-462-814.png" 415 | }, 416 | "autoScaling": false, 417 | "autoWebp": false 418 | }, 419 | "rect": { 420 | "x": 141, 421 | "y": 156, 422 | "width": 230, 423 | "height": 406 424 | }, 425 | "selfId": "12D1B83A-4764-4C73-8DE5-4649B4B95008", 426 | "nodeLayerName": "#group#animation-swing#位图", 427 | "animation": { 428 | "layerid": "A77A3610-5F80-4BA7-8A0C-B4AD5D36D6A2", 429 | "name": "swing", 430 | "keyframes": [{ 431 | "WebkitTransform": "rotate3d(0, 0, 1, 15deg)", 432 | "transform": "rotate3d(0, 0, 1, 15deg)", 433 | "offset": "0.2", 434 | "easing": "ease" 435 | }, { 436 | "WebkitTransform": "rotate3d(0, 0, 1, -10deg)", 437 | "transform": "rotate3d(0, 0, 1, -10deg)", 438 | "offset": "0.4", 439 | "easing": "ease" 440 | }, { 441 | "WebkitTransform": "rotate3d(0, 0, 1, 5deg)", 442 | "transform": "rotate3d(0, 0, 1, 5deg)", 443 | "offset": "0.6", 444 | "easing": "ease" 445 | }, { 446 | "WebkitTransform": "rotate3d(0, 0, 1, -5deg)", 447 | "transform": "rotate3d(0, 0, 1, -5deg)", 448 | "offset": "0.8", 449 | "easing": "ease" 450 | }, { 451 | "WebkitTransform": "rotate3d(0, 0, 1, 0deg)", 452 | "transform": "rotate3d(0, 0, 1, 0deg)", 453 | "offset": "1", 454 | "easing": "ease" 455 | }], 456 | "timing": { 457 | "duration": 1280, 458 | "iterations": "infinite", 459 | "easing": "linear" 460 | } 461 | } 462 | }, { 463 | "componentName": "Picture", 464 | "id": "7adf54c1-2028-11ec-82f6-bbadaeddfccc", 465 | "props": { 466 | "style": { 467 | "position": "absolute", 468 | "right": "56px", 469 | "bottom": "37px", 470 | "animationIterationCount": "infinite", 471 | "animationTimingFunction": "ease", 472 | "animationDuration": "1280ms", 473 | "animationName": "swing", 474 | "width": "302px", 475 | "height": "417px" 476 | }, 477 | "className": "item-long-2", 478 | "source": { 479 | "uri": "https://img.alicdn.com/imgextra/i4/O1CN01F3CHMW1LwkwSVqkE7_!!6000000001364-2-tps-608-838.png" 480 | }, 481 | "autoScaling": false, 482 | "autoWebp": false 483 | }, 484 | "rect": { 485 | "x": 135, 486 | "y": 169, 487 | "width": 302, 488 | "height": 417 489 | }, 490 | "selfId": "501A1B1C-AEF8-452B-A388-C0C85F98B76D", 491 | "nodeLayerName": "#group#animation-swing#位图", 492 | "animation": { 493 | "layerid": "1A6F7BCE-FF17-4BBE-8494-0A0EFC3775C3", 494 | "name": "swing", 495 | "keyframes": [{ 496 | "WebkitTransform": "rotate3d(0, 0, 1, 15deg)", 497 | "transform": "rotate3d(0, 0, 1, 15deg)", 498 | "offset": "0.2", 499 | "easing": "ease" 500 | }, { 501 | "WebkitTransform": "rotate3d(0, 0, 1, -10deg)", 502 | "transform": "rotate3d(0, 0, 1, -10deg)", 503 | "offset": "0.4", 504 | "easing": "ease" 505 | }, { 506 | "WebkitTransform": "rotate3d(0, 0, 1, 5deg)", 507 | "transform": "rotate3d(0, 0, 1, 5deg)", 508 | "offset": "0.6", 509 | "easing": "ease" 510 | }, { 511 | "WebkitTransform": "rotate3d(0, 0, 1, -5deg)", 512 | "transform": "rotate3d(0, 0, 1, -5deg)", 513 | "offset": "0.8", 514 | "easing": "ease" 515 | }, { 516 | "WebkitTransform": "rotate3d(0, 0, 1, 0deg)", 517 | "transform": "rotate3d(0, 0, 1, 0deg)", 518 | "offset": "1", 519 | "easing": "ease" 520 | }], 521 | "timing": { 522 | "duration": 1280, 523 | "iterations": "infinite", 524 | "easing": "ease" 525 | } 526 | } 527 | }, { 528 | "componentName": "Picture", 529 | "id": "7adf54c0-2028-11ec-82f6-bbadaeddfccc", 530 | "props": { 531 | "style": { 532 | "position": "absolute", 533 | "right": "0px", 534 | "bottom": "20px", 535 | "animationIterationCount": "infinite", 536 | "animationTimingFunction": "linear", 537 | "animationDuration": "1280ms", 538 | "animationName": "swing", 539 | "width": "392px", 540 | "height": "251px" 541 | }, 542 | "className": "entry-pic-1", 543 | "source": { 544 | "uri": "https://img.alicdn.com/imgextra/i4/O1CN01kfFLkC1d8yyub56ck_!!6000000003692-2-tps-786-506.png" 545 | }, 546 | "autoScaling": false, 547 | "autoWebp": false 548 | }, 549 | "rect": { 550 | "x": 101, 551 | "y": 352, 552 | "width": 392, 553 | "height": 251 554 | }, 555 | "selfId": "AA1440EF-37B4-43B6-B7D0-8063F312A7AA", 556 | "nodeLayerName": "#group#animation-swing#位图", 557 | "animation": { 558 | "layerid": "381C715B-62D6-4859-BA05-233F65238D33", 559 | "name": "swing", 560 | "keyframes": [{ 561 | "WebkitTransform": "rotate3d(0, 0, 1, 15deg)", 562 | "transform": "rotate3d(0, 0, 1, 15deg)", 563 | "offset": "0.2", 564 | "easing": "ease" 565 | }, { 566 | "WebkitTransform": "rotate3d(0, 0, 1, -10deg)", 567 | "transform": "rotate3d(0, 0, 1, -10deg)", 568 | "offset": "0.4", 569 | "easing": "ease" 570 | }, { 571 | "WebkitTransform": "rotate3d(0, 0, 1, 5deg)", 572 | "transform": "rotate3d(0, 0, 1, 5deg)", 573 | "offset": "0.6", 574 | "easing": "ease" 575 | }, { 576 | "WebkitTransform": "rotate3d(0, 0, 1, -5deg)", 577 | "transform": "rotate3d(0, 0, 1, -5deg)", 578 | "offset": "0.8", 579 | "easing": "ease" 580 | }, { 581 | "WebkitTransform": "rotate3d(0, 0, 1, 0deg)", 582 | "transform": "rotate3d(0, 0, 1, 0deg)", 583 | "offset": "1", 584 | "easing": "ease" 585 | }], 586 | "timing": { 587 | "duration": 1280, 588 | "iterations": "infinite", 589 | "easing": "linear" 590 | } 591 | } 592 | }, { 593 | "componentName": "View", 594 | "id": "ebce8372-c549-402a-b9d5-223f1ced6f15", 595 | "props": { 596 | "style": { 597 | "display": "flex", 598 | "position": "absolute", 599 | "top": "139px", 600 | "right": "18px", 601 | "alignItems": "center", 602 | "flexDirection": "row", 603 | "width": "466px", 604 | "height": "338px" 605 | }, 606 | "className": "layer-wrapper" 607 | }, 608 | "rect": { 609 | "x": 9, 610 | "y": 139, 611 | "width": 466, 612 | "height": 338 613 | }, 614 | "children": [{ 615 | "componentName": "Picture", 616 | "id": "7adf2dbb-2028-11ec-82f6-bbadaeddfccc", 617 | "props": { 618 | "style": { 619 | "position": "absolute", 620 | "top": "0px", 621 | "left": "0px", 622 | "width": "466px", 623 | "height": "338px", 624 | "overflow": "hidden" 625 | }, 626 | "className": "layer", 627 | "source": { 628 | "uri": "https://img.alicdn.com/imgextra/i4/O1CN010qI7m51VXhQsQqmgF_!!6000000002663-2-tps-932-676.png" 629 | }, 630 | "autoScaling": false, 631 | "autoWebp": false 632 | }, 633 | "rect": { 634 | "x": 9, 635 | "y": 139, 636 | "width": 466, 637 | "height": 338 638 | }, 639 | "selfId": "69F77E0D-8BEA-4891-97AF-133327A096E2", 640 | "nodeLayerName": "蒙版" 641 | }, { 642 | "componentName": "Picture", 643 | "id": "7adf2db9-2028-11ec-82f6-bbadaeddfccc", 644 | "props": { 645 | "style": { 646 | "position": "absolute", 647 | "top": "1px", 648 | "right": "-2px", 649 | "width": "3px", 650 | "height": "309px" 651 | }, 652 | "className": "vertical-line", 653 | "source": { 654 | "uri": "https://img.alicdn.com/imgextra/i2/O1CN01LMbnMX1p1RrABGVSW_!!6000000005300-2-tps-6-618.png" 655 | }, 656 | "autoScaling": false, 657 | "autoWebp": false 658 | }, 659 | "rect": { 660 | "x": 474, 661 | "y": 140, 662 | "width": 3, 663 | "height": 309 664 | }, 665 | "selfId": "6E7D2D9E-29BD-4204-9C2A-F3BF7BE3AA07", 666 | "nodeLayerName": "矩形备份 5" 667 | }] 668 | }, { 669 | "componentName": "Picture", 670 | "id": "7adf2dba-2028-11ec-82f6-bbadaeddfccc", 671 | "props": { 672 | "style": { 673 | "position": "absolute", 674 | "top": "138px", 675 | "left": "0px", 676 | "width": "319px", 677 | "height": "3px" 678 | }, 679 | "className": "bg", 680 | "source": { 681 | "uri": "https://img.alicdn.com/imgextra/i1/O1CN015WWq7v2AJriRaLXBN_!!6000000008183-2-tps-638-6.png" 682 | }, 683 | "autoScaling": false, 684 | "autoWebp": false 685 | }, 686 | "rect": { 687 | "x": -72, 688 | "y": 138, 689 | "width": 319, 690 | "height": 3 691 | }, 692 | "selfId": "2A9F6011-ADA4-4EAA-8FB3-FBC303A75960", 693 | "nodeLayerName": "矩形" 694 | }, { 695 | "componentName": "Picture", 696 | "id": "7adf2db8-2028-11ec-82f6-bbadaeddfccc", 697 | "props": { 698 | "style": { 699 | "position": "absolute", 700 | "top": "139px", 701 | "left": "81px", 702 | "width": "268px", 703 | "height": "378px", 704 | "overflow": "hidden" 705 | }, 706 | "className": "product-long-2", 707 | "source": { 708 | "uri": "https://img.alicdn.com/imgextra/i4/O1CN010QBD6u1TUpihL1m7h_!!6000000002386-2-tps-536-756.png" 709 | }, 710 | "autoScaling": false, 711 | "autoWebp": false 712 | }, 713 | "rect": { 714 | "x": 9, 715 | "y": 139, 716 | "width": 268, 717 | "height": 378 718 | }, 719 | "selfId": "2EBD36FF-20A7-4181-BF79-F9AAE128F5FB", 720 | "nodeLayerName": "蒙版" 721 | }, { 722 | "componentName": "Picture", 723 | "id": "7adf2db6-2028-11ec-82f6-bbadaeddfccc", 724 | "props": { 725 | "style": { 726 | "position": "absolute", 727 | "top": "102px", 728 | "right": "18px", 729 | "width": "466px", 730 | "height": "39px" 731 | }, 732 | "className": "background", 733 | "source": { 734 | "uri": "https://img.alicdn.com/imgextra/i3/O1CN01BlvkK31GB3c9nECsj_!!6000000000583-2-tps-934-78.png" 735 | }, 736 | "autoScaling": false, 737 | "autoWebp": false 738 | }, 739 | "rect": { 740 | "x": 9, 741 | "y": 102, 742 | "width": 466, 743 | "height": 39 744 | }, 745 | "selfId": "30B72AB6-491C-456C-9CB6-C07BB4F360BA", 746 | "nodeLayerName": "auto-#merge#" 747 | }, { 748 | "componentName": "View", 749 | "id": "3ad60bf6-00ed-4ff9-a5c9-d9e0e0cd036d", 750 | "props": { 751 | "style": { 752 | "display": "flex", 753 | "position": "absolute", 754 | "right": "11px", 755 | "bottom": "0px", 756 | "alignItems": "flex-start", 757 | "flexDirection": "row" 758 | }, 759 | "className": "wrapper" 760 | }, 761 | "rect": { 762 | "x": -46, 763 | "y": 416, 764 | "width": 528, 765 | "height": 207 766 | }, 767 | "smart": { 768 | "layerProtocol": { 769 | "group": { 770 | "type": "group" 771 | } 772 | } 773 | }, 774 | "selfId": "06ABB0FC-1ACA-49BD-BA81-059B3803CDA6", 775 | "nodeLayerName": "#group#编组 25", 776 | "children": [{ 777 | "componentName": "View", 778 | "id": "3ba4ccc0-19d9-40bf-82d5-9741bb39c495", 779 | "props": { 780 | "style": { 781 | "display": "flex", 782 | "position": "relative", 783 | "alignItems": "center", 784 | "flexDirection": "row", 785 | "marginTop": "17px", 786 | "marginLeft": "46px", 787 | "backgroundSize": "contain", 788 | "backgroundImage": "url(https://img.alicdn.com/imgextra/i2/O1CN01ofNLoD1YmIuIHhBl1_!!6000000003101-2-tps-964-380.png)", 789 | "height": "190px" 790 | }, 791 | "className": "wrapper-inner" 792 | }, 793 | "rect": { 794 | "x": 0, 795 | "y": 433, 796 | "width": 482, 797 | "height": 190 798 | }, 799 | "children": [{ 800 | "componentName": "View", 801 | "id": "7adf2db3-2028-11ec-82f6-bbadaeddfccc", 802 | "props": { 803 | "style": { 804 | "display": "flex", 805 | "position": "relative", 806 | "alignItems": "flex-start", 807 | "flexDirection": "row", 808 | "justifyContent": "center", 809 | "backgroundColor": "#141414", 810 | "width": "482px", 811 | "height": "176px", 812 | "overflow": "hidden" 813 | }, 814 | "className": "group" 815 | }, 816 | "rect": { 817 | "x": 0, 818 | "y": 440, 819 | "width": 482, 820 | "height": 176 821 | }, 822 | "selfId": "4713C36B-F068-4139-9A6A-26AD0DA10119", 823 | "nodeLayerName": "蒙版", 824 | "children": [{ 825 | "componentName": "Picture", 826 | "id": "7adf2db1-2028-11ec-82f6-bbadaeddfccc", 827 | "props": { 828 | "style": { 829 | "position": "absolute", 830 | "right": "12px", 831 | "bottom": "0px", 832 | "width": "174px", 833 | "height": "119px" 834 | }, 835 | "className": "entry-pic-2", 836 | "source": { 837 | "uri": "https://img.alicdn.com/imgextra/i3/O1CN01ZwPA581LWeMp5HThX_!!6000000001307-2-tps-350-242.png" 838 | }, 839 | "autoScaling": false, 840 | "autoWebp": false 841 | }, 842 | "rect": { 843 | "x": 296, 844 | "y": 497, 845 | "width": 174, 846 | "height": 119 847 | }, 848 | "selfId": "FC5FE3BD-C457-4882-905B-F270FB282964", 849 | "nodeLayerName": "路径 13" 850 | }, { 851 | "componentName": "View", 852 | "id": "c2501631-04b5-4698-830d-9e7f73fc0c38", 853 | "props": { 854 | "style": { 855 | "display": "flex", 856 | "flexDirection": "row", 857 | "justifyContent": "center", 858 | "alignItems": "center", 859 | "marginTop": 2, 860 | "width": "159px", 861 | "height": "40px", 862 | "lineHeight": "26px", 863 | "position": "absolute", 864 | "top": "110px", 865 | "left": "29px", 866 | "zIndex": "10", 867 | "backgroundImage": "url(https://img.alicdn.com/imgextra/i2/O1CN01A6AUU31FroypE1Fw8_!!6000000000541-2-tps-318-80.png)", 868 | "backgroundSize": "contain", 869 | "whiteSpace": "nowrap" 870 | }, 871 | "className": "view" 872 | }, 873 | "rect": { 874 | "x": 29, 875 | "y": 550, 876 | "width": 159, 877 | "height": 40 878 | }, 879 | "smart": { 880 | "layerProtocol": { 881 | "group": { 882 | "type": "group" 883 | } 884 | } 885 | }, 886 | "selfId": "052B0605-F6D2-47D4-BEE0-D3ED6AEF9E1E", 887 | "nodeLayerName": "#group#编组 7", 888 | "children": [{ 889 | "componentName": "Text", 890 | "id": "7adedf94-2028-11ec-82f6-bbadaeddfccc", 891 | "props": { 892 | "style": { 893 | "marginTop": "-2px", 894 | "color": "#141414", 895 | "fontFamily": "PingFang SC", 896 | "fontSize": "26px", 897 | "fontWeight": 400 898 | }, 899 | "text": "剩", 900 | "lines": 1, 901 | "className": "title" 902 | }, 903 | "rect": { 904 | "x": 46, 905 | "y": 552, 906 | "width": 26, 907 | "height": 34 908 | }, 909 | "selfId": "21A7BBD8-6A4D-4E94-8309-24422FE7EF260", 910 | "nodeLayerName": "剩3次机会", 911 | "smart": { 912 | "layerProtocol": { 913 | "layout": { 914 | "type": "layout", 915 | "position": "left" 916 | } 917 | } 918 | } 919 | }, { 920 | "componentName": "Text", 921 | "id": "7adedf93-2028-11ec-82f6-bbadaeddfccc", 922 | "props": { 923 | "style": { 924 | "marginTop": "-2px", 925 | "color": "#141414", 926 | "fontFamily": "PingFang SC", 927 | "fontSize": "26px", 928 | "fontWeight": 600 929 | }, 930 | "text": "3", 931 | "lines": 1, 932 | "className": "num" 933 | }, 934 | "rect": { 935 | "x": 72, 936 | "y": 552, 937 | "width": 16, 938 | "height": 34 939 | }, 940 | "selfId": "21A7BBD8-6A4D-4E94-8309-24422FE7EF261", 941 | "nodeLayerName": "剩3次机会", 942 | "smart": { 943 | "layerProtocol": { 944 | "layout": { 945 | "type": "layout", 946 | "position": "middle" 947 | } 948 | } 949 | } 950 | }, { 951 | "componentName": "Text", 952 | "id": "7adedf92-2028-11ec-82f6-bbadaeddfccc", 953 | "props": { 954 | "style": { 955 | "marginTop": "-2px", 956 | "color": "#141414", 957 | "fontFamily": "PingFang SC", 958 | "fontSize": "26px", 959 | "fontWeight": 400 960 | }, 961 | "text": "次机会", 962 | "lines": 1, 963 | "className": "caption" 964 | }, 965 | "rect": { 966 | "x": 88, 967 | "y": 552, 968 | "width": 78, 969 | "height": 34 970 | }, 971 | "selfId": "21A7BBD8-6A4D-4E94-8309-24422FE7EF262", 972 | "nodeLayerName": "剩3次机会", 973 | "smart": { 974 | "layerProtocol": { 975 | "layout": { 976 | "type": "layout", 977 | "position": "right" 978 | } 979 | } 980 | } 981 | }] 982 | }, { 983 | "componentName": "Picture", 984 | "id": "7adf2db2-2028-11ec-82f6-bbadaeddfccc", 985 | "props": { 986 | "style": { 987 | "position": "relative", 988 | "marginTop": "17px", 989 | "width": "367px", 990 | "height": "32px" 991 | }, 992 | "className": "bg-1", 993 | "source": { 994 | "uri": "https://img.alicdn.com/imgextra/i3/O1CN01X12MfA1etWqs7cVTJ_!!6000000003929-2-tps-734-64.png" 995 | }, 996 | "autoScaling": false, 997 | "autoWebp": false 998 | }, 999 | "rect": { 1000 | "x": 54, 1001 | "y": 457, 1002 | "width": 367, 1003 | "height": 32 1004 | }, 1005 | "selfId": "060F3E04-18B4-4DDA-8920-2ECC73E2998A", 1006 | "nodeLayerName": "auto-#merge#" 1007 | }] 1008 | }] 1009 | }, { 1010 | "componentName": "Picture", 1011 | "id": "7adf2db0-2028-11ec-82f6-bbadaeddfccc", 1012 | "props": { 1013 | "style": { 1014 | "position": "absolute", 1015 | "top": "0px", 1016 | "left": "0px", 1017 | "width": "174px", 1018 | "height": "119px" 1019 | }, 1020 | "className": "entry-pic-3", 1021 | "source": { 1022 | "uri": "https://img.alicdn.com/imgextra/i4/O1CN01VJPeDg24GIwJBQY71_!!6000000007363-2-tps-350-242.png" 1023 | }, 1024 | "autoScaling": false, 1025 | "autoWebp": false 1026 | }, 1027 | "rect": { 1028 | "x": -46, 1029 | "y": 416, 1030 | "width": 174, 1031 | "height": 119 1032 | }, 1033 | "selfId": "22FB7A39-BB83-4147-AEBC-C9AE4279A312", 1034 | "nodeLayerName": "路径 13备份" 1035 | }, { 1036 | "componentName": "View", 1037 | "id": "71020ced-1bfa-4c20-a99f-45e9b1806899", 1038 | "props": { 1039 | "style": { 1040 | "display": "flex", 1041 | "position": "absolute", 1042 | "top": "90px", 1043 | "right": "39px", 1044 | "alignItems": "flex-start", 1045 | "flexDirection": "column", 1046 | "borderWidth": "2px", 1047 | "borderStyle": "solid", 1048 | "borderRadius": "47px", 1049 | "borderColor": "rgba(0,0,0,0.34)", 1050 | "backgroundColor": "rgba(255,255,255,0.10)", 1051 | "width": "405px", 1052 | "height": "94px" 1053 | }, 1054 | "className": "wrapper-inner-1" 1055 | }, 1056 | "rect": { 1057 | "x": 38, 1058 | "y": 506, 1059 | "width": 405, 1060 | "height": 94 1061 | }, 1062 | "smart": { 1063 | "layerProtocol": { 1064 | "group": { 1065 | "type": "group" 1066 | } 1067 | } 1068 | }, 1069 | "selfId": "BC8E5221-E691-488C-B245-248F49695067", 1070 | "nodeLayerName": "#group#编组 7", 1071 | "children": [{ 1072 | "componentName": "View", 1073 | "id": "03836e10-ebae-4ec0-a420-5af2481aef32", 1074 | "props": { 1075 | "style": { 1076 | "display": "flex", 1077 | "position": "absolute", 1078 | "top": "10px", 1079 | "alignItems": "center", 1080 | "alignSelf": "center", 1081 | "flexDirection": "row", 1082 | "width": "382px", 1083 | "height": "74px" 1084 | }, 1085 | "className": "button-bg-wrapper" 1086 | }, 1087 | "rect": { 1088 | "x": 50, 1089 | "y": 516, 1090 | "width": 382, 1091 | "height": 74 1092 | }, 1093 | "children": [{ 1094 | "componentName": "Picture", 1095 | "id": "7adf06a8-2028-11ec-82f6-bbadaeddfccc", 1096 | "props": { 1097 | "style": { 1098 | "position": "absolute", 1099 | "top": "0px", 1100 | "left": "0px", 1101 | "width": "382px", 1102 | "height": "74px", 1103 | "overflow": "hidden" 1104 | }, 1105 | "className": "button-bg", 1106 | "source": { 1107 | "uri": "https://img.alicdn.com/imgextra/i2/O1CN01WFD5Ao1LAfM5bJvDk_!!6000000001259-2-tps-764-148.png" 1108 | }, 1109 | "autoScaling": false, 1110 | "autoWebp": false 1111 | }, 1112 | "rect": { 1113 | "x": 50, 1114 | "y": 516, 1115 | "width": 382, 1116 | "height": 74 1117 | }, 1118 | "selfId": "A773A2B6-568B-476E-BC37-B7A76AABC0F5", 1119 | "nodeLayerName": "蒙版" 1120 | }, { 1121 | "componentName": "Picture", 1122 | "id": "7adf06a6-2028-11ec-82f6-bbadaeddfccc", 1123 | "props": { 1124 | "style": { 1125 | "position": "absolute", 1126 | "top": "0px", 1127 | "left": "0px", 1128 | "width": "382px", 1129 | "height": "49px", 1130 | "overflow": "hidden" 1131 | }, 1132 | "className": "background-1", 1133 | "source": { 1134 | "uri": "https://img.alicdn.com/imgextra/i4/O1CN01AlJf431gFnjCoaXHg_!!6000000004113-2-tps-764-100.png" 1135 | }, 1136 | "autoScaling": false, 1137 | "autoWebp": false 1138 | }, 1139 | "rect": { 1140 | "x": 50, 1141 | "y": 516, 1142 | "width": 382, 1143 | "height": 49 1144 | }, 1145 | "selfId": "24FC61D3-2330-4387-951D-64906995AD9E", 1146 | "nodeLayerName": "蒙版 2" 1147 | }, { 1148 | "componentName": "Picture", 1149 | "id": "7adf06a3-2028-11ec-82f6-bbadaeddfccc", 1150 | "props": { 1151 | "style": { 1152 | "position": "absolute", 1153 | "top": "16px", 1154 | "right": "150px", 1155 | "width": "79px", 1156 | "height": "38px" 1157 | }, 1158 | "className": "large-icon", 1159 | "source": { 1160 | "uri": "https://img.alicdn.com/imgextra/i4/O1CN01mlQkrx1XIFIgOoBNq_!!6000000002900-2-tps-160-80.png" 1161 | }, 1162 | "autoScaling": false, 1163 | "autoWebp": false 1164 | }, 1165 | "rect": { 1166 | "x": 203, 1167 | "y": 532, 1168 | "width": 79, 1169 | "height": 38 1170 | }, 1171 | "selfId": "4503B205-6877-471B-9475-9F04E4AECAFC", 1172 | "nodeLayerName": "抽奖" 1173 | }] 1174 | }] 1175 | }] 1176 | }], 1177 | "imgcook": { 1178 | "restore_id": "5e0484c8-2b4a-497d-afef-00d4cfafcc8b", 1179 | "functions": [{ 1180 | "content": "export default function created() {\n\n}", 1181 | "name": "created", 1182 | "type": "lifeCycles" 1183 | }, { 1184 | "content": "export default function mounted() {\n\n}", 1185 | "name": "mounted", 1186 | "type": "lifeCycles" 1187 | }], 1188 | "dataConfig": { 1189 | "schemaLaunchConfigId": "self" 1190 | }, 1191 | "defaultReadonlyFiles": ["src/mobile/index.js", "src/mobile/actionsIndex.js", "src/mobile/builtinActions/created.js", "src/mobile/builtinActions/mounted.js", "src/mobile/mod.js", "src/mobile/mod.css", "src/mock.json"], 1192 | "genSettings": { 1193 | "exportClassName": false, 1194 | "appendRpx": true, 1195 | "hasAutoCommit": false 1196 | }, 1197 | "devSettings": { 1198 | "autoSync": true, 1199 | "fileLock": false 1200 | } 1201 | } 1202 | } --------------------------------------------------------------------------------