فهرست منبع

feat:美化数据统计页面的显示

yangyi 1 ماه پیش
والد
کامیت
0d82f114e6

+ 27 - 26
client/src/main/java/space/anyi/client/controller/MainController.java

@@ -3,6 +3,7 @@ package space.anyi.client.controller;
 import javafx.application.Platform;
 import javafx.collections.FXCollections;
 import javafx.fxml.FXML;
+import javafx.scene.chart.*;
 import javafx.fxml.FXMLLoader;
 import javafx.scene.Node;
 import javafx.scene.control.*;
@@ -122,7 +123,9 @@ public class MainController {
     @FXML private TableView<DeptDiscountDTO> discountTable;
     @FXML private TableColumn<DeptDiscountDTO, String> discountDeptCol;
     @FXML private TableColumn<DeptDiscountDTO, java.math.BigDecimal> discountRateCol;
-    @FXML private TableColumn<DeptDiscountDTO, Void> discountBarCol;
+    @FXML private PieChart deptPieChart;
+    @FXML private BarChart<String, Number> hotBookChart;
+    @FXML private LineChart<String, Number> discountChart;
 
     // Admin tab
     @FXML private TableView<User> userTable;
@@ -506,7 +509,9 @@ public class MainController {
                 }
             }
         });
-        transTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
+        deptTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
+        hotBookTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
+        discountTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
     }
 
     @FXML
@@ -838,30 +843,6 @@ public class MainController {
 
         discountDeptCol.setCellValueFactory(new PropertyValueFactory<>("dept"));
         discountRateCol.setCellValueFactory(new PropertyValueFactory<>("avgDiscountRate"));
-        discountBarCol.setCellFactory(col -> new TableCell<DeptDiscountDTO, Void>() {
-            @Override
-            protected void updateItem(Void item, boolean empty) {
-                super.updateItem(item, empty);
-                if (empty) {
-                    setGraphic(null);
-                } else {
-                    DeptDiscountDTO d = getTableView().getItems().get(getIndex());
-                    if (d.getAvgDiscountRate() == null) {
-                        setGraphic(null);
-                        return;
-                    }
-                    double rate = d.getAvgDiscountRate().doubleValue();
-                    ProgressBar bar = new ProgressBar(rate / 100.0);
-                    bar.setPrefWidth(150);
-                    bar.setPrefHeight(8);
-                    Label pct = new Label(String.format("%.0f%%", rate));
-                    pct.setStyle("-fx-font-size: 12px; -fx-text-fill: -fx-text-secondary;");
-                    HBox hb = new HBox(8, bar, pct);
-                    hb.setAlignment(Pos.CENTER_LEFT);
-                    setGraphic(hb);
-                }
-            }
-        });
     }
 
     @FXML
@@ -875,16 +856,36 @@ public class MainController {
         R<List<DeptSalesDTO>> deptResult = statisticsService.getTopDepartments();
         if (deptResult.getCode() == 200 && deptResult.getData() != null) {
             deptTable.setItems(FXCollections.observableArrayList(deptResult.getData()));
+            deptPieChart.getData().clear();
+            for (DeptSalesDTO d : deptResult.getData()) {
+                deptPieChart.getData().add(new PieChart.Data(d.getDept(), d.getSalesAmount().doubleValue()));
+            }
         }
 
         R<List<HotBookDTO>> bookResult = statisticsService.getTopBooks();
         if (bookResult.getCode() == 200 && bookResult.getData() != null) {
             hotBookTable.setItems(FXCollections.observableArrayList(bookResult.getData()));
+            hotBookChart.getData().clear();
+            XYChart.Series<String, Number> series = new XYChart.Series<>();
+            series.setName("销量");
+            for (HotBookDTO b : bookResult.getData()) {
+                String label = b.getTitle();
+                if (label.length() > 8) label = label.substring(0, 8) + "…";
+                series.getData().add(new XYChart.Data<>(label, b.getTotalSold()));
+            }
+            hotBookChart.getData().add(series);
         }
 
         R<List<DeptDiscountDTO>> discountResult = statisticsService.getDiscountRates();
         if (discountResult.getCode() == 200 && discountResult.getData() != null) {
             discountTable.setItems(FXCollections.observableArrayList(discountResult.getData()));
+            discountChart.getData().clear();
+            XYChart.Series<String, Number> ds = new XYChart.Series<>();
+            ds.setName("折扣率");
+            for (DeptDiscountDTO d : discountResult.getData()) {
+                ds.getData().add(new XYChart.Data<>(d.getDept(), d.getAvgDiscountRate().doubleValue()));
+            }
+            discountChart.getData().add(ds);
         }
     }
 

