动装分厂三区第一次提交
24
.gitignore
vendored
Normal 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
@ -0,0 +1,3 @@
|
||||
{
|
||||
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
|
||||
}
|
||||
3
README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Vue 3 + TypeScript + Vite
|
||||
# 安装three
|
||||
# npm install three
|
||||
13
index.html
Normal 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
@ -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
26
package.json
Normal 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
|
After Width: | Height: | Size: 50 KiB |
BIN
public/bg.png
Normal file
|
After Width: | Height: | Size: 422 KiB |
BIN
public/dzsq.glb
Normal file
BIN
public/guanji.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
15
public/index.html
Normal 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
|
After Width: | Height: | Size: 45 KiB |
BIN
public/logo.ico
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
public/logo.png
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
public/yunxing.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
6
src/App.vue
Normal file
@ -0,0 +1,6 @@
|
||||
<template>
|
||||
<router-view></router-view>
|
||||
</template>
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
41
src/api/index.js
Normal 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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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);
|
||||
// 将token存储到localStorage中
|
||||
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
@ -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",
|
||||
}
|
||||
})
|
||||