亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java實(shí)現(xiàn)餅圖旋轉(zhuǎn)角度的代碼詳解

 更新時(shí)間:2025年05月12日 08:41:57   作者:Katie。  
在現(xiàn)代數(shù)據(jù)可視化領(lǐng)域,餅圖因其直觀展示各部分占整體比例而被廣泛采用,為了增強(qiáng)互動(dòng)性和吸引力,常會(huì)賦予餅圖 旋轉(zhuǎn) 動(dòng)畫:自動(dòng)、平滑地旋轉(zhuǎn),讓用戶從不同角度重點(diǎn)查看扇區(qū),本文將從零開始,手把手實(shí)現(xiàn)一個(gè) Java2D Swing 版 的 可旋轉(zhuǎn)餅圖組件,需要的朋友可以參考下

一、項(xiàng)目介紹

1.1 背景

在現(xiàn)代數(shù)據(jù)可視化領(lǐng)域,餅圖(Pie Chart)因其直觀展示各部分占整體比例而被廣泛采用。為了增強(qiáng)互動(dòng)性和吸引力,常會(huì)賦予餅圖 旋轉(zhuǎn) 動(dòng)畫:自動(dòng)、平滑地旋轉(zhuǎn),讓用戶從不同角度重點(diǎn)查看扇區(qū)。旋轉(zhuǎn)角度可以突出數(shù)據(jù)變化、引導(dǎo)觀看順序、提升界面動(dòng)感。然而,要在 Java Swing/Java2D 環(huán)境下實(shí)現(xiàn)一個(gè)既平滑又可交互的餅圖旋轉(zhuǎn),需要深入掌握以下難點(diǎn):

  • 角度映射:將時(shí)間或幀數(shù)映射到旋轉(zhuǎn)角度,并與餅圖扇區(qū)正確對齊。

  • 繪制順序:在旋轉(zhuǎn)過程中,正確處理扇區(qū)的繪制順序,避免前后扇區(qū)遮擋錯(cuò)亂。

  • 動(dòng)畫驅(qū)動(dòng):使用 javax.swing.Timer 或高精度定時(shí)器控制旋轉(zhuǎn)流暢度。

  • 交互響應(yīng):支持暫停/繼續(xù)、方向切換、速率調(diào)節(jié)及拖拽控制。

1.2 目標(biāo)

本文將從零開始,手把手實(shí)現(xiàn)一個(gè) Java2D Swing 版 的 可旋轉(zhuǎn)餅圖組件,重點(diǎn)在于:

  1. 自動(dòng)旋轉(zhuǎn):按設(shè)定速率平滑且連續(xù)地旋轉(zhuǎn)。

  2. 角度控制:可隨時(shí)獲取與設(shè)置當(dāng)前旋轉(zhuǎn)角度,實(shí)現(xiàn)“瞬時(shí)跳轉(zhuǎn)”或動(dòng)畫過渡。

  3. 方向切換:順時(shí)針或逆時(shí)針旋轉(zhuǎn)可動(dòng)態(tài)切換。

  4. 拖拽控制:鼠標(biāo)拖拽實(shí)時(shí)控制餅圖角度,打斷/恢復(fù)自動(dòng)旋轉(zhuǎn)。

  5. 完整封裝:提供易用 API,支持在任意 Swing 界面中嵌入。

二、相關(guān)技術(shù)與知識

要實(shí)現(xiàn)以上功能,需要掌握和理解以下技術(shù)要點(diǎn)。

2.1 Java2D 繪圖基礎(chǔ)

  • Graphics2D:Java2D 的核心渲染上下文,支持抗鋸齒、變換、復(fù)合等。

  • 形狀構(gòu)造Arc2D 繪制扇形,Path2D 構(gòu)造側(cè)面形狀。

  • 抗鋸齒:通過 RenderingHint.KEY_ANTIALIASING 提升繪圖質(zhì)量。

  • 透明度:使用 AlphaComposite 控制半透明效果。

