开发4D模拟

dev_xd
lj7788@126.com 2025-07-05 10:14:04 +08:00
parent 5710a5b37d
commit f9ade243f9
7 changed files with 492 additions and 128 deletions

View File

@ -50,6 +50,7 @@ body {
#app { #app {
width: 1912px; width: 1912px;
height: 1080px; height: 1080px;
overflow: hidden;
} }
.screen-content-max { .screen-content-max {
width: 1912px; width: 1912px;
@ -89,6 +90,7 @@ body {
#app { #app {
width: 2550px; width: 2550px;
height: 1440px; height: 1440px;
overflow: hidden;
} }
.screen-content-max { .screen-content-max {
width: 2550px; width: 2550px;

View File

@ -0,0 +1,308 @@
const options = {
taskMapping: {
progress: "percent",
},
maxRows: 10,
title: {
label: "任务列表",
html: false,
},
row: {
height: 24,
},
times: {
timeZoom: 20,
},
calendar: {
workingDays: [1, 2, 3, 4, 5, 6],
gap: 0,
strokeWidth: 5,
hour: {
display: !1,
},
},
chart: {
progress: {
bar: false,
},
expander: {
display: true,
},
},
taskList: {
expander: {
straight: false,
},
columns: [
{
id: 1,
label: "ID",
value: "id",
width: 40,
},
{
id: 2,
label: "任务名称",
value: "label",
width: 200,
expander: true,
html: true,
},
{
id: 3,
label: "任务工期",
value: "days",
width: 120,
},
{
id: 4,
label: "开始时间",
value: "start",
width: 120,
},
{
id: 5,
label: "结束时间",
value: "end",
width: 120,
},
],
},
locale: {
name: "en",
Now: "当前时间",
"X-Scale": "缩放宽度",
"Y-Scale": "缩放高度",
"Task list width": "列头宽度",
"Before/After": "时间跨度",
"Display task list": "显示列头",
weekdays: ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
months: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
},
};
const ganttStyle = {
"main-view": {
background: "#06445b81",
"border-top": "1px solid var(--plan-color)",
},
"main-container-wrapper": {
overflow: "hidden",
"border-top": "1px solid var(--plan-color);",
"border-bottom": "1px solid var(--plan-color);",
},
"calendar-row-rect-child": {
"border-right-color": "var(--plan-color)",
},
"task-list": { "border-color": "var(--plan-color)" },
"task-list-header": {
"border-bottom": "1px solid var(--plan-color)",
"border-left": "1px solid var(--plan-color)",
},
"task-list-item": {
"border-top": "1px solid var(--plan-color)",
"border-right": "1px solid var(--plan-color)",
},
"task-list-item-column": {
"border-left": "1px solid var(--plan-color)",
"border-color": "var(--plan-color)",
},
"grid-line-time": {
stroke: "#FF000080",
"stroke-width": 5,
},
"chart-calendar-container": {
"border-right": "1px solid var(--plan-color)",
},
"chart-graph-container": {
"border-right": "1px solid var(--plan-color)",
},
"task-list-header-column": {
"border-left": "1px solid var(--plan-color)",
"box-sizing": "border-box",
display: "flex",
background: "#f3f5f7",
"border-color": "transparent",
},
calendar: {
background: "transparent",
},
"chart-row-text": {
background: "transparent",
"border-radius": "0px",
color: "#fff",
},
"task-list-expander-border": {
fill: "#00f1ff",
stroke: "#003357",
},
"chart-expander-border": {
fill: "#00f1ff",
stroke: "#000000A0",
},
};
function initEngine(that) {
console.log("开始初始化引擎")
window.bim4DApi = new SAPI(
{
serverIP: window.config.serverIP, //服务ip地址
port: window.config.port, //HTTP端口
useHttps: window.config.useHttps, //使用Https
container: "bim4DContainer", //[必须]容器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图纸预览模式
},
() => {
that.initSuccess = true;
console.log("初始化成功");
setTimeout(() => {
initLoadModel(that);
}, 10);
let mapOptions = {
imgs: {
// 六面图片
top: "/cdn/bim/sapi/img/top.png",
bottom: "/cdn/bim/sapi/img/under.png",
east: "/cdn/bim/sapi/img/east.png",
south: "/cdn/bim/sapi/img/south.png",
west: "/cdn/bim/sapi/img/west.png",
north: "/cdn/bim/sapi/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轴线
};
bim4DApi.Plugin.initNavCube(mapOptions);
}
);
}
function initLoadModel(that) {
that.$api.bim
.listBimModel({
pageNum: 1,
pageSize: 100,
comId: that.selProject.comId,
projectId: that.selProject.id,
})
.then((d) => {
that.models = (d.rows || []).map((it) => {
it.modelId = it.lightweightName;
it.visible = false;
it.checked = true;
it.gis = that.$tryToJson(it.gisJson || "{}", {});
return it;
});
if (that.models.length == 0) {
that.$modal.error("暂无模型,请先关联模型");
} else {
that.models.forEach((item) => {
item.modelId = item.lightweightName;
item.gis = JSON.parse(item.gisJson);
addModel(that, item.lightweightName);
});
}
});
}
function addModel(that, modelId, cb) {
let api = window.bim4DApi;
let url = `${window.config.modelUrl}/Tools/output/model/${modelId}/root.glt`;
console.log(modelId, url);
api.Model.add(
url,
modelId,
() => {},
() => {
cb && cb();
console.log("加载模型成功");
setTimeout(() => {
bim4DApi.Camera.getViewPort((p) => {
that.viewPoint = p;
that.modelLoaded = true;
});
}, 1000);
}
);
}
const maxLen = 500;
function showBim(that, index) {
let api = bim4DApi;
if (index <= 0) {
index = getCurrentTaskIndex(that);
}
console.log(index);
let showFeatureIds = [];
let currFeatureId = [];
for (let i = 0; i <= that.taskList.length; i++) {
if (i <= index) {
that.taskList[i].gis.forEach((item) => {
let featureId = item.featureId;
if (!showFeatureIds.includes(featureId)) {
showFeatureIds.push(featureId);
}
});
}
}
if (showFeatureIds.length > 0) {
let featureId = showFeatureIds[0];
let modelId = featureId.split("_")[0];
let tmpsIds2 = showFeatureIds.splice(0, maxLen);
api.Model.original(modelId);
api.Feature.showFeatures(tmpsIds2.join("#"));
setFeatueVisible(showFeatureIds, true);
}
}
function setFeatueVisible(featureIds, show) {
let len = maxLen;
let api = bim4DApi;
let cnt = featureIds.length;
if (cnt == 0) {
return;
}
for (let i = 0; i < cnt; i += len) {
api.Feature.setVisible(featureIds.slice(i, i + len).join("#"), show);
}
}
function getCurrentTaskIndex(that) {
let index = 0;
let taskList = that.taskList;
for (let i = 0; i < taskList.length; i++) {
let task = taskList[i];
if (task.children.length == 0 && that.$dt(task.start) - new Date() >= 0) {
index = i;
break;
}
}
return index;
}
export default {
options,
ganttStyle,
initEngine,
initLoadModel,
showBim,
};

View File

@ -1,7 +1,38 @@
<template> <template>
<div class="bim-4d-simulation main-page"> <div class="bim-4d-simulation main-page" style="--plan-color: #00aaff66" :class="{ 'show-plan': showPlan }">
<div v-if="hasLoad" class="div-plan"> <div class="icon-switch-plan" @click="doSwitchPlan">
<gantt-elastic :options="options" :tasks="taskList" :dynamicStyle="planStyle" @tasks-changed="tasksUpdate" @options-changed="optionsUpdate" @dynamic-style-changed="styleUpdate"> <svg-icon icon-class="swagger" />
</div>
<div id="bim4D" :key="elId">
<div id="bim4DContainer" class="bim4DContainer"></div>
</div>
<div class="slider-info" v-if="hasLoad">
<template v-if="isPlay == 0">
<div class="icon" @click="doPlay">
<svg-icon icon-class="play" />
</div>
<div class="icon"><svg-icon icon-class="tools" /></div>
</template>
<template v-if="isPlay == 1">
<div class="icon" @click="doPause">
<svg-icon icon-class="pause" />
</div>
<div class="icon" @click="doStop">
<svg-icon icon-class="stop" />
</div>
</template>
<template v-if="isPlay == 2">
<div class="icon" @click="doContinue">
<svg-icon icon-class="play" />
</div>
</template>
<el-slider v-model="playValue" :max="taskList.length" class="slider" @change="doSliderChange" />
</div>
<div v-if="hasLoad" class="div-plan" v-show="showPlan">
<gantt-elastic :options="options" :tasks="taskList" :dynamicStyle="planStyle" ref="gantt">
<gantt-header slot="header" :options="optionList" v-if="1 == 2"></gantt-header> <gantt-header slot="header" :options="optionList" v-if="1 == 2"></gantt-header>
</gantt-elastic> </gantt-elastic>
</div> </div>
@ -12,111 +43,8 @@
import debounce from "lodash.debounce"; import debounce from "lodash.debounce";
import ganttElastic from "gantt-elastic"; import ganttElastic from "gantt-elastic";
import ganttHeader from "gantt-elastic-header"; import ganttHeader from "gantt-elastic-header";
let options = { import bim4DTools from "./bim/bim4DTools";
taskMapping: {
progress: "percent",
},
maxRows: 100,
maxHeight: 500,
title: {
label: "任务列表",
html: false,
},
row: {
height: 24,
},
calendar: {
hour: {
display: false,
},
},
chart: {
progress: {
bar: false,
},
expander: {
display: true,
},
},
taskList: {
expander: {
straight: false,
},
columns: [
{
id: 1,
label: "ID",
value: "id",
width: 40,
},
{
id: 2,
label: "任务名称",
value: "label",
width: 200,
expander: true,
html: true,
},
{
id: 3,
label: "任务工期",
value: "days",
width: 120,
},
{
id: 4,
label: "开始时间",
value: "start",
width: 120,
},
{
id: 5,
label: "结束时间",
value: "end",
width: 120,
},
],
},
locale: {
name: "en",
Now: "当前时间",
"X-Scale": "缩放宽度",
"Y-Scale": "缩放高度",
"Task list width": "列头宽度",
"Before/After": "时间跨度",
"Display task list": "显示列头",
weekdays: ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
months: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
},
};
let ganttStyle = {
"main-view": {
background: "#06445b81",
},
"main-container-wrapper": {
overflow: "hidden",
"border-top": "1px solid #eee",
"border-bottom": "1px solid #eee",
},
calendar: {
background: "transparent",
},
'chart-row-text': {
background:"transparent",
'border-radius': '0px',
color: '#fff',
},
'task-list-expander-border': {
fill: '#00f1ff',
stroke: '#003357',
},
'chart-expander-border': {
fill: '#00f1ff',
stroke: '#000000A0',
},
};
export default { export default {
components: { components: {
ganttElastic, ganttElastic,
@ -128,10 +56,18 @@ export default {
dpi: "", dpi: "",
selProject: null, selProject: null,
elId: 1, elId: 1,
models: [],
initSuccess: false,
viewPoint: null,
modelLoaded: false,
taskList: [], taskList: [],
options, options: bim4DTools.options,
hasLoad: false, hasLoad: false,
planStyle: ganttStyle, planStyle: bim4DTools.ganttStyle,
playValue: 0,
isPlay: 0,
playTime: 300,
showPlan: true,
}; };
}, },
beforeDestroy() { beforeDestroy() {
@ -150,22 +86,32 @@ export default {
debounce((prj) => { debounce((prj) => {
this.selProject = prj; this.selProject = prj;
this.elId++; this.elId++;
//this.initEngine(); this.initEngine();
this.initPlanList(); this.initPlanList();
}) })
); );
this.selProject = this.$store.getters.selProject; this.selProject = this.$store.getters.selProject;
document.body.classList.add("is-sapi"); document.body.classList.add("is-sapi");
this.initPlanList(); this.initPlanList();
this.initEngine();
window.D4App = this;
}, },
methods: { methods: {
initEngine() {
this.elId++;
setTimeout(() => {
bim4DTools.initEngine(this);
}, 10);
},
doSwitchPlan() {
this.showPlan = !this.showPlan;
},
initPlanList() { initPlanList() {
if (this.selProject?.id) { if (this.selProject?.id) {
this.$api.bim.getPlanList(this.selProject.id).then((res) => { this.$api.bim.getPlanList(this.selProject.id).then((res) => {
this.taskList = (res.data || []).map((item) => { this.taskList = (res.data || []).map((item) => {
let o = { let o = {
id: item.taskId, id: item.taskId,
user: '<a href="https://www.google.com/search?q=Johnattan+Owens" target="_blank" style="color:#0077c0;">Johnattan Owens</a>',
parentId: item.parentId == 0 ? null : item.parentId, parentId: item.parentId == 0 ? null : item.parentId,
label: item.taskName, label: item.taskName,
start: item.planStartDate, start: item.planStartDate,
@ -173,7 +119,7 @@ export default {
duration: item.taskDuation * 24 * 60 * 60 * 1000, duration: item.taskDuation * 24 * 60 * 60 * 1000,
days: item.taskDuation + "(天)", days: item.taskDuation + "(天)",
collapsed: false, collapsed: false,
gis: this.$tryToJson(item.bimId, {}), gis: this.$tryToJson(item.bimId, []),
type: item.parentId == 0 ? "project" : "milestone", type: item.parentId == 0 ? "project" : "milestone",
style: { style: {
base: { base: {
@ -189,14 +135,48 @@ export default {
}); });
} }
}, },
tasksUpdate() { playPlay() {
console.log("---tasksUpdate->"); this.playTimer = setInterval(() => {
this.playValue++;
if (this.playValue > this.taskList.length) {
this.doStop();
} else {
this.doSliderChange();
}
}, this.playTime);
}, },
optionsUpdate() { doSliderChange() {
console.log("---optionsUpdate>"); let task = this.taskList[this.playValue];
this.$refs.gantt.scrollToTime(this.$dt(task.start));
console.log(task.start);
let d = this.$dt(task.start).$d;
const current = +d;
const currentOffset = this.$refs.gantt.timeToPixelOffsetX(current);
let el = document.querySelector(".div-plan .gantt-elastic__grid-line-time");
if (el) {
el.setAttribute("x1", currentOffset);
el.setAttribute("x2", currentOffset);
}
this.$refs.gantt.scrollTo(null, 36 * this.playValue);
bim4DTools.showBim(this, this.playValue);
}, },
styleUpdate(a) { doPlay() {
console.log("--styleUpdate-->", a); this.isPlay = 1;
this.playValue = -1;
this.playPlay();
},
doPause() {
this.isPlay = 2;
clearInterval(this.playTimer);
},
doStop() {
this.playValue = 0;
this.isPlay = 0;
clearInterval(this.playTimer);
},
doContinue() {
this.isPlay = 1;
this.playPlay();
}, },
}, },
}; };
@ -204,6 +184,12 @@ export default {
<style lang="less"> <style lang="less">
.bim-4d-simulation { .bim-4d-simulation {
position: relative;
&.show-plan {
#bim4D {
height: calc(100% - 460px);
}
}
.div-plan { .div-plan {
.gantt-elastic__main-view { .gantt-elastic__main-view {
.gantt-elastic__task-list-header-column { .gantt-elastic__task-list-header-column {
@ -214,6 +200,7 @@ export default {
background: transparent !important; background: transparent !important;
} }
.gantt-elastic__main-view-container { .gantt-elastic__main-view-container {
border-bottom: solid 1px var(--plan-color);
.gantt-elastic__calendar { .gantt-elastic__calendar {
background: transparent !important; background: transparent !important;
.gantt-elastic__calendar-row-text { .gantt-elastic__calendar-row-text {
@ -221,15 +208,28 @@ export default {
} }
} }
} }
.gantt-elastic__chart-calendar-container { .gantt-elastic__chart-graph {
border-bottom: solid 1px rgb(238, 238, 238); border-top: solid 1px var(--plan-color);
} }
.gantt-elastic__calendar-row{ .gantt-elastic__chart-calendar-container {
&.gantt-elastic__calendar-row--month{ border-bottom: solid 1px var(--plan-color);
border-bottom: solid 1px rgb(238, 238, 238); }
.gantt-elastic__task-list-items {
border-top: solid 1px var(--plan-color);
}
.gantt-elastic__task-list-wrapper {
border-bottom: solid 1px var(--plan-color);
}
.gantt-elastic__calendar-row {
&.gantt-elastic__calendar-row--month {
border-bottom: solid 1px var(--plan-color);
} }
} }
.gantt-elastic__chart-scroll-container{ .gantt-elastic__chart-days-highlight-rect {
display: none !important;
}
.gantt-elastic__chart-scroll-container {
scrollbar-color: deepskyblue transparent; scrollbar-color: deepskyblue transparent;
scrollbar-track-color: transparent; scrollbar-track-color: transparent;
-ms-overflow-style: -ms-autohiding-scrollbar; /* IE and Edge */ -ms-overflow-style: -ms-autohiding-scrollbar; /* IE and Edge */
@ -248,5 +248,56 @@ export default {
} }
} }
} }
.slider-info {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 8px;
.icon {
cursor: pointer;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
.svg-icon {
width: 24px;
height: 24px;
}
}
.el-slider {
margin-left: 10px;
flex-grow: 1;
.el-slider__runway {
height: 3px;
.el-slider__bar {
height: 3px;
.el-slider__button-wrapper {
.el-slider__button {
width: 12px;
height: 12px;
}
}
}
}
}
}
#bim4D {
height: calc(100% - 40px);
#bim4DContainer {
height: 100%;
}
}
.icon-switch-plan {
position: absolute;
top: 40px;
right: 40px;
cursor: pointer;
.svg-icon {
width: 30px;
height: 30px;
}
}
} }
</style> </style>

View File

@ -132,7 +132,7 @@ export default {
pageNum: this.pageNum, pageNum: this.pageNum,
pageSize: this.pageSize, pageSize: this.pageSize,
problemType: this.problemType, problemType: this.problemType,
projectName: this.nav, checkState: this.nav,
} }
this.$api.safety.listForBG(postData).then((d) => { this.$api.safety.listForBG(postData).then((d) => {
this.total = d.total this.total = d.total

View File

@ -374,7 +374,7 @@ export default {
data: it, data: it,
} }
}) })
this.chart2Data = (res[1].data.fi || []).map((it) => { this.chart2Data = (res[1].data || []).map((it) => {
return { return {
id: it.nickedInfo, id: it.nickedInfo,
name: it.dangerType, name: it.dangerType,

View File

@ -132,7 +132,7 @@ export default {
pageNum: this.pageNum, pageNum: this.pageNum,
pageSize: this.pageSize, pageSize: this.pageSize,
problemType: this.problemType, problemType: this.problemType,
projectName: this.nav, checkState: this.nav,
} }
this.$api.safety.listForBG(postData).then((d) => { this.$api.safety.listForBG(postData).then((d) => {
this.total = d.total this.total = d.total

View File

@ -55,7 +55,7 @@
</div> </div>
</div> </div>
<div class="tools-bottom"> <div class="tools-bottom">
<el-button type="primary" @click="doSave"></el-button> <el-button type="primary" @click="doSave" :loading="loading">保存</el-button>
</div> </div>
</div> </div>
<div class="test-box"> <div class="test-box">
@ -96,6 +96,7 @@ export default {
selItem: null, selItem: null,
addLabels: [], addLabels: [],
viewPoint:null, viewPoint:null,
loading:false,
}; };
}, },
created() { created() {
@ -127,6 +128,7 @@ export default {
confirmButtonText: "确 认", confirmButtonText: "确 认",
cancelButtonText: "取 消", cancelButtonText: "取 消",
}).then(() => { }).then(() => {
this.loading=true
let adds = this.devices let adds = this.devices
.filter((item) => { .filter((item) => {
return item.id == null; return item.id == null;
@ -162,6 +164,7 @@ export default {
ajaxs.push(devicePositionUpdateItems({ items: updates })); ajaxs.push(devicePositionUpdateItems({ items: updates }));
} }
Promise.all(ajaxs).then((res) => { Promise.all(ajaxs).then((res) => {
this.loading=false
ElMessage.success("保存成功"); ElMessage.success("保存成功");
}); });
}); });