Browse Source

feat: 媒体系统详情登录页签与详情表切换到 TDesign

main
xiaosi 3 weeks ago
parent
commit
f4826350c9
  1. 95
      src/views/DeviceDetailView/DeviceDetailView.vue
  2. 50
      src/views/LoginView/LoginView.vue
  3. 24
      src/views/MediaView/MediaView.vue
  4. 17
      src/views/SystemView/SystemView.vue

95
src/views/DeviceDetailView/DeviceDetailView.vue

@ -19,13 +19,13 @@
</div> </div>
</header> </header>
<div class="full-detail-tabs">
<button :class="{ active: activeTab === 'overview' }" @click="activeTab = 'overview'">概览</button>
<button :class="{ active: activeTab === 'state' }" @click="activeTab = 'state'">设备状态</button>
<button :class="{ active: activeTab === 'alarms' }" @click="activeTab = 'alarms'">告警记录<b v-if="alarmRecords.length">{{ alarmRecords.length }}</b></button>
<button :class="{ active: activeTab === 'logs' }" @click="activeTab = 'logs'">操作记录</button>
<button :class="{ active: activeTab === 'diagnostics' }" @click="activeTab = 'diagnostics'">运维诊断</button>
</div>
<t-tabs v-model="activeTab" class="device-detail-tabs">
<t-tab-panel value="overview" label="概览" :destroy-on-hide="false" />
<t-tab-panel value="state" label="设备状态" :destroy-on-hide="false" />
<t-tab-panel value="alarms" :label="alarmsTabLabel" :destroy-on-hide="false" />
<t-tab-panel value="logs" label="操作记录" :destroy-on-hide="false" />
<t-tab-panel value="diagnostics" label="运维诊断" :destroy-on-hide="false" />
</t-tabs>
<div v-if="activeTab === 'overview'" class="full-detail-content"> <div v-if="activeTab === 'overview'" class="full-detail-content">
<template v-if="!isDrone"> <template v-if="!isDrone">
@ -218,20 +218,19 @@
<div><h2>告警记录</h2><span>当前设备</span></div> <div><h2>告警记录</h2><span>当前设备</span></div>
<t-select v-model="alarmStatusFilter" :options="alarmStatusOptions" placeholder="全部告警状态" style="width: 160px" /> <t-select v-model="alarmStatusFilter" :options="alarmStatusOptions" placeholder="全部告警状态" style="width: 160px" />
</div> </div>
<table class="record-table">
<thead><tr><th>告警时间</th><th>告警编码</th><th>告警内容</th><th>级别</th><th>状态</th><th>恢复时间</th></tr></thead>
<tbody>
<tr v-for="item in filteredAlarms" :key="item.id">
<td>{{ item.triggeredAt }}</td>
<td>{{ item.code }}</td>
<td>{{ item.message }}</td>
<td><i class="severity" :class="item.levelClass">{{ item.levelName }}</i></td>
<td><span class="table-status" :class="item.statusClass"><i></i>{{ item.statusName }}</span></td>
<td>{{ item.resolvedAt }}</td>
</tr>
</tbody>
</table>
<div v-if="!filteredAlarms.length" class="table-empty">暂无告警记录</div>
<t-table
row-key="id"
:data="filteredAlarms"
:columns="alarmColumns"
empty="暂无告警记录"
>
<template #level="{ row }">
<i class="severity" :class="row.levelClass">{{ row.levelName }}</i>
</template>
<template #status="{ row }">
<span class="table-status" :class="row.statusClass"><i></i>{{ row.statusName }}</span>
</template>
</t-table>
</div> </div>
<div v-else-if="activeTab === 'logs'" class="full-detail-content"> <div v-else-if="activeTab === 'logs'" class="full-detail-content">
@ -239,19 +238,16 @@
<div><h2>操作记录</h2><span>远程控制与配置变更</span></div> <div><h2>操作记录</h2><span>远程控制与配置变更</span></div>
<t-button variant="outline" @click="loadLogs">刷新</t-button> <t-button variant="outline" @click="loadLogs">刷新</t-button>
</div> </div>
<table class="record-table">
<thead><tr><th>操作时间</th><th>操作类型</th><th>操作内容</th><th>执行结果</th><th>目标设备</th></tr></thead>
<tbody>
<tr v-for="log in logs" :key="log.id">
<td>{{ log.createdAt }}</td>
<td>{{ log.category }}</td>
<td>{{ log.commandName }}</td>
<td><span :class="log.resultClass">{{ log.result }}</span></td>
<td>{{ log.droneSn }}</td>
</tr>
</tbody>
</table>
<div v-if="!logs.length" class="table-empty">暂无设备操作记录</div>
<t-table
row-key="id"
:data="logs"
:columns="logColumns"
empty="暂无设备操作记录"
>
<template #result="{ row }">
<span :class="row.resultClass">{{ row.result }}</span>
</template>
</t-table>
</div> </div>
<div v-else class="full-detail-content"> <div v-else class="full-detail-content">
@ -339,6 +335,25 @@ const alarmStatusOptions = [
] ]
const editDockVisible = ref(false) const editDockVisible = ref(false)
const alarmsTabLabel = computed(() => (alarmRecords.value.length ? `告警记录 ${alarmRecords.value.length}` : '告警记录'))
const alarmColumns = [
{ colKey: 'triggeredAt', title: '告警时间', width: '18%' },
{ colKey: 'code', title: '告警编码', width: '14%' },
{ colKey: 'message', title: '告警内容', width: '28%' },
{ colKey: 'level', title: '级别', width: '10%' },
{ colKey: 'status', title: '状态', width: '12%' },
{ colKey: 'resolvedAt', title: '恢复时间', width: '18%' }
]
const logColumns = [
{ colKey: 'createdAt', title: '操作时间', width: '20%' },
{ colKey: 'category', title: '操作类型', width: '14%' },
{ colKey: 'commandName', title: '操作内容', width: '26%' },
{ colKey: 'result', title: '执行结果', width: '16%' },
{ colKey: 'droneSn', title: '目标设备', width: '24%' }
]
const record = computed(() => devices.record(route.params.id)) const record = computed(() => devices.record(route.params.id))
const asset = computed(() => devices.asset(route.params.id)) const asset = computed(() => devices.asset(route.params.id))
const isDrone = computed(() => record.value?.kind === 'drone') const isDrone = computed(() => record.value?.kind === 'drone')
@ -598,3 +613,15 @@ function goMonitor() {
router.push({ name: 'monitor', query: { deviceId: route.params.id } }) router.push({ name: 'monitor', query: { deviceId: route.params.id } })
} }
</script> </script>
<style scoped>
.device-detail-tabs :deep(.t-tabs__nav-container) {
padding: 0 24px;
}
.device-detail-tabs :deep(.t-tabs__nav-wrap) {
margin-left: 0;
}
.full-detail-content :deep(.t-table) {
background: transparent;
}
</style>

