吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 4520|回复: 40
上一主题 下一主题
收起左侧

[原创工具] 【原创工具开源】安全密码管理器 -多种主题 + 自定义快捷键 + 银行级加密防护(优化版)

  [复制链接]
跳转到指定楼层
楼主
weiajie 发表于 2025-6-25 17:41 回帖奖励
本帖最后由 weiajie 于 2025-7-2 14:20 编辑

【原创软件】安全密码管理器- 8种主题+自定义快捷键+加密防护系统
软件介绍


各位坛友好!今天给大家分享一款自己开发的**本地密码管理工具**,完全免费,无需联网,数据100%掌握在自己手中!经过开发和优化,现在发布 ,新增了大量实用功能,界面也更加美观现代化。成品下载地址:https://wwhi.lanzouo.com/iRpYT303u57e密码:52pj




源码: 密码52pj.txt (37 Bytes, 下载次数: 25)

新增功能:数据库可选择坚果云储存
                                     优化已反馈问题




(已经使用的 用户只需要把数据库文件和key文件拷贝过来即可)



        

使用说明:
01.首先设置一个强密码要求有字母大小写以及数字“一定要记住自己设置的主密码!”


核心特色:


安全可靠

AES-GCM加密:采用业界标准的AES-GCM加密算法,每个密码都有独立的随机IV
本地存储:所有数据存储在本地SQLite数据库,不联网,不上传
主密码保护:使用SHA-256哈希验证主密码,支持密钥文件备份恢复
自动锁定:15分钟无操作自动锁定,防止他人偷看

界面友好

现代化UI:基于tkinter打造的现代化界面,支持多种主题切换
主题丰富:内置浅色、深色、蓝色、绿色、夜间等多种主题
悬浮窗模式:独特的桌面悬浮小球功能,可以缩小成小球放在桌面角落,需要时展开使用
响应式布局:界面自适应窗口大小,支持全屏使用

操作便捷

全局快捷键:支持自定义快捷键,默认Alt组合键操作更顺手
Alt+A:快速添加密码
Alt+E:编辑选中密码
Alt+D:删除密码
F2:打开加密文本管理器
F3:解锁应用(锁定状态下)
智能搜索:实时搜索过滤,快速找到需要的密码
一键操作:双击URL直接打开网站,右键菜单快速复制

功能丰富

密码生成器:内置强密码生成器,可自定义长度和字符类型

强度检测:实时检测密码强度,给出安全建议
加密文本:除了密码,还能加密存储重要文本、笔记等
数据管理:支持CSV格式导入导出,方便数据迁移
备份恢复:一键备份密钥文件,支持从备份恢复


实用细节


首次使用向导:第一次运行会引导设置主密码和生成密钥
记住登录:可选择记住主密码,下次启动自动登录
防误操作:删除等危险操作都有二次确认
错误重试:密码输入错误有重试机制,超过次数自动退出保护数据
技术实现
开发语言:Python 3.x
UI框架:tkinter + ttk现代化样式
加密库:cryptography (AES-GCM)
数据库:SQLite3
打包方式:PyInstaller单文件打包
使用场景
个人密码管理:管理各种网站、应用的登录密码
团队内部使用:小团队共享一些公用账号密码
临时密码记录:临时生成的密码、验证码等快速记录
安装使用
程序已打包成单文件exe,下载后直接运行即可:


首次运行设置主密码
开始添加你的密码信息
可以导入现有的CSV格式密码文件
建议定期备份密钥文件
开发初衷
写这个工具主要是因为:


市面上好用的密码管理器大多收费
免费的要么功能简陋,要么需要联网同步
作为程序员,更信任自己写的代码
想要一个纯本地、无网络依赖的解决方案

使用过程中遇到什么问题 ,在论坛评论区进行反馈,如果提出有价值的改进建议,我会优先开发实现! 让密码管理变得简单而安全,如果觉得软件不错,请给个赞支持一下!您的支持是我继续开发的动力!


项目特点总结:这是一个注重实用性和安全性的本地密码管理器,界面现代化,功能齐全,特别适合不想把密码存在云端的用户。代码开源,欢迎大家试用和提建议希望大家多评论交流交流!!

代码如下:
[Python] 纯文本查看 复制代码
import os
import sqlite3
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
import tkinter as tk
from tkinter import ttk, messagebox, simpledialog, filedialog
import hashlib
import base64
import random
import string
import pyperclip
import webbrowser
from datetime import datetime, timedelta
import re
import threading
import time
import re
import csv
import json

class ShortcutManager:
    def __init__(self, db_path):
        self.db_path = db_path
        self.default_shortcuts = {
            'add_password': 'Alt+Q',
            'view_password': 'Alt+W',
            'edit_password': 'Alt+E',
            'delete_password': 'Alt+D',
            'generate_password': 'Alt+G',
            'search': 'Alt+F',
            'lock_app': 'Alt+L',
            'unlock_app': 'F3',
            'import_csv': 'Alt+I',
            'export_csv': 'Alt+O',
            'reset_search': 'Alt+R',
            'refresh': 'F5',
            'copy_password': 'Alt+C',
            'toggle_floating': 'F1'
        }
        self.current_shortcuts = self.load_shortcuts()

    def load_shortcuts(self):
        """从数据库加载快捷键设置"""
        try:
            conn = sqlite3.connect(self.db_path)
            c = conn.cursor()
            c.execute('SELECT name, value FROM settings WHERE name LIKE "shortcut_%"')
            results = c.fetchall()
            conn.close()

            shortcuts = {}
            for name, value in results:
                action = name.replace('shortcut_', '')
                shortcuts[action] = value

            # 合并默认快捷键(如果数据库中没有设置)
            for action, default_key in self.default_shortcuts.items():
                if action not in shortcuts:
                    shortcuts[action] = default_key

            return shortcuts
        except Exception as e:
            return self.default_shortcuts.copy()

    def save_shortcuts(self):
        """保存快捷键设置到数据库"""
        try:
            conn = sqlite3.connect(self.db_path)
            c = conn.cursor()

            for action, shortcut in self.current_shortcuts.items():
                setting_name = f'shortcut_{action}'
                c.execute('INSERT OR REPLACE INTO settings (name, value) VALUES (?, ?)',
                         (setting_name, shortcut))

            conn.commit()
            conn.close()
            return True
        except Exception as e:
            return False

    def get_shortcut(self, action):
        """获取指定动作的快捷键"""
        return self.current_shortcuts.get(action, '')

    def set_shortcut(self, action, shortcut):
        """设置指定动作的快捷键"""
        self.current_shortcuts[action] = shortcut

    def reset_to_defaults(self):
        """重置为默认快捷键"""
        self.current_shortcuts = self.default_shortcuts.copy()

    def check_conflict(self, new_shortcut, exclude_action=None):
        """检查快捷键冲突"""
        conflicts = []
        for action, shortcut in self.current_shortcuts.items():
            if action != exclude_action and shortcut == new_shortcut:
                conflicts.append(action)
        return conflicts

    def format_shortcut_display(self, shortcut):
        """格式化快捷键显示"""
        if not shortcut:
            return ""
        return shortcut

    def format_shortcut_binding(self, shortcut):
        """格式化快捷键绑定"""
        if not shortcut:
            return ""

        # 将显示格式转换为Tkinter绑定格式
        binding = shortcut.lower()

        # 处理功能键
        if binding.startswith('f') and binding[1:].isdigit():
            return f'<{binding.upper()}>'

        # 处理组合键
        parts = binding.split('+')
        if len(parts) == 1:
            # 单个键
            return f'<{parts[0]}>'

        # 多个修饰键
        result = '<'
        for i, part in enumerate(parts[:-1]):
            if part == 'ctrl':
                result += 'Control-'
            elif part == 'alt':
                result += 'Alt-'
            elif part == 'shift':
                result += 'Shift-'

        # 添加最后的键
        result += parts[-1] + '>'
        return result

class PasswordStrengthChecker:
    """密码强度检测器"""

    @staticmethod
    def check_strength(password):
        """检查密码强度,返回强度等级和详细信息"""
        if not password:
            return 0, "密码不能为空", "#dc3545"

        score = 0
        feedback = []

        # 长度检查
        if len(password) >= 12:
            score += 25
        elif len(password) >= 8:
            score += 15
            feedback.append("建议密码长度至少12位")
        else:
            feedback.append("密码太短,至少需要8位")

        # 字符类型检查
        has_lower = bool(re.search(r'[a-z]', password))
        has_upper = bool(re.search(r'[A-Z]', password))
        has_digit = bool(re.search(r'\d', password))
        has_special = bool(re.search(r'[!@#$%^&*()_+\-=\[\]{};\':"\\|,.<>\/?]', password))

        char_types = sum([has_lower, has_upper, has_digit, has_special])
        score += char_types * 15

        if not has_lower:
            feedback.append("缺少小写字母")
        if not has_upper:
            feedback.append("缺少大写字母")
        if not has_digit:
            feedback.append("缺少数字")
        if not has_special:
            feedback.append("缺少特殊字符")

        # 重复字符检查
        if len(set(password)) < len(password) * 0.7:
            score -= 10
            feedback.append("重复字符过多")

        # 常见模式检查
        common_patterns = ['123', 'abc', 'qwe', 'password', '000', '111']
        for pattern in common_patterns:
            if pattern.lower() in password.lower():
                score -= 15
                feedback.append(f"包含常见模式: {pattern}")
                break

        # 确定强度等级和颜色
        if score >= 80:
            level = "很强"
            color = "#28a745"
        elif score >= 60:
            level = "强"
            color = "#17a2b8"
        elif score >= 40:
            level = "中等"
            color = "#ffc107"
        elif score >= 20:
            level = "弱"
            color = "#fd7e14"
        else:
            level = "很弱"
            color = "#dc3545"

        return score, level, color, feedback

class AutoLockManager:
    """自动锁定管理器"""

    def __init__(self, timeout_minutes=1500):
        self.timeout_minutes = timeout_minutes
        self.last_activity = time.time()
        self.is_locked = False
        self.lock_callback = None
        self.timer_thread = None
        self.running = False

    def start_monitoring(self, lock_callback):
        """开始监控用户活动"""
        self.lock_callback = lock_callback
        self.running = True
        self.timer_thread = threading.Thread(target=self._monitor_activity, daemon=True)
        self.timer_thread.start()

    def stop_monitoring(self):
        """停止监控"""
        self.running = False

    def update_activity(self):
        """更新最后活动时间"""
        self.last_activity = time.time()
        self.is_locked = False

    def _monitor_activity(self):
        """监控活动的后台线程"""
        while self.running:
            if not self.is_locked:
                idle_time = time.time() - self.last_activity
                if idle_time > self.timeout_minutes * 60:
                    self.is_locked = True
                    if self.lock_callback:
                        self.lock_callback()
            time.sleep(30)  # 每30秒检查一次

