余思翰 2 settimane fa
parent
commit
725e83af9b

+ 7
- 1
cmc-temperature-ui/.env.development Vedi File

1
+###
2
+ # @Author: ysh
3
+ # @Date: 2025-08-19 16:55:09
4
+ # @LastEditors: 
5
+ # @LastEditTime: 2025-08-20 10:30:48
6
+### 
1
 # 开发环境配置
7
 # 开发环境配置
2
 VITE_APP_ENV = 'development'
8
 VITE_APP_ENV = 'development'
3
 
9
 
4
 
10
 
5
 # 页面标题
11
 # 页面标题
6
-VITE_APP_TITLE = 若依管理系统
12
+VITE_APP_TITLE = 国能青海黄河玛尔挡水电站水温监测系统
7
 
13
 
8
 # 开发环境前缀
14
 # 开发环境前缀
9
 VITE_APP_BASE_API = '/dev-api'
15
 VITE_APP_BASE_API = '/dev-api'

+ 1
- 1
cmc-temperature-ui/.env.production Vedi File

3
 
3
 
4
 
4
 
5
 # 页面标题
5
 # 页面标题
6
-VITE_APP_TITLE = 若依管理系统
6
+VITE_APP_TITLE = 国能青海黄河玛尔挡水电站水温监测系统
7
 
7
 
8
 # 若依管理系统/生产环境
8
 # 若依管理系统/生产环境
9
 VITE_APP_BASE_API = '/prod-api'
9
 VITE_APP_BASE_API = '/prod-api'

+ 3
- 3
cmc-temperature-ui/index.html Vedi File

6
   <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
6
   <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
7
   <meta name="renderer" content="webkit">
7
   <meta name="renderer" content="webkit">
8
   <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
8
   <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
9
-  <link rel="icon" href="/favicon.ico">
10
-  <title>若依管理系统</title>
9
+  <link rel="icon" href="/src/assets/images/logo/logo.png">
10
+  <title>国能青海黄河玛尔挡水电站水温监测系统</title>
11
   <!-- [if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif] -->
11
   <!-- [if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif] -->
12
   <style>
12
   <style>
13
     html,
13
     html,
212
   <script type="module" src="/src/main.js"></script>
212
   <script type="module" src="/src/main.js"></script>
213
 </body>
213
 </body>
214
 
214
 
215
-</html>
215
+</html>

+ 7
- 0
cmc-temperature-ui/src/api/monitoring/channel.js Vedi File

14
     params: query
14
     params: query
15
   })
15
   })
16
 }
16
 }
17
+export function channelInfoList(){
18
+  return request({
19
+    url: '/monitoring/channel/channelInfoList',
20
+    method: 'get',
21
+  })
22
+
23
+}

+ 9
- 2
cmc-temperature-ui/src/api/monitoring/temperature.js Vedi File

1
 /*
1
 /*
2
  * @Author: wrh
2
  * @Author: wrh
3
  * @Date: 2025-08-20 16:54:03
3
  * @Date: 2025-08-20 16:54:03
4
- * @LastEditors: wrh
5
- * @LastEditTime: 2025-08-20 19:13:01
4
+ * @LastEditors: Please set LastEditors
5
+ * @LastEditTime: 2025-08-21 11:17:03
6
  */
6
  */
7
 import request from '@/utils/request'
7
 import request from '@/utils/request'
8
 
8
 
14
     params: query
14
     params: query
15
   })
15
   })
16
 }
16
 }
17
+export function listByInfo(query) {
18
+  return request({
19
+    url: '/monitoring/temperature/listByInfo',
20
+    method: 'get',
21
+    params: query
22
+  })
23
+}

BIN
cmc-temperature-ui/src/assets/images/loginBg.jpeg Vedi File


BIN
cmc-temperature-ui/src/assets/images/logo/logo.png Vedi File


+ 19
- 10
cmc-temperature-ui/src/router/index.js Vedi File

57
     component: () => import('@/views/error/401'),
57
     component: () => import('@/views/error/401'),
58
     hidden: true
58
     hidden: true
59
   },
59
   },
60
+  // {
61
+  //   path: '',
62
+  //   component: Layout,
63
+  //   redirect: '/index',
64
+  //   children: [
65
+  //     {
66
+  //       path: '/index',
67
+  //       component: () => import('@/views/index'),
68
+  //       name: 'Index',
69
+  //       meta: { title: '首页', icon: 'dashboard', affix: true }
70
+  //     }
71
+  //   ]
72
+  // },
73
+
60
   {
74
   {
61
-    path: '',
62
-    component: Layout,
75
+    path: '/',
63
     redirect: '/index',
76
     redirect: '/index',
64
-    children: [
65
-      {
66
-        path: '/index',
67
-        component: () => import('@/views/index'),
68
-        name: 'Index',
69
-        meta: { title: '首页', icon: 'dashboard', affix: true }
70
-      }
71
-    ]
77
+  },
78
+  {
79
+    path: '/index',
80
+    component: () => import('@/views/temperature/index'),
72
   },
81
   },
