wangpx 1 год назад
Родитель
Сommit
27cc2a609b

BIN
src/assets/images/chart/charts/bar_stack.png


+ 54 - 0
src/packages/components/Charts/Bars/BarStacked/config.ts

@@ -0,0 +1,54 @@
+import { echartOptionProfixHandle, PublicConfigClass } from '@/packages/public'
+import { BarStackedConfig } from './index'
+import { CreateComponentType } from '@/packages/index.d'
+import cloneDeep from 'lodash/cloneDeep'
+import dataJson from './data.json'
+
+export const includes = ['legend', 'xAxis', 'yAxis', 'grid']
+export const seriesItem = {
+  type: 'bar',
+  stack: 'total',
+  barWidth: 30,
+  label: {
+    show: true,
+    // 内部显示数值标签
+    position: 'inside',
+    color: '#000',
+    fontSize: 12
+  },
+  itemStyle: {
+    color: null,
+    // 顶部圆角
+    topBorder: 0,
+    borderRadius: 2
+  }
+}
+
+// ECharts 配置项
+export const option = {
+  tooltip: {
+    show: true,
+    trigger: 'axis',
+    axisPointer: {
+      show: true,
+      type: 'shadow'
+    }
+  },
+xAxis: {
+    show: true,
+    type: 'category'
+  },
+  yAxis: {
+    show: true,
+    type: 'value'
+  },
+  dataset: { ...dataJson },
+  series: [seriesItem, seriesItem]
+}
+
+export default class Config extends PublicConfigClass implements CreateComponentType {
+  public key = BarStackedConfig.key
+  public chartConfig = cloneDeep(BarStackedConfig)
+  // 图表配置项
+  public option = echartOptionProfixHandle(option, includes)
+}

+ 69 - 0
src/packages/components/Charts/Bars/BarStacked/config.vue

@@ -0,0 +1,69 @@
+<template>
+  <!-- Echarts 全局设置 -->
+  <global-setting :optionData="optionData"></global-setting>
+  <CollapseItem v-for="(item, index) in seriesList" :key="index" :name="`柱状图-${index + 1}`" :expanded="true">
+    <SettingItemBox name="图形">
+      <SettingItem name="宽度">
+        <n-input-number v-model:value="item.barWidth" :min="1" :max="100" size="small"
+          placeholder="自动计算"></n-input-number>
+      </SettingItem>
+      <SettingItem name="圆角">
+        <n-input-number v-model:value="item.itemStyle.topBorder" :min="0" size="small"></n-input-number>
+      </SettingItem>
+    </SettingItemBox>
+    <setting-item-box name="标签">
+      <setting-item>
+        <n-space>
+          <n-switch v-model:value="item.label.show" size="small" />
+          <n-text>展示标签</n-text>
+        </n-space>
+      </setting-item>
+      <setting-item name="大小">
+        <n-input-number v-model:value="item.label.fontSize" size="small" :min="1"></n-input-number>
+      </setting-item>
+      <setting-item name="颜色">
+        <n-color-picker size="small" :modes="['hex']" v-model:value="item.label.color"></n-color-picker>
+      </setting-item>
+      <setting-item name="位置">
+        <n-select v-model:value="item.label.position" :options="[
+          { label: 'top', value: 'top' },
+          { label: 'left', value: 'left' },
+          { label: 'right', value: 'right' },
+          { label: 'bottom', value: 'bottom' },
+          { label: 'inside', value: 'inside' }
+        ]" />
+      </setting-item>
+    </setting-item-box>
+  </CollapseItem>
+</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'
+
+const props = defineProps({
+  optionData: {
+    type: Object as PropType<GlobalThemeJsonType>,
+    required: true
+  }
+})
+
+const seriesList = computed(() => {
+  // const topBorders = props.optionData.series.map(item => item.itemStyle.topBorder)
+  // let topBorder = 0;
+  // topBorders.forEach(border => {
+  //   topBorder = border > topBorder ? border : topBorder
+  // })
+  // console.log('series:', props.optionData);
+  // const series = props.optionData.series
+  // series[series.length - 1].itemStyle.topBorder = [topBorder, topBorder, 0, 0]
+  const series = props.optionData.series.map(item => {
+    const topBorder = item.itemStyle.topBorder
+    item.itemStyle.borderRadius = [topBorder, topBorder, 0, 0]
+    return item
+  })
+  return series
+  // return props.optionData.series
+})
+</script>

+ 40 - 0
src/packages/components/Charts/Bars/BarStacked/data.json

@@ -0,0 +1,40 @@
+{
+  "dimensions": ["product", "data1", "data2"],
+  "source": [
+    {
+      "product": "Mon",
+      "data1": 120,
+      "data2": 130
+    },
+    {
+      "product": "Tue",
+      "data1": 200,
+      "data2": 130
+    },
+    {
+      "product": "Wed",
+      "data1": 150,
+      "data2": 312
+    },
+    {
+      "product": "Thu",
+      "data1": 80,
+      "data2": 268
+    },
+    {
+      "product": "Fri",
+      "data1": 70,
+      "data2": 155
+    },
+    {
+      "product": "Sat",
+      "data1": 110,
+      "data2": 117
+    },
+    {
+      "product": "Sun",
+      "data1": 130,
+      "data2": 160
+    }
+  ]
+}

+ 28 - 0
src/packages/components/Charts/Bars/BarStacked/index.ts