2.2 動(dòng)畫驅(qū)動(dòng)

  • Swing Timerjavax.swing.Timer 在事件分發(fā)線程(EDT)觸發(fā)周期性 事件,安全刷圖。

  • 幀率與速率:根據(jù)延遲(delay)和每分鐘旋轉(zhuǎn)度數(shù)(RPM)計(jì)算每幀增量角度 delta = rpm * 360° / (60_000ms / delay)。

  • 平滑度:選擇合適的 delay(例如 16ms≈60FPS 或 40ms≈25FPS)平衡流暢度與性能。

2.3 深度排序

雖然我們演示的是 2D 餅圖,但若添加 3D 側(cè)面 或 陰影,則需要 深度排序:在每幀根據(jù)扇區(qū)當(dāng)前中心角度的正余弦值判斷其“前后”關(guān)系,先畫遠(yuǎn)處扇區(qū)再畫近處扇區(qū),保證遮擋效果自然。

2.4 交互處理

  • 鼠標(biāo)拖拽MouseListener + MouseMotionListener 捕獲按下、拖拽、釋放事件,實(shí)時(shí)映射拖動(dòng)距離到角度偏移。

  • 暫停/恢復(fù):拖拽開始時(shí)停止自動(dòng)旋轉(zhuǎn),釋放時(shí)可繼續(xù)。

  • 方向切換與速率調(diào)節(jié):通過暴露 API 允許調(diào)用者動(dòng)態(tài)更改 rpm 與 clockwise 標(biāo)志。

三、實(shí)現(xiàn)思路

結(jié)合上述技術(shù)棧,我們將按以下思路實(shí)現(xiàn):

  1. 數(shù)據(jù)模型

    • 定義內(nèi)部 PieSlice 類:保存扇區(qū) value、color、label、startAngle、arcAngle。

    • totalValue 累加所有扇區(qū)數(shù)值。

    • computeAngles() 方法按比例分配角度。

  2. 組件封裝

    • 繼承 JPanel,命名為 RotatingPieChartPanel,暴露 API:

      • addSlice(value, color, label)

      • setRotateSpeed(rpm)

      • setClockwise(boolean)

      • start() / stop()

      • setAngle(double) / getAngle() 實(shí)現(xiàn)“瞬時(shí)跳轉(zhuǎn)”。

  3. 動(dòng)畫與繪制

    • 在構(gòu)造器中創(chuàng)建 Timer(animationDelay, e->{ advanceOffset(); repaint(); })。

    • advanceOffset() 根據(jù) rpm 與 clockwise 計(jì)算 angleOffset

    • paintComponent() 中調(diào)用 drawPie(),分三步:陰影 → 側(cè)面(需深度排序) → 頂面。

  4. 交互

    • 添加 MouseAdapter

      • mousePressed 開始拖拽,記錄初始 angleOffset 與鼠標(biāo)點(diǎn);

      • mouseDragged 根據(jù)水平方向位移映射到增量角度,更新 angleOffset 并 repaint();

      • mouseReleased 結(jié)束拖拽,重啟動(dòng)畫。

  5. 深度排序

    • 在繪制側(cè)面時(shí),先復(fù)制扇區(qū)列表,按每個(gè)扇區(qū) 中心角度 的正弦值(或余弦值)排序;

    • depthKey = Math.sin(Math.toRadians(startAngle + arcAngle/2 + angleOffset)),值大者后繪制。

四、完整實(shí)現(xiàn)代碼

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
import java.util.List;
 
/**
 * RotatingPieChartPanel:可自動(dòng)/手動(dòng)旋轉(zhuǎn)且正確排序的餅圖組件
 */
public class RotatingPieChartPanel extends JPanel {
 
    /** 內(nèi)部扇區(qū)模型 */
    private static class PieSlice {
        double value;          // 扇區(qū)數(shù)值
        Color color;           // 扇區(qū)顏色
        String label;          // 扇區(qū)標(biāo)簽
        double startAngle;     // 起始角度(度)
        double arcAngle;       // 扇區(qū)角度(度)
        boolean highlighted;   // 是否高亮
 
