1. 搭建项目UI页面
1.1 项目UI说明
前言

常用组件:👇
- JFrame:窗口,是所有窗口的父类,它是所有窗口的基类。
- JPanel:面板,是所有面板的父类,它是所有面板的基类。
- JLabel:标签,用于显示文本或图像。
- JButton:按钮,用于触发事件。
- JTextField:文本框,用于输入文本。
- JPasswordField:密码框,用于输入密码。
- JComboBox:下拉框,用于选择选项。
- JList:列表框,用于显示列表项。
- JTable:表格,用于显示表格数据。
- JScrollPane:滚动面板,用于包含可滚动的组件。
- JToolBar:工具栏,用于放置工具按钮。
- JMenu:菜单,用于创建菜单栏。
说明:👇
- JFrame是最外层的容器,可以包含其他组件。其他的组件如JPanel、JLabel、JButton等都是JFrame的子组件。
- JPanel是一个容器,可以包含其他组件。JLabel、JButton等组件都是JPanel的子组件。
1.2 搭建主页面
前言
欢迎页面 👇

代码如下:👇
// 欢迎页面
public class WelcomeUI extends JFrame {
public WelcomeUI() {
initialize();
}
private void initialize() {
// 设置窗口标题
setTitle("黑马医药管理系统");
// 设置窗口大小
setSize(500, 400);
// 设置窗口居中显示
setLocationRelativeTo(null);
// 设置窗口关闭时退出程序
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 设置布局
setLayout(new BorderLayout());
// 创建主面板,使用网格布局来垂直排列组件
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));// 使用垂直布局
mainPanel.setBorder(BorderFactory.createEmptyBorder(50, 50, 50, 50)); // 设置边距
// 标题标签
JLabel titleLabel = new JLabel("欢迎使用黑马医药管理系统", JLabel.CENTER);
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 24));
titleLabel.setAlignmentX(Component.CENTER_ALIGNMENT); // 横向居中
titleLabel.setMaximumSize(new Dimension(Integer.MAX_VALUE, titleLabel.getPreferredSize().height)); // 设置最大宽度以保持居中
mainPanel.add(titleLabel);
mainPanel.add(Box.createVerticalStrut(50)); // 添加垂直间距
// 登录按钮
JButton loginButton = new JButton("登录");
loginButton.setFont(new Font("微软雅黑", Font.PLAIN, 16));
loginButton.setMaximumSize(new Dimension(180,40));
loginButton.setAlignmentX(Component.CENTER_ALIGNMENT); // 横向居中
loginButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//todo 切换到登录界面
WelcomeUI.this.setVisible(false);
}
});
mainPanel.add(loginButton);
mainPanel.add(Box.createVerticalStrut(20)); // 添加垂直间距
// 注册按钮
JButton registerButton = new JButton("注册");
registerButton.setFont(new Font("微软雅黑", Font.PLAIN, 16));
registerButton.setMaximumSize(new Dimension(180,40));
registerButton.setAlignmentX(Component.CENTER_ALIGNMENT); // 横向居中
registerButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//todo 切换到注册界面
WelcomeUI.this.setVisible(false);
}
});
mainPanel.add(registerButton);
mainPanel.add(Box.createVerticalStrut(20)); // 添加垂直间距
// 退出按钮
JButton exitButton = new JButton("退出");
exitButton.setFont(new Font("微软雅黑", Font.PLAIN, 16));
exitButton.setMaximumSize(new Dimension(180,40));
exitButton.setAlignmentX(Component.CENTER_ALIGNMENT); // 横向居中
exitButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.exit(0); // 退出程序
}
});
mainPanel.add(exitButton);
// 将主面板添加到窗口
add(mainPanel, BorderLayout.CENTER);
}
}
接下来,要点击登录按钮,切换到登录界面,点击注册按钮,切换到注册界面,点击退出按钮,退出程序。

