优化计划选择BIM性能

dev_xd
lj7788@126.com 2025-06-28 17:43:50 +08:00
parent 72b770d626
commit ec334f0461
3 changed files with 965 additions and 1 deletions

View File

@ -0,0 +1,416 @@
import { modelTreeAllChild } from "@/api/bim/bim";
import { getModelTree, getTreeAllLeafChild } from "@/api/bim/bimModel";
function selectFeature(that, featureId) {
let api = bimSelectionDlgApi;
if (that.selectItems.includes(featureId)) {
api.Feature.setColor(featureId, "rgba(255,255,255,1)");
let index = that.selectItems.indexOf(featureId);
if (index > -1) {
that.selectItems.splice(index, 1);
}
} else {
api.Feature.setColor(featureId, "rgba(255,0,255,1)");
if (!that.showMode) {
api.Feature.setVisible(featureId, false);
}
that.selectItems.push(featureId);
}
}
function hideFeatures(that) {
let tmps = that.allBimData.filter((it) => it.id != that.plan.id);
tmps.forEach((it) => {
it.bim.forEach((it) => {
hideFeature(that, it);
});
});
}
function getHideFeatures(that, checkedKeys) {
let result = [];
let tmps = that.allBimData.filter((it) => it.id != that.plan.id);
tmps.forEach((it) => {
it.bim.forEach((it) => {
result.push(it);
});
});
if (checkedKeys.length > 0) {
tmps = that.allFeatures
.filter((d) => !checkedKeys.includes(d.glid))
.filter((d) => d != "0")
.map((d) => d.featureId);
tmps.forEach((d) => {
result.push(d);
});
}
return result;
}
function hideFeature(that, featureId) {
let api = bimSelectionDlgApi;
api.Feature.setVisible(featureId, false);
}
function selectSingle(that) {
let api = bimSelectionDlgApi;
api.Public.clearHandler();
api.Feature.getByEvent(true, (n) => {
if (n && n["id"]) {
let featureId = n.id;
let modelId = featureId.split("^")[0];
selectFeature(that, featureId);
}
});
}
function clearAllSelection(that) {
that.$modal.confirm("确定清除所有已选择构件吗?").then(() => {
let api = bimSelectionDlgApi;
that.selectItems.forEach((d) => {
api.Feature.setColor(d, "rgba(255,255,255,1)");
});
that.selectItems = [];
this.selEl++;
});
}
function initBim(that) {
let tmps = that.allBimData.filter((it) => it.id == that.plan.id);
if (tmps.length > 0) {
tmps[0].bim.forEach((it) => {
selectFeature(that, it);
});
}
}
function getSelectFeatureIds(that) {
let tmps = that.allBimData.filter((it) => it.id == that.plan.id);
if (tmps.length > 0) {
return tmps[0].bim || [];
}
return [];
}
// 加载部分模型(计划中绑定的模型)
function partLoadModel(that) {
let featureIds = getSelectFeatureIds(that);
let api = bimSelectionDlgApi;
let obj = {};
for (let i = 0; i < featureIds.length; i++) {
let it = featureIds[i];
let tmps = it.split("^");
let modelId = tmps[0];
if (!obj[modelId]) {
obj[modelId] = [];
}
obj[modelId].push(it);
}
for (let modelId in obj) {
let tmps = that.models.filter((it) => it.modelId == modelId);
setTimeout(() => {
that.$refs.tree.setChecked(modelId, true, true);
}, 1000);
if (tmps.length > 0) {
let url = `${window.config.modelUrl}/Tools/output/model/${modelId}/root.glt`;
that.partLoad = true;
api.Model.addPart(
url,
modelId,
obj[modelId].join("#"),
(res) => {},
(res) => {
that.viewPoint = p;
that.$message.info("模型加载完成");
that.bimLoaded = true;
that.doMenu(2);
initBim(that);
},
{
FlyTo: true,
}
);
}
}
if (featureIds.length == 0) {
setTimeout(() => {
that.$refs.tree.setChecked("root", true, true);
}, 1000);
loadModels(that);
let func = () => {
if (that.loadedModelCount == that.models.length) {
setTimeout(() => {
setFeatueShowOrHide(that, []);
}, 1000);
} else {
setTimeout(func, 100);
}
};
func();
}
}
//构建树形数据
function buildTreeData(that) {
that.modelTrees = [
{
title: "项目模型",
level: 0,
type: "root",
key: "root",
children: [],
hadLoad: true,
},
];
that.models.forEach((d) => {
let node = {
title: d.modelName,
level: 1,
type: "model",
hasLoad: false,
modelId: d.lightweightName,
key: d.lightweightName,
externalId: "0",
glid: "",
children: [],
data: d,
};
that.modelTrees[0].children.push(node);
getModelFeatures(that, d.lightweightName, node);
});
that.treeExpendedKeys.push("root");
that.$message.info("模型构件信息加载完成");
}
//获取模型所有构件
function getModelFeatures(that, modelId, node) {
let tmps = that.allBimData.filter((it) => it.id != that.plan.id);
tmps.forEach((it) => {
it.bim.forEach((it) => {
if (!that.readlyParts.includes(it)) {
that.readlyParts.push(it);
}
});
});
//获取模型构件
modelTreeAllChild(modelId, "").then((res) => {
let objs = res.data || [];
objs.forEach((o) => {
o.modelId = modelId;
o.featureId = o.modelId + "^" + o.externalId;
o.name = o.name || "";
o.name = o.name.replaceAll('"', "").replaceAll("'", "").replaceAll("\\", "");
o.info = `[${o.externalId}]${o.groupname}-${o.name}`;
if (o.externalId == "0") {
return;
}
let featureId = o.modelId + "^" + o.externalId;
if (!that.allParts.includes(featureId)) {
that.allParts.push(featureId);
}
});
that.allFeatures = objs;
let makeTree = (tmps) => {
tmps.forEach((item) => {
item.children = objs.filter((it) => it.pglid == item.glid);
if (item.children.length > 0) {
makeTree(item.children);
}
item.hasLoad = true;
item.title = item.name;
item.key = item.glid;
item.modelId = node.modelId;
});
return tmps;
};
node.children = makeTree(objs.filter((item) => item.level == 0));
that.treeKey++;
});
that.showParts = that.allParts.filter((it) => !that.hideParts.includes(it));
}
//树上点击事件
function onCheckTree(that, node, event) {
let api = bimSelectionDlgApi;
if (that.partLoad) {
that.$refs.tree.setChecked("root", false, true);
that.partLoad = false;
that.models.forEach((it) => {
api.Model.remove(it.modelId);
});
return;
}
if (loadModels(that) > 0) {
let func = () => {
if (that.loadedModelCount == that.models.length) {
setTimeout(() => {
setTreeNodeChecked(that, node, event);
}, 1000);
} else {
setTimeout(func, 100);
}
};
func();
} else {
setTreeNodeChecked(that, node, event);
}
}
//根据选择控制构件的隐藏和显示
function setTreeNodeChecked(that, node, event) {
let api = bimSelectionDlgApi;
let checked = event.checkedNodes.includes(node);
if (node.type == "root") {
if (checked) {
that.models.forEach((it) => {
api.Model.setVisible(it.modelId, true);
api.Model.original(it.modelId);
});
} else {
that.models.forEach((it) => {
api.Model.setVisible(it.modelId, false);
});
}
} else if (node.type == "model") {
if (checked) {
api.Model.setVisible(node.modelId, true);
api.Model.original(node.modelId);
} else {
api.Model.setVisible(node.modelId, false);
}
} else {
api.Model.original(node.modelId);
}
setFeatueShowOrHide(that, event.checkedKeys);
}
//处理构件的隐藏和显示
function setFeatueShowOrHide(that, checkedKeys) {
let api = bimSelectionDlgApi;
//隐藏构件
let hideFeatures = getHideFeatures(that, checkedKeys);
api.Feature.setVisible(hideFeatures.join("#"), false);
//标注已选择的构件
that.selectItems.forEach((it) => {
api.Feature.setColor(it, "rgba(255,0,255,1)");
if (!that.showMode) {
api.Feature.setVisible(it, false);
}
});
api.Feature.setVisible(that.hideParts.join("#"), false);
}
//加载模型
function loadModels(that) {
let api = bimSelectionDlgApi;
let modelIds = that.models.map((it) => it.lightweightName);
let cnt = 0;
that.loadedModelCount = 0;
modelIds.forEach((modelId) => {
if (!api.m_model.has(modelId)) {
cnt++;
that.addModel(modelId);
return;
}
});
return cnt;
}
//框选构件
function boxSelection(that) {
let api = bimSelectionDlgApi;
api.Feature.boxSelect((data) => {
data.forEach((featureId) => {
selectFeature(that, featureId);
});
});
}
//隐藏构件选择
function hideSelection(that) {
let api = bimSelectionDlgApi;
api.Public.clearHandler();
api.Feature.getByEvent(true, (n) => {
if (n && n["id"]) {
let featureId = n.id;
that.hideParts.push(featureId);
api.Feature.setVisible(featureId, false);
that.hideEl++;
}
});
}
//异步加载树
function loadNode(that, node, resolve) {
if (!that.show) {
return;
}
console.log("node->", node);
if (node.level == 0) {
let nd = [
{
title: "项目模型",
level: 0,
type: "root",
key: "root",
children: [],
hadLoad: true,
},
];
resolve(nd);
setTimeout(() => {
document.querySelectorAll(".bim-selection-dialog .model-tree .el-tree-node")[0].click();
}, 1000);
} else if (node.level == 1) {
let nd = [];
that.models.forEach((d) => {
let mnd = {
title: d.modelName,
level: 1,
type: "model",
hasLoad: false,
modelId: d.lightweightName,
key: d.lightweightName,
externalId: "0",
glid: "",
children: [],
data: d,
};
nd.push(mnd);
});
resolve(nd);
} else {
getTreeChildren(that, node, resolve);
}
}
function getTreeChildren(that, node, resolve) {
let data = node.data;
getModelTree(data.modelId, data.glid).then((d) => {
let tmps = (d.data || []).map((it) => {
let title = it.externalId == 0 ? it.name : it.externalId;
title = title.replaceAll('"', "").replaceAll("'", "").replaceAll("\\", "");
it.title = title;
it.key = it.glid;
it.modelId = data.modelId;
it.type = "data";
it.leaf = +it.externalId != 0;
return it;
});
resolve(tmps);
});
}
export default {
selectSingle,
initBim,
clearAllSelection,
partLoadModel,
buildTreeData,
onCheckTree,
boxSelection,
hideSelection,
loadNode,
};

