├── .commitlintrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .fie └── fie4.config.js ├── .gitignore ├── .markdownlint.json ├── .markdownlintignore ├── .npmignore ├── .prettierignore ├── .prettierrc.js ├── .stylelintignore ├── .stylelintrc.js ├── CHANGELOG.md ├── README.md ├── abc.json ├── build.json ├── build.lowcode.js ├── build.plugin.js ├── commitlint.config.js ├── f2elint.config.js ├── lowcode ├── child-form │ └── meta.ts ├── pro-form-date-picker │ └── meta.ts ├── pro-form-input │ └── meta.ts ├── pro-form-number-input │ └── meta.ts ├── pro-form-select │ └── meta.ts ├── pro-form │ └── meta.ts ├── section-form │ └── meta.ts └── shared │ └── index.ts ├── package.json ├── postcss.config.js ├── src ├── components │ ├── form │ │ ├── components │ │ │ ├── form-date-picker.tsx │ │ │ ├── form-input.tsx │ │ │ ├── form-item.tsx │ │ │ ├── form-number-input.tsx │ │ │ ├── form-select.tsx │ │ │ └── next-wrapper.ts │ │ ├── form.tsx │ │ └── index.tsx │ ├── pro-card │ │ ├── index.scss │ │ ├── index.tsx │ │ └── pro-card.tsx │ └── section-form │ │ ├── components │ │ └── child-form.tsx │ │ ├── index.tsx │ │ └── section-form.tsx ├── index.scss ├── index.tsx ├── variables.scss └── variables.tsx ├── tailwind.config.js ├── tsconfig.json └── tslint.json /.commitlintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "header-max-length": [2, "always", 72], 4 | "subject-empty": [2, "never"], 5 | "subject-full-stop": [2, "never", "."], 6 | "type-empty": [2, "never"], 7 | "type-case": [2, "always", "lower-case"], 8 | "type-enum": [2, "always", 9 | [ 10 | "build", 11 | "chore", 12 | "ci", 13 | "docs", 14 | "feat", 15 | "fix", 16 | "perf", 17 | "refactor", 18 | "revert", 19 | "style", 20 | "test", 21 | "temp" 22 | ] 23 | ] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | quote_type = single 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | build/ 3 | dist/ 4 | **/*.min.js 5 | **/*-min.js 6 | **/*.bundle.js 7 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | 'eslint-config-ali/typescript/react', 4 | "prettier", 5 | 'prettier/@typescript-eslint', 6 | 'prettier/react', 7 | ], 8 | }; 9 | -------------------------------------------------------------------------------- /.fie/fie4.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | scene: 'component', 3 | sceneConfig: { 4 | platform: 'pc', 5 | projectType: 'multiple', 6 | componentPath: 'src/components' 7 | }, 8 | templateUrl: 'http://gitlab.alibaba-inc.com/fie4-template/pc-multiple-component-template', 9 | appType: 'TNPM', 10 | plugins: ['@ali/fie4-plugin-component'], 11 | }; 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # project custom 2 | build 3 | dist 4 | site 5 | .tmp 6 | es 7 | public/resources/meta.json 8 | packages/*/lib/ 9 | packages/*/es/ 10 | packages/*/dist/ 11 | packages/*/output/ 12 | package-lock.json 13 | yarn.lock 14 | deploy-space/packages 15 | deploy-space/.env 16 | 17 | 18 | # IDE 19 | .vscode 20 | .idea 21 | # Logs 22 | logs 23 | *.log 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | lerna-debug.log* 28 | 29 | # Runtime data 30 | pids 31 | *.pid 32 | *.seed 33 | *.pid.lock 34 | 35 | 36 | # Directory for instrumented libs generated by jscoverage/JSCover 37 | lib-cov 38 | 39 | # Coverage directory used by tools like istanbul 40 | coverage 41 | 42 | # nyc test coverage 43 | .nyc_output 44 | 45 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 46 | .grunt 47 | 48 | # Bower dependency directory (https://bower.io/) 49 | bower_components 50 | 51 | # node-waf configuration 52 | .lock-wscript 53 | 54 | # Compiled binary addons (https://nodejs.org/api/addons.html) 55 | build/Release 56 | lib 57 | 58 | # Dependency directories 59 | node_modules/ 60 | jspm_packages/ 61 | 62 | # TypeScript v1 declaration files 63 | typings/ 64 | 65 | # Optional npm cache directory 66 | .npm 67 | 68 | # Optional eslint cache 69 | .eslintcache 70 | 71 | # Optional REPL history 72 | .node_repl_history 73 | 74 | # Output of 'npm pack' 75 | *.tgz 76 | 77 | # Yarn Integrity file 78 | .yarn-integrity 79 | 80 | # dotenv environment variables file 81 | .env 82 | .env.test 83 | 84 | # parcel-bundler cache (https://parceljs.org/) 85 | .cache 86 | 87 | # next.js build output 88 | .next 89 | 90 | # nuxt.js build output 91 | .nuxt 92 | 93 | # vuepress build output 94 | .vuepress/dist 95 | 96 | # Serverless directories 97 | .serverless/ 98 | 99 | # FuseBox cache 100 | .fusebox/ 101 | 102 | # DynamoDB Local files 103 | .dynamodb/ 104 | 105 | # mac config files 106 | .DS_Store 107 | 108 | # codealike 109 | codealike.json 110 | 111 | src/assets.js 112 | src/assets.json 113 | src/assets.container.js 114 | src/assets.container.json 115 | assets.json 116 | assets.container.json 117 | material-meta.json 118 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "markdownlint-config-ali" 3 | } 4 | -------------------------------------------------------------------------------- /.markdownlintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | build/ 3 | dist/ 4 | 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vscode/ 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | lib/ 3 | build/ 4 | dist/ 5 | umd/ 6 | es/ 7 | .cachefile/ 8 | deps.json 9 | package-lock.json 10 | yarn.lock 11 | miniapp/**/*.axml 12 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | printWidth: 100, 3 | tabWidth: 2, 4 | semi: true, 5 | singleQuote: true, 6 | trailingComma: 'all', 7 | arrowParens: 'always', 8 | }; 9 | -------------------------------------------------------------------------------- /.stylelintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | build/ 3 | dist/ 4 | **/*.min.css 5 | **/*-min.css 6 | **/*.bundle.css 7 | 8 | -------------------------------------------------------------------------------- /.stylelintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: 'stylelint-config-ali', 3 | }; 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dogtiti/next-pro-lowcode-materials/5f133de0437d33323668a0cba13fdc35b4983c9e/CHANGELOG.md -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # next-pro-lowcode-materials 2 | 3 | 基于`next`实现的高级组件低代码物料 4 | 5 | ## 组件列表 6 | * [pro-form](https://github.com/Dogtiti/next-pro-lowcode-materials/blob/master/src/components/form/form.tsx) 7 | * [section-form](https://github.com/Dogtiti/next-pro-lowcode-materials/blob/master/src/components/section-form/section-form.tsx) 8 | 9 | ## 调试 10 | 启动调试 11 | 12 | ```shell 13 | npm run lowcode:dev # 打开开发环境,运行所有组件 14 | ``` 15 | 16 | 构建 17 | 18 | ```shell 19 | npm run lowcode:build 20 | ``` 21 | 22 | 发布 23 | 24 | ```shell 25 | npm publish --access public 26 | ``` 27 | -------------------------------------------------------------------------------- /abc.json: -------------------------------------------------------------------------------- 1 | { 2 | "assets": { 3 | "type": "builder", 4 | "builder": { 5 | "name": "@ali/builder-fie4-component" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /build.json: -------------------------------------------------------------------------------- 1 | { 2 | "library": "NextProLowcodeMaterials", 3 | "libraryTarget": "umd", 4 | "sourceMap": true, 5 | "alias": { 6 | "@": "./src/components" 7 | }, 8 | "plugins": [ 9 | [ 10 | "build-plugin-component-multiple", 11 | { 12 | "themePackage": "@alifd/theme-2" 13 | } 14 | ] 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /build.lowcode.js: -------------------------------------------------------------------------------- 1 | const { library } = require('./build.json'); 2 | 3 | module.exports = { 4 | alias: { 5 | '@': './src', 6 | }, 7 | plugins: [ 8 | [ 9 | '@alifd/build-plugin-lowcode', 10 | { 11 | library, 12 | engineScope: '@alilc', 13 | }, 14 | ], 15 | './build.plugin.js', 16 | ], 17 | }; 18 | -------------------------------------------------------------------------------- /build.plugin.js: -------------------------------------------------------------------------------- 1 | module.exports = ({ onGetWebpackConfig }) => { 2 | onGetWebpackConfig((config) => { 3 | config.module 4 | .rule('postcss-loader') 5 | .test(/\.scss$/) 6 | .use(['tailwindcss', 'autoprefixer']) 7 | .loader('postcss-loader'); 8 | }); 9 | }; 10 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['ali'], 3 | }; 4 | -------------------------------------------------------------------------------- /f2elint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | enableStylelint: true, 3 | enableMarkdownlint: true, 4 | enablePrettier: true, 5 | }; 6 | -------------------------------------------------------------------------------- /lowcode/child-form/meta.ts: -------------------------------------------------------------------------------- 1 | import { ComponentMetadata, Snippet } from '@alilc/lowcode-types'; 2 | import { nanoid } from 'nanoid'; 3 | import { formProps } from '../shared'; 4 | 5 | const props = formProps 6 | .filter((p) => p.name !== 'operations') 7 | .map((r) => { 8 | if (r.name === 'globalConfig') { 9 | return { 10 | ...r, 11 | items: r.items?.filter((item) => item.name !== 'field'), 12 | }; 13 | } 14 | return r; 15 | }); 16 | 17 | const ChildFormMeta: ComponentMetadata = { 18 | group: '精选组件', 19 | category: '高级表单(next)', 20 | componentName: 'ChildForm', 21 | title: '子表单', 22 | docUrl: '', 23 | screenshot: '', 24 | devMode: 'proCode', 25 | hidden: true, 26 | npm: { 27 | package: '@dogtiti/next-pro-lowcode-materials', 28 | version: '{{version}}', 29 | exportName: 'ChildForm', 30 | main: 'src/index.tsx', 31 | destructuring: true, 32 | subName: '', 33 | }, 34 | props: [], 35 | configure: { 36 | component: { 37 | isContainer: true, 38 | isMinimalRenderUnit: true, 39 | nestingRule: { 40 | childWhitelist: /ProForm.*/i, 41 | parentWhitelist: ['SectionForm', 'ProForm'], 42 | }, 43 | }, 44 | props, 45 | supports: { 46 | style: true, 47 | events: ['saveField', 'onSubmit', 'onChange'], 48 | }, 49 | }, 50 | }; 51 | const snippets: Snippet[] = [ 52 | { 53 | title: '子表单', 54 | screenshot: 55 | 'https://img.alicdn.com/imgextra/i2/O1CN016gn5DQ1FeXUNKdK22_!!6000000000512-55-tps-50-36.svg', 56 | schema: { 57 | componentName: 'ChildForm', 58 | props: { 59 | placeholder: '请在右侧面板添加表单项+', 60 | placeholderStyle: { 61 | height: '38px', 62 | color: '#0088FF', 63 | background: '#d8d8d836', 64 | border: 0, 65 | gridArea: 'span 4 / span 4', 66 | }, 67 | columns: 4, 68 | labelCol: { 69 | fixedSpan: 4, 70 | }, 71 | labelAlign: 'top', 72 | emptyContent: '添加表单项', 73 | }, 74 | children: [ 75 | { 76 | componentName: 'ProFormInput', 77 | props: { 78 | formItemProps: { 79 | primaryKey: nanoid(), 80 | label: '表单项', 81 | size: 'medium', 82 | device: 'desktop', 83 | fullWidth: true, 84 | }, 85 | placeholder: '请输入', 86 | }, 87 | }, 88 | { 89 | componentName: 'ProFormInput', 90 | props: { 91 | formItemProps: { 92 | primaryKey: nanoid(), 93 | label: '表单项', 94 | size: 'medium', 95 | device: 'desktop', 96 | fullWidth: true, 97 | }, 98 | placeholder: '请输入', 99 | }, 100 | }, 101 | ], 102 | }, 103 | }, 104 | ]; 105 | 106 | export default { 107 | ...ChildFormMeta, 108 | snippets, 109 | }; 110 | -------------------------------------------------------------------------------- /lowcode/pro-form-date-picker/meta.ts: -------------------------------------------------------------------------------- 1 | import { ComponentMetadata, Snippet } from '@alilc/lowcode-types'; 2 | import { formItemProps } from '../shared'; 3 | 4 | const ProFormDatePickerMeta: ComponentMetadata = { 5 | componentName: 'ProFormDatePicker', 6 | group: '精选组件', 7 | category: '高级表单(next)', 8 | title: '日期选择器', 9 | hidden: true, 10 | docUrl: '', 11 | screenshot: '', 12 | devMode: 'proCode', 13 | npm: { 14 | package: '@dogtiti/next-pro-lowcode-materials', 15 | version: '{{version}}', 16 | exportName: 'ProFormDatePicker', 17 | main: 'src/index.tsx', 18 | destructuring: true, 19 | subName: '', 20 | }, 21 | configure: { 22 | props: [ 23 | formItemProps, 24 | { 25 | name: 'componentProps', 26 | title: '组件配置', 27 | extraProps: { 28 | display: 'accordion', 29 | defaultCollapsed: true, 30 | }, 31 | type: 'group', 32 | items: [ 33 | { 34 | name: 'label', 35 | title: { 36 | label: '标签', 37 | tip: 'label|表单项内置标签', 38 | }, 39 | setter: 'StringSetter', 40 | }, 41 | { 42 | name: 'state', 43 | title: { 44 | label: '状态', 45 | tip: 'state|表单项状态', 46 | }, 47 | setter: { 48 | componentName: 'RadioGroupSetter', 49 | props: { 50 | options: ['success', 'loading', 'error'], 51 | }, 52 | }, 53 | }, 54 | { 55 | name: 'placeholder', 56 | title: { 57 | label: '占位提示', 58 | tip: 'placeholder|输入提示', 59 | }, 60 | setter: 'StringSetter', 61 | }, 62 | { 63 | name: 'value', 64 | title: { 65 | label: 'value', 66 | tip: 'value|日期值(受控)', 67 | }, 68 | setter: 'DateSetter', 69 | }, 70 | { 71 | name: 'format', 72 | title: { 73 | label: '格式', 74 | tip: 'format|日期值的格式(用于限定用户输入和展示)', 75 | }, 76 | setter: 'StringSetter', 77 | defaultValue: 'YYYY-MM-DD', 78 | }, 79 | { 80 | name: 'size', 81 | title: { 82 | label: '尺寸', 83 | tip: 'size|表单项尺寸', 84 | }, 85 | setter: { 86 | componentName: 'RadioGroupSetter', 87 | props: { 88 | options: ['small', 'medium', 'large'], 89 | }, 90 | }, 91 | defaultValue: 'medium', 92 | }, 93 | { 94 | name: 'disabled', 95 | title: { 96 | label: '是否禁用', 97 | tip: 'disabled|是否禁用', 98 | }, 99 | setter: 'BoolSetter', 100 | }, 101 | { 102 | name: 'hasClear', 103 | title: { 104 | label: '清除按钮', 105 | tip: 'hasClear|是否显示清空按钮', 106 | }, 107 | setter: 'BoolSetter', 108 | defaultValue: true, 109 | }, 110 | { 111 | name: 'followTrigger', 112 | setter: 'BoolSetter', 113 | title: '跟随滚动', 114 | }, 115 | { 116 | name: 'defaultValue', 117 | title: { 118 | label: '默认值', 119 | tip: 'defaultValue|初始日期值,moment 对象', 120 | }, 121 | setter: 'DateSetter', 122 | }, 123 | ], 124 | }, 125 | ], 126 | supports: { 127 | events: ['onChange', 'onOk'], 128 | }, 129 | }, 130 | }; 131 | const snippets: Snippet[] = [ 132 | { 133 | title: '选择器', 134 | screenshot: '', 135 | schema: { 136 | componentName: 'ProFormDatePicker', 137 | props: {}, 138 | }, 139 | }, 140 | ]; 141 | 142 | export default { 143 | ...ProFormDatePickerMeta, 144 | snippets, 145 | }; 146 | -------------------------------------------------------------------------------- /lowcode/pro-form-input/meta.ts: -------------------------------------------------------------------------------- 1 | import { ComponentMetadata, Snippet } from '@alilc/lowcode-types'; 2 | import { formItemProps } from '../shared'; 3 | 4 | const ProFormInputMeta: ComponentMetadata = { 5 | componentName: 'ProFormInput', 6 | group: '精选组件', 7 | category: '高级表单(next)', 8 | title: '输入框', 9 | hidden: true, 10 | docUrl: '', 11 | screenshot: '', 12 | devMode: 'proCode', 13 | npm: { 14 | package: '@dogtiti/next-pro-lowcode-materials', 15 | version: '{{version}}', 16 | exportName: 'ProFormInput', 17 | main: 'src/index.tsx', 18 | destructuring: true, 19 | subName: '', 20 | }, 21 | configure: { 22 | props: [ 23 | formItemProps, 24 | { 25 | name: 'componentProps', 26 | title: '组件配置', 27 | extraProps: { 28 | display: 'accordion', 29 | defaultCollapsed: true, 30 | }, 31 | type: 'group', 32 | items: [ 33 | { 34 | name: 'label', 35 | setter: 'StringSetter', 36 | title: { 37 | label: { 38 | type: 'i18n', 39 | zh_CN: '标签文本', 40 | en_US: 'Label', 41 | }, 42 | tip: { 43 | type: 'i18n', 44 | zh_CN: '属性: label | 说明: 标签文本内容', 45 | en_US: 'prop: label | description: label content', 46 | }, 47 | }, 48 | }, 49 | { 50 | name: 'defaultValue', 51 | title: { 52 | label: '默认值', 53 | tip: 'defaultValue|初始值', 54 | }, 55 | setter: 'StringSetter', 56 | }, 57 | { 58 | name: 'placeholder', 59 | defaultValue: '请输入', 60 | title: { 61 | label: { 62 | type: 'i18n', 63 | zh_CN: '输入提示', 64 | en_US: 'Placeholder', 65 | }, 66 | tip: { 67 | type: 'i18n', 68 | zh_CN: '属性: placeholder | 说明: 输入提示', 69 | en_US: 'prop: placeholder | description: placeholder', 70 | }, 71 | }, 72 | setter: 'StringSetter', 73 | }, 74 | { 75 | name: 'state', 76 | title: { 77 | label: { 78 | type: 'i18n', 79 | zh_CN: '状态', 80 | en_US: 'State', 81 | }, 82 | tip: { 83 | type: 'i18n', 84 | zh_CN: '属性: state | 说明: 状态\n@enumdesc 错误, 校验中, 成功, 警告', 85 | en_US: 'prop: state | description: input state', 86 | }, 87 | }, 88 | setter: { 89 | componentName: 'RadioGroupSetter', 90 | props: { 91 | options: [ 92 | { 93 | value: 'error', 94 | title: 'error', 95 | }, 96 | { 97 | value: 'loading', 98 | title: 'loading', 99 | }, 100 | { 101 | value: 'success', 102 | title: 'success', 103 | }, 104 | { 105 | value: 'warning', 106 | title: 'warning', 107 | }, 108 | { 109 | value: '', 110 | title: '默认', 111 | }, 112 | ], 113 | }, 114 | }, 115 | }, 116 | { 117 | name: 'size', 118 | title: { 119 | label: { 120 | type: 'i18n', 121 | zh_CN: '尺寸', 122 | en_US: 'Size', 123 | }, 124 | tip: { 125 | type: 'i18n', 126 | zh_CN: '属性: size | 说明: 尺寸\n@enumdesc 小, 中, 大', 127 | en_US: 'prop: size | description: size', 128 | }, 129 | }, 130 | setter: { 131 | componentName: 'RadioGroupSetter', 132 | props: { 133 | options: ['small', 'medium', 'large'], 134 | }, 135 | }, 136 | defaultValue: 'medium', 137 | }, 138 | { 139 | name: 'maxLength', 140 | title: { 141 | label: { 142 | type: 'i18n', 143 | zh_CN: '最大长度', 144 | en_US: 'MaxLength', 145 | }, 146 | tip: { 147 | type: 'i18n', 148 | zh_CN: '属性: maxLength | 说明: 最大长度', 149 | en_US: 'prop: maxLength | description: max length', 150 | }, 151 | }, 152 | setter: 'NumberSetter', 153 | description: '最大长度', 154 | }, 155 | { 156 | name: 'hasClear', 157 | title: { 158 | label: { 159 | type: 'i18n', 160 | zh_CN: '显示清除', 161 | en_US: 'Show Clear', 162 | }, 163 | tip: { 164 | type: 'i18n', 165 | zh_CN: '属性: hasClear | 说明: 是否出现清除按钮', 166 | en_US: 'prop: hasClear | description: show clear icon', 167 | }, 168 | }, 169 | setter: 'BoolSetter', 170 | description: '是否出现清除按钮', 171 | }, 172 | { 173 | name: 'disabled', 174 | title: { 175 | label: { 176 | type: 'i18n', 177 | zh_CN: '是否禁用', 178 | en_US: 'Disabled', 179 | }, 180 | tip: { 181 | type: 'i18n', 182 | zh_CN: '属性: disabled | 说明: 是否被禁用', 183 | en_US: 'prop: disabled | description: disabled', 184 | }, 185 | }, 186 | setter: 'BoolSetter', 187 | description: '是否禁用', 188 | }, 189 | { 190 | name: 'showLimitHint', 191 | title: { 192 | label: { 193 | type: 'i18n', 194 | zh_CN: '展示限制', 195 | en_US: 'ShowLimit', 196 | }, 197 | tip: { 198 | type: 'i18n', 199 | zh_CN: '属性: showLimitHint | 说明: 是否展现最大长度样式', 200 | en_US: 'prop: showLimitHint | description: showLimitHint', 201 | }, 202 | }, 203 | setter: 'BoolSetter', 204 | description: '是否展现最大长度样式', 205 | }, 206 | { 207 | name: 'cutString', 208 | title: { 209 | label: { 210 | type: 'i18n', 211 | zh_CN: '是否截断', 212 | en_US: 'Cut Off', 213 | }, 214 | tip: { 215 | type: 'i18n', 216 | zh_CN: '属性: cutString | 说明: 是否截断超出字符串', 217 | en_US: 'prop: cutString | description: whether cut off string', 218 | }, 219 | }, 220 | setter: 'BoolSetter', 221 | description: '是否截断超出字符串', 222 | }, 223 | { 224 | name: 'readOnly', 225 | title: { 226 | label: { 227 | type: 'i18n', 228 | zh_CN: '是否只读', 229 | en_US: 'ReadOnly', 230 | }, 231 | tip: { 232 | type: 'i18n', 233 | zh_CN: '属性: readOnly | 说明: 是否只读', 234 | en_US: 'prop: readOnly | description: ReadOnly', 235 | }, 236 | }, 237 | setter: 'BoolSetter', 238 | description: '是否只读', 239 | }, 240 | { 241 | name: 'trim', 242 | title: { 243 | label: { 244 | type: 'i18n', 245 | zh_CN: '是否 Trim', 246 | en_US: 'Trim', 247 | }, 248 | tip: { 249 | type: 'i18n', 250 | zh_CN: '属性: trim | 说明: onChange返回会自动去除头尾空字符', 251 | en_US: 'prop: trim | description: whether trim when onChange called', 252 | }, 253 | }, 254 | setter: 'BoolSetter', 255 | }, 256 | { 257 | name: 'hasBorder', 258 | title: { 259 | label: { 260 | type: 'i18n', 261 | zh_CN: '显示边框', 262 | en_US: 'ShowBorder', 263 | }, 264 | tip: { 265 | type: 'i18n', 266 | zh_CN: '属性: hasBorder | 说明: 是否有边框', 267 | en_US: 'prop: hasBorder | description: HasBorder', 268 | }, 269 | }, 270 | setter: 'BoolSetter', 271 | }, 272 | { 273 | name: 'autoFocus', 274 | title: { 275 | label: { 276 | type: 'i18n', 277 | zh_CN: '自动聚焦', 278 | en_US: 'Auto Focus', 279 | }, 280 | tip: { 281 | type: 'i18n', 282 | zh_CN: '属性: autoFocus | 说明: 自动聚焦', 283 | en_US: 'prop: autoFocus | description: autoFocus', 284 | }, 285 | }, 286 | setter: 'BoolSetter', 287 | description: '自动聚焦', 288 | }, 289 | { 290 | name: 'hint', 291 | title: { 292 | label: { 293 | type: 'i18n', 294 | zh_CN: 'Icon 水印', 295 | en_US: 'IconHint', 296 | }, 297 | tip: { 298 | type: 'i18n', 299 | zh_CN: '属性: hint | 说明: Icon 水印', 300 | en_US: 'prop: hint | description: Icon hint', 301 | }, 302 | }, 303 | setter: { 304 | componentName: 'IconSetter', 305 | }, 306 | }, 307 | { 308 | name: 'innerBefore', 309 | display: 'block', 310 | title: { 311 | label: { 312 | type: 'i18n', 313 | zh_CN: '文字前附加内容', 314 | en_US: 'Inner Before', 315 | }, 316 | tip: { 317 | type: 'i18n', 318 | zh_CN: '属性: innerBefore | 说明: 文字前附加内容', 319 | en_US: 'prop: innerBefore | description: innerBefore', 320 | }, 321 | }, 322 | setter: 'StringSetter', 323 | }, 324 | { 325 | name: 'innerAfter', 326 | display: 'block', 327 | title: { 328 | label: { 329 | type: 'i18n', 330 | zh_CN: '文字后附加内容', 331 | en_US: 'Inner After', 332 | }, 333 | tip: { 334 | type: 'i18n', 335 | zh_CN: '属性: innerAfter | 说明: 文字后附加内容', 336 | en_US: 'prop: innerAfter | description: innerAfter', 337 | }, 338 | }, 339 | setter: 'StringSetter', 340 | }, 341 | { 342 | name: 'addonBefore', 343 | display: 'block', 344 | title: { 345 | label: { 346 | type: 'i18n', 347 | zh_CN: '输入框前附加内容', 348 | en_US: 'Addon Before', 349 | }, 350 | tip: { 351 | type: 'i18n', 352 | zh_CN: '属性: addonBefore | 说明: 输入框前附加内容', 353 | en_US: 'prop: addonBefore | description: addonBefore', 354 | }, 355 | }, 356 | setter: 'StringSetter', 357 | }, 358 | { 359 | name: 'addonAfter', 360 | display: 'block', 361 | title: { 362 | label: { 363 | type: 'i18n', 364 | zh_CN: '输入框后附加内容', 365 | en_US: 'Addon After', 366 | }, 367 | tip: { 368 | type: 'i18n', 369 | zh_CN: '属性: addonAfter | 说明: 输入框后附加内容', 370 | en_US: 'prop: addonAfter | description: addonAfter', 371 | }, 372 | }, 373 | setter: 'StringSetter', 374 | }, 375 | { 376 | name: 'addonTextBefore', 377 | display: 'block', 378 | title: { 379 | label: { 380 | type: 'i18n', 381 | zh_CN: '输入框前附加文字', 382 | en_US: 'Text Before', 383 | }, 384 | tip: { 385 | type: 'i18n', 386 | zh_CN: '属性: addonTextBefore | 说明: 输入框前附加文字', 387 | en_US: 'prop: addonTextBefore | description: addonTextBefore', 388 | }, 389 | }, 390 | setter: 'StringSetter', 391 | }, 392 | { 393 | name: 'addonTextAfter', 394 | display: 'block', 395 | title: { 396 | label: { 397 | type: 'i18n', 398 | zh_CN: '输入框后附加文字', 399 | en_US: 'Text After', 400 | }, 401 | tip: { 402 | type: 'i18n', 403 | zh_CN: '属性: addonTextAfter | 说明: 输入框后附加文字', 404 | en_US: 'prop: addonTextAfter | description: addonTextAfter', 405 | }, 406 | }, 407 | setter: 'StringSetter', 408 | }, 409 | ], 410 | }, 411 | ], 412 | supports: { 413 | style: true, 414 | events: ['onPressEnter', 'onClear', 'onChange', 'onKeyDown', 'onFocus', 'onBlur'], 415 | }, 416 | }, 417 | }; 418 | const snippets: Snippet[] = [ 419 | { 420 | title: '输入框', 421 | screenshot: '', 422 | schema: { 423 | componentName: 'ProFormInput', 424 | props: {}, 425 | }, 426 | }, 427 | ]; 428 | 429 | export default { 430 | ...ProFormInputMeta, 431 | snippets, 432 | }; 433 | -------------------------------------------------------------------------------- /lowcode/pro-form-number-input/meta.ts: -------------------------------------------------------------------------------- 1 | import { ComponentMetadata, Snippet } from '@alilc/lowcode-types'; 2 | import { formItemProps } from '../shared'; 3 | 4 | const ProFormNumberInputMeta: ComponentMetadata = { 5 | componentName: 'ProFormNumberInput', 6 | group: '精选组件', 7 | category: '高级表单(next)', 8 | title: '数字输入框', 9 | hidden: true, 10 | docUrl: '', 11 | screenshot: '', 12 | devMode: 'proCode', 13 | npm: { 14 | package: '@dogtiti/next-pro-lowcode-materials', 15 | version: '{{version}}', 16 | exportName: 'ProFormNumberInput', 17 | main: 'src/index.tsx', 18 | destructuring: true, 19 | subName: '', 20 | }, 21 | configure: { 22 | props: [ 23 | formItemProps, 24 | { 25 | name: 'componentProps', 26 | title: '组件配置', 27 | extraProps: { 28 | display: 'accordion', 29 | defaultCollapsed: true, 30 | }, 31 | type: 'group', 32 | items: [ 33 | { 34 | name: 'alwaysShowTrigger', 35 | title: '展示操作', 36 | setter: 'BoolSetter', 37 | defaultValue: true, 38 | }, 39 | { 40 | name: 'value', 41 | title: '当前值', 42 | setter: ['NumberSetter', 'ExpressionSetter'], 43 | }, 44 | { 45 | name: 'defaultValue', 46 | title: '默认值', 47 | setter: ['NumberSetter', 'ExpressionSetter'], 48 | }, 49 | { 50 | name: 'size', 51 | title: { 52 | label: { 53 | type: 'i18n', 54 | zh_CN: '尺寸', 55 | en_US: 'Size', 56 | }, 57 | tip: { 58 | type: 'i18n', 59 | zh_CN: '属性: size | 说明: 尺寸\n@enumdesc 小, 中, 大', 60 | en_US: 'prop: size | description: size', 61 | }, 62 | }, 63 | setter: { 64 | componentName: 'RadioGroupSetter', 65 | props: { 66 | options: ['small', 'medium', 'large'], 67 | }, 68 | }, 69 | defaultValue: 'medium', 70 | }, 71 | { 72 | name: 'type', 73 | title: '类型', 74 | defaultValue: 'normal', 75 | setter: { 76 | componentName: 'MixedSetter', 77 | props: { 78 | setters: [ 79 | { 80 | componentName: 'RadioGroupSetter', 81 | props: { 82 | options: [ 83 | { 84 | title: '普通', 85 | value: 'normal', 86 | }, 87 | { 88 | title: '内联', 89 | value: 'inline', 90 | }, 91 | ], 92 | }, 93 | }, 94 | 'ExpressionSetter', 95 | ], 96 | }, 97 | }, 98 | }, 99 | { 100 | name: 'innerAfter', 101 | title: '单位', 102 | setter: ['StringSetter', 'ExpressionSetter'], 103 | }, 104 | { 105 | name: 'step', 106 | title: '步长', 107 | defaultValue: 1, 108 | setter: ['NumberSetter', 'ExpressionSetter'], 109 | }, 110 | { 111 | name: 'precision', 112 | title: '小数位数', 113 | defaultValue: 0, 114 | setter: ['NumberSetter', 'ExpressionSetter'], 115 | }, 116 | { 117 | name: 'max', 118 | title: '最大值', 119 | setter: ['NumberSetter', 'ExpressionSetter'], 120 | }, 121 | { 122 | name: 'min', 123 | title: '最小值', 124 | setter: ['NumberSetter', 'ExpressionSetter'], 125 | }, 126 | { 127 | name: 'editable', 128 | title: '可以输入', 129 | defaultValue: true, 130 | setter: ['BoolSetter', 'ExpressionSetter'], 131 | }, 132 | { 133 | name: 'format', 134 | title: '格式化', 135 | display: 'block', 136 | setter: { 137 | componentName: 'FunctionSetter', 138 | }, 139 | }, 140 | { 141 | name: 'style', 142 | setter: { 143 | componentName: 'StyleSetter', 144 | }, 145 | }, 146 | ], 147 | }, 148 | ], 149 | supports: { 150 | style: true, 151 | events: ['onPressEnter', 'onClear', 'onChange', 'onKeyDown', 'onFocus', 'onBlur'], 152 | }, 153 | }, 154 | }; 155 | const snippets: Snippet[] = [ 156 | { 157 | title: '数字输入框', 158 | screenshot: '', 159 | schema: { 160 | componentName: 'ProFormNumberInput', 161 | props: {}, 162 | }, 163 | }, 164 | ]; 165 | 166 | export default { 167 | ...ProFormNumberInputMeta, 168 | snippets, 169 | }; 170 | -------------------------------------------------------------------------------- /lowcode/pro-form-select/meta.ts: -------------------------------------------------------------------------------- 1 | import { ComponentMetadata, Snippet } from '@alilc/lowcode-types'; 2 | import { formItemProps } from '../shared'; 3 | 4 | const ProFormSelectMeta: ComponentMetadata = { 5 | componentName: 'ProFormSelect', 6 | group: '精选组件', 7 | category: '高级表单(next)', 8 | title: '选择器', 9 | hidden: true, 10 | docUrl: '', 11 | screenshot: '', 12 | devMode: 'proCode', 13 | npm: { 14 | package: '@dogtiti/next-pro-lowcode-materials', 15 | version: '{{version}}', 16 | exportName: 'ProFormSelect', 17 | main: 'src/index.tsx', 18 | destructuring: true, 19 | subName: '', 20 | }, 21 | configure: { 22 | props: [ 23 | formItemProps, 24 | { 25 | name: 'componentProps', 26 | title: '组件配置', 27 | extraProps: { 28 | display: 'accordion', 29 | defaultCollapsed: true, 30 | }, 31 | type: 'group', 32 | items: [ 33 | { 34 | name: 'placeholder', 35 | title: { 36 | label: '占位提示', 37 | tip: '属性: placeholder', 38 | }, 39 | defaultValue: '请选择', 40 | setter: 'StringSetter', 41 | }, 42 | { 43 | name: 'hasClear', 44 | title: { 45 | label: '清除按钮', 46 | tip: '属性: hasClear', 47 | }, 48 | setter: 'BoolSetter', 49 | defaultValue: false, 50 | }, 51 | { 52 | name: 'showSearch', 53 | title: { 54 | label: '可搜索', 55 | tip: '属性: showSearch', 56 | }, 57 | setter: 'BoolSetter', 58 | defaultValue: false, 59 | }, 60 | { 61 | name: 'dataSource', 62 | display: 'block', 63 | title: '选项', 64 | tip: { 65 | title: '数据格式', 66 | url: '', 67 | }, 68 | setter: { 69 | componentName: 'MixedSetter', 70 | props: { 71 | setters: [ 72 | { 73 | componentName: 'ArraySetter', 74 | props: { 75 | itemSetter: { 76 | componentName: 'ObjectSetter', 77 | props: { 78 | config: { 79 | items: [ 80 | { 81 | name: 'label', 82 | title: '标题', 83 | setter: 'StringSetter', 84 | important: true, 85 | }, 86 | { 87 | name: 'value', 88 | title: '值', 89 | setter: 'StringSetter', 90 | important: true, 91 | }, 92 | ], 93 | }, 94 | }, 95 | initialValue: { 96 | title: 'Title', 97 | }, 98 | }, 99 | }, 100 | }, 101 | 'ExpressionSetter', 102 | ], 103 | }, 104 | }, 105 | }, 106 | { 107 | name: 'mode', 108 | title: { 109 | label: '模式', 110 | tip: '属性: mode', 111 | }, 112 | setter: { 113 | componentName: 'RadioGroupSetter', 114 | props: { 115 | defaultValue: 'single', 116 | options: [ 117 | { 118 | value: 'single', 119 | title: '单选', 120 | }, 121 | { 122 | value: 'multiple', 123 | title: '多选', 124 | }, 125 | { 126 | value: 'tag', 127 | title: '标签', 128 | }, 129 | ], 130 | }, 131 | }, 132 | }, 133 | { 134 | type: 'group', 135 | title: '其他配置', 136 | display: 'block', 137 | items: [ 138 | { 139 | name: 'notFoundContent', 140 | title: { 141 | label: '空文案', 142 | tip: 'notFoundContent|弹层内容为空的文案', 143 | }, 144 | setter: 'StringSetter', 145 | }, 146 | { 147 | name: 'hasBorder', 148 | title: { 149 | label: '边框', 150 | tip: '是否有边框', 151 | }, 152 | setter: 'BoolSetter', 153 | }, 154 | { 155 | name: 'autoWidth', 156 | title: '下拉等宽', 157 | setter: 'BoolSetter', 158 | }, 159 | { 160 | name: 'hasArrow', 161 | title: '下拉箭头', 162 | setter: 'BoolSetter', 163 | defaultValue: true, 164 | }, 165 | ], 166 | }, 167 | ], 168 | }, 169 | ], 170 | supports: { 171 | style: true, 172 | events: [ 173 | { 174 | name: 'onChange', 175 | propType: 'func', 176 | description: '值发生变化', 177 | }, 178 | { 179 | name: 'onVisibleChange', 180 | propType: 'func', 181 | description: '弹层显示隐藏变化', 182 | }, 183 | { 184 | name: 'onRemove', 185 | propType: 'func', 186 | description: 'Tag 被删除', 187 | }, 188 | { 189 | name: 'onSearch', 190 | propType: 'func', 191 | description: '搜索', 192 | }, 193 | ], 194 | }, 195 | }, 196 | }; 197 | const snippets: Snippet[] = [ 198 | { 199 | title: '选择器', 200 | screenshot: '', 201 | schema: { 202 | componentName: 'ProFormSelect', 203 | props: {}, 204 | }, 205 | }, 206 | ]; 207 | 208 | export default { 209 | ...ProFormSelectMeta, 210 | snippets, 211 | }; 212 | -------------------------------------------------------------------------------- /lowcode/pro-form/meta.ts: -------------------------------------------------------------------------------- 1 | import { ComponentMetadata, Snippet } from '@alilc/lowcode-types'; 2 | import { createElement } from 'react'; 3 | import { nanoid } from 'nanoid'; 4 | 5 | const ProFormMeta: ComponentMetadata = { 6 | group: '精选组件', 7 | category: '高级表单(next)', 8 | componentName: 'ProForm', 9 | title: '高级表单', 10 | docUrl: '', 11 | screenshot: '', 12 | devMode: 'proCode', 13 | npm: { 14 | package: '@dogtiti/next-pro-lowcode-materials', 15 | version: '{{version}}', 16 | exportName: 'ProForm', 17 | main: 'src/index.tsx', 18 | destructuring: true, 19 | subName: '', 20 | }, 21 | props: [], 22 | configure: { 23 | component: { 24 | isContainer: true, 25 | isMinimalRenderUnit: true, 26 | nestingRule: { 27 | childWhitelist: /ProForm.*/i, 28 | }, 29 | }, 30 | props: [ 31 | { 32 | name: 'globalConfig', 33 | title: '全局配置', 34 | type: 'group', 35 | display: 'accordion', 36 | items: [ 37 | { 38 | name: 'status', 39 | virtual: () => true, 40 | title: '状态', 41 | setter: { 42 | componentName: 'RadioGroupSetter', 43 | props: { 44 | options: [ 45 | { 46 | title: '只读态', 47 | value: 'readonly', 48 | }, 49 | { 50 | title: '编辑态', 51 | value: 'editable', 52 | }, 53 | ], 54 | }, 55 | }, 56 | defaultValue: 'editable', 57 | }, 58 | { 59 | name: 'columns', 60 | title: '布局', 61 | setter: { 62 | componentName: 'RadioGroupSetter', 63 | props: { 64 | options: [ 65 | { 66 | title: '一列', 67 | value: 1, 68 | }, 69 | { 70 | title: '二列', 71 | value: 2, 72 | }, 73 | { 74 | title: '三列', 75 | value: 3, 76 | }, 77 | { 78 | title: '四列', 79 | value: 4, 80 | }, 81 | ], 82 | }, 83 | }, 84 | }, 85 | { 86 | name: 'labelAlign', 87 | title: { 88 | label: { 89 | type: 'i18n', 90 | zh_CN: '标签位置', 91 | en_US: 'Label Align', 92 | }, 93 | tip: { 94 | type: 'i18n', 95 | zh_CN: '属性: labelAlign | 说明: 标签的位置\n@enumdesc 上, 左, 内', 96 | en_US: 'prop: labelAlign | description: label align', 97 | }, 98 | }, 99 | setter: { 100 | componentName: 'RadioGroupSetter', 101 | props: { 102 | options: [ 103 | { 104 | title: '上', 105 | value: 'top', 106 | }, 107 | { 108 | title: '左', 109 | value: 'left', 110 | }, 111 | { 112 | title: '内', 113 | value: 'inset', 114 | }, 115 | ], 116 | }, 117 | }, 118 | defaultValue: 'top', 119 | }, 120 | { 121 | name: 'labelCol.fixedSpan', 122 | title: '标题宽度', 123 | condition: (target) => { 124 | return target.parent.getPropValue('labelAlign') === 'left'; 125 | }, 126 | setter: { 127 | componentName: 'NumberSetter', 128 | props: { 129 | min: 0, 130 | max: 24, 131 | }, 132 | }, 133 | }, 134 | { 135 | name: 'labelCol.offset', 136 | title: '标题偏移', 137 | condition: (target) => { 138 | return target.parent.getPropValue('labelAlign') === 'left'; 139 | }, 140 | setter: { 141 | componentName: 'NumberSetter', 142 | props: { 143 | min: 0, 144 | max: 24, 145 | }, 146 | }, 147 | }, 148 | { 149 | name: 'wrapperCol.span', 150 | title: '内容宽度', 151 | condition: (target) => { 152 | const labelAlign = target.parent.getPropValue('labelAlign'); 153 | return labelAlign === 'left' || labelAlign === 'inset'; 154 | }, 155 | setter: { 156 | componentName: 'NumberSetter', 157 | props: { 158 | min: 0, 159 | max: 24, 160 | }, 161 | }, 162 | }, 163 | { 164 | name: 'wrapperCol.offset', 165 | title: '内容偏移', 166 | condition: (target) => { 167 | const labelAlign = target.parent.getPropValue('labelAlign'); 168 | return labelAlign === 'left' || labelAlign === 'inset'; 169 | }, 170 | setter: { 171 | componentName: 'NumberSetter', 172 | props: { 173 | min: 0, 174 | max: 24, 175 | }, 176 | }, 177 | }, 178 | { 179 | name: 'labelTextAlign', 180 | title: { 181 | label: { 182 | type: 'i18n', 183 | zh_CN: '标签对齐', 184 | en_US: 'Text Align', 185 | }, 186 | tip: { 187 | type: 'i18n', 188 | zh_CN: '属性: labelTextAlign | 说明: 标签的左右对齐方式\n@enumdesc 左, 右', 189 | en_US: 'prop: labelTextAlign | description: label text align', 190 | }, 191 | }, 192 | condition: (target) => { 193 | return target.parent.getPropValue('labelAlign') === 'left'; 194 | }, 195 | setter: { 196 | componentName: 'RadioGroupSetter', 197 | props: { 198 | options: ['left', 'right'], 199 | }, 200 | }, 201 | defaultValue: 'right', 202 | }, 203 | ], 204 | }, 205 | { 206 | name: '!items', 207 | title: '表单项', 208 | display: 'accordion', 209 | extraProps: { 210 | getValue(target: any) { 211 | const nodes = target?.node?.children?.map((child: any = {}) => { 212 | const { propsData, componentName } = child; 213 | const { formItemProps, ...componentProps } = propsData; 214 | return { componentName, componentProps, ...formItemProps }; 215 | }); 216 | return nodes; 217 | }, 218 | setValue(target: any, value) { 219 | const { node } = target; 220 | const map = {}; 221 | const adderMap = {}; 222 | if (!Array.isArray(value)) { 223 | value = []; 224 | } 225 | value.forEach((item: any = {}) => { 226 | item.componentName = item.componentName || 'ProFormInput'; 227 | map[item.primaryKey] = item; 228 | adderMap[item.primaryKey] = item; 229 | }); 230 | 231 | node.children.mergeChildren( 232 | (child) => { 233 | const targetKey = 234 | child.getPropValue('primaryKey') || 235 | child.getPropValue('formItemProps').primaryKey; 236 | if (map?.[targetKey]) { 237 | const target = map[targetKey]; 238 | const { componentName, componentProps, ...formItemProps } = target; 239 | const props = { 240 | formItemProps, 241 | ...componentProps, 242 | }; 243 | node.replaceChild(child, { 244 | componentName, 245 | props, 246 | }); 247 | delete adderMap[targetKey]; 248 | return false; 249 | } 250 | return true; 251 | }, 252 | () => { 253 | const items = []; 254 | for (const key in adderMap) { 255 | if (Object.hasOwnProperty.call(adderMap, key)) { 256 | const { componentName, componentProps, ...formItemProps } = adderMap[key] || {}; 257 | const props = { componentProps, formItemProps }; 258 | items.push({ 259 | componentName, 260 | props, 261 | }); 262 | } 263 | } 264 | return items; 265 | }, 266 | (firstChild, secondeChild) => { 267 | const first = value.findIndex( 268 | (item) => 269 | item.primaryKey === firstChild.getPropValue('primaryKey') || 270 | firstChild.getPropValue('formItemProps').primaryKey, 271 | ); 272 | const seconde = value.findIndex( 273 | (item) => 274 | item.primaryKey === secondeChild.getPropValue('primaryKey') || 275 | secondeChild.getPropValue('formItemProps').primaryKey, 276 | ); 277 | return first - seconde; 278 | }, 279 | ); 280 | }, 281 | }, 282 | setter: { 283 | componentName: 'ArraySetter', 284 | props: { 285 | itemSetter: { 286 | componentName: 'ObjectSetter', 287 | initialValue: () => { 288 | return { 289 | componentName: 'ProFormInput', 290 | primaryKey: nanoid(), 291 | label: '表单项123', 292 | size: 'medium', 293 | colSpan: 1, 294 | fullWidth: true, 295 | }; 296 | }, 297 | props: { 298 | config: { 299 | items: [ 300 | { 301 | name: 'componentName', 302 | title: '表单项组件', 303 | display: 'inline', 304 | defaultValue: 'ProFormInput', 305 | important: true, 306 | setter: { 307 | componentName: 'SelectSetter', 308 | props: { 309 | options: [ 310 | { 311 | title: '输入框', 312 | value: 'ProFormInput', 313 | }, 314 | { 315 | title: '数字输入框', 316 | value: 'ProFormNumberInput', 317 | }, 318 | { 319 | title: '选择器', 320 | value: 'ProFormSelect', 321 | }, 322 | { 323 | title: '日期选择器', 324 | value: 'ProFormDatePicker', 325 | }, 326 | ], 327 | }, 328 | }, 329 | }, 330 | { 331 | name: 'primaryKey', 332 | title: '编号', 333 | condition: () => false, 334 | setter: 'StringSetter', 335 | defaultValue: () => nanoid(), 336 | }, 337 | { 338 | name: 'name', 339 | title: { 340 | label: { 341 | type: 'i18n', 342 | zh_CN: '表单标识', 343 | en_US: 'Name', 344 | }, 345 | tip: { 346 | type: 'i18n', 347 | zh_CN: '属性: name | 说明: 表单标识,用于表单校验', 348 | en_US: 'prop: name | description: form item name', 349 | }, 350 | }, 351 | setter: 'StringSetter', 352 | }, 353 | { 354 | name: 'label', 355 | title: '标题', 356 | display: 'inline', 357 | defaultValue: '表单项', 358 | setter: 'StringSetter', 359 | important: true, 360 | supportVariable: true, 361 | }, 362 | { 363 | name: 'size', 364 | title: { 365 | label: '尺寸', 366 | tip: '单个 Item 的 size 自定义,优先级高于 Form 的 size, 并且当组件与 Item 一起使用时,组件自身设置 size 属性无效。', 367 | }, 368 | setter: { 369 | componentName: 'RadioGroupSetter', 370 | props: { 371 | options: ['small', 'medium', 'large'], 372 | }, 373 | }, 374 | defaultValue: 'medium', 375 | }, 376 | { 377 | name: 'columnSpan', 378 | title: '表单项宽度', 379 | initialValue: 1, 380 | setter: { 381 | componentName: 'RadioGroupSetter', 382 | props: { 383 | options: [ 384 | { 385 | title: '一列', 386 | value: 1, 387 | }, 388 | { 389 | title: '二列', 390 | value: 2, 391 | }, 392 | { 393 | title: '三列', 394 | value: 3, 395 | }, 396 | { 397 | title: '四列', 398 | value: 4, 399 | }, 400 | ], 401 | }, 402 | }, 403 | }, 404 | { 405 | name: 'labelTip.enable', 406 | title: '标题提示', 407 | setter: { 408 | componentName: 'BoolSetter', 409 | }, 410 | }, 411 | { 412 | name: 'labelTip.icon', 413 | title: '提示图标', 414 | setter: { 415 | componentName: 'IconSetter', 416 | }, 417 | }, 418 | { 419 | name: 'labelTip.content', 420 | title: '提示内容', 421 | setter: { 422 | componentName: 'StringSetter', 423 | }, 424 | }, 425 | { 426 | name: 'required', 427 | defaultValue: false, 428 | title: { 429 | label: '是否必填', 430 | tip: 'required | 是否必填', 431 | }, 432 | setter: { 433 | componentName: 'BoolSetter', 434 | }, 435 | extraProps: {}, 436 | }, 437 | { 438 | name: 'fullWidth', 439 | defaultValue: true, 440 | title: { 441 | label: '宽度占满', 442 | tip: '单个 Item 中表单类组件宽度是否是100%', 443 | }, 444 | setter: { 445 | componentName: 'BoolSetter', 446 | }, 447 | }, 448 | { 449 | name: 'isPreview', 450 | title: { 451 | label: '预览态', 452 | tip: '是否开启预览态', 453 | }, 454 | setter: 'BoolSetter', 455 | }, 456 | { 457 | name: 'autoValidate', 458 | title: { 459 | label: '自动校验', 460 | tip: '是否修改数据时自动触发校验', 461 | }, 462 | setter: 'BoolSetter', 463 | }, 464 | { 465 | name: '!entry', 466 | title: '组件详细配置', 467 | display: 'block', 468 | setter: (target) => { 469 | return createElement( 470 | 'div', 471 | { 472 | onClick: () => { 473 | target.node.children.get(target.parent.key).select(); 474 | }, 475 | }, 476 | '点击配置', 477 | ); 478 | }, 479 | }, 480 | ], 481 | }, 482 | }, 483 | }, 484 | }, 485 | }, 486 | }, 487 | ], 488 | supports: { 489 | style: true, 490 | events: ['saveField', 'onSubmit', 'onChange'], 491 | }, 492 | }, 493 | }; 494 | const snippets: Snippet[] = [ 495 | { 496 | title: '高级表单', 497 | screenshot: 498 | 'https://img.alicdn.com/imgextra/i2/O1CN016gn5DQ1FeXUNKdK22_!!6000000000512-55-tps-50-36.svg', 499 | schema: { 500 | componentName: 'ProForm', 501 | props: { 502 | placeholder: '请在右侧面板添加表单项+', 503 | placeholderStyle: { 504 | height: '38px', 505 | color: '#0088FF', 506 | background: '#d8d8d836', 507 | border: 0, 508 | gridArea: 'span 4 / span 4', 509 | }, 510 | columns: 4, 511 | labelCol: { 512 | fixedSpan: 4, 513 | }, 514 | labelAlign: 'top', 515 | emptyContent: '添加表单项', 516 | }, 517 | children: [ 518 | { 519 | componentName: 'ProFormInput', 520 | props: { 521 | formItemProps: { 522 | primaryKey: nanoid(), 523 | label: '表单项', 524 | size: 'medium', 525 | device: 'desktop', 526 | fullWidth: true, 527 | }, 528 | placeholder: '请输入', 529 | }, 530 | }, 531 | { 532 | componentName: 'ProFormInput', 533 | props: { 534 | formItemProps: { 535 | primaryKey: nanoid(), 536 | label: '表单项', 537 | size: 'medium', 538 | device: 'desktop', 539 | fullWidth: true, 540 | }, 541 | placeholder: '请输入', 542 | }, 543 | }, 544 | ], 545 | }, 546 | }, 547 | ]; 548 | 549 | export default { 550 | ...ProFormMeta, 551 | snippets, 552 | }; 553 | -------------------------------------------------------------------------------- /lowcode/section-form/meta.ts: -------------------------------------------------------------------------------- 1 | import { ComponentMetadata, Snippet } from '@alilc/lowcode-types'; 2 | import { nanoid } from 'nanoid'; 3 | 4 | const SectionFormMeta: ComponentMetadata = { 5 | group: '精选组件', 6 | category: '高级表单(next)', 7 | componentName: 'SectionForm', 8 | title: '区块表单', 9 | docUrl: '', 10 | screenshot: '', 11 | devMode: 'proCode', 12 | npm: { 13 | package: '@dogtiti/next-pro-lowcode-materials', 14 | version: '{{version}}', 15 | exportName: 'SectionForm', 16 | main: 'src/index.tsx', 17 | destructuring: true, 18 | subName: '', 19 | }, 20 | props: [], 21 | configure: { 22 | component: { 23 | isContainer: true, 24 | isMinimalRenderUnit: true, 25 | nestingRule: { 26 | childWhitelist: ['ChildForm'], 27 | }, 28 | }, 29 | props: [ 30 | { 31 | name: 'globalConfig', 32 | title: '全局配置', 33 | type: 'group', 34 | display: 'accordion', 35 | items: [ 36 | { 37 | name: 'field', 38 | title: { 39 | label: { 40 | type: 'i18n', 41 | zh_CN: 'Field 实例', 42 | en_US: 'Field', 43 | }, 44 | tip: { 45 | type: 'i18n', 46 | zh_CN: '属性: field | 说明: 传入 Field 实例', 47 | en_US: 'prop: field | description: field instance', 48 | }, 49 | docUrl: 50 | 'https://fusion.alibaba-inc.com/pc/component/basic/form#%E5%A4%8D%E6%9D%82%E5%8A%9F%E8%83%BD(Field)', 51 | }, 52 | setter: { 53 | componentName: 'ExpressionSetter', 54 | }, 55 | }, 56 | { 57 | name: 'color', 58 | title: '标题颜色', 59 | setter: 'ColorSetter', 60 | defaultValue: '#5584FF', 61 | }, 62 | ], 63 | }, 64 | { 65 | name: '!items', 66 | title: '区块设置', 67 | display: 'accordion', 68 | extraProps: { 69 | getValue(target: any) { 70 | const nodes = target?.node?.children?.map((child: any = {}) => { 71 | const cardProps = child.getPropValue('cardProps'); 72 | cardProps.primaryKey = cardProps.primaryKey || nanoid(); 73 | return { 74 | ...cardProps, 75 | }; 76 | }); 77 | return nodes; 78 | }, 79 | setValue(target: any, value) { 80 | const { node } = target; 81 | const map = {}; 82 | if (!Array.isArray(value)) { 83 | value = []; 84 | } 85 | value.forEach((item: any = {}) => { 86 | const { primaryKey } = item; 87 | map[primaryKey] = item; 88 | }); 89 | 90 | node.children.mergeChildren( 91 | (child) => { 92 | const targetKey = child.getPropValue('cardProps').primaryKey; 93 | if (map?.[targetKey]) { 94 | const { ...cardProps } = map[targetKey]; 95 | child.setPropValue('cardProps', cardProps); 96 | delete map[targetKey]; 97 | return false; 98 | } 99 | return true; 100 | }, 101 | () => { 102 | const items: any = []; 103 | for (const key in map) { 104 | if (Object.hasOwnProperty.call(map, key)) { 105 | const { componentName, ...cardProps } = map[key] || {}; 106 | items.push({ 107 | componentName: 'ChildForm', 108 | props: { 109 | placeholder: '请在右侧面板添加表单项+', 110 | placeholderStyle: { 111 | height: '38px', 112 | color: '#0088FF', 113 | background: '#d8d8d836', 114 | border: 0, 115 | gridArea: 'span 4 / span 4', 116 | }, 117 | columns: 4, 118 | labelCol: { 119 | fixedSpan: 4, 120 | }, 121 | labelAlign: 'top', 122 | emptyContent: '添加表单项', 123 | cardProps, 124 | }, 125 | children: [ 126 | { 127 | componentName: 'ProFormInput', 128 | props: { 129 | formItemProps: { 130 | primaryKey: nanoid(), 131 | label: '表单项', 132 | size: 'medium', 133 | device: 'desktop', 134 | fullWidth: true, 135 | }, 136 | placeholder: '请输入', 137 | }, 138 | }, 139 | { 140 | componentName: 'ProFormInput', 141 | props: { 142 | formItemProps: { 143 | primaryKey: nanoid(), 144 | label: '表单项', 145 | size: 'medium', 146 | device: 'desktop', 147 | fullWidth: true, 148 | }, 149 | placeholder: '请输入', 150 | }, 151 | }, 152 | ], 153 | }); 154 | } 155 | } 156 | return items; 157 | }, 158 | (firstChild, secondeChild) => { 159 | const first = value.findIndex( 160 | (item) => item.primaryKey === firstChild.getPropValue('cardProps').primaryKey, 161 | ); 162 | const seconde = value.findIndex( 163 | (item) => item.primaryKey === secondeChild.getPropValue('cardProps').primaryKey, 164 | ); 165 | return first - seconde; 166 | }, 167 | ); 168 | }, 169 | }, 170 | setter: { 171 | componentName: 'ArraySetter', 172 | props: { 173 | itemSetter: { 174 | componentName: 'ObjectSetter', 175 | initialValue: () => { 176 | return { 177 | title: '区块', 178 | primaryKey: nanoid(), 179 | }; 180 | }, 181 | props: { 182 | config: { 183 | items: [ 184 | { 185 | name: 'title', 186 | title: '区块名称', 187 | display: 'inline', 188 | defaultValue: '区块', 189 | important: true, 190 | setter: 'StringSetter', 191 | }, 192 | { 193 | name: 'primaryKey', 194 | title: '编号', 195 | condition: () => false, 196 | setter: 'StringSetter', 197 | defaultValue: () => nanoid(), 198 | }, 199 | ], 200 | }, 201 | }, 202 | }, 203 | }, 204 | }, 205 | }, 206 | { 207 | name: 'operations', 208 | display: 'block', 209 | title: '操作项', 210 | getValue: (target, value) => { 211 | return value || []; 212 | }, 213 | setter: { 214 | componentName: 'MixedSetter', 215 | props: { 216 | setters: [ 217 | { 218 | componentName: 'SlotSetter', 219 | defaultValue: { 220 | type: 'JSSlot', 221 | value: [], 222 | }, 223 | }, 224 | { 225 | componentName: 'ArraySetter', 226 | props: { 227 | itemSetter: { 228 | componentName: 'ObjectSetter', 229 | initialValue: () => { 230 | return { 231 | content: '提交', 232 | type: 'normal', 233 | }; 234 | }, 235 | props: { 236 | config: { 237 | items: [ 238 | { 239 | name: 'id', 240 | condition: () => false, 241 | getValue: () => { 242 | return nanoid(); 243 | }, 244 | }, 245 | { 246 | name: 'content', 247 | display: 'inline', 248 | title: '文本', 249 | setter: 'StringSetter', 250 | important: true, 251 | }, 252 | { 253 | name: 'type', 254 | display: 'inline', 255 | title: '样式', 256 | important: true, 257 | setter: { 258 | componentName: 'SelectSetter', 259 | props: { 260 | options: [ 261 | { 262 | title: '主要', 263 | value: 'primary', 264 | }, 265 | { 266 | title: '次要', 267 | value: 'secondary', 268 | }, 269 | { 270 | title: '普通', 271 | value: 'normal', 272 | }, 273 | ], 274 | }, 275 | }, 276 | }, 277 | { 278 | name: 'onClick', 279 | display: 'inline', 280 | title: '点击事件', 281 | setter: 'FunctionSetter', 282 | extraProps: { 283 | supportVariable: true, 284 | }, 285 | }, 286 | ], 287 | }, 288 | }, 289 | }, 290 | }, 291 | }, 292 | 'VariableSetter', 293 | ], 294 | }, 295 | }, 296 | }, 297 | ], 298 | supports: { 299 | style: true, 300 | }, 301 | }, 302 | }; 303 | const snippets: Snippet[] = [ 304 | { 305 | title: '区块表单', 306 | screenshot: 307 | 'https://img.alicdn.com/imgextra/i2/O1CN016gn5DQ1FeXUNKdK22_!!6000000000512-55-tps-50-36.svg', 308 | schema: { 309 | componentName: 'SectionForm', 310 | props: {}, 311 | children: [ 312 | { 313 | componentName: 'ChildForm', 314 | props: { 315 | placeholder: '请在右侧面板添加表单项+', 316 | placeholderStyle: { 317 | height: '38px', 318 | color: '#0088FF', 319 | background: '#d8d8d836', 320 | border: 0, 321 | gridArea: 'span 4 / span 4', 322 | }, 323 | columns: 4, 324 | labelCol: { 325 | fixedSpan: 4, 326 | }, 327 | labelAlign: 'top', 328 | emptyContent: '添加表单项', 329 | cardProps: { 330 | title: '区块一', 331 | primaryKey: nanoid(), 332 | }, 333 | }, 334 | children: [ 335 | { 336 | componentName: 'ProFormInput', 337 | props: { 338 | formItemProps: { 339 | primaryKey: nanoid(), 340 | label: '表单项', 341 | size: 'medium', 342 | device: 'desktop', 343 | fullWidth: true, 344 | }, 345 | placeholder: '请输入', 346 | }, 347 | }, 348 | { 349 | componentName: 'ProFormInput', 350 | props: { 351 | formItemProps: { 352 | primaryKey: nanoid(), 353 | label: '表单项', 354 | size: 'medium', 355 | device: 'desktop', 356 | fullWidth: true, 357 | }, 358 | placeholder: '请输入', 359 | }, 360 | }, 361 | ], 362 | }, 363 | ], 364 | }, 365 | }, 366 | ]; 367 | 368 | export default { 369 | ...SectionFormMeta, 370 | snippets, 371 | }; 372 | -------------------------------------------------------------------------------- /lowcode/shared/index.ts: -------------------------------------------------------------------------------- 1 | import { FieldConfig } from '@alilc/lowcode-types'; 2 | import { nanoid } from 'nanoid'; 3 | import { createElement } from 'react'; 4 | import { Button } from '@alifd/next'; 5 | 6 | export const formItemProps: FieldConfig = { 7 | name: 'formItemProps', 8 | title: '表单项配置', 9 | extraProps: { 10 | display: 'accordion', 11 | defaultCollapsed: true, 12 | }, 13 | setter: { 14 | componentName: 'ObjectSetter', 15 | props: { 16 | config: { 17 | items: [ 18 | { 19 | name: 'primaryKey', 20 | title: '编号', 21 | setter: 'StringSetter', 22 | condition: () => false, 23 | }, 24 | { 25 | name: 'id', 26 | title: { 27 | label: { 28 | type: 'i18n', 29 | zh_CN: '唯一标识', 30 | en_US: 'ID', 31 | }, 32 | tip: { 33 | type: 'i18n', 34 | zh_CN: '属性: id | 说明: 唯一标识', 35 | en_US: 'prop: id | description: switch id', 36 | }, 37 | }, 38 | setter: 'StringSetter', 39 | condition: () => false, 40 | }, 41 | { 42 | name: 'name', 43 | title: { 44 | label: { 45 | type: 'i18n', 46 | zh_CN: '表单标识', 47 | en_US: 'Name', 48 | }, 49 | tip: { 50 | type: 'i18n', 51 | zh_CN: '属性: name | 说明: 表单标识,用于表单校验', 52 | en_US: 'prop: name | description: form item name', 53 | }, 54 | }, 55 | setter: ['StringSetter', 'EntitySetter'], 56 | supportVariable: true, 57 | }, 58 | { 59 | name: 'columnSpan', 60 | title: '表单项宽度', 61 | defaultValue: 1, 62 | setter: { 63 | componentName: 'RadioGroupSetter', 64 | props: { 65 | options: [ 66 | { 67 | title: '一列', 68 | value: 1, 69 | }, 70 | { 71 | title: '二列', 72 | value: 2, 73 | }, 74 | { 75 | title: '三列', 76 | value: 3, 77 | }, 78 | { 79 | title: '四列', 80 | value: 4, 81 | }, 82 | ], 83 | }, 84 | }, 85 | }, 86 | { 87 | name: 'label', 88 | title: '标题', 89 | display: 'inline', 90 | defaultValue: '表单项', 91 | setter: 'StringSetter', 92 | important: true, 93 | supportVariable: true, 94 | }, 95 | { 96 | name: 'help', 97 | title: { 98 | label: { 99 | type: 'i18n', 100 | zh_CN: '错误提示', 101 | en_US: 'Help Info', 102 | }, 103 | tip: { 104 | type: 'i18n', 105 | zh_CN: '属性: help | 说明: 自定义提示信息, 如不设置,则会根据校验规则自动生成.', 106 | en_US: 'prop: help | description: help infomation', 107 | }, 108 | }, 109 | setter: 'StringSetter', 110 | }, 111 | { 112 | name: 'extra', 113 | title: { 114 | label: { 115 | type: 'i18n', 116 | zh_CN: '帮助提示', 117 | en_US: 'Extra Info', 118 | }, 119 | tip: { 120 | type: 'i18n', 121 | zh_CN: 122 | '属性: extra | 说明: 额外的提示信息, 和 help 类似,当需要错误信息和提示文案同时出现时,可以使用这个。 位于错误信息后面', 123 | en_US: 'prop: extra | description: extra infomation', 124 | }, 125 | }, 126 | setter: 'StringSetter', 127 | }, 128 | { 129 | name: 'validateState', 130 | title: { 131 | label: '校验状态', 132 | tip: '如不设置,则会根据校验规则自动生成\n@enumdesc 失败, 成功, 校验中, 警告', 133 | }, 134 | setter: { 135 | componentName: 'RadioGroupSetter', 136 | props: { 137 | options: ['error', 'success', 'loading', 'warning'], 138 | }, 139 | }, 140 | }, 141 | { 142 | name: 'size', 143 | title: { 144 | label: '尺寸', 145 | tip: '单个 Item 的 size 自定义,优先级高于 Form 的 size, 并且当组件与 Item 一起使用时,组件自身设置 size 属性无效。', 146 | }, 147 | setter: { 148 | componentName: 'RadioGroupSetter', 149 | props: { 150 | options: ['small', 'medium', 'large'], 151 | }, 152 | }, 153 | defaultValue: 'medium', 154 | }, 155 | { 156 | name: 'labelAlign', 157 | condition: () => false, 158 | title: { 159 | label: '标签位置', 160 | tip: '上, 左, 内', 161 | }, 162 | getValue: (target) => { 163 | const value = target.getProps().node.parent.getPropValue(target.name); 164 | return value; 165 | }, 166 | setter: { 167 | componentName: 'RadioGroupSetter', 168 | props: { 169 | options: [ 170 | { 171 | title: '上', 172 | value: 'top', 173 | }, 174 | { 175 | title: '左', 176 | value: 'left', 177 | }, 178 | { 179 | title: '内', 180 | value: 'inset', 181 | }, 182 | { 183 | title: '默认', 184 | value: '', 185 | }, 186 | ], 187 | }, 188 | }, 189 | }, 190 | { 191 | name: 'labelCol.fixedSpan', 192 | title: '标题宽度', 193 | condition: (target) => { 194 | return target.parent.getPropValue('labelAlign') === 'left'; 195 | }, 196 | setter: { 197 | componentName: 'NumberSetter', 198 | props: { 199 | min: 0, 200 | max: 24, 201 | }, 202 | }, 203 | }, 204 | { 205 | name: 'labelCol.offset', 206 | title: '标题偏移', 207 | condition: (target) => { 208 | return target.parent.getPropValue('labelAlign') === 'left'; 209 | }, 210 | setter: { 211 | componentName: 'NumberSetter', 212 | props: { 213 | min: 0, 214 | max: 24, 215 | }, 216 | }, 217 | }, 218 | { 219 | name: 'wrapperCol.span', 220 | title: '内容宽度', 221 | condition: (target) => { 222 | const labelAlign = target.parent.getPropValue('labelAlign'); 223 | return labelAlign === 'left' || labelAlign === 'inset'; 224 | }, 225 | setter: { 226 | componentName: 'NumberSetter', 227 | props: { 228 | min: 0, 229 | max: 24, 230 | }, 231 | }, 232 | }, 233 | { 234 | name: 'wrapperCol.offset', 235 | title: '内容偏移', 236 | condition: (target) => { 237 | const labelAlign = target.parent.getPropValue('labelAlign'); 238 | return labelAlign === 'left' || labelAlign === 'inset'; 239 | }, 240 | setter: { 241 | componentName: 'NumberSetter', 242 | props: { 243 | min: 0, 244 | max: 24, 245 | }, 246 | }, 247 | }, 248 | { 249 | name: 'labelTip.enable', 250 | title: '标题提示', 251 | setter: { 252 | componentName: 'BoolSetter', 253 | }, 254 | }, 255 | { 256 | name: 'labelTip.icon', 257 | title: '提示图标', 258 | condition: (target) => { 259 | return target.parent.getPropValue('labelTip.enable'); 260 | }, 261 | setter: { 262 | componentName: 'IconSetter', 263 | }, 264 | }, 265 | { 266 | name: 'labelTip.content', 267 | title: '提示内容', 268 | condition: (target) => { 269 | return target.parent.getPropValue('labelTip.enable'); 270 | }, 271 | setter: { 272 | componentName: 'StringSetter', 273 | }, 274 | }, 275 | { 276 | name: 'labelTextAlign', 277 | title: { 278 | label: '标签对齐', 279 | tip: '左, 右', 280 | }, 281 | condition: () => false, 282 | setter: { 283 | componentName: 'RadioGroupSetter', 284 | props: { 285 | options: [ 286 | { 287 | title: '左', 288 | value: 'left', 289 | }, 290 | { 291 | title: '右', 292 | value: 'right', 293 | }, 294 | { 295 | title: '默认', 296 | value: '', 297 | }, 298 | ], 299 | }, 300 | }, 301 | defaultValue: 'right', 302 | }, 303 | { 304 | name: 'device', 305 | title: { 306 | label: '设备', 307 | }, 308 | condition: () => false, 309 | setter: { 310 | componentName: 'RadioGroupSetter', 311 | props: { 312 | options: ['phone', 'tablet', 'desktop'], 313 | }, 314 | }, 315 | defaultValue: 'desktop', 316 | }, 317 | { 318 | name: 'required', 319 | defaultValue: false, 320 | title: { 321 | label: '是否必填', 322 | tip: 'required | 是否必填', 323 | }, 324 | setter: { 325 | componentName: 'BoolSetter', 326 | }, 327 | extraProps: { 328 | setValue(target, value) { 329 | if (value === true && !target.parent.getPropValue('name')) { 330 | target.setPropValue('name', nanoid()); 331 | target.parent.setValue(target.parent.getValue()); 332 | } 333 | }, 334 | }, 335 | }, 336 | { 337 | name: 'fullWidth', 338 | defaultValue: true, 339 | title: { 340 | label: '宽度占满', 341 | tip: '单个 Item 中表单类组件宽度是否是100%', 342 | }, 343 | setter: { 344 | componentName: 'BoolSetter', 345 | }, 346 | }, 347 | { 348 | name: 'isPreview', 349 | title: { 350 | label: '预览态', 351 | tip: '是否开启预览态', 352 | }, 353 | setter: 'BoolSetter', 354 | }, 355 | { 356 | name: 'autoValidate', 357 | title: { 358 | label: '自动校验', 359 | tip: '是否修改数据时自动触发校验', 360 | }, 361 | setter: 'BoolSetter', 362 | }, 363 | { 364 | type: 'group', 365 | name: 'validation', 366 | display: 'accordion', 367 | defaultCollapsed: true, 368 | title: '校验', 369 | items: [ 370 | { 371 | type: 'group', 372 | name: 'notNullValidation', 373 | display: 'popup', 374 | title: '非空校验', 375 | items: [ 376 | { 377 | name: 'required', 378 | title: { 379 | label: '不能为空', 380 | tip: '[表单校验] 不能为空', 381 | }, 382 | setter: 'BoolSetter', 383 | }, 384 | { 385 | name: 'requiredMessage', 386 | title: { 387 | label: '错误信息', 388 | tip: '[表单校验]为空时自定义错误信息', 389 | }, 390 | setter: 'StringSetter', 391 | }, 392 | ], 393 | }, 394 | { 395 | type: 'group', 396 | name: 'maxValidation', 397 | display: 'popup', 398 | title: '最大/最小值校验', 399 | items: [ 400 | { 401 | name: 'min', 402 | title: { 403 | label: '最小值', 404 | tip: '[表单校验] 最小值', 405 | }, 406 | setter: 'NumberSetter', 407 | }, 408 | { 409 | name: 'max', 410 | title: { 411 | label: '最大值', 412 | tip: '[表单校验] 最大值', 413 | }, 414 | setter: 'NumberSetter', 415 | }, 416 | { 417 | name: 'minmaxMessage', 418 | title: { 419 | label: '错误信息', 420 | tip: '[表单校验] min/max 自定义错误信息', 421 | }, 422 | setter: 'StringSetter', 423 | }, 424 | ], 425 | }, 426 | { 427 | type: 'group', 428 | name: 'maxLenValidation', 429 | display: 'popup', 430 | title: '最大/最小长度校验', 431 | items: [ 432 | { 433 | name: 'minLength', 434 | title: { 435 | label: '最小长度', 436 | tip: '[表单校验] 字符串最小长度 / 数组最小个数', 437 | }, 438 | setter: 'NumberSetter', 439 | }, 440 | { 441 | name: 'maxLength', 442 | title: { 443 | label: '最大长度', 444 | tip: '[表单校验] 字符串最大长度 / 数组最大个数', 445 | }, 446 | setter: 'NumberSetter', 447 | }, 448 | { 449 | name: 'minmaxLengthMessage', 450 | title: { 451 | label: '错误信息', 452 | tip: '[表单校验] minLength/maxLength 自定义错误信息', 453 | }, 454 | setter: 'StringSetter', 455 | }, 456 | ], 457 | }, 458 | { 459 | type: 'group', 460 | name: 'lengthValidation', 461 | display: 'popup', 462 | title: '长度校验', 463 | items: [ 464 | { 465 | name: 'length', 466 | title: { 467 | label: '长度', 468 | tip: '[表单校验] 字符串精确长度 / 数组精确个数', 469 | }, 470 | setter: 'NumberSetter', 471 | }, 472 | { 473 | name: 'lengthMessage', 474 | title: { 475 | label: '错误信息', 476 | tip: '[表单校验] minLength/maxLength 自定义错误信息', 477 | }, 478 | setter: 'StringSetter', 479 | }, 480 | ], 481 | }, 482 | { 483 | type: 'group', 484 | name: 'regValidation', 485 | display: 'popup', 486 | title: '正则校验', 487 | items: [ 488 | { 489 | name: 'pattern', 490 | title: { 491 | label: '正则', 492 | tip: '[表单校验] 正则校验', 493 | }, 494 | setter: 'StringSetter', 495 | }, 496 | { 497 | name: 'patternMessage', 498 | title: { 499 | label: '错误信息', 500 | tip: '[表单校验] pattern 自定义错误信息', 501 | }, 502 | setter: 'StringSetter', 503 | }, 504 | ], 505 | }, 506 | { 507 | type: 'group', 508 | name: 'formatValidation', 509 | display: 'popup', 510 | title: '格式化校验', 511 | items: [ 512 | { 513 | name: 'format', 514 | title: { 515 | label: 'format', 516 | tip: '[表单校验] 四种常用的 pattern', 517 | }, 518 | setter: { 519 | componentName: 'RadioGroupSetter', 520 | props: { 521 | options: ['number', 'email', 'url', 'tel'], 522 | }, 523 | }, 524 | }, 525 | { 526 | name: 'formatMessage', 527 | title: { 528 | label: '错误信息', 529 | tip: '[表单校验] format 自定义错误信息', 530 | }, 531 | setter: 'StringSetter', 532 | }, 533 | ], 534 | }, 535 | { 536 | name: 'validator', 537 | display: 'popup', 538 | title: { 539 | label: '自定义校验函数', 540 | }, 541 | setter: 'FunctionSetter', 542 | }, 543 | ], 544 | }, 545 | ], 546 | }, 547 | }, 548 | }, 549 | }; 550 | 551 | export const formProps: FieldConfig[] = [ 552 | { 553 | name: 'globalConfig', 554 | title: '全局配置', 555 | type: 'group', 556 | display: 'accordion', 557 | items: [ 558 | { 559 | name: 'field', 560 | title: { 561 | label: { 562 | type: 'i18n', 563 | zh_CN: 'Field 实例', 564 | en_US: 'Field', 565 | }, 566 | tip: { 567 | type: 'i18n', 568 | zh_CN: '属性: field | 说明: 传入 Field 实例', 569 | en_US: 'prop: field | description: field instance', 570 | }, 571 | docUrl: 572 | 'https://fusion.alibaba-inc.com/pc/component/basic/form#%E5%A4%8D%E6%9D%82%E5%8A%9F%E8%83%BD(Field)', 573 | }, 574 | setter: { 575 | componentName: 'ExpressionSetter', 576 | }, 577 | }, 578 | { 579 | name: 'status', 580 | virtual: () => true, 581 | title: '状态', 582 | setter: { 583 | componentName: 'RadioGroupSetter', 584 | props: { 585 | options: [ 586 | { 587 | title: '只读态', 588 | value: 'readonly', 589 | }, 590 | { 591 | title: '编辑态', 592 | value: 'editable', 593 | }, 594 | ], 595 | }, 596 | }, 597 | defaultValue: 'editable', 598 | }, 599 | { 600 | name: 'columns', 601 | title: '布局', 602 | setter: { 603 | componentName: 'RadioGroupSetter', 604 | props: { 605 | options: [ 606 | { 607 | title: '一列', 608 | value: 1, 609 | }, 610 | { 611 | title: '二列', 612 | value: 2, 613 | }, 614 | { 615 | title: '三列', 616 | value: 3, 617 | }, 618 | { 619 | title: '四列', 620 | value: 4, 621 | }, 622 | ], 623 | }, 624 | }, 625 | }, 626 | { 627 | name: 'labelAlign', 628 | title: { 629 | label: { 630 | type: 'i18n', 631 | zh_CN: '标签位置', 632 | en_US: 'Label Align', 633 | }, 634 | tip: { 635 | type: 'i18n', 636 | zh_CN: '属性: labelAlign | 说明: 标签的位置\n@enumdesc 上, 左, 内', 637 | en_US: 'prop: labelAlign | description: label align', 638 | }, 639 | }, 640 | setter: { 641 | componentName: 'RadioGroupSetter', 642 | props: { 643 | options: [ 644 | { 645 | title: '上', 646 | value: 'top', 647 | }, 648 | { 649 | title: '左', 650 | value: 'left', 651 | }, 652 | { 653 | title: '内', 654 | value: 'inset', 655 | }, 656 | ], 657 | }, 658 | }, 659 | defaultValue: 'top', 660 | }, 661 | { 662 | name: 'labelCol.fixedSpan', 663 | title: '标题宽度', 664 | condition: (target) => { 665 | return target.parent.getPropValue('labelAlign') === 'left'; 666 | }, 667 | setter: { 668 | componentName: 'NumberSetter', 669 | props: { 670 | min: 0, 671 | max: 24, 672 | }, 673 | }, 674 | }, 675 | { 676 | name: 'labelCol.offset', 677 | title: '标题偏移', 678 | condition: (target) => { 679 | return target.parent.getPropValue('labelAlign') === 'left'; 680 | }, 681 | setter: { 682 | componentName: 'NumberSetter', 683 | props: { 684 | min: 0, 685 | max: 24, 686 | }, 687 | }, 688 | }, 689 | { 690 | name: 'wrapperCol.span', 691 | title: '内容宽度', 692 | condition: (target) => { 693 | const labelAlign = target.parent.getPropValue('labelAlign'); 694 | return labelAlign === 'left' || labelAlign === 'inset'; 695 | }, 696 | setter: { 697 | componentName: 'NumberSetter', 698 | props: { 699 | min: 0, 700 | max: 24, 701 | }, 702 | }, 703 | }, 704 | { 705 | name: 'wrapperCol.offset', 706 | title: '内容偏移', 707 | condition: (target) => { 708 | const labelAlign = target.parent.getPropValue('labelAlign'); 709 | return labelAlign === 'left' || labelAlign === 'inset'; 710 | }, 711 | setter: { 712 | componentName: 'NumberSetter', 713 | props: { 714 | min: 0, 715 | max: 24, 716 | }, 717 | }, 718 | }, 719 | { 720 | name: 'labelTextAlign', 721 | title: { 722 | label: { 723 | type: 'i18n', 724 | zh_CN: '标签对齐', 725 | en_US: 'Text Align', 726 | }, 727 | tip: { 728 | type: 'i18n', 729 | zh_CN: '属性: labelTextAlign | 说明: 标签的左右对齐方式\n@enumdesc 左, 右', 730 | en_US: 'prop: labelTextAlign | description: label text align', 731 | }, 732 | }, 733 | condition: (target) => { 734 | return target.parent.getPropValue('labelAlign') === 'left'; 735 | }, 736 | setter: { 737 | componentName: 'RadioGroupSetter', 738 | props: { 739 | options: ['left', 'right'], 740 | }, 741 | }, 742 | defaultValue: 'right', 743 | }, 744 | ], 745 | }, 746 | { 747 | name: '!items', 748 | title: '表单项', 749 | display: 'accordion', 750 | extraProps: { 751 | getValue(target: any) { 752 | const nodes = target?.node?.children?.map((child: any = {}) => { 753 | const { propsData, componentName } = child; 754 | const { formItemProps, ...componentProps } = propsData; 755 | return { componentName, componentProps, ...formItemProps }; 756 | }); 757 | return nodes; 758 | }, 759 | setValue(target: any, value) { 760 | const { node } = target; 761 | const map = {}; 762 | const adderMap = {}; 763 | if (!Array.isArray(value)) { 764 | value = []; 765 | } 766 | value.forEach((item: any = {}) => { 767 | item.componentName = item.componentName || 'ProFormInput'; 768 | map[item.primaryKey] = item; 769 | adderMap[item.primaryKey] = item; 770 | }); 771 | node.children.mergeChildren( 772 | (child) => { 773 | const targetKey = 774 | child.getPropValue('primaryKey') || child.getPropValue('formItemProps').primaryKey; 775 | if (map?.[targetKey]) { 776 | const target = map[targetKey]; 777 | const { componentName, componentProps, ...formItemProps } = target; 778 | const props = { 779 | formItemProps, 780 | ...componentProps, 781 | }; 782 | node.replaceChild(child, { 783 | componentName, 784 | props, 785 | }); 786 | delete adderMap[targetKey]; 787 | return false; 788 | } 789 | return true; 790 | }, 791 | () => { 792 | const items = []; 793 | for (const key in adderMap) { 794 | if (Object.hasOwnProperty.call(adderMap, key)) { 795 | const { componentName, componentProps, ...formItemProps } = adderMap[key] || {}; 796 | const props = { componentProps, formItemProps }; 797 | items.push({ 798 | componentName, 799 | props, 800 | }); 801 | } 802 | } 803 | return items; 804 | }, 805 | (firstChild, secondeChild) => { 806 | const first = value.findIndex( 807 | (item) => 808 | item.primaryKey === firstChild.getPropValue('primaryKey') || 809 | item.primaryKey === firstChild.getPropValue('formItemProps').primaryKey, 810 | ); 811 | const seconde = value.findIndex( 812 | (item) => 813 | item.primaryKey === secondeChild.getPropValue('primaryKey') || 814 | item.primaryKey === secondeChild.getPropValue('formItemProps').primaryKey, 815 | ); 816 | return first - seconde; 817 | }, 818 | ); 819 | }, 820 | }, 821 | setter: { 822 | componentName: 'ArraySetter', 823 | props: { 824 | itemSetter: { 825 | componentName: 'ObjectSetter', 826 | initialValue: () => { 827 | return { 828 | componentName: 'ProFormInput', 829 | primaryKey: nanoid(), 830 | label: '表单项', 831 | size: 'medium', 832 | colSpan: 1, 833 | fullWidth: true, 834 | }; 835 | }, 836 | props: { 837 | config: { 838 | items: [ 839 | { 840 | name: 'componentName', 841 | title: '表单项组件', 842 | display: 'inline', 843 | defaultValue: 'ProFormInput', 844 | important: true, 845 | setter: { 846 | componentName: 'SelectSetter', 847 | props: { 848 | options: [ 849 | { 850 | title: '输入框', 851 | value: 'ProFormInput', 852 | }, 853 | { 854 | title: '数字输入框', 855 | value: 'ProFormNumberInput', 856 | }, 857 | { 858 | title: '选择器', 859 | value: 'ProFormSelect', 860 | }, 861 | { 862 | title: '日期选择器', 863 | value: 'ProFormDatePicker', 864 | }, 865 | ], 866 | }, 867 | }, 868 | }, 869 | { 870 | name: 'primaryKey', 871 | title: '编号', 872 | condition: () => false, 873 | setter: 'StringSetter', 874 | defaultValue: () => nanoid(), 875 | }, 876 | { 877 | name: 'name', 878 | title: { 879 | label: { 880 | type: 'i18n', 881 | zh_CN: '表单标识', 882 | en_US: 'Name', 883 | }, 884 | tip: { 885 | type: 'i18n', 886 | zh_CN: '属性: name | 说明: 表单标识,用于表单校验', 887 | en_US: 'prop: name | description: form item name', 888 | }, 889 | }, 890 | setter: 'StringSetter', 891 | }, 892 | { 893 | name: 'label', 894 | title: '标题', 895 | display: 'inline', 896 | defaultValue: '表单项', 897 | setter: 'StringSetter', 898 | important: true, 899 | supportVariable: true, 900 | }, 901 | { 902 | name: 'size', 903 | title: { 904 | label: '尺寸', 905 | tip: '单个 Item 的 size 自定义,优先级高于 Form 的 size, 并且当组件与 Item 一起使用时,组件自身设置 size 属性无效。', 906 | }, 907 | setter: { 908 | componentName: 'RadioGroupSetter', 909 | props: { 910 | options: ['small', 'medium', 'large'], 911 | }, 912 | }, 913 | defaultValue: 'medium', 914 | }, 915 | { 916 | name: 'columnSpan', 917 | title: '表单项宽度', 918 | initialValue: 1, 919 | setter: { 920 | componentName: 'RadioGroupSetter', 921 | props: { 922 | options: [ 923 | { 924 | title: '一列', 925 | value: 1, 926 | }, 927 | { 928 | title: '二列', 929 | value: 2, 930 | }, 931 | { 932 | title: '三列', 933 | value: 3, 934 | }, 935 | { 936 | title: '四列', 937 | value: 4, 938 | }, 939 | ], 940 | }, 941 | }, 942 | }, 943 | { 944 | name: 'labelTip.enable', 945 | title: '标题提示', 946 | setter: { 947 | componentName: 'BoolSetter', 948 | }, 949 | }, 950 | { 951 | name: 'labelTip.icon', 952 | title: '提示图标', 953 | setter: { 954 | componentName: 'IconSetter', 955 | }, 956 | }, 957 | { 958 | name: 'labelTip.content', 959 | title: '提示内容', 960 | setter: { 961 | componentName: 'StringSetter', 962 | }, 963 | }, 964 | { 965 | name: 'required', 966 | defaultValue: false, 967 | title: { 968 | label: '是否必填', 969 | tip: 'required | 是否必填', 970 | }, 971 | setter: { 972 | componentName: 'BoolSetter', 973 | }, 974 | extraProps: {}, 975 | }, 976 | { 977 | name: 'fullWidth', 978 | defaultValue: true, 979 | title: { 980 | label: '宽度占满', 981 | tip: '单个 Item 中表单类组件宽度是否是100%', 982 | }, 983 | setter: { 984 | componentName: 'BoolSetter', 985 | }, 986 | }, 987 | { 988 | name: 'isPreview', 989 | title: { 990 | label: '预览态', 991 | tip: '是否开启预览态', 992 | }, 993 | setter: 'BoolSetter', 994 | }, 995 | { 996 | name: 'autoValidate', 997 | title: { 998 | label: '自动校验', 999 | tip: '是否修改数据时自动触发校验', 1000 | }, 1001 | setter: 'BoolSetter', 1002 | }, 1003 | { 1004 | name: '!entry', 1005 | title: '组件详细配置', 1006 | display: 'block', 1007 | setter: (target) => { 1008 | return createElement( 1009 | Button, 1010 | { 1011 | onClick: () => { 1012 | target.node.children.get(target.parent.key).select(); 1013 | }, 1014 | }, 1015 | '点击配置', 1016 | ); 1017 | }, 1018 | }, 1019 | ], 1020 | }, 1021 | }, 1022 | }, 1023 | }, 1024 | }, 1025 | }, 1026 | { 1027 | name: 'operations', 1028 | display: 'block', 1029 | title: '操作项', 1030 | getValue: (target, value) => { 1031 | return value || []; 1032 | }, 1033 | setter: { 1034 | componentName: 'MixedSetter', 1035 | props: { 1036 | setters: [ 1037 | { 1038 | componentName: 'SlotSetter', 1039 | defaultValue: { 1040 | type: 'JSSlot', 1041 | value: [], 1042 | }, 1043 | }, 1044 | { 1045 | componentName: 'ArraySetter', 1046 | props: { 1047 | itemSetter: { 1048 | componentName: 'ObjectSetter', 1049 | initialValue: () => { 1050 | return { 1051 | content: '提交', 1052 | type: 'normal', 1053 | }; 1054 | }, 1055 | props: { 1056 | config: { 1057 | items: [ 1058 | { 1059 | name: 'id', 1060 | condition: () => false, 1061 | getValue: () => { 1062 | return nanoid(); 1063 | }, 1064 | }, 1065 | { 1066 | name: 'content', 1067 | display: 'inline', 1068 | title: '文本', 1069 | setter: 'StringSetter', 1070 | important: true, 1071 | }, 1072 | { 1073 | name: 'type', 1074 | display: 'inline', 1075 | title: '样式', 1076 | important: true, 1077 | setter: { 1078 | componentName: 'SelectSetter', 1079 | props: { 1080 | options: [ 1081 | { 1082 | title: '主要', 1083 | value: 'primary', 1084 | }, 1085 | { 1086 | title: '次要', 1087 | value: 'secondary', 1088 | }, 1089 | { 1090 | title: '普通', 1091 | value: 'normal', 1092 | }, 1093 | ], 1094 | }, 1095 | }, 1096 | }, 1097 | { 1098 | name: 'onClick', 1099 | display: 'inline', 1100 | title: '点击事件', 1101 | setter: 'FunctionSetter', 1102 | extraProps: { 1103 | supportVariable: true, 1104 | }, 1105 | }, 1106 | ], 1107 | }, 1108 | }, 1109 | }, 1110 | }, 1111 | }, 1112 | 'VariableSetter', 1113 | ], 1114 | }, 1115 | }, 1116 | }, 1117 | ]; 1118 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dogtiti/next-pro-lowcode-materials", 3 | "version": "0.3.1", 4 | "description": "next-pro-lowcode-materials", 5 | "main": "lib/index.js", 6 | "module": "es/index.js", 7 | "typings": "types/index.d.ts", 8 | "files": [ 9 | "build", 10 | "dist", 11 | "lib", 12 | "es", 13 | "types" 14 | ], 15 | "scripts": { 16 | "start": "build-scripts start", 17 | "build": "build-scripts build", 18 | "lowcode:dev": "build-scripts start --config ./build.lowcode.js", 19 | "lowcode:build": "build-scripts build --config ./build.lowcode.js", 20 | "f2elint-scan": "f2elint scan", 21 | "f2elint-fix": "f2elint fix", 22 | "prepublishOnly": "npm run build && npm run lowcode:build" 23 | }, 24 | "directories": { 25 | "test": "test" 26 | }, 27 | "keywords": [ 28 | "Fusion" 29 | ], 30 | "author": "Dogtiti", 31 | "license": "MIT", 32 | "husky": { 33 | "hooks": { 34 | "pre-commit": "f2elint commit-file-scan", 35 | "commit-msg": "f2elint commit-msg-scan" 36 | } 37 | }, 38 | "lint-staged": { 39 | "**/*.{js,jsx,ts,tsx,vue}": "f2elint exec eslint", 40 | "**/*.{css,scss,less,acss}": "f2elint exec stylelint" 41 | }, 42 | "peerDependencies": { 43 | "react": "^16.x", 44 | "react-dom": "^16.x", 45 | "moment": "latest" 46 | }, 47 | "devDependencies": { 48 | "@alib/build-scripts": "^0.1.3", 49 | "@alifd/build-plugin-lowcode": "^0.3.0", 50 | "@alifd/theme-2": "^0.4.0", 51 | "@storybook/addon-actions": "^6.3.1", 52 | "@storybook/addon-docs": "^6.3.4", 53 | "@storybook/addon-essentials": "^6.3.0", 54 | "@storybook/addon-storysource": "^6.3.1", 55 | "@storybook/react": "^6.3.5", 56 | "@storybook/source-loader": "^6.3.1", 57 | "@types/react": "^16.14.24", 58 | "@types/react-dom": "^16.9.4", 59 | "build-plugin-component-multiple": "^1.0.0-beta.5", 60 | "build-plugin-fusion": "^0.1.0", 61 | "f2elint": "^1.2.0", 62 | "postcss": "^8.4.14", 63 | "postcss-loader": "^4.3.0", 64 | "tailwindcss": "^3.1.6", 65 | "autoprefixer": "^10.4.7" 66 | }, 67 | "dependencies": { 68 | "@alifd/next": "^1.25.27", 69 | "prop-types": "^15.5.8", 70 | "react": "^16.x", 71 | "react-dom": "^16.x" 72 | }, 73 | "acceptDependencies": { 74 | "webpack": "^4.46.x" 75 | }, 76 | "resolutions": { 77 | "webpack": "^4.46.x" 78 | }, 79 | "componentConfig": { 80 | "isComponentLibrary": true, 81 | "materialSchema": "https://unpkg.com/@dogtiti/next-pro-lowcode-materials@0.3.1/build/lowcode/assets-prod.json" 82 | }, 83 | "homepage": "https://github.com/Dogtiti/next-pro-lowcode-materials", 84 | "repository": { 85 | "type": "git", 86 | "url": "https://github.com/Dogtiti/next-pro-lowcode-materials.git" 87 | }, 88 | "bugs": { 89 | "url": "https://github.com/Dogtiti/next-pro-lowcode-materials/issues" 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /src/components/form/components/form-date-picker.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { createElement } from 'react'; 3 | import { DatePicker2 } from '@alifd/next'; 4 | import type { DatePickerProps } from '@alifd/next/types/date-picker2'; 5 | import wrapper from './next-wrapper'; 6 | 7 | export type ProFormDatePickerProps = DatePickerProps; 8 | 9 | const ProFormDatePicker = wrapper(DatePicker2, 'ProFormDatePicker'); 10 | 11 | export default ProFormDatePicker; 12 | -------------------------------------------------------------------------------- /src/components/form/components/form-input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { createElement } from 'react'; 3 | import { Input } from '@alifd/next'; 4 | import type { InputProps } from '@alifd/next/types/input'; 5 | import wrapper from './next-wrapper'; 6 | 7 | export type ProFormInputProps = InputProps; 8 | 9 | const ProFormInput = wrapper(Input, 'ProFormInput'); 10 | 11 | export default ProFormInput; 12 | -------------------------------------------------------------------------------- /src/components/form/components/form-item.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { createElement } from 'react'; 3 | import { Form } from '@alifd/next'; 4 | import type { ItemProps } from '@alifd/next/types/form'; 5 | 6 | const { Item } = Form; 7 | 8 | const ProFormItem: React.ForwardRefRenderFunction = (props, ref) => { 9 | const { children = [], ...otherProps } = props; 10 | 11 | return createElement( 12 | Item, 13 | { ...otherProps }, 14 | React.Children.map(children, (child: any) => { 15 | if (!child) { 16 | return null; 17 | } 18 | const { props: _props = {} } = child; 19 | const { defaultValue, value, ...restProps } = _props; 20 | const { __designMode } = restProps; 21 | const finalValue = __designMode === 'design' ? defaultValue : value; 22 | if (finalValue) { 23 | restProps.value = finalValue; 24 | } 25 | return React.cloneElement(child, { ..._props, ...restProps }); 26 | }), 27 | ); 28 | }; 29 | 30 | const ProFormItemRef = React.forwardRef(ProFormItem); 31 | 32 | ProFormItemRef.displayName = 'ProFormItem'; 33 | 34 | export default ProFormItemRef; 35 | -------------------------------------------------------------------------------- /src/components/form/components/form-number-input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { createElement } from 'react'; 3 | import { NumberPicker } from '@alifd/next'; 4 | import type { NumberPickerProps } from '@alifd/next/types/number-picker'; 5 | import wrapper from './next-wrapper'; 6 | 7 | export type ProFormNumberInputProps = NumberPickerProps; 8 | 9 | const ProFormNumberInput = wrapper(NumberPicker, 'ProFormNumberInput'); 10 | 11 | export default ProFormNumberInput; 12 | -------------------------------------------------------------------------------- /src/components/form/components/form-select.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { createElement } from 'react'; 3 | import { Select } from '@alifd/next'; 4 | import type { SelectProps } from '@alifd/next/types/select'; 5 | import wrapper from './next-wrapper'; 6 | 7 | export type ProFormSelectProps = SelectProps; 8 | 9 | const ProFormSelect = wrapper(Select, 'ProFormSelect'); 10 | 11 | export default ProFormSelect; 12 | -------------------------------------------------------------------------------- /src/components/form/components/next-wrapper.ts: -------------------------------------------------------------------------------- 1 | import { createElement } from 'react'; 2 | import FormItem from './form-item'; 3 | 4 | const wrapper = function wrapper(NextFormComponent, displayName) { 5 | const WrappedComponent = (props: any = {}) => { 6 | const { formItemProps = {}, ...componentProps } = props; 7 | return createElement( 8 | FormItem, 9 | formItemProps, 10 | createElement(NextFormComponent, componentProps), 11 | ); 12 | }; 13 | WrappedComponent.displayName = displayName; 14 | return WrappedComponent; 15 | }; 16 | 17 | export default wrapper; 18 | -------------------------------------------------------------------------------- /src/components/form/form.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { createElement, cloneElement } from 'react'; 3 | import { Form, ResponsiveGrid, Input } from '@alifd/next'; 4 | import type { FormProps } from '@alifd/next/types/form'; 5 | import type { ResponsiveGridProps } from '@alifd/next/types/responsive-grid'; 6 | export type ProFormProps = FormProps & 7 | ResponsiveGridProps & { spacing: number | number[]; emptyContent: string }; 8 | 9 | const ProForm: React.ForwardRefRenderFunction = (props, ref) => { 10 | const { children, columns, spacing = [0, 16, 16, 0], emptyContent, ...otherProps } = props; 11 | 12 | return ( 13 |
14 | 15 | {children ? ( 16 | formatFormItems(children, { ...otherProps }) 17 | ) : ( 18 |
{emptyContent}
19 | )} 20 |
21 |
22 | ); 23 | }; 24 | const ProFormRef = React.forwardRef(ProForm); 25 | 26 | export default ProFormRef; 27 | 28 | export const formatFormItems = (children, props: any = {}) => { 29 | const { status, labelAlign, labelCol, wrapperCol, labelTextAlign } = props; 30 | const _children = children.filter( 31 | (child) => child && ['function', 'object'].indexOf(typeof child) > -1, 32 | ); 33 | 34 | return React.Children.map(_children, (child) => { 35 | if (child && ['function', 'object'].indexOf(typeof child) > -1) { 36 | const childrenProps = { 37 | labelCol: child.props?.labelCol ?? labelCol, 38 | labelAlign: child.props?.labelAlign ?? labelAlign, 39 | wrapperCol: child.props?.wrapperCol ?? wrapperCol, 40 | labelTextAlign: child.props?.labelTextAlign ?? labelTextAlign, 41 | status, 42 | }; 43 | const { formItemProps = {} } = child.props; 44 | const formItemColumn = formItemProps.columnSpan; 45 | const columnSpan = formItemColumn ?? 1; 46 | 47 | return createElement( 48 | ResponsiveGrid.Cell, 49 | { colSpan: columnSpan }, 50 | cloneElement(child, { 51 | formItemProps: Object.assign({}, pickDefined(childrenProps), formItemProps), 52 | }), 53 | ); 54 | } 55 | 56 | return child; 57 | }); 58 | }; 59 | 60 | const pickDefined = (obj = {}) => { 61 | const newObj = {}; 62 | Object.keys(obj).forEach((i) => { 63 | if (typeof obj[i] !== 'undefined') { 64 | newObj[i] = obj[i]; 65 | } 66 | }); 67 | return newObj; 68 | }; 69 | -------------------------------------------------------------------------------- /src/components/form/index.tsx: -------------------------------------------------------------------------------- 1 | export type { ProFormProps } from './form'; 2 | export { default as ProForm } from './form'; 3 | 4 | export type { ProFormInputProps } from './components/form-input'; 5 | 6 | export { default as ProFormInput } from './components/form-input'; 7 | 8 | export type { ProFormNumberInputProps } from './components/form-number-input'; 9 | 10 | export { default as ProFormNumberInput } from './components/form-number-input'; 11 | 12 | export type { ProFormSelectProps } from './components/form-select'; 13 | 14 | export { default as ProFormSelect } from './components/form-select'; 15 | 16 | export type { ProFormDatePickerProps } from './components/form-date-picker'; 17 | 18 | export { default as ProFormDatePicker } from './components/form-date-picker'; 19 | -------------------------------------------------------------------------------- /src/components/pro-card/index.scss: -------------------------------------------------------------------------------- 1 | .title-container{ 2 | display: flex; 3 | align-items: center; 4 | .divider{ 5 | width: 3px; 6 | height: 11px; 7 | margin-right: 5px; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/components/pro-card/index.tsx: -------------------------------------------------------------------------------- 1 | import ProCard from './pro-card'; 2 | 3 | export type { ProCardProps } from './pro-card'; 4 | export default ProCard; 5 | -------------------------------------------------------------------------------- /src/components/pro-card/pro-card.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { createElement } from 'react'; 3 | import { Card, Button, Icon } from '@alifd/next'; 4 | import type { CardProps } from '@alifd/next/types/card'; 5 | import './index.scss'; 6 | 7 | const { Header, Content } = Card; 8 | 9 | export interface ProCardProps extends CardProps { 10 | color: string; 11 | } 12 | 13 | const ProCard: React.ForwardRefRenderFunction = (props) => { 14 | const [collapse, setCollapse] = React.useState(false); 15 | const { children, title, color = '#5584FF' } = props; 16 | 17 | const extra = () => { 18 | return ( 19 | 23 | ); 24 | }; 25 | 26 | const renderTitle = () => { 27 | return ( 28 |
29 |
30 |
{title}
31 |
32 | ); 33 | }; 34 | 35 | return ( 36 | 37 |
38 | 43 | {children} 44 | 45 | 46 | ); 47 | }; 48 | 49 | export default ProCard; 50 | -------------------------------------------------------------------------------- /src/components/section-form/components/child-form.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { createElement } from 'react'; 3 | import { ResponsiveGrid } from '@alifd/next'; 4 | import { formatFormItems } from '../../form/form'; 5 | import type { ResponsiveGridProps } from '@alifd/next/types/responsive-grid'; 6 | 7 | export interface ChildFormProps extends ResponsiveGridProps { 8 | spacing: number | number[]; 9 | emptyContent: string; 10 | } 11 | 12 | const ChildForm: React.ForwardRefRenderFunction = (props, ref) => { 13 | const { children, columns, spacing = [0, 16, 16, 0], emptyContent, ...otherProps } = props; 14 | return ( 15 | 16 | {children ? ( 17 | formatFormItems(children, { ...otherProps }) 18 | ) : ( 19 |
{emptyContent}
20 | )} 21 |
22 | ); 23 | }; 24 | 25 | const ChildFormRef = React.forwardRef(ChildForm); 26 | 27 | export default ChildFormRef; 28 | -------------------------------------------------------------------------------- /src/components/section-form/index.tsx: -------------------------------------------------------------------------------- 1 | export type { SectionFormProps } from './section-form'; 2 | export { default as SectionForm } from './section-form'; 3 | 4 | export type { ChildFormProps } from './components/child-form'; 5 | export { default as ChildForm } from './components/child-form'; 6 | -------------------------------------------------------------------------------- /src/components/section-form/section-form.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { createElement, cloneElement } from 'react'; 3 | import { Form, Card, Button } from '@alifd/next'; 4 | import type { FormProps } from '@alifd/next/types/form'; 5 | import type { Operations } from '../form/form'; 6 | import ProCard from '../pro-card'; 7 | 8 | export interface SectionFormProps extends FormProps { 9 | operations: Operations[]; 10 | isPreview: boolean; 11 | color: string; 12 | } 13 | 14 | const SectionForm: React.ForwardRefRenderFunction = ( 15 | props, 16 | ref, 17 | ) => { 18 | const { children, isPreview, operations = [], color, ...otherProps } = props; 19 | return ( 20 |
21 | {children ? formatChildForm(children, { color }) : null} 22 | {isPreview ? null : ( 23 |
24 | 25 | {operations.map(({ id, content, type, onClick }) => { 26 | return ( 27 | 30 | ); 31 | })} 32 | 33 |
34 | )} 35 |
36 | ); 37 | }; 38 | 39 | const SectionFormRef = React.forwardRef(SectionForm); 40 | 41 | export default SectionFormRef; 42 | 43 | const formatChildForm = (children, globalProps) => { 44 | const { color } = globalProps; 45 | return React.Children.map(children, (child) => { 46 | const { props } = child; 47 | const { cardProps = {} } = props; 48 | const { primaryKey, ...restProps } = cardProps; 49 | return ( 50 | 51 | {child} 52 | 53 | ); 54 | }); 55 | }; 56 | -------------------------------------------------------------------------------- /src/index.scss: -------------------------------------------------------------------------------- 1 | .next-form-item-control>.next-input, 2 | .next-form-item-control>.next-input-group, 3 | .next-form-item-fullwidth .next-form-item-control>.next-date-picker, 4 | .next-form-item-fullwidth .next-form-item-control>.next-date-picker2, 5 | .next-form-item-fullwidth .next-form-item-control>.next-date-picker>.next-date-picker-trigger>.next-input, 6 | .next-form-item-fullwidth .next-form-item-control>.next-input, 7 | .next-form-item-fullwidth .next-form-item-control>.next-input-group, 8 | .next-form-item-fullwidth .next-form-item-control>.next-month-picker, 9 | .next-form-item-fullwidth .next-form-item-control>.next-number-picker, 10 | .next-form-item-fullwidth .next-form-item-control>.next-range-picker, 11 | .next-form-item-fullwidth .next-form-item-control>.next-select, 12 | .next-form-item-fullwidth .next-form-item-control>.next-time-picker, 13 | .next-form-item-fullwidth .next-form-item-control>.next-year-picker { 14 | width: 100% 15 | } 16 | 17 | @tailwind base; 18 | @tailwind components; 19 | @tailwind utilities; 20 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | ProForm, 3 | ProFormInput, 4 | ProFormNumberInput, 5 | ProFormSelect, 6 | ProFormDatePicker, 7 | } from './components/form'; 8 | 9 | import { SectionForm, ChildForm } from './components/section-form'; 10 | 11 | /** 12 | * next-from 13 | */ 14 | 15 | export type { 16 | ProFormProps, 17 | ProFormInputProps, 18 | ProFormNumberInputProps, 19 | ProFormSelectProps, 20 | ProFormDatePickerProps, 21 | } from './components/form'; 22 | 23 | export { ProForm, ProFormInput, ProFormNumberInput, ProFormSelect, ProFormDatePicker }; 24 | 25 | /** 26 | * section-form 27 | */ 28 | export type { ChildFormProps, SectionFormProps } from './components/section-form'; 29 | export { SectionForm, ChildForm }; 30 | -------------------------------------------------------------------------------- /src/variables.scss: -------------------------------------------------------------------------------- 1 | @import '~@alifd/next/variables.scss'; 2 | 3 | $biz-css-prefix: '.bizpack'; 4 | -------------------------------------------------------------------------------- /src/variables.tsx: -------------------------------------------------------------------------------- 1 | const bizCssPrefix = 'bizpack'; 2 | 3 | export { bizCssPrefix }; 4 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ['./src/**/*.{ts,tsx}'], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | }; 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "ESNext", 4 | "target": "ESNext", 5 | "jsx": "react", 6 | "jsxFactory": "createElement", 7 | "declaration": false, 8 | "outDir": "./lib", 9 | "removeComments": true, 10 | "strict": false, 11 | "noImplicitAny": false, 12 | "noImplicitThis": false, 13 | "preserveConstEnums": true, 14 | "noUnusedLocals": true, 15 | "noUnusedParameters": false, 16 | "noImplicitReturns": true, 17 | "moduleResolution": "node", 18 | "baseUrl": "./", 19 | "esModuleInterop": true, 20 | "experimentalDecorators": true, 21 | "importHelpers": true 22 | }, 23 | "include": ["src"], 24 | "exclude": ["node_modules", "lib"] 25 | } 26 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["tslint-config-ali", "tslint-config-prettier"], 3 | "rules": { 4 | "no-implicit-dependencies": [true, "dev"], 5 | "quotemark": [true, "single", "jsx-double"], 6 | "no-empty": [true, "allow-empty-catch", "allow-empty-functions"], 7 | "no-console": [true, "log"], 8 | "variable-name": [ 9 | false, 10 | "check-format", 11 | "allow-leading-underscore", 12 | "allow-trailing-underscore", 13 | "allow-pascal-case" 14 | ], 15 | "only-arrow-functions": false, 16 | "object-literal-sort-keys": false, 17 | "ordered-imports": false, 18 | "interface-name": false, 19 | "no-boolean-literal-compare": false 20 | }, 21 | "linterOptions": { 22 | "exclude": ["node_modules/**"] 23 | } 24 | } 25 | --------------------------------------------------------------------------------