基于Vue.js和Element UI框架的自定义对话框组件

一个基于Vue.js和Element UI框架的自定义对话框组件。这个组件名为biuDialog,封装了Element UI的el-dialog组件,并添加了一些自定义的功能和样式。

组件特点

  1. 自定义标题和底部:组件允许通过插槽(slots)来自定义对话框的标题和底部内容。如果没有提供自定义内容,它会使用默认的标题和底部按钮(取消和确定)。

  2. 控制显示和隐藏 :通过visible属性来控制对话框的显示和隐藏。show计算属性与visible属性同步,当show改变时,会通过$emit通知父组件。

  3. 事件回调 :提供了openopenedcloseclosed事件,以便在对话框打开或关闭时执行特定的逻辑。

  4. 关闭逻辑beforeClose方法允许在关闭对话框之前执行一些逻辑,例如确认操作。beforeClose2方法是一个快捷方式,它在用户点击关闭按钮时触发。

  5. 样式定制 :通过customClass属性可以添加自定义样式。默认情况下,对话框有一个基础样式,可以通过SCSS样式部分进行修改。

组件属性

  • visible: 对话框是否可见。
  • title: 对话框标题。
  • appendToBody: 对话框是否插入到body元素上。
  • modal: 是否需要遮罩层。
  • fullscreen: 是否全屏。
  • destroyOnClose: 关闭时是否销毁对话框中的元素。
  • width: 对话框宽度。
  • top: 对话框距离顶部的距离。
  • customClass: 自定义类名。
  • showClose: 是否显示关闭按钮。
  • closeOnClickModal: 是否可以通过点击遮罩层关闭对话框。
  • beforeClose: 关闭前的回调函数。

组件方法

  • open: 触发open事件。
  • opened: 触发opened事件。
  • close: 触发close事件。
  • closed: 触发closed事件。
  • cancel: 触发cancel事件,通常用于取消操作。
  • submit: 触发submit事件,通常用于确认操作。

样式

样式使用了SCSS,并且是作用域化的,这意味着样式只应用于当前组件。对话框的头部和底部有特定的样式,可以通过修改这些样式来改变对话框的外观。

这个组件可以很容易地集成到使用Element UI的Vue.js项目中,为开发者提供了一个灵活且易于定制的对话框解决方案。

