Procházet zdrojové kódy

feat: 新增特殊形状柱状图

liaojiaxing před 8 měsíci
rodič
revize
8872c92a60

+ 1 - 1
apps/shalu-bigscreen-designer/src/config/compSetting.ts

@@ -82,7 +82,7 @@ export const compSetting: CompSetting = {
             },
             {
               name: '特殊形状柱状图',
-              componetName: 'BasicBar',
+              componetName: 'SpecialBar',
               icon: compIcon['chart/bar/special-shape']
             },
             {

+ 12 - 0
packages/shalu-dashboard-ui/components/charts/Bar/SpecialBar/index.ts

@@ -0,0 +1,12 @@
+import SpecialBar from './src/SpecialBar.vue';
+import Config from './src/Config.vue';
+
+SpecialBar.Config = Config;
+SpecialBar.install = (app: any) => {
+  app.component(SpecialBar.name, SpecialBar);
+  return app;
+};
+
+export default SpecialBar;
+export { Config };
+export { defaultPropsValue, componentProps } from './src/props';

+ 106 - 0
packages/shalu-dashboard-ui/components/charts/Bar/SpecialBar/src/Config.vue

@@ -0,0 +1,106 @@
+<template>
+  <div class="chart-config">
+    <div class="config-tab">
+      <Tabs v-model:activeKey="activeTab" size="small" centered>
+        <TabPane key="1">
+          <template #tab>
+            <DatabaseOutlined />
+            <span>数据设置</span>
+          </template>
+        </TabPane>
+        <TabPane key="2">
+          <template #tab>
+            <SkinOutlined />
+            <span>样式设置</span>
+          </template>
+        </TabPane>
+      </Tabs>
+    </div>
+
+    <DataConfig
+      v-if="activeTab === '1'"
+      :dataSource="dataSource"
+      @change="handleDataSourceChange"
+    />
+    <CusForm
+      v-if="activeTab === '2'"
+      :columns="formItems"
+      :formModel="props"
+      @change="handleFormChange"
+    />
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, defineProps, defineEmits } from "vue";
+import { Tabs, TabPane } from "ant-design-vue";
+import { DatabaseOutlined, SkinOutlined } from "@ant-design/icons-vue";
+import DataConfig from "../../../DataConfig.vue";
+import { CusForm, IFormItem } from "../../../../cusForm";
+import { componentProps } from "./props";
+import { chartFormItemsMap } from "../../../config/chartFormItemsMap";
+import { set, cloneDeep } from "lodash-es";
+import { colorPreset } from "../../../config/index";
+
+const props = defineProps(componentProps);
+const activeTab = ref("1");
+const emit = defineEmits(["change"]);
+
+const formItems: IFormItem[] = [
+  chartFormItemsMap.title,
+  chartFormItemsMap.legend,
+  chartFormItemsMap.label,
+  {
+    label: "图形",
+    prop: "",
+    type: "group",
+    children: [
+      {
+        label: "形状",
+        prop: "seriesExtend.shape",
+        type: "select",
+        fieldProps: {
+          options: [
+            { label: "三维柱形", value: "3d" },
+            { label: "圆柱", value: "circle" },
+            { label: "三角", value: "triangle" },
+            { label: "三维三角", value: "3dTriangle" },
+            // { label: "曲线三角", value: "smoothTriangle" },
+            // { label: "三维曲线三角", value: "3dSmoothTriangle" },
+          ]
+        }
+      },
+      {
+        label: "配色",
+        prop: "color",
+        type: "colorScheme",
+        defaultValue: colorPreset[0].color,
+      },
+    ]
+  },
+  chartFormItemsMap.xAxis,
+  chartFormItemsMap.yAxis,
+  chartFormItemsMap.tooltip,
+];
+
+const handleDataSourceChange = (data: any) => {
+  emit("change", {
+    ...props,
+    dataSource: data,
+  });
+};
+const handleFormChange = (formatData: any) => {
+  const obj = cloneDeep(props);
+  Object.keys(formatData).forEach((key) => {
+    set(obj, key, formatData[key]);
+  });
+  emit("change", obj);
+};
+</script>
+
+<style lang="less" scoped>
+.config-tab {
+  text-align: center;
+  margin-bottom: 12px;
+}
+</style>

+ 59 - 0
packages/shalu-dashboard-ui/components/charts/Bar/SpecialBar/src/SpecialBar.vue

