diff --git a/packages/element/docs/demos/guide/form-collapse/json-schema.vue b/packages/element/docs/demos/guide/form-collapse/json-schema.vue
new file mode 100644
index 00000000000..c49463ca9ba
--- /dev/null
+++ b/packages/element/docs/demos/guide/form-collapse/json-schema.vue
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
+ 提交
+
+
+
+
+
+
+
+
diff --git a/packages/element/docs/demos/guide/form-collapse/markup-schema.vue b/packages/element/docs/demos/guide/form-collapse/markup-schema.vue
new file mode 100644
index 00000000000..5454a3bfe9e
--- /dev/null
+++ b/packages/element/docs/demos/guide/form-collapse/markup-schema.vue
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
diff --git a/packages/element/docs/guide/form-collapse.md b/packages/element/docs/guide/form-collapse.md
new file mode 100644
index 00000000000..e4e837f8587
--- /dev/null
+++ b/packages/element/docs/guide/form-collapse.md
@@ -0,0 +1,53 @@
+# FormCollapse
+
+> 折叠面板,通常用在布局空间要求较高的表单场景
+>
+> 注意:只能用在 Schema 场景
+
+## Markup Schema 案例
+
+
+
+## JSON Schema 案例
+
+
+
+## API
+
+### FormCollapse
+
+| 属性名 | 类型 | 描述 | 默认值 |
+| ------------ | ------------- | ---------------------------------------------------------- | ------ |
+| formCollapse | IFormCollapse | 传入通过 createFormCollapse/useFormCollapse 创建出来的模型 | |
+
+其余参考 [https://element.eleme.io/#/zh-CN/component/collapse](https://element.eleme.io/#/zh-CN/component/collapse)
+
+### FormCollapse.Item
+
+参考 [https://element.eleme.io/#/zh-CN/component/collapse](https://element.eleme.io/#/zh-CN/component/collapse)
+
+### FormCollapse.createFormCollapse
+
+```ts pure
+type ActiveKey = string | number
+type ActiveKeys = string | number | Array
+
+interface createFormCollapse {
+ (defaultActiveKeys?: ActiveKeys): IFormCollpase
+}
+
+interface IFormCollapse {
+ //激活主键列表
+ activeKeys: ActiveKeys
+ //是否存在该激活主键
+ hasActiveKey(key: ActiveKey): boolean
+ //设置激活主键列表
+ setActiveKeys(keys: ActiveKeys): void
+ //添加激活主键
+ addActiveKey(key: ActiveKey): void
+ //删除激活主键
+ removeActiveKey(key: ActiveKey): void
+ //开关切换激活主键
+ toggleActiveKey(key: ActiveKey): void
+}
+```
diff --git a/packages/element/src/__builtins__/index.ts b/packages/element/src/__builtins__/index.ts
new file mode 100644
index 00000000000..8bf1dec9f69
--- /dev/null
+++ b/packages/element/src/__builtins__/index.ts
@@ -0,0 +1,2 @@
+export * from './configs'
+export * from './shared'
diff --git a/packages/element/src/array-base/index.ts b/packages/element/src/array-base/index.ts
index 0f174c0b6ad..305209003cc 100644
--- a/packages/element/src/array-base/index.ts
+++ b/packages/element/src/array-base/index.ts
@@ -201,10 +201,12 @@ const ArrayBaseIndex = defineComponent({
name: 'ArrayBaseIndex',
setup(props, { attrs }) {
const index = useIndex()
+ const prefixCls = `${stylePrefix}-array-base`
return () => {
return h(
'span',
{
+ class: `${prefixCls}-index`,
attrs,
},
{
diff --git a/packages/element/src/array-collapse/style.scss b/packages/element/src/array-collapse/style.scss
index bd46b6a6a0f..6bdd5b35c30 100644
--- a/packages/element/src/array-collapse/style.scss
+++ b/packages/element/src/array-collapse/style.scss
@@ -17,6 +17,7 @@ $array-table-prefix-cls: '#{$formily-prefix}-array-collapse';
.#{$array-table-prefix-cls}-errors-badge {
line-height: 1;
+ vertical-align: initial;
}
.#{$formily-prefix}-array-base-addition {
diff --git a/packages/element/src/array-table/index.ts b/packages/element/src/array-table/index.ts
index dead4f33ea5..510b4a5b2bc 100644
--- a/packages/element/src/array-table/index.ts
+++ b/packages/element/src/array-table/index.ts
@@ -230,7 +230,7 @@ const StatusSelect = observer(
options: Array,
pageSize: Number,
},
- setup(props, { attrs }) {
+ setup(props) {
const formRef = useForm()
const fieldRef = useField()
const prefixCls = `${stylePrefix}-array-table`
diff --git a/packages/element/src/array-tabs/style.scss b/packages/element/src/array-tabs/style.scss
index 97efae5fe2c..41162cc285e 100644
--- a/packages/element/src/array-tabs/style.scss
+++ b/packages/element/src/array-tabs/style.scss
@@ -11,5 +11,6 @@ $array-table-prefix-cls: '#{$formily-prefix}-array-tabs';
.#{$array-table-prefix-cls}-errors-badge {
line-height: 1;
+ vertical-align: initial;
}
}
diff --git a/packages/element/src/form-collapse/index.ts b/packages/element/src/form-collapse/index.ts
new file mode 100644
index 00000000000..240bf08df82
--- /dev/null
+++ b/packages/element/src/form-collapse/index.ts
@@ -0,0 +1,207 @@
+import { Collapse, CollapseItem, Badge } from 'element-ui'
+import { model } from '@formily/reactive'
+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 { Schema, SchemaKey } from '@formily/json-schema'
+import { composeExport, stylePrefix } from '../__builtins__'
+import { toArr } from '@formily/shared'
+import { computed, defineComponent, PropType } from 'vue-demi'
+import { GeneralField } from '@formily/core'
+
+type ActiveKeys = string | number | Array
+
+type ActiveKey = string | number
+
+type Panels = { name: SchemaKey; props: any; schema: Schema }[]
+
+export interface IFormCollapse {
+ activeKeys: ActiveKeys
+ hasActiveKey(key: ActiveKey): boolean
+ setActiveKeys(key: ActiveKeys): void
+ addActiveKey(key: ActiveKey): void
+ removeActiveKey(key: ActiveKey): void
+ toggleActiveKey(key: ActiveKey): void
+}
+
+export interface IFormCollapseProps extends CollapseProps {
+ formCollapse?: IFormCollapse
+ activeKey?: ActiveKey
+}
+
+const usePanels = (collapseField: GeneralField, schema: Schema) => {
+ const panels: Panels = []
+ schema.mapProperties((schema, name) => {
+ const field = collapseField.query(collapseField.address.concat(name)).take()
+ if (field?.display === 'none' || field?.display === 'hidden') return
+ if (schema['x-component']?.indexOf('FormCollapse.Item') > -1) {
+ panels.push({
+ name,
+ props: {
+ ...schema?.['x-component-props'],
+ key: schema?.['x-component-props']?.key || name,
+ },
+ schema,
+ })
+ }
+ })
+ return panels
+}
+
+const createFormCollapse = (defaultActiveKeys?: ActiveKeys) => {
+ const formCollapse = model({
+ activeKeys: defaultActiveKeys,
+ setActiveKeys(keys: ActiveKeys) {
+ formCollapse.activeKeys = keys
+ },
+ hasActiveKey(key: ActiveKey) {
+ if (Array.isArray(formCollapse.activeKeys)) {
+ if (formCollapse.activeKeys.includes(key)) {
+ return true
+ }
+ } else if (formCollapse.activeKeys == key) {
+ return true
+ }
+ return false
+ },
+ addActiveKey(key: ActiveKey) {
+ if (formCollapse.hasActiveKey(key)) return
+ formCollapse.activeKeys = toArr(formCollapse.activeKeys).concat(key)
+ },
+ removeActiveKey(key: ActiveKey) {
+ if (Array.isArray(formCollapse.activeKeys)) {
+ formCollapse.activeKeys = formCollapse.activeKeys.filter(
+ (item) => item != key
+ )
+ } else {
+ formCollapse.activeKeys = ''
+ }
+ },
+ toggleActiveKey(key: ActiveKey) {
+ if (formCollapse.hasActiveKey(key)) {
+ formCollapse.removeActiveKey(key)
+ } else {
+ formCollapse.addActiveKey(key)
+ }
+ },
+ })
+ return formCollapse
+}
+
+const FormCollapse = observer(
+ defineComponent({
+ inheritAttrs: false,
+ props: {
+ formCollapse: { type: Object as PropType },
+ activeKey: {
+ type: [String, Number],
+ },
+ },
+ setup(props, { attrs, emit }) {
+ const field = useField()
+ const schema = useFieldSchema()
+ const prefixCls = `${stylePrefix}-form-collapse`
+ const _formCollapse = computed(
+ () => props.formCollapse ?? createFormCollapse()
+ )
+
+ const takeActiveKeys = (panels: Panels) => {
+ if (props.activeKey) return props.activeKey
+ if (_formCollapse.value?.activeKeys)
+ return _formCollapse.value?.activeKeys
+ if (attrs.accordion) return panels[0]?.name
+ return panels.map((item) => item.name)
+ }
+
+ const badgedHeader = (key: SchemaKey, props: any) => {
+ const errors = field.value.form.queryFeedbacks({
+ type: 'error',
+ address: `${field.value.address.concat(key)}.*`,
+ })
+ if (errors.length) {
+ return h(
+ Badge,
+ {
+ class: [`${prefixCls}-errors-badge`],
+ props: {
+ value: errors.length,
+ },
+ },
+ { default: () => props.title }
+ )
+ }
+ return props.title
+ }
+
+ return () => {
+ const panels = usePanels(field.value, schema.value)
+ const activeKey = takeActiveKeys(panels)
+ return h(
+ Collapse,
+ {
+ class: prefixCls,
+ props: {
+ value: activeKey,
+ },
+ on: {
+ change: (key: string | string[]) => {
+ emit('input', key)
+ _formCollapse.value.setActiveKeys(key)
+ },
+ },
+ },
+ {
+ default: () => {
+ return panels.map(({ props, schema, name }, index) => {
+ return h(
+ CollapseItem,
+ {
+ key: index,
+ props: {
+ ...props,
+ name,
+ },
+ },
+ {
+ default: () => [
+ h(RecursionField, { props: { schema, name } }, {}),
+ h(
+ 'span',
+ { slot: 'title' },
+ { default: () => badgedHeader(name, props) }
+ ),
+ ],
+ }
+ )
+ })
+ },
+ }
+ )
+ }
+ },
+ })
+)
+
+export const FormCollapseItem = defineComponent({
+ name: 'FFormCollapseItem',
+ setup(_props, { slots }) {
+ return () => h(Fragment, {}, slots)
+ },
+})
+
+const composeFormCollapse = composeExport(FormCollapse, {
+ Item: FormCollapseItem,
+ createFormCollapse,
+})
+
+export { composeFormCollapse as FormCollapse }
+export default composeFormCollapse
diff --git a/packages/element/src/form-collapse/style.scss b/packages/element/src/form-collapse/style.scss
new file mode 100644
index 00000000000..e9368b76bf8
--- /dev/null
+++ b/packages/element/src/form-collapse/style.scss
@@ -0,0 +1,6 @@
+@import '../__builtins__/styles/common.scss';
+
+.#{$formily-prefix}-form-collapse-errors-badge {
+ line-height: 1;
+ vertical-align: initial;
+}
diff --git a/packages/element/src/form-collapse/style.ts b/packages/element/src/form-collapse/style.ts
new file mode 100644
index 00000000000..5835bf18c76
--- /dev/null
+++ b/packages/element/src/form-collapse/style.ts
@@ -0,0 +1,4 @@
+import './style.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/bdage.scss'
diff --git a/packages/element/src/form-dialog/index.ts b/packages/element/src/form-dialog/index.ts
index 44518e5defa..42745ef31a8 100644
--- a/packages/element/src/form-dialog/index.ts
+++ b/packages/element/src/form-dialog/index.ts
@@ -171,7 +171,6 @@ export function FormDialog(
cancelText,
okButtonProps,
cancelButtonProps,
- loadingText,
...dialogProps
} = this.dialogProps
diff --git a/packages/element/src/form-item/index.ts b/packages/element/src/form-item/index.ts
index 98b7f7e3823..d717ec4b652 100644
--- a/packages/element/src/form-item/index.ts
+++ b/packages/element/src/form-item/index.ts
@@ -5,10 +5,11 @@ import {
Ref,
onBeforeUnmount,
watch,
+ provide,
} from '@vue/composition-api'
import { isVoidField } from '@formily/core'
import { connect, mapProps, h } from '@formily/vue'
-import { useFormLayout } from '../form-layout'
+import { useFormLayout, FormLayoutShallowContext } from '../form-layout'
import { composeExport, resolveComponent } from '../__builtins__/shared'
import { stylePrefix } from '../__builtins__/configs'
import { Component } from 'vue'
@@ -137,7 +138,7 @@ export const FormBaseItem = defineComponent({
},
setup(props, { slots, attrs, refs }) {
const active = ref(false)
- const deepLayout = useFormLayout()
+ const deepLayoutRef = useFormLayout()
const prefixCls = `${stylePrefix}-form-item`
@@ -148,7 +149,10 @@ export const FormBaseItem = defineComponent({
containerRef.value = refs.labelContainer
})
+ provide(FormLayoutShallowContext, ref(null))
+
return () => {
+ const deepLayout = deepLayoutRef.value
const {
label,
colon = deepLayout.colon ?? true,
diff --git a/packages/element/src/form-layout/index.ts b/packages/element/src/form-layout/index.ts
index 575b3735f79..86d8f64cf6c 100644
--- a/packages/element/src/form-layout/index.ts
+++ b/packages/element/src/form-layout/index.ts
@@ -3,6 +3,10 @@ import {
inject,
InjectionKey,
defineComponent,
+ Ref,
+ ref,
+ watch,
+ computed,
} from '@vue/composition-api'
import { h } from '@formily/vue'
import { stylePrefix } from '../__builtins__/configs'
@@ -29,22 +33,24 @@ export type FormLayoutProps = {
inset?: boolean
}
-export const FormLayoutDeepContext: InjectionKey = Symbol(
+export const FormLayoutDeepContext: InjectionKey[> = Symbol(
'FormLayoutDeepContext'
)
-export const FormLayoutShallowContext: InjectionKey = Symbol(
- 'FormLayoutShallowContext'
-)
+export const FormLayoutShallowContext: InjectionKey][> =
+ Symbol('FormLayoutShallowContext')
-export const useFormDeepLayout = () => inject(FormLayoutDeepContext, null)
+export const useFormDeepLayout = () => inject(FormLayoutDeepContext, ref(null))
-export const useFormShallowLayout = () => inject(FormLayoutShallowContext, null)
+export const useFormShallowLayout = () =>
+ inject(FormLayoutShallowContext, ref(null))
-export const useFormLayout = () => ({
- ...useFormDeepLayout(),
- ...useFormShallowLayout(),
-})
+export const useFormLayout = () => {
+ return ref({
+ ...useFormDeepLayout().value,
+ ...useFormShallowLayout().value,
+ })
+}
export const FormLayout = defineComponent({
name: 'FFormLayout',
@@ -69,25 +75,32 @@ export const FormLayout = defineComponent({
bordered: { default: true },
inset: { default: false },
},
- setup(props, { slots, attrs }) {
+ setup(props, { slots }) {
const deepLayout = useFormDeepLayout()
-
- const newDeepLayout = {
+ const newDeepLayout = ref({
...deepLayout,
- }
- if (!props.shallow) {
- Object.assign(newDeepLayout, props)
- } else {
- if (props.size) {
- newDeepLayout.size = props.size
- }
- if (props.colon) {
- newDeepLayout.colon = props.colon
- }
- }
+ })
+ const shallowProps = computed(() => (props.shallow ? props : undefined))
+
+ watch(
+ [props, deepLayout],
+ () => {
+ if (!props.shallow) {
+ Object.assign(newDeepLayout.value, props)
+ } else {
+ if (props.size) {
+ newDeepLayout.value.size = props.size
+ }
+ if (props.colon) {
+ newDeepLayout.value.colon = props.colon
+ }
+ }
+ },
+ { deep: true, immediate: true }
+ )
provide(FormLayoutDeepContext, newDeepLayout)
- provide(FormLayoutShallowContext, props.shallow ? props : undefined)
+ provide(FormLayoutShallowContext, shallowProps)
const formPrefixCls = `${stylePrefix}-form`
return () => {
diff --git a/packages/element/src/form-tab/style.scss b/packages/element/src/form-tab/style.scss
index 57c55941fa0..c3c93cfe37e 100644
--- a/packages/element/src/form-tab/style.scss
+++ b/packages/element/src/form-tab/style.scss
@@ -2,4 +2,5 @@
.#{$formily-prefix}-form-tab-errors-badge {
line-height: 1;
+ vertical-align: initial;
}
diff --git a/packages/element/src/index.ts b/packages/element/src/index.ts
index 3a14eef44be..46d20acad2a 100644
--- a/packages/element/src/index.ts
+++ b/packages/element/src/index.ts
@@ -25,6 +25,7 @@ export * from './time-picker'
export * from './transfer'
export * from './upload'
export * from './preview-text'
+export * from './form-collapse'
export * from './form-tab'
export * from './form-step'
export * from './array-cards'
diff --git a/packages/element/src/style.ts b/packages/element/src/style.ts
index 98b3625453c..ee6d76db179 100644
--- a/packages/element/src/style.ts
+++ b/packages/element/src/style.ts
@@ -14,3 +14,4 @@ import './form-layout/style.scss'
import './form-tab/style.scss'
import './form/style.scss'
import './space/style.scss'
+import './form-collapse/style.scss'
diff --git a/packages/element/transformer.ts b/packages/element/transformer.ts
index ab8a563c497..45d5e129981 100644
--- a/packages/element/transformer.ts
+++ b/packages/element/transformer.ts
@@ -1,4 +1,3 @@
-import * as ts from 'typescript'
import createTransformer from 'ts-import-plugin'
const transformer = createTransformer({
@@ -8,6 +7,6 @@ const transformer = createTransformer({
style: false,
})
-export default function (program: ts.Program, pluginOptions: {}) {
+export default function () {
return transformer
}
]