Initial commit
This commit is contained in:
@@ -0,0 +1,331 @@
|
||||
// MeshRay 前端应用 - 原生 Vue3 + Element Plus
|
||||
|
||||
const { createApp, ref, reactive, onMounted } = Vue;
|
||||
|
||||
// 注册所有 Element Plus 图标
|
||||
const icons = {};
|
||||
for (const [key, value] of Object.entries(ElementPlusIconsVue)) {
|
||||
icons[key] = value;
|
||||
}
|
||||
|
||||
const app = createApp({
|
||||
setup() {
|
||||
// 当前页面
|
||||
const currentPage = ref('dashboard');
|
||||
|
||||
// 登录状态
|
||||
const isLoggedIn = ref(false);
|
||||
const loggingIn = ref(false);
|
||||
|
||||
// 登录表单
|
||||
const loginForm = reactive({
|
||||
username: '',
|
||||
password: ''
|
||||
});
|
||||
|
||||
// Token 存储
|
||||
let authToken = localStorage.getItem('meshray_token') || '';
|
||||
|
||||
// 检查是否已登录
|
||||
if (authToken) {
|
||||
isLoggedIn.value = true;
|
||||
// 可以在这里验证 token 是否有效
|
||||
}
|
||||
|
||||
// 统计数据
|
||||
const stats = reactive({
|
||||
networkCount: 0,
|
||||
deviceCount: 0,
|
||||
onlineDevices: 0,
|
||||
pendingApprovals: 0
|
||||
});
|
||||
|
||||
// 组网列表
|
||||
const networks = ref([]);
|
||||
|
||||
// 对话框控制
|
||||
const showCreateNetworkDialog = ref(false);
|
||||
|
||||
// 新组网表单
|
||||
const newNetwork = reactive({
|
||||
name: '',
|
||||
subnet_ipv4: '10.0.0.0/24',
|
||||
listen_port: 51820,
|
||||
mesh_mode: 'enhanced'
|
||||
});
|
||||
|
||||
// API 基础 URL
|
||||
const API_BASE = '/api/v1';
|
||||
|
||||
// 加载统计数据
|
||||
const loadStats = async () => {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/dashboard/stats`);
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
if (result.data) {
|
||||
stats.networkCount = result.data.network_count || 0;
|
||||
stats.deviceCount = result.data.device_count || 0;
|
||||
stats.onlineDevices = result.data.online_devices || 0;
|
||||
stats.pendingApprovals = result.data.pending_approvals || 0;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载统计数据失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 加载组网列表
|
||||
const loadNetworks = async () => {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/networks`);
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
networks.value = result.data || [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载组网列表失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 创建组网
|
||||
const createNetwork = async () => {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/networks`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(newNetwork)
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
ElementPlus.ElMessage.success('组网创建成功');
|
||||
showCreateNetworkDialog.value = false;
|
||||
|
||||
// 重置表单
|
||||
newNetwork.name = '';
|
||||
newNetwork.subnet_ipv4 = '10.0.0.0/24';
|
||||
newNetwork.listen_port = 51820;
|
||||
|
||||
// 刷新列表
|
||||
await loadNetworks();
|
||||
await loadStats();
|
||||
} else {
|
||||
const result = await response.json();
|
||||
ElementPlus.ElMessage.error(result.error || '创建失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('创建组网失败:', error);
|
||||
ElementPlus.ElMessage.error('创建失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 查看组网详情
|
||||
const viewNetwork = (network) => {
|
||||
ElementPlus.ElMessageBox.alert(
|
||||
`组网名称:${network.name}\n` +
|
||||
`子网:${network.subnet_ipv4}\n` +
|
||||
`模式:${network.mesh_mode === 'enhanced' ? '增强' : '原生'}`,
|
||||
'组网详情',
|
||||
{
|
||||
confirmButtonText: '确定'
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
// 生成 MeshSeed
|
||||
const generateMeshSeed = (network) => {
|
||||
ElementPlus.ElMessageBox.prompt(
|
||||
'请输入 MeshSeed 有效期(小时)',
|
||||
'生成 MeshSeed',
|
||||
{
|
||||
confirmButtonText: '生成',
|
||||
cancelButtonText: '取消',
|
||||
inputPattern: /^\d+$/,
|
||||
inputErrorMessage: '请输入有效的数字'
|
||||
}
|
||||
).then(async ({ value }) => {
|
||||
try {
|
||||
const hours = parseInt(value);
|
||||
const expiresAt = new Date(Date.now() + hours * 3600 * 1000).toISOString();
|
||||
|
||||
const response = await fetch(`${API_BASE}/networks/${network.id}/mesh-seed`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
max_uses: 10,
|
||||
expires_at: expiresAt,
|
||||
ddns_enabled: false
|
||||
})
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
const seedUrl = result.data?.seed_url || result.data?.seed;
|
||||
|
||||
if (seedUrl) {
|
||||
ElementPlus.ElMessageBox.alert(
|
||||
`MeshSeed 生成成功:<br><br>` +
|
||||
`<input type="text" value="${seedUrl}" readonly ` +
|
||||
`style="width: 100%; padding: 8px; margin-top: 10px; border: 1px solid #ddd;" ` +
|
||||
`onclick="this.select()">`,
|
||||
'MeshSeed',
|
||||
{
|
||||
dangerouslyUseHTMLString: true,
|
||||
confirmButtonText: '复制并关闭'
|
||||
}
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const result = await response.json();
|
||||
ElementPlus.ElMessage.error(result.error || '生成失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('生成 MeshSeed 失败:', error);
|
||||
ElementPlus.ElMessage.error('生成失败');
|
||||
}
|
||||
}).catch(() => {});
|
||||
};
|
||||
|
||||
// 删除组网
|
||||
const deleteNetwork = async (network) => {
|
||||
try {
|
||||
await ElementPlus.ElMessageBox.confirm(
|
||||
`确定要删除组网 "${network.name}" 吗?此操作不可恢复。`,
|
||||
'警告',
|
||||
{
|
||||
confirmButtonText: '删除',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
);
|
||||
|
||||
const response = await fetch(`${API_BASE}/networks/${network.id}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
ElementPlus.ElMessage.success('删除成功');
|
||||
await loadNetworks();
|
||||
await loadStats();
|
||||
} else {
|
||||
const result = await response.json();
|
||||
ElementPlus.ElMessage.error(result.error || '删除失败');
|
||||
}
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('删除组网失败:', error);
|
||||
ElementPlus.ElMessage.error('删除失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 组件挂载时加载数据
|
||||
onMounted(() => {
|
||||
if (isLoggedIn.value) {
|
||||
loadStats();
|
||||
loadNetworks();
|
||||
}
|
||||
});
|
||||
|
||||
// 处理登录
|
||||
const handleLogin = async () => {
|
||||
if (!loginForm.username || !loginForm.password) {
|
||||
ElementPlus.ElMessage.warning('请输入用户名和密码');
|
||||
return;
|
||||
}
|
||||
|
||||
loggingIn.value = true;
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/auth/login`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username: loginForm.username,
|
||||
password: loginForm.password
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result.data) {
|
||||
// 保存 token
|
||||
authToken = result.data.token || result.data.access_token;
|
||||
localStorage.setItem('meshray_token', authToken);
|
||||
isLoggedIn.value = true;
|
||||
|
||||
ElementPlus.ElMessage.success('登录成功');
|
||||
|
||||
// 加载数据
|
||||
await loadStats();
|
||||
await loadNetworks();
|
||||
} else {
|
||||
ElementPlus.ElMessage.error(result.error || '登录失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('登录错误:', error);
|
||||
ElementPlus.ElMessage.error('登录失败,请检查网络连接');
|
||||
} finally {
|
||||
loggingIn.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 处理退出
|
||||
const handleLogout = () => {
|
||||
ElementPlus.ElMessageBox.confirm(
|
||||
'确定要退出登录吗?',
|
||||
'提示',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
).then(() => {
|
||||
// 清除 token
|
||||
localStorage.removeItem('meshray_token');
|
||||
authToken = '';
|
||||
isLoggedIn.value = false;
|
||||
|
||||
// 重置表单
|
||||
loginForm.username = '';
|
||||
loginForm.password = '';
|
||||
|
||||
ElementPlus.ElMessage.success('已退出登录');
|
||||
}).catch(() => {});
|
||||
};
|
||||
|
||||
return {
|
||||
currentPage,
|
||||
isLoggedIn,
|
||||
loggingIn,
|
||||
loginForm,
|
||||
stats,
|
||||
networks,
|
||||
showCreateNetworkDialog,
|
||||
newNetwork,
|
||||
handleLogin,
|
||||
handleLogout,
|
||||
createNetwork,
|
||||
viewNetwork,
|
||||
generateMeshSeed,
|
||||
deleteNetwork
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// 使用 Element Plus
|
||||
app.use(ElementPlus);
|
||||
|
||||
// 注册所有图标
|
||||
for (const [key, component] of Object.entries(icons)) {
|
||||
app.component(key, component);
|
||||
}
|
||||
|
||||
// 挂载应用
|
||||
app.mount('#app');
|
||||
Reference in New Issue
Block a user