考虑到2个页面的布局基本一致,所以可以抽取一个公共的页面UserUI页面
登录页面 👇
public class UserUI extends JFrame {
private String mode; // "login" 或 "register"
// 登录界面组件
private JTextField loginUsernameField;
private JPasswordField loginPasswordField;
// 注册界面组件
private JTextField registerUsernameField;
private JPasswordField registerPasswordField;
private JPasswordField registerConfirmPasswordField;
private JTextField nameField; // 新增姓名输入框
private JTextField phoneField;
private JTextField codeField;
private JButton sendCodeButton;
public UserUI(String mode) {
this.mode = mode;
initialize();
}
private void initialize() {
// 设置窗口标题
setTitle(mode.equals("login") ? "用户登录" : "用户注册");
// 设置窗口大小
setSize(500, mode.equals("login") ? 380 : 580);
// 设置窗口居中显示
setLocationRelativeTo(null);
// 设置窗口关闭时仅隐藏窗口,不退出程序
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// 创建主面板
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
mainPanel.setBorder(BorderFactory.createEmptyBorder(50, 50, 50, 50));
// 添加标题
JLabel titleLabel = new JLabel(mode.equals("login") ? "用户登录" : "用户注册", JLabel.CENTER);
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 24));
titleLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
mainPanel.add(titleLabel);
mainPanel.add(Box.createVerticalStrut(40));
if (mode.equals("login")) {
createLoginPanel(mainPanel);
} else {
createRegisterPanel(mainPanel);
}
add(mainPanel);
}
// 创建登录界面
private void createLoginPanel(JPanel mainPanel) {
// 创建一个使用GridBagLayout的面板来对齐标签和输入框
JPanel formPanel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(5, 5, 5, 5); // 设置组件间距
// 用户名行
gbc.gridx = 0;
gbc.gridy = 0;
gbc.anchor = GridBagConstraints.EAST;
JLabel loginUsernameLabel = new JLabel("用户名:");
loginUsernameLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
formPanel.add(loginUsernameLabel, gbc);
gbc.gridx = 1;
gbc.anchor = GridBagConstraints.WEST;
loginUsernameField = new JTextField(20);
loginUsernameField.setPreferredSize(new Dimension(200, 30));
formPanel.add(loginUsernameField, gbc);
// 密码行
gbc.gridx = 0;
gbc.gridy = 1;
gbc.anchor = GridBagConstraints.EAST;
JLabel loginPasswordLabel = new JLabel("密码:");
loginPasswordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
formPanel.add(loginPasswordLabel, gbc);
gbc.gridx = 1;
gbc.anchor = GridBagConstraints.WEST;
loginPasswordField = new JPasswordField(20);
loginPasswordField.setPreferredSize(new Dimension(200, 30));
formPanel.add(loginPasswordField, gbc);
mainPanel.add(formPanel);
mainPanel.add(Box.createVerticalStrut(30));
// 按钮行
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 0));
JButton loginButton = new JButton("登录");
loginButton.setFont(new Font("微软雅黑", Font.PLAIN, 14));
loginButton.setPreferredSize(new Dimension(100, 35));
loginButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
handleLogin();
}
});
buttonPanel.add(loginButton);
JButton loginReturnButton = new JButton("返回");
loginReturnButton.setFont(new Font("微软雅黑", Font.PLAIN, 14));
loginReturnButton.setPreferredSize(new Dimension(100, 35));
loginReturnButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
returnToWelcome();
}
});
buttonPanel.add(loginReturnButton);
mainPanel.add(buttonPanel);
}
// 创建注册界面
private void createRegisterPanel(JPanel mainPanel) {
// 创建一个使用GridBagLayout的面板来对齐标签和输入框
JPanel formPanel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(5, 5, 5, 5); // 设置组件间距
// 用户名行
gbc.gridx = 0;
gbc.gridy = 0;
gbc.anchor = GridBagConstraints.EAST;
JLabel registerUsernameLabel = new JLabel("用户名:");
registerUsernameLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
formPanel.add(registerUsernameLabel, gbc);
gbc.gridx = 1;
gbc.anchor = GridBagConstraints.WEST;
registerUsernameField = new JTextField(20);
registerUsernameField.setPreferredSize(new Dimension(200, 30));
formPanel.add(registerUsernameField, gbc);
// 密码行
gbc.gridx = 0;
gbc.gridy = 1;
gbc.anchor = GridBagConstraints.EAST;
JLabel registerPasswordLabel = new JLabel("密码:");
registerPasswordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
formPanel.add(registerPasswordLabel, gbc);
gbc.gridx = 1;
gbc.anchor = GridBagConstraints.WEST;
registerPasswordField = new JPasswordField(20);
registerPasswordField.setPreferredSize(new Dimension(200, 30));
formPanel.add(registerPasswordField, gbc);
// 确认密码行
gbc.gridx = 0;
gbc.gridy = 2;
gbc.anchor = GridBagConstraints.EAST;
JLabel registerConfirmPasswordLabel = new JLabel("确认密码:");
registerConfirmPasswordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
formPanel.add(registerConfirmPasswordLabel, gbc);
gbc.gridx = 1;
gbc.anchor = GridBagConstraints.WEST;
registerConfirmPasswordField = new JPasswordField(20);
registerConfirmPasswordField.setPreferredSize(new Dimension(200, 30));
formPanel.add(registerConfirmPasswordField, gbc);
// 姓名行 - 新增
gbc.gridx = 0;
gbc.gridy = 3;
gbc.anchor = GridBagConstraints.EAST;
JLabel nameLabel = new JLabel("姓名:");
nameLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
formPanel.add(nameLabel, gbc);
gbc.gridx = 1;
gbc.anchor = GridBagConstraints.WEST;
nameField = new JTextField(20);
nameField.setPreferredSize(new Dimension(200, 30));
formPanel.add(nameField, gbc);
// 手机号行
gbc.gridx = 0;
gbc.gridy = 4; // 原来是3,现在因为新增姓名行变成4
gbc.anchor = GridBagConstraints.EAST;
JLabel phoneLabel = new JLabel("手机号:");
phoneLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
formPanel.add(phoneLabel, gbc);
gbc.gridx = 1;
gbc.anchor = GridBagConstraints.WEST;
JPanel phoneFieldPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
phoneField = new JTextField(12); // 减小宽度以适应验证码按钮
phoneField.setPreferredSize(new Dimension(120, 30));
sendCodeButton = new JButton("发送验证码");
sendCodeButton.setFont(new Font("微软雅黑", Font.PLAIN, 12));
sendCodeButton.setMaximumSize(new Dimension(90, 30));
sendCodeButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
handleSendCode();
}
});
phoneFieldPanel.add(phoneField);
phoneFieldPanel.add(sendCodeButton);
formPanel.add(phoneFieldPanel, gbc);
// 验证码行
gbc.gridx = 0;
gbc.gridy = 5; // 原来是4,现在因为新增姓名行变成5
gbc.anchor = GridBagConstraints.EAST;
JLabel codeLabel = new JLabel("验证码:");
codeLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
formPanel.add(codeLabel, gbc);
gbc.gridx = 1;
gbc.anchor = GridBagConstraints.WEST;
codeField = new JTextField(20);
codeField.setPreferredSize(new Dimension(200, 30));
formPanel.add(codeField, gbc);
mainPanel.add(formPanel);
mainPanel.add(Box.createVerticalStrut(30));
// 按钮行
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 0));
JButton registerButton = new JButton("注册");
registerButton.setFont(new Font("微软雅黑", Font.PLAIN, 14));
registerButton.setPreferredSize(new Dimension(100, 35));
registerButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
handleRegister();
}
});
buttonPanel.add(registerButton);
JButton registerReturnButton = new JButton("返回");
registerReturnButton.setFont(new Font("微软雅黑", Font.PLAIN, 14));
registerReturnButton.setPreferredSize(new Dimension(100, 35));
registerReturnButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
returnToWelcome();
}
});
buttonPanel.add(registerReturnButton);
mainPanel.add(buttonPanel);
}
// 处理登录逻辑
private void handleLogin() {
//todo 处理登录逻辑
System.out.println("登录按钮点击");
// 直接跳转到主页HomeuI
TUser user = new TUser();
user.setUsername(loginUsernameField.getText());
HomeUI homeUI = new HomeUI(user);
homeUI.setVisible(true);
dispose();
}
// 处理注册逻辑
private void handleRegister() {
// todo 处理注册逻辑
System.out.println("注册按钮点击");
}
// 处理发送验证码
private void handleSendCode() {
/// todo 处理发送验证码逻辑
System.out.println("发送验证码按钮点击");
}
// 返回欢迎界面
private void returnToWelcome() {
WelcomeUI welcomeUI = new WelcomeUI();
welcomeUI.setVisible(true);
dispose(); // 关闭当前窗口
}
}
注意: 上述UI代码,初版大家可以复制老师的,后期我们通过AI帮我们生成,然后在进行修改,这样工作量会少很多,当然里面的逻辑我们自己来实现。
HomeUI页面,这里我们搞个空白页面,后续我们再添加内容。
public class HomeUI extends JFrame {
public HomeUI(TUser user) {//通过·构造方法传递用户信息
setTitle("主页");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(800, 600);
setLocationRelativeTo(null);
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BorderLayout());
// 添加一个空白面板
JPanel blankPanel = new JPanel(); // 空白面板
//添加文字
JLabel label = new JLabel(user.getNickname()+" 欢迎来到主页");
label.setFont(new Font("微软雅黑", Font.BOLD, 24));
blankPanel.add(label);
mainPanel.add(blankPanel, BorderLayout.CENTER);
setContentPane(mainPanel);
}
}
启动类 👇
/**
* @Description 项目启动类
*/
public class Application {
public static void main(String[] args) throws Exception {
// 设置 FlatLaf 主题 白天模式
UIManager.setLookAndFeel(new FlatLightLaf());
//UIManager.setLookAndFeel(new FlatDarkLaf());
// 启动欢迎界面
java.awt.EventQueue.invokeLater(() -> new WelcomeUI().setVisible(true));
System.out.println("项目启动...");
//testUserMapper();
}
private static void testUserMapper() {
//1. 创建UserDao
UserDao userDao = new UserDao();
//2. 调用userDao的方法
TUser user = userDao.getUserByUsername("zhangsan");
//3. 输出结果
System.out.println(user);
}
}
完成上述代码后,点击运行,确保按钮可以正常点击,且正常跳转到对应的页面。

