diff --git a/src/render.js b/src/render.js index 5bd2afe..c5452a6 100644 --- a/src/render.js +++ b/src/render.js @@ -15,6 +15,16 @@ const el = (tag, className, html) => { return node; }; +const externalLink = (className, href, text, ariaLabel = text) => { + const node = el("a", className); + node.href = href; + node.target = "_blank"; + node.rel = "noreferrer"; + node.textContent = text; + node.setAttribute("aria-label", ariaLabel); + return node; +}; + /* ---------- 各轮回答内容 ---------- */ function bootContent(answer) { @@ -70,9 +80,24 @@ function projectsContent(answer) { projects.forEach((p) => { const block = el("article", "project-block gen"); const head = el("div", "project-head"); + const meta = el("div", "project-meta"); + meta.append( + p.url + ? externalLink("name entity-link", p.url, p.name, `打开 ${p.name}`) + : el("span", "name", p.name), + ); + if (p.links?.length > 1) { + const links = el("div", "project-links"); + p.links.forEach((link) => { + links.append( + externalLink("mini-link", link.url, link.label, `打开 ${link.label}`), + ); + }); + meta.append(links); + } head.append( Object.assign(el("img"), { src: p.logo, alt: p.name, loading: "lazy" }), - el("span", "name", p.name), + meta, el("span", "period", p.period), ); block.append( @@ -125,7 +150,14 @@ function experienceContent(answer) { alt: exp.company, loading: "lazy", }), - el("span", "co", exp.company), + exp.url + ? externalLink( + "co entity-link", + exp.url, + exp.company, + `打开 ${exp.company}`, + ) + : el("span", "co", exp.company), el("span", "period", exp.period), ); item.append(head, el("div", "exp-role", exp.role)); diff --git a/src/resume-data.js b/src/resume-data.js index 03b13dc..6675005 100644 --- a/src/resume-data.js +++ b/src/resume-data.js @@ -73,6 +73,11 @@ export const projects = [ id: "vtrix", name: "SeaCloud / Vtrix", logo: "/logos/vtrix.png", + url: "https://cloud.seaart.ai/", + links: [ + { label: "SeaCloud", url: "https://cloud.seaart.ai/" }, + { label: "Vtrix", url: "https://www.vtrix.ai/" }, + ], period: "2025.09 - 至今", subtitle: "模型服务平台控制台", summary: @@ -89,6 +94,7 @@ export const projects = [ id: "seabuzz", name: "SeaBuzz", logo: "/logos/seabuzz.webp", + url: "https://play.google.com/store/apps/details?id=app.seahot.ai", period: "2025.05 - 至今", subtitle: "资讯、搜索与对话应用", summary: @@ -106,6 +112,7 @@ export const projects = [ id: "bawang", name: "霸王功夫", logo: "/logos/chagee.png", + url: "https://bwcj.com/", period: "2024.03 - 2025.03", subtitle: "门店、食安与供应链系统", summary: @@ -122,6 +129,7 @@ export const projects = [ id: "jingyingbang", name: "经营帮平台 + 经营帮拉新", logo: "/logos/jingyingbang.png", + url: "https://jingyingbang.com/", period: "2022.04 - 2024.03", subtitle: "微前端平台与小程序", summary: @@ -140,6 +148,7 @@ export const experiences = [ { company: "成都海艺互娱科技有限公司", logo: "/logos/seaart.webp", + url: "https://www.seaart.ai/", role: "React Native 开发工程师", period: "2025.05 - 至今", points: [ @@ -151,6 +160,7 @@ export const experiences = [ { company: "四川茶姬企业管理有限公司", logo: "/logos/chagee.png", + url: "https://bwcj.com/", role: "高级 Web 前端开发", period: "2024.03 - 2025.03", points: [ @@ -162,6 +172,7 @@ export const experiences = [ { company: "中钧科技有限公司四川分公司", logo: "/logos/zhongjun.png", + url: "https://zhongjunkeji.com/", role: "前端开发组长", period: "2022.04 - 2024.03", points: [ diff --git a/src/styles.css b/src/styles.css index 77cdd58..b13fe77 100644 --- a/src/styles.css +++ b/src/styles.css @@ -43,6 +43,27 @@ a { text-decoration: none; } +.entity-link { + display: inline-flex; + align-items: center; + gap: 6px; + color: var(--ink); + transition: color 0.18s ease; +} + +.entity-link::after { + content: "↗"; + font-family: var(--mono); + font-size: 10px; + color: var(--cyan); + opacity: 0.72; + transform: translateY(-1px); +} + +.entity-link:hover { + color: var(--cyan); +} + /* ============ 底层画布与氛围 ============ */ #mind-canvas { position: fixed; @@ -470,6 +491,7 @@ main { .project-head { display: flex; align-items: center; + flex-wrap: wrap; gap: 14px; margin-bottom: 6px; } @@ -487,6 +509,37 @@ main { font-weight: 700; } +.project-meta { + min-width: 0; + flex: 1 1 220px; + display: flex; + flex-direction: column; + gap: 4px; +} + +.project-links { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.mini-link { + font-family: var(--mono); + font-size: 10px; + line-height: 1.4; + padding: 2px 7px; + border-radius: 999px; + border: 1px solid rgba(34, 211, 238, 0.28); + color: var(--cyan); + background: rgba(34, 211, 238, 0.06); + transition: border-color 0.18s ease, background 0.18s ease; +} + +.mini-link:hover { + border-color: rgba(34, 211, 238, 0.62); + background: rgba(34, 211, 238, 0.12); +} + .project-head .period { margin-left: auto; font-family: var(--mono);