diff --git a/packages/element/.eslintrc b/packages/element/.eslintrc deleted file mode 100644 index ae7e4ff06d5..00000000000 --- a/packages/element/.eslintrc +++ /dev/null @@ -1,73 +0,0 @@ -{ - "parser": "vue-eslint-parser", - "extends": [ - "plugin:vue/recommended", - "plugin:@typescript-eslint/recommended", - "prettier/@typescript-eslint" - ], - "env": { - "browser": true, - "es6": true, - "jest": true, - "commonjs": true - }, - "plugins": ["@typescript-eslint", "prettier"], - "parserOptions": { - "parser": "@typescript-eslint/parser", - "project": "./tsconfig.json", - "sourceType": "module", - "ecmaVersion": 6, - "ecmaFeatures": { - "jsx": true - }, - "extraFileExtensions": [".vue"] - }, - "rules": { - "prettier/prettier": 0, - // don't force es6 functions to include space before paren - "space-before-function-paren": 0, - // maybe we should no-public - "@typescript-eslint/explicit-member-accessibility": 0, - "@typescript-eslint/interface-name-prefix": 0, - "@typescript-eslint/no-explicit-any": 0, - "@typescript-eslint/explicit-function-return-type": 0, - "@typescript-eslint/no-parameter-properties": 0, - "@typescript-eslint/array-type": 0, - "@typescript-eslint/no-object-literal-type-assertion": 0, - "@typescript-eslint/no-use-before-define": 0, - "@typescript-eslint/no-unused-vars": 2, - "@typescript-eslint/no-namespace": 0, - "@typescript-eslint/ban-types": 0, - "@typescript-eslint/explicit-module-boundary-types": 0, - "no-console": [ - "error", - { - "allow": ["warn", "error", "info"] - } - ], - "prefer-const": 1, - "no-var": 1, - "@typescript-eslint/no-empty-function": 1, - "@typescript-eslint/ban-ts-comment": [ - "error", - { - "ts-ignore": "allow-with-description", - "ts-check": true - } - ], - "prefer-rest-params": 1, - "vue/html-self-closing": 0, - "vue/max-attributes-per-line": [ - "error", - { - "singleline": 3, - "multiline": { - "max": 1, - "allowFirstLine": false - } - } - ], - "react-hooks/rules-of-hooks": 0, - "vue/one-component-per-file": 0 - } -} diff --git a/packages/element/docs/.vuepress/config.js b/packages/element/docs/.vuepress/config.js index f3a3091628e..318e5e403e1 100644 --- a/packages/element/docs/.vuepress/config.js +++ b/packages/element/docs/.vuepress/config.js @@ -1,4 +1,10 @@ const path = require('path') +const utils = require('./util') + +const componentFiles = utils + .getFiles(path.resolve(__dirname, '../guide')) + .map((item) => item.replace(/(\.md)/g, '')) + .filter((item) => !['el-form', 'el-form-item', 'index'].includes(item)) module.exports = { title: 'Formily Element', @@ -25,7 +31,7 @@ module.exports = { nav: [ { text: '指南', - link: '/guide/form', + link: '/guide/', }, { text: '主站', @@ -37,29 +43,7 @@ module.exports = { }, ], sidebar: { - '/guide/': [ - 'form', - 'form-item', - 'form-layout', - 'form-grid', - 'form-button-group', - 'array-table', - 'space', - 'reset', - 'submit', - 'input', - 'input-number', - 'password', - 'select', - 'cascader', - 'checkbox', - 'radio', - 'switch', - 'date-picker', - 'time-picker', - 'transfer', - 'upload', - ], + '/guide/': ['', ...componentFiles], }, lastUpdated: 'Last Updated', smoothScroll: true, diff --git a/packages/element/docs/.vuepress/util.js b/packages/element/docs/.vuepress/util.js new file mode 100644 index 00000000000..f5a7a35837a --- /dev/null +++ b/packages/element/docs/.vuepress/util.js @@ -0,0 +1,7 @@ +const fs = require('fs') + +module.exports = { + getFiles(dir) { + return fs.readdirSync(dir) + }, +} diff --git a/packages/element/docs/README.md b/packages/element/docs/README.md index bb7274d4196..ce6402fa990 100644 --- a/packages/element/docs/README.md +++ b/packages/element/docs/README.md @@ -3,7 +3,7 @@ home: true heroText: FORMILY ELEMENT tagline: 基于 Element UI 封装的Formily2.x组件体系 actionText: 开发指南 -actionLink: /guide/form +actionLink: /guide/ features: - title: 更易用 details: 开箱即用,案例丰富 diff --git a/packages/element/docs/demos/guide/array-cards/effects-json-schema.vue b/packages/element/docs/demos/guide/array-cards/effects-json-schema.vue new file mode 100644 index 00000000000..ad828cb4ab2 --- /dev/null +++ b/packages/element/docs/demos/guide/array-cards/effects-json-schema.vue @@ -0,0 +1,135 @@ + + + + + diff --git a/packages/element/docs/demos/guide/array-cards/effects-markup-schema.vue b/packages/element/docs/demos/guide/array-cards/effects-markup-schema.vue new file mode 100644 index 00000000000..878fb8877d6 --- /dev/null +++ b/packages/element/docs/demos/guide/array-cards/effects-markup-schema.vue @@ -0,0 +1,123 @@ + + + + + diff --git a/packages/element/docs/demos/guide/array-cards/json-schema.vue b/packages/element/docs/demos/guide/array-cards/json-schema.vue new file mode 100644 index 00000000000..22b208e5acc --- /dev/null +++ b/packages/element/docs/demos/guide/array-cards/json-schema.vue @@ -0,0 +1,156 @@ + + + + + diff --git a/packages/element/docs/demos/guide/array-cards/markup-schema.vue b/packages/element/docs/demos/guide/array-cards/markup-schema.vue new file mode 100644 index 00000000000..30a478b2cf5 --- /dev/null +++ b/packages/element/docs/demos/guide/array-cards/markup-schema.vue @@ -0,0 +1,111 @@ + + + + + diff --git a/packages/element/docs/demos/guide/array-collapse/effects-json-schema.vue b/packages/element/docs/demos/guide/array-collapse/effects-json-schema.vue new file mode 100644 index 00000000000..dd50ff661f4 --- /dev/null +++ b/packages/element/docs/demos/guide/array-collapse/effects-json-schema.vue @@ -0,0 +1,141 @@ + + + + + diff --git a/packages/element/docs/demos/guide/array-collapse/effects-markup-schema.vue b/packages/element/docs/demos/guide/array-collapse/effects-markup-schema.vue new file mode 100644 index 00000000000..84c63f0ee1b --- /dev/null +++ b/packages/element/docs/demos/guide/array-collapse/effects-markup-schema.vue @@ -0,0 +1,131 @@ + + + + + diff --git a/packages/element/docs/demos/guide/array-collapse/json-schema.vue b/packages/element/docs/demos/guide/array-collapse/json-schema.vue new file mode 100644 index 00000000000..59558da4dd3 --- /dev/null +++ b/packages/element/docs/demos/guide/array-collapse/json-schema.vue @@ -0,0 +1,208 @@ + + + + + diff --git a/packages/element/docs/demos/guide/array-collapse/markup-schema.vue b/packages/element/docs/demos/guide/array-collapse/markup-schema.vue new file mode 100644 index 00000000000..7a6c86c310d --- /dev/null +++ b/packages/element/docs/demos/guide/array-collapse/markup-schema.vue @@ -0,0 +1,177 @@ + + + + + diff --git a/packages/element/docs/demos/guide/array-items/json-schema.vue b/packages/element/docs/demos/guide/array-items/json-schema.vue new file mode 100644 index 00000000000..e897c28f968 --- /dev/null +++ b/packages/element/docs/demos/guide/array-items/json-schema.vue @@ -0,0 +1,233 @@ + + + + + diff --git a/packages/element/docs/demos/guide/array-items/markup-schema.vue b/packages/element/docs/demos/guide/array-items/markup-schema.vue new file mode 100644 index 00000000000..726e9894433 --- /dev/null +++ b/packages/element/docs/demos/guide/array-items/markup-schema.vue @@ -0,0 +1,201 @@ + + + + + diff --git a/packages/element/docs/demos/guide/array-table/effects-json-schema.vue b/packages/element/docs/demos/guide/array-table/effects-json-schema.vue new file mode 100644 index 00000000000..a984b3b3aae --- /dev/null +++ b/packages/element/docs/demos/guide/array-table/effects-json-schema.vue @@ -0,0 +1,184 @@ + + + diff --git a/packages/element/docs/demos/guide/array-table/effects-markup-schema.vue b/packages/element/docs/demos/guide/array-table/effects-markup-schema.vue new file mode 100644 index 00000000000..691bacb0e82 --- /dev/null +++ b/packages/element/docs/demos/guide/array-table/effects-markup-schema.vue @@ -0,0 +1,155 @@ + + + diff --git a/packages/element/docs/demos/guide/array-table/json-schema.vue b/packages/element/docs/demos/guide/array-table/json-schema.vue new file mode 100644 index 00000000000..d1dc7cab4f3 --- /dev/null +++ b/packages/element/docs/demos/guide/array-table/json-schema.vue @@ -0,0 +1,160 @@ + + + diff --git a/packages/element/docs/demos/guide/array-table.vue b/packages/element/docs/demos/guide/array-table/markup-schema.vue similarity index 67% rename from packages/element/docs/demos/guide/array-table.vue rename to packages/element/docs/demos/guide/array-table/markup-schema.vue index 03db574e095..55ae8908eac 100644 --- a/packages/element/docs/demos/guide/array-table.vue +++ b/packages/element/docs/demos/guide/array-table/markup-schema.vue @@ -9,20 +9,21 @@ + :x-component-props="{ width: 80, title: 'Index' }" + > + - - - + + + - + 提交 @@ -100,11 +79,14 @@ import { FormItem, ArrayTable, ArrayTableColumn, - ArrayAddition, - ArrayMoveDown, - ArrayMoveUp, - ArrayRemove, + ArrayTableAddition, + ArrayTableMoveDown, + ArrayTableMoveUp, + ArrayTableRemove, + ArrayTableIndex, + ArrayTableSortHandle, Input, + Editable, } from '@formily/element' const fields = createSchemaField({ @@ -112,11 +94,14 @@ const fields = createSchemaField({ FormItem, ArrayTable, ArrayTableColumn, - ArrayAddition, - ArrayMoveDown, - ArrayMoveUp, - ArrayRemove, + ArrayTableAddition, + ArrayTableMoveDown, + ArrayTableMoveUp, + ArrayTableRemove, + ArrayTableIndex, + ArrayTableSortHandle, Input, + Editable, }, }) diff --git a/packages/element/docs/demos/guide/array-tabs/json-schema.vue b/packages/element/docs/demos/guide/array-tabs/json-schema.vue new file mode 100644 index 00000000000..f9d23e4535e --- /dev/null +++ b/packages/element/docs/demos/guide/array-tabs/json-schema.vue @@ -0,0 +1,99 @@ + + + + + diff --git a/packages/element/docs/demos/guide/array-tabs/markup-schema.vue b/packages/element/docs/demos/guide/array-tabs/markup-schema.vue new file mode 100644 index 00000000000..6924fd18f1c --- /dev/null +++ b/packages/element/docs/demos/guide/array-tabs/markup-schema.vue @@ -0,0 +1,94 @@ + + + + + diff --git a/packages/element/docs/demos/guide/cascader/base.vue b/packages/element/docs/demos/guide/cascader/json-schema.vue similarity index 94% rename from packages/element/docs/demos/guide/cascader/base.vue rename to packages/element/docs/demos/guide/cascader/json-schema.vue index 1ea72f3bc5c..04d56ac40a8 100644 --- a/packages/element/docs/demos/guide/cascader/base.vue +++ b/packages/element/docs/demos/guide/cascader/json-schema.vue @@ -57,9 +57,14 @@ const schema = { properties: { cascader: { type: 'string', - title: '复选', + title: '地址选择', 'x-decorator': 'FormItem', 'x-component': 'Cascader', + 'x-component-props': { + style: { + width: '240px', + }, + }, 'x-reactions': [ '{{useAsyncDataSource("//unpkg.com/china-location/dist/location.json",transformAddress)}}', ], diff --git a/packages/element/docs/demos/guide/cascader/markup-schema.vue b/packages/element/docs/demos/guide/cascader/markup-schema.vue new file mode 100644 index 00000000000..364a55ebb0c --- /dev/null +++ b/packages/element/docs/demos/guide/cascader/markup-schema.vue @@ -0,0 +1,91 @@ + + + +l diff --git a/packages/element/docs/demos/guide/cascader/template.vue b/packages/element/docs/demos/guide/cascader/template.vue new file mode 100644 index 00000000000..13b64e54762 --- /dev/null +++ b/packages/element/docs/demos/guide/cascader/template.vue @@ -0,0 +1,86 @@ + + + diff --git a/packages/element/docs/demos/guide/checkbox/base.vue b/packages/element/docs/demos/guide/checkbox/json-schema.vue similarity index 100% rename from packages/element/docs/demos/guide/checkbox/base.vue rename to packages/element/docs/demos/guide/checkbox/json-schema.vue diff --git a/packages/element/docs/demos/guide/checkbox/markup-schema.vue b/packages/element/docs/demos/guide/checkbox/markup-schema.vue new file mode 100644 index 00000000000..2c27f4075e4 --- /dev/null +++ b/packages/element/docs/demos/guide/checkbox/markup-schema.vue @@ -0,0 +1,59 @@ + + + +l diff --git a/packages/element/docs/demos/guide/checkbox/template.vue b/packages/element/docs/demos/guide/checkbox/template.vue new file mode 100644 index 00000000000..821a4732bde --- /dev/null +++ b/packages/element/docs/demos/guide/checkbox/template.vue @@ -0,0 +1,52 @@ + + + diff --git a/packages/element/docs/demos/guide/date-picker/base.vue b/packages/element/docs/demos/guide/date-picker/json-schema.vue similarity index 98% rename from packages/element/docs/demos/guide/date-picker/base.vue rename to packages/element/docs/demos/guide/date-picker/json-schema.vue index 2a6b0dca35e..129bbfd5a2a 100644 --- a/packages/element/docs/demos/guide/date-picker/base.vue +++ b/packages/element/docs/demos/guide/date-picker/json-schema.vue @@ -46,25 +46,25 @@ const schema = { type: 'year', }, }, - dates: { - type: 'array', - title: '多个日期', + dateTime: { + type: 'string', + title: '日期时间', 'x-decorator': 'FormItem', 'x-component': 'DatePicker', 'x-component-props': { - type: 'dates', + type: 'datetime', }, }, - dateTime: { - type: 'string', - title: '日期时间', + dates: { + type: 'array', + title: '多个日期', 'x-decorator': 'FormItem', 'x-component': 'DatePicker', 'x-component-props': { - type: 'dateTime', + type: 'dates', }, }, - daterange: { + dateRange: { type: 'string', title: '日期范围', 'x-decorator': 'FormItem', diff --git a/packages/element/docs/demos/guide/date-picker/markup-schema.vue b/packages/element/docs/demos/guide/date-picker/markup-schema.vue new file mode 100644 index 00000000000..3df0aa6fbbc --- /dev/null +++ b/packages/element/docs/demos/guide/date-picker/markup-schema.vue @@ -0,0 +1,114 @@ + + + +l diff --git a/packages/element/docs/demos/guide/date-picker/template.vue b/packages/element/docs/demos/guide/date-picker/template.vue new file mode 100644 index 00000000000..fd8ea73ff5c --- /dev/null +++ b/packages/element/docs/demos/guide/date-picker/template.vue @@ -0,0 +1,124 @@ + + + +l diff --git a/packages/element/docs/demos/guide/editable/json-schema.vue b/packages/element/docs/demos/guide/editable/json-schema.vue new file mode 100644 index 00000000000..e811e3d92b5 --- /dev/null +++ b/packages/element/docs/demos/guide/editable/json-schema.vue @@ -0,0 +1,117 @@ + + + + + diff --git a/packages/element/docs/demos/guide/editable/markup-schema.vue b/packages/element/docs/demos/guide/editable/markup-schema.vue new file mode 100644 index 00000000000..3481156b9fd --- /dev/null +++ b/packages/element/docs/demos/guide/editable/markup-schema.vue @@ -0,0 +1,117 @@ + + + + + diff --git a/packages/element/docs/demos/guide/editable/template.vue b/packages/element/docs/demos/guide/editable/template.vue new file mode 100644 index 00000000000..7843c0d9a28 --- /dev/null +++ b/packages/element/docs/demos/guide/editable/template.vue @@ -0,0 +1,111 @@ + + + + + diff --git a/packages/element/docs/demos/guide/form-grid/json-schema.vue b/packages/element/docs/demos/guide/form-grid/json-schema.vue new file mode 100644 index 00000000000..463a77a83ef --- /dev/null +++ b/packages/element/docs/demos/guide/form-grid/json-schema.vue @@ -0,0 +1,94 @@ + + + +l diff --git a/packages/element/docs/demos/guide/form-grid/markup-schema.vue b/packages/element/docs/demos/guide/form-grid/markup-schema.vue new file mode 100644 index 00000000000..47d8e4ce881 --- /dev/null +++ b/packages/element/docs/demos/guide/form-grid/markup-schema.vue @@ -0,0 +1,87 @@ + + + diff --git a/packages/element/docs/demos/guide/form-grid.vue b/packages/element/docs/demos/guide/form-grid/native.vue similarity index 100% rename from packages/element/docs/demos/guide/form-grid.vue rename to packages/element/docs/demos/guide/form-grid/native.vue diff --git a/packages/element/docs/demos/guide/form-item.vue b/packages/element/docs/demos/guide/form-item/common.vue similarity index 100% rename from packages/element/docs/demos/guide/form-item.vue rename to packages/element/docs/demos/guide/form-item/common.vue diff --git a/packages/element/docs/demos/guide/form-item/feedback.vue b/packages/element/docs/demos/guide/form-item/feedback.vue new file mode 100644 index 00000000000..653a6fb96b6 --- /dev/null +++ b/packages/element/docs/demos/guide/form-item/feedback.vue @@ -0,0 +1,258 @@ + + + diff --git a/packages/element/docs/demos/guide/input-number/base.vue b/packages/element/docs/demos/guide/form-item/json-schema.vue similarity index 80% rename from packages/element/docs/demos/guide/input-number/base.vue rename to packages/element/docs/demos/guide/form-item/json-schema.vue index c09cb067b27..c68efcdd103 100644 --- a/packages/element/docs/demos/guide/input-number/base.vue +++ b/packages/element/docs/demos/guide/form-item/json-schema.vue @@ -8,16 +8,16 @@ diff --git a/packages/element/docs/demos/guide/form-item/template.vue b/packages/element/docs/demos/guide/form-item/template.vue new file mode 100644 index 00000000000..30fdfc22b5d --- /dev/null +++ b/packages/element/docs/demos/guide/form-item/template.vue @@ -0,0 +1,36 @@ + + + diff --git a/packages/element/docs/demos/guide/form-layout/json-schema.vue b/packages/element/docs/demos/guide/form-layout/json-schema.vue new file mode 100644 index 00000000000..4946cc96e33 --- /dev/null +++ b/packages/element/docs/demos/guide/form-layout/json-schema.vue @@ -0,0 +1,69 @@ + + + diff --git a/packages/element/docs/demos/guide/form-layout.vue b/packages/element/docs/demos/guide/form-layout/markup-schema.vue similarity index 93% rename from packages/element/docs/demos/guide/form-layout.vue rename to packages/element/docs/demos/guide/form-layout/markup-schema.vue index 64165ab7988..2acce232da5 100644 --- a/packages/element/docs/demos/guide/form-layout.vue +++ b/packages/element/docs/demos/guide/form-layout/markup-schema.vue @@ -12,6 +12,9 @@ name="input" title="输入框" x-decorator="FormItem" + :x-decorator-props="{ + tooltip: '123', + }" x-component="Input" :required="true" /> diff --git a/packages/element/docs/demos/guide/form-layout/template.vue b/packages/element/docs/demos/guide/form-layout/template.vue new file mode 100644 index 00000000000..44cd7c2f2c8 --- /dev/null +++ b/packages/element/docs/demos/guide/form-layout/template.vue @@ -0,0 +1,45 @@ + + + diff --git a/packages/element/docs/demos/guide/form-step/json-schema.vue b/packages/element/docs/demos/guide/form-step/json-schema.vue new file mode 100644 index 00000000000..f0352f9587a --- /dev/null +++ b/packages/element/docs/demos/guide/form-step/json-schema.vue @@ -0,0 +1,147 @@ + + + + + diff --git a/packages/element/docs/demos/guide/form-step/markup-schema.vue b/packages/element/docs/demos/guide/form-step/markup-schema.vue new file mode 100644 index 00000000000..568d811cbc5 --- /dev/null +++ b/packages/element/docs/demos/guide/form-step/markup-schema.vue @@ -0,0 +1,123 @@ + + + + + diff --git a/packages/element/docs/demos/guide/form-tab/json-schema.vue b/packages/element/docs/demos/guide/form-tab/json-schema.vue new file mode 100644 index 00000000000..11d001b08d4 --- /dev/null +++ b/packages/element/docs/demos/guide/form-tab/json-schema.vue @@ -0,0 +1,143 @@ + + + + + diff --git a/packages/element/docs/demos/guide/form-tab/markup-schema.vue b/packages/element/docs/demos/guide/form-tab/markup-schema.vue new file mode 100644 index 00000000000..d2d03590110 --- /dev/null +++ b/packages/element/docs/demos/guide/form-tab/markup-schema.vue @@ -0,0 +1,126 @@ + + + + + diff --git a/packages/element/docs/demos/guide/input-number/json-schema.vue b/packages/element/docs/demos/guide/input-number/json-schema.vue new file mode 100644 index 00000000000..3e6ebd2789b --- /dev/null +++ b/packages/element/docs/demos/guide/input-number/json-schema.vue @@ -0,0 +1,52 @@ + + + diff --git a/packages/element/docs/demos/guide/input-number/markup-schema.vue b/packages/element/docs/demos/guide/input-number/markup-schema.vue new file mode 100644 index 00000000000..1bcb51396f1 --- /dev/null +++ b/packages/element/docs/demos/guide/input-number/markup-schema.vue @@ -0,0 +1,46 @@ + + + diff --git a/packages/element/docs/demos/guide/input-number/template.vue b/packages/element/docs/demos/guide/input-number/template.vue new file mode 100644 index 00000000000..5601f260161 --- /dev/null +++ b/packages/element/docs/demos/guide/input-number/template.vue @@ -0,0 +1,42 @@ + + + diff --git a/packages/element/docs/demos/guide/input/base.vue b/packages/element/docs/demos/guide/input/json-schema.vue similarity index 88% rename from packages/element/docs/demos/guide/input/base.vue rename to packages/element/docs/demos/guide/input/json-schema.vue index 8d6461b5c7f..b566553f6d0 100644 --- a/packages/element/docs/demos/guide/input/base.vue +++ b/packages/element/docs/demos/guide/input/json-schema.vue @@ -8,7 +8,7 @@ diff --git a/packages/element/docs/demos/guide/input/template.vue b/packages/element/docs/demos/guide/input/template.vue new file mode 100644 index 00000000000..4d25984eee2 --- /dev/null +++ b/packages/element/docs/demos/guide/input/template.vue @@ -0,0 +1,42 @@ + + + diff --git a/packages/element/docs/demos/guide/password/base.vue b/packages/element/docs/demos/guide/password/json-schema.vue similarity index 100% rename from packages/element/docs/demos/guide/password/base.vue rename to packages/element/docs/demos/guide/password/json-schema.vue diff --git a/packages/element/docs/demos/guide/password/markup-schema.vue b/packages/element/docs/demos/guide/password/markup-schema.vue new file mode 100644 index 00000000000..d2f37060e21 --- /dev/null +++ b/packages/element/docs/demos/guide/password/markup-schema.vue @@ -0,0 +1,41 @@ + + + diff --git a/packages/element/docs/demos/guide/password/template.vue b/packages/element/docs/demos/guide/password/template.vue new file mode 100644 index 00000000000..64a163f938e --- /dev/null +++ b/packages/element/docs/demos/guide/password/template.vue @@ -0,0 +1,35 @@ + + + diff --git a/packages/element/docs/demos/guide/preview-text/base.vue b/packages/element/docs/demos/guide/preview-text/base.vue new file mode 100644 index 00000000000..8300450a063 --- /dev/null +++ b/packages/element/docs/demos/guide/preview-text/base.vue @@ -0,0 +1,96 @@ + + + diff --git a/packages/element/docs/demos/guide/preview-text/extend.vue b/packages/element/docs/demos/guide/preview-text/extend.vue new file mode 100644 index 00000000000..baf01e8f3c7 --- /dev/null +++ b/packages/element/docs/demos/guide/preview-text/extend.vue @@ -0,0 +1,111 @@ + + + diff --git a/packages/element/docs/demos/guide/radio/json-schema.vue b/packages/element/docs/demos/guide/radio/json-schema.vue new file mode 100644 index 00000000000..c4c9e226894 --- /dev/null +++ b/packages/element/docs/demos/guide/radio/json-schema.vue @@ -0,0 +1,59 @@ + + + diff --git a/packages/element/docs/demos/guide/radio/markup-schema.vue b/packages/element/docs/demos/guide/radio/markup-schema.vue new file mode 100644 index 00000000000..e6912276b36 --- /dev/null +++ b/packages/element/docs/demos/guide/radio/markup-schema.vue @@ -0,0 +1,51 @@ + + + diff --git a/packages/element/docs/demos/guide/radio/template.vue b/packages/element/docs/demos/guide/radio/template.vue new file mode 100644 index 00000000000..8b6109c240d --- /dev/null +++ b/packages/element/docs/demos/guide/radio/template.vue @@ -0,0 +1,45 @@ + + + diff --git a/packages/element/docs/demos/guide/select.vue b/packages/element/docs/demos/guide/select/json-schema-async.vue similarity index 96% rename from packages/element/docs/demos/guide/select.vue rename to packages/element/docs/demos/guide/select/json-schema-async.vue index ceca416484a..f01510b6eb4 100644 --- a/packages/element/docs/demos/guide/select.vue +++ b/packages/element/docs/demos/guide/select/json-schema-async.vue @@ -1,8 +1,7 @@ diff --git a/packages/element/docs/demos/guide/radio/base.vue b/packages/element/docs/demos/guide/select/json-schema-sync.vue similarity index 72% rename from packages/element/docs/demos/guide/radio/base.vue rename to packages/element/docs/demos/guide/select/json-schema-sync.vue index 98c271bb63b..bdfd5a72343 100644 --- a/packages/element/docs/demos/guide/radio/base.vue +++ b/packages/element/docs/demos/guide/select/json-schema-sync.vue @@ -8,14 +8,14 @@ diff --git a/packages/element/docs/demos/guide/select/markup-schema-async.vue b/packages/element/docs/demos/guide/select/markup-schema-async.vue new file mode 100644 index 00000000000..9058e245028 --- /dev/null +++ b/packages/element/docs/demos/guide/select/markup-schema-async.vue @@ -0,0 +1,108 @@ + + + diff --git a/packages/element/docs/demos/guide/select/markup-schema-sync.vue b/packages/element/docs/demos/guide/select/markup-schema-sync.vue new file mode 100644 index 00000000000..0a0919306cd --- /dev/null +++ b/packages/element/docs/demos/guide/select/markup-schema-sync.vue @@ -0,0 +1,56 @@ + + + diff --git a/packages/element/docs/demos/guide/select/template-async.vue b/packages/element/docs/demos/guide/select/template-async.vue new file mode 100644 index 00000000000..ca3db2d4bbf --- /dev/null +++ b/packages/element/docs/demos/guide/select/template-async.vue @@ -0,0 +1,106 @@ + + + diff --git a/packages/element/docs/demos/guide/select/template-sync.vue b/packages/element/docs/demos/guide/select/template-sync.vue new file mode 100644 index 00000000000..8c99bbcfc93 --- /dev/null +++ b/packages/element/docs/demos/guide/select/template-sync.vue @@ -0,0 +1,52 @@ + + + diff --git a/packages/element/docs/demos/guide/space.vue b/packages/element/docs/demos/guide/space.vue deleted file mode 100644 index 8c70c5eb2a7..00000000000 --- a/packages/element/docs/demos/guide/space.vue +++ /dev/null @@ -1,106 +0,0 @@ - - - diff --git a/packages/element/docs/demos/guide/space/json-schema.vue b/packages/element/docs/demos/guide/space/json-schema.vue new file mode 100644 index 00000000000..ebaf0b3da62 --- /dev/null +++ b/packages/element/docs/demos/guide/space/json-schema.vue @@ -0,0 +1,139 @@ + + + diff --git a/packages/element/docs/demos/guide/space/markup-schema.vue b/packages/element/docs/demos/guide/space/markup-schema.vue new file mode 100644 index 00000000000..3de83cbc625 --- /dev/null +++ b/packages/element/docs/demos/guide/space/markup-schema.vue @@ -0,0 +1,124 @@ + + + diff --git a/packages/element/docs/demos/guide/space/template.vue b/packages/element/docs/demos/guide/space/template.vue new file mode 100644 index 00000000000..9b1258161f7 --- /dev/null +++ b/packages/element/docs/demos/guide/space/template.vue @@ -0,0 +1,135 @@ + + + diff --git a/packages/element/docs/demos/guide/submit/base.vue b/packages/element/docs/demos/guide/submit/base.vue index 8fdc3753c90..f91bbd86e6b 100644 --- a/packages/element/docs/demos/guide/submit/base.vue +++ b/packages/element/docs/demos/guide/submit/base.vue @@ -17,7 +17,7 @@ /> - 提交 + 提交 diff --git a/packages/element/docs/demos/guide/switch.vue b/packages/element/docs/demos/guide/switch/json-schema.vue similarity index 97% rename from packages/element/docs/demos/guide/switch.vue rename to packages/element/docs/demos/guide/switch/json-schema.vue index a2e36b9e640..608e3c4908c 100644 --- a/packages/element/docs/demos/guide/switch.vue +++ b/packages/element/docs/demos/guide/switch/json-schema.vue @@ -14,7 +14,7 @@ const schema = { type: 'object', properties: { switch: { - type: 'number', + type: 'boolean', title: '开关', 'x-decorator': 'FormItem', 'x-component': 'Switch', diff --git a/packages/element/docs/demos/guide/switch/markup-schema.vue b/packages/element/docs/demos/guide/switch/markup-schema.vue new file mode 100644 index 00000000000..02f533a8e6d --- /dev/null +++ b/packages/element/docs/demos/guide/switch/markup-schema.vue @@ -0,0 +1,41 @@ + + + diff --git a/packages/element/docs/demos/guide/switch/template.vue b/packages/element/docs/demos/guide/switch/template.vue new file mode 100644 index 00000000000..f8786c5eb0b --- /dev/null +++ b/packages/element/docs/demos/guide/switch/template.vue @@ -0,0 +1,35 @@ + + + diff --git a/packages/element/docs/demos/guide/time-picker.vue b/packages/element/docs/demos/guide/time-picker/json-schema.vue similarity index 88% rename from packages/element/docs/demos/guide/time-picker.vue rename to packages/element/docs/demos/guide/time-picker/json-schema.vue index be109bebe88..903e07afcfd 100644 --- a/packages/element/docs/demos/guide/time-picker.vue +++ b/packages/element/docs/demos/guide/time-picker/json-schema.vue @@ -18,6 +18,11 @@ const schema = { title: '时间', 'x-decorator': 'FormItem', 'x-component': 'TimePicker', + 'x-component-props': { + style: { + width: '240px', + }, + }, }, '[startTime,endTime]': { title: '时间范围', @@ -25,6 +30,9 @@ const schema = { 'x-component': 'TimePicker', 'x-component-props': { isRange: true, + style: { + width: '240px', + }, }, type: 'string', }, diff --git a/packages/element/docs/demos/guide/time-picker/markup-schema.vue b/packages/element/docs/demos/guide/time-picker/markup-schema.vue new file mode 100644 index 00000000000..7cd5d3c43b2 --- /dev/null +++ b/packages/element/docs/demos/guide/time-picker/markup-schema.vue @@ -0,0 +1,34 @@ + + + diff --git a/packages/element/docs/demos/guide/time-picker/template.vue b/packages/element/docs/demos/guide/time-picker/template.vue new file mode 100644 index 00000000000..723b5eb5dd4 --- /dev/null +++ b/packages/element/docs/demos/guide/time-picker/template.vue @@ -0,0 +1,57 @@ + + + diff --git a/packages/element/docs/demos/guide/transfer.vue b/packages/element/docs/demos/guide/transfer/json-schema.vue similarity index 92% rename from packages/element/docs/demos/guide/transfer.vue rename to packages/element/docs/demos/guide/transfer/json-schema.vue index 53144227aef..b9a0de3cd62 100644 --- a/packages/element/docs/demos/guide/transfer.vue +++ b/packages/element/docs/demos/guide/transfer/json-schema.vue @@ -17,8 +17,8 @@ const schema = { type: 'array', title: '穿梭框', enum: [ - { title: '选项1', key: 1 }, - { title: '选项2', key: 2 }, + { label: '选项1', key: 1 }, + { label: '选项2', key: 2 }, ], 'x-decorator': 'FormItem', 'x-component': 'Transfer', diff --git a/packages/element/docs/demos/guide/transfer/markup-schema.vue b/packages/element/docs/demos/guide/transfer/markup-schema.vue new file mode 100644 index 00000000000..9530770d395 --- /dev/null +++ b/packages/element/docs/demos/guide/transfer/markup-schema.vue @@ -0,0 +1,51 @@ + + + diff --git a/packages/element/docs/demos/guide/transfer/template.vue b/packages/element/docs/demos/guide/transfer/template.vue new file mode 100644 index 00000000000..1b63e446455 --- /dev/null +++ b/packages/element/docs/demos/guide/transfer/template.vue @@ -0,0 +1,45 @@ + + + diff --git a/packages/element/docs/demos/guide/upload.vue b/packages/element/docs/demos/guide/upload/json-schema.vue similarity index 98% rename from packages/element/docs/demos/guide/upload.vue rename to packages/element/docs/demos/guide/upload/json-schema.vue index 6ca8dd874b3..52ea574ccb7 100644 --- a/packages/element/docs/demos/guide/upload.vue +++ b/packages/element/docs/demos/guide/upload/json-schema.vue @@ -10,7 +10,6 @@ diff --git a/packages/element/docs/demos/guide/upload/template.vue b/packages/element/docs/demos/guide/upload/template.vue new file mode 100644 index 00000000000..b8b6b023ee4 --- /dev/null +++ b/packages/element/docs/demos/guide/upload/template.vue @@ -0,0 +1,98 @@ + + + diff --git a/packages/element/docs/guide/array-cards.md b/packages/element/docs/guide/array-cards.md new file mode 100644 index 00000000000..5f465a32243 --- /dev/null +++ b/packages/element/docs/guide/array-cards.md @@ -0,0 +1,91 @@ +# ArrayCards + +> 卡片列表,对于每行字段数量较多,联动较多的场景比较适合使用 ArrayCards +> +> 注意:该组件只适用于 Schema 场景 + +## Markup Schema 案例 + + + +## JSON Schema 案例 + + + +## Effects 联动案例 + + + +## JSON Schema 联动案例 + + + +## API + +### ArrayCards + +> 表格组件 + +参考 [https://element.eleme.io/#/zh-CN/component/card](https://element.eleme.io/#/zh-CN/component/card) + +### ArrayCardsAddition + +> 添加按钮 + +扩展属性 + +| 属性名 | 类型 | 描述 | 默认值 | +| ------------ | ------- | ---------- | -------- | -------- | +| title | string | 文案 | | +| method | `'push' | 'unshift'` | 添加方式 | `'push'` | +| defaultValue | any | 默认值 | | + +其余参考 [https://element.eleme.io/#/zh-CN/component/button](https://element.eleme.io/#/zh-CN/component/button) + +注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的 + +### ArrayCardsRemove + +> 删除按钮 + +| 属性名 | 类型 | 描述 | 默认值 | +| ------ | ------ | ---- | ------ | +| title | string | 文案 | | + +其余参考 [https://element.eleme.io/#/zh-CN/component/button](https://element.eleme.io/#/zh-CN/component/button) + +注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的 + +### ArrayCardsMoveDown + +> 下移按钮 + +| 属性名 | 类型 | 描述 | 默认值 | +| ------ | ------ | ---- | ------ | +| title | string | 文案 | | + +其余参考 [https://element.eleme.io/#/zh-CN/component/button](https://element.eleme.io/#/zh-CN/component/button) + +注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的 + +### ArrayCardsMoveUp + +> 上移按钮 + +| 属性名 | 类型 | 描述 | 默认值 | +| ------ | ------ | ---- | ------ | +| title | string | 文案 | | + +其余参考 [https://element.eleme.io/#/zh-CN/component/button](https://element.eleme.io/#/zh-CN/component/button) + +注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的 + +### ArrayCardsIndex + +> 索引渲染器 + +无属性 + +### useArrayCardsIndex + +> 读取当前渲染行索引的 Hook diff --git a/packages/element/docs/guide/array-collapse.md b/packages/element/docs/guide/array-collapse.md new file mode 100644 index 00000000000..8bba253b1c8 --- /dev/null +++ b/packages/element/docs/guide/array-collapse.md @@ -0,0 +1,93 @@ +# ArrayCollapse + +> 折叠面板,对于每行字段数量较多,联动较多的场景比较适合使用 ArrayCollapse +> +> 注意:该组件只适用于 Schema 场景 + +## Markup Schema 案例 + + + +## JSON Schema 案例 + + + +## Effects 联动案例 + + + +## JSON Schema 联动案例 + + + +## API + +### ArrayCollapse + +参考 [https://element.eleme.io/#/zh-CN/component/collapse](https://element.eleme.io/#/zh-CN/component/collapse) + +### ArrayCollapseItem + +参考 [https://element.eleme.io/#/zh-CN/component/collapse](https://element.eleme.io/#/zh-CN/component/collapse) + +### ArrayCollapseAddition + +> 添加按钮 + +扩展属性 + +| 属性名 | 类型 | 描述 | 默认值 | +| ------------ | ------- | ---------- | -------- | -------- | +| title | string | 文案 | | +| method | `'push' | 'unshift'` | 添加方式 | `'push'` | +| defaultValue | any | 默认值 | | + +其余参考 [https://element.eleme.io/#/zh-CN/component/button](https://element.eleme.io/#/zh-CN/component/button) + +注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的 + +### ArrayCollapseRemove + +> 删除按钮 + +| 属性名 | 类型 | 描述 | 默认值 | +| ------ | ------ | ---- | ------ | +| title | string | 文案 | | + +其余参考 [https://element.eleme.io/#/zh-CN/component/button](https://element.eleme.io/#/zh-CN/component/button) + +注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的 + +### ArrayCollapseMoveDown + +> 下移按钮 + +| 属性名 | 类型 | 描述 | 默认值 | +| ------ | ------ | ---- | ------ | +| title | string | 文案 | | + +其余参考 [https://element.eleme.io/#/zh-CN/component/button](https://element.eleme.io/#/zh-CN/component/button) + +注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的 + +### ArrayCollapseMoveUp + +> 上移按钮 + +| 属性名 | 类型 | 描述 | 默认值 | +| ------ | ------ | ---- | ------ | +| title | string | 文案 | | + +其余参考 [https://element.eleme.io/#/zh-CN/component/button](https://element.eleme.io/#/zh-CN/component/button) + +注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的 + +### ArrayCollapseIndex + +> 索引渲染器 + +无属性 + +### useArrayCollapseIndex + +> 读取当前渲染行索引的 Hook diff --git a/packages/element/docs/guide/array-items.md b/packages/element/docs/guide/array-items.md new file mode 100644 index 00000000000..e752836399d --- /dev/null +++ b/packages/element/docs/guide/array-items.md @@ -0,0 +1,93 @@ +# ArrayItems + +> 自增列表,对于简单的自增编辑场景比较适合,或者对于空间要求高的场景比较适合 +> +> 注意:该组件只适用于 Schema 场景 + +## Markup Schema 案例 + + + +## JSON Schema 案例 + + + +## API + +### ArrayItems + +继承 HTMLDivElement Props + +### ArrayItemsItem + +> 列表区块 + +继承 HTMLDivElement Props + +### ArrayItemsSortHandle + +> 拖拽手柄 + +参考 [https://element.eleme.io/#/zh-CN/component/button](https://element.eleme.io/#/zh-CN/component/button) + +### ArrayItemsAddition + +> 添加按钮 + +扩展属性 + +| 属性名 | 类型 | 描述 | 默认值 | +| ------ | ---- | ---- | ------ || +| title | string | 文案 | | +| method | `'push' | 'unshift'` | 添加方式 | `'push'` | +| defaultValue | any | 默认值 | | + +其余参考 [https://element.eleme.io/#/zh-CN/component/button](https://element.eleme.io/#/zh-CN/component/button) + +注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的 + +### ArrayItemsRemove + +> 删除按钮 + +| 属性名 | 类型 | 描述 | 默认值 | +| ------ | ------ | ---- | ------ | +| title | string | 文案 | | + +其余参考 [https://element.eleme.io/#/zh-CN/component/button](https://element.eleme.io/#/zh-CN/component/button) + +注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的 + +### ArrayItemsMoveDown + +> 下移按钮 + +| 属性名 | 类型 | 描述 | 默认值 | +| ------ | ------ | ---- | ------ | +| title | string | 文案 | | + +其余参考 [https://element.eleme.io/#/zh-CN/component/button](https://element.eleme.io/#/zh-CN/component/button) + +注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的 + +### ArrayItemsMoveUp + +> 上移按钮 + +| 属性名 | 类型 | 描述 | 默认值 | +| ------ | ------ | ---- | ------ | +| title | string | 文案 | | + +其余参考 [https://element.eleme.io/#/zh-CN/component/button](https://element.eleme.io/#/zh-CN/component/button) + +注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的 + +### ArrayItemsIndex + +> 索引渲染器 + +无属性 + +### useArrayItemsIndex + +> 读取当前渲染行索引的 Hook diff --git a/packages/element/docs/guide/array-table.md b/packages/element/docs/guide/array-table.md index a618f2373c6..a7694da3464 100644 --- a/packages/element/docs/guide/array-table.md +++ b/packages/element/docs/guide/array-table.md @@ -4,9 +4,21 @@ > > 注意:该组件只适用于 Schema 场景,且只能是对象数组 -## 使用案例 +## Markup Schema 案例 - + + +## JSON Schema 案例 + + + +## Effects 联动案例 + + + +## JSON Schema 联动案例 + + ## API @@ -30,22 +42,22 @@ > ArrayTableColumn 会自动检查内部的 FormItem 是否必填,并自动在表头加上红色星号。如果不希望显示,可通过 `asterisk` 属性进行覆盖。 -### ArrayAddition +### ArrayTableAddition > 添加按钮 扩展属性 -| 属性名 | 类型 | 描述 | 默认值 | -| ------ | ------- | ---------- | -------- | -------- | -| title | string | 文案 | | +| 属性名 | 类型 | 描述 | 默认值 | +| ------ | ---- | ---- | ------ || +| title | string | 文案 | | | method | `'push' | 'unshift'` | 添加方式 | `'push'` | 其余参考 [https://element.eleme.io/#/zh-CN/component/button](https://element.eleme.io/#/zh-CN/component/button) 注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的 -### ArrayRemove +### ArrayTableRemove > 删除按钮 @@ -57,7 +69,7 @@ 注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的 -### ArrayMoveDown +### ArrayTableMoveDown > 下移按钮 @@ -69,7 +81,7 @@ 注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的 -### ArrayMoveUp +### ArrayTableMoveUp > 上移按钮 @@ -80,3 +92,13 @@ 其余参考 [https://element.eleme.io/#/zh-CN/component/button](https://element.eleme.io/#/zh-CN/component/button) 注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的 + +### ArrayTableIndex + +> 索引渲染器 + +无属性 + +### useArrayTableIndex + +> 读取当前渲染行索引的 Hook diff --git a/packages/element/docs/guide/array-tabs.md b/packages/element/docs/guide/array-tabs.md new file mode 100644 index 00000000000..383c84e65b1 --- /dev/null +++ b/packages/element/docs/guide/array-tabs.md @@ -0,0 +1,19 @@ +# ArrayTabs + +> 自增选项卡,对于纵向空间要求较高的场景可以考虑使用该组件 +> +> 注意:该组件只适用于 Schema 场景 + +## Markup Schema 案例 + + + +## JSON Schema 案例 + + + +## API + +### ArrayTabs + +参考 [https://element.eleme.io/#/zh-CN/component/tab](https://element.eleme.io/#/zh-CN/component/tab) diff --git a/packages/element/docs/guide/cascader.md b/packages/element/docs/guide/cascader.md index 251d1fa6a2d..b30f515c6db 100644 --- a/packages/element/docs/guide/cascader.md +++ b/packages/element/docs/guide/cascader.md @@ -2,9 +2,17 @@ > 级联选择器 -## 使用案例 +## Markup Schema 案例 - + + +## JSON Schema 案例 + + + +## Template 案例 + + ## API diff --git a/packages/element/docs/guide/checkbox.md b/packages/element/docs/guide/checkbox.md index 5661cd6aca9..435e263a8f1 100644 --- a/packages/element/docs/guide/checkbox.md +++ b/packages/element/docs/guide/checkbox.md @@ -2,9 +2,17 @@ > 复选框 -## 使用案例 +## Markup Schema 案例 - + + +## JSON Schema 案例 + + + +## Template 案例 + + ## API diff --git a/packages/element/docs/guide/date-picker.md b/packages/element/docs/guide/date-picker.md index 3fcb03eb302..c7ad6bd0ce8 100644 --- a/packages/element/docs/guide/date-picker.md +++ b/packages/element/docs/guide/date-picker.md @@ -2,9 +2,17 @@ > 日期选择器 -## 使用案例 +## Markup Schema 案例 - + + +## JSON Schema 案例 + + + +## Template 案例 + + ## API diff --git a/packages/element/docs/guide/editable.md b/packages/element/docs/guide/editable.md new file mode 100644 index 00000000000..b7f53fa8b24 --- /dev/null +++ b/packages/element/docs/guide/editable.md @@ -0,0 +1,31 @@ +# Editable + +> 局部编辑器,对于一些空间要求较高的表单区域可以使用该组件 +> +> Editable 组件相当于是 FormItem 组件的变体,所以通常放在 decorator 中 + +## Markup Schema 案例 + + + +## JSON Schema 案例 + + + +## Template 案例 + + + +## API + +### Editable + +> 内联编辑 + +参考 [https://element.formilyjs.org/guide/form-item.html#api](https://element.formilyjs.org/guide/form-item.html#api) + +### EditablePopover + +> 浮层编辑 + +参考 [https://element.eleme.io/#/zh-CN/component/popover](https://element.eleme.io/#/zh-CN/component/popover) diff --git a/packages/element/docs/guide/form-grid.md b/packages/element/docs/guide/form-grid.md index 688ba72c929..cd6c36e4cba 100644 --- a/packages/element/docs/guide/form-grid.md +++ b/packages/element/docs/guide/form-grid.md @@ -2,9 +2,17 @@ > FormGrid 组件 -## 使用案例 +## Markup Schema 案例 - + + +## JSON Schema 案例 + + + +## 原生案例 + + ## API diff --git a/packages/element/docs/guide/form-item.md b/packages/element/docs/guide/form-item.md index 7c53927a070..ea79bf39f87 100644 --- a/packages/element/docs/guide/form-item.md +++ b/packages/element/docs/guide/form-item.md @@ -2,9 +2,27 @@ > 全新的 FormItem 组件,相比于 Element 的 FormItem,它支持的功能更多,同时它的定位是纯样式组件,不管理表单状态,所以也会更轻量,更方便定制 -## 使用案例 +## Markup Schema 案例 - + + +## JSON Schema 案例 + + + +## Template 案例 + + + +## 常用属性案例 + + + +## 反馈信息定制案例 + +可通过 `feedbackIcon` 传入指定反馈的按钮 + + ## API @@ -17,8 +35,8 @@ | labelStyle | CSSProperties | 标签样式 | - | | wrapperStyle | CSSProperties | 组件容器样式 | - | | className | string | 组件样式类名 | - | -| colon | boolean | 冒号 | true | -| tooltip | boolean | 问号提示 | - | +| colon | boolean | 冒号 | - | +| tooltip | String \| Vue Component | 问号提示 | - | | labelAlign | `"left"` \| `"right"` | 标签文本对齐方式 | `"right"` | | labelWrap | boolean | 标签换⾏,否则出现省略号,hover 有 tooltip | false | | labelWidth | `number` | 标签固定宽度 | - | @@ -35,7 +53,7 @@ | feedbackText | ReactNode | 反馈⽂案 | - | | feedbackLayout | `"loose"` \| `"terse"` \| `"popover" \| "none"` | 反馈布局 | - | | feedbackStatus | `"error"` \| `"warning"` \| `"success"` \| `"pending"` | 反馈布局 | - | -| feedbackIcon | ReactNode | 反馈图标 | - | +| feedbackIcon | string | 反馈图标 | - | | required | boolean | 星号提醒 | - | | asterisk | boolean | 星号提醒 | - | | gridSpan | number | ⽹格布局占宽 | - | diff --git a/packages/element/docs/guide/form-layout.md b/packages/element/docs/guide/form-layout.md index ba4abd1924f..5fcaedf719c 100644 --- a/packages/element/docs/guide/form-layout.md +++ b/packages/element/docs/guide/form-layout.md @@ -2,9 +2,17 @@ > 区块级布局批量控制组件,借助该组件,我们可以轻松的控制被 FormLayout 圈住的所有 FormItem 组件的布局模式 -## 使用案例 +## Markup Schema 案例 - + + +## JSON Schema 案例 + + + +## Template 案例 + + ## API diff --git a/packages/element/docs/guide/form-step.md b/packages/element/docs/guide/form-step.md new file mode 100644 index 00000000000..0800ad28f4c --- /dev/null +++ b/packages/element/docs/guide/form-step.md @@ -0,0 +1,52 @@ +# FormStep + +> 分步表单组件 +> +> 注意:该组件只能用在 Schema 场景 + +## Markup Schema 案例 + + + +## JSON Schema 案例 + + + +## API + +### FormStep + +| 属性名 | 类型 | 描述 | 默认值 | +| -------- | --------- | -------------------------------------- | ------ | +| formStep | IFormStep | 传入通过 createFormStep 创建出来的模型 | | + +其余参考 [https://element.eleme.io/#/zh-CN/component/steps](https://element.eleme.io/#/zh-CN/component/steps) + +### FormStepPane + +参考 [https://element.eleme.io/#/zh-CN/component/steps](https://element.eleme.io/#/zh-CN/component/steps) + +### createFormStep + +```ts pure +interface createFormStep { + (current?: number): IFormStep +} + +interface IFormStep { + //当前索引 + current: number + //是否允许向后 + allowNext: boolean + //是否允许向前 + allowBack: boolean + //设置当前索引 + setCurrent(key: number): void + //提交表单 + submit: Formily.Core.Models.Form['submit'] + //向后 + next(): void + //向前 + back(): void +} +``` diff --git a/packages/element/docs/guide/form-tab.md b/packages/element/docs/guide/form-tab.md new file mode 100644 index 00000000000..44d9fc3d174 --- /dev/null +++ b/packages/element/docs/guide/form-tab.md @@ -0,0 +1,44 @@ +# FormTab + +> 选项卡表单 +> +> 注意:该组件只适用于 Schema 场景 + +## Markup Schema 案例 + + + +## JSON Schema 案例 + + + +## API + +### FormTab + +| 属性名 | 类型 | 描述 | 默认值 | +| ------- | -------- | ------------------------------------- | ------ | +| formTab | IFormTab | 传入通过 createFormTab 创建出来的模型 | | + +其余参考 [https://element.eleme.io/#/zh-CN/component/tabs](https://element.eleme.io/#/zh-CN/component/tabs) + +### FormTabPane + +参考 [https://element.eleme.io/#/zh-CN/component/tabs](https://element.eleme.io/#/zh-CN/component/tabs) + +### createFormTab + +```ts pure +type ActiveKey = string | number + +interface createFormTab { + (defaultActiveKey?: ActiveKey): IFormTab +} + +interface IFormTab { + //激活主键 + activeKey: ActiveKey + //设置激活主键 + setActiveKey(key: ActiveKey): void +} +``` diff --git a/packages/element/docs/guide/form.md b/packages/element/docs/guide/form.md index 396f04adf95..0079c1393cb 100644 --- a/packages/element/docs/guide/form.md +++ b/packages/element/docs/guide/form.md @@ -12,14 +12,10 @@ 布局相关的 API 属性,我们参考 [FormLayout](./form-layout) 即可,剩下是 Form 组件独有的 API 属性 -| 属性名 | 类型 | 描述 | 默认值 | -| --------- | -------------------------------------------------- | ---------------------------------- | ------ | -| form | [Form](https://core.formilyjs.org/api/models/form) | Form 实例 | - | -| component | string | 渲染组件,可以指定为自定义组件渲染 | `form` | - -### 事件 - -| 事件名 | 回调参数 | 描述 | -| ---------------- | ------------------------------------------------------------------------------------------------ | ------------------------ | -| autoSubmit | `(values:any)=>any` | 回车提交事件回调 | -| autoSubmitFailed | (feedbacks: [IFormFeedback](https://core.formilyjs.org/api/models/form#iformfeedback)[]) => void | 回车提交校验失败事件回调 | +| 属性名 | 类型 | 描述 | 默认值 | +| ---------------------- | ------------------------------------------------------------------------------------------------ | ---------------------------------- | ------ | +| form | [Form](https://core.formilyjs.org/api/models/form) | Form 实例 | - | +| component | string | 渲染组件,可以指定为自定义组件渲染 | `form` | +| previewTextPlaceholder | string \| Vue Component | 预览态占位符 | `N/A` | +| onAutoSubmit | `(values:any)=>any` | 回车提交事件回调 | - | +| onAutoSubmitFailed | (feedbacks: [IFormFeedback](https://core.formilyjs.org/api/models/form#iformfeedback)[]) => void | 回车提交校验失败事件回调 | - | diff --git a/packages/element/docs/guide/index.md b/packages/element/docs/guide/index.md new file mode 100644 index 00000000000..5b9c216204e --- /dev/null +++ b/packages/element/docs/guide/index.md @@ -0,0 +1,71 @@ +# Element-UI + +## 介绍 + +@formily/element 是基于 Element UI 封装的针对表单场景专业级(Professional)组件库,它主要有以下几个特点: + +- 更丰富的组件体系 + + - 布局组件 + + - FormLayout + - FormItem + - FormGrid + - FormButtonGroup + - Space + - Submit + - Reset + + - 输入控件 + - Input + - Password + - Select + - DatePicker + - TimePicker + - InputNumber + - Transfer + - Cascader + - Radio + - Checkbox + - Upload + - Switch + - 场景组件 + - ArrayCards + - ArrayItems + - ArrayTable + - ArrayTabs + - FormCollapse + - FormStep + - FormTab + - Editable + - 阅读态组件 + - PreviewText + +- 主题定制能力 + - follow 组件库的样式体系,更方便定制主题 +- 支持二次封装 + - 所有组件都能二次封装 +- 支持阅读态 + - 提供了 PreviewText 组件,用户可以基于它自己做阅读态封装,灵活性更强 +- 类型更加友好 + - 每个组件都有着极其完整的类型定义,用户在实际开发过程中,可以感受到前所未有的智能提示体验 +- 更完备的布局控制能力 + - 基于 FormLayout、FormItem、FormGrid 组件,提供更智能的布局能力。 + +## 安装 + +```bash +$ npm install --save element-ui +$ npm install --save @formily/core @formily/vue @vue/composition-api @formily/element + +``` + +## Q/A + +问:我想自己封装一套组件库,该怎么做? + +答:如果是开源组件库,可以直接参与项目共建,提供 PR,如果是企业内私有组件库,参考源码即可,源码并没有太多复杂逻辑。 + +问:为什么 ArrayCards/ArrayTable/FormStep 这类组件只支持 Schema 模式,不支持纯 Template 模式? + +答:这就是 Schema 模式的核心优势,借助协议,我们可以做场景化抽象,相反,纯 Template 模式,受限于 Template 的不可解析性,我们很难做到 UI 级别的场景化抽象,更多的只是抽象 Hook。 diff --git a/packages/element/docs/guide/input-number.md b/packages/element/docs/guide/input-number.md index a183817c078..9c17ea38548 100644 --- a/packages/element/docs/guide/input-number.md +++ b/packages/element/docs/guide/input-number.md @@ -2,9 +2,17 @@ > 数字输入框 -## 使用案例 +## Markup Schema 案例 - + + +## JSON Schema 案例 + + + +## Template 案例 + + ## API diff --git a/packages/element/docs/guide/input.md b/packages/element/docs/guide/input.md index fcb095f715a..846471f8fc1 100644 --- a/packages/element/docs/guide/input.md +++ b/packages/element/docs/guide/input.md @@ -2,9 +2,17 @@ > 文本输入框 -## 使用案例 +## Markup Schema 案例 - + + +## JSON Schema 案例 + + + +## Template 案例 + + ## API diff --git a/packages/element/docs/guide/password.md b/packages/element/docs/guide/password.md index 2e7d26d099f..5eb79c55cf2 100644 --- a/packages/element/docs/guide/password.md +++ b/packages/element/docs/guide/password.md @@ -2,9 +2,17 @@ > 密码输入框 -## 使用案例 +## Markup Schema 案例 - + + +## JSON Schema 案例 + + + +## Template 案例 + + ## API diff --git a/packages/element/docs/guide/preview-text.md b/packages/element/docs/guide/preview-text.md new file mode 100644 index 00000000000..963e91c1f70 --- /dev/null +++ b/packages/element/docs/guide/preview-text.md @@ -0,0 +1,53 @@ +# PreviewText + +> 阅读态组件,主要用来实现类 Input,类 DatePicker 这些组件的阅读态 + +## 简单案例 + + + +## 扩展案例 + + + +## API + +### PreviewInputText + +参考 [https://element.eleme.io/#/zh-CN/component/input](https://element.eleme.io/#/zh-CN/component/input) + +### PreviewSelectText + +参考 [https://element.eleme.io/#/zh-CN/component/select](https://element.eleme.io/#/zh-CN/component/select) + +### PreviewCascaderText + +参考 [https://element.eleme.io/#/zh-CN/component/cascader](https://element.eleme.io/#/zh-CN/component/cascader) + +### PreviewDatePickerText + +参考 [https://element.eleme.io/#/zh-CN/component/date-picker](https://element.eleme.io/#/zh-CN/component/date-picker) + +### PreviewTimePickerText + +参考 [https://element.eleme.io/#/zh-CN/component/time-picker](https://element.eleme.io/#/zh-CN/component/time-picker) + +### PreviewText + +| 属性名 | 类型 | 描述 | 默认值 | +| ------ | ------ | ---------- | ------ | +| value | stirng | 缺省占位符 | N/A | + +### PreviewTextPlaceholder + +| 属性名 | 类型 | 描述 | 默认值 | +| ------ | ------ | ---------- | ------ | +| value | stirng | 缺省占位符 | N/A | + +### usePreviewTextPlaceholder + +```ts pure +interface usePreviewTextPlaceholder { + (): string +} +``` diff --git a/packages/element/docs/guide/radio.md b/packages/element/docs/guide/radio.md index 4dd79f7c54c..b9dc537cd1b 100644 --- a/packages/element/docs/guide/radio.md +++ b/packages/element/docs/guide/radio.md @@ -2,9 +2,17 @@ > 单选框 -## 使用案例 +## Markup Schema 案例 - + + +## JSON Schema 案例 + + + +## Template 案例 + + ## API diff --git a/packages/element/docs/guide/reset.md b/packages/element/docs/guide/reset.md index b14c61aa2bb..a2b42b0fd70 100644 --- a/packages/element/docs/guide/reset.md +++ b/packages/element/docs/guide/reset.md @@ -22,8 +22,8 @@ ### 事件 -| 事件名 | 回调参数 | 描述 | -| -------------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------- | -| click | `(event: MouseEvent) => void \| boolean` | 点击事件,如果返回 false 可以阻塞提交 | -| resetValidateSuccess | (payload: any) => void | 提交成功响应事件 | -| resetValidateFailed | (feedbacks: [IFormFeedback](https://core.formilyjs.org/api/models/form#iformfeedback)[]) => void | 提交校验失败事件回调 | +| 属性名 | 类型 | 描述 | 默认值 | +| ---------------------- | ------------------------------------------------------------------------------------------------ | ---------------- | ------------------------------------- | --- | +| onClick | `(event: MouseEvent) => void | boolean` | 点击事件,如果返回 false 可以阻塞重置 | - | +| onResetValidateSuccess | (payload: any) => void | 重置校验成功事件 | - | +| onResetValidateFailed | (feedbacks: [IFormFeedback](https://core.formilyjs.org/api/models/form#iformfeedback)[]) => void | 重置校验失败事件 | - | diff --git a/packages/element/docs/guide/select.md b/packages/element/docs/guide/select.md index cfa8e74161f..956e154703a 100644 --- a/packages/element/docs/guide/select.md +++ b/packages/element/docs/guide/select.md @@ -1,10 +1,34 @@ # Select -> 选择器 +> 下拉框组件 -## 使用案例 +## Markup Schema 同步数据源案例 - + + +## Markup Schema 异步搜索案例 + + + +## Markup Schema 异步联动数据源案例 + + + +## JSON Schema 同步数据源案例 + + + +## JSON Schema 异步联动数据源案例 + + + +## Template 同步数据源案例 + + + +## Template 异步联动数据源案例 + + ## API diff --git a/packages/element/docs/guide/space.md b/packages/element/docs/guide/space.md index 3c7b7a4318a..fd1bb87091d 100644 --- a/packages/element/docs/guide/space.md +++ b/packages/element/docs/guide/space.md @@ -2,9 +2,17 @@ > 超级便捷的 Flex 布局组件,可以帮助用户快速实现任何元素的并排紧挨布局 -## 使用案例 +## Markup Schema 案例 - + + +## JSON Schema 案例 + + + +## Template 案例 + + ## API diff --git a/packages/element/docs/guide/submit.md b/packages/element/docs/guide/submit.md index bcfe0eeb9da..efdf6a4b01c 100644 --- a/packages/element/docs/guide/submit.md +++ b/packages/element/docs/guide/submit.md @@ -14,11 +14,9 @@ 按钮相关的 API 属性,我们参考 [https://element.eleme.io/#/zh-CN/component/button](https://element.eleme.io/#/zh-CN/component/button) 即可,剩下是 Submit 组件独有的 API 属性 -### 事件 - -| 事件名 | 回调参数 | 描述 | -| ------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------- | -| click | `(event: MouseEvent) => void \| boolean` | 点击事件,如果返回 false 可以阻塞提交 | -| submit | `(values: any) => Promise \| any` | 提交事件回调 | -| submitSuccess | (payload: any) => void | 提交成功响应事件 | -| submitFailed | (feedbacks: [IFormFeedback](https://core.formilyjs.org/api/models/form#iformfeedback)[]) => void | 提交校验失败事件回调 | +| 属性名 | 类型 | 描述 | 默认值 | +| --------------- | ------------------------------------------------------------------------------------------------ | -------------------- | ------------------------------------- | --- | +| onClick | `(event: MouseEvent) => void | boolean` | 点击事件,如果返回 false 可以阻塞提交 | - | +| onSubmit | `(values: any) => Promise | any` | 提交事件回调 | - | +| onSubmitSuccess | (payload: any) => void | 提交成功响应事件 | - | +| onSubmitFailed | (feedbacks: [IFormFeedback](https://core.formilyjs.org/api/models/form#iformfeedback)[]) => void | 提交校验失败事件回调 | - | diff --git a/packages/element/docs/guide/switch.md b/packages/element/docs/guide/switch.md index 7493bad97f8..5d6cd40b033 100644 --- a/packages/element/docs/guide/switch.md +++ b/packages/element/docs/guide/switch.md @@ -2,9 +2,17 @@ > 开关组件 -## 使用案例 +## Markup Schema 案例 - + + +## JSON Schema 案例 + + + +## Template 案例 + + ## API diff --git a/packages/element/docs/guide/time-picker.md b/packages/element/docs/guide/time-picker.md index cd45745b5cc..ab0e18da36d 100644 --- a/packages/element/docs/guide/time-picker.md +++ b/packages/element/docs/guide/time-picker.md @@ -2,9 +2,17 @@ > 时间选择器 -## 使用案例 +## Markup Schema 案例 - + + +## JSON Schema 案例 + + + +## Template 案例 + + ## API diff --git a/packages/element/docs/guide/transfer.md b/packages/element/docs/guide/transfer.md index 91ff290b3a1..0af1e7d6fb9 100644 --- a/packages/element/docs/guide/transfer.md +++ b/packages/element/docs/guide/transfer.md @@ -2,9 +2,17 @@ > 穿梭框 -## 使用案例 +## Markup Schema 案例 - + + +## JSON Schema 案例 + + + +## Template 案例 + + ## API diff --git a/packages/element/docs/guide/upload.md b/packages/element/docs/guide/upload.md index 11c2262c82f..cc9a1a59403 100644 --- a/packages/element/docs/guide/upload.md +++ b/packages/element/docs/guide/upload.md @@ -4,9 +4,17 @@ > > 注意:使用上传组件,推荐用户进行二次封装,用户无需关心上传组件与 Formily 的数据通信,只需要处理样式与基本上传配置即可。 -## 使用案例 +## Markup Schema 案例 - + + +## JSON Schema 案例 + + + +## Template 案例 + + ## API diff --git a/packages/element/package.json b/packages/element/package.json index 288d6304495..7b6f143381f 100644 --- a/packages/element/package.json +++ b/packages/element/package.json @@ -49,7 +49,8 @@ "@formily/reactive": "2.0.0-beta.79", "@formily/reactive-vue": "2.0.0-beta.79", "@formily/shared": "2.0.0-beta.79", - "@formily/vue": "2.0.0-beta.79" + "@formily/vue": "2.0.0-beta.79", + "vue-slicksort": "^1.2.0" }, "peerDependencies": { "@vue/composition-api": "^1.0.0-beta.1", diff --git a/packages/element/src/__builtins__/shared/create-context.ts b/packages/element/src/__builtins__/shared/create-context.ts new file mode 100644 index 00000000000..5327d6946b8 --- /dev/null +++ b/packages/element/src/__builtins__/shared/create-context.ts @@ -0,0 +1,57 @@ +import type { Component } from 'vue' +import { + defineComponent, + provide, + inject, + readonly, + InjectionKey, + ref, + Ref, + toRef, +} from 'vue-demi' + +export type CreateContext = { + Provider: Component + Consumer: Component + injectKey: InjectionKey> +} + +export const createContext = (defaultValue?: T): CreateContext => { + const injectKey: InjectionKey> = Symbol() + + return { + Provider: defineComponent({ + name: 'ContextProvider', + props: { + value: { + type: null, + default() { + return defaultValue ?? null + }, + }, + }, + setup(props, { slots }) { + const value = toRef(props, 'value') + provide(injectKey, readonly(value)) + + return () => slots?.default?.() + }, + }), + + Consumer: defineComponent({ + name: 'ContextConsumer', + setup(_props, { slots }) { + const value = inject(injectKey) + + return () => slots?.default?.(value) + }, + }), + injectKey, + } +} + +export const useContext = (context: CreateContext) => { + const key = context.injectKey + + return inject(key, ref(null)) +} diff --git a/packages/element/src/__builtins__/shared/index.ts b/packages/element/src/__builtins__/shared/index.ts index 4d53d4ab49d..5ff711bb039 100644 --- a/packages/element/src/__builtins__/shared/index.ts +++ b/packages/element/src/__builtins__/shared/index.ts @@ -1,2 +1,3 @@ export * from './get-component-by-tag' export * from './resolve-component' +export * from './create-context' diff --git a/packages/element/src/__builtins__/shared/resolve-component.ts b/packages/element/src/__builtins__/shared/resolve-component.ts index 6f31ed64364..08abaca738f 100644 --- a/packages/element/src/__builtins__/shared/resolve-component.ts +++ b/packages/element/src/__builtins__/shared/resolve-component.ts @@ -1,8 +1,18 @@ import { h, toRaw } from 'vue-demi' -import { Component } from 'vue' +import { Component, VNode } from 'vue' -export const resolveComponent = (child?: Component | string) => { +export const resolveComponent = ( + child?: Component | string | ((...args: any[]) => VNode[]) +) => { if (child) { - return typeof child === 'string' ? child : h(toRaw(child)) + if (typeof child === 'string') { + return child + } else if (typeof child === 'function') { + return (child as Function)() + } else { + return h(toRaw(child)) + } } + + return null } diff --git a/packages/element/src/__builtins__/styles/array-base.scss b/packages/element/src/__builtins__/styles/array-base.scss deleted file mode 100644 index 48d6d4ea2a4..00000000000 --- a/packages/element/src/__builtins__/styles/array-base.scss +++ /dev/null @@ -1,23 +0,0 @@ -$array-base-prefix-cls: '#{$formily-prefix}-form-array-base'; - -.#{$array-base-prefix-cls}-addition { - transition: $--all-transition; -} - -.#{$array-base-prefix-cls}-remove { - i { - font-size: $--font-size-base; - } -} - -.#{$array-base-prefix-cls}-move-down { - i { - font-size: $--font-size-base; - } -} - -.#{$array-base-prefix-cls}-move-up { - i { - font-size: $--font-size-base; - } -} diff --git a/packages/element/src/__builtins__/styles/array-table.scss b/packages/element/src/__builtins__/styles/array-table.scss deleted file mode 100644 index 7ffb59a3626..00000000000 --- a/packages/element/src/__builtins__/styles/array-table.scss +++ /dev/null @@ -1,33 +0,0 @@ -$array-table-prefix-cls: '#{$formily-prefix}-form-array-table'; - -.#{$array-table-prefix-cls} { - .#{$formily-prefix}-form-item:not(.#{$formily-prefix}-form-item-feedback-layout-popover) { - margin-bottom: 0 !important; - } - - .#{$formily-prefix}-form-array-base-addition { - margin-top: 8px; - width: 100%; - border: $--border-width-base dashed $--border-color-base; - - &:hover { - background-color: $--color-white; - border-color: $--border-color-hover; - } - - &:active, - &:focus { - background-color: $--color-white; - border-color: $--color-primary; - } - } - - .#{$formily-prefix}-form-item-feedback-layout-popover { - margin-bottom: 0; - } - - &-inner-asterisk { - color: $--color-danger; - font-weight: $--font-weight-primary; - } -} diff --git a/packages/element/src/__builtins__/styles/form-grid.scss b/packages/element/src/__builtins__/styles/form-grid.scss deleted file mode 100644 index e2adddd0856..00000000000 --- a/packages/element/src/__builtins__/styles/form-grid.scss +++ /dev/null @@ -1,3 +0,0 @@ -.#{$formily-prefix}-form-grid { - display: grid; -} diff --git a/packages/element/src/__builtins__/styles/form-item/animation.scss b/packages/element/src/__builtins__/styles/form-item/animation.scss deleted file mode 100644 index 5dbc2c32812..00000000000 --- a/packages/element/src/__builtins__/styles/form-item/animation.scss +++ /dev/null @@ -1,72 +0,0 @@ -@-webkit-keyframes antShowHelpIn { - 0% { - -webkit-transform: translateY(-5px); - transform: translateY(-5px); - opacity: 0; - } - - to { - -webkit-transform: translateY(0); - transform: translateY(0); - opacity: 1; - } -} - -.#{$form-item-prefix}-help-appear, -.#{$form-item-prefix}-help-enter { - -webkit-animation-duration: 0.3s; - animation-duration: 0.3s; - -webkit-animation-fill-mode: both; - animation-fill-mode: both; - -webkit-animation-play-state: paused; - animation-play-state: paused; -} - -.#{$form-item-prefix}-help-appear.#{$form-item-prefix}-help-appear-active, -.#{$form-item-prefix}-help-enter.#{$form-item-prefix}-help-enter-active { - -webkit-animation-name: antShowHelpIn; - animation-name: antShowHelpIn; - -webkit-animation-play-state: running; - animation-play-state: running; -} - -.#{$form-item-prefix}-help-appear, -.#{$form-item-prefix}-help-enter { - opacity: 0; -} - -.#{$form-item-prefix}-help-appear, -.#{$form-item-prefix}-help-enter { - -webkit-animation-timing-function: cubic-bezier(0.645, 0.045, 0.355, 1); - animation-timing-function: cubic-bezier(0.645, 0.045, 0.355, 1); -} - -@keyframes antShowHelpIn { - 0% { - -webkit-transform: translateY(-5px); - transform: translateY(-5px); - opacity: 0; - } - - to { - -webkit-transform: translateY(0); - transform: translateY(0); - opacity: 1; - } -} - -@-webkit-keyframes antShowHelpOut { - to { - -webkit-transform: translateY(-5px); - transform: translateY(-5px); - opacity: 0; - } -} - -@keyframes antShowHelpOut { - to { - -webkit-transform: translateY(-5px); - transform: translateY(-5px); - opacity: 0; - } -} diff --git a/packages/element/src/__builtins__/styles/form-item/grid.scss b/packages/element/src/__builtins__/styles/form-item/grid.scss deleted file mode 100644 index d9aaad8b4d8..00000000000 --- a/packages/element/src/__builtins__/styles/form-item/grid.scss +++ /dev/null @@ -1,171 +0,0 @@ -.#{$form-item-prefix}-item-col-24 { - -webkit-box-flex: 0; - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; -} - -.#{$form-item-prefix}-item-col-23 { - -webkit-box-flex: 0; - -ms-flex: 0 0 95.83333333%; - flex: 0 0 95.83333333%; - max-width: 95.83333333%; -} - -.#{$form-item-prefix}-item-col-22 { - -webkit-box-flex: 0; - -ms-flex: 0 0 91.66666667%; - flex: 0 0 91.66666667%; - max-width: 91.66666667%; -} - -.#{$form-item-prefix}-item-col-21 { - -webkit-box-flex: 0; - -ms-flex: 0 0 87.5%; - flex: 0 0 87.5%; - max-width: 87.5%; -} - -.#{$form-item-prefix}-item-col-20 { - -webkit-box-flex: 0; - -ms-flex: 0 0 83.33333333%; - flex: 0 0 83.33333333%; - max-width: 83.33333333%; -} - -.#{$form-item-prefix}-item-col-19 { - -webkit-box-flex: 0; - -ms-flex: 0 0 79.16666667%; - flex: 0 0 79.16666667%; - max-width: 79.16666667%; -} - -.#{$form-item-prefix}-item-col-18 { - -webkit-box-flex: 0; - -ms-flex: 0 0 75%; - flex: 0 0 75%; - max-width: 75%; -} - -.#{$form-item-prefix}-item-col-17 { - -webkit-box-flex: 0; - -ms-flex: 0 0 70.83333333%; - flex: 0 0 70.83333333%; - max-width: 70.83333333%; -} - -.#{$form-item-prefix}-item-col-16 { - -webkit-box-flex: 0; - -ms-flex: 0 0 66.66666667%; - flex: 0 0 66.66666667%; - max-width: 66.66666667%; -} - -.#{$form-item-prefix}-item-col-15 { - -webkit-box-flex: 0; - -ms-flex: 0 0 62.5%; - flex: 0 0 62.5%; - max-width: 62.5%; -} - -.#{$form-item-prefix}-item-col-14 { - -webkit-box-flex: 0; - -ms-flex: 0 0 58.33333333%; - flex: 0 0 58.33333333%; - max-width: 58.33333333%; -} - -.#{$form-item-prefix}-item-col-13 { - -webkit-box-flex: 0; - -ms-flex: 0 0 54.16666667%; - flex: 0 0 54.16666667%; - max-width: 54.16666667%; -} - -.#{$form-item-prefix}-item-col-12 { - -webkit-box-flex: 0; - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; -} - -.#{$form-item-prefix}-item-col-11 { - -webkit-box-flex: 0; - -ms-flex: 0 0 45.83333333%; - flex: 0 0 45.83333333%; - max-width: 45.83333333%; -} - -.#{$form-item-prefix}-item-col-10 { - -webkit-box-flex: 0; - -ms-flex: 0 0 41.66666667%; - flex: 0 0 41.66666667%; - max-width: 41.66666667%; -} - -.#{$form-item-prefix}-item-col-9 { - -webkit-box-flex: 0; - -ms-flex: 0 0 37.5%; - flex: 0 0 37.5%; - max-width: 37.5%; -} - -.#{$form-item-prefix}-item-col-8 { - -webkit-box-flex: 0; - -ms-flex: 0 0 33.33333333%; - flex: 0 0 33.33333333%; - max-width: 33.33333333%; -} - -.#{$form-item-prefix}-item-col-7 { - -webkit-box-flex: 0; - -ms-flex: 0 0 29.16666667%; - flex: 0 0 29.16666667%; - max-width: 29.16666667%; -} - -.#{$form-item-prefix}-item-col-6 { - -webkit-box-flex: 0; - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; -} - -.#{$form-item-prefix}-item-col-5 { - -webkit-box-flex: 0; - -ms-flex: 0 0 20.83333333%; - flex: 0 0 20.83333333%; - max-width: 20.83333333%; -} - -.#{$form-item-prefix}-item-col-4 { - -webkit-box-flex: 0; - -ms-flex: 0 0 16.66666667%; - flex: 0 0 16.66666667%; - max-width: 16.66666667%; -} - -.#{$form-item-prefix}-item-col-3 { - -webkit-box-flex: 0; - -ms-flex: 0 0 12.5%; - flex: 0 0 12.5%; - max-width: 12.5%; -} - -.#{$form-item-prefix}-item-col-2 { - -webkit-box-flex: 0; - -ms-flex: 0 0 8.33333333%; - flex: 0 0 8.33333333%; - max-width: 8.33333333%; -} - -.#{$form-item-prefix}-item-col-1 { - -webkit-box-flex: 0; - -ms-flex: 0 0 4.16666667%; - flex: 0 0 4.16666667%; - max-width: 4.16666667%; -} - -.#{$form-item-prefix}-item-col-0 { - display: none; -} diff --git a/packages/element/src/__builtins__/styles/form-item/index.scss b/packages/element/src/__builtins__/styles/form-item/index.scss deleted file mode 100644 index b4345b9bd01..00000000000 --- a/packages/element/src/__builtins__/styles/form-item/index.scss +++ /dev/null @@ -1,451 +0,0 @@ -@import './var.scss'; -@import './grid.scss'; -@import './animation.scss'; - -.#{$form-item-prefix} { - display: flex; - margin-bottom: $--form-item-margin-bottom; - position: relative; - line-height: $--form-item-medium-line-height; - font-size: $--form-font-size; - - &-label * { - line-height: $--form-item-medium-line-height; - } - - &-label-content { - min-height: $--form-item-medium-line-height; - } - - &-content-component { - line-height: $--form-item-medium-line-height; - } - - .#{$namespace}-input, - .#{$namespace}-input-number, - .#{$namespace}-input-number.is-controls-right, - .#{$namespace}-select, - .#{$namespace}-cascader, - .#{$namespace}-date-editor--daterange, - .#{$namespace}-date-editor--timerange, - .#{$namespace}-date-editor--datetimerange, - .#{$namespace}-date-editor.#{$namespace}-input, - .#{$namespace}-date-editor.#{$namespace}-input__inner, - .#{$namespace}-tree-select { - width: 100%; - } - - .#{$namespace}-input-group { - vertical-align: top; - } -} - -.#{$form-item-prefix}-label { - position: relative; - display: flex; - - &-content { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } -} - -.#{$form-item-prefix}-label label { - color: $--color-text-regular; -} - -.#{$form-item-prefix}-label-align-left { - > .#{$form-item-prefix}-label { - justify-content: flex-start; - } -} - -.#{$form-item-prefix}-label-align-right { - > .#{$form-item-prefix}-label { - justify-content: flex-end; - } -} - -.#{$form-item-prefix}-label-wrap { - .#{$form-item-prefix}-label { - label { - white-space: pre-line; - } - } -} - -.#{$form-item-prefix}-feedback-layout-terse { - margin-bottom: 8px; - - &.#{$form-item-prefix}-feedback-has-text { - margin-bottom: 0; - } -} - -.#{$form-item-prefix}-feedback-layout-loose { - margin-bottom: $--form-error-line-height; - - &.#{$form-item-prefix}-feedback-has-text { - margin-bottom: 0; - } -} - -.#{$form-item-prefix}-feedback-layout-none { - margin-bottom: 0; - - &.#{$form-item-prefix}-feedback-has-text { - margin-bottom: 0; - } -} - -.#{$form-item-prefix}-control { - width: 100%; - flex: 1; - - .#{$form-item-prefix}-control-content { - display: flex; - - .#{$form-item-prefix}-control-content-component { - width: 100%; - min-height: $--form-item-medium-line-height; - line-height: $--form-item-medium-line-height; - - &-has-feedback-icon { - flex: 1; - position: relative; - display: flex; - align-items: center; - } - } - - .#{$form-item-prefix}-addon-before { - margin-right: 8px; - display: inline-flex; - align-items: center; - min-height: $--form-item-medium-line-height; - flex-shrink: 0; - } - - .#{$form-item-prefix}-addon-after { - margin-left: 8px; - display: inline-flex; - align-items: center; - min-height: $--form-item-medium-line-height; - flex-shrink: 0; - } - } -} - -.#{$form-item-prefix}-size-small { - font-size: $--form-label-font-size; - - .#{$form-item-prefix}-label * { - line-height: $--form-item-small-line-height; - } - - .#{$form-item-prefix}-label-content { - min-height: $--form-item-small-line-height; - } - - .#{$form-item-prefix}-control-content { - .#{$form-item-prefix}-control-content-component { - line-height: $--form-item-small-line-height; - min-height: $--form-item-small-line-height; - } - } - - &.#{$form-item-prefix}-feedback-layout-terse { - margin-bottom: 8px; - - &.#{$form-item-prefix}-feedback-has-text { - margin-bottom: 0; - } - } - - &.#{$form-item-prefix}-feedback-layout-loose { - margin-bottom: $--form-error-line-height; - - &.#{$form-item-prefix}-feedback-has-text { - margin-bottom: 0; - } - } - - .#{$form-item-prefix}-help, - .#{$form-item-prefix}-extra { - min-height: $--form-error-line-height; - } - - .#{$form-item-prefix}-control-content { - min-height: $--form-item-small-line-height; - } - - .#{$form-item-prefix}-label > label { - height: $--form-item-small-line-height; - } -} - -.#{$form-item-prefix}-size-large { - font-size: $--form-font-size; - - .#{$form-item-prefix}-label * { - line-height: $--form-item-large-line-height; - } - - .#{$form-item-prefix}-label-content { - min-height: $--form-item-large-line-height; - } - - .#{$form-item-prefix}-control-content { - .#{$form-item-prefix}-control-content-component { - line-height: $--form-item-large-line-height; - min-height: $--form-item-large-line-height; - } - } - - .#{$form-item-prefix}-help, - .#{$form-item-prefix}-extra { - min-height: $--form-error-line-height; - } - - .#{$form-item-prefix}-control-content { - min-height: $--form-item-large-line-height; - } - - &.#{$form-item-prefix}-feedback-layout-loose { - margin-bottom: $--form-error-line-height; - - &.#{$form-item-prefix}-feedback-has-text { - margin-bottom: 0; - } - } -} - -.#{$form-item-prefix} { - &-layout-vertical { - display: block; - - .#{$form-item-prefix}-label * { - line-height: $--form-item-label-top-line-height; - } - - .#{$form-item-prefix}-label-content { - min-height: $--form-item-label-top-line-height; - } - } -} - -.#{$form-item-prefix}-feedback-layout-popover { - margin-bottom: 8px; -} - -.#{$form-item-prefix}-label-tooltip { - margin-left: 4px; - color: $--color-text-secondary; - display: flex; - align-items: center; - height: $--form-item-medium-line-height; - cursor: pointer; - i { - line-height: 1; - } -} - -.#{$form-item-prefix}-control-align-left { - .#{$form-item-prefix}-control-content { - justify-content: flex-start; - } -} - -.#{$form-item-prefix}-control-align-right { - .#{$form-item-prefix}-control-content { - justify-content: flex-end; - } -} - -.#{$form-item-prefix}-control-wrap { - .#{$form-item-prefix}-control { - white-space: pre-line; - } -} - -.#{$form-item-prefix}-asterisk { - color: $--color-danger; - margin-right: 4px; - display: inline-block; - font-family: SimSun, sans-serif; -} - -.#{$form-item-prefix}-colon { - margin-left: 4px / 2; - margin-right: 8px; -} - -.#{$form-item-prefix}-help, -.#{$form-item-prefix}-extra { - clear: both; - min-height: $--form-error-line-height; - line-height: $--form-error-line-height; - color: $--color-text-secondary; - transition: $--color-transition-base; - padding-top: 0; -} - -.#{$form-item-prefix}-fullness { - > .#{$form-item-prefix}-control { - > .#{$form-item-prefix}-control-content { - > .#{$form-item-prefix}-control-content-component { - > *:first-child { - width: 100%; - } - } - } - } -} - -.#{$form-item-prefix}-control-content-component-has-feedback-icon { - border-radius: $--border-radius-base; - border: $--border-base; - padding-right: 8px; - transition: $--all-transition; - touch-action: manipulation; - outline: none; - - .#{$namespace}-input-number, - .#{$namespace}-date-editor .#{$namespace}-input__inner, - .#{$namespace}-select .#{$namespace}-input__inner, - .#{$namespace}-input .#{$namespace}-input__inner { - border: none !important; - box-shadow: none !important; - } - .#{$namespace}-input-number.is-controls-right .#{$namespace}-input__inner { - padding-right: 40px; - } - .#{$namespace}-input-number.is-controls-right - .#{$namespace}-input-number__increase { - top: 0; - right: 8px; - border-right: $--border-base; - } - .#{$namespace}-input-number.is-controls-right - .#{$namespace}-input-number__decrease { - bottom: 0; - right: 8px; - border-right: $--border-base; - } -} - -.#{$form-item-prefix} { - &:hover { - .#{$form-item-prefix}-control-content-component-has-feedback-icon { - @include hover; - } - } -} - -.#{$form-item-prefix}-active { - .#{$form-item-prefix}-control-content-component-has-feedback-icon { - @include active; - } -} - -.#{$form-item-prefix}-error { - & .#{$namespace}-input__inner, - & .#{$namespace}-textarea__inner { - &, - &.hover { - border-color: $--color-danger; - } - } - - & .#{$namespace}-input__inner, - & .#{$namespace}-textarea__inner { - &:focus { - border-color: $--color-danger; - } - } - - & .#{$namespace}-input-group__append, - & .#{$namespace}-input-group__prepend { - & .#{$namespace}-input__inner { - border-color: transparent; - } - } - .#{$namespace}-input__validateIcon { - color: $--color-danger !important; - } -} - -.#{$form-item-prefix}-error-help, -.#{$form-item-prefix}-warning-help, -.#{$form-item-prefix}-success-help { - i { - margin-right: 8px; - } -} - -.#{$form-item-prefix}-error-help { - color: $--color-danger; -} - -.#{$form-item-prefix}-warning-help { - color: $--color-warning; -} - -.#{$form-item-prefix}-success-help { - color: $--color-success; -} - -.#{$form-item-prefix}-warning { - & .#{$namespace}-input__inner, - & .#{$namespace}-textarea__inner { - &, - &.hover { - border-color: $--color-warning; - } - } - - & .#{$namespace}-input__inner, - & .#{$namespace}-textarea__inner { - &:focus { - border-color: $--color-warning; - } - } - - & .#{$namespace}-input-group__append, - & .#{$namespace}-input-group__prepend { - & .#{$namespace}-input__inner { - border-color: transparent; - } - } - .#{$namespace}-input__validateIcon { - color: $--color-warning !important; - } -} - -.#{$form-item-prefix}-success { - & .#{$namespace}-input__inner, - & .#{$namespace}-textarea__inner { - &, - &.hover { - border-color: $--color-success; - } - } - - & .#{$namespace}-input__inner, - & .#{$namespace}-textarea__inner { - &:focus { - border-color: $--color-success; - } - } - - & .#{$namespace}-input-group__append, - & .#{$namespace}-input-group__prepend { - & .#{$namespace}-input__inner { - border-color: transparent; - } - } - .#{$namespace}-input__validateIcon { - color: $--color-success !important; - } -} diff --git a/packages/element/src/__builtins__/styles/form-item/var.scss b/packages/element/src/__builtins__/styles/form-item/var.scss deleted file mode 100644 index b2aab2e6a7f..00000000000 --- a/packages/element/src/__builtins__/styles/form-item/var.scss +++ /dev/null @@ -1,17 +0,0 @@ -$form-item-prefix: '#{$formily-prefix}-form-item'; - -$--form-font-size: $--font-size-base !default; - -$--form-label-font-size: $--form-font-size !default; - -$--form-item-large-line-height: 40px; - -$--form-item-medium-line-height: 32px; - -$--form-item-small-line-height: 24px; - -$--form-item-label-top-line-height: 40px; - -$--form-error-line-height: 24px !default; - -$--form-item-margin-bottom: $--form-error-line-height !default; diff --git a/packages/element/src/__builtins__/styles/space.scss b/packages/element/src/__builtins__/styles/space.scss deleted file mode 100644 index 7ba518bda5e..00000000000 --- a/packages/element/src/__builtins__/styles/space.scss +++ /dev/null @@ -1,21 +0,0 @@ -.#{$formily-prefix}-space { - display: inline-flex; - &-vertical { - flex-direction: column; - } - - &-align { - &-center { - align-items: center; - } - &-start { - align-items: flex-start; - } - &-end { - align-items: flex-end; - } - &-baseline { - align-items: baseline; - } - } -} diff --git a/packages/element/src/array-base/index.ts b/packages/element/src/array-base/index.ts index 881820ca496..ff326446581 100644 --- a/packages/element/src/array-base/index.ts +++ b/packages/element/src/array-base/index.ts @@ -7,32 +7,41 @@ import { toRefs, ref, } from 'vue-demi' -import { FragmentComponent, useField, useFieldSchema, h } from '@formily/vue' +import { Fragment, useField, useFieldSchema, h } from '@formily/vue' import { isValid, uid } from '@formily/shared' import { ArrayField } from '@formily/core' import { stylePrefix } from '../__builtins__/configs' -import type { Component } from 'vue' -import type { Button as ElButtonProps } from 'element-ui' -import { Button as ElButton } from 'element-ui' +import type { Button as ButtonProps } from 'element-ui' +import { Button } from 'element-ui' import type { Schema } from '@formily/json-schema' +import { HandleDirective } from 'vue-slicksort' -interface AdditionProps extends ElButtonProps { +export interface IArrayBaseAdditionProps extends ButtonProps { title?: string method?: 'push' | 'unshift' + defaultValue?: any } -interface Context { - field: Ref - schema: Ref - keyMap: WeakMap +export interface IArrayBaseProps { + disabled?: boolean } -interface ItemProps { +export interface IArrayBaseItemProps { index: number } -const ArrayBaseSymbol: InjectionKey = Symbol('ArrayBaseContext') +export interface IArrayBaseContext { + field: Ref + schema: Ref + props: IArrayBaseProps + listeners: { + [key in string]?: Function + } +} + +const ArrayBaseSymbol: InjectionKey = + Symbol('ArrayBaseContext') const ItemSymbol: InjectionKey<{ index: Ref }> = Symbol('ItemContext') @@ -41,11 +50,24 @@ export const useArray = () => { return inject(ArrayBaseSymbol, null) } -const useIndex = (index?: number) => { +export const useIndex = (index?: number) => { const ctx = inject(ItemSymbol, null) return ctx ? ctx.index : ref(index) } +export const useKey = () => { + const keyMap = new WeakMap() + + return (record: any) => { + record = + record === null || typeof record !== 'object' ? Object(record) : record + if (!keyMap.has(record)) { + keyMap.set(record, uid()) + } + return keyMap.get(record) + } +} + const getDefaultValue = (defaultValue: any, schema: Schema): any => { if (isValid(defaultValue)) return defaultValue if (Array.isArray(schema?.items)) @@ -61,57 +83,116 @@ const getDefaultValue = (defaultValue: any, schema: Schema): any => { } export const ArrayBase = defineComponent({ - setup(props, { slots }) { + name: 'ArrayBase', + props: ['disabled'], + setup(props: IArrayBaseProps, { slots, listeners }) { const field = useField() const schema = useFieldSchema() - const keyMap = new WeakMap() - provide(ArrayBaseSymbol, { field, schema, keyMap }) + + provide(ArrayBaseSymbol, { field, schema, props, listeners }) return () => { - return h(FragmentComponent as Component, {}, slots) + return h(Fragment, {}, slots) } }, }) -export const ArrayBaseItem = defineComponent({ +export const ArrayBaseItem = defineComponent({ + name: 'ArrayBaseItem', props: ['index'], - setup(props, { slots }) { + setup(props: IArrayBaseItemProps, { slots }) { provide(ItemSymbol, toRefs(props)) return () => { - return h(FragmentComponent as Component, {}, slots) + return h(Fragment, {}, slots) } }, }) -export const ArrayAddition = defineComponent({ - props: ['title', 'method'], - setup(props, { attrs, listeners }) { +export const ArrayBaseSortHandle = defineComponent({ + name: 'ArrayBaseSortHandle', + props: ['index'], + directives: { + handle: HandleDirective, + }, + setup(props, { attrs }) { + const array = useArray() + const prefixCls = `${stylePrefix}-array-base` + + return () => { + if (!array) return null + if (array.field.value?.pattern !== 'editable') return null + + return h( + Button, + { + directives: [{ name: 'handle' }], + class: [`${prefixCls}-sort-handle`], + attrs: { + size: 'mini', + type: 'text', + icon: 'el-icon-rank', + ...attrs, + }, + }, + {} + ) + } + }, +}) + +export const ArrayBaseIndex = defineComponent({ + name: 'ArrayBaseIndex', + setup(props, { attrs }) { + const index = useIndex() + return () => + h( + 'span', + { + attrs, + }, + { + default: () => [`#${index.value + 1}.`], + } + ) + }, +}) + +export const ArrayBaseAddition = defineComponent({ + name: 'ArrayBaseAddition', + props: ['title', 'method', 'defaultValue'], + setup(props: IArrayBaseAdditionProps, { listeners }) { const self = useField() const array = useArray() - const prefixCls = `${stylePrefix}-form-array-base` + const prefixCls = `${stylePrefix}-array-base` return () => { + if (!array) return null if (array?.field.value.pattern !== 'editable') return null return h( - ElButton, + Button, { class: `${prefixCls}-addition`, attrs: { type: 'ghost', icon: 'qax-icon-Alone-Plus', - ...attrs, + ...props, }, on: { ...listeners, - click: () => { + click: (e) => { + if (array.props?.disabled) return const defaultValue = getDefaultValue( - attrs.defaultValue, + props.defaultValue, array?.schema.value ) if (props.method === 'unshift') { array?.field?.value.unshift(defaultValue) + array.listeners?.add?.(0) } else { array?.field?.value.push(defaultValue) + array.listeners?.add?.(array?.field?.value?.value?.length - 1) + } + if (listeners.click) { + listeners.click(e) } - array.keyMap.set(defaultValue, uid()) }, }, }, @@ -123,29 +204,37 @@ export const ArrayAddition = defineComponent({ }, }) -export const ArrayRemove = defineComponent< - ElButtonProps & { title?: string; index?: number } +export const ArrayBaseRemove = defineComponent< + ButtonProps & { title?: string; index?: number } >({ + name: 'ArrayBaseRemove', props: ['title', 'index'], setup(props, { attrs, listeners }) { const indexRef = useIndex(props.index) const base = useArray() - const prefixCls = `${stylePrefix}-form-array-base` + const prefixCls = `${stylePrefix}-array-base` return () => { if (base?.field.value.pattern !== 'editable') return null return h( - ElButton, + Button, { class: `${prefixCls}-remove`, attrs: { type: 'text', - icon: props.title ? undefined : 'qax-icon-Trash', + size: 'mini', + icon: 'el-icon-delete', ...attrs, }, on: { ...listeners, - click: () => { + click: (e: MouseEvent) => { + e.stopPropagation() base?.field.value.remove(indexRef.value as number) + base?.listeners?.remove?.(indexRef.value as number) + + if (listeners.click) { + listeners.click(e) + } }, }, }, @@ -157,29 +246,37 @@ export const ArrayRemove = defineComponent< }, }) -export const ArrayMoveDown = defineComponent< - ElButtonProps & { title?: string; index?: number } +export const ArrayBaseMoveDown = defineComponent< + ButtonProps & { title?: string; index?: number } >({ + name: 'ArrayBaseMoveDown', props: ['title', 'index'], setup(props, { attrs, listeners }) { const indexRef = useIndex(props.index) const base = useArray() - const prefixCls = `${stylePrefix}-form-array-base` + const prefixCls = `${stylePrefix}-array-base` return () => { if (base?.field.value.pattern !== 'editable') return null return h( - ElButton, + Button, { class: `${prefixCls}-move-down`, attrs: { + size: 'mini', type: 'text', - icon: props.title ? undefined : 'qax-icon-Angle-down', + icon: 'el-icon-arrow-down', ...attrs, }, on: { ...listeners, - click: () => { + click: (e: MouseEvent) => { + e.stopPropagation() base?.field.value.moveDown(indexRef.value as number) + base?.listeners?.moveDown?.(indexRef.value as number) + + if (listeners.click) { + listeners.click(e) + } }, }, }, @@ -191,29 +288,37 @@ export const ArrayMoveDown = defineComponent< }, }) -export const ArrayMoveUp = defineComponent< - ElButtonProps & { title?: string; index?: number } +export const ArrayBaseMoveUp = defineComponent< + ButtonProps & { title?: string; index?: number } >({ + name: 'ArrayBaseMoveUp', props: ['title', 'index'], setup(props, { attrs, listeners }) { const indexRef = useIndex(props.index) const base = useArray() - const prefixCls = `${stylePrefix}-form-array-base` + const prefixCls = `${stylePrefix}-array-base` return () => { if (base?.field.value.pattern !== 'editable') return null return h( - ElButton, + Button, { class: `${prefixCls}-move-up`, attrs: { + size: 'mini', type: 'text', - icon: props.title ? undefined : 'qax-icon-Angle-up', + icon: 'el-icon-arrow-up', ...attrs, }, on: { ...listeners, - click: () => { + click: (e: MouseEvent) => { + e.stopPropagation() base?.field.value.moveUp(indexRef.value as number) + base?.listeners?.moveUp?.(indexRef.value as number) + + if (listeners.click) { + listeners.click(e) + } }, }, }, diff --git a/packages/element/src/array-base/style.scss b/packages/element/src/array-base/style.scss index a754f2731f9..a6301983436 100644 --- a/packages/element/src/array-base/style.scss +++ b/packages/element/src/array-base/style.scss @@ -1,6 +1,6 @@ @import '../__builtins__/styles/common.scss'; -$array-base-prefix-cls: '#{$formily-prefix}-form-array-base'; +$array-base-prefix-cls: '#{$formily-prefix}-array-base'; .#{$array-base-prefix-cls}-addition { transition: $--all-transition; @@ -23,3 +23,10 @@ $array-base-prefix-cls: '#{$formily-prefix}-form-array-base'; font-size: $--font-size-base; } } + +.#{$array-base-prefix-cls}-sort-handle { + i { + font-size: $--font-size-base; + cursor: move; + } +} diff --git a/packages/element/src/array-cards/index.ts b/packages/element/src/array-cards/index.ts new file mode 100644 index 00000000000..18ac0b1297d --- /dev/null +++ b/packages/element/src/array-cards/index.ts @@ -0,0 +1,240 @@ +import { defineComponent } from 'vue-demi' +import { Card, Empty, Row } from 'element-ui' +import type { Card as CardProps } from 'element-ui' +import { ArrayField } from '@formily/core' +import { useField, useFieldSchema, RecursionField, h } from '@formily/vue' +import { observer } from '@formily/reactive-vue' +import { ISchema } from '@formily/json-schema' +import { stylePrefix } from '../__builtins__/configs' +import { ArrayBase, ArrayBaseItem, useKey } from '../array-base' + +const isAdditionComponent = (schema: ISchema) => { + return schema['x-component']?.indexOf('Addition') > -1 +} + +const isIndexComponent = (schema: ISchema) => { + return schema['x-component']?.indexOf('Index') > -1 +} + +const isRemoveComponent = (schema: ISchema) => { + return schema['x-component']?.indexOf('Remove') > -1 +} + +const isMoveUpComponent = (schema: ISchema) => { + return schema['x-component']?.indexOf('MoveUp') > -1 +} + +const isMoveDownComponent = (schema: ISchema) => { + return schema['x-component']?.indexOf('MoveDown') > -1 +} + +const isOperationComponent = (schema: ISchema) => { + return ( + isAdditionComponent(schema) || + isRemoveComponent(schema) || + isMoveDownComponent(schema) || + isMoveUpComponent(schema) + ) +} + +export const ArrayCards = observer( + defineComponent({ + name: 'ArrayCards', + props: [], + setup(props, { attrs }) { + const fieldRef = useField() + const schemaRef = useFieldSchema() + const prefixCls = `${stylePrefix}-array-cards` + const getKey = useKey() + + return () => { + const field = fieldRef.value + const schema = schemaRef.value + const dataSource = Array.isArray(field.value) ? field.value.slice() : [] + + if (!schema) throw new Error('can not found schema object') + + const renderItems = () => { + return dataSource?.map((item, index) => { + const items = Array.isArray(schema.items) + ? schema.items[index] || schema.items[0] + : schema.items + + const title = h( + 'span', + {}, + { + default: () => [ + h( + RecursionField, + { + props: { + schema: items, + name: index, + filterProperties: (schema) => { + if (!isIndexComponent(schema)) return false + return true + }, + onlyRenderProperties: true, + }, + }, + {} + ), + attrs.title || field.title, + ], + } + ) + const extra = h( + 'span', + {}, + { + default: () => [ + h( + RecursionField, + { + props: { + schema: items, + name: index, + filterProperties: (schema) => { + if (!isOperationComponent(schema)) return false + return true + }, + onlyRenderProperties: true, + }, + }, + {} + ), + attrs.extra, + ], + } + ) + const content = h( + RecursionField, + { + props: { + schema: items, + name: index, + filterProperties: (schema) => { + if (isIndexComponent(schema)) return false + if (isOperationComponent(schema)) return false + return true + }, + }, + }, + {} + ) + return h( + ArrayBaseItem, + { + key: getKey(item), + props: { + index, + }, + }, + { + default: () => + h( + Card, + { + class: [`${prefixCls}-item`], + attrs: { + shadow: 'never', + ...attrs, + }, + }, + { + default: () => [ + content, + h( + Row, + { + props: { + type: 'flex', + justify: 'space-between', + }, + slot: 'header', + }, + { + default: () => [title, extra], + } + ), + ], + } + ), + } + ) + }) + } + const renderAddition = () => { + return schema.reduceProperties((addition, schema) => { + if (isAdditionComponent(schema)) { + return h( + RecursionField, + { + props: { + schema, + name: 'addition', + }, + }, + {} + ) + } + return addition + }, null) + } + const renderEmpty = () => { + if (dataSource?.length) return + return h( + Card, + { + class: [`${prefixCls}-item`], + attrs: { + shadow: 'never', + ...attrs, + header: attrs.title || field.title, + }, + }, + { + default: () => + h( + Empty, + { props: { description: 'No Data', imageSize: 100 } }, + {} + ), + } + ) + } + + return h( + 'div', + { + class: [prefixCls], + }, + { + default: () => + h( + ArrayBase, + {}, + { + default: () => [ + renderEmpty(), + renderItems(), + renderAddition(), + ], + } + ), + } + ) + } + }, + }) +) + +export { + ArrayBaseRemove as ArrayCardsRemove, + ArrayBaseMoveDown as ArrayCardsMoveDown, + ArrayBaseMoveUp as ArrayCardsMoveUp, + ArrayBaseAddition as ArrayCardsAddition, + ArrayBaseIndex as ArrayCardsIndex, + useIndex as useArrayCardsIndex, +} from '../array-base' diff --git a/packages/element/src/array-cards/style.scss b/packages/element/src/array-cards/style.scss new file mode 100644 index 00000000000..3eee7f5592f --- /dev/null +++ b/packages/element/src/array-cards/style.scss @@ -0,0 +1,33 @@ +@import '../__builtins__/styles/common.scss'; + +$array-table-prefix-cls: '#{$formily-prefix}-array-cards'; + +.#{$array-table-prefix-cls} { + .el-card__header { + padding-top: 12.5px; + padding-bottom: 12.5px; + } + .el-empty { + padding: 0; + } + + .#{$array-table-prefix-cls}-item { + margin-bottom: 10px; + } + + .#{$formily-prefix}-array-base-addition { + width: 100%; + border: $--border-width-base dashed $--border-color-base; + + &:hover { + background-color: $--color-white; + border-color: $--border-color-hover; + } + + &:active, + &:focus { + background-color: $--color-white; + border-color: $--color-primary; + } + } +} diff --git a/packages/element/src/array-cards/style.ts b/packages/element/src/array-cards/style.ts new file mode 100644 index 00000000000..2a3a2bb6411 --- /dev/null +++ b/packages/element/src/array-cards/style.ts @@ -0,0 +1,5 @@ +import './style.scss' +import 'element-ui/packages/theme-chalk/src/card.scss' +import 'element-ui/packages/theme-chalk/src/button.scss' +import 'element-ui/packages/theme-chalk/src/empty.scss' +import 'element-ui/packages/theme-chalk/src/row.scss' diff --git a/packages/element/src/array-collapse/index.ts b/packages/element/src/array-collapse/index.ts new file mode 100644 index 00000000000..a14a31bb4d1 --- /dev/null +++ b/packages/element/src/array-collapse/index.ts @@ -0,0 +1,375 @@ +import { defineComponent, ref, watchEffect, Ref } from 'vue-demi' +import { Card, Collapse, CollapseItem, Empty, Row, Badge } from 'element-ui' +import { ArrayField } from '@formily/core' +import type { + Collapse as CollapseProps, + CollapseItem as CollapseItemProps, +} from 'element-ui' +import { + useField, + useFieldSchema, + RecursionField, + h, + Fragment, +} from '@formily/vue' +import { observer } from '@formily/reactive-vue' +import { ISchema } from '@formily/json-schema' +import { stylePrefix } from '../__builtins__/configs' +import { ArrayBase, ArrayBaseItem, useKey } from '../array-base' + +export interface IArrayCollapseProps extends CollapseProps { + defaultOpenPanelCount?: number +} + +const isAdditionComponent = (schema: ISchema) => { + return schema['x-component']?.indexOf('Addition') > -1 +} + +const isIndexComponent = (schema: ISchema) => { + return schema['x-component']?.indexOf('Index') > -1 +} + +const isRemoveComponent = (schema: ISchema) => { + return schema['x-component']?.indexOf('Remove') > -1 +} + +const isMoveUpComponent = (schema: ISchema) => { + return schema['x-component']?.indexOf('MoveUp') > -1 +} + +const isMoveDownComponent = (schema: ISchema) => { + return schema['x-component']?.indexOf('MoveDown') > -1 +} + +const isOperationComponent = (schema: ISchema) => { + return ( + isAdditionComponent(schema) || + isRemoveComponent(schema) || + isMoveDownComponent(schema) || + isMoveUpComponent(schema) + ) +} + +const range = (count: number) => Array.from({ length: count }).map((_, i) => i) + +const takeDefaultActiveKeys = ( + dataSourceLength: number, + defaultOpenPanelCount: number, + accordion = false +) => { + if (accordion) { + return 0 + } + if (dataSourceLength < defaultOpenPanelCount) return range(dataSourceLength) + + return range(defaultOpenPanelCount) +} + +const insertActiveKeys = ( + activeKeys: number[] | number, + index: number, + accordion = false +) => { + if (accordion) return index + if ((activeKeys as number[]).length <= index) + return (activeKeys as number[]).concat(index) + return (activeKeys as number[]).reduce((buf, key) => { + if (key < index) return buf.concat(key) + if (key === index) return buf.concat([key, key + 1]) + return buf.concat(key + 1) + }, []) +} + +export const ArrayCollapse = observer( + defineComponent({ + name: 'ArrayCollapse', + props: { + defaultOpenPanelCount: { + type: Number, + default: 5, + }, + }, + setup(props, { attrs }) { + const fieldRef = useField() + const schemaRef = useFieldSchema() + + const prefixCls = `${stylePrefix}-array-collapse` + const activeKeys: Ref = ref([]) + + watchEffect(() => { + const field = fieldRef.value + const dataSource = Array.isArray(field.value) ? field.value.slice() : [] + if (!field.modified && dataSource.length) { + activeKeys.value = takeDefaultActiveKeys( + dataSource.length, + props.defaultOpenPanelCount, + attrs.accordion as boolean + ) + } + }) + + const getKey = useKey() + + return () => { + const field = fieldRef.value + const schema = schemaRef.value + const dataSource = Array.isArray(field.value) ? field.value.slice() : [] + if (!schema) throw new Error('can not found schema object') + + const renderItems = () => { + if (!dataSource.length) { + return null + } + + const items = dataSource?.map((item, index) => { + const items = Array.isArray(schema.items) + ? schema.items[index] || schema.items[0] + : schema.items + const key = getKey(item) + const panelProps = field + .query(`${field.address}.${index}`) + .get('componentProps') + const props: CollapseItemProps = items['x-component-props'] + const headerTitle = panelProps?.title || props.title || field.title + const path = field.address.concat(index) + const errors = field.form.queryFeedbacks({ + type: 'error', + address: `*(${path},${path}.*)`, + }) + + const title = h( + ArrayBaseItem, + { + props: { + index, + }, + }, + { + default: () => [ + h( + RecursionField, + { + props: { + schema: items, + name: index, + filterProperties: (schema) => { + if (!isIndexComponent(schema)) return false + return true + }, + onlyRenderProperties: true, + }, + }, + {} + ), + errors.length + ? h( + Badge, + { + class: [`${prefixCls}-errors-badge`], + props: { + value: errors.length, + }, + }, + { default: () => headerTitle } + ) + : headerTitle, + ], + } + ) + const extra = h( + ArrayBaseItem, + { + props: { + index, + }, + }, + { + default: () => [ + h( + RecursionField, + { + props: { + schema: items, + name: index, + filterProperties: (schema) => { + if (!isOperationComponent(schema)) return false + return true + }, + onlyRenderProperties: true, + }, + }, + {} + ), + ], + } + ) + const content = h( + RecursionField, + { + props: { + schema: items, + name: index, + filterProperties: (schema) => { + if (isIndexComponent(schema)) return false + if (isOperationComponent(schema)) return false + return true + }, + }, + }, + {} + ) + + return h( + CollapseItem, + { + attrs: { + ...props, + ...panelProps, + name: index, + }, + key, + }, + { + default: () => [ + h( + ArrayBaseItem, + { + props: { + index, + }, + }, + { + default: () => [content], + } + ), + h( + Row, + { + style: { flex: 1 }, + props: { + type: 'flex', + justify: 'space-between', + }, + slot: 'title', + }, + { + default: () => [ + h('span', {}, { default: () => title }), + h('span', {}, { default: () => extra }), + ], + } + ), + ], + } + ) + }) + + return h( + Collapse, + { + class: [`${prefixCls}-item`], + attrs: { + ...attrs, + value: activeKeys.value, + }, + on: { + change: (keys: number[] | number) => { + activeKeys.value = keys + }, + }, + }, + { + default: () => [items], + } + ) + } + const renderAddition = () => { + return schema.reduceProperties((addition, schema) => { + if (isAdditionComponent(schema)) { + return h( + RecursionField, + { + props: { + schema, + name: 'addition', + }, + }, + {} + ) + } + return addition + }, null) + } + const renderEmpty = () => { + if (dataSource?.length) return + return h( + Card, + { + class: [`${prefixCls}-item`], + attrs: { + shadow: 'never', + ...attrs, + header: attrs.title || field.title, + }, + }, + { + default: () => + h( + Empty, + { props: { description: 'No Data', imageSize: 100 } }, + {} + ), + } + ) + } + + return h( + 'div', + { + class: [prefixCls], + }, + { + default: () => + h( + ArrayBase, + { + on: { + add: (index: number) => { + activeKeys.value = insertActiveKeys( + activeKeys.value, + index, + attrs.accordion as boolean + ) + }, + }, + }, + { + default: () => [ + renderEmpty(), + renderItems(), + renderAddition(), + ], + } + ), + } + ) + } + }, + }) +) + +export const ArrayCollapseItem = defineComponent({ + name: 'ArrayCollapseItem', + setup(_props, { slots }) { + return () => h(Fragment, {}, slots) + }, +}) + +export { + ArrayBaseRemove as ArrayCollapseRemove, + ArrayBaseMoveDown as ArrayCollapseMoveDown, + ArrayBaseMoveUp as ArrayCollapseMoveUp, + ArrayBaseAddition as ArrayCollapseAddition, + ArrayBaseIndex as ArrayCollapseIndex, + useIndex as useArrayCollapseIndex, +} from '../array-base' diff --git a/packages/element/src/array-collapse/style.scss b/packages/element/src/array-collapse/style.scss new file mode 100644 index 00000000000..bd46b6a6a0f --- /dev/null +++ b/packages/element/src/array-collapse/style.scss @@ -0,0 +1,37 @@ +@import '../__builtins__/styles/common.scss'; + +$array-table-prefix-cls: '#{$formily-prefix}-array-collapse'; + +.#{$array-table-prefix-cls} { + .el-card__header { + padding-top: 12.5px; + padding-bottom: 12.5px; + } + .el-empty { + padding: 0; + } + + .#{$array-table-prefix-cls}-item { + margin-bottom: 10px; + } + + .#{$array-table-prefix-cls}-errors-badge { + line-height: 1; + } + + .#{$formily-prefix}-array-base-addition { + width: 100%; + border: $--border-width-base dashed $--border-color-base; + + &:hover { + background-color: $--color-white; + border-color: $--border-color-hover; + } + + &:active, + &:focus { + background-color: $--color-white; + border-color: $--color-primary; + } + } +} diff --git a/packages/element/src/array-collapse/style.ts b/packages/element/src/array-collapse/style.ts new file mode 100644 index 00000000000..3b85b1fdeaa --- /dev/null +++ b/packages/element/src/array-collapse/style.ts @@ -0,0 +1,8 @@ +import './style.scss' +import 'element-ui/packages/theme-chalk/src/button.scss' +import 'element-ui/packages/theme-chalk/src/empty.scss' +import 'element-ui/packages/theme-chalk/src/row.scss' +import 'element-ui/packages/theme-chalk/src/collapse.scss' +import 'element-ui/packages/theme-chalk/src/collapse-item.scss' +import 'element-ui/packages/theme-chalk/src/card.scss' +import 'element-ui/packages/theme-chalk/src/bdage.scss' diff --git a/packages/element/src/array-items/index.ts b/packages/element/src/array-items/index.ts new file mode 100644 index 00000000000..01c3bb20237 --- /dev/null +++ b/packages/element/src/array-items/index.ts @@ -0,0 +1,165 @@ +import { defineComponent } from 'vue-demi' +import { ArrayField } from '@formily/core' +import { useField, useFieldSchema, RecursionField, h } from '@formily/vue' +import { observer } from '@formily/reactive-vue' +import { ISchema } from '@formily/json-schema' +import { stylePrefix } from '../__builtins__/configs' +import { ArrayBase, ArrayBaseItem, useKey } from '../array-base' +import { SlickList, SlickItem } from 'vue-slicksort' + +const isAdditionComponent = (schema: ISchema) => { + return schema['x-component']?.indexOf('Addition') > -1 +} + +export interface IArrayItemsItemProps { + type?: 'card' | 'divide' +} + +export const ArrayItems = observer( + defineComponent({ + name: 'ArrayItems', + setup(props, { attrs }) { + const fieldRef = useField() + const schemaRef = useFieldSchema() + + const prefixCls = `${stylePrefix}-array-items` + const getKey = useKey() + + return () => { + const field = fieldRef.value + const schema = schemaRef.value + const dataSource = Array.isArray(field.value) ? field.value.slice() : [] + + const renderItems = () => { + const items = dataSource?.map((item, index) => { + const items = Array.isArray(schema.items) + ? schema.items[index] || schema.items[0] + : schema.items + const key = getKey(item) + return h( + ArrayBaseItem, + { + key, + props: { + index, + }, + }, + { + default: () => + h( + SlickItem, + { + class: [`${prefixCls}-item-inner`], + props: { + index, + }, + key, + }, + { + default: () => + h( + RecursionField, + { + props: { + schema: items, + name: index, + }, + }, + {} + ), + } + ), + } + ) + }) + + return h( + SlickList, + { + class: [`${prefixCls}-list`], + props: { + useDragHandle: true, + lockAxis: 'y', + helperClass: `${prefixCls}-sort-helper`, + onSortEnd: ({ oldIndex, newIndex }) => { + field.move(oldIndex, newIndex) + }, + value: [], + }, + }, + { default: () => items } + ) + } + const renderAddition = () => { + return schema.reduceProperties((addition, schema) => { + if (isAdditionComponent(schema)) { + return h( + RecursionField, + { + props: { + schema, + name: 'addition', + }, + }, + {} + ) + } + return addition + }, null) + } + + return h( + ArrayBase, + {}, + { + default: () => + h( + 'div', + { + class: [prefixCls], + attrs, + on: { + change: () => {}, + }, + }, + { + default: () => [renderItems(), renderAddition()], + } + ), + } + ) + } + }, + }) +) + +export const ArrayItemsItem = defineComponent({ + name: 'ArrayItemsItem', + props: ['type'], + setup(props, { attrs, slots }) { + const prefixCls = `${stylePrefix}-array-items` + + return () => + h( + 'div', + { + class: [`${prefixCls}-${props.type || 'card'}`], + attrs, + on: { + change: () => {}, + }, + }, + slots + ) + }, +}) + +export { + ArrayBaseSortHandle as ArrayItemsSortHandle, + ArrayBaseRemove as ArrayItemsRemove, + ArrayBaseMoveDown as ArrayItemsMoveDown, + ArrayBaseMoveUp as ArrayItemsMoveUp, + ArrayBaseAddition as ArrayItemsAddition, + ArrayBaseIndex as ArrayItemsIndex, + useIndex as useArrayItemsIndex, +} from '../array-base' diff --git a/packages/element/src/array-items/style.scss b/packages/element/src/array-items/style.scss new file mode 100644 index 00000000000..cdb22e4841a --- /dev/null +++ b/packages/element/src/array-items/style.scss @@ -0,0 +1,75 @@ +@import '../__builtins__/styles/common.scss'; + +$array-items-prefix-cls: '#{$formily-prefix}-array-items'; + +.#{$array-items-prefix-cls}-item-inner { + visibility: visible; +} + +.#{$array-items-prefix-cls} { + .#{$formily-prefix}-array-base-addition { + width: 100%; + border: $--border-width-base dashed $--border-color-base; + + &:hover { + background-color: $--color-white; + border-color: $--border-color-hover; + } + + &:active, + &:focus { + background-color: $--color-white; + border-color: $--color-primary; + } + } +} + +.#{$array-items-prefix-cls}-card { + display: flex; + border: 1px solid $--card-border-color; + margin-bottom: 10px; + padding: 3px 6px; + background: $--color-white; + justify-content: space-between; + + .#{$formily-prefix}-form-item:not(.#{$formily-prefix}-form-item-feedback-layout-popover) { + margin-bottom: 0 !important; + + .#{$formily-prefix}-form-item-help { + position: absolute; + font-size: 12px; + top: 100%; + background: $--color-white; + width: 100%; + margin-top: 3px; + padding: 3px; + z-index: 1; + border-radius: 3px; + box-shadow: 0 0 10px $--border-color-base; + } + } +} + +.#{$array-items-prefix-cls}-divide { + display: flex; + border-bottom: 1px solid $--card-border-color; + padding: 10px 0; + justify-content: space-between; + + .#{$formily-prefix}-form-item:not(.#{$formily-prefix}-form-item-feedback-layout-popover) { + margin-bottom: 0 !important; + + .#{$formily-prefix}-form-item-help { + position: absolute; + font-size: 12px; + top: 100%; + background: $--color-white; + width: 100%; + margin-top: 3px; + padding: 3px; + z-index: 1; + border-radius: 3px; + box-shadow: 0 0 10px $--card-border-color; + } + } +} diff --git a/packages/element/src/array-items/style.ts b/packages/element/src/array-items/style.ts new file mode 100644 index 00000000000..efcf77037a5 --- /dev/null +++ b/packages/element/src/array-items/style.ts @@ -0,0 +1,2 @@ +import './style.scss' +import 'element-ui/packages/theme-chalk/src/button.scss' diff --git a/packages/element/src/array-table/index.ts b/packages/element/src/array-table/index.ts index d18bfc7cd7b..d16685d8a8e 100644 --- a/packages/element/src/array-table/index.ts +++ b/packages/element/src/array-table/index.ts @@ -12,8 +12,8 @@ import { h, } from '@formily/vue' import { observer } from '@formily/reactive-vue' -import { isArr, uid } from '@formily/shared' -import { ArrayBase, ArrayBaseItem, useArray } from '../array-base' +import { isArr } from '@formily/shared' +import { ArrayBase, ArrayBaseItem } from '../array-base' import { stylePrefix } from '../__builtins__/configs' import type { Schema } from '@formily/json-schema' import type { TableColumn as ElColumnProps } from 'element-ui' @@ -99,7 +99,7 @@ const getArrayTableSources = ( } } - const parseArrayItems = (schema: Schema['items']) => { + const parseArrayTable = (schema: Schema['items']) => { const sources: ObservableColumnSource[] = [] const items = isArr(schema) ? schema : ([schema] as Schema[]) return items.reduce((columns, schema) => { @@ -113,7 +113,7 @@ const getArrayTableSources = ( if (!schemaRef) throw new Error('can not found schema object') - return parseArrayItems(schemaRef.value.items) + return parseArrayTable(schemaRef.value.items) } const getArrayTableColumns = ( @@ -192,21 +192,18 @@ const renderAddition = () => { const ArrayTableInner = observer( defineComponent({ + name: 'ArrayTableInner', setup(props, { attrs }) { const fieldRef = useField() const schemaRef = useFieldSchema() - const array = useArray() - const prefixCls = `${stylePrefix}-form-array-table-inner` + const prefixCls = `${stylePrefix}-array-table-inner` return () => { const field = fieldRef.value const sources = getArrayTableSources(fieldRef, schemaRef) const dataSource = Array.isArray(field.value) ? field.value.slice() : [] const columns = getArrayTableColumns(dataSource, sources) const defaultRowKey = (record: any) => { - if (!array?.keyMap.has(record)) { - array?.keyMap.set(record, uid()) - } - return array?.keyMap.get(record) + return dataSource.indexOf(record) } const renderColumns = () => columns.map(({ key, render, asterisk, ...props }) => { @@ -261,10 +258,11 @@ const ArrayTableInner = observer( export const ArrayTable = observer( defineComponent({ + name: 'ArrayTable', setup(props, { attrs }) { const fieldRef = useField() const schemaRef = useFieldSchema() - const prefixCls = `${stylePrefix}-form-array-table` + const prefixCls = `${stylePrefix}-array-table` return () => { const sources = getArrayTableSources(fieldRef, schemaRef) const renderStateManager = () => @@ -312,3 +310,13 @@ export const ArrayTableColumn: Component = { return h() }, } + +export { + ArrayBaseSortHandle as ArrayTableSortHandle, + ArrayBaseRemove as ArrayTableRemove, + ArrayBaseMoveDown as ArrayTableMoveDown, + ArrayBaseMoveUp as ArrayTableMoveUp, + ArrayBaseAddition as ArrayTableAddition, + ArrayBaseIndex as ArrayTableIndex, + useIndex as useArrayTableIndex, +} from '../array-base' diff --git a/packages/element/src/array-table/style.scss b/packages/element/src/array-table/style.scss index c81681207ce..05dab11fa8d 100644 --- a/packages/element/src/array-table/style.scss +++ b/packages/element/src/array-table/style.scss @@ -1,13 +1,13 @@ @import '../__builtins__/styles/common.scss'; -$array-table-prefix-cls: '#{$formily-prefix}-form-array-table'; +$array-table-prefix-cls: '#{$formily-prefix}-array-table'; .#{$array-table-prefix-cls} { .#{$formily-prefix}-form-item:not(.#{$formily-prefix}-form-item-feedback-layout-popover) { margin-bottom: 0 !important; } - .#{$formily-prefix}-form-array-base-addition { + .#{$formily-prefix}-array-base-addition { margin-top: 8px; width: 100%; border: $--border-width-base dashed $--border-color-base; diff --git a/packages/element/src/array-tabs/index.ts b/packages/element/src/array-tabs/index.ts new file mode 100644 index 00000000000..6281e75d483 --- /dev/null +++ b/packages/element/src/array-tabs/index.ts @@ -0,0 +1,163 @@ +import { defineComponent, ref } from 'vue-demi' +import { observer } from '@formily/reactive-vue' +import { ArrayField } from '@formily/core' +import { h, useField, useFieldSchema, RecursionField } from '@formily/vue' +import { Tabs, TabPane, Badge, Button } from 'element-ui' +import { stylePrefix } from '../__builtins__/configs' + +import type { Tabs as TabsProps } from 'element-ui' + +export const ArrayTabs = observer( + defineComponent({ + name: 'ArrayTabs', + props: [], + setup(props, { attrs }) { + const fieldRef = useField() + const schemaRef = useFieldSchema() + + const prefixCls = `${stylePrefix}-array-tabs` + const activeKey = ref('tab-0') + + return () => { + const field = fieldRef.value + const schema = schemaRef.value + const value = Array.isArray(field.value) ? field.value : [] + const dataSource = value?.length ? value : [{}] + + const onEdit = (targetKey: any, type: 'add' | 'remove') => { + if (type == 'add') { + const id = dataSource.length + if (field?.value?.length) { + field.push(null) + } else { + field.push(null, null) + } + activeKey.value = `tab-${id}` + } else if (type == 'remove') { + const index = targetKey.match(/-(\d+)/)?.[1] + field.remove(Number(index)) + if (activeKey.value === targetKey) { + activeKey.value = `tab-${index - 1}` + } + } + } + + const addButton = h( + Button, + { + class: [`${prefixCls}-addition`], + attrs: { + size: 'large', + icon: 'el-icon-plus', + }, + on: { + click: (e: MouseEvent) => { + e.stopPropagation() + e.preventDefault() + onEdit(null, 'add') + }, + }, + }, + {} + ) + + const badgedTab = (index: number) => { + const tab = `${field.title || 'Untitled'} ${index + 1}` + const path = field.address.concat(index) + const errors = field.form.queryFeedbacks({ + type: 'error', + address: `*(${path},${path}.*)`, + }) + if (errors.length) { + return h( + 'span', + { slot: 'label' }, + { + default: () => [ + h( + Badge, + { + class: [`${prefixCls}-errors-badge`], + props: { + value: errors.length, + }, + }, + { + default: () => [tab], + } + ), + index === dataSource.length - 1 ? addButton : null, + ], + } + ) + } + return h( + 'span', + { slot: 'label' }, + { + default: () => [ + tab, + index === dataSource.length - 1 ? addButton : null, + ], + } + ) + } + + const renderItems = () => + dataSource?.map((item, index) => { + const items = Array.isArray(schema.items) + ? schema.items[index] + : schema.items + const key = `tab-${index}` + + return h( + TabPane, + { + key, + attrs: { + closable: index !== 0, + name: key, + }, + }, + { + default: () => [ + h( + RecursionField, + { + props: { + schema: items, + name: index, + }, + }, + {} + ), + badgedTab(index), + ], + } + ) + }) + return h( + Tabs, + { + class: [prefixCls], + attrs: { + ...attrs, + type: 'card', + value: activeKey.value, + addable: true, + }, + on: { + input: (key) => { + activeKey.value = key + }, + 'tab-remove': (target) => onEdit(target, 'remove'), + }, + }, + { + default: () => [renderItems()], + } + ) + } + }, + }) +) diff --git a/packages/element/src/array-tabs/style.scss b/packages/element/src/array-tabs/style.scss new file mode 100644 index 00000000000..97efae5fe2c --- /dev/null +++ b/packages/element/src/array-tabs/style.scss @@ -0,0 +1,15 @@ +@import '../__builtins__/styles/common.scss'; + +$array-table-prefix-cls: '#{$formily-prefix}-array-tabs'; + +.#{$array-table-prefix-cls} { + .#{$formily-prefix}-array-tabs-addition { + position: absolute; + right: -56px; + top: -1px; + } + + .#{$array-table-prefix-cls}-errors-badge { + line-height: 1; + } +} diff --git a/packages/element/src/array-tabs/style.ts b/packages/element/src/array-tabs/style.ts new file mode 100644 index 00000000000..ae3defeaeae --- /dev/null +++ b/packages/element/src/array-tabs/style.ts @@ -0,0 +1,5 @@ +import './style.scss' +import 'element-ui/packages/theme-chalk/src/tab.scss' +import 'element-ui/packages/theme-chalk/src/tab-pane.scss' +import 'element-ui/packages/theme-chalk/src/bdage.scss' +import 'element-ui/packages/theme-chalk/src/button.scss' diff --git a/packages/element/src/cascader/index.ts b/packages/element/src/cascader/index.ts index 51e00ba05fc..a468b970f81 100644 --- a/packages/element/src/cascader/index.ts +++ b/packages/element/src/cascader/index.ts @@ -1,8 +1,13 @@ -import { connect, mapProps } from '@formily/vue' +import { connect, mapProps, mapReadPretty } from '@formily/vue' import { Cascader as ELCascader } from 'element-ui' import type { Cascader as ElCascaderProps } from 'element-ui' +import { PreviewCascaderText } from '../preview-text' export type CascaderProps = ElCascaderProps -export const Cascader = connect(ELCascader, mapProps({ dataSource: 'options' })) +export const Cascader = connect( + ELCascader, + mapProps({ dataSource: 'options' }), + mapReadPretty(PreviewCascaderText) +) diff --git a/packages/element/src/checkbox-group/index.ts b/packages/element/src/checkbox-group/index.ts index 2400df37947..7391d60ac77 100644 --- a/packages/element/src/checkbox-group/index.ts +++ b/packages/element/src/checkbox-group/index.ts @@ -1,4 +1,4 @@ -import { connect, mapProps, h } from '@formily/vue' +import { connect, mapProps, h, mapReadPretty } from '@formily/vue' import { getComponentByTag } from '../__builtins__/shared' import { Checkbox } from '../checkbox' import { defineComponent } from 'vue-demi' @@ -6,6 +6,7 @@ import { defineComponent } from 'vue-demi' import type { CheckboxGroup as ElCheckboxGroupProps } from 'element-ui' import type { CheckboxProps } from '../checkbox' import { CheckboxGroup as ElCheckboxGroup } from 'element-ui' +import { PreviewSelectText } from '../preview-text' export type CheckboxGroupProps = ElCheckboxGroupProps & { value: any[] @@ -17,6 +18,7 @@ const TransformElCheckboxGroup = getComponentByTag(ElCheckboxGroup, { }) const CheckboxGroupOption = defineComponent({ + name: 'CheckboxGroup', props: { options: { type: Array, @@ -59,5 +61,8 @@ const CheckboxGroupOption = defineComponent({ export const CheckboxGroup = connect( CheckboxGroupOption, - mapProps({ dataSource: 'options' }) + mapProps({ dataSource: 'options' }), + mapReadPretty(PreviewSelectText, { + multiple: true, + }) ) diff --git a/packages/element/src/checkbox/index.ts b/packages/element/src/checkbox/index.ts index b7367dd69aa..3988ff6b775 100644 --- a/packages/element/src/checkbox/index.ts +++ b/packages/element/src/checkbox/index.ts @@ -16,6 +16,7 @@ export interface CheckboxProps extends ElCheckboxProps { } const CheckboxOption = defineComponent({ + name: 'Checkbox', props: { option: { type: Object, diff --git a/packages/element/src/date-picker/index.ts b/packages/element/src/date-picker/index.ts index f7cb71a073b..23f37674b20 100644 --- a/packages/element/src/date-picker/index.ts +++ b/packages/element/src/date-picker/index.ts @@ -1,8 +1,9 @@ import { getComponentByTag } from '../__builtins__/shared' -import { connect, mapProps } from '@formily/vue' +import { connect, mapProps, mapReadPretty } from '@formily/vue' import type { DatePicker as ElDatePickerProps } from 'element-ui' import { DatePicker as ElDatePicker } from 'element-ui' +import { PreviewDatePickerText } from '../preview-text' export type DatePickerProps = ElDatePickerProps @@ -10,7 +11,30 @@ const TransformElDatePicker = getComponentByTag(ElDatePicker, { change: 'input', }) +const getDefaultFormat = (props, formatType = 'format') => { + const type = props.type + + if (type === 'week' && formatType === 'format') { + return 'yyyy-WW' + } else if (type === 'month') { + return 'yyyy-MM' + } else if (type === 'year') { + return 'yyyy' + } else if (type === 'datetime' || type === 'datetimerange') { + return 'yyyy-MM-dd HH:mm:ss' + } + + return 'yyyy-MM-dd' +} + export const DatePicker = connect( TransformElDatePicker, - mapProps({ readOnly: 'readonly' }) + mapProps({ readOnly: 'readonly' }, (props) => { + return { + ...props, + format: props.format || getDefaultFormat(props), + valueFormat: props.valueFormat || getDefaultFormat(props, 'valueFormat'), + } + }), + mapReadPretty(PreviewDatePickerText) ) diff --git a/packages/element/src/editable/index.ts b/packages/element/src/editable/index.ts new file mode 100644 index 00000000000..a6795e46346 --- /dev/null +++ b/packages/element/src/editable/index.ts @@ -0,0 +1,268 @@ +import { defineComponent, ref, onBeforeUnmount } from 'vue-demi' +import { observer } from '@formily/reactive-vue' +import { reaction } from '@formily/reactive' +import { isVoidField, Field } from '@formily/core' +import { h, useField } from '@formily/vue' +import { Popover } from 'element-ui' +import { stylePrefix } from '../__builtins__/configs' + +import type { Popover as PopoverProps } from 'element-ui' +import { FormBaseItem, FormItemProps } from '../form-item' + +export type EditableProps = FormItemProps +export type EditablePopoverProps = PopoverProps + +const getParentPattern = (fieldRef) => { + const field = fieldRef.value + return field?.parent?.pattern || field?.form?.pattern +} + +const getFormItemProps = (fieldRef): FormItemProps => { + const field = fieldRef.value + + if (isVoidField(field)) return {} + if (!field) return {} + const takeMessage = () => { + if (field.errors.length) return field.errors[0] + if (field.warnings.length) return field.warnings[0] + if (field.successes.length) return field.successes[0] + } + + return { + feedbackStatus: + field.validateStatus === 'validating' ? 'pending' : field.validateStatus, + feedbackText: takeMessage(), + extra: field.description, + } +} + +export const Editable = observer( + defineComponent({ + name: 'Editable', + setup(props, { attrs, slots, refs }) { + const fieldRef = useField() + + const prefixCls = `${stylePrefix}-editable` + const setEditable = (payload: boolean) => { + const pattern = getParentPattern(fieldRef) + + if (pattern !== 'editable') return + fieldRef.value.setPattern(payload ? 'editable' : 'readPretty') + } + + const dispose = reaction( + () => { + const pattern = getParentPattern(fieldRef) + + return pattern + }, + (pattern) => { + if (pattern === 'editable') { + fieldRef.value.setPattern('readPretty') + } + }, + { + fireImmediately: true, + } + ) + + onBeforeUnmount(dispose) + + return () => { + const field = fieldRef.value + const editable = field.pattern === 'editable' + const pattern = getParentPattern(fieldRef) + const itemProps = getFormItemProps(fieldRef) + + const recover = () => { + if (editable && !fieldRef.value?.errors?.length) { + setEditable(false) + } + } + + const onClick = (e: MouseEvent) => { + const innerRef = refs.innerRef as HTMLElement + const target = e.target as HTMLElement + const close = innerRef.querySelector(`.${prefixCls}-close-btn`) + + if (target?.contains(close) || close?.contains(target)) { + recover() + } else if (!editable) { + setTimeout(() => { + setEditable(true) + setTimeout(() => { + innerRef.querySelector('input')?.focus() + }) + }) + } + } + + const renderEditHelper = () => { + if (editable) return null + + return h( + FormBaseItem, + { + attrs: { + ...attrs, + ...itemProps, + }, + }, + { + default: () => { + return h( + 'i', + { + class: [ + `${prefixCls}-edit-btn`, + pattern === 'editable' + ? 'el-icon-edit' + : 'el-icon-chat-dot-round', + ], + }, + {} + ) + }, + } + ) + } + + const renderCloseHelper = () => { + if (!editable) return null + return h( + FormBaseItem, + { + attrs: { + ...attrs, + }, + }, + { + default: () => { + return h( + 'i', + { + class: [`${prefixCls}-close-btn`, 'el-icon-close'], + }, + {} + ) + }, + } + ) + } + + return h( + 'div', + { + class: prefixCls, + ref: 'innerRef', + on: { + click: onClick, + }, + }, + { + default: () => + h( + 'div', + { + class: `${prefixCls}-content`, + }, + { + default: () => [ + h( + FormBaseItem, + { + attrs: { + ...attrs, + ...itemProps, + }, + }, + slots + ), + renderEditHelper(), + renderCloseHelper(), + ], + } + ), + } + ) + } + }, + }) +) + +export const EditablePopover = observer( + defineComponent({ + name: 'EditablePopover', + setup(props, { attrs, slots }) { + const fieldRef = useField() + + const prefixCls = `${stylePrefix}-editable` + const visible = ref(false) + + return () => { + const field = fieldRef.value + const pattern = getParentPattern(fieldRef) + return h( + Popover, + { + class: [prefixCls], + attrs: { + ...attrs, + title: attrs.title || field.title, + value: visible.value, + trigger: 'click', + }, + on: { + input: (value) => { + visible.value = value + }, + }, + }, + { + default: () => [ + slots.default(), + h( + FormBaseItem, + { slot: 'reference', class: [`${prefixCls}-trigger`] }, + { + default: () => + h( + 'div', + { + class: [`${prefixCls}-content`], + }, + { + default: () => [ + h( + 'span', + { + class: [`${prefixCls}-preview`], + }, + { + default: () => [attrs.title || field.title], + } + ), + h( + 'i', + { + class: [ + `${prefixCls}-edit-btn`, + pattern === 'editable' + ? 'el-icon-edit' + : 'el-icon-chat-dot-round', + ], + }, + {} + ), + ], + } + ), + } + ), + ], + } + ) + } + }, + }) +) diff --git a/packages/element/src/editable/style.scss b/packages/element/src/editable/style.scss new file mode 100644 index 00000000000..57870c846a9 --- /dev/null +++ b/packages/element/src/editable/style.scss @@ -0,0 +1,53 @@ +@import '../__builtins__/styles/common.scss'; + +$editable-prefix-cls: '#{$formily-prefix}-editable'; + +.#{$editable-prefix-cls} { + cursor: pointer; + display: inline-block !important; + + .#{$formily-prefix}-form-text { + .#{$formily-prefix}-tag { + transition: none !important; + } + + .#{$formily-prefix}-tag:last-child { + margin-right: 0 !important; + } + } + + &-content { + display: flex; + align-items: center; + + > * { + margin-right: 3px; + &:last-child { + margin-right: 0; + } + } + } + + .#{$editable-prefix-cls}-edit-btn, + .#{$editable-prefix-cls}-close-btn { + transition: all 0.25s ease-in-out; + + &:hover { + color: $--color-primary; + } + } + + .#{$formily-prefix}-form-text { + display: flex; + align-items: center; + } + + .#{$editable-prefix-cls}-preview { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + word-break: break-all; + max-width: 100px; + display: block; + } +} diff --git a/packages/element/src/editable/style.ts b/packages/element/src/editable/style.ts new file mode 100644 index 00000000000..aab13f7636c --- /dev/null +++ b/packages/element/src/editable/style.ts @@ -0,0 +1,2 @@ +import './style.scss' +import 'element-ui/packages/theme-chalk/src/popover.scss' diff --git a/packages/element/src/form-button-group/index.ts b/packages/element/src/form-button-group/index.ts index 28b2a569aa4..78da84135b4 100644 --- a/packages/element/src/form-button-group/index.ts +++ b/packages/element/src/form-button-group/index.ts @@ -12,20 +12,19 @@ export type FormButtonGroupProps = Omit & { } export const FormButtonGroup = defineComponent({ - name: 'FormilyFormButtonGroup', + name: 'FormButtonGroup', props: { align: { type: String, default: 'left', }, - className: String, gutter: { type: Number, default: 8, }, alignFormItem: { type: Boolean, - default: true, + default: false, }, }, setup(props, { slots, attrs }) { @@ -40,11 +39,11 @@ export const FormButtonGroup = defineComponent({ padding: 0, width: '100%', }, - props: { + attrs: { colon: false, label: ' ', + ...attrs, }, - attrs, }, { default: () => h(Space, { props: { size: props.gutter } }, slots), @@ -54,10 +53,7 @@ export const FormButtonGroup = defineComponent({ return h( Space, { - class: { - [prefixCls]: true, - [`${props.className}`]: !!props.className, - }, + class: [prefixCls], style: { justifyContent: props.align === 'left' @@ -68,7 +64,7 @@ export const FormButtonGroup = defineComponent({ display: 'flex', }, props: { - ...props, + ...attrs, size: props.gutter, }, attrs, diff --git a/packages/element/src/form-grid/index.ts b/packages/element/src/form-grid/index.ts index 73ca704ba53..34e44644001 100644 --- a/packages/element/src/form-grid/index.ts +++ b/packages/element/src/form-grid/index.ts @@ -306,7 +306,7 @@ const useGridSpan = (gridSpan: number) => { } export const FormGrid = defineComponent({ - name: 'FormilyFormGrid', + name: 'FormGrid', props: { columnGap: { type: Number, @@ -416,6 +416,7 @@ export const FormGrid = defineComponent({ }) export const FormGridColumn = defineComponent({ + name: 'FormGridColumn', props: { gridSpan: { type: Number, diff --git a/packages/element/src/form-item/index.ts b/packages/element/src/form-item/index.ts index 18686735ce4..9b4bdf2596b 100644 --- a/packages/element/src/form-item/index.ts +++ b/packages/element/src/form-item/index.ts @@ -17,7 +17,7 @@ export type FormItemProps = { required?: boolean label?: string | Component colon?: boolean - tooltip?: boolean + tooltip?: string | Component labelStyle?: Record labelAlign?: 'left' | 'right' labelWrap?: boolean @@ -76,7 +76,7 @@ const ICON_MAP = { } export const FormBaseItem = defineComponent({ - name: 'FormilyFormItem', + name: 'FormItem', props: { className: {}, required: {}, @@ -194,7 +194,7 @@ export const FormBaseItem = defineComponent({ : slots.default?.() const renderLabel = - formLayout.label !== undefined && + formLayout.label && h( 'div', { @@ -243,10 +243,25 @@ export const FormBaseItem = defineComponent({ { props: { placement: 'top', - content: formLayout.tooltip, }, }, - { default: () => h('i', { class: 'el-icon-info' }, {}) } + { + default: () => [ + h('i', { class: 'el-icon-info' }, {}), + h( + 'div', + { + class: `${prefixCls}-label-tooltip-content`, + slot: 'content', + }, + { + default: () => [ + resolveComponent(formLayout.tooltip), + ], + } + ), + ], + } ), ], } diff --git a/packages/element/src/form-item/var.scss b/packages/element/src/form-item/var.scss index b2aab2e6a7f..3f3077d1e07 100644 --- a/packages/element/src/form-item/var.scss +++ b/packages/element/src/form-item/var.scss @@ -12,6 +12,6 @@ $--form-item-small-line-height: 24px; $--form-item-label-top-line-height: 40px; -$--form-error-line-height: 24px !default; +$--form-error-line-height: 22px !default; $--form-item-margin-bottom: $--form-error-line-height !default; diff --git a/packages/element/src/form-layout/index.ts b/packages/element/src/form-layout/index.ts index 7113e4c66ed..7c92e13089e 100644 --- a/packages/element/src/form-layout/index.ts +++ b/packages/element/src/form-layout/index.ts @@ -33,7 +33,7 @@ export const useFormLayout = () => inject(FormLayoutContext, null) export const useFormShallowLayout = () => inject(FormLayoutShallowContext, null) export const FormLayout = defineComponent({ - name: 'FormilyFormLayout', + name: 'FormLayout', props: { className: {}, colon: { default: true }, diff --git a/packages/element/src/form-step/index.ts b/packages/element/src/form-step/index.ts new file mode 100644 index 00000000000..7eebdf84346 --- /dev/null +++ b/packages/element/src/form-step/index.ts @@ -0,0 +1,195 @@ +import { defineComponent, PropType } from 'vue-demi' +import { action, model, observable } from '@formily/reactive' +import { VoidField, Form } from '@formily/core' +import { observer } from '@formily/reactive-vue' +import { + h, + useField, + useFieldSchema, + RecursionField, + Fragment, +} from '@formily/vue' +import { Schema, SchemaKey } from '@formily/json-schema' +import { Steps, Step } from 'element-ui' +import { stylePrefix } from '../__builtins__/configs' + +import type { Steps as StepsProps, Step as StepProps } from 'element-ui' + +export interface IFormStep { + connect: (steps: SchemaStep[], field: VoidField) => void + current: number + allowNext: boolean + allowBack: boolean + setCurrent(key: number): void + submit: Form['submit'] + next(): void + back(): void +} + +export interface IFormStepProps extends StepsProps { + formStep?: IFormStep +} + +type SchemaStep = { + name: SchemaKey + props: any + schema: Schema +} + +type FormStepEnv = { + form: Form + field: VoidField + steps: SchemaStep[] +} + +const parseSteps = (schema: Schema) => { + const steps: SchemaStep[] = [] + schema.mapProperties((schema, name) => { + if (schema['x-component']?.indexOf('StepPane') > -1) { + steps.push({ + name, + props: schema['x-component-props'], + schema, + }) + } + }) + return steps +} + +export const createFormStep = (defaultCurrent = 0): IFormStep => { + const env: FormStepEnv = observable({ + form: null, + field: null, + steps: [], + }) + + const setDisplay = action((target: number) => { + const currentStep = env.steps[target] + env.steps.forEach(({ name }) => { + env.form.query(`${env.field.address}.${name}`).take((field) => { + if (name === currentStep.name) { + field.setDisplay('visible') + } else { + field.setDisplay('hidden') + } + }) + }) + }) + + const next = action(() => { + if (formStep.allowNext) { + setDisplay(formStep.current + 1) + formStep.setCurrent(formStep.current + 1) + } + }) + + const back = action(() => { + if (formStep.allowBack) { + setDisplay(formStep.current - 1) + formStep.setCurrent(formStep.current - 1) + } + }) + + const formStep: IFormStep = model({ + connect(steps, field) { + env.steps = steps + env.form = field?.form + env.field = field + }, + current: defaultCurrent, + setCurrent(key: number) { + formStep.current = key + }, + get allowNext() { + return formStep.current < env.steps.length - 1 + }, + get allowBack() { + return formStep.current > 0 + }, + async next() { + try { + await env.form.validate() + next() + } catch {} + }, + async back() { + back() + }, + async submit(onSubmit) { + return env.form?.submit?.(onSubmit) + }, + }) + return formStep +} + +export const FormStep = observer( + defineComponent({ + name: 'FormStep', + props: { + formStep: { + type: Object as PropType, + default() { + return { + current: 0, + } + }, + }, + }, + setup(props, { attrs }) { + const field = useField().value + const prefixCls = `${stylePrefix}-form-step` + const fieldSchemaRef = useFieldSchema() + + const steps = parseSteps(fieldSchemaRef.value) + + props.formStep.connect?.(steps, field) + + return () => { + const current = props.active || props.formStep?.current || 0 + + const renderSteps = (steps: SchemaStep[], callback) => { + return steps.map(callback) + } + + return h( + 'div', + { + class: [prefixCls], + }, + { + default: () => [ + h( + Steps, + { + props: { + active: current, + }, + style: [{ marginBottom: '10px' }, attrs.style], + attrs, + }, + { + default: () => + renderSteps(steps, ({ props }, key) => { + return h(Step, { props, key }, {}) + }), + } + ), + + renderSteps(steps, ({ name, schema }, key) => { + if (key !== current) return + return h(RecursionField, { props: { name, schema }, key }, {}) + }), + ], + } + ) + } + }, + }) +) + +export const FormStepPane = defineComponent({ + name: 'FormStepPane', + setup(_props, { slots }) { + return () => h(Fragment, {}, slots) + }, +}) diff --git a/packages/element/src/form-step/style.ts b/packages/element/src/form-step/style.ts new file mode 100644 index 00000000000..f05fb114e8d --- /dev/null +++ b/packages/element/src/form-step/style.ts @@ -0,0 +1,2 @@ +import 'element-ui/packages/theme-chalk/src/steps.scss' +import 'element-ui/packages/theme-chalk/src/step.scss' diff --git a/packages/element/src/form-tab/index.ts b/packages/element/src/form-tab/index.ts new file mode 100644 index 00000000000..01e0868bafa --- /dev/null +++ b/packages/element/src/form-tab/index.ts @@ -0,0 +1,158 @@ +import { defineComponent, reactive, computed } from 'vue-demi' +import { observer } from '@formily/reactive-vue' +import { model } from '@formily/reactive' +import { + h, + useField, + useFieldSchema, + RecursionField, + Fragment, +} from '@formily/vue' +import { Schema, SchemaKey } from '@formily/json-schema' +import { Tabs, TabPane, Badge } from 'element-ui' +import { stylePrefix } from '../__builtins__/configs' + +import type { TabPane as TabPaneProps, Tabs as TabsProps } from 'element-ui' + +export interface IFormTab { + activeKey: string + setActiveKey(key: string): void +} + +export interface IFormTabProps extends TabsProps { + formTab?: IFormTab +} + +export interface IFormTabPaneProps extends TabPaneProps { + key: string | number +} + +export const useTabs = () => { + const tabsField = useField().value + const schema = useFieldSchema().value + const tabs: { name: SchemaKey; props: any; schema: Schema }[] = reactive([]) + schema.mapProperties((schema, name) => { + const field = tabsField.query(tabsField.address.concat(name)).take() + if (field?.display === 'none' || field?.display === 'hidden') return + if (schema['x-component']?.indexOf('TabPane') > -1) { + tabs.push({ + name, + props: { + name: schema?.['x-component-props']?.name || name, + ...schema?.['x-component-props'], + }, + schema, + }) + } + }) + return tabs +} + +export const createFormTab = (defaultActiveKey?: string) => { + const formTab = model({ + activeKey: defaultActiveKey, + setActiveKey(key: string) { + formTab.activeKey = key + }, + }) + return formTab +} + +export const FormTab = observer( + defineComponent({ + name: 'FormTab', + props: ['formTab'], + setup(props, { attrs, listeners }) { + const field = useField().value + const formTabRef = computed(() => props.formTab ?? createFormTab()) + + const prefixCls = `${stylePrefix}-form-tab` + + return () => { + const formTab = formTabRef.value + const tabs = useTabs() + const activeKey = props.value || formTab?.activeKey || tabs?.[0]?.name + const badgedTab = (key: SchemaKey, props: any) => { + const errors = field.form.queryFeedbacks({ + type: 'error', + address: `${field.address.concat(key)}.*`, + }) + if (errors.length) { + return () => + h( + Badge, + { + class: [`${prefixCls}-errors-badge`], + props: { + value: errors.length, + }, + }, + { default: () => props.label } + ) + } + return () => props.label + } + + const getTabs = (tabs) => { + return tabs.map(({ props, schema, name }, key) => { + return h( + TabPane, + { + key, + props, + }, + { + default: () => [ + h( + RecursionField, + { + props: { + schema, + name, + }, + }, + {} + ), + h( + 'div', + { slot: 'label' }, + { default: badgedTab(name, props) } + ), + ], + } + ) + }) + } + + return h( + Tabs, + { + class: [prefixCls], + style: attrs.style, + props: { + ...attrs, + value: activeKey, + }, + on: { + ...listeners, + input: (key) => { + listeners.input?.(key) + formTab.setActiveKey?.(key) + }, + }, + }, + { + default: () => getTabs(tabs), + } + ) + } + }, + }) +) + +export const FormTabPane = defineComponent({ + name: 'FormTabPane', + setup(_props, { slots }) { + return () => h(Fragment, {}, slots) + }, +}) diff --git a/packages/element/src/form-tab/style.scss b/packages/element/src/form-tab/style.scss new file mode 100644 index 00000000000..57c55941fa0 --- /dev/null +++ b/packages/element/src/form-tab/style.scss @@ -0,0 +1,5 @@ +@import '../__builtins__/styles/common.scss'; + +.#{$formily-prefix}-form-tab-errors-badge { + line-height: 1; +} diff --git a/packages/element/src/form-tab/style.ts b/packages/element/src/form-tab/style.ts new file mode 100644 index 00000000000..ecbdacde01c --- /dev/null +++ b/packages/element/src/form-tab/style.ts @@ -0,0 +1,4 @@ +import './style.scss' +import 'element-ui/packages/theme-chalk/src/tab.scss' +import 'element-ui/packages/theme-chalk/src/tab-pane.scss' +import 'element-ui/packages/theme-chalk/src/bdage.scss' diff --git a/packages/element/src/form/index.ts b/packages/element/src/form/index.ts index ed6262bd1e1..5665599e4c9 100644 --- a/packages/element/src/form/index.ts +++ b/packages/element/src/form/index.ts @@ -2,61 +2,81 @@ import { Form as FormType, IFormFeedback } from '@formily/core' import { FormProvider as _FormProvider, h } from '@formily/vue' import { defineComponent } from 'vue-demi' import { FormLayout, FormLayoutProps } from '../form-layout' - -import type { Component } from 'vue' +import { PreviewTextPlaceholder } from '../preview-text' +import { Component, VNode } from 'vue' const FormProvider = _FormProvider as unknown as Component -export type FormProps = FormLayoutProps & { +export interface FormProps extends FormLayoutProps { form: FormType component?: Component + previewTextPlaceholder: string | (() => VNode) onAutoSubmit?: (values: any) => any onAutoSubmitFailed?: (feedbacks: IFormFeedback[]) => void } -export const Form = defineComponent({ - setup(empty, { attrs, slots, listeners }) { - const { - form, - component = 'form', - onAutoSubmit = listeners?.autoSubmit, - onAutoSubmitFailed = listeners?.autoSubmitFailed, - ...props - } = attrs as FormProps - - return () => - h( +export const Form = defineComponent({ + name: 'Form', + props: [ + 'form', + 'component', + 'previewTextPlaceholder', + 'onAutoSubmit', + 'onAutoSubmitFailed', + ], + setup(props, { attrs, slots, listeners }) { + return () => { + const { + form, + component = 'form', + onAutoSubmit = listeners?.autoSubmit, + onAutoSubmitFailed = listeners?.autoSubmitFailed, + previewTextPlaceholder = slots?.previewTextPlaceholder, + } = props + return h( FormProvider, { props: { form } }, { - default: () => [ + default: () => h( - FormLayout, + PreviewTextPlaceholder, { - props, + props: { + value: previewTextPlaceholder, + }, }, { default: () => [ h( - component, + FormLayout, { - on: { - submit: (e: Event) => { - e?.stopPropagation?.() - e?.preventDefault?.() - form - .submit(onAutoSubmit as (e: any) => void) - .catch(onAutoSubmitFailed as (e: any) => void) - }, - }, + attrs, }, - slots + { + default: () => [ + h( + component, + { + on: { + submit: (e: Event) => { + e?.stopPropagation?.() + e?.preventDefault?.() + form + .submit(onAutoSubmit as (e: any) => void) + .catch(onAutoSubmitFailed as (e: any) => void) + }, + }, + }, + slots + ), + ], + } ), ], } ), - ], } ) + } }, }) diff --git a/packages/element/src/index.ts b/packages/element/src/index.ts index 22791cb8743..9f9bcbf6859 100644 --- a/packages/element/src/index.ts +++ b/packages/element/src/index.ts @@ -25,3 +25,11 @@ export * from './switch' export * from './time-picker' export * from './transfer' export * from './upload' +export * from './preview-text' +export * from './form-tab' +export * from './form-step' +export * from './array-cards' +export * from './array-collapse' +export * from './array-items' +export * from './array-tabs' +export * from './editable' diff --git a/packages/element/src/input-number/index.ts b/packages/element/src/input-number/index.ts index b4da7c95d32..64f57ef5b6c 100644 --- a/packages/element/src/input-number/index.ts +++ b/packages/element/src/input-number/index.ts @@ -1,8 +1,9 @@ import { getComponentByTag } from '../__builtins__/shared' -import { connect, mapProps } from '@formily/vue' +import { connect, mapProps, mapReadPretty } from '@formily/vue' import type { InputNumber as _ElInputNumberProps } from 'element-ui' import { InputNumber as ElInputNumber } from 'element-ui' +import { PreviewInputText } from '../preview-text' export type InputNumberProps = _ElInputNumberProps @@ -23,5 +24,6 @@ export const InputNumber = connect( return { controlsPosition, } - }) + }), + mapReadPretty(PreviewInputText) ) diff --git a/packages/element/src/input/index.ts b/packages/element/src/input/index.ts index 30b910b91e6..577343d0742 100644 --- a/packages/element/src/input/index.ts +++ b/packages/element/src/input/index.ts @@ -1,6 +1,6 @@ import { getComponentByTag } from '../__builtins__/shared' -import { connect, mapProps } from '@formily/vue' - +import { connect, mapProps, mapReadPretty } from '@formily/vue' +import { PreviewInputText } from '../preview-text' import type { Input as ElInputProps } from 'element-ui' import { Input as ElInput } from 'element-ui' @@ -12,15 +12,17 @@ const TransformElInput = getComponentByTag(ElInput, { export const Input = connect( TransformElInput, - mapProps({ readOnly: 'readonly' }) + mapProps({ readOnly: 'readonly' }), + mapReadPretty(PreviewInputText) ) -export const Textarea = connect( +export const TextArea = connect( Input, mapProps((props) => { return { ...props, type: 'textarea', } - }) + }), + mapReadPretty(PreviewInputText) ) diff --git a/packages/element/src/password/index.ts b/packages/element/src/password/index.ts index 7052ed45975..428121407d8 100644 --- a/packages/element/src/password/index.ts +++ b/packages/element/src/password/index.ts @@ -1,6 +1,6 @@ import { Input } from '../input' -import { connect, mapProps } from '@formily/vue' - +import { connect, mapProps, mapReadPretty } from '@formily/vue' +import { PreviewInputText } from '../preview-text' import type { Input as ElInputProps } from 'element-ui' export type PasswordProps = ElInputProps @@ -12,5 +12,6 @@ export const Password = connect( ...props, showPassword: true, } - }) + }), + mapReadPretty(PreviewInputText) ) diff --git a/packages/element/src/preview-text/index.ts b/packages/element/src/preview-text/index.ts new file mode 100644 index 00000000000..eaf48bf2ad5 --- /dev/null +++ b/packages/element/src/preview-text/index.ts @@ -0,0 +1,298 @@ +import { defineComponent, computed } from 'vue-demi' +import { + createContext, + resolveComponent, + useContext, +} from '../__builtins__/shared' +import { Field } from '@formily/core' +import { observer } from '@formily/reactive-vue' +import { h, useField } from '@formily/vue' +import { isArr, isFn, isValid } from '@formily/shared' +import { stylePrefix } from '../__builtins__/configs' +import { InputProps } from '../input' +import type { SelectProps } from '../select' +import type { CascaderProps } from '../cascader' +import type { DatePickerProps } from '../date-picker' +import type { TimePickerProps } from '../time-picker' +import { Space } from '../space' +import { Tag } from 'element-ui' +import { formatDate } from 'element-ui/src/utils/date-util' + +const prefixCls = `${stylePrefix}-preview-text` +const PlaceholderContext = createContext('N/A') + +export const usePlaceholder = (value?: any) => { + const placeholderCtx = useContext(PlaceholderContext) + const placeholder = computed(() => { + return isValid(value) && value !== '' + ? value + : resolveComponent(placeholderCtx.value) || 'N/A' + }) + + return placeholder +} + +export const PreviewInputText = defineComponent({ + name: 'PreviewInputText', + props: [], + setup(_props, { attrs, slots }) { + const placeholder = usePlaceholder(attrs.value) + return () => { + return h( + Space, + { + class: [prefixCls], + style: attrs.style, + }, + { + default: () => [ + slots?.prepend?.(), + slots?.prefix?.(), + placeholder.value, + slots?.suffix?.(), + slots?.append?.(), + ], + } + ) + } + }, +}) + +export const PreviewSelectText = observer( + defineComponent({ + name: 'PreviewSelectText', + props: [], + setup(_props, { attrs }) { + const fieldRef = useField() + const field = fieldRef.value + const props = attrs as unknown as SelectProps + const dataSource: any[] = field?.dataSource?.length + ? field.dataSource + : props?.options?.length + ? props.options + : [] + const placeholder = usePlaceholder() + const getSelected = () => { + const value = props.value + if (props.multiple) { + return isArr(value) + ? value.map((val) => ({ label: val, value: val })) + : [] + } else { + return isValid(value) ? [{ label: value, value }] : [] + } + } + + const getLabels = () => { + const selected = getSelected() + if (!selected.length) { + return h( + Tag, + {}, + { + default: () => placeholder.value, + } + ) + } + return selected.map(({ value, label }, key) => { + const text = + dataSource?.find((item) => item.value == value)?.label || label + return h( + Tag, + { + key, + props: { + type: 'info', + effect: 'light', + }, + }, + { + default: () => text || placeholder.value, + } + ) + }) + } + + return () => { + return h( + Space, + { + class: [prefixCls], + style: attrs.style, + }, + { + default: () => getLabels(), + } + ) + } + }, + }) +) + +export const PreviewCascaderText = observer( + defineComponent({ + name: 'PreviewCascaderText', + props: [], + setup(_props, { attrs }) { + const fieldRef = useField() + const field = fieldRef.value + const props = attrs as unknown as CascaderProps + const dataSource: any[] = field?.dataSource?.length + ? field.dataSource + : props?.options?.length + ? props.options + : [] + const placeholder = usePlaceholder() + const valueKey = props.props?.value || 'value' + const labelKey = props.props?.label || 'label' + const getSelected = () => { + return isArr(props.value) ? props.value : [] + } + + const findLabel = (value: any, dataSource: any[]) => { + for (let i = 0; i < dataSource?.length; i++) { + const item = dataSource[i] + if (item?.[valueKey] === value) { + return item?.[labelKey] + } else { + const childLabel = findLabel(value, item?.children) + if (childLabel) return childLabel + } + } + } + + const getLabels = () => { + const selected = getSelected() + if (!selected?.length) { + return h( + Tag, + {}, + { + default: () => placeholder.value, + } + ) + } + return selected.map((value, key) => { + const text = findLabel(value, dataSource) + return h( + Tag, + { + key, + props: { + type: 'info', + effect: 'light', + }, + }, + { + default: () => text || placeholder.value, + } + ) + }) + } + + return () => { + return h( + Space, + { + class: [prefixCls], + style: attrs.style, + }, + { + default: () => getLabels(), + } + ) + } + }, + }) +) + +export const PreviewDatePickerText = defineComponent({ + name: 'PreviewDatePickerText', + props: [], + setup(_props, { attrs }) { + const props = attrs as unknown as DatePickerProps + const placeholder = usePlaceholder() + const getLabels = () => { + if (isArr(props.value)) { + const labels = (props.value as any[]).map( + (value: String | Date) => + formatDate(value, props.format) || placeholder.value + ) + + return labels.join('~') + } else { + return formatDate(props.value, props.format) || placeholder.value + } + } + + return () => { + return h( + 'div', + { + class: [prefixCls], + style: attrs.style, + }, + { + default: () => getLabels(), + } + ) + } + }, +}) + +export const PreviewTimePickerText = defineComponent({ + name: 'PreviewTimePickerText', + props: [], + setup(_props, { attrs }) { + const props = attrs as unknown as TimePickerProps + const format = props.pickerOptions?.format || 'HH:mm:ss' + const placeholder = usePlaceholder() + const getLabels = () => { + if (isArr(props.value)) { + const labels = props.value.map( + (value) => formatDate(value, format) || placeholder.value + ) + + return labels.join('~') + } else { + return formatDate(props.value, format) || placeholder.value + } + } + + return () => { + return h( + 'div', + { + class: [prefixCls], + style: attrs.style, + }, + { + default: () => getLabels(), + } + ) + } + }, +}) + +export const PreviewText = defineComponent({ + name: 'PreviewText', + setup(_props, { attrs }) { + const placeholder = usePlaceholder() + + return () => { + return h( + 'div', + { + class: [prefixCls], + style: attrs.style, + }, + { + default: () => placeholder.value, + } + ) + } + }, +}) + +export const PreviewTextPlaceholder = PlaceholderContext.Provider +export const usePreviewTextPlaceholder = usePlaceholder diff --git a/packages/element/src/preview-text/style.ts b/packages/element/src/preview-text/style.ts new file mode 100644 index 00000000000..0629d0e6bc0 --- /dev/null +++ b/packages/element/src/preview-text/style.ts @@ -0,0 +1 @@ +import 'element-ui/packages/theme-chalk/src/tag.scss' diff --git a/packages/element/src/radio/index.ts b/packages/element/src/radio/index.ts index fd5cbf8f90a..5f0dba0fe01 100644 --- a/packages/element/src/radio/index.ts +++ b/packages/element/src/radio/index.ts @@ -1,7 +1,7 @@ -import { connect, mapProps, h } from '@formily/vue' +import { connect, mapProps, h, mapReadPretty } from '@formily/vue' import { defineComponent } from 'vue-demi' import { getComponentByTag } from '../__builtins__/shared' - +import { PreviewSelectText } from '../preview-text' import type { Radio as ElRadioProps, RadioGroup as ElRadioGroupProps, @@ -23,6 +23,7 @@ const TransformElRadioGroup = getComponentByTag(ElRadioGroup, { }) const RadioGroupOption = defineComponent({ + name: 'RadioGroup', props: { options: { type: Array, @@ -75,6 +76,7 @@ const RadioGroupOption = defineComponent({ export const RadioGroup = connect( RadioGroupOption, - mapProps({ dataSource: 'options' }) + mapProps({ dataSource: 'options' }), + mapReadPretty(PreviewSelectText) ) export const Radio = ElRadio diff --git a/packages/element/src/reset/index.ts b/packages/element/src/reset/index.ts index 02acabec31c..402c0136efb 100644 --- a/packages/element/src/reset/index.ts +++ b/packages/element/src/reset/index.ts @@ -10,6 +10,7 @@ export type ResetProps = IFieldResetOptions & IElButton export const Reset = observer( defineComponent({ + name: 'Reset', props: { forceClear: { type: Boolean, diff --git a/packages/element/src/select/index.ts b/packages/element/src/select/index.ts index ccc21b9fae3..d92f3cd0101 100644 --- a/packages/element/src/select/index.ts +++ b/packages/element/src/select/index.ts @@ -1,5 +1,6 @@ -import { connect, mapProps, h } from '@formily/vue' +import { connect, mapProps, h, mapReadPretty } from '@formily/vue' import { defineComponent } from 'vue-demi' +import { PreviewSelectText } from '../preview-text' import type { Select as ElSelectProps, @@ -12,6 +13,7 @@ export type SelectProps = ElSelectProps & { } const SelectOption = defineComponent({ + name: 'Select', props: ['options'], setup(customProps, { attrs, slots, listeners }) { return () => { @@ -49,5 +51,6 @@ const SelectOption = defineComponent({ export const Select = connect( SelectOption, - mapProps({ dataSource: 'options', loading: true }) + mapProps({ dataSource: 'options', loading: true }), + mapReadPretty(PreviewSelectText) ) diff --git a/packages/element/src/space/index.ts b/packages/element/src/space/index.ts index fb01cbbc5ee..bf1d46e477c 100644 --- a/packages/element/src/space/index.ts +++ b/packages/element/src/space/index.ts @@ -19,7 +19,7 @@ const spaceSize = { } export const Space = defineComponent({ - name: 'ElSpace', + name: 'Space', props: ['size', 'direction', 'align'], setup(props, { slots }) { return () => { diff --git a/packages/element/src/style.ts b/packages/element/src/style.ts index 7d20eb03d0e..94d7e0ffa11 100644 --- a/packages/element/src/style.ts +++ b/packages/element/src/style.ts @@ -1,9 +1,15 @@ // auto generated code import './array-base/style.scss' +import './array-cards/style.scss' +import './array-collapse/style.scss' +import './array-items/style.scss' import './array-table/style.scss' +import './array-tabs/style.scss' +import './editable/style.scss' import './form-button-group/style.scss' import './form-grid/style.scss' import './form-item/style.scss' import './form-layout/style.scss' +import './form-tab/style.scss' import './form/style.scss' import './space/style.scss' diff --git a/packages/element/src/submit/index.ts b/packages/element/src/submit/index.ts index f499fa05172..47778551339 100644 --- a/packages/element/src/submit/index.ts +++ b/packages/element/src/submit/index.ts @@ -1,20 +1,33 @@ import { h, useForm } from '@formily/vue' +import { IFormFeedback } from '@formily/core' import { observer } from '@formily/reactive-vue' import { defineComponent } from 'vue-demi' import type { Button as ElButtonProps } from 'element-ui' import { Button as ElButton } from 'element-ui' -export type SubmitProps = ElButtonProps +export interface ISubmitProps extends ElButtonProps { + onClick?: (e: MouseEvent) => any + onSubmit?: (values: any) => any + onSubmitSuccess?: (payload: any) => void + onSubmitFailed?: (feedbacks: IFormFeedback[]) => void +} export const Submit = observer( - defineComponent({ - setup(props, context) { + defineComponent({ + name: 'Submit', + props: ['onClick', 'onSubmit', 'onSubmitSuccess', 'onSubmitFailed'], + setup(props, { attrs, slots, listeners }) { const formRef = useForm() - const { listeners, slots, attrs } = context - return () => { + const { + onClick = listeners?.click, + onSubmit = listeners?.submit, + onSubmitSuccess = listeners?.submitSuccess, + onSubmitFailed = listeners?.submitFailed, + } = props + const form = formRef?.value return h( ElButton, @@ -29,14 +42,14 @@ export const Submit = observer( on: { ...listeners, click: (e: any) => { - if (listeners?.click) { - if (listeners.click(e) === false) return + if (onClick) { + if (onClick(e) === false) return } - if (listeners?.submit) { + if (onSubmit) { form - ?.submit(listeners.submit as (e: any) => void) - .then(listeners.submitSuccess as (e: any) => void) - .catch(listeners.submitFailed as (e: any) => void) + ?.submit(onSubmit as (e: any) => void) + .then(onSubmitSuccess as (e: any) => void) + .catch(onSubmitFailed as (e: any) => void) } }, }, diff --git a/packages/element/src/time-picker/index.ts b/packages/element/src/time-picker/index.ts index 95a7171e1a3..ce71a8dead0 100644 --- a/packages/element/src/time-picker/index.ts +++ b/packages/element/src/time-picker/index.ts @@ -1,6 +1,6 @@ import { getComponentByTag } from '../__builtins__/shared' -import { connect, mapProps } from '@formily/vue' - +import { connect, mapProps, mapReadPretty } from '@formily/vue' +import { PreviewTimePickerText } from '../preview-text' import type { TimePicker as ElTimePickerProps } from 'element-ui' import { TimePicker as ElTimePicker } from 'element-ui' @@ -12,5 +12,6 @@ const TransformElTimePicker = getComponentByTag(ElTimePicker, { export const TimePicker = connect( TransformElTimePicker, - mapProps({ readOnly: 'readonly' }) + mapProps({ readOnly: 'readonly' }), + mapReadPretty(PreviewTimePickerText) ) diff --git a/packages/element/src/upload/index.ts b/packages/element/src/upload/index.ts index 3b19aa70342..fd290e0c51d 100644 --- a/packages/element/src/upload/index.ts +++ b/packages/element/src/upload/index.ts @@ -15,6 +15,7 @@ export type UploadProps = ElUploadProps & { } const UploadWrapper = defineComponent({ + name: 'Upload', props: { textContent: { type: String, diff --git a/packages/vue/src/shared/connect.ts b/packages/vue/src/shared/connect.ts index e53590a772b..711068ee422 100644 --- a/packages/vue/src/shared/connect.ts +++ b/packages/vue/src/shared/connect.ts @@ -69,7 +69,8 @@ export function mapProps( } export function mapReadPretty( - component: C + component: C, + readPrettyProps?: Record ) { return (target: T) => { return observer( @@ -84,6 +85,7 @@ export function mapReadPretty( : target, { attrs: { + ...readPrettyProps, ...attrs, }, on: listeners, diff --git a/yarn.lock b/yarn.lock index f8be7740884..5fb57e971f4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18876,6 +18876,11 @@ vue-server-renderer@^2.6.10: serialize-javascript "^3.1.0" source-map "0.5.6" +vue-slicksort@^1.2.0: + version "1.2.0" + resolved "https://registry.nlark.com/vue-slicksort/download/vue-slicksort-1.2.0.tgz#2bc1da2facc4350279ebc3866acea6ee5fe681ef" + integrity sha1-K8HaL6zENQJ568OGas6m7l/mge8= + vue-style-loader@^4.1.0: version "4.1.3" resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-4.1.3.tgz#6d55863a51fa757ab24e89d9371465072aa7bc35"