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