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 = '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
|
||||
|
@ -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 { VideoListConfig } from './VideoList'
|
||||
import { AirConditioningTableConfig } from './AirConditioningTable'
|
||||
import { SiteStatisticsConfig } from './SiteStatistics'
|
||||
|
||||
export default [
|
||||
// Theme1Config,
|
||||
@ -24,4 +25,5 @@ export default [
|
||||
SystemRuntimeConfig,
|
||||
VideoListConfig,
|
||||
AirConditioningTableConfig,
|
||||
SiteStatisticsConfig,
|
||||
]
|
||||
|
Loading…
x
Reference in New Issue
Block a user