打开 article-manager.html
按 F12 打开开发者工具,切换到 Console 标签
- 点击"+ 新建文章"
- 或点击左侧列表中的文章
- 输入标题
- 输入分类
- 输入摘要
- 在编辑器中输入内容
点击底部的"🧪 测试保存"按钮
控制台应该显示:
=== 保存功能测试 ===
当前文章: {id: 123, title: "...", ...}
文章数组: [{...}, {...}]
Quill 编辑器: Quill {...}
标题元素: <input id="input-title">
分类元素: <input id="input-category">
标题值: "我的文章"
分类值: "技术"
开始保存文章: 123
文章数据已更新: {...}
已保存到 localStorage
列表已更新
localStorage 中的数据: "[{...}]"
点击"💾 保存"按钮
应该看到:
- 状态栏显示"✅ 已保存"
- 控制台显示保存日志
- 左侧列表更新
- 在编辑器中输入内容
- 等待 2 秒
- 状态栏应该显示"💾 自动保存"
可能原因:
- 没有选中文章
- 表单元素未加载
- JavaScript 错误
排查方法:
- 查看控制台是否有错误
- 点击"🧪 测试保存"查看详细信息
- 确认是否选中了文章(左侧列表有蓝色高亮)
可能原因:
- Quill 编辑器未初始化
- 事件监听器未绑定
排查方法:
- 在控制台输入:
quillEditor - 应该显示 Quill 对象,而不是
null - 在编辑器中输入内容,查看控制台是否有"触发自动保存"
可能原因:
- localStorage 被禁用
- 浏览器隐私模式
排查方法:
- 在控制台输入:
localStorage.getItem('articles') - 应该显示 JSON 字符串
- 检查浏览器设置,确保允许 localStorage
可能原因:
updateStatus函数有问题- 元素 ID 不匹配
排查方法:
- 在控制台输入:
document.getElementById('status-text') - 应该返回元素,而不是
null
在浏览器控制台运行以下命令:
console.log('当前文章:', currentArticle);console.log('所有文章:', articles);console.log('Quill 编辑器:', quillEditor);
console.log('编辑器内容:', quillEditor?.root.innerHTML);saveCurrentArticle(false);console.log('localStorage:', localStorage.getItem('articles'));scheduleAutoSave();- ✅ 添加了 try-catch 错误捕获
- ✅ 检查必需元素是否存在
- ✅ 详细的错误提示
- ✅ 每个步骤都有 console.log
- ✅ 方便追踪执行过程
- ✅ 快速定位问题
- ✅ "🧪 测试保存"按钮
- ✅ 显示详细的调试信息
- ✅ 验证保存结果
- ✅ 创建了
handleInputChange()函数 - ✅ 同时触发预览和自动保存
- ✅ 所有输入框使用相同的处理
-
手动保存
- 点击"💾 保存"
- 状态栏显示"✅ 已保存"
- 控制台显示保存日志
- 左侧列表更新
-
自动保存
- 输入内容后等待 2 秒
- 状态栏显示"💾 自动保存"
- 控制台显示"触发自动保存"
-
数据持久化
- 刷新页面
- 文章列表仍然存在
- 点击文章,内容正确显示
// 在控制台运行
localStorage.clear();
location.reload();- 确保允许 localStorage
- 不要使用隐私/无痕模式
- 检查浏览器版本(建议使用最新版)
在控制台查看是否有红色错误信息,并提供给我。
1. 打开 article-manager.html
2. 点击"+ 新建文章"
3. 填写标题、分类、摘要
4. 在编辑器中输入内容
5. 等待 2 秒(自动保存)
6. 或点击"💾 保存"(手动保存)
7. 状态栏显示"已保存"
8. 点击"📄 生成 HTML"
1. 刷新页面(F5)
2. 左侧列表应该显示文章
3. 点击文章
4. 内容应该正确显示
现在文章管理器有了:
- ✅ 详细的日志 - 方便调试
- ✅ 错误处理 - 捕获并提示错误
- ✅ 测试功能 - 快速验证保存
- ✅ 统一的输入处理 - 自动保存和预览
- ✅ 状态提示 - 实时反馈
请打开文章管理器,按 F12 查看控制台,然后点击"🧪 测试保存"按钮,告诉我控制台显示了什么!
错误信息:
Failed to execute 'setItem' on 'Storage': Setting the value of 'articles' exceeded the quota.
原因:
- localStorage 存储限制为 5-10MB
- 图片转换为 base64 后体积增加约 33%
- 大图片会快速占满存储空间
- 插入图片时自动检测大小
- 超过 500KB 自动压缩
- 压缩参数:最大宽度 1200px,质量 80%
- 显示压缩前后的大小对比
测试方法:
- 在编辑器中点击图片按钮
- 选择一张大图片(> 1MB)
- 查看控制台输出:
原始图片大小:3.74 MB
图片压缩完成:
原始大小:4956.23 KB
压缩后大小:856.45 KB
压缩率:82.72%
- 保存前自动检查空间是否足够
- 显示详细的错误信息和建议
测试方法:
- 打开控制台(F12)
- 查看页面加载时的存储信息:
=== 存储空间使用情况 ===
总使用:3.45 MB
文章数据:3.20 MB
使用率:69.0%
- 使用率超过 70% 时自动显示警告
- 右上角显示橙色提示框
- 建议使用图床服务
在控制台运行:
let totalSize = 0;
for (let key in localStorage) {
if (localStorage.hasOwnProperty(key)) {
totalSize += localStorage[key].length + key.length;
}
}
console.log('总使用:', (totalSize / 1024 / 1024).toFixed(2), 'MB');const articles = localStorage.getItem('articles');
if (articles) {
console.log('文章数据大小:', (articles.length / 1024 / 1024).toFixed(2), 'MB');
}const articles = JSON.parse(localStorage.getItem('articles') || '[]');
let imageCount = 0;
let imageSize = 0;
articles.forEach(article => {
const matches = article.content.match(/<img[^>]+src="data:image[^"]+"/g);
if (matches) {
imageCount += matches.length;
matches.forEach(match => {
imageSize += match.length;
});
}
});
console.log('图片数量:', imageCount);
console.log('图片总大小:', (imageSize / 1024 / 1024).toFixed(2), 'MB');详见 IMAGE_STORAGE_GUIDE.md
推荐的免费图床:
- Imgur: https://imgur.com
- SM.MS: https://sm.ms
- 路过图床: https://imgse.com
- 打开
test-image-compression.html - 上传图片测试压缩效果
- 下载压缩后的图片
- 在文章中替换原图片
// 删除所有文章(谨慎使用!)
localStorage.removeItem('articles');
location.reload();- 打开
test-image-compression.html - 上传图片
- 查看压缩结果
- 下载压缩后的图片
// 测试压缩函数
async function testCompression() {
const input = document.createElement('input');
input.type = 'file';
input.accept = 'image/*';
input.onchange = async (e) => {
const file = e.target.files[0];
const reader = new FileReader();
reader.onload = async (event) => {
const original = event.target.result;
console.log('原始大小:', (original.length / 1024).toFixed(2), 'KB');
// 这里需要 compressImage 函数
const compressed = await compressImage(original, 1200, 0.8);
console.log('压缩后大小:', (compressed.length / 1024).toFixed(2), 'KB');
console.log('压缩率:', ((1 - compressed.length / original.length) * 100).toFixed(2), '%');
};
reader.readAsDataURL(file);
};
input.click();
}
testCompression();-
✅ 优先使用图床 URL
- 不占用 localStorage 空间
- 加载速度更快
- 没有大小限制
-
✅ 压缩后再上传
- 使用在线工具压缩
- 目标:< 200KB
- 保持合理的视觉质量
-
❌ 避免直接插入大图片
- 不要插入 > 1MB 的图片
- 会快速占满存储空间
- 可能导致保存失败
- 打开编辑器时查看控制台
- 注意右上角的警告提示
- 使用率超过 70% 时及时清理
- 删除不需要的旧文章
- 将 base64 图片替换为图床链接
- 导出文章备份
- 清空 localStorage 重新开始
方法 1:导出现有数据
// 导出所有文章
const articles = localStorage.getItem('articles');
const blob = new Blob([articles], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'articles-backup.json';
a.click();方法 2:清空存储
// 备份后清空
localStorage.clear();
location.reload();方法 3:删除最大的文章
const articles = JSON.parse(localStorage.getItem('articles') || '[]');
articles.sort((a, b) => JSON.stringify(b).length - JSON.stringify(a).length);
console.log('最大的文章:', articles[0].title,
(JSON.stringify(articles[0]).length / 1024).toFixed(2), 'KB');
// 手动删除这篇文章在控制台运行以下代码,查看详细的存储分析:
function analyzeStorage() {
console.log('=== 存储空间详细分析 ===\n');
// 总体统计
let total = 0;
const items = {};
for (let key in localStorage) {
if (localStorage.hasOwnProperty(key)) {
const size = localStorage[key].length + key.length;
total += size;
items[key] = size;
}
}
console.log('总使用:', (total / 1024 / 1024).toFixed(2), 'MB');
console.log('预估限制:5-10 MB');
console.log('使用率:', ((total / (5 * 1024 * 1024)) * 100).toFixed(1), '%\n');
// 各项统计
console.log('各项数据大小:');
Object.entries(items)
.sort((a, b) => b[1] - a[1])
.forEach(([key, size]) => {
console.log(` ${key}: ${(size / 1024).toFixed(2)} KB`);
});
// 文章统计
const articles = JSON.parse(localStorage.getItem('articles') || '[]');
console.log('\n文章统计:');
console.log(' 总数:', articles.length);
if (articles.length > 0) {
const sizes = articles.map(a => JSON.stringify(a).length);
const avgSize = sizes.reduce((a, b) => a + b, 0) / sizes.length;
const maxSize = Math.max(...sizes);
const maxArticle = articles[sizes.indexOf(maxSize)];
console.log(' 平均大小:', (avgSize / 1024).toFixed(2), 'KB');
console.log(' 最大文章:', maxArticle.title, '-', (maxSize / 1024).toFixed(2), 'KB');
// 统计图片
let totalImages = 0;
articles.forEach(article => {
const matches = article.content.match(/<img[^>]+src="data:image/g);
if (matches) totalImages += matches.length;
});
console.log(' 图片总数:', totalImages);
}
console.log('\n=== 分析完成 ===');
}
analyzeStorage();现在文章管理器有了完善的图片处理功能:
- ✅ 自动压缩 - 大图片自动压缩到合理大小
- ✅ 空间检查 - 保存前检查是否有足够空间
- ✅ 友好提示 - 详细的错误信息和建议
- ✅ 空间监控 - 实时显示存储使用情况
- ✅ 测试工具 - 独立的图片压缩测试页面
推荐阅读:
IMAGE_STORAGE_GUIDE.md- 详细的图片存储解决方案test-image-compression.html- 图片压缩测试工具