feat 异常警告
This commit is contained in:
@@ -1,9 +1,31 @@
|
||||
<script setup lang="ts">
|
||||
import type { WarningRecord } from '@/service/warningRecord'
|
||||
import { onMounted } from 'vue'
|
||||
import { useDbStore } from '@/store/modules/db'
|
||||
|
||||
const db = useDbStore()
|
||||
|
||||
/** 首页只展示最近几条 */
|
||||
const list = computed(() => (db.warning_record || []).slice(0, 3))
|
||||
|
||||
onMounted(async () => {
|
||||
if (!db.warning_record?.length)
|
||||
await db.loadAll()
|
||||
})
|
||||
|
||||
function goToWarningList() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/warning/index',
|
||||
})
|
||||
}
|
||||
|
||||
function goToDetail(item: WarningRecord) {
|
||||
if (!item.id)
|
||||
return
|
||||
uni.navigateTo({
|
||||
url: `/pages/warning/detail/index?id=${item.id}`,
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -17,7 +39,15 @@ function goToWarningList() {
|
||||
</wd-button>
|
||||
</view>
|
||||
<view class="card-body">
|
||||
<home-warning_list />
|
||||
<view v-if="!list.length" class="empty-tip">
|
||||
暂无异常警告
|
||||
</view>
|
||||
<home-warning_list
|
||||
v-for="item in list"
|
||||
:key="item.id"
|
||||
:item="item"
|
||||
@tap="goToDetail(item)"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
@@ -47,4 +77,11 @@ function goToWarningList() {
|
||||
.card-body {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
font-size: 14px;
|
||||
color: #999999;
|
||||
text-align: center;
|
||||
padding: 16px 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,16 +1,85 @@
|
||||
<script setup lang="ts">
|
||||
import type { WarningRecord } from '@/service/warningRecord'
|
||||
|
||||
const props = defineProps<{
|
||||
item: WarningRecord
|
||||
}>()
|
||||
|
||||
/** 异常类型:0 数据中断 / 1 高温异常 / 2 定位异常 */
|
||||
const exceptionTypeMap: Record<number, string> = {
|
||||
0: '数据中断',
|
||||
1: '高温异常',
|
||||
2: '定位异常',
|
||||
}
|
||||
|
||||
/** 异常级别:0 提醒 / 1 警告 / 2 严重 */
|
||||
const exceptionLevelMap: Record<number, string> = {
|
||||
0: '提醒',
|
||||
1: '警告',
|
||||
2: '严重',
|
||||
}
|
||||
|
||||
/** 处置类型:0 未处理 / 1 现场恢复 / 2 需要保修 */
|
||||
const disposalTypeMap: Record<number, string> = {
|
||||
0: '未处理',
|
||||
1: '现场恢复',
|
||||
2: '需要保修',
|
||||
}
|
||||
|
||||
const title = computed(() => exceptionTypeMap[props.item.exception_type ?? -1] || '未知异常')
|
||||
|
||||
const levelText = computed(() => exceptionLevelMap[props.item.exception_level ?? -1] || '')
|
||||
|
||||
const desc = computed(() => {
|
||||
const device = props.item.device_name || '未知设备'
|
||||
const site = props.item.site_description || ''
|
||||
const disposal = disposalTypeMap[props.item.disposal_type ?? -1]
|
||||
return disposal ? `${device} ${site}(${disposal})` : `${device} ${site}`
|
||||
})
|
||||
|
||||
const timeText = computed(() => formatOccurredAt(props.item.occurred_at))
|
||||
|
||||
function formatOccurredAt(value?: string) {
|
||||
if (!value)
|
||||
return ''
|
||||
const date = new Date(value.replace(/-/g, '/'))
|
||||
if (Number.isNaN(date.getTime()))
|
||||
return value
|
||||
|
||||
const diffMs = Date.now() - date.getTime()
|
||||
const diffMin = Math.floor(diffMs / 60000)
|
||||
if (diffMin < 1)
|
||||
return '刚刚'
|
||||
if (diffMin < 60)
|
||||
return `${diffMin}分钟前`
|
||||
const diffHour = Math.floor(diffMin / 60)
|
||||
if (diffHour < 24)
|
||||
return `${diffHour}小时前`
|
||||
const diffDay = Math.floor(diffHour / 24)
|
||||
if (diffDay < 7)
|
||||
return `${diffDay}天前`
|
||||
return value.slice(0, 16)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="warning-item">
|
||||
<view class="warning-icon" />
|
||||
<view class="warning-icon" :class="`level-${item.exception_level ?? 0}`" />
|
||||
<view class="warning-info">
|
||||
<view class="warning-title-row">
|
||||
<text class="warning-title">
|
||||
钢轮温度过高
|
||||
{{ title }}
|
||||
</text>
|
||||
<text v-if="levelText" class="warning-level" :class="`level-${item.exception_level ?? 0}`">
|
||||
{{ levelText }}
|
||||
</text>
|
||||
</view>
|
||||
<text class="warning-desc">
|
||||
徐工XS65H #01 钢轮温度达90°C,超过國值85°C
|
||||
{{ desc }}
|
||||
</text>
|
||||
</view>
|
||||
<text class="warning-time">
|
||||
10分钟前
|
||||
{{ timeText }}
|
||||
</text>
|
||||
</view>
|
||||
</template>
|
||||
@@ -33,28 +102,74 @@
|
||||
border-radius: 50%;
|
||||
background-color: #fff2e8;
|
||||
margin-right: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.warning-icon.level-0 {
|
||||
background-color: #e8f3ff;
|
||||
}
|
||||
|
||||
.warning-icon.level-1 {
|
||||
background-color: #fff2e8;
|
||||
}
|
||||
|
||||
.warning-icon.level-2 {
|
||||
background-color: #ffece8;
|
||||
}
|
||||
|
||||
.warning-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.warning-title-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.warning-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.warning-level {
|
||||
font-size: 12px;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.warning-level.level-0 {
|
||||
color: #3b82f6;
|
||||
background-color: #e8f3ff;
|
||||
}
|
||||
|
||||
.warning-level.level-1 {
|
||||
color: #f59e0b;
|
||||
background-color: #fff2e8;
|
||||
}
|
||||
|
||||
.warning-level.level-2 {
|
||||
color: #ef4444;
|
||||
background-color: #ffece8;
|
||||
}
|
||||
|
||||
.warning-desc {
|
||||
font-size: 14px;
|
||||
color: #999999;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.warning-time {
|
||||
font-size: 14px;
|
||||
color: #cccccc;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
const activeTab = ref(0)
|
||||
const dateRange = ref('')
|
||||
const level = ref('')
|
||||
defineProps<{
|
||||
totalCount: number
|
||||
unhandledCount: number
|
||||
}>()
|
||||
const activeTab = defineModel<number>('activeTab', { default: 0 })
|
||||
const level = defineModel<number | null>('level', { default: null })
|
||||
const dateRange = defineModel<string>('dateRange', { default: '' })
|
||||
|
||||
const tabs = ['全部', '定位异常', '高温异常']
|
||||
const tabs = ['全部', '数据中断', '高温异常', '定位异常']
|
||||
|
||||
const dateOptions = [
|
||||
{ value: '', text: '全部时间' },
|
||||
{ value: 'today', text: '今天' },
|
||||
{ value: 'yesterday', text: '昨天' },
|
||||
{ value: 'week', text: '本周' },
|
||||
@@ -13,19 +18,24 @@ const dateOptions = [
|
||||
]
|
||||
|
||||
const levelOptions = [
|
||||
{ value: 'all', text: '全部级别' },
|
||||
{ value: 'critical', text: '严重' },
|
||||
{ value: 'warning', text: '警告' },
|
||||
{ value: 'info', text: '提醒' },
|
||||
{ value: -1, text: '全部级别' },
|
||||
{ value: 2, text: '严重' },
|
||||
{ value: 1, text: '警告' },
|
||||
{ value: 0, text: '提醒' },
|
||||
]
|
||||
|
||||
const totalCount = ref(8)
|
||||
const unhandledCount = ref(3)
|
||||
/** uni-data-select 用 -1 表示全部,再转成 null */
|
||||
const levelSelect = computed({
|
||||
get: () => (level.value === null ? -1 : level.value),
|
||||
set: (val: number) => {
|
||||
level.value = val === -1 || val === null || val === undefined ? null : Number(val)
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="warning-filter">
|
||||
<!-- 标签页 -->
|
||||
<!-- 标签页:按异常类型筛选 -->
|
||||
<view class="tabs">
|
||||
<view
|
||||
v-for="(tab, index) in tabs"
|
||||
@@ -40,10 +50,10 @@ const unhandledCount = ref(3)
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 筛选条件 -->
|
||||
<!-- 筛选条件:时间 + 异常级别 -->
|
||||
<view class="filters">
|
||||
<uni-data-select v-model="dateRange" :localdata="dateOptions" />
|
||||
<uni-data-select v-model="level" :localdata="levelOptions" />
|
||||
<uni-data-select v-model="dateRange" :localdata="dateOptions" placeholder="全部时间" />
|
||||
<uni-data-select v-model="levelSelect" :localdata="levelOptions" placeholder="全部级别" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -69,7 +79,7 @@ const unhandledCount = ref(3)
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
@@ -77,7 +87,7 @@ const unhandledCount = ref(3)
|
||||
flex: 1;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 8px;
|
||||
padding: 12px 0;
|
||||
padding: 10px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@@ -86,7 +96,7 @@ const unhandledCount = ref(3)
|
||||
}
|
||||
|
||||
.tab-text {
|
||||
font-size: 18px;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,18 +5,24 @@ const props = defineProps<{
|
||||
deviceName: string
|
||||
tags: string[]
|
||||
time: string
|
||||
trigger: string
|
||||
status?: '未处理' | '已处理'
|
||||
index: number
|
||||
warningId?: number
|
||||
}>()
|
||||
|
||||
function goToDetail() {
|
||||
if (props.warningId) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/warning/detail/index?id=${props.warningId}`,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const key = `warning_${Date.now()}`
|
||||
storage.set(key, {
|
||||
deviceName: props.deviceName,
|
||||
tags: props.tags,
|
||||
time: props.time,
|
||||
trigger: props.trigger,
|
||||
status: props.status || '未处理',
|
||||
})
|
||||
uni.navigateTo({
|
||||
@@ -37,7 +43,7 @@ function goToDetail() {
|
||||
</view>
|
||||
|
||||
<view class="tags">
|
||||
<view v-for="(tag, index) in tags" :key="index" class="tag">
|
||||
<view v-for="(tag, i) in tags" :key="i" class="tag">
|
||||
<text class="tag-text">
|
||||
{{ tag }}
|
||||
</text>
|
||||
@@ -47,9 +53,6 @@ function goToDetail() {
|
||||
<text class="time">
|
||||
{{ time }}
|
||||
</text>
|
||||
<text class="trigger">
|
||||
{{ trigger }}
|
||||
</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -112,9 +115,4 @@ function goToDetail() {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.trigger {
|
||||
font-size: 18px;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import type { WarningRecord } from '@/service/warningRecord'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { ref } from 'vue'
|
||||
import { warningRecordService } from '@/service'
|
||||
import storage from '@/utils/storage'
|
||||
|
||||
definePage({
|
||||
@@ -8,9 +12,30 @@ definePage({
|
||||
},
|
||||
})
|
||||
|
||||
const warningData = ref<any>(null)
|
||||
/** 异常类型 / 级别 / 处置类型文案 */
|
||||
const exceptionTypeMap: Record<number, string> = {
|
||||
0: '数据中断',
|
||||
1: '高温异常',
|
||||
2: '定位异常',
|
||||
}
|
||||
const exceptionLevelMap: Record<number, string> = {
|
||||
0: '提醒',
|
||||
1: '警告',
|
||||
2: '严重',
|
||||
}
|
||||
const disposalTypeMap: Record<number, string> = {
|
||||
0: '未处理',
|
||||
1: '现场恢复',
|
||||
2: '需要保修',
|
||||
}
|
||||
|
||||
onLoad((options) => {
|
||||
const warningData = ref<WarningRecord | null>(null)
|
||||
|
||||
onLoad(async (options) => {
|
||||
if (options?.id) {
|
||||
warningData.value = await warningRecordService.selectById(Number(options.id))
|
||||
return
|
||||
}
|
||||
if (options?.key) {
|
||||
warningData.value = storage.get(options.key)
|
||||
storage.remove(options.key)
|
||||
@@ -23,10 +48,66 @@ onLoad((options) => {
|
||||
<back-nav title="警告详情" />
|
||||
|
||||
<view class="content">
|
||||
<view v-if="warningData" class="detail-raw">
|
||||
<text class="raw-text">
|
||||
{{ JSON.stringify(warningData, null, 2) }}
|
||||
<view v-if="warningData" class="detail-card">
|
||||
<view class="row">
|
||||
<text class="label">
|
||||
设备名称
|
||||
</text>
|
||||
<text class="value">
|
||||
{{ warningData.device_name || '-' }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="label">
|
||||
异常类型
|
||||
</text>
|
||||
<text class="value">
|
||||
{{ exceptionTypeMap[warningData.exception_type ?? -1] || '-' }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="label">
|
||||
异常级别
|
||||
</text>
|
||||
<text class="value">
|
||||
{{ exceptionLevelMap[warningData.exception_level ?? -1] || '-' }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="label">
|
||||
发生时间
|
||||
</text>
|
||||
<text class="value">
|
||||
{{ warningData.occurred_at || '-' }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="label">
|
||||
现场描述
|
||||
</text>
|
||||
<text class="value">
|
||||
{{ warningData.site_description || '-' }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="label">
|
||||
处置类型
|
||||
</text>
|
||||
<text class="value">
|
||||
{{ disposalTypeMap[warningData.disposal_type ?? -1] || '-' }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="label">
|
||||
处置记录
|
||||
</text>
|
||||
<text class="value">
|
||||
{{ warningData.disposal_record || '-' }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="empty-tip">
|
||||
未找到告警详情
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -43,16 +124,38 @@ onLoad((options) => {
|
||||
padding-top: 72px;
|
||||
}
|
||||
|
||||
.detail-raw {
|
||||
.detail-card {
|
||||
background-color: #ffffff;
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.raw-text {
|
||||
font-size: 18px;
|
||||
.row {
|
||||
display: flex;
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
}
|
||||
|
||||
.row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.label {
|
||||
width: 90px;
|
||||
flex-shrink: 0;
|
||||
font-size: 16px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.value {
|
||||
flex: 1;
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
font-family: monospace;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
padding: 24px 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import type { WarningRecord } from '@/service/warningRecord'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useDbStore } from '@/store/modules/db'
|
||||
|
||||
definePage({
|
||||
layout: false,
|
||||
style: {
|
||||
@@ -6,22 +10,107 @@ definePage({
|
||||
},
|
||||
})
|
||||
|
||||
const warnings = [
|
||||
{
|
||||
deviceName: '徐工XS65H #01',
|
||||
tags: ['定位异常', '警告'],
|
||||
time: '14:10:04·已持续5分30秒',
|
||||
trigger: '触发阈值:RTK信号弱,精度下降',
|
||||
status: '未处理' as const,
|
||||
},
|
||||
{
|
||||
deviceName: '徐工XS65H #01',
|
||||
tags: ['定位异常', '警告'],
|
||||
time: '14:10:04·已持续5分30秒',
|
||||
trigger: '触发阈值:RTK信号弱,精度下降',
|
||||
status: '未处理' as const,
|
||||
},
|
||||
]
|
||||
const db = useDbStore()
|
||||
|
||||
/** 异常类型:0 数据中断 / 1 高温异常 / 2 定位异常 */
|
||||
const exceptionTypeMap: Record<number, string> = {
|
||||
0: '数据中断',
|
||||
1: '高温异常',
|
||||
2: '定位异常',
|
||||
}
|
||||
|
||||
/** 异常级别:0 提醒 / 1 警告 / 2 严重 */
|
||||
const exceptionLevelMap: Record<number, string> = {
|
||||
0: '提醒',
|
||||
1: '警告',
|
||||
2: '严重',
|
||||
}
|
||||
|
||||
const activeTab = ref(0) // 0全部 1数据中断 2高温异常 3定位异常
|
||||
const levelFilter = ref<number | null>(null) // null全部 / 0提醒 / 1警告 / 2严重
|
||||
const dateRange = ref('') // ''全部 / today / yesterday / week / month
|
||||
|
||||
const totalCount = computed(() => db.warning_record.length)
|
||||
const unhandledCount = computed(() =>
|
||||
db.warning_record.filter(item => item.disposal_type === 0).length,
|
||||
)
|
||||
|
||||
const warnings = computed(() => {
|
||||
return db.warning_record
|
||||
.filter((item) => {
|
||||
if (activeTab.value > 0 && item.exception_type !== activeTab.value - 1)
|
||||
return false
|
||||
if (levelFilter.value !== null && item.exception_level !== levelFilter.value)
|
||||
return false
|
||||
if (dateRange.value && !matchDateRange(item.occurred_at, dateRange.value))
|
||||
return false
|
||||
return true
|
||||
})
|
||||
.map(mapWarningItem)
|
||||
})
|
||||
|
||||
function parseDate(value?: string) {
|
||||
if (!value)
|
||||
return null
|
||||
const date = new Date(value.replace(/-/g, '/'))
|
||||
return Number.isNaN(date.getTime()) ? null : date
|
||||
}
|
||||
|
||||
function startOfDay(date: Date) {
|
||||
return new Date(date.getFullYear(), date.getMonth(), date.getDate())
|
||||
}
|
||||
|
||||
/** 按今天 / 昨天 / 本周 / 本月筛选发生时间 */
|
||||
function matchDateRange(occurredAt: string | undefined, range: string) {
|
||||
const occurred = parseDate(occurredAt)
|
||||
if (!occurred)
|
||||
return false
|
||||
|
||||
const now = new Date()
|
||||
const todayStart = startOfDay(now)
|
||||
const occurredStart = startOfDay(occurred)
|
||||
|
||||
if (range === 'today')
|
||||
return occurredStart.getTime() === todayStart.getTime()
|
||||
|
||||
if (range === 'yesterday') {
|
||||
const yesterday = new Date(todayStart)
|
||||
yesterday.setDate(yesterday.getDate() - 1)
|
||||
return occurredStart.getTime() === yesterday.getTime()
|
||||
}
|
||||
|
||||
if (range === 'week') {
|
||||
// 本周一 00:00 起
|
||||
const weekStart = new Date(todayStart)
|
||||
const day = weekStart.getDay() || 7 // 周日按 7
|
||||
weekStart.setDate(weekStart.getDate() - day + 1)
|
||||
return occurredStart.getTime() >= weekStart.getTime() && occurredStart.getTime() <= todayStart.getTime()
|
||||
}
|
||||
|
||||
if (range === 'month') {
|
||||
const monthStart = new Date(now.getFullYear(), now.getMonth(), 1)
|
||||
return occurredStart.getTime() >= monthStart.getTime() && occurredStart.getTime() <= todayStart.getTime()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function mapWarningItem(item: WarningRecord) {
|
||||
const typeText = exceptionTypeMap[item.exception_type ?? -1] || '未知异常'
|
||||
const levelText = exceptionLevelMap[item.exception_level ?? -1] || ''
|
||||
return {
|
||||
id: item.id,
|
||||
deviceName: item.device_name || '未知设备',
|
||||
tags: [typeText, levelText].filter(Boolean),
|
||||
time: item.occurred_at || '',
|
||||
status: (item.disposal_type === 0 ? '未处理' : '已处理') as '未处理' | '已处理',
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
if (!db.warning_record.length)
|
||||
await db.loadAll()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -29,17 +118,26 @@ const warnings = [
|
||||
<back-nav title="异常警告" />
|
||||
|
||||
<view class="content">
|
||||
<warning-filter />
|
||||
<warning-filter
|
||||
v-model:active-tab="activeTab"
|
||||
v-model:level="levelFilter"
|
||||
v-model:date-range="dateRange"
|
||||
:total-count="totalCount"
|
||||
:unhandled-count="unhandledCount"
|
||||
/>
|
||||
|
||||
<view class="warning-list">
|
||||
<view v-if="!warnings.length" class="empty-tip">
|
||||
暂无异常警告
|
||||
</view>
|
||||
<warning-item
|
||||
v-for="(item, index) in warnings"
|
||||
:key="index"
|
||||
:key="item.id ?? index"
|
||||
:index="index"
|
||||
:warning-id="item.id"
|
||||
:device-name="item.deviceName"
|
||||
:tags="item.tags"
|
||||
:time="item.time"
|
||||
:trigger="item.trigger"
|
||||
:status="item.status"
|
||||
/>
|
||||
</view>
|
||||
@@ -63,4 +161,11 @@ const warnings = [
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
text-align: center;
|
||||
padding: 24px 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -31,7 +31,7 @@ const expectedColumns: Record<string, string[]> = {
|
||||
project: ['id', 'project_name', 'stake_range', 'total_area', 'status', 'created_at', 'updated_at'],
|
||||
work_record: ['id', 'project_id', 'start_time', 'end_time', 'compaction_area', 'pass_count', 'created_at'],
|
||||
rtk_track: ['id', 'work_record_id', 'latitude', 'longitude', 'speed', 'heading', 'steel_wheel_temp', 'compaction_value', 'created_at'],
|
||||
warning_record: ['id', 'project_id', 'device_name', 'exception_type', 'occurred_at', 'site_description', 'disposal_record', 'disposal_type', 'is_read', 'created_at'],
|
||||
warning_record: ['id', 'project_id', 'device_name', 'exception_type', 'exception_level', 'occurred_at', 'site_description', 'disposal_record', 'disposal_type', 'is_read', 'created_at'],
|
||||
history_report: ['id', 'project_id', 'report_name', 'device_id', 'construction_area', 'start_time', 'end_time', 'duration', 'compaction_area', 'pass_count', 'qualified_rate', 'avg_speed', 'avg_paving_temp', 'avg_compaction', 'exception_count', 'created_at'],
|
||||
}
|
||||
|
||||
|
||||
@@ -107,7 +107,9 @@ async function seedRtkTrack() {
|
||||
|
||||
/**
|
||||
* 插入告警记录假数据(3条)
|
||||
* 字段:project_id / device_name / exception_type(0数据中断 1高温异常 2定位异常)
|
||||
* 字段:project_id / device_name
|
||||
* exception_type(0数据中断 1高温异常 2定位异常)
|
||||
* exception_level(0提醒 1警告 2严重)
|
||||
* occurred_at / site_description / disposal_record
|
||||
* disposal_type(0未处理 1现场恢复 2需要保修) / is_read
|
||||
*/
|
||||
@@ -117,6 +119,7 @@ async function seedWarningRecord() {
|
||||
project_id: 1,
|
||||
device_name: 'XS-65H',
|
||||
exception_type: 0,
|
||||
exception_level: 1,
|
||||
occurred_at: '2026-09-10 09:15:00',
|
||||
site_description: '设备上报数据中断超过 30 秒',
|
||||
disposal_record: '',
|
||||
@@ -127,6 +130,7 @@ async function seedWarningRecord() {
|
||||
project_id: 2,
|
||||
device_name: 'XS-65H',
|
||||
exception_type: 1,
|
||||
exception_level: 2,
|
||||
occurred_at: '2026-09-09 10:20:00',
|
||||
site_description: '钢轮温度超过安全阈值',
|
||||
disposal_record: '现场降温后恢复作业',
|
||||
@@ -137,6 +141,7 @@ async function seedWarningRecord() {
|
||||
project_id: 1,
|
||||
device_name: 'RTK-01',
|
||||
exception_type: 2,
|
||||
exception_level: 0,
|
||||
occurred_at: '2026-09-10 14:05:00',
|
||||
site_description: 'RTK 定位信号丢失,无法获取有效坐标',
|
||||
disposal_record: '设备故障,已登记报修',
|
||||
|
||||
@@ -8,6 +8,7 @@ export const warningRecordTableSql = `CREATE TABLE IF NOT EXISTS warning_record
|
||||
project_id INTEGER,
|
||||
device_name TEXT,
|
||||
exception_type INTEGER,
|
||||
exception_level INTEGER DEFAULT 0,
|
||||
occurred_at DATETIME,
|
||||
site_description TEXT,
|
||||
disposal_record TEXT,
|
||||
@@ -22,6 +23,7 @@ export interface WarningRecord {
|
||||
project_id: number // 所属项目 ID,关联 project.id
|
||||
device_name?: string // 设备名称
|
||||
exception_type?: 0 | 1 | 2 // 异常类型,0:数据中断,1:高温异常,2:定位异常
|
||||
exception_level?: 0 | 1 | 2 // 异常级别,0:提醒,1:警告,2:严重
|
||||
occurred_at?: string // 发生时间
|
||||
site_description?: string // 现场描述
|
||||
disposal_record?: string // 现场处置记录
|
||||
|
||||
Reference in New Issue
Block a user