        PieSlice(double value, Color color, String label) {
            this.value = value;
            this.color = color;
            this.label = label;
            this.highlighted = false;
        }
    }
 
    private final List<PieSlice> slices = new ArrayList<>();
    private double totalValue = 0.0;
 
    // 旋轉(zhuǎn)控制
    private double angleOffset = 0.0;  // 當(dāng)前偏移角度
    private double rpm = 1.0;          // 每分鐘度數(shù)
    private boolean clockwise = true;  // 旋轉(zhuǎn)方向
    private Timer animationTimer;      // 用于自動(dòng)旋轉(zhuǎn)
 
    // 3D 效果深度(像素)
    private double depth = 50.0;
 
    // 拖拽交互狀態(tài)
    private boolean dragging = false;
    private double dragStartOffset;
    private Point dragStartPoint;
 
    public RotatingPieChartPanel() {
        setBackground(Color.WHITE);
        setPreferredSize(new Dimension(600, 400));
        initInteraction();
    }
 
    /** 初始化鼠標(biāo)交互:拖拽控制 */
    private void initInteraction() {
        MouseAdapter ma = new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                // 停止自動(dòng)旋轉(zhuǎn),進(jìn)入拖拽狀態(tài)
                stop();
                dragging = true;
                dragStartOffset = angleOffset;
                dragStartPoint = e.getPoint();
            }
 
            @Override
            public void mouseDragged(MouseEvent e) {
                if (!dragging) return;
                Point pt = e.getPoint();
                double dx = pt.x - dragStartPoint.x;
                // 每像素對應(yīng) 0.5 度
                angleOffset = dragStartOffset + dx * 0.5;
                repaint();
            }
 
