refactor(vnt-web): 前端重构为 Vite+Vue3+Pinia 工程并重新设计 UI

- 单文件 index.html 拆分为 vnt-web/ui(pnpm+Vite+Vue3 SFC+Pinia+Tailwind v4),构建产物输出到 static/
- UI 重新设计:浅色简洁风(可切换深色模式)、顶部导航、新增总览仪表盘首页
- 自定义弹窗/toast/确认框替换全部原生 alert/confirm,交互与过渡统一
- 响应式布局:移动端汉堡导航,表格与卡片自适应
- 实例卡片移除日志按钮;启动组网默认选中第一个配置
This commit is contained in:
lbl
2026-08-22 00:53:51 +08:00
parent 292d54fbc3
commit 7513465b61
37 changed files with 5090 additions and 3227 deletions
+73
View File
@@ -0,0 +1,73 @@
// fetch 封装:统一解包 ApiResponse{code,msg,data},code!==0 抛出带 msg 的错误
const request = async (url, options) => {
const res = await fetch(url, options);
const json = await res.json();
if (json.code !== 0) {
throw new Error(json.msg || "请求失败");
}
return json.data;
};
const jsonHeaders = { "Content-Type": "application/json" };
// GET /api/info?file_name=
export const getInstanceInfo = (fileName) =>
request(`/api/info?file_name=${encodeURIComponent(fileName)}`);
// GET /api/peers?file_name=
export const getPeers = (fileName) =>
request(`/api/peers?file_name=${encodeURIComponent(fileName)}`);
// GET /api/routes?file_name=
export const getRoutes = (fileName) =>
request(`/api/routes?file_name=${encodeURIComponent(fileName)}`);
// GET /api/start/status?file_name=
export const getStartStatus = (fileName) =>
request(`/api/start/status?file_name=${encodeURIComponent(fileName)}`);
// GET /api/instances
export const getInstances = () => request("/api/instances");
// DELETE /api/instance?file_name=
export const deleteInstance = (fileName) =>
request(`/api/instance?file_name=${encodeURIComponent(fileName)}`, {
method: "DELETE",
});
const postAction = (path, fileName) =>
request(path, {
method: "POST",
headers: jsonHeaders,
body: JSON.stringify({ file_name: fileName }),
});
// POST /api/start
export const startVntApi = (fileName) => postAction("/api/start", fileName);
// POST /api/stop
export const stopVntApi = (fileName) => postAction("/api/stop", fileName);
// POST /api/restart
export const restartVntApi = (fileName) => postAction("/api/restart", fileName);
// GET /api/config/list
export const getConfigList = () => request("/api/config/list");
// GET /api/config?file_name= -> TOML 原文字符串
export const getConfig = (fileName) =>
request(`/api/config?file_name=${encodeURIComponent(fileName)}`);
// POST /api/config, JSON {file_name?, config}
export const saveConfig = (fileName, config) =>
request("/api/config", {
method: "POST",
headers: jsonHeaders,
body: JSON.stringify({ file_name: fileName || null, config }),
});
// DELETE /api/config?file_name=
export const deleteConfig = (fileName) =>
request(`/api/config?file_name=${encodeURIComponent(fileName)}`, {
method: "DELETE",
});