diff --git a/src/types/electron.d.ts b/src/types/electron.d.ts
index d2087a7..d6a7489 100644
--- a/src/types/electron.d.ts
+++ b/src/types/electron.d.ts
@@ -89,6 +89,7 @@ export interface StandaloneTikTokAutomationOptions {
dataPoolSource?: 'anchor_hosts' | 'brother_info' | '/api/anchor/hosts/getAll' | '/api/gifters/brotherInfo/getAll'
groupSwitchMinutes?: number
groupViewCounts?: number[]
+ initialFullGroupNumber?: number
continuousMode?: boolean
runMode?: string
homeUrl?: string
diff --git a/src/views/auto-dm/AutoDmTkWorkbench.vue b/src/views/auto-dm/AutoDmTkWorkbench.vue
index 8278683..2fca8f8 100644
--- a/src/views/auto-dm/AutoDmTkWorkbench.vue
+++ b/src/views/auto-dm/AutoDmTkWorkbench.vue
@@ -72,11 +72,11 @@
翻译 {{
configForm.needTranslate ? '已开启' : '未开启' }}
语种 {{ languageCount
- }}
+ }}
-
{{ groupViewText(count)
- }}
+ }}
打开大哥池
-
+
@@ -275,7 +286,7 @@
{{ viewId }}
{{ statusText
- }}
+ }}
切换中...
@@ -307,6 +318,7 @@ import BrotherInfoDialog from '@/components/BrotherInfoDialog.vue'
const props = defineProps({ navSidebarWidth: { type: Number, default: 144 } })
const TIKTOK_VIEW_IDS = Array.from({ length: 9 }, (_, index) => index + 10)
const DEFAULT_GROUP_COUNTS = [3, 3, 3]
+const DEFAULT_INITIAL_FULL_GROUP_NUMBER = 1
const isElectronEnv = isElectron()
const pageMode = ref('config')
const activeViewId = ref(TIKTOK_VIEW_IDS[0])
@@ -323,7 +335,7 @@ const isPreparing = ref(false)
const isStarting = ref(false)
const isStopping = ref(false)
const isSwitchingView = ref(false)
-const configForm = reactive({ prologueList: {}, needTranslate: false, dataPoolSource: 'anchor_hosts', replyMessagesText: '', replyUnreadMessages: true, groupSwitchMinutes: 10, postUserSleepSeconds: 6, groupViewCounts: [...DEFAULT_GROUP_COUNTS] })
+const configForm = reactive({ prologueList: {}, needTranslate: false, dataPoolSource: 'anchor_hosts', replyMessagesText: '', replyUnreadMessages: true, groupSwitchMinutes: 10, initialFullGroupNumber: DEFAULT_INITIAL_FULL_GROUP_NUMBER, postUserSleepSeconds: 6, groupViewCounts: [...DEFAULT_GROUP_COUNTS] })
const aiConfig = ref({ agentName: '', guildName: '', contactTool: '', contact: '' })
const sidebarStyle = computed(() => ({ width: `${props.navSidebarWidth}px`, minWidth: '96px', maxWidth: '400px' }))
const greetingMessages = computed(() => {
@@ -339,6 +351,8 @@ const effectiveReplyUnreadMessages = computed(() => !isBrotherInfoMode.value &&
const dataPoolLabel = computed(() => (isBrotherInfoMode.value ? '大哥池' : '主播池'))
const normalizedGroupSwitchMinutes = computed(() => clampMinutes(configForm.groupSwitchMinutes))
const normalizedGroupViewCounts = computed(() => normalizeGroupViewCounts(configForm.groupViewCounts))
+const effectiveInitialFullGroupNumber = computed(() => resolveInitialFullGroupNumber(configForm.initialFullGroupNumber, normalizedGroupViewCounts.value))
+const startFullGroupOptions = computed(() => normalizedGroupViewCounts.value.map((enabledCount, groupIndex) => ({ value: groupIndex + 1, label: `第 ${groupIndex + 1} 组`, hint: groupRangeLabel(groupIndex), enabled: enabledCount > 0 })))
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 })) }
@@ -346,7 +360,7 @@ 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 totalEnabledViews = computed(() => normalizedGroupViewCounts.value.reduce((sum, count) => sum + count, 0))
const payloadPreview = computed(() => JSON.stringify(buildStartPayload(), null, 2))
-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 currentPrepareKey = computed(() => JSON.stringify({ groupViewCounts: normalizedGroupViewCounts.value, groupSwitchMinutes: normalizedGroupSwitchMinutes.value, initialFullGroupNumber: effectiveInitialFullGroupNumber.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 isConfigLocked = computed(() => workbenchPhase.value !== 'idle')
const isConfigControlDisabled = computed(() => !isElectronEnv || isPreparing.value || isStarting.value || isStopping.value || isConfigLocked.value)
@@ -355,10 +369,12 @@ const statusChipClass = computed(() => statusText.value.includes('运行') ? 'ro
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 clampGroupCount(value) { const numericValue = Number(value); return !Number.isFinite(numericValue) ? 0 : Math.min(3, Math.max(0, Math.round(numericValue))) }
+function clampInitialFullGroupNumber(value) { const numericValue = Number(value); return !Number.isFinite(numericValue) ? DEFAULT_INITIAL_FULL_GROUP_NUMBER : Math.min(DEFAULT_GROUP_COUNTS.length, Math.max(1, 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 resolveInitialFullGroupNumber(value, groupViewCounts) { const requestedGroupNumber = clampInitialFullGroupNumber(value); for (let offset = 0; offset < groupViewCounts.length; offset += 1) { const candidateIndex = (requestedGroupNumber - 1 + offset) % groupViewCounts.length; if ((groupViewCounts[candidateIndex] || 0) > 0) return candidateIndex + 1 } return requestedGroupNumber }
const normalizedPostUserSleepSeconds = computed(() => clampPositiveSeconds(configForm.postUserSleepSeconds, 6))
function buildStartPayload() {
- 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) }
+ const payload = { dataPoolSource: configForm.dataPoolSource, replyUnreadMessages: effectiveReplyUnreadMessages.value, groupSwitchMinutes: normalizedGroupSwitchMinutes.value, initialFullGroupNumber: effectiveInitialFullGroupNumber.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 (replyMessages.value.length > 0) payload.replyMessages = replyMessages.value
return payload
@@ -392,8 +408,9 @@ async function showBrowserWorkspace(viewId, fallbackStatusText) {
}
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 handleInitialFullGroupChange() { configForm.initialFullGroupNumber = effectiveInitialFullGroupNumber.value; invalidatePreparedState(); void saveSharedConfig() }
function handlePostUserSleepBlur() { configForm.postUserSleepSeconds = clampPositiveSeconds(configForm.postUserSleepSeconds, 6); invalidatePreparedState() }
-function handleGroupCountBlur(index) { configForm.groupViewCounts[index] = clampGroupCount(configForm.groupViewCounts[index]); invalidatePreparedState('视图配置已变更,请重新预热视图') }
+function handleGroupCountBlur(index) { configForm.groupViewCounts[index] = clampGroupCount(configForm.groupViewCounts[index]); configForm.initialFullGroupNumber = effectiveInitialFullGroupNumber.value; 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() {
@@ -421,6 +438,7 @@ async function loadSharedConfig() {
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')
+ if (saved?.standaloneTikTokInitialFullGroupNumber !== undefined) configForm.initialFullGroupNumber = clampInitialFullGroupNumber(saved.standaloneTikTokInitialFullGroupNumber)
if (saved?.standaloneTikTokPostUserSleepSeconds !== undefined) configForm.postUserSleepSeconds = clampPositiveSeconds(saved.standaloneTikTokPostUserSleepSeconds, 6)
} catch (error) {
console.error('load shared config failed:', error)
@@ -436,6 +454,7 @@ async function saveSharedConfig() {
needTranslate: Boolean(configForm.needTranslate),
standaloneTikTokDataPoolSource: configForm.dataPoolSource,
standaloneTikTokReplyMessages: replyMessages.value,
+ standaloneTikTokInitialFullGroupNumber: effectiveInitialFullGroupNumber.value,
standaloneTikTokPostUserSleepSeconds: normalizedPostUserSleepSeconds.value
}
await window.electronAPI.saveRunConfig(nextConfig)
@@ -529,10 +548,6 @@ async function handleStart() {
ElMessage.warning('请先完成当前配置对应的视图预热,再启动脚本')
return
}
- if (isBrotherInfoMode.value && replyMessages.value.length === 0) {
- ElMessage.warning('选择大哥池后,请先填写大哥随机回复话术')
- return
- }
if (!loginConfirmed.value) {
ElMessage.warning('请先在当前视图手动登录,并勾选“当前视图已完成手动登录”后再启动脚本')
return
@@ -594,6 +609,10 @@ watch(() => configForm.replyMessagesText, () => {
if (!isBrotherInfoMode.value) return
invalidatePreparedState()
})
+watch(normalizedGroupViewCounts, () => {
+ const nextGroupNumber = effectiveInitialFullGroupNumber.value
+ if (configForm.initialFullGroupNumber !== nextGroupNumber) configForm.initialFullGroupNumber = nextGroupNumber
+})
watch(viewIds, (nextViewIds) => {
if (nextViewIds.length === 0) {
activeViewId.value = TIKTOK_VIEW_IDS[0]