            @Override
            public void mouseReleased(MouseEvent e) {
                dragging = false;
                start(); // 恢復(fù)自動(dòng)旋轉(zhuǎn)
            }
        };
        addMouseListener(ma);
        addMouseMotionListener(ma);
    }
 
    /** 添加扇區(qū) */
    public void addSlice(double value, Color color, String label) {
        slices.add(new PieSlice(value, color, label));
        totalValue += value;
        computeAngles();
        repaint();
    }
 
    /** 重新計(jì)算扇區(qū)角度 */
    private void computeAngles() {
        double angle = 0.0;
        for (PieSlice s : slices) {
            s.startAngle = angle;
            s.arcAngle = s.value / totalValue * 360.0;
            angle += s.arcAngle;
        }
    }
 
    /** 設(shè)置旋轉(zhuǎn)速率(RPM) */
    public void setRotateSpeed(double rpm) {
        this.rpm = rpm;
        if (animationTimer != null && animationTimer.isRunning()) {
            stop();
            start();
        }
    }
 
    /** 設(shè)置旋轉(zhuǎn)方向 */
    public void setClockwise(boolean cw) {
        this.clockwise = cw;
    }
 
    /** 設(shè)置 3D 深度 */
    public void setDepth(double depth) {
        this.depth = depth;
        repaint();
    }
 
    /** 啟動(dòng)自動(dòng)旋轉(zhuǎn) */
    public void start() {
        if (animationTimer != null && animationTimer.isRunning()) return;
        int delay = 40; // 25 FPS
        double deltaDeg = rpm * 360.0 / (60_000.0 / delay);
        animationTimer = new Timer(delay, e -> {
            angleOffset += (clockwise ? -deltaDeg : deltaDeg);
            repaint();
        });
        animationTimer.start();
    }
 
    /** 停止自動(dòng)旋轉(zhuǎn) */
    public void stop() {
        if (animationTimer != null) {
            animationTimer.stop();
            animationTimer = null;
        }
    }
 
    /** 獲取當(dāng)前角度 */
    public double getAngle() {
        return angleOffset;
    }
 
    /** 直接設(shè)置角度(瞬時(shí)跳轉(zhuǎn)) */
    public void setAngle(double angle) {
        this.angleOffset = angle % 360.0;
        repaint();
    }
 
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        renderPie((Graphics2D) g);
    }
 
    /** 繪制餅圖:陰影 → 側(cè)面(深度排序) → 頂面 */
    private void renderPie(Graphics2D g2) {
        // 抗鋸齒
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                            RenderingHints.VALUE_ANTIALIAS_ON);
 
        int w = getWidth(), h = getHeight();
        double cx = w / 2.0, cy = h / 2.0 - depth / 2.0;
        double r = Math.min(w, h - depth) / 2.0 - 20.0;
 
        // 1. 繪制陰影
        drawShadow(g2, cx, cy, r);
 
        // 2. 深度排序并繪制側(cè)面
        List<PieSlice> sorted = new ArrayList<>(slices);
        sorted.sort(Comparator.comparingDouble(this::depthKey));
        for (PieSlice s : sorted) {
            drawSide(g2, cx, cy, r, s);
        }
 
        // 3. 繪制頂面
        for (PieSlice s : sorted) {
            drawTop(g2, cx, cy, r, s);
        }
    }
 
    /** 計(jì)算深度排序 key:扇區(qū)中心角度的 sin 值 */
    private double depthKey(PieSlice s) {
        double mid = s.startAngle + s.arcAngle / 2.0 + angleOffset;
        return Math.sin(Math.toRadians(mid));
    }
 
    /** 繪制底部陰影 */
    private void drawShadow(Graphics2D g2,
                            double cx, double cy, double r) {
        Ellipse2D shadow = new Ellipse2D.Double(
            cx - r, cy + depth - r / 3.0 * 2, 2 * r, r / 2.0
        );
        Composite old = g2.getComposite();
        g2.setComposite(AlphaComposite.getInstance(
            AlphaComposite.SRC_OVER, 0.3f
        ));
        g2.setColor(Color.BLACK);
        g2.fill(shadow);
        g2.setComposite(old);
    }
 
    /** 繪制扇區(qū)側(cè)面 */
    private void drawSide(Graphics2D g2,
                          double cx, double cy, double r, PieSlice s) {
        double sa = Math.toRadians(s.startAngle + angleOffset);
        double ea = Math.toRadians(s.startAngle + s.arcAngle + angleOffset);
 
        Point2D p1 = new Point2D.Double(
            cx + r * Math.cos(sa), cy + r * Math.sin(sa)
        );
        Point2D p2 = new Point2D.Double(
            cx + r * Math.cos(ea), cy + r * Math.sin(ea)
        );
        Point2D p3 = new Point2D.Double(p2.getX(), p2.getY() + depth);
        Point2D p4 = new Point2D.Double(p1.getX(), p1.getY() + depth);
 
        Path2D side = new Path2D.Double();
        side.moveTo(p1.getX(), p1.getY());
        side.lineTo(p4.getX(), p4.getY());
        side.lineTo(p3.getX(), p3.getY());
        side.lineTo(p2.getX(), p2.getY());
        side.closePath();
 
        g2.setColor(s.color.darker());
        g2.fill(side);
        if (s.highlighted) {
            g2.setColor(Color.WHITE);
            g2.setStroke(new BasicStroke(2));
            g2.draw(side);
        }
    }
 
    /** 繪制扇區(qū)頂面 */
    private void drawTop(Graphics2D g2,
                         double cx, double cy, double r, PieSlice s) {
        Arc2D top = new Arc2D.Double(
            cx - r, cy - r, 2 * r, 2 * r,
            s.startAngle + angleOffset,
            s.arcAngle, Arc2D.PIE
        );
        g2.setColor(s.color);
        g2.fill(top);
        if (s.highlighted) {
            g2.setColor(Color.WHITE);
            g2.setStroke(new BasicStroke(2));
            g2.draw(top);
        }
    }
 
    // 可擴(kuò)展:添加高亮與提示功能
}
 
/**
 * DemoMain:演示 RotatingPieChartPanel 用法
 */
class DemoMain {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            RotatingPieChartPanel pie = new RotatingPieChartPanel();
            pie.addSlice(30, Color.RED,   "紅");
            pie.addSlice(20, Color.BLUE,  "藍(lán)");
            pie.addSlice(40, Color.GREEN, "綠");
            pie.addSlice(10, Color.ORANGE,"橙");
            pie.setDepth(60);
            pie.setRotateSpeed(2.5);
            pie.setClockwise(false);
            pie.start();
 
