// app.js import { createApp, ref, reactive, computed, onMounted } from 'vue'; // 导入各模块 import { apiFetch } from './modules/api.js'; import { login, loadAuthState, clearAuthState, saveAuthState } from './modules/auth.js'; import { loadConfig, saveConfig as saveConfigApi } from './modules/config.js'; import { loadProxies, createProxy, updateProxy, deleteProxy, filterProxies } from './modules/proxies.js'; import { getFrpcStatus, reloadFrpc, startFrpc, stopFrpc } from './modules/frpc.js'; import { activeTab, switchTab, dialogVisible, dialogMode, dialogForm, openAddDialog, openEditDialog, closeDialog, searchKeyword, } from './modules/ui.js'; import { importToml, exportToml } from './modules/import.js'; // 如果有的话 const app = createApp({ setup() { // ===== 登录态 ===== const loggedIn = ref(false); const loading = ref(false); const loginError = ref(''); const loginForm = reactive({ username: '', password: '' }); const token = ref(''); const tokenExpireTime = ref(0); const transitioning = ref(false); const contentVisible = ref(false); let expireTimer = null; // ===== 数据 ===== const globalConfig = reactive({}); const proxies = ref([]); const frpcRunning = ref(false); // ===== 初始化 ===== onMounted(() => { const saved = loadAuthState(); if (saved.valid) { token.value = saved.token; tokenExpireTime.value = saved.expire; loginForm.username = saved.username; loggedIn.value = true; loadAllData().then(() => { contentVisible.value = true; startExpireTimer(); }); } }); const startExpireTimer = () => { if (expireTimer) clearInterval(expireTimer); expireTimer = setInterval(() => { if (Date.now() >= tokenExpireTime.value) { doLogout(); alert('登录已超时,请重新登录'); } }, 5000); }; const loadAllData = async () => { const [cfg, list, running] = await Promise.all([ loadConfig(), loadProxies(), getFrpcStatus(), ]); Object.assign(globalConfig, cfg); proxies.value = list; frpcRunning.value = running; }; // ===== 登录/退出 ===== const doLogin = async () => { if (!loginForm.username || !loginForm.password) { loginError.value = '请输入用户名和密码'; return; } loading.value = true; loginError.value = ''; const result = await login(loginForm.username, loginForm.password); if (result.success) { token.value = result.token; tokenExpireTime.value = result.expire; transitioning.value = true; loggedIn.value = true; setTimeout(async () => { transitioning.value = false; await loadAllData(); loading.value = false; setTimeout(() => { contentVisible.value = true; startExpireTimer(); }, 300); }, 500); } else { loginError.value = result.error; loading.value = false; } }; const doLogout = () => { if (expireTimer) clearInterval(expireTimer); contentVisible.value = false; loggedIn.value = false; token.value = ''; tokenExpireTime.value = 0; loginForm.username = ''; loginForm.password = ''; loading.value = false; transitioning.value = false; clearAuthState(); }; // ===== 保存配置 ===== const saveConfig = async () => { const result = await saveConfigApi(globalConfig); if (result.code === 0) { frpcRunning.value = await getFrpcStatus(); alert('配置已更新并热加载'); } else { alert('保存失败: ' + result.msg); } }; // ===== 隧道操作 ===== const toggleProxy = async (p) => { const result = await updateProxy(p); if (result.code !== 0) { alert('更新失败: ' + result.msg); proxies.value = await loadProxies(); } else { frpcRunning.value = await getFrpcStatus(); } }; const removeProxy = async (id) => { if (!confirm('确定要删除这条隧道吗?')) return; const result = await deleteProxy(id); if (result.code === 0) { proxies.value = await loadProxies(); frpcRunning.value = await getFrpcStatus(); } else { alert('删除失败: ' + result.msg); } }; const confirmDialog = async () => { const form = dialogForm.value; if (!form.name || !form.localIP || !form.localPort || !form.remotePort) { alert('请填写完整信息'); return; } const result = dialogMode.value === 'add' ? await createProxy(form) : await updateProxy(form); if (result.code === 0) { closeDialog(); proxies.value = await loadProxies(); frpcRunning.value = await getFrpcStatus(); } else { alert('操作失败: ' + result.msg); } }; // ===== 计算属性 ===== const filteredProxies = computed(() => filterProxies(proxies.value, searchKeyword.value)); return { loggedIn, loading, loginError, loginForm, doLogin, doLogout, activeTab, switchTab, globalConfig, saveConfig, proxies, filteredProxies, toggleProxy, removeProxy, frpcRunning, dialogVisible, dialogMode, dialogForm, openAddDialog, openEditDialog, closeDialog, confirmDialog, searchKeyword, transitioning, contentVisible, }; }, }); app.mount('#app');