上述的代码,大家可以复制,后期我们会通过AI帮我们生成,然后在进行修改,这样工作量会少很多,当然里面的逻辑我们自己来实现。
1.3 完成登录和退出逻辑
前言

从上图可以看到,我们需要实现的功能有:
- 登录逻辑
- 发送验证码逻辑
- 注册逻辑
1.3.1 完成登录逻辑 1️⃣

UserDao userDao = new UserDao();
private void handleLogin() {
//1.获得组件中用户名和密码
String username = loginUsernameField.getText().trim();
String password = new String(loginPasswordField.getPassword()).trim();
//2.判断用户名和密码是否为空,如果为空,提示用户名和密码不能为空
if (username.isEmpty() || password.isEmpty()) {
JOptionPane.showMessageDialog(this, "用户名和密码不能为空", "登录失败", JOptionPane.ERROR_MESSAGE);
return;
}
//3.如果用户名和密码不为空,调用UserDao的getUserByUsername方法,并比较密码,如果密码正确,跳转到用户管理页面,如果密码错误,提示密码错误
TUser user = userDao.getUserByUsername(username);
if (user == null) {
JOptionPane.showMessageDialog(this, "用户不存在", "登录失败", JOptionPane.ERROR_MESSAGE);
return;
}
if (!user.getPassword().equals(password)) {
JOptionPane.showMessageDialog(this, "密码错误", "登录失败", JOptionPane.ERROR_MESSAGE);
return;
}
//4.登录成功后跳转到主界面
HomeUI homeUI = new HomeUI();
homeUI.setVisible(true);
dispose(); // 关闭当前窗口
}
1.3.2 完成发送验证码逻辑 2️⃣

