补充主题静态资源与归档模板
This commit is contained in:
@@ -0,0 +1,255 @@
|
||||
/**
|
||||
* AKSRT Page Edit — 前台编辑增强
|
||||
*
|
||||
* 提供前台编辑页面的增强功能:
|
||||
* - 自动保存草稿
|
||||
* - 字数统计
|
||||
* - 标签建议
|
||||
* - 快捷键支持
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var DRAFT_KEY_PREFIX = 'aksrt_draft_';
|
||||
|
||||
/**
|
||||
* 初始化页面编辑增强
|
||||
*
|
||||
* @param {Object} options 配置项
|
||||
*/
|
||||
function PageEdit(options) {
|
||||
this.options = Object.assign({
|
||||
formSelector: 'form[data-aksrt-edit]',
|
||||
titleSelector: 'input[name="title"], input[name="topic_title"]',
|
||||
contentSelector: 'textarea[name="content"], textarea[name="topic_content"]',
|
||||
tagsSelector: 'input[name="tags"], input[name="topic_tags"]',
|
||||
autoSaveInterval: 30000, // 30秒
|
||||
maxTitleLength: 200,
|
||||
maxContentLength: 100000,
|
||||
draftKey: '',
|
||||
}, options || {});
|
||||
|
||||
this.form = document.querySelector(this.options.formSelector);
|
||||
if (!this.form) return;
|
||||
|
||||
this.autoSaveTimer = null;
|
||||
this.lastSavedContent = '';
|
||||
this.init();
|
||||
}
|
||||
|
||||
PageEdit.prototype.init = function () {
|
||||
var self = this;
|
||||
|
||||
this.titleInput = this.form.querySelector(this.options.titleSelector);
|
||||
this.contentTextarea = this.form.querySelector(this.options.contentSelector);
|
||||
this.tagsInput = this.form.querySelector(this.options.tagsSelector);
|
||||
|
||||
// 字数统计
|
||||
this.initWordCount();
|
||||
|
||||
// 自动保存草稿
|
||||
this.initAutoSave();
|
||||
|
||||
// 快捷键
|
||||
this.initShortcuts();
|
||||
|
||||
// 标签输入增强
|
||||
this.initTagInput();
|
||||
|
||||
// 表单提交时清除草稿
|
||||
this.form.addEventListener('submit', function () {
|
||||
self.clearDraft();
|
||||
});
|
||||
|
||||
// 页面离开提醒
|
||||
window.addEventListener('beforeunload', function (e) {
|
||||
if (self.hasUnsavedChanges()) {
|
||||
e.preventDefault();
|
||||
e.returnValue = '您有未保存的修改,确定要离开吗?';
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
PageEdit.prototype.initWordCount = function () {
|
||||
var self = this;
|
||||
|
||||
// 创建字数统计显示
|
||||
this.wordCountEl = document.createElement('div');
|
||||
this.wordCountEl.className = 'aksrt-page-edit__word-count';
|
||||
|
||||
if (this.titleInput) {
|
||||
var titleCount = document.createElement('span');
|
||||
titleCount.className = 'aksrt-page-edit__title-count';
|
||||
this.titleInput.parentNode.appendChild(titleCount);
|
||||
|
||||
this.titleInput.addEventListener('input', function () {
|
||||
var len = self.titleInput.value.length;
|
||||
titleCount.textContent = len + '/' + self.options.maxTitleLength;
|
||||
titleCount.classList.toggle('aksrt-page-edit__count--warn', len > self.options.maxTitleLength * 0.9);
|
||||
titleCount.classList.toggle('aksrt-page-edit__count--error', len > self.options.maxTitleLength);
|
||||
});
|
||||
}
|
||||
|
||||
if (this.contentTextarea) {
|
||||
this.contentTextarea.parentNode.appendChild(this.wordCountEl);
|
||||
this.updateWordCount();
|
||||
|
||||
this.contentTextarea.addEventListener('input', function () {
|
||||
self.updateWordCount();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
PageEdit.prototype.updateWordCount = function () {
|
||||
if (!this.contentTextarea || !this.wordCountEl) return;
|
||||
|
||||
var text = this.contentTextarea.value;
|
||||
var chars = text.length;
|
||||
var words = text.trim() ? text.trim().split(/\s+/).length : 0;
|
||||
var chinese = (text.match(/[一-鿿]/g) || []).length;
|
||||
|
||||
this.wordCountEl.textContent = '字数: ' + chars + ' | 词数: ' + words + ' | 中文: ' + chinese;
|
||||
this.wordCountEl.classList.toggle('aksrt-page-edit__count--warn', chars > this.options.maxContentLength * 0.9);
|
||||
this.wordCountEl.classList.toggle('aksrt-page-edit__count--error', chars > this.options.maxContentLength);
|
||||
};
|
||||
|
||||
PageEdit.prototype.initAutoSave = function () {
|
||||
var self = this;
|
||||
|
||||
if (!this.options.draftKey) {
|
||||
// 从页面 URL 生成 draft key
|
||||
this.options.draftKey = DRAFT_KEY_PREFIX + (window.location.pathname.replace(/\//g, '_') || 'page');
|
||||
}
|
||||
|
||||
// 恢复草稿
|
||||
this.restoreDraft();
|
||||
|
||||
// 定时自动保存
|
||||
if (this.options.autoSaveInterval > 0) {
|
||||
this.autoSaveTimer = setInterval(function () {
|
||||
self.saveDraft();
|
||||
}, this.options.autoSaveInterval);
|
||||
}
|
||||
};
|
||||
|
||||
PageEdit.prototype.saveDraft = function () {
|
||||
if (!this.form) return;
|
||||
|
||||
var data = {};
|
||||
if (this.titleInput) data.title = this.titleInput.value;
|
||||
if (this.contentTextarea) data.content = this.contentTextarea.value;
|
||||
if (this.tagsInput) data.tags = this.tagsInput.value;
|
||||
|
||||
var content = JSON.stringify(data);
|
||||
if (content === this.lastSavedContent) return;
|
||||
|
||||
try {
|
||||
localStorage.setItem(this.options.draftKey, content);
|
||||
this.lastSavedContent = content;
|
||||
|
||||
// 显示保存提示
|
||||
var toast = document.getElementById('aksrt-draft-toast');
|
||||
if (!toast) {
|
||||
toast = document.createElement('div');
|
||||
toast.id = 'aksrt-draft-toast';
|
||||
toast.className = 'aksrt-page-edit__toast';
|
||||
document.body.appendChild(toast);
|
||||
}
|
||||
toast.textContent = '草稿已自动保存';
|
||||
toast.classList.add('aksrt-page-edit__toast--visible');
|
||||
setTimeout(function () {
|
||||
toast.classList.remove('aksrt-page-edit__toast--visible');
|
||||
}, 2000);
|
||||
} catch (e) {
|
||||
console.error('[AKSRT PageEdit] 保存草稿失败:', e);
|
||||
}
|
||||
};
|
||||
|
||||
PageEdit.prototype.restoreDraft = function () {
|
||||
try {
|
||||
var saved = localStorage.getItem(this.options.draftKey);
|
||||
if (!saved) return;
|
||||
|
||||
var data = JSON.parse(saved);
|
||||
var hasContent = false;
|
||||
|
||||
if (this.titleInput && data.title && !this.titleInput.value) {
|
||||
this.titleInput.value = data.title;
|
||||
hasContent = true;
|
||||
}
|
||||
if (this.contentTextarea && data.content && !this.contentTextarea.value) {
|
||||
this.contentTextarea.value = data.content;
|
||||
hasContent = true;
|
||||
}
|
||||
if (this.tagsInput && data.tags && !this.tagsInput.value) {
|
||||
this.tagsInput.value = data.tags;
|
||||
hasContent = true;
|
||||
}
|
||||
|
||||
if (hasContent) {
|
||||
this.lastSavedContent = saved;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[AKSRT PageEdit] 恢复草稿失败:', e);
|
||||
}
|
||||
};
|
||||
|
||||
PageEdit.prototype.clearDraft = function () {
|
||||
try {
|
||||
localStorage.removeItem(this.options.draftKey);
|
||||
} catch (e) {}
|
||||
};
|
||||
|
||||
PageEdit.prototype.hasUnsavedChanges = function () {
|
||||
if (!this.form) return false;
|
||||
|
||||
var data = {};
|
||||
if (this.titleInput) data.title = this.titleInput.value;
|
||||
if (this.contentTextarea) data.content = this.contentTextarea.value;
|
||||
if (this.tagsInput) data.tags = this.tagsInput.value;
|
||||
|
||||
return JSON.stringify(data) !== this.lastSavedContent;
|
||||
};
|
||||
|
||||
PageEdit.prototype.initShortcuts = function () {
|
||||
var self = this;
|
||||
|
||||
this.form.addEventListener('keydown', function (e) {
|
||||
// Ctrl+S / Cmd+S 保存草稿
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 's') {
|
||||
e.preventDefault();
|
||||
self.saveDraft();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
PageEdit.prototype.initTagInput = function () {
|
||||
if (!this.tagsInput) return;
|
||||
|
||||
// 回车键添加标签
|
||||
this.tagsInput.addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 自动初始化
|
||||
function autoInit() {
|
||||
var form = document.querySelector('form[data-aksrt-edit]');
|
||||
if (form) {
|
||||
new PageEdit();
|
||||
}
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', autoInit);
|
||||
} else {
|
||||
autoInit();
|
||||
}
|
||||
|
||||
window.AksrtPageEdit = PageEdit;
|
||||
})();
|
||||
Reference in New Issue
Block a user