占比图文本显示样式

This commit is contained in:
jingna 2025-07-23 17:24:44 +08:00
parent c09321e258
commit 57f8815e85
20 changed files with 1670 additions and 48 deletions

View File

@ -23,6 +23,7 @@
"@form-create/designer": "^3.2.11", "@form-create/designer": "^3.2.11",
"@form-create/element-ui": "^3.2.22", "@form-create/element-ui": "^3.2.22",
"@form-create/vant": "^3.2.25", "@form-create/vant": "^3.2.25",
"@intlify/unplugin-vue-i18n": "latest",
"@monaco-editor/loader": "^1.5.0", "@monaco-editor/loader": "^1.5.0",
"@npkg/tinymce-plugins": "^0.0.7", "@npkg/tinymce-plugins": "^0.0.7",
"@tinymce/tinymce-vue": "^5.1.0", "@tinymce/tinymce-vue": "^5.1.0",

View File

@ -49,6 +49,7 @@ import DeAppApply from '@/views/common/DeAppApply.vue'
import { useEmitt } from '@/hooks/web/useEmitt' import { useEmitt } from '@/hooks/web/useEmitt'
import { useUserStoreWithOut } from '@/store/modules/user' import { useUserStoreWithOut } from '@/store/modules/user'
import TabsGroup from '@/custom-component/component-group/TabsGroup.vue' import TabsGroup from '@/custom-component/component-group/TabsGroup.vue'
import TreeGroup from '@/custom-component/component-group/TreeGroup.vue'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { updatePublishStatus } from '@/api/visualization/dataVisualization' import { updatePublishStatus } from '@/api/visualization/dataVisualization'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
@ -433,6 +434,9 @@ const fullScreenPreview = () => {
<component-group is-label :base-width="115" :icon-name="dvTab" title="Tab"> <component-group is-label :base-width="115" :icon-name="dvTab" title="Tab">
<tabs-group :dv-model="dvModel"></tabs-group> <tabs-group :dv-model="dvModel"></tabs-group>
</component-group> </component-group>
<component-group is-label :base-width="115" :icon-name="dvTab" title="树">
<tree-group :dv-model="dvModel"></tree-group>
</component-group>
<component-group <component-group
is-label is-label
:base-width="215" :base-width="215"

View File

@ -0,0 +1,65 @@
<script setup lang="ts">
import dvTabShow from '@/assets/svg/dv-tab-show.svg'
import { toRefs } from 'vue'
import eventBus from '@/utils/eventBus'
import DragComponent from '@/custom-component/component-group/DragComponent.vue'
import { commonHandleDragEnd, commonHandleDragStart } from '@/utils/canvasUtils'
const props = defineProps({
propValue: {
type: Array,
default: () => []
},
dvModel: {
type: String,
default: 'dv'
},
element: {
type: Object,
default() {
return {
propValue: null
}
}
},
themes: {
type: String,
default: 'dark'
}
})
const { dvModel } = toRefs(props)
const newComponent = () => {
eventBus.emit('handleNew', { componentName: 'DeTree', innerType: 'DeTree' })
}
const handleDragStart = e => {
commonHandleDragStart(e, dvModel.value)
}
const handleDragEnd = e => {
commonHandleDragEnd(e, dvModel.value)
}
</script>
<template>
<div
class="group"
@dragstart="handleDragStart"
@dragend="handleDragEnd"
v-on:click="newComponent"
>
<drag-component
:themes="themes"
:icon="dvTabShow"
label="树"
drag-info="DeTree&DeTree"
></drag-component>
</div>
</template>
<style lang="less" scoped>
.group {
padding: 12px 8px;
}
</style>

View File

@ -579,6 +579,42 @@ const list = [
textDecoration: 'none' textDecoration: 'none'
} }
}, },
{
component: 'DeTree',
name: t('树'),
label: t('树'),
propValue: [
{
name: 'tab',
title: t('visualization.new_tab'),
componentData: [],
closable: true
}
],
icon: 'dv-tab',
innerType: '',
editing: false,
canvasActive: false,
x: 1,
y: 1,
sizeX: 36,
sizeY: 14,
style: {
width: 600,
height: 300,
fontSize: 16,
activeFontSize: 18,
headHorizontalPosition: 'left',
headFontColor: '#000000',
headFontActiveColor: '#000000',
titleHide: false,
showTabTitle: true,
// #13540
fontWeight: 'normal',
fontStyle: 'normal',
textDecoration: 'none'
}
},
{ {
component: 'ScrollText', component: 'ScrollText',
name: t('visualization.scroll_text'), name: t('visualization.scroll_text'),
@ -631,6 +667,10 @@ export function findNewComponentFromList(
newComponent.propValue[0].name = guid() newComponent.propValue[0].name = guid()
newComponent['titleBackground'] = deepCopy(COMMON_TAB_TITLE_BACKGROUND) newComponent['titleBackground'] = deepCopy(COMMON_TAB_TITLE_BACKGROUND)
} }
if (comp.component === 'DeTree') {
newComponent.propValue[0].name = guid()
newComponent['titleBackground'] = deepCopy(COMMON_TAB_TITLE_BACKGROUND)
}
} }
}) })

View File

@ -0,0 +1,68 @@
<script setup lang="ts">
import CommonAttr from '@/custom-component/common/CommonAttr.vue'
import { toRefs } from 'vue'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { storeToRefs } from 'pinia'
const props = withDefaults(
defineProps<{
themes?: EditorTheme
}>(),
{
themes: 'dark'
}
)
const { themes } = toRefs(props)
const dvMainStore = dvMainStoreWithOut()
const { curComponent } = storeToRefs(dvMainStore)
</script>
<template>
<div class="attr-list de-collapse-style">
<CommonAttr
:themes="themes"
:element="curComponent"
:background-color-picker-width="197"
:background-border-select-width="197"
>
</CommonAttr>
</div>
</template>
<style lang="less" scoped>
.content {
width: 100%;
font-size: 12px;
padding: 10px;
}
.de-collapse-style {
:deep(.ed-collapse-item__header) {
height: 36px !important;
line-height: 36px !important;
font-size: 12px !important;
padding: 0 !important;
font-weight: 500 !important;
.ed-collapse-item__arrow {
margin: 0 6px 0 8px;
}
}
:deep(.ed-collapse-item__content) {
padding: 16px 8px 0;
}
:deep(.ed-form-item) {
display: block;
margin-bottom: 8px;
}
:deep(.ed-form-item__label) {
justify-content: flex-start;
}
}
:deep(.ed-textarea__inner) {
color: #ffffff;
background-color: #000000;
}
</style>

View File

