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"
]