fix: bug
This commit is contained in:
parent
98c219c17b
commit
a24d064951
@ -4,7 +4,22 @@ import { AlarmNowListConfig } from './index'
|
||||
import cloneDeep from 'lodash/cloneDeep'
|
||||
import dataJson from './data.json'
|
||||
|
||||
|
||||
export const styleConfig = {
|
||||
titleText: '实时报警',
|
||||
titleOption: {
|
||||
color: '#ffffff',
|
||||
fontSize: '17px',
|
||||
fontStyle: 'normal',
|
||||
fontWeight: 'normal',
|
||||
fontFamily: 'CustomFont',
|
||||
},
|
||||
headerOption: {
|
||||
paddingLeft: 30,
|
||||
paddingRight: 0,
|
||||
paddingTop: 0,
|
||||
paddingBottom: 0
|
||||
}
|
||||
}
|
||||
export const option = {
|
||||
dataset: dataJson,
|
||||
rowNum: 4,
|
||||
|
@ -1,27 +1,29 @@
|
||||
<template>
|
||||
<div class="content_box">
|
||||
<vue3-seamless-scroll v-if="option.dataset && option.dataset.length > 0" class="seamless" :list="option.dataset"
|
||||
:limitScrollNum="rowNum" :hover="true" :step="waitTime" :wheel="true" :isWatch="true">
|
||||
<div v-for="(item, index) in option.dataset" class="detail flex_column" :key="index">
|
||||
<div class="flex_v cursor" :style="!showRankNum ? { paddingLeft: 0 } : { paddingLeft: '24px' }"
|
||||
@click="handleOpenDialog(item)">
|
||||
<div v-if="index === 0 && showRankNum" class="levelOneIcon flex_c">{{ index + 1 }}</div>
|
||||
<div v-else-if="index === 1 && showRankNum" class="levelTwoIcon flex_c">{{ index + 1 }}</div>
|
||||
<div v-else-if="showRankNum" class="levelOtherIcon flex_c">{{ index + 1 }}</div>
|
||||
<div class="item_content">
|
||||
<div class="item_header flex">
|
||||
<div class="item_level_text">{{ item.alarmLevel }}</div>
|
||||
<div class="item_title_text">{{ item.alarmDescname }}</div>
|
||||
</div>
|
||||
<div class="item_footer">
|
||||
<div class="item_dept">{{ item.compName }}</div>
|
||||
<div class="item_time">{{ item.alarmTime }}</div>
|
||||
<SmallBorder :titleText="styleConfig.titleText" :title-option="styleConfig.titleOption"
|
||||
:headerOption="styleConfig.headerOption">
|
||||
<div class="content_box">
|
||||
<vue3-seamless-scroll v-if="option.dataset && option.dataset.length > 0" class="seamless" :list="option.dataset"
|
||||
:limitScrollNum="rowNum" :hover="true" :step="waitTime" :wheel="true" :isWatch="true">
|
||||
<div v-for="(item, index) in option.dataset" class="detail flex_column" :key="index">
|
||||
<div class="flex_v cursor" :style="!showRankNum ? { paddingLeft: 0 } : { paddingLeft: '24px' }"
|
||||
@click="handleOpenDialog(item)">
|
||||
<div v-if="index === 0 && showRankNum" class="levelOneIcon flex_c">{{ index + 1 }}</div>
|
||||
<div v-else-if="index === 1 && showRankNum" class="levelTwoIcon flex_c">{{ index + 1 }}</div>
|
||||
<div v-else-if="showRankNum" class="levelOtherIcon flex_c">{{ index + 1 }}</div>
|
||||
<div class="item_content">
|
||||
<div class="item_header flex">
|
||||
<div class="item_level_text">{{ item.alarmLevel }}</div>
|
||||
<div class="item_title_text">{{ item.alarmDescname }}</div>
|
||||
</div>
|
||||
<div class="item_footer">
|
||||
<div class="item_dept">{{ item.compName }}</div>
|
||||
<div class="item_time">{{ item.alarmTime }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</vue3-seamless-scroll>
|
||||
<div v-else style="
|
||||
</vue3-seamless-scroll>
|
||||
<div v-else style="
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
width: 100%;
|
||||
@ -30,109 +32,114 @@
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
">
|
||||
暂无数据
|
||||
</div>
|
||||
<n-modal v-model:show="status.showDialog" :class="['custom-tab-modal']" title="报警详情" preset="card"
|
||||
@on-after-leave="handleCloseDialog" :draggable="{ bounds: 'none' }" :style="{ width: '800px', height: '516px' }">
|
||||
<div v-if="isLoading" style="display: flex; width: 100%; justify-content: center; margin-top: 16px">
|
||||
<n-spin size="large" />
|
||||
暂无数据
|
||||
</div>
|
||||
<n-grid v-else cols="24" x-gap="12" style="overflow-y: auto; padding-bottom: 12px">
|
||||
<!-- <div class="enterBtn" @click="handleMeetingBtnClick">会商</div> -->
|
||||
<!-- 左侧详情 -->
|
||||
<n-gi :span="14">
|
||||
<!-- 基础信息 -->
|
||||
<div class="detail-item">
|
||||
<label>报警时间:</label>
|
||||
<span>{{ convertTimestampToDateTime((status.selectedRow as any)?.alarmRecord.alarmTime) }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>处置状态:</label>
|
||||
<span style="color: #ff9100">{{ (status.selectedRow as any)?.handleStatus }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>风险等级:</label>
|
||||
<span :style="{ color: '' }">{{ (status.selectedRow as any)?.alarmRecord.riskIdentName }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>报警等级:</label>
|
||||
<span class="highlight" style="color: #ff5454">{{
|
||||
(status.selectedRow as any)?.alarmRecord.alarmLevel
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>风险描述:</label>
|
||||
<span class="highlight">{{ (status.selectedRow as any)?.alarmRecord.alarmDesc }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>报警区域:</label>
|
||||
<span>{{
|
||||
(status.selectedRow as any)?.pathName
|
||||
.substring(1, (status.selectedRow as any)?.pathName.length)
|
||||
.replaceAll('/', '-') + (((status.selectedRow as any)?.subareaName && (status.selectedRow as
|
||||
any)?.subareaName !== '') ? (status.selectedRow as any)?.subareaName : '')
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="detail-item" style="display: flex; flex-direction: column"
|
||||
v-if="(status.selectedRow as any)?.camerainfos.length > 0">
|
||||
<label>警情关联设备:</label>
|
||||
<div class="screenshot-placeholder">
|
||||
<n-tabs type="line" animated>
|
||||
<n-tab-pane v-for="(item, index) in (status.selectedRow as any)?.camerainfos" :name="item.deviceCode"
|
||||
:tab="item.deviceName">
|
||||
<div class="detail-item" style="display: flex; flex-direction: column">
|
||||
<label>报警截图:</label>
|
||||
<div style="display: flex">
|
||||
<img :src="'/awimg/' + removeProtocolIpPort((status.selectedRow as any)?.alarmRecord.originImg)"
|
||||
style="width: 180px; margin-right: 16px" />
|
||||
<img :src="'/awimg/' + removeProtocolIpPort((status.selectedRow as any)?.alarmRecord.alarmImg)"
|
||||
style="width: 180px" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-item" style="display: flex; flex-direction: column">
|
||||
<label>警情录像回放:</label>
|
||||
<div class="screenshot-placeholder playback-placeholder" style="width: 350px; height: 200px">
|
||||
<play-back :device-id="item.deviceCode" :start="new Date((status.selectedRow as any)?.startTime)"
|
||||
:end="new Date((status.selectedRow as any)?.endTime)"></play-back>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-item" style="display: flex; flex-direction: column">
|
||||
<label>视频实时画面:</label>
|
||||
<div class="screenshot-placeholder playback-placeholder" style="width: 350px; height: 200px">
|
||||
<play-live :device-id="item.deviceCode"></play-live>
|
||||
</div>
|
||||
</div>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
<br />
|
||||
<n-modal v-model:show="status.showDialog" :class="['custom-tab-modal']" title="报警详情" preset="card"
|
||||
@on-after-leave="handleCloseDialog" :draggable="{ bounds: 'none' }"
|
||||
:style="{ width: '800px', height: '516px' }">
|
||||
<div v-if="isLoading" style="display: flex; width: 100%; justify-content: center; margin-top: 16px">
|
||||
<n-spin size="large" />
|
||||
</div>
|
||||
<n-grid v-else cols="24" x-gap="12" style="overflow-y: auto; padding-bottom: 12px">
|
||||
<!-- <div class="enterBtn" @click="handleMeetingBtnClick">会商</div> -->
|
||||
<!-- 左侧详情 -->
|
||||
<n-gi :span="14">
|
||||
<!-- 基础信息 -->
|
||||
<div class="detail-item">
|
||||
<label>报警时间:</label>
|
||||
<span>{{ convertTimestampToDateTime((status.selectedRow as any)?.alarmRecord.alarmTime) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</n-gi>
|
||||
<n-gi :span="10" style="height: 100%; display: flex; align-items: center">
|
||||
<n-timeline size="medium">
|
||||
<n-timeline-item class="timeLineClass" v-for="(item, index) in (status.selectedRow as any)?.taskLogs"
|
||||
:key="index" type="info" :title="item.task_name" :content="item.task_detail" :time="item.task_begin" />
|
||||
</n-timeline>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
</n-modal>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>处置状态:</label>
|
||||
<span style="color: #ff9100">{{ (status.selectedRow as any)?.handleStatus }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>风险等级:</label>
|
||||
<span :style="{ color: '' }">{{ (status.selectedRow as any)?.alarmRecord.riskIdentName }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>报警等级:</label>
|
||||
<span class="highlight" style="color: #ff5454">{{
|
||||
(status.selectedRow as any)?.alarmRecord.alarmLevel
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>风险描述:</label>
|
||||
<span class="highlight">{{ (status.selectedRow as any)?.alarmRecord.alarmDesc }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>报警区域:</label>
|
||||
<span>{{
|
||||
(status.selectedRow as any)?.pathName
|
||||
.substring(1, (status.selectedRow as any)?.pathName.length)
|
||||
.replaceAll('/', '-') + (((status.selectedRow as any)?.subareaName && (status.selectedRow as
|
||||
any)?.subareaName !== '') ? (status.selectedRow as any)?.subareaName : '')
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="detail-item" style="display: flex; flex-direction: column"
|
||||
v-if="(status.selectedRow as any)?.camerainfos.length > 0">
|
||||
<label>警情关联设备:</label>
|
||||
<div class="screenshot-placeholder">
|
||||
<n-tabs type="line" animated>
|
||||
<n-tab-pane v-for="(item, index) in (status.selectedRow as any)?.camerainfos" :name="item.deviceCode"
|
||||
:tab="item.deviceName">
|
||||
<div class="detail-item" style="display: flex; flex-direction: column">
|
||||
<label>报警截图:</label>
|
||||
<div style="display: flex">
|
||||
<img :src="'/awimg/' + removeProtocolIpPort((status.selectedRow as any)?.alarmRecord.originImg)"
|
||||
style="width: 180px; margin-right: 16px" />
|
||||
<img :src="'/awimg/' + removeProtocolIpPort((status.selectedRow as any)?.alarmRecord.alarmImg)"
|
||||
style="width: 180px" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-item" style="display: flex; flex-direction: column">
|
||||
<label>警情录像回放:</label>
|
||||
<div class="screenshot-placeholder playback-placeholder" style="width: 350px; height: 200px">
|
||||
<play-back :device-id="item.deviceCode"
|
||||
:start="new Date((status.selectedRow as any)?.startTime)"
|
||||
:end="new Date((status.selectedRow as any)?.endTime)"></play-back>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-item" style="display: flex; flex-direction: column">
|
||||
<label>视频实时画面:</label>
|
||||
<div class="screenshot-placeholder playback-placeholder" style="width: 350px; height: 200px">
|
||||
<play-live :device-id="item.deviceCode"></play-live>
|
||||
</div>
|
||||
</div>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
</n-gi>
|
||||
<n-gi :span="10" style="height: 100%; display: flex; align-items: center">
|
||||
<n-timeline size="medium">
|
||||
<n-timeline-item class="timeLineClass" v-for="(item, index) in (status.selectedRow as any)?.taskLogs"
|
||||
:key="index" type="info" :title="item.task_name" :content="item.task_detail" :time="item.task_begin" />
|
||||
</n-timeline>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
</n-modal>
|
||||
</div>
|
||||
</SmallBorder>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// @ts-nocheck
|
||||
|
||||
import { PropType, toRefs, shallowReactive, watch, ref, reactive, onMounted, onUnmounted } from 'vue'
|
||||
import { PropType, toRefs, shallowReactive, watch, computed, ref, reactive, onMounted, onUnmounted } from 'vue'
|
||||
import { CreateComponentType } from '@/packages/index.d'
|
||||
import { useChartDataFetch } from '@/hooks'
|
||||
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
|
||||
import { option as configOption } from './config' // 移除此行,不再从config导入初始数据
|
||||
import { option as configOption, styleConfig } from './config'
|
||||
import { Vue3SeamlessScroll } from 'vue3-seamless-scroll'
|
||||
import PlayBack from '@/components/Pages/yushiVideo/playback.vue'
|
||||
import PlayLive from '@/components/Pages/yushiVideo/playLive.vue'
|
||||
import axiosInstance from '@/api/axios'
|
||||
import VChart from 'vue-echarts'
|
||||
|
||||
import SmallBorder from '../SmallBorder01Co/index.vue'
|
||||
import { height } from 'dom-helpers'
|
||||
import axios from 'axios'
|
||||
const props = defineProps({
|
||||
chartConfig: {
|
||||
type: Object as PropType<CreateComponentType & typeof option>,
|
||||
@ -244,19 +251,19 @@ const handleCloseDialog = () => {
|
||||
const fetchRecentAlarms = async () => {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
const res = await axios.get(
|
||||
`/dev/space/getRecentAlarms`, // 手动添加 dev
|
||||
{
|
||||
responseType: 'json',
|
||||
}
|
||||
);
|
||||
|
||||
const res = await axiosInstance({
|
||||
method: 'GET',
|
||||
url: `/space/getRecentAlarms`,
|
||||
responseType: 'json'
|
||||
});
|
||||
|
||||
if (res && res.value) {
|
||||
if (res.data.state === true) {
|
||||
let rawData = [];
|
||||
if (Array.isArray(res.value.items)) {
|
||||
rawData = res.value.items;
|
||||
} else if (Array.isArray(res.value)) {
|
||||
rawData = res.value;
|
||||
if (Array.isArray(res.data.value.items)) {
|
||||
rawData = res.data.value.items;
|
||||
} else if (Array.isArray(res.data.value)) {
|
||||
rawData = res.data.value;
|
||||
}
|
||||
|
||||
const formattedData = rawData.map(item => {
|
||||
@ -303,13 +310,16 @@ onUnmounted(() => {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content_box {
|
||||
width: v-bind('w') + 'px';
|
||||
height: v-bind('h') + 'px';
|
||||
overflow: hidden;
|
||||
z-index: -1;
|
||||
|
||||
}
|
||||
|
||||
.seamless {
|
||||
// padding: 0 30px 14px 30px;
|
||||
z-index: 1;
|
||||
height: 240px;
|
||||
margin: 20px 10px 30px 10px;
|
||||
overflow: hidden;
|
||||
|
||||
}
|
||||
|
||||
.detail {
|
||||
@ -336,9 +346,9 @@ onUnmounted(() => {
|
||||
.flex_v {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: #072230;
|
||||
// background-color: #0a0c1260;
|
||||
border-bottom: 1.5px solid #123E54;
|
||||
|
||||
margin: 3px;
|
||||
}
|
||||
|
||||
.item_level_text {
|
||||
|
@ -43,7 +43,7 @@ const otherConfig = {
|
||||
}
|
||||
]
|
||||
},
|
||||
titleText: '今日数据',
|
||||
titleText: '报警统计',
|
||||
titleOption: {
|
||||
color: '#ffffff',
|
||||
fontSize: '17px',
|
||||
@ -60,7 +60,6 @@ const otherConfig = {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// ECharts配置
|
||||
export const option = {
|
||||
...otherConfig,
|
||||
|
@ -1,9 +1,7 @@
|
||||
{
|
||||
"dimensions": [
|
||||
"alarmLevel",
|
||||
"count",
|
||||
"itemColor",
|
||||
"borderColor"
|
||||
"count"
|
||||
],
|
||||
"source": [
|
||||
{
|
||||
|
@ -30,7 +30,7 @@ import {
|
||||
TitleComponent, GraphicComponent
|
||||
} from 'echarts/components'
|
||||
|
||||
import axiosInstance from '@/api/axios'
|
||||
import axios from 'axios'
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
@ -180,24 +180,26 @@ function generateTimeRange(period: string): { startTime: string; endTime: string
|
||||
}
|
||||
const fetchAlarmData = async ({ startTime, endTime }: { startTime: string; endTime: string }) => {
|
||||
try {
|
||||
const res = await axiosInstance({
|
||||
method: 'GET',
|
||||
url: `/space/getNumberByAlarmLevel`,
|
||||
params: { startTime, endTime },
|
||||
responseType: 'json',
|
||||
});
|
||||
if (res && (res as any).value && Array.isArray((res as any).value)) {
|
||||
const res = await axios.get(
|
||||
`/dev/space/getNumberByAlarmLevel`, // 手动添加 dev
|
||||
{
|
||||
params: { startTime, endTime }, // 请求参数(对应原 params)
|
||||
responseType: 'json',
|
||||
}
|
||||
);
|
||||
if (res.data.state === true) {
|
||||
console.log(res.data)
|
||||
return {
|
||||
dimensions: props.chartConfig.option.dataset.dimensions, // 保持原有的维度配置
|
||||
source: (res as any).value,
|
||||
dimensions: props.chartConfig.option.dataset.dimensions,
|
||||
source: res.data.value,
|
||||
};
|
||||
} else {
|
||||
console.warn('API returned unexpected data structure:', res);
|
||||
return undefined;
|
||||
console.error('API调用失败:', res.data)
|
||||
return {}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching alarm data:', error);
|
||||
return undefined; // 返回 undefined 表示获取失败
|
||||
console.error('获取图表数据失败:', error)
|
||||
return {}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -213,10 +213,10 @@
|
||||
<!-- ... 省略以上 -->
|
||||
<div class="content">
|
||||
<div class="header" :style="{
|
||||
paddingLeft: (props.headerOption?.paddingLeft ?? '0') + 'px',
|
||||
paddingTop: (props.headerOption?.paddingTop ?? '0') + 'px',
|
||||
paddingRight: (props.headerOption?.paddingRight ?? '0') + 'px',
|
||||
paddingBottom: (props.headerOption?.paddingBottom ?? '0') + 'px'
|
||||
marginLeft: (props.headerOption?.paddingLeft ?? '0') + 'px',
|
||||
marginTop: (props.headerOption?.paddingTop ?? '0') + 'px',
|
||||
marginRight: (props.headerOption?.paddingRight ?? '0') + 'px',
|
||||
marginBottom: (props.headerOption?.paddingBottom ?? '0') + 'px'
|
||||
}">
|
||||
<span class="title" :style="props.titleOption">
|
||||
{{ props.titleText }}
|
||||
@ -224,7 +224,7 @@
|
||||
<span class="right-select">
|
||||
<ConsumSelect v-show="selectOption.show" :options="selectOption.dataset"
|
||||
:selectedValue="selectOption.selectValue" @change="handleSelectChange"
|
||||
:select-style-config="selectStyleConfig" />
|
||||
select-style-config="selectStyleConfig" />
|
||||
</span>
|
||||
</div>
|
||||
<div class="body">
|
||||
@ -302,7 +302,7 @@ const handleSelectChange = (value: any) => {
|
||||
height: 100%;
|
||||
// --- 定义变量 ---
|
||||
$border-outside-padding: 5px; // 外部容器的内边距
|
||||
$bg-top-offset: 0px; // 背景SVG的top偏移量
|
||||
$bg-top-offset: -10px; // 背景SVG的top偏移量
|
||||
padding: $border-outside-padding;
|
||||
box-sizing: border-box;
|
||||
|
||||
@ -319,6 +319,7 @@ const handleSelectChange = (value: any) => {
|
||||
|
||||
// background-color: rgba(255, 0, 0, 0.2);
|
||||
.svg-border {
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
@ -334,7 +335,7 @@ const handleSelectChange = (value: any) => {
|
||||
width: 100%;
|
||||
// 高度 = 100% - 背景的 top 偏移量
|
||||
height: calc(100% - #{$bg-top-offset});
|
||||
object-fit: scale-down;
|
||||
object-fit: fill;
|
||||
z-index: -2;
|
||||
|
||||
}
|
||||
@ -352,25 +353,37 @@ const handleSelectChange = (value: any) => {
|
||||
// background-color: antiquewhite;
|
||||
|
||||
.header {
|
||||
margin-top: 5px;
|
||||
// margin-top: 8px;
|
||||
margin-bottom: 0px;
|
||||
padding: 0 20px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-left: 25px;
|
||||
padding-top: 5px;
|
||||
// background-color: aliceblue;
|
||||
|
||||
.title {
|
||||
font-family: 'CustomFont';
|
||||
letter-spacing: 1px;
|
||||
color: #ffffff;
|
||||
font-size: 17px;
|
||||
padding-left: 45px;
|
||||
}
|
||||
}
|
||||
|
||||
.body {
|
||||
// padding-top: 0px;
|
||||
// margin-top: 30px;
|
||||
// background-color: aquamarine;
|
||||
// display: flex;
|
||||
// align-content: center;
|
||||
// justify-content: center;
|
||||
margin-top: 0px;
|
||||
flex: 1;
|
||||
// flex: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
// background-color: lightblue;
|
||||
|
@ -3,5 +3,6 @@ import { PieCircleCommenConfig } from './PieCircleCommen'
|
||||
import { AlarmNowListConfig } from './AlarmNowList'
|
||||
import { LineDropdownConfig } from './LineDropdown/index'
|
||||
import { SmallBorder01CoConfig } from './SmallBorder01Co'
|
||||
export default [MapConfig, LineDropdownConfig, PieCircleCommenConfig, AlarmNowListConfig, SmallBorder01CoConfig]
|
||||
import { yushiVideoConfig } from './yushiVideo'
|
||||
export default [MapConfig, yushiVideoConfig, LineDropdownConfig, PieCircleCommenConfig, AlarmNowListConfig, SmallBorder01CoConfig]
|
||||
|
||||
|
@ -59,8 +59,7 @@
|
||||
import { PropType, computed, ref, onMounted, onUnmounted } from 'vue'
|
||||
import { option as configOption } from './config'
|
||||
import SmallBorder from '../SmallBorder/index.vue'
|
||||
import axiosInstance from '@/api/axios'
|
||||
|
||||
import axios from 'axios'
|
||||
const props = defineProps({
|
||||
chartConfig: {
|
||||
type: Object as PropType<{ option: typeof configOption }>,
|
||||
@ -88,19 +87,20 @@ const convertTimestampToDateTime = (timestamp: number | string) => {
|
||||
|
||||
const fetchRecentAlarms = async () => {
|
||||
try {
|
||||
const res = await axios.get(
|
||||
`/dev/space/getRecentAlarms`, // 手动添加 dev
|
||||
{
|
||||
responseType: 'json',
|
||||
}
|
||||
);
|
||||
|
||||
const res: any = await axiosInstance({
|
||||
method: 'GET',
|
||||
url: `/space/getRecentAlarms`,
|
||||
responseType: 'json'
|
||||
});
|
||||
|
||||
if (res && res?.value) {
|
||||
if (res.data.state === true) {
|
||||
let rawData = [];
|
||||
if (Array.isArray(res.value.items)) {
|
||||
rawData = res.value.items;
|
||||
} else if (Array.isArray(res.value)) {
|
||||
rawData = res.value;
|
||||
if (Array.isArray(res.data.value.items)) {
|
||||
rawData = res.data.value.items;
|
||||
} else if (Array.isArray(res.data.value)) {
|
||||
rawData = res.data.value;
|
||||
}
|
||||
|
||||
const formattedData = rawData.map((item: any) => {
|
||||
|
@ -24,7 +24,7 @@ import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore
|
||||
import { DatasetComponent, GridComponent, TooltipComponent, LegendComponent, GraphicComponent } from 'echarts/components'
|
||||
import CustomSelect from './select.vue'
|
||||
import dataJson from './data.json'
|
||||
import axiosInstance from '@/api/axios';
|
||||
import axios from 'axios'
|
||||
|
||||
const props = defineProps({
|
||||
themeSetting: {
|
||||
@ -61,24 +61,27 @@ function calculatePercentage(value: number, total: number) {
|
||||
}
|
||||
|
||||
const updateChartData = (newData: any) => {
|
||||
if (!newData) return
|
||||
if (!newData) {
|
||||
// 如果没有新数据,则设置为空数据集并更新图表
|
||||
props.chartConfig.option.dataset = { dimensions: ['name', 'value'], source: [] };
|
||||
} else {
|
||||
props.chartConfig.option.dataset = newData;
|
||||
}
|
||||
|
||||
// 确保数据被正确设置到图表配置中
|
||||
props.chartConfig.option.dataset = newData
|
||||
|
||||
const total = calculateTotal(newData)
|
||||
const total = calculateTotal(props.chartConfig.option.dataset)
|
||||
if (props.chartConfig.option.graphic?.[0]) {
|
||||
props.chartConfig.option.graphic[0].style.text = total.toString()
|
||||
}
|
||||
|
||||
// 统一显示为 "名称 百分比"
|
||||
props.chartConfig.option.legend.formatter = (name: string) => {
|
||||
const item = newData.source.find((it: any) => it.name === name)
|
||||
const item = props.chartConfig.option.dataset.source.find((it: any) => it.name === name)
|
||||
if (!item) return name
|
||||
const val = item.value ?? 0
|
||||
let p = calculatePercentage(val, total)
|
||||
if(Number(p)<10){
|
||||
p=' '+p
|
||||
if (Number(p) < 10) {
|
||||
p = ' ' + p
|
||||
}
|
||||
return `{name|${name}}{value|${p}}{unit|%}`;
|
||||
}
|
||||
@ -143,12 +146,13 @@ const updateChartData = (newData: any) => {
|
||||
}
|
||||
}
|
||||
|
||||
const totalValue = newData.source.reduce((total: number, item: any) => {
|
||||
const totalValue = props.chartConfig.option.dataset.source.reduce((total: number, item: any) => {
|
||||
return total + (item.value || 0)
|
||||
}, 0)
|
||||
props.chartConfig.option.series[props.chartConfig.option.series.length-1].label.formatter = `{a|${totalValue}}\n\n{b|总数}`
|
||||
props.chartConfig.option.series[props.chartConfig.option.series.length - 1].label.formatter = `{a|${totalValue}}\n\n{b|总数}`
|
||||
}
|
||||
|
||||
// watch 函数在数据源变化时更新图表,这里保持不变
|
||||
watch(
|
||||
() => props.chartConfig.option.dataset,
|
||||
newData => {
|
||||
@ -163,18 +167,10 @@ const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore, (n
|
||||
updateChartData(newData)
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
// 初始化时立即获取数据
|
||||
const timeRange = generateTimeRange(props.chartConfig.option.dateTime.selectValue);
|
||||
const fetchedData = await fetchAlarmData(timeRange);
|
||||
if (fetchedData) {
|
||||
updateChartData(fetchedData);
|
||||
} else {
|
||||
// 如果获取失败,使用默认数据
|
||||
updateChartData(props.chartConfig.option.dataset);
|
||||
}
|
||||
});
|
||||
// 处理下拉选择器变化
|
||||
/**
|
||||
* 格式化日期时间为 ISO 8601 字符串 (YYYY-MM-DDTHH:mm:ss.SSS)
|
||||
* JavaScript Date 对象只支持毫秒精度
|
||||
*/
|
||||
function formatDateTime(date: Date): string {
|
||||
const year = date.getFullYear();
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
||||
@ -182,94 +178,108 @@ function formatDateTime(date: Date): string {
|
||||
const hours = date.getHours().toString().padStart(2, '0');
|
||||
const minutes = date.getMinutes().toString().padStart(2, '0');
|
||||
const seconds = date.getSeconds().toString().padStart(2, '0');
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
const milliseconds = date.getMilliseconds().toString().padStart(3, '0'); // 添加毫秒
|
||||
|
||||
return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}.${milliseconds}`; // 使用 'T' 分隔符和毫秒
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据时间段生成开始和结束时间范围
|
||||
* 结束时间设置为该时间段的最后一天的 23:59:59.999
|
||||
*/
|
||||
function generateTimeRange(period: string): { startTime: string; endTime: string } {
|
||||
const now = new Date();
|
||||
let startTime: Date;
|
||||
let endTime: Date;
|
||||
endTime = new Date(now);
|
||||
endTime.setDate(endTime.getDate() + 1);
|
||||
endTime.setHours(0, 0, 0, 0);
|
||||
const now = new Date(); // 当前时间
|
||||
|
||||
let startTimeDate: Date;
|
||||
let endTimeDate: Date;
|
||||
|
||||
switch (period) {
|
||||
case 'day':
|
||||
startTime = new Date(now);
|
||||
startTime.setHours(0, 0, 0, 0);
|
||||
startTimeDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0); // 当天 00:00:00.000
|
||||
endTimeDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999); // 当天 23:59:59.999
|
||||
break;
|
||||
case 'week':
|
||||
startTime = new Date(now);
|
||||
startTime.setDate(startTime.getDate() - 7);
|
||||
startTime.setHours(0, 0, 0, 0);
|
||||
// 计算过去7天(包括今天)的起始时间
|
||||
startTimeDate = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 6, 0, 0, 0, 0);
|
||||
// 结束时间为今天 23:59:59.999
|
||||
endTimeDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999);
|
||||
break;
|
||||
case 'month':
|
||||
startTime = new Date(now.getFullYear(), now.getMonth(), 1);
|
||||
startTime.setHours(0, 0, 0, 0);
|
||||
startTimeDate = new Date(now.getFullYear(), now.getMonth(), 1, 0, 0, 0, 0); // 当月第一天 00:00:00.000
|
||||
// 获取当月最后一天 23:59:59.999
|
||||
endTimeDate = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59, 999);
|
||||
break;
|
||||
case 'quarter':
|
||||
const currentMonth = now.getMonth();
|
||||
const quarterStartMonth = Math.floor(currentMonth / 3) * 3;
|
||||
startTime = new Date(now.getFullYear(), quarterStartMonth, 1);
|
||||
startTime.setHours(0, 0, 0, 0);
|
||||
startTimeDate = new Date(now.getFullYear(), quarterStartMonth, 1, 0, 0, 0, 0); // 当季度第一天 00:00:00.000
|
||||
// 获取当季度最后一天 23:59:59.999
|
||||
endTimeDate = new Date(now.getFullYear(), quarterStartMonth + 3, 0, 23, 59, 59, 999);
|
||||
break;
|
||||
case 'year':
|
||||
startTime = new Date(now.getFullYear(), 0, 1);
|
||||
startTime.setHours(0, 0, 0, 0);
|
||||
startTimeDate = new Date(now.getFullYear(), 0, 1, 0, 0, 0, 0); // 当年第一天 00:00:00.000
|
||||
// 获取当年最后一天 23:59:59.999 (即下一年第一天的前一毫秒)
|
||||
endTimeDate = new Date(now.getFullYear(), 12, 0, 23, 59, 59, 999);
|
||||
break;
|
||||
default:
|
||||
startTime = new Date(now);
|
||||
startTime.setHours(0, 0, 0, 0);
|
||||
default: // 默认按 'day' 处理
|
||||
startTimeDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0);
|
||||
endTimeDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999);
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
startTime: formatDateTime(startTime),
|
||||
endTime: formatDateTime(endTime),
|
||||
startTime: formatDateTime(startTimeDate),
|
||||
endTime: formatDateTime(endTimeDate),
|
||||
};
|
||||
}
|
||||
|
||||
// 异步获取报警数据
|
||||
async function fetchAlarmData({ startTime, endTime }: { startTime: string; endTime: string }) {
|
||||
try {
|
||||
const res = await axiosInstance({
|
||||
method: 'GET',
|
||||
url: '/getAlarmdataRecord',
|
||||
params: { startTime, endTime },
|
||||
responseType: 'json',
|
||||
});
|
||||
if (Array.isArray(res)) {
|
||||
const res = await axios.get(
|
||||
`/dev/getAlarmdataRecord`,
|
||||
{
|
||||
params: { startTime, endTime }, // 请求参数
|
||||
responseType: 'json',
|
||||
}
|
||||
);
|
||||
|
||||
if (res.status === 200 && res.data) {
|
||||
console.log("API 响应数据:", res.data);
|
||||
// 转换为图表需要的数据结构
|
||||
return {
|
||||
dimensions: ['name', 'value'],
|
||||
source: res.map((item: any) => ({
|
||||
source: res.data.map((item: any) => ({
|
||||
name: item.alarmLevel,
|
||||
value: item.alarmNun
|
||||
}))
|
||||
};
|
||||
} else {
|
||||
console.warn('API returned unexpected data structure:', res);
|
||||
console.warn('API 返回非预期数据结构或空数据:', res);
|
||||
return undefined;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching alarm data:', error);
|
||||
console.error('获取报警数据失败:', error);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
async function dataHandle(newData: any) {
|
||||
if (!newData) {
|
||||
props.chartConfig.option.dataset = { dimensions: ['name', 'value'], source: [] };
|
||||
updateChartData(props.chartConfig.option.dataset);
|
||||
return;
|
||||
}
|
||||
updateChartData(newData);
|
||||
}
|
||||
const handleSelectChange = async (value: string) => {
|
||||
props.chartConfig.option.dateTime.selectValue = value;
|
||||
const { startTime, endTime } = generateTimeRange(value);
|
||||
const fetchedData = await fetchAlarmData({ startTime, endTime });
|
||||
await dataHandle(fetchedData);
|
||||
};
|
||||
|
||||
// 组件挂载时初始化数据
|
||||
onMounted(async () => {
|
||||
const timeRange = generateTimeRange(props.chartConfig.option.dateTime.selectValue);
|
||||
console.log("onMounted - 请求参数:", timeRange); // 打印初始请求参数
|
||||
const fetchedData = await fetchAlarmData(timeRange);
|
||||
await dataHandle(fetchedData);
|
||||
updateChartData(fetchedData); // 直接调用 updateChartData
|
||||
});
|
||||
|
||||
// 处理下拉选择器变化
|
||||
const handleSelectChange = async (value: string) => {
|
||||
props.chartConfig.option.dateTime.selectValue = value; // 更新选中值
|
||||
const timeRange = generateTimeRange(value);
|
||||
console.log("handleSelectChange - 请求参数:", timeRange); // 打印新的请求参数
|
||||
const fetchedData = await fetchAlarmData(timeRange);
|
||||
updateChartData(fetchedData); // 直接调用 updateChartData
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
Loading…
Reference in New Issue
Block a user