From 7dea4967179763c4ed5118657ae1a8ff672fb54b Mon Sep 17 00:00:00 2001 From: jieliu52 Date: Sat, 8 Mar 2025 11:00:19 +0800 Subject: [PATCH 1/4] feat: add hoc feat --- .../vue-common/src/adapter/vue2.7/hoc.tsx | 73 +++++++++++++++++++ .../vue-common/src/adapter/vue2.7/index.ts | 10 +++ packages/vue-common/src/adapter/vue2/hoc.tsx | 73 +++++++++++++++++++ packages/vue-common/src/adapter/vue2/index.ts | 10 +++ packages/vue-common/src/adapter/vue3/hoc.tsx | 43 +++++++++++ packages/vue-common/src/adapter/vue3/index.ts | 10 +++ packages/vue-common/src/index.ts | 4 +- 7 files changed, 221 insertions(+), 2 deletions(-) create mode 100644 packages/vue-common/src/adapter/vue2.7/hoc.tsx create mode 100644 packages/vue-common/src/adapter/vue2/hoc.tsx create mode 100644 packages/vue-common/src/adapter/vue3/hoc.tsx diff --git a/packages/vue-common/src/adapter/vue2.7/hoc.tsx b/packages/vue-common/src/adapter/vue2.7/hoc.tsx new file mode 100644 index 0000000000..2cf02c2200 --- /dev/null +++ b/packages/vue-common/src/adapter/vue2.7/hoc.tsx @@ -0,0 +1,73 @@ +import Vue from 'vue' +import { design, $prefix } from '../../index' + +export default function DesignConfigPropsHOC(BaseComponent) { + return Vue.extend({ + name: 'DesignConfigHOC', + functional: false, + inject: { + globalDesignConfig: { + from: design.configKey, + default: () => ({}) + } + }, + created() { + // 暴露实例方法 + this.$watch( + () => this.innerRef, + (val) => { + if (val) { + Object.keys(val).forEach((key) => { + if (!(key in this)) { + this[key] = val[key] + } + }) + } + } + ) + }, + data() { + return { + innerRef: null + } + }, + computed: { + componentName() { + const rawName = BaseComponent.name || BaseComponent.options.name + return rawName.replace($prefix, '') + }, + mergedProps() { + // 处理响应式配置 + const globalConfig = this.unwrap(this.globalDesignConfig) + const componentConfig = globalConfig.components?.[this.componentName] || {} + return { + ...componentConfig.props, + ...this.$attrs + } + } + }, + methods: { + // 解包可能存在的响应式对象 + unwrap(config) { + if (config && typeof config === 'object' && 'value' in config) { + return config.value + } + return config || {} + } + }, + render(h) { + return h( + BaseComponent, + { + ref: (el) => { + this.innerRef = el + }, + attrs: this.mergedProps, + on: this.$listeners, + scopedSlots: this.$scopedSlots + }, + this.$slots.default + ) + } + }) +} diff --git a/packages/vue-common/src/adapter/vue2.7/index.ts b/packages/vue-common/src/adapter/vue2.7/index.ts index b1faf1fc07..f7e62c9cc4 100644 --- a/packages/vue-common/src/adapter/vue2.7/index.ts +++ b/packages/vue-common/src/adapter/vue2.7/index.ts @@ -13,6 +13,7 @@ import Vue from 'vue' import * as hooks from 'vue' import { emitter, bindFilter, getElementCssClass, getElementStatusClass } from '../utils' import teleport from '../teleport' +import DesignConfigPropsHOC from './hoc' const Teleport = teleport(hooks) @@ -341,6 +342,15 @@ export const createComponentFn = (design) => { export const defineComponent = hooks.defineComponent +// 简便用法 +// export default WithDesignConfigPropsDefineComponent({ +// name: 'xxx', +// setup() {} +// }) +export function WithDesignConfigPropsDefineComponent(BaseComponent: any) { + return DesignConfigPropsHOC(hooks.defineComponent(BaseComponent)) +} + export default hooks export const isVue2 = true diff --git a/packages/vue-common/src/adapter/vue2/hoc.tsx b/packages/vue-common/src/adapter/vue2/hoc.tsx new file mode 100644 index 0000000000..2cf02c2200 --- /dev/null +++ b/packages/vue-common/src/adapter/vue2/hoc.tsx @@ -0,0 +1,73 @@ +import Vue from 'vue' +import { design, $prefix } from '../../index' + +export default function DesignConfigPropsHOC(BaseComponent) { + return Vue.extend({ + name: 'DesignConfigHOC', + functional: false, + inject: { + globalDesignConfig: { + from: design.configKey, + default: () => ({}) + } + }, + created() { + // 暴露实例方法 + this.$watch( + () => this.innerRef, + (val) => { + if (val) { + Object.keys(val).forEach((key) => { + if (!(key in this)) { + this[key] = val[key] + } + }) + } + } + ) + }, + data() { + return { + innerRef: null + } + }, + computed: { + componentName() { + const rawName = BaseComponent.name || BaseComponent.options.name + return rawName.replace($prefix, '') + }, + mergedProps() { + // 处理响应式配置 + const globalConfig = this.unwrap(this.globalDesignConfig) + const componentConfig = globalConfig.components?.[this.componentName] || {} + return { + ...componentConfig.props, + ...this.$attrs + } + } + }, + methods: { + // 解包可能存在的响应式对象 + unwrap(config) { + if (config && typeof config === 'object' && 'value' in config) { + return config.value + } + return config || {} + } + }, + render(h) { + return h( + BaseComponent, + { + ref: (el) => { + this.innerRef = el + }, + attrs: this.mergedProps, + on: this.$listeners, + scopedSlots: this.$scopedSlots + }, + this.$slots.default + ) + } + }) +} diff --git a/packages/vue-common/src/adapter/vue2/index.ts b/packages/vue-common/src/adapter/vue2/index.ts index f30c77acca..1e319a6140 100644 --- a/packages/vue-common/src/adapter/vue2/index.ts +++ b/packages/vue-common/src/adapter/vue2/index.ts @@ -3,6 +3,7 @@ import * as compositionHooks from '@vue/composition-api' import * as vueHooks from 'vue' import { bindFilter, emitter, getElementCssClass, getElementStatusClass } from '../utils' import teleport from '../teleport' +import DesignConfigPropsHOC from './hoc' // vue2.7有version字段 const isVueHooks = Boolean(Vue.version?.includes('2.7')) @@ -342,6 +343,15 @@ export const createComponentFn = (design) => { export const defineComponent = hooks.defineComponent +// 简便用法 +// export default WithDesignConfigPropsDefineComponent({ +// name: 'xxx', +// setup() {} +// }) +export function WithDesignConfigPropsDefineComponent(BaseComponent: any) { + return DesignConfigPropsHOC(hooks.defineComponent(BaseComponent)) +} + export default hooks export const isVue2 = true diff --git a/packages/vue-common/src/adapter/vue3/hoc.tsx b/packages/vue-common/src/adapter/vue3/hoc.tsx new file mode 100644 index 0000000000..4023cf19bf --- /dev/null +++ b/packages/vue-common/src/adapter/vue3/hoc.tsx @@ -0,0 +1,43 @@ +import { type SetupContext } from 'vue' +import { design, hooks, $prefix } from '../../index' +import { getComponentName } from '../index' + +// 修改组件 props ,注入 Design Config +export default function DesignConfigPropsHOC(BaseComponent: any) { + return { + ...BaseComponent, + props: {}, + setup(props, { attrs, slots, expose }: SetupContext) { + const innerRef = hooks.ref() + // 获取组件级配置和全局配置(inject需要带有默认值,否则控制台会报警告) + let globalDesignConfig = hooks.inject(design.configKey, {}) + // globalDesignConfig 可能是响应式对象,比如 computed + globalDesignConfig = globalDesignConfig?.value || globalDesignConfig || {} + const designConfig = globalDesignConfig?.components?.[getComponentName().replace($prefix, '')] + const designConfigProps = designConfig?.props || {} + const mergedProps = { ...designConfigProps, ...attrs } + + expose( + new Proxy( + {}, + { + get(_target, key) { + return innerRef.value?.[key] + }, + has(_target, key) { + return innerRef.value?.[key] + } + } + ) + ) + + return () => { + return ( + + {slots} + + ) + } + } + } +} diff --git a/packages/vue-common/src/adapter/vue3/index.ts b/packages/vue-common/src/adapter/vue3/index.ts index 386aabe1fa..42ddd9059e 100644 --- a/packages/vue-common/src/adapter/vue3/index.ts +++ b/packages/vue-common/src/adapter/vue3/index.ts @@ -13,6 +13,7 @@ import * as hooks from 'vue' import { camelize, capitalize, hyphenate } from '@opentiny/utils' import { bindFilter, emitter, getElementCssClass, getElementStatusClass } from '../utils' +import DesignConfigPropsHOC from './hoc' const Teleport = hooks.Teleport @@ -477,6 +478,15 @@ export const createComponentFn = (design) => { export const defineComponent = hooks.defineComponent +// 简便用法 +// export default WithDesignConfigPropsDefineComponent({ +// name: 'xxx', +// setup() {} +// }) +export function WithDesignConfigPropsDefineComponent(BaseComponent: any) { + return DesignConfigPropsHOC(hooks.defineComponent(BaseComponent)) +} + export default hooks export const isVue2 = false diff --git a/packages/vue-common/src/index.ts b/packages/vue-common/src/index.ts index 9d14e1b642..fd71a85d4e 100644 --- a/packages/vue-common/src/index.ts +++ b/packages/vue-common/src/index.ts @@ -21,7 +21,7 @@ import { import { t } from '@opentiny/vue-locale' import { stringifyCssClass, stringifyCssClassObject, stringifyCssClassArray, deduplicateCssClass } from './csscls' import '@opentiny/vue-theme/base/index.less' -import { defineComponent, isVue2, isVue3 } from './adapter' +import { defineComponent, isVue2, isVue3, WithDesignConfigPropsDefineComponent } from './adapter' import { useBreakpoint } from './breakpoint' import { useDefer } from './usedefer' import { GRADIENT_ICONS_LIST, generateIcon } from './generateIcon' @@ -37,7 +37,7 @@ export { useBreakpoint, useDefer } export { version } from '../package.json' -export { defineComponent, isVue2, isVue3, appProperties } +export { defineComponent, isVue2, isVue3, appProperties, WithDesignConfigPropsDefineComponent } export const $prefix = 'Tiny' From 6cb4266e5a19510e3f1b49fa476674ef820f46f8 Mon Sep 17 00:00:00 2001 From: jieliu52 Date: Sat, 8 Mar 2025 11:01:58 +0800 Subject: [PATCH 2/4] feat: hoc test on button component --- .../sites/demos/pc/app/button/basic-usage.vue | 31 +++++++++++++------ packages/vue/src/button/src/index.ts | 2 +- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/examples/sites/demos/pc/app/button/basic-usage.vue b/examples/sites/demos/pc/app/button/basic-usage.vue index fbd4ac12f3..11b53b5642 100644 --- a/examples/sites/demos/pc/app/button/basic-usage.vue +++ b/examples/sites/demos/pc/app/button/basic-usage.vue @@ -2,12 +2,14 @@

基本按钮

- 主要按钮 - 次要按钮 - 成功按钮 - 信息按钮 - 警告按钮 - 危险按钮 + + 主要按钮 + 次要按钮 + 成功按钮 + 信息按钮 + 警告按钮 + 危险按钮 +

朴素按钮

@@ -40,14 +42,15 @@