refactor(widget): 简化前端配置并重构核心逻辑
移除前端配置中的 postSlug 和 avatarPrefix 参数,改为从后端接口获取 重构 CWDComments 核心类,自动推导 postSlug 等参数并异步加载服务端配置 更新文档和示例页面以反映配置变更
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
> [!WARNING]
|
||||
> 目前仍处于 Beta 测试阶段,欢迎反馈测试结果。
|
||||
|
||||
<img src="https://github.com/anghunk/cwd-comments/blob/main/icon.png?raw=true" width="128" />
|
||||
<img src="https://github.com/anghunk/cwd-comments/blob/main/icon.png?raw=true" width="72" />
|
||||
|
||||
# cwd-comments
|
||||
|
||||
|
||||
38
docs/.vitepress/components/footerDoc.vue
Normal file
38
docs/.vitepress/components/footerDoc.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div id="zcomments" ref="commentsRoot"></div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref } from "vue";
|
||||
|
||||
const commentsRoot = ref(null);
|
||||
|
||||
onMounted(async () => {
|
||||
if (!commentsRoot.value || typeof window === "undefined") return;
|
||||
|
||||
const apiBaseUrl = "https://cwd-comments-api.anghunk.workers.dev";
|
||||
|
||||
if (!apiBaseUrl) return;
|
||||
|
||||
if (!window.CWDComments) {
|
||||
await new Promise((resolve, reject) => {
|
||||
const script = document.createElement("script");
|
||||
script.src = "https://cwd-comments.zishu.me/cwd-comments.js";
|
||||
script.async = true;
|
||||
script.onload = () => resolve();
|
||||
script.onerror = (e) => reject(e);
|
||||
document.head.appendChild(script);
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
if (!window.CWDComments) return;
|
||||
|
||||
const comments = new window.CWDComments({
|
||||
el: commentsRoot.value,
|
||||
apiBaseUrl,
|
||||
postSlug: window.location.pathname,
|
||||
});
|
||||
|
||||
comments.mount();
|
||||
});
|
||||
</script>
|
||||
@@ -24,6 +24,7 @@ export default defineConfig({
|
||||
{ text: '快速开始', link: '/guide/getting-started' },
|
||||
{ text: '后端配置', link: '/guide/backend-config' },
|
||||
{ text: '前端配置', link: '/guide/frontend-config' },
|
||||
{ text: '管理后台', link: '/guide/admin-panel' },
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
15
docs/.vitepress/theme/index.js
Normal file
15
docs/.vitepress/theme/index.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
import { h } from 'vue'
|
||||
import FooterDoc from '../components/footerDoc.vue'
|
||||
|
||||
export default {
|
||||
...DefaultTheme,
|
||||
Layout() {
|
||||
const Layout = DefaultTheme.Layout
|
||||
|
||||
return h(Layout, null, {
|
||||
'doc-footer-before': () => h(FooterDoc),
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
const comments = new CWDComments({
|
||||
el: '#comments',
|
||||
apiBaseUrl: 'https://your-api.example.com',
|
||||
postSlug: window.location.origin + window.location.pathname,
|
||||
});
|
||||
comments.mount();
|
||||
</script>
|
||||
@@ -25,10 +24,11 @@
|
||||
| ------------ | ----------------------- | ---- | ---------------------------------------------------------------- | ------------------------- |
|
||||
| `el` | `string \| HTMLElement` | 是 | - | 挂载元素选择器或 DOM 元素 |
|
||||
| `apiBaseUrl` | `string` | 是 | - | API 基础地址 |
|
||||
| `postSlug` | `string` | 是 | 当前页面 URL `window.location.origin + window.location.pathname` | 文章唯一标识符 |
|
||||
| `theme` | `'light' \| 'dark'` | 否 | `'light'` | 主题模式 |
|
||||
| `pageSize` | `number` | 否 | `20` | 每页显示评论数 |
|
||||
|
||||
头像前缀、博主邮箱和标识等信息由后端接口 `/api/config/comments` 提供,无需在前端进行配置。
|
||||
|
||||
## 实例方法
|
||||
|
||||
| 方法 | 说明 |
|
||||
@@ -43,7 +43,4 @@
|
||||
```javascript
|
||||
// 动态切换主题
|
||||
comments.updateConfig({ theme: 'dark' });
|
||||
|
||||
// 切换文章
|
||||
comments.updateConfig({ postSlug: 'another-post' });
|
||||
```
|
||||
|
||||
@@ -129,10 +129,6 @@
|
||||
<label>API 地址</label>
|
||||
<input type="text" id="apiBaseUrl" value="http://localhost:8788" />
|
||||
</div>
|
||||
<div class="config-item">
|
||||
<label>文章标识符</label>
|
||||
<input type="text" id="postSlug" value="demo-post" />
|
||||
</div>
|
||||
<div class="config-item">
|
||||
<label>主题</label>
|
||||
<select id="theme">
|
||||
@@ -140,10 +136,6 @@
|
||||
<option value="dark">深色</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="config-item">
|
||||
<label>头像源前缀</label>
|
||||
<input type="text" id="avatarPrefix" value="https://gravatar.com/avatar" />
|
||||
</div>
|
||||
<div class="config-actions">
|
||||
<button class="btn btn-primary" onclick="initWidget()">保存</button>
|
||||
<button class="btn btn-secondary" onclick="toggleTheme()">切换主题</button>
|
||||
@@ -155,7 +147,7 @@
|
||||
<div class="demo-post">
|
||||
<h2>这是一篇示例文章</h2>
|
||||
<p>
|
||||
这是一个演示 CWD Comments Widget 的示例页面。你可以在上方的配置面板中修改 API 地址和文章标识符,
|
||||
这是一个演示 CWD Comments Widget 的示例页面。你可以在上方的配置面板中修改 API 地址和主题,
|
||||
然后点击"应用配置"按钮重新加载评论组件。
|
||||
</p>
|
||||
</div>
|
||||
@@ -169,4 +161,4 @@
|
||||
<script type="module" src="/src/dev.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
9
widget/public/test.html
Normal file
9
widget/public/test.html
Normal file
@@ -0,0 +1,9 @@
|
||||
<script src="./cwd-comments.js"></script>
|
||||
<div id="comments"></div>
|
||||
<script>
|
||||
const comments = new CWDComments({
|
||||
el: '#comments',
|
||||
apiBaseUrl: 'https://cwd-comments-api.anghunk.workers.dev',
|
||||
});
|
||||
comments.mount();
|
||||
</script>
|
||||
@@ -17,21 +17,24 @@ export class CWDComments {
|
||||
* @param {Object} config - 配置对象
|
||||
* @param {string|HTMLElement} config.el - 挂载元素选择器或 DOM 元素
|
||||
* @param {string} config.apiBaseUrl - API 基础地址
|
||||
* @param {string} config.postSlug - 文章标识符
|
||||
* @param {string} config.postTitle - 文章标题(可选)
|
||||
* @param {string} config.postUrl - 文章 URL(可选)
|
||||
* @param {'light'|'dark'} config.theme - 主题
|
||||
* @param {number} config.pageSize - 每页评论数
|
||||
* @param {string} config.avatarPrefix - 头像服务前缀(可选,默认 https://gravatar.com/avatar/)
|
||||
* @param {string} config.adminEmail - 博主邮箱(可选,用于显示博主标识)
|
||||
* @param {string} config.adminBadge - 博主标识文字(可选,默认"博主")
|
||||
* @param {'light'|'dark'} [config.theme] - 主题(可选)
|
||||
* @param {number} [config.pageSize] - 每页评论数(可选,默认 20)
|
||||
*
|
||||
* 以下字段由组件自动推导或从后端读取,无需通过 config 传入:
|
||||
* - postSlug:window.location.origin + window.location.pathname
|
||||
* - postTitle:document.title 或 postSlug
|
||||
* - postUrl:window.location.href
|
||||
* - avatarPrefix/adminEmail/adminBadge:通过 /api/config/comments 接口获取
|
||||
*/
|
||||
constructor(config) {
|
||||
this.config = { ...config };
|
||||
if (!this.config.postTitle && typeof document !== 'undefined') {
|
||||
if (typeof window !== 'undefined') {
|
||||
this.config.postSlug = window.location.origin + window.location.pathname;
|
||||
}
|
||||
if (typeof document !== 'undefined') {
|
||||
this.config.postTitle = document.title || this.config.postSlug;
|
||||
}
|
||||
if (!this.config.postUrl && typeof window !== 'undefined') {
|
||||
if (typeof window !== 'undefined') {
|
||||
this.config.postUrl = window.location.href;
|
||||
}
|
||||
this.hostElement = this._resolveElement(config.el);
|
||||
@@ -64,6 +67,30 @@ export class CWDComments {
|
||||
return el;
|
||||
}
|
||||
|
||||
async _loadServerConfig() {
|
||||
try {
|
||||
const base = this.config.apiBaseUrl;
|
||||
if (!base) {
|
||||
return {};
|
||||
}
|
||||
const apiBaseUrl = base.replace(/\/$/, '');
|
||||
const res = await fetch(`${apiBaseUrl}/api/config/comments`);
|
||||
if (!res.ok) {
|
||||
return {};
|
||||
}
|
||||
const data = await res.json();
|
||||
return {
|
||||
adminEmail: data.adminEmail || '',
|
||||
adminBadge: data.adminBadge || '',
|
||||
adminEnabled: !!data.adminEnabled,
|
||||
avatarPrefix: data.avatarPrefix || ''
|
||||
};
|
||||
} catch (e) {
|
||||
console.warn('[CWDComments] 加载服务端评论配置失败:', e);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 挂载组件
|
||||
*/
|
||||
@@ -94,24 +121,35 @@ export class CWDComments {
|
||||
this.mountPoint.setAttribute('data-theme', this.config.theme);
|
||||
}
|
||||
|
||||
// 创建 API 客户端和 Store
|
||||
const api = createApiClient(this.config);
|
||||
this.store = createCommentStore(
|
||||
this.config,
|
||||
api.fetchComments.bind(api),
|
||||
api.submitComment.bind(api)
|
||||
);
|
||||
(async () => {
|
||||
const serverConfig = await this._loadServerConfig();
|
||||
if (!this._mounted) {
|
||||
return;
|
||||
}
|
||||
if (serverConfig.avatarPrefix) {
|
||||
this.config.avatarPrefix = serverConfig.avatarPrefix;
|
||||
}
|
||||
if (serverConfig.adminEnabled && serverConfig.adminEmail) {
|
||||
this.config.adminEmail = serverConfig.adminEmail;
|
||||
}
|
||||
if (serverConfig.adminEnabled && serverConfig.adminBadge) {
|
||||
this.config.adminBadge = serverConfig.adminBadge;
|
||||
}
|
||||
|
||||
// 订阅状态变化
|
||||
this.unsubscribe = this.store.store.subscribe((state) => {
|
||||
this._onStateChange(state);
|
||||
});
|
||||
const api = createApiClient(this.config);
|
||||
this.store = createCommentStore(
|
||||
this.config,
|
||||
api.fetchComments.bind(api),
|
||||
api.submitComment.bind(api)
|
||||
);
|
||||
|
||||
// 渲染组件
|
||||
this._render();
|
||||
this.unsubscribe = this.store.store.subscribe((state) => {
|
||||
this._onStateChange(state);
|
||||
});
|
||||
|
||||
// 加载评论
|
||||
this.store.loadComments();
|
||||
this._render();
|
||||
this.store.loadComments();
|
||||
})();
|
||||
|
||||
this._mounted = true;
|
||||
}
|
||||
@@ -339,35 +377,43 @@ export class CWDComments {
|
||||
const prevConfig = { ...this.config };
|
||||
|
||||
Object.assign(this.config, newConfig);
|
||||
if (typeof window !== 'undefined') {
|
||||
this.config.postSlug = window.location.origin + window.location.pathname;
|
||||
}
|
||||
if (typeof document !== 'undefined') {
|
||||
this.config.postTitle = document.title || this.config.postSlug;
|
||||
}
|
||||
if (typeof window !== 'undefined') {
|
||||
this.config.postUrl = window.location.href;
|
||||
}
|
||||
|
||||
// 更新主题
|
||||
if (newConfig.theme && this.mountPoint) {
|
||||
this.mountPoint.setAttribute('data-theme', newConfig.theme);
|
||||
}
|
||||
|
||||
// 如果 postSlug 变化,重新加载评论
|
||||
if (newConfig.postSlug && newConfig.postSlug !== prevConfig.postSlug) {
|
||||
// 重新创建 API 客户端和 Store
|
||||
const shouldReload =
|
||||
this.config.apiBaseUrl !== prevConfig.apiBaseUrl ||
|
||||
this.config.pageSize !== prevConfig.pageSize ||
|
||||
this.config.postSlug !== prevConfig.postSlug;
|
||||
|
||||
if (shouldReload) {
|
||||
const api = createApiClient(this.config);
|
||||
|
||||
// 取消旧订阅
|
||||
if (this.unsubscribe) {
|
||||
this.unsubscribe();
|
||||
}
|
||||
|
||||
// 创建新 store
|
||||
this.store = createCommentStore(
|
||||
this.config,
|
||||
api.fetchComments.bind(api),
|
||||
api.submitComment.bind(api)
|
||||
);
|
||||
|
||||
// 重新订阅
|
||||
this.unsubscribe = this.store.store.subscribe((state) => {
|
||||
this._onStateChange(state);
|
||||
});
|
||||
|
||||
// 加载评论
|
||||
this.store.loadComments();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ const STORAGE_KEY = 'cwd-dev-config';
|
||||
const DEFAULT_CONFIG = {
|
||||
el: '#comments',
|
||||
apiBaseUrl: 'http://localhost:8788',
|
||||
postSlug: window.location.origin + window.location.pathname,
|
||||
theme: 'light', // 默认 light / dark
|
||||
};
|
||||
|
||||
@@ -48,12 +47,10 @@ function saveConfigToStorage(config) {
|
||||
*/
|
||||
function populateInputs(config) {
|
||||
const apiBaseUrlInput = document.getElementById('apiBaseUrl');
|
||||
const postSlugInput = document.getElementById('postSlug');
|
||||
const themeSelect = document.getElementById('theme');
|
||||
const avatarPrefixInput = document.getElementById('avatarPrefix');
|
||||
|
||||
if (apiBaseUrlInput) apiBaseUrlInput.value = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl;
|
||||
if (postSlugInput) postSlugInput.value = config.postSlug || DEFAULT_CONFIG.postSlug;
|
||||
if (themeSelect) themeSelect.value = config.theme || DEFAULT_CONFIG.theme;
|
||||
if (avatarPrefixInput) avatarPrefixInput.value = config.avatarPrefix || DEFAULT_CONFIG.avatarPrefix;
|
||||
}
|
||||
@@ -63,29 +60,8 @@ function populateInputs(config) {
|
||||
*/
|
||||
function getConfigFromInputs() {
|
||||
const apiBaseUrl = document.getElementById('apiBaseUrl')?.value || DEFAULT_CONFIG.apiBaseUrl;
|
||||
const postSlug = document.getElementById('postSlug')?.value || DEFAULT_CONFIG.postSlug;
|
||||
const theme = document.getElementById('theme')?.value || DEFAULT_CONFIG.theme;
|
||||
const avatarPrefix = document.getElementById('avatarPrefix')?.value || DEFAULT_CONFIG.avatarPrefix;
|
||||
return { apiBaseUrl, postSlug, theme, avatarPrefix };
|
||||
}
|
||||
|
||||
async function loadServerCommentConfig(apiBaseUrl) {
|
||||
try {
|
||||
const res = await fetch(`${apiBaseUrl}/api/config/comments`);
|
||||
if (!res.ok) {
|
||||
return {};
|
||||
}
|
||||
const data = await res.json();
|
||||
return {
|
||||
adminEmail: data.adminEmail || '',
|
||||
adminBadge: data.adminBadge || '',
|
||||
adminEnabled: !!data.adminEnabled,
|
||||
avatarPrefix: data.avatarPrefix || '',
|
||||
};
|
||||
} catch (e) {
|
||||
console.warn('[CWDComments] 加载服务端评论配置失败:', e);
|
||||
return {};
|
||||
}
|
||||
return { apiBaseUrl, theme };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,21 +87,11 @@ async function initWidget() {
|
||||
|
||||
// 创建新实例
|
||||
try {
|
||||
const serverConfig = await loadServerCommentConfig(config.apiBaseUrl);
|
||||
|
||||
widgetInstance = new CWDComments({
|
||||
el: '#comments',
|
||||
apiBaseUrl: config.apiBaseUrl,
|
||||
postSlug: config.postSlug,
|
||||
theme: config.theme,
|
||||
avatarPrefix: serverConfig.avatarPrefix || config.avatarPrefix,
|
||||
pageSize: 20,
|
||||
...(serverConfig.adminEnabled && serverConfig.adminEmail
|
||||
? { adminEmail: serverConfig.adminEmail }
|
||||
: {}),
|
||||
...(serverConfig.adminEnabled && serverConfig.adminBadge
|
||||
? { adminBadge: serverConfig.adminBadge }
|
||||
: {}),
|
||||
});
|
||||
widgetInstance.mount();
|
||||
console.log('[CWDComments] Widget 初始化成功', config);
|
||||
@@ -193,7 +159,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
// 监听输入框变化,实时保存
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const inputs = ['apiBaseUrl', 'postSlug', 'theme', 'avatarPrefix'];
|
||||
const inputs = ['apiBaseUrl', 'theme'];
|
||||
inputs.forEach((id) => {
|
||||
const element = document.getElementById(id);
|
||||
if (element) {
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
* <script>
|
||||
* new CWDComments({
|
||||
* el: '#comments',
|
||||
* apiBaseUrl: 'https://api.example.com',
|
||||
* postSlug: 'my-post'
|
||||
* apiBaseUrl: 'https://api.example.com'
|
||||
* }).mount();
|
||||
* </script>
|
||||
* ```
|
||||
|
||||
Reference in New Issue
Block a user