feat: 改进评论系统管理员识别与样式展示
- 后端评论接口增加 isAdmin 字段,不再依赖前端邮箱比对 - 管理后台评论列表支持 Markdown 渲染并添加相应样式 - 前端组件移除 adminEmail 依赖,改用 comment.isAdmin 字段 - 添加 CSS 样式兼容其他框架导入的表情元素 - 更新文档说明部署流程
This commit is contained in:
127
cwd-admin/src/assets/markdown.css
Normal file
127
cwd-admin/src/assets/markdown.css
Normal file
@@ -0,0 +1,127 @@
|
||||
.cell-content-text {
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
color: var(--cwd-text, #24292f);
|
||||
word-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.cell-content-text p {
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
|
||||
.cell-content-text p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.cell-content-text a {
|
||||
color: var(--cwd-primary, #0969da);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.cell-content-text a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.cell-content-text h1,
|
||||
.cell-content-text h2,
|
||||
.cell-content-text h3,
|
||||
.cell-content-text h4,
|
||||
.cell-content-text h5,
|
||||
.cell-content-text h6 {
|
||||
margin-top: 16px;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 600;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
.cell-content-text h1 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.cell-content-text h2 {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.cell-content-text h3 {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.cell-content-text ul,
|
||||
.cell-content-text ol {
|
||||
padding-left: 1.7em;
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
|
||||
.cell-content-text li+li {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.cell-content-text code {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
font-size: 0.9em;
|
||||
background: var(--cwd-bg-secondary, #f6f8fa);
|
||||
padding: 0.2em 0.4em;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.cell-content-text pre {
|
||||
padding: 12px;
|
||||
margin: 12px 0;
|
||||
overflow-x: auto;
|
||||
background: var(--cwd-bg-secondary, #f6f8fa);
|
||||
border-radius: 6px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.cell-content-text pre code {
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
border-radius: 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.cell-content-text blockquote {
|
||||
margin: 8px 0;
|
||||
padding: 0 12px;
|
||||
border-left: 4px solid var(--cwd-border, #d0d7de);
|
||||
color: var(--cwd-text-secondary, #6e7781);
|
||||
}
|
||||
|
||||
.cell-content-text hr {
|
||||
border: 0;
|
||||
border-top: 1px solid var(--cwd-border-light, #eaeef2);
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
.cell-content-text table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
width: 100%;
|
||||
font-size: 13px;
|
||||
margin: 12px 0;
|
||||
}
|
||||
|
||||
.cell-content-text th,
|
||||
.cell-content-text td {
|
||||
border: 1px solid var(--cwd-border, #d0d7de);
|
||||
padding: 6px 12px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.cell-content-text th {
|
||||
background: var(--cwd-bg-secondary, #f6f8fa);
|
||||
}
|
||||
|
||||
.cell-content-text img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* 兼容其他框架导入的样式 */
|
||||
.cell-content-text .tk-owo-emotion {
|
||||
display: inline-block;
|
||||
vertical-align: bottom;
|
||||
max-width: 40px;
|
||||
}
|
||||
@@ -58,7 +58,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-cell table-cell-content">
|
||||
<div class="cell-content-text">{{ item.contentText }}</div>
|
||||
<div class="cell-content-text" v-html="item.contentHtml"></div>
|
||||
</div>
|
||||
<div class="table-cell table-cell-path">
|
||||
<a
|
||||
@@ -81,9 +81,17 @@
|
||||
>
|
||||
置顶
|
||||
</span>
|
||||
<span class="cell-status cell-likes-number" v-if="item.likes !== 0"
|
||||
<span class="cell-status cell-likes-number" v-if="item.likes !== 0">
|
||||
<svg
|
||||
style="width: 13px"
|
||||
viewBox="0 0 24 24"
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
>
|
||||
<svg style="width:13px;" viewBox="0 0 24 24" aria-hidden="true" fill="currentColor"><path d="M2 21h4V9H2v12zm20-11c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L13 1 7.59 6.41C7.22 6.78 7 7.3 7 7.83V19c0 1.1.9 2 2 2h8c.78 0 1.48-.45 1.82-1.11l3.02-7.05c.11-.23.16-.48.16-.74v-2z"></path></svg>
|
||||
<path
|
||||
d="M2 21h4V9H2v12zm20-11c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L13 1 7.59 6.41C7.22 6.78 7 7.3 7 7.83V19c0 1.1.9 2 2 2h8c.78 0 1.48-.45 1.82-1.11l3.02-7.05c.11-.23.16-.48.16-.74v-2z"
|
||||
></path>
|
||||
</svg>
|
||||
{{
|
||||
typeof item.likes === "number" &&
|
||||
Number.isFinite(item.likes) &&
|
||||
@@ -257,6 +265,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import "../assets/markdown.css";
|
||||
import { onMounted, ref, computed, watch, inject } from "vue";
|
||||
import type { Ref } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
|
||||
@@ -52,12 +52,25 @@ export const getComments = async (c: Context<{ Bindings: Bindings }>) => {
|
||||
ORDER BY priority DESC, created DESC
|
||||
`
|
||||
}
|
||||
const { results } = await c.env.CWD_DB.prepare(query).bind(...slugList).all()
|
||||
|
||||
// 并行获取评论和管理员邮箱
|
||||
// 对 adminEmail 查询进行错误捕获,防止因 Settings 表不存在导致整个接口失败
|
||||
const [commentsResult, adminEmailRow] = await Promise.all([
|
||||
c.env.CWD_DB.prepare(query).bind(...slugList).all(),
|
||||
c.env.CWD_DB.prepare('SELECT value FROM Settings WHERE key = ?')
|
||||
.bind('admin_notify_email')
|
||||
.first<{ value: string }>()
|
||||
.catch(() => null)
|
||||
]);
|
||||
|
||||
const results = commentsResult.results;
|
||||
const adminEmail = adminEmailRow?.value || null;
|
||||
|
||||
// 2. 批量处理头像并格式化
|
||||
const allComments = await Promise.all(results.map(async (row: any) => ({
|
||||
...row,
|
||||
avatar: await getCravatar(row.email, avatar_prefix || undefined),
|
||||
isAdmin: adminEmail && row.email === adminEmail,
|
||||
replies: []
|
||||
})))
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 更新部署
|
||||
|
||||
重新拉取 GitHub 项目代码
|
||||
重新拉取 GitHub 项目代码。
|
||||
|
||||
## 后端更新
|
||||
|
||||
@@ -16,6 +16,8 @@ npm run deploy
|
||||
|
||||
## 前端更新
|
||||
|
||||
如果你的管理后台项目,是通过 GitHub 仓库部署到 Cloudflare Pages,不需要手动更新,只用拉取官方仓库代码,重新推送即可触发重新部署(无需手动)。
|
||||
|
||||
### 管理后台
|
||||
|
||||
如果使用官方后台不需要更新。
|
||||
@@ -35,4 +37,4 @@ npm run build
|
||||
|
||||
如果使用官方评论端 js `https://cwd.js.org/cwd.js`,不需要更新。
|
||||
|
||||
如果你想自己托管,请下载仓库 `/cwd-admin/public` 中最新的 `cwd.js` 文件。
|
||||
如果你想自行托管,请使用打包命令,对仓库 `@docs/widget` 文件夹进行构建,然后把 `dist` 文件夹中的最新的 `cwd.js` 文件上传到你托管的地方。
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -39,7 +39,8 @@ export class CommentItem extends Component {
|
||||
const { comment, isReply, adminEmail, adminBadge } = this.props;
|
||||
const isPinned = typeof comment.priority === 'number' && comment.priority > 1;
|
||||
const isReplying = this.props.replyingTo === comment.id;
|
||||
const isAdmin = adminEmail && comment.email === adminEmail;
|
||||
// 使用后端返回的 isAdmin 字段,不再依赖前端邮箱比对
|
||||
const isAdmin = !!comment.isAdmin;
|
||||
|
||||
const root = this.createElement('div', {
|
||||
className: `cwd-comment-item ${isReply ? 'cwd-comment-reply' : ''}`,
|
||||
@@ -241,7 +242,7 @@ export class CommentItem extends Component {
|
||||
submitting: this.props.submitting,
|
||||
currentUser: this.props.currentUser,
|
||||
onUpdateUserInfo: this.props.onUpdateUserInfo,
|
||||
adminEmail: this.props.adminEmail,
|
||||
// adminEmail 已移除
|
||||
adminBadge: this.props.adminBadge,
|
||||
enableCommentLike: this.props.enableCommentLike,
|
||||
onReply: this.props.onReply,
|
||||
|
||||
@@ -97,7 +97,7 @@ export class CommentList extends Component {
|
||||
submitting: this.props.submitting,
|
||||
currentUser: this.props.currentUser,
|
||||
onUpdateUserInfo: this.props.onUpdateUserInfo,
|
||||
adminEmail: this.props.adminEmail,
|
||||
// adminEmail 已移除,改用 comment.isAdmin 字段
|
||||
adminBadge: this.props.adminBadge,
|
||||
enableCommentLike: this.props.enableCommentLike,
|
||||
onReply: (commentId) => this.handleReply(commentId),
|
||||
|
||||
@@ -367,7 +367,7 @@ export class CWDComments {
|
||||
submitting: state.submitting,
|
||||
currentUser: state.form,
|
||||
onUpdateUserInfo: (field, value) => this.store.updateFormField(field, value),
|
||||
adminEmail: this.config.adminEmail,
|
||||
// adminEmail 已移除,前端展示改用 isAdmin 字段
|
||||
adminBadge: this.config.adminBadge,
|
||||
enableCommentLike: this.config.enableCommentLike !== false,
|
||||
onRetry: () => this.store.loadComments(),
|
||||
|
||||
@@ -986,4 +986,11 @@
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
color: #db850d;
|
||||
}
|
||||
|
||||
/* 兼容其他框架导入的样式 */
|
||||
.cwd-comment-content .tk-owo-emotion {
|
||||
display: inline-block;
|
||||
vertical-align: bottom;
|
||||
max-width: 40px;
|
||||
}
|
||||
Reference in New Issue
Block a user