diff --git a/src/assets/images/chart/charts/capsule.png b/src/assets/images/chart/charts/capsule.png new file mode 100644 index 00000000..e3d6dbd7 Binary files /dev/null and b/src/assets/images/chart/charts/capsule.png differ diff --git a/src/packages/components/Charts/Mores/CapsuleChart/config.ts b/src/packages/components/Charts/Mores/CapsuleChart/config.ts new file mode 100644 index 00000000..86b362b5 --- /dev/null +++ b/src/packages/components/Charts/Mores/CapsuleChart/config.ts @@ -0,0 +1,23 @@ +import { PublicConfigClass } from '@/packages/public' +import { CapsuleChartConfig } from './index' +import { CreateComponentType } from '@/packages/index.d' +import { chartInitConfig } from '@/settings/designSetting' + +import cloneDeep from 'lodash/cloneDeep' +import dataJson from './data.json' + + +export const option = { + dataSet:dataJson, + colors: ['#e062ae', '#fb7293', '#e690d1', '#32c5e9', '#96bfff'], + unit: '', + itemHeight:10, + showValue: true +} + +export default class Config extends PublicConfigClass implements CreateComponentType { + public key: string = CapsuleChartConfig.key + public attr = { ...chartInitConfig,w: 300, h: 200 ,zIndex: -1} + public chartConfig = cloneDeep(CapsuleChartConfig) + public option = cloneDeep(option) +} diff --git a/src/packages/components/Charts/Mores/CapsuleChart/config.vue b/src/packages/components/Charts/Mores/CapsuleChart/config.vue new file mode 100644 index 00000000..f662fa37 --- /dev/null +++ b/src/packages/components/Charts/Mores/CapsuleChart/config.vue @@ -0,0 +1,43 @@ +<template> + <!-- Echarts 全局设置 --> + <global-setting :optionData="optionData"> </global-setting> + <!-- 胶囊柱图 --> + <collapse-item :name="`胶囊柱图`" expanded> + <SettingItemBox name="指标"> + <SettingItem name="显示数值"> + <n-space> + <n-switch v-model:value="optionData.showValue" size="small"></n-switch> + </n-space> + </SettingItem> + <setting-item name="单位"> + <n-input v-model:value="optionData.unit" size="small"></n-input> + </setting-item> + <setting-item name="每块高度(px)"> + <n-input-number + v-model:value="optionData.itemHeight" + :min="0" + :step="1" + size="small" + placeholder="水球数值" + ></n-input-number> + </setting-item> + </SettingItemBox> + </collapse-item> +</template> + +<script setup lang="ts"> +import { PropType, computed } from 'vue' +import { GlobalSetting, CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting' +import { GlobalThemeJsonType } from '@/settings/chartThemes/index' + +import { option } from './config' + +const props = defineProps({ + optionData: { + type: Object as PropType<typeof option & GlobalThemeJsonType>, + required: true + } +}) + +console.log(props.optionData) +</script> diff --git a/src/packages/components/Charts/Mores/CapsuleChart/data.json b/src/packages/components/Charts/Mores/CapsuleChart/data.json new file mode 100644 index 00000000..206bb50b --- /dev/null +++ b/src/packages/components/Charts/Mores/CapsuleChart/data.json @@ -0,0 +1,10 @@ +{ + "dimensions": ["name", "value"], + "source": [ + { "name": "厦门", "value": 20 }, + { "name": "南阳", "value": 40 }, + { "name": "背景", "value": 60 }, + { "name": "上海", "value": 80 }, + { "name": "新疆", "value": 100 } + ] +} diff --git a/src/packages/components/Charts/Mores/CapsuleChart/index.ts b/src/packages/components/Charts/Mores/CapsuleChart/index.ts new file mode 100644 index 00000000..96c24793 --- /dev/null +++ b/src/packages/components/Charts/Mores/CapsuleChart/index.ts @@ -0,0 +1,15 @@ +import image from '@/assets/images/chart/charts/capsule.png' +import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d' +import { ChatCategoryEnum, ChatCategoryEnumName } from '../../index.d' + +export const CapsuleChartConfig: ConfigType = { + key: 'CapsuleChart', + chartKey: 'VCapsuleChart', + conKey: 'VCCapsuleChart', + title: '胶囊柱图', + category: ChatCategoryEnum.MORE, + categoryName: ChatCategoryEnumName.MORE, + package: PackagesCategoryEnum.CHARTS, + chartFrame: ChartFrameEnum.COMMON, + image +} diff --git a/src/packages/components/Charts/Mores/CapsuleChart/index.vue b/src/packages/components/Charts/Mores/CapsuleChart/index.vue new file mode 100644 index 00000000..b7ae58ba --- /dev/null +++ b/src/packages/components/Charts/Mores/CapsuleChart/index.vue @@ -0,0 +1,218 @@ +<script setup lang="ts"> +import { onMounted, watch, reactive } from 'vue' +import merge from 'lodash/merge' +import cloneDeep from 'lodash/cloneDeep' + +const props = defineProps({ + chartConfig: { + type: Object, + default: () => ({}) + } +}) +console.log(props.chartConfig.option) +type DataProps = { + name: string | number + value: string | number + [key: string]: string | number +} + +interface StateProps { + defaultConfig: { + dataSet: { + dimensions: Array<string> + source: Array<DataProps> + } + colors: Array<string> + unit: string + showValue: boolean + itemHeight: number + } + mergedConfig: any + capsuleLength: Array<number> + capsuleValue: Array<string | Object> + labelData: Array<number> + capsuleItemHeight: string +} + +const state = reactive<StateProps>({ + defaultConfig: { + dataSet: { dimensions: ['name', 'value'], source: [] }, + colors: ['#37a2da', '#32c5e9', '#67e0e3', '#9fe6b8', '#ffdb5c', '#ff9f7f', '#fb7293'], + unit: '', + showValue: false, + itemHeight: 10 + }, + mergedConfig: null, + capsuleLength: [], + capsuleValue: [], + labelData: [], + capsuleItemHeight: '' +}) + +watch( + () => props.chartConfig.option, + newVal => { + console.log(newVal) + calcData() + }, + { + deep: true + } +) + +function calcData() { + mergeConfig() + + calcCapsuleLengthAndLabelData() +} + +function mergeConfig() { + state.mergedConfig = merge(cloneDeep(state.defaultConfig), props.chartConfig.option || {}) + console.log('state.mergedConfig', state.mergedConfig) +} + +function calcCapsuleLengthAndLabelData() { + const { source } = state.mergedConfig.dataSet + if (!source.length) return + + state.capsuleItemHeight = handle(state.mergedConfig.itemHeight) + const capsuleValue = source.map((item: DataProps) => item[state.mergedConfig.dataSet.dimensions[1]]) + + const maxValue = Math.max(...capsuleValue) + + state.capsuleValue = capsuleValue + + state.capsuleLength = capsuleValue.map((v: any) => (maxValue ? v / maxValue : 0)) + + const oneFifth = maxValue / 5 + + const labelData = Array.from(new Set(new Array(6).fill(0).map((v, i) => Math.ceil(i * oneFifth)))) + + state.labelData = labelData +} +const handle = (val: string | number) => { + return val + 'px' +} +onMounted(() => { + calcData() +}) +</script> +<template> + <div class="dv-capsule-chart"> + <template v-if="state.mergedConfig"> + <div class="label-column"> + <div + v-for="item in state.mergedConfig.dataSet.source" + :key="item[state.mergedConfig.dataSet.dimensions[0]]" + :style="{ height: state.capsuleItemHeight, lineHeight: state.capsuleItemHeight }" + > + {{ item[state.mergedConfig.dataSet.dimensions[0]] }} + </div> + <div class="laset"> </div> + </div> + + <div class="capsule-container"> + <div + v-for="(capsule, index) in state.capsuleLength" + :key="index" + class="capsule-item" + :style="{ height: state.capsuleItemHeight }" + > + <div + class="capsule-item-column" + :style="`width: ${capsule * 100}%; background-color: ${ + state.mergedConfig.colors[index % state.mergedConfig.colors.length] + };height:calc(100% - ${2}px);`" + > + <div v-if="state.mergedConfig.showValue" class="capsule-item-value"> + {{ state.capsuleValue[index] }} + </div> + </div> + </div> + + <div class="unit-label"> + <div v-for="(label, index) in state.labelData" :key="label + index"> + {{ label }} + </div> + </div> + </div> + + <div v-if="state.mergedConfig.unit" class="unit-text"> + {{ state.mergedConfig.unit }} + </div> + </template> + </div> +</template> + +<style lang="scss" scoped> +.dv-capsule-chart { + position: relative; + display: flex; + flex-direction: row; + box-sizing: border-box; + padding: 10px 16px 10px 10px; + color: #fff; + + .label-column { + display: flex; + flex-direction: column; + justify-content: space-between; + box-sizing: border-box; + padding-right: 10px; + text-align: right; + font-size: 12px; + >div:not(:last-child){ + margin: 5px 0; + } + + } + + .capsule-container { + flex: 1; + display: flex; + flex-direction: column; + justify-content: space-between; + } + + .capsule-item { + box-shadow: 0 0 3px #999; + height: 10px; + margin: 5px 0px; + border-radius: 5px; + + .capsule-item-column { + position: relative; + height: 8px; + margin-top: 1px; + border-radius: 5px; + transition: all 0.3s; + display: flex; + justify-content: flex-end; + align-items: center; + + .capsule-item-value { + font-size: 12px; + transform: translateX(100%); + } + } + } + + .unit-label { + height: 20px; + font-size: 12px; + position: relative; + display: flex; + justify-content: space-between; + align-items: center; + } + + .unit-text { + text-align: right; + display: flex; + align-items: flex-end; + font-size: 12px; + line-height: 20px; + margin-left: 10px; + } +} +</style> diff --git a/src/packages/components/Charts/Mores/index.ts b/src/packages/components/Charts/Mores/index.ts index 7539cf21..1828a518 100644 --- a/src/packages/components/Charts/Mores/index.ts +++ b/src/packages/components/Charts/Mores/index.ts @@ -4,5 +4,7 @@ import { FunnelConfig } from './Funnel/index' import { HeatmapConfig } from './Heatmap/index' import { WaterPoloConfig } from './WaterPolo/index' import { TreeMapConfig } from './TreeMap/index' +import { CapsuleChartConfig } from './CapsuleChart' -export default [ProcessConfig, RadarConfig, FunnelConfig, HeatmapConfig, WaterPoloConfig, TreeMapConfig] + +export default [ProcessConfig, RadarConfig, FunnelConfig, HeatmapConfig, WaterPoloConfig, TreeMapConfig,CapsuleChartConfig]