vue3多个页面/组件,复用同一段render页面根据接口返回的数据动态渲染逻辑,使用setup+组合式API

组件reusableComponent是可供多个组件复用的
getData请求接口,render渲染数据

<template>
	<render />
template>
<script setup>
import operationService from "@/views/operation/component/operationService.vue";

const props = defineProps({
	name: {
		type: String,
		default: "operation"
	}
});

const { name } = toRefs(props);

import baffle from "@/hooks/baffle";
import { dpSeatDrapMapList } from "@/api/commonApi";
import {
	ref,
	onMounted,
	nextTick,
	h,
	toRefs,
	getCurrentInstance,
	withScopeId
} from "vue";
onMounted(() => {
	nextTick(() => {
		init();
		getData();
	});
});


const getData = async () => {
	let res = await dpSeatDrapMapList({
		pageSize: "1000",
		pageNo: "1"
	});
	pageData.value = res.result.records;
	/*返回的示例:
	{"success":true,"message":"查询成功!","code":200,
	"result":{"records":[{"drapId":"1888858684486922242","regOper":"admin","updateDate":null,"drapType":"operation","drapAttr":"operation2","regDate":"2025-02-10 15:52:58","drapY":"0","drapW":"12","drapX":"12","drapH":"13","updateOper":null,"drapText":"接通率/服务水平"},,{"drapId":"1888858684486922243","regOper":"admin","updateDate":null,"drapType":"operation","drapAttr":"operation2","regDate":"2025-02-10 15:52:58","drapY":"0","drapW":"12","drapX":"0","drapH":"13","updateOper":null,"drapText":"运营服务总览"},{"drapId":"1889145339345186818","regOper":"","updateDate":null,"drapType":"operation","drapAttr":"operation3","regDate":"2025-02-11 10:52:02","drapY":"0","drapW":"12","drapX":"0","drapH":"15","updateOper":null,"drapText":"今日服务之星"},{"drapId":"1889145340599283714","regOper":"","updateDate":null,"drapType":"operation","drapAttr":"operation3","regDate":"2025-02-11 10:52:02","drapY":"15","drapW":"12","drapX":"0","drapH":"19","updateOper":null,"drapText":"客户情绪意图"},{"drapId":"1889145340611866626","regOper":"","updateDate":null,"drapType":"operation","drapAttr":"operation3","regDate":"2025-02-11 10:52:02","drapY":"15","drapW":"12","drapX":"12","drapH":"19","updateOper":null,"drapText":"客户热词云图"},{"drapId":"1889149440623718401","regOper":"","updateDate":null,"drapType":"operation","drapAttr":"operation3","regDate":"2025-02-11 11:08:20","drapY":"0","drapW":"12","drapX":"12","drapH":"15","updateOper":null,"drapText":"今日客户来电问题TOP5"},{"drapId":"1888770651225862146","regOper":"admin","updateDate":"2025-02-12 16:19:50","drapType":"operation","drapAttr":"operation1","regDate":"2025-02-10 10:03:09","drapY":"8","drapW":"12","drapX":"12","drapH":"11","updateOper":"","drapText":"VIP客户来电详情"},{"drapId":"1888770651263610882","regOper":"admin","updateDate":"2025-02-12 16:20:19","drapType":"operation","drapAttr":"operation1","regDate":"2025-02-10 10:03:09","drapY":"19","drapW":"24","drapX":"0","drapH":"14","updateOper":"","drapText":"外呼数据"},{"drapId":"1888770651192307713","regOper":"admin","updateDate":"2025-02-12 16:20:19","drapType":"operation","drapAttr":"operation1","regDate":"2025-02-10 10:03:09","drapY":"33","drapW":"24","drapX":"0","drapH":"15","updateOper":"","drapText":"三渠道客户等待情况"},{"drapId":"1889925330341277697","regOper":"","updateDate":null,"drapType":"satisfaction","drapAttr":"satisfaction","regDate":"2025-02-13 14:31:26","drapY":"0","drapW":"6","drapX":"0","drapH":"30","updateOper":null,"drapText":"参评率分析"},...更多数据]}}*/
	// console.table(pageData.value);
};
import { useCommonStore } from "@/store/modules/common";