73
   {
82
   {
74
     path: '/user',
83
     path: '/user',

+ 1
- 1
cmc-temperature-ui/src/utils/request.js Vedi File

17
   // axios中请求配置有baseURL选项,表示请求URL公共部分
17
   // axios中请求配置有baseURL选项,表示请求URL公共部分
18
   baseURL: import.meta.env.VITE_APP_BASE_API,
18
   baseURL: import.meta.env.VITE_APP_BASE_API,
19
   // 超时
19
   // 超时
20
-  timeout: 10000
20
+  timeout: 100000
21
 })
21
 })
22
 
22
 
23
 // request拦截器
23
 // request拦截器

+ 212
- 72
cmc-temperature-ui/src/views/login.vue Vedi File

1
 <template>
1
 <template>
2
   <div class="login">
2
   <div class="login">
3
-    <el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
4
-      <h3 class="title">{{ title }}</h3>
5
-      <el-form-item prop="username">
6
-        <el-input v-model="loginForm.username" type="text" size="large" auto-complete="off" placeholder="账号">
7
-          <template #prefix><svg-icon icon-class="user" class="el-input__icon input-icon" /></template>
8
-        </el-input>
9
-      </el-form-item>
10
-      <el-form-item prop="password">
11
-        <el-input v-model="loginForm.password" type="password" size="large" auto-complete="off" placeholder="密码" @keyup.enter="handleLogin">
12
-          <template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
13
-        </el-input>
14
-      </el-form-item>
15
-      <el-form-item v-if="captchaEnabled" prop="code">
16
-        <el-input v-model="loginForm.code" size="large" auto-complete="off" placeholder="验证码" style="width: 63%" @keyup.enter="handleLogin">
17
-          <template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
18
-        </el-input>
19
-        <div class="login-code">
20
-          <img :src="codeUrl" class="login-code-img" @click="getCode">
21
-        </div>
22
-      </el-form-item>
23
-      <el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox>
24
-      <el-form-item style="width:100%;">
25
-        <el-button type="primary" style="width:100%;" :loading="loading" size="large" @click.prevent="handleLogin">
26
-          <span v-if="!loading">登 录</span>
27
-          <span v-else>登 录 中...</span>
28
-        </el-button>
29
-        <div v-if="register" class="register-link">
30
-          <router-link class="link-type" :to="'/register'">立即注册</router-link>
31
-        </div>
32
-      </el-form-item>
33
-    </el-form>
3
+    <!-- 顶部标题区域 -->
4
+    <div class="login-header">
5
+      <h1 class="main-title">{{ title }}</h1>
6
+      <p class="sub-title">Water Temperature Monitoring System</p>
7
+    </div>
8
+    
9
+    <!-- 登录表单区域 -->
10
+    <div class="login-container">
11
+      <el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
12
+        <h3 class="form-title">用户登录</h3>
13
+        <el-form-item prop="username">
14
+          <el-input v-model="loginForm.username" type="text" size="large" auto-complete="off" placeholder="请输入用户名">
15
+            <template #prefix><svg-icon icon-class="user" class="el-input__icon input-icon" /></template>
16
+          </el-input>
17
+        </el-form-item>
18
+        <el-form-item prop="password">
19
+          <el-input v-model="loginForm.password" type="password" size="large" auto-complete="off" placeholder="请输入密码" @keyup.enter="handleLogin">
20
+            <template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
21
+          </el-input>
22
+        </el-form-item>
23
+        <el-form-item v-if="captchaEnabled" prop="code">
24
+          <el-input v-model="loginForm.code" size="large" auto-complete="off" placeholder="验证码" style="width: 63%" @keyup.enter="handleLogin">
25
+            <template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
26
+          </el-input>
27
+          <div class="login-code">
28
+            <img :src="codeUrl" class="login-code-img" @click="getCode">
29
+          </div>
30
+        </el-form-item>
31
+        <el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox>
32
+        <el-form-item style="width:100%;">
33
+          <el-button type="primary" style="width:100%;" :loading="loading" size="large" @click.prevent="handleLogin">
34
+            <span v-if="!loading">进入监测系统</span>
35
+            <span v-else>登录中...</span>
36
+          </el-button>
37
+          <!-- <div v-if="register" class="register-link">
38
+            <router-link class="link-type" :to="'/register'">立即注册</router-link>
39
+          </div> -->
40
+        </el-form-item>
41
+      </el-form>
42
+    </div>
34
 
