2026-02-04 19:56:19 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<!-- 强制更新检查 -->
|
|
|
|
|
|
<!-- <UpdateChecker v-if="false" @ready="updateReady = true" /> -->
|
|
|
|
|
|
<UpdateChecker v-if="!isDev && isElectronEnv && !updateReady" @ready="updateReady = true" />
|
|
|
|
|
|
|
|
|
|
|
|
<template v-else>
|
2026-02-11 14:49:18 +08:00
|
|
|
|
<!-- 滚动通知栏(登录页和工作台都显示) -->
|
|
|
|
|
|
<NoticeBar />
|
|
|
|
|
|
|
2026-02-04 19:56:19 +08:00
|
|
|
|
<!-- 登录页面 -->
|
|
|
|
|
|
<LoginPage v-if="currentPage === 'login'" @login-success="currentPage = 'browser'" class="animate-fadeIn" />
|
|
|
|
|
|
|
|
|
|
|
|
<template v-else>
|
|
|
|
|
|
<!-- 配置页面 - 使用 v-show 保持状态 -->
|
|
|
|
|
|
<div class="h-full w-full animate-fadeIn" v-show="currentPage === 'config'">
|
|
|
|
|
|
<ConfigPage @go-to-browser="handleGoToBrowser" @logout="handleLogout" />
|
2026-03-13 17:48:00 +08:00
|
|
|
|
<!-- 更新通知组件:启动时已在 UpdateChecker 检查,此处暂不显示 -->
|
|
|
|
|
|
<!-- <UpdateNotification /> -->
|
2026-02-04 19:56:19 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 浏览器页面 -->
|
|
|
|
|
|
<div class="h-full w-full animate-fadeIn" v-show="currentPage === 'browser'">
|
|
|
|
|
|
<WorkbenchLayout
|
|
|
|
|
|
:account-groups="accountGroups"
|
|
|
|
|
|
:rotation-status="rotationStatus"
|
|
|
|
|
|
:greeting-stats="greetingStats"
|
|
|
|
|
|
:automation-logs="automationLogs"
|
|
|
|
|
|
@go-back="handleGoToConfig"
|
|
|
|
|
|
@stop-all="handleStopAll"
|
|
|
|
|
|
@logout="handleLogout"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
|
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
|
|
|
|
|
|
import { isElectron, getAppVersion } from './utils/electronBridge'
|
|
|
|
|
|
import LoginPage from './pages/LoginPage.vue'
|
|
|
|
|
|
import ConfigPage from './pages/ConfigPage.vue'
|
|
|
|
|
|
import UpdateChecker from './pages/UpdateChecker.vue'
|
|
|
|
|
|
import WorkbenchLayout from './layout/WorkbenchLayout.vue'
|
|
|
|
|
|
import UpdateNotification from './components/UpdateNotification.vue'
|
2026-02-11 14:49:18 +08:00
|
|
|
|
import NoticeBar from './components/NoticeBar.vue'
|
|
|
|
|
|
import { useNoticeStore } from './stores/noticeStore'
|
2026-02-04 19:56:19 +08:00
|
|
|
|
|
|
|
|
|
|
// Constants
|
|
|
|
|
|
const USER_KEY = 'user_data'
|
|
|
|
|
|
const CONFIG_KEY = 'autoDm_runConfig'
|
|
|
|
|
|
|
|
|
|
|
|
// State
|
|
|
|
|
|
const updateReady = ref(false)
|
|
|
|
|
|
const currentPage = ref('login')
|
|
|
|
|
|
const isLoading = ref(false)
|
|
|
|
|
|
const automationStatus = ref({})
|
|
|
|
|
|
const accountGroups = ref([])
|
|
|
|
|
|
const viewAccountMap = ref({})
|
|
|
|
|
|
const rotationStatus = ref(null)
|
|
|
|
|
|
const greetingStats = ref({ greetingCount: 0, inviteCount: 0 })
|
|
|
|
|
|
const automationLogs = ref([])
|
|
|
|
|
|
|
|
|
|
|
|
const isElectronEnv = isElectron()
|
|
|
|
|
|
const isDev = window.location.port === '5173'
|
|
|
|
|
|
|
2026-02-11 14:49:18 +08:00
|
|
|
|
// 公告通知
|
|
|
|
|
|
const noticeStore = useNoticeStore()
|
|
|
|
|
|
noticeStore.fetchNotices()
|
|
|
|
|
|
|
2026-02-04 19:56:19 +08:00
|
|
|
|
// Lifecycle
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
// Set Title
|
|
|
|
|
|
getAppVersion().then(version => {
|
|
|
|
|
|
document.title = `Yolo(AI助手Web版)v${version}`
|
|
|
|
|
|
}).catch(() => {
|
|
|
|
|
|
document.title = 'Yolo(AI助手Web版)'
|
|
|
|
|
|
})
|
|
|
|
|
|
console.log('[App]',!isDev , isElectronEnv , !updateReady.value)
|
|
|
|
|
|
// Check Login
|
|
|
|
|
|
try {
|
|
|
|
|
|
const userData = localStorage.getItem(USER_KEY)
|
|
|
|
|
|
if (userData) {
|
|
|
|
|
|
const user = JSON.parse(userData)
|
|
|
|
|
|
if (user && user.tokenValue) {
|
|
|
|
|
|
currentPage.value = 'browser'
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch { } // eslint-disable-line no-empty
|
|
|
|
|
|
|
|
|
|
|
|
// Listeners
|
|
|
|
|
|
if (isElectronEnv) {
|
|
|
|
|
|
window.electronAPI.onRequestClearLogin(() => {
|
|
|
|
|
|
console.log('[App] 收到清除登录状态请求')
|
|
|
|
|
|
localStorage.removeItem(USER_KEY)
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// Rotation Status
|
|
|
|
|
|
window.electronAPI.getRotationStatus().then(status => {
|
|
|
|
|
|
rotationStatus.value = status
|
|
|
|
|
|
}).catch(console.error)
|
|
|
|
|
|
|
|
|
|
|
|
window.electronAPI.onRotationStatusChanged(status => {
|
|
|
|
|
|
rotationStatus.value = status
|
|
|
|
|
|
console.log('[App] 收到轮换状态变化123:', status)
|
|
|
|
|
|
// Auto switch tab if group changes
|
|
|
|
|
|
if (status && status.currentActiveGroup && status.enabled) {
|
|
|
|
|
|
console.log('[App] 收到轮换状态变化456:', status)
|
|
|
|
|
|
const targetTab = tabs.value.find(t => t.label === status.currentActiveGroup || t.id === status.currentActiveGroup)
|
|
|
|
|
|
if (targetTab && targetTab.id !== currentTab.value) {
|
|
|
|
|
|
console.log('[App] 自动切换到轮换组789:', targetTab.id)
|
|
|
|
|
|
handleTabSwitch(targetTab.id)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// Stats
|
|
|
|
|
|
window.electronAPI.getGreetingStats().then(stats => {
|
|
|
|
|
|
greetingStats.value = stats
|
|
|
|
|
|
}).catch(console.error)
|
|
|
|
|
|
|
|
|
|
|
|
window.electronAPI.onGreetingStatsChanged(stats => {
|
|
|
|
|
|
greetingStats.value = stats
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// Logs
|
|
|
|
|
|
window.electronAPI.onAutomationLog(log => {
|
|
|
|
|
|
automationLogs.value = [...automationLogs.value.slice(-99), { ...log, timestamp: Date.now() }]
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
window.addEventListener('beforeunload', handleBeforeUnload)
|
|
|
|
|
|
window.addEventListener('storage', handleStorageChange)
|
|
|
|
|
|
|
|
|
|
|
|
loadConfig()
|
|
|
|
|
|
|
|
|
|
|
|
// Health Check
|
|
|
|
|
|
startHealthCheck()
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
|
|
|
window.removeEventListener('beforeunload', handleBeforeUnload)
|
|
|
|
|
|
window.removeEventListener('storage', handleStorageChange)
|
|
|
|
|
|
stopHealthCheck()
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// Handlers
|
|
|
|
|
|
const handleBeforeUnload = () => {
|
|
|
|
|
|
localStorage.removeItem(USER_KEY)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleStorageChange = (e) => {
|
|
|
|
|
|
if (e.key === CONFIG_KEY) {
|
|
|
|
|
|
loadConfig()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let healthCheckInterval = null
|
|
|
|
|
|
|
|
|
|
|
|
const startHealthCheck = () => {
|
|
|
|
|
|
const check = async () => {
|
|
|
|
|
|
if (currentPage.value === 'login' || !isElectron()) return
|
|
|
|
|
|
try {
|
|
|
|
|
|
const result = await window.electronAPI.checkHealth()
|
|
|
|
|
|
if (result.success && result.code === 40400) {
|
|
|
|
|
|
alert('当前账号已在其他地方登录,请重新登录')
|
|
|
|
|
|
localStorage.removeItem(USER_KEY)
|
2026-03-03 14:36:25 +08:00
|
|
|
|
// 隐藏所有 BrowserView 并停止自动化,防止视图悬浮在登录页上方
|
|
|
|
|
|
try {
|
|
|
|
|
|
await window.electronAPI.hideViews()
|
|
|
|
|
|
await handleStopAll()
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.warn('[App] 清理视图失败:', e)
|
|
|
|
|
|
}
|
2026-02-04 19:56:19 +08:00
|
|
|
|
currentPage.value = 'login'
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('[App] 健康检查失败:', error)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
check()
|
|
|
|
|
|
healthCheckInterval = setInterval(check, 5000)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const stopHealthCheck = () => {
|
|
|
|
|
|
if (healthCheckInterval) clearInterval(healthCheckInterval)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const loadConfig = () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const savedConfig = localStorage.getItem(CONFIG_KEY)
|
|
|
|
|
|
if (savedConfig) {
|
|
|
|
|
|
const config = JSON.parse(savedConfig)
|
|
|
|
|
|
accountGroups.value = config.accountGroups || []
|
|
|
|
|
|
|
|
|
|
|
|
const map = {}
|
|
|
|
|
|
config.accountGroups.forEach((group, groupIndex) => {
|
|
|
|
|
|
const viewsPerGroup = 3
|
|
|
|
|
|
group.accounts.forEach((account, accIndex) => {
|
|
|
|
|
|
const viewId = groupIndex * viewsPerGroup + accIndex + 1
|
|
|
|
|
|
if (viewId <= 9 && account.email && account.pwd) {
|
|
|
|
|
|
map[viewId] = { ...account, group: group.name }
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
viewAccountMap.value = map
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch { } // eslint-disable-line no-empty
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
watch(currentPage, (newVal) => {
|
|
|
|
|
|
if (newVal === 'browser') {
|
|
|
|
|
|
loadConfig()
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// Actions
|
|
|
|
|
|
|
|
|
|
|
|
const handleGoToBrowser = async () => {
|
|
|
|
|
|
if (isElectron()) {
|
|
|
|
|
|
await window.electronAPI.showViews()
|
|
|
|
|
|
}
|
|
|
|
|
|
currentPage.value = 'browser'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleGoToConfig = async () => {
|
|
|
|
|
|
if (isElectron()) {
|
|
|
|
|
|
await window.electronAPI.hideViews()
|
|
|
|
|
|
}
|
|
|
|
|
|
currentPage.value = 'config'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleLogout = async () => {
|
2026-03-05 14:47:02 +08:00
|
|
|
|
stopHealthCheck()
|
2026-02-04 19:56:19 +08:00
|
|
|
|
currentPage.value = 'login'
|
2026-03-03 14:36:25 +08:00
|
|
|
|
localStorage.removeItem(USER_KEY)
|
2026-03-05 14:47:02 +08:00
|
|
|
|
|
|
|
|
|
|
if (isElectron()) {
|
|
|
|
|
|
try { await window.electronAPI.logout() } catch (e) { console.warn('[App] logout失败:', e) }
|
|
|
|
|
|
try {
|
|
|
|
|
|
await window.electronAPI.hideViews()
|
|
|
|
|
|
await handleStopAll()
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.warn('[App] 清理视图失败:', e)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-02-04 19:56:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleStopAll = async () => {
|
|
|
|
|
|
if (!isElectron()) return
|
|
|
|
|
|
|
|
|
|
|
|
console.log('[App] 开始并行停止所有任务...')
|
|
|
|
|
|
|
|
|
|
|
|
await Promise.allSettled(
|
|
|
|
|
|
Array.from({ length: 9 }, (_, i) => i + 1).map(viewId =>
|
|
|
|
|
|
window.electronAPI.stopTikTokAutomation(viewId).catch((e) => {
|
|
|
|
|
|
console.warn(`[App] 停止视图 ${viewId} 失败:`, e)
|
|
|
|
|
|
})
|
|
|
|
|
|
)
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
await window.electronAPI.updateAutomationConfig({
|
|
|
|
|
|
rotationEnabled: false
|
|
|
|
|
|
})
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.warn('[App] 停止轮换服务失败:', e)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
automationStatus.value = {}
|
|
|
|
|
|
rotationStatus.value = null
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
console.log('[App] 正在清空缓存...')
|
|
|
|
|
|
if (window.electronAPI.clearAllCache) await window.electronAPI.clearAllCache()
|
|
|
|
|
|
console.log('[App] 缓存清空完成')
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.warn('[App] 清空缓存失败:', e)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
console.log('[App] 已并行停止所有任务并清空缓存')
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|