@@ -0,0 +1,28 @@
+// 公共类型声明
+import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d'
+// 当前[信息模块]分类声明
+import { ChatCategoryEnum,ChatCategoryEnumName } from '../../index.d'
+// 展示图片
+// import image from '@/assets/images/chart/charts/bar_stack.png'
+
+export const BarStackedConfig: ConfigType = {
+  // 唯一key,注意!!!文件夹名称需要修改成与当前组件一致!!!
+  key: 'BarStacked',
+  // 图表组件渲染 Components 格式: V + key
+  chartKey: 'VBarStacked',
+  // 配置组件渲染 Components 格式: VC + key
+  conKey: 'VCBarStacked',
+  // 名称
+  title: '堆叠柱状图',
+  // 子分类目录
+  category: ChatCategoryEnum.BAR,
+  // 子分类目录
+  categoryName: ChatCategoryEnumName.BAR,
+  // 包分类
+  package: PackagesCategoryEnum.CHARTS,
+  // 组件框架类型 (注意!若此 Echarts 图表不支持 dataset 格式,则使用 ChartFrameEnum.COMMON)
+  chartFrame: ChartFrameEnum.ECHARTS,
+  // 图片 (注意!图片存放的路径必须在 src/assets/images/chart/包分类名称/*)
+  // 文件夹名称需要和包分类名称一致: PackagesCategoryEnum.CHARTS
+  image: "bar_stack.png"
+}

+ 97 - 0
src/packages/components/Charts/Bars/BarStacked/index.vue

@@ -0,0 +1,97 @@
+<template>
+  <v-chart
+    ref="vChartRef"
+    :init-options="initOptions"
+    :theme="themeColor"
+    :option="option"
+    :update-options="{
+      replaceMerge: replaceMergeArr
+    }"
+    autoresize
+  ></v-chart>
+</template>
+
+<script setup lang="ts">
+import { ref, nextTick, computed, watch, PropType } from 'vue'
+import VChart from 'vue-echarts'
+import { useCanvasInitOptions } from '@/hooks/useCanvasInitOptions.hook'
+import { use } from 'echarts/core'
+import { CanvasRenderer } from 'echarts/renderers'
+import { BarChart } from 'echarts/charts'
+import config, { includes, seriesItem } from './config'
+import { mergeTheme } from '@/packages/public/chart'
+import { useChartDataFetch } from '@/hooks'
+import { CreateComponentType } from '@/packages/index.d'
+import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
+import { isPreview } from '@/utils'
+import { DatasetComponent, GridComponent, TooltipComponent, LegendComponent } from 'echarts/components'
+import isObject from 'lodash/isObject'
+import cloneDeep from 'lodash/cloneDeep'
+
+const props = defineProps({
+  themeSetting: {
+    type: Object,
+    required: true
+  },
+  themeColor: {
+    type: Object,
+    required: true
+  },
+  chartConfig: {
+    type: Object as PropType<config>,
+    required: true
+  }
+})
+
+const initOptions = useCanvasInitOptions(props.chartConfig.option, props.themeSetting)
+
+use([DatasetComponent, CanvasRenderer, BarChart, GridComponent, TooltipComponent, LegendComponent])
+
+const replaceMergeArr = ref<string[]>()
+
+const option = computed(() => {
+  return mergeTheme(props.chartConfig.option, props.themeSetting, includes)
+})
+
+// dataset 无法变更条数的补丁
+watch(
+  () => props.chartConfig.option.dataset,
+  (newData: { dimensions: any }, oldData) => {
+    try {
+      if (!isObject(newData) || !('dimensions' in newData)) return
+      if (Array.isArray(newData?.dimensions)) {
+        const seriesArr = []
+        // 对oldData进行判断,防止传入错误数据之后对旧维度判断产生干扰
+        // 此处计算的是dimensions的Y轴维度,若是dimensions.length为0或1,则默认为1,排除X轴维度干扰
+        const oldDimensions = Array.isArray(oldData?.dimensions)&&oldData.dimensions.length >= 1 ? oldData.dimensions.length : 1; 
+        const newDimensions = newData.dimensions.length >= 1 ? newData.dimensions.length : 1;
+        const dimensionsGap = newDimensions - oldDimensions;
+        if (dimensionsGap < 0) {
+          props.chartConfig.option.series.splice(newDimensions - 1)
+        } else if (dimensionsGap > 0) {
+          if(!oldData || !oldData?.dimensions || !Array.isArray(oldData?.dimensions) || !oldData?.dimensions.length ) {
+              props.chartConfig.option.series=[]
+          }
+          for (let i = 0; i < dimensionsGap; i++) {
+            seriesArr.push(cloneDeep(seriesItem))
+          }
+          props.chartConfig.option.series.push(...seriesArr)
+        }
+        replaceMergeArr.value = ['series']
+        nextTick(() => {
+          replaceMergeArr.value = []
+        })
+      }
+    } catch (error) {
+      console.log(error)
+    }
+  },
+  {
+    deep: false
+  }
+)
+
+const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => {
+  props.chartConfig.option.dataset = newData
+})
+</script>

+ 4 - 1
src/packages/components/Charts/Bars/index.ts

@@ -2,5 +2,8 @@ import { BarCommonConfig } from './BarCommon/index'
 import { BarCrossrangeConfig } from './BarCrossrange/index'
 import { CapsuleChartConfig } from './CapsuleChart/index'
 import { BarLineConfig } from './BarLine/index'
+import { BarStackedConfig } from './BarStacked/index'
 
-export default [BarCommonConfig, BarCrossrangeConfig, BarLineConfig, CapsuleChartConfig]
+export default [BarCommonConfig, BarCrossrangeConfig, BarLineConfig, CapsuleChartConfig
+    , BarStackedConfig
+]