Browse Source

decs jianli

0235713 22 hours ago
parent
commit
6113604854

File diff suppressed because it is too large
+ 780 - 269
demo/H.html


+ 10 - 12
interview-web/src/modules/interview/mobile/mobile.routes.ts

@@ -8,10 +8,10 @@ export const MOBILE_ROUTES: Routes = [
         path: 'home',
         path: 'home',
         loadComponent: () => import('./page-home/page-home').then(m => m.PageHome)
         loadComponent: () => import('./page-home/page-home').then(m => m.PageHome)
       },
       },
-      /*{
-        path: 'cart',
-        loadComponent: () => import('./page-cart/page-cart').then(m => m.PageCart)
-      },*/
+      {
+        path: 'resume',
+        loadComponent: () => import('./page-resume/page-resume').then(m => m.PageResume)
+      },
       {
       {
         path: 'mine',
         path: 'mine',
         loadComponent: () => import('./page-mine/page-mine').then(m => m.PageMine)
         loadComponent: () => import('./page-mine/page-mine').then(m => m.PageMine)
@@ -22,19 +22,17 @@ export const MOBILE_ROUTES: Routes = [
         pathMatch: 'full'
         pathMatch: 'full'
       }
       }
     ]
     ]
-    
   },
   },
   {
   {
-   path: 'interview/mobile/page-interview', 
-   loadComponent: () => import('./page-interview/page-interview').then(m => m.PageInterview)
+    path: 'interview/mobile/page-interview', 
+    loadComponent: () => import('./page-interview/page-interview').then(m => m.PageInterview)
   },
   },
   {
   {
-   path: 'interview/mobile/page-mine', 
-   loadComponent: () => import('./page-mine/page-mine').then(m => m.PageMine)
+    path: 'interview/mobile/page-mine', 
+    loadComponent: () => import('./page-mine/page-mine').then(m => m.PageMine)
   },
   },
   {
   {
-    path:'interview/mobile/page-job-hunting',
+    path: 'interview/mobile/page-job-hunting',
     loadComponent: () => import('./page-job-hunting/page-job-hunting').then(m => m.PageJobHunting)
     loadComponent: () => import('./page-job-hunting/page-job-hunting').then(m => m.PageJobHunting)
   }
   }
-];
-
+];

+ 40 - 29
interview-web/src/modules/interview/mobile/nav-mobile-tabs/nav-mobile-tabs.ts