@@ -0,0 +1,59 @@
+<template>
+  <Charts
+    :width="width"
+    :height="height"
+    :echarts-options="echartsOption"
+    :loading="loading"
+  ></Charts>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent } from "vue";
+import Charts from "../../../Charts.vue";
+import { componentProps } from "./props";
+import { useChartOptions } from "../../../hooks/useChartOptions";
+import { type EChartsOption } from "echarts";
+import { 
+  get3dSeries,
+  getCircleSeries,
+  getTriangleSeries,
+  get3dTriangleSeries,
+} from './data';
+
+export default defineComponent({
+  name: "DSpecialBar",
+  components: { Charts },
+  props: componentProps,
+  setup(props) {
+    const { options, loading } = useChartOptions(props);
+
+    const echartsOption = computed(() => {
+      const opt = {
+        ...options.value,
+        series: Array.isArray(options.value?.series) 
+        ? options.value?.series.map((item, index) => {
+          // 获取不同形状的柱体
+          switch(props.seriesExtend?.shape) {
+            case "3d": return get3dSeries(item, index, options.value.color);
+            case "circle": return getCircleSeries(item, index, options.value.color);
+            case "triangle": return getTriangleSeries(item, index, options.value.color);
+            case "3dTriangle": return get3dTriangleSeries(item, index, options.value.color);
+            default: get3dSeries(item, index, options.value.color);;
+          }
+        })
+        : options.value?.series
+      } as EChartsOption;
+
+      console.log('special bar option:', opt);
+      return opt;
+    })
+
+    return {
+      echartsOption,
+      loading,
+    };
+  },
+});
+</script>
+
+<style scoped></style>

+ 377 - 0
packages/shalu-dashboard-ui/components/charts/Bar/SpecialBar/src/data.ts

