mirror of
https://gitee.com/dromara/go-view.git
synced 2025-10-13 22:12:11 +08:00
feat: 新增站点统计组件
This commit is contained in:
parent
02366f3a6c
commit
bd916dacf5
BIN
src/assets/images/chart/customponents/SiteStatistics.png
Normal file
BIN
src/assets/images/chart/customponents/SiteStatistics.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
@ -32,7 +32,7 @@ let url = computed(() => {
|
|||||||
// const account = originStore?.getOriginStore?.user?.user?.account
|
// const account = originStore?.getOriginStore?.user?.user?.account
|
||||||
const account = 'admin'
|
const account = 'admin'
|
||||||
const password = 'laimi@123'
|
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&account=${account}&password=${password}`
|
||||||
let str = `${origin}/static/#/dynamicRing/schematicDiagram/${props.chartConfig.customData.mapId}?parentOrigin=${window.location.origin}&isScreenIframe=true&access_token=${routerStore.token}`
|
let str = `${origin}/static/#/dynamicRing/schematicDiagram/${props.chartConfig.customData.mapId}?parentOrigin=${window.location.origin}&isScreenIframe=true&access_token=${routerStore.token}`
|
||||||
return str
|
return str
|
||||||
|
@ -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
|
||||||
|
})
|
||||||
|
}
|
@ -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>
|
@ -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>
|
@ -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'
|
||||||
|
}
|
@ -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>
|
@ -10,6 +10,7 @@ import { DashboardConfig } from './Dashboard'
|
|||||||
import { SystemRuntimeConfig } from './SystemRuntime'
|
import { SystemRuntimeConfig } from './SystemRuntime'
|
||||||
import { VideoListConfig } from './VideoList'
|
import { VideoListConfig } from './VideoList'
|
||||||
import { AirConditioningTableConfig } from './AirConditioningTable'
|
import { AirConditioningTableConfig } from './AirConditioningTable'
|
||||||
|
import { SiteStatisticsConfig } from './SiteStatistics'
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
// Theme1Config,
|
// Theme1Config,
|
||||||
@ -24,4 +25,5 @@ export default [
|
|||||||
SystemRuntimeConfig,
|
SystemRuntimeConfig,
|
||||||
VideoListConfig,
|
VideoListConfig,
|
||||||
AirConditioningTableConfig,
|
AirConditioningTableConfig,
|
||||||
|
SiteStatisticsConfig,
|
||||||
]
|
]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user