| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>实时数据大屏</title>
- <script src="vue.global.min.js"></script>
- <link rel="stylesheet" href="index.css">
- </head>
- <body>
- <div id="app">
- <div v-if="profiles.length > 0" class="cards-grid">
- <div v-for="(profile, index) in profiles" :key="index" class="card">
- <div class="avatar-container">
- <img
- :src="profile.webUrl"
- class="avatar-image"
- @error="handleAvatarError"
- >
- </div>
- <div class="card-section">
- <div class="card-label">学院</div>
- <div class="card-school-value">{{ profile.className}}</div>
- </div>
- <div class="card-section">
- <div class="card-label">姓名</div>
- <div class="card-name-value">{{ profile.studentName}}</div>
- </div>
- <div class="card-divider"></div>
- <div class="card-section">
- <div class="card-label">成绩</div>
- <div class="card-score">{{ profile.achievement ? profile.achievement : '0' }}</div>
- </div>
- <div class="card-section">
- <div class="card-label">违规</div>
- <div class="card-violation">{{ profile.violationTimes ? profile.violationTimes : '0' }}</div>
- </div>
- </div>
- </div>
- </div>
- <script>
- const { createApp } = Vue;
- createApp({
- data() {
- return {
- profiles: [],
- connected: false,
- wsUrl: (() => {
- // const hostname = "192.168.1.32" // 测试地址
- const hostname = window.location.hostname; // 正式地址从浏览器获取
- let url = `ws://${hostname}:8090/api/screen/websocket`;
- const urlParams = new URLSearchParams(window.location.search);
- if (urlParams.toString()) {
- url += `?${urlParams.toString()}`;
- console.log("连接地址:", url)
- }
- return url;
- })(),
- ws: null,
- maxProfiles: 5,
- noDataTimeout: null,
- noDataDelay: 180000 // 3分钟
- };
- },
- mounted() {
- this.connect();
- },
- methods: {
- connect() {
- try {
- if (this.ws) {
- this.ws.close();
- }
- this.ws = new WebSocket(this.wsUrl);
- this.ws.onopen = () => {
- this.connected = true;
- console.log('WebSocket已连接');
- this.resetNoDataTimer();
- };
- this.ws.onmessage = (event) => {
- try {
- const data = JSON.parse(event.data);
- if (Array.isArray(data)) {
- data.forEach(item => {
- this.addNewProfile(item);
- });
- } else if (data && typeof data === 'object') {
- this.addNewProfile(data);
- }
- this.resetNoDataTimer();
- } catch (e) {
- console.error('数据解析错误:', e);
- }
- };
- this.ws.onerror = (error) => {
- console.error('WebSocket错误:', error);
- };
- this.ws.onclose = () => {
- this.connected = false;
- console.log('WebSocket已断开');
- setTimeout(() => this.connect(), 1000);
- };
- } catch (e) {
- console.error('连接失败:', e.message);
- setTimeout(() => this.connect(), 3000);
- }
- },
- addNewProfile(newProfile) {
- this.profiles.unshift(newProfile);
- // 如果超过最大数量,移除最早的数据
- if (this.profiles.length > this.maxProfiles) {
- this.profiles.pop();
- }
- },
- resetNoDataTimer() {
- if (this.noDataTimeout) {
- clearTimeout(this.noDataTimeout);
- }
- this.noDataTimeout = setTimeout(() => {
- this.profiles = [];
- }, this.noDataDelay);
- },
- // getFullAvatarUrl(avatar) {
- // const hostname = window.location.hostname;
- // return `http://${hostname}${avatar}`
- // },
- handleAvatarError(event) {
- event.target.src = '/avatar.png'; // 图片加载失败时采用默认头像
- },
- beforeUnmount() {
- if (this.ws) {
- this.ws.close();
- }
- if (this.noDataTimeout) {
- clearTimeout(this.noDataTimeout);
- }
- }
- }
- }).mount('#app');
- </script>
- </body>
- </html>
|