fix(ui): 运营与扫码登录态没跟着平台走,且两个登录面板分不清
Deploy VitePress site to Pages / build (push) Canceled after 0s
Deploy VitePress site to Pages / Deploy (push) Canceled after 0s

两个 bug 是同一类问题:没做平台门禁。

1) 切到抖音等平台,「运营」仍列出小红书账号。运营读的是小红书创作者后台,
   其它平台根本没有对应的后台接口。现在按平台门禁换掉整个视图并说明原因 ——
   与 MonitorDashboard / ReportView 用 isWired 的做法一致。

2) 扫码登录面板把小红书的会话当成任意平台的登录态报。它的检测读的是小红书页面的
   __INITIAL_STATE__,后端 qrlogin.LOGIN_URL 里也只有 xhs 一项;而标题写的是
   「{当前平台}登录态」,于是切到抖音照样显示已登录—— 这是个具体的谎。
   现在非小红书直接换掉整个面板(只改标题不够,下面的块读的仍是小红书的状态)。

3) 两个面板的标题都是「登录态」,看不出区别。它们其实是不同机制:
   - Cookie 面板:存进库、每轮以 --cookies_file 注入子进程 → 改名为「Cookie(定时任务用)」
   - 扫码面板:写进浏览器 profile、CDP 模式复用          → 改名为「浏览器登录态(扫码)」
This commit is contained in:
2026-10-07 16:35:12 +08:00
parent 5484e5a3ae
commit 2f5852e311
3 changed files with 77 additions and 5 deletions
@@ -2,6 +2,7 @@ import { useState } from 'react'
import {
AlertTriangle,
ArrowLeft,
Briefcase,
Clock,
Plus,
RefreshCw,
@@ -19,6 +20,7 @@ import {
useSyncCreatorAccount,
} from '@/hooks/useCreator'
import { formatCount, formatDateTime, formatRelative } from '@/lib/monitorFormat'
import { useCurrentPlatform } from '@/hooks/usePlatform'
import type { CreatorNote, CreatorPermissionStatus } from '@/types/creator'
import { AddAccountDialog } from './AddAccountDialog'
@@ -303,9 +305,39 @@ function AccountDetail({ accountId, onBack }: { accountId: number; onBack: () =>
* 采集方式也不同(那边要浏览器登录态,这边是纯请求)。
*/
export function OperationView() {
const { platform, capability } = useCurrentPlatform()
const [selectedId, setSelectedId] = useState<number | null>(null)
const [addOpen, setAddOpen] = useState(false)
// 运营只对小红书成立:它读的是小红书创作者后台。不挡住的话,切到抖音时这里照样
// 列出小红书的账号 —— 看起来像是"抖音账号",其实平台维度根本没参与。
if (platform !== 'xhs') {
const label = capability?.label ?? platform
return (
<div className="flex-1 flex items-center justify-center overflow-y-auto terminal-scroll">
<div className="max-w-lg w-full mx-4 rounded-lg glass-panel float-panel p-6 space-y-3">
<div className="flex items-center gap-3">
<div className="p-2 rounded-md border border-cyber-neon-orange/40 bg-cyber-neon-orange/10">
<Briefcase className="w-5 h-5 text-cyber-neon-orange" />
</div>
<div>
<h2 className="font-mono text-sm text-cyber-text-primary">
{label} 没有运营模块
</h2>
<p className="text-[11px] font-mono text-cyber-text-muted">
运营读的是创作者后台的数据,目前只接入了小红书
</p>
</div>
</div>
<p className="text-[11px] font-mono text-cyber-text-muted leading-relaxed">
小红书之外,这里没有可用的后台接口 —— 能采的是公开数据,那属于「监控」。
切回小红书即可看到运营账号。
</p>
</div>
</div>
)
}
return (
<div className="flex-1 overflow-y-auto terminal-scroll pr-1">
<div className="max-w-5xl space-y-3 pb-6">
+4 -1
View File
@@ -31,8 +31,11 @@ export function CookiePanel() {
<div className="flex items-center justify-between gap-3">
<div className="flex items-center gap-2">
<KeyRound className="w-4 h-4 text-cyber-neon-cyan" />
{/* 标题要和扫码面板区分开:这个是"存进库里、每轮注入子进程"的 cookie,
那个是"写进浏览器 profile、CDP 模式复用"的登录态。两者是不同机制、
也都能同时算"登录着",都叫「登录态」就分不清在说哪一个。 */}
<span className="font-mono text-xs tracking-wider text-cyber-text-primary">
{label}登录态
{label} Cookie(定时任务用)
</span>
{status?.present ? (
<Badge variant="success" className="text-[10px]">
+41 -4
View File
@@ -39,8 +39,15 @@ export function QrLoginPanel() {
const queryClient = useQueryClient()
const [polling, setPolling] = useState(false)
const { data: state } = useQrLoginStatus(polling)
const { data: login } = useLoginState(polling)
// 登录态检测目前只实现了小红书:它读的是小红书页面的 __INITIAL_STATE__。
// 后端 qrlogin.LOGIN_URL 里也只有 xhs 一项。
//
// 不挡住的话会撒一个具体的谎 —— 切到抖音时,面板照样报"已登录",因为小红书的
// 会话还在,而标题写的是「抖音登录态」。
const wired = platform === 'xhs'
const { data: state } = useQrLoginStatus(polling && wired)
const { data: login } = useLoginState(polling && wired)
const start = useStartQrLogin()
const cancel = useCancelQrLogin()
const recheck = useRecheckLogin()
@@ -64,6 +71,29 @@ export function QrLoginPanel() {
}
}, [status, queryClient])
// 未接入的平台直接换掉整个面板。只改标题是不够的 —— 下面那些块读的是小红书的
// 状态,照渲染出来还是会显示"已登录"。
if (!wired) {
return (
<div className="rounded-lg glass-panel float-panel p-4 space-y-2">
<div className="flex items-center gap-2">
<KeyRound className="w-4 h-4 text-cyber-text-muted flex-shrink-0" />
<span className="font-mono text-xs tracking-wider text-cyber-text-primary">
浏览器登录态(扫码)
</span>
<Badge variant="outline" className="text-[10px]">
{label}未接入
</Badge>
</div>
<p className="text-[11px] font-mono text-cyber-text-muted leading-relaxed">
扫码登录目前只实现了<span className="text-cyber-neon-cyan">小红书</span> ——
登录态检测读的是小红书页面的登录状态,其它平台还没有对应的实现。
切回小红书即可使用。
</p>
</div>
)
}
const begin = () => {
setPolling(true)
start.mutate()
@@ -76,10 +106,17 @@ export function QrLoginPanel() {
<div className="flex items-center justify-between gap-3">
<div className="flex items-center gap-2 min-w-0">
<KeyRound className="w-4 h-4 text-cyber-neon-cyan flex-shrink-0" />
{/* 标题必须和 Cookie 面板区分开:那个是"存进库、每轮注入"的 cookie,
这个是"写进浏览器 profile、CDP 模式复用"的浏览器登录态。两者是
不同的机制,都能算"登录着",只写「登录态」会看不出指的是哪一个。 */}
<span className="font-mono text-xs tracking-wider text-cyber-text-primary flex-shrink-0">
{label}登录态
浏览器登录态(扫码)
</span>
{loggedIn ? (
{!wired ? (
<Badge variant="outline" className="text-[10px]">
{label}未接入
</Badge>
) : loggedIn ? (
<Badge variant="success" className="text-[10px]">
已登录
</Badge>