<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Windows 11风格垂直备忘录</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', system-ui, sans-serif;
}
body {
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
color: #f0f0f0;
min-height: 100vh;
padding: 20px;
display: flex;
flex-direction: column;
overflow-x: hidden;
}
.header {
text-align: center;
margin-bottom: 30px;
padding: 20px;
}
.header h1 {
font-size: 2.8rem;
font-weight: 300;
letter-spacing: 1px;
background: linear-gradient(90deg, #00c6ff, #0072ff);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
margin-bottom: 10px;
}
.header p {
color: #a0a0c0;
font-size: 1.1rem;
}
.container {
display: flex;
max-width: 1200px;
margin: 0 auto;
width: 100%;
gap: 30px;
}
/* 左侧备忘录列表 */
.memos-container {
background: rgba(30, 30, 46, 0.7);
border-radius: 12px;
padding: 25px;
width: 300px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
display: flex;
flex-direction: column;
max-height: 80vh;
}
.memos-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.memos-header h2 {
font-weight: 400;
font-size: 1.5rem;
color: #e0e0ff;
}
.add-btn {
background: linear-gradient(135deg, #0078d7, #0063b1);
color: white;
border: none;
border-radius: 8px;
padding: 10px 20px;
font-size: 1rem;
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.add-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 120, 215, 0.4);
}
.memos-list {
display: flex;
flex-direction: column;
gap: 12px;
overflow-y: auto;
padding: 10px 5px;
flex-grow: 1;
scrollbar-width: thin;
scrollbar-color: #0078d7 rgba(30, 30, 46, 0.7);
}
.memos-list::-webkit-scrollbar {
width: 8px;
}
.memos-list::-webkit-scrollbar-track {
background: rgba(30, 30, 46, 0.5);
border-radius: 4px;
}
.memos-list::-webkit-scrollbar-thumb {
background: #0078d7;
border-radius: 4px;
}
.memo-btn {
background: rgba(40, 40, 60, 0.8);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
padding: 18px 20px;
cursor: grab;
transition: all 0.4s cubic-bezier(0.2, 0.8, 0.4, 1);
display: flex;
flex-direction: column;
gap: 8px;
position: relative;
transform-origin: top center;
min-height: 60px;
overflow: hidden;
}
.memo-btn:hover {
background: rgba(50, 50, 80, 0.9);
transform: scaleY(1.1);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
z-index: 2;
}
.memo-btn.dragging {
opacity: 0.7;
transform: scale(0.98);
cursor: grabbing;
}
.memo-btn.active {
border-color: #0078d7;
background: rgba(25, 60, 100, 0.5);
}
.memo-title {
font-size: 1.1rem;
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.memo-preview {
font-size: 0.9rem;
color: #a0a0c0;
height: 1.5em;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
}
.memo-date {
font-size: 0.75rem;
color: #707090;
margin-top: 5px;
}
/* 空状态样式 */
.empty-state {
text-align: center;
padding: 40px 20px;
color: #707090;
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.empty-state i {
font-size: 3rem;
margin-bottom: 20px;
opacity: 0.5;
}
.empty-state p {
font-size: 1.1rem;
}
/* 右侧详情区域 */
.detail-container {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
background: rgba(30, 30, 46, 0.5);
border-radius: 12px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
border: 1px solid rgba(255, 255, 255, 0.1);
backdrop-filter: blur(8px);
}
.welcome-message {
text-align: center;
padding: 40px;
max-width: 500px;
}
.welcome-message i {
font-size: 5rem;
background: linear-gradient(135deg, #00c6ff, #0072ff);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
margin-bottom: 20px;
}
.welcome-message h2 {
font-size: 2rem;
font-weight: 300;
margin-bottom: 20px;
color: #e0e0ff;
}
.welcome-message p {
font-size: 1.1rem;
line-height: 1.6;
color: #a0a0c0;
margin-bottom: 30px;
}
/* 弹窗样式 */
.editor-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.7);
z-index: 1000;
opacity: 0;
pointer-events: none;
transition: opacity 0.3s ease;
}
.editor-modal.active {
opacity: 1;
pointer-events: all;
}
/* Windows 11风格编辑窗口 */
.editor-window {
background: rgba(30, 30, 46, 0.9);
border-radius: 12px;
overflow: hidden;
box-shadow: 0 16px 48px rgba(0, 0, 0, 0.5);
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.15);
width: 90%;
max-width: 600px;
transform: scale(0.9);
transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.editor-modal.active .editor-window {
transform: scale(1);
}
.window-header {
background: rgba(25, 25, 40, 0.95);
padding: 18px 24px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
.window-title {
display: flex;
align-items: center;
gap: 10px;
font-size: 1.2rem;
}
.window-title i {
color: #0078d7;
font-size: 1.3rem;
}
.window-controls {
display: flex;
gap: 12px;
}
.window-btn {
width: 34px;
height: 34px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s ease;
background: rgba(255, 255, 255, 0.1);
border: none;
color: #ccc;
}
.window-btn:hover {
background: rgba(255, 255, 255, 0.2);
}
.close-btn:hover {
background: #e81123;
color: white;
}
.window-body {
padding: 30px;
}
.editor-content {
display: flex;
flex-direction: column;
gap: 20px;
}
.editor-input {
background: rgba(40, 40, 60, 0.5);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
padding: 16px 18px;
color: white;
font-size: 1.1rem;
outline: none;
transition: border 0.3s ease;
}
.editor-input:focus {
border-color: #0078d7;
}
.editor-input.title {
font-size: 1.6rem;
font-weight: 500;
}
.editor-input.content {
min-height: 280px;
resize: vertical;
}
.auto-save-indicator {
display: flex;
align-items: center;
gap: 8px;
color: #a0a0c0;
font-size: 0.9rem;
margin-top: 10px;
}
.auto-save-indicator.active {
color: #4caf50;
}
.auto-save-indicator i {
font-size: 0.8rem;
}
/* 响应式设计 */
@media (max-width: 900px) {
.container {
flex-direction: column;
max-width: 600px;
}
.memos-container {
width: 100%;
}
.detail-container {
min-height: 300px;
}
}
@media (max-width: 600px) {
.header h1 {
font-size: 2.2rem;
}
.editor-input.title {
font-size: 1.3rem;
}
.memos-container {
padding: 20px;
}
.memo-btn {
padding: 15px;
}
}
@media (max-width: 480px) {
.header h1 {
font-size: 1.8rem;
}
}
</style>
</head>
<body>
<div class="header">
<marquee><h1>Windows 11 垂直备忘录</h1></marquee>
<p>拖拽重新排序,悬停按钮有惊喜,弹窗编辑内容</p>
</div>
<div class="container">
<!-- 左侧备忘录按钮区域 -->
<div class="memos-container">
<div class="memos-header">
<h2>备忘录列表</h2>
<button class="add-btn" id="add-memo">
<i class="fas fa-plus"></i>
添加
</button>
</div>
<div class="memos-list" id="memos-list">
<!-- 备忘录按钮将通过JS动态添加 -->
<div class="empty-state" id="empty-state">
<i class="fas fa-sticky-note"></i>
<p>暂无备忘录,点击添加按钮创建</p>
</div>
</div>
</div>
<!-- 右侧详情区域 -->
<div class="detail-container">
<div class="welcome-message">
<i class="fas fa-file-alt"></i>
<h2>创建您的第一个备忘录</h2>
<p>点击左侧的"添加"按钮创建一个新的备忘录,或者从已有列表中选择进行编辑。所有内容都会自动保存,无需点击保存按钮。</p>
<p>尝试将鼠标悬停在备忘录按钮上,体验优雅的展开效果!</p>
</div>
</div>
</div>
<!-- 编辑弹窗 -->
<div class="editor-modal" id="editor-modal">
<div class="editor-window">
<div class="window-header">
<div class="window-title">
<i class="fas fa-edit"></i>
<span>编辑备忘录</span>
</div>
<div class="window-controls">
<button class="window-btn minimize-btn">
<i class="fas fa-minus"></i>
</button>
<button class="window-btn close-btn" id="close-editor">
<i class="fas fa-times"></i>
</button>
</div>
</div>
<div class="window-body">
<div class="editor-content">
<input type="text" class="editor-input title" placeholder="备忘录标题..." id="memo-title">
<textarea class="editor-input content" placeholder="在此输入内容..." id="memo-content"></textarea>
<div class="auto-save-indicator" id="save-indicator">
<i class="fas fa-save"></i>
<span>已保存更改</span>
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
// 初始化变量
let memos = JSON.parse(localStorage.getItem('win11-vertical-memos')) || [];
let activeMemoId = null;
let isDragging = false;
let dragSrcIndex = null;
let saveTimeout = null;
// DOM元素
const memosList = document.getElementById('memos-list');
const emptyState = document.getElementById('empty-state');
const addButton = document.getElementById('add-memo');
const memoTitle = document.getElementById('memo-title');
const memoContent = document.getElementById('memo-content');
const saveIndicator = document.getElementById('save-indicator');
const editorModal = document.getElementById('editor-modal');
const closeEditorBtn = document.getElementById('close-editor');
// 初始化应用
initApp();
// 初始化函数
function initApp() {
renderMemos();
// 添加事件监听器
setupEventListeners();
}
// 设置事件监听器
function setupEventListeners() {
// 添加新备忘录
addButton.addEventListener('click', addNewMemo);
// 编辑区域输入事件
memoTitle.addEventListener('input', handleMemoEdit);
memoContent.addEventListener('input', handleMemoEdit);
// 关闭编辑器
closeEditorBtn.addEventListener('click', closeEditor);
// 点击模态背景关闭编辑器
editorModal.addEventListener('click', (e) => {
if (e.target === editorModal) {
closeEditor();
}
});
// 键盘快捷键
document.addEventListener('keydown', (e) => {
// 按ESC关闭编辑器
if (e.key === 'Escape' && editorModal.classList.contains('active')) {
closeEditor();
}
// 按Ctrl+N添加新备忘录
if ((e.ctrlKey || e.metaKey) && e.key === 'n') {
e.preventDefault();
addNewMemo();
}
});
}
// 渲染备忘录列表
function renderMemos() {
memosList.innerHTML = '';
if (memos.length === 0) {
emptyState.style.display = 'flex';
return;
}
emptyState.style.display = 'none';
memos.forEach((memo, index) => {
const memoElement = document.createElement('div');
memoElement.className = `memo-btn ${activeMemoId === memo.id ? 'active' : ''}`;
memoElement.dataset.id = memo.id;
memoElement.dataset.index = index;
memoElement.draggable = true;
memoElement.innerHTML = `
<div class="memo-title">${memo.title || '新备忘录'}</div>
<div class="memo-preview">${memo.content.substring(0, 60) || '无内容...'}</div>
<div class="memo-date">${formatDate(memo.lastModified)}</div>
`;
// 点击事件
memoElement.addEventListener('click', () => {
openEditor(memo.id);
});
// 拖拽事件
memoElement.addEventListener('dragstart', handleDragStart);
memoElement.addEventListener('dragover', handleDragOver);
memoElement.addEventListener('dragenter', handleDragEnter);
memoElement.addEventListener('dragleave', handleDragLeave);
memoElement.addEventListener('drop', handleDrop);
memoElement.addEventListener('dragend', handleDragEnd);
memosList.appendChild(memoElement);
});
}
// 添加新备忘录
function addNewMemo() {
const newMemo = {
id: Date.now().toString(),
title: '新备忘录',
content: '',
created: new Date(),
lastModified: new Date()
};
memos.unshift(newMemo);
saveMemos();
renderMemos();
openEditor(newMemo.id);
}
// 打开编辑器
function openEditor(memoId) {
activeMemoId = memoId;
const memo = memos.find(m => m.id === memoId);
if (memo) {
memoTitle.value = memo.title;
memoContent.value = memo.content;
}
editorModal.classList.add('active');
document.body.style.overflow = 'hidden';
// 更新按钮激活状态
document.querySelectorAll('.memo-btn').forEach(btn => {
btn.classList.toggle('active', btn.dataset.id === memoId);
});
}
// 关闭编辑器
function closeEditor() {
editorModal.classList.remove('active');
document.body.style.overflow = '';
}
// 处理备忘录编辑
function handleMemoEdit() {
if (!activeMemoId) return;
// 清除之前的保存超时
if (saveTimeout) clearTimeout(saveTimeout);
const memoIndex = memos.findIndex(m => m.id === activeMemoId);
if (memoIndex !== -1) {
memos[memoIndex].title = memoTitle.value;
memos[memoIndex].content = memoContent.value;
// 显示保存指示器
saveIndicator.classList.add('active');
saveIndicator.innerHTML = '<i class="fas fa-sync fa-spin"></i> <span>保存中...</span>';
// 设置超时以展示保存效果
saveTimeout = setTimeout(() => {
memos[memoIndex].lastModified = new Date();
saveMemos();
renderMemos();
saveIndicator.innerHTML = '<i class="fas fa-save"></i> <span>已保存更改</span>';
// 移除活动状态
setTimeout(() => {
saveIndicator.classList.remove('active');
}, 1000);
}, 800);
}
}
// 保存备忘录到localStorage
function saveMemos() {
localStorage.setItem('win11-vertical-memos', JSON.stringify(memos));
}
// 格式化日期
function formatDate(date) {
const d = new Date(date);
return `${d.getFullYear()}-${(d.getMonth()+1).toString().padStart(2, '0')}-${d.getDate().toString().padStart(2, '0')} ${d.getHours().toString().padStart(2, '0')}:${d.getMinutes().toString().padStart(2, '0')}`;
}
// 拖拽功能实现
function handleDragStart(e) {
isDragging = true;
dragSrcIndex = parseInt(e.target.dataset.index);
e.target.classList.add('dragging');
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/html', e.target.outerHTML);
}
function handleDragOver(e) {
e.preventDefault();
e.dataTransfer.dropEffect = 'move';
return false;
}
function handleDragEnter(e) {
e.preventDefault();
if (e.target.classList.contains('memo-btn') && e.target !== document.querySelector('.dragging')) {
e.target.classList.add('drag-over');
}
}
function handleDragLeave(e) {
e.preventDefault();
if (e.target.classList.contains('memo-btn')) {
e.target.classList.remove('drag-over');
}
}
function handleDrop(e) {
e.preventDefault();
e.stopPropagation();
if (e.target.classList.contains('memo-btn') && isDragging) {
const dragDestIndex = parseInt(e.target.dataset.index);
if (dragSrcIndex !== dragDestIndex) {
// 重新排序数组
const draggedItem = memos[dragSrcIndex];
memos.splice(dragSrcIndex, 1);
memos.splice(dragDestIndex, 0, draggedItem);
saveMemos();
renderMemos();
}
}
document.querySelectorAll('.memo-btn').forEach(btn => {
btn.classList.remove('drag-over');
});
return false;
}
function handleDragEnd(e) {
isDragging = false;
document.querySelectorAll('.memo-btn').forEach(btn => {
btn.classList.remove('dragging');
btn.classList.remove('drag-over');
});
}
});
</script>
</body>
</html>
index.html
style.css
index.js