Vite + Vue3 + Ts 《企业级项目》二次封装 el-table、el-pagination、el-tooltip、el-dialog

 ​前期回顾    ​      

Vite项目,vite + vue3 + ts + vuex + vue-router + axios + scss + 自动导入api(就是用v3不需要引入api直接使用)_0.活在风浪里的博客-优快云博客_vue3+vite​webpack回顾 ​ (移动端打包)一步一步,一步 从代码到,打包成为手机App,上传至nginx服务器 (Vue项目)_0.活在风浪里的博客-优快云博客_移动端打包一步一步,一步 从代码到,打包成为手机App,上传至ngnix服务器 (Vue项目) https://blog.youkuaiyun.com/m0_57904695/article/details/122500485?ops_request_misc=%257B%2522request%255Fid%2522%253A%25221656https://blog.youkuaiyun.com/m0_57904695/article/details/125487996?spm=1001.2014.3001.5501

目录

封装的功能有哪些?

最简单示例

类似这样:

Vue3 表格封装

目录结构:

子组件:newTable

抽离的 emits.ts

抽离的  props.ts

global.d.ts

全局css

newForm

页面使用:

效果: 

el-tooltip 

子组件:

父组件:

el-dialog

子组件:

全局动画弹框css

父组件:


 封装了一个子组件(table、分页),在不同页面引入

封装的功能有哪些?

 分页、表格排序、文字居中、溢出隐藏、操作列、开关、宽、最小宽、type类型(selection/index/expand)、格式化 、不同页面不同操作列、vuex、vue持久化插件、

说思路:data数据请求接口拿到,表头数据一般也是后台接口,如没,前台可自定义自己写

最简单示例

//lable指定表头名 //prop指定每一项数据 
<el-table  :data="tableData">
   <el-table-column 
    :label="item.label" 
     :prop="item.prop"  
    :key="index" v-for="(item,index) in tableHeader" 
   >
  </el-table-column>
</el-table>


//表头
 tableHeader:[
		  {label:'姓名' , prop : 'uname'},
		  {label:'年龄' , prop : 'age'},
		  {label:'性别' , prop : 'sex'},
	    ],
			    
  //表数据
tableData:[
		 {uname:"小明",age:'20',sex:'男'}, 
         {uname:"小黑",age:'18',sex:'男'},
       ]
	
	

类似这样:

 念及此!那开始正文

Vue3 表格封装

目录结构:

子组件:newTable

<template>
	<div class="container">
		<el-card shadow="hover">
			<el-scrollbar max-height="300px" v-if="isShowSearchRegion" class="wrap">
				<slot name="search"></slot>
			</el-scrollbar>
			<el-table
				v-bind="$attrs"
				stripe
				style="width: 100%"
				:data="tableData"
				:border="tableBorder"
				:height="excludeSearchAreaAfterTableHeight"
			>
				<template #empty>
					<el-empty :image-size="emptyImgSize" description="暂无数据" />
				</template>

				<el-table-column
					type="index"
					label="序号"
					min-width="60"
					:index="orderHandler"
					align="center"
				/>

				<el-table-column
					v-for="item in tableHeader"
					v-bind="item"
					:key="item.prop"
				>
					<template #default="{ row }" v-if="item.slotKey">
						<template v-if="item.slotKey.includes('default')">
							<el-link
								type="primary"
								:underline="false"
								@click="handleEdit(row)"
								>编辑</el-link
							>
							<el-popconfirm
								title="确定删除吗?"
								@confirm="handleDelete(row.id)"
							>
								<template #reference>
									<el-link class="ml15" type="danger" :underline="false"
										>删除</el-link
									>
								</template>
							</el-popconfirm>
						</template>
						<slot
							v-for="slot in item.slotKey.split(',')"
							:name="slot"
							:row="row"
						></slot>
					</template>
				</el-table-column>
			</el-table>

			<!-- 分页 -->
			<el-pagination
				v-if="paginationFlag"
				background
				:page-sizes="pageSizesArr"
				:current-page="pageNum"
				:page-size="pageSize"
				:layout="layout"
				:total="total"
				@size-change="handleSizeChange"
				@current-change="handleCurrentChange"
			></el-pagination>
		</el-card>
	</div>
