动装分厂三区第一次提交

This commit is contained in:
renna 2025-01-03 14:15:33 +08:00
commit 56acf7667f
25 changed files with 4201 additions and 0 deletions

24
.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

3
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
}

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# Vue 3 + TypeScript + Vite
# 安装three
# npm install three

13
index.html Normal file
View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/logo.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>动装三区</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

25
jsconfig.json Normal file
View File

@ -0,0 +1,25 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src/**/*.js", "src/**/*.vue", "src/main.js"],
// "references": [{ "path": "./jsconfig.node.json" }]
}

2873
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

26
package.json Normal file
View File

@ -0,0 +1,26 @@
{
"name": "my-threejs-vue3",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"@types/three": "^0.155.0",
"axios": "^1.6.2",
"element-plus": "^2.4.1",
"mqtt": "^5.3.0",
"three": "^0.154.0",
"vue": "^3.3.4",
"vue-router": "^4.2.4"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.2.3",
"typescript": "^5.0.2",
"vite": "^4.4.0",
"vue-tsc": "^1.8.3"
}
}

BIN
public/baojing.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
public/bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 422 KiB

BIN
public/dzsq.glb Normal file

Binary file not shown.

BIN
public/guanji.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

15
public/index.html Normal file
View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="./logo.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>重装分厂</title>
<script type="module" crossorigin src="./assets/index-b2e254b4.js"></script>
<link rel="stylesheet" href="./assets/index-c502a728.css">
</head>
<body>
<div id="app"></div>
</body>
</html>

BIN
public/kongxian.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

BIN
public/logo.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

BIN
public/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

BIN
public/yunxing.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

6
src/App.vue Normal file
View File

@ -0,0 +1,6 @@
<template>
<router-view></router-view>
</template>
<script setup>
</script>

41
src/api/index.js Normal file
View File

@ -0,0 +1,41 @@
import request from '../utils/request'
// const baseUrl ="http://www.67934.cn:30102" //外网地址
const baseUrl ="http://192.168.1.150:30702" //本地地址
// 登录接口
export function login(headers) {
return request({
url:baseUrl+'/superlink/authapi/jwt/login',
method: 'post',
headers,
data:{}
})
}
// 租户id
export function getTenants(headers) {
return request({
url:baseUrl+'/superlink/api/tenants/dzfc',
method: 'get',
headers,
data:{}
})
}
// 获取token get请求 路径拼接
export function getToken(tenantId,headers) {
return request({
url: baseUrl+`/superlink/authapi/jwt/getToken?tenantId=${tenantId}`,
method: 'post',
headers:headers,
})
}
// 设备信息
export function getMsg(data,headers) {
return request({
url:baseUrl+'/superlink/api/devices/listDevice',
method: 'post',
data:data,
headers,
})
}

10
src/main.js Normal file
View File

@ -0,0 +1,10 @@
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'
import elementPlus from 'element-plus'
import 'element-plus/dist/index.css'
const app = createApp(App)
app.use(router)
app.use(elementPlus)
app.mount('#app')

20
src/router/index.js Normal file
View File

@ -0,0 +1,20 @@
import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({
history: createWebHashHistory(),
routes: [
{
path: '/',
redirect: '/home'
},
{
path: "/home",
component: () => import('../views/home.vue')
},
]
})
export default router

82
src/style.css Normal file
View File

