liujintao 2 天之前
當前提交
28da487d92
共有 9 個文件被更改,包括 325 次插入0 次删除
  1. 5 0
      .idea/.gitignore
  2. 7 0
      .idea/MarsCodeWorkspaceAppSettings.xml
  3. 12 0
      .idea/largeScreen.iml
  4. 8 0
      .idea/modules.xml
  5. 6 0
      .idea/vcs.xml
  6. 二進制
      avatar.png
  7. 135 0
      index.css
  8. 152 0
      index.html
  9. 0 0
      vue.global.min.js

+ 5 - 0
.idea/.gitignore

@@ -0,0 +1,5 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/

+ 7 - 0
.idea/MarsCodeWorkspaceAppSettings.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="com.codeverse.userSettings.MarscodeWorkspaceAppSettingsState">
+    <option name="chatAppRouterInfo" value="builder/691178fbde3cff8e683e8e88" />
+    <option name="progress" value="1.0" />
+  </component>
+</project>

+ 12 - 0
.idea/largeScreen.iml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="WEB_MODULE" version="4">
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$">
+      <excludeFolder url="file://$MODULE_DIR$/.tmp" />
+      <excludeFolder url="file://$MODULE_DIR$/temp" />
+      <excludeFolder url="file://$MODULE_DIR$/tmp" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>

+ 8 - 0
.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/largeScreen.iml" filepath="$PROJECT_DIR$/.idea/largeScreen.iml" />
+    </modules>
+  </component>
+</project>

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$" vcs="Git" />
+  </component>
+</project>

二進制
avatar.png


+ 135 - 0
index.css

@@ -0,0 +1,135 @@
+* {
+    margin: 0;
+    padding: 0;
+    box-sizing: border-box;
+}
+
+body {
+    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
+    background: #e6eeff;
+    width: 240px;
+    height: 160px;
+    padding: 3px;
+    overflow: hidden;
+}
+
+#app {
+    width: 100%;
+    height: 100%;
+}
+
+.cards-grid {
+    display: flex;
+    flex-direction: row;
+    gap: 2px;
+    height: 100%;
+    overflow: hidden;
+}
+
+.card {
+    background: white;
+    box-shadow: 0 1px 3px rgba(59, 130, 246, 0.15);
+    padding: 5px;
+    text-align: center;
+    border-top: 2px solid #3b82f6;
+    border-radius: 1px;
+    width: calc((100% / 5) - 1px);
+    flex-shrink: 0;
+    display: flex;
+    flex-direction: column;
+    gap: 4px;
+}
+
+.avatar-image {
+    width: 25px;
+    height: 25px;
+    margin: 0 auto;
+    border-radius: 50%;
+    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+    background: #f0f9ff;
+    object-fit: cover;
+    transition: all 0.3s ease;
+    display: block;
+
+    /* 关键:移除所有边框 */
+    border: none !important;
+    outline: none;
+    box-sizing: border-box;
+
+    /* 防止图片周围出现间隙 */
+    vertical-align: middle;
+    flex-shrink: 0;
+
+    /* 改善图片加载失败时的显示 */
+    background-clip: content-box;
+    -webkit-appearance: none;
+    appearance: none;
+}
+
+.card-section {
+    display: flex;
+    flex-direction: column;
+    gap: 4px;
+}
+
+.card-label {
+    font-size: 6px;
+    color: #9ca3af;
+    text-transform: uppercase;
+    letter-spacing: 0.3px;
+    font-weight: 500;
+    line-height: 1;
+}
+
+.card-school-value {
+    font-size: 7px;
+    color: #1f2937;
+    font-weight: 700;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    line-height: 1.2;
+}
+
+.card-name-value {
+    font-size: 8px;
+    color: #1f2937;
+    font-weight: 700;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    line-height: 1.2;
+}
+
+.card-divider {
+    height: 1px;
+    background-color: #e5e7eb;
+    margin: 1px 0;
+}
+
+.card-score {
+    font-size: 12px;
+    color: #2563eb;
+    font-weight: bold;
+    line-height: 1.2;
+}
+
+.card-violation {
+    font-size: 12px;
+    color: #6366f1;
+    font-weight: bold;
+    line-height: 1.2;
+}
+
+::-webkit-scrollbar {
+    height: 2px;
+}
+
+::-webkit-scrollbar-track {
+    background: transparent;
+}
+
+::-webkit-scrollbar-thumb {
+    background: #bfdbfe;
+    border-radius: 1px;
+}

+ 152 - 0
index.html

@@ -0,0 +1,152 @@
+<!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>

File diff suppressed because it is too large
+ 0 - 0
vue.global.min.js


Some files were not shown because too many files changed in this diff