feat: 新增空调列表

This commit is contained in:
huanghao1412 2024-04-18 18:10:38 +08:00
parent 9272d8f9eb
commit d96e79ffec
10 changed files with 639 additions and 17 deletions

View File

@ -0,0 +1,52 @@
import cloneDeep from 'lodash/cloneDeep'
import { PublicConfigClass } from '@/packages/public'
import { CreateComponentType } from '@/packages/index.d'
import { chartInitConfig } from '@/settings/designSetting'
import { AirConditioningTableConfig } from './index'
export const customData = {
ids: [] as string[],
showInterval: true
}
export const option = {
// dataset: { dimensions: [], source: [] },
// 展示列
header: {
columns: [
{key: 'node_name', title: '设备名', unit: '', show: true, width: 'auto', align: 'left', ellipsis: true},
{key: '空调功率', title: '空调功率', unit: 'kW', show: true, width: 'auto', align: 'left', ellipsis: true},
{key: '用电量', title: '用电量', unit: 'kWh', show: true, width: 'auto', align: 'left', ellipsis: true},
],
// value: [],
// options: [],
// map: {},
},
pagination: {
show: false,
page: 1,
pageSize: 5
},
// align: 'center',
style: {
border: 'off',
singleColumn: 'off',
singleLine: 'off',
bottomBordered: 'off',
striped: 'on',
lineHeight: 50,
fontSize: 16,
borderWidth: 0,
borderColor: 'rgba(0, 0, 0, 1)',
borderStyle: 'solid'
},
inputShow: 'none'
}
export default class Config extends PublicConfigClass implements CreateComponentType {
public key = AirConditioningTableConfig.key
public attr = { ...chartInitConfig, w: 600, h: 300, zIndex: -1 }
public chartConfig = cloneDeep(AirConditioningTableConfig)
public option = cloneDeep(option)
public customData = cloneDeep(customData)
}

View File

