feat 异常警告

This commit is contained in:
Ironsp
2026-09-14 15:10:48 +08:00
parent 3d857002e0
commit dd7c8b4162
9 changed files with 443 additions and 68 deletions

View File

@@ -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>

View File

@@ -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">
<text class="warning-title">
钢轮温度过高
</text>
<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>

View File

@@ -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;
}

View File

@@ -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>