Skip to content

Commit 7fc60b1

Browse files
authored
feat(design-config): design-config adds arbitrary attribute features of global configuration components (#3419)
* feat(design-config): design-config添加全局配置组件任意属性特性 * test: 修复测试用例报错 * feat(render-component): 在renderComponent中添加customDesignProps参数以支持自定义设计属性 * fix(render-component): 优化renderComponent中的props合并逻辑,确保customDesignProps正确应用 * docs(config-provider): 更新描述信息,增加全局配置组件任意属性支持的说明
1 parent 44bad19 commit 7fc60b1

File tree

9 files changed

+97
-25
lines changed

9 files changed

+97
-25
lines changed

examples/sites/demos/pc/app/config-provider/base-composition-api.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ const design = {
5353
icons: {
5454
warning: iconWarningTriangle()
5555
},
56+
props: {
57+
center: true
58+
},
5659
/**
5760
*
5861
* @param {*} props 组件属性集合

examples/sites/demos/pc/app/config-provider/base.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ export default {
6060
}
6161
},
6262
Alert: {
63+
props: {
64+
center: true
65+
},
6366
icons: {
6467
warning: iconWarningTriangle()
6568
},

examples/sites/demos/pc/app/config-provider/basic.spec.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@ test('测试自定义事件', async ({ page }) => {
44
page.on('pageerror', (exception) => expect(exception).toBeNull())
55
await page.goto('config-provider#base')
66

7-
// 验证自定义方法
87
const demo = page.locator('#base')
8+
// 验证文字居中
9+
await expect(demo.locator('.tiny-alert')).toHaveCSS('justify-content', 'center')
10+
11+
// 验证自定义方法
912
await demo.locator('.tiny-config-provider .tiny-alert > .tiny-alert__close').click()
1013
await page.waitForTimeout(500)
1114
await expect(page.locator('.tiny-modal > .tiny-modal__box').nth(1)).toHaveText('触发自定方法')

examples/sites/demos/pc/app/config-provider/webdoc/config-provider.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ export default {
1212
},
1313
desc: {
1414
'zh-CN':
15-
'可通过<code> design </code>属性设置自定义不同设计规范的图标和逻辑,例如:全局配置 Form 表单组件的必填星号是否默认显示、Button 组件的点击后的禁用时间和是否默认圆角。',
15+
'通过 <code>design</code> 属性可以自定义不同设计规范的图标和逻辑。从 3.23.0 版本开始,支持全局配置组件的任意 <code>props</code> 属性(仅支持双层组件),例如:可以全局配置 Form 组件必填项星号的默认显示状态、Button 组件的点击防抖时间以及是否默认显示圆角等。',
1616
'en-US':
17-
'Icons and logic for different design specifications can be customized through the <code>design</code> attribute configuration.'
17+
'You can use the <code> design </code> property to set custom icons and logic for different design specifications, starting from version 3.23.0, the global configuration component (only supports double-layer components) supports the function of any <code> props </code> attribute, for example: the default display of the required star of the global configuration Form form component, the disabled time after the click of the Button component, and whether the default roundness is enabled.'
1818
},
1919
codeFiles: ['base.vue']
2020
},

packages/vue-common/src/adapter/vue2.7/index.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,32 @@ export const renderComponent = ({
2626
view = null as any,
2727
component = null as any,
2828
props,
29+
customDesignProps,
2930
context: { attrs, listeners: on, slots },
3031
extend = {}
3132
}) => {
3233
return () =>
3334
hooks.h(
3435
(view && view.value) || component,
35-
Object.assign({ props, attrs, [extend.isSvg ? 'nativeOn' : 'on']: on, scopedSlots: { ...slots } }, extend)
36+
Object.assign(
37+
{
38+
props: { ...props, ...customDesignProps },
39+
attrs,
40+
[extend.isSvg ? 'nativeOn' : 'on']: on,
41+
scopedSlots: { ...slots }
42+
},
43+
extend
44+
)
3645
)
3746
}
3847

3948
export const rootConfig = () => hooks.getCurrentInstance()?.proxy.$root
4049

50+
export const getCustomProps = () => {
51+
const instance = hooks.getCurrentInstance()?.proxy
52+
return instance?.$options?.propsData || {}
53+
}
54+
4155
export const getComponentName = () => {
4256
// 此处组件最多为两层组件,所以对多获取到父级组件即可
4357
const instance = hooks.getCurrentInstance()

packages/vue-common/src/adapter/vue2/index.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,21 +34,33 @@ export const renderComponent = ({
3434
view = null as any,
3535
component = null as any,
3636
props,
37+
customDesignProps,
3738
context: { attrs, listeners: on, slots },
3839
extend = {}
3940
}) => {
4041
return () =>
4142
hooks.h(
4243
(view && view.value) || component,
4344
Object.assign(
44-
{ props, attrs, [extend.isSvg ? 'nativeOn' : 'on']: on, ref: 'modeTemplate', scopedSlots: { ...slots } },
45+
{
46+
props: { ...props, ...customDesignProps },
47+
attrs,
48+
[extend.isSvg ? 'nativeOn' : 'on']: on,
49+
ref: 'modeTemplate',
50+
scopedSlots: { ...slots }
51+
},
4552
extend
4653
)
4754
)
4855
}
4956

5057
export const rootConfig = () => hooks.getCurrentInstance()?.proxy.$root
5158

59+
export const getCustomProps = () => {
60+
const instance = hooks.getCurrentInstance()?.proxy
61+
return instance?.$options?.propsData || {}
62+
}
63+
5264
export const getComponentName = () => {
5365
// 此处组件最多为两层组件,所以对多获取到父级组件即可
5466
const instance = hooks.getCurrentInstance()

packages/vue-common/src/adapter/vue3/index.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,16 @@ export const renderComponent = ({
2626
view = undefined as any,
2727
component = undefined as any,
2828
props,
29+
customDesignProps,
2930
context: { attrs, slots },
3031
extend = {}
3132
}) => {
32-
return () => hooks.h((view && view.value) || component, { ref: 'modeTemplate', ...props, ...attrs, ...extend }, slots)
33+
return () =>
34+
hooks.h(
35+
(view && view.value) || component,
36+
{ ref: 'modeTemplate', ...props, ...attrs, ...customDesignProps, ...extend },
37+
slots
38+
)
3339
}
3440

3541
export const rootConfig = (context) => {
@@ -38,6 +44,11 @@ export const rootConfig = (context) => {
3844
return instance?.appContext.config.globalProperties
3945
}
4046

47+
export const getCustomProps = () => {
48+
const instance = hooks.getCurrentInstance()
49+
return instance?.vnode?.props || {}
50+
}
51+
4152
export const getComponentName = () => {
4253
// 此处组件最多为两层组件,所以对多获取到父级组件即可
4354
const instance = hooks.getCurrentInstance()

packages/vue-common/src/index.ts

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
tools,
1717
useRouter,
1818
getComponentName,
19+
getCustomProps,
1920
isVnode
2021
} from './adapter'
2122
import { t } from '@opentiny/vue-locale'
@@ -122,19 +123,6 @@ const resolveChartTheme = (props, context) => {
122123
return tinyChartTheme
123124
}
124125

125-
export const $setup = ({ props, context, template, extend = {} }) => {
126-
const mode = resolveMode(props, context)
127-
const view = hooks.computed(() => {
128-
if (typeof props.tiny_template !== 'undefined') return props.tiny_template
129-
130-
const component = template(mode, props)
131-
132-
return typeof component === 'function' ? defineAsyncComponent(component) : component
133-
})
134-
135-
return renderComponent({ view, props, context, extend })
136-
}
137-
138126
// 提供给没有renderless层的组件使用(比如TinyVuePlus组件)
139127
export const design = {
140128
configKey: Symbol('designConfigKey'),
@@ -168,17 +156,55 @@ export const customDesignConfig: CustomDesignConfig = {
168156
twMerge: () => ''
169157
}
170158

171-
export const mergeClass = (...cssClasses) => customDesignConfig.twMerge(stringifyCssClass(cssClasses))
172-
173-
export const setup = ({ props, context, renderless, api, extendOptions = {}, mono = false, classes = {} }) => {
174-
const render = typeof props.tiny_renderless === 'function' ? props.tiny_renderless : renderless
175-
159+
const getDesignConfig = () => {
176160
// 获取组件级配置和全局配置(inject需要带有默认值,否则控制台会报警告)
177161
let globalDesignConfig: DesignConfig = customDesignConfig.designConfig || hooks.inject(design.configKey, {})
162+
178163
// globalDesignConfig 可能是响应式对象,比如 computed
179164
globalDesignConfig = globalDesignConfig?.value || globalDesignConfig || {}
180165
const designConfig = globalDesignConfig?.components?.[getComponentName().replace($prefix, '')]
166+
return {
167+
designConfig,
168+
globalDesignConfig
169+
}
170+
}
171+
172+
export const $setup = ({ props: propData, context, template, extend = {} }) => {
173+
const mode = resolveMode(propData, context)
174+
const view = hooks.computed(() => {
175+
if (typeof propData.tiny_template !== 'undefined') return propData.tiny_template
176+
177+
const component = template(mode, propData)
178+
179+
return typeof component === 'function' ? defineAsyncComponent(component) : component
180+
})
181+
182+
const { designConfig } = getDesignConfig()
183+
const customDesignProps = {}
184+
185+
const designProps = designConfig?.props
186+
187+
if (designProps) {
188+
// 获取用户传递的props
189+
const customProps = getCustomProps()
190+
191+
Object.keys(designProps).forEach((key) => {
192+
// 用户没有配置的属性才进行覆盖
193+
if (!Object.prototype.hasOwnProperty.call(customProps, key)) {
194+
customDesignProps[key] = designProps[key]
195+
}
196+
})
197+
}
198+
199+
return renderComponent({ view, props: propData, customDesignProps, context, extend })
200+
}
201+
202+
export const mergeClass = (...cssClasses) => customDesignConfig.twMerge(stringifyCssClass(cssClasses))
203+
204+
export const setup = ({ props, context, renderless, api, extendOptions = {}, mono = false, classes = {} }) => {
205+
const render = typeof props.tiny_renderless === 'function' ? props.tiny_renderless : renderless
181206

207+
const { designConfig, globalDesignConfig } = getDesignConfig()
182208
const utils = {
183209
$prefix,
184210
t,

packages/vue/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,4 +237,4 @@
237237
"build": "pnpm -w build:ui",
238238
"postversion": "pnpm build"
239239
}
240-
}
240+
}

0 commit comments

Comments
 (0)