+ 48 - 0
client/src/main/resources/css/style.css

@@ -346,3 +346,51 @@
     -fx-font-weight: bold;
     -fx-text-fill: -fx-text;
 }
+
+/* ── 图表 ── */
+.chart {
+    -fx-background-color: transparent;
+    -fx-padding: 8px;
+}
+.chart-plot-background {
+    -fx-background-color: transparent;
+}
+.chart-pie {
+    -fx-border-color: transparent;
+    -fx-border-width: 0;
+}
+.chart-bar {
+    -fx-bar-fill: -fx-primary;
+}
+.default-color0.chart-bar { -fx-bar-fill: -fx-primary; }
+.default-color1.chart-bar { -fx-bar-fill: #10B981; }
+.default-color2.chart-bar { -fx-bar-fill: #F59E0B; }
+.default-color3.chart-bar { -fx-bar-fill: #8B5CF6; }
+.default-color4.chart-bar { -fx-bar-fill: #EC4899; }
+.default-color0.chart-pie { -fx-pie-color: -fx-primary; }
+.default-color0.chart-series-line { -fx-stroke: -fx-primary; }
+.default-color1.chart-series-line { -fx-stroke: #10B981; }
+.default-color2.chart-series-line { -fx-stroke: #F59E0B; }
+.default-color0.chart-line-symbol { -fx-background-color: -fx-primary, white; }
+.default-color1.chart-line-symbol { -fx-background-color: #10B981, white; }
+.default-color2.chart-line-symbol { -fx-background-color: #F59E0B, white; }
+.default-color1.chart-pie { -fx-pie-color: #10B981; }
+.default-color2.chart-pie { -fx-pie-color: #F59E0B; }
+.default-color3.chart-pie { -fx-pie-color: #8B5CF6; }
+.default-color4.chart-pie { -fx-pie-color: #EC4899; }
+.chart-legend {
+    -fx-background-color: transparent;
+    -fx-padding: 4px;
+}
+.axis {
+    -fx-tick-label-fill: -fx-text-secondary;
+    -fx-tick-label-font-size: 11px;
+}
+.axis-label {
+    -fx-text-fill: -fx-text-secondary;
+    -fx-font-size: 12px;
+}
+.chart-title {
+    -fx-font-size: 13px;
+    -fx-text-fill: -fx-text-secondary;
+}

+ 46 - 23
client/src/main/resources/fxml/stats.fxml

@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
 <?import javafx.geometry.*?>
+<?import javafx.scene.chart.*?>
 <?import javafx.scene.control.*?>
 <?import javafx.scene.layout.*?>
 
@@ -23,35 +24,57 @@
         </HBox>
         <VBox styleClass="well" spacing="12">
             <Label text="🔥 热门院系 TOP 3" style="-fx-font-size: 16px; -fx-font-weight: bold;"/>
-            <TableView fx:id="deptTable" prefHeight="140">
-                <columns>
-                    <TableColumn fx:id="deptRankCol" text="排名" prefWidth="60"/>
-                    <TableColumn fx:id="deptNameCol" text="院系" prefWidth="200"/>
-                    <TableColumn fx:id="deptSalesCol" text="销售额" prefWidth="150"/>
-                </columns>
-            </TableView>
+            <SplitPane dividerPositions="0.65" prefHeight="220">
+                <TableView fx:id="deptTable" minWidth="200">
+                    <columns>
+                        <TableColumn fx:id="deptRankCol" text="排名" prefWidth="60"/>
+                        <TableColumn fx:id="deptNameCol" text="院系" prefWidth="200"/>
+                        <TableColumn fx:id="deptSalesCol" text="销售额" prefWidth="150"/>
+                    </columns>
+                </TableView>
+                <PieChart fx:id="deptPieChart" minWidth="200" labelsVisible="true" legendVisible="false"/>
+            </SplitPane>
         </VBox>
         <VBox styleClass="well" spacing="12">
             <Label text="📚 畅销书籍 TOP 5" style="-fx-font-size: 16px; -fx-font-weight: bold;"/>
-            <TableView fx:id="hotBookTable" prefHeight="160">
-                <columns>
-                    <TableColumn fx:id="hotBookRankCol" text="排名" prefWidth="60"/>
-                    <TableColumn fx:id="hotBookIdCol" text="编号" prefWidth="50"/>
-                    <TableColumn fx:id="hotBookTitleCol" text="书名" prefWidth="200"/>
-                    <TableColumn fx:id="hotBookAuthorCol" text="作者" prefWidth="120"/>
-                    <TableColumn fx:id="hotBookSoldCol" text="销量" prefWidth="80"/>
-                </columns>
-            </TableView>
+            <SplitPane dividerPositions="0.65" prefHeight="220">
+                <TableView fx:id="hotBookTable" minWidth="250">
+                    <columns>
+                        <TableColumn fx:id="hotBookRankCol" text="排名" prefWidth="60"/>
+                        <TableColumn fx:id="hotBookIdCol" text="编号" prefWidth="50"/>
+                        <TableColumn fx:id="hotBookTitleCol" text="书名" prefWidth="200"/>
+                        <TableColumn fx:id="hotBookAuthorCol" text="作者" prefWidth="120"/>
+                        <TableColumn fx:id="hotBookSoldCol" text="销量" prefWidth="80"/>
+                    </columns>
+                </TableView>
+                <BarChart fx:id="hotBookChart" minWidth="200" animated="false" legendVisible="false">
+                    <xAxis>
+                        <CategoryAxis side="BOTTOM" tickLabelRotation="-30"/>
+                    </xAxis>
+                    <yAxis>
+                        <NumberAxis side="LEFT" label="销量"/>
+                    </yAxis>
+                </BarChart>
+            </SplitPane>
         </VBox>
         <VBox styleClass="well" spacing="12">
             <Label text="📊 各院系平均折扣率" style="-fx-font-size: 16px; -fx-font-weight: bold;"/>
-            <TableView fx:id="discountTable" prefHeight="160">
-                <columns>
-                    <TableColumn fx:id="discountDeptCol" text="院系" prefWidth="200"/>
-                    <TableColumn fx:id="discountRateCol" text="平均折扣率" prefWidth="150"/>
-                    <TableColumn fx:id="discountBarCol" text="趋势" prefWidth="200"/>
-                </columns>
-            </TableView>
+            <SplitPane dividerPositions="0.65" prefHeight="220">
+                <TableView fx:id="discountTable" minWidth="250">
+                    <columns>
+                        <TableColumn fx:id="discountDeptCol" text="院系" prefWidth="200"/>
+                        <TableColumn fx:id="discountRateCol" text="平均折扣率" prefWidth="150"/>
+                    </columns>
+                </TableView>
+                <LineChart fx:id="discountChart" minWidth="200" animated="false" legendVisible="false" createSymbols="true">
+                    <xAxis>
+                        <CategoryAxis side="BOTTOM"/>
+                    </xAxis>
+                    <yAxis>
+                        <NumberAxis side="LEFT" label="折扣率(%)"/>
+                    </yAxis>
+                </LineChart>
+            </SplitPane>
         </VBox>
     </VBox>
 </ScrollPane>