Appearance
动态数据组件
一个动态数据组件,通过与数据缓存建立绑定关系来动态展示数据。本例SVGDataText组件便是一个动态数据组件,从名称就可以看出它是一个以SVG来构建的数据展示组件。
代码如下
vue
<template>
<div class="svg-container"
:style="customStyle"
@click="handleClick" @dblclick="handleDblClick" @mouseenter="showTooltip" @contextmenu="openContextMenu">
<el-tooltip class="item" :placement="tooltipPlacement"
:disabled="disableToolTip" :effect="tooltipEffect">
<template #content>
<div v-html="tooltipContent"></div>
</template>
<template #default>
<svg xmlns="http://www.w3.org/2000/svg" :width="element.style.width" :height="element.style.height">
<rect v-if="propValue.hasBackground" :x="0" :y="0" :width="element.style.width" :height="element.style.height"
:fill="showBackgroundColor" :stroke="propValue.strokeColor"
:stroke-width="propValue.strokeWidth"></rect>
<text :x="textX" :y="textY" :dx="propValue.dx"
:fill="showFontColor"
:dominant-baseline="textBaseLine"
:text-anchor="textAnchor"
:font-family="propValue.fontFamily"
:font-size="propValue.fontSize"
:font-weight="propValue.fontWeight"
>{{ showDataValue }}
</text>
</svg>
</template>
</el-tooltip>
</div>
</template>
<script setup>
import {
watch,
onMounted,
computed,
onBeforeUnmount,
ref,
onBeforeMount,
onUnmounted,
reactive,
onActivated,
onDeactivated
} from "vue";
import {useRoute, useRouter} from "vue-router";
import {useEventHandler,useDataCacheStore,createSmartAccessor,CreateCompContext} from "hflvloader";
import {CommonUtils} from "hflvloader";
import {useDynamicColor} from "@/common/useDynamicColor.js";
import ContextMenu from '@imengyu/vue3-context-menu'
import '@imengyu/vue3-context-menu/lib/vue3-context-menu.css'
import {useMenuHandler} from "@/common/useMenuHandler.js";
import {getCursorByEvents} from "@/common/useCursorManager.js";
const {
formatDate,
getGMT0Date,
getDate,
getWeek,
isNotEmpty,
isNumber,
isObject,
isValid,
hasOwnPathInObject,
getValueByPath
} = CommonUtils;
defineOptions({
name: 'SVGDataText'
});
const props = defineProps({
propValue: {
type: Object,
required: true,
default: () => {
}
},
element: {
type: Object,
default: () => {
}
},
designStatus: {
type: Boolean,
default: false
}
})
const propValue = computed(() => {
return props.propValue || {};
})
const element = computed(() => {
return props.element || {};
})
const events = computed(() => {
return element.value.events || [];
})
const dataCache = useDataCacheStore();
const smartData = createSmartAccessor(dataCache);
const eventHandler = ref(null);
const textX = ref(0);
const textY = ref(0);
const textBaseLine = ref('');
const textAnchor = ref('');
const hasTooltipContent = ref(false)
const showDataValue = ref('');
let dataValue = null;
let dataTime = null;
const tooltipContent = ref('');
let timer = null;
const bsData = smartData[propValue.value.dataCK];
const timeData = smartData[propValue.value.timeCK];
const _getValueFromObj = (obj, key, defaultVal) => {
if (hasOwnPathInObject(obj, key)) {
return getValueByPath(obj, key);
} else {
return defaultVal;
}
}
function parseBsData(value) {
if (isObject(value)) {
dataValue = _getValueFromObj(value, propValue.value.valueKey, null);
dataTime = _getValueFromObj(value, propValue.value.timeKey, null);
} else {
dataValue = value;
}
}
function dealBsData(value) {
parseBsData(value)
showDataValue.value = showValue(dataValue);
}
watch(() => bsData.value, (value) => {
if (isValid(value)) {
dealBsData(value)
}
})
watch(() => timeData.value, (value) => {
if (isNotEmpty(propValue.value.timeCK) && isValid(value)) {
if (isNotEmpty(propValue.value.timeKey)) {
if (isObject(value)) {
dataTime = _getValueFromObj(value, propValue.value.timeKey, null);
}
} else {
dataTime = value;
}
}
})
const {dynamicColor} = useDynamicColor(propValue, smartData, dataCache.rules, CommonUtils);
const showFontColor = computed(() => {
if (dynamicColor.dataColor) {
return dynamicColor.dataColor;
} else {
return propValue.value.fontColor ? propValue.value.fontColor : 'none';
}
})
const showBackgroundColor = computed(() => {
if (dynamicColor.backgroundColor) {
return dynamicColor.backgroundColor;
} else {
return propValue.value.fillColor ? propValue.value.fillColor : 'none';
}
})
const handleClick = (event) => {
eventHandler.value?.handleClick(event);
}
const handleDblClick = (event) => {
eventHandler.value?.handleDblClick(event);
}
watch(() => element.value.style.width, (value) => {
draw();
})
watch(() => element.value.style.height, (value) => {
draw();
})
watch(() => propValue.value.horizontalAlign, (value) => {
draw();
})
watch(() => propValue.value.offsetX, (value) => {
draw();
})
const disableToolTip = computed(() => {
// 设计状态不需要显示
if (props.designStatus) {
return true;
}
if (propValue.value.showTooltip) {
return !hasTooltipContent.value;
} else {
return true;
}
})
const tooltipPlacement = computed(() => {
return propValue.value.tooltipPlacement || 'bottom-start';
})
const tooltipEffect = computed(() => {
return propValue.value.tooltipEffect || 'dark';
})
const showTooltip = (event) => {
if (props.designStatus) {
return;
}
loadTipInfo();
if (isNotEmpty(tooltipContent.value)) {
hasTooltipContent.value = true;
} else {
hasTooltipContent.value = false;
}
}
const loadTipInfo = () => {
tooltipContent.value = '';
if (propValue.value.showTooltip) {
if (dataTime && isNumber(dataTime)) {
let _date = getDate(dataTime);
let _dateStr = '';
if (_date) {
_dateStr = formatDate(_date, 'yyyy-MM-dd HH:mm:ss');
let line1 = "数值:" + showDataValue.value;
let line2 = "时间:" + _dateStr;
tooltipContent.value = line1 + '<br/>' + line2;
}
} else {
let line1 = "数值:" + showDataValue.value;
tooltipContent.value = line1;
}
}
}
const draw = () => {
const {width, height} = props.element.style
drawChart(width, height)
}
const drawChart = (width, height) => {
let horizontalAlign = props.propValue.horizontalAlign || 'left';
let offsetX = props.propValue.offsetX || 0;
if ('center' == horizontalAlign) {
textAnchor.value = 'middle'
textX.value = width * 0.5
} else if ('right' == horizontalAlign) {
textX.value = width - offsetX
textAnchor.value = 'end'
} else {
textX.value = 0 + offsetX
textAnchor.value = 'start'
}
textBaseLine.value = 'central';
textY.value = height * 0.5
}
const showValue = (value) => {
let _showVal = value;
if (value == null) {
_showVal = propValue.value.nullShowTxt ?? '';
} else {
if (isNumber(value)) {
let fractionDigits = propValue.value.fractionDigits ?? 0;
let factor = propValue.value.factor;
if (isNumber(factor)) {
factor = Number(factor);
value = Number(value) * factor;
}
_showVal = dataFormatter(Number(value), fractionDigits);
} else {
if (_showVal + '' == 'undefined') {
_showVal = propValue.value.nullShowTxt ?? '';
}
}
if (isNotEmpty(propValue.value.unit)) {
_showVal = _showVal + propValue.value.unit;
}
}
return _showVal;
}
const dataFormatter = (val, fractionDigits) => {
let retVal = val;
if (val) {
const valueType = propValue.value.valueType;
// 字符串类型默认返回原始值
if (valueType == 2 && Number(val)) {
// 数值类型
if (fractionDigits && Number(fractionDigits)) {
fractionDigits = parseInt(fractionDigits);
} else {
fractionDigits = 0;
}
retVal = val.toFixed(fractionDigits);
} else if (valueType == 3) {
// 时间类型
let _date = getDate(val);
// 转换成GMT0时间
if (propValue.value.convertGMT0) {
_date = getGMT0Date(_date);
}
const _dateformat = propValue.value.dateformat || 'yyyy-MM-dd';
if (_date) {
if (isNotEmpty(propValue.value.weekMode)) {
retVal = getWeek(_date, isNumber(propValue.value.weekMode) ? Number(propValue.value.weekMode) : 0);
} else {
retVal = formatDate(_date, _dateformat);
}
}
}
}
return retVal;
}
const _clearInterval = () => {
if (timer) {
clearInterval(timer);
timer = null;
}
}
const router = useRouter();
const route = useRoute();
const compContext = CreateCompContext(propValue, element, router, route);
onBeforeMount(() => {
eventHandler.value = useEventHandler(events.value, props.designStatus, compContext);
eventHandler.value?.beforeMount();
})
onMounted(() => {
draw();
timer = setInterval(() => {
if (isValid(bsData.value)) {
dealBsData(bsData.value);
if(timer){
clearInterval(timer);
timer = null;
}
}
}, 1000)
eventHandler.value?.mounted();
})
onBeforeUnmount(() => {
_clearInterval();
eventHandler.value?.beforeUnmount();
})
onUnmounted(() => {
eventHandler.value?.unmounted();
})
onActivated(() => {
eventHandler.value?.activated();
})
onDeactivated(() => {
eventHandler.value?.deactivated();
})
const {processMenus} = useMenuHandler();
const openContextMenu = (_event) => {
_event.preventDefault();
let _menuItems = propValue.value.menuItems || [];
if (CommonUtils.isArray(_menuItems) && _menuItems.length > 0) {
let _menuList = [];
_menuList = processMenus(_menuItems, function (item, event) {
eventHandler.value?.handleContextMenu(_event, item.value);
})
ContextMenu.showContextMenu({
x: _event.x,
y: _event.y,
items: _menuList
})
}
}
const customStyle = computed(() => {
let style = {}
style['cursor'] = getCursorByEvents(events.value);
return style;
})
</script>
<style lang="scss" scoped>
.svg-container {
width: 100%;
height: 100%;
svg {
width: 100%;
height: 100%;
}
}
</style>