Merge pull request #13019 from dataease/pr@dev-v2@feat_symbol_map_custom_image_icon

feat(图表): 符号地图自定义符号支持图片
This commit is contained in:
wisonic-s 2024-10-30 16:45:35 +08:00 committed by GitHub
commit c8f7ffb18b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 85 additions and 42 deletions

View File

@ -76,7 +76,12 @@ const init = () => {
basicStyle.mapSymbol === 'custom' && basicStyle.mapSymbol === 'custom' &&
state.basicStyleForm.customIcon !== basicStyle.customIcon state.basicStyleForm.customIcon !== basicStyle.customIcon
) { ) {
const file = svgStrToUrl(basicStyle.customIcon) let file
if (basicStyle.customIcon?.startsWith('data')) {
file = basicStyle.customIcon
} else {
file = svgStrToUrl(basicStyle.customIcon)
}
file && (state.fileList[0] = { url: file }) file && (state.fileList[0] = { url: file })
} }
state.basicStyleForm = defaultsDeep(basicStyle, cloneDeep(DEFAULT_BASIC_STYLE)) as ChartBasicStyle state.basicStyleForm = defaultsDeep(basicStyle, cloneDeep(DEFAULT_BASIC_STYLE)) as ChartBasicStyle
@ -243,11 +248,12 @@ const mapSymbolOptions = [
{ name: t('commons.custom'), value: 'custom' } { name: t('commons.custom'), value: 'custom' }
] ]
const iconUpload = ref() const iconUpload = ref()
const acceptedFileType = ['image/svg+xml', 'image/jpeg', 'image/png']
const onIconChange: UploadProps['onChange'] = async uploadFile => { const onIconChange: UploadProps['onChange'] = async uploadFile => {
const rawFile = uploadFile.raw const rawFile = uploadFile.raw
let validIcon = true let validIcon = true
if (rawFile.type !== 'image/svg+xml') { if (!acceptedFileType.includes(rawFile.type)) {
ElMessage.error('请选择正确的 SVG 文件!') ElMessage.error('请选择正确的图标文件!')
validIcon = false validIcon = false
} }
if (rawFile.size / 1024 / 1024 > 1) { if (rawFile.size / 1024 / 1024 > 1) {
@ -257,20 +263,42 @@ const onIconChange: UploadProps['onChange'] = async uploadFile => {
if (!validIcon) { if (!validIcon) {
iconUpload.value?.clearFiles() iconUpload.value?.clearFiles()
state.fileList.splice(0) state.fileList.splice(0)
const svg = state.basicStyleForm.customIcon const customIcon = state.basicStyleForm.customIcon
if (svg) { if (customIcon) {
const file = svgStrToUrl(svg) let file = ''
//
if (customIcon.startsWith('data')) {
file = customIcon
} else {
// svg
file = svgStrToUrl(customIcon)
}
file && (state.fileList[0] = { url: file }) file && (state.fileList[0] = { url: file })
} }
} else { } else {
state.basicStyleForm.customIcon = await rawFile.text() if (rawFile.type === 'image/svg+xml') {
changeBasicStyle('customIcon') state.basicStyleForm.customIcon = await rawFile.text()
changeBasicStyle('customIcon')
} else {
const fileReader = new FileReader()
fileReader.onloadend = () => {
state.basicStyleForm.customIcon = fileReader.result as string
changeBasicStyle('customIcon')
}
fileReader.readAsDataURL(rawFile)
}
} }
} }
const changeMapSymbol = () => { const changeMapSymbol = () => {
if (state.basicStyleForm.mapSymbol === 'custom' && state.basicStyleForm.customIcon) { const { mapSymbol, customIcon } = state.basicStyleForm
const file = svgStrToUrl(state.basicStyleForm.customIcon) if (mapSymbol === 'custom' && customIcon) {
let file
if (customIcon.startsWith('data')) {
file = customIcon
} else {
file = svgStrToUrl(state.basicStyleForm.customIcon)
}
file && (state.fileList[0] = { url: file }) file && (state.fileList[0] = { url: file })
} }
changeBasicStyle('mapSymbol') changeBasicStyle('mapSymbol')
@ -521,12 +549,12 @@ onMounted(() => {
<el-row style="flex: 1"> <el-row style="flex: 1">
<el-col> <el-col>
<el-form-item class="form-item" :class="'form-item-' + themes"> <el-form-item class="form-item" :class="'form-item-' + themes">
<template v-if="state.basicStyleForm.mapSymbol === 'custom'" #label> <template #label>
<span class="data-area-label"> <span class="data-area-label">
<span style="margin-right: 4px">符号形状</span> <span style="margin-right: 4px">符号形状</span>
<el-tooltip class="item" effect="dark" placement="bottom"> <el-tooltip class="item" effect="dark" placement="bottom">
<template #content> <template v-if="state.basicStyleForm.mapSymbol === 'custom'" #content>
<div>支持 1MB 以内的 SVG 文件</div> <div>支持 1MB 以内的 SVG, JPG, JPEG, PNG 文件</div>
</template> </template>
<el-icon class="hint-icon" :class="{ 'hint-icon--dark': themes === 'dark' }"> <el-icon class="hint-icon" :class="{ 'hint-icon--dark': themes === 'dark' }">
<Icon name="icon_info_outlined"><icon_info_outlined class="svg-icon" /></Icon> <Icon name="icon_info_outlined"><icon_info_outlined class="svg-icon" /></Icon>
@ -555,7 +583,7 @@ onMounted(() => {
<div class="avatar-uploader-container" :class="`img-area_${themes}`"> <div class="avatar-uploader-container" :class="`img-area_${themes}`">
<el-upload <el-upload
action="#" action="#"
accept=".svg" accept=".svg,.png,.jpeg,.jpg"
class="avatar-uploader" class="avatar-uploader"
list-type="picture-card" list-type="picture-card"
ref="iconUpload" ref="iconUpload"

View File

@ -241,19 +241,27 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
.active(true) .active(true)
if (xAxisExt[0]?.dataeaseName) { if (xAxisExt[0]?.dataeaseName) {
if (basicStyle.mapSymbol === 'custom' && basicStyle.customIcon) { if (basicStyle.mapSymbol === 'custom' && basicStyle.customIcon) {
const parser = new DOMParser() // 图片无法改色
for (let index = 0; index < Math.min(colorsWithAlpha.length, colorIndex + 1); index++) { if (basicStyle.customIcon.startsWith('data')) {
const color = colorsWithAlpha[index] scene.removeImage('customIcon')
const fillRegex = /(fill="[^"]*")/g await scene.addImage('customIcon', basicStyle.customIcon)
const svgStr = basicStyle.customIcon.replace(fillRegex, '') pointLayer.shape('customIcon')
const doc = parser.parseFromString(svgStr, 'image/svg+xml') } else {
const svgEle = doc.documentElement const parser = new DOMParser()
svgEle.setAttribute('fill', color) for (let index = 0; index < Math.min(colorsWithAlpha.length, colorIndex + 1); index++) {
await scene.addImage(`icon-${color}`, svgStrToUrl(svgEle.outerHTML)) const color = colorsWithAlpha[index]
const fillRegex = /(fill="[^"]*")/g
const svgStr = basicStyle.customIcon.replace(fillRegex, '')
const doc = parser.parseFromString(svgStr, 'image/svg+xml')
const svgEle = doc.documentElement
svgEle.setAttribute('fill', color)
scene.removeImage(`icon-${color}`)
await scene.addImage(`icon-${color}`, svgStrToUrl(svgEle.outerHTML))
}
pointLayer.shape('color', c => {
return `icon-${c}`
})
} }
pointLayer.shape('color', c => {
return `icon-${c}`
})
} else { } else {
pointLayer.shape(mapSymbol).color(xAxisExt[0]?.dataeaseName, colorsWithAlpha) pointLayer.shape(mapSymbol).color(xAxisExt[0]?.dataeaseName, colorsWithAlpha)
pointLayer.style({ pointLayer.style({
@ -266,23 +274,30 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
} }
} else { } else {
if (basicStyle.mapSymbol === 'custom' && basicStyle.customIcon) { if (basicStyle.mapSymbol === 'custom' && basicStyle.customIcon) {
const parser = new DOMParser() scene.removeImage('customIcon')
const color = colorsWithAlpha[0] if (basicStyle.customIcon.startsWith('data')) {
const fillRegex = /(fill="[^"]*")/g await scene.addImage('customIcon', basicStyle.customIcon)
const svgStr = basicStyle.customIcon.replace(fillRegex, '') pointLayer.shape('customIcon')
const doc = parser.parseFromString(svgStr, 'image/svg+xml') } else {
const svgEle = doc.documentElement const parser = new DOMParser()
svgEle.setAttribute('fill', color) const color = colorsWithAlpha[0]
await scene.addImage(`customIcon`, svgStrToUrl(svgEle.outerHTML)) const fillRegex = /(fill="[^"]*")/g
pointLayer.shape('customIcon') const svgStr = basicStyle.customIcon.replace(fillRegex, '')
const doc = parser.parseFromString(svgStr, 'image/svg+xml')
const svgEle = doc.documentElement
svgEle.setAttribute('fill', color)
await scene.addImage(`customIcon`, svgStrToUrl(svgEle.outerHTML))
pointLayer.shape('customIcon')
}
} else { } else {
pointLayer.shape(mapSymbol) pointLayer
pointLayer.color(colorsWithAlpha[0]) .shape(mapSymbol)
pointLayer.style({ .color(colorsWithAlpha[0])
stroke: colorsWithAlpha[0], .style({
strokeWidth: mapSymbolStrokeWidth, stroke: colorsWithAlpha[0],
opacity: mapSymbolOpacity / 10 strokeWidth: mapSymbolStrokeWidth,
}) opacity: mapSymbolOpacity / 10
})
} }
} }
if (sizeKey) { if (sizeKey) {