Browse Source

Merge branch 'master' of http://git.fmode.cn:3000/0235713/intelligent-interview

0235713 2 days ago
parent
commit
77780affa8

+ 49 - 0
interview-web/package-lock.json

@@ -14,6 +14,9 @@
         "@angular/forms": "^20.0.0",
         "@angular/platform-browser": "^20.0.0",
         "@angular/router": "^20.0.0",
+        "@fortawesome/angular-fontawesome": "^2.0.1",
+        "@fortawesome/fontawesome-svg-core": "^6.7.2",
+        "@fortawesome/free-solid-svg-icons": "^6.7.2",
         "rxjs": "~7.8.0",
         "tslib": "^2.3.0",
         "zone.js": "~0.15.0"
@@ -1212,6 +1215,52 @@
         "node": ">=18"
       }
     },
+    "node_modules/@fortawesome/angular-fontawesome": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-2.0.1.tgz",
+      "integrity": "sha512-IdklZkuw+WS2GQWhFnr1EX/tOALnrKaj4YGnUmPaUg2Uf+Amj8Xi+M/qDrr915YJ5MaDxd9tZ1kqOHRcvQqq2A==",
+      "license": "MIT",
+      "dependencies": {
+        "@fortawesome/fontawesome-svg-core": "^6.7.2",
+        "tslib": "^2.8.1"
+      },
+      "peerDependencies": {
+        "@angular/core": "^20.0.0"
+      }
+    },
+    "node_modules/@fortawesome/fontawesome-common-types": {
+      "version": "6.7.2",
+      "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.7.2.tgz",
+      "integrity": "sha512-Zs+YeHUC5fkt7Mg1l6XTniei3k4bwG/yo3iFUtZWd/pMx9g3fdvkSK9E0FOC+++phXOka78uJcYb8JaFkW52Xg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/@fortawesome/fontawesome-svg-core": {
+      "version": "6.7.2",
+      "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.7.2.tgz",
+      "integrity": "sha512-yxtOBWDrdi5DD5o1pmVdq3WMCvnobT0LU6R8RyyVXPvFRd2o79/0NCuQoCjNTeZz9EzA9xS3JxNWfv54RIHFEA==",
+      "license": "MIT",
+      "dependencies": {
+        "@fortawesome/fontawesome-common-types": "6.7.2"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/@fortawesome/free-solid-svg-icons": {
+      "version": "6.7.2",
+      "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.7.2.tgz",
+      "integrity": "sha512-GsBrnOzU8uj0LECDfD5zomZJIjrPhIlWU82AHwa2s40FKH+kcxQaBvBo3Z4TxyZHIyX8XTDxsyA33/Vx9eFuQA==",
+      "license": "(CC-BY-4.0 AND MIT)",
+      "dependencies": {
+        "@fortawesome/fontawesome-common-types": "6.7.2"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/@inquirer/checkbox": {
       "version": "4.1.8",
       "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.1.8.tgz",

+ 3 - 0
interview-web/package.json

@@ -26,6 +26,9 @@
     "@angular/forms": "^20.0.0",
     "@angular/platform-browser": "^20.0.0",
     "@angular/router": "^20.0.0",
+    "@fortawesome/angular-fontawesome": "^2.0.1",
+    "@fortawesome/fontawesome-svg-core": "^6.7.2",
+    "@fortawesome/free-solid-svg-icons": "^6.7.2",
     "rxjs": "~7.8.0",
     "tslib": "^2.3.0",
     "zone.js": "~0.15.0"

+ 10 - 0
interview-web/src/app/app.routes.ts

@@ -1,10 +1,20 @@
 import { Routes } from '@angular/router';
+//import { PageCart } from '../modules/interview/mobile/page-interview/page-cart';
+//import { PageMine } from '../modules/interview/mobile/page-mine/page-mine';
 
 export const routes: Routes = [
     {
     path: '',
     loadChildren: () => import('../modules/interview/mobile/mobile.routes').then(m => m.MOBILE_ROUTES)
   },
+  /*{ 
+    path: 'interview/mobile/page-cart', 
+    component: PageCart // 替换为你的实际组件
+  },
+  { 
+    path: 'interview/mobile/page-mine', 
+    component: PageMine // 替换为你的实际组件
+  },*/
   {
     path: '',
     redirectTo: 'mobile',

+ 15 - 4
interview-web/src/modules/interview/mobile/mobile.routes.ts

@@ -1,19 +1,17 @@
 import { Routes } from '@angular/router';
-import { NavMobileTabs } from './nav-mobile-tabs/nav-mobile-tabs';
 
 export const MOBILE_ROUTES: Routes = [
   {
     path: '',
-    
     children: [
       {
         path: 'home',
         loadComponent: () => import('./page-home/page-home').then(m => m.PageHome)
       },
-      {
+      /*{
         path: 'cart',
         loadComponent: () => import('./page-cart/page-cart').then(m => m.PageCart)
-      },
+      },*/
       {
         path: 'mine',
         loadComponent: () => import('./page-mine/page-mine').then(m => m.PageMine)
@@ -24,6 +22,19 @@ export const MOBILE_ROUTES: Routes = [
         pathMatch: 'full'
       }
     ]
+    
+  },
+  {
+   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-job-hunting',
+    loadComponent: () => import('./page-job-hunting/page-job-hunting').then(m => m.PageJobHunting)
   }
 ];
 

+ 0 - 1
interview-web/src/modules/interview/mobile/page-cart/page-cart.html

@@ -1 +0,0 @@
-<p>page-cart works!</p>

+ 0 - 11
interview-web/src/modules/interview/mobile/page-cart/page-cart.ts

@@ -1,11 +0,0 @@
-import { Component } from '@angular/core';
-
-@Component({
-  selector: 'app-page-cart',
-  imports: [],
-  templateUrl: './page-cart.html',
-  styleUrl: './page-cart.scss'
-})
-export class PageCart {
-
-}

+ 71 - 1
interview-web/src/modules/interview/mobile/page-home/page-home.html

@@ -1 +1,71 @@
-<p>page-home works!</p>
+<div class="container">
+  <!-- 顶部导航 -->
+  <div class="header">
+    <div class="logo">AI面试官</div>
+    <div class="user-avatar">
+      <fa-icon [icon]="icons.user"></fa-icon>
+    </div>
+  </div>
+  
+  <!-- 欢迎区域 -->
+  <div class="welcome-section">
+    <div class="welcome-card">
+      <h1 class="welcome-title">{{greeting}},{{user.name}}</h1>
+      <p class="welcome-subtitle">您有{{user.pendingInterviews}}个匹配的岗位待面试,准备好了吗?</p>
+      <button class="start-btn" (click)="handleCardClick()">
+        开始模拟面试 <fa-icon [icon]="icons.arrowRight"></fa-icon>
+      </button>
+    </div>
+  </div>
+  
+  <!-- 岗位推荐 -->
+  <div class="job-section">
+    <h2 class="section-title">
+      为您推荐
+      <a routerLink="/interview/mobile/page-job-hunting" class="see-all">查看全部</a>
+    </h2>
+     <swiper-container [init]="false" [config]="swiperConfig" class="mySwiper">
+      <swiper-slide *ngFor="let job of jobs">
+        <div class="job-card" (click)="handleCardClick()">
+          <div class="job-header">
+            <h3 class="job-title">{{job.title}}</h3>
+            <div class="job-salary">{{job.salary}}</div>
+          </div>
+          <div class="job-company">
+            <div class="company-logo">{{job.companyLogo}}</div>
+            <div class="company-name">{{job.companyName}}</div>
+          </div>
+          <div class="job-tags">
+            <span class="tag" *ngFor="let tag of job.tags">{{tag}}</span>
+          </div>
+          <div class="match-bar">
+            <div class="match-text">
+              <span>匹配度</span>
+              <span class="match-value">{{job.match}}%</span>
+            </div>
+            <div class="progress-bar">
+              <div class="progress-fill" [style.width.%]="job.match"></div>
+            </div>
+          </div>
+        </div>
+      </swiper-slide>
+    </swiper-container>
+  
+
+  </div>
+  
+  <!-- 快速入口 -->
+  <div class="quick-section">
+    <h2 class="section-title">快速开始</h2>
+    
+    <div class="quick-actions">
+      <div class="action-card" *ngFor="let action of quickActions" (click)="handleCardClick()" [routerLink]="action.RouterLink">
+        <div class="action-icon" [style.background]="action.gradient">
+          <fa-icon [icon]="action.icon"></fa-icon>
+        </div>
+        <h3 class="action-title">{{action.title}}</h3>
+        <p class="action-desc">{{action.description}}</p>
+      </div>
+    </div>
+  </div>
+</div>

+ 314 - 0
interview-web/src/modules/interview/mobile/page-home/page-home.scss

@@ -0,0 +1,314 @@
+:host {
+  --primary-blue: #2A5CAA;
+  --accent-orange: #FF6B35;
+  --bg-light: #F5F7FA;
+  --text-dark: #2D3748;
+  --success-green: #48BB78;
+  --card-shadow: 0 10px 20px rgba(0,0,0,0.08);
+  
+  display: block;
+  background-color: var(--bg-light);
+  color: var(--text-dark);
+  line-height: 1.6;
+  font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
+}
+
+* {
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
+}
+
+.container {
+  max-width: 800px;
+  margin: 0 auto;
+  padding: 20px;
+  padding-bottom: 70px; // 为底部导航留空间
+}
+
+/* 顶部导航 */
+.header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 30px;
+}
+
+.logo {
+  font-size: 24px;
+  font-weight: bold;
+  background: linear-gradient(to right, var(--primary-blue), var(--accent-orange));
+  background-clip: text;
+  -webkit-text-fill-color: transparent;
+}
+
+.user-avatar {
+  width: 40px;
+  height: 40px;
+  border-radius: 50%;
+  background-color: var(--primary-blue);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  color: white;
+  font-size: 18px;
+}
+
+/* 用户欢迎区域 */
+.welcome-section {
+  margin-bottom: 30px;
+}
+
+.welcome-card {
+  background: linear-gradient(135deg, var(--primary-blue), #3A7BD5);
+  color: white;
+  border-radius: 16px;
+  padding: 25px;
+  box-shadow: var(--card-shadow);
+  position: relative;
+  overflow: hidden;
+  
+  &::after {
+    content: '';
+    position: absolute;
+    top: -50px;
+    right: -50px;
+    width: 200px;
+    height: 200px;
+    background: rgba(255,255,255,0.1);
+    border-radius: 50%;
+  }
+}
+
+.welcome-title {
+  font-size: 22px;
+  margin-bottom: 10px;
+  font-weight: 500;
+  position: relative;
+  z-index: 1;
+}
+
+.welcome-subtitle {
+  font-size: 16px;
+  opacity: 0.9;
+  margin-bottom: 20px;
+  position: relative;
+  z-index: 1;
+}
+
+.start-btn {
+  background-color: white;
+  color: var(--primary-blue);
+  border: none;
+  padding: 12px 25px;
+  border-radius: 30px;
+  font-size: 16px;
+  font-weight: 500;
+  cursor: pointer;
+  display: inline-flex;
+  align-items: center;
+  box-shadow: 0 4px 15px rgba(0,0,0,0.1);
+  transition: all 0.3s ease;
+  position: relative;
+  z-index: 1;
+  
+  &:hover {
+    transform: translateY(-3px);
+    box-shadow: 0 6px 20px rgba(0,0,0,0.15);
+  }
+  
+  fa-icon {
+    margin-left: 8px;
+  }
+}
+
+/* 岗位推荐轮播 */
+.section-title {
+  font-size: 20px;
+  margin-bottom: 20px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.see-all {
+  font-size: 14px;
+  color: var(--primary-blue);
+  text-decoration: none;
+}
+
+.swiper {
+  width: 100%;
+  height: 100%;
+  margin-bottom: 40px;
+}
+
+.swiper-slide {
+  background: white;
+  border-radius: 12px;
+  padding: 20px;
+  box-shadow: var(--card-shadow);
+  cursor: pointer;
+  transition: all 0.3s ease;
+  
+  &:hover {
+    transform: translateY(-5px);
+    box-shadow: 0 15px 30px rgba(0,0,0,0.12);
+  }
+}
+
+.job-card {
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+}
+
+.job-header {
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 15px;
+}
+
+.job-title {
+  font-size: 18px;
+  font-weight: 600;
+}
+
+.job-salary {
+  color: var(--accent-orange);
+  font-weight: bold;
+}
+
+.job-company {
+  display: flex;
+  align-items: center;
+  margin-bottom: 15px;
+}
+
+.company-logo {
+  width: 30px;
+  height: 30px;
+  border-radius: 6px;
+  background-color: #EDF2F7;
+  margin-right: 10px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  color: var(--primary-blue);
+  font-size: 12px;
+}
+
+.company-name {
+  font-size: 14px;
+  color: #718096;
+}
+
+.job-tags {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+  margin-bottom: 15px;
+}
+
+.tag {
+  background: #EDF2F7;
+  padding: 4px 10px;
+  border-radius: 20px;
+  font-size: 12px;
+  color: #4A5568;
+}
+
+.match-bar {
+  margin-top: auto;
+}
+
+.match-text {
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 5px;
+  font-size: 14px;
+}
+
+.match-value {
+  color: var(--primary-blue);
+  font-weight: 600;
+}
+
+.progress-bar {
+  height: 6px;
+  background: #EDF2F7;
+  border-radius: 3px;
+  overflow: hidden;
+}
+
+.progress-fill {
+  height: 100%;
+  border-radius: 3px;
+  background: linear-gradient(to right, var(--primary-blue), var(--accent-orange));
+}
+
+/* 快速入口 */
+.quick-actions {
+  display: grid;
+  grid-template-columns: repeat(2, 1fr);
+  gap: 15px;
+  margin-bottom: 30px;
+}
+
+.action-card {
+  background: white;
+  border-radius: 12px;
+  padding: 20px;
+  box-shadow: var(--card-shadow);
+  text-align: center;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  
+  &:hover {
+    transform: translateY(-3px);
+    box-shadow: 0 10px 25px rgba(0,0,0,0.1);
+  }
+}
+
+.action-icon {
+  width: 50px;
+  height: 50px;
+  border-radius: 50%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  color: white;
+  font-size: 20px;
+  margin: 0 auto 15px;
+}
+
+.action-title {
+  font-size: 16px;
+  font-weight: 500;
+  margin-bottom: 5px;
+}
+
+.action-desc {
+  font-size: 12px;
+  color: #718096;
+}
+
+/* 响应式调整 */
+@media (max-width: 600px) {
+  .container {
+    padding: 15px;
+    padding-bottom: 70px;
+  }
+  
+  .welcome-card {
+    padding: 20px;
+  }
+  
+  .welcome-title {
+    font-size: 20px;
+  }
+  
+  .quick-actions {
+    grid-template-columns: 1fr;
+  }
+}

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

@@ -1,11 +1,135 @@
-import { Component } from '@angular/core';
+import { Component, CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { Router, RouterLink, RouterModule, RouterState } from '@angular/router';
+import { register } from 'swiper/element/bundle';
+import { FaIconComponent, FontAwesomeModule } from '@fortawesome/angular-fontawesome';
+import { Routes } from '@angular/router';
+import { 
+  faUser, 
+  faArrowRight, 
+  faRobot, 
+  faVrCardboard, 
+  faBook, 
+  faChartLine,
+  faHome,
+  faSearch,
+  faComments
+} from '@fortawesome/free-solid-svg-icons';
+// 注册 Swiper 元素
 
+register();
 @Component({
   selector: 'app-page-home',
-  imports: [],
+  standalone: true,
+  imports: [CommonModule, RouterModule, FontAwesomeModule,FaIconComponent,RouterModule],
   templateUrl: './page-home.html',
-  styleUrl: './page-home.scss'
+  styleUrls: ['./page-home.scss'],
+  schemas: [CUSTOM_ELEMENTS_SCHEMA]
 })
+
 export class PageHome {
+  // Font Awesome icons
+  icons = {
+    user: faUser,
+    arrowRight: faArrowRight,
+    robot: faRobot,
+    vrCardboard: faVrCardboard,
+    book: faBook,
+    chartLine: faChartLine,
+    home: faHome,
+    search: faSearch,
+    comments: faComments,
+  };
+
+  // 用户信息
+  user = {
+    name: '李华',
+    pendingInterviews: 3
+  };
+
+  // 岗位数据
+  jobs = [
+    {
+      title: '前端开发工程师',
+      salary: '25-40K·15薪',
+      companyLogo: 'TX',
+      companyName: '腾讯科技 · 深圳',
+      tags: ['Vue.js', 'React', 'TypeScript'],
+      match: 87
+    },
+    {
+      title: 'Java高级工程师',
+      salary: '30-50K·16薪',
+      companyLogo: 'AL',
+      companyName: '阿里巴巴 · 杭州',
+      tags: ['Spring Cloud', '分布式', 'MySQL'],
+      match: 76
+    },
+    {
+      title: '产品经理',
+      salary: '20-35K·14薪',
+      companyLogo: 'BD',
+      companyName: '百度 · 北京',
+      tags: ['Axure', '用户研究', 'PRD'],
+      match: 92
+    }
+  ];
+
+  // 快速入口
+  quickActions = [
+    {
+      icon: this.icons.robot,
+      title: 'AI模拟面试',
+      description: '智能评估您的表现',
+      gradient: 'linear-gradient(135deg, var(--primary-blue), #3A7BD5)',
+      RouterLink:'/interview/mobile/page-interview'
+    },
+    {
+      icon: this.icons.vrCardboard,
+      title: 'VR企业展厅',
+      description: '沉浸式了解企业',
+      gradient: 'linear-gradient(135deg, #9F7AEA, #6B46C1)',
+      RouterLink:'/interview/mobile/page-job-hunting'
+    },
+    {
+      icon: this.icons.book,
+      title: '面试题库',
+      description: '海量真题练习',
+      gradient: 'linear-gradient(135deg, #F6AD55, #DD6B20)',
+      RouterLink:'/interview/mobile/page-mine'
+    },
+    {
+      icon: this.icons.chartLine,
+      title: '成长报告',
+      description: '查看历史表现',
+      gradient: 'linear-gradient(135deg, #48BB78, #38A169)',
+      RouterLink:'/interview/mobile/page-mine'
+    }
+  ];
+
+  // Swiper配置
+  swiperConfig = {
+    slidesPerView: 1.1,
+    spaceBetween: 15,
+    grabCursor: true,
+    breakpoints: {
+      600: {
+        slidesPerView: 2.1,
+        spaceBetween: 20
+      }
+    }
+  };
+
+  // 处理卡片点击
+  handleCardClick() {
+    alert('即将跳转到对应功能页面');
+  }
 
+  // 获取问候语
+  get greeting() {
+    const hour = new Date().getHours();
+    if (hour < 12) return '上午好';
+    if (hour < 18) return '下午好';
+    return '晚上好';
+  }
 }

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

@@ -0,0 +1 @@
+<p>page-interview works!</p>

+ 0 - 0
interview-web/src/modules/interview/mobile/page-cart/page-cart.scss → interview-web/src/modules/interview/mobile/page-interview/page-interview.scss


+ 6 - 6
interview-web/src/modules/interview/mobile/page-cart/page-cart.spec.ts → interview-web/src/modules/interview/mobile/page-interview/page-interview.spec.ts

@@ -1,18 +1,18 @@
 import { ComponentFixture, TestBed } from '@angular/core/testing';
 
-import { PageCart } from './page-cart';
+import { PageInterview } from './page-interview';
 
-describe('PageCart', () => {
-  let component: PageCart;
-  let fixture: ComponentFixture<PageCart>;
+describe('PageInterview', () => {
+  let component: PageInterview;
+  let fixture: ComponentFixture<PageInterview>;
 
   beforeEach(async () => {
     await TestBed.configureTestingModule({
-      imports: [PageCart]
+      imports: [PageInterview]
     })
     .compileComponents();
 
-    fixture = TestBed.createComponent(PageCart);
+    fixture = TestBed.createComponent(PageInterview);
     component = fixture.componentInstance;
     fixture.detectChanges();
   });

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

@@ -0,0 +1,11 @@
+import { Component } from '@angular/core';
+
+@Component({
+  selector: 'app-page-interview',
+  imports: [],
+  templateUrl: './page-interview.html',
+  styleUrl: './page-interview.scss'
+})
+export class PageInterview {
+
+}

+ 1 - 0
interview-web/src/modules/interview/mobile/page-job-hunting/page-job-hunting.html

@@ -0,0 +1 @@
+<p>page-job-hunting works!</p>

+ 0 - 0
interview-web/src/modules/interview/mobile/page-job-hunting/page-job-hunting.scss


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

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

+ 11 - 0
interview-web/src/modules/interview/mobile/page-job-hunting/page-job-hunting.ts

@@ -0,0 +1,11 @@
+import { Component } from '@angular/core';
+
+@Component({
+  selector: 'app-page-job-hunting',
+  imports: [],
+  templateUrl: './page-job-hunting.html',
+  styleUrl: './page-job-hunting.scss'
+})
+export class PageJobHunting {
+
+}