174 lines
4.9 KiB
TypeScript
174 lines
4.9 KiB
TypeScript
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>
|
||
)
|
||
} |