<template>
    <div class="biu-dialog-box">
      <el-dialog
        :custom-class="customClass"
        :title="$slots.title ? '' : title"
        :visible.sync="show"
        :width="width"
        :top="top"
        :append-to-body="appendToBody"
        :modal="modal"
        :fullscreen="fullscreen"
        :destroy-on-close="destroyOnClose"
        :modal-append-to-body="modalAppendToBody"
        :before-close="beforeClose"
        :close-on-click-modal="closeOnClickModal"
        :show-close="false"
        @open="open"
        @opened="opened"
        @close="close"
        @closed="closed"
      >
        <!-- 有写弹窗头部则采用输入的 -->
        <template v-if="$slots.title">
          <span slot="title">
            <slot name="title" />
          </span>
        </template>
        <!-- 自定义默认头部 -->
        <template v-if="!$slots.title">
          <div slot="title" class="biu-default-header-box">
            <div class="biu-default-header-title">{{ title }}</div>
            <div
              class="biu-default-header-close"
              @click="beforeClose2"
              v-if="showClose"
            >
              <span class="biu-icon-guanbi2">
               <i class="el-icon-circle-close"  style="font-size:16px"></i>
              </span>
            </div>
          </div>
        </template>
        <!-- 弹窗内容区域 -->
        <slot />
        <!-- 弹窗底部区域 -->
        <template v-if="$slots.footer">
          <span slot="footer">
            <slot name="footer" />
          </span>
        </template>
        <!-- 自定义默认头部 -->
        <template v-if="!$slots.footer">
          <div slot="footer" class="biu-default-header-box">
            <el-button class="btn" @click="cancel">取消</el-button>
            <el-button class="btn sure" @click="submit">确定</el-button>
          </div>
        </template>
      </el-dialog>
    </div>
  </template>
  
  <script>

  export default {
    name: "biuDialog",
    props: {
      visible: {
        type: Boolean,
        default: false,
      },
      title: {
        type: String,
        default: "提示",
      },
      appendToBody: {
        // Dialog 自身是否插入至 body 元素上。嵌套的 Dialog 必须指定该属性并赋值为 true
        type: Boolean,
        default: true,
      },
      modalAppendToBody: {
        // 遮罩层是否插入至 body 元素上,若为 false,则遮罩层会插入至 Dialog 的父元素上
        type: Boolean,
        default: true,
      },
      modal: {
        // 是否需要遮罩层
        type: Boolean,
        default: true,
      },
      fullscreen: {
        // 是否全屏
        type: Boolean,
        default: false,
      },
      destroyOnClose: {
        // 关闭时销毁 Dialog 中的元素
        type: Boolean,
        default: true,
      },
      width: {
        type: String,
        default: "30%",
      },
      top: {
        type: String,
        default: "15vh",
      },
      customClass: {
        type: String,
        default: "biu-dialog",
      },
      showClose: {
        type: Boolean,
        default: false,
      },
      closeOnClickModal: {
        type: Boolean,
        default: true,
      },
      beforeClose: {
        type: Function,
      },
    },
    computed: {
      show: {
        get() {
          return this.visible;
        },
        set(val) {
          this.$emit("update:visible", val); // visible 改变的时候通知父组件
        },
      },
    },
    data() {
      return {};
    },
    methods: {
      //点击自定义的关闭按钮
      beforeClose2() {
        this.beforeClose(() => {
          this.show = false;
        });
      },
      open() {
        // Dialog 打开的回调
        this.$emit("open");
      },
      opened() {
        // Dialog 打开动画结束时的回调
        this.$emit("opened");
      },
      close() {
        // Dialog 关闭的回调
        this.$emit("close");
      },
      closed() {
        // Dialog 关闭动画结束时的回调
        this.$emit("closed");
      },
      cancel() {
        this.$emit("cancel");
      },
      submit() {
        this.$emit("submit");
      },
    },
  };
  </script>
  
  <style scoped lang="scss">
  :deep(.el-dialog) {
    min-width: 320px;
    .el-dialog__header {
      padding: 0;
      color: #d37332;
      font-weight: 500;
      height: 50px;
      display: flex;
      flex-direction: column;
      justify-content: center;
      border-bottom: 2px solid #e9e8e8;
      font-size: 14px;
      .biu-default-header-box {
        padding: 0 20px;
        display: flex;
        line-height: 20px;
        .biu-default-header-title {
          flex: 1;
        }
        .biu-default-header-close {
          width: 15px;
          height: 15px;
          cursor: pointer;
        }
      }
    }
    .el-dialog__footer {
      padding: 0;
      text-align: center;
      height: 88px;
      border-top: 2px solid #e9e8e8;
      display: flex;
      justify-content: center;
      flex-direction: column;
      .btn {
        width: 120px;
        height: 40px;
        background: #e9e8e8;
        border-radius: 2px;
      }
      .sure {
        color: #ffffff;
        background: #de9a6c;
        margin-left: 66px;
      }
    }
  }
  </style>

在使用biuDialog组件时,您可以通过Vue的插槽(slots)机制来自定义对话框的标题和底部。插槽允许您插入自定义的HTML、组件或者任何其他Vue实例,从而实现高度的灵活性和可定制性。

自定义标题

要自定义对话框的标题,您可以在组件实例中使用<slot>元素,并给它命名(在这个例子中是title)。例如:

html 复制代码
<template>
  <biu-dialog :visible.sync="dialogVisible" title="我的对话框">
    <span slot="title">
      <h1 class="custom-title">这是自定义标题</h1>
    </span>
    <!-- 对话框的其他内容 -->
  </biu-dialog>
</template>

在这个例子中,<span slot="title">定义了一个名为title的插槽,您可以在其中放置任何您想要的内容。biuDialog组件会检查是否有任何内容被插入到title插槽中,如果有,它将使用这些内容作为对话框的标题。

