导航菜单

Resume Components

This chapter will guide you through developing the resume page components. We will use a component-based approach to make the resume structure clear and easy to maintain.

Component Overview

We will create the following components:

ComponentPurpose
Card.vueCard container for wrapping content sections
Badge.vueBadge component for contact info, skill tags
IconLink.vueIcon link component for links with icons
TimelineItem.vueTimeline item for work experience, education

Card Component

Create 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 Component

Create 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>

Create 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 Component

Create 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 Main Component

Now let’s combine all components to create src/App.vue:

<template>
  <div class="page">
    <Card>
      <div class="card-header-row">
        <div class="header-title-row">
          <h2>Your Name</h2>
          <IconLink href="https://github.com/yourusername" title="GitHub" />
        </div>
      </div>
      <div class="badge-row">
        <Badge>Phone: +1 234 567 8900</Badge>
        <Badge>Email: your@email.com</Badge>
        <Badge>
          <a href="https://yourwebsite.com" target="_blank" rel="noopener noreferrer">
            Website
          </a>
        </Badge>
      </div>
      <div class="intro-row">
        <div class="intro-column">
          <h3>About Me</h3>
          <ul class="list">
            <li>5 years of frontend development experience</li>
            <li>Proficient in Vue, React and other frameworks</li>
            <li>Open source enthusiast, continuous learner</li>
          </ul>
        </div>
      </div>
    </Card>

    <Card>
      <h3>Work Experience</h3>
      <div class="timeline">
        <TimelineItem>
          <p class="timeline-text">
            <span class="timeline-year">2020-Present</span> Tech Company - Senior Frontend Engineer
          </p>
        </TimelineItem>
        <TimelineItem>
          <p class="timeline-text">
            <span class="timeline-year">2018-2020</span> Internet Company - Frontend Engineer
          </p>
        </TimelineItem>
      </div>
    </Card>

    <Card>
      <h3>Education</h3>
      <div class="timeline">
        <TimelineItem>
          <p class="timeline-text">
            <span class="timeline-year">2014-2018</span> University - Computer Science
          </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>

Run Preview

Start development server:

pnpm dev

Open http://localhost:5173 in your browser, you should see a clean resume page.

Next Step

Now we have completed the resume component development, next we will learn how to export the resume to PDF in the PDF Generation chapter.

搜索