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">
|
||||
<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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user