video-shuoshan/prototype/admin-users.html
zyc ffe92f7b15 Initial commit: 即梦视频生成平台
- web/: React + Vite + TypeScript 前端
- backend/: Django + DRF + SimpleJWT 后端
- prototype/: HTML 设计原型
- docs/: PRD 和设计评审文档
- test: 单元测试 + E2E 极限测试
2026-03-13 09:59:33 +08:00

532 lines
24 KiB
HTML

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>用户管理 — Jimeng Admin</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=Noto+Sans+SC:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root {
--bg-page: #0a0a0f;
--bg-sidebar: #111118;
--bg-card: #16161e;
--border: #2a2a38;
--primary: #00b8e6;
--primary-dim: rgba(0,184,230,0.12);
--text-1: #ffffff;
--text-2: #8a8a9a;
--text-3: #4a4a5a;
--hover: rgba(255,255,255,0.06);
--success: #34d399;
--danger: #f87171;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Noto Sans SC', 'Space Grotesk', system-ui, sans-serif;
background: var(--bg-page); color: var(--text-1);
height: 100vh; overflow: hidden;
}
.layout { display: flex; height: 100vh; }
.sidebar {
width: 240px; min-width: 240px; background: var(--bg-sidebar);
border-right: 1px solid var(--border);
display: flex; flex-direction: column;
}
.sidebar-logo {
padding: 20px 24px; display: flex; align-items: center; gap: 10px;
border-bottom: 1px solid var(--border);
}
.sidebar-logo span { font-size: 15px; font-weight: 600; }
.sidebar-nav { flex: 1; padding: 12px 8px; display: flex; flex-direction: column; gap: 2px; }
.nav-item {
display: flex; align-items: center; gap: 12px;
padding: 10px 16px; border-radius: 8px;
font-size: 14px; color: var(--text-2);
text-decoration: none; transition: all 0.15s; cursor: pointer;
}
.nav-item:hover { background: var(--hover); color: var(--text-1); }
.nav-item.active { background: rgba(255,255,255,0.08); color: var(--text-1); font-weight: 500; }
.nav-item svg { opacity: 0.6; }
.nav-item.active svg, .nav-item:hover svg { opacity: 1; }
.sidebar-footer { padding: 16px; border-top: 1px solid var(--border); }
.sidebar-footer a {
display: flex; align-items: center; gap: 8px;
font-size: 13px; color: var(--text-3); text-decoration: none;
padding: 8px 12px; border-radius: 6px; transition: all 0.15s;
}
.sidebar-footer a:hover { color: var(--text-2); background: var(--hover); }
.main { flex: 1; overflow-y: auto; display: flex; flex-direction: column; }
.topbar {
display: flex; align-items: center; justify-content: space-between;
padding: 16px 32px; border-bottom: 1px solid var(--border);
flex-shrink: 0; background: var(--bg-page);
position: sticky; top: 0; z-index: 10; backdrop-filter: blur(12px);
}
.topbar h1 { font-size: 18px; font-weight: 600; }
.topbar-right { display: flex; align-items: center; gap: 12px; }
.admin-badge {
font-size: 12px; color: var(--primary); padding: 4px 12px;
background: var(--primary-dim); border-radius: 6px; font-weight: 500;
}
.topbar-btn {
padding: 6px 14px; background: transparent;
border: 1px solid var(--border); border-radius: 6px;
color: var(--text-2); font-size: 13px; cursor: pointer; transition: all 0.15s;
}
.topbar-btn:hover { background: var(--hover); color: var(--text-1); }
.content { padding: 28px 32px; flex: 1; }
/* Search bar */
.search-bar {
display: flex; gap: 12px; margin-bottom: 20px; align-items: center;
}
.search-input {
flex: 1; max-width: 320px; height: 38px; padding: 0 14px 0 38px;
background: rgba(255,255,255,0.04); border: 1px solid var(--border);
border-radius: 8px; color: var(--text-1); font-size: 13px; outline: none;
transition: border-color 0.2s;
}
.search-input:focus { border-color: var(--primary); }
.search-input::placeholder { color: var(--text-3); }
.search-wrap { position: relative; flex: 1; max-width: 320px; }
.search-wrap svg {
position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
color: var(--text-3);
}
.filter-select {
height: 38px; padding: 0 32px 0 12px;
background: rgba(255,255,255,0.04); border: 1px solid var(--border);
border-radius: 8px; color: var(--text-1); font-size: 13px;
outline: none; cursor: pointer;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%238a8a9a' stroke-width='2' xmlns='http://www.w3.org/2000/svg'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 10px center;
}
.filter-select option { background: #1e1e2e; color: var(--text-1); }
.refresh-btn {
height: 38px; padding: 0 14px; background: transparent;
border: 1px solid var(--border); border-radius: 8px;
color: var(--text-2); font-size: 13px; cursor: pointer;
display: flex; align-items: center; gap: 6px; transition: all 0.15s;
}
.refresh-btn:hover { background: var(--hover); color: var(--text-1); }
/* Table */
.table-card {
background: var(--bg-card); border: 1px solid var(--border);
border-radius: 12px; overflow: hidden;
}
table { width: 100%; border-collapse: collapse; }
th, td { padding: 12px 16px; text-align: left; font-size: 13px; }
th {
color: var(--text-2); font-weight: 500;
background: rgba(255,255,255,0.02);
border-bottom: 1px solid var(--border);
white-space: nowrap;
}
th .sort-icon { display: inline-block; margin-left: 4px; opacity: 0.3; }
td { color: var(--text-1); border-bottom: 1px solid rgba(255,255,255,0.03); }
tr:last-child td { border-bottom: none; }
tr:hover td { background: var(--hover); }
.username-cell { color: var(--primary); cursor: pointer; }
.username-cell:hover { text-decoration: underline; }
.status-badge {
display: inline-flex; align-items: center; gap: 4px;
padding: 2px 10px; border-radius: 10px; font-size: 12px; font-weight: 500;
}
.status-active { color: var(--success); background: rgba(52,211,153,0.1); }
.status-disabled { color: var(--danger); background: rgba(248,113,113,0.1); }
.mono { font-family: 'JetBrains Mono', monospace; font-size: 12px; }
.action-btns { display: flex; gap: 6px; }
.btn-sm {
padding: 4px 10px; border-radius: 4px; font-size: 12px;
cursor: pointer; transition: all 0.15s; border: 1px solid var(--border);
background: transparent;
}
.btn-edit { color: var(--primary); border-color: rgba(0,184,230,0.3); }
.btn-edit:hover { background: var(--primary-dim); }
.btn-disable { color: var(--danger); border-color: rgba(248,113,113,0.3); }
.btn-disable:hover { background: rgba(248,113,113,0.1); }
.btn-enable { color: var(--success); border-color: rgba(52,211,153,0.3); }
.btn-enable:hover { background: rgba(52,211,153,0.1); }
/* Pagination */
.pagination {
display: flex; align-items: center; justify-content: space-between;
padding: 16px 20px;
}
.pagination-info { font-size: 13px; color: var(--text-2); }
.pagination-btns { display: flex; gap: 4px; }
.page-btn {
width: 32px; height: 32px; display: flex; align-items: center; justify-content: center;
border-radius: 6px; font-size: 13px; cursor: pointer; transition: all 0.15s;
border: 1px solid var(--border); background: transparent; color: var(--text-2);
font-family: 'JetBrains Mono', monospace;
}
.page-btn:hover { background: var(--hover); color: var(--text-1); }
.page-btn.active { background: var(--primary); color: #fff; border-color: var(--primary); }
.page-btn:disabled { opacity: 0.3; cursor: not-allowed; }
/* Drawer (for user detail) */
.drawer-overlay {
position: fixed; inset: 0; background: rgba(0,0,0,0.5);
z-index: 100; display: none; opacity: 0;
transition: opacity 0.3s;
}
.drawer-overlay.open { display: block; opacity: 1; }
.drawer {
position: fixed; top: 0; right: -420px; width: 420px; height: 100vh;
background: var(--bg-card); border-left: 1px solid var(--border);
z-index: 101; transition: right 0.3s cubic-bezier(0.16, 1, 0.3, 1);
display: flex; flex-direction: column;
}
.drawer.open { right: 0; }
.drawer-header {
display: flex; align-items: center; justify-content: space-between;
padding: 20px 24px; border-bottom: 1px solid var(--border);
}
.drawer-header h3 { font-size: 16px; font-weight: 600; }
.drawer-close {
width: 28px; height: 28px; border-radius: 6px; border: none;
background: transparent; color: var(--text-2); cursor: pointer;
display: flex; align-items: center; justify-content: center;
transition: all 0.15s;
}
.drawer-close:hover { background: var(--hover); color: var(--text-1); }
.drawer-body { flex: 1; padding: 24px; overflow-y: auto; }
.detail-row { display: flex; justify-content: space-between; padding: 10px 0; border-bottom: 1px solid rgba(255,255,255,0.03); }
.detail-label { font-size: 13px; color: var(--text-2); }
.detail-value { font-size: 13px; color: var(--text-1); }
/* Modal */
.modal-overlay {
position: fixed; inset: 0; background: rgba(0,0,0,0.6);
z-index: 200; display: none; align-items: center; justify-content: center;
}
.modal-overlay.open { display: flex; }
.modal {
background: var(--bg-card); border: 1px solid var(--border);
border-radius: 12px; width: 400px; padding: 28px;
}
.modal h3 { font-size: 16px; font-weight: 600; margin-bottom: 20px; }
.modal-field { margin-bottom: 16px; }
.modal-field label { display: block; font-size: 13px; color: var(--text-2); margin-bottom: 6px; }
.modal-input {
width: 100%; height: 40px; padding: 0 12px;
background: rgba(255,255,255,0.04); border: 1px solid var(--border);
border-radius: 8px; color: var(--text-1); font-size: 14px;
font-family: 'JetBrains Mono', monospace; outline: none;
}
.modal-input:focus { border-color: var(--primary); }
.modal-actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 24px; }
.modal-btn {
padding: 8px 20px; border-radius: 8px; font-size: 13px;
cursor: pointer; transition: all 0.15s; border: none; font-weight: 500;
}
.modal-btn-cancel { background: transparent; border: 1px solid var(--border); color: var(--text-2); }
.modal-btn-cancel:hover { background: var(--hover); }
.modal-btn-save { background: var(--primary); color: #fff; }
.modal-btn-save:hover { opacity: 0.9; }
::-webkit-scrollbar { width: 4px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }
@keyframes fadeUp { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } }
.animate-in { animation: fadeUp 0.4s ease-out forwards; opacity: 0; }
</style>
</head>
<body>
<div class="layout">
<!-- Sidebar -->
<aside class="sidebar">
<div class="sidebar-logo">
<svg width="24" height="24" viewBox="0 0 28 28" fill="none"><path d="M4 8L14 2L24 8V20L14 26L4 20V8Z" fill="#00b8e6" opacity="0.9"/><path d="M14 2L24 8L14 14L4 8L14 2Z" fill="#33ccf0"/></svg>
<span>Jimeng Admin</span>
</div>
<nav class="sidebar-nav">
<a href="admin-dashboard.html" class="nav-item">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/><rect x="3" y="14" width="7" height="7" rx="1.5"/><rect x="14" y="14" width="7" height="7" rx="1.5"/></svg>
仪表盘
</a>
<a href="admin-users.html" class="nav-item active">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 00-3-3.87"/><path d="M16 3.13a4 4 0 010 7.75"/></svg>
用户管理
</a>
<a href="admin-records.html" class="nav-item">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>
消费记录
</a>
<a href="admin-settings.html" class="nav-item">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83-2.83l.06-.06A1.65 1.65 0 004.68 15a1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 012.83-2.83l.06.06A1.65 1.65 0 009 4.68a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 2.83l-.06.06A1.65 1.65 0 0019.4 9a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z"/></svg>
系统设置
</a>
</nav>
<div class="sidebar-footer">
<a href="video-generation.html">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>
返回首页
</a>
</div>
</aside>
<!-- Main -->
<div class="main">
<header class="topbar">
<h1>用户管理</h1>
<div class="topbar-right">
<span class="admin-badge">Admin</span>
<button class="topbar-btn">退出</button>
</div>
</header>
<div class="content animate-in">
<!-- Search & Filter -->
<div class="search-bar">
<div class="search-wrap">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" class="search-input" placeholder="搜索用户名 / 邮箱...">
</div>
<select class="filter-select">
<option>全部状态</option>
<option>已启用</option>
<option>已禁用</option>
</select>
<button class="refresh-btn">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 11-2.12-9.36L23 10"/></svg>
刷新
</button>
</div>
<!-- Table -->
<div class="table-card">
<table>
<thead>
<tr>
<th>用户名 <span class="sort-icon">&#9650;&#9660;</span></th>
<th>邮箱</th>
<th>注册时间 <span class="sort-icon">&#9650;&#9660;</span></th>
<th>状态</th>
<th>日限额 (秒)</th>
<th>月限额 (秒)</th>
<th>今日消费</th>
<th>本月消费</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td class="username-cell" onclick="openDrawer()">zhang_wei</td>
<td style="color:var(--text-2)">zhangwei@test.com</td>
<td style="color:var(--text-2)">2026-03-01</td>
<td><span class="status-badge status-active">启用</span></td>
<td class="mono">600</td>
<td class="mono">6,000</td>
<td class="mono">123s</td>
<td class="mono">2,345s</td>
<td>
<div class="action-btns">
<button class="btn-sm btn-edit" onclick="openModal()">编辑配额</button>
<button class="btn-sm btn-disable">禁用</button>
</div>
</td>
</tr>
<tr>
<td class="username-cell">li_ming</td>
<td style="color:var(--text-2)">liming@test.com</td>
<td style="color:var(--text-2)">2026-03-02</td>
<td><span class="status-badge status-active">启用</span></td>
<td class="mono">600</td>
<td class="mono">6,000</td>
<td class="mono">89s</td>
<td class="mono">1,890s</td>
<td>
<div class="action-btns">
<button class="btn-sm btn-edit">编辑配额</button>
<button class="btn-sm btn-disable">禁用</button>
</div>
</td>
</tr>
<tr>
<td class="username-cell">wang_fang</td>
<td style="color:var(--text-2)">wangfang@test.com</td>
<td style="color:var(--text-2)">2026-03-03</td>
<td><span class="status-badge status-disabled">禁用</span></td>
<td class="mono">300</td>
<td class="mono">3,000</td>
<td class="mono">0s</td>
<td class="mono">1,560s</td>
<td>
<div class="action-btns">
<button class="btn-sm btn-edit">编辑配额</button>
<button class="btn-sm btn-enable">启用</button>
</div>
</td>
</tr>
<tr>
<td class="username-cell">chen_jie</td>
<td style="color:var(--text-2)">chenjie@mail.com</td>
<td style="color:var(--text-2)">2026-03-04</td>
<td><span class="status-badge status-active">启用</span></td>
<td class="mono">600</td>
<td class="mono">6,000</td>
<td class="mono">210s</td>
<td class="mono">1,350s</td>
<td>
<div class="action-btns">
<button class="btn-sm btn-edit">编辑配额</button>
<button class="btn-sm btn-disable">禁用</button>
</div>
</td>
</tr>
<tr>
<td class="username-cell">liu_yang</td>
<td style="color:var(--text-2)">liuyang@mail.com</td>
<td style="color:var(--text-2)">2026-03-05</td>
<td><span class="status-badge status-active">启用</span></td>
<td class="mono">600</td>
<td class="mono">6,000</td>
<td class="mono">45s</td>
<td class="mono">1,140s</td>
<td>
<div class="action-btns">
<button class="btn-sm btn-edit">编辑配额</button>
<button class="btn-sm btn-disable">禁用</button>
</div>
</td>
</tr>
<tr>
<td class="username-cell">zhao_lei</td>
<td style="color:var(--text-2)">zhaolei@test.com</td>
<td style="color:var(--text-2)">2026-03-06</td>
<td><span class="status-badge status-active">启用</span></td>
<td class="mono">600</td>
<td class="mono">6,000</td>
<td class="mono">67s</td>
<td class="mono">980s</td>
<td>
<div class="action-btns">
<button class="btn-sm btn-edit">编辑配额</button>
<button class="btn-sm btn-disable">禁用</button>
</div>
</td>
</tr>
<tr>
<td class="username-cell">huang_mei</td>
<td style="color:var(--text-2)">huangmei@test.com</td>
<td style="color:var(--text-2)">2026-03-07</td>
<td><span class="status-badge status-active">启用</span></td>
<td class="mono">600</td>
<td class="mono">6,000</td>
<td class="mono">30s</td>
<td class="mono">820s</td>
<td>
<div class="action-btns">
<button class="btn-sm btn-edit">编辑配额</button>
<button class="btn-sm btn-disable">禁用</button>
</div>
</td>
</tr>
</tbody>
</table>
<div class="pagination">
<span class="pagination-info">共 56 条记录,第 1/3 页</span>
<div class="pagination-btns">
<button class="page-btn" disabled>&lsaquo;</button>
<button class="page-btn active">1</button>
<button class="page-btn">2</button>
<button class="page-btn">3</button>
<button class="page-btn">&rsaquo;</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- User Detail Drawer -->
<div class="drawer-overlay" id="drawerOverlay" onclick="closeDrawer()"></div>
<div class="drawer" id="drawer">
<div class="drawer-header">
<h3>用户详情 — zhang_wei</h3>
<button class="drawer-close" onclick="closeDrawer()">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
</button>
</div>
<div class="drawer-body">
<div class="detail-row"><span class="detail-label">用户名</span><span class="detail-value">zhang_wei</span></div>
<div class="detail-row"><span class="detail-label">邮箱</span><span class="detail-value">zhangwei@test.com</span></div>
<div class="detail-row"><span class="detail-label">注册时间</span><span class="detail-value">2026-03-01 10:23:45</span></div>
<div class="detail-row"><span class="detail-label">状态</span><span class="detail-value"><span class="status-badge status-active">启用</span></span></div>
<div class="detail-row"><span class="detail-label">日限额</span><span class="detail-value mono">600s</span></div>
<div class="detail-row"><span class="detail-label">月限额</span><span class="detail-value mono">6,000s</span></div>
<div class="detail-row"><span class="detail-label">今日消费</span><span class="detail-value mono">123s</span></div>
<div class="detail-row"><span class="detail-label">本月消费</span><span class="detail-value mono">2,345s</span></div>
<h4 style="font-size:14px;font-weight:600;margin:24px 0 12px;color:var(--text-1)">近期消费记录</h4>
<div style="display:flex;flex-direction:column;gap:8px">
<div style="padding:10px 12px;background:rgba(255,255,255,0.02);border-radius:8px;display:flex;justify-content:space-between;align-items:center">
<div>
<div style="font-size:12px;color:var(--text-2)">3/12 14:30</div>
<div style="font-size:13px;margin-top:2px">一只猫在花园里追蝴蝶...</div>
</div>
<div class="mono" style="color:var(--primary)">15s</div>
</div>
<div style="padding:10px 12px;background:rgba(255,255,255,0.02);border-radius:8px;display:flex;justify-content:space-between;align-items:center">
<div>
<div style="font-size:12px;color:var(--text-2)">3/12 13:15</div>
<div style="font-size:13px;margin-top:2px">日落海边散步的情侣...</div>
</div>
<div class="mono" style="color:var(--primary)">10s</div>
</div>
<div style="padding:10px 12px;background:rgba(255,255,255,0.02);border-radius:8px;display:flex;justify-content:space-between;align-items:center">
<div>
<div style="font-size:12px;color:var(--text-2)">3/12 10:42</div>
<div style="font-size:13px;margin-top:2px">城市夜景延时摄影...</div>
</div>
<div class="mono" style="color:var(--primary)">5s</div>
</div>
</div>
</div>
</div>
<!-- Edit Quota Modal -->
<div class="modal-overlay" id="modalOverlay">
<div class="modal">
<h3>编辑配额 — zhang_wei</h3>
<div class="modal-field">
<label>每日限额 (秒)</label>
<input type="number" class="modal-input" value="600">
</div>
<div class="modal-field">
<label>每月限额 (秒)</label>
<input type="number" class="modal-input" value="6000">
</div>
<div class="modal-actions">
<button class="modal-btn modal-btn-cancel" onclick="closeModal()">取消</button>
<button class="modal-btn modal-btn-save" onclick="closeModal()">保存</button>
</div>
</div>
</div>
<script>
function openDrawer() {
document.getElementById('drawerOverlay').classList.add('open');
document.getElementById('drawer').classList.add('open');
}
function closeDrawer() {
document.getElementById('drawerOverlay').classList.remove('open');
document.getElementById('drawer').classList.remove('open');
}
function openModal() {
document.getElementById('modalOverlay').classList.add('open');
}
function closeModal() {
document.getElementById('modalOverlay').classList.remove('open');
}
</script>
</body>
</html>