| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 |
- <template>
- <div class="alarmHistoryPage">
- <div class="searchBar">
- <a-form layout="inline" @keydown.enter="searchHandler">
- <a-form-item label="设备ID">
- <a-input
- v-model:value.trim="searchState.clientId"
- placeholder="设备ID"
- :maxlength="12"
- show-count
- allow-clear
- />
- </a-form-item>
- <a-form-item label="创建时间">
- <range-picker
- v-model:start="searchState.createTimeStart"
- v-model:end="searchState.createTimeEnd"
- @change="searchHandler"
- />
- </a-form-item>
- <a-form-item label="事件类型">
- <a-select
- v-model:value="searchState.eventType"
- placeholder="请选择"
- style="width: 150px"
- :options="eventTypeList"
- @change="searchHandler"
- />
- </a-form-item>
- <a-form-item>
- <a-space>
- <a-button type="primary" @click="searchHandler"> 搜索 </a-button>
- <a-button @click="resetHandler"> 重置 </a-button>
- </a-space>
- </a-form-item>
- </a-form>
- </div>
- <div class="tableCard">
- <div class="tableCard-header">
- <div class="tableCard-header-title">告警历史列表</div>
- </div>
- <a-table :columns="columns" :data-source="tableList" :loading="loading" :pagination="false">
- <template #bodyCell="{ column, record }">
- <template v-if="column.key === 'clientId'">
- <div>{{ record.clientId }}</div>
- <div>{{ record.devName }}</div>
- </template>
- <template v-if="column.key === 'pose'">
- {{ record.poseName }}
- </template>
- <template v-if="column.key === 'eventType'">
- {{ record.eventTypeName }}
- </template>
- <template v-if="column.key === 'info'">
- <div class="eventInfo">
- <div v-if="record.info?.start_time">开始时间:{{ record.info?.start_time }}</div>
- <div v-if="record.info?.end_time">结束时间:{{ record.info?.end_time }}</div>
- <div v-if="record.info?.stay_time">停留时长:{{ record.info?.stay_time }}(秒)</div>
- <div v-if="record.info?.count">次数:{{ record.info?.count }}</div>
- <a-collapse ghost accordion v-if="record.info?.event_list && !initPanel">
- <template #expandIcon="{ isActive }">
- <caret-right-outlined :rotate="isActive ? 90 : 0" />
- </template>
- <a-collapse-panel :header="`事件列表 (${record.info?.event_list?.length ?? 0}条)`">
- <ScrollContainer style="max-height: 110px">
- <div
- v-for="(event, index) in record.info?.event_list"
- :key="index"
- class="eventInfo-list"
- >
- <div v-if="event.start_time">开始时间:{{ event.start_time }}</div>
- <div v-if="event.end_time">结束时间:{{ event.end_time }}</div>
- <div v-if="event.stay_time">停留时长:{{ event.stay_time }}(秒)</div>
- <div v-if="event.absence_time">消失时长:{{ event.absence_time }}(秒)</div>
- <span class="showIndex">{{ index + 1 }}</span>
- </div>
- </ScrollContainer>
- </a-collapse-panel>
- </a-collapse>
- </div>
- </template>
- </template>
- </a-table>
- <base-pagination
- v-if="tableTotal > 0"
- v-model:current="current"
- v-model:pageSize="pageSize"
- :total="tableTotal"
- @change="paginationChange"
- @showSizeChange="paginationSizeChange"
- ></base-pagination>
- </div>
- <alarmPlanModal
- v-if="alarmPlanVisible"
- v-model:open="alarmPlanVisible"
- :title="alarmPlanTitle"
- type="template"
- :alarm-plan-id="alarmPlanId"
- :data="alarmPlanDataWithType"
- :area="{
- width: 400,
- length: 400,
- ranges: [-200, 200, -200, 200],
- }"
- @success="fetchList"
- ></alarmPlanModal>
- </div>
- </template>
- <script setup lang="ts">
- import { ref, onActivated, computed } from 'vue'
- import { columns } from './const'
- import { useSearch } from '@/hooks/useSearch'
- // import { useRouter } from 'vue-router'
- import * as alarmApi from '@/api/alarm'
- import alarmPlanModal from '@/views/device/detail/components/alarmPlanModal/index.vue'
- // import { message } from 'ant-design-vue'
- import { useDictName } from '@/hooks/useDictName'
- import * as statsApi from '@/api/stats'
- import type { StatsAlarmQueryDataRow } from '@/api/stats/types'
- import { CaretRightOutlined } from '@ant-design/icons-vue'
- // const router = useRouter()
- defineOptions({
- name: 'AlarmHistoryIndex',
- })
- interface SearchData {
- createTimeStart: string // 创建时间开始
- createTimeEnd: string // 创建时间结束
- type?: ID // 事件类型 一般滞留
- eventType?: ID // 事件类型 异常滞留
- clientId?: ID // 设备ID
- }
- const defaultSearch: SearchData = {
- createTimeStart: '',
- createTimeEnd: '',
- type: null,
- eventType: null,
- clientId: null,
- }
- const [searchState, resetHandler] = useSearch(defaultSearch, { afterReset: () => searchHandler() })
- const tableList = ref<StatsAlarmQueryDataRow[]>([])
- const tableTotal = ref<number>(0)
- const current = ref<number>(1)
- const pageSize = ref<number>(10)
- const alarmList = ref<StatsAlarmQueryDataRow[]>([])
- // 分页变化
- const paginationChange = (current: number, pageSize: number) => {
- console.log('change', current, pageSize)
- fetchList()
- }
- // 分页大小变化
- const paginationSizeChange = (current: number, pageSize: number) => {
- console.log('showSizeChange', current, pageSize)
- }
- const eventTypeList = ref<{ label: string; value: string }[]>([])
- // 获取事件类型下拉列表
- const fetchEventTypeList = async () => {
- try {
- const res = await alarmApi.getAlarmEventTypeList()
- console.log('获取事件类型下拉列表成功✅', res)
- const data = res.data
- eventTypeList.value =
- (Array.isArray(data) &&
- data.map((item) => ({
- label: item.eventDesc,
- value: item.eventVal,
- }))) ||
- []
- } catch (err) {
- console.log('获取事件类型下拉列表失败❌', err)
- }
- }
- const { dictNameMap: alarmEventTypeName } = useDictName(eventTypeList)
- const loading = ref(false)
- // 获取列表
- const fetchList = async () => {
- console.log('fetchList')
- try {
- loading.value = true
- initPanel.value = true
- const res = await statsApi.statsAlarmQuery({
- pageNo: current.value,
- pageSize: pageSize.value,
- createTimeStart: searchState.createTimeStart,
- createTimeEnd: searchState.createTimeEnd,
- eventType: searchState.eventType as ID,
- clientId: searchState.clientId,
- })
- console.log('✅ 获取告警统计数据成功', res)
- const { rows, total } = res.data
- alarmList.value = rows as StatsAlarmQueryDataRow[]
- alarmList.value.forEach((item) => {
- item.eventTypeName = alarmEventTypeName(item.eventType)
- try {
- item.info = item.info && JSON.parse(item.info)
- } catch (error) {
- item.info = ''
- console.log('❌ 解析info失败', error)
- }
- })
- tableList.value = alarmList.value
- tableTotal.value = Number(total)
- loading.value = false
- initPanel.value = false
- } catch (error) {
- console.error('❌ 获取告警统计数据失败', error)
- loading.value = false
- initPanel.value = false
- }
- }
- // 搜索
- const searchHandler = async () => {
- console.log('searchState', searchState)
- current.value = 1
- pageSize.value = 10
- await fetchList()
- }
- const alarmPlanVisible = ref(false) // 告警计划弹窗
- const alarmPlanId = ref<number | null>(null) // 当前编辑的告警计划id
- const alarmPlanTitle = ref<string>('添加告警计划')
- type AlarmPlan = {
- id: number
- uuid: ID
- name: string
- clientId: string
- enable: SwitchType
- region: string
- eventVal: number
- alarmTimePlanId: ID
- thresholdTime: ID
- mergeTime: ID
- param: string
- createTime: string
- updateTime: string
- remark: string | null
- alarmTimePlan: {
- createId: ID
- updateId: ID
- createTime: ID
- updateTime: ID
- isDeleted: SwitchType | null
- remark: ID
- id: ID
- startDate: string
- stopDate: string
- timeRange: string
- monthDays: string
- weekdays: string
- }
- }
- interface AlarmPlanItem {
- id?: number
- loading?: boolean
- [key: string]: unknown
- }
- const alarmPlanData = ref<AlarmPlanItem | undefined>(undefined)
- const alarmPlanDataWithType = computed(() => alarmPlanData.value as AlarmPlan)
- onActivated(async () => {
- await fetchEventTypeList().catch((err) => {
- console.error('fetchEventTypeList 失败', err)
- })
- await fetchList().catch((err) => {
- console.error('fetchList 失败', err)
- })
- })
- const initPanel = ref<boolean>(false)
- </script>
- <style scoped lang="less">
- .alarmHistoryPage {
- .searchBar {
- padding: 20px;
- background-color: #fff;
- margin-bottom: 20px;
- display: flex;
- justify-content: space-between;
- .ant-form {
- flex-grow: 1;
- }
- :deep(.ant-form-inline .ant-form-item) {
- margin-bottom: 16px !important;
- }
- }
- .tableCard {
- background-color: #fff;
- &-header {
- display: flex;
- justify-content: space-between;
- padding: 20px;
- &-title {
- font-size: 18px;
- font-weight: 600;
- }
- }
- .eventInfo {
- display: flex;
- flex-direction: column;
- align-items: flex-start;
- padding: 10px;
- border-radius: 4px;
- &-list {
- display: flex;
- flex-direction: column;
- align-items: flex-start;
- background-color: #eee;
- padding: 12px;
- border-radius: 5px;
- margin-bottom: 8px;
- position: relative;
- .showIndex {
- position: absolute;
- right: 0;
- bottom: 0;
- font-size: 12px;
- color: #fff;
- padding: 0 5px;
- border-radius: 5px 0 5px 0;
- background-color: #ccc;
- }
- }
- }
- }
- }
- :deep(.ant-collapse > .ant-collapse-item > .ant-collapse-header) {
- padding: 0 !important;
- }
- :deep(.ant-collapse > .ant-collapse-item > .ant-collapse-header > .ant-collapse-header-text) {
- flex: none;
- }
- :deep(.ant-collapse .ant-collapse-content > .ant-collapse-content-box) {
- padding: 0 !important;
- }
- </style>
|