在开发页面时,经常会遇到这样的需求,在很多地方都会用到相同的弹框组件(比如
通用的选择商品功能、表单功能等
),通常的实现方式是在每个页面上以标签的方式引入弹框组件(如 elementUI
的 el-dialog
或是 ant-design-vue
的 modal
),然后处理 弹框
的各种事件交互,这样处理会产生很多重复的代码。所有想着能不能在组件内部直接调用方法来实现需求,解决办法就是模仿 UI框架的message组件
的实现方式,以函数的方式来调用组件功能
注意:本文的例子使用
vue3
和 ant-design-vue
实现步骤
1. 首先定义一个通用方法,主要的函数式的实现逻辑在这里
createCustomModal.ts
import { Component, createApp } from "vue";
import {
Button,
Input,
Modal,
Select,
Tooltip,
Form,
Table,
} from "ant-design-vue";
/**
* modalConstructor 为传入的业务组件,
* options 为调用业务组件时可传入的参数
*/
export function createCustomModal(modalConstructor: Component, options?: any) {
// 这里使用promise的方式,使我们在以函数的方式调用组件时,可以异步处理
return new Promise((resolve, reject) => {
let app: any = null;
let instance: any = null;
const container = document.createElement("div");
document.body.appendChild(container);
// 销毁元素
function destroyNodes() {
instance = null;
app?.unmount();
document.body.removeChild(container);
}
// 定义close方法,通过props传递给组件
function close() {
destroyNodes();
reject();
}
// 定义ok方法,通过props传递给组件
function ok(val?: any) {
destroyNodes();
resolve(val);
}
// 使用createAPP方法生成vue实例,第一个参数modalConstructor为component类型,
// 第二个参数为传递给modalConstructor组件的参数props
app = createApp(modalConstructor, {
close,
ok,
...options
});
instance = app
.use(Select)
.use(Input)
.use(Tooltip)
.use(Button)
.use(Modal)
.use(Table)
.mount(container); // 渲染到创建的div节点上
});
}
上面使用 use
的方式引入Button
, Input
, Modal
, Select
, Tooltip
, Form
, Table
这些组件是因为使用 createApp
生成的 app实例
和 main.ts
中的 app实例
是完全不同的两个 app实例
,不 use
的话这些组件不能正常显示运行,而且一个个引入是因为使用了 按需加载ant-design-vue 组件
的方式 ,如果不考虑 按需加载
的话全部引入 ant-design-vue
即可。
2. 然后给出一个商品选择业务的demo页面
index.vue
<template>
<a-modal
:closeOnClickModal="false"
width="960px"
:title="modalTitle"
:visible="visible"
:row-selection="rowSelection"
@cancel="handleModalCancel"
@ok="handleModalOk"
:ok-text="'确定'"
:calcel-text="'取消'"
class="select-goods-modal"
>
<a-table
:columns="columns"
:data-source="tableData.list"
:loading="tableData.loading"
rowKey="id"
>
<template #action="{text, record, index}">
<a-button @click="handleSelectItem(text, record, index)">选择</a-button>
</template>
</a-table>
</a-modal>
</template>
<script lang="ts">
import {
defineComponent,
computed,
ref,
PropType,
onMounted,
onBeforeUnmount,
reactive,
unref,
toRaw
} from "vue";
export default defineComponent({
name: "SelectGoodsModal",
props: {
close: {
type: Function,
required: true
},
ok: {
type: Function,
required: true
},
},
setup(props) {
const modalTitle = ref("选择商品");
const visible = ref(false);
const tableData: any = reactive({
list: [] as Record<any, any>[],
total: 0
});
const columns = [
{
title: "商品",
key: "skuName",
dataIndex: "skuName",
width: 250
},
{
title: "售价",
key: "price",
dataIndex: "price",
},
{
title: "操作",
dataIndex: "",
key: "x",
slots: { customRender: "action" },
width: 100
}
];
const selectedRowKeys = ref<Key[]>([]);
const selectedRows = ref<Record<any, any>[]>([]);
const rowSelection = {
selectedRowKeys: unref(selectedRowKeys),
onChange: (changableRowKeys: (string | number)[], changableRows: any[]) => {
selectedRowKeys.value = changableRowKeys;
selectedRows.value = changableRows;
}
};
function handleModalCancel() {
visible.value = false;
props.close(); // 调用传入的close方法销毁节点
}
function handleModalOk() {
props.ok(unref(selectedRows)); // 调用传入的ok方法传入已选的“商品”,会走到promise的resolve
}
onMounted(() => {
visible.value = true;
});
return {
modalTitle,
columns,
tableData,
visible,
rowSelection,
handleModalOk,
handleModalCancel
}
}
});
3. 最后定义一个向外部暴露的可引用文件
index.ts
import type { App } from 'vue'
import {createCustomModal} from "./createCustomModal";
import SelectGoodsModalConstructor from "./index.vue";
type SFCWithInstall<T> = T & { install(app: App): void; }
const SelectGoodsModal = function(option?: Record<any, any>) {
// 在这里使用通用方法生成弹框
return createCustomModal(SelectGoodsModalConstructor, option);
} as Function;
const _SelectGoodsModal: SFCWithInstall<typeof SelectGoodsModal> = SelectGoodsModal as SFCWithInstall<typeof SelectGoodsModal>
_SelectGoodsModal.install = (app: App) => {
// 可以注册到全局的globalProperties属性上
app.config.globalProperties.$selectGoodsModal = _SelectGoodsModal
}
export default _SelectGoodsModal // 导出方法
如何在项目中使用
第一种方法
1. 在项目的main.ts中先引入
import SelectGoodsModal from "@/components/selectGoodsModal"; // 默认导入的index.ts文件
app.use(SelectGoodsModal).mount("#app");
2. 在业务里使用
const internalInstance = getCurrentInstance();
function selectGoods() {
return internalInstance!.appContext.config.globalProperties.$selectGoodsModal({
selectedList: []
});
}
selectGoods().then(res => {
console.log(res); // 选择到的商品
}, err => {
console.log("取消选择")
})
第二种,当然也可以不挂载在全局,单独导入直接使用
import {createCustomModal} from "./createCustomModal"; // 导入createCustomModal方法
import SelectGoodsModalConstructor from "./index.vue";
createCustomModal(SelectGoodsModalConstructor, {selectedList: []})
.then(res => {
console.log(res); // 选择到的商品
}, err => {
console.log("取消选择")
})
版权属于:xigua
本文链接:https://www.xianh5.com/archives/63/
转载时须注明出处及本声明