tk版大哥池隐藏回复

This commit is contained in:
2026-04-24 16:13:45 +08:00
parent fdc87fa88a
commit deb9fc98f0
2 changed files with 62 additions and 20 deletions

View File

@@ -404,7 +404,7 @@ const languageList = [
{ name: '中文 (繁體)', code: 'zh-Hant-TW' }, { name: '中文 (繁體)', code: 'zh-Hant-TW' },
{ name: 'Deutsch', code: 'de-DE' }, { name: 'Deutsch', code: 'de-DE' },
{ name: 'Italiano', code: 'it-IT' }, { name: 'Italiano', code: 'it-IT' },
{ name: 'Français', code: 'fr-FR' }, { name: 'Français', code: 'fr' },
{ name: 'Română', code: 'ro-RO' }, { name: 'Română', code: 'ro-RO' },
{ name: 'Polski', code: 'pl-PL' }, { name: 'Polski', code: 'pl-PL' },
{ name: 'Nederlands', code: 'nl-NL' }, { name: 'Nederlands', code: 'nl-NL' },

View File

@@ -9,7 +9,7 @@
</div> </div>
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<span :class="statusChipClass">{{ statusText }}</span> <span :class="statusChipClass">{{ statusText }}</span>
<button @click="handlePrepareViews" :disabled="isPreparing || !isElectronEnv" <button @click="handlePrepareViews" :disabled="isPrepareButtonDisabled"
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"> 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> <span>{{ isPreparing ? '预热中...' : '预热视图' }}</span>
</button> </button>
@@ -26,6 +26,11 @@
先填写配置再预热视图登录完成后再启动脚本 先填写配置再预热视图登录完成后再启动脚本
</div> </div>
<div v-if="isConfigLocked"
class="mb-6 rounded-xl border border-amber-200 bg-amber-50 px-4 py-3 text-sm leading-6 text-amber-800">
当前配置已锁定停止任务成功后才可以修改配置重新预热并再次启动
</div>
<div class="grid grid-cols-3 gap-6"> <div class="grid grid-cols-3 gap-6">
<div class="col-span-2 space-y-6"> <div class="col-span-2 space-y-6">
<section class="rounded-xl border border-gray-200 bg-gradient-to-b from-white to-gray-50 p-6 shadow-sm"> <section class="rounded-xl border border-gray-200 bg-gradient-to-b from-white to-gray-50 p-6 shadow-sm">
@@ -56,7 +61,7 @@
<div> <div>
<label class="mb-1 block text-sm font-medium text-gray-700">打招呼内容</label> <label class="mb-1 block text-sm font-medium text-gray-700">打招呼内容</label>
</div> </div>
<button @click="showGreetingDialog = true" :disabled="isStarting || !isElectronEnv" <button @click="showGreetingDialog = true" :disabled="isConfigControlDisabled"
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"> 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> </button>
@@ -67,28 +72,28 @@
<span class="rounded-full border border-gray-200 bg-gray-100 px-3 py-1">翻译 {{ <span class="rounded-full border border-gray-200 bg-gray-100 px-3 py-1">翻译 {{
configForm.needTranslate ? '已开启' : '未开启' }}</span> configForm.needTranslate ? '已开启' : '未开启' }}</span>
<span class="rounded-full border border-gray-200 bg-gray-100 px-3 py-1">语种 {{ languageCount <span class="rounded-full border border-gray-200 bg-gray-100 px-3 py-1">语种 {{ languageCount
}}</span> }}</span>
</div> </div>
</div> </div>
<div class="grid grid-cols-1 gap-4 md:grid-cols-[minmax(0,1fr),minmax(0,1fr),220px] items-end"> <div class="grid grid-cols-1 gap-4 md:grid-cols-[minmax(0,1fr),minmax(0,1fr),220px] items-end">
<div> <div>
<label class="mb-2 block text-sm font-medium text-gray-700">分组轮换间隔分钟</label> <label class="mb-2 block text-sm font-medium text-gray-700">分组轮换间隔分钟</label>
<input v-model.number="configForm.groupSwitchMinutes" :disabled="isStarting || !isElectronEnv" <input v-model.number="configForm.groupSwitchMinutes" :disabled="isConfigControlDisabled"
type="number" min="1" step="1" 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" 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" /> @blur="handleSwitchMinutesBlur" />
</div> </div>
<div> <div>
<label class="mb-2 block text-sm font-medium text-gray-700">每用户休眠秒数</label> <label class="mb-2 block text-sm font-medium text-gray-700">每用户休眠秒数</label>
<input v-model.number="configForm.postUserSleepSeconds" :disabled="isStarting || !isElectronEnv" <input v-model.number="configForm.postUserSleepSeconds" :disabled="isConfigControlDisabled"
type="number" min="1" step="1" 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" 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="handlePostUserSleepBlur" /> @blur="handlePostUserSleepBlur" />
</div> </div>
<label <label v-if="!isBrotherInfoMode"
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"> 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" <input v-model="configForm.replyUnreadMessages" :disabled="isConfigControlDisabled"
type="checkbox" class="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500" /> type="checkbox" class="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500" />
<span>开启 AI 回复未读消息</span> <span>开启 AI 回复未读消息</span>
</label> </label>
@@ -106,27 +111,27 @@
<div class="grid grid-cols-2 gap-3"> <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" <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'"> :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" <input v-model="configForm.dataPoolSource" :disabled="isConfigControlDisabled" type="radio"
value="anchor_hosts" class="h-4 w-4 border-gray-300 text-blue-600 focus:ring-blue-500" /> value="anchor_hosts" class="h-4 w-4 border-gray-300 text-blue-600 focus:ring-blue-500" />
<span>主播池</span> <span>主播池</span>
</label> </label>
<label class="flex items-center gap-3 rounded-lg border px-4 py-3 text-sm transition-colors" <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'"> :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" <input v-model="configForm.dataPoolSource" :disabled="isConfigControlDisabled" type="radio"
value="brother_info" class="h-4 w-4 border-gray-300 text-purple-600 focus:ring-purple-500" /> value="brother_info" class="h-4 w-4 border-gray-300 text-purple-600 focus:ring-purple-500" />
<span>大哥池</span> <span>大哥池</span>
</label> </label>
</div> </div>
<div v-if="isBrotherInfoMode" class="mt-4"> <!-- <div v-if="isBrotherInfoMode" class="mt-4">
<div class="mb-2 flex items-center justify-between gap-3"> <div class="mb-2 flex items-center justify-between gap-3">
<label class="block text-sm font-medium text-gray-700">大哥随机回复话术</label> <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">{{ <span class="rounded-full border border-gray-200 bg-gray-100 px-3 py-1 text-xs text-gray-600">{{
replyMessageCount }} </span> replyMessageCount }} </span>
</div> </div>
<textarea v-model="configForm.replyMessagesText" :disabled="isStarting || !isElectronEnv" rows="5" <textarea v-model="configForm.replyMessagesText" :disabled="isConfigControlDisabled" rows="5"
placeholder="一行一条回复话术,启动时传给后端 replyMessages" 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" /> 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> </div>
</div> </div>
</section> </section>
@@ -148,9 +153,9 @@
<div class="mt-1 text-xs text-gray-500">{{ groupRangeLabel(index) }}</div> <div class="mt-1 text-xs text-gray-500">{{ groupRangeLabel(index) }}</div>
</div> </div>
<span class="rounded-full bg-gray-100 px-2 py-1 text-[10px] text-gray-600">{{ groupViewText(count) <span class="rounded-full bg-gray-100 px-2 py-1 text-[10px] text-gray-600">{{ groupViewText(count)
}}</span> }}</span>
</div> </div>
<input v-model.number="configForm.groupViewCounts[index]" :disabled="isStarting || !isElectronEnv" <input v-model.number="configForm.groupViewCounts[index]" :disabled="isConfigControlDisabled"
type="number" min="0" max="3" step="1" 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" 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)" /> @blur="handleGroupCountBlur(index)" />
@@ -169,7 +174,7 @@
:class="['rounded-full px-3 py-1 text-xs font-medium', aiConfigured ? 'bg-green-100 text-green-700' : 'bg-yellow-100 text-yellow-700']">{{ :class="['rounded-full px-3 py-1 text-xs font-medium', aiConfigured ? 'bg-green-100 text-green-700' : 'bg-yellow-100 text-yellow-700']">{{
aiConfigured ? '已配置' : '未配置' }}</span> aiConfigured ? '已配置' : '未配置' }}</span>
</div> </div>
<button @click="showAIDialog = true" <button @click="showAIDialog = true" :disabled="isConfigControlDisabled"
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">配置 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> / 修改 AI 人设</button>
</section> </section>
@@ -270,7 +275,7 @@
{{ viewId }}</button> {{ viewId }}</button>
<div class="flex-1" /> <div class="flex-1" />
<span class="rounded border border-gray-200 bg-gray-100 px-2 py-1 text-xs text-gray-500">{{ statusText <span class="rounded border border-gray-200 bg-gray-100 px-2 py-1 text-xs text-gray-500">{{ statusText
}}</span> }}</span>
<span v-if="isSwitchingView" <span v-if="isSwitchingView"
class="rounded border border-blue-200 bg-blue-50 px-2 py-1 text-xs text-blue-600">切换中...</span> class="rounded border border-blue-200 bg-blue-50 px-2 py-1 text-xs text-blue-600">切换中...</span>
</div> </div>
@@ -313,6 +318,7 @@ const showBrotherInfoDialog = ref(false)
const aiConfigured = ref(false) const aiConfigured = ref(false)
const loginConfirmed = ref(false) const loginConfirmed = ref(false)
const preparedConfigKey = ref('') const preparedConfigKey = ref('')
const workbenchPhase = ref('idle')
const isPreparing = ref(false) const isPreparing = ref(false)
const isStarting = ref(false) const isStarting = ref(false)
const isStopping = ref(false) const isStopping = ref(false)
@@ -329,6 +335,7 @@ const greetingCount = computed(() => greetingMessages.value.length)
const replyMessageCount = computed(() => replyMessages.value.length) const replyMessageCount = computed(() => replyMessages.value.length)
const languageCount = computed(() => Object.keys(configForm.prologueList || {}).length) const languageCount = computed(() => Object.keys(configForm.prologueList || {}).length)
const isBrotherInfoMode = computed(() => configForm.dataPoolSource === 'brother_info') const isBrotherInfoMode = computed(() => configForm.dataPoolSource === 'brother_info')
const effectiveReplyUnreadMessages = computed(() => !isBrotherInfoMode.value && Boolean(configForm.replyUnreadMessages))
const dataPoolLabel = computed(() => (isBrotherInfoMode.value ? '大哥池' : '主播池')) const dataPoolLabel = computed(() => (isBrotherInfoMode.value ? '大哥池' : '主播池'))
const normalizedGroupSwitchMinutes = computed(() => clampMinutes(configForm.groupSwitchMinutes)) const normalizedGroupSwitchMinutes = computed(() => clampMinutes(configForm.groupSwitchMinutes))
const normalizedGroupViewCounts = computed(() => normalizeGroupViewCounts(configForm.groupViewCounts)) const normalizedGroupViewCounts = computed(() => normalizeGroupViewCounts(configForm.groupViewCounts))
@@ -339,8 +346,11 @@ const browserViewGroups = computed(() => normalizedGroupViewCounts.value.map((en
const viewIds = computed(() => browserViewGroups.value.flatMap(group => group.views.filter(view => view.enabled).map(view => view.viewId))) 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 totalEnabledViews = computed(() => normalizedGroupViewCounts.value.reduce((sum, count) => sum + count, 0))
const payloadPreview = computed(() => JSON.stringify(buildStartPayload(), null, 2)) const payloadPreview = computed(() => JSON.stringify(buildStartPayload(), null, 2))
const currentPrepareKey = computed(() => JSON.stringify({ groupViewCounts: normalizedGroupViewCounts.value, groupSwitchMinutes: normalizedGroupSwitchMinutes.value, postUserSleepSeconds: normalizedPostUserSleepSeconds.value, replyUnreadMessages: Boolean(configForm.replyUnreadMessages), dataPoolSource: configForm.dataPoolSource, replyMessages: replyMessages.value, prologueList: configForm.prologueList || {}, needTranslate: Boolean(configForm.needTranslate) })) const currentPrepareKey = computed(() => JSON.stringify({ groupViewCounts: normalizedGroupViewCounts.value, groupSwitchMinutes: normalizedGroupSwitchMinutes.value, postUserSleepSeconds: normalizedPostUserSleepSeconds.value, replyUnreadMessages: effectiveReplyUnreadMessages.value, dataPoolSource: configForm.dataPoolSource, replyMessages: replyMessages.value, prologueList: configForm.prologueList || {}, needTranslate: Boolean(configForm.needTranslate) }))
const hasPreparedViews = computed(() => preparedConfigKey.value === currentPrepareKey.value) const hasPreparedViews = computed(() => preparedConfigKey.value === currentPrepareKey.value)
const isConfigLocked = computed(() => workbenchPhase.value !== 'idle')
const isConfigControlDisabled = computed(() => !isElectronEnv || isPreparing.value || isStarting.value || isStopping.value || isConfigLocked.value)
const isPrepareButtonDisabled = computed(() => !isElectronEnv || isPreparing.value || isStarting.value || isStopping.value || isConfigLocked.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') 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 clampMinutes(value) { const numericValue = Number(value); return !Number.isFinite(numericValue) || numericValue <= 0 ? 10 : Math.max(1, Math.round(numericValue)) }
function clampPositiveSeconds(value, fallback = 6) { const numericValue = Number(value); return !Number.isFinite(numericValue) || numericValue <= 0 ? fallback : Math.max(1, Math.round(numericValue)) } function clampPositiveSeconds(value, fallback = 6) { const numericValue = Number(value); return !Number.isFinite(numericValue) || numericValue <= 0 ? fallback : Math.max(1, Math.round(numericValue)) }
@@ -348,13 +358,32 @@ function clampGroupCount(value) { const numericValue = Number(value); return !Nu
function normalizeGroupViewCounts(values) { const safeValues = Array.isArray(values) ? values.slice(0, 3) : []; while (safeValues.length < 3) safeValues.push(0); return safeValues.map(clampGroupCount) } function normalizeGroupViewCounts(values) { const safeValues = Array.isArray(values) ? values.slice(0, 3) : []; while (safeValues.length < 3) safeValues.push(0); return safeValues.map(clampGroupCount) }
const normalizedPostUserSleepSeconds = computed(() => clampPositiveSeconds(configForm.postUserSleepSeconds, 6)) const normalizedPostUserSleepSeconds = computed(() => clampPositiveSeconds(configForm.postUserSleepSeconds, 6))
function buildStartPayload() { function buildStartPayload() {
const payload = { dataPoolSource: configForm.dataPoolSource, replyUnreadMessages: Boolean(configForm.replyUnreadMessages), groupSwitchMinutes: normalizedGroupSwitchMinutes.value, postUserSleepSeconds: normalizedPostUserSleepSeconds.value, groupViewCounts: normalizedGroupViewCounts.value, prologueList: JSON.parse(JSON.stringify(configForm.prologueList || {})), needTranslate: Boolean(configForm.needTranslate) } const payload = { dataPoolSource: configForm.dataPoolSource, replyUnreadMessages: effectiveReplyUnreadMessages.value, groupSwitchMinutes: normalizedGroupSwitchMinutes.value, postUserSleepSeconds: normalizedPostUserSleepSeconds.value, groupViewCounts: normalizedGroupViewCounts.value, prologueList: JSON.parse(JSON.stringify(configForm.prologueList || {})), needTranslate: Boolean(configForm.needTranslate) }
if (greetingMessages.value.length > 0) payload.greetingMessages = greetingMessages.value if (greetingMessages.value.length > 0) payload.greetingMessages = greetingMessages.value
if (replyMessages.value.length > 0) payload.replyMessages = replyMessages.value if (replyMessages.value.length > 0) payload.replyMessages = replyMessages.value
return payload return payload
} }
function ensureElectronCapability(methodName) { if (!isElectronEnv || !window.electronAPI?.[methodName]) { ElMessage.error('当前环境不支持该功能'); return false } return true } 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 invalidatePreparedState(nextStatus = '配置已变更,请重新预热视图') { preparedConfigKey.value = ''; loginConfirmed.value = false; if (!statusText.value.includes('运行')) statusText.value = nextStatus }
async function syncWorkbenchState() {
if (!isElectronEnv || !window.electronAPI?.getStandaloneTikTokWorkbenchState) return
try {
const state = await window.electronAPI.getStandaloneTikTokWorkbenchState()
workbenchPhase.value = state?.phase || 'idle'
if (state?.locked) {
preparedConfigKey.value = currentPrepareKey.value
if (state.phase === 'running') {
statusText.value = '运行中'
} else if (!statusText.value.includes('运行')) {
statusText.value = '视图已预热,当前配置已锁定'
}
} else {
preparedConfigKey.value = ''
}
} catch (error) {
console.error('sync standalone tiktok workbench state failed:', error)
}
}
async function showBrowserWorkspace(viewId, fallbackStatusText) { async function showBrowserWorkspace(viewId, fallbackStatusText) {
pageMode.value = 'browser' pageMode.value = 'browser'
await window.electronAPI.switchToView(viewId) await window.electronAPI.switchToView(viewId)
@@ -416,6 +445,10 @@ async function saveSharedConfig() {
} }
async function handlePrepareViews() { async function handlePrepareViews() {
if (!ensureElectronCapability('prepareStandaloneTikTokViews')) return if (!ensureElectronCapability('prepareStandaloneTikTokViews')) return
if (isConfigLocked.value) {
ElMessage.warning('当前配置已锁定,请先停止任务后再修改配置或重新预热')
return
}
if (viewIds.value.length === 0) { if (viewIds.value.length === 0) {
ElMessage.warning('请至少启用一个视图后再预热') ElMessage.warning('请至少启用一个视图后再预热')
return return
@@ -426,6 +459,7 @@ async function handlePrepareViews() {
const result = await window.electronAPI.prepareStandaloneTikTokViews({ groupViewCounts: normalizedGroupViewCounts.value, targetViewId: activeViewId.value }) 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?.success) throw new Error(result?.error || 'prepare standalone tiktok views failed')
if (result.currentViewId) activeViewId.value = result.currentViewId if (result.currentViewId) activeViewId.value = result.currentViewId
workbenchPhase.value = 'prepared'
preparedConfigKey.value = currentPrepareKey.value preparedConfigKey.value = currentPrepareKey.value
await saveSharedConfig() await saveSharedConfig()
if (pageMode.value === 'config') { if (pageMode.value === 'config') {
@@ -487,6 +521,10 @@ async function handleSwitchView(viewId) {
} }
async function handleStart() { async function handleStart() {
if (!ensureElectronCapability('startStandaloneTikTokAutomationAll')) return if (!ensureElectronCapability('startStandaloneTikTokAutomationAll')) return
if (workbenchPhase.value === 'running') {
ElMessage.warning('当前任务已在运行中,请先停止后再重新启动')
return
}
if (!hasPreparedViews.value) { if (!hasPreparedViews.value) {
ElMessage.warning('请先完成当前配置对应的视图预热,再启动脚本') ElMessage.warning('请先完成当前配置对应的视图预热,再启动脚本')
return return
@@ -509,6 +547,7 @@ async function handleStart() {
ElMessage.error(result?.error || '启动自动私信TK版失败') ElMessage.error(result?.error || '启动自动私信TK版失败')
return return
} }
workbenchPhase.value = 'running'
statusText.value = `运行中(视图 ${activeViewId.value}` statusText.value = `运行中(视图 ${activeViewId.value}`
ElMessage.success('自动私信TK版已启动') ElMessage.success('自动私信TK版已启动')
} catch (error) { } catch (error) {
@@ -529,9 +568,10 @@ async function handleStop() {
ElMessage.error(result?.error || '停止自动私信TK版失败') ElMessage.error(result?.error || '停止自动私信TK版失败')
return return
} }
workbenchPhase.value = 'idle'
preparedConfigKey.value = '' preparedConfigKey.value = ''
loginConfirmed.value = false loginConfirmed.value = false
statusText.value = '已停止' statusText.value = result?.message?.includes('取消预热状态') ? '已解锁' : '已停止'
ElMessage.success('自动私信TK版已停止') ElMessage.success('自动私信TK版已停止')
} catch (error) { } catch (error) {
const message = error instanceof Error ? error.message : String(error) const message = error instanceof Error ? error.message : String(error)
@@ -546,6 +586,7 @@ watch(currentPrepareKey, (nextKey, prevKey) => {
if (preparedConfigKey.value && preparedConfigKey.value !== nextKey) invalidatePreparedState() if (preparedConfigKey.value && preparedConfigKey.value !== nextKey) invalidatePreparedState()
}) })
watch(() => configForm.dataPoolSource, () => { watch(() => configForm.dataPoolSource, () => {
if (isBrotherInfoMode.value && configForm.replyUnreadMessages) configForm.replyUnreadMessages = false
invalidatePreparedState() invalidatePreparedState()
void saveSharedConfig() void saveSharedConfig()
}) })
@@ -567,6 +608,7 @@ watch(pageMode, async (newVal) => {
onMounted(async () => { onMounted(async () => {
await checkAIConfig() await checkAIConfig()
await loadSharedConfig() await loadSharedConfig()
await syncWorkbenchState()
if (!isElectronEnv || !window.electronAPI?.hideViews) return if (!isElectronEnv || !window.electronAPI?.hideViews) return
await window.electronAPI.hideViews().catch(() => { }) await window.electronAPI.hideViews().catch(() => { })
}) })