feat: 新增组件

This commit is contained in:
huanghao1412 2024-01-10 18:20:47 +08:00
parent 7dcbe34de8
commit e85eda9b4f
29 changed files with 1187 additions and 80 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 840 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View File

@ -1,13 +1,13 @@
<template>
<div :style="getStyle(borderRadius)" style="overflow: visible;">
<BorderBox :title="props.chartConfig.customData.title">
<BorderBox :title="chartConfig?.customData?.title">
<div class="inner">
<div class="left">
<VCircle :value="value" style="height: 100%"/>
<VCircle :value="value" style="height: 100%;width: 100%"/>
</div>
<div class="right">
<div class="item" v-for="(item, i) in rightArr" :key="i">
<Svg v-if="i === 0" class="leftBox" style="position: absolute;width: 60px;height: 37%;"/>
<Svg v-if="i === 0" class="leftBox" style="position: absolute;width: 65px;height: 37%;"/>
<v-chart class="leftBox" :option="item.option" :update-options="{ notMerge: true, replaceMerge: ['series'] }"/>
<div class="rightBox">
<div>{{item.label}}</div>
@ -44,8 +44,10 @@ const props = defineProps({
required: true
}
})
Object.assign(props.chartConfig.attr, { w: 380, h: 250 })
if(!props.chartConfig.request.requestInterval) Object.assign(props.chartConfig.request, { requestInterval: 15, requestIntervalUnit: RequestHttpIntervalEnum.SECOND })
if(!isPreview()) {
Object.assign(props.chartConfig.attr, { w: 450, h: 300 })
Object.assign(props.chartConfig.request, { requestInterval: 15, requestIntervalUnit: RequestHttpIntervalEnum.SECOND })
}
const { w, h } = toRefs(props.chartConfig.attr)
const { dataset, fit, borderRadius } = toRefs(props.chartConfig.option)
@ -498,6 +500,7 @@ onUnmounted(() => {
display: flex;
.left{
flex: 6;
min-width: 60%;
.circle{
}
@ -510,20 +513,30 @@ onUnmounted(() => {
.right{
flex: 4;
.item{
box-sizing: border-box;
height: 33.3%;
display: flex;
align-items: center;
position: relative;
padding: 5px 0;
.leftBox{
height: 60px;
width: 60px;
height: 70px;
width: 70px;
}
.rightBox{
width: 66.6%;
height: 100%;
color: #fff;
width: calc(100% - 60px);
font-size: 14px;
font-weight: 500;
line-height: 16px;
display: flex;
flex-direction: column;
justify-content: space-evenly;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
}
}

View File

@ -1,6 +1,6 @@
<template>
<div :style="getStyle(borderRadius)">
<BorderBox :title="props.chartConfig.customData.title">
<BorderBox :title="chartConfig?.customData?.title">
<div class="item">
<BareMetalServerIcon class="left"/>
<div class="right">
@ -52,8 +52,10 @@ const props = defineProps({
required: true
}
})
Object.assign(props.chartConfig.attr, { w: 380, h: 250 })
if(!props.chartConfig.request.requestInterval) Object.assign(props.chartConfig.request, { requestInterval: 15, requestIntervalUnit: RequestHttpIntervalEnum.SECOND })
if(!isPreview()) {
Object.assign(props.chartConfig.attr, { w: 450, h: 300 })
Object.assign(props.chartConfig.request, { requestInterval: 15, requestIntervalUnit: RequestHttpIntervalEnum.SECOND })
}
const { w, h } = toRefs(props.chartConfig.attr)
const { dataset, fit, borderRadius } = toRefs(props.chartConfig.option)
@ -154,22 +156,33 @@ useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => {
justify-content: center;
.left{
color: #0ff;
height: 60px;
height: 50%;
width: 50%;
}
.right{
width: 50%;
height: 80%;
margin-left: 10px;
display: flex;
flex-direction: column;
justify-content: space-evenly;
.label{
color: #fff;
height: 20px;
line-height: 20px;
font-size: 12px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.value{
color: #0ff;
height: 20px;
line-height: 20px;
font-size: 20px;
margin-top: 8px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.leftRed{

View File

@ -0,0 +1,25 @@
import { PublicConfigClass } from '@/packages/public'
import { CreateComponentType } from '@/packages/index.d'
import { GDMapConfig } from './index'
import cloneDeep from 'lodash/cloneDeep'
// import logo from '@/assets/logo.png'
export const option = {
// 图片路径
dataset: '',
// 适应方式
fit: 'contain',
// 圆角
borderRadius: 0
}
export default class Config extends PublicConfigClass implements CreateComponentType
{
public key = GDMapConfig.key
public chartConfig = cloneDeep(GDMapConfig)
public option = cloneDeep(option)
public customData = cloneDeep({
showInterval: true,
mapId: null
})
}

View File

@ -0,0 +1,67 @@
<template>
<!-- <collapse-item name="属性" :expanded="true">-->
<!-- <setting-item-box name="路径" :alone="true">-->
<!-- <setting-item>-->
<!-- <n-input v-model:value="optionData.dataset" size="small"></n-input>-->
<!-- </setting-item>-->
<!-- </setting-item-box>-->
<!-- <setting-item-box name="样式">-->
<!-- <setting-item name="类型">-->
<!-- <n-select-->
<!-- v-model:value="optionData.fit"-->
<!-- size="small"-->
<!-- :options="fitList"-->
<!-- ></n-select>-->
<!-- </setting-item>-->
<!-- <setting-item name="圆角">-->
<!-- <n-input-number-->
<!-- v-model:value="optionData.borderRadius"-->
<!-- size="small"-->
<!-- :min="0"-->
<!-- placeholder="圆角"-->
<!-- ></n-input-number>-->
<!-- </setting-item>-->
<!-- </setting-item-box>-->
<!-- </collapse-item>-->
</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 GDMapConfig: ConfigType = {
key: 'GDMap',
chartKey: 'VGDMap',
conKey: 'VCGDMap',
// VCD开头
conDataKey: 'VCDGDMap',
title: '站点概况',
category: ChatCategoryEnum.CUSTOMCOMPONENTS,
categoryName: ChatCategoryEnumName.CUSTOMCOMPONENTS,
package: PackagesCategoryEnum.CUSTOMCOMPONENTS,
chartFrame: ChartFrameEnum.COMMON,
image: 'GDMap.png'
}

View File

@ -0,0 +1,278 @@
<template>
<div :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: 180px; height: 142px; position: absolute; z-index: 99;"
>
<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>
<svg
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 423.5 40"
style="height: 29px; position: absolute; left: 50%;transform: translateX(-50%); z-index: 99;"
>
<defs>
<linearGradient id="linear-gradient-staqyxts" x1="212.5" y1="40" x2="212.5" y2="30" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#4196ff" stop-opacity="0.5" />
<stop offset="1" stop-color="#4196ff" stop-opacity="0" />
</linearGradient>
</defs>
<g id="图层_2" data-name="图层 2">
<g id="内容">
<polygon points="161.68 39.5 157 35.76 157 5.21 161.71 0.5 183.29 0.5 188 5.21 188 35.76 183.32 39.5 161.68 39.5" style="fill:#171e28;opacity:0.8" />
<path d="M183.09,1l4.41,4.41V35.52L183.15,39h-21.3l-4.35-3.48V5.41L161.91,1h21.17m.41-1h-22l-5,5V36l5,4h22l5-4V5l-5-5Z" style="fill:#27558e" />
<polygon points="201.68 39.5 197 35.76 197 5.21 201.71 0.5 223.29 0.5 228 5.21 228 35.76 223.32 39.5 201.68 39.5" style="fill:#171e28;opacity:0.8" />
<path d="M223.09,1l4.41,4.41V35.52L223.15,39h-21.3l-4.35-3.48V5.41L201.91,1h21.17m.41-1h-22l-5,5V36l5,4h22l5-4V5l-5-5Z" style="fill:#27558e" />
<polygon points="241.68 39.5 237 35.76 237 5.21 241.71 0.5 263.29 0.5 268 5.21 268 35.76 263.32 39.5 241.68 39.5" style="fill:#171e28;opacity:0.8" />
<path d="M263.09,1l4.41,4.41V35.52L263.15,39h-21.3l-4.35-3.48V5.41L241.91,1h21.17m.41-1h-22l-5,5V36l5,4h22l5-4V5l-5-5Z" style="fill:#27558e" />
<polygon points="281.68 39.5 277 35.76 277 5.21 281.71 0.5 303.29 0.5 308 5.21 308 35.76 303.32 39.5 281.68 39.5" style="fill:#171e28;opacity:0.8" />
<path d="M303.09,1l4.41,4.41V35.52L303.15,39h-21.3l-4.35-3.48V5.41L281.91,1h21.17m.41-1h-22l-5,5V36l5,4h22l5-4V5l-5-5Z" style="fill:#27558e" />
<polygon points="321.68 39.5 317 35.76 317 5.21 321.71 0.5 343.29 0.5 348 5.21 348 35.76 343.32 39.5 321.68 39.5" style="fill:#171e28;opacity:0.8" />
<path d="M343.09,1l4.41,4.41V35.52L343.15,39h-21.3l-4.35-3.48V5.41L321.91,1h21.17m.41-1h-22l-5,5V36l5,4h22l5-4V5l-5-5Z" style="fill:#27558e" />
<polygon points="361.68 39.5 357 35.76 357 5.21 361.71 0.5 383.29 0.5 388 5.21 388 35.76 383.32 39.5 361.68 39.5" style="fill:#171e28;opacity:0.8" />
<path d="M383.09,1l4.41,4.41V35.52L383.15,39h-21.3l-4.35-3.48V5.41L361.91,1h21.17m.41-1h-22l-5,5V36l5,4h22l5-4V5l-5-5Z" style="fill:#27558e" />
<rect x="2.5" y="30" width="420" height="10" style="fill:url(#linear-gradient-staqyxts)" />
<text transform="translate(166.36 30)" style="font-size:24px;fill:aqua;font-family:LCDUltra, LCDUltra">{{ safeDaysList[0] }}</text>
<text transform="translate(206.34 30)" style="font-size:24px;fill:aqua;font-family:LCDUltra, LCDUltra">{{ safeDaysList[1] }}</text>
<text transform="translate(246.34 30)" style="font-size:24px;fill:aqua;font-family:LCDUltra, LCDUltra">{{ safeDaysList[2] }}</text>
<text transform="translate(286.34 30)" style="font-size:24px;fill:aqua;font-family:LCDUltra, LCDUltra">{{ safeDaysList[3] }}</text>
<text transform="translate(326.34 30)" style="font-size:24px;fill:aqua;font-family:LCDUltra, LCDUltra">{{ safeDaysList[4] }}</text>
<text transform="translate(366.34 30)" style="font-size:24px;fill:aqua;font-family:LCDUltra, LCDUltra">{{ safeDaysList[5] }}</text>
<text transform="translate(0 29.12)" style="font-size:24px;fill:#fff;font-weight:700">系统安全运行</text>
<text transform="translate(399.5 29.12)" style="font-size:24px;fill:#fff;font-weight:700"></text>
</g>
</g>
</svg>
<img :src="gdMap.propValue" :style="{transform: `scale(${scale(w, 870)}, ${scale(h - 100,560)})`}" style="position: absolute;top: 100px;width: 870px;height: 560px;transform-origin: left top"/>
<div :style="{transform: `scale(${scale(w, 870)}, ${scale(h - 100,560)})`}" style="position: absolute;top: 100px;width: 870px;height: 560px;transform-origin: left top">
<svg
v-for="(item, i) in point"
:key="i"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
style="position: absolute;outline: none;width: 22px;height: 22px;"
:style="{
top: `${pxToPercentTop(item.style.top + 20, 911)}`,
left: `${pxToPercentLeft(item.style.left - 220, 1400)}`,
}"
>
<g transform="translate(0, 0) scale(1, 1)">
<g>
<circle cx="11" cy="11" r="6.5" style="fill:#4dca59;opacity:0.5;isolation:isolate"></circle>
<path d="M11,5a6,6,0,1,1-6,6,6,6,0,0,1,6-6m0-1a7,7,0,1,0,7,7A7,7,0,0,0,11,4Z" style="fill:#4dca59"></path>
<g style="opacity:0.5">
<path d="M11,1A10,10,0,1,1,1,11,10,10,0,0,1,11,1m0-1A11,11,0,1,0,22,11,11,11,0,0,0,11,0Z" style="fill:#4dca59"></path>
</g>
<circle cx="11" cy="11" r="3" style="fill:#4dca59"></circle>
</g>
</g>
</svg>
<div
v-for="(item, i) in text"
:key="i"
style="position: absolute;color: #fff;"
:style="{top: `${pxToPercentTop(item.style.top + 20, 911)}`, left: `${pxToPercentLeft(item.style.left - 220, 1400)}`}"
>
{{item.propValue}}
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { PropType, shallowReactive, watch, toRefs, reactive, onMounted, onUnmounted, nextTick, ref, computed } 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";
const props = defineProps({
chartConfig: {
type: Object as PropType<CreateComponentType>,
required: true
}
})
if(!isPreview()) {
Object.assign(props.chartConfig.attr, { w: 950, h: 620 })
Object.assign(props.chartConfig.request, { requestInterval: 15, requestIntervalUnit: RequestHttpIntervalEnum.SECOND })
}
// 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
const scale = (a:number, b:number) => {
return a / b
}
const pxToPercentLeft = (left:number, w:number) => {
return `calc(${left * 100 / w}% - 11px)`
}
const pxToPercentTop = (top:number, h:number) => {
return `calc(${top * 100 / h}% - 11px)`
}
let gdMap: { [k: string]: any } = reactive({})
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)
let safeDaysList = reactive(['0', '0', '0', '0', '0', '0'])
const getData = () => {
safeDaysList.splice(0, safeDaysList.length, ...(moment().diff(moment(systemConfig.overview_dglt_idc_operation_date), 'days') + 1).toString().padStart(6, '0').split(''))
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)
gdMap = arr.find((_:any) => _.component === 'Picture') || {}
Object.assign(gdMap, arr.find((_:any) => _.component === 'Picture') || {})
text.splice(0, text.length, ...arr.filter((_:any) => _.component === 'v-text'))
point.splice(0, text.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()
}, 200)
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

@ -0,0 +1,71 @@
<template>
<BorderBox>
<template #title>
<div class="titleBox">
<div class="mr10 nowrap">{{title}}</div>
<n-checkbox :checked="checkAll" @update:checked="(v:boolean) => emit('update:checkAll', v)" size="small" class="mr10 nowrap">全选</n-checkbox>
<n-button strong size="tiny" color="rgba(36,197,231,.5)" text-color="#fff" class="button" @click="clickBatch">
<template #icon>
<AssignmentTurnedInRoundIcon/>
</template>
批量确认
</n-button>
<div style="flex: 1"></div>
<div class="more nowrap" @click="jumpMore">更多>></div>
</div>
</template>
<template #default>
<slot></slot>
</template>
</BorderBox>
</template>
<script lang="ts" setup>
import { defineEmits } from 'vue'
import BorderBox from '../components/BorderBox.vue'
import { icon } from '@/plugins/icon'
const { AssignmentTurnedInRoundIcon } = icon.material
interface propsType {
title: string,
checkAll: boolean,
}
const props = defineProps<propsType>()
const emit = defineEmits(['clickBatch', 'update:checkAll', 'jumpMore'])
const clickBatch = () => {
emit('clickBatch')
}
const jumpMore = () => {
emit('jumpMore')
}
</script>
<style lang="scss" scoped>
.mr10{
margin-right: 10px;
}
.button{
border: 1px solid #4196ff;
//border-color: #4196ff;
}
.nowrap{
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.titleBox{
display: flex;
align-items: center;
overflow: hidden;
.more{
font-size: 14px;
color: #409eff;
cursor: pointer;
}
}
</style>

View File

@ -0,0 +1,142 @@
<template>
<n-modal
:show="show"
preset="dialog"
class="modal"
title=""
:show-icon="false"
@close="close"
@esc="close"
style="width: 500px"
>
<div class="header">
<div>批量确认</div>
</div>
<div class="content">
<n-form
ref="formRef"
:model="data"
:rules="rules"
label-placement="left"
label-width="110px"
label-align="left"
require-mark-placement="right-hanging"
size="small"
:style="{
maxWidth: '640px'
}"
>
<n-form-item label="确认人" path="confirm_people">
<n-input v-model:value="data.confirm_people" readonly placeholder="请输入确认人" />
</n-form-item>
<n-form-item label="是否误报" path="is_misreport">
<n-radio-group v-model:value="data.is_misreport">
<n-radio :value="true"></n-radio>
<n-radio :value="false"></n-radio>
</n-radio-group>
</n-form-item>
<n-form-item label="备注" path="remark">
<n-input
v-model:value="data.remark"
type="textarea"
maxlength="120"
:autosize="{
minRows: 3,
maxRows: 3
}"
placeholder="请输入备注"
/>
</n-form-item>
<n-form-item label="重新确认" path="reconfirmation_time_str">
<n-date-picker v-model:formatted-value="data.reconfirmation_time_str" value-format="yyyy-MM-dd HH:mm:ss" style="width: 100%" type="datetime" clearable />
</n-form-item>
</n-form>
</div>
<div class="footer">
<div style="flex: 1;"></div>
<n-button @click="submitCallback" type="info" size="small" style="margin-right: 5px;color: #fff;">确认</n-button>
<n-button size="small" @click="close">取消</n-button>
</div>
</n-modal>
</template>
<script lang="ts" setup>
import { defineEmits, ref, reactive, watch } from 'vue'
const props = defineProps(['show', 'data'])
const emit = defineEmits(['confirm', 'update:show'])
const submitCallback = () => {
formRef.value.validate((errors:string) => {
if (!errors) {
emit('confirm')
updateShow(false)
}
})
}
const close = () => {
updateShow(false)
}
const formRef:any = ref(null)
const updateShow = (flag:boolean) => {
emit('update:show', flag)
}
const rules = {
confirm_people: {
required: true,
trigger: ['blur', 'input'],
message: '请输入确认人'
},
is_misreport: {
type: 'boolean',
required: true,
trigger: 'change',
message: '请选择'
}
}
</script>
<style>
.modal{
padding: 0;
background: #121922;
box-sizing: border-box;
}
.modal .n-dialog__content{
margin-top: 0;
}
.modal .n-dialog__close{
margin-top: 7px;
margin-right: 10px;
}
</style>
<style lang="scss" scoped>
.header{
display: flex;
align-items: center;
height: 36px;
line-height: 36px;
padding: 0 10px;;
box-sizing: border-box;
border-left: 1px solid #2f3a49;
border-top: 1px solid #2f3a49;
border-right: 1px solid #2f3a49;
}
.content{
padding: 10px;
box-sizing: border-box;
border: 1px solid #2f3a49;
}
.footer{
display: flex;
align-items: center;
padding: 5px 10px;
box-sizing: border-box;
border-left: 1px solid #2f3a49;
border-bottom: 1px solid #2f3a49;
border-right: 1px solid #2f3a49;
}
</style>

View File

@ -0,0 +1,25 @@
import { PublicConfigClass } from '@/packages/public'
import { CreateComponentType } from '@/packages/index.d'
import { MonitorRealTimeEventsConfig } from './index'
import cloneDeep from 'lodash/cloneDeep'
// import logo from '@/assets/logo.png'
export const option = {
// 图片路径
dataset: '',
// 适应方式
fit: 'contain',
// 圆角
borderRadius: 0
}
export default class Config extends PublicConfigClass implements CreateComponentType
{
public key = MonitorRealTimeEventsConfig.key
public chartConfig = cloneDeep(MonitorRealTimeEventsConfig)
public option = cloneDeep(option)
public customData = cloneDeep({
title: '自监控实时事件',
showInterval: true,
})
}

View File

@ -0,0 +1,67 @@
<template>
<!-- <collapse-item name="属性" :expanded="true">-->
<!-- <setting-item-box name="路径" :alone="true">-->
<!-- <setting-item>-->
<!-- <n-input v-model:value="optionData.dataset" size="small"></n-input>-->
<!-- </setting-item>-->
<!-- </setting-item-box>-->
<!-- <setting-item-box name="样式">-->
<!-- <setting-item name="类型">-->
<!-- <n-select-->
<!-- v-model:value="optionData.fit"-->
<!-- size="small"-->
<!-- :options="fitList"-->
<!-- ></n-select>-->
<!-- </setting-item>-->
<!-- <setting-item name="圆角">-->
<!-- <n-input-number-->
<!-- v-model:value="optionData.borderRadius"-->
<!-- size="small"-->
<!-- :min="0"-->
<!-- placeholder="圆角"-->
<!-- ></n-input-number>-->
<!-- </setting-item>-->
<!-- </setting-item-box>-->
<!-- </collapse-item>-->
</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="标题" :alone="true">
<n-input v-model:value="props.customData.title" size="small" placeholder="请输入"/>
</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 MonitorRealTimeEventsConfig: ConfigType = {
key: 'MonitorRealTimeEvents',
chartKey: 'VMonitorRealTimeEvents',
conKey: 'VCMonitorRealTimeEvents',
// VCD开头
conDataKey: 'VCDMonitorRealTimeEvents',
title: '自监控实时事件',
category: ChatCategoryEnum.CUSTOMCOMPONENTS,
categoryName: ChatCategoryEnumName.CUSTOMCOMPONENTS,
package: PackagesCategoryEnum.CUSTOMCOMPONENTS,
chartFrame: ChartFrameEnum.COMMON,
image: 'MonitorRealTimeEvents.png'
}

View File

@ -0,0 +1,271 @@
<template>
<div :style="getStyle(borderRadius)" style="overflow: visible">
<BorderBox
:title="chartConfig?.customData?.title"
v-model:checkAll="checkAll"
@clickBatch="clickBatch('batch')"
@jumpMore="jumpMore"
>
<div class="itemBox">
<div class="item" v-for="(item, i) in tableData" :key="i">
<div class="row1">
<n-checkbox :disabled="item.confirm_status === 'ok'" v-model:checked="item.checked" class="mr10" size="small" @click.stop/>
<n-tag class="mr5" size="small" strong :color="{textColor: '#000', color: item.confirm_status === 'ok' ? '#4DCA59' : '#f5b442'}">
{{ item.confirm_status === 'ok'?'已确认':'未确认' }}
</n-tag>
<n-tag class="mr5" size="small" :color="{textColor: levelToOption(item.level).remark, borderColor: levelToOption(item.level).remark}">
{{levelToOption(item.level).label}}
</n-tag>
<div class="content">{{ item.content }}</div>
</div>
<div class="row2">
<div class="mr10 nowrap" style="color: #B5BAC3;">{{ moment(item.generate_time).format('yyyy-MM-DD HH:mm:ss') }}</div>
<div style="flex: 1;"></div>
<CheckCircleOutlinedIcon @click="clickBatch('single', item.id)" style="width: 20px;height: 20px;color: #4196ff;"/>
</div>
</div>
</div>
</BorderBox>
<VModalV1 v-model:show="modalV1Obj.show" :data="modalV1Obj.data" @confirm="confirm"/>
</div>
</template>
<script setup lang="ts">
import { PropType, watch, toRefs, reactive, onMounted, onUnmounted, nextTick, ref } from 'vue'
import { CreateComponentType } from '@/packages/index.d'
import { publicInterface } from '@/api/path/business.api'
import BorderBox from './BorderBoxV2.vue'
import {isPreview, postMessageToParent} from '@/utils'
import {selectTimeOptions} from "@/views/chart/ContentConfigurations/components/ChartData/index.d";
import {RequestHttpIntervalEnum} from "@/enums/httpEnum";
import {icon} from "@/plugins";
import moment from 'moment'
import { useOriginStore } from '@/store/modules/originStore/originStore'
import VModalV1 from './VModalV1.vue'
const { CheckCircleOutlinedIcon } = icon.material
const props = defineProps({
chartConfig: {
type: Object as PropType<CreateComponentType>,
required: true
}
})
if(!isPreview()){
Object.assign(props.chartConfig.attr, { w: 450, h: 300 })
Object.assign(props.chartConfig.request, { requestInterval: 15, requestIntervalUnit: RequestHttpIntervalEnum.SECOND })
}
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 user = originStore.getOriginStore.user.user
const modalV1Obj = reactive({
show: false,
data: {
confirm_people: user.name,
is_misreport: false,
remark: '',
reconfirmation_time_str: null,
},
// batch single
type: 'batch',
singleIds: [],
})
const clickBatch = (type = 'batch', id?:number) => {
if(type === 'batch' && !tableData.filter(_ => _.checked).length) {
window['$message'].warning('请先选择数据')
return
}
let singleIds:number[] = []
if(type === 'single' && id) singleIds = [id]
Object.assign(modalV1Obj, {
show: true,
data: {
confirm_people: user.name,
is_misreport: false,
remark: '',
reconfirmation_time_str: null,
},
type,
singleIds
})
}
const confirm = () => {
const obj = {
id: null,
ids: modalV1Obj.type === 'batch' ? tableData.filter(_ => _.checked).map(_ => _.id) : modalV1Obj.singleIds,
confirm_status: "ok",
...modalV1Obj.data
}
publicInterface('/dcim/dems/devie_active_alarm', 'confirms', obj).then(res => {
window['$message'].success('操作成功')
checkAll.value = false
getData()
})
}
const jumpMore = () => {
postMessageToParent({
type: 'changeRouterV1',
url: `/alarmManage/monitorAlarm`
})
}
type tableDataItemType = {
id: number,
content: string,
generate_time: string,
checked: boolean,
confirm_status: 'ok' | 'not',
level: number
}
let tableData:tableDataItemType[] = reactive([])
watch(() => tableData.map(_ => _.checked), (v:boolean[]) => {
if(!v.length) checkAll.value = false
else if(v.every(_ => _)) checkAll.value = true
else if(v.every(_ => !_)) checkAll.value = false
})
let checkAll = ref(false)
watch(() => checkAll.value, (v) => {
tableData.forEach(_ => _.checked = v)
})
type levelItem = { [k:string]: string | number }
const warnLevelOption = originStore.getOriginStore.user.systemConstant['warn_levels'].filter((item: levelItem) => item.value !== '').map((item: levelItem) => {
return { label: item.label, value: Number(item.value), remark: item.remark }
})
const levelToOption = (level:number) => {
return warnLevelOption.find((_:levelItem) => _.value === level) || { label: '其他', remark: '#989898' }
}
const getData = () => {
const queryModel = {
condition: {
id: null,
levels: [],
confirm_statuss: ['not'],
trigger_source: 1 // 1IOT0
},
page: {
page_size: 6,
page_number: 1
}
}
publicInterface('/dcim/dems/devie_active_alarm', 'get_page', queryModel).then(res => {
if(res && res.data && res.data.item) {
const lastTableData = [...tableData]
tableData.splice(0, tableData.length, ...res.data.item.map((e:any) => ({ ...e, checked: false })))
if (checkAll.value) {
tableData.splice(0, tableData.length, ...tableData.map(e => ({ ...e, checked: e.confirm_status !== 'ok' })))
} else if (lastTableData.length) {
tableData.forEach(e => {
const lastIndex = lastTableData.findIndex(item => item.id === e.id)
if (lastIndex !== -1) {
e.checked = lastTableData[lastIndex].checked
}
})
}
}
})
}
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)
}
})
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>
.mr5{
margin-right: 5px;
}
.mr10{
margin-right: 10px;
}
.nowrap{
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.itemBox{
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
overflow-y: auto;
.item{
flex: none;
height: 60px;
padding: 5px 10px;
box-sizing: border-box;
margin: 3px 0;
background: rgba(65,150,255,.05);
cursor: pointer;
.row1{
display: flex;
align-items: center;
.content{
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 12px;
color: rgba(255, 255, 255, 0.82)
}
}
.row2{
margin-top: 8px;
display: flex;
align-items: center;
font-size: 12px;
}
}
}
</style>

View File

@ -1,5 +1,5 @@
<template>
<BorderBox :title="props.chartConfig.customData.title" :style="getStyle(borderRadius)" style="overflow: visible">
<BorderBox :title="chartConfig?.customData?.title" :style="getStyle(borderRadius)" style="overflow: visible">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 401 236" style="width: 100%;height: 100%">
<defs>
<linearGradient id="linear-gradient-compute-node" x1="99" y1="176" x2="99" y2="120" gradientUnits="userSpaceOnUse">
@ -102,8 +102,10 @@ const props = defineProps({
required: true
}
})
Object.assign(props.chartConfig.attr, { w: 380, h: 250 })
if(!props.chartConfig.request.requestInterval) Object.assign(props.chartConfig.request, { requestInterval: 15, requestIntervalUnit: RequestHttpIntervalEnum.SECOND })
if(!isPreview()) {
Object.assign(props.chartConfig.attr, { w: 450, h: 300 })
Object.assign(props.chartConfig.request, { requestInterval: 15, requestIntervalUnit: RequestHttpIntervalEnum.SECOND })
}
const { w, h } = toRefs(props.chartConfig.attr)
const { dataset, fit, borderRadius } = toRefs(props.chartConfig.option)

View File

@ -1,7 +1,7 @@
<template>
<div class="box">
<img class="scaner" src="@/assets/customComponents/RealTimeAlarmStatistics/scanner.png" alt="">
<svg v-show="alarmLevels.length === 5" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 200 200">
<svg v-show="alarmLevelsLength === 5" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 200 200">
<g>
<g>
<circle style="opacity:0.2;fill-rule:evenodd;clip-rule:evenodd;fill:#4196FF;" cx="100" cy="100" r="100" />
@ -133,7 +133,7 @@
</g>
</g>
</svg>
<svg v-show="alarmLevels.length === 4 || alarmLevels.length === 0" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 200 200">
<svg v-show="alarmLevelsLength === 4 || alarmLevelsLength === 0" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 200 200">
<defs>
<radialGradient id="real_time_alarm_statistics_35" cx="100" cy="660" r="92" gradientTransform="matrix(1, 0, 0, -1, 0, 760)" gradientUnits="userSpaceOnUse">
<stop offset="0.84" stop-color="#fff" stop-opacity="0.05" />
@ -219,7 +219,7 @@
<text transform="translate(124.59 88.89)" style="isolation:isolate;font-size:14px;fill:#fff;font-family:MicrosoftYaHei, Microsoft YaHei">{{ alarmMonitorData ? alarmMonitorData["secondary"] : 0 }}</text>
</g>
</svg>
<svg v-show="alarmLevels.length === 3" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 200 200">
<svg v-show="alarmLevelsLength === 3" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 200 200">
<g>
<g>
<circle style="opacity:0.2;fill-rule:evenodd;clip-rule:evenodd;fill:#4196FF;" cx="100" cy="100" r="100" />
@ -327,7 +327,7 @@
</g>
</g>
</svg>
<svg v-show="alarmLevels.length === 2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 200 200">
<svg v-show="alarmLevelsLength === 2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 200 200">
<g>
<g>
<circle style="opacity:0.2;fill-rule:evenodd;clip-rule:evenodd;fill:#4196FF;" cx="100" cy="100" r="100" />
@ -423,7 +423,7 @@
</g>
</g>
</svg>
<svg v-show="alarmLevels.length === 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 200 200">
<svg v-show="alarmLevelsLength === 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 200 200">
<g>
<g>
<circle style="opacity:0.2;fill-rule:evenodd;clip-rule:evenodd;fill:#4196FF;" cx="100" cy="100" r="100" />
@ -511,25 +511,41 @@
</template>
<script lang="ts" setup>
import { toRefs } from 'vue'
import { toRefs, computed } from 'vue'
const props = defineProps(['urgentName', 'seriousName', 'importantName', 'commonName', 'eventName', 'alarmMonitorData', 'alarmLevels'])
const { urgentName, seriousName, importantName, commonName, eventName, alarmMonitorData, alarmLevels } = toRefs(props)
const alarmLevelsLength = computed(() => {
return alarmLevels!.value.length
})
</script>
<style lang="scss" scoped>
@keyframes rotate {
from { transform: scale(0.69) rotate(0);}
to { transform: scale(0.69) rotate(360deg);}
from { transform: translateY(-50%) scale(0.71) rotate(0)}
to { transform: translateY(-50%) scale(0.71) rotate(360deg) }
}
.box{
position: relative;
.scaner{
height: 100%;
svg{
height: 100%;
width: 100%;
position: absolute;
top: -1.5%;
left: -0.5%;
top: 50%;
transform: translateY(-50%);
z-index: 2;
}
.scaner{
object-fit: contain;
height: 100%;
width: 100%;
position: absolute;
top: 50%;
animation: rotate linear 5s infinite;
width: 100%;
height: 100%;
z-index: 3;
//height: 100%;
}
}

View File

@ -1,6 +1,6 @@
<template>
<div :style="getStyle(borderRadius)">
<BorderBox :title="props.chartConfig.customData.title">
<BorderBox :title="chartConfig?.customData?.title">
<v-chart
ref="vChartRef"
:option="option"
@ -9,9 +9,9 @@
replaceMerge: ['series', 'xAxis', 'yAxis']
}"
autoresize
style="overflow: visible;width: 50%"
style="overflow: visible;width: calc(50% - 10px);margin-right: 20px;"
/>
<div class="box-5-item" style="width: 50%">
<div class="box-5-item" style="width: calc(50% - 10px)">
<SvgAll v-bind="nameObj"/>
</div>
</BorderBox>
@ -37,8 +37,10 @@ const props = defineProps({
}
})
Object.assign(props.chartConfig.attr, {w: 380, h: 250})
if(!props.chartConfig.request.requestInterval) Object.assign(props.chartConfig.request, { requestInterval: 15, requestIntervalUnit: RequestHttpIntervalEnum.SECOND })
if(!isPreview()) {
Object.assign(props.chartConfig.attr, {w: 450, h: 300})
Object.assign(props.chartConfig.request, { requestInterval: 15, requestIntervalUnit: RequestHttpIntervalEnum.SECOND })
}
const { w, h } = toRefs(props.chartConfig.attr)
const { dataset, fit, borderRadius } = toRefs(props.chartConfig.option)
@ -75,8 +77,8 @@ const originStore = useOriginStore()
const systemConfig = originStore?.getOriginStore?.user?.systemConfig
const systemConstant = originStore?.getOriginStore?.user?.systemConstant
if (systemConfig['active_alarm_level']) {
for (let i = 0; i < Number(systemConfig['active_alarm_level']); i++) {
nameObj.alarmLevels.push(i + 1)
for (let i:number = 0; i < Number(systemConfig['active_alarm_level']); i++) {
(nameObj.alarmLevels as number[]).push(i + 1)
}
}

View File

@ -3,7 +3,7 @@
<template #title>
<div class="titleBox">
<div class="mr10">{{title}}</div>
<n-checkbox :checked="checkAll" @update:checked="v => emit('update:checkAll', v)" size="small" class="mr10">全选</n-checkbox>
<n-checkbox :checked="checkAll" @update:checked="(v:boolean) => emit('update:checkAll', v)" size="small" class="mr10">全选</n-checkbox>
<n-button strong size="tiny" color="rgba(36,197,231,.5)" text-color="#fff" class="button" @click="clickBatch">
<template #icon>
<AssignmentTurnedInRoundIcon/>
@ -11,7 +11,7 @@
批量确认
</n-button>
<div style="flex: 1"></div>
<n-checkbox-group :value="select1.value" @update:value="v => changeSelect1(v)" class="mr10" size="small">
<n-checkbox-group v-if="showFilter" :value="select1.value" @update:value="(v:[]) => changeSelect1(v)" class="mr10" size="small">
<n-space item-style="display: flex;" size="small">
<n-checkbox :value="item.value" :label="item.label" v-for="(item, i) in select1.options" :key="i">
<div :style="{color: item.color}" style="display: inline-block">{{item.label}}</div>
@ -19,7 +19,7 @@
</n-checkbox>
</n-space>
</n-checkbox-group>
<n-checkbox-group :value="select2.value" @update:value="v => changeSelect2(v)" class="mr10" size="small">
<n-checkbox-group v-if="showFilter" :value="select2.value" @update:value="(v:[]) => changeSelect2(v)" class="mr10" size="small">
<n-space item-style="display: flex;">
<n-checkbox :value="item.value" :label="item.label" v-for="(item, i) in select2.options" :key="i">
<div :style="{color: item.color}" style="display: inline-block">{{item.label}}</div>
@ -40,12 +40,24 @@
import { ref, reactive, toRefs, defineEmits } from 'vue'
import BorderBox from '../components/BorderBox.vue'
import { icon } from '@/plugins/icon'
import VModal from './VModalV1.vue'
const { AssignmentTurnedInRoundIcon } = icon.material
interface propsType {
title: string,
select1: {
value: number[],
options: { label: string, value: number, number: number, color: string }[],
},
select2: {
value: string[],
options: { label: string, value: string, number: number, color: string }[],
},
checkAll: boolean,
showFilter: boolean,
}
const props = defineProps<propsType>()
const props = defineProps(['title', 'select1', 'select2', 'checkAll'])
const { select1, select2 } = toRefs(props)
@ -93,6 +105,10 @@ const jumpMore = () => {
border: 1px solid #4196ff;
//border-color: #4196ff;
}
.n-space{
flex-wrap: nowrap!important;
gap: 4px 2px!important;
}
.titleBox{
display: flex;
align-items: center;

View File

@ -21,5 +21,6 @@ export default class Config extends PublicConfigClass implements CreateComponent
public customData = cloneDeep({
title: '实时事件',
showInterval: true,
showFilter: false,
})
}

View File

@ -3,6 +3,12 @@
<setting-item-box name="标题" :alone="true">
<n-input v-model:value="props.customData.title" size="small" placeholder="请输入"/>
</setting-item-box>
<setting-item-box name="显示筛选条件" :alone="true">
<n-radio-group v-model:value="props.customData.showFilter" size="small" style="margin-top: 2px">
<n-radio :value="true"></n-radio>
<n-radio :value="false"></n-radio>
</n-radio-group>
</setting-item-box>
</n-space>
</template>

View File

@ -1,33 +1,38 @@
<template>
<BorderBox
:title="props.chartConfig.customData.title"
:title="chartConfig?.customData?.title"
:select1="select1"
@update:select1Value="v => select1.value = v"
:select2="select2"
@update:select2Value="v => select2.value = v"
:style="getStyle(borderRadius)"
@clickBatch="clickBatch"
v-model:checkAll="checkAll"
@jumpMore="jumpMore"
:showFilter="chartConfig?.customData?.showFilter"
:style="getStyle(borderRadius)"
style="overflow: visible"
>
<div class="itemBox">
<div class="item" v-for="(item, i) in tableData" :key="i" @click="clickItem(i)">
<n-checkbox :disabled="item.checked" v-model:checked="item.checked" class="mr10" size="small" @click.stop/>
<div v-if="tableData.length" class="itemBox">
<div class="item" v-for="(item, i) in tableData" :key="i" @click="clickItem(i)">
<n-checkbox :disabled="item.confirm_status === 'ok'" v-model:checked="item.checked" class="mr10" size="small" @click.stop/>
<n-tag class="mr5" size="small" strong :color="{textColor: '#000', color: item.confirm_status === 'ok' ? '#4DCA59' : '#f5b442'}">
{{ item.confirm_status === 'ok'?'已确认':'未确认' }}
</n-tag>
<n-tag class="mr5" size="small" :color="{textColor: item.color1, borderColor: item.color1}">
{{select1.options[item.level - 1].label}}
</n-tag>
<div style="color: rgba(255, 255, 255, 0.82);">{{ item.content }}</div>
<div class="textEllipsis" style="color: rgba(255, 255, 255, 0.82);">{{ item.content }}</div>
<div style="flex: 1"></div>
<div class="mr10" style="color: #B5BAC3;">{{ moment(item.generate_time).format('yyyy-MM-DD HH:mm:ss') }}</div>
<div class="mr10 textEllipsis" style="color: #B5BAC3;">{{ moment(item.generate_time).format('yyyy-MM-DD HH:mm:ss') }}</div>
<LocationIcon @click.stop="jumpTo(item)" class="mr10" style="width: 20px;height: 20px;color: #4196ff;"/>
<CheckCircleOutlinedIcon @click.stop="clickSingle(item.id)" v-if="item.confirm_status === 'not'" style="width: 20px;height: 20px;color: #4196ff;"/>
<div v-else style="width: 20px"></div>
</div>
</div>
<div class="emptyBox" v-else>
<img src="@/assets/images/exception/nodata.svg" style="width: 100%;height: 50%" alt="">
<div style="color: #fff;text-align: center">查询结果为空</div>
</div>
<VModal v-model:show="modalObj.show" :data="modalObj.data" :select1Options="select1.options"/>
<VModalV1 v-model:show="modalV1Obj.show" :data="modalV1Obj.data" @confirm="confirm"/>
</BorderBox>
@ -35,15 +40,10 @@
<script setup lang="ts">
import { PropType, shallowReactive, watch, toRefs, reactive, onMounted, onUnmounted, nextTick, ref } from 'vue'
import { useChartDataFetch } from '@/hooks'
import { CreateComponentType } from '@/packages/index.d'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { publicInterface } from '@/api/path/business.api'
import BorderBox from './BorderBoxV2.vue'
import VChart from 'vue-echarts'
import {isPreview, postMessageToParent} from '@/utils'
import {graphic} from "echarts";
import {cloneDeep} from 'lodash'
import moment from "moment"
import {selectTimeOptions} from "@/views/chart/ContentConfigurations/components/ChartData/index.d";
import {RequestHttpIntervalEnum} from "@/enums/httpEnum";
@ -61,8 +61,11 @@ const props = defineProps({
required: true
}
})
Object.assign(props.chartConfig.attr, { w: 1000, h: 250 })
if(!props.chartConfig.request.requestInterval) Object.assign(props.chartConfig.request, { requestInterval: 15, requestIntervalUnit: RequestHttpIntervalEnum.SECOND })
if(!isPreview()){
Object.assign(props.chartConfig.attr, { w: 950, h: 300 })
Object.assign(props.chartConfig.request, { requestInterval: 15, requestIntervalUnit: RequestHttpIntervalEnum.SECOND })
}
const { w, h } = toRefs(props.chartConfig.attr)
const { dataset, fit, borderRadius } = toRefs(props.chartConfig.option)
@ -276,7 +279,7 @@ const confirm = () => {
const obj = {
id: null,
ids: modalV1Obj.type === 'batch' ? tableData.filter(_ => _.checked).map(_ => _.id) : modalV1Obj.singleIds,
confirm_status: "not",
confirm_status: "ok",
...modalV1Obj.data
}
publicInterface('/dcim/dems/devie_active_alarm', 'confirms', obj).then(res => {
@ -359,6 +362,19 @@ onUnmounted(() => {
.mr10{
margin-right: 10px;
}
.textEllipsis{
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.emptyBox{
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
}
.itemBox{
width: 100%;
height: 100%;

View File

@ -1,6 +1,6 @@
<template>
<div :style="getStyle(borderRadius)" style="overflow: visible">
<BorderBox :title="props.chartConfig.customData.title">
<BorderBox :title="chartConfig?.customData?.title">
<v-chart
ref="vChartRef"
:option="option"
@ -18,15 +18,12 @@
<script setup lang="ts">
import { PropType, shallowReactive, watch, toRefs, reactive, onMounted, onUnmounted, nextTick, ref } from 'vue'
import { useChartDataFetch } from '@/hooks'
import { CreateComponentType } from '@/packages/index.d'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { publicInterface } from '@/api/path/business.api'
import BorderBox from '../components/BorderBox.vue'
import VChart from 'vue-echarts'
import {isPreview} from '@/utils'
import {graphic} from "echarts";
import {cloneDeep} from 'lodash'
import moment from "moment"
import {selectTimeOptions} from "@/views/chart/ContentConfigurations/components/ChartData/index.d";
import {RequestHttpIntervalEnum} from "@/enums/httpEnum";
@ -37,8 +34,10 @@ const props = defineProps({
required: true
}
})
Object.assign(props.chartConfig.attr, { w: 380, h: 250 })
if(!props.chartConfig.request.requestInterval) Object.assign(props.chartConfig.request, { requestInterval: 15, requestIntervalUnit: RequestHttpIntervalEnum.SECOND })
if(!isPreview()) {
Object.assign(props.chartConfig.attr, { w: 450, h: 300 })
Object.assign(props.chartConfig.request, { requestInterval: 15, requestIntervalUnit: RequestHttpIntervalEnum.SECOND })
}
const { w, h } = toRefs(props.chartConfig.attr)
const { dataset, fit, borderRadius } = toRefs(props.chartConfig.option)

View File

@ -90,7 +90,7 @@
</g>
</g>
</svg>
<div style="font: 30px PingFang-SC-Bold;color: #fff;width: 1920px;height: 60px;line-height: 60px;text-align: center;position: absolute;top: 0;">{{props.chartConfig.customData.title}}</div>
<div style="font: 30px PingFang-SC-Bold;color: #fff;width: 1920px;height: 60px;line-height: 60px;text-align: center;position: absolute;top: 0;">{{chartConfig?.customData?.title}}</div>
<n-image
:object-fit="fit"
preview-disabled
@ -104,14 +104,13 @@
</template>
<script setup lang="ts">
import { PropType, shallowReactive, watch, toRefs, onMounted, onUnmounted, ref } from 'vue'
import { PropType, shallowReactive, watch, toRefs, onMounted, onUnmounted, ref, computed } from 'vue'
import { requireErrorImg } from '@/utils'
import { useChartDataFetch } from '@/hooks'
import { CreateComponentType } from '@/packages/index.d'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import moment from 'moment'
import background from '@/assets/customComponents/theme1/backgrond.jpg'
import { isPreview } from '@/utils/router'
import { postMessageToParent } from "@/utils";
const props = defineProps({
chartConfig: {
@ -120,9 +119,7 @@ const props = defineProps({
}
})
props.chartConfig.attr.w = 1920
props.chartConfig.attr.h = 1080
Object.assign(props.chartConfig.attr, {w: 1920, h: 1080, x: 0, y: 0})
if(!isPreview()) Object.assign(props.chartConfig.attr, {w: 1920, h: 1080, x: 0, y: 0})
const { w, h } = toRefs(props.chartConfig.attr)
const { dataset, fit, borderRadius } = toRefs(props.chartConfig.option)
@ -145,17 +142,20 @@ onUnmounted(() => {
const showMoreMenuBts = ref(true)
//
function toggleFullscreen() {
showMoreMenuBts.value = !showMoreMenuBts.value
if (document.fullscreenElement) {
// 退
document.exitFullscreen();
} else {
//
document.documentElement.requestFullscreen()
.catch(err => {
console.error('Error attempting to enable full-screen mode:', err);
});
}
postMessageToParent({type: 'fullScreen'})
// console.log(document.documentElement)
// showMoreMenuBts.value = !showMoreMenuBts.value
//
// if (document.fullscreenElement) {
// // 退
// document.exitFullscreen();
// } else {
// //
// document.documentElement.requestFullscreen()
// .catch(err => {
// console.error('Error attempting to enable full-screen mode:', err);
// });
// }
}
const option = shallowReactive({
@ -202,6 +202,7 @@ const getStyle = (radius: number) => {
.Theme1{
position: relative;
.full-screen-btn {
z-index: 999;
border: #4396fd 1px solid;
border-radius: 3px;
display: flex;

View File

@ -30,6 +30,9 @@ const { title } = toRefs(props)
border-top: 1px solid rgba(65,150,255,.5);
border-bottom: 1px solid rgba(65,150,255,.2);
background: linear-gradient(90deg,rgba(65,150,255,.1) 0,rgba(65,150,255,0));
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
&:after{
content: '';
height: 1px;
@ -41,6 +44,9 @@ const { title } = toRefs(props)
}
}
.content{
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
height: calc(100% - 40px);
box-sizing: border-box;
display: flex;

View File

@ -5,6 +5,8 @@ import { RealTimeTrafficConfig } from './RealTimeTraffic'
import { RealTimeAlarmStatisticsConfig } from './RealTimeAlarmStatistics'
import { OverviewOfComputingNodesConfig } from './OverviewOfComputingNodes'
import { RealTimeEventConfig } from './RealTimeEvent'
import { GDMapConfig } from './GDMap'
import { MonitorRealTimeEventsConfig } from './MonitorRealTimeEvents'
export default [
Theme1Config,
@ -13,5 +15,7 @@ export default [
RealTimeTrafficConfig,
RealTimeAlarmStatisticsConfig,
OverviewOfComputingNodesConfig,
RealTimeEventConfig
RealTimeEventConfig,
GDMapConfig,
MonitorRealTimeEventsConfig,
]

View File

@ -46,6 +46,8 @@ export type ConfigType = {
icon?: string
// 事件
configEvents?: { [T: string]: Function }
// 自定义数据配置
customData?: { [T: string]: any }
}
// 数据请求

View File

@ -12,7 +12,7 @@
</div>
<div v-if="targetData && targetData.chartConfig.conDataKey">
<component :is="targetData.chartConfig.conDataKey" :customData="targetData.customData" :request="targetData.request"></component>
<setting-item-box v-if="targetData.customData.showInterval" name="更新间隔" :alone="true">
<setting-item-box v-if="targetData?.customData?.showInterval" name="更新间隔" :alone="true">
<n-input-group>
<n-input-number
v-model:value.trim="targetData.request.requestInterval"