Parcourir la source

feat: 增加Prompt组件, 并且修复 #2976 (#2979)

* feat: 增加Prompt组件

类似于Element UI组件库的MessageBox Prompt组件

* fix: #2976

* refactor: 对appendSchemaByField函数的通用操作进行整理
invalid w il y a 1 an
Parent
commit
7b26c5994c

+ 2 - 7
src/components/Form/src/hooks/useFormEvents.ts

@@ -229,16 +229,11 @@ export function useFormEvents({
     const _schemaList = isObject(schema) ? [schema as FormSchema] : (schema as FormSchema[]);
     if (!prefixField || index === -1 || first) {
       first ? schemaList.unshift(..._schemaList) : schemaList.push(..._schemaList);
-      schemaRef.value = schemaList;
-      _setDefaultValue(schema);
-      return;
-    }
-    if (index !== -1) {
+    } else if (index !== -1) {
       schemaList.splice(index + 1, 0, ..._schemaList);
     }
-    _setDefaultValue(schema);
-
     schemaRef.value = schemaList;
+    _setDefaultValue(schema);
   }
 
   async function resetSchema(data: Partial<FormSchema> | Partial<FormSchema>[]) {

+ 49 - 0
src/components/Prompt/dialog.vue

@@ -0,0 +1,49 @@
+<template>
+  <Modal
+    v-model:visible="visible"
+    :title="title"
+    @ok="handleSubmit"
+    :destroyOnClose="true"
+    :width="width || '500px'"
+    okText="确定"
+    cancelText="取消"
+  >
+    <div class="pt-5 pr-3px">
+      <BasicForm @register="register" />
+    </div>
+  </Modal>
+</template>
+
+<script setup lang="ts">
+  import { ref } from 'vue';
+  import { Modal } from 'ant-design-vue';
+  import { FormSchema } from '/@/components/Form';
+  import { BasicForm, useForm } from '/@/components/Form/index';
+
+  const visible = ref<boolean>(true);
+  const props = defineProps<{
+    title: string;
+    addFormSchemas: FormSchema[];
+    onOK?: Fn;
+    width?: string;
+    labelWidth?: number;
+    layout?: 'horizontal' | 'vertical' | 'inline';
+  }>();
+
+  const [register, { validate }] = useForm({
+    schemas: props.addFormSchemas,
+    showActionButtonGroup: false,
+    labelWidth: props.labelWidth || 80,
+    layout: props.layout || 'horizontal',
+  });
+
+  async function handleSubmit() {
+    const row = await validate();
+    if (props.onOK) {
+      await props.onOK(row.txt);
+    }
+    visible.value = false;
+  }
+</script>
+
+<style scoped></style>

+ 39 - 0
src/components/Prompt/index.ts

@@ -0,0 +1,39 @@
+import { createVNode, VNode, defineComponent, h, render, reactive } from 'vue';
+import { PromptProps, genFormSchemas } from './state';
+import Dialog from './dialog.vue';
+
+export function createPrompt(props: PromptProps) {
+  let vm: Nullable<VNode> = null;
+  const data = reactive({
+    ...props,
+    addFormSchemas: genFormSchemas({
+      label: props.label,
+      required: props.required,
+      inputType: props.inputType,
+      defaultValue: props.defaultValue,
+    }),
+  });
+  const DialogWrap = defineComponent({
+    render() {
+      return h(Dialog, { ...data } as any);
+    },
+  });
+
+  vm = createVNode(DialogWrap);
+
+  render(vm, document.createElement('div'));
+
+  function close() {
+    if (vm?.el && vm.el.parentNode) {
+      vm.el.parentNode.removeChild(vm.el);
+    }
+  }
+
+  return {
+    vm,
+    close,
+    get $el() {
+      return vm?.el as HTMLElement;
+    },
+  };
+}

+ 69 - 0
src/components/Prompt/state.ts

@@ -0,0 +1,69 @@
+import { FormSchema } from '/@/components/Form';
+
+type InputType = 'InputTextArea' | 'InputNumber' | 'Input';
+export interface PromptProps {
+  title: string;
+  label?: string;
+  required?: boolean;
+  onOK?: Fn;
+  inputType?: InputType;
+  labelWidth?: number;
+  width?: string;
+  layout?: 'horizontal' | 'vertical' | 'inline';
+  defaultValue?: string | number;
+}
+
+interface genFormSchemasProps {
+  label?: string;
+  required?: boolean;
+  inputType?: InputType;
+  defaultValue?: string | number;
+}
+
+const inputTypeMap: {
+  [key in InputType]: {
+    colProps: { span: number; offset?: number };
+    componentProps: FormSchema['componentProps'];
+  };
+} = {
+  InputTextArea: {
+    colProps: { span: 23 },
+    componentProps: {
+      placeholder: '请输入内容',
+      autoSize: { minRows: 2, maxRows: 6 },
+      maxlength: 255,
+      showCount: true,
+    },
+  },
+  InputNumber: {
+    colProps: { span: 20, offset: 2 },
+    componentProps: {
+      placeholder: '请输入数字',
+      min: 0,
+    },
+  },
+  Input: {
+    colProps: { span: 20, offset: 2 },
+    componentProps: {
+      placeholder: '请输入内容',
+      min: 0,
+    },
+  },
+};
+
+export function genFormSchemas({
+  label = '备注信息',
+  required = true,
+  inputType = 'InputTextArea',
+  defaultValue = '',
+}: genFormSchemasProps) {
+  const formSchema: FormSchema = {
+    field: 'txt',
+    component: inputType,
+    label,
+    defaultValue,
+    required: Boolean(required),
+    ...inputTypeMap[inputType],
+  };
+  return [formSchema];
+}

+ 22 - 1
src/views/demo/comp/modal/index.vue

@@ -25,6 +25,12 @@
       <a-button type="primary" class="my-4" @click="openTargetModal(4)"> 打开弹窗4 </a-button>
     </a-space>
 
+    <Alert
+      message="使用函数方式创建Prompt,适合较为简单的表单内容,如果需要弹出较为复杂的内容,请使用 Modal."
+      show-icon
+    />
+    <a-button type="primary" class="my-4" @click="handleCreatePrompt"> Prompt </a-button>
+
     <component :is="currentModal" v-model:visible="modalVisible" :userData="userData" />
 
     <Modal1 @register="register1" :minHeight="100" />
@@ -35,7 +41,7 @@
 </template>
 <script lang="ts">
   import { defineComponent, shallowRef, ComponentOptions, ref, nextTick } from 'vue';
-  import { Alert, Space } from 'ant-design-vue';
+  import { Alert, Space, message } from 'ant-design-vue';
   import { useModal } from '/@/components/Modal';
   import Modal1 from './Modal1.vue';
   import Modal2 from './Modal2.vue';
@@ -43,6 +49,7 @@
   import Modal4 from './Modal4.vue';
   import { PageWrapper } from '/@/components/Page';
   import { type Nullable } from '@vben/types';
+  import { createPrompt } from '/@/components/Prompt';
 
   export default defineComponent({
     components: { Alert, Modal1, Modal2, Modal3, Modal4, PageWrapper, ASpace: Space },
@@ -93,6 +100,19 @@
         });
       }
 
+      function handleCreatePrompt() {
+        createPrompt({
+          title: '请输入邮箱',
+          required: true,
+          label: '邮箱',
+          defaultValue: '默认邮箱',
+          onOK: async (email: string) => {
+            message.success('填写的邮箱地址为' + email);
+          },
+          inputType: 'Input',
+        });
+      }
+
       return {
         register1,
         openModal1,
@@ -108,6 +128,7 @@
         send,
         currentModal,
         openModalLoading,
+        handleCreatePrompt,
       };
     },
   });