</template>

<script setup lang="ts">
import { onMounted, ref } from "vue";
import myEmits from "./newTableConfig/emits";
import myProps from "./newTableConfig/props";

const emits = defineEmits(myEmits);
const props = defineProps(myProps);

// 序号根据数据长度计算
const orderHandler = (index: number) => {
	const { pageNum, pageSize } = props;
	// 第0条 * 每页条数 + 当前索引+1
	return (pageNum - 1) * pageSize + index + 1;
};
//  页数改变
const handleSizeChange = (val: number | string) =>
	emits("handleSizeChange", val);

// 当前页改变
const handleCurrentChange = (val: number | string) =>
	emits("handleCurrentChange", val);

// 编辑、删除
const handleEdit = (row: object) => emits("handleEdit", row);
const handleDelete = (id: number) => emits("handleDelete", id);

// 搜索区域高度及默认值
const Height = ref();
// 减去搜索区域高度后的table
const excludeSearchAreaAfterTableHeight = ref("calc(100vh - 165px)");

// 获取表格高度-动态计算搜索框高度(onMounted、resize,208是已知的面包屑tebView高度)
const updateHeight = () => {
	let wrapEl = document.querySelector(".wrap") as HTMLDivElement | null;
	if (!wrapEl) return;
	Height.value = wrapEl.getBoundingClientRect().height;
	if (props.isShowSearchRegion) {
		excludeSearchAreaAfterTableHeight.value = `calc(100vh - ${
			165 + Height.value
		}px)`;
	}
};
onMounted(() => {
	// 表格下拉动画
	const tableContainer = <HTMLElement>document.querySelector(".container");
	setTimeout(() => {
		if (tableContainer) tableContainer.style.transform = "translateY(0)";
		updateHeight();
	}, 300);
});
window.addEventListener("resize", updateHeight);
</script>

<style scoped lang="scss">
.container {
	padding: 15px;
	transform: translateY(-100%);
	transition: transform 0.5s;
	// background-color: #870404;
	background-color: #f8f8f8;
	.el-scrollbar {
		// border: 1px solid pink;
		min-height: 100px;
		width: 100%;
		height: fit-content;
	}
	.el-card {
		width: 100%;
		height: 100%;
	}

	.el-pagination {
		margin-left: 15%;
		height: 35px;
		margin-top: 16px;
	}
}
// 穿透父组件
:deep(.el-link) {
	padding-left: 10px;
}
</style>

抽离的 emits.ts

export default [
	// 这里是自定义事件 emits
	'handleSizeChange',
	'handleCurrentChange',
	'handleEdit',
	'handleDelete',
];

抽离的  props.ts

export default {
	// 表头数据
	tableHeader: {
		type: Array as () => TableHeader[],
		default: function () {
			return [];
		},
	},

	// 表格显示的数据
	tableData: {
		default: function () {
			return [];
		},
	},

	// 边框
	tableBorder: {
		type: Boolean,
		default: false,
	},

	// 总页数
	total: {
		type: Number,
		default: 0,
	},

	// 分页的页容量数组
	pageSizesArr: {
		type: Array as () => number[],
		default() {
			return [10, 20, 30, 50];
		},
	},

	// 分页的布局
	layout: {
		type: String,
		default: 'total, sizes, prev, pager, next, jumper',
	},

	// 分页是否显示
	paginationFlag: {
		type: Boolean,
		default: true,
	},
	// 当前页
	pageNum: {
		type: Number,
		default: 1,
	},
	// 页容量
	pageSize: {
		type: Number,
		default: 10,
	},
	// empty的图片大小
	emptyImgSize: {
		type: Number,
		default: 200,
	},
	// 搜索区域是否显示
	isShowSearchRegion: {
		type: Boolean,
		default: true,
	},

	// 是否展示连续序号
	isSerialNo: {
		type: Boolean,
		default: true,
	},
};