@@ -6,7 +6,8 @@ import {
   faHome,
   faHome,
   faSearch,
   faSearch,
   faComments,
   faComments,
-  faUser
+  faUser,
+  faFileAlt
 } from '@fortawesome/free-solid-svg-icons';
 } from '@fortawesome/free-solid-svg-icons';
 
 
 @Component({
 @Component({
@@ -22,36 +23,43 @@ export class NavMobileTabs {
     home: faHome,
     home: faHome,
     search: faSearch,
     search: faSearch,
     interview: faComments,
     interview: faComments,
-    mine: faUser
+    mine: faUser,
+    resume:faFileAlt
   };
   };
 
 
   // 导航项配置
   // 导航项配置
-  navItems = [
-    {
-      icon: this.icons.home,
-      label: '首页',
-      route: '/home',
-      active: false
-    },
-    {
-      icon: this.icons.search,
-      label: '职位',
-      route: '/interview/mobile/page-job-hunting',
-      active: false
-    },
-    {
-      icon: this.icons.interview,
-      label: '面试',
-      route: '/interview/mobile/page-interview',
-      active: false
-    },
-    {
-      icon: this.icons.mine,
-      label: '我的',
-      route: '/interview/mobile/page-mine',
-      active: false
-    }
-  ];
+ navItems = [
+  {
+    icon: this.icons.home,
+    label: '首页',
+    route: '/home',
+    active: false
+  },
+  {
+    icon: this.icons.search,
+    label: '职位',
+    route: '/interview/mobile/page-job-hunting',
+    active: false
+  },
+  {
+    icon: this.icons.resume,
+    label: '简历',
+    route: '/resume',
+    active: false
+  },
+  {
+    icon: this.icons.interview,
+    label: '面试',
+    route: '/interview/mobile/page-interview',
+    active: false
+  },
+  {
+    icon: this.icons.mine,
+    label: '我的',
+    route: '/interview/mobile/page-mine',
+    active: false
+  }
+];
 
 
   constructor(private router: Router) {
   constructor(private router: Router) {
     // 监听路由变化更新活动状态
     // 监听路由变化更新活动状态
@@ -67,4 +75,7 @@ export class NavMobileTabs {
   navigateTo(route: string) {
   navigateTo(route: string) {
     this.router.navigate([route]);
     this.router.navigate([route]);
   }
   }
-}
+
+  
+
+}

+ 40 - 0
interview-web/src/modules/interview/mobile/page-applications/page-applications.html

@@ -0,0 +1,40 @@
+<div class="applications-container">
+  <h1>我的申请</h1>
+  
+  @if (applications.length === 0) {
+    <div class="empty-state">
+      <p>您还没有申请任何职位</p>
+      <button class="btn primary" (click)="router.navigate(['/interview/mobile/page-job-hunting'])">
+        去浏览职位
+      </button>
+    </div>
+  } @else {
+    <div class="applications-list">
+      @for (app of applications; track app.jobId) {
+        <div class="application-card">
+          <div class="job-info">
+            <h3>{{ getJobById(app.jobId)?.title || '未知职位' }}</h3>
+            <p>{{ getJobById(app.jobId)?.company || '未知公司' }}</p>
+          </div>
+          <div class="application-status" [ngClass]="{
+            'pending': app.status === '已提交',
+            'accepted': app.status === '已通过',
+            'rejected': app.status === '已拒绝'
+          }">
+            <fa-icon [icon]="
+              app.status === '已提交' ? icons.pending :
+              app.status === '已通过' ? icons.accepted : icons.rejected
+            "></fa-icon>
+            {{ app.status }}
+          </div>
+          <div class="application-date">
+            {{ app.date | date: 'yyyy-MM-dd' }}
+          </div>
+          <button class="btn view-btn" (click)="viewJobDetails(app.jobId)">
+            查看职位
+          </button>
+        </div>
+      }
+    </div>
+  }
+</div>

+ 117 - 0
interview-web/src/modules/interview/mobile/page-applications/page-applications.scss

@@ -0,0 +1,117 @@
+.applications-container {
+  max-width: 800px;
+  margin: 0 auto;
+  padding: 20px;
+  
+  h1 {
+    color: #2A5CAA;
+    margin-bottom: 20px;
+  }
+}
+
+.empty-state {
+  text-align: center;
+  padding: 40px 20px;
+  background-color: white;
+  border-radius: 8px;
+  box-shadow: 0 2px 5px rgba(0,0,0,0.1);
+  
+  p {
+    margin-bottom: 20px;
+    color: #4A5568;
+  }
+}
+
+.applications-list {
+  display: flex;
+  flex-direction: column;
+  gap: 15px;
+}
+
+.application-card {
+  background-color: white;
+  border-radius: 8px;
+  padding: 15px;
+  box-shadow: 0 2px 5px rgba(0,0,0,0.1);
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  gap: 15px;
+  
+  .job-info {
+    flex: 1;
+    min-width: 200px;
+    
+    h3 {
+      color: #2D3748;
+      margin-bottom: 5px;
+    }
+    
+    p {
+      color: #718096;
+      font-size: 14px;
+    }
+  }
+  
+  .application-status {
+    padding: 5px 10px;
+    border-radius: 20px;
+    font-size: 14px;
+    display: flex;
+    align-items: center;
+    gap: 5px;
+    
+    &.pending {
+      background-color: #F6E05E;
+      color: #975A16;
+    }
+    
+    &.accepted {
+      background-color: #C6F6D5;
+      color: #22543D;
+    }
+    
+    &.rejected {
+      background-color: #FED7D7;
+      color: #9B2C2C;
+    }
+  }
+  
+  .application-date {
+    color: #718096;
+    font-size: 14px;
+  }
+  
+  .view-btn {
+    background-color: #EDF2F7;
+    color: #2A5CAA;
+    border: none;
+    padding: 8px 15px;
+    border-radius: 5px;
+    cursor: pointer;
+    font-size: 14px;
+  }
+}
+
+.btn {
+  padding: 10px 20px;
+  border-radius: 5px;
+  border: none;
+  cursor: pointer;
+  
+  &.primary {
+    background-color: #2A5CAA;
+    color: white;
+  }
+}
+
+@media (max-width: 600px) {
+  .application-card {
+    flex-direction: column;
+    align-items: flex-start;
+    
+    .job-info {
+      width: 100%;
+    }
+  }
+}

+ 23 - 0
interview-web/src/modules/interview/mobile/page-applications/page-applications.spec.ts

@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PageApplications } from './page-applications';
+
+describe('PageApplications', () => {
+  let component: PageApplications;
+  let fixture: ComponentFixture<PageApplications>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      imports: [PageApplications]
+    })
+    .compileComponents();
+
+    fixture = TestBed.createComponent(PageApplications);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 48 - 0
interview-web/src/modules/interview/mobile/page-applications/page-applications.ts

@@ -0,0 +1,48 @@
+import { Component } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { Router } from '@angular/router';
+import { FaIconComponent } from '@fortawesome/angular-fontawesome';
+import { 
+  faBriefcase, faClock, faCheckCircle, faTimesCircle 
+} from '@fortawesome/free-solid-svg-icons';
+import { DatePipe } from '@angular/common';
+import { NgClass } from '@angular/common';
+
+@Component({
+  selector: 'app-page-applications',
+  standalone: true,
+  imports: [CommonModule, FaIconComponent, DatePipe, NgClass], // 添加必要导入
+  templateUrl: './page-applications.html',
+  styleUrls: ['./page-applications.scss']
+})
+export class PageApplications {
+  // 定义icons对象
+  icons = {
+    job: faBriefcase,
+    pending: faClock,
+    accepted: faCheckCircle,
+    rejected: faTimesCircle
+  };
+
+  applications: any[] = [];
+  jobs: any[] = [];
+
+  constructor(public router: Router) { // 注入Router
+    this.loadData();
+  }
+
+  loadData() {
+    this.applications = JSON.parse(localStorage.getItem('jobApplications') || '[]');
+    this.jobs = JSON.parse(localStorage.getItem('jobs') || '[]');
+  }
+
+  getJobById(jobId: number) {
+    return this.jobs.find(job => job.id === jobId) || {};
+  }
+
+  viewJobDetails(jobId: number) {
+    this.router.navigate(['/interview/mobile/page-job-hunting'], {
+      state: { jobId }
+    });
+  }
+}

+ 38 - 3
interview-web/src/modules/interview/mobile/page-job-hunting/page-job-hunting.ts

@@ -1,6 +1,7 @@
 import { Component, OnInit } from '@angular/core';
 import { Component, OnInit } from '@angular/core';
 import { CommonModule } from '@angular/common';
 import { CommonModule } from '@angular/common';
 import { FormsModule } from '@angular/forms';
 import { FormsModule } from '@angular/forms';
+import { Router } from '@angular/router';
 interface Job {
 interface Job {
   id: number;
   id: number;
   title: string;
   title: string;
@@ -11,6 +12,8 @@ interface Job {
   description: string;
   description: string;
 }
 }
 
 
+
+
 @Component({
 @Component({
   selector: 'app-page-job-hunting',
   selector: 'app-page-job-hunting',
   standalone: true,
   standalone: true,
@@ -23,6 +26,8 @@ export class PageJobHunting {
   selectedType: string = '';
   selectedType: string = '';
   selectedLocation: string = '';
   selectedLocation: string = '';
 
 
+  constructor(private router: Router) {}
+
   jobs: Job[] = [
   jobs: Job[] = [
     {
     {
       id: 1,
       id: 1,
@@ -135,9 +140,39 @@ export class PageJobHunting {
   }
   }
 
 
   applyForJob(jobId: number): void {
   applyForJob(jobId: number): void {
-    // 这里可以添加申请职位的逻辑
-    console.log(`Applying for job ID: ${jobId}`);
-    alert(`已申请职位ID: ${jobId}`);
+  // 检查简历是否完整
+  const resume = localStorage.getItem('userResume');
+  if (!resume) {
+    if (confirm('您还没有创建简历,是否现在去创建?')) {
+      this.router.navigate(['/resume']);
+    }
+    return;
   }
   }
+
+  const parsedResume = JSON.parse(resume);
+  if (!parsedResume.basicInfo.name || !parsedResume.basicInfo.email) {
+    if (confirm('您的简历不完整,是否现在去完善?')) {
+      this.router.navigate(['/resume']);
+    }
+    return;
+  }
+
+  // 实际应用中这里会调用API提交申请
+  console.log(`Applying for job ID: ${jobId} with resume:`, parsedResume);
+  
+  // 模拟API调用
+  setTimeout(() => {
+    alert(`已成功申请职位ID: ${jobId}\n我们将通过 ${parsedResume.basicInfo.email} 与您联系`);
+    
+    // 添加到申请记录
+    const applications = JSON.parse(localStorage.getItem('jobApplications') || '[]');
+    applications.push({
+      jobId,
+      date: new Date().toISOString(),
+      status: '已提交'
+    });
+    localStorage.setItem('jobApplications', JSON.stringify(applications));
+  }, 1000);
+}
   
   
 }
 }

+ 3 - 3
interview-web/src/modules/interview/mobile/page-mine/page-mine.ts

@@ -45,9 +45,9 @@ export class PageMine {
   mainFeatures = [
   mainFeatures = [
     {
     {
       icon: this.icons.resume,
       icon: this.icons.resume,
-      title: '简历编辑',
-      description: '完善您的个人简历信息',
-      route: '/mine/resume'
+      title: '简历管理',
+      description: '编辑和更新您的简历',
+      route: '/resume'
     },
     },
     {
     {
       icon: this.icons.settings,
       icon: this.icons.settings,

+ 153 - 0
interview-web/src/modules/interview/mobile/page-resume/page-resume.html

@@ -0,0 +1,153 @@
+<div class="resume-container">
+  <div class="resume-header">
+    <h1>我的简历</h1>
+    <div class="action-buttons">
+      @if (isEditing) {
+        <button class="btn save-btn" (click)="saveResume()">
+          <fa-icon [icon]="icons.save"></fa-icon> 保存
+        </button>
+        <button class="btn cancel-btn" (click)="cancelEditing()">取消</button>
+      } @else {
+        <button class="btn edit-btn" (click)="startEditing()">
+          <fa-icon [icon]="icons.edit"></fa-icon> 编辑
+        </button>
+      }
+    </div>
+  </div>
+
+  <div class="resume-section">
+    <h2>基本信息</h2>
+    <div class="form-group">
+      <label>
+        <fa-icon [icon]="icons.user"></fa-icon> 姓名
+      </label>
+      <input type="text" [(ngModel)]="resume.basicInfo.name" [disabled]="!isEditing">
+    </div>
+    <div class="form-group">
+      <label>
+        <fa-icon [icon]="icons.email"></fa-icon> 邮箱
+      </label>
+      <input type="email" [(ngModel)]="resume.basicInfo.email" [disabled]="!isEditing">
+    </div>
+    <div class="form-group">
+      <label>
+        <fa-icon [icon]="icons.phone"></fa-icon> 电话
+      </label>
+      <input type="tel" [(ngModel)]="resume.basicInfo.phone" [disabled]="!isEditing">
+    </div>
+    <div class="form-group">
+      <label>
+        <fa-icon [icon]="icons.briefcase"></fa-icon> 求职意向
+      </label>
+      <input type="text" [(ngModel)]="resume.basicInfo.position" [disabled]="!isEditing">
+    </div>
+  </div>
+
+  <div class="resume-section">
+    <h2>
+      <fa-icon [icon]="icons.education"></fa-icon> 教育背景
+      @if (isEditing) {
+        <button class="btn add-btn" (click)="addEducation()">
+          <fa-icon [icon]="icons.add"></fa-icon> 添加
+        </button>
+      }
+    </h2>
+    @for (edu of resume.education; track edu.id) {
+      <div class="education-item">
+        <div class="form-group">
+          <label>学校</label>
+          <input type="text" [(ngModel)]="edu.school" [disabled]="!isEditing">
+        </div>
+        <div class="form-row">
+          <div class="form-group">
+            <label>学历</label>
+            <input type="text" [(ngModel)]="edu.degree" [disabled]="!isEditing">
+          </div>
+          <div class="form-group">
+            <label>专业</label>
+            <input type="text" [(ngModel)]="edu.major" [disabled]="!isEditing">
+          </div>
+        </div>
+        <div class="form-row">
+          <div class="form-group">
+            <label>开始时间</label>
+            <input type="date" [(ngModel)]="edu.startDate" [disabled]="!isEditing">
+          </div>
+          <div class="form-group">
+            <label>结束时间</label>
+            <input type="date" [(ngModel)]="edu.endDate" [disabled]="!isEditing">
+          </div>
+        </div>
+        @if (isEditing) {
+          <button class="btn delete-btn" (click)="removeEducation(edu.id)">
+            <fa-icon [icon]="icons.delete"></fa-icon> 删除
+          </button>
+        }
+      </div>
+    }
+  </div>
+
+  <div class="resume-section">
+    <h2>
+      <fa-icon [icon]="icons.experience"></fa-icon> 工作经历
+      @if (isEditing) {
+        <button class="btn add-btn" (click)="addExperience()">
+          <fa-icon [icon]="icons.add"></fa-icon> 添加
+        </button>
+      }
+    </h2>
+    @for (exp of resume.experiences; track exp.id) {
+      <div class="experience-item">
+        <div class="form-group">
+          <label>公司名称</label>
+          <input type="text" [(ngModel)]="exp.company" [disabled]="!isEditing">
+        </div>
+        <div class="form-group">
+          <label>职位</label>
+          <input type="text" [(ngModel)]="exp.position" [disabled]="!isEditing">
+        </div>
+        <div class="form-row">
+          <div class="form-group">
+            <label>开始时间</label>
+            <input type="date" [(ngModel)]="exp.startDate" [disabled]="!isEditing">
+          </div>
+          <div class="form-group">
+            <label>结束时间</label>
+            <input type="date" [(ngModel)]="exp.endDate" [disabled]="!isEditing">
+          </div>
+        </div>
+        <div class="form-group">
+          <label>工作描述</label>
+          <textarea [(ngModel)]="exp.description" [disabled]="!isEditing"></textarea>
+        </div>
+        @if (isEditing) {
+          <button class="btn delete-btn" (click)="removeExperience(exp.id)">
+            <fa-icon [icon]="icons.delete"></fa-icon> 删除
+          </button>
+        }
+      </div>
+    }
+  </div>
+
+  <div class="resume-section">
+    <h2>技能专长</h2>
+    @if (isEditing) {
+      <div class="skill-input">
+        <input type="text" [(ngModel)]="newSkill" placeholder="输入技能">
+        <button class="btn add-btn" (click)="addSkill()">
+          <fa-icon [icon]="icons.add"></fa-icon> 添加
+        </button>
+      </div>
+    }
+    <div class="skills-list">
+      @for (skill of resume.skills; track $index) {
+        <div class="skill-tag">
+          {{ skill }}
+          @if (isEditing) {
+            <span class="remove-tag" (click)="removeSkill($index)">×</span>
+          }
+        </div>
+      }
+    </div>
+  </div>
+</div>

+ 175 - 0
interview-web/src/modules/interview/mobile/page-resume/page-resume.scss

@@ -0,0 +1,175 @@
+.resume-container {
+  max-width: 800px;
+  margin: 0 auto;
+  padding: 20px;
+  background-color: #f9f9f9;
+  min-height: 100vh;
+}
+
+.resume-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 30px;
+  
+  h1 {
+    color: #2A5CAA;
+    font-size: 24px;
+  }
+}
+
+.action-buttons {
+  display: flex;
+  gap: 10px;
+}
+
+.btn {
+  padding: 8px 15px;
+  border-radius: 5px;
+  border: none;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  gap: 5px;
+  font-size: 14px;
+  
+  &.edit-btn {
+    background-color: #2A5CAA;
+    color: white;
+  }
+  
+  &.save-btn {
+    background-color: #48BB78;
+    color: white;
+  }
+  
+  &.cancel-btn {
+    background-color: #E53E3E;
+    color: white;
+  }
+  
+  &.add-btn {
+    background-color: #38B2AC;
+    color: white;
+    font-size: 12px;
+    padding: 5px 10px;
+  }
+  
+  &.delete-btn {
+    background-color: #E53E3E;
+    color: white;
+    font-size: 12px;
+    padding: 5px 10px;
+  }
+}
+
+.resume-section {
+  background-color: white;
+  border-radius: 8px;
+  padding: 20px;
+  margin-bottom: 20px;
+  box-shadow: 0 2px 5px rgba(0,0,0,0.1);
+  
+  h2 {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    color: #2A5CAA;
+    font-size: 18px;
+    margin-bottom: 15px;
+    padding-bottom: 10px;
+    border-bottom: 1px solid #eee;
+  }
+}
+
+.form-group {
+  margin-bottom: 15px;
+  
+  label {
+    display: block;
+    margin-bottom: 5px;
+    font-weight: 500;
+    color: #4A5568;
+  }
+  
+  input, textarea {
+    width: 100%;
+    padding: 8px 12px;
+    border: 1px solid #ddd;
+    border-radius: 4px;
+    font-size: 14px;
+    
+    &:disabled {
+      background-color: #f5f5f5;
+      border-color: #eee;
+      color: #666;
+    }
+  }
+  
+  textarea {
+    min-height: 80px;
+  }
+}
+
+.form-row {
+  display: flex;
+  gap: 15px;
+  
+  .form-group {
+    flex: 1;
+  }
+}
+
+.education-item,
+.experience-item {
+  padding: 15px;
+  margin-bottom: 15px;
+  border-radius: 5px;
+  background-color: #f8fafc;
+  border-left: 3px solid #2A5CAA;
+}
+
+.skill-input {
+  display: flex;
+  gap: 10px;
+  margin-bottom: 15px;
+  
+  input {
+    flex: 1;
+    padding: 8px 12px;
+    border: 1px solid #ddd;
+    border-radius: 4px;
+  }
+}
+
+.skills-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 10px;
+}
+
+.skill-tag {
+  background-color: #EDF2F7;
+  padding: 5px 12px;
+  border-radius: 20px;
+  font-size: 14px;
+  position: relative;
+  
+  .remove-tag {
+    cursor: pointer;
+    margin-left: 5px;
+    color: #E53E3E;
+    font-weight: bold;
+  }
+}
+
+@media (max-width: 600px) {
+  .resume-container {
+    padding: 15px;
+  }
+  
+  .form-row {
+    flex-direction: column;
+    gap: 0;
+  }
+}

+ 23 - 0
interview-web/src/modules/interview/mobile/page-resume/page-resume.spec.ts

@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PageResume } from './page-resume';
+
+describe('PageResume', () => {
+  let component: PageResume;
+  let fixture: ComponentFixture<PageResume>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      imports: [PageResume]
+    })
+    .compileComponents();
+
+    fixture = TestBed.createComponent(PageResume);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 136 - 0
interview-web/src/modules/interview/mobile/page-resume/page-resume.ts

@@ -0,0 +1,136 @@
+import { Component } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { Router } from '@angular/router';
+import { FaIconComponent } from '@fortawesome/angular-fontawesome';
+import { 
+  faUser, faEnvelope, faPhone, faGraduationCap, 
+  faBriefcase, faPen, faTrash, faPlus, faCheck
+} from '@fortawesome/free-solid-svg-icons';
+
+@Component({
+  selector: 'app-page-resume',
+  standalone: true,
+  imports: [CommonModule, FormsModule, FaIconComponent],
+  templateUrl: './page-resume.html',
+  styleUrls: ['./page-resume.scss']
+})
+export class PageResume {
+  icons = {
+    user: faUser,
+    email: faEnvelope,
+    phone: faPhone,
+    education: faGraduationCap,
+    experience: faBriefcase,
+    edit: faPen,
+    delete: faTrash,
+    add: faPlus,
+    save: faCheck,
+    briefcase: faBriefcase
+  };
+
+  resume = {
+    basicInfo: {
+      name: '',
+      email: '',
+      phone: '',
+      position: ''
+    },
+    education: [] as {
+      id: number;
+      school: string;
+      degree: string;
+      major: string;
+      startDate: string;
+      endDate: string;
+    }[],
+    experiences: [] as {
+      id: number;
+      company: string;
+      position: string;
+      startDate: string;
+      endDate: string;
+      description: string;
+    }[],
+    skills: [] as string[]
+  };
+
+  newSkill = '';
+  isEditing = false;
+
+  constructor(private router: Router) {
+    // 从本地存储加载简历数据
+    const savedResume = localStorage.getItem('userResume');
+    if (savedResume) {
+      this.resume = JSON.parse(savedResume);
+    }
+  }
+
+  addEducation() {
+    this.resume.education.push({
+      id: Date.now(),
+      school: '',
+      degree: '',
+      major: '',
+      startDate: '',
+      endDate: ''
+    });
+  }
+
+  removeEducation(id: number) {
+    this.resume.education = this.resume.education.filter(edu => edu.id !== id);
+  }
+
+  addExperience() {
+    this.resume.experiences.push({
+      id: Date.now(),
+      company: '',
+      position: '',
+      startDate: '',
+      endDate: '',
+      description: ''
+    });
+  }
+
+  removeExperience(id: number) {
+    this.resume.experiences = this.resume.experiences.filter(exp => exp.id !== id);
+  }
+
+  addSkill() {
+    if (this.newSkill.trim()) {
+      this.resume.skills.push(this.newSkill.trim());
+      this.newSkill = '';
+    }
+  }
+
+  removeSkill(index: number) {
+    this.resume.skills.splice(index, 1);
+  }
+
+  saveResume() {
+    localStorage.setItem('userResume', JSON.stringify(this.resume));
+    this.isEditing = false;
+    alert('简历保存成功!');
+  }
+
+  startEditing() {
+    this.isEditing = true;
+  }
+
+  cancelEditing() {
+    if (confirm('确定要取消编辑吗?未保存的更改将丢失。')) {
+      const savedResume = localStorage.getItem('userResume');
+      if (savedResume) {
+        this.resume = JSON.parse(savedResume);
+      } else {
+        this.resume = {
+          basicInfo: { name: '', email: '', phone: '', position: '' },
+          education: [],
+          experiences: [],
+          skills: []
+        };
+      }
+      this.isEditing = false;
+    }
+  }
+}

Some files were not shown because too many files changed in this diff