-
- {note.note_id} · {note.snapshot_count} 次快照
-
+ {editingNote === rowKey ? (
+ event.stopPropagation()}
+ onChange={(event) => setNoteAliasDraft(event.target.value)}
+ onKeyDown={(event) => {
+ event.stopPropagation()
+ if (event.key === 'Enter') commitNoteAlias(note.note_id)
+ if (event.key === 'Escape') {
+ noteAliasCancelled.current = true
+ setEditingNote(null)
+ }
+ }}
+ onBlur={() => commitNoteAlias(note.note_id)}
+ className="mt-1 w-full rounded border border-cyber-neon-cyan/40 bg-cyber-bg-tertiary px-1.5 py-0.5 text-[11px] font-mono text-cyber-text-primary outline-none"
+ />
+ ) : (
+
+
+ {note.note_id} · {note.snapshot_count} 次快照
+
+
+
+ )}
diff --git a/webui/src/hooks/useMonitor.ts b/webui/src/hooks/useMonitor.ts
index 2abefd6..3013f1b 100644
--- a/webui/src/hooks/useMonitor.ts
+++ b/webui/src/hooks/useMonitor.ts
@@ -370,6 +370,23 @@ export function useSetCreatorAlias() {
})
}
+// --- 作品备注 --------------------------------------------------------------
+
+export function useSetNoteAlias() {
+ const queryClient = useQueryClient()
+ const platform = usePlatformParam()
+ return useMutation({
+ mutationFn: ({ noteId, alias }: { noteId: string; alias: string }) =>
+ monitorApi.setNoteAlias(noteId, alias, platform),
+ onSuccess: () => {
+ toast.success('备注已保存')
+ queryClient.invalidateQueries({ queryKey: ['monitorNotes'] })
+ queryClient.invalidateQueries({ queryKey: ['monitorCommentsGrouped'] })
+ },
+ onError: (error: Error) => toast.error(`备注保存失败:${error.message}`),
+ })
+}
+
// --- 上游更新检查 -----------------------------------------------------------------------------------------------------------------
export function useUpstreamStatus() {
diff --git a/webui/src/lib/api.ts b/webui/src/lib/api.ts
index ed7deaf..a7145d9 100644
--- a/webui/src/lib/api.ts
+++ b/webui/src/lib/api.ts
@@ -290,6 +290,13 @@ export const monitorApi = {
{ alias },
{ params: { platform } },
),
+ /** 给作品起备注(空串 = 清掉)。键是 note_id —— 和博主备注是两回事。 */
+ setNoteAlias: (noteId: string, alias: string, platform?: string) =>
+ api.put(
+ `/monitor/notes/${encodeURIComponent(noteId)}`,
+ { alias },
+ { params: { platform } },
+ ),
/**
* 立刻检查一次。服务端要等 fetch 跑完才回答,而 axios 默认 30 秒对此不够 ——
* 一条卡住的 git fetch 能拖到两分钟,这里必须单独放长超时,否则会误报失败。
diff --git a/webui/src/types/monitor.ts b/webui/src/types/monitor.ts
index 4c55e16..d3b3f62 100644
--- a/webui/src/types/monitor.ts
+++ b/webui/src/types/monitor.ts
@@ -102,6 +102,25 @@ export interface MonitorNote {
* 更认不出。备注是唯一能把账号对上人的东西。空串表示没起过。
*/
creator_alias: string
+ /**
+ * 人给**这条作品**起的备注。
+ *
+ * 和 `creator_alias` 是两件事:那条回答「这个账号是谁」,这条回答「这条我要盯着」。
+ * 一个博主底下常常只有一两件值得盯的作品,所以不能合并成一条。空串表示没起过。
+ */
+ note_alias: string
+ /**
+ * 博主**账号级**指标 —— 作品列表给不了的东西:作品说的是「这条涨了多少赞」,
+ * 它说的是「这个人整个账号在涨还是在掉」。
+ *
+ * 三者都可能为 null:平台没采到(小红书那条路根本不产生它),或者某一项平台没给。
+ * 为 null 时界面整块不画 —— 画成「粉丝 0」就是在撒谎。
+ */
+ creator_fans: number | null
+ creator_total_favorited: number | null
+ creator_works: number | null
+ /** 上述指标是哪一轮采到的。null = 从来没有过。 */
+ creator_stats_at: number | null
/** 作品的**发布**时间(爬虫侧:小红书 time、抖音 create_time)。可能与
* `first_seen_at` 差很远 —— 后者是「我们第一次看到它」的时间。平台没给时为 null。 */
published_at: number | null