feat: 新增站点统计组件

This commit is contained in:
huanghao1412 2024-05-21 11:20:31 +08:00
parent 02366f3a6c
commit bd916dacf5
8 changed files with 298 additions and 1 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -32,7 +32,7 @@ let url = computed(() => {
// const account = originStore?.getOriginStore?.user?.user?.account
const account = 'admin'
const password = 'laimi@123'
let origin = process.env.NODE_ENV === 'production' ? window.location.origin : 'http://192.168.0.61:9528'
let origin = process.env.NODE_ENV === 'production' ? window.location.origin : 'http://localhost:9528'
// let str = `${origin}/static/#/dynamicRing/schematicDiagram/${props.chartConfig.customData.mapId}?parentOrigin=${window.location.origin}&isScreenIframe=true&account=${account}&password=${password}`
let str = `${origin}/static/#/dynamicRing/schematicDiagram/${props.chartConfig.customData.mapId}?parentOrigin=${window.location.origin}&isScreenIframe=true&access_token=${routerStore.token}`
return str

View File

@ -0,0 +1,24 @@
import { PublicConfigClass } from '@/packages/public'
import { CreateComponentType } from '@/packages/index.d'
import { SiteStatisticsConfig } from './index'
import cloneDeep from 'lodash/cloneDeep'
export const option = {
}
export default class Config extends PublicConfigClass implements CreateComponentType
{
constructor() {
super();
this.attr.w = 150
this.attr.h = 150
this.request.requestInterval = 15
}
public key = SiteStatisticsConfig.key
public chartConfig = cloneDeep(SiteStatisticsConfig)
public option = cloneDeep(option)
public customData = cloneDeep({
showInterval: true,
mapId: null
})
}

View File

@ -0,0 +1,44 @@
<template>
</template>
<script setup lang="ts">
import { PropType } from 'vue'
import { option } from './config'
import {
CollapseItem,
SettingItemBox,
SettingItem,
} from '@/components/Pages/ChartItemSetting'
const props = defineProps({
optionData: {
type: Object as PropType<typeof option>,
required: true,
},
})
//
const fitList = [
{
value: 'fill',
label: 'fill'
},
{
value: 'contain',
label: 'contain'
},
{
value: 'cover',
label: 'cover'
},
{
value: 'scale-down',
label: 'scale-down'
},
{
value: 'none',
label: 'none'
},
]
</script>

View File

@ -0,0 +1,16 @@
<template>
<n-space vertical>
<setting-item-box name="地图ID" :alone="true">
<n-input-number v-model:value="props.customData.mapId" size="small" placeholder="请输入地图ID"/>
</setting-item-box>
</n-space>
</template>
<script lang="ts" setup>
import { SettingItemBox } from '@/components/Pages/ChartItemSetting'
const props = defineProps(['customData', 'request'])
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,16 @@
import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d'
import { ChatCategoryEnum, ChatCategoryEnumName } from '@/packages/components/CustomComponents/index.d'
export const SiteStatisticsConfig: ConfigType = {
key: 'SiteStatistics',
chartKey: 'VSiteStatistics',
conKey: 'VCSiteStatistics',
// VCD开头
conDataKey: 'VCDSiteStatistics',
title: '站点统计',
category: ChatCategoryEnum.CUSTOMCOMPONENTS,
categoryName: ChatCategoryEnumName.CUSTOMCOMPONENTS,
package: PackagesCategoryEnum.CUSTOMCOMPONENTS,
chartFrame: ChartFrameEnum.COMMON,
image: 'SiteStatistics.png'
}

View File

@ -0,0 +1,195 @@
<template>
<div v-if="chartConfig.customData?.mapId" :style="getStyle(borderRadius)" class="box" style="overflow: visible;position: relative">
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 120 141.16"
style="width: 100%; height: 100%;"
>
<defs>
<linearGradient id="statistics_76" y1="862.16" x2="120" y2="862.16" gradientTransform="matrix(1, 0, 0, -1, 0, 890.33)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="aqua" stop-opacity="0.5" />
<stop offset="1" stop-color="aqua" stop-opacity="0.1" />
</linearGradient>
<linearGradient id="statistics_77" y1="818.16" x2="120" y2="818.16" gradientTransform="matrix(1, 0, 0, -1, 0, 890.33)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#4dca59" stop-opacity="0.5" />
<stop offset="1" stop-color="#4dca59" stop-opacity="0.1" />
</linearGradient>
<linearGradient id="statistics_78" y1="774.16" x2="120" y2="774.16" gradientTransform="matrix(1, 0, 0, -1, 0, 890.33)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#f43b42" stop-opacity="0.5" />
<stop offset="1" stop-color="#f43b42" stop-opacity="0.1" />
</linearGradient>
<linearGradient id="statistics_79" y1="730.16" x2="120" y2="730.16" gradientTransform="matrix(1, 0, 0, -1, 0, 890.33)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#999" stop-opacity="0.5" />
<stop offset="1" stop-color="#999" stop-opacity="0.1" />
</linearGradient>
</defs>
<g>
<rect y="27.16" width="120" height="2" style="fill:url(#statistics_76)" />
<rect y="27.16" width="14" height="2" style="fill:aqua" />
<g id="正常点-22">
<path d="M7,3.16a6,6,0,1,1-6,6,6,6,0,0,1,6-6m0-1a7,7,0,1,0,7,7A7,7,0,0,0,7,2.16Z" style="fill:aqua" />
<circle cx="7" cy="9.16" r="3" style="fill:aqua" />
</g>
<!-- <text transform="translate(24 14.16)" style="isolation:isolate;font-size:12px;fill:#ccc;font-family:AdobeSongStd-Light-GBpc-EUC-H, Adobe Song Std">全部站点</text> -->
<text transform="translate(24 14.16)" style="isolation:isolate;font-size:12px;fill:#ccc">全部站点</text>
<text transform="translate(95.48 17.16)" style="isolation:isolate;font-size:20px;fill:aqua;font-family:ArialMT, Arial">{{ allNumber }}</text>
<rect y="33.16" width="120" height="40" style="fill:#4dca59;fill-opacity:0" />
<rect y="71.16" width="120" height="2" style="fill:url(#statistics_77)" />
<rect y="71.16" width="14" height="2" style="fill:#4dca59" />
<g id="正常点-23">
<path d="M7,47.16a6,6,0,1,1-6,6,6,6,0,0,1,6-6m0-1a7,7,0,1,0,7,7A7,7,0,0,0,7,46.16Z" style="fill:#4dca59" />
<circle cx="7" cy="53.16" r="3" style="fill:#4dca59" />
</g>
<text transform="translate(24 58.16)" style="isolation:isolate;font-size:12px;fill:#ccc">正常站点</text>
<text transform="translate(95.48 61.16)" style="isolation:isolate;font-size:20px;fill:#4dca59;font-family:ArialMT, Arial">{{ normalNumber }}</text>
<rect y="77.16" width="120" height="40" style="fill:#f43b42;fill-opacity:0" />
<rect y="115.16" width="120" height="2" style="fill:url(#statistics_78)" />
<rect y="115.16" width="14" height="2" style="fill:#f43b42" />
<g id="正常点-24">
<path d="M7,91.16a6,6,0,1,1-6,6,6,6,0,0,1,6-6m0-1a7,7,0,1,0,7,7A7,7,0,0,0,7,90.16Z" style="fill:#f43b42" />
<circle cx="7" cy="97.16" r="3" style="fill:#f43b42" />
</g>
<text transform="translate(24 102.16)" style="isolation:isolate;font-size:12px;fill:#ccc">告警站点</text>
<text transform="translate(95.48 105.16)" style="isolation:isolate;font-size:20px;fill:#f43b42;font-family:ArialMT, Arial">{{ abnormalNumber }}</text>
</g>
</svg>
</div>
<div style="display: flex;flex-direction: column;align-items: center;justify-content: center;" v-else>
<img src="@/assets/images/exception/nodata.svg" style="width: 100%;height: 30%" alt="">
<div style="color: #fff;text-align: center;font-size: 16px;">请输入地图ID</div>
</div>
</template>
<script setup lang="ts">
import { PropType, shallowReactive, watch, toRefs, reactive, onMounted, onUnmounted, nextTick, ref, computed } from 'vue'
import type { Ref } from 'vue'
import { CreateComponentType } from '@/packages/index.d'
import { publicInterface } from '@/api/path/business.api'
import { isPreview } from '@/utils'
import {selectTimeOptions} from "@/views/chart/ContentConfigurations/components/ChartData/index.d";
import {RequestHttpIntervalEnum} from "@/enums/httpEnum";
import { useOriginStore } from '@/store/modules/originStore/originStore'
import moment from 'moment'
import { debounce } from "lodash";
import { postMessageToParent } from '@/utils/utils'
const props = defineProps({
chartConfig: {
type: Object as PropType<CreateComponentType>,
required: true
}
})
// Object.assign(props.chartConfig.attr, { w: 870, h: 560 })
const { w, h } = toRefs(props.chartConfig.attr)
const { dataset, fit, borderRadius } = toRefs(props.chartConfig.option)
const getStyle = (radius: number) => {
return {
borderRadius: `${radius}px`,
overflow: 'hidden'
}
}
const originStore = useOriginStore()
const systemConfig = originStore.getOriginStore.user.systemConfig
let text: { style: { [k: string]: any }, propValue: string }[] = reactive([])
let point: { color: string, node_status: number, style: { [k: string]: any } }[] = reactive([])
const allNumber = computed(() => point.length)
const normalNumber = computed(() => point.filter(_ => _.color !== '#f00').length)
const abnormalNumber = computed(() => point.filter(_ => _.color === '#f00').length)
const getData = () => {
if(!props.chartConfig.customData!.mapId) return
publicInterface('/dcim/space_page', 'get_one_no_permission', { id: props.chartConfig.customData!.mapId }).then(res => {
if(res && res.data){
const arr:[] = JSON.parse(res.data.canvas_data)
text.splice(0, text.length, ...arr.filter((_:any) => _.component === 'v-text'))
point.splice(0, point.length, ...arr.filter((_:any) => _.component === 'svg-shape'))
const activeAlarmData = {
levels: [],
confirm_statuses: []
}
if (systemConfig.active_alarm_confirm_status) {
activeAlarmData.confirm_statuses.splice(0, activeAlarmData.confirm_statuses.length, ...JSON.parse(systemConfig.active_alarm_confirm_status) as [])
}
if (systemConfig['active_alarm_level']) {
for (let i = 0; i < Number(systemConfig['active_alarm_level']); i++) {
(activeAlarmData.levels as number[]).push(i + 1)
}
}
const param = {
ids: point.filter((_:any) => _.dataBind.monitorIds.length).map((_:any) => Number(_.dataBind.monitorIds[0])),
...activeAlarmData,
}
const idMap:{[k:string]: number} = {}
param.ids.forEach((item, i) => {
idMap[item] = i
})
publicInterface('/dcim/dems/device', 'get_space_tree_with_status_v3', param).then(res => {
if(res && res.data) {
res.data.forEach((item:any) => {
if(item.node_status !== 0) {
point[idMap[item.id]].color = '#f00'
}
})
}
})
}
})
}
let timer:unknown
watch(() => [props.chartConfig.request.requestInterval, props.chartConfig.request.requestIntervalUnit].join('&&'), v => {
if(!isPreview()) return
if(props.chartConfig.request.requestInterval) {
if(timer) clearInterval(timer as number)
const obj = selectTimeOptions.find(_ => _.value === props.chartConfig.request.requestIntervalUnit) || {unit: 0}
const unit = obj.unit
const number = unit * props.chartConfig.request.requestInterval
timer = setInterval(() => {
getData()
}, number)
}
})
const fn = debounce(() => {
getData()
}, 1000)
watch(() => props.chartConfig.customData!.mapId, fn)
onMounted(() => {
nextTick(() => {
getData()
})
if(!isPreview()) return
const obj = selectTimeOptions.find(_ => _.value === props.chartConfig.request.requestIntervalUnit) || {unit: 0}
const unit = obj.unit
const number = unit * props.chartConfig.request.requestInterval!
timer = setInterval(() => {
nextTick(() => {
getData()
})
}, number)
})
onUnmounted(() => {
clearInterval(timer as number)
})
// const option = shallowReactive({
// dataset: ''
// })
// //
// useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => {
// option.dataset = newData
// })
</script>
<style lang="scss" scoped>
.box{
position: relative;
}
</style>

View File

@ -10,6 +10,7 @@ import { DashboardConfig } from './Dashboard'
import { SystemRuntimeConfig } from './SystemRuntime'
import { VideoListConfig } from './VideoList'
import { AirConditioningTableConfig } from './AirConditioningTable'
import { SiteStatisticsConfig } from './SiteStatistics'
export default [
// Theme1Config,
@ -24,4 +25,5 @@ export default [
SystemRuntimeConfig,
VideoListConfig,
AirConditioningTableConfig,
SiteStatisticsConfig,
]