@@ -0,0 +1,377 @@
+import * as echarts from "echarts";
+import { EChartsOption} from "echarts";
+import { lightenColor } from "@shalu/utils";
+
+/*********************************三维柱形图*********************************** */
+export const get3dSeries = (series: EChartsOption["series"], index: number, color: EChartsOption["color"]) => {
+  const leftShape = echarts.graphic.extendShape({
+    buildPath(ctx, shape) {
+      const { topBasicsYAxis, bottomYAxis, basicsXAxis } = shape;
+      // 侧面宽度
+      const WIDTH = 15;
+      // 斜角高度
+      const OBLIQUE_ANGLE_HEIGHT = 8;
+  
+      const p1 = [basicsXAxis - WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT];
+      const p2 = [basicsXAxis - WIDTH, bottomYAxis];
+      const p3 = [basicsXAxis, bottomYAxis];
+      const p4 = [basicsXAxis, topBasicsYAxis];
+  
+      ctx.moveTo(p1[0], p1[1]);
+      ctx.lineTo(p2[0], p2[1]);
+      ctx.lineTo(p3[0], p3[1]);
+      ctx.lineTo(p4[0], p4[1]);
+    },
+  });
+  const rightShape = echarts.graphic.extendShape({
+    buildPath(ctx, shape) {
+      const { topBasicsYAxis, bottomYAxis, basicsXAxis } = shape;
+      // 侧面宽度
+      const WIDTH = 15;
+      // 斜角高度
+      const OBLIQUE_ANGLE_HEIGHT = 8;
+  
+      const p1 = [basicsXAxis, topBasicsYAxis];
+      const p2 = [basicsXAxis, bottomYAxis];
+      const p3 = [basicsXAxis + WIDTH, bottomYAxis];
+      const p4 = [basicsXAxis + WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT];
+  
+      ctx.moveTo(p1[0], p1[1]);
+      ctx.lineTo(p2[0], p2[1]);
+      ctx.lineTo(p3[0], p3[1]);
+      ctx.lineTo(p4[0], p4[1]);
+    },
+  });
+  
+  const topShape = echarts.graphic.extendShape({
+    buildPath(ctx, shape) {
+      const { topBasicsYAxis, basicsXAxis } = shape;
+      // 侧面宽度
+      const WIDTH = 15;
+      // 斜角高度
+      const OBLIQUE_ANGLE_HEIGHT = 8;
+  
+      const p1 = [basicsXAxis, topBasicsYAxis];
+      const p2 = [basicsXAxis + WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT];
+      const p3 = [basicsXAxis, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT * 2];
+      const p4 = [basicsXAxis - WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT];
+  
+      ctx.moveTo(p1[0], p1[1]);
+      ctx.lineTo(p2[0], p2[1]);
+      ctx.lineTo(p3[0], p3[1]);
+      ctx.lineTo(p4[0], p4[1]);
+    },
+  });
+  
+  echarts.graphic.registerShape("leftShape1", leftShape);
+  echarts.graphic.registerShape("rightShape1", rightShape);
+  echarts.graphic.registerShape("topShape1", topShape);
+
+  return {
+    ...series,
+    type: "custom",
+    renderItem: (_params: any, api: any) => {
+      // 基础坐标
+
+      //api.value(0)可以获取到对应的Index,value(1)获取到y轴value值
+      //api.coord可以获取到某个点(索引,value)的坐标
+      const basicsCoord = api.coord([api.value(0), api.value(1)]);
+      // 顶部基础 y 轴,注意:是从顶部空白处到头部的距离
+      const topBasicsYAxis = basicsCoord[1];
+      // 基础 x 轴
+      const basicsXAxis = basicsCoord[0] + index * 30;
+      // 底部 y 轴  注意:y轴的距离都是从canvas左上角开始,到当前点位的距离
+      const bottomYAxis = api.coord([api.value(0), 0])[1];
+
+      const basicColor = Array.isArray(color) ? color[index % color.length] : color;
+      const topColor = lightenColor(basicColor as string, 20);
+      const leftColor = lightenColor(basicColor as string, -40);
+
+      return {
+        type: "group",
+        children: [
+          {
+            type: "leftShape1",
+            shape: {
+              topBasicsYAxis,
+              basicsXAxis,
+              bottomYAxis,
+            },
+            style: {
+              fill: leftColor,
+              emphasis: {
+                  fill: "yellow", // 鼠标高亮时的填充颜色
+              },
+            },
+          },
+          {
+            type: "rightShape1",
+            shape: {
+              topBasicsYAxis,
+              basicsXAxis,
+              bottomYAxis,
+            },
+            style: {
+              fill: basicColor,
+              emphasis: {
+                  fill: "yellow", // 鼠标高亮时的填充颜色
+              },
+            },
+          },
+          {
+            type: "topShape1",
+            shape: {
+              topBasicsYAxis,
+              basicsXAxis,
+              bottomYAxis,
+            },
+            style: {
+              fill: topColor,
+            },
+          },
+        ],
+      };
+    },
+  };
+};
+
+
+/*********************************圆柱体柱形图*********************************** */
+export const getCircleSeries = (series: EChartsOption["series"], index: number, color: EChartsOption["color"]) => {
+  const barShape = echarts.graphic.extendShape({
+    buildPath(ctx, shape) {
+      const { topBasicsYAxis, bottomYAxis, basicsXAxis } = shape;
+      // 侧面宽度
+      const WIDTH = 15;
+  
+      const p1 = [basicsXAxis - WIDTH, topBasicsYAxis];
+      const p2 = [basicsXAxis - WIDTH, bottomYAxis];
+      const p3 = [basicsXAxis + WIDTH, bottomYAxis];
+      const p4 = [basicsXAxis + WIDTH, topBasicsYAxis];
+  
+      ctx.moveTo(p1[0], p1[1]);
+      ctx.lineTo(p2[0], p2[1]);
+      ctx.lineTo(p3[0], p3[1]);
+      ctx.lineTo(p4[0], p4[1]);
+    },
+  });
+  
+  echarts.graphic.registerShape("barShape1", barShape);
+  
+  return {
+    ...series,
+    type: "custom",
+    renderItem: (_params: any, api: any) => {
+      // 基础坐标
+
+      //api.value(0)可以获取到对应的Index,value(1)获取到y轴value值
+      //api.coord可以获取到某个点(索引,value)的坐标
+      const basicsCoord = api.coord([api.value(0), api.value(1)]);
+      // 顶部基础 y 轴,注意:是从顶部空白处到头部的距离
+      const topBasicsYAxis = basicsCoord[1];
+      // 基础 x 轴
+      const basicsXAxis = basicsCoord[0] + index * 30;
+      // 底部 y 轴  注意:y轴的距离都是从canvas左上角开始,到当前点位的距离
+      const bottomYAxis = api.coord([api.value(0), 0])[1];
+
+      const basicColor = Array.isArray(color) ? color[index % color.length] : color;
+
+      return {
+        type: "group",
+        children: [
+          {
+            type: "barShape1",
+            shape: {
+              topBasicsYAxis,
+              basicsXAxis,
+              bottomYAxis,
+            },
+            style: {
+              fill: basicColor,
+            },
+          },
+          {
+            type: 'ellipse',
+            shape: {
+              cx: basicsXAxis, // 圆心的 x 坐标
+              cy: topBasicsYAxis, // 圆心的 y 坐标
+              rx: 15, // 水平半径
+              ry: 6  // 垂直半径
+            },
+            style: {
+              fill: lightenColor(basicColor as string, 20), // 填充颜色
+            }
+          },
+          {
+            type: 'ellipse',
+            shape: {
+              cx: basicsXAxis, // 圆心的 x 坐标
+              cy: bottomYAxis, // 圆心的 y 坐标
+              rx: 15, // 水平半径
+              ry: 6  // 垂直半径
+            },
+            style: {
+              fill: basicColor, // 填充颜色
+            }
+          },
+        ],
+      };
+    },
+  };
+};
+
+/*********************************三角形柱形图*********************************** */
+export const getTriangleSeries = (series: EChartsOption["series"], index: number, color: EChartsOption["color"]) => {
+  const barShape = echarts.graphic.extendShape({
+    buildPath(ctx, shape) {
+      const { topBasicsYAxis, bottomYAxis, basicsXAxis } = shape;
+      // 侧面宽度
+      const WIDTH = 15;
+  
+      const p1 = [basicsXAxis, topBasicsYAxis];
+      const p2 = [basicsXAxis - WIDTH, bottomYAxis];
+      const p3 = [basicsXAxis + WIDTH, bottomYAxis];
+      const p4 = [basicsXAxis, topBasicsYAxis];
+  
+      ctx.moveTo(p1[0], p1[1]);
+      ctx.lineTo(p2[0], p2[1]);
+      ctx.lineTo(p3[0], p3[1]);
+      ctx.lineTo(p4[0], p4[1]);
+    },
+  });
+  
+  echarts.graphic.registerShape("barShape2", barShape);
+  
+  return {
+    ...series,
+    type: "custom",
+    renderItem: (_params: any, api: any) => {
+      // 基础坐标
+
+      //api.value(0)可以获取到对应的Index,value(1)获取到y轴value值
+      //api.coord可以获取到某个点(索引,value)的坐标
+      const basicsCoord = api.coord([api.value(0), api.value(1)]);
+      // 顶部基础 y 轴,注意:是从顶部空白处到头部的距离
+      const topBasicsYAxis = basicsCoord[1];
+      // 基础 x 轴
+      const basicsXAxis = basicsCoord[0] + index * 30;
+      // 底部 y 轴  注意:y轴的距离都是从canvas左上角开始,到当前点位的距离
+      const bottomYAxis = api.coord([api.value(0), 0])[1];
+
+      const basicColor = Array.isArray(color) ? color[index % color.length] : color;
+
+      return {
+        type: "group",
+        children: [
+          {
+            type: "barShape2",
+            shape: {
+              topBasicsYAxis,
+              basicsXAxis,
+              bottomYAxis,
+            },
+            style: {
+              fill: basicColor,
+            },
+          },
+        ],
+      };
+    },
+  };
+};
+
+/*********************************三维三角柱形图*********************************** */
+export const get3dTriangleSeries = (series: EChartsOption["series"], index: number, color: EChartsOption["color"]) => {
+  const leftShape = echarts.graphic.extendShape({
+    buildPath(ctx, shape) {
+      const { topBasicsYAxis, bottomYAxis, basicsXAxis } = shape;
+      // 侧面宽度
+      const WIDTH = 15;
+  
+      const p1 = [basicsXAxis, topBasicsYAxis];
+      const p2 = [basicsXAxis - WIDTH, bottomYAxis];
+      const p3 = [basicsXAxis, bottomYAxis];
+      const p4 = [basicsXAxis, topBasicsYAxis];
+  
+      ctx.moveTo(p1[0], p1[1]);
+      ctx.lineTo(p2[0], p2[1]);
+      ctx.lineTo(p3[0], p3[1]);
+      ctx.lineTo(p4[0], p4[1]);
+    },
+  });
+  const rightShape = echarts.graphic.extendShape({
+    buildPath(ctx, shape) {
+      const { topBasicsYAxis, bottomYAxis, basicsXAxis } = shape;
+      // 侧面宽度
+      const WIDTH = 15;
+  
+      const p1 = [basicsXAxis, topBasicsYAxis];
+      const p2 = [basicsXAxis, bottomYAxis];
+      const p3 = [basicsXAxis + WIDTH, bottomYAxis];
+      const p4 = [basicsXAxis, topBasicsYAxis];
+  
+      ctx.moveTo(p1[0], p1[1]);
+      ctx.lineTo(p2[0], p2[1]);
+      ctx.lineTo(p3[0], p3[1]);
+      ctx.lineTo(p4[0], p4[1]);
+    },
+  });
+  
+  echarts.graphic.registerShape("leftShape3", leftShape);
+  echarts.graphic.registerShape("rightShape3", rightShape);
+
+  return {
+    ...series,
+    type: "custom",
+    renderItem: (_params: any, api: any) => {
+      // 基础坐标
+
+      //api.value(0)可以获取到对应的Index,value(1)获取到y轴value值
+      //api.coord可以获取到某个点(索引,value)的坐标
+      const basicsCoord = api.coord([api.value(0), api.value(1)]);
+      // 顶部基础 y 轴,注意:是从顶部空白处到头部的距离
+      const topBasicsYAxis = basicsCoord[1];
+      // 基础 x 轴
+      const basicsXAxis = basicsCoord[0] + index * 30;
+      // 底部 y 轴  注意:y轴的距离都是从canvas左上角开始,到当前点位的距离
+      const bottomYAxis = api.coord([api.value(0), 0])[1];
+
+      const basicColor = Array.isArray(color) ? color[index % color.length] : color;
+      const leftColor = lightenColor(basicColor as string, -40);
+
+      return {
+        type: "group",
+        children: [
+          {
+            type: "leftShape3",
+            shape: {
+              topBasicsYAxis,
+              basicsXAxis,
+              bottomYAxis,
+            },
+            style: {
+              fill: leftColor,
+              emphasis: {
+                  fill: "yellow", // 鼠标高亮时的填充颜色
+              },
+            },
+          },
+          {
+            type: "rightShape3",
+            shape: {
+              topBasicsYAxis,
+              basicsXAxis,
+              bottomYAxis,
+            },
+            style: {
+              fill: basicColor,
+              emphasis: {
+                  fill: "yellow", // 鼠标高亮时的填充颜色
+              },
+            },
+          },
+        ],
+      };
+    },
+  };
+};

