)
从需求分析到编码实现手把手带你开发一个覆盖七大业务角色的商超管理系统一、项目背景与选型在中小型超市的日常运营中采购、库存、收银、会员、财务等环节往往依赖手工记录或多套独立系统数据孤岛严重效率低下。本项目旨在用 C 和 Qt 框架开发一套一体化的商超销售管理系统将所有业务流程整合到统一的图形化界面中。技术选型理由技术选型理由C面向对象、高性能、适合桌面应用开发Qt 6.5成熟的跨平台 GUI 框架信号槽机制简洁自带 SQL 模块SQLite零配置嵌入式数据库无需额外安装数据库服务qmakeQt 原生构建工具管理.pro项目文件简单直观二、系统架构设计2.1 整体架构系统采用三层架构设计┌─────────────────────────────────────────┐ │ 表现层 (UI Layer) │ │ LoginWidget → MainWindow → 7个功能Widget │ ├─────────────────────────────────────────┤ │ 业务逻辑层 (Logic Layer) │ │ 各Widget内部的方法 信号槽通信 │ ├─────────────────────────────────────────┤ │ 数据访问层 (Data Layer) │ │ DatabaseManager (单例) → SQLite │ └─────────────────────────────────────────┘-表现层Qt Widget 组成的 GUI 界面每个业务角色对应一个独立 Widget-业务逻辑层收银结算、库存预警、积分计算等核心逻辑在各 Widget 内部实现-数据访问层通过DatabaseManager单例统一管理所有数据库操作2.2 模块划分系统将七个业务角色映射为七个功能模块主窗口使用QStackedWidget根据登录角色动态切换MainWindow ├── 侧边栏导航 (QListWidget) │ └── 根据角色动态生成菜单项 └── 内容区 (QStackedWidget) ├── AdminWidget # 管理员模块 ├── PurchaseWidget # 采购模块 ├── WarehouseWidget # 库管模块 ├── CashierWidget # 收银模块 ├── ManagerWidget # 店长模块 ├── FinanceWidget # 财务模块 └── MemberWidget # 会员模块三、数据库设计系统使用 SQLite 创建了 9 张核心数据表通过外键维护表间关联ER 关系图文字描述suppliers 1───N purchases N───1 products products 1───N stock products 1───N sales N───1 members sales 1───N return_goods purchases, sales ──→ finance通过触发业务逻辑写入核心表结构-- 商品表记录商品基本信息和库存量 CREATE TABLE products ( product_id INTEGER PRIMARY KEY AUTOINCREMENT, barcode TEXT UNIQUE, -- 商品条码扫码识别 product_name TEXT NOT NULL, category TEXT, -- 商品分类 unit TEXT, -- 计量单位 purchase_price REAL DEFAULT 0.0, -- 进价 sale_price REAL DEFAULT 0.0, -- 售价 stock_qty INTEGER DEFAULT 0, -- 当前库存 supplier_id INTEGER, -- 供应商外键 status TEXT DEFAULT 正常, FOREIGN KEY (supplier_id) REFERENCES suppliers(supplier_id) ); -- 销售记录表记录每笔销售流水 CREATE TABLE sales ( sale_id INTEGER PRIMARY KEY AUTOINCREMENT, product_id INTEGER NOT NULL, quantity INTEGER NOT NULL, unit_price REAL NOT NULL, total_amount REAL NOT NULL, sale_date TEXT DEFAULT (datetime(now,localtime)), cashier TEXT, -- 收银员 payment_method TEXT DEFAULT 现金, -- 支付方式 member_id INTEGER, -- 会员外键可为空 status TEXT DEFAULT 已完成 );四、核心模块实现4.1 数据库管理器单例模式DatabaseManager是整个系统的数据访问层核心采用 Meyers Singleton 模式确保全局唯一实例class DatabaseManager : public QObject { Q_OBJECT public: static DatabaseManager instance() { static DatabaseManager inst; return inst; } bool initDatabase(); // 创建所有表 bool login(const QString amp;username, const QString amp;password, UserInfo amp;info); QSqlQuery executeQuery(const QString amp;sql); QSqlQuery executeQuery(const QString amp;sql, const QListlt;QVariantgt; amp;params); private: explicit DatabaseManager(QObject *parent nullptr); DatabaseManager(const DatabaseManager) delete; DatabaseManager operator(const DatabaseManager) delete; QSqlDatabase mDatabase; };设计要点- 删除拷贝构造和赋值运算符防止多实例- 提供executeQuery的重载版本支持参数化查询防 SQL 注入- 程序启动时initDatabase()自动建表并插入默认测试账号4.2 登录模块无边框可拖拽窗口登录窗口使用Qt::FramelessWindowHint去掉系统标题栏自行实现拖拽功能void LoginWidget::mousePressEvent(QMouseEvent *event) { if (event-button() Qt::LeftButton) { // Qt6 新 API: globalPosition() 替代已废弃的 globalPos() mDragPos event-globalPosition().toPoint() - frameGeometry().topLeft(); event-accept(); } } void LoginWidget::mouseMoveEvent(QMouseEvent *event) { if (event-buttons() Qt::LeftButton) { move(event-globalPosition().toPoint() - mDragPos); event-accept(); } }登录验证使用参数化查询bool DatabaseManager::login(const QString username, const QString password, UserInfo userInfo) { QString sql SELECT user_id, username, real_name, role, phone FROM users WHERE username :username AND password :password; QSqlQuery query; query.prepare(sql); query.bindValue(:username, username); query.bindValue(:password, password); // ... }4.3 主窗口角色权限导航主窗口根据用户角色动态构建侧边栏菜单void MainWindow::configureNavByRole(const QString role) { mNavList-clear(); mNavItems.clear(); if (role 管理员) { mNavItems lt;lt; 用户管理 lt;lt; 采购管理 lt;lt; 库存管理 lt;lt; 销售管理 lt;lt; 数据统计 lt;lt; 财务管理 lt;lt; 会员管理; } else if (role 收银员) { mNavItems lt;lt; 销售收银 lt;lt; 销售查询 lt;lt; 退货处理; } // ... 其他角色 }导航点击通过QStackedWidget::setCurrentIndex()切换功能页面void MainWindow::onNavItemSelected(int index) { int pageIdx mapRoleToPageIndex(mCurrentUser.role, index); mContentStack-setCurrentIndex(pageIdx); }4.4 收银模块收银是系统最核心的业务模块实现了完整的购物车结算流程扫码添加商品void CashierWidget::onBarcodeEntered() { QString barcode mBarcodeInput-text().trimmed(); QString sql SELECT product_id, product_name, sale_price FROM products WHERE barcode ?; QSqlQuery query DatabaseManager::instance().executeQuery(sql, {barcode}); if (query.next()) { mProductNameLabel-setText(query.value(product_name).toString()); mProductPriceLabel-setText(query.value(sale_price).toString()); } }购物车管理使用QListCartItem结构体暂存购物车数据支持相同商品累加数量void CashierWidget::onAddToCart() { // 检查购物车中是否已有该商品 bool found false; for (int i 0; i mCartItems.size(); i) { if (mCartItems[i].barcode barcode) { mCartItems[i].quantity quantity; mCartItems[i].subtotal mCartItems[i].price * mCartItems[i].quantity; found true; break; } } if (!found) { CartItem item{productId, barcode, productName, price, quantity, price * quantity}; mCartItems.append(item); } refreshCartTable(); updateCartTotal(); }下图展示了收银模块从扫码到完成结算的完整业务逻辑与数据流flowchart TD subgraph 购物车管理 A[开始收银] -- B[扫码/输入商品条码] B -- C{查询商品信息} C --|成功| D[显示商品名称/价格] C --|失败| E[提示商品不存在] D -- F[添加商品到购物车] F -- G{检查购物车中是否已有该商品} G --|是| H[累加商品数量] G --|否| I[新增购物车项] H -- J[更新购物车显示] I -- J J -- K[更新购物车总价] K -- L{用户点击结算} end L --|否| B subgraph 结算支付 L --|是| M[确认支付方式与金额] end subgraph 数据持久化 M -- N[写入销售记录表 sales] N -- O[更新商品库存 products.stock_qty] O -- P[记录财务流水 finance] P -- Q{是否为会员购物?} Q --|是| R[更新会员积分 members.points] Q --|否| S[跳过积分更新] R -- T[打印小票/显示成功] S -- T T -- U[清空购物车] U -- V[结束本次收银] end流程说明扫码/输入收银员通过扫码枪或手动输入商品条码。商品查询系统根据条码查询products表获取商品名称、售价等信息。购物车管理商品加入购物车时会检查是否已存在相同条码的商品若存在则累加数量否则新增一项。结算确认用户点击结算后系统弹出支付确认界面选择支付方式现金、扫码等。数据持久化确认支付后系统依次执行以下数据库操作向sales表插入销售记录更新products表的库存数量向finance表插入财务流水若购物会员 ID 不为空则更新members表的积分字段。收尾工作打印小票或显示成功信息清空购物车准备下一次收银。结算流程确认支付后写入销售记录、更新商品库存、记录财务流水会员购物自动积分。4.5 QSS 样式美化系统使用 QSSQt Style Sheets实现现代化 UI 风格核心样式包括/* 侧边栏渐变背景 */ #sideBar { background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #2c3e50, stop:1 #34495e); } /* 导航选中高亮 */ #navList::item:selected { background-color: #3498db; color: #ffffff; border-left: 3px solid #2ecc71; } /* 统计卡片 */ #statsCard.warning { border-left: 4px solid #f39c12; } #statsCard.danger { border-left: 4px solid #e74c3c; } /* 表格交替色 */ #dataTable::item:alternate { background-color: #f8f9fa; }五、Qt5 到 Qt6 的迁移踩坑在开发过程中遇到了几个 Qt6 API 变更导致的编译错误这里记录解决方案5.1QApplication::desktop()已移除// Qt5已失效 move((QApplication::desktop()-width() - width()) / 2, (QApplication::desktop()-height() - height()) / 2); // Qt6 正确写法 QScreen *screen QGuiApplication::primaryScreen(); if (screen) { move((screen-geometry().width() - width()) / 2, (screen-geometry().height() - height()) / 2); }5.2QMouseEvent::globalPos()已废弃// Qt5已废弃 mDragPos event-globalPos() - frameGeometry().topLeft(); // Qt6 正确写法 mDragPos event-globalPosition().toPoint() - frameGeometry().topLeft();5.3QTableWidgetItem::setTextColor()已移除// Qt5已失效 item-setTextColor(QColor(#e74c3c)); // Qt6 正确写法 item-setForeground(QBrush(QColor(#e74c3c)));六、总结本项目通过一个完整的商超销售管理系统实践了 C 面向对象编程和 Qt6 可视化开发的综合运用技术点实践内容单例模式DatabaseManager 数据库管理信号槽机制Qt6 新语法编译期类型检查QSS 样式渐变、阴影、圆角等现代 UISQLite 数据库建表、外键、参数化查询多角色权限动态导航菜单 页面切换Qt6 API 迁移QScreen、globalPosition、setForeground项目总计约21 个源文件覆盖9 张数据库表、7 个业务角色、30 个功能页面代码量约5000 行适合作为 C 桌面应用开发的学习实践项目。---源码下载地址AtomGit - 全球开发者的开源社区,开源代码托管平台技术栈C / Qt 6.5 / SQLite 3 / qmake / MinGW