Zimo 3 дней назад
Родитель
Сommit
f7e8818c2e
2 измененных файлов с 190 добавлено и 67 удалено
  1. 1 1
      .env.local
  2. 189 66
      src/views/oli-connection/monitoring-query/index.vue

+ 1 - 1
.env.local

@@ -4,7 +4,7 @@ NODE_ENV=development
 VITE_DEV=true
 
 # 请求路径  http://192.168.188.149:48080  https://iot.deepoil.cc
-VITE_BASE_URL='http://192.168.188.79:48080'
+VITE_BASE_URL='https://iot.deepoil.cc'
 
 # 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持 S3 服务
 VITE_UPLOAD_TYPE=server

+ 189 - 66
src/views/oli-connection/monitoring-query/index.vue

@@ -1,49 +1,19 @@
 <script lang="ts" setup>
 import { IotDeviceApi } from '@/api/pms/device'
+import { useTableComponents } from '@/components/ZmTable/useTableComponents'
 import { useUserStore } from '@/store/modules/user'
 import { rangeShortcuts } from '@/utils/formatTime'
 import { useDebounceFn } from '@vueuse/core'
-import { Column, dayjs } from 'element-plus'
-
-const columns = ref<Column[]>([
-  {
-    align: 'center',
-    dataKey: 'deviceName',
-    title: '设备名称',
-    width: 100
-  },
-  {
-    align: 'center',
-    dataKey: 'serialNumber',
-    title: '设备编码',
-    width: 100
-  },
-  {
-    align: 'center',
-    dataKey: 'identity',
-    title: '属性',
-    width: 100
-  },
-  {
-    align: 'center',
-    dataKey: 'value',
-    title: '值',
-    width: 100
-  },
-  {
-    align: 'center',
-    dataKey: 'ts',
-    title: '时间',
-    width: 100
-  }
-])
+import { dayjs, ElMessage } from 'element-plus'
+import { computed, ref } from 'vue'
 
 const { t } = useI18n()
 
+// --- 状态定义 ---
 const id = useUserStore().getUser.deptId ?? 157
-
 const deptId = id
 
+// 查询参数
 interface Query {
   deptId?: number
   deviceName?: string
@@ -59,6 +29,12 @@ const query = ref<Query>({
   ]
 })
 