@ -0,0 +1,642 @@
<template>
<div>
<span>树暂无数据</span>
</div>
</template>
<script setup lang="ts">
import {
computed,
getCurrentInstance,
nextTick,
onBeforeMount,
onBeforeUnmount,
onMounted,
reactive,
ref,
toRefs,
watch
} from 'vue'
import DeCanvas from '@/views/canvas/DeCanvas.vue'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { storeToRefs } from 'pinia'
import { guid } from '@/views/visualized/data/dataset/form/util'
import eventBus from '@/utils/eventBus'
import { canvasChangeAdaptor, findComponentIndexById, isDashboard } from '@/utils/canvasUtils'
import DeCustomTab from '@/custom-component/de-tabs/DeCustomTab.vue'
import DePreview from '@/components/data-visualization/canvas/DePreview.vue'
import { getPanelAllLinkageInfo } from '@/api/visualization/linkage'
import { dataVTabComponentAdd, groupSizeStyleAdaptor } from '@/utils/style'
import { deepCopyTabItemHelper } from '@/store/modules/data-visualization/copy'
import { snapshotStoreWithOut } from '@/store/modules/data-visualization/snapshot'
import { useI18n } from '@/hooks/web/useI18n'
import { imgUrlTrans } from '@/utils/imgUtils'
import Board from '@/components/de-board/Board.vue'
import ChartCarouselTooltip from '@/views/chart/components/js/g2plot_tooltip_carousel'
const dvMainStore = dvMainStoreWithOut()
const snapshotStore = snapshotStoreWithOut()
const { tabMoveInActiveId, bashMatrixInfo, editMode, mobileInPc } = storeToRefs(dvMainStore)
const tabComponentRef = ref(null)
let carouselTimer = null
const { t } = useI18n()
const props = defineProps({
canvasStyleData: {
type: Object,
required: true
},
canvasViewInfo: {
type: Object,
required: true
},
dvInfo: {
type: Object,
required: true
},
element: {
type: Object,
default() {
return {
propValue: []
}
}
},
isEdit: {
type: Boolean,
default: false
},
showPosition: {
type: String,
required: false,
default: 'canvas'
},
scale: {
type: Number,
required: false,
default: 1
},
//
searchCount: {
type: Number,
required: false,
default: 0
},
//
fontFamily: {
type: String,
required: false,
default: 'inherit'
}
})
const {
element,
isEdit,
showPosition,
canvasStyleData,
canvasViewInfo,
dvInfo,
scale,
searchCount
} = toRefs(props)
const titleBackgroundActiveSvgInner = computed(() => {
return element.value.titleBackground.active.innerImage.replace('board/', '').replace('.svg', '')
})
const titleBackgroundInActiveSvgInner = computed(() => {
return element.value.titleBackground.inActive.innerImage.replace('board/', '').replace('.svg', '')
})
const svgInnerInActiveEnable = itemName => {
const { backgroundImageEnable, backgroundType, innerImage } =
element.value.titleBackground.inActive
return (
editableTabsValue.value !== itemName &&
!element.value.titleBackground.multiply &&
element.value.titleBackground?.enable &&
backgroundImageEnable &&
backgroundType === 'innerImage' &&
typeof innerImage === 'string'
)
}
const svgInnerActiveEnable = itemName => {
const { backgroundImageEnable, backgroundType, innerImage } = element.value.titleBackground.active
return (
(editableTabsValue.value === itemName || element.value.titleBackground.multiply) &&
element.value.titleBackground?.enable &&
backgroundImageEnable &&
backgroundType === 'innerImage' &&
typeof innerImage === 'string'
)
}
// tooltips tab
const viewToolTipsChange = () => {
element.value.propValue?.forEach(tabItem => {
const tMethod =
editableTabsValue.value === tabItem.name
? ChartCarouselTooltip.resume
: ChartCarouselTooltip.paused
tabItem.componentData?.forEach(componentItem => {
tMethod(componentItem.id)
if (componentItem.component === 'Group')
componentItem.propValue.forEach(groupItem => {
tMethod(groupItem.id)
})
})
})
}
const handleMouseEnter = () => {
state.hoverFlag = true
}
const handleMouseLeave = () => {
state.hoverFlag = false
}
const state = reactive({
activeTabName: '',
curItem: {},
textarea: '',
dialogVisible: false,
tabShow: true,
hoverFlag: false
})
const tabsAreaScroll = ref(false)
const editableTabsValue = ref(null)
//
const noBorderColor = ref('none')
let currentInstance
const showTabTitleFlag = computed(() => {
if (element.value && element.value.style && element.value.style?.showTabTitle === false) {
return false
} else {
return element.value.style?.showTabTitle
}
})
const isEditMode = computed(() => editMode.value === 'edit' && isEdit.value && !mobileInPc.value)
const curThemes = isDashboard() ? 'light' : 'dark'
const calcTabLength = () => {
setTimeout(() => {
if (element.value.propValue.length > 1) {
const containerDom = document.getElementById(
'tab-' + element.value.propValue[element.value.propValue.length - 1].name
)
tabsAreaScroll.value =
containerDom.parentNode.clientWidth > tabComponentRef.value.clientWidth - 100
} else {
tabsAreaScroll.value = false
}
})
}
const beforeHandleCommand = (item, param) => {
return {
command: item,
param: param
}
}
const curPreviewGap = computed(() =>
dvInfo.value.type === 'dashboard' && canvasStyleData.value['dashboard'].gap === 'yes'
? canvasStyleData.value['dashboard'].gapSize
: 0
)
function sureCurTitle() {
state.curItem.title = state.textarea
state.dialogVisible = false
snapshotStore.recordSnapshotCache('sureCurTitle')
}
function addTab() {
const newName = guid()
const newTab = {
name: newName,
title: t('visualization.new_tab'),
componentData: [],
closable: true
}
element.value.propValue.push(newTab)
editableTabsValue.value = newTab.name
snapshotStore.recordSnapshotCache('addTab')
}
function deleteCur(param) {
state.curItem = param
let len = element.value.propValue.length
while (len--) {
if (element.value.propValue[len].name === param.name) {
element.value.propValue.splice(len, 1)
const activeIndex =
(len - 1 + element.value.propValue.length) % element.value.propValue.length
editableTabsValue.value = element.value.propValue[activeIndex].name
state.tabShow = false
nextTick(() => {
state.tabShow = true
})
}
}
}
function copyCur(param) {
addTab()
const newTabItem = element.value.propValue[element.value.propValue.length - 1]
const idMap = {}
const newCanvasId = element.value.id + '--' + newTabItem.name
newTabItem.componentData = deepCopyTabItemHelper(newCanvasId, param.componentData, idMap)
dvMainStore.updateCopyCanvasView(idMap)
}
function editCurTitle(param) {
state.activeTabName = param.name
state.curItem = param
state.textarea = param.title
state.dialogVisible = true
}
function handleCommand(command) {
switch (command.command) {
case 'editTitle':
editCurTitle(command.param)
break
case 'deleteCur':
deleteCur(command.param)
snapshotStore.recordSnapshotCache('deleteCur')
break
case 'copyCur':
copyCur(command.param)
snapshotStore.recordSnapshotCache('copyCur')
break
}
}
const reloadLinkage = () => {
//
if (dvInfo.value.id) {
const resourceTable = ['canvas', 'canvasDataV', 'edit'].includes(showPosition.value)
? 'snapshot'
: 'core'
getPanelAllLinkageInfo(dvInfo.value.id, resourceTable).then(rsp => {
dvMainStore.setNowPanelTrackInfo(rsp.data)
})
}
}
const componentMoveIn = component => {
element.value.propValue.forEach((tabItem, index) => {
if (editableTabsValue.value === tabItem.name) {
//index
if (isDashboard()) {
eventBus.emit('removeMatrixItemById-canvas-main', component.id)
dvMainStore.setCurComponent({ component: null, index: null })
component.canvasId = element.value.id + '--' + tabItem.name
const refInstance = currentInstance.refs['tabCanvas_' + index][0]
if (refInstance) {
const matrixBase = refInstance.getBaseMatrixSize() //
canvasChangeAdaptor(component, matrixBase)
component.x = 1
component.y = 200
component.style.left = 0
component.style.top = 0
tabItem.componentData.push(component)
refInstance.addItemBox(component) //
nextTick(() => {
refInstance.canvasInitImmediately()
})
}
} else {
const curIndex = findComponentIndexById(component.id)
//
dvMainStore.deleteComponent(curIndex)
dvMainStore.setCurComponent({ component: null, index: null })
component.canvasId = element.value.id + '--' + tabItem.name
dataVTabComponentAdd(component, element.value)
tabItem.componentData.push(component)
}
}
})
reloadLinkage()
}
const componentMoveOut = component => {
if (isDashboard()) {
canvasChangeAdaptor(component, bashMatrixInfo.value, true)
}
// Tab
eventBus.emit('removeMatrixItemById-' + component.canvasId, component.id)
dvMainStore.setCurComponent({ component: null, index: null })
//
if (isDashboard()) {
eventBus.emit('moveOutFromTab-canvas-main', component)
} else {
addToMain(component)
}
reloadLinkage()
}
const addToMain = component => {
const { left, top } = element.value.style
component.style.left = component.style.left + left
component.style.top = component.style.top + top
component.canvasId = 'canvas-main'
dvMainStore.addComponent({
component,
index: undefined,
isFromGroup: true
})
}
const moveActive = computed(() => {
return tabMoveInActiveId.value && tabMoveInActiveId.value === element.value.id
})
const headClass = computed(() => {
if (tabsAreaScroll.value) {
return 'tab-head-left'
} else {
return 'tab-head-' + element.value.style.headHorizontalPosition
}
})
const backgroundStyle = backgroundParams => {
if (backgroundParams) {
const {
backdropFilterEnable,
backdropFilter,
backgroundColorSelect,
backgroundColor,
backgroundImageEnable,
backgroundType,
outerImage,
innerPadding,
borderRadius
} = backgroundParams
let style = {
padding: innerPadding * scale.value + 'px',
borderRadius: borderRadius + 'px'
}
let colorRGBA = ''
if (backgroundColorSelect && backgroundColor) {
colorRGBA = backgroundColor
}
if (backgroundImageEnable) {
if (backgroundType === 'outerImage' && typeof outerImage === 'string') {
style['background'] = `url(${imgUrlTrans(outerImage)}) no-repeat ${colorRGBA}`
} else {
style['background-color'] = colorRGBA
}
} else {
style['background-color'] = colorRGBA
}
if (backdropFilterEnable) {
style['backdrop-filter'] = 'blur(' + backdropFilter + 'px)'
}
return style
}
return {}
}
const titleStyle = itemName => {
let style = {}
if (editableTabsValue.value === itemName) {
style = {
textDecoration: element.value.style.textDecoration,
fontStyle: element.value.style.fontStyle,
fontWeight: element.value.style.fontWeight,
fontSize: (element.value.style.activeFontSize || 18) + 'px',
lineHeight: (element.value.style.activeFontSize || 18) + 'px'
}
if (element.value.titleBackground?.enable) {
style = {
...style,
...backgroundStyle(element.value.titleBackground.active)
}
}
} else {
style = {
textDecoration: element.value.style.textDecoration,
fontStyle: element.value.style.fontStyle,
fontWeight: element.value.style.fontWeight,
fontSize: (element.value.style.fontSize || 16) + 'px',
lineHeight: (element.value.style.fontSize || 16) + 'px'
}
if (element.value.titleBackground?.enable) {
style = {
...style,
...backgroundStyle(
element.value.titleBackground.multiply
? element.value.titleBackground.active
: element.value.titleBackground.inActive
)
}
}
}
return style
}
const fontColor = computed(() => {
if (
element.value &&
element.value.style &&
element.value.style.headFontColor &&
typeof element.value.style.headFontColor === 'string'
) {
return element.value.style.headFontColor
} else {
return 'none'
}
})
const activeColor = computed(() => {
if (
element.value &&
element.value.style &&
element.value.style.headFontActiveColor &&
typeof element.value.style.headFontActiveColor === 'string'
) {
return element.value.style.headFontActiveColor
} else {
return 'none'
}
})
const borderActiveColor = computed(() => {
if (
element.value &&
element.value.style &&
element.value.style.headBorderActiveColor &&
typeof element.value.style.headBorderActiveColor === 'string'
) {
return element.value.style.headBorderActiveColor
} else {
return 'none'
}
})
const titleValid = computed(() => {
return !!state.textarea && !!state.textarea.trim()
})
watch(
() => element.value,
() => {
calcTabLength()
},
{ deep: true }
)
const reShow = () => {
state.tabShow = false
nextTick(() => {
state.tabShow = true
})
}
watch(
() => isEditMode.value,
() => {
initCarousel()
}
)
watch(
() => editableTabsValue.value,
() => {
viewToolTipsChange()
}
)
const initCarousel = () => {
carouselTimer && clearInterval(carouselTimer)
carouselTimer = null
if (!isEditMode.value) {
if (element.value.carousel?.enable) {
const switchTime = (element.value.carousel.time || 5) * 1000
let switchCount = 1
//
carouselTimer = setInterval(() => {
//
if (!state.hoverFlag) {
const nowIndex = switchCount % element.value.propValue.length
switchCount++
nextTick(() => {
editableTabsValue.value = element.value.propValue[nowIndex].name
})
}
}, switchTime)
}
}
}
onMounted(() => {
if (element.value.propValue.length > 0) {
editableTabsValue.value = element.value.propValue[0].name
}
calcTabLength()
if (['canvas', 'canvasDataV', 'edit'].includes(showPosition.value) && !mobileInPc.value) {
eventBus.on('onTabMoveIn-' + element.value.id, componentMoveIn)
eventBus.on('onTabMoveOut-' + element.value.id, componentMoveOut)
eventBus.on('onTabSortChange-' + element.value.id, reShow)
}
currentInstance = getCurrentInstance()
initCarousel()
nextTick(() => {
groupSizeStyleAdaptor(element.value)
})
setTimeout(() => {
viewToolTipsChange()
}, 1000)
})
onBeforeUnmount(() => {
if (['canvas', 'canvasDataV', 'edit'].includes(showPosition.value) && !mobileInPc.value) {
eventBus.off('onTabMoveIn-' + element.value.id, componentMoveIn)
eventBus.off('onTabMoveOut-' + element.value.id, componentMoveOut)
eventBus.off('onTabSortChange-' + element.value.id, reShow)
}
})
onBeforeMount(() => {
if (carouselTimer) {
clearInterval(carouselTimer)
carouselTimer = null
}
})
</script>
<style lang="less" scoped>
.title-hidde-tab {
:deep(.ed-tabs__content) {
height: 100% !important;
}
}
.title-show-tab {
:deep(.ed-tabs__content) {
height: calc(100% - 46px) !important;
}
:deep(.ed-tabs__item) {
font-family: inherit;
padding-right: 0 !important;
}
}
.ed-tabs-dark {
:deep(.ed-tabs__new-tab) {
margin-right: 25px;
color: #fff;
}
:deep(.el-dropdown-link) {
color: #fff;
}
}
.ed-tabs-light {
:deep(.ed-tabs__new-tab) {
margin-right: 25px;
background-color: #fff;
}
}
.el-tab-pane-custom {
width: 100%;
}
.canvas-move-in {
border: 2px dotted transparent;
border-color: blueviolet;
}
.tab-head-left :deep(.ed-tabs__nav-scroll) {
display: flex;
justify-content: flex-start;
}
.tab-head-right :deep(.ed-tabs__nav-scroll) {
display: flex;
justify-content: flex-end;
}
.tab-head-center :deep(.ed-tabs__nav-scroll) {
display: flex;
justify-content: center;
}
.switch-hidden {
opacity: 0;
z-index: -1;
}
.tab-content-custom {
position: absolute;
width: 100%;
height: 100%;
}
.custom-tab-title {
.title-inner {
position: relative;
background-size: 100% 100% !important;
}
:deep(.ed-dropdown) {
vertical-align: middle !important;
}
}
</style>