global.d.ts


// new-table
//表头数据类型定义
declare interface TableHeader<T = any> {
	label: string;
	prop: string;
	align?: string;
	overHidden?: boolean;
	minWidth?: string;
	sortable?: boolean;
	type?: string;
	fixed?: string;
	width?: string;
	isActionColumn?: boolean;
	isCustomizeColumn?: boolean;
	slotKey?: string;
}

/*
 允许任何字符串作为索引
 不然会报错, 使用动态属性名,需要使用索引签名
*/
declare type SearchFormType = {
	[key: string]: string;
};

declare type FormOptions = {
	type: string;
	props: {
		label: string;
		placeholder: string;
		type: string;
		clearable: boolean;
	};
	vm: string;
	selectOptions?: [
		{
			value: string | number;
			label: string;
		}
	];
	cascaderOptions?: any;
};

全局css

/* 全局样式 个人不允许修改全局样式【重要!!!】*/

/* 开关样式
------------------------------------------------ */

.el-switch__label--left {
	position: relative;
	left: 45px;
	color: #fff;
	z-index: -1111;
}

.el-switch__core {
	width: 50px !important;
}

.el-switch__label--right {
	position: relative;
	right: 46px;
	color: #fff;
	z-index: -1111;
}

.el-switch__label--right.is-active {
	z-index: 1111;
	color: #fff !important;
}

.el-switch__label--left.is-active {
	z-index: 1111;
	color: #fff !important;
}


/* 表格样式
------------------------------------------------ */
.el-table thead tr,
.el-table tbody tr>td {
    padding: 0 !important;
    height: 75px;
    line-height: 75px;
    color: #909399;
}

.el-table thead tr>th {
    background-color: var(--el-color-info-light-8) !important;
}

newForm

<template>
	<el-form ref="searchFormRef" :model="searchForm" size="default">
		<!-- 使用了不稳定的 key,可能会导致一些不可预期的行为,比如输入框失去焦点。 -->
		<el-row>
			<el-col
				:xs="24"
				:sm="24"
				:md="24"
				:lg="12"
				:xl="6"
				v-for="item in formOptions"
				:key="item.vm"
			>
				<el-form-item :label="item.props.label" :prop="item.vm">
					<el-input
						v-if="item.type === FormOptionsType.INPUT"
						v-model.lazy="searchForm[item.vm]"
						v-bind="item.props"
						class="ml10 w100"
					></el-input>

					<el-select
						v-if="item.type === FormOptionsType.SELECT"
						v-model.lazy="searchForm[item.vm]"
						v-bind="item.props"
						class="ml10 w100"
					>
						<el-option
							v-for="option in item.selectOptions"
							:key="option.value"
							:label="option.label"
							:value="option.value"
						/>
					</el-select>

					<el-cascader
						v-if="item.type === FormOptionsType.CASCADER"
						v-model.lazy="searchForm[item.vm]"
						:options="item.cascaderOptions"
						v-bind="item.props"
						class="ml10 w100"
					/>

					<el-date-picker
						v-if="item.type === FormOptionsType.DATE_PICKER"
						v-model.lazy="searchForm[item.vm]"
						v-bind="item.props"
						class="ml10 w100"
					/>
				</el-form-item>
			</el-col>
			<el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="6" class="xs-mt">
				<el-form-item style="margin-left: 10px">
					<el-button @click="onReset">
						<SvgIcon name="ant-ReloadOutlined"></SvgIcon>
						重置
					</el-button>
					<el-button type="primary" @click="onSearch">
						<SvgIcon name="ant-SearchOutlined"></SvgIcon>
						查询
					</el-button>
				</el-form-item>
			</el-col>
		</el-row>
	</el-form>
</template>