自定义底部

自定义对话框底部的方法与自定义标题类似。您需要在biuDialog组件中使用<slot>元素,并为其命名为footer。例如:

html 复制代码
<template>
  <biu-dialog :visible.sync="dialogVisible" title="我的对话框">
    <!-- 对话框的内容 -->
    <span slot="footer">
      <el-button @click="handleCancel">取消</el-button>
      <el-button type="primary" @click="handleConfirm">确认</el-button>
    </span>
  </biu-dialog>
</template>

在这个例子中,<span slot="footer">定义了一个名为footer的插槽,您可以在其中放置任何您想要的按钮或其他元素。当您需要不同的按钮或逻辑时,只需在插槽内容中进行更改即可。

注意事项

  • 确保您的自定义内容不会破坏对话框的布局或功能。
  • 如果您提供了自定义标题或底部,biuDialog组件将不会显示其默认的标题和底部。
  • 使用插槽时,您需要了解父组件和子组件之间的数据传递和事件触发机制,以确保您的自定义内容能够正确响应用户的交互。

如果希望弹框内容通过配置显示,而不是硬编码在模板中,可以采用以下方法:

使用组件的props传递配置

您可以在父组件中定义一些数据,这些数据将作为配置传递给biuDialog组件。然后,在biuDialog组件内部使用这些配置来动态渲染内容。

父组件示例
html 复制代码
<template>
  <div>
    <!-- 触发弹框的按钮 -->
    <el-button @click="openDialog">打开对话框</el-button>

    <!-- 使用biuDialog组件 -->
    <biu-dialog
      :visible.sync="dialogVisible"
      :config="dialogConfig"
    >
    </biu-dialog>
  </div>
</template>

<script>
export default {
  data() {
    return {
      dialogVisible: false,
      dialogConfig: {
        title: '配置标题',
        content: '这是通过配置显示的内容'
        // 可以添加更多配置项,如按钮文本、样式等
      }
    };
  },
  methods: {
    openDialog() {
      this.dialogVisible = true;
    }
  }
};
</script>

修改biuDialog组件以接收和使用配置

您需要在biuDialog组件中添加新的props来接收配置,并在组件的模板中使用这些配置来渲染内容。

biuDialog组件修改示例
javascript 复制代码
export default {
  // ...
  props: {
    // ...
    config: {
      type: Object,
      default: () => ({
        title: '默认标题',
        content: '默认内容'
      })
    }
  },
  // ...
};

然后在模板中使用这些配置:

html 复制代码
<template>
  <!-- ...其他代码... -->
  <div v-if="config.title" class="dialog-content">
    {{ config.content }}
  </div>
  <!-- ...其他代码... -->
</template>

在这个例子中,config对象包含了titlecontent属性,这些属性被用来动态渲染对话框的标题和内容。如果config对象中的属性不存在,您可以设置默认值或者不渲染相应的内容。

注意事项

  • 确保父组件传递的配置对象中的属性名称与子组件props中定义的名称相匹配。
  • biuDialog组件中,您可能需要根据配置的不同来决定是否渲染某些元素,或者如何渲染它们。
  • 如果配置项较多或者较为复杂,您可能需要在组件内部编写更多的逻辑来处理这些配置。

通过这种方式,可以使弹框内容更加灵活,根据需要轻松更改配置,而无需修改模板代码。

如果弹框内容是一个对象,且该对象包含多个元素,可以将这个对象作为配置传递给biuDialog组件。然后在组件内部,根据这个对象的结构动态渲染每个元素。

父组件示例

在父组件中,您可以定义一个对象,该对象包含了弹框需要显示的所有元素和配置。然后,通过props将这个对象传递给biuDialog组件。

html 复制代码
<template>
  <div>
    <!-- 触发弹框的按钮 -->
    <el-button @click="openDialog">打开对话框</el-button>

    <!-- 使用biuDialog组件 -->
    <biu-dialog
      :visible.sync="dialogVisible"
      :dialog-content-object="contentObject"
    >
    </biu-dialog>
  </div>
