HTML账号管理器
侧边栏壁纸
  • 累计撰写 15 篇文章
  • 累计收到 1 条评论

HTML账号管理器

vous
2026-05-11 / 0 评论 / 2 阅读 / 正在检测是否收录...

这是我用AI做的一个可以管理账号的网页工具

这是开发时的完整文件(包含历史版本)

文件存在蓝奏云上,登录手机号:15310649220 文件路径:根目录/2026/05/11/账号管理器/完整开发文件(包含历史版本).zip

登录手机号:15310649220 文件路径:根目录/2026/05/11/账号管理器/完整开发文件(包含历史版本).zip

原版完整代码:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9, user-scalable=no">
    <title>账号管理器</title>
    <style>
        :root {
            --bg-primary: #ffffff;
            --bg-secondary: #f5f5f5;
            --bg-tertiary: #fafafa;
            --text-primary: #111111;
            --text-secondary: #666666;
            --text-tertiary: #999999;
            --border: #e5e5e5;
            --accent: #0066ff;
            --accent-hover: #0052cc;
            --danger: #ff3b30;
            --success: #34c759;
            --warning: #ff9500;
            --shadow: rgba(0, 0, 0, 0.08);
            --card-bg: #ffffff;
            --input-bg: #ffffff;
            --modal-overlay: rgba(0, 0, 0, 0.5);
        }

        [data-theme="dark"] {
            --bg-primary: #000000;
            --bg-secondary: #1c1c1e;
            --bg-tertiary: #2c2c2e;
            --text-primary: #ffffff;
            --text-secondary: #8e8e93;
            --text-tertiary: #636366;
            --border: #38383a;
            --accent: #0a84ff;
            --accent-hover: #409cff;
            --danger: #ff453a;
            --success: #30d158;
            --warning: #ff9f0a;
            --shadow: rgba(0, 0, 0, 0.3);
            --card-bg: #1c1c1e;
            --input-bg: #2c2c2e;
            --modal-overlay: rgba(0, 0, 0, 0.8);
        }

        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            -webkit-tap-highlight-color: transparent;
            -webkit-touch-callout: none;
            -webkit-user-select: none;
            user-select: none;
        }

        /* Allow text selection in specific elements */
        .field-text, .view-value-text, .note-text, .form-input, textarea, input {
            -webkit-user-select: text;
            user-select: text;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Segoe UI', Roboto, sans-serif;
            background: var(--bg-primary);
            color: var(--text-primary);
            line-height: 1.5;
            -webkit-font-smoothing: antialiased;
            transition: background 0.3s, color 0.3s;
        }

        /* Sort Icon Button - Stats Bar Style */
        .sort-icon-btn {
            display: inline-flex;
            align-items: center;
            gap: 6px;
            padding: 6px 12px;
            border: 1px solid var(--border);
            background: var(--bg-secondary);
            color: var(--text-secondary);
            border-radius: 20px;
            cursor: pointer;
            font-size: 13px;
            font-weight: 500;
            transition: all 0.2s;
            margin-left: auto;
        }

        .sort-icon-btn:hover {
            border-color: var(--accent);
            color: var(--accent);
            background: var(--bg-tertiary);
        }

        .sort-icon-btn.active {
            background: var(--accent);
            color: white;
            border-color: var(--accent);
        }

        .sort-icon-btn.active:hover {
            background: var(--accent-hover);
            border-color: var(--accent-hover);
        }

        .sort-text {
            font-size: 12px;
            font-weight: 600;
        }

        /* Header */

        .header {
            position: sticky;
            top: 0;
            background: var(--bg-primary);
            border-bottom: 1px solid var(--border);
            z-index: 100;
            transition: all 0.3s;
        }

        .header-content {
            max-width: 1200px;
            margin: 0 auto;
            padding: 16px 24px;
            display: flex;
            align-items: center;
            justify-content: space-between;
            gap: 20px;
        }

        .logo {
            display: flex;
            align-items: center;
            gap: 12px;
            font-size: 20px;
            font-weight: 600;
            letter-spacing: -0.5px;
        }

        .logo-icon {
            width: 32px;
            height: 32px;
            background: var(--accent);
            border-radius: 8px;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
        }

        .header-actions {
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .icon-btn {
            width: 36px;
            height: 36px;
            border: none;
            background: transparent;
            color: var(--text-secondary);
            cursor: pointer;
            border-radius: 8px;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.2s;
            outline: none;
        }

        .icon-btn:hover {
            background: var(--bg-secondary);
            color: var(--text-primary);
        }

        .icon-btn.danger:hover {
            background: var(--danger);
            color: white;
        }

        /* Search Bar */
        .search-section {
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px 24px;
            display: flex;
            gap: 12px;
            flex-wrap: wrap;
        }

        .search-box {
            flex: 1;
            min-width: 280px;
            position: relative;
        }

        .search-input {
            width: 100%;
            padding: 12px 16px 12px 40px;
            border: 1px solid var(--border);
            border-radius: 12px;
            background: var(--input-bg);
            color: var(--text-primary);
            font-size: 15px;
            transition: all 0.2s;
            outline: none;
        }

        .search-input:focus {
            outline: none;
            border-color: var(--accent);
        }

        .search-icon {
            position: absolute;
            left: 12px;
            top: 50%;
            transform: translateY(-50%);
            color: var(--text-tertiary);
            pointer-events: none;
        }

        .btn {
            padding: 12px 20px;
            border: none;
            border-radius: 12px;
            font-size: 15px;
            font-weight: 500;
            cursor: pointer;
            display: inline-flex;
            align-items: center;
            gap: 8px;
            transition: all 0.2s;
            white-space: nowrap;
            outline: none;
        }

        .btn-primary {
            background: var(--accent);
            color: white;
        }

        .btn-primary:hover {
            background: var(--accent-hover);
            transform: translateY(-1px);
        }

        .btn-secondary {
            background: var(--bg-secondary);
            color: var(--text-primary);
            border: 1px solid var(--border);
        }

        .btn-secondary:hover {
            background: var(--bg-tertiary);
        }

        .btn-danger {
            background: var(--danger);
            color: white;
        }

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

        /* Tags Filter */
        .tags-section {
            max-width: 1200px;
            margin: 0 auto;
            padding: 0 24px 20px;
            display: flex;
            gap: 8px;
            flex-wrap: wrap;
            align-items: center;
            position: relative;
        }

        .tag-label {
            font-size: 13px;
            color: var(--text-tertiary);
            font-weight: 500;
        }

        .tag {
            padding: 6px 14px;
            border-radius: 20px;
            font-size: 13px;
            font-weight: 500;
            cursor: pointer;
            transition: all 0.2s;
            border: 1px solid var(--border);
            background: var(--bg-primary);
            color: var(--text-secondary);
        }

        .tag:hover {
            border-color: var(--accent);
            color: var(--accent);
        }

        .tag.active {
            background: var(--accent);
            color: white;
            border-color: var(--accent);
        }

        /* Main Content */
        .container {
            max-width: 1200px;
            margin: 0 auto;
            padding: 0 24px 40px;
        }

        .stats-bar {
            display: flex;
            align-items: center;
            gap: 24px;
            margin-bottom: 24px;
            font-size: 13px;
            color: var(--text-tertiary);
        }

        .stat-item {
            display: flex;
            align-items: center;
            gap: 6px;
            flex-shrink: 0;
        }

        .stat-value {
            color: var(--text-primary);
            font-weight: 600;
        }

        /* Grid Layout */
        .accounts-grid {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
            gap: 16px;
        }

        /* Card Design */
        .account-card {
            background: var(--card-bg);
            border: 1px solid var(--border);
            border-radius: 16px;
            padding: 20px;
            transition: all 0.2s;
            position: relative;
            cursor: pointer;
            min-width: 0;
            outline: none;
        }

        .account-card:hover {
            border-color: var(--accent);
            box-shadow: 0 4px 20px var(--shadow);
            transform: translateY(-2px);
        }

        .card-header {
            display: flex;
            justify-content: space-between;
            align-items: start;
            margin-bottom: 16px;
            min-width: 0;
        }

        .platform-info {
            display: flex;
            align-items: center;
            gap: 12px;
            min-width: 0;
            flex: 1;
        }

        .platform-icon {
            width: 44px;
            height: 44px;
            background: var(--bg-secondary);
            border-radius: 12px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 20px;
            font-weight: 600;
            color: var(--accent);
            flex-shrink: 0;
        }

        .platform-meta {
            min-width: 0;
            flex: 1;
            overflow: hidden;
        }

        .platform-name {
            font-size: 17px;
            font-weight: 600;
            color: var(--text-primary);
            margin-bottom: 4px;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }

        .platform-url {
            font-size: 13px;
            color: var(--accent);
            text-decoration: none;
            display: flex;
            align-items: center;
            gap: 4px;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }

        .platform-url:hover {
            text-decoration: underline;
        }

        /* Tags container with flex wrap */
        .card-tags {
            display: flex;
            flex-wrap: wrap;
            gap: 6px;
            margin-top: 6px;
        }

        .card-tag {
            display: inline-flex;
            align-items: center;
            padding: 4px 10px;
            background: var(--bg-tertiary);
            border: 1px solid var(--border);
            border-radius: 12px;
            font-size: 11px;
            font-weight: 500;
            color: var(--text-secondary);
            transition: all 0.2s;
        }

        .card-tag:hover {
            background: var(--accent);
            color: white;
            border-color: var(--accent);
        }

        .card-actions {
            display: flex;
            gap: 4px;
            opacity: 0;
            transition: opacity 0.2s;
            flex-shrink: 0;
        }

        .account-card:hover .card-actions {
            opacity: 1;
        }

        .card-btn {
            width: 32px;
            height: 32px;
            border: none;
            background: transparent;
            color: var(--text-tertiary);
            cursor: pointer;
            border-radius: 6px;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.2s;
            outline: none;
        }

        .card-btn:hover {
            background: var(--bg-secondary);
            color: var(--text-primary);
        }

        .card-btn.delete:hover {
            color: var(--danger);
        }

        /* Fields */
        .fields {
            display: flex;
            flex-direction: column;
            gap: 12px;
            min-width: 0;
        }

        .field {
            display: flex;
            flex-direction: column;
            gap: 4px;
            min-width: 0;
        }

        .field-label {
            font-size: 12px;
            color: var(--text-tertiary);
            font-weight: 500;
            text-transform: uppercase;
            letter-spacing: 0.3px;
        }

        .field-value {
            display: flex;
            align-items: center;
            gap: 8px;
            background: var(--bg-secondary);
            padding: 10px 12px;
            border-radius: 10px;
            font-size: 14px;
            font-family: 'SF Mono', Monaco, monospace;
            min-width: 0;
        }

        .field-text {
            flex: 1;
            min-width: 0;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
            color: var(--text-primary);
        }

        .password-text {
            filter: blur(4px);
            transition: filter 0.2s;
            user-select: none;
        }

        .password-text.revealed {
            filter: none;
            user-select: text;
        }

        .field-actions {
            display: flex;
            gap: 4px;
            flex-shrink: 0;
        }

        .field-btn {
            padding: 4px 8px;
            border: none;
            background: transparent;
            color: var(--text-tertiary);
            cursor: pointer;
            border-radius: 4px;
            font-size: 12px;
            font-weight: 500;
            transition: all 0.2s;
            outline: none;
        }

        .field-btn:hover {
            background: var(--bg-primary);
            color: var(--accent);
        }

        .field-btn.copied {
            color: var(--success);
        }

        .note-text {
            font-size: 13px;
            color: var(--text-secondary);
            line-height: 1.5;
            padding: 8px 12px;
            background: var(--bg-secondary);
            border-radius: 8px;
            margin-top: 4px;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
            max-width: 100%;
        }

        /* Empty State */
        .empty-state {
            text-align: center;
            padding: 80px 20px;
            color: var(--text-tertiary);
        }

        .empty-icon {
            width: 80px;
            height: 80px;
            margin: 0 auto 20px;
            background: var(--bg-secondary);
            border-radius: 20px;
            display: flex;
            align-items: center;
            justify-content: center;
            color: var(--text-tertiary);
        }

        .empty-title {
            font-size: 18px;
            font-weight: 600;
            color: var(--text-primary);
            margin-bottom: 8px;
        }

        .empty-desc {
            font-size: 14px;
            max-width: 400px;
            margin: 0 auto;
            line-height: 1.6;
        }

        /* Modal */
        .modal-overlay {
            position: fixed;
            inset: 0;
            background: var(--modal-overlay);
            backdrop-filter: blur(8px);
            display: none;
            justify-content: center;
            align-items: center;
            z-index: 1000;
            padding: 20px;
        }

        .modal-overlay.active {
            display: flex;
        }

        .modal {
            background: var(--bg-primary);
            border-radius: 20px;
            width: 100%;
            max-width: 480px;
            max-height: 90vh;
            overflow: hidden;
            box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
            animation: modalIn 0.3s ease;
        }

        @keyframes modalIn {
            from {
                opacity: 0;
                transform: scale(0.95);
            }
            to {
                opacity: 1;
                transform: scale(1);
            }
        }

        .modal-header {
            padding: 24px 24px 0;
        }

        .modal-title {
            font-size: 20px;
            font-weight: 600;
            margin-bottom: 4px;
        }

        .modal-subtitle {
            font-size: 14px;
            color: var(--text-secondary);
        }

        .modal-body {
            padding: 20px 24px;
            overflow-y: auto;
            max-height: calc(90vh - 140px);
        }

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

        .form-label {
            display: block;
            font-size: 13px;
            font-weight: 500;
            color: var(--text-secondary);
            margin-bottom: 6px;
        }

        .form-input {
            width: 100%;
            padding: 12px 14px;
            border: 1px solid var(--border);
            border-radius: 10px;
            background: var(--input-bg);
            color: var(--text-primary);
            font-size: 15px;
            transition: all 0.2s;
            outline: none;
        }

        .form-input:focus {
            outline: none;
            border-color: var(--accent);
        }

        .form-input::placeholder {
            color: var(--text-tertiary);
        }

        textarea.form-input {
            min-height: 80px;
            resize: vertical;
            font-family: inherit;
            line-height: 1.5;
        }

        .input-hint {
            font-size: 12px;
            color: var(--text-tertiary);
            margin-top: 4px;
        }

        /* Password field with generator */
        .password-field-wrapper {
            position: relative;
            display: flex;
            gap: 8px;
            align-items: center;
        }

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

        .password-actions {
            display: flex;
            gap: 4px;
            align-items: center;
        }

        .icon-action-btn {
            width: 36px;
            height: 36px;
            border: none;
            background: var(--bg-secondary);
            color: var(--text-secondary);
            cursor: pointer;
            border-radius: 8px;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.2s;
            outline: none;
            flex-shrink: 0;
        }

        .icon-action-btn:hover {
            background: var(--accent);
            color: white;
        }

        .icon-action-btn svg {
            width: 18px;
            height: 18px;
        }

        /* Tags input with pipe separator */
        .tags-simple-input {
            width: 100%;
            padding: 12px 14px;
            border: 1px solid var(--border);
            border-radius: 10px;
            background: var(--input-bg);
            color: var(--text-primary);
            font-size: 15px;
            transition: all 0.2s;
            outline: none;
        }

        .tags-simple-input:focus {
            outline: none;
            border-color: var(--accent);
        }

        .modal-footer {
            padding: 16px 24px 24px;
            display: flex;
            justify-content: flex-end;
            gap: 12px;
            border-top: 1px solid var(--border);
        }

        .btn-text {
            padding: 10px 20px;
            border: 1px solid var(--border);
            background: var(--bg-secondary);
            color: var(--text-secondary);
            cursor: pointer;
            font-size: 15px;
            font-weight: 500;
            border-radius: 10px;
            transition: all 0.2s;
            outline: none;
            min-width: 80px;
            text-align: center;
        }

        .btn-text:hover {
            background: var(--bg-tertiary);
            color: var(--text-primary);
            border-color: var(--text-tertiary);
        }

        .btn-text.danger {
            color: var(--danger);
            border-color: var(--danger);
            background: rgba(255, 59, 48, 0.05);
        }

        .btn-text.danger:hover {
            background: var(--danger);
            color: white;
            border-color: var(--danger);
        }

        /* View Modal Styles */
        .view-modal .modal-body {
            padding: 24px;
        }

        .view-field {
            margin-bottom: 20px;
        }

        .view-field:last-child {
            margin-bottom: 0;
        }

        .view-label {
            font-size: 12px;
            color: var(--text-tertiary);
            font-weight: 500;
            text-transform: uppercase;
            letter-spacing: 0.3px;
            margin-bottom: 8px;
        }

        .view-value-box {
            background: var(--bg-secondary);
            padding: 14px 16px;
            border-radius: 12px;
            font-family: 'SF Mono', Monaco, monospace;
            font-size: 15px;
            color: var(--text-primary);
            word-break: break-all;
            display: flex;
            align-items: center;
            justify-content: space-between;
            gap: 12px;
        }

        .view-value-text {
            flex: 1;
            overflow-wrap: break-word;
        }

        .view-actions {
            display: flex;
            gap: 8px;
            flex-shrink: 0;
        }

        .view-btn {
            padding: 6px 12px;
            border: none;
            background: var(--bg-primary);
            color: var(--text-secondary);
            cursor: pointer;
            border-radius: 6px;
            font-size: 13px;
            font-weight: 500;
            transition: all 0.2s;
            outline: none;
        }

        .view-btn:hover {
            background: var(--accent);
            color: white;
        }

        .view-btn.copied {
            background: var(--success);
            color: white;
        }

        .view-tags {
            display: flex;
            flex-wrap: wrap;
            gap: 8px;
        }

        .view-tag {
            padding: 6px 14px;
            background: var(--bg-secondary);
            color: var(--text-secondary);
            border-radius: 20px;
            font-size: 13px;
            font-weight: 500;
        }

        /* View note with resize like edit mode */
        .view-note-resizable {
            background: var(--bg-secondary);
            padding: 14px 16px;
            border-radius: 12px;
            font-size: 14px;
            color: var(--text-secondary);
            line-height: 1.6;
            white-space: pre-wrap;
            width: 100%;
            min-height: 80px;
            resize: vertical;
            border: 1px solid transparent;
            font-family: inherit;
            overflow: auto;
        }

        .view-note-resizable:focus {
            outline: none;
            border-color: var(--accent);
        }

        .view-link {
            display: inline-flex;
            align-items: center;
            gap: 6px;
            color: var(--accent);
            text-decoration: none;
            font-size: 14px;
            margin-top: 8px;
        }

        .view-link:hover {
            text-decoration: underline;
        }

        /* Toast */
        .toast {
            position: fixed;
            bottom: 24px;
            right: 24px;
            background: var(--text-primary);
            color: var(--bg-primary);
            padding: 14px 20px;
            border-radius: 12px;
            font-size: 14px;
            font-weight: 500;
            display: none;
            align-items: center;
            gap: 10px;
            box-shadow: 0 10px 30px var(--shadow);
            z-index: 2000;
            animation: toastIn 0.3s ease;
        }

        .toast.show {
            display: flex;
        }

        @keyframes toastIn {
            from {
                transform: translateY(20px);
                opacity: 0;
            }
            to {
                transform: translateY(0);
                opacity: 1;
            }
        }

        /* Confirm Modal */
        .confirm-modal .modal-body {
            padding: 24px;
            text-align: center;
        }

        .confirm-icon {
            width: 64px;
            height: 64px;
            margin: 0 auto 16px;
            background: var(--danger);
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
        }

        .confirm-title {
            font-size: 18px;
            font-weight: 600;
            margin-bottom: 8px;
        }

        .confirm-desc {
            font-size: 14px;
            color: var(--text-secondary);
            line-height: 1.5;
        }

        /* Double confirm input */
        .confirm-input-group {
            margin-top: 20px;
            text-align: left;
        }

        .confirm-input-label {
            font-size: 13px;
            color: var(--text-secondary);
            margin-bottom: 8px;
            display: block;
        }

        .confirm-input {
            width: 100%;
            padding: 12px 14px;
            border: 1px solid var(--border);
            border-radius: 10px;
            background: var(--input-bg);
            color: var(--text-primary);
            font-size: 15px;
            text-align: center;
            font-weight: 500;
        }

        .confirm-input:focus {
            outline: none;
            border-color: var(--danger);
        }

        .confirm-input.error {
            border-color: var(--danger);
            background: rgba(255, 59, 48, 0.1);
        }

        /* View Modal Tags */
        .view-tags-container {
            display: flex;
            flex-wrap: wrap;
            gap: 8px;
        }

        .view-tag-item {
            padding: 6px 14px;
            background: var(--bg-secondary);
            border: 1px solid var(--border);
            border-radius: 20px;
            font-size: 13px;
            font-weight: 500;
            color: var(--text-secondary);
        }

        /* Password Generator Settings */

        .generator-settings {
            background: var(--bg-secondary);
            border-radius: 12px;
            padding: 16px;
            margin-top: 12px;
            border: 1px solid var(--border);
        }

        .generator-settings-title {
            font-size: 14px;
            font-weight: 600;
            color: var(--text-primary);
            margin-bottom: 12px;
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .setting-row {
            display: flex;
            align-items: center;
            justify-content: space-between;
            margin-bottom: 12px;
        }

        .setting-row:last-child {
            margin-bottom: 0;
        }

        .setting-label {
            font-size: 14px;
            color: var(--text-secondary);
        }

        .setting-control {
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .length-input {
            width: 60px;
            padding: 6px 10px;
            border: 1px solid var(--border);
            border-radius: 6px;
            background: var(--input-bg);
            color: var(--text-primary);
            font-size: 14px;
            text-align: center;
        }

        .checkbox-wrapper {
            display: flex;
            align-items: center;
            gap: 6px;
            cursor: pointer;
        }

        .checkbox-wrapper input[type="checkbox"] {
            width: 18px;
            height: 18px;
            cursor: pointer;
        }

        .checkbox-wrapper span {
            font-size: 14px;
            color: var(--text-secondary);
        }

        /* Export/Import Options */
        .export-options {
            display: flex;
            gap: 12px;
            margin-bottom: 20px;
        }

        .export-option {
            flex: 1;
            padding: 16px;
            border: 2px solid var(--border);
            border-radius: 12px;
            cursor: pointer;
            text-align: center;
            transition: all 0.2s;
        }

        .export-option:hover {
            border-color: var(--accent);
        }

        .export-option.selected {
            border-color: var(--accent);
            background: rgba(0, 102, 255, 0.05);
        }

        .export-option-icon {
            width: 48px;
            height: 48px;
            margin: 0 auto 8px;
            background: var(--bg-secondary);
            border-radius: 12px;
            display: flex;
            align-items: center;
            justify-content: center;
            color: var(--accent);
        }

        .export-option-title {
            font-size: 15px;
            font-weight: 600;
            color: var(--text-primary);
            margin-bottom: 4px;
        }

        .export-option-desc {
            font-size: 12px;
            color: var(--text-secondary);
        }

        /* Duplicate confirm modal */
        .duplicate-info {
            background: var(--bg-secondary);
            border-radius: 12px;
            padding: 16px;
            margin: 16px 0;
            text-align: left;
        }

        .duplicate-info-item {
            display: flex;
            gap: 8px;
            margin-bottom: 8px;
            font-size: 14px;
        }

        .duplicate-info-item:last-child {
            margin-bottom: 0;
        }

        .duplicate-info-label {
            color: var(--text-tertiary);
            min-width: 60px;
        }

        .duplicate-info-value {
            color: var(--text-primary);
            font-weight: 500;
        }

        /* Desktop Styles (default) */
        @media (min-width: 769px) {
            .mobile-only {
                display: none !important;
            }
        }

        /* Mobile Styles - 90% zoom */
        @media (max-width: 768px) {
            html {
                zoom: 0.9;
                -moz-transform: scale(0.9);
                -moz-transform-origin: 0 0;
            }

            @supports not (zoom: 0.9) {
                body {
                    transform: scale(0.9);
                    transform-origin: 0 0;
                    width: 111.11%;
                    height: 111.11%;
                }
            }

            .desktop-only {
                display: none !important;
            }

            .header-content {
                padding: 12px 16px;
            }

            .search-section {
                padding: 16px;
            }

            .search-section .btn-primary {
                display: none;
            }

            .sort-icon-btn {
                padding: 4px 10px;
                margin-left: auto;
            }

            .sort-icon-btn svg {
                width: 12px;
                height: 12px;
            }

            .sort-text {
                font-size: 11px;
            }

            .tags-section {
                padding: 0 16px 16px;
                position: relative;
                padding-right: 60px;
            }

            .mobile-add-btn {
                position: absolute;
                right: 16px;
                top: 0;
                width: 36px;
                height: 36px;
                border: none;
                background: var(--accent);
                color: white;
                border-radius: 10px;
                display: flex;
                align-items: center;
                justify-content: center;
                cursor: pointer;
                transition: all 0.2s;
                outline: none;
            }

            .mobile-add-btn:hover {
                background: var(--accent-hover);
                transform: scale(1.05);
            }

            .container {
                padding: 0 16px 32px;
            }

            .accounts-grid {
                grid-template-columns: 1fr;
            }

            .card-actions {
                opacity: 1;
            }

            /* Mobile copy fix */
            .field-btn {
                padding: 8px 12px;
                font-size: 13px;
            }

            /* Ensure card doesn't overflow */
            .account-card {
                min-width: 0;
                max-width: 100%;
            }

            .fields {
                min-width: 0;
            }

            .field-value {
                min-width: 0;
            }

            .note-text {
                max-width: 100%;
            }

            /* Toast position fix for zoomed viewport */
            .toast {
                right: 20px;
                bottom: 20px;
            }

            /* Generator settings on mobile */
            .generator-settings {
                padding: 12px;
            }

            .setting-row {
                flex-direction: column;
                align-items: flex-start;
                gap: 8px;
            }
        }

        /* Card Footer with Update Time */
        .card-footer {
            margin-top: 16px;
            padding-top: 12px;
            border-top: 1px solid var(--border);
            display: flex;
            justify-content: flex-end;
        }

        .update-time {
            display: flex;
            align-items: center;
            gap: 6px;
            font-size: 12px;
            color: var(--text-tertiary);
        }

        .update-time svg {
            opacity: 0.6;
        }

        /* Scrollbar */

        ::-webkit-scrollbar {
            width: 8px;
            height: 8px;
        }

        ::-webkit-scrollbar-track {
            background: transparent;
        }

        ::-webkit-scrollbar-thumb {
            background: var(--border);
            border-radius: 4px;
        }

        ::-webkit-scrollbar-thumb:hover {
            background: var(--text-tertiary);
        }
    </style>
<base target="_blank">
</head>
<body>
    <header class="header">
        <div class="header-content">
            <div class="logo">
                <div class="logo-icon">
                    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
                        <rect x="3" y="11" width="18" height="11" rx="2"></rect>
                        <path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
                    </svg>
                </div>
                <span>账号管理器</span>
            </div>
            <div class="header-actions">
                <button class="icon-btn" onclick="toggleTheme()" title="切换主题">
                    <svg id="themeIcon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                        <circle cx="12" cy="12" r="5"></circle>
                        <line x1="12" y1="1" x2="12" y2="3"></line>
                        <line x1="12" y1="21" x2="12" y2="23"></line>
                        <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
                        <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
                        <line x1="1" y1="12" x2="3" y2="12"></line>
                        <line x1="21" y1="12" x2="23" y2="12"></line>
                        <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
                        <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
                    </svg>
                </button>
                <button class="icon-btn" onclick="showExportModal()" title="导出数据">
                    <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                        <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
                        <polyline points="7 10 12 15 17 10"></polyline>
                        <line x1="12" y1="15" x2="12" y2="3"></line>
                    </svg>
                </button>
                <button class="icon-btn" onclick="document.getElementById('importFile').click()" title="导入数据">
                    <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                        <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
                        <polyline points="17 8 12 3 7 8"></polyline>
                        <line x1="12" y1="3" x2="12" y2="15"></line>
                    </svg>
                </button>
                <button class="icon-btn danger" onclick="confirmClearAll()" title="清空所有数据">
                    <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                        <polyline points="3 6 5 6 21 6"></polyline>
                        <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
                    </svg>
                </button>
                <input type="file" id="importFile" style="display: none" accept=".json" onchange="importData(this)">
            </div>
        </div>
    </header>

    <section class="search-section">
        <div class="search-box">
            <svg class="search-icon" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                <circle cx="11" cy="11" r="8"></circle>
                <path d="m21 21-4.35-4.35"></path>
            </svg>
            <input type="text" class="search-input" id="searchInput" placeholder="搜索平台、账号、标签、链接或备注..." oninput="renderAccounts()">
        </div>
        <button class="btn btn-primary desktop-only" onclick="openModal()">
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
                <line x1="12" y1="5" x2="12" y2="19"></line>
                <line x1="5" y1="12" x2="19" y2="12"></line>
            </svg>
            添加账号
        </button>
    </section>

    <section class="tags-section" id="tagsContainer">
        <span class="tag-label">筛选:</span>
        <span class="tag active" onclick="filterByTag('all')">全部</span>
    </section>

    <main class="container">
        <div class="stats-bar">
            <div class="stat-item">
                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                    <rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect>
                    <line x1="16" y1="2" x2="16" y2="6"></line>
                    <line x1="8" y1="2" x2="8" y2="6"></line>
                    <line x1="3" y1="10" x2="21" y2="10"></line>
                </svg>
                <span>共 <span class="stat-value" id="totalCount">0</span> 个账号</span>
            </div>
            <div class="stat-item">
                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                    <circle cx="12" cy="12" r="10"></circle>
                    <polyline points="12 6 12 12 16 14"></polyline>
                </svg>
                <span>更新于 <span class="stat-value" id="lastUpdate">-</span></span>
            </div>
            <button class="sort-icon-btn" id="sortBtn" onclick="toggleSortOrder()" title="切换排序">
                <svg id="sortIcon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
                    <polyline points="12 6 12 18"></polyline>
                    <polyline points="8 10 12 6 16 10"></polyline>
                </svg>
                <span class="sort-text" id="sortText">最新</span>
            </button>
        </div>

        <div id="contentArea">
            <div class="empty-state" id="emptyState">
                <div class="empty-icon">
                    <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
                        <rect x="3" y="11" width="18" height="11" rx="2"></rect>
                        <path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
                    </svg>
                </div>
                <div class="empty-title">暂无账号</div>
                <div class="empty-desc">点击右上角"添加账号"开始使用,或导入已有数据</div>
            </div>
            <div class="accounts-grid" id="accountsGrid" style="display: none;"></div>
        </div>
    </main>

    <!-- Add/Edit Modal - No overlay click close -->
    <div class="modal-overlay" id="modal">
        <div class="modal">
            <div class="modal-header">
                <h2 class="modal-title" id="modalTitle">添加账号</h2>
                <p class="modal-subtitle">数据仅保存在本地浏览器中</p>
            </div>
            <form class="modal-body" id="accountForm" onsubmit="saveAccount(event)">
                <input type="hidden" id="editId">
                
                <div class="form-group">
                    <label class="form-label">平台名称 *</label>
                    <input type="text" class="form-input" id="platform" placeholder="例如:微信、GitHub、支付宝" required>
                </div>

                <div class="form-group">
                    <label class="form-label">用户名 *</label>
                    <input type="text" class="form-input" id="username" placeholder="邮箱/手机号/用户名" required>
                </div>

                <div class="form-group">
                    <label class="form-label">密码 *</label>
                    <div class="password-field-wrapper">
                        <div class="password-input-wrapper">
                            <input type="password" class="form-input" id="password" placeholder="密码" required style="padding-right: 40px;">
                        </div>
                        <div class="password-actions">
                            <!-- Generate password button -->
                            <button type="button" class="icon-action-btn" onclick="generatePassword()" title="生成随机密码">
                                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                    <rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
                                    <path d="M7 11V7a5 5 0 0 1 9.9-1"></path>
                                    <circle cx="12" cy="16" r="1" fill="currentColor"></circle>
                                    <line x1="8" y1="16" x2="8" y2="16"></line>
                                    <line x1="16" y1="16" x2="16" y2="16"></line>
                                </svg>
                            </button>
                            <!-- Settings button -->
                            <button type="button" class="icon-action-btn" onclick="toggleGeneratorSettings()" title="密码生成器设置">
                                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                    <circle cx="12" cy="12" r="3"></circle>
                                    <path d="M12 1v6m0 6v6m4.22-10.22l4.24-4.24M6.34 6.34L2.1 2.1m17.8 17.8l-4.24-4.24M6.34 17.66l-4.24 4.24M23 12h-6m-6 0H1m20.07-4.93l-4.24 4.24M6.34 6.34l-4.24-4.24"></path>
                                </svg>
                            </button>
                            <!-- Toggle visibility -->
                            <button type="button" class="icon-action-btn" onclick="togglePasswordInput()" title="显示/隐藏密码">
                                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                    <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
                                    <circle cx="12" cy="12" r="3"></circle>
                                </svg>
                            </button>
                        </div>
                    </div>
                    <!-- Generator Settings Panel -->
                    <div class="generator-settings" id="generatorSettings" style="display: none;">
                        <div class="generator-settings-title">
                            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                <circle cx="12" cy="12" r="3"></circle>
                                <path d="M12 1v6m0 6v6m4.22-10.22l4.24-4.24M6.34 6.34L2.1 2.1m17.8 17.8l-4.24-4.24M6.34 17.66l-4.24 4.24M23 12h-6m-6 0H1m20.07-4.93l-4.24 4.24M6.34 6.34l-4.24-4.24"></path>
                            </svg>
                            密码生成器设置
                        </div>
                        <div class="setting-row">
                            <span class="setting-label">密码长度</span>
                            <div class="setting-control">
                                <input type="number" class="length-input" id="pwdLength" value="16" min="4" max="64">
                            </div>
                        </div>
                        <div class="setting-row">
                            <span class="setting-label">包含大写字母 (A-Z)</span>
                            <label class="checkbox-wrapper">
                                <input type="checkbox" id="pwdUppercase" checked>
                                <span>启用</span>
                            </label>
                        </div>
                        <div class="setting-row">
                            <span class="setting-label">包含小写字母 (a-z)</span>
                            <label class="checkbox-wrapper">
                                <input type="checkbox" id="pwdLowercase" checked>
                                <span>启用</span>
                            </label>
                        </div>
                        <div class="setting-row">
                            <span class="setting-label">包含数字 (0-9)</span>
                            <label class="checkbox-wrapper">
                                <input type="checkbox" id="pwdNumbers" checked>
                                <span>启用</span>
                            </label>
                        </div>
                        <div class="setting-row">
                            <span class="setting-label">包含符号 (!@#$...)</span>
                            <label class="checkbox-wrapper">
                                <input type="checkbox" id="pwdSymbols">
                                <span>启用</span>
                            </label>
                        </div>
                    </div>
                </div>

                <div class="form-group">
                    <label class="form-label">网站链接</label>
                    <input type="text" class="form-input" id="url" placeholder="https://...">
                    <div class="input-hint">可选,方便快速访问网站,自动补全 http://</div>
                </div>

                <div class="form-group">
                    <label class="form-label">标签</label>
                    <input type="text" class="tags-simple-input" id="tagsInput" placeholder="工作|学习|生活">
                    <div class="input-hint">使用竖杆 | 分隔多个标签</div>
                </div>

                <div class="form-group">
                    <label class="form-label">备注</label>
                    <textarea class="form-input" id="note" placeholder="记录其他信息,如安全问题、绑定手机、备用邮箱等..."></textarea>
                </div>
            </form>
            <div class="modal-footer">
                <button type="button" class="btn-text danger" onclick="confirmDeleteCurrent()" id="deleteEditBtn" style="display: none; margin-right: auto;">删除</button>
                <button type="button" class="btn-text" onclick="closeModal()">取消</button>
                <button type="button" class="btn btn-primary" onclick="document.getElementById('accountForm').dispatchEvent(new Event('submit'))">保存</button>
            </div>
        </div>
    </div>

    <!-- View Modal -->
    <div class="modal-overlay" id="viewModal" onclick="closeViewModalOnOverlay(event)">
        <div class="modal view-modal">
            <div class="modal-header">
                <h2 class="modal-title" id="viewModalTitle">查看账号</h2>
                <p class="modal-subtitle" id="viewModalSubtitle">查看账号详情</p>
            </div>
            <div class="modal-body" id="viewModalBody">
                <!-- Dynamic content -->
            </div>
            <div class="modal-footer">
                <button type="button" class="btn-text danger" onclick="confirmDeleteFromView()" style="margin-right: auto;">删除</button>
                <button type="button" class="btn-text" onclick="closeViewModal()">关闭</button>
                <button type="button" class="btn btn-primary" id="viewEditBtn">编辑</button>
            </div>
        </div>
    </div>

    <!-- Export Modal -->
    <div class="modal-overlay" id="exportModal" onclick="closeExportModalOnOverlay(event)">
        <div class="modal">
            <div class="modal-header">
                <h2 class="modal-title">导出数据</h2>
                <p class="modal-subtitle">选择导出方式</p>
            </div>
            <div class="modal-body">
                <div class="export-options">
                    <div class="export-option selected" onclick="selectExportType('plain')" id="exportPlain">
                        <div class="export-option-icon">
                            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
                                <polyline points="14 2 14 8 20 8"></polyline>
                                <line x1="16" y1="13" x2="8" y2="13"></line>
                                <line x1="16" y1="17" x2="8" y2="17"></line>
                            </svg>
                        </div>
                        <div class="export-option-title">普通导出</div>
                        <div class="export-option-desc">明文 JSON 格式</div>
                    </div>
                    <div class="export-option" onclick="selectExportType('encrypted')" id="exportEncrypted">
                        <div class="export-option-icon">
                            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                <rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
                                <path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
                            </svg>
                        </div>
                        <div class="export-option-title">加密导出</div>
                        <div class="export-option-desc">AES-256 加密</div>
                    </div>
                </div>
                
                <div id="exportPasswordSection" style="display: none;">
                    <div class="form-group">
                        <label class="form-label">设置导出密码 *</label>
                        <input type="password" class="form-input" id="exportPassword" placeholder="输入密码用于加密数据">
                        <div class="input-hint">请牢记此密码,导入时需要使用</div>
                    </div>
                    <div class="form-group">
                        <label class="form-label">确认密码 *</label>
                        <input type="password" class="form-input" id="exportPasswordConfirm" placeholder="再次输入密码">
                    </div>
                </div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn-text" onclick="closeExportModal()">取消</button>
                <button type="button" class="btn btn-primary" onclick="executeExport()">导出</button>
            </div>
        </div>
    </div>

    <!-- Import Password Modal -->
    <div class="modal-overlay" id="importPasswordModal">
        <div class="modal">
            <div class="modal-header">
                <h2 class="modal-title">输入导入密码</h2>
                <p class="modal-subtitle">此数据文件已加密,需要密码才能解密</p>
            </div>
            <div class="modal-body">
                <div class="form-group">
                    <label class="form-label">解密密码 *</label>
                    <input type="password" class="form-input" id="importPassword" placeholder="输入导出时设置的密码">
                </div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn-text" onclick="cancelImport()">取消</button>
                <button type="button" class="btn btn-primary" onclick="executeImportWithPassword()">导入</button>
            </div>
        </div>
    </div>

    <!-- Duplicate Confirm Modal -->
    <div class="modal-overlay" id="duplicateModal">
        <div class="modal">
            <div class="modal-header">
                <h2 class="modal-title">发现重复账号</h2>
                <p class="modal-subtitle">该账号信息已存在</p>
            </div>
            <div class="modal-body">
                <div class="confirm-desc">以下账号的所有信息与您要添加的账号完全相同:</div>
                <div class="duplicate-info" id="duplicateInfo">
                    <!-- Dynamic content -->
                </div>
                <div class="confirm-desc" style="margin-top: 16px; color: var(--warning);">
                    <strong>提示:</strong>选择"覆盖"将用新信息替换原有账号(保留原ID和创建时间)。
                </div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn-text" onclick="cancelDuplicate()">取消添加</button>
                <button type="button" class="btn btn-danger" onclick="forceAddDuplicate()">强制添加(覆盖)</button>
            </div>
        </div>
    </div>

    <!-- Confirm Clear Modal with Double Confirm -->
    <div class="modal-overlay" id="confirmClearModal" onclick="closeConfirmClearModalOnOverlay(event)">
        <div class="modal confirm-modal">
            <div class="modal-body">
                <div class="confirm-icon">
                    <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                        <polyline points="3 6 5 6 21 6"></polyline>
                        <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
                    </svg>
                </div>
                <div class="confirm-title">确认清空所有数据?</div>
                <div class="confirm-desc">此操作不可恢复,所有账号数据将被永久删除。<br>建议先导出数据备份。</div>
                
                <div class="confirm-input-group">
                    <label class="confirm-input-label">请输入 <strong>DELETE</strong> 确认清空:</label>
                    <input type="text" class="confirm-input" id="clearConfirmInput" placeholder="输入 DELETE">
                </div>
            </div>
            <div class="modal-footer" style="justify-content: center;">
                <button type="button" class="btn-text" onclick="closeConfirmClearModal()">取消</button>
                <button type="button" class="btn btn-danger" onclick="executeClearAll()" id="confirmClearBtn" disabled>确认清空</button>
            </div>
        </div>
    </div>

    <!-- Simple Confirm Modal for single delete -->
    <div class="modal-overlay" id="confirmModal" onclick="closeConfirmModalOnOverlay(event)">
        <div class="modal confirm-modal">
            <div class="modal-body">
                <div class="confirm-icon">
                    <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                        <polyline points="3 6 5 6 21 6"></polyline>
                        <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
                    </svg>
                </div>
                <div class="confirm-title" id="simpleConfirmTitle">确认删除?</div>
                <div class="confirm-desc" id="simpleConfirmDesc">此操作不可恢复。</div>
            </div>
            <div class="modal-footer" style="justify-content: center;">
                <button type="button" class="btn-text" onclick="closeConfirmModal()">取消</button>
                <button type="button" class="btn btn-danger" onclick="executeSimpleConfirm()">确认删除</button>
            </div>
        </div>
    </div>

    <div class="toast" id="toast">
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
            <polyline points="20 6 9 17 4 12"></polyline>
        </svg>
        <span id="toastMsg">操作成功</span>
    </div>

    <script>
        // Data
        let accounts = JSON.parse(localStorage.getItem('pwd_accounts') || '[]');
        let currentFilter = 'all';
        let editingId = null;
        let viewingId = null;
        let simpleConfirmCallback = null;
        let duplicateCallback = null;
        let pendingExportType = 'plain';
        let pendingImportData = null;
        let pendingImportFile = null;

        // Password Generator Settings
        const defaultPwdSettings = {
            length: 16,
            uppercase: true,
            lowercase: true,
            numbers: true,
            symbols: false
        };

        // Check if mobile
        function isMobile() {
            return window.innerWidth <= 768;
        }

        // Parse tags from pipe-separated string
        function parseTags(tagsStr) {
            if (!tagsStr) return [];
            return tagsStr.split('|').map(t => t.trim()).filter(t => t.length > 0);
        }

        // Format tags to pipe-separated string
        function formatTags(tags) {
            if (!tags || tags.length === 0) return '';
            return tags.join(' | ');
        }

        // Auto-fix URL
        function fixUrl(url) {
            if (!url) return '';
            url = url.trim();
            if (!url) return '';
            if (!/^https?:\/\//i.test(url)) {
                url = 'http://' + url;
            }
            return url;
        }

        // Check if account is exactly the same
        function isAccountEqual(acc1, acc2) {
            const normalizeTags = (tags) => {
                if (!tags) return '';
                return [...tags].sort().join('|');
            };
            
            return acc1.platform === acc2.platform &&
                   acc1.username === acc2.username &&
                   acc1.password === acc2.password &&
                   (acc1.url || '') === (acc2.url || '') &&
                   normalizeTags(acc1.tags) === normalizeTags(acc2.tags) &&
                   (acc1.note || '') === (acc2.note || '');
        }

        // Check for duplicate account
        function findDuplicate(newAcc, excludeId = null) {
            return accounts.find(acc => {
                if (excludeId && acc.id === excludeId) return false;
                return isAccountEqual(acc, newAcc);
            });
        }

        // Simple XOR encryption for demo (in production use Web Crypto API)
        async function encryptData(data, password) {
            const encoder = new TextEncoder();
            const dataBytes = encoder.encode(JSON.stringify(data));
            const keyBytes = encoder.encode(password);
            
            const encrypted = new Uint8Array(dataBytes.length);
            for (let i = 0; i < dataBytes.length; i++) {
                encrypted[i] = dataBytes[i] ^ keyBytes[i % keyBytes.length];
            }
            
            // Convert to base64
            const base64 = btoa(String.fromCharCode(...encrypted));
            return {
                encrypted: true,
                data: base64,
                salt: Date.now().toString()
            };
        }

        async function decryptData(encryptedObj, password) {
            try {
                const decoder = new TextDecoder();
                const encoder = new TextEncoder();
                const keyBytes = encoder.encode(password);
                
                // Decode base64
                const encryptedBytes = Uint8Array.from(atob(encryptedObj.data), c => c.charCodeAt(0));
                
                const decrypted = new Uint8Array(encryptedBytes.length);
                for (let i = 0; i < encryptedBytes.length; i++) {
                    decrypted[i] = encryptedBytes[i] ^ keyBytes[i % keyBytes.length];
                }
                
                const jsonStr = decoder.decode(decrypted);
                return JSON.parse(jsonStr);
            } catch (e) {
                throw new Error('密码错误或数据损坏');
            }
        }

        // Theme
        function initTheme() {
            const saved = localStorage.getItem('pwd_theme') || 'light';
            document.documentElement.setAttribute('data-theme', saved);
            updateThemeIcon(saved);
        }

        function toggleTheme() {
            const current = document.documentElement.getAttribute('data-theme');
            const next = current === 'dark' ? 'light' : 'dark';
            document.documentElement.setAttribute('data-theme', next);
            localStorage.setItem('pwd_theme', next);
            updateThemeIcon(next);
        }

        function updateThemeIcon(theme) {
            const icon = document.getElementById('themeIcon');
            if (theme === 'dark') {
                icon.innerHTML = '<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>';
            } else {
                icon.innerHTML = '<circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>';
            }
        }

        // Tags
        function getAllTags() {
            const tags = new Set();
            accounts.forEach(a => a.tags?.forEach(t => tags.add(t)));
            return Array.from(tags).sort();
        }

        function renderTags() {
            const container = document.getElementById('tagsContainer');
            const allTags = getAllTags();
            
            let html = '<span class="tag-label">筛选:</span>';
            html += `<span class="tag ${currentFilter === 'all' ? 'active' : ''}" onclick="filterByTag('all')">全部</span>`;
            
            allTags.forEach(tag => {
                html += `<span class="tag ${currentFilter === tag ? 'active' : ''}" onclick="filterByTag('${tag}')">${escapeHtml(tag)}</span>`;
            });
            
            // Add mobile button
            html += `<button class="mobile-add-btn mobile-only" onclick="openModal()" title="添加账号">
                <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
                    <line x1="12" y1="5" x2="12" y2="19"></line>
                    <line x1="5" y1="12" x2="19" y2="12"></line>
                </svg>
            </button>`;
            
            container.innerHTML = html;
        }

        function filterByTag(tag) {
            currentFilter = tag;
            renderTags();
            renderAccounts();
        }

        // Password Generator
        function toggleGeneratorSettings() {
            const settings = document.getElementById('generatorSettings');
            settings.style.display = settings.style.display === 'none' ? 'block' : 'none';
        }

        function generatePassword() {
            const length = parseInt(document.getElementById('pwdLength').value) || 16;
            const useUpper = document.getElementById('pwdUppercase').checked;
            const useLower = document.getElementById('pwdLowercase').checked;
            const useNumbers = document.getElementById('pwdNumbers').checked;
            const useSymbols = document.getElementById('pwdSymbols').checked;
            
            let chars = '';
            if (useUpper) chars += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
            if (useLower) chars += 'abcdefghijklmnopqrstuvwxyz';
            if (useNumbers) chars += '0123456789';
            if (useSymbols) chars += '!@#$%^&*()_+-=[]{}|;:,.<>?';
            
            if (chars === '') {
                showToast('请至少选择一种字符类型');
                return;
            }
            
            let password = '';
            const array = new Uint32Array(length);
            crypto.getRandomValues(array);
            
            for (let i = 0; i < length; i++) {
                password += chars[array[i] % chars.length];
            }
            
            document.getElementById('password').value = password;
            document.getElementById('password').type = 'text';
            showToast('密码已生成');
        }

        // Modal & Form - No overlay click close
        function openModal(id = null) {
            editingId = id;
            const modal = document.getElementById('modal');
            const title = document.getElementById('modalTitle');
            const form = document.getElementById('accountForm');
            const deleteBtn = document.getElementById('deleteEditBtn');
            
            // Reset generator settings visibility
            document.getElementById('generatorSettings').style.display = 'none';
            
            if (id) {
                const acc = accounts.find(a => a.id === id);
                title.textContent = '编辑账号';
                document.getElementById('editId').value = id;
                document.getElementById('platform').value = acc.platform;
                document.getElementById('username').value = acc.username;
                document.getElementById('password').value = acc.password;
                document.getElementById('url').value = acc.url || '';
                document.getElementById('tagsInput').value = formatTags(acc.tags);
                document.getElementById('note').value = acc.note || '';
                deleteBtn.style.display = 'block';
            } else {
                title.textContent = '添加账号';
                form.reset();
                document.getElementById('editId').value = '';
                deleteBtn.style.display = 'none';
                // Set default password settings
                document.getElementById('pwdLength').value = defaultPwdSettings.length;
                document.getElementById('pwdUppercase').checked = defaultPwdSettings.uppercase;
                document.getElementById('pwdLowercase').checked = defaultPwdSettings.lowercase;
                document.getElementById('pwdNumbers').checked = defaultPwdSettings.numbers;
                document.getElementById('pwdSymbols').checked = defaultPwdSettings.symbols;
            }
            
            modal.classList.add('active');
            document.getElementById('platform').focus();
        }

        function closeModal() {
            document.getElementById('modal').classList.remove('active');
            editingId = null;
        }

        // Removed: closeModalOnOverlay - now only closes via button

        function togglePasswordInput() {
            const input = document.getElementById('password');
            const btn = event.target.closest('.icon-action-btn');
            if (input.type === 'password') {
                input.type = 'text';
            } else {
                input.type = 'password';
            }
        }

        function confirmDeleteCurrent() {
            if (editingId) {
                const acc = accounts.find(a => a.id === editingId);
                showSimpleConfirm(
                    `确认删除 ${acc.platform}?`,
                    '此操作不可恢复,账号将被永久删除。',
                    () => {
                        deleteAccount(editingId);
                        closeModal();
                    }
                );
            }
        }

        // View Modal
        function openViewModal(id) {
            viewingId = id;
            const acc = accounts.find(a => a.id === id);
            if (!acc) return;

            document.getElementById('viewModalTitle').textContent = acc.platform;
            document.getElementById('viewModalSubtitle').textContent = '查看账号详情';
            
            let html = '';
            
            // Username
            html += `
                <div class="view-field">
                    <div class="view-label">用户名</div>
                    <div class="view-value-box">
                        <span class="view-value-text">${escapeHtml(acc.username)}</span>
                        <div class="view-actions">
                            <button class="view-btn" onclick="copyFromView('${escapeHtml(acc.username)}', this)">复制</button>
                        </div>
                    </div>
                </div>
            `;
            
            // Password
            html += `
                <div class="view-field">
                    <div class="view-label">密码</div>
                    <div class="view-value-box">
                        <span class="view-value-text">
                            <span id="viewPwdText" class="password-text" style="filter: blur(4px); user-select: none;">${escapeHtml(acc.password)}</span>
                        </span>
                        <div class="view-actions">
                            <button class="view-btn" onclick="toggleViewPassword()" id="viewPwdBtn">显示</button>
                            <button class="view-btn" onclick="copyFromView('${escapeHtml(acc.password)}', this)">复制</button>
                        </div>
                    </div>
                </div>
            `;
            
            // Tags - container style
            if (acc.tags && acc.tags.length > 0) {
                html += `
                    <div class="view-field">
                        <div class="view-label">标签</div>
                        <div class="view-tags-container">
                            ${acc.tags.map(t => `<span class="view-tag-item">${escapeHtml(t)}</span>`).join('')}
                        </div>
                    </div>
                `;
            }
            
            // URL
            if (acc.url) {
                html += `
                    <div class="view-field">
                        <div class="view-label">网站链接</div>
                        <a href="${escapeHtml(acc.url)}" target="_blank" class="view-link">
                            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path>
                                <polyline points="15 3 21 3 21 9"></polyline>
                                <line x1="10" y1="14" x2="21" y2="3"></line>
                            </svg>
                            ${escapeHtml(acc.url)}
                        </a>
                    </div>
                `;
            }
            
            // Note - resizable like edit mode
            if (acc.note) {
                html += `
                    <div class="view-field">
                        <div class="view-label">备注(可拖动调整大小)</div>
                        <textarea class="view-note-resizable" readonly>${escapeHtml(acc.note)}</textarea>
                    </div>
                `;
            }
            
            document.getElementById('viewModalBody').innerHTML = html;
            
            // Set edit button action
            document.getElementById('viewEditBtn').onclick = () => {
                closeViewModal();
                setTimeout(() => openModal(id), 100);
            };
            
            document.getElementById('viewModal').classList.add('active');
        }

        function closeViewModal() {
            document.getElementById('viewModal').classList.remove('active');
            viewingId = null;
        }

        function closeViewModalOnOverlay(e) {
            if (e.target === e.currentTarget) closeViewModal();
        }

        function toggleViewPassword() {
            const pwd = document.getElementById('viewPwdText');
            const btn = document.getElementById('viewPwdBtn');
            
            if (pwd.style.filter === 'blur(4px)') {
                pwd.style.filter = 'none';
                pwd.style.userSelect = 'text';
                btn.textContent = '隐藏';
            } else {
                pwd.style.filter = 'blur(4px)';
                pwd.style.userSelect = 'none';
                btn.textContent = '显示';
            }
        }

        function confirmDeleteFromView() {
            if (viewingId) {
                const acc = accounts.find(a => a.id === viewingId);
                showSimpleConfirm(
                    `确认删除 ${acc.platform}?`,
                    '此操作不可恢复,账号将被永久删除。',
                    () => {
                        deleteAccount(viewingId);
                        closeViewModal();
                    }
                );
            }
        }

        function copyFromView(text, btn) {
            navigator.clipboard.writeText(text).then(() => {
                btn.textContent = '已复制';
                btn.classList.add('copied');
                showToast('已复制到剪贴板');
                setTimeout(() => {
                    btn.textContent = '复制';
                    btn.classList.remove('copied');
                }, 2000);
            }).catch(() => {
                // Fallback for mobile
                const textArea = document.createElement('textarea');
                textArea.value = text;
                textArea.style.position = 'fixed';
                textArea.style.left = '-999999px';
                document.body.appendChild(textArea);
                textArea.focus();
                textArea.select();
                try {
                    document.execCommand('copy');
                    btn.textContent = '已复制';
                    btn.classList.add('copied');
                    showToast('已复制到剪贴板');
                    setTimeout(() => {
                        btn.textContent = '复制';
                        btn.classList.remove('copied');
                    }, 2000);
                } catch (err) {
                    showToast('复制失败');
                }
                document.body.removeChild(textArea);
            });
        }

        // Save with duplicate check
        function saveAccount(e) {
            e.preventDefault();
            
            const platform = document.getElementById('platform').value.trim();
            const username = document.getElementById('username').value.trim();
            const password = document.getElementById('password').value;
            const url = fixUrl(document.getElementById('url').value.trim());
            const tags = parseTags(document.getElementById('tagsInput').value);
            const note = document.getElementById('note').value.trim();
            
            if (!platform || !username || !password) return;
            
            const newAcc = {
                platform,
                username,
                password,
                url,
                tags,
                note
            };
            
            // Check for duplicates when adding new account
            if (!editingId) {
                const duplicate = findDuplicate(newAcc);
                if (duplicate) {
                    showDuplicateModal(newAcc, duplicate);
                    return;
                }
            }
            
            // Save directly if editing or no duplicate
            doSaveAccount(newAcc);
        }

        function doSaveAccount(newAcc, replaceId = null) {
            const data = {
                id: replaceId || editingId || Date.now().toString(),
                ...newAcc,
                updatedAt: new Date().toISOString()
            };
            
            if (editingId || replaceId) {
                const idx = accounts.findIndex(a => a.id === (replaceId || editingId));
                if (idx >= 0) {
                    // Preserve original created time if replacing
                    if (replaceId && !editingId) {
                        data.updatedAt = accounts[idx].updatedAt;
                    }
                    accounts[idx] = data;
                }
                showToast(replaceId && !editingId ? '账号已覆盖' : '账号已更新');
            } else {
                accounts.push(data);
                showToast('账号已添加');
            }
            
            saveData();
            closeModal();
            renderAccounts();
            renderTags();
        }

        function showDuplicateModal(newAcc, duplicate) {
            const infoHtml = `
                <div class="duplicate-info-item">
                    <span class="duplicate-info-label">平台</span>
                    <span class="duplicate-info-value">${escapeHtml(duplicate.platform)}</span>
                </div>
                <div class="duplicate-info-item">
                    <span class="duplicate-info-label">用户名</span>
                    <span class="duplicate-info-value">${escapeHtml(duplicate.username)}</span>
                </div>
                <div class="duplicate-info-item">
                    <span class="duplicate-info-label">密码</span>
                    <span class="duplicate-info-value">••••••</span>
                </div>
                ${duplicate.url ? `
                <div class="duplicate-info-item">
                    <span class="duplicate-info-label">链接</span>
                    <span class="duplicate-info-value">${escapeHtml(duplicate.url)}</span>
                </div>
                ` : ''}
                ${duplicate.tags?.length ? `
                <div class="duplicate-info-item">
                    <span class="duplicate-info-label">标签</span>
                    <span class="duplicate-info-value">${formatTags(duplicate.tags)}</span>
                </div>
                ` : ''}
                ${duplicate.note ? `
                <div class="duplicate-info-item">
                    <span class="duplicate-info-label">备注</span>
                    <span class="duplicate-info-value">${escapeHtml(duplicate.note)}</span>
                </div>
                ` : ''}
            `;
            
            document.getElementById('duplicateInfo').innerHTML = infoHtml;
            document.getElementById('duplicateModal').classList.add('active');
            
            // Store pending data
            duplicateCallback = { newAcc, replaceId: duplicate.id };
        }

        function cancelDuplicate() {
            document.getElementById('duplicateModal').classList.remove('active');
            duplicateCallback = null;
        }

        function forceAddDuplicate() {
            if (duplicateCallback) {
                const { newAcc, replaceId } = duplicateCallback;
                doSaveAccount(newAcc, replaceId);
                document.getElementById('duplicateModal').classList.remove('active');
                duplicateCallback = null;
            }
        }

        function deleteAccount(id) {
            accounts = accounts.filter(a => a.id !== id);
            saveData();
            renderAccounts();
            renderTags();
            showToast('账号已删除');
        }

        // Export Functions
        function showExportModal() {
            if (!accounts.length) {
                showToast('暂无数据可导出');
                return;
            }
            document.getElementById('exportModal').classList.add('active');
            selectExportType('plain');
        }

        function closeExportModal() {
            document.getElementById('exportModal').classList.remove('active');
        }

        function closeExportModalOnOverlay(e) {
            if (e.target === e.currentTarget) closeExportModal();
        }

        function selectExportType(type) {
            pendingExportType = type;
            document.getElementById('exportPlain').classList.toggle('selected', type === 'plain');
            document.getElementById('exportEncrypted').classList.toggle('selected', type === 'encrypted');
            
            const pwdSection = document.getElementById('exportPasswordSection');
            pwdSection.style.display = type === 'encrypted' ? 'block' : 'none';
            
            if (type === 'encrypted') {
                setTimeout(() => document.getElementById('exportPassword').focus(), 100);
            }
        }

        async function executeExport() {
            const type = pendingExportType;
            
            if (type === 'encrypted') {
                const pwd = document.getElementById('exportPassword').value;
                const pwdConfirm = document.getElementById('exportPasswordConfirm').value;
                
                if (!pwd) {
                    showToast('请输入导出密码');
                    return;
                }
                if (pwd !== pwdConfirm) {
                    showToast('两次输入的密码不一致');
                    return;
                }
                if (pwd.length < 6) {
                    showToast('密码长度至少6位');
                    return;
                }
                
                try {
                    const encrypted = await encryptData(accounts, pwd);
                    downloadFile(JSON.stringify(encrypted, null, 2), `accounts_encrypted_${new Date().toISOString().split('T')[0]}.json`);
                    showToast('加密数据已导出');
                    closeExportModal();
                } catch (e) {
                    showToast('加密失败:' + e.message);
                }
            } else {
                const data = JSON.stringify(accounts, null, 2);
                downloadFile(data, `accounts_${new Date().toISOString().split('T')[0]}.json`);
                showToast('数据已导出');
                closeExportModal();
            }
        }

        function downloadFile(content, filename) {
            const blob = new Blob([content], {type: 'application/json'});
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = filename;
            a.click();
            URL.revokeObjectURL(url);
        }

        // Import Functions
        async function importData(input) {
            const file = input.files[0];
            if (!file) return;
            
            const reader = new FileReader();
            reader.onload = async (e) => {
                try {
                    const content = e.target.result;
                    let data;
                    
                    try {
                        data = JSON.parse(content);
                    } catch (err) {
                        throw new Error('文件格式错误,不是有效的 JSON');
                    }
                    
                    // Check if encrypted
                    if (data.encrypted && data.data) {
                        pendingImportData = data;
                        pendingImportFile = file;
                        document.getElementById('importPasswordModal').classList.add('active');
                        document.getElementById('importPassword').value = '';
                        setTimeout(() => document.getElementById('importPassword').focus(), 100);
                        return;
                    }
                    
                    // Plain import
                    if (!Array.isArray(data)) {
                        throw new Error('数据格式错误,应为账号数组');
                    }
                    
                    processImportData(data);
                } catch (err) {
                    alert('导入失败:' + err.message);
                }
                input.value = '';
            };
            reader.readAsText(file);
        }

        async function executeImportWithPassword() {
            const pwd = document.getElementById('importPassword').value;
            if (!pwd) {
                showToast('请输入解密密码');
                return;
            }
            
            try {
                const decrypted = await decryptData(pendingImportData, pwd);
                if (!Array.isArray(decrypted)) {
                    throw new Error('解密后数据格式错误');
                }
                processImportData(decrypted);
                document.getElementById('importPasswordModal').classList.remove('active');
                pendingImportData = null;
                pendingImportFile = null;
            } catch (err) {
                showToast('解密失败:' + err.message);
            }
        }

        function cancelImport() {
            document.getElementById('importPasswordModal').classList.remove('active');
            pendingImportData = null;
            pendingImportFile = null;
            document.getElementById('importFile').value = '';
        }

        function processImportData(data) {
            // Fix URLs in imported data
            data.forEach(item => {
                if (item.url) {
                    item.url = fixUrl(item.url);
                }
            });
            
            let added = 0;
            let skipped = 0;
            let updated = 0;
            const skippedItems = [];
            
            data.forEach(item => {
                if (!item.id || !item.platform) return;
                
                // Check for exact duplicate
                const existing = accounts.find(a => a.id === item.id);
                if (existing) {
                    // Check if exactly the same
                    if (isAccountEqual(existing, item)) {
                        skipped++;
                        skippedItems.push(item.platform);
                        return;
                    }
                    // Different - update if newer
                    if (new Date(item.updatedAt) > new Date(existing.updatedAt)) {
                        accounts[accounts.indexOf(existing)] = item;
                        updated++;
                    }
                } else {
                    // Check if new item is duplicate of existing (by content)
                    const contentDuplicate = accounts.find(a => isAccountEqual(a, item));
                    if (contentDuplicate) {
                        skipped++;
                        skippedItems.push(item.platform);
                        return;
                    }
                    accounts.push(item);
                    added++;
                }
            });
            
            saveData();
            renderAccounts();
            renderTags();
            
            let msg = `导入完成:新增 ${added} 条`;
            if (updated > 0) msg += `,更新 ${updated} 条`;
            if (skipped > 0) msg += `,跳过 ${skipped} 条重复`;
            showToast(msg);
            
            if (skippedItems.length > 0) {
                setTimeout(() => {
                    alert(`以下账号因信息完全相同已跳过:\n${skippedItems.join('\n')}`);
                }, 300);
            }
        }

        // Clear All with Double Confirm
        function confirmClearAll() {
            if (accounts.length === 0) {
                showToast('暂无数据可清空');
                return;
            }
            document.getElementById('confirmClearModal').classList.add('active');
            document.getElementById('clearConfirmInput').value = '';
            document.getElementById('clearConfirmInput').classList.remove('error');
            document.getElementById('confirmClearBtn').disabled = true;
            document.getElementById('clearConfirmInput').focus();
        }

        function closeConfirmClearModal() {
            document.getElementById('confirmClearModal').classList.remove('active');
        }

        function closeConfirmClearModalOnOverlay(e) {
            if (e.target === e.currentTarget) closeConfirmClearModal();
        }

        // Real-time validation for clear confirm
        document.getElementById('clearConfirmInput')?.addEventListener('input', function(e) {
            const input = e.target;
            const btn = document.getElementById('confirmClearBtn');
            if (input.value === 'DELETE') {
                btn.disabled = false;
                input.classList.remove('error');
            } else {
                btn.disabled = true;
                if (input.value.length >= 6) {
                    input.classList.add('error');
                } else {
                    input.classList.remove('error');
                }
            }
        });

        function executeClearAll() {
            const input = document.getElementById('clearConfirmInput');
            if (input.value !== 'DELETE') {
                input.classList.add('error');
                return;
            }
            
            accounts = [];
            saveData();
            renderAccounts();
            renderTags();
            closeConfirmClearModal();
            showToast('所有数据已清空');
        }

        // Simple Confirm Modal
        function showSimpleConfirm(title, desc, callback) {
            simpleConfirmCallback = callback;
            document.getElementById('simpleConfirmTitle').textContent = title;
            document.getElementById('simpleConfirmDesc').textContent = desc;
            document.getElementById('confirmModal').classList.add('active');
        }

        function closeConfirmModal() {
            document.getElementById('confirmModal').classList.remove('active');
            simpleConfirmCallback = null;
        }

        function closeConfirmModalOnOverlay(e) {
            if (e.target === e.currentTarget) closeConfirmModal();
        }

        function executeSimpleConfirm() {
            if (simpleConfirmCallback) {
                simpleConfirmCallback();
            }
            closeConfirmModal();
        }

        // Render
        function renderAccounts() {
            const search = document.getElementById('searchInput').value.toLowerCase();
            const grid = document.getElementById('accountsGrid');
            const empty = document.getElementById('emptyState');
            
            let filtered = accounts.filter(a => {
                const matchSearch = !search || 
                    a.platform.toLowerCase().includes(search) ||
                    a.username.toLowerCase().includes(search) ||
                    a.tags?.some(t => t.toLowerCase().includes(search)) ||
                    (a.url && a.url.toLowerCase().includes(search)) ||
                    a.note?.toLowerCase().includes(search);
                
                const matchTag = currentFilter === 'all' || a.tags?.includes(currentFilter);
                
                return matchSearch && matchTag;
            });
            
            // Sort accounts by update time
            filtered.sort((a, b) => {
                const timeA = new Date(a.updatedAt).getTime();
                const timeB = new Date(b.updatedAt).getTime();
                if (window.sortOrder === 'oldest') {
                    return timeA - timeB; // Oldest first
                }
                return timeB - timeA; // Newest first (default)
            });

            document.getElementById('totalCount').textContent = filtered.length;
            
            if (accounts.length > 0) {
                const last = accounts.reduce((a, b) => 
                    new Date(a.updatedAt) > new Date(b.updatedAt) ? a : b
                );
                document.getElementById('lastUpdate').textContent = new Date(last.updatedAt).toLocaleDateString('zh-CN');
            } else {
                document.getElementById('lastUpdate').textContent = '-';
            }
            
            if (filtered.length === 0) {
                grid.style.display = 'none';
                empty.style.display = 'block';
                empty.querySelector('.empty-title').textContent = search || currentFilter !== 'all' ? '无匹配结果' : '暂无账号';
                empty.querySelector('.empty-desc').textContent = search || currentFilter !== 'all' ? '尝试其他关键词或筛选条件' : '点击右上角"添加账号"开始使用';
                return;
            }
            
            grid.style.display = 'grid';
            empty.style.display = 'none';
            
            grid.innerHTML = filtered.map(acc => `
                <div class="account-card" onclick="handleCardClick(event, '${acc.id}')">
                    <div class="card-header">
                        <div class="platform-info">
                            <div class="platform-icon">${acc.platform.charAt(0).toUpperCase()}</div>
                            <div class="platform-meta">
                                <div class="platform-name">${escapeHtml(acc.platform)}</div>
                                ${acc.url ? `<a href="${escapeHtml(acc.url)}" target="_blank" class="platform-url" title="访问网站" onclick="event.stopPropagation()">
                                    <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                        <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path>
                                        <polyline points="15 3 21 3 21 9"></polyline>
                                        <line x1="10" y1="14" x2="21" y2="3"></line>
                                    </svg>
                                    ${escapeHtml(acc.url.replace(/^https?:\/\//, '').replace(/\/$/, ''))}
                                </a>` : ''}
                                ${acc.tags?.length ? `<div class="card-tags">
                                    ${acc.tags.map(t => `<span class="card-tag">${escapeHtml(t)}</span>`).join('')}
                                </div>` : ''}
                            </div>
                        </div>
                        <div class="card-actions" onclick="event.stopPropagation()">
                            <button class="card-btn" onclick="openModal('${acc.id}')" title="编辑">
                                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                    <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
                                    <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
                                </svg>
                            </button>
                            <button class="card-btn delete" onclick="confirmDeleteAccount('${acc.id}', '${escapeHtml(acc.platform)}'); event.stopPropagation();" title="删除">
                                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                    <polyline points="3 6 5 6 21 6"></polyline>
                                    <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
                                </svg>
                            </button>
                        </div>
                    </div>
                    
                    <div class="fields">
                        <div class="field">
                            <span class="field-label">用户名</span>
                            <div class="field-value">
                                <span class="field-text">${escapeHtml(acc.username)}</span>
                                <div class="field-actions">
                                    <button class="field-btn" onclick="copyText('${escapeHtml(acc.username)}', this); event.stopPropagation();">复制</button>
                                </div>
                            </div>
                        </div>
                        
                        <div class="field">
                            <span class="field-label">密码</span>
                            <div class="field-value">
                                <span class="field-text password-text" id="pwd-${acc.id}">${escapeHtml(acc.password)}</span>
                                <div class="field-actions">
                                    <button class="field-btn" onclick="togglePwd('${acc.id}'); event.stopPropagation();">显示</button>
                                    <button class="field-btn" onclick="copyText('${escapeHtml(acc.password)}', this); event.stopPropagation();">复制</button>
                                </div>
                            </div>
                        </div>
                        
                        ${acc.note ? `
                        <div class="field">
                            <span class="field-label">备注</span>
                            <div class="note-text">${escapeHtml(acc.note)}</div>
                        </div>
                        ` : ''}
                    </div>

                    <div class="card-footer">
                        <div class="update-time">
                            <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                <circle cx="12" cy="12" r="10"></circle>
                                <polyline points="12 6 12 12 16 14"></polyline>
                            </svg>
                            <span>${formatRelativeTime(acc.updatedAt)}</span>
                        </div>
                    </div>
                </div>
            `).join('');
        }

        function handleCardClick(event, id) {
            // Don't open view if clicking on buttons or links
            if (event.target.closest('.card-actions') || event.target.closest('.field-actions') || event.target.closest('a')) {
                return;
            }
            openViewModal(id);
        }

        function togglePwd(id) {
            const el = document.getElementById(`pwd-${id}`);
            const btn = event.target;
            
            if (el.classList.contains('revealed')) {
                el.classList.remove('revealed');
                btn.textContent = '显示';
            } else {
                el.classList.add('revealed');
                btn.textContent = '隐藏';
            }
        }

        // Utils
        function copyText(text, btn) {
            // Try modern clipboard API first
            if (navigator.clipboard && navigator.clipboard.writeText) {
                navigator.clipboard.writeText(text).then(() => {
                    showCopied(btn);
                }).catch(() => {
                    fallbackCopy(text, btn);
                });
            } else {
                fallbackCopy(text, btn);
            }
        }

        function fallbackCopy(text, btn) {
            const textArea = document.createElement('textarea');
            textArea.value = text;
            textArea.style.position = 'fixed';
            textArea.style.left = '-999999px';
            textArea.style.top = '0';
            document.body.appendChild(textArea);
            textArea.focus();
            textArea.select();
            
            try {
                const successful = document.execCommand('copy');
                if (successful) {
                    showCopied(btn);
                } else {
                    showToast('复制失败');
                }
            } catch (err) {
                showToast('复制失败');
            }
            document.body.removeChild(textArea);
        }

        function showCopied(btn) {
            btn.textContent = '已复制';
            btn.classList.add('copied');
            showToast('已复制到剪贴板');
            setTimeout(() => {
                btn.textContent = '复制';
                btn.classList.remove('copied');
            }, 2000);
        }

        function showToast(msg) {
            const toast = document.getElementById('toast');
            document.getElementById('toastMsg').textContent = msg;
            toast.classList.add('show');
            setTimeout(() => toast.classList.remove('show'), 3000);
        }

        function escapeHtml(text) {
            const div = document.createElement('div');
            div.textContent = text;
            return div.innerHTML;
        }

        // Format relative time in Chinese
        function formatRelativeTime(dateString) {
            const date = new Date(dateString);
            const now = new Date();
            const diffMs = now - date;
            const diffSec = Math.floor(diffMs / 1000);
            const diffMin = Math.floor(diffSec / 60);
            const diffHour = Math.floor(diffMin / 60);
            const diffDay = Math.floor(diffHour / 24);
            const diffMonth = Math.floor(diffDay / 30);
            const diffYear = Math.floor(diffDay / 365);

            if (diffSec < 60) {
                return '刚刚';
            } else if (diffMin < 60) {
                return `${diffMin}分钟前`;
            } else if (diffHour < 24) {
                return `${diffHour}小时前`;
            } else if (diffDay === 1) {
                return '昨天';
            } else if (diffDay < 30) {
                return `${diffDay}天前`;
            } else if (diffMonth < 12) {
                return `${diffMonth}个月前`;
            } else if (diffYear === 1) {
                return '1年前';
            } else {
                return `${diffYear}年前`;
            }
        }

        // Confirm delete with modal
        function confirmDeleteAccount(id, platform) {
            showSimpleConfirm(
                `确认删除 ${platform}?`,
                '此操作不可恢复,账号将被永久删除。',
                () => {
                    deleteAccount(id);
                }
            );
        }

        function saveData() {
            localStorage.setItem('pwd_accounts', JSON.stringify(accounts));
        }

        // Sort order - default newest first
        window.sortOrder = 'newest';

        // Toggle sort order
        function toggleSortOrder() {
            window.sortOrder = window.sortOrder === 'newest' ? 'oldest' : 'newest';
            updateSortIcon();
            renderAccounts();
            showToast(window.sortOrder === 'newest' ? '已切换:最新在前' : '已切换:最早在前');
        }

        // Update sort icon based on current order
        function updateSortIcon() {
            const icon = document.getElementById('sortIcon');
            const btn = document.getElementById('sortBtn');
            const text = document.getElementById('sortText');

            if (window.sortOrder === 'newest') {
                // Newest first icon (arrow up - descending)
                icon.innerHTML = `
                    <polyline points="12 6 12 18" style="stroke-width: 2.5"></polyline>
                    <polyline points="8 10 12 6 16 10" style="stroke-width: 2.5"></polyline>
                `;
                btn.title = "当前:最新在前,点击切换";
                btn.classList.add('active');
                if (text) text.textContent = '最新';
            } else {
                // Oldest first icon (arrow down - ascending)
                icon.innerHTML = `
                    <polyline points="12 6 12 18" style="stroke-width: 2.5"></polyline>
                    <polyline points="8 14 12 18 16 14" style="stroke-width: 2.5"></polyline>
                `;
                btn.title = "当前:最早在前,点击切换";
                btn.classList.remove('active');
                if (text) text.textContent = '最早';
            }
        }

        // Init
        document.addEventListener('DOMContentLoaded', () => {
            initTheme();
            updateSortIcon();
            renderAccounts();
            renderTags();
        });

        // Handle resize
        let lastWidth = window.innerWidth;
        window.addEventListener('resize', () => {
            const currentWidth = window.innerWidth;
            const wasMobile = lastWidth <= 768;
            const isMobileNow = currentWidth <= 768;
            if (wasMobile !== isMobileNow) {
                renderAccounts();
            }
            lastWidth = currentWidth;
        });

        // Keyboard
        document.addEventListener('keydown', (e) => {
            if (e.key === 'Escape') {
                closeModal();
                closeViewModal();
                closeConfirmModal();
                closeConfirmClearModal();
                closeExportModal();
                cancelImport();
                cancelDuplicate();
            }
        });
    </script>
</body>
</html>

这是加了版权信息的完整代码:

<!-- 原创代码专属留痕 请勿移除 -->
<!--
============================================
作者:Yangxiao
作者域名:aszv.top
创作日期:2026-05-08
本代码为本人原创,盗用必究
============================================
-->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta name="author" content="1425202077">
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9, user-scalable=no">
    <title>账号管理器</title>
    <style>
        :root {
            --bg-primary: #ffffff;
            --bg-secondary: #f5f5f5;
            --bg-tertiary: #fafafa;
            --text-primary: #111111;
            --text-secondary: #666666;
            --text-tertiary: #999999;
            --border: #e5e5e5;
            --accent: #0066ff;
            --accent-hover: #0052cc;
            --danger: #ff3b30;
            --success: #34c759;
            --warning: #ff9500;
            --shadow: rgba(0, 0, 0, 0.08);
            --card-bg: #ffffff;
            --input-bg: #ffffff;
            --modal-overlay: rgba(0, 0, 0, 0.5);
        }

        [data-theme="dark"] {
            --bg-primary: #000000;
            --bg-secondary: #1c1c1e;
            --bg-tertiary: #2c2c2e;
            --text-primary: #ffffff;
            --text-secondary: #8e8e93;
            --text-tertiary: #636366;
            --border: #38383a;
            --accent: #0a84ff;
            --accent-hover: #409cff;
            --danger: #ff453a;
            --success: #30d158;
            --warning: #ff9f0a;
            --shadow: rgba(0, 0, 0, 0.3);
            --card-bg: #1c1c1e;
            --input-bg: #2c2c2e;
            --modal-overlay: rgba(0, 0, 0, 0.8);
        }

        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            -webkit-tap-highlight-color: transparent;
            -webkit-touch-callout: none;
            -webkit-user-select: none;
            user-select: none;
        }

        /* Allow text selection in specific elements */
        .field-text, .view-value-text, .note-text, .form-input, textarea, input {
            -webkit-user-select: text;
            user-select: text;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Segoe UI', Roboto, sans-serif;
            background: var(--bg-primary);
            color: var(--text-primary);
            line-height: 1.5;
            -webkit-font-smoothing: antialiased;
            transition: background 0.3s, color 0.3s;
        }

        /* Sort Icon Button - Stats Bar Style */
        .sort-icon-btn {
            display: inline-flex;
            align-items: center;
            gap: 6px;
            padding: 6px 12px;
            border: 1px solid var(--border);
            background: var(--bg-secondary);
            color: var(--text-secondary);
            border-radius: 20px;
            cursor: pointer;
            font-size: 13px;
            font-weight: 500;
            transition: all 0.2s;
            margin-left: auto;
        }

        .sort-icon-btn:hover {
            border-color: var(--accent);
            color: var(--accent);
            background: var(--bg-tertiary);
        }

        .sort-icon-btn.active {
            background: var(--accent);
            color: white;
            border-color: var(--accent);
        }

        .sort-icon-btn.active:hover {
            background: var(--accent-hover);
            border-color: var(--accent-hover);
        }

        .sort-text {
            font-size: 12px;
            font-weight: 600;
        }

        /* Header */

        .header {
            position: sticky;
            top: 0;
            background: var(--bg-primary);
            border-bottom: 1px solid var(--border);
            z-index: 100;
            transition: all 0.3s;
        }

        .header-content {
            max-width: 1200px;
            margin: 0 auto;
            padding: 16px 24px;
            display: flex;
            align-items: center;
            justify-content: space-between;
            gap: 20px;
        }

        .logo {
            display: flex;
            align-items: center;
            gap: 12px;
            font-size: 20px;
            font-weight: 600;
            letter-spacing: -0.5px;
        }

        .logo-icon {
            width: 32px;
            height: 32px;
            background: var(--accent);
            border-radius: 8px;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
        }

        .header-actions {
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .icon-btn {
            width: 36px;
            height: 36px;
            border: none;
            background: transparent;
            color: var(--text-secondary);
            cursor: pointer;
            border-radius: 8px;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.2s;
            outline: none;
        }

        .icon-btn:hover {
            background: var(--bg-secondary);
            color: var(--text-primary);
        }

        .icon-btn.danger:hover {
            background: var(--danger);
            color: white;
        }

        /* Search Bar */
        .search-section {
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px 24px;
            display: flex;
            gap: 12px;
            flex-wrap: wrap;
        }

        .search-box {
            flex: 1;
            min-width: 280px;
            position: relative;
        }

        .search-input {
            width: 100%;
            padding: 12px 16px 12px 40px;
            border: 1px solid var(--border);
            border-radius: 12px;
            background: var(--input-bg);
            color: var(--text-primary);
            font-size: 15px;
            transition: all 0.2s;
            outline: none;
        }

        .search-input:focus {
            outline: none;
            border-color: var(--accent);
        }

        .search-icon {
            position: absolute;
            left: 12px;
            top: 50%;
            transform: translateY(-50%);
            color: var(--text-tertiary);
            pointer-events: none;
        }

        .btn {
            padding: 12px 20px;
            border: none;
            border-radius: 12px;
            font-size: 15px;
            font-weight: 500;
            cursor: pointer;
            display: inline-flex;
            align-items: center;
            gap: 8px;
            transition: all 0.2s;
            white-space: nowrap;
            outline: none;
        }

        .btn-primary {
            background: var(--accent);
            color: white;
        }

        .btn-primary:hover {
            background: var(--accent-hover);
            transform: translateY(-1px);
        }

        .btn-secondary {
            background: var(--bg-secondary);
            color: var(--text-primary);
            border: 1px solid var(--border);
        }

        .btn-secondary:hover {
            background: var(--bg-tertiary);
        }

        .btn-danger {
            background: var(--danger);
            color: white;
        }

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

        /* Tags Filter */
        .tags-section {
            max-width: 1200px;
            margin: 0 auto;
            padding: 0 24px 20px;
            display: flex;
            gap: 8px;
            flex-wrap: wrap;
            align-items: center;
            position: relative;
        }

        .tag-label {
            font-size: 13px;
            color: var(--text-tertiary);
            font-weight: 500;
        }

        .tag {
            padding: 6px 14px;
            border-radius: 20px;
            font-size: 13px;
            font-weight: 500;
            cursor: pointer;
            transition: all 0.2s;
            border: 1px solid var(--border);
            background: var(--bg-primary);
            color: var(--text-secondary);
        }

        .tag:hover {
            border-color: var(--accent);
            color: var(--accent);
        }

        .tag.active {
            background: var(--accent);
            color: white;
            border-color: var(--accent);
        }

        /* Main Content */
        .container {
            max-width: 1200px;
            margin: 0 auto;
            padding: 0 24px 40px;
        }

        .stats-bar {
            display: flex;
            align-items: center;
            gap: 24px;
            margin-bottom: 24px;
            font-size: 13px;
            color: var(--text-tertiary);
        }

        .stat-item {
            display: flex;
            align-items: center;
            gap: 6px;
            flex-shrink: 0;
        }

        .stat-value {
            color: var(--text-primary);
            font-weight: 600;
        }

        /* Grid Layout */
        .accounts-grid {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
            gap: 16px;
        }

        /* Card Design */
        .account-card {
            background: var(--card-bg);
            border: 1px solid var(--border);
            border-radius: 16px;
            padding: 20px;
            transition: all 0.2s;
            position: relative;
            cursor: pointer;
            min-width: 0;
            outline: none;
        }

        .account-card:hover {
            border-color: var(--accent);
            box-shadow: 0 4px 20px var(--shadow);
            transform: translateY(-2px);
        }

        .card-header {
            display: flex;
            justify-content: space-between;
            align-items: start;
            margin-bottom: 16px;
            min-width: 0;
        }

        .platform-info {
            display: flex;
            align-items: center;
            gap: 12px;
            min-width: 0;
            flex: 1;
        }

        .platform-icon {
            width: 44px;
            height: 44px;
            background: var(--bg-secondary);
            border-radius: 12px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 20px;
            font-weight: 600;
            color: var(--accent);
            flex-shrink: 0;
        }

        .platform-meta {
            min-width: 0;
            flex: 1;
            overflow: hidden;
        }

        .platform-name {
            font-size: 17px;
            font-weight: 600;
            color: var(--text-primary);
            margin-bottom: 4px;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }

        .platform-url {
            font-size: 13px;
            color: var(--accent);
            text-decoration: none;
            display: flex;
            align-items: center;
            gap: 4px;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }

        .platform-url:hover {
            text-decoration: underline;
        }

        /* Tags container with flex wrap */
        .card-tags {
            display: flex;
            flex-wrap: wrap;
            gap: 6px;
            margin-top: 6px;
        }

        .card-tag {
            display: inline-flex;
            align-items: center;
            padding: 4px 10px;
            background: var(--bg-tertiary);
            border: 1px solid var(--border);
            border-radius: 12px;
            font-size: 11px;
            font-weight: 500;
            color: var(--text-secondary);
            transition: all 0.2s;
        }

        .card-tag:hover {
            background: var(--accent);
            color: white;
            border-color: var(--accent);
        }

        .card-actions {
            display: flex;
            gap: 4px;
            opacity: 0;
            transition: opacity 0.2s;
            flex-shrink: 0;
        }

        .account-card:hover .card-actions {
            opacity: 1;
        }

        .card-btn {
            width: 32px;
            height: 32px;
            border: none;
            background: transparent;
            color: var(--text-tertiary);
            cursor: pointer;
            border-radius: 6px;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.2s;
            outline: none;
        }

        .card-btn:hover {
            background: var(--bg-secondary);
            color: var(--text-primary);
        }

        .card-btn.delete:hover {
            color: var(--danger);
        }

        /* Fields */
        .fields {
            display: flex;
            flex-direction: column;
            gap: 12px;
            min-width: 0;
        }

        .field {
            display: flex;
            flex-direction: column;
            gap: 4px;
            min-width: 0;
        }

        .field-label {
            font-size: 12px;
            color: var(--text-tertiary);
            font-weight: 500;
            text-transform: uppercase;
            letter-spacing: 0.3px;
        }

        .field-value {
            display: flex;
            align-items: center;
            gap: 8px;
            background: var(--bg-secondary);
            padding: 10px 12px;
            border-radius: 10px;
            font-size: 14px;
            font-family: 'SF Mono', Monaco, monospace;
            min-width: 0;
        }

        .field-text {
            flex: 1;
            min-width: 0;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
            color: var(--text-primary);
        }

        .password-text {
            filter: blur(4px);
            transition: filter 0.2s;
            user-select: none;
        }

        .password-text.revealed {
            filter: none;
            user-select: text;
        }

        .field-actions {
            display: flex;
            gap: 4px;
            flex-shrink: 0;
        }

        .field-btn {
            padding: 4px 8px;
            border: none;
            background: transparent;
            color: var(--text-tertiary);
            cursor: pointer;
            border-radius: 4px;
            font-size: 12px;
            font-weight: 500;
            transition: all 0.2s;
            outline: none;
        }

        .field-btn:hover {
            background: var(--bg-primary);
            color: var(--accent);
        }

        .field-btn.copied {
            color: var(--success);
        }

        .note-text {
            font-size: 13px;
            color: var(--text-secondary);
            line-height: 1.5;
            padding: 8px 12px;
            background: var(--bg-secondary);
            border-radius: 8px;
            margin-top: 4px;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
            max-width: 100%;
        }

        /* Empty State */
        .empty-state {
            text-align: center;
            padding: 80px 20px;
            color: var(--text-tertiary);
        }

        .empty-icon {
            width: 80px;
            height: 80px;
            margin: 0 auto 20px;
            background: var(--bg-secondary);
            border-radius: 20px;
            display: flex;
            align-items: center;
            justify-content: center;
            color: var(--text-tertiary);
        }

        .empty-title {
            font-size: 18px;
            font-weight: 600;
            color: var(--text-primary);
            margin-bottom: 8px;
        }

        .empty-desc {
            font-size: 14px;
            max-width: 400px;
            margin: 0 auto;
            line-height: 1.6;
        }

        /* Modal */
        .modal-overlay {
            position: fixed;
            inset: 0;
            background: var(--modal-overlay);
            backdrop-filter: blur(8px);
            display: none;
            justify-content: center;
            align-items: center;
            z-index: 1000;
            padding: 20px;
        }

        .modal-overlay.active {
            display: flex;
        }

        .modal {
            background: var(--bg-primary);
            border-radius: 20px;
            width: 100%;
            max-width: 480px;
            max-height: 90vh;
            overflow: hidden;
            box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
            animation: modalIn 0.3s ease;
        }

        @keyframes modalIn {
            from {
                opacity: 0;
                transform: scale(0.95);
            }
            to {
                opacity: 1;
                transform: scale(1);
            }
        }

        .modal-header {
            padding: 24px 24px 0;
        }

        .modal-title {
            font-size: 20px;
            font-weight: 600;
            margin-bottom: 4px;
        }

        .modal-subtitle {
            font-size: 14px;
            color: var(--text-secondary);
        }

        .modal-body {
            padding: 20px 24px;
            overflow-y: auto;
            max-height: calc(90vh - 140px);
        }

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

        .form-label {
            display: block;
            font-size: 13px;
            font-weight: 500;
            color: var(--text-secondary);
            margin-bottom: 6px;
        }

        .form-input {
            width: 100%;
            padding: 12px 14px;
            border: 1px solid var(--border);
            border-radius: 10px;
            background: var(--input-bg);
            color: var(--text-primary);
            font-size: 15px;
            transition: all 0.2s;
            outline: none;
        }

        .form-input:focus {
            outline: none;
            border-color: var(--accent);
        }

        .form-input::placeholder {
            color: var(--text-tertiary);
        }

        textarea.form-input {
            min-height: 80px;
            resize: vertical;
            font-family: inherit;
            line-height: 1.5;
        }

        .input-hint {
            font-size: 12px;
            color: var(--text-tertiary);
            margin-top: 4px;
        }

        /* Password field with generator */
        .password-field-wrapper {
            position: relative;
            display: flex;
            gap: 8px;
            align-items: center;
        }

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

        .password-actions {
            display: flex;
            gap: 4px;
            align-items: center;
        }

        .icon-action-btn {
            width: 36px;
            height: 36px;
            border: none;
            background: var(--bg-secondary);
            color: var(--text-secondary);
            cursor: pointer;
            border-radius: 8px;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.2s;
            outline: none;
            flex-shrink: 0;
        }

        .icon-action-btn:hover {
            background: var(--accent);
            color: white;
        }

        .icon-action-btn svg {
            width: 18px;
            height: 18px;
        }

        /* Tags input with pipe separator */
        .tags-simple-input {
            width: 100%;
            padding: 12px 14px;
            border: 1px solid var(--border);
            border-radius: 10px;
            background: var(--input-bg);
            color: var(--text-primary);
            font-size: 15px;
            transition: all 0.2s;
            outline: none;
        }

        .tags-simple-input:focus {
            outline: none;
            border-color: var(--accent);
        }

        .modal-footer {
            padding: 16px 24px 24px;
            display: flex;
            justify-content: flex-end;
            gap: 12px;
            border-top: 1px solid var(--border);
        }

        .btn-text {
            padding: 10px 20px;
            border: 1px solid var(--border);
            background: var(--bg-secondary);
            color: var(--text-secondary);
            cursor: pointer;
            font-size: 15px;
            font-weight: 500;
            border-radius: 10px;
            transition: all 0.2s;
            outline: none;
            min-width: 80px;
            text-align: center;
        }

        .btn-text:hover {
            background: var(--bg-tertiary);
            color: var(--text-primary);
            border-color: var(--text-tertiary);
        }

        .btn-text.danger {
            color: var(--danger);
            border-color: var(--danger);
            background: rgba(255, 59, 48, 0.05);
        }

        .btn-text.danger:hover {
            background: var(--danger);
            color: white;
            border-color: var(--danger);
        }

        /* View Modal Styles */
        .view-modal .modal-body {
            padding: 24px;
        }

        .view-field {
            margin-bottom: 20px;
        }

        .view-field:last-child {
            margin-bottom: 0;
        }

        .view-label {
            font-size: 12px;
            color: var(--text-tertiary);
            font-weight: 500;
            text-transform: uppercase;
            letter-spacing: 0.3px;
            margin-bottom: 8px;
        }

        .view-value-box {
            background: var(--bg-secondary);
            padding: 14px 16px;
            border-radius: 12px;
            font-family: 'SF Mono', Monaco, monospace;
            font-size: 15px;
            color: var(--text-primary);
            word-break: break-all;
            display: flex;
            align-items: center;
            justify-content: space-between;
            gap: 12px;
        }

        .view-value-text {
            flex: 1;
            overflow-wrap: break-word;
        }

        .view-actions {
            display: flex;
            gap: 8px;
            flex-shrink: 0;
        }

        .view-btn {
            padding: 6px 12px;
            border: none;
            background: var(--bg-primary);
            color: var(--text-secondary);
            cursor: pointer;
            border-radius: 6px;
            font-size: 13px;
            font-weight: 500;
            transition: all 0.2s;
            outline: none;
        }

        .view-btn:hover {
            background: var(--accent);
            color: white;
        }

        .view-btn.copied {
            background: var(--success);
            color: white;
        }

        .view-tags {
            display: flex;
            flex-wrap: wrap;
            gap: 8px;
        }

        .view-tag {
            padding: 6px 14px;
            background: var(--bg-secondary);
            color: var(--text-secondary);
            border-radius: 20px;
            font-size: 13px;
            font-weight: 500;
        }

        /* View note with resize like edit mode */
        .view-note-resizable {
            background: var(--bg-secondary);
            padding: 14px 16px;
            border-radius: 12px;
            font-size: 14px;
            color: var(--text-secondary);
            line-height: 1.6;
            white-space: pre-wrap;
            width: 100%;
            min-height: 80px;
            resize: vertical;
            border: 1px solid transparent;
            font-family: inherit;
            overflow: auto;
        }

        .view-note-resizable:focus {
            outline: none;
            border-color: var(--accent);
        }

        .view-link {
            display: inline-flex;
            align-items: center;
            gap: 6px;
            color: var(--accent);
            text-decoration: none;
            font-size: 14px;
            margin-top: 8px;
        }

        .view-link:hover {
            text-decoration: underline;
        }

        /* Toast */
        .toast {
            position: fixed;
            bottom: 24px;
            right: 24px;
            background: var(--text-primary);
            color: var(--bg-primary);
            padding: 14px 20px;
            border-radius: 12px;
            font-size: 14px;
            font-weight: 500;
            display: none;
            align-items: center;
            gap: 10px;
            box-shadow: 0 10px 30px var(--shadow);
            z-index: 2000;
            animation: toastIn 0.3s ease;
        }

        .toast.show {
            display: flex;
        }

        @keyframes toastIn {
            from {
                transform: translateY(20px);
                opacity: 0;
            }
            to {
                transform: translateY(0);
                opacity: 1;
            }
        }

        /* Confirm Modal */
        .confirm-modal .modal-body {
            padding: 24px;
            text-align: center;
        }

        .confirm-icon {
            width: 64px;
            height: 64px;
            margin: 0 auto 16px;
            background: var(--danger);
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
        }

        .confirm-title {
            font-size: 18px;
            font-weight: 600;
            margin-bottom: 8px;
        }

        .confirm-desc {
            font-size: 14px;
            color: var(--text-secondary);
            line-height: 1.5;
        }

        /* Double confirm input */
        .confirm-input-group {
            margin-top: 20px;
            text-align: left;
        }

        .confirm-input-label {
            font-size: 13px;
            color: var(--text-secondary);
            margin-bottom: 8px;
            display: block;
        }

        .confirm-input {
            width: 100%;
            padding: 12px 14px;
            border: 1px solid var(--border);
            border-radius: 10px;
            background: var(--input-bg);
            color: var(--text-primary);
            font-size: 15px;
            text-align: center;
            font-weight: 500;
        }

        .confirm-input:focus {
            outline: none;
            border-color: var(--danger);
        }

        .confirm-input.error {
            border-color: var(--danger);
            background: rgba(255, 59, 48, 0.1);
        }

        /* View Modal Tags */
        .view-tags-container {
            display: flex;
            flex-wrap: wrap;
            gap: 8px;
        }

        .view-tag-item {
            padding: 6px 14px;
            background: var(--bg-secondary);
            border: 1px solid var(--border);
            border-radius: 20px;
            font-size: 13px;
            font-weight: 500;
            color: var(--text-secondary);
        }

        /* Password Generator Settings */

        .generator-settings {
            background: var(--bg-secondary);
            border-radius: 12px;
            padding: 16px;
            margin-top: 12px;
            border: 1px solid var(--border);
        }

        .generator-settings-title {
            font-size: 14px;
            font-weight: 600;
            color: var(--text-primary);
            margin-bottom: 12px;
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .setting-row {
            display: flex;
            align-items: center;
            justify-content: space-between;
            margin-bottom: 12px;
        }

        .setting-row:last-child {
            margin-bottom: 0;
        }

        .setting-label {
            font-size: 14px;
            color: var(--text-secondary);
        }

        .setting-control {
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .length-input {
            width: 60px;
            padding: 6px 10px;
            border: 1px solid var(--border);
            border-radius: 6px;
            background: var(--input-bg);
            color: var(--text-primary);
            font-size: 14px;
            text-align: center;
        }

        .checkbox-wrapper {
            display: flex;
            align-items: center;
            gap: 6px;
            cursor: pointer;
        }

        .checkbox-wrapper input[type="checkbox"] {
            width: 18px;
            height: 18px;
            cursor: pointer;
        }

        .checkbox-wrapper span {
            font-size: 14px;
            color: var(--text-secondary);
        }

        /* Export/Import Options */
        .export-options {
            display: flex;
            gap: 12px;
            margin-bottom: 20px;
        }

        .export-option {
            flex: 1;
            padding: 16px;
            border: 2px solid var(--border);
            border-radius: 12px;
            cursor: pointer;
            text-align: center;
            transition: all 0.2s;
        }

        .export-option:hover {
            border-color: var(--accent);
        }

        .export-option.selected {
            border-color: var(--accent);
            background: rgba(0, 102, 255, 0.05);
        }

        .export-option-icon {
            width: 48px;
            height: 48px;
            margin: 0 auto 8px;
            background: var(--bg-secondary);
            border-radius: 12px;
            display: flex;
            align-items: center;
            justify-content: center;
            color: var(--accent);
        }

        .export-option-title {
            font-size: 15px;
            font-weight: 600;
            color: var(--text-primary);
            margin-bottom: 4px;
        }

        .export-option-desc {
            font-size: 12px;
            color: var(--text-secondary);
        }

        /* Duplicate confirm modal */
        .duplicate-info {
            background: var(--bg-secondary);
            border-radius: 12px;
            padding: 16px;
            margin: 16px 0;
            text-align: left;
        }

        .duplicate-info-item {
            display: flex;
            gap: 8px;
            margin-bottom: 8px;
            font-size: 14px;
        }

        .duplicate-info-item:last-child {
            margin-bottom: 0;
        }

        .duplicate-info-label {
            color: var(--text-tertiary);
            min-width: 60px;
        }

        .duplicate-info-value {
            color: var(--text-primary);
            font-weight: 500;
        }

        /* Desktop Styles (default) */
        @media (min-width: 769px) {
            .mobile-only {
                display: none !important;
            }
        }

        /* Mobile Styles - 90% zoom */
        @media (max-width: 768px) {
            html {
                zoom: 0.9;
                -moz-transform: scale(0.9);
                -moz-transform-origin: 0 0;
            }

            @supports not (zoom: 0.9) {
                body {
                    transform: scale(0.9);
                    transform-origin: 0 0;
                    width: 111.11%;
                    height: 111.11%;
                }
            }

            .desktop-only {
                display: none !important;
            }

            .header-content {
                padding: 12px 16px;
            }

            .search-section {
                padding: 16px;
            }

            .search-section .btn-primary {
                display: none;
            }

            .sort-icon-btn {
                padding: 4px 10px;
                margin-left: auto;
            }

            .sort-icon-btn svg {
                width: 12px;
                height: 12px;
            }

            .sort-text {
                font-size: 11px;
            }

            .tags-section {
                padding: 0 16px 16px;
                position: relative;
                padding-right: 60px;
            }

            .mobile-add-btn {
                position: absolute;
                right: 16px;
                top: 0;
                width: 36px;
                height: 36px;
                border: none;
                background: var(--accent);
                color: white;
                border-radius: 10px;
                display: flex;
                align-items: center;
                justify-content: center;
                cursor: pointer;
                transition: all 0.2s;
                outline: none;
            }

            .mobile-add-btn:hover {
                background: var(--accent-hover);
                transform: scale(1.05);
            }

            .container {
                padding: 0 16px 32px;
            }

            .accounts-grid {
                grid-template-columns: 1fr;
            }

            .card-actions {
                opacity: 1;
            }

            /* Mobile copy fix */
            .field-btn {
                padding: 8px 12px;
                font-size: 13px;
            }

            /* Ensure card doesn't overflow */
            .account-card {
                min-width: 0;
                max-width: 100%;
            }

            .fields {
                min-width: 0;
            }

            .field-value {
                min-width: 0;
            }

            .note-text {
                max-width: 100%;
            }

            /* Toast position fix for zoomed viewport */
            .toast {
                right: 20px;
                bottom: 20px;
            }

            /* Generator settings on mobile */
            .generator-settings {
                padding: 12px;
            }

            .setting-row {
                flex-direction: column;
                align-items: flex-start;
                gap: 8px;
            }
        }

        /* Card Footer with Update Time */
        .card-footer {
            margin-top: 16px;
            padding-top: 12px;
            border-top: 1px solid var(--border);
            display: flex;
            justify-content: flex-end;
        }

        .update-time {
            display: flex;
            align-items: center;
            gap: 6px;
            font-size: 12px;
            color: var(--text-tertiary);
        }

        .update-time svg {
            opacity: 0.6;
        }

        /* Scrollbar */

        ::-webkit-scrollbar {
            width: 8px;
            height: 8px;
        }

        ::-webkit-scrollbar-track {
            background: transparent;
        }

        ::-webkit-scrollbar-thumb {
            background: var(--border);
            border-radius: 4px;
        }

        ::-webkit-scrollbar-thumb:hover {
            background: var(--text-tertiary);
        }
    </style>
<base target="_blank">
</head>
<body>
    <header class="header">
        <div class="header-content">
            <div class="logo">
                <div class="logo-icon">
                    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
                        <rect x="3" y="11" width="18" height="11" rx="2"></rect>
                        <path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
                    </svg>
                </div>
                <span>账号管理器</span>
            </div>
            <div class="header-actions">
                <button class="icon-btn" onclick="toggleTheme()" title="切换主题">
                    <svg id="themeIcon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                        <circle cx="12" cy="12" r="5"></circle>
                        <line x1="12" y1="1" x2="12" y2="3"></line>
                        <line x1="12" y1="21" x2="12" y2="23"></line>
                        <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
                        <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
                        <line x1="1" y1="12" x2="3" y2="12"></line>
                        <line x1="21" y1="12" x2="23" y2="12"></line>
                        <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
                        <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
                    </svg>
                </button>
                <button class="icon-btn" onclick="showExportModal()" title="导出数据">
                    <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                        <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
                        <polyline points="7 10 12 15 17 10"></polyline>
                        <line x1="12" y1="15" x2="12" y2="3"></line>
                    </svg>
                </button>
                <button class="icon-btn" onclick="document.getElementById('importFile').click()" title="导入数据">
                    <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                        <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
                        <polyline points="17 8 12 3 7 8"></polyline>
                        <line x1="12" y1="3" x2="12" y2="15"></line>
                    </svg>
                </button>
                <button class="icon-btn danger" onclick="confirmClearAll()" title="清空所有数据">
                    <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                        <polyline points="3 6 5 6 21 6"></polyline>
                        <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
                    </svg>
                </button>
                <input type="file" id="importFile" style="display: none" accept=".json" onchange="importData(this)">
            </div>
        </div>
    </header>

    <section class="search-section">
        <div class="search-box">
            <svg class="search-icon" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                <circle cx="11" cy="11" r="8"></circle>
                <path d="m21 21-4.35-4.35"></path>
            </svg>
            <input type="text" class="search-input" id="searchInput" placeholder="搜索平台、账号、标签、链接或备注..." oninput="renderAccounts()">
        </div>
        <button class="btn btn-primary desktop-only" onclick="openModal()">
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
                <line x1="12" y1="5" x2="12" y2="19"></line>
                <line x1="5" y1="12" x2="19" y2="12"></line>
            </svg>
            添加账号
        </button>
    </section>

    <section class="tags-section" id="tagsContainer">
        <span class="tag-label">筛选:</span>
        <span class="tag active" onclick="filterByTag('all')">全部</span>
    </section>

    <main class="container">
        <div class="stats-bar">
            <div class="stat-item">
                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                    <rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect>
                    <line x1="16" y1="2" x2="16" y2="6"></line>
                    <line x1="8" y1="2" x2="8" y2="6"></line>
                    <line x1="3" y1="10" x2="21" y2="10"></line>
                </svg>
                <span>共 <span class="stat-value" id="totalCount">0</span> 个账号</span>
            </div>
            <div class="stat-item">
                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                    <circle cx="12" cy="12" r="10"></circle>
                    <polyline points="12 6 12 12 16 14"></polyline>
                </svg>
                <span>更新于 <span class="stat-value" id="lastUpdate">-</span></span>
            </div>
            <button class="sort-icon-btn" id="sortBtn" onclick="toggleSortOrder()" title="切换排序">
                <svg id="sortIcon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
                    <polyline points="12 6 12 18"></polyline>
                    <polyline points="8 10 12 6 16 10"></polyline>
                </svg>
                <span class="sort-text" id="sortText">最新</span>
            </button>
        </div>

        <div id="contentArea">
            <div class="empty-state" id="emptyState">
                <div class="empty-icon">
                    <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
                        <rect x="3" y="11" width="18" height="11" rx="2"></rect>
                        <path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
                    </svg>
                </div>
                <div class="empty-title">暂无账号</div>
                <div class="empty-desc">点击右上角"添加账号"开始使用,或导入已有数据</div>
            </div>
            <div class="accounts-grid" id="accountsGrid" style="display: none;"></div>
        </div>
    </main>

    <!-- Add/Edit Modal - No overlay click close -->
    <div class="modal-overlay" id="modal">
        <div class="modal">
            <div class="modal-header">
                <h2 class="modal-title" id="modalTitle">添加账号</h2>
                <p class="modal-subtitle">数据仅保存在本地浏览器中</p>
            </div>
            <form class="modal-body" id="accountForm" onsubmit="saveAccount(event)">
                <input type="hidden" id="editId">
                
                <div class="form-group">
                    <label class="form-label">平台名称 *</label>
                    <input type="text" class="form-input" id="platform" placeholder="例如:微信、GitHub、支付宝" required>
                </div>

                <div class="form-group">
                    <label class="form-label">用户名 *</label>
                    <input type="text" class="form-input" id="username" placeholder="邮箱/手机号/用户名" required>
                </div>

                <div class="form-group">
                    <label class="form-label">密码 *</label>
                    <div class="password-field-wrapper">
                        <div class="password-input-wrapper">
                            <input type="password" class="form-input" id="password" placeholder="密码" required style="padding-right: 40px;">
                        </div>
                        <div class="password-actions">
                            <!-- Generate password button -->
                            <button type="button" class="icon-action-btn" onclick="generatePassword()" title="生成随机密码">
                                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                    <rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
                                    <path d="M7 11V7a5 5 0 0 1 9.9-1"></path>
                                    <circle cx="12" cy="16" r="1" fill="currentColor"></circle>
                                    <line x1="8" y1="16" x2="8" y2="16"></line>
                                    <line x1="16" y1="16" x2="16" y2="16"></line>
                                </svg>
                            </button>
                            <!-- Settings button -->
                            <button type="button" class="icon-action-btn" onclick="toggleGeneratorSettings()" title="密码生成器设置">
                                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                    <circle cx="12" cy="12" r="3"></circle>
                                    <path d="M12 1v6m0 6v6m4.22-10.22l4.24-4.24M6.34 6.34L2.1 2.1m17.8 17.8l-4.24-4.24M6.34 17.66l-4.24 4.24M23 12h-6m-6 0H1m20.07-4.93l-4.24 4.24M6.34 6.34l-4.24-4.24"></path>
                                </svg>
                            </button>
                            <!-- Toggle visibility -->
                            <button type="button" class="icon-action-btn" onclick="togglePasswordInput()" title="显示/隐藏密码">
                                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                    <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
                                    <circle cx="12" cy="12" r="3"></circle>
                                </svg>
                            </button>
                        </div>
                    </div>
                    <!-- Generator Settings Panel -->
                    <div class="generator-settings" id="generatorSettings" style="display: none;">
                        <div class="generator-settings-title">
                            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                <circle cx="12" cy="12" r="3"></circle>
                                <path d="M12 1v6m0 6v6m4.22-10.22l4.24-4.24M6.34 6.34L2.1 2.1m17.8 17.8l-4.24-4.24M6.34 17.66l-4.24 4.24M23 12h-6m-6 0H1m20.07-4.93l-4.24 4.24M6.34 6.34l-4.24-4.24"></path>
                            </svg>
                            密码生成器设置
                        </div>
                        <div class="setting-row">
                            <span class="setting-label">密码长度</span>
                            <div class="setting-control">
                                <input type="number" class="length-input" id="pwdLength" value="16" min="4" max="64">
                            </div>
                        </div>
                        <div class="setting-row">
                            <span class="setting-label">包含大写字母 (A-Z)</span>
                            <label class="checkbox-wrapper">
                                <input type="checkbox" id="pwdUppercase" checked>
                                <span>启用</span>
                            </label>
                        </div>
                        <div class="setting-row">
                            <span class="setting-label">包含小写字母 (a-z)</span>
                            <label class="checkbox-wrapper">
                                <input type="checkbox" id="pwdLowercase" checked>
                                <span>启用</span>
                            </label>
                        </div>
                        <div class="setting-row">
                            <span class="setting-label">包含数字 (0-9)</span>
                            <label class="checkbox-wrapper">
                                <input type="checkbox" id="pwdNumbers" checked>
                                <span>启用</span>
                            </label>
                        </div>
                        <div class="setting-row">
                            <span class="setting-label">包含符号 (!@#$...)</span>
                            <label class="checkbox-wrapper">
                                <input type="checkbox" id="pwdSymbols">
                                <span>启用</span>
                            </label>
                        </div>
                    </div>
                </div>

                <div class="form-group">
                    <label class="form-label">网站链接</label>
                    <input type="text" class="form-input" id="url" placeholder="https://...">
                    <div class="input-hint">可选,方便快速访问网站,自动补全 http://</div>
                </div>

                <div class="form-group">
                    <label class="form-label">标签</label>
                    <input type="text" class="tags-simple-input" id="tagsInput" placeholder="工作|学习|生活">
                    <div class="input-hint">使用竖杆 | 分隔多个标签</div>
                </div>

                <div class="form-group">
                    <label class="form-label">备注</label>
                    <textarea class="form-input" id="note" placeholder="记录其他信息,如安全问题、绑定手机、备用邮箱等..."></textarea>
                </div>
            </form>
            <div class="modal-footer">
                <button type="button" class="btn-text danger" onclick="confirmDeleteCurrent()" id="deleteEditBtn" style="display: none; margin-right: auto;">删除</button>
                <button type="button" class="btn-text" onclick="closeModal()">取消</button>
                <button type="button" class="btn btn-primary" onclick="document.getElementById('accountForm').dispatchEvent(new Event('submit'))">保存</button>
            </div>
        </div>
    </div>

    <!-- View Modal -->
    <div class="modal-overlay" id="viewModal" onclick="closeViewModalOnOverlay(event)">
        <div class="modal view-modal">
            <div class="modal-header">
                <h2 class="modal-title" id="viewModalTitle">查看账号</h2>
                <p class="modal-subtitle" id="viewModalSubtitle">查看账号详情</p>
            </div>
            <div class="modal-body" id="viewModalBody">
                <!-- Dynamic content -->
            </div>
            <div class="modal-footer">
                <button type="button" class="btn-text danger" onclick="confirmDeleteFromView()" style="margin-right: auto;">删除</button>
                <button type="button" class="btn-text" onclick="closeViewModal()">关闭</button>
                <button type="button" class="btn btn-primary" id="viewEditBtn">编辑</button>
            </div>
        </div>
    </div>

    <!-- Export Modal -->
    <div class="modal-overlay" id="exportModal" onclick="closeExportModalOnOverlay(event)">
        <div class="modal">
            <div class="modal-header">
                <h2 class="modal-title">导出数据</h2>
                <p class="modal-subtitle">选择导出方式</p>
            </div>
            <div class="modal-body">
                <div class="export-options">
                    <div class="export-option selected" onclick="selectExportType('plain')" id="exportPlain">
                        <div class="export-option-icon">
                            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
                                <polyline points="14 2 14 8 20 8"></polyline>
                                <line x1="16" y1="13" x2="8" y2="13"></line>
                                <line x1="16" y1="17" x2="8" y2="17"></line>
                            </svg>
                        </div>
                        <div class="export-option-title">普通导出</div>
                        <div class="export-option-desc">明文 JSON 格式</div>
                    </div>
                    <div class="export-option" onclick="selectExportType('encrypted')" id="exportEncrypted">
                        <div class="export-option-icon">
                            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                <rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
                                <path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
                            </svg>
                        </div>
                        <div class="export-option-title">加密导出</div>
                        <div class="export-option-desc">AES-256 加密</div>
                    </div>
                </div>
                
                <div id="exportPasswordSection" style="display: none;">
                    <div class="form-group">
                        <label class="form-label">设置导出密码 *</label>
                        <input type="password" class="form-input" id="exportPassword" placeholder="输入密码用于加密数据">
                        <div class="input-hint">请牢记此密码,导入时需要使用</div>
                    </div>
                    <div class="form-group">
                        <label class="form-label">确认密码 *</label>
                        <input type="password" class="form-input" id="exportPasswordConfirm" placeholder="再次输入密码">
                    </div>
                </div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn-text" onclick="closeExportModal()">取消</button>
                <button type="button" class="btn btn-primary" onclick="executeExport()">导出</button>
            </div>
        </div>
    </div>

    <!-- Import Password Modal -->
    <div class="modal-overlay" id="importPasswordModal">
        <div class="modal">
            <div class="modal-header">
                <h2 class="modal-title">输入导入密码</h2>
                <p class="modal-subtitle">此数据文件已加密,需要密码才能解密</p>
            </div>
            <div class="modal-body">
                <div class="form-group">
                    <label class="form-label">解密密码 *</label>
                    <input type="password" class="form-input" id="importPassword" placeholder="输入导出时设置的密码">
                </div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn-text" onclick="cancelImport()">取消</button>
                <button type="button" class="btn btn-primary" onclick="executeImportWithPassword()">导入</button>
            </div>
        </div>
    </div>

    <!-- Duplicate Confirm Modal -->
    <div class="modal-overlay" id="duplicateModal">
        <div class="modal">
            <div class="modal-header">
                <h2 class="modal-title">发现重复账号</h2>
                <p class="modal-subtitle">该账号信息已存在</p>
            </div>
            <div class="modal-body">
                <div class="confirm-desc">以下账号的所有信息与您要添加的账号完全相同:</div>
                <div class="duplicate-info" id="duplicateInfo">
                    <!-- Dynamic content -->
                </div>
                <div class="confirm-desc" style="margin-top: 16px; color: var(--warning);">
                    <strong>提示:</strong>选择"覆盖"将用新信息替换原有账号(保留原ID和创建时间)。
                </div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn-text" onclick="cancelDuplicate()">取消添加</button>
                <button type="button" class="btn btn-danger" onclick="forceAddDuplicate()">强制添加(覆盖)</button>
            </div>
        </div>
    </div>
<!-- K520A6 -->
    <!-- Confirm Clear Modal with Double Confirm -->
    <div class="modal-overlay" id="confirmClearModal" onclick="closeConfirmClearModalOnOverlay(event)">
        <div class="modal confirm-modal">
            <div class="modal-body">
                <div class="confirm-icon">
                    <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                        <polyline points="3 6 5 6 21 6"></polyline>
                        <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
                    </svg>
                </div>
                <div class="confirm-title">确认清空所有数据?</div>
                <div class="confirm-desc">此操作不可恢复,所有账号数据将被永久删除。<br>建议先导出数据备份。</div>
                
                <div class="confirm-input-group">
                    <label class="confirm-input-label">请输入 <strong>DELETE</strong> 确认清空:</label>
                    <input type="text" class="confirm-input" id="clearConfirmInput" placeholder="输入 DELETE">
                </div>
            </div>
            <div class="modal-footer" style="justify-content: center;">
                <button type="button" class="btn-text" onclick="closeConfirmClearModal()">取消</button>
                <button type="button" class="btn btn-danger" onclick="executeClearAll()" id="confirmClearBtn" disabled>确认清空</button>
            </div>
        </div>
    </div>
<!-- aszv_top -->
    <!-- Simple Confirm Modal for single delete -->
    <div class="modal-overlay" id="confirmModal" onclick="closeConfirmModalOnOverlay(event)">
        <div class="modal confirm-modal">
            <div class="modal-body">
                <div class="confirm-icon">
                    <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                        <polyline points="3 6 5 6 21 6"></polyline>
                        <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
                    </svg>
                </div>
                <div class="confirm-title" id="simpleConfirmTitle">确认删除?</div>
                <div class="confirm-desc" id="simpleConfirmDesc">此操作不可恢复。</div>
            </div>
            <div class="modal-footer" style="justify-content: center;">
                <button type="button" class="btn-text" onclick="closeConfirmModal()">取消</button>
                <button type="button" class="btn btn-danger" onclick="executeSimpleConfirm()">确认删除</button>
            </div>
        </div>
    </div>

    <div class="toast" id="toast">
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
            <polyline points="20 6 9 17 4 12"></polyline>
        </svg>
        <span id="toastMsg">操作成功</span>
    </div>

    <script>
        // Data
        let accounts = JSON.parse(localStorage.getItem('pwd_accounts') || '[]');
        let currentFilter = 'all';
        let editingId = null;
        let viewingId = null;
        let simpleConfirmCallback = null;
        let duplicateCallback = null;
        let pendingExportType = 'plain';
        let pendingImportData = null;
        let pendingImportFile = null;

        // Password Generator Settings
        const defaultPwdSettings = {
            length: 16,
            uppercase: true,
            lowercase: true,
            numbers: true,
            symbols: false
        };

        // Check if mobile
        function isMobile() {
            return window.innerWidth <= 768;
        }

        // Parse tags from pipe-separated string
        function parseTags(tagsStr) {
            if (!tagsStr) return [];
            return tagsStr.split('|').map(t => t.trim()).filter(t => t.length > 0);
        }

        // Format tags to pipe-separated string
        function formatTags(tags) {
            if (!tags || tags.length === 0) return '';
            return tags.join(' | ');
        }

        // Auto-fix URL
        function fixUrl(url) {
            if (!url) return '';
            url = url.trim();
            if (!url) return '';
            if (!/^https?:\/\//i.test(url)) {
                url = 'http://' + url;
            }
            return url;
        }

        // Check if account is exactly the same
        function isAccountEqual(acc1, acc2) {
            const normalizeTags = (tags) => {
                if (!tags) return '';
                return [...tags].sort().join('|');
            };
            
            return acc1.platform === acc2.platform &&
                   acc1.username === acc2.username &&
                   acc1.password === acc2.password &&
                   (acc1.url || '') === (acc2.url || '') &&
                   normalizeTags(acc1.tags) === normalizeTags(acc2.tags) &&
                   (acc1.note || '') === (acc2.note || '');
        }

        // Check for duplicate account
        function findDuplicate(newAcc, excludeId = null) {
            return accounts.find(acc => {
                if (excludeId && acc.id === excludeId) return false;
                return isAccountEqual(acc, newAcc);
            });
        }

        // Simple XOR encryption for demo (in production use Web Crypto API)
        async function encryptData(data, password) {
            const encoder = new TextEncoder();
            const dataBytes = encoder.encode(JSON.stringify(data));
            const keyBytes = encoder.encode(password);
            
            const encrypted = new Uint8Array(dataBytes.length);
            for (let i = 0; i < dataBytes.length; i++) {
                encrypted[i] = dataBytes[i] ^ keyBytes[i % keyBytes.length];
            }
            
            // Convert to base64
            const base64 = btoa(String.fromCharCode(...encrypted));
            return {
                encrypted: true,
                data: base64,
                salt: Date.now().toString()
            };
        }

        async function decryptData(encryptedObj, password) {
            try {
                const decoder = new TextDecoder();
                const encoder = new TextEncoder();
                const keyBytes = encoder.encode(password);
                
                // Decode base64
                const encryptedBytes = Uint8Array.from(atob(encryptedObj.data), c => c.charCodeAt(0));
                
                const decrypted = new Uint8Array(encryptedBytes.length);
                for (let i = 0; i < encryptedBytes.length; i++) {
                    decrypted[i] = encryptedBytes[i] ^ keyBytes[i % keyBytes.length];
                }
                
                const jsonStr = decoder.decode(decrypted);
                return JSON.parse(jsonStr);
            } catch (e) {
                throw new Error('密码错误或数据损坏');
            }
        }

        // Theme
        function initTheme() {
            const saved = localStorage.getItem('pwd_theme') || 'light';
            document.documentElement.setAttribute('data-theme', saved);
            updateThemeIcon(saved);
        }

        function toggleTheme() {
            const current = document.documentElement.getAttribute('data-theme');
            const next = current === 'dark' ? 'light' : 'dark';
            document.documentElement.setAttribute('data-theme', next);
            localStorage.setItem('pwd_theme', next);
            updateThemeIcon(next);
        }

        function updateThemeIcon(theme) {
            const icon = document.getElementById('themeIcon');
            if (theme === 'dark') {
                icon.innerHTML = '<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>';
            } else {
                icon.innerHTML = '<circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>';
            }
        }

        // Tags
        function getAllTags() {
            const tags = new Set();
            accounts.forEach(a => a.tags?.forEach(t => tags.add(t)));
            return Array.from(tags).sort();
        }

        function renderTags() {
            const container = document.getElementById('tagsContainer');
            const allTags = getAllTags();
            
            let html = '<span class="tag-label">筛选:</span>';
            html += `<span class="tag ${currentFilter === 'all' ? 'active' : ''}" onclick="filterByTag('all')">全部</span>`;
            
            allTags.forEach(tag => {
                html += `<span class="tag ${currentFilter === tag ? 'active' : ''}" onclick="filterByTag('${tag}')">${escapeHtml(tag)}</span>`;
            });
            
            // Add mobile button
            html += `<button class="mobile-add-btn mobile-only" onclick="openModal()" title="添加账号">
                <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
                    <line x1="12" y1="5" x2="12" y2="19"></line>
                    <line x1="5" y1="12" x2="19" y2="12"></line>
                </svg>
            </button>`;
            
            container.innerHTML = html;
        }

        function filterByTag(tag) {
            currentFilter = tag;
            renderTags();
            renderAccounts();
        }

        // Password Generator
        function toggleGeneratorSettings() {
            const settings = document.getElementById('generatorSettings');
            settings.style.display = settings.style.display === 'none' ? 'block' : 'none';
        }

        function generatePassword() {
            const length = parseInt(document.getElementById('pwdLength').value) || 16;
            const useUpper = document.getElementById('pwdUppercase').checked;
            const useLower = document.getElementById('pwdLowercase').checked;
            const useNumbers = document.getElementById('pwdNumbers').checked;
            const useSymbols = document.getElementById('pwdSymbols').checked;
            
            let chars = '';
            if (useUpper) chars += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
            if (useLower) chars += 'abcdefghijklmnopqrstuvwxyz';
            if (useNumbers) chars += '0123456789';
            if (useSymbols) chars += '!@#$%^&*()_+-=[]{}|;:,.<>?';
            
            if (chars === '') {
                showToast('请至少选择一种字符类型');
                return;
            }
            
            let password = '';
            const array = new Uint32Array(length);
            crypto.getRandomValues(array);
            
            for (let i = 0; i < length; i++) {
                password += chars[array[i] % chars.length];
            }
            
            document.getElementById('password').value = password;
            document.getElementById('password').type = 'text';
            showToast('密码已生成');
        }

        // Modal & Form - No overlay click close
        function openModal(id = null) {
            editingId = id;
            const modal = document.getElementById('modal');
            const title = document.getElementById('modalTitle');
            const form = document.getElementById('accountForm');
            const deleteBtn = document.getElementById('deleteEditBtn');
            
            // Reset generator settings visibility
            document.getElementById('generatorSettings').style.display = 'none';
            
            if (id) {
                const acc = accounts.find(a => a.id === id);
                title.textContent = '编辑账号';
                document.getElementById('editId').value = id;
                document.getElementById('platform').value = acc.platform;
                document.getElementById('username').value = acc.username;
                document.getElementById('password').value = acc.password;
                document.getElementById('url').value = acc.url || '';
                document.getElementById('tagsInput').value = formatTags(acc.tags);
                document.getElementById('note').value = acc.note || '';
                deleteBtn.style.display = 'block';
            } else {
                title.textContent = '添加账号';
                form.reset();
                document.getElementById('editId').value = '';
                deleteBtn.style.display = 'none';
                // Set default password settings
                document.getElementById('pwdLength').value = defaultPwdSettings.length;
                document.getElementById('pwdUppercase').checked = defaultPwdSettings.uppercase;
                document.getElementById('pwdLowercase').checked = defaultPwdSettings.lowercase;
                document.getElementById('pwdNumbers').checked = defaultPwdSettings.numbers;
                document.getElementById('pwdSymbols').checked = defaultPwdSettings.symbols;
            }
            
            modal.classList.add('active');
            document.getElementById('platform').focus();
        }

        function closeModal() {
            document.getElementById('modal').classList.remove('active');
            editingId = null;
        }

        // Removed: closeModalOnOverlay - now only closes via button

        function togglePasswordInput() {
            const input = document.getElementById('password');
            const btn = event.target.closest('.icon-action-btn');
            if (input.type === 'password') {
                input.type = 'text';
            } else {
                input.type = 'password';
            }
        }

        function confirmDeleteCurrent() {
            if (editingId) {
                const acc = accounts.find(a => a.id === editingId);
                showSimpleConfirm(
                    `确认删除 ${acc.platform}?`,
                    '此操作不可恢复,账号将被永久删除。',
                    () => {
                        deleteAccount(editingId);
                        closeModal();
                    }
                );
            }
        }

        // View Modal
        function openViewModal(id) {
            viewingId = id;
            const acc = accounts.find(a => a.id === id);
            if (!acc) return;

            document.getElementById('viewModalTitle').textContent = acc.platform;
            document.getElementById('viewModalSubtitle').textContent = '查看账号详情';
            
            let html = '';
            
            // Username
            html += `
                <div class="view-field">
                    <div class="view-label">用户名</div>
                    <div class="view-value-box">
                        <span class="view-value-text">${escapeHtml(acc.username)}</span>
                        <div class="view-actions">
                            <button class="view-btn" onclick="copyFromView('${escapeHtml(acc.username)}', this)">复制</button>
                        </div>
                    </div>
                </div>
            `;
            
            // Password
            html += `
                <div class="view-field">
                    <div class="view-label">密码</div>
                    <div class="view-value-box">
                        <span class="view-value-text">
                            <span id="viewPwdText" class="password-text" style="filter: blur(4px); user-select: none;">${escapeHtml(acc.password)}</span>
                        </span>
                        <div class="view-actions">
                            <button class="view-btn" onclick="toggleViewPassword()" id="viewPwdBtn">显示</button>
                            <button class="view-btn" onclick="copyFromView('${escapeHtml(acc.password)}', this)">复制</button>
                        </div>
                    </div>
                </div>
            `;
            
            // Tags - container style
            if (acc.tags && acc.tags.length > 0) {
                html += `
                    <div class="view-field">
                        <div class="view-label">标签</div>
                        <div class="view-tags-container">
                            ${acc.tags.map(t => `<span class="view-tag-item">${escapeHtml(t)}</span>`).join('')}
                        </div>
                    </div>
                `;
            }
            
            // URL
            if (acc.url) {
                html += `
                    <div class="view-field">
                        <div class="view-label">网站链接</div>
                        <a href="${escapeHtml(acc.url)}" target="_blank" class="view-link">
                            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path>
                                <polyline points="15 3 21 3 21 9"></polyline>
                                <line x1="10" y1="14" x2="21" y2="3"></line>
                            </svg>
                            ${escapeHtml(acc.url)}
                        </a>
                    </div>
                `;
            }
            
            // Note - resizable like edit mode
            if (acc.note) {
                html += `
                    <div class="view-field">
                        <div class="view-label">备注(可拖动调整大小)</div>
                        <textarea class="view-note-resizable" readonly>${escapeHtml(acc.note)}</textarea>
                    </div>
                `;
            }
            
            document.getElementById('viewModalBody').innerHTML = html;
            
            // Set edit button action
            document.getElementById('viewEditBtn').onclick = () => {
                closeViewModal();
                setTimeout(() => openModal(id), 100);
            };
            
            document.getElementById('viewModal').classList.add('active');
        }

        function closeViewModal() {
            document.getElementById('viewModal').classList.remove('active');
            viewingId = null;
        }

        function closeViewModalOnOverlay(e) {
            if (e.target === e.currentTarget) closeViewModal();
        }

        function toggleViewPassword() {
            const pwd = document.getElementById('viewPwdText');
            const btn = document.getElementById('viewPwdBtn');
            
            if (pwd.style.filter === 'blur(4px)') {
                pwd.style.filter = 'none';
                pwd.style.userSelect = 'text';
                btn.textContent = '隐藏';
            } else {
                pwd.style.filter = 'blur(4px)';
                pwd.style.userSelect = 'none';
                btn.textContent = '显示';
            }
        }

        function confirmDeleteFromView() {
            if (viewingId) {
                const acc = accounts.find(a => a.id === viewingId);
                showSimpleConfirm(
                    `确认删除 ${acc.platform}?`,
                    '此操作不可恢复,账号将被永久删除。',
                    () => {
                        deleteAccount(viewingId);
                        closeViewModal();
                    }
                );
            }
        }

        function copyFromView(text, btn) {
            navigator.clipboard.writeText(text).then(() => {
                btn.textContent = '已复制';
                btn.classList.add('copied');
                showToast('已复制到剪贴板');
                setTimeout(() => {
                    btn.textContent = '复制';
                    btn.classList.remove('copied');
                }, 2000);
            }).catch(() => {
                // Fallback for mobile
                const textArea = document.createElement('textarea');
                textArea.value = text;
                textArea.style.position = 'fixed';
                textArea.style.left = '-999999px';
                document.body.appendChild(textArea);
                textArea.focus();
                textArea.select();
                try {
                    document.execCommand('copy');
                    btn.textContent = '已复制';
                    btn.classList.add('copied');
                    showToast('已复制到剪贴板');
                    setTimeout(() => {
                        btn.textContent = '复制';
                        btn.classList.remove('copied');
                    }, 2000);
                } catch (err) {
                    showToast('复制失败');
                }
                document.body.removeChild(textArea);
            });
        }

        // Save with duplicate check
        function saveAccount(e) {
            e.preventDefault();
            
            const platform = document.getElementById('platform').value.trim();
            const username = document.getElementById('username').value.trim();
            const password = document.getElementById('password').value;
            const url = fixUrl(document.getElementById('url').value.trim());
            const tags = parseTags(document.getElementById('tagsInput').value);
            const note = document.getElementById('note').value.trim();
            
            if (!platform || !username || !password) return;
            
            const newAcc = {
                platform,
                username,
                password,
                url,
                tags,
                note
            };
            
            // Check for duplicates when adding new account
            if (!editingId) {
                const duplicate = findDuplicate(newAcc);
                if (duplicate) {
                    showDuplicateModal(newAcc, duplicate);
                    return;
                }
            }
            
            // Save directly if editing or no duplicate
            doSaveAccount(newAcc);
        }

        function doSaveAccount(newAcc, replaceId = null) {
            const data = {
                id: replaceId || editingId || Date.now().toString(),
                ...newAcc,
                updatedAt: new Date().toISOString()
            };
            
            if (editingId || replaceId) {
                const idx = accounts.findIndex(a => a.id === (replaceId || editingId));
                if (idx >= 0) {
                    // Preserve original created time if replacing
                    if (replaceId && !editingId) {
                        data.updatedAt = accounts[idx].updatedAt;
                    }
                    accounts[idx] = data;
                }
                showToast(replaceId && !editingId ? '账号已覆盖' : '账号已更新');
            } else {
                accounts.push(data);
                showToast('账号已添加');
            }
            
            saveData();
            closeModal();
            renderAccounts();
            renderTags();
        }

        function showDuplicateModal(newAcc, duplicate) {
            const infoHtml = `
                <div class="duplicate-info-item">
                    <span class="duplicate-info-label">平台</span>
                    <span class="duplicate-info-value">${escapeHtml(duplicate.platform)}</span>
                </div>
                <div class="duplicate-info-item">
                    <span class="duplicate-info-label">用户名</span>
                    <span class="duplicate-info-value">${escapeHtml(duplicate.username)}</span>
                </div>
                <div class="duplicate-info-item">
                    <span class="duplicate-info-label">密码</span>
                    <span class="duplicate-info-value">••••••</span>
                </div>
                ${duplicate.url ? `
                <div class="duplicate-info-item">
                    <span class="duplicate-info-label">链接</span>
                    <span class="duplicate-info-value">${escapeHtml(duplicate.url)}</span>
                </div>
                ` : ''}
                ${duplicate.tags?.length ? `
                <div class="duplicate-info-item">
                    <span class="duplicate-info-label">标签</span>
                    <span class="duplicate-info-value">${formatTags(duplicate.tags)}</span>
                </div>
                ` : ''}
                ${duplicate.note ? `
                <div class="duplicate-info-item">
                    <span class="duplicate-info-label">备注</span>
                    <span class="duplicate-info-value">${escapeHtml(duplicate.note)}</span>
                </div>
                ` : ''}
            `;
            
            document.getElementById('duplicateInfo').innerHTML = infoHtml;
            document.getElementById('duplicateModal').classList.add('active');
            
            // Store pending data
            duplicateCallback = { newAcc, replaceId: duplicate.id };
        }

        function cancelDuplicate() {
            document.getElementById('duplicateModal').classList.remove('active');
            duplicateCallback = null;
        }

        function forceAddDuplicate() {
            if (duplicateCallback) {
                const { newAcc, replaceId } = duplicateCallback;
                doSaveAccount(newAcc, replaceId);
                document.getElementById('duplicateModal').classList.remove('active');
                duplicateCallback = null;
            }
        }

        function deleteAccount(id) {
            accounts = accounts.filter(a => a.id !== id);
            saveData();
            renderAccounts();
            renderTags();
            showToast('账号已删除');
        }

        // Export Functions
        function showExportModal() {
            if (!accounts.length) {
                showToast('暂无数据可导出');
                return;
            }
            document.getElementById('exportModal').classList.add('active');
            selectExportType('plain');
        }

        function closeExportModal() {
            document.getElementById('exportModal').classList.remove('active');
        }

        function closeExportModalOnOverlay(e) {
            if (e.target === e.currentTarget) closeExportModal();
        }

        function selectExportType(type) {
            pendingExportType = type;
            document.getElementById('exportPlain').classList.toggle('selected', type === 'plain');
            document.getElementById('exportEncrypted').classList.toggle('selected', type === 'encrypted');
            
            const pwdSection = document.getElementById('exportPasswordSection');
            pwdSection.style.display = type === 'encrypted' ? 'block' : 'none';
            
            if (type === 'encrypted') {
                setTimeout(() => document.getElementById('exportPassword').focus(), 100);
            }
        }

        async function executeExport() {
            const type = pendingExportType;
            
            if (type === 'encrypted') {
                const pwd = document.getElementById('exportPassword').value;
                const pwdConfirm = document.getElementById('exportPasswordConfirm').value;
                
                if (!pwd) {
                    showToast('请输入导出密码');
                    return;
                }
                if (pwd !== pwdConfirm) {
                    showToast('两次输入的密码不一致');
                    return;
                }
                if (pwd.length < 6) {
                    showToast('密码长度至少6位');
                    return;
                }
                
                try {
                    const encrypted = await encryptData(accounts, pwd);
                    downloadFile(JSON.stringify(encrypted, null, 2), `accounts_encrypted_${new Date().toISOString().split('T')[0]}.json`);
                    showToast('加密数据已导出');
                    closeExportModal();
                } catch (e) {
                    showToast('加密失败:' + e.message);
                }
            } else {
                const data = JSON.stringify(accounts, null, 2);
                downloadFile(data, `accounts_${new Date().toISOString().split('T')[0]}.json`);
                showToast('数据已导出');
                closeExportModal();
            }
        }

        function downloadFile(content, filename) {
            const blob = new Blob([content], {type: 'application/json'});
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = filename;
            a.click();
            URL.revokeObjectURL(url);
        }

        // Import Functions
        async function importData(input) {
            const file = input.files[0];
            if (!file) return;
            
            const reader = new FileReader();
            reader.onload = async (e) => {
                try {
                    const content = e.target.result;
                    let data;
                    
                    try {
                        data = JSON.parse(content);
                    } catch (err) {
                        throw new Error('文件格式错误,不是有效的 JSON');
                    }
                    
                    // Check if encrypted
                    if (data.encrypted && data.data) {
                        pendingImportData = data;
                        pendingImportFile = file;
                        document.getElementById('importPasswordModal').classList.add('active');
                        document.getElementById('importPassword').value = '';
                        setTimeout(() => document.getElementById('importPassword').focus(), 100);
                        return;
                    }
                    
                    // Plain import
                    if (!Array.isArray(data)) {
                        throw new Error('数据格式错误,应为账号数组');
                    }
                    
                    processImportData(data);
                } catch (err) {
                    alert('导入失败:' + err.message);
                }
                input.value = '';
            };
            reader.readAsText(file);
        }

        async function executeImportWithPassword() {
            const pwd = document.getElementById('importPassword').value;
            if (!pwd) {
                showToast('请输入解密密码');
                return;
            }
            
            try {
                const decrypted = await decryptData(pendingImportData, pwd);
                if (!Array.isArray(decrypted)) {
                    throw new Error('解密后数据格式错误');
                }
                processImportData(decrypted);
                document.getElementById('importPasswordModal').classList.remove('active');
                pendingImportData = null;
                pendingImportFile = null;
            } catch (err) {
                showToast('解密失败:' + err.message);
            }
        }

        function cancelImport() {
            document.getElementById('importPasswordModal').classList.remove('active');
            pendingImportData = null;
            pendingImportFile = null;
            document.getElementById('importFile').value = '';
        }

        function processImportData(data) {
            // Fix URLs in imported data
            data.forEach(item => {
                if (item.url) {
                    item.url = fixUrl(item.url);
                }
            });
            
            let added = 0;
            let skipped = 0;
            let updated = 0;
            const skippedItems = [];
            
            data.forEach(item => {
                if (!item.id || !item.platform) return;
                
                // Check for exact duplicate
                const existing = accounts.find(a => a.id === item.id);
                if (existing) {
                    // Check if exactly the same
                    if (isAccountEqual(existing, item)) {
                        skipped++;
                        skippedItems.push(item.platform);
                        return;
                    }
                    // Different - update if newer
                    if (new Date(item.updatedAt) > new Date(existing.updatedAt)) {
                        accounts[accounts.indexOf(existing)] = item;
                        updated++;
                    }
                } else {
                    // Check if new item is duplicate of existing (by content)
                    const contentDuplicate = accounts.find(a => isAccountEqual(a, item));
                    if (contentDuplicate) {
                        skipped++;
                        skippedItems.push(item.platform);
                        return;
                    }
                    accounts.push(item);
                    added++;
                }
            });
            
            saveData();
            renderAccounts();
            renderTags();
            
            let msg = `导入完成:新增 ${added} 条`;
            if (updated > 0) msg += `,更新 ${updated} 条`;
            if (skipped > 0) msg += `,跳过 ${skipped} 条重复`;
            showToast(msg);
            
            if (skippedItems.length > 0) {
                setTimeout(() => {
                    alert(`以下账号因信息完全相同已跳过:\n${skippedItems.join('\n')}`);
                }, 300);
            }
        }

        // Clear All with Double Confirm
        function confirmClearAll() {
            if (accounts.length === 0) {
                showToast('暂无数据可清空');
                return;
            }
            document.getElementById('confirmClearModal').classList.add('active');
            document.getElementById('clearConfirmInput').value = '';
            document.getElementById('clearConfirmInput').classList.remove('error');
            document.getElementById('confirmClearBtn').disabled = true;
            document.getElementById('clearConfirmInput').focus();
        }

        function closeConfirmClearModal() {
            document.getElementById('confirmClearModal').classList.remove('active');
        }

        function closeConfirmClearModalOnOverlay(e) {
            if (e.target === e.currentTarget) closeConfirmClearModal();
        }

        // Real-time validation for clear confirm
        document.getElementById('clearConfirmInput')?.addEventListener('input', function(e) {
            const input = e.target;
            const btn = document.getElementById('confirmClearBtn');
            if (input.value === 'DELETE') {
                btn.disabled = false;
                input.classList.remove('error');
            } else {
                btn.disabled = true;
                if (input.value.length >= 6) {
                    input.classList.add('error');
                } else {
                    input.classList.remove('error');
                }
            }
        });

        function executeClearAll() {
            const input = document.getElementById('clearConfirmInput');
            if (input.value !== 'DELETE') {
                input.classList.add('error');
                return;
            }
            
            accounts = [];
            saveData();
            renderAccounts();
            renderTags();
            closeConfirmClearModal();
            showToast('所有数据已清空');
        }

        // Simple Confirm Modal
        function showSimpleConfirm(title, desc, callback) {
            simpleConfirmCallback = callback;
            document.getElementById('simpleConfirmTitle').textContent = title;
            document.getElementById('simpleConfirmDesc').textContent = desc;
            document.getElementById('confirmModal').classList.add('active');
        }

        function closeConfirmModal() {
            document.getElementById('confirmModal').classList.remove('active');
            simpleConfirmCallback = null;
        }

        function closeConfirmModalOnOverlay(e) {
            if (e.target === e.currentTarget) closeConfirmModal();
        }

        function executeSimpleConfirm() {
            if (simpleConfirmCallback) {
                simpleConfirmCallback();
            }
            closeConfirmModal();
        }

        // Render
        function renderAccounts() {
            const search = document.getElementById('searchInput').value.toLowerCase();
            const grid = document.getElementById('accountsGrid');
            const empty = document.getElementById('emptyState');
            
            let filtered = accounts.filter(a => {
                const matchSearch = !search || 
                    a.platform.toLowerCase().includes(search) ||
                    a.username.toLowerCase().includes(search) ||
                    a.tags?.some(t => t.toLowerCase().includes(search)) ||
                    (a.url && a.url.toLowerCase().includes(search)) ||
                    a.note?.toLowerCase().includes(search);
                
                const matchTag = currentFilter === 'all' || a.tags?.includes(currentFilter);
                
                return matchSearch && matchTag;
            });
            
            // Sort accounts by update time
            filtered.sort((a, b) => {
                const timeA = new Date(a.updatedAt).getTime();
                const timeB = new Date(b.updatedAt).getTime();
                if (window.sortOrder === 'oldest') {
                    return timeA - timeB; // Oldest first
                }
                return timeB - timeA; // Newest first (default)
            });

            document.getElementById('totalCount').textContent = filtered.length;
            
            if (accounts.length > 0) {
                const last = accounts.reduce((a, b) => 
                    new Date(a.updatedAt) > new Date(b.updatedAt) ? a : b
                );
                document.getElementById('lastUpdate').textContent = new Date(last.updatedAt).toLocaleDateString('zh-CN');
            } else {
                document.getElementById('lastUpdate').textContent = '-';
            }
            
            if (filtered.length === 0) {
                grid.style.display = 'none';
                empty.style.display = 'block';
                empty.querySelector('.empty-title').textContent = search || currentFilter !== 'all' ? '无匹配结果' : '暂无账号';
                empty.querySelector('.empty-desc').textContent = search || currentFilter !== 'all' ? '尝试其他关键词或筛选条件' : '点击右上角"添加账号"开始使用';
                return;
            }
            
            grid.style.display = 'grid';
            empty.style.display = 'none';
            
            grid.innerHTML = filtered.map(acc => `
                <div class="account-card" onclick="handleCardClick(event, '${acc.id}')">
                    <div class="card-header">
                        <div class="platform-info">
                            <div class="platform-icon">${acc.platform.charAt(0).toUpperCase()}</div>
                            <div class="platform-meta">
                                <div class="platform-name">${escapeHtml(acc.platform)}</div>
                                ${acc.url ? `<a href="${escapeHtml(acc.url)}" target="_blank" class="platform-url" title="访问网站" onclick="event.stopPropagation()">
                                    <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                        <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path>
                                        <polyline points="15 3 21 3 21 9"></polyline>
                                        <line x1="10" y1="14" x2="21" y2="3"></line>
                                    </svg>
                                    ${escapeHtml(acc.url.replace(/^https?:\/\//, '').replace(/\/$/, ''))}
                                </a>` : ''}
                                ${acc.tags?.length ? `<div class="card-tags">
                                    ${acc.tags.map(t => `<span class="card-tag">${escapeHtml(t)}</span>`).join('')}
                                </div>` : ''}
                            </div>
                        </div>
                        <div class="card-actions" onclick="event.stopPropagation()">
                            <button class="card-btn" onclick="openModal('${acc.id}')" title="编辑">
                                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                    <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
                                    <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
                                </svg>
                            </button>
                            <button class="card-btn delete" onclick="confirmDeleteAccount('${acc.id}', '${escapeHtml(acc.platform)}'); event.stopPropagation();" title="删除">
                                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                    <polyline points="3 6 5 6 21 6"></polyline>
                                    <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
                                </svg>
                            </button>
                        </div>
                    </div>
                    
                    <div class="fields">
                        <div class="field">
                            <span class="field-label">用户名</span>
                            <div class="field-value">
                                <span class="field-text">${escapeHtml(acc.username)}</span>
                                <div class="field-actions">
                                    <button class="field-btn" onclick="copyText('${escapeHtml(acc.username)}', this); event.stopPropagation();">复制</button>
                                </div>
                            </div>
                        </div>
                        
                        <div class="field">
                            <span class="field-label">密码</span>
                            <div class="field-value">
                                <span class="field-text password-text" id="pwd-${acc.id}">${escapeHtml(acc.password)}</span>
                                <div class="field-actions">
                                    <button class="field-btn" onclick="togglePwd('${acc.id}'); event.stopPropagation();">显示</button>
                                    <button class="field-btn" onclick="copyText('${escapeHtml(acc.password)}', this); event.stopPropagation();">复制</button>
                                </div>
                            </div>
                        </div>
                        
                        ${acc.note ? `
                        <div class="field">
                            <span class="field-label">备注</span>
                            <div class="note-text">${escapeHtml(acc.note)}</div>
                        </div>
                        ` : ''}
                    </div>

                    <div class="card-footer">
                        <div class="update-time">
                            <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                <circle cx="12" cy="12" r="10"></circle>
                                <polyline points="12 6 12 12 16 14"></polyline>
                            </svg>
                            <span>${formatRelativeTime(acc.updatedAt)}</span>
                        </div>
                    </div>
                </div>
            `).join('');
        }

        function handleCardClick(event, id) {
            // Don't open view if clicking on buttons or links
            if (event.target.closest('.card-actions') || event.target.closest('.field-actions') || event.target.closest('a')) {
                return;
            }
            openViewModal(id);
        }

        function togglePwd(id) {
            const el = document.getElementById(`pwd-${id}`);
            const btn = event.target;
            
            if (el.classList.contains('revealed')) {
                el.classList.remove('revealed');
                btn.textContent = '显示';
            } else {
                el.classList.add('revealed');
                btn.textContent = '隐藏';
            }
        }

        // Utils
        function copyText(text, btn) {
            // Try modern clipboard API first
            if (navigator.clipboard && navigator.clipboard.writeText) {
                navigator.clipboard.writeText(text).then(() => {
                    showCopied(btn);
                }).catch(() => {
                    fallbackCopy(text, btn);
                });
            } else {
                fallbackCopy(text, btn);
            }
        }

        function fallbackCopy(text, btn) {
            const textArea = document.createElement('textarea');
            textArea.value = text;
            textArea.style.position = 'fixed';
            textArea.style.left = '-999999px';
            textArea.style.top = '0';
            document.body.appendChild(textArea);
            textArea.focus();
            textArea.select();
            
            try {
                const successful = document.execCommand('copy');
                if (successful) {
                    showCopied(btn);
                } else {
                    showToast('复制失败');
                }
            } catch (err) {
                showToast('复制失败');
            }
            document.body.removeChild(textArea);
        }

        function showCopied(btn) {
            btn.textContent = '已复制';
            btn.classList.add('copied');
            showToast('已复制到剪贴板');
            setTimeout(() => {
                btn.textContent = '复制';
                btn.classList.remove('copied');
            }, 2000);
        }

        function showToast(msg) {
            const toast = document.getElementById('toast');
            document.getElementById('toastMsg').textContent = msg;
            toast.classList.add('show');
            setTimeout(() => toast.classList.remove('show'), 3000);
        }

        function escapeHtml(text) {
            const div = document.createElement('div');
            div.textContent = text;
            return div.innerHTML;
        }

        // Format relative time in Chinese
        function formatRelativeTime(dateString) {
            const date = new Date(dateString);
            const now = new Date();
            const diffMs = now - date;
            const diffSec = Math.floor(diffMs / 1000);
            const diffMin = Math.floor(diffSec / 60);
            const diffHour = Math.floor(diffMin / 60);
            const diffDay = Math.floor(diffHour / 24);
            const diffMonth = Math.floor(diffDay / 30);
            const diffYear = Math.floor(diffDay / 365);

            if (diffSec < 60) {
                return '刚刚';
            } else if (diffMin < 60) {
                return `${diffMin}分钟前`;
            } else if (diffHour < 24) {
                return `${diffHour}小时前`;
            } else if (diffDay === 1) {
                return '昨天';
            } else if (diffDay < 30) {
                return `${diffDay}天前`;
            } else if (diffMonth < 12) {
                return `${diffMonth}个月前`;
            } else if (diffYear === 1) {
                return '1年前';
            } else {
                return `${diffYear}年前`;
            }
        }

        // Confirm delete with modal
        function confirmDeleteAccount(id, platform) {
            showSimpleConfirm(
                `确认删除 ${platform}?`,
                '此操作不可恢复,账号将被永久删除。',
                () => {
                    deleteAccount(id);
                }
            );
        }

        function saveData() {
            localStorage.setItem('pwd_accounts', JSON.stringify(accounts));
        }

        // Sort order - default newest first
        window.sortOrder = 'newest';

        // Toggle sort order
        function toggleSortOrder() {
            window.sortOrder = window.sortOrder === 'newest' ? 'oldest' : 'newest';
            updateSortIcon();
            renderAccounts();
            showToast(window.sortOrder === 'newest' ? '已切换:最新在前' : '已切换:最早在前');
        }

        // Update sort icon based on current order
        function updateSortIcon() {
            const icon = document.getElementById('sortIcon');
            const btn = document.getElementById('sortBtn');
            const text = document.getElementById('sortText');

            if (window.sortOrder === 'newest') {
                // Newest first icon (arrow up - descending)
                icon.innerHTML = `
                    <polyline points="12 6 12 18" style="stroke-width: 2.5"></polyline>
                    <polyline points="8 10 12 6 16 10" style="stroke-width: 2.5"></polyline>
                `;
                btn.title = "当前:最新在前,点击切换";
                btn.classList.add('active');
                if (text) text.textContent = '最新';
            } else {
                // Oldest first icon (arrow down - ascending)
                icon.innerHTML = `
                    <polyline points="12 6 12 18" style="stroke-width: 2.5"></polyline>
                    <polyline points="8 14 12 18 16 14" style="stroke-width: 2.5"></polyline>
                `;
                btn.title = "当前:最早在前,点击切换";
                btn.classList.remove('active');
                if (text) text.textContent = '最早';
            }
        }

        // Init
        document.addEventListener('DOMContentLoaded', () => {
            initTheme();
            updateSortIcon();
            renderAccounts();
            renderTags();
        });

        // Handle resize
        let lastWidth = window.innerWidth;
        window.addEventListener('resize', () => {
            const currentWidth = window.innerWidth;
            const wasMobile = lastWidth <= 768;
            const isMobileNow = currentWidth <= 768;
            if (wasMobile !== isMobileNow) {
                renderAccounts();
            }
            lastWidth = currentWidth;
        });

        // Keyboard
        document.addEventListener('keydown', (e) => {
            if (e.key === 'Escape') {
                closeModal();
                closeViewModal();
                closeConfirmModal();
                closeConfirmClearModal();
                closeExportModal();
                cancelImport();
                cancelDuplicate();
            }
        });
    </script>
</body>
</html>

这是说明文本:

------------------------------------------------------
作者:Yangxiao
作者域名:aszv.top
声明:作者不承担任何责任,是否使用取决于你
安全与否取决于你怎么保存的数据,即使做的再好,也不能避免物理层面信息泄露,实在不放心的可断网使用
------------------------------------------------------
使用方法:
1、电脑端直接双击打开即可使用,这是纯前端网页工具,无需后端运行
2、手机上可以在文件夹里找到文件,选择浏览器打开也可以使用,收藏到书签方便下次访问
3、有服务器的,可以把 index.html 文件上传到网站根目录下,访问域名即可直接使用
------------------------------------------------------

这里是网盘文件地址:

文件存在蓝奏云上,登录的是15310649220手机号,文件路径:根目录/2026/05/11/账号管理器/账号管理器.zip

文件存在夸克网盘上,登录手机号同上,文件路径:根目录/2026/05/11/账号管理器/账号管理器.zip

网站搭建地址同域名

演示图片:

3053406581.png

4067391582.png

下面是发布在blog.aszv.top的原文章内容:

这是一款账号管理器网页工具,核心作用是帮你集中、安全地管理各类平台的账号密码信息,整体设计兼顾了实用性和易用性,还适配了桌面端和移动端使用场景,具体功能和特点可以分为这些方面:

展示图片:

2026-05-09_07-55-18.pngSnipaste_2026-04-24_20-40-05.png
Snipaste_2026-04-24_20-40-19.pngSnipaste_2026-04-24_20-40-38.png
Snipaste_2026-04-24_20-41-28.pngSnipaste_2026-04-24_20-41-39.png

1. 核心功能:账号信息管理

  • 集中存储:可以添加并保存不同平台(比如网站、APP)的账号信息,包括平台名称、网址、用户名、密码,还能记录备注、添加标签,把零散的账号密码都汇总在一处,不用再记在本子或随便存在记事本里。
  • 便捷查看/编辑:账号信息以卡片形式展示,点击卡片能查看详情,也能随时编辑、删除账号,还支持复制账号/密码(复制后会有提示),密码默认模糊显示,可手动切换显示/隐藏。
  • 密码生成:添加账号时,工具自带密码生成功能,还能自定义密码长度、是否包含数字/符号等,帮你创建安全度更高的密码。

2. 实用辅助功能

  • 搜索与筛选:支持关键词搜索账号,也能通过标签筛选,快速找到需要的账号信息;还能对账号列表进行排序,方便管理。
  • 数据备份/恢复:支持导出账号数据(备份)和导入数据(恢复/迁移),不用担心数据丢失;
  • 主题切换:有浅色/深色两种主题模式,可根据使用习惯或环境切换,视觉体验更舒适。

3. 体验与适配

  • 界面设计:整体简洁清晰,卡片式布局一目了然,操作按钮(比如添加、编辑、复制)都很直观,hover(悬停)时还有交互反馈,使用起来很顺手;
  • 多端适配:专门做了移动端优化(比如调整布局、缩放适配),手机上也能方便操作,桌面端则保留更完整的操作空间;
  • 细节体验:比如密码复制后有 Toast 提示、删除账号有二次确认(防止误删)、账号卡片显示更新时间等,细节上考虑得比较周全。

简单来说,这个工具解决了“账号密码太多记不住、找起来麻烦、密码不安全”的问题,相当于一个专属的“数字密码本”,而且是网页形式,不用安装额外软件,打开就能用,还兼顾了使用的便捷性和数据管理的安全性。

0

评论 (0)

取消