<script setup lang="ts" name="newForm">
import { toRefs, onBeforeUnmount, ref } from 'vue';
import type { PropType } from 'vue';
import { type FormInstance } from 'element-plus';

const searchFormRef = ref<FormInstance>();

enum FormOptionsType {
	INPUT = 'input', // 输入框
	SELECT = 'select', // 下拉框
	CASCADER = 'cascader', // 级联选择器
	DATE_PICKER = 'date-picker', // 日期选择器
}

const props = defineProps({
	formOptions: {
		type: Array as PropType<FormOptions[]>,
		required: true,
	},
	searchForm: {
		type: Object as PropType<SearchFormType>,
		required: true,
	},
});
const { formOptions, searchForm } = toRefs(props);

const emit = defineEmits(['reset', 'search']);

const onReset = () => emit('reset');
const onSearch = () => emit('search');
onBeforeUnmount(() => searchFormRef.value?.resetFields());
defineExpose({ searchFormRef });
</script>

<style scoped lang="scss">
:deep(.el-form-item__label) {
	margin-left: 10px;
}
</style>

封装完成,下面在使用的页面调用

页面使用:

<template>
	<div class="container-wrapper">
		<!-- 动态 page -->
		<new-table
			:tableHeader="tableHeader"
			:tableData="tableData"
			:pageNum="pageNum"
			:pageSize="pageSize"
			:total="pageTotal"
			@handleSizeChange="onHandleSizeChange"
			@handleCurrentChange="onHandleCurrentChange"
			@handleEdit="onHandleEdit"
			@handleDelete="onHandleDelete"
		>
			<template #search>
				<new-form
					:formOptions="formOptions"
					:searchForm="searchForm"
					@reset="onReset"
					@search="onSearch"
				/>
			</template>

			<template #switch="{ row }">
				<el-switch
					v-model="row.fileStatus"
					active-text="开"
					inactive-text="关"
					:active-value="1"
					:inactive-value="2"
					active-color="#13ce66"
					inactive-color="#ff4949"
					@change="changeSwitchStatus(row.id, row.fileStatus)"
				/>
			</template>
		</new-table>
	</div>
</template>