private void handleSendCode() {
String phone = phoneField.getText().trim();
if (phone.isEmpty()) {
JOptionPane.showMessageDialog(this, "请输入手机号", "提示", JOptionPane.WARNING_MESSAGE);
return;
}
//使用hutool检查手机号码是否合法
boolean isphone = PhoneUtil.isPhone(phone);
if (!isphone) {
JOptionPane.showMessageDialog(this, "手机号格式不正确", "提示", JOptionPane.WARNING_MESSAGE);
return;
}
//模仿发送验证码成功
JOptionPane.showMessageDialog(this, "验证码已发送", "提示", JOptionPane.INFORMATION_MESSAGE);
}
1.3.3 完成注册逻辑 3️⃣

private void handleRegister() {
// todo 处理注册逻辑
//1.获得组件中用户名和密码 和确认密码
String username = registerUsernameField.getText().trim();
String password = new String(registerPasswordField.getPassword());
String confirmPassword = new String(registerConfirmPasswordField.getPassword());
String name = nameField.getText().trim(); // 获取姓名
String phone = phoneField.getText().trim();
String code = codeField.getText().trim();
//2.判断用户名和密码是否为空,如果为空,提示用户名和密码不能为空
---
自己补全
---
//3.判断密码和确认密码是否一致,如果不一致,提示密码不一致
---
自己补全
---
//4.构建User对象
TUser user = new TUser();
---
自己补全
---
//5.添加用户
int count=自己补全
// 判断添加结果
if (count>0) {
JOptionPane.showMessageDialog(this, "注册成功", "注册成功", JOptionPane.INFORMATION_MESSAGE);
// 注册成功后返回欢迎界面
returnToWelcome();
} else {
JOptionPane.showMessageDialog(this, "报错了", "注册失败", JOptionPane.ERROR_MESSAGE);
}
}
1.4 完成HomeUI设计
前言
各位同学,截止到现在,我们已经完成了登录、注册、发送验证码等功能,接下来,我们需要完成HomeUI的设计。
结合之前的项目介绍,我们HomeUI界面中涉及到用户管理模块,分类管理模块,入库模块,出库模块,报表显示模块,以及退出登录模块。
考虑到我们页面布局工作繁琐,因此这项工作我们交给AI完成,完成后,我们可以进行调整,直到满意为止, 我们主要完成业务逻辑的实现。
接下来我们,来设计HomeUI界面基本布局
+-----------------------------------------------------+
| 顶部菜单/标题栏 admin [退出] |
+-------------------+-------------------------------+
| 左侧导航区 | 右侧主内容区 |
| [用户管理] | |
| [分类管理] | 根据左侧选择显示不同功能界面 |
| [入库模块] | (用户管理面板、入库面板等) |
| [出库模块] | |
| [报表显示] | |
+-------------------+-------------------------------+
大模型提示词:👇 👇
HomeUI.java 开发需求
一、项目环境
• 登录:UserUI.java 登录成功 → 跳转 HomeUI.java;
• 欢迎页:WelcomeUI.java(登录前展示);
• 后续扩展:HomeUI 内容区嵌入 CategoryUI.java、MedicineUI.java UserManageUI.java, InboundUI.java, OutboundUI.java, ReportUI.java
• 技术:Java Swing,风格与现有界面一致。
二、界面设计(苹果风格:简洁、留白、色调统一、控件圆润)
1. 整体布局
分三大部分:头部标题栏、侧边栏菜单、主要内容区域,用 BorderLayout/BoxLayout 组合,分区明确、自适应窗口。
2. 头部标题栏(顶部一行)
• 左侧:固定文字 “黑马药品管理系统”(苹方/微软雅黑,加粗大字号,深灰/黑色);
• 右侧:显示 登录用户姓名(从 TUser 对象获取)+ “[退出]”文字;
• 交互:点击“退出” → 关闭 HomeUI,打开WelcomeUI.java;
• 样式:浅灰/白色背景,控件间距均匀、适当留白。
3. 侧边栏菜单(标题栏下左侧)
• 菜单项:垂直排列 分类管理、药品管理、入库管理、出库管理、报表统计、用户管理(可扩展);
• 样式:矩形按钮(默认浅灰背景,悬停加深,点击激活变色);
• 尺寸:宽固定(如 180px),高随窗口自适应;
• 交互:点击菜单 → 右侧内容区切换对应界面(如 CategoryUI)。
4. 主要内容区域(侧边栏右侧)
• 布局:用 CardLayout 管理面板(切换无闪烁);
• 占位面板:
• CategoryUI:左上角显示“分类管理”;
• MedicineUI:左上角显示“药品管理”;
• InboundUI:左上角显示“入库管理”;
• OutboundUI:左上角显示“出库管理”;
• UserManageUI:左上角显示“用户管理”;
• ReportUI:左上角显示“报表统计”;
• 样式:白色背景,四周留白舒适。
三、交互与逻辑
1. 用户信息:UserUI 传 TUser 对象(含姓名)到 HomeUI(构造方法传参或 setter),标题栏显示姓名;
2. 菜单切换:事件监听联动,响应快、无卡顿;
3. 退出:销毁 HomeUI,打开 Welcome,无残留窗口;
4. 命名规范:组件名如 headerPanel、sideMenuPanel、contentPanel(便于维护)。
四、风格一致性
• 色调:浅灰+白色为主,深灰文字,避免花哨;
• 字体:系统无衬线字体(苹方/微软雅黑/SansSerif),字号层次分明;
• 与 UserUI、WelcomeUI 背景、按钮、对话框风格统一。
五、输出要求
1. 提供 HomeUI.java 完整可运行代码(含 import、成员变量、构造方法、界面初始化、事件绑定);
2. 代码注释清晰(说明区域作用、主要逻辑);
3. 用户信息传递方案明确(构造方法传参/setter);
4. 暂不实现 CategoryUI.java、MedicineUI.java UserManageUI.java, InboundUI.java, OutboundUI.java, ReportUI.java 内部逻辑,仅返回占位 JPanel,显示一个文字说明即可。
目标:符合 JavaSE Swing 规范,界面美观、交互流畅,无缝接入现有项目。
接下来创建一些空的面板,如 MedicineUI.java、InboundUI.java、OutboundUI.java、ReportUI.java、UserManageUI.java