class PasswordManager:
    def __init__(self, db_path='passwords.db', key_path=None):
        self.db_path = db_path
        # 设置默认密钥文件路径到运行目录
        if key_path is None:
            self.key_path = os.path.join(os.getcwd(), 'secret.key')
        else:
            self.key_path = key_path
        # 设置配置文件路径到运行目录
        self.config_path = os.path.join(os.getcwd(), 'config.dat')
        self.key = None
        self.salt = None
        self.iterations = 100000
        self.master_password_hash = None
        self.is_locked = False
        self.auto_lock_manager = AutoLockManager()
        self.strength_checker = PasswordStrengthChecker()
        self.shortcut_manager = None  # 延迟初始化
        self.setup()

    def setup(self):
        """初始化密码管理器"""
        # 检查数据库和密钥文件状态
        db_exists = os.path.exists(self.db_path)
        key_exists = os.path.exists(self.key_path)

        # 首次启动检测 - 数据库文件不存在,直接生成密钥
        if not db_exists:
            return self.first_time_generate_key()

        # 数据库存在的情况下,确保数据库结构完整
        self.create_database()

        # 清理数据库中过时的加密参数
        try:
            conn = sqlite3.connect(self.db_path)
            c = conn.cursor()
            c.execute('DELETE FROM settings WHERE name IN (?, ?)', ('encryption_salt', 'encryption_iterations'))
            conn.commit()
            conn.close()
        except Exception as e:
            pass

        # 检查密钥文件是否存在
        if not key_exists:
            self.handle_missing_key_file()
            return False  # 返回False表示初始化未完成

        # 检查是否有保存的自动登录配置
        if self.load_saved_config():
            # 初始化快捷键管理器
            self.shortcut_manager = ShortcutManager(self.db_path)
            messagebox.showinfo('欢迎回来', '自动登录成功!')
            return True
        else:
            # 数据库和密钥文件都存在,进行正常的主密码验证
            self.verify_master_password()
            # 初始化快捷键管理器
            self.shortcut_manager = ShortcutManager(self.db_path)
            return True

    def first_time_generate_key(self):
        """首次启动直接生成密钥"""
        try:
            # 先创建数据库
            self.create_database()

            # 设置主密码
            master_password = self.set_master_password()
            if not master_password:
                exit()

            # 生成密钥文件
            self.generate_key()

            # 使用主密码哈希派生密钥
            self.load_key_with_hash(self.master_password_hash)

            # 创建密钥验证数据
            self.create_key_validation()

            # 初始化快捷键管理器
            self.shortcut_manager = ShortcutManager(self.db_path)

            # 显示成功消息
            messagebox.showinfo('设置完成',
                              '&#127881; 密码管理器初始化完成!\n\n'
                              '&#9989; 数据库已创建\n'
                              '&#9989; 密钥文件已生成\n'
                              '&#9989; 使用军用级AES-GCM加密保护\n\n'
                              '现在您可以开始添加密码了!')

            return True

        except Exception as e:
            messagebox.showerror('设置失败', f'初始化失败:{str(e)}\n\n程序将退出。')
            exit()

    def first_time_setup(self):
        """首次启动设置流程"""
        # 显示欢迎对话框
        welcome_result = self.show_welcome_dialog()
        if not welcome_result:
            exit()

        # 设置主密码
        master_password = self.show_first_time_password_dialog()
        if not master_password:
            exit()

        try:

            # 创建数据库
            self.create_database()

            # 生成密钥文件
            self.generate_key()

            # 计算并保存主密码哈希
            self.master_password_hash = hashlib.sha256(master_password.encode()).hexdigest()

            # 保存主密码哈希到数据库
            conn = sqlite3.connect(self.db_path)
            c = conn.cursor()
            c.execute('INSERT OR REPLACE INTO settings (name, value) VALUES (?, ?)',
                      ('master_password_hash', self.master_password_hash))
            conn.commit()
            conn.close()

            # 使用主密码哈希派生密钥
            self.load_key_with_hash(self.master_password_hash)

            # 创建密钥验证数据
            self.create_key_validation()

            # 保存自动登录配置
            self.save_config(master_password)

            # 初始化快捷键管理器
            self.shortcut_manager = ShortcutManager(self.db_path)

            # 显示成功消息
            messagebox.showinfo('设置完成',
                              '&#127881; 密码管理器设置完成!\n\n'
                              '&#9989; 数据库已创建\n'
                              '&#9989; 密钥文件已生成\n'
                              '&#9989; 使用军用级AES-GCM加密保护\n\n'
                              '&#9888;&#65039; 请妥善保管您的主密码,遗失后无法恢复!')

            return True

        except Exception as e:
            messagebox.showerror('设置失败', f'初始化失败:{str(e)}\n\n程序将退出。')
            exit()

    def show_welcome_dialog(self):
        """显示首次启动欢迎对话框"""
        dialog = tk.Toplevel()
        dialog.title('欢迎使用安全密码管理器')
        dialog.geometry('500x400')
        dialog.resizable(False, False)
        dialog.transient()
        dialog.grab_set()
        dialog.configure(bg='#f8f9fa')

        # 居中显示
        dialog.update_idletasks()
        width = dialog.winfo_width()
        height = dialog.winfo_height()
        x = (dialog.winfo_screenwidth() // 2) - (width // 2)
        y = (dialog.winfo_screenheight() // 2) - (height // 2)
        dialog.geometry('{}x{}+{}+{}'.format(width, height, x, y))

        # 主框架
        main_frame = ttk.Frame(dialog, padding='30')
        main_frame.pack(fill=tk.BOTH, expand=True)

        # 标题
        title_label = ttk.Label(main_frame,
                               text='&#128272; 欢迎使用安全密码管理器 v1.0',
                               font=('Microsoft YaHei UI', 18, 'bold'),
                               foreground='#2c3e50')
        title_label.pack(pady=(0, 20))

        # 欢迎信息
        welcome_text = """感谢您选择安全密码管理器!

这是您第一次使用本软件,我们将为您进行初始设置。

&#128737;&#65039; 安全特性:
&#8226; 采用军用级 AES-GCM 加密算法
&#8226; 本地存储,数据完全掌控
&#8226; 密钥派生使用 PBKDF2-HMAC-SHA256

&#127912; 功能亮点:
&#8226; 8种精美主题,个性化体验
&#8226; 自定义快捷键,提升效率
&#8226; 智能密码强度检测
&#8226; 自动锁定保护隐私

接下来,您需要设置一个主密码来保护您的数据。"""

        info_label = ttk.Label(main_frame,
                              text=welcome_text,
                              font=('Microsoft YaHei UI', 10),
                              justify=tk.LEFT,
                              foreground='#34495e')
        info_label.pack(pady=(0, 30))

        result = {'continue': False}

        def continue_setup():
            result['continue'] = True
            dialog.destroy()

        def cancel_setup():
            if messagebox.askyesno('确认退出', '确定要退出设置吗?\n\n退出后程序将关闭。'):
                result['continue'] = False
                dialog.destroy()

        # 按钮框架
        btn_frame = ttk.Frame(main_frame)
        btn_frame.pack(fill=tk.X, pady=(20, 0))

        ttk.Button(btn_frame, text='&#128640; 开始设置', command=continue_setup, width=20).pack(side=tk.RIGHT, padx=(10, 0))
        ttk.Button(btn_frame, text='&#10060; 退出', command=cancel_setup, width=15).pack(side=tk.RIGHT)

        # 设置关闭事件
        dialog.protocol("WM_DELETE_WINDOW", cancel_setup)

        dialog.wait_window()
        return result['continue']

    def show_first_time_password_dialog(self):
        """显示首次设置主密码对话框(简化版)"""
        dialog = tk.Toplevel()
        dialog.title('首次使用 - 设置主密码')
        dialog.geometry('450x350')
        dialog.resizable(False, False)
        dialog.transient()
        dialog.grab_set()
        dialog.configure(bg='#f8f9fa')

        # 居中显示
        dialog.update_idletasks()
        width = dialog.winfo_width()
        height = dialog.winfo_height()
        x = (dialog.winfo_screenwidth() // 2) - (width // 2)
        y = (dialog.winfo_screenheight() // 2) - (height // 2)
        dialog.geometry('{}x{}+{}+{}'.format(width, height, x, y))

        # 主框架
        main_frame = ttk.Frame(dialog, padding='30')
        main_frame.pack(fill=tk.BOTH, expand=True)

        # 标题
        title_label = ttk.Label(main_frame,
                               text='&#128273; 设置主密码',
                               font=('Microsoft YaHei UI', 16, 'bold'),
                               foreground='#2c3e50')
        title_label.pack(pady=(0, 20))

        # 说明文字
        info_text = """主密码是保护您所有数据的关键,请务必:

&#8226; 使用至少8位字符
&#8226; 包含大小写字母、数字和特殊字符
&#8226; 避免使用个人信息(姓名、生日等)

&#9888;&#65039; 重要提醒:主密码遗失后无法恢复,请务必牢记!"""

        ttk.Label(main_frame,
                 text=info_text,
                 font=('Microsoft YaHei UI', 10),
                 justify=tk.LEFT,
                 foreground='#7f8c8d').pack(pady=(0, 20))

        # 密码输入区域
        password_frame = ttk.LabelFrame(main_frame, text='密码设置', padding='15')
        password_frame.pack(fill=tk.X, pady=(0, 20))

        # 主密码输入
        ttk.Label(password_frame, text='主密码:', font=('Microsoft YaHei UI', 10, 'bold')).pack(anchor=tk.W, pady=(0, 5))
        password_var = tk.StringVar()
        password_entry = ttk.Entry(password_frame, textvariable=password_var, show='*',
                                  font=('Microsoft YaHei UI', 12), width=30)
        password_entry.pack(fill=tk.X, pady=(0, 10))

        # 确认密码输入
        ttk.Label(password_frame, text='确认密码:', font=('Microsoft YaHei UI', 10, 'bold')).pack(anchor=tk.W, pady=(0, 5))
        confirm_var = tk.StringVar()
        confirm_entry = ttk.Entry(password_frame, textvariable=confirm_var, show='*',
                                 font=('Microsoft YaHei UI', 12), width=30)
        confirm_entry.pack(fill=tk.X, pady=(0, 10))

        result = {'password': None}

        def validate_and_confirm():
            password = password_var.get()
            confirm = confirm_var.get()

            # 验证密码长度
            if len(password) < 8:
                messagebox.showerror('密码不符合要求', '密码长度至少需要8位字符!')
                password_entry.focus()
                return

            # 验证密码一致性
            if password != confirm:
                messagebox.showerror('密码不一致', '两次输入的密码不一致,请重新输入!')
                confirm_entry.delete(0, tk.END)
                confirm_entry.focus()
                return

            result['password'] = password
            dialog.destroy()

        def cancel_setup():
            if messagebox.askyesno('确认取消', '确定要取消设置吗?\n\n取消后程序将退出。'):
                result['password'] = None
                dialog.destroy()

        # 按钮框架
        btn_frame = ttk.Frame(main_frame)
        btn_frame.pack(fill=tk.X, pady=(20, 0))

        ttk.Button(btn_frame, text='&#9989; 确认设置', command=validate_and_confirm, width=20).pack(side=tk.RIGHT, padx=(10, 0))
        ttk.Button(btn_frame, text='&#10060; 取消', command=cancel_setup, width=15).pack(side=tk.RIGHT)

        # 绑定回车键
        password_entry.bind('<Return>', lambda e: confirm_entry.focus())
        confirm_entry.bind('<Return>', lambda e: validate_and_confirm())
        password_entry.focus()

        # 设置关闭事件
        dialog.protocol("WM_DELETE_WINDOW", cancel_setup)

        dialog.wait_window()
        return result['password']

    def save_config(self, master_password):
        """保存配置到运行目录(加密存储)"""
        try:
            # 生成配置数据
            config_data = {
                'master_password_hash': hashlib.sha256(master_password.encode()).hexdigest(),
                'salt': base64.b64encode(self.salt).decode(),
                'iterations': self.iterations,
                'created_time': datetime.now().isoformat()
            }

            # 使用机器ID加密配置数据
            machine_id = self._get_machine_id()
            config_json = json.dumps(config_data)
            encrypted_config = self._encrypt_config(config_json, machine_id)

            with open(self.config_path, 'wb') as f:
                f.write(encrypted_config)

            messagebox.showinfo('信息', '配置已安全保存到运行目录,下次启动将自动登录')
        except Exception as e:
            messagebox.showerror('错误', f'保存配置失败: {str(e)}')

    def _encrypt_config(self, data, password):
        """加密配置数据"""
        # 生成随机盐和IV
        salt = os.urandom(16)
        iv = os.urandom(12)

        # 从密码派生密钥
        kdf = PBKDF2HMAC(
            algorithm=hashes.SHA256(),
            length=32,
            salt=salt,
            iterations=100000,
        )
        key = kdf.derive(password.encode())

        # 使用AES-GCM加密
        cipher = Cipher(algorithms.AES(key), modes.GCM(iv))
        encryptor = cipher.encryptor()
        ciphertext = encryptor.update(data.encode()) + encryptor.finalize()

        # 返回 salt + iv + tag + ciphertext
        return salt + iv + encryptor.tag + ciphertext

    def _decrypt_config(self, encrypted_data, password):
        """解密配置数据"""
        # 分离各部分
        salt = encrypted_data[:16]
        iv = encrypted_data[16:28]
        tag = encrypted_data[28:44]
        ciphertext = encrypted_data[44:]

        # 从密码派生密钥
        kdf = PBKDF2HMAC(
            algorithm=hashes.SHA256(),
            length=32,
            salt=salt,
            iterations=100000,
        )
        key = kdf.derive(password.encode())

        # 解密
        cipher = Cipher(algorithms.AES(key), modes.GCM(iv, tag))
        decryptor = cipher.decryptor()
        return (decryptor.update(ciphertext) + decryptor.finalize()).decode()

    def load_saved_config(self):
        """加载保存的配置(解密)"""
        try:
            if not os.path.exists(self.config_path):
                return False

            # 尝试使用机器特征作为密码解密
            machine_id = self._get_machine_id()

            with open(self.config_path, 'rb') as f:
                encrypted_data = f.read()

            config_json = self._decrypt_config(encrypted_data, machine_id)
            config_data = json.loads(config_json)

            self.master_password_hash = config_data['master_password_hash']

            # 从密钥文件加载盐值和迭代次数
            if not os.path.exists(self.key_path):
                return False

            with open(self.key_path, 'rb') as f:
                self.salt = base64.b64decode(f.readline().strip())
                self.iterations = int(f.readline().strip())

            # 使用主密码哈希派生密钥(与正常登录流程一致)
            kdf = PBKDF2HMAC(
                algorithm=hashes.SHA256(),
                length=32,
                salt=self.salt,
                iterations=self.iterations,
            )
            self.key = kdf.derive(self.master_password_hash.encode())

            # 验证密钥是否与数据库匹配
            if not self.validate_key():
                # 密钥验证失败,删除配置文件强制重新验证
                try:
                    os.remove(self.config_path)
                except:
                    pass
                return False

            return True
        except Exception as e:
            # 如果配置文件损坏,删除它
            try:
                os.remove(self.config_path)
            except:
                pass
            return False

    def _get_machine_id(self):
        """获取机器特征ID用于配置加密"""
        try:
            # 使用多个系统特征生成唯一ID
            import platform
            machine_info = f"{platform.node()}{platform.machine()}{platform.processor()}"
            return hashlib.sha256(machine_info.encode()).hexdigest()[:32]
        except:
            # 如果获取失败,使用固定字符串
            return "default_machine_id_for_config"

    def handle_missing_key_file(self):
        """处理密钥文件丢失的情况"""
        # 显示警告对话框
        warning_msg = """
&#9888;&#65039; 检测到密钥文件丢失!

数据库文件存在但密钥文件 (secret.key) 丢失。
这可能是由于以下原因:
&#8226; 密钥文件被意外删除
&#8226; 程序目录被移动
&#8226; 文件权限问题

请选择恢复方式:
        """

        # 创建恢复对话框
        recovery_dialog = tk.Toplevel()
        recovery_dialog.title('&#128273; 密钥文件恢复')
        recovery_dialog.geometry('500x400')
        recovery_dialog.resizable(False, False)
        recovery_dialog.grab_set()
        recovery_dialog.configure(bg='#fff3cd')

        # 居中显示
        recovery_dialog.update_idletasks()
        width = recovery_dialog.winfo_width()
        height = recovery_dialog.winfo_height()
        x = (recovery_dialog.winfo_screenwidth() // 2) - (width // 2)
        y = (recovery_dialog.winfo_screenheight() // 2) - (height // 2)
        recovery_dialog.geometry('{}x{}+{}+{}'.format(width, height, x, y))

        frame = ttk.Frame(recovery_dialog, padding='20')
        frame.pack(fill=tk.BOTH, expand=True)

        # 警告图标和标题
        ttk.Label(frame, text='&#9888;&#65039;', font=('Microsoft YaHei UI', 48), foreground='#856404').pack(pady=(0, 10))
        ttk.Label(frame, text='密钥文件丢失', font=('Microsoft YaHei UI', 16, 'bold'), foreground='#856404').pack(pady=5)

        # 说明文本
        text_widget = tk.Text(frame, wrap=tk.WORD, height=8, width=50, font=('Microsoft YaHei UI', 10))
        text_widget.insert(tk.END, warning_msg)
        text_widget.config(state=tk.DISABLED, bg='#fff3cd', relief=tk.FLAT)
        text_widget.pack(pady=10, fill=tk.BOTH, expand=True)

        # 按钮框架
        btn_frame = ttk.Frame(frame)
        btn_frame.pack(pady=20)

        def try_master_password_recovery():
            """尝试使用主密码恢复"""
            recovery_dialog.destroy()
            self.master_password_recovery()

        def restore_from_backup():
            """从备份恢复"""
            file_path = filedialog.askopenfilename(
                title="选择密钥文件备份",
                filetypes=[("密钥文件", "*.key"), ("所有文件", "*.*")]
            )
            if file_path:
                try:
                    import shutil
                    shutil.copy2(file_path, self.key_path)

                    # 验证恢复的密钥文件是否有效
                    if self.verify_restored_key():
                        messagebox.showinfo("恢复成功", "密钥文件已恢复!程序将重新启动。")
                        recovery_dialog.destroy()
                        self.setup()  # 重新初始化
                    else:
                        messagebox.showerror("恢复失败", "恢复的密钥文件与数据库不匹配!\n请确认选择了正确的密钥文件备份。")
                        # 删除无效的密钥文件
                        try:
                            os.remove(self.key_path)
                        except:
                            pass
                except Exception as e:
                    messagebox.showerror("恢复失败", f"无法恢复密钥文件:{str(e)}")

        def create_new_database():
            """创建新数据库"""
            confirm = messagebox.askyesno("确认操作",
                "这将创建一个全新的数据库,原有数据将无法访问。\n确定要继续吗?")
            if confirm:
                try:
                    # 备份原数据库
                    backup_name = f"passwords_backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}.db"
                    import shutil
                    shutil.copy2(self.db_path, backup_name)

                    # 删除原数据库和配置
                    os.remove(self.db_path)
                    if os.path.exists(self.config_path):
                        os.remove(self.config_path)

                    messagebox.showinfo("备份完成", f"原数据库已备份为:{backup_name}")
                    recovery_dialog.destroy()
                    self.setup()  # 重新初始化
                except Exception as e:
                    messagebox.showerror("操作失败", f"无法创建新数据库:{str(e)}")

        def close_recovery_dialog():
            """关闭恢复对话框并退出程序"""
            recovery_dialog.destroy()
            # 显示提示信息并退出程序
            messagebox.showwarning("程序退出", "密钥文件丢失,无法访问现有数据。\n程序将退出。")
            exit()

        # 恢复选项按钮
        ttk.Button(btn_frame, text='&#128273; 使用主密码恢复', command=try_master_password_recovery, width=20).pack(pady=5)
        ttk.Button(btn_frame, text='&#128193; 从备份恢复', command=restore_from_backup, width=20).pack(pady=5)
        ttk.Button(btn_frame, text='&#127381; 创建新数据库', command=create_new_database, width=20).pack(pady=5)
        ttk.Button(btn_frame, text='&#10060; 退出程序', command=close_recovery_dialog, width=20).pack(pady=5)

        # 设置对话框关闭事件
        recovery_dialog.protocol("WM_DELETE_WINDOW", close_recovery_dialog)

    def master_password_recovery(self):
        """使用主密码尝试恢复密钥文件"""
        # 从数据库获取主密码哈希
        try:
            conn = sqlite3.connect(self.db_path)
            c = conn.cursor()
            c.execute('SELECT value FROM settings WHERE name = ?', ('master_password_hash',))
            result = c.fetchone()
            conn.close()

            if not result:
                messagebox.showerror('恢复失败', '数据库中未找到主密码信息!')
                return

            stored_hash = result[0]

            # 创建密码输入对话框
            password_dialog = tk.Toplevel()
            password_dialog.title('&#128273; 主密码恢复')
            password_dialog.geometry('400x250')
            password_dialog.resizable(False, False)
            password_dialog.grab_set()
            password_dialog.configure(bg='#f8f9fa')

            # 居中显示
            password_dialog.update_idletasks()
            width = password_dialog.winfo_width()
            height = password_dialog.winfo_height()
            x = (password_dialog.winfo_screenwidth() // 2) - (width // 2)
            y = (password_dialog.winfo_screenheight() // 2) - (height // 2)
            password_dialog.geometry('{}x{}+{}+{}'.format(width, height, x, y))

            frame = ttk.Frame(password_dialog, padding='30')
            frame.pack(fill=tk.BOTH, expand=True)

            ttk.Label(frame, text='&#128273;', font=('Microsoft YaHei UI', 32)).pack(pady=(0, 10))
            ttk.Label(frame, text='密钥文件恢复', font=('Microsoft YaHei UI', 14, 'bold')).pack(pady=5)
            ttk.Label(frame, text='请输入主密码以重新生成密钥文件', font=('Microsoft YaHei UI', 10)).pack(pady=5)

            password_var = tk.StringVar()
            password_entry = ttk.Entry(frame, textvariable=password_var, show='*', width=30, font=('Microsoft YaHei UI', 10))
            password_entry.pack(pady=10)
            password_entry.focus()

            def attempt_recovery():
                password = password_var.get()
                if password and hashlib.sha256(password.encode()).hexdigest() == stored_hash:
                    try:
                        # 重新生成密钥文件
                        self.salt = os.urandom(16)
                        self.generate_key()
                        # 使用主密码哈希派生密钥
                        self.load_key_with_hash(stored_hash)
                        # 重新创建密钥验证数据
                        self.create_key_validation()
                        self.save_config(password)

                        messagebox.showinfo('恢复成功', '密钥文件已成功恢复!')
                        password_dialog.destroy()
                    except Exception as e:
                        messagebox.showerror('恢复失败', f'无法恢复密钥文件:{str(e)}')
                else:
                    messagebox.showerror('密码错误', '主密码不正确,请重试!')
                    password_var.set('')
                    password_entry.focus()

            password_entry.bind('<Return>', lambda e: attempt_recovery())

            btn_frame = ttk.Frame(frame)
            btn_frame.pack(pady=15)

            ttk.Button(btn_frame, text='&#128275; 恢复', command=attempt_recovery, width=15).pack(side=tk.LEFT, padx=5)
            ttk.Button(btn_frame, text='&#10060; 取消', command=password_dialog.destroy, width=15).pack(side=tk.LEFT, padx=5)

        except Exception as e:
            messagebox.showerror('错误', f'无法访问数据库:{str(e)}')

    def generate_key(self):
        # 生成盐值
        self.salt = os.urandom(16)
        # 确保密钥目录存在
        key_dir = os.path.dirname(self.key_path)
        if not os.path.exists(key_dir):
            os.makedirs(key_dir, exist_ok=True)

        # 保存盐值和迭代次数到密钥文件
        with open(self.key_path, 'wb') as f:
            f.write(base64.b64encode(self.salt) + b'\n')
            f.write(str(self.iterations).encode() + b'\n')
        messagebox.showinfo('信息', '加密参数已初始化')

        # 生成并保存密钥验证数据
        self.create_key_validation()

    def load_key(self, master_password):
        # 从密钥文件加载盐值和迭代次数
        try:
            if not os.path.exists(self.key_path):
                messagebox.showerror('错误', f'密钥文件不存在: {self.key_path}')
                return False
            
            with open(self.key_path, 'rb') as f:
                self.salt = base64.b64decode(f.readline().strip())
                self.iterations = int(f.readline().strip())

            # 使用PBKDF2HMAC从主密码派生密钥
            kdf = PBKDF2HMAC(
                algorithm=hashes.SHA256(),
                length=32,
                salt=self.salt,
                iterations=self.iterations,

            )
            self.key = kdf.derive(master_password.encode())
            return True
        except Exception as e:
            messagebox.showerror('错误', f'加载加密参数失败: {str(e)}')
            return False

    def load_key_with_hash(self, master_password_hash):
        """使用主密码哈希派生密钥"""
        try:
            if not os.path.exists(self.key_path):
                messagebox.showerror('错误', f'密钥文件不存在: {self.key_path}')
                return False

            with open(self.key_path, 'rb') as f:
                self.salt = base64.b64decode(f.readline().strip())
                self.iterations = int(f.readline().strip())

            # 使用PBKDF2HMAC从主密码哈希派生密钥
            kdf = PBKDF2HMAC(
                algorithm=hashes.SHA256(),
                length=32,
                salt=self.salt,
                iterations=self.iterations,
            )
            self.key = kdf.derive(master_password_hash.encode())
            return True
        except Exception as e:
            messagebox.showerror('错误', f'加载加密参数失败: {str(e)}')
            return False

    def create_key_validation(self):
        """创建密钥验证数据"""
        try:
            if not self.key:
                return False

            # 生成密钥哈希
            key_hash = hashlib.sha256(self.key).hexdigest()

            # 创建验证数据(使用已知字符串加密)
            validation_text = "PASSWORD_MANAGER_KEY_VALIDATION_2025"
            validation_encrypted = self.encrypt_password(validation_text)

            if validation_encrypted:
                conn = sqlite3.connect(self.db_path)
                c = conn.cursor()
                # 清除旧的验证数据
                c.execute('DELETE FROM key_validation')
                # 插入新的验证数据
                c.execute('INSERT INTO key_validation (key_hash, validation_data) VALUES (?, ?)',
                          (key_hash, validation_encrypted))
                conn.commit()
                conn.close()
                return True
        except Exception as e:
            return False

    def validate_key(self):
        """验证当前密钥是否与数据库匹配"""
        try:
            if not self.key:
                return False

            conn = sqlite3.connect(self.db_path)
            c = conn.cursor()
            c.execute('SELECT key_hash, validation_data FROM key_validation ORDER BY created_at DESC LIMIT 1')
            result = c.fetchone()

            if not result:
                # 如果没有验证数据,尝试验证现有密码
                c.execute('SELECT encrypted_password FROM passwords LIMIT 1')
                password_result = c.fetchone()
                conn.close()

                if password_result:
                    # 尝试解密现有密码来验证密钥
                    try:
                        decrypted = self.decrypt_password(password_result[0])
                        if decrypted:  # 如果能成功解密,说明密钥正确
                            # 创建验证数据
                            return self.create_key_validation()
                        else:
                            return False
                    except:
                        return False
                else:
                    # 没有密码数据,创建验证数据
                    return self.create_key_validation()
            else:
                conn.close()
                stored_key_hash, validation_encrypted = result
                current_key_hash = hashlib.sha256(self.key).hexdigest()

                # 检查密钥哈希是否匹配
                if stored_key_hash != current_key_hash:
                    return False

                # 尝试解密验证数据
                validation_text = self.decrypt_password(validation_encrypted)
                expected_text = "PASSWORD_MANAGER_KEY_VALIDATION_2025"

                return validation_text == expected_text

        except Exception as e:
            return False

    def verify_restored_key(self):
        """验证恢复的密钥文件是否与数据库匹配"""
        try:
            # 获取主密码哈希
            conn = sqlite3.connect(self.db_path)
            c = conn.cursor()
            c.execute('SELECT value FROM settings WHERE name = ?', ('master_password_hash',))
            result = c.fetchone()
            conn.close()

            if not result:
                return False

            master_password_hash = result[0]

            # 尝试使用主密码哈希作为密码来加载密钥
            temp_password = master_password_hash[:32]  # 使用哈希的前32位作为临时密码

            # 保存当前状态
            old_key = self.key
            old_salt = self.salt
            old_iterations = self.iterations

            try:
                # 尝试加载恢复的密钥文件
                with open(self.key_path, 'rb') as f:
                    self.salt = base64.b64decode(f.readline().strip())
                    self.iterations = int(f.readline().strip())

                # 使用多种可能的密码尝试验证
                for attempt_password in [master_password_hash, temp_password]:
                    try:
                        kdf = PBKDF2HMAC(
                            algorithm=hashes.SHA256(),
                            length=32,
                            salt=self.salt,
                            iterations=self.iterations,
                        )
                        self.key = kdf.derive(attempt_password.encode())

                        # 验证密钥
                        if self.validate_key():
                            return True
                    except:
                        continue

                return False

            except Exception:
                return False
            finally:
                # 恢复原状态
                self.key = old_key
                self.salt = old_salt
                self.iterations = old_iterations

        except Exception as e:
            return False

    def create_database(self):
        # 创建SQLite数据库和表
        try:
            conn = sqlite3.connect(self.db_path)
            c = conn.cursor()
            # 创建密码表
            c.execute('''CREATE TABLE IF NOT EXISTS passwords
                         (id INTEGER PRIMARY KEY AUTOINCREMENT,
                         service TEXT NOT NULL,
                         url TEXT,
                         notes TEXT,
                         category TEXT DEFAULT '默认',
                         expires_at TIMESTAMP,
                         created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                         updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)''')

            # 创建密码历史表
            c.execute('''CREATE TABLE IF NOT EXISTS password_history
                         (id INTEGER PRIMARY KEY AUTOINCREMENT,
                         password_id INTEGER,
                         encrypted_password TEXT NOT NULL,
                         changed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                         FOREIGN KEY (password_id) REFERENCES passwords (id))''')

            # 创建分类表
            c.execute('''CREATE TABLE IF NOT EXISTS categories
                         (id INTEGER PRIMARY KEY AUTOINCREMENT,
                         name TEXT UNIQUE NOT NULL,
                         color TEXT DEFAULT '#007bff',
                         created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)''')

            # 创建设置表
            c.execute('''CREATE TABLE IF NOT EXISTS settings
                         (name TEXT PRIMARY KEY,
                         value TEXT NOT NULL)''')

            # 创建密钥验证表
            c.execute('''CREATE TABLE IF NOT EXISTS key_validation
                         (id INTEGER PRIMARY KEY,
                         key_hash TEXT NOT NULL,
                         validation_data TEXT NOT NULL,
                         created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)''')

            # 创建加密文本表
            c.execute('''CREATE TABLE IF NOT EXISTS encrypted_texts
                         (id INTEGER PRIMARY KEY AUTOINCREMENT,
                         title TEXT NOT NULL,
                         encrypted_content TEXT NOT NULL,
                         created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                         updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)''')

            # 添加默认分类
            c.execute('''INSERT OR IGNORE INTO categories (name, color) VALUES
                         ('默认', '#6c757d'),
                         ('工作', '#007bff'),
                         ('个人', '#28a745'),
                         ('社交', '#17a2b8'),
                         ('金融', '#ffc107'),
                         ('购物', '#fd7e14')''')

            # 检查并添加新列(用于数据库升级)
            try:
                c.execute('ALTER TABLE passwords ADD COLUMN category TEXT DEFAULT "默认"')
            except sqlite3.OperationalError:
                pass  # 列已存在

            try:
                c.execute('ALTER TABLE passwords ADD COLUMN expires_at TIMESTAMP')
            except sqlite3.OperationalError:
                pass  # 列已存在

            conn.commit()
            conn.close()
        except Exception as e:
            messagebox.showerror('错误', f'创建数据库失败: {str(e)}')
            exit(1)

    def set_master_password(self):
        # 设置主密码
        while True:
            master_password = simpledialog.askstring('设置主密码', '请设置主密码 (至少8位,包含大小写字母和数字):', show='*')
            if not master_password:
                messagebox.showerror('错误', '主密码不能为空!')
                continue
            # 密码强度检查
            if len(master_password) < 8:
                messagebox.showerror('错误', '主密码长度至少为8位!')
                continue
            if not any(c.isupper() for c in master_password):
                messagebox.showerror('错误', '主密码必须包含大写字母!')
                continue
            if not any(c.islower() for c in master_password):
                messagebox.showerror('错误', '主密码必须包含小写字母!')
                continue
            if not any(c.isdigit() for c in master_password):
                messagebox.showerror('错误', '主密码必须包含数字!')
                continue

            confirm_password = simpledialog.askstring('确认主密码', '请再次输入主密码:', show='*')
            if master_password == confirm_password:
                # 哈希主密码
                self.master_password_hash = hashlib.sha256(master_password.encode()).hexdigest()
                # 保存主密码哈希到数据库
                conn = sqlite3.connect(self.db_path)
                c = conn.cursor()
                c.execute('INSERT OR REPLACE INTO settings (name, value) VALUES (?, ?)',
                          ('master_password_hash', self.master_password_hash))
                conn.commit()
                conn.close()
                messagebox.showinfo('成功', '主密码设置成功!')
                return master_password
            else:
                messagebox.showerror('错误', '两次输入的密码不匹配!')

    def verify_master_password(self):
        # 验证主密码并派生加密密钥
        conn = sqlite3.connect(self.db_path)
        c = conn.cursor()
        c.execute('SELECT value FROM settings WHERE name = ?', ('master_password_hash',))
        result = c.fetchone()
        conn.close()

        if not result:
            messagebox.showerror('错误', '未找到主密码信息,请重新设置!')
            self.set_master_password()
            return True

        self.master_password_hash = result[0]
        max_attempts = 3
        attempts = 0
        while attempts < max_attempts:
            password = simpledialog.askstring('验证', '请输入主密码:', show='*')
            if password and hashlib.sha256(password.encode()).hexdigest() == self.master_password_hash:
                # 加载并派生密钥(使用主密码哈希)
                if not self.load_key_with_hash(self.master_password_hash):
                    messagebox.showerror('错误', '密钥派生失败')
                    exit(1)

                # 验证密钥是否与数据库匹配
                if not self.validate_key():
                    messagebox.showerror('错误', '密钥文件与数据库不匹配!\n可能的原因:\n&#8226; 密钥文件已损坏\n&#8226; 使用了错误的密钥文件\n&#8226; 数据库文件已损坏\n\n请尝试从备份恢复密钥文件。')
                    exit(1)

                # 保存配置以便下次自动登录
                self.save_config(password)
                return True
            attempts += 1
            remaining = max_attempts - attempts
            if remaining > 0:
                messagebox.showerror('错误', f'密码错误! 还剩 {remaining} 次尝试机会')
            else:
                messagebox.showerror('错误', '尝试次数过多,程序将退出')
                exit(1)

    def encrypt_password(self, password):
        # 使用AES-GCM加密
        try:
            # 生成随机IV
            iv = os.urandom(12)
            # 创建加密器
            cipher = Cipher(algorithms.AES(self.key), modes.GCM(iv))
            encryptor = cipher.encryptor()
            # 加密数据
            ciphertext = encryptor.update(password.encode()) + encryptor.finalize()
            # 返回IV + 标签 + 密文 (都进行base64编码以便存储)
            return base64.b64encode(iv + encryptor.tag + ciphertext).decode()
        except Exception as e:
            messagebox.showerror('错误', f'加密失败: {str(e)}')
            return None

    def decrypt_password(self, encrypted_password):
        # 使用AES-GCM解密
        try:
            # 解码base64数据
            data = base64.b64decode(encrypted_password.encode())
            # 分离IV、标签和密文
            iv = data[:12]
            tag = data[12:28]
            ciphertext = data[28:]
            # 创建解密器
            cipher = Cipher(algorithms.AES(self.key), modes.GCM(iv, tag))
            decryptor = cipher.decryptor()
            # 解密数据
            return (decryptor.update(ciphertext) + decryptor.finalize()).decode()
        except Exception as e:
            messagebox.showerror('错误', f'解密失败: {str(e)}')
            return None

    def decrypt_password_legacy(self, encrypted_password):
        """使用原始主密码派生的密钥解密(兼容性方法)"""
        try:
            # 保存当前密钥
            current_key = self.key

            # 尝试使用原始主密码派生密钥
            if hasattr(self, 'master_password_hash'):
                # 从数据库获取主密码哈希
                conn = sqlite3.connect(self.db_path)
                c = conn.cursor()
                c.execute('SELECT value FROM settings WHERE name = ?', ('master_password_hash',))
                result = c.fetchone()
                conn.close()

                if result:
                    master_password_hash = result[0]

                    # 尝试多种可能的密钥派生方式
                    for password_candidate in [master_password_hash, master_password_hash[:32]]:
                        try:
                            # 使用原始密码派生密钥
                            kdf = PBKDF2HMAC(
                                algorithm=hashes.SHA256(),
                                length=32,
                                salt=self.salt,
                                iterations=self.iterations,
                            )
                            legacy_key = kdf.derive(password_candidate.encode())

                            # 临时使用legacy密钥
                            self.key = legacy_key

                            # 尝试解密
                            decrypted = self.decrypt_password(encrypted_password)
                            if decrypted:
                                # 恢复原密钥
                                self.key = current_key
                                return decrypted
                        except:
                            continue

            # 恢复原密钥
            self.key = current_key
            return None

        except Exception as e:
            # 恢复原密钥
            if 'current_key' in locals():
                self.key = current_key
            return None

    def add_password(self, service, username, password, url='', notes=''):
        # 添加新密码到数据库
        if not all([service, username, password]):
            messagebox.showerror('错误', '服务名、用户名和密码不能为空!')
            return False

        encrypted_password = self.encrypt_password(password)
        if not encrypted_password:
            return False

        try:
            conn = sqlite3.connect(self.db_path)
            c = conn.cursor()
            # 检查是否已存在相同服务和用户名的密码
            c.execute('SELECT id FROM passwords WHERE service = ? AND username = ?',
                      (service, username))
            if c.fetchone():
                messagebox.showerror('错误', f'已存在{service}的{username}记录!')
                conn.close()
                return False

            # 手动设置时间戳
            from datetime import datetime
            current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

            c.execute('INSERT INTO passwords (service, username, encrypted_password, url, notes, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)',
                      (service, username, encrypted_password, url, notes, current_time, current_time))
            conn.commit()
            conn.close()
            messagebox.showinfo('成功', '密码添加成功!')
            return True
        except Exception as e:
            messagebox.showerror('错误', f'添加密码失败: {str(e)}')
            return False

    def get_password(self, service, username):
        # 获取密码
        try:
            conn = sqlite3.connect(self.db_path)
            c = conn.cursor()
            c.execute('SELECT encrypted_password FROM passwords WHERE service = ? AND username = ?',
                      (service, username))
            result = c.fetchone()
            conn.close()
            if result:
                # 尝试用当前密钥解密
                decrypted = self.decrypt_password(result[0])
                if decrypted:
                    return decrypted

                # 如果失败,尝试用原始主密码派生的密钥解密(兼容性)
                return self.decrypt_password_legacy(result[0])
            return None
        except Exception as e:
            messagebox.showerror('错误', f'获取密码失败: {str(e)}')
            return None

    def update_password(self, password_id, service, username, new_password, url, notes):
        # 更新密码
        if new_password:  # 只有当提供新密码时才加密
            encrypted_password = self.encrypt_password(new_password)
            if not encrypted_password:
                return False
        else:
            # 如果没有提供新密码,获取当前加密密码
            try:
                conn = sqlite3.connect(self.db_path)
                c = conn.cursor()
                c.execute('SELECT encrypted_password FROM passwords WHERE id = ?', (password_id,))
                result = c.fetchone()
                conn.close()
                if not result:
                    messagebox.showerror('错误', '未找到密码记录!')
                    return False
                encrypted_password = result[0]
            except Exception as e:
                messagebox.showerror('错误', f'获取当前密码失败: {str(e)}')
                return False

        try:
            # 手动设置更新时间戳
            from datetime import datetime
            current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

            conn = sqlite3.connect(self.db_path)
            c = conn.cursor()
            c.execute('UPDATE passwords SET service = ?, username = ?, encrypted_password = ?, url = ?, notes = ?, updated_at = ? WHERE id = ?',
                      (service, username, encrypted_password, url, notes, current_time, password_id))
            conn.commit()
            affected = c.rowcount
            conn.close()
            if affected > 0:
                messagebox.showinfo('成功', '记录更新成功!')
                return True
            else:
                messagebox.showerror('错误', '未找到密码记录!')
                return False
        except Exception as e:
            messagebox.showerror('错误', f'更新记录失败: {str(e)}')
            return False

    def delete_password(self, password_id):
        # 删除密码
        try:
            conn = sqlite3.connect(self.db_path)
            c = conn.cursor()
            c.execute('DELETE FROM passwords WHERE id = ?', (password_id,))
            conn.commit()
            affected = c.rowcount
            conn.close()
            if affected > 0:
                messagebox.showinfo('成功', '密码删除成功!')
                return True
            else:
                messagebox.showerror('错误', '未找到密码记录!')
                return False
        except Exception as e:
            messagebox.showerror('错误', f'删除密码失败: {str(e)}')
            return False

    def get_all_passwords(self):
        # 获取所有密码
        try:
            conn = sqlite3.connect(self.db_path)
            c = conn.cursor()
            c.execute('SELECT id, service, username, url, notes, created_at, updated_at FROM passwords ORDER BY service')
            results = c.fetchall()
            conn.close()
            return results
        except Exception as e:
            messagebox.showerror('错误', f'获取密码列表失败: {str(e)}')
            return []

    def search_passwords(self, keyword):
        # 搜索密码
        try:
            conn = sqlite3.connect(self.db_path)
            c = conn.cursor()
            query = "SELECT id, service, username, url, notes, created_at, updated_at FROM passwords WHERE service LIKE ? OR username LIKE ? OR url LIKE ? OR notes LIKE ? ORDER BY service"
            c.execute(query, (f'%{keyword}%', f'%{keyword}%', f'%{keyword}%', f'%{keyword}%'))
            results = c.fetchall()
            conn.close()
            return results
        except Exception as e:
            messagebox.showerror('错误', f'搜索密码失败: {str(e)}')
            return []

    def generate_strong_password(self, length=16):
        # 生成强密码
        if length < 8:
            length = 8
        # 定义字符集
        uppercase = string.ascii_uppercase
        lowercase = string.ascii_lowercase
        digits = string.digits
        special_chars = '!@#$%^&*()_-+=[]{}|;:,.<>?'

        # 确保包含每种类型的字符
        password = [
            random.choice(uppercase),
            random.choice(lowercase),
            random.choice(digits),
            random.choice(special_chars)
        ]

        # 填充剩余长度
        all_chars = uppercase + lowercase + digits + special_chars
        password += [random.choice(all_chars) for _ in range(length - 4)]

        # 打乱顺序
        random.shuffle(password)

        return ''.join(password)

    def export_to_csv(self, file_path):
        """导出密码到CSV文件"""
        try:
            conn = sqlite3.connect(self.db_path)
            c = conn.cursor()
            c.execute('''SELECT service, username, url, notes, category,
                         created_at, updated_at FROM passwords ORDER BY service''')
            results = c.fetchall()
            conn.close()

            with open(file_path, 'w', newline='', encoding='utf-8') as csvfile:
                writer = csv.writer(csvfile)
                writer.writerow(['服务名', '用户名', '密码', 'URL', '备注', '分类', '创建时间', '更新时间'])

                for row in results:
                    # 解密密码
                    password = self.get_password(row[0], row[1])
                    writer.writerow([row[0], row[1], password, row[2], row[3], row[4], row[5], row[6]])

            return True, f"成功导出 {len(results)} 条记录到 {file_path}"
        except Exception as e:
            return False, f"导出失败: {str(e)}"

    def import_from_csv(self, file_path):
        """从CSV文件导入密码"""
        try:
            imported_count = 0
            skipped_count = 0

            with open(file_path, 'r', encoding='utf-8') as csvfile:
                reader = csv.DictReader(csvfile)

                for row in reader:
                    service = row.get('服务名', '').strip()
                    username = row.get('用户名', '').strip()
                    password = row.get('密码', '').strip()
                    url = row.get('URL', '').strip()
                    notes = row.get('备注', '').strip()
                    category = row.get('分类', '默认').strip()

                    if not service or not username or not password:
                        skipped_count += 1
                        continue

                    # 检查是否已存在
                    conn = sqlite3.connect(self.db_path)
                    c = conn.cursor()
                    c.execute('SELECT id FROM passwords WHERE service = ? AND username = ?',
                              (service, username))
                    if c.fetchone():
                        skipped_count += 1
                        conn.close()
                        continue

                    # 加密密码并添加
                    encrypted_password = self.encrypt_password(password)
                    if encrypted_password:
                        c.execute('''INSERT INTO passwords
                                   (service, username, encrypted_password, url, notes, category)
                                   VALUES (?, ?, ?, ?, ?, ?)''',
                                  (service, username, encrypted_password, url, notes, category))
                        conn.commit()
                        imported_count += 1
                    else:
                        skipped_count += 1

                    conn.close()

            return True, f"成功导入 {imported_count} 条记录,跳过 {skipped_count} 条记录"
        except Exception as e:
            return False, f"导入失败: {str(e)}"

    # 加密文本管理方法
    def encrypt_text(self, text):
        """加密文本内容"""
        try:
            # 生成随机IV
            iv = os.urandom(12)
            # 创建加密器
            cipher = Cipher(algorithms.AES(self.key), modes.GCM(iv))
            encryptor = cipher.encryptor()
            # 加密数据
            ciphertext = encryptor.update(text.encode('utf-8')) + encryptor.finalize()
            # 返回IV + 标签 + 密文 (都进行base64编码以便存储)
            return base64.b64encode(iv + encryptor.tag + ciphertext).decode()
        except Exception as e:
            messagebox.showerror('错误', f'文本加密失败: {str(e)}')
            return None

    def decrypt_text(self, encrypted_text):
        """解密文本内容"""
        try:
            # 解码base64数据
            data = base64.b64decode(encrypted_text.encode())
            # 分离IV、标签和密文
            iv = data[:12]
            tag = data[12:28]
            ciphertext = data[28:]
            # 创建解密器
            cipher = Cipher(algorithms.AES(self.key), modes.GCM(iv, tag))
            decryptor = cipher.decryptor()
            # 解密数据
            return (decryptor.update(ciphertext) + decryptor.finalize()).decode('utf-8')
        except Exception as e:
            messagebox.showerror('错误', f'文本解密失败: {str(e)}')
            return None

    def add_encrypted_text(self, title, content):
        """添加加密文本"""
        if not all([title.strip(), content.strip()]):
            messagebox.showerror('错误', '标题和内容不能为空!')
            return False

        encrypted_content = self.encrypt_text(content)
        if not encrypted_content:
            return False

        try:
            from datetime import datetime
            current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

            conn = sqlite3.connect(self.db_path)
            c = conn.cursor()
            c.execute('INSERT INTO encrypted_texts (title, encrypted_content, created_at, updated_at) VALUES (?, ?, ?, ?)',
                      (title.strip(), encrypted_content, current_time, current_time))
            conn.commit()
            conn.close()
            messagebox.showinfo('成功', '加密文本添加成功!')
            return True
        except Exception as e:
            messagebox.showerror('错误', f'添加加密文本失败: {str(e)}')
            return False

    def get_encrypted_text(self, text_id):
        """获取解密后的文本内容"""
        try:
            conn = sqlite3.connect(self.db_path)
            c = conn.cursor()
            c.execute('SELECT title, encrypted_content FROM encrypted_texts WHERE id = ?', (text_id,))
            result = c.fetchone()
            conn.close()

            if result:
                title, encrypted_content = result
                decrypted_content = self.decrypt_text(encrypted_content)
                return title, decrypted_content
            return None, None
        except Exception as e:
            messagebox.showerror('错误', f'获取加密文本失败: {str(e)}')
            return None, None

    def get_all_encrypted_texts(self, sort_by='title', sort_order='ASC'):
        """获取所有加密文本列表(不包含内容)"""
        try:
            conn = sqlite3.connect(self.db_path)
            c = conn.cursor()

            # 构建排序查询
            valid_columns = ['id', 'title', 'created_at', 'updated_at']
            if sort_by not in valid_columns:
                sort_by = 'title'
            if sort_order.upper() not in ['ASC', 'DESC']:
                sort_order = 'ASC'

            query = f'SELECT id, title, created_at, updated_at FROM encrypted_texts ORDER BY {sort_by} {sort_order}'
            c.execute(query)
            results = c.fetchall()
            conn.close()

            # 确保时间格式正确,处理可能的空值或格式问题
            formatted_results = []
            for result in results:
                text_id, title, created_at, updated_at = result

                # 处理标题
                if not title:
                    title = "无标题"

                # 处理时间格式 - 确保时间正确显示
                if not created_at or str(created_at).strip() == '':
                    from datetime import datetime
                    created_at = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                else:
                    # 确保时间格式正确
                    created_at = str(created_at)

                if not updated_at or str(updated_at).strip() == '':
                    from datetime import datetime
                    updated_at = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                else:
                    # 确保时间格式正确
                    updated_at = str(updated_at)

                formatted_results.append((text_id, title, created_at, updated_at))

            return formatted_results
        except Exception as e:
            messagebox.showerror('错误', f'获取加密文本列表失败: {str(e)}')
            return []

    def update_encrypted_text(self, text_id, title, content):
        """更新加密文本"""
        if not all([title.strip(), content.strip()]):
            messagebox.showerror('错误', '标题和内容不能为空!')
            return False

        encrypted_content = self.encrypt_text(content)
        if not encrypted_content:
            return False

        try:
            from datetime import datetime
            current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

            conn = sqlite3.connect(self.db_path)
            c = conn.cursor()
            c.execute('UPDATE encrypted_texts SET title = ?, encrypted_content = ?, updated_at = ? WHERE id = ?',
                      (title.strip(), encrypted_content, current_time, text_id))
            conn.commit()
            affected = c.rowcount
            conn.close()

            if affected > 0:
                messagebox.showinfo('成功', '加密文本更新成功!')
                return True
            else:
                messagebox.showerror('错误', '未找到文本记录!')
                return False
        except Exception as e:
            messagebox.showerror('错误', f'更新加密文本失败: {str(e)}')
            return False

    def delete_encrypted_text(self, text_id):
        """删除加密文本"""
        try:
            conn = sqlite3.connect(self.db_path)
            c = conn.cursor()
            c.execute('DELETE FROM encrypted_texts WHERE id = ?', (text_id,))
            conn.commit()
            affected = c.rowcount
            conn.close()

            if affected > 0:
                messagebox.showinfo('成功', '加密文本删除成功!')
                return True
            else:
                messagebox.showerror('错误', '未找到文本记录!')
                return False
        except Exception as e:
            messagebox.showerror('错误', f'删除加密文本失败: {str(e)}')
            return False

    def search_encrypted_texts(self, keyword):
        """搜索加密文本(仅搜索标题)"""
        try:
            conn = sqlite3.connect(self.db_path)
            c = conn.cursor()
            query = "SELECT id, title, created_at, updated_at FROM encrypted_texts WHERE title LIKE ? ORDER BY title"
            c.execute(query, (f'%{keyword}%',))
            results = c.fetchall()
            conn.close()

            # 确保时间格式正确,处理可能的空值或格式问题
            formatted_results = []
            for result in results:
                text_id, title, created_at, updated_at = result

                # 处理标题
                if not title:
                    title = "无标题"

                # 处理时间格式
                if not created_at or str(created_at).strip() == '':
                    from datetime import datetime
                    created_at = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

                if not updated_at or str(updated_at).strip() == '':
                    from datetime import datetime
                    updated_at = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

                formatted_results.append((text_id, title, created_at, updated_at))

            return formatted_results
        except Exception as e:
            messagebox.showerror('错误', f'搜索加密文本失败: {str(e)}')
            return []

    def export_texts_to_csv(self, file_path):
        """导出加密文本到CSV文件"""
        try:
            conn = sqlite3.connect(self.db_path)
            c = conn.cursor()
            c.execute('SELECT id, title, encrypted_content, created_at, updated_at FROM encrypted_texts ORDER BY title')
            results = c.fetchall()
            conn.close()

            with open(file_path, 'w', newline='', encoding='utf-8') as csvfile:
                writer = csv.writer(csvfile)
                writer.writerow(['ID', '标题', '内容', '创建时间', '更新时间'])

                for row in results:
                    text_id, title, encrypted_content, created_at, updated_at = row

                    # 解密文本内容
                    decrypted_content = self.decrypt_text(encrypted_content)
                    if not decrypted_content:
                        decrypted_content = "[解密失败]"

                    writer.writerow([text_id, title, decrypted_content, created_at, updated_at])

            return True, f"成功导出 {len(results)} 条文本记录到 {file_path}"
        except Exception as e:
            return False, f"导出失败: {str(e)}"

    def import_texts_from_csv(self, file_path):
        """从CSV文件导入加密文本"""
        try:
            from datetime import datetime

            imported_count = 0
            skipped_count = 0

            with open(file_path, 'r', encoding='utf-8') as csvfile:
                reader = csv.reader(csvfile)

                # 跳过标题行
                next(reader, None)

                for row in reader:
                    if len(row) < 3:  # 至少需要ID、标题、内容
                        continue

                    # 解析CSV行:ID, 标题, 内容, 创建时间, 更新时间
                    text_id = row[0] if row[0] else None
                    title = row[1].strip() if row[1] else None
                    content = row[2] if row[2] else None

                    if not title or not content:
                        skipped_count += 1
                        continue

                    # 检查是否已存在相同标题的文本
                    conn = sqlite3.connect(self.db_path)
                    c = conn.cursor()
                    c.execute('SELECT id FROM encrypted_texts WHERE title = ?', (title,))
                    if c.fetchone():
                        skipped_count += 1
                        conn.close()
                        continue

                    # 加密并添加文本
                    encrypted_content = self.encrypt_text(content)
                    if encrypted_content:
                        current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                        c.execute('INSERT INTO encrypted_texts (title, encrypted_content, created_at, updated_at) VALUES (?, ?, ?, ?)',
                                  (title, encrypted_content, current_time, current_time))
                        conn.commit()
                        imported_count += 1
                    else:
                        skipped_count += 1

                    conn.close()

            return True, f"成功导入 {imported_count} 条文本记录,跳过 {skipped_count} 条记录"
        except Exception as e:
            return False, f"导入失败: {str(e)}"

class PasswordManagerGUI:
    def __init__(self, root):
        self.root = root
        self.root.title('安全密码管理器 v1.0 by:危阿杰  吾爱破解: www.52pojie.cn')
        self.root.geometry('1200x700')
        self.root.resizable(True, True)
        self.root.configure(bg='#f8f9fa')

        # 设置现代化样式
        self.style = ttk.Style()
        self.style.theme_use('clam')
        self.style.configure('TLabel', font=('Microsoft YaHei UI', 10), background='#f8f9fa')
        self.style.configure('TButton', font=('Microsoft YaHei UI', 10), padding=6)
        self.style.configure('Treeview', font=('Microsoft YaHei UI', 9), rowheight=25)
        self.style.configure('Treeview.Heading', font=('Microsoft YaHei UI', 10, 'bold'))
        self.style.configure('Title.TLabel', font=('Microsoft YaHei UI', 18, 'bold'), background='#f8f9fa', foreground='#2c3e50')

        # 创建密码管理器实例
        self.password_manager = PasswordManager()

        # 悬浮窗相关属性
        self.floating_window = None
        self.is_floating = False
        self.floating_position = {'x': 100, 'y': 100}  # 默认悬浮窗位置
        self.floating_expanded = False  # 悬浮窗是否展开
        self.floating_current_tab = 'passwords'  # 当前标签页
        self.floating_data = []  # 当前显示的数据

        # 检查密码管理器是否成功初始化
        if not hasattr(self.password_manager, 'key') or not self.password_manager.key:
            # 密码管理器未完全初始化(可能是密钥文件丢失),隐藏主窗口
            self.root.withdraw()
            # 等待用户处理密钥文件问题
            self.wait_for_key_recovery()
            return

        # 创建界面组件
        self.create_widgets()
        # 加载密码列表
        self.update_password_list()

        # 启动自动锁定监控
        self.password_manager.auto_lock_manager.start_monitoring(self.lock_application)

        # 绑定活动事件
        self.bind_activity_events()

        # 绑定快捷键
        self.bind_shortcuts()

        # 加载悬浮窗位置
        self.load_floating_position()

    def wait_for_key_recovery(self):
        """等待密钥恢复完成"""
        def check_recovery():
            if hasattr(self.password_manager, 'key') and self.password_manager.key:
                # 密钥恢复成功,显示主窗口并初始化界面
                self.root.deiconify()
                self.create_widgets()
                self.update_password_list()
                self.password_manager.auto_lock_manager.start_monitoring(self.lock_application)
                self.bind_activity_events()
                self.bind_shortcuts()
            else:
                # 继续等待,每500ms检查一次
                self.root.after(500, check_recovery)

        # 开始检查
        check_recovery()

    def create_menu(self):
        """创建菜单栏"""
        menubar = tk.Menu(self.root)
        self.root.config(menu=menubar)

        # 获取快捷键管理器
        sm = self.password_manager.shortcut_manager

        # 文件菜单
        file_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="文件", menu=file_menu)
        file_menu.add_command(label=f"&#128229; 导入CSV\t{sm.get_shortcut('import_csv')}", command=self.import_csv)
        file_menu.add_command(label=f"&#128228; 导出CSV\t{sm.get_shortcut('export_csv')}", command=self.export_csv)
        file_menu.add_separator()
        file_menu.add_command(label="&#128229; 导入文本CSV", command=self.import_texts_menu)
        file_menu.add_command(label="&#128228; 导出文本CSV", command=self.export_texts_menu)
        file_menu.add_separator()
        file_menu.add_command(label="&#128190; 备份密钥文件", command=self.backup_key_file)
        file_menu.add_command(label="&#128260; 恢复密钥文件", command=self.restore_key_file)
        file_menu.add_separator()
        file_menu.add_command(label=f"&#128274; 锁定应用\t{sm.get_shortcut('lock_app')}", command=self.lock_application)
        file_menu.add_separator()
        file_menu.add_command(label="&#10060; 退出", command=self.root.quit)

        # 编辑菜单
        edit_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="编辑", menu=edit_menu)
        edit_menu.add_command(label=f"&#10133; 添加密码\t{sm.get_shortcut('add_password')}", command=self.add_password_dialog)
        edit_menu.add_command(label=f"&#128065;&#65039; 查看密码\t{sm.get_shortcut('view_password')}", command=self.view_password)
        edit_menu.add_command(label=f"&#9999;&#65039; 编辑密码\t{sm.get_shortcut('edit_password')}", command=self.update_password_dialog)
        edit_menu.add_command(label=f"&#128465;&#65039; 删除密码\t{sm.get_shortcut('delete_password')}", command=self.delete_password)
        edit_menu.add_separator()
        edit_menu.add_command(label=f"&#127922; 生成密码\t{sm.get_shortcut('generate_password')}", command=self.generate_password)
        edit_menu.add_command(label=f"&#128269; 搜索\t{sm.get_shortcut('search')}", command=lambda: self.search_entry.focus())

        # 工具菜单
        tools_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="工具", menu=tools_menu)
        tools_menu.add_command(label="&#128269; 密码强度检查", command=self.check_all_passwords_strength)
        tools_menu.add_command(label="&#9200; 过期密码检查", command=self.check_expired_passwords)
        tools_menu.add_separator()
        tools_menu.add_command(label="&#128221; 文本管理\tF2", command=self.show_text_manager)
        tools_menu.add_separator()
        tools_menu.add_command(label="&#127991;&#65039; 管理分类", command=self.manage_categories)
        tools_menu.add_separator()
        tools_menu.add_command(label="&#9000;&#65039; 快捷键设置", command=self.show_shortcut_settings)

        # 视图菜单
        view_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="视图", menu=view_menu)

        # 悬浮窗选项
        view_menu.add_command(label=f"&#129695; 悬浮窗模式\t{sm.get_shortcut('toggle_floating')}", command=self.toggle_floating_mode)
        view_menu.add_separator()

        # 主题子菜单
        theme_menu = tk.Menu(view_menu, tearoff=0)
        view_menu.add_cascade(label="&#127912; 主题", menu=theme_menu)
        theme_menu.add_command(label="&#9728;&#65039; 浅色主题", command=lambda: self.change_theme('light'))
        theme_menu.add_command(label="&#127769; 深色主题", command=lambda: self.change_theme('dark'))
        theme_menu.add_command(label="&#128309; 蓝色主题", command=lambda: self.change_theme('blue'))
        theme_menu.add_separator()
        theme_menu.add_command(label="&#127807; 绿色主题", command=lambda: self.change_theme('green'))
        theme_menu.add_command(label="&#128156; 紫色主题", command=lambda: self.change_theme('purple'))
        theme_menu.add_command(label="&#129505; 橙色主题", command=lambda: self.change_theme('orange'))
        theme_menu.add_separator()
        theme_menu.add_command(label="&#9899; 高对比度", command=lambda: self.change_theme('high_contrast'))
        theme_menu.add_command(label="&#127747; 夜间模式", command=lambda: self.change_theme('night'))

        # 帮助菜单
        help_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="帮助", menu=help_menu)
        help_menu.add_command(label="&#128214; 使用说明", command=self.show_help)
        help_menu.add_command(label="&#128273; 密钥恢复指南", command=self.show_key_recovery_guide)
        help_menu.add_separator()
        help_menu.add_command(label="&#8505;&#65039; 关于", command=self.show_about)

    def create_widgets(self):
        # 创建菜单栏
        self.create_menu()

        # 创建主框架
        main_frame = ttk.Frame(self.root, padding='15')
        main_frame.pack(fill=tk.BOTH, expand=True)

        # 创建标题
        title_label = ttk.Label(main_frame, text='&#128272; 安全密码管理器', style='Title.TLabel')
        title_label.pack(pady=(0, 20))

        # 创建按钮框架
        button_frame = ttk.Frame(main_frame)
        button_frame.pack(fill=tk.X, pady=(0, 15))

        # 添加按钮 - 使用更现代的图标和文字
        ttk.Button(button_frame, text='&#10133; 添加密码', command=self.add_password_dialog, width=18).pack(side=tk.LEFT, padx=8)
        ttk.Button(button_frame, text='&#128065;&#65039; 查看密码', command=self.view_password, width=18).pack(side=tk.LEFT, padx=8)
        ttk.Button(button_frame, text='&#9999;&#65039; 更新密码', command=self.update_password_dialog, width=18).pack(side=tk.LEFT, padx=8)
        ttk.Button(button_frame, text='&#128465;&#65039; 删除密码', command=self.delete_password, width=18).pack(side=tk.LEFT, padx=8)
        ttk.Button(button_frame, text='&#127922; 生成密码', command=self.generate_password, width=18).pack(side=tk.LEFT, padx=8)

        # 创建搜索框
        search_frame = ttk.Frame(main_frame)
        search_frame.pack(fill=tk.X, pady=(0, 15))

        ttk.Label(search_frame, text='&#128269; 搜索:', font=('Microsoft YaHei UI', 11)).pack(side=tk.LEFT, padx=(0, 10))
        self.search_var = tk.StringVar()
        self.search_entry = ttk.Entry(search_frame, textvariable=self.search_var, font=('Microsoft YaHei UI', 10), width=40)
        self.search_entry.pack(side=tk.LEFT, padx=(0, 10))
        self.search_entry.bind('<Return>', lambda event: self.search_passwords())
        ttk.Button(search_frame, text='&#128269; 搜索', command=self.search_passwords, width=12).pack(side=tk.LEFT, padx=5)
        ttk.Button(search_frame, text='&#128260; 重置', command=self.reset_search, width=12).pack(side=tk.LEFT, padx=5)

        # 创建密码列表
        columns = ('ID', '名称', '用户名', 'URL', '备注', '创建时间', '更新时间')
        self.password_tree = ttk.Treeview(main_frame, columns=columns, show='headings', selectmode='browse')

        # 排序状态
        self.sort_reverse = {}

        for col in columns:
            self.password_tree.heading(col, text=col, command=lambda c=col: self.sort_column(c))
            if col == 'ID':
                self.password_tree.column(col, width=50, anchor=tk.CENTER)
            elif col in ['创建时间', '更新时间']:
                self.password_tree.column(col, width=150, anchor=tk.CENTER)
            elif col == 'URL':
                self.password_tree.column(col, width=200, anchor=tk.W)
                self.password_tree.tag_configure('url', foreground='blue')
            elif col == '备注':
                self.password_tree.column(col, width=150, anchor=tk.W)
            elif col == '名称':
                self.password_tree.column(col, width=150, anchor=tk.CENTER)
            else:
                self.password_tree.column(col, width=120, anchor=tk.CENTER)

            # 初始化排序状态
            self.sort_reverse[col] = False

        # 添加滚动条
        scrollbar = ttk.Scrollbar(main_frame, orient=tk.VERTICAL, command=self.password_tree.yview)
        self.password_tree.configure(yscroll=scrollbar.set)
        self.password_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, pady=5)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        # 创建状态栏框架
        status_frame = ttk.Frame(self.root)
        status_frame.pack(side=tk.BOTTOM, fill=tk.X)

        # 主状态标签
        self.status_var = tk.StringVar()
        self.status_var.set('就绪')
        status_label = ttk.Label(status_frame, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)
        status_label.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(2, 5), pady=2)

        # 时间标签
        self.time_var = tk.StringVar()
        time_label = ttk.Label(status_frame, textvariable=self.time_var, relief=tk.SUNKEN, anchor=tk.CENTER, width=20)
        time_label.pack(side=tk.RIGHT, padx=(5, 2), pady=2)

        # 更新时间
        self.update_time()

        # 绑定双击事件查看密码或打开链接
        self.password_tree.bind('<Double-1>', self.on_double_click)

        # 绑定右键菜单
        self.password_tree.bind('<Button-3>', self.show_context_menu)

    def on_double_click(self, event):
        # 获取双击的项和列
        if not self.password_tree.selection():
            return
        item = self.password_tree.selection()[0]
        column = self.password_tree.identify_column(event.x)
        # 列索引从1开始,URL是第4列(索引3)
        if column == '#4':  # '#4'对应第4列,即URL列
            url = self.password_tree.item(item, 'values')[3]
            if url:
                webbrowser.open(url)
            else:
                messagebox.showinfo('提示', '该记录没有URL链接')
        else:
            # 双击其他列时查看密码详情
            self.view_password()

    def update_password_list(self, passwords=None):
        # 更新密码列表
        # 清空现有项
        for item in self.password_tree.get_children():
            self.password_tree.delete(item)

        # 获取密码列表
        if passwords is None:
            passwords = self.password_manager.get_all_passwords()

        # 添加到列表
        for password in passwords:
            item_id = self.password_tree.insert('', tk.END, values=password)
            # 如果URL不为空,设置URL标签
            if password[3]:  # password[3]是URL字段
                self.password_tree.item(item_id, tags=('url',))

        self.status_var.set(f'共 {len(passwords)} 条记录')

    def add_password_dialog(self):
        # 添加密码对话框
        dialog = tk.Toplevel(self.root)
        dialog.title('添加新密码')
        dialog.geometry('550x350')
        dialog.resizable(False, False)
        dialog.transient(self.root)
        dialog.grab_set()

        # 居中显示
        dialog.update_idletasks()
        width = dialog.winfo_width()
        height = dialog.winfo_height()
        x = (dialog.winfo_screenwidth() // 2) - (width // 2)
        y = (dialog.winfo_screenheight() // 2) - (height // 2)
        dialog.geometry('{}x{}+{}+{}'.format(width, height, x, y))

        # 创建框架
        frame = ttk.Frame(dialog, padding='25')
        frame.pack(fill=tk.BOTH, expand=True)

        # 配置列权重
        frame.grid_columnconfigure(1, weight=1)

        # 服务名
        ttk.Label(frame, text='名称:', font=('Microsoft YaHei UI', 10, 'bold')).grid(row=0, column=0, sticky=tk.W, pady=10, padx=(0, 15))
        service_var = tk.StringVar()
        service_entry = ttk.Entry(frame, textvariable=service_var, width=30, font=('Microsoft YaHei UI', 10))
        service_entry.grid(row=0, column=1, columnspan=2, sticky=tk.W+tk.E, pady=10)

        # 用户名
        ttk.Label(frame, text='用户名:', font=('Microsoft YaHei UI', 10, 'bold')).grid(row=1, column=0, sticky=tk.W, pady=10, padx=(0, 15))
        username_var = tk.StringVar()
        username_entry = ttk.Entry(frame, textvariable=username_var, width=30, font=('Microsoft YaHei UI', 10))
        username_entry.grid(row=1, column=1, columnspan=2, sticky=tk.W+tk.E, pady=10)

        # 密码行
        ttk.Label(frame, text='密码:', font=('Microsoft YaHei UI', 10, 'bold')).grid(row=2, column=0, sticky=tk.W, pady=10, padx=(0, 15))

        # 密码输入框架
        password_input_frame = ttk.Frame(frame)
        password_input_frame.grid(row=2, column=1, columnspan=2, sticky=tk.W+tk.E, pady=10)
        password_input_frame.grid_columnconfigure(0, weight=1)

        password_var = tk.StringVar()
        password_entry = ttk.Entry(password_input_frame, textvariable=password_var, font=('Consolas', 10))
        password_entry.grid(row=0, column=0, sticky=tk.W+tk.E, padx=(0, 10))

        # 生成密码按钮
        def generate_new_password():
            new_password = self.password_manager.generate_strong_password()
            password_var.set(new_password)
            update_strength()
        ttk.Button(password_input_frame, text='生成强密码', command=generate_new_password, width=12).grid(row=0, column=1)

        # 密码强度显示
        strength_frame = ttk.Frame(frame)
        strength_frame.grid(row=3, column=1, columnspan=2, sticky=tk.W+tk.E, pady=(5, 10))
        strength_frame.grid_columnconfigure(1, weight=1)

        strength_label = ttk.Label(strength_frame, text='强度: 未检测', font=('Microsoft YaHei UI', 9))
        strength_label.grid(row=0, column=0, sticky=tk.W)

        strength_bar = ttk.Progressbar(strength_frame, length=250, mode='determinate')
        strength_bar.grid(row=0, column=1, sticky=tk.W+tk.E, padx=(10, 0))

        def update_strength(*args):
            password = password_var.get()
            if password:
                score, level, color, feedback = self.password_manager.strength_checker.check_strength(password)
                strength_label.config(text=f'强度: {level}', foreground=color)
                strength_bar['value'] = score
                if feedback:
                    # 显示最重要的反馈
                    main_feedback = feedback[0] if feedback else ""
                    strength_label.config(text=f'强度: {level} - {main_feedback}')
            else:
                strength_label.config(text='强度: 未检测', foreground='gray')
                strength_bar['value'] = 0

        password_var.trace('w', update_strength)

        # URL
        ttk.Label(frame, text='URL:', font=('Microsoft YaHei UI', 10, 'bold')).grid(row=4, column=0, sticky=tk.W, pady=10, padx=(0, 15))
        url_var = tk.StringVar()
        url_entry = ttk.Entry(frame, textvariable=url_var, font=('Microsoft YaHei UI', 10))
        url_entry.grid(row=4, column=1, columnspan=2, sticky=tk.W+tk.E, pady=10)

        # 备注
        ttk.Label(frame, text='备注:', font=('Microsoft YaHei UI', 10, 'bold')).grid(row=5, column=0, sticky=tk.W, pady=10, padx=(0, 15))
        notes_var = tk.StringVar()
        notes_entry = ttk.Entry(frame, textvariable=notes_var, font=('Microsoft YaHei UI', 10))
        notes_entry.grid(row=5, column=1, columnspan=2, sticky=tk.W+tk.E, pady=10)

        # 按钮框架
        btn_frame = ttk.Frame(frame)
        btn_frame.grid(row=6, column=0, columnspan=3, pady=15)

        def save_password():
            service = service_var.get().strip()
            username = username_var.get().strip()
            password = password_var.get().strip()
            url = url_var.get().strip()
            notes = notes_var.get().strip()
            if self.password_manager.add_password(service, username, password, url, notes):
                self.update_password_list()
                dialog.destroy()

        ttk.Button(btn_frame, text='保存', command=save_password).pack(side=tk.LEFT, padx=10)
        ttk.Button(btn_frame, text='取消', command=dialog.destroy).pack(side=tk.LEFT, padx=10)

    def view_password(self):
        # 查看密码
        selected_item = self.password_tree.selection()
        if not selected_item:
            messagebox.showwarning('提示', '请先选择一条记录!')
            return

        item = self.password_tree.item(selected_item[0])
        password_id, service, username = item['values'][0], item['values'][1], item['values'][2]

        # 获取解密后的密码
        password = self.password_manager.get_password(service, username)
        if not password:
            messagebox.showerror('错误', '无法获取密码!')
            return

        # 显示密码对话框
        dialog = tk.Toplevel(self.root)
        dialog.title(f'&#128065;&#65039; {service} - 密码详情')
        dialog.geometry('650x400')
        dialog.resizable(True, True)
        dialog.transient(self.root)
        dialog.grab_set()
        dialog.configure(bg='#f8f9fa')

        # 居中显示
        dialog.update_idletasks()
        width = dialog.winfo_width()
        height = dialog.winfo_height()
        x = (dialog.winfo_screenwidth() // 2) - (width // 2)
        y = (dialog.winfo_screenheight() // 2) - (height // 2)
        dialog.geometry('{}x{}+{}+{}'.format(width, height, x, y))

        # 创建框架
        frame = ttk.Frame(dialog, padding='25')
        frame.pack(fill=tk.BOTH, expand=True)

        # 标题
        title_label = ttk.Label(frame, text=f'&#128272; {service}', font=('Microsoft YaHei UI', 14, 'bold'), foreground='#2c3e50')
        title_label.grid(row=0, column=0, columnspan=3, pady=(0, 20))

        # 服务名
        ttk.Label(frame, text='名称:', font=('Microsoft YaHei UI', 10, 'bold')).grid(row=1, column=0, sticky=tk.W, pady=10, padx=(0, 15))
        service_label = ttk.Label(frame, text=service, font=('Microsoft YaHei UI', 12), foreground='#34495e')
        service_label.grid(row=1, column=1, sticky=tk.W, pady=10, columnspan=2)

        # 用户名
        ttk.Label(frame, text='用户名:', font=('Microsoft YaHei UI', 10, 'bold')).grid(row=2, column=0, sticky=tk.W, pady=10, padx=(0, 15))
        username_label = ttk.Label(frame, text=username, font=('Microsoft YaHei UI', 12), foreground='#34495e')
        username_label.grid(row=2, column=1, sticky=tk.W, pady=10, columnspan=2)

        # 密码
        ttk.Label(frame, text='密码:', font=('Microsoft YaHei UI', 10, 'bold')).grid(row=3, column=0, sticky=tk.W, pady=12, padx=(0, 15))

        # 密码显示框架
        password_frame = ttk.Frame(frame)
        password_frame.grid(row=3, column=1, sticky=tk.W+tk.E, pady=12, columnspan=2)
        password_frame.grid_columnconfigure(0, weight=1)

        # 密码显示框 - 使用Entry组件以便复制
        password_display = tk.Entry(password_frame,
                                   font=('Consolas', 12, 'bold'),
                                   fg='#e74c3c',
                                   bg='#ffffff',
                                   relief='solid',
                                   bd=1,
                                   state='readonly',
                                   readonlybackground='#ffffff')
        password_display.insert(0, password)
        password_display.pack(side=tk.LEFT, padx=(0, 15), fill=tk.X, expand=True)

        # 复制按钮
        def copy_to_clipboard():
            pyperclip.copy(password)
            messagebox.showinfo('&#9989; 成功', '密码已复制到剪贴板!')

        ttk.Button(password_frame, text='&#128203; 复制', command=copy_to_clipboard, width=12).pack(side=tk.RIGHT)

        current_row = 4

        # URL
        if item['values'][3]:
            ttk.Label(frame, text='URL:', font=('Microsoft YaHei UI', 10, 'bold')).grid(row=current_row, column=0, sticky=tk.W, pady=8, padx=(0, 15))
            url_text = item['values'][3]
            url_label = ttk.Label(frame, text=url_text, font=('Microsoft YaHei UI', 10), foreground='#3498db', cursor='hand2')
            url_label.grid(row=current_row, column=1, sticky=tk.W, pady=8, columnspan=2)

            # 点击URL打开链接
            def open_url(event):
                webbrowser.open(url_text)
            url_label.bind('<Button-1>', open_url)
            current_row += 1

        # 备注
        if item['values'][4]:
            ttk.Label(frame, text='备注:', font=('Microsoft YaHei UI', 10, 'bold')).grid(row=current_row, column=0, sticky=tk.W, pady=8, padx=(0, 15))
            notes_label = ttk.Label(frame, text=item['values'][4], font=('Microsoft YaHei UI', 10), foreground='#34495e', wraplength=300)
            notes_label.grid(row=current_row, column=1, sticky=tk.W, pady=8, columnspan=2)
            current_row += 1

        # 时间信息
        ttk.Label(frame, text='创建时间:', font=('Microsoft YaHei UI', 9)).grid(row=current_row, column=0, sticky=tk.W, pady=(15, 5), padx=(0, 15))
        ttk.Label(frame, text=item['values'][5], font=('Microsoft YaHei UI', 9), foreground='#7f8c8d').grid(row=current_row, column=1, sticky=tk.W, pady=(15, 5))

        ttk.Label(frame, text='更新时间:', font=('Microsoft YaHei UI', 9)).grid(row=current_row+1, column=0, sticky=tk.W, pady=5, padx=(0, 15))
        ttk.Label(frame, text=item['values'][6], font=('Microsoft YaHei UI', 9), foreground='#7f8c8d').grid(row=current_row+1, column=1, sticky=tk.W, pady=5)

        # 按钮框架
        btn_frame = ttk.Frame(frame)
        btn_frame.grid(row=current_row+2, column=0, columnspan=3, pady=(20, 0))

        # 关闭按钮
        ttk.Button(btn_frame, text='&#10060; 关闭', command=dialog.destroy, width=15).pack(side=tk.RIGHT, padx=10)

        # 编辑按钮
        def edit_password():
            dialog.destroy()
            self.update_password_dialog()
        ttk.Button(btn_frame, text='&#9999;&#65039; 编辑', command=edit_password, width=15).pack(side=tk.RIGHT, padx=10)

    def update_password_dialog(self):
        # 更新密码对话框
        selected_item = self.password_tree.selection()
        if not selected_item:
            messagebox.showwarning('提示', '请先选择一条记录!')
            return

        item = self.password_tree.item(selected_item[0])
        password_id, service, username = item['values'][0], item['values'][1], item['values'][2]

        # 显示更新对话框
        dialog = tk.Toplevel(self.root)
        dialog.title(f'{service} - 更新密码')
        dialog.geometry('550x320')
        dialog.resizable(False, False)
        dialog.transient(self.root)
        dialog.grab_set()

        # 居中显示
        dialog.update_idletasks()
        width = dialog.winfo_width()
        height = dialog.winfo_height()
        x = (dialog.winfo_screenwidth() // 2) - (width // 2)
        y = (dialog.winfo_screenheight() // 2) - (height // 2)
        dialog.geometry('{}x{}+{}+{}'.format(width, height, x, y))

        # 创建框架
        frame = ttk.Frame(dialog, padding='25')
        frame.pack(fill=tk.BOTH, expand=True)

        # 配置列权重
        frame.grid_columnconfigure(1, weight=1)

        # 服务名称
        ttk.Label(frame, text='名称:', font=('Microsoft YaHei UI', 10, 'bold')).grid(row=0, column=0, sticky=tk.W, pady=10, padx=(0, 15))
        service_var = tk.StringVar(value=service)
        service_entry = ttk.Entry(frame, textvariable=service_var, font=('Microsoft YaHei UI', 10))
        service_entry.grid(row=0, column=1, columnspan=2, sticky=tk.W+tk.E, pady=10)

        # 用户名
        ttk.Label(frame, text='用户名:', font=('Microsoft YaHei UI', 10, 'bold')).grid(row=1, column=0, sticky=tk.W, pady=10, padx=(0, 15))
        username_var = tk.StringVar(value=username)
        username_entry = ttk.Entry(frame, textvariable=username_var, font=('Microsoft YaHei UI', 10))
        username_entry.grid(row=1, column=1, columnspan=2, sticky=tk.W+tk.E, pady=10)

        # 密码行
        ttk.Label(frame, text='新密码:', font=('Microsoft YaHei UI', 10, 'bold')).grid(row=2, column=0, sticky=tk.W, pady=10, padx=(0, 15))

        # 密码输入框架
        password_input_frame = ttk.Frame(frame)
        password_input_frame.grid(row=2, column=1, columnspan=2, sticky=tk.W+tk.E, pady=10)
        password_input_frame.grid_columnconfigure(0, weight=1)

        password_var = tk.StringVar()
        password_entry = ttk.Entry(password_input_frame, textvariable=password_var, font=('Consolas', 10))
        password_entry.grid(row=0, column=0, sticky=tk.W+tk.E, padx=(0, 10))

        # 生成密码按钮
        def generate_new_password():
            new_password = self.password_manager.generate_strong_password()
            password_var.set(new_password)
        ttk.Button(password_input_frame, text='生成强密码', command=generate_new_password, width=12).grid(row=0, column=1)

        # URL
        ttk.Label(frame, text='URL:', font=('Microsoft YaHei UI', 10, 'bold')).grid(row=3, column=0, sticky=tk.W, pady=10, padx=(0, 15))
        url_var = tk.StringVar(value=item['values'][3])
        url_entry = ttk.Entry(frame, textvariable=url_var, font=('Microsoft YaHei UI', 10))
        url_entry.grid(row=3, column=1, columnspan=2, sticky=tk.W+tk.E, pady=10)

        # 备注
        ttk.Label(frame, text='备注:', font=('Microsoft YaHei UI', 10, 'bold')).grid(row=4, column=0, sticky=tk.W, pady=10, padx=(0, 15))
        notes_var = tk.StringVar(value=item['values'][4])
        notes_entry = ttk.Entry(frame, textvariable=notes_var, font=('Microsoft YaHei UI', 10))
        notes_entry.grid(row=4, column=1, columnspan=2, sticky=tk.W+tk.E, pady=10)

        # 按钮框架
        btn_frame = ttk.Frame(frame)
        btn_frame.grid(row=5, column=0, columnspan=3, pady=25)

        def save_update():
            service = service_var.get().strip()
            username = username_var.get().strip()
            new_password = password_var.get().strip()
            url = url_var.get().strip()
            notes = notes_var.get().strip()

            # 检查必填字段
            if not service or not username:
                messagebox.showerror('错误', '服务名和用户名不能为空!')
                return

            # 如果没有输入新密码,提示用户
            if not new_password:
                confirm = messagebox.askyesno('确认', '您没有输入新密码,是否只更新其他信息?')
                if not confirm:
                    return

            success = self.password_manager.update_password(password_id, service, username, new_password, url, notes)
            if success:
                self.update_password_list()
                dialog.destroy()
            # update_password 方法内部已经显示了成功或错误消息


        ttk.Button(btn_frame, text='保存', command=save_update).pack(side=tk.LEFT, padx=10)
        ttk.Button(btn_frame, text='取消', command=dialog.destroy).pack(side=tk.LEFT, padx=10)

    def delete_password(self):
        # 删除密码
        selected_item = self.password_tree.selection()
        if not selected_item:
            messagebox.showwarning('提示', '请先选择一条记录!')
            return

        item = self.password_tree.item(selected_item[0])
        password_id, service, username = item['values'][0], item['values'][1], item['values'][2]

        confirm = messagebox.askyesno('确认', f'确定要删除 {service} 的 {username} 密码记录吗?')
        if confirm:
            if self.password_manager.delete_password(password_id):
                self.update_password_list()

    def search_passwords(self):
        # 搜索密码
        keyword = self.search_var.get().strip()
        if not keyword:
            self.update_password_list()
            return

        results = self.password_manager.search_passwords(keyword)
        self.update_password_list(results)
        self.status_var.set(f'搜索到 {len(results)} 条记录')

    def reset_search(self):
        # 重置搜索
        self.search_var.set('')
        self.update_password_list()

    def generate_password(self):
        # 生成密码对话框
        password = self.password_manager.generate_strong_password()

        dialog = tk.Toplevel(self.root)
        dialog.title('&#127922; 生成强密码')
        dialog.geometry('500x200')
        dialog.resizable(False, False)
        dialog.transient(self.root)
        dialog.grab_set()
        dialog.configure(bg='#f8f9fa')

        # 居中显示
        dialog.update_idletasks()
        width = dialog.winfo_width()
        height = dialog.winfo_height()
        x = (dialog.winfo_screenwidth() // 2) - (width // 2)
        y = (dialog.winfo_screenheight() // 2) - (height // 2)
        dialog.geometry('{}x{}+{}+{}'.format(width, height, x, y))

        # 创建框架
        frame = ttk.Frame(dialog, padding='25')
        frame.pack(fill=tk.BOTH, expand=True)

        # 标题
        title_label = ttk.Label(frame, text='&#128272; 强密码生成器', font=('Microsoft YaHei UI', 14, 'bold'), foreground='#2c3e50')
        title_label.grid(row=0, column=0, columnspan=3, pady=(0, 20))

        # 密码显示
        ttk.Label(frame, text='生成的密码:', font=('Microsoft YaHei UI', 10, 'bold')).grid(row=1, column=0, sticky=tk.W, pady=10, padx=(0, 15))

        password_frame = ttk.Frame(frame)
        password_frame.grid(row=1, column=1, sticky=tk.W, pady=10)

        # 密码文本框
        password_var = tk.StringVar(value=password)
        password_entry = ttk.Entry(password_frame, textvariable=password_var, width=25,
                                 font=('Consolas', 12, 'bold'), state='readonly')
        password_entry.pack(side=tk.LEFT, padx=(0, 10))

        # 复制按钮
        def copy_to_clipboard():
            current_password = password_var.get()
            pyperclip.copy(current_password)
            messagebox.showinfo('&#9989; 成功', '密码已复制到剪贴板!')
            dialog.destroy()

        # 重新生成按钮
        def regenerate_password():
            new_password = self.password_manager.generate_strong_password()
            password_var.set(new_password)

        # 按钮框架
        btn_frame = ttk.Frame(frame)
        btn_frame.grid(row=2, column=0, columnspan=3, pady=(20, 0))

        ttk.Button(btn_frame, text='&#128260; 重新生成', command=regenerate_password, width=15).pack(side=tk.LEFT, padx=10)
        ttk.Button(btn_frame, text='&#128203; 复制并关闭', command=copy_to_clipboard, width=15).pack(side=tk.LEFT, padx=10)
        ttk.Button(btn_frame, text='&#10060; 关闭', command=dialog.destroy, width=15).pack(side=tk.LEFT, padx=10)

    def bind_activity_events(self):
        """绑定用户活动事件"""
        def on_activity(event=None):
            self.password_manager.auto_lock_manager.update_activity()

        # 绑定各种用户活动事件
        self.root.bind('<Motion>', on_activity)
        self.root.bind('<Button>', on_activity)
        self.root.bind('<Key>', on_activity)
        self.root.bind('<MouseWheel>', on_activity)

        # 绑定到所有子组件
        def bind_to_children(widget):
            widget.bind('<Motion>', on_activity, add='+')
            widget.bind('<Button>', on_activity, add='+')
            widget.bind('<Key>', on_activity, add='+')
            for child in widget.winfo_children():
                bind_to_children(child)

        bind_to_children(self.root)

    def bind_shortcuts(self):
        """绑定快捷键"""
        # 获取快捷键管理器并确保已初始化
        sm = self.password_manager.shortcut_manager

        # 确保快捷键管理器已经初始化
        if sm is None:
            # 如果快捷键管理器未初始化,创建一个新的
            sm = ShortcutManager(self.password_manager.db_path)
            self.password_manager.shortcut_manager = sm

        # 绑定动态快捷键
        shortcuts = {
            'add_password': lambda e: self.add_password_dialog(),
            'view_password': lambda e: self.view_password() if self.password_tree.selection() else None,
            'edit_password': lambda e: self.update_password_dialog(),
            'delete_password': lambda e: self.delete_password(),
            'generate_password': lambda e: self.generate_password(),
            'search': lambda e: self.search_entry.focus(),
            'lock_app': lambda e: self.lock_application(),
            'import_csv': lambda e: self.import_csv(),
            'export_csv': lambda e: self.export_csv(),
            'reset_search': lambda e: self.reset_search(),
            'refresh': lambda e: self.update_password_list(),
            'copy_password': self.copy_selected_password,
            'toggle_floating': lambda e: self.toggle_floating_mode()
        }

        for action, callback in shortcuts.items():
            shortcut = sm.get_shortcut(action)
            if shortcut:
                binding = sm.format_shortcut_binding(shortcut)
                if binding:
                    try:
                        self.root.bind(binding, callback)
                    except tk.TclError:
                        pass

        # 固定快捷键(不可自定义)
        self.root.bind('<Escape>', lambda e: self.password_tree.selection_remove(self.password_tree.selection()))
        self.root.bind('<Return>', lambda e: self.view_password() if self.password_tree.selection() else None)
        self.root.bind('<F2>', lambda e: self.show_text_manager())  # F2打开文本管理器

    def copy_selected_password(self, event=None):
        """复制选中项的密码"""
        selected_item = self.password_tree.selection()
        if selected_item:
            item = self.password_tree.item(selected_item[0])
            service, username = item['values'][1], item['values'][2]
            password = self.password_manager.get_password(service, username)
            if password:
                pyperclip.copy(password)
                self.status_var.set(f'已复制 {service} 的密码到剪贴板')
                # 3秒后恢复状态栏
                self.root.after(3000, lambda: self.status_var.set('就绪'))
            else:
                messagebox.showerror('错误', '无法获取密码!')

    def show_context_menu(self, event):
        """显示右键菜单"""
        # 选中右键点击的项
        item = self.password_tree.identify_row(event.y)
        if item:
            self.password_tree.selection_set(item)

            # 创建右键菜单
            context_menu = tk.Menu(self.root, tearoff=0)
            context_menu.add_command(label="&#128065;&#65039; 查看密码", command=self.view_password)
            context_menu.add_command(label="&#9999;&#65039; 编辑密码", command=self.update_password_dialog)
            context_menu.add_command(label="&#128203; 复制密码", command=self.copy_selected_password)
            context_menu.add_separator()

            # 如果有URL,添加打开链接选项
            selected_item = self.password_tree.selection()
            if selected_item:
                item_values = self.password_tree.item(selected_item[0])['values']
                if len(item_values) > 3 and item_values[3]:  # 有URL
                    context_menu.add_command(label="&#127760; 打开链接",
                                           command=lambda: webbrowser.open(item_values[3]))
                    context_menu.add_separator()

            context_menu.add_command(label="&#128465;&#65039; 删除密码", command=self.delete_password)

            # 显示菜单
            try:
                context_menu.tk_popup(event.x_root, event.y_root)
            finally:
                context_menu.grab_release()
        else:
            # 空白区域右键菜单
            context_menu = tk.Menu(self.root, tearoff=0)
            context_menu.add_command(label="&#10133; 添加密码", command=self.add_password_dialog)
            context_menu.add_command(label="&#127922; 生成密码", command=self.generate_password)
            context_menu.add_separator()
            context_menu.add_command(label="&#128260; 刷新列表", command=self.update_password_list)

            try:
                context_menu.tk_popup(event.x_root, event.y_root)
            finally:
                context_menu.grab_release()

    def lock_application(self):
        """锁定应用程序"""
        if self.password_manager.is_locked:
            return

        self.password_manager.is_locked = True

        # 创建锁定对话框
        lock_dialog = tk.Toplevel(self.root)
        lock_dialog.title('&#128274; 应用程序已锁定')
        lock_dialog.geometry('450x250')
        lock_dialog.resizable(False, False)
        lock_dialog.transient(self.root)
        lock_dialog.grab_set()
        lock_dialog.configure(bg='#f8f9fa')

        # 禁用主窗口
        self.root.withdraw()

        # 居中显示
        lock_dialog.update_idletasks()
        width = lock_dialog.winfo_width()
        height = lock_dialog.winfo_height()
        x = (lock_dialog.winfo_screenwidth() // 2) - (width // 2)
        y = (lock_dialog.winfo_screenheight() // 2) - (height // 2)
        lock_dialog.geometry('{}x{}+{}+{}'.format(width, height, x, y))

        frame = ttk.Frame(lock_dialog, padding='30')
        frame.pack(fill=tk.BOTH, expand=True)

        # 锁定图标和提示
        ttk.Label(frame, text='&#128274;', font=('Microsoft YaHei UI', 48)).pack(pady=(0, 10))
        ttk.Label(frame, text='应用程序已自动锁定', font=('Microsoft YaHei UI', 14, 'bold')).pack(pady=5)
        ttk.Label(frame, text='请输入主密码解锁', font=('Microsoft YaHei UI', 10)).pack(pady=5)

        # 获取解锁快捷键
        unlock_shortcut = self.password_manager.shortcut_manager.get_shortcut('unlock_app')
        ttk.Label(frame, text=f'或按 {unlock_shortcut} 快速解锁',
                 font=('Microsoft YaHei UI', 9), foreground='#666666').pack(pady=2)

        # 密码输入
        password_var = tk.StringVar()
        password_entry = ttk.Entry(frame, textvariable=password_var, show='*', width=30, font=('Microsoft YaHei UI', 10))
        password_entry.pack(pady=10)
        password_entry.focus()

        def unlock():
            password = password_var.get()
            if password and hashlib.sha256(password.encode()).hexdigest() == self.password_manager.master_password_hash:
                self.password_manager.is_locked = False
                self.password_manager.auto_lock_manager.update_activity()
                lock_dialog.destroy()
                self.root.deiconify()
            else:
                messagebox.showerror('错误', '密码错误!')
                password_var.set('')
                password_entry.focus()

        password_entry.bind('<Return>', lambda e: unlock())

        # 绑定解锁快捷键
        def handle_unlock_shortcut(event):
            """处理解锁快捷键"""
            unlock()

        # 绑定快捷键到对话框
        unlock_shortcut = self.password_manager.shortcut_manager.get_shortcut('unlock_app')
        if unlock_shortcut:
            try:
                lock_dialog.bind(f'<{unlock_shortcut}>', handle_unlock_shortcut)
                # 确保对话框能接收键盘事件
                lock_dialog.focus_set()
            except tk.TclError:
                pass  # 如果快捷键格式不正确,忽略错误

        btn_frame = ttk.Frame(frame)
        btn_frame.pack(pady=10)

        ttk.Button(btn_frame, text='&#128275; 解锁', command=unlock, width=15).pack(side=tk.LEFT, padx=5)
        ttk.Button(btn_frame, text='&#10060; 退出', command=self.root.quit, width=15).pack(side=tk.LEFT, padx=5)

    def export_csv(self):
        """导出CSV对话框"""
        file_path = filedialog.asksaveasfilename(
            title="导出密码数据",
            defaultextension=".csv",
            filetypes=[("CSV文件", "*.csv"), ("所有文件", "*.*")]
        )

        if file_path:
            success, message = self.password_manager.export_to_csv(file_path)
            if success:
                messagebox.showinfo("导出成功", message)
            else:
                messagebox.showerror("导出失败", message)

    def import_csv(self):
        """导入CSV对话框"""
        file_path = filedialog.askopenfilename(
            title="导入密码数据",
            filetypes=[("CSV文件", "*.csv"), ("所有文件", "*.*")]
        )

        if file_path:
            confirm = messagebox.askyesno("确认导入",
                "导入操作将添加新的密码记录。\n已存在的记录将被跳过。\n是否继续?")
            if confirm:
                success, message = self.password_manager.import_from_csv(file_path)
                if success:
                    messagebox.showinfo("导入完成", message)
                    self.update_password_list()
                else:
                    messagebox.showerror("导入失败", message)

    def check_all_passwords_strength(self):
        """检查所有密码强度"""
        passwords = self.password_manager.get_all_passwords()
        weak_passwords = []

        for password_info in passwords:
            service, username = password_info[1], password_info[2]
            password = self.password_manager.get_password(service, username)
            if password:
                score, level, color, feedback = self.password_manager.strength_checker.check_strength(password)
                if score < 60:  # 强度低于60分的密码
                    weak_passwords.append((service, username, level, score))

        if weak_passwords:
            # 显示弱密码报告
            report_dialog = tk.Toplevel(self.root)
            report_dialog.title('&#128269; 密码强度报告')
            report_dialog.geometry('600x400')
            report_dialog.transient(self.root)
            report_dialog.grab_set()

            frame = ttk.Frame(report_dialog, padding='20')
            frame.pack(fill=tk.BOTH, expand=True)

            ttk.Label(frame, text=f'发现 {len(weak_passwords)} 个弱密码',
                     font=('Microsoft YaHei UI', 14, 'bold')).pack(pady=(0, 10))

            # 创建列表
            columns = ('服务名', '用户名', '强度等级', '分数')
            tree = ttk.Treeview(frame, columns=columns, show='headings', height=15)

            for col in columns:
                tree.heading(col, text=col)
                tree.column(col, width=120)

            for weak in weak_passwords:
                tree.insert('', tk.END, values=weak)

            tree.pack(fill=tk.BOTH, expand=True, pady=10)

            ttk.Button(frame, text='关闭', command=report_dialog.destroy).pack(pady=10)
        else:
            messagebox.showinfo("密码强度检查", "所有密码强度都很好!")

    def check_expired_passwords(self):
        """检查过期密码"""
        messagebox.showinfo("功能开发中", "密码过期检查功能正在开发中...")

    def manage_categories(self):
        """管理分类"""
        messagebox.showinfo("功能开发中", "分类管理功能正在开发中...")

    def show_help(self):
        """显示帮助"""
        help_text = """
&#128272; 安全密码管理器 使用说明

主要功能:
&#8226; 添加、查看、编辑、删除密码
&#8226; 自动生成强密码
&#8226; 密码强度检测
&#8226; 数据导入导出(CSV格式)
&#8226; 自动锁定保护

快捷操作:
&#8226; 双击URL列可直接打开链接
&#8226; 应用会在15分钟无操作后自动锁定
&#8226; 支持CSV格式的数据导入导出

安全特性:
&#8226; 使用AES-GCM加密存储密码
&#8226; 配置文件使用机器特征加密
&#8226; 密码强度实时检测
&#8226; 自动锁定防止未授权访问
        """

        help_dialog = tk.Toplevel(self.root)
        help_dialog.title('&#128214; 使用说明')
        help_dialog.geometry('500x400')
        help_dialog.transient(self.root)

        frame = ttk.Frame(help_dialog, padding='20')
        frame.pack(fill=tk.BOTH, expand=True)

        text_widget = tk.Text(frame, wrap=tk.WORD, font=('Microsoft YaHei UI', 10))
        text_widget.insert(tk.END, help_text)
        text_widget.config(state=tk.DISABLED)
        text_widget.pack(fill=tk.BOTH, expand=True)

        ttk.Button(frame, text='关闭', command=help_dialog.destroy).pack(pady=10)

    def show_about(self):
        """显示关于信息"""
        about_text = """
&#128272; 安全密码管理器 v1.0

一个功能强大的本地密码管理工具

主要特性:
&#9989; 军用级AES加密
&#9989; 密码强度检测
&#9989; 自动锁定保护
&#9989; 数据导入导出
&#9989; 现代化界面设计

开发信息:
&#8226; 基于Python + Tkinter
&#8226; 使用SQLite数据库
&#8226; 采用AES-GCM加密算法

版权所有 &#169; 2025
        """

        messagebox.showinfo("关于", about_text)

    def backup_key_file(self):
        """备份密钥文件"""
        if not os.path.exists(self.password_manager.key_path):
            messagebox.showerror("备份失败", "密钥文件不存在!")
            return

        file_path = filedialog.asksaveasfilename(
            title="备份密钥文件",
            defaultextension=".key",
            filetypes=[("密钥文件", "*.key"), ("所有文件", "*.*")],
            initialfile=f"secret_backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}.key"
        )

        if file_path:
            try:
                import shutil
                shutil.copy2(self.password_manager.key_path, file_path)
                messagebox.showinfo("备份成功", f"密钥文件已备份到:\n{file_path}\n\n请妥善保管此文件!")
            except Exception as e:
                messagebox.showerror("备份失败", f"无法备份密钥文件:{str(e)}")

    def restore_key_file(self):
        """恢复密钥文件"""
        confirm = messagebox.askyesno("确认恢复",
            "恢复密钥文件将覆盖当前的密钥文件。\n确定要继续吗?")
        if not confirm:
            return

        file_path = filedialog.askopenfilename(
            title="选择密钥文件备份",
            filetypes=[("密钥文件", "*.key"), ("所有文件", "*.*")]
        )

        if file_path:
            try:
                import shutil
                # 备份当前密钥文件
                if os.path.exists(self.password_manager.key_path):
                    backup_name = f"secret_old_{datetime.now().strftime('%Y%m%d_%H%M%S')}.key"
                    shutil.copy2(self.password_manager.key_path, backup_name)

                # 恢复密钥文件
                shutil.copy2(file_path, self.password_manager.key_path)
                messagebox.showinfo("恢复成功", "密钥文件已恢复!\n程序将重新启动以应用更改。")

                # 重新启动程序
                self.root.quit()

            except Exception as e:
                messagebox.showerror("恢复失败", f"无法恢复密钥文件:{str(e)}")

    def show_key_recovery_guide(self):
        """显示密钥恢复指南"""
        guide_dialog = tk.Toplevel(self.root)
        guide_dialog.title('&#128273; 密钥文件恢复指南')
        guide_dialog.geometry('700x500')
        guide_dialog.transient(self.root)

        frame = ttk.Frame(guide_dialog, padding='20')
        frame.pack(fill=tk.BOTH, expand=True)

        # 标题
        ttk.Label(frame, text='&#128273; 密钥文件恢复指南',
                 font=('Microsoft YaHei UI', 16, 'bold')).pack(pady=(0, 15))

        # 创建滚动文本框
        text_frame = ttk.Frame(frame)
        text_frame.pack(fill=tk.BOTH, expand=True)

        text_widget = tk.Text(text_frame, wrap=tk.WORD, font=('Microsoft YaHei UI', 10))
        scrollbar = ttk.Scrollbar(text_frame, orient=tk.VERTICAL, command=text_widget.yview)
        text_widget.configure(yscrollcommand=scrollbar.set)

        # 指南内容
        guide_content = """
&#128737;&#65039; 预防措施

1. 定期备份密钥文件
   &#8226; 使用菜单:文件 → &#128190; 备份密钥文件
   &#8226; 保存到安全位置(云存储、U盘等)
   &#8226; 建议每月备份一次

2. 多重备份策略
   &#8226; 本地备份:不同硬盘分区
   &#8226; 云端备份:云存储服务
   &#8226; 物理备份:U盘或移动硬盘

&#9888;&#65039; 密钥文件丢失时的恢复选项

选项1:&#128273; 使用主密码恢复
&#8226; 适用:记得主密码的情况
&#8226; 步骤:输入主密码 → 重新生成密钥文件
&#8226; 优点:完全恢复访问权限

选项2:&#128193; 从备份恢复
&#8226; 适用:有密钥文件备份
&#8226; 步骤:选择备份文件 → 自动恢复
&#8226; 优点:最安全可靠的方式

选项3:&#127381; 创建新数据库
&#8226; 适用:无法通过其他方式恢复
&#8226; 注意:原有数据将无法访问

&#128295; 手动恢复方法

方法1:菜单恢复
文件 → &#128260; 恢复密钥文件 → 选择备份

方法2:手动复制
将备份文件重命名为 secret.key → 复制到程序目录

&#128203; 最佳实践

&#8226; 定期备份(每次重要修改后)
&#8226; 安全存储(不同位置、加密保护)
&#8226; 记录管理(位置、日期、命名规范)

&#128680; 紧急情况

如果忘记主密码且没有备份:
1. 尝试回忆密码变体
2. 检查其他设备备份
3. 最后选择创建新数据库

&#128274; 安全提醒

&#8226; 密钥文件是访问密码的唯一凭证
&#8226; 不要通过不安全渠道传输
&#8226; 定期更换主密码
&#8226; 使用强密码作为主密码

重要:请务必做好密钥文件的备份工作!
        """

        text_widget.insert(tk.END, guide_content)
        text_widget.config(state=tk.DISABLED)

        text_widget.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        # 按钮框架
        btn_frame = ttk.Frame(frame)
        btn_frame.pack(pady=15)

        ttk.Button(btn_frame, text='&#128190; 立即备份密钥', command=self.backup_key_file, width=20).pack(side=tk.LEFT, padx=10)
        ttk.Button(btn_frame, text='关闭', command=guide_dialog.destroy, width=15).pack(side=tk.LEFT, padx=10)

    def change_theme(self, theme_name):
        """切换主题"""
        if theme_name == 'light':
            # 浅色主题
            self.root.configure(bg='#f8f9fa')
            self.style.configure('TLabel', background='#f8f9fa', foreground='#212529')
            self.style.configure('TFrame', background='#f8f9fa')
            self.style.configure('Treeview', background='white', foreground='#212529')
            self.style.configure('Title.TLabel', background='#f8f9fa', foreground='#2c3e50')
            # 按钮样式
            self.style.configure('TButton',
                               background='#ffffff',
                               foreground='#212529',
                               borderwidth=1,
                               focuscolor='none')
            self.style.map('TButton',
                         background=[('active', '#e9ecef'), ('pressed', '#dee2e6')],
                         foreground=[('active', '#212529'), ('pressed', '#212529')])

        elif theme_name == 'dark':
            # 深色主题
            self.root.configure(bg='#2b2b2b')
            self.style.configure('TLabel', background='#2b2b2b', foreground='#ffffff')
            self.style.configure('TFrame', background='#2b2b2b')
            self.style.configure('Treeview', background='#3c3c3c', foreground='#ffffff')
            self.style.configure('Title.TLabel', background='#2b2b2b', foreground='#ffffff')
            # 按钮样式
            self.style.configure('TButton',
                               background='#404040',
                               foreground='#ffffff',
                               borderwidth=1,
                               focuscolor='none')
            self.style.map('TButton',
                         background=[('active', '#505050'), ('pressed', '#606060')],
                         foreground=[('active', '#ffffff'), ('pressed', '#ffffff')])

        elif theme_name == 'blue':
            # 蓝色主题
            self.root.configure(bg='#e3f2fd')
            self.style.configure('TLabel', background='#e3f2fd', foreground='#0d47a1')
            self.style.configure('TFrame', background='#e3f2fd')
            self.style.configure('Treeview', background='#f3e5f5', foreground='#0d47a1')
            self.style.configure('Title.TLabel', background='#e3f2fd', foreground='#1565c0')
            # 按钮样式
            self.style.configure('TButton',
                               background='#bbdefb',
                               foreground='#0d47a1',
                               borderwidth=1,
                               focuscolor='none')
            self.style.map('TButton',
                         background=[('active', '#90caf9'), ('pressed', '#64b5f6')],
                         foreground=[('active', '#0d47a1'), ('pressed', '#0d47a1')])

        elif theme_name == 'green':
            # 绿色主题(护眼绿色调)
            self.root.configure(bg='#e8f5e8')
            self.style.configure('TLabel', background='#e8f5e8', foreground='#1b5e20')
            self.style.configure('TFrame', background='#e8f5e8')
            self.style.configure('Treeview', background='#f1f8e9', foreground='#2e7d32')
            self.style.configure('Title.TLabel', background='#e8f5e8', foreground='#388e3c')
            # 按钮样式
            self.style.configure('TButton',
                               background='#c8e6c9',
                               foreground='#1b5e20',
                               borderwidth=1,
                               focuscolor='none')
            self.style.map('TButton',
                         background=[('active', '#a5d6a7'), ('pressed', '#81c784')],
                         foreground=[('active', '#1b5e20'), ('pressed', '#1b5e20')])

        elif theme_name == 'purple':
            # 紫色主题(优雅紫色调)
            self.root.configure(bg='#f3e5f5')
            self.style.configure('TLabel', background='#f3e5f5', foreground='#4a148c')
            self.style.configure('TFrame', background='#f3e5f5')
            self.style.configure('Treeview', background='#fce4ec', foreground='#6a1b9a')
            self.style.configure('Title.TLabel', background='#f3e5f5', foreground='#7b1fa2')
            # 按钮样式
            self.style.configure('TButton',
                               background='#e1bee7',
                               foreground='#4a148c',
                               borderwidth=1,
                               focuscolor='none')
            self.style.map('TButton',
                         background=[('active', '#ce93d8'), ('pressed', '#ba68c8')],
                         foreground=[('active', '#4a148c'), ('pressed', '#4a148c')])

        elif theme_name == 'orange':
            # 橙色主题(温暖橙色调)
            self.root.configure(bg='#fff3e0')
            self.style.configure('TLabel', background='#fff3e0', foreground='#e65100')
            self.style.configure('TFrame', background='#fff3e0')
            self.style.configure('Treeview', background='#fef7f0', foreground='#f57c00')
            self.style.configure('Title.TLabel', background='#fff3e0', foreground='#ff9800')
            # 按钮样式
            self.style.configure('TButton',
                               background='#ffcc80',
                               foreground='#e65100',
                               borderwidth=1,
                               focuscolor='none')
            self.style.map('TButton',
                         background=[('active', '#ffb74d'), ('pressed', '#ffa726')],
                         foreground=[('active', '#e65100'), ('pressed', '#e65100')])

        elif theme_name == 'high_contrast':
            # 高对比度主题(适合视力不佳用户)
            self.root.configure(bg='#ffffff')
            self.style.configure('TLabel', background='#ffffff', foreground='#000000')
            self.style.configure('TFrame', background='#ffffff')
            self.style.configure('Treeview', background='#ffffff', foreground='#000000')
            self.style.configure('Title.TLabel', background='#ffffff', foreground='#000000')
            # 设置高对比度的选中颜色
            self.style.configure('Treeview', selectbackground='#000000', selectforeground='#ffffff')
            # 按钮样式
            self.style.configure('TButton',
                               background='#ffffff',
                               foreground='#000000',
                               borderwidth=2,
                               relief='solid',
                               focuscolor='none')
            self.style.map('TButton',
                         background=[('active', '#000000'), ('pressed', '#333333')],
                         foreground=[('active', '#ffffff'), ('pressed', '#ffffff')])

        elif theme_name == 'night':
            # 夜间模式主题(深黑背景,减少蓝光)
            self.root.configure(bg='#0d1117')
            self.style.configure('TLabel', background='#0d1117', foreground='#f0f6fc')
            self.style.configure('TFrame', background='#0d1117')
            self.style.configure('Treeview', background='#161b22', foreground='#f0f6fc')
            self.style.configure('Title.TLabel', background='#0d1117', foreground='#58a6ff')
            # 设置夜间模式的选中颜色
            self.style.configure('Treeview', selectbackground='#21262d', selectforeground='#f0f6fc')
            # 按钮样式
            self.style.configure('TButton',
                               background='#21262d',
                               foreground='#f0f6fc',
                               borderwidth=1,
                               focuscolor='none')
            self.style.map('TButton',
                         background=[('active', '#30363d'), ('pressed', '#484f58')],
                         foreground=[('active', '#f0f6fc'), ('pressed', '#f0f6fc')])

        # 保存主题设置
        try:
            with open(os.path.join(os.getcwd(), 'theme.json'), 'w') as f:
                json.dump({'theme': theme_name}, f)
        except:
            pass

    def load_theme(self):
        """加载保存的主题"""
        try:
            theme_file = os.path.join(os.getcwd(), 'theme.json')
            if os.path.exists(theme_file):
                with open(theme_file, 'r') as f:
                    theme_data = json.load(f)
                    self.change_theme(theme_data.get('theme', 'blue'))
        except:
            self.change_theme('blue')  # 默认蓝色主题

    def sort_column(self, col):
        """列排序功能"""
        # 获取当前数据
        data = [(self.password_tree.set(child, col), child) for child in self.password_tree.get_children('')]

        # 排序
        reverse = self.sort_reverse[col]
        if col in ['ID']:
            # 数字排序
            data.sort(key=lambda x: int(x[0]) if x[0].isdigit() else 0, reverse=reverse)
        elif col in ['创建时间', '更新时间']:
            # 时间排序
            data.sort(key=lambda x: x[0], reverse=reverse)
        else:
            # 文本排序
            data.sort(key=lambda x: x[0].lower(), reverse=reverse)

        # 重新排列
        for index, (val, child) in enumerate(data):
            self.password_tree.move(child, '', index)

        # 更新排序状态
        self.sort_reverse[col] = not reverse

        # 更新列标题显示排序方向
        for column in self.password_tree['columns']:
            if column == col:
                direction = ' ↓' if reverse else ' ↑'
                self.password_tree.heading(column, text=column + direction)
            else:
                self.password_tree.heading(column, text=column)

    def show_shortcut_settings(self):
        """显示快捷键设置对话框"""
        dialog = tk.Toplevel(self.root)
        dialog.title('&#9000;&#65039; 快捷键设置')
        dialog.geometry('600x500')
        dialog.resizable(False, False)
        dialog.transient(self.root)
        dialog.grab_set()
        dialog.configure(bg='#f8f9fa')

        # 居中显示
        dialog.update_idletasks()
        width = dialog.winfo_width()
        height = dialog.winfo_height()
        x = (dialog.winfo_screenwidth() // 2) - (width // 2)
        y = (dialog.winfo_screenheight() // 2) - (height // 2)
        dialog.geometry('{}x{}+{}+{}'.format(width, height, x, y))

        # 创建主框架
        main_frame = ttk.Frame(dialog, padding='20')
        main_frame.pack(fill=tk.BOTH, expand=True)

        # 标题
        title_label = ttk.Label(main_frame, text='&#9000;&#65039; 快捷键设置',
                               font=('Microsoft YaHei UI', 16, 'bold'),
                               foreground='#2c3e50')
        title_label.pack(pady=(0, 20))

        # 说明文字
        info_label = ttk.Label(main_frame,
                              text='点击快捷键输入框,然后按下新的组合键来设置快捷键',
                              font=('Microsoft YaHei UI', 10),
                              foreground='#7f8c8d')
        info_label.pack(pady=(0, 15))

        # 创建滚动框架
        canvas = tk.Canvas(main_frame, bg='#f8f9fa', highlightthickness=0)
        scrollbar = ttk.Scrollbar(main_frame, orient="vertical", command=canvas.yview)
        scrollable_frame = ttk.Frame(canvas)

        scrollable_frame.bind(
            "<Configure>",
            lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
        )

        canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
        canvas.configure(yscrollcommand=scrollbar.set)

        # 快捷键设置项
        shortcut_entries = {}
        action_names = {
            'add_password': '添加密码',
            'view_password': '查看密码',
            'edit_password': '编辑密码',
            'delete_password': '删除密码',
            'generate_password': '生成密码',
            'search': '搜索',
            'lock_app': '锁定应用',
            'unlock_app': '解锁应用',
            'import_csv': '导入CSV',
            'export_csv': '导出CSV',
            'reset_search': '重置搜索',
            'refresh': '刷新',
            'copy_password': '复制密码',
            'toggle_floating': '悬浮窗模式'
        }

        row = 0
        for action, name in action_names.items():
            # 功能名称
            name_label = ttk.Label(scrollable_frame, text=name,
                                  font=('Microsoft YaHei UI', 10, 'bold'))
            name_label.grid(row=row, column=0, sticky=tk.W, padx=(0, 20), pady=8)

            # 快捷键输入框
            shortcut_var = tk.StringVar(value=self.password_manager.shortcut_manager.get_shortcut(action))
            shortcut_entry = ttk.Entry(scrollable_frame, textvariable=shortcut_var,
                                     width=15, font=('Microsoft YaHei UI', 10))
            shortcut_entry.grid(row=row, column=1, padx=(0, 10), pady=8)

            # 绑定键盘事件
            shortcut_entry.bind('<KeyPress>', lambda e, action=action, var=shortcut_var:
                               self.capture_shortcut(e, action, var, dialog))
            shortcut_entry.bind('<FocusIn>', lambda e: e.widget.select_range(0, tk.END))

            shortcut_entries[action] = shortcut_var

            # 清除按钮
            clear_btn = ttk.Button(scrollable_frame, text='清除', width=8,
                                  command=lambda a=action, v=shortcut_var: v.set(''))
            clear_btn.grid(row=row, column=2, padx=5, pady=8)

            row += 1

        canvas.pack(side="left", fill="both", expand=True)
        scrollbar.pack(side="right", fill="y")

        # 按钮框架
        btn_frame = ttk.Frame(main_frame)
        btn_frame.pack(fill=tk.X, pady=(20, 0))

        def save_shortcuts():
            """保存快捷键设置"""
            # 检查冲突
            conflicts = []
            for action1, var1 in shortcut_entries.items():
                shortcut1 = var1.get().strip()
                if shortcut1:
                    for action2, var2 in shortcut_entries.items():
                        if action1 != action2 and var2.get().strip() == shortcut1:
                            conflicts.append(f"{action_names[action1]} 和 {action_names[action2]} 使用了相同的快捷键: {shortcut1}")

            if conflicts:
                messagebox.showerror('快捷键冲突', '\n'.join(conflicts))
                return

            # 保存设置
            for action, var in shortcut_entries.items():
                self.password_manager.shortcut_manager.set_shortcut(action, var.get().strip())

            if self.password_manager.shortcut_manager.save_shortcuts():
                messagebox.showinfo('成功', '快捷键设置已保存!')
                self.update_shortcuts()  # 更新快捷键绑定
                dialog.destroy()
            else:
                messagebox.showerror('错误', '保存快捷键设置失败!')

        def reset_defaults():
            """重置为默认快捷键"""
            if messagebox.askyesno('确认', '确定要重置为默认快捷键吗?'):
                for action, var in shortcut_entries.items():
                    default_shortcut = self.password_manager.shortcut_manager.default_shortcuts.get(action, '')
                    var.set(default_shortcut)

        # 按钮
        ttk.Button(btn_frame, text='&#128190; 保存', command=save_shortcuts, width=15).pack(side=tk.LEFT, padx=5)
        ttk.Button(btn_frame, text='&#128260; 重置默认', command=reset_defaults, width=15).pack(side=tk.LEFT, padx=5)
        ttk.Button(btn_frame, text='&#10060; 取消', command=dialog.destroy, width=15).pack(side=tk.RIGHT, padx=5)

    def capture_shortcut(self, event, action, var, dialog):
        """捕获快捷键输入"""
        # 阻止默认行为
        event.widget.focus_set()

        # 构建快捷键字符串
        modifiers = []
        if event.state & 0x4:  # Control
            modifiers.append('Ctrl')
        if event.state & 0x8:  # Alt
            modifiers.append('Alt')
        if event.state & 0x1:  # Shift
            modifiers.append('Shift')

        key = event.keysym

        # 特殊键处理
        if key in ['Control_L', 'Control_R', 'Alt_L', 'Alt_R', 'Shift_L', 'Shift_R']:
            return "break"

        # 功能键
        if key.startswith('F') and key[1:].isdigit():
            shortcut = key
        elif len(key) == 1 and key.isalnum():
            if modifiers:
                shortcut = '+'.join(modifiers) + '+' + key.upper()
            else:
                shortcut = key.upper()
        else:
            return "break"

        var.set(shortcut)
        return "break"

    def update_shortcuts(self):
        """更新快捷键绑定"""
        # 清除所有现有绑定
        self.root.unbind_all('<Alt-q>')
        self.root.unbind_all('<Alt-w>')
        self.root.unbind_all('<Alt-e>')
        self.root.unbind_all('<Alt-d>')
        self.root.unbind_all('<Alt-g>')
        self.root.unbind_all('<Alt-f>')
        self.root.unbind_all('<Alt-l>')
        self.root.unbind_all('<Alt-i>')
        self.root.unbind_all('<Alt-o>')
        self.root.unbind_all('<Alt-r>')
        self.root.unbind_all('<F5>')
        self.root.unbind_all('<Alt-c>')
        self.root.unbind_all('<Alt-Escape>')

        # 重新绑定快捷键
        self.bind_shortcuts()

    def toggle_floating_mode(self):
        """切换悬浮窗模式"""
        if self.is_floating:
            self.exit_floating_mode()
        else:
            self.enter_floating_mode()

    def enter_floating_mode(self):
        """进入悬浮窗模式"""
        if self.is_floating:
            return

        # 保存主窗口位置
        self.main_window_geometry = self.root.geometry()

        # 隐藏主窗口
        self.root.withdraw()

        # 创建悬浮窗
        self.create_floating_window()

        self.is_floating = True

    def exit_floating_mode(self):
        """退出悬浮窗模式"""
        if not self.is_floating:
            return

        # 保存悬浮窗位置
        if self.floating_window:
            self.save_floating_position()
            self.floating_window.destroy()
            self.floating_window = None

        # 恢复主窗口
        self.root.deiconify()
        if hasattr(self, 'main_window_geometry'):
            self.root.geometry(self.main_window_geometry)

        self.is_floating = False

    def create_floating_window(self):
        """创建悬浮窗"""
        self.floating_window = tk.Toplevel()
        self.floating_window.title('')  # 无标题
        self.floating_window.overrideredirect(True)  # 无边框

        # 初始为小球状态
        self.floating_expanded = False
        self.floating_window.geometry('60x60')

        # 设置窗口属性
        self.floating_window.attributes('-topmost', True)  # 置顶
        self.floating_window.attributes('-alpha', 0.8)     # 半透明

        # 设置位置
        self.floating_window.geometry(f"60x60+{self.floating_position['x']}+{self.floating_position['y']}")

        # 创建悬浮窗内容
        self.create_floating_content()

        # 绑定拖拽事件
        self.bind_floating_drag()

    def create_floating_content(self):
        """创建悬浮窗内容"""
        # 清空现有内容
        for widget in self.floating_window.winfo_children():
            widget.destroy()

        if not self.floating_expanded:
            # 小球状态 - 只显示一个圆形按钮
            self.create_ball_content()
        else:
            # 展开状态 - 显示完整功能
            self.create_expanded_content()

    def create_ball_content(self):
        """创建小球状态的内容"""
        # 创建圆形按钮
        ball_frame = tk.Frame(self.floating_window, bg='#4CAF50', width=60, height=60)
        ball_frame.pack_propagate(False)
        ball_frame.pack(fill=tk.BOTH, expand=True)

        # 圆形效果(通过设置圆角边框模拟)
        ball_frame.configure(relief='raised', bd=2)

        # 中心图标
        icon_label = tk.Label(ball_frame, text='&#128272;', font=('Microsoft YaHei UI', 20),
                             bg='#4CAF50', fg='white', cursor='hand2')
        icon_label.place(relx=0.5, rely=0.5, anchor='center')

        # 绑定点击事件
        def toggle_expand(event=None):
            self.toggle_floating_expand()

        ball_frame.bind('<Button-1>', toggle_expand)
        icon_label.bind('<Button-1>', toggle_expand)

        # 绑定右键菜单
        def show_context_menu(event):
            context_menu = tk.Menu(self.floating_window, tearoff=0)
            context_menu.add_command(label="&#128214; 展开", command=self.toggle_floating_expand)
            context_menu.add_command(label="&#10133; 添加密码", command=self.floating_add_password)
            context_menu.add_separator()
            context_menu.add_command(label="&#128281; 返回主窗口", command=self.exit_floating_mode)

            try:
                context_menu.tk_popup(event.x_root, event.y_root)
            finally:
                context_menu.grab_release()

        ball_frame.bind('<Button-3>', show_context_menu)
        icon_label.bind('<Button-3>', show_context_menu)

    def create_expanded_content(self):
        """创建展开状态的内容"""
        # 主框架
        main_frame = tk.Frame(self.floating_window, bg='#f0f0f0', padx=10, pady=10)
        main_frame.pack(fill=tk.BOTH, expand=True)

        # 标题栏
        title_frame = tk.Frame(main_frame, bg='#f0f0f0')
        title_frame.pack(fill=tk.X, pady=(0, 10))

        title_label = tk.Label(title_frame, text='&#128272; 密码管理器',
                              font=('Microsoft YaHei UI', 10, 'bold'), bg='#f0f0f0')
        title_label.pack(side=tk.LEFT)

        # 收起按钮
        collapse_btn = tk.Button(title_frame, text='&#128214;', font=('Microsoft YaHei UI', 8),
                                command=self.toggle_floating_expand, bg='#ff9800', fg='white',
                                width=3, height=1, relief='flat', cursor='hand2')
        collapse_btn.pack(side=tk.RIGHT)

        # 快速搜索
        search_frame = tk.Frame(main_frame, bg='#f0f0f0')
        search_frame.pack(fill=tk.X, pady=(0, 10))

        self.floating_search_var = tk.StringVar()
        search_entry = tk.Entry(search_frame, textvariable=self.floating_search_var,
                               font=('Microsoft YaHei UI', 9), width=25)
        search_entry.pack(fill=tk.X)
        search_entry.bind('<Return>', lambda e: self.floating_search())

        # 功能选择标签页
        tab_frame = tk.Frame(main_frame, bg='#f0f0f0')
        tab_frame.pack(fill=tk.X, pady=(0, 10))

        # 标签页按钮 - 存储为实例变量以便后续更新
        self.password_tab_btn = tk.Button(tab_frame, text='&#128272; 密码',
                                         command=lambda: self.switch_floating_tab('passwords'),
                                         font=('Microsoft YaHei UI', 8), width=10,
                                         bg='#2196F3' if self.floating_current_tab == 'passwords' else '#e0e0e0',
                                         fg='white' if self.floating_current_tab == 'passwords' else 'black')
        self.password_tab_btn.pack(side=tk.LEFT, padx=2)

        self.text_tab_btn = tk.Button(tab_frame, text='&#128221; 文本',
                                     command=lambda: self.switch_floating_tab('texts'),
                                     font=('Microsoft YaHei UI', 8), width=10,
                                     bg='#2196F3' if self.floating_current_tab == 'texts' else '#e0e0e0',
                                     fg='white' if self.floating_current_tab == 'texts' else 'black')
        self.text_tab_btn.pack(side=tk.LEFT, padx=2)

        # 快速操作按钮
        btn_frame = tk.Frame(main_frame, bg='#f0f0f0')
        btn_frame.pack(fill=tk.X, pady=(0, 10))

        self.floating_add_btn = tk.Button(btn_frame, text='&#10133; 添加', command=self.floating_add_item,
                                         font=('Microsoft YaHei UI', 8), width=8, bg='#4CAF50', fg='white')
        self.floating_add_btn.pack(side=tk.LEFT, padx=2)

        self.floating_search_btn = tk.Button(btn_frame, text='&#128269; 搜索', command=self.floating_search,
                                            font=('Microsoft YaHei UI', 8), width=8, bg='#2196F3', fg='white')
        self.floating_search_btn.pack(side=tk.RIGHT, padx=2)

        # 密码/文本列表
        list_frame = tk.Frame(main_frame, bg='#f0f0f0')
        list_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 10))

        # 创建列表框
        self.floating_listbox = tk.Listbox(list_frame, height=8,
                                          font=('Microsoft YaHei UI', 9),
                                          selectmode=tk.SINGLE)

        # 添加滚动条
        scrollbar = tk.Scrollbar(list_frame, orient=tk.VERTICAL, command=self.floating_listbox.yview)
        self.floating_listbox.configure(yscrollcommand=scrollbar.set)

        # 布局
        self.floating_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        # 绑定事件
        self.floating_listbox.bind('<Double-1>', self.on_floating_double_click)
        self.floating_listbox.bind('<Button-3>', self.show_floating_context_menu)

        # 底部按钮
        bottom_frame = tk.Frame(main_frame, bg='#f0f0f0')
        bottom_frame.pack(fill=tk.X)

        tk.Button(bottom_frame, text='&#128203; 复制', command=self.floating_copy_password,
                 font=('Microsoft YaHei UI', 8), width=8, bg='#FF9800', fg='white').pack(side=tk.LEFT, padx=2)
        tk.Button(bottom_frame, text='&#128281; 返回', command=self.exit_floating_mode,
                 font=('Microsoft YaHei UI', 8), width=8, bg='#f44336', fg='white').pack(side=tk.RIGHT, padx=2)

        # 更新悬浮窗列表
        self.update_floating_list()

    def switch_floating_tab(self, tab_name):
        """切换悬浮窗标签页"""
        self.floating_current_tab = tab_name
        # 只更新列表内容,不重新创建整个界面
        self.update_floating_list()
        # 更新标签页按钮状态
        self.update_tab_buttons()

    def update_tab_buttons(self):
        """更新标签页按钮状态"""
        if hasattr(self, 'password_tab_btn') and hasattr(self, 'text_tab_btn'):
            if self.floating_current_tab == 'passwords':
                self.password_tab_btn.config(bg='#2196F3', fg='white')
                self.text_tab_btn.config(bg='#e0e0e0', fg='black')
            else:
                self.password_tab_btn.config(bg='#e0e0e0', fg='black')
                self.text_tab_btn.config(bg='#2196F3', fg='white')

    def toggle_floating_expand(self):
        """切换悬浮窗展开/收起状态"""
        self.floating_expanded = not self.floating_expanded

        if self.floating_expanded:
            # 展开到完整大小
            self.floating_window.geometry('250x350')
        else:
            # 收起到小球大小
            self.floating_window.geometry('60x60')

        # 重新创建内容
        self.create_floating_content()

    def bind_floating_drag(self):
        """绑定悬浮窗拖拽事件"""
        def start_drag(event):
            self.floating_window.x = event.x
            self.floating_window.y = event.y

        def on_drag(event):
            x = self.floating_window.winfo_pointerx() - self.floating_window.x
            y = self.floating_window.winfo_pointery() - self.floating_window.y
            self.floating_window.geometry(f"+{x}+{y}")

        # 绑定到整个窗口(因为使用了 overrideredirect)
        self.floating_window.bind('<Button-1>', start_drag)
        self.floating_window.bind('<B1-Motion>', on_drag)

        # 绑定到所有子组件
        def bind_to_children(widget):
            widget.bind('<Button-1>', start_drag, add='+')
            widget.bind('<B1-Motion>', on_drag, add='+')
            for child in widget.winfo_children():
                if not isinstance(child, (tk.Button, tk.Entry, tk.Listbox)):  # 排除交互组件
                    bind_to_children(child)

        bind_to_children(self.floating_window)

    def update_floating_list(self):
        """更新悬浮窗列表"""
        # 只在展开状态下更新列表
        if not self.floating_expanded or not hasattr(self, 'floating_listbox'):
            return

        # 清空现有项
        self.floating_listbox.delete(0, tk.END)

        if self.floating_current_tab == 'passwords':
            passwords = self.password_manager.get_all_passwords()
            self.floating_data = passwords[:20]  # 存储数据用于后续操作
            for password in self.floating_data:
                display_text = f"&#128272; {password[1]} - {password[2]}"  # 服务名 - 用户名
                if password[3]:  # 如果有URL
                    display_text += " &#127760;"
                self.floating_listbox.insert(tk.END, display_text)
        elif self.floating_current_tab == 'texts':
            texts = self.password_manager.get_all_encrypted_texts('title', 'ASC')
            self.floating_data = texts[:20]  # 存储数据用于后续操作
            for text in self.floating_data:
                # 确保有足够的字段
                if len(text) >= 4:
                    title = text[1] if text[1] else "无标题"
                    created_time = text[2] if text[2] else "未知时间"
                    # 只显示日期部分,避免显示过长
                    time_display = created_time[:10] if len(created_time) >= 10 else created_time
                    display_text = f"&#128221; {title} - {time_display}"
                else:
                    display_text = f"&#128221; {text[1] if len(text) > 1 else '未知文本'}"
                self.floating_listbox.insert(tk.END, display_text)

    def on_floating_double_click(self, event):
        """悬浮窗双击事件处理"""
        selection = self.floating_listbox.curselection()
        if not selection or not hasattr(self, 'floating_data'):
            return

        index = selection[0]
        if index >= len(self.floating_data):
            return

        if self.floating_current_tab == 'passwords':
            # 双击复制密码
            password_info = self.floating_data[index]
            service, username = password_info[1], password_info[2]
            password = self.password_manager.get_password(service, username)
            if password:
                pyperclip.copy(password)
                messagebox.showinfo('&#9989; 成功', f'{service} 的密码已复制到剪贴板!')
        elif self.floating_current_tab == 'texts':
            # 双击查看文本
            text_info = self.floating_data[index]
            text_id = text_info[0]
            title, content = self.password_manager.get_encrypted_text(text_id)
            if content:
                # 临时显示主窗口来查看文本
                self.root.deiconify()
                self.show_text_content_dialog(title, content)
                self.root.withdraw()

    def show_floating_context_menu(self, event):
        """显示悬浮窗右键菜单"""
        selection = self.floating_listbox.curselection()
        if not selection or not hasattr(self, 'floating_data'):
            return

        index = selection[0]
        if index >= len(self.floating_data):
            return

        context_menu = tk.Menu(self.floating_window, tearoff=0)

        if self.floating_current_tab == 'passwords':
            password_info = self.floating_data[index]
            service, username, url = password_info[1], password_info[2], password_info[3]

            context_menu.add_command(label="&#128203; 复制密码",
                                   command=lambda: self.copy_floating_password(service, username))
            if url:
                context_menu.add_command(label="&#127760; 打开URL",
                                       command=lambda: self.open_floating_url(url))
            context_menu.add_separator()
            context_menu.add_command(label="&#128065;&#65039; 查看详情",
                                   command=lambda: self.view_floating_password_details(password_info))
        elif self.floating_current_tab == 'texts':
            text_info = self.floating_data[index]
            text_id, title = text_info[0], text_info[1]

            context_menu.add_command(label="&#128065;&#65039; 查看内容",
                                   command=lambda: self.view_floating_text(text_id))
            context_menu.add_command(label="&#128203; 复制内容",
                                   command=lambda: self.copy_floating_text(text_id))

        try:
            context_menu.tk_popup(event.x_root, event.y_root)
        finally:
            context_menu.grab_release()

    def copy_floating_password(self, service, username):
        """复制悬浮窗密码"""
        password = self.password_manager.get_password(service, username)
        if password:
            pyperclip.copy(password)
            messagebox.showinfo('&#9989; 成功', f'{service} 的密码已复制到剪贴板!')

    def open_floating_url(self, url):
        """打开悬浮窗URL"""
        import webbrowser
        webbrowser.open(url)

    def view_floating_password_details(self, password_info):
        """查看悬浮窗密码详情"""
        # 临时显示主窗口
        self.root.deiconify()
        # 这里可以调用现有的查看密码方法
        messagebox.showinfo('密码详情', f'服务: {password_info[1]}\n用户名: {password_info[2]}\nURL: {password_info[3] or "无"}')
        self.root.withdraw()

    def view_floating_text(self, text_id):
        """查看悬浮窗文本"""
        title, content = self.password_manager.get_encrypted_text(text_id)
        if content:
            # 临时显示主窗口来查看文本
            self.root.deiconify()
            self.show_text_content_dialog(title, content)
            self.root.withdraw()

    def copy_floating_text(self, text_id):
        """复制悬浮窗文本"""
        title, content = self.password_manager.get_encrypted_text(text_id)
        if content:
            pyperclip.copy(content)
            messagebox.showinfo('&#9989; 成功', f'{title} 的内容已复制到剪贴板!')



    def floating_search(self):
        """悬浮窗搜索功能"""
        # 只在展开状态下搜索
        if not self.floating_expanded or not hasattr(self, 'floating_search_var'):
            return

        keyword = self.floating_search_var.get().strip()
        if not keyword:
            self.update_floating_list()
            return

        # 清空现有项
        self.floating_listbox.delete(0, tk.END)

        if self.floating_current_tab == 'passwords':
            results = self.password_manager.search_passwords(keyword)
            self.floating_data = results[:20]  # 存储搜索结果
            for password in self.floating_data:
                display_text = f"&#128272; {password[1]} - {password[2]}"  # 服务名 - 用户名
                if password[3]:  # 如果有URL
                    display_text += " &#127760;"
                self.floating_listbox.insert(tk.END, display_text)
        elif self.floating_current_tab == 'texts':
            results = self.password_manager.search_encrypted_texts(keyword)
            self.floating_data = results[:20]  # 存储搜索结果
            for text in self.floating_data:
                # 确保有足够的字段
                if len(text) >= 4:
                    title = text[1] if text[1] else "无标题"
                    created_time = text[2] if text[2] else "未知时间"
                    # 只显示日期部分,避免显示过长
                    time_display = created_time[:10] if len(created_time) >= 10 else created_time
                    display_text = f"&#128221; {title} - {time_display}"
                else:
                    display_text = f"&#128221; {text[1] if len(text) > 1 else '未知文本'}"
                self.floating_listbox.insert(tk.END, display_text)

    def floating_add_item(self):
        """悬浮窗添加项目"""
        if self.floating_current_tab == 'passwords':
            self.floating_add_password()
        elif self.floating_current_tab == 'texts':
            self.floating_add_text()

    def floating_add_password(self):
        """悬浮窗添加密码"""
        # 临时显示主窗口来添加密码
        self.root.deiconify()
        self.add_password_dialog()
        self.root.withdraw()
        # 更新悬浮窗列表
        self.update_floating_list()

    def floating_add_text(self):
        """悬浮窗添加文本"""
        # 临时显示主窗口来添加文本
        self.root.deiconify()
        self.show_add_text_dialog()
        self.root.withdraw()
        # 更新悬浮窗列表
        self.update_floating_list()

    def floating_copy_password(self):
        """悬浮窗复制密码 - 现在通过列表项直接操作"""
        messagebox.showinfo('提示', '请点击密码项右侧的&#128203;图标来复制密码')

    def save_floating_position(self):
        """保存悬浮窗位置"""
        if self.floating_window:
            geometry = self.floating_window.geometry()
            # 解析几何字符串 "200x300+100+100"
            parts = geometry.split('+')
            if len(parts) >= 3:
                self.floating_position['x'] = int(parts[1])
                self.floating_position['y'] = int(parts[2])

                # 保存到文件
                try:
                    config_file = os.path.join(os.getcwd(), 'floating_config.json')
                    with open(config_file, 'w') as f:
                        json.dump(self.floating_position, f)
                except:
                    pass

    def load_floating_position(self):
        """加载悬浮窗位置"""
        try:
            config_file = os.path.join(os.getcwd(), 'floating_config.json')
            if os.path.exists(config_file):
                with open(config_file, 'r') as f:
                    self.floating_position = json.load(f)
        except:
            # 使用默认位置
            self.floating_position = {'x': 100, 'y': 100}

    def show_text_manager(self):
        """显示文本管理器窗口"""
        dialog = tk.Toplevel(self.root)
        dialog.title('&#128221; 加密文本管理器')
        dialog.geometry('800x600')
        dialog.resizable(True, True)
        dialog.transient(self.root)
        dialog.grab_set()
        dialog.configure(bg='#f8f9fa')

        # 居中显示
        dialog.update_idletasks()
        width = dialog.winfo_width()
        height = dialog.winfo_height()
        x = (dialog.winfo_screenwidth() // 2) - (width // 2)
        y = (dialog.winfo_screenheight() // 2) - (height // 2)
        dialog.geometry('{}x{}+{}+{}'.format(width, height, x, y))

        # 创建主框架
        main_frame = ttk.Frame(dialog, padding='15')
        main_frame.pack(fill=tk.BOTH, expand=True)

        # 标题
        title_label = ttk.Label(main_frame, text='&#128221; 加密文本管理器',
                               font=('Microsoft YaHei UI', 16, 'bold'),
                               foreground='#2c3e50')
        title_label.pack(pady=(0, 20))

        # 按钮框架 - 优化布局,更紧凑
        button_frame = ttk.Frame(main_frame)
        button_frame.pack(fill=tk.X, pady=(0, 10))

        ttk.Button(button_frame, text='&#10133; 添加文本', command=lambda: self.show_add_text_dialog(dialog),
                  width=12).pack(side=tk.LEFT, padx=3)
        ttk.Button(button_frame, text='&#128065;&#65039; 查看文本', command=lambda: self.view_selected_text(text_tree, dialog),
                  width=12).pack(side=tk.LEFT, padx=3)
        ttk.Button(button_frame, text='&#9999;&#65039; 编辑文本', command=lambda: self.edit_selected_text(text_tree, dialog),
                  width=12).pack(side=tk.LEFT, padx=3)
        ttk.Button(button_frame, text='&#128465;&#65039; 删除文本', command=lambda: self.delete_selected_text(text_tree),
                  width=12).pack(side=tk.LEFT, padx=3)

        # 添加分隔符
        ttk.Separator(button_frame, orient='vertical').pack(side=tk.LEFT, fill=tk.Y, padx=8)

        # 导出导入按钮
        ttk.Button(button_frame, text='&#128228; 导出CSV', command=lambda: self.export_texts_dialog(text_tree),
                  width=12).pack(side=tk.LEFT, padx=3)
        ttk.Button(button_frame, text='&#128229; 导入CSV', command=lambda: self.import_texts_dialog(text_tree),
                  width=12).pack(side=tk.LEFT, padx=3)

        # 搜索和排序框架 - 优化布局,更紧凑
        search_frame = ttk.Frame(main_frame)
        search_frame.pack(fill=tk.X, pady=(0, 10))

        # 搜索部分 - 缩小搜索框
        ttk.Label(search_frame, text='&#128269; 搜索:', font=('Microsoft YaHei UI', 10)).pack(side=tk.LEFT, padx=(0, 5))
        text_search_var = tk.StringVar()
        text_search_entry = ttk.Entry(search_frame, textvariable=text_search_var,
                                     font=('Microsoft YaHei UI', 9), width=20)
        text_search_entry.pack(side=tk.LEFT, padx=(0, 8))

        # 排序部分 - 减少间距
        ttk.Label(search_frame, text='&#128202; 排序:', font=('Microsoft YaHei UI', 10)).pack(side=tk.LEFT, padx=(10, 3))

        sort_by_var = tk.StringVar(value='title')
        sort_by_combo = ttk.Combobox(search_frame, textvariable=sort_by_var,
                                    values=['id', 'title', 'created_at', 'updated_at'],
                                    state='readonly', width=10, font=('Microsoft YaHei UI', 9))
        sort_by_combo.pack(side=tk.LEFT, padx=(0, 3))

        sort_order_var = tk.StringVar(value='ASC')
        sort_order_combo = ttk.Combobox(search_frame, textvariable=sort_order_var,
                                       values=['ASC', 'DESC'], state='readonly', width=6, font=('Microsoft YaHei UI', 9))
        sort_order_combo.pack(side=tk.LEFT, padx=(0, 8))

        def search_and_sort_texts():
            keyword = text_search_var.get().strip()
            sort_by = sort_by_var.get()
            sort_order = sort_order_var.get()

            if keyword:
                results = self.password_manager.search_encrypted_texts(keyword)
                # 对搜索结果进行排序
                if sort_by == 'id':
                    results.sort(key=lambda x: x[0], reverse=(sort_order == 'DESC'))
                elif sort_by == 'title':
                    results.sort(key=lambda x: x[1] or '', reverse=(sort_order == 'DESC'))
                elif sort_by == 'created_at':
                    results.sort(key=lambda x: x[2] or '', reverse=(sort_order == 'DESC'))
                elif sort_by == 'updated_at':
                    results.sort(key=lambda x: x[3] or '', reverse=(sort_order == 'DESC'))
            else:
                results = self.password_manager.get_all_encrypted_texts(sort_by, sort_order)
            self.update_text_list(text_tree, results)

        text_search_entry.bind('<Return>', lambda e: search_and_sort_texts())
        sort_by_combo.bind('<<ComboboxSelected>>', lambda e: search_and_sort_texts())
        sort_order_combo.bind('<<ComboboxSelected>>', lambda e: search_and_sort_texts())

        # 按钮 - 缩小尺寸和间距
        ttk.Button(search_frame, text='&#128269; 搜索', command=search_and_sort_texts, width=8).pack(side=tk.LEFT, padx=3)
        ttk.Button(search_frame, text='&#128260; 重置',
                  command=lambda: [text_search_var.set(''), sort_by_var.set('title'), sort_order_var.set('ASC'), search_and_sort_texts()],
                  width=8).pack(side=tk.LEFT, padx=3)

        # 文本列表
        columns = ('ID', '标题', '创建时间', '更新时间')
        text_tree = ttk.Treeview(main_frame, columns=columns, show='headings', selectmode='browse')

        for col in columns:
            text_tree.heading(col, text=col)
            if col == 'ID':
                text_tree.column(col, width=50, anchor=tk.CENTER)
            elif col == '标题':
                text_tree.column(col, width=300, anchor=tk.W)
            else:
                text_tree.column(col, width=150, anchor=tk.CENTER)

        # 添加滚动条
        text_scrollbar = ttk.Scrollbar(main_frame, orient=tk.VERTICAL, command=text_tree.yview)
        text_tree.configure(yscroll=text_scrollbar.set)
        text_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, pady=5)
        text_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        # 绑定双击事件
        text_tree.bind('<Double-1>', lambda e: self.view_selected_text(text_tree, dialog))

        # 初始化文本列表(默认按标题升序排序)
        texts = self.password_manager.get_all_encrypted_texts('title', 'ASC')
        self.update_text_list(text_tree, texts)

    def update_text_list(self, text_tree, texts):
        """更新文本列表"""
        # 清空现有项
        for item in text_tree.get_children():
            text_tree.delete(item)

        # 添加到列表
        for text in texts:
            text_tree.insert('', tk.END, values=text)

    def update_time(self):
        """更新状态栏时间"""
        current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        self.time_var.set(current_time)
        # 每秒更新一次
        self.root.after(1000, self.update_time)

    def show_add_text_dialog(self, parent=None):
        """显示添加文本对话框"""
        if parent is None:
            parent = self.root

        dialog = tk.Toplevel(parent)
        dialog.title('&#10133; 添加加密文本')
        dialog.geometry('600x500')
        dialog.resizable(True, True)
        dialog.transient(parent)
        dialog.grab_set()

        # 居中显示
        dialog.update_idletasks()
        width = dialog.winfo_width()
        height = dialog.winfo_height()
        x = (dialog.winfo_screenwidth() // 2) - (width // 2)
        y = (dialog.winfo_screenheight() // 2) - (height // 2)
        dialog.geometry('{}x{}+{}+{}'.format(width, height, x, y))

        # 创建框架
        frame = ttk.Frame(dialog, padding='25')
        frame.pack(fill=tk.BOTH, expand=True)

        # 标题输入
        ttk.Label(frame, text='标题:', font=('Microsoft YaHei UI', 10, 'bold')).pack(anchor=tk.W, pady=(0, 5))
        title_var = tk.StringVar()
        title_entry = ttk.Entry(frame, textvariable=title_var, font=('Microsoft YaHei UI', 10))
        title_entry.pack(fill=tk.X, pady=(0, 15))

        # 内容输入
        ttk.Label(frame, text='内容:', font=('Microsoft YaHei UI', 10, 'bold')).pack(anchor=tk.W, pady=(0, 5))
        content_text = tk.Text(frame, font=('Microsoft YaHei UI', 10), wrap=tk.WORD)
        content_text.pack(fill=tk.BOTH, expand=True, pady=(0, 15))

        # 添加滚动条
        content_scrollbar = ttk.Scrollbar(content_text, orient=tk.VERTICAL, command=content_text.yview)
        content_text.configure(yscrollcommand=content_scrollbar.set)
        content_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        # 按钮框架
        btn_frame = ttk.Frame(frame)
        btn_frame.pack(fill=tk.X, pady=(15, 0))

        def save_text():
            title = title_var.get().strip()
            content = content_text.get('1.0', tk.END).strip()

            if self.password_manager.add_encrypted_text(title, content):
                dialog.destroy()
                # 如果有父窗口是文本管理器,刷新列表
                if hasattr(parent, 'winfo_children'):
                    for child in parent.winfo_children():
                        if isinstance(child, ttk.Frame):
                            for grandchild in child.winfo_children():
                                if isinstance(grandchild, ttk.Treeview):
                                    texts = self.password_manager.get_all_encrypted_texts('title', 'ASC')
                                    self.update_text_list(grandchild, texts)
                                    break

        ttk.Button(btn_frame, text='&#128190; 保存', command=save_text).pack(side=tk.LEFT, padx=10)
        ttk.Button(btn_frame, text='&#10060; 取消', command=dialog.destroy).pack(side=tk.LEFT, padx=10)

        # 设置焦点
        title_entry.focus()

    def view_selected_text(self, text_tree, parent):
        """查看选中的文本"""
        selection = text_tree.selection()
        if not selection:
            messagebox.showwarning('提示', '请先选择一条文本记录!')
            return

        item = text_tree.item(selection[0])
        text_id, title = item['values'][0], item['values'][1]

        # 获取解密后的内容
        _, content = self.password_manager.get_encrypted_text(text_id)
        if content is None:
            messagebox.showerror('错误', '无法获取文本内容!')
            return

        self.show_text_content_dialog(title, content, parent)

    def show_text_content_dialog(self, title, content, parent=None):
        """显示文本内容对话框"""
        if parent is None:
            parent = self.root

        dialog = tk.Toplevel(parent)
        dialog.title(f'&#128065;&#65039; {title}')
        dialog.geometry('700x500')
        dialog.resizable(True, True)
        dialog.transient(parent)
        dialog.grab_set()

        # 居中显示
        dialog.update_idletasks()
        width = dialog.winfo_width()
        height = dialog.winfo_height()
        x = (dialog.winfo_screenwidth() // 2) - (width // 2)
        y = (dialog.winfo_screenheight() // 2) - (height // 2)
        dialog.geometry('{}x{}+{}+{}'.format(width, height, x, y))

        # 创建框架
        frame = ttk.Frame(dialog, padding='25')
        frame.pack(fill=tk.BOTH, expand=True)

        # 标题显示
        title_label = ttk.Label(frame, text=f'&#128221; {title}',
                               font=('Microsoft YaHei UI', 14, 'bold'),
                               foreground='#2c3e50')
        title_label.pack(pady=(0, 15))

        # 内容显示
        content_text = tk.Text(frame, font=('Microsoft YaHei UI', 10), wrap=tk.WORD, state=tk.DISABLED)
        content_text.pack(fill=tk.BOTH, expand=True, pady=(0, 15))

        # 插入内容
        content_text.config(state=tk.NORMAL)
        content_text.insert('1.0', content)
        content_text.config(state=tk.DISABLED)

        # 添加滚动条
        content_scrollbar = ttk.Scrollbar(content_text, orient=tk.VERTICAL, command=content_text.yview)
        content_text.configure(yscrollcommand=content_scrollbar.set)
        content_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        # 按钮框架
        btn_frame = ttk.Frame(frame)
        btn_frame.pack(fill=tk.X)

        def copy_content():
            pyperclip.copy(content)
            messagebox.showinfo('&#9989; 成功', '内容已复制到剪贴板!')

        ttk.Button(btn_frame, text='&#128203; 复制内容', command=copy_content).pack(side=tk.LEFT, padx=10)
        ttk.Button(btn_frame, text='&#10060; 关闭', command=dialog.destroy).pack(side=tk.RIGHT, padx=10)

    def edit_selected_text(self, text_tree, parent):
        """编辑选中的文本"""
        selection = text_tree.selection()
        if not selection:
            messagebox.showwarning('提示', '请先选择一条文本记录!')
            return

        item = text_tree.item(selection[0])
        text_id, title = item['values'][0], item['values'][1]

        # 获取解密后的内容
        _, content = self.password_manager.get_encrypted_text(text_id)
        if content is None:
            messagebox.showerror('错误', '无法获取文本内容!')
            return

        self.show_edit_text_dialog(text_id, title, content, text_tree, parent)

    def show_edit_text_dialog(self, text_id, title, content, text_tree, parent):
        """显示编辑文本对话框"""
        dialog = tk.Toplevel(parent)
        dialog.title('&#9999;&#65039; 编辑加密文本')
        dialog.geometry('600x500')
        dialog.resizable(True, True)
        dialog.transient(parent)
        dialog.grab_set()

        # 居中显示
        dialog.update_idletasks()
        width = dialog.winfo_width()
        height = dialog.winfo_height()
        x = (dialog.winfo_screenwidth() // 2) - (width // 2)
        y = (dialog.winfo_screenheight() // 2) - (height // 2)
        dialog.geometry('{}x{}+{}+{}'.format(width, height, x, y))

        # 创建框架
        frame = ttk.Frame(dialog, padding='25')
        frame.pack(fill=tk.BOTH, expand=True)

        # 标题输入
        ttk.Label(frame, text='标题:', font=('Microsoft YaHei UI', 10, 'bold')).pack(anchor=tk.W, pady=(0, 5))
        title_var = tk.StringVar(value=title)
        title_entry = ttk.Entry(frame, textvariable=title_var, font=('Microsoft YaHei UI', 10))
        title_entry.pack(fill=tk.X, pady=(0, 15))

        # 内容输入
        ttk.Label(frame, text='内容:', font=('Microsoft YaHei UI', 10, 'bold')).pack(anchor=tk.W, pady=(0, 5))
        content_text = tk.Text(frame, font=('Microsoft YaHei UI', 10), wrap=tk.WORD)
        content_text.pack(fill=tk.BOTH, expand=True, pady=(0, 15))
        content_text.insert('1.0', content)

        # 添加滚动条
        content_scrollbar = ttk.Scrollbar(content_text, orient=tk.VERTICAL, command=content_text.yview)
        content_text.configure(yscrollcommand=content_scrollbar.set)
        content_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        # 按钮框架
        btn_frame = ttk.Frame(frame)
        btn_frame.pack(fill=tk.X, pady=(15, 0))

        def update_text():
            new_title = title_var.get().strip()
            new_content = content_text.get('1.0', tk.END).strip()

            if self.password_manager.update_encrypted_text(text_id, new_title, new_content):
                dialog.destroy()
                # 刷新文本列表
                texts = self.password_manager.get_all_encrypted_texts('title', 'ASC')
                self.update_text_list(text_tree, texts)

        ttk.Button(btn_frame, text='&#128190; 保存', command=update_text).pack(side=tk.LEFT, padx=10)
        ttk.Button(btn_frame, text='&#10060; 取消', command=dialog.destroy).pack(side=tk.LEFT, padx=10)

        # 设置焦点
        title_entry.focus()

    def export_texts_dialog(self, text_tree):
        """导出加密文本对话框"""
        from datetime import datetime
        from tkinter import filedialog

        file_path = filedialog.asksaveasfilename(
            title="导出加密文本",
            defaultextension=".csv",
            filetypes=[("CSV文件", "*.csv"), ("所有文件", "*.*")],
            initialfile=f"encrypted_texts_export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
        )

        if file_path:
            success, message = self.password_manager.export_texts_to_csv(file_path)
            if success:
                messagebox.showinfo("导出成功", message)
            else:
                messagebox.showerror("导出失败", message)

    def import_texts_dialog(self, text_tree):
        """导入加密文本对话框"""
        from tkinter import filedialog

        file_path = filedialog.askopenfilename(
            title="导入加密文本",
            filetypes=[("CSV文件", "*.csv"), ("所有文件", "*.*")]
        )

        if file_path:
            confirm = messagebox.askyesno("确认导入",
                "导入操作将添加新的文本记录。\n已存在相同标题的记录将被跳过。\n是否继续?")
            if confirm:
                success, message = self.password_manager.import_texts_from_csv(file_path)
                if success:
                    messagebox.showinfo("导入完成", message)
                    # 刷新文本列表
                    texts = self.password_manager.get_all_encrypted_texts('title', 'ASC')
                    self.update_text_list(text_tree, texts)
                else:
                    messagebox.showerror("导入失败", message)

    def export_texts_menu(self):
        """从菜单导出文本"""
        from datetime import datetime
        from tkinter import filedialog

        file_path = filedialog.asksaveasfilename(
            title="导出加密文本",
            defaultextension=".csv",
            filetypes=[("CSV文件", "*.csv"), ("所有文件", "*.*")],
            initialfile=f"encrypted_texts_export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
        )

        if file_path:
            success, message = self.password_manager.export_texts_to_csv(file_path)
            if success:
                messagebox.showinfo("导出成功", message)
            else:
                messagebox.showerror("导出失败", message)

    def import_texts_menu(self):
        """从菜单导入文本"""
        from tkinter import filedialog

        file_path = filedialog.askopenfilename(
            title="导入加密文本",
            filetypes=[("CSV文件", "*.csv"), ("所有文件", "*.*")]
        )

        if file_path:
            confirm = messagebox.askyesno("确认导入",
                "导入操作将添加新的文本记录。\n已存在相同标题的记录将被跳过。\n是否继续?")
            if confirm:
                success, message = self.password_manager.import_texts_from_csv(file_path)
                if success:
                    messagebox.showinfo("导入完成", message)
                else:
                    messagebox.showerror("导入失败", message)

    def delete_selected_text(self, text_tree):
        """删除选中的文本"""
        selection = text_tree.selection()
        if not selection:
            messagebox.showwarning('提示', '请先选择一条文本记录!')
            return

        item = text_tree.item(selection[0])
        text_id, title = item['values'][0], item['values'][1]

        # 确认删除
        if messagebox.askyesno('确认删除', f'确定要删除文本 "{title}" 吗?\n\n此操作不可撤销!'):
            if self.password_manager.delete_encrypted_text(text_id):
                # 刷新文本列表
                texts = self.password_manager.get_all_encrypted_texts('title', 'ASC')
                self.update_text_list(text_tree, texts)

if __name__ == '__main__':
    root = tk.Tk()
    app = PasswordManagerGUI(root)
    app.load_theme()  # 加载保存的主题
    root.mainloop()


免费评分

参与人数 13吾爱币 +25 热心值 +12 收起 理由
jigen + 1 + 1 我很赞同!
wwr21 + 1 + 1 谢谢@Thanks!
djgsdj + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
FreeMagnet + 1 + 1 鼓励转贴优秀软件安全工具和文档!
seny1163 + 1 + 1 我很赞同!
mypj52 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
454540162 + 1 + 1 我很赞同!
viconly + 1 + 1 谢谢@Thanks!
风之暇想 + 7 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
aria1983 + 1 + 1 谢谢@Thanks!
pxianghui + 1 + 1 支持原创
SenseView + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
Hmily + 7 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
忘情的城市 发表于 2025-6-27 13:16

搞不懂的是,既然强设密码大小写,登录后本地又自动生成免登,然后双击直接能打开软件,意义是啥?目的是啥?
为啥不能用户自定义设置密码,大小写数字位数随意
推荐
ew52000 发表于 2025-7-29 19:10
提示没有写入权限:

=== 存储系统诊断报告 ===

存储类型: local
配置验证: &#10003; 通过
连接测试: &#10003; 通过
读写测试: &#10007; 失败
同步测试: &#10003; 通过

错误信息:
  &#8226; 写入测试失败

=== 性能统计 ===

文件状态:
  &#10003; passwords.db: exists
  &#10003; secret.key: exists
  &#10007; config.dat: missing

文件大小:
  passwords.db: 40960 bytes
  secret.key: 32 bytes

=== 总体状态 ===
&#9888; 存储系统存在问题,请检查上述错误信息
3#
 楼主| weiajie 发表于 2025-6-25 17:49 |楼主
本帖最后由 weiajie 于 2025-6-25 18:35 编辑

下载地址:https://wwhi.lanzouo.com/imGyM2zjaf0h  密码:52pj
4#
SenseView 发表于 2025-6-26 17:30
本帖最后由 SenseView 于 2025-6-26 17:38 编辑

支持原创试用了下,登录2次后,后面在打开都是自动登陆了,不用输密码,这样是不是不太安全
5#
andytang866 发表于 2025-6-26 17:30
支持原创  收藏先
6#
wick 发表于 2025-6-26 19:00
看起来挺强的
7#
 楼主| weiajie 发表于 2025-6-27 00:14 |楼主
SenseView 发表于 2025-6-26 17:30
支持原创试用了下,登录2次后,后面在打开都是自动登陆了,不用输密码,这样是不是不太安全

不会的,主要是这样自己用着方便,你可以把代码改一下不生成登录文件就可以了
8#
mmyl520 发表于 2025-6-27 10:07
支持原创,收藏收藏
9#
Long321 发表于 2025-6-27 11:08
看起来很牛逼
10#
fengkai110 发表于 2025-6-27 16:56
大佬牛逼
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-4-15 01:05

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表