refactor: 移除开发日志并优化代码格式
fix(api): 添加头像前缀参数支持
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -13,417 +13,405 @@ import styles from '@/styles/main.css?inline';
|
|||||||
* CWDComments 评论组件主类
|
* CWDComments 评论组件主类
|
||||||
*/
|
*/
|
||||||
export class CWDComments {
|
export class CWDComments {
|
||||||
/**
|
/**
|
||||||
* @param {Object} config - 配置对象
|
* @param {Object} config - 配置对象
|
||||||
* @param {string|HTMLElement} config.el - 挂载元素选择器或 DOM 元素
|
* @param {string|HTMLElement} config.el - 挂载元素选择器或 DOM 元素
|
||||||
* @param {string} config.apiBaseUrl - API 基础地址
|
* @param {string} config.apiBaseUrl - API 基础地址
|
||||||
* @param {'light'|'dark'} [config.theme] - 主题(可选)
|
* @param {'light'|'dark'} [config.theme] - 主题(可选)
|
||||||
* @param {number} [config.pageSize] - 每页评论数(可选,默认 20)
|
* @param {number} [config.pageSize] - 每页评论数(可选,默认 20)
|
||||||
*
|
*
|
||||||
* 以下字段由组件自动推导或从后端读取,无需通过 config 传入:
|
* 以下字段由组件自动推导或从后端读取,无需通过 config 传入:
|
||||||
* - postSlug:window.location.origin + window.location.pathname
|
* - postSlug:window.location.origin + window.location.pathname
|
||||||
* - postTitle:document.title 或 postSlug
|
* - postTitle:document.title 或 postSlug
|
||||||
* - postUrl:window.location.href
|
* - postUrl:window.location.href
|
||||||
* - avatarPrefix/adminEmail/adminBadge:通过 /api/config/comments 接口获取
|
* - avatarPrefix/adminEmail/adminBadge:通过 /api/config/comments 接口获取
|
||||||
*/
|
*/
|
||||||
constructor(config) {
|
constructor(config) {
|
||||||
this.config = { ...config };
|
this.config = { ...config };
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
this.config.postSlug = window.location.origin + window.location.pathname;
|
this.config.postSlug = window.location.origin + window.location.pathname;
|
||||||
}
|
}
|
||||||
if (typeof document !== 'undefined') {
|
if (typeof document !== 'undefined') {
|
||||||
this.config.postTitle = document.title || this.config.postSlug;
|
this.config.postTitle = document.title || this.config.postSlug;
|
||||||
}
|
}
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
this.config.postUrl = window.location.href;
|
this.config.postUrl = window.location.href;
|
||||||
}
|
}
|
||||||
this.hostElement = this._resolveElement(config.el);
|
this.hostElement = this._resolveElement(config.el);
|
||||||
this.shadowRoot = null;
|
this.shadowRoot = null;
|
||||||
this.mountPoint = null;
|
this.mountPoint = null;
|
||||||
this.commentForm = null;
|
this.commentForm = null;
|
||||||
this.commentList = null;
|
this.commentList = null;
|
||||||
this.store = null;
|
this.store = null;
|
||||||
this.unsubscribe = null;
|
this.unsubscribe = null;
|
||||||
|
|
||||||
// 初始加载标志
|
// 初始加载标志
|
||||||
this._mounted = false;
|
this._mounted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解析挂载元素
|
* 解析挂载元素
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_resolveElement(el) {
|
_resolveElement(el) {
|
||||||
if (typeof el === 'string') {
|
if (typeof el === 'string') {
|
||||||
const element = document.querySelector(el);
|
const element = document.querySelector(el);
|
||||||
if (!element) {
|
if (!element) {
|
||||||
throw new Error(`元素未找到: ${el}`);
|
throw new Error(`元素未找到: ${el}`);
|
||||||
}
|
}
|
||||||
if (!(element instanceof HTMLElement)) {
|
if (!(element instanceof HTMLElement)) {
|
||||||
throw new Error(`目标不是 HTMLElement: ${el}`);
|
throw new Error(`目标不是 HTMLElement: ${el}`);
|
||||||
}
|
}
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _loadServerConfig() {
|
async _loadServerConfig() {
|
||||||
try {
|
try {
|
||||||
const base = this.config.apiBaseUrl;
|
const base = this.config.apiBaseUrl;
|
||||||
if (!base) {
|
if (!base) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
const apiBaseUrl = base.replace(/\/$/, '');
|
const apiBaseUrl = base.replace(/\/$/, '');
|
||||||
const res = await fetch(`${apiBaseUrl}/api/config/comments`);
|
const res = await fetch(`${apiBaseUrl}/api/config/comments`);
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
return {
|
return {
|
||||||
adminEmail: data.adminEmail || '',
|
adminEmail: data.adminEmail || '',
|
||||||
adminBadge: data.adminBadge || '',
|
adminBadge: data.adminBadge || '',
|
||||||
adminEnabled: !!data.adminEnabled,
|
adminEnabled: !!data.adminEnabled,
|
||||||
avatarPrefix: data.avatarPrefix || ''
|
avatarPrefix: data.avatarPrefix || '',
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('[CWDComments] 加载服务端评论配置失败:', e);
|
return {};
|
||||||
return {};
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 挂载组件
|
* 挂载组件
|
||||||
*/
|
*/
|
||||||
mount() {
|
mount() {
|
||||||
if (this._mounted) {
|
if (this._mounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建 Shadow DOM
|
// 创建 Shadow DOM
|
||||||
this.shadowRoot = this.hostElement.attachShadow({ mode: 'open' });
|
this.shadowRoot = this.hostElement.attachShadow({ mode: 'open' });
|
||||||
|
|
||||||
// 注入样式
|
// 注入样式
|
||||||
const styleElement = document.createElement('style');
|
const styleElement = document.createElement('style');
|
||||||
if (typeof styles === 'string') {
|
if (typeof styles === 'string') {
|
||||||
styleElement.textContent = styles;
|
styleElement.textContent = styles;
|
||||||
} else if (styles && typeof styles === 'object' && 'default' in styles) {
|
} else if (styles && typeof styles === 'object' && 'default' in styles) {
|
||||||
styleElement.textContent = styles.default;
|
styleElement.textContent = styles.default;
|
||||||
}
|
}
|
||||||
this.shadowRoot.appendChild(styleElement);
|
this.shadowRoot.appendChild(styleElement);
|
||||||
|
|
||||||
// 创建容器
|
// 创建容器
|
||||||
this.mountPoint = document.createElement('div');
|
this.mountPoint = document.createElement('div');
|
||||||
this.mountPoint.className = 'cwd-comments-container';
|
this.mountPoint.className = 'cwd-comments-container';
|
||||||
this.shadowRoot.appendChild(this.mountPoint);
|
this.shadowRoot.appendChild(this.mountPoint);
|
||||||
|
|
||||||
// 设置主题
|
// 设置主题
|
||||||
if (this.config.theme) {
|
if (this.config.theme) {
|
||||||
this.mountPoint.setAttribute('data-theme', this.config.theme);
|
this.mountPoint.setAttribute('data-theme', this.config.theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const serverConfig = await this._loadServerConfig();
|
const serverConfig = await this._loadServerConfig();
|
||||||
if (!this._mounted) {
|
if (!this._mounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (serverConfig.avatarPrefix) {
|
if (serverConfig.avatarPrefix) {
|
||||||
this.config.avatarPrefix = serverConfig.avatarPrefix;
|
this.config.avatarPrefix = serverConfig.avatarPrefix;
|
||||||
}
|
}
|
||||||
if (serverConfig.adminEnabled && serverConfig.adminEmail) {
|
if (serverConfig.adminEnabled && serverConfig.adminEmail) {
|
||||||
this.config.adminEmail = serverConfig.adminEmail;
|
this.config.adminEmail = serverConfig.adminEmail;
|
||||||
}
|
}
|
||||||
if (serverConfig.adminEnabled && serverConfig.adminBadge) {
|
if (serverConfig.adminEnabled && serverConfig.adminBadge) {
|
||||||
this.config.adminBadge = serverConfig.adminBadge;
|
this.config.adminBadge = serverConfig.adminBadge;
|
||||||
}
|
}
|
||||||
|
|
||||||
const api = createApiClient(this.config);
|
const api = createApiClient(this.config);
|
||||||
this.store = createCommentStore(
|
this.store = createCommentStore(this.config, api.fetchComments.bind(api), api.submitComment.bind(api));
|
||||||
this.config,
|
|
||||||
api.fetchComments.bind(api),
|
|
||||||
api.submitComment.bind(api)
|
|
||||||
);
|
|
||||||
|
|
||||||
this.unsubscribe = this.store.store.subscribe((state) => {
|
this.unsubscribe = this.store.store.subscribe((state) => {
|
||||||
this._onStateChange(state);
|
this._onStateChange(state);
|
||||||
});
|
});
|
||||||
|
|
||||||
this._render();
|
this._render();
|
||||||
this.store.loadComments();
|
this.store.loadComments();
|
||||||
})();
|
})();
|
||||||
|
|
||||||
this._mounted = true;
|
this._mounted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 卸载组件
|
* 卸载组件
|
||||||
*/
|
*/
|
||||||
unmount() {
|
unmount() {
|
||||||
if (!this._mounted) {
|
if (!this._mounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 销毁组件
|
// 销毁组件
|
||||||
if (this.commentForm) {
|
if (this.commentForm) {
|
||||||
this.commentForm.destroy();
|
this.commentForm.destroy();
|
||||||
this.commentForm = null;
|
this.commentForm = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.commentList) {
|
if (this.commentList) {
|
||||||
this.commentList.destroy();
|
this.commentList.destroy();
|
||||||
this.commentList = null;
|
this.commentList = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 取消订阅
|
// 取消订阅
|
||||||
if (this.unsubscribe) {
|
if (this.unsubscribe) {
|
||||||
this.unsubscribe();
|
this.unsubscribe();
|
||||||
this.unsubscribe = null;
|
this.unsubscribe = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 移除 Shadow DOM - 通过替换所有子节点
|
// 移除 Shadow DOM - 通过替换所有子节点
|
||||||
if (this.hostElement) {
|
if (this.hostElement) {
|
||||||
// Shadow DOM 会在清空子节点时自动移除
|
// Shadow DOM 会在清空子节点时自动移除
|
||||||
while (this.hostElement.firstChild) {
|
while (this.hostElement.firstChild) {
|
||||||
this.hostElement.removeChild(this.hostElement.firstChild);
|
this.hostElement.removeChild(this.hostElement.firstChild);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.shadowRoot = null;
|
this.shadowRoot = null;
|
||||||
this.mountPoint = null;
|
this.mountPoint = null;
|
||||||
this.store = null;
|
this.store = null;
|
||||||
this._mounted = false;
|
this._mounted = false;
|
||||||
|
}
|
||||||
|
|
||||||
console.log('[CWDComments] 组件已卸载');
|
/**
|
||||||
}
|
* 渲染组件
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_render() {
|
||||||
|
if (!this.mountPoint) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
const state = this.store.store.getState();
|
||||||
* 渲染组件
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_render() {
|
|
||||||
if (!this.mountPoint) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const state = this.store.store.getState();
|
// 创建评论表单
|
||||||
|
if (!this.commentForm) {
|
||||||
|
this.commentForm = new CommentForm(this.mountPoint, {
|
||||||
|
form: state.form,
|
||||||
|
formErrors: state.formErrors,
|
||||||
|
submitting: state.submitting,
|
||||||
|
onSubmit: () => this._handleSubmit(),
|
||||||
|
onFieldChange: (field, value) => this.store.updateFormField(field, value),
|
||||||
|
});
|
||||||
|
this.commentForm.render();
|
||||||
|
}
|
||||||
|
|
||||||
// 创建评论表单
|
// 创建错误提示
|
||||||
if (!this.commentForm) {
|
const existingError = this.mountPoint.querySelector('.cwd-error-inline');
|
||||||
this.commentForm = new CommentForm(this.mountPoint, {
|
if (state.error) {
|
||||||
form: state.form,
|
if (!existingError) {
|
||||||
formErrors: state.formErrors,
|
const errorEl = document.createElement('div');
|
||||||
submitting: state.submitting,
|
errorEl.className = 'cwd-error-inline';
|
||||||
onSubmit: () => this._handleSubmit(),
|
errorEl.innerHTML = `
|
||||||
onFieldChange: (field, value) => this.store.updateFormField(field, value)
|
|
||||||
});
|
|
||||||
this.commentForm.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建错误提示
|
|
||||||
const existingError = this.mountPoint.querySelector('.cwd-error-inline');
|
|
||||||
if (state.error) {
|
|
||||||
if (!existingError) {
|
|
||||||
const errorEl = document.createElement('div');
|
|
||||||
errorEl.className = 'cwd-error-inline';
|
|
||||||
errorEl.innerHTML = `
|
|
||||||
<span>${state.error}</span>
|
<span>${state.error}</span>
|
||||||
<button type="button" class="cwd-error-close" data-action="clear-error">✕</button>
|
<button type="button" class="cwd-error-close" data-action="clear-error">✕</button>
|
||||||
`;
|
`;
|
||||||
errorEl.querySelector('[data-action="clear-error"]').addEventListener('click', () => {
|
errorEl.querySelector('[data-action="clear-error"]').addEventListener('click', () => {
|
||||||
this.store.clearError();
|
this.store.clearError();
|
||||||
});
|
});
|
||||||
this.mountPoint.insertBefore(errorEl, this.mountPoint.firstChild);
|
this.mountPoint.insertBefore(errorEl, this.mountPoint.firstChild);
|
||||||
}
|
}
|
||||||
} else if (existingError) {
|
} else if (existingError) {
|
||||||
existingError.remove();
|
existingError.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建头部统计
|
// 创建头部统计
|
||||||
let header = this.mountPoint.querySelector('.cwd-comments-header');
|
let header = this.mountPoint.querySelector('.cwd-comments-header');
|
||||||
if (!header) {
|
if (!header) {
|
||||||
header = document.createElement('div');
|
header = document.createElement('div');
|
||||||
header.className = 'cwd-comments-header';
|
header.className = 'cwd-comments-header';
|
||||||
header.innerHTML = `
|
header.innerHTML = `
|
||||||
<h3 class="cwd-comments-count">
|
<h3 class="cwd-comments-count">
|
||||||
共 <span class="cwd-comments-count-number">0</span> 条评论
|
共 <span class="cwd-comments-count-number">0</span> 条评论
|
||||||
</h3>
|
</h3>
|
||||||
`;
|
`;
|
||||||
this.mountPoint.appendChild(header);
|
this.mountPoint.appendChild(header);
|
||||||
}
|
}
|
||||||
const countEl = header.querySelector('.cwd-comments-count-number');
|
const countEl = header.querySelector('.cwd-comments-count-number');
|
||||||
if (countEl) {
|
if (countEl) {
|
||||||
countEl.textContent = state.pagination.totalCount;
|
countEl.textContent = state.pagination.totalCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建评论列表
|
// 创建评论列表
|
||||||
if (!this.commentList) {
|
if (!this.commentList) {
|
||||||
const listContainer = document.createElement('div');
|
const listContainer = document.createElement('div');
|
||||||
this.mountPoint.appendChild(listContainer);
|
this.mountPoint.appendChild(listContainer);
|
||||||
|
|
||||||
this.commentList = new CommentList(listContainer, {
|
this.commentList = new CommentList(listContainer, {
|
||||||
comments: state.comments,
|
comments: state.comments,
|
||||||
loading: state.loading,
|
loading: state.loading,
|
||||||
error: null,
|
error: null,
|
||||||
currentPage: state.pagination.page,
|
currentPage: state.pagination.page,
|
||||||
totalPages: this.store.getTotalPages(),
|
totalPages: this.store.getTotalPages(),
|
||||||
replyingTo: state.replyingTo,
|
replyingTo: state.replyingTo,
|
||||||
replyContent: state.replyContent,
|
replyContent: state.replyContent,
|
||||||
replyError: state.replyError,
|
replyError: state.replyError,
|
||||||
submitting: state.submitting,
|
submitting: state.submitting,
|
||||||
adminEmail: this.config.adminEmail,
|
adminEmail: this.config.adminEmail,
|
||||||
adminBadge: this.config.adminBadge || '博主',
|
adminBadge: this.config.adminBadge || '博主',
|
||||||
onRetry: () => this.store.loadComments(),
|
onRetry: () => this.store.loadComments(),
|
||||||
onReply: (commentId) => this.store.startReply(commentId),
|
onReply: (commentId) => this.store.startReply(commentId),
|
||||||
onSubmitReply: (commentId) => this.store.submitReply(commentId),
|
onSubmitReply: (commentId) => this.store.submitReply(commentId),
|
||||||
onCancelReply: () => this.store.cancelReply(),
|
onCancelReply: () => this.store.cancelReply(),
|
||||||
onUpdateReplyContent: (content) => this.store.updateReplyContent(content),
|
onUpdateReplyContent: (content) => this.store.updateReplyContent(content),
|
||||||
onClearReplyError: () => this.store.clearReplyError(),
|
onClearReplyError: () => this.store.clearReplyError(),
|
||||||
onPrevPage: () => this.store.goToPage(state.pagination.page - 1),
|
onPrevPage: () => this.store.goToPage(state.pagination.page - 1),
|
||||||
onNextPage: () => this.store.goToPage(state.pagination.page + 1),
|
onNextPage: () => this.store.goToPage(state.pagination.page + 1),
|
||||||
onGoToPage: (page) => this.store.goToPage(page)
|
onGoToPage: (page) => this.store.goToPage(page),
|
||||||
});
|
});
|
||||||
this.commentList.render();
|
this.commentList.render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 状态变化处理
|
* 状态变化处理
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_onStateChange(state, prevState) {
|
_onStateChange(state, prevState) {
|
||||||
if (!this._mounted) {
|
if (!this._mounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据回复状态显示/隐藏主评论表单
|
// 根据回复状态显示/隐藏主评论表单
|
||||||
if (this.commentForm?.elements?.root) {
|
if (this.commentForm?.elements?.root) {
|
||||||
const formRoot = this.commentForm.elements.root;
|
const formRoot = this.commentForm.elements.root;
|
||||||
if (state.replyingTo !== null) {
|
if (state.replyingTo !== null) {
|
||||||
formRoot.style.display = 'none';
|
formRoot.style.display = 'none';
|
||||||
} else {
|
} else {
|
||||||
formRoot.style.display = '';
|
formRoot.style.display = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新评论表单
|
// 更新评论表单
|
||||||
if (this.commentForm) {
|
if (this.commentForm) {
|
||||||
this.commentForm.setProps({
|
this.commentForm.setProps({
|
||||||
form: state.form,
|
form: state.form,
|
||||||
formErrors: state.formErrors,
|
formErrors: state.formErrors,
|
||||||
submitting: state.submitting
|
submitting: state.submitting,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新错误提示
|
// 更新错误提示
|
||||||
const existingError = this.mountPoint?.querySelector('.cwd-error-inline');
|
const existingError = this.mountPoint?.querySelector('.cwd-error-inline');
|
||||||
if (state.error) {
|
if (state.error) {
|
||||||
if (!existingError) {
|
if (!existingError) {
|
||||||
const errorEl = document.createElement('div');
|
const errorEl = document.createElement('div');
|
||||||
errorEl.className = 'cwd-error-inline';
|
errorEl.className = 'cwd-error-inline';
|
||||||
errorEl.innerHTML = `
|
errorEl.innerHTML = `
|
||||||
<span>${state.error}</span>
|
<span>${state.error}</span>
|
||||||
<button type="button" class="cwd-error-close" data-action="clear-error">✕</button>
|
<button type="button" class="cwd-error-close" data-action="clear-error">✕</button>
|
||||||
`;
|
`;
|
||||||
errorEl.querySelector('[data-action="clear-error"]').addEventListener('click', () => {
|
errorEl.querySelector('[data-action="clear-error"]').addEventListener('click', () => {
|
||||||
this.store.clearError();
|
this.store.clearError();
|
||||||
});
|
});
|
||||||
this.mountPoint?.insertBefore(errorEl, this.mountPoint.firstChild);
|
this.mountPoint?.insertBefore(errorEl, this.mountPoint.firstChild);
|
||||||
}
|
}
|
||||||
} else if (existingError) {
|
} else if (existingError) {
|
||||||
existingError.remove();
|
existingError.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新头部统计
|
// 更新头部统计
|
||||||
const header = this.mountPoint?.querySelector('.cwd-comments-header');
|
const header = this.mountPoint?.querySelector('.cwd-comments-header');
|
||||||
const countEl = header?.querySelector('.cwd-comments-count-number');
|
const countEl = header?.querySelector('.cwd-comments-count-number');
|
||||||
if (countEl) {
|
if (countEl) {
|
||||||
countEl.textContent = state.pagination.totalCount;
|
countEl.textContent = state.pagination.totalCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新评论列表
|
// 更新评论列表
|
||||||
if (this.commentList) {
|
if (this.commentList) {
|
||||||
this.commentList.setProps({
|
this.commentList.setProps({
|
||||||
comments: state.comments,
|
comments: state.comments,
|
||||||
loading: state.loading,
|
loading: state.loading,
|
||||||
currentPage: state.pagination.page,
|
currentPage: state.pagination.page,
|
||||||
totalPages: this.store.getTotalPages(),
|
totalPages: this.store.getTotalPages(),
|
||||||
replyingTo: state.replyingTo,
|
replyingTo: state.replyingTo,
|
||||||
replyContent: state.replyContent,
|
replyContent: state.replyContent,
|
||||||
replyError: state.replyError,
|
replyError: state.replyError,
|
||||||
submitting: state.submitting
|
submitting: state.submitting,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理评论提交
|
* 处理评论提交
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
async _handleSubmit() {
|
async _handleSubmit() {
|
||||||
const success = await this.store.submitNewComment();
|
const success = await this.store.submitNewComment();
|
||||||
if (success) {
|
if (success) {
|
||||||
// 表单内容已在 store 中清空
|
// 表单内容已在 store 中清空
|
||||||
// 更新表单组件
|
// 更新表单组件
|
||||||
if (this.commentForm) {
|
if (this.commentForm) {
|
||||||
this.commentForm.state.localForm = { ...this.store.store.getState().form };
|
this.commentForm.state.localForm = { ...this.store.store.getState().form };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新配置
|
* 更新配置
|
||||||
* @param {Object} newConfig - 新配置
|
* @param {Object} newConfig - 新配置
|
||||||
*/
|
*/
|
||||||
updateConfig(newConfig) {
|
updateConfig(newConfig) {
|
||||||
const prevConfig = { ...this.config };
|
const prevConfig = { ...this.config };
|
||||||
|
|
||||||
Object.assign(this.config, newConfig);
|
Object.assign(this.config, newConfig);
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
this.config.postSlug = window.location.origin + window.location.pathname;
|
this.config.postSlug = window.location.origin + window.location.pathname;
|
||||||
}
|
}
|
||||||
if (typeof document !== 'undefined') {
|
if (typeof document !== 'undefined') {
|
||||||
this.config.postTitle = document.title || this.config.postSlug;
|
this.config.postTitle = document.title || this.config.postSlug;
|
||||||
}
|
}
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
this.config.postUrl = window.location.href;
|
this.config.postUrl = window.location.href;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新主题
|
// 更新主题
|
||||||
if (newConfig.theme && this.mountPoint) {
|
if (newConfig.theme && this.mountPoint) {
|
||||||
this.mountPoint.setAttribute('data-theme', newConfig.theme);
|
this.mountPoint.setAttribute('data-theme', newConfig.theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
const shouldReload =
|
const shouldReload =
|
||||||
this.config.apiBaseUrl !== prevConfig.apiBaseUrl ||
|
this.config.apiBaseUrl !== prevConfig.apiBaseUrl ||
|
||||||
this.config.pageSize !== prevConfig.pageSize ||
|
this.config.pageSize !== prevConfig.pageSize ||
|
||||||
this.config.postSlug !== prevConfig.postSlug;
|
this.config.postSlug !== prevConfig.postSlug;
|
||||||
|
|
||||||
if (shouldReload) {
|
if (shouldReload) {
|
||||||
const api = createApiClient(this.config);
|
const api = createApiClient(this.config);
|
||||||
|
|
||||||
if (this.unsubscribe) {
|
if (this.unsubscribe) {
|
||||||
this.unsubscribe();
|
this.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.store = createCommentStore(
|
this.store = createCommentStore(this.config, api.fetchComments.bind(api), api.submitComment.bind(api));
|
||||||
this.config,
|
|
||||||
api.fetchComments.bind(api),
|
|
||||||
api.submitComment.bind(api)
|
|
||||||
);
|
|
||||||
|
|
||||||
this.unsubscribe = this.store.store.subscribe((state) => {
|
this.unsubscribe = this.store.store.subscribe((state) => {
|
||||||
this._onStateChange(state);
|
this._onStateChange(state);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.store.loadComments();
|
this.store.loadComments();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取当前配置
|
|
||||||
* @returns {Object}
|
|
||||||
*/
|
|
||||||
getConfig() {
|
|
||||||
return { ...this.config };
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前配置
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
getConfig() {
|
||||||
|
return { ...this.config };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,67 +11,70 @@
|
|||||||
* @param {string} config.postUrl - 文章 URL(可选)
|
* @param {string} config.postUrl - 文章 URL(可选)
|
||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
export function createApiClient(config) {
|
export function createApiClient(config) {
|
||||||
const baseUrl = config.apiBaseUrl.replace(/\/$/, '');
|
const baseUrl = config.apiBaseUrl.replace(/\/$/, '');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取评论列表
|
* 获取评论列表
|
||||||
* @param {number} page - 页码
|
* @param {number} page - 页码
|
||||||
* @param {number} limit - 每页数量
|
* @param {number} limit - 每页数量
|
||||||
* @returns {Promise<Object>}
|
* @returns {Promise<Object>}
|
||||||
*/
|
*/
|
||||||
async function fetchComments(page = 1, limit = 20) {
|
async function fetchComments(page = 1, limit = 20) {
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
post_slug: config.postSlug,
|
post_slug: config.postSlug,
|
||||||
page: page.toString(),
|
page: page.toString(),
|
||||||
limit: limit.toString(),
|
limit: limit.toString(),
|
||||||
nested: 'true'
|
nested: 'true',
|
||||||
});
|
});
|
||||||
|
|
||||||
const response = await fetch(`${baseUrl}/api/comments?${params}`);
|
if (config.avatarPrefix) {
|
||||||
if (!response.ok) {
|
params.set('avatar_prefix', config.avatarPrefix);
|
||||||
throw new Error(`获取评论失败: ${response.status} ${response.statusText}`);
|
}
|
||||||
}
|
|
||||||
console.log('[API] fetchComments response:', response);
|
|
||||||
return response.json();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
const response = await fetch(`${baseUrl}/api/comments?${params}`);
|
||||||
* 提交评论
|
if (!response.ok) {
|
||||||
* @param {Object} data - 评论数据
|
throw new Error(`获取评论失败: ${response.status} ${response.statusText}`);
|
||||||
* @param {string} data.name - 昵称
|
}
|
||||||
* @param {string} data.email - 邮箱
|
return response.json();
|
||||||
* @param {string} data.url - 网址(可选)
|
}
|
||||||
* @param {string} data.content - 评论内容
|
|
||||||
* @param {number} data.parentId - 父评论 ID(可选,用于回复)
|
|
||||||
* @returns {Promise<Object>}
|
|
||||||
*/
|
|
||||||
async function submitComment(data) {
|
|
||||||
const response = await fetch(`${baseUrl}/api/comments`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
post_slug: config.postSlug,
|
|
||||||
post_title: config.postTitle,
|
|
||||||
post_url: config.postUrl,
|
|
||||||
name: data.name,
|
|
||||||
email: data.email,
|
|
||||||
url: data.url || undefined,
|
|
||||||
content: data.content,
|
|
||||||
parent_id: data.parentId
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
/**
|
||||||
throw new Error(`提交评论失败: ${response.status} ${response.statusText}`);
|
* 提交评论
|
||||||
}
|
* @param {Object} data - 评论数据
|
||||||
return response.json();
|
* @param {string} data.name - 昵称
|
||||||
}
|
* @param {string} data.email - 邮箱
|
||||||
|
* @param {string} data.url - 网址(可选)
|
||||||
|
* @param {string} data.content - 评论内容
|
||||||
|
* @param {number} data.parentId - 父评论 ID(可选,用于回复)
|
||||||
|
* @returns {Promise<Object>}
|
||||||
|
*/
|
||||||
|
async function submitComment(data) {
|
||||||
|
const response = await fetch(`${baseUrl}/api/comments`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
post_slug: config.postSlug,
|
||||||
|
post_title: config.postTitle,
|
||||||
|
post_url: config.postUrl,
|
||||||
|
name: data.name,
|
||||||
|
email: data.email,
|
||||||
|
url: data.url || undefined,
|
||||||
|
content: data.content,
|
||||||
|
parent_id: data.parentId,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
if (!response.ok) {
|
||||||
fetchComments,
|
throw new Error(`提交评论失败: ${response.status} ${response.statusText}`);
|
||||||
submitComment
|
}
|
||||||
};
|
return response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
fetchComments,
|
||||||
|
submitComment,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,20 +10,18 @@ const STORAGE_KEY = 'cwd_user_info';
|
|||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
function loadUserInfo() {
|
function loadUserInfo() {
|
||||||
try {
|
try {
|
||||||
const data = localStorage.getItem(STORAGE_KEY);
|
const data = localStorage.getItem(STORAGE_KEY);
|
||||||
if (data) {
|
if (data) {
|
||||||
const parsed = JSON.parse(data);
|
const parsed = JSON.parse(data);
|
||||||
return {
|
return {
|
||||||
name: parsed.name || '',
|
name: parsed.name || '',
|
||||||
email: parsed.email || '',
|
email: parsed.email || '',
|
||||||
url: parsed.url || ''
|
url: parsed.url || '',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {}
|
||||||
console.error('读取用户信息失败:', e);
|
return { name: '', email: '', url: '' };
|
||||||
}
|
|
||||||
return { name: '', email: '', url: '' };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,58 +31,56 @@ function loadUserInfo() {
|
|||||||
* @param {string} url - 网址
|
* @param {string} url - 网址
|
||||||
*/
|
*/
|
||||||
function saveUserInfo(name, email, url) {
|
function saveUserInfo(name, email, url) {
|
||||||
try {
|
try {
|
||||||
const data = { name, email, url };
|
const data = { name, email, url };
|
||||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
|
||||||
} catch (e) {
|
} catch (e) {}
|
||||||
console.error('保存用户信息失败:', e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 简单的 Store 类
|
* 简单的 Store 类
|
||||||
*/
|
*/
|
||||||
class Store {
|
class Store {
|
||||||
constructor(initialState) {
|
constructor(initialState) {
|
||||||
this.state = { ...initialState };
|
this.state = { ...initialState };
|
||||||
this.listeners = [];
|
this.listeners = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前状态
|
* 获取当前状态
|
||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
getState() {
|
getState() {
|
||||||
return { ...this.state };
|
return { ...this.state };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新状态
|
* 更新状态
|
||||||
* @param {Object} updates - 要更新的属性
|
* @param {Object} updates - 要更新的属性
|
||||||
*/
|
*/
|
||||||
setState(updates) {
|
setState(updates) {
|
||||||
const prevState = { ...this.state };
|
const prevState = { ...this.state };
|
||||||
this.state = { ...this.state, ...updates };
|
this.state = { ...this.state, ...updates };
|
||||||
|
|
||||||
// 通知所有监听器
|
// 通知所有监听器
|
||||||
this.listeners.forEach(listener => {
|
this.listeners.forEach((listener) => {
|
||||||
listener(this.state, prevState);
|
listener(this.state, prevState);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 订阅状态变化
|
* 订阅状态变化
|
||||||
* @param {Function} listener - 监听器函数
|
* @param {Function} listener - 监听器函数
|
||||||
* @returns {Function} - 取消订阅的函数
|
* @returns {Function} - 取消订阅的函数
|
||||||
*/
|
*/
|
||||||
subscribe(listener) {
|
subscribe(listener) {
|
||||||
this.listeners.push(listener);
|
this.listeners.push(listener);
|
||||||
|
|
||||||
// 返回取消订阅的函数
|
// 返回取消订阅的函数
|
||||||
return () => {
|
return () => {
|
||||||
this.listeners = this.listeners.filter(l => l !== listener);
|
this.listeners = this.listeners.filter((l) => l !== listener);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -95,281 +91,277 @@ class Store {
|
|||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
export function createCommentStore(config, fetchComments, submitComment) {
|
export function createCommentStore(config, fetchComments, submitComment) {
|
||||||
// 从 localStorage 加载用户信息
|
// 从 localStorage 加载用户信息
|
||||||
const savedInfo = loadUserInfo();
|
const savedInfo = loadUserInfo();
|
||||||
|
|
||||||
// 创建 store 实例
|
// 创建 store 实例
|
||||||
const store = new Store({
|
const store = new Store({
|
||||||
// 评论数据
|
// 评论数据
|
||||||
comments: [],
|
comments: [],
|
||||||
loading: true,
|
loading: true,
|
||||||
error: null,
|
error: null,
|
||||||
|
|
||||||
// 分页
|
// 分页
|
||||||
pagination: {
|
pagination: {
|
||||||
page: 1,
|
page: 1,
|
||||||
limit: config.pageSize || 20,
|
limit: config.pageSize || 20,
|
||||||
total: 0,
|
total: 0,
|
||||||
totalCount: 0
|
totalCount: 0,
|
||||||
},
|
},
|
||||||
|
|
||||||
// 表单数据
|
// 表单数据
|
||||||
form: {
|
form: {
|
||||||
name: savedInfo.name || '',
|
name: savedInfo.name || '',
|
||||||
email: savedInfo.email || '',
|
email: savedInfo.email || '',
|
||||||
url: savedInfo.url || '',
|
url: savedInfo.url || '',
|
||||||
content: ''
|
content: '',
|
||||||
},
|
},
|
||||||
formErrors: {},
|
formErrors: {},
|
||||||
submitting: false,
|
submitting: false,
|
||||||
|
|
||||||
// 回复状态
|
// 回复状态
|
||||||
replyingTo: null,
|
replyingTo: null,
|
||||||
replyContent: '',
|
replyContent: '',
|
||||||
replyError: null
|
replyError: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 监听用户信息变化,自动保存到 localStorage
|
// 监听用户信息变化,自动保存到 localStorage
|
||||||
store.subscribe((state) => {
|
store.subscribe((state) => {
|
||||||
if (state.form.name || state.form.email || state.form.url) {
|
if (state.form.name || state.form.email || state.form.url) {
|
||||||
saveUserInfo(state.form.name, state.form.email, state.form.url);
|
saveUserInfo(state.form.name, state.form.email, state.form.url);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加载评论列表
|
* 加载评论列表
|
||||||
* @param {number} page - 页码
|
* @param {number} page - 页码
|
||||||
*/
|
*/
|
||||||
async function loadComments(page = 1) {
|
async function loadComments(page = 1) {
|
||||||
store.setState({
|
store.setState({
|
||||||
loading: true,
|
loading: true,
|
||||||
error: null
|
error: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetchComments(page, store.getState().pagination.limit);
|
const response = await fetchComments(page, store.getState().pagination.limit);
|
||||||
console.log('[Store] loadComments response:', response);
|
store.setState({
|
||||||
console.log('[Store] comments data:', response.data);
|
comments: response.data,
|
||||||
store.setState({
|
pagination: {
|
||||||
comments: response.data,
|
page: response.pagination.page,
|
||||||
pagination: {
|
limit: response.pagination.limit,
|
||||||
page: response.pagination.page,
|
total: response.pagination.total,
|
||||||
limit: response.pagination.limit,
|
totalCount: response.pagination.totalCount,
|
||||||
total: response.pagination.total,
|
},
|
||||||
totalCount: response.pagination.totalCount
|
loading: false,
|
||||||
},
|
});
|
||||||
loading: false
|
} catch (e) {
|
||||||
});
|
store.setState({
|
||||||
} catch (e) {
|
error: e instanceof Error ? e.message : '加载评论失败',
|
||||||
store.setState({
|
loading: false,
|
||||||
error: e instanceof Error ? e.message : '加载评论失败',
|
});
|
||||||
loading: false
|
}
|
||||||
});
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 提交评论
|
* 提交评论
|
||||||
*/
|
*/
|
||||||
async function submitNewComment() {
|
async function submitNewComment() {
|
||||||
const state = store.getState();
|
const state = store.getState();
|
||||||
const form = state.form;
|
const form = state.form;
|
||||||
|
|
||||||
// 验证表单
|
// 验证表单
|
||||||
const { validateCommentForm } = await import('@/utils/validator.js');
|
const { validateCommentForm } = await import('@/utils/validator.js');
|
||||||
const validation = validateCommentForm(form);
|
const validation = validateCommentForm(form);
|
||||||
if (!validation.valid) {
|
if (!validation.valid) {
|
||||||
store.setState({
|
store.setState({
|
||||||
formErrors: validation.errors
|
formErrors: validation.errors,
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清空错误
|
// 清空错误
|
||||||
store.setState({
|
store.setState({
|
||||||
formErrors: {},
|
formErrors: {},
|
||||||
submitting: true,
|
submitting: true,
|
||||||
error: null
|
error: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await submitComment({
|
await submitComment({
|
||||||
name: form.name,
|
name: form.name,
|
||||||
email: form.email,
|
email: form.email,
|
||||||
url: form.url,
|
url: form.url,
|
||||||
content: form.content
|
content: form.content,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 清空评论内容
|
// 清空评论内容
|
||||||
store.setState({
|
store.setState({
|
||||||
form: { ...form, content: '' },
|
form: { ...form, content: '' },
|
||||||
submitting: false
|
submitting: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 重新加载评论
|
// 重新加载评论
|
||||||
await loadComments(state.pagination.page);
|
await loadComments(state.pagination.page);
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
store.setState({
|
store.setState({
|
||||||
error: e instanceof Error ? e.message : '提交评论失败',
|
error: e instanceof Error ? e.message : '提交评论失败',
|
||||||
submitting: false
|
submitting: false,
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 提交回复
|
* 提交回复
|
||||||
* @param {number} parentId - 父评论 ID
|
* @param {number} parentId - 父评论 ID
|
||||||
*/
|
*/
|
||||||
async function submitReply(parentId) {
|
async function submitReply(parentId) {
|
||||||
const state = store.getState();
|
const state = store.getState();
|
||||||
|
|
||||||
// 验证回复内容
|
// 验证回复内容
|
||||||
if (!state.replyContent.trim()) {
|
if (!state.replyContent.trim()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证用户信息
|
// 验证用户信息
|
||||||
const { validateReplyUserInfo } = await import('@/utils/validator.js');
|
const { validateReplyUserInfo } = await import('@/utils/validator.js');
|
||||||
const validation = validateReplyUserInfo(state.form);
|
const validation = validateReplyUserInfo(state.form);
|
||||||
if (!validation.valid) {
|
if (!validation.valid) {
|
||||||
const errorMessages = Object.values(validation.errors).join(';');
|
const errorMessages = Object.values(validation.errors).join(';');
|
||||||
store.setState({
|
store.setState({
|
||||||
replyError: errorMessages
|
replyError: errorMessages,
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
store.setState({
|
store.setState({
|
||||||
formErrors: {},
|
formErrors: {},
|
||||||
submitting: true,
|
submitting: true,
|
||||||
replyError: null
|
replyError: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await submitComment({
|
await submitComment({
|
||||||
name: state.form.name,
|
name: state.form.name,
|
||||||
email: state.form.email,
|
email: state.form.email,
|
||||||
url: state.form.url,
|
url: state.form.url,
|
||||||
content: state.replyContent,
|
content: state.replyContent,
|
||||||
parentId
|
parentId,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 清空回复内容并关闭回复框
|
// 清空回复内容并关闭回复框
|
||||||
store.setState({
|
store.setState({
|
||||||
replyContent: '',
|
replyContent: '',
|
||||||
replyingTo: null,
|
replyingTo: null,
|
||||||
submitting: false
|
submitting: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 重新加载评论
|
// 重新加载评论
|
||||||
await loadComments(state.pagination.page);
|
await loadComments(state.pagination.page);
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
store.setState({
|
store.setState({
|
||||||
error: e instanceof Error ? e.message : '提交回复失败',
|
error: e instanceof Error ? e.message : '提交回复失败',
|
||||||
submitting: false
|
submitting: false,
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 开始回复
|
* 开始回复
|
||||||
* @param {number} commentId - 评论 ID
|
* @param {number} commentId - 评论 ID
|
||||||
*/
|
*/
|
||||||
function startReply(commentId) {
|
function startReply(commentId) {
|
||||||
console.log('[Store] startReply called with commentId:', commentId);
|
store.setState({
|
||||||
store.setState({
|
replyingTo: commentId,
|
||||||
replyingTo: commentId,
|
replyContent: '',
|
||||||
replyContent: '',
|
replyError: null,
|
||||||
replyError: null
|
});
|
||||||
});
|
}
|
||||||
console.log('[Store] New state:', store.getState());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 取消回复
|
* 取消回复
|
||||||
*/
|
*/
|
||||||
function cancelReply() {
|
function cancelReply() {
|
||||||
store.setState({
|
store.setState({
|
||||||
replyingTo: null,
|
replyingTo: null,
|
||||||
replyContent: '',
|
replyContent: '',
|
||||||
replyError: null
|
replyError: null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新表单字段
|
* 更新表单字段
|
||||||
* @param {string} field - 字段名
|
* @param {string} field - 字段名
|
||||||
* @param {string} value - 值
|
* @param {string} value - 值
|
||||||
*/
|
*/
|
||||||
function updateFormField(field, value) {
|
function updateFormField(field, value) {
|
||||||
const form = { ...store.getState().form };
|
const form = { ...store.getState().form };
|
||||||
form[field] = value;
|
form[field] = value;
|
||||||
store.setState({ form });
|
store.setState({ form });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新回复内容
|
* 更新回复内容
|
||||||
* @param {string} content - 回复内容
|
* @param {string} content - 回复内容
|
||||||
*/
|
*/
|
||||||
function updateReplyContent(content) {
|
function updateReplyContent(content) {
|
||||||
store.setState({
|
store.setState({
|
||||||
replyContent: content
|
replyContent: content,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 清除回复错误
|
* 清除回复错误
|
||||||
*/
|
*/
|
||||||
function clearReplyError() {
|
function clearReplyError() {
|
||||||
store.setState({
|
store.setState({
|
||||||
replyError: null
|
replyError: null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 清除错误
|
* 清除错误
|
||||||
*/
|
*/
|
||||||
function clearError() {
|
function clearError() {
|
||||||
store.setState({
|
store.setState({
|
||||||
error: null
|
error: null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 切换页码
|
* 切换页码
|
||||||
* @param {number} page - 页码
|
* @param {number} page - 页码
|
||||||
*/
|
*/
|
||||||
function goToPage(page) {
|
function goToPage(page) {
|
||||||
const totalPages = store.getState().pagination.total;
|
const totalPages = store.getState().pagination.total;
|
||||||
if (page >= 1 && page <= totalPages) {
|
if (page >= 1 && page <= totalPages) {
|
||||||
loadComments(page);
|
loadComments(page);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// Store 实例
|
// Store 实例
|
||||||
store,
|
store,
|
||||||
|
|
||||||
// 计算属性方法
|
// 计算属性方法
|
||||||
getTotalPages: () => {
|
getTotalPages: () => {
|
||||||
const state = store.getState();
|
const state = store.getState();
|
||||||
return state.pagination.total;
|
return state.pagination.total;
|
||||||
},
|
},
|
||||||
|
|
||||||
// 操作方法
|
// 操作方法
|
||||||
loadComments,
|
loadComments,
|
||||||
submitNewComment,
|
submitNewComment,
|
||||||
submitReply,
|
submitReply,
|
||||||
startReply,
|
startReply,
|
||||||
cancelReply,
|
cancelReply,
|
||||||
updateFormField,
|
updateFormField,
|
||||||
updateReplyContent,
|
updateReplyContent,
|
||||||
clearReplyError,
|
clearReplyError,
|
||||||
clearError,
|
clearError,
|
||||||
goToPage
|
goToPage,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,9 +25,7 @@ function loadConfigFromStorage() {
|
|||||||
if (saved) {
|
if (saved) {
|
||||||
return { ...DEFAULT_CONFIG, ...JSON.parse(saved) };
|
return { ...DEFAULT_CONFIG, ...JSON.parse(saved) };
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {}
|
||||||
console.warn('[CWDComments] 读取本地存储失败:', e);
|
|
||||||
}
|
|
||||||
return DEFAULT_CONFIG;
|
return DEFAULT_CONFIG;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,9 +35,7 @@ function loadConfigFromStorage() {
|
|||||||
function saveConfigToStorage(config) {
|
function saveConfigToStorage(config) {
|
||||||
try {
|
try {
|
||||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(config));
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(config));
|
||||||
} catch (e) {
|
} catch (e) {}
|
||||||
console.warn('[CWDComments] 保存到本地存储失败:', e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -94,10 +90,7 @@ async function initWidget() {
|
|||||||
pageSize: 20,
|
pageSize: 20,
|
||||||
});
|
});
|
||||||
widgetInstance.mount();
|
widgetInstance.mount();
|
||||||
console.log('[CWDComments] Widget 初始化成功', config);
|
} catch (error) {}
|
||||||
} catch (error) {
|
|
||||||
console.error('[CWDComments] Widget 初始化失败:', error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -105,7 +98,6 @@ async function initWidget() {
|
|||||||
*/
|
*/
|
||||||
function toggleTheme() {
|
function toggleTheme() {
|
||||||
if (!widgetInstance) {
|
if (!widgetInstance) {
|
||||||
console.warn('[CWDComments] 请先初始化 widget');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,8 +115,6 @@ function toggleTheme() {
|
|||||||
const config = getConfigFromInputs();
|
const config = getConfigFromInputs();
|
||||||
config.theme = newTheme;
|
config.theme = newTheme;
|
||||||
saveConfigToStorage(config);
|
saveConfigToStorage(config);
|
||||||
|
|
||||||
console.log('[CWDComments] 主题已切换为:', newTheme);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -134,10 +124,7 @@ function clearConfig() {
|
|||||||
try {
|
try {
|
||||||
localStorage.removeItem(STORAGE_KEY);
|
localStorage.removeItem(STORAGE_KEY);
|
||||||
populateInputs(DEFAULT_CONFIG);
|
populateInputs(DEFAULT_CONFIG);
|
||||||
console.log('[CWDComments] 配置已重置为默认值');
|
} catch (e) {}
|
||||||
} catch (e) {
|
|
||||||
console.error('[CWDComments] 重置配置失败:', e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将函数挂载到 window 对象,使其在 HTML 中可访问
|
// 将函数挂载到 window 对象,使其在 HTML 中可访问
|
||||||
@@ -147,8 +134,6 @@ window.clearConfig = clearConfig;
|
|||||||
|
|
||||||
// 页面加载完成后自动初始化
|
// 页面加载完成后自动初始化
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
console.log('[CWDComments] 开发模式 - 页面加载完成,正在初始化...');
|
|
||||||
|
|
||||||
// 从本地存储加载配置并填充到输入框
|
// 从本地存储加载配置并填充到输入框
|
||||||
const savedConfig = loadConfigFromStorage();
|
const savedConfig = loadConfigFromStorage();
|
||||||
populateInputs(savedConfig);
|
populateInputs(savedConfig);
|
||||||
|
|||||||
Reference in New Issue
Block a user