@ -0,0 +1,82 @@
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
p{
margin: 0;
padding:0;
}
body {
margin: 0;
padding:0;
text-decoration: none;
overflow: hidden;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
.card {
padding: 2em;
}
#app {
margin: 0;
padding: 0;
}
@media (prefers-color-scheme: light) {
:root {
color: #2e4152;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}

54
src/utils/index.js Normal file
View File

@ -0,0 +1,54 @@
/**
* 鼠标单击高亮
*/
import * as THREE from 'three'
import { FXAAShader } from 'three/examples/jsm/shaders/FXAAShader'
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer'
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass'
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass'
import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass'
import { log } from 'three/examples/jsm/nodes/Nodes.js'
/* 选中模型的数组 颜色 */
/**
*
* @param {mesh数组} selectedObjects
* @param {高亮的颜色} color
* @param {scene/camera/renderer} config
*/
let composer, renderPass, outlinePass, effectFXAA
const addColor = ( color, config) => {
// 创建一个EffectComposer效果组合器对象然后在该对象上添加后期处理通道。
composer = new EffectComposer(config.renderer)
console.log("composer",composer);
// 新建一个场景通道 为了覆盖到原理来的场景上
renderPass = new RenderPass(config.scene, config.camera)
composer.addPass(renderPass)
// 物体边缘发光通道
outlinePass = new OutlinePass(new THREE.Vector2(window.innerWidth, window.innerHeight), config.scene, config.camera)
outlinePass.edgeStrength = 10.0 // 边框的亮度
outlinePass.edgeGlow = 1 // 光晕[0,1]
outlinePass.usePatternTexture = false // 是否使用父级的材质
outlinePass.edgeThickness = 1.0 // 边框宽度
outlinePass.downSampleRatio = 2 // 边框弯曲度
outlinePass.pulsePeriod = 5 // 呼吸闪烁的速度
outlinePass.visibleEdgeColor.set(parseInt(color)) // 呼吸显示的颜色
outlinePass.hiddenEdgeColor = new THREE.Color(0, 0, 0) // 呼吸消失的颜色
outlinePass.clear = true
composer.addPass(outlinePass)
// 自定义的着色器通道 作为参数
effectFXAA = new ShaderPass(FXAAShader)
effectFXAA.uniforms.resolution.value.set(1 / window.innerWidth, 1 / window.innerHeight)
effectFXAA.renderToScreen = true
composer.addPass(effectFXAA)
return {
composer, // composer在render循环函数中调用
outlinePass // 实例化一次后设置 outlinePass.selectedObjects = selectedObjects
}
}
export default addColor

40
src/utils/request.js Normal file
View File

@ -0,0 +1,40 @@
import axios from 'axios';
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8';
const request = axios.create({
// axios中请求配置有baseURL选项表示请求URL公共部分
baseURL:'',
// baseURL:'http://www.67934.cn:30102/superlink/api',
// 超时
timeout: 10000,
});
// request拦截器
request.interceptors.request.use(
(config) => {
// 在请求发送之前做些什么
// 可以在请求头中添加token等信息
return config;
},
(error) => {
// 对请求错误做些什么
return Promise.reject(error);
}
);
// response拦截器
request.interceptors.response.use(
(response) => {
// 对响应数据做些什么
return response.data;
},
(error) => {
// 对响应错误做些什么
return Promise.reject(error);
}
);
export default request;

955
src/views/home.vue Normal file
View File

@ -0,0 +1,955 @@
<template>
<div id="loader">
<div id="loader-wrapper">
<img class="pic" src="/logo.png" alt="" />
<p class="text">动装三区</p>
</div>
<div id="progress">
<el-progress :percentage="parseFloat(jd)" :text-inside="true" :stroke-width="26" striped striped-flow
:duration="10" />
</div>
</div>
<div>
<!-- <div id="po" v-html="po"></div> -->
</div>
<div v-if="showInfo" class="popover" :style="{
top: popoverTop + 'px',
left: popoverLeft + 'px',
}">
<div class="tri">
<div class="left" v-if="pageDeviceName">
<p class="bg_left">设备名称</p>
<h4 class="bg">{{ pageDeviceName }}</h4>
</div>
<div class="left" v-if="pageDeviceNumber">
<p class="bg_left">设备编号</p>
<h4 class="bg">{{ pageDeviceNumber }}</h4>
</div>
<div class="left" v-if="pageDeviceTaskNumber">
<p class="bg_left">工票编号</p>
<h4 class="bg">{{ pageDeviceTaskNumber }}</h4>
</div>
<div class="left" v-if="pageDeviceWork">
<p class="bg_left">工作令号</p>
<h4 class="bg">{{ pageDeviceWork }}</h4>
</div>
<div class="left" v-if="pageDevicePic">
<p class="bg_left">零件图号</p>
<h4 class="bg">{{ pageDevicePic }}</h4>
</div>
<div class="left" v-if="pageDeviceStatus">
<p class="bg_left">设备状态</p>
<h4 class="bg">{{ pageDeviceStatus }}</h4>
</div>
<div class="left" v-if="pageDeviceDuration">
<p class="bg_left">持续时间</p>
<h4 class="bg">{{ pageDeviceDuration }}</h4>
</div>
<div class="left" v-if="pageDeviceReason">
<p class="bg_left">待机原因</p>
<h4 class="bg" >{{ pageDeviceReason }}</h4>
</div>
<div class="left1" v-if="infoMsg">
<p class="bg_left1">详细信息</p>
<div class="info" >
<div class="info_list" v-if="isNumeric(infoMsg.sspeedovr)">主轴倍率:{{ Math.abs(infoMsg.sspeedovr).toFixed(3) }}
</div>
<div class="info_list" v-if="isNumeric(infoMsg.path_svalue)">主轴转速:{{ Math.abs(infoMsg.path_svalue).toFixed(3)
}}</div>
<div class="info_list" v-if="isNumeric(infoMsg.feed_commanded)">进给设定值:{{
Math.abs(infoMsg.feed_commanded).toFixed(3) }}</div>
<div class="info_list" v-if="isNumeric(infoMsg.path_feedrate)">进给速度:{{
Math.abs(infoMsg.path_feedrate).toFixed(3) }}</div>
<div class="info_list" v-if="isNumeric(infoMsg.feed_ovr)">进给倍率:{{ Math.abs(infoMsg.feed_ovr).toFixed(3) }}
</div>
<div class="info_list" v-if="isNumeric(infoMsg.s1load)">主轴负载:{{ Math.abs(infoMsg.s1load).toFixed(3) }}</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import * as THREE from "three";
import mqtt from "mqtt";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
//
import { OutlineEffect } from "three/examples/jsm/effects/OutlineEffect.js";
import { getMsg, login, getTenants, getToken } from "../api/index";
const po = ref(null);
const jd = ref();
const selectedItem = ref(null); //
const deviceMapping = {
1: {
id: 1,
name: "6111铣床(3)",
number: "026-078",
},
2: {
id: 2,
name: "6111铣床(2)",
number: "026-079",
},
3: {
id: 3,
name: "6111铣床(1)",
number: "026-080",
},
4: {
id: 4,
name: "6513镗床",
number: "026-071",
},
5: {
id: 5,
name: "65车床(1)",
number: "016-368",
},
6: {
id: 6,
name: "1.6M立车",
number: "015-117",
},
7: {
id: 7,
name: "2.5M立车",
number: "015-098",
},
8: {
id: 8,
name: "1.6M立车(3)",
number: "015-100",
},
9: {
id: 9,
name: "1.6M立车(2)",
number: "015-116",
},
10: {
id: 10,
name: "1.6M立车(1)",
number: "015-115",
},
11: {
id: 11,
name: "2M立车",
number: "015-092",
},
12: {
id: 12,
name: "65车床(2)",
number: "016-399",
},
13: {
id: 13,
name: "65车床(3)",
number: "016-434",
},
14: {
id: 14,
name: "伞齿机",
number: "053-023",
},
15: {
id: 15,
name: "6513镗床",
number: "026-064",
},
16: {
id: 16,
name: "6813镗床",
number: "026-081",
},
17: {
id: 17,
name: "1.6M立车(7)",
number: "015-096",
},
18: {
id: 18,
name: "1.6M立车(6)",
number: "015-099",
},
19: {
id: 19,
name: "1.6M立车(5)",
number: "015-013",
},
};
const showInfo = ref(false);
//
let camera;
let scene;
let renderer;
let controls;
let analyser;
let audioSource;
let listener;
let model;
let selectedObjectName;
//
let effect;
let outlineEffect = null;
let selectObject = null;
const factory = {}; //
const clock = new THREE.Clock(); // Clock
let selectedObject;
const popoverRef = ref(null);
const popoverTop = ref(0);
const popoverLeft = ref(0);
const popoverData = ref({});
const pageDeviceName = ref(null);
const pageDeviceNumber = ref(null);
const pageDeviceStatus = ref(null);
const pageDeviceDuration = ref(null);
const pageDeviceWork = ref(null);
const pageDevicePic = ref(null);
const pageDeviceTaskNumber = ref(null);
const pageDeviceReason = ref(null);
onMounted(() => {
toLogin();
MQTT()
init();
renderer.domElement.addEventListener("click", onMouseClick);
});
// login
const toLogin = () => {
const headers = {
Authorization: "Basic c3pyOnN6cjEyMyQlXg==",
};
login(headers) //
.then((result) => {
// console.log("---------result.data",result.data);
if (result.code === 100) {
// console.log("", result.data.token);
// tokenlocalStorage
authorization.value = result.data.token;
// localStorage.setItem('token', result.data.token);
toId();
}
})
.catch((error) => {
console.error("登录接口请求失败", error);
});
};
// id getTenants
const userId = ref();
const toId = () => {
const headers = {
// Authorization: `Bearer ${localStorage.getItem('token')}`
Authorization: `Bearer ${authorization.value}`,
};
// console.log("-----------------headers---------",headers);
getTenants(headers) //
.then((result) => {
// console.log("---------result.data",result.data);
if (result.code === 100) {
// console.log("id", result.data.id);
userId.value = result.data.id;
toToken();
}
})
.catch((error) => {
console.error("拿到租户id请求失败", error);
});
};
// token getToken
const authorization = ref();
const token = ref();
const toToken = () => {
const headers = {
Authorization: `Bearer ${authorization.value}`,
};
const tenantId = userId.value;
getToken(tenantId, headers) //
.then((result) => {
if (result.code === 100) {
// console.log("token", result.data);
token.value = result.data;
getMsgInfo();
}
})
.catch((error) => {
console.error("获取token请求失败", error);
});
};
function getStatusText(status) {
if (status === "RUNNING") {
return "运行";
} else if (status === "IDLE") {
return "空闲";
} else if (status === "ALARM") {
return "报警";
} else {
return "关机";
}
}
function getDuration(commencementTime) {
// console.log("", commencementTime);
const [_, time] = commencementTime.split(" ");
const [hours, minutes, seconds] = time.split(":");
const oldTime = new Date();
oldTime.setHours(hours, minutes, seconds);
// console.log("::", oldTime);
const currentTime = new Date(); //
// console.log("", currentTime);
const duration = new Date(currentTime - oldTime);
// console.log("", duration);
const durationHours = duration.getUTCHours().toString().padStart(2, "0");
const durationMinutes = duration.getUTCMinutes().toString().padStart(2, "0");
const durationSeconds = duration.getUTCSeconds().toString().padStart(2, "0");
const durationString = `${durationHours}:${durationMinutes}:${durationSeconds}`;
// console.log("::", durationString);
return durationString;
}
const newDeviceList = ref();
const getMsgInfo = () => {
const headers = {
Authorization: token.value,
};
const params = {
pageNumber: "0",
pageSize: "0",
queryDeviceMestag: true,
queryAndontag:true,
};
getMsg(params, headers).then((result) => {
// console.log("", result);
const deviceList = {};
result.data.results.forEach((device) => {
const deviceInfo = Object.values(deviceMapping).find(
(info) => info.number === device.token
);
// console.log("device",device);
// console.log("deviceInfo", deviceInfo);
if (deviceInfo) {
const id = deviceInfo.id;
const name = device.name;
const number = deviceInfo.number;
const status = getStatusText(device.status);
let duration = null;
let work = null;
let pic = null;
let taskNumber = null;
let reason = null;
if (device.andonInfo) {
reason = device.andonInfo.andonSetName;
console.log("reason",reason);
}
if (device.deviceMesTask && device.deviceMesTask.commencementTime) {
// console.log("");
duration = getDuration(device.deviceMesTask.commencementTime);
// console.log("duration", device.deviceMesTask);
work = device.deviceMesTask.workNo;
// console.log("work", work);
pic = device.deviceMesTask.parentDutyCode.replace(/^\w/, "*");
// console.log("pic", pic);
taskNumber = device.deviceMesTask.taskNumber;
// console.log("taskNumber",taskNumber);
}
deviceList[id] = { id, name, number, status,taskNumber, duration, work, pic,reason };
}
});
console.log("新的设备列表", deviceList);
newDeviceList.value = deviceList;
});
};
const infoMsg = ref(null);
const isNumeric = (value) => {
return !isNaN(parseFloat(value)) && isFinite(value);
};
let mqttClient = null;
// MQTT
const MQTT = () => {
mqttClient = mqtt.connect("ws://172.16.2.5:30714/mqtt");
// mqttClient = mqtt.connect("ws://www.67934.cn:30114/mqtt");
mqttClient.on("connect", () => {
console.log("MQTT 连接成功");
for (const key in deviceMapping) {
const device = deviceMapping[key];
const topic = `$avic/superlink/iot/zzfc/${device.number}/measurement`;
mqttClient.subscribe(topic);
}
});
mqttClient.on("message", (topic, message) => {
// console.log("", message.toString());
const sanitizedMessage = message.toString().replace(/\\/g, '');
// JSON
const data = JSON.parse(sanitizedMessage);
// console.log("data",data);
if (data.deviceToken === pageDeviceNumber.value) {
infoMsg.value = data.request.metadata;
// console.log("infoMsg.value", infoMsg.value);
}
});
mqttClient.on("error", (error) => {
console.log("MQTT 连接失败:", error);
});
};
function init() {
// div
const container = document.createElement("div");
document.body.appendChild(container);
//
camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
1,
2000
);
// camera.position.set(8,9,0);
// camera.position.set(-1.34, 186.8, 129);
// camera.position.set(-0,65,1.3);
// camera.position.set(-40,22,91);
camera.position.set(-125,139,-0.2);
//
scene = new THREE.Scene();
//0xa0a0a0 16 #bababa
scene.background = new THREE.Color(0x000000);
// const bgTextureImg ='./bg.png'
// //
// const bgTexture = new THREE.TextureLoader().load(bgTextureImg);
// //
// scene.background = bgTexture;
//
// const axesHelper = new THREE.AxesHelper(50);
// axesHelper.position.y = 100
// scene.add(axesHelper);
//
const ambient = new THREE.AmbientLight(0xf0f0f0, 1.5);
scene.add(ambient);
//
const directionalLight = new THREE.DirectionalLight(0xffffff,1);
// directionalLight.position.set(50, 100, -350).normalize();
directionalLight.position.set(80, 100, -150)
// directionalLight.position.set(101,80,-58); //
// directionalLight.target.position.set(-50, 0, -30); //
directionalLight.castShadow = true; //
// directionalLight.target = target
// 齿
directionalLight.shadow.mapSize.set(4096, 4096);
//
directionalLight.shadow.radius = 1;
// //
directionalLight.shadow.camera.left = -450;
directionalLight.shadow.camera.right = 450;
directionalLight.shadow.camera.top = 450;
directionalLight.shadow.camera.bottom = -450;
directionalLight.shadow.camera.near = 0.5;
directionalLight.shadow.camera.far = 1200;
// directionalLight.target = mesh;
scene.add(directionalLight);
scene.add(directionalLight.target);
//
// const cameraHelper = new THREE.CameraHelper(directionalLight.shadow.camera);
// scene.add(cameraHelper);
//
renderer = new THREE.WebGLRenderer({ antialias: true });
//
renderer.setPixelRatio(window.devicePixelRatio);
//
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
// .shadowMap.type
renderer.shadowMap.type = THREE.VSMShadowMap;
container.appendChild(renderer.domElement);
controls = new OrbitControls(camera, renderer.domElement);
// controls.enableDamping = true; //
// controls.rotateSpeed = 0.5; //
// 使animate
//controls.addEventListener( 'change', render );
// 使使
// controls.enableDamping = true;
//
controls.enableRotate = true;
controls.rotateSpeed = 0.5;
//
controls.dampingFactor = 0.25;
//
controls.enableZoom = true;
controls.zoomSpeed = 1.0;
//
controls.autoRotate = false;
controls.autoRotateSpeed = 0.5;
//
controls.minDistance = 0.1;
//
controls.maxDistance = 200;
//
controls.enablePan = true;
//z
// controls.target.set(-40, -40, 40); //
controls.target.set(0, 0, 0); //
const modelFile = "./dzsq.glb";
// GLTFLoader
const loader = new GLTFLoader();
model = new THREE.Group(); // Group
// model.position.set(10, 5, 30);
// AudioListener
listener = new THREE.AudioListener();
camera.add(listener);
loader.load(
modelFile,
function (gltf) {
gltf.scene.traverse(function (obj) {
if (obj.isMesh) {
//
// Mesh
obj.castShadow = true;
obj.receiveShadow = true;
}
});
gltf.scene.scale.multiplyScalar(1);
model.add(gltf.scene);
model.position.x = 2;
model.position.y = 28;
// model.position.z = 10;
model.receiveShadow = true;
updateSprites(gltf.scene)
scene.add(model); // model
document.getElementById("loader").style.visibility = "hidden";
document.getElementById("progress").style.visibility = "hidden";
},
(xhr) => {
// console.log(''+(xhr.loaded/xhr.total*100)+'%')
jd.value = ((xhr.loaded / xhr.total) * 100).toFixed(1);
}
);
animate();
}
const updateSprites = (modelScene) => {
//
const textureLoader = new THREE.TextureLoader();
const textures = {
运行: textureLoader.load("./yunxing.png"),
空闲: textureLoader.load("./kongxian.png"),
报警: textureLoader.load("./baojing.png"),
关机: textureLoader.load("./guanji.png"),
}; //
for (let i = 1; i <= 19; i++) {
const modelName = i.toString();
factory[modelName] = modelScene.getObjectByName(modelName);
const spriteModel = factory[modelName]; // // const spritePosition = factory[modelName].position; // console.log("",factory[modelName].position); //
if (!spriteModel) {
console.warn(`模型 ${modelName} 未找到`);
continue; //
}
// console.log("",factory[modelName].name);
// const randomTextureIndex = Math.floor(Math.random() * textures.length);
// const spriteTexture = textures[randomTextureIndex]; //
//
if (newDeviceList.value && newDeviceList.value[modelName] && newDeviceList.value[modelName].status) {
const deviceStatus = newDeviceList.value[modelName].status;
// console.log("11111111111111111111deviceStatus",deviceStatus);
//
const spriteTexture = textures[deviceStatus];
// console.log("2222222222222222222spriteTexture",spriteTexture);
const spriteMaterial = new THREE.SpriteMaterial({
map: spriteTexture,
}); //
const sprite = new THREE.Sprite(spriteMaterial);
sprite.scale.set(4.5,4.5,4.5); //
const spritePosition = spriteModel.position.clone();
spritePosition.y += 2; // 4
// spritePosition.x -= 1; // 4
sprite.position.copy(spritePosition); //
spriteModel.parent.add(sprite);
}
}
// console.log("factory", factory);
}
//
function addOutline(object) {
outlineEffect = new OutlineEffect(renderer, {
defaultThickness: 0.003, //线
defaultColor: "#88ced5", //线
efaultAlpha: 0.9,
// defaultKeepAlive: true // keeps outline material in cache even if material is removed from scene
});
}
//
function stopOutlineEffect() {
outlineEffect = null;
}
//
let px;
let py;
function onMouseClick(event) {
px = event.offsetX;
py = event.offsetY;
//
const x = (px / window.innerWidth) * 2 - 1;
const y = -(py / window.innerHeight) * 2 + 1;
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(new THREE.Vector2(x, y), camera);
// console.log(raycaster, "raycaster");
// 线
const intersects = raycaster.intersectObjects(Object.values(factory));
// console.log("intersects", intersects);
if (!intersects.length) return;
//
selectedObject = intersects[0].object;
// findClickModel
findClickModel(selectedObject);
}
const findClickModel = (object) => {
// 'Group'selectedObjectName
if (
object.type === "Group" &&
object.name !== "" &&
object.name !== "Scene"
) {
selectedObjectName = object.name;
// console.log("selectedObjectName", selectedObjectName);
//
if (showInfo.value) {
//
hideInfo();
stopOutlineEffect();
} else {
//
openInfo(selectedObjectName, px, py);
// addOutline(selectedObject);
}
}
// 'Scene'findClickModel
if (object.parent && object.type !== "Scene") {
findClickModel(object.parent);
}
};
let updateInterval;
let durationInterval; //
//
function openInfo(object, deviceX, deviceY) {
// console.log("object", object);
hideInfo();
// object
const newInfo = newDeviceList.value[object];
// console.log("newInfo", newInfo);
if (newInfo) {
const name = newInfo.name;
const number = newInfo.number;
const status = newInfo.status;
let duration = newInfo.duration;
const work = newInfo.work;
const pic = newInfo.pic;
const taskNumber = newInfo.taskNumber;
const stopReason = newInfo.reason;
// const { name, number, status, duration, work, pic, taskNumber, reason } = newInfo;
// console.log(":", name);
pageDeviceName.value = name;
// console.log(":", number);
pageDeviceNumber.value = number;
// console.log(":", status);
pageDeviceStatus.value = status;
pageDeviceReason.value = stopReason;
pageDeviceTaskNumber.value = taskNumber;
// console.log("",pageDeviceTaskNumber.value);
// console.log(":", duration);
pageDeviceDuration.value = duration;
// console.log(":", work);
pageDeviceWork.value = work;
// console.log(":", pic);
pageDevicePic.value = pic;
//
durationInterval = setInterval(() => {
duration = updateDuration(duration);
pageDeviceDuration.value = duration;
}, 1000);
}
if (object) {
//
// selectedItem.value = device; //
//
showInfo.value = true;
//
popoverTop.value = deviceY;
popoverLeft.value = deviceX;
// console.log("popoverTop.value", popoverTop.value);
// console.log("popoverLeft.value", popoverLeft.value);
// getMsgInfo()
updateInterval = setInterval(() => {
getMsgInfo();
updateSprites(model); //
}, 5000);
} else {
//
showInfo.value = false;
// false
}
}
//
function updateDuration(duration) {
if (duration) {
const durationSplit = duration.split(":");
let hours = parseInt(durationSplit[0]);
let minutes = parseInt(durationSplit[1]);
let seconds = parseInt(durationSplit[2]);
seconds++;
if (seconds >= 60) {
seconds = 0;
minutes++;
if (minutes >= 60) {
minutes = 0;
hours++;
}
}
return (
hours.toString().padStart(2, "0") +
":" +
minutes.toString().padStart(2, "0") +
":" +
seconds.toString().padStart(2, "0")
);
}
}
//
function hideInfo() {
//
showInfo.value = false;
//
pageDeviceName.value = "";
pageDeviceNumber.value = "";
pageDeviceStatus.value = "";
pageDeviceReason.value = "";
pageDeviceTaskNumber.value = "";
pageDeviceDuration.value = "";
pageDeviceWork.value = "";
pageDevicePic.value = "";
//
clearInterval(updateInterval);
clearInterval(durationInterval);
}
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
// console.log('camera.position', camera.position) //
po.value =
"x:" +
camera.position.x +
"<br/> y:" +
camera.position.y +
"<br/> z:" +
camera.position.z;
//
renderer.render(scene, camera);
//
if (selectedObject && outlineEffect) {
renderer.setClearAlpha(0);
outlineEffect.render(selectedObject, camera);
}
controls.update();
// console.log('Model position:', model.position);
}
</script>
<style>
body {
margin: 0;
/* background-color: #000; */
color: #fff;
font-family: "宋体";
font-size: 16px;
line-height: 24px;
overscroll-behavior: none;
}
#po {
position: absolute;
right: 10px;
top: 10px;
width: 200px;
height: 200px;
/* background-color: red; */
color: #FFF;
}
.popover {
position: absolute;
border-radius: 5px;
font-size: 0.2rem;
color: #fff;
/* width: 24rem;
height: 12rem; */
}
.tri {
/* width: 22rem; */
/* height: 10rem; */
min-width: 12rem;
min-height: 5vh;
background: url("/bg.png") no-repeat;
background-size: 100% 100%;
padding: 20px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.left {
display: flex;
justify-content: flex-start;
align-items: center;
gap: 10px;
font-size: 0.9rem;
font-weight: bold;
color: #fff;
}
.left1 {
display: flex;
justify-content: flex-start;
align-items: flex-start;
gap: 10px;
font-size: 0.9rem;
font-weight: bold;
color: #fff;
}
.bg_left {
width: 80px;
margin: 0;
padding: 0;
font-size: 0.9rem;
font-weight: bold;
}
.bg {
margin: 0;
padding: 0;
font-size: 0.9rem;
}
/* */
#loader {
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: black;
position: fixed;
z-index: 1;
top: 0px;
left: 0px;
color: #ffffff;
}
#loader-wrapper {
width: 100%;
height: 200px;
display: flex;
justify-content: center;
align-items: center;
gap: 20px;
}
/* .pic {
width: 130px;
height: 130px;
} */
.text {
font-size: 42px;
font-weight: bold;
color: #fff;
background: none;
}
#progress {
width: 30%;
height: 20px;
border-radius: 50%;
/* border:1px solid red; */
}
.info {
display: grid;
grid-template-columns: 1fr 1fr; /* 将info容器内的元素排列成一列 */
gap: 10px; /* 元素之间的间隔 */
font-size: 0.9rem;
font-weight: bold;
}
.info_list {
/* 每个info_list元素的样式设置 */
font-size: 0.9rem;
font-weight: bold;
}
.bg_left1 {
width: 80px;
font-size: 0.9rem;
font-weight: bold;
}
</style>

11
vite.config.js Normal file
View File

@ -0,0 +1,11 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
base:'./',
plugins: [vue()],
server: {
host: "0.0.0.0",
}
})