            JFrame f = new JFrame("可旋轉(zhuǎn)餅圖示例");
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.add(pie);
            f.pack();
            f.setLocationRelativeTo(null);
            f.setVisible(true);
        });
    }
}

五、方法級功能解讀

  1. addSlice(value, color, label)

    • 創(chuàng)建 PieSlice 對象,累加 totalValue,調(diào)用 computeAngles() 重新計(jì)算所有扇區(qū)的角度分布。

  2. computeAngles()

    • 遍歷 slices 列表,按比例 (value / totalValue) * 360° 分配各扇區(qū) arcAngle,并依次累加 startAngle

  3. start() / stop()

    • 使用 javax.swing.Timerdelay = 40ms,每次 actionPerformed 中計(jì)算增量

double deltaDeg = rpm * 360.0 / (60_000.0 / delay);
angleOffset += clockwise ? -deltaDeg : deltaDeg;
  • 調(diào)用 repaint() 刷新組件。
  1. paintComponent(...)

    • 先 super.paintComponent(g) 清除背景,然后調(diào)用 renderPie(g2)

      • 啟用抗鋸齒

      • 計(jì)算中心 (cx,cy) 與半徑 r

      • 調(diào)用 drawShadow、drawSide(深度排序)和 drawTop

  2. depthKey(PieSlice s)

    • 計(jì)算扇區(qū)中心角度:s.startAngle + s.arcAngle/2 + angleOffset

    • 取正弦值作為深度排序依據(jù)(越大越“前”),并對列表排序,保證先畫“后面”的側(cè)面,再畫“前面”的側(cè)面與頂面。

  3. drawShadow

    • 底部繪制半透明黑色橢圓,使用 AlphaComposite 設(shè)為 0.3f。

  4. drawSide

    • 計(jì)算扇區(qū)邊緣兩點(diǎn) (p1, p2),并向下延伸 depth 得到底部兩點(diǎn) (p4, p3)

    • 構(gòu)造 Path2D 四邊形填充較暗顏色;

  5. drawTop

    • 使用 Arc2D.PIE 繪制扇形頂面;

  6. 拖拽交互

    • mousePressed 中停止自動(dòng)旋轉(zhuǎn)并記錄初始狀態(tài);

    • mouseDragged 根據(jù)水平位移映射到增量角度更新 angleOffset;

    • mouseReleased 中恢復(fù)自動(dòng)旋轉(zhuǎn)。

六、項(xiàng)目總結(jié)與擴(kuò)展思考

6.1 核心收獲

  • 深入理解 Java2D 在復(fù)雜動(dòng)態(tài)圖形中的應(yīng)用技巧;

  • 掌握 旋轉(zhuǎn)動(dòng)畫 與 幀率控制 的實(shí)現(xiàn);

  • 學(xué)會(huì)使用 深度排序 解決旋轉(zhuǎn)遮擋問題;

  • 熟悉 拖拽交互 在圖形組件中的集成。

6.2 性能優(yōu)化建議

  1. Shape 緩存:對每個(gè)扇區(qū)在固定角度步長下預(yù)生成 Path2D 與 Arc2D,避免每幀大量對象創(chuàng)建。

  2. 離屏緩沖:使用 BufferedImage 或 VolatileImage 離屏渲染靜態(tài)部分(陰影、側(cè)面基礎(chǔ)形狀),只動(dòng)態(tài)繪制旋轉(zhuǎn)部分。

  3. OpenGL 加速:設(shè)置系統(tǒng)屬性 -Dsun.java2d.opengl=true 啟用硬件加速。

6.3 擴(kuò)展功能

  • 漸變與紋理:為扇面添加漸變填充或貼圖。

  • 多層餅圖/環(huán)形圖:支持環(huán)形(Donut)或嵌套餅圖。

  • 標(biāo)簽與引導(dǎo)線:在旋轉(zhuǎn)中動(dòng)態(tài)顯示標(biāo)簽,引導(dǎo)線可選顯示。

  • JavaFX 版本:基于 JavaFX Canvas 或 3D API 實(shí)現(xiàn)更高性能和光照效果。