<script setup lang="ts" name="algorithmRegistrationQuery">
import { onMounted, reactive, toRefs } from "vue";
// import { getTestList } from "/@/api/encryptionAlgorithm/templateDefinition";
// import { STATUS_CODE } from "/@/enum/global";
const state = reactive({
	//表头数据
	// el-table-column有的属性都可以在这传
	tableHeader: <TableHeader[]>[
		{ label: "姓名", prop: "uname" },
		{ label: "年龄", prop: "age" },
		{ label: "性别", prop: "sex", slotKey: "switch" },
		{ label: "操作", fixed: "right", slotKey: "default" },
	],
	//表项数据
	tableData: [
		{ uname: "小帅", age: "18", sex: "男", status: false, id: 1 },
		{ uname: "小美", age: "148", sex: "女", status: false, id: 2 },
		{ uname: "小明", age: "12", sex: "男", status: true, id: 3 },
		{ uname: "小红", age: "12", sex: "女", status: false, id: 4 },
		{ uname: "小黑", age: "12", sex: "男", status: true, id: 5 },
		{ uname: "小白", age: "12", sex: "女", status: false, id: 6 },
		{ uname: "小黑", age: "12", sex: "男", status: true, id: 7 },
		{ uname: "小白", age: "12", sex: "女", status: false, id: 8 },
		{ uname: "小黑", age: "12", sex: "男", status: true, id: 9 },
		{ uname: "小白", age: "12", sex: "女", status: false, id: 10 },
		{ uname: "小黑", age: "12", sex: "男", status: true, id: 11 },
	],
	formOptions: <FormOptions[]>[
		{
			type: "input",
			props: {
				label: "合规规则",
				placeholder: "请输入合规规则",
				type: "text",
				clearable: true,
			},
			vm: "knowledgeName",
		},
		{
			type: "input",
			props: {
				label: "文件数量",
				placeholder: "请输入文件数量",
				type: "text",
				clearable: true,
			},
			vm: "documentNumber",
		},
		// 下拉选择器
		{
			type: "select",
			props: {
				label: "所属部门",
				placeholder: "请选择",
				clearable: true,
			},
			vm: "department",
			selectOptions: [
				{
					label: "数据安全",
					value: 1,
				},
				{
					label: "研发",
					value: 2,
				},
				{
					label: "事业",
					value: 3,
				},
			],
		},
		// 时间范围选择器
		{
			type: "date-picker",
			props: {
				label: "时间范围",
				type: "datetimerange", // datetimerange范围 datetime日期
				clearable: true,
				"range-separator": "-",
				"start-placeholder": "开始日期",
				"end-placeholder": "结束日期",
				"value-format": "YYYY-MM-DD HH:mm:ss",
			},
			vm: "createTime",
		},

		// 级联选择器
		{
			type: "cascader",
			props: {
				label: "所属部门",
				placeholder: "请选择",
				clearable: true,
			},
			vm: "cascader",
			cascaderOptions: [
				{
					value: "guide",
					label: "Guide",
					children: [
						{
							value: "disciplines",
							label: "Disciplines",
							children: [
								{
									value: "consistency",
									label: "Consistency",
								},
							],
						},
						{
							value: "navigation",
							label: "Navigation",
							children: [
								{
									value: "side nav",
									label: "Side Navigation",
								},
								{
									value: "top nav",
									label: "Top Navigation",
								},
							],
						},
					],
				},
				{
					value: "component",
					label: "Component",
					children: [
						{
							value: "basic",
							label: "Basic",
							children: [
								{
									value: "button",
									label: "Button",
								},
							],
						},
						{
							value: "form",
							label: "Form",
							children: [
								{
									value: "radio",
									label: "Radio",
								},
								{
									value: "checkbox",
									label: "Checkbox",
								},
							],
						},
						{
							value: "data",
							label: "Data",
							children: [
								{
									value: "table",
									label: "Table",
								},
							],
						},
						{
							value: "notice",
							label: "Notice",
							children: [
								{
									value: "alert",
									label: "Alert",
								},
							],
						},
						{
							value: "navigation",
							label: "Navigation",
							children: [
								{
									value: "menu",
									label: "Menu",
								},
							],
						},
						{
							value: "others",
							label: "Others",
							children: [
								{
									value: "dialog",
									label: "Dialog",
								},
							],
						},
					],
				},
				{
					value: "resource",
					label: "Resource",
					children: [
						{
							value: "axure",
							label: "Axure Components",
						},
					],
				},
			],
		},
	],
	//这里允许动态属性所以可为空
	searchForm: <SearchFormType>{},
	pageNum: 1,
	pageSize: 10,
	pageTotal: 0,
});
const {
	tableHeader,
	tableData,
	formOptions,
	searchForm,
	pageNum,
	pageSize,
	pageTotal,
} = toRefs(state);

// 修改
const onHandleEdit = (row: object) => {
	console.log(row);
};

// 删除
const onHandleDelete = (row: object) => {
	console.log(row);
};

// switch
const changeSwitchStatus = (id: number, status: boolean) => {
	console.log(id, status);
};

//页容量改变
const onHandleSizeChange = (val: number) => {
	// console.log('页容量 ==>:', val);
	pageSize.value = val;
	getTableList(pageNum.value, pageSize.value);
};
//当前分页改变
const onHandleCurrentChange = (val: number) => {
	// console.log('当前页 🚀 ==>:', val);
	pageNum.value = val;
	getTableList(pageNum.value, pageSize.value);
};

