HTML安全密码备忘录

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>安全密码备忘录</title>
    <script>
        const builtinAccounts = [
            // 格式: [账户名称, 用户名/邮箱, 密码, 网站, 备注]
            ['163邮箱', 'user11@163.com', 'Pass163!Example', 'https://mail.163.com', '倩女幽魂账号'],
            ['微信', 'wechat_user', 'WeChat@2023', 'https://weixin.qq.com', '微信社交账号'],
            ['淘宝', 'taobao_buyer', 'TaobaoShop456', 'https://www.taobao.com', '淘宝购物账号'],
            ['工商银行', 'icbc_user_001', 'ICBC@Bank2023', 'https://www.icbc.com.cn', '工商银行网上银行'],
            ['百度网盘', 'baidu_user', 'BaiduCloud789', 'https://pan.baidu.com', '百度云存储服务'],
            ['哔哩哔哩', 'bilibili_viewer', 'Bilibili@Video123', 'https://www.bilibili.com', 'B站视频账号'],
            ['Microsoft Office', 'office365_user', 'Office365#2023', 'https://www.office.com', 'Office 365订阅账号'],
            ['Gmail邮箱', 'user@gmail.com', 'GmailPass123!', 'https://mail.google.com', '个人Gmail邮箱账号']
        ];
    </script>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
        }

        body {
            background-color: #f5f7fa;
            color: #333;
            line-height: 1.5;
            padding: 10px;
            min-height: 100vh;
            max-height: 100vh;
            overflow: hidden;
        }

        .container {
            max-width: 100%;
            height: calc(100vh - 20px);
            margin: 0 auto;
            background: white;
            border-radius: 12px;
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08);
            overflow: hidden;
            display: flex;
            flex-direction: column;
        }

        header {
            background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%);
            color: white;
            padding: 5px 10px;
            display: flex;
            align-items: baseline;
            flex-shrink: 0;
            /* 确保头部不会随着内容滚动而收缩,保持固定的高度。 */
        }

        h1 {
            font-size: 1.5rem;
            margin-bottom: 5px;
            gap: 1px;
        }

        .subtitle {
            opacity: 0.9;
            font-weight: 300;
            font-size: 0.85rem;
        }

        .main-content {
            padding: 15px;
            display: grid;
            grid-template-columns: 1fr 1.5fr;
            gap: 15px;
            flex: 1;
            overflow: hidden;
            min-height: 0;
        }

        @media (max-width: 768px) {
            .main-content {
                grid-template-columns: 1fr;
                grid-template-rows: auto 1fr;
                gap: 12px;
            }

            body {
                padding: 8px;
            }

            header {
                padding: 12px 15px;
            }

            h1 {
                font-size: 1.3rem;
            }
        }

        @media (max-width: 480px) {
            .main-content {
                padding: 10px;
                gap: 10px;
            }

            h1 {
                font-size: 1.2rem;
            }

            .section-title {
                font-size: 1.1rem;
            }
        }

        .auth-section,
        .password-list-section {
            background: #f9fafc;
            border-radius: 10px;
            padding: 15px;
            border: 1px solid #eef1f7;
            overflow: hidden;
            display: flex;
            flex-direction: column;
        }

        .auth-section {
            min-height: 300px;
        }

        .password-list-section {
            min-height: 300px;
        }

        .section-title {
            font-size: 1.2rem;
            margin-bottom: 1px;
            color: #2c3e50;
            display: flex;
            align-items: center;
            gap: 8px;
            flex-shrink: 0;
        }

        .section-title i {
            color: #3498db;
        }

        .form-group {
            margin-bottom: 12px;
        }

        label {
            display: block;
            margin-bottom: 1px;
            font-weight: 600;
            color: #4a5568;
            font-size: 0.9rem;
        }

        input,
        textarea,
        select {
            width: 100%;
            padding: 5px 6px;
            border: 2px solid #e2e8f0;
            border-radius: 6px;
            font-size: 0.9rem;
            transition: all 0.3s;
        }

        input:focus,
        textarea:focus,
        select:focus {
            outline: none;
            border-color: #3498db;
            box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2);
        }

        .btn {
            display: inline-block;
            padding: 7px 10px;
            background: #3498db;
            color: white;
            border: none;
            border-radius: 6px;
            font-size: 0.9rem;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s;
            text-align: center;
            width: 100%;
        }

        .btn:hover {
            background: #2980b9;
            transform: translateY(-1px);
        }

        .btn-primary {
            background: linear-gradient(to right, #3498db, #2ecc71);
        }

        .btn-primary:hover {
            background: linear-gradient(to right, #2980b9, #27ae60);
        }

        .btn-danger {
            background: #e74c3c;
        }

        .btn-danger:hover {
            background: #c0392b;
        }

        .btn-success {
            background: #2ecc71;
        }

        .btn-success:hover {
            background: #27ae60;
        }

        .btn-small {
            padding: 8px 12px;
            font-size: 0.85rem;
        }

        .btn-info {
            background: #9b59b6;
        }

        .btn-info:hover {
            background: #8e44ad;
        }

        .btn-warning {
            background: #f39c12;
        }

        .btn-warning:hover {
            background: #d68910;
        }

        .passwords-container {
            flex: 1;
            overflow-y: auto;
            padding-right: 5px;
        }

        .password-item {
            background: white;
            border-radius: 8px;
            padding: 12px 15px;
            margin-bottom: 10px;
            border: 1px solid #eef1f7;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.03);
            transition: all 0.2s;
        }

        .password-item:hover {
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.05);
        }

        .password-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 8px;
        }

        .password-title {
            font-weight: 700;
            font-size: 1rem;
            color: #2c3e50;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
            max-width: 70%;
        }

        .password-actions {
            display: flex;
            gap: 6px;
        }

        .password-actions button {
            background: none;
            border: none;
            cursor: pointer;
            font-size: 0.9rem;
            color: #7f8c8d;
            transition: color 0.3s;
            width: 30px;
            height: 30px;
            border-radius: 4px;
            display: flex;
            align-items: center;
            justify-content: center;
        }

        .password-actions button:hover {
            color: #3498db;
            background-color: #f0f7ff;
        }

        .password-field {
            margin-bottom: 6px;
            display: flex;
            align-items: flex-start;
        }

        .password-field label {
            min-width: 60px;
            margin-bottom: 0;
            color: #7f8c8d;
            font-size: 0.85rem;
            line-height: 1.2;
        }

        .password-value {
            font-family: 'Courier New', monospace;
            background: #f8f9fa;
            padding: 6px 10px;
            border-radius: 4px;
            flex-grow: 1;
            word-break: break-all;
            font-size: 0.85rem;
            line-height: 1.4;
        }

        .masked {
            color: transparent;
            text-shadow: 0 0 6px rgba(0, 0, 0, 0.5);
        }

        .empty-state {
            text-align: center;
            padding: 30px 15px;
            color: #7f8c8d;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100%;
        }

        .empty-state i {
            font-size: 2.5rem;
            margin-bottom: 10px;
            color: #bdc3c7;
        }

        .empty-state h3 {
            font-size: 1.1rem;
            margin-bottom: 8px;
        }

        .empty-state p {
            font-size: 0.9rem;
        }

        .security-notice {
            background: #f8f9fa;
            border-left: 3px solid #3498db;
            padding: 10px 12px;
            margin-top: 15px;
            border-radius: 0 6px 6px 0;
            font-size: 0.8rem;
            flex-shrink: 0;
        }

        .hidden {
            display: none;
        }
        .password-strength {
            height: 4px;
            border-radius: 2px;
            margin-top: 6px;
            background: #e0e0e0;
            overflow: hidden;
        }

        .strength-bar {
            height: 100%;
            width: 0%;
            transition: width 0.5s, background 0.5s;
        }

        .strength-weak {
            background: #e74c3c;
            width: 30%;
        }

        .strength-medium {
            background: #f39c12;
            width: 60%;
        }

        .strength-strong {
            background: #2ecc71;
            width: 100%;
        }

        footer {
            text-align: center;
            padding: 12px 15px;
            color: #7f8c8d;
            font-size: 0.8rem;
            border-top: 1px solid #eef1f7;
            flex-shrink: 0;
        }

        .form-section {
            flex: 1;
            overflow-y: auto;
            padding-right: 5px;
        }

        /* 滚动条样式 */
        ::-webkit-scrollbar {
            width: 6px;
        }

        ::-webkit-scrollbar-track {
            background: #f1f1f1;
            border-radius: 3px;
        }

        ::-webkit-scrollbar-thumb {
            background: #c1c1c1;
            border-radius: 3px;
        }

        ::-webkit-scrollbar-thumb:hover {
            background: #a8a8a8;
        }

        .compact-controls {
            display: flex;
            gap: 8px;
        }

        .compact-controls .btn {
            width: auto;
            flex: 1;
        }

        .compact-view .password-field {
            flex-direction: column;
            align-items: flex-start;
        }

        .compact-view .password-field label {
            margin-bottom: 3px;
            min-width: auto;
        }

        .compact-view .password-value {
            width: 100%;
        }

        .mobile-only {
            display: none;
        }

        @media (max-width: 768px) {
            .mobile-only {
                display: block;
            }

            .desktop-only {
                display: none;
            }

            .compact-controls {
                flex-direction: column;
            }
        }

        @media (max-height: 600px) {
            .main-content {
                padding: 10px;
                gap: 10px;
            }

            .auth-section,
            .password-list-section {
                padding: 12px;
            }

            .password-item {
                padding: 10px 12px;
                margin-bottom: 8px;
            }

            .form-group {
                margin-bottom: 8px;
            }

            .section-title {
                margin-bottom: 12px;
            }

            .password-header {
                margin-bottom: 6px;
            }

            .password-field {
                margin-bottom: 4px;
            }
        }

        .view-toggle {
            display: flex;
            border-radius: 6px;
            overflow: hidden;
            border: 1px solid #e2e8f0;
        }

        .view-toggle button {
            flex: 1;
            padding: 8px 10px;
            background: white;
            border: none;
            font-size: 0.85rem;
            cursor: pointer;
            transition: all 0.3s;
        }

        .view-toggle button.active {
            background: #3498db;
            color: white;
        }

        .view-toggle button:not(.active):hover {
            background: #f0f7ff;
        }

        /* 响应式表格视图 */
        .table-view {
            width: 100%;
            border-collapse: collapse;
            font-size: 0.85rem;
        }

        .table-view th {
            background-color: #f8f9fa;
            padding: 8px 10px;
            text-align: left;
            font-weight: 600;
            color: #4a5568;
            border-bottom: 2px solid #e2e8f0;
        }

        .table-view td {
            padding: 8px 10px;
            border-bottom: 1px solid #eef1f7;
        }

        .table-view tr:hover {
            background-color: #f8f9fa;
        }

        .tab-buttons {
            display: flex;
            border-bottom: 1px solid #e2e8f0;
            margin-bottom: 15px;
        }

        .tab-button {
            padding: 10px 15px;
            background: none;
            border: none;
            border-bottom: 3px solid transparent;
            font-size: 0.9rem;
            cursor: pointer;
            transition: all 0.3s;
            flex: 1;
            text-align: center;
        }

        .tab-button.active {
            border-bottom-color: #3498db;
            color: #3498db;
            font-weight: 600;
        }

        .tab-button:hover:not(.active) {
            background-color: #f0f7ff;
        }

        .tab-content {
            display: none;
            flex: 1;
            overflow-y: auto;
        }

        .tab-content.active {
            display: block;
        }

        .builtin-account {
            border-left: 4px solid #2ecc71;
        }

        .builtin-account .password-title {
            color: #27ae60;
        }

        .account-badge {
            display: inline-block;
            padding: 2px 6px;
            font-size: 0.7rem;
            border-radius: 10px;
            margin-left: 6px;
            font-weight: 600;
        }

        .badge-builtin {
            background-color: #2ecc71;
            color: white;
        }

        .badge-custom {
            background-color: #3498db;
            color: white;
        }

        .filter-section {
            margin-bottom: 1px;
            padding: 2px;
            background: #f8f9fa;
            border-radius: 8px;
            border: 1px solid #eef1f7;
        }

        .filter-controls {
            display: flex;
            gap: 8px;
            margin-top: 10px;
        }

        .filter-input-group {
            position: relative;
            flex: 1;
        }

        .filter-input-group input {
            padding-left: 35px;
        }

        .filter-icon {
            position: absolute;
            left: 12px;
            top: 50%;
            transform: translateY(-50%);
            color: #7f8c8d;
        }

        .filter-results {
            font-size: 0.85rem;
            color: #7f8c8d;
            margin-top: 8px;
        }

        .filter-results span {
            font-weight: 600;
            color: #3498db;
        }

        .clear-filter-btn {
            background: none;
            border: none;
            color: #e74c3c;
            cursor: pointer;
            font-size: 0.8rem;
            display: flex;
            align-items: center;
            gap: 4px;
        }

        .clear-filter-btn:hover {
            text-decoration: underline;
        }

        .no-results {
            text-align: center;
            padding: 40px 20px;
            color: #7f8c8d;
        }

        .no-results i {
            font-size: 2.5rem;
            margin-bottom: 10px;
            color: #bdc3c7;
        }

        .no-results h3 {
            font-size: 1.1rem;
            margin-bottom: 1px;
        }

        .no-results p {
            font-size: 0.9rem;
        }

        .filter-tags {
            display: flex;
            flex-wrap: wrap;
            gap: 6px;
            margin-top: 1px;
        }

        .filter-tag {
            background: #e3f2fd;
            color: #1565c0;
            padding: 0px 10px;
            border-radius: 12px;
            font-size: 0.8rem;
            cursor: pointer;
            transition: all 0.2s;
        }

        .filter-tag:hover {
            background: #bbdefb;
        }

        .filter-tag.active {
            background: #3498db;
            color: white;
        }

        .controls-row {
            display: flex;
            justify-content: space-between;
            align-items: center;
            gap: 10px;
            margin-bottom: 1px;
        }

        .password-length {
            font-size: 0.75rem;
            color: #7f8c8d;
            text-align: right;
        }

        .password-length-label {
            font-weight: 600;
        }

        .password-input-wrapper {
            position: relative;
        }

        .char-count {
            position: absolute;
            right: 10px;
            top: 50%;
            transform: translateY(-50%);
            background: rgba(255, 255, 255, 0.9);
            padding: 2px 8px;
            border-radius: 10px;
            font-size: 0.75rem;
            color: #7f8c8d;
            border: 1px solid #e2e8f0;
        }

        .lock-countdown {
            position: absolute;
            right: 10px;
            top: 50%;
            transform: translateY(-50%);
            background: #e74c3c;
            color: white;
            padding: 2px 8px;
            border-radius: 10px;
            font-size: 0.75rem;
            font-weight: bold;
        }

        /* SVG图标样式 */
        .icon {
            display: inline-block;
            width: 1em;
            height: 1em;
            stroke-width: 0;
            stroke: currentColor;
            fill: currentColor;
        }

        .icon-lock {
            width: 1.2em;
            height: 1.2em;
        }

        .icon-key {
            width: 1.2em;
            height: 1.2em;
        }

        .icon-list {
            width: 1.2em;
            height: 1.2em;
        }

        .icon-plus {
            width: 1.2em;
            height: 1.2em;
        }

        .icon-filter {
            width: 1.2em;
            height: 1.2em;
        }

        .icon-search {
            width: 1.2em;
            height: 1.2em;
        }

        .icon-times {
            width: 1.2em;
            height: 1.2em;
        }

        .icon-user {
            width: 1.2em;
            height: 1.2em;
        }

        .icon-copy {
            width: 1.2em;
            height: 1.2em;
        }

        .icon-eye {
            width: 1.2em;
            height: 1.2em;
        }

        .icon-eye-slash {
            width: 1.2em;
            height: 1.2em;
        }

        .icon-trash {
            width: 1.2em;
            height: 1.2em;
        }

        .icon-grip-vertical {
            width: 1.2em;
            height: 1.2em;
        }

        .icon-table {
            width: 1.2em;
            height: 1.2em;
        }

        .icon-info-circle {
            width: 1.2em;
            height: 1.2em;
        }
    </style>