View File

@ -0,0 +1,548 @@
<template>
<el-dialog v-model="show" append-to-body :close-on-click-modal="false" :close-on-press-escape="false" :title="title" modal-class="bim-selection-dialog">
<div id="bimSelectionDlg">
<div id="bimSelectionDlgContainer" class="bimSelectionDlgContainer"></div>
</div>
<div class="div-left" :class="{ 'is-hide': !leftExpend }">
<div class="div-left-title">
<el-tabs v-model="activeTab">
<el-tab-pane label="结构树" name="a1">
<div class="model-tree scroll" :key="treeKey">
<el-tree
:key="treeKey"
ref="tree"
:default-expanded-keys="treeExpendedKeys"
:props="{
children: 'children',
label: 'title',
isLeaf: 'leaf',
}"
node-key="key"
@check="onCheckTree"
:load="loadNode"
lazy
show-checkbox></el-tree>
</div>
</el-tab-pane>
<el-tab-pane label="已关联" name="a2">
<div class="sel-list scroll" :key="selEl">
<div v-for="(it, idx) in getSelectItems(selectItems)" :key="idx" class="div-sel-item">
<el-tooltip placement="bottom" :content="it.info">
<div class="sel-item-info">{{ it.info }}</div>
</el-tooltip>
<el-button link @click="delSelectItem(idx)"></el-button>
</div>
</div>
</el-tab-pane>
<el-tab-pane label="已隐藏" name="a3">
<div style="padding: 0px 10px 10px" v-show="hideParts.length > 0">
<el-button @click="delAllDelList"></el-button>
</div>
<div class="sel-list scroll hide-list" :key="hideEl">
<div v-for="(it, idx) in getSelectItems(hideParts)" :key="idx" class="div-sel-item">
<el-tooltip placement="bottom" :content="it.info">
<div class="sel-item-info">{{ it.info }}</div>
</el-tooltip>
<el-button link @click="delHideItem(idx)"></el-button>
</div>
</div>
</el-tab-pane>
</el-tabs>
<div class="div-icon">
<el-icon v-show="leftExpend" @click="leftExpend = false"><ArrowUpBold /></el-icon>
<el-icon v-show="!leftExpend" @click="leftExpend = true"><ArrowDownBold /></el-icon>
</div>
</div>
</div>
<div v-show="bimLoaded">
<div class="footer-box" v-if="models.length > 0">
<el-tooltip placement="top" content="主视图">
<div class="footer-btn" @click="doMenu(0)" :class="activeMenu == 0 ? 'is-active' : ''">
<svg-icon icon-class="home" />
</div>
</el-tooltip>
<el-tooltip placement="top" content="取消所有关联">
<div class="footer-btn" @click="doMenu(1)" :class="activeMenu == 1 ? 'is-active' : ''">
<svg-icon icon-class="cancel" />
</div>
</el-tooltip>
<el-tooltip placement="top" content="点选">
<div class="footer-btn" @click="doMenu(2)" :class="activeMenu == 2 ? 'is-active' : ''">
<svg-icon icon-class="pointselect" />
</div>
</el-tooltip>
<el-tooltip placement="top" content="框选">
<div class="footer-btn" @click="doMenu(3)" :class="activeMenu == 3 ? 'is-active' : ''">
<svg-icon icon-class="boundselect" />
</div>
</el-tooltip>
<el-tooltip placement="top" content="隐藏构件">
<div class="footer-btn" @click="doMenu(4)" :class="activeMenu == 4 ? 'is-active' : ''">
<svg-icon icon-class="hide" />
</div>
</el-tooltip>
</div>
</div>
<div class="div-mode">
<el-switch v-model="showMode" class="ml-2" inline-prompt active-text="" inactive-text="" @change="doModeChange" style="--el-switch-off-color: #336699" />
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="show = false">取消</el-button>
<el-button type="primary" @click="doSave"> </el-button>
</div>
</template>
</el-dialog>
</template>
<script>
import useUserStore from "@/store/modules/user";
import { listBimModel } from "@/api/bim/bimModel";
import bimTools from "./bimSelectTools2";
import { updateBimInfo, getPlanAllBimInfo } from "@/api/bim/bim";
import { ElMessage, ElMessageBox } from "element-plus";
export default {
data() {
return {
title: "任务计划绑定BIM",
bimLoaded: false,
loadedModelCount: 0,
show: false,
partLoad: false, //
plan: null,
currentPrjId: null,
currentComId: null,
models: [],
viewPoint: null,
activeTab: "a1",
initSuccess: false,
leftExpend: true,
activeMenu: 0,
treeKey: 0,
elId: 0,
selEl: 0,
hideEl: 0,
showMode: true,
selectItems: [],
allBimData: [],
treeKey: 0,
modelTrees: [],
treeExpendedKeys: ["root"],
allFeatures: [], //
allParts: [], //ID
showParts: [], //ID
readlyParts: [], //ID
};
},
beforeDestroy() {
let api = bimSelectionDlgApi;
if (api) {
api.Public.clearScene();
api.Public.destroy();
window.bimSelectionDlgApi = null;
}
},
methods: {
loadNode(node, resolve) {
bimTools.loadNode(this, node, resolve);
},
delAllDelList() {
this.$modal.confirm("确定删除吗?").then(() => {
let api = bimSelectionDlgApi;
api.Feature.setVisible(this.hideParts.join("#"), true);
this.hideParts = [];
this.hideEl++;
});
},
delHideItem(idx) {
let api = bimSelectionDlgApi;
let featureId = this.hideParts[idx];
api.Feature.setVisible(featureId, true);
this.hideParts.splice(idx, 1);
this.hideEl++;
},
doModeChange() {
let api = bimSelectionDlgApi;
api.Feature.setVisible(this.selectItems.join("#"), this.showMode);
},
delSelectItem(idx) {
this.$modal.confirm("确定删除吗?").then(() => {
let api = bimSelectionDlgApi;
let featureId = this.selectItems[idx];
api.Feature.setColor(featureId, "rgba(255,255,255,1)");
this.selectItems.splice(idx, 1);
this.selEl++;
});
},
onCheckTree(node, event) {
bimTools.onCheckTree(this, node, event);
},
getSelectItems(items) {
return items.map((it) => {
return { info: it };
});
},
doSave() {
updateBimInfo({
id: this.plan.id,
text: this.selectItems.length > 0 ? JSON.stringify(this.selectItems) : "",
}).then((res) => {
if (res.success) {
ElMessage.success("保存成功");
this.show = false;
this.$emit("success");
} else {
ElMessage.error("保存失败");
}
});
},
doMenu(index) {
let api = bimSelectionDlgApi;
if (api) {
if ([0, 2, 4].includes(index)) {
api.Feature.closeBoxSelect();
}
}
switch (index) {
case 0:
this.activeMenu = 0;
this.resetScene();
break;
case 1:
bimTools.clearAllSelection(this);
break;
case 2:
this.activeMenu = 2;
bimTools.selectSingle(this);
break;
case 3:
this.activeMenu = 3;
bimTools.boxSelection(this);
break;
case 4:
this.activeMenu = 4;
bimTools.hideSelection(this);
break;
}
},
showDialog(plan) {
window.bimDlg = this;
this.plan = plan;
this.show = true;
this.activeTab = "a1";
this.userStore = useUserStore();
this.currentPrjId = this.userStore.currentPrjId;
this.currentComId = this.userStore.currentComId;
this.selectItems = [];
this.allBimData = [];
this.treeKey = 0;
this.partLoad = false;
this.modelTrees = [];
this.treeExpendedKeys = [];
this.allParts = [];
this.hideParts = [];
this.readlyParts = [];
this.allFeatures = [];
this.bimLoaded = false;
this.partLoad = false;
this.elId = 0;
this.hideEl = 0;
this.showMode = true;
this.loadedModelCount = 0;
getPlanAllBimInfo(this.plan.projectId).then((res) => {
this.allBimData = (res.data || []).map((it) => {
it.bim = this.$tryToJson(it.bimId || "[]", []);
return it;
});
});
this.initEngine();
},
initEngine() {
this.elId++;
setTimeout(() => {
this.loadEngine();
}, 10);
},
loadEngine() {
window.bimSelectionDlgApi = new SAPI(
{
serverIP: window.config.serverIP, //ip
port: window.config.port, //HTTP
useHttps: window.config.useHttps, //使Https
container: "bimSelectionDlgContainer", //[]id
secretKey: window.config.secretKey,
openEarth: window.config.openEarth, //[]Gis
bgColor: window.config.bgColor, //[]bim,
tintColor: window.config.tintColor, //[]osgb
sceneTime: window.config.sceneTime, //[]
cadMode: window.config.cadMode, // Cad
},
() => {
this.initSuccess = true;
console.log("初始化成功");
setTimeout(() => {
this.initLoadModel();
}, 10);
let mapOptions = {
imgs: {
//
top: "./img/top.png",
bottom: "./img/under.png",
east: "./img/east.png",
south: "./img/south.png",
west: "./img/west.png",
north: "./img/north.png",
},
offset: {
//
corner: GLENavigationCube.RightTop,
x: 25,
y: 20,
},
cube: {
hoverColor: "#7193dc", //
size: 32, //
hotPointSize: 7, //
cubeTextColor: "#4c4c4ccc", // cube
cubeStrokeColor: "#374769cc", // cube
cubeFillColor: "#374769cc", // cube
},
zoomRatios: 1, //
show: true, //
showAxes: true, // XYZ线
};
bimSelectionDlgApi.Plugin.initNavCube(mapOptions);
}
);
},
initLoadModel() {
listBimModel({
pageNum: 1,
pageSize: 10,
comId: this.currentComId,
projectId: this.currentPrjId,
}).then((d) => {
this.models = (d.rows || []).map((it) => {
it.modelId = it.lightweightName;
it.gis = this.$tryToJson(it.gisJson || "{}", {});
return it;
});
this.treeKey++;
if (this.models.length == 0) {
this.$modal.msgError("暂无模型,请先关联模型");
} else {
//bimTools.buildTreeData(this);
bimTools.partLoadModel(this);
}
});
},
addModel(modelId, cb) {
let url = `${window.config.modelUrl}/Tools/output/model/${modelId}/root.glt`;
bimSelectionDlgApi.Model.add(
url,
modelId,
() => {},
() => {
cb && cb();
console.log("加载模型成功");
this.loadedModelCount++;
setTimeout(() => {
bimSelectionDlgApi.Camera.getViewPort((p) => {
this.viewPoint = p;
this.$message.info("模型加载完成");
this.bimLoaded = true;
this.doMenu(2);
});
}, 1000);
}
);
},
resetScene() {
let api = bimSelectionDlgApi;
api.Camera.stopImmersiveRoam();
api.Model.location(api.m_model.keys().toArray()[0]);
api.Plugin.deleteMiniMap();
if (this.viewPoint) {
api.Camera.setViewPort(this.viewPoint);
}
},
},
};
</script>
<style lang="scss">
.bim-selection-dialog {
.el-dialog {
min-width: 960px;
width: 80%;
height: 80vh;
.el-dialog__body {
max-height: calc(100% - 80px) !important;
overflow: hidden !important;
height: calc(100% - 80px);
#bimSelectionDlg {
height: 100%;
.bimSelectionDlgContainer {
height: 100%;
}
}
.div-left {
position: absolute;
left: 40px;
top: 80px;
width: 300px;
background: rgba(0, 57, 64, 0.6);
height: calc(100% - 180px);
border-radius: 5px;
&.is-hide {
height: 45px;
}
.div-left-title {
height: 100%;
.el-tabs {
height: 100%;
.el-tabs__content {
height: 100%;
.el-tab-pane {
height: 100%;
padding-bottom: 10px;
}
}
}
.el-tabs__nav-wrap {
.el-tabs__nav-prev,
.el-tabs__nav-next {
display: none;
}
}
.el-tabs__nav {
&::after {
content: " ";
display: block;
width: 100px;
}
.el-tabs__active-bar {
background-color: rgb(0, 255, 174);
}
.el-tabs__item {
padding: 0px 15px;
color: #fff;
&.is-active {
color: rgb(0, 255, 174);
}
}
}
.div-icon {
position: absolute;
right: 10px;
top: 8px;
z-index: 9;
.el-icon {
font-size: 24px;
color: #fff;
}
}
}
}
}
.footer-box {
position: absolute;
bottom: 6vh;
left: 50%;
margin-left: -75px;
background: #274754;
border-radius: 4px;
.footer-btn {
display: inline-flex;
width: 40px;
height: 40px;
justify-content: center;
align-items: center;
cursor: pointer;
svg {
width: 20px;
height: 20px;
fill: #fff;
}
&:hover {
background: #408edb97;
}
&.is-active {
svg {
fill: rgb(0, 255, 174);
}
}
}
}
.sel-list {
padding: 0px 10px 10px;
height: 100%;
overflow-y: auto;
&.hide-list {
height: calc(100% - 40px);
}
.div-sel-item {
background-color: #f7f7f975;
position: relative;
color: #fff;
line-height: 30px;
padding: 0px 10px;
margin-top: 5px;
.sel-item-info {
width: calc(100% - 40px);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor: pointer;
}
.el-button {
position: absolute;
right: 10px;
top: 4px;
color: #f40606;
}
}
}
.model-tree {
height: calc(100% - 36px);
overflow-y: auto;
.el-tree {
background: transparent;
color: #eee;
.el-checkbox {
color: #45fdfe;
}
.el-tree-node {
&:focus {
& > .el-tree-node__content {
background: #3489d966;
&:hover {
background: #3489d966;
}
}
}
.el-tree-node__content:hover {
background: #3489d966;
}
}
}
}
.div-mode {
position: absolute;
left: 360px;
top: 75px;
.el-switch__inner {
margin: 0px 4px;
}
}
}
}
</style>

View File

@ -78,7 +78,7 @@ import { getToken } from "@/utils/auth";
import { listPlan, getPlan, delPlan, addPlan, updatePlan } from "@/api/manage/plan";
import useUserStore from "@/store/modules/user";
import { findMyProjectList } from "@/api/publics";
import bimSelectionDialog from "./bimSelectionDialog.vue";
import bimSelectionDialog from "./bimSelectionDialog2.vue";
const { proxy } = getCurrentInstance();
const userStore = useUserStore();
const planList = ref([]);