feat(admin): 添加博主显示开关功能

在评论系统设置中添加管理员显示开关,允许控制博主标识的显示
更新前后端接口以支持新功能,包括数据库存储和界面切换组件
This commit is contained in:
anghunk
2026-01-20 09:31:40 +08:00
parent 456771bae3
commit ebcf814f91
6 changed files with 103 additions and 9 deletions

View File

@@ -1,3 +1,3 @@
VITE_API_BASE_URL=https://api.example.com
VITE_ADMIN_NAME=anghunk@example.com
VITE_ADMIN_NAME=admin@example.com
VITE_ADMIN_PASSWORD=123456

View File

@@ -37,6 +37,7 @@ export type CommentSettingsResponse = {
adminEmail: string | null;
adminBadge: string | null;
avatarPrefix: string | null;
adminEnabled: boolean;
};
export async function loginAdmin(name: string, password: string): Promise<string> {
@@ -78,6 +79,7 @@ export function saveCommentSettings(data: {
adminEmail?: string;
adminBadge?: string;
avatarPrefix?: string;
adminEnabled?: boolean;
}): Promise<{ message: string }> {
return put<{ message: string }>('/admin/settings/comments', data);
}

View File

@@ -428,7 +428,7 @@ onMounted(() => {
border-radius: 4px;
border: 1px solid #d0d7de;
background-color: #ffffff;
font-size: 11px;
font-size: 12px;
cursor: pointer;
}

View File

@@ -36,6 +36,13 @@
<label class="form-label">博主标签文字</label>
<input v-model="commentAdminBadge" class="form-input" type="text" />
</div>
<div class="form-item">
<label class="form-label">是否开启博主显示</label>
<label class="switch">
<input v-model="commentAdminEnabled" type="checkbox" />
<span class="slider" />
</label>
</div>
<div class="form-item">
<label class="form-label">头像前缀Gravatar/Cravatar</label>
<input v-model="avatarPrefix" class="form-input" type="text" />
@@ -64,6 +71,7 @@ const email = ref("");
const commentAdminEmail = ref("");
const commentAdminBadge = ref("");
const avatarPrefix = ref("");
const commentAdminEnabled = ref(false);
const savingEmail = ref(false);
const savingComment = ref(false);
const loading = ref(false);
@@ -81,6 +89,7 @@ async function load() {
commentAdminEmail.value = commentRes.adminEmail || "";
commentAdminBadge.value = commentRes.adminBadge || "博主";
avatarPrefix.value = commentRes.avatarPrefix || "";
commentAdminEnabled.value = !!commentRes.adminEnabled;
} catch (e: any) {
message.value = e.message || "加载失败";
messageType.value = "error";
@@ -117,6 +126,7 @@ async function saveComment() {
adminEmail: commentAdminEmail.value,
adminBadge: commentAdminBadge.value,
avatarPrefix: avatarPrefix.value,
adminEnabled: commentAdminEnabled.value,
});
message.value = res.message || "保存成功";
messageType.value = "success";
@@ -184,6 +194,53 @@ onMounted(() => {
box-shadow: 0 0 0 1px rgba(9, 105, 218, 0.2);
}
.switch {
position: relative;
display: inline-flex;
width: 40px;
height: 22px;
align-items: center;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #d0d7de;
transition: 0.2s;
border-radius: 999px;
}
.slider::before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 3px;
top: 3px;
background-color: #ffffff;
transition: 0.2s;
border-radius: 50%;
box-shadow: 0 1px 2px rgba(27, 31, 36, 0.15);
}
.switch input:checked + .slider {
background-color: #0969da;
}
.switch input:checked + .slider::before {
transform: translateX(16px);
}
.card-actions {
display: flex;
justify-content: flex-end;

View File

@@ -19,12 +19,18 @@ const VERSION = 'v0.0.1';
const COMMENT_ADMIN_EMAIL_KEY = 'comment_admin_email';
const COMMENT_ADMIN_BADGE_KEY = 'comment_admin_badge';
const COMMENT_AVATAR_PREFIX_KEY = 'comment_avatar_prefix';
const COMMENT_ADMIN_ENABLED_KEY = 'comment_admin_enabled';
async function loadCommentSettings(env: Bindings) {
await env.CWD_DB.prepare(
'CREATE TABLE IF NOT EXISTS Settings (key TEXT PRIMARY KEY, value TEXT NOT NULL)'
).run();
const keys = [COMMENT_ADMIN_EMAIL_KEY, COMMENT_ADMIN_BADGE_KEY, COMMENT_AVATAR_PREFIX_KEY];
const keys = [
COMMENT_ADMIN_EMAIL_KEY,
COMMENT_ADMIN_BADGE_KEY,
COMMENT_AVATAR_PREFIX_KEY,
COMMENT_ADMIN_ENABLED_KEY
];
const { results } = await env.CWD_DB.prepare(
'SELECT key, value FROM Settings WHERE key IN (?, ?, ?)'
)
@@ -38,16 +44,25 @@ async function loadCommentSettings(env: Bindings) {
}
}
const enabledRaw = map.get(COMMENT_ADMIN_ENABLED_KEY) ?? null;
const adminEnabled = enabledRaw === '1';
return {
adminEmail: map.get(COMMENT_ADMIN_EMAIL_KEY) ?? null,
adminBadge: map.get(COMMENT_ADMIN_BADGE_KEY) ?? null,
avatarPrefix: map.get(COMMENT_AVATAR_PREFIX_KEY) ?? null
avatarPrefix: map.get(COMMENT_AVATAR_PREFIX_KEY) ?? null,
adminEnabled
};
}
async function saveCommentSettings(
env: Bindings,
settings: { adminEmail?: string; adminBadge?: string; avatarPrefix?: string }
settings: {
adminEmail?: string;
adminBadge?: string;
avatarPrefix?: string;
adminEnabled?: boolean;
}
) {
await env.CWD_DB.prepare(
'CREATE TABLE IF NOT EXISTS Settings (key TEXT PRIMARY KEY, value TEXT NOT NULL)'
@@ -56,7 +71,16 @@ async function saveCommentSettings(
const entries: { key: string; value: string | null | undefined }[] = [
{ key: COMMENT_ADMIN_EMAIL_KEY, value: settings.adminEmail },
{ key: COMMENT_ADMIN_BADGE_KEY, value: settings.adminBadge },
{ key: COMMENT_AVATAR_PREFIX_KEY, value: settings.avatarPrefix }
{ key: COMMENT_AVATAR_PREFIX_KEY, value: settings.avatarPrefix },
{
key: COMMENT_ADMIN_ENABLED_KEY,
value:
typeof settings.adminEnabled === 'boolean'
? settings.adminEnabled
? '1'
: '0'
: undefined
}
];
for (const entry of entries) {
@@ -138,10 +162,15 @@ app.put('/admin/settings/comments', async (c) => {
const rawAdminEmail = typeof body.adminEmail === 'string' ? body.adminEmail : '';
const rawAdminBadge = typeof body.adminBadge === 'string' ? body.adminBadge : '';
const rawAvatarPrefix = typeof body.avatarPrefix === 'string' ? body.avatarPrefix : '';
const rawAdminEnabled = body.adminEnabled;
const adminEmail = rawAdminEmail.trim();
const adminBadge = rawAdminBadge.trim();
const avatarPrefix = rawAvatarPrefix.trim();
const adminEnabled =
typeof rawAdminEnabled === 'boolean'
? rawAdminEnabled
: rawAdminEnabled === '1' || rawAdminEnabled === 1;
if (adminEmail && !isValidEmail(adminEmail)) {
return c.json({ message: '邮箱格式不正确' }, 400);
@@ -150,7 +179,8 @@ app.put('/admin/settings/comments', async (c) => {
await saveCommentSettings(c.env, {
adminEmail,
adminBadge,
avatarPrefix
avatarPrefix,
adminEnabled
});
return c.json({ message: '保存成功' });

View File

@@ -79,6 +79,7 @@ async function loadServerCommentConfig(apiBaseUrl) {
return {
adminEmail: data.adminEmail || '',
adminBadge: data.adminBadge || '',
adminEnabled: !!data.adminEnabled,
avatarPrefix: data.avatarPrefix || '',
};
} catch (e) {
@@ -119,8 +120,12 @@ async function initWidget() {
theme: config.theme,
avatarPrefix: serverConfig.avatarPrefix || config.avatarPrefix,
pageSize: 20,
...(serverConfig.adminEmail ? { adminEmail: serverConfig.adminEmail } : {}),
...(serverConfig.adminBadge ? { adminBadge: serverConfig.adminBadge } : {}),
...(serverConfig.adminEnabled && serverConfig.adminEmail
? { adminEmail: serverConfig.adminEmail }
: {}),
...(serverConfig.adminEnabled && serverConfig.adminBadge
? { adminBadge: serverConfig.adminBadge }
: {}),
});
widgetInstance.mount();
console.log('[CWDComments] Widget 初始化成功', config);