以上就是Java實(shí)現(xiàn)餅圖旋轉(zhuǎn)角度的代碼詳解的詳細(xì)內(nèi)容,更多關(guān)于Java餅圖旋轉(zhuǎn)角度的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • IntelliJ?IDEA?2023.1.4?無法刷新Maven項(xiàng)目模塊的問題及解決方法

    IntelliJ?IDEA?2023.1.4?無法刷新Maven項(xiàng)目模塊的問題及解決方法

    這篇文章主要介紹了如何排查?IDEA?自身報(bào)錯(cuò)問題,本文以IntelliJ?IDEA?2023.1.4無法刷新項(xiàng)目Maven模塊的問題為例,給大家詳細(xì)講解,需要的朋友可以參考下
    2023-08-08
  • 使用JAVA8 filter對List多條件篩選的實(shí)現(xiàn)

    使用JAVA8 filter對List多條件篩選的實(shí)現(xiàn)

    這篇文章主要介紹了使用JAVA8 filter對List多條件篩選的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • Java如何利用狀態(tài)模式(state pattern)替代if else

    Java如何利用狀態(tài)模式(state pattern)替代if else

    這篇文章主要給大家介紹了關(guān)于Java如何利用狀態(tài)模式(state pattern)替代if else的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • 基于HttpServletRequest 相關(guān)常用方法的應(yīng)用

    基于HttpServletRequest 相關(guān)常用方法的應(yīng)用

    本篇文章小編為大家介紹,基于HttpServletRequest 相關(guān)常用方法的應(yīng)用,需要的朋友參考下
    2013-04-04
  • Java集合繼承體系詳解

    Java集合繼承體系詳解

    這篇文章主要為大家詳細(xì)介紹了Java集合繼承體系,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • 深入了解Spring中g(shù)etBean()的五種方式

    深入了解Spring中g(shù)etBean()的五種方式

    在本文中,我們將詳細(xì)介紹從BeanFactory中獲取bean的多種方式。簡單地說,正如方法的名稱所表達(dá)的,getBean()負(fù)責(zé)從Spring?IOC容器中獲取bean實(shí)例,希望對大家有所幫助
    2023-02-02
  • Spring Properties的使用和配置方法

    Spring Properties的使用和配置方法

    這篇文章主要介紹了Spring Properties的使用和配置方法,本文不是原理分析、源碼分析文章,只是希望可以幫助讀者更好地理解和使用 Spring Properties,有興趣的可以了解一下
    2018-01-01
  • Java 實(shí)現(xiàn)攔截器Interceptor的攔截功能方式

    Java 實(shí)現(xiàn)攔截器Interceptor的攔截功能方式

    這篇文章主要介紹了Java 實(shí)現(xiàn)攔截器Interceptor的攔截功能方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • Java中final、static關(guān)鍵字與方法的重寫和繼承易錯(cuò)點(diǎn)整理

    Java中final、static關(guān)鍵字與方法的重寫和繼承易錯(cuò)點(diǎn)整理

    這篇文章主要給大家介紹了關(guān)于Java中final、static關(guān)鍵字與方法的重寫和繼承易錯(cuò)點(diǎn)的相關(guān)資料,在Java編程中final關(guān)鍵字用于限制方法或類的進(jìn)一步修改,final方法不能被子類重寫,而static方法不可被重寫,只能被遮蔽,需要的朋友可以參考下
    2024-10-10
  • 基于mybatis-plus-generator實(shí)現(xiàn)代碼自動(dòng)生成器

    基于mybatis-plus-generator實(shí)現(xiàn)代碼自動(dòng)生成器

    這篇文章專門為小白準(zhǔn)備了入門級mybatis-plus-generator代碼自動(dòng)生成器,可以提高開發(fā)效率。文中的示例代碼講解詳細(xì),感興趣的可以了解一下
    2022-05-05

最新評論