|
|
|
|
@ -411,7 +411,7 @@
|
|
|
|
|
<!-- 用户信息区域 -->
|
|
|
|
|
<div class="p-4 border-t border-gray-700 flex items-center justify-between">
|
|
|
|
|
<div class="flex items-center gap-3">
|
|
|
|
|
<img src=".\tubiao.png" alt="用户头像" class="w-8 h-8 rounded-full"> <!-- 替换绝对路径 -->
|
|
|
|
|
<img src=".\touxiang.png" alt="用户头像" class="w-8 h-8 rounded-full"> <!-- 替换绝对路径 -->
|
|
|
|
|
<div>
|
|
|
|
|
<div class="text-sm font-medium" id="user-name">昵称</div>
|
|
|
|
|
<div class="text-xs text-text-muted" id="user-grade-major">年级 | 专业</div>
|
|
|
|
|
@ -432,23 +432,6 @@
|
|
|
|
|
<div class="bg-white p-4 border-b border-gray-200 flex items-center justify-between">
|
|
|
|
|
<h1 class="text-xl font-semibold">笔记管理</h1>
|
|
|
|
|
<div class="flex items-center gap-3">
|
|
|
|
|
<div class="relative mr-3">
|
|
|
|
|
<select id="categoryFilter" class="input-field pl-3 pr-8 w-48 border border-gray-300 rounded-md py-2 focus:outline-none focus:ring-2 focus:ring-primary/50 appearance-none bg-white">
|
|
|
|
|
<!-- 选项将通过JS动态生成 -->
|
|
|
|
|
</select>
|
|
|
|
|
<i class="fa fa-chevron-down absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 pointer-events-none"></i>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="relative">
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
id="noteSearch"
|
|
|
|
|
placeholder="搜索笔记..."
|
|
|
|
|
class="input-field pl-9 w-64 border border-gray-300 rounded-md py-2 px-3 focus:outline-none focus:ring-2 focus:ring-primary/50"
|
|
|
|
|
>
|
|
|
|
|
</div>
|
|
|
|
|
<button class="btn-action btn-primary bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition-colors" id="newNoteBtn">
|
|
|
|
|
<i class="fa fa-plus mr-1"></i> 新建笔记
|
|
|
|
|
</button>
|
|
|
|
|
<button class="btn-action btn-secondary border border-gray-300 px-4 py-2 rounded-md hover:bg-gray-100 transition-colors" id="importNoteBtn">
|
|
|
|
|
<i class="fa fa-upload mr-1"></i> 导入
|
|
|
|
|
</button>
|
|
|
|
|
@ -459,17 +442,9 @@
|
|
|
|
|
<div class="p-6 border-b border-gray-200 bg-white">
|
|
|
|
|
<h2 class="text-lg font-semibold mb-4">已上传文件</h2>
|
|
|
|
|
<div id="documents-container" class="border rounded-lg p-4 bg-gray-50">
|
|
|
|
|
<!-- 文件列表将通过JS动态生成 -->
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 笔记列表区域(删除了"我的笔记"标题) -->
|
|
|
|
|
<div class="p-6 flex-1">
|
|
|
|
|
<!-- 删除了原来的 <h2 class="text-lg font-semibold mb-4">我的笔记</h2> -->
|
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6" id="notes-container">
|
|
|
|
|
<!-- 笔记卡片将通过JS动态生成 -->
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -484,7 +459,7 @@
|
|
|
|
|
<!-- 系统消息示例 -->
|
|
|
|
|
<div class="max-w-[80%]">
|
|
|
|
|
<div class="flex items-center gap-2 mb-1">
|
|
|
|
|
<img src=".\tubiao.png" alt="AI" class="w-6 h-6 rounded-full">
|
|
|
|
|
<img src=".\touxiang.png" alt="AI" class="w-6 h-6 rounded-full">
|
|
|
|
|
<span class="text-sm font-medium text-gray-700">知记助手--KnowNo</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="bg-gray-100 p-3 rounded-lg rounded-tl-none">
|
|
|
|
|
@ -613,224 +588,6 @@
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 新建/编辑笔记模态框 -->
|
|
|
|
|
<div id="newNoteModal" class="fixed inset-0 bg-black/50 flex items-center justify-center p-4 z-50 hidden transition-opacity duration-300">
|
|
|
|
|
<div class="w-full max-w-2xl bg-white rounded-xl p-8 shadow-lg transform transition-transform duration-300 scale-95">
|
|
|
|
|
<div class="flex justify-between items-center mb-6">
|
|
|
|
|
<h2 class="text-xl font-semibold text-dark" id="noteModalTitle">新建笔记</h2>
|
|
|
|
|
<button id="closeNewNoteModal" class="text-gray-400 hover:text-gray-600">
|
|
|
|
|
<i class="fa fa-times text-xl"></i>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<form id="newNoteForm" class="space-y-5">
|
|
|
|
|
<input type="hidden" id="noteId"> <!-- 用于编辑模式存储笔记ID -->
|
|
|
|
|
|
|
|
|
|
<!-- 笔记标题 -->
|
|
|
|
|
<div>
|
|
|
|
|
<label for="noteTitle" class="block text-sm font-medium text-gray-700 mb-1">笔记标题</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
id="noteTitle"
|
|
|
|
|
class="login-input"
|
|
|
|
|
placeholder="请输入笔记标题"
|
|
|
|
|
required
|
|
|
|
|
>
|
|
|
|
|
<p id="noteTitleError" class="text-red-500 text-xs mt-1 hidden">请输入笔记标题</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 笔记分类(修正后) -->
|
|
|
|
|
<div>
|
|
|
|
|
<label for="noteCategory" class="block text-sm font-medium text-gray-700 mb-1">笔记分类</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
id="noteCategory"
|
|
|
|
|
class="login-input"
|
|
|
|
|
placeholder="请输入笔记分类(例如:高等数学、计算机网络)"
|
|
|
|
|
>
|
|
|
|
|
<p id="noteCategoryError" class="text-red-500 text-xs mt-1 hidden">请输入笔记分类</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 笔记内容(富文本编辑器) -->
|
|
|
|
|
<div>
|
|
|
|
|
<label for="noteContent" class="block text-sm font-medium text-gray-700 mb-1">笔记内容</label>
|
|
|
|
|
<div class="border border-gray-200 rounded-md mb-2 overflow-hidden">
|
|
|
|
|
<!-- 富文本工具栏 -->
|
|
|
|
|
<div class="flex items-center gap-1 p-2 border-b border-gray-200 bg-gray-50 flex-wrap">
|
|
|
|
|
<button type="button" class="toolbar-btn" data-command="bold" title="加粗 (Ctrl+B)">
|
|
|
|
|
<i class="fa fa-bold"></i>
|
|
|
|
|
</button>
|
|
|
|
|
<button type="button" class="toolbar-btn" data-command="italic" title="斜体 (Ctrl+I)">
|
|
|
|
|
<i class="fa fa-italic"></i>
|
|
|
|
|
</button>
|
|
|
|
|
<button type="button" class="toolbar-btn" data-command="underline" title="下划线 (Ctrl+U)">
|
|
|
|
|
<i class="fa fa-underline"></i>
|
|
|
|
|
</button>
|
|
|
|
|
<span class="h-4 w-px bg-gray-300 mx-1"></span>
|
|
|
|
|
<button type="button" class="toolbar-btn" data-command="insertUnorderedList" title="无序列表">
|
|
|
|
|
<i class="fa fa-list-ul"></i>
|
|
|
|
|
</button>
|
|
|
|
|
<button type="button" class="toolbar-btn" data-command="insertOrderedList" title="有序列表">
|
|
|
|
|
<i class="fa fa-list-ol"></i>
|
|
|
|
|
</button>
|
|
|
|
|
<button type="button" class="toolbar-btn" data-command="outdent" title="减少缩进">
|
|
|
|
|
<i class="fa fa-outdent"></i>
|
|
|
|
|
</button>
|
|
|
|
|
<button type="button" class="toolbar-btn" data-command="indent" title="增加缩进">
|
|
|
|
|
<i class="fa fa-indent"></i>
|
|
|
|
|
</button>
|
|
|
|
|
<span class="h-4 w-px bg-gray-300 mx-1"></span>
|
|
|
|
|
<button type="button" class="toolbar-btn" data-command="justifyLeft" title="左对齐">
|
|
|
|
|
<i class="fa fa-align-left"></i>
|
|
|
|
|
</button>
|
|
|
|
|
<button type="button" class="toolbar-btn" data-command="justifyCenter" title="居中对齐">
|
|
|
|
|
<i class="fa fa-align-center"></i>
|
|
|
|
|
</button>
|
|
|
|
|
<button type="button" class="toolbar-btn" data-command="justifyRight" title="右对齐">
|
|
|
|
|
<i class="fa fa-align-right"></i>
|
|
|
|
|
</button>
|
|
|
|
|
<span class="h-4 w-px bg-gray-300 mx-1"></span>
|
|
|
|
|
<button type="button" class="toolbar-btn" id="insertImageBtn" title="插入图片">
|
|
|
|
|
<i class="fa fa-image"></i>
|
|
|
|
|
</button>
|
|
|
|
|
<button type="button" class="toolbar-btn" title="插入公式">
|
|
|
|
|
<i class="fa fa-superscript"></i>
|
|
|
|
|
</button>
|
|
|
|
|
<button type="button" class="toolbar-btn" data-command="createLink" title="插入链接">
|
|
|
|
|
<i class="fa fa-link"></i>
|
|
|
|
|
</button>
|
|
|
|
|
<button type="button" class="toolbar-btn" data-command="unlink" title="移除链接">
|
|
|
|
|
<i class="fa fa-unlink"></i>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 富文本编辑区域 -->
|
|
|
|
|
<div
|
|
|
|
|
id="noteContent"
|
|
|
|
|
class="note-editor min-h-[300px]"
|
|
|
|
|
contenteditable="true"
|
|
|
|
|
placeholder="请输入笔记内容..."
|
|
|
|
|
></div>
|
|
|
|
|
</div>
|
|
|
|
|
<p id="noteContentError" class="text-red-500 text-xs mt-1 hidden">请输入笔记内容</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 笔记封面 -->
|
|
|
|
|
<div>
|
|
|
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">笔记封面</label>
|
|
|
|
|
<div class="flex items-center gap-3">
|
|
|
|
|
<div class="w-20 h-20 bg-gray-100 rounded-md flex items-center justify-center border border-dashed border-gray-300">
|
|
|
|
|
<img id="noteCoverPreview" src="" alt="封面预览" class="w-full h-full object-cover hidden">
|
|
|
|
|
<i class="fa fa-picture-o text-gray-400" id="coverPlaceholder"></i>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label for="noteCover" class="btn-action btn-secondary cursor-pointer inline-flex items-center">
|
|
|
|
|
<i class="fa fa-upload mr-1"></i> 选择图片
|
|
|
|
|
</label>
|
|
|
|
|
<input type="file" id="noteCover" accept="image/*" class="hidden">
|
|
|
|
|
<p class="text-xs text-gray-500 mt-1">支持JPG、PNG格式,建议尺寸400×200</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 提交按钮 -->
|
|
|
|
|
<div class="flex gap-3 pt-2">
|
|
|
|
|
<button type="button" id="cancelNewNote" class="flex-1 btn-action btn-secondary">
|
|
|
|
|
取消
|
|
|
|
|
</button>
|
|
|
|
|
<button type="submit" class="flex-1 login-btn">
|
|
|
|
|
<span id="noteSubmitBtnText">保存笔记</span>
|
|
|
|
|
<i class="fa fa-check"></i>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</form>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 插入图片模态框 -->
|
|
|
|
|
<div id="insertImageModal" class="fixed inset-0 bg-black/50 flex items-center justify-center p-4 z-50 hidden transition-opacity duration-300">
|
|
|
|
|
<div class="w-full max-w-md bg-white rounded-xl p-6 shadow-lg transform transition-transform duration-300 scale-95">
|
|
|
|
|
<div class="flex justify-between items-center mb-4">
|
|
|
|
|
<h2 class="text-xl font-semibold text-dark">插入图片</h2>
|
|
|
|
|
<button id="closeInsertImageModal" class="text-gray-400 hover:text-gray-600">
|
|
|
|
|
<i class="fa fa-times text-xl"></i>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="space-y-5">
|
|
|
|
|
<!-- 上传选项卡 -->
|
|
|
|
|
<div class="border-b border-gray-200">
|
|
|
|
|
<div class="flex">
|
|
|
|
|
<button class="px-4 py-2 text-sm font-medium text-primary border-b-2 border-primary" id="uploadTabBtn">
|
|
|
|
|
本地上传
|
|
|
|
|
</button>
|
|
|
|
|
<button class="px-4 py-2 text-sm font-medium text-gray-500 hover:text-gray-700" id="urlTabBtn">
|
|
|
|
|
图片链接
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- 其他表单内容(如笔记内容、封面上传等) -->
|
|
|
|
|
<!-- ... -->
|
|
|
|
|
</form>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- 本地上传区域 -->
|
|
|
|
|
<div id="uploadTab" class="space-y-4">
|
|
|
|
|
<div class="file-drop-area" id="imageDropArea">
|
|
|
|
|
<input type="file" id="imageUpload" accept="image/*" class="hidden">
|
|
|
|
|
<i class="fa fa-cloud-upload text-2xl text-gray-400 mb-2"></i>
|
|
|
|
|
<p class="text-sm text-gray-600 mb-1">点击或拖拽图片到此处</p>
|
|
|
|
|
<p class="text-xs text-gray-500">支持JPG、PNG格式,最大5MB</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 预览区域 -->
|
|
|
|
|
<div id="imagePreviewContainer" class="hidden">
|
|
|
|
|
<p class="text-sm font-medium text-gray-700 mb-2">预览:</p>
|
|
|
|
|
<div class="relative">
|
|
|
|
|
<img id="imagePreview" src="" alt="图片预览" class="max-w-full max-h-60 rounded-md border border-gray-200">
|
|
|
|
|
<button id="removeImage" class="absolute top-2 right-2 w-6 h-6 bg-black/50 text-white rounded-full flex items-center justify-center hover:bg-black/70 transition-colors">
|
|
|
|
|
<i class="fa fa-times text-xs"></i>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 图片链接区域 -->
|
|
|
|
|
<div id="urlTab" class="hidden space-y-4">
|
|
|
|
|
<div>
|
|
|
|
|
<label for="imageUrl" class="block text-sm font-medium text-gray-700 mb-1">图片URL</label>
|
|
|
|
|
<input
|
|
|
|
|
type="url"
|
|
|
|
|
id="imageUrl"
|
|
|
|
|
class="login-input"
|
|
|
|
|
placeholder="https://example.com/image.jpg"
|
|
|
|
|
>
|
|
|
|
|
<p class="text-xs text-gray-500 mt-1">请输入有效的图片链接</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="bg-gray-50 p-3 rounded-md">
|
|
|
|
|
<label class="flex items-center gap-2 text-sm">
|
|
|
|
|
<input type="checkbox" id="imageResponsive" checked>
|
|
|
|
|
<span>自适应宽度</span>
|
|
|
|
|
</label>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 按钮 -->
|
|
|
|
|
<div class="flex gap-3 pt-2">
|
|
|
|
|
<button type="button" id="cancelInsertImage" class="flex-1 bg-gray-200 text-dark py-2 rounded-md font-medium hover:bg-gray-300 transition-colors">
|
|
|
|
|
取消
|
|
|
|
|
</button>
|
|
|
|
|
<button type="button" id="confirmInsertImage" class="flex-1 bg-primary text-white py-2 rounded-md font-medium hover:bg-primary/90 transition-colors flex items-center justify-center gap-2">
|
|
|
|
|
<span>插入图片</span>
|
|
|
|
|
<i class="fa fa-arrow-right"></i>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 导入笔记模态框 -->
|
|
|
|
|
<div id="importNoteModal" class="fixed inset-0 bg-black/50 flex items-center justify-center p-4 z-50 hidden transition-opacity duration-300">
|
|
|
|
|
@ -962,44 +719,6 @@
|
|
|
|
|
const userName = document.getElementById('user-name');
|
|
|
|
|
const userGradeMajor = document.getElementById('user-grade-major');
|
|
|
|
|
|
|
|
|
|
// 笔记相关元素
|
|
|
|
|
const categoryFilter = document.getElementById('categoryFilter');
|
|
|
|
|
const newNoteBtn = document.getElementById('newNoteBtn');
|
|
|
|
|
const importNoteBtn = document.getElementById('importNoteBtn');
|
|
|
|
|
const newNoteModal = document.getElementById('newNoteModal');
|
|
|
|
|
const closeNewNoteModal = document.getElementById('closeNewNoteModal');
|
|
|
|
|
const cancelNewNote = document.getElementById('cancelNewNote');
|
|
|
|
|
const newNoteForm = document.getElementById('newNoteForm');
|
|
|
|
|
const noteModalTitle = document.getElementById('noteModalTitle');
|
|
|
|
|
const noteId = document.getElementById('noteId');
|
|
|
|
|
const noteTitle = document.getElementById('noteTitle');
|
|
|
|
|
const noteCategory = document.getElementById('noteCategory');
|
|
|
|
|
const noteContent = document.getElementById('noteContent');
|
|
|
|
|
const noteCover = document.getElementById('noteCover');
|
|
|
|
|
const noteCoverPreview = document.getElementById('noteCoverPreview');
|
|
|
|
|
const coverPlaceholder = document.getElementById('coverPlaceholder');
|
|
|
|
|
const noteSubmitBtnText = document.getElementById('noteSubmitBtnText');
|
|
|
|
|
const notesContainer = document.getElementById('notes-container');
|
|
|
|
|
let currentSelectedCategory = 'all';
|
|
|
|
|
|
|
|
|
|
// 富文本编辑器元素
|
|
|
|
|
const toolbarButtons = document.querySelectorAll('.toolbar-btn[data-command]');
|
|
|
|
|
const insertImageBtn = document.getElementById('insertImageBtn');
|
|
|
|
|
const insertImageModal = document.getElementById('insertImageModal');
|
|
|
|
|
const closeInsertImageModal = document.getElementById('closeInsertImageModal');
|
|
|
|
|
const cancelInsertImage = document.getElementById('cancelInsertImage');
|
|
|
|
|
const confirmInsertImage = document.getElementById('confirmInsertImage');
|
|
|
|
|
const uploadTabBtn = document.getElementById('uploadTabBtn');
|
|
|
|
|
const urlTabBtn = document.getElementById('urlTabBtn');
|
|
|
|
|
const uploadTab = document.getElementById('uploadTab');
|
|
|
|
|
const urlTab = document.getElementById('urlTab');
|
|
|
|
|
const imageDropArea = document.getElementById('imageDropArea');
|
|
|
|
|
const imageUpload = document.getElementById('imageUpload');
|
|
|
|
|
const imagePreviewContainer = document.getElementById('imagePreviewContainer');
|
|
|
|
|
const imagePreview = document.getElementById('imagePreview');
|
|
|
|
|
const removeImage = document.getElementById('removeImage');
|
|
|
|
|
const imageUrl = document.getElementById('imageUrl');
|
|
|
|
|
const imageResponsive = document.getElementById('imageResponsive');
|
|
|
|
|
|
|
|
|
|
// 导入相关元素
|
|
|
|
|
const importNoteModal = document.getElementById('importNoteModal');
|
|
|
|
|
@ -1261,491 +980,8 @@ document.querySelectorAll('.sidebar-item[data-view]').forEach(item => {
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 搜索笔记 + 分类筛选 联合功能
|
|
|
|
|
const noteSearchInput = document.getElementById('noteSearch');
|
|
|
|
|
|
|
|
|
|
// 搜索框输入事件
|
|
|
|
|
if (noteSearchInput) {
|
|
|
|
|
noteSearchInput.addEventListener('input', function () {
|
|
|
|
|
const searchTerm = this.value.trim().toLowerCase();
|
|
|
|
|
// 调用联合筛选函数(分类+关键词)
|
|
|
|
|
filterNotesByCategoryAndSearch(searchTerm);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 分类筛选下拉框切换事件(新增)
|
|
|
|
|
if (categoryFilter) {
|
|
|
|
|
categoryFilter.addEventListener('change', function () {
|
|
|
|
|
currentSelectedCategory = this.value; // 更新当前选中分类
|
|
|
|
|
const searchTerm = noteSearchInput.value.trim().toLowerCase();
|
|
|
|
|
// 调用联合筛选函数(分类+关键词)
|
|
|
|
|
filterNotesByCategoryAndSearch(searchTerm);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 新增:联合筛选函数(同时处理分类和搜索关键词)
|
|
|
|
|
function filterNotesByCategoryAndSearch(searchTerm) {
|
|
|
|
|
let filteredNotes = notesData;
|
|
|
|
|
|
|
|
|
|
// 第一步:按分类筛选(非“全部分类”时过滤)
|
|
|
|
|
if (currentSelectedCategory !== 'all') {
|
|
|
|
|
filteredNotes = filteredNotes.filter(note => note.category === currentSelectedCategory);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 第二步:按关键词筛选(搜索框有内容时过滤)
|
|
|
|
|
if (searchTerm !== '') {
|
|
|
|
|
filteredNotes = filteredNotes.filter(note => {
|
|
|
|
|
const titleMatch = note.title.toLowerCase().includes(searchTerm);
|
|
|
|
|
const contentMatch = stripHtml(note.content).toLowerCase().includes(searchTerm);
|
|
|
|
|
return titleMatch || contentMatch;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 渲染筛选后的笔记列表
|
|
|
|
|
renderNotes(filteredNotes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 富文本编辑器功能实现
|
|
|
|
|
// 文本格式化按钮
|
|
|
|
|
toolbarButtons.forEach(button => {
|
|
|
|
|
button.addEventListener('click', () => {
|
|
|
|
|
const command = button.getAttribute('data-command');
|
|
|
|
|
|
|
|
|
|
// 特殊处理链接插入
|
|
|
|
|
if (command === 'createLink') {
|
|
|
|
|
const url = prompt('请输入链接地址:');
|
|
|
|
|
if (url) {
|
|
|
|
|
document.execCommand(command, false, url);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 执行命令
|
|
|
|
|
document.execCommand(command, false, null);
|
|
|
|
|
|
|
|
|
|
// 聚焦回编辑区域
|
|
|
|
|
noteContent.focus();
|
|
|
|
|
|
|
|
|
|
// 更新按钮状态(如粗体按钮是否激活)
|
|
|
|
|
updateToolbarState();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 更新工具栏按钮状态
|
|
|
|
|
function updateToolbarState() {
|
|
|
|
|
toolbarButtons.forEach(button => {
|
|
|
|
|
const command = button.getAttribute('data-command');
|
|
|
|
|
if (document.queryCommandState(command)) {
|
|
|
|
|
button.classList.add('active');
|
|
|
|
|
} else {
|
|
|
|
|
button.classList.remove('active');
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 监听编辑区域选择变化,更新工具栏状态
|
|
|
|
|
noteContent.addEventListener('mouseup', updateToolbarState);
|
|
|
|
|
noteContent.addEventListener('keyup', updateToolbarState);
|
|
|
|
|
|
|
|
|
|
// 插入图片模态框控制
|
|
|
|
|
insertImageBtn.addEventListener('click', () => {
|
|
|
|
|
insertImageModal.classList.remove('hidden');
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
insertImageModal.querySelector('div').classList.add('scale-100');
|
|
|
|
|
insertImageModal.querySelector('div').classList.remove('scale-95');
|
|
|
|
|
}, 10);
|
|
|
|
|
resetInsertImageForm();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
closeInsertImageModal.addEventListener('click', () => {
|
|
|
|
|
closeInsertImageModalHandler();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
cancelInsertImage.addEventListener('click', () => {
|
|
|
|
|
closeInsertImageModalHandler();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
function closeInsertImageModalHandler() {
|
|
|
|
|
insertImageModal.querySelector('div').classList.add('scale-95');
|
|
|
|
|
insertImageModal.querySelector('div').classList.remove('scale-100');
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
insertImageModal.classList.add('hidden');
|
|
|
|
|
}, 300);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 重置插入图片表单
|
|
|
|
|
function resetInsertImageForm() {
|
|
|
|
|
imageUpload.value = '';
|
|
|
|
|
imagePreviewContainer.classList.add('hidden');
|
|
|
|
|
imagePreview.src = '';
|
|
|
|
|
imageUrl.value = '';
|
|
|
|
|
imageResponsive.checked = true;
|
|
|
|
|
showTab('upload');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 切换上传/URL选项卡
|
|
|
|
|
uploadTabBtn.addEventListener('click', () => {
|
|
|
|
|
showTab('upload');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
urlTabBtn.addEventListener('click', () => {
|
|
|
|
|
showTab('url');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
function showTab(tabName) {
|
|
|
|
|
if (tabName === 'upload') {
|
|
|
|
|
uploadTab.classList.remove('hidden');
|
|
|
|
|
urlTab.classList.add('hidden');
|
|
|
|
|
uploadTabBtn.classList.add('text-primary', 'border-b-2', 'border-primary');
|
|
|
|
|
uploadTabBtn.classList.remove('text-gray-500');
|
|
|
|
|
urlTabBtn.classList.remove('text-primary', 'border-b-2', 'border-primary');
|
|
|
|
|
urlTabBtn.classList.add('text-gray-500');
|
|
|
|
|
} else {
|
|
|
|
|
uploadTab.classList.add('hidden');
|
|
|
|
|
urlTab.classList.remove('hidden');
|
|
|
|
|
uploadTabBtn.classList.remove('text-primary', 'border-b-2', 'border-primary');
|
|
|
|
|
uploadTabBtn.classList.add('text-gray-500');
|
|
|
|
|
urlTabBtn.classList.add('text-primary', 'border-b-2', 'border-primary');
|
|
|
|
|
urlTabBtn.classList.remove('text-gray-500');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 图片上传处理
|
|
|
|
|
imageDropArea.addEventListener('click', () => {
|
|
|
|
|
imageUpload.click();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
imageUpload.addEventListener('change', (e) => {
|
|
|
|
|
const file = e.target.files[0];
|
|
|
|
|
if (file) {
|
|
|
|
|
handleImageUpload(file);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 拖拽图片处理
|
|
|
|
|
imageDropArea.addEventListener('dragover', (e) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
imageDropArea.classList.add('border-primary', 'bg-primary/5');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
imageDropArea.addEventListener('dragleave', () => {
|
|
|
|
|
imageDropArea.classList.remove('border-primary', 'bg-primary/5');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
imageDropArea.addEventListener('drop', (e) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
imageDropArea.classList.remove('border-primary', 'bg-primary/5');
|
|
|
|
|
|
|
|
|
|
const file = e.dataTransfer.files[0];
|
|
|
|
|
if (file) {
|
|
|
|
|
handleImageUpload(file);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 处理图片上传
|
|
|
|
|
function handleImageUpload(file) {
|
|
|
|
|
// 检查文件类型
|
|
|
|
|
if (!file.type.startsWith('image/')) {
|
|
|
|
|
alert('请选择图片文件(JPG、PNG格式)');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查文件大小
|
|
|
|
|
if (file.size > 5 * 1024 * 1024) {
|
|
|
|
|
alert('图片大小不能超过5MB');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 预览图片
|
|
|
|
|
const reader = new FileReader();
|
|
|
|
|
reader.onload = (event) => {
|
|
|
|
|
imagePreview.src = event.target.result;
|
|
|
|
|
imagePreviewContainer.classList.remove('hidden');
|
|
|
|
|
};
|
|
|
|
|
reader.readAsDataURL(file);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 移除已选择的图片
|
|
|
|
|
removeImage.addEventListener('click', () => {
|
|
|
|
|
imageUpload.value = '';
|
|
|
|
|
imagePreviewContainer.classList.add('hidden');
|
|
|
|
|
imagePreview.src = '';
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 确认插入图片
|
|
|
|
|
confirmInsertImage.addEventListener('click', () => {
|
|
|
|
|
let imageSrc = '';
|
|
|
|
|
|
|
|
|
|
// 检查是上传还是URL
|
|
|
|
|
if (!uploadTab.classList.contains('hidden')) {
|
|
|
|
|
// 上传方式
|
|
|
|
|
if (!imagePreview.src) {
|
|
|
|
|
alert('请先选择图片');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
imageSrc = imagePreview.src;
|
|
|
|
|
} else {
|
|
|
|
|
// URL方式
|
|
|
|
|
if (!imageUrl.value.trim()) {
|
|
|
|
|
alert('请输入图片URL');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
imageSrc = imageUrl.value.trim();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 在编辑器中插入图片
|
|
|
|
|
const isResponsive = imageResponsive.checked;
|
|
|
|
|
const img = document.createElement('img');
|
|
|
|
|
img.src = imageSrc;
|
|
|
|
|
img.alt = '插入的图片';
|
|
|
|
|
if (isResponsive) {
|
|
|
|
|
img.style.maxWidth = '100%';
|
|
|
|
|
img.style.height = 'auto';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取选择对象
|
|
|
|
|
const selection = window.getSelection();
|
|
|
|
|
if (selection.rangeCount > 0) {
|
|
|
|
|
const range = selection.getRangeAt(0);
|
|
|
|
|
range.deleteContents();
|
|
|
|
|
range.insertNode(img);
|
|
|
|
|
|
|
|
|
|
// 在图片后添加一个空格,方便继续输入
|
|
|
|
|
range.setStartAfter(img);
|
|
|
|
|
range.setEndAfter(img);
|
|
|
|
|
range.insertNode(document.createTextNode(' '));
|
|
|
|
|
selection.removeAllRanges();
|
|
|
|
|
selection.addRange(range);
|
|
|
|
|
} else {
|
|
|
|
|
// 如果没有选择范围,直接添加到末尾
|
|
|
|
|
noteContent.appendChild(img);
|
|
|
|
|
noteContent.appendChild(document.createTextNode(' '));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 关闭模态框
|
|
|
|
|
closeInsertImageModalHandler();
|
|
|
|
|
|
|
|
|
|
// 聚焦回编辑区域
|
|
|
|
|
noteContent.focus();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 新建笔记模态框控制
|
|
|
|
|
newNoteBtn.addEventListener('click', () => {
|
|
|
|
|
// 切换到新建模式
|
|
|
|
|
noteModalTitle.textContent = '新建笔记';
|
|
|
|
|
noteSubmitBtnText.textContent = '保存笔记';
|
|
|
|
|
noteId.value = '';
|
|
|
|
|
|
|
|
|
|
newNoteModal.classList.remove('hidden');
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
newNoteModal.querySelector('div').classList.add('scale-100');
|
|
|
|
|
newNoteModal.querySelector('div').classList.remove('scale-95');
|
|
|
|
|
}, 10);
|
|
|
|
|
resetNewNoteForm();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
closeNewNoteModal.addEventListener('click', () => {
|
|
|
|
|
closeNewNoteModalHandler();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
cancelNewNote.addEventListener('click', () => {
|
|
|
|
|
closeNewNoteModalHandler();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
function closeNewNoteModalHandler() {
|
|
|
|
|
newNoteModal.querySelector('div').classList.add('scale-95');
|
|
|
|
|
newNoteModal.querySelector('div').classList.remove('scale-100');
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
newNoteModal.classList.add('hidden');
|
|
|
|
|
}, 300);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 重置新建笔记表单
|
|
|
|
|
function resetNewNoteForm() {
|
|
|
|
|
newNoteForm.reset();
|
|
|
|
|
noteTitleError.classList.add('hidden');
|
|
|
|
|
noteCategoryError.classList.add('hidden');
|
|
|
|
|
noteContentError.classList.add('hidden');
|
|
|
|
|
noteContent.innerHTML = '';
|
|
|
|
|
noteCoverPreview.classList.add('hidden');
|
|
|
|
|
coverPlaceholder.classList.remove('hidden');
|
|
|
|
|
noteCover.value = '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 笔记封面预览
|
|
|
|
|
noteCover.addEventListener('change', (e) => {
|
|
|
|
|
const file = e.target.files[0];
|
|
|
|
|
if (file) {
|
|
|
|
|
const reader = new FileReader();
|
|
|
|
|
reader.onload = (event) => {
|
|
|
|
|
noteCoverPreview.src = event.target.result;
|
|
|
|
|
noteCoverPreview.classList.remove('hidden');
|
|
|
|
|
coverPlaceholder.classList.add('hidden');
|
|
|
|
|
};
|
|
|
|
|
reader.readAsDataURL(file);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 编辑笔记功能
|
|
|
|
|
notesContainer.addEventListener('click', (e) => {
|
|
|
|
|
const editBtn = e.target.closest('.edit-note');
|
|
|
|
|
if (editBtn) {
|
|
|
|
|
const noteId = editBtn.getAttribute('data-id');
|
|
|
|
|
openEditNoteModal(noteId);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 打开编辑笔记模态框
|
|
|
|
|
function openEditNoteModal(id) {
|
|
|
|
|
const note = notesData.find(n => n.id == id);
|
|
|
|
|
if (!note) return;
|
|
|
|
|
|
|
|
|
|
// 切换到编辑模式
|
|
|
|
|
noteModalTitle.textContent = '编辑笔记';
|
|
|
|
|
noteSubmitBtnText.textContent = '更新笔记';
|
|
|
|
|
noteId.value = note.id;
|
|
|
|
|
|
|
|
|
|
// 填充表单数据
|
|
|
|
|
noteTitle.value = note.title;
|
|
|
|
|
noteCategory.value = note.category;
|
|
|
|
|
noteContent.innerHTML = note.content;
|
|
|
|
|
|
|
|
|
|
// 显示封面
|
|
|
|
|
if (note.cover) {
|
|
|
|
|
noteCoverPreview.src = note.cover;
|
|
|
|
|
noteCoverPreview.classList.remove('hidden');
|
|
|
|
|
coverPlaceholder.classList.add('hidden');
|
|
|
|
|
} else {
|
|
|
|
|
noteCoverPreview.classList.add('hidden');
|
|
|
|
|
coverPlaceholder.classList.remove('hidden');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 显示模态框
|
|
|
|
|
newNoteModal.classList.remove('hidden');
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
newNoteModal.querySelector('div').classList.add('scale-100');
|
|
|
|
|
newNoteModal.querySelector('div').classList.remove('scale-95');
|
|
|
|
|
}, 10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 删除笔记功能
|
|
|
|
|
notesContainer.addEventListener('click', (e) => {
|
|
|
|
|
const deleteBtn = e.target.closest('.delete-note');
|
|
|
|
|
if (deleteBtn) {
|
|
|
|
|
const noteId = deleteBtn.getAttribute('data-id');
|
|
|
|
|
const note = notesData.find(n => n.id == noteId);
|
|
|
|
|
|
|
|
|
|
if (confirm(`确定要删除“${note.title}”吗?`)) {
|
|
|
|
|
// 从内存中移除笔记
|
|
|
|
|
notesData = notesData.filter(n => n.id != noteId);
|
|
|
|
|
// 同步保存到 LocalStorage
|
|
|
|
|
localStorage.setItem('notesData', JSON.stringify(notesData));
|
|
|
|
|
// 重新渲染笔记列表
|
|
|
|
|
const searchTerm = noteSearchInput.value.trim().toLowerCase();
|
|
|
|
|
filterNotesByCategoryAndSearch(searchTerm);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 新建/编辑笔记表单提交
|
|
|
|
|
if (newNoteForm) { // 确保表单元素存在
|
|
|
|
|
newNoteForm.addEventListener('submit', (e) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
let isValid = true;
|
|
|
|
|
|
|
|
|
|
// 验证表单(先检查元素是否存在,避免报错)
|
|
|
|
|
if (noteTitle && noteTitleError) {
|
|
|
|
|
if (!noteTitle.value.trim()) {
|
|
|
|
|
noteTitleError.classList.remove('hidden');
|
|
|
|
|
isValid = false;
|
|
|
|
|
} else {
|
|
|
|
|
noteTitleError.classList.add('hidden');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 分类验证(假设分类输入框id为"noteCategory",错误提示id为"noteCategoryError")
|
|
|
|
|
if (noteCategory && noteCategoryError) {
|
|
|
|
|
// 允许分类为空时,可改为:if (noteCategory.value.trim() === '')
|
|
|
|
|
// 验证笔记分类
|
|
|
|
|
if (!noteCategory.value) {
|
|
|
|
|
noteCategoryError.classList.remove('hidden');
|
|
|
|
|
isValid = false;
|
|
|
|
|
} else {
|
|
|
|
|
noteCategoryError.classList.add('hidden');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 内容验证
|
|
|
|
|
if (noteContent && noteContentError) {
|
|
|
|
|
if (!noteContent.innerHTML.trim()) {
|
|
|
|
|
noteContentError.classList.remove('hidden');
|
|
|
|
|
isValid = false;
|
|
|
|
|
} else {
|
|
|
|
|
noteContentError.classList.add('hidden');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isValid) {
|
|
|
|
|
const isEditMode = !!noteId?.value; // 安全访问noteId
|
|
|
|
|
|
|
|
|
|
// 确保notesData是数组(避免未初始化导致的错误)
|
|
|
|
|
if (!Array.isArray(notesData)) {
|
|
|
|
|
notesData = [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isEditMode) {
|
|
|
|
|
// 更新现有笔记
|
|
|
|
|
const index = notesData.findIndex(n => n.id == noteId.value);
|
|
|
|
|
if (index !== -1) {
|
|
|
|
|
notesData[index] = {
|
|
|
|
|
...notesData[index],
|
|
|
|
|
title: noteTitle.value.trim(),
|
|
|
|
|
category: noteCategory?.value || '', // 安全访问分类值
|
|
|
|
|
content: noteContent.innerHTML,
|
|
|
|
|
date: new Date().toISOString().split('T')[0]
|
|
|
|
|
};
|
|
|
|
|
// 若上传新封面,更新封面
|
|
|
|
|
if (noteCoverPreview && noteCoverPreview.src && !noteCoverPreview.classList.contains('hidden')) {
|
|
|
|
|
notesData[index].cover = noteCoverPreview.src;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// 生成新笔记ID(处理空数组情况)
|
|
|
|
|
const maxId = notesData.length > 0
|
|
|
|
|
? Math.max(...notesData.map(n => Number(n.id) || 0))
|
|
|
|
|
: 0;
|
|
|
|
|
const newNoteId = maxId + 1;
|
|
|
|
|
|
|
|
|
|
// 创建新笔记
|
|
|
|
|
const newNote = {
|
|
|
|
|
id: newNoteId,
|
|
|
|
|
title: noteTitle.value.trim(),
|
|
|
|
|
category: noteCategory?.value || '', // 分类为空时用空字符串
|
|
|
|
|
content: noteContent.innerHTML,
|
|
|
|
|
cover: noteCoverPreview?.src || `https://picsum.photos/id/${30 + notesData.length}/400/200`,
|
|
|
|
|
date: new Date().toISOString().split('T')[0],
|
|
|
|
|
type: 'Text',
|
|
|
|
|
views: 0
|
|
|
|
|
};
|
|
|
|
|
notesData.unshift(newNote); // 添加到数组开头(最新的在前面)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 同步保存到LocalStorage
|
|
|
|
|
localStorage.setItem('notesData', JSON.stringify(notesData));
|
|
|
|
|
|
|
|
|
|
// 刷新列表(确保搜索框元素存在)
|
|
|
|
|
const searchTerm = noteSearchInput?.value.trim().toLowerCase() || '';
|
|
|
|
|
filterNotesByCategoryAndSearch(searchTerm);
|
|
|
|
|
|
|
|
|
|
// 关闭模态框 + 提示成功
|
|
|
|
|
closeNewNoteModalHandler();
|
|
|
|
|
alert(isEditMode ? '笔记更新成功!' : '笔记创建成功!');
|
|
|
|
|
|
|
|
|
|
// 更新分类下拉框
|
|
|
|
|
renderDynamicCategoryFilter();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
// 导入笔记模态框控制
|
|
|
|
|
importNoteBtn.addEventListener('click', () => {
|
|
|
|
|
importNoteModal.classList.remove('hidden');
|
|
|
|
|
@ -2239,8 +1475,7 @@ function fetchNotesFromServer() {
|
|
|
|
|
notesData = validNotes;
|
|
|
|
|
localStorage.setItem('notesData', JSON.stringify(notesData));
|
|
|
|
|
renderNotes();
|
|
|
|
|
// 新增:获取数据后渲染分类下拉框
|
|
|
|
|
renderDynamicCategoryFilter();
|
|
|
|
|
|
|
|
|
|
})
|
|
|
|
|
.catch(err => {
|
|
|
|
|
console.error('刷新失败:', err);
|
|
|
|
|
@ -2432,19 +1667,7 @@ document.getElementById('documents-container').addEventListener('click', async f
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 填充笔记编辑区域并打开模态框
|
|
|
|
|
function fillNoteWithContent(title, content, type) {
|
|
|
|
|
const noteTitle = document.getElementById('noteTitle');
|
|
|
|
|
const noteContent = document.getElementById('noteContent');
|
|
|
|
|
const noteType = document.getElementById('noteType'); // 假设笔记有“类型”字段(需在HTML中添加隐藏输入)
|
|
|
|
|
|
|
|
|
|
noteTitle.value = title;
|
|
|
|
|
noteContent.innerHTML = content;
|
|
|
|
|
if (noteType) noteType.value = type; // 可选:标记笔记类型
|
|
|
|
|
|
|
|
|
|
// 打开“新建笔记”模态框(模拟点击按钮)
|
|
|
|
|
document.getElementById('newNoteBtn').click();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 单独渲染PDF文件(用PDF.js)
|
|
|
|
|
function openPdfViewer(pdfUrl) {
|
|
|
|
|
@ -2485,100 +1708,8 @@ function openPdfViewer(pdfUrl) {
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 动态渲染分类下拉框(根据用户输入的分类自动生成选项)
|
|
|
|
|
function renderDynamicCategoryFilter() {
|
|
|
|
|
const categoryFilter = document.getElementById('categoryFilter');
|
|
|
|
|
if (!categoryFilter) return;
|
|
|
|
|
|
|
|
|
|
// 清空现有选项
|
|
|
|
|
categoryFilter.innerHTML = '';
|
|
|
|
|
|
|
|
|
|
// 添加“全部分类”默认选项
|
|
|
|
|
const allOption = document.createElement('option');
|
|
|
|
|
allOption.value = 'all';
|
|
|
|
|
allOption.textContent = '全部分类';
|
|
|
|
|
categoryFilter.appendChild(allOption);
|
|
|
|
|
|
|
|
|
|
// 提取所有非空分类(去重)
|
|
|
|
|
const categories = new Set();
|
|
|
|
|
if (Array.isArray(notesData)) {
|
|
|
|
|
notesData.forEach(note => {
|
|
|
|
|
if (note.category && note.category.trim() !== '') {
|
|
|
|
|
categories.add(note.category.trim());
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 排序并生成选项
|
|
|
|
|
const sortedCategories = Array.from(categories).sort((a, b) => a.localeCompare(b));
|
|
|
|
|
sortedCategories.forEach(category => {
|
|
|
|
|
const option = document.createElement('option');
|
|
|
|
|
option.value = category;
|
|
|
|
|
option.textContent = category;
|
|
|
|
|
categoryFilter.appendChild(option);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 保持当前选中分类
|
|
|
|
|
if (currentSelectedCategory && categoryFilter.querySelector(`option[value="${currentSelectedCategory}"]`)) {
|
|
|
|
|
categoryFilter.value = currentSelectedCategory;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 渲染笔记列表(支持传入过滤后的笔记数据)
|
|
|
|
|
function renderNotes(notesToRender = notesData) {
|
|
|
|
|
// 1. 确保能找到笔记容器元素
|
|
|
|
|
const notesContainer = document.getElementById('notes-container');
|
|
|
|
|
if (!notesContainer) {
|
|
|
|
|
console.error('页面中未找到笔记容器元素 #notes-container,请检查DOM结构');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. 处理「无数据」或「数据格式错误」的情况
|
|
|
|
|
if (!Array.isArray(notesToRender) || notesToRender.length === 0) {
|
|
|
|
|
notesContainer.innerHTML = `
|
|
|
|
|
<div class="text-center py-12 text-gray-500">
|
|
|
|
|
<i class="fa fa-book-open text-4xl mb-4"></i>
|
|
|
|
|
</div>
|
|
|
|
|
`;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
notesContainer.innerHTML = `
|
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
|
|
|
${notesToRender.map(note => `
|
|
|
|
|
<div class="bg-white rounded-lg overflow-hidden shadow-sm hover:shadow-md transition-shadow border border-gray-200">
|
|
|
|
|
<div class="h-40 bg-gray-100 flex items-center justify-center">
|
|
|
|
|
<img src="${note.cover}" alt="${note.title}预览" class="w-full h-full object-cover">
|
|
|
|
|
</div>
|
|
|
|
|
<div class="p-4">
|
|
|
|
|
<div class="flex items-center justify-between mb-2">
|
|
|
|
|
<span class="text-xs bg-blue-100 text-blue-800 px-2 py-0.5 rounded">${note.category}</span>
|
|
|
|
|
<span class="text-xs text-gray-500">${note.date}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<h3 class="font-medium mb-2 line-clamp-1">${note.title}</h3>
|
|
|
|
|
<p class="text-sm text-gray-600 mb-3 line-clamp-2">${stripHtml(note.content.substring(0, 100))}...</p>
|
|
|
|
|
<div class="flex items-center justify-between">
|
|
|
|
|
<div class="flex items-center gap-2">
|
|
|
|
|
<span class="text-xs text-gray-500 flex items-center">
|
|
|
|
|
<i class="fa fa-file-${note.type === 'PDF' ? 'pdf' : note.type === 'Word' ? 'word' : 'text'}-o mr-1"></i> ${note.type}
|
|
|
|
|
</span>
|
|
|
|
|
<span class="text-xs text-gray-500 flex items-center">
|
|
|
|
|
<i class="fa fa-eye mr-1"></i> ${note.views}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="flex items-center gap-3">
|
|
|
|
|
<button class="text-gray-500 hover:text-primary edit-note" title="编辑" data-id="${note.id}">
|
|
|
|
|
<i class="fa fa-pencil text-lg"></i>
|
|
|
|
|
</button>
|
|
|
|
|
<button class="text-gray-500 hover:text-red-500 delete-note" title="删除" data-id="${note.id}">
|
|
|
|
|
<i class="fa fa-trash text-lg"></i>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
`).join('')}
|
|
|
|
|
</div>
|
|
|
|
|
`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 辅助函数:去除HTML标签
|
|
|
|
|
function stripHtml(html) {
|
|
|
|
|
@ -2780,7 +1911,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
|
|
fetchAllDocuments();
|
|
|
|
|
document.getElementById('notes-view').classList.remove('hidden');
|
|
|
|
|
document.getElementById('qa-view').classList.add('hidden');
|
|
|
|
|
renderDynamicCategoryFilter();
|
|
|
|
|
renderGraphCategoryFilter(); // 新增:初始化知识图谱分类选择框
|
|
|
|
|
const searchTerm = noteSearchInput.value.trim().toLowerCase();
|
|
|
|
|
filterNotesByCategoryAndSearch(searchTerm);
|
|
|
|
|
|