feat(admin): 添加评论按域名筛选功能并调整统计页面布局
- 在评论管理页面添加域名筛选功能 - 调整统计页面中卡片顺序,将图表移至概览下方 - 更新文档添加版本更新警告提示 - 修改评论列表API支持域名参数查询
This commit is contained in:
@@ -93,8 +93,13 @@ export function logoutAdmin(): void {
|
||||
localStorage.removeItem('cwd_admin_token');
|
||||
}
|
||||
|
||||
export function fetchComments(page: number): Promise<CommentListResponse> {
|
||||
return get<CommentListResponse>(`/admin/comments/list?page=${page}`);
|
||||
export function fetchComments(page: number, domain?: string): Promise<CommentListResponse> {
|
||||
const searchParams = new URLSearchParams();
|
||||
searchParams.set('page', String(page));
|
||||
if (domain) {
|
||||
searchParams.set('domain', domain);
|
||||
}
|
||||
return get<CommentListResponse>(`/admin/comments/list?${searchParams.toString()}`);
|
||||
}
|
||||
|
||||
export function deleteComment(id: number): Promise<{ message: string }> {
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
<option value="pending">待审核</option>
|
||||
<option value="rejected">已拒绝</option>
|
||||
</select>
|
||||
<select v-model="domainFilter" class="toolbar-select">
|
||||
<option value="">全部域名</option>
|
||||
<option v-for="item in domainOptions" :key="item" :value="item">
|
||||
{{ item }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="toolbar-right">
|
||||
<button class="toolbar-button" @click="loadComments">刷新</button>
|
||||
@@ -191,6 +197,7 @@ import {
|
||||
CommentItem,
|
||||
CommentListResponse,
|
||||
fetchComments,
|
||||
fetchCommentStats,
|
||||
deleteComment,
|
||||
updateCommentStatus,
|
||||
blockIp,
|
||||
@@ -205,8 +212,11 @@ const pagination = ref<{ page: number; total: number }>({ page: 1, total: 1 });
|
||||
const loading = ref(false);
|
||||
const error = ref("");
|
||||
const statusFilter = ref("");
|
||||
const domainFilter = ref("");
|
||||
const jumpPageInput = ref("");
|
||||
|
||||
const domainOptions = ref<string[]>([]);
|
||||
|
||||
const filteredComments = computed(() => {
|
||||
if (!statusFilter.value) {
|
||||
return comments.value;
|
||||
@@ -263,7 +273,7 @@ async function loadComments(page?: number) {
|
||||
loading.value = true;
|
||||
error.value = "";
|
||||
try {
|
||||
const res = await fetchComments(targetPage);
|
||||
const res = await fetchComments(targetPage, domainFilter.value || undefined);
|
||||
comments.value = res.data;
|
||||
pagination.value = { page: res.pagination.page, total: res.pagination.total };
|
||||
} catch (e: any) {
|
||||
@@ -280,6 +290,11 @@ function updateRoutePage(page: number) {
|
||||
} else {
|
||||
query.p = String(page);
|
||||
}
|
||||
if (domainFilter.value) {
|
||||
query.domain = domainFilter.value;
|
||||
} else {
|
||||
delete query.domain;
|
||||
}
|
||||
router.push({ query });
|
||||
}
|
||||
|
||||
@@ -370,6 +385,20 @@ onMounted(() => {
|
||||
initialPage = Math.floor(value);
|
||||
}
|
||||
}
|
||||
const d = route.query.domain;
|
||||
if (typeof d === "string" && d.trim()) {
|
||||
domainFilter.value = d.trim();
|
||||
}
|
||||
|
||||
fetchCommentStats()
|
||||
.then((res: CommentListResponse | any) => {
|
||||
const domains = Array.isArray(res.domains) ? res.domains.map((item: any) => item.domain) : [];
|
||||
domainOptions.value = Array.from(new Set(domains));
|
||||
})
|
||||
.catch(() => {
|
||||
domainOptions.value = [];
|
||||
});
|
||||
|
||||
loadComments(initialPage);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -9,15 +9,6 @@
|
||||
{{ toastMessage }}
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3 class="card-title">最近 7 天评论数趋势</h3>
|
||||
<div v-if="statsLoading" class="page-hint">加载中...</div>
|
||||
<div v-else-if="statsError" class="page-error">{{ statsError }}</div>
|
||||
<div v-else class="chart-wrapper">
|
||||
<div ref="chartEl" class="chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3 class="card-title">整体概览</h3>
|
||||
<div v-if="statsLoading" class="page-hint">加载中...</div>
|
||||
@@ -30,7 +21,9 @@
|
||||
</div>
|
||||
<div class="stats-item">
|
||||
<div class="stats-label">已通过</div>
|
||||
<div class="stats-value stats-value-approved">{{ statsSummary.approved }}</div>
|
||||
<div class="stats-value stats-value-approved">
|
||||
{{ statsSummary.approved }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="stats-item">
|
||||
<div class="stats-label">待审核</div>
|
||||
@@ -38,12 +31,23 @@
|
||||
</div>
|
||||
<div class="stats-item">
|
||||
<div class="stats-label">已拒绝</div>
|
||||
<div class="stats-value stats-value-rejected">{{ statsSummary.rejected }}</div>
|
||||
<div class="stats-value stats-value-rejected">
|
||||
{{ statsSummary.rejected }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3 class="card-title">最近 7 天评论数趋势</h3>
|
||||
<div v-if="statsLoading" class="page-hint">加载中...</div>
|
||||
<div v-else-if="statsError" class="page-error">{{ statsError }}</div>
|
||||
<div v-else class="chart-wrapper">
|
||||
<div ref="chartEl" class="chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3 class="card-title">按域名统计</h3>
|
||||
<div v-if="statsLoading" class="page-hint">加载中...</div>
|
||||
|
||||
@@ -9,14 +9,27 @@ export const listComments = async (c: Context<{ Bindings: Bindings }>) => {
|
||||
const limit = 10;
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
const rawDomain = c.req.query('domain') || '';
|
||||
const domain = rawDomain.trim();
|
||||
|
||||
let whereSql = '';
|
||||
const params: (string | number)[] = [];
|
||||
if (domain) {
|
||||
const pattern = `%://${domain}/%`;
|
||||
whereSql = 'WHERE post_slug LIKE ? OR url LIKE ?';
|
||||
params.push(pattern, pattern);
|
||||
}
|
||||
|
||||
const totalCount = await c.env.CWD_DB.prepare(
|
||||
'SELECT COUNT(*) as count FROM Comment'
|
||||
).first<{ count: number }>();
|
||||
`SELECT COUNT(*) as count FROM Comment ${whereSql}`
|
||||
)
|
||||
.bind(...params)
|
||||
.first<{ count: number }>();
|
||||
|
||||
const { results } = await c.env.CWD_DB.prepare(
|
||||
'SELECT * FROM Comment ORDER BY created DESC LIMIT ? OFFSET ?'
|
||||
`SELECT * FROM Comment ${whereSql} ORDER BY created DESC LIMIT ? OFFSET ?`
|
||||
)
|
||||
.bind(limit, offset)
|
||||
.bind(...params, limit, offset)
|
||||
.all();
|
||||
|
||||
await c.env.CWD_DB.prepare(
|
||||
|
||||
@@ -20,6 +20,9 @@ npm run deploy
|
||||
|
||||
如果使用官方后台不需要更新。
|
||||
|
||||
> [!WARNING]
|
||||
> 存在一个弊端,如果官方后台版本有了大更新,而你部署的 api 端没有及时更新部署,可能会导致管理后台无法正常使用(无法获取到最新的接口),正常情况下无需担心。
|
||||
|
||||
```
|
||||
cd cwd-admin
|
||||
npm install
|
||||
|
||||
Reference in New Issue
Block a user