From a1d7e81859f554f3a53680cc35f0f49bf1f77098 Mon Sep 17 00:00:00 2001
From: wwf <1971391498@qq.com>
Date: 星期四, 14 五月 2026 14:37:02 +0800
Subject: [PATCH] 导入项目

---
 src/views/iot/device/device/detail/DeviceDetailsInfo.vue |  197 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 197 insertions(+), 0 deletions(-)

diff --git a/src/views/iot/device/device/detail/DeviceDetailsInfo.vue b/src/views/iot/device/device/detail/DeviceDetailsInfo.vue
new file mode 100644
index 0000000..e3beda7
--- /dev/null
+++ b/src/views/iot/device/device/detail/DeviceDetailsInfo.vue
@@ -0,0 +1,197 @@
+<!-- 璁惧淇℃伅 -->
+<template>
+  <div>
+    <ContentWrap>
+      <el-row :gutter="16">
+        <!-- 宸︿晶璁惧淇℃伅 -->
+        <el-col :span="12">
+          <el-card class="h-full">
+            <template #header>
+              <div class="flex items-center">
+                <Icon icon="ep:info-filled" class="mr-2 text-primary" />
+                <span>璁惧淇℃伅</span>
+              </div>
+            </template>
+            <el-descriptions :column="2" border>
+              <el-descriptions-item label="浜у搧鍚嶇О">{{ product.name }}</el-descriptions-item>
+              <el-descriptions-item label="ProductKey">
+                {{ product.productKey }}
+              </el-descriptions-item>
+              <el-descriptions-item label="璁惧绫诲瀷">
+                <dict-tag :type="DICT_TYPE.IOT_PRODUCT_DEVICE_TYPE" :value="product.deviceType" />
+              </el-descriptions-item>
+              <el-descriptions-item label="瀹氫綅绫诲瀷">
+                <dict-tag :type="DICT_TYPE.IOT_LOCATION_TYPE" :value="device.locationType" />
+              </el-descriptions-item>
+              <el-descriptions-item label="DeviceName">
+                {{ device.deviceName }}
+              </el-descriptions-item>
+              <el-descriptions-item label="澶囨敞鍚嶇О">{{ device.nickname }}</el-descriptions-item>
+              <el-descriptions-item label="鍒涘缓鏃堕棿">
+                {{ formatDate(device.createTime) }}
+              </el-descriptions-item>
+              <el-descriptions-item label="褰撳墠鐘舵��">
+                <dict-tag :type="DICT_TYPE.IOT_DEVICE_STATE" :value="device.state" />
+              </el-descriptions-item>
+              <el-descriptions-item label="婵�娲绘椂闂�">
+                {{ formatDate(device.activeTime) }}
+              </el-descriptions-item>
+              <el-descriptions-item label="鏈�鍚庝笂绾挎椂闂�">
+                {{ formatDate(device.onlineTime) }}
+              </el-descriptions-item>
+              <el-descriptions-item label="鏈�鍚庣绾挎椂闂�">
+                {{ formatDate(device.offlineTime) }}
+              </el-descriptions-item>
+              <el-descriptions-item label="璁よ瘉淇℃伅">
+                <el-button type="primary" @click="handleAuthInfoDialogOpen" plain size="small"
+                  >鏌ョ湅</el-button
+                >
+              </el-descriptions-item>
+            </el-descriptions>
+          </el-card>
+        </el-col>
+
+        <!-- 鍙充晶鍦板浘 -->
+        <el-col :span="12">
+          <el-card class="h-full">
+            <template #header>
+              <div class="flex items-center justify-between">
+                <div class="flex items-center">
+                  <Icon icon="ep:location" class="mr-2 text-primary" />
+                  <span>璁惧浣嶇疆</span>
+                </div>
+                <div class="text-[14px] text-[var(--el-text-color-secondary)]">
+                  鏈�鍚庝笂绾挎椂闂达細
+                  {{ device.onlineTime ? formatDate(device.onlineTime) : '--' }}
+                </div>
+              </div>
+            </template>
+            <div class="h-[400px] w-full">
+              <Map v-if="showMap" :center="getLocationString()" class="h-full w-full" />
+              <div
+                v-else
+                class="flex items-center justify-center h-full w-full bg-[var(--el-fill-color-light)] text-[var(--el-text-color-secondary)]"
+              >
+                <Icon icon="ep:warning" class="mr-2 text-warning" />
+                <span>鏆傛棤浣嶇疆淇℃伅</span>
+              </div>
+            </div>
+          </el-card>
+        </el-col>
+      </el-row>
+    </ContentWrap>
+
+    <!-- 璁よ瘉淇℃伅寮规 -->
+    <Dialog
+      title="璁惧璁よ瘉淇℃伅"
+      v-model="authDialogVisible"
+      width="640px"
+      :before-close="handleAuthInfoDialogClose"
+    >
+      <el-form :model="authInfo" label-width="120px">
+        <el-form-item label="clientId">
+          <el-input v-model="authInfo.clientId" readonly>
+            <template #append>
+              <el-button @click="copyToClipboard(authInfo.clientId)" type="primary">
+                <Icon icon="ph:copy" />
+              </el-button>
+            </template>
+          </el-input>
+        </el-form-item>
+        <el-form-item label="username">
+          <el-input v-model="authInfo.username" readonly>
+            <template #append>
+              <el-button @click="copyToClipboard(authInfo.username)" type="primary">
+                <Icon icon="ph:copy" />
+              </el-button>
+            </template>
+          </el-input>
+        </el-form-item>
+        <el-form-item label="password">
+          <el-input
+            v-model="authInfo.password"
+            readonly
+            :type="authPasswordVisible ? 'text' : 'password'"
+          >
+            <template #append>
+              <el-button @click="authPasswordVisible = !authPasswordVisible" type="primary">
+                <Icon :icon="authPasswordVisible ? 'ph:eye-slash' : 'ph:eye'" />
+              </el-button>
+              <el-button @click="copyToClipboard(authInfo.password)" type="primary">
+                <Icon icon="ph:copy" />
+              </el-button>
+            </template>
+          </el-input>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="handleAuthInfoDialogClose">鍏抽棴</el-button>
+      </template>
+    </Dialog>
+  </div>
+
+  <!-- TODO 寰呭紑鍙戯細璁惧鏍囩 -->
+</template>
+<script setup lang="ts">
+import { DICT_TYPE } from '@/utils/dict'
+import { ProductVO } from '@/api/iot/product/product'
+import { formatDate } from '@/utils/formatTime'
+import { DeviceVO } from '@/api/iot/device/device'
+import { DeviceApi, IotDeviceAuthInfoVO } from '@/api/iot/device/device'
+import Map from '@/components/Map/index.vue'
+import { ref, computed } from 'vue'
+import { useClipboard } from '@vueuse/core'
+
+const message = useMessage() // 娑堟伅鎻愮ず
+const { t } = useI18n() // 鍥介檯鍖�
+
+const { product, device } = defineProps<{ product: ProductVO; device: DeviceVO }>() // 瀹氫箟 Props
+const emit = defineEmits(['refresh']) // 瀹氫箟 Emits
+
+const authDialogVisible = ref(false) // 瀹氫箟璁惧璁よ瘉淇℃伅寮规鐨勫彲瑙佹��
+const authPasswordVisible = ref(false) // 瀹氫箟瀵嗙爜鍙鎬х姸鎬�
+const authInfo = ref<IotDeviceAuthInfoVO>({} as IotDeviceAuthInfoVO) // 瀹氫箟璁惧璁よ瘉淇℃伅瀵硅薄
+
+/** 鎺у埗鍦板浘鏄剧ず鐨勬爣蹇� */
+const showMap = computed(() => {
+  return !!(device.longitude && device.latitude)
+})
+
+/** 鑾峰彇浣嶇疆瀛楃涓诧紝鐢ㄤ簬鍦板浘缁勪欢 */
+const getLocationString = () => {
+  if (device.longitude && device.latitude) {
+    return `${device.longitude},${device.latitude}`
+  }
+  return ''
+}
+
+/** 澶嶅埗鍒板壀璐存澘鏂规硶 */
+const copyToClipboard = async (text: string) => {
+  const { copy, copied, isSupported } = useClipboard({ legacy: true, source: text })
+  if (!isSupported) {
+    message.error(t('common.copyError'))
+    return
+  }
+  await copy()
+  if (unref(copied)) {
+    message.success(t('common.copySuccess'))
+  }
+}
+
+/** 鎵撳紑璁惧璁よ瘉淇℃伅寮规鐨勬柟娉� */
+const handleAuthInfoDialogOpen = async () => {
+  try {
+    authInfo.value = await DeviceApi.getDeviceAuthInfo(device.id)
+    // 鏄剧ず璁惧璁よ瘉淇℃伅寮规
+    authDialogVisible.value = true
+  } catch (error) {
+    console.error('鑾峰彇璁惧璁よ瘉淇℃伅鍑洪敊锛�', error)
+    message.error('鑾峰彇璁惧璁よ瘉淇℃伅澶辫触锛岃妫�鏌ョ綉缁滆繛鎺ユ垨鑱旂郴绠$悊鍛�')
+  }
+}
+
+/** 鍏抽棴璁惧璁よ瘉淇℃伅寮规鐨勬柟娉� */
+const handleAuthInfoDialogClose = () => {
+  authDialogVisible.value = false
+}
+</script>

--
Gitblit v1.8.0