43
 
35
-    <!-- 底部 -->
44
+    <!-- 底部信息 -->
36
     <div class="el-login-footer">
45
     <div class="el-login-footer">
37
-      <span>Copyright © 2023-2025 ruoyi All Rights Reserved.</span>
46
+      <span>Copyright © 2025 四川中水成勘院测绘工程有限责任公司 All Rights Reserved.</span>
38
     </div>
47
     </div>
39
   </div>
48
   </div>
40
 </template>
49
 </template>
95
       }
104
       }
96
       // 调用action的登录方法
105
       // 调用action的登录方法
97
       userStore.login(loginForm.value).then(() => {
106
       userStore.login(loginForm.value).then(() => {
98
-        router.push({ path: redirect.value || '/' })
107
+        router.push({ path: redirect.value || '/index' })
99
       }).catch(() => {
108
       }).catch(() => {
100
         loading.value = false
109
         loading.value = false
101
         // 重新获取验证码
110
         // 重新获取验证码
136
 <style lang='scss' scoped>
145
 <style lang='scss' scoped>
137
 .login {
146
 .login {
138
   display: flex;
147
   display: flex;
148
+  flex-direction: column;
139
   justify-content: center;
149
   justify-content: center;
140
   align-items: center;
150
   align-items: center;
141
   height: 100%;
151
   height: 100%;
142
-  background-image: url('@/assets/images/login-background.jpg');
152
+  background-image: url('@/assets/images/loginBg.jpeg');
143
   background-size: cover;
153
   background-size: cover;
154
+  background-position: center;
155
+  position: relative;
144
 }
156
 }
145
-.title {
146
-  margin: 0px auto 30px auto;
157
+
158
+.login-header {
147
   text-align: center;
159
   text-align: center;
148
-  color: #707070;
160
+  margin-bottom: 40px;
161
+  position: relative;
162
+  z-index: 2;
163
+  
164
+  .main-title {
165
+    font-size: 42px;
166
+    font-weight: 700;
167
+    color: #ffffff;
168
+    margin-bottom: 10px;
169
+    text-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
170
+    letter-spacing: 2px;
171
+  }
172
+  
173
+  .sub-title {
174
+    font-size: 18px;
175
+    color: rgba(255, 255, 255, 0.9);
176
+    font-weight: 300;
177
+    letter-spacing: 1px;
178
+    text-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
179
+  }
149
 }
180
 }
150
 
181
 
151
-.login-form {
152
-  border-radius: 6px;
153
-  background: #ffffff;
154
-  width: 400px;
155
-  padding: 25px 25px 5px 25px;
156
-  .el-input {
157
-    height: 40px;
158
-    input {
159
-      height: 40px;
182
+.login-container {
183
+  background: rgba(255, 255, 255, 0.5);
184
+  backdrop-filter: blur(20px);
185
+  -webkit-backdrop-filter: blur(20px);
186
+  border-radius: 16px;
187
+  padding: 40px;
188
+  box-shadow: 0 15px 35px rgba(0, 0, 0, 0.2);
189
+  width: 450px;
190
+  position: relative;
191
+  z-index: 2;
192
+  border: 1px solid rgba(255, 255, 255, 0.3);
193
+  transition: all 0.3s ease;
194
+  
195
+  .login-form {
196
+    .form-title {
197
+      text-align: center;
198
+      margin-bottom: 30px;
199
+      font-size: 26px;
200
+      font-weight: 600;
201
+      color: #2c3e50;
202
+      position: relative;
203
+      
204
+      &::after {
205
+        content: '';
206
+        position: absolute;
207
+        bottom: -10px;
208
+        left: 50%;
209
+        transform: translateX(-50%);
210
+        width: 50px;
211
+        height: 3px;
212
+        background: linear-gradient(135deg, #409eff, #36a3f7);
213
+        border-radius: 2px;
214
+      }
215
+    }
216
+    
217
+    .el-input {
218
+      height: 48px;
219
+      margin-bottom: 8px;
220
+      
221
+      input {
222
+        height: 48px;
223
+        border-radius: 8px;
224
+        border: 2px solid #e8eaec;
225
+        transition: all 0.3s ease;
226
+        font-size: 14px;
227
+        
228
+        &:focus {
229
+          border-color: #409eff;
230
+          box-shadow: 0 0 0 3px rgba(64, 158, 255, 0.1);
231
+        }
232
+        
233
+        &::placeholder {
234
+          color: #a0a0a0;
235
+        }
236
+      }
237
+    }
238
+    
239
+    .input-icon {
240
+      height: 46px;
241
+      width: 18px;
242
+      margin-left: 0px;
243
+      color: #409eff;
244
+    }
245
+    
246
+    .el-button {
247
+      border-radius: 8px;
248
+      height: 48px;
249
+      font-size: 16px;
250
+      font-weight: 600;
251
+      background: linear-gradient(135deg, #409eff 0%, #36a3f7 100%);
252
+      border: none;
253
+      transition: all 0.3s ease;
254
+      
255
+      &:hover {
256
+        transform: translateY(-1px);
257
+        box-shadow: 0 6px 20px rgba(64, 158, 255, 0.3);
258
+      }
259
+      
260
+      &:active {
261
+        transform: translateY(0);
262
+      }
263
+    }
264
+    
265
+    .el-checkbox {
266
+      .el-checkbox__label {
267
+        color: #606266;
268
+        font-size: 14px;
269
+      }
270
+      
271
+      .el-checkbox__input.is-checked .el-checkbox__inner {
272
+        background-color: #409eff;
273
+        border-color: #409eff;
274
+      }
160
     }
275
     }
161
   }
276
   }
162
-  .input-icon {
163
-    height: 39px;
164
-    width: 14px;
165
-    margin-left: 0px;
277
+  
278
+  .login-tip {
279
+    font-size: 13px;
280
+    text-align: center;
281
+    color: #bfbfbf;
166
   }
282
   }
167
-}
168
-.login-tip {
169
-  font-size: 13px;
170
-  text-align: center;
171
-  color: #bfbfbf;
172
-}
173
-.login-code {
174
-  width: 33%;
175
-  height: 40px;
176
-  float: right;
177
-  img {
178
-    cursor: pointer;
179
-    vertical-align: middle;
283
+  
284
+  .login-code {
285
+    width: 33%;
286
+    height: 48px;
287
+    float: right;
288
+    
289
+    img {
290
+      cursor: pointer;
291
+      vertical-align: middle;
292
+      border-radius: 6px;
293
+      border: 2px solid #e8eaec;
294
+      transition: all 0.3s ease;
295
+      
296
+      &:hover {
297
+        border-color: #409eff;
298
+        transform: scale(1.02);
299
+      }
300
+    }
301
+  }
302
+  
303
+  .register-link {
304
+    margin-left: auto;
305
+    margin-bottom: -10px;
306
+    margin-top: 20px;
307
+    text-align: center;
308
+    
309
+    .link-type {
310
+      color: #409eff;
311
+      text-decoration: none;
312
+      font-size: 14px;
313
+      font-weight: 500;
314
+      transition: all 0.3s ease;
315
+      
316
+      &:hover {
317
+        color: #36a3f7;
318
+        text-decoration: underline;
319
+      }
320
+    }
180
   }
321
   }
181
 }
322
 }
182
-.register-link {
183
-  margin-left: auto;
184
-  margin-bottom: -10px;
185
-  margin-top: 10px;
186
-}
323
+
187
 .el-login-footer {
324
 .el-login-footer {
188
   height: 40px;
325
   height: 40px;
189
   line-height: 40px;
326
   line-height: 40px;
191
   bottom: 0;
328
   bottom: 0;
192
   width: 100%;
329
   width: 100%;
193
   text-align: center;
330
   text-align: center;
194
-  color: #fff;
331
+  color: rgba(255, 255, 255, 0.9);
195
   font-family: Arial;
332
   font-family: Arial;
196
   font-size: 12px;
333
   font-size: 12px;
197
   letter-spacing: 1px;
334
   letter-spacing: 1px;
335
+  z-index: 2;
336
+  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
198
 }
337
 }
338
+
199
 .login-code-img {
339
 .login-code-img {
200
-  height: 40px;
201
-  padding-left: 12px;
340
+  height: 48px;
341
+  margin-left: 12px;
202
 }
342
 }
203
 </style>
343
 </style>

+ 17282
- 0
cmc-temperature-ui/src/views/temperature/data.js
File diff soppresso perché troppo grande
Vedi File


+ 201
- 0
cmc-temperature-ui/src/views/temperature/index.vue Vedi File

1
+<!--
2
+ * @Author: ysh
3
+ * @Date: 2025-08-20 10:14:22
4
+ * @LastEditors: Please set LastEditors
5
+ * @LastEditTime: 2025-08-21 09:52:13
6
+-->
7
+<template>
8
+  <div class="temperature-page">
9
+    <!-- 顶部导航栏 -->
10
+    <div class="top-header">
11
+      <div class="header-left">
12
+        <h2 class="page-title">{{ title }}</h2>
13
+      </div>
14
+      <div class="header-right">
15
+        <el-dropdown @command="handleCommand" trigger="click">
16
+          <div class="user-info">
17
+            <span class="username">{{ userInfo.username }}</span>
18
+            <el-avatar :size="32" :src="userInfo.avatar" class="user-avatar">
19
+              <svg-icon icon-class="user" />
20
+            </el-avatar>
21
+            <span class="dropdown-text">▼</span>
22
+          </div>
23
+          <template #dropdown>
24
+            <el-dropdown-menu>
25
+              <el-dropdown-item divided command="logout">
26
+                <svg-icon icon-class="logout" />
27
+                退出登录
28
+              </el-dropdown-item>
29
+            </el-dropdown-menu>
30
+          </template>
31
+        </el-dropdown>
32
+      </div>
33
+    </div>
34
+
35
+    <!-- 主要内容区域 -->
36
+    <div class="main-content">
37
+      <tem-monitor></tem-monitor>
38
+    </div>
39
+  </div>
40
+</template>
41
+
42
+<script setup>
43
+import { ref, onMounted } from 'vue'
44
+import { useRouter } from 'vue-router'
45
+import useUserStore from '@/store/modules/user'
46
+import { title } from '@/store/modules/settings'
47
+import temMonitor from './temMonitor.vue'
48
+
49
+const router = useRouter()
50
+const userStore = useUserStore()
51
+const { proxy } = getCurrentInstance()
52
+
53
+// 用户信息
54
+const userInfo = ref({
55
+  username: 'admin',
56
+  avatar: ''
57
+})
58
+
59
+// 获取用户信息
60
+const getUserInfo = () => {
61
+  const user = userStore.user
62
+  if (user) {
63
+    userInfo.value.username = user.userName || 'admin'
64
+    userInfo.value.avatar = user.avatar || ''
65
+  }
66
+}
67
+
68
+// 处理下拉菜单命令
69
+const handleCommand = (command) => {
70
+  switch (command) {
71
+    case 'profile':
72
+      // 跳转到个人中心
73
+      router.push('/system/user/profile')
74
+      break
75
+    case 'settings':
76
+      // 跳转到系统设置
77
+      router.push('/system/config')
78
+      break
79
+    case 'logout':
80
+      handleLogout()
81
+      break
82
+  }
83
+}
84
+
85
+// 退出登录
86
+const handleLogout = () => {
87
+  proxy.$modal.confirm('确定注销并退出系统吗?', '提示', {
88
+    confirmButtonText: '确定',
89
+    cancelButtonText: '取消',
90
+    type: 'warning'
91
+  }).then(() => {
92
+    userStore.logOut().then(() => {
93
+      location.href = import.meta.env.VITE_APP_CONTEXT_PATH + 'index'
94
+    })
95
+  }).catch(() => { })
96
+}
97
+
98
+onMounted(() => {
99
+  getUserInfo()
100
+})
101
+</script>
102
+
103
+<style lang="scss" scoped>
104
+.temperature-page {
105
+  height: 100vh;
106
+  display: flex;
107
+  flex-direction: column;
108
+  background: #f5f7fa;
109
+}
110
+
111
+.top-header {
112
+  height: 60px;
113
+  background: #ffffff;
114
+  border-bottom: 1px solid #e4e7ed;
115
+  display: flex;
116
+  align-items: center;
117
+  justify-content: space-between;
118
+  padding: 0 20px;
119
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
120
+  z-index: 1000;
121
+
122
+  .header-left {
123
+    .page-title {
124
+      font-size: 20px;
125
+      font-weight: 600;
126
+      color: #303133;
127
+      margin: 0;
128
+    }
129
+  }
130
+
131
+  .header-right {
132
+    .user-info {
133
+      display: flex;
134
+      align-items: center;
135
+      gap: 12px;
136
+      cursor: pointer;
137
+      padding: 8px 12px;
138
+      border-radius: 6px;
139
+      transition: all 0.3s ease;
140
+
141
+      &:hover {
142
+        background: #f5f7fa;
143
+      }
144
+
145
+      .username {
146
+        font-size: 14px;
147
+        color: #606266;
148
+        font-weight: 500;
149
+      }
150
+
151
+      .user-avatar {
152
+        border: 2px solid #e4e7ed;
153
+        transition: all 0.3s ease;
154
+
155
+        &:hover {
156
+          border-color: #409eff;
157
+        }
158
+      }
159
+
160
+      .dropdown-text {
161
+        font-size: 12px;
162
+        font-weight: bold;
163
+        color: #909399;
164
+        transition: all 0.3s ease;
165
+      }
166
+
167
+      &:hover .dropdown-text {
168
+        color: #409eff;
169
+      }
170
+    }
171
+  }
172
+}
173
+
174
+.main-content {
175
+  flex: 1;
176
+  padding: 20px;
177
+  overflow-y: auto;
178
+}
179
+
180
+// 下拉菜单样式
181
+:deep(.el-dropdown-menu) {
182
+  .el-dropdown-item {
183
+    display: flex;
184
+    align-items: center;
185
+    gap: 8px;
186
+    padding: 10px 16px;
187
+
188
+    .svg-icon {
189
+      font-size: 16px;
190
+    }
191
+  }
192
+}
193
+
194
+// 头像样式
195
+:deep(.el-avatar) {
196
+  .svg-icon {
197
+    font-size: 18px;
198
+    color: #909399;
199
+  }
200
+}
201
+</style>

+ 425
- 0
cmc-temperature-ui/src/views/temperature/temMonitor.vue Vedi File

1
+<template>
2
+  <div class="temperature-monitor">
3
+    <!-- 搜索区域 -->
4
+    <div class="search-section">
5
+      <el-card class="search-card">
6
+        <template #header>
7
+          <div class="card-header">
8
+            <span>温度监控查询</span>
9
+          </div>
10
+        </template>
11
+
12
+        <el-form :model="searchForm" :inline="true" class="search-form">
13
+          <!-- 时间范围选择 -->
14
+          <el-form-item label="时间范围">
15
+            <el-date-picker v-model="searchForm.timeRange" type="datetimerange" range-separator="至"
16
+              start-placeholder="开始时间" end-placeholder="结束时间" format="YYYY-MM-DD HH:mm:ss"
17
+              value-format="YYYY-MM-DD HH:mm:ss" :shortcuts="timeShortcuts" :default-time="defaultTime"
18
+              class="time-picker" />
19
+          </el-form-item>
20
+
21
+          <!-- 设备选择 -->
22
+          <el-form-item label="查询条件">
23
+            <el-select v-model="searchForm.Info" placeholder="请选择查询条件" clearable filterable class="device-select"
24
+              @change="handleChange">
25
+              <el-option v-for="device in deviceOptions" :key="device.value" :label="device.label"
26
+                :value="device.value" />
27
+            </el-select>
28
+          </el-form-item>
29
+
30
+          <!-- 操作按钮 -->
31
+          <el-form-item>
32
+            <el-button type="primary" @click="handleSearch" :loading="loading">
33
+              <el-icon>
34
+                <Search />
35
+              </el-icon>
36
+              查询
37
+            </el-button>
38
+            <el-button @click="handleReset">
39
+              <el-icon>
40
+                <Refresh />
41
+              </el-icon>
42
+              重置
43
+            </el-button>
44
+            <el-button type="success" @click="handleExport">
45
+              <el-icon>
46
+                <Download />
47
+              </el-icon>
48
+              导出
49
+            </el-button>
50
+          </el-form-item>
51
+        </el-form>
52
+      </el-card>
53
+    </div>
54
+
55
+    <!-- 结果展示区域 -->
56
+    <div class="result-section">
57
+      <el-card>
58
+        <template #header>
59
+          <div class="card-header">
60
+            <span>查询结果</span>
61
+            <span class="result-count">共 {{ totalCount }} 条记录</span>
62
+          </div>
63
+        </template>
64
+
65
+        <!-- 这里可以添加表格或其他结果展示组件 -->
66
+        <div class="result-content">
67
+          <el-empty v-if="!hasData" description="暂无数据" />
68
+          <div v-else>
69
+            <!-- 数据展示区域 -->
70
+            <div class="table-container">
71
+              <el-table :data="tableData" border stripe :style="{ minWidth: tableMinWidth + 'px' }" :max-height="600"
72
+                v-loading="loading" :scrollbar-always-on="true">
73
+                <!-- 序号列 -->
74
+                <el-table-column type="index" label="序号" width="60" align="center" fixed="left" />
75
+
76
+                <!-- 时间列 -->
77
+                <el-table-column prop="datetime" label="时间" width="160" align="center" fixed="left" />
78
+
79
+                <!-- 动态生成传感器列 -->
80
+                <el-table-column v-for="sensor in sensorColumns" :key="sensor.sensorName" :prop="sensor.sensorName"
81
+                  :label="sensor.sensorName" width="100" align="center" show-overflow-tooltip>
82
+                </el-table-column>
83
+              </el-table>
84
+            </div>
85
+
86
+            <!-- 分页 -->
87
+            <div class="pagination-container" v-if="hasData">
88
+              <el-pagination v-model:current-page="queryParams.pageNum" v-model:page-size="queryParams.pageSize"
89
+                :page-sizes="[10, 20, 50, 100]" :total="totalCount" layout="total, sizes, prev, pager, next, jumper"
90
+                @size-change="handleSizeChange" @current-change="handleCurrentChange" />
91
+            </div>
92
+          </div>
93
+        </div>
94
+      </el-card>
95
+    </div>
96
+  </div>
97
+</template>
98
+
99
+<script setup>
100
+import { ref, reactive, computed, onMounted } from 'vue'
101
+import { Search, Refresh, Download } from '@element-plus/icons-vue'
102
+import { ElMessage } from 'element-plus'
103
+import { listTemperature, listByInfo } from '@/api/monitoring/temperature'
104
+import { listChannel, channelInfoList } from '@/api/monitoring/channel'
105
+
106
+// 响应式数据
107
+const loading = ref(false)
108
+const totalCount = ref(0)
109
+
110
+// 搜索表单数据
111
+const searchForm = reactive({
112
+  timeRange: [],
113
+  queryType: '',
114
+  deviceId: '',
115
+  minTemp: null,
116
+  maxTemp: null
117
+})
118
+const queryParams = ref({
119
+  pageNum: 1,
120
+  pageSize: 5760
121
+})
122
+// 时间快捷选项
123
+const timeShortcuts = [
124
+  {
125
+    text: '最近1小时',
126
+    value: () => {
127
+      const end = new Date()
128
+      const start = new Date()
129
+      start.setTime(start.getTime() - 3600 * 1000)
130
+      return [start, end]
131
+    }
132
+  },
133
+  {
134
+    text: '最近24小时',
135
+    value: () => {
136
+      const end = new Date()
137
+      const start = new Date()
138
+      start.setTime(start.getTime() - 3600 * 1000 * 24)
139
+      return [start, end]
140
+    }
141
+  },
142
+  {
143
+    text: '最近7天',
144
+    value: () => {
145
+      const end = new Date()
146
+      const start = new Date()
147
+      start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
148
+      return [start, end]
149
+    }
150
+  },
151
+  {
152
+    text: '最近30天',
153
+    value: () => {
154
+      const end = new Date()
155
+      const start = new Date()
156
+      start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
157
+      return [start, end]
158
+    }
159
+  }
160
+]
161
+
162
+// 默认时间
163
+const defaultTime = [
164
+  new Date(2000, 1, 1, 0, 0, 0),
165
+  new Date(2000, 1, 1, 23, 59, 59)
166
+]
167
+
168
+// 设备选项
169
+const deviceOptions = ref([])
170
+
171
+// 计算属性
172
+const hasData = computed(() => {
173
+  return totalCount.value > 0
174
+})
175
+
176
+// 计算表格最小宽度
177
+const tableMinWidth = computed(() => {
178
+  const baseWidth = 220 // 序号列(60) + 时间列(160)
179
+  const sensorWidth = sensorColumns.value.length * 100 // 每个传感器列100px
180
+  return Math.max(800, baseWidth + sensorWidth) // 最小800px
181
+})
182
+
183
+// 表格数据相关
184
+const tableData = ref([])
185
+const sensorColumns = ref([])
186
+const tableList = ref([])
187
+
188
+// 处理表格数据,按sensorNo分组
189
+const processTableData = () => {
190
+  if (!tableList.value || tableList.value.length === 0) {
191
+    tableData.value = []
192
+    sensorColumns.value = []
193
+    return
194
+  }
195
+
196
+  // 获取所有唯一的传感器编号
197
+  const sensorNos = [...new Set(tableList.value.map(item => item.channel.info))]
198
+
199
+  // 生成传感器列配置
200
+  sensorColumns.value = sensorNos.map(sensorNo => ({
201
+    sensorNo: sensorNo,
202
+    sensorName: `${sensorNo}`
203
+  }))
204
+
205
+  // 按时间分组数据
206
+  const timeGroups = {}
207
+  tableList.value.forEach(item => {
208
+    if (!timeGroups[item.datetime]) {
209
+      timeGroups[item.datetime] = {
210
+        datetime: item.datetime,
211
+        ...sensorNos.reduce((acc, sensorNo) => {
212
+          acc[sensorNo] = null
213
+          return acc
214
+        }, {})
215
+      }
216
+    }
217
+    timeGroups[item.datetime][item.channel.info] = item.data
218
+  })
219
+
220
+  // 转换为数组并排序
221
+  tableData.value = Object.values(timeGroups).sort((a, b) =>
222
+    new Date(b.datetime) - new Date(a.datetime)
223
+  )
224
+}
225
+
226
+// 分页处理
227
+const handleSizeChange = (size) => {
228
+  queryParams.value.pageSize = size
229
+  queryParams.value.pageNum = 1
230
+  handleSearch()
231
+}
232
+
233
+const handleCurrentChange = (page) => {
234
+  queryParams.value.pageNum = page
235
+  handleSearch()
236
+}
237
+
238
+// 查询监控数据
239
+const handleSearch = async () => {
240
+  loading.value = true
241
+  try {
242
+    // 验证表单
243
+    if (!searchForm.timeRange || searchForm.timeRange.length === 0) {
244
+      ElMessage.warning('请选择时间范围')
245
+      return
246
+    }
247
+
248
+    const res = await listByInfo({
249
+      startTime: searchForm.timeRange[0],
250
+      endTime: searchForm.timeRange[1],
251
+      Info: searchForm.Info,
252
+      ...queryParams.value
253
+    })
254
+
255
+    console.log('查询结果:', res)
256
+    totalCount.value = res.total
257
+    tableList.value = res.list
258
+
259
+    // 处理数据,生成表格数据
260
+    processTableData()
261
+
262
+    ElMessage.success('查询成功')
263
+
264
+  } catch (error) {
265
+    ElMessage.error('查询失败')
266
+    console.error('查询错误:', error)
267
+  } finally {
268
+    loading.value = false
269
+  }
270
+}
271
+
272
+const handleReset = () => {
273
+  searchForm.timeRange = []
274
+  searchForm.Info = ''
275
+  totalCount.value = 0
276
+  tableList.value = []
277
+  tableData.value = []
278
+  sensorColumns.value = []
279
+  ElMessage.info('已重置搜索条件')
280
+}
281
+
282
+const handleExport = () => {
283
+  if (totalCount.value === 0) {
284
+    ElMessage.warning('暂无数据可导出')
285
+    return
286
+  }
287
+
288
+  ElMessage.success('导出功能开发中...')
289
+  // 这里可以添加导出逻辑
290
+}
291
+const sensorNoList = ref([]);
292
+const handleChange = (value) => {
293
+  listChannel({ Info: value }).then(res => {
294
+    console.log(res)
295
+    sensorNoList.value = res;
296
+  })
297
+}
298
+
299
+onMounted(() => {
300
+  channelInfoList().then(res => {
301
+    console.log('设备选项:', res)
302
+    deviceOptions.value = res.map(item => ({
303
+      label: item,
304
+      value: item
305
+    }))
306
+  })
307
+})
308
+</script>
309
+
310
+<style lang="scss" scoped>
311
+.temperature-monitor {
312
+  padding: 20px;
313
+  background-color: #f5f5f5;
314
+  min-height: 100vh;
315
+
316
+  .search-section {
317
+    margin-bottom: 20px;
318
+
319
+    .search-card {
320
+      .card-header {
321
+        font-weight: bold;
322
+        color: #303133;
323
+      }
324
+
325
+      .search-form {
326
+        .time-picker {
327
+          width: 400px;
328
+        }
329
+
330
+        .query-select,
331
+        .device-select {
332
+          width: 200px;
333
+        }
334
+
335
+        .temp-input {
336
+          width: 120px;
337
+        }
338
+
339
+        .temp-separator {
340
+          margin: 0 10px;
341
+          color: #909399;
342
+        }
343
+      }
344
+    }
345
+  }
346
+
347
+  .result-section {
348
+    .card-header {
349
+      display: flex;
350
+      justify-content: space-between;
351
+      align-items: center;
352
+      font-weight: bold;
353
+      color: #303133;
354
+
355
+      .result-count {
356
+        font-size: 14px;
357
+        color: #909399;
358
+        font-weight: normal;
359
+      }
360
+    }
361
+
362
+    .result-content {
363
+      min-height: 200px;
364
+
365
+      .pagination-container {
366
+        margin-top: 20px;
367
+        text-align: center;
368
+      }
369
+
370
+      // 表格容器样式
371
+      .table-container {
372
+        overflow-x: auto;
373
+        width: 100%;
374
+
375
+        // 自定义滚动条样式
376
+        &::-webkit-scrollbar {
377
+          height: 8px;
378
+        }
379
+
380
+        &::-webkit-scrollbar-track {
381
+          background: #f1f1f1;
382
+          border-radius: 4px;
383
+        }
384
+
385
+        &::-webkit-scrollbar-thumb {
386
+          background: #c1c1c1;
387
+          border-radius: 4px;
388
+
389
+          &:hover {
390
+            background: #a8a8a8;
391
+          }
392
+        }
393
+
394
+        .el-table {
395
+          .el-table__body-wrapper {
396
+            overflow-x: auto;
397
+          }
398
+        }
399
+      }
400
+    }
401
+  }
402
+}
403
+
404
+// 响应式设计
405
+@media (max-width: 768px) {
406
+  .temperature-monitor {
407
+    padding: 10px;
408
+
409
+    .search-section {
410
+      .search-card {
411
+        .search-form {
412
+          .time-picker {
413
+            width: 100%;
414
+          }
415
+
416
+          .query-select,
417
+          .device-select {
418
+            width: 100%;
419
+          }
420
+        }
421
+      }
422
+    }
423
+  }
424
+}
425
+</style>

Loading…
Annulla
Salva