feat(widget): 添加 postSlug 配置以支持多语言/多路径评论聚合

- 在配置面板和核心逻辑中新增 postSlug 字段,允许自定义评论标识符
- 更新前端配置文档,说明 postSlug 的用途和默认行为
- 当未配置 postSlug 时,默认仍使用 window.location.pathname
This commit is contained in:
anghunk
2026-02-09 17:23:21 +08:00
parent cf068a6dc1
commit 6d2e1659d0
4 changed files with 36 additions and 16 deletions

View File

@@ -26,15 +26,21 @@ CWD 评论组件采用 **Shadow DOM** 技术构建,基于独立根节点渲染
<script>
const comments = new CWDComments({
el: '#comments',
apiBaseUrl: 'https://your-api.example.com', // 换成你的 API 地址
siteId: 'blog', // 换成你的站点 ID可选如需多站点隔离请配置
el: '#comments', // 必填
apiBaseUrl: 'https://your-api.example.com', // 必填,换成你的 API 地址
postSlug: 'post-unique-id-001', // 选填,自定义评论标识符,用于跨路径/多语言聚合
siteId: 'blog', // 选填,推荐配置,用于多站点数据隔离。
});
comments.mount();
</script>
```
**cdn 链接(推荐使用)**
如果你的站点是多语言结构(例如 `/en/post/1``/zh/post/1`),或者是不同路径需要共享同一份评论数据,可以通过 `postSlug` 参数手动指定唯一的标识符;
如果未指定 `postSlug`,组件将默认使用 `window.location.pathname` 作为标识符。
**cdn 链接(推荐使用):请单独修改版本号**
```
https://unpkg.com/cwd-widget@0.0.x/dist/cwd.js
@@ -48,14 +54,15 @@ https://cwd.js.org/cwd.js
### 参数说明
| 参数 | 类型 | 必填 | 默认值 | 说明 |
| -------------- | ----------------------- | ---- | ------ | ---------------------------------------- |
| `el` | `string \| HTMLElement` | 是 | - | 挂载元素选择器或 DOM 元素 |
| `apiBaseUrl` | `string` | 是 | - | API 基础地址 |
| `siteId` | `string` | 否 | `''` | 站点 ID用于多站点数据隔离 |
| `theme` | `'light' \| 'dark'` | 否 | `'light'` | 主题模式 |
| `pageSize` | `number` | 否 | `20` | 每页显示评论数 |
| `customCssUrl` | `string` | 否 | - | 自定义样式表 URL追加到 Shadow DOM 底部 |
| 参数 | 类型 | 必填 | 默认值 | 说明 |
| -------------- | ----------------------- | ---- | -------------------------- | ---------------------------------------- |
| `el` | `string \| HTMLElement` | 是 | - | 挂载元素选择器或 DOM 元素 |
| `apiBaseUrl` | `string` | 是 | - | API 基础地址 |
| `siteId` | `string` | 否 | `''` | 站点 ID用于多站点数据隔离,推荐配置 |
| `postSlug` | `string` | 否 | `window.location.pathname` | 自定义评论标识符,用于跨路径/多语言聚合 |
| `theme` | `'light' \| 'dark'` | 否 | `'light'` | 主题模式 |
| `pageSize` | `number` | 否 | `20` | 每页显示评论数 |
| `customCssUrl` | `string` | 否 | - | 自定义样式表 URL追加到 Shadow DOM 底部 |
头像前缀、博主邮箱和标识等信息由后端接口 `/api/config/comments` 提供,无需在前端进行配置。
@@ -82,6 +89,9 @@ https://cwd.js.org/cwd.js
// 动态切换主题
comments.updateConfig({ theme: 'dark' });
// 动态修改评论标识符(适用于单页应用路由切换)
comments.updateConfig({ postSlug: '/new-post-slug' });
// 配置自定义样式(会以 <link> 形式注入到 Shadow DOM 底部)
comments.updateConfig({
customCssUrl: 'https://your-cdn.example.com/cwd-custom.css',

View File

@@ -30,6 +30,7 @@
h1 {
margin-bottom: 20px;
color: #333;
font-size: 1.4rem;
}
.config-panel {
@@ -133,6 +134,10 @@
<label>Site ID可选用于隔离不同站点/环境的评论)</label>
<input type="text" id="siteId" placeholder="例如blog、dev、zishu.me" />
</div>
<div class="config-item">
<label>自定义 postSlug可选留空则使用当前路径</label>
<input type="text" id="postSlug" placeholder="例如post-unique-id-001用于多语言/多路径聚合" />
</div>
<div class="config-item">
<label>主题</label>
<select id="theme">

View File

@@ -32,7 +32,7 @@ export class CWDComments {
if (config.siteId) {
this.config.siteId = config.siteId;
}
if (typeof window !== 'undefined') {
if (typeof window !== 'undefined' && !this.config.postSlug) {
this.config.postSlug = window.location.pathname;
}
if (typeof document !== 'undefined') {
@@ -577,7 +577,7 @@ export class CWDComments {
if (newConfig.siteId !== undefined) {
this.config.siteId = newConfig.siteId;
}
if (typeof window !== 'undefined') {
if (typeof window !== 'undefined' && !this.config.postSlug) {
this.config.postSlug = window.location.pathname;
}
if (typeof document !== 'undefined') {

View File

@@ -13,6 +13,7 @@ const DEFAULT_CONFIG = {
apiBaseUrl: 'http://localhost:8788',
siteId: 'cwd-dev-config',
theme: 'light', // 默认 light / dark
postSlug: '',
};
let widgetInstance = null;
@@ -49,11 +50,13 @@ function populateInputs(config) {
const themeSelect = document.getElementById('theme');
const avatarPrefixInput = document.getElementById('avatarPrefix');
const siteIdInput = document.getElementById('siteId');
const postSlugInput = document.getElementById('postSlug');
if (apiBaseUrlInput) apiBaseUrlInput.value = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl;
if (themeSelect) themeSelect.value = config.theme || DEFAULT_CONFIG.theme;
if (avatarPrefixInput) avatarPrefixInput.value = config.avatarPrefix || DEFAULT_CONFIG.avatarPrefix;
if (siteIdInput) siteIdInput.value = config.siteId || DEFAULT_CONFIG.siteId || '';
if (postSlugInput) postSlugInput.value = config.postSlug || DEFAULT_CONFIG.postSlug || '';
}
/**
@@ -63,7 +66,8 @@ function getConfigFromInputs() {
const apiBaseUrl = document.getElementById('apiBaseUrl')?.value || DEFAULT_CONFIG.apiBaseUrl;
const theme = document.getElementById('theme')?.value || DEFAULT_CONFIG.theme;
const siteId = document.getElementById('siteId')?.value || DEFAULT_CONFIG.siteId || '';
return { apiBaseUrl, theme, siteId };
const postSlug = document.getElementById('postSlug')?.value || DEFAULT_CONFIG.postSlug || '';
return { apiBaseUrl, theme, siteId, postSlug };
}
/**
@@ -93,6 +97,7 @@ async function initWidget() {
el: '#comments',
apiBaseUrl: config.apiBaseUrl,
siteId: config.siteId,
postSlug: config.postSlug,
});
widgetInstance.mount();
} catch (error) {}
@@ -153,7 +158,7 @@ document.addEventListener('DOMContentLoaded', () => {
// 监听输入框变化,实时保存
document.addEventListener('DOMContentLoaded', () => {
const inputs = ['apiBaseUrl', 'theme', 'siteId'];
const inputs = ['apiBaseUrl', 'theme', 'siteId', 'postSlug'];
inputs.forEach((id) => {
const element = document.getElementById(id);
if (element) {