+ 137 - 0
packages/shalu-dashboard-ui/components/charts/Bar/SpecialBar/src/props.ts

@@ -0,0 +1,137 @@
+import type { PropType, ExtractPropTypes } from "vue";
+import { EChartsOption } from "echarts";
+import { getNormalizedChart, dataSource } from "../../../utils";
+import { DataSourceType } from "../../../chartEnum";
+
+export const componentProps = {
+  width: {
+    type: Number as PropType<number>,
+    default: 400,
+  },
+  height: {
+    type: Number as PropType<number>,
+    default: 260,
+  },
+  dataSource,
+  // 标题
+  title: {
+    type: Object as PropType<EChartsOption["title"]>,
+  },
+  // 图例
+  legend: {
+    type: Object as PropType<EChartsOption["legend"]>,
+  },
+  // 背景
+  backgroundColor: {
+    type: String as PropType<string>,
+  },
+  // 边框
+  grid: {
+    type: Object as PropType<EChartsOption["grid"]>,
+  },
+  // 提示框
+  tooltip: {
+    type: Object as PropType<EChartsOption["tooltip"]>,
+  },
+  // x轴数据
+  xAxis: {
+    type: Object as PropType<EChartsOption["xAxis"]>,
+  },
+  // y轴数据
+  yAxis: {
+    type: Object as PropType<EChartsOption["yAxis"]>,
+  },
+  // 折线
+  series: {
+    type: Array as PropType<EChartsOption["series"]>,
+  },
+  // color
+  color: {
+    type: Object as PropType<EChartsOption["color"]>
+  },
+  // 系列设置
+  seriesExtend: {
+    type: Object as PropType<EChartsOption["series"] & { shape: string}>,
+    default: () => seriesExtend
+  }
+};
+
+/* 系列相关 */
+const series: EChartsOption['series'] = [];
+const seriesExtend = {
+  // @ts-ignore
+  fixedBarWidth: false,
+  barWidth: 'auto',
+  barGap: '10%',
+  barCategoryGap: '20%',
+  itemStyle: {
+    borderColor: '#ccc',
+    borderRadius: 0,
+    borderWidth: 0,
+  },
+  shape: '3d'
+};
+
+const chartOptions = getNormalizedChart({
+  title: {
+    text: "特殊形状柱状图",
+  },
+  xAxis: {
+    data: ['轴标签A', '轴标签B', '轴标签C', '轴标签D']
+  },
+  series,
+  seriesExtend
+})
+
+export const defaultPropsValue: EChartsOption = {
+  // 组件容器默认属性
+  container: {
+    props: {
+      width: 400,
+      height: 260,
+    },
+  },
+  // 图表默认属性
+  props: {
+    // 数据源
+    dataSource: {
+      sourceType: DataSourceType.STATIC,
+      data: {
+        series: [
+          {
+            name: '系列1',
+            data: [10, 30, 20, 40],
+          },
+          {
+            name: '系列2',
+            data: [15, 35, 25, 45]
+          },
+        ]
+      },
+      url: location.origin + "/mock/api/get/example/bar",
+      method: "POST",
+      params: {},
+      headers: {},
+      refreshTime: 0,
+      dataProcess: `
+        (res) => {
+          // 取出列表
+          const data = res.data;
+          // x轴数据
+          const xData = data.map((item) => item.name); 
+          // 系列数据
+          const series = [
+            { type: 'bar', name: '价格', data: data.map(item => item.price) },
+            { type: 'bar', name: '总量', data: data.map(item => item.count) },
+          ];
+
+          // 返回图表数据
+          return { xData, series };
+        }
+      `
+    },
+    ...chartOptions
+  },
+};
+
+export type SpecialBarProps = ExtractPropTypes<typeof componentProps>;