public class MedicineUI extends JPanel {
public MedicineUI() {
super();
this.setLayout(new BorderLayout());
this.add(new JLabel("药品管理"), BorderLayout.NORTH);
}
}
public class CategoryUI extends JPanel {
public CategoryUI() {
super();
this.setLayout(new BorderLayout());
this.add(new JLabel("分类管理"), BorderLayout.NORTH);
}
}
public class InboundUI extends JPanel {
public InboundUI() {
super();
this.setLayout(new BorderLayout());
this.add(new JLabel("入库管理"), BorderLayout.NORTH);
}
}
public class OutboundUI extends JPanel {
public OutboundUI() {
super();
this.setLayout(new BorderLayout());
this.add(new JLabel("出库管理"), BorderLayout.NORTH);
}
}
public class ReportUI extends JPanel {
public ReportUI() {
super();
this.setLayout(new BorderLayout());
this.add(new JLabel("报表统计"), BorderLayout.NORTH);
}
}
public class UserManageUI extends JPanel {
public UserManageUI() {
super();
this.setLayout(new BorderLayout());
this.add(new JLabel("用户管理"), BorderLayout.NORTH);
}
}
利用IDea的tongyi灵码进行辅助编程 👇

AI生成的代码如下:
public class HomeUI extends JFrame {
private TUser user;
private JPanel contentPanel; // 内容面板
private CardLayout cardLayout; // 卡片布局
public HomeUI(TUser user) {
this.user = user;
initializeComponents();
}
private void initializeComponents() {
setTitle("黑马医药管理系统");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(1000, 700);
setLocationRelativeTo(null);
// 创建主面板,使用BorderLayout布局
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BorderLayout());
// 创建头部面板
JPanel headerPanel = createHeaderPanel();
mainPanel.add(headerPanel, BorderLayout.NORTH);
// 创建中间内容面板(左右分栏)
JPanel centerPanel = new JPanel();
centerPanel.setLayout(new BorderLayout());
// 创建侧边菜单栏
JPanel sideMenuPanel = createSideMeuPanel();
centerPnel.add(sideMenuPanel, BorderLayout.WEST);
// 创建主要内容区域
contentPanel = createContentPanel();
centerPanel.add(contentPanel, BorderLayout.CENTER);
mainPanel.add(centerPanel, BorderLayout.CENTER);
setContentPane(mainPanel);
}
// 创建头部面板
private JPanel createHeaderPanel() {
JPanel headerPanel = new JPanel();
headerPanel.setLayout(new BorderLayout());
headerPanel.setBackground(Color.LIGHT_GRAY);
headerPanel.setBorder(BorderFactory.createEmptyBorder(10, 15, 10, 15));
// 左侧标题
JLabel titleLabel = new JLabel("黑马药品管理系统");
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 20));
titleLabel.setForeground(Color.BLACK);
headerPanel.add(titleLabel, BorderLayout.WEST);
// 右侧用户信息和退出按钮
JPanel userInfoPanel = new JPanel();
userInfoPanel.setBackground(Color.LIGHT_GAY);
JLabel userLabel = new JLabel(user.getNicknme());
userLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
userInfoPanel.add(userLabel);
JButton logoutButton = new JButton("退出");
logoutButton.setFont(new Font("微软雅黑", Font.PLAIN, 14));
//设置按钮样式:无边框、无背景(模拟文本)
logoutButton.setBorderPainted(false); // 不绘制边框
logoutButton.setContentAreaFilled(false); // 不填充背景
logoutButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int result = JOptionPane.showConfirmDialog(
HomeUI.this,
"确定要退出吗?",
"确认退出",
JOptionPane.YES_NO_OPTION
);
if (result == JOptionPane.YES_OPTION) {
dispose(); // 销毁当前窗口
SwingUtilities.invokLater(() -> {
WelcomeUI welcomeUI = new WelcomeUI();
welcomeUI.setVisible(true);
});
}
}
});
userInfoPanel.add(logoutButton);
headerPanel.add(userInfoPanel, BorderLayout.EAST);
return headerPanel;
}
// 创建侧边菜单面板
private JPanel createSideMenuPanel() {
JPanel sideMenuPanel = new JPanel();
sideMenuPanel.setLayout(new BoxLayout(sideMenuPanel, BoxLayout.Y_AXIS));
sideMenuPanel.setPrefrredSize(new Dimension(180, 0)); // 固定宽度
sideMenuPanel.setBackground(Color.LIGHT_GRAY);
sideMenuPanel.setBorder(BorderFactory.creteEmptyBorder(10, 10, 10, 10));
// 创建菜单按钮数组
String[] menuItems = {"分类管理", "药品管理", "入库管理", "出库管理", "报表统计", "用户管理"};
for (String menuItem : menuItems) {
JButton button = new JButton(menuItem);
button.setAlignmentX(Component.CENTER_ALIGNMENT); // 居中对齐
button.setMaximumSize(new Dimension(Integer.MAX_VALUE, 40)); // 设置最大高度
button.setBackground(Color.WHITE); // 默认背景
button.setFocuPainted(false); // 移除焦点框
// 添加悬停效果
button.addMouseLisener(new java.awt.event.MouseAdapter() {
@Override
public void mouseEnered(java.awt.event.MouseEvent evt) {
button.setBackground(Color.LIGHT_GRAY); // 悬停时加深
}
@Override
public void mousexited(java.awt.event.MouseEvent evt) {
button.setBackground(Color.WHITE); // 离开时恢复
}
});
// 添加点击事件
button.addActionLstener(e -> showContntPanel(menuItem));
sideMenuPanel.add(button);
sideMenuPanel.add(Box.createVerticlStrut(5)); // 添加垂直间距
}
return sideMenuPanel;
}
// 创建内容面板
private JPanel createContentPanel() {
JPanel contentPanel = new JPanel();
cardLayout = new CardLayout();
contentPanel.setLayout(cardLayout);
// 添加各个功能面板
contentPanel.add(new UserManageUI(), "用户管理");
contentPanel.add(new CategoryUI(), "分类管理");
contentPanel.add(new MedicineUI(), "药品管理");
contentPanel.add(new InboudUI(), "入库管理");
contentPanel.add(new OutboundUI(), "出库管理");
contentPanel.add(new ReprtUI(), "报表统计");
return contentPanel;
}
// 显示指定的内容面板
private void showContentPanel(String panelName) {
cardLayout.show(contentPanel, panelName);
}
}

