我正在创建仪表板,其中我为侧边栏创建了布局,并且侧边栏的不同页面具有不同的内容。
这是layout.ejs:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= pageTitle %></title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<%- css %>
<style>
:root {
--primary-bg: #ffffff;
--secondary-bg: #f3f4f6;
--dark-bg: #111827;
--sidebar-width: 280px;
--header-height: 70px;
--primary-color: #030a13;
--text-primary: #111827;
--text-secondary: #6b7280;
--border-color: #e5e7eb;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Inter', sans-serif;
}
body {
background-color: var(--secondary-bg);
}
.sidebar {
width: var(--sidebar-width);
background: var(--dark-bg);
height: 100vh;
position: fixed;
left: 0;
top: 0;
z-index: 1000;
transition: all 0.3s ease;
box-shadow: 4px 0 10px rgba(0, 0, 0, 0.1);
}
.sidebar-header {
height: var(--header-height);
display: flex;
align-items: center;
padding: 0 24px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.sidebar-brand {
color: white;
font-size: 24px;
font-weight: 700;
text-decoration: none;
letter-spacing: 0.5px;
}
.sidebar-menu {
padding: 24px 0;
}
.menu-title {
color: #6b7280;
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
padding: 0 24px;
margin: 20px 0 10px;
}
.menu-item {
padding: 0 12px;
margin: 4px 0;
}
.menu-link {
display: flex;
align-items: center;
padding: 12px;
color: #9ca3af;
text-decoration: none;
border-radius: 8px;
transition: all 0.2s ease;
}
.menu-link:hover {
background: rgba(255, 255, 255, 0.1);
color: white;
}
.menu-link.active {
background: var(--primary-color);
color: white;
}
.menu-icon {
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 12px;
}
.menu-text {
font-size: 14px;
font-weight: 500;
}
.main-content {
margin-left: var(--sidebar-width);
min-height: 100vh;
background: var(--secondary-bg);
padding: 24px;
}
.top-bar {
height: var(--header-height);
background: var(--primary-bg);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
margin: -24px -24px 24px;
border-bottom: 1px solid var(--border-color);
}
.page-title {
font-size: 24px;
font-weight: 600;
color: var(--text-primary);
}
.user-menu {
display: flex;
align-items: center;
gap: 16px;
}
.notifications {
position: relative;
cursor: pointer;
}
.notification-icon {
color: var(--text-primary);
font-size: 30px;
}
.notification-badge {
position: absolute;
top: -6px;
right: -6px;
background: #ef4444;
color: white;
font-size: 10px;
font-weight: 600;
padding: 1px 5px;
border-radius: 10px;
border: 2px solid var(--primary-bg);
}
.user-profile {
display: flex;
align-items: center;
gap: 12px;
cursor: pointer;
padding: 8px;
border-radius: 8px;
transition: all 0.2s ease;
}
.user-profile:hover {
background: var(--secondary-bg);
}
.user-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
object-fit: cover;
}
.user-info {
line-height: 1.3;
}
.user-name {
font-size: 14px;
font-weight: 600;
color: var(--text-primary);
}
.user-role {
font-size: 12px;
color: var(--text-secondary);
}
/* Content wrapper */
.content-wrapper {
max-width: 1600px;
margin: 0 auto;
}
/* Responsive */
@media (max-width: 1024px) {
.sidebar {
transform: translateX(-100%);
}
.main-content {
margin-left: 0;
}
.sidebar.active {
transform: translateX(0);
}
}
.user-profile {
position: relative;
display: flex;
align-items: center;
gap: 12px;
cursor: pointer;
padding: 8px;
border-radius: 8px;
transition: all 0.2s ease;
}
.user-profile:hover {
background: var(--secondary-bg);
}
.user-profile:hover .dropdown-menu {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.dropdown-menu {
position: absolute;
top: 100%;
right: 0;
background: white;
border: 1px solid var(--border-color);
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
border-radius: 8px;
padding: 8px 0;
opacity: 0;
visibility: hidden;
transform: translateY(-10px);
transition: all 0.2s ease-in-out;
z-index: 10;
}
.dropdown-item {
display: block;
padding: 10px 16px;
color: var(--text-primary);
text-decoration: none;
font-size: 14px;
transition: background 0.2s ease, color 0.2s ease;
}
.dropdown-item:hover {
background: var(--primary-color);
color: white;
}
.mobile-menu-toggle {
display: none;
position: fixed;
top: 15px;
left: 10px;
z-index: 1001;
/* background: var(--primary-color); */
color: black;
border: none;
border-radius: 8px;
padding: 10px;
cursor: pointer;
transition: all 0.3s ease;
}
.mobile-menu-toggle i {
font-size: 24px;
}
/* Update responsive styles */
@media (max-width: 1024px) {
.mobile-menu-toggle {
display: block;
}
.sidebar {
transform: translateX(-100%);
}
.main-content {
margin-left: 0;
}
.sidebar.active {
transform: translateX(0);
}
/* Add overlay when sidebar is active */
.sidebar-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 999;
}
.sidebar-overlay.active {
display: block;
}
/* Adjust top bar padding for mobile */
.top-bar {
padding-left: 60px;
}
}
</style>
</head>
<body>
<div class="admin-container">
<!-- Enhanced Sidebar -->
<button class="mobile-menu-toggle">
<i class="fas fa-bars-staggered"></i>
</button>
<!-- Add overlay div -->
<div class="sidebar-overlay"></div>
<aside class="sidebar">
<div class="sidebar-header">
<a href="/admin" class="sidebar-brand">
<i class="fas fa-court fa-fw"></i>
CourtCraft
</a>
</div>
<nav class="sidebar-menu">
<div class="menu-title">Main Menu</div>
<div class="menu-item">
<a href="/admin" class="menu-link <%= path === '/admin' ? 'active' : '' %>">
<span class="menu-icon">
<i class="fas fa-chart-line"></i>
</span>
<span class="menu-text">Dashboard</span>
</a>
</div>
<div class="menu-item">
<a href="/admin/vendor-requests" class="menu-link <%= path === '/admin/vendor-requests' ? 'active' : '' %>">
<span class="menu-icon">
<i class="fas fa-user-plus"></i>
</span>
<span class="menu-text">Vendor Requests</span>
</a>
</div>
<div class="menu-item">
<a href="/admin/venues" class="menu-link <%= path === '/admin/venues' ? 'active' : '' %>">
<span class="menu-icon">
<i class="fas fa-building"></i>
</span>
<span class="menu-text">All Venues</span>
</a>
</div>
<div class="menu-title">System</div>
<div class="menu-item">
<a href="/admin/notifications" class="menu-link <%= path === '/admin/notifications' ? 'active' : '' %>">
<span class="menu-icon">
<i class="fas fa-bell"></i>
</span>
<span class="menu-text">Notifications</span>
</a>
</div>
<div class="menu-item">
<a href="/admin/logout" class="menu-link">
<span class="menu-icon">
<i class="fas fa-sign-out-alt"></i>
</span>
<span class="menu-text">Logout</span>
</a>
</div>
</nav>
</aside>
<!-- Main Content -->
<main class="main-content">
<div class="top-bar">
<h1 class="page-title"><%= pageTitle %></h1>
<div class="user-menu">
<div class="notifications">
<i class="fas fa-bell notification-icon"></i>
<span class="notification-badge">3</span>
</div>
<div class="user-profile">
<img src="https://ui-avatars.com/api/?name=Admin&background=0D8ABC&color=fff" alt="Admin" class="user-avatar">
<div class="user-info">
<div class="user-name">Admin</div>
<div class="user-role">Super Admin</div>
</div>
<div class="dropdown-menu">
<a href="/admin/change-password" class="dropdown-item">Change Password</a>
<a href="/admin/logout" class="dropdown-item">Logout</a>
</div>
</div>
</div>
</div>
<div class="content-wrapper">
<%- body %>
</div>
</main>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const menuToggle = document.querySelector('.mobile-menu-toggle');
const sidebar = document.querySelector('.sidebar');
const overlay = document.querySelector('.sidebar-overlay');
function toggleSidebar() {
sidebar.classList.toggle('active');
overlay.classList.toggle('active');
}
menuToggle.addEventListener('click', toggleSidebar);
overlay.addEventListener('click', toggleSidebar);
// Close sidebar when clicking outside on mobile
document.addEventListener('click', (e) => {
if (window.innerWidth <= 1024) {
if (!sidebar.contains(e.target) &&
!menuToggle.contains(e.target) &&
sidebar.classList.contains('active')) {
toggleSidebar();
}
}
});
// Handle window resize
window.addEventListener('resize', () => {
if (window.innerWidth > 1024) {
sidebar.classList.remove('active');
overlay.classList.remove('active');
}
});
});
</script>
</body>
</html>
我已经在仪表板页面上创建了工作正常的页面,并为供应商请求创建了一页,但总是给我错误,我已经验证了很多次以关闭页面
这是供应商请求页面内容:
<%- include('layouts/admin-layout', {
css: `<link rel="stylesheet" href="/css/vendor-requests.css">`,
body: `
<div class="vendor-requests-container">
<div class="header">
<h1>Vendor Requests</h1>
<p>Manage incoming vendor requests seamlessly</p>
</div>
<div id="vendor-requests-list" class="vendor-tiles">
<%- if (vendorRequests && vendorRequests.length > 0) { %>
<% vendorRequests.forEach(function(request) { %>
<div class="vendor-tile">
<img src="/images/vendor-placeholder.png" alt="Vendor Image" class="vendor-img" />
<div class="vendor-info">
<h2 class="vendor-name"><%= request.name %></h2>
<p class="vendor-email"><%= request.email %></p>
<p class="request-date">Request Sent: <%= request.requestDate %></p>
</div>
<div class="vendor-actions">
<button class="action-dots" data-id="<%= request.id %>">⋮</button>
<div class="dropdown-menu" id="dropdown-<%= request.id %>">
<button class="dropdown-item btn-details" data-id="<%= request.id %>">View Details</button>
<button class="dropdown-item btn-accept" data-id="<%= request.id %>">Accept</button>
<button class="dropdown-item btn-reject" data-id="<%= request.id %>">Reject</button>
</div>
</div>
</div>
<% }) %>
<% } else { %>
<p class="no-requests">No vendor requests available</p>
<% } %>
</div>
</div>
<script>
document.addEventListener('click', function(e) {
if (e.target.classList.contains('action-dots')) {
const dropdownId = e.target.dataset.id;
document.querySelectorAll('.dropdown-menu').forEach(menu => {
if (menu.id === 'dropdown-' + dropdownId) {
menu.classList.toggle('show');
} else {
menu.classList.remove('show');
}
});
} else {
document.querySelectorAll('.dropdown-menu').forEach(menu => menu.classList.remove('show'));
}
});
</script>
`
}) %>
现在如果我删除此代码部分:
<div id="vendor-requests-list" class="vendor-tiles">
<%- if (vendorRequests && vendorRequests.length > 0) { %>
<% vendorRequests.forEach(function(request) { %>
<div class="vendor-tile">
<img src="/images/vendor-placeholder.png" alt="Vendor Image" class="vendor-img" />
<div class="vendor-info">
<h2 class="vendor-name"><%= request.name %></h2>
<p class="vendor-email"><%= request.email %></p>
<p class="request-date">Request Sent: <%= request.requestDate %></p>
</div>
<div class="vendor-actions">
<button class="action-dots" data-id="<%= request.id %>">⋮</button>
<div class="dropdown-menu" id="dropdown-<%= request.id %>">
<button class="dropdown-item btn-details" data-id="<%= request.id %>">View Details</button>
<button class="dropdown-item btn-accept" data-id="<%= request.id %>">Accept</button>
<button class="dropdown-item btn-reject" data-id="<%= request.id %>">Reject</button>
</div>
</div>
</div>
<% }) %>
<% } else { %>
<p class="no-requests">No vendor requests available</p>
<% } %>
</div>
视图渲染得很好,但这样总是给我错误:
Could not find matching close tag for "<%-".
任何帮助解决此问题的帮助都将不胜感激
您的供应商请求页面包含一个 EJS 标签
<%-
,该标签出现在另一个 EJS 标签内的字符串中。 EJS 解析器无法处理此问题,但您可以通过在字符串中将百分号写为 \x25
来规避它。
例如,而不是
<%- "<%- %>" %>
写
<%- "<\x25- \x25>" %>