682 lines
26 KiB
Java
682 lines
26 KiB
Java
import javax.swing.*;
|
||
import javax.swing.border.EmptyBorder;
|
||
import javax.swing.border.LineBorder;
|
||
import javax.swing.border.TitledBorder;
|
||
import javax.swing.table.DefaultTableCellRenderer;
|
||
import javax.swing.table.DefaultTableModel;
|
||
import java.awt.*;
|
||
import java.awt.event.ActionEvent;
|
||
import java.awt.event.MouseAdapter;
|
||
import java.awt.event.MouseEvent;
|
||
import java.io.*;
|
||
import java.nio.charset.StandardCharsets;
|
||
import java.util.ArrayList;
|
||
import java.util.Collections;
|
||
import java.util.List;
|
||
|
||
public class LotteryProgram extends JFrame {
|
||
|
||
// Define tech-style colors
|
||
private static final Color COLOR_BG = new Color(30, 33, 40); // Dark gray background
|
||
private static final Color COLOR_PANEL = new Color(43, 47, 57); // Panel background
|
||
private static final Color COLOR_ACCENT = new Color(0, 180, 216); // Tech blue accent
|
||
private static final Color COLOR_TEXT = new Color(220, 223, 228); // Bright white text
|
||
private static final Color COLOR_TABLE_BG = new Color(43, 47, 57);
|
||
private static final Color COLOR_TABLE_SEL = new Color(0, 120, 215);
|
||
private static final Color COLOR_TABLE_GRID = new Color(60, 64, 72);
|
||
|
||
// Data storage
|
||
private List<String> allParticipants = new ArrayList<>();
|
||
private List<String> remainingParticipants = new ArrayList<>();
|
||
private List<Prize> prizes = new ArrayList<>();
|
||
|
||
// UI components
|
||
private JTextArea nameInputArea;
|
||
private JTable prizeTable;
|
||
private DefaultTableModel prizeTableModel;
|
||
private JComboBox<Prize> prizeComboBox;
|
||
private JLabel lblRemainingCount;
|
||
private JLabel lblRollingName;
|
||
private JTextArea resultArea;
|
||
private JSpinner spinnerDrawCount;
|
||
|
||
// Data file path (Cross-platform compatible)
|
||
private static final String DATA_FILE = System.getProperty("user.home") + File.separator + ".lottery_data.dat";
|
||
|
||
// Cross-platform font support
|
||
private static Font getCompatibleFont(int style, int size) {
|
||
if (System.getProperty("os.name").toLowerCase().contains("mac")) {
|
||
return new Font("PingFang SC", style, size);
|
||
} else if (System.getProperty("os.name").toLowerCase().contains("win")) {
|
||
return new Font("Microsoft YaHei", style, size);
|
||
} else {
|
||
return new Font("SansSerif", style, size);
|
||
}
|
||
}
|
||
|
||
public LotteryProgram() {
|
||
// Setup macOS specific properties if applicable
|
||
setupMacOSProperties();
|
||
|
||
setTitle("xxxxxxx公司年终大抽奖系统");
|
||
setSize(1200, 800);
|
||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||
setLocationRelativeTo(null);
|
||
setLayout(new BorderLayout(10, 10));
|
||
getContentPane().setBackground(COLOR_BG);
|
||
|
||
// Initialize data
|
||
loadData();
|
||
if (prizes.isEmpty()) {
|
||
prizes.add(new Prize("特等奖", 1));
|
||
prizes.add(new Prize("一等奖", 3));
|
||
prizes.add(new Prize("二等奖", 10));
|
||
prizes.add(new Prize("三等奖", 20));
|
||
}
|
||
|
||
// Initialize UI
|
||
initComponents();
|
||
|
||
// Update UI state
|
||
updatePrizeTable();
|
||
updatePrizeComboBox();
|
||
updateRemainingCount();
|
||
}
|
||
|
||
private void setupMacOSProperties() {
|
||
if (System.getProperty("os.name").toLowerCase().contains("mac")) {
|
||
System.setProperty("apple.laf.useScreenMenuBar", "true");
|
||
System.setProperty("com.apple.mrj.application.apple.menu.about.name", "Lottery Program");
|
||
}
|
||
}
|
||
|
||
private void initComponents() {
|
||
// === Top: Rolling display area ===
|
||
JPanel topPanel = createPanel(new BorderLayout());
|
||
topPanel.setBorder(new EmptyBorder(20, 20, 20, 20));
|
||
|
||
lblRollingName = new JLabel("准备就绪", SwingConstants.CENTER);
|
||
lblRollingName.setFont(getCompatibleFont(Font.BOLD, 60));
|
||
lblRollingName.setForeground(COLOR_ACCENT);
|
||
lblRollingName.setBorder(BorderFactory.createEmptyBorder(20, 0, 20, 0));
|
||
|
||
topPanel.add(lblRollingName, BorderLayout.CENTER);
|
||
add(topPanel, BorderLayout.NORTH);
|
||
|
||
// === Middle: Left and right split ===
|
||
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
|
||
splitPane.setDividerLocation(450);
|
||
splitPane.setBackground(COLOR_BG);
|
||
splitPane.setDividerSize(5);
|
||
|
||
// --- Left: Settings area ---
|
||
JPanel leftPanel = createPanel(new BorderLayout());
|
||
leftPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
|
||
|
||
// 1. Name management
|
||
JPanel namePanel = new JPanel(new BorderLayout());
|
||
namePanel.setBackground(COLOR_PANEL);
|
||
namePanel.setBorder(createTitledBorder("名单管理 (批量添加,一行一个)"));
|
||
|
||
nameInputArea = new JTextArea();
|
||
nameInputArea.setBackground(new Color(30, 33, 40));
|
||
nameInputArea.setForeground(COLOR_TEXT);
|
||
nameInputArea.setFont(getCompatibleFont(Font.PLAIN, 14));
|
||
nameInputArea.setCaretColor(COLOR_TEXT);
|
||
JScrollPane nameScroll = new JScrollPane(nameInputArea);
|
||
nameScroll.setBorder(new LineBorder(COLOR_TABLE_GRID));
|
||
namePanel.add(nameScroll, BorderLayout.CENTER);
|
||
|
||
JButton btnUpdateNames = createTechButton("更新/重置名单");
|
||
btnUpdateNames.addActionListener(e -> updateNames());
|
||
JPanel nameBtnPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
|
||
nameBtnPanel.setBackground(COLOR_PANEL);
|
||
nameBtnPanel.add(btnUpdateNames);
|
||
namePanel.add(nameBtnPanel, BorderLayout.SOUTH);
|
||
|
||
leftPanel.add(namePanel, BorderLayout.CENTER);
|
||
|
||
// 2. Prize management
|
||
JPanel prizeManagePanel = new JPanel(new BorderLayout());
|
||
prizeManagePanel.setBackground(COLOR_PANEL);
|
||
prizeManagePanel.setBorder(createTitledBorder("奖项设置 (右键点击可修改)"));
|
||
|
||
prizeTableModel = new DefaultTableModel(new Object[]{"奖项名称", "总人数", "剩余"}, 0) {
|
||
@Override
|
||
public boolean isCellEditable(int row, int column) { return false; }
|
||
@Override
|
||
public Class<?> getColumnClass(int columnIndex) {
|
||
return columnIndex == 0 ? String.class : Integer.class;
|
||
}
|
||
};
|
||
prizeTable = new JTable(prizeTableModel);
|
||
styleTable(prizeTable);
|
||
|
||
// Table right-click menu
|
||
prizeTable.addMouseListener(new MouseAdapter() {
|
||
@Override
|
||
public void mousePressed(MouseEvent e) {
|
||
if (SwingUtilities.isRightMouseButton(e)) {
|
||
int row = prizeTable.rowAtPoint(e.getPoint());
|
||
if (row >= 0) {
|
||
prizeTable.setRowSelectionInterval(row, row);
|
||
showTablePopupMenu(e.getComponent(), e.getX(), e.getY(), row);
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
prizeManagePanel.add(new JScrollPane(prizeTable), BorderLayout.CENTER);
|
||
|
||
JPanel prizeBtnPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
||
prizeBtnPanel.setBackground(COLOR_PANEL);
|
||
JButton btnAddPrize = createTechButton("添加奖项");
|
||
btnAddPrize.addActionListener(e -> addPrize());
|
||
JButton btnDelPrize = createTechButton("删除选中");
|
||
btnDelPrize.addActionListener(e -> deletePrize());
|
||
JButton btnResetPrizes = createTechButton("重置奖项数量");
|
||
btnResetPrizes.addActionListener(e -> resetPrizes());
|
||
|
||
prizeBtnPanel.add(btnAddPrize);
|
||
prizeBtnPanel.add(btnDelPrize);
|
||
prizeBtnPanel.add(btnResetPrizes);
|
||
prizeManagePanel.add(prizeBtnPanel, BorderLayout.SOUTH);
|
||
|
||
leftPanel.add(prizeManagePanel, BorderLayout.SOUTH);
|
||
|
||
// --- Right: Control and results ---
|
||
JPanel rightPanel = createPanel(new BorderLayout());
|
||
rightPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
|
||
|
||
// Control area
|
||
JPanel controlPanel = createPanel(new GridBagLayout());
|
||
controlPanel.setBorder(createTitledBorder("抽奖控制"));
|
||
GridBagConstraints gbc = new GridBagConstraints();
|
||
gbc.insets = new Insets(10, 10, 10, 10);
|
||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||
|
||
// Style labels
|
||
Font labelFont = getCompatibleFont(Font.PLAIN, 16);
|
||
|
||
gbc.gridx = 0; gbc.gridy = 0;
|
||
JLabel lblSelect = new JLabel("选择奖项:");
|
||
lblSelect.setForeground(COLOR_TEXT);
|
||
lblSelect.setFont(labelFont);
|
||
controlPanel.add(lblSelect, gbc);
|
||
|
||
gbc.gridx = 1; gbc.weightx = 1.0;
|
||
styleComboBox(prizeComboBox = new JComboBox<>());
|
||
controlPanel.add(prizeComboBox, gbc);
|
||
|
||
gbc.gridx = 0; gbc.gridy = 1; gbc.weightx = 0;
|
||
JLabel lblPool = new JLabel("奖池剩余:");
|
||
lblPool.setForeground(COLOR_TEXT);
|
||
lblPool.setFont(labelFont);
|
||
controlPanel.add(lblPool, gbc);
|
||
|
||
gbc.gridx = 1; gbc.weightx = 1.0;
|
||
lblRemainingCount = new JLabel("0 人");
|
||
lblRemainingCount.setForeground(COLOR_ACCENT);
|
||
lblRemainingCount.setFont(getCompatibleFont(Font.BOLD, 16));
|
||
controlPanel.add(lblRemainingCount, gbc);
|
||
|
||
gbc.gridx = 0; gbc.gridy = 2; gbc.weightx = 0;
|
||
JLabel lblDraw = new JLabel("本次抽取人数:");
|
||
lblDraw.setForeground(COLOR_TEXT);
|
||
lblDraw.setFont(labelFont);
|
||
controlPanel.add(lblDraw, gbc);
|
||
|
||
gbc.gridx = 1; gbc.weightx = 1.0;
|
||
spinnerDrawCount = new JSpinner(new SpinnerNumberModel(1, 1, 100, 1));
|
||
styleSpinner(spinnerDrawCount);
|
||
controlPanel.add(spinnerDrawCount, gbc);
|
||
|
||
// Round button
|
||
gbc.gridx = 0; gbc.gridy = 3; gbc.gridwidth = 2;
|
||
gbc.weightx = 0;
|
||
gbc.fill = GridBagConstraints.NONE;
|
||
gbc.anchor = GridBagConstraints.CENTER;
|
||
|
||
RoundButton btnStart = new RoundButton("开始 / 停止");
|
||
btnStart.setBackground(new Color(220, 53, 69)); // Bright red
|
||
btnStart.setForeground(Color.WHITE);
|
||
btnStart.setFont(getCompatibleFont(Font.BOLD, 18));
|
||
btnStart.setPreferredSize(new Dimension(150, 150));
|
||
btnStart.addActionListener(e -> toggleLottery());
|
||
controlPanel.add(btnStart, gbc);
|
||
|
||
rightPanel.add(controlPanel, BorderLayout.NORTH);
|
||
|
||
// Results area
|
||
JPanel resultPanel = new JPanel(new BorderLayout());
|
||
resultPanel.setBackground(COLOR_PANEL);
|
||
resultPanel.setBorder(createTitledBorder("中奖名单"));
|
||
|
||
resultArea = new JTextArea();
|
||
resultArea.setEditable(false);
|
||
resultArea.setBackground(new Color(30, 33, 40));
|
||
resultArea.setForeground(COLOR_TEXT);
|
||
// Monospaced font for results looks better on all platforms
|
||
resultArea.setFont(new Font("Monospaced", Font.PLAIN, 14));
|
||
resultPanel.add(new JScrollPane(resultArea), BorderLayout.CENTER);
|
||
|
||
JButton btnClearResult = createTechButton("清空结果");
|
||
btnClearResult.addActionListener(e -> resultArea.setText(""));
|
||
JPanel resBtnPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
|
||
resBtnPanel.setBackground(COLOR_PANEL);
|
||
resBtnPanel.add(btnClearResult);
|
||
resultPanel.add(resBtnPanel, BorderLayout.SOUTH);
|
||
|
||
rightPanel.add(resultPanel, BorderLayout.CENTER);
|
||
|
||
splitPane.setLeftComponent(leftPanel);
|
||
splitPane.setRightComponent(rightPanel);
|
||
add(splitPane, BorderLayout.CENTER);
|
||
}
|
||
|
||
// === UI helper methods: Tech-style ===
|
||
|
||
private JPanel createPanel(LayoutManager layout) {
|
||
JPanel p = new JPanel(layout);
|
||
p.setBackground(COLOR_PANEL);
|
||
p.setBorder(new EmptyBorder(5, 5, 5, 5));
|
||
return p;
|
||
}
|
||
|
||
private TitledBorder createTitledBorder(String title) {
|
||
TitledBorder border = BorderFactory.createTitledBorder(
|
||
BorderFactory.createLineBorder(COLOR_ACCENT, 1),
|
||
title,
|
||
TitledBorder.LEFT,
|
||
TitledBorder.TOP,
|
||
getCompatibleFont(Font.BOLD, 14),
|
||
COLOR_ACCENT);
|
||
border.setTitleColor(COLOR_ACCENT);
|
||
return border;
|
||
}
|
||
|
||
private JButton createTechButton(String text) {
|
||
JButton btn = new JButton(text);
|
||
btn.setBackground(new Color(0, 123, 255));
|
||
btn.setForeground(Color.WHITE);
|
||
btn.setFocusPainted(false);
|
||
btn.setFont(getCompatibleFont(Font.PLAIN, 14));
|
||
btn.setBorder(new EmptyBorder(8, 15, 8, 15));
|
||
btn.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
||
btn.addMouseListener(new MouseAdapter() {
|
||
@Override
|
||
public void mouseEntered(MouseEvent e) { btn.setBackground(btn.getBackground().brighter()); }
|
||
@Override
|
||
public void mouseExited(MouseEvent e) { btn.setBackground(new Color(0, 123, 255)); }
|
||
});
|
||
return btn;
|
||
}
|
||
|
||
private void styleTable(JTable table) {
|
||
table.setBackground(COLOR_TABLE_BG);
|
||
table.setForeground(COLOR_TEXT);
|
||
table.setGridColor(COLOR_TABLE_GRID);
|
||
table.setRowHeight(30);
|
||
table.setSelectionBackground(COLOR_TABLE_SEL);
|
||
table.setSelectionForeground(Color.WHITE);
|
||
table.setFont(getCompatibleFont(Font.PLAIN, 14));
|
||
table.getTableHeader().setBackground(new Color(52, 58, 64));
|
||
table.getTableHeader().setForeground(COLOR_TEXT);
|
||
table.getTableHeader().setFont(getCompatibleFont(Font.BOLD, 14));
|
||
|
||
// Custom cell renderer to maintain background color
|
||
DefaultTableCellRenderer renderer = new DefaultTableCellRenderer() {
|
||
@Override
|
||
public Component getTableCellRendererComponent(JTable table, Object value,
|
||
boolean isSelected, boolean hasFocus, int row, int column) {
|
||
Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||
if (!isSelected) {
|
||
c.setBackground(COLOR_TABLE_BG);
|
||
}
|
||
return c;
|
||
}
|
||
};
|
||
table.setDefaultRenderer(Object.class, renderer);
|
||
}
|
||
|
||
private void styleComboBox(JComboBox<?> combo) {
|
||
combo.setBackground(new Color(52, 58, 64));
|
||
combo.setForeground(COLOR_TEXT);
|
||
combo.setFont(getCompatibleFont(Font.PLAIN, 14));
|
||
}
|
||
|
||
private void styleSpinner(JSpinner spinner) {
|
||
JComponent editor = spinner.getEditor();
|
||
if (editor instanceof JSpinner.DefaultEditor) {
|
||
((JSpinner.DefaultEditor) editor).getTextField().setBackground(new Color(52, 58, 64));
|
||
((JSpinner.DefaultEditor) editor).getTextField().setForeground(COLOR_TEXT);
|
||
((JSpinner.DefaultEditor) editor).getTextField().setFont(getCompatibleFont(Font.PLAIN, 14));
|
||
}
|
||
}
|
||
|
||
// === Logic processing methods ===
|
||
|
||
private void showTablePopupMenu(Component invoker, int x, int y, int row) {
|
||
JPopupMenu popup = new JPopupMenu();
|
||
popup.setBackground(COLOR_PANEL);
|
||
popup.setForeground(COLOR_TEXT);
|
||
|
||
JMenuItem editTotalItem = new JMenuItem("修改总人数");
|
||
JMenuItem editRemainItem = new JMenuItem("修改剩余人数");
|
||
|
||
// Unified style
|
||
for(JMenuItem item : new JMenuItem[]{editTotalItem, editRemainItem}) {
|
||
item.setBackground(COLOR_PANEL);
|
||
item.setForeground(COLOR_TEXT);
|
||
}
|
||
|
||
editTotalItem.addActionListener(e -> {
|
||
Prize p = prizes.get(row);
|
||
String newVal = JOptionPane.showInputDialog(this, "修改 [" + p.getName() + "] 的总人数:", p.getTotalCount());
|
||
processEdit(p, newVal, true);
|
||
});
|
||
|
||
editRemainItem.addActionListener(e -> {
|
||
Prize p = prizes.get(row);
|
||
String newVal = JOptionPane.showInputDialog(this, "修改 [" + p.getName() + "] 的剩余人数:", p.getRemaining());
|
||
processEdit(p, newVal, false);
|
||
});
|
||
|
||
popup.add(editTotalItem);
|
||
popup.add(editRemainItem);
|
||
popup.show(invoker, x, y);
|
||
}
|
||
|
||
private void processEdit(Prize p, String newVal, boolean isTotal) {
|
||
try {
|
||
if (newVal != null) {
|
||
int count = Integer.parseInt(newVal);
|
||
if (count < 0) throw new NumberFormatException();
|
||
|
||
if (isTotal) {
|
||
if (count < (p.getTotalCount() - p.getRemaining())) {
|
||
JOptionPane.showMessageDialog(this, "新人数不能少于已中奖人数 (" + (p.getTotalCount() - p.getRemaining()) + ")!");
|
||
return;
|
||
}
|
||
// Keep awarded count unchanged
|
||
int awarded = p.getTotalCount() - p.getRemaining();
|
||
p.setTotalCount(count);
|
||
p.setRemaining(count - awarded);
|
||
} else {
|
||
if (count > p.getTotalCount()) {
|
||
JOptionPane.showMessageDialog(this, "剩余人数不能大于总人数!");
|
||
return;
|
||
}
|
||
p.setRemaining(count);
|
||
}
|
||
|
||
updatePrizeTable();
|
||
updatePrizeComboBox();
|
||
saveData();
|
||
}
|
||
} catch (NumberFormatException ex) {
|
||
JOptionPane.showMessageDialog(this, "请输入有效的数字!");
|
||
}
|
||
}
|
||
|
||
private void updateNames() {
|
||
String text = nameInputArea.getText();
|
||
String[] lines = text.split("\n");
|
||
allParticipants.clear();
|
||
for (String line : lines) {
|
||
String name = line.trim();
|
||
if (!name.isEmpty()) {
|
||
allParticipants.add(name);
|
||
}
|
||
}
|
||
remainingParticipants.clear();
|
||
remainingParticipants.addAll(allParticipants);
|
||
updateRemainingCount();
|
||
JOptionPane.showMessageDialog(this, "名单已更新!共 " + allParticipants.size() + " 人。");
|
||
saveData();
|
||
}
|
||
|
||
private void addPrize() {
|
||
String name = JOptionPane.showInputDialog(this, "请输入奖项名称:");
|
||
if (name == null || name.trim().isEmpty()) return;
|
||
String countStr = JOptionPane.showInputDialog(this, "请输入中奖人数:");
|
||
try {
|
||
int count = Integer.parseInt(countStr);
|
||
if (count <= 0) throw new NumberFormatException();
|
||
prizes.add(new Prize(name, count));
|
||
updatePrizeTable();
|
||
updatePrizeComboBox();
|
||
saveData();
|
||
} catch (NumberFormatException e) {
|
||
JOptionPane.showMessageDialog(this, "请输入有效的正整数人数!");
|
||
}
|
||
}
|
||
|
||
private void deletePrize() {
|
||
int row = prizeTable.getSelectedRow();
|
||
if (row >= 0) {
|
||
prizes.remove(row);
|
||
updatePrizeTable();
|
||
updatePrizeComboBox();
|
||
saveData();
|
||
} else {
|
||
JOptionPane.showMessageDialog(this, "请先选择要删除的奖项!");
|
||
}
|
||
}
|
||
|
||
private void resetPrizes() {
|
||
int confirm = JOptionPane.showConfirmDialog(this, "确定要重置所有奖项的剩余人数吗?", "确认", JOptionPane.YES_NO_OPTION);
|
||
if (confirm == JOptionPane.YES_OPTION) {
|
||
for (Prize p : prizes) p.resetRemaining();
|
||
updatePrizeTable();
|
||
saveData();
|
||
}
|
||
}
|
||
|
||
private void updatePrizeTable() {
|
||
prizeTableModel.setRowCount(0);
|
||
for (Prize p : prizes) {
|
||
prizeTableModel.addRow(new Object[]{p.getName(), p.getTotalCount(), p.getRemaining()});
|
||
}
|
||
}
|
||
|
||
private void updatePrizeComboBox() {
|
||
Object selected = prizeComboBox.getSelectedItem();
|
||
prizeComboBox.removeAllItems();
|
||
for (Prize p : prizes) prizeComboBox.addItem(p);
|
||
if (selected != null) prizeComboBox.setSelectedItem(selected);
|
||
}
|
||
|
||
private void updateRemainingCount() {
|
||
lblRemainingCount.setText(remainingParticipants.size() + " 人");
|
||
}
|
||
|
||
private Timer timer;
|
||
private boolean isRunning = false;
|
||
|
||
private void toggleLottery() {
|
||
Prize selectedPrize = (Prize) prizeComboBox.getSelectedItem();
|
||
if (selectedPrize == null) {
|
||
JOptionPane.showMessageDialog(this, "请先添加并选择一个奖项!");
|
||
return;
|
||
}
|
||
if (selectedPrize.getRemaining() <= 0) {
|
||
JOptionPane.showMessageDialog(this, "该奖项已抽完!如需重新抽取,请重置奖项。");
|
||
return;
|
||
}
|
||
if (remainingParticipants.isEmpty()) {
|
||
JOptionPane.showMessageDialog(this, "奖池已空,请添加更多名单!");
|
||
return;
|
||
}
|
||
if (isRunning) stopLottery(selectedPrize);
|
||
else startLottery();
|
||
}
|
||
|
||
private void startLottery() {
|
||
isRunning = true;
|
||
timer = new Timer(30, e -> { // Speed up rolling
|
||
if (!remainingParticipants.isEmpty()) {
|
||
int randomIndex = (int) (Math.random() * remainingParticipants.size());
|
||
lblRollingName.setText(remainingParticipants.get(randomIndex));
|
||
}
|
||
});
|
||
timer.start();
|
||
}
|
||
|
||
private void stopLottery(Prize prize) {
|
||
isRunning = false;
|
||
timer.stop();
|
||
|
||
int drawCount = (int) spinnerDrawCount.getValue();
|
||
if (drawCount > prize.getRemaining()) {
|
||
drawCount = prize.getRemaining();
|
||
JOptionPane.showMessageDialog(this, "剩余名额不足,将抽取剩余的 " + drawCount + " 人。");
|
||
}
|
||
if (drawCount > remainingParticipants.size()) {
|
||
drawCount = remainingParticipants.size();
|
||
JOptionPane.showMessageDialog(this, "奖池总人数不足,将抽取剩余的 " + drawCount + " 人。");
|
||
}
|
||
|
||
List<String> currentWinners = new ArrayList<>();
|
||
for (int i = 0; i < drawCount; i++) {
|
||
Collections.shuffle(remainingParticipants);
|
||
String winner = remainingParticipants.remove(0);
|
||
currentWinners.add(winner);
|
||
prize.decrementRemaining();
|
||
}
|
||
|
||
if (currentWinners.size() == 1) {
|
||
lblRollingName.setText(currentWinners.get(0));
|
||
} else {
|
||
lblRollingName.setText("恭喜 " + currentWinners.size() + " 位中奖者!");
|
||
}
|
||
|
||
updatePrizeTable();
|
||
updateRemainingCount();
|
||
|
||
String time = java.time.LocalDateTime.now().toString().substring(0, 19);
|
||
String winnersStr = String.join(", ", currentWinners);
|
||
resultArea.append(String.format("[%s] %s: %s\n", time, prize.getName(), winnersStr));
|
||
saveData();
|
||
}
|
||
|
||
private void saveData() {
|
||
try (ObjectOutputStream oos = new ObjectOutputStream(
|
||
new FileOutputStream(DATA_FILE))) {
|
||
oos.writeObject(allParticipants);
|
||
oos.writeObject(prizes);
|
||
} catch (IOException e) {
|
||
e.printStackTrace();
|
||
JOptionPane.showMessageDialog(this, "数据保存失败: " + e.getMessage());
|
||
}
|
||
}
|
||
|
||
@SuppressWarnings("unchecked")
|
||
private void loadData() {
|
||
File file = new File(DATA_FILE);
|
||
if (file.exists()) {
|
||
try (ObjectInputStream ois = new ObjectInputStream(
|
||
new FileInputStream(DATA_FILE))) {
|
||
allParticipants = (List<String>) ois.readObject();
|
||
prizes = (List<Prize>) ois.readObject();
|
||
remainingParticipants = new ArrayList<>(allParticipants);
|
||
} catch (IOException | ClassNotFoundException e) {
|
||
e.printStackTrace();
|
||
JOptionPane.showMessageDialog(this, "数据加载失败,将使用默认设置。");
|
||
// Reset to defaults if load fails
|
||
allParticipants.clear();
|
||
remainingParticipants.clear();
|
||
prizes.clear();
|
||
}
|
||
}
|
||
}
|
||
|
||
static class Prize implements Serializable {
|
||
private String name;
|
||
private int totalCount;
|
||
private int remainingCount;
|
||
|
||
public Prize(String name, int totalCount) {
|
||
this.name = name;
|
||
this.totalCount = totalCount;
|
||
this.remainingCount = totalCount;
|
||
}
|
||
|
||
public String getName() { return name; }
|
||
public int getTotalCount() { return totalCount; }
|
||
public int getRemaining() { return remainingCount; }
|
||
|
||
public void setTotalCount(int totalCount) { this.totalCount = totalCount; }
|
||
public void setRemaining(int remainingCount) { this.remainingCount = remainingCount; }
|
||
|
||
public void decrementRemaining() { if (remainingCount > 0) remainingCount--; }
|
||
public void resetRemaining() { remainingCount = totalCount; }
|
||
|
||
@Override
|
||
public String toString() { return name + " (剩: " + remainingCount + ")"; }
|
||
}
|
||
|
||
class RoundButton extends JButton {
|
||
public RoundButton(String label) {
|
||
super(label);
|
||
setContentAreaFilled(false);
|
||
setFocusPainted(false);
|
||
setBorderPainted(false);
|
||
setCursor(new Cursor(Cursor.HAND_CURSOR));
|
||
}
|
||
|
||
@Override
|
||
protected void paintComponent(Graphics g) {
|
||
Graphics2D g2 = (Graphics2D) g;
|
||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||
if (getModel().isArmed()) {
|
||
g2.setColor(getBackground().darker());
|
||
} else {
|
||
g2.setColor(getBackground());
|
||
}
|
||
g2.fillOval(0, 0, getSize().width - 1, getSize().height - 1);
|
||
super.paintComponent(g);
|
||
}
|
||
|
||
@Override
|
||
protected void paintBorder(Graphics g) { }
|
||
|
||
@Override
|
||
public boolean contains(int x, int y) {
|
||
if (shape == null || !shape.getBounds().equals(getBounds())) {
|
||
shape = new java.awt.geom.Ellipse2D.Float(0, 0, getWidth(), getHeight());
|
||
}
|
||
return shape.contains(x, y);
|
||
}
|
||
|
||
private transient java.awt.Shape shape;
|
||
}
|
||
|
||
public static void main(String[] args) {
|
||
try {
|
||
// Force UTF-8 property early (best effort)
|
||
System.setProperty("file.encoding", "UTF-8");
|
||
|
||
// Set CrossPlatform LookAndFeel for consistency
|
||
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
|
||
|
||
// Optional: Set global font defaults for the LookAndFeel
|
||
Font defaultFont = getCompatibleFont(Font.PLAIN, 12);
|
||
UIManager.put("Button.font", defaultFont);
|
||
UIManager.put("Label.font", defaultFont);
|
||
UIManager.put("Table.font", defaultFont);
|
||
UIManager.put("TableHeader.font", defaultFont);
|
||
UIManager.put("TextField.font", defaultFont);
|
||
UIManager.put("TextArea.font", defaultFont);
|
||
UIManager.put("ComboBox.font", defaultFont);
|
||
|
||
} catch (Exception e) {
|
||
e.printStackTrace();
|
||
}
|
||
SwingUtilities.invokeLater(() -> {
|
||
LotteryProgram frame = new LotteryProgram();
|
||
frame.setVisible(true);
|
||
});
|
||
}
|
||
}
|