至此,我们已经完成了主界面,接下来我们需要完成各个功能页面。
1.5 完成用户管理页面
前言
接下来,我们完成用户管理页面,如下的空白页面👇

用户管理页面,大致如下👇

提示词👇
UserManageUI.java 开发需求文档
一、项目环境
• 所属模块:HomeUI.java 内容区嵌入的子界面(通过侧边栏“用户管理”菜单切换显示);
• 技术:Java Swing,风格与现有界面(UserUI、WelcomeUI、HomeUI)一致;
• 数据依赖:需加载用户数据(模拟从数据库查询,包含序号、用户名、昵称、手机号码、角色、创建时间等字段)。
二、界面设计(苹果风格:简洁、留白、色调统一、控件圆润)
1. 整体布局
采用 BorderLayout + FlowLayout + 边距控制,分区明确:
• 顶部:检索区(左:检索框+检索按钮;右:添加按钮);
• 中部:表格区(占满剩余空间,带滚动条);
• 整体背景白色,四周留白舒适(如 EmptyBorder(10, 10, 10, 10))。
2. 检索与操作区(顶部第一行)
• 左侧:检索组件
• 检索输入框:宽度约 200px,提示文字“请输入用户名/昵称”,无边框(或浅灰细边框),圆角(6px);
• 检索按钮:文字“检索”,浅灰背景(默认)、悬停加深、点击激活变色,圆角矩形,与输入框间距 8px。
• 重置按钮:文字“重置”,浅灰背景(默认)、悬停加深、点击激活变色,圆角矩形,与输入框间距 8px。
• 右侧:添加按钮
• 文字“添加用户”,样式同检索按钮(统一按钮风格),与左侧检索组件间距 16px。
• 布局:用 FlowLayout 横向排列,居中对齐,整体区域背景白色,高度约 50px(含内边距)。
3. 表格区(第二行)
• 表格组件:JTable + JScrollPane(带垂直滚动条,无水平滚动条,列宽自适应内容);
• 表头字段(按顺序):
序号 用户名 昵称 手机号码 角色 创建时间 操作
• 操作列:每行包含“编辑”和“删除”两个按钮(文字按钮,样式同检索按钮,间距 2,列宽固定约 100px,按钮高度和列一致);
• 表格样式:
• 表头:浅灰背景,深灰文字,加粗,居中对齐;
• 内容行:白色背景,深灰文字,居中对齐(序号、手机号、创建时间),用户名/昵称/角色左对齐;
• 行高:紧凑适中,隔行浅灰背景(斑马纹,提升可读性);
• 边框:表格外框浅灰细边框,单元格间无分隔线(或极浅灰分隔线,符合苹果简洁风格);
• 列宽:序号 20px,其他 100px,剩余的宽度自适应内容(如手机号、创建时间)。
• 按钮:所有的按钮通过一个方法 createButton() 统一初始化样式(如圆角、背景色、字体、边距等),避免重复代码。
三、交互与逻辑
1. 数据加载
• 定义独立方法 loadUserData():模拟从数据源(如 UserDao)加载用户数据,返回 List<TUser>(包含序号、用户名、昵称、手机号、角色、创建时间等字段),TUser实体类在cn.yangeit.entity包下;
• 页面初始化时自动调用 loadUserData() 加载数据并刷新表格;
• 检索按钮点击时,根据输入框关键词过滤数据,调用 loadUserData(keyword) 重新加载并刷新表格(关键词可为空,即加载全部)。
2. 表格初始化
• 定义独立方法 initTable():创建 DefaultTableModel(指定表头字段,操作列设为非 editable),关联 JTable,并设置表格样式(行高、对齐方式、斑马纹等);
• 数据加载后,将 List<TUser> 转换为表格行数据,通过 tableModel.setDataVector() 更新表格内容。
3. 按钮交互
• 检索按钮:绑定 ActionListener,获取输入框文本,调用 loadUserData(keyword) 刷新表格;
• 添加用户按钮:绑定 ActionListener,点击后打开添加用户对话框(显示用户名username,名字nickname, 手机号码telphone,密码password),点击保存后,触发saveuser方法;
• 编辑/删除按钮:操作列的每行按钮绑定 ActionListener,点击编辑按钮时获取当前行数据(如用户名,用户id,用户密码,用户昵称,手机号码),弹出对应的编辑对话框(确认和取消按钮 在最下方),确认后执行逻辑saveUser(当前先留空,仅打印日志)。
• 点击删除按钮时获取当前行数据(如用户名,用户id),弹出确认对话框(如“确定删除用户XXX?”),确认后执行逻辑deleteById(用户id)(当前先留空,仅打印日志)
• 要让 JTable正确显示操作列的按钮(水平布局,编辑在左,删除在右),必须通过 自定义 TableCellRenderer和 TableCellEditor 实现,操作列背景颜色和其他列一致,且删除按钮红色,编辑按钮蓝色
4. 组件命名规范
• 检索输入框:searchField;
• 检索按钮:searchBtn;
• 添加按钮:addBtn;
• 表格:userTable;
• 表格模型:tableModel;
• 滚动面板:scrollPane。
四、风格一致性
• 色调:浅灰+白色为主(背景、按钮默认色),深灰文字(表头、内容),按钮悬停/激活色为深一点的浅灰(如 #E0E0E0 → #D0D0D0);
• 字体:系统无衬线字体(苹方/微软雅黑/SansSerif),表头字号 13px(加粗),内容字号 12px;
• 与现有界面统一:按钮样式、对话框风格、背景色与 HomeUI 侧边栏按钮、UserUI 输入框保持一致。
五、输出要求
1. 提供 UserManageUI.java 完整可运行代码(含 import、成员变量、构造方法、界面初始化、事件绑定);
2. 代码注释清晰:说明各区域作用(如“检索区”“表格区”)、主要方法逻辑(如 loadUserData() 数据加载流程);
3. 独立方法定义:
• initTable():初始化表格模型和样式;
• loadUserData():无参,加载全部用户数据;
• loadUserData(String keyword):带参,根据关键词过滤加载数据;
• saveUser():保存用户数据(仅打印日志)
• deleteById():删除用户数据(仅打印日志)
4. 暂不实现业务逻辑(如实际数据查询、改密/删除后端交互),仅模拟数据(如 List<TUser> 硬编码 5-10 条测试数据)和前端交互(按钮点击日志打印);
5. 表格操作列按钮可点击,点击时打印当前行数据(如“点击删除用户:张三”)。
目标:符合 JavaSE Swing 开发规范,界面美观(苹果风格)、交互流畅,数据与界面分离,便于后续扩展业务逻辑。
将UserManageUI.java拖拽到右侧的tongyi灵码对话框中,输入上述提示词,点击确定 👇

代码审查后,点击接受修改 👇
然后调整错误地方,点击运行,观察运行效果 👇

如果有错,爆红, 等待修改后,再运行,直至运行成功 👇

接下来测试添加,删除,编辑按钮,是否正常输出日志 👇

1.6 完成用户列表数据
前言
上面我们完成了用户管理页面的界面,接下来我们需要完成用户列表数据,👇

首先,在UserMapper中增加一个getAllUsersWithoutPassword获取所有用户的sql语句(不包含密码)
@Select("select id, username, nickname, role, telphone, create_time, update_time from t_user")
List<User> getAllUsersWithoutPassword();
接着,在UserDao中增加一个listUser方法,调用UserMapper的getAllUsersWithoutPassword方法(复制已有的方法,改改方法名 ),返回用户列表
public List<TUser> listUser() {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<TUser> list =userMapper.getAllUsersWithoutPassword();
sqlSession.commit();
sqlSession.close();
return list;
}
最后,在UserManageUI中的数据加载方法中,调用UserDao的listUser方法,返回用户列表
UserDao userDao = new UserDao();
/**
* 获取模拟数据
* @return 用户列表
*/
private List<TUser> getMockData() {
List<TUser> users = userDao.listUser();
return users;
}
重启运行,观察运行效果如下图 👇

1.7 完成用户增加
前言
接下来我们来完成用户增加功能,👇,当然这项功能是管理员才有权限的👇

我们先分析一下,流程,如下图:👇

在UserManageUI中,找到添加用户确认框的方法,如下图 👇

接下来参考流程图,完成代码,如下图 👇
private boolean saveUser(String username, String nickname, String tel, String password) {
//2.判断用户名和密码是否为空,如果为空,提示用户名和密码不能为空
if (username.isEmpty() || password.isEmpty() ||nickname.isEmpty() || tel.isEmpty()) {
JOptionPane.showMessageDialog(this, "请填写所有字段", "注册失败", JOptionPane.ERROR_MESSAGE);
return false;
}
// System.out.println("保存用户: " + username + ", 昵称: " + nickname + ", 手机: " + tel);
//1.通过用户名查询用户,如果存在,弹出提示框,用户已存在,结束
TUser user = userDao.getUserByUsername(username);
if (user != null) {
JOptionPane.showMessageDialog(this, "用户已存在!", "错误", JOptionPane.ERROR_MESSAGE);
return false;
}
//2.判断手机是否合法
if (!PhoneUtil.isPhone(tel)) {
JOptionPane.showMessageDialog(this, "手机号格式不正确!", "错误", JOptionPane.ERROR_MESSAGE);
return false;
}
//3.如果用户名不存在,构建用户对象,赋值属性
user = new TUser();
user.setUsername(username);
user.setNickname(nickname);
user.setTelphone(tel);
user.setPassword(password);
user.setRole("user");
user.setCreateTime(LocalDateTime.now());
user.setUpdateTime(LocalDateTime.now());
//4.调用数据库保存用户
userDao.addUser(user);
return true;
}

1.8 完成用户编辑
前言
点击编辑按钮,弹出编辑对话框,如下图 👇

在UserManageUI中,找到编辑用户确认框的方法,如下图 👇

接下来开始编码:👇
首先,在UserMapper.java中增加一个updateById方法,用于更新用户信息,如下 👇
//修改用户昵称,手机号码,密码
@Update("update t_user set nickname = #{nickname}, telphone = #{telphone}, password = #{password} where id = #{id}")
int updateById(TUser user);
接着,在UserDao中增加一个updateUserById方法,调用UserMapper的updateById方法,如下 👇
public int updateUserById(TUser user) {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
int count=userMapper.updateById(user);
sqlSession.commit();
sqlSession.close();
return count;
}
最后,在UserManageUI中,找到编辑用户确认框的方法,并改造saveUser方法 👇
private boolean saveUser(String type, String username, String nickname, String tel, String password) {
System.out.println("用户: " + username + ", 昵称: " + nickname + ", 手机: " + tel);
if (type.equals("save")) {
//1.判断用户名和密码是否为空,如果为空,提示用户名和密码不能为空
if (username.isEmpty() || password.isEmpty() || nickname.isEmpty() || tel.isEmpty()) {
JOptionPane.showMessageDialog(this, "请填写所有字段", "注册失败", JOptionPane.ERROR_MESSAGE);
return false;
}
//2.判断手机是否合法
if (!PhoneUtil.isPhone(tel)) {
JOptionPane.showMessageDialog(this, "手机号格式不正确!", "错误", JOptionPane.ERROR_MESSAGE);
return false;
}
//1.通过用户名查询用户,如果存在,弹出提示框,用户已存在,结束
TUser user = userDao.getUserByUsername(username);
if (user != null) {
JOptionPane.showMessageDialog(this, "用户已存在!", "错误", JOptionPane.ERROR_MESSAGE);
return false;
}
//3.如果用户名不存在,构建用户对象,赋值属性
user = new TUser();
user.setUsername(username);
user.setNickname(nickname);
user.setTelphone(tel);
user.setPassword(password);
user.setRole("user");
user.setCreateTime(LocalDateTime.now());
user.setUpdateTime(LocalDateTime.now());
//4.调用数据库保存用户
userDao.addUser(user);
return true;
} else {
TUser user = userDao.getUserByUsername(username);
if (!password.isEmpty()) {
user.setPassword(password);
}
user.setTelphone(tel);
user.setNickname(nickname);
user.setPassword(password);
user.setUpdateTime(LocalDateTime.now());
userDao.updateUserById(user);
return true;
}
}
1.9 完成用户删除
前言
点击删除按钮,调用deleteUserByUsername 删除用户,大家自行完成!
注意:只有role为admin的用户才有权限删除用户