@ -0,0 +1,243 @@
<template>
<collapse-item name="表格设置" :expanded="true">
<n-tag type="primary">若配置无响应请在预览页面查看效果</n-tag>
<setting-item-box v-for="(it, i) in optionData.header.columns" :key="i" :name="`表头-列${i+1}`">
<setting-item name="是否展示">
<n-space justify="start">
<n-switch v-model:value="it.show" size="small"/>
</n-space>
</setting-item>
<setting-item name="字段名 对应设备测点名称">
<n-input v-model:value="it.key"/>
</setting-item>
<setting-item name="列名">
<n-input v-model:value="it.title"/>
</setting-item>
<setting-item name="单位">
<n-input v-model:value="it.unit"/>
</setting-item>
<setting-item name="宽度">
<n-input v-model:value="it.width" placeholder="50/50%/auto"/>
</setting-item>
<setting-item name="对齐">
<n-select v-model:value="it.align" :options="[
{ label: '靠左', value: 'left' },
{ label: '居中', value: 'center' },
{ label: '靠右', value: 'right' }
]"/>
</setting-item>
</setting-item-box>
<!-- <setting-item-box name="表头" :alone="true">-->
<!-- <div class="rows">-->
<!-- <div class="columns">字段</div>-->
<!-- <div class="columns">标题</div>-->
<!-- </div>-->
<!-- <div class="rows" v-for="(row, i) in optionData.header.options" :key="i">-->
<!-- <div class="columns">{{ row }}</div>-->
<!-- <n-input class="columns" v-model:value="optionData.header.map[row]" size="small"/>-->
<!-- </div>-->
<!-- </setting-item-box>-->
<!-- <setting-item-box name="展示列" :alone="true">-->
<!-- <n-select-->
<!-- v-model:value="optionData.header.value"-->
<!-- :options="optionData.header.options.map(_ => ({label: optionData.header.map[_], value: _}))"-->
<!-- multiple-->
<!-- size="small"-->
<!-- />-->
<!-- </setting-item-box>-->
<!-- <setting-item-box :alone="true" name="对齐方式">-->
<!-- <setting-item :alone="true">-->
<!-- <n-select-->
<!-- v-model:value="optionData.align"-->
<!-- size="small"-->
<!-- :options="[-->
<!-- { label: '靠左', value: 'left' },-->
<!-- { label: '居中', value: 'center' },-->
<!-- { label: '靠右', value: 'right' }-->
<!-- ]"-->
<!-- />-->
<!-- </setting-item>-->
<!-- </setting-item-box>-->
<setting-item-box :alone="false" name="分页设置">
<setting-item name="显示分页" :alone="true">
<n-space align="start">
<n-switch v-model:value="optionData.pagination.show" size="small"/>
</n-space>
</setting-item>
<setting-item name="默认页码" :alone="true">
<n-input-number v-model:value="optionData.pagination.page" :min="1" size="small"
placeholder="字体大小"></n-input-number>
</setting-item>
<setting-item name="分页" :alone="true">
<n-select v-model:value="optionData.pagination.pageSize" size="small" :options="page"/>
</setting-item>
</setting-item-box>
<!-- <setting-item-box :alone="false" name="表格数据">-->
<!-- <SettingItem name="表头名称" class="form_name">-->
<!-- <div style="width: 260px">-->
<!-- <n-input v-model:value="header" size="small" placeholder="表头数据(英文','分割)"></n-input>-->
<!-- </div>-->
<!-- </SettingItem>-->
<!-- </setting-item-box>-->
<setting-item-box :alone="false" name="表格样式">
<SettingItem name="显示边框" :alone="true">
<n-select v-model:value="(optionData as any).style.border" size="small" :options="borderFlag"/>
</SettingItem>
<SettingItem name="底部边框" :alone="true">
<n-select
v-model:value="(optionData as any).style.bottomBordered"
size="small"
:options="bottom_borderedFlag"
/>
</SettingItem>
<SettingItem name="列分割线" :alone="true">
<n-select v-model:value="(optionData as any).style.singleLine" size="small" :options="columnFlag"/>
</SettingItem>
<SettingItem name="行分割线" :alone="true">
<n-select v-model:value="(optionData as any).style.singleColumn" size="small" :options="lineFlag"/>
</SettingItem>
<SettingItem name="斑马条纹" :alone="true">
<n-select v-model:value="(optionData as any).style.striped" size="small" :options="stripedFlag"/>
</SettingItem>
<setting-item name="行高" :alone="true">
<n-input-number
v-model:value="optionData.style.lineHeight"
:min="12"
size="small"
placeholder="行高"
></n-input-number>
</setting-item>
<setting-item name="字体大小" :alone="true">
<n-input-number
v-model:value="optionData.style.fontSize"
:min="12"
size="small"
placeholder="字体大小"
></n-input-number>
</setting-item>
<setting-item name="边框宽度" :alone="true">
<n-input-number
v-model:value="optionData.style.borderWidth"
:min="0"
size="small"
placeholder="字体大小"
></n-input-number>
</setting-item>
<setting-item name="边框颜色" :alone="true">
<n-color-picker size="small" :modes="['rgb']" v-model:value="optionData.style.borderColor"></n-color-picker>
</setting-item>
<setting-item name="边框样式" :alone="true">
<n-select v-model:value="optionData.style.borderStyle" size="small" :options="borderStyleFlag"/>
</setting-item>
<SettingItem name="表格搜索(前端静态搜索)" :alone="true">
<n-select v-model:value="optionData.inputShow" size="small" :options="inputSelect"/>
</SettingItem>
</setting-item-box>
</collapse-item>
</template>
<script setup lang="ts">
import {PropType, watch, ref} from 'vue'
import {option} from './config'
import {CollapseItem, SettingItemBox, SettingItem} from '@/components/Pages/ChartItemSetting'
const page = [
{label: '2', value: 2},
{label: '5', value: 5},
{label: '10', value: 10},
{label: '15', value: 15},
{label: '30', value: 30}
]
const borderFlag = [
{label: '显示', value: 'on'},
{label: '不显示', value: 'off'}
]
const columnFlag = [
{label: '显示', value: 'off'},
{label: '不显示', value: 'on'}
]
const lineFlag = [
{label: '显示', value: 'off'},
{label: '不显示', value: 'on'}
]
const bottom_borderedFlag = [
{label: '显示', value: 'on'},
{label: '不显示', value: 'off'}
]
const stripedFlag = [
{label: '显示', value: 'on'},
{label: '不显示', value: 'off'}
]
const borderStyleFlag = [
{label: '实线边框', value: 'solid'},
{label: '虚线边框', value: 'dashed'},
{label: '点状边框', value: 'dotted'},
{label: '双线边框', value: 'double'}
]
const inputSelect = [
{label: '停用', value: 'none'},
{label: '启用', value: 'flex'}
]
const props = defineProps({
optionData: {
type: Object as PropType<typeof option>,
required: true
}
})
// const header = ref()
// const median = ref<string[]>([])
// props.optionData.dataset.dimensions.forEach(item => {
// median.value.push(item.title)
// })
// //string
// watch(
// () => props.optionData,
// () => {
// median.value = []
// props.optionData.dataset.dimensions.forEach(item => {
// median.value.push(item.title)
// })
// header.value = median.value.toString()
// },
// {
// deep: false,
// immediate: true
// }
// )
//columns
// watch([header], ([headerNew], [headerOld]) => {
// if (headerNew !== headerOld) {
// headerNew.split(',').forEach((item: string, index: number) => {
// if (index + 1 <= props.optionData.dataset.dimensions.length) {
// props.optionData.dataset.dimensions[index].title = headerNew.split(',')[index]
// }
// })
// }
// })
</script>
<style lang="scss" scoped>
.rows {
margin-bottom: 10px;
display: flex;
height: 28px;
line-height: 28px;
&:nth-last-child(1) {
margin-bottom: 0;
}
.columns:nth-child(1) {
flex: none;
width: 40%;
}
.columns:nth-child(2) {
flex: none;
width: 60%;
}
}
</style>