+ 16 - 2
packages/shalu-dashboard-ui/components/components.ts

@@ -1,3 +1,6 @@
+/**
+ * 用于异步加载 
+ */
 export const asyncComponentAll = {
   Title: () => import('./text/Title'),
 
@@ -7,6 +10,7 @@ export const asyncComponentAll = {
   PercentBar: () => import('./charts/Bar/PercentBar'),
   StackBar: () => import('./charts/Bar/StackBar'),
   PolarBar: () => import('./charts/Bar/PolarBar'),
+  SpecialBar: () => import('./charts/Bar/SpecialBar'),
 
   BasicPie: () => import('./charts/Pie/BasicPie'),
 
@@ -81,6 +85,14 @@ export {
   componentProps as polarBarProps
 } from './charts/Bar/PolarBar';
 
+/* 特殊形状柱形图 */
+import { default as SpecialBar } from './charts/Bar/SpecialBar/src/SpecialBar.vue';
+export { 
+  Config as SpecialBarConfig,
+  defaultPropsValue as specialBarDefaultProps,
+  componentProps as specialBarProps
+} from './charts/Bar/SpecialBar';
+
 export const components = {
   BasicLine,
   BasicBar,
@@ -90,7 +102,8 @@ export const components = {
   BasicStrip,
   StackBar,
   PercentBar,
-  PolarBar
+  PolarBar,
+  SpecialBar,
 }
 
 export {
@@ -102,5 +115,6 @@ export {
   BasicStrip,
   StackBar,
   PercentBar,
-  PolarBar
+  PolarBar,
+  SpecialBar,
 }

+ 28 - 0
packages/utils/color.ts

@@ -0,0 +1,28 @@
+/**
+ * 改变颜色亮度
+ * @param color 颜色
+ * @param amount 改变值
+ * @returns 
+ */
+export function lightenColor(color: string, amount: number): string {
+  let usePound = false;
+  if (color[0] === '#') {
+    color = color.slice(1);
+    usePound = true;
+  }
+
+  const num = parseInt(color, 16);
+  let r = (num >> 16) + amount;
+  if (r > 255) r = 255;
+  else if (r < 0) r = 0;
+
+  let b = ((num >> 8) & 0x00ff) + amount;
+  if (b > 255) b = 255;
+  else if (b < 0) b = 0;
+
+  let g = (num & 0x0000ff) + amount;
+  if (g > 255) g = 255;
+  else if (g < 0) g = 0;
+
+  return (usePound ? '#' : '') + (g | (b << 8) | (r << 16)).toString(16).padStart(6, '0');
+}

+ 2 - 1
packages/utils/index.ts

@@ -1,2 +1,3 @@
 export * from './getDataOrigin';
-export * from './transStyle';
+export * from './transStyle';
+export * from './color';