|
|
const e="c4c1eab4-b5b7-40f3-8ed9-4f84a96a0a99",t="custom-horizontal-barchart",a="双y轴柱状图",n='<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1697419854399" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8090" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M480 192H160a32 32 0 0 1 0-64h320a32 32 0 0 1 0 64z" fill="#4A8BFE" p-id="8091"></path><path d="M608 288H160a32 32 0 0 1 0-64h448a32 32 0 0 1 0 64z" fill="#3BD5B3" p-id="8092"></path><path d="M608 496H160a32 32 0 0 1 0-64h448a32 32 0 0 1 0 64z" fill="#4A8BFE" p-id="8093"></path><path d="M736 592H160a32 32 0 0 1 0-64h576a32 32 0 0 1 0 64zM864 896H160a32 32 0 0 1 0-64h704a32 32 0 0 1 0 64z" fill="#3BD5B3" p-id="8094"></path><path d="M736 800H160a32 32 0 0 1 0-64h576a32 32 0 0 1 0 64z" fill="#4A8BFE" p-id="8095"></path></svg>',o="svg",i="动态",r="图表组件",c=!1,p=!0,l="时间",s=`{"type":"page","id":"u:270584784ce1","name":"page1","asideResizor":false,"style":{"boxShadow":" 0px 0px 0px 0px transparent"},"pullRefresh":{"disabled":true},"body":[{"type":"tabs","name":"tab","tabs":[{"title":"样式","icon":"fa fa-th-large","body":[{"type":"form","title":"","name":"basicPropForm","body":[{"type":"input-text","label":"名称","name":"nodeAlias","id":"u:6b126f0520cb","size":"full","mode":"horizontal","inputControlClassName":"w-100","className":"m-b"},{"type":"input-text","label":"ID ","name":"id","id":"u:6232710ac003","size":"full","mode":"horizontal","inputControlClassName":"w-100","className":"m-b"},{"type":"grid","id":"u:c605398a724c","className":"m-b","columns":[{"body":[{"type":"input-number","label":"宽度","name":"width","keyboard":true,"id":"u:dcc0c21d16f6","step":1,"suffix":"px","placeholder":"组件左边距","size":"full","mode":"horizontal","className":"m-b","value":200,"labelAlign":"left","precision":2,"inputClassName":"w-full","labelClassName":"w-8"}],"id":"u:14cc19d6ffb0","md":6},{"body":[{"type":"input-number","label":"高度","name":"height","keyboard":true,"id":"u:cd6fdff9ca88","step":1,"suffix":"px","placeholder":"组件上边距","size":"full","mode":"horizontal","className":"m-b","value":200,"labelAlign":"left","precision":2,"inputClassName":"w-full","labelClassName":"w-8"}],"id":"u:4931801ca9b8","md":6}]},{"type":"grid","id":"u:da449a94908a","className":"m-b","columns":[{"body":[{"type":"input-number","label":"X 轴","name":"x","keyboard":true,"id":"u:29852d093d9d","step":1,"suffix":"px","placeholder":"组件左边距","size":"full","mode":"horizontal","className":"m-b","value":200,"labelAlign":"left","precision":2,"inputClassName":"w-full","labelClassName":"w-8"}],"id":"u:1b561d652acc","md":6},{"body":[{"type":"input-number","label":"Y 轴","name":"y","keyboard":true,"id":"u:dc8c1daed8ed","step":1,"suffix":"px","placeholder":"组件上边距","size":"full","mode":"horizontal","className":"m-b","value":200,"labelAlign":"left","precision":2,"inputClassName":"w-full","labelClassName":"w-8"}],"id":"u:9672575193ac","md":6}]},{"type":"grid","id":"u:a332a7bf83c1","className":"m-b","columns":[{"body":[{"type":"input-number","label":"旋转","name":"rotation","id":"u:f6a2dbb518f9","placeholder":"组件旋转角度","mode":"horizontal","size":"full","className":"","keyboard":true,"step":1,"suffix":"deg","value":0,"labelAlign":"left","inputClassName":"w-full"}],"id":"u:646cd98b7955","md":6},{"body":[{"type":"input-number","label":"透明","name":"opacity","id":"u:cf80f59d8d42","placeholder":"组件透明度","mode":"horizontal","size":"full","className":"m-b","keyboard":true,"step":0,"suffix":"","value":1,"inputClassName":"w-full","precision":2}],"id":"u:51ddf54ac749","md":6}],"gap":""},{"type":"grid","columns":[],"id":"u:235f153e5ad5","className":"m-b"},{"type":"grid","columns":[{"body":[{"type":"input-color","label":"名称颜色","name":"nameColor","id":"u:10e95c495126","format":"hex","mode":"horizontal","inputClassName":"w-full myColorPick"}],"id":"u:1149609
"nodes": [ { "id": "26f2607a-db35-4f34-8653-a4fc11fafc7f", "type": "custom-horizontal-barchart", "x": 200, "y": 200, "text": { "value": "", "x": 200, "y": 200 }, "properties": { "id": "26f2607a-db35-4f34-8653-a4fc11fafc7f", "width": 500, "height": 200, "x": 200, "y": 200, "rotation": 0, "opacity": 1, "codeConfig": "return option", "nodeAlias": "双轴柱图", "showDefaultValue": false, "showUnit": false, "valueColor": "#000000", "fontSize": 12, "maxValueBg": "#181f44", "valueFrontBg": "linear-gradient(to right, rgb(57,89,255,1), rgb(46,200,207,1))", "nameColor": "#000000", "itemColor": "#000000", "tooltipColor": "#ffffff", "tooltipBack": "#3d2fd7", "tooltipBorderColor": "#3d2fd7", "dynamic": { "normalData": { "dataPoint": "", "compareType": "", "conditionVariables": [], "defaultValue": "", "unit": "", "renderIntervalEnabled": true, "yAxisNameType": "", "dataShowTypes": "oneThingManyAttr", "radios": "oneThingManyAttr" } } } } ]}`,javascript:`const { createApp, createVNode, render } = Vue;const app = createApp({})
const timeArr = new Array(24).fill('');const totals = [];timeArr.forEach((i, index) => { const t = window.dayjs().hour(index).valueOf(); totals.push({ val: Math.random(1000) * 100, ts: t, attrKey: "A29" }) });const defaultSocketValue = []
const HoriBarChart = { template: \`<div :id="chartId" :style="getStyle" class="horibar-chart-box">
<div v-for="item in totalDatas" class="horibar-item-row" :style="getRowStyle(totalDatas.length)" @mouseenter="mouseEnterHandler" @mousemove="(e) => hoverTooltipDebounced(e, item)" @mouseleave="mouseLeaveHandler"> <div class="horibar-name" :style="{color: nameColor}">{{item.name}}</div> <div class="horibar-value-wrapper"> <div class="horibar-value" :style="getValueStyle(item.value, item.maxVal, valueFrontBg)"></div> <div class="horibar-bg" :style="getBgStyle"></div> </div> <div class="horibar-value-show" :style="{color: valueColor}">{{item.formatVal}}</div> </div> <div v-show="showTooltip" :style="getTooltipStyle" class="hori-tooltip"> <span>{{currentRow.name}} :</span> <span> {{currentRow.value}}</span> <span> {{ unit }}</span> </div> </div>\`, props: { chartId: { type: String, default: '' }, historyDatas: { type: Array, default: () => [] }, width: { type: Number, default: 350 }, height: { type: Number, default: 150 }, codeConfig: { type: String, default: '' }, nameColor: { type: String, default: '' }, valueColor: { type: String, default: '' }, valueFrontBg: { type: String, default: '' }, maxValueBg: { type: String, default: '' }, tooltipColor: { type: String, default: '' }, tooltipBack: { type: String, default: '' }, tooltipBorderColor: { type: String, default: '' }, unit: { type: String, default: '' }, dataShowTypes: { type: String, default: '' }, yAxisNameType: { type: String, default: '' }, apiid: { type: String, default: '' } }, computed: { getStyle() { return { width: \`\${this.width}px\`,
height: \`\${this.height}px\`
} }, getRowStyle: () => (len) => { const ratio = 1 / (len + 3) * len / len * 100 + '%' return { flex: \`0 0 \${ratio}\`,
} }, getValueStyle: () => (val, maxVal, valueFrontBg) => { return { width: val / maxVal * 100 + '%', background: valueFrontBg } }, getBgStyle() { return { 'background-color': this.maxValueBg } }, }, setup(props) { const { onMounted, computed, nextTick, ref, toRefs, watch, reactive } = Vue; const { historyDatas, codeConfig, dataShowTypes, yAxisNameType, apiid } = toRefs(props)
const filterDatas = (api, yAxisName, dataArr, datas) => { if (api) { const infos = window.totalDeviceInfos[api]; const thing = infos[datas[0].thingCode]; const attrName = thing.attrs[datas[0].attrKey].name if (dataShowTypes.value === 'oneThingOneAttr') { switch (yAxisNameType.value) { case "thingName": yAxisName.push(thing.entityName); dataArr.push(datas[0].val); return; case "thingCode": yAxisName.push(datas[0].thingCode); dataArr.push(datas[0].val); return; case "attrName": yAxisName.push(attrName); dataArr.push(datas[0].val); return; case "attrCode": yAxisName.push(datas[0].attrKey); dataArr.push(datas[0].val); return; case "thingNameAttrName": const thingName = thing.entityName; yAxisName.push(thingName + ' ' + attrName); dataArr.push(datas[0].val); return; case "thingCodeAttrCode": const thingCode = datas[0].thingCode; const attrCode = datas[0].attrKey; yAxisName.push(thingCode + ' ' + attrCode); dataArr.push(datas[0].val); return; } } else if (dataShowTypes.value === 'oneThingManyAttr') { const datasGrouped = window._.groupBy(datas, 'attrKey'); switch (yAxisNameType.value) { case "attrName": for (const key in datasGrouped) { const serieData = datasGrouped[key]; const thing = infos[serieData[0].thingCode]; const attrName = thing.attrs[serieData[0].attrKey].name yAxisName.push(attrName); dataArr.push(serieData[0].val); } return; case "attrCode": for (const key in datasGrouped) { const serieData = datasGrouped[key]; yAxisName.push(serieData[0].attrKey); dataArr.push(serieData[0].val); } return; case "thingNameAttrName": for (const key in datasGrouped) { const serieData = datasGrouped[key]; const thing = infos[serieData[0].thingCode]; const thingName = thing.entityName; const attrName = thing.attrs[serieData[0].attrKey].name yAxisName.push(thingName + ' ' + attrName); dataArr.push(serieData[0].val); } return; case "thingCodeAttrCode": for (const key in datasGrouped) { const serieData = datasGrouped[key]; const thingCode = serieData[0].thingCode; const attrCode = serieData[0].attrKey; yAxisName.push(thingCode + ' ' + attrCode); dataArr.push(serieData[0].val); } return; } } else if (dataShowTypes.value === 'manyThingManyAttr') { switch (yAxisNameType.value) { case "thingNameAttrName": datas.forEach((val) => { const thing = infos[val.thingCode]; const thingName = thing.entityName; const attrName = thing.attrs[val.attrKey].name yAxisName.push(thingName + ' ' + attrName); dataArr.push(val.val); }) return; case "thingCodeAttrCode": datas.forEach((val) => { yAxisName.push(val.thingCode + ' ' + val.attrKey); dataArr.push(val.val); }) return; } } else if (dataShowTypes.value === 'manyThingOneAttr') { const datasGrouped = window._.groupBy(datas, 'thingCode'); switch (yAxisNameType.value) { case "thingName": for (const key in datasGrouped) { const serieData = datasGrouped[key]; const thing = infos[serieData[0].thingCode]; yAxisName.push(thing.entityName); dataArr.push(serieData[0].val); } return; case "thingCode": for (const key in datasGrouped) { const serieData = datasGrouped[key]; yAxisName.push(key); dataArr.push(serieData[0].val); } return; case "thingNameAttrName": for (const key in datasGrouped) { const serieData = datasGrouped[key]; const thing = infos[serieData[0].thingCode]; const thingName = thing.entityName; const attrName = thing.attrs[serieData[0].attrKey].name yAxisName.push(thingName + ' ' + attrName); dataArr.push(serieData[0].val); } return; case "thingCodeAttrCode": for (const key in datasGrouped) { const serieData = datasGrouped[key]; const thingCode = serieData[0].thingCode; const attrCode = serieData[0].attrKey; yAxisName.push(thingCode + ' ' + attrCode); dataArr.push(serieData[0].val); } return; } } } }
const totalDatas = ref([]); const totalBackground = ref([]); const initChart = (datas) => { // 基于准备好的dom,初始化echarts实例
if (datas) { let dataArr = []; let databackground = []; let yAxisName = []; if (datas.length > 0) { filterDatas(apiid.value, yAxisName, dataArr, datas); const maxVal = Math.max(...dataArr); databackground = dataArr.map(() => maxVal); } else { dataArr = [50000000, 22000000, 10000000, 5000000, 1]; databackground = [50000000, 50000000, 50000000, 50000000, 50000000]; yAxisName = ['大米', '玉米', '蔬菜', '鸡蛋', '坚果']; }
totalDatas.value = dataArr.map((item, index) => { const formatVal = dataArr[index] > 10000 ? (dataArr[index] / 10000).toFixed(2) + '万' : dataArr[index]; return { name: yAxisName[index], value: dataArr[index], formatVal, maxVal: databackground[index] } }) totalBackground.value = databackground; setTimeout(() => { const bgs = document.getElementById(props.chartId).querySelectorAll('.horibar-bg'); bgs.forEach((bg) => { bg.style.transform = 'scaleX(1)'; }) const vals = document.getElementById(props.chartId).querySelectorAll('.horibar-value'); vals.forEach((val) => { val.style.transform = 'scaleX(1)'; }) }, 100)
} }
watch(historyDatas, (val) => { if (val) { nextTick(() => { initChart(val); }) } }, { immediate: true })
const currentRow = ref({}); const showTooltip = ref(false); const tooltipPos = ref({ top: -1000, left: -1000, }) let parentX = 0; let parentY = 0; onMounted(() => { nextTick(() => { const parentDom = document.getElementById(props.chartId); const { x, y } = parentDom.getBoundingClientRect(); parentX = x; parentY = y; }) }) const hoverTooltip = (e, row) => { if (!showTooltip.value) { showTooltip.value = true; }; const parentDom = document.getElementById(props.chartId); const { height } = parentDom.querySelector('.hori-tooltip').getBoundingClientRect(); let hovertipHeight = height; currentRow.value = row; const isCloseBottom = Math.abs(props.height - (e.y - parentY)) < hovertipHeight; tooltipPos.value = { left: e.x - parentX, top: isCloseBottom ? e.y - parentY - hovertipHeight : e.y - parentY, } } const hoverTooltipDebounced = window._.throttle(hoverTooltip, 500);
const getTooltipStyle = computed(() => { const pos = tooltipPos.value; return { position: 'absolute', top: pos.top + 'px', left: pos.left + 'px', padding: '15px 15px', color: props.tooltipColor, 'background-color': props.tooltipBack, border: \`1px solid \${props.tooltipBorderColor}\`,
'border-radius': '5px', 'z-index': 9999, transition: 'all 0.25s ease-in-out' } })
const mouseEnterHandler = window._.throttle(() => { showTooltip.value = true; }, 200)
const mouseLeaveHandler = window._.throttle(() => { showTooltip.value = false; }, 200)
return reactive({ totalDatas, totalBackground, hoverTooltipDebounced, currentRow, tooltipPos, showTooltip, getTooltipStyle, mouseEnterHandler, mouseLeaveHandler, }) }}
class CustomHoriBarChartNode extends HtmlResize.view { chartRendered = false historyDatas = [] oldProperties = {}
setHtml(rootEl) { if (!rootEl) return; const { properties, width, height, } = this.props.model; const { nodeAlias, codeConfig, nameColor, valueColor, valueFrontBg, maxValueBg, tooltipColor, tooltipBack, tooltipBorderColor } = properties; const { normalData } = properties.dynamic || {}; const { unit, dataShowTypes, yAxisNameType } = normalData || {};
const el = document.createElement('div'); rootEl.innerHTML = ''; const instance = createVNode(HoriBarChart, { name: nodeAlias, chartId: \`horibar-\${properties.id}\`,
historyDatas: this.historyDatas, width, height, codeConfig, nameColor, valueColor, valueFrontBg, maxValueBg, tooltipColor, tooltipBack, tooltipBorderColor, unit, dataShowTypes, yAxisNameType, apiid: properties.apiid }) instance.appContext = app._context render(instance, el) rootEl.appendChild(el); }
sameProps(properties) { const isSame = window._.isEqual(this.oldProperties, properties); if (isSame) return true; this.oldProperties = properties; return false }
filterHistoryData(thingCodeArr, dataPointArr, apiid, renderIntervalEnabled) { if (dataPointArr && dataPointArr.length > 0) { let datas = [] if (renderIntervalEnabled) { datas = window.totalHistoryDatas[apiid]; } else { if (window.globalDashboardDatas[apiid]) { datas = window.globalDashboardDatas[apiid].values; } } if (datas && datas.length > 0) { const gotValues = datas.filter((val) => thingCodeArr.includes(val.thingCode) && dataPointArr.includes(val.attrKey)) this.historyDatas = gotValues this.chartRendered = true; } } }
// 生命周期 支持重写内容, 但格式需一致
shouldUpdate() { const { properties } = this.props.model; const { apiid } = properties; const { normalData } = properties.dynamic || {}; const { thingCodeArr, dataPointArr, defaultValue } = normalData || {}
if (normalData && !normalData.dataPoint && !normalData.defaultValue) { this.historyDatas = defaultSocketValue; return true } else if (normalData && !normalData.dataPoint && normalData.defaultValue) { this.historyDatas = JSON.parse(defaultValue); return true }
const propertiesBack = window._.cloneDeep(properties); if (propertiesBack.dynamic.normalData) { propertiesBack.dynamic.normalData.defaultValue = ''; if (this.sameProps(propertiesBack) && this.chartRendered) { return false } if (dataPointArr && apiid && !this.chartRendered) { this.filterHistoryData(thingCodeArr, dataPointArr, apiid, normalData.renderIntervalEnabled); return true; } } return true; }
updateHtml() { this.setHtml(this.rootEl); }
componentDidMount() { // 防止拖动时候频繁渲染图表
this.updateHtmlDebounced = window._.debounce(this.updateHtml.bind(this), 500); const { properties } = this.props.model; const { normalData } = properties.dynamic || {}; const { renderInterval, dataPointArr, thingCodeArr } = normalData || {}; if (this.shouldUpdate()) { this.setHtml(this.rootEl); }
let inters = parseInt(renderInterval || '30000') if (normalData && !normalData.renderIntervalEnabled) { inters = 1000 } setInterval(() => { if (window.totalHistoryDatas[properties.apiid]) { this.filterHistoryData(thingCodeArr, dataPointArr, properties.apiid, normalData.renderIntervalEnabled); this.setHtml(this.rootEl); } }, inters) }
componentDidUpdate() { if (this.shouldUpdate()) { this.updateHtmlDebounced(); } }}
class CustomHoriBarChartModel extends HtmlResize.model { initNodeData(data) { // 自定义组件,需最开始重置一下text 。
data.text = { value: "", x: data.x, y: data.y, };
super.initNodeData(data); const { properties } = this; this.width = properties.width || 80; this.height = properties.height || 35; this.text.editable = false; // 不允许文本被编辑
}
setAttributes() { // 自定义组件需重置 text
const { x, y, properties } = this; const { textHorizontalMove = 0, textVerticalMove = 0 } = properties; this.text = { ...this.text, x: x + textHorizontalMove, y: y + textVerticalMove, value: "", } }}
lf.register({ type: 'custom-horizontal-barchart', view: CustomHoriBarChartNode, model: CustomHoriBarChartModel,})`,css:`.horibar-chart-box {\r display: flex;\r flex-direction: column;\r justify-content: space-between;\r position: relative;\r}\r.horibar-chart-box .horibar-item-row {\r display: flex;\r}\r.horibar-chart-box .horibar-item-row .horibar-name {\r flex: 0 0 auto;\r overflow: hidden;\r text-overflow: ellipsis;\r white-space: nowrap;\r display: flex;\r align-items: center;\r padding: 0 5px;\r}\r.horibar-chart-box .horibar-item-row .horibar-value-wrapper {\r flex: 1;\r position: relative;\r}\r.horibar-chart-box .horibar-item-row .horibar-value-wrapper .horibar-value {\r position: absolute;\r top: 0;\r bottom: 0;\r height: 100%;\r z-index: 1;\r border-radius: 20px;\r transform: scaleX(0);\r transform-origin: 0%;\r transition: all 0.35s ease-in-out;\r}\r.horibar-chart-box .horibar-item-row .horibar-value-wrapper .horibar-bg {\r width: 100%;\r height: 100%;\r border-radius: 20px;\r transform: scaleX(0);\r transform-origin: 0%;\r transition: all 0.2s ease-in-out;\r}\r\r.horibar-chart-box .horibar-item-row .horibar-value-show {\r flex: 0 0 70px;\r overflow: hidden;\r text-overflow: ellipsis;\r white-space: nowrap;\r display: flex;\r align-items: center;\r padding-left: 5px;\r}`,fakeData:""},u={id:e,name:t,aliasName:a,image:n,imageType:o,groupName:i,groupType:r,isRemote:!1,isDefault:!0,sectionType:l,config:s,files:d};export{a as aliasName,s as config,u as default,d as files,i as groupName,r as groupType,e as id,n as image,o as imageType,p as isDefault,c as isRemote,t as name,l as sectionType};
|