导航菜单

简历组件开发

本章将指导你开发简历页面的各个组件。我们将采用组件化的方式,让简历结构清晰、易于维护。

组件概览

我们将创建以下组件:

组件用途
Card.vue卡片容器,用于包裹各个内容区块
Badge.vue标签组件,用于显示联系方式、技能标签等
IconLink.vue图标链接组件,用于显示带图标的链接
TimelineItem.vue时间线项组件,用于显示工作经历、教育经历

Card 组件

创建 src/components/Card.vue

<template>
  <div class="card">
    <slot />
  </div>
</template>

<style scoped>
.card {
  background: #fff;
  border-radius: 12px;
  padding: 16px 20px;
  margin-bottom: 12px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
  border: 1px solid #e5e7eb;
}
</style>

Badge 组件

创建 src/components/Badge.vue

<template>
  <span class="badge">
    <slot />
  </span>
</template>

<style scoped>
.badge {
  display: inline-block;
  padding: 4px 10px;
  border-radius: 6px;
  background: #f3f4f6;
  color: #374151;
  font-size: 12px;
  line-height: 1.4;
}

.badge a {
  color: inherit;
  text-decoration: none;
}

.badge a:hover {
  text-decoration: underline;
}
</style>

创建 src/components/IconLink.vue

<template>
  <a
    :href="href"
    :title="title"
    target="_blank"
    rel="noopener noreferrer"
    class="icon-link"
  >
    <span class="sr-only">{{ title }}</span>
    <svg
      xmlns="http://www.w3.org/2000/svg"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
    >
      <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
      <polyline points="15 3 21 3 21 9" />
      <line x1="10" y1="14" x2="21" y2="3" />
    </svg>
  </a>
</template>

<script setup lang="ts">
defineProps<{
  href: string
  title: string
}>()
</script>

<style scoped>
.icon-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  margin-left: 6px;
  border-radius: 6px;
  color: #2563eb;
  vertical-align: middle;
}

.icon-link:hover {
  background: rgba(37, 99, 235, 0.10);
  text-decoration: none;
}

.icon-link svg {
  width: 16px;
  height: 16px;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
</style>

TimelineItem 组件

创建 src/components/TimelineItem.vue

<template>
  <div class="timeline-item">
    <slot />
  </div>
</template>

<style scoped>
.timeline-item {
  position: relative;
  padding-bottom: 12px;
  margin-bottom: 12px;
}

.timeline-item::before {
  content: '';
  position: absolute;
  left: -1.15rem;
  top: 6px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #f97316;
  border: 2px solid #fff;
  box-shadow: 0 0 0 2px #f97316;
}
</style>

App.vue 主组件

现在我们将所有组件组合起来,创建 src/App.vue

<template>
  <div class="page">
    <Card>
      <div class="card-header-row">
        <div class="header-title-row">
          <h2>你的姓名</h2>
          <IconLink href="https://github.com/yourusername" title="GitHub" />
        </div>
      </div>
      <div class="badge-row">
        <Badge>手机:13800138000</Badge>
        <Badge>邮箱:your@email.com</Badge>
        <Badge>
          <a href="https://yourwebsite.com" target="_blank" rel="noopener noreferrer">
            个人网站
          </a>
        </Badge>
      </div>
      <div class="intro-row">
        <div class="intro-column">
          <h3>个人简介</h3>
          <ul class="list">
            <li>5 年前端开发经验</li>
            <li>熟悉 Vue、React 等主流框架</li>
            <li>热爱开源,持续学习</li>
          </ul>
        </div>
      </div>
    </Card>

    <Card>
      <h3>工作经历</h3>
      <div class="timeline">
        <TimelineItem>
          <p class="timeline-text">
            <span class="timeline-year">2020-至今</span> 某科技公司 - 高级前端工程师
          </p>
        </TimelineItem>
        <TimelineItem>
          <p class="timeline-text">
            <span class="timeline-year">2018-2020</span> 某互联网公司 - 前端工程师
          </p>
        </TimelineItem>
      </div>
    </Card>

    <Card>
      <h3>教育经历</h3>
      <div class="timeline">
        <TimelineItem>
          <p class="timeline-text">
            <span class="timeline-year">2014-2018</span> 某大学 - 计算机科学与技术
          </p>
        </TimelineItem>
      </div>
    </Card>
  </div>
</template>

<script setup lang="ts">
import IconLink from './components/IconLink.vue'
import Badge from './components/Badge.vue'
import TimelineItem from './components/TimelineItem.vue'
import Card from './components/Card.vue'
</script>

<style>
@page {
  size: A4;
  margin: 20mm;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  margin: 0;
  padding: 0;
  width: 170mm;
  max-width: 100%;
  margin: 0 auto;
  box-sizing: border-box;
}

.page {
  width: 100%;
  max-width: 170mm;
  margin: 0 auto;
  padding: 0;
  box-sizing: border-box;
}

.card h2 {
  margin: 0 0 2px;
  font-size: 18px;
  color: #0f172a;
}

.card h3 {
  margin: 0 0 8px;
  font-size: 14px;
  color: #1e293b;
}

.header-title-row {
  display: flex;
  align-items: center;
  gap: 4px;
}

.card-header-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 6px;
}

.badge-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 6px;
}

.intro-row {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  margin-top: 12px;
}

.intro-column {
  flex: 1;
  min-width: 0;
  padding: 10px 12px;
  border-radius: 8px;
  background: #eef2ff;
  border: 1px solid #e5e7eb;
}

.list {
  margin: 0;
  padding-left: 18px;
  color: #475569;
  font-size: 13px;
  line-height: 1.6;
}

.list li {
  margin-bottom: 4px;
}

.timeline {
  position: relative;
  padding-left: 1.5rem;
}

.timeline::before {
  content: '';
  position: absolute;
  left: 0.35rem;
  top: 0;
  bottom: 0;
  width: 2px;
  background: linear-gradient(to bottom, #f97316, #ef4444);
}

.timeline-year {
  font-weight: 600;
  color: #374151;
  margin-bottom: 0.25rem;
  font-size: 14px;
}

.timeline-text {
  margin: 0;
  color: #6b7280;
  font-size: 13px;
}

@media print {
  body {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
}
</style>

运行预览

启动开发服务器:

pnpm dev

在浏览器中打开 http://localhost:5173,你应该能看到一个简洁的简历页面。

下一步

现在我们已经完成了简历组件的开发,接下来我们将在 PDF 生成 章节中学习如何将简历导出为 PDF。

搜索