// 获取表项数据
const getTableList = (pageNum: number, pageSize: number) => {
	// 处理searchForm.value createTime
	if (searchForm.value.createTime) {
		searchForm.value.startTime = searchForm.value.createTime[0];
		searchForm.value.createTimeEnd = searchForm.value.createTime[1];
		delete searchForm.value.createTime;
	}
	// getTestList({
	// 	pageNum,
	// 	pageSize,
	// 	...searchForm.value,
	// }).then((res) => {
	// 	// if (res.code !== STATUS_CODE.SUCCESS) return;
	// 	const { list, total } = res.data;
	// 	tableData.value = list;
	// 	// console.log('🤺🤺 表项 🚀 ==>:', list);
	// 	pageTotal.value = total;
	// });
};

// 重置
const onReset = () => {
	searchForm.value = {};
	getTableList(pageNum.value, pageSize.value);
};
// 查询
const onSearch = () => {
	console.log("🤺🤺 查询表单数据 ==>:", searchForm.value);
	// 获取表项数据
	getTableList(pageNum.value, pageSize.value);
};

onMounted(() => getTableList(pageNum.value, pageSize.value));
</script>

效果: 

源码:huozaifenlangli/Vue3-template: 🎉🎉🔥 Vite + Vue3 + Ts + router + Vuex + axios + eslint 、prettier、stylelint、husky、gitCommit --- 集成多种组件、Hooks支持开封即用,严格的代码质量检验、祝您轻松上大分😷🤺🤺🤺 【动态路由、特效、N个组件、N个自定义指令...】 (github.com)icon-default.png?t=N7T8https://github.com/huozaifenlangli/Vue3-template


el-tooltip 

子组件:

<template>
	<!-- 省略号文字显示不全鼠标放在文字弹框显示所有详情 el-tooltip 子组件 page -->
	<el-tooltip :show-after="200">
		<template #content>
			<div v-html="content" class="text" :style="{ width: myWidth }"></div>
		</template>
		<slot name="slotView" :num="num"></slot>
	</el-tooltip>
</template>
<script setup lang="ts">
let num = 666;
// js
defineProps({
	content: {
		type: String,
		default: '',
	},
	myWidth: {
		type: String,
		default: '',
	},
});
</script>

<style lang="scss" scoped>
.text {
	color: #fff;
	word-break: break-all;
	word-spacing: 2px;
	letter-spacing: 2px;
	line-height: 1.5;
	font-style: 14px;
}
</style>

父组件:

<template>
	<div>
		<!-- 父组件 page -->
		<mt-tip :content="htmlStr" my-width="700px">
			<template v-slot:slotView="slotProps">
				<el-button type="warning">按钮——1——————{{ slotProps }}</el-button>
			</template>
		</mt-tip>
		<mt-tip :content="htmlStr" my-width="500px">
			<template #slotView="slotProps">
				<el-button type="warning">按钮——2——————{{ slotProps }}</el-button>
			</template>
		</mt-tip>
	</div>
</template>

<script setup lang="ts">
import mtTip from './chidren.vue';
let htmlStr = `我是一个字符串你说我是不是我是一个字符串你说我是不是我是一个字符串你说我是不是我是一个字符串你说我是不是我是一个字符串你说我是不是我是一个字符串你说我是不是我是一个字符串你说我是不是我是一个字符串你说我是不是我是一个字符串你说我是不是-·····`;
</script>

<style lang="scss" scoped></style>


el-dialog

子组件:

<template>
	<el-dialog
		v-bind="$attrs"
		v-if="visible"
		:title="myTitle"
		:width="myWidth"
		:before-close="handleClose"
		@open="handleOpen"
	>
		<!-- 封装Dialog子组件 page -->
		<div v-loading="myLoading">
			<slot name="main"></slot>
			<slot name="msg"></slot>
			<span class="dialog-footer">
				<el-button type="default" @click="cancel">关闭</el-button>
				<el-button :type="props.myBtnText[0]" @click="define">{{
					props.myBtnText[1]
				}}</el-button>
			</span>
		</div>
	</el-dialog>
</template>

