组件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);
```