50
src/views/LoginView/LoginView.vue

@ -225,22 +225,10 @@ async function submitForgot() {
<h2>欢迎回来</h2> <h2>欢迎回来</h2>
<p>登录平台继续管理您的飞行任务</p> <p>登录平台继续管理您的飞行任务</p>
</div> </div>
<div :class="s.methodTabs">
<button
type="button"
:class="[s.tab, { [s.tabActive]: method === 'password' }]"
@click="method = 'password'"
>
账号密码登录
</button>
<button
type="button"
:class="[s.tab, { [s.tabActive]: method === 'sms' }]"
@click="method = 'sms'"
>
手机号登录
</button>
</div>
<t-tabs v-model="method" :class="s.methodTabs">
<t-tab-panel value="password" label="账号密码登录" :destroy-on-hide="false" />
<t-tab-panel value="sms" label="手机号登录" :destroy-on-hide="false" />
</t-tabs>
<t-form label-width="0" @submit="submitLogin"> <t-form label-width="0" @submit="submitLogin">
<template v-if="method === 'password'"> <template v-if="method === 'password'">
@ -677,33 +665,25 @@ async function submitForgot() {
} }
.methodTabs { .methodTabs {
height: 38px;
margin: 0 0 22px; margin: 0 0 22px;
display: grid;
grid-template-columns: 1fr 1fr;
border-bottom: 1px solid #dce4e9;
}
.tab {
position: relative;
border: 0;
:global(.t-tabs__nav-container) {
border-bottom-color: #dce4e9;
}
:global(.t-tabs__nav-item) {
flex: 1;
justify-content: center;
color: #7d8b94; color: #7d8b94;
background: transparent;
font-size: 13px; font-size: 13px;
cursor: pointer;
}
}
.tabActive {
:global(.t-tabs__nav-item.t-is-active) {
color: #1f6feb; color: #1f6feb;
font-weight: 600; font-weight: 600;
}
&::after {
content: '';
position: absolute;
right: 0;
bottom: -1px;
left: 0;
height: 2px;
:global(.t-tabs__bar) {
background: #1f6feb; background: #1f6feb;
} }
} }

