Procházet zdrojové kódy

✨ feat(mini-charts): 新增环形图组件并优化饼图

- 新增 RingChart 组件,专门用于显示环形图
- 新增 RingChartFCExample 组件作为环形图示例
- 重构 PieChart 组件,移除 pieType 参数,专注于饼图功能
- 更新 package.json 导出配置,添加新组件的导出路径
- 优化图表内边距和边框样式配置
- 在示例页面中更新组件使用,将环形图替换为新的 RingChart 组件
- 在示例页面中添加 RingChartFCExample 测试组件
yourname před 2 týdny
rodič
revize
2619f6858c

+ 10 - 0
mini-ui-packages/mini-charts/package.json

@@ -55,6 +55,16 @@
       "types": "./dist/src/components/PieChartFCExample.d.ts",
       "import": "./dist/src/components/PieChartFCExample.js",
       "require": "./dist/src/components/PieChartFCExample.js"
+    },
+    "./components/RingChart": {
+      "types": "./dist/src/components/RingChart.d.ts",
+      "import": "./dist/src/components/RingChart.js",
+      "require": "./dist/src/components/RingChart.js"
+    },
+    "./components/RingChartFCExample": {
+      "types": "./dist/src/components/RingChartFCExample.d.ts",
+      "import": "./dist/src/components/RingChartFCExample.js",
+      "require": "./dist/src/components/RingChartFCExample.js"
     }
   },
   "scripts": {

+ 8 - 13
mini-ui-packages/mini-charts/src/components/PieChart.tsx

@@ -2,11 +2,6 @@ import React from 'react';
 import { BaseChart } from './BaseChart';
 import type { ChartsConfig } from '../lib/u-charts-original';
 
-/**
- * 饼图类型
- */
-export type PieChartType = 'pie' | 'ring';
-
 /**
  * PieChart 组件的 Props 接口
  * 使用原始 u-charts.js + Canvas 2D API
@@ -18,8 +13,6 @@ export interface PieChartProps {
   width?: number;
   /** 图表高度(像素) */
   height?: number;
-  /** 饼图类型 */
-  pieType?: PieChartType;
   /** 系列数据 */
   series: ChartsConfig['series'];
   /** 额外的图表配置 */
@@ -30,14 +23,15 @@ export interface PieChartProps {
  * PieChart 饼图组件
  *
  * 使用原始 u-charts.js + Canvas 2D API
- * 用于显示占比数据的饼图,支持普通饼图和环形图
+ * 用于显示占比数据的饼图
+ *
+ * 注意:环形图请使用 RingChart 组件
  */
 export const PieChart: React.FC<PieChartProps> = (props) => {
   const {
     canvasId,
     width,
     height,
-    pieType = 'pie',
     series,
     config = {},
   } = props;
@@ -49,7 +43,7 @@ export const PieChart: React.FC<PieChartProps> = (props) => {
     animation: true,
     background: '#FFFFFF',
     color: ['#3b82f6', '#10b981', '#f59e0b', '#8b5cf6', '#ef4444'],
-    padding: [15, 15, 0, 5],
+    padding: [5, 5, 5, 5],
     enableScroll: false,
     legend: {},
     extra: {
@@ -57,9 +51,10 @@ export const PieChart: React.FC<PieChartProps> = (props) => {
         activeOpacity: 0.5,
         activeRadius: 10,
         offsetAngle: 0,
-        ringWidth: pieType === 'ring' ? 0.6 : 0,
         labelWidth: 15,
-        ringWidthRatio: pieType === 'ring' ? 0.6 : 0,
+        border: false,
+        borderWidth: 3,
+        borderColor: "#FFFFFF",
         ...config.extra?.pie,
       },
       ...config.extra,
@@ -72,7 +67,7 @@ export const PieChart: React.FC<PieChartProps> = (props) => {
       canvasId={canvasId}
       width={width}
       height={height}
-      type={pieType}
+      type="pie"
       series={series}
       config={mergedConfig}
     />

+ 80 - 0
mini-ui-packages/mini-charts/src/components/RingChart.tsx

@@ -0,0 +1,80 @@
+import React from 'react';
+import { BaseChart } from './BaseChart';
+import type { ChartsConfig } from '../lib/u-charts-original';
+
+/**
+ * RingChart 环形图组件的 Props 接口
+ * 使用原始 u-charts.js + Canvas 2D API
+ */
+export interface RingChartProps {
+  /** Canvas 元素的 ID,必须唯一 */
+  canvasId: string;
+  /** 图表宽度(像素) */
+  width?: number;
+  /** 图表高度(像素) */
+  height?: number;
+  /** 环形宽度(0-1之间的比例,默认 0.6) */
+  ringWidth?: number;
+  /** 系列数据 */
+  series: ChartsConfig['series'];
+  /** 额外的图表配置 */
+  config?: Partial<ChartsConfig>;
+}
+
+/**
+ * RingChart 环形图组件
+ *
+ * 使用原始 u-charts.js + Canvas 2D API
+ * 用于显示占比数据的环形图
+ */
+export const RingChart: React.FC<RingChartProps> = (props) => {
+  const {
+    canvasId,
+    width,
+    height,
+    ringWidth = 0.6,
+    series,
+    config = {},
+  } = props;
+
+  /**
+   * 合并默认配置
+   */
+  const mergedConfig: Partial<ChartsConfig> = {
+    animation: true,
+    background: '#FFFFFF',
+    color: ['#3b82f6', '#10b981', '#f59e0b', '#8b5cf6', '#ef4444'],
+    padding: [5, 5, 5, 5],
+    enableScroll: false,
+    legend: {},
+    extra: {
+      pie: {
+        activeOpacity: 0.5,
+        activeRadius: 10,
+        offsetAngle: 0,
+        labelWidth: 15,
+        ringWidth: ringWidth,
+        ringWidthRatio: ringWidth,
+        border: false,
+        borderWidth: 3,
+        borderColor: "#FFFFFF",
+        ...config.extra?.pie,
+      },
+      ...config.extra,
+    },
+    ...config,
+  };
+
+  return (
+    <BaseChart
+      canvasId={canvasId}
+      width={width}
+      height={height}
+      type="ring"
+      series={series}
+      config={mergedConfig}
+    />
+  );
+};
+
+export default RingChart;

+ 136 - 0
mini-ui-packages/mini-charts/src/components/RingChartFCExample.tsx

@@ -0,0 +1,136 @@
+import React, { useState, useRef, useLayoutEffect } from 'react';
+import Taro from '@tarojs/taro';
+import { View, Canvas } from '@tarojs/components';
+import uChartsClass from '../lib/u-charts-original.js';
+import type { ChartsConfig, TouchEvent } from '../lib/u-charts-original';
+import type { ExtendedCanvasContext } from '../types';
+
+/**
+ * RingChartFCExample 组件
+ *
+ * FC 示例组件(环形图版本)
+ * 使用原始 u-charts.js + Canvas 2D API
+ *
+ * 用于与 BaseChartOriginal2D 进行对比调试
+ */
+interface RingChartFCExampleProps {
+  /** Canvas 元素的 ID,必须唯一 */
+  canvasId?: string;
+  /** 图表宽度(像素),默认为屏幕宽度 */
+  width?: number;
+  /** 图表高度(像素),默认根据宽高比计算 */
+  height?: number;
+}
+
+function RingChartFCComponent(props: RingChartFCExampleProps) {
+  const { canvasId = 'RingChartFCExample', width = 750, height = 500 } = props;
+
+  const [cWidth, setCWidth] = useState(750);
+  const [cHeight, setCHeight] = useState(500);
+  const [pixelRatio, setPixelRatio] = useState(1);
+
+  // 使用 ref 存储图表实例
+  const uChartsInstanceRef = useRef<Record<string, any>>({});
+
+  const drawCharts = (id: string, data: any) => {
+    const query = Taro.createSelectorQuery();
+    query.select('#' + id).fields({ node: true, size: true }).exec(res => {
+      if (res[0]) {
+        const canvas = res[0].node;
+        const ctx = canvas.getContext('2d') as ExtendedCanvasContext;
+        const chart = new uChartsClass({
+          type: "ring",
+          context: ctx,
+          width: canvas.width,
+          height: canvas.height,
+          series: data.series,
+          animation: true,
+          background: "#FFFFFF",
+          color: ["#1890FF","#91CB74","#FAC858","#EE6666","#73C0DE","#3CA272","#FC8452","#9A60B4","#ea7ccc"],
+          padding: [5,5,5,5],
+          enableScroll: false,
+          extra: {
+            pie: {
+              activeOpacity: 0.5,
+              activeRadius: 10,
+              offsetAngle: 0,
+              labelWidth: 15,
+              ringWidth: 0.6,
+              ringWidthRatio: 0.6,
+              border: false,
+              borderWidth: 3,
+              borderColor: "#FFFFFF"
+            }
+          }
+        } as ChartsConfig);
+        uChartsInstanceRef.current[id] = chart;
+        const series = data.series;
+        console.log('[RingChartFCExample] 图表初始化完成:', id, {
+          cWidth, cHeight,
+          canvasWidth: canvas.width,
+          canvasHeight: canvas.height,
+          seriesLength: series.length,
+          series,
+        });
+      } else {
+        console.error("[RingChartFCExample]: 未获取到 context");
+      }
+    });
+  }
+
+  const getServerData = () => {
+    //模拟从服务器获取数据时的延时
+    setTimeout(() => {
+      //模拟服务器返回数据,如果数据格式和标准格式不同,需自行按下面的格式拼接
+      let res = {
+        series: [
+          {
+            data: [{"name":"在职","value":24},{"name":"离职","value":8},{"name":"试用期","value":5},{"name":"停薪留职","value":2}]
+          }
+        ]
+      };
+      drawCharts(canvasId, res);
+    }, 500);
+  }
+
+  const tap = (e: any) => {
+    uChartsInstanceRef.current[e.currentTarget.id]?.touchLegend(e);
+    uChartsInstanceRef.current[e.currentTarget.id]?.showToolTip(e);
+  }
+
+  useLayoutEffect(() => {
+    console.debug('[RingChartFCExample] useLayoutEffect')
+    const sysInfo = Taro.getSystemInfoSync();
+    const pr = sysInfo.pixelRatio;
+    //这里的第一个 750 对应 css .charts 的 width
+    const cw = width / 750 * sysInfo.windowWidth;
+    //这里的 500 对应 css .charts 的 height
+    const ch = height / 750 * sysInfo.windowWidth;
+    setCWidth(cw);
+    setCHeight(ch);
+    setPixelRatio(pr);
+
+    // 直接在这里获取数据
+    getServerData();
+  }, [width, height, canvasId]);
+
+  const canvasProps = { style: { width: cWidth, height: cHeight } };
+  return (
+    <View>
+      <Canvas
+        {...canvasProps}
+        canvas-id={canvasId}
+        id={canvasId}
+        type="2d"
+        className="charts"
+        onTouchEnd={tap}
+      />
+    </View>
+  );
+}
+
+// 默认导出
+export default RingChartFCComponent;
+
+// 命名导出,保持向后兼容
+export const RingChartFCExample = RingChartFCComponent;

+ 15 - 3
mini-ui-packages/yongren-statistics-ui/src/pages/Statistics/Statistics.tsx

@@ -6,7 +6,9 @@ import { Navbar } from '@d8d/mini-shared-ui-components/components/navbar'
 import { ColumnChart } from '@d8d/mini-charts/components/ColumnChart'
 import { BarChart } from '@d8d/mini-charts/components/BarChart'
 import { PieChart } from '@d8d/mini-charts/components/PieChart'
+import { RingChart } from '@d8d/mini-charts/components/RingChart'
 import { PieChartFCExample } from '@d8d/mini-charts/components/PieChartFCExample'
+import { RingChartFCExample } from '@d8d/mini-charts/components/RingChartFCExample'
 import { enterpriseStatisticsClient } from '../../api/enterpriseStatisticsClient'
 import type {
   DisabilityTypeDistributionResponse,
@@ -320,7 +322,6 @@ const Statistics: React.FC<StatisticsProps> = () => {
                     canvasId="age-chart"
                     width={650}
                     height={300}
-                    pieType="pie"
                     series={convertToPieData(ageStats)}
                     config={{
                       color: ['#3b82f6', '#10b981', '#f59e0b', '#8b5cf6', '#ef4444'],
@@ -372,11 +373,10 @@ const Statistics: React.FC<StatisticsProps> = () => {
               if (stats.length > 0) {
                 return (
                   <View className="mt-3">
-                    <PieChart
+                    <RingChart
                       canvasId="job-status-chart"
                       width={650}
                       height={300}
-                      pieType="ring"
                       series={convertToPieData(stats)}
                       config={{
                         color: ['#3b82f6', '#f59e0b', '#ef4444', '#10b981'],
@@ -431,6 +431,18 @@ const Statistics: React.FC<StatisticsProps> = () => {
           </View>
         </View>
 
+        {/* RingChartFCExample 测试组件 */}
+        <View className="card bg-white p-4 mb-4 rounded-lg shadow-sm flex flex-col">
+          <Text className="font-semibold text-gray-700">环形图示例测试 (RingChartFCExample)</Text>
+          <View className="mt-3">
+            <RingChartFCExample
+              canvasId="ring-chart-fc-example-test"
+              width={650}
+              height={400}
+            />
+          </View>
+        </View>
+
       </ScrollView>
     </YongrenTabBarLayout>
   )