1 Commits

Author SHA1 Message Date
湛兮 3b3a187835 chore: import fluid portfolio backup snapshot 2026-06-11 12:24:52 +08:00
15 changed files with 2844 additions and 6811 deletions
+6 -3
View File
@@ -1,10 +1,13 @@
# 王元有 - 前端工程师简历网站 # Wang Yuanyou Fluid Portfolio
基于 Astro 的个人简历网站,内容整理自 `王元有-前端工程师.pdf` Independent Vite + Three.js portfolio site for 王元有, focused on a more grounded frontend-engineering resume.
## Commands ## Commands
```bash ```bash
npm install npm install
npm run dev npm run dev -- --port 5177
npm run build
``` ```
The page uses a restrained scroll-aware WebGL backdrop, critical first-paint styling, grounded resume summaries, responsive layout, subtle scroll reveals, project cards, skills, experience, and contact sections.
-5
View File
@@ -1,5 +0,0 @@
import { defineConfig } from "astro/config";
export default defineConfig({
output: "static",
});
+195
View File
@@ -0,0 +1,195 @@
<!doctype html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta
name="description"
content="王元有个人网站,前端工程师,7 年 Web 与跨端经验,近一年参与 SeaBuzz、SeaCloud 等 AI 产品前端,覆盖对话、内容流、计费、权限和国际化。"
/>
<script>
(() => {
const saved = localStorage.getItem("wy-fluid-theme");
if (saved) document.documentElement.dataset.theme = saved;
})();
</script>
<style>
:root {
background: #07080d;
color: #f5f7fb;
color-scheme: dark;
}
html[data-theme="light"] {
background: #f2f5f7;
color: #11131a;
color-scheme: light;
}
body {
margin: 0;
min-height: 100vh;
background: inherit;
color: inherit;
font-family:
Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
"Microsoft YaHei", sans-serif;
}
</style>
<link rel="stylesheet" href="/src/styles.css" />
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
<title>王元有 - 前端工程师</title>
</head>
<body>
<div class="progress" id="scroll-progress"></div>
<canvas id="fluid-canvas" aria-hidden="true"></canvas>
<div class="scene-backdrop" id="scene-backdrop" aria-hidden="true"></div>
<div class="grain" aria-hidden="true"></div>
<header class="site-header">
<a class="brand-mark" href="#top" aria-label="王元有个人网站首页">
<span>WY</span>
<strong>王元有</strong>
</a>
<nav class="site-nav" aria-label="页面导航">
<a href="#ai-fit">方向</a>
<a href="#proof">证据</a>
<a href="#projects">项目</a>
<a href="#skills">能力</a>
<a href="#experience">经历</a>
<a href="#contact">联系</a>
</nav>
<div class="header-actions">
<a class="icon-link" href="https://github.com/zhanBoss" target="_blank" rel="noreferrer" aria-label="GitHub">
<svg viewBox="0 0 24 24" aria-hidden="true">
<path
fill="currentColor"
d="M12 2C6.48 2 2 6.58 2 12.23c0 4.52 2.87 8.35 6.84 9.7.5.1.68-.22.68-.5v-1.75c-2.78.62-3.37-1.37-3.37-1.37-.45-1.19-1.11-1.5-1.11-1.5-.91-.64.07-.63.07-.63 1 .07 1.53 1.06 1.53 1.06.9 1.56 2.35 1.11 2.92.85.09-.66.35-1.11.63-1.37-2.22-.26-4.56-1.14-4.56-5.07 0-1.12.39-2.03 1.03-2.75-.1-.26-.45-1.3.1-2.71 0 0 .84-.28 2.75 1.05A9.35 9.35 0 0 1 12 6.9c.85 0 1.71.12 2.51.34 1.91-1.33 2.75-1.05 2.75-1.05.55 1.41.2 2.45.1 2.71.64.72 1.03 1.63 1.03 2.75 0 3.94-2.34 4.81-4.57 5.06.36.32.68.94.68 1.9v2.82c0 .28.18.6.69.5A10.08 10.08 0 0 0 22 12.23C22 6.58 17.52 2 12 2Z"
></path>
</svg>
</a>
<button class="icon-link" id="theme-toggle" type="button" aria-label="切换明暗主题">
<svg viewBox="0 0 24 24" aria-hidden="true">
<path fill="currentColor" d="M12 2a10 10 0 1 0 0 20V2Zm2 2.29A8 8 0 0 1 14 19.71V4.29Z"></path>
</svg>
</button>
</div>
</header>
<main id="top">
<section class="hero" aria-labelledby="hero-title">
<div class="hero-meta reveal" data-drift="-24">
<p class="eyebrow">Frontend Engineer / Product UI / Cross-platform</p>
<h1 id="hero-title">王元有</h1>
<p class="hero-subtitle">前端工程师,近一年在 AI 产品团队做前端</p>
<p class="hero-lede">
做过 B 端供应链系统、小程序、微前端平台,也参与过 SeaBuzz 和 SeaCloud 这类 AI 产品。现在更擅长把对话流、
内容流、计费、权限、国际化这些复杂链路整理成稳定的前端界面。
</p>
<div class="hero-actions">
<a class="button primary" href="#projects">看项目经历</a>
<a class="button" href="mailto:419021733@qq.com">419021733@qq.com</a>
</div>
</div>
<div class="signal-board reveal" data-drift="28" aria-label="候选人摘要">
<div>
<span>前端经验</span>
<strong>7 年</strong>
</div>
<div>
<span>近一年方向</span>
<strong>对话与控制台</strong>
</div>
<div>
<span>主要场景</span>
<strong>对话 / 内容 / 控制台</strong>
</div>
<div>
<span>协作方式</span>
<strong>能独立推进模块</strong>
</div>
</div>
<div class="profile-panel reveal" data-drift="20" aria-label="工作方式摘要">
<div>
<span>最近职责</span>
<strong>SeaBuzz 跨端应用、SeaCloud 控制台</strong>
</div>
<div>
<span>更擅长</span>
<strong>把复杂状态、权限和数据边界梳理清楚</strong>
</div>
<div>
<span>常用技术</span>
<strong>React / Next.js / React Native / TypeScript</strong>
</div>
<div>
<span>工作习惯</span>
<strong>先对齐业务边界,再拆组件和状态模型</strong>
</div>
</div>
</section>
<section id="ai-fit" class="ai-fit-section reveal" data-drift="36" aria-labelledby="ai-fit-title">
<div class="section-heading">
<p>Current Direction</p>
<h2 id="ai-fit-title">AI 是最近一年的工作场景,不是拿来堆关键词的标签。</h2>
</div>
<div class="ai-focus-grid" id="ai-focus-grid"></div>
<div class="resume-signal-panel reveal" data-drift="-30">
<div>
<p>Resume Summary</p>
<h3>前端工程师|AI 产品方向|Web / React Native</h3>
</div>
<ul id="resume-signal-list"></ul>
</div>
</section>
<section id="proof" class="proof-strip reveal" data-drift="-40" aria-labelledby="proof-title">
<div class="section-kicker">Evidence</div>
<h2 id="proof-title">一些可以核对的经历,先放在前面。</h2>
<div class="metric-row" id="metric-row"></div>
</section>
<section id="projects" class="work-section" aria-labelledby="projects-title">
<div class="section-heading reveal" data-drift="42">
<p>Selected Work</p>
<h2 id="projects-title">最近两段经历和 AI 产品相关,但履历底子仍然是前端工程交付。</h2>
</div>
<div class="project-stack" id="project-stack"></div>
</section>
<section id="skills" class="skills-section" aria-labelledby="skills-title">
<div class="section-heading reveal" data-drift="-42">
<p>Capabilities</p>
<h2 id="skills-title">按实际做过的工作来组织技能,而不是按热门词来分类。</h2>
</div>
<div class="skill-matrix" id="skill-matrix"></div>
</section>
<section id="experience" class="experience-section" aria-labelledby="experience-title">
<div class="section-heading reveal" data-drift="34">
<p>Experience</p>
<h2 id="experience-title">职责从核心交付演进到架构设计和团队规范建设。</h2>
</div>
<div class="timeline" id="timeline"></div>
</section>
<section id="contact" class="contact-section reveal" data-drift="-26" aria-labelledby="contact-title">
<div>
<p class="section-kicker">Contact</p>
<h2 id="contact-title">正在看前端、跨端、AI 产品前端相关机会。</h2>
<span>男 · 29 岁 · 19980439383 · 电子科技大学 信息管理与信息系统 本科</span>
</div>
<div class="contact-actions">
<a class="button primary" href="mailto:419021733@qq.com">发送邮件</a>
<a class="button" href="mailto:wmagmgema521@gmail.com">wmagmgema521@gmail.com</a>
</div>
</section>
</main>
<footer>© 2026 王元有. Built as an independent fluid portfolio.</footer>
<script type="module" src="/src/main.js"></script>
</body>
</html>
+118 -3819
View File
File diff suppressed because it is too large Load Diff
+6 -7
View File
@@ -1,17 +1,16 @@
{ {
"name": "wang-yuanyou-resume-site", "name": "wang-yuanyou-fluid-portfolio",
"version": "1.0.0", "version": "1.0.0",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "astro dev", "dev": "vite --host 0.0.0.0",
"build": "astro build", "build": "vite build",
"build:test": "PUBLIC_APP_ENV_LABEL=测试环境 astro build", "preview": "vite preview --host 0.0.0.0"
"build:prod": "PUBLIC_APP_ENV_LABEL=生产环境 astro build",
"preview": "astro preview"
}, },
"dependencies": { "dependencies": {
"astro": "^6.3.1" "three": "^0.184.0",
"vite": "^7.2.7"
}, },
"devDependencies": {} "devDependencies": {}
} }
+649
View File
@@ -0,0 +1,649 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
dependencies:
three:
specifier: ^0.184.0
version: 0.184.0
vite:
specifier: ^7.2.7
version: 7.3.5
packages:
'@esbuild/aix-ppc64@0.27.7':
resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [aix]
'@esbuild/android-arm64@0.27.7':
resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==}
engines: {node: '>=18'}
cpu: [arm64]
os: [android]
'@esbuild/android-arm@0.27.7':
resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==}
engines: {node: '>=18'}
cpu: [arm]
os: [android]
'@esbuild/android-x64@0.27.7':
resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==}
engines: {node: '>=18'}
cpu: [x64]
os: [android]
'@esbuild/darwin-arm64@0.27.7':
resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==}
engines: {node: '>=18'}
cpu: [arm64]
os: [darwin]
'@esbuild/darwin-x64@0.27.7':
resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [darwin]
'@esbuild/freebsd-arm64@0.27.7':
resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==}
engines: {node: '>=18'}
cpu: [arm64]
os: [freebsd]
'@esbuild/freebsd-x64@0.27.7':
resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [freebsd]
'@esbuild/linux-arm64@0.27.7':
resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==}
engines: {node: '>=18'}
cpu: [arm64]
os: [linux]
'@esbuild/linux-arm@0.27.7':
resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==}
engines: {node: '>=18'}
cpu: [arm]
os: [linux]
'@esbuild/linux-ia32@0.27.7':
resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==}
engines: {node: '>=18'}
cpu: [ia32]
os: [linux]
'@esbuild/linux-loong64@0.27.7':
resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==}
engines: {node: '>=18'}
cpu: [loong64]
os: [linux]
'@esbuild/linux-mips64el@0.27.7':
resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==}
engines: {node: '>=18'}
cpu: [mips64el]
os: [linux]
'@esbuild/linux-ppc64@0.27.7':
resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [linux]
'@esbuild/linux-riscv64@0.27.7':
resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==}
engines: {node: '>=18'}
cpu: [riscv64]
os: [linux]
'@esbuild/linux-s390x@0.27.7':
resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==}
engines: {node: '>=18'}
cpu: [s390x]
os: [linux]
'@esbuild/linux-x64@0.27.7':
resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==}
engines: {node: '>=18'}
cpu: [x64]
os: [linux]
'@esbuild/netbsd-arm64@0.27.7':
resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==}
engines: {node: '>=18'}
cpu: [arm64]
os: [netbsd]
'@esbuild/netbsd-x64@0.27.7':
resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==}
engines: {node: '>=18'}
cpu: [x64]
os: [netbsd]
'@esbuild/openbsd-arm64@0.27.7':
resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openbsd]
'@esbuild/openbsd-x64@0.27.7':
resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==}
engines: {node: '>=18'}
cpu: [x64]
os: [openbsd]
'@esbuild/openharmony-arm64@0.27.7':
resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openharmony]
'@esbuild/sunos-x64@0.27.7':
resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==}
engines: {node: '>=18'}
cpu: [x64]
os: [sunos]
'@esbuild/win32-arm64@0.27.7':
resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [win32]
'@esbuild/win32-ia32@0.27.7':
resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==}
engines: {node: '>=18'}
cpu: [ia32]
os: [win32]
'@esbuild/win32-x64@0.27.7':
resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==}
engines: {node: '>=18'}
cpu: [x64]
os: [win32]
'@rollup/rollup-android-arm-eabi@4.61.1':
resolution: {integrity: sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==}
cpu: [arm]
os: [android]
'@rollup/rollup-android-arm64@4.61.1':
resolution: {integrity: sha512-Jx2g7iSjw4AOT0HDPHM9RV3GNjRXwybWtSFZiZAYUTjUwjVrYIwq3kBf+LnhqJlzXFAqTAh2F7IGI+O568exPw==}
cpu: [arm64]
os: [android]
'@rollup/rollup-darwin-arm64@4.61.1':
resolution: {integrity: sha512-0F1L/Z3Eqv8mT2n3dCpeO8GcTvHvVqkP5/t6DMsn0KzhYVcg+s7Ncl5DS8qjKYEeio6Az0Gt6nyBORay5qIlCA==}
cpu: [arm64]
os: [darwin]
'@rollup/rollup-darwin-x64@4.61.1':
resolution: {integrity: sha512-qLttcH871ujY4YcVfUSShhOw+CsoTatYz8gRbHO7Bb92QH059/P0y5do1KMs41fY0BpD2x4AJH/gID0zFiqVKQ==}
cpu: [x64]
os: [darwin]
'@rollup/rollup-freebsd-arm64@4.61.1':
resolution: {integrity: sha512-fUI4RapGE0Oh3mb8mgfvC1O2nU1RpDZUKnDQm3xB1Ipg7C2wTs5Kstz7G2uWK99a8S2yTMq8/P4uycwNa0nJyw==}
cpu: [arm64]
os: [freebsd]
'@rollup/rollup-freebsd-x64@4.61.1':
resolution: {integrity: sha512-H5YrdvJaDtI/U9/emrD4b++xkvp3y/JvOe4rizHbxvkyMfRS/CiRYdji+Pl8D0brEaNFWUh1drQxgAGIl6Xudw==}
cpu: [x64]
os: [freebsd]
'@rollup/rollup-linux-arm-gnueabihf@4.61.1':
resolution: {integrity: sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA==}
cpu: [arm]
os: [linux]
'@rollup/rollup-linux-arm-musleabihf@4.61.1':
resolution: {integrity: sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ==}
cpu: [arm]
os: [linux]
'@rollup/rollup-linux-arm64-gnu@4.61.1':
resolution: {integrity: sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg==}
cpu: [arm64]
os: [linux]
'@rollup/rollup-linux-arm64-musl@4.61.1':
resolution: {integrity: sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w==}
cpu: [arm64]
os: [linux]
'@rollup/rollup-linux-loong64-gnu@4.61.1':
resolution: {integrity: sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ==}
cpu: [loong64]
os: [linux]
'@rollup/rollup-linux-loong64-musl@4.61.1':
resolution: {integrity: sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ==}
cpu: [loong64]
os: [linux]
'@rollup/rollup-linux-ppc64-gnu@4.61.1':
resolution: {integrity: sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g==}
cpu: [ppc64]
os: [linux]
'@rollup/rollup-linux-ppc64-musl@4.61.1':
resolution: {integrity: sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw==}
cpu: [ppc64]
os: [linux]
'@rollup/rollup-linux-riscv64-gnu@4.61.1':
resolution: {integrity: sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g==}
cpu: [riscv64]
os: [linux]
'@rollup/rollup-linux-riscv64-musl@4.61.1':
resolution: {integrity: sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ==}
cpu: [riscv64]
os: [linux]
'@rollup/rollup-linux-s390x-gnu@4.61.1':
resolution: {integrity: sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g==}
cpu: [s390x]
os: [linux]
'@rollup/rollup-linux-x64-gnu@4.61.1':
resolution: {integrity: sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q==}
cpu: [x64]
os: [linux]
'@rollup/rollup-linux-x64-musl@4.61.1':
resolution: {integrity: sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw==}
cpu: [x64]
os: [linux]
'@rollup/rollup-openbsd-x64@4.61.1':
resolution: {integrity: sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg==}
cpu: [x64]
os: [openbsd]
'@rollup/rollup-openharmony-arm64@4.61.1':
resolution: {integrity: sha512-fbWnKqVkjrJN38vNe3ahkbk6iejS/3b0Nt7EEtPpE6RBacZcGXNKbzfHN3GUUlXOPghUg0j6XUGrtjX9z1sIvA==}
cpu: [arm64]
os: [openharmony]
'@rollup/rollup-win32-arm64-msvc@4.61.1':
resolution: {integrity: sha512-ArMl38iVAbk0New1ogihQNY6iphLi4ZaRsa037gUzv5yeKPY8TD3Dmy4x2RNC1VztU/uqm+G+/RwFrSka3Oy2g==}
cpu: [arm64]
os: [win32]
'@rollup/rollup-win32-ia32-msvc@4.61.1':
resolution: {integrity: sha512-0mYtjHS9ucAbcATycCNK9IGBk/cCe/ma7EmSLGZdsxnOA8cjRIyU04wDpVAD9NiOfLUR9KTxdiO53uOkherqjQ==}
cpu: [ia32]
os: [win32]
'@rollup/rollup-win32-x64-gnu@4.61.1':
resolution: {integrity: sha512-gK1iCEPfpoSG9wfBihXxvBMi8ZfcWffYkEsC/Eih+iFENTaewvNcrEQ69lIOWYO5pePHKLHHO7nq5AILGO/HQQ==}
cpu: [x64]
os: [win32]
'@rollup/rollup-win32-x64-msvc@4.61.1':
resolution: {integrity: sha512-X+zaP2x+j4RXGfbp/seSoRHWnPxzApilDszisZxbYH5C/jTxFhCtDNdPGZb9lJyYPs24wGxruPF7Y+sIXt9Gzw==}
cpu: [x64]
os: [win32]
'@types/estree@1.0.9':
resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
esbuild@0.27.7:
resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==}
engines: {node: '>=18'}
hasBin: true
fdir@6.5.0:
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
engines: {node: '>=12.0.0'}
peerDependencies:
picomatch: ^3 || ^4
peerDependenciesMeta:
picomatch:
optional: true
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
nanoid@3.3.12:
resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
picomatch@4.0.4:
resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==}
engines: {node: '>=12'}
postcss@8.5.15:
resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==}
engines: {node: ^10 || ^12 || >=14}
rollup@4.61.1:
resolution: {integrity: sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
three@0.184.0:
resolution: {integrity: sha512-wtTRjG92pM5eUg/KuUnHsqSAlPM296brTOcLgMRqEeylYTh/CdtvKUvCyyCQTzFuStieWxvZb8mVTMvdPyUpxg==}
tinyglobby@0.2.17:
resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
engines: {node: '>=12.0.0'}
vite@7.3.5:
resolution: {integrity: sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
'@types/node': ^20.19.0 || >=22.12.0
jiti: '>=1.21.0'
less: ^4.0.0
lightningcss: ^1.21.0
sass: ^1.70.0
sass-embedded: ^1.70.0
stylus: '>=0.54.8'
sugarss: ^5.0.0
terser: ^5.16.0
tsx: ^4.8.1
yaml: ^2.4.2
peerDependenciesMeta:
'@types/node':
optional: true
jiti:
optional: true
less:
optional: true
lightningcss:
optional: true
sass:
optional: true
sass-embedded:
optional: true
stylus:
optional: true
sugarss:
optional: true
terser:
optional: true
tsx:
optional: true
yaml:
optional: true
snapshots:
'@esbuild/aix-ppc64@0.27.7':
optional: true
'@esbuild/android-arm64@0.27.7':
optional: true
'@esbuild/android-arm@0.27.7':
optional: true
'@esbuild/android-x64@0.27.7':
optional: true
'@esbuild/darwin-arm64@0.27.7':
optional: true
'@esbuild/darwin-x64@0.27.7':
optional: true
'@esbuild/freebsd-arm64@0.27.7':
optional: true
'@esbuild/freebsd-x64@0.27.7':
optional: true
'@esbuild/linux-arm64@0.27.7':
optional: true
'@esbuild/linux-arm@0.27.7':
optional: true
'@esbuild/linux-ia32@0.27.7':
optional: true
'@esbuild/linux-loong64@0.27.7':
optional: true
'@esbuild/linux-mips64el@0.27.7':
optional: true
'@esbuild/linux-ppc64@0.27.7':
optional: true
'@esbuild/linux-riscv64@0.27.7':
optional: true
'@esbuild/linux-s390x@0.27.7':
optional: true
'@esbuild/linux-x64@0.27.7':
optional: true
'@esbuild/netbsd-arm64@0.27.7':
optional: true
'@esbuild/netbsd-x64@0.27.7':
optional: true
'@esbuild/openbsd-arm64@0.27.7':
optional: true
'@esbuild/openbsd-x64@0.27.7':
optional: true
'@esbuild/openharmony-arm64@0.27.7':
optional: true
'@esbuild/sunos-x64@0.27.7':
optional: true
'@esbuild/win32-arm64@0.27.7':
optional: true
'@esbuild/win32-ia32@0.27.7':
optional: true
'@esbuild/win32-x64@0.27.7':
optional: true
'@rollup/rollup-android-arm-eabi@4.61.1':
optional: true
'@rollup/rollup-android-arm64@4.61.1':
optional: true
'@rollup/rollup-darwin-arm64@4.61.1':
optional: true
'@rollup/rollup-darwin-x64@4.61.1':
optional: true
'@rollup/rollup-freebsd-arm64@4.61.1':
optional: true
'@rollup/rollup-freebsd-x64@4.61.1':
optional: true
'@rollup/rollup-linux-arm-gnueabihf@4.61.1':
optional: true
'@rollup/rollup-linux-arm-musleabihf@4.61.1':
optional: true
'@rollup/rollup-linux-arm64-gnu@4.61.1':
optional: true
'@rollup/rollup-linux-arm64-musl@4.61.1':
optional: true
'@rollup/rollup-linux-loong64-gnu@4.61.1':
optional: true
'@rollup/rollup-linux-loong64-musl@4.61.1':
optional: true
'@rollup/rollup-linux-ppc64-gnu@4.61.1':
optional: true
'@rollup/rollup-linux-ppc64-musl@4.61.1':
optional: true
'@rollup/rollup-linux-riscv64-gnu@4.61.1':
optional: true
'@rollup/rollup-linux-riscv64-musl@4.61.1':
optional: true
'@rollup/rollup-linux-s390x-gnu@4.61.1':
optional: true
'@rollup/rollup-linux-x64-gnu@4.61.1':
optional: true
'@rollup/rollup-linux-x64-musl@4.61.1':
optional: true
'@rollup/rollup-openbsd-x64@4.61.1':
optional: true
'@rollup/rollup-openharmony-arm64@4.61.1':
optional: true
'@rollup/rollup-win32-arm64-msvc@4.61.1':
optional: true
'@rollup/rollup-win32-ia32-msvc@4.61.1':
optional: true
'@rollup/rollup-win32-x64-gnu@4.61.1':
optional: true
'@rollup/rollup-win32-x64-msvc@4.61.1':
optional: true
'@types/estree@1.0.9': {}
esbuild@0.27.7:
optionalDependencies:
'@esbuild/aix-ppc64': 0.27.7
'@esbuild/android-arm': 0.27.7
'@esbuild/android-arm64': 0.27.7
'@esbuild/android-x64': 0.27.7
'@esbuild/darwin-arm64': 0.27.7
'@esbuild/darwin-x64': 0.27.7
'@esbuild/freebsd-arm64': 0.27.7
'@esbuild/freebsd-x64': 0.27.7
'@esbuild/linux-arm': 0.27.7
'@esbuild/linux-arm64': 0.27.7
'@esbuild/linux-ia32': 0.27.7
'@esbuild/linux-loong64': 0.27.7
'@esbuild/linux-mips64el': 0.27.7
'@esbuild/linux-ppc64': 0.27.7
'@esbuild/linux-riscv64': 0.27.7
'@esbuild/linux-s390x': 0.27.7
'@esbuild/linux-x64': 0.27.7
'@esbuild/netbsd-arm64': 0.27.7
'@esbuild/netbsd-x64': 0.27.7
'@esbuild/openbsd-arm64': 0.27.7
'@esbuild/openbsd-x64': 0.27.7
'@esbuild/openharmony-arm64': 0.27.7
'@esbuild/sunos-x64': 0.27.7
'@esbuild/win32-arm64': 0.27.7
'@esbuild/win32-ia32': 0.27.7
'@esbuild/win32-x64': 0.27.7
fdir@6.5.0(picomatch@4.0.4):
optionalDependencies:
picomatch: 4.0.4
fsevents@2.3.3:
optional: true
nanoid@3.3.12: {}
picocolors@1.1.1: {}
picomatch@4.0.4: {}
postcss@8.5.15:
dependencies:
nanoid: 3.3.12
picocolors: 1.1.1
source-map-js: 1.2.1
rollup@4.61.1:
dependencies:
'@types/estree': 1.0.9
optionalDependencies:
'@rollup/rollup-android-arm-eabi': 4.61.1
'@rollup/rollup-android-arm64': 4.61.1
'@rollup/rollup-darwin-arm64': 4.61.1
'@rollup/rollup-darwin-x64': 4.61.1
'@rollup/rollup-freebsd-arm64': 4.61.1
'@rollup/rollup-freebsd-x64': 4.61.1
'@rollup/rollup-linux-arm-gnueabihf': 4.61.1
'@rollup/rollup-linux-arm-musleabihf': 4.61.1
'@rollup/rollup-linux-arm64-gnu': 4.61.1
'@rollup/rollup-linux-arm64-musl': 4.61.1
'@rollup/rollup-linux-loong64-gnu': 4.61.1
'@rollup/rollup-linux-loong64-musl': 4.61.1
'@rollup/rollup-linux-ppc64-gnu': 4.61.1
'@rollup/rollup-linux-ppc64-musl': 4.61.1
'@rollup/rollup-linux-riscv64-gnu': 4.61.1
'@rollup/rollup-linux-riscv64-musl': 4.61.1
'@rollup/rollup-linux-s390x-gnu': 4.61.1
'@rollup/rollup-linux-x64-gnu': 4.61.1
'@rollup/rollup-linux-x64-musl': 4.61.1
'@rollup/rollup-openbsd-x64': 4.61.1
'@rollup/rollup-openharmony-arm64': 4.61.1
'@rollup/rollup-win32-arm64-msvc': 4.61.1
'@rollup/rollup-win32-ia32-msvc': 4.61.1
'@rollup/rollup-win32-x64-gnu': 4.61.1
'@rollup/rollup-win32-x64-msvc': 4.61.1
fsevents: 2.3.3
source-map-js@1.2.1: {}
three@0.184.0: {}
tinyglobby@0.2.17:
dependencies:
fdir: 6.5.0(picomatch@4.0.4)
picomatch: 4.0.4
vite@7.3.5:
dependencies:
esbuild: 0.27.7
fdir: 6.5.0(picomatch@4.0.4)
picomatch: 4.0.4
postcss: 8.5.15
rollup: 4.61.1
tinyglobby: 0.2.17
optionalDependencies:
fsevents: 2.3.3
+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<rect width="64" height="64" rx="14" fill="#07080d"/>
<path d="M12 17h8l5 25 7-25h7l7 25 5-25h7L49 50h-8l-6-22-6 22h-8L12 17Z" fill="#56f7c5"/>
<path d="M13 50h38" stroke="#ff5ea8" stroke-width="4" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 297 B

-481
View File
@@ -1,481 +0,0 @@
export const resume = {
name: "王元有",
alias: "湛兮",
title: "前端开发工程师",
intent: "求职意向:前端开发工程师",
profile:
"7 年前端与跨端开发经验,长期负责 Web、App、H5、小程序与管理后台的架构设计、工程化建设和核心业务交付。熟悉 AI 产品工程、SSE 流式通信、Next.js/Vue/React Native 跨端体系、微前端和 Monorepo 协作模式。",
basics: [
{ label: "工作经验", value: "7 年" },
{ label: "技术管理", value: "2+ 年" },
{ label: "项目用户规模", value: "千万级 MAU" },
{ label: "教育背景", value: "电子科技大学 本科" },
],
contact: {
phone: "19980439383",
email: "419021733@qq.com",
emailAlt: "wmagmgema521@gmail.com",
meta: "男 | 29 岁",
},
highlights: [
"主导多款企业级产品从 0 到 1 搭建,覆盖 AI 模型服务、AI 资讯对话、供应链、门店数字化、工业互联网平台等场景。",
"掌握 Vue3、React、Next.js、React Native、Expo、UniApp 等技术栈,能独立完成 Web、移动端、App 与管理后台方案落地。",
"推动 Micro-Frontend、SSR、pnpm Monorepo、Git Flow、代码审查、CI/CD、监控体系等工程化实践。",
"有明确的性能与效率结果:构建体积优化 30%、首屏白屏时间缩短 25%、组件库工程化提升开发效率 50%、重复开发成本减少约 40%。",
],
metrics: [
{ value: "-30%", label: "构建体积优化" },
{ value: "-25%", label: "首屏白屏时间" },
{ value: "+50%", label: "组件库效率提升" },
{ value: "+40%", label: "团队人效提升" },
{ value: "40%", label: "重复开发成本降低" },
{ value: "30%", label: "消息渲染性能提升" },
],
skills: [
{
group: "前端框架",
items: [
"Next.js 16",
"React 19",
"Vue 3",
"TypeScript 5",
"Tailwind CSS 4",
"Nuxt/SSR",
],
},
{
group: "跨端开发",
items: [
"React Native 0.79",
"Expo 53",
"UniApp",
"H5",
"微信/飞书小程序",
],
},
{
group: "AI 与通信",
items: [
"Vercel AI SDK v6",
"SSE 流式通信",
"多 Agent 协作",
"Markdown/LaTeX 渲染",
"AI 对话",
"AI 生图/生视频",
],
},
{
group: "工程化",
items: [
"pnpm Monorepo",
"Git Submodules",
"Qiankun",
"Lerna",
"Git Flow",
"Playwright",
],
},
{
group: "状态与 UI",
items: [
"Zustand",
"MobX",
"Pinia",
"Radix UI",
"Ant Design",
"Element Plus",
"ProComponents",
],
},
{
group: "基础设施",
items: [
"Alova",
"next-intl",
"i18next",
"ARMS 监控",
"CDN 优化",
"Adyen 支付",
],
},
],
experiences: [
{
company: "成都海艺互娱科技有限公司",
role: "React Native 开发工程师",
period: "2025.05 - 至今",
points: [
"参与多款 AI 产品从 0 到 1 搭建与架构设计,覆盖前端性能、交互体验、国际化体系与多端组件复用。",
"在 AI 对话、生图、生视频与多 Agent 协作领域沉淀工程经验,负责流式通信与前端渲染体验优化。",
"支撑千万级海外用户产品,推动多端组件库统一建设,减少重复开发成本约 40%。",
],
},
{
company: "四川茶姬企业管理有限公司",
role: "高级 Web 前端开发",
period: "2024.03 - 2025.03",
points: [
"主导供应链管理系统前端开发,实现采购、仓储、配送等核心模块。",
"独立负责门店报损与食安管理核心功能搭建及迭代,覆盖国内、东南亚与北美业务逻辑。",
"引入阿里云 ARMS 前端监控体系,落地 CDN 静态资源优化方案,并制定代码审查与 Git 操作规范。",
],
},
{
company: "中钧科技有限公司四川分公司",
role: "前端开发组长",
period: "2022.04 - 2024.03",
points: [
"负责经营帮 PC 端微前端、门户和商管核心模块开发,完成 UI 还原、接口联调及性能优化。",
"从零搭建新 E 畅行小程序基础框架,完成技术方案制定与全流程开发。",
"主导经营帮小程序、H5、Admin 后台迭代开发与跨端技术方案设计,建立 Git Flow、代码审查和新人培训机制。",
],
},
],
projects: [
{
id: "vtrix",
name: "SeaCloud / Vtrix",
subtitle: "AI 模型服务平台(Web 端)",
period: "2025.09 - 至今",
tech: [
"Next.js 16",
"React 19",
"TypeScript",
"Tailwind CSS",
"Zustand",
"Alova",
"Vercel AI SDK",
"Radix UI",
"next-intl",
],
summary:
"面向全球用户的 AI 模型聚合服务平台,聚合 LLM、图像、视频、音频、3D 等多模态模型能力,覆盖 C 端调用与 B 端组织/分销管理。",
modules: [
"分销商客户邀请、折扣模板、额度分配、销售配置与利润率计算。",
"多币种与汇率体系,封装 useCurrency Hook 并贯穿 Pricing、Billing、API Keys 等模块。",
"组织成员、角色权限守卫、配额设置、支出限额、账单报表、交易筛选与 Excel 导出。",
"i18n Submodule 架构迁移、企业微信同步工作流、通用表格/筛选器/分页组件增强。",
],
},
{
id: "seabuzz",
name: "SeaBuzz",
subtitle: "AI 智能资讯与对话平台",
period: "2025.05 - 至今",
tech: [
"React Native",
"Expo 53",
"Expo Router",
"Zustand",
"NativeWind",
"SSE",
"i18next",
"Adyen",
"Lerna",
],
summary:
"海艺 AI 旗下 AI 新闻聚合、智能搜索与多模态对话平台,基于 Expo 实现 iOS、Android、Web 三端统一开发。",
modules: [
"AI Agent 对话完整链路:SSE 流式聊天、打字机渲染、Markdown/LaTeX、思考动画、来源引用与历史同步。",
"Discover 发现页与新闻详情,支持瀑布流、大/小卡动态布局、骨架屏与 Smart Image。",
"Google、Facebook、Discord、Email 登录与 SeaArt Auth SDK 对接。",
"Monorepo 公共包体系、API 层、数据模型、UI 组件、状态管理、OTA 热更新与代码签名机制。",
],
},
{
id: "bawang",
name: "霸王功夫",
subtitle: "门店数字化与供应链管理平台",
period: "2024.03 - 2025.03",
tech: [
"React 18",
"Vue 3",
"UniApp",
"MobX",
"Pinia",
"Element Plus",
"Ant Design",
"ProComponents",
],
summary:
"服务全球 6000+ 门店及运营伙伴的数字化管理平台,覆盖门店运营、食品安全、供应链协同等核心业务。",
modules: [
"小程序报损模块:摄像头扫码、在线报损登记,兼容微信小程序与飞书 H5。",
"食安管理模块:低频蓝牙连接 TSPL 指令集打印机,支持国内、东南亚、北美三套业务逻辑。",
"经营信息模块:多门店经营状态移动端看板,按区域、时间、指标筛选。",
"供应链模块:采购系统、供应商合同管理、供应商结算系统,对接云厉、费控等外部系统。",
],
},
{
id: "jingyingbang",
name: "经营帮平台 + 经营帮拉新",
subtitle: "工业互联网与微前端平台",
period: "2022.04 - 2024.03",
tech: [
"Vue",
"Qiankun",
"Element UI",
"华为云 OBS",
"百度地图",
"高德地图",
"IM",
"UniApp",
],
summary:
"基于信息化设计理念和区块链技术的工业互联网平台,为企业和个人提供数字化运营服务。",
modules: [
"参与单体前端到 Qiankun 微前端拆分,拆分出 12 个基础项目。",
"负责商管、门户、经营帮系列小程序/H5/Admin 后台核心业务交付。",
"引入华为云 OBS 直传减轻请求链路性能浪费。",
"开发内部 Chrome 插件 zjkj-decryption,提升数据解析效率。",
],
},
],
education: {
school: "电子科技大学",
degree: "本科",
major: "信息管理与信息系统",
period: "2023 - 2025",
},
};
export const resumeEn = {
name: "Wang Yuanyou",
alias: "mrZhan",
title: "Frontend Engineer",
intent: "Target Role: Frontend Engineer",
profile:
"Frontend and cross-platform engineer with 7 years of experience building Web, App, H5, mini-program and admin systems. Strong in AI product engineering, SSE streaming, Next.js/Vue/React Native ecosystems, micro-frontends and Monorepo collaboration.",
basics: [
{ label: "Experience", value: "7 years" },
{ label: "Tech Leadership", value: "2+ years" },
{ label: "Product Scale", value: "10M+ MAU" },
{ label: "Education", value: "UESTC Bachelor" },
],
contact: {
phone: "19980439383",
email: "419021733@qq.com",
emailAlt: "wmagmgema521@gmail.com",
meta: "Male | 29",
},
highlights: [
"Led multiple enterprise products from 0 to 1 across AI model services, AI news and chat, supply chain, store digitization and industrial internet platforms.",
"Hands-on with Vue3, React, Next.js, React Native, Expo and UniApp, capable of delivering Web, mobile, App and admin products end to end.",
"Drove engineering practices including Micro-Frontend, SSR, pnpm Monorepo, Git Flow, code review, CI/CD and frontend observability.",
"Delivered measurable outcomes: 30% smaller bundles, 25% faster first screen, 50% faster component-driven delivery and around 40% less duplicated work.",
],
metrics: [
{ value: "-30%", label: "Bundle Size" },
{ value: "-25%", label: "First Screen Blank Time" },
{ value: "+50%", label: "Component Delivery Efficiency" },
{ value: "+40%", label: "Team Productivity" },
{ value: "40%", label: "Duplicated Work Reduced" },
{ value: "30%", label: "Message Rendering Performance" },
],
skills: [
{
group: "Frontend Frameworks",
items: [
"Next.js 16",
"React 19",
"Vue 3",
"TypeScript 5",
"Tailwind CSS 4",
"Nuxt/SSR",
],
},
{
group: "Cross-platform",
items: [
"React Native 0.79",
"Expo 53",
"UniApp",
"H5",
"WeChat/Lark Mini Programs",
],
},
{
group: "AI & Streaming",
items: [
"Vercel AI SDK v6",
"SSE Streaming",
"Multi-Agent Collaboration",
"Markdown/LaTeX",
"AI Chat",
"AI Image/Video",
],
},
{
group: "Engineering",
items: [
"pnpm Monorepo",
"Git Submodules",
"Qiankun",
"Lerna",
"Git Flow",
"Playwright",
],
},
{
group: "State & UI",
items: [
"Zustand",
"MobX",
"Pinia",
"Radix UI",
"Ant Design",
"Element Plus",
"ProComponents",
],
},
{
group: "Infrastructure",
items: [
"Alova",
"next-intl",
"i18next",
"ARMS Monitoring",
"CDN Optimization",
"Adyen Payments",
],
},
],
experiences: [
{
company: "Chengdu Haiyi Interactive Entertainment Technology Co., Ltd.",
role: "React Native Engineer",
period: "2025.05 - Present",
points: [
"Contributed to architecture and 0-to-1 delivery of multiple AI products, covering performance, UX, i18n and multi-platform component reuse.",
"Built engineering experience in AI chat, image/video generation and multi-agent collaboration, with focus on streaming and frontend rendering performance.",
"Supported products serving 10M+ overseas users and promoted unified multi-platform component libraries, reducing duplicate work by around 40%.",
],
},
{
company: "Sichuan Chaji Enterprise Management Co., Ltd.",
role: "Senior Web Frontend Developer",
period: "2024.03 - 2025.03",
points: [
"Led frontend development of supply-chain management systems, including procurement, warehousing and delivery modules.",
"Owned store loss-reporting and food-safety features across China, Southeast Asia and North America business rules.",
"Introduced Alibaba Cloud ARMS frontend monitoring, delivered CDN optimization and established code review and Git workflow standards.",
],
},
{
company: "Zhongjun Technology Sichuan Branch",
role: "Frontend Team Lead",
period: "2022.04 - 2024.03",
points: [
"Delivered core modules for the Jingyingbang PC micro-frontend platform, portal and business management systems, including UI implementation, API integration and performance optimization.",
"Built the New E Travel mini-program foundation from scratch and owned technical planning through delivery.",
"Led mini-program, H5 and admin iterations, designed cross-platform solutions, and established Git Flow, code review and onboarding practices.",
],
},
],
projects: [
{
id: "vtrix",
name: "SeaCloud / Vtrix",
subtitle: "AI Model Service Platform (Web)",
period: "2025.09 - Present",
tech: [
"Next.js 16",
"React 19",
"TypeScript",
"Tailwind CSS",
"Zustand",
"Alova",
"Vercel AI SDK",
"Radix UI",
"next-intl",
],
summary:
"A global AI model aggregation platform covering LLM, image, video, audio and 3D model capabilities for developers, organizations and distributors.",
modules: [
"Built distributor invitation, discount templates, credit allocation, sales configuration and profit margin workflows.",
"Implemented global currency switching and exchange-rate conversion via a reusable useCurrency hook across Pricing, Billing and API Keys.",
"Delivered organization roles, quota controls, spending limits, billing reports, transaction filtering and Excel export flows.",
"Supported i18n submodule migration, WeCom translation sync and shared table/filter/pagination component enhancements.",
],
},
{
id: "seabuzz",
name: "SeaBuzz",
subtitle: "AI News and Conversation Platform",
period: "2025.05 - Present",
tech: [
"React Native",
"Expo 53",
"Expo Router",
"Zustand",
"NativeWind",
"SSE",
"i18next",
"Adyen",
"Lerna",
],
summary:
"An AI-powered news aggregation, smart search and multimodal conversation platform under SeaArt AI, built with Expo for iOS, Android and Web.",
modules: [
"Built the AI Agent chat flow with SSE streaming, typewriter rendering, Markdown/LaTeX, thinking animation, citations and history sync.",
"Delivered Discover feed and news detail pages with masonry layout, large/small dynamic cards, skeleton loading and Smart Image.",
"Integrated Google, Facebook, Discord and Email login with SeaArt Auth SDK.",
"Built Monorepo shared packages for API, data models, UI components, state, OTA updates and code signing.",
],
},
{
id: "bawang",
name: "Bawang Kungfu",
subtitle: "Store Digitization and Supply Chain Platform",
period: "2024.03 - 2025.03",
tech: [
"React 18",
"Vue 3",
"UniApp",
"MobX",
"Pinia",
"Element Plus",
"Ant Design",
"ProComponents",
],
summary:
"A digital operations platform serving 6,000+ stores and partners, covering store operations, food safety and supply-chain collaboration.",
modules: [
"Built mini-program loss reporting with camera scanning and online registration, compatible with WeChat mini-program and Lark H5.",
"Implemented food safety flows with Bluetooth TSPL printers and separate China, Southeast Asia and North America business rules.",
"Delivered mobile dashboards for multi-store operation metrics with region, time and KPI filtering.",
"Built procurement, supplier contract and settlement modules, integrating multiple external systems.",
],
},
{
id: "jingyingbang",
name: "Jingyingbang Platform",
subtitle: "Industrial Internet and Micro-frontend Platform",
period: "2022.04 - 2024.03",
tech: [
"Vue",
"Qiankun",
"Element UI",
"Huawei Cloud OBS",
"Baidu Map",
"Amap",
"IM",
"UniApp",
],
summary:
"An industrial internet platform based on informatization and blockchain concepts, providing digital operation services for companies and individuals.",
modules: [
"Participated in splitting a large frontend monolith into 12 Qiankun-based micro-frontend projects.",
"Delivered core business features for portals, business management, mini-program, H5 and admin systems.",
"Introduced Huawei Cloud OBS direct upload to reduce request-chain overhead.",
"Built an internal Chrome extension, zjkj-decryption, to improve data parsing efficiency.",
],
},
],
education: {
school: "University of Electronic Science and Technology of China",
degree: "Bachelor",
major: "Information Management and Information Systems",
period: "2023 - 2025",
},
};
+669
View File
@@ -0,0 +1,669 @@
import * as THREE from "three";
import { experiences, focusAreas, metrics, projects, resumeSignals, skills } from "./resume-data";
const $ = (selector) => document.querySelector(selector);
const $$ = (selector) => [...document.querySelectorAll(selector)];
const metricRow = $("#metric-row");
const aiFocusGrid = $("#ai-focus-grid");
const resumeSignalList = $("#resume-signal-list");
const projectStack = $("#project-stack");
const skillMatrix = $("#skill-matrix");
const timeline = $("#timeline");
const progress = $("#scroll-progress");
const themeToggle = $("#theme-toggle");
const root = document.documentElement;
const clamp = (value, min = 0, max = 1) => Math.min(max, Math.max(min, value));
const lerp = (from, to, amount) => from + (to - from) * amount;
const smoothstep = (edge0, edge1, value) => {
const t = clamp((value - edge0) / (edge1 - edge0));
return t * t * (3 - 2 * t);
};
const fluidScenes = [
{
id: "top",
name: "hero",
colors: ["#64cbb8", "#8aa4d6", "#d77b91"],
desktop: { x: 2.05, y: 0.22, z: -0.78, scale: 1.08 },
compact: { x: 1.34, y: 1.14, z: -1.08, scale: 0.38 },
flow: 0.62,
twist: 0.48,
ring: 0.82,
particle: 0.78,
angle: 128,
},
{
id: "ai-fit",
name: "ai-fit",
colors: ["#74c8bd", "#8097d4", "#d4b56c"],
desktop: { x: 1.84, y: 0.08, z: -0.88, scale: 0.96 },
compact: { x: 1.18, y: 1.05, z: -1.14, scale: 0.34 },
flow: 0.48,
twist: 0.72,
ring: 0.62,
particle: 0.58,
angle: 154,
},
{
id: "proof",
name: "proof",
colors: ["#d4b56c", "#6dc4b1", "#c97a8d"],
desktop: { x: 2.22, y: -0.04, z: -0.84, scale: 0.9 },
compact: { x: 1.48, y: 1.0, z: -1.18, scale: 0.31 },
flow: 0.36,
twist: 0.34,
ring: 0.92,
particle: 0.52,
angle: 96,
},
{
id: "projects",
name: "projects",
colors: ["#889fd4", "#c97991", "#68c7b2"],
desktop: { x: 1.68, y: 0.26, z: -0.7, scale: 1.18 },
compact: { x: 1.22, y: 1.18, z: -1.02, scale: 0.4 },
flow: 0.82,
twist: 0.92,
ring: 0.78,
particle: 0.9,
angle: 202,
},
{
id: "skills",
name: "skills",
colors: ["#75bd9a", "#d1b46d", "#879dd0"],
desktop: { x: 2.3, y: 0.04, z: -0.96, scale: 0.86 },
compact: { x: 1.52, y: 1.08, z: -1.22, scale: 0.31 },
flow: 0.42,
twist: 0.56,
ring: 1.08,
particle: 0.46,
angle: 72,
},
{
id: "experience",
name: "experience",
colors: ["#9a8fcb", "#70beb0", "#849bd0"],
desktop: { x: 1.96, y: -0.1, z: -0.92, scale: 1.02 },
compact: { x: 1.36, y: 1.0, z: -1.14, scale: 0.36 },
flow: 0.3,
twist: 0.82,
ring: 0.72,
particle: 0.64,
angle: 232,
},
{
id: "contact",
name: "contact",
colors: ["#c97991", "#d0b56f", "#70bda8"],
desktop: { x: 1.46, y: 0.02, z: -0.66, scale: 1.24 },
compact: { x: 1.16, y: 1.14, z: -1.02, scale: 0.42 },
flow: 0.24,
twist: 0.28,
ring: 0.5,
particle: 0.42,
angle: 312,
},
].map((scene) => ({
...scene,
colorObjects: scene.colors.map((color) => new THREE.Color(color)),
}));
function getSceneFrame() {
const center = window.scrollY + window.innerHeight * 0.28;
const points = fluidScenes.map((scene) => ({
scene,
top: scene.id === "top" ? 0 : document.getElementById(scene.id)?.offsetTop ?? document.documentElement.scrollHeight,
}));
let index = 0;
for (let i = 0; i < points.length - 1; i += 1) {
if (center >= points[i + 1].top) index = i + 1;
}
const current = points[index];
const next = points[Math.min(points.length - 1, index + 1)];
const span = Math.max(1, next.top - current.top);
const blend = current === next ? 0 : smoothstep(0.18, 0.86, (center - current.top) / span);
return { current: current.scene, next: next.scene, blend };
}
function mixScene(frame, compact) {
const layoutA = compact ? frame.current.compact : frame.current.desktop;
const layoutB = compact ? frame.next.compact : frame.next.desktop;
return {
name: frame.blend > 0.58 ? frame.next.name : frame.current.name,
x: lerp(layoutA.x, layoutB.x, frame.blend),
y: lerp(layoutA.y, layoutB.y, frame.blend),
z: lerp(layoutA.z, layoutB.z, frame.blend),
scale: lerp(layoutA.scale, layoutB.scale, frame.blend),
flow: lerp(frame.current.flow, frame.next.flow, frame.blend),
twist: lerp(frame.current.twist, frame.next.twist, frame.blend),
ring: lerp(frame.current.ring, frame.next.ring, frame.blend),
particle: lerp(frame.current.particle, frame.next.particle, frame.blend),
angle: lerp(frame.current.angle, frame.next.angle, frame.blend),
colors: frame.current.colorObjects.map((color, index) => color.clone().lerp(frame.next.colorObjects[index], frame.blend)),
};
}
function writeSceneCss(scene, scroll) {
root.dataset.scene = scene.name;
root.style.setProperty("--scene-a", scene.colors[0].getStyle());
root.style.setProperty("--scene-b", scene.colors[1].getStyle());
root.style.setProperty("--scene-c", scene.colors[2].getStyle());
root.style.setProperty("--scene-angle", `${scene.angle.toFixed(2)}deg`);
root.style.setProperty("--scene-opacity", (0.18 + scene.flow * 0.12).toFixed(3));
root.style.setProperty("--scene-drift-x", `${(-4 + scene.x * 1.4 - scroll * 3).toFixed(2)}vw`);
root.style.setProperty("--scene-drift-y", `${(-2 + scene.y * 2 + scroll * 2.5).toFixed(2)}vh`);
root.style.setProperty("--scene-rotate", `${(-1 + scene.twist * 2.5 + scroll * 3).toFixed(2)}deg`);
}
function renderMetrics() {
metricRow.innerHTML = metrics
.map((item, index) => `<article class="reveal" data-drift="${index % 2 === 0 ? 28 : -28}"><strong>${item.value}</strong><span>${item.label}</span></article>`)
.join("");
}
function renderFocusAreas() {
aiFocusGrid.innerHTML = focusAreas
.map(
(item, index) => `
<article class="ai-focus-card reveal" data-drift="${index % 2 === 0 ? -34 : 34}">
<span>${String(index + 1).padStart(2, "0")}</span>
<h3>${item.title}</h3>
<p>${item.summary}</p>
<div>${item.points.map((point) => `<em>${point}</em>`).join("")}</div>
</article>
`,
)
.join("");
}
function renderResumeSignals() {
resumeSignalList.innerHTML = resumeSignals.map((signal) => `<li>${signal}</li>`).join("");
}
function renderProjects() {
projectStack.innerHTML = projects
.map(
(project, index) => `
<article class="project-card reveal" data-drift="${index % 2 === 0 ? -52 : 52}">
<div class="project-index">
<span class="logo-frame"><img src="${project.logo}" alt="" loading="lazy" decoding="async" /></span>
<span>${String(index + 1).padStart(2, "0")}</span>
</div>
<div class="project-body">
<div class="project-head">
<div>
<h3>${project.name}</h3>
<p>${project.subtitle}</p>
</div>
<time>${project.period}</time>
</div>
<p class="project-summary">${project.summary}</p>
<ul>${project.modules.map((item) => `<li>${item}</li>`).join("")}</ul>
<div class="tech-row">${project.tech.map((item) => `<span>${item}</span>`).join("")}</div>
</div>
</article>
`,
)
.join("");
}
function renderSkills() {
skillMatrix.innerHTML = skills
.map(
(skill, index) => `
<article class="skill-group reveal" data-drift="${index % 3 === 0 ? -36 : index % 3 === 1 ? 18 : 42}">
<h3>${skill.group}</h3>
<div>${skill.items.map((item) => `<span>${item}</span>`).join("")}</div>
</article>
`,
)
.join("");
}
function renderExperience() {
timeline.innerHTML = experiences
.map(
(item, index) => `
<article class="reveal" data-drift="${index % 2 === 0 ? 38 : -38}">
<div class="timeline-side">
<span class="logo-frame"><img src="${item.logo}" alt="" loading="lazy" decoding="async" /></span>
<time>${item.period}</time>
</div>
<div>
<h3>${item.company}</h3>
<p>${item.role}</p>
<ul>${item.points.map((point) => `<li>${point}</li>`).join("")}</ul>
</div>
</article>
`,
)
.join("");
}
function setupReveal() {
$$(".reveal").forEach((node) => {
node.style.setProperty("--drift", node.dataset.drift || 0);
});
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) entry.target.classList.add("is-visible");
});
},
{ threshold: 0.14 },
);
$$(".reveal").forEach((node) => observer.observe(node));
}
function updateScrollProgress() {
const max = document.documentElement.scrollHeight - window.innerHeight;
const value = max > 0 ? window.scrollY / max : 0;
root.style.setProperty("--scroll-progress", value.toFixed(4));
root.style.setProperty("--bg-grid-x", `${(-220 * value).toFixed(2)}px`);
root.style.setProperty("--bg-grid-y", `${(150 * value).toFixed(2)}px`);
root.style.setProperty("--bg-grid-x-2", `${(140 * value).toFixed(2)}px`);
root.style.setProperty("--bg-grid-y-2", `${(-190 * value).toFixed(2)}px`);
progress.style.transform = `scaleX(${Math.min(1, Math.max(0, value))})`;
const frame = getSceneFrame();
const activeScene = frame.blend > 0.58 ? frame.next : frame.current;
const sections = fluidScenes
.filter((scene) => scene.id !== "top")
.map((scene) => document.getElementById(scene.id))
.filter(Boolean);
const current = activeScene.id === "top" ? null : sections.find((section) => section.id === activeScene.id);
$$(".site-nav a").forEach((link) => link.classList.toggle("active", current && link.getAttribute("href") === `#${current.id}`));
}
function restoreHashPosition() {
const id = decodeURIComponent(location.hash.slice(1));
if (!id) return;
const target = document.getElementById(id);
if (!target) return;
requestAnimationFrame(() => {
window.scrollTo({ top: target.offsetTop, behavior: "auto" });
updateScrollProgress();
});
}
function setupTheme() {
const saved = localStorage.getItem("wy-fluid-theme");
if (saved) document.documentElement.dataset.theme = saved;
themeToggle.addEventListener("click", () => {
const next = document.documentElement.dataset.theme === "dark" ? "light" : "dark";
document.documentElement.dataset.theme = next;
localStorage.setItem("wy-fluid-theme", next);
});
}
function setupTilts() {
$$(".project-card").forEach((card) => {
card.addEventListener("pointerenter", () => card.classList.add("is-hovered"));
card.addEventListener("pointerleave", () => card.classList.remove("is-hovered"));
});
}
function setupFluidScene() {
const canvas = $("#fluid-canvas");
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (!canvas || reduceMotion) return;
const renderer = new THREE.WebGLRenderer({
canvas,
antialias: true,
alpha: true,
preserveDrawingBuffer: true,
powerPreference: "high-performance",
});
renderer.outputColorSpace = THREE.SRGBColorSpace;
renderer.setClearColor(0x000000, 0);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(39, 1, 0.1, 100);
camera.position.set(0, 0, 6.2);
const pointer = new THREE.Vector2(0.18, -0.08);
const pointerTarget = new THREE.Vector2(0.18, -0.08);
const pointerVisual = new THREE.Vector2(58, 46);
const pointerVisualTarget = new THREE.Vector2(58, 46);
const pointerState = {
down: false,
lastX: window.innerWidth * 0.58,
lastY: window.innerHeight * 0.46,
velocity: 0,
targetVelocity: 0,
impact: 0,
pressure: 0,
};
const group = new THREE.Group();
scene.add(group);
const uniforms = {
uTime: { value: 0 },
uPointer: { value: pointer },
uScroll: { value: 0 },
uImpact: { value: 0 },
uFlow: { value: fluidScenes[0].flow },
uTwist: { value: fluidScenes[0].twist },
uColorA: { value: fluidScenes[0].colorObjects[0].clone() },
uColorB: { value: fluidScenes[0].colorObjects[1].clone() },
uColorC: { value: fluidScenes[0].colorObjects[2].clone() },
uTheme: { value: document.documentElement.dataset.theme === "light" ? 1 : 0 },
};
const vertexShader = `
uniform float uTime;
uniform vec2 uPointer;
uniform float uScroll;
uniform float uImpact;
uniform float uFlow;
uniform float uTwist;
varying vec3 vNormal;
varying vec3 vWorld;
varying float vWave;
float ridge(float value) {
return 1.0 - abs(value);
}
void main() {
vec3 p = position;
float t = uTime;
float sceneBreath = sin(uScroll * 6.283 + t * (0.18 + uFlow * 0.22));
float waveA = sin(p.x * (2.15 + uTwist * 1.25) + t * (0.58 + uFlow * 0.82) + sin(p.y * 1.4 + t * 0.32));
float waveB = cos(p.y * (2.6 + uTwist * 1.8) - t * (0.42 + uFlow * 0.74) + p.z * (1.35 + uTwist));
float waveC = sin((p.x + p.y + p.z) * (1.58 + uTwist * 1.1) + t * (0.86 + uFlow * 1.05));
vec2 pointerPlane = uPointer * vec2(1.55, 1.08);
float pointerDistance = distance(p.xy, pointerPlane);
float pointerWave = ridge(sin(pointerDistance * (4.8 + uImpact * 1.2) - t * (1.8 + uImpact * 0.8)));
float pointerPull = smoothstep(1.62, 0.18, pointerDistance);
float scrollWave = sin(uScroll * (5.5 + uFlow * 5.0) + p.z * (2.4 + uTwist * 1.6) + sceneBreath) * (0.045 + uTwist * 0.055);
float displacement = waveA * (0.11 + uFlow * 0.065) + waveB * (0.085 + uTwist * 0.04) + waveC * (0.052 + uFlow * 0.035) + pointerWave * (0.055 + uImpact * 0.11) + pointerPull * (0.05 + uImpact * 0.075) + scrollWave;
p += normal * displacement;
vec2 pointerDir = normalize((p.xy - pointerPlane) + vec2(0.0001, -0.0001));
p.xy += pointerDir * pointerPull * (0.024 + uImpact * 0.055);
p.x += sin(t * (0.34 + uFlow * 0.28) + p.y * (2.1 + uTwist * 0.9)) * (0.04 + uFlow * 0.03);
p.y += cos(t * (0.28 + uFlow * 0.26) + p.x * (1.8 + uTwist * 0.75)) * (0.032 + uTwist * 0.025);
vec4 world = modelMatrix * vec4(p, 1.0);
vNormal = normalize(normalMatrix * normal);
vWorld = world.xyz;
vWave = displacement;
gl_Position = projectionMatrix * viewMatrix * world;
}
`;
const fragmentShader = `
uniform float uTime;
uniform float uTheme;
uniform vec3 uColorA;
uniform vec3 uColorB;
uniform vec3 uColorC;
varying vec3 vNormal;
varying vec3 vWorld;
varying float vWave;
void main() {
vec3 viewDir = normalize(cameraPosition - vWorld);
float fresnel = pow(1.0 - max(dot(normalize(vNormal), viewDir), 0.0), 2.4);
float scan = sin((vWorld.x - vWorld.y) * 8.0 + uTime * 1.8) * 0.5 + 0.5;
vec3 darkA = uColorA;
vec3 darkB = uColorB;
vec3 darkC = uColorC;
vec3 lightA = mix(uColorA, vec3(0.95, 1.0, 0.96), 0.42);
vec3 lightB = mix(uColorB, vec3(0.9, 0.94, 1.0), 0.34);
vec3 lightC = mix(uColorC, vec3(1.0, 0.94, 0.9), 0.3);
vec3 a = mix(darkA, lightA, uTheme);
vec3 b = mix(darkB, lightB, uTheme);
vec3 c = mix(darkC, lightC, uTheme);
vec3 color = mix(a, b, smoothstep(-0.22, 0.38, vWave));
color = mix(color, c, fresnel * 0.86 + scan * 0.12);
color += fresnel * 0.16;
float alpha = 0.56 + fresnel * 0.24 + abs(vWave) * 0.06;
gl_FragColor = vec4(color, alpha);
}
`;
const material = new THREE.ShaderMaterial({
uniforms,
vertexShader,
fragmentShader,
transparent: true,
depthWrite: false,
});
const shellMaterial = new THREE.MeshBasicMaterial({
color: 0xffffff,
transparent: true,
opacity: 0.13,
wireframe: true,
blending: THREE.AdditiveBlending,
});
const blob = new THREE.Mesh(new THREE.SphereGeometry(1.52, 128, 128), material);
const shell = new THREE.Mesh(new THREE.SphereGeometry(1.72, 48, 32), shellMaterial);
shell.rotation.set(0.6, 0.1, -0.4);
group.add(blob, shell);
const ringMaterial = new THREE.MeshBasicMaterial({
color: 0x74a7ff,
transparent: true,
opacity: 0.16,
wireframe: true,
blending: THREE.AdditiveBlending,
});
const rings = Array.from({ length: 4 }, (_, index) => {
const ring = new THREE.Mesh(new THREE.TorusGeometry(1.9 + index * 0.16, 0.008, 8, 180), ringMaterial.clone());
ring.rotation.set(Math.PI / 2.3 + index * 0.18, index * 0.5, index * 0.8);
ring.material.opacity = 0.08 + index * 0.018;
group.add(ring);
return ring;
});
const rayMaterial = new THREE.LineBasicMaterial({
color: 0x56f7c5,
transparent: true,
opacity: 0.025,
blending: THREE.AdditiveBlending,
});
const rayGeometry = new THREE.BufferGeometry();
const rayPositions = new Float32Array(18 * 3);
rayGeometry.setAttribute("position", new THREE.BufferAttribute(rayPositions, 3));
const interactionRay = new THREE.LineSegments(rayGeometry, rayMaterial);
group.add(interactionRay);
const particleCount = 950;
const positions = new Float32Array(particleCount * 3);
const colors = new Float32Array(particleCount * 3);
const colorA = new THREE.Color("#56f7c5");
const colorB = new THREE.Color("#ff5ea8");
const colorC = new THREE.Color("#74a7ff");
for (let index = 0; index < particleCount; index += 1) {
const radius = 2.05 + Math.random() * 2.3;
const theta = Math.random() * Math.PI * 2;
const phi = Math.acos(Math.random() * 2 - 1);
positions[index * 3] = Math.sin(phi) * Math.cos(theta) * radius;
positions[index * 3 + 1] = Math.sin(phi) * Math.sin(theta) * radius;
positions[index * 3 + 2] = Math.cos(phi) * radius;
const color = index % 3 === 0 ? colorA : index % 3 === 1 ? colorB : colorC;
colors[index * 3] = color.r;
colors[index * 3 + 1] = color.g;
colors[index * 3 + 2] = color.b;
}
const particleGeometry = new THREE.BufferGeometry();
particleGeometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
particleGeometry.setAttribute("color", new THREE.BufferAttribute(colors, 3));
const particles = new THREE.Points(
particleGeometry,
new THREE.PointsMaterial({
size: 0.018,
vertexColors: true,
transparent: true,
opacity: 0.78,
depthWrite: false,
blending: THREE.AdditiveBlending,
}),
);
group.add(particles);
let width = 0;
let height = 0;
const basePosition = new THREE.Vector3(fluidScenes[0].desktop.x, fluidScenes[0].desktop.y, fluidScenes[0].desktop.z);
const sceneVisual = {
name: fluidScenes[0].name,
x: basePosition.x,
y: basePosition.y,
z: basePosition.z,
scale: fluidScenes[0].desktop.scale,
flow: fluidScenes[0].flow,
twist: fluidScenes[0].twist,
ring: fluidScenes[0].ring,
particle: fluidScenes[0].particle,
angle: fluidScenes[0].angle,
colors: fluidScenes[0].colorObjects.map((color) => color.clone()),
};
function resize() {
width = window.innerWidth;
height = window.innerHeight;
const dpr = Math.min(window.devicePixelRatio || 1, 2);
renderer.setPixelRatio(dpr);
renderer.setSize(width, height, false);
camera.aspect = width / height;
camera.updateProjectionMatrix();
const compact = width < 760;
const target = mixScene(getSceneFrame(), compact);
basePosition.set(target.x, target.y, target.z);
group.position.copy(basePosition);
group.scale.setScalar(target.scale);
}
function onPointerMove(event) {
const dx = event.clientX - pointerState.lastX;
const dy = event.clientY - pointerState.lastY;
pointerState.lastX = event.clientX;
pointerState.lastY = event.clientY;
const speed = Math.hypot(dx, dy);
pointerState.targetVelocity = Math.min(1, speed / 170);
pointerState.impact = Math.max(pointerState.impact, Math.min(0.26, pointerState.targetVelocity * 0.2));
pointerTarget.x = (event.clientX / window.innerWidth) * 2 - 1;
pointerTarget.y = -((event.clientY / window.innerHeight) * 2 - 1);
pointerVisualTarget.set((event.clientX / window.innerWidth) * 100, (event.clientY / window.innerHeight) * 100);
}
function onPointerDown(event) {
pointerState.down = true;
pointerState.impact = Math.max(pointerState.impact, 0.46);
root.classList.add("is-pointer-down");
onPointerMove(event);
}
function onPointerUp() {
pointerState.down = false;
root.classList.remove("is-pointer-down");
}
window.addEventListener("resize", resize);
window.addEventListener("pointermove", onPointerMove, { passive: true });
window.addEventListener("pointerdown", onPointerDown, { passive: true });
window.addEventListener("pointerup", onPointerUp, { passive: true });
window.addEventListener("pointercancel", onPointerUp, { passive: true });
resize();
const startedAt = performance.now();
function animate() {
const elapsed = (performance.now() - startedAt) / 1000;
const scrollMax = document.documentElement.scrollHeight - window.innerHeight;
const scroll = scrollMax > 0 ? window.scrollY / scrollMax : 0;
const compact = width < 760;
const sceneTarget = mixScene(getSceneFrame(), compact);
sceneVisual.name = sceneTarget.name;
sceneVisual.x = lerp(sceneVisual.x, sceneTarget.x, 0.045);
sceneVisual.y = lerp(sceneVisual.y, sceneTarget.y, 0.045);
sceneVisual.z = lerp(sceneVisual.z, sceneTarget.z, 0.045);
sceneVisual.scale = lerp(sceneVisual.scale, sceneTarget.scale, 0.045);
sceneVisual.flow = lerp(sceneVisual.flow, sceneTarget.flow, 0.04);
sceneVisual.twist = lerp(sceneVisual.twist, sceneTarget.twist, 0.04);
sceneVisual.ring = lerp(sceneVisual.ring, sceneTarget.ring, 0.04);
sceneVisual.particle = lerp(sceneVisual.particle, sceneTarget.particle, 0.04);
sceneVisual.angle = lerp(sceneVisual.angle, sceneTarget.angle, 0.035);
sceneVisual.colors.forEach((color, index) => color.lerp(sceneTarget.colors[index], 0.045));
basePosition.set(sceneVisual.x, sceneVisual.y, sceneVisual.z);
writeSceneCss(sceneVisual, scroll);
pointer.lerp(pointerTarget, 0.024);
pointerVisual.lerp(pointerVisualTarget, 0.07);
root.style.setProperty("--cursor-x", `${pointerVisual.x.toFixed(2)}vw`);
root.style.setProperty("--cursor-y", `${pointerVisual.y.toFixed(2)}vh`);
pointerState.velocity += (pointerState.targetVelocity - pointerState.velocity) * 0.045;
pointerState.targetVelocity *= 0.82;
pointerState.pressure += ((pointerState.down ? 1 : 0) - pointerState.pressure) * 0.075;
pointerState.impact *= pointerState.down ? 0.985 : 0.955;
uniforms.uImpact.value = Math.min(0.72, pointerState.velocity * 0.24 + pointerState.impact * 0.72 + pointerState.pressure * 0.12);
uniforms.uTime.value = elapsed;
uniforms.uScroll.value = scroll;
uniforms.uFlow.value = sceneVisual.flow;
uniforms.uTwist.value = sceneVisual.twist;
uniforms.uColorA.value.copy(sceneVisual.colors[0]);
uniforms.uColorB.value.copy(sceneVisual.colors[1]);
uniforms.uColorC.value.copy(sceneVisual.colors[2]);
uniforms.uTheme.value += ((document.documentElement.dataset.theme === "light" ? 1 : 0) - uniforms.uTheme.value) * 0.08;
group.position.x = basePosition.x + pointer.x * (0.06 + uniforms.uImpact.value * 0.035);
group.position.y = basePosition.y + Math.sin(elapsed * 0.47) * 0.07 + pointer.y * (0.045 + uniforms.uImpact.value * 0.03);
group.position.z = basePosition.z + uniforms.uImpact.value * 0.025;
group.scale.setScalar(sceneVisual.scale + Math.sin(elapsed * 0.18 + scroll * 5.2) * 0.018);
group.rotation.x = Math.sin(elapsed * 0.28) * 0.14 + pointer.y * (0.11 + uniforms.uImpact.value * 0.055);
group.rotation.y = elapsed * (0.055 + sceneVisual.flow * 0.065 + uniforms.uImpact.value * 0.018) + pointer.x * (0.18 + uniforms.uImpact.value * 0.075) + scroll * (0.5 + sceneVisual.twist * 0.5);
group.rotation.z = Math.cos(elapsed * 0.18) * 0.055 + pointer.x * pointer.y * 0.055 + sceneVisual.twist * 0.06;
blob.scale.setScalar(1 + Math.sin(elapsed * 0.62) * 0.025 + uniforms.uImpact.value * 0.014);
shell.rotation.y -= 0.0014;
shell.rotation.z += 0.001;
particles.material.opacity = 0.42 + sceneVisual.particle * 0.36;
particles.rotation.y -= 0.00035 + sceneVisual.flow * 0.00045 + uniforms.uImpact.value * 0.00035;
particles.rotation.x = Math.sin(elapsed * (0.14 + sceneVisual.flow * 0.12)) * (0.055 + sceneVisual.twist * 0.06);
rings.forEach((ring, index) => {
ring.material.opacity = (0.04 + index * 0.012) * (0.75 + sceneVisual.ring * 0.7);
ring.rotation.z += 0.0007 + sceneVisual.ring * 0.00075 + index * 0.00035 + uniforms.uImpact.value * 0.00055;
ring.rotation.x += Math.sin(elapsed * 0.24 + index) * 0.00048 + pointer.y * 0.00022;
});
for (let index = 0; index < 9; index += 1) {
const offset = index * 6;
const angle = elapsed * (0.28 + sceneVisual.flow * 0.22 + index * 0.025) + index * 0.7 + scroll * sceneVisual.twist * 2.4;
const length = 0.42 + sceneVisual.ring * 0.18 + uniforms.uImpact.value * 0.72;
rayPositions[offset] = pointer.x * 0.82;
rayPositions[offset + 1] = pointer.y * 0.58;
rayPositions[offset + 2] = 0.38;
rayPositions[offset + 3] = pointer.x * 0.82 + Math.cos(angle) * length;
rayPositions[offset + 4] = pointer.y * 0.58 + Math.sin(angle) * length * 0.42;
rayPositions[offset + 5] = 0.12 + Math.sin(angle * 0.7) * 0.28;
}
rayGeometry.attributes.position.needsUpdate = true;
rayMaterial.opacity = 0.025 + uniforms.uImpact.value * 0.13;
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
}
renderMetrics();
renderFocusAreas();
renderResumeSignals();
renderProjects();
renderSkills();
renderExperience();
setupTheme();
setupReveal();
setupTilts();
setupFluidScene();
updateScrollProgress();
restoreHashPosition();
window.addEventListener("scroll", () => requestAnimationFrame(updateScrollProgress), { passive: true });
window.addEventListener("hashchange", () => {
restoreHashPosition();
updateScrollProgress();
});
File diff suppressed because it is too large Load Diff
+173
View File
@@ -0,0 +1,173 @@
export const metrics = [
{ value: "7 年", label: "Web 与跨端前端经验" },
{ value: "2 条", label: "近一年参与的产品线" },
{ value: "SSE", label: "对话流、思考态、消息重试" },
{ value: "12 个", label: "微前端拆分基础项目" },
{ value: "30%", label: "消息渲染链路优化结果" },
{ value: "6000+", label: "门店业务系统服务规模" },
];
export const focusAreas = [
{
title: "对话链路",
summary: "做过流式消息、思考态、来源引用、历史同步和失败重试,重点是让对话过程稳定、可恢复。",
points: ["SSE", "Markdown/LaTeX", "来源引用", "消息重试"],
},
{
title: "控制台业务",
summary: "参与过 API Key、账单、组织权限、额度、交易筛选和导出,知道复杂后台最怕状态不清和边界不稳。",
points: ["API Key", "Billing", "RBAC", "Excel 导出"],
},
{
title: "跨端内容流",
summary: "在 Expo / React Native 项目里做过新闻流、详情页、瀑布流、图片组件和游客转登录数据处理。",
points: ["Expo", "瀑布流", "Smart Image", "游客绑定"],
},
{
title: "工程习惯",
summary: "经历过微前端拆分、跨项目公共包、i18n 协作、监控接入和代码审查,能把模块交付和团队维护放在一起考虑。",
points: ["Qiankun", "Monorepo", "i18n", "Code Review"],
},
];
export const resumeSignals = [
"前端经验覆盖 PC 管理后台、小程序、H5、React Native 和微前端,最近一年主要在 AI 产品团队做业务前端。",
"SeaBuzz 侧更偏用户体验:对话、内容流、新闻详情、游客数据、分享和反馈链路。",
"SeaCloud / Vtrix 侧更偏控制台:Pricing、Billing、API Keys、组织权限、交易筛选和分销配置。",
"技术栈以 React / Next.js / React Native / TypeScript 为主,也有 Vue、UniApp 和 Qiankun 项目经验。",
];
export const skills = [
{
group: "对话与内容",
items: ["SSE 流式通信", "打字机响应", "Markdown/LaTeX", "来源引用", "对话历史", "新闻详情"],
},
{
group: "控制台",
items: ["Pricing", "Billing", "API Keys", "组织权限", "额度限制", "交易筛选", "Excel 导出"],
},
{
group: "跨端体验",
items: ["React Native", "Expo Router", "NativeWind", "游客转登录", "只读分享", "Smart Image"],
},
{
group: "Web 主栈",
items: ["Next.js", "React", "Vue 3", "TypeScript", "Tailwind CSS", "Zustand", "Alova"],
},
{
group: "小程序与 H5",
items: ["UniApp", "微信小程序", "飞书小程序", "H5", "摄像头扫码", "低功耗蓝牙"],
},
{
group: "工程协作",
items: ["pnpm Monorepo", "Git Submodules", "Qiankun", "Lerna", "i18n 同步", "Code Review"],
},
{
group: "工程化与质量",
items: ["ARMS 监控", "CDN 优化", "Playwright", "Git Flow", "分支规范", "新人培训"],
},
];
export const projects = [
{
id: "vtrix",
name: "SeaCloud / Vtrix",
logo: "/logos/vtrix.png",
period: "2025.09 - 至今",
subtitle: "模型服务平台控制台",
summary:
"参与模型服务平台的前端建设,主要处理 Pricing、Billing、API Keys、组织权限、额度和分销相关页面。项目复杂度不在单个页面,而在状态、权限和账务边界。",
modules: [
"建设分销控制台:客户邀请、折扣模板、额度分配、销售配置与利润率计算。",
"封装 useCurrency Hook,贯穿 Pricing、Billing、API Keys 等模型调用和费用模块。",
"落地组织角色权限、成员配额、支出限额、账单报表、交易筛选与 Excel 导出。",
"参与 i18n Submodule 架构迁移、企业微信翻译同步和通用表格/筛选器/分页组件增强。",
],
tech: ["Next.js 16", "React 19", "TypeScript", "Tailwind CSS", "Zustand", "Alova", "Radix UI", "next-intl"],
},
{
id: "seabuzz",
name: "SeaBuzz",
logo: "/logos/seabuzz.webp",
period: "2025.05 - 至今",
subtitle: "资讯、搜索与对话应用",
summary:
"基于 Expo 的跨端应用,覆盖 iOS、Android、Web。我的工作集中在对话链路、内容流、登录绑定、分享和公共包沉淀。",
modules: [
"实现对话链路:SSE 流式聊天、打字机渲染、Markdown/LaTeX、思考动画、来源引用、消息重写与历史同步。",
"处理游客聊天记录本地存储、登录后自动绑定、分享链接只读模式和反馈机制。",
"交付 Discover 发现页、新闻详情、瀑布流动态布局、骨架屏、Smart Image 与富文本章节渲染。",
"集成 Google、Facebook、Discord、Email 登录与 SeaArt Auth SDK。",
"封装 Monorepo 公共包体系,覆盖 API、数据模型、UI 组件、状态、OTA 热更新与代码签名。",
],
tech: ["React Native", "Expo 53", "Expo Router", "Zustand", "NativeWind", "SSE", "i18next", "Adyen"],
},
{
id: "bawang",
name: "霸王功夫",
logo: "/logos/chagee.png",
period: "2024.03 - 2025.03",
subtitle: "门店、食安与供应链系统",
summary:
"服务全球 6000+ 门店及运营伙伴,覆盖门店运营、食品安全、供应链协同等核心业务。",
modules: [
"搭建小程序报损模块,调用摄像头扫码并兼容微信小程序与飞书 H5。",
"实现食安管理模块,通过低频蓝牙连接 TSPL 指令集打印机,覆盖国内、东南亚、北美三套业务逻辑。",
"交付多门店经营状态移动看板,支持区域、时间、指标筛选。",
"负责采购、供应商合同、供应商结算等供应链系统,对接云厉、费控等外部系统。",
],
tech: ["React 18", "Vue 3", "UniApp", "MobX", "Pinia", "Element Plus", "Ant Design"],
},
{
id: "jingyingbang",
name: "经营帮平台 + 经营帮拉新",
logo: "/logos/jingyingbang.png",
period: "2022.04 - 2024.03",
subtitle: "微前端平台与小程序",
summary:
"基于信息化设计理念和区块链技术的工业互联网平台,为企业和个人提供数字化运营服务。",
modules: [
"参与单体前端到 Qiankun 微前端拆分,共拆分出 12 个基础项目。",
"负责商管、门户、经营帮小程序、H5、Admin 后台核心业务交付。",
"引入华为云 OBS 直传,降低请求链路性能浪费。",
"开发内部 Chrome 插件 zjkj-decryption,提升数据解析效率。",
],
tech: ["Vue", "Qiankun", "Element UI", "华为云 OBS", "百度地图", "高德地图", "UniApp"],
},
];
export const experiences = [
{
company: "成都海艺互娱科技有限公司",
logo: "/logos/seaart.webp",
role: "React Native 开发工程师",
period: "2025.05 - 至今",
points: [
"参与 SeaBuzz、SeaCloud / Vtrix 等项目,主要负责 React Native 跨端页面和 Web 控制台模块。",
"负责对话流、内容流、分享、游客数据绑定、账单、API Keys、组织权限等业务页面开发。",
"优化消息渲染链路和跨端公共包复用,消息渲染性能提升约 30%。",
],
},
{
company: "四川茶姬企业管理有限公司",
logo: "/logos/chagee.png",
role: "高级 Web 前端开发",
period: "2024.03 - 2025.03",
points: [
"主导供应链管理系统前端开发,实现采购、仓储、配送等核心模块功能。",
"独立负责门店报损与食安核心功能搭建及迭代。",
"完成前端监控体系引入、CDN 静态资源优化、代码审查机制与 Git 操作规范建设。",
],
},
{
company: "中钧科技有限公司四川分公司",
logo: "/logos/zhongjun.png",
role: "前端开发组长",
period: "2022.04 - 2024.03",
points: [
"负责经营帮 PC 端微前端、门户和商管核心模块开发,完成 UI 还原、接口联调与性能优化。",
"从零搭建新 E 畅行小程序基础框架,制定技术方案并完成全流程开发。",
"设计 Git Flow、代码审查与分支管理机制,主导新人培训与项目分工协调。",
],
},
];
+1010
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-3
View File
@@ -1,3 +0,0 @@
{
"extends": "astro/tsconfigs/strict"
}
+13
View File
@@ -0,0 +1,13 @@
import { defineConfig } from "vite";
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
three: ["three"],
},
},
},
},
});