+// 分页相关参数
+const pageSize = ref(20) // 每页数量
+const lastTs = ref<string | null>(null) // 当前查询的游标 (上一页最后一条的时间)
+const historyStack = ref<(string | null)[]>([]) // 历史游标栈,用于实现“上一页”
+
+// 数据列表类型
 interface ListItem {
   deviceName: string
   serialNumber: string
@@ -68,51 +44,117 @@ interface ListItem {
 }
 
 const list = ref<ListItem[]>([])
-
 const loading = ref(false)
 
+// --- 计算属性 ---
+
+// 判断是否选择了设备 (根据你的需求,这里主要判断 deviceCode)
+const isDeviceSelected = computed(() => {
+  return !!query.value.deviceCode
+})
+
+// 判断是否可以点击上一页
+const canGoBack = computed(() => historyStack.value.length > 0)
+// 判断是否可以点击下一页 (简单逻辑:当前列表数量等于每页数量,说明可能还有下一页)
+const canGoNext = computed(() => list.value.length === pageSize.value)
+
+// --- 核心逻辑 ---
+
 const loadList = useDebounceFn(async function () {
+  // 如果没有选择设备,不请求,直接清空列表
+  if (!isDeviceSelected.value) {
+    list.value = []
+    return
+  }
+
   loading.value = true
   try {
     const { time, ...other } = query.value
 
-    const data = await IotDeviceApi.getMonitoringQuery({
+    // 构造请求参数,加入分页参数
+    // 注意:这里假设后端接口支持 pageSize 和 lastTs 参数
+    // 如果后端字段名不同(例如 limit, offset 等),请在此处修改
+    const params = {
       ...other,
       beginTime: time?.[0],
-      endTime: time?.[1]
-    })
-    list.value = data.data
+      endTime: time?.[1],
+      pageSize: pageSize.value, // 每页条数
+      lastTs: lastTs.value // 游标
+    }
+
+    const data = await IotDeviceApi.getMonitoringQuery(params)
+    list.value = data.data || []
+  } catch (error) {
+    console.error(error)
+    list.value = []
   } finally {
     loading.value = false
   }
 })
 
+// 搜索按钮逻辑
 function handleQuery() {
+  if (!query.value.deviceCode) {
+    ElMessage.warning('请先输入设备编码')
+    return
+  }
+  // 重置分页状态
+  lastTs.value = null
+  historyStack.value = []
+  loadList()
+}
+
+// 下一页
+function handleNext() {
+  if (list.value.length === 0) return
+
+  // 1. 将当前游标推入栈中 (保存当前页的状态以便返回)
+  historyStack.value.push(lastTs.value)
+
+  // 2. 获取列表最后一条数据的时间戳作为新的游标
+  // 假设 list 中包含 ts 字段,且数据是按时间排序的
+  const lastItem = list.value[list.value.length - 1]
+  if (lastItem && lastItem.ts) {
+    lastTs.value = lastItem.ts
+    loadList()
+  } else {
+    ElMessage.warning('无法获取下一页游标')
+  }
+}
+
+// 上一页
+function handlePrev() {
+  if (historyStack.value.length === 0) return
+
+  // 1. 弹出栈顶元素作为游标
+  const prevTs = historyStack.value.pop()
+
+  // 2. 设置并加载
+  lastTs.value = prevTs ?? null
+  loadList()
+}
+
+// 每页数量变化
+function handleSizeChange() {
+  // 切换大小时重置回第一页,避免游标错乱
+  lastTs.value = null
+  historyStack.value = []
   loadList()
 }
 
-// watch(
-//   [
-//     () => query.value.deptId,
-//     () => query.value.deviceName,
-//     () => query.value.deviceCode,
-//     () => query.value.time
-//   ],
-//   () => {
-//     handleQuery()
-//   },
-//   { immediate: true }
-// )
+const { ZmTable, ZmTableColumn } = useTableComponents<ListItem>()
 </script>
 
 <template>
   <div
     class="grid grid-cols-[15%_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
   >
-    <!-- v-model="query.deptId" -->
+    <!-- 左侧部门树 -->
     <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-2">
       <DeptTreeSelect :top-id="156" :deptId="deptId" :init-select="false" :show-title="false" />
     </div>
+
+    <!-- 顶部搜索栏 -->
     <el-form
       size="default"
       class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-8 gap-8 flex items-center justify-between"
@@ -155,19 +197,100 @@ function handleQuery() {
         </el-form-item>
       </div>
     </el-form>
-    <div class="bg-white dark:bg-[#1d1e1f] shadow rounded-lg flex flex-col p-4">
-      <div class="flex-1 relative">
-        <el-auto-resizer class="absolute">
-          <template #default="{ width, height }">
-            <el-table-v2
-              v-loading="loading"
-              :columns="columns"
-              :width="width"
-              :height="height"
-              :data="list"
-            />
-          </template>
-        </el-auto-resizer>
+
+    <!-- 表格区域 -->
+    <div class="bg-white dark:bg-[#1d1e1f] shadow rounded-lg flex flex-col p-4 relative">
+      <!-- 场景1:未选择设备 -->
+      <div
+        v-if="!isDeviceSelected"
+        class="flex-1 flex flex-col items-center justify-center text-gray-400"
+      >
+        <Icon icon="ep:warning" class="text-6xl mb-4" />
+        <span class="text-lg">请先输入设备编码进行搜索</span>
+      </div>
+
+      <!-- 场景2:已选择设备 (显示表格) -->
+      <div v-else class="flex-1 flex flex-col h-full overflow-hidden">
+        <div class="flex-1 min-h-0">
+          <!-- 注意:这里使用了 el-auto-resizer 包裹 zm-table 以获得自适应高度 -->
+          <el-auto-resizer>
+            <template #default="{ width, height }">
+              <zm-table
+                :data="list"
+                :loading="loading"
+                :width="width"
+                :height="height"
+                :max-height="height"
+              >
+                <zm-table-column type="index" label="序号" :width="60" align="center" />
+                <zm-table-column
+                  prop="deviceName"
+                  title="设备名称"
+                  label="设备名称"
+                  align="center"
+                  min-width="120"
+                />
+                <zm-table-column
+                  prop="serialNumber"
+                  title="设备编码"
+                  label="设备编码"
+                  align="center"
+                  min-width="120"
+                />
+                <zm-table-column
+                  prop="identity"
+                  title="属性"
+                  label="属性"
+                  align="center"
+                  min-width="100"
+                />
+                <zm-table-column
+                  prop="value"
+                  title="值"
+                  label="值"
+                  align="center"
+                  min-width="100"
+                />
+                <zm-table-column
+                  prop="ts"
+                  title="时间"
+                  label="时间"
+                  align="center"
+                  min-width="160"
+                />
+              </zm-table>
+            </template>
+          </el-auto-resizer>
+        </div>
+
+        <!-- 自定义分页工具栏 -->
+        <div
+          class="h-[50px] flex items-center justify-end border-t border-gray-100 dark:border-gray-700 mt-2 gap-4"
+        >
+          <div class="text-sm text-gray-500">
+            每页显示:
+            <el-select
+              v-model="pageSize"
+              class="!w-[80px]"
+              size="default"
+              @change="handleSizeChange"
+            >
+              <el-option :value="10" label="10" />
+              <el-option :value="20" label="20" />
+              <el-option :value="50" label="50" />
+              <el-option :value="100" label="100" />
+            </el-select>
+          </div>
+
+          <div class="flex gap-2">
+            <el-button size="default" :disabled="!canGoBack || loading" @click="handlePrev">
+              <Icon icon="ep:arrow-left" /> 上一页
+            </el-button>
+            <el-button size="default" :disabled="!canGoNext || loading" @click="handleNext">
+              下一页 <Icon icon="ep:arrow-right" />
+            </el-button>
+          </div>
+        </div>
       </div>
     </div>
   </div>