@@ -1,61 +1,46 @@
< template >
< template >
< div class = "h-full w-full overflow-hidden bg-gradient-to-br from-slate-100 to-slate-200" >
< div v-if = "pageMode === 'config'" class="h-full overflow-auto p-6" >
< div class = "max-w-5xl mx-auto pb-8" >
< div class = "bg-white rounded-2xl shadow-xl p-8 " >
< div class = "flex items-end justify-between mb-4 gap-6" >
< div class = "mx-auto m ax-w-5xl pb-8" >
< div class = "rounded-2xl bg-white p-8 shadow-xl" >
< div class = "mb-4 flex items-end justify-between gap-6" >
< div >
< h1 class = "text-2xl font-semibold text-gray-900" > 自动私信工作台 ( TK版 ) < / h1 >
< p class = "text-sm text-gray-500 mt-1" >
先配置发送话术 、 AI 回复和分组视图 , 再进入浏览器视图页执行任务 。
< / p >
< / div >
< div class = "flex items-center gap-3" >
< span :class = "statusChipClass" >
{ { statusText } }
< / span >
< button @click ="handlePrepareViews" : disabled = "isPreparing || !isElectronEnv"
class = "px-4 py-2 text-sm bg-gradient-to-r from-emerald-500 to-teal-500 text-white rounded-lg hover:from-emerald-600 hover:to-teal-600 transition-all shadow-sm flex items-center gap-2 disabled:cursor-not-allowed disabled:opacity-60" >
< span :class = "statusChipClass" > { { statusText } } < / span >
< button @click ="handlePrepareViews" : disabled = "isPreparing || !isElectronEnv" class = "flex items-center gap-2 rounded-lg bg-gradient-to-r from-emerald-500 to-teal-500 px-4 py-2 text-sm text-white shadow-sm transition-all hover:from-emerald-600 hover:to-teal-600 disabled:cursor-not-allowed disabled:opacity-60" >
< span > { { isPreparing ? '预热中...' : '预热视图' } } < / span >
< svg class = "w-4 h-4" fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" >
< path stroke -linecap = " round " stroke -linejoin = " round " stroke -width = " 2 " d = "M5 12h14M12 5l7 7-7 7" / >
< / svg >
< / button >
< button @click ="openBrowserView"
class = "px-4 py-2 text-sm bg-gradient-to-r from-blue-500 to-cyan-500 text-white rounded-lg hover:from-blue-600 hover:to-cyan-600 transition-all shadow-sm flex items-center gap-2" >
< button @click ="openBrowserView" class = "flex items-center gap-2 rounded-lg bg-gradient-to-r from-blue-500 to-cyan-500 px-4 py-2 text-sm text-white shadow-sm transition-all hover:from-blue-600 hover:to-cyan-600" >
< span > 打开浏览器视图 < / span >
< svg class = "w-4 h-4" fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" >
< path stroke -linecap = " round " stroke -linejoin = " round " stroke -width = " 2 " d = "M13 7l5 5m0 0l-5 5m5-5H6" / >
< / svg >
< / button >
< / div >
< / div >
< div
class = "flex items-center gap-2 mb-6 px-4 py-2 rounded-full text-sm text-gray-700 bg-gradient-to-r from-blue-50 to-green-50 border border-blue-200 w-fit" >
< span class = "w-2 h-2 rounded-full bg-green-500 animate-pulse" / >
` groupViewCounts ` 固定传 3 个元素 , 分别对应第一组 、 第二组 、 第三组开启的视图数 。
< div class = "mb-6 flex w-fit items-center gap-2 rounded-full border border-blue-200 bg-gradient-to-r from-blue-50 to-green-50 px-4 py-2 text-sm text-gray-700" >
< span class = "h-2 w-2 animate-pulse rounded-full bg-green-500" / >
先填写配置 , 再预热视图 , 登录完成后再启动脚本 。
< / div >
< div class = "grid grid-cols-3 gap-6" >
< div class = "col-span-2 space-y-6" >
< section class = "bg-gradient-to-b from-white to-gray-50 rounded-xl border border-gray-200 shadow-sm p-6 " >
< div class = "flex items-center justify-between mb-6 " >
< section class = "rounded-xl border border-gray-200 bg-gradient-to-b from-white to-gray-50 p-6 shadow-sm" >
< div class = "mb-6 flex items-center justify-between" >
< div class = "flex items-center gap-2" >
< span class = "w-1 h-4 rounded-full bg-gradient-to-b from-blue-500 to-green-500" / >
< span class = "h-4 w-1 rounded-full bg-gradient-to-b from-blue-500 to-green-500" / >
< span class = "font-medium text-gray-900" > 运行参数 < / span >
< / div >
< div class = "grid grid-cols-3 gap-3 text-center" >
< div class = "rounded-lg border border-gray-200 bg-white px-3 py-2 min-w-[88px] " >
< div class = "min-w-[88px] rounded-lg border border-gray-200 bg-white px-3 py-2" >
< div class = "text-[10px] text-gray-500" > 话术数 < / div >
< div class = "mt-1 text-base font-semibold text-gray-800" > { { greetingCount } } < / div >
< / div >
< div class = "rounded-lg border border-gray-200 bg-white px-3 py-2 min-w-[88px] " >
< div class = "text-[10px] text-gray-500" > AI回复 < / div >
< div class = "mt-1 text-base font-semibold text-gray-800" > { { configForm . replyUnreadMessages ? '开启' :
'关闭' } } < / div >
< div class = "min-w-[88px] rounded-lg border border-gray-200 bg-white px-3 py-2" >
< div class = "text-[10px] text-gray-500" > 数据池 < / div >
< div class = "mt-1 text-base font-semibold text-gray-800" > { { dataPoolLabel } } < / div >
< / div >
< div class = "rounded-lg border border-gray-200 bg-white px-3 py-2 min-w-[88px] " >
< div class = "min-w-[88px] rounded-lg border border-gray-200 bg-white px-3 py-2" >
< div class = "text-[10px] text-gray-500" > 开启视图 < / div >
< div class = "mt-1 text-base font-semibold text-gray-800" > { { totalEnabledViews } } < / div >
< / div >
@@ -63,237 +48,167 @@
< / div >
< div class = "space-y-5" >
< div >
< label class = "block text-sm font-medium text-gray-700 mb-2" > 发送话术 < / label >
< textarea v-model = "configForm.greetingText" :disabled="isStarting || !isElectronEnv" rows="6"
placeholder = "一行一条话术"
class = "w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-sm text-gray-800 outline-none focus:border-blue-500 disabled:cursor-not-allowed disabled:opacity-60"
@blur ="handleGreetingBlur" / >
< p class = "mt-2 text-xs text-gray-500" > ` greetingMessages ` 会按一行一条组装提交 。 < / p >
< div class = "rounded-xl border border-gray-200 bg-white p-4" >
< div class = "flex items-center justify-between gap-4" >
< div >
< label class = "mb-1 block text-sm font-medium text-gray-700" > 打招呼内容 < / label >
< / div >
< button @click ="showGreetingDialog = true" : disabled = "isStarting || !isElectronEnv" class = "rounded-lg border border-blue-200 bg-blue-50 px-4 py-2 text-sm font-medium text-blue-600 transition-colors hover:bg-blue-100 disabled:cursor-not-allowed disabled:opacity-60" >
打招呼内容配置
< / button >
< / div >
< div class = "mt-3 flex items-center gap-3 text-xs text-gray-500" >
< span class = "rounded-full border border-gray-200 bg-gray-100 px-3 py-1" > 基础话术 { { greetingCount } } 条 < / span >
< span class = "rounded-full border border-gray-200 bg-gray-100 px-3 py-1" > 翻译 { { configForm . needTranslate ? '已开启' : '未开启' } } < / span >
< span class = "rounded-full border border-gray-200 bg-gray-100 px-3 py-1" > 语种 { { languageCount } } < / span >
< / div >
< / div >
< div class = "grid grid-cols-[minmax(0,1fr),220px] gap-4 items-end" >
< div >
< label class = "block text-sm font-medium text-gray-700 mb-2 " > 分组轮换间隔 ( 分钟 ) < / label >
< input v -model .number = " configForm.groupSwitchMinutes " : disabled = "isStarting || !isElectronEnv"
type = "number" min = "1" step = "1"
class = "h-11 w-full rounded-lg border border-gray-300 bg-white px-4 text-sm text-gray-800 outline-none focus:border-blue-500 disabled:cursor-not-allowed disabled:opacity-60"
@blur ="handleSwitchMinutesBlur" / >
< label class = "mb-2 block text-sm font-medium text-gray-700" > 分组轮换间隔 ( 分钟 ) < / label >
< input v -model .number = " configForm.groupSwitchMinutes " : disabled = "isStarting || !isElectronEnv" type = "number" min = "1" step = "1" class = "h-11 w-full rounded-lg border border-gray-300 bg-white px-4 text-sm text-gray-800 outline-none focus:border-blue-500 disabled:cursor-not-allowed disabled:opacity-60" @blur ="handleSwitchMinutesBlur" / >
< / div >
< label
class = "flex items-center gap-3 rounded-lg border border-gray-2 00 bg-gray-50 px-4 py-3 text-sm text-gray-700 h-11" >
< input v-model = "configForm.replyUnreadMessages" :disabled="isStarting || !isElectronEnv"
type = "checkbox" class = "h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500" / >
< label class = "flex h-11 items-center gap-3 rounded-lg border border-gray-200 bg-gray-50 px-4 py-3 text-sm text-gray-700" >
< input v-model = "configForm.replyUnreadMessages" :disabled="isStarting || !isElectronEnv" type="checkbox" class="h-4 w-4 rounded border-gray-3 00 text-blue-600 focus:ring-blue-500" / >
< span > 开启 AI 回复未读消息 < / span >
< / label >
< / div >
< div class = "rounded-xl border border-gray-200 bg-white p-4" >
< div class = "mb-4 flex items-center justify-between gap-4" >
< div >
< label class = "mb-1 block text-sm font-medium text-gray-700" > 数据池来源 < / label >
< p class = "text-xs text-gray-500" > 启动时只需告诉后端用主播池还是大哥池 。 < / p >
< / div >
< span class = "rounded-full border border-gray-200 bg-gray-100 px-3 py-1 text-xs text-gray-600" > { { dataPoolLabel } } < / span >
< / div >
< div class = "grid grid-cols-2 gap-3" >
< label class = "flex items-center gap-3 rounded-lg border px-4 py-3 text-sm transition-colors" : class = "configForm.dataPoolSource === 'anchor_hosts' ? 'border-blue-500 bg-blue-50 text-blue-700' : 'border-gray-200 bg-gray-50 text-gray-700'" >
< input v-model = "configForm.dataPoolSource" :disabled="isStarting || !isElectronEnv" type="radio" value="anchor_hosts" class="h-4 w-4 border-gray-300 text-blue-600 focus:ring-blue-500" / >
< span > 主播池 < / span >
< / label >
< label class = "flex items-center gap-3 rounded-lg border px-4 py-3 text-sm transition-colors" : class = "configForm.dataPoolSource === 'brother_info' ? 'border-purple-500 bg-purple-50 text-purple-700' : 'border-gray-200 bg-gray-50 text-gray-700'" >
< input v-model = "configForm.dataPoolSource" :disabled="isStarting || !isElectronEnv" type="radio" value="brother_info" class="h-4 w-4 border-gray-300 text-purple-600 focus:ring-purple-500" / >
< span > 大哥池 < / span >
< / label >
< / div >
< div v-if = "isBrotherInfoMode" class="mt-4" >
< div class = "mb-2 flex items-center justify-between gap-3" >
< label class = "block text-sm font-medium text-gray-700" > 大哥随机回复话术 < / label >
< span class = "rounded-full border border-gray-200 bg-gray-100 px-3 py-1 text-xs text-gray-600" > { { replyMessageCount } } 条 < / span >
< / div >
< textarea v-model = "configForm.replyMessagesText" :disabled="isStarting || !isElectronEnv" rows="5" placeholder="一行一条回复话术,启动时传给后端 replyMessages" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-sm text-gray-800 outline-none focus:border-purple-500 disabled:cursor-not-allowed disabled:opacity-60" / >
< / div >
< / div >
< / div >
< / section >
< section class = "bg-gradient-to-b from-white to-gray-50 rounded-xl border border-gray-200 shadow-sm p-6 " >
< div class = "flex items-center justify-between mb-4 " >
< section class = "rounded-xl border border-gray-200 bg-gradient-to-b from-white to-gray-50 p-6 shadow-sm" >
< div class = "mb-4 flex items-center justify-between" >
< div class = "flex items-center gap-2" >
< span class = "w-1 h-4 rounded-full bg-gradient-to-b from-purple-500 to-blue-500" / >
< span class = "h-4 w-1 rounded-full bg-gradient-to-b from-purple-500 to-blue-500" / >
< span class = "font-medium text-gray-900" > 视图分组 < / span >
< / div >
< span class = "text-xs text-gray-500" > 每组支持 0 - 3 个视图 < / span >
< / div >
< div class = "grid grid-cols-3 gap-4" >
< div v-for = "(count, index) in configForm.groupViewCounts" :key="index"
class = "rounded-xl border border-gray-200 bg-white p-4" >
< div v-for = "(count, index) in configForm.groupViewCounts" :key="index" class="rounded-xl border border-gray-200 bg-white p-4" >
< div class = "flex items-center justify-between gap-2" >
< div >
< div class = "text-sm font-medium text-gray-800" > 第 { { index + 1 } } 组 < / div >
< div class = "mt-1 text-xs text-gray-500" > { { groupRangeLabel ( index ) } } < / div >
< / div >
< span class = "px-2 py-1 rounded-full text-[10px] bg-gray-100 text-gray-600" >
{ { groupViewText ( count ) } }
< / span >
< span class = "rounded-full bg-gray-100 px-2 py-1 text-[10px] text-gray-600" > { { groupViewText ( count ) } } < / span >
< / div >
< input v -model .number = " configForm.groupViewCounts [ index ] " : disabled = "isStarting || !isElectronEnv"
type = "number" min = "0" max = "3" step = "1"
class = "mt-4 h-11 w-full rounded-lg border border-gray-300 bg-gray-50 px-4 text-sm text-gray-800 outline-none focus:border-blue-500 disabled:cursor-not-allowed disabled:opacity-60"
@blur ="handleGroupCountBlur(index)" / >
< input v -model .number = " configForm.groupViewCounts [ index ] " : disabled = "isStarting || !isElectronEnv" type = "number" min = "0" max = "3" step = "1" class = "mt-4 h-11 w-full rounded-lg border border-gray-300 bg-gray-50 px-4 text-sm text-gray-800 outline-none focus:border-blue-500 disabled:cursor-not-allowed disabled:opacity-60" @blur ="handleGroupCountBlur(index)" / >
< / div >
< / div >
< / section >
< / div >
< div class = "space-y-6" >
< section class = "bg-white rounded-xl border border-gray-200 shadow-sm p-5 " >
< section class = "rounded-xl border border-gray-200 bg-white p-5 shadow-sm" >
< div class = "flex items-center justify-between" >
< div >
< div class = "text-sm font-medium text-gray-900" > AI 人设 < / div >
< div class = "mt-1 text-xs text-gray-500" > 沿用自动私信工作台的 AI 配置入口 。 < / div >
< / div >
< span : class = "[
'px-3 py-1 rounded-full text-xs font-medium',
aiConfigured ? 'bg-green-100 text-green-700' : 'bg-yellow-100 text-yellow-700'
]" >
{ { aiConfigured ? '已配置' : '未配置' } }
< / span >
< div > < div class = "text-sm font-medium text-gray-900" > AI 人设 < / div > < / div >
< span : class = "['rounded-full px-3 py-1 text-x s font-medium', aiConfigured ? 'bg-green-100 text-green-700' : 'bg-yellow-100 text-yellow-700']" > { { aiConfigured ? '已配置' : '未配置' } } < / span >
< / div >
< button @click ="showAIDialog = true"
class = "mt-4 w-full px-4 py-3 text-sm font-medium text-blue-600 border border-blue-200 bg-blue-50 hover:bg-blue-100 rounded-lg transition-colors" >
配置 / 修改 AI 人设
< / button >
< button @click ="showAIDialog = true" class = "mt-4 w-full rounded-lg border border-blue-200 bg-blue-50 px-4 py-3 text-sm font-medium text-blue-600 transition-colors hover:bg-blue-100" > 配置 / 修改 AI 人设 < / button >
< / section >
< section class = "bg-white rounded-xl border border-gray-200 shadow-sm p-5 " >
< section class = "rounded-xl border border-gray-200 bg-white p-5 shadow-sm" >
< div class = "text-sm font-medium text-gray-900" > 执行主播库 < / div >
< div class = "mt-1 text-xs text-gray-500" > 复用主播库弹窗 , 单独维护执行名单 。 < / div >
< button @click ="showHostDialog = true"
class = "mt-4 w-full px-4 py-3 text-sm font-medium text-purple-600 border border-purple-200 bg-purple-50 hover:bg-purple-100 rounded-lg transition-colors" >
打开执行主播库
< / button >
< button @click ="showHostDialog = true" class = "mt-4 w-full rounded-lg border border-purple-200 bg-purple-50 px-4 py-3 text-sm font-medium text-purple-600 transition-colors hover:bg-purple-100" > 打开执行主播库 < / button >
< / section >
< section class = "bg-slate-950 rounded-xl shadow-sm p-5 " >
< section class = "rounded-xl bg-slate-950 p-5 shadow-sm" >
< div class = "text-sm font-medium text-slate-100" > 本次提交参数 < / div >
< pre class = "mt-3 overflow-x-auto text-xs leading-6 text-slate-300" > { { payloadPreview } } < / pre >
< / section >
< / div >
< / div >
< div v-if = "!isElectronEnv"
class = "mt-6 rounded-xl border border-amber-200 bg-amber-50 px-4 py-3 text-xs leading-5 text-amber-700" >
当前是 Web 环境 , 无法调用 Electron 的 TikTok 自动私信能力 。
< / div >
< div v-if = "!isElectronEnv" class="mt-6 rounded-xl border border-amber-200 bg-amber-50 px-4 py-3 text-xs leading-5 text-amber-700" > 当前是 Web 环境 , 无法调用 Electron 的 TikTok 自动私信能力 。 < / div >
< / div >
< / div >
< / div >
< div v-else class = "flex h-full w-full bg-gradient-to-br from-gray-50 to-gray-100 animate-fadeIn" >
< aside :style = "sidebarStyle"
class = "h-full bg-white border-r border-gray-200 flex flex-col shadow-sm flex-shrink-0" >
< aside :style = "sidebarStyle" class = "h-full flex-shrink-0 border-r border-gray-200 bg-white shadow-sm" >
< div class = "m-3 mb-0 flex gap-2" >
< button @click ="backToConfig"
class = "flex-1 px-3 py-2 text-xs bg-gray-100 text-gray-700 border border-gray-200 rounded-lg hover:bg-gray-200 transition-colors text-left" >
返回配置
< / button >
< button @click ="handleStop" : disabled = "isStopping || !isElectronEnv"
class = "px-3 py-2 text-xs bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
title = "停止独立版自动私信任务" >
{ { isStopping ? '停止中' : '停止任务' } }
< / button >
< button @click ="backToConfig" class = "flex-1 rounded-lg border border-gray-200 bg-gray-100 px-3 py-2 text-left text-xs text-gray-700 transition-colors hover:bg-gray-200" > 返回配置 < / button >
< button @click ="handleStop" : disabled = "isStopping || !isElectronEnv" class = "rounded-lg bg-red-500 px-3 py-2 text-xs text-white transition-colors hover:bg-red-600 disabled:cursor-not-allowed disabled:opacity-50" > { { isStopping ? '停止中' : '停止任务' } } < / button >
< / div >
< div class = "p-4 border-b border-gray-200" >
< h1 class = "text-lg font-bold bg-gradient-to-r from-blue-600 to-cyan-500 bg-clip-text text-transparent" >
自动私信 ( TK版 )
< / h1 >
< p class = "text-xs text-gray-500 mt-1" > 浏览器视图页 , 位置与 BrowserView 完全对齐 < / p >
< div class = "border-b border-gray-200 p-4" >
< h1 class = "bg-gradient-to-r from-blue-600 to-cyan-500 bg-clip-text text-lg font-bold text-transparent" > 自动私信 ( TK版 ) < / h1 >
< p class = "mt-1 text-xs text-gray-500" > 浏览器视图页 , 位置与 BrowserView 完全对齐 < / p >
< / div >
< div class = "p-4 space-y-4 overflow-auto" >
< div v-if = "false" class="rounded-xl border border-gray-200 bg-gray-50 p-4" >
< div class = "text-xs font-semibold text-gray-500 uppercase tracking-wider mb-3" > 任务摘要 < / div >
< div class = "grid grid-cols-2 gap-3 text-sm" >
< div class = "rounded-lg bg-white border border-gray-200 px-3 py-2" >
< div class = "text-[10px] text-gray-500" > 当前视图 < / div >
< div class = "mt-1 font-semibold text-gray-800" > { { activeViewId } } < / div >
< / div >
< div class = "rounded-lg bg-white border border-gray-200 px-3 py-2" >
< div class = "text-[10px] text-gray-500" > 任务状态 < / div >
< div class = "mt-1 font-semibold text-gray-800" > { { statusText } } < / div >
< / div >
< div class = "rounded-lg bg-white border border-gray-200 px-3 py-2" >
< div class = "text-[10px] text-gray-500" > 发送话术 < / div >
< div class = "mt-1 font-semibold text-gray-800" > { { greetingCount } } 条 < / div >
< / div >
< div class = "rounded-lg bg-white border border-gray-200 px-3 py-2" >
< div class = "text-[10px] text-gray-500" > AI 回复 < / div >
< div class = "mt-1 font-semibold text-gray-800" > { { configForm . replyUnreadMessages ? '开启' : '关闭' } } < / div >
< / div >
< / div >
< / div >
< div class = "space-y-4 overflow-auto p-4" >
< div class = "rounded-xl border border-gray-200 bg-gray-50 p-4" >
< div class = "text-xs font-semibold text-gray-500 uppercase tracking-wider mb-3 " > 视图分组 < / div >
< div class = "mb-3 text-xs font-semibold uppercase tracking-wider text-gray-500 " > 视图分组 < / div >
< div class = "space-y-3" >
< div v-for = "group in browserViewGroups" :key="group.groupIndex"
class = "rounded-lg border border-gray-200 bg-white p-3" >
< div v-for = "group in browserViewGroups" :key="group.groupIndex" class="rounded-lg border border-gray-200 bg-white p-3" >
< div class = "mb-2 flex items-center justify-between" >
< span class = "text-sm font-medium text-gray-800" > 第 { { group . groupIndex + 1 } } 组 < / span >
< span class = "text-xs text-gray-500" > { { group . label } } < / span >
< / div >
< div class = "grid grid-cols-3 gap-2" >
< button v-for = "view in group.views" :key="view.viewId"
: disabled = "!view.enabled || isSwitchingView || !isElectronEnv"
@click ="handleSwitchView(view.viewId)" : class = "[
'h-10 rounded-lg border text-sm font-medium transition-all disabled:cursor-not-allowed',
view.active
? 'border-blue-500 bg-blue-500 text-white shadow-sm'
: view.enabled
? 'border-gray-200 bg-gray-50 text-gray-700 hover:border-blue-300 hover:bg-blue-50'
: 'border-dashed border-gray-200 bg-gray-100 text-gray-400 opacity-70'
]" >
{ { view . viewId } }
< / button >
< button v-for = "view in group.views" :key="view.viewId" :disabled="!view.enabled || isSwitchingView || !isElectronEnv" @click="handleSwitchView(view.viewId)" :class="['h-10 rounded-lg border text-sm font-medium transition-all disabled:cursor-not-allowed', view.active ? 'border-blue-500 bg-blue-500 text-white shadow-sm' : view.enabled ? 'border-gray-200 bg-gray-50 text-gray-700 hover:border-blue-300 hover:bg-blue-50' : 'border-dashed border-gray-200 bg-gray-100 text-gray-400 opacity-70']" > {{ view.viewId }} < / button >
< / div >
< / div >
< / div >
< / div >
< div class = "rounded-xl border border-gray-200 bg-gray-50 p-4" >
< div class = "text-xs font-semibold text-gray-500 uppercase tracking-wider mb-3 " > 快速操作 < / div >
< div class = "mb-3 text-xs font-semibold uppercase tracking-wider text-gray-500 " > 快速操作 < / div >
< div class = "space-y-3" >
< label
class = "flex items-center gap-3 rounded-lg border border-gray-2 00 bg-white px-3 py-3 text-sm text-gray-7 00">
< input v-model = "loginConfirmed" :disabled="isStarting || !isElectronEnv" type="checkbox"
class = "h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500" / >
< label class = "flex items-center gap-3 rounded-lg border border-gray-200 bg-white px-3 py-3 text-sm text-gray-700" >
< input v-model = "loginConfirmed" :disabled="isStarting || !isElectronEnv" type="checkbox" class="h-4 w-4 rounded border-gray-3 00 text-blue-600 focus:ring-blue-5 00" / >
< span > 当前视图已完成手动登录 < / span >
< / label >
< button
class = "w-full h-9 rounded-lg border border-blue-600 bg-blue-600 text-sm text-white hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-60"
: disabled = "isStarting || !isElectronEnv || !loginConfirmed" @click ="handleStart" >
{{ isStarting ? ' 启动中... ' : ' 启动任务 ' }}
< / button >
< div class = "text-xs leading-5 text-gray-500" >
先打开浏览器视图 , 在当前视图里手动登录账号 ; 确认登录完成后 , 再勾选并启动脚本 。
< / div >
< button class = "h-9 w-full rounded-lg border border-blue-600 bg-blue-600 text-sm text-white hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-60" : disabled = "isStarting || !isElectronEnv" @click ="handleStart" > {{ isStarting ? ' 启动中... ' : ' 启动任务 ' }} < / button >
< div class = "text-xs leading-5 text-gray-500" > 先打开浏览器视图 , 在当前视图里手动登录账号 ; 确认登录完成后 , 再勾选并启动脚本 。 < / div >
< / div >
< / div >
< / div >
< / aside >
< main class = "flex-1 flex flex-col relative min-w-0 " >
< div class = "h-12 bg-white border-b border-gray-200 flex items-center px-4 gap-2 shadow-sm" >
< span class = "text-gray-500 text-sm mr-2 " > 视图 : < / span >
< button v-for = "viewId in viewIds" :key="viewId" @click="handleSwitchView(viewId)"
: disabled = "isSwitchingView || !isElectronEnv" : class = "[
'px-3 py-1.5 rounded-lg text-sm font-medium transition-all disabled:opacity-60 disabled:cursor-not-allowed',
activeViewId === viewId
? 'bg-blue-500 text-white shadow-md'
: 'bg-gray-100 text-gray-600 hover:bg-gray-200 border border-gray-200'
]" >
视图 { { viewId } }
< / button >
< main class = "relative flex min-w-0 flex-1 flex-col" >
< div class = "flex h-12 items-center gap-2 border-b border-gray-200 bg-white px-4 shadow-sm" >
< span class = "mr-2 text-sm text-gray-500" > 视图 : < / span >
< button v-for = "viewId in viewIds" :key="viewId" @click="handleSwitchView(viewId)" :disabled="isSwitchingView || !isElectronEnv" :class="['rounded-lg px-3 py-1.5 text-sm font-medium transition-all disabled:cursor-not-allowed disabled:opacity-60', activeViewId === viewId ? 'bg-blue-500 text-white shadow-md' : 'border border-gray-200 bg-gray-100 text-gray-600 hover:bg-gray-200']" > 视图 {{ viewId }} < / button >
< div class = "flex-1" / >
< span class = "text-xs text-gray-500 bg-gray-100 px-2 py-1 rounded border border-gray-200" >
{ { statusText } }
< / span >
< span v-if = "isSwitchingView"
class = "text-xs text-blue-600 bg-blue-50 px-2 py-1 rounded border border-blue-200" >
切换中 ...
< / span >
< span class = "rounded border border-gray-200 bg-gray-100 px-2 py-1 text-xs text-gray-500" > { { statusText } } < / span >
< span v-if = "isSwitchingView" class="rounded border border-blue-200 bg-blue-50 px-2 py-1 text-xs text-blue-600" > 切换中... < / span >
< / div >
< div class = "flex-1 relative" >
< div class = "relative flex-1" >
< ViewPlaceholder class = "absolute inset-0" / >
< / div >
< / main >
< / div >
< AIConfigDialog :visible = "showAIDialog" :config = "aiConfig" @close ="showAIDialog = false" @save ="handleSaveAIConfig"
@change ="(key, value) => aiConfig[key] = v alue " / >
< HostListDialog :visible = "showHostDialog" @close ="showHostDialog = false" @save ="() => { }" / >
< AIConfigDialog :visible = "showAIDialog" :config = "aiConfig" @close ="showAIDialog = false" @save ="handleSaveAIConfig" @change ="(key, value) => aiConfig[key] = value" / >
< HostListDialog :visible = "showHostDialog" @close ="showHostDialog = f alse" @save ="() => {} " / >
< GreetingDialog :visible = "showGreetingDialog" @close ="showGreetingDialog = false" @confirm ="handleGreetingConfirm" / >
< / div >
< / template >
@@ -304,182 +219,76 @@ import { isElectron } from '@/utils/electronBridge'
import ViewPlaceholder from '@/components/ViewPlaceholder.vue'
import HostListDialog from '@/components/HostListDialog.vue'
import AIConfigDialog from '@/components/AIConfigDialog.vue'
import GreetingDialog from '@/components/GreetingDialog.vue'
const props = defineProps ( {
navSidebarWidth : {
type : Number ,
default : 144
}
} )
const props = defineProps ( { navSidebarWidth : { type : Number , default : 144 } } )
const TIKTOK _VIEW _IDS = Array . from ( { length : 9 } , ( _ , index ) => index + 10 )
const DEFAULT _GREETING _TEXT = 'hello\nhi'
const DEFAULT _GROUP _COUNTS = [ 3 , 3 , 3 ]
const isElectronEnv = isElectron ( )
const pageMode = ref ( 'config' )
const activeViewId = ref ( TIKTOK _VIEW _IDS [ 0 ] )
const statusText = ref ( '待启动' )
const showAIDialog = ref ( false )
const showHostDialog = ref ( false )
const showGreetingDialog = ref ( false )
const aiConfigured = ref ( false )
const loginConfirmed = ref ( false )
const preparedConfigKey = ref ( '' )
const isPreparing = ref ( false )
const isStarting = ref ( false )
const isStopping = ref ( false )
const isSwitchingView = ref ( false )
const c onfigForm = reactive ( {
greetingText : DEFAULT _GREETING _TEXT ,
replyUnreadMessages : true ,
groupSwitchMinutes : 10 ,
groupViewCounts : [ ... DEFAULT _GROUP _COUNTS ]
} )
const aiConfig = ref ( {
agentName : '' ,
guildName : '' ,
contactTool : '' ,
contact : ''
} )
const sidebarStyle = computed ( ( ) => ( {
width : ` ${ props . navSidebarWidth } px ` ,
minWidth : '96px' ,
maxWidth : '400px'
} ) )
const configForm = reactive ( { prologueList : { } , needTranslate : false , dataPoolSource : 'anchor_hosts' , replyMessagesText : '' , replyUnreadMessages : true , groupSwitchMinutes : 10 , groupViewCounts : [ ... DEFAULT _GROUP _COUNTS ] } )
const aiC onfig = ref ( { agentName : '' , guildName : '' , contactTool : '' , contact : '' } )
const sidebarStyle = computed ( ( ) => ( { width : ` ${ props . navSidebarWidth } px ` , minWidth : '96px' , maxWidth : '400px' } ) )
const greetingMessages = computed ( ( ) => {
return configForm . greetingText
. split ( /\r?\n/ )
. map ( item => item . trim ( ) )
. filter ( Boolean )
const baseMessages = Array . isArray ( configForm . prologueList ? . yolo ) ? configForm . prologueList . yolo : [ ]
return baseMessages . map ( item => String ( item || '' ) . trim ( ) ) . filter ( Boolean )
} )
const replyMessages = computed ( ( ) => String ( configForm . replyMessagesText || '' ) . split ( /\r?\n/ ) . map ( item => item . trim ( ) ) . filter ( Boolean ) )
const greetingCount = computed ( ( ) => greetingMessages . value . length )
const replyMessageCount = computed ( ( ) => replyMessages . value . length )
const languageCount = computed ( ( ) => Object . keys ( configForm . prologueList || { } ) . length )
const isBrotherInfoMode = computed ( ( ) => configForm . dataPoolSource === 'brother_info' )
const dataPoolLabel = computed ( ( ) => ( isBrotherInfoMode . value ? '大哥池' : '主播池' ) )
const normalizedGroupSwitchMinutes = computed ( ( ) => clampMinutes ( configForm . groupSwitchMinutes ) )
const normalizedGroupViewCounts = computed ( ( ) => normalizeGroupViewCounts ( configForm . groupViewCounts ) )
const browserViewGroups = computed ( ( ) =>
normalizedGroupViewCounts . value . map ( ( enabledCount , groupIndex ) => {
const baseViewId = TIKTOK _VIEW _IDS [ groupIndex * 3 ]
return {
groupIndex ,
label : ` 视图 ${ baseViewId } - ${ baseViewId + 2 } ` ,
views : Array . from ( { length : 3 } , ( _ , offset ) => {
const viewId = baseViewId + offset
return {
viewId ,
enabled : offset < enabledCount ,
active : activeViewId . value === viewId
}
} )
}
} )
)
const viewIds = computed ( ( ) =>
browserViewGroups . value . flatMap ( group =>
group . views . filter ( view => view . enabled ) . map ( view => view . viewId )
)
)
const browserViewGroups = computed ( ( ) => normalizedGroupViewCounts . value . map ( ( enabledCount , groupIndex ) => {
const baseViewId = TIKTOK _VIEW _IDS [ groupIndex * 3 ]
return { groupIndex , label : ` 视图 ${ baseViewId } - ${ baseViewId + 2 } ` , views : Array . from ( { length : 3 } , ( _ , offset ) => ( { viewId : baseViewId + offset , enabled : offset < enabledCount , active : activeViewId . value === baseViewId + offset } ) ) }
} ) )
const viewIds = computed ( ( ) => browserViewGroups . value . flatMap ( group => group . views . filter ( view => view . enabled ) . map ( view => view . viewId ) ) )
const totalEnabledViews = computed ( ( ) => normalizedGroupViewCounts . value . reduce ( ( sum , count ) => sum + count , 0 ) )
const payloadPreview = computed ( ( ) => JSON . stringify ( buildStartPayload ( ) , null , 2 ) )
const statusChipClass = computed ( ( ) => {
if ( statusText . value . includes ( '运行' ) ) {
return 'px-3 py-1 rounded-full text-xs font-medium border bg-emerald-50 text-emerald-700 border-emerald-200'
}
if ( statusText . value . includes ( '失败' ) ) {
return 'px-3 py-1 rounded-full text-xs font-medium border bg-rose-50 text-rose-700 border-rose-200'
}
return 'px-3 py-1 rounded-full text-xs font-medium border bg-amber-50 text-amber-700 border-amber-200'
} )
function clampMinutes ( value ) {
const numericValue = Number ( value )
if ( ! Number . isFinite ( numericValue ) || numericValue <= 0 ) {
return 10
}
return Math . max ( 1 , Math . round ( numericValue ) )
}
function clampGroupCount ( value ) {
const numericValue = Number ( value )
if ( ! Number . isFinite ( numericValue ) ) {
return 0
}
return Math . min ( 3 , Math . max ( 0 , Math . round ( numericValue ) ) )
}
function normalizeGroupViewCounts ( values ) {
const safeValues = Array . isArray ( values ) ? values . slice ( 0 , 3 ) : [ ]
while ( safeValues . length < 3 ) {
safeValues . push ( 0 )
}
return safeValues . map ( clampGroupCount )
}
const currentPrepareKey = computed ( ( ) => JSON . stringify ( { groupViewCounts : normalizedGroupViewCounts . value , groupSwitchMinutes : normalizedGroupSwitchMinutes . value , replyUnreadMessages : Boolean ( configForm . replyUnreadMessages ) , dataPoolSource : configForm . dataPoolSource , replyMessages : replyMessages . value , prologueList : configForm . prologueList || { } , needTranslate : Boolean ( configForm . needTranslate ) } ) )
const hasPreparedViews = computed ( ( ) => preparedConfigKey . value === currentPrepareKey . value )
const statusChipClass = computed ( ( ) => statusText . value . includes ( '运行' ) ? 'rounded-full border border-emerald-200 bg-emerald-50 px-3 py-1 text-xs font-medium text-emerald-700' : statusText . value . includes ( '失败' ) ? 'rounded-full border border-rose-200 bg-rose-50 px-3 py-1 text-xs font-medium text-rose-700' : 'rounded-full border border-amber-200 bg-amber-50 px-3 py-1 text-xs font-medium text-amber-700' )
function clampMinutes ( value ) { const numericValue = Number ( value ) ; return ! Number . isFinite ( numericValue ) || numericValue <= 0 ? 10 : Math . max ( 1 , Math . round ( numericValue ) ) }
function clampGroupCount ( value ) { const numericValue = Number ( value ) ; return ! Number . isFinite ( numericValue ) ? 0 : Math . min ( 3 , Math . max ( 0 , Math . round ( numericValue ) ) ) }
function normalizeGroupViewCounts ( values ) { const safeValues = Array . isArray ( values ) ? values . slice ( 0 , 3 ) : [ ] ; while ( safeValues . length < 3 ) safeValues . push ( 0 ) ; return safeValues . map ( clampGroupCount ) }
function buildStartPayload ( ) {
const payload = {
replyUnreadMessages : Boolean ( configForm . replyUnread Messages ) ,
groupSwitchMinutes : normalizedGroupSwitchMinut es . value ,
groupViewCounts : normalizedGroupViewCounts . value
}
if ( greetingMessages . value . length > 0 ) {
payload . greetingMessages = greetingMessages . value
}
const payload = { dataPoolSource : configForm . dataPoolSource , replyUnreadMessages : Boolean ( configForm . replyUnreadMessages ) , groupSwitchMinutes : normalizedGroupSwitchMinutes . value , groupViewCounts : normalizedGroupViewCounts . value , prologueList : JSON . parse ( JSON . stringify ( configForm . prologueList || { } ) ) , needTranslate : Boolean ( configForm . needTranslate ) }
if ( greetingMessages . value . length > 0 ) payload . g reetingMessages = greeting Messages. value
if ( replyMessages . value . length > 0 ) payload . replyMessages = replyMessag es. value
return payload
}
function ensureElectronCapability ( methodName ) {
if ( ! isElectronEnv || ! window . electronAPI ? . [ methodName ] ) {
ElMessage . error ( '当前环境不支持该功能' )
return false
}
return true
}
function handleGreetingBlur ( ) {
configForm . greetingText = greetingMessages . value . join ( '\n' )
}
function handleSwitchMinutesBlur ( ) {
configForm . groupSwitchMinutes = clampMinutes ( configForm . groupSwitchMinutes )
}
function handleGroupCountBlur ( index ) {
configForm . groupViewCounts [ index ] = clampGroupCount ( configForm . groupViewCounts [ index ] )
}
function groupViewText ( count ) {
if ( count <= 0 ) {
return '不启用'
}
return ` 开启 ${ count } 个 `
}
function groupRangeLabel ( index ) {
const startViewId = TIKTOK _VIEW _IDS [ index * 3 ]
const endViewId = TIKTOK _VIEW _IDS [ index * 3 + 2 ]
return ` 视图 ${ startViewId } - ${ endViewId } `
}
function ensureElectronCapability ( methodName ) { if ( ! isElectronEnv || ! window . electronAPI ? . [ methodName ] ) { ElMessage . error ( '当前环境不支持该功能' ) ; return false } return true }
function invalidatePreparedState ( nextStatus = '配置已变更,请重新预热视图' ) { preparedConfigKey . value = '' ; loginConfirmed . value = false ; if ( ! statusText . value . includes ( '运行' ) ) statusText . value = nextStatus }
function handleGreetingConfirm ( data ) { configForm . prologueList = { yolo : data . sentences || [ ] , ... ( data . translations || { } ) } ; configForm . needTranslate = Boolean ( data . needTranslate ) ; showGreetingDialog . value = false ; invalidatePreparedState ( ) ; void saveSharedConfig ( ) }
function handleSwitchMinutesBlur ( ) { configForm . groupSwitchMinutes = clampMinutes ( configForm . groupSwitchMinutes ) ; invalidatePreparedState ( ) }
function handleGroupCountBlur ( index ) { configForm . groupViewCounts [ index ] = clampGroupCount ( configForm . groupViewCounts [ index ] ) ; invalidatePreparedState ( '视图配置已变更,请重新预热视图' ) }
function groupViewText ( count ) { return count <= 0 ? '不启用' : ` 开启 ${ count } 个 ` }
function groupRangeLabel ( index ) { const startViewId = TIKTOK _VIEW _IDS [ index * 3 ] ; const endViewId = TIKTOK _VIEW _IDS [ index * 3 + 2 ] ; return ` 视图 ${ startViewId } - ${ endViewId } ` }
async function checkAIConfig ( ) {
if ( ! isElectronEnv || ! window . electronAPI ? . loadAIConfig ) return
try {
const saved = await window . electronAPI . loadAIConfig ( )
if ( saved && ( saved . agentName || saved . guildName || saved . contactTool || saved . contact ) ) {
aiConfig . value = saved
aiConfigured . value = true
return
}
aiConfigured . value = false
aiConfigured . value = Boolean ( saved && ( saved . agentName || saved . guildName || saved . contactTool || saved . contact ) )
if ( aiConfigured . value ) aiConfig . value = saved
} catch {
aiConfigured . value = false
}
}
async function handleSaveAIConfig ( ) {
if ( isElectronEnv && window . electronAPI ? . saveAIConfig ) {
await window . electronAPI . saveAIConfig ( JSON . parse ( JSON . stringify ( aiConfig . value ) ) )
@@ -487,26 +296,51 @@ async function handleSaveAIConfig() {
aiConfigured . value = true
showAIDialog . value = false
}
async function loadSharedConfig ( ) {
if ( ! isElectronEnv || ! window . electronAPI ? . loadRunConfig ) return
try {
const saved = await window . electronAPI . loadRunConfig ( )
if ( saved ? . prologueList && typeof saved . prologueList === 'object' ) configForm . prologueList = JSON . parse ( JSON . stringify ( saved . prologueList ) )
if ( typeof saved ? . needTranslate === 'boolean' ) configForm . needTranslate = saved . needTranslate
if ( saved ? . standaloneTikTokDataPoolSource === 'anchor_hosts' || saved ? . standaloneTikTokDataPoolSource === 'brother_info' ) configForm . dataPoolSource = saved . standaloneTikTokDataPoolSource
if ( Array . isArray ( saved ? . standaloneTikTokReplyMessages ) ) configForm . replyMessagesText = saved . standaloneTikTokReplyMessages . join ( '\n' )
} catch ( error ) {
console . error ( 'load shared config failed:' , error )
}
}
async function saveSharedConfig ( ) {
if ( ! isElectronEnv || ! window . electronAPI ? . saveRunConfig || ! window . electronAPI ? . loadRunConfig ) return
try {
const saved = await window . electronAPI . loadRunConfig ( )
const nextConfig = {
... ( saved || { } ) ,
prologueList : JSON . parse ( JSON . stringify ( configForm . prologueList || { } ) ) ,
needTranslate : Boolean ( configForm . needTranslate ) ,
standaloneTikTokDataPoolSource : configForm . dataPoolSource ,
standaloneTikTokReplyMessages : replyMessages . value
}
await window . electronAPI . saveRunConfig ( nextConfig )
} catch ( error ) {
console . error ( 'save shared config failed:' , error )
}
}
async function handlePrepareViews ( ) {
if ( ! ensureElectronCapability ( 'prepareStandaloneTikTokViews' ) ) return
if ( viewIds . value . length === 0 ) {
ElMessage . warning ( '请至少启用一个视图后再预热' )
return
}
loginConfirmed . value = false
isPreparing . value = true
try {
const result = await window . electronAPI . prepareStandaloneTikTokViews ( {
groupViewCounts : normalizedGroupViewCounts . value ,
targe tViewId : activeViewId . value
} )
if ( ! result ? . success ) {
throw new Error ( result ? . error || 'prepare standalone tiktok views failed' )
}
if ( result . currentViewId ) {
activeViewId . value = result . currentViewId
}
const result = await window . electronAPI . prepareStandaloneTikTokViews ( { groupViewCounts : normalizedGroupViewCounts . value , targetViewId : activeViewId . value } )
if ( ! result ? . success ) throw new Error ( result ? . error || 'prepare standalone tiktok views failed' )
if ( result . curren tViewId) activeViewId . value = result . currentViewId
preparedConfigKey . value = currentPrepareKey . value
await saveSharedConfig ( )
pageMode . value = 'browser'
await window . electronAPI . showViews ( )
statusText . value = ` 请先在视图 ${ activeViewId . value } 手动登录,完成后 再启动脚本 `
statusText . value = ` 视图已预热, 请先在视图 ${ activeViewId . value } 手动登录,再启动脚本 `
ElMessage . success ( '视图预热完成,已切换到浏览器视图' )
} catch ( error ) {
const message = error instanceof Error ? error . message : String ( error )
@@ -515,23 +349,20 @@ async function handlePrepareViews() {
isPreparing . value = false
}
}
async function openBrowserView ( ) {
if ( ! ensureElectronCapability ( 'switchToView' ) ) return
pageMode . value = 'browser'
try {
await window . electronAPI . switchToView ( activeViewId . value )
await window . electronAPI . showViews ( )
if ( ! statusText . value . includes ( '运行' ) ) {
statusText . value = ` 已打开视图 ${ activeViewId . value } , 如 未预热请先返回工作台执行预热视图`
statusText . value = hasPreparedViews . value ? ` 已打开预热 视图 ${ activeViewId . value } , 请手动登录后启动脚本 ` : '当前配置尚 未预热, 请先返回工作台执行预热视图'
}
} catch ( error ) {
const message = error instanceof Error ? error . message : String ( error )
ElMessage . error ( ` 打开浏览器视图失败: ${ message } ` )
}
}
async function backToConfig ( ) {
loginConfirmed . value = false
pageMode . value = 'config'
@@ -542,10 +373,8 @@ async function backToConfig() {
console . error ( 'hide views failed:' , error )
}
}
async function handleSwitchView ( viewId ) {
if ( ! ensureElectronCapability ( 'switchToView' ) ) return
isSwitchingView . value = true
try {
await window . electronAPI . switchToView ( viewId )
@@ -560,16 +389,23 @@ async function handleSwitchView(viewId) {
isSwitchingView . value = false
}
}
async function handleStart ( ) {
if ( ! ensureElectronCapability ( 'startStandaloneTikTokAutomationAll' ) ) return
if ( ! loginConfirmed . value ) {
ElMessage . warning ( '请先在当前视图手动登录,并勾选“已完成登录”后 再启动脚本' )
if ( ! hasPreparedViews . value ) {
ElMessage . warning ( '请先完成当前配置对应的视图预热, 再启动脚本' )
return
}
if ( isBrotherInfoMode . value && replyMessages . value . length === 0 ) {
ElMessage . warning ( '选择大哥池后,请先填写大哥随机回复话术' )
return
}
if ( ! loginConfirmed . value ) {
ElMessage . warning ( '请先在当前视图手动登录,并勾选“当前视图已完成手动登录”后再启动脚本' )
return
}
isStarting . value = true
try {
await saveSharedConfig ( )
await window . electronAPI . switchToView ( activeViewId . value )
await window . electronAPI . showViews ( )
const result = await window . electronAPI . startStandaloneTikTokAutomationAll ( buildStartPayload ( ) )
@@ -588,10 +424,8 @@ async function handleStart() {
isStarting . value = false
}
}
async function handleStop ( ) {
if ( ! ensureElectronCapability ( 'stopStandaloneTikTokAutomationAll' ) ) return
isStopping . value = true
try {
const result = await window . electronAPI . stopStandaloneTikTokAutomationAll ( )
@@ -600,6 +434,8 @@ async function handleStop() {
ElMessage . error ( result ? . error || '停止自动私信( TK版) 失败' )
return
}
preparedConfigKey . value = ''
loginConfirmed . value = false
statusText . value = '已停止'
ElMessage . success ( '自动私信( TK版) 已停止' )
} catch ( error ) {
@@ -610,33 +446,37 @@ async function handleStop() {
isStopping . value = false
}
}
watch ( currentPrepareKey , ( nextKey , prevKey ) => {
if ( ! prevKey ) return
if ( preparedConfigKey . value && preparedConfigKey . value !== nextKey ) invalidatePreparedState ( )
} )
watch ( ( ) => configForm . dataPoolSource , ( ) => {
invalidatePreparedState ( )
void saveSharedConfig ( )
} )
watch ( ( ) => configForm . replyMessagesText , ( ) => {
if ( ! isBrotherInfoMode . value ) return
invalidatePreparedState ( )
} )
watch ( viewIds , ( nextViewIds ) => {
if ( nextViewIds . length === 0 ) {
activeViewId . value = TIKTOK _VIEW _IDS [ 0 ]
return
}
if ( ! nextViewIds . includes ( activeViewId . value ) ) {
activeViewId . value = nextViewIds [ 0 ]
}
if ( ! nextViewIds . includes ( activeViewId . value ) ) activeViewId . value = nextViewIds [ 0 ]
} , { immediate : true } )
watch ( pageMode , async ( newVal ) => {
if ( ! isElectronEnv ) return
if ( newVal === 'config' && window . electronAPI ? . hideViews ) {
await window . electronAPI . hideViews ( ) . catch ( ( ) => { } )
}
if ( newVal === 'config' && window . electronAPI ? . hideViews ) await window . electronAPI . hideViews ( ) . catch ( ( ) => { } )
} )
onMounted ( async ( ) => {
await checkAIConfig ( )
await loadSharedConfig ( )
if ( ! isElectronEnv || ! window . electronAPI ? . hideViews ) return
await window . electronAPI . hideViews ( ) . catch ( ( ) => { } )
await window . electronAPI . hideViews ( ) . catch ( ( ) => { } )
} )
onUnmounted ( async ( ) => {
if ( ! isElectronEnv || ! window . electronAPI ? . hideViews ) return
await window . electronAPI . hideViews ( ) . catch ( ( ) => { } )
await window . electronAPI . hideViews ( ) . catch ( ( ) => { } )
} )
< / script >