Files
router-studio/web/src/app/cv/index.tsx
2026-01-15 12:32:07 +08:00

174 lines
4.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useState, useEffect } from 'react'
import MDEditor from '@uiw/react-md-editor'
import { ToastContainer, Slide } from 'react-toastify'
import { Save, Download, Printer, Eye, Edit, RotateCcw } from 'lucide-react'
import 'github-markdown-css/github-markdown-light.css'
import './index.css'
const defaultResume = `# 张三
**前端开发工程师** | 北京 | zhangsan@example.com | 138-0000-0000
## 个人简介
热爱前端开发拥有5年React和Vue开发经验。专注于构建高性能、可维护的Web应用。
## 工作经历
### 高级前端工程师 | ABC科技有限公司
*2021年6月 - 至今*
- 负责公司核心产品的前端架构设计和开发
- 使用React和TypeScript构建企业级管理系统
- 优化前端性能页面加载速度提升40%
- 带领5人前端团队制定代码规范和最佳实践
### 前端开发工程师 | XYZ互联网公司
*2019年3月 - 2021年5月*
- 参与电商平台的前端开发
- 使用Vue.js开发响应式Web应用
- 与后端团队协作实现RESTful API对接
## 技能
- **前端框架**: React, Vue.js, Next.js, Astro
- **编程语言**: TypeScript, JavaScript, HTML5, CSS3
- **工具**: Git, Webpack, Vite, Docker
- **其他**: Node.js, GraphQL, Tailwind CSS
## 教育背景
### 计算机科学与技术 | 本科
*北京某大学 | 2015年9月 - 2019年6月*
- 主修课程:数据结构、算法、计算机网络、数据库原理
- 优秀毕业生GPA 3.8/4.0
## 项目经验
### 企业级管理系统
*技术栈: React, TypeScript, Ant Design*
- 设计并实现模块化权限管理系统
- 开发数据可视化大屏,支持实时数据展示
- 编写单元测试代码覆盖率达到85%
### 电商平台前端
*技术栈: Vue.js, Vuex, Element UI*
- 实现购物车、订单管理等核心功能
- 优化首屏加载时间LCP从2.5s降至1.2s
- 响应式设计,完美支持移动端访问
## 语言能力
- 英语CET-6能够阅读英文技术文档
- 普通话:母语
`
export const AppProvider = () => {
return (
<div>
<App />
<ToastContainer transition={Slide} />
</div>
)
}
export const App = () => {
const [markdown, setMarkdown] = useState<string | undefined>(defaultResume)
const [isPreviewMode, setIsPreviewMode] = useState(false)
// 从 localStorage 加载保存的简历
useEffect(() => {
const saved = localStorage.getItem('cv-content')
if (saved) {
setMarkdown(saved)
}
}, [])
// 保存到 localStorage
const handleSave = () => {
if (markdown) {
localStorage.setItem('cv-content', markdown)
alert('简历已保存!')
}
}
// 导出为 Markdown 文件
const handleExport = () => {
if (markdown) {
const blob = new Blob([markdown], { type: 'text/markdown' })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = 'resume.md'
a.click()
URL.revokeObjectURL(url)
}
}
// 打印简历
const handlePrint = () => {
window.print()
}
// 重置为默认模板
const handleReset = () => {
if (confirm('确定要重置为默认模板吗?当前内容将丢失。')) {
setMarkdown(defaultResume)
localStorage.removeItem('cv-content')
}
}
return (
<div className="cv-app">
<header className="cv-header">
<h1>📄 </h1>
<div className="cv-actions">
<button onClick={handleSave} className="cv-btn cv-btn-primary">
<Save size={16} />
<span></span>
</button>
<button onClick={handleExport} className="cv-btn cv-btn-secondary">
<Download size={16} />
<span> MD</span>
</button>
<button onClick={handlePrint} className="cv-btn cv-btn-secondary">
<Printer size={16} />
<span></span>
</button>
<button onClick={() => setIsPreviewMode(!isPreviewMode)} className="cv-btn cv-btn-secondary">
{isPreviewMode ? <Edit size={16} /> : <Eye size={16} />}
<span>{isPreviewMode ? '编辑' : '预览'}</span>
</button>
<button onClick={handleReset} className="cv-btn cv-btn-danger">
<RotateCcw size={16} />
<span></span>
</button>
</div>
</header>
{isPreviewMode ? (
<div className="cv-preview-container">
<div className="cv-preview markdown-body">
<MDEditor.Markdown source={markdown} />
</div>
</div>
) : (
<div className="cv-editor-container" data-color-mode="light">
<MDEditor
value={markdown}
onChange={setMarkdown}
height={800}
preview="edit"
textareaProps={{
placeholder: '在这里输入你的简历内容(支持 Markdown 格式)...'
}}
/>
</div>
)}
</div>
)
}