</template>

<script>
export default {
  data() {
    return {
      dialogVisible: false,
      contentObject: {
        title: '详细信息',
        elements: [
          { type: 'text', content: '这是一段文本。' },
          { type: 'image', src: 'path/to/image.jpg' },
          { type: 'button', text: '点击我', onClick: this.handleButtonClick }
          // 更多元素...
        ]
      },
      handleButtonClick() {
        // 处理按钮点击事件
        console.log('按钮被点击了!');
      }
    }
  },
  methods: {
    openDialog() {
      this.dialogVisible = true;
    }
  }
};
</script>

修改biuDialog组件以接收和使用对象配置

biuDialog组件中,您需要添加一个新的prop来接收父组件传递的对象,并在模板中遍历这个对象的elements数组,根据每个元素的type来决定如何渲染。

javascript 复制代码
export default {
  // ...
  props: {
    // ...
    dialogContentObject: {
      type: Object,
      default: () => ({
        title: '',
        elements: []
      })
    }
  },
  // ...
};

在模板中使用这个对象:

html 复制代码
<template>
  <el-dialog
    :visible.sync="show"
    :custom-class="customClass"
    :title="config.title || dialogContentObject.title"
    @open="open"
    @opened="opened"
    @close="close"
    @closed="closed"
  >
    <!-- 遍历元素数组并渲染每个元素 -->
    <div v-for="element in dialogContentObject.elements" :key="element.type">
      <template v-if="element.type === 'text'">
        <p>{{ element.content }}</p>
      </template>
      <template v-if="element.type === 'image'">
        <img :src="element.src" alt="Image" />
      </template>
      <template v-if="element.type === 'button'">
        <el-button @click="element.onClick">{{ element.text }}</el-button>
      </template>
      <!-- 更多类型的条件渲染... -->
    </div>
  </el-dialog>
</template>

在这个例子中,dialogContentObject对象包含了一个title和一个elements数组。elements数组中的每个元素都有一个type属性,用于指示应该渲染哪种类型的元素(如文本、图片、按钮等)。模板中的v-for指令用于遍历这些元素,并根据type属性使用v-ifv-else-if来决定渲染哪种模板。

注意事项

  • 确保父组件传递的对象结构与子组件中处理该对象的逻辑相匹配。
  • 在处理事件(如按钮点击)时,确保事件处理函数能够正确地从父组件传递到子组件,并且能够在子组件中被正确调用。
  • 如果元素类型未知或组件需要处理更多类型的元素,您可能需要扩展v-if/v-else-if条件渲染逻辑,以支持更多的元素类型。

这种方法使得弹框内容的渲染完全基于配置,提供了极高的灵活性和可扩展性。您可以根据需要轻松添加或修改配置对象中的元素,而无需更改组件的模板代码。

相关推荐
hibiscusxin40 分钟前
vue-office:word(.docx)、pdf、excel(.xlsx,.xls)格式文件预览
vue.js·word·excel
老魏爱学习1 小时前
记一次JS逆向-新榜数据nonce和xyz参数分析
开发语言·javascript·笔记·网络安全·php
鲤鱼_5992 小时前
记录———封装uni-app+vant(u-upload)上传图片组件
vue.js
joe02352 小时前
Vue+ElementUI 导出为PDF文件
vue.js·elementui·pdf
Jornici2 小时前
搭建vue-electron项目
前端·vue.js·electron
三天不学习3 小时前
前端开发之 节流与防抖
前端·javascript·节流防抖
一雨方知深秋3 小时前
prop校验,prop和data区别
前端·javascript·webpack·data·prop校验
前端熊猫3 小时前
vue的生命周期和nextTick的关系
前端·javascript·vue.js
温柔的男孩像海洋丶3 小时前
LVGL学习之样式和时间,基于正点原子
前端·javascript·学习
zgscwxd8 小时前
thinkphp6模板调用URL方法生成的链接异常
前端·javascript·html