View File

@ -380,6 +380,10 @@ declare interface ChartBasicStyle {
* 圆形填充图间距 * 圆形填充图间距
*/ */
circlePadding: number circlePadding: number
/**
* 占比图图形底色
*/
proportionBackgroundColor: string
} }
/** /**
* 表头属性 * 表头属性

View File

@ -2,6 +2,7 @@
* 图表样式设置 * 图表样式设置
*/ */
declare interface ChartStyle { declare interface ChartStyle {
graphicText: any
/** /**
* 标题样式设置 * 标题样式设置
*/ */
@ -89,7 +90,46 @@ declare interface ChartTextStyle {
*/ */
fontShadow: boolean fontShadow: boolean
} }
/**
* 图形文本标注设置
*/
declare interface ChartGraphicTextStyle {
/**
* 显隐
*/
show: boolean
/**
* 展示文字值
*/
showContent: [
{
show: boolean
type: string
fontFamily: string
positionX:string
positionY:string
fontSize: number
fill: string
fontWeight: number
textAlign: string
offsetX:number
offsetY:number
},
{
show: boolean
type: string
fontFamily: string
positionX:string
positionY:string
fontSize: number
fill: string
fontWeight: number
textAlign: string
offsetX:number
offsetY:number
}
]
}
/** /**
* 图例设置 * 图例设置
*/ */

View File

@ -33,6 +33,7 @@ declare type EditorProperty =
| 'bubble-animate' | 'bubble-animate'
| 'summary-selector' | 'summary-selector'
| 'bullet-graph-selector' | 'bullet-graph-selector'
| 'graphic-text-selector'
declare type EditorPropertyInner = { declare type EditorPropertyInner = {
[key in EditorProperty]?: string[] [key in EditorProperty]?: string[]
} }

View File

@ -74,6 +74,10 @@ export function findNewComponent(componentName, innerType, staticMap?) {
newComponent.propValue[0].name = guid() newComponent.propValue[0].name = guid()
newComponent['titleBackground'] = deepCopy(COMMON_TAB_TITLE_BACKGROUND) newComponent['titleBackground'] = deepCopy(COMMON_TAB_TITLE_BACKGROUND)
} }
if (newComponent.component === 'DeTree') {
newComponent.propValue[0].name = guid()
newComponent['titleBackground'] = deepCopy(COMMON_TAB_TITLE_BACKGROUND)
}
newComponent.innerType = innerType newComponent.innerType = innerType
if (newComponent.innerType === 'richText') { if (newComponent.innerType === 'richText') {
newComponent.propValue = { newComponent.propValue = {

View File

@ -13,6 +13,8 @@ import CanvasIcon from '@/custom-component/canvas-icon/Component.vue'
import CanvasIconAttr from '@/custom-component/canvas-icon/Attr.vue' import CanvasIconAttr from '@/custom-component/canvas-icon/Attr.vue'
import DeTabs from '@/custom-component/de-tabs/Component.vue' import DeTabs from '@/custom-component/de-tabs/Component.vue'
import DeTabsAttr from '@/custom-component/de-tabs/Attr.vue' import DeTabsAttr from '@/custom-component/de-tabs/Attr.vue'
import DeTree from '@/custom-component/de-tree/Component.vue'
import DeTreeAttr from '@/custom-component/de-tree/Attr.vue'
import DeGraphical from '@/custom-component/de-graphical/Component.vue' import DeGraphical from '@/custom-component/de-graphical/Component.vue'
import DeGraphicalAttr from '@/custom-component/de-graphical/Attr.vue' import DeGraphicalAttr from '@/custom-component/de-graphical/Attr.vue'
import CircleShape from '@/custom-component/circle-shape/Component.vue' import CircleShape from '@/custom-component/circle-shape/Component.vue'
@ -53,6 +55,8 @@ export const componentsMap = {
CanvasIconAttr: CanvasIconAttr, CanvasIconAttr: CanvasIconAttr,
DeTabs: DeTabs, DeTabs: DeTabs,
DeTabsAttr: DeTabsAttr, DeTabsAttr: DeTabsAttr,
DeTree: DeTree,
DeTreeAttr: DeTreeAttr,
DeGraphical: DeGraphical, DeGraphical: DeGraphical,
DeGraphicalAttr: DeGraphicalAttr, DeGraphicalAttr: DeGraphicalAttr,
CircleShape: CircleShape, CircleShape: CircleShape,

View File

@ -8,6 +8,7 @@ import XAxisSelector from '@/views/chart/components/editor/editor-style/componen
import YAxisSelector from '@/views/chart/components/editor/editor-style/components/YAxisSelector.vue' import YAxisSelector from '@/views/chart/components/editor/editor-style/components/YAxisSelector.vue'
import DualYAxisSelector from '@/views/chart/components/editor/editor-style/components/DualYAxisSelector.vue' import DualYAxisSelector from '@/views/chart/components/editor/editor-style/components/DualYAxisSelector.vue'
import TitleSelector from '@/views/chart/components/editor/editor-style/components/TitleSelector.vue' import TitleSelector from '@/views/chart/components/editor/editor-style/components/TitleSelector.vue'
import GraphicTextSelector from '@/views/chart/components/editor/editor-style/components/GraphicTextSelector.vue'
import LegendSelector from '@/views/chart/components/editor/editor-style/components/LegendSelector.vue' import LegendSelector from '@/views/chart/components/editor/editor-style/components/LegendSelector.vue'
import SummarySelector from '@/views/chart/components/editor/editor-style/components/SummarySelector.vue' import SummarySelector from '@/views/chart/components/editor/editor-style/components/SummarySelector.vue'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain' import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
@ -116,6 +117,7 @@ const emit = defineEmits([
'onChangeYAxisForm', 'onChangeYAxisForm',
'onChangeYAxisExtForm', 'onChangeYAxisExtForm',
'onTextChange', 'onTextChange',
'onGraphicTextChange',
'onLegendChange', 'onLegendChange',
'onBasicStyleChange', 'onBasicStyleChange',
'onBackgroundChange', 'onBackgroundChange',
@ -172,7 +174,9 @@ const onChangeYAxisExtForm = (val, prop) => {
const onTextChange = (val, prop) => { const onTextChange = (val, prop) => {
state.initReady && emit('onTextChange', val, prop) state.initReady && emit('onTextChange', val, prop)
} }
const onGraphicTextChange = (val, prop) => {
state.initReady && emit('onGraphicTextChange', val, prop)
}
const onIndicatorChange = (val, prop) => { const onIndicatorChange = (val, prop) => {
const value = { indicatorValue: val, indicatorName: undefined } const value = { indicatorValue: val, indicatorName: undefined }
if (prop === 'color' || prop === 'suffixColor') { if (prop === 'color' || prop === 'suffixColor') {
@ -344,6 +348,24 @@ watch(
@onTextChange="onTextChange" @onTextChange="onTextChange"
/> />
</collapse-switch-item> </collapse-switch-item>
<!-- 图形文本 -->
<collapse-switch-item
:themes="themes"
v-model="chart.customStyle.graphicText.show"
v-if="showProperties('graphic-text-selector')"
:change-model="chart.customStyle.graphicText"
@modelChange="val => onGraphicTextChange(val, 'show')"
name="graphicText"
:title="'图形文本'"
>
<graphic-text-selector
:property-inner="propertyInnerAll['graphic-text-selector']"
:themes="themes"
class="attr-selector"
:chart="chart"
@onGraphicTextChange="onGraphicTextChange"
/>
</collapse-switch-item>
<collapse-switch-item <collapse-switch-item
:themes="themes" :themes="themes"
v-if="showProperties('legend-selector')" v-if="showProperties('legend-selector')"

View File

@ -92,7 +92,7 @@ init()
<template> <template>
<el-form ref="colorForm" :model="state.colorForm" label-width="80px" size="small"> <el-form ref="colorForm" :model="state.colorForm" label-width="80px" size="small">
<div>11 <div>
<el-form-item :label="t('chart.color_case')" class="form-item"> <el-form-item :label="t('chart.color_case')" class="form-item">
<el-popover placement="bottom" width="400" trigger="click"> <el-popover placement="bottom" width="400" trigger="click">
<template #reference> <template #reference>

View File

@ -158,7 +158,6 @@ const switchSeriesColor = (seriesColor, index) => {
} }
}) })
} }
const changeSeriesColor = () => { const changeSeriesColor = () => {
// seriesColorState.curSeriesColor.color // seriesColorState.curSeriesColor.color
let changed = false let changed = false
@ -176,6 +175,9 @@ const changeSeriesColor = () => {
changeBasicStyle(seriesColorName.value) changeBasicStyle(seriesColorName.value)
} }
} }
const changeproportionBackgroundColor = () => {
changeBasicStyle('proportionBackgroundColor')
}
watch( watch(
[ [
() => props.chart, () => props.chart,
@ -327,6 +329,7 @@ const colorItemBorderColor = (index, state) => {
@select-color-case="selectColorCase" @select-color-case="selectColorCase"
/> />
</el-form-item> </el-form-item>
<!-- 配色方案 -->
<el-form-item <el-form-item
v-if="!showProperty('gradient-color')" v-if="!showProperty('gradient-color')"
:label="t('chart.color_case')" :label="t('chart.color_case')"
@ -500,8 +503,13 @@ const colorItemBorderColor = (index, state) => {
</div> </div>
<div :id="`series-color-picker-${sub ? 1 : 0}-${index}`"></div> <div :id="`series-color-picker-${sub ? 1 : 0}-${index}`"></div>
<span class="color-item-name" :title="item.name" :class="themes === 'dark' ? 'dark' : ''"> <span class="color-item-name" :title="item.name" :class="themes === 'dark' ? 'dark' : ''">
{{ item.name }}</span {{ item.name }}</span>
> </div>
<div v-if="showProperty('proportionBackgroundColor')" class="proportion-color-picker">
<!-- 图形底色 props.chart.customAttr.basicStyle.proportionBackgroundColor -->
<div style="color: #a6a6a6;margin: 10px 0px;">图形底色</div>
<el-color-picker v-model="state.basicStyleForm.proportionBackgroundColor"
show-alpha :predefine="predefineColors" size="small" @change="changeproportionBackgroundColor" />
</div> </div>
</div> </div>
</template> </template>
@ -752,4 +760,16 @@ const colorItemBorderColor = (index, state) => {
margin-top: 16px !important; margin-top: 16px !important;
} }
} }
.proportion-color-picker {
:deep(.ed-color-picker--small .ed-color-picker__trigger){
width: 23px;
height: 23px;
}
:deep(.ed-color-picker .ed-color-picker__icon){
display: none;
}
:deep(.ed-color-picker__trigger){
padding: 2px;
}
}
</style> </style>

View File

@ -0,0 +1,612 @@
<script lang="ts" setup>
import icon_letterSpacing_outlined from '@/assets/svg/icon_letter-spacing_outlined.svg'
import icon_bold_outlined from '@/assets/svg/icon_bold_outlined.svg'
import icon_italic_outlined from '@/assets/svg/icon_italic_outlined.svg'
import icon_leftAlignment_outlined from '@/assets/svg/icon_left-alignment_outlined.svg'
import icon_centerAlignment_outlined from '@/assets/svg/icon_center-alignment_outlined.svg'
import icon_rightAlignment_outlined from '@/assets/svg/icon_right-alignment_outlined.svg'
import icon_edit_outlined from '@/assets/svg/icon_edit_outlined.svg'
import { PropType, computed, onMounted, reactive, toRefs, watch, nextTick, ref } from 'vue'
import { useAppearanceStoreWithOut } from '@/store/modules/appearance'
import { useI18n } from '@/hooks/web/useI18n'
import {
COLOR_PANEL,
CHART_FONT_FAMILY,
CHART_FONT_LETTER_SPACE,
DEFAULT_TITLE_STYLE,
DEFAULT_GRAPHIC_TEXT_STYLE
} from '@/views/chart/components/editor/util/chart'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { storeToRefs } from 'pinia'
import { cloneDeep, defaultsDeep } from 'lodash-es'
import { ElButton, ElIcon } from 'element-plus-secondary'
import Icon from '@/components/icon-custom/src/Icon.vue'
const dvMainStore = dvMainStoreWithOut()
const { batchOptStatus, mobileInPc } = storeToRefs(dvMainStore)
const { t } = useI18n()
const props = defineProps({
chart: {
type: Object,
required: true
},
themes: {
type: String as PropType<EditorTheme>,
default: 'dark'
},
propertyInner: {
type: Array<string>
}
})
const appearanceStore = useAppearanceStoreWithOut()
const emit = defineEmits(['onGraphicTextChange'])
const toolTip = computed(() => {
return props.themes === 'dark' ? 'light' : 'dark'
})
const predefineColors = COLOR_PANEL
const fontFamily = CHART_FONT_FAMILY.concat(
appearanceStore.fontList.map(ele => ({
name: ele.name,
value: ele.name
}))
)
const fontLetterSpace = CHART_FONT_LETTER_SPACE
const state = reactive({
titleForm: JSON.parse(JSON.stringify(DEFAULT_GRAPHIC_TEXT_STYLE))
})
const { chart } = toRefs(props)
const fontSizeList = computed(() => {
const arr = []
for (let i = 10; i <= 40; i = i + 2) {
arr.push({
name: i + '',
value: i
})
}
for (let i = 50; i <= 200; i = i + 10) {
arr.push({
name: i + '',
value: i
})
}
return arr
})
const changeTitleStyle = prop => {
emit('onGraphicTextChange', state.titleForm, prop)
}
const init = () => {
const customText = defaultsDeep(
cloneDeep(props.chart?.customStyle?.graphicText),
cloneDeep(DEFAULT_GRAPHIC_TEXT_STYLE)
)
state.titleForm = cloneDeep(customText)
//
nextTick(() => {
// state.titleForm.color = customText.color
})
}
const showEditRemark = ref<boolean>(false)
const tempRemark = ref<string>('')
onMounted(() => {
init()
})
watch(
() => props.chart?.customStyle?.graphicText,
() => {
init()
},
{ deep: true }
)
</script>
<template>
<div>
<el-form ref="titleForm" :disabled="!state.titleForm.show" :model="state.titleForm" label-position="top"
size="small">
<div v-for="(item, index) in state.titleForm.showContent" :key="index">
<el-form-item class="form-item" :class="'form-item-' + themes">
<el-checkbox size="small" :effect="themes" v-model="item.show"
@change="changeTitleStyle('showContent')">
{{ item.type }}
</el-checkbox>
</el-form-item>
<div style="margin: 5px 0px;color: #D2D2D2;">文本</div>
<!-- <el-form-item class="form-item" :class="'form-item-' + themes" :effect="themes"
:label="t('chart.text')">
<el-select :effect="themes" v-model="item.fontFamily" :disabled="!item.show"
:placeholder="t('chart.font_family')" @change="changeTitleStyle('showContent')">
<el-option v-for="option in fontFamily" :key="option.value" :label="option.name"
:value="option.value" />
</el-select>
</el-form-item> -->
<div style="display: flex">
<el-form-item class="form-item" :class="'form-item-' + themes" style="padding-right: 4px">
<el-color-picker :effect="themes" v-model="item.fill" :disabled="!item.show"
class="color-picker-style" :predefine="predefineColors" @change="changeTitleStyle('showContent')"
is-custom />
</el-form-item>
<el-form-item class="form-item" :class="'form-item-' + themes" style="padding: 0 4px">
<el-tooltip :content="t('chart.font_size')" :effect="toolTip" placement="top">
<el-select style="width: 56px" :effect="themes" v-model="item.fontSize"
:placeholder="t('chart.text_fontsize')" size="small"
@change="changeTitleStyle('showContent')" :disabled="!item.show">
<el-option v-for="option in fontSizeList" :key="option.value" :label="option.name"
:value="option.value" />
</el-select>
</el-tooltip>
</el-form-item>
<!-- <el-form-item class="form-item" :class="'form-item-' + themes"
style="width: 106px; padding-left: 4px">
<el-select
:effect="themes"
v-model="state.titleForm.nameConfig.letterSpace"
:placeholder="t('chart.quota_letter_space')"
@change="changeTitleStyle('letterSpace')"
>
<template #prefix>
<el-icon size="16">
<Icon name="icon_letter-spacing_outlined"
><icon_letterSpacing_outlined class="svg-icon"
/></Icon>
</el-icon>
</template>
<el-option v-for="option in fontLetterSpace" :key="option.value" :label="option.name" :value="option.value" />
</el-select>
</el-form-item> -->
</div>
<div style="display: flex">
<el-form-item class="form-item" :class="'form-item-' + themes" :effect="themes" :label="'字体粗细'">
<el-input :effect="themes" v-model="item.fontWeight" size="small"
:placeholder="'字体粗细'" :disabled="!item.show" @blur="changeTitleStyle('showContent')" />
</el-form-item>
</div>
<div style="margin: 5px 0px;color: #D2D2D2;">文本位置</div>
<div style="display: flex" class="input-append-item">
<el-form-item class="form-item" :class="'form-item-' + themes" :effect="themes" :label="'X'">
<el-input :effect="themes" v-model="item.positionX" :disabled="!item.show" size="small" :placeholder="''"
@blur="changeTitleStyle('showContent')">
<template #append>%</template>
</el-input>
</el-form-item>
<el-form-item class="form-item" :class="'form-item-' + themes" :effect="themes" :label="'Y'"
style="margin-left: 10px;">
<el-input :effect="themes" v-model="item.positionY" :disabled="!item.show" size="small" :placeholder="''"
@blur="changeTitleStyle('showContent')">
<template #append>%</template>
</el-input>
</el-form-item>
</div>
<div style="margin: 5px 0px;color: #D2D2D2;">文本偏移量</div>
<div style="display: flex">
<el-form-item class="form-item" :class="'form-item-' + themes" :effect="themes" :label="'X'">
<el-input :effect="themes" v-model="item.offsetX" :disabled="!item.show" size="small" :placeholder="''"
@blur="changeTitleStyle('showContent')" />
</el-form-item>
<el-form-item class="form-item" :class="'form-item-' + themes" :effect="themes" :label="'Y'"
style="margin-left: 10px;">
<el-input :effect="themes" v-model="item.offsetY" :disabled="!item.show" size="small" :placeholder="''"
@blur="changeTitleStyle('showContent')" />
</el-form-item>
</div>
</div>
<!-- <el-form-item
:label="t('chart.title')"
class="form-item"
:class="'form-item-' + themes"
v-if="!batchOptStatus && !mobileInPc"
>
<el-input
:effect="themes"
v-model="chart.title"
size="small"
maxlength="100"
:placeholder="t('chart.title')"
clearable
@blur="changeTitleStyle('title')"
/>
</el-form-item>
<el-form-item
class="form-item"
:class="'form-item-' + themes"
:effect="themes"
:label="t('chart.text')"
>
<el-select
:effect="themes"
v-model="state.titleForm.fontFamily"
:placeholder="t('chart.font_family')"
@change="changeTitleStyle('fontFamily')"
>
<el-option
v-for="option in fontFamily"
:key="option.value"
:label="option.name"
:value="option.value"
/>
</el-select>
</el-form-item>
<div style="display: flex">
<el-form-item class="form-item" :class="'form-item-' + themes" style="padding-right: 4px">
<el-color-picker
:effect="themes"
v-model="state.titleForm.color"
class="color-picker-style"
:predefine="predefineColors"
@change="changeTitleStyle('color')"
is-custom
/>
</el-form-item>
<el-form-item class="form-item" :class="'form-item-' + themes" style="padding: 0 4px">
<el-tooltip :content="t('chart.font_size')" :effect="toolTip" placement="top">
<el-select
style="width: 56px"
:effect="themes"
v-model="state.titleForm.fontSize"
:placeholder="t('chart.text_fontsize')"
size="small"
@change="changeTitleStyle('fontSize')"
>
<el-option
v-for="option in fontSizeList"
:key="option.value"
:label="option.name"
:value="option.value"
/>
</el-select>
</el-tooltip>
</el-form-item>
<el-form-item
class="form-item"
:class="'form-item-' + themes"
style="width: 106px; padding-left: 4px"
>
<el-select
:effect="themes"
v-model="state.titleForm.letterSpace"
:placeholder="t('chart.quota_letter_space')"
@change="changeTitleStyle('letterSpace')"
>
<template #prefix>
<el-icon size="16">
<Icon name="icon_letter-spacing_outlined"
><icon_letterSpacing_outlined class="svg-icon"
/></Icon>
</el-icon>
</template>
<el-option
v-for="option in fontLetterSpace"
:key="option.value"
:label="option.name"
:value="option.value"
/>
</el-select>
</el-form-item>
</div> -->
<!-- <el-space>
<el-form-item class="form-item" :class="'form-item-' + themes">
<el-checkbox
:effect="themes"
class="icon-checkbox"
v-model="state.titleForm.isBolder"
@change="changeTitleStyle('isBolder')"
>
<el-tooltip :effect="toolTip" placement="top">
<template #content>
{{ t('chart.bolder') }}
</template>
<div
class="icon-btn"
:class="{ dark: themes === 'dark', active: state.titleForm.isBolder }"
>
<el-icon>
<Icon name="icon_bold_outlined"><icon_bold_outlined class="svg-icon" /></Icon>
</el-icon>
</div>
</el-tooltip>
</el-checkbox>
</el-form-item>
<el-form-item class="form-item" :class="'form-item-' + themes">
<el-checkbox
:effect="themes"
class="icon-checkbox"
v-model="state.titleForm.isItalic"
@change="changeTitleStyle('isItalic')"
>
<el-tooltip :effect="toolTip" placement="top">
<template #content>
{{ t('chart.italic') }}
</template>
<div
class="icon-btn"
:class="{ dark: themes === 'dark', active: state.titleForm.isItalic }"
>
<el-icon>
<Icon name="icon_italic_outlined"><icon_italic_outlined class="svg-icon" /></Icon>
</el-icon>
</div>
</el-tooltip>
</el-checkbox>
</el-form-item>
<div class="position-divider" :class="'position-divider--' + themes"></div>
<el-form-item class="form-item" :class="'form-item-' + themes">
<el-radio-group
:effect="themes"
class="icon-radio-group"
v-model="state.titleForm.hPosition"
@change="changeTitleStyle('hPosition')"
>
<el-radio :effect="themes" label="left">
<el-tooltip :effect="toolTip" placement="top">
<template #content>
{{ t('chart.text_pos_left') }}
</template>
<div
class="icon-btn"
:class="{ dark: themes === 'dark', active: state.titleForm.hPosition === 'left' }"
>
<el-icon>
<Icon name="icon_left-alignment_outlined"
><icon_leftAlignment_outlined class="svg-icon"
/></Icon>
</el-icon>
</div>
</el-tooltip>
</el-radio>
<el-radio :effect="themes" label="center">
<el-tooltip :effect="toolTip" placement="top">
<template #content>
{{ t('chart.text_pos_center') }}
</template>
<div
class="icon-btn"
:class="{
dark: themes === 'dark',
active: state.titleForm.hPosition === 'center'
}"
>
<el-icon>
<Icon name="icon_center-alignment_outlined"
><icon_centerAlignment_outlined class="svg-icon"
/></Icon>
</el-icon>
</div>
</el-tooltip>
</el-radio>
<el-radio :effect="themes" label="right">
<el-tooltip :effect="toolTip" placement="top">
<template #content>
{{ t('chart.text_pos_right') }}
</template>
<div
class="icon-btn"
:class="{
dark: themes === 'dark',
active: state.titleForm.hPosition === 'right'
}"
>
<el-icon>
<Icon name="icon_right-alignment_outlined"
><icon_rightAlignment_outlined class="svg-icon"
/></Icon>
</el-icon>
</div>
</el-tooltip>
</el-radio>
</el-radio-group>
</el-form-item>
</el-space> -->
<!-- <el-form-item class="form-item" :class="'form-item-' + themes">
<el-checkbox
size="small"
:effect="themes"
v-model="state.titleForm.fontShadow"
@change="changeTitleStyle('fontShadow')"
>
{{ t('chart.font_shadow') }}
</el-checkbox>
</el-form-item>
<el-form-item class="form-item" :class="'form-item-' + themes">
<el-checkbox
size="small"
:effect="themes"
v-model="state.titleForm.remarkShow"
@change="changeTitleStyle('remarkShow')"
>
{{ t('chart.remark_show') }}
</el-checkbox>
</el-form-item>
<el-form-item class="form-item" :class="'form-item-' + themes" style="margin-left: 22px">
<label class="remark-label" :class="{ 'remark-label--dark': themes === 'dark' }">
{{ t('chart.remark_edit') }}
</label>
<el-button text @click="openEditRemark" :effect="themes">
<el-icon size="14px">
<Icon name="icon_edit_outlined"><icon_edit_outlined class="svg-icon" /></Icon>
</el-icon>
</el-button>
</el-form-item> -->
</el-form>
</div>
</template>
<style lang="less" scoped>
:deep(.ed-input .ed-select__prefix--light) {
padding-right: 6px;
}
.icon-btn {
font-size: 16px;
line-height: 16px;
width: 24px;
height: 24px;
text-align: center;
border-radius: 4px;
padding-top: 4px;
color: #1f2329;
cursor: pointer;
&.dark {
color: #a6a6a6;
&.active {
color: var(--ed-color-primary);
background-color: var(--ed-color-primary-1a, rgba(51, 112, 255, 0.1));
}
&:hover {
background-color: rgba(255, 255, 255, 0.1);
}
}
&.active {
color: var(--ed-color-primary);
background-color: var(--ed-color-primary-1a, rgba(51, 112, 255, 0.1));
}
&:hover {
background-color: rgba(31, 35, 41, 0.1);
}
}
.is-disabled {
.icon-btn {
color: #8f959e;
cursor: not-allowed;
&:hover {
background-color: inherit;
}
&.active {
background-color: #f5f7fa;
&:hover {
background-color: #f5f7fa;
}
}
&.dark {
color: #5f5f5f;
&.active {
background-color: #373737;
&:hover {
background-color: #373737;
}
}
}
}
}
.icon-checkbox {
:deep(.ed-checkbox__input) {
display: none;
}
:deep(.ed-checkbox__label) {
padding: 0;
}
}
.icon-radio-group {
:deep(.ed-radio) {
margin-right: 8px;
&:last-child {
margin-right: 0;
}
}
:deep(.ed-radio__input) {
display: none;
}
:deep(.ed-radio__label) {
padding: 0;
}
}
.position-divider {
width: 1px;
height: 18px;
margin-bottom: 16px;
background: rgba(31, 35, 41, 0.15);
&.position-divider--dark {
background: rgba(235, 235, 235, 0.15);
}
}
.remark-label {
color: var(--N600, #646a73);
font-family: var(--de-custom_font, 'PingFang');
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 20px;
&.remark-label--dark {
color: var(--N600-Dark, #a6a6a6);
}
}
.dialog-footer .ed-button {
color: #F2F4F5;
background-color: #212121;
border: 1px solid #434343;
}
.dialog-footer .ed-button--primary {
background-color: #0089FF;
border: 1px solid #0089FF;
}
:deep(.ed-textarea__inner) {
background-color: #252424;
box-shadow: none;
border: 1px solid #434343;
color: #fff;
}
:deep(.ed-textarea__inner:hover) {
box-shadow: none;
border: 1px solid #5f5f5f;
}
:deep(.ed-textarea__inner:focus) {
box-shadow: none;
border: 1px solid #0089ff;
}
:deep(.ed-form-item__label) {
color: #D2D2D2 !important;
}
.input-append-item{
:deep(.ed-input-group__append, .ed-input-group__prepend){
padding: 0px 8px;
}
}
</style>

View File

@ -1180,7 +1180,10 @@ const onTextChange = val => {
snapshotStore.recordSnapshotCache('renderChart', view.value.id) snapshotStore.recordSnapshotCache('renderChart', view.value.id)
} }
} }
const onGraphicTextChange = val => {
view.value.customStyle.graphicText = val
renderChart(view.value)
}
const onLegendChange = val => { const onLegendChange = val => {
view.value.customStyle.legend = val view.value.customStyle.legend = val
renderChart(view.value) renderChart(view.value)
@ -2012,6 +2015,7 @@ const deleteChartFieldItem = id => {
@onChangeYAxisForm="onChangeYAxisForm" @onChangeYAxisForm="onChangeYAxisForm"
@onChangeYAxisExtForm="onChangeYAxisExtForm" @onChangeYAxisExtForm="onChangeYAxisExtForm"
@onTextChange="onTextChange" @onTextChange="onTextChange"
@onGraphicTextChange="onGraphicTextChange"
@onIndicatorChange="onIndicatorChange" @onIndicatorChange="onIndicatorChange"
@onIndicatorNameChange="onIndicatorNameChange" @onIndicatorNameChange="onIndicatorNameChange"
@onLegendChange="onLegendChange" @onLegendChange="onLegendChange"
@ -3351,6 +3355,7 @@ const deleteChartFieldItem = id => {
@onChangeYAxisForm="onChangeYAxisForm" @onChangeYAxisForm="onChangeYAxisForm"
@onChangeYAxisExtForm="onChangeYAxisExtForm" @onChangeYAxisExtForm="onChangeYAxisExtForm"
@onTextChange="onTextChange" @onTextChange="onTextChange"
@onGraphicTextChange="onGraphicTextChange"
@onIndicatorChange="onIndicatorChange" @onIndicatorChange="onIndicatorChange"
@onIndicatorNameChange="onIndicatorNameChange" @onIndicatorNameChange="onIndicatorNameChange"
@onLegendChange="onLegendChange" @onLegendChange="onLegendChange"

View File

@ -515,7 +515,38 @@ export const DEFAULT_TITLE_STYLE: ChartTextStyle = {
letterSpace: '0', letterSpace: '0',
fontShadow: false fontShadow: false
} }
//百分比图图形文本样式
export const DEFAULT_GRAPHIC_TEXT_STYLE: ChartGraphicTextStyle = {
show: true,
showContent: [
{
show: true,
type:'名称',
fontFamily:'',
positionX:'50',
positionY:'50',
fontSize: 12,
fill: '#8c8c8c',
fontWeight: 300,
textAlign: 'center',
offsetX:0,
offsetY:-12,
},
{
show: true,
type:'值',
fontFamily:'',
positionX:'50',
positionY:'50',
fontSize: 18,
fill: '#000000',
fontWeight: 500,
textAlign: 'center',
offsetX:0,
offsetY:10,
}
]
}
export const DEFAULT_INDICATOR_STYLE: ChartIndicatorStyle = { export const DEFAULT_INDICATOR_STYLE: ChartIndicatorStyle = {
show: true, show: true,
fontSize: 20, fontSize: 20,
@ -1739,7 +1770,8 @@ export const DEFAULT_BASIC_STYLE: ChartBasicStyle = {
circleBorderWidth: 0, circleBorderWidth: 0,
circlePadding: 0, circlePadding: 0,
quotaPosition: 'col', quotaPosition: 'col',
quotaColLabel: t('dataset.value') quotaColLabel: t('dataset.value'),
proportionBackgroundColor:'#ffffff'
} }
export const BASE_VIEW_CONFIG = { export const BASE_VIEW_CONFIG = {
@ -1782,6 +1814,7 @@ export const BASE_VIEW_CONFIG = {
} }
}, },
customStyle: { customStyle: {
graphicText:DEFAULT_GRAPHIC_TEXT_STYLE,
text: DEFAULT_TITLE_STYLE, text: DEFAULT_TITLE_STYLE,
legend: DEFAULT_LEGEND_STYLE, legend: DEFAULT_LEGEND_STYLE,
xAxis: DEFAULT_XAXIS_STYLE, xAxis: DEFAULT_XAXIS_STYLE,

View File

@ -11,7 +11,7 @@ export const PIE_EDITOR_PROPERTY: EditorProperty[] = [
'label-selector', 'label-selector',
'tooltip-selector', 'tooltip-selector',
'jump-set', 'jump-set',
'linkage' 'linkage',
] ]
export const PIE_EDITOR_PROPERTY_INNER: EditorPropertyInner = { export const PIE_EDITOR_PROPERTY_INNER: EditorPropertyInner = {
'background-overall-component': ['all'], 'background-overall-component': ['all'],

View File

@ -30,7 +30,6 @@ class ChartWrapper {
destroy() { destroy() {
this.chartInstance?.destroy?.() this.chartInstance?.destroy?.()
} }
render() { render() {
this.chartInstance?.render?.() this.chartInstance?.render?.()
} }
@ -39,21 +38,35 @@ class ChartWrapper {
export class G2Pie extends G2ChartView { export class G2Pie extends G2ChartView {
axis: AxisType[] = PIE_AXIS_TYPE axis: AxisType[] = PIE_AXIS_TYPE
properties = PIE_EDITOR_PROPERTY // properties = PIE_EDITOR_PROPERTY
properties = [
'background-overall-component',
'border-style',
'basic-style-selector',
'title-selector',
'label-selector',
'tooltip-selector',
'jump-set',
'linkage',
'graphic-text-selector'
] as any
propertyInner: EditorPropertyInner = { propertyInner: EditorPropertyInner = {
...PIE_EDITOR_PROPERTY_INNER, ...PIE_EDITOR_PROPERTY_INNER,
'basic-style-selector': ['colors', 'alpha', 'radius','innerRadius', 'seriesColor'], 'basic-style-selector': ['colors', 'alpha', 'radius','innerRadius', 'seriesColor','proportionBackgroundColor'],
'tooltip-selector': [...PIE_EDITOR_PROPERTY_INNER['tooltip-selector'], 'carousel'] 'tooltip-selector': [...PIE_EDITOR_PROPERTY_INNER['tooltip-selector'], 'carousel'],
} }
axisConfig = PIE_AXIS_CONFIG axisConfig = PIE_AXIS_CONFIG
constructor() { constructor() {
super() super('pie-proportion')
this.name = 'pie-proportion' this.name = 'pie-proportion'
} }
async drawChart({ container, chart }: { container: HTMLElement; chart: ChartObj }) { async drawChart({ container, chart }: { container: HTMLElement; chart: ChartObj }) {
const customAttr = parseJson(chart.customAttr) const customAttr = parseJson(chart.customAttr)
const customStyle = parseJson(chart.customStyle)
const innerRadius = customAttr.basicStyle.innerRadius ?? 60 const innerRadius = customAttr.basicStyle.innerRadius ?? 60
const data = cloneDeep(chart.data?.data || []) const data = cloneDeep(chart.data?.data || [])
const { radius = 0.75, alpha = 1, colors = [] } = customAttr.basicStyle const { radius = 0.75, alpha = 1, colors = [] } = customAttr.basicStyle
@ -75,17 +88,24 @@ export class G2Pie extends G2ChartView {
g2.legend(false) g2.legend(false)
g2.tooltip({ g2.tooltip({
showMarkers: false showMarkers: false,
showContent: (datum) => {
return !datum.some(item => item?.name === '其他');
},
}) })
g2.facet('rect', { g2.facet('rect', {
fields: ['name'], fields: ['name'],
padding: 20, padding: 20,
showTitle: false, showTitle: false,
eachView: (view, facet) => { eachView: (view, facet) => {
const data:any = facet.data const data:any = facet.data
console.log(customAttr,999)
let color let color
color = colorList[0]; customAttr.basicStyle.seriesColor.forEach((item) => {
if(data.name == item.name){
color = item.color
}
})
data.push({ name: '其他', value: 100 - data[0].value }) data.push({ name: '其他', value: 100 - data[0].value })
view.data(data) view.data(data)
view.coordinate('theta', { view.coordinate('theta', {
@ -97,44 +117,84 @@ export class G2Pie extends G2ChartView {
.adjust('stack') .adjust('stack')
.position('value') .position('value')
.color('name', (name) => { .color('name', (name) => {
return name === '其他' ? colorList[1] : colorList[0]; let colorval = ''
customAttr.basicStyle.seriesColor.forEach((item) => {
if(name == item.name){
colorval = item.color
}
})
return name === '其他' ? customAttr.basicStyle.proportionBackgroundColor : colorval;
}) })
.style({ .style({
opacity: 1, opacity: 1,
}); })
if(customStyle.graphicText.show){
customStyle.graphicText.showContent.forEach(item => {
const positionarr:any = []
positionarr.push(item.positionX + '%')
positionarr.push(item.positionY + '%')
console.log(positionarr)
if(item.type == '名称' && item.show == true){
view.annotation().text({ view.annotation().text({
position: [ '50%', '50%' ], position: positionarr,
content: data[0].name, content: data[0].name,
style: { style: {
fontSize: 12, fontSize: item.fontSize,
fill: '#8c8c8c', fill: item.fill,
fontWeight: 300, fontWeight: item.fontWeight,
textBaseline: 'bottom', textBaseline: 'bottom',
textAlign: 'center' textAlign: 'center'
}, },
offsetY: -12, offsetX: item.offsetX,
offsetY: item.offsetY
}) })
}else if(item.type == '值' && item.show == true){
view.annotation().text({ view.annotation().text({
position: ['50%', '50%'], position: positionarr,
content: data[0].value, content: data[0].value,
style: { style: {
fontSize: 18, fontSize: item.fontSize,
fill: '#fff', fill: item.fill,
fontWeight: 500, fontWeight: item.fontWeight,
textAlign: 'center' textAlign: 'center'
}, },
offsetY: 10, offsetX: item.offsetX,
offsetY: item.offsetY
}) })
}
})
}
// view.annotation().text({
// position: [ '50%', '50%' ],
// content: data[0].name,
// style: {
// fontSize: 12,
// fill: '#8c8c8c',
// fontWeight: 300,
// textBaseline: 'bottom',
// textAlign: 'center'
// },
// offsetY: -12,
// })
// view.annotation().text({
// position: ['50%', '50%'],
// content: data[0].value,
// style: {
// fontSize: 18,
// fill: '#fff',
// fontWeight: 500,
// textAlign: 'center'
// },
// offsetY: 10,
// })
} }
}) })
g2.render() g2.render()
return new ChartWrapper(g2) return new ChartWrapper(g2)
} }
public setupSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] { public setupSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] {
return setUpSingleDimensionSeriesColor(chart, data) return setUpSingleDimensionSeriesColor(chart, data)
} }

View File

@ -276,17 +276,14 @@ const renderChart = async (view, callback?) => {
curView = view curView = view
// view view.data // view view.data
// 便 // 便
console.log(view,998779)
const chart = deepCopy({ const chart = deepCopy({
...defaultsDeep(view, cloneDeep(BASE_VIEW_CONFIG)), ...defaultsDeep(view, cloneDeep(BASE_VIEW_CONFIG)),
data: chartData.value, data: chartData.value,
...(props.fontFamily && props.fontFamily !== 'inherit' ? { fontFamily: props.fontFamily } : {}) ...(props.fontFamily && props.fontFamily !== 'inherit' ? { fontFamily: props.fontFamily } : {})
}) })
console.log('chart1', chart.customAttr.basicStyle.colors)
const chartView = chartViewManager.getChartView(view.render, view.type) const chartView = chartViewManager.getChartView(view.render, view.type)
recursionTransObj(customAttrTrans, chart.customAttr, scale.value, terminal.value) recursionTransObj(customAttrTrans, chart.customAttr, scale.value, terminal.value)
recursionTransObj(customStyleTrans, chart.customStyle, scale.value, terminal.value) recursionTransObj(customStyleTrans, chart.customStyle, scale.value, terminal.value)
console.log('chart', chart)
switch (chartView.library) { switch (chartView.library) {
case ChartLibraryType.L7_PLOT: case ChartLibraryType.L7_PLOT:
await renderL7Plot(chart, chartView as L7PlotChartView<any, any>, callback) await renderL7Plot(chart, chartView as L7PlotChartView<any, any>, callback)