feat(widget): 新增评论组件核心功能及开发环境配置

新增评论组件核心功能,包括评论列表、分页、回复、表单验证等功能
添加开发环境配置,包括Vite构建配置、样式变量和工具函数
实现管理员验证功能,支持本地存储加密
添加组件基础类及常用组件如加载状态、分页、模态框等
配置文档站点构建流程,支持widget独立构建和集成
This commit is contained in:
anghunk
2026-01-22 09:01:11 +08:00
parent f221245c44
commit 6555907156
28 changed files with 7 additions and 7 deletions

View File

@@ -0,0 +1,194 @@
/**
* ReplyEditor 回复编辑器组件
*/
import { Component } from './Component.js';
export class ReplyEditor extends Component {
/**
* @param {HTMLElement|string} container - 容器元素或选择器
* @param {Object} props - 组件属性
* @param {string} props.replyToAuthor - 被回复的作者名
* @param {string} props.content - 回复内容
* @param {string|null} props.error - 错误信息
* @param {boolean} props.submitting - 是否正在提交
* @param {Function} props.onUpdate - 内容更新回调
* @param {Function} props.onSubmit - 提交回调
* @param {Function} props.onCancel - 取消回调
* @param {Function} props.onClearError - 清除错误回调
*/
constructor(container, props = {}) {
super(container, props);
this.state = {
content: props.content || ''
};
}
render() {
const root = this.createElement('div', {
className: 'cwd-reply-editor',
children: [
// 头部
this.createElement('div', {
className: 'cwd-reply-header',
children: [
this.createTextElement('span', `回复 @${this.props.replyToAuthor}`, 'cwd-reply-to'),
this.createElement('button', {
className: 'cwd-btn-close',
attributes: {
type: 'button',
onClick: () => this.handleCancel()
},
text: '✕'
})
]
}),
// 文本框
this.createElement('textarea', {
className: 'cwd-reply-textarea',
attributes: {
rows: 3,
placeholder: '写下你的回复...',
disabled: this.props.submitting,
onInput: (e) => this.handleInput(e)
}
}),
// 错误提示
...(this.props.error ? [
this.createElement('div', {
className: 'cwd-error-inline cwd-error-small',
children: [
this.createTextElement('span', this.props.error),
this.createElement('button', {
className: 'cwd-error-close',
attributes: {
type: 'button',
onClick: () => this.handleClearError()
},
text: '✕'
})
]
})
] : []),
// 操作按钮
this.createElement('div', {
className: 'cwd-reply-actions',
children: [
this.createElement('button', {
className: 'cwd-btn cwd-btn-primary cwd-btn-small',
attributes: {
type: 'button',
disabled: this.props.submitting || !this.state.content.trim(),
onClick: () => this.handleSubmit()
},
text: this.props.submitting ? '提交中...' : '提交回复'
}),
this.createElement('button', {
className: 'cwd-btn cwd-btn-secondary cwd-btn-small',
attributes: {
type: 'button',
disabled: this.props.submitting,
onClick: () => this.handleCancel()
},
text: '取消'
})
]
})
]
});
// 设置文本框内容
const textarea = root.querySelector('textarea');
if (textarea) {
textarea.value = this.state.content;
}
this.elements.root = root;
this.empty(this.container);
this.container.appendChild(root);
}
updateProps(prevProps) {
// 如果外部传入的 content 变化,更新内部状态
if (this.props.content !== this.state.content && this.props.content !== prevProps?.content) {
this.state.content = this.props.content;
this.render();
return;
}
// 如果有错误显示/隐藏变化,重新渲染
if (this.props.error !== prevProps?.error) {
this.render();
return;
}
// 如果 submitting 状态变化,重新渲染
if (this.props.submitting !== prevProps?.submitting) {
this.render();
return;
}
}
handleInput(e) {
this.state.content = e.target.value;
// 更新提交按钮的禁用状态
const submitBtn = this.elements.root?.querySelector('.cwd-btn-primary');
if (submitBtn) {
submitBtn.disabled = this.props.submitting || !this.state.content.trim();
}
if (this.props.onUpdate) {
this.props.onUpdate(this.state.content);
}
}
handleSubmit() {
if (this.props.onSubmit) {
this.props.onSubmit();
}
}
handleCancel() {
if (this.props.onCancel) {
this.props.onCancel();
}
}
handleClearError() {
if (this.props.onClearError) {
this.props.onClearError();
}
}
/**
* 设置内容
* @param {string} content - 新内容
*/
setContent(content) {
this.state.content = content;
const textarea = this.elements.root?.querySelector('textarea');
if (textarea) {
textarea.value = content;
}
}
/**
* 获取内容
* @returns {string}
*/
getContent() {
return this.state.content;
}
/**
* 聚焦文本框
*/
focus() {
const textarea = this.elements.root?.querySelector('textarea');
if (textarea) {
textarea.focus();
}
}
}