</head>

<body>
    <div class="container">
        <header>
            <h1>
                <svg class="icon icon-lock" viewBox="0 0 24 24">
                    <path
                        d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z" />
                </svg>
                安全密码备忘录
            </h1>
            <p class="subtitle">本地存储,安全可靠,管理您的所有账号和密码</p>
        </header>
        <div class="main-content">
            <section class="auth-section">
                <h2 class="section-title">
                    <svg class="icon icon-key" viewBox="0 0 24 24">
                        <path
                            d="M21 10h-8.35C11.83 7.67 9.61 6 7 6c-3.31 0-6 2.69-6 6s2.69 6 6 6c2.61 0 4.83-1.67 5.65-4H13l2 2 2-2 2 2 4-4.04L21 10zM7 15c-1.65 0-3-1.35-3-3s1.35-3 3-3 3 1.35 3 3-1.35 3-3 3z" />
                    </svg>
                    访问控制
                </h2>
                <div id="login-section">
                    <div class="form-group">
                        <label for="master-password">主密码</label>
                        <input type="password" id="master-password" placeholder="请输入主密码">
                        <div class="password-strength">
                            <div id="password-strength-bar" class="strength-bar"></div>
                        </div>
                    </div>
                    <button id="login-btn" class="btn btn-primary">解锁备忘录</button>
                    <p class="security-notice">
                        <svg class="icon icon-info-circle" viewBox="0 0 24 24"
                            style="width: 1em; height: 1em; margin-right: 5px;">
                            <path
                                d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z" />
                        </svg>
                        数据存储在浏览器本地,请务必记住主密码,忘记密码将无法恢复数据。
                    </p>
                </div>
                <div id="add-password-section" class="hidden">
                    <h2 class="section-title">
                        <svg class="icon icon-plus" viewBox="0 0 24 24">
                            <path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
                        </svg>
                        添加新账号
                    </h2>
                    <div class="form-section">
                        <div class="form-group">
                            <label for="account-name">账户名称</label>
                            <input type="text" id="account-name" placeholder="例如:Gmail、微信">
                        </div>
                        <div class="form-group">
                            <label for="username">用户名/邮箱</label>
                            <input type="text" id="username" placeholder="输入用户名或邮箱">
                        </div>
                        <div class="form-group">
                            <label for="password">密码</label>
                            <div class="compact-controls">
                                <div class="password-input-wrapper" style="flex: 2;">
                                    <input type="text" id="password" placeholder="输入密码">
                                    <span class="char-count" id="password-char-count">0</span>
                                </div>
                                <button type="button" id="generate-password" class="btn btn-small"
                                    style="flex: 1; background: #9b59b6;">生成</button>
                            </div>
                            <div class="password-length">
                                <span class="password-length-label">密码长度:</span> <span id="password-length">0</span> 个字符
                            </div>
                        </div>
                        <div class="form-group">
                            <label for="website">网站/应用地址</label>
                            <input type="text" id="website" placeholder="https://example.com">
                        </div>
                        <div class="form-group">
                            <label for="notes">备注</label>
                            <textarea id="notes" rows="2" placeholder="可选的备注信息"></textarea>
                        </div>
                    </div>
                    <div class="compact-controls">
                        <button id="save-password-btn" class="btn btn-success">保存</button>
                        <div class="password-input-wrapper" style="flex: 1; position: relative;">
                            <button id="logout-btn" class="btn btn-danger">锁定 (<span
                                    id="lock-timer">300</span>s)</button>
                        </div>
                        <button id="show-builtin-btn" class="btn btn-info">示例账号</button>
                    </div>
                </div>
            </section>
            <section class="password-list-section">
                <div class="controls-row">
                    <div class="controls-row">
                        <h2 class="section-title">
                            <svg class="icon icon-list" viewBox="0 0 24 24">
                                <path
                                    d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm4 4h14v-2H7v2zm0 4h14v-2H7v2zM7 7v2h14V7H7z" />
                            </svg>
                            账号列表 (<span id="password-count">0</span>)
                        </h2>
                        <div class="filter-section" id="filter-section" style="display: none;">
                            <div class="filter-controls">
                                <div class="filter-input-group">
                                    <input type="text" id="account-filter" placeholder="按账户名称筛选..."
                                        style="display: none;">
                                </div>
                                <button id="quick-filter-btn" class="btn btn-small btn-warning">快速筛选</button>
                                <button id="clear-filter-btn" class="clear-filter-btn" style="display: none;">
                                    <svg class="icon icon-times" viewBox="0 0 24 24"
                                        style="width: 0.9em; height: 0.9em;">
                                        <path
                                            d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" />
                                    </svg>
                                    清除筛选
                                </button>
                            </div>
                        </div>
                    </div>
                    <div class="view-toggle mobile-only">
                        <button id="card-view-btn" class="active" title="卡片视图">
                            <svg class="icon icon-grip-vertical" viewBox="0 0 24 24">
                                <path
                                    d="M9 4h2v2H9zm4 0h2v2h-2zM9 8h2v2H9zm4 0h2v2h-2zm-4 4h2v2H9zm4 0h2v2h-2zm-4 4h2v2H9zm4 0h2v2h-2z" />
                            </svg>
                        </button>
                        <button id="table-view-btn" title="表格视图">
                            <svg class="icon icon-table" viewBox="0 0 24 24">
                                <path d="M3 9h18V7H3v2zm0 4h18v-2H3v2zm0 4h18v-2H3v2zM3 5v2h18V5H3z" />
                            </svg>
                        </button>
                    </div>
                </div>
                <div class="filter-tags" id="filter-tags"></div>
                <div class="filter-results" id="filter-results">显示所有账号</div>
                <div class="tab-buttons">
                    <button class="tab-button active" data-tab="custom">我的账号</button>
                    <button class="tab-button" data-tab="builtin">示例账号</button>
                </div>
                <div id="custom-passwords-container" class="tab-content active passwords-container">
                    <div class="empty-state">
                        <svg class="icon icon-lock" viewBox="0 0 24 24"
                            style="width: 3em; height: 3em; margin-bottom: 10px;">
                            <path
                                d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z"
                                fill="#bdc3c7" />
                        </svg>
                        <h3>备忘录已锁定</h3>
                        <p>请输入主密码解锁以查看账号信息</p>
                    </div>
                </div>
                <div id="builtin-passwords-container" class="tab-content passwords-container">
                    <div class="empty-state">
                        <svg class="icon icon-lock" viewBox="0 0 24 24"
                            style="width: 3em; height: 3em; margin-bottom: 10px;">
                            <path
                                d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z"
                                fill="#bdc3c7" />
                        </svg>
                        <h3>备忘录已锁定</h3>
                        <p>请输入主密码解锁以查看示例账号</p>
                    </div>
                </div>
            </section>
        </div>
        <footer>
            <p>安全密码备忘录 &copy; 2023 | 本地存储 | 请勿在公共电脑上使用</p>
        </footer>
    </div>
    <script>
        // 主密码和加密密钥
        let masterPassword = '';
        let isAuthenticated = false;
        let isTableView = false;
        let currentTab = 'custom';
        let currentFilter = '';
        let allCustomPasswords = [];
        let allBuiltinPasswords = [];
        // 自动锁定相关变量
        let lockTimer = null;
        let lockTimeout = 5 * 60; // 5分钟,单位:秒
        let remainingTime = lockTimeout;
        let lastActivityTime = Date.now();
        // 账号字段名称数组
        const accountFields = ['accountName', 'username', 'password', 'website', 'notes'];

        // 从二维数组生成内置账号对象数组
        function generateBuiltinAccounts() {
            const accounts = [];
            for (let i = 0; i < builtinAccounts.length; i++) {
                const accountData = builtinAccounts[i];
                const account = {};
                for (let j = 0; j < accountFields.length; j++) {
                    account[accountFields[j]] = accountData[j];
                }
                accounts.push(account);
            }
            return accounts;
        }
        // 筛选标签 - 初始为空,从账号名称中自动提取
        let commonFilters = [];
        let builtinFilters = [];
        // DOM 元素
        const loginSection = document.getElementById('login-section');
        const addPasswordSection = document.getElementById('add-password-section');
        const customPasswordsContainer = document.getElementById('custom-passwords-container');
        const builtinPasswordsContainer = document.getElementById('builtin-passwords-container');
        const passwordCountElement = document.getElementById('password-count');
        const masterPasswordInput = document.getElementById('master-password');
        const loginBtn = document.getElementById('login-btn');
        const logoutBtn = document.getElementById('logout-btn');
        const savePasswordBtn = document.getElementById('save-password-btn');
        const generatePasswordBtn = document.getElementById('generate-password');
        const showBuiltinBtn = document.getElementById('show-builtin-btn');
        const passwordStrengthBar = document.getElementById('password-strength-bar');
        const cardViewBtn = document.getElementById('card-view-btn');
        const tableViewBtn = document.getElementById('table-view-btn');
        const tabButtons = document.querySelectorAll('.tab-button');
        const accountFilterInput = document.getElementById('account-filter');
        const clearFilterBtn = document.getElementById('clear-filter-btn');
        const filterResults = document.getElementById('filter-results');
        const filterTags = document.getElementById('filter-tags');
        const quickFilterBtn = document.getElementById('quick-filter-btn');
        const filterSection = document.getElementById('filter-section');
        const passwordInput = document.getElementById('password');
        const passwordCharCount = document.getElementById('password-char-count');
        const passwordLengthElement = document.getElementById('password-length');
        const lockTimerElement = document.getElementById('lock-timer');
        // 初始化
        document.addEventListener('DOMContentLoaded', function () {
            // 检查是否已有主密码设置
            checkExistingMasterPassword();
            // 事件监听
            loginBtn.addEventListener('click', handleLogin);
            logoutBtn.addEventListener('click', handleLogout);
            savePasswordBtn.addEventListener('click', savePassword);
            generatePasswordBtn.addEventListener('click', generateRandomPassword);
            showBuiltinBtn.addEventListener('click', showBuiltinAccounts);
            masterPasswordInput.addEventListener('input', checkPasswordStrength);
            cardViewBtn.addEventListener('click', () => switchView('card'));
            tableViewBtn.addEventListener('click', () => switchView('table'));
            accountFilterInput.addEventListener('input', applyFilter);
            clearFilterBtn.addEventListener('click', clearFilter);
            quickFilterBtn.addEventListener('click', showQuickFilter);
            // 密码输入框字数统计
            passwordInput.addEventListener('input', updatePasswordLength);
            // 标签页切换
            tabButtons.forEach(button => {
                button.addEventListener('click', function () {
                    const tabId = this.getAttribute('data-tab');
                    switchTab(tabId);
                });
            });
            // 密码输入框回车登录
            masterPasswordInput.addEventListener('keypress', function (e) {
                if (e.key === 'Enter') {
                    handleLogin();
                }
            });
            // 筛选输入框回车筛选
            accountFilterInput.addEventListener('keypress', function (e) {
                if (e.key === 'Enter') {
                    applyFilter();
                }
            });
            // 用户活动检测
            document.addEventListener('click', updateLastActivityTime);
            document.addEventListener('keypress', updateLastActivityTime);
            document.addEventListener('scroll', updateLastActivityTime);
            // 初始隐藏筛选区域
            filterSection.style.display = 'none';
            // 初始化内置账号
            allBuiltinPasswords = generateBuiltinAccounts();
            builtinFilters = extractAccountNames(allBuiltinPasswords);
            // 初始更新密码长度显示
            updatePasswordLength();
            // 初始更新锁定计时器显示
            updateLockTimerDisplay();
            // 启动自动锁定检查
            startAutoLockCheck();
        });
        // 更新最后活动时间
        function updateLastActivityTime() {
            if (isAuthenticated) {
                lastActivityTime = Date.now();
                resetLockTimer();
            }
        }
        // 重置锁定计时器
        function resetLockTimer() {
            remainingTime = lockTimeout;
            updateLockTimerDisplay();
        }
        // 开始自动锁定检查
        function startAutoLockCheck() {
            if (lockTimer) {
                clearInterval(lockTimer);
            }
            lockTimer = setInterval(function () {
                if (isAuthenticated) {
                    const currentTime = Date.now();
                    const inactiveTime = Math.floor((currentTime - lastActivityTime) / 1000);
                    if (inactiveTime >= lockTimeout) {
                        // 自动锁定
                        handleLogout();
                        alert('由于长时间无操作,备忘录已自动锁定');
                    } else {
                        // 更新剩余时间
                        remainingTime = lockTimeout - inactiveTime;
                        updateLockTimerDisplay();
                    }
                }
            }, 1000); // 每秒检查一次
        }
        // 更新锁定计时器显示
        function updateLockTimerDisplay() {
            lockTimerElement.textContent = remainingTime;
            // 根据剩余时间改变颜色
            if (remainingTime <= 30) {
                lockTimerElement.style.color = '#e74c3c'; // 红色,剩余时间少
            } else if (remainingTime <= 60) {
                lockTimerElement.style.color = '#f39c12'; // 橙色
            } else {
                lockTimerElement.style.color = 'white'; // 白色
            }
        }
        // 检查是否已有主密码
        function checkExistingMasterPassword() {
            const storedMasterHash = localStorage.getItem('masterPasswordHash');
            if (!storedMasterHash) {
                // 首次使用,需要设置主密码
                loginBtn.textContent = '设置主密码';
                document.querySelector('.security-notice').innerHTML = `
                    <svg class="icon icon-info-circle" viewBox="0 0 24 24" style="width: 1em; height: 1em; margin-right: 5px;">
                        <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/>
                    </svg>
                    首次使用,请设置主密码。请务必记住此密码,忘记密码将无法恢复数据。
                `;
            }
        }
        // 处理登录/设置主密码
        function handleLogin() {
            const password = masterPasswordInput.value.trim();
            if (!password) {
                alert('请输入主密码');
                return;
            }
            const storedMasterHash = localStorage.getItem('masterPasswordHash');
            if (!storedMasterHash) {
                // 首次使用,设置主密码
                setMasterPassword(password);
            } else {
                // 验证主密码
                if (simpleHash(password) === storedMasterHash) {
                    authenticateUser(password);
                } else {
                    alert('密码错误,请重试');
                    masterPasswordInput.value = '';
                }
            }
        }
        // 设置主密码
        function setMasterPassword(password) {
            const hash = simpleHash(password);
            localStorage.setItem('masterPasswordHash', hash);
            authenticateUser(password);
            alert('主密码设置成功!请务必记住此密码。');
        }
        // 验证用户
        function authenticateUser(password) {
            masterPassword = password;
            isAuthenticated = true;
            // 更新最后活动时间
            lastActivityTime = Date.now();
            resetLockTimer();
            // 切换UI
            loginSection.classList.add('hidden');
            addPasswordSection.classList.remove('hidden');
            masterPasswordInput.value = '';
            // 显示筛选区域
            filterSection.style.display = 'block';
            // 加载保存的密码
            loadPasswords();
            // 加载示例账号
            displayBuiltinAccounts();
        }
        // 处理登出
        function handleLogout() {
            isAuthenticated = false;
            masterPassword = '';
            // 切换UI
            loginSection.classList.remove('hidden');
            addPasswordSection.classList.add('hidden');
            // 隐藏筛选区域
            filterSection.style.display = 'none';
            // 清空密码显示
            customPasswordsContainer.innerHTML = `
                <div class="empty-state">
                    <svg class="icon icon-lock" viewBox="0 0 24 24" style="width: 3em; height: 3em; margin-bottom: 10px;">
                        <path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z" fill="#bdc3c7"/>
                    </svg>
                    <h3>备忘录已锁定</h3>
                    <p>请输入主密码解锁以查看保存的账号信息</p>
                </div>
            `;
            builtinPasswordsContainer.innerHTML = `
                <div class="empty-state">
                    <svg class="icon icon-lock" viewBox="0 0 24 24" style="width: 3em; height: 3em; margin-bottom: 10px;">
                        <path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z" fill="#bdc3c7"/>
                    </svg>
                    <h3>备忘录已锁定</h3>
                    <p>请输入主密码解锁以查看示例账号</p>
                </div>
            `;
            passwordCountElement.textContent = '0';
            // 清除筛选
            clearFilter();
            // 重置锁定计时器
            resetLockTimer();
        }
        // 切换标签页
        function switchTab(tabId) {
            currentTab = tabId;
            // 更新标签按钮状态
            tabButtons.forEach(button => {
                if (button.getAttribute('data-tab') === tabId) {
                    button.classList.add('active');
                } else {
                    button.classList.remove('active');
                }
            });
            // 显示对应的内容
            document.querySelectorAll('.tab-content').forEach(content => {
                if (content.id === `${tabId}-passwords-container`) {
                    content.classList.add('active');
                } else {
                    content.classList.remove('active');
                }
            });
            // 更新筛选标签
            updateFilterTags();
            // 重新应用筛选
            applyFilter();
            // 更新最后活动时间
            updateLastActivityTime();
        }
        // 切换视图模式
        function switchView(viewType) {
            isTableView = viewType === 'table';
            if (isTableView) {
                cardViewBtn.classList.remove('active');
                tableViewBtn.classList.add('active');
            } else {
                tableViewBtn.classList.remove('active');
                cardViewBtn.classList.add('active');
            }
            // 重新加载密码以应用新视图
            if (isAuthenticated) {
                if (currentTab === 'custom') {
                    displayFilteredPasswords();
                } else if (currentTab === 'builtin') {
                    displayBuiltinAccounts();
                }
            }
            // 更新最后活动时间
            updateLastActivityTime();
        }
        // 加载密码
        function loadPasswords() {
            const encryptedPasswords = localStorage.getItem('encryptedPasswords');
            if (!encryptedPasswords) {
                allCustomPasswords = [];
                updateFilterTags();
                displayFilteredPasswords();
                updatePasswordCount();
                return;
            }
            try {
                // 简单解密(在实际应用中应使用更安全的加密方法)
                const decryptedData = simpleDecrypt(encryptedPasswords, masterPassword);
                allCustomPasswords = JSON.parse(decryptedData);
                updateFilterTags();
                displayFilteredPasswords();
                updatePasswordCount();
            } catch (error) {
                console.error('解密失败:', error);
                alert('加载数据时发生错误,请检查主密码是否正确');
                handleLogout();
            }
            // 更新最后活动时间
            updateLastActivityTime();
        }
        // 更新密码数量显示
        function updatePasswordCount() {
            let totalCount = 0;
            if (currentTab === 'custom') {
                totalCount = allCustomPasswords.length;
            } else if (currentTab === 'builtin') {
                totalCount = allBuiltinPasswords.length;
            }
            passwordCountElement.textContent = totalCount;
        }
        // 从账号名称中提取标签
        function extractAccountNames(passwords) {
            return [...new Set(passwords.map(p => p.accountName))];
        }
        // 应用筛选
        function applyFilter() {
            currentFilter = accountFilterInput.value.trim().toLowerCase();
            // 显示/隐藏清除筛选按钮
            if (currentFilter) {
                clearFilterBtn.style.display = 'block';
            } else {
                clearFilterBtn.style.display = 'none';
            }
            // 重新显示密码
            if (currentTab === 'custom') {
                displayFilteredPasswords();
            } else if (currentTab === 'builtin') {
                displayBuiltinAccounts();
            }
            // 更新筛选结果信息
            updateFilterResults();
            // 更新最后活动时间
            updateLastActivityTime();
        }
        // 更新筛选结果信息
        function updateFilterResults() {
            let filteredCount = 0;
            if (currentTab === 'custom') {
                if (currentFilter) {
                    filteredCount = allCustomPasswords.filter(password => {
                        return password.accountName.toLowerCase().includes(currentFilter) ||
                            password.username.toLowerCase().includes(currentFilter) ||
                            (password.notes && password.notes.toLowerCase().includes(currentFilter));
                    }).length;
                } else {
                    filteredCount = allCustomPasswords.length;
                }
            } else if (currentTab === 'builtin') {
                if (currentFilter) {
                    filteredCount = allBuiltinPasswords.filter(password => {
                        return password.accountName.toLowerCase().includes(currentFilter) ||
                            password.username.toLowerCase().includes(currentFilter) ||
                            (password.notes && password.notes.toLowerCase().includes(currentFilter));
                    }).length;
                } else {
                    filteredCount = allBuiltinPasswords.length;
                }
            }
            if (currentFilter) {
                filterResults.innerHTML = `找到 <span>${filteredCount}</span> 个匹配"${currentFilter}"的账号`;
            } else {
                filterResults.innerHTML = `显示所有 <span>${filteredCount}</span> 个账号`;
            }
        }
        // 清除筛选
        function clearFilter() {
            accountFilterInput.value = '';
            currentFilter = '';
            clearFilterBtn.style.display = 'none';
            // 移除所有标签的活动状态
            document.querySelectorAll('.filter-tag').forEach(tag => {
                tag.classList.remove('active');
            });
            applyFilter();
            // 更新最后活动时间
            updateLastActivityTime();
        }
        // 显示快速筛选标签
        function showQuickFilter() {
            // 如果标签已显示,则隐藏;否则显示
            if (filterTags.style.display === 'flex') {
                filterTags.style.display = 'none';
                quickFilterBtn.innerHTML = '快速筛选';
            } else {
                filterTags.style.display = 'flex';
                quickFilterBtn.innerHTML = '隐藏标签';
            }
            // 更新最后活动时间
            updateLastActivityTime();
        }
        // 更新筛选标签
        function updateFilterTags() {
            filterTags.innerHTML = '';
            // 根据当前标签页选择筛选标签
            let filters = [];
            if (currentTab === 'custom') {
                filters = extractAccountNames(allCustomPasswords);
            } else if (currentTab === 'builtin') {
                filters = builtinFilters;
            }
            // 如果没有账号,添加一些默认标签
            if (filters.length === 0) {
                filters = ['邮箱', '社交', '银行', '购物', '云', '办公', '视频', '学习'];
            }
            // 限制标签数量,避免太多
            const maxTags = 15;
            const tagsToShow = filters.slice(0, maxTags);
            // 添加筛选标签
            tagsToShow.forEach(filter => {
                const tag = document.createElement('div');
                tag.className = 'filter-tag';
                tag.textContent = filter;
                tag.addEventListener('click', function () {
                    // 如果已经是活动状态,则取消筛选
                    if (tag.classList.contains('active')) {
                        tag.classList.remove('active');
                        clearFilter();
                    } else {
                        // 移除其他标签的活动状态
                        document.querySelectorAll('.filter-tag').forEach(t => {
                            t.classList.remove('active');
                        });
                        tag.classList.add('active');
                        accountFilterInput.value = filter;
                        applyFilter();
                    }
                    // 更新最后活动时间
                    updateLastActivityTime();
                });
                filterTags.appendChild(tag);
            });
            // 如果标签数量超过限制,显示提示
            if (filters.length > maxTags) {
                const moreTag = document.createElement('div');
                moreTag.className = 'filter-tag';
                moreTag.textContent = `+${filters.length - maxTags}更多`;
                moreTag.style.backgroundColor = '#f5f5f5';
                moreTag.style.color = '#757575';
                moreTag.style.cursor = 'default';
                filterTags.appendChild(moreTag);
            }
        }
        // 显示筛选后的密码
        function displayFilteredPasswords() {
            let filteredPasswords = [];
            if (currentFilter) {
                // 筛选密码
                filteredPasswords = allCustomPasswords.filter(password => {
                    return password.accountName.toLowerCase().includes(currentFilter) ||
                        password.username.toLowerCase().includes(currentFilter) ||
                        (password.notes && password.notes.toLowerCase().includes(currentFilter));
                });
            } else {
                // 显示所有密码
                filteredPasswords = allCustomPasswords;
            }
            if (filteredPasswords.length === 0) {
                if (allCustomPasswords.length === 0) {
                    customPasswordsContainer.innerHTML = `
                        <div class="empty-state">
                            <svg class="icon icon-key" viewBox="0 0 24 24" style="width: 3em; height: 3em; margin-bottom: 10px;">
                                <path d="M21 10h-8.35C11.83 7.67 9.61 6 7 6c-3.31 0-6 2.69-6 6s2.69 6 6 6c2.61 0 4.83-1.67 5.65-4H13l2 2 2-2 2 2 4-4.04L21 10zM7 15c-1.65 0-3-1.35-3-3s1.35-3 3-3 3 1.35 3 3-1.35 3-3 3z" fill="#bdc3c7"/>
                            </svg>
                            <h3>暂无保存的账号</h3>
                            <p>点击左侧"添加新账号"按钮开始保存您的账号信息</p>
                        </div>
                    `;
                } else {
                    customPasswordsContainer.innerHTML = `
                        <div class="no-results">
                            <svg class="icon icon-search" viewBox="0 0 24 24" style="width: 3em; height: 3em; margin-bottom: 10px;">
                                <path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z" fill="#bdc3c7"/>
                            </svg>
                            <h3>未找到匹配的账号</h3>
                            <p>没有找到包含"${currentFilter}"的账号,请尝试其他关键词</p>
                            ${currentFilter ? `<button class="btn btn-small" onclick="clearFilter()" style="margin-top: 10px;">清除筛选</button>` : ''}
                        </div>
                    `;
                }
                return;
            }
            // 显示密码列表
            if (isTableView) {
                displayPasswordsTable(filteredPasswords, customPasswordsContainer, false);
            } else {
                displayPasswordsCards(filteredPasswords, customPasswordsContainer, false);
            }
            // 更新最后活动时间
            updateLastActivityTime();
        }
        // 显示内置账号
        function displayBuiltinAccounts() {
            if (!isAuthenticated) {
                builtinPasswordsContainer.innerHTML = `
                    <div class="empty-state">
                        <svg class="icon icon-lock" viewBox="0 0 24 24" style="width: 3em; height: 3em; margin-bottom: 10px;">
                            <path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z" fill="#bdc3c7"/>
                        </svg>
                        <h3>备忘录已锁定</h3>
                        <p>请输入主密码解锁以查看示例账号</p>
                    </div>
                `;
                return;
            }
            let filteredBuiltinPasswords = allBuiltinPasswords;
            if (currentFilter) {
                // 筛选内置账号
                filteredBuiltinPasswords = allBuiltinPasswords.filter(password => {
                    return password.accountName.toLowerCase().includes(currentFilter) ||
                        password.username.toLowerCase().includes(currentFilter) ||
                        (password.notes && password.notes.toLowerCase().includes(currentFilter));
                });
            }
            if (filteredBuiltinPasswords.length === 0) {
                builtinPasswordsContainer.innerHTML = `
                    <div class="no-results">
                        <svg class="icon icon-search" viewBox="0 0 24 24" style="width: 3em; height: 3em; margin-bottom: 10px;">
                            <path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z" fill="#bdc3c7"/>
                        </svg>
                        <h3>未找到匹配的示例账号</h3>
                        <p>没有找到包含"${currentFilter}"的示例账号,请尝试其他关键词</p>
                        ${currentFilter ? `<button class="btn btn-small" onclick="clearFilter()" style="margin-top: 10px;">清除筛选</button>` : ''}
                    </div>
                `;
                return;
            }
            if (isTableView) {
                displayPasswordsTable(filteredBuiltinPasswords, builtinPasswordsContainer, true);
            } else {
                displayPasswordsCards(filteredBuiltinPasswords, builtinPasswordsContainer, true);
            }
            // 更新最后活动时间
            updateLastActivityTime();
        }
        // 显示卡片视图
        function displayPasswordsCards(passwords, container, isBuiltin) {
            container.innerHTML = '';
            passwords.forEach((password, index) => {
                const passwordElement = document.createElement('div');
                passwordElement.className = `password-item ${isBuiltin ? 'builtin-account' : ''}`;
                passwordElement.innerHTML = `
                    <div class="password-header">
                        <div class="password-title">
                            ${escapeHtml(password.accountName)}
                            <span class="account-badge ${isBuiltin ? 'badge-builtin' : 'badge-custom'}">
                                ${isBuiltin ? '示例' : '自定义'}
                            </span>
                        </div>
                        <div class="password-actions">
                            <button class="copy-username" data-index="${index}" title="复制用户名">
                                <svg class="icon icon-user" viewBox="0 0 24 24">
                                    <path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/>
                                </svg>
                            </button>
                            <button class="copy-password" data-index="${index}" title="复制密码">
                                <svg class="icon icon-copy" viewBox="0 0 24 24">
                                    <path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/>
                                </svg>
                            </button>
                            <button class="toggle-password" data-index="${index}" title="显示/隐藏密码">
                                <svg class="icon icon-eye" viewBox="0 0 24 24">
                                    <path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/>
                                </svg>
                            </button>
                            ${!isBuiltin ? `
                            <button class="delete-password" data-index="${index}" title="删除">
                                <svg class="icon icon-trash" viewBox="0 0 24 24">
                                    <path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/>
                                </svg>
                            </button>
                            ` : ''}
                        </div>
                    </div>
                    <div class="password-field">
                        <label>用户名:</label>
                        <div class="password-value username-value" id="username-${isBuiltin ? 'builtin-' : ''}${index}">${escapeHtml(password.username)}</div>
                    </div>
                    <div class="password-field">
                        <label>密码:</label>
                        <div class="password-value masked password-value" id="password-${isBuiltin ? 'builtin-' : ''}${index}">
                            ${password.password}
                                <span class="password-length-label" style="margin-top: 4px;"> ${password.password.length} 字符</span>
                        </div>
                    </div>
                    ${password.website ? `
                    <div class="password-field">
                        <label>网址:</label>
                        <div class="password-value">
                            <a href="${password.website}" target="_blank" style="color: #3498db; text-decoration: none;">${escapeHtml(password.website.length > 30 ? password.website.substring(0, 30) + '...' : password.website)}</a>
                        </div>
                    </div>` : ''}
                    ${password.notes ? `
                    <div class="password-field">
                        <label>备注:</label>
                        <div class="password-value">${escapeHtml(password.notes.length > 50 ? password.notes.substring(0, 50) + '...' : password.notes)}</div>
                    </div>` : ''}
                `;
                container.appendChild(passwordElement);
            });
            if (isBuiltin) {
                attachBuiltinPasswordEvents(passwords);
            } else {
                attachCustomPasswordEvents(passwords);
            }
        }
        // 显示表格视图
        function displayPasswordsTable(passwords, container, isBuiltin) {
            let tableHTML = `
                <table class="table-view">
                    <thead>
                        <tr>
                            <th style="width: 25%;">账户名称</th>
                            <th style="width: 25%;">用户名</th>
                            <th style="width: 20%;">密码</th>
                            <th style="width: 30%;">操作</th>
                        </tr>
                    </thead>
                    <tbody>
            `;
            passwords.forEach((password, index) => {
                tableHTML += `
                    <tr>
                        <td>
                            ${escapeHtml(password.accountName)}
                            <span class="account-badge ${isBuiltin ? 'badge-builtin' : 'badge-custom'}">
                                ${isBuiltin ? '示例' : '自定义'}
                            </span>
                        </td>
                        <td>${escapeHtml(password.username)}</td>
                        <td>
                            <span class="masked password-value" id="table-password-${isBuiltin ? 'builtin-' : ''}${index}">${password.password}</span>
                            <span class="password-length" style="font-size: 0.7rem; color: #7f8c8d;">
                               ${password.password.length} 字符
                            </span>
                        </td>
                        <td>
                            <div style="display: flex; gap: 5px;">
                                <button class="copy-username btn-small" data-index="${index}" title="复制用户名" style="padding: 4px 8px; font-size: 0.8rem;">
                                    <svg class="icon icon-user" viewBox="0 0 24 24" style="width: 0.9em; height: 0.9em;">
                                        <path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/>
                                    </svg>
                                </button>
                                <button class="copy-password btn-small" data-index="${index}" title="复制密码" style="padding: 4px 8px; font-size: 0.8rem;">
                                <svg class="icon icon-copy" viewBox="0 0 24 24" style="width: 0.9em; height: 0.9em;">
                                        <path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/>
                                    </svg>
                                </button>
                                <button class="toggle-password btn-small" data-index="${index}" title="显示/隐藏密码" style="padding: 4px 8px; font-size: 0.8rem;">
                                    <svg class="icon icon-eye" viewBox="0 0 24 24" style="width: 0.9em; height: 0.9em;">
                                        <path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/>
                                    </svg>
                                </button>
                                ${!isBuiltin ? `
                                <button class="delete-password btn-small" data-index="${index}" title="删除" style="padding: 4px 8px; font-size: 0.8rem; background: #e74c3c;">
                                    <svg class="icon icon-trash" viewBox="0 0 24 24" style="width: 0.9em; height: 0.9em;">
                                        <path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/>
                                    </svg>
                                </button>
                                ` : ''}
                            </div>
                        </td>
                    </tr>
                `;
            });
            tableHTML += `
                    </tbody>
                </table>
            `;
            container.innerHTML = tableHTML;
            if (isBuiltin) {
                attachBuiltinPasswordEvents(passwords);
            } else {
                attachCustomPasswordEvents(passwords);
            }
        }
        // 附加自定义密码事件监听
        function attachCustomPasswordEvents(passwords) {
            // 复制用户名
            document.querySelectorAll('.copy-username').forEach(button => {
                button.addEventListener('click', function () {
                    const index = this.getAttribute('data-index');
                    copyToClipboard(passwords[index].username, '用户名');
                    updateLastActivityTime();
                });
            });
            // 复制密码
            document.querySelectorAll('.copy-password').forEach(button => {
                button.addEventListener('click', function () {
                    const index = this.getAttribute('data-index');
                    copyToClipboard(passwords[index].password, '密码');
                    updateLastActivityTime();
                });
            });
            // 显示/隐藏密码
            document.querySelectorAll('.toggle-password').forEach(button => {
                button.addEventListener('click', function () {
                    const index = this.getAttribute('data-index');
                    const prefix = isTableView ? 'table-password-' : 'password-';
                    const passwordElement = document.getElementById(`${prefix}${index}`);
                    const icon = this.querySelector('svg');
                    if (passwordElement.classList.contains('masked')) {
                        passwordElement.classList.remove('masked');
                        // 切换为隐藏眼睛图标
                        icon.innerHTML = '<path d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46C3.08 8.3 1.78 10.02 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78l3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z"/>';
                    } else {
                        passwordElement.classList.add('masked');
                        // 切换为显示眼睛图标
                        icon.innerHTML = '<path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/>';
                    }
                    updateLastActivityTime();
                });
            });
            // 删除密码
            document.querySelectorAll('.delete-password').forEach(button => {
                button.addEventListener('click', function () {
                    const index = this.getAttribute('data-index');
                    if (confirm('确定要删除这个账号信息吗?')) {
                        deletePassword(index);
                    }
                    updateLastActivityTime();
                });
            });
        }
        // 附加内置密码事件监听
        function attachBuiltinPasswordEvents(passwords) {
            // 复制用户名
            document.querySelectorAll('.copy-username').forEach(button => {
                button.addEventListener('click', function () {
                    const index = this.getAttribute('data-index');
                    copyToClipboard(passwords[index].username, '用户名');
                    updateLastActivityTime();
                });
            });
            // 复制密码
            document.querySelectorAll('.copy-password').forEach(button => {
                button.addEventListener('click', function () {
                    const index = this.getAttribute('data-index');
                    copyToClipboard(passwords[index].password, '密码');
                    updateLastActivityTime();
                });
            });
            // 显示/隐藏密码
            document.querySelectorAll('.toggle-password').forEach(button => {
                button.addEventListener('click', function () {
                    const index = this.getAttribute('data-index');
                    const prefix = isTableView ? 'table-password-builtin-' : 'password-builtin-';
                    const passwordElement = document.getElementById(`${prefix}${index}`);
                    const icon = this.querySelector('svg');
                    if (passwordElement.classList.contains('masked')) {
                        passwordElement.classList.remove('masked');
                        // 切换为隐藏眼睛图标
                        icon.innerHTML = '<path d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46C3.08 8.3 1.78 10.02 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78l3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z"/>';
                    } else {
                        passwordElement.classList.add('masked');
                        // 切换为显示眼睛图标
                        icon.innerHTML = '<path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/>';
                    }
                    updateLastActivityTime();
                });
            });
        }
        // 显示内置账号
        function showBuiltinAccounts() {
            switchTab('builtin');
            updateLastActivityTime();
        }
        // 保存新密码
        function savePassword() {
            const accountName = document.getElementById('account-name').value.trim();
            const username = document.getElementById('username').value.trim();
            const password = document.getElementById('password').value.trim();
            const website = document.getElementById('website').value.trim();
            const notes = document.getElementById('notes').value.trim();
            if (!accountName || !username || !password) {
                alert('账户名称、用户名和密码为必填项');
                return;
            }
            // 获取现有密码
            let passwords = [];
            const encryptedPasswords = localStorage.getItem('encryptedPasswords');
            if (encryptedPasswords) {
                try {
                    const decryptedData = simpleDecrypt(encryptedPasswords, masterPassword);
                    passwords = JSON.parse(decryptedData);
                } catch (error) {
                    console.error('解密失败:', error);
                    alert('保存失败,请重新登录后重试');
                    return;
                }
            }
            // 添加新密码
            passwords.push({
                accountName,
                username,
                password,
                website,
                notes
            });
            // 加密并保存
            const encryptedData = simpleEncrypt(JSON.stringify(passwords), masterPassword);
            localStorage.setItem('encryptedPasswords', encryptedData);
            // 清空表单
            document.getElementById('account-name').value = '';
            document.getElementById('username').value = '';
            document.getElementById('password').value = '';
            document.getElementById('website').value = '';
            document.getElementById('notes').value = '';
            // 重新加载密码列表
            loadPasswords();
            // 切换到自定义账号标签
            switchTab('custom');
            alert('账号信息保存成功!');
            updateLastActivityTime();
        }
        // 删除密码
        function deletePassword(index) {
            const encryptedPasswords = localStorage.getItem('encryptedPasswords');
            if (!encryptedPasswords) return;
            try {
                const decryptedData = simpleDecrypt(encryptedPasswords, masterPassword);
                let passwords = JSON.parse(decryptedData);
                // 删除指定索引的密码
                passwords.splice(index, 1);
                // 重新加密保存
                const newEncryptedData = simpleEncrypt(JSON.stringify(passwords), masterPassword);
                localStorage.setItem('encryptedPasswords', newEncryptedData);
                // 重新加载
                loadPasswords();
            } catch (error) {
                console.error('删除失败:', error);
                alert('删除失败,请重试');
            }
        }
        // 生成随机密码
        function generateRandomPassword() {
            const length = 12;
            const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*";
            let password = "";
            for (let i = 0; i < length; i++) {
                password += charset.charAt(Math.floor(Math.random() * charset.length));
            }
            document.getElementById('password').value = password;
            updatePasswordLength();
            checkPasswordStrength();
            updateLastActivityTime();
        }
        // 更新密码长度显示
        function updatePasswordLength() {
            const password = passwordInput.value;
            const length = password.length;
            passwordCharCount.textContent = length;
            passwordLengthElement.textContent = length;
            // 根据长度改变颜色
            if (length === 0) {
                passwordCharCount.style.color = '#7f8c8d';
            } else if (length < 8) {
                passwordCharCount.style.color = '#e74c3c'; // 红色,太短
            } else if (length < 12) {
                passwordCharCount.style.color = '#f39c12'; // 橙色,中等
            } else {
                passwordCharCount.style.color = '#2ecc71'; // 绿色,足够长
            }
        }
        // 检查密码强度
        function checkPasswordStrength() {
            const password = document.getElementById('password') ? document.getElementById('password').value : masterPasswordInput.value;
            let strength = 0;
            if (password.length >= 8) strength++;
            if (/[a-z]/.test(password)) strength++;
            if (/[A-Z]/.test(password)) strength++;
            if (/[0-9]/.test(password)) strength++;
            if (/[^a-zA-Z0-9]/.test(password)) strength++;
            // 更新强度条
            passwordStrengthBar.className = 'strength-bar';
            if (password.length === 0) {
                passwordStrengthBar.style.width = '0%';
            } else if (strength <= 2) {
                passwordStrengthBar.classList.add('strength-weak');
            } else if (strength <= 4) {
                passwordStrengthBar.classList.add('strength-medium');
            } else {
                passwordStrengthBar.classList.add('strength-strong');
            }
        }
        // 复制到剪贴板
        function copyToClipboard(text, type) {
            navigator.clipboard.writeText(text).then(() => {
                alert(`${type} 已复制到剪贴板`);
            }).catch(err => {
                console.error('复制失败:', err);
                // 备用复制方法
                const textArea = document.createElement('textarea');
                textArea.value = text;
                document.body.appendChild(textArea);
                textArea.select();
                try {
                    document.execCommand('copy');
                    alert(`${type} 已复制到剪贴板`);
                } catch (err) {
                    console.error('备用复制方法失败:', err);
                    alert('复制失败,请手动复制');
                }
                document.body.removeChild(textArea);
            });
        }
        // 简单的哈希函数(仅用于演示,实际应用应使用更安全的哈希算法)
        function simpleHash(str) {
            let hash = 0;
            for (let i = 0; i < str.length; i++) {
                const char = str.charCodeAt(i);
                hash = ((hash << 5) - hash) + char;
                hash |= 0; // 转换为32位整数
            }
            return hash.toString(16);
        }
        // 简单加密(仅用于演示,实际应用应使用更安全的加密算法)
        function simpleEncrypt(data, key) {
            return btoa(escape(data + key));
        }
        // 简单解密
        function simpleDecrypt(encryptedData, key) {
            const decrypted = unescape(atob(encryptedData));
            return decrypted.slice(0, -key.length);
        }
        // 转义HTML
        function escapeHtml(text) {
            const div = document.createElement('div');
            div.textContent = text;
            return div.innerHTML;
        }
    </script>
</body>

</html>
相关推荐
ヤ鬧鬧o.2 小时前
小巧路径转换器优化
前端·javascript·css
阿宇爱吃鱼2 小时前
uniapp input输入框,限制金额输入格式
前端·javascript·uni-app
Dreamy smile2 小时前
JavaScript 实现 HTTPS SSE 连接
开发语言·javascript·https
coding随想2 小时前
Web SQL Database API:一段被时代淘汰的浏览器存储技术
前端·数据库·sql
Marshmallowc2 小时前
React 刷新页面 Token 消失?深度解析 Redux + LocalStorage 数据持久化方案与 Hook 避坑指南
javascript·react·数据持久化·redux·前端工程化
Dreamy smile2 小时前
vue3 vite pinia实现动态路由,菜单权限,按钮权限
前端·javascript·vue.js
翱翔的苍鹰2 小时前
智谱(Zhipu)大模型的流式使用 response.iter_lines() 逐行解析 SSE 流
服务器·前端·数据库
未来之窗软件服务2 小时前
仙盟创梦IDE-集成开发测试:自动解析驱动的多线路自动化测试
前端·测试自动化·仙盟创梦ide·东方仙盟
天天睡大觉3 小时前
python命名规则(PEP8编码规则)
开发语言·前端·python