فهرست منبع

fix: 优化组件方法透传并新增表单弹窗示例 (#6443)

RanMaoting 2 ماه پیش
والد
کامیت
fee811d950

+ 10 - 17
apps/web-antd/src/adapter/component/index.ts

@@ -8,13 +8,7 @@ import type { Component } from 'vue';
 import type { BaseFormComponentType } from '@vben/common-ui';
 import type { Recordable } from '@vben/types';
 
-import {
-  defineAsyncComponent,
-  defineComponent,
-  getCurrentInstance,
-  h,
-  ref,
-} from 'vue';
+import { defineAsyncComponent, defineComponent, h, ref } from 'vue';
 
 import { ApiComponent, globalShareState, IconPicker } from '@vben/common-ui';
 import { $t } from '@vben/locales';
@@ -82,16 +76,15 @@ const withDefaultPlaceholder = <T extends Component>(
         $t(`ui.placeholder.${type}`);
       // 透传组件暴露的方法
       const innerRef = ref();
-      const publicApi: Recordable<any> = {};
-      expose(publicApi);
-      const instance = getCurrentInstance();
-      instance?.proxy?.$nextTick(() => {
-        for (const key in innerRef.value) {
-          if (typeof innerRef.value[key] === 'function') {
-            publicApi[key] = innerRef.value[key];
-          }
-        }
-      });
+      expose(
+        new Proxy(
+          {},
+          {
+            get: (_target, key) => innerRef.value?.[key],
+            has: (_target, key) => key in (innerRef.value || {}),
+          },
+        ),
+      );
       return () =>
         h(
           component,

+ 10 - 17
apps/web-ele/src/adapter/component/index.ts

@@ -8,13 +8,7 @@ import type { Component } from 'vue';
 import type { BaseFormComponentType } from '@vben/common-ui';
 import type { Recordable } from '@vben/types';
 
-import {
-  defineAsyncComponent,
-  defineComponent,
-  getCurrentInstance,
-  h,
-  ref,
-} from 'vue';
+import { defineAsyncComponent, defineComponent, h, ref } from 'vue';
 
 import { ApiComponent, globalShareState, IconPicker } from '@vben/common-ui';
 import { $t } from '@vben/locales';
@@ -139,16 +133,15 @@ const withDefaultPlaceholder = <T extends Component>(
         $t(`ui.placeholder.${type}`);
       // 透传组件暴露的方法
       const innerRef = ref();
-      const publicApi: Recordable<any> = {};
-      expose(publicApi);
-      const instance = getCurrentInstance();
-      instance?.proxy?.$nextTick(() => {
-        for (const key in innerRef.value) {
-          if (typeof innerRef.value[key] === 'function') {
-            publicApi[key] = innerRef.value[key];
-          }
-        }
-      });
+      expose(
+        new Proxy(
+          {},
+          {
+            get: (_target, key) => innerRef.value?.[key],
+            has: (_target, key) => key in (innerRef.value || {}),
+          },
+        ),
+      );
       return () =>
         h(
           component,

+ 10 - 17
apps/web-naive/src/adapter/component/index.ts

@@ -8,13 +8,7 @@ import type { Component } from 'vue';
 import type { BaseFormComponentType } from '@vben/common-ui';
 import type { Recordable } from '@vben/types';
 
-import {
-  defineAsyncComponent,
-  defineComponent,
-  getCurrentInstance,
-  h,
-  ref,
-} from 'vue';
+import { defineAsyncComponent, defineComponent, h, ref } from 'vue';
 
 import { ApiComponent, globalShareState, IconPicker } from '@vben/common-ui';
 import { $t } from '@vben/locales';
@@ -85,16 +79,15 @@ const withDefaultPlaceholder = <T extends Component>(
         $t(`ui.placeholder.${type}`);
       // 透传组件暴露的方法
       const innerRef = ref();
-      const publicApi: Recordable<any> = {};
-      expose(publicApi);
-      const instance = getCurrentInstance();
-      instance?.proxy?.$nextTick(() => {
-        for (const key in innerRef.value) {
-          if (typeof innerRef.value[key] === 'function') {
-            publicApi[key] = innerRef.value[key];
-          }
-        }
-      });
+      expose(
+        new Proxy(
+          {},
+          {
+            get: (_target, key) => innerRef.value?.[key],
+            has: (_target, key) => key in (innerRef.value || {}),
+          },
+        ),
+      );
       return () =>
         h(
           component,

+ 11 - 1
apps/web-naive/src/views/demos/form/basic.vue

@@ -1,11 +1,13 @@
 <script lang="ts" setup>
-import { Page } from '@vben/common-ui';
+import { Page, useVbenModal } from '@vben/common-ui';
 
 import { NButton, NCard, useMessage } from 'naive-ui';
 
 import { useVbenForm } from '#/adapter/form';
 import { getAllMenusApi } from '#/api';
 
+import modalDemo from './modal.vue';
+
 const message = useMessage();
 const [Form, formApi] = useVbenForm({
   commonConfig: {
@@ -143,6 +145,10 @@ function setFormValues() {
     date: Date.now(),
   });
 }
+
+const [Modal, modalApi] = useVbenModal({
+  connectedComponent: modalDemo,
+});
 </script>
 <template>
   <Page
@@ -152,8 +158,12 @@ function setFormValues() {
     <NCard title="基础表单">
       <template #header-extra>
         <NButton type="primary" @click="setFormValues">设置表单值</NButton>
+        <NButton type="primary" @click="modalApi.open()" class="ml-2">
+          打开弹窗
+        </NButton>
       </template>
       <Form />
     </NCard>
+    <Modal />
   </Page>
 </template>

+ 71 - 0
apps/web-naive/src/views/demos/form/modal.vue

@@ -0,0 +1,71 @@
+<script lang="ts" setup>
+import { useVbenModal } from '@vben/common-ui';
+
+import { useVbenForm } from '#/adapter/form';
+
+defineOptions({
+  name: 'FormModelDemo',
+});
+
+const [Form, formApi] = useVbenForm({
+  schema: [
+    {
+      component: 'Input',
+      componentProps: {
+        placeholder: '请输入',
+      },
+      fieldName: 'field1',
+      label: '字段1',
+      rules: 'required',
+    },
+    {
+      component: 'Input',
+      componentProps: {
+        placeholder: '请输入',
+      },
+      fieldName: 'field2',
+      label: '字段2',
+      rules: 'required',
+    },
+    {
+      component: 'Select',
+      componentProps: {
+        options: [
+          { label: '选项1', value: '1' },
+          { label: '选项2', value: '2' },
+        ],
+        placeholder: '请输入',
+      },
+      fieldName: 'field3',
+      label: '字段3',
+      rules: 'required',
+    },
+  ],
+  showDefaultActions: false,
+});
+
+const [Modal, modalApi] = useVbenModal({
+  fullscreenButton: false,
+  onCancel() {
+    modalApi.close();
+  },
+  onConfirm: async () => {
+    await formApi.validateAndSubmitForm();
+    // modalApi.close();
+  },
+  onOpenChange(isOpen: boolean) {
+    if (isOpen) {
+      const { values } = modalApi.getData<Record<string, any>>();
+      if (values) {
+        formApi.setValues(values);
+      }
+    }
+  },
+  title: '内嵌表单示例',
+});
+</script>
+<template>
+  <Modal>
+    <Form />
+  </Modal>
+</template>

+ 19 - 17
playground/src/adapter/component/index.ts

@@ -8,13 +8,7 @@ import type { Component } from 'vue';
 import type { BaseFormComponentType } from '@vben/common-ui';
 import type { Recordable } from '@vben/types';
 
-import {
-  defineAsyncComponent,
-  defineComponent,
-  getCurrentInstance,
-  h,
-  ref,
-} from 'vue';
+import { defineAsyncComponent, defineComponent, h, ref } from 'vue';
 
 import { ApiComponent, globalShareState, IconPicker } from '@vben/common-ui';
 import { $t } from '@vben/locales';
@@ -82,16 +76,24 @@ const withDefaultPlaceholder = <T extends Component>(
         $t(`ui.placeholder.${type}`);
       // 透传组件暴露的方法
       const innerRef = ref();
-      const publicApi: Recordable<any> = {};
-      expose(publicApi);
-      const instance = getCurrentInstance();
-      instance?.proxy?.$nextTick(() => {
-        for (const key in innerRef.value) {
-          if (typeof innerRef.value[key] === 'function') {
-            publicApi[key] = innerRef.value[key];
-          }
-        }
-      });
+      // const publicApi: Recordable<any> = {};
+      expose(
+        new Proxy(
+          {},
+          {
+            get: (_target, key) => innerRef.value?.[key],
+            has: (_target, key) => key in (innerRef.value || {}),
+          },
+        ),
+      );
+      // const instance = getCurrentInstance();
+      // instance?.proxy?.$nextTick(() => {
+      //   for (const key in innerRef.value) {
+      //     if (typeof innerRef.value[key] === 'function') {
+      //       publicApi[key] = innerRef.value[key];
+      //     }
+      //   }
+      // });
       return () =>
         h(
           component,

+ 0 - 8
pnpm-lock.yaml

@@ -1470,9 +1470,6 @@ importers:
       vue:
         specifier: ^3.5.17
         version: 3.5.17(typescript@5.8.3)
-      vue-sonner:
-        specifier: ^2.0.1
-        version: 2.0.1
 
   packages/@core/ui-kit/tabs-ui:
     dependencies:
@@ -11258,9 +11255,6 @@ packages:
     peerDependencies:
       vue: ^3.5.17
 
-  vue-sonner@2.0.1:
-    resolution: {integrity: sha512-sn4vjCRzRcnMaxaLa9aNSyZQi6S+gshiea5Lc3eqpkj0ES9LH8ljg+WJCkxefr28V4PZ9xkUXBIWpxGfQxstIg==}
-
   vue-tippy@6.7.1:
     resolution: {integrity: sha512-gdHbBV5/Vc8gH87hQHLA7TN1K4BlLco3MAPrTb70ZYGXxx+55rAU4a4mt0fIoP+gB3etu1khUZ6c29Br1n0CiA==}
     peerDependencies:
@@ -22068,8 +22062,6 @@ snapshots:
       '@vue/devtools-api': 6.6.4
       vue: 3.5.17(typescript@5.8.3)
 
-  vue-sonner@2.0.1: {}
-
   vue-tippy@6.7.1(vue@3.5.17(typescript@5.8.3)):
     dependencies:
       tippy.js: 6.3.7