<script setup lang="ts">
import { computed, watch } from 'vue';
export interface Props {
	myTitle: string;
	myVisible: boolean;
	myWidth?: string;
	myLoading?: boolean;
	myBtnText?: Array<string>;
}
const props = withDefaults(defineProps<Props>(), {
	myWidth: '60%',
	myLoading: false,
	myBtnText: () => ['primary', '确定'],
});
const emits = defineEmits(['update:modelValue', 'closeDialog', 'closeDefine', 'handleOpen']);

const visible = computed({
	get: () => props.myVisible,
	set: (value) => {
		// console.log('!这里输出 🚀 ==>:', value);
		emits('update:modelValue', value);
	},
});

//取消按钮事件
const cancel = () => {
	emits('closeDialog');
};

// 确定按钮事件
const define = () => {
	emits('closeDefine', props.myTitle);
};

// 弹框打开事件
const handleOpen = () => {
	emits('handleOpen');
};

// 关闭之前事件
const handleClose = () => {
	// console.log('!这里输出 🚀 ==>:', '关闭之前事件');
	emits('closeDialog');
};
</script>

<style lang="scss" scoped>
.dialog-footer {
	display: flex;
	justify-content: center;
	margin-top: 20px;
	width: 100%;
}
</style>

全局动画弹框css

/* 全局样式 个人不允许修改全局样式【重要!!!】*/

/* 全部dialog进入动画
------------------------------------------------ */
.dialog-fade-enter-active {
	display: inline-block;
	animation: zoomIn;
	animation-duration: 0.5s;
	background-color: rgba(0, 0, 0, 0);
}

@keyframes zoomIn {
	from {
		opacity: 0;
		-webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -12.5rem, 0);
		transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -12.5rem, 0);
		-webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
		animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
	}

	60% {
		opacity: 1;
		-webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 0.75rem, 0);
		transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 0.75rem, 0);
		-webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
		animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
	}
}



/* 全部dialog离开动画
------------------------------------------------ */
.dialog-fade-leave-active {
	display: inline-block;
	animation: zoomOut;
	animation-duration: 0.5s;
	background-color: rgba(0, 0, 0, 0);
}


@keyframes zoomOut {
	from {
		opacity: 1;
	}

	60% {
		opacity: 0;
		-webkit-transform: scale3d(0.3, 0.3, 0.3);
		transform: scale3d(0.3, 0.3, 0.3);
	}

	to {
		opacity: 0;
	}
}

父组件:

<template>
	<div>
		<el-button type="primary" size="small" @click="handlerOpen">详情</el-button>

		<zw-dialog
			@closeDialog="handlerCloseDialog"
			@closeDefine="HandleSubmit"
			@open="handlerOpen"
			v-bind="myProps"
			:myLoading="Loading"
			v-model="myProps.myVisible"
		>
			<template #msg> 我是msg </template>
			<template #btn="propsSlots">
				<el-button type="danger">删除-{{ propsSlots.num }}</el-button>
			</template>
		</zw-dialog>
	</div>
</template>

<script lang="ts" setup>
import { ref } from 'vue';

// 控制子组件的显示隐藏
const dialogVisible = ref(false);
// 控制子组件的loading
const Loading = ref(false);

// 弹框打开
const handlerOpen = () => {
	console.log('弹框打开了');
	Loading.value = true;
	dialogVisible.value = true;
	// 关闭弹框loading 此处应该根据业务逻辑来控制loading的关闭【重要】
	setTimeout(() => {
		Loading.value = false;
		console.log('!这里输出 🚀 ==>:三秒关闭', Loading.value);
	}, 3000);
};

// 确定
const HandleSubmit = () => {
	dialogVisible.value = false;
	console.log('点击了确定');
};

// 取消
const handlerCloseDialog = () => {
	dialogVisible.value = false;
	console.log('点击了取消');
};

const myProps = ref({
	myTitle: '编辑',
	myVisible: dialogVisible,
	myWidth: '45%',
	myLoading: Loading,
	myBtnText: ['primary', '保存'],
});
</script>

 本文完!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龙雨溪 彩色之外

你的打赏是我创作的氮气加速动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值