de/core/core-frontend/src/utils/canvasStyle.ts
2024-08-15 16:24:23 +08:00

536 lines
15 KiB
Java

import { cos, sin } from '@/utils/translate'
import {
DEFAULT_COLOR_CASE,
DEFAULT_COLOR_CASE_DARK
} from '@/views/chart/components/editor/util/chart'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { useEmitt } from '@/hooks/web/useEmitt'
import { merge } from 'lodash-es'
const dvMainStore = dvMainStoreWithOut()
export const LIGHT_THEME_COLOR_MAIN = '#000000'
export const LIGHT_THEME_COLOR_SLAVE1 = '#CCCCCC'
export const LIGHT_THEME_DASHBOARD_BACKGROUND = '#f5f6f7'
export const LIGHT_THEME_COMPONENT_BACKGROUND = '#FFFFFF'
export const DARK_THEME_COLOR_MAIN = '#FFFFFF'
export const DARK_THEME_COLOR_SLAVE1 = '#858383'
export const DARK_THEME_DASHBOARD_BACKGROUND = '#030B2E'
export const DARK_THEME_COMPONENT_BACKGROUND = '#131E42'
export const DARK_THEME_COMPONENT_BACKGROUND_BACK = '#5a5c62'
export function getStyle(style, filter = []) {
const needUnit = [
'fontSize',
'width',
'height',
'top',
'left',
'borderWidth',
'letterSpacing',
'borderRadius',
'margin',
'padding'
]
const result = {}
Object.keys(style).forEach(key => {
if (!filter.includes(key)) {
if (key !== 'rotate') {
result[key] = style[key]
if (key) {
if (key === 'backgroundColor') {
result[key] = colorRgb(style[key], style.opacity)
}
if (key === 'fontSize' && result[key] < 12) {
result[key] = 12
}
if (needUnit.includes(key)) {
result[key] += 'px'
}
}
} else {
result['transform'] = key + '(' + style[key] + 'deg)'
}
}
})
if (result['backgroundColor'] && (result['opacity'] || result['opacity'] === 0)) {
delete result['opacity']
}
return result
}
// 获取一个组件旋转 rotate 后的样式
export function getComponentRotatedStyle(style) {
style = { ...style }
if (style.rotate !== 0) {
const newWidth = style.width * cos(style.rotate) + style.height * sin(style.rotate)
const diffX = (style.width - newWidth) / 2 // 旋转后范围变小是正值,变大是负值
style.left += diffX
style.right = style.left + newWidth
const newHeight = style.height * cos(style.rotate) + style.width * sin(style.rotate)
const diffY = (newHeight - style.height) / 2 // 始终是正
style.top -= diffY
style.bottom = style.top + newHeight
style.width = newWidth
style.height = newHeight
} else {
style.bottom = style.top + style.height
style.right = style.left + style.width
}
return style
}
export function colorRgb(color, opacity) {
const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/
let sColor = color
if (sColor && reg.test(sColor)) {
sColor = sColor.toLowerCase()
if (sColor.length === 4) {
let sColorNew = '#'
for (let i = 1; i < 4; i += 1) {
sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1))
}
sColor = sColorNew
}
// 处理六位的颜色值
const sColorChange = []
for (let i = 1; i < 7; i += 2) {
sColorChange.push(parseInt('0x' + sColor.slice(i, i + 2)))
}
if (opacity || opacity === 0) {
return 'rgba(' + sColorChange.join(',') + ',' + opacity + ')'
} else {
return 'rgba(' + sColorChange.join(',') + ')'
}
} else {
return sColor
}
}
export const customAttrTrans = {
basicStyle: ['barWidth', 'lineWidth', 'lineSymbolSize'],
tableHeader: ['tableTitleFontSize', 'tableTitleHeight'],
tableCell: ['tableItemFontSize', 'tableItemHeight'],
misc: [
'nameFontSize',
'valueFontSize',
'spaceSplit', // 间隔
'scatterSymbolSize', // 气泡大小,散点图
'radarSize', // 雷达占比
'wordSizeRange',
'wordSpacing'
],
label: {
fontSize: '',
seriesLabelFormatter: ['fontSize']
},
tooltip: {
fontSize: '',
seriesTooltipFormatter: ['fontSize']
},
indicator: ['fontSize', 'suffixFontSize'],
indicatorName: ['fontSize', 'nameValueSpacing']
}
export const customStyleTrans = {
text: ['fontSize'],
legend: ['fontSize'],
xAxis: {
fontSize: 'fontSize',
axisLabel: ['fontSize'],
splitLine: {
lineStyle: ['width']
}
},
yAxis: {
fontSize: 'fontSize',
axisLabel: ['fontSize'],
splitLine: {
lineStyle: ['width']
}
},
yAxisExt: {
fontSize: 'fontSize',
axisLabel: ['fontSize'],
splitLine: {
lineStyle: ['width']
}
},
misc: {
fontSize: 'fontSize',
axisLine: {
lineStyle: ['width']
},
axisTick: {
lineStyle: ['width']
},
axisLabel: ['margin', 'fontSize'],
splitLine: {
lineStyle: ['width']
}
}
}
export const THEME_STYLE_TRANS_MAIN_BACK = {
legend: {
textStyle: ['color']
},
xAxis: {
nameTextStyle: ['color'],
axisLabel: ['color'],
splitLine: {
lineStyle: ['color']
}
},
yAxis: {
nameTextStyle: ['color'],
axisLabel: ['color'],
splitLine: {
lineStyle: ['color']
}
},
yAxisExt: {
nameTextStyle: ['color'],
axisLabel: ['color'],
splitLine: {
lineStyle: ['color']
}
},
split: {
name: ['color'],
axisLine: {
lineStyle: ['color']
},
axisTick: {
lineStyle: ['color']
},
axisLabel: ['color'],
splitLine: {
lineStyle: ['color']
}
}
}
export const THEME_STYLE_TRANS_MAIN = {
legend: ['color'],
xAxis: {
// 一级属性直接字符串
color: 'color',
axisLabel: ['color']
},
yAxis: {
color: '',
axisLabel: ['color']
},
yAxisExt: {
color: '',
axisLabel: ['color']
},
misc: {
color: 'color',
axisTick: {
lineStyle: ['color']
},
axisLabel: ['color']
}
}
export const THEME_STYLE_TRANS_SLAVE1 = {
xAxis: {
splitLine: {
lineStyle: ['color']
}
},
yAxis: {
splitLine: {
lineStyle: ['color']
}
},
yAxisExt: {
splitLine: {
lineStyle: ['color']
}
},
misc: {
splitLine: {
lineStyle: ['color']
},
axisLine: {
lineStyle: ['color']
}
}
}
export const THEME_ATTR_TRANS_MAIN = {
label: ['color'],
tooltip: ['color'],
indicatorName: ['color']
}
export const THEME_ATTR_TRANS_MAIN_SYMBOL = {
label: ['color']
}
export const THEME_ATTR_TRANS_SLAVE1_BACKGROUND = {
tooltip: ['backgroundColor']
}
// 移动端特殊属性
export const mobileSpecialProps = {
lineWidth: 2, // 线宽固定值
lineSymbolSize: 8 // 折点固定值
}
export function getScaleValue(propValue, scale) {
if (propValue instanceof Array) {
propValue.forEach((v, i) => {
const val = Math.round(v * scale)
propValue[i] = val > 1 ? val : 1
})
return propValue
}
const propValueTemp = Math.round(propValue * scale)
return propValueTemp > 1 ? propValueTemp : 1
}
export const THEME_ATTR_TRANS_ARR_MAIN = {
label: {
seriesLabelFormatter: {
isArray: []
}
}
}
export function seriesAdaptor(template, color) {
template.label?.seriesLabelFormatter?.forEach(series => {
series['color'] = color
})
template.label?.seriesTooltipFormatter?.forEach(series => {
series['color'] = color
})
}
export function recursionTransObj(template, infoObj, scale, terminal) {
for (const templateKey in template) {
// 如果是数组 进行赋值计算
if (template[templateKey] instanceof Array) {
// 词云图的大小区间,不需要缩放
template[templateKey]
.filter(field => field !== 'wordSizeRange')
.forEach(templateProp => {
if (
infoObj[templateKey] &&
(infoObj[templateKey][templateProp] || infoObj[templateKey].length)
) {
// 移动端特殊属性值设置
if (terminal === 'mobile' && mobileSpecialProps[templateProp] !== undefined) {
infoObj[templateKey][templateProp] = mobileSpecialProps[templateProp]
} else {
// 数组依次设置
if (infoObj[templateKey] instanceof Array) {
infoObj[templateKey].forEach(v => {
v[templateProp] = getScaleValue(v[templateProp], scale)
})
} else {
infoObj[templateKey][templateProp] = getScaleValue(
infoObj[templateKey][templateProp],
scale
)
}
}
}
})
} else if (typeof template[templateKey] === 'string') {
// 一级字段为字符串直接赋值
infoObj[templateKey] = getScaleValue(infoObj[templateKey], scale)
} else {
// 如果是对象 继续进行递归
if (infoObj[templateKey]) {
recursionTransObj(template[templateKey], infoObj[templateKey], scale, terminal)
}
}
}
}
export function recursionThemTransObj(template, infoObj, color) {
for (const templateKey in template) {
// 如果是数组 进行赋值计算
if (template[templateKey] instanceof Array) {
template[templateKey].forEach(templateProp => {
if (infoObj[templateKey]) {
infoObj[templateKey][templateProp] = color
}
})
} else if (typeof template[templateKey] === 'string') {
// 一级字段为字符串直接赋值
infoObj[templateKey] = color
} else {
// 如果是对象 继续进行递归
if (infoObj[templateKey]) {
recursionThemTransObj(template[templateKey], infoObj[templateKey], color)
}
}
}
}
export function componentScalePublic(chartInfo, heightScale, widthScale) {
const scale = Math.min(heightScale, widthScale)
// attr 缩放转换
recursionTransObj(this.customAttrTrans, chartInfo.customAttr, scale, null)
// style 缩放转换
recursionTransObj(this.customStyleTrans, chartInfo.customStyle, scale, null)
return chartInfo
}
export function adaptCurTheme(customStyle, customAttr) {
const canvasStyle = dvMainStore.canvasStyleData
const themeColor = canvasStyle.dashboard.themeColor
if (themeColor === 'light') {
recursionThemTransObj(THEME_STYLE_TRANS_MAIN, customStyle, LIGHT_THEME_COLOR_MAIN)
recursionThemTransObj(THEME_STYLE_TRANS_SLAVE1, customStyle, LIGHT_THEME_COLOR_SLAVE1)
recursionThemTransObj(THEME_ATTR_TRANS_MAIN, customAttr, LIGHT_THEME_COLOR_MAIN)
recursionThemTransObj(
THEME_ATTR_TRANS_SLAVE1_BACKGROUND,
customAttr,
LIGHT_THEME_COMPONENT_BACKGROUND
)
seriesAdaptor(customAttr, LIGHT_THEME_COLOR_MAIN)
merge(customAttr, DEFAULT_COLOR_CASE, canvasStyle.component.chartColor)
} else {
recursionThemTransObj(THEME_STYLE_TRANS_MAIN, customStyle, DARK_THEME_COLOR_MAIN)
recursionThemTransObj(THEME_STYLE_TRANS_SLAVE1, customStyle, DARK_THEME_COLOR_SLAVE1)
recursionThemTransObj(THEME_ATTR_TRANS_MAIN, customAttr, DARK_THEME_COLOR_MAIN)
recursionThemTransObj(
THEME_ATTR_TRANS_SLAVE1_BACKGROUND,
customAttr,
DARK_THEME_COMPONENT_BACKGROUND_BACK
)
seriesAdaptor(customAttr, DARK_THEME_COLOR_MAIN)
merge(customAttr, DEFAULT_COLOR_CASE_DARK, canvasStyle.component.chartColor)
}
customStyle['text'] = {
...canvasStyle.component.chartTitle,
title: customStyle['text']['title'],
show: customStyle['text']['show'],
remarkShow: customStyle['text']['remarkShow'],
remark: customStyle['text']['remark']
}
}
export function adaptCurThemeCommonStyle(component) {
if (['DeTabs'].includes(component.component)) {
component.commonBackground['innerPadding'] = 0
}
// 背景融合-Begin 如果是大屏['CanvasBoard', 'CanvasIcon', 'Picture']组件不需要设置背景
if (
dvMainStore.dvInfo.type === 'dataV' &&
['CanvasBoard', 'CanvasIcon', 'Picture', 'Group', 'SvgTriangle', 'SvgStar'].includes(
component.component
)
) {
component.commonBackground['backgroundColorSelect'] = false
component.commonBackground['innerPadding'] = 0
} else {
const commonStyle = dvMainStore.canvasStyleData.component.chartCommonStyle
for (const key in commonStyle) {
component.commonBackground[key] = commonStyle[key]
}
}
// 背景融合-End
// 通用样式-Begin
if (component.style.color) {
if (dvMainStore.canvasStyleData.dashboard.themeColor === 'light') {
component.style.color = LIGHT_THEME_COLOR_MAIN
} else {
component.style.color = DARK_THEME_COLOR_MAIN
}
}
if (component.component === 'UserView') {
// 图表-Begin
const curViewInfo = dvMainStore.canvasViewInfo[component.id]
adaptCurTheme(curViewInfo.customStyle, curViewInfo.customAttr)
useEmitt().emitter.emit('renderChart-' + component.id, curViewInfo)
// 图表-Begin
} else if (component.component === 'Group') {
component.propValue.forEach(groupItem => {
adaptCurThemeCommonStyle(groupItem)
})
} else if (component.component === 'DeTabs') {
if (dvMainStore.canvasStyleData.dashboard.themeColor === 'light') {
component.style.headFontColor = LIGHT_THEME_COLOR_MAIN
component.style.headFontActiveColor = LIGHT_THEME_COLOR_MAIN
} else {
component.style.headFontColor = DARK_THEME_COLOR_MAIN
component.style.headFontActiveColor = DARK_THEME_COLOR_MAIN
}
component.propValue.forEach(tabItem => {
tabItem.componentData.forEach(tabComponent => {
adaptCurThemeCommonStyle(tabComponent)
})
})
} else if (component.component === 'VQuery') {
const viewInfo = dvMainStore.canvasViewInfo[component.id]
if (viewInfo) {
adaptCurThemeFilterStyleAllKeyComponent(viewInfo)
}
}
return component
}
export function adaptCurThemeCommonStyleAll() {
const componentData = dvMainStore.componentData
componentData.forEach(item => {
adaptCurThemeCommonStyle(item)
})
}
interface CanvasViewInfo {
type: string
customStyle: {
component: object
}
}
const colors = ['labelColor', 'borderColor', 'text', 'bgColor']
const colorsSwitch = ['borderShow', 'textColorShow', 'bgColorShow']
export function adaptCurThemeFilterStyleAllKeyComponent(component) {
if (isFilterComponent(component.type)) {
const filterStyle = dvMainStore.canvasStyleData.component.filterStyle
colors.forEach(styleKey => {
component.customStyle.component[styleKey] = filterStyle[styleKey]
const index = colors.indexOf(styleKey)
if (index !== -1) {
component.customStyle.component[colorsSwitch[index]] = true
}
})
}
}
export function adaptCurThemeFilterStyleAll(styleKey) {
const componentViewData = Object.values(dvMainStore.canvasViewInfo) as CanvasViewInfo[]
const filterStyle = dvMainStore.canvasStyleData.component.filterStyle
componentViewData.forEach(item => {
if (isFilterComponent(item.type)) {
item.customStyle.component[styleKey] = filterStyle[styleKey]
const index = colors.indexOf(styleKey)
if (index !== -1) {
item.customStyle.component[colorsSwitch[index]] = true
}
}
})
}
export function isFilterComponent(component) {
return ['VQuery'].includes(component)
}
export function isTabComponent(component) {
return ['DeTabs'].includes(component)
}