222 lines
4.9 KiB
Vue
222 lines
4.9 KiB
Vue
<template>
|
|
<div class="dv-conical-column-chart" :ref="ref">
|
|
<svg :width="width" :height="height">
|
|
<g
|
|
v-for="(item, i) in column"
|
|
:key="i"
|
|
>
|
|
<path
|
|
:d="item.d"
|
|
:fill="mergedConfig.columnColor"
|
|
/>
|
|
<text
|
|
:style="`fontSize:${mergedConfig.fontSize}px`"
|
|
:fill="mergedConfig.textColor"
|
|
:x="item.x"
|
|
:y="height - 4"
|
|
>
|
|
{{ item.name }}
|
|
</text>
|
|
<image
|
|
v-if="mergedConfig.img.length"
|
|
:xlink:href="mergedConfig.img[i % mergedConfig.img.length]"
|
|
:width="mergedConfig.imgSideLength"
|
|
:height="mergedConfig.imgSideLength"
|
|
:x="item.x - mergedConfig.imgSideLength / 2"
|
|
:y="item.y - mergedConfig.imgSideLength"
|
|
/>
|
|
<text
|
|
v-if="mergedConfig.showValue"
|
|
:style="`fontSize:${mergedConfig.fontSize}px`"
|
|
:fill="mergedConfig.textColor"
|
|
:x="item.x"
|
|
:y="item.textY"
|
|
>
|
|
{{ item.value }}
|
|
</text>
|
|
</g>
|
|
</svg>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import autoResize from '../../mixins/autoResize.js'
|
|
|
|
import { deepMerge } from '@jiaminghi/charts/lib/util/index'
|
|
|
|
import { deepClone } from '@jiaminghi/c-render/lib/plugin/util'
|
|
|
|
export default {
|
|
name: 'ConicalColumnChart',
|
|
mixins: [autoResize],
|
|
props: {
|
|
config: {
|
|
type: Object,
|
|
default: () => ({})
|
|
}
|
|
},
|
|
data () {
|
|
return {
|
|
ref: 'conical-column-chart',
|
|
|
|
defaultConfig: {
|
|
/**
|
|
* @description Chart data
|
|
* @type {Array<Object>}
|
|
* @default data = []
|
|
*/
|
|
data: [],
|
|
/**
|
|
* @description Chart img
|
|
* @type {Array<String>}
|
|
* @default img = []
|
|
*/
|
|
img: [],
|
|
/**
|
|
* @description Chart font size
|
|
* @type {Number}
|
|
* @default fontSize = 12
|
|
*/
|
|
fontSize: 12,
|
|
/**
|
|
* @description Img side length
|
|
* @type {Number}
|
|
* @default imgSideLength = 30
|
|
*/
|
|
imgSideLength: 30,
|
|
/**
|
|
* @description Column color
|
|
* @type {String}
|
|
* @default columnColor = 'rgba(0, 194, 255, 0.4)'
|
|
*/
|
|
columnColor: 'rgba(0, 194, 255, 0.4)',
|
|
/**
|
|
* @description Text color
|
|
* @type {String}
|
|
* @default textColor = '#fff'
|
|
*/
|
|
textColor: '#fff',
|
|
/**
|
|
* @description Show value
|
|
* @type {Boolean}
|
|
* @default showValue = false
|
|
*/
|
|
showValue: false
|
|
},
|
|
|
|
mergedConfig: null,
|
|
|
|
column: []
|
|
}
|
|
},
|
|
watch: {
|
|
config () {
|
|
const { calcData } = this
|
|
|
|
calcData()
|
|
}
|
|
},
|
|
methods: {
|
|
afterAutoResizeMixinInit () {
|
|
const { calcData } = this
|
|
|
|
calcData()
|
|
},
|
|
onResize () {
|
|
const { calcData } = this
|
|
|
|
calcData()
|
|
},
|
|
calcData () {
|
|
const { mergeConfig, initData, calcSVGPath } = this
|
|
|
|
mergeConfig()
|
|
|
|
initData()
|
|
|
|
calcSVGPath()
|
|
},
|
|
mergeConfig () {
|
|
const { defaultConfig, config } = this
|
|
|
|
this.mergedConfig = deepMerge(deepClone(defaultConfig, true), config || {})
|
|
},
|
|
initData () {
|
|
const { mergedConfig } = this
|
|
let { data } = mergedConfig
|
|
|
|
data = deepClone(data, true)
|
|
|
|
data.sort(({ value: a }, { value: b }) => {
|
|
if (a > b) return -1
|
|
if (a < b) return 1
|
|
if (a === b) return 0
|
|
})
|
|
|
|
const max = data[0] ? data[0].value : 10
|
|
|
|
data = data.map(item => ({
|
|
...item,
|
|
percent: item.value / max
|
|
}))
|
|
|
|
mergedConfig.data = data
|
|
},
|
|
calcSVGPath () {
|
|
const { mergedConfig, width, height } = this
|
|
|
|
const { imgSideLength, fontSize, data } = mergedConfig
|
|
|
|
const itemNum = data.length
|
|
const gap = width / (itemNum + 1)
|
|
|
|
const useAbleHeight = height - imgSideLength - fontSize - 5
|
|
const svgBottom = height - fontSize - 5
|
|
|
|
this.column = data.map((item, i) => {
|
|
const { percent } = item
|
|
|
|
const middleXPos = gap * (i + 1)
|
|
const leftXPos = gap * i
|
|
const rightXpos = gap * (i + 2)
|
|
|
|
const middleYPos = svgBottom - useAbleHeight * percent
|
|
const controlYPos = useAbleHeight * percent * 0.6 + middleYPos
|
|
|
|
const d = `
|
|
M${leftXPos}, ${svgBottom}
|
|
Q${middleXPos}, ${controlYPos} ${middleXPos},${middleYPos}
|
|
M${middleXPos},${middleYPos}
|
|
Q${middleXPos}, ${controlYPos} ${rightXpos},${svgBottom}
|
|
L${leftXPos}, ${svgBottom}
|
|
Z
|
|
`
|
|
|
|
const textY = (svgBottom + middleYPos) / 2 + fontSize / 2
|
|
|
|
return {
|
|
...item,
|
|
d,
|
|
x: middleXPos,
|
|
y: middleYPos,
|
|
textY
|
|
}
|
|
})
|
|
|
|
console.warn(this.column)
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="less">
|
|
.dv-conical-column-chart {
|
|
width: 100%;
|
|
height: 100%;
|
|
|
|
text {
|
|
text-anchor: middle;
|
|
}
|
|
}
|
|
</style>
|