24
src/views/MediaView/MediaView.vue

@ -11,10 +11,10 @@
</header> </header>
<div class="business-workspace"> <div class="business-workspace">
<div class="business-tabs">
<button :class="{ active: view === 'live' }" @click="setView('live')">直播中心<b>{{ liveStreams.length }}</b></button>
<button :class="{ active: view === 'videos' }" @click="setView('videos')">原始视频<b>{{ videos.length }}</b></button>
</div>
<t-tabs v-model="view" class="business-tdesign-tabs" @change="setView">
<t-tab-panel value="live" :label="liveTabLabel" :destroy-on-hide="false" />
<t-tab-panel value="videos" :label="videosTabLabel" :destroy-on-hide="false" />
</t-tabs>
<!-- 直播中心 --> <!-- 直播中心 -->
<div class="business-panel" :class="{ active: view === 'live' }" style="padding:0"> <div class="business-panel" :class="{ active: view === 'live' }" style="padding:0">
@ -65,7 +65,7 @@
</template> </template>
<script setup> <script setup>
import { onMounted, ref, watch } from 'vue'
import { computed, onMounted, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { useUiStore } from '@/stores/modules/uiStore' import { useUiStore } from '@/stores/modules/uiStore'
import request from '@/utils/http' import request from '@/utils/http'
@ -76,19 +76,22 @@ const router = useRouter()
const ui = useUiStore() const ui = useUiStore()
const view = ref(route.query.view || 'videos') const view = ref(route.query.view || 'videos')
watch( watch(
() => route.query.view, () => route.query.view,
(v) => { if (v) view.value = v } (v) => { if (v) view.value = v }
) )
function setView(v) { function setView(v) {
if (!v || route.query.view === v) return
router.push({ name: 'media', query: { view: v } }) router.push({ name: 'media', query: { view: v } })
} }
const liveStreams = ref([]) const liveStreams = ref([])
const videos = ref([]) const videos = ref([])
const liveTabLabel = computed(() => `直播中心 ${liveStreams.value.length}`)
const videosTabLabel = computed(() => `原始视频 ${videos.value.length}`)
function fmtDuration(sec) { function fmtDuration(sec) {
if (!sec && sec !== 0) return '--:--' if (!sec && sec !== 0) return '--:--'
const s = Number(sec) const s = Number(sec)
@ -160,3 +163,12 @@ async function download(v) {
} }
} }
</script> </script>
<style scoped>
.business-tdesign-tabs :deep(.t-tabs__nav-container) {
padding: 0 18px;
}
.business-tdesign-tabs :deep(.t-tabs__nav-wrap) {
margin-left: 0;
}
</style>

17
src/views/SystemView/SystemView.vue

@ -8,10 +8,10 @@
</header> </header>
<div class="business-workspace system-workspace"> <div class="business-workspace system-workspace">
<div class="business-tabs">
<button :class="{ active: tab === 'general' }" @click="tab = 'general'">基础设置</button>
<button :class="{ active: tab === 'service' }" @click="tab = 'service'">服务状态</button>
</div>
<t-tabs v-model="tab" class="business-tdesign-tabs">
<t-tab-panel value="general" label="基础设置" :destroy-on-hide="false" />
<t-tab-panel value="service" label="服务状态" :destroy-on-hide="false" />
</t-tabs>
<!-- 基础设置 --> <!-- 基础设置 -->
<div class="system-panel" :class="{ active: tab === 'general' }"> <div class="system-panel" :class="{ active: tab === 'general' }">
@ -87,3 +87,12 @@ async function load() {
onMounted(load) onMounted(load)
</script> </script>
<style scoped>
.business-tdesign-tabs :deep(.t-tabs__nav-container) {
padding: 0 18px;
}
.business-tdesign-tabs :deep(.t-tabs__nav-wrap) {
margin-left: 0;
}
</style>

Loading…
Cancel
Save