<template>
|
<div class="Startjob">
|
<div class="rect" style="position: relative">
|
<div class="rect-top">
|
<img src="@/assets/TheCurrentJob/left.png" alt="" />
|
<img
|
style="margin-left: -1.5rem; margin-right: 4rem"
|
src="@/assets/TheCurrentJob/left1.png"
|
alt=""
|
/>
|
<span>开始作业({{ title }})</span>
|
<img
|
style="margin-right: -1.5rem; margin-left: 4rem"
|
src="@/assets/TheCurrentJob/right1.png"
|
alt=""
|
/>
|
<img src="@/assets/TheCurrentJob/right.png" alt="" />
|
</div>
|
<div class="rect-center">
|
<div class="rect-item1">
|
<div style="margin-top: 0.5rem">
|
<img src="@/assets/TheCurrentJob/icon/icon.png" alt="" />
|
<span style="color: rgba(26, 201, 255, 1); font-size: 1.5rem"
|
>任务作业信息</span
|
>
|
</div>
|
<div style="height: 100%; width: 100%">
|
<div style="width: 100%; padding-left: 1rem">
|
<div
|
style="
|
color: #1ac2f7;
|
font-size: 1rem;
|
display: flex;
|
margin-top: 0.5rem;
|
"
|
>
|
<span style="width: 6rem; text-align: right; display: block"
|
>任务名称:</span
|
><span>{{ info.takename }}</span>
|
</div>
|
<div
|
style="
|
color: #1ac2f7;
|
font-size: 1rem;
|
display: flex;
|
margin-top: 0.4rem;
|
"
|
>
|
<span style="width: 6rem; text-align: right; display: block"
|
>技术员:</span
|
><span>{{ info.jishuyuan }}</span>
|
</div>
|
<div
|
style="
|
color: #1ac2f7;
|
font-size: 1rem;
|
display: flex;
|
margin-top: 0.4rem;
|
"
|
>
|
<span style="width: 6rem; text-align: right; display: block"
|
>检修工厂:</span
|
><span>{{ info.gonzhang }}</span>
|
</div>
|
<div
|
style="
|
color: #1ac2f7;
|
font-size: 1rem;
|
display: flex;
|
margin-top: 0.4rem;
|
"
|
>
|
<span style="width: 6rem; text-align: right; display: block"
|
>质检员:</span
|
><span>{{ info.zhijianyuan }}</span>
|
</div>
|
<div
|
style="
|
color: #1ac2f7;
|
font-size: 1rem;
|
display: flex;
|
margin-top: 0.4rem;
|
"
|
>
|
<span style="width: 6rem; text-align: right; display: block"
|
>力矩作业员:</span
|
><span>{{ info.lijuzouyeyuan }}</span>
|
</div>
|
<div
|
style="
|
color: #1ac2f7;
|
font-size: 1rem;
|
display: flex;
|
margin-top: 0.4rem;
|
"
|
>
|
<span style="width: 6rem; text-align: right; display: block"
|
>辅助员:</span
|
><span>{{ info.fuzyuan }}</span>
|
</div>
|
<div
|
style="
|
position: absolute;
|
color: #1ac2f7;
|
font-size: 1rem;
|
display: flex;
|
margin-top: 0.4rem;
|
"
|
>
|
<span style="width: 6rem; text-align: right; display: block"
|
>任务详情:</span
|
>
|
<span
|
ref="spanHide1"
|
@click="showDetail(!contentShow1)"
|
style="color: #1ac2f7; font-size: 0.88rem; cursor: pointer"
|
>查看</span
|
>
|
<div
|
v-if="contentShow1"
|
style="
|
position: relative;
|
top: 1.5rem;
|
left: -3rem;
|
width: 49rem;
|
"
|
>
|
<el-table
|
empty-text="暂无数据"
|
:data="taskData"
|
width="100%"
|
:header-cell-style="{
|
color: '#fff',
|
background: '#1AC2F7',
|
}"
|
:cell-style="{
|
height: '3.38rem',
|
color: '#fff',
|
background: '#1373A8',
|
}"
|
>
|
<el-table-column property="trainKind" label="车型" />
|
<el-table-column property="trainNum" label="车组" />
|
<el-table-column property="track" label="股道" />
|
<el-table-column property="coachNum" label="车厢号位置" />
|
<el-table-column property="bogie" label="转向架位置" />
|
<el-table-column property="processDept" label="检修班组" />
|
</el-table>
|
</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
<div class="rect-item2">
|
<div>
|
<span style="color: rgba(26, 200, 254, 1); font-size: 1.2rem"
|
>工艺项点/步骤共<span
|
style="font-size: 1.5rem; font-weight: bold"
|
>{{ sunNUm }}</span
|
>
|
</span>
|
<span
|
style="
|
margin-left: 1rem;
|
color: rgba(26, 200, 254, 1);
|
font-size: 1.2rem;
|
"
|
>当前作业项点/步骤<span
|
style="font-size: 1.5rem; font-weight: bold"
|
>{{ obj.setpNum }}</span
|
>
|
</span>
|
</div>
|
<div style="width: 62rem; height: 2.5rem">
|
<el-table
|
empty-text="暂无数据"
|
style="margin-top: 0.88rem; opacity: 0.8"
|
:data="gridData"
|
width="100%"
|
:header-cell-style="{
|
height: '1.61rem',
|
color: '#fff',
|
fontSize: '0.88rem',
|
border: 'solid 1px #1ac2f7',
|
}"
|
:cell-style="{
|
color: '#fff',
|
}"
|
>
|
<el-table-column
|
property="craftsStep"
|
label="工艺项点/步骤"
|
min-width="13%"
|
>
|
<template #default="scope">
|
<el-tooltip placement="bottom" effect="light">
|
<template #content>
|
<span style="display: block; width: 15rem">{{
|
scope.row.craftsStep
|
}}</span>
|
</template>
|
<span
|
@click="true"
|
style="
|
color: #ffffff;
|
font-size: 0.88rem;
|
display: -webkit-box; /* 设置为WebKit内核的弹性盒子模型 */
|
-webkit-box-orient: vertical; /* 垂直排列 */
|
-webkit-line-clamp: 1; /* 限制显示两行 */
|
overflow: hidden; /* 隐藏超出范围的内容 */
|
text-overflow: ellipsis; /* 使用省略号 */
|
"
|
>{{ scope.row.craftsStep }}</span
|
>
|
</el-tooltip>
|
</template>
|
</el-table-column>
|
|
<el-table-column
|
property="craftContent"
|
label="工艺内容"
|
min-width="40%"
|
>
|
<template #default="scope">
|
<el-tooltip placement="bottom" effect="light">
|
<template #content>
|
<span style="display: block; width: 15rem">{{
|
scope.row.craftContent
|
}}</span>
|
</template>
|
<span
|
@click="true"
|
style="
|
color: #ffffff;
|
font-size: 0.88rem;
|
display: -webkit-box; /* 设置为WebKit内核的弹性盒子模型 */
|
-webkit-box-orient: vertical; /* 垂直排列 */
|
-webkit-line-clamp: 2; /* 限制显示两行 */
|
overflow: hidden; /* 隐藏超出范围的内容 */
|
text-overflow: ellipsis; /* 使用省略号 */
|
"
|
>{{ scope.row.craftContent }}</span
|
>
|
</el-tooltip>
|
</template></el-table-column
|
>
|
<el-table-column property="tools" label="工具" min-width="12%">
|
<template #default="scope">
|
<el-tooltip placement="bottom" effect="light">
|
<template #content>
|
<span style="display: block; width: 15rem">{{
|
scope.row.tools
|
}}</span>
|
</template>
|
<span
|
@click="true"
|
style="
|
color: #ffffff;
|
font-size: 0.88rem;
|
display: -webkit-box; /* 设置为WebKit内核的弹性盒子模型 */
|
-webkit-box-orient: vertical; /* 垂直排列 */
|
-webkit-line-clamp: 2; /* 限制显示两行 */
|
overflow: hidden; /* 隐藏超出范围的内容 */
|
text-overflow: ellipsis; /* 使用省略号 */
|
"
|
>{{ scope.row.tools }}</span
|
>
|
</el-tooltip>
|
</template></el-table-column
|
>
|
<el-table-column
|
property="craftsStep"
|
label="物料"
|
min-width="15%"
|
>
|
<template #default="scope">
|
<el-tooltip placement="bottom" effect="light">
|
<template #content>
|
<span style="display: block; width: 15rem">{{
|
scope.row.material
|
}}</span>
|
</template>
|
<span
|
@click="true"
|
style="
|
color: #ffffff;
|
font-size: 0.88rem;
|
display: -webkit-box; /* 设置为WebKit内核的弹性盒子模型 */
|
-webkit-box-orient: vertical; /* 垂直排列 */
|
-webkit-line-clamp: 2; /* 限制显示两行 */
|
overflow: hidden; /* 隐藏超出范围的内容 */
|
text-overflow: ellipsis; /* 使用省略号 */
|
"
|
>{{ scope.row.material }}</span
|
>
|
</el-tooltip>
|
</template></el-table-column
|
>
|
</el-table>
|
<!-- <div class="consten" v-show="contentShow">
|
<el-table
|
empty-text="暂无数据"
|
:data="gridData"
|
width="100%"
|
:header-cell-style="{
|
height: '1.61rem',
|
color: '#fff',
|
background: '#0A5B91',
|
fontSize: '0.88rem',
|
}"
|
:cell-style="{
|
color: '#fff',
|
background: '#147BAF',
|
}"
|
>
|
<el-table-column
|
property="craftsStep"
|
label="工艺内容"
|
min-width="30%"
|
>
|
<template #default="scope">
|
<span @click="true" style="color: #fff; font-size: 0.88rem"
|
>{{ scope.row.craftContent }}
|
</span></template
|
></el-table-column
|
>
|
<el-table-column
|
property="craftsStep"
|
label="工具"
|
min-width="10%"
|
>
|
<template #default="scope">
|
<span style="color: #ffffff">{{
|
scope.row.tools
|
}}</span></template
|
></el-table-column
|
>
|
<el-table-column
|
property="craftsStep"
|
label="物料"
|
min-width="10%"
|
>
|
<template #default="scope">
|
<span style="color: #ffffff">{{
|
scope.row.material
|
}}</span></template
|
></el-table-column
|
>
|
</el-table>
|
</div> -->
|
</div>
|
</div>
|
<div class="rect-item3" style="position: relative">
|
<div style="overflow: hidden; padding-bottom: 1rem">
|
<img src="@/assets/TheCurrentJob/icon/icon.png" alt="" />
|
<span style="color: rgba(26, 201, 255, 1); font-size: 0.88rem"
|
>力矩值显示</span
|
>
|
<el-scrollbar ref="scrollbarRef" height="130rpx">
|
<div
|
ref="innerRef"
|
style="
|
display: flex;
|
justify-content: center;
|
flex-direction: column;
|
padding-left: 1.5rem;
|
padding-top: 0.5rem;
|
padding-bottom: 1rem;
|
box-sizing: border-box;
|
"
|
>
|
<span
|
style="color: #ffffff; font-size: 1rem; margin: 0.4rem 0"
|
v-for="(item, index) in Torque"
|
:key="item.id"
|
>{{
|
index + 1 + "、" + item.torqueSize + " " + "N*m"
|
}}</span
|
>
|
</div>
|
<el-input
|
type="number"
|
v-if="isTorque"
|
v-model="torqueFrom.torqueSize"
|
style="font-size: 0.75rem; height: 2rem"
|
placeholder="请输入扭力值"
|
><template #suffix>
|
<span style="font-size: 0.88rem; color: black">N*m</span>
|
</template>
|
</el-input>
|
<!-- @blur="addTorque" -->
|
</el-scrollbar>
|
</div>
|
<div
|
style="
|
position: absolute;
|
bottom: -2.3rem;
|
left: -0.1rem;
|
width: 100%;
|
"
|
>
|
<el-button
|
v-if="!isTorque"
|
type="primary"
|
style="font-size: 0.75rem; width: 100%; height: 2rem"
|
@click="showTorque"
|
>手动模拟扭力值</el-button
|
>
|
<div v-else style="display: flex; justify-content: space-between">
|
<el-button
|
type="primary"
|
style="font-size: 0.75rem; width: 100%; height: 2rem"
|
@click="addTorque"
|
>确认</el-button
|
>
|
<el-button
|
type="primary"
|
style="font-size: 0.75rem; width: 100%; height: 2rem"
|
@click="isTorque = false"
|
>取消</el-button
|
>
|
</div>
|
</div>
|
</div>
|
</div>
|
<div
|
ref="screenDom"
|
style="
|
margin: 0 auto;
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
"
|
>
|
<div
|
ref="container"
|
class="my-three"
|
style="width: 100%; height: 100%"
|
></div>
|
<!-- <model-gltf
|
:width="400"
|
:height="400"
|
:backgroundAlpha="0"
|
:backgroundColor="'#000000'"
|
src="ThreeModel/test.gltf"
|
:controlsOptions="{
|
enableZoom,
|
}"
|
/> -->
|
</div>
|
<div style="position: absolute; bottom: 5%; left: 2%">
|
<span style="color: rgba(26, 201, 255, 1); font-size: 1.5em"
|
>X:{{ xPos }} Y:{{ yPos }} Z:{{ zPos }}</span
|
>
|
</div>
|
<div
|
style="
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
position: absolute;
|
bottom: 5%;
|
right: 0%;
|
transform: translateX(-20%);
|
z-index: 999;
|
"
|
>
|
<el-button
|
:disabled="obj.setpNum == 1"
|
@click="Previous(false)"
|
type="primary"
|
:style="{
|
width: '5rem',
|
height: '1.88rem',
|
background:
|
obj.setpNum == 1
|
? 'rgba(164, 173, 179, 1)'
|
: 'rgba(26, 194, 247, 1)',
|
color: 'rgba(255, 255, 255, 1)',
|
fontSize: '0.88rem;',
|
}"
|
>上一步</el-button
|
>
|
<el-button
|
:disabled="obj.setpNum == sunNUm"
|
@click="DialogVisible = true"
|
type="primary"
|
:style="{
|
width: '5rem',
|
height: '1.88rem',
|
background:
|
obj.setpNum != sunNUm
|
? 'rgba(26, 194, 247, 1)'
|
: 'rgba(164, 173, 179, 1)',
|
color: ' rgba(255, 255, 255, 1)',
|
fontSize: '0.88rem',
|
}"
|
>下一步</el-button
|
>
|
<el-button
|
:disabled="obj.setpNum != sunNUm"
|
@click="Completee"
|
type="primary"
|
:style="{
|
height: '1.88rem',
|
width: '5rem',
|
background:
|
obj.setpNum == sunNUm
|
? 'rgba(26, 194, 247, 1)'
|
: 'rgba(164, 173, 179, 1)',
|
color: 'rgba(255, 255, 255, 1)',
|
fontSize: '0.88rem',
|
}"
|
>完成作业</el-button
|
>
|
</div>
|
</div>
|
|
<!-- 强制跳转确认框 -->
|
<el-dialog v-model="centerDialogVisible" title="确认" width="300" center>
|
<span sty> 当前任务未完成,是否强制跳转到下一步? </span>
|
<template #footer>
|
<div class="dialog-footer">
|
<el-button style="width: 5rem" @click="centerDialogVisible = false"
|
>取消</el-button
|
>
|
<el-button style="width: 5rem" type="primary" @click="Next(true)">
|
确认
|
</el-button>
|
</div>
|
</template>
|
</el-dialog>
|
|
<!-- 是否合格或已完成 -->
|
<el-dialog v-model="DialogVisible" title="确认" width="300" center>
|
<span style="display: block; text-align: center; font-size: 1.5rem">
|
请确认当前步骤已完成且合格
|
</span>
|
<template #footer>
|
<div class="dialog-footer">
|
<el-button style="width: 5rem" @click="DialogVisible = false"
|
>取消</el-button
|
>
|
<el-button style="width: 5rem" type="primary" @click="Next(false)">
|
确认
|
</el-button>
|
</div>
|
</template>
|
</el-dialog>
|
</div>
|
</template>
|
<script setup>
|
import { ref, onMounted, nextTick, onBeforeUnmount } from "vue";
|
import {
|
GetScreenData,
|
GetPre,
|
GetNext,
|
Complete,
|
GetTorque,
|
ChangeStatus,
|
AddTorque, //添加扭力值接口
|
} from "@/api/newapi/Thecurrentjob";
|
import { GetPageData } from "@/api/newapi/NjTask";
|
import { useRouter } from "vue-router";
|
import { ElMessage, ElMessageBox } from "element-plus";
|
import { ModelCollada, ModelGltf } from "vue-3d-model";
|
import { formatTime } from "@/utils/index.js";
|
import * as THREE from "three";
|
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
|
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; //gltf
|
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
|
|
const container = ref(null);
|
|
const router = useRouter();
|
const gridData = ref([]);
|
const contentShow = ref(false);
|
const contentShow1 = ref(false);
|
const DialogVisible = ref(false);
|
const info = ref({});
|
info.value = history.state?.info ? JSON.parse(history.state?.info) : {};
|
const title = ref("");
|
title.value = history.state?.title ? history.state?.title : "机械";
|
const enableZoom = ref(false);
|
const obj = ref({});
|
const sunNUm = ref(0);
|
const Torque = ref("");
|
const showDetail1 = (va1) => {
|
contentShow1.value = false;
|
contentShow.value = true;
|
};
|
const showDetail = (va1) => {
|
contentShow1.value = va1;
|
contentShow.value = false;
|
checko();
|
};
|
const taskData = ref([]);
|
const centerDialogVisible = ref(false);
|
const from = ref({
|
group: "",
|
takeid: "",
|
setnum: 0,
|
});
|
|
const client = ref(null);
|
|
//扭力值
|
const torqueFrom = ref({
|
creater: JSON.parse(localStorage.getItem("user")).userName, //当前登陆人
|
createDate: formatTime(new Date()), //当前时间
|
modifier: JSON.parse(localStorage.getItem("user")).userName,
|
modifyDate: formatTime(new Date()),
|
id: 0,
|
deviceCode: "", //设备编号
|
takeId: "", //任务id
|
groupOp: "", //班组
|
processSte: 0, //当前步骤
|
torqueSize: null, //输入的扭力值
|
});
|
|
const isTorque = ref(false);
|
const flag = ref(false);
|
|
const innerRef = ref();
|
const scrollbarRef = ref();
|
const isJob = ref([]); //已经完成的步骤
|
|
//上一步
|
const Previous = (val) => {
|
//上一个的值
|
from.value.group = info.value.grouptype;
|
from.value.takeid = info.value.njtakeid;
|
from.value.setnum = obj.value.setpNum;
|
flag.value = val;
|
GetPre(from.value, flag.value).then((res) => {
|
gridData.value = [res.data.nex];
|
obj.value = res.data.nex;
|
xPos.value =
|
gridData.value[0].pointAxisXYZ != null &&
|
gridData.value[0].pointAxisXYZ != 0
|
? gridData.value[0].pointAxisXYZ.split(",")[0]
|
: -585;
|
yPos.value =
|
gridData.value[0].pointAxisXYZ != null &&
|
gridData.value[0].pointAxisXYZ != 0
|
? gridData.value[0].pointAxisXYZ.split(",")[1]
|
: 692;
|
zPos.value =
|
gridData.value[0].pointAxisXYZ != null &&
|
gridData.value[0].pointAxisXYZ != 0
|
? gridData.value[0].pointAxisXYZ.split(",")[2]
|
: 692;
|
isJob.value = nodeList.slice(0, gridData.value[0].setpNum - 1); //已经完成的步骤
|
cameraAnimate(
|
[xPos.value, yPos.value, zPos.value],
|
gridData.value[0].moduleName
|
);
|
queryData.value.setnum = obj.value.setpNum;
|
GetTorque(queryData.value).then((res) => {
|
Torque.value = res.data;
|
});
|
});
|
};
|
//下一步
|
const Next = (val) => {
|
from.value.group = info.value.grouptype;
|
from.value.takeid = info.value.njtakeid;
|
from.value.setnum = obj.value.setpNum;
|
flag.value = val;
|
GetNext(from.value, flag.value).then((res) => {
|
if (res.message == "没有完成当前步骤") {
|
gridData.value = res.data.nowdate;
|
obj.value = res.data.nowdate[0];
|
if (res.data.nowdate[0].setpNum == obj.value.setpNum) {
|
centerDialogVisible.value = true;
|
DialogVisible.value = false;
|
return;
|
}
|
return;
|
}
|
centerDialogVisible.value = false;
|
DialogVisible.value = false;
|
gridData.value = [res.data.nex];
|
xPos.value =
|
gridData.value[0].pointAxisXYZ != null &&
|
gridData.value[0].pointAxisXYZ != 0
|
? gridData.value[0].pointAxisXYZ.split(",")[0]
|
: -585;
|
yPos.value =
|
gridData.value[0].pointAxisXYZ != null &&
|
gridData.value[0].pointAxisXYZ != 0
|
? gridData.value[0].pointAxisXYZ.split(",")[1]
|
: 692;
|
zPos.value =
|
gridData.value[0].pointAxisXYZ != null &&
|
gridData.value[0].pointAxisXYZ != 0
|
? gridData.value[0].pointAxisXYZ.split(",")[2]
|
: 692;
|
isJob.value = res.data.finish.map((item) => {
|
return item.moduleName;
|
});
|
cameraAnimate(
|
[xPos.value, yPos.value, zPos.value],
|
gridData.value[0].moduleName
|
);
|
obj.value = res.data.nex;
|
queryData.value.setnum = obj.value.setpNum;
|
GetTorque(queryData.value).then((res) => {
|
Torque.value = res.data;
|
});
|
});
|
};
|
//完成
|
const Completee = () => {
|
from.value.gruops = info.value.grouptype;
|
from.value.id = info.value.njtakeid;
|
from.value.creater = info.value.creater;
|
|
ChangeStatus(from.value).then((res) => {
|
ElMessage({
|
message: "操作完成",
|
type: "success",
|
});
|
router.push("/Thecurrentjob");
|
});
|
};
|
//查看数据
|
const checko = () => {
|
GetPageData({
|
page: 1,
|
rows: 10,
|
total: 100,
|
tableName: "",
|
sort: "",
|
order: "",
|
wheres: "",
|
export: true,
|
value: "",
|
filter: [
|
{
|
name: "njtaskid",
|
value: info.value.njtakeid,
|
displayType: "String",
|
},
|
],
|
}).then((res) => {
|
taskData.value = res.rows;
|
});
|
};
|
const queryData = ref({
|
grop: info.value.grouptype,
|
takeid: info.value.njtakeid,
|
setnum: "",
|
});
|
const initData = async () => {
|
await GetScreenData({
|
group: info.value.grouptype,
|
takeid: info.value.njtakeid,
|
}).then((res) => {
|
gridData.value = [res.data.proNow];
|
sunNUm.value = res.data.maxproce;
|
obj.value = res.data.proNow;
|
queryData.value.setnum = obj.value.setpNum;
|
xPos.value =
|
gridData.value[0].pointAxisXYZ != null &&
|
gridData.value[0].pointAxisXYZ != 0
|
? gridData.value[0].pointAxisXYZ.split(",")[0]
|
: -585;
|
yPos.value =
|
gridData.value[0].pointAxisXYZ != null &&
|
gridData.value[0].pointAxisXYZ != 0
|
? gridData.value[0].pointAxisXYZ.split(",")[1]
|
: 692;
|
zPos.value =
|
gridData.value[0].pointAxisXYZ != null &&
|
gridData.value[0].pointAxisXYZ != 0
|
? gridData.value[0].pointAxisXYZ.split(",")[2]
|
: 692;
|
isJob.value = res.data.finish;
|
cameraAnimate(
|
[xPos.value, yPos.value, zPos.value],
|
gridData.value[0].moduleName
|
);
|
GetTorque(queryData.value).then((res) => {
|
Torque.value = res.data;
|
});
|
});
|
};
|
|
const CAMERA_POS = [100, 100, 150];
|
const BASE_COLOR = [0.2, 0.4, 0.6];
|
const RED_COLOR = [3.0, 0.2, 0.4];
|
const GREEN_COLOR = [0.0, 1.0, 0.0];
|
|
const xPos = ref("");
|
const yPos = ref("");
|
const zPos = ref("");
|
const scene = new THREE.Scene();
|
const camera = new THREE.PerspectiveCamera(
|
75,
|
window.innerWidth / window.innerHeight,
|
0.1,
|
3000
|
);
|
const renderer = new THREE.WebGLRenderer({
|
antialias: true,
|
alpha: true,
|
precision: "highp",
|
});
|
|
const painting = (part) => {
|
let newArr = [];
|
// let newArr = flatten(isJob.value);
|
scene.traverse(function (child) {
|
// 检查对象是否具有材质属性
|
if (child.isMesh) {
|
let materials = child.material;
|
// 如果材质是单个对象而不是数组,则将其放入数组中以便统一处理
|
if (!Array.isArray(materials)) {
|
materials = [materials];
|
}
|
let isString = []; // 判断是否是字符串
|
// 遍历材质数组并设置颜色
|
//已经完成的节点
|
isJob.value.forEach((item) => {
|
if (
|
item ==
|
"抗蛇行减振器螺栓1,抗蛇行减振器螺栓2,抗蛇行减振器螺栓3,抗蛇行减振器螺栓4" &&
|
item != part
|
) {
|
isString = item.split(",");
|
materials.forEach(function (material) {
|
if (isString.includes(material.name)) {
|
material.color.setRGB(...GREEN_COLOR);
|
}
|
material.needsUpdate = true; // 强制更新材质
|
});
|
}
|
materials.forEach(function (material) {
|
if (material.name === item) {
|
material.color.setRGB(...GREEN_COLOR);
|
}
|
material.needsUpdate = true; // 强制更新材质
|
});
|
});
|
if (
|
part ==
|
"抗蛇行减振器螺栓1,抗蛇行减振器螺栓2,抗蛇行减振器螺栓3,抗蛇行减振器螺栓4"
|
) {
|
materials.forEach(function (material) {
|
if (part.split(",").includes(material.name)) {
|
material.color.setRGB(...RED_COLOR);
|
}
|
material.needsUpdate = true; // 强制更新材质
|
});
|
} else {
|
materials.forEach(function (material) {
|
if (material.name == part) {
|
material.color.setRGB(...RED_COLOR);
|
}
|
material.needsUpdate = true; // 强制更新材质
|
});
|
}
|
|
// if (
|
// typeof part ==
|
// "抗蛇行减振器螺栓1,抗蛇行减振器螺栓2,抗蛇行减振器螺栓3,抗蛇行减振器螺栓4"
|
// ) {
|
// newArr = part.split(",");
|
// materials.forEach(function (material) {
|
// if (newArr.includes(material.name)) {
|
// material.color.setRGB(...GREEN_COLOR);
|
// }
|
// if (part.includes(material.name)) {
|
// material.color.setRGB(...RED_COLOR);
|
// } else {
|
// material.color.setRGB(...BASE_COLOR);
|
// }
|
// material.needsUpdate = true; // 强制更新材质
|
// });
|
// } else {
|
// materials.forEach(function (material) {
|
// if (newArr.includes(material.name)) {
|
// material.color.setRGB(...GREEN_COLOR);
|
// } else if (material.name === part) {
|
// material.color.setRGB(...RED_COLOR);
|
// } else {
|
// material.color.setRGB(...BASE_COLOR);
|
// }
|
// // if (newArr.includes(material.name)) {
|
// // material.color.setRGB(...GREEN_COLOR);
|
// // }
|
// material.needsUpdate = true; // 强制更新材质
|
// });
|
// }
|
}
|
});
|
};
|
const cameraAnimate = (
|
targetPosition = CAMERA_POS,
|
part = null,
|
duration = 500
|
) => {
|
let startTime = null;
|
const startPosition = {
|
x: camera.position.x,
|
y: camera.position.y,
|
z: camera.position.z,
|
};
|
const distance = {
|
x: targetPosition[0] - startPosition.x,
|
y: targetPosition[1] - startPosition.y,
|
z: targetPosition[2] - startPosition.z,
|
};
|
|
function animate(time) {
|
if (!startTime) startTime = time;
|
const elapsed = time - startTime;
|
const progress = Math.min(elapsed / duration, 1);
|
|
// 使用线性插值计算新的位置
|
camera.position.x = startPosition.x + distance.x * progress;
|
camera.position.y = startPosition.y + distance.y * progress;
|
camera.position.z = startPosition.z + distance.z * progress;
|
xPos.value = Math.floor(camera.position.x);
|
yPos.value = Math.floor(camera.position.y);
|
zPos.value = Math.floor(camera.position.z);
|
if (progress < 1) {
|
requestAnimationFrame(animate);
|
}
|
}
|
requestAnimationFrame(animate);
|
painting(part);
|
renderer.render(scene, camera); // 强制渲染一次
|
};
|
const flatten = (arr) => {
|
return arr.reduce(
|
(acc, val) =>
|
Array.isArray(val) ? acc.concat(flatten(val)) : acc.concat(val),
|
[]
|
);
|
};
|
// window.addEventListener("resize", () => {
|
// // console.log("初始化场景", window.innerWidth, window.innerHeight);
|
// // camera.aspect = window.innerWidth / window.innerHeight;
|
// // camera.updateProjectionMatrix();
|
// // renderer.setSize(window.innerWidth, window.innerHeight);
|
// });
|
const nodeList = [
|
"转向架",
|
"抗蛇行减振器螺栓1,抗蛇行减振器螺栓2,抗蛇行减振器螺栓3,抗蛇行减振器螺栓4",
|
"抗蛇行减振器螺栓1,抗蛇行减振器螺栓2,抗蛇行减振器螺栓3,抗蛇行减振器螺栓4",
|
"抗蛇行减振器螺栓2",
|
"抗蛇行减振器螺栓3",
|
"抗蛇行减振器螺栓4",
|
"抗蛇行减振器螺栓1,抗蛇行减振器螺栓2,抗蛇行减振器螺栓3,抗蛇行减振器螺栓4",
|
"抗蛇行减振器螺栓2",
|
"抗蛇行减振器螺栓3",
|
"抗蛇行减振器螺栓4",
|
"高度调整杆",
|
"高度调整杆",
|
"高度调整杆",
|
"转向架",
|
"转向架",
|
"转向架",
|
"转向架",
|
"转向架",
|
"转向架",
|
"转向架",
|
"抗蛇行减振器螺栓1,抗蛇行减振器螺栓2,抗蛇行减振器螺栓3,抗蛇行减振器螺栓4",
|
"抗蛇行减振器螺栓2",
|
"抗蛇行减振器螺栓3",
|
"抗蛇行减振器螺栓4",
|
"抗蛇行减振器螺栓1,抗蛇行减振器螺栓2,抗蛇行减振器螺栓3,抗蛇行减振器螺栓4",
|
"抗蛇行减振器螺栓1,抗蛇行减振器螺栓2,抗蛇行减振器螺栓3,抗蛇行减振器螺栓4",
|
"高度调整杆",
|
"高度调整杆",
|
"高度调整杆",
|
"转向架",
|
"转向架",
|
"转向架",
|
"转向架",
|
"转向架",
|
"转向架",
|
"转向架",
|
"转向架",
|
];
|
|
const spanHide = ref();
|
const spanHide1 = ref();
|
const handleClickOutside = () => {
|
if (spanHide.value && !spanHide.value.contains(event.target)) {
|
contentShow.value = false;
|
}
|
if (spanHide1.value && !spanHide1.value.contains(event.target)) {
|
contentShow1.value = false;
|
}
|
};
|
const showTorque = () => {
|
isTorque.value = true;
|
nextTick(() => {
|
if (innerRef.value.clientHeight > 150) {
|
scrollbarRef.value.setScrollTop(innerRef.value.clientHeight);
|
}
|
});
|
};
|
//添加扭力值
|
const addTorque = () => {
|
torqueFrom.value.processSte = queryData.value.setnum;
|
torqueFrom.value.takeId = queryData.value.takeid;
|
torqueFrom.value.groupOp = queryData.value.grop;
|
if (
|
torqueFrom.value.torqueSize == null ||
|
torqueFrom.value.torqueSize == "" ||
|
torqueFrom.value.torqueSize == 0
|
) {
|
ElMessage({
|
message: "请输入力矩值",
|
type: "warning",
|
});
|
return;
|
}
|
|
AddTorque(torqueFrom.value)
|
.then((res) => {
|
if (res.code == 400) {
|
ElMessage({
|
message: res.message,
|
type: "warning",
|
});
|
return;
|
}
|
ElMessage({
|
message: "添加成功",
|
type: "success",
|
});
|
isTorque.value = false;
|
initData();
|
})
|
.catch((error) => {
|
console.error("添加失败", error);
|
});
|
};
|
const timer = ref(null);
|
|
const createSocket = (url) => {
|
clearInterval(timer.value);
|
// 创建WebSocket连接
|
//"ws://127.0.0.1:9295/admin"
|
client.value = new WebSocket("ws://115.159.85.185:5173/");
|
|
client.value.onopen = function () {
|
console.log("WebSocket 连接成功");
|
};
|
client.value.onmessage = function (event) {
|
let data = JSON.parse(event.data);
|
(gridData.value = [data.process.proNow].map((item) => {
|
return {
|
articleOne: item.ArticleOne,
|
articleOneid: item.ArticleOneid,
|
articleTowid: item.ArticleTowid,
|
articleTwo: item.ArticleTwo,
|
craftContent: item.CraftContent,
|
craftID: item.CraftID,
|
craftType: item.CraftType,
|
craftsStep: item.CraftsStep,
|
createDate: item.CreateDate,
|
creater: item.Creater,
|
material: item.Material,
|
modifier: item.Modifier,
|
modifyDate: item.ModifyDate,
|
moduleName: item.ModuleName,
|
nodal: item.Nodal,
|
pointAxisHPB: item.PointAxisHPB,
|
pointAxisXYZ: item.PointAxisXYZ,
|
setpNum: item.SetpNum,
|
tools: item.Tools,
|
torqueOne: item.TorqueOne,
|
torqueOneQuantity: item.TorqueOneQuantity,
|
torqueSum: item.TorqueSum,
|
torqueTwo: item.TorqueTwo,
|
torqueTwoQuantity: item.TorqueTwoQuantity,
|
};
|
})),
|
(sunNUm.value = data.process.maxproce);
|
obj.value = [data.process.proNow].map((item) => {
|
return {
|
articleOne: item.ArticleOne,
|
articleOneid: item.ArticleOneid,
|
articleTowid: item.ArticleTowid,
|
articleTwo: item.ArticleTwo,
|
craftContent: item.CraftContent,
|
craftID: item.CraftID,
|
craftType: item.CraftType,
|
craftsStep: item.CraftsStep,
|
createDate: item.CreateDate,
|
creater: item.Creater,
|
material: item.Material,
|
modifier: item.Modifier,
|
modifyDate: item.ModifyDate,
|
moduleName: item.ModuleName,
|
nodal: item.Nodal,
|
pointAxisHPB: item.PointAxisHPB,
|
pointAxisXYZ: item.PointAxisXYZ,
|
setpNum: item.SetpNum,
|
tools: item.Tools,
|
torqueOne: item.TorqueOne,
|
torqueOneQuantity: item.TorqueOneQuantity,
|
torqueSum: item.TorqueSum,
|
torqueTwo: item.TorqueTwo,
|
torqueTwoQuantity: item.TorqueTwoQuantity,
|
};
|
})[0];
|
Torque.value = data.operation.map((item) => {
|
return {
|
createDate: item.CreateDate,
|
creater: item.Creater,
|
deviceCode: item.SystemDeviceCode,
|
groupOp: item.GroupOp,
|
id: item.ID,
|
modifier: item.Modifier,
|
modifyDate: item.ModifyDate,
|
processSte: item.ProcessSte,
|
takeId: item.TakeId,
|
torqueSize: item.TorqueSize,
|
};
|
});
|
xPos.value =
|
gridData.value[0].pointAxisXYZ != null &&
|
gridData.value[0].pointAxisXYZ != 0
|
? gridData.value[0].pointAxisXYZ.split(",")[0]
|
: -585;
|
yPos.value =
|
gridData.value[0].pointAxisXYZ != null &&
|
gridData.value[0].pointAxisXYZ != 0
|
? gridData.value[0].pointAxisXYZ.split(",")[1]
|
: 692;
|
zPos.value =
|
gridData.value[0].pointAxisXYZ != null &&
|
gridData.value[0].pointAxisXYZ != 0
|
? gridData.value[0].pointAxisXYZ.split(",")[2]
|
: 692;
|
isJob.value = data.process.finish.map((item) => {
|
return item.moduleName;
|
});
|
cameraAnimate(
|
[xPos.value, yPos.value, zPos.value],
|
gridData.value[0].moduleName
|
);
|
console.log(
|
"WebSocket 接收到消息",
|
data,
|
gridData.value,
|
sunNUm.value,
|
Torque.value,
|
isJob.value
|
);
|
};
|
client.value.onclose = function () {
|
console.log("WebSocket 连接关闭");
|
timer.value = setTimeout(createSocket, 1000);
|
};
|
|
client.value.onerror = function () {};
|
};
|
|
onMounted(() => {
|
console.log("mounted", window.innerWidth, window.innerHeight);
|
|
// 初始化场景
|
renderer.setSize(window.innerWidth - 20, window.innerHeight - 100);
|
renderer.setClearColor(0xeeeeee); // 设置背景色
|
// 启用物理渲染模式
|
// renderer.physicallyCorrectLights = true;
|
// renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
const mainTag = document.querySelector(".my-three");
|
mainTag.appendChild(renderer.domElement);
|
|
// 添加交互控制器(鼠标拖拽缩放)
|
const controls = new OrbitControls(camera, renderer.domElement);
|
controls.enablePan = false; //禁止右键拖拽
|
//相机位置与观察目标点最小值
|
controls.minDistance = 600;
|
//相机位置与观察目标点最大值
|
controls.maxDistance = 1000;
|
|
camera.position.set(...CAMERA_POS);
|
controls.update();
|
|
// 添加光源(重要!否则模型可能显示为全黑)
|
const light = new THREE.DirectionalLight(0xffffff, 3);
|
light.position.set(5, 5, 5);
|
scene.add(light);
|
scene.add(new THREE.AmbientLight(0x404040));
|
renderer.setClearAlpha(0);
|
renderer.setPixelRatio(window.devicePixelRatio);
|
scene.background = null;
|
let dracoLoader = new DRACOLoader();
|
dracoLoader.setDecoderPath("ThreeModel/draco/");
|
dracoLoader.setDecoderConfig({ type: "js" });
|
dracoLoader.preload();
|
// 加载GLTF模型
|
const loader = new GLTFLoader();
|
loader.setDRACOLoader(dracoLoader);
|
loader.load(
|
"ThreeModel/modelDraco.glb", // 替换为你的.gltf文件路径
|
(gltf) => {
|
const model = gltf.scene;
|
model.scale.set(80, 80, 80);
|
scene.add(model);
|
// 自动居中模型(可选)
|
const box = new THREE.Box3().setFromObject(model);
|
const center = box.getCenter(new THREE.Vector3());
|
model.position.sub(center);
|
},
|
(xhr) => {
|
// 加载进度回调
|
// console.log(`${(xhr.loaded / xhr.total * 100).toFixed(1)}% loaded`);
|
},
|
(error) => {
|
console.error("加载失败:", error);
|
}
|
);
|
// 动画循环
|
const animate = () => {
|
requestAnimationFrame(animate);
|
controls.update(); // 启用控制器时需要
|
renderer.render(scene, camera);
|
};
|
initData();
|
|
animate();
|
mainTag.addEventListener("wheel", () => {
|
xPos.value = Math.floor(camera.position.x);
|
yPos.value = Math.floor(camera.position.y);
|
zPos.value = Math.floor(camera.position.z);
|
});
|
mainTag.addEventListener("mousemove", () => {
|
xPos.value = Math.floor(camera.position.x);
|
yPos.value = Math.floor(camera.position.y);
|
zPos.value = Math.floor(camera.position.z);
|
});
|
// };
|
document.addEventListener("click", handleClickOutside);
|
console.log(window.webConfig);
|
|
createSocket();
|
// showModel();
|
});
|
onBeforeUnmount(() => {
|
clearInterval(timer.value);
|
});
|
</script>
|
<style lang="scss" scoped>
|
* {
|
padding: 0;
|
margin: 0;
|
box-sizing: border-box;
|
}
|
|
.Startjob {
|
width: 100%;
|
height: 100%;
|
background: linear-gradient(to left, #02cde6, #02cde6) left top no-repeat,
|
linear-gradient(to bottom, #02cde6, #02cde6) left top no-repeat,
|
linear-gradient(to left, #02cde6, #02cde6) right top no-repeat,
|
linear-gradient(to bottom, #02cde6, #02cde6) right top no-repeat,
|
linear-gradient(to left, #02cde6, #02cde6) left bottom no-repeat,
|
linear-gradient(to bottom, #02cde6, #02cde6) left bottom no-repeat,
|
linear-gradient(to left, #02cde6, #02cde6) right bottom no-repeat,
|
linear-gradient(to left, #02cde6, #02cde6) right bottom no-repeat;
|
background-size: 0.3rem 2rem, 2rem 0.3rem, 0.3rem 2rem, 2rem 0.3rem;
|
padding: 0.2rem;
|
box-sizing: border-box;
|
background-color: rgba(0, 0, 0, 0.1);
|
overflow: hidden;
|
|
.rect {
|
display: flex;
|
flex-direction: column;
|
width: 100%;
|
height: 100%;
|
border: 0.1rem solid #02cde6;
|
box-shadow: 0rem 0rem 0.3rem #02cde6;
|
|
.rect-top {
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
width: 100%;
|
height: 3rem;
|
background: #051d3f;
|
text-align: center;
|
|
img {
|
width: 3.75rem;
|
height: 2.81rem;
|
}
|
|
span {
|
color: rgba(26, 194, 247, 1);
|
font-size: 1.25rem;
|
}
|
}
|
|
.rect-center {
|
display: flex;
|
justify-content: space-around;
|
width: 100%;
|
margin: 0 auto;
|
padding: 0.8rem 2.15rem;
|
box-sizing: border-box;
|
position: absolute;
|
left: 50%;
|
top: 5%;
|
transform: translateX(-50%);
|
.rect-item1 {
|
display: flex;
|
flex-direction: column;
|
width: 16rem;
|
height: 15.38rem;
|
// background-image: url("../../../assets/TheCurrentJob/bg.png");
|
// background-repeat: no-repeat;
|
// background-size: 100% 100%;
|
border-radius: 8px;
|
background: linear-gradient(
|
0deg,
|
rgba(0, 0, 0, 0.001),
|
rgba(0, 0, 0, 0.001)
|
),
|
rgba(0, 0, 0, 0),
|
linear-gradient(
|
135deg,
|
rgba(30, 58, 138, 0.6) -3%,
|
rgba(49, 46, 129, 0.6) 99%
|
);
|
box-sizing: border-box;
|
border: 1px solid rgba(96, 165, 250, 0.3);
|
box-shadow: 0px 4px 6px -4px rgba(0, 0, 0, 0.1),
|
0px 10px 15px -3px rgba(0, 0, 0, 0.1);
|
padding: 0.28rem 1.28rem;
|
box-sizing: border-box;
|
}
|
|
.rect-item2 {
|
width: 65rem;
|
height: 14.38rem;
|
border-radius: 8px;
|
background: linear-gradient(
|
0deg,
|
rgba(0, 0, 0, 0.001),
|
rgba(0, 0, 0, 0.001)
|
),
|
rgba(0, 0, 0, 0),
|
linear-gradient(
|
135deg,
|
rgba(30, 58, 138, 0.6) -3%,
|
rgba(49, 46, 129, 0.6) 99%
|
);
|
box-sizing: border-box;
|
border: 1px solid rgba(96, 165, 250, 0.3);
|
box-shadow: 0px 4px 6px -4px rgba(0, 0, 0, 0.1),
|
0px 10px 15px -3px rgba(0, 0, 0, 0.1);
|
padding: 0.69rem 1.44rem;
|
position: relative;
|
|
.el-table :deep(.el-table__header th) {
|
// border: solid 1px #1ac0f6;
|
color: white;
|
}
|
|
.consten {
|
width: 100%;
|
position: absolute;
|
top: 10rem;
|
z-index: 999;
|
}
|
}
|
|
.rect-item3 {
|
display: flex;
|
flex-direction: column;
|
width: 11.31rem;
|
height: 15.38rem;
|
// background-image: url("../../../assets/TheCurrentJob/bg.png");
|
// background-repeat: no-repeat;
|
// background-size: 100% 100%;
|
border-radius: 8px;
|
background: linear-gradient(
|
0deg,
|
rgba(0, 0, 0, 0.001),
|
rgba(0, 0, 0, 0.001)
|
),
|
rgba(0, 0, 0, 0),
|
linear-gradient(
|
135deg,
|
rgba(30, 58, 138, 0.6) -3%,
|
rgba(49, 46, 129, 0.6) 99%
|
);
|
box-sizing: border-box;
|
border: 1px solid rgba(96, 165, 250, 0.3);
|
box-shadow: 0px 4px 6px -4px rgba(0, 0, 0, 0.1),
|
0px 10px 15px -3px rgba(0, 0, 0, 0.1);
|
padding: 0.28rem 0.28rem;
|
box-sizing: border-box;
|
}
|
}
|
|
.rect-content {
|
display: flex;
|
justify-content: space-between;
|
width: 100%;
|
padding: 0.8rem 2.15rem;
|
box-sizing: border-box;
|
}
|
}
|
}
|
|
// .el-table :deep(.el-table__header th) {
|
// border-bottom: solid 1px #1ac2f7;
|
// color: white;
|
// }
|
.el-table {
|
--el-table-border-color: transparent;
|
--el-table-border: none;
|
--el-table-text-color: #bdbdbe;
|
--el-table-header-text-color: #bdbdbe;
|
--el-table-row-hover-bg-color: transparent;
|
--el-table-current-row-bg-color: transparent;
|
--el-table-header-bg-color: transparent;
|
--el-table-bg-color: transparent;
|
--el-table-tr-bg-color: transparent;
|
--el-table-expanded-cell-bg-color: transparent;
|
}
|
|
.el-table :deep(.el-table__row td) {
|
border: solid 1px #1ac2f7;
|
color: white;
|
}
|
|
.el-table :deep(.el-table__empty-block) {
|
background-color: #137aaf;
|
color: #1ac2f7;
|
}
|
|
// .el-table :deep(.el-table__body-wrapper) {
|
// background-color: #1ac2f7;
|
// }
|
|
:deep(.el-popper) {
|
background-color: #ad2525;
|
}
|
</style>
|