const render = () => {
	const store = useCommonStore();
	//接口数据未返回的时候,页面loading,
	if (!notEmptyArray(pageData.value)) {
		store.startLoading();
		console.log("加载中");
		return h("div", "");
	} else {
	//接口数据有了之后,渲染页面
	//接口数据中drapAttr标识归属于哪个页面,operation2是页面代号,name是父组件传递的要渲染的页面的代号,筛选对应的数据,就是要渲染的组件集合。数据drapText是组件的标识(transObj变量),根据这个来决定组件数组(componentsArray变量)中哪些组件被渲染
		let pageList = pageData.value.filter(item => {
			return item.drapAttr && item.drapAttr === name.value;
		});
		//初始化空的虚拟节点数组
		let VNodeList = [];

	//解析每个模块的宽高 位置并将待渲染的style对象返回,"drapY":"0","drapW":"12","drapX":"12","drapH":"13"分别代表y x width height 
	//因为数据来源页面用了vue-grid-layout,生成的数据都是layout: [
     //           {"x":0,"y":0,"w":2,"h":2,"i":"0"},
     //           {"x":2,"y":0,"w":2,"h":4,"i":"1"},
     //       ],这样的代表位置和宽高,把一个单位的宽高代表的实际长度换算清楚后,渲染到页面上
	let pageNameSet = new Set(pageList.map(compare));
		const calcW = w => w * 3.993 + "vw";
		const calcH = h => h * 1.791 + "vh";
		const getStyle = name => {
			let infoArr = pageList.filter(item => {
				return item.drapText === name;
			});
			let infoObj = {};
			if (notEmptyArray(infoArr)) {
				infoObj = infoArr[0];
			}
			const { drapW, drapH, drapX, drapY } = infoObj;
			return {
				width: calcW(drapW),
				height: calcH(drapH),
				position: "absolute",
				left: calcW(drapX),
				top: calcH(drapY),
				padding: "5px",
				boxSizing: "border-box"
			};
		};

		let transObj = {
			VIPCustomer: "VIP客户来电详情",
			//...待渲染的页面子组件与接口返回的数据的映射关系
		}
		let componentsArray = [
			VIPCustomer,
			//...待渲染的页面子组件
			]
			//根据接口返回的数据,筛选属于name的pageNameSet后,过滤出属于pageNameSet的组件并返回
	const getVNode = (name, componentName) => {
			if (pageNameSet.has(name)) {
				return h(
					"div",
					{
						style: getStyle(name)
					},
					[h(componentName, { translateObj: translateObj.value })]
				);
			}
		};
		//过滤出属于pageNameSet的组件并压入VNodeList虚拟dom数组,待渲染
		componentsArray.map(item => {
			if (transObj[item.__name] && getVNode(transObj[item.__name])) {
				VNodeList.push(getVNode(transObj[item.__name], item));
			}
		});
		//结束页面loading
		store.finishLoading();
		//使用VNodeList渲染页面
		return h(
			"div",
			{ class: "operation1_body operation1" },
			h("div", { class: "operation1_content" }, VNodeList)
		);
	}
};
script>
<style lang="less" >
.operation1_body {
	margin: 2vh auto 0 !important;
	// display: grid;
	width: 95.8vw;
	// grid-template-columns: 1fr 1fr;
	// grid-row-gap: 1vh;
	// grid-column-gap: 1vh;
	background: #001c5c;
}
.operation1_content {
	position: relative;
	width: 100%;
	height: 100%;
}style >

父页面使用组件并传name进去渲染指定的页面

<template>
	<div>
		<reusableComponent :name="name" />
	div>
template>
<script setup>
import reusableComponent from "@/components/reusableComponent";
import { ref } from "vue";
const name = ref("sit2");//operation/sit/sit3/sit2/operation1/operation2这些页面可供渲染
script>
<style lang="less" scoped>
.tbody {
	margin: 2vh auto 0;
	display: grid;
	width: 95.8vw;	
	background: #001c5c;
}
style>

补充一下:
1.这些子组件原本有watch父组件的传参translateObj,但是在切换sit sit2等页面的时候,watch不执行,可能是生命周期等问题导致的吗,页面加载时好像watch不是第一次传入,或者未更改,之前正常写页面可以watch到translateObj,使用这种方式(公共组件动态渲染页面时候)页面生成没有watch translateObj的逻辑没有执行;在watch里加入immediate:true,deep:true后,解决了这个问题;原理:加入immediate后,watch里的逻辑会在本组件初次加载时执行一次,而不是只在translateObj变化时执行。

2.render的h函数渲染时,标签进行参数传递:textActuslArrival和回调函数的@getDelay =>onGetDelay: getDelayData的写法 以及v-model:showFieldSupport的替代写法,

	if (pageNameSet.has(name)) {
				console.log("name,name", name, componentName && componentName.__name);
				let compName = componentName && componentName.__name;
				return h(
					"div",
					{
						// 渲染style样式
						style: Object.assign(
							{},
							getStyle(name),

							compName === "employeeStatusWarning" ||
								compName === "fieldSupport"
								? { pointerEvents: "none" }
								: {}
						)
					},
					[
						h(
							componentName,
							Object.assign(
								{},
								{
									// 参数translateObj,transInfo
									translateObj: translateObj.value,
									transInfo: translateArray.value

									// class: componentName && componentName.__name
								},
								// 参数textActuslArrival,callAndVideoActuslArrival
								compName === "workStaff" || compName === "sitMap"
									? {
											textActuslArrival: textActuslArrival.value,
											callAndVideoActuslArrival: callAndVideoActuslArrival.value
									  }
									: {},
								// 参数delayData  :delayData="delayData"
								compName === "delayData" ? { delayData: delayData.value } : {},
								//  :showFieldSupport="showFieldSupport" @updateShowFieldSupport = "()=>{	showFieldSupport.value = value;}"
								compName === "fieldSupport" || compName === "sitMap"
									? {
											showFieldSupport: showFieldSupport.value,
											onUpdateShowFieldSupport: value => {
												console.log("onUpdate:modelValue", value);
												showFieldSupport.value = value;
											}
									  }
									: {},
								// 	@getDelay="getDelayData"
								compName === "sitMap"
									? {
											onGetDelay: getDelayData
									  }
									: {}
							)
						)
					]
				);
			}


子组件

 const props = defineProps({
	// 显示支援弹窗
	showFieldSupport: {
		type: Boolean,
		default: false
	},	emit("updateShowFieldSupport", !props.showFieldSupport);
	```

你可能感兴趣的:(vue,web前端,前端,render,动态渲染页面)