diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 188c2c3..d209999 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -32,6 +32,8 @@ jobs: - name: 构建 Docker 镜像 run: | docker build \ + --build-arg GIT_COMMIT=${GITHUB_SHA} \ + --build-arg BUILD_TIME=$(date +%Y%m%d%H%M%S) \ -t "${IMAGE_NAME}:${{ steps.meta.outputs.tag }}" \ -t "${IMAGE_NAME}:latest" \ . diff --git a/Dockerfile b/Dockerfile index 22c6183..68b3978 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,6 +3,12 @@ FROM node:22-alpine WORKDIR /app +# 构建元数据(由 CI 传入;未传入时回退到运行时本地 git 信息) +ARG GIT_COMMIT=unknown +ARG BUILD_TIME=unknown +ENV GIT_COMMIT=${GIT_COMMIT} +ENV BUILD_TIME=${BUILD_TIME} + # 先复制清单以利用镜像层缓存 COPY package.json package-lock.json ./ diff --git a/public/admin.html b/public/admin.html index 21e87ec..5714f03 100644 --- a/public/admin.html +++ b/public/admin.html @@ -130,8 +130,11 @@ @@ -271,6 +274,7 @@ + - + + diff --git a/public/js/api.js b/public/js/api.js index 5a6b1da..cd4e97e 100644 --- a/public/js/api.js +++ b/public/js/api.js @@ -71,6 +71,10 @@ const API = { return this.request(`/api/projects/${id}/mappings`, { method: "PUT", body: mappings }); }, + updateProject(id, data) { + return this.request(`/api/projects/${id}`, { method: "PUT", body: data }); + }, + // ========== 配置 ========== getConfigs(projectId) { diff --git a/public/js/app.js b/public/js/app.js index b7b69bc..65286c1 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -290,17 +290,48 @@ function renderConfigTable(configs, path) { * @param {boolean} exists - 是否存在 * @returns {string} 值列 HTML */ -function buildValueCell(cfg, exists) { - if (!exists) return '—'; - const value = String(cfg.value); - if (value.trim() !== "") return escapeHtml(value); - const hasDefault = - cfg.defaultValue !== undefined && cfg.defaultValue !== null && String(cfg.defaultValue).trim() !== ""; - if (hasDefault) { - return `默认 ${escapeHtml(String(cfg.defaultValue))}`; - } - return ""; -} +function buildValueCell(cfg, exists) { + if (!exists) return '—'; + const value = String(cfg.value); + if (value.trim() !== "") { + if (value.length > 40) { + return ( + `${escapeHtml(value.slice(0, 40))}…` + + `` + ); + } + return escapeHtml(value); + } + const hasDefault = + cfg.defaultValue !== undefined && cfg.defaultValue !== null && String(cfg.defaultValue).trim() !== ""; + if (hasDefault) { + return `默认 ${escapeHtml(String(cfg.defaultValue))}`; + } + return ""; +} + +/** + * 值列"更多"按钮:展开/收起完整值(事件委托,仅在长值单元格内生效) + * @param {Event} e - 点击事件 + * @returns {void} + */ +function onValueMoreClick(e) { + const btn = e.target.closest(".value-more-btn"); + if (!btn) return; + const span = btn.previousElementSibling; + if (!span || !span.classList.contains("value-truncated")) return; + + if (btn.dataset.expanded === "1") { + span.innerHTML = escapeHtml(span.dataset.full.slice(0, 40)) + "…"; + btn.textContent = "更多"; + btn.dataset.expanded = ""; + } else { + span.innerHTML = escapeHtml(span.dataset.full); + btn.textContent = "收起"; + btn.dataset.expanded = "1"; + } +} +document.addEventListener("click", onValueMoreClick); /** * 根据当前项目维度开关,控制配置模态框中维度字段的显示 @@ -849,17 +880,31 @@ function bindResizer() { * 应用初始化入口 * @returns {Promise} */ -async function init() { - bindEvents(); - try { - const modeData = await API.getMode(); - state.mode = modeData.mode; - } catch { - state.mode = "edit"; - } - applyMode(); - await loadProjects(); - setStatus("就绪"); +async function init() { + bindEvents(); + try { + const modeData = await API.getMode(); + state.mode = modeData.mode; + } catch { + state.mode = "edit"; + } + applyMode(); + await loadProjects(); + + // 从项目列表跳转进入时,依据 URL 参数或上次选中项自动选中项目 + const params = new URLSearchParams(window.location.search); + let projectId = params.get("project"); + if (!projectId) { + try { projectId = localStorage.getItem("registry_selected_project"); } catch (e) {} + } + if (projectId && state.projects.some((p) => p.id === projectId)) { + const selector = document.getElementById("project-selector"); + if (selector) selector.value = projectId; + try { localStorage.setItem("registry_selected_project", projectId); } catch (e) {} + await selectProject(projectId); + } else { + setStatus("就绪"); + } } // 页面加载完成后启动 diff --git a/public/js/version.js b/public/js/version.js new file mode 100644 index 0000000..6d99002 --- /dev/null +++ b/public/js/version.js @@ -0,0 +1,26 @@ +/** + * 版本号加载器 + * 读取 /api/version 并将版本号填充到页面元素的 #version-text(或 .statusbar-version) + */ +(function () { + function fill(text) { + var el = document.getElementById("version-text"); + if (!el) el = document.querySelector(".statusbar-version"); + if (!el) return; + el.textContent = text; + } + + try { + fetch("/api/version") + .then(function (r) { + if (!r.ok) throw new Error("bad status"); + return r.json(); + }) + .then(function (data) { + if (data && data.success && data.data) { + fill(data.data.version || ""); + } + }) + .catch(function () {}); + } catch (e) {} +})(); \ No newline at end of file diff --git a/public/login.html b/public/login.html index 6b86494..c63dffc 100644 --- a/public/login.html +++ b/public/login.html @@ -51,8 +51,19 @@ - + + +