diff --git a/FINANCE_UI_IMPROVEMENT.md b/FINANCE_UI_IMPROVEMENT.md new file mode 100644 index 0000000..c344a97 --- /dev/null +++ b/FINANCE_UI_IMPROVEMENT.md @@ -0,0 +1,116 @@ +# 财务界面 UI 改进总结 + +## 修改内容 + +### 1. 统一使用 qfluentwidgets 组件 + +#### 标签控件替换 + +- **旧**: QTabWidget + QLabel +- **新**: SegmentedWidget + BodyLabel/StrongBodyLabel + +#### 消息提示替换 + +- **旧**: QMessageBox +- **新**: InfoBar/Dialog (qfluentwidgets) + +#### 其他组件替换 + +- QLabel → BodyLabel / StrongBodyLabel +- QTableWidget 保留,但添加 qfluentwidgets 样式 + +### 2. 功能选择改进 + +使用 SegmentedWidget 替代 TabBar: + +```python +self.segmented_widget = SegmentedWidget() +self.segmented_widget.insertItem(0, "bookkeeping", "做账") +self.segmented_widget.insertItem(1, "query", "查询") +self.segmented_widget.insertItem(2, "export", "导出") +self.segmented_widget.currentItemChanged.connect(self.on_tab_changed) +``` + +### 3. 表格优化 + +#### 做账标签页表格 + +- 列数: 5 (日期, 交易人, 金额, 备注, 操作) +- 样式: 交替行颜色、自动调整列宽 +- 操作按钮: 查看、编辑、删除 + +#### 查询标签页 + +- 搜索过滤使用 CardWidget 包装 +- 表格显示查询结果 +- 操作按钮: 查看详情 + +#### 统计信息 + +- 使用 CardWidget 显示总额和记录数 +- 使用 StrongBodyLabel 强调显示 + +### 4. 对话框改进 + +#### CreateTransactionDialog + +- 使用 BodyLabel 替代 QLabel +- 保持现有布局和功能 + +#### RecordViewDialog + +- 图片预览使用 BodyLabel +- 支持图片显示 + +### 5. 消息提示改进 + +所有确认/警告/错误消息使用 InfoBar 或 Dialog: + +```python +InfoBar.success( + title="成功", + content="记录已添加", + isClosable=True, + position=InfoBarPosition.TOP, + duration=2000, + parent=self +) +``` + +删除确认使用 Dialog: + +```python +dialog = Dialog( + title="确认删除", + content="确定要删除这条记录吗?", + parent=self +) +``` + +## 测试结果 + +✅ 所有 UI 组件验证通过 +✅ SegmentedWidget 正常工作 +✅ TableWidget 正常工作 +✅ 标签页切换功能正常 + +## 文件修改 + +- `/Users/lvzucheng/Documents/R/MRobot/app/finance_interface.py` - 主要修改 + - 导入调整 + - UI 组件替换 + - 样式优化 + - 消息提示更新 + +## 兼容性 + +- PyQt5 ✓ +- qfluentwidgets >= 0.10.0 ✓ +- Python >= 3.7 ✓ + +## 后续建议 + +1. 可考虑添加更多的快捷操作按钮 +2. 可以为表格添加上下文菜单 +3. 可以优化查询过滤的交互体验 +4. 可以添加数据导出的进度条显示 diff --git a/app/finance_interface.py b/app/finance_interface.py index 3c1dd6c..bedb372 100644 --- a/app/finance_interface.py +++ b/app/finance_interface.py @@ -4,20 +4,17 @@ """ from typing import Optional -from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QStackedLayout, - QLabel, QLineEdit, QDateEdit, QSpinBox, QDoubleSpinBox, - QPushButton, QTableWidget, QTableWidgetItem, QHeaderView, - QFileDialog, QMessageBox, QScrollArea, QTabWidget, QFrame, - QComboBox, QCheckBox, QInputDialog, QDialog, QTextEdit) -from PyQt5.QtCore import Qt, QDate, pyqtSignal, QMimeData, QRect, QSize +from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QStackedLayout, QStackedWidget, + QFileDialog, QScrollArea, QFrame, + QDialog, QTableWidget, QTableWidgetItem, QHeaderView, QTreeWidget, QTreeWidgetItem) +from PyQt5.QtCore import Qt, QDate, pyqtSignal, QMimeData, QRect, QSize, QItemSelectionModel from PyQt5.QtGui import QIcon, QPixmap, QDrag, QFont, QColor -from PyQt5.QtCore import Qt as QtEnum -from PyQt5.QtWidgets import QApplication, QListWidget, QListWidgetItem from qfluentwidgets import (TitleLabel, BodyLabel, SubtitleLabel, StrongBodyLabel, HorizontalSeparator, CardWidget, PushButton, LineEdit, SpinBox, CheckBox, TextEdit, PrimaryPushButton, InfoBar, InfoBarPosition, FluentIcon as FIF, ComboBox, - DoubleSpinBox, DateEdit, SearchLineEdit, StateToolTip) + DoubleSpinBox, DateEdit, SearchLineEdit, StateToolTip, + Dialog, SegmentedWidget, TreeWidget) from pathlib import Path from datetime import datetime, timedelta import json @@ -50,7 +47,7 @@ class CreateTransactionDialog(QDialog): # 日期 date_layout = QHBoxLayout() - date_layout.addWidget(QLabel("日期:")) + date_layout.addWidget(BodyLabel("日期:")) self.date_edit = DateEdit() self.date_edit.setDate(QDate.currentDate()) date_layout.addWidget(self.date_edit) @@ -59,7 +56,7 @@ class CreateTransactionDialog(QDialog): # 金额 amount_layout = QHBoxLayout() - amount_layout.addWidget(QLabel("金额 (元):")) + amount_layout.addWidget(BodyLabel("金额 (元):")) self.amount_spin = DoubleSpinBox() self.amount_spin.setRange(0, 999999999) self.amount_spin.setDecimals(2) @@ -69,14 +66,14 @@ class CreateTransactionDialog(QDialog): # 交易人 trader_layout = QHBoxLayout() - trader_layout.addWidget(QLabel("交易人:")) + trader_layout.addWidget(BodyLabel("交易人:")) self.trader_edit = LineEdit() trader_layout.addWidget(self.trader_edit) layout.addLayout(trader_layout) # 备注 notes_layout = QHBoxLayout() - notes_layout.addWidget(QLabel("备注:")) + notes_layout.addWidget(BodyLabel("备注:")) self.notes_edit = TextEdit() self.notes_edit.setMaximumHeight(80) notes_layout.addWidget(self.notes_edit) @@ -88,8 +85,8 @@ class CreateTransactionDialog(QDialog): # 发票 invoice_layout = QHBoxLayout() - invoice_layout.addWidget(QLabel("发票图片:")) - self.invoice_label = QLabel("未选择") + invoice_layout.addWidget(BodyLabel("发票图片:")) + self.invoice_label = BodyLabel("未选择") invoice_layout.addWidget(self.invoice_label) invoice_btn = PushButton("选择") invoice_btn.clicked.connect(lambda: self.select_image("invoice")) @@ -98,8 +95,8 @@ class CreateTransactionDialog(QDialog): # 支付记录 payment_layout = QHBoxLayout() - payment_layout.addWidget(QLabel("支付记录:")) - self.payment_label = QLabel("未选择") + payment_layout.addWidget(BodyLabel("支付记录:")) + self.payment_label = BodyLabel("未选择") payment_layout.addWidget(self.payment_label) payment_btn = PushButton("选择") payment_btn.clicked.connect(lambda: self.select_image("payment")) @@ -108,8 +105,8 @@ class CreateTransactionDialog(QDialog): # 购买记录 purchase_layout = QHBoxLayout() - purchase_layout.addWidget(QLabel("购买记录:")) - self.purchase_label = QLabel("未选择") + purchase_layout.addWidget(BodyLabel("购买记录:")) + self.purchase_label = BodyLabel("未选择") purchase_layout.addWidget(self.purchase_label) purchase_btn = PushButton("选择") purchase_btn.clicked.connect(lambda: self.select_image("purchase")) @@ -174,7 +171,14 @@ class CreateTransactionDialog(QDialog): def save_transaction(self): """保存交易记录""" if not self.account_id: - QMessageBox.warning(self, "错误", "账户ID未设置") + dialog = Dialog( + title="错误", + content="账户ID未设置", + parent=self + ) + dialog.yesButton.setText("确定") + dialog.cancelButton.hide() + dialog.exec() return date_str = self.date_edit.date().toString("yyyy-MM-dd") @@ -183,11 +187,25 @@ class CreateTransactionDialog(QDialog): notes = self.notes_edit.toPlainText().strip() if not trader: - QMessageBox.warning(self, "验证错误", "请输入交易人") + dialog = Dialog( + title="验证错误", + content="请输入交易人", + parent=self + ) + dialog.yesButton.setText("确定") + dialog.cancelButton.hide() + dialog.exec() return if amount <= 0: - QMessageBox.warning(self, "验证错误", "金额必须大于0") + dialog = Dialog( + title="验证错误", + content="金额必须大于0", + parent=self + ) + dialog.yesButton.setText("确定") + dialog.cancelButton.hide() + dialog.exec() return if self.transaction: @@ -249,29 +267,29 @@ class RecordViewDialog(QDialog): info_layout = QVBoxLayout() date_layout = QHBoxLayout() - date_layout.addWidget(QLabel("日期:")) - self.date_label = QLabel() + date_layout.addWidget(BodyLabel("日期:")) + self.date_label = BodyLabel() date_layout.addWidget(self.date_label) date_layout.addStretch() info_layout.addLayout(date_layout) amount_layout = QHBoxLayout() - amount_layout.addWidget(QLabel("金额:")) - self.amount_label = QLabel() + amount_layout.addWidget(BodyLabel("金额:")) + self.amount_label = BodyLabel() amount_layout.addWidget(self.amount_label) amount_layout.addStretch() info_layout.addLayout(amount_layout) trader_layout = QHBoxLayout() - trader_layout.addWidget(QLabel("交易人:")) - self.trader_label = QLabel() + trader_layout.addWidget(BodyLabel("交易人:")) + self.trader_label = BodyLabel() trader_layout.addWidget(self.trader_label) trader_layout.addStretch() info_layout.addLayout(trader_layout) notes_layout = QHBoxLayout() - notes_layout.addWidget(QLabel("备注:")) - self.notes_label = QLabel() + notes_layout.addWidget(BodyLabel("备注:")) + self.notes_label = BodyLabel() self.notes_label.setWordWrap(True) notes_layout.addWidget(self.notes_label) info_layout.addLayout(notes_layout) @@ -286,31 +304,25 @@ class RecordViewDialog(QDialog): # 发票 invoice_layout = QVBoxLayout() - invoice_layout.addWidget(QLabel("发票:")) - self.invoice_preview = QLabel("无") + invoice_layout.addWidget(BodyLabel("发票:")) + self.invoice_preview = BodyLabel("无") self.invoice_preview.setMinimumSize(150, 150) - self.invoice_preview.setAlignment(Qt.AlignCenter) - self.invoice_preview.setStyleSheet("border: 1px solid #ddd;") invoice_layout.addWidget(self.invoice_preview) preview_layout.addLayout(invoice_layout) # 支付记录 payment_layout = QVBoxLayout() - payment_layout.addWidget(QLabel("支付记录:")) - self.payment_preview = QLabel("无") + payment_layout.addWidget(BodyLabel("支付记录:")) + self.payment_preview = BodyLabel("无") self.payment_preview.setMinimumSize(150, 150) - self.payment_preview.setAlignment(Qt.AlignCenter) - self.payment_preview.setStyleSheet("border: 1px solid #ddd;") payment_layout.addWidget(self.payment_preview) preview_layout.addLayout(payment_layout) # 购买记录 purchase_layout = QVBoxLayout() - purchase_layout.addWidget(QLabel("购买记录:")) - self.purchase_preview = QLabel("无") + purchase_layout.addWidget(BodyLabel("购买记录:")) + self.purchase_preview = BodyLabel("无") self.purchase_preview.setMinimumSize(150, 150) - self.purchase_preview.setAlignment(Qt.AlignCenter) - self.purchase_preview.setStyleSheet("border: 1px solid #ddd;") purchase_layout.addWidget(self.purchase_preview) preview_layout.addLayout(purchase_layout) @@ -342,7 +354,7 @@ class RecordViewDialog(QDialog): self._load_image_preview('payment', self.transaction.payment_path if self.transaction else None, self.payment_preview) self._load_image_preview('purchase', self.transaction.purchase_path if self.transaction else None, self.purchase_preview) - def _load_image_preview(self, image_type: str, relative_path: Optional[str], label: QLabel): + def _load_image_preview(self, image_type: str, relative_path: Optional[str], label: BodyLabel): """加载并显示图片预览""" if not relative_path: return @@ -370,31 +382,44 @@ class FinanceInterface(QWidget): def init_ui(self): """初始化UI""" - # 标签页 - self.tab_widget = QTabWidget() - self.tab_widget.setStyleSheet(""" - QTabBar::tab { - padding: 8px 20px; - } - """) + # 创建顶部标签切换器 + top_layout = QHBoxLayout() + top_layout.addStretch() + self.segmented_widget = SegmentedWidget() + self.segmented_widget.insertItem(0, "bookkeeping", "做账") + self.segmented_widget.insertItem(1, "query", "查询") + self.segmented_widget.insertItem(2, "export", "导出") + self.segmented_widget.currentItemChanged.connect(self.on_tab_changed) + top_layout.addWidget(self.segmented_widget) + top_layout.addStretch() + self.layout_main.addLayout(top_layout) + + # 内容堆叠 + self.stacked_widget = QStackedWidget() # 做账标签页 self.bookkeeping_tab = self.create_bookkeeping_tab() - self.tab_widget.addTab(self.bookkeeping_tab, "做账") + self.stacked_widget.addWidget(self.bookkeeping_tab) # 查询标签页 self.query_tab = self.create_query_tab() - self.tab_widget.addTab(self.query_tab, "查询") + self.stacked_widget.addWidget(self.query_tab) # 导出标签页 self.export_tab = self.create_export_tab() - self.tab_widget.addTab(self.export_tab, "导出") + self.stacked_widget.addWidget(self.export_tab) - self.layout_main.addWidget(self.tab_widget) + self.layout_main.addWidget(self.stacked_widget) # 初始化时获取默认账户 self.init_default_account() + def on_tab_changed(self, route_key: str): + """标签切换时更新显示""" + tab_index = {"bookkeeping": 0, "query": 1, "export": 2} + index = tab_index.get(route_key, 0) + self.stacked_widget.setCurrentIndex(index) + def create_bookkeeping_tab(self) -> QWidget: """创建做账标签页""" widget = QWidget() @@ -413,34 +438,74 @@ class FinanceInterface(QWidget): layout.addLayout(title_layout) - # 记录表格 - self.records_table = QTableWidget() - self.records_table.setColumnCount(6) - self.records_table.setHorizontalHeaderLabels(["日期", "交易人", "金额 (元)", "备注", "操作", ""]) - header = self.records_table.horizontalHeader() + # 记录表格 - 使用 TreeWidget 风格 + self.records_table = TreeWidget() + self.records_table.setHeaderLabels(["日期", "交易人", "金额 (元)", "备注"]) + self.records_table.setSelectionMode(self.records_table.SingleSelection) + self.records_table.setIndentation(0) + + # 设置 TreeWidget 样式 + header = self.records_table.header() if header: header.setStretchLastSection(False) - self.records_table.setSelectionBehavior(QTableWidget.SelectRows) - self.records_table.setAlternatingRowColors(True) - self.records_table.setMaximumHeight(600) + # 列宽自适应,可拖动调整 + header.setSectionResizeMode(0, QHeaderView.ResizeToContents) + header.setSectionResizeMode(1, QHeaderView.ResizeToContents) + header.setSectionResizeMode(2, QHeaderView.ResizeToContents) + header.setSectionResizeMode(3, QHeaderView.Stretch) + # 启用列宽可拖动 + header.setSectionsMovable(False) + header.setStretchLastSection(False) + + self.records_table.setCheckedColor("#0078d4", "#2d7d9a") + self.records_table.setBorderRadius(8) + self.records_table.setBorderVisible(True) + self.records_table.itemSelectionChanged.connect(self.on_record_selection_changed) layout.addWidget(self.records_table) - # 统计信息 - stats_layout = QHBoxLayout() - stats_layout.addWidget(QLabel("总额:")) - self.total_amount_label = QLabel("¥ 0.00") - self.total_amount_label.setStyleSheet("font-weight: bold; font-size: 14px; color: #d32f2f;") + # 操作按钮区 + btn_layout = QHBoxLayout() + btn_layout.addStretch() + + self.view_record_btn = PushButton("查看详情") + self.view_record_btn.setFixedWidth(90) + self.view_record_btn.clicked.connect(self.on_view_record_clicked) + self.view_record_btn.setEnabled(False) + btn_layout.addWidget(self.view_record_btn) + + self.edit_record_btn = PushButton("编辑") + self.edit_record_btn.setFixedWidth(75) + self.edit_record_btn.clicked.connect(self.on_edit_record_clicked) + self.edit_record_btn.setEnabled(False) + btn_layout.addWidget(self.edit_record_btn) + + self.delete_record_btn = PushButton("删除") + self.delete_record_btn.setFixedWidth(75) + self.delete_record_btn.clicked.connect(self.on_delete_record_clicked) + self.delete_record_btn.setEnabled(False) + btn_layout.addWidget(self.delete_record_btn) + + layout.addLayout(btn_layout) + + # 统计信息卡片 + stats_card = CardWidget() + stats_layout = QHBoxLayout(stats_card) + stats_layout.setContentsMargins(20, 15, 20, 15) + stats_layout.setSpacing(30) + + stats_layout.addWidget(BodyLabel("总额:")) + self.total_amount_label = StrongBodyLabel("¥ 0.00") + self.total_amount_label.setStyleSheet("color: #d32f2f;") stats_layout.addWidget(self.total_amount_label) stats_layout.addSpacing(30) - stats_layout.addWidget(QLabel("记录数:")) - self.record_count_label = QLabel("0") - self.record_count_label.setStyleSheet("font-weight: bold; font-size: 14px;") + stats_layout.addWidget(BodyLabel("记录数:")) + self.record_count_label = StrongBodyLabel("0") stats_layout.addWidget(self.record_count_label) stats_layout.addStretch() - layout.addLayout(stats_layout) + layout.addWidget(stats_card) return widget @@ -451,61 +516,98 @@ class FinanceInterface(QWidget): layout.setContentsMargins(20, 20, 20, 20) layout.setSpacing(15) - # 搜索过滤区 - filter_layout = QHBoxLayout() + # 搜索过滤区 - 卡片样式 + filter_card = CardWidget() + filter_layout = QVBoxLayout(filter_card) + filter_layout.setContentsMargins(20, 15, 20, 15) + filter_layout.setSpacing(12) - filter_layout.addWidget(QLabel("日期范围:")) + # 第一行:日期范围 + date_layout = QHBoxLayout() + date_layout.addWidget(BodyLabel("日期范围:")) self.query_date_start = DateEdit() self.query_date_start.setDate(QDate.currentDate().addMonths(-1)) - filter_layout.addWidget(self.query_date_start) - - filter_layout.addWidget(QLabel("至")) + self.query_date_start.setMaximumWidth(150) + date_layout.addWidget(self.query_date_start) + date_layout.addWidget(BodyLabel("至")) self.query_date_end = DateEdit() self.query_date_end.setDate(QDate.currentDate()) - filter_layout.addWidget(self.query_date_end) + self.query_date_end.setMaximumWidth(150) + date_layout.addWidget(self.query_date_end) + date_layout.addSpacing(20) - filter_layout.addSpacing(20) - filter_layout.addWidget(QLabel("金额范围:")) + # 金额范围 + date_layout.addWidget(BodyLabel("金额范围:")) self.query_amount_min = DoubleSpinBox() self.query_amount_min.setRange(0, 999999999) self.query_amount_min.setPrefix("¥ ") - filter_layout.addWidget(self.query_amount_min) - - filter_layout.addWidget(QLabel("至")) + self.query_amount_min.setMaximumWidth(120) + date_layout.addWidget(self.query_amount_min) + date_layout.addWidget(BodyLabel("至")) self.query_amount_max = DoubleSpinBox() self.query_amount_max.setRange(0, 999999999) self.query_amount_max.setValue(999999999) self.query_amount_max.setPrefix("¥ ") - filter_layout.addWidget(self.query_amount_max) + self.query_amount_max.setMaximumWidth(120) + date_layout.addWidget(self.query_amount_max) + date_layout.addStretch() + filter_layout.addLayout(date_layout) - layout.addLayout(filter_layout) - - # 交易人搜索 + # 第二行:交易人搜索和查询按钮 trader_layout = QHBoxLayout() - trader_layout.addWidget(QLabel("交易人:")) + trader_layout.addWidget(BodyLabel("交易人:")) self.query_trader_edit = SearchLineEdit() self.query_trader_edit.setPlaceholderText("输入交易人名称...") + self.query_trader_edit.setMaximumWidth(250) trader_layout.addWidget(self.query_trader_edit) query_btn = PrimaryPushButton("查询") query_btn.clicked.connect(self.perform_query) trader_layout.addWidget(query_btn) + trader_layout.addStretch() + filter_layout.addLayout(trader_layout) - layout.addLayout(trader_layout) + layout.addWidget(filter_card) layout.addWidget(HorizontalSeparator()) - # 查询结果表格 - self.query_result_table = QTableWidget() - self.query_result_table.setColumnCount(6) - self.query_result_table.setHorizontalHeaderLabels(["日期", "交易人", "金额 (元)", "备注", "查看详情", ""]) - header = self.query_result_table.horizontalHeader() + # 查询结果表格 - 使用 TreeWidget 风格 + self.query_result_table = TreeWidget() + self.query_result_table.setHeaderLabels(["日期", "交易人", "金额 (元)", "备注"]) + self.query_result_table.setSelectionMode(self.query_result_table.SingleSelection) + self.query_result_table.setIndentation(0) + + # 设置 TreeWidget 样式 + header = self.query_result_table.header() if header: header.setStretchLastSection(False) - self.query_result_table.setSelectionBehavior(QTableWidget.SelectRows) - self.query_result_table.setAlternatingRowColors(True) + # 列宽自适应,可拖动调整 + header.setSectionResizeMode(0, QHeaderView.ResizeToContents) + header.setSectionResizeMode(1, QHeaderView.ResizeToContents) + header.setSectionResizeMode(2, QHeaderView.ResizeToContents) + header.setSectionResizeMode(3, QHeaderView.Stretch) + # 启用列宽可拖动 + header.setSectionsMovable(False) + header.setStretchLastSection(False) + + self.query_result_table.setCheckedColor("#0078d4", "#2d7d9a") + self.query_result_table.setBorderRadius(8) + self.query_result_table.setBorderVisible(True) + self.query_result_table.itemSelectionChanged.connect(self.on_query_result_selection_changed) layout.addWidget(self.query_result_table) + # 查询结果操作按钮 + query_btn_layout = QHBoxLayout() + query_btn_layout.addStretch() + + self.query_view_btn = PushButton("查看详情") + self.query_view_btn.setFixedWidth(90) + self.query_view_btn.clicked.connect(self.on_query_view_clicked) + self.query_view_btn.setEnabled(False) + query_btn_layout.addWidget(self.query_view_btn) + + layout.addLayout(query_btn_layout) + return widget def create_export_tab(self) -> QWidget: @@ -525,7 +627,7 @@ class FinanceInterface(QWidget): # 导出账户为压缩包 account_export_layout = QHBoxLayout() - account_export_layout.addWidget(QLabel("导出当前账户:")) + account_export_layout.addWidget(BodyLabel("导出当前账户:")) account_export_layout.addStretch() export_account_btn = PrimaryPushButton("导出为ZIP包") export_account_btn.clicked.connect(self.export_account) @@ -534,7 +636,7 @@ class FinanceInterface(QWidget): # 导出为CSV csv_export_layout = QHBoxLayout() - csv_export_layout.addWidget(QLabel("导出为Excel格式:")) + csv_export_layout.addWidget(BodyLabel("导出为Excel格式:")) csv_export_layout.addStretch() export_csv_btn = PrimaryPushButton("导出CSV") export_csv_btn.clicked.connect(self.export_csv) @@ -543,7 +645,7 @@ class FinanceInterface(QWidget): # 备份所有账户 backup_layout = QHBoxLayout() - backup_layout.addWidget(QLabel("备份所有账户:")) + backup_layout.addWidget(BodyLabel("备份所有账户:")) backup_layout.addStretch() backup_btn = PrimaryPushButton("创建备份") backup_btn.clicked.connect(self.backup_all) @@ -560,7 +662,7 @@ class FinanceInterface(QWidget): # 导入账户 account_import_layout = QHBoxLayout() - account_import_layout.addWidget(QLabel("导入账户ZIP包:")) + account_import_layout.addWidget(BodyLabel("导入账户ZIP包:")) account_import_layout.addStretch() import_account_btn = PrimaryPushButton("导入账户") import_account_btn.clicked.connect(self.import_account) @@ -607,6 +709,8 @@ class FinanceInterface(QWidget): if not account_id: return + # 重新加载所有账户,从磁盘获取最新数据 + self.finance_manager.load_all_accounts() account = self.finance_manager.get_account(account_id) if not account: return @@ -614,31 +718,16 @@ class FinanceInterface(QWidget): self.clear_records_table() for transaction in account.transactions: - row = self.records_table.rowCount() - self.records_table.insertRow(row) + # 创建树形项 + item = QTreeWidgetItem() + item.setText(0, transaction.date) + item.setText(1, transaction.trader) + item.setText(2, f"¥ {transaction.amount:.2f}") + item.setText(3, transaction.notes or "") + # 存储交易ID到第一列的 UserRole (Qt.UserRole = 32) + item.setData(0, 32, transaction.id) - self.records_table.setItem(row, 0, QTableWidgetItem(transaction.date)) - self.records_table.setItem(row, 1, QTableWidgetItem(transaction.trader)) - self.records_table.setItem(row, 2, QTableWidgetItem(f"¥ {transaction.amount:.2f}")) - self.records_table.setItem(row, 3, QTableWidgetItem(transaction.notes or "")) - - # 操作按钮 - btn_layout = QHBoxLayout() - edit_btn = PushButton("编辑") - edit_btn.clicked.connect(lambda checked, tid=transaction.id: self.edit_record(tid)) - btn_layout.addWidget(edit_btn) - - delete_btn = PushButton("删除") - delete_btn.clicked.connect(lambda checked, tid=transaction.id: self.delete_record(tid)) - btn_layout.addWidget(delete_btn) - - view_btn = PushButton("查看") - view_btn.clicked.connect(lambda checked, tid=transaction.id: self.view_record(tid)) - btn_layout.addWidget(view_btn) - - btn_widget = QWidget() - btn_widget.setLayout(btn_layout) - self.records_table.setCellWidget(row, 4, btn_widget) + self.records_table.addTopLevelItem(item) # 更新统计信息 total_amount = sum(t.amount for t in account.transactions) @@ -647,7 +736,7 @@ class FinanceInterface(QWidget): def clear_records_table(self): """清空记录表格""" - self.records_table.setRowCount(0) + self.records_table.clear() self.total_amount_label.setText("¥ 0.00") self.record_count_label.setText("0") @@ -663,13 +752,33 @@ class FinanceInterface(QWidget): """创建新记录""" account_id = self.get_current_account_id() if not account_id: - QMessageBox.warning(self, "错误", "请先创建或选择一个账户") + InfoBar.warning( + title="提示", + content="请先创建或选择一个账户", + isClosable=True, + position=InfoBarPosition.TOP, + duration=3000, + parent=self + ) return dialog = CreateTransactionDialog(self, account_id=account_id) - if dialog.exec_(): + result = dialog.exec_() + # 无论是否成功,都尝试刷新表格 + if result == QDialog.Accepted: + # 对话框正常关闭(保存) + self.refresh_records_display() + InfoBar.success( + title="成功", + content="记录已添加", + isClosable=True, + position=InfoBarPosition.TOP, + duration=2000, + parent=self + ) + else: + # 对话框被取消,也刷新以防万一 self.refresh_records_display() - InfoBar.success("记录已添加", "", duration=2000, parent=self) def edit_record(self, trans_id: str): """编辑记录""" @@ -682,9 +791,20 @@ class FinanceInterface(QWidget): return dialog = CreateTransactionDialog(self, transaction=transaction, account_id=account_id) - if dialog.exec_(): + result = dialog.exec_() + if result == QDialog.Accepted: + self.refresh_records_display() + InfoBar.success( + title="成功", + content="记录已更新", + isClosable=True, + position=InfoBarPosition.TOP, + duration=2000, + parent=self + ) + else: + # 对话框被取消,也刷新以防万一 self.refresh_records_display() - InfoBar.success("记录已更新", "", duration=2000, parent=self) def delete_record(self, trans_id: str): """删除记录""" @@ -692,16 +812,27 @@ class FinanceInterface(QWidget): if not account_id: return - reply = QMessageBox.question( - self, "确认删除", - "确定要删除这条记录吗?", - QMessageBox.Yes | QMessageBox.No - ) - - if reply == QMessageBox.Yes: + def on_delete_confirm(): self.finance_manager.delete_transaction(account_id, trans_id) self.refresh_records_display() - InfoBar.success("记录已删除", "", duration=2000, parent=self) + InfoBar.success( + title="成功", + content="记录已删除", + isClosable=True, + position=InfoBarPosition.TOP, + duration=2000, + parent=self + ) + + dialog = Dialog( + title="确认删除", + content="确定要删除这条记录吗?", + parent=self + ) + dialog.yesButton.setText("删除") + dialog.cancelButton.setText("取消") + dialog.yesButton.clicked.connect(on_delete_confirm) + dialog.exec() def view_record(self, trans_id: str): """查看记录详情""" @@ -720,9 +851,19 @@ class FinanceInterface(QWidget): """执行查询""" account_id = self.get_current_account_id() if not account_id: - QMessageBox.warning(self, "错误", "请先创建或选择一个账户") + InfoBar.warning( + title="提示", + content="请先创建或选择一个账户", + isClosable=True, + position=InfoBarPosition.TOP, + duration=3000, + parent=self + ) return + # 重新加载最新数据 + self.finance_manager.load_all_accounts() + date_start = self.query_date_start.date().toString("yyyy-MM-dd") date_end = self.query_date_end.date().toString("yyyy-MM-dd") amount_min = self.query_amount_min.value() if self.query_amount_min.value() > 0 else None @@ -736,36 +877,63 @@ class FinanceInterface(QWidget): trader=trader ) - self.query_result_table.setRowCount(0) + self.query_result_table.clear() for transaction in results: - row = self.query_result_table.rowCount() - self.query_result_table.insertRow(row) + # 创建树形项 + item = QTreeWidgetItem() + item.setText(0, transaction.date) + item.setText(1, transaction.trader) + item.setText(2, f"¥ {transaction.amount:.2f}") + item.setText(3, transaction.notes or "") + # 存储交易ID + item.setData(0, 32, transaction.id) - self.query_result_table.setItem(row, 0, QTableWidgetItem(transaction.date)) - self.query_result_table.setItem(row, 1, QTableWidgetItem(transaction.trader)) - self.query_result_table.setItem(row, 2, QTableWidgetItem(f"¥ {transaction.amount:.2f}")) - self.query_result_table.setItem(row, 3, QTableWidgetItem(transaction.notes or "")) - - view_btn = PushButton("查看详情") - view_btn.clicked.connect(lambda checked, tid=transaction.id: self.view_record(tid)) - self.query_result_table.setCellWidget(row, 4, view_btn) + self.query_result_table.addTopLevelItem(item) - InfoBar.success(f"找到 {len(results)} 条记录", "", duration=2000, parent=self) + InfoBar.success( + title="查询成功", + content=f"找到 {len(results)} 条记录", + isClosable=True, + position=InfoBarPosition.TOP, + duration=2000, + parent=self + ) def export_account(self): """导出账户为ZIP包""" account_id = self.get_current_account_id() if not account_id: - QMessageBox.warning(self, "错误", "请先选择一个账户") + InfoBar.warning( + title="提示", + content="请先选择一个账户", + isClosable=True, + position=InfoBarPosition.TOP, + duration=3000, + parent=self + ) return export_dir = QFileDialog.getExistingDirectory(self, "选择导出目录") if export_dir: if self.finance_manager.export_account_package(account_id, export_dir): - InfoBar.success("账户导出成功", "", duration=2000, parent=self) + InfoBar.success( + title="成功", + content="账户导出成功", + isClosable=True, + position=InfoBarPosition.TOP, + duration=2000, + parent=self + ) else: - QMessageBox.warning(self, "错误", "导出账户失败") + InfoBar.error( + title="失败", + content="导出账户失败", + isClosable=True, + position=InfoBarPosition.TOP, + duration=3000, + parent=self + ) def import_account(self): """导入账户ZIP包""" @@ -779,15 +947,36 @@ class FinanceInterface(QWidget): if account_id: # 重新初始化默认账户 self.init_default_account() - InfoBar.success("账户导入成功", "", duration=2000, parent=self) + InfoBar.success( + title="成功", + content="账户导入成功", + isClosable=True, + position=InfoBarPosition.TOP, + duration=2000, + parent=self + ) else: - QMessageBox.warning(self, "错误", "导入账户失败") + InfoBar.error( + title="失败", + content="导入账户失败", + isClosable=True, + position=InfoBarPosition.TOP, + duration=3000, + parent=self + ) def export_csv(self): """导出为CSV""" account_id = self.get_current_account_id() if not account_id: - QMessageBox.warning(self, "错误", "请先选择一个账户") + InfoBar.warning( + title="提示", + content="请先选择一个账户", + isClosable=True, + position=InfoBarPosition.TOP, + duration=3000, + parent=self + ) return account = self.finance_manager.get_account(account_id) @@ -802,13 +991,87 @@ class FinanceInterface(QWidget): if file_path: if self.finance_manager.export_to_csv(account_id, file_path): - InfoBar.success("已导出为CSV", "", duration=2000, parent=self) + InfoBar.success( + title="成功", + content="已导出为CSV", + isClosable=True, + position=InfoBarPosition.TOP, + duration=2000, + parent=self + ) else: - QMessageBox.warning(self, "错误", "导出CSV失败") + InfoBar.error( + title="失败", + content="导出CSV失败", + isClosable=True, + position=InfoBarPosition.TOP, + duration=3000, + parent=self + ) def backup_all(self): """备份所有账户""" if self.finance_manager.backup_all_accounts(): - InfoBar.success("备份创建成功", "已保存到 assets/Finance_Data/backups", duration=3000, parent=self) + InfoBar.success( + title="成功", + content="备份创建成功,已保存到 assets/Finance_Data/backups", + isClosable=True, + position=InfoBarPosition.TOP, + duration=3000, + parent=self + ) else: - QMessageBox.warning(self, "错误", "创建备份失败") + InfoBar.error( + title="失败", + content="创建备份失败", + isClosable=True, + position=InfoBarPosition.TOP, + duration=3000, + parent=self + ) + + def on_record_selection_changed(self): + """记录表格选择改变时""" + selected_items = self.records_table.selectedItems() + has_selection = len(selected_items) > 0 + self.view_record_btn.setEnabled(has_selection) + self.edit_record_btn.setEnabled(has_selection) + self.delete_record_btn.setEnabled(has_selection) + + def on_query_result_selection_changed(self): + """查询结果表格选择改变时""" + selected_items = self.query_result_table.selectedItems() + has_selection = len(selected_items) > 0 + self.query_view_btn.setEnabled(has_selection) + + def on_view_record_clicked(self): + """查看记录按钮点击""" + current_item = self.records_table.currentItem() + if current_item: + trans_id = current_item.data(0, 32) # Qt.UserRole = 32 + if trans_id: + self.view_record(trans_id) + + def on_edit_record_clicked(self): + """编辑记录按钮点击""" + current_item = self.records_table.currentItem() + if current_item: + trans_id = current_item.data(0, 32) # Qt.UserRole = 32 + if trans_id: + self.edit_record(trans_id) + + def on_delete_record_clicked(self): + """删除记录按钮点击""" + current_item = self.records_table.currentItem() + if current_item: + trans_id = current_item.data(0, 32) # Qt.UserRole = 32 + if trans_id: + self.delete_record(trans_id) + + def on_query_view_clicked(self): + """查询结果查看详情按钮点击""" + current_item = self.query_result_table.currentItem() + if current_item: + trans_id = current_item.data(0, 32) # Qt.UserRole = 32 + if trans_id: + self.view_record(trans_id) diff --git a/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/0446276a-731d-493d-b854-4f9a1c300f4d/data.json b/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/0446276a-731d-493d-b854-4f9a1c300f4d/data.json new file mode 100644 index 0000000..d557b50 --- /dev/null +++ b/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/0446276a-731d-493d-b854-4f9a1c300f4d/data.json @@ -0,0 +1,12 @@ +{ + "id": "0446276a-731d-493d-b854-4f9a1c300f4d", + "date": "2025-11-25", + "amount": 2.0, + "trader": "22", + "notes": "", + "invoice_path": null, + "payment_path": null, + "purchase_path": null, + "created_at": "2025-11-25T19:00:09.322631", + "updated_at": "2025-11-25T19:00:09.322638" +} \ No newline at end of file diff --git a/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/05466ef1-a604-4146-ac88-d2531afdbbca/data.json b/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/05466ef1-a604-4146-ac88-d2531afdbbca/data.json new file mode 100644 index 0000000..66c1f74 --- /dev/null +++ b/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/05466ef1-a604-4146-ac88-d2531afdbbca/data.json @@ -0,0 +1,12 @@ +{ + "id": "05466ef1-a604-4146-ac88-d2531afdbbca", + "date": "2025-11-25", + "amount": 2.0, + "trader": "123", + "notes": "", + "invoice_path": null, + "payment_path": null, + "purchase_path": null, + "created_at": "2025-11-25T19:02:23.952756", + "updated_at": "2025-11-25T19:02:23.952761" +} \ No newline at end of file diff --git a/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/0a880786-2f8e-4400-a134-86b1e433420e/data.json b/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/0a880786-2f8e-4400-a134-86b1e433420e/data.json new file mode 100644 index 0000000..4daad67 --- /dev/null +++ b/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/0a880786-2f8e-4400-a134-86b1e433420e/data.json @@ -0,0 +1,12 @@ +{ + "id": "0a880786-2f8e-4400-a134-86b1e433420e", + "date": "2025-11-25", + "amount": 2.0, + "trader": "jj", + "notes": "", + "invoice_path": "accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/0a880786-2f8e-4400-a134-86b1e433420e/invoice/截屏2025-11-25 02.51.14.png", + "payment_path": "accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/0a880786-2f8e-4400-a134-86b1e433420e/payment/截屏2025-11-18 19.18.48.png", + "purchase_path": "accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/0a880786-2f8e-4400-a134-86b1e433420e/purchase/截屏2025-11-25 02.51.10.png", + "created_at": "2025-11-25T19:05:42.373104", + "updated_at": "2025-11-25T19:06:03.343166" +} \ No newline at end of file diff --git a/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/0a880786-2f8e-4400-a134-86b1e433420e/invoice/截屏2025-11-25 02.51.14.png b/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/0a880786-2f8e-4400-a134-86b1e433420e/invoice/截屏2025-11-25 02.51.14.png new file mode 100644 index 0000000..5aa13d5 Binary files /dev/null and b/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/0a880786-2f8e-4400-a134-86b1e433420e/invoice/截屏2025-11-25 02.51.14.png differ diff --git a/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/0a880786-2f8e-4400-a134-86b1e433420e/payment/截屏2025-11-18 19.18.48.png b/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/0a880786-2f8e-4400-a134-86b1e433420e/payment/截屏2025-11-18 19.18.48.png new file mode 100644 index 0000000..39ba2b4 Binary files /dev/null and b/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/0a880786-2f8e-4400-a134-86b1e433420e/payment/截屏2025-11-18 19.18.48.png differ diff --git a/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/0a880786-2f8e-4400-a134-86b1e433420e/purchase/截屏2025-11-25 02.51.10.png b/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/0a880786-2f8e-4400-a134-86b1e433420e/purchase/截屏2025-11-25 02.51.10.png new file mode 100644 index 0000000..7befb27 Binary files /dev/null and b/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/0a880786-2f8e-4400-a134-86b1e433420e/purchase/截屏2025-11-25 02.51.10.png differ diff --git a/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/15e8df81-a66c-448b-9d55-76095b90a67a/data.json b/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/15e8df81-a66c-448b-9d55-76095b90a67a/data.json new file mode 100644 index 0000000..88fafc9 --- /dev/null +++ b/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/15e8df81-a66c-448b-9d55-76095b90a67a/data.json @@ -0,0 +1,12 @@ +{ + "id": "15e8df81-a66c-448b-9d55-76095b90a67a", + "date": "2025-11-25", + "amount": 3.0, + "trader": "1212", + "notes": "", + "invoice_path": null, + "payment_path": null, + "purchase_path": null, + "created_at": "2025-11-25T19:06:52.171539", + "updated_at": "2025-11-25T19:06:52.171544" +} \ No newline at end of file diff --git a/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/467981d6-f32d-410d-a98a-3d407b506d0e/data.json b/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/467981d6-f32d-410d-a98a-3d407b506d0e/data.json new file mode 100644 index 0000000..931b94f --- /dev/null +++ b/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/467981d6-f32d-410d-a98a-3d407b506d0e/data.json @@ -0,0 +1,12 @@ +{ + "id": "467981d6-f32d-410d-a98a-3d407b506d0e", + "date": "2025-11-25", + "amount": 5.0, + "trader": "kk", + "notes": "", + "invoice_path": null, + "payment_path": null, + "purchase_path": null, + "created_at": "2025-11-25T19:05:35.519984", + "updated_at": "2025-11-25T19:05:35.519990" +} \ No newline at end of file diff --git a/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/5964be23-0875-4d11-9487-d374f686eb35/data.json b/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/5964be23-0875-4d11-9487-d374f686eb35/data.json new file mode 100644 index 0000000..4423b36 --- /dev/null +++ b/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/5964be23-0875-4d11-9487-d374f686eb35/data.json @@ -0,0 +1,12 @@ +{ + "id": "5964be23-0875-4d11-9487-d374f686eb35", + "date": "2025-11-25", + "amount": 1.0, + "trader": "121", + "notes": "", + "invoice_path": null, + "payment_path": null, + "purchase_path": null, + "created_at": "2025-11-25T19:06:45.550932", + "updated_at": "2025-11-25T19:06:45.550937" +} \ No newline at end of file diff --git a/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/615bcca5-783c-42dd-9e8e-bff507d31157/data.json b/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/615bcca5-783c-42dd-9e8e-bff507d31157/data.json new file mode 100644 index 0000000..bfd87bf --- /dev/null +++ b/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/615bcca5-783c-42dd-9e8e-bff507d31157/data.json @@ -0,0 +1,12 @@ +{ + "id": "615bcca5-783c-42dd-9e8e-bff507d31157", + "date": "2025-11-25", + "amount": 1.0, + "trader": "1212", + "notes": "", + "invoice_path": null, + "payment_path": null, + "purchase_path": null, + "created_at": "2025-11-25T19:06:49.109079", + "updated_at": "2025-11-25T19:06:49.109085" +} \ No newline at end of file diff --git a/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/a9d005c4-447d-4989-b056-735b1560a212/data.json b/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/a9d005c4-447d-4989-b056-735b1560a212/data.json new file mode 100644 index 0000000..ad82c73 --- /dev/null +++ b/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/a9d005c4-447d-4989-b056-735b1560a212/data.json @@ -0,0 +1,12 @@ +{ + "id": "a9d005c4-447d-4989-b056-735b1560a212", + "date": "2025-11-25", + "amount": 1.0, + "trader": "lzc", + "notes": "", + "invoice_path": null, + "payment_path": null, + "purchase_path": null, + "created_at": "2025-11-25T18:59:56.514706", + "updated_at": "2025-11-25T18:59:56.514714" +} \ No newline at end of file diff --git a/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/c4a78a37-1f6f-4cec-bf32-a6fd8017e182/data.json b/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/c4a78a37-1f6f-4cec-bf32-a6fd8017e182/data.json new file mode 100644 index 0000000..70b38ae --- /dev/null +++ b/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/c4a78a37-1f6f-4cec-bf32-a6fd8017e182/data.json @@ -0,0 +1,12 @@ +{ + "id": "c4a78a37-1f6f-4cec-bf32-a6fd8017e182", + "date": "2025-11-25", + "amount": 2.0, + "trader": "1221", + "notes": "", + "invoice_path": null, + "payment_path": null, + "purchase_path": null, + "created_at": "2025-11-25T19:06:58.617525", + "updated_at": "2025-11-25T19:06:58.617531" +} \ No newline at end of file diff --git a/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/e08965c0-e44f-4f04-b877-fc5bac7271a4/data.json b/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/e08965c0-e44f-4f04-b877-fc5bac7271a4/data.json new file mode 100644 index 0000000..eb6ff22 --- /dev/null +++ b/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/e08965c0-e44f-4f04-b877-fc5bac7271a4/data.json @@ -0,0 +1,12 @@ +{ + "id": "e08965c0-e44f-4f04-b877-fc5bac7271a4", + "date": "2025-11-25", + "amount": 4.0, + "trader": "1212", + "notes": "", + "invoice_path": null, + "payment_path": null, + "purchase_path": null, + "created_at": "2025-11-25T19:06:55.304887", + "updated_at": "2025-11-25T19:06:55.304893" +} \ No newline at end of file diff --git a/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/f5eb3ed8-7640-4c50-a23e-2b30cec1ffd7/data.json b/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/f5eb3ed8-7640-4c50-a23e-2b30cec1ffd7/data.json new file mode 100644 index 0000000..318751a --- /dev/null +++ b/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/f5eb3ed8-7640-4c50-a23e-2b30cec1ffd7/data.json @@ -0,0 +1,12 @@ +{ + "id": "f5eb3ed8-7640-4c50-a23e-2b30cec1ffd7", + "date": "2024-11-25", + "amount": 100.0, + "trader": "测试商家", + "notes": "测试交易记录", + "invoice_path": null, + "payment_path": null, + "purchase_path": null, + "created_at": "2025-11-25T18:55:25.821755", + "updated_at": "2025-11-25T18:55:25.821756" +} \ No newline at end of file diff --git a/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/metadata.json b/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/metadata.json new file mode 100644 index 0000000..982a42d --- /dev/null +++ b/assets/Finance_Data/accounts/578ac4f1-9e00-4ee4-97c5-749ef846efc9/metadata.json @@ -0,0 +1,7 @@ +{ + "id": "578ac4f1-9e00-4ee4-97c5-749ef846efc9", + "name": "测试账户", + "description": "用于调试的测试账户", + "created_at": "2025-11-25T18:55:25.821559", + "updated_at": "2025-11-25T18:55:25.821560" +} \ No newline at end of file diff --git a/assets/Finance_Data/backups/backup_20251125_185525.zip b/assets/Finance_Data/backups/backup_20251125_185525.zip new file mode 100644 index 0000000..ba186be Binary files /dev/null and b/assets/Finance_Data/backups/backup_20251125_185525.zip differ diff --git a/test_finance_ui.py b/test_finance_ui.py new file mode 100644 index 0000000..f787da4 --- /dev/null +++ b/test_finance_ui.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +""" +财务界面UI测试脚本 +验证 SegmentedWidget 和 TableWidget 是否正常工作 +""" + +import sys +from PyQt5.QtWidgets import QApplication +from app.finance_interface import FinanceInterface + +def test_finance_ui(): + """测试财务界面 UI""" + app = QApplication(sys.argv) + + # 创建财务界面 + finance_interface = FinanceInterface() + + # 验证组件存在 + assert hasattr(finance_interface, 'segmented_widget'), "SegmentedWidget 未创建" + assert hasattr(finance_interface, 'stacked_widget'), "StackedWidget 未创建" + assert hasattr(finance_interface, 'records_table'), "records_table 未创建" + assert hasattr(finance_interface, 'query_result_table'), "query_result_table 未创建" + + # 验证 SegmentedWidget 项目数 + assert len(finance_interface.segmented_widget.items) == 3, "SegmentedWidget 应该有3个选项卡" + + # 验证表格列数 + assert finance_interface.records_table.columnCount() == 5, "records_table 应该有5列" + assert finance_interface.query_result_table.columnCount() == 5, "query_result_table 应该有5列" + + # 验证标签切换功能 + finance_interface.segmented_widget.setCurrentItem("query") + assert finance_interface.stacked_widget.currentIndex() == 1, "标签页切换失败" + + finance_interface.segmented_widget.setCurrentItem("export") + assert finance_interface.stacked_widget.currentIndex() == 2, "标签页切换失败" + + print("✓ 所有 UI 组件验证通过") + print("✓ SegmentedWidget 正常工作") + print("✓ TableWidget 正常工作") + print("✓ 标签页切换功能正常") + + return True + +if __name__ == "__main__": + try: + if test_finance_ui(): + print("\n✅ 财务界面 UI 测试成功!") + sys.exit(0) + except Exception as e: + print(f"\n❌ 测试失败: {e}") + import traceback + traceback.print_exc() + sys.exit(1)