mirror of
https://github.com/goldenfishs/MRobot.git
synced 2026-02-04 18:00:19 +08:00
395 lines
13 KiB
Python
395 lines
13 KiB
Python
"""
|
|
分类管理对话框
|
|
提供新增、重命名、删除分类的功能
|
|
"""
|
|
|
|
from typing import Optional
|
|
from PyQt5.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QListWidget, QListWidgetItem, QMessageBox, QScrollArea, QWidget)
|
|
from PyQt5.QtCore import Qt
|
|
from PyQt5.QtGui import QIcon, QColor, QFont
|
|
from qfluentwidgets import (BodyLabel, PushButton, PrimaryPushButton, LineEdit,
|
|
InfoBar, InfoBarPosition, SubtitleLabel, TitleLabel,
|
|
HorizontalSeparator, CardWidget, FluentIcon, StrongBodyLabel, theme, Theme)
|
|
from .tools.finance_manager import FinanceManager
|
|
|
|
|
|
class CategoryManagementDialog(QDialog):
|
|
"""分类管理对话框"""
|
|
|
|
def __init__(self, parent=None, finance_manager: Optional[FinanceManager] = None, account_id: Optional[str] = None):
|
|
super().__init__(parent)
|
|
self.finance_manager = finance_manager
|
|
self.account_id = account_id
|
|
self.setWindowTitle("分类管理")
|
|
self.setGeometry(100, 100, 650, 550)
|
|
self.setMinimumWidth(600)
|
|
self.setMinimumHeight(480)
|
|
|
|
# 设置背景色跟随主题
|
|
if theme() == Theme.DARK:
|
|
self.setStyleSheet("background-color: #232323;")
|
|
else:
|
|
self.setStyleSheet("background-color: #f7f9fc;")
|
|
|
|
self.init_ui()
|
|
|
|
def init_ui(self):
|
|
"""初始化UI"""
|
|
main_layout = QVBoxLayout()
|
|
main_layout.setContentsMargins(24, 24, 24, 24)
|
|
main_layout.setSpacing(16)
|
|
|
|
# 标题区域
|
|
title_layout = QVBoxLayout()
|
|
title_layout.setSpacing(6)
|
|
|
|
title_label = TitleLabel("分类管理")
|
|
title_layout.addWidget(title_label)
|
|
|
|
desc_label = BodyLabel("新增、编辑或删除您的交易分类")
|
|
title_layout.addWidget(desc_label)
|
|
|
|
main_layout.addLayout(title_layout)
|
|
main_layout.addWidget(HorizontalSeparator())
|
|
|
|
# 内容区域(分类列表)
|
|
content_layout = QHBoxLayout()
|
|
content_layout.setSpacing(16)
|
|
|
|
# 左侧:分类列表卡片
|
|
list_card = CardWidget()
|
|
list_card_layout = QVBoxLayout()
|
|
list_card_layout.setContentsMargins(0, 0, 0, 0)
|
|
list_card_layout.setSpacing(0)
|
|
|
|
list_label = StrongBodyLabel("现有分类")
|
|
list_label.setStyleSheet("padding: 12px 16px; border-bottom: 1px solid var(--border-color);")
|
|
list_card_layout.addWidget(list_label)
|
|
|
|
# 分类列表
|
|
self.category_list = QListWidget()
|
|
self.category_list.itemSelectionChanged.connect(self.on_category_selected)
|
|
self.category_list.setStyleSheet("""
|
|
QListWidget {
|
|
border: none;
|
|
background-color: transparent;
|
|
}
|
|
QListWidget::item {
|
|
padding: 10px 12px;
|
|
border-radius: 4px;
|
|
margin: 2px 4px;
|
|
}
|
|
QListWidget::item:hover {
|
|
background-color: rgba(0, 0, 0, 0.05);
|
|
}
|
|
QListWidget::item:selected {
|
|
background-color: var(--highlight-color);
|
|
color: var(--highlight-text-color);
|
|
font-weight: bold;
|
|
}
|
|
""")
|
|
list_card_layout.addWidget(self.category_list, 1)
|
|
list_card.setLayout(list_card_layout)
|
|
list_card.setMinimumHeight(280)
|
|
content_layout.addWidget(list_card, 1)
|
|
|
|
main_layout.addLayout(content_layout, 1)
|
|
|
|
# 加载分类
|
|
self.load_categories()
|
|
|
|
# 按钮区域
|
|
btn_layout = QHBoxLayout()
|
|
btn_layout.setSpacing(12)
|
|
|
|
# 新增按钮
|
|
add_btn = PrimaryPushButton()
|
|
add_btn.setIcon(FluentIcon.ADD)
|
|
add_btn.setText("新增分类")
|
|
add_btn.clicked.connect(self.on_add_category)
|
|
add_btn.setMinimumWidth(120)
|
|
btn_layout.addWidget(add_btn)
|
|
|
|
# 重命名按钮
|
|
self.rename_btn = PushButton()
|
|
self.rename_btn.setIcon(FluentIcon.EDIT)
|
|
self.rename_btn.setText("重命名")
|
|
self.rename_btn.clicked.connect(self.on_rename_category)
|
|
self.rename_btn.setEnabled(False)
|
|
self.rename_btn.setMinimumWidth(110)
|
|
btn_layout.addWidget(self.rename_btn)
|
|
|
|
# 删除按钮
|
|
self.delete_btn = PushButton()
|
|
self.delete_btn.setIcon(FluentIcon.DELETE)
|
|
self.delete_btn.setText("删除")
|
|
self.delete_btn.clicked.connect(self.on_delete_category)
|
|
self.delete_btn.setEnabled(False)
|
|
self.delete_btn.setMinimumWidth(110)
|
|
btn_layout.addWidget(self.delete_btn)
|
|
|
|
btn_layout.addStretch()
|
|
|
|
# 关闭按钮
|
|
close_btn = PushButton("关闭")
|
|
close_btn.clicked.connect(self.accept)
|
|
close_btn.setMinimumWidth(110)
|
|
btn_layout.addWidget(close_btn)
|
|
|
|
main_layout.addLayout(btn_layout)
|
|
self.setLayout(main_layout)
|
|
|
|
def load_categories(self):
|
|
"""加载分类列表"""
|
|
if not self.finance_manager or not self.account_id:
|
|
return
|
|
|
|
self.category_list.clear()
|
|
categories = self.finance_manager.get_categories(self.account_id)
|
|
for category in categories:
|
|
item = QListWidgetItem(category)
|
|
self.category_list.addItem(item)
|
|
|
|
def on_category_selected(self):
|
|
"""分类被选择"""
|
|
has_selection = self.category_list.currentItem() is not None
|
|
self.rename_btn.setEnabled(has_selection)
|
|
self.delete_btn.setEnabled(has_selection)
|
|
|
|
def on_add_category(self):
|
|
"""新增分类"""
|
|
# 弹出输入对话框
|
|
from PyQt5.QtWidgets import QDialog as QStdDialog
|
|
|
|
dialog = QStdDialog(self)
|
|
dialog.setWindowTitle("新增分类")
|
|
dialog.setGeometry(150, 150, 450, 220)
|
|
dialog.setStyleSheet("""
|
|
QDialog {
|
|
background-color: white;
|
|
}
|
|
""")
|
|
|
|
layout = QVBoxLayout(dialog)
|
|
layout.setContentsMargins(24, 24, 24, 24)
|
|
layout.setSpacing(16)
|
|
|
|
# 标题
|
|
title = StrongBodyLabel("创建新分类")
|
|
layout.addWidget(title)
|
|
|
|
# 说明文字
|
|
desc = BodyLabel("请输入分类名称")
|
|
desc.setStyleSheet("color: #606366;")
|
|
layout.addWidget(desc)
|
|
|
|
# 输入框
|
|
input_edit = LineEdit()
|
|
input_edit.setPlaceholderText("例如:食品、交通、娱乐、购物等")
|
|
input_edit.setMinimumHeight(40)
|
|
layout.addWidget(input_edit)
|
|
|
|
# 按钮
|
|
btn_layout = QHBoxLayout()
|
|
btn_layout.addStretch()
|
|
|
|
def on_create():
|
|
category_name = input_edit.text().strip()
|
|
if not category_name:
|
|
InfoBar.warning(
|
|
title="提示",
|
|
content="分类名称不能为空",
|
|
isClosable=True,
|
|
position=InfoBarPosition.TOP,
|
|
duration=2000,
|
|
parent=self
|
|
)
|
|
return
|
|
|
|
if self.finance_manager.add_category(self.account_id, category_name):
|
|
InfoBar.success(
|
|
title="成功",
|
|
content=f"分类 '{category_name}' 创建成功",
|
|
isClosable=True,
|
|
position=InfoBarPosition.TOP,
|
|
duration=2000,
|
|
parent=self
|
|
)
|
|
self.load_categories()
|
|
dialog.accept()
|
|
else:
|
|
InfoBar.warning(
|
|
title="提示",
|
|
content="分类已存在",
|
|
isClosable=True,
|
|
position=InfoBarPosition.TOP,
|
|
duration=2000,
|
|
parent=self
|
|
)
|
|
|
|
cancel_btn = PushButton("取消")
|
|
cancel_btn.setMinimumWidth(100)
|
|
cancel_btn.clicked.connect(dialog.reject)
|
|
btn_layout.addWidget(cancel_btn)
|
|
|
|
create_btn = PrimaryPushButton("创建")
|
|
create_btn.setMinimumWidth(100)
|
|
create_btn.clicked.connect(on_create)
|
|
btn_layout.addWidget(create_btn)
|
|
|
|
layout.addSpacing(12)
|
|
layout.addLayout(btn_layout)
|
|
|
|
# 回车快速创建
|
|
input_edit.returnPressed.connect(on_create)
|
|
|
|
dialog.exec()
|
|
|
|
def on_rename_category(self):
|
|
"""重命名分类"""
|
|
current_item = self.category_list.currentItem()
|
|
if not current_item:
|
|
return
|
|
|
|
old_name = current_item.text()
|
|
|
|
# 弹出输入对话框
|
|
from PyQt5.QtWidgets import QDialog as QStdDialog
|
|
|
|
dialog = QStdDialog(self)
|
|
dialog.setWindowTitle("重命名分类")
|
|
dialog.setGeometry(150, 150, 450, 280)
|
|
dialog.setStyleSheet("""
|
|
QDialog {
|
|
background-color: white;
|
|
}
|
|
""")
|
|
|
|
layout = QVBoxLayout(dialog)
|
|
layout.setContentsMargins(24, 24, 24, 24)
|
|
layout.setSpacing(16)
|
|
|
|
# 标题
|
|
title = StrongBodyLabel("重命名分类")
|
|
layout.addWidget(title)
|
|
|
|
# 原名称显示
|
|
old_info_layout = QHBoxLayout()
|
|
old_label = BodyLabel("原分类名:")
|
|
old_label.setMinimumWidth(80)
|
|
old_value = StrongBodyLabel(old_name)
|
|
old_value.setStyleSheet("color: #1976d2;")
|
|
old_info_layout.addWidget(old_label)
|
|
old_info_layout.addWidget(old_value)
|
|
old_info_layout.addStretch()
|
|
layout.addLayout(old_info_layout)
|
|
|
|
# 新名称输入
|
|
new_label = BodyLabel("新分类名:")
|
|
new_label.setMinimumWidth(80)
|
|
layout.addWidget(new_label)
|
|
|
|
input_edit = LineEdit()
|
|
input_edit.setText(old_name)
|
|
input_edit.selectAll()
|
|
input_edit.setMinimumHeight(40)
|
|
layout.addWidget(input_edit)
|
|
|
|
# 按钮
|
|
btn_layout = QHBoxLayout()
|
|
btn_layout.addStretch()
|
|
|
|
def on_rename():
|
|
new_name = input_edit.text().strip()
|
|
if not new_name:
|
|
InfoBar.warning(
|
|
title="提示",
|
|
content="分类名称不能为空",
|
|
isClosable=True,
|
|
position=InfoBarPosition.TOP,
|
|
duration=2000,
|
|
parent=self
|
|
)
|
|
return
|
|
|
|
if new_name == old_name:
|
|
dialog.accept()
|
|
return
|
|
|
|
if self.finance_manager.rename_category(self.account_id, old_name, new_name):
|
|
InfoBar.success(
|
|
title="成功",
|
|
content=f"分类已重命名为 '{new_name}'",
|
|
isClosable=True,
|
|
position=InfoBarPosition.TOP,
|
|
duration=2000,
|
|
parent=self
|
|
)
|
|
self.load_categories()
|
|
dialog.accept()
|
|
else:
|
|
InfoBar.warning(
|
|
title="提示",
|
|
content="重命名失败,可能分类已存在",
|
|
isClosable=True,
|
|
position=InfoBarPosition.TOP,
|
|
duration=2000,
|
|
parent=self
|
|
)
|
|
|
|
cancel_btn = PushButton("取消")
|
|
cancel_btn.setMinimumWidth(100)
|
|
cancel_btn.clicked.connect(dialog.reject)
|
|
btn_layout.addWidget(cancel_btn)
|
|
|
|
rename_btn = PrimaryPushButton("重命名")
|
|
rename_btn.setMinimumWidth(100)
|
|
rename_btn.clicked.connect(on_rename)
|
|
btn_layout.addWidget(rename_btn)
|
|
|
|
layout.addSpacing(12)
|
|
layout.addLayout(btn_layout)
|
|
|
|
# 回车快速重命名
|
|
input_edit.returnPressed.connect(on_rename)
|
|
|
|
dialog.exec()
|
|
|
|
def on_delete_category(self):
|
|
"""删除分类"""
|
|
current_item = self.category_list.currentItem()
|
|
if not current_item:
|
|
return
|
|
|
|
category_name = current_item.text()
|
|
|
|
# 确认删除
|
|
reply = QMessageBox.question(
|
|
self,
|
|
"确认删除",
|
|
f"确定要删除分类 '{category_name}' 吗?\n\n使用该分类的交易记录分类将被清空。",
|
|
QMessageBox.Yes | QMessageBox.No,
|
|
QMessageBox.No
|
|
)
|
|
|
|
if reply == QMessageBox.Yes:
|
|
if self.finance_manager.delete_category(self.account_id, category_name):
|
|
InfoBar.success(
|
|
title="成功",
|
|
content=f"分类 '{category_name}' 已删除",
|
|
isClosable=True,
|
|
position=InfoBarPosition.TOP,
|
|
duration=2000,
|
|
parent=self
|
|
)
|
|
self.load_categories()
|
|
else:
|
|
InfoBar.warning(
|
|
title="错误",
|
|
content="删除分类失败",
|
|
isClosable=True,
|
|
position=InfoBarPosition.TOP,
|
|
duration=2000,
|
|
parent=self
|
|
)
|