修改页面渲染

This commit is contained in:
zyj
2025-11-22 17:48:41 +08:00
parent ec3fc77403
commit 1f5eccb7f7
7 changed files with 641 additions and 61 deletions

View File

@@ -129,8 +129,8 @@
v-else
:columns="columns"
:data-source="hostingSites"
:pagination="{ pageSize: 10, showSizeChanger: true, showTotal: (total) => `共 ${total} 个站点` }"
:scroll="{ x: 800 }"
:pagination="{ pageSize: 10, showSizeChanger: true, showTotal: (total: number) => `共 ${total} 个站点` }"
:scroll="{ x: 1050 }"
class="hosting-table"
>
<template #bodyCell="{ column, record }">
@@ -251,8 +251,12 @@
</a-col>
</a-row>
<a-form-item label="网站路径" name="path">
<a-input v-model:value="formData.path" disabled />
<a-form-item label="网站路径" name="path" :rules="[{ required: true, message: '请输入网站路径' }]">
<a-input v-model:value="formData.path" placeholder="相对路径,如: www/example.com" />
<div class="form-tip">
<info-circle-outlined />
<span>相对于项目根目录的路径</span>
</div>
</a-form-item>
<a-form-item label="启用状态">
@@ -309,7 +313,7 @@ const formRef = ref();
const formData = ref({
name: '',
domains: ['127.0.0.1'],
port: 80,
port: 8080,
path: '',
index: 'index.html',
enabled: true
@@ -320,29 +324,31 @@ const columns = [
{
title: '网站',
key: 'site',
width: '25%',
width: 280,
ellipsis: true,
},
{
title: '域名',
key: 'domain',
width: '25%',
width: 250,
ellipsis: true,
},
{
title: '端口',
key: 'port',
width: '10%',
width: 100,
align: 'center',
},
{
title: '状态',
key: 'status',
width: '10%',
width: 100,
align: 'center',
},
{
title: '操作',
key: 'action',
width: '30%',
width: 320,
align: 'center',
fixed: 'right',
},
@@ -357,10 +363,22 @@ onMounted(() => {
const loadData = async () => {
await Promise.all([
getAvailableSites(),
getHostingSites()
getHostingSites(),
checkNginxStatus()
]);
};
const checkNginxStatus = async () => {
try {
const status = await App.CheckNginxStatus();
nginxRunning.value = status;
console.log("Nginx running status:", status);
} catch (error) {
console.error('获取 Nginx 状态失败:', error);
}
};
// 获取可用网站列表(已下载的)
const getAvailableSites = async () => {
try {
@@ -375,19 +393,8 @@ const getAvailableSites = async () => {
const getHostingSites = async () => {
loading.value = true;
try {
// TODO: 调用后端接口获取托管列表
// 暂时使用模拟数据
hostingSites.value = [
{
id: '1',
name: 'example.com',
path: '/www/example.com',
domains: ['example.com', '127.0.0.1'],
port: 80,
index: 'index.html',
enabled: true,
},
];
const sites = await App.GetAllNginxSites();
hostingSites.value = sites || [];
} catch (error) {
console.error('获取托管列表失败:', error);
message.error('获取托管列表失败');
@@ -408,13 +415,13 @@ const toggleNginx = async () => {
try {
if (nginxRunning.value) {
// TODO: 调用后端接口停止 Nginx
await new Promise(resolve => setTimeout(resolve, 1000));
await App.StopNginx();
nginxRunning.value = false;
uptime.value = '--';
message.success('Nginx 已停止');
} else {
// TODO: 调用后端接口启动 Nginx
await new Promise(resolve => setTimeout(resolve, 1000));
await App.StartNginx();
nginxRunning.value = true;
uptime.value = '00:00:00';
message.success('Nginx 已启动');
@@ -432,7 +439,7 @@ const showAddModal = () => {
formData.value = {
name: '',
domains: ['127.0.0.1'],
port: 80,
port: 8080,
path: '',
index: 'index.html',
enabled: true
@@ -451,8 +458,9 @@ const editSite = (record: any) => {
const onSiteSelect = (value: string) => {
const site = availableSites.value.find(s => s.name === value);
if (site) {
formData.value.path = `/www/${value}`;
formData.value.domains = [value];
// 设置网站路径(从工作目录)
formData.value.path = `www/${value}`;
formData.value.domains = [value, '127.0.0.1'];
}
};
@@ -462,24 +470,44 @@ const handleModalOk = async () => {
await formRef.value?.validate();
if (editingRecord.value) {
// 更新
const index = hostingSites.value.findIndex(s => s.id === editingRecord.value.id);
if (index !== -1) {
hostingSites.value[index] = { ...formData.value, id: editingRecord.value.id };
}
// 更新站点配置
await App.UpdateNginxSite({
ID: "",
Name: formData.value.name,
Domains: formData.value.domains,
Port: formData.value.port,
Path: formData.value.path,
Index: formData.value.index,
Enabled: formData.value.enabled
});
message.success('更新成功');
} else {
// 添加
hostingSites.value.push({
...formData.value,
id: Date.now().toString()
// 添加站点配置
await App.AddNginxSite({
ID: "",
Name: formData.value.name,
Domains: formData.value.domains,
Port: formData.value.port,
Path: formData.value.path,
Index: formData.value.index,
Enabled: formData.value.enabled
});
message.success('添加成功');
message.success('站点配置创建成功');
}
// 重新加载站点列表
await getHostingSites();
// 如果 nginx 正在运行,重载配置
if (nginxRunning.value) {
await App.ReloadNginx();
message.info('Nginx 配置已重载');
}
modalVisible.value = false;
} catch (error) {
console.error('验证失败:', error);
} catch (error: any) {
console.error('操作失败:', error);
message.error(error.message || '操作失败');
}
};
@@ -489,16 +517,44 @@ const handleModalCancel = () => {
};
// 切换站点状态
const toggleSiteStatus = (record: any) => {
message.success(record.enabled ? '已启用' : '已禁用');
const toggleSiteStatus = async (record: any) => {
try {
if (record.enabled) {
await App.EnableNginxSite(record.name);
message.success('站点已启用');
} else {
await App.DisableNginxSite(record.name);
message.success('站点已禁用');
}
// 如果 nginx 正在运行,重载配置
if (nginxRunning.value) {
await App.ReloadNginx();
}
} catch (error: any) {
console.error('切换状态失败:', error);
message.error(error.message || '操作失败');
// 回滚状态
record.enabled = !record.enabled;
}
};
// 删除站点
const deleteSite = (record: any) => {
const index = hostingSites.value.findIndex(s => s.id === record.id);
if (index !== -1) {
hostingSites.value.splice(index, 1);
message.success('删除成功');
const deleteSite = async (record: any) => {
try {
await App.DeleteNginxSite(record.name);
message.success('站点配置已删除');
// 重新加载站点列表
await getHostingSites();
// 如果 nginx 正在运行,重载配置
if (nginxRunning.value) {
await App.ReloadNginx();
}
} catch (error: any) {
console.error('删除失败:', error);
message.error(error.message || '删除失败');
}
};
@@ -727,6 +783,7 @@ const getAvatarColor = (name: string) => {
font-weight: 600;
color: #333;
border-bottom: 2px solid #1890ff;
padding: 16px;
}
:deep(.hosting-table .ant-table-tbody > tr) {
@@ -737,15 +794,22 @@ const getAvatarColor = (name: string) => {
background: #e6f7ff !important;
}
:deep(.hosting-table .ant-table-tbody > tr > td) {
padding: 16px;
border-bottom: 1px solid #f0f0f0;
}
.site-cell {
display: flex;
align-items: center;
gap: 12px;
max-width: 100%;
}
.site-details {
flex: 1;
min-width: 0;
overflow: hidden;
}
.site-name {
@@ -753,6 +817,10 @@ const getAvatarColor = (name: string) => {
font-weight: 600;
color: #333;
margin-bottom: 4px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
}
.site-path {
@@ -761,12 +829,22 @@ const getAvatarColor = (name: string) => {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
}
.domain-cell {
display: flex;
flex-wrap: wrap;
gap: 4px;
max-width: 100%;
overflow: hidden;
}
.domain-cell :deep(.ant-tag) {
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 加载和空状态 */

View File

@@ -61,7 +61,7 @@
:columns="columns"
:loading="loading"
:pagination="paginationConfig"
:scroll="{ x: 800 }"
:scroll="{ x: 950 }"
class="custom-table"
>
<template #bodyCell="{ column, record, index }">
@@ -167,33 +167,33 @@ const columns = [
title: '站点名称',
dataIndex: 'name',
key: 'name',
width: '35%',
width: 300,
ellipsis: true,
},
{
title: '文件大小',
dataIndex: 'size',
key: 'size',
width: '15%',
width: 130,
sorter: (a, b) => a.size - b.size,
},
{
title: '权限',
dataIndex: 'mode',
key: 'mode',
width: '15%',
width: 120,
},
{
title: '最后修改时间',
key: 'modTime',
dataIndex: 'modTime',
width: '20%',
width: 180,
sorter: (a, b) => new Date(a.modTime) - new Date(b.modTime),
},
{
title: '操作',
key: 'action',
width: '15%',
width: 220,
fixed: 'right',
},
]
@@ -455,6 +455,11 @@ onMounted(() => {
animation: fadeInUp 0.6s ease-out 0.2s both;
}
:deep(.table-card .ant-card-body) {
padding: 0;
overflow: hidden;
}
:deep(.table-card .ant-card-head) {
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
border-bottom: 2px solid #f0f0f0;
@@ -505,6 +510,9 @@ onMounted(() => {
:deep(.custom-table .ant-table-tbody > tr > td) {
padding: 16px;
border-bottom: 1px solid #f0f0f0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 名称单元格 */
@@ -512,11 +520,13 @@ onMounted(() => {
display: flex;
align-items: center;
gap: 12px;
max-width: 100%;
}
.name-info {
flex: 1;
min-width: 0;
overflow: hidden;
}
.site-name {
@@ -527,6 +537,7 @@ onMounted(() => {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
}
.site-path {
@@ -535,6 +546,7 @@ onMounted(() => {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
}
/* 大小标签 */

View File

@@ -11,9 +11,86 @@ import { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Cr
import * as services$0 from "./services/models.js";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as storage$0 from "./storage/models.js";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as types$0 from "./types/models.js";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as utils$0 from "./utils/models.js";
/**
* AddDownloadRecord 添加下载记录
* @param {storage$0.DownloadRecord} record
* @returns {$CancellablePromise<void>}
*/
export function AddDownloadRecord(record) {
return $Call.ByID(357560545, record);
}
/**
* 添加站点配置
* @param {types$0.NginxSiteConfig} site
* @returns {$CancellablePromise<void>}
*/
export function AddNginxSite(site) {
return $Call.ByID(950690035, site);
}
/**
* BackupDatabase 备份数据库
* @param {string} backupPath
* @returns {$CancellablePromise<void>}
*/
export function BackupDatabase(backupPath) {
return $Call.ByID(2830093186, backupPath);
}
/**
* 检查 Nginx 状态
* @returns {$CancellablePromise<boolean>}
*/
export function CheckNginxStatus() {
return $Call.ByID(2402834819);
}
/**
* 清空 Nginx 日志
* @returns {$CancellablePromise<void>}
*/
export function ClearNginxLogs() {
return $Call.ByID(3579671429);
}
/**
* ClearOldDownloadRecords 清理旧的下载记录
* @param {number} days
* @returns {$CancellablePromise<number>}
*/
export function ClearOldDownloadRecords(days) {
return $Call.ByID(84469747, days);
}
/**
* DeleteDownloadRecord 删除下载记录
* @param {string} id
* @returns {$CancellablePromise<void>}
*/
export function DeleteDownloadRecord(id) {
return $Call.ByID(1013800695, id);
}
/**
* 删除站点配置
* @param {string} siteName
* @returns {$CancellablePromise<void>}
*/
export function DeleteNginxSite(siteName) {
return $Call.ByID(86965973, siteName);
}
/**
* 删除网站文件夹
* @param {string} pathDir
* @returns {$CancellablePromise<boolean>}
*/
@@ -22,6 +99,16 @@ export function DeleteSiteFileDir(pathDir) {
}
/**
* 禁用站点
* @param {string} siteName
* @returns {$CancellablePromise<void>}
*/
export function DisableNginxSite(siteName) {
return $Call.ByID(3342681398, siteName);
}
/**
* 下载网站资源
* @param {string} uri
* @param {services$0.ResourcesList} obj
* @returns {$CancellablePromise<boolean>}
@@ -31,24 +118,117 @@ export function DownloadSite(uri, obj) {
}
/**
* @returns {$CancellablePromise<utils$0.FileDir[]>}
* 启用站点
* @param {string} siteName
* @returns {$CancellablePromise<void>}
*/
export function GetDownloadList() {
return $Call.ByID(2717901443).then(/** @type {($result: any) => any} */(($result) => {
export function EnableNginxSite(siteName) {
return $Call.ByID(2112827609, siteName);
}
/**
* GetAllDownloadRecords 获取所有下载记录
* @returns {$CancellablePromise<storage$0.DownloadRecord[]>}
*/
export function GetAllDownloadRecords() {
return $Call.ByID(1915535268).then(/** @type {($result: any) => any} */(($result) => {
return $$createType1($result);
}));
}
/**
* 获取所有站点配置
* @returns {$CancellablePromise<types$0.NginxSiteConfig[]>}
*/
export function GetAllNginxSites() {
return $Call.ByID(3317211258).then(/** @type {($result: any) => any} */(($result) => {
return $$createType3($result);
}));
}
/**
* 获取本地已下载网站列表
* @returns {$CancellablePromise<utils$0.FileDir[]>}
*/
export function GetDownloadList() {
return $Call.ByID(2717901443).then(/** @type {($result: any) => any} */(($result) => {
return $$createType5($result);
}));
}
/**
* GetDownloadStats 获取下载统计
* @returns {$CancellablePromise<{ [_: string]: any }>}
*/
export function GetDownloadStats() {
return $Call.ByID(180408518).then(/** @type {($result: any) => any} */(($result) => {
return $$createType6($result);
}));
}
/**
* 获取 Nginx 访问日志
* @param {number} lines
* @returns {$CancellablePromise<string[]>}
*/
export function GetNginxAccessLog(lines) {
return $Call.ByID(3967804239, lines).then(/** @type {($result: any) => any} */(($result) => {
return $$createType7($result);
}));
}
/**
* 获取 Nginx 错误日志
* @param {number} lines
* @returns {$CancellablePromise<string[]>}
*/
export function GetNginxErrorLog(lines) {
return $Call.ByID(3102869025, lines).then(/** @type {($result: any) => any} */(($result) => {
return $$createType7($result);
}));
}
/**
* GetRecentDownloadRecords 获取最近的下载记录
* @param {number} limit
* @returns {$CancellablePromise<storage$0.DownloadRecord[]>}
*/
export function GetRecentDownloadRecords(limit) {
return $Call.ByID(361708134, limit).then(/** @type {($result: any) => any} */(($result) => {
return $$createType1($result);
}));
}
/**
* return nil
* }
*
* ========== 网站克隆相关方法 ==========
* @param {string} rawURL
* @returns {$CancellablePromise<services$0.ResourcesList | null>}
*/
export function GetResources(rawURL) {
return $Call.ByID(2167352808, rawURL).then(/** @type {($result: any) => any} */(($result) => {
return $$createType3($result);
return $$createType9($result);
}));
}
/**
* 服务关闭时不关闭 nginx但关闭数据库
* @returns {$CancellablePromise<void>}
*/
export function OnShutdown() {
return $Call.ByID(999779538);
}
/**
* 服务启动时初始化 nginx 服务和数据库
* @returns {$CancellablePromise<void>}
*/
export function OnStartup() {
return $Call.ByID(2232168041);
}
/**
* 打开网站文件夹
* @param {string} pathDir
@@ -58,8 +238,63 @@ export function OpenSiteFileDir(pathDir) {
return $Call.ByID(4158138211, pathDir);
}
/**
* 重载 Nginx 配置
* @returns {$CancellablePromise<void>}
*/
export function ReloadNginx() {
return $Call.ByID(412894400);
}
/**
* 重启 Nginx
* @returns {$CancellablePromise<void>}
*/
export function RestartNginx() {
return $Call.ByID(2491282768);
}
/**
* 启动 Nginx
* @returns {$CancellablePromise<void>}
*/
export function StartNginx() {
return $Call.ByID(1903679817);
}
/**
* 停止 Nginx
* @returns {$CancellablePromise<void>}
*/
export function StopNginx() {
return $Call.ByID(1067982487);
}
/**
* 测试 Nginx 配置
* @returns {$CancellablePromise<void>}
*/
export function TestNginxConfig() {
return $Call.ByID(222716517);
}
/**
* 更新站点配置
* @param {types$0.NginxSiteConfig} site
* @returns {$CancellablePromise<void>}
*/
export function UpdateNginxSite(site) {
return $Call.ByID(2978221891, site);
}
// Private type creation functions
const $$createType0 = utils$0.FileDir.createFrom;
const $$createType0 = storage$0.DownloadRecord.createFrom;
const $$createType1 = $Create.Array($$createType0);
const $$createType2 = services$0.ResourcesList.createFrom;
const $$createType3 = $Create.Nullable($$createType2);
const $$createType2 = types$0.NginxSiteConfig.createFrom;
const $$createType3 = $Create.Array($$createType2);
const $$createType4 = utils$0.FileDir.createFrom;
const $$createType5 = $Create.Array($$createType4);
const $$createType6 = $Create.Map($Create.Any, $Create.Any);
const $$createType7 = $Create.Array($Create.Any);
const $$createType8 = services$0.ResourcesList.createFrom;
const $$createType9 = $Create.Nullable($$createType8);

View File

@@ -0,0 +1,7 @@
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
export {
DownloadRecord
} from "./models.js";

View File

@@ -0,0 +1,147 @@
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import { Create as $Create } from "@wailsio/runtime";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as time$0 from "../../time/models.js";
/**
* DownloadRecord 下载记录
*/
export class DownloadRecord {
/**
* Creates a new DownloadRecord instance.
* @param {Partial<DownloadRecord>} [$$source = {}] - The source object to create the DownloadRecord.
*/
constructor($$source = {}) {
if (!("id" in $$source)) {
/**
* 唯一标识
* @member
* @type {string}
*/
this["id"] = "";
}
if (!("url" in $$source)) {
/**
* 下载的 URL
* @member
* @type {string}
*/
this["url"] = "";
}
if (!("site_name" in $$source)) {
/**
* 站点名称
* @member
* @type {string}
*/
this["site_name"] = "";
}
if (!("status" in $$source)) {
/**
* 状态success, failed, processing
* @member
* @type {string}
*/
this["status"] = "";
}
if (!("total_files" in $$source)) {
/**
* 总文件数
* @member
* @type {number}
*/
this["total_files"] = 0;
}
if (!("downloaded" in $$source)) {
/**
* 已下载数
* @member
* @type {number}
*/
this["downloaded"] = 0;
}
if (!("css_count" in $$source)) {
/**
* CSS 文件数
* @member
* @type {number}
*/
this["css_count"] = 0;
}
if (!("script_count" in $$source)) {
/**
* JS 文件数
* @member
* @type {number}
*/
this["script_count"] = 0;
}
if (!("image_count" in $$source)) {
/**
* 图片文件数
* @member
* @type {number}
*/
this["image_count"] = 0;
}
if (!("video_count" in $$source)) {
/**
* 视频文件数
* @member
* @type {number}
*/
this["video_count"] = 0;
}
if (!("error_msg" in $$source)) {
/**
* 错误信息
* @member
* @type {string}
*/
this["error_msg"] = "";
}
if (!("start_time" in $$source)) {
/**
* 开始时间
* @member
* @type {time$0.Time}
*/
this["start_time"] = null;
}
if (!("end_time" in $$source)) {
/**
* 结束时间
* @member
* @type {time$0.Time}
*/
this["end_time"] = null;
}
if (!("duration" in $$source)) {
/**
* 耗时(秒)
* @member
* @type {number}
*/
this["duration"] = 0;
}
Object.assign(this, $$source);
}
/**
* Creates a new DownloadRecord instance from a string or object.
* @param {any} [$$source = {}]
* @returns {DownloadRecord}
*/
static createFrom($$source = {}) {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new DownloadRecord(/** @type {Partial<DownloadRecord>} */($$parsedSource));
}
}

View File

@@ -0,0 +1,7 @@
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
export {
NginxSiteConfig
} from "./models.js";

View File

@@ -0,0 +1,94 @@
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import { Create as $Create } from "@wailsio/runtime";
/**
* NginxSiteConfig 站点配置结构
*/
export class NginxSiteConfig {
/**
* Creates a new NginxSiteConfig instance.
* @param {Partial<NginxSiteConfig>} [$$source = {}] - The source object to create the NginxSiteConfig.
*/
constructor($$source = {}) {
if (!("id" in $$source)) {
/**
* 站点唯一ID
* @member
* @type {string}
*/
this["id"] = "";
}
if (!("name" in $$source)) {
/**
* 站点名称
* @member
* @type {string}
*/
this["name"] = "";
}
if (!("domains" in $$source)) {
/**
* 域名列表
* @member
* @type {string[]}
*/
this["domains"] = [];
}
if (!("port" in $$source)) {
/**
* 监听端口
* @member
* @type {number}
*/
this["port"] = 0;
}
if (!("path" in $$source)) {
/**
* 网站根目录路径
* @member
* @type {string}
*/
this["path"] = "";
}
if (!("index" in $$source)) {
/**
* 默认首页文件
* @member
* @type {string}
*/
this["index"] = "";
}
if (!("enabled" in $$source)) {
/**
* 是否启用
* @member
* @type {boolean}
*/
this["enabled"] = false;
}
Object.assign(this, $$source);
}
/**
* Creates a new NginxSiteConfig instance from a string or object.
* @param {any} [$$source = {}]
* @returns {NginxSiteConfig}
*/
static createFrom($$source = {}) {
const $$createField2_0 = $$createType0;
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
if ("domains" in $$parsedSource) {
$$parsedSource["domains"] = $$createField2_0($$parsedSource["domains"]);
}
return new NginxSiteConfig(/** @type {Partial<NginxSiteConfig>} */($$parsedSource));
}
}
// Private type creation functions
const $$createType0 = $Create.Array($Create.Any);