View File

@ -0,0 +1,74 @@
<template>
<setting-item-box name="设备UID" :alone="true">
<n-space vertical>
<n-space v-for="(item, i) in computeIds" :key="i" align="center" :wrap="false">
<n-input
:value="item.value"
@update:value="(v: string) => handleChange(v, i)"
placeholder="请输入设备UID"
size="small"
clearable
/>
<n-button @click="handleDelete(i)" circle size="tiny">
<template #icon>
<n-icon><CloseIcon /></n-icon>
</template>
</n-button>
<n-button v-if="i === computeIds.length - 1" @click="handleAdd" circle size="tiny">
<template #icon>
<n-icon><AddIcon /></n-icon>
</template>
</n-button>
<div v-else style="width: 22px"></div>
</n-space>
</n-space>
</setting-item-box>
</template>
<script lang="ts" setup>
import { SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
import { reactive, watch, toRefs, ToRefs } from 'vue';
import {customData as cd} from './config'
import {icon} from "@/plugins";
const props = defineProps(['customData', 'request'])
const { CloseIcon, AddIcon } = icon.ionicons5
type computeIdsItemType = {
id: string,
value: string
}
const computeIds: computeIdsItemType[] = reactive([])
const {customData} = toRefs(props) as ToRefs<{customData: typeof cd}>
watch(() => customData.value.ids, () => {
if(!customData.value.ids.length) customData.value.ids.push('')
let arr = customData.value.ids.map((item: string, i: number) => {
return {
id: `a_${i}`,
value: item
}
})
computeIds.splice(0, computeIds.length, ...arr)
}, {
deep: true,
immediate: true
})
const handleChange = (v: string, i: number) => {
customData.value.ids[i] = v
}
const handleAdd = () => {
customData.value.ids.push('')
}
const handleDelete = (i: number) => {
customData.value.ids.splice(i, 1)
}
</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 AirConditioningTableConfig: ConfigType = {
key: 'AirConditioningTable',
chartKey: 'VAirConditioningTable',
conKey: 'VCAirConditioningTable',
conDataKey: 'VCDAirConditioningTable',
title: '空调可视化表格',
category: ChatCategoryEnum.CUSTOMCOMPONENTS,
categoryName: ChatCategoryEnumName.CUSTOMCOMPONENTS,
package: PackagesCategoryEnum.CUSTOMCOMPONENTS,
chartFrame: ChartFrameEnum.COMMON,
image: 'tables_basic.png'
}

View File

@ -0,0 +1,220 @@
<template>
<div class="go-tables-basic" :style="{'--lineHeight': option.style.lineHeight + 'px'}">
<n-input
v-model:value="inputData"
placeholder="请输入信息"
:style="`display: ${inputShow}`"
style="margin-bottom: 5px; float: right; width: 240px"
>
<template #prefix>
<n-icon :component="SearchIcon" />
</template>
</n-input>
<n-data-table
style="box-sizing: border-box"
row-class-name="custom-row"
:style="`
width: ${w}px;
height: ${h}px;
font-size: ${option.style.fontSize}px;
border-width: ${option.style.border === 'on' ? option.style.borderWidth : 0}px;
border-color: ${option.style.borderColor};
border-style: ${option.style.borderStyle}`"
:bordered="option.style.border === 'on'"
:single-column="option.style.singleColumn === 'on'"
:single-line="option.style.singleLine === 'on'"
:bottom-bordered="option.style.bottomBordered === 'on'"
:striped="option.style.striped === 'on'"
:max-height="h"
size="small"
:columns="columns"
:data="filterData"
:pagination="pagination.show ? pagination : false"
/>
</div>
</template>
<script setup lang="ts">
import { computed, PropType, toRefs, watch, reactive, ref, onMounted, onUnmounted } from 'vue'
import type { Ref } from 'vue'
import { CreateComponentType } from '@/packages/index.d'
import { icon } from '@/plugins'
import {useChartCommonData} from "@/hooks";
import {useChartEditStore} from "@/store/modules/chartEditStore/chartEditStore";
import { isPreview } from '@/utils'
import { cloneDeep } from 'lodash'
import {publicInterface} from "@/api/path";
import { useOriginStore } from '@/store/modules/originStore/originStore'
import {selectTimeOptions} from "@/views/chart/ContentConfigurations/components/ChartData/index.d";
import {customData as cd} from './config'
const props = defineProps({
chartConfig: {
type: Object as PropType<CreateComponentType>,
required: true
}
})
const { SearchIcon } = icon.ionicons5
const { pagination, inputShow } = toRefs(props.chartConfig.option)
pagination.value.onChange = (page: number) => {
pagination.value.page = page
}
const { w, h } = toRefs(props.chartConfig.attr)
const option = reactive({
dataset: props.chartConfig.option.dataset,
style: props.chartConfig.option.style,
header: props.chartConfig.option.header
})
watch(() => props.chartConfig.option.header, v => {
option.header = v
}, {
immediate: true,
deep: true
})
let originData = ref({}) as {[k: string]: any}
const customData = computed(() => {
return props.chartConfig.customData as typeof cd
})
let data = computed(() => {
let arr:any = []
if(!Object.keys(originData.value).length) return arr
customData.value.ids.forEach((id: string) => {
let obj: any = {}
option.header.columns.forEach((col: any, i: number) => {
if(i === 0) {
obj[col.key] = originData.value[id]?.[col.key]
}
else {
let o = originData.value[id] ? originData.value[id] : null
if(o) {
let arr = [
...Object.values(o.aic),
...Object.values(o.aoc),
...Object.values(o.dic),
...Object.values(o.doc),
]
let t:any = arr.find((_: any) => _.node_name === col.key) || {}
obj[col.key] = t.value
}
else obj[col.key] = undefined
}
})
arr.push(obj)
})
return arr
})
//
const inputData = ref('')
//
const filterData = computed(() => {
return data.value.filter((item: any) => {
return Object.values(item).some((val: any) => {
return String(val).toLowerCase().includes(inputData.value.toLowerCase())
})
})
})
const columns = computed(() => {
let arr = option.header.columns.filter((_: any) => _.show).map((item: any) => {
let obj = item
obj.render = (row: any) => {
if(row[item.key] !== null && row[item.key] !== undefined) return row[item.key] + (item.unit ? item.unit : '')
else return '--'
}
return obj
})
return arr
})
const originStore = useOriginStore()
const systemConfig = originStore.getOriginStore.user.systemConfig
let levels: number[] = []
let confirm_statuses: string[] = []
if (systemConfig['active_alarm_level']) {
for (let i = 0; i < Number(systemConfig['active_alarm_level']); i++) {
levels.push(i + 1)
}
}
if (systemConfig['active_alarm_confirm_status']) {
confirm_statuses = [...JSON.parse(systemConfig['active_alarm_confirm_status'])]
}
const getData = () => {
if(!customData.value.ids.filter((_: string) => _).length) {
return
}
let params = {
confirm_statuses,
levels,
device: customData.value.ids.map((id: string) => {
return {dataType: 'point-title', dems_device_uid: id, is_all_point: true, points: []}
})
}
publicInterface('/dcim/dems/device_point', 'cinterface_realtime_data_get_by_uid_no_err_v2', params).then(res => {
if (res && res.data) {
originData.value = res.data
}
})
}
watch(() => customData.value.ids, () => {
getData()
}, {
immediate: true,
deep: true
})
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(() => {
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(() => {
getData()
}, number)
})
onUnmounted(() => {
if(timer) clearInterval(timer as number)
})
// useChartCommonData(props.chartConfig, useChartEditStore)
</script>
<style lang="scss" scoped>
::v-deep tr{
height: var(--lineHeight);
}
@include go('tables-basic') {
display: flex;
flex-direction: column;
gap: 15px;
align-items: flex-end;
}
</style>

View File

@ -9,6 +9,7 @@ import { MonitorRealTimeEventsConfig } from './MonitorRealTimeEvents'
import { DashboardConfig } from './Dashboard'
import { SystemRuntimeConfig } from './SystemRuntime'
import { VideoListConfig } from './VideoList'
import { AirConditioningTableConfig } from './AirConditioningTable'
export default [
// Theme1Config,
@ -22,4 +23,5 @@ export default [
DashboardConfig,
SystemRuntimeConfig,
VideoListConfig,
AirConditioningTableConfig,
]

View File

@ -14,20 +14,20 @@
</n-space>
</SettingItem>
</setting-item-box>
<setting-item-box name="链接" :alone="true">
<setting-item>
<n-input-group>
<n-select
v-model:value="optionData.linkHead"
size="small"
:style="{ width: '80%' }"
:options="linkHeadOptions"
/>
<n-input v-model:value="optionData.link" size="small"></n-input>
<n-button :disabled="!optionData.link" secondary size="small" @click="handleLinkClick">跳转</n-button>
</n-input-group>
</setting-item>
</setting-item-box>
<!-- <setting-item-box name="链接" :alone="true">-->
<!-- <setting-item>-->
<!-- <n-input-group>-->
<!-- <n-select-->
<!-- v-model:value="optionData.linkHead"-->
<!-- size="small"-->
<!-- :style="{ width: '80%' }"-->
<!-- :options="linkHeadOptions"-->
<!-- />-->
<!-- <n-input v-model:value="optionData.link" size="small"></n-input>-->
<!-- <n-button :disabled="!optionData.link" secondary size="small" @click="handleLinkClick">跳转</n-button>-->
<!-- </n-input-group>-->
<!-- </setting-item>-->
<!-- </setting-item-box>-->
</collapse-item>
<collapse-item name="样式" :expanded="true">

View File

@ -115,7 +115,8 @@ const commonData: commonDataType = {
const customEvent: CustomEventType = {
click: {
linkHead: 'http://',
link: ''
link: '',
isBlank: false
}
}

View File

@ -396,6 +396,7 @@ export interface CustomEventType {
click: {
linkHead: 'http://' | 'https://'
link: string
isBlank: boolean
}
}

View File

@ -10,7 +10,7 @@
<!-- <chart-event-advanced-handle></chart-event-advanced-handle>-->
<div style="display: flex;align-items:center;margin-top: 10px">
<div style="margin-right: 10px;width: 60px;">链接</div>
<n-input-group>
<n-input-group style="width: calc(100% - 70px)">
<n-select
v-model:value="targetData.customEvent.click.linkHead"
size="small"
@ -21,6 +21,10 @@
<n-button :disabled="!targetData.customEvent.click.link" secondary size="small" @click="handleClick">跳转</n-button>
</n-input-group>
</div>
<div style="display: flex;align-items:center;margin-top: 10px">
<div style="margin-right: 10px;width: 60px;">新开页面</div>
<n-switch size="small" v-model:value="targetData.customEvent.click.isBlank"/>
</div>
</n-collapse>
</template>
@ -30,6 +34,7 @@ import { ChartEventInteraction } from './components/ChartEventInteraction'
import { ChartEventAdvancedHandle } from './components/ChartEventAdvancedHandle'
import { ChartEventBaseHandle } from './components/ChartEventBaseHandle'
import { useTargetData } from '../hooks/useTargetData.hook'
import { postMessageToParent } from "@/utils";
const { targetData, chartEditStore } = useTargetData()
const showModal = ref(false)
@ -70,6 +75,14 @@ const finallyLink = computed(() => {
return targetData.value.customEvent.click.linkHead + (targetData.value as any).customEvent.click.link
})
const handleClick = () => {
window.open(finallyLink.value)
if(targetData.value.customEvent.click.isBlank) postMessageToParent({
type: 'windowOpen',
url: finallyLink.value,
openNew: true
})
else postMessageToParent({
type: 'windowOpen',
url: finallyLink.value,
})
}
</script>