dataDashboard.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. <template>
  2. <div class="screen">
  3. <div style="height: 10px"></div>
  4. <div class="titleImage">
  5. <el-image :src="titleUrl" />
  6. <span class="Heading">龙弛智慧隧道系统</span>
  7. </div>
  8. <div class="coreBox">
  9. <div ref="container" class="three-container"></div>
  10. <div class="formBox">
  11. <div class="panel">
  12. <span class="panel-title">亮度调整</span>
  13. <el-slider
  14. v-model="transparency"
  15. :step="50"
  16. show-stops
  17. :show-tooltip="false"
  18. class="panel-slider"
  19. @change="changeTransparency"
  20. />
  21. <div class="brightness" style="margin: 90px 0 0 25px;">
  22. <span>低</span>
  23. <span>中</span>
  24. <span>高</span>
  25. </div>
  26. <el-slider
  27. v-model="transparencyTwo"
  28. :step="50"
  29. show-stops
  30. :show-tooltip="false"
  31. class="panel-slider2"
  32. @change="changeTransparencyTwo"
  33. />
  34. <div class="brightness" style="margin: 50px 0 0 25px;">
  35. <span>低</span>
  36. <span>中</span>
  37. <span>高</span>
  38. </div>
  39. <div class="panel-bottom"></div>
  40. </div>
  41. </div>
  42. <div class="formBox_two">
  43. <div class="panel" style="height: 100px">
  44. <span class="panel-title">轴流式风机</span>
  45. <span class="panelSwitchTitle">风机开关</span>
  46. <el-switch class="panelSwitch" size="large"></el-switch>
  47. <div class="panel-bottom"></div>
  48. </div>
  49. </div>
  50. <div class="vehicleBox">
  51. <div class="panel" style="height: 190px">
  52. <span class="panel-title">车辆检测器</span>
  53. <el-form style="position: absolute;left: 40px;top: 50px">
  54. <el-form-item label="交通量" label-width="110" label-position="left">
  55. <el-row style="width: 120px">
  56. <el-col :span="15" class="vehicleData">
  57. <span>123</span>
  58. </el-col>
  59. <el-col :span="9" class="vehicleUnit">
  60. <span>辆</span>
  61. </el-col>
  62. </el-row>
  63. </el-form-item>
  64. <el-form-item label="平均车速" label-width="110" label-position="left">
  65. <el-row style="width: 120px">
  66. <el-col :span="15" class="vehicleData">
  67. <span>66</span>
  68. </el-col>
  69. <el-col :span="9" class="vehicleUnit">
  70. <span>km/h</span>
  71. </el-col>
  72. </el-row>
  73. </el-form-item>
  74. <el-form-item label="车道占有量" label-width="110" label-position="left">
  75. <el-row style="width: 120px">
  76. <el-col :span="15" class="vehicleData">
  77. <span>12</span>
  78. </el-col>
  79. <el-col :span="9" class="vehicleUnit">
  80. <span>%</span>
  81. </el-col>
  82. </el-row>
  83. </el-form-item>
  84. </el-form>
  85. <div class="panel-bottom"></div>
  86. </div>
  87. </div>
  88. </div>
  89. </div>
  90. </template>
  91. <script setup>
  92. import * as THREE from 'three';
  93. import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
  94. import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
  95. import { onMounted, onUnmounted, reactive, ref } from 'vue';
  96. import { useScreenStore } from '@/pinia/modules/screen'
  97. // 单灯控制
  98. import { updateTunnelLamp } from "@/api/tunnel";
  99. // 四路控制
  100. import { deviceSwitch } from "@/api/device";
  101. // 标题图片
  102. import titleUrl from '@/assets/title_bg.png'
  103. import {ElMessage} from "element-plus";
  104. const useScreen = useScreenStore()
  105. // 获取容器引用
  106. const container = ref(null);
  107. // 定义 Three.js 相关变量
  108. let scene, camera, renderer, controls;
  109. // 创建场景
  110. scene = new THREE.Scene();
  111. // 添加环境光
  112. const ambientLight = new THREE.AmbientLight(0xffffff, 1);
  113. // 初始化 Three.js 场景
  114. const initThree = () => {
  115. scene.background = null;
  116. // textureLoader.load('@/assets/OIP-C.jpg', (texture) => {
  117. // // 将图片设置为场景的背景
  118. // scene.background = texture;
  119. // });
  120. // 创建相机
  121. camera = new THREE.PerspectiveCamera(
  122. 75,
  123. container.value.clientWidth / container.value.clientHeight,
  124. 0.1,
  125. 1000
  126. );
  127. camera.position.set(30, 20, 30);
  128. // 创建渲染器
  129. renderer = new THREE.WebGLRenderer({ antialias: true });
  130. renderer.setSize(container.value.clientWidth, container.value.clientHeight);
  131. renderer.setClearAlpha(0);
  132. // renderer.setClearColor(222842);
  133. container.value.appendChild(renderer.domElement);
  134. // 添加轨道控制器
  135. controls = new OrbitControls(camera, renderer.domElement);
  136. controls.enableDamping = true; // 启用阻尼效果
  137. controls.dampingFactor = 0.05;
  138. scene.add(ambientLight);
  139. // 添加平行光
  140. const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
  141. directionalLight.position.set(5, 5, 5).normalize();
  142. scene.add(directionalLight);
  143. };
  144. // 存储模型数据
  145. let materialOne = reactive({})
  146. let materialTwo = reactive({})
  147. const relayList = reactive([])
  148. // 加载 GLB 模型
  149. const loadModel = () => {
  150. const loader = new GLTFLoader();
  151. loader.load('../../public/隧道1.glb', function (gltf){
  152. let object = gltf.scene
  153. console.log(object.children)
  154. object.children.forEach(item => {
  155. if (item.name === '柱体') {
  156. materialOne = item.children[3].material
  157. } else if(item.name === '柱体001') {
  158. materialTwo = item.children[3].material
  159. }
  160. })
  161. //gltf.scene获取gltf文件包含的模型数据
  162. scene.add(gltf.scene)
  163. }, function (xhr) {
  164. // 加载进度回调
  165. // console.log((xhr.loaded / xhr.total * 100) + '% loaded');
  166. }, function (error) {
  167. // 加载错误回调
  168. // console.error('An error happened', error);
  169. }
  170. )
  171. };
  172. // 动画循环
  173. const animate = () => {
  174. requestAnimationFrame(animate);
  175. controls.update(); // 更新控制器
  176. renderer.render(scene, camera);
  177. };
  178. // 亮度调整
  179. const transparency = ref(0)
  180. const changeTransparency = (e) => {
  181. let option = {
  182. 0: [0.8, 33, 1],
  183. 50: [0.5, 66, 2],
  184. 100: [0.2, 100, 3]
  185. }
  186. materialOne.opacity = option[e][0]
  187. if (lampData.switchType === '单灯控制器') {
  188. updateTunnelLamp({
  189. id: lampData.ID,
  190. tunnelSn: lampData.tunnelSn,
  191. lampValue1: option[e][1]
  192. }).then(res => {
  193. if (res.code === 0) {
  194. ElMessage.success('已发送')
  195. }
  196. })
  197. } else {
  198. let relay = relayList[0].deviceRelays
  199. for (let i = 0; i < relay.length; i++) {
  200. relay[i].state = i < option[e][2];
  201. }
  202. for (let j = 0; j < relay.length; j++) {
  203. deviceSwitch({
  204. tunnelSn: lampData.tunnelSn,
  205. radarId: relayList[0].radarId,
  206. relayId: relay[j].relayId,
  207. state: relay[j].state
  208. }).then(res => {
  209. if (res.code === 0) {
  210. console.log('发送成功')
  211. }
  212. })
  213. }
  214. }
  215. }
  216. const transparencyTwo = ref(0)
  217. const changeTransparencyTwo = (e) => {
  218. let option = {
  219. 0: [0.8, 33, 1],
  220. 50: [0.5, 66, 2],
  221. 100: [0.2, 100, 3]
  222. }
  223. materialTwo.opacity = option[e][0]
  224. if (lampData.switchType === '单灯控制器') {
  225. updateTunnelLamp({
  226. id: lampData.ID,
  227. tunnelSn: lampData.tunnelSn,
  228. lampValue2: option[e][1]
  229. }).then(res => {
  230. if (res.code === 0) {
  231. ElMessage.success('已发送')
  232. }
  233. })
  234. } else {
  235. let relay = relayList[1].deviceRelays
  236. for (let i = 0; i < relay.length; i++) {
  237. relay[i].state = i < option[e][2];
  238. }
  239. for (let j = 0; j < relay.length; j++) {
  240. deviceSwitch({
  241. tunnelSn: lampData.tunnelSn,
  242. radarId: relayList[1].radarId,
  243. relayId: relay[j].relayId,
  244. state: relay[j].state
  245. }).then(res => {
  246. if (res.code === 0) {
  247. console.log('发送成功');
  248. }
  249. })
  250. }
  251. }
  252. }
  253. // 灯光数据
  254. const lampData = reactive({})
  255. const judgeLamp = () => {
  256. relayList.length = 0
  257. if (lampData.switchType === '单灯控制器') {
  258. let val1 = lampData.lampValue1
  259. let val2 = lampData.lampValue2
  260. let judge = {
  261. 33: [0.8,0],
  262. 66: [0.5,50],
  263. 100: [0.2,100],
  264. }
  265. transparency.value = judge[val1][1]
  266. materialOne.opacity = judge[val1][0]
  267. transparencyTwo.value = judge[val1][1]
  268. materialTwo.opacity = judge[val2][0]
  269. } else if(lampData.switchType === '四路控制器') {
  270. lampData.devices.forEach(item => {
  271. if (item.genre === 6) {
  272. relayList.push(item)
  273. }
  274. })
  275. let deviceLamp1 = relayList[0].deviceRelays
  276. let deviceLamp2 = relayList[1].deviceRelays
  277. let count1 = 0
  278. let count2 = 0
  279. deviceLamp1.forEach(item => {
  280. item.state ? count1++ : count1
  281. })
  282. deviceLamp2.forEach(item => {
  283. item.state ? count2++ : count2
  284. })
  285. let judgeWay = {
  286. 1: [0.8,0],
  287. 2: [0.5,50],
  288. 3: [0.2,100],
  289. 4: [0.2,100]
  290. }
  291. transparency.value = judgeWay[count1][1]
  292. materialOne.opacity = judgeWay[count1][0]
  293. transparencyTwo.value = judgeWay[count2][1]
  294. materialTwo.opacity = judgeWay[count2][0]
  295. }
  296. }
  297. // 组件挂载时初始化
  298. onMounted(() => {
  299. Object.assign(lampData, useScreen.controllerData)
  300. initThree();
  301. loadModel();
  302. animate();
  303. setTimeout(function () {
  304. judgeLamp()
  305. },500)
  306. });
  307. // 组件卸载时清理资源
  308. onUnmounted(() => {
  309. if (renderer) {
  310. renderer.dispose();
  311. }
  312. });
  313. </script>
  314. <style scoped lang="less">
  315. .horn {
  316. position: absolute;
  317. content: "";
  318. width: 10px;
  319. height: 10px;
  320. }
  321. .screen{
  322. width: 100%;
  323. height: 970px;
  324. //background-image: url("@/assets/back.jpeg");
  325. background-size:100% 100%;
  326. background: #1d2b56 url("@/assets/line.png");
  327. .titleImage {
  328. width: 100%;
  329. height: 70px;
  330. display: flex;
  331. align-items: center;
  332. justify-content: center;
  333. position: relative;
  334. .Heading{
  335. position: absolute;
  336. left: 42%;
  337. top: 25%;
  338. text-align: center;
  339. font-size: 1.7vw;
  340. font-weight: bold;
  341. color: #0EFCFF;
  342. }
  343. }
  344. .coreBox{
  345. width: 100%;
  346. height: 900px;
  347. margin-top: 20px;
  348. position: relative;
  349. .three-container {
  350. z-index: 1;
  351. position: absolute;
  352. height: 800px;
  353. width: 100%;
  354. }
  355. .formBox{
  356. position: absolute;
  357. right: 20px;
  358. z-index:2;
  359. }
  360. .formBox_two{
  361. position: absolute;
  362. right: 20px;
  363. top: 250px;
  364. z-index:2;
  365. }
  366. .vehicleBox{
  367. position: absolute;
  368. right: 20px;
  369. top: 380px;
  370. z-index:2;
  371. .vehicleData{
  372. display: flex;
  373. justify-content: end;
  374. align-items: center;
  375. span {
  376. font-size: 18px;
  377. font-weight: bold;
  378. color: #0EFCFF;
  379. }
  380. }
  381. .vehicleUnit{
  382. display: flex;
  383. justify-content: end;
  384. align-items: center;
  385. span {
  386. color: #909399;
  387. }
  388. }
  389. }
  390. .panel{
  391. position: relative;
  392. width: 300px;
  393. height: 200px;
  394. border: 1px solid rgba(25,186,139,0.17);
  395. padding: 0 0.1875rem 0.5rem;
  396. margin-bottom: 0.1875rem;
  397. background: url(@/assets/line.png) rgba(255,255,255,0.04);
  398. &::before{
  399. .horn;
  400. top: 0;
  401. left: 0;
  402. border-left: 2px solid #02a6b5;
  403. border-top: 2px solid #02a6b5;
  404. }
  405. &::after{
  406. .horn;
  407. top: 0;
  408. right: 0;
  409. border-right: 2px solid #02a6b5;
  410. border-top: 2px solid #02a6b5;
  411. }
  412. .panel-bottom{
  413. &::before{
  414. .horn;
  415. bottom: 0;
  416. left: 0;
  417. border-left: 2px solid #02a6b5;
  418. border-bottom: 2px solid #02a6b5;
  419. }
  420. &::after{
  421. .horn;
  422. bottom: 0;
  423. right: 0;
  424. border-right: 2px solid #02a6b5;
  425. border-bottom: 2px solid #02a6b5;
  426. }
  427. }
  428. .panel-title{
  429. position: absolute;
  430. left: 110px;
  431. top: 10px;
  432. font-size: 20px;
  433. font-weight: bold;
  434. color: #0EFCFF;
  435. }
  436. .panelSwitchTitle{
  437. color: #ffffff;
  438. font-size: 16px;
  439. position: absolute;
  440. bottom: 30px;
  441. font-weight: bold;
  442. left:25px;
  443. }
  444. .panelSwitch{
  445. position: absolute;
  446. right: 25px;
  447. bottom: 20px;
  448. }
  449. .panel-slider{
  450. width: 250px;
  451. position: absolute;
  452. top: 60px;
  453. left: 25px;
  454. }
  455. .panel-slider2{
  456. width: 250px;
  457. position: absolute;
  458. top: 120px;
  459. left: 25px;
  460. }
  461. .brightness{
  462. width: 250px;
  463. display: flex;
  464. justify-content: space-between;
  465. color: #909399;
  466. font-size: 14px;
  467. }
  468. }
  469. }
  470. }
  471. /* ::v-deep是vue3提供的深度选择器,.el-form-item__label是element-plus官方定义的类 */
  472. /deep/ .el-form-item__label{
  473. color: white;
  474. font-size: 16px;
  475. }
  476. .el-slider__runway{ // 滑块的进度条颜色
  477. background-color: #000000;
  478. }
  479. </style>