@@ -1365,6 +1407,9 @@ HTML_TEMPLATE = '''
adminUrl = adminUrl.replace(/localhost/g, externalIp);
}
+ // 获取自定义链接
+ const customLinks = getCustomLinks(p.id);
+
return `
@@ -1375,13 +1420,15 @@ HTML_TEMPLATE = '''
${statusInfo.text}
${p.ports && p.ports.length > 0 ? `
-
+
${p.ports.map(port => {
const portStatus = p.status?.ports?.[port];
const isRunning = portStatus?.running;
return `
${port}`;
}).join('')}
${adminUrl ? `
后台` : ''}
+ ${customLinks.map(link => `
${link.name}`).join('')}
+
` : ''}
${p.type === 'web' ? `
@@ -1401,6 +1448,121 @@ HTML_TEMPLATE = '''
`;
}
+
+ // ==================== 自定义链接管理 ====================
+
+ function getCustomLinks(projectId) {
+ const stored = localStorage.getItem('customLinks_' + projectId);
+ return stored ? JSON.parse(stored) : [];
+ }
+
+ function saveCustomLinks(projectId, links) {
+ localStorage.setItem('customLinks_' + projectId, JSON.stringify(links));
+ }
+
+ function showAddLinkModal(projectId, projectName) {
+ document.getElementById('linkModalTitle').textContent = `${projectName} - 添加链接`;
+ document.getElementById('linkProjectId').value = projectId;
+ document.getElementById('linkName').value = '';
+ document.getElementById('linkPort').value = '';
+ document.getElementById('linkPath').value = '';
+ document.getElementById('linkFullUrl').value = '';
+ document.getElementById('linkModal').classList.remove('hidden');
+
+ // 显示已有的自定义链接(供删除)
+ const existingLinks = getCustomLinks(projectId);
+ const existingList = document.getElementById('existingLinks');
+ if (existingLinks.length > 0) {
+ existingList.innerHTML = '
已有链接:
' +
+ existingLinks.map(link => `
+
+ ${link.name}: ${link.url}
+
+
+ `).join('');
+ } else {
+ existingList.innerHTML = '';
+ }
+ }
+
+ function closeLinkModal() {
+ document.getElementById('linkModal').classList.add('hidden');
+ }
+
+ function saveCustomLink(event) {
+ event.preventDefault();
+
+ const projectId = document.getElementById('linkProjectId').value;
+ const name = document.getElementById('linkName').value.trim();
+ const port = document.getElementById('linkPort').value.trim();
+ const path = document.getElementById('linkPath').value.trim();
+ const fullUrl = document.getElementById('linkFullUrl').value.trim();
+
+ if (!name) {
+ alert('请输入链接名称');
+ return;
+ }
+
+ let url;
+ if (fullUrl) {
+ url = fullUrl;
+ } else if (port) {
+ url = `http://${externalIp}:${port}${path || ''}`;
+ } else {
+ alert('请输入端口或完整URL');
+ return;
+ }
+
+ const links = getCustomLinks(projectId);
+ const newLink = {
+ id: Date.now().toString(),
+ name: name,
+ url: url
+ };
+ links.push(newLink);
+ saveCustomLinks(projectId, links);
+
+ closeLinkModal();
+ renderProjects();
+ }
+
+ function deleteCustomLink(projectId, linkId) {
+ if (!confirm('确定删除此链接?')) return;
+
+ const links = getCustomLinks(projectId);
+ const filtered = links.filter(l => l.id !== linkId);
+ saveCustomLinks(projectId, filtered);
+
+ // 更新模态框中的显示
+ const existingLinks = getCustomLinks(projectId);
+ const existingList = document.getElementById('existingLinks');
+ if (existingLinks.length > 0) {
+ existingList.innerHTML = '
已有链接:
' +
+ existingLinks.map(link => `
+
+ ${link.name}: ${link.url}
+
+
+ `).join('');
+ } else {
+ existingList.innerHTML = '
暂无自定义链接
';
+ }
+
+ renderProjects();
+ }
+
+ // 自动生成URL预览
+ function updateLinkPreview() {
+ const port = document.getElementById('linkPort').value.trim();
+ const path = document.getElementById('linkPath').value.trim();
+ const preview = document.getElementById('linkPreview');
+
+ if (port) {
+ preview.textContent = `预览: http://${externalIp}:${port}${path || ''}`;
+ } else {
+ preview.textContent = '';
+ }
+ }
function getStatusInfo(status) {
const map = {